FPSMS-frontend
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

317 line
11 KiB

  1. import type {
  2. ItemLotTraceLocationBlock,
  3. ItemLotTraceResponse,
  4. } from "@/app/api/itemTracing";
  5. import { blockWarehouseCode } from "./buildLocationBlockGraphNodes";
  6. export type ScopedRow<T> = T & {
  7. scopeWarehouseCode: string;
  8. inventoryLotId: number;
  9. };
  10. export const primaryWarehouseLabel = (data: ItemLotTraceResponse): string =>
  11. data.warehouseLines
  12. .map((w) => w.warehouseCode)
  13. .filter(Boolean)
  14. .join(" / ") || "—";
  15. const sortByTimestampDesc = <T extends { timestamp?: string | null }>(
  16. rows: T[],
  17. ): T[] =>
  18. [...rows].sort((a, b) =>
  19. (b.timestamp ?? "").localeCompare(a.timestamp ?? ""),
  20. );
  21. const sortByReceiptDateDesc = <T extends { receiptDate?: string | null }>(
  22. rows: T[],
  23. ): T[] =>
  24. [...rows].sort((a, b) =>
  25. (b.receiptDate ?? "").localeCompare(a.receiptDate ?? ""),
  26. );
  27. export const mergeScopedOrigins = (data: ItemLotTraceResponse) => {
  28. const primaryWh = primaryWarehouseLabel(data);
  29. const primary = data.origins.map((o) => ({
  30. ...o,
  31. scopeWarehouseCode: primaryWh,
  32. inventoryLotId: data.lot.inventoryLotId,
  33. }));
  34. const fromBlocks = (data.locationBlocks ?? []).flatMap((block) => {
  35. const wh = blockWarehouseCode(block);
  36. return block.origins.map((o) => ({
  37. ...o,
  38. scopeWarehouseCode: wh,
  39. inventoryLotId: block.inventoryLotId,
  40. }));
  41. });
  42. return sortByReceiptDateDesc([...primary, ...fromBlocks]);
  43. };
  44. export const mergeScopedQcResults = (data: ItemLotTraceResponse) => {
  45. const primaryWh = primaryWarehouseLabel(data);
  46. const primary = data.qcResults.map((q, i) => ({
  47. ...q,
  48. scopeWarehouseCode: primaryWh,
  49. inventoryLotId: data.lot.inventoryLotId,
  50. rowKey: `primary-${q.stockInLineId}-${i}`,
  51. }));
  52. const fromBlocks = (data.locationBlocks ?? []).flatMap((block) => {
  53. const wh = blockWarehouseCode(block);
  54. return block.qcResults.map((q, i) => ({
  55. ...q,
  56. scopeWarehouseCode: wh,
  57. inventoryLotId: block.inventoryLotId,
  58. rowKey: `loc-${block.inventoryLotId}-${q.stockInLineId}-${i}`,
  59. }));
  60. });
  61. return [...primary, ...fromBlocks].sort((a, b) =>
  62. (b.created ?? "").localeCompare(a.created ?? ""),
  63. );
  64. };
  65. export const mergeScopedOutboundUsage = (data: ItemLotTraceResponse) => {
  66. const primaryWh = primaryWarehouseLabel(data);
  67. const primary = data.outboundUsage.map((u) => ({
  68. ...u,
  69. scopeWarehouseCode: primaryWh,
  70. inventoryLotId: data.lot.inventoryLotId,
  71. }));
  72. const fromBlocks = (data.locationBlocks ?? []).flatMap((block) => {
  73. const wh = blockWarehouseCode(block);
  74. return block.outboundUsage.map((u) => ({
  75. ...u,
  76. scopeWarehouseCode: wh,
  77. inventoryLotId: block.inventoryLotId,
  78. }));
  79. });
  80. return sortByTimestampDesc([...primary, ...fromBlocks]);
  81. };
  82. export const mergeScopedStockTakeEvents = (data: ItemLotTraceResponse) => {
  83. const primaryWh = primaryWarehouseLabel(data);
  84. const primary = data.stockTakeEvents.map((e, i) => ({
  85. ...e,
  86. scopeWarehouseCode: e.warehouseCode?.trim() || primaryWh,
  87. inventoryLotId: data.lot.inventoryLotId,
  88. rowKey: `primary-${e.stockTakeCode}-${i}`,
  89. }));
  90. const fromBlocks = (data.locationBlocks ?? []).flatMap((block) => {
  91. const wh = blockWarehouseCode(block);
  92. return block.stockTakeEvents.map((e, i) => ({
  93. ...e,
  94. scopeWarehouseCode: e.warehouseCode?.trim() || wh,
  95. inventoryLotId: block.inventoryLotId,
  96. rowKey: `loc-${block.inventoryLotId}-${e.stockTakeCode}-${i}`,
  97. }));
  98. });
  99. return sortByTimestampDesc([...primary, ...fromBlocks]);
  100. };
  101. export const mergeScopedAdjustments = (data: ItemLotTraceResponse) => {
  102. const primaryWh = primaryWarehouseLabel(data);
  103. const primary = data.adjustments.map((a, i) => ({
  104. ...a,
  105. scopeWarehouseCode: primaryWh,
  106. inventoryLotId: data.lot.inventoryLotId,
  107. rowKey: `primary-${a.refCode}-${i}`,
  108. }));
  109. const fromBlocks = (data.locationBlocks ?? []).flatMap((block) => {
  110. const wh = blockWarehouseCode(block);
  111. return block.adjustments.map((a, i) => ({
  112. ...a,
  113. scopeWarehouseCode: wh,
  114. inventoryLotId: block.inventoryLotId,
  115. rowKey: `loc-${block.inventoryLotId}-${a.refCode}-${i}`,
  116. }));
  117. });
  118. return sortByTimestampDesc([...primary, ...fromBlocks]);
  119. };
  120. export const mergeScopedTransfers = (data: ItemLotTraceResponse) => {
  121. const primaryWh = primaryWarehouseLabel(data);
  122. const primary = data.transfers.map((tr, i) => ({
  123. ...tr,
  124. scopeWarehouseCode: primaryWh,
  125. inventoryLotId: data.lot.inventoryLotId,
  126. rowKey: `primary-${tr.transferCode}-${i}`,
  127. }));
  128. const fromBlocks = (data.locationBlocks ?? []).flatMap((block) => {
  129. const wh = blockWarehouseCode(block);
  130. return block.transfers.map((tr, i) => ({
  131. ...tr,
  132. scopeWarehouseCode: wh,
  133. inventoryLotId: block.inventoryLotId,
  134. rowKey: `loc-${block.inventoryLotId}-${tr.transferCode}-${i}`,
  135. }));
  136. });
  137. return sortByTimestampDesc([...primary, ...fromBlocks]);
  138. };
  139. export const mergeScopedMovements = (data: ItemLotTraceResponse) => {
  140. const primaryWh = primaryWarehouseLabel(data);
  141. const primary = data.movements.map((m, i) => ({
  142. ...m,
  143. scopeWarehouseCode: m.warehouseCode?.trim() || primaryWh,
  144. inventoryLotId: data.lot.inventoryLotId,
  145. rowKey: `primary-mov-${i}-${m.refCode}`,
  146. }));
  147. const fromBlocks = (data.locationBlocks ?? []).flatMap((block) => {
  148. const wh = blockWarehouseCode(block);
  149. return block.movements.map((m, i) => ({
  150. ...m,
  151. scopeWarehouseCode: m.warehouseCode?.trim() || wh,
  152. inventoryLotId: block.inventoryLotId,
  153. rowKey: `loc-${block.inventoryLotId}-mov-${i}-${m.refCode}`,
  154. }));
  155. });
  156. return sortByTimestampDesc([...primary, ...fromBlocks]);
  157. };
  158. export const mergeScopedPutawayEvents = (data: ItemLotTraceResponse) => {
  159. const primaryWh = primaryWarehouseLabel(data);
  160. const primary = data.putawayEvents.map((p, i) => ({
  161. ...p,
  162. scopeWarehouseCode: p.warehouseCode?.trim() || primaryWh,
  163. inventoryLotId: data.lot.inventoryLotId,
  164. rowKey: `primary-putaway-${i}-${p.refCode}`,
  165. }));
  166. const fromBlocks = (data.locationBlocks ?? []).flatMap((block) => {
  167. const wh = blockWarehouseCode(block);
  168. return block.putawayEvents.map((p, i) => ({
  169. ...p,
  170. scopeWarehouseCode: p.warehouseCode?.trim() || wh,
  171. inventoryLotId: block.inventoryLotId,
  172. rowKey: `loc-${block.inventoryLotId}-putaway-${i}-${p.refCode}`,
  173. }));
  174. });
  175. return sortByTimestampDesc([...primary, ...fromBlocks]);
  176. };
  177. export const mergeScopedPurchaseEvents = (data: ItemLotTraceResponse) => {
  178. const primaryWh = primaryWarehouseLabel(data);
  179. const primary = (data.purchaseEvents ?? []).map((p, i) => ({
  180. ...p,
  181. scopeWarehouseCode: primaryWh,
  182. inventoryLotId: data.lot.inventoryLotId,
  183. rowKey: `primary-po-${i}-${p.purchaseOrderLineId}`,
  184. }));
  185. const fromBlocks = (data.locationBlocks ?? []).flatMap((block) => {
  186. const wh = blockWarehouseCode(block);
  187. return (block.purchaseEvents ?? []).map((p, i) => ({
  188. ...p,
  189. scopeWarehouseCode: wh,
  190. inventoryLotId: block.inventoryLotId,
  191. rowKey: `loc-${block.inventoryLotId}-po-${i}-${p.purchaseOrderLineId}`,
  192. }));
  193. });
  194. return [...primary, ...fromBlocks].sort((a, b) =>
  195. (b.orderDate ?? "").localeCompare(a.orderDate ?? ""),
  196. );
  197. };
  198. export const mergeScopedDoDeliveries = (data: ItemLotTraceResponse) => {
  199. const primaryWh = primaryWarehouseLabel(data);
  200. const primary = data.doDeliveries.map((d, i) => ({
  201. ...d,
  202. scopeWarehouseCode: primaryWh,
  203. inventoryLotId: data.lot.inventoryLotId,
  204. rowKey: `primary-do-${i}-${d.stockOutLineId}`,
  205. }));
  206. const fromBlocks = (data.locationBlocks ?? []).flatMap((block) => {
  207. const wh = blockWarehouseCode(block);
  208. return block.doDeliveries.map((d, i) => ({
  209. ...d,
  210. scopeWarehouseCode: wh,
  211. inventoryLotId: block.inventoryLotId,
  212. rowKey: `loc-${block.inventoryLotId}-do-${i}-${d.stockOutLineId}`,
  213. }));
  214. });
  215. return sortByTimestampDesc([...primary, ...fromBlocks]);
  216. };
  217. export const mergeScopedFailEvents = (data: ItemLotTraceResponse) => {
  218. const primaryWh = primaryWarehouseLabel(data);
  219. const primary = data.failEvents.map((f, i) => ({
  220. ...f,
  221. scopeWarehouseCode: primaryWh,
  222. inventoryLotId: data.lot.inventoryLotId,
  223. rowKey: `primary-fail-${i}-${f.failId}`,
  224. timestamp: f.recordDate,
  225. }));
  226. const fromBlocks = (data.locationBlocks ?? []).flatMap((block) => {
  227. const wh = blockWarehouseCode(block);
  228. return block.failEvents.map((f, i) => ({
  229. ...f,
  230. scopeWarehouseCode: wh,
  231. inventoryLotId: block.inventoryLotId,
  232. rowKey: `loc-${block.inventoryLotId}-fail-${i}-${f.failId}`,
  233. timestamp: f.recordDate,
  234. }));
  235. });
  236. return sortByTimestampDesc([...primary, ...fromBlocks]);
  237. };
  238. export const mergeScopedReturnEvents = (data: ItemLotTraceResponse) => {
  239. const primaryWh = primaryWarehouseLabel(data);
  240. const primary = data.returnEvents.map((r, i) => ({
  241. ...r,
  242. scopeWarehouseCode: r.warehouseCode?.trim() || primaryWh,
  243. inventoryLotId: data.lot.inventoryLotId,
  244. rowKey: `primary-return-${i}-${r.stockOutLineId}`,
  245. }));
  246. const fromBlocks = (data.locationBlocks ?? []).flatMap((block) => {
  247. const wh = blockWarehouseCode(block);
  248. return block.returnEvents.map((r, i) => ({
  249. ...r,
  250. scopeWarehouseCode: r.warehouseCode?.trim() || wh,
  251. inventoryLotId: block.inventoryLotId,
  252. rowKey: `loc-${block.inventoryLotId}-return-${i}-${r.stockOutLineId}`,
  253. }));
  254. });
  255. return sortByTimestampDesc([...primary, ...fromBlocks]);
  256. };
  257. export const mergeScopedOpenMovements = (data: ItemLotTraceResponse) => {
  258. const primaryWh = primaryWarehouseLabel(data);
  259. const primary = data.openMovements.map((o, i) => ({
  260. ...o,
  261. scopeWarehouseCode: o.warehouseCode?.trim() || primaryWh,
  262. inventoryLotId: data.lot.inventoryLotId,
  263. rowKey: `primary-open-${i}-${o.stockInLineId}`,
  264. }));
  265. const fromBlocks = (data.locationBlocks ?? []).flatMap((block) => {
  266. const wh = blockWarehouseCode(block);
  267. return block.openMovements.map((o, i) => ({
  268. ...o,
  269. scopeWarehouseCode: o.warehouseCode?.trim() || wh,
  270. inventoryLotId: block.inventoryLotId,
  271. rowKey: `loc-${block.inventoryLotId}-open-${i}-${o.stockInLineId}`,
  272. }));
  273. });
  274. return sortByTimestampDesc([...primary, ...fromBlocks]);
  275. };
  276. export const mergeScopedReplenishmentEvents = (data: ItemLotTraceResponse) => {
  277. const primaryWh = primaryWarehouseLabel(data);
  278. const primary = (data.replenishmentEvents ?? []).map((r, i) => ({
  279. ...r,
  280. scopeWarehouseCode: primaryWh,
  281. inventoryLotId: data.lot.inventoryLotId,
  282. rowKey: `primary-replenish-${i}-${r.replenishmentId}`,
  283. }));
  284. const fromBlocks = (data.locationBlocks ?? []).flatMap((block) => {
  285. const wh = blockWarehouseCode(block);
  286. return (block.replenishmentEvents ?? []).map((r, i) => ({
  287. ...r,
  288. scopeWarehouseCode: wh,
  289. inventoryLotId: block.inventoryLotId,
  290. rowKey: `loc-${block.inventoryLotId}-replenish-${i}-${r.replenishmentId}`,
  291. }));
  292. });
  293. return sortByTimestampDesc([...primary, ...fromBlocks]);
  294. };
  295. export const hasMultipleLocations = (data: ItemLotTraceResponse): boolean =>
  296. (data.locationBlocks?.length ?? 0) > 0 ||
  297. (data.alternateLocations?.length ?? 0) > 0;