Files
shanxia2.0/web-admin/scripts/pl-plan-contract-diff.test.ts
T
wuzilongandCursor bde19c0644 feat: 锁定执行中计划并按装卸明细差量归档库存,补齐派车日历数量与酸碱类型。
执行中的运输计划不再随意改单;库存流水记到装卸产品明细;手工派车日历区分酸碱并记录数量。同时收紧合同附件与子表的差量保存,并清理已废弃的 bak 备份目录。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-29 07:59:53 +08:00

57 lines
3.8 KiB
TypeScript

import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import test from 'node:test'
import { parse } from '@vue/compiler-sfc'
import ts from 'typescript'
import * as stopForms from '../src/utils/pl-plan-stop-form'
import { collectStopContractIds } from '../src/utils/pl-plan-contract-product'
const source = parse(readFileSync(new URL('../src/components/business/pl-plan-form-dialog/index.vue', import.meta.url), 'utf8')).descriptor.scriptSetup!.content
const ast = ts.createSourceFile('plan.ts', source, ts.ScriptTarget.Latest, true)
function contractWatcher(side: 'load' | 'delivery', bindings: Record<string, unknown>) {
const statement = ast.statements.find((node) => {
if (!ts.isExpressionStatement(node) || !ts.isCallExpression(node.expression)) return false
const call = node.expression
return call.expression.getText(ast) === 'watch' && (side === 'load'
? call.arguments[0].getText(ast) === '() => loadForm.value.contracts'
: call.arguments[0].getText(ast).includes('contractKey: collectStopContractIds'))
}) as ts.ExpressionStatement
assert.ok(statement, 'actual contract watcher exists')
const callback = (statement.expression as ts.CallExpression).arguments[1].getText(ast)
return new Function(...Object.keys(bindings), ts.transpile(`let loadContractFetchSeq = 0; const run = ${callback};`) + ';return run')(...Object.values(bindings))
}
for (const mobile of [false, true]) {
for (const side of ['load', 'delivery'] as const) {
for (const change of ['add', 'remove', 'clear'] as const) {
test(`${mobile ? 'H5' : 'desktop'} ${side} ${change} contracts keeps unaffected row identities`, async () => {
const kept = { id: 'row-a', planProductId: 'pp-a', contractId: 'a', contractProductId: 'line-a', productId: 'sku', inboundQty: 3, archiveRemark: 'preserve', planQty: 8 }
const removed = { id: 'row-b', planProductId: 'pp-b', contractId: 'b', productId: 'sku', planQty: 6 }
const legacy = { id: 'row-old', planProductId: 'pp-old', productId: 'legacy', planQty: 2 }
const before = ['a', 'b']
const after = change === 'add' ? ['a', 'b', 'c'] : change === 'remove' ? ['a'] : []
const stop = { id: 'stop', contracts: after.map(contractId => ({ contractId })), products: [kept, removed, legacy] }
const loadForm = { value: stop }
const deliveryForms = { value: [stop] }
const run = contractWatcher(side, {
isPageVariant: { value: mobile }, pageReady: { value: true }, pageLoadGeneration: 1,
detailLoading: { value: false }, suppressStopProductSync: { value: false },
loadForm, deliveryForms, collectStopContractIds, mobileStopIdentity: () => 1,
deliveryContractFetchSeqByIndex: {}, loadContractProductIds: { value: [] },
contractProductIdsCache: { value: {} }, fetchContractProductIds: async () => {},
isPageLoadCurrent: () => true, unionContractProductIds: () => ['sku', 'legacy'],
filterProductsByAllowedProductIds: (rows: unknown[]) => rows, syncFromLoadProducts: () => {},
retainStopProductsForContracts: (stopForms as any).retainStopProductsForContracts
})
if (side === 'load') await run(stop.contracts, before.map(contractId => ({ contractId })))
else await run([{ identity: mobile ? 1 : undefined, contractKey: after.join(',') }], [{ identity: mobile ? 1 : undefined, contractKey: before.join(',') }])
const products = side === 'load' ? loadForm.value.products : deliveryForms.value[0].products
const expected = change === 'add' ? [kept, removed, legacy] : change === 'remove' ? [kept] : []
assert.deepEqual(products, expected)
expected.forEach((row, index) => assert.equal(products[index], row, 'keep original row object and all saved fields'))
})
}
}
}