@endora-commerce/mod-carts 0.100.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.
Files changed (155) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +62 -0
  3. package/dist/admin/components/CartApprovalPolicyPanel.d.ts +51 -0
  4. package/dist/admin/components/CartApprovalPolicyPanel.d.ts.map +1 -0
  5. package/dist/admin/components/CartApprovalPolicyPanel.js +59 -0
  6. package/dist/admin/components/CartApprovalPolicyPanel.js.map +1 -0
  7. package/dist/admin/index.d.ts +38 -0
  8. package/dist/admin/index.d.ts.map +1 -0
  9. package/dist/admin/index.js +73 -0
  10. package/dist/admin/index.js.map +1 -0
  11. package/dist/admin/pages/CartDetail.d.ts +3 -0
  12. package/dist/admin/pages/CartDetail.d.ts.map +1 -0
  13. package/dist/admin/pages/CartDetail.js +74 -0
  14. package/dist/admin/pages/CartDetail.js.map +1 -0
  15. package/dist/admin/pages/CartsList.d.ts +3 -0
  16. package/dist/admin/pages/CartsList.d.ts.map +1 -0
  17. package/dist/admin/pages/CartsList.js +90 -0
  18. package/dist/admin/pages/CartsList.js.map +1 -0
  19. package/dist/admin/zones/CartApprovalPolicy.d.ts +29 -0
  20. package/dist/admin/zones/CartApprovalPolicy.d.ts.map +1 -0
  21. package/dist/admin/zones/CartApprovalPolicy.js +11 -0
  22. package/dist/admin/zones/CartApprovalPolicy.js.map +1 -0
  23. package/dist/backend/cli/abandonment-sweep.d.ts +64 -0
  24. package/dist/backend/cli/abandonment-sweep.d.ts.map +1 -0
  25. package/dist/backend/cli/abandonment-sweep.js +17 -0
  26. package/dist/backend/cli/abandonment-sweep.js.map +1 -0
  27. package/dist/backend/entities/cart-audit-entry.entity.d.ts +29 -0
  28. package/dist/backend/entities/cart-audit-entry.entity.d.ts.map +1 -0
  29. package/dist/backend/entities/cart-audit-entry.entity.js +109 -0
  30. package/dist/backend/entities/cart-audit-entry.entity.js.map +1 -0
  31. package/dist/backend/entities/cart-item.entity.d.ts +42 -0
  32. package/dist/backend/entities/cart-item.entity.d.ts.map +1 -0
  33. package/dist/backend/entities/cart-item.entity.js +119 -0
  34. package/dist/backend/entities/cart-item.entity.js.map +1 -0
  35. package/dist/backend/entities/cart.entity.d.ts +96 -0
  36. package/dist/backend/entities/cart.entity.d.ts.map +1 -0
  37. package/dist/backend/entities/cart.entity.js +207 -0
  38. package/dist/backend/entities/cart.entity.js.map +1 -0
  39. package/dist/backend/index.d.ts +122 -0
  40. package/dist/backend/index.d.ts.map +1 -0
  41. package/dist/backend/index.js +184 -0
  42. package/dist/backend/index.js.map +1 -0
  43. package/dist/backend/routes.admin.d.ts +31 -0
  44. package/dist/backend/routes.admin.d.ts.map +1 -0
  45. package/dist/backend/routes.admin.js +95 -0
  46. package/dist/backend/routes.admin.js.map +1 -0
  47. package/dist/backend/routes.d.ts +101 -0
  48. package/dist/backend/routes.d.ts.map +1 -0
  49. package/dist/backend/routes.js +620 -0
  50. package/dist/backend/routes.js.map +1 -0
  51. package/dist/backend/routes.organization.d.ts +29 -0
  52. package/dist/backend/routes.organization.d.ts.map +1 -0
  53. package/dist/backend/routes.organization.js +150 -0
  54. package/dist/backend/routes.organization.js.map +1 -0
  55. package/dist/backend/services/cart-abandonment-settings.d.ts +34 -0
  56. package/dist/backend/services/cart-abandonment-settings.d.ts.map +1 -0
  57. package/dist/backend/services/cart-abandonment-settings.js +55 -0
  58. package/dist/backend/services/cart-abandonment-settings.js.map +1 -0
  59. package/dist/backend/services/cart-abandonment-worker.d.ts +110 -0
  60. package/dist/backend/services/cart-abandonment-worker.d.ts.map +1 -0
  61. package/dist/backend/services/cart-abandonment-worker.js +185 -0
  62. package/dist/backend/services/cart-abandonment-worker.js.map +1 -0
  63. package/dist/backend/services/cart-admin-service.d.ts +58 -0
  64. package/dist/backend/services/cart-admin-service.d.ts.map +1 -0
  65. package/dist/backend/services/cart-admin-service.js +397 -0
  66. package/dist/backend/services/cart-admin-service.js.map +1 -0
  67. package/dist/backend/services/cart-approval-service.d.ts +147 -0
  68. package/dist/backend/services/cart-approval-service.d.ts.map +1 -0
  69. package/dist/backend/services/cart-approval-service.js +242 -0
  70. package/dist/backend/services/cart-approval-service.js.map +1 -0
  71. package/dist/backend/services/cart-audit-service.d.ts +75 -0
  72. package/dist/backend/services/cart-audit-service.d.ts.map +1 -0
  73. package/dist/backend/services/cart-audit-service.js +70 -0
  74. package/dist/backend/services/cart-audit-service.js.map +1 -0
  75. package/dist/backend/services/cart-conversion-service.d.ts +104 -0
  76. package/dist/backend/services/cart-conversion-service.d.ts.map +1 -0
  77. package/dist/backend/services/cart-conversion-service.js +175 -0
  78. package/dist/backend/services/cart-conversion-service.js.map +1 -0
  79. package/dist/backend/services/cart-coupon-service.d.ts +129 -0
  80. package/dist/backend/services/cart-coupon-service.d.ts.map +1 -0
  81. package/dist/backend/services/cart-coupon-service.js +244 -0
  82. package/dist/backend/services/cart-coupon-service.js.map +1 -0
  83. package/dist/backend/services/cart-organization-visibility-service.d.ts +43 -0
  84. package/dist/backend/services/cart-organization-visibility-service.d.ts.map +1 -0
  85. package/dist/backend/services/cart-organization-visibility-service.js +98 -0
  86. package/dist/backend/services/cart-organization-visibility-service.js.map +1 -0
  87. package/dist/backend/services/cart-placement-apply-port.d.ts +38 -0
  88. package/dist/backend/services/cart-placement-apply-port.d.ts.map +1 -0
  89. package/dist/backend/services/cart-placement-apply-port.js +71 -0
  90. package/dist/backend/services/cart-placement-apply-port.js.map +1 -0
  91. package/dist/backend/services/cart-pricing-recompute.d.ts +62 -0
  92. package/dist/backend/services/cart-pricing-recompute.d.ts.map +1 -0
  93. package/dist/backend/services/cart-pricing-recompute.js +131 -0
  94. package/dist/backend/services/cart-pricing-recompute.js.map +1 -0
  95. package/dist/backend/services/cart-query-service.d.ts +29 -0
  96. package/dist/backend/services/cart-query-service.d.ts.map +1 -0
  97. package/dist/backend/services/cart-query-service.js +40 -0
  98. package/dist/backend/services/cart-query-service.js.map +1 -0
  99. package/dist/backend/services/cart-read-port.d.ts +40 -0
  100. package/dist/backend/services/cart-read-port.d.ts.map +1 -0
  101. package/dist/backend/services/cart-read-port.js +144 -0
  102. package/dist/backend/services/cart-read-port.js.map +1 -0
  103. package/dist/backend/services/cart-recompute-cache.d.ts +49 -0
  104. package/dist/backend/services/cart-recompute-cache.d.ts.map +1 -0
  105. package/dist/backend/services/cart-recompute-cache.js +56 -0
  106. package/dist/backend/services/cart-recompute-cache.js.map +1 -0
  107. package/dist/backend/services/cart-service.d.ts +165 -0
  108. package/dist/backend/services/cart-service.d.ts.map +1 -0
  109. package/dist/backend/services/cart-service.js +556 -0
  110. package/dist/backend/services/cart-service.js.map +1 -0
  111. package/dist/backend/services/cart-state-machine.d.ts +51 -0
  112. package/dist/backend/services/cart-state-machine.d.ts.map +1 -0
  113. package/dist/backend/services/cart-state-machine.js +74 -0
  114. package/dist/backend/services/cart-state-machine.js.map +1 -0
  115. package/dist/backend/services/cart-upsell-service.d.ts +57 -0
  116. package/dist/backend/services/cart-upsell-service.d.ts.map +1 -0
  117. package/dist/backend/services/cart-upsell-service.js +100 -0
  118. package/dist/backend/services/cart-upsell-service.js.map +1 -0
  119. package/dist/backend/services/customer-group-resolver.d.ts +27 -0
  120. package/dist/backend/services/customer-group-resolver.d.ts.map +1 -0
  121. package/dist/backend/services/customer-group-resolver.js +32 -0
  122. package/dist/backend/services/customer-group-resolver.js.map +1 -0
  123. package/dist/manifest.d.ts +221 -0
  124. package/dist/manifest.d.ts.map +1 -0
  125. package/dist/manifest.js +224 -0
  126. package/dist/manifest.js.map +1 -0
  127. package/dist/migrations/20260611T140352_carts_consolidation.d.ts +43 -0
  128. package/dist/migrations/20260611T140352_carts_consolidation.d.ts.map +1 -0
  129. package/dist/migrations/20260611T140352_carts_consolidation.js +237 -0
  130. package/dist/migrations/20260611T140352_carts_consolidation.js.map +1 -0
  131. package/dist/migrations/20260611T140413_carts_cart_item_packaging.d.ts +12 -0
  132. package/dist/migrations/20260611T140413_carts_cart_item_packaging.d.ts.map +1 -0
  133. package/dist/migrations/20260611T140413_carts_cart_item_packaging.js +20 -0
  134. package/dist/migrations/20260611T140413_carts_cart_item_packaging.js.map +1 -0
  135. package/dist/migrations/20260818T081253_carts_cart_completed_order_fk.d.ts +50 -0
  136. package/dist/migrations/20260818T081253_carts_cart_completed_order_fk.d.ts.map +1 -0
  137. package/dist/migrations/20260818T081253_carts_cart_completed_order_fk.js +62 -0
  138. package/dist/migrations/20260818T081253_carts_cart_completed_order_fk.js.map +1 -0
  139. package/dist/migrations/20260830T112911_carts_organization_attribution_check.d.ts +67 -0
  140. package/dist/migrations/20260830T112911_carts_organization_attribution_check.d.ts.map +1 -0
  141. package/dist/migrations/20260830T112911_carts_organization_attribution_check.js +121 -0
  142. package/dist/migrations/20260830T112911_carts_organization_attribution_check.js.map +1 -0
  143. package/dist/migrations/index.d.ts +30 -0
  144. package/dist/migrations/index.d.ts.map +1 -0
  145. package/dist/migrations/index.js +35 -0
  146. package/dist/migrations/index.js.map +1 -0
  147. package/dist/ports/index.d.ts +106 -0
  148. package/dist/ports/index.d.ts.map +1 -0
  149. package/dist/ports/index.js +2 -0
  150. package/dist/ports/index.js.map +1 -0
  151. package/docs/carts.md +232 -0
  152. package/i18n/en.json +96 -0
  153. package/i18n/pl.json +96 -0
  154. package/package.json +107 -0
  155. package/tailwind.css +14 -0
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Returns the post-event approval state for a cart whose current
3
+ * approval state is `from`. Pre-flight checks (policy on, cart not
4
+ * empty, buyer-not-org-admin, etc.) live in the service layer; this
5
+ * function captures only the FSM legality of (state, event) pairs.
6
+ */
7
+ export function nextApprovalState(from, event) {
8
+ if (from === 'rejected_by_org_admin') {
9
+ // Terminal on the approval axis. No event re-opens it; a new cart
10
+ // must be created.
11
+ return { ok: false, reason: 'already_terminal' };
12
+ }
13
+ switch (event) {
14
+ case 'submit':
15
+ if (from === 'not_required')
16
+ return { ok: true, next: 'pending' };
17
+ return { ok: false, reason: 'already_initiated' };
18
+ case 'approve':
19
+ if (from === 'pending')
20
+ return { ok: true, next: 'approved' };
21
+ return { ok: false, reason: 'not_pending' };
22
+ case 'reject':
23
+ if (from === 'pending')
24
+ return { ok: true, next: 'rejected_by_org_admin' };
25
+ return { ok: false, reason: 'not_pending' };
26
+ case 'buyer_mutation':
27
+ // Re-arm rule: any buyer-driven mutation on an `approved` cart
28
+ // silently drops it back to `pending`. Any other state is a no-op
29
+ // (the service short-circuits before calling).
30
+ if (from === 'approved')
31
+ return { ok: true, next: 'pending' };
32
+ return { ok: false, reason: 'not_applicable' };
33
+ case 'policy_off':
34
+ // Turning the org-wide policy off resets every pending/approved
35
+ // cart back to not_required. not_required carts stay put.
36
+ if (from === 'pending' || from === 'approved') {
37
+ return { ok: true, next: 'not_required' };
38
+ }
39
+ return { ok: false, reason: 'not_applicable' };
40
+ case 'policy_on':
41
+ // Turning the policy on does NOT auto-flip existing not_required
42
+ // carts to pending — the buyer must explicitly submit. The hook
43
+ // exists so audit-row builders can decide whether to record a
44
+ // pure policy-change event.
45
+ return { ok: false, reason: 'not_applicable' };
46
+ }
47
+ }
48
+ export function derivePrimaryCta(input) {
49
+ if (input.status === 'rejected' || input.status === 'completed') {
50
+ return 'blocked_by_organization';
51
+ }
52
+ if (input.approvalStatus === 'pending')
53
+ return 'awaiting_approval';
54
+ if (input.approvalStatus === 'rejected_by_org_admin') {
55
+ return 'blocked_by_organization';
56
+ }
57
+ // `not_required` and `approved` both → checkout. The
58
+ // `submit_for_approval` CTA is only emitted when the Org policy is on
59
+ // and the buyer has not yet submitted.
60
+ if (input.approvalStatus === 'not_required' && input.requiresApproval === true) {
61
+ return 'submit_for_approval';
62
+ }
63
+ return 'checkout';
64
+ }
65
+ /**
66
+ * `true` when an `approved` cart should be silently re-armed to
67
+ * `pending` because the buyer just mutated it. Lives here so the
68
+ * various write paths (add / update / remove / coupon apply / coupon
69
+ * clear) all agree on the same rule.
70
+ */
71
+ export function shouldReArmOnBuyerMutation(approvalStatus) {
72
+ return approvalStatus === 'approved';
73
+ }
74
+ //# sourceMappingURL=cart-state-machine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cart-state-machine.js","sourceRoot":"","sources":["../../../src/backend/services/cart-state-machine.ts"],"names":[],"mappings":"AAoCA;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAC/B,IAAwB,EACxB,KAAoB;IAEpB,IAAI,IAAI,KAAK,uBAAuB,EAAE,CAAC;QACrC,kEAAkE;QAClE,mBAAmB;QACnB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC;IACnD,CAAC;IAED,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,QAAQ;YACX,IAAI,IAAI,KAAK,cAAc;gBAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;YAClE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,mBAAmB,EAAE,CAAC;QAEpD,KAAK,SAAS;YACZ,IAAI,IAAI,KAAK,SAAS;gBAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;YAC9D,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;QAE9C,KAAK,QAAQ;YACX,IAAI,IAAI,KAAK,SAAS;gBAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,uBAAuB,EAAE,CAAC;YAC3E,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;QAE9C,KAAK,gBAAgB;YACnB,+DAA+D;YAC/D,kEAAkE;YAClE,+CAA+C;YAC/C,IAAI,IAAI,KAAK,UAAU;gBAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;YAC9D,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;QAEjD,KAAK,YAAY;YACf,gEAAgE;YAChE,0DAA0D;YAC1D,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;gBAC9C,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC;YAC5C,CAAC;YACD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;QAEjD,KAAK,WAAW;YACd,iEAAiE;YACjE,gEAAgE;YAChE,8DAA8D;YAC9D,4BAA4B;YAC5B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;IACnD,CAAC;AACH,CAAC;AAaD,MAAM,UAAU,gBAAgB,CAAC,KAIhC;IACC,IAAI,KAAK,CAAC,MAAM,KAAK,UAAU,IAAI,KAAK,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;QAChE,OAAO,yBAAyB,CAAC;IACnC,CAAC;IACD,IAAI,KAAK,CAAC,cAAc,KAAK,SAAS;QAAE,OAAO,mBAAmB,CAAC;IACnE,IAAI,KAAK,CAAC,cAAc,KAAK,uBAAuB,EAAE,CAAC;QACrD,OAAO,yBAAyB,CAAC;IACnC,CAAC;IACD,qDAAqD;IACrD,sEAAsE;IACtE,uCAAuC;IACvC,IAAI,KAAK,CAAC,cAAc,KAAK,cAAc,IAAI,KAAK,CAAC,gBAAgB,KAAK,IAAI,EAAE,CAAC;QAC/E,OAAO,qBAAqB,CAAC;IAC/B,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,0BAA0B,CACxC,cAAkC;IAElC,OAAO,cAAc,KAAK,UAAU,CAAC;AACvC,CAAC"}
@@ -0,0 +1,57 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql';
2
+ import { type CatalogProductReadPort, type ProductAudience } from '@endora-commerce/contracts';
3
+ /**
4
+ * Cart up-sell aggregator (feature 027 §R9 / FR-039).
5
+ *
6
+ * Walks `product_links` (kind = `up_sell`) for every product currently in
7
+ * the cart, aggregates by target product, drops candidates that are
8
+ * already in the cart, and ranks by match-count (most-linked-from first),
9
+ * tie-breaking by older `created_at`.
10
+ *
11
+ * The catalog module's `ProductLink.kind = 'up_sell'` is the source the
12
+ * spec calls "Up-sell" (the same relation surfaced on PDP). The strip's
13
+ * placement is the cart UI; the relation type is the same.
14
+ */
15
+ export interface CartUpsellCandidate {
16
+ productId: string;
17
+ productName: string;
18
+ productSlug: string;
19
+ productThumbnailUrl: string | null;
20
+ matchCount: number;
21
+ }
22
+ export declare class CartUpsellService {
23
+ private readonly emFactory;
24
+ /**
25
+ * `catalog`'s product read model (feature 075, Phase C). Both reads this
26
+ * replaced — the `up_sell` links and the target rows — were `em.find`
27
+ * against `catalog`'s tables, so a switched-off `catalog` still filled the
28
+ * cart's up-sell strip with products the platform had stopped serving.
29
+ */
30
+ private readonly catalogProducts;
31
+ constructor(emFactory: () => EntityManager,
32
+ /**
33
+ * `catalog`'s product read model (feature 075, Phase C). Both reads this
34
+ * replaced — the `up_sell` links and the target rows — were `em.find`
35
+ * against `catalog`'s tables, so a switched-off `catalog` still filled the
36
+ * cart's up-sell strip with products the platform had stopped serving.
37
+ */
38
+ catalogProducts: CatalogProductReadPort);
39
+ forCart(cartId: string, limit: number,
40
+ /**
41
+ * Who is looking at the strip (issue #227). Required: this method returns
42
+ * a product's name, slug and thumbnail for rows the buyer never asked for
43
+ * by id, which makes it a listing rather than a lookup, and a listing that
44
+ * defaulted its audience would silently show the wrong one.
45
+ */
46
+ audience: ProductAudience): Promise<CartUpsellCandidate[]>;
47
+ private thumbnailFor;
48
+ /**
49
+ * Picks the most useful locale from the multilingual `name` JSONB. The
50
+ * cart UI hands the buyer's session locale to the API; the resolver in
51
+ * front of this service is expected to use that. For now we fall
52
+ * through to the first available locale, matching the foundation's
53
+ * "best-effort" rendering pattern.
54
+ */
55
+ private localizedName;
56
+ }
57
+ //# sourceMappingURL=cart-upsell-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cart-upsell-service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/cart-upsell-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAEL,KAAK,sBAAsB,EAE3B,KAAK,eAAe,EACrB,MAAM,4BAA4B,CAAC;AAGpC;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,iBAAiB;IAE1B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,CAAC,eAAe;gBAPf,SAAS,EAAE,MAAM,aAAa;IAC/C;;;;;OAKG;IACc,eAAe,EAAE,sBAAsB;IAGpD,OAAO,CACX,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM;IACb;;;;;OAKG;IACH,QAAQ,EAAE,eAAe,GACxB,OAAO,CAAC,mBAAmB,EAAE,CAAC;IA4DjC,OAAO,CAAC,YAAY;IAUpB;;;;;;OAMG;IACH,OAAO,CAAC,aAAa;CAOtB"}
@@ -0,0 +1,100 @@
1
+ import { isProductVisibleTo, } from '@endora-commerce/contracts';
2
+ import { CartItem } from '../entities/cart-item.entity.js';
3
+ export class CartUpsellService {
4
+ emFactory;
5
+ catalogProducts;
6
+ constructor(emFactory,
7
+ /**
8
+ * `catalog`'s product read model (feature 075, Phase C). Both reads this
9
+ * replaced — the `up_sell` links and the target rows — were `em.find`
10
+ * against `catalog`'s tables, so a switched-off `catalog` still filled the
11
+ * cart's up-sell strip with products the platform had stopped serving.
12
+ */
13
+ catalogProducts) {
14
+ this.emFactory = emFactory;
15
+ this.catalogProducts = catalogProducts;
16
+ }
17
+ async forCart(cartId, limit,
18
+ /**
19
+ * Who is looking at the strip (issue #227). Required: this method returns
20
+ * a product's name, slug and thumbnail for rows the buyer never asked for
21
+ * by id, which makes it a listing rather than a lookup, and a listing that
22
+ * defaulted its audience would silently show the wrong one.
23
+ */
24
+ audience) {
25
+ if (limit <= 0)
26
+ return [];
27
+ const em = this.emFactory();
28
+ const items = await em.find(CartItem, { cartId });
29
+ if (items.length === 0)
30
+ return [];
31
+ const sourceProductIds = Array.from(new Set(items.map((it) => it.productId)));
32
+ // One query: every up_sell link emanating from any of the source
33
+ // products. The target set is deduplicated in app code so we can
34
+ // attach match-counts cheaply.
35
+ const links = await this.catalogProducts.listLinksBySourceIds(sourceProductIds, 'up_sell');
36
+ if (links.length === 0)
37
+ return [];
38
+ const matchCountByTarget = new Map();
39
+ for (const link of links) {
40
+ // Exclude products already in the cart.
41
+ if (sourceProductIds.includes(link.targetProductId))
42
+ continue;
43
+ matchCountByTarget.set(link.targetProductId, (matchCountByTarget.get(link.targetProductId) ?? 0) + 1);
44
+ }
45
+ if (matchCountByTarget.size === 0)
46
+ return [];
47
+ const targetIds = Array.from(matchCountByTarget.keys());
48
+ // Issue #227 — an up-sell target is a product the buyer never named, so
49
+ // the strip is a listing. A link from a product they may see to one they
50
+ // may not does not make the second one theirs to see.
51
+ const products = (await this.catalogProducts.findByIds(targetIds)).filter((p) => isProductVisibleTo(p, audience));
52
+ const byId = new Map(products.map((p) => [p.id, p]));
53
+ const candidates = targetIds
54
+ .map((id) => byId.get(id))
55
+ .filter((p) => Boolean(p))
56
+ .map((p) => ({
57
+ productId: p.id,
58
+ productName: this.localizedName(p),
59
+ productSlug: p.slug,
60
+ productThumbnailUrl: this.thumbnailFor(p),
61
+ matchCount: matchCountByTarget.get(p.id) ?? 0,
62
+ }));
63
+ candidates.sort((a, b) => {
64
+ if (b.matchCount !== a.matchCount)
65
+ return b.matchCount - a.matchCount;
66
+ // Tie-break by older `created_at` (the existing product wins over
67
+ // a newly-added one). Sorted post-mapping for clarity.
68
+ const aProduct = byId.get(a.productId);
69
+ const bProduct = byId.get(b.productId);
70
+ const aTime = aProduct?.createdAt?.getTime() ?? 0;
71
+ const bTime = bProduct?.createdAt?.getTime() ?? 0;
72
+ return aTime - bTime;
73
+ });
74
+ return candidates.slice(0, limit);
75
+ }
76
+ thumbnailFor(product) {
77
+ // The product record carries the `attributeValues` JSONB blob that
78
+ // foundation code reads for `primaryAssetUrl`. We mirror that read
79
+ // path; if missing, the caller's UI hides the thumbnail.
80
+ const url = product.attributeValues?.['primaryAssetUrl'];
81
+ return typeof url === 'string' && url.length > 0 ? url : null;
82
+ }
83
+ /**
84
+ * Picks the most useful locale from the multilingual `name` JSONB. The
85
+ * cart UI hands the buyer's session locale to the API; the resolver in
86
+ * front of this service is expected to use that. For now we fall
87
+ * through to the first available locale, matching the foundation's
88
+ * "best-effort" rendering pattern.
89
+ */
90
+ localizedName(product) {
91
+ if (!product.name || typeof product.name !== 'object')
92
+ return product.id;
93
+ const en = product.name['en-US'] ?? product.name['en'];
94
+ if (typeof en === 'string' && en.length > 0)
95
+ return en;
96
+ const first = Object.values(product.name).find((v) => typeof v === 'string' && v.length > 0);
97
+ return first ?? product.id;
98
+ }
99
+ }
100
+ //# sourceMappingURL=cart-upsell-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cart-upsell-service.js","sourceRoot":"","sources":["../../../src/backend/services/cart-upsell-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import type { CustomerAccountReadPort, OrganizationDetailsPort } from '@endora-commerce/contracts';
2
+ /**
3
+ * The effective customer group of a cart's owner (issue #177).
4
+ *
5
+ * The chain is the platform's one answer to "which group is this buyer in":
6
+ * the account's own group, else the Organization's, else none. The pricing
7
+ * engine spells it as `context.customerGroupId ?? organization.customerGroupId`
8
+ * and `pwa`'s push audience builder as `account.customerGroupId ?? orgGroup`;
9
+ * this is the same rule for the promotion engine, which had been receiving a
10
+ * hard-coded `null` on every cart path since the field was introduced.
11
+ *
12
+ * Both rows arrive over their owners' published ports, so a switched-off
13
+ * `customer_accounts` or `organizations` refuses here rather than answering
14
+ * "no group" — which reads as a legitimate answer and is not one: it silently
15
+ * withdraws every group-targeted discount from every buyer.
16
+ *
17
+ * An anonymous cart has neither an account nor an Organization and resolves to
18
+ * `null`, which is the correct answer rather than a degraded one.
19
+ */
20
+ export declare function resolveCartCustomerGroupId(ports: {
21
+ customerAccounts: CustomerAccountReadPort;
22
+ organizations: OrganizationDetailsPort;
23
+ }, owner: {
24
+ customerAccountId: string | null;
25
+ organizationId: string | null;
26
+ }): Promise<string | null>;
27
+ //# sourceMappingURL=customer-group-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"customer-group-resolver.d.ts","sourceRoot":"","sources":["../../../src/backend/services/customer-group-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAEnG;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,0BAA0B,CAC9C,KAAK,EAAE;IACL,gBAAgB,EAAE,uBAAuB,CAAC;IAC1C,aAAa,EAAE,uBAAuB,CAAC;CACxC,EACD,KAAK,EAAE;IAAE,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GACzE,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAUxB"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * The effective customer group of a cart's owner (issue #177).
3
+ *
4
+ * The chain is the platform's one answer to "which group is this buyer in":
5
+ * the account's own group, else the Organization's, else none. The pricing
6
+ * engine spells it as `context.customerGroupId ?? organization.customerGroupId`
7
+ * and `pwa`'s push audience builder as `account.customerGroupId ?? orgGroup`;
8
+ * this is the same rule for the promotion engine, which had been receiving a
9
+ * hard-coded `null` on every cart path since the field was introduced.
10
+ *
11
+ * Both rows arrive over their owners' published ports, so a switched-off
12
+ * `customer_accounts` or `organizations` refuses here rather than answering
13
+ * "no group" — which reads as a legitimate answer and is not one: it silently
14
+ * withdraws every group-targeted discount from every buyer.
15
+ *
16
+ * An anonymous cart has neither an account nor an Organization and resolves to
17
+ * `null`, which is the correct answer rather than a degraded one.
18
+ */
19
+ export async function resolveCartCustomerGroupId(ports, owner) {
20
+ if (owner.customerAccountId) {
21
+ const account = await ports.customerAccounts.findById(owner.customerAccountId);
22
+ if (account?.customerGroupId)
23
+ return account.customerGroupId;
24
+ }
25
+ if (owner.organizationId) {
26
+ const organization = await ports.organizations.findById(owner.organizationId);
27
+ if (organization?.customerGroupId)
28
+ return organization.customerGroupId;
29
+ }
30
+ return null;
31
+ }
32
+ //# sourceMappingURL=customer-group-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"customer-group-resolver.js","sourceRoot":"","sources":["../../../src/backend/services/customer-group-resolver.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,KAGC,EACD,KAA0E;IAE1E,IAAI,KAAK,CAAC,iBAAiB,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,gBAAgB,CAAC,QAAQ,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QAC/E,IAAI,OAAO,EAAE,eAAe;YAAE,OAAO,OAAO,CAAC,eAAe,CAAC;IAC/D,CAAC;IACD,IAAI,KAAK,CAAC,cAAc,EAAE,CAAC;QACzB,MAAM,YAAY,GAAG,MAAM,KAAK,CAAC,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;QAC9E,IAAI,YAAY,EAAE,eAAe;YAAE,OAAO,YAAY,CAAC,eAAe,CAAC;IACzE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,221 @@
1
+ import { type ModuleCliCommand } from '@endora-commerce/contracts';
2
+ import type { ModuleContext } from '@endora-commerce/platform/kernel';
3
+ /**
4
+ * Carts module — feature 027 consolidation pass.
5
+ *
6
+ * Settings (`carts.abandonment.*`) declared here are seeded by the module-
7
+ * lifecycle ManifestReconciler on backend boot. No raw INSERT in the
8
+ * migration; the reconciler is the single seed path (matches the
9
+ * `inventory.*` and `organizations.*` patterns).
10
+ */
11
+ export declare const CARTS_SETTING_CODES: {
12
+ readonly ABANDONMENT_INACTIVITY_MINUTES: "carts.abandonment.inactivity_minutes";
13
+ readonly ABANDONMENT_NOTIFICATION_RECIPIENT: "carts.abandonment.notification_recipient";
14
+ };
15
+ /**
16
+ * The manifest defaults, exported so a read that has to degrade degrades to
17
+ * *this* binding rather than to a literal invented at the call site (feature
18
+ * 072, D-43).
19
+ *
20
+ * The two used to diverge: the manifest said 10080 while both readers' `catch`
21
+ * answered `0`, and `CartAbandonmentWorker` treats `<= 0` as "sweep nothing".
22
+ * The divergence did not merely lose the configured value — it inverted the
23
+ * feature, and it would still have been off after the channel was fixed. A
24
+ * compiled-in fallback is a second source of truth consulted only when the
25
+ * first is unreachable, i.e. exactly when nobody is watching.
26
+ */
27
+ export declare const DEFAULT_ABANDONMENT_INACTIVITY_MINUTES = 10080;
28
+ export declare const DEFAULT_ABANDONMENT_NOTIFICATION_RECIPIENT = "";
29
+ export declare const manifest: {
30
+ id: string;
31
+ name: string;
32
+ version: string;
33
+ dependencies: string[];
34
+ description?: string | undefined;
35
+ acknowledgedDependencies?: {
36
+ moduleId: string;
37
+ port: string;
38
+ reason: string;
39
+ }[] | undefined;
40
+ nonBindingDependencies?: {
41
+ moduleId: string;
42
+ name: string;
43
+ kind: "contributes-to" | "degrades-without" | "refuses-without";
44
+ reason: string;
45
+ whenAbsent?: string | undefined;
46
+ }[] | undefined;
47
+ activation?: {
48
+ settingCode: string;
49
+ default: boolean;
50
+ } | {
51
+ nonDeactivatable: true;
52
+ reason: string;
53
+ } | undefined;
54
+ settings?: {
55
+ moduleCode: string;
56
+ groups: {
57
+ code: string;
58
+ name: string;
59
+ salesChannelCodes?: string[] | undefined;
60
+ isSystemProtected?: boolean | undefined;
61
+ }[];
62
+ settings: {
63
+ code: string;
64
+ name: string;
65
+ valueType: "string" | "number" | "boolean" | "json" | "string_list" | "secret" | "credential_ref";
66
+ defaultValue: unknown;
67
+ description?: string | undefined;
68
+ groupCode?: string | undefined;
69
+ previousDefaultValues?: unknown[] | undefined;
70
+ salesChannelCodes?: string[] | undefined;
71
+ enumOptions?: string[] | undefined;
72
+ configurationType?: string | undefined;
73
+ hidden?: boolean | undefined;
74
+ }[];
75
+ } | undefined;
76
+ i18n?: {
77
+ bundlesDir: string;
78
+ } | undefined;
79
+ docs?: false | {
80
+ dir: string;
81
+ } | undefined;
82
+ demo?: false | {
83
+ summary: string;
84
+ seed: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoSeedResult>;
85
+ reset: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoResetResult>;
86
+ after?: readonly string[] | undefined;
87
+ package?: string | undefined;
88
+ } | undefined;
89
+ actions?: {
90
+ id: string;
91
+ labelKey: string;
92
+ icon: "Plus" | "Sparkles" | "Settings" | "Search" | "Boxes" | "Layers" | "Menu" | "PlusCircle" | "PlusSquare" | "FilePlus" | "FolderPlus" | "Upload" | "FileUp" | "CloudUpload" | "Download" | "FileDown" | "FileText" | "BookOpen" | "Rss" | "Package" | "Tag" | "ShoppingCart" | "Receipt" | "CreditCard" | "Users" | "UserPlus" | "Inbox" | "ListChecks" | "ClipboardList" | "Image" | "Video" | "LayoutDashboard" | "PanelLeft" | "KeyRound" | "ShieldCheck" | "Edit" | "Archive" | "Box" | "Truck" | "CircleDollarSign" | "Activity" | "LineChart" | "Smartphone" | "Webhook" | "Scale" | "PlugZap" | "PercentDiamond" | "Newspaper" | "Languages" | "Eraser" | "Warehouse" | "TrendingDown" | "Bell" | "PackageOpen" | "Building2" | "Store" | "ClipboardCheck";
93
+ targetRoute: string;
94
+ keywords: string[];
95
+ weight: number;
96
+ descriptionKey?: string | undefined;
97
+ requiredPermission?: string | undefined;
98
+ }[] | undefined;
99
+ permissions?: {
100
+ code: string;
101
+ label: string;
102
+ module?: string | undefined;
103
+ description?: string | undefined;
104
+ requires?: string[] | undefined;
105
+ }[] | undefined;
106
+ transactionalEmails?: {
107
+ code: string;
108
+ name: string;
109
+ variables: {
110
+ key: string;
111
+ label: string;
112
+ sampleValue?: string | undefined;
113
+ description?: string | undefined;
114
+ }[];
115
+ description?: string | undefined;
116
+ group?: string | undefined;
117
+ }[] | undefined;
118
+ capabilities?: string[] | undefined;
119
+ exclusiveCapabilities?: {
120
+ key: string;
121
+ errorCode: string;
122
+ }[] | undefined;
123
+ errorCodes?: {
124
+ code: string;
125
+ tokens?: string[] | undefined;
126
+ }[] | undefined;
127
+ blocks?: {
128
+ name: string;
129
+ labelKey: string;
130
+ category: string;
131
+ contexts: ("email" | "invoice" | "cms" | "newsletter")[];
132
+ fields: Record<string, {
133
+ type: "number" | "object" | "array" | "uuid" | "text" | "select" | "textarea" | "radio" | "external" | "richtext";
134
+ label?: string | undefined;
135
+ required?: boolean | undefined;
136
+ options?: {
137
+ label: string;
138
+ value: string | number;
139
+ }[] | undefined;
140
+ refKind?: string | undefined;
141
+ }>;
142
+ descriptionKey?: string | undefined;
143
+ defaultProps?: Record<string, unknown> | undefined;
144
+ responsiveFields?: string[] | undefined;
145
+ previewIcon?: string | undefined;
146
+ weight?: number | undefined;
147
+ }[] | undefined;
148
+ blockCategories?: {
149
+ key: string;
150
+ titleKey: string;
151
+ contexts: ("email" | "invoice" | "cms" | "newsletter")[];
152
+ weight?: number | undefined;
153
+ visible?: boolean | undefined;
154
+ }[] | undefined;
155
+ env?: {
156
+ name: string;
157
+ describes: {
158
+ en: string;
159
+ pl: string;
160
+ };
161
+ requirement: {
162
+ kind: "required";
163
+ } | {
164
+ kind: "requiredWhen";
165
+ input: string;
166
+ equals: string;
167
+ } | {
168
+ kind: "optional";
169
+ without: {
170
+ en: string;
171
+ pl: string;
172
+ };
173
+ };
174
+ secret: boolean;
175
+ generable: boolean;
176
+ owner: {
177
+ kind: "platform";
178
+ } | {
179
+ kind: "application";
180
+ application: "admin" | "backend" | "storefront";
181
+ } | {
182
+ kind: "module";
183
+ moduleId: string;
184
+ };
185
+ consumers: ("admin" | "backend" | "storefront")[];
186
+ addressOf: "admin" | "backend" | "storefront" | null;
187
+ }[] | undefined;
188
+ };
189
+ /** Legacy export retained for backward compatibility. */
190
+ export declare const cartsManifest: {
191
+ moduleCode: string;
192
+ groups: {
193
+ code: string;
194
+ name: string;
195
+ salesChannelCodes?: string[] | undefined;
196
+ isSystemProtected?: boolean | undefined;
197
+ }[];
198
+ settings: {
199
+ code: string;
200
+ name: string;
201
+ valueType: "string" | "number" | "boolean" | "json" | "string_list" | "secret" | "credential_ref";
202
+ defaultValue: unknown;
203
+ description?: string | undefined;
204
+ groupCode?: string | undefined;
205
+ previousDefaultValues?: unknown[] | undefined;
206
+ salesChannelCodes?: string[] | undefined;
207
+ enumOptions?: string[] | undefined;
208
+ configurationType?: string | undefined;
209
+ hidden?: boolean | undefined;
210
+ }[];
211
+ };
212
+ /**
213
+ * The operator command this module declares — feature 080, T042b / D-160.9.
214
+ *
215
+ * It was `scripts/abandonment-sweep.ts`, the tree's one call site of
216
+ * `requireModuleEnabled` and the worked example D-157 was written around. The
217
+ * host asks that question now, at the declaration seam, for every command; see
218
+ * `cli/abandonment-sweep.ts` for what the move changed and what it did not.
219
+ */
220
+ export declare const cliCommands: ReadonlyArray<ModuleCliCommand<ModuleContext>>;
221
+ //# sourceMappingURL=manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAEtE;;;;;;;GAOG;AAEH,eAAO,MAAM,mBAAmB;;;CAGtB,CAAC;AAEX;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sCAAsC,QAAQ,CAAC;AAC5D,eAAO,MAAM,0CAA0C,KAAK,CAAC;AA2B7D,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+JnB,CAAC;AAEH,yDAAyD;AACzD,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;CAAW,CAAC;AAEtC;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,EAAE,aAAa,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAOtE,CAAC"}