@open-mercato/core 0.6.7-develop.6669.1.40b669666b → 0.6.7-develop.6671.1.4b925c5096

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 (473) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/dist/generated/entities/inventory_balance/index.js +21 -0
  3. package/dist/generated/entities/inventory_balance/index.js.map +7 -0
  4. package/dist/generated/entities/inventory_lot/index.js +19 -0
  5. package/dist/generated/entities/inventory_lot/index.js.map +7 -0
  6. package/dist/generated/entities/inventory_movement/index.js +35 -0
  7. package/dist/generated/entities/inventory_movement/index.js.map +7 -0
  8. package/dist/generated/entities/inventory_reservation/index.js +23 -0
  9. package/dist/generated/entities/inventory_reservation/index.js.map +7 -0
  10. package/dist/generated/entities/product_inventory_profile/index.js +21 -0
  11. package/dist/generated/entities/product_inventory_profile/index.js.map +7 -0
  12. package/dist/generated/entities/sales_order_warehouse_assignment/index.js +11 -0
  13. package/dist/generated/entities/sales_order_warehouse_assignment/index.js.map +7 -0
  14. package/dist/generated/entities/warehouse/index.js +25 -0
  15. package/dist/generated/entities/warehouse/index.js.map +7 -0
  16. package/dist/generated/entities/warehouse_location/index.js +21 -0
  17. package/dist/generated/entities/warehouse_location/index.js.map +7 -0
  18. package/dist/generated/entities/warehouse_zone/index.js +11 -0
  19. package/dist/generated/entities/warehouse_zone/index.js.map +7 -0
  20. package/dist/generated/entities.ids.generated.js +12 -0
  21. package/dist/generated/entities.ids.generated.js.map +2 -2
  22. package/dist/generated/entity-fields-registry.js +98 -0
  23. package/dist/generated/entity-fields-registry.js.map +2 -2
  24. package/dist/modules/auth/backend/roles/page.js +12 -4
  25. package/dist/modules/auth/backend/roles/page.js.map +2 -2
  26. package/dist/modules/auth/lib/backendChrome.js +1 -0
  27. package/dist/modules/auth/lib/backendChrome.js.map +2 -2
  28. package/dist/modules/auth/services/rbacService.js +16 -17
  29. package/dist/modules/auth/services/rbacService.js.map +2 -2
  30. package/dist/modules/catalog/api/products/route.js +3 -0
  31. package/dist/modules/catalog/api/products/route.js.map +2 -2
  32. package/dist/modules/catalog/api/variants/route.js +3 -0
  33. package/dist/modules/catalog/api/variants/route.js.map +2 -2
  34. package/dist/modules/query_index/lib/engine.js +6 -6
  35. package/dist/modules/query_index/lib/engine.js.map +2 -2
  36. package/dist/modules/sales/api/documents/factory.js +1 -0
  37. package/dist/modules/sales/api/documents/factory.js.map +2 -2
  38. package/dist/modules/sales/commands/documents.js +53 -0
  39. package/dist/modules/sales/commands/documents.js.map +2 -2
  40. package/dist/modules/sales/components/documents/ItemsSection.js +51 -2
  41. package/dist/modules/sales/components/documents/ItemsSection.js.map +2 -2
  42. package/dist/modules/sales/events.js +2 -0
  43. package/dist/modules/sales/events.js.map +2 -2
  44. package/dist/modules/sales/widgets/injection/order-items-context.js +11 -0
  45. package/dist/modules/sales/widgets/injection/order-items-context.js.map +7 -0
  46. package/dist/modules/wms/acl.js +18 -0
  47. package/dist/modules/wms/acl.js.map +7 -0
  48. package/dist/modules/wms/analytics.js +11 -0
  49. package/dist/modules/wms/analytics.js.map +7 -0
  50. package/dist/modules/wms/api/dashboard/operational/route.js +80 -0
  51. package/dist/modules/wms/api/dashboard/operational/route.js.map +7 -0
  52. package/dist/modules/wms/api/interceptors.js +90 -0
  53. package/dist/modules/wms/api/interceptors.js.map +7 -0
  54. package/dist/modules/wms/api/inventory/adjust/route.js +51 -0
  55. package/dist/modules/wms/api/inventory/adjust/route.js.map +7 -0
  56. package/dist/modules/wms/api/inventory/allocate/route.js +51 -0
  57. package/dist/modules/wms/api/inventory/allocate/route.js.map +7 -0
  58. package/dist/modules/wms/api/inventory/balances/route.js +172 -0
  59. package/dist/modules/wms/api/inventory/balances/route.js.map +7 -0
  60. package/dist/modules/wms/api/inventory/cycle-count/route.js +52 -0
  61. package/dist/modules/wms/api/inventory/cycle-count/route.js.map +7 -0
  62. package/dist/modules/wms/api/inventory/helpers.js +113 -0
  63. package/dist/modules/wms/api/inventory/helpers.js.map +7 -0
  64. package/dist/modules/wms/api/inventory/import/apply/route.js +46 -0
  65. package/dist/modules/wms/api/inventory/import/apply/route.js.map +7 -0
  66. package/dist/modules/wms/api/inventory/import/helpers.js +195 -0
  67. package/dist/modules/wms/api/inventory/import/helpers.js.map +7 -0
  68. package/dist/modules/wms/api/inventory/import/template/route.js +32 -0
  69. package/dist/modules/wms/api/inventory/import/template/route.js.map +7 -0
  70. package/dist/modules/wms/api/inventory/import/validate/route.js +54 -0
  71. package/dist/modules/wms/api/inventory/import/validate/route.js.map +7 -0
  72. package/dist/modules/wms/api/inventory/move/route.js +51 -0
  73. package/dist/modules/wms/api/inventory/move/route.js.map +7 -0
  74. package/dist/modules/wms/api/inventory/movements/route.js +164 -0
  75. package/dist/modules/wms/api/inventory/movements/route.js.map +7 -0
  76. package/dist/modules/wms/api/inventory/receive/route.js +50 -0
  77. package/dist/modules/wms/api/inventory/receive/route.js.map +7 -0
  78. package/dist/modules/wms/api/inventory/release/route.js +44 -0
  79. package/dist/modules/wms/api/inventory/release/route.js.map +7 -0
  80. package/dist/modules/wms/api/inventory/reservations/route.js +128 -0
  81. package/dist/modules/wms/api/inventory/reservations/route.js.map +7 -0
  82. package/dist/modules/wms/api/inventory/reserve/route.js +59 -0
  83. package/dist/modules/wms/api/inventory/reserve/route.js.map +7 -0
  84. package/dist/modules/wms/api/inventory-profiles/route.js +178 -0
  85. package/dist/modules/wms/api/inventory-profiles/route.js.map +7 -0
  86. package/dist/modules/wms/api/listEnrichers.js +181 -0
  87. package/dist/modules/wms/api/listEnrichers.js.map +7 -0
  88. package/dist/modules/wms/api/listSearch.js +77 -0
  89. package/dist/modules/wms/api/listSearch.js.map +7 -0
  90. package/dist/modules/wms/api/locations/route.js +172 -0
  91. package/dist/modules/wms/api/locations/route.js.map +7 -0
  92. package/dist/modules/wms/api/lots/route.js +204 -0
  93. package/dist/modules/wms/api/lots/route.js.map +7 -0
  94. package/dist/modules/wms/api/openapi.js +27 -0
  95. package/dist/modules/wms/api/openapi.js.map +7 -0
  96. package/dist/modules/wms/api/sales-orders/[salesOrderId]/re-run-reservation/route.js +54 -0
  97. package/dist/modules/wms/api/sales-orders/[salesOrderId]/re-run-reservation/route.js.map +7 -0
  98. package/dist/modules/wms/api/sales-orders/[salesOrderId]/warehouse-assignment/route.js +279 -0
  99. package/dist/modules/wms/api/sales-orders/[salesOrderId]/warehouse-assignment/route.js.map +7 -0
  100. package/dist/modules/wms/api/sales-orders/assign-warehouse/route.js +56 -0
  101. package/dist/modules/wms/api/sales-orders/assign-warehouse/route.js.map +7 -0
  102. package/dist/modules/wms/api/sales-orders/unassign-warehouse/route.js +43 -0
  103. package/dist/modules/wms/api/sales-orders/unassign-warehouse/route.js.map +7 -0
  104. package/dist/modules/wms/api/warehouses/route.js +181 -0
  105. package/dist/modules/wms/api/warehouses/route.js.map +7 -0
  106. package/dist/modules/wms/api/zones/route.js +159 -0
  107. package/dist/modules/wms/api/zones/route.js.map +7 -0
  108. package/dist/modules/wms/backend/config/wms/page.js +9 -0
  109. package/dist/modules/wms/backend/config/wms/page.js.map +7 -0
  110. package/dist/modules/wms/backend/config/wms/page.meta.js +18 -0
  111. package/dist/modules/wms/backend/config/wms/page.meta.js.map +7 -0
  112. package/dist/modules/wms/backend/page.js +9 -0
  113. package/dist/modules/wms/backend/page.js.map +7 -0
  114. package/dist/modules/wms/backend/page.meta.js +16 -0
  115. package/dist/modules/wms/backend/page.meta.js.map +7 -0
  116. package/dist/modules/wms/backend/wms/inventory/page.js +9 -0
  117. package/dist/modules/wms/backend/wms/inventory/page.js.map +7 -0
  118. package/dist/modules/wms/backend/wms/inventory/page.meta.js +18 -0
  119. package/dist/modules/wms/backend/wms/inventory/page.meta.js.map +7 -0
  120. package/dist/modules/wms/backend/wms/location/[id]/page.js +9 -0
  121. package/dist/modules/wms/backend/wms/location/[id]/page.js.map +7 -0
  122. package/dist/modules/wms/backend/wms/location/[id]/page.meta.js +18 -0
  123. package/dist/modules/wms/backend/wms/location/[id]/page.meta.js.map +7 -0
  124. package/dist/modules/wms/backend/wms/locations/page.js +10 -0
  125. package/dist/modules/wms/backend/wms/locations/page.js.map +7 -0
  126. package/dist/modules/wms/backend/wms/locations/page.meta.js +18 -0
  127. package/dist/modules/wms/backend/wms/locations/page.meta.js.map +7 -0
  128. package/dist/modules/wms/backend/wms/lot/[id]/page.js +9 -0
  129. package/dist/modules/wms/backend/wms/lot/[id]/page.js.map +7 -0
  130. package/dist/modules/wms/backend/wms/lot/[id]/page.meta.js +18 -0
  131. package/dist/modules/wms/backend/wms/lot/[id]/page.meta.js.map +7 -0
  132. package/dist/modules/wms/backend/wms/lots/page.js +9 -0
  133. package/dist/modules/wms/backend/wms/lots/page.js.map +7 -0
  134. package/dist/modules/wms/backend/wms/lots/page.meta.js +18 -0
  135. package/dist/modules/wms/backend/wms/lots/page.meta.js.map +7 -0
  136. package/dist/modules/wms/backend/wms/movements/page.js +22 -0
  137. package/dist/modules/wms/backend/wms/movements/page.js.map +7 -0
  138. package/dist/modules/wms/backend/wms/movements/page.meta.js +18 -0
  139. package/dist/modules/wms/backend/wms/movements/page.meta.js.map +7 -0
  140. package/dist/modules/wms/backend/wms/reservations/page.js +23 -0
  141. package/dist/modules/wms/backend/wms/reservations/page.js.map +7 -0
  142. package/dist/modules/wms/backend/wms/reservations/page.meta.js +18 -0
  143. package/dist/modules/wms/backend/wms/reservations/page.meta.js.map +7 -0
  144. package/dist/modules/wms/backend/wms/sku/[id]/page.js +9 -0
  145. package/dist/modules/wms/backend/wms/sku/[id]/page.js.map +7 -0
  146. package/dist/modules/wms/backend/wms/sku/[id]/page.meta.js +18 -0
  147. package/dist/modules/wms/backend/wms/sku/[id]/page.meta.js.map +7 -0
  148. package/dist/modules/wms/backend/wms/warehouses/page.js +10 -0
  149. package/dist/modules/wms/backend/wms/warehouses/page.js.map +7 -0
  150. package/dist/modules/wms/backend/wms/warehouses/page.meta.js +18 -0
  151. package/dist/modules/wms/backend/wms/warehouses/page.meta.js.map +7 -0
  152. package/dist/modules/wms/backend/wms/zones/page.js +10 -0
  153. package/dist/modules/wms/backend/wms/zones/page.js.map +7 -0
  154. package/dist/modules/wms/backend/wms/zones/page.meta.js +18 -0
  155. package/dist/modules/wms/backend/wms/zones/page.meta.js.map +7 -0
  156. package/dist/modules/wms/ce.js +80 -0
  157. package/dist/modules/wms/ce.js.map +7 -0
  158. package/dist/modules/wms/cli.js +83 -0
  159. package/dist/modules/wms/cli.js.map +7 -0
  160. package/dist/modules/wms/commands/configuration.js +1519 -0
  161. package/dist/modules/wms/commands/configuration.js.map +7 -0
  162. package/dist/modules/wms/commands/index.js +4 -0
  163. package/dist/modules/wms/commands/index.js.map +7 -0
  164. package/dist/modules/wms/commands/inventory-actions.js +1539 -0
  165. package/dist/modules/wms/commands/inventory-actions.js.map +7 -0
  166. package/dist/modules/wms/commands/sales-order-assignment.js +277 -0
  167. package/dist/modules/wms/commands/sales-order-assignment.js.map +7 -0
  168. package/dist/modules/wms/commands/shared.js +82 -0
  169. package/dist/modules/wms/commands/shared.js.map +7 -0
  170. package/dist/modules/wms/components/backend/AdjustInventoryDialog.js +755 -0
  171. package/dist/modules/wms/components/backend/AdjustInventoryDialog.js.map +7 -0
  172. package/dist/modules/wms/components/backend/ChangeLotStatusDialog.js +269 -0
  173. package/dist/modules/wms/components/backend/ChangeLotStatusDialog.js.map +7 -0
  174. package/dist/modules/wms/components/backend/CycleCountWizardDialog.js +1783 -0
  175. package/dist/modules/wms/components/backend/CycleCountWizardDialog.js.map +7 -0
  176. package/dist/modules/wms/components/backend/ImportInventoryDialog.js +766 -0
  177. package/dist/modules/wms/components/backend/ImportInventoryDialog.js.map +7 -0
  178. package/dist/modules/wms/components/backend/InventoryOperationsSection.js +119 -0
  179. package/dist/modules/wms/components/backend/InventoryOperationsSection.js.map +7 -0
  180. package/dist/modules/wms/components/backend/LocationEditDialog.js +140 -0
  181. package/dist/modules/wms/components/backend/LocationEditDialog.js.map +7 -0
  182. package/dist/modules/wms/components/backend/MoveInventoryDialog.js +791 -0
  183. package/dist/modules/wms/components/backend/MoveInventoryDialog.js.map +7 -0
  184. package/dist/modules/wms/components/backend/ReceiveInventoryDialog.js +656 -0
  185. package/dist/modules/wms/components/backend/ReceiveInventoryDialog.js.map +7 -0
  186. package/dist/modules/wms/components/backend/ReleaseReservationDialog.js +344 -0
  187. package/dist/modules/wms/components/backend/ReleaseReservationDialog.js.map +7 -0
  188. package/dist/modules/wms/components/backend/ReserveInventoryDialog.js +475 -0
  189. package/dist/modules/wms/components/backend/ReserveInventoryDialog.js.map +7 -0
  190. package/dist/modules/wms/components/backend/WmsConfigurationPage.js +1112 -0
  191. package/dist/modules/wms/components/backend/WmsConfigurationPage.js.map +7 -0
  192. package/dist/modules/wms/components/backend/WmsInventoryConsolePage.js +908 -0
  193. package/dist/modules/wms/components/backend/WmsInventoryConsolePage.js.map +7 -0
  194. package/dist/modules/wms/components/backend/WmsLocationDetailPage.js +1015 -0
  195. package/dist/modules/wms/components/backend/WmsLocationDetailPage.js.map +7 -0
  196. package/dist/modules/wms/components/backend/WmsLotDetailPage.js +1049 -0
  197. package/dist/modules/wms/components/backend/WmsLotDetailPage.js.map +7 -0
  198. package/dist/modules/wms/components/backend/WmsLotsListPage.js +297 -0
  199. package/dist/modules/wms/components/backend/WmsLotsListPage.js.map +7 -0
  200. package/dist/modules/wms/components/backend/WmsOperationalDashboardPage.js +757 -0
  201. package/dist/modules/wms/components/backend/WmsOperationalDashboardPage.js.map +7 -0
  202. package/dist/modules/wms/components/backend/WmsSkuDetailPage.js +975 -0
  203. package/dist/modules/wms/components/backend/WmsSkuDetailPage.js.map +7 -0
  204. package/dist/modules/wms/components/backend/inventoryMutationLoaders.js +710 -0
  205. package/dist/modules/wms/components/backend/inventoryMutationLoaders.js.map +7 -0
  206. package/dist/modules/wms/components/backend/useWmsInventoryMutationAccess.js +79 -0
  207. package/dist/modules/wms/components/backend/useWmsInventoryMutationAccess.js.map +7 -0
  208. package/dist/modules/wms/components/backend/useWmsInventoryScopeFromSearchParams.js +24 -0
  209. package/dist/modules/wms/components/backend/useWmsInventoryScopeFromSearchParams.js.map +7 -0
  210. package/dist/modules/wms/components/backend/wmsLookupLoaders.js +55 -0
  211. package/dist/modules/wms/components/backend/wmsLookupLoaders.js.map +7 -0
  212. package/dist/modules/wms/data/enrichers.js +501 -0
  213. package/dist/modules/wms/data/enrichers.js.map +7 -0
  214. package/dist/modules/wms/data/entities.js +467 -0
  215. package/dist/modules/wms/data/entities.js.map +7 -0
  216. package/dist/modules/wms/data/validators.js +399 -0
  217. package/dist/modules/wms/data/validators.js.map +7 -0
  218. package/dist/modules/wms/di.js +27 -0
  219. package/dist/modules/wms/di.js.map +7 -0
  220. package/dist/modules/wms/events.js +42 -0
  221. package/dist/modules/wms/events.js.map +7 -0
  222. package/dist/modules/wms/index.js +17 -0
  223. package/dist/modules/wms/index.js.map +7 -0
  224. package/dist/modules/wms/lib/catalogInventoryProfileIntent.js +54 -0
  225. package/dist/modules/wms/lib/catalogInventoryProfileIntent.js.map +7 -0
  226. package/dist/modules/wms/lib/catalogInventoryProfileSync.js +144 -0
  227. package/dist/modules/wms/lib/catalogInventoryProfileSync.js.map +7 -0
  228. package/dist/modules/wms/lib/expiry.js +29 -0
  229. package/dist/modules/wms/lib/expiry.js.map +7 -0
  230. package/dist/modules/wms/lib/flashMutationError.js +51 -0
  231. package/dist/modules/wms/lib/flashMutationError.js.map +7 -0
  232. package/dist/modules/wms/lib/inventoryDisplayUi.js +188 -0
  233. package/dist/modules/wms/lib/inventoryDisplayUi.js.map +7 -0
  234. package/dist/modules/wms/lib/inventoryIdempotency.js +37 -0
  235. package/dist/modules/wms/lib/inventoryIdempotency.js.map +7 -0
  236. package/dist/modules/wms/lib/inventoryImportCsv.js +173 -0
  237. package/dist/modules/wms/lib/inventoryImportCsv.js.map +7 -0
  238. package/dist/modules/wms/lib/inventoryImportService.js +468 -0
  239. package/dist/modules/wms/lib/inventoryImportService.js.map +7 -0
  240. package/dist/modules/wms/lib/inventoryMutationUi.js +25 -0
  241. package/dist/modules/wms/lib/inventoryMutationUi.js.map +7 -0
  242. package/dist/modules/wms/lib/inventoryPolicy.js +61 -0
  243. package/dist/modules/wms/lib/inventoryPolicy.js.map +7 -0
  244. package/dist/modules/wms/lib/inventoryReconciliation.js +193 -0
  245. package/dist/modules/wms/lib/inventoryReconciliation.js.map +7 -0
  246. package/dist/modules/wms/lib/inventoryTrackingValidation.js +13 -0
  247. package/dist/modules/wms/lib/inventoryTrackingValidation.js.map +7 -0
  248. package/dist/modules/wms/lib/loadOperationalDashboard.js +580 -0
  249. package/dist/modules/wms/lib/loadOperationalDashboard.js.map +7 -0
  250. package/dist/modules/wms/lib/lowStockBalanceFilter.js +49 -0
  251. package/dist/modules/wms/lib/lowStockBalanceFilter.js.map +7 -0
  252. package/dist/modules/wms/lib/primaryWarehousePolicy.js +32 -0
  253. package/dist/modules/wms/lib/primaryWarehousePolicy.js.map +7 -0
  254. package/dist/modules/wms/lib/roleFeatures.js +30 -0
  255. package/dist/modules/wms/lib/roleFeatures.js.map +7 -0
  256. package/dist/modules/wms/lib/salesOrderInventoryAutomation.js +291 -0
  257. package/dist/modules/wms/lib/salesOrderInventoryAutomation.js.map +7 -0
  258. package/dist/modules/wms/lib/salesOrderWarehouseAssignment.js +54 -0
  259. package/dist/modules/wms/lib/salesOrderWarehouseAssignment.js.map +7 -0
  260. package/dist/modules/wms/lib/wmsIntegrationToggles.js +71 -0
  261. package/dist/modules/wms/lib/wmsIntegrationToggles.js.map +7 -0
  262. package/dist/modules/wms/migrations/Migration20260428110546.js +78 -0
  263. package/dist/modules/wms/migrations/Migration20260428110546.js.map +7 -0
  264. package/dist/modules/wms/migrations/Migration20260527120000.js +15 -0
  265. package/dist/modules/wms/migrations/Migration20260527120000.js.map +7 -0
  266. package/dist/modules/wms/migrations/Migration20260527140000.js +37 -0
  267. package/dist/modules/wms/migrations/Migration20260527140000.js.map +7 -0
  268. package/dist/modules/wms/migrations/Migration20260613120000.js +23 -0
  269. package/dist/modules/wms/migrations/Migration20260613120000.js.map +7 -0
  270. package/dist/modules/wms/migrations/Migration20260616090000.js +13 -0
  271. package/dist/modules/wms/migrations/Migration20260616090000.js.map +7 -0
  272. package/dist/modules/wms/migrations/Migration20260707180000.js +15 -0
  273. package/dist/modules/wms/migrations/Migration20260707180000.js.map +7 -0
  274. package/dist/modules/wms/notifications.client.js +18 -0
  275. package/dist/modules/wms/notifications.client.js.map +7 -0
  276. package/dist/modules/wms/notifications.js +53 -0
  277. package/dist/modules/wms/notifications.js.map +7 -0
  278. package/dist/modules/wms/search.js +196 -0
  279. package/dist/modules/wms/search.js.map +7 -0
  280. package/dist/modules/wms/setup.js +30 -0
  281. package/dist/modules/wms/setup.js.map +7 -0
  282. package/dist/modules/wms/subscribers/low-stock-notification.js +40 -0
  283. package/dist/modules/wms/subscribers/low-stock-notification.js.map +7 -0
  284. package/dist/modules/wms/subscribers/reservation-shortfall-notification.js +69 -0
  285. package/dist/modules/wms/subscribers/reservation-shortfall-notification.js.map +7 -0
  286. package/dist/modules/wms/subscribers/sales-order-cancelled-release.js +14 -0
  287. package/dist/modules/wms/subscribers/sales-order-cancelled-release.js.map +7 -0
  288. package/dist/modules/wms/subscribers/sales-order-confirmed-reserve.js +14 -0
  289. package/dist/modules/wms/subscribers/sales-order-confirmed-reserve.js.map +7 -0
  290. package/dist/modules/wms/translations.js +10 -0
  291. package/dist/modules/wms/translations.js.map +7 -0
  292. package/dist/modules/wms/vector.js +9 -0
  293. package/dist/modules/wms/vector.js.map +7 -0
  294. package/dist/modules/wms/widgets/injection/catalog-inventory-profile/widget.js +200 -0
  295. package/dist/modules/wms/widgets/injection/catalog-inventory-profile/widget.js.map +7 -0
  296. package/dist/modules/wms/widgets/injection/order-items-stock-column/widget.js +104 -0
  297. package/dist/modules/wms/widgets/injection/order-items-stock-column/widget.js.map +7 -0
  298. package/dist/modules/wms/widgets/injection/sales-order-stock-context/widget.client.js +389 -0
  299. package/dist/modules/wms/widgets/injection/sales-order-stock-context/widget.client.js.map +7 -0
  300. package/dist/modules/wms/widgets/injection/sales-order-stock-context/widget.js +16 -0
  301. package/dist/modules/wms/widgets/injection/sales-order-stock-context/widget.js.map +7 -0
  302. package/dist/modules/wms/widgets/injection-table.js +36 -0
  303. package/dist/modules/wms/widgets/injection-table.js.map +7 -0
  304. package/dist/modules/wms/widgets/notifications/WmsLowStockRenderer.js +150 -0
  305. package/dist/modules/wms/widgets/notifications/WmsLowStockRenderer.js.map +7 -0
  306. package/dist/modules/wms/widgets/notifications/WmsReservationShortfallRenderer.js +186 -0
  307. package/dist/modules/wms/widgets/notifications/WmsReservationShortfallRenderer.js.map +7 -0
  308. package/dist/modules/wms/widgets/notifications/index.js +7 -0
  309. package/dist/modules/wms/widgets/notifications/index.js.map +7 -0
  310. package/generated/entities/inventory_balance/index.ts +9 -0
  311. package/generated/entities/inventory_lot/index.ts +8 -0
  312. package/generated/entities/inventory_movement/index.ts +16 -0
  313. package/generated/entities/inventory_reservation/index.ts +10 -0
  314. package/generated/entities/product_inventory_profile/index.ts +9 -0
  315. package/generated/entities/sales_order_warehouse_assignment/index.ts +4 -0
  316. package/generated/entities/warehouse/index.ts +11 -0
  317. package/generated/entities/warehouse_location/index.ts +9 -0
  318. package/generated/entities/warehouse_zone/index.ts +4 -0
  319. package/generated/entities.ids.generated.ts +12 -0
  320. package/generated/entity-fields-registry.ts +98 -0
  321. package/package.json +7 -7
  322. package/src/modules/auth/backend/roles/page.tsx +13 -4
  323. package/src/modules/auth/i18n/de.json +2 -0
  324. package/src/modules/auth/i18n/en.json +2 -0
  325. package/src/modules/auth/i18n/es.json +2 -0
  326. package/src/modules/auth/i18n/pl.json +2 -0
  327. package/src/modules/auth/lib/backendChrome.tsx +1 -0
  328. package/src/modules/auth/services/rbacService.ts +25 -21
  329. package/src/modules/catalog/api/products/route.ts +3 -0
  330. package/src/modules/catalog/api/variants/route.ts +3 -0
  331. package/src/modules/query_index/lib/engine.ts +18 -6
  332. package/src/modules/sales/api/documents/factory.ts +1 -0
  333. package/src/modules/sales/commands/documents.ts +71 -0
  334. package/src/modules/sales/components/documents/ItemsSection.tsx +58 -0
  335. package/src/modules/sales/events.ts +2 -0
  336. package/src/modules/sales/widgets/injection/order-items-context.ts +15 -0
  337. package/src/modules/wms/acl.ts +14 -0
  338. package/src/modules/wms/analytics.ts +9 -0
  339. package/src/modules/wms/api/dashboard/operational/route.ts +83 -0
  340. package/src/modules/wms/api/interceptors.ts +108 -0
  341. package/src/modules/wms/api/inventory/adjust/route.ts +50 -0
  342. package/src/modules/wms/api/inventory/allocate/route.ts +50 -0
  343. package/src/modules/wms/api/inventory/balances/route.ts +178 -0
  344. package/src/modules/wms/api/inventory/cycle-count/route.ts +51 -0
  345. package/src/modules/wms/api/inventory/helpers.ts +128 -0
  346. package/src/modules/wms/api/inventory/import/apply/route.ts +47 -0
  347. package/src/modules/wms/api/inventory/import/helpers.ts +223 -0
  348. package/src/modules/wms/api/inventory/import/template/route.ts +31 -0
  349. package/src/modules/wms/api/inventory/import/validate/route.ts +56 -0
  350. package/src/modules/wms/api/inventory/move/route.ts +50 -0
  351. package/src/modules/wms/api/inventory/movements/route.ts +164 -0
  352. package/src/modules/wms/api/inventory/receive/route.ts +49 -0
  353. package/src/modules/wms/api/inventory/release/route.ts +43 -0
  354. package/src/modules/wms/api/inventory/reservations/route.ts +128 -0
  355. package/src/modules/wms/api/inventory/reserve/route.ts +59 -0
  356. package/src/modules/wms/api/inventory-profiles/route.ts +177 -0
  357. package/src/modules/wms/api/listEnrichers.ts +312 -0
  358. package/src/modules/wms/api/listSearch.ts +121 -0
  359. package/src/modules/wms/api/locations/route.ts +171 -0
  360. package/src/modules/wms/api/lots/route.ts +205 -0
  361. package/src/modules/wms/api/openapi.ts +28 -0
  362. package/src/modules/wms/api/sales-orders/[salesOrderId]/re-run-reservation/route.ts +58 -0
  363. package/src/modules/wms/api/sales-orders/[salesOrderId]/warehouse-assignment/route.ts +306 -0
  364. package/src/modules/wms/api/sales-orders/assign-warehouse/route.ts +57 -0
  365. package/src/modules/wms/api/sales-orders/unassign-warehouse/route.ts +43 -0
  366. package/src/modules/wms/api/warehouses/route.ts +181 -0
  367. package/src/modules/wms/api/zones/route.ts +158 -0
  368. package/src/modules/wms/backend/config/wms/page.meta.ts +14 -0
  369. package/src/modules/wms/backend/config/wms/page.tsx +5 -0
  370. package/src/modules/wms/backend/page.meta.ts +12 -0
  371. package/src/modules/wms/backend/page.tsx +5 -0
  372. package/src/modules/wms/backend/wms/inventory/page.meta.ts +14 -0
  373. package/src/modules/wms/backend/wms/inventory/page.tsx +5 -0
  374. package/src/modules/wms/backend/wms/location/[id]/page.meta.ts +14 -0
  375. package/src/modules/wms/backend/wms/location/[id]/page.tsx +5 -0
  376. package/src/modules/wms/backend/wms/locations/page.meta.ts +14 -0
  377. package/src/modules/wms/backend/wms/locations/page.tsx +12 -0
  378. package/src/modules/wms/backend/wms/lot/[id]/page.meta.ts +14 -0
  379. package/src/modules/wms/backend/wms/lot/[id]/page.tsx +5 -0
  380. package/src/modules/wms/backend/wms/lots/page.meta.ts +14 -0
  381. package/src/modules/wms/backend/wms/lots/page.tsx +5 -0
  382. package/src/modules/wms/backend/wms/movements/page.meta.ts +14 -0
  383. package/src/modules/wms/backend/wms/movements/page.tsx +23 -0
  384. package/src/modules/wms/backend/wms/reservations/page.meta.ts +14 -0
  385. package/src/modules/wms/backend/wms/reservations/page.tsx +24 -0
  386. package/src/modules/wms/backend/wms/sku/[id]/page.meta.ts +14 -0
  387. package/src/modules/wms/backend/wms/sku/[id]/page.tsx +5 -0
  388. package/src/modules/wms/backend/wms/warehouses/page.meta.ts +14 -0
  389. package/src/modules/wms/backend/wms/warehouses/page.tsx +12 -0
  390. package/src/modules/wms/backend/wms/zones/page.meta.ts +14 -0
  391. package/src/modules/wms/backend/wms/zones/page.tsx +12 -0
  392. package/src/modules/wms/ce.ts +76 -0
  393. package/src/modules/wms/cli.ts +96 -0
  394. package/src/modules/wms/commands/configuration.ts +1844 -0
  395. package/src/modules/wms/commands/index.ts +3 -0
  396. package/src/modules/wms/commands/inventory-actions.ts +1866 -0
  397. package/src/modules/wms/commands/sales-order-assignment.ts +346 -0
  398. package/src/modules/wms/commands/shared.ts +90 -0
  399. package/src/modules/wms/components/backend/AdjustInventoryDialog.tsx +820 -0
  400. package/src/modules/wms/components/backend/ChangeLotStatusDialog.tsx +312 -0
  401. package/src/modules/wms/components/backend/CycleCountWizardDialog.tsx +2007 -0
  402. package/src/modules/wms/components/backend/ImportInventoryDialog.tsx +1018 -0
  403. package/src/modules/wms/components/backend/InventoryOperationsSection.tsx +159 -0
  404. package/src/modules/wms/components/backend/LocationEditDialog.tsx +185 -0
  405. package/src/modules/wms/components/backend/MoveInventoryDialog.tsx +907 -0
  406. package/src/modules/wms/components/backend/ReceiveInventoryDialog.tsx +695 -0
  407. package/src/modules/wms/components/backend/ReleaseReservationDialog.tsx +424 -0
  408. package/src/modules/wms/components/backend/ReserveInventoryDialog.tsx +511 -0
  409. package/src/modules/wms/components/backend/WmsConfigurationPage.tsx +1340 -0
  410. package/src/modules/wms/components/backend/WmsInventoryConsolePage.tsx +1132 -0
  411. package/src/modules/wms/components/backend/WmsLocationDetailPage.tsx +1310 -0
  412. package/src/modules/wms/components/backend/WmsLotDetailPage.tsx +1355 -0
  413. package/src/modules/wms/components/backend/WmsLotsListPage.tsx +359 -0
  414. package/src/modules/wms/components/backend/WmsOperationalDashboardPage.tsx +958 -0
  415. package/src/modules/wms/components/backend/WmsSkuDetailPage.tsx +1245 -0
  416. package/src/modules/wms/components/backend/inventoryMutationLoaders.ts +960 -0
  417. package/src/modules/wms/components/backend/useWmsInventoryMutationAccess.ts +89 -0
  418. package/src/modules/wms/components/backend/useWmsInventoryScopeFromSearchParams.ts +32 -0
  419. package/src/modules/wms/components/backend/wmsLookupLoaders.ts +78 -0
  420. package/src/modules/wms/data/enrichers.ts +718 -0
  421. package/src/modules/wms/data/entities.ts +436 -0
  422. package/src/modules/wms/data/validators.ts +463 -0
  423. package/src/modules/wms/di.ts +25 -0
  424. package/src/modules/wms/events.ts +41 -0
  425. package/src/modules/wms/i18n/de.json +1051 -0
  426. package/src/modules/wms/i18n/en.json +1051 -0
  427. package/src/modules/wms/i18n/es.json +1051 -0
  428. package/src/modules/wms/i18n/pl.json +1051 -0
  429. package/src/modules/wms/index.ts +15 -0
  430. package/src/modules/wms/lib/catalogInventoryProfileIntent.ts +65 -0
  431. package/src/modules/wms/lib/catalogInventoryProfileSync.ts +199 -0
  432. package/src/modules/wms/lib/expiry.ts +30 -0
  433. package/src/modules/wms/lib/flashMutationError.ts +74 -0
  434. package/src/modules/wms/lib/inventoryDisplayUi.ts +249 -0
  435. package/src/modules/wms/lib/inventoryIdempotency.ts +54 -0
  436. package/src/modules/wms/lib/inventoryImportCsv.ts +212 -0
  437. package/src/modules/wms/lib/inventoryImportService.ts +629 -0
  438. package/src/modules/wms/lib/inventoryMutationUi.ts +21 -0
  439. package/src/modules/wms/lib/inventoryPolicy.ts +111 -0
  440. package/src/modules/wms/lib/inventoryReconciliation.ts +263 -0
  441. package/src/modules/wms/lib/inventoryTrackingValidation.ts +23 -0
  442. package/src/modules/wms/lib/loadOperationalDashboard.ts +776 -0
  443. package/src/modules/wms/lib/lowStockBalanceFilter.ts +74 -0
  444. package/src/modules/wms/lib/primaryWarehousePolicy.ts +46 -0
  445. package/src/modules/wms/lib/roleFeatures.ts +32 -0
  446. package/src/modules/wms/lib/salesOrderInventoryAutomation.ts +390 -0
  447. package/src/modules/wms/lib/salesOrderWarehouseAssignment.ts +78 -0
  448. package/src/modules/wms/lib/wmsIntegrationToggles.ts +81 -0
  449. package/src/modules/wms/migrations/.snapshot-open-mercato.json +2880 -0
  450. package/src/modules/wms/migrations/Migration20260428110546.ts +91 -0
  451. package/src/modules/wms/migrations/Migration20260527120000.ts +15 -0
  452. package/src/modules/wms/migrations/Migration20260527140000.ts +35 -0
  453. package/src/modules/wms/migrations/Migration20260613120000.ts +21 -0
  454. package/src/modules/wms/migrations/Migration20260616090000.ts +11 -0
  455. package/src/modules/wms/migrations/Migration20260707180000.ts +13 -0
  456. package/src/modules/wms/notifications.client.ts +18 -0
  457. package/src/modules/wms/notifications.ts +51 -0
  458. package/src/modules/wms/search.ts +230 -0
  459. package/src/modules/wms/setup.ts +29 -0
  460. package/src/modules/wms/subscribers/low-stock-notification.ts +54 -0
  461. package/src/modules/wms/subscribers/reservation-shortfall-notification.ts +98 -0
  462. package/src/modules/wms/subscribers/sales-order-cancelled-release.ts +21 -0
  463. package/src/modules/wms/subscribers/sales-order-confirmed-reserve.ts +21 -0
  464. package/src/modules/wms/translations.ts +6 -0
  465. package/src/modules/wms/vector.ts +7 -0
  466. package/src/modules/wms/widgets/injection/catalog-inventory-profile/widget.ts +238 -0
  467. package/src/modules/wms/widgets/injection/order-items-stock-column/widget.tsx +153 -0
  468. package/src/modules/wms/widgets/injection/sales-order-stock-context/widget.client.tsx +476 -0
  469. package/src/modules/wms/widgets/injection/sales-order-stock-context/widget.ts +15 -0
  470. package/src/modules/wms/widgets/injection-table.ts +34 -0
  471. package/src/modules/wms/widgets/notifications/WmsLowStockRenderer.tsx +153 -0
  472. package/src/modules/wms/widgets/notifications/WmsReservationShortfallRenderer.tsx +194 -0
  473. package/src/modules/wms/widgets/notifications/index.ts +2 -0
@@ -0,0 +1,1132 @@
1
+ "use client"
2
+
3
+ import * as React from 'react'
4
+ import Link from 'next/link'
5
+ import type { ColumnDef, SortingState } from '@tanstack/react-table'
6
+ import { useQuery } from '@tanstack/react-query'
7
+ import { Page, PageBody } from '@open-mercato/ui/backend/Page'
8
+ import { DataTable } from '@open-mercato/ui/backend/DataTable'
9
+ import { EmptyState } from '@open-mercato/ui/backend/EmptyState'
10
+ import { RowActions } from '@open-mercato/ui/backend/RowActions'
11
+ import { ComboboxInput } from '@open-mercato/ui/backend/inputs/ComboboxInput'
12
+ import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
13
+ import { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'
14
+ import { flash } from '@open-mercato/ui/backend/FlashMessages'
15
+ import { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'
16
+ import { useLocale, useT } from '@open-mercato/shared/lib/i18n/context'
17
+ import { StatusBadge, type StatusBadgeVariant } from '@open-mercato/ui/primitives/status-badge'
18
+ import { Boxes, Package, Route, ShieldCheck, Warehouse as WarehouseIcon, X } from 'lucide-react'
19
+ import { E } from '#generated/entities.ids.generated'
20
+ import {
21
+ createInventoryDateTimeFormatter,
22
+ createInventoryQuantityFormatter,
23
+ formatCatalogVariantLabel,
24
+ formatInventoryDateTime,
25
+ formatInventoryQuantity,
26
+ formatReservationSourceLabel,
27
+ inventoryMovementTypeLabel,
28
+ inventoryReferenceTypeLabel,
29
+ inventoryReservationSourceTypeLabel,
30
+ inventoryReservationStatusLabel,
31
+ } from '../../lib/inventoryDisplayUi'
32
+ import { parseInventoryQuantity } from '../../lib/inventoryMutationUi'
33
+ import { ImportInventoryDialog } from './ImportInventoryDialog'
34
+ import { InventoryOperationsSection } from './InventoryOperationsSection'
35
+ import { MoveInventoryDialog } from './MoveInventoryDialog'
36
+ import { ReceiveInventoryDialog } from './ReceiveInventoryDialog'
37
+ import { ReleaseReservationDialog } from './ReleaseReservationDialog'
38
+ import {
39
+ useWmsInventoryMutationAccess,
40
+ type WmsInventoryMutationAccess,
41
+ } from './useWmsInventoryMutationAccess'
42
+ import { Button } from '@open-mercato/ui/primitives/button'
43
+ import {
44
+ loadCatalogVariantOptions,
45
+ loadWarehouseOptions,
46
+ resolveCatalogVariantLabel,
47
+ resolveWarehouseLabel,
48
+ } from './inventoryMutationLoaders'
49
+ import { mergeLabelCacheEntries } from './wmsLookupLoaders'
50
+ import {
51
+ useWmsInventoryScopeFromSearchParams,
52
+ type WmsLowStockFilter,
53
+ } from './useWmsInventoryScopeFromSearchParams'
54
+
55
+ type PagedResponse<T> = {
56
+ items: T[]
57
+ total: number
58
+ totalPages: number
59
+ }
60
+
61
+ type InventoryBalanceRow = {
62
+ id: string
63
+ warehouse_id?: string | null
64
+ warehouse_name?: string | null
65
+ warehouse_code?: string | null
66
+ location_id?: string | null
67
+ location_code?: string | null
68
+ location_type?: string | null
69
+ catalog_variant_id?: string | null
70
+ variant_name?: string | null
71
+ variant_sku?: string | null
72
+ lot_id?: string | null
73
+ quantity_on_hand?: string | number | null
74
+ quantity_reserved?: string | number | null
75
+ quantity_allocated?: string | number | null
76
+ quantity_available?: number | null
77
+ }
78
+
79
+ type InventoryReservationRow = {
80
+ id: string
81
+ warehouse_id?: string | null
82
+ warehouse_name?: string | null
83
+ warehouse_code?: string | null
84
+ catalog_variant_id?: string | null
85
+ variant_name?: string | null
86
+ variant_sku?: string | null
87
+ quantity?: string | number | null
88
+ source_type?: string | null
89
+ source_id?: string | null
90
+ source_label?: string | null
91
+ status?: string | null
92
+ }
93
+
94
+ type InventoryMovementRow = {
95
+ id: string
96
+ warehouse_id?: string | null
97
+ warehouse_name?: string | null
98
+ warehouse_code?: string | null
99
+ location_from_id?: string | null
100
+ location_from_code?: string | null
101
+ location_from_type?: string | null
102
+ location_to_id?: string | null
103
+ location_to_code?: string | null
104
+ location_to_type?: string | null
105
+ catalog_variant_id?: string | null
106
+ variant_name?: string | null
107
+ variant_sku?: string | null
108
+ quantity?: string | number | null
109
+ type?: string | null
110
+ reference_type?: string | null
111
+ reference_id?: string | null
112
+ performed_at?: string | null
113
+ received_at?: string | null
114
+ }
115
+
116
+ function formatVariantLabel(row: {
117
+ variant_name?: string | null
118
+ variant_sku?: string | null
119
+ catalog_variant_id?: string | null
120
+ }): string {
121
+ return formatCatalogVariantLabel(row)
122
+ }
123
+
124
+ const movementStatusMap: Record<string, StatusBadgeVariant> = {
125
+ receipt: 'success',
126
+ return_receive: 'success',
127
+ adjust: 'warning',
128
+ transfer: 'info',
129
+ pick: 'info',
130
+ pack: 'info',
131
+ cycle_count: 'neutral',
132
+ putaway: 'info',
133
+ ship: 'success',
134
+ }
135
+
136
+ const reservationStatusMap: Record<string, StatusBadgeVariant> = {
137
+ active: 'info',
138
+ released: 'neutral',
139
+ fulfilled: 'success',
140
+ }
141
+
142
+ function useInventoryDisplayFormatters() {
143
+ const locale = useLocale()
144
+ const quantityFormatter = React.useMemo(
145
+ () => createInventoryQuantityFormatter(locale),
146
+ [locale],
147
+ )
148
+ const dateTimeFormatter = React.useMemo(
149
+ () => createInventoryDateTimeFormatter(locale),
150
+ [locale],
151
+ )
152
+ return { quantityFormatter, dateTimeFormatter }
153
+ }
154
+
155
+ function formatWarehouseLabel(row: {
156
+ warehouse_name?: string | null
157
+ warehouse_code?: string | null
158
+ warehouse_id?: string | null
159
+ }): string {
160
+ return (
161
+ row.warehouse_name?.trim() ||
162
+ row.warehouse_code?.trim() ||
163
+ row.warehouse_id ||
164
+ '—'
165
+ )
166
+ }
167
+
168
+ function formatLocationLabel(
169
+ row: Record<string, unknown>,
170
+ prefix: 'location' | 'location_from' | 'location_to',
171
+ ): string {
172
+ const code = String((row[`${prefix}_code`] as string | null | undefined) ?? '').trim()
173
+ if (code) return code
174
+ const id = row[`${prefix}_id`]
175
+ return typeof id === 'string' && id ? id : '—'
176
+ }
177
+
178
+ function SectionCard({
179
+ title,
180
+ description,
181
+ icon,
182
+ children,
183
+ }: {
184
+ title: string
185
+ description: string
186
+ icon: React.ReactNode
187
+ children: React.ReactNode
188
+ }) {
189
+ return (
190
+ <section className="rounded-lg border bg-card p-5 text-card-foreground shadow-sm">
191
+ <div className="mb-4 flex items-start gap-3">
192
+ <div className="rounded-md border bg-muted/40 p-2 text-muted-foreground">
193
+ {icon}
194
+ </div>
195
+ <div className="space-y-1">
196
+ <h2 className="text-xl font-semibold">{title}</h2>
197
+ <p className="text-sm text-muted-foreground">{description}</p>
198
+ </div>
199
+ </div>
200
+ {/* `DataTable` in `embedded` mode omits its own scroll wrapper (shared
201
+ * primitive behavior kept intact for other consumers), so this
202
+ * WMS-local card provides horizontal scroll instead of clipping wide
203
+ * tables. */}
204
+ <div className="overflow-x-auto">{children}</div>
205
+ </section>
206
+ )
207
+ }
208
+
209
+ function buildInventoryQuery(
210
+ search: string,
211
+ page: number,
212
+ pageSize: number,
213
+ sorting: SortingState,
214
+ scope: {
215
+ warehouseId: string
216
+ variantId: string
217
+ lotId: string
218
+ lowStock: WmsLowStockFilter | null
219
+ },
220
+ ) {
221
+ const sortCol = sorting[0]
222
+ const params = new URLSearchParams({
223
+ page: String(page),
224
+ pageSize: String(pageSize),
225
+ sortField: sortCol ? sortCol.id : 'updatedAt',
226
+ sortDir: sortCol ? (sortCol.desc ? 'desc' : 'asc') : 'desc',
227
+ })
228
+ if (search.trim()) params.set('search', search.trim())
229
+ if (scope.warehouseId.trim()) params.set('warehouseId', scope.warehouseId.trim())
230
+ if (scope.variantId.trim()) params.set('catalogVariantId', scope.variantId.trim())
231
+ if (scope.lotId.trim()) params.set('lotId', scope.lotId.trim())
232
+ if (scope.lowStock) params.set('lowStock', scope.lowStock)
233
+ return params.toString()
234
+ }
235
+
236
+ type InventoryDataTableSectionProps<T> = {
237
+ sectionQueryKey: string
238
+ endpoint: string
239
+ titleKey: string
240
+ titleFallback: string
241
+ descriptionKey: string
242
+ descriptionFallback: string
243
+ errorKey: string
244
+ errorFallback: string
245
+ searchKey: string
246
+ searchFallback: string
247
+ emptyTitleKey: string
248
+ emptyTitleFallback: string
249
+ emptyDescriptionKey: string
250
+ emptyDescriptionFallback: string
251
+ tableId: string
252
+ entityId: string
253
+ icon: React.ReactNode
254
+ columns: ColumnDef<T>[]
255
+ rowActions?: (row: T) => React.ReactNode
256
+ warehouseId?: string
257
+ variantId?: string
258
+ lotId?: string
259
+ lowStock?: WmsLowStockFilter | null
260
+ extraParams?: Record<string, string>
261
+ toolbarActions?: React.ReactNode
262
+ emptyStateAction?: React.ReactNode
263
+ }
264
+
265
+ function InventoryDataTableSection<T>({
266
+ sectionQueryKey,
267
+ endpoint,
268
+ titleKey,
269
+ titleFallback,
270
+ descriptionKey,
271
+ descriptionFallback,
272
+ errorKey,
273
+ errorFallback,
274
+ searchKey,
275
+ searchFallback,
276
+ emptyTitleKey,
277
+ emptyTitleFallback,
278
+ emptyDescriptionKey,
279
+ emptyDescriptionFallback,
280
+ tableId,
281
+ entityId,
282
+ icon,
283
+ columns,
284
+ rowActions,
285
+ warehouseId = '',
286
+ variantId = '',
287
+ lotId = '',
288
+ lowStock = null,
289
+ extraParams,
290
+ toolbarActions,
291
+ emptyStateAction,
292
+ }: InventoryDataTableSectionProps<T>) {
293
+ const t = useT()
294
+ const [page, setPage] = React.useState(1)
295
+ const [search, setSearch] = React.useState('')
296
+ const [sorting, setSorting] = React.useState<SortingState>([])
297
+
298
+ const handleSortingChange = React.useCallback((nextSorting: SortingState) => {
299
+ setSorting(nextSorting)
300
+ setPage(1)
301
+ }, [])
302
+
303
+ const params = React.useMemo(() => {
304
+ const base = buildInventoryQuery(search, page, 20, sorting, {
305
+ warehouseId,
306
+ variantId,
307
+ lotId,
308
+ lowStock,
309
+ })
310
+ if (!extraParams || Object.keys(extraParams).length === 0) return base
311
+ const urlParams = new URLSearchParams(base)
312
+ for (const [key, value] of Object.entries(extraParams)) {
313
+ urlParams.set(key, value)
314
+ }
315
+ return urlParams.toString()
316
+ }, [extraParams, lowStock, lotId, page, search, sorting, variantId, warehouseId])
317
+
318
+ React.useEffect(() => {
319
+ setPage(1)
320
+ }, [warehouseId, variantId, lotId, lowStock])
321
+
322
+ const query = useQuery({
323
+ queryKey: ['wms-inventory-console', sectionQueryKey, params],
324
+ queryFn: async () => {
325
+ const call = await apiCall<PagedResponse<T>>(`${endpoint}?${params}`)
326
+ if (!call.ok) {
327
+ await raiseCrudError(call.response, t(errorKey, errorFallback))
328
+ }
329
+ return call.result ?? { items: [], total: 0, totalPages: 1 }
330
+ },
331
+ })
332
+
333
+ return (
334
+ <SectionCard
335
+ title={t(titleKey, titleFallback)}
336
+ description={t(descriptionKey, descriptionFallback)}
337
+ icon={icon}
338
+ >
339
+ <DataTable
340
+ embedded
341
+ title={t(titleKey, titleFallback)}
342
+ columns={columns}
343
+ data={query.data?.items ?? []}
344
+ isLoading={query.isLoading}
345
+ error={query.isError ? t(errorKey, errorFallback) : null}
346
+ entityId={entityId}
347
+ searchValue={search}
348
+ onSearchChange={(value) => {
349
+ setSearch(value)
350
+ setPage(1)
351
+ }}
352
+ searchPlaceholder={t(searchKey, searchFallback)}
353
+ pagination={{
354
+ page,
355
+ pageSize: 20,
356
+ total: query.data?.total ?? 0,
357
+ totalPages: query.data?.totalPages ?? 1,
358
+ onPageChange: setPage,
359
+ }}
360
+ perspective={{ tableId }}
361
+ sortable
362
+ manualSorting
363
+ sorting={sorting}
364
+ onSortingChange={handleSortingChange}
365
+ rowActions={rowActions}
366
+ emptyState={
367
+ <EmptyState
368
+ title={t(emptyTitleKey, emptyTitleFallback)}
369
+ description={t(emptyDescriptionKey, emptyDescriptionFallback)}
370
+ actions={emptyStateAction}
371
+ />
372
+ }
373
+ actions={toolbarActions}
374
+ />
375
+ </SectionCard>
376
+ )
377
+ }
378
+
379
+ function InventoryScopeBar({
380
+ warehouseId,
381
+ variantId,
382
+ onWarehouseChange,
383
+ onVariantChange,
384
+ }: {
385
+ warehouseId: string
386
+ variantId: string
387
+ onWarehouseChange: (id: string) => void
388
+ onVariantChange: (id: string) => void
389
+ }) {
390
+ const t = useT()
391
+ const [warehouseLabelCache, setWarehouseLabelCache] = React.useState<Record<string, string>>({})
392
+ const [variantLabelCache, setVariantLabelCache] = React.useState<Record<string, string>>({})
393
+ const warehouseLabelCacheRef = React.useRef(warehouseLabelCache)
394
+ warehouseLabelCacheRef.current = warehouseLabelCache
395
+ const variantLabelCacheRef = React.useRef(variantLabelCache)
396
+ variantLabelCacheRef.current = variantLabelCache
397
+
398
+ React.useEffect(() => {
399
+ if (!warehouseId.trim() || warehouseLabelCacheRef.current[warehouseId]) return
400
+ let cancelled = false
401
+ void resolveWarehouseLabel(warehouseId).then((label) => {
402
+ if (cancelled || !label) return
403
+ setWarehouseLabelCache((c) => ({ ...c, [warehouseId]: label }))
404
+ })
405
+ return () => { cancelled = true }
406
+ }, [warehouseId])
407
+
408
+ React.useEffect(() => {
409
+ if (!variantId.trim() || variantLabelCacheRef.current[variantId]) return
410
+ let cancelled = false
411
+ void resolveCatalogVariantLabel(variantId).then((label) => {
412
+ if (cancelled || !label) return
413
+ setVariantLabelCache((c) => ({ ...c, [variantId]: label }))
414
+ })
415
+ return () => { cancelled = true }
416
+ }, [variantId])
417
+
418
+ // `mergeLabelCacheEntries` bails out with the same object reference when the
419
+ // fetched options are already cached. Without that guard, every suggestion
420
+ // fetch would produce a fresh object identity and re-render this component,
421
+ // which recreates `loadSuggestions` and retriggers ComboboxInput's own
422
+ // suggestion-fetch effect — looping indefinitely and starving the other
423
+ // field's suggestions from ever loading.
424
+ const loadWarehouseSuggestions = React.useCallback(async (query?: string) => {
425
+ const options = await loadWarehouseOptions(query)
426
+ setWarehouseLabelCache((c) => mergeLabelCacheEntries(c, options))
427
+ return options.map((o) => ({ value: o.value, label: o.label }))
428
+ }, [])
429
+
430
+ const loadVariantSuggestions = React.useCallback(async (query?: string) => {
431
+ const options = await loadCatalogVariantOptions(query)
432
+ setVariantLabelCache((c) => mergeLabelCacheEntries(c, options))
433
+ return options.map((o) => ({ value: o.value, label: o.label }))
434
+ }, [])
435
+
436
+ const hasScope = warehouseId.trim() || variantId.trim()
437
+
438
+ return (
439
+ <div className="flex flex-wrap items-center gap-3 rounded-lg border bg-card px-4 py-3 shadow-sm">
440
+ <div className="flex items-center gap-2 text-muted-foreground">
441
+ <WarehouseIcon className="size-4 shrink-0" />
442
+ <span className="text-sm font-medium">
443
+ {t('wms.backend.inventory.console.scopeBar.label', 'Scope')}
444
+ </span>
445
+ </div>
446
+ <div className="w-56">
447
+ <ComboboxInput
448
+ value={warehouseId}
449
+ onChange={(next) => onWarehouseChange(next.trim())}
450
+ loadSuggestions={loadWarehouseSuggestions}
451
+ resolveLabel={(value) => warehouseLabelCache[value] ?? value}
452
+ placeholder={t('wms.backend.inventory.console.scopeBar.allWarehouses', 'All warehouses')}
453
+ allowCustomValues={false}
454
+ clearable
455
+ />
456
+ </div>
457
+ <div className="flex items-center gap-2 text-muted-foreground">
458
+ <Package className="size-4 shrink-0" />
459
+ </div>
460
+ <div className="w-56">
461
+ <ComboboxInput
462
+ value={variantId}
463
+ onChange={(next) => onVariantChange(next.trim())}
464
+ loadSuggestions={loadVariantSuggestions}
465
+ resolveLabel={(value) => variantLabelCache[value] ?? value}
466
+ placeholder={t('wms.backend.inventory.console.scopeBar.allVariants', 'All SKUs')}
467
+ allowCustomValues={false}
468
+ clearable
469
+ />
470
+ </div>
471
+ {hasScope ? (
472
+ <Button
473
+ type="button"
474
+ variant="ghost"
475
+ size="sm"
476
+ className="h-auto px-2 py-1 text-xs"
477
+ onClick={() => {
478
+ onWarehouseChange('')
479
+ onVariantChange('')
480
+ }}
481
+ >
482
+ <X className="size-3" />
483
+ {t('wms.backend.inventory.console.scopeBar.clearAll', 'Clear')}
484
+ </Button>
485
+ ) : null}
486
+ </div>
487
+ )
488
+ }
489
+
490
+ export function InventoryBalancesSection({
491
+ access,
492
+ warehouseId = '',
493
+ variantId = '',
494
+ lotId = '',
495
+ lowStock = null,
496
+ }: {
497
+ access: WmsInventoryMutationAccess
498
+ warehouseId?: string
499
+ variantId?: string
500
+ lotId?: string
501
+ lowStock?: WmsLowStockFilter | null
502
+ }) {
503
+ const t = useT()
504
+ const { quantityFormatter } = useInventoryDisplayFormatters()
505
+ const [moveOpen, setMoveOpen] = React.useState(false)
506
+ const [movePreset, setMovePreset] = React.useState<InventoryBalanceRow | null>(null)
507
+ const [receiveOpen, setReceiveOpen] = React.useState(false)
508
+ const [importOpen, setImportOpen] = React.useState(false)
509
+
510
+ const openMoveDialog = React.useCallback((row: InventoryBalanceRow) => {
511
+ setMovePreset(row)
512
+ setMoveOpen(true)
513
+ }, [])
514
+
515
+ const columns = React.useMemo<ColumnDef<InventoryBalanceRow>[]>(
516
+ () => [
517
+ {
518
+ accessorKey: 'catalog_variant_id',
519
+ id: 'catalogVariantId',
520
+ header: t('wms.backend.inventory.balances.columns.variant', 'Variant'),
521
+ enableSorting: true,
522
+ cell: ({ row }) => {
523
+ const variantId = row.original.catalog_variant_id?.trim()
524
+ const label = formatVariantLabel(row.original)
525
+ if (!variantId) return label
526
+ return (
527
+ <Link
528
+ href={`/backend/wms/sku/${encodeURIComponent(variantId)}`}
529
+ className="font-medium text-primary hover:underline"
530
+ >
531
+ {label}
532
+ </Link>
533
+ )
534
+ },
535
+ },
536
+ {
537
+ accessorKey: 'warehouse_id',
538
+ id: 'warehouseId',
539
+ header: t(
540
+ 'wms.backend.inventory.balances.columns.warehouse',
541
+ 'Warehouse',
542
+ ),
543
+ enableSorting: true,
544
+ cell: ({ row }) => formatWarehouseLabel(row.original),
545
+ },
546
+ {
547
+ accessorKey: 'location_id',
548
+ id: 'locationId',
549
+ header: t(
550
+ 'wms.backend.inventory.balances.columns.location',
551
+ 'Location',
552
+ ),
553
+ enableSorting: true,
554
+ cell: ({ row }) => {
555
+ const locationId = row.original.location_id?.trim()
556
+ const label = formatLocationLabel(row.original as Record<string, unknown>, 'location')
557
+ if (!locationId || label === '—') return label
558
+ return (
559
+ <Link
560
+ href={`/backend/wms/location/${encodeURIComponent(locationId)}`}
561
+ className="font-medium text-primary hover:underline"
562
+ >
563
+ {label}
564
+ </Link>
565
+ )
566
+ },
567
+ },
568
+ {
569
+ accessorKey: 'lot_id',
570
+ header: t('wms.backend.inventory.balances.columns.lot', 'Lot'),
571
+ enableSorting: false,
572
+ cell: ({ row }) => {
573
+ const lotId = row.original.lot_id?.trim()
574
+ if (!lotId) return '—'
575
+ return (
576
+ <Link
577
+ href={`/backend/wms/lot/${encodeURIComponent(lotId)}`}
578
+ className="font-medium text-primary hover:underline"
579
+ >
580
+ {t('wms.backend.inventory.balances.lotLink', 'View lot')}
581
+ </Link>
582
+ )
583
+ },
584
+ },
585
+ {
586
+ accessorKey: 'quantity_available',
587
+ id: 'quantityAvailable',
588
+ header: t(
589
+ 'wms.backend.inventory.balances.columns.available',
590
+ 'Available',
591
+ ),
592
+ enableSorting: true,
593
+ cell: ({ row }) => (
594
+ <span className="tabular-nums">
595
+ {formatInventoryQuantity(row.original.quantity_available, quantityFormatter)}
596
+ </span>
597
+ ),
598
+ },
599
+ {
600
+ accessorKey: 'quantity_reserved',
601
+ id: 'quantityReserved',
602
+ header: t(
603
+ 'wms.backend.inventory.balances.columns.reserved',
604
+ 'Reserved',
605
+ ),
606
+ enableSorting: true,
607
+ cell: ({ row }) => (
608
+ <span className="tabular-nums">
609
+ {formatInventoryQuantity(row.original.quantity_reserved, quantityFormatter)}
610
+ </span>
611
+ ),
612
+ },
613
+ {
614
+ accessorKey: 'quantity_allocated',
615
+ id: 'quantityAllocated',
616
+ header: t(
617
+ 'wms.backend.inventory.balances.columns.allocated',
618
+ 'Allocated',
619
+ ),
620
+ enableSorting: true,
621
+ cell: ({ row }) => (
622
+ <span className="tabular-nums">
623
+ {formatInventoryQuantity(row.original.quantity_allocated, quantityFormatter)}
624
+ </span>
625
+ ),
626
+ },
627
+ ],
628
+ [quantityFormatter, t],
629
+ )
630
+
631
+ const rowActions = React.useCallback(
632
+ (row: InventoryBalanceRow) => {
633
+ if (!access.canMove) return null
634
+ const available = parseInventoryQuantity(row.quantity_available)
635
+ if (available <= 0) return null
636
+ return (
637
+ <RowActions
638
+ items={[
639
+ {
640
+ id: 'move',
641
+ label: t('wms.backend.inventory.balances.actions.move', 'Move'),
642
+ onSelect: () => openMoveDialog(row),
643
+ },
644
+ ]}
645
+ />
646
+ )
647
+ },
648
+ [access.canMove, openMoveDialog, t],
649
+ )
650
+
651
+ return (
652
+ <>
653
+ <InventoryDataTableSection<InventoryBalanceRow>
654
+ sectionQueryKey="balances"
655
+ endpoint="/api/wms/inventory/balances"
656
+ titleKey="wms.backend.inventory.balances.title"
657
+ titleFallback="Inventory balances"
658
+ descriptionKey="wms.backend.inventory.balances.description"
659
+ descriptionFallback="Current on-hand, reserved, allocated, and available quantities by bucket."
660
+ errorKey="wms.backend.inventory.errors.balances"
661
+ errorFallback="Failed to load balances."
662
+ searchKey="wms.backend.inventory.balances.search"
663
+ searchFallback="Search balances"
664
+ emptyTitleKey="wms.backend.inventory.balances.empty.title"
665
+ emptyTitleFallback="No balance buckets"
666
+ emptyDescriptionKey="wms.backend.inventory.balances.empty.description"
667
+ emptyDescriptionFallback="Balances appear after receipts, adjustments, or moves create inventory buckets."
668
+ tableId="wms.inventory.balances"
669
+ entityId={E.wms.inventory_balance}
670
+ icon={<Boxes className="size-5" />}
671
+ columns={columns}
672
+ rowActions={rowActions}
673
+ warehouseId={warehouseId}
674
+ variantId={variantId}
675
+ lotId={lotId}
676
+ lowStock={lowStock}
677
+ emptyStateAction={
678
+ access.canReceive || access.canImport ? (
679
+ <div className="flex flex-wrap items-center justify-center gap-2">
680
+ {access.canReceive ? (
681
+ <Button type="button" variant="outline" size="sm" onClick={() => setReceiveOpen(true)}>
682
+ {t('wms.backend.inventory.balances.empty.receive', 'Receive stock')}
683
+ </Button>
684
+ ) : null}
685
+ {access.canImport ? (
686
+ <Button type="button" variant="outline" size="sm" onClick={() => setImportOpen(true)}>
687
+ {t('wms.backend.inventory.balances.empty.importCsv', 'Import CSV')}
688
+ </Button>
689
+ ) : null}
690
+ </div>
691
+ ) : null
692
+ }
693
+ />
694
+ {access.canReceive ? (
695
+ <ReceiveInventoryDialog
696
+ open={receiveOpen}
697
+ onOpenChange={setReceiveOpen}
698
+ access={access}
699
+ />
700
+ ) : null}
701
+ {access.canImport ? (
702
+ <ImportInventoryDialog open={importOpen} onOpenChange={setImportOpen} access={access} />
703
+ ) : null}
704
+ {access.canMove ? (
705
+ <MoveInventoryDialog
706
+ open={moveOpen}
707
+ onOpenChange={setMoveOpen}
708
+ access={access}
709
+ initialCatalogVariantId={movePreset?.catalog_variant_id ?? undefined}
710
+ initialWarehouseId={movePreset?.warehouse_id ?? undefined}
711
+ initialFromLocationId={movePreset?.location_id ?? undefined}
712
+ initialLotId={movePreset?.lot_id ?? undefined}
713
+ initialAvailable={movePreset ? parseInventoryQuantity(movePreset.quantity_available) : null}
714
+ lockSourceContext
715
+ />
716
+ ) : null}
717
+ </>
718
+ )
719
+ }
720
+
721
+ export function InventoryReservationsSection({
722
+ access,
723
+ warehouseId = '',
724
+ variantId = '',
725
+ lotId = '',
726
+ }: {
727
+ access: WmsInventoryMutationAccess
728
+ warehouseId?: string
729
+ variantId?: string
730
+ lotId?: string
731
+ }) {
732
+ const t = useT()
733
+ const { quantityFormatter } = useInventoryDisplayFormatters()
734
+ const [releaseOpen, setReleaseOpen] = React.useState(false)
735
+ const [releasePreset, setReleasePreset] = React.useState<InventoryReservationRow | null>(null)
736
+ const [activeOnly, setActiveOnly] = React.useState(true)
737
+
738
+ const { runMutation: runAllocateMutation, retryLastMutation: retryAllocate } = useGuardedMutation({
739
+ contextId: 'wms-inventory-allocate',
740
+ })
741
+ const allocateMutationContext = React.useMemo(
742
+ () => ({ retryLastMutation: retryAllocate }),
743
+ [retryAllocate],
744
+ )
745
+
746
+ const handleAllocate = React.useCallback(
747
+ async (row: InventoryReservationRow) => {
748
+ if (!access.organizationId || !access.tenantId || !row.id) return
749
+ const payload = {
750
+ organizationId: access.organizationId,
751
+ tenantId: access.tenantId,
752
+ reservationId: row.id,
753
+ }
754
+ try {
755
+ await runAllocateMutation({
756
+ operation: async () => {
757
+ const call = await apiCall<{ ok?: boolean }>('/api/wms/inventory/allocate', {
758
+ method: 'POST',
759
+ headers: { 'content-type': 'application/json' },
760
+ body: JSON.stringify(payload),
761
+ })
762
+ if (!call.ok) await raiseCrudError(call.response, t('wms.backend.inventory.allocate.errors.submit', 'Failed to allocate reservation.'))
763
+ return call.result ?? {}
764
+ },
765
+ context: allocateMutationContext,
766
+ mutationPayload: payload,
767
+ })
768
+ flash(t('wms.backend.inventory.allocate.flash.success', 'Reservation allocated'), 'success')
769
+ } catch {
770
+ flash(t('wms.backend.inventory.allocate.errors.submit', 'Failed to allocate reservation.'), 'error')
771
+ }
772
+ },
773
+ [access.organizationId, access.tenantId, allocateMutationContext, runAllocateMutation, t],
774
+ )
775
+
776
+ const openReleaseDialog = React.useCallback((row: InventoryReservationRow) => {
777
+ setReleasePreset(row)
778
+ setReleaseOpen(true)
779
+ }, [])
780
+
781
+ const columns = React.useMemo<ColumnDef<InventoryReservationRow>[]>(
782
+ () => [
783
+ {
784
+ accessorKey: 'catalog_variant_id',
785
+ header: t(
786
+ 'wms.backend.inventory.reservations.columns.variant',
787
+ 'Variant',
788
+ ),
789
+ enableSorting: true,
790
+ cell: ({ row }) => formatVariantLabel(row.original),
791
+ },
792
+ {
793
+ accessorKey: 'warehouse_id',
794
+ header: t(
795
+ 'wms.backend.inventory.reservations.columns.warehouse',
796
+ 'Warehouse',
797
+ ),
798
+ enableSorting: true,
799
+ cell: ({ row }) => formatWarehouseLabel(row.original),
800
+ },
801
+ {
802
+ accessorKey: 'quantity',
803
+ header: t(
804
+ 'wms.backend.inventory.reservations.columns.quantity',
805
+ 'Quantity',
806
+ ),
807
+ enableSorting: true,
808
+ cell: ({ row }) => (
809
+ <span className="tabular-nums">
810
+ {formatInventoryQuantity(row.original.quantity, quantityFormatter)}
811
+ </span>
812
+ ),
813
+ },
814
+ {
815
+ accessorKey: 'source_type',
816
+ header: t(
817
+ 'wms.backend.inventory.reservations.columns.sourceType',
818
+ 'Source type',
819
+ ),
820
+ enableSorting: true,
821
+ cell: ({ row }) => {
822
+ const sourceType = row.original.source_type?.trim()
823
+ if (!sourceType) return '—'
824
+ return inventoryReservationSourceTypeLabel(sourceType, t)
825
+ },
826
+ },
827
+ {
828
+ accessorKey: 'source_id',
829
+ header: t(
830
+ 'wms.backend.inventory.reservations.columns.sourceId',
831
+ 'Source',
832
+ ),
833
+ enableSorting: false,
834
+ cell: ({ row }) => formatReservationSourceLabel(row.original, t),
835
+ },
836
+ {
837
+ accessorKey: 'status',
838
+ header: t(
839
+ 'wms.backend.inventory.reservations.columns.status',
840
+ 'Status',
841
+ ),
842
+ enableSorting: true,
843
+ cell: ({ row }) => {
844
+ const status = row.original.status?.trim()
845
+ if (!status) return '—'
846
+ return (
847
+ <StatusBadge variant={reservationStatusMap[status] ?? 'neutral'} dot>
848
+ {inventoryReservationStatusLabel(status, t)}
849
+ </StatusBadge>
850
+ )
851
+ },
852
+ },
853
+ ],
854
+ [quantityFormatter, t],
855
+ )
856
+
857
+ const rowActions = React.useCallback(
858
+ (row: InventoryReservationRow) => {
859
+ const status = (row.status ?? '').trim().toLowerCase()
860
+ const items = []
861
+ if (access.canAllocate && status === 'active') {
862
+ items.push({
863
+ id: 'allocate',
864
+ label: t('wms.backend.inventory.reservations.actions.allocate', 'Allocate'),
865
+ onSelect: () => void handleAllocate(row),
866
+ })
867
+ }
868
+ if (access.canRelease && status === 'active') {
869
+ items.push({
870
+ id: 'release',
871
+ label: t('wms.backend.inventory.reservations.actions.release', 'Release'),
872
+ destructive: true,
873
+ onSelect: () => openReleaseDialog(row),
874
+ })
875
+ }
876
+ if (items.length === 0) return null
877
+ return <RowActions items={items} />
878
+ },
879
+ [access.canAllocate, access.canRelease, handleAllocate, openReleaseDialog, t],
880
+ )
881
+
882
+ return (
883
+ <>
884
+ <InventoryDataTableSection<InventoryReservationRow>
885
+ sectionQueryKey="reservations"
886
+ endpoint="/api/wms/inventory/reservations"
887
+ titleKey="wms.backend.inventory.reservations.title"
888
+ titleFallback="Inventory reservations"
889
+ descriptionKey="wms.backend.inventory.reservations.description"
890
+ descriptionFallback="Active and historical reservation records created by manual API calls or sales lifecycle automation."
891
+ errorKey="wms.backend.inventory.errors.reservations"
892
+ errorFallback="Failed to load reservations."
893
+ searchKey="wms.backend.inventory.reservations.search"
894
+ searchFallback="Search reservations"
895
+ emptyTitleKey="wms.backend.inventory.reservations.empty.title"
896
+ emptyTitleFallback="No reservations"
897
+ emptyDescriptionKey="wms.backend.inventory.reservations.empty.description"
898
+ emptyDescriptionFallback="Reservations show stock committed to orders, transfers, or manual holds."
899
+ tableId="wms.inventory.reservations"
900
+ entityId={E.wms.inventory_reservation}
901
+ icon={<ShieldCheck className="size-5" />}
902
+ columns={columns}
903
+ rowActions={rowActions}
904
+ warehouseId={warehouseId}
905
+ variantId={variantId}
906
+ lotId={lotId}
907
+ extraParams={activeOnly ? { status: 'active' } : undefined}
908
+ toolbarActions={
909
+ <Button
910
+ type="button"
911
+ variant={activeOnly ? 'outline' : 'ghost'}
912
+ size="sm"
913
+ onClick={() => setActiveOnly((prev) => !prev)}
914
+ >
915
+ {activeOnly
916
+ ? t('wms.backend.inventory.reservations.filter.showAll', 'Show all')
917
+ : t('wms.backend.inventory.reservations.filter.activeOnly', 'Active only')}
918
+ </Button>
919
+ }
920
+ />
921
+ {access.canRelease ? (
922
+ <ReleaseReservationDialog
923
+ open={releaseOpen}
924
+ onOpenChange={setReleaseOpen}
925
+ access={access}
926
+ reservation={releasePreset}
927
+ />
928
+ ) : null}
929
+ </>
930
+ )
931
+ }
932
+
933
+ export function InventoryMovementsSection({
934
+ warehouseId = '',
935
+ variantId = '',
936
+ lotId = '',
937
+ }: {
938
+ warehouseId?: string
939
+ variantId?: string
940
+ lotId?: string
941
+ }) {
942
+ const t = useT()
943
+ const { quantityFormatter, dateTimeFormatter } = useInventoryDisplayFormatters()
944
+
945
+ const columns = React.useMemo<ColumnDef<InventoryMovementRow>[]>(
946
+ () => [
947
+ {
948
+ accessorKey: 'type',
949
+ header: t('wms.backend.inventory.movements.columns.type', 'Type'),
950
+ enableSorting: true,
951
+ cell: ({ row }) => {
952
+ const type = row.original.type?.trim()
953
+ if (!type) return '—'
954
+ return (
955
+ <StatusBadge variant={movementStatusMap[type] ?? 'neutral'} dot>
956
+ {inventoryMovementTypeLabel(type, t)}
957
+ </StatusBadge>
958
+ )
959
+ },
960
+ },
961
+ {
962
+ accessorKey: 'catalog_variant_id',
963
+ header: t(
964
+ 'wms.backend.inventory.movements.columns.variant',
965
+ 'Variant',
966
+ ),
967
+ enableSorting: true,
968
+ cell: ({ row }) => formatVariantLabel(row.original),
969
+ },
970
+ {
971
+ accessorKey: 'warehouse_id',
972
+ header: t(
973
+ 'wms.backend.inventory.movements.columns.warehouse',
974
+ 'Warehouse',
975
+ ),
976
+ enableSorting: true,
977
+ cell: ({ row }) => formatWarehouseLabel(row.original),
978
+ },
979
+ {
980
+ accessorKey: 'location_from_id',
981
+ header: t(
982
+ 'wms.backend.inventory.movements.columns.locationFrom',
983
+ 'From',
984
+ ),
985
+ enableSorting: false,
986
+ cell: ({ row }) =>
987
+ formatLocationLabel(row.original as Record<string, unknown>, 'location_from'),
988
+ },
989
+ {
990
+ accessorKey: 'location_to_id',
991
+ header: t(
992
+ 'wms.backend.inventory.movements.columns.locationTo',
993
+ 'To',
994
+ ),
995
+ enableSorting: false,
996
+ cell: ({ row }) =>
997
+ formatLocationLabel(row.original as Record<string, unknown>, 'location_to'),
998
+ },
999
+ {
1000
+ accessorKey: 'quantity',
1001
+ header: t(
1002
+ 'wms.backend.inventory.movements.columns.quantity',
1003
+ 'Quantity',
1004
+ ),
1005
+ enableSorting: true,
1006
+ cell: ({ row }) => (
1007
+ <span className="tabular-nums">
1008
+ {formatInventoryQuantity(row.original.quantity, quantityFormatter)}
1009
+ </span>
1010
+ ),
1011
+ },
1012
+ {
1013
+ accessorKey: 'reference_type',
1014
+ header: t(
1015
+ 'wms.backend.inventory.movements.columns.referenceType',
1016
+ 'Reference type',
1017
+ ),
1018
+ enableSorting: true,
1019
+ cell: ({ row }) => {
1020
+ const referenceType = row.original.reference_type?.trim()
1021
+ if (!referenceType) return '—'
1022
+ return inventoryReferenceTypeLabel(referenceType, t)
1023
+ },
1024
+ },
1025
+ {
1026
+ accessorKey: 'performed_at',
1027
+ header: t(
1028
+ 'wms.backend.inventory.movements.columns.performedAt',
1029
+ 'Performed at',
1030
+ ),
1031
+ enableSorting: true,
1032
+ cell: ({ row }) => (
1033
+ <span className="text-sm text-muted-foreground">
1034
+ {formatInventoryDateTime(
1035
+ row.original.performed_at || row.original.received_at,
1036
+ dateTimeFormatter,
1037
+ )}
1038
+ </span>
1039
+ ),
1040
+ },
1041
+ ],
1042
+ [dateTimeFormatter, quantityFormatter, t],
1043
+ )
1044
+
1045
+ return (
1046
+ <InventoryDataTableSection<InventoryMovementRow>
1047
+ sectionQueryKey="movements"
1048
+ endpoint="/api/wms/inventory/movements"
1049
+ titleKey="wms.backend.inventory.movements.title"
1050
+ titleFallback="Inventory movement ledger"
1051
+ descriptionKey="wms.backend.inventory.movements.description"
1052
+ descriptionFallback="Immutable movement history for receipts, transfers, adjustments, and cycle counts."
1053
+ errorKey="wms.backend.inventory.errors.movements"
1054
+ errorFallback="Failed to load movements."
1055
+ searchKey="wms.backend.inventory.movements.search"
1056
+ searchFallback="Search movement ledger"
1057
+ emptyTitleKey="wms.backend.inventory.movements.empty.title"
1058
+ emptyTitleFallback="No inventory movements"
1059
+ emptyDescriptionKey="wms.backend.inventory.movements.empty.description"
1060
+ emptyDescriptionFallback="Movement rows are created by receipts, reservations, moves, and reconciliation actions."
1061
+ tableId="wms.inventory.movements"
1062
+ entityId={E.wms.inventory_movement}
1063
+ icon={<Route className="size-5" />}
1064
+ columns={columns}
1065
+ warehouseId={warehouseId}
1066
+ variantId={variantId}
1067
+ lotId={lotId}
1068
+ />
1069
+ )
1070
+ }
1071
+
1072
+ export default function WmsInventoryConsolePage() {
1073
+ const access = useWmsInventoryMutationAccess()
1074
+ const scopeFromUrl = useWmsInventoryScopeFromSearchParams()
1075
+ const [warehouseId, setWarehouseId] = React.useState(scopeFromUrl.warehouseId)
1076
+ const [variantId, setVariantId] = React.useState(scopeFromUrl.catalogVariantId)
1077
+ const [lotId, setLotId] = React.useState(scopeFromUrl.lotId)
1078
+ const [lowStock, setLowStock] = React.useState(scopeFromUrl.lowStock)
1079
+
1080
+ React.useEffect(() => {
1081
+ setWarehouseId(scopeFromUrl.warehouseId)
1082
+ setVariantId(scopeFromUrl.catalogVariantId)
1083
+ setLotId(scopeFromUrl.lotId)
1084
+ setLowStock(scopeFromUrl.lowStock)
1085
+ }, [scopeFromUrl.catalogVariantId, scopeFromUrl.warehouseId, scopeFromUrl.lotId, scopeFromUrl.lowStock])
1086
+
1087
+ const handleWarehouseChange = React.useCallback((next: string) => {
1088
+ setWarehouseId(next)
1089
+ setLotId('')
1090
+ setLowStock(null)
1091
+ }, [])
1092
+
1093
+ const handleVariantChange = React.useCallback((next: string) => {
1094
+ setVariantId(next)
1095
+ setLotId('')
1096
+ setLowStock(null)
1097
+ }, [])
1098
+
1099
+ return (
1100
+ <Page>
1101
+ <PageBody>
1102
+ <div className="space-y-6">
1103
+ <InventoryOperationsSection access={access} />
1104
+ <InventoryScopeBar
1105
+ warehouseId={warehouseId}
1106
+ variantId={variantId}
1107
+ onWarehouseChange={handleWarehouseChange}
1108
+ onVariantChange={handleVariantChange}
1109
+ />
1110
+ <InventoryBalancesSection
1111
+ access={access}
1112
+ warehouseId={warehouseId}
1113
+ variantId={variantId}
1114
+ lotId={lotId}
1115
+ lowStock={lowStock}
1116
+ />
1117
+ <InventoryReservationsSection
1118
+ access={access}
1119
+ warehouseId={warehouseId}
1120
+ variantId={variantId}
1121
+ lotId={lotId}
1122
+ />
1123
+ <InventoryMovementsSection
1124
+ warehouseId={warehouseId}
1125
+ variantId={variantId}
1126
+ lotId={lotId}
1127
+ />
1128
+ </div>
1129
+ </PageBody>
1130
+ </Page>
1131
+ )
1132
+ }