@porulle/core 0.7.0 → 0.9.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 +8 -5
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +7 -0
- package/dist/config/types.d.ts +26 -1
- 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 +17 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/interfaces/rest/index.d.ts.map +1 -1
- package/dist/interfaces/rest/index.js +12 -0
- package/dist/interfaces/rest/routes/admin/staff.d.ts +12 -0
- package/dist/interfaces/rest/routes/admin/staff.d.ts.map +1 -0
- package/dist/interfaces/rest/routes/admin/staff.js +206 -0
- package/dist/interfaces/rest/routes/analytics.d.ts +5 -0
- package/dist/interfaces/rest/routes/analytics.d.ts.map +1 -0
- package/dist/interfaces/rest/routes/analytics.js +24 -0
- package/dist/interfaces/rest/routes/carts.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/carts.js +39 -2
- package/dist/interfaces/rest/routes/catalog.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/catalog.js +17 -1
- 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 +97 -21
- package/dist/interfaces/rest/routes/documents.d.ts +10 -0
- package/dist/interfaces/rest/routes/documents.d.ts.map +1 -0
- package/dist/interfaces/rest/routes/documents.js +40 -0
- 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 +111 -1
- package/dist/interfaces/rest/routes/pricing.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/pricing.js +36 -1
- package/dist/interfaces/rest/routes/settings.d.ts +5 -0
- package/dist/interfaces/rest/routes/settings.d.ts.map +1 -0
- package/dist/interfaces/rest/routes/settings.js +50 -0
- package/dist/interfaces/rest/routes/shipping.d.ts +5 -0
- package/dist/interfaces/rest/routes/shipping.d.ts.map +1 -0
- package/dist/interfaces/rest/routes/shipping.js +68 -0
- package/dist/interfaces/rest/routes/tax.d.ts +5 -0
- package/dist/interfaces/rest/routes/tax.d.ts.map +1 -0
- package/dist/interfaces/rest/routes/tax.js +68 -0
- package/dist/interfaces/rest/schemas/admin-staff.d.ts +899 -0
- package/dist/interfaces/rest/schemas/admin-staff.d.ts.map +1 -0
- package/dist/interfaces/rest/schemas/admin-staff.js +98 -0
- package/dist/interfaces/rest/schemas/analytics.d.ts +252 -0
- package/dist/interfaces/rest/schemas/analytics.d.ts.map +1 -0
- package/dist/interfaces/rest/schemas/analytics.js +33 -0
- package/dist/interfaces/rest/schemas/carts.d.ts +656 -0
- package/dist/interfaces/rest/schemas/carts.d.ts.map +1 -1
- package/dist/interfaces/rest/schemas/carts.js +38 -0
- package/dist/interfaces/rest/schemas/catalog.d.ts +1218 -1
- package/dist/interfaces/rest/schemas/catalog.d.ts.map +1 -1
- package/dist/interfaces/rest/schemas/catalog.js +48 -0
- package/dist/interfaces/rest/schemas/checkout.d.ts +2 -0
- package/dist/interfaces/rest/schemas/checkout.d.ts.map +1 -1
- package/dist/interfaces/rest/schemas/checkout.js +4 -0
- package/dist/interfaces/rest/schemas/customer-portal.d.ts +35 -0
- package/dist/interfaces/rest/schemas/customer-portal.d.ts.map +1 -1
- package/dist/interfaces/rest/schemas/documents.d.ts +509 -0
- package/dist/interfaces/rest/schemas/documents.d.ts.map +1 -0
- package/dist/interfaces/rest/schemas/documents.js +65 -0
- package/dist/interfaces/rest/schemas/orders.d.ts +3110 -43
- package/dist/interfaces/rest/schemas/orders.d.ts.map +1 -1
- package/dist/interfaces/rest/schemas/orders.js +234 -1
- package/dist/interfaces/rest/schemas/pricing.d.ts +405 -0
- package/dist/interfaces/rest/schemas/pricing.d.ts.map +1 -1
- package/dist/interfaces/rest/schemas/pricing.js +70 -0
- package/dist/interfaces/rest/schemas/responses.d.ts +190 -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/interfaces/rest/schemas/settings.d.ts +397 -0
- package/dist/interfaces/rest/schemas/settings.d.ts.map +1 -0
- package/dist/interfaces/rest/schemas/settings.js +79 -0
- package/dist/interfaces/rest/schemas/shipping.d.ts +1093 -0
- package/dist/interfaces/rest/schemas/shipping.d.ts.map +1 -0
- package/dist/interfaces/rest/schemas/shipping.js +146 -0
- package/dist/interfaces/rest/schemas/tax.d.ts +1074 -0
- package/dist/interfaces/rest/schemas/tax.d.ts.map +1 -0
- package/dist/interfaces/rest/schemas/tax.js +132 -0
- package/dist/kernel/database/schema.d.ts +4 -0
- package/dist/kernel/database/schema.d.ts.map +1 -1
- package/dist/kernel/database/schema.js +8 -0
- 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 +62 -1
- package/dist/kernel/hooks/types.d.ts +1 -1
- package/dist/kernel/hooks/types.d.ts.map +1 -1
- package/dist/kernel/plugin/manifest.d.ts +26 -0
- package/dist/kernel/plugin/manifest.d.ts.map +1 -1
- package/dist/kernel/plugin/manifest.js +17 -2
- package/dist/modules/analytics/drizzle-adapter.d.ts.map +1 -1
- package/dist/modules/analytics/drizzle-adapter.js +16 -3
- package/dist/modules/analytics/module.d.ts.map +1 -1
- package/dist/modules/analytics/module.js +6 -0
- package/dist/modules/analytics/reports.d.ts +58 -0
- package/dist/modules/analytics/reports.d.ts.map +1 -0
- package/dist/modules/analytics/reports.js +249 -0
- package/dist/modules/analytics/service.d.ts +10 -0
- package/dist/modules/analytics/service.d.ts.map +1 -1
- package/dist/modules/analytics/service.js +14 -0
- package/dist/modules/cart/repository/index.d.ts +7 -0
- package/dist/modules/cart/repository/index.d.ts.map +1 -1
- package/dist/modules/cart/repository/index.js +25 -1
- package/dist/modules/cart/schema.d.ts +17 -0
- package/dist/modules/cart/schema.d.ts.map +1 -1
- package/dist/modules/cart/schema.js +2 -0
- package/dist/modules/cart/schemas.d.ts +1 -0
- package/dist/modules/cart/schemas.d.ts.map +1 -1
- package/dist/modules/cart/schemas.js +1 -0
- package/dist/modules/cart/service.d.ts +32 -0
- package/dist/modules/cart/service.d.ts.map +1 -1
- package/dist/modules/cart/service.js +61 -2
- package/dist/modules/catalog/entity-service.d.ts +21 -0
- package/dist/modules/catalog/entity-service.d.ts.map +1 -1
- package/dist/modules/catalog/entity-service.js +166 -5
- package/dist/modules/catalog/schema.d.ts +34 -0
- package/dist/modules/catalog/schema.d.ts.map +1 -1
- package/dist/modules/catalog/schema.js +5 -0
- package/dist/modules/catalog/schemas.d.ts +3 -0
- package/dist/modules/catalog/schemas.d.ts.map +1 -1
- package/dist/modules/catalog/schemas.js +3 -0
- package/dist/modules/catalog/service.d.ts +36 -0
- package/dist/modules/catalog/service.d.ts.map +1 -1
- package/dist/modules/catalog/service.js +6 -0
- package/dist/modules/customers/service.d.ts.map +1 -1
- package/dist/modules/customers/service.js +6 -0
- package/dist/modules/documents/module.d.ts +7 -0
- package/dist/modules/documents/module.d.ts.map +1 -0
- package/dist/modules/documents/module.js +12 -0
- package/dist/modules/documents/pdf.d.ts +25 -0
- package/dist/modules/documents/pdf.d.ts.map +1 -0
- package/dist/modules/documents/pdf.js +80 -0
- package/dist/modules/documents/render.d.ts +32 -0
- package/dist/modules/documents/render.d.ts.map +1 -0
- package/dist/modules/documents/render.js +190 -0
- package/dist/modules/documents/repository/index.d.ts +25 -0
- package/dist/modules/documents/repository/index.d.ts.map +1 -0
- package/dist/modules/documents/repository/index.js +47 -0
- package/dist/modules/documents/schema.d.ts +214 -0
- package/dist/modules/documents/schema.d.ts.map +1 -0
- package/dist/modules/documents/schema.js +42 -0
- package/dist/modules/documents/service.d.ts +51 -0
- package/dist/modules/documents/service.d.ts.map +1 -0
- package/dist/modules/documents/service.js +119 -0
- package/dist/modules/fulfillment/service.d.ts +18 -0
- package/dist/modules/fulfillment/service.d.ts.map +1 -1
- package/dist/modules/fulfillment/service.js +85 -1
- package/dist/modules/orders/repository/index.d.ts +18 -1
- package/dist/modules/orders/repository/index.d.ts.map +1 -1
- package/dist/modules/orders/repository/index.js +86 -1
- package/dist/modules/orders/schema.d.ts +391 -0
- package/dist/modules/orders/schema.d.ts.map +1 -1
- package/dist/modules/orders/schema.js +56 -1
- package/dist/modules/orders/service.d.ts +102 -3
- package/dist/modules/orders/service.d.ts.map +1 -1
- package/dist/modules/orders/service.js +653 -29
- package/dist/modules/orders/stale-order-cleanup.d.ts.map +1 -1
- package/dist/modules/orders/stale-order-cleanup.js +13 -1
- package/dist/modules/pricing/repository/index.d.ts +5 -0
- package/dist/modules/pricing/repository/index.d.ts.map +1 -1
- package/dist/modules/pricing/repository/index.js +19 -0
- package/dist/modules/pricing/service.d.ts +18 -0
- package/dist/modules/pricing/service.d.ts.map +1 -1
- package/dist/modules/pricing/service.js +43 -5
- package/dist/modules/settings/module.d.ts +6 -0
- package/dist/modules/settings/module.d.ts.map +1 -0
- package/dist/modules/settings/module.js +11 -0
- package/dist/modules/settings/repository/index.d.ts +16 -0
- package/dist/modules/settings/repository/index.d.ts.map +1 -0
- package/dist/modules/settings/repository/index.js +41 -0
- package/dist/modules/settings/schema.d.ts +121 -0
- package/dist/modules/settings/schema.d.ts.map +1 -0
- package/dist/modules/settings/schema.js +24 -0
- package/dist/modules/settings/service.d.ts +26 -0
- package/dist/modules/settings/service.d.ts.map +1 -0
- package/dist/modules/settings/service.js +51 -0
- package/dist/modules/shipping/calculator.d.ts +5 -0
- package/dist/modules/shipping/calculator.d.ts.map +1 -1
- package/dist/modules/shipping/calculator.js +10 -0
- package/dist/modules/shipping/module.d.ts +5 -1
- package/dist/modules/shipping/module.d.ts.map +1 -1
- package/dist/modules/shipping/module.js +4 -1
- package/dist/modules/shipping/repository/index.d.ts +27 -2
- package/dist/modules/shipping/repository/index.d.ts.map +1 -1
- package/dist/modules/shipping/repository/index.js +105 -1
- package/dist/modules/shipping/schema.d.ts +419 -0
- package/dist/modules/shipping/schema.d.ts.map +1 -0
- package/dist/modules/shipping/schema.js +56 -0
- package/dist/modules/shipping/service.d.ts +54 -0
- package/dist/modules/shipping/service.d.ts.map +1 -1
- package/dist/modules/shipping/service.js +161 -2
- package/dist/modules/tax/adapter.d.ts +13 -0
- package/dist/modules/tax/adapter.d.ts.map +1 -1
- package/dist/modules/tax/module.d.ts +5 -1
- package/dist/modules/tax/module.d.ts.map +1 -1
- package/dist/modules/tax/module.js +4 -1
- package/dist/modules/tax/repository/index.d.ts +38 -2
- package/dist/modules/tax/repository/index.d.ts.map +1 -1
- package/dist/modules/tax/repository/index.js +138 -1
- package/dist/modules/tax/schema.d.ts +355 -0
- package/dist/modules/tax/schema.d.ts.map +1 -0
- package/dist/modules/tax/schema.js +55 -0
- package/dist/modules/tax/service.d.ts +60 -1
- package/dist/modules/tax/service.d.ts.map +1 -1
- package/dist/modules/tax/service.js +227 -2
- package/dist/runtime/kernel-modules.d.ts +15 -2
- package/dist/runtime/kernel-modules.d.ts.map +1 -1
- package/dist/runtime/kernel-modules.js +4 -0
- package/dist/runtime/kernel-types.d.ts +5 -1
- package/dist/runtime/kernel-types.d.ts.map +1 -1
- package/dist/runtime/kernel-types.js +2 -0
- package/dist/runtime/server.js +1 -1
- package/dist/test-utils/create-plugin-test-app.d.ts +3 -0
- package/dist/test-utils/create-plugin-test-app.d.ts.map +1 -1
- package/dist/test-utils/create-plugin-test-app.js +15 -3
- 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 +7 -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 +3 -3
- package/src/auth/middleware.ts +26 -7
- package/src/auth/setup.ts +11 -4
- package/src/config/defaults.ts +7 -0
- package/src/config/types.ts +26 -1
- 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 +36 -11
- package/src/index.ts +1 -0
- package/src/interfaces/rest/index.ts +12 -0
- package/src/interfaces/rest/routes/admin/staff.ts +260 -0
- package/src/interfaces/rest/routes/analytics.ts +33 -0
- package/src/interfaces/rest/routes/carts.ts +52 -2
- package/src/interfaces/rest/routes/catalog.ts +34 -0
- package/src/interfaces/rest/routes/checkout.ts +114 -27
- package/src/interfaces/rest/routes/documents.ts +61 -0
- package/src/interfaces/rest/routes/inventory.ts +14 -0
- package/src/interfaces/rest/routes/orders.ts +148 -1
- package/src/interfaces/rest/routes/pricing.ts +44 -1
- package/src/interfaces/rest/routes/settings.ts +61 -0
- package/src/interfaces/rest/routes/shipping.ts +92 -0
- package/src/interfaces/rest/routes/tax.ts +90 -0
- package/src/interfaces/rest/schemas/admin-staff.ts +110 -0
- package/src/interfaces/rest/schemas/analytics.ts +39 -0
- package/src/interfaces/rest/schemas/carts.ts +40 -0
- package/src/interfaces/rest/schemas/catalog.ts +53 -0
- package/src/interfaces/rest/schemas/checkout.ts +4 -0
- package/src/interfaces/rest/schemas/documents.ts +72 -0
- package/src/interfaces/rest/schemas/orders.ts +258 -1
- package/src/interfaces/rest/schemas/pricing.ts +75 -0
- package/src/interfaces/rest/schemas/settings.ts +93 -0
- package/src/interfaces/rest/schemas/shipping.ts +164 -0
- package/src/interfaces/rest/schemas/tax.ts +148 -0
- package/src/kernel/database/schema.ts +12 -0
- package/src/kernel/database/scoped-db.ts +70 -1
- package/src/kernel/hooks/types.ts +1 -0
- package/src/kernel/plugin/manifest.ts +41 -2
- package/src/modules/analytics/drizzle-adapter.ts +19 -3
- package/src/modules/analytics/module.ts +10 -0
- package/src/modules/analytics/reports.ts +311 -0
- package/src/modules/analytics/service.ts +23 -0
- package/src/modules/cart/repository/index.ts +30 -1
- package/src/modules/cart/schema.ts +2 -0
- package/src/modules/cart/schemas.ts +1 -0
- package/src/modules/cart/service.ts +104 -2
- package/src/modules/catalog/entity-service.ts +196 -4
- package/src/modules/catalog/schema.ts +5 -0
- package/src/modules/catalog/schemas.ts +3 -0
- package/src/modules/catalog/service.ts +30 -0
- package/src/modules/customers/service.ts +6 -0
- package/src/modules/documents/module.ts +19 -0
- package/src/modules/documents/pdf.ts +101 -0
- package/src/modules/documents/render.ts +231 -0
- package/src/modules/documents/repository/index.ts +68 -0
- package/src/modules/documents/schema.ts +44 -0
- package/src/modules/documents/service.ts +176 -0
- package/src/modules/fulfillment/service.ts +139 -1
- package/src/modules/orders/repository/index.ts +127 -1
- package/src/modules/orders/schema.ts +58 -1
- package/src/modules/orders/service.ts +1041 -65
- package/src/modules/orders/stale-order-cleanup.ts +18 -4
- package/src/modules/pricing/repository/index.ts +34 -0
- package/src/modules/pricing/service.ts +76 -6
- package/src/modules/settings/module.ts +18 -0
- package/src/modules/settings/repository/index.ts +59 -0
- package/src/modules/settings/schema.ts +25 -0
- package/src/modules/settings/service.ts +82 -0
- package/src/modules/shipping/calculator.ts +27 -0
- package/src/modules/shipping/module.ts +6 -2
- package/src/modules/shipping/repository/index.ts +136 -2
- package/src/modules/shipping/schema.ts +58 -0
- package/src/modules/shipping/service.ts +260 -1
- package/src/modules/tax/adapter.ts +9 -0
- package/src/modules/tax/module.ts +6 -2
- package/src/modules/tax/repository/index.ts +169 -2
- package/src/modules/tax/schema.ts +57 -0
- package/src/modules/tax/service.ts +313 -2
- package/src/runtime/kernel-modules.ts +4 -0
- package/src/runtime/kernel-types.ts +6 -0
- package/src/runtime/server.ts +1 -1
- package/src/test-utils/create-plugin-test-app.ts +17 -4
- 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 +7 -0
- package/src/test-utils/test-actors.ts +1 -1
- package/src/testing.ts +1 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { Actor } from "../../auth/types.js";
|
|
2
|
+
import type { TxContext } from "../../kernel/database/tx-context.js";
|
|
3
|
+
import { type Result } from "../../kernel/result.js";
|
|
4
|
+
import type { HydratedOrder } from "../orders/service.js";
|
|
5
|
+
import type { DocumentsRepository, OrderDocument } from "./repository/index.js";
|
|
6
|
+
interface DocumentsServiceDeps {
|
|
7
|
+
repository: DocumentsRepository;
|
|
8
|
+
services: Record<string, unknown>;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Order document rendering + fiscal numbering (issue #47).
|
|
12
|
+
*/
|
|
13
|
+
export declare class DocumentsService {
|
|
14
|
+
private repository;
|
|
15
|
+
private services;
|
|
16
|
+
constructor(deps: DocumentsServiceDeps);
|
|
17
|
+
private get orders();
|
|
18
|
+
private get settings();
|
|
19
|
+
private brandingFor;
|
|
20
|
+
/**
|
|
21
|
+
* Issues (or returns the previously issued) fiscal invoice number for an
|
|
22
|
+
* order. Idempotent per (org, order): once handed out, the number never
|
|
23
|
+
* changes. Sequence allocation is atomic; a lost creation race falls back
|
|
24
|
+
* to the winner's document.
|
|
25
|
+
*/
|
|
26
|
+
issueInvoiceNumber(order: HydratedOrder, ctx?: TxContext): Promise<OrderDocument>;
|
|
27
|
+
private loadOrder;
|
|
28
|
+
renderInvoiceHtml(orderId: string, actor: Actor | null, ctx?: TxContext): Promise<Result<{
|
|
29
|
+
html: string;
|
|
30
|
+
invoiceNumber: string;
|
|
31
|
+
}>>;
|
|
32
|
+
renderInvoicePdf(orderId: string, actor: Actor | null, ctx?: TxContext): Promise<Result<{
|
|
33
|
+
pdf: Uint8Array;
|
|
34
|
+
invoiceNumber: string;
|
|
35
|
+
}>>;
|
|
36
|
+
renderReceiptHtml(orderId: string, actor: Actor | null, ctx?: TxContext): Promise<Result<{
|
|
37
|
+
html: string;
|
|
38
|
+
}>>;
|
|
39
|
+
/**
|
|
40
|
+
* Emails the invoice via the configured email adapter. The adapter's
|
|
41
|
+
* template contract is (template, to, data) — the rendered HTML and the
|
|
42
|
+
* fiscal number ride in `data` so template-based adapters can use either.
|
|
43
|
+
*/
|
|
44
|
+
emailInvoice(orderId: string, to: string, actor: Actor | null, ctx?: TxContext): Promise<Result<{
|
|
45
|
+
sent: true;
|
|
46
|
+
invoiceNumber: string;
|
|
47
|
+
to: string;
|
|
48
|
+
}>>;
|
|
49
|
+
}
|
|
50
|
+
export {};
|
|
51
|
+
//# sourceMappingURL=service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/modules/documents/service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qCAAqC,CAAC;AAErE,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAgB,MAAM,sBAAsB,CAAC;AASxE,OAAO,KAAK,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEhF,UAAU,oBAAoB;IAC5B,UAAU,EAAE,mBAAmB,CAAC;IAGhC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAQD;;GAEG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,UAAU,CAAsB;IACxC,OAAO,CAAC,QAAQ,CAA0B;gBAE9B,IAAI,EAAE,oBAAoB;IAKtC,OAAO,KAAK,MAAM,GAEjB;IAED,OAAO,KAAK,QAAQ,GAEnB;YAEa,WAAW;IAIzB;;;;;OAKG;IACG,kBAAkB,CACtB,KAAK,EAAE,aAAa,EACpB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,aAAa,CAAC;YA0BX,SAAS;IAQjB,iBAAiB,CACrB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,KAAK,GAAG,IAAI,EACnB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAWrD,gBAAgB,CACpB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,KAAK,GAAG,IAAI,EACnB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC;QAAE,GAAG,EAAE,UAAU,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAaxD,iBAAiB,CACrB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,KAAK,GAAG,IAAI,EACnB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAOpC;;;;OAIG;IACG,YAAY,CAChB,OAAO,EAAE,MAAM,EACf,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,KAAK,GAAG,IAAI,EACnB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC;QAAE,IAAI,EAAE,IAAI,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CA0BtE"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { resolveOrgId } from "../../auth/org.js";
|
|
2
|
+
import { CommerceValidationError } from "../../kernel/errors.js";
|
|
3
|
+
import { Err, Ok } from "../../kernel/result.js";
|
|
4
|
+
import { buildPdf } from "./pdf.js";
|
|
5
|
+
import { invoiceHtml, invoicePdfOps, receiptHtml, } from "./render.js";
|
|
6
|
+
const DEFAULT_INVOICE_PREFIX = "INV-";
|
|
7
|
+
/**
|
|
8
|
+
* Order document rendering + fiscal numbering (issue #47).
|
|
9
|
+
*/
|
|
10
|
+
export class DocumentsService {
|
|
11
|
+
repository;
|
|
12
|
+
services;
|
|
13
|
+
constructor(deps) {
|
|
14
|
+
this.repository = deps.repository;
|
|
15
|
+
this.services = deps.services;
|
|
16
|
+
}
|
|
17
|
+
get orders() {
|
|
18
|
+
return this.services.orders;
|
|
19
|
+
}
|
|
20
|
+
get settings() {
|
|
21
|
+
return this.services.settings;
|
|
22
|
+
}
|
|
23
|
+
async brandingFor(orgId, ctx) {
|
|
24
|
+
return (await this.settings.read(orgId, "branding", ctx));
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Issues (or returns the previously issued) fiscal invoice number for an
|
|
28
|
+
* order. Idempotent per (org, order): once handed out, the number never
|
|
29
|
+
* changes. Sequence allocation is atomic; a lost creation race falls back
|
|
30
|
+
* to the winner's document.
|
|
31
|
+
*/
|
|
32
|
+
async issueInvoiceNumber(order, ctx) {
|
|
33
|
+
const orgId = order.organizationId;
|
|
34
|
+
const existing = await this.repository.findDocument(orgId, order.id, "invoice", ctx);
|
|
35
|
+
if (existing)
|
|
36
|
+
return existing;
|
|
37
|
+
const documentsSettings = await this.settings.read(orgId, "documents", ctx);
|
|
38
|
+
const prefix = typeof documentsSettings.invoicePrefix === "string"
|
|
39
|
+
? documentsSettings.invoicePrefix
|
|
40
|
+
: DEFAULT_INVOICE_PREFIX;
|
|
41
|
+
const value = await this.repository.allocate(orgId, "invoice", ctx);
|
|
42
|
+
const documentNumber = `${prefix}${String(value).padStart(6, "0")}`;
|
|
43
|
+
try {
|
|
44
|
+
return await this.repository.createDocument({ organizationId: orgId, orderId: order.id, type: "invoice", documentNumber }, ctx);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
// Unique (org, order, type) — a concurrent request won; use its number.
|
|
48
|
+
const winner = await this.repository.findDocument(orgId, order.id, "invoice", ctx);
|
|
49
|
+
if (winner)
|
|
50
|
+
return winner;
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async loadOrder(orderId, actor, ctx) {
|
|
55
|
+
return this.orders.getById(orderId, actor, ctx);
|
|
56
|
+
}
|
|
57
|
+
async renderInvoiceHtml(orderId, actor, ctx) {
|
|
58
|
+
const order = await this.loadOrder(orderId, actor, ctx);
|
|
59
|
+
if (!order.ok)
|
|
60
|
+
return order;
|
|
61
|
+
const branding = await this.brandingFor(order.value.organizationId, ctx);
|
|
62
|
+
const doc = await this.issueInvoiceNumber(order.value, ctx);
|
|
63
|
+
return Ok({
|
|
64
|
+
html: invoiceHtml({ order: order.value, branding, invoiceNumber: doc.documentNumber }),
|
|
65
|
+
invoiceNumber: doc.documentNumber,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
async renderInvoicePdf(orderId, actor, ctx) {
|
|
69
|
+
const order = await this.loadOrder(orderId, actor, ctx);
|
|
70
|
+
if (!order.ok)
|
|
71
|
+
return order;
|
|
72
|
+
const branding = await this.brandingFor(order.value.organizationId, ctx);
|
|
73
|
+
const doc = await this.issueInvoiceNumber(order.value, ctx);
|
|
74
|
+
const pages = invoicePdfOps({
|
|
75
|
+
order: order.value,
|
|
76
|
+
branding,
|
|
77
|
+
invoiceNumber: doc.documentNumber,
|
|
78
|
+
});
|
|
79
|
+
return Ok({ pdf: buildPdf(pages), invoiceNumber: doc.documentNumber });
|
|
80
|
+
}
|
|
81
|
+
async renderReceiptHtml(orderId, actor, ctx) {
|
|
82
|
+
const order = await this.loadOrder(orderId, actor, ctx);
|
|
83
|
+
if (!order.ok)
|
|
84
|
+
return order;
|
|
85
|
+
const branding = await this.brandingFor(order.value.organizationId, ctx);
|
|
86
|
+
return Ok({ html: receiptHtml({ order: order.value, branding }) });
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Emails the invoice via the configured email adapter. The adapter's
|
|
90
|
+
* template contract is (template, to, data) — the rendered HTML and the
|
|
91
|
+
* fiscal number ride in `data` so template-based adapters can use either.
|
|
92
|
+
*/
|
|
93
|
+
async emailInvoice(orderId, to, actor, ctx) {
|
|
94
|
+
const email = this.services.email;
|
|
95
|
+
if (!email) {
|
|
96
|
+
return Err(new CommerceValidationError("No email adapter is configured."));
|
|
97
|
+
}
|
|
98
|
+
const rendered = await this.renderInvoiceHtml(orderId, actor, ctx);
|
|
99
|
+
if (!rendered.ok)
|
|
100
|
+
return rendered;
|
|
101
|
+
const order = await this.loadOrder(orderId, actor, ctx);
|
|
102
|
+
if (!order.ok)
|
|
103
|
+
return order;
|
|
104
|
+
await email.send({
|
|
105
|
+
template: "order-invoice",
|
|
106
|
+
to,
|
|
107
|
+
data: {
|
|
108
|
+
orgId: resolveOrgId(actor ?? ctx?.actor ?? null),
|
|
109
|
+
orderId,
|
|
110
|
+
orderNumber: order.value.orderNumber,
|
|
111
|
+
invoiceNumber: rendered.value.invoiceNumber,
|
|
112
|
+
grandTotal: order.value.grandTotal,
|
|
113
|
+
currency: order.value.currency,
|
|
114
|
+
html: rendered.value.html,
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
return Ok({ sent: true, invoiceNumber: rendered.value.invoiceNumber, to });
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -37,6 +37,24 @@ export declare class FulfillmentService {
|
|
|
37
37
|
private strategies;
|
|
38
38
|
constructor(deps: FulfillmentServiceDeps);
|
|
39
39
|
fulfillOrder(orderId: string, actor?: Actor | null, ctx?: TxContext): Promise<Result<void>>;
|
|
40
|
+
/**
|
|
41
|
+
* Records a fulfillment (e.g. a shipment) for a subset of an order's line
|
|
42
|
+
* items, with optional carrier/tracking details. Supports partial
|
|
43
|
+
* fulfillment (per-line quantities) and multiple fulfillments per order.
|
|
44
|
+
*/
|
|
45
|
+
createFulfillment(input: {
|
|
46
|
+
orderId: string;
|
|
47
|
+
lineItems: Array<{
|
|
48
|
+
orderLineItemId: string;
|
|
49
|
+
quantity: number;
|
|
50
|
+
}>;
|
|
51
|
+
carrier?: string;
|
|
52
|
+
trackingNumber?: string;
|
|
53
|
+
trackingUrl?: string;
|
|
54
|
+
type?: string;
|
|
55
|
+
status?: string;
|
|
56
|
+
metadata?: Record<string, unknown>;
|
|
57
|
+
}, actor?: Actor | null, ctx?: TxContext): Promise<Result<FulfillmentRecord>>;
|
|
40
58
|
getByOrderId(orderId: string, ctx?: TxContext): Promise<Result<FulfillmentRecord[]>>;
|
|
41
59
|
updateTracking(input: {
|
|
42
60
|
fulfillmentId: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/modules/fulfillment/service.ts"],"names":[],"mappings":"AAEA,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;AAIrE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAEnE,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAE9D,OAAO,KAAK,EAEV,qBAAqB,EACtB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,KAAK,EAEV,iBAAiB,EAGlB,MAAM,YAAY,CAAC;AAGpB,UAAU,oBAAoB;IAC5B,MAAM,CAAC,KAAK,EAAE;QACZ,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;QACf,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtC,OAAO,CAAC,KAAK,EAAE;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACtB;AAED,UAAU,sBAAsB;IAC9B,UAAU,EAAE,qBAAqB,CAAC;IAClC,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,gBAAgB,CAAC,EAAE,oBAAoB,CAAC;IACxC,KAAK,EAAE,YAAY,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,QAAQ,EAAE,eAAe,CAAC;CAC3B;AA4LD,qBAAa,kBAAkB;IAGjB,OAAO,CAAC,IAAI;IAFxB,OAAO,CAAC,UAAU,CAA0C;gBAExC,IAAI,EAAE,sBAAsB;IAiB1C,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EAAE,GAAG,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/modules/fulfillment/service.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHmac } from "node:crypto";
|
|
2
2
|
import { resolveOrgId } from "../../auth/org.js";
|
|
3
|
-
import { CommerceNotFoundError } from "../../kernel/errors.js";
|
|
3
|
+
import { CommerceNotFoundError, CommerceValidationError } from "../../kernel/errors.js";
|
|
4
4
|
import { runAfterHooks } from "../../kernel/hooks/executor.js";
|
|
5
5
|
import { createHookContext } from "../../kernel/hooks/create-context.js";
|
|
6
6
|
import { Err, Ok } from "../../kernel/result.js";
|
|
@@ -198,6 +198,90 @@ export class FulfillmentService {
|
|
|
198
198
|
}
|
|
199
199
|
return Ok(undefined);
|
|
200
200
|
}
|
|
201
|
+
/**
|
|
202
|
+
* Records a fulfillment (e.g. a shipment) for a subset of an order's line
|
|
203
|
+
* items, with optional carrier/tracking details. Supports partial
|
|
204
|
+
* fulfillment (per-line quantities) and multiple fulfillments per order.
|
|
205
|
+
*/
|
|
206
|
+
async createFulfillment(input, actor, ctx) {
|
|
207
|
+
const orgId = resolveOrgId(actor ?? ctx?.actor ?? null);
|
|
208
|
+
const order = await this.deps.ordersRepository.findById(orgId, input.orderId, ctx);
|
|
209
|
+
if (!order)
|
|
210
|
+
return Err(new CommerceNotFoundError("Order not found."));
|
|
211
|
+
const orderLines = await this.deps.ordersRepository.findLineItemsByOrderId(input.orderId, ctx);
|
|
212
|
+
const linesById = new Map(orderLines.map((li) => [li.id, li]));
|
|
213
|
+
// Validate every requested line before writing anything: it must belong
|
|
214
|
+
// to the order, and requested + already-fulfilled must not exceed ordered.
|
|
215
|
+
const alreadyFulfilled = new Map();
|
|
216
|
+
for (const req of input.lineItems) {
|
|
217
|
+
const line = linesById.get(req.orderLineItemId);
|
|
218
|
+
if (!line) {
|
|
219
|
+
return Err(new CommerceValidationError(`Line item ${req.orderLineItemId} does not belong to this order.`));
|
|
220
|
+
}
|
|
221
|
+
const fulfilled = await this.deps.repository.getFulfilledQuantity(req.orderLineItemId, ctx);
|
|
222
|
+
alreadyFulfilled.set(req.orderLineItemId, fulfilled);
|
|
223
|
+
if (fulfilled + req.quantity > line.quantity) {
|
|
224
|
+
return Err(new CommerceValidationError(`Cannot fulfill ${req.quantity} of "${line.title}" — only ${line.quantity - fulfilled} remaining.`));
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
const status = input.status ?? "shipped";
|
|
228
|
+
const created = await this.deps.repository.create({
|
|
229
|
+
orderId: order.id,
|
|
230
|
+
type: input.type ?? "physical",
|
|
231
|
+
status,
|
|
232
|
+
carrier: input.carrier ?? null,
|
|
233
|
+
trackingNumber: input.trackingNumber ?? null,
|
|
234
|
+
trackingUrl: input.trackingUrl ?? null,
|
|
235
|
+
...(status === "shipped" ? { shippedAt: new Date() } : {}),
|
|
236
|
+
...(status === "delivered" ? { deliveredAt: new Date() } : {}),
|
|
237
|
+
...(order.customerId != null ? { customerId: order.customerId } : {}),
|
|
238
|
+
metadata: input.metadata ?? {},
|
|
239
|
+
}, ctx);
|
|
240
|
+
await this.deps.repository.createLineItems(input.lineItems.map((li) => ({
|
|
241
|
+
fulfillmentId: created.id,
|
|
242
|
+
orderLineItemId: li.orderLineItemId,
|
|
243
|
+
quantity: li.quantity,
|
|
244
|
+
})), ctx);
|
|
245
|
+
await this.deps.repository.createEvent({
|
|
246
|
+
fulfillmentId: created.id,
|
|
247
|
+
eventType: "created",
|
|
248
|
+
toStatus: status,
|
|
249
|
+
...(actor?.userId !== undefined ? { actorId: actor.userId } : {}),
|
|
250
|
+
description: input.trackingNumber
|
|
251
|
+
? `Shipment recorded (${input.trackingNumber})`
|
|
252
|
+
: "Fulfillment recorded",
|
|
253
|
+
}, ctx);
|
|
254
|
+
// Reflect per-line fulfillment progress on the order line items
|
|
255
|
+
for (const req of input.lineItems) {
|
|
256
|
+
const line = linesById.get(req.orderLineItemId);
|
|
257
|
+
const total = (alreadyFulfilled.get(req.orderLineItemId) ?? 0) + req.quantity;
|
|
258
|
+
await this.deps.ordersRepository.updateLineItem(req.orderLineItemId, { fulfillmentStatus: total >= line.quantity ? "fulfilled" : "partial" }, ctx);
|
|
259
|
+
}
|
|
260
|
+
const record = {
|
|
261
|
+
...toServiceRecord(created, input.lineItems.map((li) => ({
|
|
262
|
+
id: "",
|
|
263
|
+
fulfillmentId: created.id,
|
|
264
|
+
orderLineItemId: li.orderLineItemId,
|
|
265
|
+
quantity: li.quantity,
|
|
266
|
+
}))),
|
|
267
|
+
lineItems: input.lineItems.map((li) => ({
|
|
268
|
+
id: li.orderLineItemId,
|
|
269
|
+
title: linesById.get(li.orderLineItemId)?.title ?? "",
|
|
270
|
+
quantity: li.quantity,
|
|
271
|
+
})),
|
|
272
|
+
};
|
|
273
|
+
const afterHooks = this.deps.hooks.resolve("fulfillment.afterCreate");
|
|
274
|
+
const hookCtx = createHookContext({
|
|
275
|
+
actor: actor ?? ctx?.actor ?? null,
|
|
276
|
+
tx: ctx?.tx ?? null,
|
|
277
|
+
logger: createLogger("fulfillment"),
|
|
278
|
+
services: this.deps.services,
|
|
279
|
+
context: { moduleName: "fulfillment" },
|
|
280
|
+
database: { db: this.deps.database.db },
|
|
281
|
+
});
|
|
282
|
+
await runAfterHooks(afterHooks, null, record, "create", hookCtx);
|
|
283
|
+
return Ok(record);
|
|
284
|
+
}
|
|
201
285
|
async getByOrderId(orderId, ctx) {
|
|
202
286
|
const dbRecords = await this.deps.repository.findByOrderId(orderId, ctx);
|
|
203
287
|
// Hydrate each record with its associated line items
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { TxContext } from "../../../kernel/database/tx-context.js";
|
|
2
2
|
import type { DrizzleDatabase } from "../../../kernel/database/drizzle-db.js";
|
|
3
|
-
import { orders, orderLineItems, orderStatusHistory } from "../schema.js";
|
|
3
|
+
import { orders, orderLineItems, orderNotes, orderRefunds, orderStatusHistory } from "../schema.js";
|
|
4
4
|
export interface OrderLookupRow {
|
|
5
5
|
id: string;
|
|
6
6
|
orderNumber: string;
|
|
@@ -18,6 +18,10 @@ export type OrderLineItem = typeof orderLineItems.$inferSelect;
|
|
|
18
18
|
export type OrderLineItemInsert = typeof orderLineItems.$inferInsert;
|
|
19
19
|
export type OrderStatusHistory = typeof orderStatusHistory.$inferSelect;
|
|
20
20
|
export type OrderStatusHistoryInsert = typeof orderStatusHistory.$inferInsert;
|
|
21
|
+
export type OrderRefund = typeof orderRefunds.$inferSelect;
|
|
22
|
+
export type OrderRefundInsert = typeof orderRefunds.$inferInsert;
|
|
23
|
+
export type OrderNote = typeof orderNotes.$inferSelect;
|
|
24
|
+
export type OrderNoteInsert = typeof orderNotes.$inferInsert;
|
|
21
25
|
/**
|
|
22
26
|
* OrdersRepository provides type-safe database operations for orders.
|
|
23
27
|
*
|
|
@@ -44,6 +48,7 @@ export declare class OrdersRepository {
|
|
|
44
48
|
limit?: number;
|
|
45
49
|
offset?: number;
|
|
46
50
|
}, ctx?: TxContext): Promise<Order[]>;
|
|
51
|
+
findByIdempotencyKey(orgId: string, idempotencyKey: string, ctx?: TxContext): Promise<Order | undefined>;
|
|
47
52
|
create(data: OrderInsert, ctx?: TxContext): Promise<Order>;
|
|
48
53
|
update(id: string, data: Partial<Omit<OrderInsert, "id">>, ctx?: TxContext): Promise<Order | undefined>;
|
|
49
54
|
updateStatus(id: string, currentStatus: string, newStatus: string, ctx?: TxContext): Promise<Order | undefined>;
|
|
@@ -62,6 +67,18 @@ export declare class OrdersRepository {
|
|
|
62
67
|
order: Order;
|
|
63
68
|
lineItems: OrderLineItem[];
|
|
64
69
|
} | undefined>;
|
|
70
|
+
createRefund(data: OrderRefundInsert, ctx?: TxContext): Promise<OrderRefund>;
|
|
71
|
+
findRefundById(orgId: string, id: string, ctx?: TxContext): Promise<OrderRefund | undefined>;
|
|
72
|
+
findRefundsByOrderId(orderId: string, ctx?: TxContext): Promise<OrderRefund[]>;
|
|
73
|
+
markRefundUndone(id: string, undoneBy: string, ctx?: TxContext): Promise<OrderRefund | undefined>;
|
|
74
|
+
/**
|
|
75
|
+
* Sum of completed refunds performed by an operator for the local calendar
|
|
76
|
+
* day in the given timezone — the daily-cap accounting query.
|
|
77
|
+
*/
|
|
78
|
+
sumRefundsByOperatorToday(orgId: string, performedBy: string, timezone: string, ctx?: TxContext): Promise<number>;
|
|
79
|
+
createNote(data: OrderNoteInsert, ctx?: TxContext): Promise<OrderNote>;
|
|
80
|
+
findNotesByOrderId(orderId: string, ctx?: TxContext): Promise<OrderNote[]>;
|
|
81
|
+
deleteNote(orgId: string, orderId: string, noteId: string, ctx?: TxContext): Promise<boolean>;
|
|
65
82
|
getNextOrderNumber(ctx?: TxContext): Promise<string>;
|
|
66
83
|
}
|
|
67
84
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -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;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modules/orders/repository/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { eq, and, or, gte, lte, desc, sql } from "drizzle-orm";
|
|
2
|
-
import { orders, orderLineItems, orderStatusHistory } from "../schema.js";
|
|
2
|
+
import { orders, orderLineItems, orderNotes, orderRefunds, orderStatusHistory } from "../schema.js";
|
|
3
3
|
import { customers } from "../../customers/schema.js";
|
|
4
4
|
/**
|
|
5
5
|
* OrdersRepository provides type-safe database operations for orders.
|
|
@@ -106,6 +106,14 @@ export class OrdersRepository {
|
|
|
106
106
|
}
|
|
107
107
|
return query;
|
|
108
108
|
}
|
|
109
|
+
async findByIdempotencyKey(orgId, idempotencyKey, ctx) {
|
|
110
|
+
const db = this.getDb(ctx);
|
|
111
|
+
const rows = await db
|
|
112
|
+
.select()
|
|
113
|
+
.from(orders)
|
|
114
|
+
.where(and(eq(orders.organizationId, orgId), eq(orders.idempotencyKey, idempotencyKey)));
|
|
115
|
+
return rows[0];
|
|
116
|
+
}
|
|
109
117
|
async create(data, ctx) {
|
|
110
118
|
const db = this.getDb(ctx);
|
|
111
119
|
const rows = await db.insert(orders).values(data).returning();
|
|
@@ -223,6 +231,83 @@ export class OrdersRepository {
|
|
|
223
231
|
return { order, lineItems };
|
|
224
232
|
}
|
|
225
233
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
234
|
+
// Line-level refunds (issue #52)
|
|
235
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
236
|
+
async createRefund(data, ctx) {
|
|
237
|
+
const db = this.getDb(ctx);
|
|
238
|
+
const rows = await db.insert(orderRefunds).values(data).returning();
|
|
239
|
+
return rows[0];
|
|
240
|
+
}
|
|
241
|
+
async findRefundById(orgId, id, ctx) {
|
|
242
|
+
const db = this.getDb(ctx);
|
|
243
|
+
const rows = await db
|
|
244
|
+
.select()
|
|
245
|
+
.from(orderRefunds)
|
|
246
|
+
.where(and(eq(orderRefunds.organizationId, orgId), eq(orderRefunds.id, id)));
|
|
247
|
+
return rows[0];
|
|
248
|
+
}
|
|
249
|
+
async findRefundsByOrderId(orderId, ctx) {
|
|
250
|
+
const db = this.getDb(ctx);
|
|
251
|
+
return db
|
|
252
|
+
.select()
|
|
253
|
+
.from(orderRefunds)
|
|
254
|
+
.where(eq(orderRefunds.orderId, orderId))
|
|
255
|
+
.orderBy(desc(orderRefunds.createdAt));
|
|
256
|
+
}
|
|
257
|
+
async markRefundUndone(id, undoneBy, ctx) {
|
|
258
|
+
const db = this.getDb(ctx);
|
|
259
|
+
const rows = await db
|
|
260
|
+
.update(orderRefunds)
|
|
261
|
+
.set({ status: "undone", undoneAt: new Date(), undoneBy })
|
|
262
|
+
.where(and(eq(orderRefunds.id, id), eq(orderRefunds.status, "completed")))
|
|
263
|
+
.returning();
|
|
264
|
+
return rows[0];
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Sum of completed refunds performed by an operator for the local calendar
|
|
268
|
+
* day in the given timezone — the daily-cap accounting query.
|
|
269
|
+
*/
|
|
270
|
+
async sumRefundsByOperatorToday(orgId, performedBy, timezone, ctx) {
|
|
271
|
+
const db = this.getDb(ctx);
|
|
272
|
+
const result = await db.execute(sql `
|
|
273
|
+
SELECT COALESCE(sum(amount), 0)::int AS "total"
|
|
274
|
+
FROM order_refunds
|
|
275
|
+
WHERE organization_id = ${orgId}
|
|
276
|
+
AND performed_by = ${performedBy}
|
|
277
|
+
AND status = 'completed'
|
|
278
|
+
AND (created_at AT TIME ZONE ${timezone})::date = (now() AT TIME ZONE ${timezone})::date
|
|
279
|
+
`);
|
|
280
|
+
const rows = Array.isArray(result)
|
|
281
|
+
? result
|
|
282
|
+
: (result.rows ?? []);
|
|
283
|
+
const total = rows[0]?.total;
|
|
284
|
+
return typeof total === "bigint" ? Number(total) : (total ?? 0);
|
|
285
|
+
}
|
|
286
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
287
|
+
// Order notes (issue #56)
|
|
288
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
289
|
+
async createNote(data, ctx) {
|
|
290
|
+
const db = this.getDb(ctx);
|
|
291
|
+
const rows = await db.insert(orderNotes).values(data).returning();
|
|
292
|
+
return rows[0];
|
|
293
|
+
}
|
|
294
|
+
async findNotesByOrderId(orderId, ctx) {
|
|
295
|
+
const db = this.getDb(ctx);
|
|
296
|
+
return db
|
|
297
|
+
.select()
|
|
298
|
+
.from(orderNotes)
|
|
299
|
+
.where(eq(orderNotes.orderId, orderId))
|
|
300
|
+
.orderBy(desc(orderNotes.pinned), desc(orderNotes.createdAt));
|
|
301
|
+
}
|
|
302
|
+
async deleteNote(orgId, orderId, noteId, ctx) {
|
|
303
|
+
const db = this.getDb(ctx);
|
|
304
|
+
const rows = await db
|
|
305
|
+
.delete(orderNotes)
|
|
306
|
+
.where(and(eq(orderNotes.id, noteId), eq(orderNotes.orderId, orderId), eq(orderNotes.organizationId, orgId)))
|
|
307
|
+
.returning();
|
|
308
|
+
return rows.length > 0;
|
|
309
|
+
}
|
|
310
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
226
311
|
// Order Number Generation
|
|
227
312
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
228
313
|
async getNextOrderNumber(ctx) {
|