@stalfh233/omc-cli 0.4.56 → 0.4.58
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.
|
@@ -499,8 +499,16 @@ export function registerRuntimeDataLane(tools) {
|
|
|
499
499
|
const selected = fields ?? Object.keys(data).filter((field) => !["id", "bizObjectId", "schemaCode", "sequenceStatus", "createdTime", "updatedTime", "creator", "updater"].includes(field));
|
|
500
500
|
return { id: row.bizObjectId ?? row.id ?? data.id, fields: Object.fromEntries(selected.map((field) => [field, data[field] ?? null])) };
|
|
501
501
|
});
|
|
502
|
+
// Live 2026-10-19: when a model has NO configured list view, the
|
|
503
|
+
// platform projects only system columns — every business field comes
|
|
504
|
+
// back null while `data.load` shows the real values. That reads as
|
|
505
|
+
// "the write failed". Detect an all-null business projection (items
|
|
506
|
+
// present, zero non-null business values) and say so.
|
|
507
|
+
const businessKeys = fields ?? Object.keys(items[0]?.fields ?? {}).filter((field) => !["id", "bizObjectId", "schemaCode", "sequenceStatus", "createdTime", "updatedTime", "creator", "updater", "name"].includes(field));
|
|
508
|
+
const projectionEmpty = items.length > 0 && businessKeys.length > 0 && items.every((item) => businessKeys.every((key) => item.fields[key] === null || item.fields[key] === undefined));
|
|
502
509
|
const readback = {
|
|
503
510
|
schemaCode: args.schemaCode,
|
|
511
|
+
...(projectionEmpty ? { projectionWarning: { reason: "business-columns-null", hint: "模型可能未配置列表视图:data.list 只投影列表列,业务列会全为 null;核对写入结果请用 data.load(或先建列表视图)" } } : {}),
|
|
504
512
|
...(args.queryCode !== undefined ? { queryCode: args.queryCode } : {}),
|
|
505
513
|
total: args.showTotal === false ? null : page.total,
|
|
506
514
|
// This branch is the non-`all` read: the platform's default filter
|
package/dist/lanes/workflow.js
CHANGED
|
@@ -914,7 +914,21 @@ async function childTableFieldsOf(session, schemaCode) {
|
|
|
914
914
|
};
|
|
915
915
|
const rows = (await read("false")).length > 0 ? await read("false") : await read("true");
|
|
916
916
|
return rows
|
|
917
|
-
.map((row) =>
|
|
917
|
+
.map((row) => {
|
|
918
|
+
const type = row.propertyType ?? row.type ?? 0;
|
|
919
|
+
// Live 2026-10-19: the list face carries `subSchema: null` on EVERY row,
|
|
920
|
+
// so `subSchema !== undefined` classified every scalar/relation field as
|
|
921
|
+
// a child table and refused `required` with a false reason. Only a
|
|
922
|
+
// NON-EMPTY subSchema marks a child table (plus the real type code:
|
|
923
|
+
// propertyType 8 = CHILD_TABLE / SHEET).
|
|
924
|
+
const subSchema = row.subSchema;
|
|
925
|
+
const hasSubSchema = subSchema !== undefined && subSchema !== null && String(subSchema).trim() !== "";
|
|
926
|
+
const isChild = Number(type) === 8 ||
|
|
927
|
+
String(type).toUpperCase() === "CHILD_TABLE" ||
|
|
928
|
+
String(type).toUpperCase() === "SHEET" ||
|
|
929
|
+
hasSubSchema;
|
|
930
|
+
return { code: String(row.code ?? row.propertyCode ?? ""), name: String(row.name ?? row.code ?? ""), isChild };
|
|
931
|
+
})
|
|
918
932
|
.filter((row) => row.isChild && row.code !== "");
|
|
919
933
|
}
|
|
920
934
|
const CHILD_TABLE_LIST_BUTTONS = [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stalfh233/omc-cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.58",
|
|
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.58",
|
|
54
54
|
"commander": "14.0.3",
|
|
55
|
-
"omc-core": "0.4.
|
|
55
|
+
"omc-core": "0.4.58",
|
|
56
56
|
"proper-lockfile": "4.1.2",
|
|
57
57
|
"write-file-atomic": "6.0.0",
|
|
58
58
|
"zod": "^3.23.8",
|
|
@@ -32,12 +32,22 @@ omc help omc-write-hazards
|
|
|
32
32
|
- `propertyEmpty` 默认 `false`。模型层保持可空;必填、只读、可见性在表单和流程节点配置。
|
|
33
33
|
- 无映射关联单选 `WORK_SHEET` 使用 `mappings: ""`,不要用 `"[]"`。`"[]"` 是非空字符串,设计器会显示“已设置”,但弹窗没有映射行。
|
|
34
34
|
- 有映射时传非空 JSON 数组字符串,并在 draft/published 两面读回。
|
|
35
|
-
- **运行时写关联值是另一种形状**(`data.save`/`data.update` 的 formData):单选关联(`propertyType 9`)传 **id 字符串或 `{ id }`**,传**数组会稳定 50000
|
|
35
|
+
- **运行时写关联值是另一种形状**(`data.save`/`data.update` 的 formData):单选关联(`propertyType 9`)传 **id 字符串或 `{ id }`**,传**数组会稳定 50000**;**多选关联(11)传分号分隔的 id 字符串 `"id1;id2"`(或 `{id:"id1;id2"}`),传数组稳定 50000**;**人员/组织(50/51/60/61)传 `[{"id","type","name"}]`,`type` 必填**(缺它稳定 50000)——`type` 是 UnitType 索引:**用户=3、部门=1**。用错形状只会拿到一个不透明的 50000。
|
|
36
36
|
- **`mappings` 由表单运行时在用户选择时套用**,服务端 `data.save` **不会**自动回填映射目标字段。API 侧要自己写被映射字段,否则读回为空——别把它误判成“映射配置失败”。
|
|
37
37
|
- **关联映射的事实源在字段行 `options.mappings`,不在表单控件属性**(真机 2026-10-14 实证:改表单 attributes 里的映射会被平台按字段重算覆盖、回读不生效)。改映射走:`field.update` 按完整 options 提交(只改 mappings,其余键原值保留)→ `field.publish` → `form.draft`(**rebuild:false**,保留现有布局与在线JS)重存 → `form.publish` → 回读核对。**别用 field.update 的 cascadeRepublish 刷已带自定义布局/在线JS 的表单**(它会 rebuild 脚手架)。
|
|
38
|
-
- **运行时写值的最小形状**(组合出错只会得到一个不透明的 `50000`/NPE,逐一最小化再合):单选关联(9) → id 字符串 `"<id>"
|
|
38
|
+
- **运行时写值的最小形状**(组合出错只会得到一个不透明的 `50000`/NPE,逐一最小化再合):单选关联(9) → id 字符串 `"<id>"`;**多选关联(11) → 分号串 `"<id1>;<id2>"`**;**人员/组织(50/51/60/61) → `[{"id":"<id>","type":3,"name":"<名>"}]`,`type` 必填且为整数**(缺/非整数 → `BizField.parseFromClient` 拆箱 NPE→50000;用户=3、部门=1);日期(3) → 字符串 `"2026-09-30 10:00:00"`;子表(8) → 行对象数组 `[{"<子列code>":<值>, ...}]`。先各写一条最小行通过,再组合。
|
|
39
39
|
- **DATE(3) 字段的 `options.format` 必须是平台大写枚举**(`YYYY-MM-DD`、`YYYY-MM-DD HH:mm:ss`、`YYYY年MM月DD日`…),**不是 Java 的 `yyyy-MM-dd`**:平台 `DateBizField` 按字面比较,Java 写法能创建字段但**首次运行时 save 稳定 `50000`(`不支持的format`)**(2026-09-30 R5 实测)。`field.create`/`intent` 会就地拒绝非枚举值。
|
|
40
40
|
|
|
41
|
+
- **多选关联(11) 的写入源码依据**(1.1.25 实机 jar):`WorkSheetMultiBizField.contentFromClient` 只接受 `String` 或 `Map`,其它类型直接 `AppAsserts.isTrue(false, "Incorrect value type, should be string or map.")` → 表现为 50000;`contentToDbValue()` 返回 `map.get("id")`,而读回时按 `";"` 切分(`MULTI_WORK_SHEET_SPILT`)并组装 `items` 数组。所以**数组是读写不对称的读侧形状,不能拿来写**;分号串才是写侧形状。实测三种形状:`"<id>"`/`"<id>;<id>"` 成功、`["<id>"]` 50000。
|
|
42
|
+
|
|
43
|
+
## 业务规则与流程事件(源码实证,1.1.25)
|
|
44
|
+
|
|
45
|
+
- **`Create` 的触发判定是「旧状态→新状态」**:旧 `DRAFT` 且新 `PROCESSING` 或 `COMPLETED`(`DomainServiceImpl#getDefaultRuleCode`)。所以无流程模型一调 `data.submit`(→COMPLETED)**和流程表单提交(DRAFT→PROCESSING)都触发 Create**;`data.save` 存草稿不触发。不要因为"目标表没变化"就断言规则没触发。
|
|
46
|
+
- **副作用缺失归因三步**:先查执行记录(`h_log_biz_service` 按服务/方法/时间,或容器日志)→ ①无调用=规则/触发问题 ②有调用但失败=参数/权限/业务异常 ③调用成功但不可见=视图投影/事务未提交/查询条件。禁止只看目标表。
|
|
47
|
+
- **BIZ_ACTION 判成功只看传输**:REST 适配器解析响应后直接返回,**不校验 `errcode`**。扩展接口回 `{"errcode":50000}` + HTTP 200 会被当成节点成功(日志 `biz action node execute successful`)。要阻断必须走表单 `onValidate` 或数据规则,不能靠业务返回码。
|
|
48
|
+
- **流程事件处理器吞异常**:`start`/`end`/`cancel`/`activate` 中的 BIZ_ACTION 抛异常时,`InstanceContext#processingBusiness` 只记 `processingBusiness error.` 并返回失败结果,**流程继续**。事件处理器不能做"提交前拦截"。
|
|
49
|
+
- **禁写当前单据**:BIZ_ACTION 是 HTTP 自调用,外层事务已持行锁;同事务更新当前单据会等到 15s 超时并整条回滚。副作用写独立表;需要留痕用独立事务/异步重建 `RequestContext`。
|
|
50
|
+
|
|
41
51
|
## 运行数据读取的默认过滤
|
|
42
52
|
|
|
43
53
|
- `data.list` **默认不含 DRAFT**(`total` 也只算 COMPLETED):清测试数据时 `total:0` 不代表没有残留。核对/清理一律显式 `{"all":true,"showTotal":true}`,或读回体里的过滤标注。`data.load` 用于单行核对。
|