@porulle/core 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (325) hide show
  1. package/dist/auth/middleware.d.ts.map +1 -1
  2. package/dist/auth/middleware.js +26 -7
  3. package/dist/auth/setup.d.ts.map +1 -1
  4. package/dist/auth/setup.js +8 -5
  5. package/dist/config/defaults.d.ts.map +1 -1
  6. package/dist/config/defaults.js +7 -0
  7. package/dist/config/types.d.ts +26 -1
  8. package/dist/config/types.d.ts.map +1 -1
  9. package/dist/generated/plugin-manifest.d.ts +3 -0
  10. package/dist/generated/plugin-manifest.d.ts.map +1 -1
  11. package/dist/generated/plugin-manifest.js +1 -0
  12. package/dist/hooks/checkout-completion.d.ts.map +1 -1
  13. package/dist/hooks/checkout-completion.js +2 -2
  14. package/dist/hooks/checkout.d.ts.map +1 -1
  15. package/dist/hooks/checkout.js +17 -2
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +1 -0
  19. package/dist/interfaces/rest/index.d.ts.map +1 -1
  20. package/dist/interfaces/rest/index.js +12 -0
  21. package/dist/interfaces/rest/routes/admin/staff.d.ts +12 -0
  22. package/dist/interfaces/rest/routes/admin/staff.d.ts.map +1 -0
  23. package/dist/interfaces/rest/routes/admin/staff.js +206 -0
  24. package/dist/interfaces/rest/routes/analytics.d.ts +5 -0
  25. package/dist/interfaces/rest/routes/analytics.d.ts.map +1 -0
  26. package/dist/interfaces/rest/routes/analytics.js +24 -0
  27. package/dist/interfaces/rest/routes/carts.d.ts.map +1 -1
  28. package/dist/interfaces/rest/routes/carts.js +39 -2
  29. package/dist/interfaces/rest/routes/catalog.d.ts.map +1 -1
  30. package/dist/interfaces/rest/routes/catalog.js +17 -1
  31. package/dist/interfaces/rest/routes/checkout.d.ts +9 -0
  32. package/dist/interfaces/rest/routes/checkout.d.ts.map +1 -1
  33. package/dist/interfaces/rest/routes/checkout.js +97 -21
  34. package/dist/interfaces/rest/routes/documents.d.ts +10 -0
  35. package/dist/interfaces/rest/routes/documents.d.ts.map +1 -0
  36. package/dist/interfaces/rest/routes/documents.js +40 -0
  37. package/dist/interfaces/rest/routes/inventory.d.ts.map +1 -1
  38. package/dist/interfaces/rest/routes/inventory.js +13 -0
  39. package/dist/interfaces/rest/routes/orders.d.ts.map +1 -1
  40. package/dist/interfaces/rest/routes/orders.js +111 -1
  41. package/dist/interfaces/rest/routes/pricing.d.ts.map +1 -1
  42. package/dist/interfaces/rest/routes/pricing.js +36 -1
  43. package/dist/interfaces/rest/routes/settings.d.ts +5 -0
  44. package/dist/interfaces/rest/routes/settings.d.ts.map +1 -0
  45. package/dist/interfaces/rest/routes/settings.js +50 -0
  46. package/dist/interfaces/rest/routes/shipping.d.ts +5 -0
  47. package/dist/interfaces/rest/routes/shipping.d.ts.map +1 -0
  48. package/dist/interfaces/rest/routes/shipping.js +68 -0
  49. package/dist/interfaces/rest/routes/tax.d.ts +5 -0
  50. package/dist/interfaces/rest/routes/tax.d.ts.map +1 -0
  51. package/dist/interfaces/rest/routes/tax.js +68 -0
  52. package/dist/interfaces/rest/schemas/admin-staff.d.ts +899 -0
  53. package/dist/interfaces/rest/schemas/admin-staff.d.ts.map +1 -0
  54. package/dist/interfaces/rest/schemas/admin-staff.js +98 -0
  55. package/dist/interfaces/rest/schemas/analytics.d.ts +252 -0
  56. package/dist/interfaces/rest/schemas/analytics.d.ts.map +1 -0
  57. package/dist/interfaces/rest/schemas/analytics.js +33 -0
  58. package/dist/interfaces/rest/schemas/carts.d.ts +656 -0
  59. package/dist/interfaces/rest/schemas/carts.d.ts.map +1 -1
  60. package/dist/interfaces/rest/schemas/carts.js +38 -0
  61. package/dist/interfaces/rest/schemas/catalog.d.ts +1218 -1
  62. package/dist/interfaces/rest/schemas/catalog.d.ts.map +1 -1
  63. package/dist/interfaces/rest/schemas/catalog.js +48 -0
  64. package/dist/interfaces/rest/schemas/checkout.d.ts +2 -0
  65. package/dist/interfaces/rest/schemas/checkout.d.ts.map +1 -1
  66. package/dist/interfaces/rest/schemas/checkout.js +4 -0
  67. package/dist/interfaces/rest/schemas/customer-portal.d.ts +35 -0
  68. package/dist/interfaces/rest/schemas/customer-portal.d.ts.map +1 -1
  69. package/dist/interfaces/rest/schemas/documents.d.ts +509 -0
  70. package/dist/interfaces/rest/schemas/documents.d.ts.map +1 -0
  71. package/dist/interfaces/rest/schemas/documents.js +65 -0
  72. package/dist/interfaces/rest/schemas/orders.d.ts +3110 -43
  73. package/dist/interfaces/rest/schemas/orders.d.ts.map +1 -1
  74. package/dist/interfaces/rest/schemas/orders.js +234 -1
  75. package/dist/interfaces/rest/schemas/pricing.d.ts +405 -0
  76. package/dist/interfaces/rest/schemas/pricing.d.ts.map +1 -1
  77. package/dist/interfaces/rest/schemas/pricing.js +70 -0
  78. package/dist/interfaces/rest/schemas/responses.d.ts +190 -0
  79. package/dist/interfaces/rest/schemas/responses.d.ts.map +1 -1
  80. package/dist/interfaces/rest/schemas/search.d.ts +17 -0
  81. package/dist/interfaces/rest/schemas/search.d.ts.map +1 -1
  82. package/dist/interfaces/rest/schemas/settings.d.ts +397 -0
  83. package/dist/interfaces/rest/schemas/settings.d.ts.map +1 -0
  84. package/dist/interfaces/rest/schemas/settings.js +79 -0
  85. package/dist/interfaces/rest/schemas/shipping.d.ts +1093 -0
  86. package/dist/interfaces/rest/schemas/shipping.d.ts.map +1 -0
  87. package/dist/interfaces/rest/schemas/shipping.js +146 -0
  88. package/dist/interfaces/rest/schemas/tax.d.ts +1074 -0
  89. package/dist/interfaces/rest/schemas/tax.d.ts.map +1 -0
  90. package/dist/interfaces/rest/schemas/tax.js +132 -0
  91. package/dist/kernel/database/schema.d.ts +4 -0
  92. package/dist/kernel/database/schema.d.ts.map +1 -1
  93. package/dist/kernel/database/schema.js +8 -0
  94. package/dist/kernel/database/scoped-db.d.ts +2 -1
  95. package/dist/kernel/database/scoped-db.d.ts.map +1 -1
  96. package/dist/kernel/database/scoped-db.js +62 -1
  97. package/dist/kernel/hooks/types.d.ts +1 -1
  98. package/dist/kernel/hooks/types.d.ts.map +1 -1
  99. package/dist/kernel/plugin/manifest.d.ts +26 -0
  100. package/dist/kernel/plugin/manifest.d.ts.map +1 -1
  101. package/dist/kernel/plugin/manifest.js +17 -2
  102. package/dist/modules/analytics/drizzle-adapter.d.ts.map +1 -1
  103. package/dist/modules/analytics/drizzle-adapter.js +16 -3
  104. package/dist/modules/analytics/module.d.ts.map +1 -1
  105. package/dist/modules/analytics/module.js +6 -0
  106. package/dist/modules/analytics/reports.d.ts +58 -0
  107. package/dist/modules/analytics/reports.d.ts.map +1 -0
  108. package/dist/modules/analytics/reports.js +249 -0
  109. package/dist/modules/analytics/service.d.ts +10 -0
  110. package/dist/modules/analytics/service.d.ts.map +1 -1
  111. package/dist/modules/analytics/service.js +14 -0
  112. package/dist/modules/cart/repository/index.d.ts +7 -0
  113. package/dist/modules/cart/repository/index.d.ts.map +1 -1
  114. package/dist/modules/cart/repository/index.js +25 -1
  115. package/dist/modules/cart/schema.d.ts +17 -0
  116. package/dist/modules/cart/schema.d.ts.map +1 -1
  117. package/dist/modules/cart/schema.js +2 -0
  118. package/dist/modules/cart/schemas.d.ts +1 -0
  119. package/dist/modules/cart/schemas.d.ts.map +1 -1
  120. package/dist/modules/cart/schemas.js +1 -0
  121. package/dist/modules/cart/service.d.ts +32 -0
  122. package/dist/modules/cart/service.d.ts.map +1 -1
  123. package/dist/modules/cart/service.js +61 -2
  124. package/dist/modules/catalog/entity-service.d.ts +21 -0
  125. package/dist/modules/catalog/entity-service.d.ts.map +1 -1
  126. package/dist/modules/catalog/entity-service.js +166 -5
  127. package/dist/modules/catalog/schema.d.ts +34 -0
  128. package/dist/modules/catalog/schema.d.ts.map +1 -1
  129. package/dist/modules/catalog/schema.js +5 -0
  130. package/dist/modules/catalog/schemas.d.ts +3 -0
  131. package/dist/modules/catalog/schemas.d.ts.map +1 -1
  132. package/dist/modules/catalog/schemas.js +3 -0
  133. package/dist/modules/catalog/service.d.ts +36 -0
  134. package/dist/modules/catalog/service.d.ts.map +1 -1
  135. package/dist/modules/catalog/service.js +6 -0
  136. package/dist/modules/customers/service.d.ts.map +1 -1
  137. package/dist/modules/customers/service.js +6 -0
  138. package/dist/modules/documents/module.d.ts +7 -0
  139. package/dist/modules/documents/module.d.ts.map +1 -0
  140. package/dist/modules/documents/module.js +12 -0
  141. package/dist/modules/documents/pdf.d.ts +25 -0
  142. package/dist/modules/documents/pdf.d.ts.map +1 -0
  143. package/dist/modules/documents/pdf.js +80 -0
  144. package/dist/modules/documents/render.d.ts +32 -0
  145. package/dist/modules/documents/render.d.ts.map +1 -0
  146. package/dist/modules/documents/render.js +190 -0
  147. package/dist/modules/documents/repository/index.d.ts +25 -0
  148. package/dist/modules/documents/repository/index.d.ts.map +1 -0
  149. package/dist/modules/documents/repository/index.js +47 -0
  150. package/dist/modules/documents/schema.d.ts +214 -0
  151. package/dist/modules/documents/schema.d.ts.map +1 -0
  152. package/dist/modules/documents/schema.js +42 -0
  153. package/dist/modules/documents/service.d.ts +51 -0
  154. package/dist/modules/documents/service.d.ts.map +1 -0
  155. package/dist/modules/documents/service.js +119 -0
  156. package/dist/modules/fulfillment/service.d.ts +18 -0
  157. package/dist/modules/fulfillment/service.d.ts.map +1 -1
  158. package/dist/modules/fulfillment/service.js +85 -1
  159. package/dist/modules/orders/repository/index.d.ts +18 -1
  160. package/dist/modules/orders/repository/index.d.ts.map +1 -1
  161. package/dist/modules/orders/repository/index.js +86 -1
  162. package/dist/modules/orders/schema.d.ts +391 -0
  163. package/dist/modules/orders/schema.d.ts.map +1 -1
  164. package/dist/modules/orders/schema.js +56 -1
  165. package/dist/modules/orders/service.d.ts +102 -3
  166. package/dist/modules/orders/service.d.ts.map +1 -1
  167. package/dist/modules/orders/service.js +653 -29
  168. package/dist/modules/orders/stale-order-cleanup.d.ts.map +1 -1
  169. package/dist/modules/orders/stale-order-cleanup.js +13 -1
  170. package/dist/modules/pricing/repository/index.d.ts +5 -0
  171. package/dist/modules/pricing/repository/index.d.ts.map +1 -1
  172. package/dist/modules/pricing/repository/index.js +19 -0
  173. package/dist/modules/pricing/service.d.ts +18 -0
  174. package/dist/modules/pricing/service.d.ts.map +1 -1
  175. package/dist/modules/pricing/service.js +43 -5
  176. package/dist/modules/settings/module.d.ts +6 -0
  177. package/dist/modules/settings/module.d.ts.map +1 -0
  178. package/dist/modules/settings/module.js +11 -0
  179. package/dist/modules/settings/repository/index.d.ts +16 -0
  180. package/dist/modules/settings/repository/index.d.ts.map +1 -0
  181. package/dist/modules/settings/repository/index.js +41 -0
  182. package/dist/modules/settings/schema.d.ts +121 -0
  183. package/dist/modules/settings/schema.d.ts.map +1 -0
  184. package/dist/modules/settings/schema.js +24 -0
  185. package/dist/modules/settings/service.d.ts +26 -0
  186. package/dist/modules/settings/service.d.ts.map +1 -0
  187. package/dist/modules/settings/service.js +51 -0
  188. package/dist/modules/shipping/calculator.d.ts +5 -0
  189. package/dist/modules/shipping/calculator.d.ts.map +1 -1
  190. package/dist/modules/shipping/calculator.js +10 -0
  191. package/dist/modules/shipping/module.d.ts +5 -1
  192. package/dist/modules/shipping/module.d.ts.map +1 -1
  193. package/dist/modules/shipping/module.js +4 -1
  194. package/dist/modules/shipping/repository/index.d.ts +27 -2
  195. package/dist/modules/shipping/repository/index.d.ts.map +1 -1
  196. package/dist/modules/shipping/repository/index.js +105 -1
  197. package/dist/modules/shipping/schema.d.ts +419 -0
  198. package/dist/modules/shipping/schema.d.ts.map +1 -0
  199. package/dist/modules/shipping/schema.js +56 -0
  200. package/dist/modules/shipping/service.d.ts +54 -0
  201. package/dist/modules/shipping/service.d.ts.map +1 -1
  202. package/dist/modules/shipping/service.js +161 -2
  203. package/dist/modules/tax/adapter.d.ts +13 -0
  204. package/dist/modules/tax/adapter.d.ts.map +1 -1
  205. package/dist/modules/tax/module.d.ts +5 -1
  206. package/dist/modules/tax/module.d.ts.map +1 -1
  207. package/dist/modules/tax/module.js +4 -1
  208. package/dist/modules/tax/repository/index.d.ts +38 -2
  209. package/dist/modules/tax/repository/index.d.ts.map +1 -1
  210. package/dist/modules/tax/repository/index.js +138 -1
  211. package/dist/modules/tax/schema.d.ts +355 -0
  212. package/dist/modules/tax/schema.d.ts.map +1 -0
  213. package/dist/modules/tax/schema.js +55 -0
  214. package/dist/modules/tax/service.d.ts +60 -1
  215. package/dist/modules/tax/service.d.ts.map +1 -1
  216. package/dist/modules/tax/service.js +227 -2
  217. package/dist/runtime/kernel-modules.d.ts +15 -2
  218. package/dist/runtime/kernel-modules.d.ts.map +1 -1
  219. package/dist/runtime/kernel-modules.js +4 -0
  220. package/dist/runtime/kernel-types.d.ts +5 -1
  221. package/dist/runtime/kernel-types.d.ts.map +1 -1
  222. package/dist/runtime/kernel-types.js +2 -0
  223. package/dist/runtime/server.js +1 -1
  224. package/dist/test-utils/create-plugin-test-app.d.ts +3 -0
  225. package/dist/test-utils/create-plugin-test-app.d.ts.map +1 -1
  226. package/dist/test-utils/create-plugin-test-app.js +15 -3
  227. package/dist/test-utils/create-test-config.d.ts.map +1 -1
  228. package/dist/test-utils/create-test-config.js +2 -0
  229. package/dist/test-utils/order-test-helpers.d.ts +12 -0
  230. package/dist/test-utils/order-test-helpers.d.ts.map +1 -0
  231. package/dist/test-utils/order-test-helpers.js +18 -0
  232. package/dist/test-utils/rest-api-test-utils.d.ts.map +1 -1
  233. package/dist/test-utils/rest-api-test-utils.js +7 -0
  234. package/dist/test-utils/test-actors.js +1 -1
  235. package/dist/testing.d.ts +1 -0
  236. package/dist/testing.d.ts.map +1 -1
  237. package/dist/testing.js +1 -0
  238. package/package.json +3 -3
  239. package/src/auth/middleware.ts +26 -7
  240. package/src/auth/setup.ts +11 -4
  241. package/src/config/defaults.ts +7 -0
  242. package/src/config/types.ts +26 -1
  243. package/src/generated/plugin-capabilities.d.ts +1 -0
  244. package/src/generated/plugin-manifest.ts +1 -0
  245. package/src/generated/plugin-repositories.d.ts +1 -0
  246. package/src/hooks/checkout-completion.ts +22 -16
  247. package/src/hooks/checkout.ts +36 -11
  248. package/src/index.ts +1 -0
  249. package/src/interfaces/rest/index.ts +12 -0
  250. package/src/interfaces/rest/routes/admin/staff.ts +260 -0
  251. package/src/interfaces/rest/routes/analytics.ts +33 -0
  252. package/src/interfaces/rest/routes/carts.ts +52 -2
  253. package/src/interfaces/rest/routes/catalog.ts +34 -0
  254. package/src/interfaces/rest/routes/checkout.ts +114 -27
  255. package/src/interfaces/rest/routes/documents.ts +61 -0
  256. package/src/interfaces/rest/routes/inventory.ts +14 -0
  257. package/src/interfaces/rest/routes/orders.ts +148 -1
  258. package/src/interfaces/rest/routes/pricing.ts +44 -1
  259. package/src/interfaces/rest/routes/settings.ts +61 -0
  260. package/src/interfaces/rest/routes/shipping.ts +92 -0
  261. package/src/interfaces/rest/routes/tax.ts +90 -0
  262. package/src/interfaces/rest/schemas/admin-staff.ts +110 -0
  263. package/src/interfaces/rest/schemas/analytics.ts +39 -0
  264. package/src/interfaces/rest/schemas/carts.ts +40 -0
  265. package/src/interfaces/rest/schemas/catalog.ts +53 -0
  266. package/src/interfaces/rest/schemas/checkout.ts +4 -0
  267. package/src/interfaces/rest/schemas/documents.ts +72 -0
  268. package/src/interfaces/rest/schemas/orders.ts +258 -1
  269. package/src/interfaces/rest/schemas/pricing.ts +75 -0
  270. package/src/interfaces/rest/schemas/settings.ts +93 -0
  271. package/src/interfaces/rest/schemas/shipping.ts +164 -0
  272. package/src/interfaces/rest/schemas/tax.ts +148 -0
  273. package/src/kernel/database/schema.ts +12 -0
  274. package/src/kernel/database/scoped-db.ts +70 -1
  275. package/src/kernel/hooks/types.ts +1 -0
  276. package/src/kernel/plugin/manifest.ts +41 -2
  277. package/src/modules/analytics/drizzle-adapter.ts +19 -3
  278. package/src/modules/analytics/module.ts +10 -0
  279. package/src/modules/analytics/reports.ts +311 -0
  280. package/src/modules/analytics/service.ts +23 -0
  281. package/src/modules/cart/repository/index.ts +30 -1
  282. package/src/modules/cart/schema.ts +2 -0
  283. package/src/modules/cart/schemas.ts +1 -0
  284. package/src/modules/cart/service.ts +104 -2
  285. package/src/modules/catalog/entity-service.ts +196 -4
  286. package/src/modules/catalog/schema.ts +5 -0
  287. package/src/modules/catalog/schemas.ts +3 -0
  288. package/src/modules/catalog/service.ts +30 -0
  289. package/src/modules/customers/service.ts +6 -0
  290. package/src/modules/documents/module.ts +19 -0
  291. package/src/modules/documents/pdf.ts +101 -0
  292. package/src/modules/documents/render.ts +231 -0
  293. package/src/modules/documents/repository/index.ts +68 -0
  294. package/src/modules/documents/schema.ts +44 -0
  295. package/src/modules/documents/service.ts +176 -0
  296. package/src/modules/fulfillment/service.ts +139 -1
  297. package/src/modules/orders/repository/index.ts +127 -1
  298. package/src/modules/orders/schema.ts +58 -1
  299. package/src/modules/orders/service.ts +1041 -65
  300. package/src/modules/orders/stale-order-cleanup.ts +18 -4
  301. package/src/modules/pricing/repository/index.ts +34 -0
  302. package/src/modules/pricing/service.ts +76 -6
  303. package/src/modules/settings/module.ts +18 -0
  304. package/src/modules/settings/repository/index.ts +59 -0
  305. package/src/modules/settings/schema.ts +25 -0
  306. package/src/modules/settings/service.ts +82 -0
  307. package/src/modules/shipping/calculator.ts +27 -0
  308. package/src/modules/shipping/module.ts +6 -2
  309. package/src/modules/shipping/repository/index.ts +136 -2
  310. package/src/modules/shipping/schema.ts +58 -0
  311. package/src/modules/shipping/service.ts +260 -1
  312. package/src/modules/tax/adapter.ts +9 -0
  313. package/src/modules/tax/module.ts +6 -2
  314. package/src/modules/tax/repository/index.ts +169 -2
  315. package/src/modules/tax/schema.ts +57 -0
  316. package/src/modules/tax/service.ts +313 -2
  317. package/src/runtime/kernel-modules.ts +4 -0
  318. package/src/runtime/kernel-types.ts +6 -0
  319. package/src/runtime/server.ts +1 -1
  320. package/src/test-utils/create-plugin-test-app.ts +17 -4
  321. package/src/test-utils/create-test-config.ts +2 -0
  322. package/src/test-utils/order-test-helpers.ts +26 -0
  323. package/src/test-utils/rest-api-test-utils.ts +7 -0
  324. package/src/test-utils/test-actors.ts +1 -1
  325. package/src/testing.ts +1 -0
@@ -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,12 +144,47 @@ 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: "/",
146
185
  tags: ["Orders"],
147
186
  summary: "Create a draft / manual order",
148
- description: "Operator-created order (phone / POS / manual) with line items and totals, optionally without immediate payment.",
187
+ description: "Requires `orders:manage`. Creates an operator-entered order (phone / POS / manual); supplied line prices are stored as audited manual overrides.",
149
188
  request: {
150
189
  body: {
151
190
  content: { "application/json": { schema: CreateOrderBodySchema } },
@@ -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: "Requires `orders:manage`. Adds a line item to a non-terminal order, records its supplied price as a manual override, and recalculates totals.",
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
+ });
@@ -0,0 +1,164 @@
1
+ import { z, createRoute } from "@hono/zod-openapi";
2
+ import { errorResponses } from "./shared.js";
3
+
4
+ // ─── Request Schemas ────────────────────────────────────────────────────────
5
+
6
+ export const CreateShippingZoneBodySchema = z.object({
7
+ name: z.string().min(1).openapi({ example: "United States" }),
8
+ countries: z.array(z.string().min(1)).min(1).openapi({ example: ["US"] }),
9
+ states: z.array(z.string().min(1)).optional().openapi({ example: ["NY", "NJ"] }),
10
+ priority: z.number().int().optional(),
11
+ isActive: z.boolean().optional(),
12
+ }).openapi("CreateShippingZoneRequest");
13
+
14
+ export const UpdateShippingZoneBodySchema = z.object({
15
+ name: z.string().min(1).optional(),
16
+ countries: z.array(z.string().min(1)).min(1).optional(),
17
+ states: z.array(z.string().min(1)).optional(),
18
+ priority: z.number().int().optional(),
19
+ isActive: z.boolean().optional(),
20
+ }).openapi("UpdateShippingZoneRequest");
21
+
22
+ export const CreateShippingRateBodySchema = z.object({
23
+ zoneId: z.uuid(),
24
+ name: z.string().min(1).openapi({ example: "Standard" }),
25
+ amount: z.number().int().nonnegative().openapi({ example: 1500, description: "Flat cost in minor units" }),
26
+ currency: z.string().length(3).optional().openapi({ example: "USD" }),
27
+ minSubtotal: z.number().int().nonnegative().optional(),
28
+ maxSubtotal: z.number().int().nonnegative().optional(),
29
+ minWeightGrams: z.number().int().nonnegative().optional(),
30
+ maxWeightGrams: z.number().int().nonnegative().optional(),
31
+ freeShippingThreshold: z.number().int().nonnegative().optional().openapi({ example: 10000, description: "Subtotal at or above which shipping is free" }),
32
+ isActive: z.boolean().optional(),
33
+ }).openapi("CreateShippingRateRequest");
34
+
35
+ export const UpdateShippingRateBodySchema = z.object({
36
+ name: z.string().min(1).optional(),
37
+ amount: z.number().int().nonnegative().optional(),
38
+ currency: z.string().length(3).optional(),
39
+ minSubtotal: z.number().int().nonnegative().nullable().optional(),
40
+ maxSubtotal: z.number().int().nonnegative().nullable().optional(),
41
+ minWeightGrams: z.number().int().nonnegative().nullable().optional(),
42
+ maxWeightGrams: z.number().int().nonnegative().nullable().optional(),
43
+ freeShippingThreshold: z.number().int().nonnegative().nullable().optional(),
44
+ isActive: z.boolean().optional(),
45
+ }).openapi("UpdateShippingRateRequest");
46
+
47
+ // ─── Shared ─────────────────────────────────────────────────────────────────
48
+
49
+ const IdParam = z.object({
50
+ id: z.uuid().openapi({ example: "550e8400-e29b-41d4-a716-446655440000" }),
51
+ });
52
+
53
+ const DataResponse = z.object({ data: z.any() }).openapi("ShippingConfigResponse");
54
+
55
+ // ─── Zone Routes ────────────────────────────────────────────────────────────
56
+
57
+ export const createShippingZoneRoute = createRoute({
58
+ method: "post",
59
+ path: "/zones",
60
+ tags: ["Shipping"],
61
+ summary: "Create a shipping zone",
62
+ request: {
63
+ body: { content: { "application/json": { schema: CreateShippingZoneBodySchema } }, required: true },
64
+ },
65
+ responses: {
66
+ 201: { content: { "application/json": { schema: DataResponse } }, description: "Zone created." },
67
+ ...errorResponses,
68
+ },
69
+ });
70
+
71
+ export const listShippingZonesRoute = createRoute({
72
+ method: "get",
73
+ path: "/zones",
74
+ tags: ["Shipping"],
75
+ summary: "List shipping zones",
76
+ responses: {
77
+ 200: { content: { "application/json": { schema: DataResponse } }, description: "Zones" },
78
+ ...errorResponses,
79
+ },
80
+ });
81
+
82
+ export const updateShippingZoneRoute = createRoute({
83
+ method: "patch",
84
+ path: "/zones/{id}",
85
+ tags: ["Shipping"],
86
+ summary: "Update a shipping zone",
87
+ request: {
88
+ params: IdParam,
89
+ body: { content: { "application/json": { schema: UpdateShippingZoneBodySchema } }, required: true },
90
+ },
91
+ responses: {
92
+ 200: { content: { "application/json": { schema: DataResponse } }, description: "Zone updated." },
93
+ ...errorResponses,
94
+ },
95
+ });
96
+
97
+ export const deleteShippingZoneRoute = createRoute({
98
+ method: "delete",
99
+ path: "/zones/{id}",
100
+ tags: ["Shipping"],
101
+ summary: "Delete a shipping zone (and its rates)",
102
+ request: { params: IdParam },
103
+ responses: {
104
+ 200: { content: { "application/json": { schema: DataResponse } }, description: "Zone deleted." },
105
+ ...errorResponses,
106
+ },
107
+ });
108
+
109
+ // ─── Rate Routes ────────────────────────────────────────────────────────────
110
+
111
+ export const createShippingRateRoute = createRoute({
112
+ method: "post",
113
+ path: "/rates",
114
+ tags: ["Shipping"],
115
+ summary: "Create a shipping rate in a zone",
116
+ request: {
117
+ body: { content: { "application/json": { schema: CreateShippingRateBodySchema } }, required: true },
118
+ },
119
+ responses: {
120
+ 201: { content: { "application/json": { schema: DataResponse } }, description: "Rate created." },
121
+ ...errorResponses,
122
+ },
123
+ });
124
+
125
+ export const listShippingRatesRoute = createRoute({
126
+ method: "get",
127
+ path: "/rates",
128
+ tags: ["Shipping"],
129
+ summary: "List shipping rates",
130
+ request: {
131
+ query: z.object({ zoneId: z.string().optional() }),
132
+ },
133
+ responses: {
134
+ 200: { content: { "application/json": { schema: DataResponse } }, description: "Rates" },
135
+ ...errorResponses,
136
+ },
137
+ });
138
+
139
+ export const updateShippingRateRoute = createRoute({
140
+ method: "patch",
141
+ path: "/rates/{id}",
142
+ tags: ["Shipping"],
143
+ summary: "Update a shipping rate",
144
+ request: {
145
+ params: IdParam,
146
+ body: { content: { "application/json": { schema: UpdateShippingRateBodySchema } }, required: true },
147
+ },
148
+ responses: {
149
+ 200: { content: { "application/json": { schema: DataResponse } }, description: "Rate updated." },
150
+ ...errorResponses,
151
+ },
152
+ });
153
+
154
+ export const deleteShippingRateRoute = createRoute({
155
+ method: "delete",
156
+ path: "/rates/{id}",
157
+ tags: ["Shipping"],
158
+ summary: "Delete a shipping rate",
159
+ request: { params: IdParam },
160
+ responses: {
161
+ 200: { content: { "application/json": { schema: DataResponse } }, description: "Rate deleted." },
162
+ ...errorResponses,
163
+ },
164
+ });