修改备注没有带入的问题

This commit is contained in:
andy committed 2026-09-29 15:43:15 +08:00
1 parent c2c71d35af
commit 2f0e6582d5
25 files changed
+5104 -30

No files matched your search

@@ -11,9 +11,14 @@ Chrome Manifest V3 扩展,在用户已登录的 LTJT ERP 页面内执行经过
## 当前版本
当前源码版本为 `0.5.190`。版本化 ZIP、文件哈希和 Skill/DOCX 基线见 [`../../dist/release-manifest.json`](../../dist/release-manifest.json)。旧版本实现流水已冻结在 [`../../archive/project-history/2026-08-16/chrome-extension-README.pre-governance.md`](../../archive/project-history/2026-08-16/chrome-extension-README.pre-governance.md)。
当前源码版本为 `0.5.191`。版本化 ZIP、文件哈希和 Skill/DOCX 基线见 [`../../dist/release-manifest.json`](../../dist/release-manifest.json)。旧版本实现流水已冻结在 [`../../archive/project-history/2026-08-16/chrome-extension-README.pre-governance.md`](../../archive/project-history/2026-08-16/chrome-extension-README.pre-governance.md)。
0.5.190 当前重点:
0.5.191 当前重点:
- 酒店新增未填备注时保留选中酒店原生联动备注,明确填写时使用用户备注;在资源选择后、日期/数量回调前冻结,保存前校验及写后回查均验证。默认备注回查使用 v2 证据,旧 v1 回查保持兼容。单条与批量逐条适用;变更未填备注仍保留原记录。仅本地合成验证,真实 ERP 验收待部署方完成。
- 需更新插件和平台最低版本至 0.5.191 并刷新平台/ERP 页面;无新增数据库迁移。
继续保留 0.5.190 的同团批量酒店新增:
- 同团 2—10 条酒店新增由程序解析为批量信封,插件按输入顺序复用单条原生保存/回查;首次失败或不确定即停止,逐条保存成功前缀、失败/待核验项与未执行项。每条确认成功才进入账号隔离的安排账本和团队进度;整批成功回执包含逐条结果及当前安排。只读回查只核验不确定项,不自动继续剩余安排。禁止跨团、混合动作、批量变更、整批自动重试。
- 需同步更新控制面、平台和插件 0.5.190,无新增数据库迁移。单条输入及原生资源匹配规则保持;本地合成验证通过,真实 ERP 验收待部署方完成。
@@ -6677,7 +6677,7 @@
}
window.LTJTOrderAssistant = {
version: '0.5.190',
version: '0.5.191',
resolveNativeListSearchValues,
lookupKeywordMatchesText,
inspectLifecycleSearchCriteria: lifecycleSearchCriteria,
@@ -651,6 +651,13 @@
return sha256(JSON.stringify([operation.action, operation.data.existing_refs, arrangement]));
}
function usesNativeHotelRemark(operation) {
// Public input only permits a nonempty explicit remark. Resolution also
// normalizes an omitted remark to '', so both forms mean inherit here.
return operation.action === 'arrangement_hotel' && operation.data?.arrangement?.mode === 'create'
&& !normalize(operation.data.arrangement.remark);
}
async function arrangementCreateEvidence(form, operation) {
const rows = [];
for (const index of arrangementRowIndexes(form)) {
@@ -660,7 +667,7 @@
const preserved = createPreservedFields(form, operation);
const preservedProof = await Promise.all(preserved.map(async (field) => ({ name: field.name, date: Boolean(field.date),
sha256: await sha256(field.date ? dateYyyyMD(field.expected) : normalize(field.expected)) })));
return { version: 1, slot_index: operation.data.arrangement.target.slot_index,
return { version: usesNativeHotelRemark(operation) ? 2 : 1, slot_index: operation.data.arrangement.target.slot_index,
operation_key: await arrangementCreateOperationKey(operation), existing_rows: rows,
...(operation.action === 'arrangement_other' ? { preserved_fields: preservedProof } : {}), value_redacted: true };
}
@@ -2318,11 +2325,13 @@
}
const linkedFields = ['danwei0', 'danweiid0', 'fangxing0', 'diqu0', 'dianhua0', 'bizhong0', 'danjia0', 'fangshi0'].map((name) => arrangementFieldName(operation, name));
const before = Object.fromEntries(linkedFields.map((name) => [name, controlValue(getControl(form, name))]));
let nativeRemark;
try {
// SetValToObj is the same ERP-native mapping used by a real hotel-row
// click. It fills the read-only room type and all linked fields.
selectBox.SetVal = setVal;
selectBox.SetValToObj(selected.candidate.raw || selected.candidate.row.join('◆'));
nativeRemark = controlValue(getArrangementControl(form, operation, 'beizhu0'));
dispatchNativeFieldEvents(control);
} catch (error) {
blockers.push(`arrangement_hotel_native_selectbox_failed:${error?.message || String(error)}`);
@@ -2340,7 +2349,7 @@
linked_fields_changed: linkedFields.filter((name) => before[name] !== after[name]),
value_redacted: true
});
return selected;
return { ...selected, nativeRemark };
}
function applyNativeSettlementSelection(form, supplier, changes, blockers, action = 'arrangement_transport', operation = {}) {
@@ -2793,9 +2802,18 @@
const preserved = createPreservedFields(form, operation);
const field = (name) => arrangementFieldName(operation, name);
const action = operation.action;
if (action === 'arrangement_hotel') applyNativeHotelSelection(form, value.resource, changes, blockers, operation);
let hotelSelection;
if (action === 'arrangement_hotel') hotelSelection = applyNativeHotelSelection(form, value.resource, changes, blockers, operation);
else applyNativeSettlementSelection(form, value.supplier, changes, blockers, action, operation);
if (blockers.length) return { changes, blockers, arrangementSlot: slot };
// Freeze the selected resource's note before date/quantity callbacks can
// clear it. Never inherit another existing row or read it back after save.
const nativeRemark = usesNativeHotelRemark(operation)
? hotelSelection.nativeRemark : undefined;
const effectiveRemark = nativeRemark ?? value.remark;
const projectionOperation = nativeRemark === undefined ? operation : {
...operation, data: { ...operation.data, arrangement: { ...value, remark: effectiveRemark } }
};
if (action === 'arrangement_hotel' || action === 'arrangement_vehicle') {
applyExact(form, field('riqi0'), 'set', value.start_date, changes, blockers);
applyExact(form, field('riqis0'), 'set', value.end_date, changes, blockers);
@@ -2820,15 +2838,16 @@
// Native date/room callbacks may reset status and remark. Set and assert
// the requested final values only after those callbacks have settled.
applyExact(form, field('zt0'), 'set', value.status, changes, blockers, { optional: action === 'arrangement_other' });
applyExact(form, field('beizhu0'), 'set', value.remark, changes, blockers);
applyExact(form, field('beizhu0'), 'set', effectiveRemark, changes, blockers);
if (action === 'arrangement_other') {
for (const [key, name] of Object.entries({ number: 'beianhao', date: 'beianri', entry_port: 'rujingkou', exit_port: 'chujingkou' })) {
if (value.filing?.[key] !== undefined) applyExact(form, name, 'set', value.filing[key], changes, blockers);
}
}
const writeGuard = () => arrangementCreateWriteGuard(form, operation, signature, controls, preserved);
const writeGuard = () => arrangementCreateWriteGuard(form, projectionOperation, signature, controls, preserved);
blockers.push(...writeGuard());
return { changes, blockers, arrangementSlot: slot, writeGuard, linkageWait };
return { changes, blockers, arrangementSlot: slot, writeGuard, linkageWait,
...(nativeRemark !== undefined ? { nativeRemarkSha256: await sha256(normalize(nativeRemark)) } : {}) };
}
async function applyArrangement(form, operation) {
@@ -4077,7 +4096,7 @@
async function indexedCreateRequeryProjection(form, operation, proof) {
const blockers = [];
if (!proof || proof.version !== 1 || !Array.isArray(proof.existing_rows)
if (!proof || ![1, 2].includes(proof.version) || !Array.isArray(proof.existing_rows)
|| !Number.isInteger(proof.slot_index) || proof.slot_index < 0
|| proof.operation_key !== await arrangementCreateOperationKey(operation)) {
return { fields: [], blockers: ['arrangement_create_before_evidence_missing_or_mismatched'], operation };
@@ -4097,6 +4116,16 @@
if (newRows.length !== 1) blockers.push(`arrangement_create_new_row_count:${newRows.length}`);
if (blockers.length) return { fields: [], blockers: [...new Set(blockers)], operation };
const projectedOperation = arrangementCreateAtSlot(operation, newRows[0]);
if (proof.version === 2) {
if (!usesNativeHotelRemark(operation) || !/^(?:[a-f0-9]{64}|fallback-[a-f0-9]{1,8})$/.test(proof.native_remark_sha256 || '')) {
return { fields: [], blockers: ['arrangement_create_native_remark_evidence_missing'], operation };
}
const nativeRemark = controlValue(getArrangementControl(form, projectedOperation, 'beizhu0'));
if (await sha256(normalize(nativeRemark)) !== proof.native_remark_sha256) {
return { fields: [], blockers: ['arrangement_create_native_remark_changed_after_save'], operation };
}
projectedOperation.data.arrangement.remark = nativeRemark;
}
const projection = arrangementExpectedFields(form, projectedOperation);
if (operation.action === 'arrangement_other') {
const expectedNames = createPreservedFields(form, projectedOperation).map((field) => field.name);
@@ -4526,6 +4555,9 @@
applyBlockers = applied.blockers;
writeGuard = applied.writeGuard || null;
if (applied.linkageWait) preflight.preflight.arrangement_linkage = applied.linkageWait;
if (applied.nativeRemarkSha256 && preflight.preflight.hotel_create?.version === 2) {
preflight.preflight.hotel_create.native_remark_sha256 = applied.nativeRemarkSha256;
}
} else if (operation.action.startsWith('order_update_')) {
const applied = applyUpdates(form, operation);
const receivable = applyReceivableFixture(form, operation);
@@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "联泰下单助手",
"version": "0.5.190",
"version": "0.5.191",
"description": "在已登录 LTJT ERP 页面内规划并受控执行联泰 ERP 业务操作。",
"permissions": [
"activeTab",
@@ -944,7 +944,7 @@
window.LTJTOrderAssistant = {
...(window.LTJTOrderAssistant || {}),
version: '0.5.190',
version: '0.5.191',
openTeamBatchForm,
pingTeamBatchFrame,
preflightTeamBatchNative,