ForgeFlow-ERP/frontend/scripts/test-inventory-ledger-options.mjs
2026-06-12 16:29:05 +08:00

561 lines
23 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { buildGenericItemOptions, buildProductOperationOptions } from "../src/utils/inventoryOperationOptions.js";
const stockedBalances = [
{
item_id: 101,
item_code: "原材料00001",
item_name: "304不锈钢",
warehouse_id: 1,
warehouse_type: "RAW",
weight_available_kg: 100,
qty_available: 0,
avg_unit_cost: 4
},
{
item_id: 101,
item_code: "原材料00001",
item_name: "304不锈钢",
warehouse_id: 1,
warehouse_type: "RAW",
weight_available_kg: 40,
qty_available: 0,
avg_unit_cost: 5
},
{
item_id: 102,
item_code: "原材料00002",
item_name: "冷轧板",
warehouse_id: 1,
warehouse_type: "RAW",
weight_available_kg: 0,
qty_available: 0
},
{
item_id: 103,
item_code: "原材料00003",
item_name: "铝合金",
warehouse_id: 2,
warehouse_type: "RAW",
weight_available_kg: 80,
qty_available: 0
}
];
const rawOutboundOptions = buildGenericItemOptions({
direction: "out",
activeInventoryTab: "raw",
activeWarehouseType: "RAW",
warehouseId: 1,
balances: stockedBalances,
materials: [],
products: []
});
assert.deepEqual(
rawOutboundOptions.map((item) => ({
item_id: item.item_id,
item_code: item.item_code,
item_name: item.item_name,
weight_available_kg: item.weight_available_kg
})),
[
{
item_id: 101,
item_code: "原材料00001",
item_name: "304不锈钢",
weight_available_kg: 140
}
]
);
const rawInboundOptions = buildGenericItemOptions({
direction: "in",
activeInventoryTab: "raw",
activeWarehouseType: "RAW",
warehouseId: 1,
balances: stockedBalances,
materials: [{ item_id: 201, item_code: "原材料00004", item_name: "铜板" }],
products: [{ item_id: 301, item_code: "产品00001", item_name: "支架" }]
});
assert.deepEqual(rawInboundOptions.map((item) => item.item_id), [201]);
const finishedOutboundOptions = buildGenericItemOptions({
direction: "out",
activeInventoryTab: "finished",
activeWarehouseType: "FINISHED",
warehouseId: 8,
balances: [
{
item_id: 401,
item_code: "产品需规00001",
item_name: "电脑支架",
warehouse_id: 8,
warehouse_type: "FINISHED",
qty_available: 12,
weight_available_kg: 30
}
],
materials: [],
products: []
});
assert.equal(finishedOutboundOptions.length, 1);
assert.equal(finishedOutboundOptions[0].qty_available, 12);
const semiOutboundOptions = buildGenericItemOptions({
direction: "out",
activeInventoryTab: "semi",
activeWarehouseType: "SEMI",
warehouseId: 9,
balances: [
{
item_id: 501,
item_code: "产品需规00002",
item_name: "半成品支架",
source_line_id: 9101,
option_key: "501::9101",
operation_label: "产品半成品支架已完成工序第1道 冲压",
warehouse_id: 9,
warehouse_type: "SEMI",
qty_available: 6,
weight_available_kg: 12
},
{
item_id: 501,
item_code: "产品需规00002",
item_name: "半成品支架",
source_line_id: 9102,
option_key: "501::9102",
operation_label: "产品半成品支架已完成工序第2道 折弯",
warehouse_id: 9,
warehouse_type: "SEMI",
qty_available: 8,
weight_available_kg: 20
}
],
materials: [],
products: []
});
assert.equal(semiOutboundOptions.length, 2);
assert.deepEqual(semiOutboundOptions.map((item) => item.option_key), ["501::9101", "501::9102"]);
const productMasterRows = [{ item_id: 601, item_code: "产品需规00003", item_name: "委外成品" }];
const productOperationRows = [
{
option_key: "601::7001",
item_id: 601,
item_code: "产品需规00003",
item_name: "委外成品",
route_id: 8001,
route_operation_id: 7001,
seq_no: 1,
operation_name: "冲压"
},
{
option_key: "601::7002",
item_id: 601,
item_code: "产品需规00003",
item_name: "委外成品",
route_id: 8001,
route_operation_id: 7002,
seq_no: 2,
operation_name: "折弯"
}
];
const routeOperationOptions = buildProductOperationOptions([
{
route_id: 8001,
product_item_id: 601,
product_code: "产品需规00003",
product_name: "委外成品",
version_no: "V1.0",
status: "ACTIVE",
operations: [
{
route_operation_id: 7001,
seq_no: 1,
operation_name: "1",
process_name: "1",
stamping_method: "切边冲孔",
status: "ACTIVE"
}
]
}
]);
assert.equal(routeOperationOptions[0].operation_label, "第1道 切边冲孔");
assert.equal(routeOperationOptions[0].source_material_summary, "产品委外成品已完成工序第1道 切边冲孔");
const operationWithoutStampingMethod = buildProductOperationOptions([
{
route_id: 8002,
product_item_id: 602,
product_code: "产品需规00004",
product_name: "无冲压方式产品",
version_no: "V1.0",
status: "ACTIVE",
operations: [
{
route_operation_id: 7003,
seq_no: 1,
operation_name: "1",
process_name: "开平",
stamping_method: "",
status: "ACTIVE"
}
]
}
]);
assert.equal(operationWithoutStampingMethod[0].operation_label, "第1道");
const semiInboundOptions = buildGenericItemOptions({
direction: "in",
activeInventoryTab: "semi",
activeWarehouseType: "SEMI",
warehouseId: 9,
balances: [],
materials: [],
products: productMasterRows,
productOperations: productOperationRows
});
const finishedInboundOptions = buildGenericItemOptions({
direction: "in",
activeInventoryTab: "finished",
activeWarehouseType: "FINISHED",
warehouseId: 8,
balances: [],
materials: [],
products: productMasterRows
});
assert.deepEqual(semiInboundOptions.map((item) => item.option_key), ["601::7001", "601::7002"]);
assert.deepEqual(semiInboundOptions.map((item) => item.route_operation_id), [7001, 7002]);
assert.deepEqual(finishedInboundOptions.map((item) => item.item_id), [601]);
const auxiliaryOutboundOptions = buildGenericItemOptions({
direction: "out",
activeInventoryTab: "auxiliary",
activeWarehouseType: "AUX",
warehouseId: 10,
balances: [
{
item_id: 701,
item_code: "辅料00001",
item_name: "包装膜",
warehouse_id: 10,
warehouse_type: "AUX",
qty_available: 0,
weight_available_kg: 25
}
],
materials: [],
products: []
});
const auxiliaryInboundOptions = buildGenericItemOptions({
direction: "in",
activeInventoryTab: "auxiliary",
activeWarehouseType: "AUX",
warehouseId: 10,
balances: [],
materials: [{ item_id: 702, item_code: "辅料00002", item_name: "手套" }],
products: []
});
assert.equal(auxiliaryOutboundOptions.length, 1);
assert.equal(auxiliaryOutboundOptions[0].item_id, 701);
assert.deepEqual(auxiliaryInboundOptions.map((item) => item.item_id), [702]);
const scrapInboundOptions = buildGenericItemOptions({
direction: "in",
activeInventoryTab: "scrap",
activeWarehouseType: "SCRAP",
warehouseId: 11,
balances: [],
materials: [{ item_id: 801, item_code: "原材料00005", item_name: "冲压边角料" }],
products: [{ item_id: 901, item_code: "产品需规00004", item_name: "报废支架" }]
});
const scrapOutboundOptions = buildGenericItemOptions({
direction: "out",
activeInventoryTab: "scrap",
activeWarehouseType: "SCRAP",
warehouseId: 11,
balances: [
{
item_id: 801,
item_code: "原材料00005",
item_name: "冲压边角料",
warehouse_id: 11,
warehouse_type: "SCRAP",
qty_available: 10,
weight_available_kg: 0
},
{
item_id: 901,
item_code: "产品需规00004",
item_name: "报废支架",
warehouse_id: 11,
warehouse_type: "SCRAP",
qty_available: 0,
weight_available_kg: 42
},
{
item_id: 902,
item_code: "产品需规00005",
item_name: "异仓废料",
warehouse_id: 12,
warehouse_type: "SCRAP",
qty_available: 0,
weight_available_kg: 18
}
],
materials: [],
products: []
});
const scrapWarehouseTypeInboundOptions = buildGenericItemOptions({
direction: "in",
activeInventoryTab: "raw",
activeWarehouseType: "SCRAP",
warehouseId: 11,
balances: [],
materials: [{ item_id: 803, item_code: "原材料00006", item_name: "废铜" }],
products: [{ item_id: 903, item_code: "产品需规00006", item_name: "报废夹具" }]
});
assert.deepEqual(scrapInboundOptions.map((item) => item.item_id), [801, 901]);
assert.deepEqual(scrapWarehouseTypeInboundOptions.map((item) => item.item_id), [803, 903]);
assert.deepEqual(
scrapOutboundOptions.map((item) => ({
item_id: item.item_id,
qty_available: item.qty_available,
weight_available_kg: item.weight_available_kg
})),
[
{
item_id: 901,
qty_available: 0,
weight_available_kg: 42
}
]
);
const inventoryLedgerSource = readFileSync(
fileURLToPath(new URL("../src/views/InventoryLedgerView.vue", import.meta.url)),
"utf8"
);
const deliveryManagementSource = readFileSync(
fileURLToPath(new URL("../src/views/DeliveryManagementView.vue", import.meta.url)),
"utf8"
);
const tableControlsSource = readFileSync(
fileURLToPath(new URL("../src/components/TableControls.vue", import.meta.url)),
"utf8"
);
const stockLotTagSelectSource = readFileSync(
fileURLToPath(new URL("../src/components/StockLotTagSelect.vue", import.meta.url)),
"utf8"
);
assert.match(inventoryLedgerSource, /key: "scrap"/);
assert.match(inventoryLedgerSource, /label: "废料库"/);
assert.match(inventoryLedgerSource, /PRODUCTION_SCRAP_IN/);
assert.match(
inventoryLedgerSource,
/const genericUnitCostLabel = computed\(\(\) =>[\s\S]*isProductionScrapInbound\.value[\s\S]*原材料成本单价\(元\/kg\)/,
"production scrap inbound should label unit cost as raw material cost unit price"
);
assert.match(
inventoryLedgerSource,
/const productionScrapToleranceRemainingWeight = computed\(\(\) => \{[\s\S]*productionScrapExpectedWeight\.value[\s\S]*1\.15[\s\S]*productionScrapInboundBeforeWeight/,
"production scrap inbound should calculate the non-settle upper limit from cumulative scrap inbound weight"
);
assert.match(
inventoryLedgerSource,
/genericForm\.settle_work_order && productionScrapSettleNeedsRemark\.value/,
"production scrap inbound should require a settle explanation when final scrap weight is outside tolerance"
);
assert.match(
inventoryLedgerSource,
/const scrapToleranceMax = Number\(productionScrapToleranceRemainingWeight\.value \|\| 0\);[\s\S]*inboundWeight > scrapToleranceMax/,
"production scrap inbound validation should compare inbound weight against cumulative upper-limit remaining amount"
);
assert.match(
inventoryLedgerSource,
/activeOperation\?\.direction !== 'out' && !isCustomerSuppliedInbound && !isSourceLotReturnInbound && !isSemiSourceLotTraceInbound && !isFinishedOutsourcingInbound && !isProductionScrapInbound && !isReworkScrapInbound && !isOutsourcingScrapInbound/,
"production scrap inbound should hide the manual batch number field"
);
assert.match(
inventoryLedgerSource,
/lot_no: isCustomerSuppliedInbound\.value \? null : isProductionScrapInbound\.value \? null[\s\S]*isOutsourcingScrapInbound\.value \? null/,
"production scrap inbound should never submit a manually-entered batch number"
);
assert.match(inventoryLedgerSource, /OUTSOURCING_SCRAP_IN/);
assert.match(inventoryLedgerSource, /SCRAP_SALE_OUT/);
assert.match(inventoryLedgerSource, /LOGISTICS_REQUIRED_BIZ_TYPES/);
assert.match(inventoryLedgerSource, /"CUSTOMER_SUPPLIED"/);
assert.match(inventoryLedgerSource, /const isCustomerSuppliedInbound = computed/);
assert.match(inventoryLedgerSource, /customerSuppliedLotNoPreview/);
assert.match(inventoryLedgerSource, /提交后自动生成/);
assert.match(inventoryLedgerSource, /activeOperation\?\.direction !== 'out' && !isCustomerSuppliedInbound/);
assert.match(inventoryLedgerSource, /lot_no: isCustomerSuppliedInbound\.value \? null/);
assert.match(inventoryLedgerSource, /source_material_sub_batch_no: isCustomerSuppliedInbound\.value\s+\?\s+null/);
assert.match(inventoryLedgerSource, /const workOrderLedgerRows = ref\(\[\]\);/);
assert.match(inventoryLedgerSource, /fetchResource\("\/production\/work-order-ledger\?limit=500", \[\]\)/);
assert.match(inventoryLedgerSource, /workOrderLedgerRows\.value = workOrderLedgerRowData;/);
assert.match(
stockLotTagSelectSource,
/issued_weight_kg:\s*normalizeSelectedIssuedWeight\(lot\)/,
"StockLotTagSelect should preserve an option's issued_weight_kg instead of overwriting it with remaining weight"
);
assert.match(
stockLotTagSelectSource,
/function displayWeight\(lot\)/,
"StockLotTagSelect should support scenario-specific selected-tag weight display"
);
assert.match(
inventoryLedgerSource,
/:display-weight-field="isProductionSurplusInbound \? 'issued_weight_kg' : 'remaining_weight_kg'"/,
"production surplus inbound should display selected source lots by work-order issued weight"
);
assert.doesNotMatch(
inventoryLedgerSource,
/<div v-if="usesGenericSourceLotSelector && !isProductTraceOnlySourceLotInbound" class="feedback-banner">/,
"source-lot return/outbound live total should not use feedback-banner because it triggers toast notifications while typing"
);
assert.doesNotMatch(
inventoryLedgerSource,
/<div v-else-if="isProductTraceOnlySourceLotInbound && selectedGenericSourceLots\.length" class="feedback-banner">/,
"source-lot trace-only live summary should not use feedback-banner because it is not an operation result"
);
assert.match(
inventoryLedgerSource,
/function computeWorkOrderSourceLotReturnLimit\(workOrder,\s*material\)/,
"production surplus inbound should calculate source-lot return limit from the work-order surplus limit"
);
assert.match(
inventoryLedgerSource,
/max_return_weight_kg:\s*returnLimit\.normal/,
"source-lot normal return weight should be capped by the work-order normal surplus amount"
);
assert.match(
inventoryLedgerSource,
/tolerance_return_weight_kg:\s*returnLimit\.tolerance/,
"source-lot tolerance return weight should be capped by the work-order 15 percent tolerance amount"
);
assert.match(
inventoryLedgerSource,
/const productionSurplusToleranceRemainingWeight = computed\(\(\) => \{[\s\S]*productionSurplusExpectedReturnWeight\.value[\s\S]*1\.15[\s\S]*productionSurplusReturnedBeforeWeight/,
"production surplus inbound should calculate the non-settle upper limit from cumulative returned weight"
);
assert.match(
inventoryLedgerSource,
/:max="productionSurplusReturnInputMax\(lot\)"/,
"production surplus inbound input should use a dynamic return max so settle mode can allow explained deviations"
);
assert.match(
inventoryLedgerSource,
/function productionSurplusReturnInputMax\(lot\)[\s\S]*genericForm\.settle_work_order[\s\S]*source_remaining_weight_kg/,
"production surplus inbound should relax the input max to source-lot remaining return weight when settling"
);
assert.match(
inventoryLedgerSource,
/const toleranceMax = Number\(productionSurplusToleranceRemainingWeight\.value \|\| 0\);[\s\S]*genericSourceLotReturnWeight\.value > toleranceMax/,
"production surplus inbound validation should compare the selected return total against the cumulative upper-limit remaining amount"
);
assert.match(
inventoryLedgerSource,
/const receivableWorkOrders = computed\(\(\) =>\s*workOrderLedgerRows\.value\.filter\([\s\S]*!isWorkOrderSettled\(item\)[\s\S]*tolerance_finished_inbound_qty[\s\S]*\)\s*\);/
);
assert.match(
inventoryLedgerSource,
/completionForm\.tolerance_receivable_qty = Number\(workOrder\.limits\?\.tolerance_finished_inbound_qty \|\| completionForm\.max_receivable_qty \|\| 0\);/
);
assert.match(
inventoryLedgerSource,
/const genericWorkOrderOptions = computed\(\(\) => \{[\s\S]*!isWorkOrderSettled\(item,\s*"PRODUCTION_SCRAP_IN"\)[\s\S]*tolerance_scrap_inbound_weight_kg[\s\S]*!isWorkOrderSettled\(item,\s*"PRODUCTION_SURPLUS"\)[\s\S]*tolerance_surplus_inbound_weight_kg[\s\S]*return workOrderLedgerRows\.value\.filter\(\(item\) => !isWorkOrderSettled\(item\)\);[\s\S]*\}\);/
);
assert.match(
inventoryLedgerSource,
/source_doc_type: isProductionSurplusInbound\.value \? "PRODUCTION_LEDGER" : null[\s\S]*source_doc_id: isProductionSurplusInbound\.value \? Number\(selectedGenericWorkOrder\.value\.production_ledger_id \|\| selectedGenericWorkOrder\.value\.work_order_id\) : null/
);
assert.match(
inventoryLedgerSource,
/source_doc_type: isProductionScrapInbound\.value \? "PRODUCTION_LEDGER" : isReworkScrapInbound\.value \? "WORK_ORDER" : activeWarehouseType\.value === "SEMI" \? "PRODUCT_ROUTE_OPERATION" : null[\s\S]*isProductionScrapInbound\.value[\s\S]*Number\(selectedGenericWorkOrder\.value\.production_ledger_id \|\| selectedGenericWorkOrder\.value\.work_order_id\)[\s\S]*isReworkScrapInbound\.value[\s\S]*Number\(selectedGenericWorkOrder\.value\.work_order_id\)/
);
assert.match(inventoryLedgerSource, /:key="`inventory-main-table-\$\{activeInventoryTab\}`"/);
assert.match(inventoryLedgerSource, /:key="`inventory-controls-\$\{activeInventoryTab\}`"/);
assert.match(inventoryLedgerSource, /balanceControls\.sortKey\.value === "operation_label"/);
assert.match(tableControlsSource, /hasEnhancedButton/);
assert.match(tableControlsSource, /currentHeaderLabel/);
assert.match(inventoryLedgerSource, /const isSelectedFinishedWarehouse = computed/);
assert.match(inventoryLedgerSource, /const selectedPrimaryAvailableLabel = computed/);
assert.match(inventoryLedgerSource, /const selectedPrimaryOnHandLabel = computed/);
assert.match(inventoryLedgerSource, /const selectedAuxiliaryMetricLabel = computed/);
assert.match(inventoryLedgerSource, /function formatSelectedUnitCost/);
assert.match(inventoryLedgerSource, /selectedWarehouseType\.value === "FINISHED"[\s\S]*return "\/件"/);
assert.match(inventoryLedgerSource, /import DrawerDataTable from "\.\.\/components\/DrawerDataTable\.vue"/);
assert.match(inventoryLedgerSource, /const overviewLotDrawerColumns = computed/);
assert.match(inventoryLedgerSource, /const lotDrawerColumns = computed/);
assert.match(inventoryLedgerSource, /const txnDrawerColumns = computed/);
assert.match(inventoryLedgerSource, /key: "inbound_qty"[\s\S]*label: "入库数量"/);
assert.match(inventoryLedgerSource, /key: "auxiliary_weight"[\s\S]*label: "辅助重量\(kg\)"/);
assert.match(inventoryLedgerSource, /v-model:expanded-rows="expandedLotRows"/);
assert.match(inventoryLedgerSource, /v-model:expanded-rows="expandedTxnRows"/);
assert.match(inventoryLedgerSource, /"OUTSOURCING_SURPLUS"/);
assert.match(inventoryLedgerSource, /"OUTSOURCING_IN"/);
assert.match(inventoryLedgerSource, /"OUTSOURCING_SCRAP_IN"/);
assert.match(inventoryLedgerSource, /"OUTSOURCING_OUT"/);
assert.match(inventoryLedgerSource, /"SCRAP_SALE_OUT"/);
assert.match(inventoryLedgerSource, /v-model\.trim="genericForm\.waybill_no"/);
assert.match(inventoryLedgerSource, /v-model\.number="genericForm\.freight_amount"/);
assert.match(inventoryLedgerSource, /handleLogisticsPhotoUpload\(genericForm/);
assert.match(inventoryLedgerSource, /order_photo_url: genericRequiresLogistics\.value \? genericForm\.order_photo_url/);
assert.match(inventoryLedgerSource, /v-model\.trim="deliveryForm\.waybill_no"/);
assert.match(inventoryLedgerSource, /v-model\.number="deliveryForm\.freight_amount"/);
assert.match(inventoryLedgerSource, /handleLogisticsPhotoUpload\(deliveryForm/);
assert.match(inventoryLedgerSource, /order_photo_url: deliveryForm\.order_photo_url/);
assert.match(deliveryManagementSource, /发货台账/);
assert.match(deliveryManagementSource, /交付查询/);
assert.doesNotMatch(deliveryManagementSource, /v-model\.trim="form\.waybill_no"/);
assert.doesNotMatch(deliveryManagementSource, /v-model\.number="form\.freight_amount"/);
assert.doesNotMatch(deliveryManagementSource, /handleLogisticsPhotoUpload/);
assert.doesNotMatch(deliveryManagementSource, /waybill_no: form\.waybill_no/);
assert.doesNotMatch(deliveryManagementSource, /order_photo_url: form\.order_photo_url/);
assert.doesNotMatch(deliveryManagementSource, /freight_amount: Number\(form\.freight_amount\)/);
assert.match(inventoryLedgerSource, /fetchResource\("\/master-data\/materials\?limit=200", \[\]\)/);
assert.match(inventoryLedgerSource, /fetchResource\("\/master-data\/products\?limit=200", \[\]\)/);
assert.match(inventoryLedgerSource, /fetchResource\("\/sales\/customers\?limit=200", \[\]\)/);
assert.doesNotMatch(inventoryLedgerSource, /fetchResource\("\/master-data\/materials\?limit=500", \[\]\)/);
assert.doesNotMatch(inventoryLedgerSource, /fetchResource\("\/master-data\/products\?limit=500", \[\]\)/);
assert.doesNotMatch(inventoryLedgerSource, /fetchResource\("\/sales\/customers\?limit=500", \[\]\)/);
assert.match(inventoryLedgerSource, /const isSemiSourceLotTraceInbound = computed/);
assert.match(inventoryLedgerSource, /\["WIP_IN", "OUTSOURCING_IN"\]\.includes/);
assert.match(inventoryLedgerSource, /const isSemiAutoSourceOutbound = computed/);
assert.match(inventoryLedgerSource, /\["WIP_OUT", "OUTSOURCING_OUT"\]\.includes/);
assert.match(inventoryLedgerSource, /const semiAutoSourceOutboundSourceLots = computed/);
assert.match(inventoryLedgerSource, /入库数量/);
assert.match(inventoryLedgerSource, /出库数量/);
assert.match(inventoryLedgerSource, /入库类型/);
assert.match(inventoryLedgerSource, /按件入库/);
assert.match(inventoryLedgerSource, /按重入库/);
assert.match(inventoryLedgerSource, /const selectedSemiMeasureBasis = computed/);
assert.match(inventoryLedgerSource, /function formatSemiAvailability/);
assert.match(inventoryLedgerSource, /function formatSemiMeasureValue/);
assert.match(inventoryLedgerSource, /委外方/);
assert.match(inventoryLedgerSource, /outsourcing_party_name/);
assert.match(inventoryLedgerSource, /getProductBomMaterialItemIds\(genericForm\.item_id\)/);
assert.match(inventoryLedgerSource, /:allow-unavailable="isSourceLotReturnInbound \|\| isSemiSourceLotTraceInbound \|\| isOutsourcingScrapInbound"/);
assert.match(inventoryLedgerSource, /const inboundQty = activeWarehouseType\.value === "AUX" \|\| isFinishedOutsourcingInbound\.value[\s\S]*\? Number\(genericForm\.qty \|\| 0\)[\s\S]*: isSemiSourceLotTraceInbound\.value && selectedSemiMeasureBasis\.value === "PIECE"/);
assert.match(inventoryLedgerSource, /outbound_qty: activeWarehouseType\.value === "AUX"[\s\S]*\? Number\(genericForm\.qty \|\| 0\)[\s\S]*: isSemiAutoSourceOutbound\.value && selectedSemiMeasureBasis\.value === "PIECE"/);
assert.match(inventoryLedgerSource, /const selectedTraceSourceLotIds = isProductTraceOnlySourceLotInbound\.value[\s\S]*selectedGenericSourceLots\.value\.map/);
assert.match(inventoryLedgerSource, /source_lot_ids: selectedTraceSourceLotIds/);
assert.match(inventoryLedgerSource, /semiAutoSourceOutboundSourceLotSummary/);
assert.match(inventoryLedgerSource, /仅用于来源追溯,不扣减原材料库存/);
console.log("inventory ledger option tests passed");