@porulle/core 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/middleware.d.ts.map +1 -1
- package/dist/auth/middleware.js +26 -7
- package/dist/auth/setup.d.ts.map +1 -1
- package/dist/auth/setup.js +7 -5
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +3 -1
- package/dist/config/types.d.ts +13 -3
- package/dist/config/types.d.ts.map +1 -1
- package/dist/generated/plugin-manifest.d.ts +3 -0
- package/dist/generated/plugin-manifest.d.ts.map +1 -1
- package/dist/generated/plugin-manifest.js +1 -0
- package/dist/hooks/checkout-completion.d.ts.map +1 -1
- package/dist/hooks/checkout-completion.js +2 -2
- package/dist/hooks/checkout.d.ts.map +1 -1
- package/dist/hooks/checkout.js +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/interfaces/rest/router.d.ts.map +1 -1
- package/dist/interfaces/rest/router.js +2 -0
- package/dist/interfaces/rest/routes/admin/staff.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/admin/staff.js +37 -3
- package/dist/interfaces/rest/routes/checkout.d.ts +9 -0
- package/dist/interfaces/rest/routes/checkout.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/checkout.js +82 -22
- package/dist/interfaces/rest/routes/inventory.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/inventory.js +13 -0
- package/dist/interfaces/rest/routes/orders.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/orders.js +13 -0
- package/dist/interfaces/rest/schemas/catalog.d.ts +391 -0
- package/dist/interfaces/rest/schemas/catalog.d.ts.map +1 -1
- package/dist/interfaces/rest/schemas/orders.js +2 -2
- package/dist/interfaces/rest/schemas/responses.d.ts +85 -0
- package/dist/interfaces/rest/schemas/responses.d.ts.map +1 -1
- package/dist/interfaces/rest/schemas/search.d.ts +17 -0
- package/dist/interfaces/rest/schemas/search.d.ts.map +1 -1
- package/dist/kernel/database/scoped-db.d.ts +2 -1
- package/dist/kernel/database/scoped-db.d.ts.map +1 -1
- package/dist/kernel/database/scoped-db.js +74 -15
- package/dist/kernel/errors.d.ts +12 -0
- package/dist/kernel/errors.d.ts.map +1 -1
- package/dist/kernel/errors.js +16 -0
- package/dist/kernel/jobs/adapter.d.ts +27 -8
- package/dist/kernel/jobs/adapter.d.ts.map +1 -1
- package/dist/kernel/jobs/adapter.js +1 -2
- package/dist/kernel/jobs/drizzle-adapter.d.ts +13 -3
- package/dist/kernel/jobs/drizzle-adapter.d.ts.map +1 -1
- package/dist/kernel/jobs/drizzle-adapter.js +46 -5
- package/dist/kernel/jobs/runner.d.ts +2 -1
- package/dist/kernel/jobs/runner.d.ts.map +1 -1
- package/dist/kernel/jobs/runner.js +100 -15
- package/dist/kernel/jobs/types.d.ts +13 -0
- package/dist/kernel/jobs/types.d.ts.map +1 -1
- package/dist/kernel/plugin/manifest.d.ts +5 -0
- package/dist/kernel/plugin/manifest.d.ts.map +1 -1
- package/dist/kernel/plugin/manifest.js +9 -0
- package/dist/modules/analytics/drizzle-adapter.d.ts.map +1 -1
- package/dist/modules/analytics/drizzle-adapter.js +16 -3
- package/dist/modules/cart/service.d.ts.map +1 -1
- package/dist/modules/cart/service.js +3 -2
- package/dist/modules/catalog/entity-service.d.ts +1 -0
- package/dist/modules/catalog/entity-service.d.ts.map +1 -1
- package/dist/modules/catalog/entity-service.js +49 -3
- package/dist/modules/catalog/repository/index.d.ts +2 -2
- package/dist/modules/catalog/repository/index.d.ts.map +1 -1
- package/dist/modules/catalog/repository/index.js +9 -1
- package/dist/modules/catalog/schema.d.ts +51 -0
- package/dist/modules/catalog/schema.d.ts.map +1 -1
- package/dist/modules/catalog/schema.js +12 -1
- package/dist/modules/catalog/schemas.d.ts +3 -1
- package/dist/modules/catalog/schemas.d.ts.map +1 -1
- package/dist/modules/channels/adapter.d.ts +117 -0
- package/dist/modules/channels/adapter.d.ts.map +1 -0
- package/dist/modules/channels/adapter.js +3 -0
- package/dist/modules/customers/service.d.ts.map +1 -1
- package/dist/modules/customers/service.js +6 -0
- package/dist/modules/inventory/repository/index.js +4 -4
- package/dist/modules/inventory/service.d.ts.map +1 -1
- package/dist/modules/inventory/service.js +7 -3
- package/dist/modules/orders/schema.d.ts +17 -0
- package/dist/modules/orders/schema.d.ts.map +1 -1
- package/dist/modules/orders/schema.js +4 -0
- package/dist/modules/orders/service.d.ts +16 -2
- package/dist/modules/orders/service.d.ts.map +1 -1
- package/dist/modules/orders/service.js +226 -26
- package/dist/modules/orders/stale-order-cleanup.d.ts.map +1 -1
- package/dist/modules/orders/stale-order-cleanup.js +37 -16
- package/dist/modules/pricing/service.d.ts.map +1 -1
- package/dist/modules/pricing/service.js +5 -5
- package/dist/modules/promotions/repository/index.d.ts +1 -1
- package/dist/modules/promotions/repository/index.d.ts.map +1 -1
- package/dist/modules/promotions/repository/index.js +6 -3
- package/dist/modules/promotions/service.d.ts +1 -0
- package/dist/modules/promotions/service.d.ts.map +1 -1
- package/dist/modules/promotions/service.js +1 -1
- package/dist/modules/webhooks/repository/index.d.ts +1 -1
- package/dist/modules/webhooks/repository/index.d.ts.map +1 -1
- package/dist/modules/webhooks/repository/index.js +16 -10
- package/dist/runtime/kernel.d.ts.map +1 -1
- package/dist/runtime/kernel.js +14 -2
- package/dist/runtime/server.d.ts +1 -4
- package/dist/runtime/server.d.ts.map +1 -1
- package/dist/runtime/server.js +16 -34
- package/dist/test-utils/create-test-config.d.ts.map +1 -1
- package/dist/test-utils/create-test-config.js +2 -0
- package/dist/test-utils/order-test-helpers.d.ts +12 -0
- package/dist/test-utils/order-test-helpers.d.ts.map +1 -0
- package/dist/test-utils/order-test-helpers.js +18 -0
- package/dist/test-utils/rest-api-test-utils.d.ts.map +1 -1
- package/dist/test-utils/rest-api-test-utils.js +1 -0
- package/dist/test-utils/test-actors.js +1 -1
- package/dist/testing.d.ts +1 -0
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +1 -0
- package/package.json +1 -1
- package/src/auth/middleware.ts +26 -7
- package/src/auth/setup.ts +10 -4
- package/src/config/defaults.ts +3 -1
- package/src/config/types.ts +16 -3
- package/src/generated/plugin-capabilities.d.ts +1 -0
- package/src/generated/plugin-manifest.ts +1 -0
- package/src/generated/plugin-repositories.d.ts +1 -0
- package/src/hooks/checkout-completion.ts +22 -16
- package/src/hooks/checkout.ts +11 -8
- package/src/index.ts +31 -1
- package/src/interfaces/rest/router.ts +2 -0
- package/src/interfaces/rest/routes/admin/staff.ts +45 -3
- package/src/interfaces/rest/routes/checkout.ts +94 -28
- package/src/interfaces/rest/routes/inventory.ts +14 -0
- package/src/interfaces/rest/routes/orders.ts +11 -0
- package/src/interfaces/rest/schemas/orders.ts +2 -2
- package/src/kernel/database/scoped-db.ts +83 -15
- package/src/kernel/errors.ts +20 -0
- package/src/kernel/jobs/adapter.ts +39 -8
- package/src/kernel/jobs/drizzle-adapter.ts +72 -9
- package/src/kernel/jobs/runner.ts +194 -72
- package/src/kernel/jobs/types.ts +25 -0
- package/src/kernel/plugin/manifest.ts +15 -0
- package/src/modules/analytics/drizzle-adapter.ts +19 -3
- package/src/modules/cart/service.ts +3 -2
- package/src/modules/catalog/entity-service.ts +37 -2
- package/src/modules/catalog/repository/index.ts +13 -3
- package/src/modules/catalog/schema.ts +33 -18
- package/src/modules/catalog/schemas.ts +3 -1
- package/src/modules/channels/adapter.ts +139 -0
- package/src/modules/customers/service.ts +6 -0
- package/src/modules/inventory/repository/index.ts +4 -4
- package/src/modules/inventory/service.ts +12 -2
- package/src/modules/orders/schema.ts +4 -0
- package/src/modules/orders/service.ts +404 -64
- package/src/modules/orders/stale-order-cleanup.ts +52 -27
- package/src/modules/pricing/service.ts +5 -6
- package/src/modules/promotions/repository/index.ts +6 -2
- package/src/modules/promotions/service.ts +2 -0
- package/src/modules/webhooks/repository/index.ts +16 -14
- package/src/runtime/kernel.ts +16 -2
- package/src/runtime/server.ts +22 -42
- package/src/test-utils/create-test-config.ts +2 -0
- package/src/test-utils/order-test-helpers.ts +26 -0
- package/src/test-utils/rest-api-test-utils.ts +1 -0
- package/src/test-utils/test-actors.ts +1 -1
- package/src/testing.ts +1 -0
|
@@ -7,12 +7,14 @@ import { canTransition, orderStateMachine, } from "../../kernel/state-machine/ma
|
|
|
7
7
|
import { Err, Ok } from "../../kernel/result.js";
|
|
8
8
|
import { createLogger } from "../../utils/logger.js";
|
|
9
9
|
import { paginate } from "../../utils/pagination.js";
|
|
10
|
+
import { withTransaction, } from "../../kernel/database/tx-context.js";
|
|
10
11
|
function context(actor, services, database, tx = null) {
|
|
11
12
|
return createHookContext({
|
|
12
13
|
actor,
|
|
13
14
|
tx,
|
|
14
15
|
logger: createLogger("orders"),
|
|
15
16
|
services,
|
|
17
|
+
...(services.jobs ? { jobs: services.jobs } : {}),
|
|
16
18
|
context: { moduleName: "orders" },
|
|
17
19
|
database: { db: database.db },
|
|
18
20
|
});
|
|
@@ -30,7 +32,70 @@ export class OrderService {
|
|
|
30
32
|
const lineItems = await this.repo.findLineItemsByOrderId(order.id, ctx);
|
|
31
33
|
return { ...order, lineItems };
|
|
32
34
|
}
|
|
33
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Resolve every line item's price with org-integrity + price provenance,
|
|
37
|
+
* mirroring how our forefathers handle this (Vendure: order lines are always
|
|
38
|
+
* server-priced from the variant; Medusa: an admin override is honored but
|
|
39
|
+
* flagged `is_custom_price`). Three modes:
|
|
40
|
+
* - trusted caller (checkout / POS exchange, already server-priced) → prices honored as-is.
|
|
41
|
+
* - actor with `orders:manage` → explicit price honored as a manual override, flagged isCustomPrice.
|
|
42
|
+
* - otherwise → price re-derived server-side from the catalog; client prices ignored.
|
|
43
|
+
* The referenced entity MUST belong to the order's org in every mode.
|
|
44
|
+
*/
|
|
45
|
+
async resolveLines(input, actor, trusted, ctx) {
|
|
46
|
+
let canOverride = trusted;
|
|
47
|
+
if (!canOverride) {
|
|
48
|
+
try {
|
|
49
|
+
assertPermission(actor, "orders:manage");
|
|
50
|
+
canOverride = true;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
canOverride = false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const catalog = this.deps.services.catalog;
|
|
57
|
+
const pricing = this.deps.services.pricing;
|
|
58
|
+
const resolved = [];
|
|
59
|
+
for (const item of input.lineItems) {
|
|
60
|
+
// Org-integrity — the referenced entity must belong to this order's org.
|
|
61
|
+
const owned = await catalog.getById(item.entityId, { includeVariants: item.variantId !== undefined }, actor, ctx);
|
|
62
|
+
if (!owned.ok) {
|
|
63
|
+
return Err(new CommerceValidationError(`Line item entity ${item.entityId} does not belong to this organization.`));
|
|
64
|
+
}
|
|
65
|
+
if (item.variantId !== undefined &&
|
|
66
|
+
!owned.value?.variants?.some((variant) => variant.id === item.variantId)) {
|
|
67
|
+
return Err(new CommerceValidationError(`Line item variant ${item.variantId} does not belong to entity ${item.entityId}.`));
|
|
68
|
+
}
|
|
69
|
+
if (trusted || canOverride) {
|
|
70
|
+
// Trusted pipeline totals, or a staff manual/negotiated override.
|
|
71
|
+
resolved.push({ ...item, isCustomPrice: !trusted && canOverride });
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
// Server-derive the price; the client-supplied unitPrice/totalPrice is ignored.
|
|
75
|
+
const priced = await pricing.resolve({
|
|
76
|
+
entityId: item.entityId,
|
|
77
|
+
currency: input.currency,
|
|
78
|
+
quantity: item.quantity,
|
|
79
|
+
...(item.variantId ? { variantId: item.variantId } : {}),
|
|
80
|
+
...(input.customerId ? { customerId: input.customerId } : {}),
|
|
81
|
+
}, actor, ctx);
|
|
82
|
+
if (!priced.ok || !priced.value) {
|
|
83
|
+
return Err(new CommerceValidationError(`Cannot resolve price for ${item.entityId}.`));
|
|
84
|
+
}
|
|
85
|
+
const unitPrice = priced.value.finalAmount;
|
|
86
|
+
resolved.push({
|
|
87
|
+
...item,
|
|
88
|
+
unitPrice,
|
|
89
|
+
totalPrice: unitPrice * item.quantity,
|
|
90
|
+
isCustomPrice: false,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
return Ok(resolved);
|
|
94
|
+
}
|
|
95
|
+
async create(input, actor, ctx, opts) {
|
|
96
|
+
if (opts?.stockPolicy === "reserve" && !ctx) {
|
|
97
|
+
return withTransaction(this.deps.database, { actor }, (txCtx) => this.create(input, actor, txCtx, opts));
|
|
98
|
+
}
|
|
34
99
|
try {
|
|
35
100
|
assertPermission(actor, "orders:create");
|
|
36
101
|
}
|
|
@@ -53,6 +118,38 @@ export class OrderService {
|
|
|
53
118
|
return Ok(await this.hydrateOrder(existing, ctx));
|
|
54
119
|
}
|
|
55
120
|
}
|
|
121
|
+
// Resolve line prices with org-integrity + provenance (see resolveLines).
|
|
122
|
+
const trusted = opts?.trustedPricing === true;
|
|
123
|
+
const resolvedResult = await this.resolveLines(processed, actor, trusted, ctx);
|
|
124
|
+
if (!resolvedResult.ok)
|
|
125
|
+
return Err(resolvedResult.error);
|
|
126
|
+
const resolvedLines = resolvedResult.value;
|
|
127
|
+
// Order totals: trusted callers (checkout / exchange) supply rich totals
|
|
128
|
+
// (promotions, tax, shipping); otherwise derive from the server-priced lines.
|
|
129
|
+
const computedSubtotal = trusted
|
|
130
|
+
? processed.subtotal
|
|
131
|
+
: resolvedLines.reduce((sum, l) => sum + (l.totalPrice ?? l.unitPrice * l.quantity), 0);
|
|
132
|
+
const computedGrandTotal = trusted
|
|
133
|
+
? processed.grandTotal
|
|
134
|
+
: computedSubtotal +
|
|
135
|
+
processed.taxTotal +
|
|
136
|
+
processed.shippingTotal -
|
|
137
|
+
(processed.discountTotal ?? 0);
|
|
138
|
+
// Stock: order creation does NOT reserve by default. Stock is allocated at
|
|
139
|
+
// the lifecycle transition to a committed state — checkout reserves in its
|
|
140
|
+
// own pipeline, mirroring Vendure's StockAllocationStrategy (allocate on
|
|
141
|
+
// PaymentAuthorized, not on creation). A caller that represents an immediate
|
|
142
|
+
// committed sale (e.g. a POS exchange) opts in with stockPolicy: "reserve".
|
|
143
|
+
const stockPolicy = opts?.stockPolicy;
|
|
144
|
+
if (stockPolicy === "reserve") {
|
|
145
|
+
const inventory = this.deps.services.inventory;
|
|
146
|
+
for (const line of resolvedLines) {
|
|
147
|
+
const avail = await inventory.getAvailable(line.entityId, line.variantId, ctx, actor);
|
|
148
|
+
if (!avail.ok || (avail.value ?? 0) < line.quantity) {
|
|
149
|
+
return Err(new CommerceValidationError(`Insufficient stock for ${line.title ?? line.entityId}. Available: ${avail.ok ? (avail.value ?? 0) : 0}, requested: ${line.quantity}.`));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
56
153
|
const orderNumber = await this.repo.getNextOrderNumber(ctx);
|
|
57
154
|
let order;
|
|
58
155
|
try {
|
|
@@ -61,11 +158,11 @@ export class OrderService {
|
|
|
61
158
|
orderNumber,
|
|
62
159
|
status: "pending",
|
|
63
160
|
currency: processed.currency,
|
|
64
|
-
subtotal:
|
|
161
|
+
subtotal: computedSubtotal,
|
|
65
162
|
taxTotal: processed.taxTotal,
|
|
66
163
|
shippingTotal: processed.shippingTotal,
|
|
67
164
|
discountTotal: processed.discountTotal ?? 0,
|
|
68
|
-
grandTotal:
|
|
165
|
+
grandTotal: computedGrandTotal,
|
|
69
166
|
...(processed.paymentIntentId != null ? { paymentIntentId: processed.paymentIntentId } : {}),
|
|
70
167
|
...(processed.paymentMethodId != null ? { paymentMethodId: processed.paymentMethodId } : {}),
|
|
71
168
|
...(processed.idempotencyKey != null ? { idempotencyKey: processed.idempotencyKey } : {}),
|
|
@@ -85,14 +182,15 @@ export class OrderService {
|
|
|
85
182
|
}
|
|
86
183
|
throw error;
|
|
87
184
|
}
|
|
88
|
-
const lineItemsData =
|
|
185
|
+
const lineItemsData = resolvedLines.map((item) => ({
|
|
89
186
|
orderId: order.id,
|
|
90
187
|
entityId: item.entityId,
|
|
91
188
|
entityType: item.entityType,
|
|
92
189
|
title: item.title,
|
|
93
190
|
quantity: item.quantity,
|
|
94
191
|
unitPrice: item.unitPrice,
|
|
95
|
-
totalPrice: item.totalPrice,
|
|
192
|
+
totalPrice: item.totalPrice ?? item.unitPrice * item.quantity,
|
|
193
|
+
isCustomPrice: item.isCustomPrice,
|
|
96
194
|
taxAmount: item.taxAmount ?? 0,
|
|
97
195
|
discountAmount: item.discountAmount ?? 0,
|
|
98
196
|
fulfillmentStatus: "unfulfilled",
|
|
@@ -101,6 +199,23 @@ export class OrderService {
|
|
|
101
199
|
...(item.sku !== undefined ? { sku: item.sku } : {}),
|
|
102
200
|
}));
|
|
103
201
|
await this.repo.createLineItems(lineItemsData, ctx);
|
|
202
|
+
// Reserve stock for the resolved lines when the reserve policy is active
|
|
203
|
+
// (availability was verified above). Trusted callers reserve themselves.
|
|
204
|
+
if (stockPolicy === "reserve") {
|
|
205
|
+
const inventory = this.deps.services.inventory;
|
|
206
|
+
for (const line of resolvedLines) {
|
|
207
|
+
const reserved = await inventory.reserve({
|
|
208
|
+
entityId: line.entityId,
|
|
209
|
+
quantity: line.quantity,
|
|
210
|
+
orderId: order.id,
|
|
211
|
+
...(line.variantId ? { variantId: line.variantId } : {}),
|
|
212
|
+
}, actor, ctx);
|
|
213
|
+
if (!reserved.ok) {
|
|
214
|
+
return Err(toCommerceError(reserved.error ??
|
|
215
|
+
new CommerceValidationError(`Unable to reserve stock for ${line.title ?? line.entityId}.`)));
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
104
219
|
await this.repo.createStatusHistory({
|
|
105
220
|
orderId: order.id,
|
|
106
221
|
fromStatus: "pending",
|
|
@@ -270,6 +385,12 @@ export class OrderService {
|
|
|
270
385
|
await runBeforeHooks(beforeHooks, statusHookInput, "statusChange", hookCtx);
|
|
271
386
|
const previous = order.status;
|
|
272
387
|
const lineItems = await this.repo.findLineItemsByOrderId(order.id, ctx);
|
|
388
|
+
// R-04: goods must not ship against refunded lines. Reject the fulfilled
|
|
389
|
+
// transition if any line has been (partially) refunded.
|
|
390
|
+
if (input.newStatus === "fulfilled" &&
|
|
391
|
+
lineItems.some((li) => li.refundedQuantity > 0)) {
|
|
392
|
+
return Err(new CommerceValidationError("Cannot fulfill an order that has refunded line items."));
|
|
393
|
+
}
|
|
273
394
|
// VAPT r2 (codex) finding: side effects (inventory release, payment
|
|
274
395
|
// refund, tax void) used to run BEFORE the atomic compare-and-swap on
|
|
275
396
|
// updateStatus. Two parallel cancel/refund requests on the same order
|
|
@@ -294,7 +415,7 @@ export class OrderService {
|
|
|
294
415
|
// Fulfilled items had their reservation released during the
|
|
295
416
|
// fulfilled transition — releasing again would double-release.
|
|
296
417
|
if (lineItem.fulfillmentStatus === "unfulfilled") {
|
|
297
|
-
await inventory.release({
|
|
418
|
+
const releaseResult = await inventory.release({
|
|
298
419
|
entityId: lineItem.entityId,
|
|
299
420
|
quantity: lineItem.quantity,
|
|
300
421
|
orderId: order.id,
|
|
@@ -302,7 +423,15 @@ export class OrderService {
|
|
|
302
423
|
...(lineItem.variantId != null
|
|
303
424
|
? { variantId: lineItem.variantId }
|
|
304
425
|
: {}),
|
|
305
|
-
});
|
|
426
|
+
}, actor, ctx);
|
|
427
|
+
if (!releaseResult.ok) {
|
|
428
|
+
const err = toCommerceError(releaseResult.error);
|
|
429
|
+
// Tolerate a missing inventory record (nothing to release); surface
|
|
430
|
+
// any other inventory error. Check the typed code, not the message.
|
|
431
|
+
if (err.code !== "INVENTORY_RECORD_NOT_FOUND") {
|
|
432
|
+
return Err(err);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
306
435
|
}
|
|
307
436
|
}
|
|
308
437
|
}
|
|
@@ -312,11 +441,19 @@ export class OrderService {
|
|
|
312
441
|
if (paymentIntentId) {
|
|
313
442
|
const payments = this.deps.services.payments;
|
|
314
443
|
if (payments?.refund) {
|
|
315
|
-
|
|
444
|
+
// R-01: subtract prior gateway refunds (line-level refundLines, and
|
|
445
|
+
// undone refunds — the gateway was never reversed) so this order-level
|
|
446
|
+
// refund can't double-pay on top of what has already been refunded.
|
|
447
|
+
const priorRefunds = await this.repo.findRefundsByOrderId(order.id, ctx);
|
|
448
|
+
const alreadyRefunded = priorRefunds.reduce((sum, r) => sum + r.amount, 0);
|
|
449
|
+
const maxRefund = Math.max(0, Math.min(order.grandTotal, order.amountCaptured ?? order.grandTotal) -
|
|
450
|
+
alreadyRefunded);
|
|
316
451
|
const refundAmount = input.refundAmount != null
|
|
317
452
|
? Math.min(input.refundAmount, maxRefund)
|
|
318
453
|
: maxRefund;
|
|
319
|
-
|
|
454
|
+
if (refundAmount > 0) {
|
|
455
|
+
await payments.refund(paymentIntentId, refundAmount, input.reason ?? `order_${input.newStatus}`);
|
|
456
|
+
}
|
|
320
457
|
}
|
|
321
458
|
}
|
|
322
459
|
// 3. Void tax transaction
|
|
@@ -349,7 +486,7 @@ export class OrderService {
|
|
|
349
486
|
: {}),
|
|
350
487
|
});
|
|
351
488
|
// Release reservation (no longer needed)
|
|
352
|
-
await inventory.release({
|
|
489
|
+
const releaseResult = await inventory.release({
|
|
353
490
|
entityId: lineItem.entityId,
|
|
354
491
|
quantity: lineItem.quantity,
|
|
355
492
|
orderId: order.id,
|
|
@@ -357,7 +494,15 @@ export class OrderService {
|
|
|
357
494
|
...(lineItem.variantId != null
|
|
358
495
|
? { variantId: lineItem.variantId }
|
|
359
496
|
: {}),
|
|
360
|
-
});
|
|
497
|
+
}, actor, ctx);
|
|
498
|
+
if (!releaseResult.ok) {
|
|
499
|
+
const err = toCommerceError(releaseResult.error);
|
|
500
|
+
// Tolerate a missing inventory record (nothing to release); surface
|
|
501
|
+
// any other inventory error. Check the typed code, not the message.
|
|
502
|
+
if (err.code !== "INVENTORY_RECORD_NOT_FOUND") {
|
|
503
|
+
return Err(err);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
361
506
|
// Mark line item as fulfilled
|
|
362
507
|
await this.repo.updateLineItem(lineItem.id, { fulfillmentStatus: "fulfilled" }, ctx);
|
|
363
508
|
}
|
|
@@ -423,6 +568,18 @@ export class OrderService {
|
|
|
423
568
|
if (input.lines.length === 0) {
|
|
424
569
|
return Err(new CommerceValidationError("At least one line is required."));
|
|
425
570
|
}
|
|
571
|
+
// R-02: a terminal order (cancelled / fully refunded) cannot be line-refunded.
|
|
572
|
+
if (this.machine.terminal.includes(found.order.status)) {
|
|
573
|
+
return Err(new CommerceValidationError(`Cannot refund an order in terminal status "${found.order.status}".`));
|
|
574
|
+
}
|
|
575
|
+
// R-03: a refund moves collected money — reject an unpaid order (otherwise a
|
|
576
|
+
// "refund" is recorded against funds never taken). "Paid" is either a core
|
|
577
|
+
// captured payment (checkout: amountCaptured > 0) or the order having left
|
|
578
|
+
// the initial pending state (e.g. a completed POS/in-store sale). Only a
|
|
579
|
+
// still-pending, uncaptured order is treated as unpaid.
|
|
580
|
+
if ((found.order.amountCaptured ?? 0) <= 0 && found.order.status === "pending") {
|
|
581
|
+
return Err(new CommerceValidationError("Cannot refund an unpaid order (no captured payment and still pending)."));
|
|
582
|
+
}
|
|
426
583
|
const byId = new Map(found.lineItems.map((li) => [li.id, li]));
|
|
427
584
|
const refundLines = [];
|
|
428
585
|
for (const line of input.lines) {
|
|
@@ -456,11 +613,16 @@ export class OrderService {
|
|
|
456
613
|
const payments = this.deps.services.payments;
|
|
457
614
|
if (payments?.refund) {
|
|
458
615
|
const priorRefunds = await this.repo.findRefundsByOrderId(orderId, ctx);
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
616
|
+
// Gross total incl. UNDONE refunds: undoRefund reverses the local ledger
|
|
617
|
+
// but not the payment gateway, so that money already left. Counting only
|
|
618
|
+
// "completed" would let refund → undo → refund re-issue a gateway refund
|
|
619
|
+
// (F-04 / R-05 / R-07). Capping on gross keeps total payout ≤ captured.
|
|
620
|
+
const alreadyRefunded = priorRefunds.reduce((sum, r) => sum + r.amount, 0);
|
|
462
621
|
const refundable = Math.max(0, (found.order.amountCaptured ?? 0) - alreadyRefunded);
|
|
463
|
-
|
|
622
|
+
const payAmount = Math.min(totalAmount, refundable);
|
|
623
|
+
if (payAmount > 0) {
|
|
624
|
+
await payments.refund(found.order.paymentIntentId, payAmount, input.reason ?? "line_refund");
|
|
625
|
+
}
|
|
464
626
|
}
|
|
465
627
|
}
|
|
466
628
|
for (const line of refundLines) {
|
|
@@ -718,6 +880,12 @@ export class OrderService {
|
|
|
718
880
|
order.amountCaptured >= order.grandTotal) {
|
|
719
881
|
return new CommerceValidationError("Order payment is fully captured; line items cannot be edited.");
|
|
720
882
|
}
|
|
883
|
+
// R-06: a checkout order carries a paymentIntentId. If amountCaptured is null
|
|
884
|
+
// (auth-only capture, or a mock adapter reporting 0), treat it as authorized —
|
|
885
|
+
// an order with a live payment must not have its lines silently edited/inflated.
|
|
886
|
+
if (order.paymentIntentId != null && order.amountCaptured == null) {
|
|
887
|
+
return new CommerceValidationError("Order has an authorized payment; line items cannot be edited.");
|
|
888
|
+
}
|
|
721
889
|
return null;
|
|
722
890
|
}
|
|
723
891
|
/**
|
|
@@ -754,21 +922,53 @@ export class OrderService {
|
|
|
754
922
|
const guard = this.lineItemEditGuard(order);
|
|
755
923
|
if (guard)
|
|
756
924
|
return Err(guard);
|
|
925
|
+
// Org-integrity + price provenance for the added line (same rules as create):
|
|
926
|
+
// the entity must belong to the order's org, and the price is server-derived
|
|
927
|
+
// unless the actor holds `orders:manage` (then honored + flagged isCustomPrice).
|
|
928
|
+
const resolvedResult = await this.resolveLines({
|
|
929
|
+
currency: order.currency,
|
|
930
|
+
subtotal: 0,
|
|
931
|
+
taxTotal: 0,
|
|
932
|
+
shippingTotal: 0,
|
|
933
|
+
grandTotal: 0,
|
|
934
|
+
lineItems: [
|
|
935
|
+
{
|
|
936
|
+
entityId: input.entityId,
|
|
937
|
+
entityType: input.entityType,
|
|
938
|
+
title: input.title,
|
|
939
|
+
quantity: input.quantity,
|
|
940
|
+
unitPrice: input.unitPrice,
|
|
941
|
+
totalPrice: input.totalPrice ?? input.unitPrice * input.quantity,
|
|
942
|
+
...(input.variantId !== undefined ? { variantId: input.variantId } : {}),
|
|
943
|
+
...(input.sku !== undefined ? { sku: input.sku } : {}),
|
|
944
|
+
...(input.taxAmount !== undefined ? { taxAmount: input.taxAmount } : {}),
|
|
945
|
+
...(input.discountAmount !== undefined
|
|
946
|
+
? { discountAmount: input.discountAmount }
|
|
947
|
+
: {}),
|
|
948
|
+
...(input.metadata !== undefined ? { metadata: input.metadata } : {}),
|
|
949
|
+
},
|
|
950
|
+
],
|
|
951
|
+
...(order.customerId ? { customerId: order.customerId } : {}),
|
|
952
|
+
}, actor, false, ctx);
|
|
953
|
+
if (!resolvedResult.ok)
|
|
954
|
+
return Err(resolvedResult.error);
|
|
955
|
+
const line = resolvedResult.value[0];
|
|
757
956
|
await this.repo.createLineItems([
|
|
758
957
|
{
|
|
759
958
|
orderId: order.id,
|
|
760
|
-
entityId:
|
|
761
|
-
entityType:
|
|
762
|
-
title:
|
|
763
|
-
quantity:
|
|
764
|
-
unitPrice:
|
|
765
|
-
totalPrice:
|
|
766
|
-
|
|
767
|
-
|
|
959
|
+
entityId: line.entityId,
|
|
960
|
+
entityType: line.entityType,
|
|
961
|
+
title: line.title,
|
|
962
|
+
quantity: line.quantity,
|
|
963
|
+
unitPrice: line.unitPrice,
|
|
964
|
+
totalPrice: line.totalPrice ?? line.unitPrice * line.quantity,
|
|
965
|
+
isCustomPrice: line.isCustomPrice,
|
|
966
|
+
taxAmount: line.taxAmount ?? 0,
|
|
967
|
+
discountAmount: line.discountAmount ?? 0,
|
|
768
968
|
fulfillmentStatus: "unfulfilled",
|
|
769
|
-
metadata:
|
|
770
|
-
...(
|
|
771
|
-
...(
|
|
969
|
+
metadata: line.metadata ?? {},
|
|
970
|
+
...(line.variantId !== undefined ? { variantId: line.variantId } : {}),
|
|
971
|
+
...(line.sku !== undefined ? { sku: line.sku } : {}),
|
|
772
972
|
},
|
|
773
973
|
], ctx);
|
|
774
974
|
return Ok(await this.recalcOrderTotals(order, actor, "line_item_added", ctx));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stale-order-cleanup.d.ts","sourceRoot":"","sources":["../../../src/modules/orders/stale-order-cleanup.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"stale-order-cleanup.d.ts","sourceRoot":"","sources":["../../../src/modules/orders/stale-order-cleanup.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAejE;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,qBAAqB,EAAE,cAAc,CAChD;IAAE,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,EAC3B;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CA6D/C,CAAC"}
|
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import { eq, and, lt } from "drizzle-orm";
|
|
2
2
|
import { orders } from "./schema.js";
|
|
3
|
+
function systemActorForOrg(organizationId) {
|
|
4
|
+
return {
|
|
5
|
+
type: "user",
|
|
6
|
+
userId: "system",
|
|
7
|
+
email: null,
|
|
8
|
+
name: "System",
|
|
9
|
+
vendorId: null,
|
|
10
|
+
organizationId,
|
|
11
|
+
role: "admin",
|
|
12
|
+
permissions: ["*:*"],
|
|
13
|
+
};
|
|
14
|
+
}
|
|
3
15
|
/**
|
|
4
16
|
* Stale Order Cleanup Task
|
|
5
17
|
*
|
|
@@ -24,25 +36,34 @@ export const staleOrderCleanupTask = {
|
|
|
24
36
|
async handler({ input, ctx }) {
|
|
25
37
|
const thresholdHours = input.thresholdHours ?? 48;
|
|
26
38
|
const cutoff = new Date(Date.now() - thresholdHours * 60 * 60 * 1000);
|
|
27
|
-
// Find stale pending orders
|
|
28
|
-
const staleOrders = await ctx.db
|
|
29
|
-
.select()
|
|
30
|
-
.from(orders)
|
|
31
|
-
.where(and(eq(orders.status, "pending"), lt(orders.placedAt, cutoff)));
|
|
32
39
|
const cancelledIds = [];
|
|
33
40
|
// Cancel each stale order via the service (triggers inventory release + payment refund)
|
|
34
41
|
const orderService = ctx.services.orders;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
// This is a cross-org system sweep, but the row reads stay org-scoped: first
|
|
43
|
+
// enumerate the orgs that have stale orders (id-only projection), then read
|
|
44
|
+
// each org's stale orders under an explicit organizationId predicate. No
|
|
45
|
+
// query returns another tenant's order rows.
|
|
46
|
+
const staleOrgs = await ctx.db
|
|
47
|
+
.selectDistinct({ organizationId: orders.organizationId })
|
|
48
|
+
.from(orders)
|
|
49
|
+
.where(and(eq(orders.status, "pending"), lt(orders.placedAt, cutoff)));
|
|
50
|
+
for (const { organizationId } of staleOrgs) {
|
|
51
|
+
const staleOrders = await ctx.db
|
|
52
|
+
.select()
|
|
53
|
+
.from(orders)
|
|
54
|
+
.where(and(eq(orders.organizationId, organizationId), eq(orders.status, "pending"), lt(orders.placedAt, cutoff)));
|
|
55
|
+
for (const order of staleOrders) {
|
|
56
|
+
try {
|
|
57
|
+
await orderService.cancel(order.id, systemActorForOrg(order.organizationId), `Auto-cancelled: pending for >${thresholdHours}h`);
|
|
58
|
+
cancelledIds.push(order.id);
|
|
59
|
+
ctx.logger.info(`Stale order ${order.orderNumber} auto-cancelled`, {
|
|
60
|
+
orderId: order.id,
|
|
61
|
+
placedAt: order.placedAt,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
ctx.logger.error(`Failed to cancel stale order ${order.orderNumber}`, { error });
|
|
66
|
+
}
|
|
46
67
|
}
|
|
47
68
|
}
|
|
48
69
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/modules/pricing/service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAExE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qCAAqC,CAAC;AAOrE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAEnE,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAE9D,OAAO,KAAK,EACV,iBAAiB,EACjB,KAAK,EACL,aAAa,EAGd,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAGxE,MAAM,MAAM,iBAAiB,GACzB,qBAAqB,GACrB,gBAAgB,GAChB,QAAQ,GACR,UAAU,CAAC;AAEf,UAAU,kBAAkB;IAC1B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,KAAK,EAAE,YAAY,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,QAAQ,EAAE,eAAe,CAAC;CAC3B;AAkBD,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,SAAS,CAAC,EAAE,IAAI,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,KAAK,CAAC;QACtB,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,iBAAiB,CAAC;QACxB,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;IACH,SAAS,EAAE,kBAAkB,EAAE,CAAC;IAChC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,YAAY,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAChF,OAAO,KAAK,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAuGhF,qBAAa,cAAc;IAIb,OAAO,CAAC,IAAI;IAHxB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAoB;IACzC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAoB;gBAE5B,IAAI,EAAE,kBAAkB;IAKtC,YAAY,CAChB,KAAK,EAAE,iBAAiB,EACxB,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EACpB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/modules/pricing/service.ts"],"names":[],"mappings":"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"}
|
|
@@ -103,11 +103,11 @@ export class PricingService {
|
|
|
103
103
|
if (input.amount < 0) {
|
|
104
104
|
return Err(new CommerceValidationError("Base price amount cannot be negative."));
|
|
105
105
|
}
|
|
106
|
-
const
|
|
106
|
+
const orgId = resolveOrgId(actor ?? ctx?.actor ?? null);
|
|
107
|
+
const entity = await this.catalogRepo.findEntityById(input.entityId, ctx, orgId);
|
|
107
108
|
if (!entity) {
|
|
108
109
|
return Err(new CommerceNotFoundError("Entity not found for price assignment."));
|
|
109
110
|
}
|
|
110
|
-
const orgId = resolveOrgId(actor ?? ctx?.actor ?? null);
|
|
111
111
|
const priceData = {
|
|
112
112
|
organizationId: orgId,
|
|
113
113
|
entityId: input.entityId,
|
|
@@ -256,7 +256,8 @@ export class PricingService {
|
|
|
256
256
|
return Ok({ prices, modifiers });
|
|
257
257
|
}
|
|
258
258
|
async resolve(input, actor, ctx) {
|
|
259
|
-
const
|
|
259
|
+
const orgId = resolveOrgId(actor ?? ctx?.actor ?? null);
|
|
260
|
+
const entity = await this.catalogRepo.findEntityById(input.entityId, ctx, orgId);
|
|
260
261
|
if (!entity) {
|
|
261
262
|
return Err(new CommerceNotFoundError(`Entity ${input.entityId} not found.`));
|
|
262
263
|
}
|
|
@@ -266,8 +267,7 @@ export class PricingService {
|
|
|
266
267
|
const timestamp = input.timestamp ?? new Date();
|
|
267
268
|
const currency = normalizeCurrency(input.currency);
|
|
268
269
|
const groupSet = toGroupSet(input);
|
|
269
|
-
// Get matching prices from repository
|
|
270
|
-
const orgId = resolveOrgId(actor ?? ctx?.actor ?? null);
|
|
270
|
+
// Get matching prices from repository (orgId resolved above)
|
|
271
271
|
const allPrices = await this.repo.findPricesByEntityId(orgId, input.entityId, ctx);
|
|
272
272
|
const matchingPrices = allPrices.filter((price) => {
|
|
273
273
|
if (input.variantId !== undefined) {
|
|
@@ -28,7 +28,7 @@ export declare class PromotionsRepository {
|
|
|
28
28
|
findUsageById(id: string, ctx?: TxContext): Promise<PromotionUsage | undefined>;
|
|
29
29
|
findUsagesByPromotionId(promotionId: string, ctx?: TxContext): Promise<PromotionUsage[]>;
|
|
30
30
|
findUsagesByCustomerId(customerId: string, ctx?: TxContext): Promise<PromotionUsage[]>;
|
|
31
|
-
createUsage(data: PromotionUsageInsert, ctx?: TxContext): Promise<PromotionUsage>;
|
|
31
|
+
createUsage(data: PromotionUsageInsert, orgId: string, ctx?: TxContext): Promise<PromotionUsage>;
|
|
32
32
|
countUsages(promotionId: string, ctx?: TxContext): Promise<number>;
|
|
33
33
|
countUsagesByCustomer(promotionId: string, customerId: string, ctx?: TxContext): Promise<number>;
|
|
34
34
|
isUsageLimitReached(orgId: string, promotionId: string, ctx?: TxContext): Promise<boolean>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modules/promotions/repository/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wCAAwC,CAAC;AACxE,OAAO,KAAK,EACV,eAAe,EAEhB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAG3D,MAAM,MAAM,SAAS,GAAG,OAAO,UAAU,CAAC,YAAY,CAAC;AACvD,MAAM,MAAM,eAAe,GAAG,OAAO,UAAU,CAAC,YAAY,CAAC;AAC7D,MAAM,MAAM,cAAc,GAAG,OAAO,eAAe,CAAC,YAAY,CAAC;AACjE,MAAM,MAAM,oBAAoB,GAAG,OAAO,eAAe,CAAC,YAAY,CAAC;AAEvE;;;;;GAKG;AACH,qBAAa,oBAAoB;IACnB,OAAO,CAAC,QAAQ,CAAC,EAAE;gBAAF,EAAE,EAAE,eAAe;IAEhD,OAAO,CAAC,KAAK;IAQP,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IASpF,UAAU,CACd,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAc3B,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAS7D,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAkBhE,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAmBnE,MAAM,CAAC,IAAI,EAAE,eAAe,EAAE,GAAG,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;IAMlE,MAAM,CACV,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC,EAC1C,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAU3B,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC;IASrD,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAIrE,UAAU,CACd,EAAE,EAAE,MAAM,EACV,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAQ3B,aAAa,CACjB,EAAE,EAAE,MAAM,EACV,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;IAShC,uBAAuB,CAC3B,WAAW,EAAE,MAAM,EACnB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,cAAc,EAAE,CAAC;IAQtB,sBAAsB,CAC1B,UAAU,EAAE,MAAM,EAClB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,cAAc,EAAE,CAAC;IAQtB,WAAW,CACf,IAAI,EAAE,oBAAoB,EAC1B,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modules/promotions/repository/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -112,16 +112,19 @@ export class PromotionsRepository {
|
|
|
112
112
|
.from(promotionUsages)
|
|
113
113
|
.where(eq(promotionUsages.customerId, customerId));
|
|
114
114
|
}
|
|
115
|
-
async createUsage(data, ctx) {
|
|
115
|
+
async createUsage(data, orgId, ctx) {
|
|
116
116
|
const db = this.getDb(ctx);
|
|
117
117
|
// Atomic guard: use INSERT ... SELECT WHERE count < limit to prevent
|
|
118
118
|
// race conditions between concurrent checkouts using the same coupon.
|
|
119
119
|
// A plain count-then-insert has a TOCTOU gap; this single statement
|
|
120
120
|
// ensures the insert only succeeds if the limit has not been reached.
|
|
121
|
+
// Both reads are scoped by organizationId: these are raw/lock queries that
|
|
122
|
+
// bypass the scoped-db proxy, so a promotionId from another tenant must
|
|
123
|
+
// resolve to no row rather than reading/locking a foreign promotion.
|
|
121
124
|
const promo = await db
|
|
122
125
|
.select({ usageLimitTotal: promotions.usageLimitTotal })
|
|
123
126
|
.from(promotions)
|
|
124
|
-
.where(eq(promotions.id, data.promotionId));
|
|
127
|
+
.where(and(eq(promotions.id, data.promotionId), eq(promotions.organizationId, orgId)));
|
|
125
128
|
const limit = promo[0]?.usageLimitTotal;
|
|
126
129
|
if (limit != null) {
|
|
127
130
|
// Atomic guard: lock the promotion row and check usage count in the
|
|
@@ -131,7 +134,7 @@ export class PromotionsRepository {
|
|
|
131
134
|
// SELECT ... FOR UPDATE acquires a row-level lock that serializes
|
|
132
135
|
// concurrent callers. If the caller is already inside a transaction
|
|
133
136
|
// (ctx.tx), the lock is held until that transaction commits.
|
|
134
|
-
await db.execute(sql `SELECT id FROM promotions WHERE id = ${data.promotionId} FOR UPDATE`);
|
|
137
|
+
await db.execute(sql `SELECT id FROM promotions WHERE id = ${data.promotionId} AND organization_id = ${orgId} FOR UPDATE`);
|
|
135
138
|
const currentCount = await this.countUsages(data.promotionId, ctx);
|
|
136
139
|
if (currentCount >= limit) {
|
|
137
140
|
throw new Error(`Promotion usage limit reached (${currentCount}/${limit})`);
|
|
@@ -85,6 +85,7 @@ export declare class PromotionService {
|
|
|
85
85
|
applyPromotions(context: PromotionEvaluationContext, ctx?: TxContext): Promise<Result<PromotionApplicationResult>>;
|
|
86
86
|
recordUsage(input: {
|
|
87
87
|
promotions: AppliedPromotion[];
|
|
88
|
+
organizationId: string;
|
|
88
89
|
customerId?: string;
|
|
89
90
|
orderId?: string;
|
|
90
91
|
}, ctx?: TxContext): Promise<Result<PromotionUsage[]>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/modules/promotions/service.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/modules/promotions/service.ts"],"names":[],"mappings":"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"}
|
|
@@ -29,7 +29,7 @@ export declare class WebhooksRepository {
|
|
|
29
29
|
limit?: number;
|
|
30
30
|
}, ctx?: TxContext): Promise<WebhookDelivery[]>;
|
|
31
31
|
findPendingDeliveries(ctx?: TxContext): Promise<WebhookDelivery[]>;
|
|
32
|
-
findFailedDeliveries(endpointId?: string, ctx?: TxContext): Promise<WebhookDelivery[]>;
|
|
32
|
+
findFailedDeliveries(orgId: string, endpointId?: string, ctx?: TxContext): Promise<WebhookDelivery[]>;
|
|
33
33
|
createDelivery(data: WebhookDeliveryInsert, ctx?: TxContext): Promise<WebhookDelivery>;
|
|
34
34
|
updateDelivery(id: string, data: Partial<Omit<WebhookDeliveryInsert, "id">>, ctx?: TxContext): Promise<WebhookDelivery | undefined>;
|
|
35
35
|
markDelivered(id: string, statusCode: number, ctx?: TxContext): Promise<WebhookDelivery | undefined>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modules/webhooks/repository/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wCAAwC,CAAC;AACxE,OAAO,KAAK,EACV,eAAe,EAEhB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAGnE,MAAM,MAAM,eAAe,GAAG,OAAO,gBAAgB,CAAC,YAAY,CAAC;AACnE,MAAM,MAAM,qBAAqB,GAAG,OAAO,gBAAgB,CAAC,YAAY,CAAC;AACzE,MAAM,MAAM,eAAe,GAAG,OAAO,iBAAiB,CAAC,YAAY,CAAC;AACpE,MAAM,MAAM,qBAAqB,GAAG,OAAO,iBAAiB,CAAC,YAAY,CAAC;AAE1E;;;;;GAKG;AACH,qBAAa,kBAAkB;IACjB,OAAO,CAAC,QAAQ,CAAC,EAAE;gBAAF,EAAE,EAAE,eAAe;IAEhD,OAAO,CAAC,KAAK;IAaP,gBAAgB,CACpB,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,EACb,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;IAcjC,gBAAgB,CACpB,KAAK,EAAE,MAAM,EACb,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,eAAe,EAAE,CAAC;IAQvB,mBAAmB,CACvB,KAAK,EAAE,MAAM,EACb,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,eAAe,EAAE,CAAC;IAavB,qBAAqB,CACzB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,eAAe,EAAE,CAAC;IASvB,cAAc,CAClB,IAAI,EAAE,qBAAqB,EAC3B,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,eAAe,CAAC;IAMrB,cAAc,CAClB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,qBAAqB,EAAE,IAAI,CAAC,CAAC,EAChD,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;IAUjC,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC;IAS7D,gBAAgB,CACpB,EAAE,EAAE,MAAM,EACV,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;IAIjC,kBAAkB,CACtB,EAAE,EAAE,MAAM,EACV,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;IAQjC,gBAAgB,CACpB,EAAE,EAAE,MAAM,EACV,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;IASjC,0BAA0B,CAC9B,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,EAC5B,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,eAAe,EAAE,CAAC;IAgBvB,qBAAqB,CAAC,GAAG,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAoBlE,oBAAoB,CACxB,UAAU,CAAC,EAAE,MAAM,EACnB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,eAAe,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modules/webhooks/repository/index.ts"],"names":[],"mappings":"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"}
|