@sparkerp/plugin-sdk 0.1.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 +61 -0
- package/bundle/blocks.json +12229 -0
- package/bundle/catalog.json +32475 -0
- package/bundle/docs/applications/hcm/employee-profile.md +144 -0
- package/bundle/docs/concepts/artifact-types.md +80 -0
- package/bundle/docs/concepts/glossary.md +124 -0
- package/bundle/docs/concepts/overview.md +88 -0
- package/bundle/docs/concepts/page-data-flow.md +84 -0
- package/bundle/docs/concepts/plugin-lifecycle.md +81 -0
- package/bundle/docs/concepts/sdk-modes.md +71 -0
- package/bundle/docs/docs.json +109 -0
- package/bundle/docs/guides/add-a-cadence-job.md +134 -0
- package/bundle/docs/guides/add-a-compliance-job.md +139 -0
- package/bundle/docs/guides/add-a-create-edit-form.md +167 -0
- package/bundle/docs/guides/add-a-custom-block.md +225 -0
- package/bundle/docs/guides/add-a-data-provider.md +216 -0
- package/bundle/docs/guides/add-a-kpi.md +154 -0
- package/bundle/docs/guides/add-a-reminder-job.md +181 -0
- package/bundle/docs/guides/add-an-aggregation-job.md +153 -0
- package/bundle/docs/guides/add-an-approval-workflow.md +222 -0
- package/bundle/docs/guides/add-an-entity.md +170 -0
- package/bundle/docs/guides/add-business-rules.md +147 -0
- package/bundle/docs/guides/add-i18n.md +121 -0
- package/bundle/docs/guides/add-menus.md +149 -0
- package/bundle/docs/guides/build-a-code-plugin.md +141 -0
- package/bundle/docs/guides/build-a-page.md +213 -0
- package/bundle/docs/guides/build-a-polyglot-service-plugin.md +201 -0
- package/bundle/docs/guides/build-a-tenant-extension-service.md +149 -0
- package/bundle/docs/guides/create-a-plugin.md +172 -0
- package/bundle/docs/guides/expose-a-plugin-operation-as-an-ai-tool.md +114 -0
- package/bundle/docs/guides/index.md +53 -0
- package/bundle/docs/guides/publish-and-upgrade.md +152 -0
- package/bundle/docs/guides/responsive-plugin.md +117 -0
- package/bundle/docs/guides/seed-data-on-install.md +127 -0
- package/bundle/docs/guides/set-up-the-sdk.md +120 -0
- package/bundle/docs/guides/use-the-mcp-server.md +104 -0
- package/bundle/docs/guides/validate-and-test.md +119 -0
- package/bundle/docs/guides/wire-a-pages-data.md +153 -0
- package/bundle/docs/quickstart/index.md +118 -0
- package/bundle/docs/recipes/condition-based-compliance-flag.md +70 -0
- package/bundle/docs/recipes/cross-plugin-offboarding-fanout.md +68 -0
- package/bundle/docs/recipes/cross-row-aggregation.md +67 -0
- package/bundle/docs/recipes/entity-with-approval-workflow.md +72 -0
- package/bundle/docs/recipes/index.md +40 -0
- package/bundle/docs/recipes/install-lifecycle-data-seeding.md +76 -0
- package/bundle/docs/recipes/kpi-dashboard-page.md +92 -0
- package/bundle/docs/recipes/master-detail-page.md +84 -0
- package/bundle/docs/recipes/n-days-before-reminder.md +72 -0
- package/bundle/docs/recipes/per-row-document-generator.md +65 -0
- package/bundle/docs/recipes/related-record-lookup-field.md +74 -0
- package/bundle/docs/recipes/resolved-reference-grid-column.md +63 -0
- package/bundle/docs/recipes/scheduled-record-generation.md +68 -0
- package/bundle/docs/reference/action.md +18 -0
- package/bundle/docs/reference/block-definition.md +35 -0
- package/bundle/docs/reference/block-instance.md +33 -0
- package/bundle/docs/reference/composite-pack-manifest.md +33 -0
- package/bundle/docs/reference/connector-definition.md +36 -0
- package/bundle/docs/reference/data-service-definition.md +22 -0
- package/bundle/docs/reference/data-view-definition.md +26 -0
- package/bundle/docs/reference/entity-aggregation-config.md +38 -0
- package/bundle/docs/reference/entity-cadence-config.md +33 -0
- package/bundle/docs/reference/entity-compliance-config.md +37 -0
- package/bundle/docs/reference/entity-cross-plugin-action-config.md +37 -0
- package/bundle/docs/reference/entity-definition.md +18 -0
- package/bundle/docs/reference/entity-document-generator-config.md +35 -0
- package/bundle/docs/reference/entity-rule-definition.md +29 -0
- package/bundle/docs/reference/entity-status-date-sweep-config.md +27 -0
- package/bundle/docs/reference/form.md +35 -0
- package/bundle/docs/reference/index.md +37 -0
- package/bundle/docs/reference/menu.md +26 -0
- package/bundle/docs/reference/mobile-nav.md +22 -0
- package/bundle/docs/reference/page-template.md +29 -0
- package/bundle/docs/reference/page.md +44 -0
- package/bundle/docs/reference/plugin-manifest.md +54 -0
- package/bundle/docs/reference/plugin-seed-data.md +21 -0
- package/bundle/docs/reference/print-template-definition.md +22 -0
- package/bundle/docs/reference/provider-definition.md +22 -0
- package/bundle/docs/reference/report-definition.md +22 -0
- package/bundle/docs/reference/rule.md +30 -0
- package/bundle/docs/reference/workflow-definition.md +28 -0
- package/bundle/docs/troubleshooting/grid-shows-zero-rows.md +82 -0
- package/bundle/docs/troubleshooting/job-never-runs.md +89 -0
- package/bundle/docs/troubleshooting/known-gotchas.md +193 -0
- package/bundle/docs/troubleshooting/publishing-errors.md +119 -0
- package/bundle/docs/tutorial/01-create-the-plugin.md +157 -0
- package/bundle/docs/tutorial/02-category-entity-and-seed.md +94 -0
- package/bundle/docs/tutorial/03-equipment-entity.md +121 -0
- package/bundle/docs/tutorial/04-equipment-register-page.md +153 -0
- package/bundle/docs/tutorial/05-checkout-entity-and-page.md +116 -0
- package/bundle/docs/tutorial/06-checkout-approval-workflow.md +139 -0
- package/bundle/docs/tutorial/07-return-due-reminder-job.md +129 -0
- package/bundle/docs/tutorial/08-menus-i18n-publish.md +123 -0
- package/bundle/docs/tutorial/index.md +72 -0
- package/bundle/examples/backend/modules/hcm-assets/spk-assembly/metadata/seed-data/hcm-assets-aggregation-configs.json +18 -0
- package/bundle/examples/backend/modules/hcm-assets/spk-assembly/metadata/seed-data/hcm-assets-cadence-configs.json +9 -0
- package/bundle/examples/backend/modules/hcm-assets/spk-assembly/metadata/seed-data/hcm-assets-compliance-configs.json +74 -0
- package/bundle/examples/backend/modules/hcm-assets/spk-assembly/metadata/seed-data/hcm-assets-cross-plugin-configs.json +9 -0
- package/bundle/examples/backend/modules/hcm-assets/spk-assembly/metadata/seed-data/hcm-assets-document-configs.json +9 -0
- package/bundle/examples/backend/modules/hcm-assets/spk-assembly/metadata/seed-data/hcm-assets-sweep-configs.json +260 -0
- package/bundle/examples/backend/modules/hcm-compensation/spk-assembly/metadata/entities/hcm_salary_planning_entry.json +41 -0
- package/bundle/examples/backend/modules/hcm-compensation/spk-assembly/metadata/page/salary-planning.json +1 -0
- package/bundle/examples/backend/modules/hcm-employee/spk-assembly/metadata/page/promotions.json +10944 -0
- package/bundle/examples/backend/modules/hcm-employee/spk-assembly/metadata/workflow/employee.movement.json +44 -0
- package/bundle/examples/backend/modules/hcm-employee/spk-assembly/metadata/workflow/employee.promotion-acknowledge.json +19 -0
- package/bundle/examples/backend/modules/hcm-employee/spk-assembly/metadata/workflow/employee.resignation.json +29 -0
- package/bundle/examples/example-patterns.mjs +94 -0
- package/bundle/manifest.json +14 -0
- package/bundle/schemas/action.schema.json +102 -0
- package/bundle/schemas/block-definition.schema.json +222 -0
- package/bundle/schemas/block-instance.schema.json +139 -0
- package/bundle/schemas/composite-pack-manifest.schema.json +160 -0
- package/bundle/schemas/connector-definition.schema.json +29 -0
- package/bundle/schemas/data-service-definition.schema.json +64 -0
- package/bundle/schemas/data-view-definition.schema.json +148 -0
- package/bundle/schemas/entity-aggregation-config.schema.json +32 -0
- package/bundle/schemas/entity-cadence-config.schema.json +27 -0
- package/bundle/schemas/entity-compliance-config.schema.json +31 -0
- package/bundle/schemas/entity-cross-plugin-action-config.schema.json +31 -0
- package/bundle/schemas/entity-definition.schema.json +126 -0
- package/bundle/schemas/entity-document-generator-config.schema.json +29 -0
- package/bundle/schemas/entity-rule-definition.schema.json +75 -0
- package/bundle/schemas/entity-status-date-sweep-config.schema.json +21 -0
- package/bundle/schemas/form.schema.json +281 -0
- package/bundle/schemas/menu.schema.json +106 -0
- package/bundle/schemas/mobile-nav.schema.json +53 -0
- package/bundle/schemas/page-template.schema.json +46 -0
- package/bundle/schemas/page.schema.json +292 -0
- package/bundle/schemas/plugin-manifest.schema.json +139 -0
- package/bundle/schemas/plugin-seed-data.schema.json +24 -0
- package/bundle/schemas/print-template-definition.schema.json +90 -0
- package/bundle/schemas/provider-definition.schema.json +27 -0
- package/bundle/schemas/report-definition.schema.json +96 -0
- package/bundle/schemas/rule.schema.json +82 -0
- package/bundle/schemas/workflow-definition.schema.json +42 -0
- package/bundle/validators/block-engine.mjs +23222 -0
- package/bundle/validators/page-engine.mjs +21645 -0
- package/erp-cli/authoring-root.mjs +344 -0
- package/erp-cli/erp.mjs +4065 -0
- package/erp-cli/example-patterns.mjs +94 -0
- package/erp-mcp-server/index.mjs +818 -0
- package/package.json +27 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://erp-platform.dev/schemas/block-instance-config.schema.json",
|
|
4
|
+
"title": "Block instance-config contract v1",
|
|
5
|
+
"description": "The fragment a Phase 6 form definition or Phase 7 page definition embeds per placed block (W1-04): the block type + the pinned version it was authored against, property values (each a declared value source validated against the type's property surface), and event-to-action-chain wiring. Structured diagnostics on violation: unknown-block-type, unknown-property, property-type-mismatch, invalid-value-source, invalid-binding, version-unsatisfiable.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"$defs": {
|
|
8
|
+
"boundKey": {
|
|
9
|
+
"description": "A Phase 4 bound key (state-binding.schema.json shape): scopeKey placeholders ($self/$page/$form/$session/$user) resolve at mount.",
|
|
10
|
+
"type": "object",
|
|
11
|
+
"properties": {
|
|
12
|
+
"scope": { "enum": ["application", "tenant", "user", "session", "page", "form", "block"] },
|
|
13
|
+
"scopeKey": { "type": "string", "maxLength": 128 },
|
|
14
|
+
"key": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"minLength": 1,
|
|
17
|
+
"maxLength": 256,
|
|
18
|
+
"pattern": "^[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)*$"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"required": ["scope", "key"],
|
|
22
|
+
"additionalProperties": false
|
|
23
|
+
},
|
|
24
|
+
"propertyValue": {
|
|
25
|
+
"oneOf": [
|
|
26
|
+
{
|
|
27
|
+
"type": "object",
|
|
28
|
+
"properties": {
|
|
29
|
+
"source": { "const": "static" },
|
|
30
|
+
"value": {}
|
|
31
|
+
},
|
|
32
|
+
"required": ["source", "value"],
|
|
33
|
+
"additionalProperties": false
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"type": "object",
|
|
37
|
+
"properties": {
|
|
38
|
+
"source": { "const": "expression" },
|
|
39
|
+
"expression": { "type": "string", "minLength": 1, "maxLength": 2000 }
|
|
40
|
+
},
|
|
41
|
+
"required": ["source", "expression"],
|
|
42
|
+
"additionalProperties": false
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"type": "object",
|
|
46
|
+
"properties": {
|
|
47
|
+
"source": { "const": "binding" },
|
|
48
|
+
"binding": { "$ref": "#/$defs/boundKey" }
|
|
49
|
+
},
|
|
50
|
+
"required": ["source", "binding"],
|
|
51
|
+
"additionalProperties": false
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
"eventWiring": {
|
|
56
|
+
"description": "An event handler: a Phase 2 action chain executed through executeChain when the block emits the event.",
|
|
57
|
+
"type": "object",
|
|
58
|
+
"properties": {
|
|
59
|
+
"source": { "const": "action-chain" },
|
|
60
|
+
"actions": {
|
|
61
|
+
"type": "array",
|
|
62
|
+
"items": { "type": "object" },
|
|
63
|
+
"minItems": 1
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"required": ["source", "actions"],
|
|
67
|
+
"additionalProperties": false
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
"properties": {
|
|
71
|
+
"contractVersion": { "const": 1 },
|
|
72
|
+
"instanceId": {
|
|
73
|
+
"description": "The block's instance identity - also its Phase 4 block-scope scopeKey.",
|
|
74
|
+
"type": "string",
|
|
75
|
+
"minLength": 1,
|
|
76
|
+
"maxLength": 128
|
|
77
|
+
},
|
|
78
|
+
"blockType": {
|
|
79
|
+
"type": "string",
|
|
80
|
+
"minLength": 1,
|
|
81
|
+
"maxLength": 128,
|
|
82
|
+
"pattern": "^[a-z][a-z0-9-]*(\\.[a-z][a-z0-9-]*)+$"
|
|
83
|
+
},
|
|
84
|
+
"blockVersion": {
|
|
85
|
+
"description": "The version this instance was authored against (the pin, W1-05).",
|
|
86
|
+
"type": "string",
|
|
87
|
+
"pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)$"
|
|
88
|
+
},
|
|
89
|
+
"properties": {
|
|
90
|
+
"type": "object",
|
|
91
|
+
"propertyNames": { "pattern": "^[A-Za-z_][A-Za-z0-9_]*$" },
|
|
92
|
+
"additionalProperties": { "$ref": "#/$defs/propertyValue" }
|
|
93
|
+
},
|
|
94
|
+
"events": {
|
|
95
|
+
"type": "object",
|
|
96
|
+
"propertyNames": { "pattern": "^[A-Za-z_][A-Za-z0-9_]*$" },
|
|
97
|
+
"additionalProperties": { "$ref": "#/$defs/eventWiring" }
|
|
98
|
+
},
|
|
99
|
+
"outputs": {
|
|
100
|
+
"description": "Optional output redirection: where each output port writes. Defaults to the block's own block-scope entry named after the port.",
|
|
101
|
+
"type": "object",
|
|
102
|
+
"propertyNames": { "pattern": "^[A-Za-z_][A-Za-z0-9_]*$" },
|
|
103
|
+
"additionalProperties": { "$ref": "#/$defs/boundKey" }
|
|
104
|
+
},
|
|
105
|
+
"children": {
|
|
106
|
+
"description": "Container-class blocks only: slot name to nested instance configs.",
|
|
107
|
+
"type": "object",
|
|
108
|
+
"propertyNames": { "pattern": "^[A-Za-z_][A-Za-z0-9_]*$" },
|
|
109
|
+
"additionalProperties": {
|
|
110
|
+
"type": "array",
|
|
111
|
+
"items": { "$ref": "#" }
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
"a11yLabelKey": {
|
|
115
|
+
"description": "W10 (additive, 2026-07-16): the authored label's translation key for this placed instance (a Phase 6 field's labelKey), resolved through the runtime i18n resolver into the node's accessible name; takes precedence over the block definition's labelFrom default.",
|
|
116
|
+
"type": "string",
|
|
117
|
+
"minLength": 1,
|
|
118
|
+
"maxLength": 256
|
|
119
|
+
},
|
|
120
|
+
"name": {
|
|
121
|
+
"description": "2026-07-18: an author-facing display name (e.g. \"Header block\"), never read at render/runtime — a Studio authoring aid only.",
|
|
122
|
+
"type": "string",
|
|
123
|
+
"minLength": 1,
|
|
124
|
+
"maxLength": 128
|
|
125
|
+
},
|
|
126
|
+
"className": {
|
|
127
|
+
"description": "2026-07-17: a framework-specific class name applied to the rendered root, the universal escape hatch alongside style.",
|
|
128
|
+
"type": "string",
|
|
129
|
+
"maxLength": 512
|
|
130
|
+
},
|
|
131
|
+
"style": {
|
|
132
|
+
"description": "2026-07-17: raw inline style properties applied to the rendered root, the universal escape hatch alongside className.",
|
|
133
|
+
"type": "object",
|
|
134
|
+
"additionalProperties": { "type": ["string", "number"] }
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
"required": ["contractVersion", "instanceId", "blockType", "blockVersion", "properties"],
|
|
138
|
+
"additionalProperties": false
|
|
139
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://erp-platform.dev/schemas/plugin-manifest.schema.json",
|
|
4
|
+
"title": "Plugin/composite-pack manifest contract v1",
|
|
5
|
+
"description": "The machine-readable contract a plugin or composite pack (Country/Industry Pack, solution template) ships (W1-02...W1-08, ADR 0013): twelve extension points, a dependency graph, a distribution mode (locked vs editable), and the upgrade/data-erasure hook declarations. Cross-references (does a declared function name actually have a supplied implementation, does a dependency resolve) are checked by validatePluginManifest/resolveInstallOrder/PluginRegistry.install against the live registries and the supplied PluginImplementations, not by this schema alone - the same division of labor every earlier phase's schema draws.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"$defs": {
|
|
8
|
+
"identifier": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"minLength": 1,
|
|
11
|
+
"maxLength": 128,
|
|
12
|
+
"pattern": "^[A-Za-z_][A-Za-z0-9_.-]*$"
|
|
13
|
+
},
|
|
14
|
+
"semver": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)$"
|
|
17
|
+
},
|
|
18
|
+
"versionRange": {
|
|
19
|
+
"description": "Exact \"1.2.3\", caret \"^1.2.0\", tilde \"~1.2.0\", or \"*\" - a deliberately small, closed subset (versionRange.ts), not the full npm range grammar.",
|
|
20
|
+
"type": "string",
|
|
21
|
+
"minLength": 1
|
|
22
|
+
},
|
|
23
|
+
"dependency": {
|
|
24
|
+
"type": "object",
|
|
25
|
+
"properties": {
|
|
26
|
+
"id": { "$ref": "#/$defs/identifier" },
|
|
27
|
+
"versionRange": { "$ref": "#/$defs/versionRange" }
|
|
28
|
+
},
|
|
29
|
+
"required": ["id", "versionRange"],
|
|
30
|
+
"additionalProperties": false
|
|
31
|
+
},
|
|
32
|
+
"argKind": {
|
|
33
|
+
"enum": ["Numeric", "MoneyOrNumeric", "String", "Boolean", "Array", "Collection", "DateLike", "Any"]
|
|
34
|
+
},
|
|
35
|
+
"actionTypeContribution": {
|
|
36
|
+
"type": "object",
|
|
37
|
+
"properties": {
|
|
38
|
+
"type": { "$ref": "#/$defs/identifier" },
|
|
39
|
+
"description": { "type": "string", "minLength": 1 }
|
|
40
|
+
},
|
|
41
|
+
"required": ["type", "description"],
|
|
42
|
+
"additionalProperties": false
|
|
43
|
+
},
|
|
44
|
+
"functionContribution": {
|
|
45
|
+
"type": "object",
|
|
46
|
+
"properties": {
|
|
47
|
+
"name": { "type": "string", "minLength": 1 },
|
|
48
|
+
"description": { "type": "string", "minLength": 1 },
|
|
49
|
+
"minArity": { "type": "integer", "minimum": 0 },
|
|
50
|
+
"maxArity": { "type": "number", "minimum": 0 },
|
|
51
|
+
"argKinds": { "type": "array", "items": { "$ref": "#/$defs/argKind" } },
|
|
52
|
+
"returnType": { "type": "string" }
|
|
53
|
+
},
|
|
54
|
+
"required": ["name", "description", "minArity", "maxArity", "argKinds", "returnType"],
|
|
55
|
+
"additionalProperties": false
|
|
56
|
+
},
|
|
57
|
+
"idDescription": {
|
|
58
|
+
"type": "object",
|
|
59
|
+
"properties": {
|
|
60
|
+
"id": { "$ref": "#/$defs/identifier" },
|
|
61
|
+
"description": { "type": "string", "minLength": 1 }
|
|
62
|
+
},
|
|
63
|
+
"required": ["id", "description"],
|
|
64
|
+
"additionalProperties": false
|
|
65
|
+
},
|
|
66
|
+
"networkScopedContribution": {
|
|
67
|
+
"type": "object",
|
|
68
|
+
"properties": {
|
|
69
|
+
"id": { "$ref": "#/$defs/identifier" },
|
|
70
|
+
"description": { "type": "string", "minLength": 1 },
|
|
71
|
+
"networkScopes": { "type": "array", "items": { "type": "string", "minLength": 1 } }
|
|
72
|
+
},
|
|
73
|
+
"required": ["id", "description", "networkScopes"],
|
|
74
|
+
"additionalProperties": false
|
|
75
|
+
},
|
|
76
|
+
"scheduledJobContribution": {
|
|
77
|
+
"type": "object",
|
|
78
|
+
"properties": {
|
|
79
|
+
"id": { "$ref": "#/$defs/identifier" },
|
|
80
|
+
"description": { "type": "string", "minLength": 1 },
|
|
81
|
+
"schedule": { "type": "string", "minLength": 1 },
|
|
82
|
+
"networkScopes": { "type": "array", "items": { "type": "string", "minLength": 1 } }
|
|
83
|
+
},
|
|
84
|
+
"required": ["id", "description", "schedule", "networkScopes"],
|
|
85
|
+
"additionalProperties": false
|
|
86
|
+
},
|
|
87
|
+
"themeContribution": {
|
|
88
|
+
"type": "object",
|
|
89
|
+
"properties": {
|
|
90
|
+
"id": { "$ref": "#/$defs/identifier" },
|
|
91
|
+
"tokenSetId": { "type": "string", "minLength": 1 }
|
|
92
|
+
},
|
|
93
|
+
"required": ["id", "tokenSetId"],
|
|
94
|
+
"additionalProperties": false
|
|
95
|
+
},
|
|
96
|
+
"menuItemContribution": {
|
|
97
|
+
"type": "object",
|
|
98
|
+
"properties": {
|
|
99
|
+
"area": { "type": "string", "minLength": 1 },
|
|
100
|
+
"group": { "type": "string", "minLength": 1 },
|
|
101
|
+
"labelKey": { "type": "string", "minLength": 1 },
|
|
102
|
+
"route": { "type": "string", "minLength": 1 }
|
|
103
|
+
},
|
|
104
|
+
"required": ["area", "group", "labelKey", "route"],
|
|
105
|
+
"additionalProperties": false
|
|
106
|
+
},
|
|
107
|
+
"localeBundleContribution": {
|
|
108
|
+
"type": "object",
|
|
109
|
+
"properties": {
|
|
110
|
+
"locale": { "type": "string", "minLength": 1 },
|
|
111
|
+
"namespace": { "type": "string", "minLength": 1 }
|
|
112
|
+
},
|
|
113
|
+
"required": ["locale", "namespace"],
|
|
114
|
+
"additionalProperties": false
|
|
115
|
+
},
|
|
116
|
+
"contributions": {
|
|
117
|
+
"type": "object",
|
|
118
|
+
"properties": {
|
|
119
|
+
"actions": { "type": "array", "items": { "$ref": "#/$defs/actionTypeContribution" } },
|
|
120
|
+
"blocks": { "type": "array", "items": { "type": "object", "description": "A frozen @erp/block-engine BlockDefinition - validated against the live BlockRegistry at install time." } },
|
|
121
|
+
"validators": { "type": "array", "items": { "$ref": "#/$defs/idDescription" } },
|
|
122
|
+
"functions": { "type": "array", "items": { "$ref": "#/$defs/functionContribution" } },
|
|
123
|
+
"dataSources": { "type": "array", "items": { "$ref": "#/$defs/networkScopedContribution" } },
|
|
124
|
+
"integrations": { "type": "array", "items": { "$ref": "#/$defs/networkScopedContribution" } },
|
|
125
|
+
"themes": { "type": "array", "items": { "$ref": "#/$defs/themeContribution" } },
|
|
126
|
+
"workflowTasks": { "type": "array", "items": { "$ref": "#/$defs/idDescription" } },
|
|
127
|
+
"menuItems": { "type": "array", "items": { "$ref": "#/$defs/menuItemContribution" } },
|
|
128
|
+
"scheduledJobs": { "type": "array", "items": { "$ref": "#/$defs/scheduledJobContribution" } },
|
|
129
|
+
"documentTemplates": { "type": "array", "items": { "$ref": "#/$defs/idDescription" } },
|
|
130
|
+
"localeBundles": { "type": "array", "items": { "$ref": "#/$defs/localeBundleContribution" } }
|
|
131
|
+
},
|
|
132
|
+
"additionalProperties": false
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
"properties": {
|
|
136
|
+
"contractVersion": { "const": 1 },
|
|
137
|
+
"id": { "$ref": "#/$defs/identifier" },
|
|
138
|
+
"version": { "$ref": "#/$defs/semver" },
|
|
139
|
+
"publisher": { "type": "string", "minLength": 1 },
|
|
140
|
+
"displayNameKey": { "type": "string", "minLength": 1 },
|
|
141
|
+
"descriptionKey": { "type": "string", "minLength": 1 },
|
|
142
|
+
"distribution": { "enum": ["locked", "editable"] },
|
|
143
|
+
"dependencies": { "type": "array", "items": { "$ref": "#/$defs/dependency" } },
|
|
144
|
+
"contributions": { "$ref": "#/$defs/contributions" },
|
|
145
|
+
"hasUpgradeMigration": { "type": "boolean" },
|
|
146
|
+
"hasDataErasureHook": { "type": "boolean" },
|
|
147
|
+
"frontendBundle": {
|
|
148
|
+
"description": "2026-09-03, closes the dynamic-frontend-plugin-loading gap (ai/domains/ai-native-developer-platform-gap-analysis.md): a compiled @erp/* frontend bundle a runtime loader fetches from the real, generic engine-file content endpoint (GET /api/v1/files/{fileId}/content) and dynamically import()s, instead of the code shipping inside a host application's own build. Optional and additive - absent on every manifest that predates this field (every real plugin in the fleet today except this pass's own proof plugin), which a frontend loader must treat as a cheap, silent no-op.",
|
|
149
|
+
"type": "object",
|
|
150
|
+
"properties": {
|
|
151
|
+
"fileId": { "type": "string", "minLength": 1, "description": "A real engine-file file id - not a path, not a URL. A loader builds the URL itself against its own baseUrl." },
|
|
152
|
+
"entrypointExport": { "type": "string", "minLength": 1, "description": "The bundle's exported registration function name a loader calls with its own live BlockRegistry (e.g. \"installRiskScoreCardPlugin\"). Defaults to \"default\" when absent." }
|
|
153
|
+
},
|
|
154
|
+
"required": ["fileId"],
|
|
155
|
+
"additionalProperties": false
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
"required": ["contractVersion", "id", "version", "publisher", "displayNameKey", "descriptionKey", "distribution", "contributions"],
|
|
159
|
+
"additionalProperties": false
|
|
160
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://erp.internal/schema/connector-definition.schema.json",
|
|
4
|
+
"title": "ExternalProviderConnectorDefinition",
|
|
5
|
+
"description": "Authoring shape for one external_provider_definition row (engine-provider-connector, V157) — the generic, domain-agnostic 'call an external HTTP provider per JSON config' capability. Consumed by GenericProviderConnectorExecutor via PUT /api/v1/provider-connectors/definitions/{domain}/{providerKey}. Authored/verified through `erp connector create|validate|list|test` (tools/erp-cli/erp.mjs) — never by hand-editing rows directly.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["domain", "providerKey", "name", "connectorKind"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"domain": { "type": "string", "description": "Consumer namespace, e.g. \"hcm-employee-documents.signature\" — any plugin/feature can register its own domain." },
|
|
10
|
+
"providerKey": { "type": "string", "description": "Unique per (tenant, domain) — what the consuming entity's provider_name/provider_key column resolves against." },
|
|
11
|
+
"name": { "type": "string" },
|
|
12
|
+
"connectorKind": { "type": "string", "enum": ["DIRECT", "HTTP"] },
|
|
13
|
+
"active": { "type": "boolean" },
|
|
14
|
+
"isDefault": { "type": "boolean" },
|
|
15
|
+
"baseUrl": { "type": "string" },
|
|
16
|
+
"authType": { "type": "string", "enum": ["NONE", "API_KEY_HEADER", "BEARER_TOKEN", "BASIC"] },
|
|
17
|
+
"authConfig": { "type": "object", "description": "e.g. {headerName, secretRef} — secretRef of shape \"env:VAR_NAME\" resolved from environment at call time, never a literal secret stored here." },
|
|
18
|
+
"requestPath": { "type": "string" },
|
|
19
|
+
"httpMethod": { "type": "string", "enum": ["POST", "PUT"] },
|
|
20
|
+
"requestTemplate": { "type": "object", "description": "JSON object whose string leaves may contain {{fieldName}} tokens substituted at call time." },
|
|
21
|
+
"responseMapping": { "type": "object", "description": "{providerReferencePath, statusPath, signingUrlPath} dot-paths into the vendor's JSON response." },
|
|
22
|
+
"statusMapping": { "type": "object", "description": "Vendor status string -> this domain's own internal status vocabulary." },
|
|
23
|
+
"completionMode": { "type": "string", "enum": ["MANUAL_CONFIRM", "POLL", "WEBHOOK"] },
|
|
24
|
+
"pollPathTemplate": { "type": "string" },
|
|
25
|
+
"webhookPath": { "type": "string" },
|
|
26
|
+
"notes": { "type": "string" },
|
|
27
|
+
"ownerPlugin": { "type": "string" }
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://erp.internal/schema/data-service-definition.schema.json",
|
|
4
|
+
"title": "DataServiceDefinitionArtifact",
|
|
5
|
+
"description": "The real on-disk authoring shape for a Data Service JSON artifact (spk-assembly/metadata/data_service/*.json), backed by com.erp.platform.engine.entity.dataservice.DataServiceSpec (backend/platform-runtime/engine-entity) and executed by DataServiceExecutionController. Confirmed against a live shipped file (hcm-payroll-audit-report.json). `source` is a loose union on the wire — only the field matching source.kind is populated (matches DataServiceSpec.Source's own doc comment about mirroring the frontend's plain-object DataServiceSourceKind union rather than a polymorphic Jackson type).",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["name", "definition"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"name": { "type": "string" },
|
|
10
|
+
"description": { "type": "string" },
|
|
11
|
+
"metadata": { "type": "object" },
|
|
12
|
+
"modules": { "type": "array", "items": { "type": "string" } },
|
|
13
|
+
"definition": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"required": ["operation", "source"],
|
|
16
|
+
"properties": {
|
|
17
|
+
"operation": { "type": "string", "enum": ["get", "count", "search", "composite"], "description": "Also determines the /execute RESPONSE envelope shape a page must bind against — undocumented elsewhere, confirmed against shipped hcm-analytics-reports KPI bindings: operation \"count\" returns `{\"value\": <number>}` (bind as `${api.<callName>.value}` — NOT `.total`, which is only the entity-records LIST pagination envelope's field, unrelated to this endpoint). operation \"get\"/\"search\" return `{\"items\": [...]}` (or `{\"records\": [...]}` for a dataView source — check a shipped example for the exact key before guessing). operation \"composite\" returns `{\"results\": {\"<step.as>\": <that step's own envelope>}}`." },
|
|
18
|
+
"source": {
|
|
19
|
+
"type": "object",
|
|
20
|
+
"properties": {
|
|
21
|
+
"kind": { "type": "string", "enum": ["provider", "entity", "dataView"] },
|
|
22
|
+
"providerName": { "type": ["string", "null"] },
|
|
23
|
+
"entityName": { "type": ["string", "null"] },
|
|
24
|
+
"dataViewName": { "type": ["string", "null"], "description": "Names a published data-view-definition artifact — see data-view-definition.schema.json." }
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"filters": {
|
|
28
|
+
"type": "array",
|
|
29
|
+
"items": {
|
|
30
|
+
"type": "object",
|
|
31
|
+
"properties": {
|
|
32
|
+
"field": { "type": "string" },
|
|
33
|
+
"operator": { "type": "string" },
|
|
34
|
+
"value": { "type": "string" },
|
|
35
|
+
"valueType": { "type": ["string", "null"], "enum": ["string", null], "description": "Opt-in escape hatch telling DataViewQueryExecutor not to numeric-auto-coerce a text id column (e.g. attachment.entity_id). null (default) preserves auto-detect behavior." }
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"parameters": {
|
|
40
|
+
"type": "array",
|
|
41
|
+
"description": "Declares this artifact's own named parameters (referenced in `filters`/`steps` as `${param.<name>}`). NOTE — this only documents the artifact's declared shape, not the caller-side wire shape: a REST call to POST /api/v1/data-services/<name>/execute must nest its argument object one level deeper than a normal entity-records call, as `{\"params\": {\"parameters\": {\"<name>\": \"<value>\"}}}`, NOT `{\"params\": {\"<name>\": \"<value>\"}}`. See ai/patterns/data-service-execute-wiring.md for the full wire-shape writeup and a shipped example.",
|
|
42
|
+
"items": {
|
|
43
|
+
"type": "object",
|
|
44
|
+
"required": ["name"],
|
|
45
|
+
"properties": { "name": { "type": "string" }, "type": { "type": "string" } }
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"steps": {
|
|
49
|
+
"type": "array",
|
|
50
|
+
"description": "Only for operation \"composite\" — each a fan-out leg naming another published Data Service, executed strictly in declaration order.",
|
|
51
|
+
"items": {
|
|
52
|
+
"type": "object",
|
|
53
|
+
"required": ["as", "service"],
|
|
54
|
+
"properties": {
|
|
55
|
+
"as": { "type": "string", "description": "Key this step's result lands under in ExecuteResponse.results." },
|
|
56
|
+
"service": { "type": "string" },
|
|
57
|
+
"parameters": { "type": "object", "description": "Values are literals or ${param.<name>} / ${step.<as>.<field>} templates." }
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://erp.internal/schema/data-view-definition.schema.json",
|
|
4
|
+
"title": "DataViewDefinitionArtifact",
|
|
5
|
+
"description": "The real on-disk authoring shape for a Data View JSON artifact (spk-assembly/metadata/data_view/*.json), backed by com.erp.platform.engine.entity.dataview.DataViewSpec (backend/platform-runtime/engine-entity) and executed by DataViewQueryExecutor. Confirmed against a live shipped file (hcm-employee's employee-activity.json). Every table/column reference here is a PLAIN PHYSICAL SQL identifier (never an Entity Designer field name) — see DataViewSpec's own doc comment. Not directly externally-callable: only reachable through a Data Service whose source.kind is \"dataView\" (see data-service-definition.schema.json).",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["name", "definition"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"name": { "type": "string" },
|
|
10
|
+
"description": { "type": "string" },
|
|
11
|
+
"metadata": { "type": "object" },
|
|
12
|
+
"modules": { "type": "array", "items": { "type": "string" } },
|
|
13
|
+
"definition": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"required": ["source"],
|
|
16
|
+
"properties": {
|
|
17
|
+
"source": {
|
|
18
|
+
"type": "object",
|
|
19
|
+
"required": ["table", "alias"],
|
|
20
|
+
"properties": {
|
|
21
|
+
"table": { "type": "string" },
|
|
22
|
+
"alias": { "type": "string" },
|
|
23
|
+
"excludeDeleted": { "type": "boolean" },
|
|
24
|
+
"schema": { "type": ["string", "null"], "description": "Nullable — defaults to unqualified, resolving through the executing connection's own search_path. A Data View sourced from a plugin-owned schema (e.g. HCM's employee/org_unit/position tables, all in `hcm`) must say so explicitly." }
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"joins": {
|
|
28
|
+
"type": "array",
|
|
29
|
+
"items": {
|
|
30
|
+
"type": "object",
|
|
31
|
+
"required": ["table", "alias", "type", "on"],
|
|
32
|
+
"properties": {
|
|
33
|
+
"table": { "type": "string" },
|
|
34
|
+
"alias": { "type": "string" },
|
|
35
|
+
"type": { "type": "string", "enum": ["LEFT", "INNER"] },
|
|
36
|
+
"on": {
|
|
37
|
+
"type": "array",
|
|
38
|
+
"items": {
|
|
39
|
+
"type": "object",
|
|
40
|
+
"required": ["leftRef", "rightRef"],
|
|
41
|
+
"properties": { "leftRef": { "type": "string" }, "rightRef": { "type": "string" } }
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"excludeDeleted": { "type": "boolean" },
|
|
45
|
+
"schema": { "type": ["string", "null"] }
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"fields": {
|
|
50
|
+
"type": "array",
|
|
51
|
+
"items": {
|
|
52
|
+
"type": "object",
|
|
53
|
+
"required": ["ref"],
|
|
54
|
+
"properties": { "ref": { "type": "string", "description": "\"alias.column\"" }, "outputName": { "type": "string" } }
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"calculatedFields": {
|
|
58
|
+
"type": "array",
|
|
59
|
+
"description": "A closed, whitelisted expression AST (com.erp.platform.engine.entity.dataview.CalcExpr) — never a raw SQL/expression string (injection risk). Each item is a { outputName, expression } CalculatedField.",
|
|
60
|
+
"items": {
|
|
61
|
+
"type": "object",
|
|
62
|
+
"required": ["outputName", "expression"],
|
|
63
|
+
"properties": {
|
|
64
|
+
"outputName": { "type": "string" },
|
|
65
|
+
"expression": { "$ref": "#/definitions/calcExpr" }
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"filter": { "type": ["string", "null"], "description": "A structured JSON predicate tree ({and:[...]}/{or:[...]}/{field,operator,value}) — Studio's PredicateBuilder.tsx grammar, with qualified \"alias.column\" field names. Stored as a JSON string, same as workflow's *Json columns." },
|
|
70
|
+
"groupBy": { "type": "array", "items": { "type": "string" }, "description": "Each a \"alias.column\" ref. See groupByTruncations for opt-in SQL-level date bucketing of one of these refs." },
|
|
71
|
+
"groupByTruncations": {
|
|
72
|
+
"type": "array",
|
|
73
|
+
"description": "(2026-09-07, date-truncation gap-closing pass) Optional, additive — enables a real SQL date_trunc()-bucketed group-by/select for a time-series \"Trend\" chart (e.g. monthly headcount). Absent/empty reproduces today's exact raw-column behavior. When present, a { ref, unit } entry here causes BOTH the matching groupBy entry AND any fields[] entry with the SAME ref to compile to date_trunc('<unit>', <ref>) instead of the raw column — author the SAME ref string in both places.",
|
|
74
|
+
"items": {
|
|
75
|
+
"type": "object",
|
|
76
|
+
"required": ["ref", "unit"],
|
|
77
|
+
"properties": {
|
|
78
|
+
"ref": { "type": "string", "description": "\"alias.column\" — must exactly match a groupBy entry (and, if selected, a fields[].ref) to take effect." },
|
|
79
|
+
"unit": { "type": "string", "enum": ["day", "week", "month", "quarter", "year"], "description": "Validated server-side against this exact whitelist — never concatenated unchecked (it lands inside a single-quoted SQL string literal)." }
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
"aggregations": {
|
|
84
|
+
"type": "array",
|
|
85
|
+
"items": {
|
|
86
|
+
"type": "object",
|
|
87
|
+
"required": ["ref", "fn"],
|
|
88
|
+
"properties": { "ref": { "type": "string" }, "fn": { "type": "string", "enum": ["SUM", "AVG", "COUNT", "MIN", "MAX"] }, "outputName": { "type": "string" } }
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
"havingFilter": {
|
|
92
|
+
"type": ["string", "null"],
|
|
93
|
+
"description": "(2026-09-07, HAVING gap-closing pass) Optional, additive post-aggregation filter — e.g. a minimum-group-size-5 suppression on a demographic breakdown. Only meaningful when groupBy is non-empty. Stored as a JSON string using the SAME structured predicate grammar as `filter` ({and:[...]}/{or:[...]}/{field,operator,value}), EXCEPT a leaf's \"field\" here MUST name one of this spec's own aggregations[].outputName values (never a raw \"alias.column\" — Postgres cannot reference a SELECT alias inside HAVING, so the executor re-resolves the real FN(ref) expression from the matching aggregation). An outputName with no matching aggregation is rejected. null (every Data View authored before this field existed) adds no HAVING clause — today's exact behavior. Example: {\"field\":\"headcount\",\"operator\":\"gte\",\"value\":5} where aggregations includes {\"ref\":\"e.id\",\"fn\":\"COUNT\",\"outputName\":\"headcount\"}."
|
|
94
|
+
},
|
|
95
|
+
"sort": {
|
|
96
|
+
"type": "array",
|
|
97
|
+
"items": {
|
|
98
|
+
"type": "object",
|
|
99
|
+
"required": ["ref"],
|
|
100
|
+
"properties": { "ref": { "type": "string" }, "descending": { "type": "boolean" } }
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
"pagination": {
|
|
104
|
+
"type": "object",
|
|
105
|
+
"properties": { "defaultPageSize": { "type": "number" }, "maxPageSize": { "type": "number" } }
|
|
106
|
+
},
|
|
107
|
+
"permissionKey": { "type": ["string", "null"], "description": "Authored, NOT enforced in v1 — same disclosed ceiling every other Data Service execution has until real platform-wide RBAC/ABAC lands." }
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
"definitions": {
|
|
112
|
+
"calcExpr": {
|
|
113
|
+
"type": "object",
|
|
114
|
+
"description": "com.erp.platform.engine.entity.dataview.CalcExpr — a loose union on the wire; only the properties matching \"op\" are populated.",
|
|
115
|
+
"required": ["op"],
|
|
116
|
+
"properties": {
|
|
117
|
+
"op": { "type": "string", "enum": ["field", "literal", "concat", "coalesce", "add", "sub", "mul", "div", "case", "aggregate", "ratio"] },
|
|
118
|
+
"fieldRef": { "type": "string", "description": "op==\"field\": an \"alias.column\" ref. Also reused as the aggregated column ref for op==\"aggregate\" (SUM/AVG/MIN/MAX; omit for COUNT(*))." },
|
|
119
|
+
"literalValue": { "description": "op==\"literal\": string/number/boolean/null — always bind-parameterized." },
|
|
120
|
+
"args": { "type": "array", "items": { "$ref": "#/definitions/calcExpr" }, "description": "op==\"concat\"|\"coalesce\": 2+ operands." },
|
|
121
|
+
"left": { "$ref": "#/definitions/calcExpr", "description": "op==\"add\"|\"sub\"|\"mul\"|\"div\"." },
|
|
122
|
+
"right": { "$ref": "#/definitions/calcExpr" },
|
|
123
|
+
"whenClauses": {
|
|
124
|
+
"type": "array",
|
|
125
|
+
"description": "op==\"case\".",
|
|
126
|
+
"items": {
|
|
127
|
+
"type": "object",
|
|
128
|
+
"required": ["when", "then"],
|
|
129
|
+
"properties": {
|
|
130
|
+
"when": { "type": "string", "description": "A structured predicate JSON string — the SAME grammar as DataViewSpec.filter()." },
|
|
131
|
+
"then": { "$ref": "#/definitions/calcExpr" }
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
"elseExpr": { "$ref": "#/definitions/calcExpr", "description": "op==\"case\", optional ELSE." },
|
|
136
|
+
"aggFn": {
|
|
137
|
+
"type": "string",
|
|
138
|
+
"enum": ["SUM", "AVG", "COUNT", "MIN", "MAX"],
|
|
139
|
+
"description": "(2026-09-07, ratio gap-closing pass) op==\"aggregate\": compiles to a real SQL aggregate function, valid in a grouped OR ungrouped select list (unlike op==\"field\", which must also appear in groupBy). Combine with \"filter\" for a conditional/filtered aggregate, e.g. count(case when <filter> then 1 end) — the building block a ratio numerator/denominator needs."
|
|
140
|
+
},
|
|
141
|
+
"filter": { "type": "string", "description": "op==\"aggregate\": optional structured predicate JSON (same grammar as DataViewSpec.filter()) — CASE WHEN this matches THEN counted/summed ELSE excluded." },
|
|
142
|
+
"numerator": { "$ref": "#/definitions/calcExpr", "description": "op==\"ratio\": typically an \"aggregate\" expression, but any calcExpr is accepted." },
|
|
143
|
+
"denominator": { "$ref": "#/definitions/calcExpr", "description": "op==\"ratio\": compiled with a NULLIF(...,0) guard, so a zero denominator yields SQL NULL rather than a divide-by-zero error." },
|
|
144
|
+
"multiplyBy": { "type": "number", "description": "op==\"ratio\": optional post-division multiplier (e.g. 100 for a percentage). Defaults to 1 (a plain ratio) when absent. Example — attritionRate = { \"op\":\"ratio\", \"numerator\":{\"op\":\"aggregate\",\"aggFn\":\"COUNT\",\"filter\":\"{\\\"field\\\":\\\"r.status\\\",\\\"operator\\\":\\\"eq\\\",\\\"value\\\":\\\"separated\\\"}\"}, \"denominator\":{\"op\":\"aggregate\",\"aggFn\":\"COUNT\"}, \"multiplyBy\":100 }." }
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://erp.platform/schema/entity-aggregation-config.schema.json",
|
|
4
|
+
"title": "entity_aggregation_config row",
|
|
5
|
+
"description": "One config row for the generic engine-entity.aggregation-sweep job (EntityAggregationSweepJob): fold many rows of source_entity into a scalar (or per-group scalar) and upsert the result into target_entity. Ship via metadata/seed-data/*.json. See ai/patterns/generic-aggregation-and-cadence-jobs.md.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["sweep_code", "source_entity", "aggregation", "target_entity", "target_key_field", "target_value_field", "active"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"sweep_code": { "type": "string", "description": "Stable identifier / natural key for idempotent seeding." },
|
|
11
|
+
"source_entity": { "type": "string" },
|
|
12
|
+
"filter_field": { "type": "string", "description": "Optional single-column equality pre-filter." },
|
|
13
|
+
"filter_value": { "type": "string" },
|
|
14
|
+
"when_status_in": { "type": "string", "description": "Optional comma-separated status filter." },
|
|
15
|
+
"status_field": { "type": "string", "default": "status" },
|
|
16
|
+
"date_field": { "type": "string", "description": "Optional date pre-filter column." },
|
|
17
|
+
"date_compare_op": { "type": "string", "enum": ["lte", "lt", "gte", "gt"], "default": "lte" },
|
|
18
|
+
"day_offset": { "type": "integer", "default": 0 },
|
|
19
|
+
"aggregation": { "type": "string", "enum": ["count", "sum", "avg", "min", "max"] },
|
|
20
|
+
"agg_field": { "type": "string", "description": "Required for sum/avg/min/max; the numeric column to fold." },
|
|
21
|
+
"group_by_field": { "type": "string", "description": "Optional; one result bucket per distinct value." },
|
|
22
|
+
"target_entity": { "type": "string" },
|
|
23
|
+
"target_key_field": { "type": "string", "description": "Column on target_entity that holds the bucket key (a declared field). Special value \"id\": the bucket key IS a row's own primary key — the fold is written straight back onto target_entity row #<key> (never a create). Use with group_by_field yielding the parent id (e.g. maintenance_id), target_entity = that parent entity, and a blank target_key_prefix — this is the true 'roll child lines up onto the parent record' form (MaintenanceCostSyncJob)." },
|
|
24
|
+
"target_key_prefix": { "type": "string", "description": "Prepended to the bucket key when writing (so multiple configs can share one summary entity without collisions)." },
|
|
25
|
+
"result_key": { "type": "string", "description": "Used as the bucket key when group_by_field is blank; defaults to sweep_code." },
|
|
26
|
+
"target_value_field": { "type": "string" },
|
|
27
|
+
"target_timestamp_field": { "type": "string", "description": "Optional; now written here on each run." },
|
|
28
|
+
"target_extra_json": { "type": "object", "description": "Optional static fields set only when a target row is first created." },
|
|
29
|
+
"seeded_by": { "type": "string" },
|
|
30
|
+
"active": { "type": "boolean" }
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://erp.platform/schema/entity-cadence-config.schema.json",
|
|
4
|
+
"title": "entity_cadence_config row",
|
|
5
|
+
"description": "One config row for the generic engine-entity.cadence-generator job (EntityCadenceGeneratorJob): create new target_entity rows on a cadence, either one-per-due-source-row or one standalone row from a template. Ship via metadata/seed-data/*.json. See ai/patterns/generic-aggregation-and-cadence-jobs.md.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["cadence_code", "target_entity", "interval_days", "active"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"cadence_code": { "type": "string", "description": "Stable identifier / natural key for idempotent seeding." },
|
|
11
|
+
"target_entity": { "type": "string" },
|
|
12
|
+
"interval_days": { "type": "integer", "description": "Minimum days between generations; the job stamps last_generated_at on its own config row after each run." },
|
|
13
|
+
"last_generated_at": { "type": "string", "description": "Managed by the job; leave unset in seed files." },
|
|
14
|
+
"template_json": { "type": "object", "description": "Static field values for each created row. String values may use ${date} ${ts} ${uuid} ${sourceId} placeholders." },
|
|
15
|
+
"source_entity": { "type": "string", "description": "Set for source-driven generation (one target row per due source row); leave blank for a standalone single-row generator." },
|
|
16
|
+
"source_filter_field": { "type": "string" },
|
|
17
|
+
"source_filter_value": { "type": "string" },
|
|
18
|
+
"source_status_field": { "type": "string", "default": "status" },
|
|
19
|
+
"source_status_in": { "type": "string" },
|
|
20
|
+
"source_date_field": { "type": "string", "description": "Only source rows whose date_field <= now + due_within_days are generated from." },
|
|
21
|
+
"due_within_days": { "type": "integer", "default": 0 },
|
|
22
|
+
"field_map_json": { "type": "object", "description": "{ targetField: sourceField } - copies values from each source row." },
|
|
23
|
+
"dedupe_target_field": { "type": "string", "description": "Target column holding the source row id (source-driven) or the cadence marker (standalone) - the idempotency key." },
|
|
24
|
+
"seeded_by": { "type": "string" },
|
|
25
|
+
"active": { "type": "boolean" }
|
|
26
|
+
}
|
|
27
|
+
}
|