diff --git a/plugins/1c/connector/adapter_1c_server.py b/plugins/1c/connector/adapter_1c_server.py index adcbd1f..1bc0d2c 100644 --- a/plugins/1c/connector/adapter_1c_server.py +++ b/plugins/1c/connector/adapter_1c_server.py @@ -762,6 +762,7 @@ METHODS = [ {"name": "metadata.object.components", "transport": "POST /rpc", "description": "Read-only confirmed component graph for one metadata object. Returns only modules, forms, templates, and SCD nodes confirmed by the existing live-SQL decoders, with public child selectors and explicit unresolved areas. It never infers absent children from an object kind."}, {"name": "metadata.object.modules", "transport": "POST /rpc", "description": "1C-facing BSL module list for a metadata object, including owned form modules. Exact extension objects are resolved from public kind/name or ref through a live-validated route cache. include_storage must be a JSON boolean true/false, string values are invalid. Physical module ids are hidden unless include_storage=true."}, {"name": "metadata.object.related", "transport": "POST /rpc", "description": "1C-facing related metadata objects such as forms and templates. Physical record paths are hidden unless include_storage=true."}, + {"name": "report.inspect", "transport": "POST /rpc", "description": "Read-only aggregate of one report's confirmed modules, forms, templates and Data Composition Schemas. Does not infer an SCD from a same-named form or stream."}, {"name": "scd.inspect", "transport": "POST /rpc", "description": "Read-only inspection entry point for a report Data Composition Schema. Resolves a base report from Config or an extension report from ConfigCAS by public names, and returns partial evidence instead of inventing undecoded parameters, datasets, resources, or variants."}, {"name": "appearance.inspect", "transport": "POST /rpc", "description": "Read-only inspection of a base CommonTemplate whose payload is confirmed as AppearanceTemplate XML. Returns XML-backed appearance rules; no write route is exposed."}, {"name": "scd.prepare", "transport": "POST /rpc", "description": "Prepare the complete SQL saved-state file set required for one report SCD. For base reports it includes both report files and the separately stored Template payload group, while keeping SQL file identifiers internal."}, @@ -36102,6 +36103,36 @@ def appearance_inspect(payload: dict[str, Any]) -> dict[str, Any]: return {"schema": "onec_appearance_inspect.v1", "method": method, "status": decoded.get("status"), "base_id": base_id, "template": public_metadata_row(object_card), "appearance": decoded, "activation_state": "working_not_runtime_applied"} +def report_inspect(payload: dict[str, Any]) -> dict[str, Any]: + """Return the confirmed component graph of a report without filling gaps.""" + method = "report.inspect" + base_id_or_error = require_base_id(payload, method) + if isinstance(base_id_or_error, dict): + return base_id_or_error + report = str(payload.get("report") or payload.get("name") or "").strip() + if not report: + return invalid_argument(method, "report", "report is required as a public 1C report name.") + component_payload = {"base_id": base_id_or_error, "kind": "Report", "name": report, "include_storage": False, "timeout_seconds": int(payload.get("timeout_seconds") or 60)} + for key in ("extension", "extension_guid"): + if payload.get(key) not in {None, ""}: + component_payload[key] = payload[key] + components_result = metadata_object_components(component_payload) + if not isinstance(components_result.get("object"), dict): + return {"schema": "onec_report_inspect.v1", "method": method, "status": components_result.get("status") or "not_found", "base_id": base_id_or_error, "unresolved": components_result.get("unresolved") or [], "diagnostics": components_result.get("diagnostics") or {}} + components = [item for item in components_result.get("components") or [] if isinstance(item, dict)] + return { + "schema": "onec_report_inspect.v1", "method": method, "status": "ok", "base_id": base_id_or_error, + "report": public_metadata_row(components_result["object"]), + "components": components, + "scd": [item for item in components if item.get("component_kind") == "scd"], + "templates": [item for item in components if item.get("component_kind") in {"scd", "template"}], + "forms": [item for item in components if item.get("component_kind") == "form"], + "modules": [item for item in components if item.get("component_kind") == "module"], + "unresolved": components_result.get("unresolved") or [], + "diagnostics": {"rule": "Only components proven by the individual live-SQL readers are included; a form is never promoted to an SCD by name."}, + } + + def scd_inspect(payload: dict[str, Any]) -> dict[str, Any]: """Resolve and decode one report SCD from its SQL ConfigCAS payload.""" method = "scd.inspect" @@ -70437,6 +70468,8 @@ def call_method_impl(method: str, payload: dict[str, Any] | None) -> dict[str, A return metadata_object_modules(payload) if method == "metadata.object.related": return metadata_object_related(payload) + if method == "report.inspect": + return report_inspect(payload) if method == "appearance.inspect": return appearance_inspect(payload) if method == "scd.inspect": diff --git a/tests/1c/test_payload_codec.py b/tests/1c/test_payload_codec.py index de03188..47b0063 100644 --- a/tests/1c/test_payload_codec.py +++ b/tests/1c/test_payload_codec.py @@ -280,6 +280,23 @@ def test_appearance_inspect_resolves_public_common_template(monkeypatch: pytest. assert result["appearance"]["items"][0]["parameter"] == "Заголовок" +def test_report_inspect_keeps_forms_separate_from_scd(monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setattr(adapter_server, "metadata_object_components", lambda _payload: { + "status": "ok", "object": {"kind": "Report", "name": "Продажи", "guid": "r" * 36}, + "components": [ + {"component_kind": "form", "name": "ОсновнаяСхемаКомпоновкиДанных"}, + {"component_kind": "scd", "name": "ОсновнаяСхема", "guid": "s" * 36}, + ], "unresolved": [{"area": "templates", "status": "partial"}], + }) + + result = adapter_server.report_inspect({"base_id": "upo_test", "report": "Продажи"}) + + assert result["status"] == "ok" + assert [item["name"] for item in result["forms"]] == ["ОсновнаяСхемаКомпоновкиДанных"] + assert [item["name"] for item in result["scd"]] == ["ОсновнаяСхема"] + assert result["unresolved"][0]["area"] == "templates" + + def test_scd_scalar_patch_preserves_platform_prefix_and_trailer() -> None: xml = """