diff --git a/robosystems_client/api/graph_operations/op_change_reporting_style.py b/robosystems_client/api/graph_operations/op_change_reporting_style.py new file mode 100644 index 0000000..9d2b725 --- /dev/null +++ b/robosystems_client/api/graph_operations/op_change_reporting_style.py @@ -0,0 +1,296 @@ +from http import HTTPStatus +from typing import Any +from urllib.parse import quote + +import httpx + +from ... import errors +from ...client import AuthenticatedClient, Client +from ...models.change_reporting_style_op import ChangeReportingStyleOp +from ...models.error_response import ErrorResponse +from ...models.operation_envelope import OperationEnvelope +from ...types import UNSET, Response, Unset + + +def _get_kwargs( + graph_id: str, + *, + body: ChangeReportingStyleOp, + idempotency_key: None | str | Unset = UNSET, +) -> dict[str, Any]: + headers: dict[str, Any] = {} + if not isinstance(idempotency_key, Unset): + headers["Idempotency-Key"] = idempotency_key + + _kwargs: dict[str, Any] = { + "method": "post", + "url": "/v1/graphs/{graph_id}/operations/change-reporting-style".format( + graph_id=quote(str(graph_id), safe=""), + ), + } + + _kwargs["json"] = body.to_dict() + + headers["Content-Type"] = "application/json" + + _kwargs["headers"] = headers + return _kwargs + + +def _parse_response( + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> ErrorResponse | OperationEnvelope | None: + if response.status_code == 200: + response_200 = OperationEnvelope.from_dict(response.json()) + + return response_200 + + if response.status_code == 400: + response_400 = ErrorResponse.from_dict(response.json()) + + return response_400 + + if response.status_code == 401: + response_401 = ErrorResponse.from_dict(response.json()) + + return response_401 + + if response.status_code == 403: + response_403 = ErrorResponse.from_dict(response.json()) + + return response_403 + + if response.status_code == 404: + response_404 = ErrorResponse.from_dict(response.json()) + + return response_404 + + if response.status_code == 409: + response_409 = ErrorResponse.from_dict(response.json()) + + return response_409 + + if response.status_code == 422: + response_422 = ErrorResponse.from_dict(response.json()) + + return response_422 + + if response.status_code == 429: + response_429 = ErrorResponse.from_dict(response.json()) + + return response_429 + + if response.status_code == 500: + response_500 = ErrorResponse.from_dict(response.json()) + + return response_500 + + if client.raise_on_unexpected_status: + raise errors.UnexpectedStatus(response.status_code, response.content) + else: + return None + + +def _build_response( + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> Response[ErrorResponse | OperationEnvelope]: + return Response( + status_code=HTTPStatus(response.status_code), + content=response.content, + headers=response.headers, + parsed=_parse_response(client=client, response=response), + ) + + +def sync_detailed( + graph_id: str, + *, + client: AuthenticatedClient, + body: ChangeReportingStyleOp, + idempotency_key: None | str | Unset = UNSET, +) -> Response[ErrorResponse | OperationEnvelope]: + """Change Reporting Style + + Switches the graph's Reporting Style (Phase 2 of §3.2). Synchronous: validates the target Style has + a complete composition in the tenant schema, then updates `graphs.reporting_style_id`. Filed Reports + are unaffected; new reports use the new Style. Idempotent on the same id. + + **Idempotency**: supply an `Idempotency-Key` header to make safe retries; replays within 24 hours + return the same envelope. Reusing the key with a different body returns HTTP 409 Conflict. + + Args: + graph_id (str): + idempotency_key (None | str | Unset): + body (ChangeReportingStyleOp): Body for the change-reporting-style operation (Phase 2 of + §3.2). + + Switches the graph to a different Reporting Style. The target Style + must be a library- or customer-authored Structure with + ``structure_type='reporting_style'`` and a complete composition + (one Network per required statement type — BS / IS / CF / SE). Filed + Reports are unaffected because each ``Report`` already pins its own + ``structure_id`` per FactSet at create-time; new reports use the new + Style. Idempotent on the same target id. + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Response[ErrorResponse | OperationEnvelope] + """ + + kwargs = _get_kwargs( + graph_id=graph_id, + body=body, + idempotency_key=idempotency_key, + ) + + response = client.get_httpx_client().request( + **kwargs, + ) + + return _build_response(client=client, response=response) + + +def sync( + graph_id: str, + *, + client: AuthenticatedClient, + body: ChangeReportingStyleOp, + idempotency_key: None | str | Unset = UNSET, +) -> ErrorResponse | OperationEnvelope | None: + """Change Reporting Style + + Switches the graph's Reporting Style (Phase 2 of §3.2). Synchronous: validates the target Style has + a complete composition in the tenant schema, then updates `graphs.reporting_style_id`. Filed Reports + are unaffected; new reports use the new Style. Idempotent on the same id. + + **Idempotency**: supply an `Idempotency-Key` header to make safe retries; replays within 24 hours + return the same envelope. Reusing the key with a different body returns HTTP 409 Conflict. + + Args: + graph_id (str): + idempotency_key (None | str | Unset): + body (ChangeReportingStyleOp): Body for the change-reporting-style operation (Phase 2 of + §3.2). + + Switches the graph to a different Reporting Style. The target Style + must be a library- or customer-authored Structure with + ``structure_type='reporting_style'`` and a complete composition + (one Network per required statement type — BS / IS / CF / SE). Filed + Reports are unaffected because each ``Report`` already pins its own + ``structure_id`` per FactSet at create-time; new reports use the new + Style. Idempotent on the same target id. + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + ErrorResponse | OperationEnvelope + """ + + return sync_detailed( + graph_id=graph_id, + client=client, + body=body, + idempotency_key=idempotency_key, + ).parsed + + +async def asyncio_detailed( + graph_id: str, + *, + client: AuthenticatedClient, + body: ChangeReportingStyleOp, + idempotency_key: None | str | Unset = UNSET, +) -> Response[ErrorResponse | OperationEnvelope]: + """Change Reporting Style + + Switches the graph's Reporting Style (Phase 2 of §3.2). Synchronous: validates the target Style has + a complete composition in the tenant schema, then updates `graphs.reporting_style_id`. Filed Reports + are unaffected; new reports use the new Style. Idempotent on the same id. + + **Idempotency**: supply an `Idempotency-Key` header to make safe retries; replays within 24 hours + return the same envelope. Reusing the key with a different body returns HTTP 409 Conflict. + + Args: + graph_id (str): + idempotency_key (None | str | Unset): + body (ChangeReportingStyleOp): Body for the change-reporting-style operation (Phase 2 of + §3.2). + + Switches the graph to a different Reporting Style. The target Style + must be a library- or customer-authored Structure with + ``structure_type='reporting_style'`` and a complete composition + (one Network per required statement type — BS / IS / CF / SE). Filed + Reports are unaffected because each ``Report`` already pins its own + ``structure_id`` per FactSet at create-time; new reports use the new + Style. Idempotent on the same target id. + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Response[ErrorResponse | OperationEnvelope] + """ + + kwargs = _get_kwargs( + graph_id=graph_id, + body=body, + idempotency_key=idempotency_key, + ) + + response = await client.get_async_httpx_client().request(**kwargs) + + return _build_response(client=client, response=response) + + +async def asyncio( + graph_id: str, + *, + client: AuthenticatedClient, + body: ChangeReportingStyleOp, + idempotency_key: None | str | Unset = UNSET, +) -> ErrorResponse | OperationEnvelope | None: + """Change Reporting Style + + Switches the graph's Reporting Style (Phase 2 of §3.2). Synchronous: validates the target Style has + a complete composition in the tenant schema, then updates `graphs.reporting_style_id`. Filed Reports + are unaffected; new reports use the new Style. Idempotent on the same id. + + **Idempotency**: supply an `Idempotency-Key` header to make safe retries; replays within 24 hours + return the same envelope. Reusing the key with a different body returns HTTP 409 Conflict. + + Args: + graph_id (str): + idempotency_key (None | str | Unset): + body (ChangeReportingStyleOp): Body for the change-reporting-style operation (Phase 2 of + §3.2). + + Switches the graph to a different Reporting Style. The target Style + must be a library- or customer-authored Structure with + ``structure_type='reporting_style'`` and a complete composition + (one Network per required statement type — BS / IS / CF / SE). Filed + Reports are unaffected because each ``Report`` already pins its own + ``structure_id`` per FactSet at create-time; new reports use the new + Style. Idempotent on the same target id. + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + ErrorResponse | OperationEnvelope + """ + + return ( + await asyncio_detailed( + graph_id=graph_id, + client=client, + body=body, + idempotency_key=idempotency_key, + ) + ).parsed diff --git a/robosystems_client/models/__init__.py b/robosystems_client/models/__init__.py index ee91f98..72bbf57 100644 --- a/robosystems_client/models/__init__.py +++ b/robosystems_client/models/__init__.py @@ -48,6 +48,7 @@ CancelOperationResponseCanceloperation, ) from .cancel_subscription_request import CancelSubscriptionRequest +from .change_reporting_style_op import ChangeReportingStyleOp from .change_tier_op import ChangeTierOp from .change_tier_op_new_tier import ChangeTierOpNewTier from .checkout_response import CheckoutResponse @@ -55,6 +56,9 @@ from .classification_lite import ClassificationLite from .close_period_operation import ClosePeriodOperation from .close_period_response import ClosePeriodResponse +from .close_period_response_rule_summary_type_0 import ( + ClosePeriodResponseRuleSummaryType0, +) from .connection_lite import ConnectionLite from .connection_options_response import ConnectionOptionsResponse from .connection_provider_info import ConnectionProviderInfo @@ -438,6 +442,7 @@ from .password_policy_response import PasswordPolicyResponse from .password_policy_response_policy import PasswordPolicyResponsePolicy from .payment_method import PaymentMethod +from .pending_obligation_detail_response import PendingObligationDetailResponse from .performance_insights import PerformanceInsights from .performance_insights_operation_stats import PerformanceInsightsOperationStats from .performance_insights_slow_queries_item import PerformanceInsightsSlowQueriesItem @@ -677,6 +682,7 @@ "BillingCustomer", "CancelOperationResponseCanceloperation", "CancelSubscriptionRequest", + "ChangeReportingStyleOp", "ChangeTierOp", "ChangeTierOpNewTier", "CheckoutResponse", @@ -684,6 +690,7 @@ "ClassificationLite", "ClosePeriodOperation", "ClosePeriodResponse", + "ClosePeriodResponseRuleSummaryType0", "ConnectionLite", "ConnectionOptionsResponse", "ConnectionProviderInfo", @@ -947,6 +954,7 @@ "PasswordPolicyResponse", "PasswordPolicyResponsePolicy", "PaymentMethod", + "PendingObligationDetailResponse", "PerformanceInsights", "PerformanceInsightsOperationStats", "PerformanceInsightsSlowQueriesItem", diff --git a/robosystems_client/models/change_reporting_style_op.py b/robosystems_client/models/change_reporting_style_op.py new file mode 100644 index 0000000..7550ba0 --- /dev/null +++ b/robosystems_client/models/change_reporting_style_op.py @@ -0,0 +1,72 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +T = TypeVar("T", bound="ChangeReportingStyleOp") + + +@_attrs_define +class ChangeReportingStyleOp: + """Body for the change-reporting-style operation (Phase 2 of §3.2). + + Switches the graph to a different Reporting Style. The target Style + must be a library- or customer-authored Structure with + ``structure_type='reporting_style'`` and a complete composition + (one Network per required statement type — BS / IS / CF / SE). Filed + Reports are unaffected because each ``Report`` already pins its own + ``structure_id`` per FactSet at create-time; new reports use the new + Style. Idempotent on the same target id. + + Attributes: + reporting_style_id (str): Structure id of the target Reporting Style (e.g., + `025f5d48-12ce-5d65-b9eb-4f137a10ef06` for the library-seeded Default Style). Must resolve to a Structure with + structure_type='reporting_style' that has a complete composition in the graph's tenant schema. + """ + + reporting_style_id: str + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + reporting_style_id = self.reporting_style_id + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update( + { + "reporting_style_id": reporting_style_id, + } + ) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + reporting_style_id = d.pop("reporting_style_id") + + change_reporting_style_op = cls( + reporting_style_id=reporting_style_id, + ) + + change_reporting_style_op.additional_properties = d + return change_reporting_style_op + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/robosystems_client/models/close_period_response.py b/robosystems_client/models/close_period_response.py index 014dd00..6d5dcf6 100644 --- a/robosystems_client/models/close_period_response.py +++ b/robosystems_client/models/close_period_response.py @@ -1,7 +1,7 @@ from __future__ import annotations from collections.abc import Mapping -from typing import TYPE_CHECKING, Any, TypeVar +from typing import TYPE_CHECKING, Any, TypeVar, cast from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -9,6 +9,9 @@ from ..types import UNSET, Unset if TYPE_CHECKING: + from ..models.close_period_response_rule_summary_type_0 import ( + ClosePeriodResponseRuleSummaryType0, + ) from ..models.fiscal_calendar_response import FiscalCalendarResponse @@ -25,15 +28,26 @@ class ClosePeriodResponse: entries_posted (int | Unset): Number of draft entries transitioned to posted Default: 0. target_auto_advanced (bool | Unset): Whether close_target was auto-advanced because it was reached Default: False. + rule_summary (ClosePeriodResponseRuleSummaryType0 | None | Unset): Aggregated rule-eval outcome across every + schedule Structure with facts in the closed period — keys: pass/fail/error/skipped. None when no schedules had + facts in the period (§3.8 auto-run on close). + evaluated_structure_ids (list[str] | Unset): ids of schedule Structures whose rules were evaluated during the + close. Pairs with rule_summary. """ fiscal_calendar: FiscalCalendarResponse period: str entries_posted: int | Unset = 0 target_auto_advanced: bool | Unset = False + rule_summary: ClosePeriodResponseRuleSummaryType0 | None | Unset = UNSET + evaluated_structure_ids: list[str] | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: + from ..models.close_period_response_rule_summary_type_0 import ( + ClosePeriodResponseRuleSummaryType0, + ) + fiscal_calendar = self.fiscal_calendar.to_dict() period = self.period @@ -42,6 +56,18 @@ def to_dict(self) -> dict[str, Any]: target_auto_advanced = self.target_auto_advanced + rule_summary: dict[str, Any] | None | Unset + if isinstance(self.rule_summary, Unset): + rule_summary = UNSET + elif isinstance(self.rule_summary, ClosePeriodResponseRuleSummaryType0): + rule_summary = self.rule_summary.to_dict() + else: + rule_summary = self.rule_summary + + evaluated_structure_ids: list[str] | Unset = UNSET + if not isinstance(self.evaluated_structure_ids, Unset): + evaluated_structure_ids = self.evaluated_structure_ids + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update( @@ -54,11 +80,18 @@ def to_dict(self) -> dict[str, Any]: field_dict["entries_posted"] = entries_posted if target_auto_advanced is not UNSET: field_dict["target_auto_advanced"] = target_auto_advanced + if rule_summary is not UNSET: + field_dict["rule_summary"] = rule_summary + if evaluated_structure_ids is not UNSET: + field_dict["evaluated_structure_ids"] = evaluated_structure_ids return field_dict @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + from ..models.close_period_response_rule_summary_type_0 import ( + ClosePeriodResponseRuleSummaryType0, + ) from ..models.fiscal_calendar_response import FiscalCalendarResponse d = dict(src_dict) @@ -70,11 +103,34 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: target_auto_advanced = d.pop("target_auto_advanced", UNSET) + def _parse_rule_summary( + data: object, + ) -> ClosePeriodResponseRuleSummaryType0 | None | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + try: + if not isinstance(data, dict): + raise TypeError() + rule_summary_type_0 = ClosePeriodResponseRuleSummaryType0.from_dict(data) + + return rule_summary_type_0 + except (TypeError, ValueError, AttributeError, KeyError): + pass + return cast(ClosePeriodResponseRuleSummaryType0 | None | Unset, data) + + rule_summary = _parse_rule_summary(d.pop("rule_summary", UNSET)) + + evaluated_structure_ids = cast(list[str], d.pop("evaluated_structure_ids", UNSET)) + close_period_response = cls( fiscal_calendar=fiscal_calendar, period=period, entries_posted=entries_posted, target_auto_advanced=target_auto_advanced, + rule_summary=rule_summary, + evaluated_structure_ids=evaluated_structure_ids, ) close_period_response.additional_properties = d diff --git a/robosystems_client/models/close_period_response_rule_summary_type_0.py b/robosystems_client/models/close_period_response_rule_summary_type_0.py new file mode 100644 index 0000000..32b8eb6 --- /dev/null +++ b/robosystems_client/models/close_period_response_rule_summary_type_0.py @@ -0,0 +1,47 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +T = TypeVar("T", bound="ClosePeriodResponseRuleSummaryType0") + + +@_attrs_define +class ClosePeriodResponseRuleSummaryType0: + """ """ + + additional_properties: dict[str, int] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + close_period_response_rule_summary_type_0 = cls() + + close_period_response_rule_summary_type_0.additional_properties = d + return close_period_response_rule_summary_type_0 + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> int: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: int) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/robosystems_client/models/create_legacy_arm.py b/robosystems_client/models/create_legacy_arm.py index acf5b06..6c24e95 100644 --- a/robosystems_client/models/create_legacy_arm.py +++ b/robosystems_client/models/create_legacy_arm.py @@ -22,11 +22,11 @@ class CreateLegacyArm: a typed construction path at the API boundary. Statement-family blocks (balance_sheet, income_statement, - cash_flow_statement, equity_statement) are constructed via - `create-report`, not this endpoint. Metric blocks are recognized - but their evaluator has not shipped. Calling this endpoint with one - of these block types returns HTTP 501 with a hint pointing to the - correct construction path. + cash_flow_statement, equity_statement, comprehensive_income) are + constructed via `create-report`, not this endpoint. Metric blocks + are recognized but their evaluator has not shipped. Calling this + endpoint with one of these block types returns HTTP 501 with a hint + pointing to the correct construction path. Attributes: block_type (CreateLegacyArmBlockType): Statement-family or metric block type. The endpoint returns 501 for these diff --git a/robosystems_client/models/create_legacy_arm_block_type.py b/robosystems_client/models/create_legacy_arm_block_type.py index abbdf69..b284d5c 100644 --- a/robosystems_client/models/create_legacy_arm_block_type.py +++ b/robosystems_client/models/create_legacy_arm_block_type.py @@ -4,6 +4,7 @@ class CreateLegacyArmBlockType(str, Enum): BALANCE_SHEET = "balance_sheet" CASH_FLOW_STATEMENT = "cash_flow_statement" + COMPREHENSIVE_INCOME = "comprehensive_income" EQUITY_STATEMENT = "equity_statement" INCOME_STATEMENT = "income_statement" METRIC = "metric" diff --git a/robosystems_client/models/delete_legacy_arm_block_type.py b/robosystems_client/models/delete_legacy_arm_block_type.py index cdadc9c..238fc84 100644 --- a/robosystems_client/models/delete_legacy_arm_block_type.py +++ b/robosystems_client/models/delete_legacy_arm_block_type.py @@ -4,6 +4,7 @@ class DeleteLegacyArmBlockType(str, Enum): BALANCE_SHEET = "balance_sheet" CASH_FLOW_STATEMENT = "cash_flow_statement" + COMPREHENSIVE_INCOME = "comprehensive_income" EQUITY_STATEMENT = "equity_statement" INCOME_STATEMENT = "income_statement" METRIC = "metric" diff --git a/robosystems_client/models/fact_lite.py b/robosystems_client/models/fact_lite.py index 79d6b53..e9da24b 100644 --- a/robosystems_client/models/fact_lite.py +++ b/robosystems_client/models/fact_lite.py @@ -24,6 +24,8 @@ class FactLite: period_end (datetime.date): period_type (str): fact_scope (str): historical | in_scope + element_name (None | str | Unset): + element_qname (None | str | Unset): period_start (datetime.date | None | Unset): unit (str | Unset): Default: 'USD'. fact_set_id (None | str | Unset): @@ -35,6 +37,8 @@ class FactLite: period_end: datetime.date period_type: str fact_scope: str + element_name: None | str | Unset = UNSET + element_qname: None | str | Unset = UNSET period_start: datetime.date | None | Unset = UNSET unit: str | Unset = "USD" fact_set_id: None | str | Unset = UNSET @@ -53,6 +57,18 @@ def to_dict(self) -> dict[str, Any]: fact_scope = self.fact_scope + element_name: None | str | Unset + if isinstance(self.element_name, Unset): + element_name = UNSET + else: + element_name = self.element_name + + element_qname: None | str | Unset + if isinstance(self.element_qname, Unset): + element_qname = UNSET + else: + element_qname = self.element_qname + period_start: None | str | Unset if isinstance(self.period_start, Unset): period_start = UNSET @@ -81,6 +97,10 @@ def to_dict(self) -> dict[str, Any]: "fact_scope": fact_scope, } ) + if element_name is not UNSET: + field_dict["element_name"] = element_name + if element_qname is not UNSET: + field_dict["element_qname"] = element_qname if period_start is not UNSET: field_dict["period_start"] = period_start if unit is not UNSET: @@ -105,6 +125,24 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: fact_scope = d.pop("fact_scope") + def _parse_element_name(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + element_name = _parse_element_name(d.pop("element_name", UNSET)) + + def _parse_element_qname(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + element_qname = _parse_element_qname(d.pop("element_qname", UNSET)) + def _parse_period_start(data: object) -> datetime.date | None | Unset: if data is None: return data @@ -140,6 +178,8 @@ def _parse_fact_set_id(data: object) -> None | str | Unset: period_end=period_end, period_type=period_type, fact_scope=fact_scope, + element_name=element_name, + element_qname=element_qname, period_start=period_start, unit=unit, fact_set_id=fact_set_id, diff --git a/robosystems_client/models/fiscal_calendar_response.py b/robosystems_client/models/fiscal_calendar_response.py index e40ba23..9a61a05 100644 --- a/robosystems_client/models/fiscal_calendar_response.py +++ b/robosystems_client/models/fiscal_calendar_response.py @@ -12,6 +12,9 @@ if TYPE_CHECKING: from ..models.fiscal_period_summary import FiscalPeriodSummary + from ..models.pending_obligation_detail_response import ( + PendingObligationDetailResponse, + ) T = TypeVar("T", bound="FiscalCalendarResponse") @@ -32,7 +35,17 @@ class FiscalCalendarResponse: closeable_now (bool | Unset): Whether the next period in the catch-up sequence passes all closeable gates Default: False. blockers (list[str] | Unset): Structured blocker codes when closeable_now is False: 'sequence_violation', - 'period_incomplete', 'sync_stale', 'calendar_not_initialized', 'period_already_closed' + 'period_incomplete', 'sync_stale', 'calendar_not_initialized', 'period_already_closed', 'pending_obligations' + pending_obligation_count (int | Unset): Number of pending schedule_entry_due events blocking close. Non-zero + only when `pending_obligations` is in `blockers`. Default: 0. + pending_obligation_sample (list[PendingObligationDetailResponse] | Unset): Sample of up to 5 pending obligations + (schedule_id, schedule_name, period, event_id) ordered by occurred_at. Use `list-event-blocks` with + event_type=schedule_entry_due&status=pending for the full set. + earliest_pending_period (None | str | Unset): Earliest period (YYYY-MM) with a pending obligation blocking + close. Null when no pending_obligations blocker is active. + sync_stale_days (int | None | Unset): Days the most recent sync is stale relative to the period to close. + Populated only when `sync_stale` is in `blockers` and last_sync_at exists (null when there's a connection but no + sync has ever run). last_close_at (datetime.datetime | None | Unset): initialized_at (datetime.datetime | None | Unset): last_sync_at (datetime.datetime | None | Unset): Most recent QB sync timestamp (if connected) @@ -47,6 +60,10 @@ class FiscalCalendarResponse: catch_up_sequence: list[str] | Unset = UNSET closeable_now: bool | Unset = False blockers: list[str] | Unset = UNSET + pending_obligation_count: int | Unset = 0 + pending_obligation_sample: list[PendingObligationDetailResponse] | Unset = UNSET + earliest_pending_period: None | str | Unset = UNSET + sync_stale_days: int | None | Unset = UNSET last_close_at: datetime.datetime | None | Unset = UNSET initialized_at: datetime.datetime | None | Unset = UNSET last_sync_at: datetime.datetime | None | Unset = UNSET @@ -82,6 +99,27 @@ def to_dict(self) -> dict[str, Any]: if not isinstance(self.blockers, Unset): blockers = self.blockers + pending_obligation_count = self.pending_obligation_count + + pending_obligation_sample: list[dict[str, Any]] | Unset = UNSET + if not isinstance(self.pending_obligation_sample, Unset): + pending_obligation_sample = [] + for pending_obligation_sample_item_data in self.pending_obligation_sample: + pending_obligation_sample_item = pending_obligation_sample_item_data.to_dict() + pending_obligation_sample.append(pending_obligation_sample_item) + + earliest_pending_period: None | str | Unset + if isinstance(self.earliest_pending_period, Unset): + earliest_pending_period = UNSET + else: + earliest_pending_period = self.earliest_pending_period + + sync_stale_days: int | None | Unset + if isinstance(self.sync_stale_days, Unset): + sync_stale_days = UNSET + else: + sync_stale_days = self.sync_stale_days + last_close_at: None | str | Unset if isinstance(self.last_close_at, Unset): last_close_at = UNSET @@ -133,6 +171,14 @@ def to_dict(self) -> dict[str, Any]: field_dict["closeable_now"] = closeable_now if blockers is not UNSET: field_dict["blockers"] = blockers + if pending_obligation_count is not UNSET: + field_dict["pending_obligation_count"] = pending_obligation_count + if pending_obligation_sample is not UNSET: + field_dict["pending_obligation_sample"] = pending_obligation_sample + if earliest_pending_period is not UNSET: + field_dict["earliest_pending_period"] = earliest_pending_period + if sync_stale_days is not UNSET: + field_dict["sync_stale_days"] = sync_stale_days if last_close_at is not UNSET: field_dict["last_close_at"] = last_close_at if initialized_at is not UNSET: @@ -147,6 +193,9 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: from ..models.fiscal_period_summary import FiscalPeriodSummary + from ..models.pending_obligation_detail_response import ( + PendingObligationDetailResponse, + ) d = dict(src_dict) graph_id = d.pop("graph_id") @@ -179,6 +228,39 @@ def _parse_close_target(data: object) -> None | str | Unset: blockers = cast(list[str], d.pop("blockers", UNSET)) + pending_obligation_count = d.pop("pending_obligation_count", UNSET) + + _pending_obligation_sample = d.pop("pending_obligation_sample", UNSET) + pending_obligation_sample: list[PendingObligationDetailResponse] | Unset = UNSET + if _pending_obligation_sample is not UNSET: + pending_obligation_sample = [] + for pending_obligation_sample_item_data in _pending_obligation_sample: + pending_obligation_sample_item = PendingObligationDetailResponse.from_dict( + pending_obligation_sample_item_data + ) + + pending_obligation_sample.append(pending_obligation_sample_item) + + def _parse_earliest_pending_period(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + earliest_pending_period = _parse_earliest_pending_period( + d.pop("earliest_pending_period", UNSET) + ) + + def _parse_sync_stale_days(data: object) -> int | None | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(int | None | Unset, data) + + sync_stale_days = _parse_sync_stale_days(d.pop("sync_stale_days", UNSET)) + def _parse_last_close_at(data: object) -> datetime.datetime | None | Unset: if data is None: return data @@ -248,6 +330,10 @@ def _parse_last_sync_at(data: object) -> datetime.datetime | None | Unset: catch_up_sequence=catch_up_sequence, closeable_now=closeable_now, blockers=blockers, + pending_obligation_count=pending_obligation_count, + pending_obligation_sample=pending_obligation_sample, + earliest_pending_period=earliest_pending_period, + sync_stale_days=sync_stale_days, last_close_at=last_close_at, initialized_at=initialized_at, last_sync_at=last_sync_at, diff --git a/robosystems_client/models/information_block_envelope.py b/robosystems_client/models/information_block_envelope.py index e1ebe54..9d3ce1a 100644 --- a/robosystems_client/models/information_block_envelope.py +++ b/robosystems_client/models/information_block_envelope.py @@ -50,6 +50,9 @@ class InformationBlockEnvelope: registered block types (the Structure → Taxonomy FK is non-null); declared optional to keep the shape forward- compatible with future synthetic blocks that don't originate from a taxonomy. taxonomy_name (None | str | Unset): Display name of the source taxonomy. + disclosure_id (None | str | Unset): Qname of the named Disclosure this block corresponds to (e.g., + 'disclosures:BalanceSheet'), when an inbound reportedDisclosure-requiresDisclosure arc identifies one. Null for + tenant-authored blocks without a Disclosure mapping. elements (list[ElementLite] | Unset): connections (list[ConnectionLite] | Unset): facts (list[FactLite] | Unset): @@ -84,6 +87,7 @@ class InformationBlockEnvelope: artifact: ArtifactResponse taxonomy_id: None | str | Unset = UNSET taxonomy_name: None | str | Unset = UNSET + disclosure_id: None | str | Unset = UNSET elements: list[ElementLite] | Unset = UNSET connections: list[ConnectionLite] | Unset = UNSET facts: list[FactLite] | Unset = UNSET @@ -123,6 +127,12 @@ def to_dict(self) -> dict[str, Any]: else: taxonomy_name = self.taxonomy_name + disclosure_id: None | str | Unset + if isinstance(self.disclosure_id, Unset): + disclosure_id = UNSET + else: + disclosure_id = self.disclosure_id + elements: list[dict[str, Any]] | Unset = UNSET if not isinstance(self.elements, Unset): elements = [] @@ -194,6 +204,8 @@ def to_dict(self) -> dict[str, Any]: field_dict["taxonomy_id"] = taxonomy_id if taxonomy_name is not UNSET: field_dict["taxonomy_name"] = taxonomy_name + if disclosure_id is not UNSET: + field_dict["disclosure_id"] = disclosure_id if elements is not UNSET: field_dict["elements"] = elements if connections is not UNSET: @@ -261,6 +273,15 @@ def _parse_taxonomy_name(data: object) -> None | str | Unset: taxonomy_name = _parse_taxonomy_name(d.pop("taxonomy_name", UNSET)) + def _parse_disclosure_id(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + disclosure_id = _parse_disclosure_id(d.pop("disclosure_id", UNSET)) + _elements = d.pop("elements", UNSET) elements: list[ElementLite] | Unset = UNSET if _elements is not UNSET: @@ -353,6 +374,7 @@ def _parse_fact_set(data: object) -> FactSetLite | None | Unset: artifact=artifact, taxonomy_id=taxonomy_id, taxonomy_name=taxonomy_name, + disclosure_id=disclosure_id, elements=elements, connections=connections, facts=facts, diff --git a/robosystems_client/models/pending_obligation_detail_response.py b/robosystems_client/models/pending_obligation_detail_response.py new file mode 100644 index 0000000..ec75670 --- /dev/null +++ b/robosystems_client/models/pending_obligation_detail_response.py @@ -0,0 +1,115 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar, cast + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +from ..types import UNSET, Unset + +T = TypeVar("T", bound="PendingObligationDetailResponse") + + +@_attrs_define +class PendingObligationDetailResponse: + """One pending schedule-derived obligation blocking close. + + Surfaced on `FiscalCalendarResponse` when `pending_obligations` is in + the blockers list so callers can name which schedules to promote. + + Attributes: + event_id (str): + period (str): Period in YYYY-MM format + schedule_id (None | str | Unset): + schedule_name (None | str | Unset): + """ + + event_id: str + period: str + schedule_id: None | str | Unset = UNSET + schedule_name: None | str | Unset = UNSET + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + event_id = self.event_id + + period = self.period + + schedule_id: None | str | Unset + if isinstance(self.schedule_id, Unset): + schedule_id = UNSET + else: + schedule_id = self.schedule_id + + schedule_name: None | str | Unset + if isinstance(self.schedule_name, Unset): + schedule_name = UNSET + else: + schedule_name = self.schedule_name + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update( + { + "event_id": event_id, + "period": period, + } + ) + if schedule_id is not UNSET: + field_dict["schedule_id"] = schedule_id + if schedule_name is not UNSET: + field_dict["schedule_name"] = schedule_name + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + event_id = d.pop("event_id") + + period = d.pop("period") + + def _parse_schedule_id(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + schedule_id = _parse_schedule_id(d.pop("schedule_id", UNSET)) + + def _parse_schedule_name(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + schedule_name = _parse_schedule_name(d.pop("schedule_name", UNSET)) + + pending_obligation_detail_response = cls( + event_id=event_id, + period=period, + schedule_id=schedule_id, + schedule_name=schedule_name, + ) + + pending_obligation_detail_response.additional_properties = d + return pending_obligation_detail_response + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/robosystems_client/models/update_legacy_arm_block_type.py b/robosystems_client/models/update_legacy_arm_block_type.py index 0e3f1fd..ec389b5 100644 --- a/robosystems_client/models/update_legacy_arm_block_type.py +++ b/robosystems_client/models/update_legacy_arm_block_type.py @@ -4,6 +4,7 @@ class UpdateLegacyArmBlockType(str, Enum): BALANCE_SHEET = "balance_sheet" CASH_FLOW_STATEMENT = "cash_flow_statement" + COMPREHENSIVE_INCOME = "comprehensive_income" EQUITY_STATEMENT = "equity_statement" INCOME_STATEMENT = "income_statement" METRIC = "metric"