Bläddra i källkod

align the calculation of unit with stock in line data create calculation

production
PC-20260115JRSN\Administrator 2 dagar sedan
förälder
incheckning
49d5ae2fa8
3 ändrade filer med 87 tillägg och 3 borttagningar
  1. +3
    -0
      src/app/api/po/index.ts
  2. +2
    -0
      src/components/PoDetail/PoDetailRow.tsx
  3. +82
    -3
      src/components/PoDetail/stockQtyRound.ts

+ 3
- 0
src/app/api/po/index.ts Visa fil

@@ -60,6 +60,9 @@ export interface StockUomForPoLine {
stockRatioD: number;
purchaseRatioN: number;
purchaseRatioD: number;
sourceRatioN?: number | null;
sourceRatioD?: number | null;
stockQtyInteger?: boolean | null;
}

// DEPRECIATED


+ 2
- 0
src/components/PoDetail/PoDetailRow.tsx Visa fil

@@ -117,6 +117,7 @@ export const PoDetailRow = memo(function PoDetailRow({
orderQty,
Number(row.stockUom?.stockQty ?? 0),
acceptedQty,
row.stockUom,
);

const doSubmit = () => {
@@ -211,6 +212,7 @@ export const PoDetailRow = memo(function PoDetailRow({
Number(row.qty ?? 0),
Number(row.stockUom?.stockQty ?? 0),
enteredBatchQty,
row.stockUom,
);
const convertedStockQtyWithUnit = formatQtyWithStockUom(
convertedStockQty,


+ 82
- 3
src/components/PoDetail/stockQtyRound.ts Visa fil

@@ -7,16 +7,95 @@ export type StockQtyRoundChoice = {
after: number;
};

/** FP-MTMS Version Checklist | Functions Ref. No. 56 | v1.0.4 | 2026-09-10 */
/** Keep in sync with ItemUomService.isIntegerCountStockUom */
const INTEGER_COUNT_UNIT_TOKENS = new Set([
"包",
"箱",
"件",
"個",
"PCS",
"PC",
"CTN",
"EA",
"PK",
"PKT",
"BOX",
]);

export type PoStockQtyConvertHint = {
sourceRatioN?: number | null;
sourceRatioD?: number | null;
stockRatioN?: number | null;
stockRatioD?: number | null;
purchaseRatioN?: number | null;
purchaseRatioD?: number | null;
stockUomCode?: string | null;
stockUomShortDesc?: string | null;
stockQtyInteger?: boolean | null;
};

function toRatio(value: number | null | undefined): number | null {
const n = Number(value);
if (!Number.isFinite(n) || n === 0) return null;
return n;
}

function stockQtyMustBeInteger(hint?: PoStockQtyConvertHint): boolean {
if (hint?.stockQtyInteger === true) return true;
if (hint?.stockQtyInteger === false) return false;
const code = hint?.stockUomCode?.trim() ?? "";
if (code.toUpperCase().startsWith("PCS/CTN")) return true;
const short = hint?.stockUomShortDesc?.trim() ?? "";
return INTEGER_COUNT_UNIT_TOKENS.has(short) || INTEGER_COUNT_UNIT_TOKENS.has(code.toUpperCase());
}

/** Java BigDecimal HALF_UP for non-negative qty. */
function roundHalfUp(value: number, scale: number): number {
const factor = 10 ** scale;
return Math.round(value * factor) / factor;
}

function finalizePoStockQty(raw: number, hint?: PoStockQtyConvertHint): number {
if (!Number.isFinite(raw)) return 0;
return stockQtyMustBeInteger(hint) ? roundHalfUp(raw, 0) : roundHalfUp(raw, 2);
}

/**
* Same ratio path as backend convertQtyToStockQtyPrecise:
* base = qty * sourceRatioN / sourceRatioD
* stock = base * stockRatioD / stockRatioN
*/
function convertPoBatchToStockQty(
batchM18Qty: number,
hint?: PoStockQtyConvertHint,
): number | null {
if (!hint || !Number.isFinite(batchM18Qty)) return null;
const sourceN = toRatio(hint.sourceRatioN) ?? toRatio(hint.purchaseRatioN);
const sourceD = toRatio(hint.sourceRatioD) ?? toRatio(hint.purchaseRatioD);
const stockN = toRatio(hint.stockRatioN);
const stockD = toRatio(hint.stockRatioD);
if (sourceN == null || sourceD == null || stockN == null || stockD == null) return null;
const baseQty = (batchM18Qty * sourceN) / sourceD;
const stockQty = (baseQty * stockD) / stockN;
return finalizePoStockQty(stockQty, hint);
}

/**
* Preview converted stock qty for a 來貨數 batch.
* Matches create-stock-in: ratio convert, then integer HALF_UP for 包/箱/PCS, else 2 decimals.
*/
export function previewPoBatchStockQty(
orderM18Qty: number,
orderStockQty: number,
batchM18Qty: number,
hint?: PoStockQtyConvertHint,
): number {
const fromRatios = convertPoBatchToStockQty(batchM18Qty, hint);
if (fromRatios != null) return fromRatios;
if (!Number.isFinite(orderM18Qty) || orderM18Qty === 0) {
return Number(batchM18Qty.toFixed(2));
return finalizePoStockQty(batchM18Qty, hint);
}
return Number(((batchM18Qty * orderStockQty) / orderM18Qty).toFixed(2));
return finalizePoStockQty((batchM18Qty * orderStockQty) / orderM18Qty, hint);
}

export function isNotIntegerQty(qty: number): boolean {


Laddar…
Avbryt
Spara