diff --git a/robosystems_client/api/connections/set_connection_write_policy.py b/robosystems_client/api/connections/set_connection_write_policy.py new file mode 100644 index 0000000..985293a --- /dev/null +++ b/robosystems_client/api/connections/set_connection_write_policy.py @@ -0,0 +1,259 @@ +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.connection_response import ConnectionResponse +from ...models.error_response import ErrorResponse +from ...models.http_validation_error import HTTPValidationError +from ...models.set_write_policy_request import SetWritePolicyRequest +from ...types import Response + + +def _get_kwargs( + graph_id: str, + connection_id: str, + *, + body: SetWritePolicyRequest, +) -> dict[str, Any]: + headers: dict[str, Any] = {} + + _kwargs: dict[str, Any] = { + "method": "put", + "url": "/v1/graphs/{graph_id}/connections/{connection_id}/write-policy".format( + graph_id=quote(str(graph_id), safe=""), + connection_id=quote(str(connection_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 +) -> ConnectionResponse | ErrorResponse | HTTPValidationError | None: + if response.status_code == 200: + response_200 = ConnectionResponse.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[ConnectionResponse | ErrorResponse | HTTPValidationError]: + 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, + connection_id: str, + *, + client: AuthenticatedClient, + body: SetWritePolicyRequest, +) -> Response[ConnectionResponse | ErrorResponse | HTTPValidationError]: + """Set Connection Write Policy + + Opt a connection into or out of outbound write-back. 'qb_authoritative' makes QuickBooks the source + of truth — RoboSystems-originated entries (manual JEs, schedule drafts) publish to QuickBooks when + executed or at close. 'native' keeps RoboSystems authoritative with no write-back. This is the + explicit operator opt-in for writing to your books of record. + + Args: + graph_id (str): + connection_id (str): Unique connection identifier + body (SetWritePolicyRequest): Request to set a connection's source-of-truth write policy. + + The explicit operator opt-in for outbound write-back. `hybrid` is omitted + until its code path ships. + + 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[ConnectionResponse | ErrorResponse | HTTPValidationError] + """ + + kwargs = _get_kwargs( + graph_id=graph_id, + connection_id=connection_id, + body=body, + ) + + response = client.get_httpx_client().request( + **kwargs, + ) + + return _build_response(client=client, response=response) + + +def sync( + graph_id: str, + connection_id: str, + *, + client: AuthenticatedClient, + body: SetWritePolicyRequest, +) -> ConnectionResponse | ErrorResponse | HTTPValidationError | None: + """Set Connection Write Policy + + Opt a connection into or out of outbound write-back. 'qb_authoritative' makes QuickBooks the source + of truth — RoboSystems-originated entries (manual JEs, schedule drafts) publish to QuickBooks when + executed or at close. 'native' keeps RoboSystems authoritative with no write-back. This is the + explicit operator opt-in for writing to your books of record. + + Args: + graph_id (str): + connection_id (str): Unique connection identifier + body (SetWritePolicyRequest): Request to set a connection's source-of-truth write policy. + + The explicit operator opt-in for outbound write-back. `hybrid` is omitted + until its code path ships. + + 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: + ConnectionResponse | ErrorResponse | HTTPValidationError + """ + + return sync_detailed( + graph_id=graph_id, + connection_id=connection_id, + client=client, + body=body, + ).parsed + + +async def asyncio_detailed( + graph_id: str, + connection_id: str, + *, + client: AuthenticatedClient, + body: SetWritePolicyRequest, +) -> Response[ConnectionResponse | ErrorResponse | HTTPValidationError]: + """Set Connection Write Policy + + Opt a connection into or out of outbound write-back. 'qb_authoritative' makes QuickBooks the source + of truth — RoboSystems-originated entries (manual JEs, schedule drafts) publish to QuickBooks when + executed or at close. 'native' keeps RoboSystems authoritative with no write-back. This is the + explicit operator opt-in for writing to your books of record. + + Args: + graph_id (str): + connection_id (str): Unique connection identifier + body (SetWritePolicyRequest): Request to set a connection's source-of-truth write policy. + + The explicit operator opt-in for outbound write-back. `hybrid` is omitted + until its code path ships. + + 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[ConnectionResponse | ErrorResponse | HTTPValidationError] + """ + + kwargs = _get_kwargs( + graph_id=graph_id, + connection_id=connection_id, + body=body, + ) + + response = await client.get_async_httpx_client().request(**kwargs) + + return _build_response(client=client, response=response) + + +async def asyncio( + graph_id: str, + connection_id: str, + *, + client: AuthenticatedClient, + body: SetWritePolicyRequest, +) -> ConnectionResponse | ErrorResponse | HTTPValidationError | None: + """Set Connection Write Policy + + Opt a connection into or out of outbound write-back. 'qb_authoritative' makes QuickBooks the source + of truth — RoboSystems-originated entries (manual JEs, schedule drafts) publish to QuickBooks when + executed or at close. 'native' keeps RoboSystems authoritative with no write-back. This is the + explicit operator opt-in for writing to your books of record. + + Args: + graph_id (str): + connection_id (str): Unique connection identifier + body (SetWritePolicyRequest): Request to set a connection's source-of-truth write policy. + + The explicit operator opt-in for outbound write-back. `hybrid` is omitted + until its code path ships. + + 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: + ConnectionResponse | ErrorResponse | HTTPValidationError + """ + + return ( + await asyncio_detailed( + graph_id=graph_id, + connection_id=connection_id, + client=client, + body=body, + ) + ).parsed diff --git a/robosystems_client/api/extensions_robo_ledger/op_promote_obligations.py b/robosystems_client/api/extensions_robo_ledger/op_promote_obligations.py new file mode 100644 index 0000000..3edea99 --- /dev/null +++ b/robosystems_client/api/extensions_robo_ledger/op_promote_obligations.py @@ -0,0 +1,312 @@ +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.operation_envelope_promote_obligations_response import ( + OperationEnvelopePromoteObligationsResponse, +) +from ...models.promote_obligations_request import PromoteObligationsRequest +from ...types import UNSET, Response, Unset + + +def _get_kwargs( + graph_id: str, + *, + body: PromoteObligationsRequest, + 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/promote-obligations".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 | OperationEnvelopePromoteObligationsResponse | None: + if response.status_code == 200: + response_200 = OperationEnvelopePromoteObligationsResponse.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 | OperationEnvelopePromoteObligationsResponse]: + 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: PromoteObligationsRequest, + idempotency_key: None | str | Unset = UNSET, +) -> Response[ErrorResponse | OperationEnvelopePromoteObligationsResponse]: + """Promote Due Schedule Obligations + + Promote matured pending schedule obligations (schedule_entry_due events whose period boundary has + passed) to 'classified', and — when dispatch_handlers=true (default) — draft their closing entries + in the same transaction. This is the on-demand form of the background obligation-promotion sweep; + run it before close-period when a schedule was just created or when you can't wait for the Dagster + sensor. Idempotent: re-running skips already-classified obligations and reconciles to existing + drafts. + + **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 (PromoteObligationsRequest): On-demand trigger for the obligation-promotion sweep. + + Mirrors what the ``scheduled_obligation_promoter`` Dagster sensor does + on its tick, but lets an interactive caller or an MCP close co-pilot + run it now instead of waiting for the background cadence — required to + drive a schedule-driven close to completion in a single session. + Flips matured ``pending`` ``schedule_entry_due`` events (period boundary + passed) to ``classified``; with ``dispatch_handlers`` it also drafts the + closing entries in the same transaction (idempotent — reconciles to an + existing draft). + + 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 | OperationEnvelopePromoteObligationsResponse] + """ + + 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: PromoteObligationsRequest, + idempotency_key: None | str | Unset = UNSET, +) -> ErrorResponse | OperationEnvelopePromoteObligationsResponse | None: + """Promote Due Schedule Obligations + + Promote matured pending schedule obligations (schedule_entry_due events whose period boundary has + passed) to 'classified', and — when dispatch_handlers=true (default) — draft their closing entries + in the same transaction. This is the on-demand form of the background obligation-promotion sweep; + run it before close-period when a schedule was just created or when you can't wait for the Dagster + sensor. Idempotent: re-running skips already-classified obligations and reconciles to existing + drafts. + + **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 (PromoteObligationsRequest): On-demand trigger for the obligation-promotion sweep. + + Mirrors what the ``scheduled_obligation_promoter`` Dagster sensor does + on its tick, but lets an interactive caller or an MCP close co-pilot + run it now instead of waiting for the background cadence — required to + drive a schedule-driven close to completion in a single session. + Flips matured ``pending`` ``schedule_entry_due`` events (period boundary + passed) to ``classified``; with ``dispatch_handlers`` it also drafts the + closing entries in the same transaction (idempotent — reconciles to an + existing draft). + + 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 | OperationEnvelopePromoteObligationsResponse + """ + + 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: PromoteObligationsRequest, + idempotency_key: None | str | Unset = UNSET, +) -> Response[ErrorResponse | OperationEnvelopePromoteObligationsResponse]: + """Promote Due Schedule Obligations + + Promote matured pending schedule obligations (schedule_entry_due events whose period boundary has + passed) to 'classified', and — when dispatch_handlers=true (default) — draft their closing entries + in the same transaction. This is the on-demand form of the background obligation-promotion sweep; + run it before close-period when a schedule was just created or when you can't wait for the Dagster + sensor. Idempotent: re-running skips already-classified obligations and reconciles to existing + drafts. + + **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 (PromoteObligationsRequest): On-demand trigger for the obligation-promotion sweep. + + Mirrors what the ``scheduled_obligation_promoter`` Dagster sensor does + on its tick, but lets an interactive caller or an MCP close co-pilot + run it now instead of waiting for the background cadence — required to + drive a schedule-driven close to completion in a single session. + Flips matured ``pending`` ``schedule_entry_due`` events (period boundary + passed) to ``classified``; with ``dispatch_handlers`` it also drafts the + closing entries in the same transaction (idempotent — reconciles to an + existing draft). + + 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 | OperationEnvelopePromoteObligationsResponse] + """ + + 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: PromoteObligationsRequest, + idempotency_key: None | str | Unset = UNSET, +) -> ErrorResponse | OperationEnvelopePromoteObligationsResponse | None: + """Promote Due Schedule Obligations + + Promote matured pending schedule obligations (schedule_entry_due events whose period boundary has + passed) to 'classified', and — when dispatch_handlers=true (default) — draft their closing entries + in the same transaction. This is the on-demand form of the background obligation-promotion sweep; + run it before close-period when a schedule was just created or when you can't wait for the Dagster + sensor. Idempotent: re-running skips already-classified obligations and reconciles to existing + drafts. + + **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 (PromoteObligationsRequest): On-demand trigger for the obligation-promotion sweep. + + Mirrors what the ``scheduled_obligation_promoter`` Dagster sensor does + on its tick, but lets an interactive caller or an MCP close co-pilot + run it now instead of waiting for the background cadence — required to + drive a schedule-driven close to completion in a single session. + Flips matured ``pending`` ``schedule_entry_due`` events (period boundary + passed) to ``classified``; with ``dispatch_handlers`` it also drafts the + closing entries in the same transaction (idempotent — reconciles to an + existing draft). + + 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 | OperationEnvelopePromoteObligationsResponse + """ + + return ( + await asyncio_detailed( + graph_id=graph_id, + client=client, + body=body, + idempotency_key=idempotency_key, + ) + ).parsed diff --git a/robosystems_client/graphql/queries/ledger/__init__.py b/robosystems_client/graphql/queries/ledger/__init__.py index 1c02009..55107b6 100644 --- a/robosystems_client/graphql/queries/ledger/__init__.py +++ b/robosystems_client/graphql/queries/ledger/__init__.py @@ -747,10 +747,11 @@ def parse_period_close_status(data: dict[str, Any]) -> dict[str, Any] | None: periodDrafts(period: $period) { period periodStart periodEnd draftCount totalDebit totalCredit allBalanced + qbWritebackConnectionId qbWritePolicy qbPublishCount localOnlyCount drafts { entryId postingDate type memo provenance sourceStructureId sourceStructureName - totalDebit totalCredit balanced + totalDebit totalCredit balanced willPublishToQb lineItems { lineItemId elementId elementCode elementName debitAmount creditAmount description diff --git a/robosystems_client/models/__init__.py b/robosystems_client/models/__init__.py index f25c20a..9b42177 100644 --- a/robosystems_client/models/__init__.py +++ b/robosystems_client/models/__init__.py @@ -396,6 +396,12 @@ from .operation_envelope_preview_event_block_response_status import ( OperationEnvelopePreviewEventBlockResponseStatus, ) +from .operation_envelope_promote_obligations_response import ( + OperationEnvelopePromoteObligationsResponse, +) +from .operation_envelope_promote_obligations_response_status import ( + OperationEnvelopePromoteObligationsResponseStatus, +) from .operation_envelope_publish_list_response import ( OperationEnvelopePublishListResponse, ) @@ -491,6 +497,11 @@ from .preview_event_block_response_handler_metadata import ( PreviewEventBlockResponseHandlerMetadata, ) +from .promote_obligations_request import PromoteObligationsRequest +from .promote_obligations_response import PromoteObligationsResponse +from .promote_obligations_response_errors_item import ( + PromoteObligationsResponseErrorsItem, +) from .publish_list_member_response import PublishListMemberResponse from .publish_list_response import PublishListResponse from .query_limits import QueryLimits @@ -548,6 +559,8 @@ from .service_offering_summary import ServiceOfferingSummary from .service_offerings_response import ServiceOfferingsResponse from .set_close_target_operation import SetCloseTargetOperation +from .set_write_policy_request import SetWritePolicyRequest +from .set_write_policy_request_write_policy import SetWritePolicyRequestWritePolicy from .share_report_operation import ShareReportOperation from .share_report_response import ShareReportResponse from .share_result_item import ShareResultItem @@ -963,6 +976,8 @@ "OperationEnvelopePortfolioBlockEnvelopeStatus", "OperationEnvelopePreviewEventBlockResponse", "OperationEnvelopePreviewEventBlockResponseStatus", + "OperationEnvelopePromoteObligationsResponse", + "OperationEnvelopePromoteObligationsResponseStatus", "OperationEnvelopePublishListResponse", "OperationEnvelopePublishListResponseStatus", "OperationEnvelopeReportResponse", @@ -1030,6 +1045,9 @@ "PositionBlock", "PreviewEventBlockResponse", "PreviewEventBlockResponseHandlerMetadata", + "PromoteObligationsRequest", + "PromoteObligationsResponse", + "PromoteObligationsResponseErrorsItem", "PublishListMemberResponse", "PublishListResponse", "QueryLimits", @@ -1079,6 +1097,8 @@ "ServiceOfferingsResponse", "ServiceOfferingSummary", "SetCloseTargetOperation", + "SetWritePolicyRequest", + "SetWritePolicyRequestWritePolicy", "ShareReportOperation", "ShareReportResponse", "ShareResultItem", diff --git a/robosystems_client/models/connection_response.py b/robosystems_client/models/connection_response.py index c5ce7bf..a92c2bd 100644 --- a/robosystems_client/models/connection_response.py +++ b/robosystems_client/models/connection_response.py @@ -30,6 +30,9 @@ class ConnectionResponse: entity_id (None | str | Unset): Entity identifier updated_at (datetime.datetime | None | str | Unset): Last update timestamp last_sync (datetime.datetime | None | str | Unset): Last sync timestamp + write_policy (None | str | Unset): Source-of-truth write policy: 'native' (RoboSystems is authoritative; no + outbound write-back) or 'qb_authoritative' (QuickBooks is authoritative; RoboSystems-originated entries publish + to QB). Set via the write-policy endpoint. """ connection_id: str @@ -40,6 +43,7 @@ class ConnectionResponse: entity_id: None | str | Unset = UNSET updated_at: datetime.datetime | None | str | Unset = UNSET last_sync: datetime.datetime | None | str | Unset = UNSET + write_policy: None | str | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: @@ -79,6 +83,12 @@ def to_dict(self) -> dict[str, Any]: else: last_sync = self.last_sync + write_policy: None | str | Unset + if isinstance(self.write_policy, Unset): + write_policy = UNSET + else: + write_policy = self.write_policy + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update( @@ -96,6 +106,8 @@ def to_dict(self) -> dict[str, Any]: field_dict["updated_at"] = updated_at if last_sync is not UNSET: field_dict["last_sync"] = last_sync + if write_policy is not UNSET: + field_dict["write_policy"] = write_policy return field_dict @@ -168,6 +180,15 @@ def _parse_last_sync(data: object) -> datetime.datetime | None | str | Unset: last_sync = _parse_last_sync(d.pop("last_sync", UNSET)) + def _parse_write_policy(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + write_policy = _parse_write_policy(d.pop("write_policy", UNSET)) + connection_response = cls( connection_id=connection_id, provider=provider, @@ -177,6 +198,7 @@ def _parse_last_sync(data: object) -> datetime.datetime | None | str | Unset: entity_id=entity_id, updated_at=updated_at, last_sync=last_sync, + write_policy=write_policy, ) connection_response.additional_properties = d diff --git a/robosystems_client/models/create_event_block_request.py b/robosystems_client/models/create_event_block_request.py index 28950af..50da6aa 100644 --- a/robosystems_client/models/create_event_block_request.py +++ b/robosystems_client/models/create_event_block_request.py @@ -42,7 +42,7 @@ class CreateEventBlockRequest: (control, approval, reconciliation, inquiry) require event_class='support'. The DB CHECK rejects mismatched pairings. occurred_at (datetime.datetime): When the event happened in the real world - source (str): 'quickbooks' | 'xero' | 'plaid' | 'native' | 'scheduled' | ... + source (str): 'manual' | 'system' | 'schedule' | 'quickbooks' | 'xero' | 'plaid' 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. diff --git a/robosystems_client/models/event_block_envelope.py b/robosystems_client/models/event_block_envelope.py index c6daafd..589c0c2 100644 --- a/robosystems_client/models/event_block_envelope.py +++ b/robosystems_client/models/event_block_envelope.py @@ -39,7 +39,7 @@ class EventBlockEnvelope: `fulfilled` (obligation discharged), `voided` (canceled — terminal), `superseded` (replaced by a corrected event — terminal). See `UpdateEventBlockRequest.transition_to` for the valid transition graph. occurred_at (datetime.datetime): When the event happened in the real world (UTC). - source (str): Capture source (`quickbooks`, `xero`, `plaid`, `native`, `scheduled`, …). Used for adapter + source (str): Capture source (`manual`, `system`, `schedule`, `quickbooks`, `xero`, `plaid`). Used for adapter routing. currency (str): ISO 4217 currency code for `amount`. metadata (EventBlockEnvelopeMetadata): Free-form payload — handler-specific keys when the event ran through a diff --git a/robosystems_client/models/operation_envelope_promote_obligations_response.py b/robosystems_client/models/operation_envelope_promote_obligations_response.py new file mode 100644 index 0000000..79fb121 --- /dev/null +++ b/robosystems_client/models/operation_envelope_promote_obligations_response.py @@ -0,0 +1,158 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import TYPE_CHECKING, Any, TypeVar, cast + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +from ..models.operation_envelope_promote_obligations_response_status import ( + OperationEnvelopePromoteObligationsResponseStatus, +) +from ..types import UNSET, Unset + +if TYPE_CHECKING: + from ..models.promote_obligations_response import PromoteObligationsResponse + + +T = TypeVar("T", bound="OperationEnvelopePromoteObligationsResponse") + + +@_attrs_define +class OperationEnvelopePromoteObligationsResponse: + """ + Attributes: + operation (str): Kebab-case operation name + operation_id (str): op_-prefixed ULID for audit and SSE correlation + status (OperationEnvelopePromoteObligationsResponseStatus): Operation lifecycle state + at (str): ISO-8601 UTC timestamp + result (None | PromoteObligationsResponse | Unset): Command-specific result payload + created_by (None | str | Unset): User ID that initiated the operation (null for legacy callers) + idempotent_replay (bool | Unset): True when this envelope came from the idempotency cache — the underlying + command did not execute again. False on fresh executions. Default: False. + """ + + operation: str + operation_id: str + status: OperationEnvelopePromoteObligationsResponseStatus + at: str + result: None | PromoteObligationsResponse | Unset = UNSET + created_by: None | str | Unset = UNSET + idempotent_replay: bool | Unset = False + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + from ..models.promote_obligations_response import PromoteObligationsResponse + + operation = self.operation + + operation_id = self.operation_id + + status = self.status.value + + at = self.at + + result: dict[str, Any] | None | Unset + if isinstance(self.result, Unset): + result = UNSET + elif isinstance(self.result, PromoteObligationsResponse): + result = self.result.to_dict() + else: + result = self.result + + created_by: None | str | Unset + if isinstance(self.created_by, Unset): + created_by = UNSET + else: + created_by = self.created_by + + idempotent_replay = self.idempotent_replay + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update( + { + "operation": operation, + "operationId": operation_id, + "status": status, + "at": at, + } + ) + if result is not UNSET: + field_dict["result"] = result + if created_by is not UNSET: + field_dict["createdBy"] = created_by + if idempotent_replay is not UNSET: + field_dict["idempotentReplay"] = idempotent_replay + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + from ..models.promote_obligations_response import PromoteObligationsResponse + + d = dict(src_dict) + operation = d.pop("operation") + + operation_id = d.pop("operationId") + + status = OperationEnvelopePromoteObligationsResponseStatus(d.pop("status")) + + at = d.pop("at") + + def _parse_result(data: object) -> None | PromoteObligationsResponse | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + try: + if not isinstance(data, dict): + raise TypeError() + result_type_0 = PromoteObligationsResponse.from_dict(data) + + return result_type_0 + except (TypeError, ValueError, AttributeError, KeyError): + pass + return cast(None | PromoteObligationsResponse | Unset, data) + + result = _parse_result(d.pop("result", UNSET)) + + def _parse_created_by(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + created_by = _parse_created_by(d.pop("createdBy", UNSET)) + + idempotent_replay = d.pop("idempotentReplay", UNSET) + + operation_envelope_promote_obligations_response = cls( + operation=operation, + operation_id=operation_id, + status=status, + at=at, + result=result, + created_by=created_by, + idempotent_replay=idempotent_replay, + ) + + operation_envelope_promote_obligations_response.additional_properties = d + return operation_envelope_promote_obligations_response + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/robosystems_client/models/operation_envelope_promote_obligations_response_status.py b/robosystems_client/models/operation_envelope_promote_obligations_response_status.py new file mode 100644 index 0000000..0b93fdb --- /dev/null +++ b/robosystems_client/models/operation_envelope_promote_obligations_response_status.py @@ -0,0 +1,10 @@ +from enum import Enum + + +class OperationEnvelopePromoteObligationsResponseStatus(str, Enum): + COMPLETED = "completed" + FAILED = "failed" + PENDING = "pending" + + def __str__(self) -> str: + return str(self.value) diff --git a/robosystems_client/models/promote_obligations_request.py b/robosystems_client/models/promote_obligations_request.py new file mode 100644 index 0000000..df1634b --- /dev/null +++ b/robosystems_client/models/promote_obligations_request.py @@ -0,0 +1,73 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +from ..types import UNSET, Unset + +T = TypeVar("T", bound="PromoteObligationsRequest") + + +@_attrs_define +class PromoteObligationsRequest: + """On-demand trigger for the obligation-promotion sweep. + + Mirrors what the ``scheduled_obligation_promoter`` Dagster sensor does + on its tick, but lets an interactive caller or an MCP close co-pilot + run it now instead of waiting for the background cadence — required to + drive a schedule-driven close to completion in a single session. + Flips matured ``pending`` ``schedule_entry_due`` events (period boundary + passed) to ``classified``; with ``dispatch_handlers`` it also drafts the + closing entries in the same transaction (idempotent — reconciles to an + existing draft). + + Attributes: + dispatch_handlers (bool | Unset): When True (default), also fire the schedule_entry_due handler for each + promoted obligation so the draft closing entry materializes immediately (autopilot). When False, flip status + only (co-pilot) — the draft is created separately. Default: True. + """ + + dispatch_handlers: bool | Unset = True + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + dispatch_handlers = self.dispatch_handlers + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({}) + if dispatch_handlers is not UNSET: + field_dict["dispatch_handlers"] = dispatch_handlers + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + dispatch_handlers = d.pop("dispatch_handlers", UNSET) + + promote_obligations_request = cls( + dispatch_handlers=dispatch_handlers, + ) + + promote_obligations_request.additional_properties = d + return promote_obligations_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/promote_obligations_response.py b/robosystems_client/models/promote_obligations_response.py new file mode 100644 index 0000000..96d478e --- /dev/null +++ b/robosystems_client/models/promote_obligations_response.py @@ -0,0 +1,123 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import TYPE_CHECKING, Any, TypeVar, cast + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +from ..types import UNSET, Unset + +if TYPE_CHECKING: + from ..models.promote_obligations_response_errors_item import ( + PromoteObligationsResponseErrorsItem, + ) + + +T = TypeVar("T", bound="PromoteObligationsResponse") + + +@_attrs_define +class PromoteObligationsResponse: + """Counts from a single on-demand promotion sweep. + + Attributes: + classified_count (int): Matured obligations flipped pending → classified. + dispatched_count (int): Obligations whose closing entry was drafted this run. + error_count (int): Per-obligation handler errors (non-fatal). + classified_event_ids (list[str] | Unset): + errors (list[PromoteObligationsResponseErrorsItem] | Unset): Per-obligation errors as {event_id, error}; the + sweep continues past them. + """ + + classified_count: int + dispatched_count: int + error_count: int + classified_event_ids: list[str] | Unset = UNSET + errors: list[PromoteObligationsResponseErrorsItem] | Unset = UNSET + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + classified_count = self.classified_count + + dispatched_count = self.dispatched_count + + error_count = self.error_count + + classified_event_ids: list[str] | Unset = UNSET + if not isinstance(self.classified_event_ids, Unset): + classified_event_ids = self.classified_event_ids + + errors: list[dict[str, Any]] | Unset = UNSET + if not isinstance(self.errors, Unset): + errors = [] + for errors_item_data in self.errors: + errors_item = errors_item_data.to_dict() + errors.append(errors_item) + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update( + { + "classified_count": classified_count, + "dispatched_count": dispatched_count, + "error_count": error_count, + } + ) + if classified_event_ids is not UNSET: + field_dict["classified_event_ids"] = classified_event_ids + if errors is not UNSET: + field_dict["errors"] = errors + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + from ..models.promote_obligations_response_errors_item import ( + PromoteObligationsResponseErrorsItem, + ) + + d = dict(src_dict) + classified_count = d.pop("classified_count") + + dispatched_count = d.pop("dispatched_count") + + error_count = d.pop("error_count") + + classified_event_ids = cast(list[str], d.pop("classified_event_ids", UNSET)) + + _errors = d.pop("errors", UNSET) + errors: list[PromoteObligationsResponseErrorsItem] | Unset = UNSET + if _errors is not UNSET: + errors = [] + for errors_item_data in _errors: + errors_item = PromoteObligationsResponseErrorsItem.from_dict(errors_item_data) + + errors.append(errors_item) + + promote_obligations_response = cls( + classified_count=classified_count, + dispatched_count=dispatched_count, + error_count=error_count, + classified_event_ids=classified_event_ids, + errors=errors, + ) + + promote_obligations_response.additional_properties = d + return promote_obligations_response + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/robosystems_client/models/promote_obligations_response_errors_item.py b/robosystems_client/models/promote_obligations_response_errors_item.py new file mode 100644 index 0000000..b80e0be --- /dev/null +++ b/robosystems_client/models/promote_obligations_response_errors_item.py @@ -0,0 +1,47 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +T = TypeVar("T", bound="PromoteObligationsResponseErrorsItem") + + +@_attrs_define +class PromoteObligationsResponseErrorsItem: + """ """ + + additional_properties: dict[str, str] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + promote_obligations_response_errors_item = cls() + + promote_obligations_response_errors_item.additional_properties = d + return promote_obligations_response_errors_item + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> str: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: str) -> 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/rule_variable_lite.py b/robosystems_client/models/rule_variable_lite.py index ef077ed..8ff2a57 100644 --- a/robosystems_client/models/rule_variable_lite.py +++ b/robosystems_client/models/rule_variable_lite.py @@ -1,11 +1,13 @@ from __future__ import annotations from collections.abc import Mapping -from typing import Any, TypeVar +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="RuleVariableLite") @@ -15,26 +17,44 @@ class RuleVariableLite: Attributes: variable_name (str): Local name in the rule expression, e.g. 'Assets'. - variable_qname (str): Concept qname the variable resolves to, e.g. 'fac:Assets'. + variable_qname (None | str | Unset): Concept qname the variable resolves to, e.g. 'fac:Assets'. Null for tenant + CoA elements (which key on `code`/`element_id`, not qname) — in that case the binding is carried by + `variable_element_id`. + variable_element_id (None | str | Unset): Element id the variable binds to directly. Set for schedule SumEquals + rules over CoA-debit elements that have no qname; null otherwise. """ variable_name: str - variable_qname: str + variable_qname: None | str | Unset = UNSET + variable_element_id: None | str | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: variable_name = self.variable_name - variable_qname = self.variable_qname + variable_qname: None | str | Unset + if isinstance(self.variable_qname, Unset): + variable_qname = UNSET + else: + variable_qname = self.variable_qname + + variable_element_id: None | str | Unset + if isinstance(self.variable_element_id, Unset): + variable_element_id = UNSET + else: + variable_element_id = self.variable_element_id field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update( { "variable_name": variable_name, - "variable_qname": variable_qname, } ) + if variable_qname is not UNSET: + field_dict["variable_qname"] = variable_qname + if variable_element_id is not UNSET: + field_dict["variable_element_id"] = variable_element_id return field_dict @@ -43,11 +63,30 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: d = dict(src_dict) variable_name = d.pop("variable_name") - variable_qname = d.pop("variable_qname") + def _parse_variable_qname(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + variable_qname = _parse_variable_qname(d.pop("variable_qname", UNSET)) + + def _parse_variable_element_id(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + variable_element_id = _parse_variable_element_id( + d.pop("variable_element_id", UNSET) + ) rule_variable_lite = cls( variable_name=variable_name, variable_qname=variable_qname, + variable_element_id=variable_element_id, ) rule_variable_lite.additional_properties = d diff --git a/robosystems_client/models/set_write_policy_request.py b/robosystems_client/models/set_write_policy_request.py new file mode 100644 index 0000000..efc9b6b --- /dev/null +++ b/robosystems_client/models/set_write_policy_request.py @@ -0,0 +1,70 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +from ..models.set_write_policy_request_write_policy import ( + SetWritePolicyRequestWritePolicy, +) + +T = TypeVar("T", bound="SetWritePolicyRequest") + + +@_attrs_define +class SetWritePolicyRequest: + """Request to set a connection's source-of-truth write policy. + + The explicit operator opt-in for outbound write-back. `hybrid` is omitted + until its code path ships. + + Attributes: + write_policy (SetWritePolicyRequestWritePolicy): 'native' = RoboSystems authoritative, no write-back; + 'qb_authoritative' = QuickBooks authoritative, entries publish to QB. + """ + + write_policy: SetWritePolicyRequestWritePolicy + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + write_policy = self.write_policy.value + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update( + { + "write_policy": write_policy, + } + ) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + write_policy = SetWritePolicyRequestWritePolicy(d.pop("write_policy")) + + set_write_policy_request = cls( + write_policy=write_policy, + ) + + set_write_policy_request.additional_properties = d + return set_write_policy_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/set_write_policy_request_write_policy.py b/robosystems_client/models/set_write_policy_request_write_policy.py new file mode 100644 index 0000000..8561147 --- /dev/null +++ b/robosystems_client/models/set_write_policy_request_write_policy.py @@ -0,0 +1,9 @@ +from enum import Enum + + +class SetWritePolicyRequestWritePolicy(str, Enum): + NATIVE = "native" + QB_AUTHORITATIVE = "qb_authoritative" + + def __str__(self) -> str: + return str(self.value)