Archived
fix(w05): 两颗料不能是同一颗 —— 起单这一刻立墙,因为路只在这里
用户:「创建活动的启动参数里面不应该能选到同样的两个芯片」。 查下来这不只是没意义,是**把单子做死**:sub_eval.part 的主键是 eval_ref+internal_pn (role 不在主键里),S1 配方铸的两行撞成同一个主键、后一笔覆盖前一笔,两行收成一行 —— sub_eval_part_exists_status_found = 2 永不成立,S1 收不了步,而件上那两行人加不出来 (行源 mine:,没有「+加一行」),唯一出路是终止重开。与「两格留空」是同一条链。 平台加互斥声明 x-distinct(契约 distinctGroup)——与 x-link 正相反:那个是引导,这个是墙。 · 前端:别的格占着的候选这一格不列(自己填着的那个永远留着,否则 select 当场变空白), 并说清是谁占了哪个;走「新增」自己写一个同样的 → 小红字 + 启动按不下去; · 服务端 validateLaunchParams 独立拦(脚本 / API 绕不过),400 点名后填的那格; · 「算不算同一个」= 去空白 + 不分大小写(launchDistinctKey,前后端同一把尺)—— 只挡精确相等的话 lm2596s-5.0 照样进去,混一颗垃圾主数据出来。 W05 两格挂 x-distinct: 'part',2.14.0 已装库核过。 测:服务端 6 条 + 前端 8 条(含联动与互斥合起来的真形状)+ 包内 1 条,三处变异验证过; 四道门禁绿(2548 测)。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LKMZDwMbyKzjw6Hembwvet
This commit is contained in:
@@ -293,3 +293,61 @@ describe('x-options.group / x-link', () => {
|
||||
expect(out).toEqual({ ok: true, data: { originalInternalPn: 'LM2596S-5.0', candidateInternalPn: 'AMS1117-3.3' } });
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* 互斥组(2026-08-31 用户定:「不应该能选到同样的两个芯片」)。
|
||||
* 与 `x-link` 正相反 —— **这一条是墙**:W05 两格同一颗料会把单子做死(两行撞同一个主键收成一行,
|
||||
* S1 的 `exists_status_found = 2` 永不成立,而件上那两行人加不出来),而唯一能改的时机就是起单这一刻。
|
||||
*/
|
||||
describe('x-distinct', () => {
|
||||
const DISTINCT = {
|
||||
type: 'object',
|
||||
required: ['originalInternalPn', 'candidateInternalPn'],
|
||||
properties: {
|
||||
originalInternalPn: { type: 'string', title: '原内部料号', 'x-control': 'combo', 'x-options': { ledger: 'parts.profile', value: 'internal_pn' }, 'x-distinct': 'part', 'x-order': 1 },
|
||||
candidateInternalPn: { type: 'string', title: '候选内部料号', 'x-control': 'combo', 'x-options': { ledger: 'parts.profile', value: 'internal_pn' }, 'x-distinct': 'part', 'x-order': 2 },
|
||||
/* 同一张表上不在这一组的格子:填成一样也与它无关 */
|
||||
note: { type: 'string', title: '备注' },
|
||||
},
|
||||
};
|
||||
const paramsOf = async () => launchParamsOf(DISTINCT, async () => [{ value: 'LM2596S-5.0', label: 'LM2596S-5.0' }, { value: 'XL2596S-5.0', label: 'XL2596S-5.0' }]);
|
||||
|
||||
it('读得到组名;**不以有候选为前提** —— 自由输入的两格同样归它管(墙管的是值,不是候选)', async () => {
|
||||
expect(fieldSpecsOf(DISTINCT).map((s) => s.distinctGroup)).toEqual(['part', 'part', undefined]);
|
||||
const bare = await launchParamsOf({ properties: { a: { type: 'string', 'x-distinct': 'g' }, b: { type: 'string', 'x-distinct': '' } } }, noResolve);
|
||||
expect(bare.map((p) => [p.type, p.distinctGroup])).toEqual([
|
||||
['text', 'g'],
|
||||
['text', undefined],
|
||||
]);
|
||||
});
|
||||
|
||||
it('两格同一个值 → 拒,点名**后填的那格**,话里指向它撞上了谁', async () => {
|
||||
const out = validateLaunchParams(await paramsOf(), { originalInternalPn: 'LM2596S-5.0', candidateInternalPn: 'LM2596S-5.0' }, fieldSpecsOf(DISTINCT));
|
||||
expect(out).toEqual({ ok: false, issues: [{ key: 'candidateInternalPn', message: '与「原内部料号」填的是同一个 —— 这两格不能一样,换一个再启动' }] });
|
||||
});
|
||||
|
||||
it('**去空白 + 不分大小写**:只挡精确相等的话,`lm2596s-5.0` 会照样进去,混一颗垃圾主数据出来', async () => {
|
||||
const out = validateLaunchParams(await paramsOf(), { originalInternalPn: 'LM2596S-5.0', candidateInternalPn: ' lm2596s-5.0 ' }, fieldSpecsOf(DISTINCT));
|
||||
expect(out.ok).toBe(false);
|
||||
});
|
||||
|
||||
it('两颗不同的料照收;不在这一组的格子填成一样也不管', async () => {
|
||||
const out = validateLaunchParams(
|
||||
await paramsOf(),
|
||||
{ originalInternalPn: 'LM2596S-5.0', candidateInternalPn: 'XL2596S-5.0', note: 'LM2596S-5.0' },
|
||||
fieldSpecsOf(DISTINCT),
|
||||
);
|
||||
expect(out).toEqual({ ok: true, data: { originalInternalPn: 'LM2596S-5.0', candidateInternalPn: 'XL2596S-5.0', note: 'LM2596S-5.0' } });
|
||||
});
|
||||
|
||||
it('空的不算撞上 —— 两格都没填该报的是必填,不是「填了同一个」', async () => {
|
||||
const out = validateLaunchParams(await paramsOf(), { originalInternalPn: '', candidateInternalPn: '' }, fieldSpecsOf(DISTINCT));
|
||||
expect(out).toEqual({ ok: false, issues: [{ key: 'originalInternalPn', message: '必填' }, { key: 'candidateInternalPn', message: '必填' }] });
|
||||
});
|
||||
|
||||
it('数组格不参与(契约写死的边界:本期没有这种形状)', async () => {
|
||||
const arrays = { properties: { a: { type: 'array', items: { type: 'string' }, title: 'A', 'x-distinct': 'g' }, b: { type: 'array', items: { type: 'string' }, title: 'B', 'x-distinct': 'g' } } };
|
||||
const out = validateLaunchParams(await launchParamsOf(arrays, noResolve), { a: ['X'], b: ['X'] }, fieldSpecsOf(arrays));
|
||||
expect(out).toEqual({ ok: true, data: { a: ['X'], b: ['X'] } });
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user