@cogenta/commerce 0.2.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 (98) hide show
  1. package/LICENSE +373 -0
  2. package/dist/admin/permissions.d.ts +57 -0
  3. package/dist/admin/permissions.d.ts.map +1 -0
  4. package/dist/admin/permissions.js +87 -0
  5. package/dist/admin/permissions.js.map +1 -0
  6. package/dist/admin/router.d.ts +53 -0
  7. package/dist/admin/router.d.ts.map +1 -0
  8. package/dist/admin/router.js +476 -0
  9. package/dist/admin/router.js.map +1 -0
  10. package/dist/cart/store.d.ts +91 -0
  11. package/dist/cart/store.d.ts.map +1 -0
  12. package/dist/cart/store.js +342 -0
  13. package/dist/cart/store.js.map +1 -0
  14. package/dist/cart/totals.d.ts +73 -0
  15. package/dist/cart/totals.d.ts.map +1 -0
  16. package/dist/cart/totals.js +65 -0
  17. package/dist/cart/totals.js.map +1 -0
  18. package/dist/catalog/store.d.ts +53 -0
  19. package/dist/catalog/store.d.ts.map +1 -0
  20. package/dist/catalog/store.js +358 -0
  21. package/dist/catalog/store.js.map +1 -0
  22. package/dist/catalog/types.d.ts +95 -0
  23. package/dist/catalog/types.d.ts.map +1 -0
  24. package/dist/catalog/types.js +5 -0
  25. package/dist/catalog/types.js.map +1 -0
  26. package/dist/coupon/store.d.ts +103 -0
  27. package/dist/coupon/store.d.ts.map +1 -0
  28. package/dist/coupon/store.js +181 -0
  29. package/dist/coupon/store.js.map +1 -0
  30. package/dist/customer/store.d.ts +41 -0
  31. package/dist/customer/store.d.ts.map +1 -0
  32. package/dist/customer/store.js +103 -0
  33. package/dist/customer/store.js.map +1 -0
  34. package/dist/index.d.ts +42 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +23 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/invoice/pdf.d.ts +67 -0
  39. package/dist/invoice/pdf.d.ts.map +1 -0
  40. package/dist/invoice/pdf.js +449 -0
  41. package/dist/invoice/pdf.js.map +1 -0
  42. package/dist/invoice/store.d.ts +96 -0
  43. package/dist/invoice/store.d.ts.map +1 -0
  44. package/dist/invoice/store.js +221 -0
  45. package/dist/invoice/store.js.map +1 -0
  46. package/dist/money.d.ts +55 -0
  47. package/dist/money.d.ts.map +1 -0
  48. package/dist/money.js +146 -0
  49. package/dist/money.js.map +1 -0
  50. package/dist/order/store.d.ts +75 -0
  51. package/dist/order/store.d.ts.map +1 -0
  52. package/dist/order/store.js +301 -0
  53. package/dist/order/store.js.map +1 -0
  54. package/dist/order/types.d.ts +67 -0
  55. package/dist/order/types.d.ts.map +1 -0
  56. package/dist/order/types.js +71 -0
  57. package/dist/order/types.js.map +1 -0
  58. package/dist/payment/manual.d.ts +27 -0
  59. package/dist/payment/manual.d.ts.map +1 -0
  60. package/dist/payment/manual.js +105 -0
  61. package/dist/payment/manual.js.map +1 -0
  62. package/dist/payment/registry.d.ts +23 -0
  63. package/dist/payment/registry.d.ts.map +1 -0
  64. package/dist/payment/registry.js +30 -0
  65. package/dist/payment/registry.js.map +1 -0
  66. package/dist/payment/store.d.ts +38 -0
  67. package/dist/payment/store.d.ts.map +1 -0
  68. package/dist/payment/store.js +253 -0
  69. package/dist/payment/store.js.map +1 -0
  70. package/dist/payment/stripe.d.ts +10 -0
  71. package/dist/payment/stripe.d.ts.map +1 -0
  72. package/dist/payment/stripe.js +419 -0
  73. package/dist/payment/stripe.js.map +1 -0
  74. package/dist/payment/types.d.ts +124 -0
  75. package/dist/payment/types.d.ts.map +1 -0
  76. package/dist/payment/types.js +18 -0
  77. package/dist/payment/types.js.map +1 -0
  78. package/dist/rows.d.ts +26 -0
  79. package/dist/rows.d.ts.map +1 -0
  80. package/dist/rows.js +87 -0
  81. package/dist/rows.js.map +1 -0
  82. package/dist/shipping/store.d.ts +88 -0
  83. package/dist/shipping/store.d.ts.map +1 -0
  84. package/dist/shipping/store.js +165 -0
  85. package/dist/shipping/store.js.map +1 -0
  86. package/dist/subscription/store.d.ts +106 -0
  87. package/dist/subscription/store.d.ts.map +1 -0
  88. package/dist/subscription/store.js +310 -0
  89. package/dist/subscription/store.js.map +1 -0
  90. package/dist/tables.d.ts +41 -0
  91. package/dist/tables.d.ts.map +1 -0
  92. package/dist/tables.js +383 -0
  93. package/dist/tables.js.map +1 -0
  94. package/dist/tax/store.d.ts +68 -0
  95. package/dist/tax/store.d.ts.map +1 -0
  96. package/dist/tax/store.js +126 -0
  97. package/dist/tax/store.js.map +1 -0
  98. package/package.json +42 -0
@@ -0,0 +1,342 @@
1
+ import { CogentaError, identifier, newId, sql } from '@cogenta/core';
2
+ import { assertCurrency } from '../money.js';
3
+ import { toInt, toNullableText, toText } from '../rows.js';
4
+ import { TABLES } from '../tables.js';
5
+ import { computeTotals } from './totals.js';
6
+ export const CART_STATUSES = ['open', 'ordered', 'abandoned'];
7
+ /** Thirty days. Long enough to be a real "saved basket", short enough that the
8
+ * table does not become a graveyard nobody ever prunes. */
9
+ export const CART_TTL_MS = 30 * 24 * 60 * 60 * 1000;
10
+ export function createCartStore(db, dependencies, now = Date.now) {
11
+ const d = db.dialect;
12
+ const carts = identifier(TABLES.carts, d);
13
+ const cartLines = identifier(TABLES.cartLines, d);
14
+ const stamp = () => new Date(now()).toISOString();
15
+ async function linesOf(cartId) {
16
+ const result = await db.query(sql `select * from ${cartLines} where cart_id = ${cartId} order by created_at asc, id asc`);
17
+ return result.rows.map((row) => ({
18
+ id: toText(row.id, 'cart_line.id'),
19
+ variantId: toText(row.variant_id, 'cart_line.variant_id'),
20
+ quantity: toInt(row.quantity, 'cart_line.quantity'),
21
+ unitPriceMinor: toInt(row.unit_price_minor, 'cart_line.unit_price_minor'),
22
+ }));
23
+ }
24
+ async function decode(row) {
25
+ const id = toText(row.id, 'cart.id');
26
+ return {
27
+ id,
28
+ customerId: toNullableText(row.customer_id),
29
+ sessionKey: toNullableText(row.session_key),
30
+ currency: toText(row.currency, 'cart.currency'),
31
+ couponCode: toNullableText(row.coupon_code),
32
+ shippingCountry: toNullableText(row.shipping_country),
33
+ shippingRegion: toNullableText(row.shipping_region),
34
+ shippingMethodId: toNullableText(row.shipping_method_id),
35
+ status: toText(row.status, 'cart.status'),
36
+ orderId: toNullableText(row.order_id),
37
+ lines: await linesOf(id),
38
+ createdAt: toText(row.created_at, 'cart.created_at'),
39
+ updatedAt: toText(row.updated_at, 'cart.updated_at'),
40
+ expiresAt: toText(row.expires_at, 'cart.expires_at'),
41
+ };
42
+ }
43
+ async function read(id) {
44
+ const result = await db.query(sql `select * from ${carts} where id = ${id}`);
45
+ const row = result.rows[0];
46
+ return row === undefined ? null : decode(row);
47
+ }
48
+ /** An open cart, or a refusal that says which of the two things went wrong. */
49
+ async function openCart(id) {
50
+ const cart = await read(id);
51
+ if (cart === null) {
52
+ throw new CogentaError({
53
+ code: 'COMMERCE_CART_NOT_FOUND',
54
+ message: 'This basket no longer exists.',
55
+ hint: 'Start a new basket. A basket is kept for thirty days.',
56
+ });
57
+ }
58
+ if (cart.status !== 'open') {
59
+ throw new CogentaError({
60
+ code: 'COMMERCE_CART_CLOSED',
61
+ message: cart.status === 'ordered'
62
+ ? 'This basket has already been ordered.'
63
+ : 'This basket was abandoned.',
64
+ hint: cart.status === 'ordered'
65
+ ? 'Look at the order rather than the basket it came from.'
66
+ : 'Start a new basket.',
67
+ details: { cartId: cart.id, status: cart.status },
68
+ });
69
+ }
70
+ return cart;
71
+ }
72
+ async function touch(id) {
73
+ await db.query(sql `update ${carts} set updated_at = ${stamp()} where id = ${id}`);
74
+ const cart = await read(id);
75
+ if (cart === null) {
76
+ throw new CogentaError({
77
+ code: 'COMMERCE_CART_NOT_FOUND',
78
+ message: 'This basket no longer exists.',
79
+ hint: 'Start a new basket.',
80
+ });
81
+ }
82
+ return cart;
83
+ }
84
+ function assertQuantity(quantity) {
85
+ if (!Number.isInteger(quantity) || quantity <= 0 || quantity > 10_000) {
86
+ throw new CogentaError({
87
+ code: 'COMMERCE_QUANTITY_INVALID',
88
+ message: `A basket quantity must be a whole number between 1 and 10000, got ${String(quantity)}.`,
89
+ hint: 'To remove a line, remove it. A quantity of zero is not a line.',
90
+ });
91
+ }
92
+ return quantity;
93
+ }
94
+ return {
95
+ open: async (input) => {
96
+ const id = newId(now);
97
+ const at = stamp();
98
+ const expires = new Date(now() + (input.ttlMs ?? CART_TTL_MS)).toISOString();
99
+ await db.query(sql `
100
+ insert into ${carts} (id, customer_id, session_key, currency, coupon_code,
101
+ shipping_country, shipping_region, shipping_method_id,
102
+ status, order_id, created_at, updated_at, expires_at)
103
+ values (${id}, ${input.customerId ?? null}, ${input.sessionKey ?? null},
104
+ ${assertCurrency(input.currency)}, ${null}, ${null}, ${null}, ${null},
105
+ ${'open'}, ${null}, ${at}, ${at}, ${expires})`);
106
+ return touch(id);
107
+ },
108
+ read,
109
+ find: async (owner) => {
110
+ const at = stamp();
111
+ const result = owner.customerId !== undefined
112
+ ? await db.query(sql `
113
+ select * from ${carts}
114
+ where customer_id = ${owner.customerId} and status = ${'open'} and expires_at > ${at}
115
+ order by updated_at desc`)
116
+ : await db.query(sql `
117
+ select * from ${carts}
118
+ where session_key = ${owner.sessionKey ?? ''} and status = ${'open'} and expires_at > ${at}
119
+ order by updated_at desc`);
120
+ const row = result.rows[0];
121
+ return row === undefined ? null : decode(row);
122
+ },
123
+ addLine: async (cartId, variantId, quantity) => {
124
+ const cart = await openCart(cartId);
125
+ assertQuantity(quantity);
126
+ const variant = await dependencies.catalog.readVariant(variantId);
127
+ if (variant === null) {
128
+ throw new CogentaError({
129
+ code: 'COMMERCE_VARIANT_NOT_FOUND',
130
+ message: 'This product variant does not exist.',
131
+ hint: 'It may have been withdrawn from sale.',
132
+ });
133
+ }
134
+ if (variant.currency !== cart.currency) {
135
+ throw new CogentaError({
136
+ code: 'COMMERCE_CURRENCY_MISMATCH',
137
+ message: `"${variant.title}" is priced in ${variant.currency}, and this basket is in ${cart.currency}.`,
138
+ hint: 'A basket holds one currency. Start a new basket for another one.',
139
+ });
140
+ }
141
+ const existing = cart.lines.find((line) => line.variantId === variantId);
142
+ if (existing !== undefined) {
143
+ // Adding the same thing twice adds to the line rather than making a
144
+ // second one. Two lines for one variant is the shape that lets a
145
+ // per-line stock check oversell.
146
+ assertQuantity(existing.quantity + quantity);
147
+ await db.query(sql `
148
+ update ${cartLines} set quantity = ${existing.quantity + quantity}, updated_at = ${stamp()}
149
+ where id = ${existing.id}`);
150
+ }
151
+ else {
152
+ const at = stamp();
153
+ await db.query(sql `
154
+ insert into ${cartLines} (id, cart_id, variant_id, quantity, unit_price_minor, created_at, updated_at)
155
+ values (${newId(now)}, ${cartId}, ${variantId}, ${quantity}, ${variant.priceMinor}, ${at}, ${at})`);
156
+ }
157
+ return touch(cartId);
158
+ },
159
+ setLineQuantity: async (cartId, variantId, quantity) => {
160
+ await openCart(cartId);
161
+ assertQuantity(quantity);
162
+ await db.query(sql `
163
+ update ${cartLines} set quantity = ${quantity}, updated_at = ${stamp()}
164
+ where cart_id = ${cartId} and variant_id = ${variantId}`);
165
+ return touch(cartId);
166
+ },
167
+ removeLine: async (cartId, variantId) => {
168
+ await openCart(cartId);
169
+ await db.query(sql `delete from ${cartLines} where cart_id = ${cartId} and variant_id = ${variantId}`);
170
+ return touch(cartId);
171
+ },
172
+ setAddress: async (cartId, zone) => {
173
+ await openCart(cartId);
174
+ await db.query(sql `
175
+ update ${carts}
176
+ set shipping_country = ${zone?.country.toUpperCase() ?? null},
177
+ shipping_region = ${zone?.region ?? null},
178
+ updated_at = ${stamp()}
179
+ where id = ${cartId}`);
180
+ return touch(cartId);
181
+ },
182
+ setShippingMethod: async (cartId, methodId) => {
183
+ await openCart(cartId);
184
+ await db.query(sql `update ${carts} set shipping_method_id = ${methodId}, updated_at = ${stamp()} where id = ${cartId}`);
185
+ return touch(cartId);
186
+ },
187
+ applyCoupon: async (cartId, code) => {
188
+ const cart = await openCart(cartId);
189
+ if (code === null) {
190
+ await db.query(sql `update ${carts} set coupon_code = ${null}, updated_at = ${stamp()} where id = ${cartId}`);
191
+ return touch(cartId);
192
+ }
193
+ // Checked here, and checked again when the order is placed. The first
194
+ // check is for the shopper (a clear message now, not at payment); the
195
+ // second is the one that is authoritative, because a coupon can expire
196
+ // or run out between the two.
197
+ const priced = await priceOf(cart);
198
+ const check = await dependencies.coupons.check(code, priced.totals.subtotalMinor, cart.currency);
199
+ if (check.kind !== 'ok') {
200
+ throw couponRefusal(check);
201
+ }
202
+ await db.query(sql `update ${carts} set coupon_code = ${check.coupon.code}, updated_at = ${stamp()} where id = ${cartId}`);
203
+ return touch(cartId);
204
+ },
205
+ claim: async (cartId, customerId) => {
206
+ await openCart(cartId);
207
+ await db.query(sql `update ${carts} set customer_id = ${customerId}, updated_at = ${stamp()} where id = ${cartId}`);
208
+ return touch(cartId);
209
+ },
210
+ price: async (cartId) => {
211
+ const cart = await read(cartId);
212
+ if (cart === null) {
213
+ throw new CogentaError({
214
+ code: 'COMMERCE_CART_NOT_FOUND',
215
+ message: 'This basket no longer exists.',
216
+ hint: 'Start a new basket.',
217
+ });
218
+ }
219
+ return priceOf(cart);
220
+ },
221
+ abandon: async (cartId) => {
222
+ await db.query(sql `update ${carts} set status = ${'abandoned'}, updated_at = ${stamp()} where id = ${cartId} and status = ${'open'}`);
223
+ },
224
+ };
225
+ /** Prices a cart against the *live* catalogue, tax rules and coupon. */
226
+ async function priceOf(cart) {
227
+ const zone = cart.shippingCountry === null
228
+ ? null
229
+ : { country: cart.shippingCountry, region: cart.shippingRegion };
230
+ const lines = [];
231
+ const priceChanges = [];
232
+ const missingVariantIds = [];
233
+ const categories = new Set();
234
+ for (const line of cart.lines) {
235
+ const variant = await dependencies.catalog.readVariant(line.variantId);
236
+ if (variant === null) {
237
+ missingVariantIds.push(line.variantId);
238
+ continue;
239
+ }
240
+ if (variant.priceMinor !== line.unitPriceMinor) {
241
+ priceChanges.push({
242
+ variantId: line.variantId,
243
+ wasMinor: line.unitPriceMinor,
244
+ nowMinor: variant.priceMinor,
245
+ });
246
+ }
247
+ categories.add(variant.taxCategory);
248
+ lines.push({
249
+ variantId: variant.id,
250
+ sku: variant.sku,
251
+ title: variant.title,
252
+ quantity: line.quantity,
253
+ // The live price wins, and the change is reported. A cart that quietly
254
+ // charges a stale price is a bug the shop only finds in its margins.
255
+ unitPriceMinor: variant.priceMinor,
256
+ taxCategory: variant.taxCategory,
257
+ weightGrams: variant.weightGrams,
258
+ });
259
+ }
260
+ categories.add('shipping');
261
+ const taxRules = new Map();
262
+ for (const category of categories) {
263
+ taxRules.set(category, await dependencies.tax.resolve(zone, category));
264
+ }
265
+ const subtotalMinor = lines.reduce((sum, line) => sum + line.unitPriceMinor * line.quantity, 0);
266
+ const weightGrams = lines.reduce((sum, line) => sum + line.weightGrams * line.quantity, 0);
267
+ let shippingMinor = 0;
268
+ if (cart.shippingMethodId !== null && lines.length > 0) {
269
+ const quote = await dependencies.shipping.quote(cart.shippingMethodId, zone, {
270
+ weightGrams,
271
+ subtotalMinor,
272
+ currency: cart.currency,
273
+ });
274
+ shippingMinor = quote.amountMinor;
275
+ }
276
+ // Re-checked, never trusted from the row: a coupon stored on the cart may
277
+ // have expired, been exhausted or been deactivated since it was applied.
278
+ let coupon = null;
279
+ if (cart.couponCode !== null) {
280
+ const check = await dependencies.coupons.check(cart.couponCode, subtotalMinor, cart.currency);
281
+ if (check.kind === 'ok')
282
+ coupon = check.coupon;
283
+ }
284
+ const totals = computeTotals({
285
+ currency: cart.currency,
286
+ lines,
287
+ taxRules,
288
+ coupon,
289
+ shippingMinor,
290
+ shippingTaxCategory: 'shipping',
291
+ });
292
+ return { cart, totals, priceChanges, missingVariantIds };
293
+ }
294
+ }
295
+ /** Turns a coupon check into the message a shopper should actually read. */
296
+ export function couponRefusal(check) {
297
+ switch (check.kind) {
298
+ case 'unknown':
299
+ return new CogentaError({
300
+ code: 'COMMERCE_COUPON_NOT_FOUND',
301
+ message: 'This discount code does not exist.',
302
+ hint: 'Check the spelling. Codes are not case sensitive.',
303
+ });
304
+ case 'inactive':
305
+ return new CogentaError({
306
+ code: 'COMMERCE_COUPON_INVALID',
307
+ message: 'This discount code is no longer active.',
308
+ hint: 'The shop has withdrawn it.',
309
+ });
310
+ case 'not_yet':
311
+ return new CogentaError({
312
+ code: 'COMMERCE_COUPON_INVALID',
313
+ message: 'This discount code is not valid yet.',
314
+ hint: `It can be used from ${check.startsAt}.`,
315
+ });
316
+ case 'expired':
317
+ return new CogentaError({
318
+ code: 'COMMERCE_COUPON_INVALID',
319
+ message: 'This discount code has expired.',
320
+ hint: `It ended on ${check.endsAt}.`,
321
+ });
322
+ case 'exhausted':
323
+ return new CogentaError({
324
+ code: 'COMMERCE_COUPON_EXHAUSTED',
325
+ message: 'This discount code has been fully used.',
326
+ hint: 'It had a limited number of uses and they are all taken.',
327
+ });
328
+ case 'below_minimum':
329
+ return new CogentaError({
330
+ code: 'COMMERCE_COUPON_INVALID',
331
+ message: 'The basket is below the minimum for this discount code.',
332
+ hint: `It applies from a subtotal of ${String(check.minSubtotalMinor)} minor units.`,
333
+ });
334
+ case 'wrong_currency':
335
+ return new CogentaError({
336
+ code: 'COMMERCE_CURRENCY_MISMATCH',
337
+ message: `This discount code only applies to baskets in ${check.currency}.`,
338
+ hint: 'Use a code issued for this currency.',
339
+ });
340
+ }
341
+ }
342
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/cart/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ import type { Coupon } from '../coupon/store.js';
2
+ import type { TaxRule } from '../tax/store.js';
3
+ /**
4
+ * The one place an order total is computed.
5
+ *
6
+ * Deliberately a pure function over plain values, with no database handle and
7
+ * no store: the totals a cart shows and the totals an order is placed with
8
+ * must be produced by the same code, or the number on the confirmation page
9
+ * and the number charged to the card drift apart. Everything that varies —
10
+ * prices, rules, coupon — is passed in.
11
+ *
12
+ * The order of operations is the part worth being explicit about, because
13
+ * every ordering is defensible and only one is conventional:
14
+ *
15
+ * 1. line subtotal = unit price × quantity
16
+ * 2. the order discount is spread across lines in proportion to subtotal
17
+ * 3. tax is computed **per line, after discount** — a discount reduces the
18
+ * tax owed, and a per-line rate is needed because two lines can sit in
19
+ * different tax categories
20
+ * 4. shipping is added, and taxed under its own category
21
+ * 5. total = subtotal − discount + shipping + tax added (tax *included* is
22
+ * already inside the subtotal and is never added again)
23
+ *
24
+ * Step 3 after step 2 is what an invoice has to show. Taxing before the
25
+ * discount overstates the tax and is, in most of Europe, simply wrong.
26
+ */
27
+ export interface TotalsLineInput {
28
+ readonly variantId: string;
29
+ readonly sku: string;
30
+ readonly title: string;
31
+ readonly quantity: number;
32
+ readonly unitPriceMinor: number;
33
+ readonly taxCategory: string;
34
+ readonly weightGrams: number;
35
+ }
36
+ export interface TotalsLine {
37
+ readonly variantId: string;
38
+ readonly sku: string;
39
+ readonly title: string;
40
+ readonly quantity: number;
41
+ readonly unitPriceMinor: number;
42
+ readonly subtotalMinor: number;
43
+ readonly discountMinor: number;
44
+ readonly taxMinor: number;
45
+ readonly taxRateBp: number;
46
+ /** What this line contributes to the order total. */
47
+ readonly totalMinor: number;
48
+ }
49
+ export interface TotalsInput {
50
+ readonly currency: string;
51
+ readonly lines: readonly TotalsLineInput[];
52
+ /** Resolved tax rule per tax category. A missing category means no tax. */
53
+ readonly taxRules: ReadonlyMap<string, TaxRule | null>;
54
+ readonly coupon?: Coupon | null;
55
+ readonly shippingMinor?: number;
56
+ /** The category the shipping line is taxed under. */
57
+ readonly shippingTaxCategory?: string;
58
+ }
59
+ export interface Totals {
60
+ readonly currency: string;
61
+ readonly lines: readonly TotalsLine[];
62
+ readonly subtotalMinor: number;
63
+ readonly discountMinor: number;
64
+ readonly shippingMinor: number;
65
+ /** Tax **added** on top. Tax already included in prices is not counted here. */
66
+ readonly taxMinor: number;
67
+ /** Tax already contained in the prices shown. Reported, never added. */
68
+ readonly taxIncludedMinor: number;
69
+ readonly totalMinor: number;
70
+ readonly weightGrams: number;
71
+ }
72
+ export declare function computeTotals(input: TotalsInput): Totals;
73
+ //# sourceMappingURL=totals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"totals.d.ts","sourceRoot":"","sources":["../../src/cart/totals.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAGhD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAG9C;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAC7B;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,qDAAqD;IACrD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,KAAK,EAAE,SAAS,eAAe,EAAE,CAAA;IAC1C,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC,CAAA;IACtD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;IAC/B,qDAAqD;IACrD,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAA;CACtC;AAED,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAA;IACrC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,gFAAgF;IAChF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,wEAAwE;IACxE,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAA;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAC7B;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAqExD"}
@@ -0,0 +1,65 @@
1
+ import { discountFor } from '../coupon/store.js';
2
+ import { assertSameCurrency, distribute } from '../money.js';
3
+ import { taxFor } from '../tax/store.js';
4
+ export function computeTotals(input) {
5
+ const currency = assertSameCurrency(input.currency, input.currency);
6
+ const subtotals = input.lines.map((line) => line.unitPriceMinor * line.quantity);
7
+ const subtotalMinor = subtotals.reduce((sum, value) => sum + value, 0);
8
+ const weightGrams = input.lines.reduce((sum, line) => sum + line.weightGrams * line.quantity, 0);
9
+ const couponOutcome = input.coupon == null
10
+ ? { discountMinor: 0, freeShipping: false }
11
+ : discountFor(input.coupon, subtotalMinor);
12
+ const discountMinor = couponOutcome.discountMinor;
13
+ const shippingMinor = couponOutcome.freeShipping ? 0 : (input.shippingMinor ?? 0);
14
+ // The order-level discount attributed to lines, exactly — `distribute`
15
+ // hands out the remainder so the parts sum to the whole. Rounding each
16
+ // line independently would lose or invent up to one minor unit per line,
17
+ // and an invoice whose lines do not add up to its total is not an invoice.
18
+ const lineDiscounts = distribute(discountMinor, subtotals);
19
+ let taxMinor = 0;
20
+ let taxIncludedMinor = 0;
21
+ const lines = input.lines.map((line, index) => {
22
+ const lineSubtotal = subtotals[index] ?? 0;
23
+ const lineDiscount = lineDiscounts[index] ?? 0;
24
+ const taxable = lineSubtotal - lineDiscount;
25
+ const rule = input.taxRules.get(line.taxCategory) ?? null;
26
+ const outcome = taxFor(taxable, rule);
27
+ if (outcome.includedInPrice)
28
+ taxIncludedMinor += outcome.taxMinor;
29
+ else
30
+ taxMinor += outcome.taxMinor;
31
+ return {
32
+ variantId: line.variantId,
33
+ sku: line.sku,
34
+ title: line.title,
35
+ quantity: line.quantity,
36
+ unitPriceMinor: line.unitPriceMinor,
37
+ subtotalMinor: lineSubtotal,
38
+ discountMinor: lineDiscount,
39
+ taxMinor: outcome.taxMinor,
40
+ taxRateBp: outcome.rateBp,
41
+ totalMinor: taxable + (outcome.includedInPrice ? 0 : outcome.taxMinor),
42
+ };
43
+ });
44
+ if (shippingMinor > 0) {
45
+ const shippingRule = input.taxRules.get(input.shippingTaxCategory ?? 'shipping') ?? null;
46
+ const shippingTax = taxFor(shippingMinor, shippingRule);
47
+ if (shippingTax.includedInPrice)
48
+ taxIncludedMinor += shippingTax.taxMinor;
49
+ else
50
+ taxMinor += shippingTax.taxMinor;
51
+ }
52
+ const totalMinor = subtotalMinor - discountMinor + shippingMinor + taxMinor;
53
+ return {
54
+ currency,
55
+ lines,
56
+ subtotalMinor,
57
+ discountMinor,
58
+ shippingMinor,
59
+ taxMinor,
60
+ taxIncludedMinor,
61
+ totalMinor,
62
+ weightGrams,
63
+ };
64
+ }
65
+ //# sourceMappingURL=totals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"totals.js","sourceRoot":"","sources":["../../src/cart/totals.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import { type DatabaseHandle, type SqlExecutor } from '@cogenta/core';
2
+ import type { CreateProductInput, CreateVariantInput, Product, ProductStatus, StockRequest, StockShortfall, UpdateProductInput, UpdateVariantInput, Variant } from './types.js';
3
+ export interface ProductListOptions {
4
+ readonly status?: ProductStatus;
5
+ /** Case-insensitive substring of handle or title. */
6
+ readonly search?: string;
7
+ readonly limit?: number;
8
+ readonly offset?: number;
9
+ }
10
+ /**
11
+ * The outcome of taking stock. Never a boolean and never a throw for the
12
+ * ordinary case: "the last one just went" is a normal thing to happen in a
13
+ * shop, and the caller needs to know *which* line was short and by how much.
14
+ */
15
+ export type StockOutcome = {
16
+ readonly kind: 'taken';
17
+ } | {
18
+ readonly kind: 'short';
19
+ readonly shortfalls: readonly StockShortfall[];
20
+ };
21
+ export interface CatalogStore {
22
+ createProduct(input: CreateProductInput): Promise<Product>;
23
+ readProduct(id: string): Promise<Product | null>;
24
+ readProductByHandle(handle: string): Promise<Product | null>;
25
+ updateProduct(id: string, input: UpdateProductInput): Promise<Product>;
26
+ /** Archives. A product that has ever been ordered is never deleted: an order
27
+ * line copies what it needs, but the admin still wants the trail. */
28
+ archiveProduct(id: string): Promise<Product>;
29
+ deleteProduct(id: string): Promise<void>;
30
+ listProducts(options?: ProductListOptions): Promise<readonly Product[]>;
31
+ createVariant(input: CreateVariantInput): Promise<Variant>;
32
+ readVariant(id: string): Promise<Variant | null>;
33
+ readVariantBySku(sku: string): Promise<Variant | null>;
34
+ updateVariant(id: string, input: UpdateVariantInput): Promise<Variant>;
35
+ deleteVariant(id: string): Promise<void>;
36
+ listVariants(productId: string): Promise<readonly Variant[]>;
37
+ /** Sets absolute stock. For a stock take, never for a sale. */
38
+ setStock(variantId: string, onHand: number): Promise<Variant>;
39
+ /** Adds to stock. For a delivery, and for putting a cancelled order back. */
40
+ restock(requests: readonly StockRequest[], tx?: SqlExecutor): Promise<void>;
41
+ /**
42
+ * Takes stock for a sale, atomically, all lines or none.
43
+ *
44
+ * The only correct way to lower stock in this package. Runs in one immediate
45
+ * transaction and lowers each line with `update … set on_hand = on_hand - :n
46
+ * where id = :id and on_hand >= :n`, reading `rowsAffected`. Two shoppers
47
+ * racing for the last unit both pass the read, and exactly one passes the
48
+ * UPDATE — which is the difference between a shop and an oversold shop.
49
+ */
50
+ takeStock(requests: readonly StockRequest[], tx?: SqlExecutor): Promise<StockOutcome>;
51
+ }
52
+ export declare function createCatalogStore(db: DatabaseHandle, now?: () => number): CatalogStore;
53
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/catalog/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EAInB,KAAK,WAAW,EAGjB,MAAM,eAAe,CAAA;AAItB,OAAO,KAAK,EAEV,kBAAkB,EAClB,kBAAkB,EAClB,OAAO,EACP,aAAa,EACb,YAAY,EACZ,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,OAAO,EACR,MAAM,YAAY,CAAA;AAEnB,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAA;IAC/B,qDAAqD;IACrD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CACzB;AAED;;;;GAIG;AACH,MAAM,MAAM,YAAY,GACpB;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAC1B;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,SAAS,cAAc,EAAE,CAAA;CAAE,CAAA;AAE9E,MAAM,WAAW,YAAY;IAC3B,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC1D,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAA;IAChD,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAA;IAC5D,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACtE;yEACqE;IACrE,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC5C,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACxC,YAAY,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,SAAS,OAAO,EAAE,CAAC,CAAA;IAEvE,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC1D,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAA;IAChD,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAA;IACtD,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACtE,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACxC,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,OAAO,EAAE,CAAC,CAAA;IAE5D,+DAA+D;IAC/D,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC7D,6EAA6E;IAC7E,OAAO,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,EAAE,EAAE,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3E;;;;;;;;OAQG;IACH,SAAS,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,EAAE,EAAE,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;CACtF;AA+GD,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,cAAc,EAAE,GAAG,GAAE,MAAM,MAAiB,GAAG,YAAY,CAqUjG"}