Files
LWLT-AI/archive/handoff/2026-07-12/legacy-erp-handoff/tools/erp_split_child_execute.js
2026-07-13 19:57:46 +08:00

178 lines
7.3 KiB
JavaScript

const fs = require('fs');
const path = require('path');
const { chromium } = require('playwright');
const ROOT = path.resolve(__dirname, '..');
const PROFILE = path.join(ROOT, 'diagnostics', 'erp-playwright-profile');
const RUN_ID = new Date().toISOString().replace(/[-:T.Z]/g, '').slice(0, 14);
const RUN_DIR = path.join(ROOT, 'diagnostics', 'split-child-execute', `run-${RUN_ID}`);
const ORDER = {
parentGroup: 'LW-260704A-A',
date: '2026-07-04',
customerName: 'LW云南七彩金桥国旅(昆明景腾)',
customerId: '778',
pax: { adult: 5, childBed: 0, childNoBed: 0, infant: 0, leader: 0 },
prices: { adult: 500, childBed: 0, childNoBed: 0, infant: 0, leader: 0 },
op: '测试',
sales: '琳琳',
origin: '云南',
note: '测试散拼子单,自动化测试后可删除。',
};
fs.mkdirSync(RUN_DIR, { recursive: true });
function log(message, data) {
console.log(`[${new Date().toISOString()}] ${message}${data === undefined ? '' : ` ${JSON.stringify(data)}`}`);
}
async function setField(scope, selector, value) {
const locator = scope.locator(selector).first();
await locator.waitFor({ state: 'attached', timeout: 15000 });
await locator.fill(String(value));
await locator.evaluate((el) => {
el.dispatchEvent(new Event('input', { bubbles: true }));
el.dispatchEvent(new Event('change', { bubbles: true }));
el.dispatchEvent(new Event('blur', { bubbles: true }));
});
}
async function setDomValue(frame, id, value) {
await frame.evaluate(([fieldId, fieldValue]) => {
const el = document.getElementById(fieldId) || document.querySelector(`[name="${fieldId}"]`);
if (!el) throw new Error(`field not found: ${fieldId}`);
el.value = String(fieldValue);
el.dispatchEvent(new Event('input', { bubbles: true }));
el.dispatchEvent(new Event('change', { bubbles: true }));
el.dispatchEvent(new Event('blur', { bubbles: true }));
}, [id, value]);
}
async function queryParent(page) {
await page.goto('https://ltjt.yunzhi.run/System/Business/plan.asp', { waitUntil: 'domcontentloaded', timeout: 30000 });
await page.waitForTimeout(900);
await setField(page, '#S_chufariqi', ORDER.date);
await setField(page, '#S_chufarizhi', ORDER.date);
await setField(page, '#S_tuanxuhao', ORDER.parentGroup);
await page.locator('#SearchButton').click();
await page.waitForTimeout(2500);
}
async function getChildFrame(page) {
for (let i = 0; i < 60; i += 1) {
const frame = page.frames().find((f) => f.url().includes('/Business/plan_order.asp'));
if (frame) return frame;
await page.waitForTimeout(500);
}
throw new Error('plan_order frame not found');
}
async function readAllText(page) {
const texts = [];
for (const frame of page.frames()) {
const text = await frame.evaluate(() => document.body.innerText.replace(/\s+/g, ' ').trim()).catch(() => '');
if (text) texts.push(text);
}
return texts.join('\n');
}
function extractChildOrderNumber(text) {
const matches = text.match(/D\d{4,}/g) || [];
return matches.length ? matches[matches.length - 1] : null;
}
async function clickDialogOk(page) {
const selectors = ['button:has-text("确定")', 'input[value="确定"]', 'a:has-text("确定")', '.aui_state_highlight'];
for (const selector of selectors) {
const locator = page.locator(selector).first();
if (await locator.count().catch(() => 0)) {
await locator.click().catch(() => {});
await page.waitForTimeout(500);
return true;
}
}
return false;
}
async function main() {
log('run-start', { RUN_DIR, ORDER });
const context = await chromium.launchPersistentContext(PROFILE, {
headless: false,
viewport: { width: 1920, height: 1000 },
acceptDownloads: true,
});
const page = context.pages()[0] || await context.newPage();
page.setDefaultTimeout(15000);
try {
await queryParent(page);
await page.screenshot({ path: path.join(RUN_DIR, '01-parent-before.png'), fullPage: false });
const beforeListText = await readAllText(page);
fs.writeFileSync(path.join(RUN_DIR, '01-parent-before.txt'), beforeListText, 'utf8');
await page.locator('a:has-text("拼单")').first().click();
const frame = await getChildFrame(page);
await frame.waitForSelector('#zutuanshe', { timeout: 30000 });
await setDomValue(frame, 'zutuanshe', ORDER.customerName);
await setDomValue(frame, 'zutuansheid', ORDER.customerId);
await setDomValue(frame, 'gendanren', ORDER.op);
await setDomValue(frame, 'xiaoshouren', ORDER.sales);
await setDomValue(frame, 'keyuandi', ORDER.origin);
await setDomValue(frame, 'xiadanbeizhu', ORDER.note);
await setDomValue(frame, 'darenshu', ORDER.pax.adult);
await setDomValue(frame, 'xiaorenshu', ORDER.pax.childBed);
await setDomValue(frame, 'ertrenshu', ORDER.pax.childNoBed);
await setDomValue(frame, 'yingrenshu', ORDER.pax.infant);
await setDomValue(frame, 'quanrenshu', ORDER.pax.leader);
const rows = [
{ row: 0, qty: ORDER.pax.adult, price: ORDER.prices.adult },
{ row: 1, qty: ORDER.pax.childBed, price: ORDER.prices.childBed },
{ row: 2, qty: ORDER.pax.childNoBed, price: ORDER.prices.childNoBed },
{ row: 3, qty: ORDER.pax.infant, price: ORDER.prices.infant },
];
for (const item of rows) {
await setDomValue(frame, `ys_danwei${item.row}`, ORDER.customerName).catch(() => {});
await setDomValue(frame, `ys_danweiid${item.row}`, ORDER.customerId).catch(() => {});
await setDomValue(frame, `ys_shuliang${item.row}`, item.qty).catch(() => {});
await setDomValue(frame, `ys_danjia${item.row}`, item.price).catch(() => {});
await setDomValue(frame, `ys_jine${item.row}`, item.qty * item.price).catch(() => {});
}
await page.waitForTimeout(1000);
await page.screenshot({ path: path.join(RUN_DIR, '02-before-save.png'), fullPage: false });
const beforeSave = await readAllText(page);
fs.writeFileSync(path.join(RUN_DIR, '02-before-save.txt'), beforeSave, 'utf8');
await frame.locator('#SubmitButton, [name="SubmitButton"]').first().click();
await page.waitForTimeout(4000);
await page.screenshot({ path: path.join(RUN_DIR, '03-after-save.png'), fullPage: false });
const afterSave = await readAllText(page);
fs.writeFileSync(path.join(RUN_DIR, '03-after-save.txt'), afterSave, 'utf8');
const childOrder = extractChildOrderNumber(afterSave);
if (!childOrder) throw new Error(`could not capture child order number: ${afterSave.slice(0, 800)}`);
await clickDialogOk(page);
await queryParent(page);
await page.screenshot({ path: path.join(RUN_DIR, '04-parent-after.png'), fullPage: false });
const afterListText = await readAllText(page);
fs.writeFileSync(path.join(RUN_DIR, '04-parent-after.txt'), afterListText, 'utf8');
const receivedOk = afterListText.includes(ORDER.parentGroup) && afterListText.includes('成人5');
const result = { runDir: RUN_DIR, parentGroup: ORDER.parentGroup, date: ORDER.date, childOrder, receivedOk };
fs.writeFileSync(path.join(RUN_DIR, 'result.json'), JSON.stringify(result, null, 2), 'utf8');
log('run-complete', result);
} catch (error) {
log('error', { message: error.message, stack: error.stack });
await page.screenshot({ path: path.join(RUN_DIR, 'error.png'), fullPage: false }).catch(() => {});
throw error;
} finally {
await context.close();
}
}
main().catch((error) => {
console.error(error);
process.exit(1);
});