@porulle/core 0.10.1 → 0.10.3

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.
@@ -1 +1 @@
1
- {"version":3,"file":"checkout.d.ts","sourceRoot":"","sources":["../../../../src/interfaces/rest/routes/checkout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAoBzD,OAAO,EAAE,KAAK,MAAM,EAAwC,MAAM,aAAa,CAAC;AAKhF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAEpD;;;;;;GAMG;AACH,wBAAsB,2BAA2B,CAC/C,SAAS,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,WAAW,CAAC,EAC1C,KAAK,EAAE,KAAK,GAAG,IAAI,EACnB,UAAU,EAAE,MAAM,GAAG,SAAS,GAC7B,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAmC7B;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,gCA4S5C"}
1
+ {"version":3,"file":"checkout.d.ts","sourceRoot":"","sources":["../../../../src/interfaces/rest/routes/checkout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAoBzD,OAAO,EAAE,KAAK,MAAM,EAAwC,MAAM,aAAa,CAAC;AAKhF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAEpD;;;;;;GAMG;AACH,wBAAsB,2BAA2B,CAC/C,SAAS,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,WAAW,CAAC,EAC1C,KAAK,EAAE,KAAK,GAAG,IAAI,EACnB,UAAU,EAAE,MAAM,GAAG,SAAS,GAC7B,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAmC7B;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,gCA+S5C"}
@@ -130,8 +130,11 @@ export function checkoutRoutes(kernel) {
130
130
  resolveCurrentPrices,
131
131
  checkInventoryAvailability,
132
132
  applyPromotionCodes,
133
- calculateTax,
133
+ // Shipping BEFORE tax: calculateTax reads data.shippingTotal for
134
+ // appliesToShipping rates, so shipping must be computed first or shipping
135
+ // tax is silently never collected (audit C1).
134
136
  calculateShipping,
137
+ calculateTax,
135
138
  ...kernel.hooks.resolve("checkout.beforePayment"),
136
139
  validatePaymentMethod,
137
140
  ];
@@ -1 +1 @@
1
- {"version":3,"file":"orders.d.ts","sourceRoot":"","sources":["../../../../src/interfaces/rest/routes/orders.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAAE,KAAK,MAAM,EAAiE,MAAM,aAAa,CAAC;AAIzG,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,gCAgRzC"}
1
+ {"version":3,"file":"orders.d.ts","sourceRoot":"","sources":["../../../../src/interfaces/rest/routes/orders.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAAE,KAAK,MAAM,EAAiE,MAAM,aAAa,CAAC;AAKzG,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,gCAkTzC"}
@@ -1,6 +1,7 @@
1
1
  import { OpenAPIHono } from "@hono/zod-openapi";
2
- import { changeOrderStatusRoute, listOrdersRoute, orderLookupRoute, getOrderRoute, getOrderFulfillmentsRoute, createOrderRoute, refundOrderRoute, captureOrderRoute, createOrderFulfillmentRoute, addOrderLineItemRoute, updateOrderLineItemRoute, removeOrderLineItemRoute, refundOrderLinesRoute, undoOrderRefundRoute, listOrderRefundsRoute, refundCapStatusRoute, createOrderNoteRoute, listOrderNotesRoute, deleteOrderNoteRoute, orderTimelineRoute } from "../schemas/orders.js";
2
+ import { changeOrderStatusRoute, listOrdersRoute, orderLookupRoute, getOrderRoute, getOrderFulfillmentsRoute, createOrderRoute, quoteOrderRoute, refundOrderRoute, captureOrderRoute, createOrderFulfillmentRoute, addOrderLineItemRoute, updateOrderLineItemRoute, removeOrderLineItemRoute, refundOrderLinesRoute, undoOrderRefundRoute, listOrderRefundsRoute, refundCapStatusRoute, createOrderNoteRoute, listOrderNotesRoute, deleteOrderNoteRoute, orderTimelineRoute } from "../schemas/orders.js";
3
3
  import { isUUID, mapErrorToResponse, mapErrorToStatus, parsePagination } from "../utils.js";
4
+ import { computeOrderPricing } from "../../../modules/orders/quote.js";
4
5
  import { assertPermission } from "../../../auth/permissions.js";
5
6
  export function orderRoutes(kernel) {
6
7
  const router = new OpenAPIHono();
@@ -69,6 +70,30 @@ export function orderRoutes(kernel) {
69
70
  return c.json({ data: result.value }, 201);
70
71
  });
71
72
  // @ts-expect-error -- openapi handler union return type
73
+ router.openapi(quoteOrderRoute, async (c) => {
74
+ try {
75
+ assertPermission(c.get("actor"), "orders:manage");
76
+ }
77
+ catch (error) {
78
+ return c.json(mapErrorToResponse(error), mapErrorToStatus(error));
79
+ }
80
+ const body = c.req.valid("json");
81
+ try {
82
+ const breakdown = await computeOrderPricing(kernel, {
83
+ currency: body.currency,
84
+ lineItems: body.lineItems,
85
+ ...(body.customerId !== undefined ? { customerId: body.customerId } : {}),
86
+ ...(body.customerGroupIds !== undefined ? { customerGroupIds: body.customerGroupIds } : {}),
87
+ ...(body.promotionCodes !== undefined ? { promotionCodes: body.promotionCodes } : {}),
88
+ ...(body.shippingAddress !== undefined ? { shippingAddress: body.shippingAddress } : {}),
89
+ }, c.get("actor"));
90
+ return c.json({ data: breakdown });
91
+ }
92
+ catch (error) {
93
+ return c.json(mapErrorToResponse(error), mapErrorToStatus(error));
94
+ }
95
+ });
96
+ // @ts-expect-error -- openapi handler union return type
72
97
  router.openapi(refundOrderRoute, async (c) => {
73
98
  const body = c.req.valid("json");
74
99
  const result = await kernel.services.orders.refund(c.req.param("id"), c.get("actor"), body?.reason ?? "refunded", undefined, body?.amount !== undefined ? { amount: body.amount } : undefined);
@@ -1 +1 @@
1
- {"version":3,"file":"promotions.d.ts","sourceRoot":"","sources":["../../../../src/interfaces/rest/routes/promotions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAGzD,OAAO,EAAE,KAAK,MAAM,EAAqD,MAAM,aAAa,CAAC;AAG7F,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,gCA4G7C"}
1
+ {"version":3,"file":"promotions.d.ts","sourceRoot":"","sources":["../../../../src/interfaces/rest/routes/promotions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAGzD,OAAO,EAAE,KAAK,MAAM,EAAqD,MAAM,aAAa,CAAC;AAG7F,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,gCAiH7C"}
@@ -46,7 +46,12 @@ export function promotionRoutes(kernel) {
46
46
  const payload = c.req.valid("json");
47
47
  const actor = c.get("actor");
48
48
  const orgId = resolveOrgId(actor);
49
- const result = await kernel.services.promotions.validate(payload.code, {
49
+ // Apply (not just validate) so the response carries the authoritative
50
+ // discount the cart would receive — the same computation checkout runs.
51
+ // Returning only the promotion forces callers to re-derive the amount, which
52
+ // drifts from checkout and shows customers a wrong number. apply() still
53
+ // Err's on an invalid / inapplicable code (it validates first).
54
+ const result = await kernel.services.promotions.apply(payload.code, {
50
55
  orgId,
51
56
  currency: payload.currency,
52
57
  subtotal: payload.subtotal,
@@ -36,6 +36,213 @@ export declare const CreateOrderBodySchema: z.ZodObject<{
36
36
  metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
37
37
  }, z.core.$strip>>;
38
38
  }, z.core.$strip>;
39
+ export declare const QuoteOrderBodySchema: z.ZodObject<{
40
+ currency: z.ZodString;
41
+ lineItems: z.ZodArray<z.ZodObject<{
42
+ entityId: z.ZodUUID;
43
+ entityType: z.ZodOptional<z.ZodString>;
44
+ variantId: z.ZodOptional<z.ZodUUID>;
45
+ quantity: z.ZodNumber;
46
+ title: z.ZodOptional<z.ZodString>;
47
+ }, z.core.$strip>>;
48
+ customerId: z.ZodOptional<z.ZodUUID>;
49
+ customerGroupIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
50
+ promotionCodes: z.ZodOptional<z.ZodArray<z.ZodString>>;
51
+ shippingAddress: z.ZodOptional<z.ZodObject<{
52
+ line1: z.ZodString;
53
+ line2: z.ZodOptional<z.ZodString>;
54
+ city: z.ZodString;
55
+ state: z.ZodOptional<z.ZodString>;
56
+ postalCode: z.ZodString;
57
+ country: z.ZodString;
58
+ }, z.core.$strip>>;
59
+ }, z.core.$strip>;
60
+ export declare const QuoteOrderResultSchema: z.ZodObject<{
61
+ data: z.ZodObject<{
62
+ currency: z.ZodString;
63
+ subtotal: z.ZodNumber;
64
+ discountTotal: z.ZodNumber;
65
+ shippingTotal: z.ZodNumber;
66
+ taxTotal: z.ZodNumber;
67
+ grandTotal: z.ZodNumber;
68
+ lineItems: z.ZodArray<z.ZodObject<{
69
+ entityId: z.ZodString;
70
+ variantId: z.ZodOptional<z.ZodString>;
71
+ quantity: z.ZodNumber;
72
+ unitPrice: z.ZodNumber;
73
+ totalPrice: z.ZodNumber;
74
+ discountAmount: z.ZodNumber;
75
+ taxAmount: z.ZodNumber;
76
+ }, z.core.$strip>>;
77
+ }, z.core.$strip>;
78
+ }, z.core.$strip>;
79
+ export declare const quoteOrderRoute: {
80
+ method: "post";
81
+ path: "/quote";
82
+ tags: string[];
83
+ summary: string;
84
+ description: string;
85
+ request: {
86
+ body: {
87
+ content: {
88
+ "application/json": {
89
+ schema: z.ZodObject<{
90
+ currency: z.ZodString;
91
+ lineItems: z.ZodArray<z.ZodObject<{
92
+ entityId: z.ZodUUID;
93
+ entityType: z.ZodOptional<z.ZodString>;
94
+ variantId: z.ZodOptional<z.ZodUUID>;
95
+ quantity: z.ZodNumber;
96
+ title: z.ZodOptional<z.ZodString>;
97
+ }, z.core.$strip>>;
98
+ customerId: z.ZodOptional<z.ZodUUID>;
99
+ customerGroupIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
100
+ promotionCodes: z.ZodOptional<z.ZodArray<z.ZodString>>;
101
+ shippingAddress: z.ZodOptional<z.ZodObject<{
102
+ line1: z.ZodString;
103
+ line2: z.ZodOptional<z.ZodString>;
104
+ city: z.ZodString;
105
+ state: z.ZodOptional<z.ZodString>;
106
+ postalCode: z.ZodString;
107
+ country: z.ZodString;
108
+ }, z.core.$strip>>;
109
+ }, z.core.$strip>;
110
+ };
111
+ };
112
+ required: true;
113
+ };
114
+ };
115
+ responses: {
116
+ 400: {
117
+ readonly content: {
118
+ readonly "application/json": {
119
+ readonly schema: z.ZodObject<{
120
+ error: z.ZodObject<{
121
+ code: z.ZodString;
122
+ message: z.ZodString;
123
+ details: z.ZodOptional<z.ZodObject<{
124
+ issues: z.ZodOptional<z.ZodArray<z.ZodObject<{
125
+ path: z.ZodString;
126
+ message: z.ZodString;
127
+ code: z.ZodString;
128
+ }, z.core.$strip>>>;
129
+ }, z.core.$strip>>;
130
+ }, z.core.$strip>;
131
+ }, z.core.$strip>;
132
+ };
133
+ };
134
+ readonly description: "Business logic error.";
135
+ };
136
+ 401: {
137
+ readonly content: {
138
+ readonly "application/json": {
139
+ readonly schema: z.ZodObject<{
140
+ error: z.ZodObject<{
141
+ code: z.ZodString;
142
+ message: z.ZodString;
143
+ details: z.ZodOptional<z.ZodObject<{
144
+ issues: z.ZodOptional<z.ZodArray<z.ZodObject<{
145
+ path: z.ZodString;
146
+ message: z.ZodString;
147
+ code: z.ZodString;
148
+ }, z.core.$strip>>>;
149
+ }, z.core.$strip>>;
150
+ }, z.core.$strip>;
151
+ }, z.core.$strip>;
152
+ };
153
+ };
154
+ readonly description: "Authentication required.";
155
+ };
156
+ 403: {
157
+ readonly content: {
158
+ readonly "application/json": {
159
+ readonly schema: z.ZodObject<{
160
+ error: z.ZodObject<{
161
+ code: z.ZodString;
162
+ message: z.ZodString;
163
+ details: z.ZodOptional<z.ZodObject<{
164
+ issues: z.ZodOptional<z.ZodArray<z.ZodObject<{
165
+ path: z.ZodString;
166
+ message: z.ZodString;
167
+ code: z.ZodString;
168
+ }, z.core.$strip>>>;
169
+ }, z.core.$strip>>;
170
+ }, z.core.$strip>;
171
+ }, z.core.$strip>;
172
+ };
173
+ };
174
+ readonly description: "Insufficient permissions.";
175
+ };
176
+ 404: {
177
+ readonly content: {
178
+ readonly "application/json": {
179
+ readonly schema: z.ZodObject<{
180
+ error: z.ZodObject<{
181
+ code: z.ZodString;
182
+ message: z.ZodString;
183
+ details: z.ZodOptional<z.ZodObject<{
184
+ issues: z.ZodOptional<z.ZodArray<z.ZodObject<{
185
+ path: z.ZodString;
186
+ message: z.ZodString;
187
+ code: z.ZodString;
188
+ }, z.core.$strip>>>;
189
+ }, z.core.$strip>>;
190
+ }, z.core.$strip>;
191
+ }, z.core.$strip>;
192
+ };
193
+ };
194
+ readonly description: "Resource not found.";
195
+ };
196
+ 422: {
197
+ readonly content: {
198
+ readonly "application/json": {
199
+ readonly schema: z.ZodObject<{
200
+ error: z.ZodObject<{
201
+ code: z.ZodString;
202
+ message: z.ZodString;
203
+ details: z.ZodOptional<z.ZodObject<{
204
+ issues: z.ZodOptional<z.ZodArray<z.ZodObject<{
205
+ path: z.ZodString;
206
+ message: z.ZodString;
207
+ code: z.ZodString;
208
+ }, z.core.$strip>>>;
209
+ }, z.core.$strip>>;
210
+ }, z.core.$strip>;
211
+ }, z.core.$strip>;
212
+ };
213
+ };
214
+ readonly description: "Validation error.";
215
+ };
216
+ 200: {
217
+ content: {
218
+ "application/json": {
219
+ schema: z.ZodObject<{
220
+ data: z.ZodObject<{
221
+ currency: z.ZodString;
222
+ subtotal: z.ZodNumber;
223
+ discountTotal: z.ZodNumber;
224
+ shippingTotal: z.ZodNumber;
225
+ taxTotal: z.ZodNumber;
226
+ grandTotal: z.ZodNumber;
227
+ lineItems: z.ZodArray<z.ZodObject<{
228
+ entityId: z.ZodString;
229
+ variantId: z.ZodOptional<z.ZodString>;
230
+ quantity: z.ZodNumber;
231
+ unitPrice: z.ZodNumber;
232
+ totalPrice: z.ZodNumber;
233
+ discountAmount: z.ZodNumber;
234
+ taxAmount: z.ZodNumber;
235
+ }, z.core.$strip>>;
236
+ }, z.core.$strip>;
237
+ }, z.core.$strip>;
238
+ };
239
+ };
240
+ description: string;
241
+ };
242
+ };
243
+ } & {
244
+ getRoutingPath(): "/quote";
245
+ };
39
246
  export declare const OrderDataResponseSchema: z.ZodObject<{
40
247
  data: import("drizzle-zod").BuildSchema<"select", {
41
248
  id: import("drizzle-orm/pg-core").PgColumn<{
@@ -1 +1 @@
1
- {"version":3,"file":"orders.d.ts","sourceRoot":"","sources":["../../../../src/interfaces/rest/schemas/orders.ts"],"names":[],"mappings":"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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoBtC,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmB3B,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoB3B,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoB5B,CAAC;AAEH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;iBAYF,CAAC;AAEtC,eAAO,MAAM,6BAA6B;;iBAEF,CAAC;AAOzC,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoBhC,CAAC;AAEH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoBnC,CAAC;AAEH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgBnC,CAAC;AAEH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqBjC,CAAC;AAIH,eAAO,MAAM,qBAAqB;;;;;;iBAMF,CAAC;AASjC,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAchC,CAAC;AAEH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAU/B,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAUhC,CAAC;AAEH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAS/B,CAAC;AAIH,eAAO,MAAM,yBAAyB;;;iBAGF,CAAC;AASrC,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAa/B,CAAC;AAEH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAU9B,CAAC;AAEH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAU/B,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAU7B,CAAC"}
1
+ {"version":3,"file":"orders.d.ts","sourceRoot":"","sources":["../../../../src/interfaces/rest/schemas/orders.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAe,MAAM,mBAAmB,CAAC;AAMnD,eAAO,MAAM,2BAA2B;;;iBAMF,CAAC;AAEvC,eAAO,MAAM,qBAAqB;;;iBAIF,CAAC;AAEjC,eAAO,MAAM,sBAAsB;;iBAGF,CAAC;AAgBlC,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;iBAgBF,CAAC;AAmBjC,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;iBAOF,CAAC;AAEhC,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;iBAkBL,CAAC;AAE/B,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoB1B,CAAC;AAIH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAgB,CAAC;AAUrD,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkB1B,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmB3B,CAAC;AAEH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiBxB,CAAC;AAEH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAepC,CAAC;AAEH,eAAO,MAAM,2BAA2B;;;;;;;;;;;iBAWF,CAAC;AAEvC,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoBtC,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmB3B,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoB3B,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoB5B,CAAC;AAEH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;iBAYF,CAAC;AAEtC,eAAO,MAAM,6BAA6B;;iBAEF,CAAC;AAOzC,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoBhC,CAAC;AAEH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoBnC,CAAC;AAEH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgBnC,CAAC;AAEH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqBjC,CAAC;AAIH,eAAO,MAAM,qBAAqB;;;;;;iBAMF,CAAC;AASjC,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAchC,CAAC;AAEH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAU/B,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAUhC,CAAC;AAEH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAS/B,CAAC;AAIH,eAAO,MAAM,yBAAyB;;;iBAGF,CAAC;AASrC,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAa/B,CAAC;AAEH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAU9B,CAAC;AAEH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAU/B,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAU7B,CAAC"}
@@ -48,6 +48,68 @@ export const CreateOrderBodySchema = z.object({
48
48
  metadata: z.record(z.string(), z.unknown()).optional(),
49
49
  lineItems: z.array(CreateOrderLineItemSchema).min(1),
50
50
  }).openapi("CreateOrderRequest");
51
+ const QuoteLineItemSchema = z.object({
52
+ entityId: z.uuid().openapi({ example: "550e8400-e29b-41d4-a716-446655440000" }),
53
+ entityType: z.string().optional().openapi({ example: "product" }),
54
+ variantId: z.uuid().optional(),
55
+ quantity: z.number().int().positive().openapi({ example: 2 }),
56
+ title: z.string().optional(),
57
+ });
58
+ const QuoteAddressSchema = z.object({
59
+ line1: z.string(),
60
+ line2: z.string().optional(),
61
+ city: z.string(),
62
+ state: z.string().optional(),
63
+ postalCode: z.string(),
64
+ country: z.string(),
65
+ });
66
+ export const QuoteOrderBodySchema = z.object({
67
+ currency: z.string().min(3).max(3).openapi({ example: "USD" }),
68
+ lineItems: z.array(QuoteLineItemSchema).min(1),
69
+ customerId: z.uuid().optional(),
70
+ customerGroupIds: z.array(z.string()).optional(),
71
+ promotionCodes: z.array(z.string()).optional(),
72
+ shippingAddress: QuoteAddressSchema.optional(),
73
+ }).openapi("QuoteOrderRequest");
74
+ export const QuoteOrderResultSchema = z.object({
75
+ data: z.object({
76
+ currency: z.string(),
77
+ subtotal: z.number().openapi({ description: "Minor units (e.g. cents)." }),
78
+ discountTotal: z.number(),
79
+ shippingTotal: z.number(),
80
+ taxTotal: z.number(),
81
+ grandTotal: z.number(),
82
+ lineItems: z.array(z.object({
83
+ entityId: z.string(),
84
+ variantId: z.string().optional(),
85
+ quantity: z.number(),
86
+ unitPrice: z.number(),
87
+ totalPrice: z.number(),
88
+ discountAmount: z.number(),
89
+ taxAmount: z.number(),
90
+ })),
91
+ }),
92
+ }).openapi("QuoteOrderResult");
93
+ export const quoteOrderRoute = createRoute({
94
+ method: "post",
95
+ path: "/quote",
96
+ tags: ["Orders"],
97
+ summary: "Quote an order's charges without creating it",
98
+ description: "Computes subtotal, discount, shipping, tax and grand total for the given line items (+ optional address, customer, promotion codes) using the SAME pricing pipeline checkout runs — so a manual/draft order previews exactly what it will be charged. Persists nothing.",
99
+ request: {
100
+ body: {
101
+ content: { "application/json": { schema: QuoteOrderBodySchema } },
102
+ required: true,
103
+ },
104
+ },
105
+ responses: {
106
+ 200: {
107
+ content: { "application/json": { schema: QuoteOrderResultSchema } },
108
+ description: "Computed order charges (authoritative — matches checkout).",
109
+ },
110
+ ...errorResponses,
111
+ },
112
+ });
51
113
  // ─── Response Schemas ───────────────────────────────────────────────────────
52
114
  export const OrderDataResponseSchema = OrderResponse;
53
115
  // ─── Path Params ────────────────────────────────────────────────────────────
@@ -48,6 +48,24 @@ export declare const ValidatePromotionBodySchema: z.ZodObject<{
48
48
  export declare const PromotionResponseSchema: z.ZodObject<{
49
49
  data: z.ZodRecord<z.ZodString, z.ZodUnknown>;
50
50
  }, z.core.$strip>;
51
+ export declare const PromotionValidationResultSchema: z.ZodObject<{
52
+ data: z.ZodObject<{
53
+ totalDiscount: z.ZodNumber;
54
+ freeShipping: z.ZodBoolean;
55
+ applied: z.ZodArray<z.ZodObject<{
56
+ promotionId: z.ZodString;
57
+ code: z.ZodOptional<z.ZodString>;
58
+ type: z.ZodString;
59
+ discountAmount: z.ZodNumber;
60
+ freeShipping: z.ZodBoolean;
61
+ description: z.ZodString;
62
+ }, z.core.$strip>>;
63
+ rejectedCodes: z.ZodArray<z.ZodObject<{
64
+ code: z.ZodString;
65
+ reason: z.ZodString;
66
+ }, z.core.$strip>>;
67
+ }, z.core.$strip>;
68
+ }, z.core.$strip>;
51
69
  export declare const listPromotionsRoute: {
52
70
  method: "get";
53
71
  path: "/";
@@ -371,7 +389,22 @@ export declare const validatePromotionRoute: {
371
389
  content: {
372
390
  "application/json": {
373
391
  schema: z.ZodObject<{
374
- data: z.ZodRecord<z.ZodString, z.ZodUnknown>;
392
+ data: z.ZodObject<{
393
+ totalDiscount: z.ZodNumber;
394
+ freeShipping: z.ZodBoolean;
395
+ applied: z.ZodArray<z.ZodObject<{
396
+ promotionId: z.ZodString;
397
+ code: z.ZodOptional<z.ZodString>;
398
+ type: z.ZodString;
399
+ discountAmount: z.ZodNumber;
400
+ freeShipping: z.ZodBoolean;
401
+ description: z.ZodString;
402
+ }, z.core.$strip>>;
403
+ rejectedCodes: z.ZodArray<z.ZodObject<{
404
+ code: z.ZodString;
405
+ reason: z.ZodString;
406
+ }, z.core.$strip>>;
407
+ }, z.core.$strip>;
375
408
  }, z.core.$strip>;
376
409
  };
377
410
  };
@@ -1 +1 @@
1
- {"version":3,"file":"promotions.d.ts","sourceRoot":"","sources":["../../../../src/interfaces/rest/schemas/promotions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAe,MAAM,mBAAmB,CAAC;AAKnD,OAAO,EACL,yBAAyB,EAE1B,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,yBAAyB,EAAE,CAAC;AAErC,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAErC,CAAC;AAEF,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;iBAaF,CAAC;AAIvC,eAAO,MAAM,uBAAuB;;iBAEL,CAAC;AAUhC,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiB9B,CAAC;AAEH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoB/B,CAAC;AAEH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoBjC,CAAC;AAEH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsB/B,CAAC;AAEH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAenC,CAAC"}
1
+ {"version":3,"file":"promotions.d.ts","sourceRoot":"","sources":["../../../../src/interfaces/rest/schemas/promotions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAe,MAAM,mBAAmB,CAAC;AAKnD,OAAO,EACL,yBAAyB,EAE1B,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,yBAAyB,EAAE,CAAC;AAErC,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAErC,CAAC;AAEF,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;iBAaF,CAAC;AAIvC,eAAO,MAAM,uBAAuB;;iBAEL,CAAC;AAKhC,eAAO,MAAM,+BAA+B;;;;;;;;;;;;;;;;;iBAoBL,CAAC;AAUxC,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiB9B,CAAC;AAEH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoB/B,CAAC;AAEH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoBjC,CAAC;AAEH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsB/B,CAAC;AAEH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAenC,CAAC"}
@@ -22,6 +22,30 @@ export const ValidatePromotionBodySchema = z.object({
22
22
  export const PromotionResponseSchema = z.object({
23
23
  data: z.record(z.string(), z.unknown()),
24
24
  }).openapi("PromotionResponse");
25
+ // The authoritative result of applying a code to a cart — the SAME computation
26
+ // checkout uses, so a storefront can show the exact discount the order will get
27
+ // (no client-side re-derivation, which would drift from checkout).
28
+ export const PromotionValidationResultSchema = z.object({
29
+ data: z.object({
30
+ totalDiscount: z.number().openapi({
31
+ example: 1000,
32
+ description: "Discount this code applies to the cart, in the cart's minor units (e.g. cents).",
33
+ }),
34
+ freeShipping: z.boolean().openapi({ example: false }),
35
+ applied: z.array(z.object({
36
+ promotionId: z.string(),
37
+ code: z.string().optional(),
38
+ type: z.string(),
39
+ discountAmount: z.number(),
40
+ freeShipping: z.boolean(),
41
+ description: z.string(),
42
+ })).openapi({ description: "The promotion(s) applied by this code." }),
43
+ rejectedCodes: z.array(z.object({
44
+ code: z.string(),
45
+ reason: z.string(),
46
+ })),
47
+ }),
48
+ }).openapi("PromotionValidationResult");
25
49
  // ─── Path Params ────────────────────────────────────────────────────────────
26
50
  const PromotionIdParam = z.object({
27
51
  id: z.uuid().openapi({ example: "550e8400-e29b-41d4-a716-446655440000" }),
@@ -81,8 +105,8 @@ export const validatePromotionRoute = createRoute({
81
105
  },
82
106
  responses: {
83
107
  200: {
84
- content: { "application/json": { schema: PromotionResponseSchema } },
85
- description: "Promotion validation result.",
108
+ content: { "application/json": { schema: PromotionValidationResultSchema } },
109
+ description: "The code is valid for this cart: the authoritative discount it applies (same computation as checkout).",
86
110
  },
87
111
  ...errorResponses,
88
112
  },
@@ -0,0 +1,47 @@
1
+ import type { Kernel } from "../../runtime/kernel.js";
2
+ import type { Actor } from "../../auth/types.js";
3
+ import type { ShippingAddress } from "../shipping/calculator.js";
4
+ export interface OrderPricingInput {
5
+ currency: string;
6
+ lineItems: Array<{
7
+ entityId: string;
8
+ entityType?: string;
9
+ variantId?: string;
10
+ quantity: number;
11
+ title?: string;
12
+ }>;
13
+ customerId?: string;
14
+ customerGroupIds?: string[];
15
+ promotionCodes?: string[];
16
+ shippingAddress?: ShippingAddress;
17
+ }
18
+ export interface OrderPricingBreakdown {
19
+ currency: string;
20
+ subtotal: number;
21
+ discountTotal: number;
22
+ shippingTotal: number;
23
+ taxTotal: number;
24
+ grandTotal: number;
25
+ lineItems: Array<{
26
+ entityId: string;
27
+ variantId?: string;
28
+ quantity: number;
29
+ unitPrice: number;
30
+ totalPrice: number;
31
+ discountAmount: number;
32
+ taxAmount: number;
33
+ }>;
34
+ }
35
+ /**
36
+ * The single pricing authority for an order. Runs the SAME hooks checkout runs —
37
+ * `resolveCurrentPrices → applyPromotionCodes → calculateShipping → calculateTax`
38
+ * (shipping before tax so `appliesToShipping` rates fire) — against a synthetic
39
+ * `CheckoutData`, with NO cart claim, inventory check, or payment side effects.
40
+ *
41
+ * Because it reuses the checkout hooks verbatim, a manual-order quote produced
42
+ * here equals what checkout will actually charge for the same inputs — there is
43
+ * no second pricing implementation to drift. Used by `POST /api/orders/quote`
44
+ * and available to any manual/draft-order flow.
45
+ */
46
+ export declare function computeOrderPricing(kernel: Kernel, input: OrderPricingInput, actor: Actor | null, tx?: unknown): Promise<OrderPricingBreakdown>;
47
+ //# sourceMappingURL=quote.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quote.d.ts","sourceRoot":"","sources":["../../../src/modules/orders/quote.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAGjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAGjE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,KAAK,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC,CAAC;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,eAAe,CAAC,EAAE,eAAe,CAAC;CACnC;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,KAAK,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;QACnB,cAAc,EAAE,MAAM,CAAC;QACvB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC,CAAC;CACJ;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,iBAAiB,EACxB,KAAK,EAAE,KAAK,GAAG,IAAI,EACnB,EAAE,CAAC,EAAE,OAAO,GACX,OAAO,CAAC,qBAAqB,CAAC,CAiEhC"}
@@ -0,0 +1,74 @@
1
+ import { createHookContext } from "../../kernel/hooks/create-context.js";
2
+ import { runBeforeHooks } from "../../kernel/hooks/executor.js";
3
+ import { resolveCurrentPrices, applyPromotionCodes, calculateShipping, calculateTax, } from "../../hooks/checkout.js";
4
+ import { makeId } from "../../utils/id.js";
5
+ /**
6
+ * The single pricing authority for an order. Runs the SAME hooks checkout runs —
7
+ * `resolveCurrentPrices → applyPromotionCodes → calculateShipping → calculateTax`
8
+ * (shipping before tax so `appliesToShipping` rates fire) — against a synthetic
9
+ * `CheckoutData`, with NO cart claim, inventory check, or payment side effects.
10
+ *
11
+ * Because it reuses the checkout hooks verbatim, a manual-order quote produced
12
+ * here equals what checkout will actually charge for the same inputs — there is
13
+ * no second pricing implementation to drift. Used by `POST /api/orders/quote`
14
+ * and available to any manual/draft-order flow.
15
+ */
16
+ export async function computeOrderPricing(kernel, input, actor, tx) {
17
+ const data = {
18
+ checkoutId: makeId(),
19
+ cartId: "",
20
+ currency: input.currency,
21
+ paymentMethodId: "",
22
+ lineItems: input.lineItems.map((li) => ({
23
+ id: `${li.entityId}:${li.variantId ?? "_"}`,
24
+ entityId: li.entityId,
25
+ ...(li.entityType !== undefined ? { entityType: li.entityType } : {}),
26
+ ...(li.variantId !== undefined ? { variantId: li.variantId } : {}),
27
+ title: li.title ?? li.entityId,
28
+ quantity: li.quantity,
29
+ })),
30
+ subtotal: 0,
31
+ discountTotal: 0,
32
+ taxTotal: 0,
33
+ shippingTotal: 0,
34
+ total: 0,
35
+ ...(input.customerId !== undefined ? { customerId: input.customerId } : {}),
36
+ ...(input.customerGroupIds !== undefined
37
+ ? { customerGroupIds: input.customerGroupIds }
38
+ : {}),
39
+ ...(input.promotionCodes !== undefined
40
+ ? { promotionCodes: input.promotionCodes }
41
+ : {}),
42
+ ...(input.shippingAddress !== undefined
43
+ ? { shippingAddress: input.shippingAddress }
44
+ : {}),
45
+ };
46
+ const context = createHookContext({
47
+ actor,
48
+ logger: kernel.logger,
49
+ services: kernel.services,
50
+ context: { moduleName: "orders" },
51
+ origin: "rest",
52
+ database: { db: kernel.database.db },
53
+ });
54
+ if (tx !== undefined)
55
+ context.tx = tx;
56
+ const priced = await runBeforeHooks([resolveCurrentPrices, applyPromotionCodes, calculateShipping, calculateTax], data, "create", context);
57
+ return {
58
+ currency: priced.currency,
59
+ subtotal: priced.subtotal,
60
+ discountTotal: priced.discountTotal,
61
+ shippingTotal: priced.shippingTotal,
62
+ taxTotal: priced.taxTotal,
63
+ grandTotal: priced.total,
64
+ lineItems: priced.lineItems.map((li) => ({
65
+ entityId: li.entityId,
66
+ ...(li.variantId !== undefined ? { variantId: li.variantId } : {}),
67
+ quantity: li.quantity,
68
+ unitPrice: li.resolvedUnitPrice ?? 0,
69
+ totalPrice: li.resolvedTotal ?? 0,
70
+ discountAmount: li.discountAmount ?? 0,
71
+ taxAmount: li.taxAmount ?? 0,
72
+ })),
73
+ };
74
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/modules/tax/service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/modules/tax/service.ts"],"names":[],"mappings":"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"}
@@ -30,7 +30,10 @@ export class TaxService {
30
30
  if (this.repository && orgId && params.toAddress) {
31
31
  const matched = await this.matchRuntimeRates(orgId, params.toAddress, ctx);
32
32
  if (matched.length > 0) {
33
- const taxableAmount = params.lineItems.reduce((sum, lineItem) => sum + lineItem.unitPrice * lineItem.quantity - (lineItem.discount ?? 0), 0);
33
+ // Subtract the order-level discount from the taxable base too (audit
34
+ // C2a) — otherwise runtime rates tax the pre-discount subtotal and
35
+ // over-collect on every discounted order, unlike the class-based path.
36
+ const taxableAmount = Math.max(0, params.lineItems.reduce((sum, lineItem) => sum + lineItem.unitPrice * lineItem.quantity - (lineItem.discount ?? 0), 0) - (params.orderDiscount ?? 0));
34
37
  let amountToCollect = 0;
35
38
  const breakdown = {};
36
39
  for (const rate of matched) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@porulle/core",
3
- "version": "0.10.1",
3
+ "version": "0.10.3",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -164,8 +164,11 @@ export function checkoutRoutes(kernel: Kernel) {
164
164
  resolveCurrentPrices,
165
165
  checkInventoryAvailability,
166
166
  applyPromotionCodes,
167
- calculateTax,
167
+ // Shipping BEFORE tax: calculateTax reads data.shippingTotal for
168
+ // appliesToShipping rates, so shipping must be computed first or shipping
169
+ // tax is silently never collected (audit C1).
168
170
  calculateShipping,
171
+ calculateTax,
169
172
  ...(kernel.hooks.resolve("checkout.beforePayment") as BeforeHook<CheckoutData>[]),
170
173
  validatePaymentMethod,
171
174
  ];
@@ -1,8 +1,9 @@
1
1
  import { OpenAPIHono } from "@hono/zod-openapi";
2
2
  import type { Kernel } from "../../../runtime/kernel.js";
3
- import { changeOrderStatusRoute, listOrdersRoute, orderLookupRoute, getOrderRoute, getOrderFulfillmentsRoute, createOrderRoute, refundOrderRoute, captureOrderRoute, createOrderFulfillmentRoute, addOrderLineItemRoute, updateOrderLineItemRoute, removeOrderLineItemRoute, refundOrderLinesRoute, undoOrderRefundRoute, listOrderRefundsRoute, refundCapStatusRoute, createOrderNoteRoute, listOrderNotesRoute, deleteOrderNoteRoute, orderTimelineRoute } from "../schemas/orders.js";
3
+ import { changeOrderStatusRoute, listOrdersRoute, orderLookupRoute, getOrderRoute, getOrderFulfillmentsRoute, createOrderRoute, quoteOrderRoute, refundOrderRoute, captureOrderRoute, createOrderFulfillmentRoute, addOrderLineItemRoute, updateOrderLineItemRoute, removeOrderLineItemRoute, refundOrderLinesRoute, undoOrderRefundRoute, listOrderRefundsRoute, refundCapStatusRoute, createOrderNoteRoute, listOrderNotesRoute, deleteOrderNoteRoute, orderTimelineRoute } from "../schemas/orders.js";
4
4
  import { type AppEnv, isUUID, mapErrorToResponse, mapErrorToStatus, parsePagination } from "../utils.js";
5
5
  import type { CreateOrderInput } from "../../../modules/orders/service.js";
6
+ import { computeOrderPricing } from "../../../modules/orders/quote.js";
6
7
  import { assertPermission } from "../../../auth/permissions.js";
7
8
 
8
9
  export function orderRoutes(kernel: Kernel) {
@@ -74,6 +75,40 @@ export function orderRoutes(kernel: Kernel) {
74
75
  return c.json({ data: result.value }, 201);
75
76
  });
76
77
 
78
+ // @ts-expect-error -- openapi handler union return type
79
+ router.openapi(quoteOrderRoute, async (c) => {
80
+ try {
81
+ assertPermission(c.get("actor"), "orders:manage");
82
+ } catch (error) {
83
+ return c.json(mapErrorToResponse(error), mapErrorToStatus(error));
84
+ }
85
+ const body = c.req.valid("json") as {
86
+ currency: string;
87
+ lineItems: Array<{ entityId: string; entityType?: string; variantId?: string; quantity: number; title?: string }>;
88
+ customerId?: string;
89
+ customerGroupIds?: string[];
90
+ promotionCodes?: string[];
91
+ shippingAddress?: import("../../../modules/shipping/calculator.js").ShippingAddress;
92
+ };
93
+ try {
94
+ const breakdown = await computeOrderPricing(
95
+ kernel,
96
+ {
97
+ currency: body.currency,
98
+ lineItems: body.lineItems,
99
+ ...(body.customerId !== undefined ? { customerId: body.customerId } : {}),
100
+ ...(body.customerGroupIds !== undefined ? { customerGroupIds: body.customerGroupIds } : {}),
101
+ ...(body.promotionCodes !== undefined ? { promotionCodes: body.promotionCodes } : {}),
102
+ ...(body.shippingAddress !== undefined ? { shippingAddress: body.shippingAddress } : {}),
103
+ },
104
+ c.get("actor"),
105
+ );
106
+ return c.json({ data: breakdown });
107
+ } catch (error) {
108
+ return c.json(mapErrorToResponse(error), mapErrorToStatus(error));
109
+ }
110
+ });
111
+
77
112
  // @ts-expect-error -- openapi handler union return type
78
113
  router.openapi(refundOrderRoute, async (c) => {
79
114
  const body = c.req.valid("json") as { amount?: number; reason?: string } | undefined;
@@ -61,7 +61,12 @@ export function promotionRoutes(kernel: Kernel) {
61
61
  const actor = c.get("actor");
62
62
  const orgId = resolveOrgId(actor);
63
63
 
64
- const result = await kernel.services.promotions.validate(payload.code, {
64
+ // Apply (not just validate) so the response carries the authoritative
65
+ // discount the cart would receive — the same computation checkout runs.
66
+ // Returning only the promotion forces callers to re-derive the amount, which
67
+ // drifts from checkout and shows customers a wrong number. apply() still
68
+ // Err's on an invalid / inapplicable code (it validates first).
69
+ const result = await kernel.services.promotions.apply(payload.code, {
65
70
  orgId,
66
71
  currency: payload.currency,
67
72
  subtotal: payload.subtotal,
@@ -55,6 +55,74 @@ export const CreateOrderBodySchema = z.object({
55
55
  lineItems: z.array(CreateOrderLineItemSchema).min(1),
56
56
  }).openapi("CreateOrderRequest");
57
57
 
58
+ const QuoteLineItemSchema = z.object({
59
+ entityId: z.uuid().openapi({ example: "550e8400-e29b-41d4-a716-446655440000" }),
60
+ entityType: z.string().optional().openapi({ example: "product" }),
61
+ variantId: z.uuid().optional(),
62
+ quantity: z.number().int().positive().openapi({ example: 2 }),
63
+ title: z.string().optional(),
64
+ });
65
+
66
+ const QuoteAddressSchema = z.object({
67
+ line1: z.string(),
68
+ line2: z.string().optional(),
69
+ city: z.string(),
70
+ state: z.string().optional(),
71
+ postalCode: z.string(),
72
+ country: z.string(),
73
+ });
74
+
75
+ export const QuoteOrderBodySchema = z.object({
76
+ currency: z.string().min(3).max(3).openapi({ example: "USD" }),
77
+ lineItems: z.array(QuoteLineItemSchema).min(1),
78
+ customerId: z.uuid().optional(),
79
+ customerGroupIds: z.array(z.string()).optional(),
80
+ promotionCodes: z.array(z.string()).optional(),
81
+ shippingAddress: QuoteAddressSchema.optional(),
82
+ }).openapi("QuoteOrderRequest");
83
+
84
+ export const QuoteOrderResultSchema = z.object({
85
+ data: z.object({
86
+ currency: z.string(),
87
+ subtotal: z.number().openapi({ description: "Minor units (e.g. cents)." }),
88
+ discountTotal: z.number(),
89
+ shippingTotal: z.number(),
90
+ taxTotal: z.number(),
91
+ grandTotal: z.number(),
92
+ lineItems: z.array(z.object({
93
+ entityId: z.string(),
94
+ variantId: z.string().optional(),
95
+ quantity: z.number(),
96
+ unitPrice: z.number(),
97
+ totalPrice: z.number(),
98
+ discountAmount: z.number(),
99
+ taxAmount: z.number(),
100
+ })),
101
+ }),
102
+ }).openapi("QuoteOrderResult");
103
+
104
+ export const quoteOrderRoute = createRoute({
105
+ method: "post",
106
+ path: "/quote",
107
+ tags: ["Orders"],
108
+ summary: "Quote an order's charges without creating it",
109
+ description:
110
+ "Computes subtotal, discount, shipping, tax and grand total for the given line items (+ optional address, customer, promotion codes) using the SAME pricing pipeline checkout runs — so a manual/draft order previews exactly what it will be charged. Persists nothing.",
111
+ request: {
112
+ body: {
113
+ content: { "application/json": { schema: QuoteOrderBodySchema } },
114
+ required: true,
115
+ },
116
+ },
117
+ responses: {
118
+ 200: {
119
+ content: { "application/json": { schema: QuoteOrderResultSchema } },
120
+ description: "Computed order charges (authoritative — matches checkout).",
121
+ },
122
+ ...errorResponses,
123
+ },
124
+ });
125
+
58
126
  // ─── Response Schemas ───────────────────────────────────────────────────────
59
127
 
60
128
  export const OrderDataResponseSchema = OrderResponse;
@@ -34,6 +34,31 @@ export const PromotionResponseSchema = z.object({
34
34
  data: z.record(z.string(), z.unknown()),
35
35
  }).openapi("PromotionResponse");
36
36
 
37
+ // The authoritative result of applying a code to a cart — the SAME computation
38
+ // checkout uses, so a storefront can show the exact discount the order will get
39
+ // (no client-side re-derivation, which would drift from checkout).
40
+ export const PromotionValidationResultSchema = z.object({
41
+ data: z.object({
42
+ totalDiscount: z.number().openapi({
43
+ example: 1000,
44
+ description: "Discount this code applies to the cart, in the cart's minor units (e.g. cents).",
45
+ }),
46
+ freeShipping: z.boolean().openapi({ example: false }),
47
+ applied: z.array(z.object({
48
+ promotionId: z.string(),
49
+ code: z.string().optional(),
50
+ type: z.string(),
51
+ discountAmount: z.number(),
52
+ freeShipping: z.boolean(),
53
+ description: z.string(),
54
+ })).openapi({ description: "The promotion(s) applied by this code." }),
55
+ rejectedCodes: z.array(z.object({
56
+ code: z.string(),
57
+ reason: z.string(),
58
+ })),
59
+ }),
60
+ }).openapi("PromotionValidationResult");
61
+
37
62
  // ─── Path Params ────────────────────────────────────────────────────────────
38
63
 
39
64
  const PromotionIdParam = z.object({
@@ -98,8 +123,8 @@ export const validatePromotionRoute = createRoute({
98
123
  },
99
124
  responses: {
100
125
  200: {
101
- content: { "application/json": { schema: PromotionResponseSchema } },
102
- description: "Promotion validation result.",
126
+ content: { "application/json": { schema: PromotionValidationResultSchema } },
127
+ description: "The code is valid for this cart: the authoritative discount it applies (same computation as checkout).",
103
128
  },
104
129
  ...errorResponses,
105
130
  },
@@ -0,0 +1,131 @@
1
+ import { createHookContext } from "../../kernel/hooks/create-context.js";
2
+ import { runBeforeHooks } from "../../kernel/hooks/executor.js";
3
+ import {
4
+ resolveCurrentPrices,
5
+ applyPromotionCodes,
6
+ calculateShipping,
7
+ calculateTax,
8
+ type CheckoutData,
9
+ } from "../../hooks/checkout.js";
10
+ import type { Kernel } from "../../runtime/kernel.js";
11
+ import type { Actor } from "../../auth/types.js";
12
+ import type { ServiceContainer } from "../../kernel/hooks/types.js";
13
+ import type { PluginDb } from "../../kernel/database/plugin-types.js";
14
+ import type { ShippingAddress } from "../shipping/calculator.js";
15
+ import { makeId } from "../../utils/id.js";
16
+
17
+ export interface OrderPricingInput {
18
+ currency: string;
19
+ lineItems: Array<{
20
+ entityId: string;
21
+ entityType?: string;
22
+ variantId?: string;
23
+ quantity: number;
24
+ title?: string;
25
+ }>;
26
+ customerId?: string;
27
+ customerGroupIds?: string[];
28
+ promotionCodes?: string[];
29
+ shippingAddress?: ShippingAddress;
30
+ }
31
+
32
+ export interface OrderPricingBreakdown {
33
+ currency: string;
34
+ subtotal: number;
35
+ discountTotal: number;
36
+ shippingTotal: number;
37
+ taxTotal: number;
38
+ grandTotal: number;
39
+ lineItems: Array<{
40
+ entityId: string;
41
+ variantId?: string;
42
+ quantity: number;
43
+ unitPrice: number;
44
+ totalPrice: number;
45
+ discountAmount: number;
46
+ taxAmount: number;
47
+ }>;
48
+ }
49
+
50
+ /**
51
+ * The single pricing authority for an order. Runs the SAME hooks checkout runs —
52
+ * `resolveCurrentPrices → applyPromotionCodes → calculateShipping → calculateTax`
53
+ * (shipping before tax so `appliesToShipping` rates fire) — against a synthetic
54
+ * `CheckoutData`, with NO cart claim, inventory check, or payment side effects.
55
+ *
56
+ * Because it reuses the checkout hooks verbatim, a manual-order quote produced
57
+ * here equals what checkout will actually charge for the same inputs — there is
58
+ * no second pricing implementation to drift. Used by `POST /api/orders/quote`
59
+ * and available to any manual/draft-order flow.
60
+ */
61
+ export async function computeOrderPricing(
62
+ kernel: Kernel,
63
+ input: OrderPricingInput,
64
+ actor: Actor | null,
65
+ tx?: unknown,
66
+ ): Promise<OrderPricingBreakdown> {
67
+ const data: CheckoutData = {
68
+ checkoutId: makeId(),
69
+ cartId: "",
70
+ currency: input.currency,
71
+ paymentMethodId: "",
72
+ lineItems: input.lineItems.map((li) => ({
73
+ id: `${li.entityId}:${li.variantId ?? "_"}`,
74
+ entityId: li.entityId,
75
+ ...(li.entityType !== undefined ? { entityType: li.entityType } : {}),
76
+ ...(li.variantId !== undefined ? { variantId: li.variantId } : {}),
77
+ title: li.title ?? li.entityId,
78
+ quantity: li.quantity,
79
+ })),
80
+ subtotal: 0,
81
+ discountTotal: 0,
82
+ taxTotal: 0,
83
+ shippingTotal: 0,
84
+ total: 0,
85
+ ...(input.customerId !== undefined ? { customerId: input.customerId } : {}),
86
+ ...(input.customerGroupIds !== undefined
87
+ ? { customerGroupIds: input.customerGroupIds }
88
+ : {}),
89
+ ...(input.promotionCodes !== undefined
90
+ ? { promotionCodes: input.promotionCodes }
91
+ : {}),
92
+ ...(input.shippingAddress !== undefined
93
+ ? { shippingAddress: input.shippingAddress }
94
+ : {}),
95
+ };
96
+
97
+ const context = createHookContext({
98
+ actor,
99
+ logger: kernel.logger,
100
+ services: kernel.services as ServiceContainer,
101
+ context: { moduleName: "orders" },
102
+ origin: "rest",
103
+ database: { db: kernel.database.db as PluginDb },
104
+ });
105
+ if (tx !== undefined) context.tx = tx as typeof context.tx;
106
+
107
+ const priced = await runBeforeHooks(
108
+ [resolveCurrentPrices, applyPromotionCodes, calculateShipping, calculateTax],
109
+ data,
110
+ "create",
111
+ context,
112
+ );
113
+
114
+ return {
115
+ currency: priced.currency,
116
+ subtotal: priced.subtotal,
117
+ discountTotal: priced.discountTotal,
118
+ shippingTotal: priced.shippingTotal,
119
+ taxTotal: priced.taxTotal,
120
+ grandTotal: priced.total,
121
+ lineItems: priced.lineItems.map((li) => ({
122
+ entityId: li.entityId,
123
+ ...(li.variantId !== undefined ? { variantId: li.variantId } : {}),
124
+ quantity: li.quantity,
125
+ unitPrice: li.resolvedUnitPrice ?? 0,
126
+ totalPrice: li.resolvedTotal ?? 0,
127
+ discountAmount: li.discountAmount ?? 0,
128
+ taxAmount: li.taxAmount ?? 0,
129
+ })),
130
+ };
131
+ }
@@ -53,10 +53,16 @@ export class TaxService {
53
53
  if (this.repository && orgId && params.toAddress) {
54
54
  const matched = await this.matchRuntimeRates(orgId, params.toAddress, ctx);
55
55
  if (matched.length > 0) {
56
- const taxableAmount = params.lineItems.reduce(
57
- (sum, lineItem) =>
58
- sum + lineItem.unitPrice * lineItem.quantity - (lineItem.discount ?? 0),
56
+ // Subtract the order-level discount from the taxable base too (audit
57
+ // C2a) — otherwise runtime rates tax the pre-discount subtotal and
58
+ // over-collect on every discounted order, unlike the class-based path.
59
+ const taxableAmount = Math.max(
59
60
  0,
61
+ params.lineItems.reduce(
62
+ (sum, lineItem) =>
63
+ sum + lineItem.unitPrice * lineItem.quantity - (lineItem.discount ?? 0),
64
+ 0,
65
+ ) - (params.orderDiscount ?? 0),
60
66
  );
61
67
  let amountToCollect = 0;
62
68
  const breakdown: Record<string, unknown> = {};