diff --git a/.github/release-notes/v2.8.0.md b/.github/release-notes/v2.8.0.md new file mode 100644 index 00000000..55b06c73 --- /dev/null +++ b/.github/release-notes/v2.8.0.md @@ -0,0 +1,54 @@ +The generated tier catches up with the API's multi-entity step: a graph is +one reporting group, and every ledger operation can name which entity of it +to act on. Nothing on the stable tier changes shape; one generated-tier +removal and one path move are named below. + +## New: entities in a reporting group + +`api.robo_ledger_setup.create_entity` with `CreateEntityRequest` +(`POST /extensions/roboledger/{graph_id}/operations/create-entity`) adds a +subsidiary to the graph's group: a name, its legal form, the share the parent +holds (`ownership_pct`), an optional `parent_entity_id` to nest it under +another subsidiary, and an optional `ticker`, which prefixes the entity's +account names and must be unique in the graph (409). `LedgerEntityResponse` +gains `ownership_pct`. + +`entity_id` is a new optional field on `UpdateEntityRequest`, +`InitializeLedgerRequest`, `SetCloseTargetOperation`, `ClosePeriodOperation`, +`ReopenPeriodOperation`, `BackfillPlanHistoryOperation`, +`InitializeChartOfAccountsRequest`, `CreateReportRequest`, +`LinkEntityTaxonomyRequest`, `CreateEventBlockRequest` and +`LiveFinancialStatementRequest`. Omitted, each acts on the group parent, so +every existing call behaves as before. `FiscalCalendarResponse` and +`ReportResponse` carry `entity_id`. + +`LiveFinancialStatementRequest.consolidated` renders the group's combined +statement on the parent — every entity under the parent's Reporting Style, +summed per concept, with nothing eliminated between them — and +`LiveFinancialStatementResponse` carries `entity_id`, `consolidated` and +`combined_entity_ids`. + +The checked-in GraphQL schema gains `entityId` on `entity`, +`fiscalCalendar`, `periodCloseStatus`, `periodDrafts`, `openReceivables`, +`openPayables`, `openReceivablesByAgent`, `openPayablesByAgent`, +`eventBlocks`, `summary`, `accounts`, `accountTree`, `accountRollups`, +`reconciliations`, `trialBalance`, `transactions`, `closingBookStructures`, +`journalEntries` and `reports`; `LedgerEntity` gains `ownershipPct` and +`LedgerSummary` `entityId`. The checked-in operations are unchanged, so the +generated GraphQL models are too. + +## Removed: `api.graphs.select_graph` + +The API deprecated the graph-scoped select in favour of `set_selected_graph` +— the replacement 2.7.0 shipped — and has now removed the endpoint, so the +generated function would only ever have met a 404. No facade wrapped it and +the integration template's emit path never imported it; this is a +generated-tier removal riding a minor. `api.user.set_selected_graph` is the +call. + +## Moved: `validate_schema` + +`api.schema.validate_schema` is now `api.graphs.validate_schema`: the API +retagged the operation under Graphs, and a package path follows its tag. The +function, its request and its response are unchanged; update the import. No +facade wrapped it and the template never imported it. diff --git a/robosystems_client/api/graphs/select_graph.py b/robosystems_client/api/graphs/select_graph.py deleted file mode 100644 index b24d2039..00000000 --- a/robosystems_client/api/graphs/select_graph.py +++ /dev/null @@ -1,204 +0,0 @@ -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.error_response import ErrorResponse -from ...models.http_validation_error import HTTPValidationError -from ...models.success_response import SuccessResponse -from ...types import Response - - -def _get_kwargs( - graph_id: str, -) -> dict[str, Any]: - - _kwargs: dict[str, Any] = { - "method": "post", - "url": "/v1/graphs/{graph_id}/select".format( - graph_id=quote(str(graph_id), safe=""), - ), - } - - return _kwargs - - -def _parse_response( - *, client: AuthenticatedClient | Client, response: httpx.Response -) -> ErrorResponse | HTTPValidationError | SuccessResponse | None: - if response.status_code == 200: - response_200 = SuccessResponse.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 == 422: - response_422 = HTTPValidationError.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 | HTTPValidationError | SuccessResponse]: - 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, -) -> Response[ErrorResponse | HTTPValidationError | SuccessResponse]: - """Select Graph - - Deprecated: use `PUT /v1/user/selected-graph`, which writes the same selection. Remembers the graph - as the caller's current one. - - Args: - graph_id (str): - - 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 | HTTPValidationError | SuccessResponse] - """ - - kwargs = _get_kwargs( - graph_id=graph_id, - ) - - response = client.get_httpx_client().request( - **kwargs, - ) - - return _build_response(client=client, response=response) - - -def sync( - graph_id: str, - *, - client: AuthenticatedClient, -) -> ErrorResponse | HTTPValidationError | SuccessResponse | None: - """Select Graph - - Deprecated: use `PUT /v1/user/selected-graph`, which writes the same selection. Remembers the graph - as the caller's current one. - - Args: - graph_id (str): - - 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 | HTTPValidationError | SuccessResponse - """ - - return sync_detailed( - graph_id=graph_id, - client=client, - ).parsed - - -async def asyncio_detailed( - graph_id: str, - *, - client: AuthenticatedClient, -) -> Response[ErrorResponse | HTTPValidationError | SuccessResponse]: - """Select Graph - - Deprecated: use `PUT /v1/user/selected-graph`, which writes the same selection. Remembers the graph - as the caller's current one. - - Args: - graph_id (str): - - 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 | HTTPValidationError | SuccessResponse] - """ - - kwargs = _get_kwargs( - graph_id=graph_id, - ) - - response = await client.get_async_httpx_client().request(**kwargs) - - return _build_response(client=client, response=response) - - -async def asyncio( - graph_id: str, - *, - client: AuthenticatedClient, -) -> ErrorResponse | HTTPValidationError | SuccessResponse | None: - """Select Graph - - Deprecated: use `PUT /v1/user/selected-graph`, which writes the same selection. Remembers the graph - as the caller's current one. - - Args: - graph_id (str): - - 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 | HTTPValidationError | SuccessResponse - """ - - return ( - await asyncio_detailed( - graph_id=graph_id, - client=client, - ) - ).parsed diff --git a/robosystems_client/api/schema/validate_schema.py b/robosystems_client/api/graphs/validate_schema.py similarity index 100% rename from robosystems_client/api/schema/validate_schema.py rename to robosystems_client/api/graphs/validate_schema.py diff --git a/robosystems_client/api/robo_ledger_analytical_views/live_financial_statement.py b/robosystems_client/api/robo_ledger_analytical_views/live_financial_statement.py index ce518d4e..6318e946 100644 --- a/robosystems_client/api/robo_ledger_analytical_views/live_financial_statement.py +++ b/robosystems_client/api/robo_ledger_analytical_views/live_financial_statement.py @@ -117,7 +117,9 @@ def sync_detailed( Generate an ad-hoc financial statement directly from the tenant's OLTP ledger data using the active CoA→GAAP mapping. This is the authoritative source for RoboLedger entity graphs — no graph - materialization required. Rejected on shared-repository graphs; those should use `financial- + materialization required. One entity's books: `entity_id` names a subsidiary, omitted means the + group parent; `consolidated` on the parent combines every entity of the group at the rs-gaap + concepts, with no eliminations. Rejected on shared-repository graphs; those should use `financial- statement-analysis` instead. **Idempotency**: supply an `Idempotency-Key` header to make safe retries; replays within 24 hours @@ -161,7 +163,9 @@ def sync( Generate an ad-hoc financial statement directly from the tenant's OLTP ledger data using the active CoA→GAAP mapping. This is the authoritative source for RoboLedger entity graphs — no graph - materialization required. Rejected on shared-repository graphs; those should use `financial- + materialization required. One entity's books: `entity_id` names a subsidiary, omitted means the + group parent; `consolidated` on the parent combines every entity of the group at the rs-gaap + concepts, with no eliminations. Rejected on shared-repository graphs; those should use `financial- statement-analysis` instead. **Idempotency**: supply an `Idempotency-Key` header to make safe retries; replays within 24 hours @@ -200,7 +204,9 @@ async def asyncio_detailed( Generate an ad-hoc financial statement directly from the tenant's OLTP ledger data using the active CoA→GAAP mapping. This is the authoritative source for RoboLedger entity graphs — no graph - materialization required. Rejected on shared-repository graphs; those should use `financial- + materialization required. One entity's books: `entity_id` names a subsidiary, omitted means the + group parent; `consolidated` on the parent combines every entity of the group at the rs-gaap + concepts, with no eliminations. Rejected on shared-repository graphs; those should use `financial- statement-analysis` instead. **Idempotency**: supply an `Idempotency-Key` header to make safe retries; replays within 24 hours @@ -242,7 +248,9 @@ async def asyncio( Generate an ad-hoc financial statement directly from the tenant's OLTP ledger data using the active CoA→GAAP mapping. This is the authoritative source for RoboLedger entity graphs — no graph - materialization required. Rejected on shared-repository graphs; those should use `financial- + materialization required. One entity's books: `entity_id` names a subsidiary, omitted means the + group parent; `consolidated` on the parent combines every entity of the group at the rs-gaap + concepts, with no eliminations. Rejected on shared-repository graphs; those should use `financial- statement-analysis` instead. **Idempotency**: supply an `Idempotency-Key` header to make safe retries; replays within 24 hours diff --git a/robosystems_client/api/robo_ledger_setup/create_entity.py b/robosystems_client/api/robo_ledger_setup/create_entity.py new file mode 100644 index 00000000..b72c65dc --- /dev/null +++ b/robosystems_client/api/robo_ledger_setup/create_entity.py @@ -0,0 +1,314 @@ +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.create_entity_request import CreateEntityRequest +from ...models.error_response import ErrorResponse +from ...models.operation_envelope_ledger_entity_response import ( + OperationEnvelopeLedgerEntityResponse, +) +from ...types import UNSET, Response, Unset + + +def _get_kwargs( + graph_id: str, + *, + body: CreateEntityRequest, + 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": "/extensions/roboledger/{graph_id}/operations/create-entity".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 | OperationEnvelopeLedgerEntityResponse | None: + if response.status_code == 200: + response_200 = OperationEnvelopeLedgerEntityResponse.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 | OperationEnvelopeLedgerEntityResponse]: + 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: CreateEntityRequest, + idempotency_key: None | str | Unset = UNSET, +) -> Response[ErrorResponse | OperationEnvelopeLedgerEntityResponse]: + """Create Entity + + Add an entity to the graph's reporting group: a subsidiary under `parent_entity_id` (default the + group parent) that keeps its own books, chart of accounts and close, on the group's fiscal cadence. + A graph created without an entity gets this one as its group parent. Creates the entity row only — + give it a chart next (initialize-chart-of-accounts with `entity_id`) and a calendar (initialize with + `entity_id`); from then on every ledger operation takes `entity_id` to act in its books, and + omitting it means the group parent. The Reporting Style follows `entity_type` unless + `reporting_style_id` names one. `ticker` prefixes the entity's account names and must be unique in + the graph (409). There is no cap on entities: a graph is one reporting group, and everyone with + access to it sees every entity. + + **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 (CreateEntityRequest): Add an entity to the graph's reporting group. + + The new entity is a subsidiary of `parent_entity_id`, default the group + parent, and keeps its own books: give it a chart next + (`initialize-chart-of-accounts` with `entity_id`) and a calendar + (`initialize`), then name it with `entity_id` on any ledger operation. + A graph created without an entity gets this one as its group parent. + There is no cap on entities in a graph; capacity is the tier's. + + 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 | OperationEnvelopeLedgerEntityResponse] + """ + + 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: CreateEntityRequest, + idempotency_key: None | str | Unset = UNSET, +) -> ErrorResponse | OperationEnvelopeLedgerEntityResponse | None: + """Create Entity + + Add an entity to the graph's reporting group: a subsidiary under `parent_entity_id` (default the + group parent) that keeps its own books, chart of accounts and close, on the group's fiscal cadence. + A graph created without an entity gets this one as its group parent. Creates the entity row only — + give it a chart next (initialize-chart-of-accounts with `entity_id`) and a calendar (initialize with + `entity_id`); from then on every ledger operation takes `entity_id` to act in its books, and + omitting it means the group parent. The Reporting Style follows `entity_type` unless + `reporting_style_id` names one. `ticker` prefixes the entity's account names and must be unique in + the graph (409). There is no cap on entities: a graph is one reporting group, and everyone with + access to it sees every entity. + + **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 (CreateEntityRequest): Add an entity to the graph's reporting group. + + The new entity is a subsidiary of `parent_entity_id`, default the group + parent, and keeps its own books: give it a chart next + (`initialize-chart-of-accounts` with `entity_id`) and a calendar + (`initialize`), then name it with `entity_id` on any ledger operation. + A graph created without an entity gets this one as its group parent. + There is no cap on entities in a graph; capacity is the tier's. + + 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 | OperationEnvelopeLedgerEntityResponse + """ + + 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: CreateEntityRequest, + idempotency_key: None | str | Unset = UNSET, +) -> Response[ErrorResponse | OperationEnvelopeLedgerEntityResponse]: + """Create Entity + + Add an entity to the graph's reporting group: a subsidiary under `parent_entity_id` (default the + group parent) that keeps its own books, chart of accounts and close, on the group's fiscal cadence. + A graph created without an entity gets this one as its group parent. Creates the entity row only — + give it a chart next (initialize-chart-of-accounts with `entity_id`) and a calendar (initialize with + `entity_id`); from then on every ledger operation takes `entity_id` to act in its books, and + omitting it means the group parent. The Reporting Style follows `entity_type` unless + `reporting_style_id` names one. `ticker` prefixes the entity's account names and must be unique in + the graph (409). There is no cap on entities: a graph is one reporting group, and everyone with + access to it sees every entity. + + **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 (CreateEntityRequest): Add an entity to the graph's reporting group. + + The new entity is a subsidiary of `parent_entity_id`, default the group + parent, and keeps its own books: give it a chart next + (`initialize-chart-of-accounts` with `entity_id`) and a calendar + (`initialize`), then name it with `entity_id` on any ledger operation. + A graph created without an entity gets this one as its group parent. + There is no cap on entities in a graph; capacity is the tier's. + + 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 | OperationEnvelopeLedgerEntityResponse] + """ + + 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: CreateEntityRequest, + idempotency_key: None | str | Unset = UNSET, +) -> ErrorResponse | OperationEnvelopeLedgerEntityResponse | None: + """Create Entity + + Add an entity to the graph's reporting group: a subsidiary under `parent_entity_id` (default the + group parent) that keeps its own books, chart of accounts and close, on the group's fiscal cadence. + A graph created without an entity gets this one as its group parent. Creates the entity row only — + give it a chart next (initialize-chart-of-accounts with `entity_id`) and a calendar (initialize with + `entity_id`); from then on every ledger operation takes `entity_id` to act in its books, and + omitting it means the group parent. The Reporting Style follows `entity_type` unless + `reporting_style_id` names one. `ticker` prefixes the entity's account names and must be unique in + the graph (409). There is no cap on entities: a graph is one reporting group, and everyone with + access to it sees every entity. + + **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 (CreateEntityRequest): Add an entity to the graph's reporting group. + + The new entity is a subsidiary of `parent_entity_id`, default the group + parent, and keeps its own books: give it a chart next + (`initialize-chart-of-accounts` with `entity_id`) and a calendar + (`initialize`), then name it with `entity_id` on any ledger operation. + A graph created without an entity gets this one as its group parent. + There is no cap on entities in a graph; capacity is the tier's. + + 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 | OperationEnvelopeLedgerEntityResponse + """ + + return ( + await asyncio_detailed( + graph_id=graph_id, + client=client, + body=body, + idempotency_key=idempotency_key, + ) + ).parsed diff --git a/robosystems_client/api/robo_ledger_setup/initialize_chart_of_accounts.py b/robosystems_client/api/robo_ledger_setup/initialize_chart_of_accounts.py index 85fd9d38..db0136fd 100644 --- a/robosystems_client/api/robo_ledger_setup/initialize_chart_of_accounts.py +++ b/robosystems_client/api/robo_ledger_setup/initialize_chart_of_accounts.py @@ -124,9 +124,10 @@ def sync_detailed( services), `product` (inventory and COGS) — the `chartTemplates` GraphQL field lists them with names and account counts. Creates the chart, its `coa_mapping` structure and the template's CoA → rs-gaap mapping associations in one transaction, with the equity rows mapped by the entity's legal form - (`entity_type`, defaulting to the graph's primary entity). One-time: 409 once a chart exists — a - chart is never replaced. Customize afterwards with update-taxonomy-block; accounts that carry - activity are never deleted. + (`entity_type`, defaulting to the entity's). Each entity of the group keeps its own chart: + `entity_id` names a subsidiary, omitted means the group parent. One-time per entity: 409 once that + entity has a chart — a chart is never replaced. Customize afterwards with update-taxonomy-block; + accounts that carry activity are never deleted. **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. @@ -137,9 +138,10 @@ def sync_detailed( body (InitializeChartOfAccountsRequest): Create the graph's chart of accounts from a shipped template. - Refused (409) when the graph already has an active `chart_of_accounts` + Refused (409) when the entity already has an active `chart_of_accounts` taxonomy — a QuickBooks-synced tenant never needs this, and a chart is - never replaced. The template's equity rows are mapped by the entity's + never replaced. Each entity of the group keeps its own chart; omit + `entity_id` for the group parent. The template's equity rows are mapped by the entity's legal form (`entity_type`: corporation / llc / partnership); omit it to use the graph's primary entity, falling back to corporation. @@ -180,9 +182,10 @@ def sync( services), `product` (inventory and COGS) — the `chartTemplates` GraphQL field lists them with names and account counts. Creates the chart, its `coa_mapping` structure and the template's CoA → rs-gaap mapping associations in one transaction, with the equity rows mapped by the entity's legal form - (`entity_type`, defaulting to the graph's primary entity). One-time: 409 once a chart exists — a - chart is never replaced. Customize afterwards with update-taxonomy-block; accounts that carry - activity are never deleted. + (`entity_type`, defaulting to the entity's). Each entity of the group keeps its own chart: + `entity_id` names a subsidiary, omitted means the group parent. One-time per entity: 409 once that + entity has a chart — a chart is never replaced. Customize afterwards with update-taxonomy-block; + accounts that carry activity are never deleted. **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. @@ -193,9 +196,10 @@ def sync( body (InitializeChartOfAccountsRequest): Create the graph's chart of accounts from a shipped template. - Refused (409) when the graph already has an active `chart_of_accounts` + Refused (409) when the entity already has an active `chart_of_accounts` taxonomy — a QuickBooks-synced tenant never needs this, and a chart is - never replaced. The template's equity rows are mapped by the entity's + never replaced. Each entity of the group keeps its own chart; omit + `entity_id` for the group parent. The template's equity rows are mapped by the entity's legal form (`entity_type`: corporation / llc / partnership); omit it to use the graph's primary entity, falling back to corporation. @@ -231,9 +235,10 @@ async def asyncio_detailed( services), `product` (inventory and COGS) — the `chartTemplates` GraphQL field lists them with names and account counts. Creates the chart, its `coa_mapping` structure and the template's CoA → rs-gaap mapping associations in one transaction, with the equity rows mapped by the entity's legal form - (`entity_type`, defaulting to the graph's primary entity). One-time: 409 once a chart exists — a - chart is never replaced. Customize afterwards with update-taxonomy-block; accounts that carry - activity are never deleted. + (`entity_type`, defaulting to the entity's). Each entity of the group keeps its own chart: + `entity_id` names a subsidiary, omitted means the group parent. One-time per entity: 409 once that + entity has a chart — a chart is never replaced. Customize afterwards with update-taxonomy-block; + accounts that carry activity are never deleted. **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. @@ -244,9 +249,10 @@ async def asyncio_detailed( body (InitializeChartOfAccountsRequest): Create the graph's chart of accounts from a shipped template. - Refused (409) when the graph already has an active `chart_of_accounts` + Refused (409) when the entity already has an active `chart_of_accounts` taxonomy — a QuickBooks-synced tenant never needs this, and a chart is - never replaced. The template's equity rows are mapped by the entity's + never replaced. Each entity of the group keeps its own chart; omit + `entity_id` for the group parent. The template's equity rows are mapped by the entity's legal form (`entity_type`: corporation / llc / partnership); omit it to use the graph's primary entity, falling back to corporation. @@ -285,9 +291,10 @@ async def asyncio( services), `product` (inventory and COGS) — the `chartTemplates` GraphQL field lists them with names and account counts. Creates the chart, its `coa_mapping` structure and the template's CoA → rs-gaap mapping associations in one transaction, with the equity rows mapped by the entity's legal form - (`entity_type`, defaulting to the graph's primary entity). One-time: 409 once a chart exists — a - chart is never replaced. Customize afterwards with update-taxonomy-block; accounts that carry - activity are never deleted. + (`entity_type`, defaulting to the entity's). Each entity of the group keeps its own chart: + `entity_id` names a subsidiary, omitted means the group parent. One-time per entity: 409 once that + entity has a chart — a chart is never replaced. Customize afterwards with update-taxonomy-block; + accounts that carry activity are never deleted. **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. @@ -298,9 +305,10 @@ async def asyncio( body (InitializeChartOfAccountsRequest): Create the graph's chart of accounts from a shipped template. - Refused (409) when the graph already has an active `chart_of_accounts` + Refused (409) when the entity already has an active `chart_of_accounts` taxonomy — a QuickBooks-synced tenant never needs this, and a chart is - never replaced. The template's equity rows are mapped by the entity's + never replaced. Each entity of the group keeps its own chart; omit + `entity_id` for the group parent. The template's equity rows are mapped by the entity's legal form (`entity_type`: corporation / llc / partnership); omit it to use the graph's primary entity, falling back to corporation. diff --git a/robosystems_client/api/robo_ledger_setup/update_entity.py b/robosystems_client/api/robo_ledger_setup/update_entity.py index 36e6478c..1d9f8d37 100644 --- a/robosystems_client/api/robo_ledger_setup/update_entity.py +++ b/robosystems_client/api/robo_ledger_setup/update_entity.py @@ -113,8 +113,9 @@ def sync_detailed( ) -> Response[ErrorResponse | OperationEnvelopeLedgerEntityResponse]: """Update Entity - Update the graph's primary entity. Only provided (non-null) fields are updated. The graph is - implicit in the URL — the operation always targets the graph's primary entity. + Update an entity of the graph's reporting group. Only provided (non-null) fields are updated. Omit + `entity_id` to target the group parent; name a subsidiary's id to edit it. `ownership_pct` is + refused on the group parent (422), and `ticker` must stay unique in the graph (409). **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. @@ -122,13 +123,13 @@ def sync_detailed( Args: graph_id (str): idempotency_key (None | str | Unset): - body (UpdateEntityRequest): Update the graph's primary entity. All fields are optional — - pass only what changes. Identifiers (CIK, LEI, tax_id) are typically - set once at onboarding; address fields are flattened to make them - easy to project into reporting forms (1099, state filings). + body (UpdateEntityRequest): Update an entity of the graph's reporting group. All fields + are + optional — pass only what changes. Identifiers (CIK, LEI, tax_id) are + typically set once at onboarding; address fields are flattened to make + them easy to project into reporting forms (1099, state filings). - The graph is implicit (URL path) — there's no `entity_id` field - because the operation always targets the graph's primary entity. + Omit `entity_id` to target the group parent. Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -160,8 +161,9 @@ def sync( ) -> ErrorResponse | OperationEnvelopeLedgerEntityResponse | None: """Update Entity - Update the graph's primary entity. Only provided (non-null) fields are updated. The graph is - implicit in the URL — the operation always targets the graph's primary entity. + Update an entity of the graph's reporting group. Only provided (non-null) fields are updated. Omit + `entity_id` to target the group parent; name a subsidiary's id to edit it. `ownership_pct` is + refused on the group parent (422), and `ticker` must stay unique in the graph (409). **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. @@ -169,13 +171,13 @@ def sync( Args: graph_id (str): idempotency_key (None | str | Unset): - body (UpdateEntityRequest): Update the graph's primary entity. All fields are optional — - pass only what changes. Identifiers (CIK, LEI, tax_id) are typically - set once at onboarding; address fields are flattened to make them - easy to project into reporting forms (1099, state filings). + body (UpdateEntityRequest): Update an entity of the graph's reporting group. All fields + are + optional — pass only what changes. Identifiers (CIK, LEI, tax_id) are + typically set once at onboarding; address fields are flattened to make + them easy to project into reporting forms (1099, state filings). - The graph is implicit (URL path) — there's no `entity_id` field - because the operation always targets the graph's primary entity. + Omit `entity_id` to target the group parent. Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -202,8 +204,9 @@ async def asyncio_detailed( ) -> Response[ErrorResponse | OperationEnvelopeLedgerEntityResponse]: """Update Entity - Update the graph's primary entity. Only provided (non-null) fields are updated. The graph is - implicit in the URL — the operation always targets the graph's primary entity. + Update an entity of the graph's reporting group. Only provided (non-null) fields are updated. Omit + `entity_id` to target the group parent; name a subsidiary's id to edit it. `ownership_pct` is + refused on the group parent (422), and `ticker` must stay unique in the graph (409). **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. @@ -211,13 +214,13 @@ async def asyncio_detailed( Args: graph_id (str): idempotency_key (None | str | Unset): - body (UpdateEntityRequest): Update the graph's primary entity. All fields are optional — - pass only what changes. Identifiers (CIK, LEI, tax_id) are typically - set once at onboarding; address fields are flattened to make them - easy to project into reporting forms (1099, state filings). + body (UpdateEntityRequest): Update an entity of the graph's reporting group. All fields + are + optional — pass only what changes. Identifiers (CIK, LEI, tax_id) are + typically set once at onboarding; address fields are flattened to make + them easy to project into reporting forms (1099, state filings). - The graph is implicit (URL path) — there's no `entity_id` field - because the operation always targets the graph's primary entity. + Omit `entity_id` to target the group parent. Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -247,8 +250,9 @@ async def asyncio( ) -> ErrorResponse | OperationEnvelopeLedgerEntityResponse | None: """Update Entity - Update the graph's primary entity. Only provided (non-null) fields are updated. The graph is - implicit in the URL — the operation always targets the graph's primary entity. + Update an entity of the graph's reporting group. Only provided (non-null) fields are updated. Omit + `entity_id` to target the group parent; name a subsidiary's id to edit it. `ownership_pct` is + refused on the group parent (422), and `ticker` must stay unique in the graph (409). **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. @@ -256,13 +260,13 @@ async def asyncio( Args: graph_id (str): idempotency_key (None | str | Unset): - body (UpdateEntityRequest): Update the graph's primary entity. All fields are optional — - pass only what changes. Identifiers (CIK, LEI, tax_id) are typically - set once at onboarding; address fields are flattened to make them - easy to project into reporting forms (1099, state filings). + body (UpdateEntityRequest): Update an entity of the graph's reporting group. All fields + are + optional — pass only what changes. Identifiers (CIK, LEI, tax_id) are + typically set once at onboarding; address fields are flattened to make + them easy to project into reporting forms (1099, state filings). - The graph is implicit (URL path) — there's no `entity_id` field - because the operation always targets the graph's primary entity. + Omit `entity_id` to target the group parent. Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. diff --git a/robosystems_client/graphql/schema.graphql b/robosystems_client/graphql/schema.graphql index 134b868a..b298df3e 100644 --- a/robosystems_client/graphql/schema.graphql +++ b/robosystems_client/graphql/schema.graphql @@ -82,8 +82,14 @@ type Query { portfolioId: String! ): PortfolioBlock - """Return the parent ledger entity (company) for a graph.""" - entity: LedgerEntity + """ + One entity of the graph's reporting group: the named one, else the + group parent. Null on a graph with no entity yet. + """ + entity( + """A subsidiary's id. Omit for the group parent.""" + entityId: String = null + ): LedgerEntity """List entities for a graph, optionally filtered by source.""" entities( @@ -120,22 +126,34 @@ type Query { ): [Agent!]! """ - Graph-wide open AR — total + counterparty count + open invoice count. + One entity's open AR — total + counterparty count + open invoice count. Derived from the event duality chain: sum of unsettled `invoice_issued` / `sales_receipt_recorded` amounts minus discharges pointed at them. """ - openReceivables: OpenBalanceAggregate! + openReceivables( + """The entity whose books to read. Omit for the group parent.""" + entityId: String = null + ): OpenBalanceAggregate! - """Graph-wide open AP — symmetric to `openReceivables`.""" - openPayables: OpenBalanceAggregate! + """One entity's open AP — symmetric to `openReceivables`.""" + openPayables( + """The entity whose books to read. Omit for the group parent.""" + entityId: String = null + ): OpenBalanceAggregate! """Per-counterparty open AR rows, ordered by absolute balance descending.""" - openReceivablesByAgent: [OpenBalanceByAgent!]! + openReceivablesByAgent( + """The entity whose books to read. Omit for the group parent.""" + entityId: String = null + ): [OpenBalanceByAgent!]! """Per-counterparty open AP rows, ordered by absolute balance descending.""" - openPayablesByAgent: [OpenBalanceByAgent!]! + openPayablesByAgent( + """The entity whose books to read. Omit for the group parent.""" + entityId: String = null + ): [OpenBalanceByAgent!]! """Fetch a single event block by id.""" eventBlock( @@ -144,7 +162,7 @@ type Query { ): EventBlock """ - List event blocks with optional filters. + List one entity's event blocks with optional filters. Recent events first (`occurred_at` descending). Filter by `status` (`captured` is the unposted queue, `committed` the audit trail), @@ -185,19 +203,26 @@ type Query { Rows to skip before returning, 0 or greater. Out of range raises `INVALID_PAGINATION`. """ offset: Int = null + + """The entity whose books to read. Omit for the group parent.""" + entityId: String = null ): [EventBlock!]! """ - Ledger counts + date range + connection metadata. + One entity's ledger counts + date range, with the graph's connection + metadata. Counts and dates come from the ledger itself; the connection fields describe the source system feeding it. If connection metadata cannot be read the field still resolves, reporting a zero count and a null timestamp rather than failing the whole query. """ - summary: LedgerSummary + summary( + """The entity whose books to count. Omit for the group parent.""" + entityId: String = null + ): LedgerSummary - """Paginated Chart of Accounts listing.""" + """Paginated Chart of Accounts listing, one entity's chart.""" accounts( """ Filter on the account's statement classification, e.g. `asset` or `revenue`. @@ -216,10 +241,13 @@ type Query { Rows to skip before returning, 0 or greater. Out of range raises `INVALID_PAGINATION`. """ offset: Int = null + + """The entity whose chart to read. Omit for the group parent.""" + entityId: String = null ): AccountList """ - Chart of Accounts as a recursive tree. + One entity's Chart of Accounts as a recursive tree. `includeInactive` defaults to `False` so deleted source-system accounts (still kept in OLTP for historical journal-line FK integrity) @@ -231,12 +259,15 @@ type Query { Include accounts deleted upstream but retained for journal-line integrity. Defaults to false. """ includeInactive: Boolean = null + + """The entity whose chart to read. Omit for the group parent.""" + entityId: String = null ): AccountTree """CoA accounts grouped by reporting element with balances.""" accountRollups( """ - The mapping whose associations group the accounts. Omit for the active mapping. + The mapping whose associations group the accounts. Omit for the entity's active mapping. """ mappingId: String = null @@ -245,10 +276,13 @@ type Query { """End of the balance window (inclusive).""" endDate: Date = null + + """The entity whose books to read. Omit for the group parent.""" + entityId: String = null ): AccountRollups """ - Every reconciliation's standing at a period end. + Every reconciliation's standing at a period end, for one entity. A reconciliation not yet compared for the period is `not_started`. Comparisons are recorded by the `refresh-reconciliations` operation. @@ -256,18 +290,24 @@ type Query { reconciliations( """The period, as YYYY-MM.""" period: String! + + """The entity whose books to read. Omit for the group parent.""" + entityId: String = null ): ReconciliationList - """Trial balance for posted entries in a date range.""" + """One entity's trial balance for posted entries in a date range.""" trialBalance( """Start of the posting window (inclusive).""" startDate: Date = null """End of the posting window (inclusive).""" endDate: Date = null + + """The entity whose books to read. Omit for the group parent.""" + entityId: String = null ): TrialBalance - """Paginated list of transactions.""" + """Paginated list of one entity's transactions.""" transactions( """Filter by transaction type.""" type: String = null @@ -287,6 +327,9 @@ type Query { Rows to skip before returning, 0 or greater. Out of range raises `INVALID_PAGINATION`. """ offset: Int = null + + """The entity whose books to read. Omit for the group parent.""" + entityId: String = null ): LedgerTransactionList """Single transaction with all entries and line items.""" @@ -296,7 +339,8 @@ type Query { ): LedgerTransactionDetail """ - Paginated journal — entries with their line items, newest first. + Paginated journal — one entity's entries with their line items, + newest first. The entry-centric read. `transactions` lists transactions and hangs entries off them, so it cannot show an entry with no parent; the @@ -339,6 +383,9 @@ type Query { Rows to skip before returning, 0 or greater. Out of range raises `INVALID_PAGINATION`. """ offset: Int = null + + """The entity whose books to read. Omit for the group parent.""" + entityId: String = null ): LedgerJournalEntryList """List all active taxonomies, optionally filtered by type.""" @@ -439,13 +486,16 @@ type Query { endDate: Date = null ): MappedTrialBalance - """Close status for all schedules in a fiscal period.""" + """Close status for an entity's schedules in a fiscal period.""" periodCloseStatus( """First day of the fiscal period, as `YYYY-MM-DD`.""" periodStart: Date! """Last day of the fiscal period, as `YYYY-MM-DD`.""" periodEnd: Date! + + """The entity whose schedules to read. Omit for the group parent.""" + entityId: String = null ): PeriodCloseStatus """ @@ -453,11 +503,15 @@ type Query { """ chartTemplates: [ChartTemplate!]! - """Current fiscal calendar state — pointers, gap, closeable status.""" - fiscalCalendar: FiscalCalendar + """An entity's fiscal calendar — pointers, gap, closeable status.""" + fiscalCalendar( + """The entity whose calendar to read. Omit for the group parent.""" + entityId: String = null + ): FiscalCalendar """ - All draft entries for a fiscal period, ready for review before close. + An entity's draft entries for a fiscal period, ready for review + before close. The close-review *outbox*: each draft is annotated with its QB write-back disposition (`willPublishToQb`) and the response carries @@ -467,19 +521,31 @@ type Query { periodDrafts( """The fiscal period, as `YYYY-MM`.""" period: String! + + """The entity whose drafts to list. Omit for the group parent.""" + entityId: String = null ): PeriodDrafts """ - Closing book sidebar navigation (statements, schedules, rollups, etc.). + One entity's closing book sidebar navigation (statements, schedules, + rollups, etc.). """ - closingBookStructures: ClosingBookStructures + closingBookStructures( + """The entity whose books to read. Omit for the group parent.""" + entityId: String = null + ): ClosingBookStructures - """List report definitions for this graph, newest first.""" + """List one entity's report definitions, newest first.""" reports( """ `CURRENT` (the default) leaves out archived reports, `ARCHIVED` returns only those, `ALL` returns every report. """ lifecycle: ReportLifecycle! = CURRENT + + """ + The entity whose reports to list. Omit for the group parent, whose list also carries reports shared in from other graphs. + """ + entityId: String = null ): ReportList """Single report definition with structures + entity name.""" @@ -1384,6 +1450,11 @@ type LedgerEntity { """Parent entity ID for subsidiaries; null for top-level.""" parentEntityId: String + """ + The parent's share of this entity, as a percent (100 = wholly owned). Null on the group parent, and where it was never recorded. + """ + ownershipPct: Float + """Provenance: 'native' | 'sec' | 'quickbooks' | 'xero' | 'plaid'.""" source: String! @@ -1512,6 +1583,9 @@ reflects active integrations (QuickBooks / Plaid / etc.); a non-null """ type LedgerSummary { graphId: String! + + """The entity whose books were counted; null before one exists.""" + entityId: String accountCount: Int! transactionCount: Int! entryCount: Int! @@ -2294,9 +2368,12 @@ type ChartTemplate { accountCount: Int! } -"""Current fiscal calendar state for a graph.""" +"""Current fiscal calendar state for one entity of a graph.""" type FiscalCalendar { graphId: String! + + """The entity whose calendar this is.""" + entityId: String fiscalYearStartMonth: Int! """Latest closed period (YYYY-MM), or null if nothing closed""" @@ -2617,7 +2694,12 @@ type Report { """ structures: [StructureSummary!]! - """Display name of the primary entity the report is tagged to.""" + """ + The entity the report is about. Null on a shared-in copy, and on a report from before entities were recorded, which is the group parent's. + """ + entityId: String + + """Display name of the entity the report is tagged to.""" entityName: String """ diff --git a/robosystems_client/models/__init__.py b/robosystems_client/models/__init__.py index 2dff6541..7983c3a9 100644 --- a/robosystems_client/models/__init__.py +++ b/robosystems_client/models/__init__.py @@ -91,6 +91,7 @@ from .create_checkout_request_resource_config import CreateCheckoutRequestResourceConfig from .create_connection_request import CreateConnectionRequest from .create_connection_request_provider import CreateConnectionRequestProvider +from .create_entity_request import CreateEntityRequest from .create_event_block_request import CreateEventBlockRequest from .create_event_block_request_event_action_type_0 import ( CreateEventBlockRequestEventActionType0, @@ -1182,6 +1183,7 @@ "CreateCheckoutRequestResourceConfig", "CreateConnectionRequest", "CreateConnectionRequestProvider", + "CreateEntityRequest", "CreateEventBlockRequest", "CreateEventBlockRequestEventActionType0", "CreateEventBlockRequestEventCategory", diff --git a/robosystems_client/models/backfill_plan_history_operation.py b/robosystems_client/models/backfill_plan_history_operation.py index aa0d83c9..e03843e2 100644 --- a/robosystems_client/models/backfill_plan_history_operation.py +++ b/robosystems_client/models/backfill_plan_history_operation.py @@ -16,6 +16,8 @@ class BackfillPlanHistoryOperation: """Compile monthly statement history behind the close boundary. Attributes: + entity_id (None | str | Unset): The entity whose books this acts on, by id. Omit for the group parent — the + single-entity default. start_period (None | str | Unset): YYYY-MM period to backfill from. Clamped to the earliest month with ledger data; defaults to that month when omitted. Must be on or before `closed_through`. max_periods (int | Unset): Maximum months to restamp in this call. Each month runs a full reopen → reclose @@ -41,6 +43,7 @@ class BackfillPlanHistoryOperation: note (None | str | Unset): Free-form note attached to each close audit event """ + entity_id: None | str | Unset = UNSET start_period: None | str | Unset = UNSET max_periods: int | Unset = 12 allow_stale_sync: bool | Unset = False @@ -53,6 +56,12 @@ class BackfillPlanHistoryOperation: additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: + entity_id: None | str | Unset + if isinstance(self.entity_id, Unset): + entity_id = UNSET + else: + entity_id = self.entity_id + start_period: None | str | Unset if isinstance(self.start_period, Unset): start_period = UNSET @@ -82,6 +91,8 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) + if entity_id is not UNSET: + field_dict["entity_id"] = entity_id if start_period is not UNSET: field_dict["start_period"] = start_period if max_periods is not UNSET: @@ -107,6 +118,15 @@ def to_dict(self) -> dict[str, Any]: def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: d = dict(src_dict) + def _parse_entity_id(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + entity_id = _parse_entity_id(d.pop("entity_id", UNSET)) + def _parse_start_period(data: object) -> None | str | Unset: if data is None: return data @@ -140,6 +160,7 @@ def _parse_note(data: object) -> None | str | Unset: note = _parse_note(d.pop("note", UNSET)) backfill_plan_history_operation = cls( + entity_id=entity_id, start_period=start_period, max_periods=max_periods, allow_stale_sync=allow_stale_sync, diff --git a/robosystems_client/models/backfill_plan_history_response.py b/robosystems_client/models/backfill_plan_history_response.py index 30232f5c..fe725346 100644 --- a/robosystems_client/models/backfill_plan_history_response.py +++ b/robosystems_client/models/backfill_plan_history_response.py @@ -21,7 +21,7 @@ class BackfillPlanHistoryResponse: """Response from one chunked plan-history backfill call. Attributes: - fiscal_calendar (FiscalCalendarResponse): Current fiscal calendar state for a graph. + fiscal_calendar (FiscalCalendarResponse): Current fiscal calendar state for one entity of a graph. earliest_available_period (str): First month with ledger data — the hard floor for backfill effective_start_period (str): The start actually used after clamping to earliest_available_period closed_through (str): The close boundary the backfill runs up to (inclusive) diff --git a/robosystems_client/models/close_period_operation.py b/robosystems_client/models/close_period_operation.py index 3b1f3480..8f24aeb9 100644 --- a/robosystems_client/models/close_period_operation.py +++ b/robosystems_client/models/close_period_operation.py @@ -19,6 +19,8 @@ class ClosePeriodOperation: Attributes: period (str): Period to close, in YYYY-MM. Must be exactly `closed_through + 1` — close runs sequentially. + entity_id (None | str | Unset): The entity whose books this acts on, by id. Omit for the group parent — the + single-entity default. note (None | str | Unset): Free-form note attached to the close event allow_stale_sync (bool | Unset): Override the sync-currency gate. Only use when you have manually verified that the source data for the period is complete. Default: False. @@ -42,6 +44,7 @@ class ClosePeriodOperation: """ period: str + entity_id: None | str | Unset = UNSET note: None | str | Unset = UNSET allow_stale_sync: bool | Unset = False allow_stranded_obligations: bool | Unset = False @@ -53,6 +56,12 @@ class ClosePeriodOperation: def to_dict(self) -> dict[str, Any]: period = self.period + entity_id: None | str | Unset + if isinstance(self.entity_id, Unset): + entity_id = UNSET + else: + entity_id = self.entity_id + note: None | str | Unset if isinstance(self.note, Unset): note = UNSET @@ -76,6 +85,8 @@ def to_dict(self) -> dict[str, Any]: "period": period, } ) + if entity_id is not UNSET: + field_dict["entity_id"] = entity_id if note is not UNSET: field_dict["note"] = note if allow_stale_sync is not UNSET: @@ -96,6 +107,15 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: d = dict(src_dict) period = d.pop("period") + def _parse_entity_id(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + entity_id = _parse_entity_id(d.pop("entity_id", UNSET)) + def _parse_note(data: object) -> None | str | Unset: if data is None: return data @@ -117,6 +137,7 @@ def _parse_note(data: object) -> None | str | Unset: close_period_operation = cls( period=period, + entity_id=entity_id, note=note, allow_stale_sync=allow_stale_sync, allow_stranded_obligations=allow_stranded_obligations, diff --git a/robosystems_client/models/close_period_response.py b/robosystems_client/models/close_period_response.py index 555f6d3c..a81708b5 100644 --- a/robosystems_client/models/close_period_response.py +++ b/robosystems_client/models/close_period_response.py @@ -29,7 +29,7 @@ class ClosePeriodResponse: """Response from a single-period close operation. Attributes: - fiscal_calendar (FiscalCalendarResponse): Current fiscal calendar state for a graph. + fiscal_calendar (FiscalCalendarResponse): Current fiscal calendar state for one entity of a graph. period (str): entries_posted (int | Unset): Total draft entries the close transitioned to posted, across both post paths (QB pre-publish + local bulk transition). See entries_published_to_qb / entries_posted_locally for the split. diff --git a/robosystems_client/models/create_entity_request.py b/robosystems_client/models/create_entity_request.py new file mode 100644 index 00000000..d99d2cf8 --- /dev/null +++ b/robosystems_client/models/create_entity_request.py @@ -0,0 +1,542 @@ +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="CreateEntityRequest") + + +@_attrs_define +class CreateEntityRequest: + """Add an entity to the graph's reporting group. + + The new entity is a subsidiary of `parent_entity_id`, default the group + parent, and keeps its own books: give it a chart next + (`initialize-chart-of-accounts` with `entity_id`) and a calendar + (`initialize`), then name it with `entity_id` on any ledger operation. + A graph created without an entity gets this one as its group parent. + There is no cap on entities in a graph; capacity is the tier's. + + Attributes: + name (str): Display name. + legal_name (None | str | Unset): Registered legal name. Defaults to `name`. + entity_type (None | str | Unset): Legal form: `corporation`, `llc`, `partnership`, `sole_proprietorship`, + `non_profit`. Picks the default Reporting Style (partnership and llc have equity-form Styles of their own; + anything else is corporate) and the equity rows of a chart template. + reporting_style_id (None | str | Unset): Structure id of the Reporting Style to present under, validated in the + graph like change-reporting-style. Omit to derive it from `entity_type`. + parent_entity_id (None | str | Unset): The entity this one is held under. Omit for the group parent; name a + subsidiary to nest a sub-group under it. + ownership_pct (float | None | Unset): The parent's share of this entity, as a percent (100 = wholly owned). Omit + when not recorded. Refused on a graph's first entity, which becomes the group parent. + ticker (None | str | Unset): Short symbol, unique in the graph; it prefixes the entity's account names + (`coa-:1000`). Derived from the name's initials when omitted. + uri (None | str | Unset): Canonical URL / external identifier. + cik (None | str | Unset): + sic (None | str | Unset): + sic_description (None | str | Unset): + category (None | str | Unset): + state_of_incorporation (None | str | Unset): + fiscal_year_end (None | str | Unset): Fiscal year-end as MM-DD. Defaults to the parent's: a graph has one fiscal + cadence, and every entity's calendar follows it. + tax_id (None | str | Unset): + lei (None | str | Unset): + industry (None | str | Unset): + phone (None | str | Unset): + website (None | str | Unset): + address_line1 (None | str | Unset): + address_city (None | str | Unset): + address_state (None | str | Unset): + address_postal_code (None | str | Unset): + address_country (None | str | Unset): + """ + + name: str + legal_name: None | str | Unset = UNSET + entity_type: None | str | Unset = UNSET + reporting_style_id: None | str | Unset = UNSET + parent_entity_id: None | str | Unset = UNSET + ownership_pct: float | None | Unset = UNSET + ticker: None | str | Unset = UNSET + uri: None | str | Unset = UNSET + cik: None | str | Unset = UNSET + sic: None | str | Unset = UNSET + sic_description: None | str | Unset = UNSET + category: None | str | Unset = UNSET + state_of_incorporation: None | str | Unset = UNSET + fiscal_year_end: None | str | Unset = UNSET + tax_id: None | str | Unset = UNSET + lei: None | str | Unset = UNSET + industry: None | str | Unset = UNSET + phone: None | str | Unset = UNSET + website: None | str | Unset = UNSET + address_line1: None | str | Unset = UNSET + address_city: None | str | Unset = UNSET + address_state: None | str | Unset = UNSET + address_postal_code: None | str | Unset = UNSET + address_country: None | str | Unset = UNSET + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + name = self.name + + legal_name: None | str | Unset + if isinstance(self.legal_name, Unset): + legal_name = UNSET + else: + legal_name = self.legal_name + + entity_type: None | str | Unset + if isinstance(self.entity_type, Unset): + entity_type = UNSET + else: + entity_type = self.entity_type + + reporting_style_id: None | str | Unset + if isinstance(self.reporting_style_id, Unset): + reporting_style_id = UNSET + else: + reporting_style_id = self.reporting_style_id + + parent_entity_id: None | str | Unset + if isinstance(self.parent_entity_id, Unset): + parent_entity_id = UNSET + else: + parent_entity_id = self.parent_entity_id + + ownership_pct: float | None | Unset + if isinstance(self.ownership_pct, Unset): + ownership_pct = UNSET + else: + ownership_pct = self.ownership_pct + + ticker: None | str | Unset + if isinstance(self.ticker, Unset): + ticker = UNSET + else: + ticker = self.ticker + + uri: None | str | Unset + if isinstance(self.uri, Unset): + uri = UNSET + else: + uri = self.uri + + cik: None | str | Unset + if isinstance(self.cik, Unset): + cik = UNSET + else: + cik = self.cik + + sic: None | str | Unset + if isinstance(self.sic, Unset): + sic = UNSET + else: + sic = self.sic + + sic_description: None | str | Unset + if isinstance(self.sic_description, Unset): + sic_description = UNSET + else: + sic_description = self.sic_description + + category: None | str | Unset + if isinstance(self.category, Unset): + category = UNSET + else: + category = self.category + + state_of_incorporation: None | str | Unset + if isinstance(self.state_of_incorporation, Unset): + state_of_incorporation = UNSET + else: + state_of_incorporation = self.state_of_incorporation + + fiscal_year_end: None | str | Unset + if isinstance(self.fiscal_year_end, Unset): + fiscal_year_end = UNSET + else: + fiscal_year_end = self.fiscal_year_end + + tax_id: None | str | Unset + if isinstance(self.tax_id, Unset): + tax_id = UNSET + else: + tax_id = self.tax_id + + lei: None | str | Unset + if isinstance(self.lei, Unset): + lei = UNSET + else: + lei = self.lei + + industry: None | str | Unset + if isinstance(self.industry, Unset): + industry = UNSET + else: + industry = self.industry + + phone: None | str | Unset + if isinstance(self.phone, Unset): + phone = UNSET + else: + phone = self.phone + + website: None | str | Unset + if isinstance(self.website, Unset): + website = UNSET + else: + website = self.website + + address_line1: None | str | Unset + if isinstance(self.address_line1, Unset): + address_line1 = UNSET + else: + address_line1 = self.address_line1 + + address_city: None | str | Unset + if isinstance(self.address_city, Unset): + address_city = UNSET + else: + address_city = self.address_city + + address_state: None | str | Unset + if isinstance(self.address_state, Unset): + address_state = UNSET + else: + address_state = self.address_state + + address_postal_code: None | str | Unset + if isinstance(self.address_postal_code, Unset): + address_postal_code = UNSET + else: + address_postal_code = self.address_postal_code + + address_country: None | str | Unset + if isinstance(self.address_country, Unset): + address_country = UNSET + else: + address_country = self.address_country + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update( + { + "name": name, + } + ) + if legal_name is not UNSET: + field_dict["legal_name"] = legal_name + if entity_type is not UNSET: + field_dict["entity_type"] = entity_type + if reporting_style_id is not UNSET: + field_dict["reporting_style_id"] = reporting_style_id + if parent_entity_id is not UNSET: + field_dict["parent_entity_id"] = parent_entity_id + if ownership_pct is not UNSET: + field_dict["ownership_pct"] = ownership_pct + if ticker is not UNSET: + field_dict["ticker"] = ticker + if uri is not UNSET: + field_dict["uri"] = uri + if cik is not UNSET: + field_dict["cik"] = cik + if sic is not UNSET: + field_dict["sic"] = sic + if sic_description is not UNSET: + field_dict["sic_description"] = sic_description + if category is not UNSET: + field_dict["category"] = category + if state_of_incorporation is not UNSET: + field_dict["state_of_incorporation"] = state_of_incorporation + if fiscal_year_end is not UNSET: + field_dict["fiscal_year_end"] = fiscal_year_end + if tax_id is not UNSET: + field_dict["tax_id"] = tax_id + if lei is not UNSET: + field_dict["lei"] = lei + if industry is not UNSET: + field_dict["industry"] = industry + if phone is not UNSET: + field_dict["phone"] = phone + if website is not UNSET: + field_dict["website"] = website + if address_line1 is not UNSET: + field_dict["address_line1"] = address_line1 + if address_city is not UNSET: + field_dict["address_city"] = address_city + if address_state is not UNSET: + field_dict["address_state"] = address_state + if address_postal_code is not UNSET: + field_dict["address_postal_code"] = address_postal_code + if address_country is not UNSET: + field_dict["address_country"] = address_country + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + name = d.pop("name") + + def _parse_legal_name(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + legal_name = _parse_legal_name(d.pop("legal_name", UNSET)) + + def _parse_entity_type(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + entity_type = _parse_entity_type(d.pop("entity_type", UNSET)) + + def _parse_reporting_style_id(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + reporting_style_id = _parse_reporting_style_id(d.pop("reporting_style_id", UNSET)) + + def _parse_parent_entity_id(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + parent_entity_id = _parse_parent_entity_id(d.pop("parent_entity_id", UNSET)) + + def _parse_ownership_pct(data: object) -> float | None | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(float | None | Unset, data) + + ownership_pct = _parse_ownership_pct(d.pop("ownership_pct", UNSET)) + + def _parse_ticker(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + ticker = _parse_ticker(d.pop("ticker", UNSET)) + + def _parse_uri(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + uri = _parse_uri(d.pop("uri", UNSET)) + + def _parse_cik(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + cik = _parse_cik(d.pop("cik", UNSET)) + + def _parse_sic(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + sic = _parse_sic(d.pop("sic", UNSET)) + + def _parse_sic_description(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + sic_description = _parse_sic_description(d.pop("sic_description", UNSET)) + + def _parse_category(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + category = _parse_category(d.pop("category", UNSET)) + + def _parse_state_of_incorporation(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + state_of_incorporation = _parse_state_of_incorporation( + d.pop("state_of_incorporation", UNSET) + ) + + def _parse_fiscal_year_end(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + fiscal_year_end = _parse_fiscal_year_end(d.pop("fiscal_year_end", UNSET)) + + def _parse_tax_id(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + tax_id = _parse_tax_id(d.pop("tax_id", UNSET)) + + def _parse_lei(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + lei = _parse_lei(d.pop("lei", UNSET)) + + def _parse_industry(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + industry = _parse_industry(d.pop("industry", UNSET)) + + def _parse_phone(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + phone = _parse_phone(d.pop("phone", UNSET)) + + def _parse_website(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + website = _parse_website(d.pop("website", UNSET)) + + def _parse_address_line1(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + address_line1 = _parse_address_line1(d.pop("address_line1", UNSET)) + + def _parse_address_city(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + address_city = _parse_address_city(d.pop("address_city", UNSET)) + + def _parse_address_state(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + address_state = _parse_address_state(d.pop("address_state", UNSET)) + + def _parse_address_postal_code(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + address_postal_code = _parse_address_postal_code( + d.pop("address_postal_code", UNSET) + ) + + def _parse_address_country(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + address_country = _parse_address_country(d.pop("address_country", UNSET)) + + create_entity_request = cls( + name=name, + legal_name=legal_name, + entity_type=entity_type, + reporting_style_id=reporting_style_id, + parent_entity_id=parent_entity_id, + ownership_pct=ownership_pct, + ticker=ticker, + uri=uri, + cik=cik, + sic=sic, + sic_description=sic_description, + category=category, + state_of_incorporation=state_of_incorporation, + fiscal_year_end=fiscal_year_end, + tax_id=tax_id, + lei=lei, + industry=industry, + phone=phone, + website=website, + address_line1=address_line1, + address_city=address_city, + address_state=address_state, + address_postal_code=address_postal_code, + address_country=address_country, + ) + + create_entity_request.additional_properties = d + return create_entity_request + + @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/create_event_block_request.py b/robosystems_client/models/create_event_block_request.py index 610cdf0f..95ddb30b 100644 --- a/robosystems_client/models/create_event_block_request.py +++ b/robosystems_client/models/create_event_block_request.py @@ -43,6 +43,8 @@ class CreateEventBlockRequest: occurred_at (datetime.datetime): When the event happened in the real world source (str): 'manual' | 'system' | 'schedule', a connected provider name (e.g. 'quickbooks'), or a source_name registered via an 'external' connection. Validated against the graph's registered connections. + entity_id (None | str | Unset): The entity whose books the event lands in, by id; every GL row its handler + writes follows it. Omit for the group parent. event_class (CreateEventBlockRequestEventClass | Unset): REA event class. 'economic' events change resources and drive GL postings; 'support' events are audit-trail / value-chain primitives (typically captured with apply_handlers=False). Default: CreateEventBlockRequestEventClass.ECONOMIC. @@ -82,6 +84,7 @@ class CreateEventBlockRequest: event_category: CreateEventBlockRequestEventCategory occurred_at: datetime.datetime source: str + entity_id: None | str | Unset = UNSET event_class: CreateEventBlockRequestEventClass | Unset = ( CreateEventBlockRequestEventClass.ECONOMIC ) @@ -111,6 +114,12 @@ def to_dict(self) -> dict[str, Any]: source = self.source + entity_id: None | str | Unset + if isinstance(self.entity_id, Unset): + entity_id = UNSET + else: + entity_id = self.entity_id + event_class: str | Unset = UNSET if not isinstance(self.event_class, Unset): event_class = self.event_class.value @@ -209,6 +218,8 @@ def to_dict(self) -> dict[str, Any]: "source": source, } ) + if entity_id is not UNSET: + field_dict["entity_id"] = entity_id if event_class is not UNSET: field_dict["event_class"] = event_class if event_action is not UNSET: @@ -259,6 +270,15 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: source = d.pop("source") + def _parse_entity_id(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + entity_id = _parse_entity_id(d.pop("entity_id", UNSET)) + _event_class = d.pop("event_class", UNSET) event_class: CreateEventBlockRequestEventClass | Unset if isinstance(_event_class, Unset): @@ -417,6 +437,7 @@ def _parse_discharges_event_id(data: object) -> None | str | Unset: event_category=event_category, occurred_at=occurred_at, source=source, + entity_id=entity_id, event_class=event_class, event_action=event_action, agent_id=agent_id, diff --git a/robosystems_client/models/create_report_request.py b/robosystems_client/models/create_report_request.py index d5e8e0a3..473bc563 100644 --- a/robosystems_client/models/create_report_request.py +++ b/robosystems_client/models/create_report_request.py @@ -40,6 +40,9 @@ class CreateReportRequest: period_start (datetime.date): Current-period start (inclusive). Ignored when `periods` is supplied. period_end (datetime.date): Current-period end (inclusive). Must be >= `period_start`. Ignored when `periods` is supplied. + entity_id (None | str | Unset): The entity the report is for, by id. Omit for the entity whose chart + `mapping_id` maps from (the group parent on a one-entity graph). Named alongside a mapping of another entity's + chart, it is refused. taxonomy_id (str | Unset): Taxonomy that defines the structures (BS / IS / CF / Equity / Schedules) this report can render. Accepts either an exact tenant-specific taxonomy UUID or a standard name (e.g. 'rs-gaap'). Standard names resolve to the latest reporting_standard taxonomy with that name. Defaults to 'rs-gaap', the canonical @@ -56,6 +59,7 @@ class CreateReportRequest: mapping_id: str period_start: datetime.date period_end: datetime.date + entity_id: None | str | Unset = UNSET taxonomy_id: str | Unset = "rs-gaap" period_type: str | Unset = "quarterly" comparative: bool | Unset = True @@ -71,6 +75,12 @@ def to_dict(self) -> dict[str, Any]: period_end = self.period_end.isoformat() + entity_id: None | str | Unset + if isinstance(self.entity_id, Unset): + entity_id = UNSET + else: + entity_id = self.entity_id + taxonomy_id = self.taxonomy_id period_type = self.period_type @@ -99,6 +109,8 @@ def to_dict(self) -> dict[str, Any]: "period_end": period_end, } ) + if entity_id is not UNSET: + field_dict["entity_id"] = entity_id if taxonomy_id is not UNSET: field_dict["taxonomy_id"] = taxonomy_id if period_type is not UNSET: @@ -123,6 +135,15 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: period_end = datetime.date.fromisoformat(d.pop("period_end")) + def _parse_entity_id(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + entity_id = _parse_entity_id(d.pop("entity_id", UNSET)) + taxonomy_id = d.pop("taxonomy_id", UNSET) period_type = d.pop("period_type", UNSET) @@ -156,6 +177,7 @@ def _parse_periods(data: object) -> list[PeriodSpec] | None | Unset: mapping_id=mapping_id, period_start=period_start, period_end=period_end, + entity_id=entity_id, taxonomy_id=taxonomy_id, period_type=period_type, comparative=comparative, diff --git a/robosystems_client/models/fiscal_calendar_response.py b/robosystems_client/models/fiscal_calendar_response.py index 73232a26..2383dacb 100644 --- a/robosystems_client/models/fiscal_calendar_response.py +++ b/robosystems_client/models/fiscal_calendar_response.py @@ -21,11 +21,12 @@ @_attrs_define class FiscalCalendarResponse: - """Current fiscal calendar state for a graph. + """Current fiscal calendar state for one entity of a graph. Attributes: graph_id (str): fiscal_year_start_month (int): + entity_id (None | str | Unset): The entity whose calendar this is. closed_through (None | str | Unset): Latest closed period (YYYY-MM), or null if nothing closed close_target (None | str | Unset): Target period the user wants closed through (YYYY-MM) gap_periods (int | Unset): Number of periods between closed_through and close_target (inclusive of @@ -75,6 +76,7 @@ class FiscalCalendarResponse: graph_id: str fiscal_year_start_month: int + entity_id: None | str | Unset = UNSET closed_through: None | str | Unset = UNSET close_target: None | str | Unset = UNSET gap_periods: int | Unset = 0 @@ -104,6 +106,12 @@ def to_dict(self) -> dict[str, Any]: fiscal_year_start_month = self.fiscal_year_start_month + entity_id: None | str | Unset + if isinstance(self.entity_id, Unset): + entity_id = UNSET + else: + entity_id = self.entity_id + closed_through: None | str | Unset if isinstance(self.closed_through, Unset): closed_through = UNSET @@ -215,6 +223,8 @@ def to_dict(self) -> dict[str, Any]: "fiscal_year_start_month": fiscal_year_start_month, } ) + if entity_id is not UNSET: + field_dict["entity_id"] = entity_id if closed_through is not UNSET: field_dict["closed_through"] = closed_through if close_target is not UNSET: @@ -274,6 +284,15 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: fiscal_year_start_month = d.pop("fiscal_year_start_month") + def _parse_entity_id(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + entity_id = _parse_entity_id(d.pop("entity_id", UNSET)) + def _parse_closed_through(data: object) -> None | str | Unset: if data is None: return data @@ -425,6 +444,7 @@ def _parse_last_sync_at(data: object) -> datetime.datetime | None | Unset: fiscal_calendar_response = cls( graph_id=graph_id, fiscal_year_start_month=fiscal_year_start_month, + entity_id=entity_id, closed_through=closed_through, close_target=close_target, gap_periods=gap_periods, diff --git a/robosystems_client/models/initialize_chart_of_accounts_request.py b/robosystems_client/models/initialize_chart_of_accounts_request.py index 1970a69f..061e6f41 100644 --- a/robosystems_client/models/initialize_chart_of_accounts_request.py +++ b/robosystems_client/models/initialize_chart_of_accounts_request.py @@ -18,9 +18,10 @@ class InitializeChartOfAccountsRequest: """Create the graph's chart of accounts from a shipped template. - Refused (409) when the graph already has an active `chart_of_accounts` + Refused (409) when the entity already has an active `chart_of_accounts` taxonomy — a QuickBooks-synced tenant never needs this, and a chart is - never replaced. The template's equity rows are mapped by the entity's + never replaced. Each entity of the group keeps its own chart; omit + `entity_id` for the group parent. The template's equity rows are mapped by the entity's legal form (`entity_type`: corporation / llc / partnership); omit it to use the graph's primary entity, falling back to corporation. @@ -28,12 +29,15 @@ class InitializeChartOfAccountsRequest: template (InitializeChartOfAccountsRequestTemplate): Template key: `saas` (subscription software — deferred revenue, cost of revenue, R&D / S&M / G&A), `services` (professional services — no inventory, no COGS), `product` (inventory and cost of goods sold, direct + wholesale + subscription revenue). + entity_id (None | str | Unset): The entity to give a chart to, by id. Omit for the group parent. A sibling + already having one is no bar. entity_type (None | str | Unset): Legal form for the equity mapping: `corporation`, `llc` or `partnership`. Defaults to the graph's primary entity, then to corporation. name (None | str | Unset): Chart display name. Defaults to 'Chart of Accounts'. """ template: InitializeChartOfAccountsRequestTemplate + entity_id: None | str | Unset = UNSET entity_type: None | str | Unset = UNSET name: None | str | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) @@ -41,6 +45,12 @@ class InitializeChartOfAccountsRequest: def to_dict(self) -> dict[str, Any]: template = self.template.value + entity_id: None | str | Unset + if isinstance(self.entity_id, Unset): + entity_id = UNSET + else: + entity_id = self.entity_id + entity_type: None | str | Unset if isinstance(self.entity_type, Unset): entity_type = UNSET @@ -60,6 +70,8 @@ def to_dict(self) -> dict[str, Any]: "template": template, } ) + if entity_id is not UNSET: + field_dict["entity_id"] = entity_id if entity_type is not UNSET: field_dict["entity_type"] = entity_type if name is not UNSET: @@ -72,6 +84,15 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: d = dict(src_dict) template = InitializeChartOfAccountsRequestTemplate(d.pop("template")) + def _parse_entity_id(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + entity_id = _parse_entity_id(d.pop("entity_id", UNSET)) + def _parse_entity_type(data: object) -> None | str | Unset: if data is None: return data @@ -92,6 +113,7 @@ def _parse_name(data: object) -> None | str | Unset: initialize_chart_of_accounts_request = cls( template=template, + entity_id=entity_id, entity_type=entity_type, name=name, ) diff --git a/robosystems_client/models/initialize_ledger_request.py b/robosystems_client/models/initialize_ledger_request.py index 6c7c3907..9ff26ae7 100644 --- a/robosystems_client/models/initialize_ledger_request.py +++ b/robosystems_client/models/initialize_ledger_request.py @@ -29,6 +29,8 @@ class InitializeLedgerRequest: closing toward. Set independently via `set-close-target`. Attributes: + entity_id (None | str | Unset): The entity whose books this acts on, by id. Omit for the group parent — the + single-entity default. closed_through (None | str | Unset): YYYY-MM period. Periods ≤ this date are treated as historical (already closed before the user joined). Set to null for a fresh business with no prior close state. fiscal_year_start_month (int | Unset): Fiscal year start month (1-12). Defaults to calendar year. Default: 1. @@ -39,6 +41,7 @@ class InitializeLedgerRequest: note (None | str | Unset): Free-form note attached to the audit event """ + entity_id: None | str | Unset = UNSET closed_through: None | str | Unset = UNSET fiscal_year_start_month: int | Unset = 1 auto_seed_schedules: bool | Unset = False @@ -47,6 +50,12 @@ class InitializeLedgerRequest: additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: + entity_id: None | str | Unset + if isinstance(self.entity_id, Unset): + entity_id = UNSET + else: + entity_id = self.entity_id + closed_through: None | str | Unset if isinstance(self.closed_through, Unset): closed_through = UNSET @@ -72,6 +81,8 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) + if entity_id is not UNSET: + field_dict["entity_id"] = entity_id if closed_through is not UNSET: field_dict["closed_through"] = closed_through if fiscal_year_start_month is not UNSET: @@ -89,6 +100,15 @@ def to_dict(self) -> dict[str, Any]: def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: d = dict(src_dict) + def _parse_entity_id(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + entity_id = _parse_entity_id(d.pop("entity_id", UNSET)) + def _parse_closed_through(data: object) -> None | str | Unset: if data is None: return data @@ -123,6 +143,7 @@ def _parse_note(data: object) -> None | str | Unset: note = _parse_note(d.pop("note", UNSET)) initialize_ledger_request = cls( + entity_id=entity_id, closed_through=closed_through, fiscal_year_start_month=fiscal_year_start_month, auto_seed_schedules=auto_seed_schedules, diff --git a/robosystems_client/models/initialize_ledger_response.py b/robosystems_client/models/initialize_ledger_response.py index 4726d75f..f8e9233d 100644 --- a/robosystems_client/models/initialize_ledger_response.py +++ b/robosystems_client/models/initialize_ledger_response.py @@ -19,7 +19,7 @@ class InitializeLedgerResponse: """ Attributes: - fiscal_calendar (FiscalCalendarResponse): Current fiscal calendar state for a graph. + fiscal_calendar (FiscalCalendarResponse): Current fiscal calendar state for one entity of a graph. periods_created (int | Unset): Number of FiscalPeriod rows created by initialization Default: 0. warnings (list[str] | Unset): Non-fatal warnings (e.g., auto_seed_schedules not implemented) """ diff --git a/robosystems_client/models/ledger_entity_response.py b/robosystems_client/models/ledger_entity_response.py index f1843257..b76f6176 100644 --- a/robosystems_client/models/ledger_entity_response.py +++ b/robosystems_client/models/ledger_entity_response.py @@ -45,6 +45,8 @@ class LedgerEntityResponse: status (str | Unset): Operational status: 'active' | 'inactive' | 'dissolved'. Default: 'active'. is_parent (bool | Unset): True for top-level entities; False for subsidiaries. Default: True. parent_entity_id (None | str | Unset): Parent entity ID for subsidiaries; null for top-level. + ownership_pct (float | None | Unset): The parent's share of this entity, as a percent (100 = wholly owned). Null + on the group parent, and where it was never recorded. source (str | Unset): Provenance: 'native' | 'sec' | 'quickbooks' | 'xero' | 'plaid'. Default: 'native'. source_id (None | str | Unset): Source-system primary key for sync reconciliation. source_graph_id (None | str | Unset): Origin graph for received entities (cross-graph linking, e.g. RoboInvestor @@ -81,6 +83,7 @@ class LedgerEntityResponse: status: str | Unset = "active" is_parent: bool | Unset = True parent_entity_id: None | str | Unset = UNSET + ownership_pct: float | None | Unset = UNSET source: str | Unset = "native" source_id: None | str | Unset = UNSET source_graph_id: None | str | Unset = UNSET @@ -211,6 +214,12 @@ def to_dict(self) -> dict[str, Any]: else: parent_entity_id = self.parent_entity_id + ownership_pct: float | None | Unset + if isinstance(self.ownership_pct, Unset): + ownership_pct = UNSET + else: + ownership_pct = self.ownership_pct + source = self.source source_id: None | str | Unset @@ -321,6 +330,8 @@ def to_dict(self) -> dict[str, Any]: field_dict["is_parent"] = is_parent if parent_entity_id is not UNSET: field_dict["parent_entity_id"] = parent_entity_id + if ownership_pct is not UNSET: + field_dict["ownership_pct"] = ownership_pct if source is not UNSET: field_dict["source"] = source if source_id is not UNSET: @@ -521,6 +532,15 @@ def _parse_parent_entity_id(data: object) -> None | str | Unset: parent_entity_id = _parse_parent_entity_id(d.pop("parent_entity_id", UNSET)) + def _parse_ownership_pct(data: object) -> float | None | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(float | None | Unset, data) + + ownership_pct = _parse_ownership_pct(d.pop("ownership_pct", UNSET)) + source = d.pop("source", UNSET) def _parse_source_id(data: object) -> None | str | Unset: @@ -638,6 +658,7 @@ def _parse_updated_at(data: object) -> None | str | Unset: status=status, is_parent=is_parent, parent_entity_id=parent_entity_id, + ownership_pct=ownership_pct, source=source, source_id=source_id, source_graph_id=source_graph_id, diff --git a/robosystems_client/models/link_entity_taxonomy_request.py b/robosystems_client/models/link_entity_taxonomy_request.py index 432e148f..6592bf64 100644 --- a/robosystems_client/models/link_entity_taxonomy_request.py +++ b/robosystems_client/models/link_entity_taxonomy_request.py @@ -27,6 +27,7 @@ class LinkEntityTaxonomyRequest: Attributes: taxonomy_id (str): The taxonomy to link to. + entity_id (None | str | Unset): The entity to link, by id. Omit for the group parent. basis (LinkEntityTaxonomyRequestBasis | Unset): Linkage role: `chart_of_accounts` (the entity's CoA), `reporting` (reporting standard like us-gaap), `mapping` (CoA→reporting rollup), `schedule` (schedule structure). Default: LinkEntityTaxonomyRequestBasis.CHART_OF_ACCOUNTS. @@ -37,6 +38,7 @@ class LinkEntityTaxonomyRequest: """ taxonomy_id: str + entity_id: None | str | Unset = UNSET basis: LinkEntityTaxonomyRequestBasis | Unset = ( LinkEntityTaxonomyRequestBasis.CHART_OF_ACCOUNTS ) @@ -47,6 +49,12 @@ class LinkEntityTaxonomyRequest: def to_dict(self) -> dict[str, Any]: taxonomy_id = self.taxonomy_id + entity_id: None | str | Unset + if isinstance(self.entity_id, Unset): + entity_id = UNSET + else: + entity_id = self.entity_id + basis: str | Unset = UNSET if not isinstance(self.basis, Unset): basis = self.basis.value @@ -66,6 +74,8 @@ def to_dict(self) -> dict[str, Any]: "taxonomy_id": taxonomy_id, } ) + if entity_id is not UNSET: + field_dict["entity_id"] = entity_id if basis is not UNSET: field_dict["basis"] = basis if is_primary is not UNSET: @@ -80,6 +90,15 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: d = dict(src_dict) taxonomy_id = d.pop("taxonomy_id") + def _parse_entity_id(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + entity_id = _parse_entity_id(d.pop("entity_id", UNSET)) + _basis = d.pop("basis", UNSET) basis: LinkEntityTaxonomyRequestBasis | Unset if isinstance(_basis, Unset): @@ -100,6 +119,7 @@ def _parse_adoption_context(data: object) -> None | str | Unset: link_entity_taxonomy_request = cls( taxonomy_id=taxonomy_id, + entity_id=entity_id, basis=basis, is_primary=is_primary, adoption_context=adoption_context, diff --git a/robosystems_client/models/live_financial_statement_request.py b/robosystems_client/models/live_financial_statement_request.py index a54ab79b..86bb865a 100644 --- a/robosystems_client/models/live_financial_statement_request.py +++ b/robosystems_client/models/live_financial_statement_request.py @@ -20,25 +20,39 @@ class LiveFinancialStatementRequest: statement_type (str): income_statement | balance_sheet | cash_flow_statement | equity_statement. `equity_statement` is provisional — equity balances, not a rollforward — and is not offered on the MCP surface until it articulates. + entity_id (None | str | Unset): The entity whose books this acts on, by id. Omit for the group parent — the + single-entity default. period_start (datetime.date | None | Unset): Explicit window start. Overrides period_type/fiscal_year. period_end (datetime.date | None | Unset): Explicit window end. Overrides period_type/fiscal_year. period_type (None | str | Unset): annual | quarterly | instant (ignored when dates supplied) fiscal_year (int | None | Unset): Fiscal year for annual window (anchored on FiscalCalendar) + consolidated (bool | Unset): A combined statement, not a consolidation: every entity of the reporting group + rendered under the group parent's Reporting Style and summed per rs-gaap concept, with nothing eliminated + between them. Only on the group parent (422 on a subsidiary). An entity with no CoA mapping yet contributes + nothing and is left out of `combined_entity_ids`. Default: False. limit (int | Unset): Max fact rows returned. Defaults to the ceiling so a statement is never cut mid-section — visible rows would stop footing to visible subtotals. Lower it only for a preview. Default: 1000. """ statement_type: str + entity_id: None | str | Unset = UNSET period_start: datetime.date | None | Unset = UNSET period_end: datetime.date | None | Unset = UNSET period_type: None | str | Unset = UNSET fiscal_year: int | None | Unset = UNSET + consolidated: bool | Unset = False limit: int | Unset = 1000 additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: statement_type = self.statement_type + entity_id: None | str | Unset + if isinstance(self.entity_id, Unset): + entity_id = UNSET + else: + entity_id = self.entity_id + period_start: None | str | Unset if isinstance(self.period_start, Unset): period_start = UNSET @@ -67,6 +81,8 @@ def to_dict(self) -> dict[str, Any]: else: fiscal_year = self.fiscal_year + consolidated = self.consolidated + limit = self.limit field_dict: dict[str, Any] = {} @@ -76,6 +92,8 @@ def to_dict(self) -> dict[str, Any]: "statement_type": statement_type, } ) + if entity_id is not UNSET: + field_dict["entity_id"] = entity_id if period_start is not UNSET: field_dict["period_start"] = period_start if period_end is not UNSET: @@ -84,6 +102,8 @@ def to_dict(self) -> dict[str, Any]: field_dict["period_type"] = period_type if fiscal_year is not UNSET: field_dict["fiscal_year"] = fiscal_year + if consolidated is not UNSET: + field_dict["consolidated"] = consolidated if limit is not UNSET: field_dict["limit"] = limit @@ -94,6 +114,15 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: d = dict(src_dict) statement_type = d.pop("statement_type") + def _parse_entity_id(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + entity_id = _parse_entity_id(d.pop("entity_id", UNSET)) + def _parse_period_start(data: object) -> datetime.date | None | Unset: if data is None: return data @@ -146,14 +175,18 @@ def _parse_fiscal_year(data: object) -> int | None | Unset: fiscal_year = _parse_fiscal_year(d.pop("fiscal_year", UNSET)) + consolidated = d.pop("consolidated", UNSET) + limit = d.pop("limit", UNSET) live_financial_statement_request = cls( statement_type=statement_type, + entity_id=entity_id, period_start=period_start, period_end=period_end, period_type=period_type, fiscal_year=fiscal_year, + consolidated=consolidated, limit=limit, ) diff --git a/robosystems_client/models/live_financial_statement_response.py b/robosystems_client/models/live_financial_statement_response.py index 5dba0c0d..0fbbe232 100644 --- a/robosystems_client/models/live_financial_statement_response.py +++ b/robosystems_client/models/live_financial_statement_response.py @@ -29,6 +29,10 @@ class LiveFinancialStatementResponse: indirect-method delta basis and not rendered. facts (list[LiveStatementFactRow]): fact_count (int): + entity_id (None | str | Unset): The entity whose books were rendered. + consolidated (bool | Unset): Whether the group's entities were summed into this: a combined statement, not a + consolidation — nothing is eliminated between them. Default: False. + combined_entity_ids (list[str] | Unset): The entities summed into a combined statement, parent first. validation (None | Unset | ValidationCheckResponse): Guard-rail outcome for the rendered columns — accounting equation, net-income equation, totals footing, operating-plug size. Null only when no structure rendered. unmapped_count (int | Unset): Default: 0. @@ -40,6 +44,9 @@ class LiveFinancialStatementResponse: periods: list[PeriodSpec] facts: list[LiveStatementFactRow] fact_count: int + entity_id: None | str | Unset = UNSET + consolidated: bool | Unset = False + combined_entity_ids: list[str] | Unset = UNSET validation: None | Unset | ValidationCheckResponse = UNSET unmapped_count: int | Unset = 0 truncated: bool | Unset = False @@ -64,6 +71,18 @@ def to_dict(self) -> dict[str, Any]: fact_count = self.fact_count + entity_id: None | str | Unset + if isinstance(self.entity_id, Unset): + entity_id = UNSET + else: + entity_id = self.entity_id + + consolidated = self.consolidated + + combined_entity_ids: list[str] | Unset = UNSET + if not isinstance(self.combined_entity_ids, Unset): + combined_entity_ids = self.combined_entity_ids + validation: dict[str, Any] | None | Unset if isinstance(self.validation, Unset): validation = UNSET @@ -87,6 +106,12 @@ def to_dict(self) -> dict[str, Any]: "fact_count": fact_count, } ) + if entity_id is not UNSET: + field_dict["entity_id"] = entity_id + if consolidated is not UNSET: + field_dict["consolidated"] = consolidated + if combined_entity_ids is not UNSET: + field_dict["combined_entity_ids"] = combined_entity_ids if validation is not UNSET: field_dict["validation"] = validation if unmapped_count is not UNSET: @@ -123,6 +148,19 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: fact_count = d.pop("fact_count") + def _parse_entity_id(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + entity_id = _parse_entity_id(d.pop("entity_id", UNSET)) + + consolidated = d.pop("consolidated", UNSET) + + combined_entity_ids = cast(list[str], d.pop("combined_entity_ids", UNSET)) + def _parse_validation(data: object) -> None | Unset | ValidationCheckResponse: if data is None: return data @@ -150,6 +188,9 @@ def _parse_validation(data: object) -> None | Unset | ValidationCheckResponse: periods=periods, facts=facts, fact_count=fact_count, + entity_id=entity_id, + consolidated=consolidated, + combined_entity_ids=combined_entity_ids, validation=validation, unmapped_count=unmapped_count, truncated=truncated, diff --git a/robosystems_client/models/reopen_period_operation.py b/robosystems_client/models/reopen_period_operation.py index bf92911b..9dc734a3 100644 --- a/robosystems_client/models/reopen_period_operation.py +++ b/robosystems_client/models/reopen_period_operation.py @@ -20,11 +20,14 @@ class ReopenPeriodOperation: period (str): Period to reopen, in YYYY-MM. Only the latest closed period (the current `closed_through`) can be reopened, and reopening it retreats `closed_through` by one month. To reach an earlier period, reopen the later ones first, latest to earliest. + entity_id (None | str | Unset): The entity whose books this acts on, by id. Omit for the group parent — the + single-entity default. note (None | str | Unset): Additional free-form note """ reason: str period: str + entity_id: None | str | Unset = UNSET note: None | str | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) @@ -33,6 +36,12 @@ def to_dict(self) -> dict[str, Any]: period = self.period + entity_id: None | str | Unset + if isinstance(self.entity_id, Unset): + entity_id = UNSET + else: + entity_id = self.entity_id + note: None | str | Unset if isinstance(self.note, Unset): note = UNSET @@ -47,6 +56,8 @@ def to_dict(self) -> dict[str, Any]: "period": period, } ) + if entity_id is not UNSET: + field_dict["entity_id"] = entity_id if note is not UNSET: field_dict["note"] = note @@ -59,6 +70,15 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: period = d.pop("period") + def _parse_entity_id(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + entity_id = _parse_entity_id(d.pop("entity_id", UNSET)) + def _parse_note(data: object) -> None | str | Unset: if data is None: return data @@ -71,6 +91,7 @@ def _parse_note(data: object) -> None | str | Unset: reopen_period_operation = cls( reason=reason, period=period, + entity_id=entity_id, note=note, ) diff --git a/robosystems_client/models/report_response.py b/robosystems_client/models/report_response.py index 9de5860a..c926c1ca 100644 --- a/robosystems_client/models/report_response.py +++ b/robosystems_client/models/report_response.py @@ -47,7 +47,9 @@ class ReportResponse: last_generated (datetime.datetime | None | Unset): When the facts were last (re)generated. structures (list[StructureSummary] | Unset): Structures available for this report's taxonomy — renderable sections (BS / IS / CF / Equity / Schedules). - entity_name (None | str | Unset): Display name of the primary entity the report is tagged to. + entity_id (None | str | Unset): The entity the report is about. Null on a shared-in copy, and on a report from + before entities were recorded, which is the group parent's. + entity_name (None | str | Unset): Display name of the entity the report is tagged to. filing_status (str | Unset): Filing lifecycle (orthogonal to `generation_status`): `draft`, `under_review`, `filed`, `archived`. Default: 'draft'. filed_at (datetime.datetime | None | Unset): When the report was transitioned to `filed`. On a report shared in @@ -81,6 +83,7 @@ class ReportResponse: ai_generated: bool | Unset = False last_generated: datetime.datetime | None | Unset = UNSET structures: list[StructureSummary] | Unset = UNSET + entity_id: None | str | Unset = UNSET entity_name: None | str | Unset = UNSET filing_status: str | Unset = "draft" filed_at: datetime.datetime | None | Unset = UNSET @@ -163,6 +166,12 @@ def to_dict(self) -> dict[str, Any]: structures_item = structures_item_data.to_dict() structures.append(structures_item) + entity_id: None | str | Unset + if isinstance(self.entity_id, Unset): + entity_id = UNSET + else: + entity_id = self.entity_id + entity_name: None | str | Unset if isinstance(self.entity_name, Unset): entity_name = UNSET @@ -252,6 +261,8 @@ def to_dict(self) -> dict[str, Any]: field_dict["last_generated"] = last_generated if structures is not UNSET: field_dict["structures"] = structures + if entity_id is not UNSET: + field_dict["entity_id"] = entity_id if entity_name is not UNSET: field_dict["entity_name"] = entity_name if filing_status is not UNSET: @@ -391,6 +402,15 @@ def _parse_last_generated(data: object) -> datetime.datetime | None | Unset: structures.append(structures_item) + def _parse_entity_id(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + entity_id = _parse_entity_id(d.pop("entity_id", UNSET)) + def _parse_entity_name(data: object) -> None | str | Unset: if data is None: return data @@ -515,6 +535,7 @@ def _parse_rule_summary( ai_generated=ai_generated, last_generated=last_generated, structures=structures, + entity_id=entity_id, entity_name=entity_name, filing_status=filing_status, filed_at=filed_at, diff --git a/robosystems_client/models/set_close_target_operation.py b/robosystems_client/models/set_close_target_operation.py index 67edb5d6..fafe8525 100644 --- a/robosystems_client/models/set_close_target_operation.py +++ b/robosystems_client/models/set_close_target_operation.py @@ -16,16 +16,25 @@ class SetCloseTargetOperation: """ Attributes: period (str): Target period in YYYY-MM format. Must be > current `closed_through`. + entity_id (None | str | Unset): The entity whose books this acts on, by id. Omit for the group parent — the + single-entity default. note (None | str | Unset): Free-form note attached to the audit event """ period: str + entity_id: None | str | Unset = UNSET note: None | str | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: period = self.period + entity_id: None | str | Unset + if isinstance(self.entity_id, Unset): + entity_id = UNSET + else: + entity_id = self.entity_id + note: None | str | Unset if isinstance(self.note, Unset): note = UNSET @@ -39,6 +48,8 @@ def to_dict(self) -> dict[str, Any]: "period": period, } ) + if entity_id is not UNSET: + field_dict["entity_id"] = entity_id if note is not UNSET: field_dict["note"] = note @@ -49,6 +60,15 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: d = dict(src_dict) period = d.pop("period") + def _parse_entity_id(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + entity_id = _parse_entity_id(d.pop("entity_id", UNSET)) + def _parse_note(data: object) -> None | str | Unset: if data is None: return data @@ -60,6 +80,7 @@ def _parse_note(data: object) -> None | str | Unset: set_close_target_operation = cls( period=period, + entity_id=entity_id, note=note, ) diff --git a/robosystems_client/models/update_entity_request.py b/robosystems_client/models/update_entity_request.py index 8abb85e1..4111d88b 100644 --- a/robosystems_client/models/update_entity_request.py +++ b/robosystems_client/models/update_entity_request.py @@ -13,15 +13,16 @@ @_attrs_define class UpdateEntityRequest: - """Update the graph's primary entity. All fields are optional — - pass only what changes. Identifiers (CIK, LEI, tax_id) are typically - set once at onboarding; address fields are flattened to make them - easy to project into reporting forms (1099, state filings). + """Update an entity of the graph's reporting group. All fields are + optional — pass only what changes. Identifiers (CIK, LEI, tax_id) are + typically set once at onboarding; address fields are flattened to make + them easy to project into reporting forms (1099, state filings). - The graph is implicit (URL path) — there's no `entity_id` field - because the operation always targets the graph's primary entity. + Omit `entity_id` to target the group parent. Attributes: + entity_id (None | str | Unset): The entity to update. Omit to target the group parent — the single-entity + default. name (None | str | Unset): legal_name (None | str | Unset): uri (None | str | Unset): @@ -44,8 +45,11 @@ class UpdateEntityRequest: address_state (None | str | Unset): address_postal_code (None | str | Unset): address_country (None | str | Unset): + ownership_pct (float | None | Unset): The parent's share of this entity, as a percent. Refused on the group + parent, which has no owner in the graph. """ + entity_id: None | str | Unset = UNSET name: None | str | Unset = UNSET legal_name: None | str | Unset = UNSET uri: None | str | Unset = UNSET @@ -68,9 +72,16 @@ class UpdateEntityRequest: address_state: None | str | Unset = UNSET address_postal_code: None | str | Unset = UNSET address_country: None | str | Unset = UNSET + ownership_pct: float | None | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: + entity_id: None | str | Unset + if isinstance(self.entity_id, Unset): + entity_id = UNSET + else: + entity_id = self.entity_id + name: None | str | Unset if isinstance(self.name, Unset): name = UNSET @@ -203,9 +214,17 @@ def to_dict(self) -> dict[str, Any]: else: address_country = self.address_country + ownership_pct: float | None | Unset + if isinstance(self.ownership_pct, Unset): + ownership_pct = UNSET + else: + ownership_pct = self.ownership_pct + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) + if entity_id is not UNSET: + field_dict["entity_id"] = entity_id if name is not UNSET: field_dict["name"] = name if legal_name is not UNSET: @@ -250,6 +269,8 @@ def to_dict(self) -> dict[str, Any]: field_dict["address_postal_code"] = address_postal_code if address_country is not UNSET: field_dict["address_country"] = address_country + if ownership_pct is not UNSET: + field_dict["ownership_pct"] = ownership_pct return field_dict @@ -257,6 +278,15 @@ def to_dict(self) -> dict[str, Any]: def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: d = dict(src_dict) + def _parse_entity_id(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + entity_id = _parse_entity_id(d.pop("entity_id", UNSET)) + def _parse_name(data: object) -> None | str | Unset: if data is None: return data @@ -459,7 +489,17 @@ def _parse_address_country(data: object) -> None | str | Unset: address_country = _parse_address_country(d.pop("address_country", UNSET)) + def _parse_ownership_pct(data: object) -> float | None | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(float | None | Unset, data) + + ownership_pct = _parse_ownership_pct(d.pop("ownership_pct", UNSET)) + update_entity_request = cls( + entity_id=entity_id, name=name, legal_name=legal_name, uri=uri, @@ -482,6 +522,7 @@ def _parse_address_country(data: object) -> None | str | Unset: address_state=address_state, address_postal_code=address_postal_code, address_country=address_country, + ownership_pct=ownership_pct, ) update_entity_request.additional_properties = d