@legenki/print2medusa 0.2.0 → 0.4.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 +29 -8
- package/.medusa/server/src/admin/index.mjs +29 -8
- package/.medusa/server/src/api/admin/printful/sync/route.js +36 -6
- package/.medusa/server/src/jobs/sync-products.js +17 -3
- package/.medusa/server/src/modules/printful/migrations/Migration20260802000000.js +40 -0
- package/.medusa/server/src/modules/printful/models/printful-sync-log.js +4 -1
- package/.medusa/server/src/modules/printful/service.js +67 -1
- package/.medusa/server/src/providers/printful-fulfillment/service.js +272 -15
- package/.medusa/server/src/utils/mappers.js +19 -3
- package/.medusa/server/src/utils/orphans.js +47 -0
- package/.medusa/server/src/utils/printful-client.js +15 -1
- package/.medusa/server/src/utils/shipping-rates.js +156 -0
- package/.medusa/server/src/utils/stock.js +85 -0
- package/.medusa/server/src/workflows/sync-products.js +61 -22
- package/README.md +117 -19
- package/package.json +2 -2
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LEGACY_OPTION_IDS = exports.PRINTFUL_RETURN_OPTION_ID = exports.PRINTFUL_SHIPPING_METHODS = void 0;
|
|
4
|
+
exports.buildRateCacheKey = buildRateCacheKey;
|
|
5
|
+
exports.selectRate = selectRate;
|
|
6
|
+
exports.isAddressQuotable = isAddressQuotable;
|
|
7
|
+
exports.buildRateItems = buildRateItems;
|
|
8
|
+
const crypto_1 = require("crypto");
|
|
9
|
+
const mappers_1 = require("./mappers");
|
|
10
|
+
/**
|
|
11
|
+
* Shipping methods we expose as fulfillment options.
|
|
12
|
+
*
|
|
13
|
+
* These are Printful's own `ShippingInfo.id` values. The OpenAPI spec publishes
|
|
14
|
+
* no enum — only the example `STANDARD` — so this list comes from the recorded
|
|
15
|
+
* fixture and grows when a live response reveals more.
|
|
16
|
+
*
|
|
17
|
+
* The same string serves as the Medusa option id and the fallbackShippingRates
|
|
18
|
+
* key, so the three cannot drift apart.
|
|
19
|
+
*/
|
|
20
|
+
exports.PRINTFUL_SHIPPING_METHODS = ["STANDARD"];
|
|
21
|
+
/**
|
|
22
|
+
* The return option id.
|
|
23
|
+
*
|
|
24
|
+
* Not a Printful method id: Printful's rate API quotes outbound shipping only,
|
|
25
|
+
* so a return has no live rate and must be priced flat by the admin.
|
|
26
|
+
*/
|
|
27
|
+
exports.PRINTFUL_RETURN_OPTION_ID = "PRINTFUL_RETURN";
|
|
28
|
+
/**
|
|
29
|
+
* Option ids used before 0.3.0. Printful never returns these, so an option row
|
|
30
|
+
* surviving the upgrade prices at zero rather than from a live quote.
|
|
31
|
+
*/
|
|
32
|
+
exports.LEGACY_OPTION_IDS = new Set([
|
|
33
|
+
"printful-standard",
|
|
34
|
+
"printful-return",
|
|
35
|
+
]);
|
|
36
|
+
function normalize(value) {
|
|
37
|
+
return (value ?? "").trim().toLowerCase();
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Cache key for a rate quote.
|
|
41
|
+
*
|
|
42
|
+
* Deliberately excludes the shipping method: Printful returns every method in
|
|
43
|
+
* one response, so a store offering two options costs one API call rather than
|
|
44
|
+
* two. `selectRate` picks the method from the cached list locally.
|
|
45
|
+
*
|
|
46
|
+
* Carts that differ only incidentally — item order, address casing, stray
|
|
47
|
+
* whitespace — must produce the same key, or the cache never hits.
|
|
48
|
+
*/
|
|
49
|
+
function buildRateCacheKey(input) {
|
|
50
|
+
// JSON-encode rather than joining with delimiters: a "|" typed into an
|
|
51
|
+
// address field would otherwise merge two fields and collide with a
|
|
52
|
+
// genuinely different address.
|
|
53
|
+
const canonical = JSON.stringify([
|
|
54
|
+
normalize(input.address.country_code),
|
|
55
|
+
normalize(input.address.state_code),
|
|
56
|
+
normalize(input.address.city),
|
|
57
|
+
normalize(input.address.zip),
|
|
58
|
+
normalize(input.address.address1),
|
|
59
|
+
normalize(input.address.address2),
|
|
60
|
+
// `value` is part of the request Printful prices against — it drives
|
|
61
|
+
// duties on international shipments — so it must be part of the identity
|
|
62
|
+
// of the quote, or two carts differing only in price share one answer.
|
|
63
|
+
[...input.items]
|
|
64
|
+
.map((i) => `${i.variant_id}x${i.quantity}x${i.value ?? ""}`)
|
|
65
|
+
.sort(),
|
|
66
|
+
normalize(input.currency),
|
|
67
|
+
]);
|
|
68
|
+
return (0, crypto_1.createHash)("sha256").update(canonical).digest("hex");
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Pick one method out of a quote list.
|
|
72
|
+
*
|
|
73
|
+
* Printful sends `rate` as a decimal string; `parsePriceToMinorUnits` rounds it
|
|
74
|
+
* correctly, where `parseFloat("4.99") * 100` would yield 498.99999999999994.
|
|
75
|
+
*
|
|
76
|
+
* A quote in a currency the cart cannot use is rejected rather than converted —
|
|
77
|
+
* we never source an exchange rate ourselves.
|
|
78
|
+
*/
|
|
79
|
+
function selectRate(rates, methodId, cartCurrency) {
|
|
80
|
+
const match = rates.find((r) => r.id === methodId);
|
|
81
|
+
if (!match) {
|
|
82
|
+
return { ok: false, reason: "method_unavailable" };
|
|
83
|
+
}
|
|
84
|
+
const quoteCurrency = match.currency.trim().toUpperCase();
|
|
85
|
+
const wantCurrency = cartCurrency.trim().toUpperCase();
|
|
86
|
+
if (!quoteCurrency || !wantCurrency || quoteCurrency !== wantCurrency) {
|
|
87
|
+
return { ok: false, reason: "currency_mismatch" };
|
|
88
|
+
}
|
|
89
|
+
// parseFloat stops at the first invalid character, so "4.99abc" would parse
|
|
90
|
+
// as 4.99 and "-4.99" as a negative price. Require the whole string to be a
|
|
91
|
+
// non-negative decimal; "0.00" is a legitimate free method and must pass.
|
|
92
|
+
if (!/^\d+(\.\d+)?$/.test(match.rate.trim())) {
|
|
93
|
+
return { ok: false, reason: "method_unavailable" };
|
|
94
|
+
}
|
|
95
|
+
return { ok: true, amount: (0, mappers_1.parsePriceToMinorUnits)(match.rate) };
|
|
96
|
+
}
|
|
97
|
+
/** Countries where Printful requires a state code alongside the country. */
|
|
98
|
+
const STATE_REQUIRED_COUNTRIES = new Set(["US", "AU", "CA"]);
|
|
99
|
+
/**
|
|
100
|
+
* Whether an address carries enough for Printful to quote.
|
|
101
|
+
*
|
|
102
|
+
* `calculatePrice` runs on every cart refresh, long before a shipping address
|
|
103
|
+
* exists. Calling the API anyway means a 400 on every storefront visit, so an
|
|
104
|
+
* incomplete address short-circuits to the fallback instead.
|
|
105
|
+
*/
|
|
106
|
+
function isAddressQuotable(address) {
|
|
107
|
+
const country = (address.country_code ?? "").trim().toUpperCase();
|
|
108
|
+
if (!country) {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
if (STATE_REQUIRED_COUNTRIES.has(country)) {
|
|
112
|
+
return Boolean((address.state_code ?? "").trim());
|
|
113
|
+
}
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Turn cart lines into Printful rate items.
|
|
118
|
+
*
|
|
119
|
+
* `catalogIdByVariantId` maps a Medusa variant id to the Printful *catalog*
|
|
120
|
+
* variant id stored in variant metadata during sync. Lines whose variant has no
|
|
121
|
+
* catalog id are skipped: that covers non-Printful products in a mixed store,
|
|
122
|
+
* and Printful variants synced before the mapper recorded that id.
|
|
123
|
+
*/
|
|
124
|
+
function buildRateItems(lines, catalogIdByVariantId) {
|
|
125
|
+
const items = [];
|
|
126
|
+
for (const line of lines) {
|
|
127
|
+
const catalogId = catalogIdByVariantId.get(line.variant_id);
|
|
128
|
+
if (!catalogId) {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
// parseInt stops at the first invalid character, so "40.12" would parse as
|
|
132
|
+
// 40 — a quote for a different product. Require the whole string to be a
|
|
133
|
+
// positive integer.
|
|
134
|
+
const trimmed = catalogId.trim();
|
|
135
|
+
if (!/^\d+$/.test(trimmed)) {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
const parsed = Number.parseInt(trimmed, 10);
|
|
139
|
+
if (parsed <= 0) {
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
const quantity = Number(line.quantity);
|
|
143
|
+
if (!Number.isInteger(quantity) || quantity <= 0) {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
items.push({
|
|
147
|
+
variant_id: parsed,
|
|
148
|
+
quantity,
|
|
149
|
+
...(line.unit_price != null
|
|
150
|
+
? { value: (line.unit_price / 100).toFixed(2) }
|
|
151
|
+
: {}),
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
return items;
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2hpcHBpbmctcmF0ZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvdXRpbHMvc2hpcHBpbmctcmF0ZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBMERBLDhDQXFCQztBQWNELGdDQXdCQztBQVlELDhDQVlDO0FBaUJELHdDQXNDQztBQXBNRCxtQ0FBbUM7QUFDbkMsdUNBQWtEO0FBUWxEOzs7Ozs7Ozs7R0FTRztBQUNVLFFBQUEseUJBQXlCLEdBQUcsQ0FBQyxVQUFVLENBQVUsQ0FBQTtBQUU5RDs7Ozs7R0FLRztBQUNVLFFBQUEseUJBQXlCLEdBQUcsaUJBQWlCLENBQUE7QUFFMUQ7OztHQUdHO0FBQ1UsUUFBQSxpQkFBaUIsR0FBRyxJQUFJLEdBQUcsQ0FBQztJQUN2QyxtQkFBbUI7SUFDbkIsaUJBQWlCO0NBQ2xCLENBQUMsQ0FBQTtBQVFGLFNBQVMsU0FBUyxDQUFDLEtBQXlCO0lBQzFDLE9BQU8sQ0FBQyxLQUFLLElBQUksRUFBRSxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUMsV0FBVyxFQUFFLENBQUE7QUFDM0MsQ0FBQztBQUVEOzs7Ozs7Ozs7R0FTRztBQUNILFNBQWdCLGlCQUFpQixDQUFDLEtBQXdCO0lBQ3hELHVFQUF1RTtJQUN2RSxvRUFBb0U7SUFDcEUsK0JBQStCO0lBQy9CLE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUM7UUFDL0IsU0FBUyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsWUFBWSxDQUFDO1FBQ3JDLFNBQVMsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLFVBQVUsQ0FBQztRQUNuQyxTQUFTLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUM7UUFDN0IsU0FBUyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDO1FBQzVCLFNBQVMsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQztRQUNqQyxTQUFTLENBQUMsS0FBSyxD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
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DISCONTINUED_MARKER_KEY = exports.STOCK_MARKER_KEY = void 0;
|
|
4
|
+
exports.planStockActions = planStockActions;
|
|
5
|
+
exports.resolvePublication = resolvePublication;
|
|
6
|
+
/** Statuses that mean a variant cannot currently be ordered. */
|
|
7
|
+
const UNAVAILABLE = new Set([
|
|
8
|
+
"out_of_stock",
|
|
9
|
+
"temporary_out_of_stock",
|
|
10
|
+
"discontinued",
|
|
11
|
+
]);
|
|
12
|
+
/**
|
|
13
|
+
* Decide what a product's stock state means for its publication.
|
|
14
|
+
*
|
|
15
|
+
* Pure: returns intent, performs nothing. The caller applies the marker rules
|
|
16
|
+
* that keep this from overriding a merchant's own draft.
|
|
17
|
+
*
|
|
18
|
+
* An unrecognized or missing status counts as available. Printful can add a
|
|
19
|
+
* status at any time, and treating the unknown as sold out would hide a
|
|
20
|
+
* catalog on a value we simply have not seen before.
|
|
21
|
+
*/
|
|
22
|
+
function planStockActions(variants) {
|
|
23
|
+
const variantAvailability = {};
|
|
24
|
+
let anyAvailable = false;
|
|
25
|
+
let hasDiscontinued = false;
|
|
26
|
+
for (const v of variants) {
|
|
27
|
+
const status = v.availability_status ?? "active";
|
|
28
|
+
variantAvailability[String(v.id)] = status;
|
|
29
|
+
if (status === "discontinued") {
|
|
30
|
+
hasDiscontinued = true;
|
|
31
|
+
}
|
|
32
|
+
if (!UNAVAILABLE.has(status)) {
|
|
33
|
+
anyAvailable = true;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// No variants at all is a data oddity, not evidence of a sellout.
|
|
37
|
+
const allUnavailable = variants.length > 0 && !anyAvailable;
|
|
38
|
+
return {
|
|
39
|
+
status: allUnavailable ? "draft" : "published",
|
|
40
|
+
allUnavailable,
|
|
41
|
+
hasDiscontinued,
|
|
42
|
+
variantAvailability,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/** Marks a product the plugin unpublished because Printful ran out of it. */
|
|
46
|
+
exports.STOCK_MARKER_KEY = "printful_stock_status";
|
|
47
|
+
/** Set when Printful reports a variant as discontinued. Informational only. */
|
|
48
|
+
exports.DISCONTINUED_MARKER_KEY = "printful_discontinued";
|
|
49
|
+
/**
|
|
50
|
+
* Apply the stock plan without overriding the merchant.
|
|
51
|
+
*
|
|
52
|
+
* The plugin owns only the transitions it caused: it marks a product when it
|
|
53
|
+
* unpublishes one for being sold out, and re-publishes only a product carrying
|
|
54
|
+
* that marker. A product the merchant drafted themselves — no marker — is left
|
|
55
|
+
* exactly as they left it, however available Printful says it is.
|
|
56
|
+
*/
|
|
57
|
+
function resolvePublication(input) {
|
|
58
|
+
const { plan, currentStatus, currentMetadata } = input;
|
|
59
|
+
const metadata = { ...currentMetadata };
|
|
60
|
+
const marked = currentMetadata[exports.STOCK_MARKER_KEY] === "unavailable";
|
|
61
|
+
if (plan.allUnavailable) {
|
|
62
|
+
// Only claim ownership of an unpublish we are actually performing. A
|
|
63
|
+
// product already drafted without our marker was drafted by the merchant,
|
|
64
|
+
// and marking it would make us republish their draft on restock.
|
|
65
|
+
if (currentStatus === "draft") {
|
|
66
|
+
return { status: "draft", metadata, changed: false };
|
|
67
|
+
}
|
|
68
|
+
metadata[exports.STOCK_MARKER_KEY] = "unavailable";
|
|
69
|
+
return { status: "draft", metadata, changed: true };
|
|
70
|
+
}
|
|
71
|
+
// Available again. Only undo an unpublish we performed.
|
|
72
|
+
if (currentStatus === "draft") {
|
|
73
|
+
if (!marked) {
|
|
74
|
+
return { status: "draft", metadata, changed: false };
|
|
75
|
+
}
|
|
76
|
+
delete metadata[exports.STOCK_MARKER_KEY];
|
|
77
|
+
return { status: "published", metadata, changed: true };
|
|
78
|
+
}
|
|
79
|
+
if (marked) {
|
|
80
|
+
delete metadata[exports.STOCK_MARKER_KEY];
|
|
81
|
+
return { status: "published", metadata, changed: true };
|
|
82
|
+
}
|
|
83
|
+
return { status: "published", metadata, changed: false };
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RvY2suanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvdXRpbHMvc3RvY2sudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBbUJBLDRDQTRCQztBQTZCRCxnREErQkM7QUF6R0QsZ0VBQWdFO0FBQ2hFLE1BQU0sV0FBVyxHQUFHLElBQUksR0FBRyxDQUFDO0lBQzFCLGNBQWM7SUFDZCx3QkFBd0I7SUFDeEIsY0FBYztDQUNmLENBQUMsQ0FBQTtBQUVGOzs7Ozs7Ozs7R0FTRztBQUNILFNBQWdCLGdCQUFnQixDQUM5QixRQUF3RTtJQUV4RSxNQUFNLG1CQUFtQixHQUEyQixFQUFFLENBQUE7SUFDdEQsSUFBSSxZQUFZLEdBQUcsS0FBSyxDQUFBO0lBQ3hCLElBQUksZUFBZSxHQUFHLEtBQUssQ0FBQTtJQUUzQixLQUFLLE1BQU0sQ0FBQyxJQUFJLFFBQVEsRUFBRSxDQUFDO1FBQ3pCLE1BQU0sTUFBTSxHQUFHLENBQUMsQ0FBQyxtQkFBbUIsSUFBSSxRQUFRLENBQUE7UUFDaEQsbUJBQW1CLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxHQUFHLE1BQU0sQ0FBQTtRQUUxQyxJQUFJLE1BQU0sS0FBSyxjQUFjLEVBQUUsQ0FBQztZQUM5QixlQUFlLEdBQUcsSUFBSSxDQUFBO1FBQ3hCLENBQUM7UUFDRCxJQUFJLENBQUMsV0FBVyxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDO1lBQzdCLFlBQVksR0FBRyxJQUFJLENBQUE7UUFDckIsQ0FBQztJQUNILENBQUM7SUFFRCxrRUFBa0U7SUFDbEUsTUFBTSxjQUFjLEdBQUcsUUFBUSxDQUFDLE1BQU0sR0FBRyxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUE7SUFFM0QsT0FBTztRQUNMLE1BQU0sRUFBRSxjQUFjLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsV0FBVztRQUM5QyxjQUFjO1FBQ2QsZUFBZTtRQUNmLG1CQUFtQjtLQUNwQixDQUFBO0FBQ0gsQ0FBQztBQUVELDZFQUE2RTtBQUNoRSxRQUFBLGdCQUFnQixHQUFHLHVCQUF1QixDQUFBO0FBRXZELCtFQUErRTtBQUNsRSxRQUFBLHVCQUF1QixHQUFHLHVCQUF1QixDQUFBO0FBZTlEOzs7Ozs7O0dBT0c7QUFDSCxTQUFnQixrQkFBa0IsQ0FBQyxLQUF1QjtJQUN4RCxNQUFNLEVBQUUsSUFBSSxFQUFFLGFBQWEsRUFBRSxlQUFlLEVBQUUsR0FBRyxLQUFLLENBQUE7SUFDdEQsTUFBTSxRQUFRLEdBQUcsRUFBRSxHQUFHLGVBQWUsRUFBRSxDQUFBO0lBQ3ZDLE1BQU0sTUFBTSxHQUFHLGVBQWUsQ0FBQyx3QkFBZ0IsQ0FBQyxLQUFLLGFBQWEsQ0FBQTtJQUVsRSxJQUFJLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQztRQUN4QixxRUFBcUU7UUFDckUsMEVBQTBFO1FBQzFFLGlFQUFpRTtRQUNqRSxJQUFJLGFBQWEsS0FBSyxPQUFPLEVBQUUsQ0FBQztZQUM5QixPQUFPLEVBQUUsTUFBTSxFQUFFLE9BQU8sRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxDQUFBO1FBQ3RELENBQUM7UUFDRCxRQUFRLENBQUMsd0JBQWdCLENBQUMsR0FBRyxhQUFhLENBQUE7UUFDMUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxPQUFPLEVBQUUsUUFBUSxFQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUUsQ0FBQTtJQUNyRCxDQUFDO0lBRUQsd0RBQXdEO0lBQ3hELElBQUksYUFBYSxLQUFLLE9BQU8sRUFBRSxDQUFDO1FBQzlCLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUNaLE9BQU8sRUFBRSxNQUFNLEVBQUUsT0FBTyxFQUFFLFFBQVEsRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLENBQUE7UUFDdEQsQ0FBQztRQUNELE9BQU8sUUFBUSxDQUFDLHdCQUFnQixDQUFDLENBQUE7UUFDakMsT0FBTyxFQUFFLE1BQU0sRUFBRSxXQUFXLEVBQUUsUUFBUSxFQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUUsQ0FBQTtJQUN6RCxDQUFDO0lBRUQsSUFBSSxNQUFNLEVBQUUsQ0FBQztRQUNYLE9BQU8sUUFBUSxDQUFDLHdCQUFnQixDQUFDLENBQUE7UUFDakMsT0FBTyxFQUFFLE1BQU0sRUFBRSxXQUFXLEVBQUUsUUFBUSxFQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUUsQ0FBQTtJQUN6RCxDQUFDO0lBRUQsT0FBTyxFQUFFLE1BQU0sRUFBRSxXQUFXLEVBQUUsUUFBUSxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsQ0FBQTtBQUMxRCxDQUFDIn0=
|
|
@@ -6,18 +6,20 @@ const core_flows_1 = require("@medusajs/medusa/core-flows");
|
|
|
6
6
|
const utils_1 = require("@medusajs/framework/utils");
|
|
7
7
|
const printful_1 = require("../modules/printful");
|
|
8
8
|
const mappers_1 = require("../utils/mappers");
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
9
|
+
const orphans_1 = require("../utils/orphans");
|
|
10
|
+
const syncProductsStep = (0, workflows_sdk_1.createStep)({
|
|
11
|
+
name: "printful-sync-products",
|
|
12
|
+
// Runs past the HTTP response and completes on its own — no
|
|
13
|
+
// setStepSuccess needed, which is what backgroundExecution adds to async.
|
|
14
|
+
async: true,
|
|
15
|
+
backgroundExecution: true,
|
|
16
|
+
// No timeout on purpose. Medusa schedules one only when the step sets it
|
|
17
|
+
// (TransactionStep.hasTimeout() returns !!definition.timeout, and
|
|
18
|
+
// transaction-orchestrator.js:637 guards on it), so omitting it is the
|
|
19
|
+
// safe state. Any value we picked could be exceeded by a large enough
|
|
20
|
+
// catalog, and the penalty is a SUCCESSFUL sync being reverted — deleting
|
|
21
|
+
// the orphans it was about to link.
|
|
22
|
+
}, async (input, { container }) => {
|
|
21
23
|
const printful = container.resolve(printful_1.PRINTFUL_MODULE);
|
|
22
24
|
const productModule = container.resolve(utils_1.Modules.PRODUCT);
|
|
23
25
|
const client = await printful.getClient();
|
|
@@ -33,6 +35,14 @@ const syncProductsStep = (0, workflows_sdk_1.createStep)("printful-sync-products
|
|
|
33
35
|
failed: 0,
|
|
34
36
|
errors: [],
|
|
35
37
|
};
|
|
38
|
+
// Products created but not yet linked. A crash leaves exactly these
|
|
39
|
+
// invisible to the next sync's findProductLink, so they are the only
|
|
40
|
+
// thing the compensation may delete.
|
|
41
|
+
const orphans = new orphans_1.OrphanTracker();
|
|
42
|
+
let processed = 0;
|
|
43
|
+
await printful.heartbeatSyncLog(input.sync_log_id, {
|
|
44
|
+
products_total: toProcess.length,
|
|
45
|
+
});
|
|
36
46
|
for (const summary of toProcess) {
|
|
37
47
|
if (summary.is_ignored) {
|
|
38
48
|
continue;
|
|
@@ -44,11 +54,11 @@ const syncProductsStep = (0, workflows_sdk_1.createStep)("printful-sync-products
|
|
|
44
54
|
counters.errors.push(`Product ${summary.id} has no variants`);
|
|
45
55
|
continue;
|
|
46
56
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
57
|
+
// Pass the options wholesale rather than picking fields. Hand-building
|
|
58
|
+
// this object is how `onDiscontinued` was silently dropped: every field
|
|
59
|
+
// is optional, so omitting one is legal TypeScript and typecheck stayed
|
|
60
|
+
// green while the option did nothing. The mapper narrows internally.
|
|
61
|
+
const mapped = (0, mappers_1.mapSyncProductToMedusa)(detail, options);
|
|
52
62
|
const existingLink = await printful.findProductLink(String(summary.id));
|
|
53
63
|
if (existingLink?.medusa_product_id) {
|
|
54
64
|
const productId = existingLink.medusa_product_id;
|
|
@@ -162,12 +172,15 @@ const syncProductsStep = (0, workflows_sdk_1.createStep)("printful-sync-products
|
|
|
162
172
|
},
|
|
163
173
|
});
|
|
164
174
|
const created = result[0];
|
|
175
|
+
orphans.track(created.id);
|
|
165
176
|
await printful.createPrintfulProductLinks({
|
|
166
177
|
printful_store_id: storeId,
|
|
167
178
|
printful_sync_product_id: String(summary.id),
|
|
168
179
|
medusa_product_id: created.id,
|
|
169
180
|
last_synced_at: new Date(),
|
|
170
181
|
});
|
|
182
|
+
// Linked — no longer an orphan, and never to be deleted.
|
|
183
|
+
orphans.release(created.id);
|
|
171
184
|
for (const variant of created.variants ?? []) {
|
|
172
185
|
const syncVariantId = variant.metadata?.printful_sync_variant_id;
|
|
173
186
|
if (!syncVariantId) {
|
|
@@ -189,8 +202,35 @@ const syncProductsStep = (0, workflows_sdk_1.createStep)("printful-sync-products
|
|
|
189
202
|
const message = err instanceof Error ? err.message : String(err);
|
|
190
203
|
counters.errors.push(`Product ${summary.id}: ${message}`);
|
|
191
204
|
}
|
|
205
|
+
finally {
|
|
206
|
+
// In `finally` so it runs however the product exited — success, an
|
|
207
|
+
// early `continue`, or a throw. The heartbeat is what keeps the claim
|
|
208
|
+
// from being reaped, so a catalog of failures must not starve it.
|
|
209
|
+
processed += 1;
|
|
210
|
+
await printful.heartbeatSyncLog(input.sync_log_id, {
|
|
211
|
+
products_processed: processed,
|
|
212
|
+
products_total: toProcess.length,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return new workflows_sdk_1.StepResponse(counters, { orphanProductIds: orphans.toDelete() });
|
|
217
|
+
}, async (compensateInput, { container }) => {
|
|
218
|
+
if (!compensateInput?.orphanProductIds?.length) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const productModule = container.resolve(utils_1.Modules.PRODUCT);
|
|
222
|
+
const logger = container.resolve(utils_1.ContainerRegistrationKeys.LOGGER);
|
|
223
|
+
// Delete through the product module so variants, prices, and images go
|
|
224
|
+
// with the product rather than being orphaned a second time.
|
|
225
|
+
for (const id of compensateInput.orphanProductIds) {
|
|
226
|
+
try {
|
|
227
|
+
await productModule.deleteProducts([id]);
|
|
228
|
+
}
|
|
229
|
+
catch (err) {
|
|
230
|
+
logger.error(`Printful sync rollback: could not delete orphaned product ${id}: ${err instanceof Error ? err.message : String(err)}`);
|
|
231
|
+
}
|
|
192
232
|
}
|
|
193
|
-
|
|
233
|
+
logger.info(`Printful sync rolled back ${compensateInput.orphanProductIds.length} orphaned product(s)`);
|
|
194
234
|
});
|
|
195
235
|
const finalizeSyncLogStep = (0, workflows_sdk_1.createStep)("printful-finalize-sync-log", async (input, { container }) => {
|
|
196
236
|
const printful = container.resolve(printful_1.PRINTFUL_MODULE);
|
|
@@ -211,10 +251,9 @@ const finalizeSyncLogStep = (0, workflows_sdk_1.createStep)("printful-finalize-s
|
|
|
211
251
|
return new workflows_sdk_1.StepResponse(log);
|
|
212
252
|
});
|
|
213
253
|
exports.syncProductsWorkflow = (0, workflows_sdk_1.createWorkflow)("printful-sync-products", (input) => {
|
|
214
|
-
const log = createSyncLogStep();
|
|
215
254
|
const counters = syncProductsStep(input);
|
|
216
|
-
const finalizeInput = (0, workflows_sdk_1.transform)({
|
|
217
|
-
logId: data.
|
|
255
|
+
const finalizeInput = (0, workflows_sdk_1.transform)({ input, counters }, (data) => ({
|
|
256
|
+
logId: data.input.sync_log_id,
|
|
218
257
|
counters: data.counters,
|
|
219
258
|
}));
|
|
220
259
|
const finalLog = finalizeSyncLogStep(finalizeInput);
|
|
@@ -224,4 +263,4 @@ exports.syncProductsWorkflow = (0, workflows_sdk_1.createWorkflow)("printful-syn
|
|
|
224
263
|
});
|
|
225
264
|
});
|
|
226
265
|
exports.default = exports.syncProductsWorkflow;
|
|
227
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3luYy1wcm9kdWN0cy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy93b3JrZmxvd3Mvc3luYy1wcm9kdWN0cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSxxRUFNMEM7QUFDMUMsNERBR29DO0FBQ3BDLHFEQUFtRDtBQUNuRCxrREFBcUQ7QUFFckQsOENBQWdGO0FBY2hGLE1BQU0saUJBQWlCLEdBQUcsSUFBQSwwQkFBVSxFQUNsQywwQkFBMEIsRUFDMUIsS0FBSyxFQUFFLE1BQVksRUFBRSxFQUFFLFNBQVMsRUFBRSxFQUFFLEVBQUU7SUFDcEMsTUFBTSxRQUFRLEdBQTBCLFNBQVMsQ0FBQyxPQUFPLENBQUMsMEJBQWUsQ0FBQyxDQUFBO0lBQzFFLE1BQU0sR0FBRyxHQUFHLE1BQU0sUUFBUSxDQUFDLHNCQUFzQixDQUFDO1FBQ2hELE1BQU0sRUFBRSxTQUFTO1FBQ2pCLFVBQVUsRUFBRSxJQUFJLElBQUksRUFBRTtRQUN0QixnQkFBZ0IsRUFBRSxDQUFDO1FBQ25CLGdCQUFnQixFQUFFLENBQUM7UUFDbkIsZUFBZSxFQUFFLENBQUM7S0FDbkIsQ0FBQyxDQUFBO0lBQ0YsT0FBTyxJQUFJLDRCQUFZLENBQUMsR0FBRyxDQUFDLENBQUE7QUFDOUIsQ0FBQyxDQUNGLENBQUE7QUFFRCxNQUFNLGdCQUFnQixHQUFHLElBQUEsMEJBQVUsRUFDakMsd0JBQXdCLEVBQ3hCLEtBQUssRUFBRSxLQUF3QixFQUFFLEVBQUUsU0FBUyxFQUFFLEVBQUUsRUFBRTtJQUNoRCxNQUFNLFFBQVEsR0FBMEIsU0FBUyxDQUFDLE9BQU8sQ0FBQywwQkFBZSxDQUFDLENBQUE7SUFDMUUsTUFBTSxhQUFhLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FBQyxlQUFPLENBQUMsT0FBTyxDQUFDLENBQUE7SUFDeEQsTUFBTSxNQUFNLEdBQUcsTUFBTSxRQUFRLENBQUMsU0FBUyxFQUFFLENBQUE7SUFDekMsTUFBTSxPQUFPLEdBQUcsTUFBTSxRQUFRLENBQUMsVUFBVSxFQUFFLENBQUE7SUFDM0MsTUFBTSxPQUFPLEdBQUcsTUFBTSxRQUFRLENBQUMsVUFBVSxFQUFFLENBQUE7SUFFM0MsTUFBTSxTQUFTLEdBQUcsTUFBTSxNQUFNLENBQUMsbUJBQW1CLENBQUM7UUFDakQsS0FBSyxFQUFFLEdBQUc7S0FDWCxDQUFDLENBQUE7SUFFRixNQUFNLFNBQVMsR0FDYixLQUFLLENBQUMsS0FBSyxJQUFJLElBQUksQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUE7SUFFbkUsTUFBTSxRQUFRLEdBQWlCO1FBQzdCLE9BQU8sRUFBRSxDQUFDO1FBQ1YsT0FBTyxFQUFFLENBQUM7UUFDVixNQUFNLEVBQUUsQ0FBQztRQUNULE1BQU0sRUFBRSxFQUFFO0tBQ1gsQ0FBQTtJQUVELEtBQUssTUFBTSxPQUFPLElBQUksU0FBUyxFQUFFLENBQUM7UUFDaEMsSUFBSSxPQUFPLENBQUMsVUFBVSxFQUFFLENBQUM7WUFDdkIsU0FBUTtRQUNWLENBQUM7UUFFRCxJQUFJLENBQUM7WUFDSCxNQUFNLE1BQU0sR0FBRyxNQUFNLE1BQU0sQ0FBQyxjQUFjLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxDQUFBO1lBQ3RELElBQUksQ0FBQyxNQUFNLENBQUMsYUFBYSxFQUFFLE1BQU0sRUFBRSxDQUFDO2dCQUNsQyxRQUFRLENBQUMsTUFBTSxJQUFJLENBQUMsQ0FBQTtnQkFDcEIsUUFBUSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsV0FBVyxPQUFPLENBQUMsRUFBRSxrQkFBa0IsQ0FBQyxDQUFBO2dCQUM3RCxTQUFRO1lBQ1YsQ0FBQztZQUVELE1BQU0sTUFBTSxHQUFHLElBQUEsZ0NBQXNCLEVBQUMsTUFBTSxFQUFFO2dCQUM1QyxPQUFPLEVBQUUsT0FBTyxDQUFDLE9BQU87Z0JBQ3hCLGVBQWUsRUFBRSxPQUFPLENBQUMsZUFBZTtnQkFDeEMsYUFBYSxFQUFFLE9BQU8sQ0FBQyxhQUFhO2FBQ3JDLENBQUMsQ0FBQTtZQUVGLE1BQU0sWUFBWSxHQUFHLE1BQU0sUUFBUSxDQUFDLGVBQWUsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUE7WUFFdkUsSUFBSSxZQUFZLEVBQUUsaUJBQWlCLEVBQUUsQ0FBQztnQkFDcEMsTUFBTSxTQUFTLEdBQUcsWUFBWSxDQUFDLGlCQUFpQixDQUFBO2dCQUVoRCw4QkFBOEI7Z0JBQzlCLE1BQU0sYUFBYSxDQUFDLGNBQWMsQ0FBQyxTQUFTLEVBQUU7b0JBQzVDLEtBQUssRUFBRSxNQUFNLENBQUMsS0FBSztvQkFDbkIsU0FBUyxFQUFFLE1BQU0sQ0FBQyxTQUFTO29CQUMzQixRQUFRLEVBQUUsTUFBTSxDQUFDLFFBQVE7b0JBQ3pCLE1BQU0sRUFBRSxNQUFNLENBQUMsTUFBTTtpQkFDdEIsQ0FBQyxDQUFBO2dCQUVGLHdFQUF3RTtnQkFDeEUsTUFBTSxPQUFPLEdBQUcsTUFBTSxhQUFhLENBQUMsZUFBZSxDQUFDLFNBQVMsRUFBRTtvQkFDN0QsU0FBUyxFQUFFLENBQUMsVUFBVSxDQUFDO2lCQUN4QixDQUFDLENBQUE7Z0JBQ0YsTUFBTSxFQUFFLFFBQVEsRUFBRSxRQUFRLEVBQUUsR0FBRyxJQUFBLCtCQUFxQixFQUNsRCxNQUFNLENBQUMsUUFBUSxFQUNmLENBQUMsT0FBTyxDQUFDLFFBQVEsSUFBSSxFQUFFLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLEVBQUUsRUFBRSxDQUFDLENBQUM7b0JBQ3BDLEVBQUUsRUFBRSxFQUFFLENBQUMsRUFBRTtvQkFDVCxRQUFRLEVBQUUsRUFBRSxDQUFDLFFBQVE7aUJBQ3RCLENBQUMsQ0FBQyxDQUNKLENBQUE7Z0JBRUQsSUFBSSxRQUFRLENBQUMsTUFBTSxFQUFFLENBQUM7b0JBQ3BCLE1BQU0sSUFBQSx5Q0FBNEIsRUFBQyxTQUFTLENBQUMsQ0FBQyxHQUFHLENBQUM7d0JBQ2hELEtBQUssRUFBRTs0QkFDTCxNQUFNLEVBQUUsUUFBUSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQztnQ0FDM0IsRUFBRSxFQUFFLENBQUMsQ0FBQyxFQUFFO2dDQUNSLEtBQUssRUFBRSxDQUFDLENBQUMsS0FBSztnQ0FDZCxNQUFNLEVBQUUsQ0FBQyxDQUFDLE1BQU07Z0NBQ2hCLFFBQVEsRUFBRSxDQUFDLENBQUMsUUFBUTs2QkFDckIsQ0FBQyxDQUFDO3lCQUNKO3FCQUNGLENBQUMsQ0FBQTtnQkFDSixDQUFDO2dCQUVELElBQUksUUFBUSxDQUFDLE1BQU0sRUFBRSxDQUFDO29CQUNwQixNQUFNLEVBQUUsTUFBTSxFQUFFLE9BQU8sRUFBRSxHQUFHLE1BQU0sSUFBQSx5Q0FBNEIsRUFDNUQsU0FBUyxDQUNWLENBQUMsR0FBRyxDQUFDO3dCQUNKLEtBQUssRUFBRTs0QkFDTCxNQUFNLEVBQUUsUUFBUSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQztnQ0FDM0IsVUFBVSxFQUFFLFNBQVM7Z0NBQ3JCLEtBQUssRUFBRSxDQUFDLENBQUMsS0FBSztnQ0FDZCxHQUFHLEVBQUUsQ0FBQyxDQUFDLEdBQUc7Z0NBQ1YsT0FBTyxFQUFFLENBQUMsQ0FBQyxPQUFPO2dDQUNsQixNQUFNLEVBQUUsQ0FBQyxDQUFDLE1BQU07Z0NBQ2hCLFFBQVEsRUFBRSxDQUFDLENBQUMsUUFBUTtnQ0FDcEIsZ0JBQWdCLEVBQUUsQ0FBQyxDQUFDLGdCQUFnQjtnQ0FDcEMsZUFBZSxFQUFFLENBQUMsQ0FBQyxlQUFlOzZCQUNuQyxDQUFDLENBQUM7eUJBQ0o7cUJBQ0YsQ0FBQyxDQUFBO29CQUVGLEtBQUssTUFBTSxPQUFPLElBQUksT0FBTyxDQUFDLE9BQU8sSUFBSSxFQUFFLEVBQUUsQ0FBQzt3QkFDNUMsTUFBTSxhQUFhLEdBQUcsT0FBTyxDQUFDLFFBQVEsRUFBRSx3QkFBd0IsQ0FBQTt3QkFDaEUsSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDOzRCQUNuQixTQUFRO3dCQUNWLENBQUM7d0JBQ0QsTUFBTSxRQUFRLENBQUMsMEJBQTBCLENBQUM7NEJBQ3hDLGlCQUFpQixFQUFFLE9BQU87NEJBQzFCLHdCQUF3QixFQUFFLE1BQU0sQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDOzRCQUM1Qyx3QkFBd0IsRUFBRSxNQUFNLENBQUMsYUFBYSxDQUFDOzRCQUMvQyxpQkFBaUIsRUFBRSxPQUFPLENBQUMsRUFBRTs0QkFDN0IsY0FBYyxFQUFFLElBQUksSUFBSSxFQUFFO3lCQUMzQixDQUFDLENBQUE7b0JBQ0osQ0FBQztnQkFDSCxDQUFDO2dCQUVELGdEQUFnRDtnQkFDaEQsS0FBSyxNQUFNLENBQUMsSUFBSSxNQUFNLENBQUMsYUFBYSxFQUFFLENBQUM7b0JBQ3JDLE1BQU0sZUFBZSxHQUFHLE1BQU0sUUFBUSxDQUFDLGVBQWUsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUE7b0JBQ3BFLElBQUksZUFBZSxFQUFFLENBQUM7d0JBQ3BCLE1BQU0sUUFBUSxDQUFDLDBCQUEwQixDQUFDOzRCQUN4QyxFQUFFLEVBQUUsZUFBZSxDQUFDLEVBQUU7NEJBQ3RCLGNBQWMsRUFBRSxJQUFJLElBQUksRUFBRTt5QkFDM0IsQ0FBQyxDQUFBO29CQUNKLENBQUM7Z0JBQ0gsQ0FBQztnQkFFRCxNQUFNLFFBQVEsQ0FBQywwQkFBMEIsQ0FBQztvQkFDeEMsRUFBRSxFQUFFLFlBQVksQ0FBQyxFQUFFO29CQUNuQixjQUFjLEVBQUUsSUFBSSxJQUFJLEVBQUU7aUJBQzNCLENBQUMsQ0FBQTtnQkFDRixRQUFRLENBQUMsT0FBTyxJQUFJLENBQUMsQ0FBQTtZQUN2QixDQUFDO2lCQUFNLENBQUM7Z0JBQ04sb0NBQW9DO2dCQUNwQyxJQUFJLE1BQU0sR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFBO2dCQUMxQixJQUFJLENBQUM7b0JBQ0gsTUFBTSxRQUFRLEdBQUcsTUFBTSxhQUFhLENBQUMsWUFBWSxDQUMvQyxFQUFFLE1BQU0sRUFBRSxDQUFDLE1BQU0sQ0FBQyxFQUFFLEVBQ3BCLEVBQUUsSUFBSSxFQUFFLENBQUMsRUFBRSxDQUNaLENBQUE7b0JBQ0QsSUFBSSxRQUFRLENBQUMsTUFBTSxFQUFFLENBQUM7d0JBQ3BCLE1BQU0sR0FBRyxHQUFHLE1BQU0sT0FBTyxPQUFPLENBQUMsRUFBRSxFQUFFLENBQUE7b0JBQ3ZDLENBQUM7Z0JBQ0gsQ0FBQztnQkFBQyxNQUFNLENBQUM7b0JBQ1AsdUJBQXVCO2dCQUN6QixDQUFDO2dCQUVELE1BQU0sRUFBRSxNQUFNLEVBQUUsR0FBRyxNQUFNLElBQUEsbUNBQXNCLEVBQUMsU0FBUyxDQUFDLENBQUMsR0FBRyxDQUFDO29CQUM3RCxLQUFLLEVBQUU7d0JBQ0wsUUFBUSxFQUFFOzRCQUNSO2dDQUNFLEtBQUssRUFBRSxNQUFNLENBQUMsS0FBSztnQ0FDbkIsTUFBTTtnQ0FDTixNQUFNLEVBQUUsTUFBTSxDQUFDLE1BQU07Z0NBQ3JCLFNBQVMsRUFBRSxNQUFNLENBQUMsU0FBUztnQ0FDM0IsTUFBTSxFQUFFLE1BQU0sQ0FBQyxNQUFNO2dDQUNyQixPQUFPLEVBQUUsTUFBTSxDQUFDLE9BQU87Z0NBQ3ZCLFFBQVEsRUFBRSxNQUFNLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQztvQ0FDcEMsS0FBSyxFQUFFLENBQUMsQ0FBQyxLQUFLO29DQUNkLEdBQUcsRUFBRSxDQUFDLENBQUMsR0FBRztvQ0FDVixPQUFPLEVBQUUsQ0FBQyxDQUFDLE9BQU87b0NBQ2xCLE1BQU0sRUFBRSxDQUFDLENBQUMsTUFBTTtvQ0FDaEIsUUFBUSxFQUFFLENBQUMsQ0FBQyxRQUFRO29DQUNwQixnQkFBZ0IsRUFBRSxDQUFDLENBQUMsZ0JBQWdCO29DQUNwQyxlQUFlLEVBQUUsQ0FBQyxDQUFDLGVBQWU7aUNBQ25DLENBQUMsQ0FBQztnQ0FDSCxRQUFRLEVBQUUsTUFBTSxDQUFDLFFBQVE7Z0NBQ3pCLFdBQVcsRUFBRSxNQUFNLENBQUMsV0FBVzs2QkFDaEM7eUJBQ0Y7cUJBQ0Y7aUJBQ0YsQ0FBQyxDQUFBO2dCQUVGLE1BQU0sT0FBTyxHQUFHLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQTtnQkFDekIsTUFBTSxRQUFRLENBQUMsMEJBQTBCLENBQUM7b0JBQ3hDLGlCQUFpQixFQUFFLE9BQU87b0JBQzFCLHdCQUF3QixFQUFFLE1BQU0sQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDO29CQUM1QyxpQkFBaUIsRUFBRSxPQUFPLENBQUMsRUFBRTtvQkFDN0IsY0FBYyxFQUFFLElBQUksSUFBSSxFQUFFO2lCQUMzQixDQUFDLENBQUE7Z0JBRUYsS0FBSyxNQUFNLE9BQU8sSUFBSSxPQUFPLENBQUMsUUFBUSxJQUFJLEVBQUUsRUFBRSxDQUFDO29CQUM3QyxNQUFNLGFBQWEsR0FBRyxPQUFPLENBQUMsUUFBUSxFQUFFLHdCQUF3QixDQUFBO29CQUNoRSxJQUFJLENBQUMsYUFBYSxFQUFFLENBQUM7d0JBQ25CLFNBQVE7b0JBQ1YsQ0FBQztvQkFDRCxNQUFNLFFBQVEsQ0FBQywwQkFBMEIsQ0FBQzt3QkFDeEMsaUJBQWlCLEVBQUUsT0FBTzt3QkFDMUIsd0JBQXdCLEVBQUUsTUFBTSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7d0JBQzVDLHdCQUF3QixFQUFFLE1BQU0sQ0FBQyxhQUFhLENBQUM7d0JBQy9DLGlCQUFpQixFQUFFLE9BQU8sQ0FBQyxFQUFFO3dCQUM3QixjQUFjLEVBQUUsSUFBSSxJQUFJLEVBQUU7cUJBQzNCLENBQUMsQ0FBQTtnQkFDSixDQUFDO2dCQUVELFFBQVEsQ0FBQyxPQUFPLElBQUksQ0FBQyxDQUFBO1lBQ3ZCLENBQUM7UUFDSCxDQUFDO1FBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztZQUNiLFFBQVEsQ0FBQyxNQUFNLElBQUksQ0FBQyxDQUFBO1lBQ3BCLE1BQU0sT0FBTyxHQUFHLEdBQUcsWUFBWSxLQUFLLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQTtZQUNoRSxRQUFRLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxXQUFXLE9BQU8sQ0FBQyxFQUFFLEtBQUssT0FBTyxFQUFFLENBQUMsQ0FBQTtRQUMzRCxDQUFDO0lBQ0gsQ0FBQztJQUVELE9BQU8sSUFBSSw0QkFBWSxDQUFDLFFBQVEsQ0FBQyxDQUFBO0FBQ25DLENBQUMsQ0FDRixDQUFBO0FBRUQsTUFBTSxtQkFBbUIsR0FBRyxJQUFBLDBCQUFVLEVBQ3BDLDRCQUE0QixFQUM1QixLQUFLLEVBQUUsS0FBZ0QsRUFBRSxFQUFFLFNBQVMsRUFBRSxFQUFFLEVBQUU7SUFDeEUsTUFBTSxRQUFRLEdBQTBCLFNBQVMsQ0FBQyxPQUFPLENBQUMsMEJBQWUsQ0FBQyxDQUFBO0lBQzFFLE1BQU0sVUFBVSxHQUNkLEtBQUssQ0FBQyxRQUFRLENBQUMsTUFBTSxHQUFHLENBQUM7UUFDekIsS0FBSyxDQUFDLFFBQVEsQ0FBQyxPQUFPLEtBQUssQ0FBQztRQUM1QixLQUFLLENBQUMsUUFBUSxDQUFDLE9BQU8sS0FBSyxDQUFDLENBQUE7SUFFOUIsTUFBTSxHQUFHLEdBQUcsTUFBTSxRQUFRLENBQUMsc0JBQXNCLENBQUM7UUFDaEQsRUFBRSxFQUFFLEtBQUssQ0FBQyxLQUFLO1FBQ2YsTUFBTSxFQUFFLFVBQVUsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxTQUFTO1FBQ3pDLFdBQVcsRUFBRSxJQUFJLElBQUksRUFBRTtRQUN2QixnQkFBZ0IsRUFBRSxLQUFLLENBQUMsUUFBUSxDQUFDLE9BQU87UUFDeEMsZ0JBQWdCLEVBQUUsS0FBSyxDQUFDLFFBQVEsQ0FBQyxPQUFPO1FBQ3hDLGVBQWUsRUFBRSxLQUFLLENBQUMsUUFBUSxDQUFDLE1BQU07UUFDdEMsYUFBYSxFQUNYLEtBQUssQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLE1BQU0sR0FBRyxDQUFDO1lBQzlCLENBQUMsQ0FBQyxLQUFLLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUM7WUFDL0MsQ0FBQyxDQUFDLElBQUk7S0FDWCxDQUFDLENBQUE7SUFFRixPQUFPLElBQUksNEJBQVksQ0FBQyxHQUFHLENBQUMsQ0FBQTtBQUM5QixDQUFDLENBQ0YsQ0FBQTtBQUVZLFFBQUEsb0JBQW9CLEdBQUcsSUFBQSw4QkFBYyxFQUNoRCx3QkFBd0IsRUFDeEIsQ0FBQyxLQUF3QixFQUFFLEVBQUU7SUFDM0IsTUFBTSxHQUFHLEdBQUcsaUJBQWlCLEVBQUUsQ0FBQTtJQUMvQixNQUFNLFFBQVEsR0FBRyxnQkFBZ0IsQ0FBQyxLQUFLLENBQUMsQ0FBQTtJQUN4QyxNQUFNLGFBQWEsR0FBRyxJQUFBLHlCQUFTLEVBQUMsRUFBRSxHQUFHLEVBQUUsUUFBUSxFQUFFLEVBQUUsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFDNUQsS0FBSyxFQUFFLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBWTtRQUM1QixRQUFRLEVBQUUsSUFBSSxDQUFDLFFBQXdCO0tBQ3hDLENBQUMsQ0FBQyxDQUFBO0lBQ0gsTUFBTSxRQUFRLEdBQUcsbUJBQW1CLENBQUMsYUFBYSxDQUFDLENBQUE7SUFFbkQsT0FBTyxJQUFJLGdDQUFnQixDQUFDO1FBQzFCLFFBQVEsRUFBRSxRQUFRO1FBQ2xCLFFBQVE7S0FDVCxDQUFDLENBQUE7QUFDSixDQUFDLENBQ0YsQ0FBQTtBQUVELGtCQUFlLDRCQUFvQixDQUFBIn0=
|
|
266
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3luYy1wcm9kdWN0cy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy93b3JrZmxvd3Mvc3luYy1wcm9kdWN0cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSxxRUFNMEM7QUFDMUMsNERBR29DO0FBQ3BDLHFEQUE4RTtBQUM5RSxrREFBcUQ7QUFFckQsOENBQWdGO0FBQ2hGLDhDQUFnRDtBQWdCaEQsTUFBTSxnQkFBZ0IsR0FBRyxJQUFBLDBCQUFVLEVBQ2pDO0lBQ0UsSUFBSSxFQUFFLHdCQUF3QjtJQUM5Qiw0REFBNEQ7SUFDNUQsMEVBQTBFO0lBQzFFLEtBQUssRUFBRSxJQUFJO0lBQ1gsbUJBQW1CLEVBQUUsSUFBSTtJQUN6Qix5RUFBeUU7SUFDekUsa0VBQWtFO0lBQ2xFLHVFQUF1RTtJQUN2RSxzRUFBc0U7SUFDdEUsMEVBQTBFO0lBQzFFLG9DQUFvQztDQUNyQyxFQUNELEtBQUssRUFBRSxLQUF3QixFQUFFLEVBQUUsU0FBUyxFQUFFLEVBQUUsRUFBRTtJQUNoRCxNQUFNLFFBQVEsR0FBMEIsU0FBUyxDQUFDLE9BQU8sQ0FBQywwQkFBZSxDQUFDLENBQUE7SUFDMUUsTUFBTSxhQUFhLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FBQyxlQUFPLENBQUMsT0FBTyxDQUFDLENBQUE7SUFDeEQsTUFBTSxNQUFNLEdBQUcsTUFBTSxRQUFRLENBQUMsU0FBUyxFQUFFLENBQUE7SUFDekMsTUFBTSxPQUFPLEdBQUcsTUFBTSxRQUFRLENBQUMsVUFBVSxFQUFFLENBQUE7SUFDM0MsTUFBTSxPQUFPLEdBQUcsTUFBTSxRQUFRLENBQUMsVUFBVSxFQUFFLENBQUE7SUFFM0MsTUFBTSxTQUFTLEdBQUcsTUFBTSxNQUFNLENBQUMsbUJBQW1CLENBQUM7UUFDakQsS0FBSyxFQUFFLEdBQUc7S0FDWCxDQUFDLENBQUE7SUFFRixNQUFNLFNBQVMsR0FDYixLQUFLLENBQUMsS0FBSyxJQUFJLElBQUksQ0FBQyxDQUFDLENB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
|
package/README.md
CHANGED
|
@@ -76,15 +76,16 @@ See `examples/basic-store/` for a fuller snippet.
|
|
|
76
76
|
|
|
77
77
|
## What it does (MVP)
|
|
78
78
|
|
|
79
|
-
| Feature | How
|
|
80
|
-
| -------------------- |
|
|
81
|
-
| Product sync | Admin **Sync Now** or `POST /admin/printful/sync` →
|
|
82
|
-
|
|
|
83
|
-
|
|
|
84
|
-
|
|
|
85
|
-
|
|
|
86
|
-
|
|
|
87
|
-
|
|
|
79
|
+
| Feature | How |
|
|
80
|
+
| -------------------- | ----------------------------------------------------------------------------------------- |
|
|
81
|
+
| Product sync | Admin **Sync Now** or `POST /admin/printful/sync` → runs in the background, one at a time |
|
|
82
|
+
| Stock awareness | Variants Printful reports as unavailable unpublish the product; restock republishes it |
|
|
83
|
+
| Links | `printful_product_link` / `printful_variant_link` (+ metadata IDs) |
|
|
84
|
+
| Orders | On `payment.captured` → creates Printful order with **`sync_variant_id`** |
|
|
85
|
+
| Fulfillment provider | Select Printful shipping option in Admin locations |
|
|
86
|
+
| Status | `GET /admin/printful/status` + product list widget |
|
|
87
|
+
| Shipment tracking | Printful webhooks → Medusa fulfillment + shipment per parcel, with tracking |
|
|
88
|
+
| Order visibility | Printful status and per-parcel tracking on the Admin order page |
|
|
88
89
|
|
|
89
90
|
### Idempotency
|
|
90
91
|
|
|
@@ -177,6 +178,101 @@ lines contain the real path. The endpoint's body limit is therefore raised to
|
|
|
177
178
|
exceeded by roughly a 25-item order), so genuine Printful traffic does not
|
|
178
179
|
reach that path. This is another reason to treat the secret as rotatable.
|
|
179
180
|
|
|
181
|
+
## Live shipping rates
|
|
182
|
+
|
|
183
|
+
Printful quotes shipping for the destination and cart contents instead of you
|
|
184
|
+
setting a flat price by hand.
|
|
185
|
+
|
|
186
|
+
```ts
|
|
187
|
+
plugins: [
|
|
188
|
+
{
|
|
189
|
+
resolve: "@legenki/print2medusa",
|
|
190
|
+
options: {
|
|
191
|
+
apiToken: process.env.PRINTFUL_API_TOKEN,
|
|
192
|
+
liveShippingRates: true,
|
|
193
|
+
fallbackShippingRates: { STANDARD: 500 }, // minor units
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
],
|
|
197
|
+
modules: [
|
|
198
|
+
{
|
|
199
|
+
resolve: "@medusajs/medusa/fulfillment",
|
|
200
|
+
// Required. The provider reads Printful variant ids from variant metadata
|
|
201
|
+
// through Query, and Medusa only bridges modules a provider declares.
|
|
202
|
+
dependencies: ["query"],
|
|
203
|
+
options: {
|
|
204
|
+
providers: [
|
|
205
|
+
{
|
|
206
|
+
resolve: "@legenki/print2medusa/providers/printful-fulfillment",
|
|
207
|
+
id: "printful",
|
|
208
|
+
options: { apiToken: process.env.PRINTFUL_API_TOKEN },
|
|
209
|
+
},
|
|
210
|
+
],
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
],
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
**`dependencies: ["query"]` is not optional.** Without it the provider cannot
|
|
217
|
+
resolve Printful variant ids, and every quote quietly falls back to the flat
|
|
218
|
+
rate. Medusa resolves an undeclared dependency to `undefined` rather than
|
|
219
|
+
failing, so the plugin logs an error at startup instead.
|
|
220
|
+
|
|
221
|
+
**Give `fallbackShippingRates` an entry for every method you offer.** A method
|
|
222
|
+
with no entry prices at **zero** rather than blocking checkout: Medusa cannot
|
|
223
|
+
complete a cart whose shipping price fails to resolve, so an underpriced
|
|
224
|
+
delivery is the lesser harm. The plugin logs an error each time it happens.
|
|
225
|
+
|
|
226
|
+
### What happens when Printful is unreachable
|
|
227
|
+
|
|
228
|
+
Checkout still completes. Prices fall back in this order:
|
|
229
|
+
|
|
230
|
+
1. A cached quote inside `shippingRateCacheTtlSeconds` (default 600)
|
|
231
|
+
2. A cached quote past that but within `shippingRateStaleSeconds` (default 86400)
|
|
232
|
+
3. The flat rate from `fallbackShippingRates`
|
|
233
|
+
|
|
234
|
+
A day-old real quote beats a constant someone typed once, which is why the stale
|
|
235
|
+
tier outranks the flat rate. One Printful call serves every shipping option on a
|
|
236
|
+
cart — the whole response is cached, and each option is picked from it locally.
|
|
237
|
+
|
|
238
|
+
### Limits worth knowing
|
|
239
|
+
|
|
240
|
+
- **Printful chooses the shipping method on the order.** The method the customer
|
|
241
|
+
selected is priced correctly but is not passed through to Printful, so it can
|
|
242
|
+
ship by a different service. Medusa does not carry provider data from price
|
|
243
|
+
calculation onto the shipping method, and the mechanism that would fix this
|
|
244
|
+
needs its own release.
|
|
245
|
+
- **Return options are never priced live.** Printful quotes outbound shipping
|
|
246
|
+
only, so a return shipping option must be given a flat admin price.
|
|
247
|
+
- **Rates are quoted in the cart's currency** by asking Printful to convert. If
|
|
248
|
+
a quote comes back in another currency it is discarded rather than converted.
|
|
249
|
+
|
|
250
|
+
## Catalog sync
|
|
251
|
+
|
|
252
|
+
`POST /admin/printful/sync` returns `202 {sync_id}` right away and the sync runs
|
|
253
|
+
in the background, so a large catalog no longer holds the request open. The
|
|
254
|
+
widget polls progress while it runs.
|
|
255
|
+
|
|
256
|
+
**One sync at a time.** A second request gets `409` with the running sync's
|
|
257
|
+
`started_at`, and the nightly job skips quietly rather than piling on. This is
|
|
258
|
+
enforced by a partial unique index in Postgres, not by a check-then-insert, so
|
|
259
|
+
double-clicking **Sync Now** cannot start two.
|
|
260
|
+
|
|
261
|
+
**A killed process is recovered lazily.** If Medusa dies mid-sync, the log row
|
|
262
|
+
stays `running` and the widget keeps showing a sync that is no longer alive.
|
|
263
|
+
Nothing sweeps on a timer: the next sync attempt — manual, or the nightly job —
|
|
264
|
+
reclaims any claim whose heartbeat is older than `syncStaleMinutes` (default 60)
|
|
265
|
+
and proceeds. Products created but not yet linked are deleted on rollback, so a
|
|
266
|
+
crash leaves no half-imported products behind.
|
|
267
|
+
|
|
268
|
+
### Stock
|
|
269
|
+
|
|
270
|
+
A product whose variants Printful all reports as unavailable is set to `draft`,
|
|
271
|
+
and republished when it comes back. The plugin only republishes what it
|
|
272
|
+
unpublished itself — a product you set to draft by hand stays draft. Variants
|
|
273
|
+
carry `printful_availability_status` in metadata, and discontinued products get
|
|
274
|
+
`printful_discontinued` unless `onDiscontinued: "ignore"`.
|
|
275
|
+
|
|
180
276
|
## Admin usage
|
|
181
277
|
|
|
182
278
|
1. Create products in Printful (Store Products).
|
|
@@ -217,16 +313,18 @@ the testing strategy for each release.
|
|
|
217
313
|
|
|
218
314
|
## Options
|
|
219
315
|
|
|
220
|
-
| Option | Description
|
|
221
|
-
| --------------------- |
|
|
222
|
-
| `apiToken` | Printful private token (required)
|
|
223
|
-
| `storeId` | `X-PF-Store-Id` for account-level tokens
|
|
224
|
-
| `autoSubmitOrders` | Confirm orders for fulfillment (default true)
|
|
225
|
-
| `createOnOrderPlaced` | Also create Printful order on `order.placed`
|
|
226
|
-
| `allowPartialOrders` | Allow orders that mix Printful + non-Printful items
|
|
227
|
-
| `markupPercent` | Markup on retail prices during sync
|
|
228
|
-
| `defaultCurrency` | Fallback currency code
|
|
229
|
-
| `webhookSecret` | Shared secret for the Printful webhook path (see [Webhooks](#webhooks))
|
|
316
|
+
| Option | Description |
|
|
317
|
+
| --------------------- | --------------------------------------------------------------------------- |
|
|
318
|
+
| `apiToken` | Printful private token (required) |
|
|
319
|
+
| `storeId` | `X-PF-Store-Id` for account-level tokens |
|
|
320
|
+
| `autoSubmitOrders` | Confirm orders for fulfillment (default true) |
|
|
321
|
+
| `createOnOrderPlaced` | Also create Printful order on `order.placed` |
|
|
322
|
+
| `allowPartialOrders` | Allow orders that mix Printful + non-Printful items |
|
|
323
|
+
| `markupPercent` | Markup on retail prices during sync |
|
|
324
|
+
| `defaultCurrency` | Fallback currency code |
|
|
325
|
+
| `webhookSecret` | Shared secret for the Printful webhook path (see [Webhooks](#webhooks)) |
|
|
326
|
+
| `syncStaleMinutes` | Minutes before a running sync is presumed dead and reclaimed (default 60) |
|
|
327
|
+
| `onDiscontinued` | `"flag"` (default) marks discontinued products, `"ignore"` omits the marker |
|
|
230
328
|
|
|
231
329
|
## License
|
|
232
330
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@legenki/print2medusa",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Printful → Medusa v2 plugin: product sync, auto fulfillment, and admin tools",
|
|
5
5
|
"author": "Andy Legenki",
|
|
6
6
|
"license": "MIT",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"@types/react-dom": "^18.2.25",
|
|
67
67
|
"eslint": "^9.0.0",
|
|
68
68
|
"jiti": "^2.0.0",
|
|
69
|
-
"prettier": "^3.
|
|
69
|
+
"prettier": "^3.9.6",
|
|
70
70
|
"prop-types": "^15.8.1",
|
|
71
71
|
"react": "^18.2.0",
|
|
72
72
|
"react-dom": "^18.2.0",
|