@porulle/core 0.1.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/middleware.d.ts.map +1 -1
- package/dist/auth/middleware.js +17 -1
- package/dist/config/define-config.d.ts.map +1 -1
- package/dist/config/define-config.js +6 -0
- package/dist/config/types.d.ts +12 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/hooks/checkout.d.ts +3 -3
- package/dist/hooks/checkout.js +4 -4
- package/dist/index.d.ts +9 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/interfaces/rest/audit-middleware.d.ts +46 -0
- package/dist/interfaces/rest/audit-middleware.d.ts.map +1 -0
- package/dist/interfaces/rest/audit-middleware.js +82 -0
- package/dist/interfaces/rest/parse-json.d.ts +39 -0
- package/dist/interfaces/rest/parse-json.d.ts.map +1 -0
- package/dist/interfaces/rest/parse-json.js +45 -0
- package/dist/interfaces/rest/routes/catalog.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/catalog.js +17 -2
- package/dist/interfaces/rest/routes/customers.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/customers.js +59 -4
- package/dist/interfaces/rest/routes/inventory.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/inventory.js +4 -2
- package/dist/interfaces/rest/routes/media.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/media.js +6 -0
- package/dist/interfaces/rest/routes/orders.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/orders.js +23 -1
- package/dist/interfaces/rest/routes/promotions.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/promotions.js +21 -1
- package/dist/interfaces/rest/schemas/carts.d.ts +175 -0
- package/dist/interfaces/rest/schemas/carts.d.ts.map +1 -1
- package/dist/interfaces/rest/schemas/catalog.d.ts +1410 -0
- package/dist/interfaces/rest/schemas/catalog.d.ts.map +1 -1
- package/dist/interfaces/rest/schemas/catalog.js +28 -1
- package/dist/interfaces/rest/schemas/checkout.d.ts +35 -0
- package/dist/interfaces/rest/schemas/checkout.d.ts.map +1 -1
- package/dist/interfaces/rest/schemas/customer-portal.d.ts +280 -0
- package/dist/interfaces/rest/schemas/customer-portal.d.ts.map +1 -1
- package/dist/interfaces/rest/schemas/customers.d.ts +863 -0
- package/dist/interfaces/rest/schemas/customers.d.ts.map +1 -1
- package/dist/interfaces/rest/schemas/customers.js +88 -0
- package/dist/interfaces/rest/schemas/inventory.d.ts +182 -1
- package/dist/interfaces/rest/schemas/inventory.d.ts.map +1 -1
- package/dist/interfaces/rest/schemas/media.d.ts +105 -0
- package/dist/interfaces/rest/schemas/media.d.ts.map +1 -1
- package/dist/interfaces/rest/schemas/orders.d.ts +136 -0
- package/dist/interfaces/rest/schemas/orders.d.ts.map +1 -1
- package/dist/interfaces/rest/schemas/orders.js +20 -0
- package/dist/interfaces/rest/schemas/pricing.d.ts +70 -0
- package/dist/interfaces/rest/schemas/pricing.d.ts.map +1 -1
- package/dist/interfaces/rest/schemas/promotions.d.ts +298 -0
- package/dist/interfaces/rest/schemas/promotions.d.ts.map +1 -1
- package/dist/interfaces/rest/schemas/promotions.js +25 -1
- package/dist/interfaces/rest/schemas/shared.d.ts +47 -0
- package/dist/interfaces/rest/schemas/shared.d.ts.map +1 -1
- package/dist/interfaces/rest/schemas/shared.js +8 -0
- package/dist/interfaces/rest/schemas/webhooks.d.ts +70 -0
- package/dist/interfaces/rest/schemas/webhooks.d.ts.map +1 -1
- package/dist/kernel/database/adapter.d.ts +6 -0
- package/dist/kernel/database/adapter.d.ts.map +1 -1
- package/dist/kernel/database/adapter.js +62 -1
- package/dist/kernel/database/migrate.d.ts +22 -2
- package/dist/kernel/database/migrate.d.ts.map +1 -1
- package/dist/kernel/database/migrate.js +42 -2
- package/dist/modules/analytics/hooks.d.ts +5 -5
- package/dist/modules/analytics/hooks.js +5 -5
- package/dist/modules/analytics/repository/index.d.ts.map +1 -1
- package/dist/modules/catalog/category-service.d.ts +6 -1
- package/dist/modules/catalog/category-service.d.ts.map +1 -1
- package/dist/modules/catalog/category-service.js +35 -5
- package/dist/modules/catalog/schema.d.ts +17 -0
- package/dist/modules/catalog/schema.d.ts.map +1 -1
- package/dist/modules/catalog/schema.js +2 -0
- package/dist/modules/catalog/service.d.ts +11 -2
- package/dist/modules/catalog/service.d.ts.map +1 -1
- package/dist/modules/catalog/service.js +8 -2
- package/dist/modules/customers/repository/index.d.ts +8 -1
- package/dist/modules/customers/repository/index.d.ts.map +1 -1
- package/dist/modules/customers/repository/index.js +42 -2
- package/dist/modules/customers/schema.d.ts +162 -0
- package/dist/modules/customers/schema.d.ts.map +1 -1
- package/dist/modules/customers/schema.js +18 -0
- package/dist/modules/customers/service.d.ts +32 -2
- package/dist/modules/customers/service.d.ts.map +1 -1
- package/dist/modules/customers/service.js +119 -3
- package/dist/modules/inventory/schemas.d.ts +7 -1
- package/dist/modules/inventory/schemas.d.ts.map +1 -1
- package/dist/modules/inventory/schemas.js +6 -1
- package/dist/modules/inventory/service.d.ts +18 -0
- package/dist/modules/inventory/service.d.ts.map +1 -1
- package/dist/modules/inventory/service.js +58 -21
- package/dist/modules/media/noop-adapter.d.ts +11 -0
- package/dist/modules/media/noop-adapter.d.ts.map +1 -0
- package/dist/modules/media/noop-adapter.js +31 -0
- package/dist/modules/orders/repository/index.d.ts +19 -0
- package/dist/modules/orders/repository/index.d.ts.map +1 -1
- package/dist/modules/orders/repository/index.js +42 -1
- package/dist/modules/orders/service.d.ts +31 -0
- package/dist/modules/orders/service.d.ts.map +1 -1
- package/dist/modules/orders/service.js +52 -2
- package/dist/modules/payments/repository/index.d.ts.map +1 -1
- package/dist/modules/promotions/schemas.d.ts +46 -0
- package/dist/modules/promotions/schemas.d.ts.map +1 -1
- package/dist/modules/promotions/schemas.js +19 -8
- package/dist/modules/promotions/service.d.ts +8 -2
- package/dist/modules/promotions/service.d.ts.map +1 -1
- package/dist/modules/promotions/service.js +74 -9
- package/dist/modules/search/repository/index.d.ts.map +1 -1
- package/dist/modules/shipping/repository/index.d.ts.map +1 -1
- package/dist/modules/tax/repository/index.d.ts.map +1 -1
- package/dist/runtime/client-ip.d.ts +31 -0
- package/dist/runtime/client-ip.d.ts.map +1 -0
- package/dist/runtime/client-ip.js +31 -0
- package/dist/runtime/server.d.ts +4 -0
- package/dist/runtime/server.d.ts.map +1 -1
- package/dist/runtime/server.js +35 -19
- package/dist/test-utils/create-plugin-test-app.d.ts.map +1 -1
- package/dist/test-utils/create-plugin-test-app.js +4 -1
- package/dist/test-utils/test-actors.d.ts +3 -2
- package/dist/test-utils/test-actors.d.ts.map +1 -1
- package/dist/test-utils/test-actors.js +3 -2
- package/package.json +4 -4
- package/src/auth/middleware.ts +18 -1
- package/src/config/define-config.ts +7 -0
- package/src/config/types.ts +12 -0
- package/src/hooks/checkout.ts +4 -4
- package/src/index.ts +18 -1
- package/src/interfaces/rest/audit-middleware.ts +110 -0
- package/src/interfaces/rest/parse-json.ts +81 -0
- package/src/interfaces/rest/routes/catalog.ts +21 -1
- package/src/interfaces/rest/routes/customers.ts +64 -3
- package/src/interfaces/rest/routes/inventory.ts +4 -2
- package/src/interfaces/rest/routes/media.ts +7 -0
- package/src/interfaces/rest/routes/orders.ts +21 -1
- package/src/interfaces/rest/routes/promotions.ts +22 -1
- package/src/interfaces/rest/schemas/catalog.ts +30 -1
- package/src/interfaces/rest/schemas/customers.ts +99 -0
- package/src/interfaces/rest/schemas/orders.ts +21 -0
- package/src/interfaces/rest/schemas/promotions.ts +32 -1
- package/src/interfaces/rest/schemas/shared.ts +9 -0
- package/src/kernel/database/adapter.ts +64 -1
- package/src/kernel/database/migrate.ts +50 -2
- package/src/modules/analytics/hooks.ts +5 -5
- package/src/modules/analytics/repository/index.ts +0 -1
- package/src/modules/catalog/category-service.ts +26 -5
- package/src/modules/catalog/schema.ts +2 -0
- package/src/modules/catalog/service.ts +25 -3
- package/src/modules/customers/repository/index.ts +55 -1
- package/src/modules/customers/schema.ts +19 -0
- package/src/modules/customers/service.ts +137 -2
- package/src/modules/inventory/schemas.ts +6 -1
- package/src/modules/inventory/service.ts +84 -29
- package/src/modules/media/noop-adapter.ts +36 -0
- package/src/modules/orders/repository/index.ts +63 -1
- package/src/modules/orders/service.ts +86 -3
- package/src/modules/payments/repository/index.ts +0 -1
- package/src/modules/promotions/schemas.ts +21 -4
- package/src/modules/promotions/service.ts +90 -18
- package/src/modules/search/repository/index.ts +0 -1
- package/src/modules/shipping/repository/index.ts +0 -1
- package/src/modules/tax/repository/index.ts +0 -1
- package/src/runtime/client-ip.ts +49 -0
- package/src/runtime/server.ts +39 -18
- package/src/test-utils/create-plugin-test-app.ts +3 -1
- package/src/test-utils/test-actors.ts +3 -2
|
@@ -146,23 +146,58 @@ export class InventoryService {
|
|
|
146
146
|
return doRelease(txCtx);
|
|
147
147
|
});
|
|
148
148
|
}
|
|
149
|
-
|
|
149
|
+
/**
|
|
150
|
+
* Adjust inventory with optional add/remove/set modes, returning before /
|
|
151
|
+
* after / delta and the movement id. `mode` omitted ⇒ signed-delta (legacy)
|
|
152
|
+
* behavior using `adjustment`. `remove` clamps at 0; `set` writes an absolute
|
|
153
|
+
* value. The lock, compute, write, and movement all happen in one transaction
|
|
154
|
+
* so concurrent adjustments can't lose updates.
|
|
155
|
+
*/
|
|
156
|
+
async adjustDetailed(input, actor, ctx) {
|
|
150
157
|
try {
|
|
151
158
|
assertPermission(actor ?? null, "inventory:adjust");
|
|
152
159
|
}
|
|
153
160
|
catch (error) {
|
|
154
161
|
return Err(toCommerceError(error));
|
|
155
162
|
}
|
|
163
|
+
// Cross-field validation: mode form needs `amount`; legacy form needs `adjustment`.
|
|
164
|
+
if (input.mode) {
|
|
165
|
+
if (input.amount === undefined) {
|
|
166
|
+
return Err(new CommerceValidationError("`amount` is required when `mode` is set."));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
else if (input.adjustment === undefined) {
|
|
170
|
+
return Err(new CommerceValidationError("`adjustment` is required when `mode` is omitted."));
|
|
171
|
+
}
|
|
156
172
|
const warehouseId = input.warehouseId ?? (await this.pickWarehouse(actor, ctx));
|
|
157
173
|
const variantId = input.variantId ?? null;
|
|
158
174
|
const performedBy = input.performedBy ?? actor?.userId ?? "system";
|
|
159
175
|
const doAdjust = async (txCtx) => {
|
|
160
176
|
const orgId = resolveOrgId(actor ?? txCtx.actor ?? null, undefined, this.deps.config);
|
|
161
|
-
//
|
|
162
|
-
const
|
|
177
|
+
// Lock the level first so `before` and the write are atomic.
|
|
178
|
+
const existing = await this.repo.findLevelForUpdate(orgId, input.entityId, variantId, warehouseId, txCtx);
|
|
179
|
+
const before = existing?.quantityOnHand ?? 0;
|
|
180
|
+
const amount = input.amount ?? 0;
|
|
181
|
+
let effectiveAdjustment;
|
|
182
|
+
switch (input.mode) {
|
|
183
|
+
case "add":
|
|
184
|
+
effectiveAdjustment = amount;
|
|
185
|
+
break;
|
|
186
|
+
case "remove":
|
|
187
|
+
effectiveAdjustment = -amount;
|
|
188
|
+
break;
|
|
189
|
+
case "set":
|
|
190
|
+
effectiveAdjustment = amount - before;
|
|
191
|
+
break;
|
|
192
|
+
default:
|
|
193
|
+
effectiveAdjustment = input.adjustment ?? 0;
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
163
196
|
let level;
|
|
164
|
-
if (
|
|
165
|
-
|
|
197
|
+
if (existing) {
|
|
198
|
+
// Row-locked atomic SQL increment (GREATEST(0, qoh + delta)).
|
|
199
|
+
const lockResult = await this.repo.adjustWithLock(orgId, input.entityId, variantId, warehouseId, effectiveAdjustment, txCtx);
|
|
200
|
+
level = lockResult.ok ? lockResult.level : existing;
|
|
166
201
|
}
|
|
167
202
|
else {
|
|
168
203
|
// No existing level — create is safe; unique index on
|
|
@@ -171,31 +206,25 @@ export class InventoryService {
|
|
|
171
206
|
organizationId: orgId,
|
|
172
207
|
entityId: input.entityId,
|
|
173
208
|
warehouseId,
|
|
174
|
-
quantityOnHand: Math.max(0,
|
|
209
|
+
quantityOnHand: Math.max(0, effectiveAdjustment),
|
|
175
210
|
quantityReserved: 0,
|
|
176
211
|
quantityIncoming: 0,
|
|
177
|
-
...(input.variantId !== undefined
|
|
178
|
-
? { variantId: input.variantId }
|
|
179
|
-
: {}),
|
|
212
|
+
...(input.variantId !== undefined ? { variantId: input.variantId } : {}),
|
|
180
213
|
}, txCtx);
|
|
181
214
|
}
|
|
182
|
-
|
|
215
|
+
const after = level.quantityOnHand;
|
|
216
|
+
const delta = after - before;
|
|
217
|
+
const movement = await this.repo.createMovement({
|
|
183
218
|
organizationId: orgId,
|
|
184
219
|
entityId: input.entityId,
|
|
185
220
|
warehouseId,
|
|
186
221
|
type: "adjustment",
|
|
187
|
-
quantity:
|
|
222
|
+
quantity: delta,
|
|
188
223
|
reason: input.reason,
|
|
189
224
|
performedBy,
|
|
190
|
-
...(input.variantId !== undefined
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
...(input.referenceType !== undefined
|
|
194
|
-
? { referenceType: input.referenceType }
|
|
195
|
-
: {}),
|
|
196
|
-
...(input.referenceId !== undefined
|
|
197
|
-
? { referenceId: input.referenceId }
|
|
198
|
-
: {}),
|
|
225
|
+
...(input.variantId !== undefined ? { variantId: input.variantId } : {}),
|
|
226
|
+
...(input.referenceType !== undefined ? { referenceType: input.referenceType } : {}),
|
|
227
|
+
...(input.referenceId !== undefined ? { referenceId: input.referenceId } : {}),
|
|
199
228
|
}, txCtx);
|
|
200
229
|
const hookCtx = createHookContext({
|
|
201
230
|
actor: actor ?? null,
|
|
@@ -207,13 +236,21 @@ export class InventoryService {
|
|
|
207
236
|
});
|
|
208
237
|
const afterHooks = this.deps.hooks.resolve("inventory.afterAdjust");
|
|
209
238
|
await runAfterHooks(afterHooks, null, level, "update", hookCtx);
|
|
210
|
-
return Ok(level);
|
|
239
|
+
return Ok({ level, before, after, delta, movementId: movement.id });
|
|
211
240
|
};
|
|
212
241
|
return this.withTransaction(ctx, async (tx) => {
|
|
213
242
|
const txCtx = ctx?.tx ? ctx : createTxContext(tx, { actor: actor ?? null });
|
|
214
243
|
return doAdjust(txCtx);
|
|
215
244
|
});
|
|
216
245
|
}
|
|
246
|
+
/**
|
|
247
|
+
* Back-compat wrapper: adjust inventory and return just the level. Existing
|
|
248
|
+
* callers and the signed-delta `adjustment` form are unchanged.
|
|
249
|
+
*/
|
|
250
|
+
async adjust(input, actor, ctx) {
|
|
251
|
+
const result = await this.adjustDetailed(input, actor, ctx);
|
|
252
|
+
return result.ok ? Ok(result.value.level) : result;
|
|
253
|
+
}
|
|
217
254
|
/**
|
|
218
255
|
* Set inventory to an absolute quantity (not a delta).
|
|
219
256
|
*
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { StorageAdapter } from "./adapter.js";
|
|
2
|
+
/**
|
|
3
|
+
* The default `StorageAdapter` used when `defineConfig` is given no `storage`.
|
|
4
|
+
*
|
|
5
|
+
* Lets a catalog-only deployment boot with zero storage config: read-only
|
|
6
|
+
* `getUrl` passes the key through (so catalog projections keep working), while
|
|
7
|
+
* the mutating media operations return a typed `STORAGE_NOT_SUPPORTED` error.
|
|
8
|
+
* Configure a real adapter (local/S3/R2) to enable media.
|
|
9
|
+
*/
|
|
10
|
+
export declare const noopStorageAdapter: StorageAdapter;
|
|
11
|
+
//# sourceMappingURL=noop-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"noop-adapter.d.ts","sourceRoot":"","sources":["../../../src/modules/media/noop-adapter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAQnD;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,EAAE,cAiBhC,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Ok, Err } from "../../kernel/result.js";
|
|
2
|
+
const notSupported = (op) => Err({
|
|
3
|
+
code: "STORAGE_NOT_SUPPORTED",
|
|
4
|
+
message: `storage.${op} is disabled — no storage adapter is configured.`,
|
|
5
|
+
});
|
|
6
|
+
/**
|
|
7
|
+
* The default `StorageAdapter` used when `defineConfig` is given no `storage`.
|
|
8
|
+
*
|
|
9
|
+
* Lets a catalog-only deployment boot with zero storage config: read-only
|
|
10
|
+
* `getUrl` passes the key through (so catalog projections keep working), while
|
|
11
|
+
* the mutating media operations return a typed `STORAGE_NOT_SUPPORTED` error.
|
|
12
|
+
* Configure a real adapter (local/S3/R2) to enable media.
|
|
13
|
+
*/
|
|
14
|
+
export const noopStorageAdapter = {
|
|
15
|
+
providerId: "noop",
|
|
16
|
+
async upload() {
|
|
17
|
+
return notSupported("upload");
|
|
18
|
+
},
|
|
19
|
+
async getUrl(key) {
|
|
20
|
+
return Ok(key);
|
|
21
|
+
},
|
|
22
|
+
async getSignedUrl() {
|
|
23
|
+
return notSupported("getSignedUrl");
|
|
24
|
+
},
|
|
25
|
+
async delete() {
|
|
26
|
+
return Ok(undefined);
|
|
27
|
+
},
|
|
28
|
+
async list() {
|
|
29
|
+
return Ok([]);
|
|
30
|
+
},
|
|
31
|
+
};
|
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import type { TxContext } from "../../../kernel/database/tx-context.js";
|
|
2
2
|
import type { DrizzleDatabase } from "../../../kernel/database/drizzle-db.js";
|
|
3
3
|
import { orders, orderLineItems, orderStatusHistory } from "../schema.js";
|
|
4
|
+
export interface OrderLookupRow {
|
|
5
|
+
id: string;
|
|
6
|
+
orderNumber: string;
|
|
7
|
+
placedAt: Date;
|
|
8
|
+
status: string;
|
|
9
|
+
grandTotal: number;
|
|
10
|
+
customerId: string | null;
|
|
11
|
+
firstName: string | null;
|
|
12
|
+
lastName: string | null;
|
|
13
|
+
phone: string | null;
|
|
14
|
+
}
|
|
4
15
|
export type Order = typeof orders.$inferSelect;
|
|
5
16
|
export type OrderInsert = typeof orders.$inferInsert;
|
|
6
17
|
export type OrderLineItem = typeof orderLineItems.$inferSelect;
|
|
@@ -20,6 +31,14 @@ export declare class OrdersRepository {
|
|
|
20
31
|
findById(orgId: string, id: string, ctx?: TxContext): Promise<Order | undefined>;
|
|
21
32
|
findByOrderNumber(orgId: string, orderNumber: string, ctx?: TxContext): Promise<Order | undefined>;
|
|
22
33
|
findByCustomerId(orgId: string, customerId: string, ctx?: TxContext): Promise<Order[]>;
|
|
34
|
+
/**
|
|
35
|
+
* Fuzzy lookup across order number, customer email/name/phone, and the
|
|
36
|
+
* walk-in label. Phone matches ignore non-digits on both sides. Org-scoped.
|
|
37
|
+
*/
|
|
38
|
+
lookup(orgId: string, q: string, opts: {
|
|
39
|
+
from?: Date;
|
|
40
|
+
to?: Date;
|
|
41
|
+
}, ctx?: TxContext): Promise<OrderLookupRow[]>;
|
|
23
42
|
findByStatus(orgId: string, status: string, ctx?: TxContext): Promise<Order[]>;
|
|
24
43
|
findAll(orgId: string, options?: {
|
|
25
44
|
limit?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modules/orders/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,MAAM,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAG1E,MAAM,MAAM,KAAK,GAAG,OAAO,MAAM,CAAC,YAAY,CAAC;AAC/C,MAAM,MAAM,WAAW,GAAG,OAAO,MAAM,CAAC,YAAY,CAAC;AACrD,MAAM,MAAM,aAAa,GAAG,OAAO,cAAc,CAAC,YAAY,CAAC;AAC/D,MAAM,MAAM,mBAAmB,GAAG,OAAO,cAAc,CAAC,YAAY,CAAC;AACrE,MAAM,MAAM,kBAAkB,GAAG,OAAO,kBAAkB,CAAC,YAAY,CAAC;AACxE,MAAM,MAAM,wBAAwB,GAAG,OAAO,kBAAkB,CAAC,YAAY,CAAC;AAE9E;;;;;GAKG;AACH,qBAAa,gBAAgB;IACf,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,KAAK,GAAG,SAAS,CAAC;IAShF,iBAAiB,CACrB,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC;IASvB,gBAAgB,CACpB,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAClB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,KAAK,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modules/orders/repository/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { eq, and, desc, sql } from "drizzle-orm";
|
|
1
|
+
import { eq, and, or, gte, lte, desc, sql } from "drizzle-orm";
|
|
2
2
|
import { orders, orderLineItems, orderStatusHistory } from "../schema.js";
|
|
3
|
+
import { customers } from "../../customers/schema.js";
|
|
3
4
|
/**
|
|
4
5
|
* OrdersRepository provides type-safe database operations for orders.
|
|
5
6
|
*
|
|
@@ -41,6 +42,46 @@ export class OrdersRepository {
|
|
|
41
42
|
.where(and(eq(orders.organizationId, orgId), eq(orders.customerId, customerId)))
|
|
42
43
|
.orderBy(desc(orders.placedAt));
|
|
43
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Fuzzy lookup across order number, customer email/name/phone, and the
|
|
47
|
+
* walk-in label. Phone matches ignore non-digits on both sides. Org-scoped.
|
|
48
|
+
*/
|
|
49
|
+
async lookup(orgId, q, opts, ctx) {
|
|
50
|
+
const db = this.getDb(ctx);
|
|
51
|
+
const like = `%${q}%`;
|
|
52
|
+
const qDigits = q.replace(/\D/g, "");
|
|
53
|
+
const matchers = [
|
|
54
|
+
sql `${orders.orderNumber} ILIKE ${like}`,
|
|
55
|
+
sql `${customers.email} ILIKE ${like}`,
|
|
56
|
+
sql `(coalesce(${customers.firstName}, '') || ' ' || coalesce(${customers.lastName}, '')) ILIKE ${like}`,
|
|
57
|
+
sql `(${orders.metadata} ->> 'customerLabel') ILIKE ${like}`,
|
|
58
|
+
];
|
|
59
|
+
if (qDigits.length >= 3) {
|
|
60
|
+
matchers.push(sql `regexp_replace(coalesce(${customers.phone}, ''), '[^0-9]', '', 'g') ILIKE ${`%${qDigits}%`}`);
|
|
61
|
+
}
|
|
62
|
+
const filters = [eq(orders.organizationId, orgId), or(...matchers)];
|
|
63
|
+
if (opts.from)
|
|
64
|
+
filters.push(gte(orders.placedAt, opts.from));
|
|
65
|
+
if (opts.to)
|
|
66
|
+
filters.push(lte(orders.placedAt, opts.to));
|
|
67
|
+
return db
|
|
68
|
+
.select({
|
|
69
|
+
id: orders.id,
|
|
70
|
+
orderNumber: orders.orderNumber,
|
|
71
|
+
placedAt: orders.placedAt,
|
|
72
|
+
status: orders.status,
|
|
73
|
+
grandTotal: orders.grandTotal,
|
|
74
|
+
customerId: customers.id,
|
|
75
|
+
firstName: customers.firstName,
|
|
76
|
+
lastName: customers.lastName,
|
|
77
|
+
phone: customers.phone,
|
|
78
|
+
})
|
|
79
|
+
.from(orders)
|
|
80
|
+
.leftJoin(customers, eq(orders.customerId, customers.id))
|
|
81
|
+
.where(and(...filters))
|
|
82
|
+
.orderBy(desc(orders.placedAt))
|
|
83
|
+
.limit(50);
|
|
84
|
+
}
|
|
44
85
|
async findByStatus(orgId, status, ctx) {
|
|
45
86
|
const db = this.getDb(ctx);
|
|
46
87
|
return db
|
|
@@ -35,6 +35,13 @@ export interface ListOrdersParams {
|
|
|
35
35
|
page?: number;
|
|
36
36
|
limit?: number;
|
|
37
37
|
status?: string;
|
|
38
|
+
/** When true, listByCustomer also returns a lifetime-spend rollup. */
|
|
39
|
+
includeTotals?: boolean;
|
|
40
|
+
}
|
|
41
|
+
export interface CustomerOrderTotals {
|
|
42
|
+
count: number;
|
|
43
|
+
lifetimeSpend: number;
|
|
44
|
+
averageBasket: number;
|
|
38
45
|
}
|
|
39
46
|
export interface ChangeStatusInput {
|
|
40
47
|
orderId: string;
|
|
@@ -55,6 +62,7 @@ export type HydratedOrder = Order & {
|
|
|
55
62
|
type OrderListResult = {
|
|
56
63
|
items: HydratedOrder[];
|
|
57
64
|
pagination: Pagination;
|
|
65
|
+
totals?: CustomerOrderTotals;
|
|
58
66
|
};
|
|
59
67
|
export declare class OrderService {
|
|
60
68
|
private deps;
|
|
@@ -67,6 +75,29 @@ export declare class OrderService {
|
|
|
67
75
|
getByNumber(orderNumber: string, actor: Actor | null, ctx?: TxContext): Promise<Result<HydratedOrder>>;
|
|
68
76
|
list(params: ListOrdersParams, actor: Actor | null, ctx?: TxContext): Promise<Result<OrderListResult>>;
|
|
69
77
|
listByCustomer(customerId: string, params: ListOrdersParams, actor?: Actor | null, ctx?: TxContext): Promise<Result<OrderListResult>>;
|
|
78
|
+
/**
|
|
79
|
+
* Fuzzy order lookup for receipt-less returns / support: matches across
|
|
80
|
+
* order number, customer email/name/phone (digits-normalized), and the
|
|
81
|
+
* walk-in label. Returns a compact result; <3 chars returns a hint.
|
|
82
|
+
*/
|
|
83
|
+
lookup(q: string, opts: {
|
|
84
|
+
from?: Date;
|
|
85
|
+
to?: Date;
|
|
86
|
+
}, actor?: Actor | null, ctx?: TxContext): Promise<Result<{
|
|
87
|
+
items: Array<{
|
|
88
|
+
id: string;
|
|
89
|
+
orderNumber: string;
|
|
90
|
+
placedAt: Date;
|
|
91
|
+
status: string;
|
|
92
|
+
grandTotal: number;
|
|
93
|
+
customer: {
|
|
94
|
+
id: string;
|
|
95
|
+
name: string | null;
|
|
96
|
+
phone: string | null;
|
|
97
|
+
} | null;
|
|
98
|
+
}>;
|
|
99
|
+
hint?: string;
|
|
100
|
+
}>>;
|
|
70
101
|
changeStatus(input: ChangeStatusInput, actor: Actor | null, ctx?: TxContext): Promise<Result<HydratedOrder>>;
|
|
71
102
|
cancel(orderId: string, actor: Actor | null, reason?: string, ctx?: TxContext): Promise<Result<HydratedOrder>>;
|
|
72
103
|
refund(orderId: string, actor: Actor | null, reason?: string, ctx?: TxContext): Promise<Result<HydratedOrder>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/modules/orders/service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAcjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAGL,KAAK,UAAU,EACf,KAAK,eAAe,EACrB,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAE9D,OAAO,EAAY,KAAK,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qCAAqC,CAAC;AACrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAExE,OAAO,EACL,gBAAgB,EAChB,KAAK,KAAK,EACV,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACxB,MAAM,uBAAuB,CAAC;AAE/B,MAAM,WAAW,gBAAgB;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,SAAS,EAAE,KAAK,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACpC,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/modules/orders/service.ts"],"names":[],"mappings":"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"}
|
|
@@ -158,13 +158,63 @@ export class OrderService {
|
|
|
158
158
|
items = items.filter((order) => order.status === params.status);
|
|
159
159
|
}
|
|
160
160
|
items.sort((a, b) => b.placedAt.getTime() - a.placedAt.getTime());
|
|
161
|
+
// Lifetime rollup over the full (un-paginated) set, computed server-side.
|
|
162
|
+
// Refunds/voids are excluded from lifetimeSpend.
|
|
163
|
+
let totals;
|
|
164
|
+
if (params.includeTotals) {
|
|
165
|
+
const excluded = new Set(["refunded", "voided"]);
|
|
166
|
+
const count = items.length;
|
|
167
|
+
const lifetimeSpend = items
|
|
168
|
+
.filter((order) => !excluded.has(order.status))
|
|
169
|
+
.reduce((sum, order) => sum + order.grandTotal, 0);
|
|
170
|
+
totals = {
|
|
171
|
+
count,
|
|
172
|
+
lifetimeSpend,
|
|
173
|
+
averageBasket: count > 0 ? Math.round(lifetimeSpend / count) : 0,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
161
176
|
const paged = paginate(items, params.page ?? 1, params.limit ?? 20);
|
|
162
177
|
const hydratedItems = await Promise.all(paged.items.map((order) => this.hydrateOrder(order, ctx)));
|
|
163
178
|
return Ok({
|
|
164
179
|
items: hydratedItems,
|
|
165
180
|
pagination: paged.pagination,
|
|
181
|
+
...(totals ? { totals } : {}),
|
|
166
182
|
});
|
|
167
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* Fuzzy order lookup for receipt-less returns / support: matches across
|
|
186
|
+
* order number, customer email/name/phone (digits-normalized), and the
|
|
187
|
+
* walk-in label. Returns a compact result; <3 chars returns a hint.
|
|
188
|
+
*/
|
|
189
|
+
async lookup(q, opts, actor, ctx) {
|
|
190
|
+
try {
|
|
191
|
+
assertPermission(actor ?? null, "orders:read");
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
return Err(toCommerceError(error));
|
|
195
|
+
}
|
|
196
|
+
const term = (q ?? "").trim();
|
|
197
|
+
if (term.length < 3) {
|
|
198
|
+
return Ok({ items: [], hint: "Enter at least 3 characters to search." });
|
|
199
|
+
}
|
|
200
|
+
const orgId = resolveOrgId(actor ?? ctx?.actor ?? null);
|
|
201
|
+
const rows = await this.repo.lookup(orgId, term, opts, ctx);
|
|
202
|
+
const items = rows.map((r) => ({
|
|
203
|
+
id: r.id,
|
|
204
|
+
orderNumber: r.orderNumber,
|
|
205
|
+
placedAt: r.placedAt,
|
|
206
|
+
status: r.status,
|
|
207
|
+
grandTotal: r.grandTotal,
|
|
208
|
+
customer: r.customerId
|
|
209
|
+
? {
|
|
210
|
+
id: r.customerId,
|
|
211
|
+
name: `${r.firstName ?? ""} ${r.lastName ?? ""}`.trim() || null,
|
|
212
|
+
phone: r.phone ?? null,
|
|
213
|
+
}
|
|
214
|
+
: null,
|
|
215
|
+
}));
|
|
216
|
+
return Ok({ items });
|
|
217
|
+
}
|
|
168
218
|
async changeStatus(input, actor, ctx) {
|
|
169
219
|
const orgId = resolveOrgId(actor);
|
|
170
220
|
const order = await this.repo.findById(orgId, input.orderId, ctx);
|
|
@@ -290,8 +340,8 @@ export class OrderService {
|
|
|
290
340
|
changedBy: actor?.userId ?? "system",
|
|
291
341
|
...(input.reason !== undefined ? { reason: input.reason } : {}),
|
|
292
342
|
}, ctx);
|
|
293
|
-
// Audit logging is
|
|
294
|
-
//
|
|
343
|
+
// Audit logging is automatic via the audit hooks registered at kernel
|
|
344
|
+
// boot — no manual audit.record() call needed here.
|
|
295
345
|
const hydrated = await this.hydrateOrder(cas, ctx);
|
|
296
346
|
const report = await runAfterHooks(afterHooks, null, hydrated, "statusChange", hookCtx);
|
|
297
347
|
return Ok(hydrated, report.hasErrors ? { hookErrors: report.errors } : undefined);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modules/payments/repository/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wCAAwC,CAAC;AAExE,MAAM,WAAW,kBAAkB;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modules/payments/repository/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wCAAwC,CAAC;AAExE,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC"}
|
|
@@ -1,4 +1,19 @@
|
|
|
1
1
|
import { z } from "@hono/zod-openapi";
|
|
2
|
+
/**
|
|
3
|
+
* The valid promotion `type` values. Single source of truth for the REST
|
|
4
|
+
* body schema, the OpenAPI enum, the service-layer validation, and the
|
|
5
|
+
* exported {@link PromotionType} union.
|
|
6
|
+
*/
|
|
7
|
+
export declare const promotionTypeEnum: z.ZodEnum<{
|
|
8
|
+
percentage_off_order: "percentage_off_order";
|
|
9
|
+
fixed_off_order: "fixed_off_order";
|
|
10
|
+
percentage_off_item: "percentage_off_item";
|
|
11
|
+
fixed_off_item: "fixed_off_item";
|
|
12
|
+
free_shipping: "free_shipping";
|
|
13
|
+
buy_x_get_y: "buy_x_get_y";
|
|
14
|
+
}>;
|
|
15
|
+
/** Valid promotion type discriminator. @see promotionTypeEnum */
|
|
16
|
+
export type PromotionType = z.infer<typeof promotionTypeEnum>;
|
|
2
17
|
export declare const CreatePromotionBodySchema: z.ZodObject<{
|
|
3
18
|
name: z.ZodString;
|
|
4
19
|
type: z.ZodEnum<{
|
|
@@ -29,5 +44,36 @@ export declare const CreatePromotionBodySchema: z.ZodObject<{
|
|
|
29
44
|
validUntil: z.ZodOptional<z.ZodCoercedDate<unknown>>;
|
|
30
45
|
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
31
46
|
}, z.core.$strip>;
|
|
47
|
+
export declare const UpdatePromotionBodySchema: z.ZodObject<{
|
|
48
|
+
name: z.ZodOptional<z.ZodString>;
|
|
49
|
+
type: z.ZodOptional<z.ZodEnum<{
|
|
50
|
+
percentage_off_order: "percentage_off_order";
|
|
51
|
+
fixed_off_order: "fixed_off_order";
|
|
52
|
+
percentage_off_item: "percentage_off_item";
|
|
53
|
+
fixed_off_item: "fixed_off_item";
|
|
54
|
+
free_shipping: "free_shipping";
|
|
55
|
+
buy_x_get_y: "buy_x_get_y";
|
|
56
|
+
}>>;
|
|
57
|
+
value: z.ZodOptional<z.ZodNumber>;
|
|
58
|
+
code: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
59
|
+
buyQuantity: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
|
|
60
|
+
getQuantity: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
|
|
61
|
+
isAutomatic: z.ZodOptional<z.ZodOptional<z.ZodBoolean>>;
|
|
62
|
+
isActive: z.ZodOptional<z.ZodOptional<z.ZodBoolean>>;
|
|
63
|
+
priority: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
|
|
64
|
+
conditions: z.ZodOptional<z.ZodOptional<z.ZodObject<{
|
|
65
|
+
minimumOrderValue: z.ZodOptional<z.ZodNumber>;
|
|
66
|
+
minimumQuantity: z.ZodOptional<z.ZodNumber>;
|
|
67
|
+
entityTypes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
68
|
+
categories: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
69
|
+
customerGroups: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
70
|
+
}, z.core.$strip>>>;
|
|
71
|
+
usageLimitTotal: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
|
|
72
|
+
usageLimitPerCustomer: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
|
|
73
|
+
validFrom: z.ZodOptional<z.ZodOptional<z.ZodCoercedDate<unknown>>>;
|
|
74
|
+
validUntil: z.ZodOptional<z.ZodOptional<z.ZodCoercedDate<unknown>>>;
|
|
75
|
+
metadata: z.ZodOptional<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
76
|
+
}, z.core.$strip>;
|
|
32
77
|
export type CreatePromotionInput = z.infer<typeof CreatePromotionBodySchema>;
|
|
78
|
+
export type UpdatePromotionInput = z.infer<typeof UpdatePromotionBodySchema>;
|
|
33
79
|
//# sourceMappingURL=schemas.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../../src/modules/promotions/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,mBAAmB,CAAC;AAItC,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../../src/modules/promotions/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,mBAAmB,CAAC;AAItC;;;;GAIG;AACH,eAAO,MAAM,iBAAiB;;;;;;;EASkC,CAAC;AAEjE,iEAAiE;AACjE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE9D,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAuBF,CAAC;AAGrC,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAsC,CAAC;AAI7E,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAC7E,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC"}
|
|
@@ -1,15 +1,24 @@
|
|
|
1
1
|
import { z } from "@hono/zod-openapi";
|
|
2
2
|
// ─── Zod Body Schemas (single source of truth) ─────────────────────────────
|
|
3
|
+
/**
|
|
4
|
+
* The valid promotion `type` values. Single source of truth for the REST
|
|
5
|
+
* body schema, the OpenAPI enum, the service-layer validation, and the
|
|
6
|
+
* exported {@link PromotionType} union.
|
|
7
|
+
*/
|
|
8
|
+
export const promotionTypeEnum = z
|
|
9
|
+
.enum([
|
|
10
|
+
"percentage_off_order",
|
|
11
|
+
"fixed_off_order",
|
|
12
|
+
"percentage_off_item",
|
|
13
|
+
"fixed_off_item",
|
|
14
|
+
"free_shipping",
|
|
15
|
+
"buy_x_get_y",
|
|
16
|
+
])
|
|
17
|
+
.openapi("PromotionType", { example: "percentage_off_order" });
|
|
3
18
|
export const CreatePromotionBodySchema = z.object({
|
|
4
19
|
name: z.string().openapi({ example: "Summer Sale" }),
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
"fixed_off_order",
|
|
8
|
-
"percentage_off_item",
|
|
9
|
-
"fixed_off_item",
|
|
10
|
-
"free_shipping",
|
|
11
|
-
"buy_x_get_y",
|
|
12
|
-
]).openapi({ example: "percentage_off_order" }),
|
|
20
|
+
/** @see PromotionType for valid values */
|
|
21
|
+
type: promotionTypeEnum,
|
|
13
22
|
value: z.number().openapi({ example: 10 }),
|
|
14
23
|
code: z.string().optional().openapi({ example: "SUMMER10" }),
|
|
15
24
|
buyQuantity: z.number().int().optional(),
|
|
@@ -30,3 +39,5 @@ export const CreatePromotionBodySchema = z.object({
|
|
|
30
39
|
validUntil: z.coerce.date().optional(),
|
|
31
40
|
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
32
41
|
}).openapi("CreatePromotionRequest");
|
|
42
|
+
// Edit any subset of the create body. Validated the same way create is.
|
|
43
|
+
export const UpdatePromotionBodySchema = CreatePromotionBodySchema.partial();
|
|
@@ -6,7 +6,8 @@ import type { PromotionsRepository, Promotion, PromotionUsage } from "./reposito
|
|
|
6
6
|
import type { CatalogRepository } from "../catalog/repository/index.js";
|
|
7
7
|
import type { OrdersRepository } from "../orders/repository/index.js";
|
|
8
8
|
import type { Actor } from "../../auth/types.js";
|
|
9
|
-
|
|
9
|
+
import { type PromotionType } from "./schemas.js";
|
|
10
|
+
export type { PromotionType };
|
|
10
11
|
/** Filter status for listing promotions. Used by the REST API and service layer. */
|
|
11
12
|
export type PromotionStatusFilter = "active" | "inactive" | "expired" | "scheduled";
|
|
12
13
|
interface PromotionServiceDeps {
|
|
@@ -44,7 +45,7 @@ export interface PromotionConditions {
|
|
|
44
45
|
firstOrderOnly?: boolean;
|
|
45
46
|
}
|
|
46
47
|
export type { CreatePromotionInput } from "./schemas.js";
|
|
47
|
-
import type { CreatePromotionInput } from "./schemas.js";
|
|
48
|
+
import type { CreatePromotionInput, UpdatePromotionInput } from "./schemas.js";
|
|
48
49
|
export interface AppliedPromotion {
|
|
49
50
|
promotionId: string;
|
|
50
51
|
code?: string;
|
|
@@ -70,6 +71,11 @@ export declare class PromotionService {
|
|
|
70
71
|
constructor(deps: PromotionServiceDeps);
|
|
71
72
|
create(input: CreatePromotionInput, actor?: Actor | null, ctx?: TxContext): Promise<Result<Promotion>>;
|
|
72
73
|
deactivate(orgId: string, id: string, ctx?: TxContext): Promise<Result<Promotion>>;
|
|
74
|
+
/**
|
|
75
|
+
* Edit any subset of a promotion's fields. Validated the same way create is
|
|
76
|
+
* (value bounds, type enum, code uniqueness). Only provided keys are written.
|
|
77
|
+
*/
|
|
78
|
+
update(orgId: string, id: string, input: UpdatePromotionInput, actor?: Actor | null, ctx?: TxContext): Promise<Result<Promotion>>;
|
|
73
79
|
list(filter?: {
|
|
74
80
|
status?: PromotionStatusFilter;
|
|
75
81
|
}, actor?: Actor | null, ctx?: TxContext): Promise<Result<Promotion[]>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/modules/promotions/service.ts"],"names":[],"mappings":"AAAA,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,oBAAoB,EACpB,SAAS,EACT,cAAc,
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/modules/promotions/service.ts"],"names":[],"mappings":"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"}
|