@shundoo-ai/dsh-cosmic 1.0.19 → 1.0.21

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.
Files changed (245) hide show
  1. package/cordis.patch.yml +5 -5
  2. package/package.json +1 -1
  3. package/skills/cosmic-build/CHANGELOG.md +4 -0
  4. package/skills/cosmic-build/LOCAL-PATCHES.md +91 -40
  5. package/skills/cosmic-build/SKILL.md +46 -13
  6. package/skills/cosmic-build/assets/platform-report-filters/TypedReportFilterFields.java +169 -0
  7. package/skills/cosmic-build/assets/platform-report-filters/platform.patch +98 -0
  8. package/skills/cosmic-build/assets/report-algo-benchmark/ReportAlgoBenchmark.java +48 -0
  9. package/skills/cosmic-build/assets/report-algo-benchmark/ReportBenchmarkLogFactory.java +7 -0
  10. package/skills/cosmic-build/examples/java-rules/CosmicEntryQuery.java +26 -0
  11. package/skills/cosmic-build/examples/java-rules/CosmicReportAggregation.java +35 -0
  12. package/skills/cosmic-build/examples/java-rules/CosmicReportFilters.java +77 -0
  13. package/skills/cosmic-build/examples/java-rules/CosmicTimeAdapter.java +25 -0
  14. package/skills/cosmic-build/examples/java-rules/CosmicTimeAdapterCheck.java +26 -0
  15. package/skills/cosmic-build/knowledge/cosmic/platform/api/algo/GroupbyDataSet.md +47 -0
  16. package/skills/cosmic-build/knowledge/cosmic/platform/api/entity/report/FilterInfo.md +12 -12
  17. package/skills/cosmic-build/knowledge/cosmic/platform/api/entity/report/FilterItemInfo.md +2 -0
  18. package/skills/cosmic-build/knowledge/cosmic/platform/form-plugin.md +2 -4
  19. package/skills/cosmic-build/knowledge/cosmic/platform/java-type-contracts.md +11 -2
  20. package/skills/cosmic-build/knowledge/cosmic/platform/list-plugin.md +18 -14
  21. package/skills/cosmic-build/knowledge/cosmic/platform/operation-plugin.md +4 -4
  22. package/skills/cosmic-build/knowledge/cosmic/platform/reportlistdata-plugin.md +17 -9
  23. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/operation/cross-entity-aggregate-writeback.md +2 -0
  24. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/operation/pre-post-operation.md +15 -7
  25. package/skills/cosmic-build/knowledge/cosmic/platform/sdk/sdk-algo.md +24 -68
  26. package/skills/cosmic-build/knowledge/cosmic/platform/workflow-plugin.md +2 -7
  27. package/skills/cosmic-build/references/application-context.md +35 -1
  28. package/skills/cosmic-build/references/common-reference.md +1 -1
  29. package/skills/cosmic-build/references/compile-guide.md +55 -68
  30. package/skills/cosmic-build/references/delivery.md +59 -7
  31. package/skills/cosmic-build/references/execution-contract.md +38 -0
  32. package/skills/cosmic-build/references/form-discovery.md +7 -1
  33. package/skills/cosmic-build/references/java-plugin-delivery.md +32 -162
  34. package/skills/cosmic-build/references/operation-dispatch.md +17 -0
  35. package/skills/cosmic-build/references/plugin-handoff-input.md +131 -0
  36. package/skills/cosmic-build/references/plugin-runtime-routing.md +1 -1
  37. package/skills/cosmic-build/references/report-filter-contract.md +78 -0
  38. package/skills/cosmic-build/references/sdk-inspection.md +23 -0
  39. package/skills/cosmic-build/schemas/data-model-authoring.schema.json +2 -3
  40. package/skills/cosmic-build/scripts/_http_client.py +55 -55
  41. package/skills/cosmic-build/scripts/appconfig_load.py +2 -2
  42. package/skills/cosmic-build/scripts/apply_report_filter_extension.py +39 -0
  43. package/skills/cosmic-build/scripts/bootstrap_cosmic_context.py +37 -2
  44. package/skills/cosmic-build/scripts/compile_plugin_source.py +279 -156
  45. package/skills/cosmic-build/scripts/execution_binding.py +40 -0
  46. package/skills/cosmic-build/scripts/inspect_platform_sdk.py +169 -0
  47. package/skills/cosmic-build/scripts/kd_api.py +23 -22
  48. package/skills/cosmic-build/scripts/kd_auth.py +1 -1
  49. package/skills/cosmic-build/scripts/kd_env_loader.py +19 -10
  50. package/skills/cosmic-build/scripts/kd_token_cache.py +2 -2
  51. package/skills/cosmic-build/scripts/kingscript_sdk_sync.py +350 -0
  52. package/skills/cosmic-build/scripts/kingscript_source_gate.py +11 -8
  53. package/skills/cosmic-build/scripts/platform_api_contract.py +246 -11
  54. package/skills/cosmic-build/scripts/plugin_corpus/build_index.py +403 -0
  55. package/skills/cosmic-build/scripts/plugin_corpus/jvm_classfile.py +281 -0
  56. package/skills/cosmic-build/scripts/plugin_corpus/measure_api_usage.py +446 -0
  57. package/skills/cosmic-build/scripts/plugin_source_gate.py +48 -7
  58. package/skills/cosmic-build/scripts/plugin_target_sdk.py +45 -0
  59. package/skills/cosmic-build/scripts/prepare_plugin_handoff.py +493 -47
  60. package/skills/cosmic-build/scripts/preview_url.py +35 -6
  61. package/skills/cosmic-build/scripts/query_plugin_handoff.py +250 -0
  62. package/skills/cosmic-build/scripts/register_plugins.py +28 -2
  63. package/skills/cosmic-build/scripts/render_output_cards.py +42 -9
  64. package/skills/cosmic-build/scripts/report_algo_benchmark.py +46 -0
  65. package/skills/cosmic-build/scripts/run.py +2 -2
  66. package/skills/cosmic-build/scripts/sync_generated_locale_assets.py +43 -0
  67. package/skills/cosmic-build/scripts/test/fixtures/ReportSdkCheck.java +64 -0
  68. package/skills/cosmic-build/scripts/test/fixtures/java_context_booking_native.json +541 -0
  69. package/skills/cosmic-build/scripts/test/test_add_rule_business_contract.py +85 -14
  70. package/skills/cosmic-build/scripts/test/test_batch5_drift_governance.py +12 -3
  71. package/skills/cosmic-build/scripts/test/test_build_meta_order.py +0 -16
  72. package/skills/cosmic-build/scripts/test/test_cli_continuation.py +87 -0
  73. package/skills/cosmic-build/scripts/test/test_compile_plugin_source.py +224 -3
  74. package/skills/cosmic-build/scripts/test/test_control_authoring.py +123 -4
  75. package/skills/cosmic-build/scripts/test/test_cosmic_sdk_examples.py +141 -0
  76. package/skills/cosmic-build/scripts/test/test_data_model_contract.py +30 -5
  77. package/skills/cosmic-build/scripts/test/test_delivery_stage_boundary.py +2 -10
  78. package/skills/cosmic-build/scripts/test/test_dym_sync_background.py +11 -0
  79. package/skills/cosmic-build/scripts/test/test_dynamic_form_metadata_only_contract.py +59 -1
  80. package/skills/cosmic-build/scripts/test/test_guide_skeleton_contract.py +87 -43
  81. package/skills/cosmic-build/scripts/test/test_inspect_platform_sdk.py +71 -0
  82. package/skills/cosmic-build/scripts/test/test_kd_api.py +67 -19
  83. package/skills/cosmic-build/scripts/test/test_kingscript_sdk_sync.py +221 -0
  84. package/skills/cosmic-build/scripts/test/test_kingscript_source_gate.py +10 -0
  85. package/skills/cosmic-build/scripts/test/test_list_configuration_closure.py +18 -0
  86. package/skills/cosmic-build/scripts/test/test_long_session_regressions.py +116 -0
  87. package/skills/cosmic-build/scripts/test/test_mcp_cli.py +524 -0
  88. package/skills/cosmic-build/scripts/test/test_modeling_input_recovery.py +141 -0
  89. package/skills/cosmic-build/scripts/test/test_open_world_cli_catalog.py +36 -2
  90. package/skills/cosmic-build/scripts/test/test_platform_api_contract.py +161 -1
  91. package/skills/cosmic-build/scripts/test/test_plugin_business_contract.py +91 -0
  92. package/skills/cosmic-build/scripts/test/test_plugin_corpus.py +195 -0
  93. package/skills/cosmic-build/scripts/test/test_plugin_handoff.py +346 -1
  94. package/skills/cosmic-build/scripts/test/test_plugin_target_sdk.py +70 -0
  95. package/skills/cosmic-build/scripts/test/test_preview_url.py +44 -1
  96. package/skills/cosmic-build/scripts/test/test_property_batch.py +22 -0
  97. package/skills/cosmic-build/scripts/test/test_property_input_discovery.py +13 -11
  98. package/skills/cosmic-build/scripts/test/test_query_plugin_handoff.py +160 -0
  99. package/skills/cosmic-build/scripts/test/test_register_plugins.py +31 -1
  100. package/skills/cosmic-build/scripts/test/test_render_output_cards.py +50 -4
  101. package/skills/cosmic-build/scripts/test/test_report_business_contract.py +27 -0
  102. package/skills/cosmic-build/scripts/test/test_report_doc_contract_sync.py +5 -4
  103. package/skills/cosmic-build/scripts/test/test_report_sdk_optimization.py +36 -0
  104. package/skills/cosmic-build/scripts/test/test_rule_batch_evidence.py +1 -1
  105. package/skills/cosmic-build/scripts/test/test_rule_domain_contract.py +20 -0
  106. package/skills/cosmic-build/scripts/test/test_rule_function_formula.py +16 -6
  107. package/skills/cosmic-build/scripts/test/test_skill_entry_contract.py +1 -0
  108. package/skills/cosmic-build/scripts/test/test_test_environment.py +45 -0
  109. package/skills/cosmic-build/scripts/test/test_verify_plugin_loading.py +79 -0
  110. package/skills/cosmic-build/scripts/test_environment.py +49 -0
  111. package/skills/cosmic-build/scripts/verify_plugin_loading.py +71 -0
  112. package/skills/cosmic-build/skills/cosmic-coding-impl/SKILL.md +86 -162
  113. package/skills/cosmic-build/skills/cosmic-coding-script/ATTRIBUTION.md +1 -1
  114. package/skills/cosmic-build/skills/cosmic-coding-script/SKILL.md +45 -14
  115. package/skills/cosmic-build/skills/cosmic-coding-script/references/kwc-controller-authoring.md +6 -0
  116. package/skills/cosmic-build/skills/cosmic-coding-script/references/sdk/README.md +29 -19
  117. package/skills/cosmic-build/skills/cosmic-coding-script/references/sdk/docs//350/204/232/346/234/254SDK.md +3 -4
  118. package/skills/cosmic-build/skills/cosmic-coding-script/references/sdk/indexes/package-index.md +1 -1
  119. package/skills/cosmic-build/skills/cosmic-coding-script/references/sdk/strategy.md +27 -18
  120. package/skills/cosmic-build/skills/cosmic-meta-api/SKILL.md +11 -17
  121. package/skills/cosmic-build/skills/cosmic-meta-api/apis/ai_meta.md +1 -1
  122. package/skills/cosmic-build/skills/cosmic-meta-api/apis/app_menu.md +1 -1
  123. package/skills/cosmic-build/skills/cosmic-meta-api/apis/meta_log.md +1 -1
  124. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/control-authoring.json +45 -4
  125. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/form-control-traits.json +1 -1
  126. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/layout-ui.json +18 -0
  127. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/menu-publish.json +8 -2
  128. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/property-batch.json +7 -0
  129. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/property-input-index.json +3 -3
  130. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/report-write-contracts.json +1 -0
  131. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/rule-domain.json +37 -5
  132. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/rule-event-contract.json +24 -3
  133. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/run-build-meta.json +61 -12
  134. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/schema/dropdown-menu.schema.json +69 -0
  135. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/schema/report-write.schema.json +2 -1
  136. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/schema/rule-set.schema.json +1 -0
  137. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/schema/rule-write.schema.json +14 -7
  138. package/skills/cosmic-build/skills/cosmic-meta-api/docs/api-summary.md +2 -2
  139. package/skills/cosmic-build/skills/cosmic-meta-api/modules/app-action/SKILL.md +6 -3
  140. package/skills/cosmic-build/skills/cosmic-meta-api/modules/develop/SKILL.md +1 -2
  141. package/skills/cosmic-build/skills/cosmic-meta-api/modules/export/SKILL.md +5 -6
  142. package/skills/cosmic-build/skills/cosmic-meta-api/modules/init/SKILL.md +13 -36
  143. package/skills/cosmic-build/skills/cosmic-meta-api/modules/pdm/SKILL.md +8 -9
  144. package/skills/cosmic-build/skills/cosmic-meta-api/modules/pdm/scripts/config.json.example +5 -15
  145. package/skills/cosmic-build/skills/cosmic-meta-api/modules/reset/SKILL.md +6 -6
  146. package/skills/cosmic-build/skills/cosmic-meta-api/modules/usage/SKILL.md +20 -148
  147. package/skills/cosmic-build/skills/cosmic-meta-api/modules/verify/SKILL.md +1 -1
  148. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/build_receipts.py +171 -0
  149. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_apps.py +25 -6
  150. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_controls.py +55 -21
  151. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_fields.py +10 -3
  152. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_formactions.py +63 -6
  153. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_forms.py +59 -2
  154. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_listui.py +8 -0
  155. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_metaops.py +36 -1
  156. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_plugins.py +115 -90
  157. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_properties.py +666 -4
  158. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_rules.py +1415 -87
  159. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/commands.py +6 -1
  160. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/config.py +28 -15
  161. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/execution_errors.py +40 -0
  162. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/formatters.py +32 -5
  163. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/property_policy_contract.py +6 -0
  164. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/report_ops.py +113 -17
  165. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/reset.py +3 -3
  166. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/rule_contract.py +107 -26
  167. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/build_meta_order.py +50 -49
  168. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/cosmic_api.py +856 -150
  169. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/field_decorator.py +47 -14
  170. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_build_receipt_resume.py +374 -0
  171. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_cli_failure_contract.py +24 -0
  172. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_creation_identity_boundary.py +4 -2
  173. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_dropdown_menu_contract.py +382 -0
  174. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_existing_form_ui.py +0 -25
  175. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_field_decorator.py +50 -6
  176. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_functional_group_resolution.py +83 -16
  177. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_identity_template_resolution.py +103 -0
  178. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_plugin_contract.py +28 -2
  179. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_readback_derivation.py +22 -0
  180. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_typed_filters.py +65 -0
  181. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_rule_write_closure.py +1041 -15
  182. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_run_batch_post_build_recovery.py +65 -0
  183. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_run_batch_verification_evidence.py +17 -0
  184. package/skills/cosmic-build/skills/cosmic-meta-knowledge/generation/api-param-checklist.md +1 -1
  185. package/skills/cosmic-build/skills/cosmic-meta-knowledge/generation/rule-modeling-guide.md +7 -4
  186. package/skills/cosmic-build/skills/cosmic-meta-modeling/SKILL.md +61 -13
  187. package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/app-menu/SKILL.md +8 -0
  188. package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/formtemplate/SKILL.md +5 -0
  189. package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/formtemplate/dynamic-form-artifact-schema.md +5 -0
  190. package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/report/SKILL.md +21 -9
  191. package/skills/cosmic-build/skills/cosmic-meta-modeling/references/authoring-entity-graphs.md +1 -1
  192. package/skills/cosmic-build/skills/cosmic-meta-modeling/references/authoring-storage-constraints.md +5 -3
  193. package/skills/cosmic-build/skills/cosmic-meta-modeling/references/authoring-ui-composition.md +6 -3
  194. package/skills/cosmic-build/skills/cosmic-meta-modeling/references/creation-recovery.md +13 -0
  195. package/skills/cosmic-build/skills/cosmic-meta-modeling/references/parse-core.md +3 -0
  196. package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/data_model_contract.py +6 -1
  197. package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/data_model_emit.py +135 -87
  198. package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/field_type_registry.py +11 -1
  199. package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/isv_resolver.py +1 -3
  200. package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/report_validator.py +29 -0
  201. package/skills/cosmic-build/skills/cosmic-meta-modifier/SKILL.md +4 -2
  202. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/context/SKILL.md +2 -2
  203. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/execute/SKILL.md +1 -1
  204. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/operation/SKILL.md +5 -4
  205. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/operation/operation-write.md +6 -2
  206. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/operation/validation.md +2 -0
  207. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/property/SKILL.md +20 -5
  208. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/property/complex-apis.md +1 -1
  209. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/property/policy-grammar.md +4 -1
  210. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/report-op/SKILL.md +9 -0
  211. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/SKILL.md +61 -4
  212. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/raw-rule-authoring.md +3 -1
  213. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/rule-actions.md +2 -2
  214. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/rule-reference.md +6 -6
  215. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/state.md +1 -1
  216. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/unit/SKILL.md +6 -3
  217. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/verify/usage-guide.md +4 -4
  218. package/skills/cosmic-build/skills/cosmic-meta-modifier/references/list-filter-modification.md +4 -1
  219. package/skills/cosmic-build/skills/cosmic-meta-ui/SKILL.md +9 -1
  220. package/skills/cosmic-build/skills/cosmic-meta-ui/modules/button/SKILL.md +3 -0
  221. package/skills/cosmic-build/skills/mcp-server/SKILL.md +210 -0
  222. package/skills/cosmic-build/skills/mcp-server/scripts/mcp_cli.py +431 -0
  223. package/skills/cosmic-build/skills/openapi-gen/SKILL.md +3 -3
  224. package/skills/cosmic-build/skills/openapi-gen/scripts/api/gen_runner.py +2 -2
  225. package/skills/cosmic-dev-best-practices/references/bill-dev-convention.md +19 -9
  226. package/skills/cosmic-dev-best-practices/references/cosmic-meta-api.md +14 -11
  227. package/skills/cosmic-dev-best-practices/references/design-examples/basedata-design-example.md +1 -1
  228. package/skills/cosmic-dev-best-practices/references/design-examples/bill-design-example.md +1 -1
  229. package/skills/cosmic-dev-best-practices/references/elements/AssistantField.md +1 -1
  230. package/skills/cosmic-dev-best-practices/references/elements/BasedataField.md +2 -2
  231. package/skills/cosmic-dev-best-practices/references/elements/BasedataPropField.md +3 -3
  232. package/skills/cosmic-dev-best-practices/references/elements/CreaterField.md +2 -2
  233. package/skills/cosmic-dev-best-practices/references/elements/CurrencyField.md +2 -2
  234. package/skills/cosmic-dev-best-practices/references/elements/CustomerField.md +2 -2
  235. package/skills/cosmic-dev-best-practices/references/elements/FlexField.md +1 -1
  236. package/skills/cosmic-dev-best-practices/references/elements/MaterielField.md +2 -2
  237. package/skills/cosmic-dev-best-practices/references/elements/OrgField.md +2 -2
  238. package/skills/cosmic-dev-best-practices/references/elements/SupplierField.md +2 -2
  239. package/skills/cosmic-dev-best-practices/references/elements/UnitField.md +1 -1
  240. package/skills/cosmic-dev-best-practices/references/elements/UserField.md +3 -3
  241. package/skills/cosmic-dev-best-practices/references/elements/common-field-property-conventions.md +16 -11
  242. package/skills/cosmic-dev-best-practices/references/layout-control-convention.md +5 -4
  243. package/skills/erp-analysis/references/delivery-contract.md +4 -2
  244. package/skills/erp-analysis/templates/AGENTS.template.md +1 -1
  245. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/.dsh/cosmic/.kd/config.json +0 -21
@@ -360,7 +360,35 @@ def _add_receipt(unit_id: str = "ABCDEF1234", number: str | None = None) -> dict
360
360
  }
361
361
 
362
362
 
363
- def test_add_unit_name_only_omits_defaulted_fields_and_verifies_root() -> None:
363
+ def test_add_unit_missing_code_stops_before_platform_write():
364
+ with mock.patch.object(cmd_apps, "_api_call") as write:
365
+ failure = cmd_apps.do_add_unit({}, "token", biz_app_id="APP-1", name="Group A")
366
+ assert failure["errorCode"] == "FUNCTIONAL_GROUP_CODE_REQUIRED"
367
+ assert failure["writeAttempted"] is False
368
+ write.assert_not_called()
369
+
370
+
371
+ def test_add_unit_binds_app_root_and_validates_explicit_application(tmp_path):
372
+ runtime = tmp_path / "_runtime/cosmic-metadata"
373
+ runtime.mkdir(parents=True)
374
+ (tmp_path / "archive.json").write_text(json.dumps({"appCode": "uhyl_zlgl", "appId": "APP-1"}), encoding="utf-8")
375
+ (runtime / "workspace.json").write_text(json.dumps({"appNumber": "uhyl_zlgl", "bizAppId": "APP-1"}), encoding="utf-8")
376
+ assert cosmic_api._unit_app_target(str(tmp_path)) == ("APP-1", None)
377
+ with pytest.raises(RuntimeError, match="APP_TARGET_MISMATCH"):
378
+ cosmic_api._unit_app_target(str(tmp_path), "OTHER-APP")
379
+ receipt = _add_receipt("UNIT-1", "quality_master_data")
380
+ readback = _units_envelope([{"id": "UNIT-1", "number": "quality_master_data", "name": "基础数据"}])
381
+ with mock.patch.object(cmd_apps, "_api_call", return_value=receipt) as write, \
382
+ mock.patch.object(cmd_apps, "do_list_units", return_value=readback):
383
+ result = cosmic_api._dispatch_serve({}, "token", "addUnit", {
384
+ "appRoot": str(tmp_path), "name": "基础数据", "number": "quality_master_data", "seq": 2,
385
+ })
386
+ assert result["status"] is True
387
+ assert write.call_args.args[3] == {"bizAppId": "APP-1", "name": "基础数据",
388
+ "number": "quality_master_data", "description": "基础数据", "seq": 2}
389
+
390
+
391
+ def test_add_unit_explicit_code_defaults_description_and_verifies_root() -> None:
364
392
  receipt = _add_receipt()
365
393
  readback = {
366
394
  "status": True,
@@ -380,12 +408,12 @@ def test_add_unit_name_only_omits_defaulted_fields_and_verifies_root() -> None:
380
408
  mock.patch.object(cmd_apps, "do_list_units", return_value=readback) as list_units,
381
409
  ):
382
410
  result = cmd_apps.do_add_unit(
383
- {}, "token", biz_app_id="APP-1", name="Group A"
411
+ {}, "token", biz_app_id="APP-1", name="Group A", number="lingee_abcdef1234", seq=2
384
412
  )
385
413
 
386
414
  assert api_call.call_args.args[2:] == (
387
415
  "app/unit/add",
388
- {"bizAppId": "APP-1", "name": "Group A"},
416
+ {"bizAppId": "APP-1", "name": "Group A", "number": "lingee_abcdef1234", "description": "Group A", "seq": 2},
389
417
  )
390
418
  assert api_call.call_args.kwargs == {"method": "POST"}
391
419
  list_units.assert_called_once_with(
@@ -399,6 +427,45 @@ def test_add_unit_name_only_omits_defaulted_fields_and_verifies_root() -> None:
399
427
  }
400
428
 
401
429
 
430
+ def test_add_unit_without_seq_appends_after_verified_existing_groups() -> None:
431
+ before = _units_envelope([{"id": "HOME", "name": "首页", "number": "MainPage", "seq": 1}])
432
+ after = _units_envelope([
433
+ {"id": "HOME", "name": "首页", "number": "MainPage", "seq": 1},
434
+ {"id": "ABCDEF1234", "name": "Group A", "number": "lingee_abcdef1234", "seq": 2},
435
+ ])
436
+ with (
437
+ mock.patch.object(cmd_apps, "_api_call", return_value=_add_receipt()) as write,
438
+ mock.patch.object(cmd_apps, "do_list_units", side_effect=[before, after]) as list_units,
439
+ ):
440
+ result = cmd_apps.do_add_unit({}, "token", biz_app_id="APP-1", name="Group A", number="lingee_abcdef1234")
441
+ assert result["status"] is True
442
+ assert write.call_args.args[3]["seq"] == 2
443
+ assert list_units.call_count == 2
444
+
445
+
446
+ def test_add_unit_without_verified_sequence_stops_before_write() -> None:
447
+ with (
448
+ mock.patch.object(cmd_apps, "_api_call") as write,
449
+ mock.patch.object(cmd_apps, "do_list_units", return_value=_units_envelope([{"id": "HOME", "name": "首页"}])),
450
+ ):
451
+ result = cmd_apps.do_add_unit({}, "token", biz_app_id="APP-1", name="Group A", number="lingee_abcdef1234")
452
+ assert result["errorCode"] == "FUNCTIONAL_GROUP_SEQ_UNAVAILABLE"
453
+ assert result["writeAttempted"] is False
454
+ write.assert_not_called()
455
+
456
+
457
+ def test_nested_add_unit_requires_a_sibling_sequence_before_write() -> None:
458
+ with (
459
+ mock.patch.object(cmd_apps, "_api_call") as write,
460
+ mock.patch.object(cmd_apps, "do_list_units") as list_units,
461
+ ):
462
+ result = cmd_apps.do_add_unit({}, "token", biz_app_id="APP-1", name="Child", number="child", parent_unit_id="PARENT")
463
+ assert result["errorCode"] == "FUNCTIONAL_GROUP_SEQ_UNAVAILABLE"
464
+ assert result["writeAttempted"] is False
465
+ write.assert_not_called()
466
+ list_units.assert_not_called()
467
+
468
+
402
469
  def test_add_unit_full_payload_is_preserved_and_verified() -> None:
403
470
  receipt = _add_receipt("UNIT-1", "group_a")
404
471
  readback = {
@@ -444,7 +511,7 @@ def test_add_unit_write_failure_does_not_read_back() -> None:
444
511
  mock.patch.object(cmd_apps, "do_list_units") as list_units,
445
512
  ):
446
513
  result = cmd_apps.do_add_unit(
447
- {}, "token", biz_app_id="APP-1", name="Group A"
514
+ {}, "token", biz_app_id="APP-1", name="Group A", number="lingee_abcdef1234", seq=2
448
515
  )
449
516
 
450
517
  assert result is failed
@@ -467,7 +534,7 @@ def test_add_unit_readback_failure_is_unknown_after_write(
467
534
  mock.patch.object(cmd_apps, "do_list_units", return_value=readback),
468
535
  ):
469
536
  result = cmd_apps.do_add_unit(
470
- {}, "token", biz_app_id="APP-1", name="Group A"
537
+ {}, "token", biz_app_id="APP-1", name="Group A", number="lingee_abcdef1234", seq=2
471
538
  )
472
539
 
473
540
  assert result["status"] is False
@@ -505,7 +572,7 @@ def test_add_unit_nested_child_is_not_accepted_as_first_level_readback() -> None
505
572
  mock.patch.object(cmd_apps, "do_list_units", return_value=readback),
506
573
  ):
507
574
  result = cmd_apps.do_add_unit(
508
- {}, "token", biz_app_id="APP-1", name="Group A"
575
+ {}, "token", biz_app_id="APP-1", name="Group A", number="lingee_abcdef1234", seq=2
509
576
  )
510
577
 
511
578
  assert result["errorCode"] == "ADD_UNIT_READBACK_MISMATCH"
@@ -528,7 +595,7 @@ def test_add_unit_duplicate_or_different_id_readback_fails_closed() -> None:
528
595
  mock.patch.object(cmd_apps, "do_list_units", return_value=duplicate),
529
596
  ):
530
597
  result = cmd_apps.do_add_unit(
531
- {}, "token", biz_app_id="APP-1", name="Group A"
598
+ {}, "token", biz_app_id="APP-1", name="Group A", number="lingee_abcdef1234", seq=2
532
599
  )
533
600
  assert result["data"]["verification"]["reason"] == "candidate_count_mismatch"
534
601
 
@@ -546,7 +613,7 @@ def test_add_unit_duplicate_or_different_id_readback_fails_closed() -> None:
546
613
  mock.patch.object(cmd_apps, "do_list_units", return_value=different),
547
614
  ):
548
615
  result = cmd_apps.do_add_unit(
549
- {}, "token", biz_app_id="APP-1", name="Group A"
616
+ {}, "token", biz_app_id="APP-1", name="Group A", number="lingee_abcdef1234", seq=2
550
617
  )
551
618
  assert result["data"]["verification"]["reason"] == "unit_id_mismatch"
552
619
  assert "unitId" not in result["data"]
@@ -560,7 +627,7 @@ def test_add_unit_readback_exception_fails_closed() -> None:
560
627
  ),
561
628
  ):
562
629
  result = cmd_apps.do_add_unit(
563
- {}, "token", biz_app_id="APP-1", name="Group A"
630
+ {}, "token", biz_app_id="APP-1", name="Group A", number="lingee_abcdef1234", seq=2
564
631
  )
565
632
 
566
633
  assert result["errorCode"] == "ADD_UNIT_READBACK_FAILED"
@@ -574,7 +641,7 @@ def test_add_unit_invalid_default_receipt_fails_before_readback() -> None:
574
641
  mock.patch.object(cmd_apps, "do_list_units") as list_units,
575
642
  ):
576
643
  result = cmd_apps.do_add_unit(
577
- {}, "token", biz_app_id="APP-1", name="Group A"
644
+ {}, "token", biz_app_id="APP-1", name="Group A", number="lingee_abcdef1234", seq=2
578
645
  )
579
646
 
580
647
  assert result["errorCode"] == "ADD_UNIT_READBACK_MISMATCH"
@@ -582,11 +649,11 @@ def test_add_unit_invalid_default_receipt_fails_before_readback() -> None:
582
649
  list_units.assert_not_called()
583
650
 
584
651
 
585
- def test_add_unit_cli_accepts_name_only(monkeypatch: pytest.MonkeyPatch) -> None:
652
+ def test_add_unit_cli_accepts_explicit_code(monkeypatch: pytest.MonkeyPatch) -> None:
586
653
  monkeypatch.setattr(
587
654
  sys,
588
655
  "argv",
589
- ["cosmic_api.py", "addUnit", "--bizAppId", "APP-1", "--name", "Group A"],
656
+ ["cosmic_api.py", "addUnit", "--bizAppId", "APP-1", "--name", "Group A", "--number", "group_a"],
590
657
  )
591
658
  with (
592
659
  mock.patch.object(cosmic_api, "load_config", return_value={}),
@@ -604,7 +671,7 @@ def test_add_unit_cli_accepts_name_only(monkeypatch: pytest.MonkeyPatch) -> None
604
671
  biz_app_id="APP-1",
605
672
  biz_app_number=None,
606
673
  name="Group A",
607
- number=None,
674
+ number="group_a",
608
675
  description=None,
609
676
  seq=None,
610
677
  parent_unit_id=None,
@@ -612,12 +679,12 @@ def test_add_unit_cli_accepts_name_only(monkeypatch: pytest.MonkeyPatch) -> None
612
679
  )
613
680
 
614
681
 
615
- def test_serve_add_unit_name_only_uses_shared_implementation() -> None:
682
+ def test_serve_add_unit_uses_shared_implementation() -> None:
616
683
  with mock.patch.object(
617
684
  cosmic_api, "do_add_unit", return_value={"status": True}
618
685
  ) as add_unit:
619
686
  result = cosmic_api._dispatch_serve(
620
- {}, "token", "addUnit", {"bizAppId": "APP-1", "name": "Group A"}
687
+ {}, "token", "addUnit", {"bizAppId": "APP-1", "name": "Group A", "number": "group_a"}
621
688
  )
622
689
 
623
690
  assert result == {"status": True}
@@ -627,7 +694,7 @@ def test_serve_add_unit_name_only_uses_shared_implementation() -> None:
627
694
  biz_app_id="APP-1",
628
695
  biz_app_number=None,
629
696
  name="Group A",
630
- number=None,
697
+ number="group_a",
631
698
  description=None,
632
699
  seq=None,
633
700
  parent_unit_id=None,
@@ -0,0 +1,103 @@
1
+ import sys
2
+ from pathlib import Path
3
+ import pytest
4
+ from types import SimpleNamespace
5
+
6
+ sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
7
+ from _core import cmd_forms, cmd_properties
8
+ import cosmic_api
9
+
10
+
11
+ def test_key_rename_uses_single_setter_and_new_key_readback(monkeypatch):
12
+ calls = []
13
+ monkeypatch.setattr(cmd_properties, "do_get_property_schema", lambda *args:
14
+ {"status": True, "data": [{"propertyName": "Key", "editable": True,
15
+ "complexity": "simple", "editorType": "text"}]})
16
+ def write(cfg, token, form, key, properties):
17
+ calls.append(("write", key, properties))
18
+ return {"status": True, "data": {}}
19
+ def read(cfg, token, form, key, names):
20
+ calls.append(("read", key, names))
21
+ return {"status": True, "data": {"Key": key}}
22
+ monkeypatch.setattr(cmd_properties, "do_set_properties", write)
23
+ monkeypatch.setattr(cmd_properties, "do_get_properties", read)
24
+ result = cmd_properties.do_set_properties_verified({}, "", "form", "acme_old", {"Key": "acme_new"})
25
+ assert result["verification"]["verified"]
26
+ assert calls == [("write", "acme_old", {"Key": "acme_new"}), ("read", "acme_new", ["Key"])]
27
+
28
+
29
+ def test_cli_key_rename_applies_developer_prefix_once(monkeypatch, tmp_path):
30
+ monkeypatch.setattr(cosmic_api, "_load_workspace_at",
31
+ lambda root: {"isv": "uhyl", "standard": False})
32
+ args = SimpleNamespace(app_root=str(tmp_path))
33
+ assert cosmic_api._normalize_key_property(args, {"Key": "sample_description"})["Key"] == "uhyl_sample_description"
34
+ assert cosmic_api._normalize_key_property(args, {"Key": "uhyl_sample_description"})["Key"] == "uhyl_sample_description"
35
+
36
+
37
+ def test_rename_element_key_migrates_rules_and_preserves_layout(monkeypatch, tmp_path):
38
+ args = SimpleNamespace(formNumber="form", element="申请说明",
39
+ new_key="sample_description", app_root=str(tmp_path))
40
+ monkeypatch.setattr(cosmic_api, "_load_workspace_at",
41
+ lambda root: {"isv": "uhyl", "standard": False})
42
+ monkeypatch.setattr(cosmic_api, "resolve_element_selector",
43
+ lambda *args: "uhyl_apply_note")
44
+ before = {"key": "uhyl_apply_note", "name": "申请说明", "type": "TextField",
45
+ "parentKey": "head", "index": 4}
46
+ after = {**before, "key": "uhyl_sample_description"}
47
+ element_reads = iter([
48
+ {"status": True, "data": [before]},
49
+ {"status": True, "data": [after]},
50
+ ])
51
+ monkeypatch.setattr(cosmic_api, "do_list_elements", lambda *args: next(element_reads))
52
+ rule_before = {"ruleId": "r1", "ruleType": "formRule",
53
+ "preCondition": "uhyl_apply_note = '加急'",
54
+ "triggerField": "申请说明(uhyl_apply_note)"}
55
+ rule_after = {**rule_before, "preCondition": "uhyl_sample_description = '加急'",
56
+ "triggerField": "申请说明(uhyl_sample_description)"}
57
+ rule_reads = iter([
58
+ {"status": True, "data": {"rules": [rule_before]}},
59
+ {"status": True, "data": {"rules": [rule_after]}},
60
+ ])
61
+ monkeypatch.setattr(cosmic_api, "do_list_rules", lambda *args: next(rule_reads))
62
+ writes = []
63
+ monkeypatch.setattr(cosmic_api, "do_set_properties_verified",
64
+ lambda cfg, token, form, key, props:
65
+ writes.append((key, props)) or
66
+ {"status": True, "verification": {"verified": True}})
67
+ patches = []
68
+ monkeypatch.setattr(cosmic_api, "do_update_rule_verified",
69
+ lambda cfg, token, form, patch:
70
+ patches.append(patch) or
71
+ {"status": True, "verification": {"verified": True}})
72
+ result = cosmic_api._rename_element_key({}, "", args)
73
+ assert result["verification"] == {
74
+ "verified": True, "preservationVerified": True,
75
+ "oldKeyAbsent": True, "newKeyPresent": True, "staleRuleIds": [],
76
+ }
77
+ assert writes == [("uhyl_apply_note", {"Key": "uhyl_sample_description"})]
78
+ assert patches == [{"ruleType": "formRule", "ruleId": "r1",
79
+ "preCondition": "uhyl_sample_description = '加急'",
80
+ "triggerField": "申请说明(uhyl_sample_description)"}]
81
+
82
+
83
+ def test_template_code_and_name_use_same_live_identity():
84
+ rows = [{"number": "vendor_tpl", "name": "单据带组织模板", "modelType": "BillFormModel"}]
85
+ for selector in ("vendor_tpl", "单据带组织模板", "单据带组织模版"):
86
+ assert cmd_forms.resolve_template_selector(selector, rows, "BillFormModel") == "vendor_tpl"
87
+ assert cmd_forms.resolve_template_selector("单据带组织模版",
88
+ [dict(rows[0], name="单据(带组织)")], "BillFormModel") == "vendor_tpl"
89
+ with pytest.raises(ValueError, match="TEMPLATE_NAME_AMBIGUOUS"):
90
+ cmd_forms.resolve_template_selector("单据带组织模板", rows + [
91
+ dict(rows[0], number="other_tpl")], "BillFormModel")
92
+ with pytest.raises(ValueError, match="TEMPLATE_NOT_FOUND"):
93
+ cmd_forms.resolve_template_selector("单据带组织模板", rows, "BaseFormModel")
94
+
95
+
96
+ def test_ascii_template_name_still_uses_live_directory(monkeypatch):
97
+ monkeypatch.setattr(cmd_forms, "do_get_template_list", lambda *args, **kwargs:
98
+ [{"number": "vendor_tpl", "name": "OrganizationTemplate",
99
+ "modelType": "BillFormModel"}])
100
+ assert cmd_forms.resolve_template_input({}, "", "OrganizationTemplate",
101
+ model_type="BillFormModel") == "vendor_tpl"
102
+ assert cmd_forms.resolve_template_input({}, "", "bos_billorgtpl",
103
+ model_type="BillFormModel") == "bos_billorgtpl"
@@ -211,7 +211,7 @@ class CompileReportPluginContractTest(unittest.TestCase):
211
211
  )
212
212
  requirements = {row["requirement"] for row in contract["filterEnforcement"]}
213
213
  self.assertEqual(requirements, {"compare", "mustInput", "defaultValue"})
214
- self.assertIn('getFilterItem("flt_date")', contract["filterEnforcement"][0]["java"])
214
+ self.assertIn('getFilterItems()', contract["filterEnforcement"][0]["java"])
215
215
 
216
216
  def test_every_filter_carries_its_comparison_as_a_requirement(self):
217
217
  """compare is not persisted at all, so query() is the only place it can be applied.
@@ -231,7 +231,33 @@ class CompileReportPluginContractTest(unittest.TestCase):
231
231
  if row["requirement"] == "compare"]
232
232
  self.assertEqual([(row["filterKey"], row["value"]) for row in compares],
233
233
  [("flt_date", "between"), ("flt_sup", "in")])
234
- self.assertIn('getFilterItem("flt_sup")', compares[1]["java"])
234
+ self.assertIn('getValue("flt_sup")', compares[1]["java"])
235
+
236
+ def test_filter_contract_keeps_explicit_source_mapping_without_claiming_runtime_proof(self):
237
+ contract = report_ops.compile_report_plugin_contract({
238
+ "reportFilters": [{"fieldPath": "flt_sourcearea", "compare": "in"},
239
+ {"fieldPath": "flt_date", "compare": "between"}],
240
+ "reportProperties": {"filterFieldConfig": [{"key": "flt_sourcearea",
241
+ "fieldName": "shfz_admindivision", "dataSourceId": "arrival-form-id"}]},
242
+ }, "test_report")
243
+ area, dates = contract["filterValueContracts"]
244
+ self.assertEqual(area["candidateValueKeys"], ["shfz_admindivision", "flt_sourcearea", "shfz_sourcearea"])
245
+ self.assertFalse(area["runtimeKeysVerified"])
246
+ self.assertEqual(area["sourceBinding"], {"fieldName": "shfz_admindivision", "dataSourceId": "arrival-form-id"})
247
+ self.assertEqual(dates["valueChannel"], "getFilterItems")
248
+ self.assertEqual(dates["rangeItemKeys"], {"start": "flt_date_startdate", "end": "flt_date_enddate"})
249
+ self.assertEqual(contract["permissionFilterChannel"], "getQFilters")
250
+
251
+ def test_compatibility_candidates_do_not_invent_a_project_namespace(self):
252
+ contract = report_ops.compile_report_plugin_contract({
253
+ "reportFilters": [{"fieldPath": "flt_supplier", "compare": "in"}],
254
+ }, "acme_report")
255
+ self.assertEqual(contract["filterValueContracts"][0]["candidateValueKeys"], ["flt_supplier"])
256
+ contract = report_ops.compile_report_plugin_contract({
257
+ "reportFilters": [{"fieldPath": "flt_area", "compare": "in"}],
258
+ "reportProperties": {"filterFieldConfig": [{"key": "flt_area", "fieldName": "acme_region"}]},
259
+ }, "acme_report")
260
+ self.assertEqual(contract["filterValueContracts"][0]["candidateValueKeys"], ["acme_region", "flt_area", "acme_area"])
235
261
 
236
262
  def test_default_valued_filter_properties_are_not_requirements(self):
237
263
  contract = _compile(
@@ -108,6 +108,28 @@ class TestColumnsFromLayout(unittest.TestCase):
108
108
 
109
109
 
110
110
  class TestFiltersFromLayout(unittest.TestCase):
111
+ def test_fieldap_filters_read_actual_entity_types(self):
112
+ layout = _layout()
113
+ layout["children"][1]["children"][0]["children"] = [
114
+ {"key": key, "type": "FieldAp", "name": key}
115
+ for key in ("startdate", "orgscope", "resultms")
116
+ ] + [{"key": "label", "type": "LabelAp", "name": "Label"}]
117
+ schema_text = "### Entity: kdtest_rpt (Report) [ReportEntity]\n" + "\n".join([
118
+ "Field: startdate (Start Date) Type=DateField",
119
+ "Field: orgscope (Organizations) Type=MulBaseDataField",
120
+ "Field: resultms (Result) Type=ComboListField",
121
+ "Field: column_only (Column Only) Type=TextField",
122
+ ])
123
+ filters = _filters_from_layout(layout, schema_text)
124
+ self.assertEqual(["startdate", "orgscope", "resultms"], [row["fieldPath"] for row in filters])
125
+ self.assertEqual(["DateField", "MulBaseDataField", "ComboListField"], [row["fieldType"] for row in filters])
126
+ self.assertTrue(all(row["controlType"] == "FieldAp" for row in filters))
127
+
128
+ def test_fieldap_without_entity_evidence_does_not_guess_text(self):
129
+ layout = _layout()
130
+ layout["children"][1]["children"][0]["children"] = [{"key": "date", "type": "FieldAp", "name": "Date"}]
131
+ self.assertEqual([{"fieldPath": "date", "displayName": "Date", "controlType": "FieldAp"}], _filters_from_layout(layout))
132
+
111
133
  def test_filter_controls_are_derived_with_control_type(self):
112
134
  filters = _filters_from_layout(_layout())
113
135
  self.assertEqual(["biz_date", "sup_number"], [item["fieldPath"] for item in filters])
@@ -0,0 +1,65 @@
1
+ import sys
2
+ from pathlib import Path
3
+ import pytest
4
+
5
+ sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
6
+ from _core import report_ops
7
+
8
+
9
+ def request():
10
+ return report_ops.normalize_report_filters_input([{
11
+ "fieldPath": "flt_supplier", "displayName": "Supplier", "compare": "in",
12
+ "source": {"formNumber": "purchase", "fieldKey": "supplier"},
13
+ }])
14
+
15
+
16
+ def test_source_is_typed_input_and_not_an_arbitrary_field_selector():
17
+ item = request()[0]
18
+ assert report_ops._build_filters_payload([item])[0]["source"] == item["source"]
19
+ with pytest.raises(ValueError):
20
+ report_ops.normalize_report_filters_input([{**item, "source": {"formNumber": "purchase", "fieldType": "TextField"}}])
21
+
22
+
23
+ def test_legacy_platform_rejected_before_write_and_never_falls_back(monkeypatch):
24
+ writes = []
25
+ monkeypatch.setattr(report_ops, "_get_schema", lambda *args: {"data": {"schemaText": "legacy"}})
26
+ monkeypatch.setattr(report_ops, "_set_complex", lambda *args: writes.append(args))
27
+ with pytest.raises(RuntimeError, match="PLATFORM_UNSUPPORTED"):
28
+ report_ops.set_report_filters({}, "token", "report", request(), form_number="report")
29
+ assert writes == []
30
+
31
+
32
+ def test_typed_source_uses_dedicated_route(monkeypatch):
33
+ calls = []
34
+ monkeypatch.setattr(report_ops, "_get_schema", lambda *args: {"data": {"fieldSchemas": [
35
+ {"fieldPath": "supplier", "fieldType": "BasedataField", "referenceEntityId": "bd_supplier"}]}})
36
+ monkeypatch.setattr(report_ops, "_assert_report_entity_writable", lambda *args, **kwargs: None)
37
+ monkeypatch.setattr(report_ops, "_set_complex", lambda *args: calls.append(args) or {"success": True})
38
+ report_ops.set_report_filters({}, "token", "report", request(), form_number="report")
39
+ assert calls[0][3] == "ReportFilters.typed"
40
+
41
+
42
+ def test_readback_compares_fresh_source_reference_and_enum_configuration(monkeypatch):
43
+ source = {"fieldPath": "supplier", "fieldType": "BasedataField", "referenceEntityId": "bd_supplier", "displayProp": "name"}
44
+ monkeypatch.setattr(report_ops, "_get_schema", lambda *args: {"data": {"fieldSchemas": [source]}})
45
+ schema = {"readBackAvailable": True, "readBackSource": "formLayout", "typedFiltersReadBack": True,
46
+ "undecidableFilterProperties": ["compare", "defaultValue", "visible"],
47
+ "filterSourceExpectations": report_ops.report_filter_source_expectations({}, "token", request()),
48
+ "reportFilters": [{**source, "fieldPath": "flt_supplier", "displayName": "Supplier"}]}
49
+ assert report_ops.report_readback_verification("filters", request(), schema)["verified"]
50
+ schema["reportFilters"][0]["referenceEntityId"] = "wrong-reference"
51
+ assert not report_ops.report_readback_verification("filters", request(), schema)["verified"]
52
+ schema["reportFilters"][0]["referenceEntityId"] = "bd_supplier"
53
+ del schema["typedFiltersReadBack"]
54
+ assert not report_ops.report_readback_verification("filters", request(), schema)["verified"]
55
+
56
+
57
+ def test_date_range_and_ambiguous_source_fail_closed(monkeypatch):
58
+ items = [{"fieldPath": "flt_date", "compare": "between", "source": {"formNumber": "purchase", "fieldKey": "date"}}]
59
+ monkeypatch.setattr(report_ops, "_get_schema", lambda *args: {"data": {"fieldSchemas": [
60
+ {"fieldPath": "date", "fieldType": "DateField", "timeZoneTransType": 0}]}})
61
+ assert report_ops.report_filter_source_expectations({}, "token", items)["flt_date"]["fieldType"] == "DateRangeField"
62
+ monkeypatch.setattr(report_ops, "_get_schema", lambda *args: {"data": {"fieldSchemas": [
63
+ {"fieldPath": "date", "fieldType": "TextField"}]}})
64
+ with pytest.raises(ValueError, match="RANGE_TYPE_UNSUPPORTED"):
65
+ report_ops.report_filter_source_expectations({}, "token", items)