@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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/modules/wms/events.ts"],
4
- "sourcesContent": ["import { createModuleEvents } from '@open-mercato/shared/modules/events'\n\nconst events = [\n { id: 'wms.warehouse.created', label: 'Warehouse Created', entity: 'warehouse', category: 'crud' },\n { id: 'wms.warehouse.updated', label: 'Warehouse Updated', entity: 'warehouse', category: 'crud' },\n { id: 'wms.zone.created', label: 'Warehouse Zone Created', entity: 'zone', category: 'crud' },\n { id: 'wms.zone.updated', label: 'Warehouse Zone Updated', entity: 'zone', category: 'crud' },\n { id: 'wms.location.created', label: 'Warehouse Location Created', entity: 'location', category: 'crud' },\n { id: 'wms.location.updated', label: 'Warehouse Location Updated', entity: 'location', category: 'crud' },\n { id: 'wms.inventory_profile.created', label: 'Inventory Profile Created', entity: 'inventory_profile', category: 'crud' },\n { id: 'wms.inventory_profile.updated', label: 'Inventory Profile Updated', entity: 'inventory_profile', category: 'crud' },\n { id: 'wms.inventory_profile.deleted', label: 'Inventory Profile Deleted', entity: 'inventory_profile', category: 'crud' },\n { id: 'wms.inventory_balance.created', label: 'Inventory Balance Created', entity: 'inventory_balance', category: 'crud' },\n { id: 'wms.inventory_balance.updated', label: 'Inventory Balance Updated', entity: 'inventory_balance', category: 'crud' },\n { id: 'wms.inventory_balance.deleted', label: 'Inventory Balance Deleted', entity: 'inventory_balance', category: 'crud' },\n { id: 'wms.inventory_reservation.created', label: 'Inventory Reservation Created', entity: 'inventory_reservation', category: 'crud' },\n { id: 'wms.inventory_reservation.updated', label: 'Inventory Reservation Updated', entity: 'inventory_reservation', category: 'crud' },\n { id: 'wms.inventory_reservation.deleted', label: 'Inventory Reservation Deleted', entity: 'inventory_reservation', category: 'crud' },\n { id: 'wms.inventory_movement.created', label: 'Inventory Movement Created', entity: 'inventory_movement', category: 'crud' },\n { id: 'wms.inventory_movement.updated', label: 'Inventory Movement Updated', entity: 'inventory_movement', category: 'crud' },\n { id: 'wms.inventory_movement.deleted', label: 'Inventory Movement Deleted', entity: 'inventory_movement', category: 'crud' },\n { id: 'wms.inventory.received', label: 'Inventory Received', entity: 'inventory', category: 'custom' },\n { id: 'wms.inventory.adjusted', label: 'Inventory Adjusted', entity: 'inventory', category: 'custom' },\n { id: 'wms.inventory.reserved', label: 'Inventory Reserved', entity: 'inventory', category: 'custom' },\n { id: 'wms.inventory.released', label: 'Inventory Released', entity: 'inventory', category: 'custom' },\n { id: 'wms.inventory.allocated', label: 'Inventory Allocated', entity: 'inventory', category: 'custom' },\n { id: 'wms.inventory.moved', label: 'Inventory Moved', entity: 'inventory', category: 'custom' },\n { id: 'wms.inventory.reconciled', label: 'Inventory Reconciled', entity: 'inventory', category: 'custom' },\n { id: 'wms.inventory.low_stock', label: 'Inventory Low Stock', entity: 'inventory', category: 'lifecycle' },\n { id: 'wms.inventory.balance_drift', label: 'Inventory Balance Drift', entity: 'inventory', category: 'lifecycle' },\n { id: 'wms.inventory.reservation_shortfall', label: 'Inventory Reservation Shortfall', entity: 'inventory', category: 'lifecycle' },\n] as const\n\nexport const eventsConfig = createModuleEvents({\n moduleId: 'wms',\n events,\n})\n\nexport const emitWmsEvent = eventsConfig.emit\nexport type WmsEventId = typeof events[number]['id']\n\nexport default eventsConfig\n"],
5
- "mappings": "AAAA,SAAS,0BAA0B;AAEnC,MAAM,SAAS;AAAA,EACb,EAAE,IAAI,yBAAyB,OAAO,qBAAqB,QAAQ,aAAa,UAAU,OAAO;AAAA,EACjG,EAAE,IAAI,yBAAyB,OAAO,qBAAqB,QAAQ,aAAa,UAAU,OAAO;AAAA,EACjG,EAAE,IAAI,oBAAoB,OAAO,0BAA0B,QAAQ,QAAQ,UAAU,OAAO;AAAA,EAC5F,EAAE,IAAI,oBAAoB,OAAO,0BAA0B,QAAQ,QAAQ,UAAU,OAAO;AAAA,EAC5F,EAAE,IAAI,wBAAwB,OAAO,8BAA8B,QAAQ,YAAY,UAAU,OAAO;AAAA,EACxG,EAAE,IAAI,wBAAwB,OAAO,8BAA8B,QAAQ,YAAY,UAAU,OAAO;AAAA,EACxG,EAAE,IAAI,iCAAiC,OAAO,6BAA6B,QAAQ,qBAAqB,UAAU,OAAO;AAAA,EACzH,EAAE,IAAI,iCAAiC,OAAO,6BAA6B,QAAQ,qBAAqB,UAAU,OAAO;AAAA,EACzH,EAAE,IAAI,iCAAiC,OAAO,6BAA6B,QAAQ,qBAAqB,UAAU,OAAO;AAAA,EACzH,EAAE,IAAI,iCAAiC,OAAO,6BAA6B,QAAQ,qBAAqB,UAAU,OAAO;AAAA,EACzH,EAAE,IAAI,iCAAiC,OAAO,6BAA6B,QAAQ,qBAAqB,UAAU,OAAO;AAAA,EACzH,EAAE,IAAI,iCAAiC,OAAO,6BAA6B,QAAQ,qBAAqB,UAAU,OAAO;AAAA,EACzH,EAAE,IAAI,qCAAqC,OAAO,iCAAiC,QAAQ,yBAAyB,UAAU,OAAO;AAAA,EACrI,EAAE,IAAI,qCAAqC,OAAO,iCAAiC,QAAQ,yBAAyB,UAAU,OAAO;AAAA,EACrI,EAAE,IAAI,qCAAqC,OAAO,iCAAiC,QAAQ,yBAAyB,UAAU,OAAO;AAAA,EACrI,EAAE,IAAI,kCAAkC,OAAO,8BAA8B,QAAQ,sBAAsB,UAAU,OAAO;AAAA,EAC5H,EAAE,IAAI,kCAAkC,OAAO,8BAA8B,QAAQ,sBAAsB,UAAU,OAAO;AAAA,EAC5H,EAAE,IAAI,kCAAkC,OAAO,8BAA8B,QAAQ,sBAAsB,UAAU,OAAO;AAAA,EAC5H,EAAE,IAAI,0BAA0B,OAAO,sBAAsB,QAAQ,aAAa,UAAU,SAAS;AAAA,EACrG,EAAE,IAAI,0BAA0B,OAAO,sBAAsB,QAAQ,aAAa,UAAU,SAAS;AAAA,EACrG,EAAE,IAAI,0BAA0B,OAAO,sBAAsB,QAAQ,aAAa,UAAU,SAAS;AAAA,EACrG,EAAE,IAAI,0BAA0B,OAAO,sBAAsB,QAAQ,aAAa,UAAU,SAAS;AAAA,EACrG,EAAE,IAAI,2BAA2B,OAAO,uBAAuB,QAAQ,aAAa,UAAU,SAAS;AAAA,EACvG,EAAE,IAAI,uBAAuB,OAAO,mBAAmB,QAAQ,aAAa,UAAU,SAAS;AAAA,EAC/F,EAAE,IAAI,4BAA4B,OAAO,wBAAwB,QAAQ,aAAa,UAAU,SAAS;AAAA,EACzG,EAAE,IAAI,2BAA2B,OAAO,uBAAuB,QAAQ,aAAa,UAAU,YAAY;AAAA,EAC1G,EAAE,IAAI,+BAA+B,OAAO,2BAA2B,QAAQ,aAAa,UAAU,YAAY;AAAA,EAClH,EAAE,IAAI,uCAAuC,OAAO,mCAAmC,QAAQ,aAAa,UAAU,YAAY;AACpI;AAEO,MAAM,eAAe,mBAAmB;AAAA,EAC7C,UAAU;AAAA,EACV;AACF,CAAC;AAEM,MAAM,eAAe,aAAa;AAGzC,IAAO,iBAAQ;",
4
+ "sourcesContent": ["import { createModuleEvents } from '@open-mercato/shared/modules/events'\n\nconst events = [\n { id: 'wms.warehouse.created', label: 'Warehouse Created', entity: 'warehouse', category: 'crud' },\n { id: 'wms.warehouse.updated', label: 'Warehouse Updated', entity: 'warehouse', category: 'crud' },\n { id: 'wms.zone.created', label: 'Warehouse Zone Created', entity: 'zone', category: 'crud' },\n { id: 'wms.zone.updated', label: 'Warehouse Zone Updated', entity: 'zone', category: 'crud' },\n { id: 'wms.location.created', label: 'Warehouse Location Created', entity: 'location', category: 'crud' },\n { id: 'wms.location.updated', label: 'Warehouse Location Updated', entity: 'location', category: 'crud' },\n { id: 'wms.inventory_profile.created', label: 'Inventory Profile Created', entity: 'inventory_profile', category: 'crud' },\n { id: 'wms.inventory_profile.updated', label: 'Inventory Profile Updated', entity: 'inventory_profile', category: 'crud' },\n { id: 'wms.inventory_profile.deleted', label: 'Inventory Profile Deleted', entity: 'inventory_profile', category: 'crud' },\n { id: 'wms.inventory_balance.created', label: 'Inventory Balance Created', entity: 'inventory_balance', category: 'crud' },\n { id: 'wms.inventory_balance.updated', label: 'Inventory Balance Updated', entity: 'inventory_balance', category: 'crud' },\n { id: 'wms.inventory_balance.deleted', label: 'Inventory Balance Deleted', entity: 'inventory_balance', category: 'crud' },\n { id: 'wms.inventory_reservation.created', label: 'Inventory Reservation Created', entity: 'inventory_reservation', category: 'crud' },\n { id: 'wms.inventory_reservation.updated', label: 'Inventory Reservation Updated', entity: 'inventory_reservation', category: 'crud' },\n { id: 'wms.inventory_reservation.deleted', label: 'Inventory Reservation Deleted', entity: 'inventory_reservation', category: 'crud' },\n { id: 'wms.inventory_movement.created', label: 'Inventory Movement Created', entity: 'inventory_movement', category: 'crud' },\n { id: 'wms.inventory_movement.updated', label: 'Inventory Movement Updated', entity: 'inventory_movement', category: 'crud' },\n { id: 'wms.inventory_movement.deleted', label: 'Inventory Movement Deleted', entity: 'inventory_movement', category: 'crud' },\n { id: 'wms.inventory.received', label: 'Inventory Received', entity: 'inventory', category: 'custom' },\n { id: 'wms.inventory.adjusted', label: 'Inventory Adjusted', entity: 'inventory', category: 'custom' },\n { id: 'wms.inventory.reserved', label: 'Inventory Reserved', entity: 'inventory', category: 'custom' },\n { id: 'wms.inventory.released', label: 'Inventory Released', entity: 'inventory', category: 'custom' },\n { id: 'wms.inventory.allocated', label: 'Inventory Allocated', entity: 'inventory', category: 'custom' },\n { id: 'wms.inventory.moved', label: 'Inventory Moved', entity: 'inventory', category: 'custom' },\n { id: 'wms.inventory.reconciled', label: 'Inventory Reconciled', entity: 'inventory', category: 'custom' },\n { id: 'wms.inventory.low_stock', label: 'Inventory Low Stock', entity: 'inventory', category: 'lifecycle' },\n { id: 'wms.inventory.balance_drift', label: 'Inventory Balance Drift', entity: 'inventory', category: 'lifecycle' },\n { id: 'wms.inventory.reservation_shortfall', label: 'Inventory Reservation Shortfall', entity: 'inventory', category: 'lifecycle' },\n { id: 'wms.inventory.receipt_qc_failed', label: 'Inventory Receipt QC Failed', entity: 'inventory', category: 'custom' },\n { id: 'wms.asn.created', label: 'ASN Created', entity: 'asn', category: 'crud' },\n { id: 'wms.asn.updated', label: 'ASN Updated', entity: 'asn', category: 'crud' },\n { id: 'wms.asn.receiving_started', label: 'ASN Receiving Started', entity: 'asn', category: 'lifecycle' },\n { id: 'wms.asn.line_received', label: 'ASN Line Received', entity: 'asn', category: 'custom' },\n { id: 'wms.asn.received', label: 'ASN Received', entity: 'asn', category: 'lifecycle' },\n { id: 'wms.putaway.created', label: 'Putaway Task Created', entity: 'putaway', category: 'crud' },\n { id: 'wms.putaway.assigned', label: 'Putaway Task Assigned', entity: 'putaway', category: 'lifecycle' },\n { id: 'wms.putaway.started', label: 'Putaway Task Started', entity: 'putaway', category: 'lifecycle' },\n { id: 'wms.putaway.completed', label: 'Putaway Task Completed', entity: 'putaway', category: 'lifecycle' },\n { id: 'wms.putaway.cancelled', label: 'Putaway Task Cancelled', entity: 'putaway', category: 'lifecycle' },\n] as const\n\nexport const eventsConfig = createModuleEvents({\n moduleId: 'wms',\n events,\n})\n\nexport const emitWmsEvent = eventsConfig.emit\nexport type WmsEventId = typeof events[number]['id']\n\nexport default eventsConfig\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -15,7 +15,9 @@ const extensionPoints = defineModuleExtensionPoints({
15
15
  lotActivityTable: dataTableExtensionHost({ tableId: "wms.lot.activity", source: "components/backend/WmsLotDetailPage.tsx" }),
16
16
  lotsTable: dataTableExtensionHost({ tableId: "wms.lots.list", source: "components/backend/WmsLotsListPage.tsx" }),
17
17
  skuDistributionTable: dataTableExtensionHost({ tableId: "wms.sku.distribution", source: "components/backend/WmsSkuDetailPage.tsx" }),
18
- skuActivityTable: dataTableExtensionHost({ tableId: "wms.sku.activity", source: "components/backend/WmsSkuDetailPage.tsx" })
18
+ skuActivityTable: dataTableExtensionHost({ tableId: "wms.sku.activity", source: "components/backend/WmsSkuDetailPage.tsx" }),
19
+ asnsTable: dataTableExtensionHost({ tableId: "wms.asns.list", source: "components/backend/WmsAsnsListPage.tsx" }),
20
+ putawayTasksTable: dataTableExtensionHost({ tableId: "wms.putaway.tasks", source: "components/backend/WmsPutawayQueuePage.tsx" })
19
21
  }
20
22
  });
21
23
  var extension_points_default = extensionPoints;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/modules/wms/extension-points.ts"],
4
- "sourcesContent": ["import { dataTableExtensionHost, defineModuleExtensionPoints } from '@open-mercato/shared/modules/widgets/extension-points'\n\nexport const extensionPoints = defineModuleExtensionPoints({\n moduleId: 'wms',\n hosts: {\n warehousesTable: dataTableExtensionHost({ tableId: 'wms.config.warehouses', source: 'components/backend/WmsConfigurationPage.tsx' }),\n zonesTable: dataTableExtensionHost({ tableId: 'wms.config.zones', source: 'components/backend/WmsConfigurationPage.tsx' }),\n locationsTable: dataTableExtensionHost({ tableId: 'wms.config.locations', source: 'components/backend/WmsConfigurationPage.tsx' }),\n inventoryProfilesTable: dataTableExtensionHost({ tableId: 'wms.config.inventoryProfiles', source: 'components/backend/WmsConfigurationPage.tsx' }),\n balancesTable: dataTableExtensionHost({ tableId: 'wms.inventory.balances', source: 'components/backend/WmsInventoryConsolePage.tsx' }),\n reservationsTable: dataTableExtensionHost({ tableId: 'wms.inventory.reservations', source: 'components/backend/WmsInventoryConsolePage.tsx' }),\n movementsTable: dataTableExtensionHost({ tableId: 'wms.inventory.movements', source: 'components/backend/WmsInventoryConsolePage.tsx' }),\n locationItemsTable: dataTableExtensionHost({ tableId: 'wms.location.items', source: 'components/backend/WmsLocationDetailPage.tsx' }),\n locationActivityTable: dataTableExtensionHost({ tableId: 'wms.location.activity', source: 'components/backend/WmsLocationDetailPage.tsx' }),\n lotDistributionTable: dataTableExtensionHost({ tableId: 'wms.lot.distribution', source: 'components/backend/WmsLotDetailPage.tsx' }),\n lotActivityTable: dataTableExtensionHost({ tableId: 'wms.lot.activity', source: 'components/backend/WmsLotDetailPage.tsx' }),\n lotsTable: dataTableExtensionHost({ tableId: 'wms.lots.list', source: 'components/backend/WmsLotsListPage.tsx' }),\n skuDistributionTable: dataTableExtensionHost({ tableId: 'wms.sku.distribution', source: 'components/backend/WmsSkuDetailPage.tsx' }),\n skuActivityTable: dataTableExtensionHost({ tableId: 'wms.sku.activity', source: 'components/backend/WmsSkuDetailPage.tsx' }),\n },\n})\n\nexport default extensionPoints\n"],
5
- "mappings": "AAAA,SAAS,wBAAwB,mCAAmC;AAE7D,MAAM,kBAAkB,4BAA4B;AAAA,EACzD,UAAU;AAAA,EACV,OAAO;AAAA,IACL,iBAAiB,uBAAuB,EAAE,SAAS,yBAAyB,QAAQ,8CAA8C,CAAC;AAAA,IACnI,YAAY,uBAAuB,EAAE,SAAS,oBAAoB,QAAQ,8CAA8C,CAAC;AAAA,IACzH,gBAAgB,uBAAuB,EAAE,SAAS,wBAAwB,QAAQ,8CAA8C,CAAC;AAAA,IACjI,wBAAwB,uBAAuB,EAAE,SAAS,gCAAgC,QAAQ,8CAA8C,CAAC;AAAA,IACjJ,eAAe,uBAAuB,EAAE,SAAS,0BAA0B,QAAQ,iDAAiD,CAAC;AAAA,IACrI,mBAAmB,uBAAuB,EAAE,SAAS,8BAA8B,QAAQ,iDAAiD,CAAC;AAAA,IAC7I,gBAAgB,uBAAuB,EAAE,SAAS,2BAA2B,QAAQ,iDAAiD,CAAC;AAAA,IACvI,oBAAoB,uBAAuB,EAAE,SAAS,sBAAsB,QAAQ,+CAA+C,CAAC;AAAA,IACpI,uBAAuB,uBAAuB,EAAE,SAAS,yBAAyB,QAAQ,+CAA+C,CAAC;AAAA,IAC1I,sBAAsB,uBAAuB,EAAE,SAAS,wBAAwB,QAAQ,0CAA0C,CAAC;AAAA,IACnI,kBAAkB,uBAAuB,EAAE,SAAS,oBAAoB,QAAQ,0CAA0C,CAAC;AAAA,IAC3H,WAAW,uBAAuB,EAAE,SAAS,iBAAiB,QAAQ,yCAAyC,CAAC;AAAA,IAChH,sBAAsB,uBAAuB,EAAE,SAAS,wBAAwB,QAAQ,0CAA0C,CAAC;AAAA,IACnI,kBAAkB,uBAAuB,EAAE,SAAS,oBAAoB,QAAQ,0CAA0C,CAAC;AAAA,EAC7H;AACF,CAAC;AAED,IAAO,2BAAQ;",
4
+ "sourcesContent": ["import { dataTableExtensionHost, defineModuleExtensionPoints } from '@open-mercato/shared/modules/widgets/extension-points'\n\nexport const extensionPoints = defineModuleExtensionPoints({\n moduleId: 'wms',\n hosts: {\n warehousesTable: dataTableExtensionHost({ tableId: 'wms.config.warehouses', source: 'components/backend/WmsConfigurationPage.tsx' }),\n zonesTable: dataTableExtensionHost({ tableId: 'wms.config.zones', source: 'components/backend/WmsConfigurationPage.tsx' }),\n locationsTable: dataTableExtensionHost({ tableId: 'wms.config.locations', source: 'components/backend/WmsConfigurationPage.tsx' }),\n inventoryProfilesTable: dataTableExtensionHost({ tableId: 'wms.config.inventoryProfiles', source: 'components/backend/WmsConfigurationPage.tsx' }),\n balancesTable: dataTableExtensionHost({ tableId: 'wms.inventory.balances', source: 'components/backend/WmsInventoryConsolePage.tsx' }),\n reservationsTable: dataTableExtensionHost({ tableId: 'wms.inventory.reservations', source: 'components/backend/WmsInventoryConsolePage.tsx' }),\n movementsTable: dataTableExtensionHost({ tableId: 'wms.inventory.movements', source: 'components/backend/WmsInventoryConsolePage.tsx' }),\n locationItemsTable: dataTableExtensionHost({ tableId: 'wms.location.items', source: 'components/backend/WmsLocationDetailPage.tsx' }),\n locationActivityTable: dataTableExtensionHost({ tableId: 'wms.location.activity', source: 'components/backend/WmsLocationDetailPage.tsx' }),\n lotDistributionTable: dataTableExtensionHost({ tableId: 'wms.lot.distribution', source: 'components/backend/WmsLotDetailPage.tsx' }),\n lotActivityTable: dataTableExtensionHost({ tableId: 'wms.lot.activity', source: 'components/backend/WmsLotDetailPage.tsx' }),\n lotsTable: dataTableExtensionHost({ tableId: 'wms.lots.list', source: 'components/backend/WmsLotsListPage.tsx' }),\n skuDistributionTable: dataTableExtensionHost({ tableId: 'wms.sku.distribution', source: 'components/backend/WmsSkuDetailPage.tsx' }),\n skuActivityTable: dataTableExtensionHost({ tableId: 'wms.sku.activity', source: 'components/backend/WmsSkuDetailPage.tsx' }),\n asnsTable: dataTableExtensionHost({ tableId: 'wms.asns.list', source: 'components/backend/WmsAsnsListPage.tsx' }),\n putawayTasksTable: dataTableExtensionHost({ tableId: 'wms.putaway.tasks', source: 'components/backend/WmsPutawayQueuePage.tsx' }),\n },\n})\n\nexport default extensionPoints\n"],
5
+ "mappings": "AAAA,SAAS,wBAAwB,mCAAmC;AAE7D,MAAM,kBAAkB,4BAA4B;AAAA,EACzD,UAAU;AAAA,EACV,OAAO;AAAA,IACL,iBAAiB,uBAAuB,EAAE,SAAS,yBAAyB,QAAQ,8CAA8C,CAAC;AAAA,IACnI,YAAY,uBAAuB,EAAE,SAAS,oBAAoB,QAAQ,8CAA8C,CAAC;AAAA,IACzH,gBAAgB,uBAAuB,EAAE,SAAS,wBAAwB,QAAQ,8CAA8C,CAAC;AAAA,IACjI,wBAAwB,uBAAuB,EAAE,SAAS,gCAAgC,QAAQ,8CAA8C,CAAC;AAAA,IACjJ,eAAe,uBAAuB,EAAE,SAAS,0BAA0B,QAAQ,iDAAiD,CAAC;AAAA,IACrI,mBAAmB,uBAAuB,EAAE,SAAS,8BAA8B,QAAQ,iDAAiD,CAAC;AAAA,IAC7I,gBAAgB,uBAAuB,EAAE,SAAS,2BAA2B,QAAQ,iDAAiD,CAAC;AAAA,IACvI,oBAAoB,uBAAuB,EAAE,SAAS,sBAAsB,QAAQ,+CAA+C,CAAC;AAAA,IACpI,uBAAuB,uBAAuB,EAAE,SAAS,yBAAyB,QAAQ,+CAA+C,CAAC;AAAA,IAC1I,sBAAsB,uBAAuB,EAAE,SAAS,wBAAwB,QAAQ,0CAA0C,CAAC;AAAA,IACnI,kBAAkB,uBAAuB,EAAE,SAAS,oBAAoB,QAAQ,0CAA0C,CAAC;AAAA,IAC3H,WAAW,uBAAuB,EAAE,SAAS,iBAAiB,QAAQ,yCAAyC,CAAC;AAAA,IAChH,sBAAsB,uBAAuB,EAAE,SAAS,wBAAwB,QAAQ,0CAA0C,CAAC;AAAA,IACnI,kBAAkB,uBAAuB,EAAE,SAAS,oBAAoB,QAAQ,0CAA0C,CAAC;AAAA,IAC3H,WAAW,uBAAuB,EAAE,SAAS,iBAAiB,QAAQ,yCAAyC,CAAC;AAAA,IAChH,mBAAmB,uBAAuB,EAAE,SAAS,qBAAqB,QAAQ,6CAA6C,CAAC;AAAA,EAClI;AACF,CAAC;AAED,IAAO,2BAAQ;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,124 @@
1
+ import { createHash } from "crypto";
2
+ import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
3
+ const ASN_STATUSES = /* @__PURE__ */ new Set([
4
+ "draft",
5
+ "in_transit",
6
+ "received",
7
+ "closed"
8
+ ]);
9
+ function buildAsnStatusFilter(statusQuery) {
10
+ if (typeof statusQuery !== "string" || statusQuery.trim().length === 0) return void 0;
11
+ const statuses = [
12
+ ...new Set(
13
+ statusQuery.split(",").map((value) => value.trim()).filter((value) => ASN_STATUSES.has(value))
14
+ )
15
+ ];
16
+ if (statuses.length === 0) return void 0;
17
+ if (statuses.length === 1) return { $eq: statuses[0] };
18
+ return { $in: statuses };
19
+ }
20
+ function assertAsnQcTransition(current, next) {
21
+ if (current === "pending") return;
22
+ if (current === next) return;
23
+ throw new CrudHttpError(422, { error: "invalid_qc_transition" });
24
+ }
25
+ function shouldWriteStockOnQcPass(qcStatus) {
26
+ return qcStatus === "passed";
27
+ }
28
+ function acceptedReceivedQty(line) {
29
+ const received = Number(line.receivedQty);
30
+ if (!Number.isFinite(received) || received <= 0) return 0;
31
+ if (line.qcStatus !== "passed") return 0;
32
+ return received;
33
+ }
34
+ function hasAsnReceiptActivity(lines) {
35
+ return lines.some((line) => {
36
+ const received = Number(line.receivedQty);
37
+ return Number.isFinite(received) && received > 0;
38
+ });
39
+ }
40
+ function hasAsnDeleteBlockingLineActivity(lines) {
41
+ return lines.some((line) => {
42
+ const received = Number(line.receivedQty);
43
+ if (Number.isFinite(received) && received > 0) return true;
44
+ return line.qcStatus !== "pending";
45
+ });
46
+ }
47
+ function isAsnCloseable(lines, closeWhenShort) {
48
+ if (lines.length === 0) return false;
49
+ if (!hasAsnReceiptActivity(lines)) return false;
50
+ if (closeWhenShort) return true;
51
+ return lines.every((line) => {
52
+ const expected = Number(line.expectedQty);
53
+ if (!Number.isFinite(expected)) return false;
54
+ return acceptedReceivedQty(line) + 1e-6 >= expected;
55
+ });
56
+ }
57
+ function buildStableUuid(seed) {
58
+ const hex = createHash("sha256").update(seed).digest("hex");
59
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
60
+ }
61
+ function resolveAsnReceiveAttempt(input) {
62
+ const idempotencyKey = input.idempotencyKey?.trim() || null;
63
+ const hasAbsoluteTarget = input.targetReceivedQty != null && Number.isFinite(input.targetReceivedQty);
64
+ if (!hasAbsoluteTarget) {
65
+ throw new CrudHttpError(422, { error: "target_received_qty_required" });
66
+ }
67
+ const targetReceivedQty = Number(input.targetReceivedQty);
68
+ const applyQty = Math.max(0, targetReceivedQty - input.priorReceivedQty);
69
+ const attemptKey = idempotencyKey ? ["wms:asn-receive-idemp", input.lineId, idempotencyKey, String(targetReceivedQty)].join(":") : ["wms:asn-receive-target", input.lineId, String(targetReceivedQty)].join(":");
70
+ return { attemptKey, targetReceivedQty, applyQty };
71
+ }
72
+ function buildAsnReceiveReferenceId(input) {
73
+ return buildStableUuid(
74
+ ["wms:asn-receive", input.attemptKey, input.serialNumber?.trim() ?? ""].join(":")
75
+ );
76
+ }
77
+ function buildAsnReceivePutawayKey(input) {
78
+ const serialNumber = input.serialNumber?.trim() ?? "";
79
+ const parts = ["wms:asn-receive-putaway", input.attemptKey];
80
+ if (serialNumber.length > 0) parts.push(serialNumber);
81
+ return buildStableUuid(parts.join(":"));
82
+ }
83
+ function putawayMetadataMatchesKey(metadata, putawayKey) {
84
+ if (!metadata) return false;
85
+ return metadata.putawayKey === putawayKey;
86
+ }
87
+ function shouldEnsurePutawayOnAlreadyAtTarget(existingOpenPutaway) {
88
+ return existingOpenPutaway == null;
89
+ }
90
+ function resolvePutawayQuantityForAlreadyAtTargetRetry(input) {
91
+ const fromCancelled = input.cancelledTaskQuantity != null && Number.isFinite(input.cancelledTaskQuantity) ? Number(input.cancelledTaskQuantity) : null;
92
+ if (fromCancelled != null && fromCancelled > 1e-6) return fromCancelled;
93
+ return Math.max(0, input.absoluteTargetQty);
94
+ }
95
+ function shouldRecreatePutawayOnAlreadyAtTarget(input) {
96
+ if (!(input.requestedQuantity > 1e-6)) return false;
97
+ return input.requestedQuantity <= input.remainingAvailable + 1e-6;
98
+ }
99
+ function assertReceivingLineLifecycleFieldsForbidden(rawInput) {
100
+ if (!rawInput || typeof rawInput !== "object") return;
101
+ const payload = rawInput;
102
+ if ("qcStatus" in payload || "receivedQty" in payload || "rejectionReason" in payload) {
103
+ throw new CrudHttpError(422, { error: "lifecycle_field_forbidden" });
104
+ }
105
+ }
106
+ export {
107
+ acceptedReceivedQty,
108
+ assertAsnQcTransition,
109
+ assertReceivingLineLifecycleFieldsForbidden,
110
+ buildAsnReceivePutawayKey,
111
+ buildAsnReceiveReferenceId,
112
+ buildAsnStatusFilter,
113
+ buildStableUuid,
114
+ hasAsnDeleteBlockingLineActivity,
115
+ hasAsnReceiptActivity,
116
+ isAsnCloseable,
117
+ putawayMetadataMatchesKey,
118
+ resolveAsnReceiveAttempt,
119
+ resolvePutawayQuantityForAlreadyAtTargetRetry,
120
+ shouldEnsurePutawayOnAlreadyAtTarget,
121
+ shouldRecreatePutawayOnAlreadyAtTarget,
122
+ shouldWriteStockOnQcPass
123
+ };
124
+ //# sourceMappingURL=asnReceiving.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/wms/lib/asnReceiving.ts"],
4
+ "sourcesContent": ["import { createHash } from 'crypto'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport type { AsnStatus, ReceivingLineQcStatus } from '../data/entities'\n\nconst ASN_STATUSES: ReadonlySet<AsnStatus> = new Set([\n 'draft',\n 'in_transit',\n 'received',\n 'closed',\n])\n\n/**\n * Parse ASN list `status` query: single value or comma-separated\n * (e.g. `draft,in_transit` for the open receiving queue).\n * Returns `$eq` / `$in` filter fragment, or undefined when empty/invalid.\n */\nexport function buildAsnStatusFilter(\n statusQuery: string | undefined,\n): { $eq: AsnStatus } | { $in: AsnStatus[] } | undefined {\n if (typeof statusQuery !== 'string' || statusQuery.trim().length === 0) return undefined\n const statuses = [\n ...new Set(\n statusQuery\n .split(',')\n .map((value) => value.trim())\n .filter((value): value is AsnStatus => ASN_STATUSES.has(value as AsnStatus)),\n ),\n ]\n if (statuses.length === 0) return undefined\n if (statuses.length === 1) return { $eq: statuses[0]! }\n return { $in: statuses }\n}\n\nexport function assertAsnQcTransition(\n current: ReceivingLineQcStatus,\n next: 'passed' | 'failed',\n): void {\n if (current === 'pending') return\n if (current === next) return\n throw new CrudHttpError(422, { error: 'invalid_qc_transition' })\n}\n\nexport function shouldWriteStockOnQcPass(qcStatus: 'passed' | 'failed'): boolean {\n return qcStatus === 'passed'\n}\n\n/**\n * Accepted qty for close checks: only QC-passed quantity counts toward\n * fulfillment. QC-failed receipts increment receivedQty for audit but do not\n * satisfy the ASN unless `closeWhenShort` is set.\n */\nexport function acceptedReceivedQty(line: {\n receivedQty: string | number\n qcStatus?: ReceivingLineQcStatus | string | null\n}): number {\n const received = Number(line.receivedQty)\n if (!Number.isFinite(received) || received <= 0) return 0\n if (line.qcStatus !== 'passed') return 0\n return received\n}\n\n/** True when at least one receiving line has recorded receipt qty (> 0). */\nexport function hasAsnReceiptActivity(\n lines: Array<{ receivedQty: string | number }>,\n): boolean {\n return lines.some((line) => {\n const received = Number(line.receivedQty)\n return Number.isFinite(received) && received > 0\n })\n}\n\n/**\n * Aligns with `wms.receiving-lines.delete`: any received qty or non-pending QC\n * blocks soft-deleting the ASN (would free `source_key` while staging stock remains).\n */\nexport function hasAsnDeleteBlockingLineActivity(\n lines: Array<{ receivedQty: string | number; qcStatus?: string | null }>,\n): boolean {\n return lines.some((line) => {\n const received = Number(line.receivedQty)\n if (Number.isFinite(received) && received > 0) return true\n return line.qcStatus !== 'pending'\n })\n}\n\nexport function isAsnCloseable(\n lines: Array<{\n expectedQty: string | number\n receivedQty: string | number\n qcStatus?: ReceivingLineQcStatus | string | null\n }>,\n closeWhenShort: boolean,\n): boolean {\n // Header-only ASNs (no receiving lines) are never closeable \u2014 `[].every` would\n // otherwise be vacuously true and `closeWhenShort` would short-circuit to true.\n if (lines.length === 0) return false\n // Zero-activity lines (all receivedQty 0) must not close even with closeWhenShort \u2014\n // that flag only covers short / QC-fail receipts after some receipt activity.\n if (!hasAsnReceiptActivity(lines)) return false\n if (closeWhenShort) return true\n return lines.every((line) => {\n const expected = Number(line.expectedQty)\n if (!Number.isFinite(expected)) return false\n return acceptedReceivedQty(line) + 0.000001 >= expected\n })\n}\n\nexport function buildStableUuid(seed: string): string {\n const hex = createHash('sha256').update(seed).digest('hex')\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`\n}\n\n/**\n * Resolve the stable receive attempt identity.\n *\n * Both QC-pass and QC-fail MUST supply absolute `targetReceivedQty` (enforced by\n * zod + command). Optional `idempotencyKey` is an extra stabilizer, but the\n * attempt key always includes absolute target \u2014 same key + different target must\n * not reuse the first movement/putaway identity while advancing line qty.\n * Never derive target as `prior + receivedQty` (that path double-applies audit\n * qty on identical HTTP retry after success).\n */\nexport function resolveAsnReceiveAttempt(input: {\n lineId: string\n priorReceivedQty: number\n receivedQty: number\n targetReceivedQty?: number | null\n idempotencyKey?: string | null\n}): {\n attemptKey: string\n targetReceivedQty: number\n applyQty: number\n} {\n const idempotencyKey = input.idempotencyKey?.trim() || null\n const hasAbsoluteTarget =\n input.targetReceivedQty != null && Number.isFinite(input.targetReceivedQty)\n if (!hasAbsoluteTarget) {\n throw new CrudHttpError(422, { error: 'target_received_qty_required' })\n }\n const targetReceivedQty = Number(input.targetReceivedQty)\n const applyQty = Math.max(0, targetReceivedQty - input.priorReceivedQty)\n // Always include absolute target so same idempotencyKey + higher target cannot\n // reuse the first movement referenceId / putaway_key while applyQty > 0.\n const attemptKey = idempotencyKey\n ? ['wms:asn-receive-idemp', input.lineId, idempotencyKey, String(targetReceivedQty)].join(':')\n : ['wms:asn-receive-target', input.lineId, String(targetReceivedQty)].join(':')\n return { attemptKey, targetReceivedQty, applyQty }\n}\n\n/** Deterministic movement referenceId so client retries reuse inventory idempotency. */\nexport function buildAsnReceiveReferenceId(input: {\n attemptKey: string\n serialNumber?: string | null\n}): string {\n return buildStableUuid(\n ['wms:asn-receive', input.attemptKey, input.serialNumber?.trim() ?? ''].join(':'),\n )\n}\n\nexport function buildAsnReceivePutawayKey(input: {\n attemptKey: string\n serialNumber?: string | null\n}): string {\n const serialNumber = input.serialNumber?.trim() ?? ''\n const parts = ['wms:asn-receive-putaway', input.attemptKey]\n // Blank serial keeps the legacy attempt key so non-serial retries still find the original task.\n if (serialNumber.length > 0) parts.push(serialNumber)\n return buildStableUuid(parts.join(':'))\n}\n\nexport function putawayMetadataMatchesKey(\n metadata: Record<string, unknown> | null | undefined,\n putawayKey: string,\n): boolean {\n if (!metadata) return false\n return metadata.putawayKey === putawayKey\n}\n\n/**\n * QC-pass retries with applyQty=0 must still ensure a queue task when the\n * auto-created putaway was cancelled (cancel clears putaway_key). Reuse any\n * open/in_progress/done task; otherwise find-or-create again.\n */\nexport function shouldEnsurePutawayOnAlreadyAtTarget(\n existingOpenPutaway: { id: string } | null | undefined,\n): boolean {\n return existingOpenPutaway == null\n}\n\n/**\n * Prefer the cancelled task's quantity (metadata still carries putawayKey after\n * column clear); fall back to absolute target for first-shot receives.\n */\nexport function resolvePutawayQuantityForAlreadyAtTargetRetry(input: {\n cancelledTaskQuantity: number | null | undefined\n absoluteTargetQty: number\n}): number {\n const fromCancelled =\n input.cancelledTaskQuantity != null && Number.isFinite(input.cancelledTaskQuantity)\n ? Number(input.cancelledTaskQuantity)\n : null\n if (fromCancelled != null && fromCancelled > 0.000001) return fromCancelled\n return Math.max(0, input.absoluteTargetQty)\n}\n\n/**\n * Idempotent already-at-target recreate must not open a second putaway when\n * staging stock is already fully committed by other open/in_progress tasks\n * (manual putaway after cancel). Skip create when remaining would go negative.\n */\nexport function shouldRecreatePutawayOnAlreadyAtTarget(input: {\n requestedQuantity: number\n remainingAvailable: number\n}): boolean {\n if (!(input.requestedQuantity > 0.000001)) return false\n return input.requestedQuantity <= input.remainingAvailable + 0.000001\n}\n\n/** Reject lifecycle fields on receiving-line CRUD before zod strips them. */\nexport function assertReceivingLineLifecycleFieldsForbidden(rawInput: unknown): void {\n if (!rawInput || typeof rawInput !== 'object') return\n const payload = rawInput as Record<string, unknown>\n if ('qcStatus' in payload || 'receivedQty' in payload || 'rejectionReason' in payload) {\n throw new CrudHttpError(422, { error: 'lifecycle_field_forbidden' })\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,52 @@
1
+ import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
2
+ import { E } from "../../../generated/entities.ids.generated.js";
3
+ function isOptionalCustomersPeerAbsentError(error) {
4
+ if (!error || typeof error !== "object") return false;
5
+ const name = String(error.name ?? "");
6
+ const message = String(error.message ?? "");
7
+ if (name === "AwilixResolutionError") return true;
8
+ if (/Could not resolve ['"]?queryEngine['"]?/i.test(message)) return true;
9
+ if (/unknown entity|entity id .+ not (found|registered)|no entity metadata for|entity .+ is not registered/i.test(
10
+ message
11
+ )) {
12
+ return true;
13
+ }
14
+ return false;
15
+ }
16
+ function mapVendorLookupFailure(error) {
17
+ if (error instanceof CrudHttpError) return error;
18
+ return new CrudHttpError(503, { error: "vendor_lookup_unavailable" });
19
+ }
20
+ async function requireVendorIfPresent(container, vendorId, scope) {
21
+ if (!vendorId) return;
22
+ const customerEntityId = E.customers?.customer_entity;
23
+ if (!customerEntityId) return;
24
+ let queryEngine;
25
+ try {
26
+ queryEngine = container.resolve("queryEngine");
27
+ } catch (error) {
28
+ if (isOptionalCustomersPeerAbsentError(error)) return;
29
+ throw mapVendorLookupFailure(error);
30
+ }
31
+ try {
32
+ const result = await queryEngine.query(customerEntityId, {
33
+ tenantId: scope.tenantId,
34
+ organizationId: scope.organizationId,
35
+ filters: { id: { $eq: vendorId } },
36
+ fields: ["id"],
37
+ page: { page: 1, pageSize: 1 }
38
+ });
39
+ if (!result.items[0]?.id) {
40
+ throw new CrudHttpError(422, { error: "invalid_vendor" });
41
+ }
42
+ } catch (error) {
43
+ if (error instanceof CrudHttpError) throw error;
44
+ if (isOptionalCustomersPeerAbsentError(error)) return;
45
+ throw mapVendorLookupFailure(error);
46
+ }
47
+ }
48
+ export {
49
+ isOptionalCustomersPeerAbsentError,
50
+ requireVendorIfPresent
51
+ };
52
+ //# sourceMappingURL=asnVendor.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/wms/lib/asnVendor.ts"],
4
+ "sourcesContent": ["import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport type { QueryEngine } from '@open-mercato/shared/lib/query/types'\nimport { E } from '#generated/entities.ids.generated'\n\ntype VendorLookupScope = {\n tenantId: string\n organizationId: string\n}\n\ntype VendorLookupContainer = {\n resolve: (name: string) => unknown\n}\n\n/**\n * True when the failure indicates the optional `customers` peer (or query\n * engine) is absent from the deployment \u2014 not a transient lookup failure.\n * Transient errors must NOT be treated as \"vendor OK\".\n */\nexport function isOptionalCustomersPeerAbsentError(error: unknown): boolean {\n if (!error || typeof error !== 'object') return false\n const name = String((error as { name?: string }).name ?? '')\n const message = String((error as { message?: string }).message ?? '')\n if (name === 'AwilixResolutionError') return true\n if (/Could not resolve ['\"]?queryEngine['\"]?/i.test(message)) return true\n if (\n /unknown entity|entity id .+ not (found|registered)|no entity metadata for|entity .+ is not registered/i.test(\n message,\n )\n ) {\n return true\n }\n return false\n}\n\nfunction mapVendorLookupFailure(error: unknown): CrudHttpError {\n if (error instanceof CrudHttpError) return error\n return new CrudHttpError(503, { error: 'vendor_lookup_unavailable' })\n}\n\n/**\n * When `vendorId` is set and the customers peer is available, require a\n * matching customer entity in scope. Skip only when the peer is absent;\n * re-throw / map other failures so invalid vendors are not silently accepted.\n */\nexport async function requireVendorIfPresent(\n container: VendorLookupContainer,\n vendorId: string | null | undefined,\n scope: VendorLookupScope,\n): Promise<void> {\n if (!vendorId) return\n const customerEntityId = (\n E as { customers?: { customer_entity?: string } }\n ).customers?.customer_entity\n if (!customerEntityId) return\n\n let queryEngine: QueryEngine\n try {\n queryEngine = container.resolve('queryEngine') as QueryEngine\n } catch (error) {\n if (isOptionalCustomersPeerAbsentError(error)) return\n throw mapVendorLookupFailure(error)\n }\n\n try {\n const result = await queryEngine.query<{ id?: string }>(customerEntityId, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n filters: { id: { $eq: vendorId } },\n fields: ['id'],\n page: { page: 1, pageSize: 1 },\n })\n if (!result.items[0]?.id) {\n throw new CrudHttpError(422, { error: 'invalid_vendor' })\n }\n } catch (error) {\n if (error instanceof CrudHttpError) throw error\n if (isOptionalCustomersPeerAbsentError(error)) return\n throw mapVendorLookupFailure(error)\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,qBAAqB;AAE9B,SAAS,SAAS;AAgBX,SAAS,mCAAmC,OAAyB;AAC1E,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,OAAO,OAAQ,MAA4B,QAAQ,EAAE;AAC3D,QAAM,UAAU,OAAQ,MAA+B,WAAW,EAAE;AACpE,MAAI,SAAS,wBAAyB,QAAO;AAC7C,MAAI,2CAA2C,KAAK,OAAO,EAAG,QAAO;AACrE,MACE,yGAAyG;AAAA,IACvG;AAAA,EACF,GACA;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,uBAAuB,OAA+B;AAC7D,MAAI,iBAAiB,cAAe,QAAO;AAC3C,SAAO,IAAI,cAAc,KAAK,EAAE,OAAO,4BAA4B,CAAC;AACtE;AAOA,eAAsB,uBACpB,WACA,UACA,OACe;AACf,MAAI,CAAC,SAAU;AACf,QAAM,mBACJ,EACA,WAAW;AACb,MAAI,CAAC,iBAAkB;AAEvB,MAAI;AACJ,MAAI;AACF,kBAAc,UAAU,QAAQ,aAAa;AAAA,EAC/C,SAAS,OAAO;AACd,QAAI,mCAAmC,KAAK,EAAG;AAC/C,UAAM,uBAAuB,KAAK;AAAA,EACpC;AAEA,MAAI;AACF,UAAM,SAAS,MAAM,YAAY,MAAuB,kBAAkB;AAAA,MACxE,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,MACtB,SAAS,EAAE,IAAI,EAAE,KAAK,SAAS,EAAE;AAAA,MACjC,QAAQ,CAAC,IAAI;AAAA,MACb,MAAM,EAAE,MAAM,GAAG,UAAU,EAAE;AAAA,IAC/B,CAAC;AACD,QAAI,CAAC,OAAO,MAAM,CAAC,GAAG,IAAI;AACxB,YAAM,IAAI,cAAc,KAAK,EAAE,OAAO,iBAAiB,CAAC;AAAA,IAC1D;AAAA,EACF,SAAS,OAAO;AACd,QAAI,iBAAiB,cAAe,OAAM;AAC1C,QAAI,mCAAmC,KAAK,EAAG;AAC/C,UAAM,uBAAuB,KAAK;AAAA,EACpC;AACF;",
6
+ "names": []
7
+ }
@@ -7,6 +7,8 @@ const FORBIDDEN_FEATURE_MESSAGE_KEYS = {
7
7
  "wms.manage_reservations": "wms.errors.forbidden.manageReservations",
8
8
  "wms.adjust_inventory": "wms.errors.forbidden.adjustInventory",
9
9
  "wms.receive_inventory": "wms.errors.forbidden.receiveInventory",
10
+ "wms.manage_asn": "wms.errors.forbidden.manageAsn",
11
+ "wms.manage_putaway": "wms.errors.forbidden.managePutaway",
10
12
  "wms.cycle_count": "wms.errors.forbidden.cycleCount",
11
13
  "wms.import": "wms.errors.forbidden.import"
12
14
  };
@@ -18,6 +20,8 @@ const FORBIDDEN_FEATURE_MESSAGE_FALLBACKS = {
18
20
  "wms.manage_reservations": "You don't have permission to manage inventory reservations.",
19
21
  "wms.adjust_inventory": "You don't have permission to adjust or move inventory.",
20
22
  "wms.receive_inventory": "You don't have permission to receive inbound inventory.",
23
+ "wms.manage_asn": "You don't have permission to manage ASNs.",
24
+ "wms.manage_putaway": "You don't have permission to manage putaway tasks.",
21
25
  "wms.cycle_count": "You don't have permission to run cycle counts.",
22
26
  "wms.import": "You don't have permission to import inventory."
23
27
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/wms/lib/flashMutationError.ts"],
4
- "sourcesContent": ["import { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport type { TranslateFn } from '@open-mercato/shared/lib/i18n/context'\n\n/**\n * Maps a `requireFeatures` ACL feature id to the i18n key that names the\n * specific permission a 403 response is missing (#4102 follow-up \u2014 the\n * generic \"Forbidden\" toast didn't say what was denied).\n */\nconst FORBIDDEN_FEATURE_MESSAGE_KEYS: Record<string, string> = {\n 'wms.manage_warehouses': 'wms.errors.forbidden.manageWarehouses',\n 'wms.manage_zones': 'wms.errors.forbidden.manageZones',\n 'wms.manage_locations': 'wms.errors.forbidden.manageLocations',\n 'wms.manage_inventory': 'wms.errors.forbidden.manageInventory',\n 'wms.manage_reservations': 'wms.errors.forbidden.manageReservations',\n 'wms.adjust_inventory': 'wms.errors.forbidden.adjustInventory',\n 'wms.receive_inventory': 'wms.errors.forbidden.receiveInventory',\n 'wms.cycle_count': 'wms.errors.forbidden.cycleCount',\n 'wms.import': 'wms.errors.forbidden.import',\n}\n\nconst FORBIDDEN_FEATURE_MESSAGE_FALLBACKS: Record<string, string> = {\n 'wms.manage_warehouses': \"You don't have permission to manage warehouses.\",\n 'wms.manage_zones': \"You don't have permission to manage warehouse zones.\",\n 'wms.manage_locations': \"You don't have permission to manage warehouse locations.\",\n 'wms.manage_inventory': \"You don't have permission to manage inventory profiles.\",\n 'wms.manage_reservations': \"You don't have permission to manage inventory reservations.\",\n 'wms.adjust_inventory': \"You don't have permission to adjust or move inventory.\",\n 'wms.receive_inventory': \"You don't have permission to receive inbound inventory.\",\n 'wms.cycle_count': \"You don't have permission to run cycle counts.\",\n 'wms.import': \"You don't have permission to import inventory.\",\n}\n\n/**\n * Reads the 403 response's `requiredFeatures` (attached to the thrown error\n * by `raiseCrudError`) and, when the missing feature is a known WMS\n * permission, resolves a translated message naming it. Returns `null` when\n * the error isn't a recognized 403-with-features shape so callers can fall\n * back to the generic message.\n */\nfunction resolveForbiddenFeatureMessage(error: unknown, t?: TranslateFn): string | null {\n if (!error || typeof error !== 'object') return null\n const status = (error as Record<string, unknown>).status\n if (status !== 403) return null\n const requiredFeatures = (error as Record<string, unknown>).requiredFeatures\n if (!Array.isArray(requiredFeatures)) return null\n const knownFeature = requiredFeatures.find(\n (feature): feature is string => typeof feature === 'string' && feature in FORBIDDEN_FEATURE_MESSAGE_KEYS,\n )\n if (!knownFeature) return null\n const key = FORBIDDEN_FEATURE_MESSAGE_KEYS[knownFeature]\n const fallback = FORBIDDEN_FEATURE_MESSAGE_FALLBACKS[knownFeature]\n return t ? t(key, fallback) : fallback\n}\n\n/**\n * Surfaces a mutation failure as an error flash toast. Prefers a\n * feature-specific 403 message derived from the server's `requiredFeatures`\n * hint (#4102 follow-up), then the thrown error's own message (already\n * server-derived and translated by `raiseCrudError`/`useGuardedMutation`),\n * and falls back to a translated, dialog-specific message otherwise (#4103).\n */\nexport function flashMutationError(error: unknown, fallbackMessage: string, t?: TranslateFn): void {\n const forbiddenMessage = resolveForbiddenFeatureMessage(error, t)\n if (forbiddenMessage) {\n flash(forbiddenMessage, 'error')\n return\n }\n // apiFetch throws ForbiddenError with a bare \"Forbidden\" message when the\n // ACL body has no mappable feature \u2014 prefer the dialog-specific fallback\n // over flashing the untranslated server token.\n const rawMessage = error instanceof Error ? error.message.trim() : ''\n const isGenericForbidden = rawMessage.length === 0 || /^forbidden$/i.test(rawMessage)\n flash(isGenericForbidden ? fallbackMessage : rawMessage, 'error')\n}\n"],
5
- "mappings": "AAAA,SAAS,aAAa;AAQtB,MAAM,iCAAyD;AAAA,EAC7D,yBAAyB;AAAA,EACzB,oBAAoB;AAAA,EACpB,wBAAwB;AAAA,EACxB,wBAAwB;AAAA,EACxB,2BAA2B;AAAA,EAC3B,wBAAwB;AAAA,EACxB,yBAAyB;AAAA,EACzB,mBAAmB;AAAA,EACnB,cAAc;AAChB;AAEA,MAAM,sCAA8D;AAAA,EAClE,yBAAyB;AAAA,EACzB,oBAAoB;AAAA,EACpB,wBAAwB;AAAA,EACxB,wBAAwB;AAAA,EACxB,2BAA2B;AAAA,EAC3B,wBAAwB;AAAA,EACxB,yBAAyB;AAAA,EACzB,mBAAmB;AAAA,EACnB,cAAc;AAChB;AASA,SAAS,+BAA+B,OAAgB,GAAgC;AACtF,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,SAAU,MAAkC;AAClD,MAAI,WAAW,IAAK,QAAO;AAC3B,QAAM,mBAAoB,MAAkC;AAC5D,MAAI,CAAC,MAAM,QAAQ,gBAAgB,EAAG,QAAO;AAC7C,QAAM,eAAe,iBAAiB;AAAA,IACpC,CAAC,YAA+B,OAAO,YAAY,YAAY,WAAW;AAAA,EAC5E;AACA,MAAI,CAAC,aAAc,QAAO;AAC1B,QAAM,MAAM,+BAA+B,YAAY;AACvD,QAAM,WAAW,oCAAoC,YAAY;AACjE,SAAO,IAAI,EAAE,KAAK,QAAQ,IAAI;AAChC;AASO,SAAS,mBAAmB,OAAgB,iBAAyB,GAAuB;AACjG,QAAM,mBAAmB,+BAA+B,OAAO,CAAC;AAChE,MAAI,kBAAkB;AACpB,UAAM,kBAAkB,OAAO;AAC/B;AAAA,EACF;AAIA,QAAM,aAAa,iBAAiB,QAAQ,MAAM,QAAQ,KAAK,IAAI;AACnE,QAAM,qBAAqB,WAAW,WAAW,KAAK,eAAe,KAAK,UAAU;AACpF,QAAM,qBAAqB,kBAAkB,YAAY,OAAO;AAClE;",
4
+ "sourcesContent": ["import { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport type { TranslateFn } from '@open-mercato/shared/lib/i18n/context'\n\n/**\n * Maps a `requireFeatures` ACL feature id to the i18n key that names the\n * specific permission a 403 response is missing (#4102 follow-up \u2014 the\n * generic \"Forbidden\" toast didn't say what was denied).\n */\nconst FORBIDDEN_FEATURE_MESSAGE_KEYS: Record<string, string> = {\n 'wms.manage_warehouses': 'wms.errors.forbidden.manageWarehouses',\n 'wms.manage_zones': 'wms.errors.forbidden.manageZones',\n 'wms.manage_locations': 'wms.errors.forbidden.manageLocations',\n 'wms.manage_inventory': 'wms.errors.forbidden.manageInventory',\n 'wms.manage_reservations': 'wms.errors.forbidden.manageReservations',\n 'wms.adjust_inventory': 'wms.errors.forbidden.adjustInventory',\n 'wms.receive_inventory': 'wms.errors.forbidden.receiveInventory',\n 'wms.manage_asn': 'wms.errors.forbidden.manageAsn',\n 'wms.manage_putaway': 'wms.errors.forbidden.managePutaway',\n 'wms.cycle_count': 'wms.errors.forbidden.cycleCount',\n 'wms.import': 'wms.errors.forbidden.import',\n}\n\nconst FORBIDDEN_FEATURE_MESSAGE_FALLBACKS: Record<string, string> = {\n 'wms.manage_warehouses': \"You don't have permission to manage warehouses.\",\n 'wms.manage_zones': \"You don't have permission to manage warehouse zones.\",\n 'wms.manage_locations': \"You don't have permission to manage warehouse locations.\",\n 'wms.manage_inventory': \"You don't have permission to manage inventory profiles.\",\n 'wms.manage_reservations': \"You don't have permission to manage inventory reservations.\",\n 'wms.adjust_inventory': \"You don't have permission to adjust or move inventory.\",\n 'wms.receive_inventory': \"You don't have permission to receive inbound inventory.\",\n 'wms.manage_asn': \"You don't have permission to manage ASNs.\",\n 'wms.manage_putaway': \"You don't have permission to manage putaway tasks.\",\n 'wms.cycle_count': \"You don't have permission to run cycle counts.\",\n 'wms.import': \"You don't have permission to import inventory.\",\n}\n\n/**\n * Reads the 403 response's `requiredFeatures` (attached to the thrown error\n * by `raiseCrudError`) and, when the missing feature is a known WMS\n * permission, resolves a translated message naming it. Returns `null` when\n * the error isn't a recognized 403-with-features shape so callers can fall\n * back to the generic message.\n */\nfunction resolveForbiddenFeatureMessage(error: unknown, t?: TranslateFn): string | null {\n if (!error || typeof error !== 'object') return null\n const status = (error as Record<string, unknown>).status\n if (status !== 403) return null\n const requiredFeatures = (error as Record<string, unknown>).requiredFeatures\n if (!Array.isArray(requiredFeatures)) return null\n const knownFeature = requiredFeatures.find(\n (feature): feature is string => typeof feature === 'string' && feature in FORBIDDEN_FEATURE_MESSAGE_KEYS,\n )\n if (!knownFeature) return null\n const key = FORBIDDEN_FEATURE_MESSAGE_KEYS[knownFeature]\n const fallback = FORBIDDEN_FEATURE_MESSAGE_FALLBACKS[knownFeature]\n return t ? t(key, fallback) : fallback\n}\n\n/**\n * Surfaces a mutation failure as an error flash toast. Prefers a\n * feature-specific 403 message derived from the server's `requiredFeatures`\n * hint (#4102 follow-up), then the thrown error's own message (already\n * server-derived and translated by `raiseCrudError`/`useGuardedMutation`),\n * and falls back to a translated, dialog-specific message otherwise (#4103).\n */\nexport function flashMutationError(error: unknown, fallbackMessage: string, t?: TranslateFn): void {\n const forbiddenMessage = resolveForbiddenFeatureMessage(error, t)\n if (forbiddenMessage) {\n flash(forbiddenMessage, 'error')\n return\n }\n // apiFetch throws ForbiddenError with a bare \"Forbidden\" message when the\n // ACL body has no mappable feature \u2014 prefer the dialog-specific fallback\n // over flashing the untranslated server token.\n const rawMessage = error instanceof Error ? error.message.trim() : ''\n const isGenericForbidden = rawMessage.length === 0 || /^forbidden$/i.test(rawMessage)\n flash(isGenericForbidden ? fallbackMessage : rawMessage, 'error')\n}\n"],
5
+ "mappings": "AAAA,SAAS,aAAa;AAQtB,MAAM,iCAAyD;AAAA,EAC7D,yBAAyB;AAAA,EACzB,oBAAoB;AAAA,EACpB,wBAAwB;AAAA,EACxB,wBAAwB;AAAA,EACxB,2BAA2B;AAAA,EAC3B,wBAAwB;AAAA,EACxB,yBAAyB;AAAA,EACzB,kBAAkB;AAAA,EAClB,sBAAsB;AAAA,EACtB,mBAAmB;AAAA,EACnB,cAAc;AAChB;AAEA,MAAM,sCAA8D;AAAA,EAClE,yBAAyB;AAAA,EACzB,oBAAoB;AAAA,EACpB,wBAAwB;AAAA,EACxB,wBAAwB;AAAA,EACxB,2BAA2B;AAAA,EAC3B,wBAAwB;AAAA,EACxB,yBAAyB;AAAA,EACzB,kBAAkB;AAAA,EAClB,sBAAsB;AAAA,EACtB,mBAAmB;AAAA,EACnB,cAAc;AAChB;AASA,SAAS,+BAA+B,OAAgB,GAAgC;AACtF,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,SAAU,MAAkC;AAClD,MAAI,WAAW,IAAK,QAAO;AAC3B,QAAM,mBAAoB,MAAkC;AAC5D,MAAI,CAAC,MAAM,QAAQ,gBAAgB,EAAG,QAAO;AAC7C,QAAM,eAAe,iBAAiB;AAAA,IACpC,CAAC,YAA+B,OAAO,YAAY,YAAY,WAAW;AAAA,EAC5E;AACA,MAAI,CAAC,aAAc,QAAO;AAC1B,QAAM,MAAM,+BAA+B,YAAY;AACvD,QAAM,WAAW,oCAAoC,YAAY;AACjE,SAAO,IAAI,EAAE,KAAK,QAAQ,IAAI;AAChC;AASO,SAAS,mBAAmB,OAAgB,iBAAyB,GAAuB;AACjG,QAAM,mBAAmB,+BAA+B,OAAO,CAAC;AAChE,MAAI,kBAAkB;AACpB,UAAM,kBAAkB,OAAO;AAC/B;AAAA,EACF;AAIA,QAAM,aAAa,iBAAiB,QAAQ,MAAM,QAAQ,KAAK,IAAI;AACnE,QAAM,qBAAqB,WAAW,WAAW,KAAK,eAAe,KAAK,UAAU;AACpF,QAAM,qBAAqB,kBAAkB,YAAY,OAAO;AAClE;",
6
6
  "names": []
7
7
  }
@@ -118,6 +118,9 @@ function translateStableReasonCode(code, movementType, t) {
118
118
  if (code === "cycle_count") {
119
119
  return t("wms.backend.dashboard.activity.reasons.cycleCount", "Cycle count");
120
120
  }
121
+ if (code === "asn_receive") {
122
+ return t("wms.backend.dashboard.activity.reasons.asnReceive", "Receive (from ASN)");
123
+ }
121
124
  const type = (movementType ?? "").trim();
122
125
  if (type === "transfer" && code in MOVE_REASON_FALLBACKS) {
123
126
  return t(`wms.backend.inventory.move.reasons.${code}`, MOVE_REASON_FALLBACKS[code]);
@@ -146,6 +149,26 @@ function inventoryMovementReasonLabel(input, t) {
146
149
  }
147
150
  return reason;
148
151
  }
152
+ function formatInventoryMovementActivitySubtitle(input, t) {
153
+ const source = input.source?.trim() || null;
154
+ const effectiveReasonCode = input.reasonCode?.trim() || (source === "asn_receive" ? "asn_receive" : null);
155
+ const reasonLabel = inventoryMovementReasonLabel(
156
+ {
157
+ reasonCode: effectiveReasonCode,
158
+ reason: input.reason,
159
+ movementType: input.movementType
160
+ },
161
+ t
162
+ );
163
+ const referenceLabel = input.referenceLabel?.trim() || null;
164
+ if (reasonLabel && referenceLabel) return `${reasonLabel} \xB7 ${referenceLabel}`;
165
+ if (reasonLabel) return reasonLabel;
166
+ const referenceType = input.referenceType?.trim() || null;
167
+ if (!referenceType) return null;
168
+ const typeLabel = inventoryReferenceTypeLabel(referenceType, t);
169
+ if (referenceLabel) return `${typeLabel} \xB7 ${referenceLabel}`;
170
+ return typeLabel;
171
+ }
149
172
  function formatCatalogProductLabel(row) {
150
173
  const title = (row.product_title ?? "").trim();
151
174
  const sku = (row.product_sku ?? "").trim();
@@ -176,6 +199,7 @@ export {
176
199
  formatCatalogProductLabel,
177
200
  formatCatalogVariantLabel,
178
201
  formatInventoryDateTime,
202
+ formatInventoryMovementActivitySubtitle,
179
203
  formatInventoryQuantity,
180
204
  formatReservationSourceLabel,
181
205
  inventoryMovementReasonLabel,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/wms/lib/inventoryDisplayUi.ts"],
4
- "sourcesContent": ["import type { TranslateFn } from '@open-mercato/shared/lib/i18n/context'\nimport { parseInventoryQuantity } from './inventoryMutationUi'\n\nexport type InventoryDisplayTranslator = TranslateFn\n\nconst MOVEMENT_TYPE_FALLBACKS: Record<string, string> = {\n receipt: 'Receive',\n return_receive: 'Receive',\n adjust: 'Adjust',\n transfer: 'Move',\n pick: 'Allocate',\n pack: 'Allocate',\n cycle_count: 'Reconcile',\n putaway: 'Putaway',\n ship: 'Ship',\n}\n\nconst RESERVATION_SOURCE_TYPE_FALLBACKS: Record<string, string> = {\n order: 'Sales order',\n transfer: 'Transfer',\n manual: 'Manual',\n}\n\nconst REFERENCE_TYPE_FALLBACKS: Record<string, string> = {\n po: 'Purchase order',\n so: 'Sales order',\n transfer: 'Transfer',\n manual: 'Manual',\n qc: 'Quality control',\n rma: 'RMA',\n}\n\nconst RESERVATION_STATUS_FALLBACKS: Record<string, string> = {\n active: 'Active',\n released: 'Released',\n fulfilled: 'Fulfilled',\n}\n\nconst ROTATION_STRATEGY_FALLBACKS: Record<string, string> = {\n fifo: 'FIFO',\n lifo: 'LIFO',\n fefo: 'FEFO',\n}\n\nconst ADJUST_REASON_FALLBACKS: Record<string, string> = {\n damaged: 'Damaged',\n shrinkage: 'Shrinkage',\n found: 'Found stock',\n correction: 'Correction',\n other: 'Other',\n}\n\nconst MOVE_REASON_FALLBACKS: Record<string, string> = {\n transfer: 'Transfer',\n replenishment: 'Replenishment',\n consolidation: 'Consolidation',\n correction: 'Correction',\n other: 'Other',\n}\n\nconst RELEASE_REASON_FALLBACKS: Record<string, string> = {\n order_cancelled: 'Order cancelled',\n manual_release: 'Manual release',\n correction: 'Correction',\n other: 'Other',\n}\n\nconst STABLE_REASON_CODE_PATTERN = /^[a-z][a-z0-9_]*$/\n\nexport function createInventoryQuantityFormatter(locale: string): Intl.NumberFormat {\n return new Intl.NumberFormat(locale, { maximumFractionDigits: 2 })\n}\n\nexport function formatInventoryQuantity(\n value: string | number | null | undefined,\n formatter: Intl.NumberFormat,\n): string {\n return formatter.format(parseInventoryQuantity(value))\n}\n\nexport function createInventoryDateTimeFormatter(\n locale: string,\n options: Intl.DateTimeFormatOptions = {\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n },\n): Intl.DateTimeFormat {\n return new Intl.DateTimeFormat(locale, options)\n}\n\nexport function formatInventoryDateTime(\n value: string | null | undefined,\n formatter: Intl.DateTimeFormat,\n): string {\n if (!value?.trim()) return '\u2014'\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return '\u2014'\n return formatter.format(date)\n}\n\nexport function inventoryMovementTypeLabel(type: string, t: InventoryDisplayTranslator): string {\n const normalized = type.trim()\n if (!normalized) return '\u2014'\n return t(\n `wms.backend.dashboard.activity.types.${normalized}`,\n MOVEMENT_TYPE_FALLBACKS[normalized] ?? normalized,\n )\n}\n\nexport function inventoryReservationSourceTypeLabel(\n sourceType: string,\n t: InventoryDisplayTranslator,\n): string {\n const normalized = sourceType.trim()\n if (!normalized) return '\u2014'\n return t(\n `wms.backend.inventory.sourceTypes.${normalized}`,\n RESERVATION_SOURCE_TYPE_FALLBACKS[normalized] ?? normalized,\n )\n}\n\nexport function inventoryReferenceTypeLabel(\n referenceType: string,\n t: InventoryDisplayTranslator,\n): string {\n const normalized = referenceType.trim()\n if (!normalized) return '\u2014'\n return t(\n `wms.backend.inventory.referenceTypes.${normalized}`,\n REFERENCE_TYPE_FALLBACKS[normalized] ?? normalized,\n )\n}\n\nexport function inventoryReservationStatusLabel(status: string, t: InventoryDisplayTranslator): string {\n const normalized = status.trim()\n if (!normalized) return '\u2014'\n return t(\n `wms.backend.inventory.reservationStatuses.${normalized}`,\n RESERVATION_STATUS_FALLBACKS[normalized] ?? normalized,\n )\n}\n\nexport function inventoryRotationStrategyLabel(strategy: string, t: InventoryDisplayTranslator): string {\n const normalized = strategy.trim()\n if (!normalized) return '\u2014'\n return t(\n `wms.widgets.catalog.inventoryProfile.strategy.${normalized}`,\n ROTATION_STRATEGY_FALLBACKS[normalized] ?? normalized,\n )\n}\n\nfunction translateStableReasonCode(\n code: string,\n movementType: string | null | undefined,\n t: InventoryDisplayTranslator,\n): string | null {\n if (code === 'cycle_count') {\n return t('wms.backend.dashboard.activity.reasons.cycleCount', 'Cycle count')\n }\n\n const type = (movementType ?? '').trim()\n if (type === 'transfer' && code in MOVE_REASON_FALLBACKS) {\n return t(`wms.backend.inventory.move.reasons.${code}`, MOVE_REASON_FALLBACKS[code])\n }\n if ((type === 'adjust' || type === 'cycle_count') && code in ADJUST_REASON_FALLBACKS) {\n return t(`wms.backend.inventory.adjust.reasons.${code}`, ADJUST_REASON_FALLBACKS[code])\n }\n if (code in ADJUST_REASON_FALLBACKS) {\n return t(`wms.backend.inventory.adjust.reasons.${code}`, ADJUST_REASON_FALLBACKS[code])\n }\n if (code in MOVE_REASON_FALLBACKS) {\n return t(`wms.backend.inventory.move.reasons.${code}`, MOVE_REASON_FALLBACKS[code])\n }\n if (code in RELEASE_REASON_FALLBACKS) {\n return t(`wms.backend.inventory.release.reasons.${code}`, RELEASE_REASON_FALLBACKS[code])\n }\n return null\n}\n\n/**\n * Resolve a movement/activity reason for display. Prefers the stable\n * `reasonCode` (re-translated for the current locale) over a free-text\n * `reason` that may have been baked in a different language at write time.\n */\nexport function inventoryMovementReasonLabel(\n input: {\n reasonCode?: string | null\n reason?: string | null\n movementType?: string | null\n },\n t: InventoryDisplayTranslator,\n): string | null {\n const reasonCode = input.reasonCode?.trim() || null\n const reason = input.reason?.trim() || null\n const candidateCode =\n reasonCode ||\n (reason && STABLE_REASON_CODE_PATTERN.test(reason) ? reason : null)\n\n if (candidateCode) {\n const translated = translateStableReasonCode(candidateCode, input.movementType, t)\n if (translated) return translated\n }\n\n return reason\n}\n\nexport function formatCatalogProductLabel(row: {\n product_title?: string | null\n product_sku?: string | null\n catalog_product_id?: string | null\n}): string {\n const title = (row.product_title ?? '').trim()\n const sku = (row.product_sku ?? '').trim()\n if (title && sku) return `${title} (${sku})`\n if (title) return title\n if (sku) return sku\n return row.catalog_product_id?.trim() || '\u2014'\n}\n\nexport function formatCatalogVariantLabel(row: {\n variant_name?: string | null\n variant_sku?: string | null\n catalog_variant_id?: string | null\n}): string {\n const name = (row.variant_name ?? '').trim()\n const sku = (row.variant_sku ?? '').trim()\n if (name && sku) return `${name} (${sku})`\n if (name) return name\n if (sku) return sku\n return row.catalog_variant_id?.trim() || '\u2014'\n}\n\nexport function formatReservationSourceLabel(\n row: {\n source_type?: string | null\n source_id?: string | null\n source_label?: string | null\n },\n t: InventoryDisplayTranslator,\n): string {\n const sourceType = row.source_type?.trim()\n if (!sourceType) return '\u2014'\n const typeLabel = inventoryReservationSourceTypeLabel(sourceType, t)\n const sourceLabel = row.source_label?.trim()\n if (sourceLabel) return `${typeLabel} \u00B7 ${sourceLabel}`\n return typeLabel\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { TranslateFn } from '@open-mercato/shared/lib/i18n/context'\nimport { parseInventoryQuantity } from './inventoryMutationUi'\n\nexport type InventoryDisplayTranslator = TranslateFn\n\nconst MOVEMENT_TYPE_FALLBACKS: Record<string, string> = {\n receipt: 'Receive',\n return_receive: 'Receive',\n adjust: 'Adjust',\n transfer: 'Move',\n pick: 'Allocate',\n pack: 'Allocate',\n cycle_count: 'Reconcile',\n putaway: 'Putaway',\n ship: 'Ship',\n}\n\nconst RESERVATION_SOURCE_TYPE_FALLBACKS: Record<string, string> = {\n order: 'Sales order',\n transfer: 'Transfer',\n manual: 'Manual',\n}\n\nconst REFERENCE_TYPE_FALLBACKS: Record<string, string> = {\n po: 'Purchase order',\n so: 'Sales order',\n transfer: 'Transfer',\n manual: 'Manual',\n qc: 'Quality control',\n rma: 'RMA',\n}\n\nconst RESERVATION_STATUS_FALLBACKS: Record<string, string> = {\n active: 'Active',\n released: 'Released',\n fulfilled: 'Fulfilled',\n}\n\nconst ROTATION_STRATEGY_FALLBACKS: Record<string, string> = {\n fifo: 'FIFO',\n lifo: 'LIFO',\n fefo: 'FEFO',\n}\n\nconst ADJUST_REASON_FALLBACKS: Record<string, string> = {\n damaged: 'Damaged',\n shrinkage: 'Shrinkage',\n found: 'Found stock',\n correction: 'Correction',\n other: 'Other',\n}\n\nconst MOVE_REASON_FALLBACKS: Record<string, string> = {\n transfer: 'Transfer',\n replenishment: 'Replenishment',\n consolidation: 'Consolidation',\n correction: 'Correction',\n other: 'Other',\n}\n\nconst RELEASE_REASON_FALLBACKS: Record<string, string> = {\n order_cancelled: 'Order cancelled',\n manual_release: 'Manual release',\n correction: 'Correction',\n other: 'Other',\n}\n\nconst STABLE_REASON_CODE_PATTERN = /^[a-z][a-z0-9_]*$/\n\nexport function createInventoryQuantityFormatter(locale: string): Intl.NumberFormat {\n return new Intl.NumberFormat(locale, { maximumFractionDigits: 2 })\n}\n\nexport function formatInventoryQuantity(\n value: string | number | null | undefined,\n formatter: Intl.NumberFormat,\n): string {\n return formatter.format(parseInventoryQuantity(value))\n}\n\nexport function createInventoryDateTimeFormatter(\n locale: string,\n options: Intl.DateTimeFormatOptions = {\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n },\n): Intl.DateTimeFormat {\n return new Intl.DateTimeFormat(locale, options)\n}\n\nexport function formatInventoryDateTime(\n value: string | null | undefined,\n formatter: Intl.DateTimeFormat,\n): string {\n if (!value?.trim()) return '\u2014'\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return '\u2014'\n return formatter.format(date)\n}\n\nexport function inventoryMovementTypeLabel(type: string, t: InventoryDisplayTranslator): string {\n const normalized = type.trim()\n if (!normalized) return '\u2014'\n return t(\n `wms.backend.dashboard.activity.types.${normalized}`,\n MOVEMENT_TYPE_FALLBACKS[normalized] ?? normalized,\n )\n}\n\nexport function inventoryReservationSourceTypeLabel(\n sourceType: string,\n t: InventoryDisplayTranslator,\n): string {\n const normalized = sourceType.trim()\n if (!normalized) return '\u2014'\n return t(\n `wms.backend.inventory.sourceTypes.${normalized}`,\n RESERVATION_SOURCE_TYPE_FALLBACKS[normalized] ?? normalized,\n )\n}\n\nexport function inventoryReferenceTypeLabel(\n referenceType: string,\n t: InventoryDisplayTranslator,\n): string {\n const normalized = referenceType.trim()\n if (!normalized) return '\u2014'\n return t(\n `wms.backend.inventory.referenceTypes.${normalized}`,\n REFERENCE_TYPE_FALLBACKS[normalized] ?? normalized,\n )\n}\n\nexport function inventoryReservationStatusLabel(status: string, t: InventoryDisplayTranslator): string {\n const normalized = status.trim()\n if (!normalized) return '\u2014'\n return t(\n `wms.backend.inventory.reservationStatuses.${normalized}`,\n RESERVATION_STATUS_FALLBACKS[normalized] ?? normalized,\n )\n}\n\nexport function inventoryRotationStrategyLabel(strategy: string, t: InventoryDisplayTranslator): string {\n const normalized = strategy.trim()\n if (!normalized) return '\u2014'\n return t(\n `wms.widgets.catalog.inventoryProfile.strategy.${normalized}`,\n ROTATION_STRATEGY_FALLBACKS[normalized] ?? normalized,\n )\n}\n\nfunction translateStableReasonCode(\n code: string,\n movementType: string | null | undefined,\n t: InventoryDisplayTranslator,\n): string | null {\n if (code === 'cycle_count') {\n return t('wms.backend.dashboard.activity.reasons.cycleCount', 'Cycle count')\n }\n if (code === 'asn_receive') {\n return t('wms.backend.dashboard.activity.reasons.asnReceive', 'Receive (from ASN)')\n }\n\n const type = (movementType ?? '').trim()\n if (type === 'transfer' && code in MOVE_REASON_FALLBACKS) {\n return t(`wms.backend.inventory.move.reasons.${code}`, MOVE_REASON_FALLBACKS[code])\n }\n if ((type === 'adjust' || type === 'cycle_count') && code in ADJUST_REASON_FALLBACKS) {\n return t(`wms.backend.inventory.adjust.reasons.${code}`, ADJUST_REASON_FALLBACKS[code])\n }\n if (code in ADJUST_REASON_FALLBACKS) {\n return t(`wms.backend.inventory.adjust.reasons.${code}`, ADJUST_REASON_FALLBACKS[code])\n }\n if (code in MOVE_REASON_FALLBACKS) {\n return t(`wms.backend.inventory.move.reasons.${code}`, MOVE_REASON_FALLBACKS[code])\n }\n if (code in RELEASE_REASON_FALLBACKS) {\n return t(`wms.backend.inventory.release.reasons.${code}`, RELEASE_REASON_FALLBACKS[code])\n }\n return null\n}\n\n/**\n * Resolve a movement/activity reason for display. Prefers the stable\n * `reasonCode` (re-translated for the current locale) over a free-text\n * `reason` that may have been baked in a different language at write time.\n */\nexport function inventoryMovementReasonLabel(\n input: {\n reasonCode?: string | null\n reason?: string | null\n movementType?: string | null\n },\n t: InventoryDisplayTranslator,\n): string | null {\n const reasonCode = input.reasonCode?.trim() || null\n const reason = input.reason?.trim() || null\n const candidateCode =\n reasonCode ||\n (reason && STABLE_REASON_CODE_PATTERN.test(reason) ? reason : null)\n\n if (candidateCode) {\n const translated = translateStableReasonCode(candidateCode, input.movementType, t)\n if (translated) return translated\n }\n\n return reason\n}\n\n/**\n * Resolve a movement/activity subtitle. Prefers a stable reason label, then a\n * human reference label (never the opaque idempotency `referenceId` UUID).\n * Mirrors `formatReservationSourceLabel` so activity feeds stay readable.\n */\nexport function formatInventoryMovementActivitySubtitle(\n input: {\n reasonCode?: string | null\n reason?: string | null\n movementType?: string | null\n referenceType?: string | null\n referenceId?: string | null\n referenceLabel?: string | null\n /** Movement metadata `source` \u2014 used to recover ASN-receive labels for legacy rows. */\n source?: string | null\n },\n t: InventoryDisplayTranslator,\n): string | null {\n const source = input.source?.trim() || null\n const effectiveReasonCode =\n input.reasonCode?.trim() || (source === 'asn_receive' ? 'asn_receive' : null)\n const reasonLabel = inventoryMovementReasonLabel(\n {\n reasonCode: effectiveReasonCode,\n reason: input.reason,\n movementType: input.movementType,\n },\n t,\n )\n const referenceLabel = input.referenceLabel?.trim() || null\n\n if (reasonLabel && referenceLabel) return `${reasonLabel} \u00B7 ${referenceLabel}`\n if (reasonLabel) return reasonLabel\n\n const referenceType = input.referenceType?.trim() || null\n if (!referenceType) return null\n const typeLabel = inventoryReferenceTypeLabel(referenceType, t)\n if (referenceLabel) return `${typeLabel} \u00B7 ${referenceLabel}`\n // Never append raw referenceId UUIDs (ASN receive + manual receive both use\n // synthetic idempotency UUIDs that truncate poorly in the activity feed).\n return typeLabel\n}\n\nexport function formatCatalogProductLabel(row: {\n product_title?: string | null\n product_sku?: string | null\n catalog_product_id?: string | null\n}): string {\n const title = (row.product_title ?? '').trim()\n const sku = (row.product_sku ?? '').trim()\n if (title && sku) return `${title} (${sku})`\n if (title) return title\n if (sku) return sku\n return row.catalog_product_id?.trim() || '\u2014'\n}\n\nexport function formatCatalogVariantLabel(row: {\n variant_name?: string | null\n variant_sku?: string | null\n catalog_variant_id?: string | null\n}): string {\n const name = (row.variant_name ?? '').trim()\n const sku = (row.variant_sku ?? '').trim()\n if (name && sku) return `${name} (${sku})`\n if (name) return name\n if (sku) return sku\n return row.catalog_variant_id?.trim() || '\u2014'\n}\n\nexport function formatReservationSourceLabel(\n row: {\n source_type?: string | null\n source_id?: string | null\n source_label?: string | null\n },\n t: InventoryDisplayTranslator,\n): string {\n const sourceType = row.source_type?.trim()\n if (!sourceType) return '\u2014'\n const typeLabel = inventoryReservationSourceTypeLabel(sourceType, t)\n const sourceLabel = row.source_label?.trim()\n if (sourceLabel) return `${typeLabel} \u00B7 ${sourceLabel}`\n return typeLabel\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,3 +1,17 @@
1
+ const NON_RESERVABLE_LOCATION_TYPES = /* @__PURE__ */ new Set([
2
+ "staging",
3
+ "dock"
4
+ ]);
5
+ function isReservableLocationType(type) {
6
+ if (typeof type !== "string" || type.length === 0) return true;
7
+ return !NON_RESERVABLE_LOCATION_TYPES.has(type);
8
+ }
9
+ function isBalanceLocationReservable(balance) {
10
+ const location = balance.location;
11
+ if (!location || typeof location === "string") return true;
12
+ const type = typeof location.type === "string" ? location.type : null;
13
+ return isReservableLocationType(type);
14
+ }
1
15
  function isLotEligible(lot, now = /* @__PURE__ */ new Date()) {
2
16
  if (!lot) return true;
3
17
  if (lot.status !== "available") return false;
@@ -53,8 +67,11 @@ function evaluateLowStock(profile, availableQuantity) {
53
67
  };
54
68
  }
55
69
  export {
70
+ NON_RESERVABLE_LOCATION_TYPES,
56
71
  evaluateLowStock,
72
+ isBalanceLocationReservable,
57
73
  isLotEligible,
74
+ isReservableLocationType,
58
75
  resolveReservationStrategyFromProfile,
59
76
  sortBucketsForStrategy
60
77
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/wms/lib/inventoryPolicy.ts"],
4
- "sourcesContent": ["import type { InventoryLotStatus, InventoryStrategy } from '../data/entities'\n\ntype LotEligibilityInput = {\n status?: InventoryLotStatus | null\n expiresAt?: Date | null\n} | null | undefined\n\nexport function isLotEligible(lot: LotEligibilityInput, now: Date = new Date()): boolean {\n if (!lot) return true\n if (lot.status !== 'available') return false\n if (lot.expiresAt && lot.expiresAt.getTime() <= now.getTime()) return false\n return true\n}\n\ntype NumericLike = string | number | null | undefined\n\ntype StrategyProfile = {\n trackExpiration?: boolean | null\n defaultStrategy?: InventoryStrategy | null\n}\n\ntype LowStockProfile = {\n reorderPoint?: NumericLike\n safetyStock?: NumericLike\n}\n\nexport type InventoryStrategyBucket = {\n warehouseId: string\n locationId: string\n catalogVariantId: string\n createdAt: Date\n lotId?: string | null\n lotExpiresAt?: Date | null\n serialNumber?: string | null\n}\n\nfunction toNumber(value: NumericLike): number {\n if (typeof value === 'number') return value\n if (typeof value === 'string' && value.trim().length > 0) {\n return Number(value)\n }\n return 0\n}\n\nfunction buildBucketKey(bucket: InventoryStrategyBucket): string {\n return [\n bucket.warehouseId,\n bucket.locationId,\n bucket.catalogVariantId,\n bucket.lotId ?? '',\n bucket.serialNumber ?? '',\n ].join('::')\n}\n\nfunction resolveBucketSortValue(\n bucket: InventoryStrategyBucket,\n strategy: InventoryStrategy,\n receiptMap: Map<string, Date>,\n): number {\n const receiptAt = receiptMap.get(buildBucketKey(bucket))\n if (strategy === 'fefo') {\n return bucket.lotExpiresAt?.getTime() ?? Number.MAX_SAFE_INTEGER\n }\n return receiptAt?.getTime() ?? bucket.createdAt.getTime()\n}\n\nexport function sortBucketsForStrategy<T extends InventoryStrategyBucket>(\n buckets: T[],\n strategy: InventoryStrategy,\n receiptMap: Map<string, Date>,\n): T[] {\n return [...buckets].sort((left, right) => {\n const leftValue = resolveBucketSortValue(left, strategy, receiptMap)\n const rightValue = resolveBucketSortValue(right, strategy, receiptMap)\n if (leftValue === rightValue) {\n return left.createdAt.getTime() - right.createdAt.getTime()\n }\n return strategy === 'lifo' ? rightValue - leftValue : leftValue - rightValue\n })\n}\n\nexport function resolveReservationStrategyFromProfile(\n profile: StrategyProfile | null | undefined,\n requestedStrategy?: InventoryStrategy | null,\n): InventoryStrategy {\n if (profile?.trackExpiration) return 'fefo'\n return requestedStrategy ?? profile?.defaultStrategy ?? 'fifo'\n}\n\nexport function evaluateLowStock(\n profile: LowStockProfile,\n availableQuantity: number,\n):\n | {\n state: 'below_safety_stock' | 'below_reorder_point'\n reorderPoint: NumericLike\n safetyStock: NumericLike\n availableQuantity: string\n }\n | null {\n const reorderPoint = toNumber(profile.reorderPoint)\n if (availableQuantity > reorderPoint) return null\n\n const safetyStock = toNumber(profile.safetyStock)\n return {\n state: availableQuantity <= safetyStock ? 'below_safety_stock' : 'below_reorder_point',\n reorderPoint: profile.reorderPoint,\n safetyStock: profile.safetyStock,\n availableQuantity: String(availableQuantity),\n }\n}\n"],
5
- "mappings": "AAOO,SAAS,cAAc,KAA0B,MAAY,oBAAI,KAAK,GAAY;AACvF,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI,IAAI,WAAW,YAAa,QAAO;AACvC,MAAI,IAAI,aAAa,IAAI,UAAU,QAAQ,KAAK,IAAI,QAAQ,EAAG,QAAO;AACtE,SAAO;AACT;AAwBA,SAAS,SAAS,OAA4B;AAC5C,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,SAAS,GAAG;AACxD,WAAO,OAAO,KAAK;AAAA,EACrB;AACA,SAAO;AACT;AAEA,SAAS,eAAe,QAAyC;AAC/D,SAAO;AAAA,IACL,OAAO;AAAA,IACP,OAAO;AAAA,IACP,OAAO;AAAA,IACP,OAAO,SAAS;AAAA,IAChB,OAAO,gBAAgB;AAAA,EACzB,EAAE,KAAK,IAAI;AACb;AAEA,SAAS,uBACP,QACA,UACA,YACQ;AACR,QAAM,YAAY,WAAW,IAAI,eAAe,MAAM,CAAC;AACvD,MAAI,aAAa,QAAQ;AACvB,WAAO,OAAO,cAAc,QAAQ,KAAK,OAAO;AAAA,EAClD;AACA,SAAO,WAAW,QAAQ,KAAK,OAAO,UAAU,QAAQ;AAC1D;AAEO,SAAS,uBACd,SACA,UACA,YACK;AACL,SAAO,CAAC,GAAG,OAAO,EAAE,KAAK,CAAC,MAAM,UAAU;AACxC,UAAM,YAAY,uBAAuB,MAAM,UAAU,UAAU;AACnE,UAAM,aAAa,uBAAuB,OAAO,UAAU,UAAU;AACrE,QAAI,cAAc,YAAY;AAC5B,aAAO,KAAK,UAAU,QAAQ,IAAI,MAAM,UAAU,QAAQ;AAAA,IAC5D;AACA,WAAO,aAAa,SAAS,aAAa,YAAY,YAAY;AAAA,EACpE,CAAC;AACH;AAEO,SAAS,sCACd,SACA,mBACmB;AACnB,MAAI,SAAS,gBAAiB,QAAO;AACrC,SAAO,qBAAqB,SAAS,mBAAmB;AAC1D;AAEO,SAAS,iBACd,SACA,mBAQO;AACP,QAAM,eAAe,SAAS,QAAQ,YAAY;AAClD,MAAI,oBAAoB,aAAc,QAAO;AAE7C,QAAM,cAAc,SAAS,QAAQ,WAAW;AAChD,SAAO;AAAA,IACL,OAAO,qBAAqB,cAAc,uBAAuB;AAAA,IACjE,cAAc,QAAQ;AAAA,IACtB,aAAa,QAAQ;AAAA,IACrB,mBAAmB,OAAO,iBAAiB;AAAA,EAC7C;AACF;",
4
+ "sourcesContent": ["import type {\n InventoryLotStatus,\n InventoryStrategy,\n WarehouseLocationType,\n} from '../data/entities'\n\ntype LotEligibilityInput = {\n status?: InventoryLotStatus | null\n expiresAt?: Date | null\n} | null | undefined\n\n/** Staging/dock hold inbound stock; Phase 2 must not treat them as pick/reserve candidates. */\nexport const NON_RESERVABLE_LOCATION_TYPES: ReadonlySet<WarehouseLocationType> = new Set([\n 'staging',\n 'dock',\n])\n\nexport function isReservableLocationType(type: string | null | undefined): boolean {\n if (typeof type !== 'string' || type.length === 0) return true\n return !NON_RESERVABLE_LOCATION_TYPES.has(type as WarehouseLocationType)\n}\n\n/**\n * Returns false when the balance sits on staging/dock. Unpopulated location FKs\n * (string id only) stay eligible so callers that omit `populate: ['location']`\n * do not accidentally empty the candidate set.\n */\nexport function isBalanceLocationReservable(balance: { location?: unknown }): boolean {\n const location = balance.location\n if (!location || typeof location === 'string') return true\n const type =\n typeof (location as { type?: unknown }).type === 'string'\n ? (location as { type: string }).type\n : null\n return isReservableLocationType(type)\n}\n\nexport function isLotEligible(lot: LotEligibilityInput, now: Date = new Date()): boolean {\n if (!lot) return true\n if (lot.status !== 'available') return false\n if (lot.expiresAt && lot.expiresAt.getTime() <= now.getTime()) return false\n return true\n}\n\ntype NumericLike = string | number | null | undefined\n\ntype StrategyProfile = {\n trackExpiration?: boolean | null\n defaultStrategy?: InventoryStrategy | null\n}\n\ntype LowStockProfile = {\n reorderPoint?: NumericLike\n safetyStock?: NumericLike\n}\n\nexport type InventoryStrategyBucket = {\n warehouseId: string\n locationId: string\n catalogVariantId: string\n createdAt: Date\n lotId?: string | null\n lotExpiresAt?: Date | null\n serialNumber?: string | null\n}\n\nfunction toNumber(value: NumericLike): number {\n if (typeof value === 'number') return value\n if (typeof value === 'string' && value.trim().length > 0) {\n return Number(value)\n }\n return 0\n}\n\nfunction buildBucketKey(bucket: InventoryStrategyBucket): string {\n return [\n bucket.warehouseId,\n bucket.locationId,\n bucket.catalogVariantId,\n bucket.lotId ?? '',\n bucket.serialNumber ?? '',\n ].join('::')\n}\n\nfunction resolveBucketSortValue(\n bucket: InventoryStrategyBucket,\n strategy: InventoryStrategy,\n receiptMap: Map<string, Date>,\n): number {\n const receiptAt = receiptMap.get(buildBucketKey(bucket))\n if (strategy === 'fefo') {\n return bucket.lotExpiresAt?.getTime() ?? Number.MAX_SAFE_INTEGER\n }\n return receiptAt?.getTime() ?? bucket.createdAt.getTime()\n}\n\nexport function sortBucketsForStrategy<T extends InventoryStrategyBucket>(\n buckets: T[],\n strategy: InventoryStrategy,\n receiptMap: Map<string, Date>,\n): T[] {\n return [...buckets].sort((left, right) => {\n const leftValue = resolveBucketSortValue(left, strategy, receiptMap)\n const rightValue = resolveBucketSortValue(right, strategy, receiptMap)\n if (leftValue === rightValue) {\n return left.createdAt.getTime() - right.createdAt.getTime()\n }\n return strategy === 'lifo' ? rightValue - leftValue : leftValue - rightValue\n })\n}\n\nexport function resolveReservationStrategyFromProfile(\n profile: StrategyProfile | null | undefined,\n requestedStrategy?: InventoryStrategy | null,\n): InventoryStrategy {\n if (profile?.trackExpiration) return 'fefo'\n return requestedStrategy ?? profile?.defaultStrategy ?? 'fifo'\n}\n\nexport function evaluateLowStock(\n profile: LowStockProfile,\n availableQuantity: number,\n):\n | {\n state: 'below_safety_stock' | 'below_reorder_point'\n reorderPoint: NumericLike\n safetyStock: NumericLike\n availableQuantity: string\n }\n | null {\n const reorderPoint = toNumber(profile.reorderPoint)\n if (availableQuantity > reorderPoint) return null\n\n const safetyStock = toNumber(profile.safetyStock)\n return {\n state: availableQuantity <= safetyStock ? 'below_safety_stock' : 'below_reorder_point',\n reorderPoint: profile.reorderPoint,\n safetyStock: profile.safetyStock,\n availableQuantity: String(availableQuantity),\n }\n}\n"],
5
+ "mappings": "AAYO,MAAM,gCAAoE,oBAAI,IAAI;AAAA,EACvF;AAAA,EACA;AACF,CAAC;AAEM,SAAS,yBAAyB,MAA0C;AACjF,MAAI,OAAO,SAAS,YAAY,KAAK,WAAW,EAAG,QAAO;AAC1D,SAAO,CAAC,8BAA8B,IAAI,IAA6B;AACzE;AAOO,SAAS,4BAA4B,SAA0C;AACpF,QAAM,WAAW,QAAQ;AACzB,MAAI,CAAC,YAAY,OAAO,aAAa,SAAU,QAAO;AACtD,QAAM,OACJ,OAAQ,SAAgC,SAAS,WAC5C,SAA8B,OAC/B;AACN,SAAO,yBAAyB,IAAI;AACtC;AAEO,SAAS,cAAc,KAA0B,MAAY,oBAAI,KAAK,GAAY;AACvF,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI,IAAI,WAAW,YAAa,QAAO;AACvC,MAAI,IAAI,aAAa,IAAI,UAAU,QAAQ,KAAK,IAAI,QAAQ,EAAG,QAAO;AACtE,SAAO;AACT;AAwBA,SAAS,SAAS,OAA4B;AAC5C,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,SAAS,GAAG;AACxD,WAAO,OAAO,KAAK;AAAA,EACrB;AACA,SAAO;AACT;AAEA,SAAS,eAAe,QAAyC;AAC/D,SAAO;AAAA,IACL,OAAO;AAAA,IACP,OAAO;AAAA,IACP,OAAO;AAAA,IACP,OAAO,SAAS;AAAA,IAChB,OAAO,gBAAgB;AAAA,EACzB,EAAE,KAAK,IAAI;AACb;AAEA,SAAS,uBACP,QACA,UACA,YACQ;AACR,QAAM,YAAY,WAAW,IAAI,eAAe,MAAM,CAAC;AACvD,MAAI,aAAa,QAAQ;AACvB,WAAO,OAAO,cAAc,QAAQ,KAAK,OAAO;AAAA,EAClD;AACA,SAAO,WAAW,QAAQ,KAAK,OAAO,UAAU,QAAQ;AAC1D;AAEO,SAAS,uBACd,SACA,UACA,YACK;AACL,SAAO,CAAC,GAAG,OAAO,EAAE,KAAK,CAAC,MAAM,UAAU;AACxC,UAAM,YAAY,uBAAuB,MAAM,UAAU,UAAU;AACnE,UAAM,aAAa,uBAAuB,OAAO,UAAU,UAAU;AACrE,QAAI,cAAc,YAAY;AAC5B,aAAO,KAAK,UAAU,QAAQ,IAAI,MAAM,UAAU,QAAQ;AAAA,IAC5D;AACA,WAAO,aAAa,SAAS,aAAa,YAAY,YAAY;AAAA,EACpE,CAAC;AACH;AAEO,SAAS,sCACd,SACA,mBACmB;AACnB,MAAI,SAAS,gBAAiB,QAAO;AACrC,SAAO,qBAAqB,SAAS,mBAAmB;AAC1D;AAEO,SAAS,iBACd,SACA,mBAQO;AACP,QAAM,eAAe,SAAS,QAAQ,YAAY;AAClD,MAAI,oBAAoB,aAAc,QAAO;AAE7C,QAAM,cAAc,SAAS,QAAQ,WAAW;AAChD,SAAO;AAAA,IACL,OAAO,qBAAqB,cAAc,uBAAuB;AAAA,IACjE,cAAc,QAAQ;AAAA,IACtB,aAAa,QAAQ;AAAA,IACrB,mBAAmB,OAAO,iBAAiB;AAAA,EAC7C;AACF;",
6
6
  "names": []
7
7
  }