@stalfh233/omc-cli 0.0.0-stage → 0.4.0
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/LICENSE +21 -0
- package/README.md +304 -3
- package/coverage/m1-coverage-manifest-v1.json +1278 -0
- package/dist/approval-token.js +102 -0
- package/dist/args.js +25 -0
- package/dist/artifacts.js +119 -0
- package/dist/bench/call-face-eval.js +256 -0
- package/dist/bench/context-attribution.js +151 -0
- package/dist/bench/discovery-cost-eval.js +230 -0
- package/dist/bench/driver.js +81 -0
- package/dist/bench/evals.js +236 -0
- package/dist/bench/fake-http-server.js +65 -0
- package/dist/bench/instrument.js +87 -0
- package/dist/bench/intent-face-eval.js +343 -0
- package/dist/bench/run.js +166 -0
- package/dist/bench/scenario.js +343 -0
- package/dist/bench/types.js +76 -0
- package/dist/bi-wire.js +41 -0
- package/dist/bizservice-config.js +581 -0
- package/dist/call.js +153 -0
- package/dist/capability-absences.js +23 -0
- package/dist/capability-overview.js +492 -0
- package/dist/capability-shape.js +154 -0
- package/dist/cli-contract.js +70 -0
- package/dist/cli-output.js +73 -0
- package/dist/cli.js +1418 -0
- package/dist/code-rules.js +69 -0
- package/dist/command-transport.js +155 -0
- package/dist/config-store.js +195 -0
- package/dist/context.js +21 -0
- package/dist/contract-consistency.js +66 -0
- package/dist/contract-resources.js +62 -0
- package/dist/coverage-consistency.js +62 -0
- package/dist/coverage-registry.js +76 -0
- package/dist/coverage.js +130 -0
- package/dist/data-list-filter.js +114 -0
- package/dist/discovery.js +390 -0
- package/dist/endpoints.js +154 -0
- package/dist/environment-policy.js +26 -0
- package/dist/execution-metadata.js +1092 -0
- package/dist/fake/app.js +45 -0
- package/dist/fake/b2-registration.js +594 -0
- package/dist/fake/businessrule.js +242 -0
- package/dist/fake/datarule.js +72 -0
- package/dist/fake/dictionary.js +108 -0
- package/dist/fake/environment.js +30 -0
- package/dist/fake/field.js +167 -0
- package/dist/fake/form.js +101 -0
- package/dist/fake/index.js +121 -0
- package/dist/fake/list-view.js +289 -0
- package/dist/fake/model.js +136 -0
- package/dist/fake/online-js.js +18 -0
- package/dist/fake/report.js +253 -0
- package/dist/fake/routes.js +47 -0
- package/dist/fake/rule-lifecycle.js +37 -0
- package/dist/fake/runtime-data.js +367 -0
- package/dist/fake/state.js +67 -0
- package/dist/fake/workflow.js +364 -0
- package/dist/field-change.js +200 -0
- package/dist/field-families.js +896 -0
- package/dist/form-layout.js +111 -0
- package/dist/form-support.js +821 -0
- package/dist/goal-routes.js +468 -0
- package/dist/governed-execution.js +87 -0
- package/dist/human-summary.js +212 -0
- package/dist/identity.js +62 -0
- package/dist/intent/baseline.js +57 -0
- package/dist/intent/capabilities/bizservice.js +274 -0
- package/dist/intent/capabilities/businessrule.js +493 -0
- package/dist/intent/capabilities/datarule.js +187 -0
- package/dist/intent/capabilities/field.js +545 -0
- package/dist/intent/capabilities/form.js +136 -0
- package/dist/intent/capabilities/index.js +64 -0
- package/dist/intent/capabilities/listview.js +157 -0
- package/dist/intent/capabilities/model.js +100 -0
- package/dist/intent/capabilities/onlinejs.js +108 -0
- package/dist/intent/capabilities/report.js +355 -0
- package/dist/intent/capabilities/workflow.js +458 -0
- package/dist/intent/capability.js +6 -0
- package/dist/intent/cli.js +91 -0
- package/dist/intent/compare.js +56 -0
- package/dist/intent/compiler.js +79 -0
- package/dist/intent/dsl.js +129 -0
- package/dist/intent/plan-file.js +63 -0
- package/dist/intent/readback.js +65 -0
- package/dist/intent/schema.js +158 -0
- package/dist/intent/validation.js +30 -0
- package/dist/intent/yaml.js +315 -0
- package/dist/json-column.js +68 -0
- package/dist/lanes/app-contract.js +95 -0
- package/dist/lanes/app-coverage.js +16 -0
- package/dist/lanes/app.js +174 -0
- package/dist/lanes/apply-changes.js +231 -0
- package/dist/lanes/b2-registration-contract.js +292 -0
- package/dist/lanes/b2-registration-coverage.js +48 -0
- package/dist/lanes/b2-registration.js +1187 -0
- package/dist/lanes/businessrule-contract.js +232 -0
- package/dist/lanes/businessrule-coverage.js +16 -0
- package/dist/lanes/businessrule.js +221 -0
- package/dist/lanes/contract-support.js +65 -0
- package/dist/lanes/coverage-declaration.js +9 -0
- package/dist/lanes/datarule-contract.js +155 -0
- package/dist/lanes/datarule-coverage.js +19 -0
- package/dist/lanes/datarule-protocol.js +308 -0
- package/dist/lanes/datarule.js +818 -0
- package/dist/lanes/dictionary-contract.js +78 -0
- package/dist/lanes/dictionary-coverage.js +24 -0
- package/dist/lanes/dictionary.js +235 -0
- package/dist/lanes/environment-contract.js +70 -0
- package/dist/lanes/environment-coverage.js +14 -0
- package/dist/lanes/environment.js +163 -0
- package/dist/lanes/field-contract.js +223 -0
- package/dist/lanes/field-coverage.js +27 -0
- package/dist/lanes/field.js +374 -0
- package/dist/lanes/form-contract.js +97 -0
- package/dist/lanes/form-coverage.js +16 -0
- package/dist/lanes/form.js +185 -0
- package/dist/lanes/lane-ids.js +34 -0
- package/dist/lanes/list-view-contract.js +175 -0
- package/dist/lanes/list-view-coverage.js +20 -0
- package/dist/lanes/list-view-shapes.js +1207 -0
- package/dist/lanes/list-view.js +578 -0
- package/dist/lanes/meta-contract.js +85 -0
- package/dist/lanes/meta.js +255 -0
- package/dist/lanes/model-contract.js +168 -0
- package/dist/lanes/model-coverage.js +20 -0
- package/dist/lanes/model.js +986 -0
- package/dist/lanes/online-js-contract.js +99 -0
- package/dist/lanes/online-js-coverage.js +28 -0
- package/dist/lanes/online-js.js +127 -0
- package/dist/lanes/report-contract.js +144 -0
- package/dist/lanes/report-coverage.js +21 -0
- package/dist/lanes/report.js +476 -0
- package/dist/lanes/rule-graph.js +1846 -0
- package/dist/lanes/rule-lifecycle-contract.js +92 -0
- package/dist/lanes/rule-lifecycle-coverage.js +20 -0
- package/dist/lanes/rule-lifecycle.js +176 -0
- package/dist/lanes/runtime-data-contract.js +264 -0
- package/dist/lanes/runtime-data-coverage.js +25 -0
- package/dist/lanes/runtime-data.js +1054 -0
- package/dist/lanes/workflow-contract.js +223 -0
- package/dist/lanes/workflow-coverage.js +40 -0
- package/dist/lanes/workflow.js +1813 -0
- package/dist/online-js-layout.js +58 -0
- package/dist/online-js-source.js +276 -0
- package/dist/package-tool.js +51 -0
- package/dist/package.js +73 -0
- package/dist/plan.js +73 -0
- package/dist/read.js +144 -0
- package/dist/redact.js +28 -0
- package/dist/rule-support.js +587 -0
- package/dist/runtime-support.js +134 -0
- package/dist/server.js +92 -0
- package/dist/session-manager.js +30 -0
- package/dist/session.js +149 -0
- package/dist/skills.js +112 -0
- package/dist/support.js +98 -0
- package/dist/tool-types.js +127 -0
- package/dist/tools.js +59 -0
- package/dist/usage-log.js +197 -0
- package/dist/wire.js +287 -0
- package/dist/workflow-support.js +99 -0
- package/dist/write-lease.js +26 -0
- package/dist/write-lock.js +109 -0
- package/dist/write.js +176 -0
- package/dist/zip.js +156 -0
- package/docs/tool-surface-map.md +42 -0
- package/package.json +71 -6
- package/skills/omc-acceptance-criteria.md +47 -0
- package/skills/omc-business-configuration.md +257 -0
- package/skills/omc-capabilities.md +197 -0
- package/skills/omc-capability-scouting.md +55 -0
- package/skills/omc-config-draft-review.md +171 -0
- package/skills/omc-five-piece-flow.md +35 -0
- package/skills/omc-glossary.md +86 -0
- package/skills/omc-refusals.md +110 -0
- package/skills/omc-requirement-analysis.md +161 -0
- package/skills/omc-requirement-vocabulary.md +51 -0
- package/skills/omc-start-here.md +82 -0
- package/skills/omc-tool-selection.md +119 -0
- package/skills/omc-write-hazards.md +87 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { RULE_STATUS } from "../lanes/businessrule-contract.js";
|
|
2
|
+
import { DYNAMIC_ACTION_SOURCE_KEY, DYNAMIC_CONDITION_SOURCE_KEY } from "../lanes/rule-graph.js";
|
|
3
|
+
import { NOT_HANDLED } from "./state.js";
|
|
4
|
+
/**
|
|
5
|
+
* B1-businessrule lane: rule listing/read + save (upsert). Twin of
|
|
6
|
+
* src/lanes/businessrule.ts. Both list faces share one serializer: the app
|
|
7
|
+
* face is driven by the rule-lifecycle lane, the runtime face by this lane.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Ticket 97: scan a saved rule node graph for methodMapping bindings and flag
|
|
11
|
+
* the bound biz-service methods referenced (the platform derives the same
|
|
12
|
+
* flag from its mapping tables; a referenced method refuses method/delete
|
|
13
|
+
* with 500108).
|
|
14
|
+
*/
|
|
15
|
+
function markReferencedMethods(ctx, nodeJson) {
|
|
16
|
+
if (typeof nodeJson !== "string")
|
|
17
|
+
return;
|
|
18
|
+
let nodes;
|
|
19
|
+
try {
|
|
20
|
+
nodes = JSON.parse(nodeJson);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (!Array.isArray(nodes))
|
|
26
|
+
return;
|
|
27
|
+
for (const entry of nodes) {
|
|
28
|
+
if (!entry || typeof entry !== "object")
|
|
29
|
+
continue;
|
|
30
|
+
const mapping = entry.methodMapping;
|
|
31
|
+
if (!mapping || typeof mapping !== "object" || Array.isArray(mapping))
|
|
32
|
+
continue;
|
|
33
|
+
const m = mapping;
|
|
34
|
+
const serviceCode = typeof m.serviceCode === "string" ? m.serviceCode : "";
|
|
35
|
+
const serviceMethodCode = typeof m.serviceMethodCode === "string" ? m.serviceMethodCode : "";
|
|
36
|
+
if (!serviceCode || !serviceMethodCode)
|
|
37
|
+
continue;
|
|
38
|
+
const method = ctx.state.services.get(serviceCode)?.methods.find((candidate) => candidate.code === serviceMethodCode);
|
|
39
|
+
if (method)
|
|
40
|
+
method.referenced = true;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export const businessRuleRoute = (ctx, input) => {
|
|
44
|
+
const q = input.query ?? {};
|
|
45
|
+
const body = (input.body ?? {});
|
|
46
|
+
switch (input.path) {
|
|
47
|
+
case "/api/api/app/businessrule/list_by_schemacode":
|
|
48
|
+
case "/api/api/runtime/business_rule/list_by_schemacode":
|
|
49
|
+
return [...ctx.state.rules.values()].filter((rule) => rule.schemaCode === q.schemaCode).map((rule) => ({
|
|
50
|
+
id: rule.id,
|
|
51
|
+
schemaCode: rule.schemaCode,
|
|
52
|
+
code: rule.code,
|
|
53
|
+
ruleCode: rule.code,
|
|
54
|
+
name: rule.name,
|
|
55
|
+
enabled: rule.enabled,
|
|
56
|
+
status: rule.status ?? (rule.enabled ? RULE_STATUS.enabled : RULE_STATUS.disabled),
|
|
57
|
+
priority: rule.priority ?? 0,
|
|
58
|
+
createdTime: rule.createdTime ?? 1730000000000,
|
|
59
|
+
creator: rule.creator ?? "fake-user",
|
|
60
|
+
// latestPublished must read the COMPUTED status, not the raw optional
|
|
61
|
+
// field: a rule enabled by businessrule/publish has no explicit
|
|
62
|
+
// publishStatus and previously reported latestPublished:false here.
|
|
63
|
+
publishStatus: rule.publishStatus ?? (rule.enabled ? "PUBLISHED" : "DRAFT"),
|
|
64
|
+
developmentMode: rule.developmentMode ?? "CODING",
|
|
65
|
+
ruleVersion: rule.ruleVersion ?? 1,
|
|
66
|
+
latestPublished: (rule.publishStatus ?? (rule.enabled ? "PUBLISHED" : "DRAFT")) === "PUBLISHED",
|
|
67
|
+
}));
|
|
68
|
+
case "/api/api/app/businessrule/get": {
|
|
69
|
+
// Ticket 109 fidelity: superseded rows stay readable by their OLD id
|
|
70
|
+
// (historical versions) — the current rows map only holds the newest.
|
|
71
|
+
const rule = [...ctx.state.rules.values()].find((row) => row.id === q.id) ?? ctx.state.ruleHistory.find((row) => row.id === q.id);
|
|
72
|
+
if (!rule)
|
|
73
|
+
return null;
|
|
74
|
+
return {
|
|
75
|
+
id: rule.id,
|
|
76
|
+
code: rule.code,
|
|
77
|
+
name: rule.name,
|
|
78
|
+
schemaCode: rule.schemaCode,
|
|
79
|
+
bizRuleType: rule.bizRuleType ?? 0,
|
|
80
|
+
enabled: rule.enabled,
|
|
81
|
+
status: rule.status ?? "DISABLED",
|
|
82
|
+
defaultRule: false,
|
|
83
|
+
developmentMode: 0,
|
|
84
|
+
ruleVersion: rule.ruleVersion ?? 1,
|
|
85
|
+
node: rule.node ?? JSON.stringify([{ nodeCode: "Start", nodeType: "START" }]),
|
|
86
|
+
route: rule.route ?? JSON.stringify([]),
|
|
87
|
+
schedulerSetting: rule.schedulerSetting ?? null,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
case "/api/api/app/businessrule/create":
|
|
91
|
+
case "/api/api/app/businessrule/update": {
|
|
92
|
+
const code = String(body.code ?? q.code ?? "");
|
|
93
|
+
const schemaCode = String(body.schemaCode ?? q.schemaCode ?? "");
|
|
94
|
+
const existing = [...ctx.state.rules.values()].find((rule) => rule.code === code && rule.schemaCode === schemaCode);
|
|
95
|
+
// Ticket 109 platform fidelity: every save (create OR update of the
|
|
96
|
+
// same code) mints a NEW record id and bumps ruleVersion; the
|
|
97
|
+
// superseded row moves to ruleHistory where get?id= still reads it —
|
|
98
|
+
// reading a stale id shows the OLD graph (live: 6509ed… → 8a8313… →
|
|
99
|
+
// 8f850e…, ruleVersion 1→4→5).
|
|
100
|
+
if (existing)
|
|
101
|
+
ctx.state.ruleHistory.push(existing);
|
|
102
|
+
ctx.state.ruleSeq = (ctx.state.ruleSeq ?? 0) + 1;
|
|
103
|
+
const id = `rule-${ctx.state.ruleSeq}`;
|
|
104
|
+
ctx.state.rules.set(`${schemaCode}#${code}`, {
|
|
105
|
+
...existing,
|
|
106
|
+
id,
|
|
107
|
+
schemaCode,
|
|
108
|
+
code,
|
|
109
|
+
name: String(body.name ?? code),
|
|
110
|
+
enabled: body.enabled === true,
|
|
111
|
+
bizRuleType: typeof body.bizRuleType === "number" ? body.bizRuleType : existing?.bizRuleType,
|
|
112
|
+
node: typeof body.node === "string" ? body.node : existing?.node,
|
|
113
|
+
route: typeof body.route === "string" ? body.route : existing?.route,
|
|
114
|
+
schedulerSetting: typeof body.schedulerSetting === "string" ? body.schedulerSetting : body.schedulerSetting === null ? null : existing?.schedulerSetting,
|
|
115
|
+
priority: typeof body.priority === "number" ? body.priority : existing?.priority,
|
|
116
|
+
createdTime: typeof body.createdTime === "number" ? body.createdTime : existing?.createdTime,
|
|
117
|
+
creator: typeof body.creator === "string" ? body.creator : existing?.creator,
|
|
118
|
+
publishStatus: typeof body.publishStatus === "string" ? body.publishStatus : existing?.publishStatus,
|
|
119
|
+
developmentMode: typeof body.developmentMode === "string" ? body.developmentMode : existing?.developmentMode,
|
|
120
|
+
status: typeof body.status === "string" ? body.status : RULE_STATUS.disabled,
|
|
121
|
+
ruleVersion: (existing?.ruleVersion ?? 0) + 1,
|
|
122
|
+
});
|
|
123
|
+
// Ticket 97 fidelity: a saved methodMapping (serviceCode+serviceMethodCode
|
|
124
|
+
// inside the node graph) marks the bound method referenced — the
|
|
125
|
+
// platform derives the same flag from the mapping tables, and a
|
|
126
|
+
// referenced method refuses method/delete with 500108.
|
|
127
|
+
markReferencedMethods(ctx, typeof body.node === "string" ? body.node : existing?.node);
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
default:
|
|
131
|
+
return NOT_HANDLED;
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
export function applyBusinessRules(state, event, source) {
|
|
135
|
+
for (const rule of state.rules.values()) {
|
|
136
|
+
if (!rule.enabled || rule.schemaCode !== String(source.schemaCode ?? "") || rule.code !== event)
|
|
137
|
+
continue;
|
|
138
|
+
for (const node of parseJsonArray(rule.node)) {
|
|
139
|
+
if (node.nodeType !== "UPDATE" || typeof node.targetObjectCode !== "string")
|
|
140
|
+
continue;
|
|
141
|
+
const conditions = readConditionGroups(node.filterCondition);
|
|
142
|
+
for (const target of state.dataRows.values()) {
|
|
143
|
+
if (target.schemaCode !== node.targetObjectCode || !conditions.every((group) => group.some((andGroup) => andGroup.every((leaf) => matchesRuleCondition(leaf, target, source)))))
|
|
144
|
+
continue;
|
|
145
|
+
applyActions(target, node.dataActions, source);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
function parseJsonArray(value) {
|
|
151
|
+
if (!value)
|
|
152
|
+
return [];
|
|
153
|
+
try {
|
|
154
|
+
const parsed = JSON.parse(value);
|
|
155
|
+
return Array.isArray(parsed) ? parsed.filter(isRecord) : [];
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
return [];
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function readConditionGroups(filterCondition) {
|
|
162
|
+
if (!isRecord(filterCondition) || !Array.isArray(filterCondition.mainConditions))
|
|
163
|
+
return [];
|
|
164
|
+
return filterCondition.mainConditions.map((group) => {
|
|
165
|
+
if (!Array.isArray(group))
|
|
166
|
+
return [];
|
|
167
|
+
if (group.every(isRecord))
|
|
168
|
+
return [group];
|
|
169
|
+
return group.map((andGroup) => (Array.isArray(andGroup) ? andGroup.filter(isRecord) : []));
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
function matchesRuleCondition(condition, target, source) {
|
|
173
|
+
const targetField = condition.targetSchemaDataItem;
|
|
174
|
+
if (typeof targetField !== "string")
|
|
175
|
+
return false;
|
|
176
|
+
if (condition.ruleValueType === "DYNAMIC") {
|
|
177
|
+
// Shared wire key (rule-graph.ts): the DYNAMIC filter source lives in
|
|
178
|
+
// currentSchemaDataItem and value must not carry it (ticket 09).
|
|
179
|
+
const sourceField = condition[DYNAMIC_CONDITION_SOURCE_KEY];
|
|
180
|
+
if (typeof sourceField !== "string" || !sourceField || condition.value !== undefined)
|
|
181
|
+
return false;
|
|
182
|
+
return compareRuleValues(readRuleField(target, targetField), source[sourceField], String(condition.ruleConditionType ?? "EQ"));
|
|
183
|
+
}
|
|
184
|
+
if (condition.ruleValueType === "FIXED")
|
|
185
|
+
return compareRuleValues(readRuleField(target, targetField), condition.value, String(condition.ruleConditionType ?? "EQ"));
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
function readRuleField(target, field) {
|
|
189
|
+
const value = field === "id" ? target.id ?? target.bizObjectId : target[field];
|
|
190
|
+
if (value && typeof value === "object" && !Array.isArray(value) && typeof value.id === "string")
|
|
191
|
+
return value.id;
|
|
192
|
+
return value;
|
|
193
|
+
}
|
|
194
|
+
function compareRuleValues(actual, expected, operator) {
|
|
195
|
+
switch (operator) {
|
|
196
|
+
case "EQ": return fixedValueEqual(actual, expected);
|
|
197
|
+
case "NEQ": return !fixedValueEqual(actual, expected);
|
|
198
|
+
case "GT": return Number(actual) > Number(expected);
|
|
199
|
+
case "LT": return Number(actual) < Number(expected);
|
|
200
|
+
case "GTEQ": return Number(actual) >= Number(expected);
|
|
201
|
+
case "LTEQ": return Number(actual) <= Number(expected);
|
|
202
|
+
case "CT": return String(actual ?? "").includes(String(expected ?? ""));
|
|
203
|
+
case "NCT": return !String(actual ?? "").includes(String(expected ?? ""));
|
|
204
|
+
case "EP": return actual === null || actual === undefined || actual === "";
|
|
205
|
+
case "NEP": return actual !== null && actual !== undefined && actual !== "";
|
|
206
|
+
default: return false;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
function applyActions(target, actions, source) {
|
|
210
|
+
if (!Array.isArray(actions))
|
|
211
|
+
return;
|
|
212
|
+
for (const action of actions) {
|
|
213
|
+
if (!isRecord(action) || action.ruleActionType !== "EQUALS" || typeof action.targetDataItem !== "string")
|
|
214
|
+
continue;
|
|
215
|
+
if (action.ruleValueType === "DYNAMIC") {
|
|
216
|
+
// Shared wire key (rule-graph.ts): the DYNAMIC action source lives in
|
|
217
|
+
// currentDataItemValue with value pinned to the empty string.
|
|
218
|
+
const sourceField = action[DYNAMIC_ACTION_SOURCE_KEY];
|
|
219
|
+
if (typeof sourceField === "string" && sourceField && action.value === "")
|
|
220
|
+
target[action.targetDataItem] = source[sourceField];
|
|
221
|
+
}
|
|
222
|
+
else if (action.ruleValueType === "FIXED") {
|
|
223
|
+
target[action.targetDataItem] = action.value;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function fixedValueEqual(actual, expected) {
|
|
228
|
+
const actualId = relationId(actual) ?? actual;
|
|
229
|
+
const expectedId = relationId(expected) ?? expected;
|
|
230
|
+
if (Array.isArray(actualId) || Array.isArray(expectedId))
|
|
231
|
+
return JSON.stringify(actualId) === JSON.stringify(expectedId);
|
|
232
|
+
return actualId === expectedId || String(actualId ?? "") === String(expectedId ?? "");
|
|
233
|
+
}
|
|
234
|
+
function relationId(value) {
|
|
235
|
+
if (!isRecord(value) || typeof value.id !== "string")
|
|
236
|
+
return undefined;
|
|
237
|
+
return value.id;
|
|
238
|
+
}
|
|
239
|
+
function isRecord(value) {
|
|
240
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
241
|
+
}
|
|
242
|
+
//# sourceMappingURL=businessrule.js.map
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { businessRefusal, NOT_HANDLED } from "./state.js";
|
|
2
|
+
/** G1-datarule lane: 8.6.26 field-level data rules. Twin of src/lanes/datarule.ts. */
|
|
3
|
+
function normalizeRule(id, body) {
|
|
4
|
+
const propertyCodes = Array.isArray(body.propertyCodes) ? body.propertyCodes.map((code) => String(code)) : [];
|
|
5
|
+
const propertyCode = body.propertyCode === null || body.propertyCode === undefined ? "" : String(body.propertyCode);
|
|
6
|
+
return {
|
|
7
|
+
id,
|
|
8
|
+
schemaCode: String(body.schemaCode),
|
|
9
|
+
propertyCode: propertyCode || (propertyCodes.length === 1 ? propertyCodes[0] : ""),
|
|
10
|
+
...(propertyCodes.length > 0 ? { propertyCodes } : {}),
|
|
11
|
+
dataRuleType: Number(body.dataRuleType),
|
|
12
|
+
options: typeof body.options === "string" ? body.options : JSON.stringify(body.options ?? {}),
|
|
13
|
+
...(body.checkType !== undefined ? { checkType: Number(body.checkType) } : {}),
|
|
14
|
+
...(body.childRule !== undefined ? { childRule: Boolean(body.childRule) } : {}),
|
|
15
|
+
...(body.name !== undefined ? { name: String(body.name) } : {}),
|
|
16
|
+
// Platform fidelity: the live engine stores a defaulted `sortKey: 0` on every
|
|
17
|
+
// row even when the caller omits it (bench56). The fake used to omit it, which
|
|
18
|
+
// hid a readback false-positive on an omitted sortKey.
|
|
19
|
+
sortKey: Number(body.sortKey ?? 0),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export const dataRuleRoute = (ctx, input) => {
|
|
23
|
+
const q = input.query ?? {};
|
|
24
|
+
const body = (input.body ?? {});
|
|
25
|
+
switch (input.path) {
|
|
26
|
+
case "/api/api/app/bizdatarule/list": {
|
|
27
|
+
const schemaCode = String(q.schemaCode ?? "");
|
|
28
|
+
const type = q.dataRuleType !== undefined && q.dataRuleType !== "" ? Number(q.dataRuleType) : undefined;
|
|
29
|
+
return [...ctx.state.dataRules.values()]
|
|
30
|
+
.filter((rule) => rule.schemaCode === schemaCode && (type === undefined || rule.dataRuleType === type))
|
|
31
|
+
.map((rule) => ({ ...rule }));
|
|
32
|
+
}
|
|
33
|
+
case "/api/api/app/bizdatarule/create": {
|
|
34
|
+
if (typeof body.schemaCode !== "string" || !body.schemaCode)
|
|
35
|
+
return businessRefusal(10005, "数据模型编码不能为空");
|
|
36
|
+
if (typeof body.dataRuleType !== "number")
|
|
37
|
+
return businessRefusal(10005, "规则类型不能为空");
|
|
38
|
+
// Ticket 78 live fidelity: on an UNPUBLISHED app, type2 CALCULATE answers
|
|
39
|
+
// a FALSE dead-cycle 305003 — checkCalculateRulesDeadCycle concatenates
|
|
40
|
+
// `schemaCode.propertyCode` when getAppFunctionByCode(schemaCode) is
|
|
41
|
+
// null, mismatching the assignment/parameter code口径. Types 3/4/5 are
|
|
42
|
+
// unaffected.
|
|
43
|
+
if (Number(body.dataRuleType) === 2) {
|
|
44
|
+
const model = ctx.state.models.get(String(body.schemaCode));
|
|
45
|
+
const app = ctx.state.apps.get(model?.appCode ?? "test");
|
|
46
|
+
if (app?.published === false)
|
|
47
|
+
return businessRefusal(305003, "计算规则存在死循环");
|
|
48
|
+
}
|
|
49
|
+
ctx.state.dataRuleSeq += 1;
|
|
50
|
+
const id = `datarule-${ctx.state.dataRuleSeq}`;
|
|
51
|
+
ctx.state.dataRules.set(id, normalizeRule(id, body));
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
case "/api/api/app/bizdatarule/update": {
|
|
55
|
+
const id = String(body.id ?? "");
|
|
56
|
+
const existing = ctx.state.dataRules.get(id);
|
|
57
|
+
if (!existing)
|
|
58
|
+
return businessRefusal(304001, "数据规则不存在");
|
|
59
|
+
// The platform update is a full-row overwrite; callers must echo every
|
|
60
|
+
// persisted column or the omitted nullable columns are cleared.
|
|
61
|
+
ctx.state.dataRules.set(id, normalizeRule(id, body));
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
case "/api/api/app/bizdatarule/delete": {
|
|
65
|
+
ctx.state.dataRules.delete(String(q.id ?? ""));
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
default:
|
|
69
|
+
return NOT_HANDLED;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
//# sourceMappingURL=datarule.js.map
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { NOT_HANDLED } from "./state.js";
|
|
2
|
+
function flatten(records) {
|
|
3
|
+
return records.flatMap((record) => [record, ...(record.children ? flatten(record.children) : [])]);
|
|
4
|
+
}
|
|
5
|
+
function classes(ctx) {
|
|
6
|
+
return [...ctx.state.dictionaryClasses.values()].map((item) => ({ ...item, dictionaryInfos: item.dictionaryIds.map((id) => ctx.state.dictionaries.get(id)).filter(Boolean) }));
|
|
7
|
+
}
|
|
8
|
+
function page(records, query) {
|
|
9
|
+
const searchWord = query?.searchWord ?? "";
|
|
10
|
+
const filtered = records.filter((record) => !searchWord || record.name.includes(searchWord) || record.code.includes(searchWord));
|
|
11
|
+
const pageNumber = Math.max(1, Number(query?.page ?? 1));
|
|
12
|
+
const size = Math.max(1, Number(query?.size ?? 20));
|
|
13
|
+
const content = filtered.slice((pageNumber - 1) * size, pageNumber * size);
|
|
14
|
+
return { totalElements: filtered.length, totalPages: Math.ceil(filtered.length / size), content };
|
|
15
|
+
}
|
|
16
|
+
export function dictionaryRoute(ctx, input) {
|
|
17
|
+
const dictionaries = ctx.state.dictionaries;
|
|
18
|
+
switch (input.path) {
|
|
19
|
+
case "/api/api/data_dictionary/searchDictionary": {
|
|
20
|
+
const rows = [...dictionaries.values()].filter((dictionary) => !input.query?.classificationId || dictionary.classificationId === input.query.classificationId).filter((dictionary) => !input.query?.searchWord || dictionary.name.includes(input.query.searchWord) || dictionary.code.includes(input.query.searchWord));
|
|
21
|
+
return { totalElements: rows.length, totalPages: rows.length ? 1 : 0, content: rows };
|
|
22
|
+
}
|
|
23
|
+
case "/api/api/data_dictionary/getDictionaryById":
|
|
24
|
+
return dictionaries.get(String(input.query?.id)) ?? {};
|
|
25
|
+
case "/api/api/data_dictionary/getDictionaryByCode":
|
|
26
|
+
return [...dictionaries.values()].find((dictionary) => dictionary.code === input.query?.code) ?? {};
|
|
27
|
+
case "/api/api/data_dictionary/searchDictionaryRecord": {
|
|
28
|
+
const dictionary = dictionaries.get(String(input.query?.id));
|
|
29
|
+
return page(dictionary ? flatten(dictionary.records) : [], input.query);
|
|
30
|
+
}
|
|
31
|
+
case "/api/api/data_dictionary/searchDictionaryRecordByCode": {
|
|
32
|
+
const dictionary = [...dictionaries.values()].find((item) => item.code === input.query?.code);
|
|
33
|
+
return page(dictionary ? flatten(dictionary.records) : [], input.query);
|
|
34
|
+
}
|
|
35
|
+
case "/api/api/data_dictionary/getEnableRecordsByDictionaryId": {
|
|
36
|
+
const dictionary = dictionaries.get(String(input.query?.dicId));
|
|
37
|
+
return dictionary?.status ? flatten(dictionary.records).filter((record) => record.status) : [];
|
|
38
|
+
}
|
|
39
|
+
case "/api/api/data_dictionary/getEnableRecordsByDictionaryCode": {
|
|
40
|
+
const dictionary = [...dictionaries.values()].find((item) => item.code === input.query?.code);
|
|
41
|
+
return dictionary?.status ? flatten(dictionary.records).filter((record) => record.status) : [];
|
|
42
|
+
}
|
|
43
|
+
case "/api/api/data_dictionary/getDictionaryRecordByParentId": {
|
|
44
|
+
const records = [...dictionaries.values()].flatMap((dictionary) => flatten(dictionary.records)).filter((record) => record.parentId === input.query?.parentId);
|
|
45
|
+
return records;
|
|
46
|
+
}
|
|
47
|
+
case "/api/api/data_dictionary/getDictionaryClass":
|
|
48
|
+
return classes(ctx);
|
|
49
|
+
case "/api/api/data_dictionary/getDictionaryClassDetail":
|
|
50
|
+
return classes(ctx);
|
|
51
|
+
case "/api/api/data_dictionary/saveDictionaryClass": {
|
|
52
|
+
const body = input.body;
|
|
53
|
+
const id = String(body.id ?? `class-${ctx.state.dictionaryClasses.size + 1}`);
|
|
54
|
+
ctx.state.dictionaryClasses.set(id, { id, name: String(body.name), sortKey: Number(body.sortKey ?? 0), dictionaryIds: Array.isArray(body.dictionaryIds) ? body.dictionaryIds.map(String) : [] });
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
case "/api/api/data_dictionary/deleteDictionaryClass": {
|
|
58
|
+
const body = input.body;
|
|
59
|
+
ctx.state.dictionaryClasses.delete(String(body.id));
|
|
60
|
+
return 1;
|
|
61
|
+
}
|
|
62
|
+
case "/api/api/data_dictionary/deleteDictionaryClassBatch": {
|
|
63
|
+
const body = input.body;
|
|
64
|
+
for (const id of Array.isArray(body.ids) ? body.ids : [])
|
|
65
|
+
ctx.state.dictionaryClasses.delete(String(id));
|
|
66
|
+
return 1;
|
|
67
|
+
}
|
|
68
|
+
case "/api/api/data_dictionary/updateDictionaryRecordUsed": {
|
|
69
|
+
const body = input.body;
|
|
70
|
+
const ids = String(body.id ?? "").split(";");
|
|
71
|
+
for (const dictionary of dictionaries.values())
|
|
72
|
+
for (const record of flatten(dictionary.records))
|
|
73
|
+
if (ids.includes(record.id))
|
|
74
|
+
record.initialUsed = true;
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
case "/api/api/data_dictionary/saveDictionary": {
|
|
78
|
+
const body = input.body;
|
|
79
|
+
const id = String(body.id ?? `dictionary-${dictionaries.size + 1}`);
|
|
80
|
+
const records = Array.isArray(body.records) ? body.records : [];
|
|
81
|
+
const normalize = (items, parentId = null) => items.map((item, index) => {
|
|
82
|
+
const recordId = String(item.id ?? `${id}-record-${index + 1}`);
|
|
83
|
+
return { id: recordId, code: String(item.code), name: String(item.name), name_i18n: typeof item.name_i18n === "string" ? item.name_i18n : JSON.stringify(item.name_i18n ?? { "zh-CN": item.name }), parentId, status: item.status !== false, dictionaryId: id, sortKey: Number(item.sortKey ?? index + 1), initialUsed: item.initialUsed === true, ...(Array.isArray(item.children) ? { children: normalize(item.children, recordId) } : {}) };
|
|
84
|
+
});
|
|
85
|
+
const dictionary = { id, code: String(body.code), name: String(body.name), name_i18n: typeof body.name_i18n === "string" ? body.name_i18n : undefined, dictionaryType: Number(body.dictionaryType ?? 0), status: body.status !== false, sortKey: Number(body.sortKey ?? 0), classificationId: String(body.classificationId ?? ""), records: normalize(records) };
|
|
86
|
+
dictionaries.set(id, dictionary);
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
case "/api/api/data_dictionary/updateDictionaryStatus": {
|
|
90
|
+
const body = input.body;
|
|
91
|
+
const dictionary = dictionaries.get(String(body.id));
|
|
92
|
+
if (dictionary)
|
|
93
|
+
dictionary.status = body.status === true;
|
|
94
|
+
return 1;
|
|
95
|
+
}
|
|
96
|
+
case "/api/api/data_dictionary/updateDictionaryRecordStatus": {
|
|
97
|
+
const body = input.body;
|
|
98
|
+
for (const dictionary of dictionaries.values()) {
|
|
99
|
+
const record = flatten(dictionary.records).find((item) => item.id === String(body.id));
|
|
100
|
+
if (record)
|
|
101
|
+
record.status = body.status === true;
|
|
102
|
+
}
|
|
103
|
+
return 1;
|
|
104
|
+
}
|
|
105
|
+
default: return NOT_HANDLED;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=dictionary.js.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { FAKE_RSA_PUBLIC_KEY, NOT_HANDLED } from "./state.js";
|
|
2
|
+
/**
|
|
3
|
+
* environment-governance lane: the auth flow the session manager drives to
|
|
4
|
+
* reach the environment. Wire-contract twin of src/lanes/environment.ts.
|
|
5
|
+
*/
|
|
6
|
+
export const environmentRoute = (ctx, input) => {
|
|
7
|
+
const q = input.query ?? {};
|
|
8
|
+
switch (input.path) {
|
|
9
|
+
case "/api/public/system/config":
|
|
10
|
+
return {
|
|
11
|
+
corpId: "fake-corp",
|
|
12
|
+
client_secret: "fake-client-secret",
|
|
13
|
+
pcServerUrl: ctx.options.baseUrl ?? "https://fake.cloudpivot.example",
|
|
14
|
+
version: ctx.options.version ?? "8.6.26",
|
|
15
|
+
};
|
|
16
|
+
case "/user/public/getKey":
|
|
17
|
+
return { key: FAKE_RSA_PUBLIC_KEY, index: "1" };
|
|
18
|
+
case "/user/login/Authentication/get_code":
|
|
19
|
+
return { code: "fake-auth-code" };
|
|
20
|
+
case "/user/login/Authentication/get_token":
|
|
21
|
+
return { access_token: q.code === "fake-auth-code" ? "fake-token" : "denied" };
|
|
22
|
+
case "/api/api/organization/user/info_login":
|
|
23
|
+
if ((input.headers ?? {}).Authorization !== "Bearer fake-token")
|
|
24
|
+
return undefined;
|
|
25
|
+
return { permissions: ["ADMIN"], name: "fake-admin" };
|
|
26
|
+
default:
|
|
27
|
+
return NOT_HANDLED;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
//# sourceMappingURL=environment.js.map
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { capabilityShapeError } from "../capability-shape.js";
|
|
2
|
+
import { businessRefusal, NOT_HANDLED } from "./state.js";
|
|
3
|
+
/** field-families lane: bizproperty CRUD + model-scoped publish. Twin of src/lanes/field.ts. */
|
|
4
|
+
export const fieldRoute = (ctx, input) => {
|
|
5
|
+
const q = input.query ?? {};
|
|
6
|
+
const body = (input.body ?? {});
|
|
7
|
+
switch (input.path) {
|
|
8
|
+
case "/api/api/app/bizproperty/list": {
|
|
9
|
+
const rows = [...ctx.state.fields.entries()]
|
|
10
|
+
.filter(([key]) => key.startsWith(`${q.schemaCode}#`))
|
|
11
|
+
.map(([, field]) => ({
|
|
12
|
+
code: field.code,
|
|
13
|
+
name: field.name,
|
|
14
|
+
name_i18n: field.name_i18n ?? null,
|
|
15
|
+
type: field.type,
|
|
16
|
+
options: field.options ?? null,
|
|
17
|
+
...(field.relativeCode ? { relativeCode: field.relativeCode } : {}),
|
|
18
|
+
...(field.relativePropertyCode ? { relativePropertyCode: field.relativePropertyCode } : {}),
|
|
19
|
+
...(field.subSchema ? { subSchema: field.subSchema } : {}),
|
|
20
|
+
}))
|
|
21
|
+
.filter((row) => (q.isPublish === "true" ? ctx.state.fields.get(`${q.schemaCode}#${row.code}`)?.published : !ctx.state.fields.get(`${q.schemaCode}#${row.code}`)?.published));
|
|
22
|
+
return rows;
|
|
23
|
+
}
|
|
24
|
+
case "/api/api/app/bizproperty/get": {
|
|
25
|
+
const key = `${q.schemaCode}#${q.propertyCode}`;
|
|
26
|
+
const field = ctx.state.fields.get(key);
|
|
27
|
+
if (!field)
|
|
28
|
+
return businessRefusal(50000, `属性不存在:${q.propertyCode}`);
|
|
29
|
+
const drift = ctx.state.fieldDetailDrift.get(key);
|
|
30
|
+
const propertyType = drift?.propertyType ?? Number(field.type);
|
|
31
|
+
return {
|
|
32
|
+
code: field.code,
|
|
33
|
+
name: drift?.name ?? field.name,
|
|
34
|
+
name_i18n: field.name_i18n ?? null,
|
|
35
|
+
propertyType,
|
|
36
|
+
type: propertyType,
|
|
37
|
+
options: field.options ?? null,
|
|
38
|
+
...(field.relativeCode ? { relativeCode: field.relativeCode } : {}),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
case "/api/api/app/bizproperty/create":
|
|
42
|
+
case "/api/api/app/bizproperty/update": {
|
|
43
|
+
const key = `${body.schemaCode}#${body.code}`;
|
|
44
|
+
const existing = ctx.state.fields.get(key);
|
|
45
|
+
const type = Number(body.propertyType ?? body.type ?? existing?.type);
|
|
46
|
+
let options = {};
|
|
47
|
+
if (typeof body.options === "string") {
|
|
48
|
+
try {
|
|
49
|
+
options = JSON.parse(body.options);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return businessRefusal(50000, "options 不是合法 JSON");
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
else if (body.options && typeof body.options === "object") {
|
|
56
|
+
options = body.options;
|
|
57
|
+
}
|
|
58
|
+
if (Number.isInteger(type)) {
|
|
59
|
+
const shapeError = capabilityShapeError(type, "field", options);
|
|
60
|
+
if (shapeError)
|
|
61
|
+
return businessRefusal(50000, shapeError);
|
|
62
|
+
}
|
|
63
|
+
let subSchema;
|
|
64
|
+
if (body.subSchema && typeof body.subSchema === "object")
|
|
65
|
+
subSchema = body.subSchema;
|
|
66
|
+
else if (typeof body.subSchema === "string") {
|
|
67
|
+
try {
|
|
68
|
+
subSchema = JSON.parse(body.subSchema);
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return businessRefusal(50000, "subSchema 不是合法 JSON");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
else if (existing?.subSchema)
|
|
75
|
+
subSchema = existing.subSchema;
|
|
76
|
+
ctx.state.fields.set(key, {
|
|
77
|
+
code: String(body.code),
|
|
78
|
+
name: String(body.name ?? body.code),
|
|
79
|
+
type: String(body.propertyType ?? body.type ?? "TEXT"),
|
|
80
|
+
published: existing?.published ?? false,
|
|
81
|
+
...(typeof body.name_i18n === "string" ? { name_i18n: body.name_i18n } : existing?.name_i18n !== undefined ? { name_i18n: existing.name_i18n } : {}),
|
|
82
|
+
...(body.options !== undefined ? { options: String(body.options) } : existing?.options !== undefined ? { options: existing.options } : {}),
|
|
83
|
+
...(body.relativeCode ? { relativeCode: String(body.relativeCode) } : existing?.relativeCode ? { relativeCode: existing.relativeCode } : {}),
|
|
84
|
+
...(body.relativePropertyCode !== undefined ? { relativePropertyCode: String(body.relativePropertyCode) } : existing?.relativePropertyCode !== undefined ? { relativePropertyCode: existing.relativePropertyCode } : {}),
|
|
85
|
+
...(subSchema ? { subSchema } : {}),
|
|
86
|
+
});
|
|
87
|
+
// Live 2026-09-18: creating/updating a field whose schemaCode IS a
|
|
88
|
+
// CHILD_TABLE field code adds/updates that column and syncs the parent
|
|
89
|
+
// field's subSchema.properties. Mirror it so the form scaffold sees new
|
|
90
|
+
// child columns.
|
|
91
|
+
const childSchema = String(body.schemaCode ?? "");
|
|
92
|
+
const childCode = String(body.code ?? "");
|
|
93
|
+
for (const parent of ctx.state.fields.values()) {
|
|
94
|
+
if (parent.type !== "8" || parent.code !== childSchema)
|
|
95
|
+
continue;
|
|
96
|
+
const sub = parent.subSchema;
|
|
97
|
+
if (!sub || !Array.isArray(sub.properties))
|
|
98
|
+
continue;
|
|
99
|
+
const row = {
|
|
100
|
+
code: childCode,
|
|
101
|
+
name: String(body.name ?? childCode),
|
|
102
|
+
...(typeof body.name_i18n === "string" ? { name_i18n: body.name_i18n } : {}),
|
|
103
|
+
schemaCode: childSchema,
|
|
104
|
+
propertyType: type,
|
|
105
|
+
propertyEmpty: body.propertyEmpty === true,
|
|
106
|
+
sortKey: null,
|
|
107
|
+
...(body.options !== undefined ? { options: String(body.options) } : {}),
|
|
108
|
+
defaultProperty: false,
|
|
109
|
+
published: false,
|
|
110
|
+
};
|
|
111
|
+
const at = sub.properties.findIndex((candidate) => candidate.code === childCode);
|
|
112
|
+
if (at >= 0)
|
|
113
|
+
sub.properties[at] = { ...sub.properties[at], ...row };
|
|
114
|
+
else
|
|
115
|
+
sub.properties.push(row);
|
|
116
|
+
}
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
case "/api/api/app/bizproperty/publish": {
|
|
120
|
+
const schemaCode = String(body.bizSchemaCode ?? body.schemaCode);
|
|
121
|
+
// Ticket 79 live-fidelity (docker log evidence, 2026-09-13 21:43-21:45):
|
|
122
|
+
// under DB connection-pool contention a publish can fail with
|
|
123
|
+
// `表单:<t>_<schema>不存在列:<col>` (BizSchemaDbManagerImpl addIndexes/
|
|
124
|
+
// updateColumns assert against the serializer view) while the DDL from
|
|
125
|
+
// the failed attempt auto-commits — the state SELF-HEALS across
|
|
126
|
+
// retries (live: attempt#2 failed on ownerDeptQueryCode, #3 on
|
|
127
|
+
// omc_mcp_wt_num, #4 succeeded). Tests seed the remaining count via
|
|
128
|
+
// state.transientPublishFailures.
|
|
129
|
+
const remaining = ctx.state.transientPublishFailures.get(schemaCode) ?? 0;
|
|
130
|
+
if (remaining > 0) {
|
|
131
|
+
ctx.state.transientPublishFailures.set(schemaCode, remaining - 1);
|
|
132
|
+
return businessRefusal(50000, `表单:ixtw3_${schemaCode}不存在列:ownerDeptQueryCode`);
|
|
133
|
+
}
|
|
134
|
+
for (const [key, field] of ctx.state.fields)
|
|
135
|
+
if (key.startsWith(`${schemaCode}#`))
|
|
136
|
+
field.published = true;
|
|
137
|
+
// Publishing a model publishes its child tables' schemas too (their
|
|
138
|
+
// properties live under the CHILD_TABLE field's code on the same model).
|
|
139
|
+
for (const field of ctx.state.fields.values()) {
|
|
140
|
+
if (field.type !== "8" || !ctx.state.fields.get(`${schemaCode}#${field.code}`))
|
|
141
|
+
continue;
|
|
142
|
+
for (const [key, child] of ctx.state.fields)
|
|
143
|
+
if (key.startsWith(`${field.code}#`))
|
|
144
|
+
child.published = true;
|
|
145
|
+
}
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
case "/api/api/app/bizproperty/force_delete":
|
|
149
|
+
case "/api/api/app/bizproperty/delete": {
|
|
150
|
+
const delSchema = String(q.bizSchemaCode ?? q.schemaCode ?? body.schemaCode ?? "");
|
|
151
|
+
const delCode = String(q.bizPropertyCode ?? q.code ?? body.code ?? "");
|
|
152
|
+
ctx.state.fields.delete(`${delSchema}#${delCode}`);
|
|
153
|
+
// Deleting a child column drops it from the parent child-table subSchema.
|
|
154
|
+
for (const parent of ctx.state.fields.values()) {
|
|
155
|
+
if (parent.type !== "8" || parent.code !== delSchema)
|
|
156
|
+
continue;
|
|
157
|
+
const sub = parent.subSchema;
|
|
158
|
+
if (sub && Array.isArray(sub.properties))
|
|
159
|
+
sub.properties = sub.properties.filter((candidate) => candidate.code !== delCode);
|
|
160
|
+
}
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
default:
|
|
164
|
+
return NOT_HANDLED;
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
//# sourceMappingURL=field.js.map
|