@hszrdm/core 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.medusa/server/src/admin/index.js +5582 -3769
- package/.medusa/server/src/admin/index.mjs +5582 -3769
- package/.medusa/server/src/api/admin/back-in-stock/route.d.ts +2 -0
- package/.medusa/server/src/api/admin/back-in-stock/route.js +70 -0
- package/.medusa/server/src/api/admin/customer-activation/route.d.ts +2 -0
- package/.medusa/server/src/api/admin/customer-activation/route.js +97 -0
- package/.medusa/server/src/api/admin/customer-import/route.d.ts +2 -0
- package/.medusa/server/src/api/admin/customer-import/route.js +166 -0
- package/.medusa/server/src/api/admin/order-export/route.d.ts +2 -0
- package/.medusa/server/src/api/admin/order-export/route.js +88 -0
- package/.medusa/server/src/api/admin/product-migration/inventory/route.d.ts +3 -0
- package/.medusa/server/src/api/admin/product-migration/inventory/route.js +164 -0
- package/.medusa/server/src/api/admin/product-migration/price-list-prices/route.d.ts +3 -0
- package/.medusa/server/src/api/admin/product-migration/price-list-prices/route.js +204 -0
- package/.medusa/server/src/api/admin/product-migration/products/route.d.ts +2 -0
- package/.medusa/server/src/api/admin/product-migration/products/route.js +24 -0
- package/.medusa/server/src/api/admin/product-reviews/[id]/route.js +36 -4
- package/.medusa/server/src/api/admin/product-reviews/settings/route.d.ts +3 -0
- package/.medusa/server/src/api/admin/product-reviews/settings/route.js +63 -0
- package/.medusa/server/src/api/admin/system-runtime/route.d.ts +2 -0
- package/.medusa/server/src/api/admin/system-runtime/route.js +18 -0
- package/.medusa/server/src/api/hooks/packeta/status/route.d.ts +2 -0
- package/.medusa/server/src/api/hooks/packeta/status/route.js +55 -0
- package/.medusa/server/src/api/middlewares.js +16 -1
- package/.medusa/server/src/api/store/abandoned-cart/recover/route.d.ts +2 -0
- package/.medusa/server/src/api/store/abandoned-cart/recover/route.js +37 -0
- package/.medusa/server/src/api/store/back-in-stock/route.d.ts +8 -0
- package/.medusa/server/src/api/store/back-in-stock/route.js +124 -0
- package/.medusa/server/src/api/store/customer-activation/complete/route.d.ts +2 -0
- package/.medusa/server/src/api/store/customer-activation/complete/route.js +96 -0
- package/.medusa/server/src/api/store/customer-activation/route.d.ts +2 -0
- package/.medusa/server/src/api/store/customer-activation/route.js +34 -0
- package/.medusa/server/src/api/store/products/[id]/reviews/route.d.ts +1 -0
- package/.medusa/server/src/api/store/products/[id]/reviews/route.js +76 -34
- package/.medusa/server/src/api/store/review-request/route.d.ts +2 -0
- package/.medusa/server/src/api/store/review-request/route.js +60 -0
- package/.medusa/server/src/core/abandoned-cart.d.ts +64 -0
- package/.medusa/server/src/core/abandoned-cart.js +197 -0
- package/.medusa/server/src/core/back-in-stock.d.ts +51 -0
- package/.medusa/server/src/core/back-in-stock.js +107 -0
- package/.medusa/server/src/core/checkout-contract.d.ts +1 -0
- package/.medusa/server/src/core/checkout-contract.js +2 -1
- package/.medusa/server/src/core/customer-account-contract.d.ts +3 -0
- package/.medusa/server/src/core/customer-account-contract.js +4 -1
- package/.medusa/server/src/core/customer-activation.d.ts +120 -0
- package/.medusa/server/src/core/customer-activation.js +169 -0
- package/.medusa/server/src/core/customer-email.d.ts +1 -1
- package/.medusa/server/src/core/customer-email.js +4 -1
- package/.medusa/server/src/core/native-migration-export.d.ts +6 -0
- package/.medusa/server/src/core/native-migration-export.js +105 -0
- package/.medusa/server/src/core/order-export.d.ts +11 -0
- package/.medusa/server/src/core/order-export.js +297 -0
- package/.medusa/server/src/core/review-request.d.ts +61 -0
- package/.medusa/server/src/core/review-request.js +105 -0
- package/.medusa/server/src/core/review-reward.d.ts +94 -0
- package/.medusa/server/src/core/review-reward.js +124 -0
- package/.medusa/server/src/core/runtime-health.d.ts +39 -0
- package/.medusa/server/src/core/runtime-health.js +250 -0
- package/.medusa/server/src/core/storefront-core-config.d.ts +10 -0
- package/.medusa/server/src/core/storefront-core-config.js +26 -1
- package/.medusa/server/src/core/version.d.ts +2 -2
- package/.medusa/server/src/core/version.js +2 -2
- package/.medusa/server/src/jobs/abandoned-cart-recovery.d.ts +23 -0
- package/.medusa/server/src/jobs/abandoned-cart-recovery.js +138 -0
- package/.medusa/server/src/jobs/back-in-stock-reconciliation.d.ts +15 -0
- package/.medusa/server/src/jobs/back-in-stock-reconciliation.js +90 -0
- package/.medusa/server/src/jobs/dpd-statusdata-sync.d.ts +17 -0
- package/.medusa/server/src/jobs/dpd-statusdata-sync.js +107 -0
- package/.medusa/server/src/jobs/mpl-push-tracking.d.ts +19 -0
- package/.medusa/server/src/jobs/mpl-push-tracking.js +183 -0
- package/.medusa/server/src/jobs/mpl-status-sync.d.ts +8 -2
- package/.medusa/server/src/jobs/mpl-status-sync.js +12 -7
- package/.medusa/server/src/jobs/product-review-request-dispatch.d.ts +28 -0
- package/.medusa/server/src/jobs/product-review-request-dispatch.js +84 -0
- package/.medusa/server/src/jobs/runtime-heartbeat.d.ts +12 -0
- package/.medusa/server/src/jobs/runtime-heartbeat.js +13 -0
- package/.medusa/server/src/jobs/shipping-status-sync.d.ts +8 -2
- package/.medusa/server/src/jobs/shipping-status-sync.js +11 -14
- package/.medusa/server/src/lib/modules/shipping/idempotency.js +3 -3
- package/.medusa/server/src/lib/modules/shipping/index.d.ts +1 -0
- package/.medusa/server/src/lib/modules/shipping/index.js +2 -1
- package/.medusa/server/src/lib/modules/shipping/tracking.d.ts +48 -0
- package/.medusa/server/src/lib/modules/shipping/tracking.js +368 -0
- package/.medusa/server/src/lib/modules/shipping/webhooks.d.ts +6 -9
- package/.medusa/server/src/lib/modules/shipping/webhooks.js +24 -137
- package/.medusa/server/src/modules/back-in-stock/index.d.ts +23 -0
- package/.medusa/server/src/modules/back-in-stock/index.js +16 -0
- package/.medusa/server/src/modules/back-in-stock/migrations/Migration20261101202000.d.ts +5 -0
- package/.medusa/server/src/modules/back-in-stock/migrations/Migration20261101202000.js +18 -0
- package/.medusa/server/src/modules/back-in-stock/models/back-in-stock-subscription.d.ts +13 -0
- package/.medusa/server/src/modules/back-in-stock/models/back-in-stock-subscription.js +23 -0
- package/.medusa/server/src/modules/back-in-stock/service.d.ts +22 -0
- package/.medusa/server/src/modules/back-in-stock/service.js +12 -0
- package/.medusa/server/src/modules/email-center/abandoned-cart-registration.d.ts +1 -0
- package/.medusa/server/src/modules/email-center/abandoned-cart-registration.js +27 -0
- package/.medusa/server/src/modules/email-center/back-in-stock-registration.d.ts +1 -0
- package/.medusa/server/src/modules/email-center/back-in-stock-registration.js +27 -0
- package/.medusa/server/src/modules/email-center/defaults.js +7 -1
- package/.medusa/server/src/modules/email-center/index.d.ts +2 -0
- package/.medusa/server/src/modules/email-center/index.js +3 -1
- package/.medusa/server/src/modules/integration-settings/abandoned-cart-registration.d.ts +1 -0
- package/.medusa/server/src/modules/integration-settings/abandoned-cart-registration.js +30 -0
- package/.medusa/server/src/modules/integration-settings/index.d.ts +1 -0
- package/.medusa/server/src/modules/integration-settings/index.js +2 -1
- package/.medusa/server/src/modules/integration-settings/storefront-public-registration.js +15 -1
- package/.medusa/server/src/modules/product-review/index.d.ts +14 -0
- package/.medusa/server/src/modules/product-review/migrations/Migration20261101113000.d.ts +5 -0
- package/.medusa/server/src/modules/product-review/migrations/Migration20261101113000.js +28 -0
- package/.medusa/server/src/modules/product-review/migrations/Migration20261101114500.d.ts +5 -0
- package/.medusa/server/src/modules/product-review/migrations/Migration20261101114500.js +20 -0
- package/.medusa/server/src/modules/product-review/migrations/Migration20261101121500.d.ts +5 -0
- package/.medusa/server/src/modules/product-review/migrations/Migration20261101121500.js +18 -0
- package/.medusa/server/src/modules/product-review/models/product-review-settings.d.ts +15 -0
- package/.medusa/server/src/modules/product-review/models/product-review-settings.js +19 -0
- package/.medusa/server/src/modules/product-review/models/product-review.d.ts +6 -2
- package/.medusa/server/src/modules/product-review/models/product-review.js +7 -3
- package/.medusa/server/src/modules/product-review/service.d.ts +22 -1
- package/.medusa/server/src/modules/product-review/service.js +3 -2
- package/.medusa/server/src/providers/dpd/index.d.ts +2 -0
- package/.medusa/server/src/providers/dpd/index.js +3 -1
- package/.medusa/server/src/providers/dpd/statusdata-ftp.d.ts +16 -0
- package/.medusa/server/src/providers/dpd/statusdata-ftp.js +268 -0
- package/.medusa/server/src/providers/dpd/statusdata.d.ts +14 -0
- package/.medusa/server/src/providers/dpd/statusdata.js +161 -0
- package/.medusa/server/src/providers/mpl/client.d.ts +3 -1
- package/.medusa/server/src/providers/mpl/client.js +154 -2
- package/.medusa/server/src/providers/mpl/service.js +31 -20
- package/.medusa/server/src/providers/mpl/types.d.ts +25 -0
- package/.medusa/server/src/providers/mpl/types.js +1 -1
- package/.medusa/server/src/providers/packeta/index.d.ts +1 -0
- package/.medusa/server/src/providers/packeta/index.js +2 -1
- package/.medusa/server/src/providers/packeta/push-tracking.d.ts +8 -0
- package/.medusa/server/src/providers/packeta/push-tracking.js +96 -0
- package/.medusa/server/src/providers/packeta/service.js +22 -2
- package/.medusa/server/src/scripts/core-init.d.ts +1 -1
- package/.medusa/server/src/scripts/core-init.js +3 -2
- package/.medusa/server/src/subscribers/back-in-stock-inventory-changed.d.ts +7 -0
- package/.medusa/server/src/subscribers/back-in-stock-inventory-changed.js +65 -0
- package/.medusa/server/src/workflows/link-migrated-customer.d.ts +8 -0
- package/.medusa/server/src/workflows/link-migrated-customer.js +17 -0
- package/package.json +2 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const CORE_VERSION = "1.
|
|
1
|
+
export declare const CORE_VERSION = "1.5.0";
|
|
2
2
|
export declare const CORE_API_CONTRACT_VERSION = "1.0";
|
|
3
3
|
export declare const STOREFRONT_CORE_CONFIG_SCHEMA_VERSION = 1;
|
|
4
|
-
export declare const CORE_DB_SCHEMA_VERSION =
|
|
4
|
+
export declare const CORE_DB_SCHEMA_VERSION = 6;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.CORE_DB_SCHEMA_VERSION = exports.STOREFRONT_CORE_CONFIG_SCHEMA_VERSION = exports.CORE_API_CONTRACT_VERSION = exports.CORE_VERSION = void 0;
|
|
4
|
-
exports.CORE_VERSION = "1.
|
|
4
|
+
exports.CORE_VERSION = "1.5.0";
|
|
5
5
|
exports.CORE_API_CONTRACT_VERSION = "1.0";
|
|
6
6
|
exports.STOREFRONT_CORE_CONFIG_SCHEMA_VERSION = 1;
|
|
7
|
-
exports.CORE_DB_SCHEMA_VERSION =
|
|
7
|
+
exports.CORE_DB_SCHEMA_VERSION = 6;
|
|
8
8
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmVyc2lvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9jb3JlL3ZlcnNpb24udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQWEsUUFBQSxZQUFZLEdBQUcsT0FBTyxDQUFBO0FBQ3RCLFFBQUEseUJBQXlCLEdBQUcsS0FBSyxDQUFBO0FBQ2pDLFFBQUEscUNBQXFDLEdBQUcsQ0FBQyxDQUFBO0FBQ3pDLFFBQUEsc0JBQXNCLEdBQUcsQ0FBQyxDQUFBIn0=
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { MedusaContainer } from "@medusajs/framework/types";
|
|
2
|
+
export default function abandonedCartRecovery(container: MedusaContainer): Promise<{
|
|
3
|
+
scanned: number;
|
|
4
|
+
sent: number;
|
|
5
|
+
delayHours?: undefined;
|
|
6
|
+
} | {
|
|
7
|
+
scanned: number;
|
|
8
|
+
sent: number;
|
|
9
|
+
delayHours: number;
|
|
10
|
+
} | undefined>;
|
|
11
|
+
export declare function runBatch(container: MedusaContainer): Promise<{
|
|
12
|
+
scanned: number;
|
|
13
|
+
sent: number;
|
|
14
|
+
delayHours?: undefined;
|
|
15
|
+
} | {
|
|
16
|
+
scanned: number;
|
|
17
|
+
sent: number;
|
|
18
|
+
delayHours: number;
|
|
19
|
+
}>;
|
|
20
|
+
export declare const config: {
|
|
21
|
+
name: string;
|
|
22
|
+
schedule: string;
|
|
23
|
+
};
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.config = void 0;
|
|
4
|
+
exports.default = abandonedCartRecovery;
|
|
5
|
+
exports.runBatch = runBatch;
|
|
6
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
7
|
+
const integration_settings_1 = require("../modules/integration-settings");
|
|
8
|
+
const abandoned_cart_1 = require("../core/abandoned-cart");
|
|
9
|
+
const PAGE_SIZE = 100;
|
|
10
|
+
async function abandonedCartRecovery(container) {
|
|
11
|
+
const locking = container.resolve("locking");
|
|
12
|
+
try {
|
|
13
|
+
return await locking.execute("job:abandoned-cart-recovery", () => runBatch(container), { timeout: 1, expire: 3300 });
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
const logger = container.resolve(utils_1.ContainerRegistrationKeys.LOGGER);
|
|
17
|
+
logger.info(JSON.stringify({
|
|
18
|
+
event: "scheduled_job_skipped_locked",
|
|
19
|
+
job: "abandoned-cart-recovery",
|
|
20
|
+
message: error instanceof Error ? error.message : String(error),
|
|
21
|
+
}));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async function runBatch(container) {
|
|
25
|
+
const settingsStore = container.resolve(integration_settings_1.INTEGRATION_SETTINGS_MODULE);
|
|
26
|
+
const resolved = await (0, integration_settings_1.resolvedIntegrationEnvironment)(settingsStore, "abandoned-cart");
|
|
27
|
+
const settings = (0, abandoned_cart_1.normalizeAbandonedCartConfig)(resolved.env);
|
|
28
|
+
if (!resolved.enabled || !settings.enabled)
|
|
29
|
+
return { scanned: 0, sent: 0 };
|
|
30
|
+
const query = container.resolve(utils_1.ContainerRegistrationKeys.QUERY);
|
|
31
|
+
const cartService = container.resolve(utils_1.Modules.CART);
|
|
32
|
+
const logger = container.resolve(utils_1.ContainerRegistrationKeys.LOGGER);
|
|
33
|
+
const cutoff = new Date(Date.now() - settings.delayHours * 60 * 60 * 1000);
|
|
34
|
+
let cursor = null;
|
|
35
|
+
let scanned = 0;
|
|
36
|
+
let sent = 0;
|
|
37
|
+
while (true) {
|
|
38
|
+
const { data: carts } = await query.graph({
|
|
39
|
+
entity: "cart",
|
|
40
|
+
fields: [
|
|
41
|
+
"id",
|
|
42
|
+
"email",
|
|
43
|
+
"locale",
|
|
44
|
+
"currency_code",
|
|
45
|
+
"updated_at",
|
|
46
|
+
"completed_at",
|
|
47
|
+
"metadata",
|
|
48
|
+
"items.quantity",
|
|
49
|
+
],
|
|
50
|
+
filters: {
|
|
51
|
+
updated_at: { $lte: cutoff },
|
|
52
|
+
email: { $ne: null },
|
|
53
|
+
completed_at: null,
|
|
54
|
+
...(cursor ? { id: { $gt: cursor } } : {}),
|
|
55
|
+
},
|
|
56
|
+
pagination: {
|
|
57
|
+
take: PAGE_SIZE,
|
|
58
|
+
order: { id: "ASC" },
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
if (!carts.length)
|
|
62
|
+
break;
|
|
63
|
+
for (const cart of carts) {
|
|
64
|
+
cursor = cart.id;
|
|
65
|
+
scanned += 1;
|
|
66
|
+
if (!cart.email)
|
|
67
|
+
continue;
|
|
68
|
+
if (cart.completed_at)
|
|
69
|
+
continue;
|
|
70
|
+
if (!Array.isArray(cart.items) || !cart.items.some((item) => Number(item?.quantity) > 0))
|
|
71
|
+
continue;
|
|
72
|
+
if (cart.metadata?.abandoned_cart_recovery_sent_at)
|
|
73
|
+
continue;
|
|
74
|
+
try {
|
|
75
|
+
// Newsletter is the existing marketing-consent source of truth. Do not
|
|
76
|
+
// create a coupon or send a recovery message to unsubscribed addresses.
|
|
77
|
+
const subscriber = await (0, abandoned_cart_1.subscribedNewsletterRecipient)(container, cart.email);
|
|
78
|
+
if (!subscriber)
|
|
79
|
+
continue;
|
|
80
|
+
const existingCoupon = settings.couponEnabled
|
|
81
|
+
? String(cart.metadata?.abandoned_cart_recovery_coupon_code || "").trim() || null
|
|
82
|
+
: null;
|
|
83
|
+
const coupon = settings.couponEnabled && !existingCoupon
|
|
84
|
+
? await (0, abandoned_cart_1.issueAbandonedCartCoupon)({ container: container, cartId: cart.id, config: settings })
|
|
85
|
+
: existingCoupon
|
|
86
|
+
? {
|
|
87
|
+
id: cart.metadata?.abandoned_cart_recovery_promotion_id || null,
|
|
88
|
+
code: existingCoupon,
|
|
89
|
+
existing: true,
|
|
90
|
+
}
|
|
91
|
+
: null;
|
|
92
|
+
if (coupon?.code && !existingCoupon) {
|
|
93
|
+
await cartService.updateCarts({
|
|
94
|
+
id: cart.id,
|
|
95
|
+
metadata: {
|
|
96
|
+
abandoned_cart_recovery_coupon_code: coupon.code,
|
|
97
|
+
abandoned_cart_recovery_promotion_id: coupon.id || null,
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
cart.metadata = {
|
|
101
|
+
...(cart.metadata || {}),
|
|
102
|
+
abandoned_cart_recovery_coupon_code: coupon.code,
|
|
103
|
+
abandoned_cart_recovery_promotion_id: coupon.id || null,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
const result = await (0, abandoned_cart_1.sendAbandonedCartRecoveryEmail)({
|
|
107
|
+
container: container,
|
|
108
|
+
cart,
|
|
109
|
+
config: settings,
|
|
110
|
+
couponCode: coupon?.code || null,
|
|
111
|
+
});
|
|
112
|
+
if (result?.skipped && "reason" in result && result.reason === "not-subscribed")
|
|
113
|
+
continue;
|
|
114
|
+
await cartService.updateCarts({
|
|
115
|
+
id: cart.id,
|
|
116
|
+
metadata: {
|
|
117
|
+
abandoned_cart_recovery_sent_at: new Date().toISOString(),
|
|
118
|
+
abandoned_cart_recovery_delay_hours: settings.delayHours,
|
|
119
|
+
...(coupon?.code ? {
|
|
120
|
+
abandoned_cart_recovery_coupon_code: coupon.code,
|
|
121
|
+
abandoned_cart_recovery_promotion_id: coupon.id || null,
|
|
122
|
+
} : {}),
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
if (!result?.skipped)
|
|
126
|
+
sent += 1;
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
logger.warn(`[abandoned-cart-recovery] ${cart.id}: ${error instanceof Error ? error.message : String(error)}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (carts.length < PAGE_SIZE)
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
return { scanned, sent, delayHours: settings.delayHours };
|
|
136
|
+
}
|
|
137
|
+
exports.config = { name: "abandoned-cart-recovery", schedule: "30 * * * *" };
|
|
138
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWJhbmRvbmVkLWNhcnQtcmVjb3ZlcnkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvam9icy9hYmFuZG9uZWQtY2FydC1yZWNvdmVyeS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUErQkEsd0NBbUJDO0FBRUQsNEJBa0hDO0FBcktELHFEQUE4RTtBQUM5RSwwRUFJd0M7QUFDeEMsMkRBSytCO0FBRS9CLE1BQU0sU0FBUyxHQUFHLEdBQUcsQ0FBQTtBQWlCTixLQUFLLFVBQVUscUJBQXFCLENBQUMsU0FBMEI7SUFDNUUsTUFBTSxPQUFPLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBRTFDLENBQUE7SUFFRCxJQUFJLENBQUM7UUFDSCxPQUFPLE1BQU0sT0FBTyxDQUFDLE9BQU8sQ0FDMUIsNkJBQTZCLEVBQzdCLEdBQUcsRUFBRSxDQUFDLFFBQVEsQ0FBQyxTQUFTLENBQUMsRUFDekIsRUFBRSxPQUFPLEVBQUUsQ0FBQyxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUUsQ0FDN0IsQ0FBQTtJQUNILENBQUM7SUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1FBQ2YsTUFBTSxNQUFNLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FBQyxpQ0FBeUIsQ0FBQyxNQUFNLENBQUMsQ0FBQTtRQUNsRSxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUM7WUFDekIsS0FBSyxFQUFFLDhCQUE4QjtZQUNyQyxHQUFHLEVBQUUseUJBQXlCO1lBQzlCLE9BQU8sRUFBRSxLQUFLLFlBQVksS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDO1NBQ2hFLENBQUMsQ0FBQyxDQUFBO0lBQ0wsQ0FBQztBQUNILENBQUM7QUFFTSxLQUFLLFVBQVUsUUFBUSxDQUFDLFNBQTBCO0lBQ3ZELE1BQU0sYUFBYSxHQUFHLFNBQVMsQ0FBQyxPQUFPLENBQUMsa0RBQTJCLENBQXdDLENBQUE7SUFDM0csTUFBTSxRQUFRLEdBQUcsTUFBTSxJQUFBLHFEQUE4QixFQUFDLGFBQWEsRUFBRSxnQkFBZ0IsQ0FBQyxDQUFBO0lBQ3RGLE1BQU0sUUFBUSxHQUFHLElBQUEsNkNBQTRCLEVBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxDQUFBO0lBQzNELElBQUksQ0FBQyxRQUFRLENBQUMsT0FBTyxJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU87UUFBRSxPQUFPLEVBQUUsT0FBTyxFQUFFLENBQUMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxFQUFFLENBQUE7SUFFMUUsTUFBTSxLQUFLLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FBQyxpQ0FBeUIsQ0FBQyxLQUFLLENBQTBCLENBQUE7SUFDekYsTUFBTSxXQUFXLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FBQyxlQUFPLENBQUMsSUFBSSxDQUFRLENBQUE7SUFDMUQsTUFBTSxNQUFNLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FBQyxpQ0FBeUIsQ0FBQyxNQUFNLENBQUMsQ0FBQTtJQUNsRSxNQUFNLE1BQU0sR0FBRyxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFLEdBQUcsUUFBUSxDQUFDLFVBQVUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQyxDQUFBO0lBQzFFLElBQUksTUFBTSxHQUFrQixJQUFJLENBQUE7SUFDaEMsSUFBSSxPQUFPLEdBQUcsQ0FBQyxDQUFBO0lBQ2YsSUFBSSxJQUFJLEdBQUcsQ0FBQyxDQUFBO0lBRVosT0FBTyxJQUFJLEVBQUUsQ0FBQztRQUNaLE1BQU0sRUFBRSxJQUFJLEVBQUUsS0FBSyxFQUFFLEdBQUcsTUFBTSxLQUFLLENBQUMsS0FBSyxDQUFDO1lBQ3hDLE1BQU0sRUFBRSxNQUFNO1lBQ2QsTUFBTSxFQUFFO2dCQUNOLElBQUk7Z0JBQ0osT0FBTztnQkFDUCxRQUFRO2dCQUNSLGVBQWU7Z0JBQ2YsWUFBWTtnQkFDWixjQUFjO2dCQUNkLFVBQVU7Z0JBQ1YsZ0JBQWdCO2FBQ2pCO1lBQ0QsT0FBTyxFQUFFO2dCQUNQLFVBQVUsRUFBRSxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUU7Z0JBQzVCLEtBQUssRUFBRSxFQUFFLEdBQUcsRUFBRSxJQUFJLEVBQUU7Z0JBQ3BCLFlBQVksRUFBRSxJQUFJO2dCQUNsQixHQUFHLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsRUFBRSxFQUFFLEdBQUcsRUFBRSxNQUFNLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7YUFDM0M7WUFDRCxVQUFVLEVBQUU7Z0JBQ1YsSUFBSSxFQUFFLFNBQVM7Z0JBQ2YsS0FBSyxFQUFFLEVBQUUsRUFBRSxFQUFFLEtBQUssRUFBRTthQUNyQjtTQUNGLENBQUMsQ0FBQTtRQUVGLElBQUksQ0FBQyxLQUFLLENBQUMsTUFBTTtZQUFFLE1BQUs7UUFFeEIsS0FBSyxNQUFNLElBQUksSUFBSSxLQUFLLEVBQUUsQ0FBQztZQUN6QixNQUFNLEdBQUcsSUFBSSxDQUFDLEVBQUUsQ0FBQTtZQUNoQixPQUFPLElBQUksQ0FBQyxDQUFBO1lBQ1osSUFBSSxDQUFDLElBQUksQ0FBQyxLQUFLO2dCQUFFLFNBQVE7WUFDekIsSUFBSSxJQUFJLENBQUMsWUFBWTtnQkFBRSxTQUFRO1lBQy9CLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQyxNQUFNLENBQUMsSUFBSSxFQUFFLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQztnQkFBRSxTQUFRO1lBQ2xHLElBQUksSUFBSSxDQUFDLFFBQVEsRUFBRSwrQkFBK0I7Z0JBQUUsU0FBUTtZQUU1RCxJQUFJLENBQUM7Z0JBQ0gsdUVBQXVFO2dCQUN2RSx3RUFBd0U7Z0JBQ3hFLE1BQU0sVUFBVSxHQUFHLE1BQU0sSUFBQSw4Q0FBNkIsRUFBQyxTQUFnQixFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQTtnQkFDcEYsSUFBSSxDQUFDLFVBQVU7b0JBQUUsU0FBUTtnQkFFekIsTUFBTSxjQUFjLEdBQUcsUUFBUSxDQUFDLGFBQWE7b0JBQzNDLENBQUMsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxtQ0FBbUMsSUFBSSxFQUFFLENBQUMsQ0FBQyxJQUFJLEVBQUUsSUFBSSxJQUFJO29CQUNqRixDQUFDLENBQUMsSUFBSSxDQUFBO2dCQUNSLE1BQU0sTUFBTSxHQUFHLFFBQVEsQ0FBQyxhQUFhLElBQUksQ0FBQyxjQUFjO29CQUN0RCxDQUFDLENBQUMsTUFBTSxJQUFBLHlDQUF3QixFQUFDLEVBQUUsU0FBUyxFQUFFLFNBQWdCLEVBQUUsTUFBTSxFQUFFLElBQUksQ0FBQyxFQUFFLEVBQUUsTUFBTSxFQUFFLFFBQVEsRUFBRSxDQUFDO29CQUNwRyxDQUFDLENBQUMsY0FBYzt3QkFDZCxDQUFDLENBQUM7NEJBQ0UsRUFBRSxFQUFFLElBQUksQ0FBQyxRQUFRLEVBQUUsb0NBQW9DLElBQUksSUFBSTs0QkFDL0QsSUFBSSxFQUFFLGNBQWM7NEJBQ3BCLFFBQVEsRUFBRSxJQUFJO3lCQUNmO3dCQUNILENBQUMsQ0FBQyxJQUFJLENBQUE7Z0JBRVYsSUFBSSxNQUFNLEVBQUUsSUFBSSxJQUFJLENBQUMsY0FBYyxFQUFFLENBQUM7b0JBQ3BDLE1BQU0sV0FBVyxDQUFDLFdBQVcsQ0FBQzt3QkFDNUIsRUFBRSxFQUFFLElBQUksQ0FBQyxFQUFFO3dCQUNYLFFBQVEsRUFBRTs0QkFDUixtQ0FBbUMsRUFBRSxNQUFNLENBQUMsSUFBSTs0QkFDaEQsb0NBQW9DLEVBQUUsTUFBTSxDQUFDLEVBQUUsSUFBSSxJQUFJO3lCQUN4RDtxQkFDRixDQUFDLENBQUE7b0JBQ0YsSUFBSSxDQUFDLFFBQVEsR0FBRzt3QkFDZCxHQUFHLENBQUMsSUFBSSxDQUFDLFFBQVEsSUFBSSxFQUFFLENBQUM7d0JBQ3hCLG1DQUFtQyxFQUFFLE1BQU0sQ0FBQyxJQUFJO3dCQUNoRCxvQ0FBb0MsRUFBRSxNQUFNLENBQUMsRUFBRSxJQUFJLElBQUk7cUJBQ3hELENBQUE7Z0JBQ0gsQ0FBQztnQkFFRCxNQUFNLE1BQU0sR0FBRyxNQUFNLElBQUEsK0NBQThCLEVBQUM7b0JBQ2xELFNBQVMsRUFBRSxTQUFnQjtvQkFDM0IsSUFBSTtvQkFDSixNQUFNLEVBQUUsUUFBUTtvQkFDaEIsVUFBVSxFQUFFLE1BQU0sRUFBRSxJQUFJLElBQUksSUFBSTtpQkFDakMsQ0FBQyxDQUFBO2dCQUNGLElBQUksTUFBTSxFQUFFLE9BQU8sSUFBSSxRQUFRLElBQUksTUFBTSxJQUFJLE1BQU0sQ0FBQyxNQUFNLEtBQUssZ0JBQWdCO29CQUFFLFNBQVE7Z0JBRXpGLE1BQU0sV0FBVyxDQUFDLFdBQVcsQ0FBQztvQkFDNUIsRUFBRSxFQUFFLElBQUksQ0FBQyxFQUFFO29CQUNYLFFBQVEsRUFBRTt3QkFDUiwrQkFBK0IsRUFBRSxJQUFJLElBQUksRUFBRSxDQUFDLFdBQVcsRUFBRTt3QkFDekQsbUNBQW1DLEVBQUUsUUFBUSxDQUFDLFVBQVU7d0JBQ3hELEdBQUcsQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQzs0QkFDakIsbUNBQW1DLEVBQUUsTUFBTSxDQUFDLElBQUk7NEJBQ2hELG9DQUFvQyxFQUFFLE1BQU0sQ0FBQyxFQUFFLElBQUksSUFBSTt5QkFDeEQsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO3FCQUNSO2lCQUNGLENBQUMsQ0FBQTtnQkFDRixJQUFJLENBQUMsTUFBTSxFQUFFLE9BQU87b0JBQUUsSUFBSSxJQUFJLENBQUMsQ0FBQTtZQUNqQyxDQUFDO1lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztnQkFDZixNQUFNLENBQUMsSUFBSSxDQUNULDZCQUE2QixJQUFJLENBQUMsRUFBRSxLQUFLLEtBQUssWUFBWSxLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUNsRyxDQUFBO1lBQ0gsQ0FBQztRQUNILENBQUM7UUFFRCxJQUFJLEtBQUssQ0FBQyxNQUFNLEdBQUcsU0FBUztZQUFFLE1BQUs7SUFDckMsQ0FBQztJQUVELE9BQU8sRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLFVBQVUsRUFBRSxRQUFRLENBQUMsVUFBVSxFQUFFLENBQUE7QUFDM0QsQ0FBQztBQUVZLFFBQUEsTUFBTSxHQUFHLEVBQUUsSUFBSSxFQUFFLHlCQUF5QixFQUFFLFFBQVEsRUFBRSxZQUFZLEVBQUUsQ0FBQSJ9
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { MedusaContainer } from "@medusajs/framework/types";
|
|
2
|
+
export default function backInStockReconciliation(container: MedusaContainer): Promise<{
|
|
3
|
+
scanned: number;
|
|
4
|
+
variants: number;
|
|
5
|
+
sent: number;
|
|
6
|
+
} | undefined>;
|
|
7
|
+
export declare function runBatch(container: MedusaContainer): Promise<{
|
|
8
|
+
scanned: number;
|
|
9
|
+
variants: number;
|
|
10
|
+
sent: number;
|
|
11
|
+
}>;
|
|
12
|
+
export declare const config: {
|
|
13
|
+
name: string;
|
|
14
|
+
schedule: string;
|
|
15
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.config = void 0;
|
|
4
|
+
exports.default = backInStockReconciliation;
|
|
5
|
+
exports.runBatch = runBatch;
|
|
6
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
7
|
+
const back_in_stock_1 = require("../core/back-in-stock");
|
|
8
|
+
const back_in_stock_2 = require("../modules/back-in-stock");
|
|
9
|
+
const SUBSCRIPTION_PAGE_SIZE = 1_000;
|
|
10
|
+
const VARIANT_BATCH_SIZE = 250;
|
|
11
|
+
async function backInStockReconciliation(container) {
|
|
12
|
+
const locking = container.resolve("locking");
|
|
13
|
+
try {
|
|
14
|
+
return await locking.execute("job:back-in-stock-reconciliation", () => runBatch(container), { timeout: 1, expire: 840 });
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
const logger = container.resolve(utils_1.ContainerRegistrationKeys.LOGGER);
|
|
18
|
+
logger.info(JSON.stringify({
|
|
19
|
+
event: "scheduled_job_skipped_locked",
|
|
20
|
+
job: "back-in-stock-reconciliation",
|
|
21
|
+
message: error instanceof Error ? error.message : String(error),
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
async function runBatch(container) {
|
|
26
|
+
const service = container.resolve(back_in_stock_2.BACK_IN_STOCK_MODULE);
|
|
27
|
+
const query = container.resolve(utils_1.ContainerRegistrationKeys.QUERY);
|
|
28
|
+
const locking = container.resolve("locking");
|
|
29
|
+
const logger = container.resolve(utils_1.ContainerRegistrationKeys.LOGGER);
|
|
30
|
+
// Build the active waiting-list snapshot before changing any subscription
|
|
31
|
+
// statuses. This avoids offset pagination skipping rows as notifications turn
|
|
32
|
+
// active records into notified records during the dispatch phase.
|
|
33
|
+
const variantIds = new Set();
|
|
34
|
+
let scanned = 0;
|
|
35
|
+
for (let skip = 0;; skip += SUBSCRIPTION_PAGE_SIZE) {
|
|
36
|
+
const subscriptions = await service.listBackInStockSubscriptions({ status: "active" }, { take: SUBSCRIPTION_PAGE_SIZE, skip, order: { created_at: "ASC" } });
|
|
37
|
+
scanned += subscriptions.length;
|
|
38
|
+
for (const subscription of subscriptions) {
|
|
39
|
+
const variantId = String(subscription.variant_id || "").trim();
|
|
40
|
+
if (variantId)
|
|
41
|
+
variantIds.add(variantId);
|
|
42
|
+
}
|
|
43
|
+
if (subscriptions.length < SUBSCRIPTION_PAGE_SIZE)
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
const ids = [...variantIds];
|
|
47
|
+
let sent = 0;
|
|
48
|
+
for (let offset = 0; offset < ids.length; offset += VARIANT_BATCH_SIZE) {
|
|
49
|
+
const batchIds = ids.slice(offset, offset + VARIANT_BATCH_SIZE);
|
|
50
|
+
const { data: variants } = await query.graph({
|
|
51
|
+
entity: "product_variant",
|
|
52
|
+
fields: [
|
|
53
|
+
"id",
|
|
54
|
+
"title",
|
|
55
|
+
"sku",
|
|
56
|
+
"manage_inventory",
|
|
57
|
+
"allow_backorder",
|
|
58
|
+
"metadata",
|
|
59
|
+
"product.id",
|
|
60
|
+
"product.title",
|
|
61
|
+
"product.handle",
|
|
62
|
+
"product.metadata",
|
|
63
|
+
],
|
|
64
|
+
filters: { id: batchIds },
|
|
65
|
+
});
|
|
66
|
+
for (const variant of variants) {
|
|
67
|
+
try {
|
|
68
|
+
const result = await locking.execute(`back-in-stock:variant:${variant.id}`, () => (0, back_in_stock_1.dispatchBackInStockVariant)({
|
|
69
|
+
container: container,
|
|
70
|
+
query,
|
|
71
|
+
variant,
|
|
72
|
+
}), { timeout: 1, expire: 300 });
|
|
73
|
+
sent += result.sent;
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
logger.info(JSON.stringify({
|
|
77
|
+
event: "back_in_stock_variant_skipped_or_failed",
|
|
78
|
+
variant_id: variant.id,
|
|
79
|
+
message: error instanceof Error ? error.message : String(error),
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return { scanned, variants: ids.length, sent };
|
|
85
|
+
}
|
|
86
|
+
exports.config = {
|
|
87
|
+
name: "back-in-stock-reconciliation",
|
|
88
|
+
schedule: "*/15 * * * *",
|
|
89
|
+
};
|
|
90
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmFjay1pbi1zdG9jay1yZWNvbmNpbGlhdGlvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9qb2JzL2JhY2staW4tc3RvY2stcmVjb25jaWxpYXRpb24udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBWUEsNENBZ0JDO0FBRUQsNEJBdUVDO0FBcEdELHFEQUFxRTtBQUNyRSx5REFBMkY7QUFDM0YsNERBQXdGO0FBRXhGLE1BQU0sc0JBQXNCLEdBQUcsS0FBSyxDQUFBO0FBQ3BDLE1BQU0sa0JBQWtCLEdBQUcsR0FBRyxDQUFBO0FBTWYsS0FBSyxVQUFVLHlCQUF5QixDQUFDLFNBQTBCO0lBQ2hGLE1BQU0sT0FBTyxHQUFHLFNBQVMsQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFZLENBQUE7SUFDdkQsSUFBSSxDQUFDO1FBQ0gsT0FBTyxNQUFNLE9BQU8sQ0FBQyxPQUFPLENBQzFCLGtDQUFrQyxFQUNsQyxHQUFHLEVBQUUsQ0FBQyxRQUFRLENBQUMsU0FBUyxDQUFDLEVBQ3pCLEVBQUUsT0FBTyxFQUFFLENBQUMsRUFBRSxNQUFNLEVBQUUsR0FBRyxFQUFFLENBQzVCLENBQUE7SUFDSCxDQUFDO0lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztRQUNmLE1BQU0sTUFBTSxHQUFHLFNBQVMsQ0FBQyxPQUFPLENBQUMsaUNBQXlCLENBQUMsTUFBTSxDQUFDLENBQUE7UUFDbEUsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDO1lBQ3pCLEtBQUssRUFBRSw4QkFBOEI7WUFDckMsR0FBRyxFQUFFLDhCQUE4QjtZQUNuQyxPQUFPLEVBQUUsS0FBSyxZQUFZLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQztTQUNoRSxDQUFDLENBQUMsQ0FBQTtJQUNMLENBQUM7QUFDSCxDQUFDO0FBRU0sS0FBSyxVQUFVLFFBQVEsQ0FBQyxT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
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { MedusaContainer } from "@medusajs/framework/types";
|
|
2
|
+
export default function dpdStatusdataSync(container: MedusaContainer): Promise<{
|
|
3
|
+
files: number;
|
|
4
|
+
processedFiles: number;
|
|
5
|
+
records: number;
|
|
6
|
+
matched: number;
|
|
7
|
+
} | undefined>;
|
|
8
|
+
export declare function runBatch(container: MedusaContainer): Promise<{
|
|
9
|
+
files: number;
|
|
10
|
+
processedFiles: number;
|
|
11
|
+
records: number;
|
|
12
|
+
matched: number;
|
|
13
|
+
}>;
|
|
14
|
+
export declare const config: {
|
|
15
|
+
name: string;
|
|
16
|
+
schedule: string;
|
|
17
|
+
};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.config = void 0;
|
|
4
|
+
exports.default = dpdStatusdataSync;
|
|
5
|
+
exports.runBatch = runBatch;
|
|
6
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
7
|
+
const dpd_1 = require("../providers/dpd");
|
|
8
|
+
const integration_operation_1 = require("../modules/integration-operation");
|
|
9
|
+
const state_1 = require("../modules/integration-operation/state");
|
|
10
|
+
const shipping_1 = require("../lib/modules/shipping");
|
|
11
|
+
async function dpdStatusdataSync(container) {
|
|
12
|
+
if (process.env.DPD_STATUSDATA_ENABLED !== "true")
|
|
13
|
+
return;
|
|
14
|
+
const locking = container.resolve("locking");
|
|
15
|
+
try {
|
|
16
|
+
return await locking.execute("job:dpd-statusdata-sync", () => runBatch(container), { timeout: 1, expire: 14 * 60 });
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
const logger = container.resolve(utils_1.ContainerRegistrationKeys.LOGGER);
|
|
20
|
+
logger.info(JSON.stringify({
|
|
21
|
+
event: "scheduled_job_skipped_locked",
|
|
22
|
+
job: "dpd-statusdata-sync",
|
|
23
|
+
message: error instanceof Error ? error.message : String(error),
|
|
24
|
+
}));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async function runBatch(container) {
|
|
28
|
+
const logger = container.resolve(utils_1.ContainerRegistrationKeys.LOGGER);
|
|
29
|
+
const orderService = container.resolve("order");
|
|
30
|
+
const operationStore = container.resolve(integration_operation_1.INTEGRATION_OPERATION_MODULE);
|
|
31
|
+
const client = new dpd_1.DpdStatusdataFtpClient((0, dpd_1.dpdStatusdataFtpConfigFromEnv)());
|
|
32
|
+
const files = (await client.listFiles()).sort();
|
|
33
|
+
let processedFiles = 0;
|
|
34
|
+
let records = 0;
|
|
35
|
+
let matched = 0;
|
|
36
|
+
for (const filename of files) {
|
|
37
|
+
const acquired = await (0, state_1.acquireOperation)(operationStore, {
|
|
38
|
+
provider: "dpd",
|
|
39
|
+
operation_type: "statusdata.file",
|
|
40
|
+
entity_type: "statusdata_file",
|
|
41
|
+
entity_id: filename,
|
|
42
|
+
idempotency_key: `dpd:statusdata:file:${filename}`,
|
|
43
|
+
reference_data: {
|
|
44
|
+
replay_safe: true,
|
|
45
|
+
filename,
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
if (!acquired.acquired) {
|
|
49
|
+
if (acquired.operation.status === "succeeded")
|
|
50
|
+
continue;
|
|
51
|
+
logger.info(`[dpd-statusdata-sync] ${filename}: a fájl már feldolgozás alatt áll.`);
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const content = await client.readFile(filename);
|
|
56
|
+
const parsed = (0, dpd_1.parseDpdStatusdata)(content);
|
|
57
|
+
let fileMatched = 0;
|
|
58
|
+
let fileUnmatched = 0;
|
|
59
|
+
for (const record of parsed) {
|
|
60
|
+
const result = await (0, shipping_1.applyNormalizedTrackingEvent)({
|
|
61
|
+
orderService,
|
|
62
|
+
operationStore,
|
|
63
|
+
event: (0, dpd_1.dpdStatusdataEvent)(record),
|
|
64
|
+
});
|
|
65
|
+
if (result)
|
|
66
|
+
fileMatched += 1;
|
|
67
|
+
else
|
|
68
|
+
fileUnmatched += 1;
|
|
69
|
+
}
|
|
70
|
+
if (fileUnmatched > 0) {
|
|
71
|
+
// Keep the file replayable. Rows already applied above are independently
|
|
72
|
+
// idempotent, so a later retry only has to match the previously missing rows.
|
|
73
|
+
throw new Error(`${fileUnmatched} DPD Statusdata eseményhez még nem található rendelés.`);
|
|
74
|
+
}
|
|
75
|
+
await (0, state_1.operationSucceeded)(operationStore, acquired.operation, filename, {
|
|
76
|
+
replay_safe: true,
|
|
77
|
+
filename,
|
|
78
|
+
record_count: parsed.length,
|
|
79
|
+
matched_count: fileMatched,
|
|
80
|
+
processed_at: new Date().toISOString(),
|
|
81
|
+
});
|
|
82
|
+
processedFiles += 1;
|
|
83
|
+
records += parsed.length;
|
|
84
|
+
matched += fileMatched;
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
await (0, state_1.operationFailed)(operationStore, acquired.operation, error, false, filename, {
|
|
88
|
+
replay_safe: true,
|
|
89
|
+
filename,
|
|
90
|
+
});
|
|
91
|
+
logger.warn(`[dpd-statusdata-sync] ${filename}: ${error instanceof Error ? error.message : String(error)}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
files: files.length,
|
|
96
|
+
processedFiles,
|
|
97
|
+
records,
|
|
98
|
+
matched,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
// DPD Hungary generates Statusdata when statuses changed (hourly). Poll the
|
|
102
|
+
// dedicated FTP every 15 minutes so a newly generated file is picked up quickly.
|
|
103
|
+
exports.config = {
|
|
104
|
+
name: "dpd-statusdata-sync",
|
|
105
|
+
schedule: "*/15 * * * *",
|
|
106
|
+
};
|
|
107
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZHBkLXN0YXR1c2RhdGEtc3luYy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9qb2JzL2RwZC1zdGF0dXNkYXRhLXN5bmMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBc0JBLG9DQXlCQztBQUVELDRCQStGQztBQS9JRCxxREFBcUU7QUFDckUsMENBS3lCO0FBQ3pCLDRFQUd5QztBQUN6QyxrRUFJK0M7QUFDL0Msc0RBR2dDO0FBRWpCLEtBQUssVUFBVSxpQkFBaUIsQ0FBQyxTQUEwQjtJQUN4RSxJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMsc0JBQXNCLEtBQUssTUFBTTtRQUFFLE9BQU07SUFFekQsTUFBTSxPQUFPLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBTTFDLENBQUE7SUFFRCxJQUFJLENBQUM7UUFDSCxPQUFPLE1BQU0sT0FBTyxDQUFDLE9BQU8sQ0FDMUIseUJBQXlCLEVBQ3pCLEdBQUcsRUFBRSxDQUFDLFFBQVEsQ0FBQyxTQUFTLENBQUMsRUFDekIsRUFBRSxPQUFPLEVBQUUsQ0FBQyxFQUFFLE1BQU0sRUFBRSxFQUFFLEdBQUcsRUFBRSxFQUFFLENBQ2hDLENBQUE7SUFDSCxDQUFDO0lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztRQUNmLE1BQU0sTUFBTSxHQUFHLFNBQVMsQ0FBQyxPQUFPLENBQUMsaUNBQXlCLENBQUMsTUFBTSxDQUFDLENBQUE7UUFDbEUsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDO1lBQ3pCLEtBQUssRUFBRSw4QkFBOEI7WUFDckMsR0FBRyxFQUFFLHFCQUFxQjtZQUMxQixPQUFPLEVBQUUsS0FBSyxZQUFZLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQztTQUNoRSxDQUFDLENBQUMsQ0FBQTtJQUNMLENBQUM7QUFDSCxDQUFDO0FBRU0sS0FBSyxVQUFV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
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { MedusaContainer } from "@medusajs/framework/types";
|
|
2
|
+
export default function mplPushTracking(container: MedusaContainer): Promise<{
|
|
3
|
+
records: number;
|
|
4
|
+
applied: number;
|
|
5
|
+
malformed: number;
|
|
6
|
+
unmatched: number;
|
|
7
|
+
lastRead: string | null;
|
|
8
|
+
} | undefined>;
|
|
9
|
+
export declare function runBatch(container: MedusaContainer): Promise<{
|
|
10
|
+
records: number;
|
|
11
|
+
applied: number;
|
|
12
|
+
malformed: number;
|
|
13
|
+
unmatched: number;
|
|
14
|
+
lastRead: string | null;
|
|
15
|
+
}>;
|
|
16
|
+
export declare const config: {
|
|
17
|
+
name: string;
|
|
18
|
+
schedule: string;
|
|
19
|
+
};
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.config = void 0;
|
|
4
|
+
exports.default = mplPushTracking;
|
|
5
|
+
exports.runBatch = runBatch;
|
|
6
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
7
|
+
const mpl_1 = require("../providers/mpl");
|
|
8
|
+
const integration_operation_1 = require("../modules/integration-operation");
|
|
9
|
+
const shipping_1 = require("../lib/modules/shipping");
|
|
10
|
+
const STATE_KEY = "mpl:tracking:push:subscription";
|
|
11
|
+
const TOKEN_RENEW_AFTER_MS = 23 * 60 * 60 * 1000;
|
|
12
|
+
async function mplPushTracking(container) {
|
|
13
|
+
if (process.env.MPL_ENABLED !== "true" ||
|
|
14
|
+
process.env.MPL_PUSH_TRACKING_ENABLED !== "true") {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const locking = container.resolve("locking");
|
|
18
|
+
try {
|
|
19
|
+
return await locking.execute("job:mpl-push-tracking", () => runBatch(container), { timeout: 1, expire: 14 * 60 });
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
const logger = container.resolve(utils_1.ContainerRegistrationKeys.LOGGER);
|
|
23
|
+
logger.info(JSON.stringify({
|
|
24
|
+
event: "scheduled_job_skipped_locked",
|
|
25
|
+
job: "mpl-push-tracking",
|
|
26
|
+
message: error instanceof Error ? error.message : String(error),
|
|
27
|
+
}));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async function runBatch(container) {
|
|
31
|
+
const operationStore = container.resolve(integration_operation_1.INTEGRATION_OPERATION_MODULE);
|
|
32
|
+
const orderService = container.resolve("order");
|
|
33
|
+
const logger = container.resolve(utils_1.ContainerRegistrationKeys.LOGGER);
|
|
34
|
+
const state = await loadOrCreateState(operationStore);
|
|
35
|
+
const reference = state.reference_data && typeof state.reference_data === "object"
|
|
36
|
+
? state.reference_data
|
|
37
|
+
: {};
|
|
38
|
+
let subscriptionToken = text(reference.subscription_token);
|
|
39
|
+
let subscribedAt = dateValue(reference.subscribed_at);
|
|
40
|
+
let lastRead = text(reference.last_read) || undefined;
|
|
41
|
+
const tokenExpired = !subscriptionToken || !subscribedAt ||
|
|
42
|
+
Date.now() - subscribedAt.getTime() >= TOKEN_RENEW_AFTER_MS;
|
|
43
|
+
if (tokenExpired) {
|
|
44
|
+
subscriptionToken = await mpl_1.mplService.client.subscribePushTracking();
|
|
45
|
+
subscribedAt = new Date();
|
|
46
|
+
// lastRead is intentionally preserved across subscription renewal. MPL keeps
|
|
47
|
+
// events for 24 hours and officially supports reconnecting with lastRead.
|
|
48
|
+
}
|
|
49
|
+
const records = await mpl_1.mplService.client.readPushTracking(subscriptionToken, lastRead);
|
|
50
|
+
let applied = 0;
|
|
51
|
+
let malformed = 0;
|
|
52
|
+
let unmatched = 0;
|
|
53
|
+
let newestLastRead = lastRead;
|
|
54
|
+
let blockedPackageId;
|
|
55
|
+
let blockedInsertedAt;
|
|
56
|
+
for (const record of records) {
|
|
57
|
+
const packageId = text(record.PackageId);
|
|
58
|
+
const insertedAt = text(record.InsertedAt) || undefined;
|
|
59
|
+
if (!packageId) {
|
|
60
|
+
malformed += 1;
|
|
61
|
+
logger.warn("[mpl-push-tracking] MPL PUSH rekord PackageId nélkül; a rekord kihagyva.");
|
|
62
|
+
// A malformed provider record can never be correlated. Advancing over it
|
|
63
|
+
// prevents one invalid record from permanently blocking the stream.
|
|
64
|
+
newestLastRead = laterTimestamp(newestLastRead, insertedAt) || newestLastRead;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const occurredAt = text(record.EventDateTime) || undefined;
|
|
68
|
+
const status = (0, shipping_1.mapMplPushStatus)(record.PackageStatus, record.PackageStatusText);
|
|
69
|
+
const result = await (0, shipping_1.applyNormalizedTrackingEvent)({
|
|
70
|
+
orderService,
|
|
71
|
+
operationStore,
|
|
72
|
+
event: {
|
|
73
|
+
carrier: "mpl",
|
|
74
|
+
shipmentIdentifiers: [packageId],
|
|
75
|
+
status,
|
|
76
|
+
rawStatus: text(record.PackageStatus) || null,
|
|
77
|
+
statusText: text(record.PackageStatusText) || null,
|
|
78
|
+
occurredAt: occurredAt || insertedAt || null,
|
|
79
|
+
eventId: text(record.TrackingRecordId) || null,
|
|
80
|
+
source: "push",
|
|
81
|
+
trackingNumber: packageId,
|
|
82
|
+
extra: {
|
|
83
|
+
lastEvent: text(record.PackageStatusText) || undefined,
|
|
84
|
+
lastEventCategory: text(record.PackageStatus) || undefined,
|
|
85
|
+
lastEventAt: occurredAt || insertedAt,
|
|
86
|
+
trackingSource: "push",
|
|
87
|
+
eventPlace: text(record.EventPlace) || undefined,
|
|
88
|
+
insertedAt,
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
if (!result) {
|
|
93
|
+
// Do not checkpoint past an event whose order is not visible yet. On the
|
|
94
|
+
// next reconnect MPL replays it from lastRead; the failed event operation
|
|
95
|
+
// itself is retry-safe through the existing idempotency machinery.
|
|
96
|
+
unmatched += 1;
|
|
97
|
+
blockedPackageId = packageId;
|
|
98
|
+
blockedInsertedAt = insertedAt;
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
applied += 1;
|
|
102
|
+
newestLastRead = laterTimestamp(newestLastRead, insertedAt) || newestLastRead;
|
|
103
|
+
}
|
|
104
|
+
await operationStore.updateIntegrationOperations({
|
|
105
|
+
id: state.id,
|
|
106
|
+
status: "succeeded",
|
|
107
|
+
error_summary: unmatched
|
|
108
|
+
? "Az MPL PUSH cursor egy még nem párosítható esemény előtt megállt; a következő futás újrapróbálja."
|
|
109
|
+
: null,
|
|
110
|
+
completed_at: new Date(),
|
|
111
|
+
reference_data: {
|
|
112
|
+
replay_safe: true,
|
|
113
|
+
subscription_token: subscriptionToken,
|
|
114
|
+
subscribed_at: subscribedAt?.toISOString() || new Date().toISOString(),
|
|
115
|
+
last_read: newestLastRead || null,
|
|
116
|
+
last_poll_at: new Date().toISOString(),
|
|
117
|
+
last_record_count: records.length,
|
|
118
|
+
last_applied_count: applied,
|
|
119
|
+
last_malformed_count: malformed,
|
|
120
|
+
last_unmatched_count: unmatched,
|
|
121
|
+
blocked_package_id: blockedPackageId || null,
|
|
122
|
+
blocked_inserted_at: blockedInsertedAt || null,
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
return {
|
|
126
|
+
records: records.length,
|
|
127
|
+
applied,
|
|
128
|
+
malformed,
|
|
129
|
+
unmatched,
|
|
130
|
+
lastRead: newestLastRead || null,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
async function loadOrCreateState(store) {
|
|
134
|
+
const [existing] = await store.listIntegrationOperations({ idempotency_key: STATE_KEY }, { take: 1 });
|
|
135
|
+
if (existing)
|
|
136
|
+
return existing;
|
|
137
|
+
try {
|
|
138
|
+
return await store.createIntegrationOperations({
|
|
139
|
+
provider: "mpl",
|
|
140
|
+
operation_type: "tracking.push.subscription",
|
|
141
|
+
entity_type: "tracking_subscription",
|
|
142
|
+
entity_id: "global",
|
|
143
|
+
idempotency_key: STATE_KEY,
|
|
144
|
+
status: "succeeded",
|
|
145
|
+
attempts: 1,
|
|
146
|
+
error_summary: null,
|
|
147
|
+
next_attempt_at: null,
|
|
148
|
+
completed_at: new Date(),
|
|
149
|
+
reference_data: { replay_safe: true },
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
const [raced] = await store.listIntegrationOperations({ idempotency_key: STATE_KEY }, { take: 1 });
|
|
154
|
+
if (raced)
|
|
155
|
+
return raced;
|
|
156
|
+
throw error;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
function text(value) {
|
|
160
|
+
return value == null ? "" : String(value).trim();
|
|
161
|
+
}
|
|
162
|
+
function dateValue(value) {
|
|
163
|
+
const date = value ? new Date(String(value)) : null;
|
|
164
|
+
return date && !Number.isNaN(date.getTime()) ? date : null;
|
|
165
|
+
}
|
|
166
|
+
function laterTimestamp(left, right) {
|
|
167
|
+
if (!right)
|
|
168
|
+
return left;
|
|
169
|
+
if (!left)
|
|
170
|
+
return right;
|
|
171
|
+
const leftTime = Date.parse(left);
|
|
172
|
+
const rightTime = Date.parse(right);
|
|
173
|
+
if (!Number.isFinite(leftTime))
|
|
174
|
+
return right;
|
|
175
|
+
if (!Number.isFinite(rightTime))
|
|
176
|
+
return left;
|
|
177
|
+
return rightTime > leftTime ? right : left;
|
|
178
|
+
}
|
|
179
|
+
exports.config = {
|
|
180
|
+
name: "mpl-push-tracking",
|
|
181
|
+
schedule: "*/15 * * * *",
|
|
182
|
+
};
|
|
183
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibXBsLXB1c2gtdHJhY2tpbmcuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvam9icy9tcGwtcHVzaC10cmFja2luZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFnQkEsa0NBOEJDO0FBRUQsNEJBa0hDO0FBaktELHFEQUFxRTtBQUNyRSwwQ0FBNkM7QUFDN0MsNEVBR3lDO0FBQ3pDLHNEQUlnQztBQUVoQyxNQUFNLFNBQVMsR0FBRyxnQ0FBZ0MsQ0FBQTtBQUNsRCxNQUFNLG9CQUFvQixHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQTtBQUVqQyxLQUFLLFVBQVUsZUFBZSxDQUFDLFNBQTBCO0lBQ3RFLElBQ0UsT0FBTyxDQUFDLEdBQUcsQ0FBQyxXQUFXLEtBQUssTUFBTTtRQUNsQyxPQUFPLENBQUMsR0FBRyxDQUFDLHlCQUF5QixLQUFLLE1BQU0sRUFDaEQsQ0FBQztRQUNELE9BQU07SUFDUixDQUFDO0lBRUQsTUFBTSxPQUFPLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBTTFDLENBQUE7SUFFRCxJQUFJLENBQUM7UUFDSCxPQUFPLE1BQU0sT0FBTyxDQUFDLE9BQU8sQ0FDMUIsdUJBQXVCLEVBQ3ZCLEdBQUcsRUFBRSxDQUFDLFFBQVEsQ0FBQyxTQUFTLENBQUMsRUFDekIsRUFBRSxPQUFPLEVBQUUsQ0FBQyxFQUFFLE1BQU0sRUFBRSxFQUFFLEdBQUcsRUFBRSxFQUFFLENBQ2hDLENBQUE7SUFDSCxDQUFDO0lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztRQUNmLE1BQU0sTUFBTSxHQUFHLFNBQVMsQ0FBQyxPQUFPLENBQUMsaUNBQXlCLENBQUMsTUFBTSxDQUFDLENBQUE7UUFDbEUsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDO1lBQ3pCLEtBQUssRUFBRSw4QkFBOEI7WUFDckMsR0FBRyxFQUFFLG1CQUFtQjtZQUN4QixPQUFPLEVB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
|