FPSMS-frontend
25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

83 lines
2.7 KiB

  1. /** Keys used to pick one inventory row when an item has multiple stock-UOM buckets. */
  2. export type InventoryBucketPick = {
  3. itemId?: number | null;
  4. itemCode?: string | null;
  5. itemName?: string | null;
  6. uomId?: number | null;
  7. stockUomId?: number | null;
  8. uom?: string | null;
  9. shortUom?: string | null;
  10. stockUom?: string | null;
  11. };
  12. export type InventoryBucketRow = {
  13. itemId?: number | null;
  14. itemCode?: string | null;
  15. itemName?: string | null;
  16. stockUomId?: number | null;
  17. uomCode?: string | null;
  18. uomUdfudesc?: string | null;
  19. uomShortDesc?: string | null;
  20. availableQty?: number | null;
  21. onHandQty?: number | null;
  22. unavailableQty?: number | null;
  23. };
  24. const norm = (s?: string | null) => (s ?? "").trim().toLowerCase();
  25. /** Available = onHand − unavailable. Do not treat 0 as missing (`||` is wrong). */
  26. export function inventoryAvailableQty(inv: InventoryBucketRow): number {
  27. if (inv.availableQty != null && !Number.isNaN(Number(inv.availableQty))) {
  28. return Number(inv.availableQty);
  29. }
  30. return Number(inv.onHandQty ?? 0) - Number(inv.unavailableQty ?? 0);
  31. }
  32. function itemMatches(inv: InventoryBucketRow, pick: InventoryBucketPick): boolean {
  33. if (pick.itemId != null && inv.itemId != null) {
  34. return Number(inv.itemId) === Number(pick.itemId);
  35. }
  36. if (pick.itemCode && inv.itemCode) {
  37. return inv.itemCode === pick.itemCode;
  38. }
  39. if (pick.itemName && inv.itemName) {
  40. return inv.itemName === pick.itemName;
  41. }
  42. return false;
  43. }
  44. function uomMatches(inv: InventoryBucketRow, pick: InventoryBucketPick): boolean {
  45. const pickUomId = pick.stockUomId ?? pick.uomId;
  46. if (pickUomId != null && inv.stockUomId != null) {
  47. return Number(inv.stockUomId) === Number(pickUomId);
  48. }
  49. const labels = [pick.uom, pick.shortUom, pick.stockUom].map(norm).filter(Boolean);
  50. if (labels.length === 0) return true;
  51. const invLabels = [inv.uomUdfudesc, inv.uomShortDesc, inv.uomCode].map(norm);
  52. return labels.some((l) => invLabels.includes(l));
  53. }
  54. export function matchInventoryBucket(
  55. inventories: InventoryBucketRow[],
  56. pick: InventoryBucketPick,
  57. ): InventoryBucketRow | undefined {
  58. const itemHits = inventories.filter((inv) => itemMatches(inv, pick));
  59. if (itemHits.length === 0) return undefined;
  60. const hasUomPick =
  61. pick.stockUomId != null ||
  62. pick.uomId != null ||
  63. [pick.uom, pick.shortUom, pick.stockUom].some((s) => Boolean(norm(s)));
  64. if (hasUomPick) {
  65. return itemHits.find((inv) => uomMatches(inv, pick));
  66. }
  67. return itemHits[0];
  68. }
  69. export function getStockAvailableFromInventories(
  70. inventories: InventoryBucketRow[],
  71. pick: InventoryBucketPick,
  72. ): number {
  73. const inv = matchInventoryBucket(inventories, pick);
  74. return inv ? inventoryAvailableQty(inv) : 0;
  75. }