@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,144 @@
1
+ import { Cart } from '../entities/cart.entity.js';
2
+ import { CartItem } from '../entities/cart-item.entity.js';
3
+ /**
4
+ * The row-level read model `carts` publishes, and the seed operation that goes
5
+ * with it (feature 075, Phase P).
6
+ *
7
+ * Six inbound sites are the same pair of queries — the customer's active cart,
8
+ * then its items — written out in `orders`' checkout, `orders`' reorder and
9
+ * `quote_requests`' quote-to-cart conversion. Two of those three then
10
+ * `em.create(Cart, …)` when the lookup misses and hand-build `CartItem` rows,
11
+ * which is two modules writing this module's two tables.
12
+ *
13
+ * `replaceItemsForCustomer` is what they call instead. The clear-then-seed
14
+ * rule it implements is the single-active-cart model admin order-create,
15
+ * reorder and RFQ conversion all already assume; it was spelled out twice, in
16
+ * two modules, and `lastActivityAt` was maintained in neither.
17
+ */
18
+ export class CartReadService {
19
+ emFactory;
20
+ constructor(emFactory) {
21
+ this.emFactory = emFactory;
22
+ }
23
+ async findActiveForCustomer(ctx) {
24
+ const em = this.emFactory();
25
+ const cart = await em.findOne(Cart, {
26
+ customerAccountId: ctx.customerAccountId,
27
+ // The organisation is always present for a signed-in customer since
28
+ // D-178, so the empty arm is unreachable rather than a supported mode.
29
+ // It stays because the context type still admits a null.
30
+ ...(ctx.organizationId === null ? {} : { organizationId: ctx.organizationId }),
31
+ status: 'active',
32
+ });
33
+ if (!cart)
34
+ return null;
35
+ return { cart: toCartRecord(cart), items: await this.#items(em, cart.id) };
36
+ }
37
+ async findById(cartId) {
38
+ const em = this.emFactory();
39
+ const cart = await em.findOne(Cart, { id: cartId });
40
+ if (!cart)
41
+ return null;
42
+ return { cart: toCartRecord(cart), items: await this.#items(em, cart.id) };
43
+ }
44
+ async #items(em, cartId) {
45
+ const items = await em.find(CartItem, { cartId }, { orderBy: { createdAt: 'asc', id: 'asc' } });
46
+ return items.map(toCartItemRecord);
47
+ }
48
+ }
49
+ /**
50
+ * The write port, adapting `CartService` and adding the seed operation.
51
+ *
52
+ * The service arrives as a getter so the adapter resolves it from the
53
+ * container per call rather than capturing it — it is this module's own gated
54
+ * port, and a singleton may not hold a gate.
55
+ */
56
+ export function createCartWritePort(emFactory, getService) {
57
+ const read = new CartReadService(emFactory);
58
+ return {
59
+ async getOrCreateForCustomer(ctx) {
60
+ return toCartRecord(await getService().getOrCreateForCustomer(ctx));
61
+ },
62
+ async addItem(actor, input) {
63
+ const result = await getService().addItem(actor, input);
64
+ return { cart: toCartRecord(result.cart), items: result.items.map(toCartItemRecord) };
65
+ },
66
+ clearForCustomer: (ctx) => getService().clearForCustomer(ctx),
67
+ async replaceItemsForCustomer(ctx, lines) {
68
+ // command-coverage-ignore: seeds the customer's own active cart from an
69
+ // already-audited source (a reorder, an accepted quote). The cart is not
70
+ // an auditable domain object — the order or quote that produced these
71
+ // lines is, and its own Command has already recorded the decision.
72
+ const em = emFactory();
73
+ const cart = await getService().getOrCreateForCustomer(ctx);
74
+ const existing = await em.find(CartItem, { cartId: cart.id });
75
+ if (existing.length > 0)
76
+ em.remove(existing);
77
+ for (const line of lines) {
78
+ em.create(CartItem, {
79
+ cartId: cart.id,
80
+ productId: line.productId,
81
+ ...(line.variantId ? { variantId: line.variantId } : {}),
82
+ quantity: line.quantity,
83
+ unitPrice: line.unitPrice,
84
+ currency: line.currency,
85
+ });
86
+ }
87
+ cart.lastActivityAt = new Date();
88
+ await em.flush();
89
+ const seeded = await read.findById(cart.id);
90
+ // The cart was created or loaded one statement ago in this same
91
+ // `EntityManager`, so a miss here is not a race — it is impossible, and
92
+ // an empty answer would be worse than a throw because the caller is
93
+ // about to place an order from it.
94
+ if (!seeded) {
95
+ throw new Error(`[carts] cart ${cart.id} vanished between seeding and reading it back.`);
96
+ }
97
+ return seeded;
98
+ },
99
+ };
100
+ }
101
+ export function toCartRecord(cart) {
102
+ return {
103
+ id: cart.id,
104
+ customerAccountId: cart.customerAccountId ?? null,
105
+ organizationId: cart.organizationId ?? null,
106
+ anonymousCartToken: cart.anonymousCartToken ?? null,
107
+ salesChannelId: cart.salesChannelId ?? null,
108
+ status: cart.status,
109
+ approvalStatus: cart.approvalStatus,
110
+ submittedForApprovalAt: cart.submittedForApprovalAt ?? null,
111
+ approvedAt: cart.approvedAt ?? null,
112
+ approvedByCustomerAccountId: cart.approvedByCustomerAccountId ?? null,
113
+ rejectedAt: cart.rejectedAt ?? null,
114
+ rejectedByActor: cart.rejectedByActor ?? null,
115
+ rejectedReason: cart.rejectedReason ?? null,
116
+ appliedPromotionCode: cart.appliedPromotionCode ?? null,
117
+ convertedToQuoteRequestId: cart.convertedToQuoteRequestId ?? null,
118
+ abandonmentNotifiedAt: cart.abandonmentNotifiedAt ?? null,
119
+ lastActivityAt: cart.lastActivityAt,
120
+ version: cart.version,
121
+ createdAt: cart.createdAt,
122
+ updatedAt: cart.updatedAt,
123
+ };
124
+ }
125
+ export function toCartItemRecord(item) {
126
+ return {
127
+ id: item.id,
128
+ cartId: item.cartId,
129
+ productId: item.productId,
130
+ variantId: item.variantId ?? null,
131
+ quantity: item.quantity,
132
+ unitPrice: item.unitPrice,
133
+ currency: item.currency,
134
+ recomputedUnitPrice: item.recomputedUnitPrice ?? null,
135
+ recomputedAt: item.recomputedAt ?? null,
136
+ recomputedCurrency: item.recomputedCurrency ?? null,
137
+ packagingUnitId: item.packagingUnitId ?? null,
138
+ packagingUnitName: item.packagingUnitName ?? null,
139
+ packagingUnitBaseQuantity: item.packagingUnitBaseQuantity ?? null,
140
+ createdAt: item.createdAt,
141
+ updatedAt: item.updatedAt,
142
+ };
143
+ }
144
+ //# sourceMappingURL=cart-read-port.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cart-read-port.js","sourceRoot":"","sources":["../../../src/backend/services/cart-read-port.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ import type { Redis } from 'ioredis';
2
+ /**
3
+ * Redis-backed per-cart pricing-recompute cache (feature 027 §R5).
4
+ *
5
+ * Stores the most recently re-resolved line prices keyed by `cartId` so
6
+ * the mini-cart → full-cart → checkout-entry walk does not re-run the
7
+ * `PricingService.resolveLinePrice` resolver three times within seconds.
8
+ *
9
+ * Cache TTL is short (30 s by default) so a price-list edit propagates to
10
+ * the buyer's next page-open with predictable latency. Every cart-side
11
+ * write must call `invalidate(cartId)` so the next read sees fresh
12
+ * resolver output.
13
+ *
14
+ * The cache is **optional** — when Redis is unavailable the cache may be
15
+ * disabled (set `enabled: false` in the options) and the recompute helper
16
+ * still works (just resolves on every read).
17
+ */
18
+ export declare const CART_RECOMPUTE_CACHE_KEY_PREFIX = "b2b:cart:recompute:";
19
+ export declare const CART_RECOMPUTE_CACHE_DEFAULT_TTL_SECONDS = 30;
20
+ export interface CartRecomputeCacheOptions {
21
+ ttlSeconds?: number;
22
+ enabled?: boolean;
23
+ }
24
+ export interface CachedCartLinePrice {
25
+ cartItemId: string;
26
+ /** `amount: null` = resolver failed; the caller should fall back to the snapshot. */
27
+ unitPrice: {
28
+ amount: number | null;
29
+ currency: string;
30
+ };
31
+ resolvedAt: string;
32
+ }
33
+ export interface CachedCartRecompute {
34
+ cartId: string;
35
+ lines: CachedCartLinePrice[];
36
+ /** Wall-clock when the resolver was last asked. ISO-8601. */
37
+ resolvedAt: string;
38
+ }
39
+ export declare class CartRecomputeCache {
40
+ private readonly redis;
41
+ private readonly ttlSeconds;
42
+ private readonly enabled;
43
+ constructor(redis: Redis, options?: CartRecomputeCacheOptions);
44
+ static keyFor(cartId: string): string;
45
+ get(cartId: string): Promise<CachedCartRecompute | null>;
46
+ put(cartId: string, payload: CachedCartRecompute): Promise<void>;
47
+ invalidate(cartId: string): Promise<void>;
48
+ }
49
+ //# sourceMappingURL=cart-recompute-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cart-recompute-cache.d.ts","sourceRoot":"","sources":["../../../src/backend/services/cart-recompute-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC;;;;;;;;;;;;;;;GAeG;AAEH,eAAO,MAAM,+BAA+B,wBAAwB,CAAC;AACrE,eAAO,MAAM,wCAAwC,KAAK,CAAC;AAE3D,MAAM,WAAW,yBAAyB;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,qFAAqF;IACrF,SAAS,EAAE;QAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IACvD,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,mBAAmB,EAAE,CAAC;IAC7B,6DAA6D;IAC7D,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,kBAAkB;IAIjB,OAAO,CAAC,QAAQ,CAAC,KAAK;IAHlC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;gBAEL,KAAK,EAAE,KAAK,EAAE,OAAO,GAAE,yBAA8B;IAKlF,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAI/B,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC;IAYxD,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAUhE,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAIhD"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Redis-backed per-cart pricing-recompute cache (feature 027 §R5).
3
+ *
4
+ * Stores the most recently re-resolved line prices keyed by `cartId` so
5
+ * the mini-cart → full-cart → checkout-entry walk does not re-run the
6
+ * `PricingService.resolveLinePrice` resolver three times within seconds.
7
+ *
8
+ * Cache TTL is short (30 s by default) so a price-list edit propagates to
9
+ * the buyer's next page-open with predictable latency. Every cart-side
10
+ * write must call `invalidate(cartId)` so the next read sees fresh
11
+ * resolver output.
12
+ *
13
+ * The cache is **optional** — when Redis is unavailable the cache may be
14
+ * disabled (set `enabled: false` in the options) and the recompute helper
15
+ * still works (just resolves on every read).
16
+ */
17
+ export const CART_RECOMPUTE_CACHE_KEY_PREFIX = 'b2b:cart:recompute:';
18
+ export const CART_RECOMPUTE_CACHE_DEFAULT_TTL_SECONDS = 30;
19
+ export class CartRecomputeCache {
20
+ redis;
21
+ ttlSeconds;
22
+ enabled;
23
+ constructor(redis, options = {}) {
24
+ this.redis = redis;
25
+ this.ttlSeconds = options.ttlSeconds ?? CART_RECOMPUTE_CACHE_DEFAULT_TTL_SECONDS;
26
+ this.enabled = (options.enabled ?? true) && this.ttlSeconds > 0;
27
+ }
28
+ static keyFor(cartId) {
29
+ return `${CART_RECOMPUTE_CACHE_KEY_PREFIX}${cartId}`;
30
+ }
31
+ async get(cartId) {
32
+ if (!this.enabled)
33
+ return null;
34
+ const raw = await this.redis.get(CartRecomputeCache.keyFor(cartId));
35
+ if (raw === null)
36
+ return null;
37
+ try {
38
+ return JSON.parse(raw);
39
+ }
40
+ catch {
41
+ await this.redis.del(CartRecomputeCache.keyFor(cartId));
42
+ return null;
43
+ }
44
+ }
45
+ async put(cartId, payload) {
46
+ if (!this.enabled)
47
+ return;
48
+ await this.redis.set(CartRecomputeCache.keyFor(cartId), JSON.stringify(payload), 'EX', this.ttlSeconds);
49
+ }
50
+ async invalidate(cartId) {
51
+ if (!this.enabled)
52
+ return;
53
+ await this.redis.del(CartRecomputeCache.keyFor(cartId));
54
+ }
55
+ }
56
+ //# sourceMappingURL=cart-recompute-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cart-recompute-cache.js","sourceRoot":"","sources":["../../../src/backend/services/cart-recompute-cache.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,CAAC,MAAM,+BAA+B,GAAG,qBAAqB,CAAC;AACrE,MAAM,CAAC,MAAM,wCAAwC,GAAG,EAAE,CAAC;AAqB3D,MAAM,OAAO,kBAAkB;IAIA;IAHZ,UAAU,CAAS;IACnB,OAAO,CAAU;IAElC,YAA6B,KAAY,EAAE,UAAqC,EAAE;QAArD,UAAK,GAAL,KAAK,CAAO;QACvC,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,wCAAwC,CAAC;QACjF,IAAI,CAAC,OAAO,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;IAClE,CAAC;IAED,MAAM,CAAC,MAAM,CAAC,MAAc;QAC1B,OAAO,GAAG,+BAA+B,GAAG,MAAM,EAAE,CAAC;IACvD,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,MAAc;QACtB,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC;QAC/B,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,kBAAkB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;QACpE,IAAI,GAAG,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QAC9B,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAwB,CAAC;QAChD,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,kBAAkB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;YACxD,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,MAAc,EAAE,OAA4B;QACpD,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAC1B,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAClB,kBAAkB,CAAC,MAAM,CAAC,MAAM,CAAC,EACjC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EACvB,IAAI,EACJ,IAAI,CAAC,UAAU,CAChB,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,MAAc;QAC7B,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAC1B,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,kBAAkB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1D,CAAC;CACF"}
@@ -0,0 +1,165 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql';
2
+ import { type CartMergeOutcome } from '@endora-commerce/contracts';
3
+ import { Cart } from '../entities/cart.entity.js';
4
+ import { CartItem } from '../entities/cart-item.entity.js';
5
+ import type { CatalogProductReadPort, LinePricePort, OrganizationDetailsPort } from '@endora-commerce/contracts';
6
+ import type { SalesChannelMembershipPort } from '@endora-commerce/platform/kernel';
7
+ import type { CartApprovalService } from './cart-approval-service.js';
8
+ import type { CartAuditService } from './cart-audit-service.js';
9
+ import type { CartRecomputeCache } from './cart-recompute-cache.js';
10
+ /**
11
+ * CartService (T125).
12
+ *
13
+ * Two cart shapes:
14
+ * - Anonymous: identified by a caller-held cookie token. No customer or
15
+ * organization attached.
16
+ * - Authenticated: the customer's single active cart.
17
+ *
18
+ * At login, the existing anonymous cart (if any) is merged into the
19
+ * authenticated cart — duplicate lines have their quantities summed.
20
+ */
21
+ export interface AnonymousContext {
22
+ anonymousCartToken: string;
23
+ }
24
+ export interface CustomerContext {
25
+ customerAccountId: string;
26
+ /**
27
+ * Feature 026 US2 — null for guest-style Customer accounts that have no
28
+ * Organization. Carts already persist `organization_id` nullable, so the
29
+ * service handles either case (no-org carts fall back to platform-default
30
+ * prices via PricingService).
31
+ */
32
+ organizationId: string | null;
33
+ }
34
+ /**
35
+ * Cart-line hard cap (feature 027 FR-001 / spec edge case "Cart contents
36
+ * cap"). The 201st distinct line is refused with HTTP 422.
37
+ */
38
+ export declare const CART_MAX_LINES = 200;
39
+ export declare class CartService {
40
+ #private;
41
+ private readonly emFactory;
42
+ /** `price_lists`' line-resolution slice (feature 075, Phase C). */
43
+ private readonly pricingService;
44
+ /** `catalog`'s product read model — products, variants, packaging units. */
45
+ private readonly catalogProducts;
46
+ /** `organizations`' read model — the buying org the price resolves against. */
47
+ private readonly organizations;
48
+ /**
49
+ * The sanctioned bridge accessor (Constitution XII), for the assortment
50
+ * gate `addItem` owes (issue #259). Required, like `catalogProducts`: an
51
+ * optional one would make "this rig did not wire it" and "this channel
52
+ * publishes the product" the same answer, which is the fail-open shape the
53
+ * whole issue is about.
54
+ */
55
+ private readonly channelMembership;
56
+ private readonly approvalService?;
57
+ private readonly auditService?;
58
+ private readonly recomputeCache?;
59
+ /**
60
+ * `pricingService` is **required** since issue #124. It used to be optional
61
+ * "for test rigs", and the cart then read the legacy
62
+ * `attributeValues['defaultPrice']` whenever it was missing — so a rig, a
63
+ * wiring slip and `price_lists` switched off all produced the same thing: a
64
+ * cart line priced from a catalogue attribute that no price list supports.
65
+ * Absence is now a `lazyPort` gate that throws `MODULE_DISABLED` at the call,
66
+ * which nothing downstream can mistake for a price.
67
+ *
68
+ * Cart entities don't currently track which sales channel they were
69
+ * created on (multi-channel cart attribution is a separate
70
+ * follow-up), so the resolver runs against the system-default
71
+ * channel — matching the behaviour the foundation cart already had.
72
+ * Lists with a `salesChannel` criterion targeting other channels
73
+ * therefore won't apply on the cart line until cart-side channel
74
+ * tracking lands.
75
+ */
76
+ /**
77
+ * `approvalService` and `auditService` are optional — when wired
78
+ * (production composition), any buyer-driven mutation on an
79
+ * `approved` cart re-arms approval to `pending` and a typed audit
80
+ * row is landed (line_added / line_qty_changed / line_removed).
81
+ * Foundation tests and legacy test rigs that don't wire either still
82
+ * construct the service without them; both hooks no-op when absent.
83
+ */
84
+ constructor(emFactory: () => EntityManager,
85
+ /** `price_lists`' line-resolution slice (feature 075, Phase C). */
86
+ pricingService: LinePricePort,
87
+ /** `catalog`'s product read model — products, variants, packaging units. */
88
+ catalogProducts: CatalogProductReadPort,
89
+ /** `organizations`' read model — the buying org the price resolves against. */
90
+ organizations: OrganizationDetailsPort,
91
+ /**
92
+ * The sanctioned bridge accessor (Constitution XII), for the assortment
93
+ * gate `addItem` owes (issue #259). Required, like `catalogProducts`: an
94
+ * optional one would make "this rig did not wire it" and "this channel
95
+ * publishes the product" the same answer, which is the fail-open shape the
96
+ * whole issue is about.
97
+ */
98
+ channelMembership: SalesChannelMembershipPort, approvalService?: CartApprovalService | undefined, auditService?: CartAuditService | undefined, recomputeCache?: CartRecomputeCache | undefined);
99
+ getOrCreateForCustomer(ctx: CustomerContext): Promise<Cart>;
100
+ getOrCreateForAnon(em: EntityManager, token: string): Promise<Cart>;
101
+ getItems(cartId: string): Promise<CartItem[]>;
102
+ addItem(actor: {
103
+ customer?: CustomerContext;
104
+ anonymousToken?: string;
105
+ }, input: {
106
+ productId: string;
107
+ variantId?: string;
108
+ quantity: number;
109
+ packagingUnitId?: string;
110
+ }): Promise<{
111
+ cart: Cart;
112
+ items: CartItem[];
113
+ }>;
114
+ updateItem(actor: {
115
+ customer?: CustomerContext;
116
+ anonymousToken?: string;
117
+ }, itemId: string, quantity: number): Promise<{
118
+ cart: Cart;
119
+ items: CartItem[];
120
+ }>;
121
+ removeItem(actor: {
122
+ customer?: CustomerContext;
123
+ anonymousToken?: string;
124
+ }, itemId: string): Promise<{
125
+ cart: Cart;
126
+ items: CartItem[];
127
+ }>;
128
+ /**
129
+ * Explicit "this buyer just opened the cart page" ping (feature 027 §R4 /
130
+ * FR-032). Bumps `last_activity_at` and, if the cart had transitioned to
131
+ * `abandoned`, brings it back to `active` and clears
132
+ * `abandonment_notified_at` so the next abandonment cycle can notify
133
+ * again. No-op on a missing cart.
134
+ */
135
+ touch(actor: {
136
+ customer?: CustomerContext;
137
+ anonymousToken?: string;
138
+ }): Promise<Cart | null>;
139
+ /**
140
+ * Merge an anonymous cart's items into the customer's cart at login
141
+ * time (feature 037-cart-merge-on-login).
142
+ *
143
+ * Returns a `CartMergeOutcome` diagnostic the caller (the login route
144
+ * via the `onLogin` hook) uses to surface the result to the storefront
145
+ * and to decide whether to clear the stale `b2b_cart_anon` cookie.
146
+ * See specs/037-cart-merge-on-login/contracts/cart-merge.md § 3.
147
+ *
148
+ * State matrix:
149
+ * - `adopted` — the customer had no active cart; the anon
150
+ * cart's lines were placed onto a freshly-created destination.
151
+ * - `merged` — the customer already had an active cart and
152
+ * ≥ 1 anon line was moved or summed into it.
153
+ * - `noop_empty` — the anon cart was found but carried no items;
154
+ * the source row is still flipped to `completed` + detokenised
155
+ * (cleanup), no audit row is written.
156
+ * - `noop_no_anon` — no cart for the supplied token; no DB change.
157
+ *
158
+ * Coupons on the source cart are NOT carried to the destination
159
+ * (R-05 / FR-010 — the destination's existing `appliedPromotionCode`
160
+ * is preserved verbatim).
161
+ */
162
+ mergeAnonymousIntoCustomer(anonymousToken: string, ctx: CustomerContext): Promise<CartMergeOutcome>;
163
+ clearForCustomer(ctx: CustomerContext): Promise<void>;
164
+ }
165
+ //# sourceMappingURL=cart-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cart-service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/cart-service.ts"],"names":[],"mappings":"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"}