import assert from 'node:assert/strict' import test from 'node:test' import { readFileSync } from 'node:fs' import { parse } from '@vue/compiler-sfc' import ts from 'typescript' import { ref, nextTick } from 'vue' import * as stops from '../src/utils/pl-plan-stop-form' import * as persistence from '../src/views/m/pl/plan/mobilePlanPersistence' // Evaluate current working-tree handlers, with only external APIs/UI services stubbed. const source = parse( readFileSync( new URL('../src/components/business/pl-plan-form-dialog/index.vue', import.meta.url), 'utf8' ) ).descriptor.scriptSetup!.content const tree = ts.createSourceFile('form.ts', source, ts.ScriptTarget.Latest, true) const names = [ 'loadPlanForEdit', 'loadPlanForCopy', 'applyStopsFromPlan', 'enrichStopFormsDisplayFromPlanProducts', 'bootstrapStopsFromPlanProducts', 'mirrorPlanProductsFromLoad', 'mapPlanProductToFormRow', 'mapPlanProductToCopyFormRow', 'mapProductsToSubmitPayload', 'buildPlanCommonPayload', 'handleSubmit' ] const code = names .map((name) => { const node = tree.statements.find( (node) => ts.isFunctionDeclaration(node) && node.name?.text === name ) assert.ok(node, name) return node.getText(tree) }) .join('\n') const clone = (value: any) => JSON.parse(JSON.stringify(value)) const line = { productId: 'sku', productNo: 'SKU', productName: '产品', unit: '吨', packageSpec: '桶', model: 'M', planQty: 5, quantity: 5 } function harness(mobile: boolean) { const noop = () => {}, yes = () => true, identity = async (rows: any[]) => rows let saved: any = { id: 'plan', mobileProductMetaVersion: 1, products: [{ ...line, id: 'pp' }], load: { id: 'load', products: [{ ...line, id: 'lp', planProductId: 'pp' }] }, deliveries: [{ id: 'delivery', seq: 1, products: [{ ...line, id: 'dp', planProductId: 'pp' }] }] } const writes: any[] = [], events: any[] = [] const b: any = { ...stops, ...persistence, nextTick, isPageVariant: ref(mobile), props: { planId: 'plan' }, isEdit: ref(true), pageReady: ref(true), pageLoadGeneration: 1, assertPageLoadCurrent: noop, isPageLoadCurrent: yes, isPageSubmitCurrent: yes, suppressAddressMutex: ref(false), suppressStopProductSync: ref(false), stopFormsReady: ref(false), formData: { products: [] }, loadForm: ref(stops.createEmptyLoadForm()), deliveryForms: ref([]), resetForm: noop, validateLoadedPagePlan: noop, formatDate: () => '', todayYmd: () => '2026-10-27', resolveCurrentUserId: () => 'u', resolveCurrentUserName: () => '', syncCommittedPartyIds: noop, loadedTransportStatus: ref(''), loadedFollowStatus: ref(''), loadedPlanSnapshot: ref(null), enrichPlanProductsHazardFromCrm: identity, enrichPlanProductLinesAlias: identity, freightCalc: { resetFreightCalc: noop }, restoreFreightFromTransport: noop, selectedDriverId: ref(''), selectedEscortId: ref(''), vehicleBundle: ref(null), syncSelectedCustomerOptions: noop, reapplyStopDibangByVehiclePlate: noop, prepareLineAliasAndBrandForSubmit: noop, resolveLineProductName: (name: string) => name, buildTransportBlock: () => undefined, mobileMetadata: { ready: yes }, mobileSavedPlanId: ref(''), mobileCreateUncertain: ref(false), submitLoading: ref(false), basicFormRef: ref({ validate: async () => true }), validateBusinessRules: yes, prefetchStopContractProducts: noop, validateSingleContractPerStop: yes, validateContractPackageSpecs: yes, validateStopContractsRequired: yes, pickBxData: (x: any) => x, showPlanSaveWarnings: noop, ElMessage: { warning: noop, error: noop, success: noop }, innerVisible: ref(true), emit: (...args: any[]) => events.push(args), planApi: { get: async () => clone(saved), update: async (_id: string, payload: any) => { writes.push(clone(payload)) // Server returns generated identities for genuinely new rows. saved = { ...clone(payload), id: 'plan', mobileProductMetaVersion: 1 } saved.products.forEach((row: any, i: number) => { row.id ||= `pp-new-${i}` }) const resolve = (row: any) => saved.products.find((pp: any) => pp.clientKey === row.planProductClientKey)?.id || row.planProductId saved.load.id ||= 'load-new' saved.load.products.forEach((row: any, i: number) => { row.id ||= `lp-new-${i}` row.planProductId = resolve(row) }) saved.deliveries.forEach((stop: any, i: number) => { stop.id ||= `delivery-new-${i}` stop.products.forEach((row: any, j: number) => { row.id ||= `dp-new-${i}-${j}` row.planProductId = resolve(row) }) }) return clone(saved) } } } const api = new Function( ...Object.keys(b), ts.transpile(code, { target: ts.ScriptTarget.ESNext }) + `;return {${names.join(',')}}` )(...Object.values(b)) return { b, api, writes, events, saved: () => saved } } for (const mobile of [false, true]) { const platform = mobile ? 'H5' : 'Web' test(`${platform} actual edit/save/reopen/save keeps all existing and freshly assigned child IDs`, async () => { const h = harness(mobile) await h.api.loadPlanForEdit('plan') h.b.loadForm.value.products.push({ ...line, packageSpec: '袋', id: undefined, planProductId: undefined }) h.b.deliveryForms.value.push({ ...stops.createEmptyDeliveryForm(2), products: [{ ...line, packageSpec: '袋' }] }) await h.api.handleSubmit('save') assert.equal(h.b.innerVisible.value, false) assert.deepEqual(h.events.at(-1), ['success', { planId: 'plan', arranged: false }]) const saved = clone(h.saved()) await h.api.loadPlanForEdit('plan') await h.api.handleSubmit('save') const payload = h.writes[1] assert.deepEqual( payload.products.map((p: any) => p.id), saved.products.map((p: any) => p.id) ) assert.equal(payload.load.id, saved.load.id) assert.deepEqual( payload.load.products.map((p: any) => p.id), saved.load.products.map((p: any) => p.id) ) assert.deepEqual( payload.deliveries.map((d: any) => [d.id, ...d.products.map((p: any) => p.id)]), saved.deliveries.map((d: any) => [d.id, ...d.products.map((p: any) => p.id)]) ) assert.ok(payload.load.products.every((p: any) => p.planProductId)) assert.ok(payload.deliveries.every((d: any) => d.products.every((p: any) => p.planProductId))) }) test(`${platform} actual copy handler strips source parent/product IDs from submit payload`, async () => { const h = harness(mobile) await h.api.loadPlanForCopy('plan') const payload = h.api.buildPlanCommonPayload() assert.equal(payload.load.id, undefined) assert.ok(payload.products.every((p: any) => !p.id)) assert.ok(payload.load.products.every((p: any) => !p.id && !p.planProductId)) assert.ok( payload.deliveries.every( (d: any) => !d.id && d.products.every((p: any) => !p.id && !p.planProductId) ) ) }) }