@shundoo-ai/dsh-cosmic 1.0.21 → 1.0.22
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/package.json +1 -1
- package/skills/cosmic-build/LOCAL-PATCHES.md +43 -14
- package/skills/cosmic-build/SKILL.md +24 -9
- package/skills/cosmic-build/examples/java-rules/CosmicReportAggregation.java +26 -17
- package/skills/cosmic-build/examples/java-rules/CosmicReportFilters.java +96 -2
- package/skills/cosmic-build/knowledge/cosmic/arch/security/security_standard.md +4 -4
- package/skills/cosmic-build/knowledge/cosmic/platform/allimport.md +2 -1
- package/skills/cosmic-build/knowledge/cosmic/platform/core/dynamic-object-collection.md +6 -4
- package/skills/cosmic-build/knowledge/cosmic/platform/core/model.md +9 -9
- package/skills/cosmic-build/knowledge/cosmic/platform/core/view.md +5 -5
- package/skills/cosmic-build/knowledge/cosmic/platform/form-plugin.md +8 -4
- package/skills/cosmic-build/knowledge/cosmic/platform/list-plugin.md +5 -3
- package/skills/cosmic-build/knowledge/cosmic/platform/print-plugin-guide.md +1 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/report-plugin.md +269 -57
- package/skills/cosmic-build/knowledge/cosmic/platform/reportlistdata-plugin.md +130 -23
- package/skills/cosmic-build/knowledge/cosmic/platform/reporttreedata-plugin.md +48 -28
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/_index.md +12 -3
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/common/attachment-name-process.md +17 -13
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/common/dynamic-update-field.md +4 -4
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/filter-grid/filter-grid-set-value.md +1 -1
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/list/filter-basedatafield.md +2 -3
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/list/filter-container-init.md +0 -1
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/list/filter-popup.md +0 -1
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/list/filter-value-parse.md +5 -6
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/list/list-show-form.md +1 -1
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/list/query-field-dynamic.md +0 -7
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/data-access/business-data/delete-service.md +24 -29
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/data-access/ksql/db-batch.md +4 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/data-access/ksql/db-direct-access.md +4 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/data-access/qfilter/entity-object-access.md +8 -3
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/data-access/qfilter/qfilter-compose.md +6 -5
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/event/after-bind-data-set-visible.md +5 -1
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/event/click-return-data.md +2 -2
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/event/closed-call-back-set-value.md +2 -2
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/event/register-listener.md +1 -1
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/event/set-filter-tab-org.md +3 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/infra/dispatch-service/microservice-dev.md +18 -11
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/infra/dist-lock/dist-lock-basic.md +3 -6
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/infra/mq/mq-subscribe.md +5 -1
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/infra/thread-pool/async-operation.md +10 -6
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/infra/thread-pool/big-task-async.md +4 -2
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/infra/thread-pool/schedule-task.md +9 -7
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/operation/operation-interactive.md +33 -34
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/operation/operation-plugin-events.md +30 -22
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-async-query.md +133 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-batch-query.md +26 -22
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-chart.md +112 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-dataset-assembly.md +200 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-head-filter.md +177 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-hyperlink-drill.md +216 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-list-data-query.md +12 -12
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-merge-column.md +44 -6
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-print-data.md +163 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-row-data-toolbar.md +188 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-subtotal-total.md +139 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-tree-data-query.md +108 -12
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-tree-list.md +181 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/system-service/report-plugin-interface.md +3 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/tree/tree-basedata-cascade-confirm.md +14 -11
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/tree/tree-list-before-item-validate.md +1 -1
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/tree/tree-list-build-filter.md +1 -1
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/tree/tree-list-multi-level-filter.md +11 -11
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/tree/tree-list-refresh-node.md +22 -15
- package/skills/cosmic-build/knowledge/cosmic/platform/sdk/sdk-algo.md +2 -2
- package/skills/cosmic-build/knowledge/cosmic/platform/sdk/sdk-entity-model.md +3 -4
- package/skills/cosmic-build/knowledge/cosmic/platform/sdk/sdk-exception.md +9 -5
- package/skills/cosmic-build/knowledge/cosmic/platform/sdk/sdk-orm-access.md +4 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/sdk/sdk-request-context.md +7 -3
- package/skills/cosmic-build/knowledge/cosmic/platform/standards.md +28 -3
- package/skills/cosmic-build/references/compile-guide.md +36 -1
- package/skills/cosmic-build/references/dcs-code-rules.md +180 -0
- package/skills/cosmic-build/references/delivery.md +16 -2
- package/skills/cosmic-build/references/execution-contract.md +15 -3
- package/skills/cosmic-build/references/java-plugin-delivery.md +21 -11
- package/skills/cosmic-build/references/plugin-handoff-input.md +82 -24
- package/skills/cosmic-build/references/plugin-runtime-routing.md +23 -6
- package/skills/cosmic-build/references/report-filter-contract.md +53 -15
- package/skills/cosmic-build/references/sdk-inspection.md +4 -1
- package/skills/cosmic-build/references/type-registry.json +26 -17
- package/skills/cosmic-build/schemas/data-model-authoring.schema.json +1 -1
- package/skills/cosmic-build/scripts/_http_client.py +3 -0
- package/skills/cosmic-build/scripts/compile_plugin_source.py +251 -42
- package/skills/cosmic-build/scripts/configure_list.py +112 -0
- package/skills/cosmic-build/scripts/dcs-rule-catalog.json +799 -0
- package/skills/cosmic-build/scripts/dcs_code_gate.py +1976 -0
- package/skills/cosmic-build/scripts/execution_binding.py +25 -0
- package/skills/cosmic-build/scripts/platform_api_contract.py +352 -7
- package/skills/cosmic-build/scripts/plugin_source_gate.py +9 -1
- package/skills/cosmic-build/scripts/plugin_target_sdk.py +12 -8
- package/skills/cosmic-build/scripts/prepare_plugin_handoff.py +488 -88
- package/skills/cosmic-build/scripts/query_plugin_handoff.py +207 -20
- package/skills/cosmic-build/scripts/register_plugins.py +27 -10
- package/skills/cosmic-build/scripts/render_output_cards.py +57 -20
- package/skills/cosmic-build/scripts/resolve_output_path.py +60 -26
- package/skills/cosmic-build/scripts/run_java_tests.py +162 -0
- package/skills/cosmic-build/scripts/sync_generated_locale_assets.py +28 -0
- package/skills/cosmic-build/scripts/test/fixtures/ReportSdkCheck.java +1 -22
- package/skills/cosmic-build/scripts/test/fixtures/list_column_property_declarations.json +887 -0
- package/skills/cosmic-build/scripts/test/fixtures/operation-entry-field-prepare.java +78 -0
- package/skills/cosmic-build/scripts/test/test_add_field_required_contract.py +12 -0
- package/skills/cosmic-build/scripts/test/test_batch5_drift_governance.py +23 -2
- package/skills/cosmic-build/scripts/test/test_compile_plugin_source.py +287 -4
- package/skills/cosmic-build/scripts/test/test_configure_list_delivery.py +132 -0
- package/skills/cosmic-build/scripts/test/test_cosmic_sdk_examples.py +58 -1
- package/skills/cosmic-build/scripts/test/test_data_model_contract.py +19 -0
- package/skills/cosmic-build/scripts/test/test_dcs_code_gate.py +901 -0
- package/skills/cosmic-build/scripts/test/test_delivery_stage_boundary.py +1 -1
- package/skills/cosmic-build/scripts/test/test_direct_cli_architecture.py +2 -0
- package/skills/cosmic-build/scripts/test/test_http_client.py +6 -0
- package/skills/cosmic-build/scripts/test/test_java_toolchain_feedback.py +206 -0
- package/skills/cosmic-build/scripts/test/test_kd_api.py +21 -0
- package/skills/cosmic-build/scripts/test/test_list_configuration_closure.py +184 -0
- package/skills/cosmic-build/scripts/test/test_mcp_cli.py +139 -2
- package/skills/cosmic-build/scripts/test/test_menu_creation_closure.py +16 -0
- package/skills/cosmic-build/scripts/test/test_modeling_input_recovery.py +55 -1
- package/skills/cosmic-build/scripts/test/test_open_world_cli_catalog.py +1 -1
- package/skills/cosmic-build/scripts/test/test_operation_field_preparation.py +84 -0
- package/skills/cosmic-build/scripts/test/test_platform_api_contract.py +322 -1
- package/skills/cosmic-build/scripts/test/test_plugin_handoff.py +951 -4
- package/skills/cosmic-build/scripts/test/test_plugin_source_gate.py +30 -2
- package/skills/cosmic-build/scripts/test/test_plugin_target_sdk.py +21 -0
- package/skills/cosmic-build/scripts/test/test_property_reroute.py +165 -0
- package/skills/cosmic-build/scripts/test/test_property_schema_first.py +35 -0
- package/skills/cosmic-build/scripts/test/test_query_plugin_handoff.py +221 -0
- package/skills/cosmic-build/scripts/test/test_register_plugins.py +76 -4
- package/skills/cosmic-build/scripts/test/test_render_output_cards.py +75 -3
- package/skills/cosmic-build/scripts/test/test_report_business_contract.py +27 -5
- package/skills/cosmic-build/scripts/test/test_report_doc_contract_sync.py +103 -11
- package/skills/cosmic-build/scripts/test/test_report_plugin_source_gate.py +46 -16
- package/skills/cosmic-build/scripts/test/test_report_properties.py +74 -15
- package/skills/cosmic-build/scripts/test/test_report_query_data_source.py +3 -1
- package/skills/cosmic-build/scripts/test/test_report_sdk_optimization.py +1 -2
- package/skills/cosmic-build/scripts/test/test_test_environment.py +76 -1
- package/skills/cosmic-build/scripts/test_environment.py +67 -1
- package/skills/cosmic-build/skills/cosmic-coding-impl/SKILL.md +52 -10
- package/skills/cosmic-build/skills/cosmic-meta-api/apis/ai_meta.md +16 -3
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/report-write-contracts.json +59 -26
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/rule-domain.json +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/schema/report-write.schema.json +75 -18
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/pdm/scripts/pdm_auto.py +39 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/pdm/scripts/pdm_client.py +97 -10
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/verify/SKILL.md +10 -12
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_apps.py +3 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_formactions.py +38 -29
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_listui.py +400 -233
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_metaops.py +87 -45
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_properties.py +42 -15
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_rules.py +135 -5
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/config.py +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/execution_errors.py +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/formatters.py +17 -11
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/property_reroute.py +15 -5
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/report_ops.py +1429 -238
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/validators.py +56 -28
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/cosmic_api.py +380 -134
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/field_decorator.py +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/meta_export.py +12 -11
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/meta_verifier.py +42 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_add_field_placement_closure.py +8 -8
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_build_configuration_readback.py +94 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_build_receipt_resume.py +47 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_metadata_unchanged_fail_closed.py +87 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_dym_evidence.py +23 -23
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_entity_integrity.py +279 -4
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_field_guard.py +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_plugin_contract.py +509 -32
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_readback_derivation.py +178 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_recreate_plan.py +125 -10
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_serve_contract.py +208 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_typed_filters.py +259 -50
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_rule_inventory_perf.py +29 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_rule_write_closure.py +20 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_run_batch_post_build_recovery.py +18 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_run_batch_verification_evidence.py +35 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_run_identity_guard.py +25 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_time_range_rule.py +98 -0
- package/skills/cosmic-build/skills/cosmic-meta-knowledge/domain-model/report-model.md +20 -9
- package/skills/cosmic-build/skills/cosmic-meta-knowledge/generation/report-modeling-guide.md +55 -25
- package/skills/cosmic-build/skills/cosmic-meta-knowledge/property-guides/report-column-properties.md +15 -39
- package/skills/cosmic-build/skills/cosmic-meta-modeling/SKILL.md +19 -17
- package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/report/SKILL.md +82 -38
- package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/report/examples/golden-example-report-batch.md +64 -19
- package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/report/examples/golden-example-report-plugin.md +32 -20
- package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/report/examples/golden-example-report-tree.md +78 -31
- package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/report/report-artifact-schema.json +2 -1
- package/skills/cosmic-build/skills/cosmic-meta-modeling/references/authoring-companion-fields.md +3 -0
- package/skills/cosmic-build/skills/cosmic-meta-modeling/references/authoring-list-configuration.md +2 -0
- package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/data_model_contract.py +3 -1
- package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/data_model_emit.py +27 -4
- package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/report_validator.py +99 -44
- package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/test_report_examples.py +31 -4
- package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/test_report_validator.py +129 -29
- package/skills/cosmic-build/skills/cosmic-meta-modifier/SKILL.md +6 -2
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/operation/validation.md +2 -0
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/property/SKILL.md +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/report-op/SKILL.md +104 -25
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/SKILL.md +9 -0
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/verify/usage-guide.md +2 -2
- package/skills/cosmic-build/skills/cosmic-meta-modifier/references/list-filter-modification.md +39 -12
- package/skills/cosmic-build/skills/mcp-server/SKILL.md +39 -24
- package/skills/cosmic-build/skills/mcp-server/scripts/mcp_cli.py +20 -6
- package/skills/cosmic-dev-best-practices/SKILL.md +1 -1
- package/skills/cosmic-dev-best-practices/references/README.md +2 -2
- package/skills/cosmic-dev-best-practices/references/basedata-dev-convention.md +2 -2
- package/skills/cosmic-dev-best-practices/references/bill-dev-convention.md +4 -4
- package/skills/cosmic-dev-best-practices/references/design-document-convention.md +2 -2
- package/skills/cosmic-dev-best-practices/references/design-examples/basedata-design-example.md +4 -4
- package/skills/cosmic-dev-best-practices/references/design-examples/bill-design-example.md +13 -13
- package/skills/cosmic-dev-best-practices/references/elements/CustomerField.md +1 -1
- package/skills/cosmic-dev-best-practices/references/elements/README.md +1 -1
- package/skills/cosmic-dev-best-practices/references/elements/SupplierField.md +1 -1
- package/skills/cosmic-dev-best-practices/references/elements/common-field-property-conventions.md +109 -13
- package/skills/cosmic-dev-best-practices/references/layout-control-convention.md +1 -1
- package/skills/erp-analysis/references/delivery-contract.md +2 -1
- package/skills/erp-analysis/templates/AGENTS.template.md +3 -1
- package/skills/cosmic-build/assets/platform-report-filters/TypedReportFilterFields.java +0 -169
- package/skills/cosmic-build/assets/platform-report-filters/platform.patch +0 -98
- package/skills/cosmic-build/scripts/apply_report_filter_extension.py +0 -39
|
@@ -196,10 +196,12 @@ class WritePayloadTest(unittest.TestCase):
|
|
|
196
196
|
"data": {"formmeta": {"Items": [{"_Type_": "BillFormAp"}]}}},
|
|
197
197
|
})
|
|
198
198
|
with mock.patch.object(report_ops, "_api_call", call):
|
|
199
|
-
with self.assertRaises(
|
|
199
|
+
with self.assertRaises(report_ops.ReportMutationError) as raised:
|
|
200
200
|
report_ops.set_report_query_data_source(
|
|
201
201
|
{}, "t", "kdtest_720_test_bill", data_source_number="test818")
|
|
202
202
|
self.assertIn("REPORT_QUERY_DATA_SOURCE_READBACK_UNAVAILABLE", str(raised.exception))
|
|
203
|
+
self.assertTrue(raised.exception.mutation_applied)
|
|
204
|
+
self.assertEqual(sum(endpoint == "setComplexProperty" for endpoint, _payload in _calls), 1)
|
|
203
205
|
|
|
204
206
|
def test_dry_run_reaches_the_platform_only_to_resolve(self) -> None:
|
|
205
207
|
call, calls = _api({"queryForms": {"status": True, "errorCode": "0",
|
|
@@ -11,11 +11,10 @@ HOME = os.environ.get("COSMIC_SDK_JAVA_HOME", "")
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
@pytest.mark.skipif(not CLASSPATH or not HOME, reason="target SDK and Java 8 home unavailable")
|
|
14
|
-
def
|
|
14
|
+
def test_aggregation_uses_actual_sdk(tmp_path):
|
|
15
15
|
javac = Path(HOME) / "bin" / ("javac.exe" if os.name == "nt" else "javac")
|
|
16
16
|
java = Path(HOME) / "bin" / ("java.exe" if os.name == "nt" else "java")
|
|
17
17
|
subprocess.run([str(javac), "-source", "8", "-target", "8", "-cp", CLASSPATH, "-d", str(tmp_path),
|
|
18
|
-
str(ROOT / "assets/platform-report-filters/TypedReportFilterFields.java"),
|
|
19
18
|
str(ROOT / "examples/java-rules/CosmicReportAggregation.java"),
|
|
20
19
|
str(ROOT / "scripts/test/fixtures/ReportSdkCheck.java")], check=True, capture_output=True, text=True, timeout=60)
|
|
21
20
|
result = subprocess.run([str(java), "-DclusterName=report-sdk-check", "-Dlogfactory.className=ReportSdkCheck$TestLogFactory",
|
|
@@ -5,7 +5,7 @@ from pathlib import Path
|
|
|
5
5
|
import pytest
|
|
6
6
|
|
|
7
7
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
|
8
|
-
from test_environment import export_test_environment
|
|
8
|
+
from test_environment import export_test_environment, plugin_runtime_context
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
def test_export_preserves_target_and_excludes_credentials(tmp_path):
|
|
@@ -43,3 +43,78 @@ def test_export_rejects_changed_environment_or_credential_url(tmp_path, server):
|
|
|
43
43
|
with pytest.raises(ValueError):
|
|
44
44
|
export_test_environment(tmp_path, server, ["form_a"])
|
|
45
45
|
assert not (tmp_path / "_runtime" / "test-environment.json").exists()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_plugin_runtime_reuses_exact_archived_list_without_discovery(tmp_path):
|
|
49
|
+
runtime = tmp_path / "_runtime"
|
|
50
|
+
runtime.mkdir()
|
|
51
|
+
(runtime / "context.json").write_text(json.dumps({"environment": {"baseUrl": "http://127.0.0.1:8080/ierp"}}))
|
|
52
|
+
url = "http://127.0.0.1:8080/ierp/?formId=bos_list&type=list&billFormId=booking&aiv=observed"
|
|
53
|
+
(tmp_path / "archive.json").write_text(json.dumps({"artifacts": [{"formNumber": "booking", "listUrl": url}]}))
|
|
54
|
+
before = (tmp_path / "archive.json").read_bytes()
|
|
55
|
+
result = plugin_runtime_context(tmp_path, "booking")
|
|
56
|
+
assert result["listUrl"] == url
|
|
57
|
+
assert result["status"] == "entry-available" and result["runtimeVerified"] is False
|
|
58
|
+
assert "previewUrl" not in result
|
|
59
|
+
assert (tmp_path / "archive.json").read_bytes() == before
|
|
60
|
+
assert "listUrl" not in plugin_runtime_context(tmp_path, "other")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@pytest.mark.parametrize("url", [
|
|
64
|
+
"http://other.invalid/ierp/?formId=bos_list&billFormId=booking",
|
|
65
|
+
"http://127.0.0.1:8080/ierp/?formId=bos_list&billFormId=other",
|
|
66
|
+
"http://user:secret@127.0.0.1:8080/ierp/?formId=bos_list&billFormId=booking",
|
|
67
|
+
"http://127.0.0.1:8080/ierp/?formId=bos_list&billFormId=booking&token=secret",
|
|
68
|
+
])
|
|
69
|
+
def test_plugin_runtime_rejects_wrong_targets_and_credential_links(tmp_path, url):
|
|
70
|
+
(tmp_path / "_runtime").mkdir()
|
|
71
|
+
(tmp_path / "_runtime/context.json").write_text(json.dumps({"environment": {"baseUrl": "http://127.0.0.1:8080/ierp"}}))
|
|
72
|
+
(tmp_path / "archive.json").write_text(json.dumps({"artifacts": [{"formNumber": "booking", "listUrl": url}]}))
|
|
73
|
+
result = plugin_runtime_context(tmp_path, "booking")
|
|
74
|
+
assert "listUrl" not in result
|
|
75
|
+
assert result["discovery"]["platformOperation"] == "getPreviewUrl"
|
|
76
|
+
assert "secret" not in json.dumps(result)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def test_plugin_runtime_does_not_use_previous_account_or_another_application(tmp_path):
|
|
80
|
+
(tmp_path / "_runtime").mkdir()
|
|
81
|
+
(tmp_path / "_runtime/context.json").write_text(json.dumps({"environment": {"baseUrl": "http://127.0.0.1:8080/ierp", "accountId": "new"}}))
|
|
82
|
+
export = {"schemaVersion": "cosmic-test-environment/v1", "baseUrl": "http://127.0.0.1:8080/ierp", "accountId": "old",
|
|
83
|
+
"application": {"appRoot": str(tmp_path)}, "forms": [{"formNumber": "booking"}]}
|
|
84
|
+
(tmp_path / "_runtime/test-environment.json").write_text(json.dumps(export))
|
|
85
|
+
assert plugin_runtime_context(tmp_path, "booking")["status"] == "target-mismatch"
|
|
86
|
+
export["accountId"] = "new"
|
|
87
|
+
export["application"]["appRoot"] = str(tmp_path / "other")
|
|
88
|
+
(tmp_path / "_runtime/test-environment.json").write_text(json.dumps(export))
|
|
89
|
+
assert plugin_runtime_context(tmp_path, "booking")["status"] == "target-mismatch"
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_resolve_entry_checks_active_environment_before_platform_lookup(tmp_path, monkeypatch):
|
|
93
|
+
import kd_env_loader
|
|
94
|
+
import preview_url
|
|
95
|
+
(tmp_path / "_runtime").mkdir()
|
|
96
|
+
(tmp_path / "_runtime/context.json").write_text(json.dumps({"environment": {"baseUrl": "http://127.0.0.1:8080/ierp"}}))
|
|
97
|
+
monkeypatch.setattr(kd_env_loader, "load_effective_skill_config", lambda: {"server": {"baseUrl": "http://other.invalid/ierp"}})
|
|
98
|
+
def unexpected(*args, **kwargs):
|
|
99
|
+
raise AssertionError("must not contact a different platform")
|
|
100
|
+
monkeypatch.setattr(preview_url, "get_preview_url", unexpected)
|
|
101
|
+
assert plugin_runtime_context(tmp_path, "booking", resolve_entry=True)["status"] == "target-mismatch"
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def test_resolve_entry_reuses_the_existing_platform_preview_resolver(tmp_path, monkeypatch):
|
|
105
|
+
import kd_env_loader
|
|
106
|
+
import preview_url
|
|
107
|
+
(tmp_path / "_runtime").mkdir()
|
|
108
|
+
base = "http://127.0.0.1:8080/ierp"
|
|
109
|
+
(tmp_path / "_runtime/context.json").write_text(json.dumps({"environment": {"baseUrl": base}}))
|
|
110
|
+
monkeypatch.setattr(kd_env_loader, "load_effective_skill_config", lambda: {"server": {"baseUrl": base}})
|
|
111
|
+
requests = []
|
|
112
|
+
def response(server, path, config, params):
|
|
113
|
+
requests.append((path, params))
|
|
114
|
+
url = base + "/?formId=booking" if "viewType" not in params else base + "/?formId=bos_list&type=list&billFormId=booking"
|
|
115
|
+
return {"status": True, "data": url}
|
|
116
|
+
monkeypatch.setattr(preview_url.kd_api, "get", response)
|
|
117
|
+
result = plugin_runtime_context(tmp_path, "booking", resolve_entry=True)
|
|
118
|
+
assert result["status"] == "entry-available" and "billFormId=booking" in result["listUrl"]
|
|
119
|
+
assert [params for _, params in requests] == [{"formNumber": "booking"}, {"formNumber": "booking", "viewType": "list"}]
|
|
120
|
+
assert all(path == preview_url.PREVIEW_URL_PATH for path, _ in requests)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from urllib.parse import urlsplit, urlunsplit
|
|
5
|
+
from urllib.parse import parse_qs, urlsplit, urlunsplit
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def environment_identity(server):
|
|
@@ -15,6 +15,72 @@ def environment_identity(server):
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
|
|
18
|
+
def plugin_runtime_context(app_root, form_number, *, resolve_entry=False):
|
|
19
|
+
"""Return observed entry links for one bound form, without logging in or inventing routes."""
|
|
20
|
+
root = Path(app_root).resolve()
|
|
21
|
+
result = {"formNumber": form_number, "status": "discovery-required", "runtimeVerified": False,
|
|
22
|
+
"discovery": {"operation": "runtime-context", "resolveEntry": True, "platformOperation": "getPreviewUrl", "formNumber": form_number,
|
|
23
|
+
"appRoot": str(root)},
|
|
24
|
+
"nextAction": "Use the saved entry for the required view; otherwise repeat runtime-context with --resolve-entry for a read-only platform lookup. Do not guess routes. Login requires user-provided identity or an existing authorized session."}
|
|
25
|
+
context = root / "_runtime" / "context.json"
|
|
26
|
+
bound = json.loads(context.read_text(encoding="utf-8-sig")) if context.is_file() else {}
|
|
27
|
+
saved = root / "_runtime" / "test-environment.json"
|
|
28
|
+
environment = json.loads(saved.read_text(encoding="utf-8-sig")) if saved.is_file() else {}
|
|
29
|
+
owner = environment.get("application", {}).get("appRoot")
|
|
30
|
+
if environment and (environment.get("schemaVersion") != "cosmic-test-environment/v1"
|
|
31
|
+
or not owner or Path(owner).resolve() != root):
|
|
32
|
+
return {**result, "status": "target-mismatch"}
|
|
33
|
+
identity = environment_identity(environment) if environment else None
|
|
34
|
+
current = environment_identity(bound["environment"]) if bound.get("environment") else identity
|
|
35
|
+
if not current:
|
|
36
|
+
return result
|
|
37
|
+
if identity and any(identity.get(key) and identity[key] != current.get(key) for key in ("baseUrl", "accountId")):
|
|
38
|
+
return {**result, "status": "target-mismatch"}
|
|
39
|
+
result.update(current)
|
|
40
|
+
candidates = [(saved, form) for form in environment.get("forms", [])
|
|
41
|
+
if isinstance(form, dict) and form.get("formNumber") == form_number]
|
|
42
|
+
archive = root / "archive.json"
|
|
43
|
+
if archive.is_file():
|
|
44
|
+
data = json.loads(archive.read_text(encoding="utf-8-sig"))
|
|
45
|
+
if bound.get("appCode") and data.get("appCode") and bound["appCode"] != data["appCode"]:
|
|
46
|
+
return {**result, "status": "target-mismatch"}
|
|
47
|
+
candidates += [(archive, form) for form in data.get("artifacts", [])
|
|
48
|
+
if isinstance(form, dict) and form.get("formNumber") == form_number]
|
|
49
|
+
if resolve_entry:
|
|
50
|
+
from kd_env_loader import load_effective_skill_config
|
|
51
|
+
from preview_url import get_preview_url
|
|
52
|
+
config = load_effective_skill_config()
|
|
53
|
+
server = config.get("server", config)
|
|
54
|
+
selected = environment_identity(server)
|
|
55
|
+
if any(current.get(key) and current[key] != selected.get(key) for key in ("baseUrl", "accountId")):
|
|
56
|
+
return {**result, "status": "target-mismatch"}
|
|
57
|
+
editor = get_preview_url(server, current["baseUrl"], form_number)
|
|
58
|
+
listing = get_preview_url(server, current["baseUrl"], form_number, view_type="list", form_preview_url=editor)
|
|
59
|
+
candidates = [("platform:getPreviewUrl", {"previewUrl": editor, "listUrl": listing}), *candidates]
|
|
60
|
+
base = urlsplit(current["baseUrl"])
|
|
61
|
+
for source, form in candidates:
|
|
62
|
+
for key in ("previewUrl", "listUrl"):
|
|
63
|
+
if key in result or not isinstance(form.get(key), str):
|
|
64
|
+
continue
|
|
65
|
+
url = urlsplit(form[key])
|
|
66
|
+
query = parse_qs(url.query)
|
|
67
|
+
if (url.scheme, url.hostname, url.port) != (base.scheme, base.hostname, base.port):
|
|
68
|
+
continue
|
|
69
|
+
if url.username or url.password or url.fragment or any(
|
|
70
|
+
re_key in name.lower() for name in query for re_key in ("token", "password", "secret", "cookie", "authorization")):
|
|
71
|
+
continue
|
|
72
|
+
if url.path.rstrip("/") != base.path.rstrip("/"):
|
|
73
|
+
continue
|
|
74
|
+
if key == "listUrl" and (query.get("formId") != ["bos_list"] or query.get("billFormId") != [form_number]):
|
|
75
|
+
continue
|
|
76
|
+
if key == "previewUrl" and query.get("formId") != [form_number]:
|
|
77
|
+
continue
|
|
78
|
+
result[key] = form[key]
|
|
79
|
+
result.setdefault("sources", {})[key] = str(source)
|
|
80
|
+
if result.get("previewUrl") or result.get("listUrl"):
|
|
81
|
+
result["status"] = "entry-available"
|
|
82
|
+
return result
|
|
83
|
+
|
|
18
84
|
def export_test_environment(app_root, server, form_numbers, form_ids=None, form_links=None):
|
|
19
85
|
root = Path(app_root).resolve()
|
|
20
86
|
context_path = root / "_runtime" / "context.json"
|
|
@@ -1,13 +1,24 @@
|
|
|
1
1
|
# Cosmic Java 编码实现
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
`
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
根据用户需求、所选应用/表单和 Java 工程实现或修改源码。前置数据由本技能独立获取,
|
|
4
|
+
不要求调用方准备 handoff、建模产物、写入回执或字段映射,也不消费上游编码快照。
|
|
5
|
+
`{ROOT_DIR}` 是根技能的技能包目录。
|
|
6
|
+
|
|
7
|
+
在查找工程、目录、配置或源码之前,若用户在本任务中尚未指定 Java 插件源码目录,立即通过 `question`(AskUserQuestion) 询问并等待答复。复用用户已明确给出的目录或已有源码文件;不要根据工作区、appconfig、历史工程或命名约定推断用户选择。与目录无关的元数据工作可继续。
|
|
8
|
+
用户选择 `src/main/java` 根或其下包目录时,将其绝对路径放入 `intent.sourceDirectory`,编码命令直接写入该目录并从路径确定模块根;修改现有文件时使用 `ownershipMode: "modify"` 和准确 `sourcePath`。所选目录不在 Java 模块内时,询问模块根,不自行搜索。
|
|
9
|
+
|
|
10
|
+
写源码前,按[运行时路由](../../references/plugin-runtime-routing.md)确定业务触发类型,
|
|
11
|
+
由本技能按[编码上下文输入](../../references/plugin-handoff-input.md)读取当前目标的在线元数据及目标 SDK。
|
|
12
|
+
准备脚本保存的上下文只是本轮编码的本地证据,不是上游交接或元数据阶段的前置条件;
|
|
13
|
+
脚本的历史文件名不改变责任归属。传入所选 appRoot、准确表单编码、业务意图和 Java 工程,
|
|
14
|
+
不传建模产物,不复用之前的 handoff。直接执行返回的 `codingStartCommand`,读取一次本轮 `coding-start`,仅沿准确 pointer 补查缺失事实。
|
|
15
|
+
不依赖平台元数据的已有源码修改使用文档中的 source-only 分支。
|
|
16
|
+
|
|
17
|
+
涉及持久化数据时,完整字段归属和集合路径以当前 `formGraph` / `relatedForms` 为准,
|
|
18
|
+
可选 `fields` 别名不是完整清单。使用解析出的工程、包与源码路径。缺失事实通过目标原生契约读取,
|
|
19
|
+
不能猜测,也不搜索兄弟项目或旧测试重新推导。无需额外规划文档或编码 Agent。
|
|
20
|
+
|
|
21
|
+
目标字段、必需接收者签名和业务范围明确后就写源码,不把准备扩展为设计阶段,也不为确认已知事实重复查证或浏览邻近实现。同一表单同类型需要多个独立类时,在准备前明确各自 `className`;默认名只标识一个类,不表示平台限制一个类。只有本来属于同一类的行为才合并意图。
|
|
11
22
|
|
|
12
23
|
## 使用已提供的证据
|
|
13
24
|
|
|
@@ -15,6 +26,16 @@
|
|
|
15
26
|
契约。其中的准确签名、导入和弃用提示已经是 API 证据,不要求为满足“本轮已读”而重复打开
|
|
16
27
|
页面。`coreLogic` 中的业务事件名不是 SDK 签名。
|
|
17
28
|
|
|
29
|
+
只读取缺失或适用的知识。先用类型指南中本次事件和直接相关场景;未覆盖时查
|
|
30
|
+
`knowledge/cosmic/platform/scenes/_index.md`,结合事件、控件和行为查找相关场景。
|
|
31
|
+
类型标签用于缩小候选,不覆盖场景已验证的适用范围。读取 `codingEvidence.scenes` 中
|
|
32
|
+
已选场景,按实际需要补核心 view/model 或 SDK 指南,再查准确 API 或绑定 SDK。
|
|
33
|
+
对实际存在的幂等、大数据或安全风险使用已提供的 `specialized.hitModules` 和约束;
|
|
34
|
+
未提供命中时,根据行为读取 `references/type-registry.json` 中 `specializedStandards`
|
|
35
|
+
对应标准,不为记录命中重复准备。已有命中读取 `codingEvidence.specializedReferences`。
|
|
36
|
+
用户可见文本还需阅读
|
|
37
|
+
`standards.md` 的多语言章节。
|
|
38
|
+
|
|
18
39
|
只读取缺失或适用的知识:
|
|
19
40
|
|
|
20
41
|
- 已有证据不能确定事件位置、必需 `super` 调用或异常行为时,读所选指南的相关生命周期/场景。
|
|
@@ -53,12 +74,16 @@ modify 先完整读取现有文件,以它为起点。在已有触发方法内
|
|
|
53
74
|
方法清单,不是要求重新声明。保留无关代码、包、类与导入。create 使用所选类型的基类及生命
|
|
54
75
|
周期契约,写入返回的 `sourcePath`。
|
|
55
76
|
|
|
56
|
-
|
|
77
|
+
写入前仅在会话绑定一致或未绑定时,复用 `coding-start` 实读的 `sourceGate.state="coding"`;
|
|
78
|
+
新子会话只为获派的类重新绑定一次,门禁标记不代表源码已编译。
|
|
79
|
+
`sourceGate.requestedState` 本身不代表成功。未提供实读状态时,只读取一次门禁:
|
|
57
80
|
|
|
58
81
|
```text
|
|
59
|
-
{python} "{ROOT_DIR}/scripts/plugin_source_gate.py" --project-root "{projectRoot}" --class-name "{className}" --
|
|
82
|
+
{python} "{ROOT_DIR}/scripts/plugin_source_gate.py" --project-root "{projectRoot}" --class-name "{className}" --read
|
|
60
83
|
```
|
|
61
84
|
|
|
85
|
+
确实需要开放源码编辑时,同一命令改用 `--write coding` 并确认返回成功,不为开门禁重复准备 handoff。
|
|
86
|
+
|
|
62
87
|
- 使用已验证的 `fields` / `entities` 键,以及 `fieldContracts` 中的准确访问器和值类型。
|
|
63
88
|
引用字段的记录不是 ID,比较时使用其 `idAccessor` 或 `filterValue`。
|
|
64
89
|
目标 8.0 SDK 的 `TimeProp` 是距午夜的整秒数,使用返回的 `getInt` 访问器,
|
|
@@ -66,6 +91,12 @@ modify 先完整读取现有文件,以它为起点。在已有触发方法内
|
|
|
66
91
|
[CosmicTimeAdapter.java](../../../../examples/java-rules/CosmicTimeAdapter.java)
|
|
67
92
|
示例演示先拒绝异常值,再转换为普通 Java 时间。
|
|
68
93
|
- 准确复制基类导入和重写签名,包括大小写与参数类型。按接收者证据核对平台调用,不靠近似名称猜 API。
|
|
94
|
+
核对准确重载的方法名、参数数量/类型及各参数的业务角色。
|
|
95
|
+
`OperationServiceHelper.executeOperate` 顺序是 `(operationKey, entityNumber, ids/dataEntities, option)`;
|
|
96
|
+
前两个参数都是 `String`,编译不能发现角色颠倒。已收录重载需要 `OperateOption`,
|
|
97
|
+
不能借用 `DeleteServiceHelper` 的三参数重载。删除时传已验证的主键,先检查
|
|
98
|
+
`OperationResult.isSuccess()`,成功后才清空关联引用或保存依赖变更;失败按生命周期异常契约上抛。
|
|
99
|
+
仅方法名相同或 `compile=false` 不能证明调用合法。
|
|
69
100
|
- `QFilter` 使用操作符表推荐的辅助方法,否则使用对应 `QCP` 常量,不能用字符串操作符。
|
|
70
101
|
仅在所需操作符尚无证据时读取该表。
|
|
71
102
|
- 实现 `coreLogic`、已提供的 `reportContract` 和 `specialized.constraints`。
|
|
@@ -85,6 +116,13 @@ modify 先完整读取现有文件,以它为起点。在已有触发方法内
|
|
|
85
116
|
测试时检查实际生产规则,不要为了脱离平台运行而把规则复制到测试专用类。
|
|
86
117
|
返回真实命令与结果,未执行时如实标记未运行。本地规则测试不能证明平台事件绑定或运行态业务覆盖。
|
|
87
118
|
- 按所选生命周期处理空值、异常和 `super`。标识符用英文,注释和用户可见文本跟随回复语言。
|
|
119
|
+
- 从第一版源码遵循 [DCS 部署规则](../../references/dcs-code-rules.md):编译和运行成功仍可能被部署门禁拒绝。
|
|
120
|
+
常见违规包括静态集合字段(即使 `final`)、`ORM` / `DB`、循环内日志/保存/`updateView`、
|
|
121
|
+
无有效过滤条件的查询、使用三次却未提取常量的字段键、未使用导入和空方法。规则目录取原表全部规则
|
|
122
|
+
与新表高危/严重规则的并集,共 1582 个编号。规则指定的 `BusinessDataServiceHelper` /
|
|
123
|
+
`QueryServiceHelper` 方法即使在单层循环内也阻断;不绕过 TLS 证书/主机名验证,不使用可预测的
|
|
124
|
+
`SecureRandom` 种子,使用 `try-with-resources`。目录登记不等于完整自动检测;核实建议项,
|
|
125
|
+
全构建、TypeScript、KSC 和元数据规则交给对应引擎检查。
|
|
88
126
|
|
|
89
127
|
检查修改源码中的重复签名、导入/可见性、字段访问、空值和集合边界、吞异常及已提供约束,
|
|
90
128
|
在任务范围内修复。
|
|
@@ -96,9 +134,13 @@ modify 先完整读取现有文件,以它为起点。在已有触发方法内
|
|
|
96
134
|
## 编译并继续交付
|
|
97
135
|
|
|
98
136
|
按 [compile-guide.md](../../references/compile-guide.md) 执行原生编译/归档边界。
|
|
137
|
+
需要工程测试时使用准备结果的 `testCommand` 和该指南的目标测试入口;
|
|
138
|
+
实际测试数量与断言失败独立于编译结果。
|
|
99
139
|
该命令同时推进源码门禁,不单独写归档或标记 `source_ready`。
|
|
100
140
|
实施者随后继续 [java-plugin-delivery.md](../../references/java-plugin-delivery.md) 完成注册与交付;
|
|
101
141
|
达到 `source_ready` 不代表用户整体请求结束。
|
|
102
142
|
|
|
103
143
|
报告原生结果中的真实源码身份、编译/归档结果、`compileSkipped` 和未验证项。
|
|
104
144
|
源码及编译/归档写入保持在已验证项目和应用路径内。
|
|
145
|
+
|
|
146
|
+
操作插件读取分录时,在字段准备中声明实际读取的子字段;仅声明集合不足以保证列表批量操作加载子字段。逐单将错误挂在对应的 `ExtendedDataEntity`,保留混合批量中合格单据的执行。时间值 0 是零点;先识别原始空值,再判断业务时段。
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
| 查询属性 Schema | POST | `/ai-meta/property/getPropertySchema` | 查询元素可设置属性的 Schema 信息 |
|
|
22
22
|
| 读取属性当前值 | POST | `/ai-meta/property/getProperties` | 读取元素属性当前值 |
|
|
23
23
|
| 批量设置简单属性 | POST | `/ai-meta/property/setProperties` | 批量设置简单属性(text/checkbox/combo 等) |
|
|
24
|
-
| 设置复杂属性 | POST | `/ai-meta/property/setComplexProperty` |
|
|
24
|
+
| 设置复杂属性 | POST | `/ai-meta/property/setComplexProperty` | 设置复杂属性(统一入口,由 ComplexPropertyRouter 路由;支持分支取决于后端版本) |
|
|
25
25
|
| 查询参数表单列表 | POST | `/ai-meta/property/listParameterForms` | 查询可选的参数表单列表 |
|
|
26
26
|
| 搜索基础资料 | POST | `/ai-meta/basedata/search` | 按关键词搜索可用的基础资料 |
|
|
27
27
|
| 获取基础资料实体 ID | POST | `/ai-meta/basedata/getEntityId` | 根据表单编码获取基础资料实体 ID |
|
|
@@ -836,7 +836,7 @@ Content-Type: application/json
|
|
|
836
836
|
| `columnName` | String | 否 | DB 列名(省略则自动生成) |
|
|
837
837
|
| `basedataNumber` | String | BasedataField 必填 | 基础资料编码,如 `"bd_supplier"`、`"bd_customer"` |
|
|
838
838
|
|
|
839
|
-
>
|
|
839
|
+
> 已检查的 API 中,`FieldOperationHandler.applyAddField` 接受 Boolean 类型的 `value.mustInput`。初始必录直接声明并回读,不能发送字符串 `"true"`。旧部署若拒绝该参数,先独立完成并验证创建,再用带回读的 `setProperties` 写入 `MustInput`;不要追加不可靠的属性 modify 或重建字段。报表过滤还需在 `verifyQuery()` 和 `query()` 取数前校验必录,字段标志保存成功不代表查询校验已经实现。
|
|
840
840
|
|
|
841
841
|
### 响应
|
|
842
842
|
|
|
@@ -1229,7 +1229,7 @@ python cosmic_api.py setProperties --formNumber sal_order --elementKey amount --
|
|
|
1229
1229
|
|
|
1230
1230
|
### 8.5 设置复杂属性(统一入口)
|
|
1231
1231
|
|
|
1232
|
-
设置 `btnedit` 类型的复杂属性。通过 `complexType` 参数路由到 `ComplexPropertyRouter`
|
|
1232
|
+
设置 `btnedit` 类型的复杂属性。通过 `complexType` 参数路由到 `ComplexPropertyRouter` 对应的处理方法,支持的分支取决于实际部署的后端版本。
|
|
1233
1233
|
|
|
1234
1234
|
#### 请求
|
|
1235
1235
|
|
|
@@ -1296,6 +1296,19 @@ Content-Type: application/json
|
|
|
1296
1296
|
| `instantSync` | 数据实时同步 | Map | `{columns:[{fieldkey:string, canUpdate:bool}]}` —— 平台存成 JSON 字符串且 columns 是对象;`action`(行事件)只读。优先用 `setInstantSync` |
|
|
1297
1297
|
| `dropdownItems` | 下拉菜单 | List | 下拉菜单项列表 |
|
|
1298
1298
|
|
|
1299
|
+
**报表叶子列删除(不传 elementKey;需要后端补丁):**
|
|
1300
|
+
|
|
1301
|
+
| complexType | 说明 | data 类型 | data 结构 |
|
|
1302
|
+
|-------------|------|----------|----------|
|
|
1303
|
+
| `ReportColumns.remove` | 删除一个精确叶子列;拒绝分组列 | Map | `{fieldKey:string}` |
|
|
1304
|
+
|
|
1305
|
+
使用已发布 `removeReportColumn` 命令,传入当前应用目录与精确报表编码。
|
|
1306
|
+
处理器删除列控件、关联孤立字段和对应默认排序,重排同级列后保存。
|
|
1307
|
+
PropertyResult 的 data 包含 fieldKey、fieldId 和 action removed;保存失败不能证明没有修改。
|
|
1308
|
+
旧 Router 可能在 HTTP 端点已经存在时仍返回不支持类型,并被 META_SAVE_FAIL 包装。
|
|
1309
|
+
CLI 返回 REPORT_COLUMN_DELETE_API_UNAVAILABLE 并停止,不隐藏、不重建、不重放;业务、
|
|
1310
|
+
权限、保存和瞬时错误不属于这个能力判定。
|
|
1311
|
+
|
|
1299
1312
|
#### 请求示例 1:设置权限控制(主实体级,不传 elementKey)
|
|
1300
1313
|
|
|
1301
1314
|
```json
|
package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/report-write-contracts.json
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"commands": {
|
|
11
11
|
"setReportColumns": {
|
|
12
12
|
"payloadSchemaRef": "skills/cosmic-meta-api/contracts/schema/report-write.schema.json#/$defs/columns",
|
|
13
|
-
"writeSemantics": "create-time only; ReportColumns replaces the column set but appends report-entity fields instead of replacing them, so a second call duplicates every field key and bricks the form. Refused with REPORT_COLUMNS_NOT_REPLACEABLE, whose message names addReportColumns for an addition and planReportRecreate for
|
|
13
|
+
"writeSemantics": "create-time only; ReportColumns replaces the column set but appends report-entity fields instead of replacing them, so a second call duplicates every field key and bricks the form. Refused with REPORT_COLUMNS_NOT_REPLACEABLE, whose message names addReportColumns for an addition, removeReportColumn for a single leaf removal on a patched backend, and planReportRecreate for reorder or re-type, and states that adding the column in the designer is not the way around it.",
|
|
14
14
|
"readBack": "getReportSchema.reportColumns matches the requested column tree"
|
|
15
15
|
},
|
|
16
16
|
"addReportColumns": {
|
|
@@ -20,17 +20,30 @@
|
|
|
20
20
|
"response": "plannedAppendFieldKeys and preexistingFieldKeys classify the recursive request from the pre-write projection; they are planning facts, not a platform receipt.",
|
|
21
21
|
"preWriteGuard": "REPORT_ENTITY_FIELDS_DUPLICATED still applies: appending cannot repair a report whose entity already carries duplicate field keys. A column-key collision is deliberately NOT refused — the platform skips it, and refusing it would break idempotence."
|
|
22
22
|
},
|
|
23
|
+
"removeReportColumn": {
|
|
24
|
+
"payloadSchemaRef": "skills/cosmic-meta-api/contracts/schema/report-write.schema.json#/$defs/columnRemoval",
|
|
25
|
+
"writeSemantics": "Delete one exact leaf (nested leaves included) through POST ai-meta/property/setComplexProperty, complexType ReportColumns.remove, data {fieldKey}. The backend removes the control, removes associated orphan entity fields while preserving fields referenced by remaining controls, renumbers siblings and removes default sorts for that fieldKey, then saves. No standalone delete endpoint, full replacement, hide or recreate fallback.",
|
|
26
|
+
"preWriteGuard": "CLI and serve require appRoot plus exact formNumber, prove owned ReportFormModel and optional formId, and require readable unique column structure. Groups and absent/ambiguous keys fail before deletion. A known presentation.currencyField / measureUnitField dependent blocks deletion with REPORT_COLUMN_IN_USE. The duplicate entity guard remains; this command is not a corrupted-entity repair.",
|
|
27
|
+
"compatibility": "Backend and client releases are independent. Explicit unsupported complex-property-type replies (including inner PropertyResult under META_SAVE_FAIL), or a missing HTTP route, return status=false with REPORT_COLUMN_DELETE_API_UNAVAILABLE and capabilityAvailable=false. Preserve the local baseline and stop; wait for the backend patch. Do not treat PARAM_INVALID, missing elements, permission denial, save rejection or transient/unknown transport errors as unavailable. No version guessing, capability cache or automatic POST replay.",
|
|
28
|
+
"readBack": "Success requires a matching fieldKey/action=removed receipt followed by independent getReportSchema: target absent, all other nodes preserved with observable properties, parent and sibling order (index may renumber); default sorts for the key removed when observable, filters/plugins preserved when observable. Missing sections are unverified, never empty-array evidence. Compact schema does not expose the field-id reference graph: entityFieldCleanup remains explicitly unverified, not a runtime claim.",
|
|
29
|
+
"authoring": "Refresh a matching current complete baseline only after read-back verifies: remove the leaf, its default sorts and reportProperties.columns entry, retaining other presentations, filters and plugins. Missing/drifted baseline or unverified write becomes incomplete. An empty column tree or empty group is not representable by the creation Artifact and also becomes incomplete; never guess requirements or rewrite historic run results.",
|
|
30
|
+
"failureSemantics": "Explicit pre-mutation input/element rejections keep the baseline. Save failures or unknown outcomes carry platformMutationPossible; a successful receipt followed by failed/mismatching read-back carries platformMutationApplied. Invalidate stale authoring in either case and re-read/reconcile before any retry. A repeated command on an already absent column is a preflight failure, not a replay or proof of the earlier write.",
|
|
31
|
+
"javaFollowUp": "Metadata deletion does not update existing Java: reconcile getColumns(), query() DataSet bindings and dependent logic against the refreshed reportContract. Do not claim compilation, registration/deployment or runtime verification from deletion alone."
|
|
32
|
+
},
|
|
23
33
|
"setReportFilters": {
|
|
24
34
|
"payloadSchemaRef": "skills/cosmic-meta-api/contracts/schema/report-write.schema.json#/$defs/filters",
|
|
25
|
-
"writeSemantics": "full replacement; the supplied array is the desired complete filter state",
|
|
26
|
-
"controlTypeBoundary": "Optional
|
|
27
|
-
"
|
|
28
|
-
"
|
|
35
|
+
"writeSemantics": "full replacement; the supplied array is the desired complete filter state, editor parameters included",
|
|
36
|
+
"controlTypeBoundary": "Optional fieldType (25 editor classes) with its parameters builds the filter's native editor through the platform's typed report filter API, which ships as a platform patch. Without fieldType the platform renders a text box, or a date range for between. A platform without the patch accepts a typed request, ignores the editor part and renders that legacy control: the write succeeds and filterEditors reports the filter as degraded. A platform with an older patch refuses an editor type it does not offer before saving anything; the CLI resends that filter without its editor and reports it degraded. Never recreate a report to change an editor and never emulate one with setComplexProperty: re-apply setReportFilters once the patch is installed.",
|
|
37
|
+
"receipt": "A platform with the typed filter patch answers with data.filters[] (fieldPath, the editor class it built, fieldId, action created/updated/reused/replaced) and data.removed[]. Its absence is how an unpatched platform is recognized; no separate probe is sent.",
|
|
38
|
+
"readBack": "优先消费 getFormSchema.data.reportFilters 的结构化投影,按过滤分区保存 readBackSources,核对注册键、唯一控件、实体类型、mustInput、visibleEffective、引用目标、范围键及可读选项;名称仅取同响应中匹配 controlId/fieldId/key 的控件。unknown/null 保持未验证,坏 JSON、缺面板、孤儿/重复控件不能验收通过。旧平台保留 formLayout/schemaText fallback;多选引用只在新投影实际给出 baseEntityNumber 时验证。compareConfig 保留原来源/目标/类型,不猜比较 ID,也不证明 query 已生效。",
|
|
39
|
+
"preWriteGuard": "REPORT_FILTER_KEY_COLLIDES_WITH_COLUMN: a fieldPath repeating a report column or group fieldKey is refused before the platform call, because control and column would share one element key and only the designer would reject the duplicated identifier. Use a distinct key such as flt_<column> and apply it to the column inside the data-fetch plugin. REPORT_INPUT_INVALID for the editor rules the patch enforces: an unknown fieldType, a parameter without fieldType or on another type, a missing required parameter (basedataNumber, comboItems, itemClassTypes, typeFieldKey, asstTypeId), between on a single-value editor, a range editor without between, defaultValue on an F7 editor, mustInput together with visible:false."
|
|
29
40
|
},
|
|
30
41
|
"setReportSorts": {
|
|
31
42
|
"payloadSchemaRef": "skills/cosmic-meta-api/contracts/schema/report-write.schema.json#/$defs/sorts",
|
|
32
|
-
"
|
|
33
|
-
"
|
|
43
|
+
"availability": "blocked",
|
|
44
|
+
"writeSemantics": "REPORT_SORTS_API_UNSAFE: 所有入口写前阻断,包括 dry-run 和通用 ReportSorts/InitField 排序写入。旧 API 曾写入非法可见性条目;bos1030 已返回 REPORT_SORT_NOT_SUPPORTED,但仍没有已验证的原生排序通道。InitField 是默认列可见性,不是排序。",
|
|
45
|
+
"readBack": "No metadata sorting verification. InitField sort entries are invalid visibility data, never evidence that ordering works.",
|
|
46
|
+
"replacement": "Retain reportSorts in the complete Artifact. Before createPage require exactly one enabled Java reportlist plugin and leaf-column references. Compile ordered fieldKey/direction/dateIndex rules into reportContract.defaultSorts for query(); no silent dropping or unauthorized plugin substitution. Existing invalid InitField needs separate repair."
|
|
34
47
|
},
|
|
35
48
|
"registerReportPlugin": {
|
|
36
49
|
"payloadSchemaRef": "skills/cosmic-meta-api/contracts/schema/report-write.schema.json#/$defs/plugin",
|
|
@@ -39,10 +52,10 @@
|
|
|
39
52
|
},
|
|
40
53
|
"setReportProperties": {
|
|
41
54
|
"payloadSchemaRef": "skills/cosmic-meta-api/contracts/schema/report-write.schema.json#/$defs/reportProperties",
|
|
42
|
-
"writeSemantics": "per-key update of one host control's scalar properties, selected by --target: list -> ReportListSettings on ReportListAp, filterPanel -> ReportFilterPanel on ReportFilterAp, moreFilterPanel -> ReportMoreFilterPanel, form -> ReportFormSettings on the form root (whose element key IS the form number), tree -> ReportTree on ReportTreeAp, column -> ReportColumnProps on one EntryFieldAp or EntryFieldGroupAp named by --element. Keys absent from the map are left alone; this is not a full replacement.
|
|
55
|
+
"writeSemantics": "per-key update of one host control's scalar properties, selected by --target: list -> ReportListSettings on ReportListAp, filterPanel -> ReportFilterPanel on ReportFilterAp, moreFilterPanel -> ReportMoreFilterPanel, form -> ReportFormSettings on the form root (whose element key IS the form number), tree -> ReportTree on the report's one ReportTreeAp, found by element type through listElements because a user adds that control in the designer under any key, column -> ReportColumnProps on one EntryFieldAp or EntryFieldGroupAp named by --element. Keys absent from the map are left alone; this is not a full replacement. 'column' needs an --element; 'tree' takes one only to pick among several ReportTreeAp controls, and refuses a key of any other type.",
|
|
43
56
|
"readBack": "getProperties on the resolved host, ONE property per request, both before and after the write. Reading first is what makes the verdict honest: platform defaults are never assumed, so a value that already held is reported under indistinguishableFromCurrentValue instead of as proven. ColumnWidthStyle, MaxRowHeight, HeadHeight and ReportSumMeta resolve on no getProperties call and are reported under unverifiableRequestedProperties with a designStateExpectation the caller can settle from the platform's own export; verified is false when the write consists only of such keys.",
|
|
44
|
-
"preWriteGuard": "REPORT_PROPERTY_INPUT_INVALID for a name outside the target's allow-list, a wrong value type, a value outside a declared domain, or a key another route owns — the refusal names that route. REPORT_PROPERTY_ELEMENT_MISMATCH when the host resolves none of the requested readable properties, which is what stops a
|
|
45
|
-
"hazard": "ShowFilterFields is also written by setReportFilters
|
|
57
|
+
"preWriteGuard": "REPORT_PROPERTY_INPUT_INVALID for a name outside the target's allow-list, a wrong value type, a value outside a declared domain, or a key another route owns — the refusal names that route. REPORT_PROPERTY_ELEMENT_MISMATCH when the form has no ReportTreeAp for a tree write, or when the host resolves none of the requested readable properties, which is what stops a write against a report that lacks the host from reporting success and changing nothing. The local allow-list is not defensive duplication: the backend applies a flat map by reflection and IGNORES an unrecognised key while still answering success, so a case slip would otherwise look applied.",
|
|
58
|
+
"hazard": "ShowFilterFields is also written by setReportFilters. A platform without the typed report filter patch writes it as a comma-joined string (observed live as \"flt_biztime,flt_supplier,flt_billstatus\"); the runtime parses that property as a JSON array, so the comma form raises KDException when a user updates the selected conditions. This command writes the JSON array form and is therefore the repair; re-apply it after any setReportFilters call on such a platform, which the filter write reports as filterEditors.platformReceipt false. A patched platform writes the array itself, but resets FilterMustInput and DefaultHiddenFields from the filters' mustInput / visible, so set those after setReportFilters, not before."
|
|
46
59
|
},
|
|
47
60
|
"setReportRowBackColor": {
|
|
48
61
|
"payloadSchemaRef": "skills/cosmic-meta-api/contracts/schema/report-write.schema.json#/$defs/rowBackColorRules",
|
|
@@ -76,21 +89,34 @@
|
|
|
76
89
|
"identityResolution": "queryForms is a LIKE search, so only a row whose formNumber equals the requested number counts; its formId becomes dataSourceId and its formName the cached dataSourceName. The stored name is never compared on read-back: a live binding was observed still carrying an older name after a rename."
|
|
77
90
|
}
|
|
78
91
|
},
|
|
92
|
+
"typedFilterEditors": {
|
|
93
|
+
"note": "The skill ships with the client and the typed report filter API ships as a platform patch, so a client can be newer than the platform. Every setReportFilters result and every report run carries filterEditors (schemaVersion app-build-report-filter-editors/v1): per filter the requested editor, the legacy one, what the platform built and the status, plus typedFilterSupport for the whole write.",
|
|
94
|
+
"statuses": {
|
|
95
|
+
"typed": "the requested editor rendered",
|
|
96
|
+
"degraded": "the platform refused or ignored the editor and renders the legacy control; an unresolved boundary the reply must name, fixed by re-applying the filters after the patch is installed",
|
|
97
|
+
"unconfirmed": "no receipt and no read-back type yet",
|
|
98
|
+
"mismatch": "an editor that is neither the requested nor the legacy one; fails the write",
|
|
99
|
+
"legacy": "the filter asked for no editor type"
|
|
100
|
+
},
|
|
101
|
+
"typedFilterSupport": ["available", "partial", "unavailable", "unconfirmed", "mismatch", "notRequested"],
|
|
102
|
+
"compatibility": "A degraded filter is not a failed write: the report keeps working with the legacy control. The plugin contract names both editors for every typed filter, so query() can read either value shape and keeps working when the patch arrives and the filters are re-applied."
|
|
103
|
+
},
|
|
79
104
|
"readBackSource": {
|
|
80
|
-
"canonical": "Backend exposes data.reportSchema;
|
|
81
|
-
"formLayout": "No data.reportSchema
|
|
82
|
-
"
|
|
105
|
+
"canonical": "Backend exposes data.reportSchema; observable metadata sections are compared. reportSorts remains unverified: no supported native sorting contract has been confirmed, and InitField is visibility, not sorting.",
|
|
106
|
+
"formLayout": "No data.reportSchema on legacy deployments. getReportSchema derives columns and filters from data.formLayout and lists what that source cannot carry in unverifiableSections.",
|
|
107
|
+
"reportFilterProjection": "bos1030 的 data.reportFilters 独立于 data.reportSchema;只提升过滤分区,保留完整 reportFilterProjection/unknown/孤儿/重复/未管理控件诊断。正常 JSON 注册与承载面板必须可验证,其他分区不因此提升。新过滤投影不被 DYM 的默认值覆盖;比较配置只保留为 compareConfig,业务查询仍需独立验收。",
|
|
108
|
+
"formLayout+dym": "formLayout plus the platform's own design-state export, which syncDym writes into the application. <ReportQueryPlugin> proves reportPlugins, which leaves unverifiableSections. <InitField> is default column visibility; purpose=sort entries are pollution and never prove reportSorts. The export also proves two filter properties the API projection cannot decide: <MustInput> and <Visible> on each filter control, promoted per property rather than per section because formLayout already proves the controls exist. It serializes design state and omits any property still holding its default, so for a control the export contains, a present node proves the value and an absent node proves the default; a property is promoted only when the export covers every derived filter. The export carries no plugin type/bizAppId/fpk and no <DefaultValue>: those are listed in undecidablePluginProperties / undecidableFilterProperties instead of being compared. Plugin description is in that list for a different reason, and is the one entry expected to leave it: a platform defect, fixed at the source but not yet in the measured deployment. Sending Description \"DESC_VALUE\" with DisplayName \"DISPLAY_VALUE\" read back the plugin's own full class name and no mulDescription resId, while the identical payload sent as ReportPluginExt read back \"DESC_VALUE\" with a resId. The cause is a pair of coupled ReportListAp accessors: getReportPlugin() falls back to the first Java plugin's class name and save serialisation goes through it, so the legacy <ReportPlugin> node is resurrected on every save; getReportQueryPlugin() then sees that non-empty legacy field and CLEARS the persisted plugin list to rebuild it with description=class name, so a second save could corrupt a registration that had stored correctly. Comparing description failed every run whose artifact declared one. Remove it from the list once the fixed platform is deployed. An empty section in the export never proves absence, and it never overrides a canonical projection.",
|
|
83
109
|
"unavailable": "Neither is present. Report verified=false; never treat an empty section array as a passing read-back."
|
|
84
110
|
},
|
|
85
111
|
"designStateEvidence": {
|
|
86
112
|
"freshness": "A .dym on disk only proves the platform state as of the last syncDym, so it is synced after the writes and before verification. The report pipeline does this automatically — including when the only thing needing it is a filter that requested mustInput or visible — while getReportSchema does it only under --with-dym-evidence (which requires --app-root) and never trusts a file it did not just sync.",
|
|
87
|
-
"provenSections": ["
|
|
113
|
+
"provenSections": ["reportPlugins"],
|
|
88
114
|
"provenFilterProperties": ["mustInput", "visible"]
|
|
89
115
|
},
|
|
90
116
|
"rejectedInputs": {
|
|
91
117
|
"note": "Rejected at input normalization so a caller never believes an unsupported property was applied.",
|
|
92
118
|
"reportColumns": ["currencyField", "measureUnitField", "scale", "format", "entityId", "displayProp", "formula", "style"],
|
|
93
|
-
"reportFilters": ["enableFuzzy", "required", "basedataFilter"],
|
|
119
|
+
"reportFilters": ["enableFuzzy", "required", "basedataFilter", "source", "baseEntityId"],
|
|
94
120
|
"reportPlugins": ["displayName", "generateCode", "codeSpec"],
|
|
95
121
|
"queryDataSource": ["dataSourceId", "dataSourceName"],
|
|
96
122
|
"artifact": ["dataSources", "reportBehavior"],
|
|
@@ -100,7 +126,7 @@
|
|
|
100
126
|
"DataSource": "setReportQueryDataSource --dataSourceNumber (this key overwrites the deprecated string-shaped property)",
|
|
101
127
|
"QueryDataSource": "setReportQueryDataSource --dataSourceNumber",
|
|
102
128
|
"ReportColumns": "addReportColumns",
|
|
103
|
-
"InitField": "
|
|
129
|
+
"InitField": "default column visibility, not sorting; ReportSorts is blocked. Preserve sorting requirements in reportContract.defaultSorts for Java query().",
|
|
104
130
|
"ReportQueryPlugin": "registerReportPlugin",
|
|
105
131
|
"ReportPluginExt": "registerReportPluginExt",
|
|
106
132
|
"RowBackColor": "setReportRowBackColor",
|
|
@@ -122,25 +148,32 @@
|
|
|
122
148
|
},
|
|
123
149
|
"pluginContract": {
|
|
124
150
|
"note": "Report metadata declares presentation only, so every requirement it cannot carry is compiled into {appRoot}/_runtime/<formNumber>.report-plugin-contract.json and forwarded by prepare_plugin_handoff as handoff.reportContract. A report with at least one column has a contract whenever its rows come from code: which columns query() must return, and with which Java type, is itself a requirement. A report that binds a query data source and registers no plugin gets none, because there is no query() to constrain.",
|
|
151
|
+
"pluginClasses": "The Java classes the report registers, and pluginTypes the plugin type each one's slot runs: a reportPlugins entry is reportlist (ReportListAp.ReportQueryPlugin, AbstractReportListDataPlugin), the left tree's ReportTreeAp.ReportPlugin is reporttreelist (present only after a user adds the report tree control in the designer, AbstractReportTreeDataPlugin). JS and KingScript entries are not listed: they have no Java source to deliver. render_output_cards.py refuses the cards with REPORT_PLUGIN_SOURCE_MISSING while any listed class has no archived source, naming the type to deliver it as.",
|
|
125
152
|
"dataSetColumns": "The dateIndex of every declared column, case-sensitive: what query() must return. No metadata cross-check is possible.",
|
|
126
|
-
"presentationBinding": "presentation.currencyField / measureUnitField
|
|
153
|
+
"presentationBinding": "presentation.currencyField / measureUnitField 保留币别或单位引用要求。REPORT_PRECISION_REFERENCE_UNVERIFIED 在 normalizer、CLI/serve/pipeline 和 set/add writer 写前拒绝 amount/price/qty。隐藏 bigint 的 Long、fieldKey == dateIndex 及 ReportColumnFactory 不能证明 DynamicObject 物化;R-presentation-binding-target 为 error。原始/历史契约仍用 bindsTo/requiredBy 保留依赖,不代表可写或可运行。",
|
|
154
|
+
"precisionSource": "动态绑定逐列标记 requiredRuntimeValueType=DynamicObject、runtimeVerified=false、rawIdIsReference=false、metadataWriteBlocked=true。requiredEvidence 要求持久化引用属性及旧标量迁移、格式化前批量引用物化、混合精度/分组/合计/分页/导出验收。平台能力验证后再开放 schema 和 writer;不以固定精度或删除绑定替代需求。",
|
|
127
155
|
"dataSetColumnTypes": "Per column: rowAccessor (the kd.bos.algo.Row method that reads it), javaType (the value it must carry) and algoDataType (the kd.bos.algo.DataType to declare when building a DataSet by hand), all derived from the column's declared fieldType. The report sets each value into a metadata field, so a wrong type fails at set time naming the field, not the query: a date column returned as text raises 'setvalue fail, name: <column>, Unparseable date'. date and datetime both resolve to DataType.DateType because there is no DateTimeType, and decimal resolves to createBigDecimalType(precision, scale) because DataType.md documents a standalone BigDecimalType as illegal — with precision left as a placeholder, since no metadata knows it.",
|
|
128
|
-
"columnPresentation": "
|
|
129
|
-
"
|
|
156
|
+
"columnPresentation": "java 和 javaFollowUp 保留 ReportColumnFactory 的已记录签名,不是动态精度运行证据。历史/原始需求的 amount/price/qty 必须先满足 precisionSource,当前禁止写入及作为已完成实现交付。支持的呈现在 getColumns() 递归 ReportColumnGroup.getChildren() 并保留 setDateIndex。",
|
|
157
|
+
"defaultSorts": "Ordered fieldKey, direction and case-sensitive dateIndex bindings compiled from reportSorts, leaf columns only. Requires exactly one enabled Java reportlist plugin; no sorting metadata is written.",
|
|
158
|
+
"defaultSortPolicy": "query() honors validated interactive ReportQueryParam.getSortInfo() ordering first; defaults apply only when interactive ordering is absent. Apply to the final assembled DataSet after joins/unions/aggregation and before paging/export, preserving permissions. Target-SDK ordering APIs only; no unchecked SQL sort fragments. Source/compile/registration is not runtime evidence; verify default and interactive sorting separately.",
|
|
159
|
+
"filterEnforcement": "每个 compare 都保留为 query() 的显式条件要求。编辑器或 ReportFilterFieldConfig 的配置回读不证明实际业务查询已下推;mustInput 同样需要 verifyQuery/query 取数前共用校验。",
|
|
160
|
+
"filterValueContracts": "Per filter: the runtime key candidates, the value channel (getValue, or getFilterItems with the range item keys), the editor (editorFieldType) and the FilterInfo call that reads its value (valueAccessor, valueJavaType, valueEvidence measured or observeOnce). A typed filter also carries legacyEditor, the control a platform without the typed filter patch renders, and, when the run reported it, editorStatus and renderedFieldType. query() reads the editor that rendered and, unless editorStatus is typed, accepts the legacy shape too."
|
|
130
161
|
},
|
|
131
162
|
"acceptedButNotPersisted": {
|
|
132
|
-
"note": "
|
|
163
|
+
"note": "业务要求不静默丢弃,并保留至插件交付。按实际回读来源判定属性,不再笼统称未持久化:bos1030 投影可证明必录、有效可见性及引用/范围;旧平台使用布局及新鲜 DYM。缺失/null/unknown 属未验证,不是假阴性或默认 false。compareConfig 是配置证据,不替代业务 query;defaultValue 尚未读到。",
|
|
133
164
|
"reportFilters": {
|
|
134
|
-
"mustInput": "
|
|
135
|
-
"visible": "
|
|
165
|
+
"mustInput": "从结构化投影的实际实体字段 Boolean 验证;旧平台可由新鲜 DYM 补证据。未知不当 false,保存标志不等于查询必录校验。",
|
|
166
|
+
"visible": "新投影使用 visibleEffective 的实际 Boolean,不能把控件阶段字符串当 Boolean。旧平台由新鲜 DYM 验证;DefaultHiddenFields 可被后续独立写入替换。",
|
|
136
167
|
"defaultValue": "Sent, with no observable evidence in either read-back source. Reported unverifiable.",
|
|
137
|
-
"compare": "
|
|
168
|
+
"compare": "必填业务比较,始终编译为 query() 条件要求。旧平台观察不到比较;新投影从 ReportFilterFieldConfig 读出 compareConfig 来源和目标,但 setReportFilters 不因此自动绑定业务查询,不将其冒充公开比较运算符。"
|
|
138
169
|
}
|
|
139
170
|
},
|
|
140
|
-
"
|
|
171
|
+
"metaOpsReceipt": "新 modifyMeta 回执逐项核对 formId、opsCount、opIndex、op/目标类型、target 和 path。仅 committed、无回滚、完整同请求 no_op 且 metadataChanged=false 时报告未变更成功;no_channel/rejected/未知或不完整回执拒绝全成功。submitted/applied 不替代逐项持久化回读;整体快照未变化且不是完整 no_op 时保持未确认,不盲目重试。",
|
|
172
|
+
"failureSemantics": "A rejected report save can still have changed the form: a failed setReportFilters was observed having already replaced the filter panel. addReportColumns / setReportFilters / registerReportPlugin failures say so; re-read before retrying. ReportSorts is a pre-write safety rejection with no request and no authoring-state invalidation.",
|
|
141
173
|
"columnChangeRoute": {
|
|
142
174
|
"add": "Adding columns to an existing report is addReportColumns. It is incremental, idempotent, and needs no recreate.",
|
|
143
|
-
"
|
|
175
|
+
"remove": "removeReportColumn deletes one exact leaf on a backend with ReportColumns.remove. If unavailable, stop and report the release boundary; a recreate or hide needs separate user authorization. Groups cannot be deleted by this route.",
|
|
176
|
+
"recreate": "Changes outside the single-leaf removal and append routes need an explicitly authorized planReportRecreate when no supported property route applies: group removal, reordering or changing fieldType / dateIndex, and repairing duplicate entity field keys.",
|
|
144
177
|
"plan": "planReportRecreate --app-root <appRoot> --formNumber <exact> [--target-artifact <path>] is read-only apart from the backup it writes to {appRoot}/_runtime/<formNumber>.pre-recreate.json. It returns the current state, the column diff, every menu entry bound to the report, lostOnRecreate, and the ordered command sequence.",
|
|
145
178
|
"execute": "The plan's own steps, as published commands: deleteForm (critical, two-step confirmation) then run with the intended artifact under the same formNumber, then re-point the listed menu entries. planReportRecreate performs no platform write itself.",
|
|
146
179
|
"irreversible": "The recreated form gets a new formId, so menu bindings, designer-side adjustments and form-level permissions attached to the old formId do not survive."
|
|
@@ -151,7 +184,7 @@
|
|
|
151
184
|
"repair": "A field already added this way is a report-entity field without a column. addReportColumns reuses it by fieldKey and gives it a real column, which is also what turns the stray control into the intended one. removeField is only for dropping it instead."
|
|
152
185
|
},
|
|
153
186
|
"rollback": {
|
|
154
|
-
"existingReport": "
|
|
187
|
+
"existingReport": "Replaceable sections may be restored from confirmed prior state. An appended leaf can be removed with removeReportColumn only on a patched backend, with fresh target/read-back verification and explicit deletion scope. This is not an automatic rollback: old backends stop safely, groups are unsupported, and prior entity ids, dependent Java and other side effects are not recreated by re-appending.",
|
|
155
188
|
"disposableReport": "delete only the exact formId returned by createPage, then prove the exact formNumber absent"
|
|
156
189
|
}
|
|
157
190
|
}
|
|
@@ -198,7 +198,7 @@
|
|
|
198
198
|
"effect": "write",
|
|
199
199
|
"multiMapping": "多组字段带出用 --input {mappings:[{source,target,可选 onlyEmptyTarget}],可选 conditionField/operator/value/ruleId/branch},或 ensureRuleSet 的 args.mappings;一条 SetEqutionAction 包含全部映射。省略 branch 表示条件成立时执行;branch=false 只在条件不成立时执行;不成立时不执行服务就省略 branch。合并已有拆分规则必须传准确 ruleId。",
|
|
200
200
|
"semantics": "resolve one entity, condition, source and distinct target; compile SetEqutionAction Expression/FieldName as one TakeValueId mapping pair with optional only-empty behavior, validate entity scope, deduplicate semantically and independently read back the nested mapping",
|
|
201
|
-
"arguments": ["formNumber OR form-name", "optional entity", "condition-field", "operator", "value", "source", "target", "optional only-empty-target"],
|
|
201
|
+
"arguments": ["formNumber OR form-name", "optional entity", "condition-field", "operator (empty/not-empty take no value)", "value for other operators", "source", "target", "optional only-empty-target"],
|
|
202
202
|
"failClosed": [
|
|
203
203
|
"source and target must be distinct fields owned by the selected entity",
|
|
204
204
|
"callers never assemble TakeValueId, FieldName, RET, itemId or mountPoint",
|