@porulle/core 0.6.0 → 0.8.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/setup.d.ts.map +1 -1
- package/dist/auth/setup.js +1 -0
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +5 -0
- package/dist/config/types.d.ts +17 -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.d.ts.map +1 -1
- package/dist/hooks/checkout.js +16 -1
- 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 +172 -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.map +1 -1
- package/dist/interfaces/rest/routes/checkout.js +17 -0
- 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/orders.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/orders.js +122 -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 +1230 -2
- 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 +4467 -7
- package/dist/interfaces/rest/schemas/orders.d.ts.map +1 -1
- package/dist/interfaces/rest/schemas/orders.js +330 -0
- 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 +156 -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/error-mapper.d.ts.map +1 -1
- package/dist/kernel/error-mapper.js +1 -0
- package/dist/kernel/errors.d.ts +5 -0
- package/dist/kernel/errors.d.ts.map +1 -1
- package/dist/kernel/errors.js +9 -0
- 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 +23 -0
- package/dist/kernel/plugin/manifest.d.ts.map +1 -1
- package/dist/kernel/plugin/manifest.js +17 -2
- 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 +58 -0
- 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 +151 -6
- 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 +15 -1
- package/dist/modules/catalog/schemas.d.ts.map +1 -1
- package/dist/modules/catalog/schemas.js +17 -1
- package/dist/modules/catalog/service.d.ts +43 -1
- package/dist/modules/catalog/service.d.ts.map +1 -1
- package/dist/modules/catalog/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/media/service.d.ts +18 -0
- package/dist/modules/media/service.d.ts.map +1 -1
- package/dist/modules/media/service.js +26 -0
- 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 +374 -0
- package/dist/modules/orders/schema.d.ts.map +1 -1
- package/dist/modules/orders/schema.js +52 -1
- package/dist/modules/orders/service.d.ts +102 -2
- package/dist/modules/orders/service.d.ts.map +1 -1
- package/dist/modules/orders/service.js +494 -23
- 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 +57 -1
- 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.d.ts.map +1 -1
- package/dist/runtime/server.js +35 -3
- 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/rest-api-test-utils.d.ts.map +1 -1
- package/dist/test-utils/rest-api-test-utils.js +6 -0
- package/package.json +3 -3
- package/src/auth/setup.ts +1 -0
- package/src/config/defaults.ts +5 -0
- package/src/config/types.ts +17 -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.ts +25 -3
- package/src/index.ts +1 -0
- package/src/interfaces/rest/index.ts +12 -0
- package/src/interfaces/rest/routes/admin/staff.ts +218 -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 +22 -0
- package/src/interfaces/rest/routes/documents.ts +61 -0
- package/src/interfaces/rest/routes/orders.ts +172 -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 +361 -0
- 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/error-mapper.ts +1 -0
- package/src/kernel/errors.ts +11 -0
- package/src/kernel/hooks/types.ts +1 -0
- package/src/kernel/plugin/manifest.ts +38 -2
- 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 +101 -0
- package/src/modules/catalog/entity-service.ts +195 -6
- package/src/modules/catalog/schema.ts +5 -0
- package/src/modules/catalog/schemas.ts +18 -1
- package/src/modules/catalog/service.ts +32 -2
- 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/media/service.ts +40 -0
- package/src/modules/orders/repository/index.ts +127 -1
- package/src/modules/orders/schema.ts +54 -1
- package/src/modules/orders/service.ts +739 -24
- package/src/modules/pricing/repository/index.ts +34 -0
- package/src/modules/pricing/service.ts +100 -1
- 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 +36 -3
- package/src/test-utils/create-plugin-test-app.ts +17 -4
- package/src/test-utils/rest-api-test-utils.ts +6 -0
|
@@ -28,6 +28,16 @@ export interface AttachMediaInput {
|
|
|
28
28
|
sortOrder?: number;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
export interface AttachedMedia {
|
|
32
|
+
mediaAssetId: string;
|
|
33
|
+
role: string;
|
|
34
|
+
sortOrder: number;
|
|
35
|
+
variantId: string | null;
|
|
36
|
+
url: string;
|
|
37
|
+
alt: string | null;
|
|
38
|
+
contentType: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
31
41
|
interface MediaServiceDeps {
|
|
32
42
|
repository: MediaRepository;
|
|
33
43
|
catalogRepository: CatalogRepository;
|
|
@@ -234,4 +244,34 @@ export class MediaService {
|
|
|
234
244
|
|
|
235
245
|
return Ok(undefined);
|
|
236
246
|
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Media attached to an entity, resolved to public URLs and ordered by
|
|
250
|
+
* sortOrder. Powers catalog `?include=media` hydration. Returns entity-level
|
|
251
|
+
* links only unless a specific variantId is requested.
|
|
252
|
+
*/
|
|
253
|
+
async listEntityMedia(
|
|
254
|
+
entityId: string,
|
|
255
|
+
opts?: { variantId?: string; orgId?: string },
|
|
256
|
+
ctx?: TxContext,
|
|
257
|
+
): Promise<Result<AttachedMedia[]>> {
|
|
258
|
+
const links = await this.repo.findEntityMedia(entityId, opts?.variantId, ctx);
|
|
259
|
+
const out: AttachedMedia[] = [];
|
|
260
|
+
for (const link of links) {
|
|
261
|
+
const asset = await this.repo.findAssetById(link.mediaAssetId, ctx, opts?.orgId);
|
|
262
|
+
if (!asset) continue;
|
|
263
|
+
const urlResult = await this.deps.storage.getUrl(asset.storageKey);
|
|
264
|
+
out.push({
|
|
265
|
+
mediaAssetId: link.mediaAssetId,
|
|
266
|
+
role: link.role,
|
|
267
|
+
sortOrder: link.sortOrder,
|
|
268
|
+
variantId: link.variantId ?? null,
|
|
269
|
+
url: urlResult.ok ? urlResult.value : "",
|
|
270
|
+
alt: asset.alt ?? null,
|
|
271
|
+
contentType: asset.contentType,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
out.sort((a, b) => a.sortOrder - b.sortOrder);
|
|
275
|
+
return Ok(out);
|
|
276
|
+
}
|
|
237
277
|
}
|
|
@@ -4,7 +4,7 @@ import type {
|
|
|
4
4
|
DrizzleDatabase,
|
|
5
5
|
DbOrTx,
|
|
6
6
|
} from "../../../kernel/database/drizzle-db.js";
|
|
7
|
-
import { orders, orderLineItems, orderStatusHistory } from "../schema.js";
|
|
7
|
+
import { orders, orderLineItems, orderNotes, orderRefunds, orderStatusHistory } from "../schema.js";
|
|
8
8
|
import { customers } from "../../customers/schema.js";
|
|
9
9
|
|
|
10
10
|
export interface OrderLookupRow {
|
|
@@ -26,6 +26,10 @@ export type OrderLineItem = typeof orderLineItems.$inferSelect;
|
|
|
26
26
|
export type OrderLineItemInsert = typeof orderLineItems.$inferInsert;
|
|
27
27
|
export type OrderStatusHistory = typeof orderStatusHistory.$inferSelect;
|
|
28
28
|
export type OrderStatusHistoryInsert = typeof orderStatusHistory.$inferInsert;
|
|
29
|
+
export type OrderRefund = typeof orderRefunds.$inferSelect;
|
|
30
|
+
export type OrderRefundInsert = typeof orderRefunds.$inferInsert;
|
|
31
|
+
export type OrderNote = typeof orderNotes.$inferSelect;
|
|
32
|
+
export type OrderNoteInsert = typeof orderNotes.$inferInsert;
|
|
29
33
|
|
|
30
34
|
/**
|
|
31
35
|
* OrdersRepository provides type-safe database operations for orders.
|
|
@@ -159,6 +163,24 @@ export class OrdersRepository {
|
|
|
159
163
|
return query;
|
|
160
164
|
}
|
|
161
165
|
|
|
166
|
+
async findByIdempotencyKey(
|
|
167
|
+
orgId: string,
|
|
168
|
+
idempotencyKey: string,
|
|
169
|
+
ctx?: TxContext,
|
|
170
|
+
): Promise<Order | undefined> {
|
|
171
|
+
const db = this.getDb(ctx);
|
|
172
|
+
const rows = await db
|
|
173
|
+
.select()
|
|
174
|
+
.from(orders)
|
|
175
|
+
.where(
|
|
176
|
+
and(
|
|
177
|
+
eq(orders.organizationId, orgId),
|
|
178
|
+
eq(orders.idempotencyKey, idempotencyKey),
|
|
179
|
+
),
|
|
180
|
+
);
|
|
181
|
+
return rows[0];
|
|
182
|
+
}
|
|
183
|
+
|
|
162
184
|
async create(data: OrderInsert, ctx?: TxContext): Promise<Order> {
|
|
163
185
|
const db = this.getDb(ctx);
|
|
164
186
|
const rows = await db.insert(orders).values(data).returning();
|
|
@@ -328,6 +350,110 @@ export class OrdersRepository {
|
|
|
328
350
|
return { order, lineItems };
|
|
329
351
|
}
|
|
330
352
|
|
|
353
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
354
|
+
// Line-level refunds (issue #52)
|
|
355
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
356
|
+
|
|
357
|
+
async createRefund(data: OrderRefundInsert, ctx?: TxContext): Promise<OrderRefund> {
|
|
358
|
+
const db = this.getDb(ctx);
|
|
359
|
+
const rows = await db.insert(orderRefunds).values(data).returning();
|
|
360
|
+
return rows[0]!;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
async findRefundById(
|
|
364
|
+
orgId: string,
|
|
365
|
+
id: string,
|
|
366
|
+
ctx?: TxContext,
|
|
367
|
+
): Promise<OrderRefund | undefined> {
|
|
368
|
+
const db = this.getDb(ctx);
|
|
369
|
+
const rows = await db
|
|
370
|
+
.select()
|
|
371
|
+
.from(orderRefunds)
|
|
372
|
+
.where(and(eq(orderRefunds.organizationId, orgId), eq(orderRefunds.id, id)));
|
|
373
|
+
return rows[0];
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
async findRefundsByOrderId(orderId: string, ctx?: TxContext): Promise<OrderRefund[]> {
|
|
377
|
+
const db = this.getDb(ctx);
|
|
378
|
+
return db
|
|
379
|
+
.select()
|
|
380
|
+
.from(orderRefunds)
|
|
381
|
+
.where(eq(orderRefunds.orderId, orderId))
|
|
382
|
+
.orderBy(desc(orderRefunds.createdAt));
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
async markRefundUndone(
|
|
386
|
+
id: string,
|
|
387
|
+
undoneBy: string,
|
|
388
|
+
ctx?: TxContext,
|
|
389
|
+
): Promise<OrderRefund | undefined> {
|
|
390
|
+
const db = this.getDb(ctx);
|
|
391
|
+
const rows = await db
|
|
392
|
+
.update(orderRefunds)
|
|
393
|
+
.set({ status: "undone", undoneAt: new Date(), undoneBy })
|
|
394
|
+
.where(and(eq(orderRefunds.id, id), eq(orderRefunds.status, "completed")))
|
|
395
|
+
.returning();
|
|
396
|
+
return rows[0];
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Sum of completed refunds performed by an operator for the local calendar
|
|
401
|
+
* day in the given timezone — the daily-cap accounting query.
|
|
402
|
+
*/
|
|
403
|
+
async sumRefundsByOperatorToday(
|
|
404
|
+
orgId: string,
|
|
405
|
+
performedBy: string,
|
|
406
|
+
timezone: string,
|
|
407
|
+
ctx?: TxContext,
|
|
408
|
+
): Promise<number> {
|
|
409
|
+
const db = this.getDb(ctx);
|
|
410
|
+
const result = await db.execute(sql`
|
|
411
|
+
SELECT COALESCE(sum(amount), 0)::int AS "total"
|
|
412
|
+
FROM order_refunds
|
|
413
|
+
WHERE organization_id = ${orgId}
|
|
414
|
+
AND performed_by = ${performedBy}
|
|
415
|
+
AND status = 'completed'
|
|
416
|
+
AND (created_at AT TIME ZONE ${timezone})::date = (now() AT TIME ZONE ${timezone})::date
|
|
417
|
+
`);
|
|
418
|
+
const rows = Array.isArray(result)
|
|
419
|
+
? (result as Record<string, unknown>[])
|
|
420
|
+
: ((result as { rows?: Record<string, unknown>[] }).rows ?? []);
|
|
421
|
+
const total = rows[0]?.total;
|
|
422
|
+
return typeof total === "bigint" ? Number(total) : ((total as number) ?? 0);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
426
|
+
// Order notes (issue #56)
|
|
427
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
428
|
+
|
|
429
|
+
async createNote(data: OrderNoteInsert, ctx?: TxContext): Promise<OrderNote> {
|
|
430
|
+
const db = this.getDb(ctx);
|
|
431
|
+
const rows = await db.insert(orderNotes).values(data).returning();
|
|
432
|
+
return rows[0]!;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
async findNotesByOrderId(orderId: string, ctx?: TxContext): Promise<OrderNote[]> {
|
|
436
|
+
const db = this.getDb(ctx);
|
|
437
|
+
return db
|
|
438
|
+
.select()
|
|
439
|
+
.from(orderNotes)
|
|
440
|
+
.where(eq(orderNotes.orderId, orderId))
|
|
441
|
+
.orderBy(desc(orderNotes.pinned), desc(orderNotes.createdAt));
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
async deleteNote(orgId: string, orderId: string, noteId: string, ctx?: TxContext): Promise<boolean> {
|
|
445
|
+
const db = this.getDb(ctx);
|
|
446
|
+
const rows = await db
|
|
447
|
+
.delete(orderNotes)
|
|
448
|
+
.where(and(
|
|
449
|
+
eq(orderNotes.id, noteId),
|
|
450
|
+
eq(orderNotes.orderId, orderId),
|
|
451
|
+
eq(orderNotes.organizationId, orgId),
|
|
452
|
+
))
|
|
453
|
+
.returning();
|
|
454
|
+
return rows.length > 0;
|
|
455
|
+
}
|
|
456
|
+
|
|
331
457
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
332
458
|
// Order Number Generation
|
|
333
459
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { sql } from "drizzle-orm";
|
|
2
|
+
import { boolean, index, integer, jsonb, pgTable, text, timestamp, uniqueIndex, uuid } from "drizzle-orm/pg-core";
|
|
2
3
|
import { organization } from "../../auth/auth-schema.js";
|
|
3
4
|
import { sellableEntities, variants } from "../catalog/schema.js";
|
|
4
5
|
|
|
@@ -17,6 +18,9 @@ export const orders = pgTable("orders", {
|
|
|
17
18
|
amountCaptured: integer("amount_captured"),
|
|
18
19
|
paymentIntentId: text("payment_intent_id"),
|
|
19
20
|
paymentMethodId: text("payment_method_id"),
|
|
21
|
+
// Client-supplied key making order creation safely retryable (offline POS
|
|
22
|
+
// queues, network retries) — replays return the original order.
|
|
23
|
+
idempotencyKey: text("idempotency_key"),
|
|
20
24
|
metadata: jsonb("metadata").$type<Record<string, unknown>>().default({}),
|
|
21
25
|
placedAt: timestamp("placed_at", { withTimezone: true }).defaultNow().notNull(),
|
|
22
26
|
fulfilledAt: timestamp("fulfilled_at", { withTimezone: true }),
|
|
@@ -28,6 +32,9 @@ export const orders = pgTable("orders", {
|
|
|
28
32
|
customerIdIdx: index("idx_orders_customer_id").on(table.customerId),
|
|
29
33
|
placedAtIdx: index("idx_orders_placed_at").on(table.placedAt),
|
|
30
34
|
paymentIntentIdx: index("idx_orders_payment_intent").on(table.paymentIntentId),
|
|
35
|
+
orgIdempotencyKeyUnique: uniqueIndex("orders_org_idempotency_key_unique")
|
|
36
|
+
.on(table.organizationId, table.idempotencyKey)
|
|
37
|
+
.where(sql`idempotency_key IS NOT NULL`),
|
|
31
38
|
}));
|
|
32
39
|
|
|
33
40
|
export const orderLineItems = pgTable("order_line_items", {
|
|
@@ -45,6 +52,9 @@ export const orderLineItems = pgTable("order_line_items", {
|
|
|
45
52
|
totalPrice: integer("total_price").notNull(),
|
|
46
53
|
taxAmount: integer("tax_amount").notNull().default(0),
|
|
47
54
|
discountAmount: integer("discount_amount").notNull().default(0),
|
|
55
|
+
// Units already refunded on this line (issue #52) — line-level refund REST
|
|
56
|
+
// rejects refunds beyond `quantity - refundedQuantity`.
|
|
57
|
+
refundedQuantity: integer("refunded_quantity").notNull().default(0),
|
|
48
58
|
fulfillmentStatus: text("fulfillment_status").notNull().default("unfulfilled"),
|
|
49
59
|
metadata: jsonb("metadata").$type<Record<string, unknown>>().default({}),
|
|
50
60
|
}, (table) => [
|
|
@@ -52,6 +62,49 @@ export const orderLineItems = pgTable("order_line_items", {
|
|
|
52
62
|
index("idx_order_line_items_entity_id").on(table.entityId),
|
|
53
63
|
]);
|
|
54
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Line-level refunds (issue #52). One row per refund operation — the ledger
|
|
67
|
+
* behind the daily refund cap and the undo window. `lines` records which
|
|
68
|
+
* line items and quantities the refund covered so undo can restore them.
|
|
69
|
+
*/
|
|
70
|
+
export const orderRefunds = pgTable("order_refunds", {
|
|
71
|
+
id: uuid("id").defaultRandom().primaryKey(),
|
|
72
|
+
organizationId: text("organization_id").notNull().references(() => organization.id, { onDelete: "cascade" }),
|
|
73
|
+
orderId: uuid("order_id")
|
|
74
|
+
.references(() => orders.id, { onDelete: "cascade" })
|
|
75
|
+
.notNull(),
|
|
76
|
+
amount: integer("amount").notNull(),
|
|
77
|
+
reason: text("reason"),
|
|
78
|
+
lines: jsonb("lines").$type<Array<{ lineItemId: string; quantity: number; amount: number }>>().notNull(),
|
|
79
|
+
performedBy: text("performed_by").notNull(),
|
|
80
|
+
status: text("status", { enum: ["completed", "undone"] }).notNull().default("completed"),
|
|
81
|
+
undoneAt: timestamp("undone_at", { withTimezone: true }),
|
|
82
|
+
undoneBy: text("undone_by"),
|
|
83
|
+
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
|
|
84
|
+
}, (table) => [
|
|
85
|
+
index("idx_order_refunds_order_id").on(table.orderId),
|
|
86
|
+
index("idx_order_refunds_org_performed_by").on(table.organizationId, table.performedBy, table.createdAt),
|
|
87
|
+
]);
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Operator annotations on orders (issue #56) — "customer will pick up
|
|
91
|
+
* Thursday". Merged into GET /orders/{id}/timeline alongside status history
|
|
92
|
+
* and refund ledger events.
|
|
93
|
+
*/
|
|
94
|
+
export const orderNotes = pgTable("order_notes", {
|
|
95
|
+
id: uuid("id").defaultRandom().primaryKey(),
|
|
96
|
+
organizationId: text("organization_id").notNull().references(() => organization.id, { onDelete: "cascade" }),
|
|
97
|
+
orderId: uuid("order_id")
|
|
98
|
+
.references(() => orders.id, { onDelete: "cascade" })
|
|
99
|
+
.notNull(),
|
|
100
|
+
author: text("author").notNull(),
|
|
101
|
+
body: text("body").notNull(),
|
|
102
|
+
pinned: boolean("pinned").notNull().default(false),
|
|
103
|
+
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
|
|
104
|
+
}, (table) => [
|
|
105
|
+
index("idx_order_notes_order_id").on(table.orderId),
|
|
106
|
+
]);
|
|
107
|
+
|
|
55
108
|
export const orderStatusHistory = pgTable("order_status_history", {
|
|
56
109
|
id: uuid("id").defaultRandom().primaryKey(),
|
|
57
110
|
orderId: uuid("order_id")
|