@open-mercato/core 0.10.1-develop.7709.1.046124599c → 0.10.1-develop.7714.1.9dbb8200d8

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 (276) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/dist/generated/entities/asn/index.js +19 -0
  3. package/dist/generated/entities/asn/index.js.map +7 -0
  4. package/dist/generated/entities/putaway_task/index.js +25 -0
  5. package/dist/generated/entities/putaway_task/index.js.map +7 -0
  6. package/dist/generated/entities/receiving_line/index.js +21 -0
  7. package/dist/generated/entities/receiving_line/index.js.map +7 -0
  8. package/dist/generated/entities.ids.generated.js +4 -1
  9. package/dist/generated/entities.ids.generated.js.map +2 -2
  10. package/dist/generated/entity-fields-registry.js +34 -0
  11. package/dist/generated/entity-fields-registry.js.map +2 -2
  12. package/dist/modules/communication_channels/lib/sanitize-channel-html.js +22 -3
  13. package/dist/modules/communication_channels/lib/sanitize-channel-html.js.map +2 -2
  14. package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.client.js +40 -11
  15. package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.client.js.map +2 -2
  16. package/dist/modules/customers/components/calendar/CalendarEventEditor.js +13 -2
  17. package/dist/modules/customers/components/calendar/CalendarEventEditor.js.map +2 -2
  18. package/dist/modules/wms/acl.js +2 -0
  19. package/dist/modules/wms/acl.js.map +2 -2
  20. package/dist/modules/wms/api/asns/[id]/complete/route.js +61 -0
  21. package/dist/modules/wms/api/asns/[id]/complete/route.js.map +7 -0
  22. package/dist/modules/wms/api/asns/[id]/receive/route.js +68 -0
  23. package/dist/modules/wms/api/asns/[id]/receive/route.js.map +7 -0
  24. package/dist/modules/wms/api/asns/route.js +197 -0
  25. package/dist/modules/wms/api/asns/route.js.map +7 -0
  26. package/dist/modules/wms/api/inventory/helpers.js +42 -34
  27. package/dist/modules/wms/api/inventory/helpers.js.map +2 -2
  28. package/dist/modules/wms/api/inventory/receive/route.js +1 -1
  29. package/dist/modules/wms/api/inventory/receive/route.js.map +2 -2
  30. package/dist/modules/wms/api/listEnrichers.js +23 -0
  31. package/dist/modules/wms/api/listEnrichers.js.map +2 -2
  32. package/dist/modules/wms/api/putaway-tasks/[id]/assign/route.js +56 -0
  33. package/dist/modules/wms/api/putaway-tasks/[id]/assign/route.js.map +7 -0
  34. package/dist/modules/wms/api/putaway-tasks/[id]/cancel/route.js +56 -0
  35. package/dist/modules/wms/api/putaway-tasks/[id]/cancel/route.js.map +7 -0
  36. package/dist/modules/wms/api/putaway-tasks/[id]/complete/route.js +65 -0
  37. package/dist/modules/wms/api/putaway-tasks/[id]/complete/route.js.map +7 -0
  38. package/dist/modules/wms/api/putaway-tasks/[id]/start/route.js +56 -0
  39. package/dist/modules/wms/api/putaway-tasks/[id]/start/route.js.map +7 -0
  40. package/dist/modules/wms/api/putaway-tasks/create-from-balance/route.js +51 -0
  41. package/dist/modules/wms/api/putaway-tasks/create-from-balance/route.js.map +7 -0
  42. package/dist/modules/wms/api/putaway-tasks/route.js +192 -0
  43. package/dist/modules/wms/api/putaway-tasks/route.js.map +7 -0
  44. package/dist/modules/wms/api/receiving-lines/route.js +180 -0
  45. package/dist/modules/wms/api/receiving-lines/route.js.map +7 -0
  46. package/dist/modules/wms/api/scan/putaway/route.js +104 -0
  47. package/dist/modules/wms/api/scan/putaway/route.js.map +7 -0
  48. package/dist/modules/wms/api/scan/receive/route.js +109 -0
  49. package/dist/modules/wms/api/scan/receive/route.js.map +7 -0
  50. package/dist/modules/wms/api/scan/resolve-location/route.js +89 -0
  51. package/dist/modules/wms/api/scan/resolve-location/route.js.map +7 -0
  52. package/dist/modules/wms/api/scan/resolve-lot/route.js +89 -0
  53. package/dist/modules/wms/api/scan/resolve-lot/route.js.map +7 -0
  54. package/dist/modules/wms/backend/wms/asns/[id]/page.js +9 -0
  55. package/dist/modules/wms/backend/wms/asns/[id]/page.js.map +7 -0
  56. package/dist/modules/wms/backend/wms/asns/[id]/page.meta.js +18 -0
  57. package/dist/modules/wms/backend/wms/asns/[id]/page.meta.js.map +7 -0
  58. package/dist/modules/wms/backend/wms/asns/page.js +9 -0
  59. package/dist/modules/wms/backend/wms/asns/page.js.map +7 -0
  60. package/dist/modules/wms/backend/wms/asns/page.meta.js +18 -0
  61. package/dist/modules/wms/backend/wms/asns/page.meta.js.map +7 -0
  62. package/dist/modules/wms/backend/wms/putaway/page.js +9 -0
  63. package/dist/modules/wms/backend/wms/putaway/page.js.map +7 -0
  64. package/dist/modules/wms/backend/wms/putaway/page.meta.js +18 -0
  65. package/dist/modules/wms/backend/wms/putaway/page.meta.js.map +7 -0
  66. package/dist/modules/wms/backend/wms/receiving/page.js +8 -0
  67. package/dist/modules/wms/backend/wms/receiving/page.js.map +7 -0
  68. package/dist/modules/wms/backend/wms/receiving/page.meta.js +18 -0
  69. package/dist/modules/wms/backend/wms/receiving/page.meta.js.map +7 -0
  70. package/dist/modules/wms/ce.js +27 -0
  71. package/dist/modules/wms/ce.js.map +2 -2
  72. package/dist/modules/wms/commands/asn.js +1746 -0
  73. package/dist/modules/wms/commands/asn.js.map +7 -0
  74. package/dist/modules/wms/commands/index.js +2 -0
  75. package/dist/modules/wms/commands/index.js.map +2 -2
  76. package/dist/modules/wms/commands/inventory-actions.js +347 -243
  77. package/dist/modules/wms/commands/inventory-actions.js.map +2 -2
  78. package/dist/modules/wms/commands/putaway.js +945 -0
  79. package/dist/modules/wms/commands/putaway.js.map +7 -0
  80. package/dist/modules/wms/commands/shared.js +51 -0
  81. package/dist/modules/wms/commands/shared.js.map +2 -2
  82. package/dist/modules/wms/components/backend/AssignPutawayTaskDialog.js +278 -0
  83. package/dist/modules/wms/components/backend/AssignPutawayTaskDialog.js.map +7 -0
  84. package/dist/modules/wms/components/backend/CompletePutawayTaskDialog.js +327 -0
  85. package/dist/modules/wms/components/backend/CompletePutawayTaskDialog.js.map +7 -0
  86. package/dist/modules/wms/components/backend/CreateAsnDialog.js +404 -0
  87. package/dist/modules/wms/components/backend/CreateAsnDialog.js.map +7 -0
  88. package/dist/modules/wms/components/backend/InventoryOperationsSection.js +16 -3
  89. package/dist/modules/wms/components/backend/InventoryOperationsSection.js.map +2 -2
  90. package/dist/modules/wms/components/backend/ReceiveAsnLineDialog.js +476 -0
  91. package/dist/modules/wms/components/backend/ReceiveAsnLineDialog.js.map +7 -0
  92. package/dist/modules/wms/components/backend/ReceiveInventoryDialog.js +2 -2
  93. package/dist/modules/wms/components/backend/ReceiveInventoryDialog.js.map +2 -2
  94. package/dist/modules/wms/components/backend/WmsAsnDetailPage.js +524 -0
  95. package/dist/modules/wms/components/backend/WmsAsnDetailPage.js.map +7 -0
  96. package/dist/modules/wms/components/backend/WmsAsnsListPage.js +339 -0
  97. package/dist/modules/wms/components/backend/WmsAsnsListPage.js.map +7 -0
  98. package/dist/modules/wms/components/backend/WmsLocationDetailPage.js +5 -6
  99. package/dist/modules/wms/components/backend/WmsLocationDetailPage.js.map +2 -2
  100. package/dist/modules/wms/components/backend/WmsLotDetailPage.js +5 -6
  101. package/dist/modules/wms/components/backend/WmsLotDetailPage.js.map +2 -2
  102. package/dist/modules/wms/components/backend/WmsOperationalDashboardPage.js +14 -10
  103. package/dist/modules/wms/components/backend/WmsOperationalDashboardPage.js.map +2 -2
  104. package/dist/modules/wms/components/backend/WmsPutawayQueuePage.js +598 -0
  105. package/dist/modules/wms/components/backend/WmsPutawayQueuePage.js.map +7 -0
  106. package/dist/modules/wms/components/backend/WmsSkuDetailPage.js +5 -6
  107. package/dist/modules/wms/components/backend/WmsSkuDetailPage.js.map +2 -2
  108. package/dist/modules/wms/components/backend/asnCompleteOptimisticLock.js +32 -0
  109. package/dist/modules/wms/components/backend/asnCompleteOptimisticLock.js.map +7 -0
  110. package/dist/modules/wms/components/backend/asnReceivingLinesLoader.js +36 -0
  111. package/dist/modules/wms/components/backend/asnReceivingLinesLoader.js.map +7 -0
  112. package/dist/modules/wms/components/backend/inboundStatusUi.js +106 -0
  113. package/dist/modules/wms/components/backend/inboundStatusUi.js.map +7 -0
  114. package/dist/modules/wms/components/backend/inventoryMutationLoaders.js +17 -0
  115. package/dist/modules/wms/components/backend/inventoryMutationLoaders.js.map +2 -2
  116. package/dist/modules/wms/components/backend/putawayQueueOptimisticLock.js +14 -0
  117. package/dist/modules/wms/components/backend/putawayQueueOptimisticLock.js.map +7 -0
  118. package/dist/modules/wms/components/backend/useWmsInventoryMutationAccess.js +4 -0
  119. package/dist/modules/wms/components/backend/useWmsInventoryMutationAccess.js.map +2 -2
  120. package/dist/modules/wms/data/enrichers.js +117 -12
  121. package/dist/modules/wms/data/enrichers.js.map +2 -2
  122. package/dist/modules/wms/data/entities.js +150 -1
  123. package/dist/modules/wms/data/entities.js.map +2 -2
  124. package/dist/modules/wms/data/validators.js +214 -0
  125. package/dist/modules/wms/data/validators.js.map +2 -2
  126. package/dist/modules/wms/events.js +12 -1
  127. package/dist/modules/wms/events.js.map +2 -2
  128. package/dist/modules/wms/extension-points.js +3 -1
  129. package/dist/modules/wms/extension-points.js.map +2 -2
  130. package/dist/modules/wms/lib/asnReceiving.js +124 -0
  131. package/dist/modules/wms/lib/asnReceiving.js.map +7 -0
  132. package/dist/modules/wms/lib/asnVendor.js +52 -0
  133. package/dist/modules/wms/lib/asnVendor.js.map +7 -0
  134. package/dist/modules/wms/lib/flashMutationError.js +4 -0
  135. package/dist/modules/wms/lib/flashMutationError.js.map +2 -2
  136. package/dist/modules/wms/lib/inventoryDisplayUi.js +24 -0
  137. package/dist/modules/wms/lib/inventoryDisplayUi.js.map +2 -2
  138. package/dist/modules/wms/lib/inventoryPolicy.js +17 -0
  139. package/dist/modules/wms/lib/inventoryPolicy.js.map +2 -2
  140. package/dist/modules/wms/lib/loadOperationalDashboard.js +67 -1
  141. package/dist/modules/wms/lib/loadOperationalDashboard.js.map +2 -2
  142. package/dist/modules/wms/lib/putaway.js +141 -0
  143. package/dist/modules/wms/lib/putaway.js.map +7 -0
  144. package/dist/modules/wms/lib/roleFeatures.js +3 -1
  145. package/dist/modules/wms/lib/roleFeatures.js.map +2 -2
  146. package/dist/modules/wms/lib/salesOrderInventoryAutomation.js +99 -1
  147. package/dist/modules/wms/lib/salesOrderInventoryAutomation.js.map +2 -2
  148. package/dist/modules/wms/lib/scanResolve.js +147 -0
  149. package/dist/modules/wms/lib/scanResolve.js.map +7 -0
  150. package/dist/modules/wms/lib/wmsIntegrationToggles.js +1 -1
  151. package/dist/modules/wms/lib/wmsIntegrationToggles.js.map +2 -2
  152. package/dist/modules/wms/migrations/Migration20260812120000.js +43 -0
  153. package/dist/modules/wms/migrations/Migration20260812120000.js.map +7 -0
  154. package/dist/modules/wms/migrations/Migration20260812130000.js +30 -0
  155. package/dist/modules/wms/migrations/Migration20260812130000.js.map +7 -0
  156. package/dist/modules/wms/migrations/Migration20260822120000.js +17 -0
  157. package/dist/modules/wms/migrations/Migration20260822120000.js.map +7 -0
  158. package/dist/modules/wms/migrations/Migration20260822130000.js +17 -0
  159. package/dist/modules/wms/migrations/Migration20260822130000.js.map +7 -0
  160. package/dist/modules/wms/migrations/Migration20261029120000.js +13 -0
  161. package/dist/modules/wms/migrations/Migration20261029120000.js.map +7 -0
  162. package/dist/modules/wms/search.js +85 -0
  163. package/dist/modules/wms/search.js.map +2 -2
  164. package/dist/modules/wms/subscribers/asn-line-received-reevaluate-reservations.js +14 -0
  165. package/dist/modules/wms/subscribers/asn-line-received-reevaluate-reservations.js.map +7 -0
  166. package/dist/modules/wms/subscribers/invalidate-enricher-cache-asn.js +12 -0
  167. package/dist/modules/wms/subscribers/invalidate-enricher-cache-asn.js.map +7 -0
  168. package/dist/modules/wms/subscribers/procurement-goods-receipt-created.js +163 -0
  169. package/dist/modules/wms/subscribers/procurement-goods-receipt-created.js.map +7 -0
  170. package/dist/modules/wms/subscribers/putaway-completed-reevaluate-reservations.js +14 -0
  171. package/dist/modules/wms/subscribers/putaway-completed-reevaluate-reservations.js.map +7 -0
  172. package/dist/modules/wms/widgets/injection/sales-order-stock-context/widget.client.js +37 -8
  173. package/dist/modules/wms/widgets/injection/sales-order-stock-context/widget.client.js.map +2 -2
  174. package/generated/entities/asn/index.ts +8 -0
  175. package/generated/entities/putaway_task/index.ts +11 -0
  176. package/generated/entities/receiving_line/index.ts +9 -0
  177. package/generated/entities.ids.generated.ts +4 -1
  178. package/generated/entity-fields-registry.ts +34 -0
  179. package/package.json +7 -7
  180. package/src/modules/auth/i18n/de.json +2 -0
  181. package/src/modules/auth/i18n/en.json +2 -0
  182. package/src/modules/auth/i18n/es.json +2 -0
  183. package/src/modules/auth/i18n/ko.json +2 -0
  184. package/src/modules/auth/i18n/pl.json +2 -0
  185. package/src/modules/communication_channels/i18n/de.json +2 -0
  186. package/src/modules/communication_channels/i18n/en.json +2 -0
  187. package/src/modules/communication_channels/i18n/es.json +2 -0
  188. package/src/modules/communication_channels/i18n/ko.json +2 -0
  189. package/src/modules/communication_channels/i18n/pl.json +2 -0
  190. package/src/modules/communication_channels/lib/sanitize-channel-html.ts +23 -3
  191. package/src/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.client.tsx +44 -12
  192. package/src/modules/customers/components/calendar/CalendarEventEditor.tsx +16 -2
  193. package/src/modules/wms/acl.ts +2 -0
  194. package/src/modules/wms/api/asns/[id]/complete/route.ts +62 -0
  195. package/src/modules/wms/api/asns/[id]/receive/route.ts +74 -0
  196. package/src/modules/wms/api/asns/route.ts +200 -0
  197. package/src/modules/wms/api/inventory/helpers.ts +57 -34
  198. package/src/modules/wms/api/inventory/receive/route.ts +2 -1
  199. package/src/modules/wms/api/listEnrichers.ts +38 -0
  200. package/src/modules/wms/api/putaway-tasks/[id]/assign/route.ts +56 -0
  201. package/src/modules/wms/api/putaway-tasks/[id]/cancel/route.ts +56 -0
  202. package/src/modules/wms/api/putaway-tasks/[id]/complete/route.ts +66 -0
  203. package/src/modules/wms/api/putaway-tasks/[id]/start/route.ts +56 -0
  204. package/src/modules/wms/api/putaway-tasks/create-from-balance/route.ts +50 -0
  205. package/src/modules/wms/api/putaway-tasks/route.ts +199 -0
  206. package/src/modules/wms/api/receiving-lines/route.ts +180 -0
  207. package/src/modules/wms/api/scan/putaway/route.ts +108 -0
  208. package/src/modules/wms/api/scan/receive/route.ts +118 -0
  209. package/src/modules/wms/api/scan/resolve-location/route.ts +92 -0
  210. package/src/modules/wms/api/scan/resolve-lot/route.ts +92 -0
  211. package/src/modules/wms/backend/wms/asns/[id]/page.meta.ts +14 -0
  212. package/src/modules/wms/backend/wms/asns/[id]/page.tsx +5 -0
  213. package/src/modules/wms/backend/wms/asns/page.meta.ts +14 -0
  214. package/src/modules/wms/backend/wms/asns/page.tsx +5 -0
  215. package/src/modules/wms/backend/wms/putaway/page.meta.ts +14 -0
  216. package/src/modules/wms/backend/wms/putaway/page.tsx +5 -0
  217. package/src/modules/wms/backend/wms/receiving/page.meta.ts +14 -0
  218. package/src/modules/wms/backend/wms/receiving/page.tsx +5 -0
  219. package/src/modules/wms/ce.ts +27 -0
  220. package/src/modules/wms/commands/asn.ts +2122 -0
  221. package/src/modules/wms/commands/index.ts +2 -0
  222. package/src/modules/wms/commands/inventory-actions.ts +413 -243
  223. package/src/modules/wms/commands/putaway.ts +1169 -0
  224. package/src/modules/wms/commands/shared.ts +51 -0
  225. package/src/modules/wms/components/backend/AssignPutawayTaskDialog.tsx +328 -0
  226. package/src/modules/wms/components/backend/CompletePutawayTaskDialog.tsx +366 -0
  227. package/src/modules/wms/components/backend/CreateAsnDialog.tsx +427 -0
  228. package/src/modules/wms/components/backend/InventoryOperationsSection.tsx +20 -2
  229. package/src/modules/wms/components/backend/ReceiveAsnLineDialog.tsx +535 -0
  230. package/src/modules/wms/components/backend/ReceiveInventoryDialog.tsx +2 -2
  231. package/src/modules/wms/components/backend/WmsAsnDetailPage.tsx +647 -0
  232. package/src/modules/wms/components/backend/WmsAsnsListPage.tsx +407 -0
  233. package/src/modules/wms/components/backend/WmsLocationDetailPage.tsx +4 -5
  234. package/src/modules/wms/components/backend/WmsLotDetailPage.tsx +4 -5
  235. package/src/modules/wms/components/backend/WmsOperationalDashboardPage.tsx +12 -8
  236. package/src/modules/wms/components/backend/WmsPutawayQueuePage.tsx +677 -0
  237. package/src/modules/wms/components/backend/WmsSkuDetailPage.tsx +4 -5
  238. package/src/modules/wms/components/backend/asnCompleteOptimisticLock.ts +52 -0
  239. package/src/modules/wms/components/backend/asnReceivingLinesLoader.ts +83 -0
  240. package/src/modules/wms/components/backend/inboundStatusUi.ts +154 -0
  241. package/src/modules/wms/components/backend/inventoryMutationLoaders.ts +20 -0
  242. package/src/modules/wms/components/backend/putawayQueueOptimisticLock.ts +20 -0
  243. package/src/modules/wms/components/backend/useWmsInventoryMutationAccess.ts +4 -0
  244. package/src/modules/wms/data/enrichers.ts +175 -13
  245. package/src/modules/wms/data/entities.ts +155 -0
  246. package/src/modules/wms/data/validators.ts +256 -0
  247. package/src/modules/wms/events.ts +11 -0
  248. package/src/modules/wms/extension-points.ts +2 -0
  249. package/src/modules/wms/i18n/de.json +205 -3
  250. package/src/modules/wms/i18n/en.json +205 -3
  251. package/src/modules/wms/i18n/es.json +205 -3
  252. package/src/modules/wms/i18n/ko.json +205 -3
  253. package/src/modules/wms/i18n/pl.json +205 -3
  254. package/src/modules/wms/lib/asnReceiving.ts +226 -0
  255. package/src/modules/wms/lib/asnVendor.ts +80 -0
  256. package/src/modules/wms/lib/flashMutationError.ts +4 -0
  257. package/src/modules/wms/lib/inventoryDisplayUi.ts +46 -0
  258. package/src/modules/wms/lib/inventoryPolicy.ts +31 -1
  259. package/src/modules/wms/lib/loadOperationalDashboard.ts +89 -1
  260. package/src/modules/wms/lib/putaway.ts +210 -0
  261. package/src/modules/wms/lib/roleFeatures.ts +2 -0
  262. package/src/modules/wms/lib/salesOrderInventoryAutomation.ts +150 -1
  263. package/src/modules/wms/lib/scanResolve.ts +185 -0
  264. package/src/modules/wms/lib/wmsIntegrationToggles.ts +2 -1
  265. package/src/modules/wms/migrations/.snapshot-open-mercato.json +1000 -0
  266. package/src/modules/wms/migrations/Migration20260812120000.ts +43 -0
  267. package/src/modules/wms/migrations/Migration20260812130000.ts +28 -0
  268. package/src/modules/wms/migrations/Migration20260822120000.ts +17 -0
  269. package/src/modules/wms/migrations/Migration20260822130000.ts +15 -0
  270. package/src/modules/wms/migrations/Migration20261029120000.ts +11 -0
  271. package/src/modules/wms/search.ts +95 -0
  272. package/src/modules/wms/subscribers/asn-line-received-reevaluate-reservations.ts +21 -0
  273. package/src/modules/wms/subscribers/invalidate-enricher-cache-asn.ts +12 -0
  274. package/src/modules/wms/subscribers/procurement-goods-receipt-created.ts +238 -0
  275. package/src/modules/wms/subscribers/putaway-completed-reevaluate-reservations.ts +21 -0
  276. package/src/modules/wms/widgets/injection/sales-order-stock-context/widget.client.tsx +63 -13
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../../src/modules/wms/api/asns/%5Bid%5D/complete/route.ts"],
4
+ "sourcesContent": ["import { z } from 'zod'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { asnCloseSchema } from '../../../../data/validators'\nimport { executeWmsCustomPostRoute } from '../../../inventory/helpers'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['wms.manage_asn'] },\n}\n\nconst paramsSchema = z.object({\n id: z.string().uuid(),\n})\n\nexport async function POST(request: Request, routeContext: { params: { id: string } }) {\n const parsedParams = paramsSchema.parse(routeContext.params)\n const body = await readJsonSafe<Record<string, unknown>>(request, {})\n const scopedRequest = new Request(request.url, {\n method: 'POST',\n headers: request.headers,\n body: JSON.stringify({ ...body, id: parsedParams.id }),\n })\n return executeWmsCustomPostRoute({\n request: scopedRequest,\n routePath: `wms/asns/${parsedParams.id}/complete`,\n inputSchema: asnCloseSchema,\n commandId: 'wms.asns.close',\n describeResource: (input) => ({\n resourceKind: 'wms.asn',\n resourceId: input.id,\n }),\n mapSuccess: (result: { status: string }) => ({\n ok: true,\n status: result.status,\n }),\n })\n}\n\nconst successSchema = z.object({\n ok: z.literal(true),\n status: z.enum(['draft', 'in_transit', 'received', 'closed']),\n})\nconst errorSchema = z.object({ error: z.string() })\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'WMS',\n summary: 'Complete ASN',\n methods: {\n POST: {\n summary: 'Complete ASN',\n description:\n 'Marks an ASN as received when it has receipt activity (at least one line with receivedQty > 0) and lines are complete (QC-passed accepted qty meets expected), or when closeWhenShort is true for short/QC-fail receipts after some receipt activity. Header-only ASNs and zero-receipt lines are rejected even with closeWhenShort.',\n requestBody: { contentType: 'application/json', schema: asnCloseSchema },\n responses: [{ status: 200, description: 'ASN completed', schema: successSchema }],\n errors: [\n { status: 400, description: 'Validation failed', schema: errorSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n { status: 409, description: 'Invalid receipt state', schema: errorSchema },\n ],\n },\n },\n}"],
5
+ "mappings": "AAAA,SAAS,SAAS;AAElB,SAAS,oBAAoB;AAC7B,SAAS,sBAAsB;AAC/B,SAAS,iCAAiC;AAEnC,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,gBAAgB,EAAE;AACjE;AAEA,MAAM,eAAe,EAAE,OAAO;AAAA,EAC5B,IAAI,EAAE,OAAO,EAAE,KAAK;AACtB,CAAC;AAED,eAAsB,KAAK,SAAkB,cAA0C;AACrF,QAAM,eAAe,aAAa,MAAM,aAAa,MAAM;AAC3D,QAAM,OAAO,MAAM,aAAsC,SAAS,CAAC,CAAC;AACpE,QAAM,gBAAgB,IAAI,QAAQ,QAAQ,KAAK;AAAA,IAC7C,QAAQ;AAAA,IACR,SAAS,QAAQ;AAAA,IACjB,MAAM,KAAK,UAAU,EAAE,GAAG,MAAM,IAAI,aAAa,GAAG,CAAC;AAAA,EACvD,CAAC;AACD,SAAO,0BAA0B;AAAA,IAC/B,SAAS;AAAA,IACT,WAAW,YAAY,aAAa,EAAE;AAAA,IACtC,aAAa;AAAA,IACb,WAAW;AAAA,IACX,kBAAkB,CAAC,WAAW;AAAA,MAC5B,cAAc;AAAA,MACd,YAAY,MAAM;AAAA,IACpB;AAAA,IACA,YAAY,CAAC,YAAgC;AAAA,MAC3C,IAAI;AAAA,MACJ,QAAQ,OAAO;AAAA,IACjB;AAAA,EACF,CAAC;AACH;AAEA,MAAM,gBAAgB,EAAE,OAAO;AAAA,EAC7B,IAAI,EAAE,QAAQ,IAAI;AAAA,EAClB,QAAQ,EAAE,KAAK,CAAC,SAAS,cAAc,YAAY,QAAQ,CAAC;AAC9D,CAAC;AACD,MAAM,cAAc,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAE3C,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,EACT,SAAS;AAAA,IACP,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,aACE;AAAA,MACF,aAAa,EAAE,aAAa,oBAAoB,QAAQ,eAAe;AAAA,MACvE,WAAW,CAAC,EAAE,QAAQ,KAAK,aAAa,iBAAiB,QAAQ,cAAc,CAAC;AAAA,MAChF,QAAQ;AAAA,QACN,EAAE,QAAQ,KAAK,aAAa,qBAAqB,QAAQ,YAAY;AAAA,QACrE,EAAE,QAAQ,KAAK,aAAa,gBAAgB,QAAQ,YAAY;AAAA,QAChE,EAAE,QAAQ,KAAK,aAAa,yBAAyB,QAAQ,YAAY;AAAA,MAC3E;AAAA,IACF;AAAA,EACF;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,68 @@
1
+ import { z } from "zod";
2
+ import { readJsonSafe } from "@open-mercato/shared/lib/http/readJsonSafe";
3
+ import { asnReceiveLineSchema } from "../../../../data/validators.js";
4
+ import { executeWmsCustomPostRoute } from "../../../inventory/helpers.js";
5
+ const metadata = {
6
+ POST: { requireAuth: true, requireFeatures: ["wms.receive_inventory"] }
7
+ };
8
+ const paramsSchema = z.object({
9
+ id: z.string().uuid()
10
+ });
11
+ async function POST(request, routeContext) {
12
+ const parsedParams = paramsSchema.parse(routeContext.params);
13
+ const body = await readJsonSafe(request, {});
14
+ const scopedRequest = new Request(request.url, {
15
+ method: "POST",
16
+ headers: request.headers,
17
+ body: JSON.stringify({ ...body, asnId: parsedParams.id })
18
+ });
19
+ return executeWmsCustomPostRoute({
20
+ request: scopedRequest,
21
+ routePath: `wms/asns/${parsedParams.id}/receive`,
22
+ inputSchema: asnReceiveLineSchema,
23
+ commandId: "wms.asns.receive-line",
24
+ describeResource: (input) => ({
25
+ resourceKind: "wms.asn",
26
+ resourceId: input.asnId
27
+ }),
28
+ mapSuccess: (result) => ({
29
+ ok: true,
30
+ movementIds: result.movementIds,
31
+ putawayTaskIds: result.putawayTaskIds,
32
+ receivedQty: result.receivedQty,
33
+ asnUpdatedAt: result.asnUpdatedAt
34
+ })
35
+ });
36
+ }
37
+ const successSchema = z.object({
38
+ ok: z.literal(true),
39
+ movementIds: z.array(z.string().uuid()),
40
+ putawayTaskIds: z.array(z.string().uuid()),
41
+ receivedQty: z.number().optional(),
42
+ asnUpdatedAt: z.string().datetime().optional()
43
+ });
44
+ const errorSchema = z.object({ error: z.string() });
45
+ const openApi = {
46
+ tag: "WMS",
47
+ summary: "Receive ASN line",
48
+ methods: {
49
+ POST: {
50
+ summary: "Receive ASN line",
51
+ description: "Receives a line against an ASN with QC outcome. QC pass writes receipt stock at staging/dock; QC fail records audit without balance increase. Both require absolute targetReceivedQty for retry-safe attempts (optional idempotencyKey is an extra stabilizer).",
52
+ requestBody: { contentType: "application/json", schema: asnReceiveLineSchema },
53
+ responses: [{ status: 200, description: "ASN line received", schema: successSchema }],
54
+ errors: [
55
+ { status: 400, description: "Validation failed", schema: errorSchema },
56
+ { status: 401, description: "Unauthorized", schema: errorSchema },
57
+ { status: 409, description: "Invalid receipt state", schema: errorSchema },
58
+ { status: 422, description: "Tracking or QC transition invalid", schema: errorSchema }
59
+ ]
60
+ }
61
+ }
62
+ };
63
+ export {
64
+ POST,
65
+ metadata,
66
+ openApi
67
+ };
68
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../../src/modules/wms/api/asns/%5Bid%5D/receive/route.ts"],
4
+ "sourcesContent": ["import { z } from 'zod'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { asnReceiveLineSchema } from '../../../../data/validators'\nimport { executeWmsCustomPostRoute } from '../../../inventory/helpers'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['wms.receive_inventory'] },\n}\n\nconst paramsSchema = z.object({\n id: z.string().uuid(),\n})\n\nexport async function POST(request: Request, routeContext: { params: { id: string } }) {\n const parsedParams = paramsSchema.parse(routeContext.params)\n const body = await readJsonSafe<Record<string, unknown>>(request, {})\n const scopedRequest = new Request(request.url, {\n method: 'POST',\n headers: request.headers,\n body: JSON.stringify({ ...body, asnId: parsedParams.id }),\n })\n return executeWmsCustomPostRoute({\n request: scopedRequest,\n routePath: `wms/asns/${parsedParams.id}/receive`,\n inputSchema: asnReceiveLineSchema,\n commandId: 'wms.asns.receive-line',\n describeResource: (input) => ({\n resourceKind: 'wms.asn',\n resourceId: input.asnId,\n }),\n mapSuccess: (result: {\n movementIds: string[]\n putawayTaskIds: string[]\n receivedQty?: number\n asnUpdatedAt?: string\n }) => ({\n ok: true,\n movementIds: result.movementIds,\n putawayTaskIds: result.putawayTaskIds,\n receivedQty: result.receivedQty,\n asnUpdatedAt: result.asnUpdatedAt,\n }),\n })\n}\n\nconst successSchema = z.object({\n ok: z.literal(true),\n movementIds: z.array(z.string().uuid()),\n putawayTaskIds: z.array(z.string().uuid()),\n receivedQty: z.number().optional(),\n asnUpdatedAt: z.string().datetime().optional(),\n})\nconst errorSchema = z.object({ error: z.string() })\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'WMS',\n summary: 'Receive ASN line',\n methods: {\n POST: {\n summary: 'Receive ASN line',\n description:\n 'Receives a line against an ASN with QC outcome. QC pass writes receipt stock at staging/dock; QC fail records audit without balance increase. Both require absolute targetReceivedQty for retry-safe attempts (optional idempotencyKey is an extra stabilizer).',\n requestBody: { contentType: 'application/json', schema: asnReceiveLineSchema },\n responses: [{ status: 200, description: 'ASN line received', schema: successSchema }],\n errors: [\n { status: 400, description: 'Validation failed', schema: errorSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n { status: 409, description: 'Invalid receipt state', schema: errorSchema },\n { status: 422, description: 'Tracking or QC transition invalid', schema: errorSchema },\n ],\n },\n },\n}\n"],
5
+ "mappings": "AAAA,SAAS,SAAS;AAElB,SAAS,oBAAoB;AAC7B,SAAS,4BAA4B;AACrC,SAAS,iCAAiC;AAEnC,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,uBAAuB,EAAE;AACxE;AAEA,MAAM,eAAe,EAAE,OAAO;AAAA,EAC5B,IAAI,EAAE,OAAO,EAAE,KAAK;AACtB,CAAC;AAED,eAAsB,KAAK,SAAkB,cAA0C;AACrF,QAAM,eAAe,aAAa,MAAM,aAAa,MAAM;AAC3D,QAAM,OAAO,MAAM,aAAsC,SAAS,CAAC,CAAC;AACpE,QAAM,gBAAgB,IAAI,QAAQ,QAAQ,KAAK;AAAA,IAC7C,QAAQ;AAAA,IACR,SAAS,QAAQ;AAAA,IACjB,MAAM,KAAK,UAAU,EAAE,GAAG,MAAM,OAAO,aAAa,GAAG,CAAC;AAAA,EAC1D,CAAC;AACD,SAAO,0BAA0B;AAAA,IAC/B,SAAS;AAAA,IACT,WAAW,YAAY,aAAa,EAAE;AAAA,IACtC,aAAa;AAAA,IACb,WAAW;AAAA,IACX,kBAAkB,CAAC,WAAW;AAAA,MAC5B,cAAc;AAAA,MACd,YAAY,MAAM;AAAA,IACpB;AAAA,IACA,YAAY,CAAC,YAKN;AAAA,MACL,IAAI;AAAA,MACJ,aAAa,OAAO;AAAA,MACpB,gBAAgB,OAAO;AAAA,MACvB,aAAa,OAAO;AAAA,MACpB,cAAc,OAAO;AAAA,IACvB;AAAA,EACF,CAAC;AACH;AAEA,MAAM,gBAAgB,EAAE,OAAO;AAAA,EAC7B,IAAI,EAAE,QAAQ,IAAI;AAAA,EAClB,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC;AAAA,EACtC,gBAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC;AAAA,EACzC,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,EACjC,cAAc,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAC/C,CAAC;AACD,MAAM,cAAc,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAE3C,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,EACT,SAAS;AAAA,IACP,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,aACE;AAAA,MACF,aAAa,EAAE,aAAa,oBAAoB,QAAQ,qBAAqB;AAAA,MAC7E,WAAW,CAAC,EAAE,QAAQ,KAAK,aAAa,qBAAqB,QAAQ,cAAc,CAAC;AAAA,MACpF,QAAQ;AAAA,QACN,EAAE,QAAQ,KAAK,aAAa,qBAAqB,QAAQ,YAAY;AAAA,QACrE,EAAE,QAAQ,KAAK,aAAa,gBAAgB,QAAQ,YAAY;AAAA,QAChE,EAAE,QAAQ,KAAK,aAAa,yBAAyB,QAAQ,YAAY;AAAA,QACzE,EAAE,QAAQ,KAAK,aAAa,qCAAqC,QAAQ,YAAY;AAAA,MACvF;AAAA,IACF;AAAA,EACF;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,197 @@
1
+ import { z } from "zod";
2
+ import { makeCrudRoute } from "@open-mercato/shared/lib/crud/factory";
3
+ import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
4
+ import { parseScopedCommandInput, resolveCrudRecordId } from "@open-mercato/shared/lib/api/scoped";
5
+ import { escapeLikePattern } from "@open-mercato/shared/lib/db/escapeLikePattern";
6
+ import { E } from "../../../../generated/entities.ids.generated.js";
7
+ import { Asn } from "../../data/entities.js";
8
+ import { asnCreatePublicSchema, asnUpdateSchema } from "../../data/validators.js";
9
+ import { buildAsnStatusFilter } from "../../lib/asnReceiving.js";
10
+ import { attachVendorLabelsToListItems, attachWarehouseLabelsToListItems } from "../listEnrichers.js";
11
+ import { createPagedListResponseSchema, createWmsCrudOpenApi, defaultOkResponseSchema } from "../openapi.js";
12
+ const F = {
13
+ id: "id",
14
+ organization_id: "organization_id",
15
+ tenant_id: "tenant_id",
16
+ warehouse_id: "warehouse_id",
17
+ vendor_id: "vendor_id",
18
+ status: "status",
19
+ expected_at: "expected_at",
20
+ reference_number: "reference_number",
21
+ notes: "notes",
22
+ created_at: "created_at",
23
+ updated_at: "updated_at"
24
+ };
25
+ const routeMetadata = {
26
+ GET: { requireAuth: true, requireFeatures: ["wms.view"] },
27
+ POST: { requireAuth: true, requireFeatures: ["wms.manage_asn"] },
28
+ PUT: { requireAuth: true, requireFeatures: ["wms.manage_asn"] },
29
+ DELETE: { requireAuth: true, requireFeatures: ["wms.manage_asn"] }
30
+ };
31
+ const metadata = routeMetadata;
32
+ const rawBodySchema = z.object({}).passthrough();
33
+ const listSchema = z.object({
34
+ page: z.coerce.number().min(1).default(1),
35
+ pageSize: z.coerce.number().min(1).max(100).default(25),
36
+ search: z.string().optional(),
37
+ ids: z.string().optional(),
38
+ warehouseId: z.string().uuid().optional(),
39
+ vendorId: z.string().uuid().optional(),
40
+ // Single status or comma-separated (e.g. draft,in_transit for open receiving queue).
41
+ status: z.string().optional(),
42
+ sortField: z.string().optional(),
43
+ sortDir: z.enum(["asc", "desc"]).optional()
44
+ }).passthrough();
45
+ const crud = makeCrudRoute({
46
+ metadata: routeMetadata,
47
+ orm: {
48
+ entity: Asn,
49
+ idField: "id",
50
+ orgField: "organizationId",
51
+ tenantField: "tenantId",
52
+ softDeleteField: "deletedAt"
53
+ },
54
+ indexer: { entityType: E.wms.asn },
55
+ list: {
56
+ schema: listSchema,
57
+ disableListCache: true,
58
+ entityId: E.wms.asn,
59
+ fields: [
60
+ F.id,
61
+ F.organization_id,
62
+ F.tenant_id,
63
+ F.warehouse_id,
64
+ F.vendor_id,
65
+ F.status,
66
+ F.expected_at,
67
+ F.reference_number,
68
+ F.notes,
69
+ F.created_at,
70
+ F.updated_at
71
+ ],
72
+ sortFieldMap: {
73
+ status: F.status,
74
+ expectedAt: F.expected_at,
75
+ createdAt: F.created_at,
76
+ updatedAt: F.updated_at,
77
+ referenceNumber: F.reference_number
78
+ },
79
+ buildFilters: async (query) => {
80
+ const filters = {};
81
+ if (typeof query.ids === "string" && query.ids.trim().length > 0) {
82
+ filters[F.id] = {
83
+ $in: query.ids.split(",").map((value) => value.trim()).filter((value) => value.length > 0)
84
+ };
85
+ }
86
+ if (typeof query.warehouseId === "string" && query.warehouseId.length > 0) {
87
+ filters[F.warehouse_id] = { $eq: query.warehouseId };
88
+ }
89
+ if (typeof query.vendorId === "string" && query.vendorId.length > 0) {
90
+ filters[F.vendor_id] = { $eq: query.vendorId };
91
+ }
92
+ if (typeof query.status === "string" && query.status.length > 0) {
93
+ const statusFilter = buildAsnStatusFilter(query.status);
94
+ if (statusFilter) filters[F.status] = statusFilter;
95
+ }
96
+ const term = query.search?.trim();
97
+ if (term) {
98
+ const like = `%${escapeLikePattern(term)}%`;
99
+ filters.$or = [
100
+ { [F.reference_number]: { $ilike: like } },
101
+ { [F.notes]: { $ilike: like } }
102
+ ];
103
+ }
104
+ return filters;
105
+ }
106
+ },
107
+ hooks: {
108
+ afterList: async (payload, ctx) => {
109
+ await Promise.all([
110
+ attachWarehouseLabelsToListItems(payload, ctx),
111
+ attachVendorLabelsToListItems(payload, ctx)
112
+ ]);
113
+ }
114
+ },
115
+ actions: {
116
+ create: {
117
+ commandId: "wms.asns.create",
118
+ schema: rawBodySchema,
119
+ mapInput: async ({ raw, ctx }) => {
120
+ const { translate } = await resolveTranslations();
121
+ return parseScopedCommandInput(asnCreatePublicSchema, raw ?? {}, ctx, translate);
122
+ },
123
+ response: ({ result }) => ({
124
+ id: result?.asnId ?? null,
125
+ lineIds: result?.lineIds ?? []
126
+ }),
127
+ status: 201
128
+ },
129
+ update: {
130
+ commandId: "wms.asns.update",
131
+ schema: rawBodySchema,
132
+ mapInput: async ({ raw, ctx }) => {
133
+ const { translate } = await resolveTranslations();
134
+ return parseScopedCommandInput(asnUpdateSchema, raw ?? {}, ctx, translate);
135
+ },
136
+ response: () => ({ ok: true })
137
+ },
138
+ delete: {
139
+ commandId: "wms.asns.delete",
140
+ schema: rawBodySchema,
141
+ mapInput: async ({ parsed, ctx }) => {
142
+ const { translate } = await resolveTranslations();
143
+ return { id: resolveCrudRecordId(parsed, ctx, translate) };
144
+ },
145
+ response: () => ({ ok: true })
146
+ }
147
+ }
148
+ });
149
+ const GET = crud.GET;
150
+ const POST = crud.POST;
151
+ const PUT = crud.PUT;
152
+ const DELETE = crud.DELETE;
153
+ const asnListItemSchema = z.object({
154
+ id: z.string().uuid().nullable().optional(),
155
+ organization_id: z.string().uuid().nullable().optional(),
156
+ tenant_id: z.string().uuid().nullable().optional(),
157
+ warehouse_id: z.string().uuid().nullable().optional(),
158
+ warehouse_name: z.string().nullable().optional(),
159
+ warehouse_code: z.string().nullable().optional(),
160
+ vendor_id: z.string().uuid().nullable().optional(),
161
+ vendor_name: z.string().nullable().optional(),
162
+ status: z.string().nullable().optional(),
163
+ expected_at: z.string().nullable().optional(),
164
+ reference_number: z.string().nullable().optional(),
165
+ notes: z.string().nullable().optional(),
166
+ created_at: z.string().nullable().optional(),
167
+ updated_at: z.string().nullable().optional()
168
+ });
169
+ const openApi = createWmsCrudOpenApi({
170
+ resourceName: "ASN",
171
+ pluralName: "ASNs",
172
+ querySchema: listSchema,
173
+ listResponseSchema: createPagedListResponseSchema(asnListItemSchema),
174
+ create: {
175
+ schema: asnCreatePublicSchema,
176
+ description: "Creates an ASN with optional expected receiving lines. `sourceKey` is server-only (procurement/system path) and is not accepted on this public API."
177
+ },
178
+ update: {
179
+ schema: asnUpdateSchema,
180
+ responseSchema: defaultOkResponseSchema,
181
+ description: "Updates an ASN by id while status is draft or in_transit and no receiving line has receipt/QC activity. Received/closed ASNs, or ASNs with line receipt/QC activity, return 409 invalid_receipt_state (status demotion and warehouse/header changes are not allowed). Status may only be set to draft or in_transit; received/closed are set via receive/complete flows."
182
+ },
183
+ del: {
184
+ schema: z.object({ id: z.string().uuid() }),
185
+ responseSchema: defaultOkResponseSchema,
186
+ description: "Soft-deletes an ASN by id."
187
+ }
188
+ });
189
+ export {
190
+ DELETE,
191
+ GET,
192
+ POST,
193
+ PUT,
194
+ metadata,
195
+ openApi
196
+ };
197
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/wms/api/asns/route.ts"],
4
+ "sourcesContent": ["import { z } from 'zod'\nimport { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { parseScopedCommandInput, resolveCrudRecordId } from '@open-mercato/shared/lib/api/scoped'\nimport { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'\nimport { E } from '#generated/entities.ids.generated'\nimport { Asn } from '../../data/entities'\nimport { asnCreatePublicSchema, asnUpdateSchema } from '../../data/validators'\nimport { buildAsnStatusFilter } from '../../lib/asnReceiving'\nimport { attachVendorLabelsToListItems, attachWarehouseLabelsToListItems } from '../listEnrichers'\nimport { createPagedListResponseSchema, createWmsCrudOpenApi, defaultOkResponseSchema } from '../openapi'\n\nconst F = {\n id: 'id',\n organization_id: 'organization_id',\n tenant_id: 'tenant_id',\n warehouse_id: 'warehouse_id',\n vendor_id: 'vendor_id',\n status: 'status',\n expected_at: 'expected_at',\n reference_number: 'reference_number',\n notes: 'notes',\n created_at: 'created_at',\n updated_at: 'updated_at',\n} as const\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['wms.view'] },\n POST: { requireAuth: true, requireFeatures: ['wms.manage_asn'] },\n PUT: { requireAuth: true, requireFeatures: ['wms.manage_asn'] },\n DELETE: { requireAuth: true, requireFeatures: ['wms.manage_asn'] },\n}\n\nexport const metadata = routeMetadata\n\nconst rawBodySchema = z.object({}).passthrough()\n\nconst listSchema = z.object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(25),\n search: z.string().optional(),\n ids: z.string().optional(),\n warehouseId: z.string().uuid().optional(),\n vendorId: z.string().uuid().optional(),\n // Single status or comma-separated (e.g. draft,in_transit for open receiving queue).\n status: z.string().optional(),\n sortField: z.string().optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n}).passthrough()\n\nconst crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity: Asn,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n indexer: { entityType: E.wms.asn },\n list: {\n schema: listSchema,\n disableListCache: true,\n entityId: E.wms.asn,\n fields: [\n F.id,\n F.organization_id,\n F.tenant_id,\n F.warehouse_id,\n F.vendor_id,\n F.status,\n F.expected_at,\n F.reference_number,\n F.notes,\n F.created_at,\n F.updated_at,\n ],\n sortFieldMap: {\n status: F.status,\n expectedAt: F.expected_at,\n createdAt: F.created_at,\n updatedAt: F.updated_at,\n referenceNumber: F.reference_number,\n },\n buildFilters: async (query) => {\n const filters: Record<string, unknown> = {}\n if (typeof query.ids === 'string' && query.ids.trim().length > 0) {\n filters[F.id] = {\n $in: query.ids.split(',').map((value) => value.trim()).filter((value) => value.length > 0),\n }\n }\n if (typeof query.warehouseId === 'string' && query.warehouseId.length > 0) {\n filters[F.warehouse_id] = { $eq: query.warehouseId }\n }\n if (typeof query.vendorId === 'string' && query.vendorId.length > 0) {\n filters[F.vendor_id] = { $eq: query.vendorId }\n }\n if (typeof query.status === 'string' && query.status.length > 0) {\n const statusFilter = buildAsnStatusFilter(query.status)\n if (statusFilter) filters[F.status] = statusFilter\n }\n const term = query.search?.trim()\n if (term) {\n const like = `%${escapeLikePattern(term)}%`\n filters.$or = [\n { [F.reference_number]: { $ilike: like } },\n { [F.notes]: { $ilike: like } },\n ]\n }\n return filters\n },\n },\n hooks: {\n afterList: async (payload, ctx) => {\n await Promise.all([\n attachWarehouseLabelsToListItems(payload, ctx),\n attachVendorLabelsToListItems(payload, ctx),\n ])\n },\n },\n actions: {\n create: {\n commandId: 'wms.asns.create',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n // Public create strips sourceKey \u2014 procurement/system sets it via command with auth: null.\n return parseScopedCommandInput(asnCreatePublicSchema, raw ?? {}, ctx, translate)\n },\n response: ({ result }) => ({\n id: result?.asnId ?? null,\n lineIds: result?.lineIds ?? [],\n }),\n status: 201,\n },\n update: {\n commandId: 'wms.asns.update',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n return parseScopedCommandInput(asnUpdateSchema, raw ?? {}, ctx, translate)\n },\n response: () => ({ ok: true }),\n },\n delete: {\n commandId: 'wms.asns.delete',\n schema: rawBodySchema,\n mapInput: async ({ parsed, ctx }) => {\n const { translate } = await resolveTranslations()\n return { id: resolveCrudRecordId(parsed, ctx, translate) }\n },\n response: () => ({ ok: true }),\n },\n },\n})\n\nexport const GET = crud.GET\nexport const POST = crud.POST\nexport const PUT = crud.PUT\nexport const DELETE = crud.DELETE\n\nconst asnListItemSchema = z.object({\n id: z.string().uuid().nullable().optional(),\n organization_id: z.string().uuid().nullable().optional(),\n tenant_id: z.string().uuid().nullable().optional(),\n warehouse_id: z.string().uuid().nullable().optional(),\n warehouse_name: z.string().nullable().optional(),\n warehouse_code: z.string().nullable().optional(),\n vendor_id: z.string().uuid().nullable().optional(),\n vendor_name: z.string().nullable().optional(),\n status: z.string().nullable().optional(),\n expected_at: z.string().nullable().optional(),\n reference_number: z.string().nullable().optional(),\n notes: z.string().nullable().optional(),\n created_at: z.string().nullable().optional(),\n updated_at: z.string().nullable().optional(),\n})\n\nexport const openApi = createWmsCrudOpenApi({\n resourceName: 'ASN',\n pluralName: 'ASNs',\n querySchema: listSchema,\n listResponseSchema: createPagedListResponseSchema(asnListItemSchema),\n create: {\n schema: asnCreatePublicSchema,\n description:\n 'Creates an ASN with optional expected receiving lines. `sourceKey` is server-only (procurement/system path) and is not accepted on this public API.',\n },\n update: {\n schema: asnUpdateSchema,\n responseSchema: defaultOkResponseSchema,\n description:\n 'Updates an ASN by id while status is draft or in_transit and no receiving line has receipt/QC activity. Received/closed ASNs, or ASNs with line receipt/QC activity, return 409 invalid_receipt_state (status demotion and warehouse/header changes are not allowed). Status may only be set to draft or in_transit; received/closed are set via receive/complete flows.',\n },\n del: {\n schema: z.object({ id: z.string().uuid() }),\n responseSchema: defaultOkResponseSchema,\n description: 'Soft-deletes an ASN by id.',\n },\n})\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -4,16 +4,20 @@ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
4
4
  import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
5
5
  import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
6
6
  import { resolveSingleOrganizationIdOrDeny } from "@open-mercato/core/modules/directory/utils/organizationScopeFilter";
7
+ import { ensureOrganizationScope } from "@open-mercato/shared/lib/commands/scope";
7
8
  import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
8
9
  import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
9
10
  import { readJsonSafe } from "@open-mercato/shared/lib/http/readJsonSafe";
10
- import {
11
- runCrudMutationGuardAfterSuccess,
12
- validateCrudMutationGuard
13
- } from "@open-mercato/shared/lib/crud/mutation-guard";
11
+ import { runRouteMutationGuards } from "@open-mercato/shared/lib/crud/route-mutation-guard";
14
12
  import { runCustomRouteAfterInterceptors } from "@open-mercato/shared/lib/crud/custom-route-interceptor";
15
13
  import { createLogger } from "@open-mercato/shared/lib/logger";
16
14
  const logger = createLogger("wms");
15
+ function resolveBodyOrganizationId(body) {
16
+ const raw = body.organizationId;
17
+ if (typeof raw !== "string") return null;
18
+ const trimmed = raw.trim();
19
+ return trimmed.length > 0 ? trimmed : null;
20
+ }
17
21
  async function executeWmsCustomPostRoute(options) {
18
22
  try {
19
23
  const container = await createRequestContainer();
@@ -35,41 +39,45 @@ async function executeWmsCustomPostRoute(options) {
35
39
  organizationIds: organizationScope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),
36
40
  request: options.request
37
41
  };
38
- const body = await readJsonSafe(options.request, {});
39
- const parsed = options.inputSchema.parse(body);
40
- const resource = options.describeResource(parsed);
41
- const guardResult = await validateCrudMutationGuard(container, {
42
+ const body = await readJsonSafe(options.request, {}) ?? {};
43
+ const sessionOrganizationId = ctx.selectedOrganizationId ?? auth.orgId ?? null;
44
+ const organizationId = resolveBodyOrganizationId(body) ?? sessionOrganizationId;
45
+ if (!organizationId) {
46
+ throw new CrudHttpError(400, { error: "organization_scope_required" });
47
+ }
48
+ ensureOrganizationScope(ctx, organizationId);
49
+ const scopedBody = {
50
+ ...body,
42
51
  tenantId: auth.tenantId,
43
- organizationId: ctx.selectedOrganizationId,
44
- userId: auth.sub,
45
- resourceKind: resource.resourceKind,
46
- resourceId: resource.resourceId,
47
- operation: "custom",
48
- requestMethod: options.request.method,
49
- requestHeaders: options.request.headers,
50
- mutationPayload: parsed
52
+ organizationId
53
+ };
54
+ const parsed = options.inputSchema.parse(scopedBody);
55
+ const resource = options.describeResource(parsed);
56
+ const guardResult = await runRouteMutationGuards({
57
+ container,
58
+ req: options.request,
59
+ auth: {
60
+ userId: auth.sub,
61
+ tenantId: auth.tenantId,
62
+ organizationId
63
+ },
64
+ input: {
65
+ resourceKind: resource.resourceKind,
66
+ resourceId: resource.resourceId,
67
+ operation: "custom",
68
+ mutationPayload: parsed
69
+ }
51
70
  });
52
- if (guardResult && !guardResult.ok) {
53
- return NextResponse.json(guardResult.body, { status: guardResult.status });
71
+ if (!guardResult.ok) {
72
+ return NextResponse.json(guardResult.errorBody, { status: guardResult.errorStatus });
54
73
  }
74
+ const commandInput = guardResult.modifiedPayload ?? parsed;
55
75
  const commandBus = container.resolve("commandBus");
56
76
  const execution = await commandBus.execute(options.commandId, {
57
- input: parsed,
77
+ input: commandInput,
58
78
  ctx
59
79
  });
60
- if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {
61
- await runCrudMutationGuardAfterSuccess(container, {
62
- tenantId: auth.tenantId,
63
- organizationId: ctx.selectedOrganizationId,
64
- userId: auth.sub,
65
- resourceKind: resource.resourceKind,
66
- resourceId: resource.resourceId,
67
- operation: "custom",
68
- requestMethod: options.request.method,
69
- requestHeaders: options.request.headers,
70
- metadata: guardResult.metadata ?? null
71
- });
72
- }
80
+ await guardResult.runAfterSuccess();
73
81
  const responseBody = options.mapSuccess(execution.result);
74
82
  const intercepted = await runCustomRouteAfterInterceptors({
75
83
  routePath: options.routePath,
@@ -77,7 +85,7 @@ async function executeWmsCustomPostRoute(options) {
77
85
  request: {
78
86
  method: "POST",
79
87
  url: options.request.url,
80
- body: parsed,
88
+ body: commandInput,
81
89
  headers: Object.fromEntries(options.request.headers.entries())
82
90
  },
83
91
  response: {
@@ -89,7 +97,7 @@ async function executeWmsCustomPostRoute(options) {
89
97
  em: container.resolve("em"),
90
98
  container,
91
99
  userId: auth.sub,
92
- organizationId: ctx.selectedOrganizationId,
100
+ organizationId,
93
101
  tenantId: auth.tenantId
94
102
  }
95
103
  });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/wms/api/inventory/helpers.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { resolveSingleOrganizationIdOrDeny } from '@open-mercato/core/modules/directory/utils/organizationScopeFilter'\nimport type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport { runCustomRouteAfterInterceptors } from '@open-mercato/shared/lib/crud/custom-route-interceptor'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('wms')\n\ntype ResourceDescriptor = {\n resourceKind: string\n resourceId: string\n}\n\ntype ExecuteWmsCustomPostRouteOptions<TInput, TResult> = {\n request: Request\n routePath: string\n inputSchema: z.ZodType<TInput>\n commandId: string\n describeResource: (input: TInput) => ResourceDescriptor\n mapSuccess: (result: TResult) => Record<string, unknown>\n}\n\nexport async function executeWmsCustomPostRoute<TInput, TResult>(\n options: ExecuteWmsCustomPostRouteOptions<TInput, TResult>,\n) {\n try {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(options.request)\n const { translate } = await resolveTranslations()\n if (!auth || !auth.tenantId) {\n throw new CrudHttpError(401, { error: translate('wms.errors.unauthorized', 'Unauthorized') })\n }\n const organizationScope = await resolveOrganizationScopeForRequest({\n container,\n auth,\n request: options.request,\n })\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope,\n selectedOrganizationId: resolveSingleOrganizationIdOrDeny(organizationScope, auth) ?? null,\n organizationIds: organizationScope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: options.request,\n }\n const body = await readJsonSafe<Record<string, unknown>>(options.request, {})\n const parsed = options.inputSchema.parse(body)\n const resource = options.describeResource(parsed)\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId: ctx.selectedOrganizationId,\n userId: auth.sub,\n resourceKind: resource.resourceKind,\n resourceId: resource.resourceId,\n operation: 'custom',\n requestMethod: options.request.method,\n requestHeaders: options.request.headers,\n mutationPayload: parsed as Record<string, unknown>,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n const commandBus = container.resolve('commandBus') as CommandBus\n const execution = await commandBus.execute<TInput, TResult>(options.commandId, {\n input: parsed,\n ctx,\n })\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId: ctx.selectedOrganizationId,\n userId: auth.sub,\n resourceKind: resource.resourceKind,\n resourceId: resource.resourceId,\n operation: 'custom',\n requestMethod: options.request.method,\n requestHeaders: options.request.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n const responseBody = options.mapSuccess(execution.result)\n const intercepted = await runCustomRouteAfterInterceptors({\n routePath: options.routePath,\n method: 'POST',\n request: {\n method: 'POST',\n url: options.request.url,\n body: parsed as Record<string, unknown>,\n headers: Object.fromEntries(options.request.headers.entries()),\n },\n response: {\n statusCode: 200,\n body: responseBody,\n headers: {},\n },\n context: {\n em: container.resolve('em'),\n container,\n userId: auth.sub,\n organizationId: ctx.selectedOrganizationId,\n tenantId: auth.tenantId,\n },\n })\n if (!intercepted.ok) {\n return NextResponse.json(intercepted.body, { status: intercepted.statusCode })\n }\n return NextResponse.json(intercepted.body, { status: intercepted.statusCode })\n } catch (error) {\n if (error instanceof CrudHttpError) {\n return NextResponse.json(error.body, { status: error.status })\n }\n if (error instanceof z.ZodError) {\n return NextResponse.json({ error: 'Validation failed', details: error.issues }, { status: 400 })\n }\n logger.error('custom route failed', { routePath: options.routePath, commandId: options.commandId, err: error })\n return NextResponse.json({ error: 'Internal server error' }, { status: 500 })\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { resolveSingleOrganizationIdOrDeny } from '@open-mercato/core/modules/directory/utils/organizationScopeFilter'\nimport type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { ensureOrganizationScope } from '@open-mercato/shared/lib/commands/scope'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { runRouteMutationGuards } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport { runCustomRouteAfterInterceptors } from '@open-mercato/shared/lib/crud/custom-route-interceptor'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('wms')\n\ntype ResourceDescriptor = {\n resourceKind: string\n resourceId: string\n}\n\ntype ExecuteWmsCustomPostRouteOptions<TInput, TResult> = {\n request: Request\n routePath: string\n inputSchema: z.ZodType<TInput>\n commandId: string\n describeResource: (input: TInput) => ResourceDescriptor\n mapSuccess: (result: TResult) => Record<string, unknown>\n}\n\nfunction resolveBodyOrganizationId(body: Record<string, unknown>): string | null {\n const raw = body.organizationId\n if (typeof raw !== 'string') return null\n const trimmed = raw.trim()\n return trimmed.length > 0 ? trimmed : null\n}\n\n/**\n * Shared POST helper for WMS custom write routes.\n *\n * Organization scoping:\n * - When the body includes `organizationId`, pass it through and validate with\n * `ensureOrganizationScope` (allowed org ids / Pattern C). Do not silently\n * overwrite with the session-selected org \u2014 that mis-routes multi-org writes.\n * - When the body omits `organizationId`, default to the session selected/auth org.\n * - `tenantId` is always taken from auth (never from the body).\n *\n * Intentional session-only exception: barcode scan resolve/receive/putaway routes\n * force session org onto the request body *before* calling this helper so resolve\n * lookups cannot probe another org. That pre-scope policy stays on those routes.\n */\nexport async function executeWmsCustomPostRoute<TInput, TResult>(\n options: ExecuteWmsCustomPostRouteOptions<TInput, TResult>,\n) {\n try {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(options.request)\n const { translate } = await resolveTranslations()\n if (!auth || !auth.tenantId) {\n throw new CrudHttpError(401, { error: translate('wms.errors.unauthorized', 'Unauthorized') })\n }\n const organizationScope = await resolveOrganizationScopeForRequest({\n container,\n auth,\n request: options.request,\n })\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope,\n selectedOrganizationId: resolveSingleOrganizationIdOrDeny(organizationScope, auth) ?? null,\n organizationIds: organizationScope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: options.request,\n }\n const body = (await readJsonSafe<Record<string, unknown>>(options.request, {})) ?? {}\n const sessionOrganizationId = ctx.selectedOrganizationId ?? auth.orgId ?? null\n const organizationId = resolveBodyOrganizationId(body) ?? sessionOrganizationId\n if (!organizationId) {\n throw new CrudHttpError(400, { error: 'organization_scope_required' })\n }\n ensureOrganizationScope(ctx, organizationId)\n const scopedBody = {\n ...body,\n tenantId: auth.tenantId,\n organizationId,\n }\n const parsed = options.inputSchema.parse(scopedBody)\n const resource = options.describeResource(parsed)\n const guardResult = await runRouteMutationGuards({\n container,\n req: options.request,\n auth: {\n userId: auth.sub,\n tenantId: auth.tenantId,\n organizationId,\n },\n input: {\n resourceKind: resource.resourceKind,\n resourceId: resource.resourceId,\n operation: 'custom',\n mutationPayload: parsed as Record<string, unknown>,\n },\n })\n if (!guardResult.ok) {\n return NextResponse.json(guardResult.errorBody, { status: guardResult.errorStatus })\n }\n const commandInput = (guardResult.modifiedPayload as TInput | undefined) ?? parsed\n const commandBus = container.resolve('commandBus') as CommandBus\n const execution = await commandBus.execute<TInput, TResult>(options.commandId, {\n input: commandInput,\n ctx,\n })\n await guardResult.runAfterSuccess()\n const responseBody = options.mapSuccess(execution.result)\n const intercepted = await runCustomRouteAfterInterceptors({\n routePath: options.routePath,\n method: 'POST',\n request: {\n method: 'POST',\n url: options.request.url,\n body: commandInput as Record<string, unknown>,\n headers: Object.fromEntries(options.request.headers.entries()),\n },\n response: {\n statusCode: 200,\n body: responseBody,\n headers: {},\n },\n context: {\n em: container.resolve('em'),\n container,\n userId: auth.sub,\n organizationId,\n tenantId: auth.tenantId,\n },\n })\n if (!intercepted.ok) {\n return NextResponse.json(intercepted.body, { status: intercepted.statusCode })\n }\n return NextResponse.json(intercepted.body, { status: intercepted.statusCode })\n } catch (error) {\n if (error instanceof CrudHttpError) {\n return NextResponse.json(error.body, { status: error.status })\n }\n if (error instanceof z.ZodError) {\n return NextResponse.json({ error: 'Validation failed', details: error.issues }, { status: 400 })\n }\n logger.error('custom route failed', { routePath: options.routePath, commandId: options.commandId, err: error })\n return NextResponse.json({ error: 'Internal server error' }, { status: 500 })\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -31,7 +31,7 @@ const openApi = {
31
31
  methods: {
32
32
  POST: {
33
33
  summary: "Receive inventory",
34
- description: "Records inbound inventory receipt and appends a receipt movement ledger row.",
34
+ description: "Records inbound inventory receipt and appends a receipt movement ledger row. When `organizationId` is present it must be in the caller organization scope; otherwise the session-selected organization is used.",
35
35
  requestBody: { contentType: "application/json", schema: inventoryReceiveSchema },
36
36
  responses: [{ status: 200, description: "Inventory received", schema: successSchema }],
37
37
  errors: [
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/wms/api/inventory/receive/route.ts"],
4
- "sourcesContent": ["import { z } from 'zod'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { inventoryReceiveSchema } from '../../../data/validators'\nimport { executeWmsCustomPostRoute } from '../helpers'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['wms.receive_inventory'] },\n}\n\nexport async function POST(request: Request) {\n return executeWmsCustomPostRoute({\n request,\n routePath: 'wms/inventory/receive',\n inputSchema: inventoryReceiveSchema,\n commandId: 'wms.inventory.receive',\n describeResource: (input) => ({\n resourceKind: 'wms.inventory',\n resourceId: `${input.warehouseId}:${input.locationId}:${input.catalogVariantId}`,\n }),\n mapSuccess: (result: { movementId: string }) => ({\n ok: true,\n movementId: result.movementId,\n }),\n })\n}\n\nconst successSchema = z.object({\n ok: z.literal(true),\n movementId: z.string().uuid(),\n})\nconst errorSchema = z.object({ error: z.string() })\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'WMS',\n summary: 'Receive inventory',\n methods: {\n POST: {\n summary: 'Receive inventory',\n description: 'Records inbound inventory receipt and appends a receipt movement ledger row.',\n requestBody: { contentType: 'application/json', schema: inventoryReceiveSchema },\n responses: [{ status: 200, description: 'Inventory received', schema: successSchema }],\n errors: [\n { status: 400, description: 'Validation failed', schema: errorSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n { status: 422, description: 'Invalid location', schema: errorSchema },\n ],\n },\n },\n}\n"],
5
- "mappings": "AAAA,SAAS,SAAS;AAElB,SAAS,8BAA8B;AACvC,SAAS,iCAAiC;AAEnC,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,uBAAuB,EAAE;AACxE;AAEA,eAAsB,KAAK,SAAkB;AAC3C,SAAO,0BAA0B;AAAA,IAC/B;AAAA,IACA,WAAW;AAAA,IACX,aAAa;AAAA,IACb,WAAW;AAAA,IACX,kBAAkB,CAAC,WAAW;AAAA,MAC5B,cAAc;AAAA,MACd,YAAY,GAAG,MAAM,WAAW,IAAI,MAAM,UAAU,IAAI,MAAM,gBAAgB;AAAA,IAChF;AAAA,IACA,YAAY,CAAC,YAAoC;AAAA,MAC/C,IAAI;AAAA,MACJ,YAAY,OAAO;AAAA,IACrB;AAAA,EACF,CAAC;AACH;AAEA,MAAM,gBAAgB,EAAE,OAAO;AAAA,EAC7B,IAAI,EAAE,QAAQ,IAAI;AAAA,EAClB,YAAY,EAAE,OAAO,EAAE,KAAK;AAC9B,CAAC;AACD,MAAM,cAAc,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAE3C,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,EACT,SAAS;AAAA,IACP,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,aAAa;AAAA,MACb,aAAa,EAAE,aAAa,oBAAoB,QAAQ,uBAAuB;AAAA,MAC/E,WAAW,CAAC,EAAE,QAAQ,KAAK,aAAa,sBAAsB,QAAQ,cAAc,CAAC;AAAA,MACrF,QAAQ;AAAA,QACN,EAAE,QAAQ,KAAK,aAAa,qBAAqB,QAAQ,YAAY;AAAA,QACrE,EAAE,QAAQ,KAAK,aAAa,gBAAgB,QAAQ,YAAY;AAAA,QAChE,EAAE,QAAQ,KAAK,aAAa,oBAAoB,QAAQ,YAAY;AAAA,MACtE;AAAA,IACF;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import { z } from 'zod'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { inventoryReceiveSchema } from '../../../data/validators'\nimport { executeWmsCustomPostRoute } from '../helpers'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['wms.receive_inventory'] },\n}\n\nexport async function POST(request: Request) {\n return executeWmsCustomPostRoute({\n request,\n routePath: 'wms/inventory/receive',\n inputSchema: inventoryReceiveSchema,\n commandId: 'wms.inventory.receive',\n describeResource: (input) => ({\n resourceKind: 'wms.inventory',\n resourceId: `${input.warehouseId}:${input.locationId}:${input.catalogVariantId}`,\n }),\n mapSuccess: (result: { movementId: string }) => ({\n ok: true,\n movementId: result.movementId,\n }),\n })\n}\n\nconst successSchema = z.object({\n ok: z.literal(true),\n movementId: z.string().uuid(),\n})\nconst errorSchema = z.object({ error: z.string() })\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'WMS',\n summary: 'Receive inventory',\n methods: {\n POST: {\n summary: 'Receive inventory',\n description:\n 'Records inbound inventory receipt and appends a receipt movement ledger row. When `organizationId` is present it must be in the caller organization scope; otherwise the session-selected organization is used.',\n requestBody: { contentType: 'application/json', schema: inventoryReceiveSchema },\n responses: [{ status: 200, description: 'Inventory received', schema: successSchema }],\n errors: [\n { status: 400, description: 'Validation failed', schema: errorSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n { status: 422, description: 'Invalid location', schema: errorSchema },\n ],\n },\n },\n}\n"],
5
+ "mappings": "AAAA,SAAS,SAAS;AAElB,SAAS,8BAA8B;AACvC,SAAS,iCAAiC;AAEnC,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,uBAAuB,EAAE;AACxE;AAEA,eAAsB,KAAK,SAAkB;AAC3C,SAAO,0BAA0B;AAAA,IAC/B;AAAA,IACA,WAAW;AAAA,IACX,aAAa;AAAA,IACb,WAAW;AAAA,IACX,kBAAkB,CAAC,WAAW;AAAA,MAC5B,cAAc;AAAA,MACd,YAAY,GAAG,MAAM,WAAW,IAAI,MAAM,UAAU,IAAI,MAAM,gBAAgB;AAAA,IAChF;AAAA,IACA,YAAY,CAAC,YAAoC;AAAA,MAC/C,IAAI;AAAA,MACJ,YAAY,OAAO;AAAA,IACrB;AAAA,EACF,CAAC;AACH;AAEA,MAAM,gBAAgB,EAAE,OAAO;AAAA,EAC7B,IAAI,EAAE,QAAQ,IAAI;AAAA,EAClB,YAAY,EAAE,OAAO,EAAE,KAAK;AAC9B,CAAC;AACD,MAAM,cAAc,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAE3C,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,EACT,SAAS;AAAA,IACP,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,aACE;AAAA,MACF,aAAa,EAAE,aAAa,oBAAoB,QAAQ,uBAAuB;AAAA,MAC/E,WAAW,CAAC,EAAE,QAAQ,KAAK,aAAa,sBAAsB,QAAQ,cAAc,CAAC;AAAA,MACrF,QAAQ;AAAA,QACN,EAAE,QAAQ,KAAK,aAAa,qBAAqB,QAAQ,YAAY;AAAA,QACrE,EAAE,QAAQ,KAAK,aAAa,gBAAgB,QAAQ,YAAY;AAAA,QAChE,EAAE,QAAQ,KAAK,aAAa,oBAAoB,QAAQ,YAAY;AAAA,MACtE;AAAA,IACF;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
@@ -144,6 +144,28 @@ async function attachInventoryProfileCatalogLabelsToListItems(payload, ctx) {
144
144
  attachVariantLabelsToListItems(payload, ctx)
145
145
  ]);
146
146
  }
147
+ const CUSTOMER_FIELDS = ["id", "display_name"];
148
+ async function attachVendorLabelsToListItems(payload, ctx) {
149
+ const items = Array.isArray(payload?.items) ? payload.items : [];
150
+ if (items.length === 0) return;
151
+ const ids = uniqueIdsFromKeys(items, ["vendor_id"]);
152
+ if (ids.length === 0) return;
153
+ const map = await batchLoadById(
154
+ ctx,
155
+ E.customers.customer_entity,
156
+ ids,
157
+ CUSTOMER_FIELDS,
158
+ "vendor"
159
+ );
160
+ if (map.size === 0) return;
161
+ for (const item of items) {
162
+ const id = item?.vendor_id;
163
+ if (typeof id !== "string" || !id) continue;
164
+ const row = map.get(id);
165
+ if (!row) continue;
166
+ decorateOnce(item, "vendor_name", row.display_name ?? null);
167
+ }
168
+ }
147
169
  const SALES_ORDER_FIELDS = ["id", "order_number"];
148
170
  async function attachReservationSourceLabelsToListItems(payload, ctx) {
149
171
  const items = Array.isArray(payload?.items) ? payload.items : [];
@@ -176,6 +198,7 @@ export {
176
198
  attachProductLabelsToListItems,
177
199
  attachReservationSourceLabelsToListItems,
178
200
  attachVariantLabelsToListItems,
201
+ attachVendorLabelsToListItems,
179
202
  attachWarehouseLabelsToListItems
180
203
  };
181
204
  //# sourceMappingURL=listEnrichers.js.map