diff --git a/scripts/admin-test-env-modules/finance-full-fixtures-contract.test.mjs b/scripts/admin-test-env-modules/finance-full-fixtures-contract.test.mjs index 3b1ab096e..73ae3735e 100644 --- a/scripts/admin-test-env-modules/finance-full-fixtures-contract.test.mjs +++ b/scripts/admin-test-env-modules/finance-full-fixtures-contract.test.mjs @@ -31,13 +31,15 @@ test('fee writes use verified Firstleg paths and do not fabricate historical IDs assert.match(source, /chargeUnit === 'CBM'/) }) -test('PAYABLE fee selection requires the formal flag and a Finance-supported supplier category', () => { +test('PAYABLE fee selection uses the formal feeCode and feeTypeName fields', () => { const selector = source.slice(source.indexOf('async function loadFeeItem'), source.indexOf('async function createManualFee')) assert.match(selector, /item\.warehousePayable === true/) assert.match(selector, /supplierCategoryForFeeItem\(item\)/) - assert.equal(supplierCategoryForFeeItem({ feeCode: 'FBA_HEAD_FREIGHT', feeName: '头程运费' }), 'BOOKING') - assert.equal(supplierCategoryForFeeItem({ code: 'CUSTOMS_CLEARANCE', name: '报关费' }), 'CUSTOMS') - assert.equal(supplierCategoryForFeeItem({ feeCode: 'UNSUPPORTED', feeName: '操作费' }), null) + assert.equal(supplierCategoryForFeeItem({ feeCode: 'FBA_HEAD_FREIGHT', feeTypeName: '头程运费' }), 'BOOKING') + assert.equal(supplierCategoryForFeeItem({ feeCode: 'FEE_TYPE_001', feeTypeName: '拖车费2' }), 'TRUCKING') + assert.equal(supplierCategoryForFeeItem({ feeCode: 'CUSTOMS_CLEARANCE', feeTypeName: '报关费' }), 'CUSTOMS') + assert.equal(supplierCategoryForFeeItem({ feeCode: 'UNSUPPORTED', feeTypeName: '操作费' }), null) + assert.equal(supplierCategoryForFeeItem({ feeCode: 'FEE_TYPE_001', feeName: '拖车费2' }), null) }) test('Finance PAYABLE fixture creates an isolated ACTIVE supplier through the formal API', () => { diff --git a/scripts/admin-test-env-modules/finance-full-fixtures.mjs b/scripts/admin-test-env-modules/finance-full-fixtures.mjs index 8923454bf..49ebdba0e 100644 --- a/scripts/admin-test-env-modules/finance-full-fixtures.mjs +++ b/scripts/admin-test-env-modules/finance-full-fixtures.mjs @@ -28,7 +28,7 @@ export function normalizeFinanceFixtureSources(sources, orders, allocations, cus } export function supplierCategoryForFeeItem(item) { - const value = `${item?.feeCode ?? item?.code ?? ''} ${item?.feeName ?? item?.name ?? ''}`.toUpperCase() + const value = `${item?.feeCode ?? ''} ${item?.feeTypeName ?? ''}`.toUpperCase() if (['TRUCK', 'TRAILER', 'DRAYAGE', '拖车', '卡车'].some((candidate) => value.includes(candidate))) return 'TRUCKING' if (['CUSTOMS', 'CLEARANCE', 'DECLARATION', '报关', '清关'].some((candidate) => value.includes(candidate))) return 'CUSTOMS' if (['CERTIFICATE', 'CERTIFICATION', 'CERT_', '认证', '证书'].some((candidate) => value.includes(candidate))) return 'CERTIFICATE' @@ -406,7 +406,7 @@ export async function run(context) { const payableSupplierCategory = supplierCategoryForFeeItem(feeItems.PAYABLE) const supplier = await loadSupplier(context, payableSupplierCategory) if (!supplier?.id) blocker(context, '取得 Warehouse PAYABLE 供应商', - `未找到 ACTIVE ${payableSupplierCategory || 'SUPPORTED'} 供应商或匹配正式类别的 PAYABLE 费用项;不能伪造 supplierId/partnerId`) + `未找到 ACTIVE ${payableSupplierCategory || 'SUPPORTED'} 供应商或 feeCode + feeTypeName 无法映射正式类别的 PAYABLE 费用项;不能伪造 supplierId/partnerId`) for (const direction of ['RECEIVABLE', 'PAYABLE']) { if (!feeItems[direction]) context.addCoverageGap(MODULE, `missing-${direction.toLowerCase()}-fee-item`, `${direction} 人工费用 fixture`, `没有找到带 warehouse${direction === 'RECEIVABLE' ? 'Receivable' : 'Payable'}=true 的 ACTIVE 费用项目,禁止猜测费用项目 ID`, 'missing-deployed-fee-item') @@ -465,11 +465,14 @@ export async function run(context) { supplierName: supplier?.name || null, supplierCategory: supplier?.category || payableSupplierCategory, containerBookingId: id(container.containerBookingId), containerNo: container.containerNo || null, loadingPlanId: id(container.loadingPlanId) } - } else { + } else if (!containerOwned) { context.addStep(MODULE, '取得同 marker 柜次用于整柜费用分摊', null, false, '没有同 marker 的已完成柜次和可核验 containerBookingId;不读取历史柜次、不猜测柜号', OPTIONS) context.addCoverageGap(MODULE, 'container-shared-cost-allocation', '按柜添加费用并按订单/运单体积分摊', '当前上下文没有同 marker 的已完成柜次及真实 Firstleg 体积快照,禁止借用历史柜次或模拟 Finance 分摊成功', 'no-marker-owned-container-snapshot') + } else { + context.addCoverageGap(MODULE, 'container-shared-cost-allocation', '按柜添加费用并按订单/运单体积分摊', + '已识别同 marker 的 COMPLETED 柜次,但 PAYABLE 费用项目无法按 feeCode + feeTypeName 映射类别或缺少 ACTIVE 供应商', 'missing-payable-supplier') } if (containerFixture) fixtures.push(containerFixture)