|
|
import fs from "fs";
|
|
|
|
|
|
{
|
|
|
const path = "src/services/parse/packing-list.ts";
|
|
|
let src = fs.readFileSync(path, "utf8");
|
|
|
|
|
|
if (!src.includes("function isInstructionNoiseRow")) {
|
|
|
const insertAt = src.indexOf("export async function parsePackingListBuffer");
|
|
|
if (insertAt < 0) throw new Error("fn missing");
|
|
|
const helper = `/** Template footer instruction row <20>?not a shipment */
|
|
|
export function isInstructionNoiseRow(raw: Record<string, unknown>): boolean {
|
|
|
const parts = Object.values(raw).map((v) => String(v ?? "").trim());
|
|
|
const blob = parts.join(" ");
|
|
|
if (!blob) return false;
|
|
|
if (/^\u6ce8\u610f\\s*[\\uFF1A:]/.test(blob)) return true;
|
|
|
if (parts.some((p) => /^\u6ce8\u610f\\s*[\\uFF1A:]/.test(p))) return true;
|
|
|
const channel = String(raw.F_Transporter ?? "").trim();
|
|
|
const fba = String(raw.F_FBACode ?? "").trim();
|
|
|
const addr = String(raw.F_Address ?? "").trim();
|
|
|
if (
|
|
|
(channel.length > 40 || fba.length > 40 || addr.length > 80) &&
|
|
|
/\u6ce8\u610f|\u586b\u5199|\u5355\u5143\u683c|\u5fc5\u987b\u586b\u5199/.test(blob)
|
|
|
) {
|
|
|
return true;
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
`;
|
|
|
src = src.slice(0, insertAt) + helper + src.slice(insertAt);
|
|
|
}
|
|
|
|
|
|
if (!src.includes("isInstructionNoiseRow(raw)")) {
|
|
|
src = src.replace(
|
|
|
/\/\/ \u8df3\u8fc7\u7a7a\u884c\r?\n\s*const hasAny = Object\.values\(raw\)\.some\(\r?\n\s*\(v\) => v != null && String\(v\)\.trim\(\) !== "",\r?\n\s*\);\r?\n\s*if \(!hasAny\) continue;/,
|
|
|
`// skip empty / instruction footer rows\n const hasAny = Object.values(raw).some(\n (v) => v != null && String(v).trim() !== "",\n );\n if (!hasAny) continue;\n if (isInstructionNoiseRow(raw)) continue;`,
|
|
|
);
|
|
|
}
|
|
|
|
|
|
if (!src.includes("isInstructionNoiseRow(raw)")) {
|
|
|
throw new Error("failed to insert skip call");
|
|
|
}
|
|
|
fs.writeFileSync(path, src);
|
|
|
console.log("packing-list patched");
|
|
|
}
|
|
|
|
|
|
{
|
|
|
const path = "src/services/parse/channel-map.ts";
|
|
|
let src = fs.readFileSync(path, "utf8");
|
|
|
if (src.includes("text.length > 40")) {
|
|
|
console.log("channel-map already ok");
|
|
|
} else {
|
|
|
src = src.replace(
|
|
|
/export function mapChannel\(raw: string\): \{[\s\S]*?return \{ transporter: text\.slice\(0, 30\), unmapped: true \};\n\}/,
|
|
|
`export function mapChannel(raw: string): {
|
|
|
transporter: string;
|
|
|
unmapped: boolean;
|
|
|
} {
|
|
|
const text = (raw || "").trim();
|
|
|
if (!text) return { transporter: "", unmapped: true };
|
|
|
// Long instruction text must not map (e.g. note mentioning Truck)
|
|
|
if (text.length > 40 || /^\\u6ce8\\u610f\\s*[\\uFF1A:]/.test(text)) {
|
|
|
return { transporter: "", unmapped: true };
|
|
|
}
|
|
|
const upper = text.toUpperCase();
|
|
|
for (const rule of CHANNEL_MAP) {
|
|
|
if (
|
|
|
rule.keys.some((k) => {
|
|
|
const key = k.toUpperCase();
|
|
|
if (/^[A-Z0-9]+$/i.test(k)) {
|
|
|
return new RegExp(\`(?:^|[^A-Z0-9])\${key}(?:[^A-Z0-9]|$)\`).test(upper);
|
|
|
}
|
|
|
return upper.includes(key);
|
|
|
})
|
|
|
) {
|
|
|
return { transporter: rule.value, unmapped: false };
|
|
|
}
|
|
|
}
|
|
|
return { transporter: text.slice(0, 30), unmapped: true };
|
|
|
}`,
|
|
|
);
|
|
|
// Fix the broken unicode escape in the written file - use real chars
|
|
|
src = src.replace(
|
|
|
"if (text.length > 40 || /^\\\\u6ce8\\\\u610f\\\\s*[\\\\uFF1A:]/.test(text))",
|
|
|
"if (text.length > 40 || /^\u6ce8\u610f\\s*[\uFF1A:]/.test(text))",
|
|
|
);
|
|
|
// Also if the replace used the regex with single backslash unicode incorrectly
|
|
|
if (!src.includes("text.length > 40")) {
|
|
|
throw new Error("channel map replace failed");
|
|
|
}
|
|
|
fs.writeFileSync(path, src);
|
|
|
console.log("channel-map patched");
|
|
|
}
|
|
|
}
|