forked from ChaiHao/BPOS-MVP
人手改的那一手真正落得下去;界面不撒谎那几处补齐
手改是**兜 AI 的错**的那一手 —— 这一轮把它从「摆在那儿但落不了地」做成真的: 件上就地改 · 保存**按地址收**,不看当下那张卡:载荷从前借道工单的 `blocks[i]` 取列名, 于是熔断卡(没有 blocks)在屏上时,人补的那一格一个字都送不出去 —— 而他要补的恰恰是把这一单卡住的那个必填格。授权面改由活动清单的台账声明把关。 · **能增删行**:错不止「这一格填错了」,还有「这一行不该在」与「漏了一行」。 删挂记号不当场抹掉(看得见、可后悔),且**删要说出来**(`drops`)—— 「没回传就是删了」会把一次不完整的回传变成一次删表。 · **必填没填按不下提交**:与放行门 L1 同一个数同一套算法;闸上摆出差在哪一行哪一格。 · 只写真改了的那几行 —— 改一格却留「修订 4 行」的痕,那句话是假的。 判据与门 · 裁量判据带谓词时**谓词说了算**:「选填缺口已补齐」基座数得出来, 人填好的目标价它照样判「客户未提供」的毛病由此消掉。 · 验收卡判据**读时现算** —— 人在成果件上补一格,判据跟着变。 · 放行门认「**办完了没有**」不认「有人写过」:从前写个 `false` 就把门推开, 于是一单没打标就进了放行门。三个数全由基座现数。 · 拒回把「活没干完」说成「数或行对不上」的毛病:判据点名的表一行都没写时直说那一步没办。 **指向两扇打不开的门,比不指还糟。** 界面不撒谎 · 日志只有三色:负责人绿 · 智能体蓝 · 系统灰(归侧五种是度量的分法,不是给人看的)。 · 验收收口答两件事:落了什么(按**交付物**点名,一份成果的几张表并成一行)、 往哪儿去(转出哪一口、带过去哪些字段);一个口都没发就直说到此为止。 · 签完到收尾之间那一段有自己的话「字已签下,正在落地」—— 中间态本来就该说出来,只是要说对。 · 消息不再被内部事件刷屏:只有从通道进来的件才谈得上「无人认领」。 · 建客户公司与联系人**一次确认落两行**;联系人编码自增不问人;卡面摆表的人读名。 A1 三案例端到端全 released:全 A 类直通 12 拍 / 客户点名跳一条 10 拍 / 新客户缺必填 14 拍。 五道门禁全绿(971 passed)。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JsTDjoHovERXyy6Wmw1ALp
This commit is contained in:
@@ -228,3 +228,50 @@ describe('完成只出自放行通过', () => {
|
||||
expect(allCriteriaGreen(acceptance({ criteria: [{ label: 'x', level: 'L2', pass: false }] }))).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* **一张录入卡可以落好几张表**(2026-08-02 用户:「新建客户 联系人和公司都需要确认」)。
|
||||
*
|
||||
* 建一位新客户落的是两条主数据:公司档 + 联系人档。从前卡上只有公司那一半,
|
||||
* 联系人由 AI 自己写 —— 而联系人档的 `contact_code` 是它编出来的,人一眼没看过。
|
||||
* 值按段隔开(`段号.列名`):两张表都有 `name` 这一列,平铺就会互相盖掉,
|
||||
* **而盖掉之后没人看得出来**。
|
||||
*/
|
||||
describe('录入卡分段:段号.列名', () => {
|
||||
/** 与 `routes/desk.ts` 那段拆分同一口径 —— 改一处这里要跟着改 */
|
||||
const split = (values: Record<string, string>, at: number): Record<string, string> => {
|
||||
const out: Record<string, string> = {};
|
||||
for (const [k, v] of Object.entries(values)) {
|
||||
const mark = `${at}.`;
|
||||
if (k.startsWith(mark)) out[k.slice(mark.length)] = v;
|
||||
else if (at === 0 && !/^\d+\./.test(k)) out[k] = v;
|
||||
}
|
||||
return out;
|
||||
};
|
||||
const values = {
|
||||
'0.customer_code': 'CU-9001',
|
||||
'0.name': '上海精密电子有限公司',
|
||||
'1.customer_code': 'CU-9001',
|
||||
'1.contact_code': 'CT-01',
|
||||
'1.name': '陈刚',
|
||||
'1.email': 'chen@sh-elec.com',
|
||||
};
|
||||
|
||||
it('**同名列不互相盖** —— 公司叫「上海精密电子」,联系人叫「陈刚」', () => {
|
||||
expect(split(values, 0)['name']).toBe('上海精密电子有限公司');
|
||||
expect(split(values, 1)['name']).toBe('陈刚');
|
||||
});
|
||||
|
||||
it('每段只拿自己那几格 —— 联系人编码不该落进公司档', () => {
|
||||
expect(split(values, 0)['contact_code']).toBeUndefined();
|
||||
expect(Object.keys(split(values, 1)).sort()).toEqual(['contact_code', 'customer_code', 'email', 'name']);
|
||||
});
|
||||
|
||||
it('**老形状(没段号的平键)仍旧管用** —— 一张卡只有一段时照旧落主段', () => {
|
||||
expect(split({ customer_code: 'CU-1', name: '老写法' }, 0)).toEqual({ customer_code: 'CU-1', name: '老写法' });
|
||||
});
|
||||
|
||||
it('老形状不喂给后面的段 —— 它说不清自己是哪一张表的', () => {
|
||||
expect(split({ customer_code: 'CU-1', name: '老写法' }, 1)).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user