@legenki/print2medusa 0.4.0 → 0.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.
@@ -4,6 +4,13 @@ const adminSdk = require("@medusajs/admin-sdk");
4
4
  const ui = require("@medusajs/ui");
5
5
  const react = require("react");
6
6
  const ATTENTION_STATES = /* @__PURE__ */ new Set(["failed", "canceled", "onhold"]);
7
+ const formatMinor = (amount, currency) => {
8
+ if (typeof amount !== "number" || !Number.isFinite(amount)) {
9
+ return null;
10
+ }
11
+ const code = typeof currency === "string" ? currency.toUpperCase() : "";
12
+ return `${(amount / 100).toFixed(2)}${code ? ` ${code}` : ""}`;
13
+ };
7
14
  const PrintfulOrderWidget = ({ data }) => {
8
15
  const metadata = data.metadata ?? {};
9
16
  const printfulOrderId = metadata.printful_order_id;
@@ -14,6 +21,19 @@ const PrintfulOrderWidget = ({ data }) => {
14
21
  const statusUpdatedAt = metadata.printful_status_updated_at;
15
22
  const shipments = metadata.printful_shipments ?? [];
16
23
  const isAttention = status ? ATTENTION_STATES.has(status) : false;
24
+ const costTotal = formatMinor(
25
+ metadata.printful_cost_total,
26
+ metadata.printful_cost_currency
27
+ );
28
+ const retailTotal = formatMinor(
29
+ metadata.printful_retail_total,
30
+ metadata.printful_retail_currency
31
+ );
32
+ const margin = formatMinor(
33
+ metadata.printful_margin,
34
+ metadata.printful_cost_currency
35
+ );
36
+ const currencyMismatch = costTotal !== null && retailTotal !== null && margin === null;
17
37
  return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
18
38
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
19
39
  /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Printful" }),
@@ -49,7 +69,23 @@ const PrintfulOrderWidget = ({ data }) => {
49
69
  "Last synced: ",
50
70
  new Date(statusUpdatedAt).toLocaleString()
51
71
  ] }) : null
52
- ] })
72
+ ] }),
73
+ costTotal ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 py-4 flex flex-col gap-2", children: [
74
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", children: "Economics" }),
75
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: [
76
+ "Printful cost: ",
77
+ costTotal
78
+ ] }),
79
+ retailTotal ? /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: [
80
+ "Retail: ",
81
+ retailTotal
82
+ ] }) : null,
83
+ margin ? /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "small", children: [
84
+ "Margin: ",
85
+ margin
86
+ ] }) : null,
87
+ currencyMismatch ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Margin unavailable — Printful billed in a different currency than the order." }) : null
88
+ ] }) : null
53
89
  ] });
54
90
  };
55
91
  adminSdk.defineWidgetConfig({
@@ -3,6 +3,13 @@ import { defineWidgetConfig } from "@medusajs/admin-sdk";
3
3
  import { Container, Heading, Badge, Text, Button, toast } from "@medusajs/ui";
4
4
  import { useState, useEffect } from "react";
5
5
  const ATTENTION_STATES = /* @__PURE__ */ new Set(["failed", "canceled", "onhold"]);
6
+ const formatMinor = (amount, currency) => {
7
+ if (typeof amount !== "number" || !Number.isFinite(amount)) {
8
+ return null;
9
+ }
10
+ const code = typeof currency === "string" ? currency.toUpperCase() : "";
11
+ return `${(amount / 100).toFixed(2)}${code ? ` ${code}` : ""}`;
12
+ };
6
13
  const PrintfulOrderWidget = ({ data }) => {
7
14
  const metadata = data.metadata ?? {};
8
15
  const printfulOrderId = metadata.printful_order_id;
@@ -13,6 +20,19 @@ const PrintfulOrderWidget = ({ data }) => {
13
20
  const statusUpdatedAt = metadata.printful_status_updated_at;
14
21
  const shipments = metadata.printful_shipments ?? [];
15
22
  const isAttention = status ? ATTENTION_STATES.has(status) : false;
23
+ const costTotal = formatMinor(
24
+ metadata.printful_cost_total,
25
+ metadata.printful_cost_currency
26
+ );
27
+ const retailTotal = formatMinor(
28
+ metadata.printful_retail_total,
29
+ metadata.printful_retail_currency
30
+ );
31
+ const margin = formatMinor(
32
+ metadata.printful_margin,
33
+ metadata.printful_cost_currency
34
+ );
35
+ const currencyMismatch = costTotal !== null && retailTotal !== null && margin === null;
16
36
  return /* @__PURE__ */ jsxs(Container, { className: "divide-y p-0", children: [
17
37
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
18
38
  /* @__PURE__ */ jsx(Heading, { level: "h2", children: "Printful" }),
@@ -48,7 +68,23 @@ const PrintfulOrderWidget = ({ data }) => {
48
68
  "Last synced: ",
49
69
  new Date(statusUpdatedAt).toLocaleString()
50
70
  ] }) : null
51
- ] })
71
+ ] }),
72
+ costTotal ? /* @__PURE__ */ jsxs("div", { className: "px-6 py-4 flex flex-col gap-2", children: [
73
+ /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", children: "Economics" }),
74
+ /* @__PURE__ */ jsxs(Text, { size: "small", className: "text-ui-fg-subtle", children: [
75
+ "Printful cost: ",
76
+ costTotal
77
+ ] }),
78
+ retailTotal ? /* @__PURE__ */ jsxs(Text, { size: "small", className: "text-ui-fg-subtle", children: [
79
+ "Retail: ",
80
+ retailTotal
81
+ ] }) : null,
82
+ margin ? /* @__PURE__ */ jsxs(Text, { size: "small", children: [
83
+ "Margin: ",
84
+ margin
85
+ ] }) : null,
86
+ currencyMismatch ? /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "Margin unavailable — Printful billed in a different currency than the order." }) : null
87
+ ] }) : null
52
88
  ] });
53
89
  };
54
90
  defineWidgetConfig({
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MARGIN_KEY = exports.RETAIL_TOTAL_KEY = exports.RETAIL_CURRENCY_KEY = exports.COST_TOTAL_KEY = exports.COST_CURRENCY_KEY = void 0;
4
+ exports.toMinorUnits = toMinorUnits;
5
+ exports.planCostMetadata = planCostMetadata;
6
+ /**
7
+ * Printful reports money as JSON numbers in major units (12.34), and Medusa
8
+ * stores integer minor units (1234). Everything crossing that boundary goes
9
+ * through here.
10
+ *
11
+ * `undefined` means "we could not trust this value" and is deliberately
12
+ * distinct from `0`. Reporting an unparseable cost as zero would show the
13
+ * owner a 100% margin on an order that in fact cost them money.
14
+ */
15
+ function toMinorUnits(value) {
16
+ if (value === null || value === undefined || value === "") {
17
+ return 0;
18
+ }
19
+ let n;
20
+ if (typeof value === "number") {
21
+ n = value;
22
+ }
23
+ else if (typeof value === "string") {
24
+ // Number() rejects trailing garbage that parseFloat would silently accept:
25
+ // Number("4.99abc") is NaN, parseFloat("4.99abc") is 4.99.
26
+ n = Number(value);
27
+ }
28
+ else {
29
+ return undefined;
30
+ }
31
+ if (!Number.isFinite(n)) {
32
+ return undefined;
33
+ }
34
+ return Math.round(n * 100);
35
+ }
36
+ exports.COST_CURRENCY_KEY = "printful_cost_currency";
37
+ exports.COST_TOTAL_KEY = "printful_cost_total";
38
+ exports.RETAIL_CURRENCY_KEY = "printful_retail_currency";
39
+ exports.RETAIL_TOTAL_KEY = "printful_retail_total";
40
+ exports.MARGIN_KEY = "printful_margin";
41
+ /** Cost fields copied verbatim, minus `currency` and `total` which are special. */
42
+ const COST_FIELDS = [
43
+ "subtotal",
44
+ "discount",
45
+ "shipping",
46
+ "digitization",
47
+ "additional_fee",
48
+ "fulfillment_fee",
49
+ "retail_delivery_fee",
50
+ "tax",
51
+ "vat",
52
+ ];
53
+ /**
54
+ * Build the order-metadata patch describing what an order cost and what it
55
+ * earned. Shaped like `planOrderStateActions` so the webhook workflow can
56
+ * merge it the same way.
57
+ *
58
+ * Every amount is in minor units. A field that could not be parsed is omitted
59
+ * rather than defaulted, so a partial Printful response never turns into a
60
+ * confidently wrong margin.
61
+ */
62
+ function planCostMetadata(order) {
63
+ const metadata = {};
64
+ const costs = order.costs;
65
+ const retail = order.retail_costs;
66
+ if (!costs && !retail) {
67
+ return metadata;
68
+ }
69
+ const costCurrency = costs?.currency?.toLowerCase();
70
+ const retailCurrency = retail?.currency?.toLowerCase();
71
+ if (costs) {
72
+ if (costCurrency) {
73
+ metadata[exports.COST_CURRENCY_KEY] = costCurrency;
74
+ }
75
+ const total = toMinorUnits(costs.total);
76
+ if (total !== undefined) {
77
+ metadata[exports.COST_TOTAL_KEY] = total;
78
+ }
79
+ for (const field of COST_FIELDS) {
80
+ const value = toMinorUnits(costs[field]);
81
+ if (value !== undefined && value !== 0) {
82
+ metadata[`printful_cost_${field}`] = value;
83
+ }
84
+ }
85
+ }
86
+ if (retail) {
87
+ if (retailCurrency) {
88
+ metadata[exports.RETAIL_CURRENCY_KEY] = retailCurrency;
89
+ }
90
+ const total = toMinorUnits(retail.total);
91
+ if (total !== undefined) {
92
+ metadata[exports.RETAIL_TOTAL_KEY] = total;
93
+ }
94
+ }
95
+ // Margin only where the subtraction is meaningful. Converting between
96
+ // currencies would need an exchange rate this plugin does not have, and a
97
+ // margin built on a guessed rate is worse than no margin at all.
98
+ const costTotal = metadata[exports.COST_TOTAL_KEY];
99
+ const retailTotal = metadata[exports.RETAIL_TOTAL_KEY];
100
+ if (typeof costTotal === "number" &&
101
+ typeof retailTotal === "number" &&
102
+ costCurrency &&
103
+ retailCurrency &&
104
+ costCurrency === retailCurrency) {
105
+ metadata[exports.MARGIN_KEY] = retailTotal - costTotal;
106
+ }
107
+ return metadata;
108
+ }
109
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29zdHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvdXRpbHMvY29zdHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBV0Esb0NBdUJDO0FBOEJELDRDQXdEQztBQXRIRDs7Ozs7Ozs7R0FRRztBQUNILFNBQWdCLFlBQVksQ0FDMUIsS0FBeUM7SUFFekMsSUFBSSxLQUFLLEtBQUssSUFBSSxJQUFJLEtBQUssS0FBSyxTQUFTLElBQUksS0FBSyxLQUFLLEVBQUUsRUFBRSxDQUFDO1FBQzFELE9BQU8sQ0FBQyxDQUFBO0lBQ1YsQ0FBQztJQUVELElBQUksQ0FBUyxDQUFBO0lBQ2IsSUFBSSxPQUFPLEtBQUssS0FBSyxRQUFRLEVBQUUsQ0FBQztRQUM5QixDQUFDLEdBQUcsS0FBSyxDQUFBO0lBQ1gsQ0FBQztTQUFNLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUSxFQUFFLENBQUM7UUFDckMsMkVBQTJFO1FBQzNFLDJEQUEyRDtRQUMzRCxDQUFDLEdBQUcsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFBO0lBQ25CLENBQUM7U0FBTSxDQUFDO1FBQ04sT0FBTyxTQUFTLENBQUE7SUFDbEIsQ0FBQztJQUVELElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDeEIsT0FBTyxTQUFTLENBQUE7SUFDbEIsQ0FBQztJQUVELE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLEdBQUcsR0FBRyxDQUFDLENBQUE7QUFDNUIsQ0FBQztBQUVZLFFBQUEsaUJBQWlCLEdBQUcsd0JBQXdCLENBQUE7QUFDNUMsUUFBQSxjQUFjLEdBQUcscUJBQXFCLENBQUE7QUFDdEMsUUFBQSxtQkFBbUIsR0FBRywwQkFBMEIsQ0FBQTtBQUNoRCxRQUFBLGdCQUFnQixHQUFHLHVCQUF1QixDQUFBO0FBQzFDLFFBQUEsVUFBVSxHQUFHLGlCQUFpQixDQUFBO0FBRTNDLG1GQUFtRjtBQUNuRixNQUFNLFdB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
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.planOrderStateActions = planOrderStateActions;
4
+ const costs_1 = require("./costs");
4
5
  /** Printful states that mean a human should look at the order. */
5
6
  const ATTENTION_STATES = new Set(["failed", "canceled", "onhold"]);
6
7
  /**
@@ -35,6 +36,9 @@ function planOrderStateActions(order, recordedShipmentIds) {
35
36
  return {
36
37
  shipments: allShipments.filter((s) => !recorded.has(s.printful_shipment_id)),
37
38
  metadata: {
39
+ // Spread first so a status key always wins a name collision rather than
40
+ // being silently overwritten by a cost key.
41
+ ...(0, costs_1.planCostMetadata)(order),
38
42
  printful_order_id: String(order.id),
39
43
  printful_status: order.status,
40
44
  printful_status_updated_at: new Date().toISOString(),
@@ -55,4 +59,4 @@ function planOrderStateActions(order, recordedShipmentIds) {
55
59
  needsAttention: ATTENTION_STATES.has(order.status),
56
60
  };
57
61
  }
58
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3JkZXItc3RhdGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvdXRpbHMvb3JkZXItc3RhdGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUE2Q0Esc0RBOENDO0FBN0RELGtFQUFrRTtBQUNsRSxNQUFNLGdCQUFnQixHQUFHLElBQUksR0FBRyxDQUFDLENBQUMsUUFBUSxFQUFFLFVBQVUsRUFBRSxRQUFRLENBQUMsQ0FBQyxDQUFBO0FBRWxFOzs7Ozs7Ozs7OztHQVdHO0FBQ0gsU0FBZ0IscUJBQXFCLENBQ25DLEtBQW9CLEVBQ3BCLG1CQUE2QjtJQUU3QixNQUFNLFFBQVEsR0FBRyxJQUFJLEdBQUcsQ0FBQyxtQkFBbUIsQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQTtJQUV6RCxNQUFNLFlBQVksR0FBc0IsQ0FBQyxLQUFLLENBQUMsU0FBUyxJQUFJLEVBQUUsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQztRQUMxRSxvQkFBb0IsRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUNsQyxPQUFPLEVBQUUsQ0FBQyxDQUFDLE9BQU87UUFDbEIsT0FBTyxFQUFFLENBQUMsQ0FBQyxPQUFPO1FBQ2xCLGVBQWUsRUFBRSxDQUFDLENBQUMsZUFBZTtRQUNsQyxZQUFZLEVBQUUsQ0FBQyxDQUFDLFlBQVk7UUFDNUIsU0FBUyxFQUFFLENBQUMsQ0FBQyxTQUFTO1FBQ3RCLFVBQVUsRUFBRSxDQUFDLENBQUMsVUFBVTtRQUN4QiwwRUFBMEU7UUFDMUUsMkRBQTJEO1FBQzNELEtBQUssRUFBRSxDQUFDLENBQUMsS0FBSyxFQUFFLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQztZQUMxQixPQUFPLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUM7WUFDMUIsUUFBUSxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDO1NBQzdCLENBQUMsQ0FBQztLQUNKLENBQUMsQ0FBQyxDQUFBO0lBRUgsT0FBTztRQUNMLFNBQVMsRUFBRSxZQUFZLENBQUMsTUFBTSxDQUM1QixDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxvQkFBb0IsQ0FBQyxDQUM3QztRQUNELFFBQVEsRUFBRTtZQUNSLGlCQUFpQixFQUFFLE1BQU0sQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQ25DLGVBQWUsRUFBRSxLQUFLLENBQUMsTUFBTTtZQUM3QiwwQkFBMEIsRUFBRSxJQUFJLElBQUksRUFBRSxDQUFDLFdBQVcsRUFBRTtZQUNwRCxrQkFBa0IsRUFBRSxZQUFZLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO2dCQUMzQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLG9CQUFvQjtnQkFDMUIsT0FBTyxFQUFFLENBQUMsQ0FBQyxPQUFPO2dCQUNsQixPQUFPLEVBQUUsQ0FBQyxDQUFDLE9BQU87Z0JBQ2xCLGVBQWUsRUFBRSxDQUFDLENBQUMsZUFBZTtnQkFDbEMsWUFBWSxFQUFFLENBQUMsQ0FBQyxZQUFZO2dCQUM1QixTQUFTLEVBQUUsQ0FBQyxDQUFDLFNBQVM7Z0JBQ3RCLHdFQUF3RTtnQkFDeEUsc0VBQXNFO2dCQUN0RSxvRUFBb0U7Z0JBQ3BFLGdFQUFnRTtnQkFDaEUsVUFBVSxFQUFFLENBQUMsQ0FBQyxVQUFVLElBQUksS0FBSzthQUNsQyxDQUFDLENBQUM7U0FDSjtRQUNELGNBQWMsRUFBRSxnQkFBZ0IsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQztLQUNuRCxDQUFBO0FBQ0gsQ0FBQyJ9
62
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3JkZXItc3RhdGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvdXRpbHMvb3JkZXItc3RhdGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUE4Q0Esc0RBaURDO0FBOUZELG1DQUEwQztBQThCMUMsa0VBQWtFO0FBQ2xFLE1BQU0sZ0JBQWdCLEdBQUcsSUFBSSxHQUFHLENBQUMsQ0FBQyxRQUFRLEVBQUUsVUFBVSxFQUFFLFFBQVEsQ0FBQyxDQUFDLENBQUE7QUFFbEU7Ozs7Ozs7Ozs7O0dBV0c7QUFDSCxTQUFnQixxQkFBcUIsQ0FDbkMsS0FBb0IsRUFDcEIsbUJBQTZCO0lBRTdCLE1BQU0sUUFBUSxHQUFHLElBQUksR0FBRyxDQUFDLG1CQUFtQixDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFBO0lBRXpELE1BQU0sWUFBWSxHQUFzQixDQUFDLEtBQUssQ0FBQyxTQUFTLElBQUksRUFBRSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBQzFFLG9CQUFvQixFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQ2xDLE9BQU8sRUFBRSxDQUFDLENBQUMsT0FBTztRQUNsQixPQUFPLEVBQUUsQ0FBQyxDQUFDLE9BQU87UUFDbEIsZUFBZSxFQUFFLENBQUMsQ0FBQyxlQUFlO1FBQ2xDLFlBQVksRUFBRSxDQUFDLENBQUMsWUFBWTtRQUM1QixTQUFTLEVBQUUsQ0FBQyxDQUFDLFNBQVM7UUFDdEIsVUFBVSxFQUFFLENBQUMsQ0FBQyxVQUFVO1FBQ3hCLDBFQUEwRTtRQUMxRSwyREFBMkQ7UUFDM0QsS0FBSyxFQUFFLENBQUMsQ0FBQyxLQUFLLEVBQUUsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1lBQzFCLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQztZQUMxQixRQUFRLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUM7U0FDN0IsQ0FBQyxDQUFDO0tBQ0osQ0FBQyxDQUFDLENBQUE7SUFFSCxPQUFPO1FBQ0wsU0FBUyxFQUFFLFlBQVksQ0FBQyxNQUFNLENBQzVCLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLG9CQUFvQixDQUFDLENBQzdDO1FBQ0QsUUFBUSxFQUFFO1lBQ1Isd0VBQXdFO1lBQ3hFLDRDQUE0QztZQUM1QyxHQUFHLElBQUEsd0JBQWdCLEVBQUMsS0FBSyxDQUFDO1lBQzFCLGlCQUFpQixFQUFFLE1BQU0sQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQ25DLGVBQWUsRUFBRSxLQUFLLENBQUMsTUFBTTtZQUM3QiwwQkFBMEIsRUFBRSxJQUFJLElBQUksRUFBRSxDQUFDLFdBQVcsRUFBRTtZQUNwRCxrQkFBa0IsRUFBRSxZQUFZLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO2dCQUMzQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLG9CQUFvQjtnQkFDMUIsT0FBTyxFQUFFLENBQUMsQ0FBQyxPQUFPO2dCQUNsQixPQUFPLEVBQUUsQ0FBQyxDQUFDLE9BQU87Z0JBQ2xCLGVBQWUsRUFBRSxDQUFDLENBQUMsZUFBZTtnQkFDbEMsWUFBWSxFQUFFLENBQUMsQ0FBQyxZQUFZO2dCQUM1QixTQUFTLEVBQUUsQ0FBQyxDQUFDLFNBQVM7Z0JBQ3RCLHdFQUF3RTtnQkFDeEUsc0VBQXNFO2dCQUN0RSxvRUFBb0U7Z0JBQ3BFLGdFQUFnRTtnQkFDaEUsVUFBVSxFQUFFLENBQUMsQ0FBQyxVQUFVLElBQUksS0FBSzthQUNsQyxDQUFDLENBQUM7U0FDSjtRQUNELGNBQWMsRUFBRSxnQkFBZ0IsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQztLQUNuRCxDQUFBO0FBQ0gsQ0FBQyJ9
@@ -5,9 +5,11 @@ const workflows_sdk_1 = require("@medusajs/framework/workflows-sdk");
5
5
  const utils_1 = require("@medusajs/framework/utils");
6
6
  const printful_1 = require("../modules/printful");
7
7
  const mappers_1 = require("../utils/mappers");
8
+ const costs_1 = require("../utils/costs");
8
9
  const createPrintfulOrderStep = (0, workflows_sdk_1.createStep)("printful-create-order", async (input, { container }) => {
9
10
  const printful = container.resolve(printful_1.PRINTFUL_MODULE);
10
11
  const orderModule = container.resolve(utils_1.Modules.ORDER);
12
+ const logger = container.resolve(utils_1.ContainerRegistrationKeys.LOGGER);
11
13
  const options = await printful.getOptions();
12
14
  const client = await printful.getClient();
13
15
  const existing = await printful.findOrderLink(input.order_id);
@@ -110,6 +112,26 @@ const createPrintfulOrderStep = (0, workflows_sdk_1.createStep)("printful-create
110
112
  printful_order_id: String(pfOrder.id),
111
113
  status: pfOrder.status || "created",
112
114
  });
115
+ // Printful returns the real costs with the created order, so no separate
116
+ // estimate call is needed. Written best-effort: the order exists in
117
+ // Printful either way, and losing the margin figure must never fail the
118
+ // workflow and roll back a real order.
119
+ const costMetadata = (0, costs_1.planCostMetadata)(pfOrder);
120
+ if (Object.keys(costMetadata).length > 0) {
121
+ try {
122
+ // `orderRow` rather than `existing` — that name is taken by the
123
+ // order-link lookup earlier in this step.
124
+ const orderRow = await orderModule.retrieveOrder(input.order_id, {
125
+ select: ["id", "metadata"],
126
+ });
127
+ await orderModule.updateOrders(input.order_id, {
128
+ metadata: { ...(orderRow.metadata ?? {}), ...costMetadata },
129
+ });
130
+ }
131
+ catch (err) {
132
+ logger.error(`Printful order ${pfOrder.id}: could not store costs: ${err instanceof Error ? err.message : String(err)}`);
133
+ }
134
+ }
113
135
  return new workflows_sdk_1.StepResponse({
114
136
  skipped: false,
115
137
  printful_order_id: String(pfOrder.id),
@@ -121,4 +143,4 @@ exports.createPrintfulOrderWorkflow = (0, workflows_sdk_1.createWorkflow)("print
121
143
  return new workflows_sdk_1.WorkflowResponse(result);
122
144
  });
123
145
  exports.default = exports.createPrintfulOrderWorkflow;
124
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JlYXRlLXByaW50ZnVsLW9yZGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL3dvcmtmbG93cy9jcmVhdGUtcHJpbnRmdWwtb3JkZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEscUVBSzBDO0FBQzFDLHFEQUFtRDtBQUNuRCxrREFBcUQ7QUFPckQsOENBQW1EO0FBa0JuRCxNQUFNLHVCQUF1QixHQUFHLElBQUEsMEJBQVUsRUFDeEMsdUJBQXVCLEVBQ3ZCLEtBQUssRUFBRSxLQUErQixFQUFFLEVBQUUsU0FBUyxFQUFFLEVBQUUsRUFBRTtJQUN2RCxNQUFNLFFBQVEsR0FBMEIsU0FBUyxDQUFDLE9BQU8sQ0FBQywwQkFBZSxDQUFDLENBQUE7SUFDMUUsTUFBTSxXQUFXLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FBQyxlQUFPLENBQUMsS0FBSyxDQUFDLENBQUE7SUFDcEQsTUFBTSxPQUFPLEdBQUcsTUFBTSxRQUFRLENBQUMsVUFBVSxFQUFFLENBQUE7SUFDM0MsTUFBTSxNQUFNLEdBQUcsTUFBTSxRQUFRLENBQUMsU0FBUyxFQUFFLENBQUE7SUFFekMsTUFBTSxRQUFRLEdBQUcsTUFBTSxRQUFRLENBQUMsYUFBYSxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUMsQ0FBQTtJQUM3RCxJQUFJLFFBQVEsRUFBRSxDQUFDO1FBQ2IsT0FBTyxJQUFJLDRCQUFZLENBQWU7WUFDcEMsT0FBTyxFQUFFLElBQUk7WUFDYixNQUFNLEVBQUUsZ0JBQWdCO1lBQ3hCLGlCQUFpQixFQUFFLFFBQVEsQ0FBQyxpQkFBaUI7U0FDOUMsQ0FBQyxDQUFBO0lBQ0osQ0FBQztJQUVELE1BQU0sS0FBSyxHQUFHLE1BQU0sV0FBVyxDQUFDLGFBQWEsQ0FBQyxLQUFLLENBQUMsUUFBUSxFQUFFO1FBQzVELFNBQVMsRUFBRSxDQUFDLE9BQU8sRUFBRSxrQkFBa0IsQ0FBQztLQUN6QyxDQUFDLENBQUE7SUFFRixNQUFNLEtBQUssR0FB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
146
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JlYXRlLXByaW50ZnVsLW9yZGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL3dvcmtmbG93cy9jcmVhdGUtcHJpbnRmdWwtb3JkZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEscUVBSzBDO0FBQzFDLHFEQUE4RTtBQUM5RSxrREFBcUQ7QUFPckQsOENBQW1EO0FBQ25ELDBDQUFpRDtBQWtCakQsTUFBTSx1QkFBdUIsR0FBRyxJQUFBLDBCQUFVLEVBQ3hDLHVCQUF1QixFQUN2QixLQUFLLEVBQUUsS0FBK0IsRUFBRSxFQUFFLFNBQVMsRUFBRSxFQUFFLEVBQUU7SUFDdkQsTUFBTSxRQUFRLEdBQTBCLFNBQVMsQ0FBQyxPQUFPLENBQUMsMEJBQWUsQ0FBQyxDQUFBO0lBQzFFLE1BQU0sV0FBVyxHQUFHLFNBQVMsQ0FBQyxPQUFPLENBQUMsZUFBTyxDQUFDLEtBQUssQ0FBQyxDQUFBO0lBQ3BELE1BQU0sTUFBTSxHQUFHLFNBQVMsQ0FBQyxPQUFPLENBQUMsaUNBQXlCLENBQUMsTUFBTSxDQUFDLENBQUE7SUFDbEUsTUFBTSxPQUFPLEdBQUcsTUFBTSxRQUFRLENBQUMsVUFBVSxFQUFFLENBQUE7SUFDM0MsTUFBTSxNQUFNLEdBQUcsTUFBTSxRQUFRLENBQUMsU0FBUyxFQUFFLENBQUE7SUFFekMsTUFBTSxRQUFRLEdBQUcsTUFBTSxRQUFRLENBQUMsYUFBYSxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUMsQ0FBQTtJQUM3RCxJQUFJLFFBQVEsRUFBRSxDQUFDO1FBQ2IsT0FBTyxJQUFJLDRCQUFZLENBQWU7WUFDcEMsT0FBTyxFQUFFLElBQUk7WUFDYixNQUFNLEVBQUUsZ0JBQWdCO1lBQ3hCLGlCQUFpQixFQUFFLFFBQVEsQ0FBQyxpQkFBaUI7U0FDOUMsQ0FBQyxDQUFBO0lBQ0osQ0FBQztJQUVELE1BQU0sS0FBSyxHQUFHLE1BQU0sV0FBVyxDQUFDLGFBQWEsQ0FBQyxLQUFLLENBQUMsUUFBUSxF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
package/README.md CHANGED
@@ -273,6 +273,28 @@ unpublished itself — a product you set to draft by hand stays draft. Variants
273
273
  carry `printful_availability_status` in metadata, and discontinued products get
274
274
  `printful_discontinued` unless `onDiscontinued: "ignore"`.
275
275
 
276
+ ## Order economics
277
+
278
+ Printful returns what it charged along with the created order, so the plugin
279
+ stores it on the Medusa order rather than making a second API call. The order
280
+ page shows the Printful cost, the retail total, and the margin between them.
281
+ The figures are refreshed whenever a webhook re-reads the order, because
282
+ Printful finalizes shipping and fees at fulfillment.
283
+
284
+ Amounts are stored in minor units under `printful_cost_*`, `printful_retail_*`
285
+ and `printful_margin` in order metadata.
286
+
287
+ **Margin is only shown when both figures are in the same currency.** If
288
+ Printful bills in USD while the order is in EUR, both totals are stored and the
289
+ margin is withheld — converting would need an exchange rate this plugin does
290
+ not have, and a margin built on a guessed rate is worse than none.
291
+
292
+ The order page deliberately shows only the two totals and the margin, not the
293
+ per-fee breakdown. Those three are always written together from one response,
294
+ so they cannot disagree; the individual `printful_cost_*` fee keys are refreshed
295
+ per-key and a fee absent from a later response keeps its previous value, so a
296
+ breakdown need not sum to the total.
297
+
276
298
  ## Admin usage
277
299
 
278
300
  1. Create products in Printful (Store Products).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@legenki/print2medusa",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Printful → Medusa v2 plugin: product sync, auto fulfillment, and admin tools",
5
5
  "author": "Andy Legenki",
6
6
  "license": "MIT",