@stalfh233/omc-cli 0.4.62 → 0.4.63
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lanes/runtime-data.js +27 -6
- package/dist/lanes/workflow.js +14 -3
- package/package.json +3 -3
- package/skills/omc-write-hazards.md +15 -0
|
@@ -300,10 +300,10 @@ async function stampChildRowStatuses(session, schemaCode, formData) {
|
|
|
300
300
|
}
|
|
301
301
|
}
|
|
302
302
|
catch {
|
|
303
|
-
return formData;
|
|
303
|
+
return { formData, childCodes: [] };
|
|
304
304
|
}
|
|
305
305
|
if (childCodes.size === 0)
|
|
306
|
-
return formData;
|
|
306
|
+
return { formData, childCodes: [] };
|
|
307
307
|
let changed = false;
|
|
308
308
|
const next = { ...formData };
|
|
309
309
|
for (const code of childCodes) {
|
|
@@ -321,7 +321,7 @@ async function stampChildRowStatuses(session, schemaCode, formData) {
|
|
|
321
321
|
return { ...row, rowStatus: hasId ? "Modified" : "Added" };
|
|
322
322
|
});
|
|
323
323
|
}
|
|
324
|
-
return changed ? next : formData;
|
|
324
|
+
return { formData: changed ? next : formData, childCodes: [...childCodes] };
|
|
325
325
|
}
|
|
326
326
|
/** Runtime business-object data keyed by field code. */
|
|
327
327
|
export function registerRuntimeDataLane(tools) {
|
|
@@ -646,7 +646,7 @@ export function registerRuntimeDataLane(tools) {
|
|
|
646
646
|
message: "submit:true 与 workflowCode 互斥——workflowCode 是流程草稿两段式的第一段(返回 workItemId 后用 data.submit 提交);无流程模型的一调完成态用 submit:true 且不带 workflowCode",
|
|
647
647
|
});
|
|
648
648
|
const { sheetCode, resolvedFrom } = await resolveFormSheetCode(session, { schemaCode: args.schemaCode, sheetCode: args.sheetCode });
|
|
649
|
-
const formData = await stampChildRowStatuses(session, args.schemaCode, args.formData);
|
|
649
|
+
const formData = (await stampChildRowStatuses(session, args.schemaCode, args.formData)).formData;
|
|
650
650
|
if (typeof args.workflowCode === "string" && args.workflowCode.trim()) {
|
|
651
651
|
// Portal draft-save shape. The row id travels in data.id (the portal
|
|
652
652
|
// form SDK receives a pre-generated id from form/load and echoes it);
|
|
@@ -808,7 +808,7 @@ export function registerRuntimeDataLane(tools) {
|
|
|
808
808
|
message: "no live business object with this id; refusing to update (the platform would otherwise insert a new row)",
|
|
809
809
|
});
|
|
810
810
|
}
|
|
811
|
-
const formData = await stampChildRowStatuses(session, args.schemaCode, args.formData);
|
|
811
|
+
const formData = (await stampChildRowStatuses(session, args.schemaCode, args.formData)).formData;
|
|
812
812
|
const performed = await wireCall(session, {
|
|
813
813
|
path: ENDPOINTS.runtimeFormSave,
|
|
814
814
|
method: "POST",
|
|
@@ -907,6 +907,7 @@ export function registerRuntimeDataLane(tools) {
|
|
|
907
907
|
workItemId: z.string().optional(),
|
|
908
908
|
bizObjectId: z.string().optional().describe("existing record id; REQUIRED for workItem approval submits (formType=1 + workItemId) — the submit's bizObject must reference the in-flight record or the engine NPEs (DataChangeHandlerImpl.changeBizObject)"),
|
|
909
909
|
agree: z.boolean().optional(),
|
|
910
|
+
allowChildRowAppend: z.boolean().optional().describe("默认 false:审批提交(带 workItemId/workflowInstanceId)时 formData 含子表数组会被拒——平台按追加插行,每次提交重复一份。确实要追加子行时才置 true。"),
|
|
910
911
|
approval: z.record(z.string(), z.unknown()).optional(),
|
|
911
912
|
runMode: z.string().optional(),
|
|
912
913
|
isSimulative: z.boolean().optional(),
|
|
@@ -926,7 +927,27 @@ export function registerRuntimeDataLane(tools) {
|
|
|
926
927
|
});
|
|
927
928
|
}
|
|
928
929
|
const { sheetCode, resolvedFrom } = await resolveFormSheetCode(session, { schemaCode: args.schemaCode, sheetCode: args.sheetCode });
|
|
929
|
-
const
|
|
930
|
+
const stampedSubmit = await stampChildRowStatuses(session, args.schemaCode, args.formData);
|
|
931
|
+
const formData = stampedSubmit.formData;
|
|
932
|
+
// r5-asset 2026-10-20: the platform APPENDS child rows carried in an
|
|
933
|
+
// approval submit's formData (no upsert by row id, no error). Live: an
|
|
934
|
+
// approval that echoed the whole form twice turned 2 detail rows into 6
|
|
935
|
+
// and 30000 into 90000 while every call answered errcode 0 — node-level
|
|
936
|
+
// `editable:false` does not stop it server-side. Refuse before the wire.
|
|
937
|
+
if ((hasWorkItem || hasInstance) && args.allowChildRowAppend !== true) {
|
|
938
|
+
const carried = stampedSubmit.childCodes.filter((code) => {
|
|
939
|
+
const value = args.formData[code];
|
|
940
|
+
return Array.isArray(value) && value.length > 0;
|
|
941
|
+
});
|
|
942
|
+
if (carried.length > 0)
|
|
943
|
+
throw new ToolRefusalError({
|
|
944
|
+
status: "refused",
|
|
945
|
+
blocker: "submit-approval-child-rows",
|
|
946
|
+
schemaCode: args.schemaCode,
|
|
947
|
+
childTables: carried,
|
|
948
|
+
message: `审批提交(workItemId/workflowInstanceId)的 formData 不要带子表数组:${carried.join("、")}。平台按「追加行」处理——每提交一次就再插一份(真机:2 行 → 6 行,30000 → 90000),且不报错、审批节点的 editable:false 也拦不住。只传主表字段;确实要在审批中追加子行时显式传 allowChildRowAppend:true。`,
|
|
949
|
+
});
|
|
950
|
+
}
|
|
930
951
|
// Portal required-field semantics: the REST submit does NOT enforce them,
|
|
931
952
|
// so a missing required value must be REPORTED, not silently accepted as a
|
|
932
953
|
// portal-valid submit (dogfood R3). Best-effort read; a failure never
|
package/dist/lanes/workflow.js
CHANGED
|
@@ -691,7 +691,7 @@ function validateWorkflowRender(template, tool) {
|
|
|
691
691
|
* chain order" — reusing any edge whose endpoints survived. A draft whose
|
|
692
692
|
* (possibly branching) graph is untouched passes the chain check verbatim.
|
|
693
693
|
*/
|
|
694
|
-
function completeWorkflowChain(rules, activities) {
|
|
694
|
+
export function completeWorkflowChain(rules, activities) {
|
|
695
695
|
const codes = activities.map((activity) => String(activity.activityCode ?? "")).filter((code) => code.length > 0);
|
|
696
696
|
if (codes.length < 2)
|
|
697
697
|
return rules;
|
|
@@ -712,13 +712,24 @@ function completeWorkflowChain(rules, activities) {
|
|
|
712
712
|
}
|
|
713
713
|
for (const code of codes) {
|
|
714
714
|
const type = typeOf(code);
|
|
715
|
-
if (type !== "START" && (inbound.get(code) ?? 0)
|
|
715
|
+
if (type !== "START" && (inbound.get(code) ?? 0) < 1)
|
|
716
716
|
valid = false;
|
|
717
|
-
if (type !== "END" && (outbound.get(code) ?? 0)
|
|
717
|
+
if (type !== "END" && (outbound.get(code) ?? 0) < 1)
|
|
718
718
|
valid = false;
|
|
719
719
|
}
|
|
720
720
|
if (valid)
|
|
721
721
|
return rules;
|
|
722
|
+
// r5-asset 2026-10-20: a node with TWO outgoing edges is a conditional
|
|
723
|
+
// branch (condition edge + utilizeElse fallback), NOT an invalid chain. The
|
|
724
|
+
// old rule demanded outbound === 1, so every branched graph fell through to
|
|
725
|
+
// the linear rebuild below — which maps consecutive activity codes and
|
|
726
|
+
// silently DROPS the else edge. Live symptom: a workflow.update that sent
|
|
727
|
+
// activities without routes erased A3→A5, every low-amount instance stalled
|
|
728
|
+
// in PROCESSING with errcode 0, and the only trace was a missing edge in
|
|
729
|
+
// get_published. Never linearize a graph that already branches.
|
|
730
|
+
const branched = [...outbound.values()].some((count) => count > 1);
|
|
731
|
+
if (branched)
|
|
732
|
+
return rules;
|
|
722
733
|
const byEdge = new Map(rules.map((rule) => [`${rule.preActivityCode}->${rule.postActivityCode}`, rule]));
|
|
723
734
|
return codes.slice(0, -1).map((code, index) => byEdge.get(`${code}->${codes[index + 1]}`) ?? { preActivityCode: code, postActivityCode: codes[index + 1] });
|
|
724
735
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stalfh233/omc-cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.63",
|
|
4
4
|
"description": "CloudPivot 8.6 configuration capability CLI — governed, fail-closed, coverage-manifest bounded.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -50,9 +50,9 @@
|
|
|
50
50
|
"access": "public"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@stalfh233/omc-fake-platform": "0.4.
|
|
53
|
+
"@stalfh233/omc-fake-platform": "0.4.63",
|
|
54
54
|
"commander": "14.0.3",
|
|
55
|
-
"omc-core": "0.4.
|
|
55
|
+
"omc-core": "0.4.63",
|
|
56
56
|
"proper-lockfile": "4.1.2",
|
|
57
57
|
"write-file-atomic": "6.0.0",
|
|
58
58
|
"zod": "^3.23.8",
|
|
@@ -125,3 +125,18 @@ omc call field.publish --env local --args '{"schemaCode":"expense"}'
|
|
|
125
125
|
## 固定验收顺序
|
|
126
126
|
|
|
127
127
|
执行阶段以 `omc-five-piece-flow` 为准;写面安全只额外强调两点:写前先 `commands/describe/contract` + dry-run 静态校验,写后权威 readback,必要时再用 CLI 触发运行验证。
|
|
128
|
+
|
|
129
|
+
## 审批提交回传子表 = 追加行(真机 2026-10-20)
|
|
130
|
+
|
|
131
|
+
带 `workItemId` / `workflowInstanceId` 的 `data.submit` 里放子表数组,平台按**追加**处理,不按行 `id` upsert、也不报错:
|
|
132
|
+
2 行明细回传两次 → 6 行,`totalValue` 30000 → 90000,审批节点的 `editable:false` 拦不住服务端写入。
|
|
133
|
+
**只传主表字段**;确需追加行时显式 `allowChildRowAppend:true`(0.4.63 起默认拒绝,blocker `submit-approval-child-rows`)。
|
|
134
|
+
收尾时前后各读一次子表行数确认未变。
|
|
135
|
+
|
|
136
|
+
## 跨表回写只能取主表字段(真机 2026-10-20)
|
|
137
|
+
|
|
138
|
+
- `id = {子表code}.asset` / `...asset.id`:保存发布全绿,**运行不命中**,无日志。
|
|
139
|
+
- 请求体直接引用子表关联(CREATE 的 dataActions 等):提交报 `550010 业务规则使用的数据项不存在`。
|
|
140
|
+
- BRANCH 后接两个跨表 UPDATE:`50000`。
|
|
141
|
+
- 固定 id / 主表字段条件:可运行。
|
|
142
|
+
逐件联动(一张单多件资产)纯配置做不到,走二开 BIZ_ACTION 或人工。
|