修改备注没有带入的问题
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
# 酒店新增保留原生备注
|
||||
|
||||
## Scope
|
||||
|
||||
- 用户授权修复新增酒店时默认备注被空值覆盖:未填写备注保留选中酒店原生备注,明确填写时覆盖;酒店变更继续保留未指定的原记录备注。
|
||||
- 单条与同团批量共用执行路径。无真实 ERP 写入、部署、重启、扩展重载或外部发送。
|
||||
|
||||
## Worktree gate
|
||||
|
||||
- Branch: main;worktree: `/Users/andy/IdeaProjects/LWLT-AIBOT`;base: `c2c71d35afbb7c1b9d28bda1b28926a9681414b6`。
|
||||
- 唯一 worktree;既有改动仅暂存 `.idea/` 文件,与本任务无重叠,保留原状。Overlap: Clear。
|
||||
- Feature 模式;单智能体;canonical 项目记忆只读。
|
||||
|
||||
## Plan
|
||||
|
||||
1. 捕获所选酒店原生备注,防止日期/数量回调清空;用户明确备注优先。
|
||||
2. 将冻结后的备注纳入保存前保护和持久回查证据,旧版历史回查兼容。
|
||||
3. 增加单条/批量及异常回归,更新插件版本、最低版本、mapping、ZIP 和清单,完成仓库检查。
|
||||
|
||||
## Results / Validation
|
||||
|
||||
- 完成默认酒店备注捕获与冻结;显式备注覆盖;资源/日期/数量回调不能静默清空待保存备注,其他行与已有酒店变更语义保持。
|
||||
- 默认备注创建证据 v2 绑定原始操作与备注摘要,保存前 guard 和写后 fresh GET/只读回查验证;缺失或不一致时阻断/不确定,历史 v1 回查兼容。批量按条独立生效。
|
||||
- 本地控制面 299/299 通过;插件/兼容 452 项中沙箱通过 450 项,另 2 项 HTTP 监听受限,经批准后定向重跑 2/2 通过。类型检查、构建及干净同内容源码副本仓库检查 10/10 通过。
|
||||
- 原工作目录仅有两项既有卫生检查失败(.idea、.DS_Store、历史日志),未删除或认领;.env 未读取。
|
||||
- 插件、平台最低版本、mapping、测试与发布清单同步 0.5.191。ZIP 21 文件逐字节匹配源码;原 0.5.190 ZIP 验证原清单哈希后归档。扫描仅清单引用旧 ZIP,无其他活动链接需修复。
|
||||
- 无新迁移;解析器/Skill/运营模板/DOCX 不变。当前未提交 Git,未部署或真实 ERP 验收。
|
||||
- [验证证据](../../../archive/evidence/2026-09-29/hotel-native-remark-01a0858b/README.md);[发布清单](../../../dist/release-manifest.json)。
|
||||
|
||||
## Follow-ups
|
||||
|
||||
- 更新插件至 0.5.191 并同步平台最低版本,刷新平台/ERP 后在授权环境验收原生备注联动;历史已保存空备注不自动回填。
|
||||
|
||||
## Promotion Candidates
|
||||
|
||||
- 目标:canonical 安排规则。建议登记酒店新增未指定备注继承所选资源默认备注,显式值覆盖,变更保留未指定原记录值;v2 证据用于新增默认备注持久核验。用户已授权方向,本地验证通过,生产真实验收待执行。
|
||||
@@ -103,7 +103,7 @@ const persistedExtensionResultVersions = new Map();
|
||||
let taskCreateInProgress = false;
|
||||
const AUTO_HANDOFF_RETRY_MS = 30_000;
|
||||
|
||||
const REQUIRED_EXTENSION_VERSION = '0.5.190';
|
||||
const REQUIRED_EXTENSION_VERSION = '0.5.191';
|
||||
const MANUAL_HANDOFF_LABEL = '确认并提交到 ERP 插件';
|
||||
const RETRY_HANDOFF_LABEL = '继续提交到 ERP 插件';
|
||||
const RECONCILE_LABEL = '只读回查 ERP 现有结果';
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
## 当前基线
|
||||
|
||||
- Chrome 插件:`0.5.190`
|
||||
- Chrome 插件:`0.5.191`
|
||||
- `0.5.191` 酒店新增默认备注保留:原生选择后立即冻结备注,用户明确填写时覆盖;保存前保护及持久回查核对,单条/批量共用。旧 v1 回查与已有安排变更保持。仅本地合成回归,真实 ERP 验收待部署方完成;需更新插件/平台最低版本,无迁移。见[验证证据](../../../archive/evidence/2026-09-29/hotel-native-remark-01a0858b/README.md)及[任务记录](../../../.project-docs/30-worklog/tasks/20260929-hotel-native-remark-01a0858b.md)。
|
||||
- `0.5.190` 同团批量酒店新增:2—10 个完整酒店块先整批验证,串行逐条复用原生保存/回查,首次失败或不确定停止,保留成功前缀和未执行清单;只读核验不自动续写。需同步控制面/平台和插件,无新增迁移;本地合成验证,真实 ERP 验收待部署方完成。见[验证证据](../../../archive/evidence/2026-09-24/hotel-batch-01a0858b/README.md)和[任务记录](../../../.project-docs/30-worklog/tasks/20260924-hotel-batch-01a0858b.md)。
|
||||
- `0.5.189` 仅修复大交通与其他/备案新增的默认日期空行误判:行日期等于页面有效发团日期且数量为原生空/0/1 时允许进入原有空行校验,资源、说明、备注、费用、付款、审核与独立备案保护仍保留;其他安排、非默认日期和变更/清除不放宽。需更新插件、平台最低版本并刷新 ERP 页面,无服务端逻辑/迁移变更;真实 ERP 验收待部署方完成。见[本地验证](../../../archive/evidence/2026-09-22/arrangement-default-date-01a0b77e/README.md)及[任务记录](../../../.project-docs/30-worklog/tasks/20260922-arrangement-default-date-01a0b77e.md)。
|
||||
- `0.5.188` 与程序解析器 `v1.0.14` 修复单个下单的产品格式匹配:比较双方统一 NFKC/空白,保留标点及业务限定词,唯一候选使用 ERP 原名联动;keyword 保留输入原始格式。初始失败跳过后续依赖校验,零/多产品候选提供独立中文原因。真实公共预检入口的合成回归通过,生产 ERP 验收待部署方完成;需同步服务端、插件和平台,无迁移。见[验证证据](../../../archive/evidence/2026-09-19/product-name-normalization-01a0b77e/README.md)及[任务记录](../../../.project-docs/30-worklog/tasks/20260919-product-name-normalization-01a0b77e.md)。
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# 酒店新增默认备注本地验证
|
||||
|
||||
此目录保存 2026-09-29 修复的不可变本地证据,不定义当前业务规则,也不是运行时输入。
|
||||
|
||||
## 结果
|
||||
|
||||
- 酒店原生选择后、资源/日期/数量事件前冻结所选行备注;省略备注继承该值,明确填写覆盖。其他行保持,已有酒店变更未填备注保持原记录。
|
||||
- 默认备注保存前校验,v2 创建证据绑定原操作与备注摘要,fresh GET/只读回查验证持久值;缺失证据或保存后变化不报成功。历史 v1 回查保持原语义。单条与批量逐条均覆盖。
|
||||
- 控制面 299/299 通过;插件/兼容测试共 452 项,沙箱中 450 项通过,2 项仅因本机监听 `EPERM` 失败,经批准解除该限制后定向重跑 2/2 通过。完整日志和定向结果均保留,没有隐藏失败日志。
|
||||
- 类型检查、构建通过;同内容干净源码副本仓库检查 10/10 通过,ZIP 的 21 个文件已与当前插件源码逐字节比对。
|
||||
- 原工作目录的仓库检查有两项既有卫生失败:`.idea/`、`.DS_Store`、历史日志。均保留不动;干净副本只排除这些已有杂项及秘密配置,未读取 `.env`。
|
||||
- 发布插件/平台最低版本/mapping/ZIP 为 0.5.191。无 Schema、解析器、Skill、运营模板/DOCX 或数据库迁移变更。
|
||||
- 未访问/写入真实 ERP,未部署、重启、扩展重载或外部发送。生产验收仍需更新后执行。
|
||||
|
||||
## 日志
|
||||
|
||||
- [控制面](control-plane-tests.txt)
|
||||
- [插件/兼容完整沙箱结果](legacy-tests-sandbox.txt)、[HTTP 两项定向重跑](http-tests.txt)
|
||||
- [干净副本仓库检查](repository-clean-tests.txt)、[原目录既有残留](repository-local-residue.txt)
|
||||
- [类型检查](typecheck.txt)、[构建](build.txt)(成功时无输出)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
||||
▶ business-system HTTP boundary
|
||||
✔ returns the real AI connectivity probe from /api/status (19.389709ms)
|
||||
✔ exposes /api/parse and no longer exposes /api/agent routes (4.931792ms)
|
||||
✔ business-system HTTP boundary (25.077166ms)
|
||||
ℹ tests 2
|
||||
ℹ suites 1
|
||||
ℹ pass 2
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 135.24725
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,70 @@
|
||||
TAP version 13
|
||||
# Subtest: root contains only governed long-lived entries
|
||||
ok 1 - root contains only governed long-lived entries
|
||||
---
|
||||
duration_ms: 92.746625
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: project docs are the sole active project memory
|
||||
ok 2 - project docs are the sole active project memory
|
||||
---
|
||||
duration_ms: 19.769834
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: temporary residue and report outputs do not pollute the repository
|
||||
ok 3 - temporary residue and report outputs do not pollute the repository
|
||||
---
|
||||
duration_ms: 338.834166
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: dist is an exact versioned release set with valid hashes
|
||||
ok 4 - dist is an exact versioned release set with valid hashes
|
||||
---
|
||||
duration_ms: 21.956
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: extension and release baselines agree
|
||||
ok 5 - extension and release baselines agree
|
||||
---
|
||||
duration_ms: 5.963541
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: packaged Skills and extension runtime match their project sources
|
||||
ok 6 - packaged Skills and extension runtime match their project sources
|
||||
---
|
||||
duration_ms: 1081.545417
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: active Markdown links resolve and current docs do not depend on rolling history
|
||||
ok 7 - active Markdown links resolve and current docs do not depend on rolling history
|
||||
---
|
||||
duration_ms: 347.337041
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: archive README indexes remain navigable
|
||||
ok 8 - archive README indexes remain navigable
|
||||
---
|
||||
duration_ms: 145.497375
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: single-source and generated-output boundaries remain explicit
|
||||
ok 9 - single-source and generated-output boundaries remain explicit
|
||||
---
|
||||
duration_ms: 39.299333
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: runtime image includes both LibreOffice document converters
|
||||
ok 10 - runtime image includes both LibreOffice document converters
|
||||
---
|
||||
duration_ms: 1.320125
|
||||
type: 'test'
|
||||
...
|
||||
1..10
|
||||
# tests 10
|
||||
# suites 0
|
||||
# pass 10
|
||||
# fail 0
|
||||
# cancelled 0
|
||||
# skipped 0
|
||||
# todo 0
|
||||
# duration_ms 2460.424584
|
||||
@@ -0,0 +1,132 @@
|
||||
TAP version 13
|
||||
# Subtest: root contains only governed long-lived entries
|
||||
not ok 1 - root contains only governed long-lived entries
|
||||
---
|
||||
duration_ms: 5.146542
|
||||
type: 'test'
|
||||
location: '/Users/andy/IdeaProjects/LWLT-AIBOT/tools/repository-hygiene.test.mjs:111:1'
|
||||
failureType: 'testCodeFailure'
|
||||
error: |-
|
||||
Expected values to be strictly deep-equal:
|
||||
+ actual - expected
|
||||
|
||||
+ [
|
||||
+ '.DS_Store',
|
||||
+ '.idea'
|
||||
+ ]
|
||||
- []
|
||||
|
||||
code: 'ERR_ASSERTION'
|
||||
name: 'AssertionError'
|
||||
expected:
|
||||
actual:
|
||||
0: '.DS_Store'
|
||||
1: '.idea'
|
||||
operator: 'deepStrictEqual'
|
||||
stack: |-
|
||||
TestContext.<anonymous> (file:///Users/andy/IdeaProjects/LWLT-AIBOT/tools/repository-hygiene.test.mjs:114:10)
|
||||
async Test.run (node:internal/test_runner/test:1054:7)
|
||||
async startSubtestAfterBootstrap (node:internal/test_runner/harness:296:3)
|
||||
...
|
||||
# Subtest: project docs are the sole active project memory
|
||||
ok 2 - project docs are the sole active project memory
|
||||
---
|
||||
duration_ms: 3.319083
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: temporary residue and report outputs do not pollute the repository
|
||||
not ok 3 - temporary residue and report outputs do not pollute the repository
|
||||
---
|
||||
duration_ms: 45.544084
|
||||
type: 'test'
|
||||
location: '/Users/andy/IdeaProjects/LWLT-AIBOT/tools/repository-hygiene.test.mjs:149:1'
|
||||
failureType: 'testCodeFailure'
|
||||
error: |-
|
||||
Expected values to be strictly deep-equal:
|
||||
+ actual - expected
|
||||
|
||||
+ [
|
||||
+ '.DS_Store',
|
||||
+ 'archive/evidence/2026-09-14/arrangement-original-date-year-01a0858b/build.log',
|
||||
+ 'archive/evidence/2026-09-14/arrangement-original-date-year-01a0858b/control-plane-tests.log',
|
||||
+ 'archive/evidence/2026-09-14/arrangement-original-date-year-01a0858b/focused-tests.log',
|
||||
+ 'archive/evidence/2026-09-14/arrangement-original-date-year-01a0858b/legacy-tests.log',
|
||||
+ 'archive/evidence/2026-09-14/arrangement-original-date-year-01a0858b/repository-clean.log',
|
||||
+ 'archive/evidence/2026-09-14/arrangement-original-date-year-01a0858b/repository-local-metadata.log',
|
||||
+ 'archive/evidence/2026-09-14/arrangement-original-date-year-01a0858b/runtime-layout-smoke.log',
|
||||
+ 'archive/evidence/2026-09-14/arrangement-original-date-year-01a0858b/typecheck.log',
|
||||
+ 'chrome-extension/.DS_Store'
|
||||
+ ]
|
||||
- []
|
||||
|
||||
code: 'ERR_ASSERTION'
|
||||
name: 'AssertionError'
|
||||
expected:
|
||||
actual:
|
||||
0: '.DS_Store'
|
||||
1: 'archive/evidence/2026-09-14/arrangement-original-date-year-01a0858b/build.log'
|
||||
2: 'archive/evidence/2026-09-14/arrangement-original-date-year-01a0858b/control-plane-tests.log'
|
||||
3: 'archive/evidence/2026-09-14/arrangement-original-date-year-01a0858b/focused-tests.log'
|
||||
4: 'archive/evidence/2026-09-14/arrangement-original-date-year-01a0858b/legacy-tests.log'
|
||||
5: 'archive/evidence/2026-09-14/arrangement-original-date-year-01a0858b/repository-clean.log'
|
||||
6: 'archive/evidence/2026-09-14/arrangement-original-date-year-01a0858b/repository-local-metadata.log'
|
||||
7: 'archive/evidence/2026-09-14/arrangement-original-date-year-01a0858b/runtime-layout-smoke.log'
|
||||
8: 'archive/evidence/2026-09-14/arrangement-original-date-year-01a0858b/typecheck.log'
|
||||
9: 'chrome-extension/.DS_Store'
|
||||
operator: 'deepStrictEqual'
|
||||
stack: |-
|
||||
TestContext.<anonymous> (file:///Users/andy/IdeaProjects/LWLT-AIBOT/tools/repository-hygiene.test.mjs:153:10)
|
||||
async Test.run (node:internal/test_runner/test:1054:7)
|
||||
async Test.processPendingSubtests (node:internal/test_runner/test:744:7)
|
||||
...
|
||||
# Subtest: dist is an exact versioned release set with valid hashes
|
||||
ok 4 - dist is an exact versioned release set with valid hashes
|
||||
---
|
||||
duration_ms: 5.366667
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: extension and release baselines agree
|
||||
ok 5 - extension and release baselines agree
|
||||
---
|
||||
duration_ms: 2.476417
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: packaged Skills and extension runtime match their project sources
|
||||
ok 6 - packaged Skills and extension runtime match their project sources
|
||||
---
|
||||
duration_ms: 137.733208
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: active Markdown links resolve and current docs do not depend on rolling history
|
||||
ok 7 - active Markdown links resolve and current docs do not depend on rolling history
|
||||
---
|
||||
duration_ms: 48.791875
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: archive README indexes remain navigable
|
||||
ok 8 - archive README indexes remain navigable
|
||||
---
|
||||
duration_ms: 27.249208
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: single-source and generated-output boundaries remain explicit
|
||||
ok 9 - single-source and generated-output boundaries remain explicit
|
||||
---
|
||||
duration_ms: 2.113625
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: runtime image includes both LibreOffice document converters
|
||||
ok 10 - runtime image includes both LibreOffice document converters
|
||||
---
|
||||
duration_ms: 0.471583
|
||||
type: 'test'
|
||||
...
|
||||
1..10
|
||||
# tests 10
|
||||
# suites 0
|
||||
# pass 8
|
||||
# fail 2
|
||||
# cancelled 0
|
||||
# skipped 0
|
||||
# todo 0
|
||||
# duration_ms 377.6575
|
||||
5
archive/releases/2026-09-29/README.md
Normal file
5
archive/releases/2026-09-29/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# 2026-09-29 发布归档
|
||||
|
||||
此处为历史资料,不定义当前业务规则。
|
||||
|
||||
- [酒店默认备注修复前发布物](hotel-native-remark-01a0858b/README.md)
|
||||
@@ -0,0 +1,3 @@
|
||||
# 酒店备注修复前发布物
|
||||
|
||||
只读历史发布物,不定义当前业务规则。0.5.190 ZIP 在归档前核对原发布清单哈希,原字节保留。当前版本与哈希以 [发布清单](../../../../dist/release-manifest.json) 为准。
|
||||
@@ -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,
|
||||
|
||||
BIN
dist/ltjt-order-assistant-0.5.191.zip
vendored
Normal file
BIN
dist/ltjt-order-assistant-0.5.191.zip
vendored
Normal file
Binary file not shown.
10
dist/release-manifest.json
vendored
10
dist/release-manifest.json
vendored
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"manifest_version": 1,
|
||||
"generated_on": "2026-09-24",
|
||||
"generated_on": "2026-09-29",
|
||||
"baselines": {
|
||||
"chrome_extension": "0.5.190",
|
||||
"chrome_extension": "0.5.191",
|
||||
"skills": "0.5.129",
|
||||
"business_instruction_docx": "0.5.137",
|
||||
"agent_prompt": "ltjt-agent-prompt-v1.8-independent-headcount-categories"
|
||||
@@ -10,10 +10,10 @@
|
||||
"artifacts": [
|
||||
{
|
||||
"kind": "chrome_extension",
|
||||
"version": "0.5.190",
|
||||
"path": "dist/ltjt-order-assistant-0.5.190.zip",
|
||||
"version": "0.5.191",
|
||||
"path": "dist/ltjt-order-assistant-0.5.191.zip",
|
||||
"source": "chrome-extension/ltjt-order-assistant",
|
||||
"sha256": "092838d79cf5864ec3894035b60b6dde5bbeb979c6bf9bce0ad656dc9b7c7f69"
|
||||
"sha256": "97ea457b40c164b5f015a051b450aed8e3617da950608eb293f18e3ae5e41d69"
|
||||
},
|
||||
{
|
||||
"kind": "skill",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"contract_version": "ltjt-lifecycle-v2.9-roster-leader-contact-2026-08",
|
||||
"current_agent_parse_prompt_version": "ltjt-agent-prompt-v1.8-independent-headcount-categories",
|
||||
"current_extension_version": "0.5.190",
|
||||
"current_extension_version": "0.5.191",
|
||||
"original_arrangement_date_selection": "Hotel, vehicle, transport and other/filing update selection accepts MM-DD or YYYY-MM-DD; omitted year matches original ERP month/day within the exact group only. Require exactly one persisted row, then freeze its actual full dates and id. Full year is exact; mixed/cross-year endpoints are matched independently with actual range order checked. No match or ambiguity blocks before mutation; show up to five original candidates and ask to resubmit complete dates. Creation and legacy target-date changes retain existing semantics.",
|
||||
"historical_test_marker": "TEST-202609",
|
||||
"scope": "/System/Business/",
|
||||
@@ -333,6 +333,7 @@
|
||||
}
|
||||
},
|
||||
"arrangement_hotel": {
|
||||
"create_remark_policy": "0.5.191: omitted/normalized-empty remark inherits the selected native hotel note captured before field callbacks; explicit nonempty remark overrides. Freeze final note for the pre-POST guard. Hotel create proof v2 binds the inherited note hash before POST and verifies it on fresh requery/reconciliation; missing/mismatched proof cannot complete. Historical v1 and existing-row updates retain prior semantics. Applies independently to each batch item.",
|
||||
"page": "/System/Business/teams_jiudian.asp",
|
||||
"action": "DoInfo_jiudian",
|
||||
"create_form_readiness": {
|
||||
|
||||
@@ -2,6 +2,12 @@ import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import vm from 'node:vm';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { webcrypto } from 'node:crypto';
|
||||
import { createRequire } from 'node:module';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const plans = require('../chrome-extension/ltjt-order-assistant/operation-plans.js');
|
||||
const hotelBatch = require('../chrome-extension/ltjt-order-assistant/hotel-batch.js');
|
||||
|
||||
const inpage = readFileSync(new URL('../chrome-extension/ltjt-order-assistant/inpage.js', import.meta.url), 'utf8');
|
||||
const adapter = readFileSync(new URL('../chrome-extension/ltjt-order-assistant/lifecycle-adapters.js', import.meta.url), 'utf8')
|
||||
@@ -15,7 +21,7 @@ const adapter = readFileSync(new URL('../chrome-extension/ltjt-order-assistant/l
|
||||
teamSummaryRequery = async (operation, subpage) => subpage;
|
||||
assistant.preflightLifecycleOperation = preflightLifecycleOperation;`);
|
||||
|
||||
function fixture({ occupied = 1, rows = occupied + 2, onSelection = () => {}, onValidate = () => {}, sort = false, onPost = () => {}, linkageDelays = [], onLinkageTick = () => {} } = {}) {
|
||||
function fixture({ occupied = 1, rows = occupied + 2, onSelection = () => {}, onValidate = () => {}, sort = false, onPost = () => {}, linkageDelays = [], onLinkageTick = () => {}, onFieldEvent = () => {}, crypto = webcrypto } = {}) {
|
||||
let now = 10000;
|
||||
let posts = 0;
|
||||
let selections = 0;
|
||||
@@ -25,13 +31,13 @@ function fixture({ occupied = 1, rows = occupied + 2, onSelection = () => {}, on
|
||||
const fields = new Map();
|
||||
const document = { querySelector: () => form, querySelectorAll: () => [], readyState: 'complete', body: { innerText: '' } };
|
||||
const location = { href: 'https://erp.invalid/System/Business/teams_jiudian.asp?tdID=123', pathname: '/System/Business/teams_jiudian.asp' };
|
||||
const window = { document, Event: class {}, location, testValidate: () => onValidate(fields), getComputedStyle: () => ({ display: 'block', visibility: 'visible', opacity: '1' }) };
|
||||
const window = { document, crypto, Event: class {}, location, testValidate: () => onValidate(fields), getComputedStyle: () => ({ display: 'block', visibility: 'visible', opacity: '1' }) };
|
||||
window.parent = window;
|
||||
document.defaultView = window;
|
||||
form.ownerDocument = document;
|
||||
const add = (name, value) => {
|
||||
const control = { name, id: name, value: String(value), type: name === 'SubmitButton' ? 'submit' : 'text', tagName: 'INPUT', disabled: false,
|
||||
ownerDocument: document, dispatchEvent() {},
|
||||
ownerDocument: document, dispatchEvent() { onFieldEvent(fields, name); },
|
||||
getAttribute: (key) => key.toLowerCase() === 'data' ? '270◆测试新酒店◆TWN H◆CNY◆120' : key.toLowerCase() === 'setval' ? name : null };
|
||||
fields.set(name, control); form.elements.push(control);
|
||||
};
|
||||
@@ -204,3 +210,100 @@ test('requery cannot report an old row, duplicate new rows, changed existing row
|
||||
assert.equal(f.posts(), 1);
|
||||
}
|
||||
});
|
||||
|
||||
test('omitted hotel remarks survive selection/date callbacks, save and read-only requery', async () => {
|
||||
for (const omitted of [undefined, '']) for (const crypto of [webcrypto, null]) {
|
||||
const f = fixture({ sort: true, crypto,
|
||||
onSelection(fields, index) { fields.get(`beizhu${index}`).value = '6月21号起 按照包房12间\n有房损另计'; },
|
||||
onFieldEvent(fields, name) { if (/^(danwei|riqi|riqis|jianshu)1$/.test(name)) fields.get('beizhu1').value = ''; }
|
||||
});
|
||||
if (omitted === undefined) delete f.operation.data.arrangement.remark;
|
||||
const before = JSON.stringify(f.operation);
|
||||
const result = await f.api.liveSubmitLifecycleOperation(f.operation);
|
||||
assert.equal(result.status, 'lifecycle_completed', JSON.stringify(result));
|
||||
assert.equal(f.fields.get('beizhu1').value, '6月21号起 按照包房12间\n有房损另计');
|
||||
assert.equal(f.fields.get('beizhu0').value, '保留备注0');
|
||||
assert.equal(JSON.stringify(f.operation), before);
|
||||
assert.equal(result.preflight.hotel_create.version, 2);
|
||||
const checked = result.requery.checks.find(check => check.field === 'beizhu0');
|
||||
assert.equal(checked.actual, f.fields.get('beizhu1').value);
|
||||
assert.equal(checked.matched, true);
|
||||
const verified = await f.api.verifyLifecycleOperation(f.operation, { hotel_create: result.preflight.hotel_create });
|
||||
assert.equal(verified.status, 'lifecycle_requery_matched', JSON.stringify(verified));
|
||||
assert.equal(f.posts(), 1);
|
||||
}
|
||||
});
|
||||
|
||||
test('explicit hotel remark overrides the native default, while missing native notes remain empty', async () => {
|
||||
const f = fixture({ onSelection(fields, index) { fields.get(`beizhu${index}`).value = '酒店默认备注'; } });
|
||||
f.operation.data.arrangement.remark = '用户备注:晚到保留';
|
||||
const result = await f.api.liveSubmitLifecycleOperation(f.operation);
|
||||
assert.equal(result.status, 'lifecycle_completed', JSON.stringify(result));
|
||||
assert.equal(f.fields.get('beizhu1').value, '用户备注:晚到保留');
|
||||
assert.equal(result.preflight.hotel_create.version, 1);
|
||||
const empty = fixture();
|
||||
const blank = await empty.api.liveSubmitLifecycleOperation(empty.operation);
|
||||
assert.equal(blank.status, 'lifecycle_completed', JSON.stringify(blank));
|
||||
assert.equal(empty.fields.get('beizhu1').value, '');
|
||||
// Old executions stored v1 and intentionally wrote the omitted note as ''.
|
||||
const oldProof = { ...blank.preflight.hotel_create, version: 1 };
|
||||
delete oldProof.native_remark_sha256;
|
||||
assert.equal((await empty.api.verifyLifecycleOperation(empty.operation, { hotel_create: oldProof })).status, 'lifecycle_requery_matched');
|
||||
});
|
||||
|
||||
test('native remark changes at validation block POST; missing or changed saved evidence cannot report success', async () => {
|
||||
const selection = (fields, index) => { fields.get(`beizhu${index}`).value = '酒店默认备注'; };
|
||||
const f = fixture({ onSelection: selection, onValidate(fields) { fields.get('beizhu1').value = ''; } });
|
||||
const blocked = await f.api.liveSubmitLifecycleOperation(f.operation);
|
||||
assert.equal(blocked.status, 'lifecycle_apply_blocked', JSON.stringify(blocked));
|
||||
assert.equal(f.posts(), 0);
|
||||
const changed = fixture({ onSelection: selection, onPost(form) { form.elements.find(control => control.name === 'beizhu1').value = ''; } });
|
||||
const uncertain = await changed.api.liveSubmitLifecycleOperation(changed.operation);
|
||||
assert.equal(uncertain.status, 'lifecycle_execution_uncertain');
|
||||
assert.ok(uncertain.requery.blockers.includes('arrangement_create_native_remark_changed_after_save'));
|
||||
const good = fixture({ onSelection: selection });
|
||||
const result = await good.api.liveSubmitLifecycleOperation(good.operation);
|
||||
const proof = structuredClone(result.preflight.hotel_create);
|
||||
delete proof.native_remark_sha256;
|
||||
assert.equal((await good.api.verifyLifecycleOperation(good.operation, { hotel_create: proof })).status, 'lifecycle_requery_not_matched');
|
||||
good.savedForm().elements.find(control => control.name === 'beizhu1').value = 'ERP后续变动';
|
||||
assert.equal((await good.api.verifyLifecycleOperation(good.operation, { hotel_create: result.preflight.hotel_create })).status, 'lifecycle_requery_not_matched');
|
||||
assert.equal(good.posts(), 1);
|
||||
});
|
||||
|
||||
test('batch orchestration preserves each hotel native or explicit remark through actual lifecycle execution', async () => {
|
||||
const base = fixture().operation;
|
||||
const arrangements = [5, 6, 7].map(day => ({ mode: 'create', resource: { name: '测试新酒店TWN', keyword: '测试新酒店TWN' },
|
||||
start_date: `2026-09-0${day}`, end_date: `2026-09-0${day + 1}`, room_count: 12,
|
||||
...(day === 6 ? { remark: '用户独立备注' } : {}) }));
|
||||
const operation = { action: 'arrangement_hotel', order_nature: 'formal', submit_mode: 'dry_run',
|
||||
data: { existing_refs: { identifier: 'LW-TEST-HOTEL' }, arrangements } };
|
||||
const before = JSON.stringify(operation);
|
||||
const results = {}, reports = [];
|
||||
let final;
|
||||
const context = { operationPlans: plans, hotelBatch, throwIfTaskCancelled() {},
|
||||
publishHotelBatch: async (_id, _op, batch) => { final = batch; },
|
||||
chrome: { storage: { local: { get: async () => ({ businessTaskResults: results }) } } },
|
||||
executeLifecycleOperation: async (id, child) => {
|
||||
const index = reports.length;
|
||||
const f = fixture({ onSelection(fields, slot) { fields.get(`beizhu${slot}`).value = `酒店备注${index}`; },
|
||||
onFieldEvent(fields, name) { if (/^(riqi|riqis|jianshu)1$/.test(name)) fields.get('beizhu1').value = ''; },
|
||||
onPost(form) { form.elements.find(control => control.name === 'id1').value = String(999 + index); }
|
||||
});
|
||||
f.operation.data.arrangement = { ...base.data.arrangement, ...child.data.arrangement, resource: base.data.arrangement.resource };
|
||||
const report = await f.api.liveSubmitLifecycleOperation(f.operation);
|
||||
assert.equal(report.status, 'lifecycle_completed', JSON.stringify(report));
|
||||
report.requery = { matched: report.requery.matched, subpage: report.requery };
|
||||
reports.push(report);
|
||||
results[id] = { status: 'completed', report, write_attempted: true, no_erp_write: false };
|
||||
}
|
||||
};
|
||||
const background = readFileSync(new URL('../chrome-extension/ltjt-order-assistant/background.js', import.meta.url), 'utf8');
|
||||
vm.createContext(context);
|
||||
vm.runInContext(background.slice(background.indexOf('async function executeHotelBatch('), background.indexOf('async function runAutoTask(')), context);
|
||||
await context.executeHotelBatch('TASK', operation);
|
||||
assert.equal(hotelBatch.inspect(final).allCompleted, true);
|
||||
assert.deepEqual(reports.map(report => report.requery.subpage.checks.find(check => /^beizhu\d+$/.test(check.field)).actual),
|
||||
['酒店备注0', '用户独立备注', '酒店备注2']);
|
||||
assert.equal(JSON.stringify(operation), before);
|
||||
});
|
||||
|
||||
@@ -123,6 +123,19 @@ test('fresh ERP MAIN world resolves the reported 2027 hotel update using the act
|
||||
assert.equal(f.fields.get('riqis1').value, '2027-9-13');
|
||||
});
|
||||
|
||||
test('hotel replacement preserves the existing note unless the user supplies a new one', async () => {
|
||||
for (const remark of [undefined, '明确的新备注']) {
|
||||
const f = fixture({ onSelection(fields, slot) { fields.get(`beizhu${slot}`).value = '新酒店默认备注'; } });
|
||||
if (remark !== undefined) f.input.data.arrangement.changes.remark = remark;
|
||||
const resolved = f.api.testResolveHotel(f.form, f.input);
|
||||
assert.equal(resolved.ok, true, JSON.stringify(resolved));
|
||||
const applied = await f.api.testApply(f.form, resolved.operation);
|
||||
assert.equal(applied.blockers.length, 0, JSON.stringify(applied));
|
||||
assert.equal(f.fields.get('beizhu1').value, remark ?? '保留备注1');
|
||||
assert.equal(f.fields.get('beizhu0').value, '保留备注0');
|
||||
}
|
||||
});
|
||||
|
||||
test('missing page date dependency is diagnosed before matching and never reported as zero ERP records', () => {
|
||||
const f = fixture({ omitDateMatcher: true });
|
||||
const before = [...f.fields].map(([key, control]) => [key, control.value]);
|
||||
|
||||
@@ -1374,7 +1374,7 @@ test('passenger explicit server success is terminal without a post-save row requ
|
||||
assert.doesNotMatch(passengerBranch, /passengerRequery|verifyLifecycleOperation/);
|
||||
assert.doesNotMatch(background, /attemptAutomaticPassengerReconciliation/);
|
||||
assert.match(background, /名单已取得 ERP 明确成功响应,按业务规则确认录入成功/);
|
||||
assert.match(platformApp, /REQUIRED_EXTENSION_VERSION = '0\.5\.190'/);
|
||||
assert.match(platformApp, /REQUIRED_EXTENSION_VERSION = '0\.5\.191'/);
|
||||
});
|
||||
|
||||
test('uncertain lifecycle writes can only converge through a read-only plugin requery', async () => {
|
||||
@@ -1584,7 +1584,7 @@ test('lifecycle execution keeps the MV3 worker alive until the durable task sett
|
||||
assert.match(background, /arrangement_resource_candidate_data_missing/);
|
||||
});
|
||||
|
||||
test('extension 0.5.190 keeps reload dormant unless its durable execution state is fully idle', async () => {
|
||||
test('extension 0.5.191 keeps reload dormant unless its durable execution state is fully idle', async () => {
|
||||
const [background, bridge] = await Promise.all([
|
||||
readFile(new URL('../chrome-extension/ltjt-order-assistant/background.js', import.meta.url), 'utf8'),
|
||||
readFile(new URL('../chrome-extension/ltjt-order-assistant/business-bridge.js', import.meta.url), 'utf8')
|
||||
@@ -2037,7 +2037,7 @@ test('delete guard distinguishes independent, shared child, and shared parent ro
|
||||
for (const operation of operations) assert.equal(plans.validateOperation(operation).ok, true, plans.validateOperation(operation).blockers.join('; '));
|
||||
|
||||
const mapping = JSON.parse(await readFile(new URL('../mappings/lifecycle.mapping.json', import.meta.url), 'utf8'));
|
||||
assert.equal(mapping.current_extension_version, '0.5.190');
|
||||
assert.equal(mapping.current_extension_version, '0.5.191');
|
||||
assert.equal(
|
||||
mapping.updates.order_update_independent.field_mapped_pending_live_validation['pax.child_no_bed'],
|
||||
'ertrenshu'
|
||||
@@ -2421,10 +2421,10 @@ test('schema and browser adapters contain the v2 safety fields and no confirm ov
|
||||
assert.match(inpage, /`ys_danweiid\$\{index\}`, resolvedCustomerId/);
|
||||
assert.doesNotMatch(inpage, /product_customer_source_region|sourceRegionCheck|source_reference/);
|
||||
assert.doesNotMatch(teamBatchInpage, /product_customer_source_region|sourceRegionCheck/);
|
||||
assert.equal(extensionManifest.version, '0.5.190');
|
||||
assert.match(inpage, /version: '0\.5\.190'/);
|
||||
assert.match(teamBatchInpage, /version: '0\.5\.190'/);
|
||||
assert.match(platformApp, /REQUIRED_EXTENSION_VERSION = '0\.5\.190'/);
|
||||
assert.equal(extensionManifest.version, '0.5.191');
|
||||
assert.match(inpage, /version: '0\.5\.191'/);
|
||||
assert.match(teamBatchInpage, /version: '0\.5\.191'/);
|
||||
assert.match(platformApp, /REQUIRED_EXTENSION_VERSION = '0\.5\.191'/);
|
||||
assert.match(inpage, /function strictIsoDate\(value\)/);
|
||||
assert.match(inpage, /const startDate = strictIsoDate\(current\('riqi'\)\)/);
|
||||
assert.match(inpage, /const endDate = strictIsoDate\(current\('riqis'\)\)/);
|
||||
|
||||
Reference in New Issue
Block a user