@porulle/core 0.7.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 +98 -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 +233 -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/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 +132 -3
- 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/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 +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 +86 -1
- package/dist/modules/orders/service.d.ts.map +1 -1
- package/dist/modules/orders/service.js +447 -20
- 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 +38 -0
- 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/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 +137 -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 +257 -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/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 +168 -3
- 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/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 +54 -1
- package/src/modules/orders/service.ts +654 -22
- package/src/modules/pricing/repository/index.ts +34 -0
- package/src/modules/pricing/service.ts +71 -0
- 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/rest-api-test-utils.ts +6 -0
|
@@ -477,3 +477,56 @@ export const generateVariantsRoute = createRoute({
|
|
|
477
477
|
...errorResponses,
|
|
478
478
|
},
|
|
479
479
|
});
|
|
480
|
+
|
|
481
|
+
// ── One-call variant creation (issue #50) ───────────────────────────────────
|
|
482
|
+
|
|
483
|
+
export const QuickVariantBodySchema = z.object({
|
|
484
|
+
options: z.record(z.string().min(1), z.string().min(1)).openapi({
|
|
485
|
+
example: { color: "Red", size: "M" },
|
|
486
|
+
description: "Axis name → value. Option types/values are upserted inline.",
|
|
487
|
+
}),
|
|
488
|
+
sku: z.string().min(1).optional(),
|
|
489
|
+
barcode: z.string().min(1).optional(),
|
|
490
|
+
}).openapi("QuickVariantRequest");
|
|
491
|
+
|
|
492
|
+
export const BulkVariantsBodySchema = z.object({
|
|
493
|
+
axes: z.array(z.object({
|
|
494
|
+
name: z.string().min(1),
|
|
495
|
+
values: z.array(z.string().min(1)).min(1),
|
|
496
|
+
})).openapi({
|
|
497
|
+
example: [{ name: "color", values: ["Red", "Blue"] }, { name: "size", values: ["S", "M"] }],
|
|
498
|
+
description: "Matrix axes; an empty array creates one option-less variant.",
|
|
499
|
+
}),
|
|
500
|
+
skuPrefix: z.string().min(1).optional(),
|
|
501
|
+
}).openapi("BulkVariantsRequest");
|
|
502
|
+
|
|
503
|
+
export const quickVariantRoute = createRoute({
|
|
504
|
+
method: "post",
|
|
505
|
+
path: "/entities/{id}/variants/quick",
|
|
506
|
+
tags: ["Catalog"],
|
|
507
|
+
summary: "Create one sellable variant in a single call (upserts axes, seeds inventory)",
|
|
508
|
+
request: {
|
|
509
|
+
params: EntityIdParam,
|
|
510
|
+
body: { content: { "application/json": { schema: QuickVariantBodySchema } }, required: true },
|
|
511
|
+
},
|
|
512
|
+
responses: {
|
|
513
|
+
201: { content: { "application/json": { schema: DataResponse } }, description: "Variant created and inventory seeded." },
|
|
514
|
+
200: { content: { "application/json": { schema: DataResponse } }, description: "The combination already existed; the existing variant is returned." },
|
|
515
|
+
...errorResponses,
|
|
516
|
+
},
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
export const bulkVariantsRoute = createRoute({
|
|
520
|
+
method: "post",
|
|
521
|
+
path: "/entities/{id}/variants/bulk",
|
|
522
|
+
tags: ["Catalog"],
|
|
523
|
+
summary: "Create a variant matrix in a single call (upserts axes, seeds inventory, skips existing)",
|
|
524
|
+
request: {
|
|
525
|
+
params: EntityIdParam,
|
|
526
|
+
body: { content: { "application/json": { schema: BulkVariantsBodySchema } }, required: true },
|
|
527
|
+
},
|
|
528
|
+
responses: {
|
|
529
|
+
201: { content: { "application/json": { schema: DataResponse } }, description: "Variants created (existing combinations skipped)." },
|
|
530
|
+
...errorResponses,
|
|
531
|
+
},
|
|
532
|
+
});
|
|
@@ -6,6 +6,10 @@ import { ErrorSchema, errorResponses } from "./shared.js";
|
|
|
6
6
|
export const CheckoutBodySchema = z.object({
|
|
7
7
|
cartId: z.uuid().openapi({ example: "550e8400-e29b-41d4-a716-446655440000" }),
|
|
8
8
|
paymentMethodId: z.string().openapi({ example: "pm_card_visa" }),
|
|
9
|
+
idempotencyKey: z.string().min(8).max(255).optional().openapi({
|
|
10
|
+
example: "checkout-8f14e45f-1738312200",
|
|
11
|
+
description: "Client-supplied retry key. A re-submitted checkout with the same key returns the already-created order without re-authorizing payment (safe offline-queue replay).",
|
|
12
|
+
}),
|
|
9
13
|
customerId: z.string().optional().openapi({ example: "customer-uuid-or-user-id" }),
|
|
10
14
|
customerGroupIds: z.array(z.string()).optional(),
|
|
11
15
|
currency: z.string().length(3).optional().openapi({ example: "USD" }),
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { z, createRoute } from "@hono/zod-openapi";
|
|
2
|
+
import { errorResponses } from "./shared.js";
|
|
3
|
+
|
|
4
|
+
const IdParam = z.object({
|
|
5
|
+
id: z.uuid().openapi({ example: "550e8400-e29b-41d4-a716-446655440000" }),
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export const EmailInvoiceBodySchema = z.object({
|
|
9
|
+
to: z.email().openapi({ example: "customer@example.com" }),
|
|
10
|
+
}).openapi("EmailInvoiceRequest");
|
|
11
|
+
|
|
12
|
+
const DataResponse = z.object({ data: z.any() }).openapi("DocumentActionResponse");
|
|
13
|
+
|
|
14
|
+
export const invoicePdfRoute = createRoute({
|
|
15
|
+
method: "get",
|
|
16
|
+
path: "/{id}/invoice.pdf",
|
|
17
|
+
tags: ["Documents"],
|
|
18
|
+
summary: "Render the order's invoice as PDF (issues a fiscal invoice number on first render)",
|
|
19
|
+
request: { params: IdParam },
|
|
20
|
+
responses: {
|
|
21
|
+
200: {
|
|
22
|
+
content: { "application/pdf": { schema: z.any().openapi({ type: "string", format: "binary" }) } },
|
|
23
|
+
description: "The rendered PDF invoice.",
|
|
24
|
+
},
|
|
25
|
+
...errorResponses,
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export const invoiceHtmlRoute = createRoute({
|
|
30
|
+
method: "get",
|
|
31
|
+
path: "/{id}/invoice.html",
|
|
32
|
+
tags: ["Documents"],
|
|
33
|
+
summary: "Render the order's invoice as HTML",
|
|
34
|
+
request: { params: IdParam },
|
|
35
|
+
responses: {
|
|
36
|
+
200: {
|
|
37
|
+
content: { "text/html": { schema: z.string() } },
|
|
38
|
+
description: "The rendered HTML invoice.",
|
|
39
|
+
},
|
|
40
|
+
...errorResponses,
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
export const receiptHtmlRoute = createRoute({
|
|
45
|
+
method: "get",
|
|
46
|
+
path: "/{id}/receipt.html",
|
|
47
|
+
tags: ["Documents"],
|
|
48
|
+
summary: "Render the order's receipt as HTML",
|
|
49
|
+
request: { params: IdParam },
|
|
50
|
+
responses: {
|
|
51
|
+
200: {
|
|
52
|
+
content: { "text/html": { schema: z.string() } },
|
|
53
|
+
description: "The rendered HTML receipt.",
|
|
54
|
+
},
|
|
55
|
+
...errorResponses,
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
export const emailInvoiceRoute = createRoute({
|
|
60
|
+
method: "post",
|
|
61
|
+
path: "/{id}/invoice/email",
|
|
62
|
+
tags: ["Documents"],
|
|
63
|
+
summary: "Email the order's invoice via the configured email adapter",
|
|
64
|
+
request: {
|
|
65
|
+
params: IdParam,
|
|
66
|
+
body: { content: { "application/json": { schema: EmailInvoiceBodySchema } }, required: true },
|
|
67
|
+
},
|
|
68
|
+
responses: {
|
|
69
|
+
200: { content: { "application/json": { schema: DataResponse } }, description: "Invoice emailed." },
|
|
70
|
+
...errorResponses,
|
|
71
|
+
},
|
|
72
|
+
});
|
|
@@ -39,6 +39,10 @@ const CreateOrderLineItemSchema = z.object({
|
|
|
39
39
|
|
|
40
40
|
export const CreateOrderBodySchema = z.object({
|
|
41
41
|
customerId: z.uuid().optional(),
|
|
42
|
+
idempotencyKey: z.string().min(8).max(255).optional().openapi({
|
|
43
|
+
example: "pos-sale-8f14e45f-1738312200",
|
|
44
|
+
description: "Client-supplied retry key. Re-submitting a create with the same key returns the original order instead of creating a duplicate (safe offline-queue replay).",
|
|
45
|
+
}),
|
|
42
46
|
currency: z.string().min(3).max(3).openapi({ example: "USD" }),
|
|
43
47
|
subtotal: z.number().int().nonnegative().openapi({ example: 2500 }),
|
|
44
48
|
taxTotal: z.number().int().nonnegative().openapi({ example: 200 }),
|
|
@@ -140,6 +144,41 @@ export const getOrderFulfillmentsRoute = createRoute({
|
|
|
140
144
|
},
|
|
141
145
|
});
|
|
142
146
|
|
|
147
|
+
export const CreateFulfillmentBodySchema = z.object({
|
|
148
|
+
lineItems: z.array(z.object({
|
|
149
|
+
orderLineItemId: z.uuid().openapi({ example: "550e8400-e29b-41d4-a716-446655440000" }),
|
|
150
|
+
quantity: z.number().int().positive().openapi({ example: 2 }),
|
|
151
|
+
})).min(1),
|
|
152
|
+
carrier: z.string().optional().openapi({ example: "DHL" }),
|
|
153
|
+
trackingNumber: z.string().optional().openapi({ example: "DHL-123456" }),
|
|
154
|
+
trackingUrl: z.string().optional().openapi({ example: "https://track.dhl.com/DHL-123456" }),
|
|
155
|
+
type: z.string().optional().openapi({ example: "physical" }),
|
|
156
|
+
status: z.string().optional().openapi({ example: "shipped" }),
|
|
157
|
+
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
158
|
+
}).openapi("CreateFulfillmentRequest");
|
|
159
|
+
|
|
160
|
+
export const createOrderFulfillmentRoute = createRoute({
|
|
161
|
+
method: "post",
|
|
162
|
+
path: "/{id}/fulfillments",
|
|
163
|
+
tags: ["Orders"],
|
|
164
|
+
summary: "Record a fulfillment (shipment) for an order",
|
|
165
|
+
description: "Creates a fulfillment for a subset of the order's line items with optional carrier/tracking details. Supports partial fulfillment (per-line quantities) and multiple fulfillments per order.",
|
|
166
|
+
request: {
|
|
167
|
+
params: OrderIdParam,
|
|
168
|
+
body: {
|
|
169
|
+
content: { "application/json": { schema: CreateFulfillmentBodySchema } },
|
|
170
|
+
required: true,
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
responses: {
|
|
174
|
+
201: {
|
|
175
|
+
content: { "application/json": { schema: OrderDataResponseSchema } },
|
|
176
|
+
description: "Fulfillment recorded.",
|
|
177
|
+
},
|
|
178
|
+
...errorResponses,
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
|
|
143
182
|
export const createOrderRoute = createRoute({
|
|
144
183
|
method: "post",
|
|
145
184
|
path: "/",
|
|
@@ -205,6 +244,91 @@ export const captureOrderRoute = createRoute({
|
|
|
205
244
|
},
|
|
206
245
|
});
|
|
207
246
|
|
|
247
|
+
export const AddOrderLineItemBodySchema = z.object({
|
|
248
|
+
entityId: z.uuid().openapi({ example: "550e8400-e29b-41d4-a716-446655440000" }),
|
|
249
|
+
entityType: z.string().min(1).openapi({ example: "product" }),
|
|
250
|
+
variantId: z.uuid().optional(),
|
|
251
|
+
sku: z.string().optional(),
|
|
252
|
+
title: z.string().min(1).openapi({ example: "Ceylon Black Tea 250g" }),
|
|
253
|
+
quantity: z.number().int().positive().openapi({ example: 1 }),
|
|
254
|
+
unitPrice: z.number().int().nonnegative().openapi({ example: 1250 }),
|
|
255
|
+
totalPrice: z.number().int().nonnegative().optional().openapi({ example: 1250 }),
|
|
256
|
+
taxAmount: z.number().int().nonnegative().optional(),
|
|
257
|
+
discountAmount: z.number().int().nonnegative().optional(),
|
|
258
|
+
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
259
|
+
}).openapi("AddOrderLineItemRequest");
|
|
260
|
+
|
|
261
|
+
export const UpdateOrderLineItemBodySchema = z.object({
|
|
262
|
+
quantity: z.number().int().positive().openapi({ example: 3 }),
|
|
263
|
+
}).openapi("UpdateOrderLineItemRequest");
|
|
264
|
+
|
|
265
|
+
const OrderLineItemParams = z.object({
|
|
266
|
+
id: z.uuid().openapi({ example: "550e8400-e29b-41d4-a716-446655440000" }),
|
|
267
|
+
lineItemId: z.uuid().openapi({ example: "660e8400-e29b-41d4-a716-446655440001" }),
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
export const addOrderLineItemRoute = createRoute({
|
|
271
|
+
method: "post",
|
|
272
|
+
path: "/{id}/line-items",
|
|
273
|
+
tags: ["Orders"],
|
|
274
|
+
summary: "Add a line item to a placed order",
|
|
275
|
+
description: "Adds a line item to a non-terminal order and recalculates subtotal/tax/grand totals. Records an audit entry.",
|
|
276
|
+
request: {
|
|
277
|
+
params: OrderIdParam,
|
|
278
|
+
body: {
|
|
279
|
+
content: { "application/json": { schema: AddOrderLineItemBodySchema } },
|
|
280
|
+
required: true,
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
responses: {
|
|
284
|
+
201: {
|
|
285
|
+
content: { "application/json": { schema: OrderDataResponseSchema } },
|
|
286
|
+
description: "Line item added; updated order returned.",
|
|
287
|
+
},
|
|
288
|
+
...errorResponses,
|
|
289
|
+
},
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
export const updateOrderLineItemRoute = createRoute({
|
|
293
|
+
method: "patch",
|
|
294
|
+
path: "/{id}/line-items/{lineItemId}",
|
|
295
|
+
tags: ["Orders"],
|
|
296
|
+
summary: "Adjust a line item's quantity on a placed order",
|
|
297
|
+
description: "Changes a line item's quantity on a non-terminal order, scaling line totals/tax and recalculating order totals. Records an audit entry.",
|
|
298
|
+
request: {
|
|
299
|
+
params: OrderLineItemParams,
|
|
300
|
+
body: {
|
|
301
|
+
content: { "application/json": { schema: UpdateOrderLineItemBodySchema } },
|
|
302
|
+
required: true,
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
responses: {
|
|
306
|
+
200: {
|
|
307
|
+
content: { "application/json": { schema: OrderDataResponseSchema } },
|
|
308
|
+
description: "Line item updated; updated order returned.",
|
|
309
|
+
},
|
|
310
|
+
...errorResponses,
|
|
311
|
+
},
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
export const removeOrderLineItemRoute = createRoute({
|
|
315
|
+
method: "delete",
|
|
316
|
+
path: "/{id}/line-items/{lineItemId}",
|
|
317
|
+
tags: ["Orders"],
|
|
318
|
+
summary: "Remove a line item from a placed order",
|
|
319
|
+
description: "Removes a line item from a non-terminal order (at least one line item must remain) and recalculates order totals. Records an audit entry.",
|
|
320
|
+
request: {
|
|
321
|
+
params: OrderLineItemParams,
|
|
322
|
+
},
|
|
323
|
+
responses: {
|
|
324
|
+
200: {
|
|
325
|
+
content: { "application/json": { schema: OrderDataResponseSchema } },
|
|
326
|
+
description: "Line item removed; updated order returned.",
|
|
327
|
+
},
|
|
328
|
+
...errorResponses,
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
|
|
208
332
|
export const changeOrderStatusRoute = createRoute({
|
|
209
333
|
method: "patch",
|
|
210
334
|
path: "/{id}/status",
|
|
@@ -227,3 +351,136 @@ export const changeOrderStatusRoute = createRoute({
|
|
|
227
351
|
...errorResponses,
|
|
228
352
|
},
|
|
229
353
|
});
|
|
354
|
+
|
|
355
|
+
// ── Line-level refund policy primitives (issue #52) ─────────────────────────
|
|
356
|
+
|
|
357
|
+
export const RefundLinesBodySchema = z.object({
|
|
358
|
+
lines: z.array(z.object({
|
|
359
|
+
lineItemId: z.uuid(),
|
|
360
|
+
quantity: z.number().int().min(1),
|
|
361
|
+
})).min(1),
|
|
362
|
+
reason: z.string().max(500).optional(),
|
|
363
|
+
}).openapi("RefundLinesRequest");
|
|
364
|
+
|
|
365
|
+
const RefundParam = z.object({
|
|
366
|
+
id: z.uuid(),
|
|
367
|
+
refundId: z.uuid(),
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
const RefundDataResponse = z.object({ data: z.any() }).openapi("RefundResponse");
|
|
371
|
+
|
|
372
|
+
export const refundOrderLinesRoute = createRoute({
|
|
373
|
+
method: "post",
|
|
374
|
+
path: "/{id}/refunds",
|
|
375
|
+
tags: ["Orders"],
|
|
376
|
+
summary: "Refund specific line-item quantities",
|
|
377
|
+
description: "Enforces per-line refundable quantity and the operator's daily refund cap (policies.refundDailyCap). Records an auditable refund ledger row supporting undo.",
|
|
378
|
+
request: {
|
|
379
|
+
params: OrderIdParam,
|
|
380
|
+
body: { content: { "application/json": { schema: RefundLinesBodySchema } }, required: true },
|
|
381
|
+
},
|
|
382
|
+
responses: {
|
|
383
|
+
201: { content: { "application/json": { schema: RefundDataResponse } }, description: "Refund recorded." },
|
|
384
|
+
...errorResponses,
|
|
385
|
+
},
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
export const undoOrderRefundRoute = createRoute({
|
|
389
|
+
method: "post",
|
|
390
|
+
path: "/{id}/refunds/{refundId}/undo",
|
|
391
|
+
tags: ["Orders"],
|
|
392
|
+
summary: "Undo a refund within the policy window",
|
|
393
|
+
request: { params: RefundParam },
|
|
394
|
+
responses: {
|
|
395
|
+
200: { content: { "application/json": { schema: RefundDataResponse } }, description: "Refund undone." },
|
|
396
|
+
...errorResponses,
|
|
397
|
+
},
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
export const listOrderRefundsRoute = createRoute({
|
|
401
|
+
method: "get",
|
|
402
|
+
path: "/{id}/refunds",
|
|
403
|
+
tags: ["Orders"],
|
|
404
|
+
summary: "List an order's refunds",
|
|
405
|
+
request: { params: OrderIdParam },
|
|
406
|
+
responses: {
|
|
407
|
+
200: { content: { "application/json": { schema: RefundDataResponse } }, description: "Refund ledger rows." },
|
|
408
|
+
...errorResponses,
|
|
409
|
+
},
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
export const refundCapStatusRoute = createRoute({
|
|
413
|
+
method: "get",
|
|
414
|
+
path: "/refunds/cap",
|
|
415
|
+
tags: ["Orders"],
|
|
416
|
+
summary: "The acting operator's daily refund-cap status",
|
|
417
|
+
responses: {
|
|
418
|
+
200: { content: { "application/json": { schema: RefundDataResponse } }, description: "Cap, used-today, and remaining." },
|
|
419
|
+
...errorResponses,
|
|
420
|
+
},
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
// ── Order notes + activity timeline (issue #56) ─────────────────────────────
|
|
424
|
+
|
|
425
|
+
export const CreateOrderNoteBodySchema = z.object({
|
|
426
|
+
body: z.string().min(1).max(2000),
|
|
427
|
+
pinned: z.boolean().optional(),
|
|
428
|
+
}).openapi("CreateOrderNoteRequest");
|
|
429
|
+
|
|
430
|
+
const NoteParam = z.object({
|
|
431
|
+
id: z.uuid(),
|
|
432
|
+
noteId: z.uuid(),
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
const NoteDataResponse = z.object({ data: z.any() }).openapi("OrderNoteResponse");
|
|
436
|
+
|
|
437
|
+
export const createOrderNoteRoute = createRoute({
|
|
438
|
+
method: "post",
|
|
439
|
+
path: "/{id}/notes",
|
|
440
|
+
tags: ["Orders"],
|
|
441
|
+
summary: "Add an operator note to an order",
|
|
442
|
+
request: {
|
|
443
|
+
params: OrderIdParam,
|
|
444
|
+
body: { content: { "application/json": { schema: CreateOrderNoteBodySchema } }, required: true },
|
|
445
|
+
},
|
|
446
|
+
responses: {
|
|
447
|
+
201: { content: { "application/json": { schema: NoteDataResponse } }, description: "Note created." },
|
|
448
|
+
...errorResponses,
|
|
449
|
+
},
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
export const listOrderNotesRoute = createRoute({
|
|
453
|
+
method: "get",
|
|
454
|
+
path: "/{id}/notes",
|
|
455
|
+
tags: ["Orders"],
|
|
456
|
+
summary: "List an order's notes (pinned first)",
|
|
457
|
+
request: { params: OrderIdParam },
|
|
458
|
+
responses: {
|
|
459
|
+
200: { content: { "application/json": { schema: NoteDataResponse } }, description: "Notes." },
|
|
460
|
+
...errorResponses,
|
|
461
|
+
},
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
export const deleteOrderNoteRoute = createRoute({
|
|
465
|
+
method: "delete",
|
|
466
|
+
path: "/{id}/notes/{noteId}",
|
|
467
|
+
tags: ["Orders"],
|
|
468
|
+
summary: "Delete an order note",
|
|
469
|
+
request: { params: NoteParam },
|
|
470
|
+
responses: {
|
|
471
|
+
200: { content: { "application/json": { schema: NoteDataResponse } }, description: "Note deleted." },
|
|
472
|
+
...errorResponses,
|
|
473
|
+
},
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
export const orderTimelineRoute = createRoute({
|
|
477
|
+
method: "get",
|
|
478
|
+
path: "/{id}/timeline",
|
|
479
|
+
tags: ["Orders"],
|
|
480
|
+
summary: "Merged activity timeline: status changes + notes + refunds, newest first",
|
|
481
|
+
request: { params: OrderIdParam },
|
|
482
|
+
responses: {
|
|
483
|
+
200: { content: { "application/json": { schema: NoteDataResponse } }, description: "Timeline events." },
|
|
484
|
+
...errorResponses,
|
|
485
|
+
},
|
|
486
|
+
});
|
|
@@ -57,6 +57,81 @@ export const setBasePriceRoute = createRoute({
|
|
|
57
57
|
},
|
|
58
58
|
});
|
|
59
59
|
|
|
60
|
+
export const UpdateModifierBodySchema = z.object({
|
|
61
|
+
name: z.string().optional(),
|
|
62
|
+
value: z.number().optional(),
|
|
63
|
+
priority: z.number().int().optional(),
|
|
64
|
+
validFrom: z.coerce.date().nullable().optional(),
|
|
65
|
+
validUntil: z.coerce.date().nullable().optional(),
|
|
66
|
+
minQuantity: z.number().int().nullable().optional(),
|
|
67
|
+
maxQuantity: z.number().int().nullable().optional(),
|
|
68
|
+
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
69
|
+
}).openapi("UpdateModifierRequest");
|
|
70
|
+
|
|
71
|
+
const ModifierIdParam = z.object({
|
|
72
|
+
id: z.uuid().openapi({ example: "550e8400-e29b-41d4-a716-446655440000" }),
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
export const listModifiersRoute = createRoute({
|
|
76
|
+
method: "get",
|
|
77
|
+
path: "/modifiers",
|
|
78
|
+
tags: ["Pricing"],
|
|
79
|
+
summary: "List price modifiers",
|
|
80
|
+
description: "Lists active and scheduled price modifiers. Filter by entityId, currency, or active=true (currently within validity window).",
|
|
81
|
+
request: {
|
|
82
|
+
query: z.object({
|
|
83
|
+
entityId: z.string().optional(),
|
|
84
|
+
currency: z.string().optional(),
|
|
85
|
+
active: z.string().optional().openapi({ example: "true" }),
|
|
86
|
+
}),
|
|
87
|
+
},
|
|
88
|
+
responses: {
|
|
89
|
+
200: {
|
|
90
|
+
content: { "application/json": { schema: PricingResponseSchema } },
|
|
91
|
+
description: "Price modifiers",
|
|
92
|
+
},
|
|
93
|
+
...errorResponses,
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
export const updateModifierRoute = createRoute({
|
|
98
|
+
method: "patch",
|
|
99
|
+
path: "/modifiers/{id}",
|
|
100
|
+
tags: ["Pricing"],
|
|
101
|
+
summary: "Update a price modifier's value or validity",
|
|
102
|
+
request: {
|
|
103
|
+
params: ModifierIdParam,
|
|
104
|
+
body: {
|
|
105
|
+
content: { "application/json": { schema: UpdateModifierBodySchema } },
|
|
106
|
+
required: true,
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
responses: {
|
|
110
|
+
200: {
|
|
111
|
+
content: { "application/json": { schema: PricingResponseSchema } },
|
|
112
|
+
description: "Price modifier updated.",
|
|
113
|
+
},
|
|
114
|
+
...errorResponses,
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
export const deleteModifierRoute = createRoute({
|
|
119
|
+
method: "delete",
|
|
120
|
+
path: "/modifiers/{id}",
|
|
121
|
+
tags: ["Pricing"],
|
|
122
|
+
summary: "Delete a price modifier (end a sale early)",
|
|
123
|
+
request: {
|
|
124
|
+
params: ModifierIdParam,
|
|
125
|
+
},
|
|
126
|
+
responses: {
|
|
127
|
+
200: {
|
|
128
|
+
content: { "application/json": { schema: PricingResponseSchema } },
|
|
129
|
+
description: "Price modifier deleted.",
|
|
130
|
+
},
|
|
131
|
+
...errorResponses,
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
|
|
60
135
|
export const createModifierRoute = createRoute({
|
|
61
136
|
method: "post",
|
|
62
137
|
path: "/modifiers",
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { z, createRoute } from "@hono/zod-openapi";
|
|
2
|
+
import { errorResponses } from "./shared.js";
|
|
3
|
+
|
|
4
|
+
function isValidTimezone(tz: string): boolean {
|
|
5
|
+
try {
|
|
6
|
+
new Intl.DateTimeFormat("en-US", { timeZone: tz });
|
|
7
|
+
return true;
|
|
8
|
+
} catch {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Known typed groups. Every field is optional (PATCH is a partial merge) and
|
|
14
|
+
// nullable (null deletes the key). Unknown groups accept any flat JSON object
|
|
15
|
+
// so plugins can namespace their own settings.
|
|
16
|
+
export const GeneralSettingsPatchSchema = z.object({
|
|
17
|
+
currency: z.string().regex(/^[A-Z]{3}$/, "ISO 4217 uppercase code").nullable().optional(),
|
|
18
|
+
timezone: z.string().refine(isValidTimezone, "Must be a valid IANA timezone (e.g. Asia/Colombo)").nullable().optional(),
|
|
19
|
+
locale: z.string().min(2).nullable().optional(),
|
|
20
|
+
}).strict();
|
|
21
|
+
|
|
22
|
+
export const BrandingSettingsPatchSchema = z.object({
|
|
23
|
+
storeName: z.string().nullable().optional(),
|
|
24
|
+
receiptHeader: z.string().nullable().optional(),
|
|
25
|
+
receiptFooter: z.string().nullable().optional(),
|
|
26
|
+
logoUrl: z.string().nullable().optional(),
|
|
27
|
+
address: z.string().nullable().optional(),
|
|
28
|
+
phone: z.string().nullable().optional(),
|
|
29
|
+
email: z.string().nullable().optional(),
|
|
30
|
+
taxId: z.string().nullable().optional(),
|
|
31
|
+
}).strict();
|
|
32
|
+
|
|
33
|
+
export const PoliciesSettingsPatchSchema = z.record(
|
|
34
|
+
z.string(),
|
|
35
|
+
z.union([z.string(), z.number(), z.boolean(), z.null()]),
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
export const KNOWN_GROUP_SCHEMAS: Record<string, z.ZodTypeAny> = {
|
|
39
|
+
general: GeneralSettingsPatchSchema,
|
|
40
|
+
branding: BrandingSettingsPatchSchema,
|
|
41
|
+
policies: PoliciesSettingsPatchSchema,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const GroupParam = z.object({
|
|
45
|
+
group: z.string().regex(/^[a-z][a-z0-9_-]*$/).openapi({ example: "branding" }),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const SettingsPatchBodySchema = z
|
|
49
|
+
.record(z.string(), z.unknown())
|
|
50
|
+
.openapi("SettingsPatchRequest", {
|
|
51
|
+
description:
|
|
52
|
+
"Shallow merge into the group: keys overwrite, null deletes a key. Known groups (general, branding, policies) are validated.",
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const DataResponse = z.object({ data: z.any() }).openapi("SettingsResponse");
|
|
56
|
+
|
|
57
|
+
export const getAllSettingsRoute = createRoute({
|
|
58
|
+
method: "get",
|
|
59
|
+
path: "/",
|
|
60
|
+
tags: ["Settings"],
|
|
61
|
+
summary: "Get all settings groups for the organization",
|
|
62
|
+
responses: {
|
|
63
|
+
200: { content: { "application/json": { schema: DataResponse } }, description: "All settings groups" },
|
|
64
|
+
...errorResponses,
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
export const getSettingsGroupRoute = createRoute({
|
|
69
|
+
method: "get",
|
|
70
|
+
path: "/{group}",
|
|
71
|
+
tags: ["Settings"],
|
|
72
|
+
summary: "Get one settings group",
|
|
73
|
+
request: { params: GroupParam },
|
|
74
|
+
responses: {
|
|
75
|
+
200: { content: { "application/json": { schema: DataResponse } }, description: "Settings group (empty object if unset)" },
|
|
76
|
+
...errorResponses,
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
export const patchSettingsGroupRoute = createRoute({
|
|
81
|
+
method: "patch",
|
|
82
|
+
path: "/{group}",
|
|
83
|
+
tags: ["Settings"],
|
|
84
|
+
summary: "Merge values into a settings group",
|
|
85
|
+
request: {
|
|
86
|
+
params: GroupParam,
|
|
87
|
+
body: { content: { "application/json": { schema: SettingsPatchBodySchema } }, required: true },
|
|
88
|
+
},
|
|
89
|
+
responses: {
|
|
90
|
+
200: { content: { "application/json": { schema: DataResponse } }, description: "The merged group value" },
|
|
91
|
+
...errorResponses,
|
|
92
|
+
},
|
|
93
|
+
});
|