Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
310 changes: 310 additions & 0 deletions robosystems_client/api/robo_ledger_setup/link_bank_account.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,310 @@
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.link_bank_account_request import LinkBankAccountRequest
from ...models.operation_envelope_link_bank_account_response import (
OperationEnvelopeLinkBankAccountResponse,
)
from ...types import UNSET, Response, Unset


def _get_kwargs(
graph_id: str,
*,
body: LinkBankAccountRequest,
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/link-bank-account".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 | OperationEnvelopeLinkBankAccountResponse | None:
if response.status_code == 200:
response_200 = OperationEnvelopeLinkBankAccountResponse.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 | OperationEnvelopeLinkBankAccountResponse]:
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: LinkBankAccountRequest,
idempotency_key: None | str | Unset = UNSET,
) -> Response[ErrorResponse | OperationEnvelopeLinkBankAccountResponse]:
"""Link Bank Account

Point a bank feed's account at a chart account. Name `element_id` for an existing active account, or
`entity_id` alone to create one in that entity's chart. The chart the account is in decides whose
books the feed's lines go into, so this is also how a feed account is bound to a subsidiary. Lines
still in the inbox move with it (across an entity change their suggestion is resolved again on the
new chart, and a classification that named the old entity's account is dropped); posted entries stay
where they were posted. An account another connection already feeds is refused. An account the feed
created and then left stays on its chart as an ordinary account. A sync already in flight when the
link moves can still land a line or two on the old account; running this again moves them. Read the
group's accounts with the `bankAccounts` GraphQL field.

**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 (LinkBankAccountRequest): Point a bank feed's account at a chart account.

Name `element_id` for an existing active account, or `entity_id` alone to
create one in that entity's chart. The chart the account is in decides
whose books the feed's lines go into, so this is also how a feed account
is bound to a subsidiary. Lines still in the inbox move with it; posted
entries stay where they were posted.

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 | OperationEnvelopeLinkBankAccountResponse]
"""

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: LinkBankAccountRequest,
idempotency_key: None | str | Unset = UNSET,
) -> ErrorResponse | OperationEnvelopeLinkBankAccountResponse | None:
"""Link Bank Account

Point a bank feed's account at a chart account. Name `element_id` for an existing active account, or
`entity_id` alone to create one in that entity's chart. The chart the account is in decides whose
books the feed's lines go into, so this is also how a feed account is bound to a subsidiary. Lines
still in the inbox move with it (across an entity change their suggestion is resolved again on the
new chart, and a classification that named the old entity's account is dropped); posted entries stay
where they were posted. An account another connection already feeds is refused. An account the feed
created and then left stays on its chart as an ordinary account. A sync already in flight when the
link moves can still land a line or two on the old account; running this again moves them. Read the
group's accounts with the `bankAccounts` GraphQL field.

**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 (LinkBankAccountRequest): Point a bank feed's account at a chart account.

Name `element_id` for an existing active account, or `entity_id` alone to
create one in that entity's chart. The chart the account is in decides
whose books the feed's lines go into, so this is also how a feed account
is bound to a subsidiary. Lines still in the inbox move with it; posted
entries stay where they were posted.

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 | OperationEnvelopeLinkBankAccountResponse
"""

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: LinkBankAccountRequest,
idempotency_key: None | str | Unset = UNSET,
) -> Response[ErrorResponse | OperationEnvelopeLinkBankAccountResponse]:
"""Link Bank Account

Point a bank feed's account at a chart account. Name `element_id` for an existing active account, or
`entity_id` alone to create one in that entity's chart. The chart the account is in decides whose
books the feed's lines go into, so this is also how a feed account is bound to a subsidiary. Lines
still in the inbox move with it (across an entity change their suggestion is resolved again on the
new chart, and a classification that named the old entity's account is dropped); posted entries stay
where they were posted. An account another connection already feeds is refused. An account the feed
created and then left stays on its chart as an ordinary account. A sync already in flight when the
link moves can still land a line or two on the old account; running this again moves them. Read the
group's accounts with the `bankAccounts` GraphQL field.

**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 (LinkBankAccountRequest): Point a bank feed's account at a chart account.

Name `element_id` for an existing active account, or `entity_id` alone to
create one in that entity's chart. The chart the account is in decides
whose books the feed's lines go into, so this is also how a feed account
is bound to a subsidiary. Lines still in the inbox move with it; posted
entries stay where they were posted.

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 | OperationEnvelopeLinkBankAccountResponse]
"""

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: LinkBankAccountRequest,
idempotency_key: None | str | Unset = UNSET,
) -> ErrorResponse | OperationEnvelopeLinkBankAccountResponse | None:
"""Link Bank Account

Point a bank feed's account at a chart account. Name `element_id` for an existing active account, or
`entity_id` alone to create one in that entity's chart. The chart the account is in decides whose
books the feed's lines go into, so this is also how a feed account is bound to a subsidiary. Lines
still in the inbox move with it (across an entity change their suggestion is resolved again on the
new chart, and a classification that named the old entity's account is dropped); posted entries stay
where they were posted. An account another connection already feeds is refused. An account the feed
created and then left stays on its chart as an ordinary account. A sync already in flight when the
link moves can still land a line or two on the old account; running this again moves them. Read the
group's accounts with the `bankAccounts` GraphQL field.

**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 (LinkBankAccountRequest): Point a bank feed's account at a chart account.

Name `element_id` for an existing active account, or `entity_id` alone to
create one in that entity's chart. The chart the account is in decides
whose books the feed's lines go into, so this is also how a feed account
is bound to a subsidiary. Lines still in the inbox move with it; posted
entries stay where they were posted.

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 | OperationEnvelopeLinkBankAccountResponse
"""

return (
await asyncio_detailed(
graph_id=graph_id,
client=client,
body=body,
idempotency_key=idempotency_key,
)
).parsed
47 changes: 47 additions & 0 deletions robosystems_client/clients/ledger_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@
from ..api.robo_ledger_setup.create_entity import (
sync_detailed as op_create_entity,
)
from ..api.robo_ledger_setup.link_bank_account import (
sync_detailed as op_link_bank_account,
)
from ..api.robo_ledger_setup.update_entity import (
sync_detailed as op_update_entity,
)
Expand Down Expand Up @@ -224,6 +227,12 @@
from ..graphql.generated.get_ledger_account_tree import (
GetLedgerAccountTreeAccountTree as LedgerAccountTree,
)
from ..graphql.generated.list_ledger_bank_accounts import (
ListLedgerBankAccounts,
)
from ..graphql.generated.list_ledger_bank_accounts import (
ListLedgerBankAccountsBankAccounts as LedgerBankAccountList,
)
from ..graphql.generated.get_ledger_agent import (
GetLedgerAgent,
)
Expand Down Expand Up @@ -428,6 +437,7 @@
GET_LEDGER_ACCOUNT_ROLLUPS_GQL,
GET_LEDGER_ACCOUNT_TREE_GQL,
GET_LEDGER_AGENT_GQL,
LIST_LEDGER_BANK_ACCOUNTS_GQL,
GET_LEDGER_CLOSING_BOOK_STRUCTURES_GQL,
GET_LEDGER_ENTITY_GQL,
GET_LEDGER_EVENT_BLOCK_GQL,
Expand Down Expand Up @@ -469,6 +479,8 @@
from ..models.auto_map_elements_operation import AutoMapElementsOperation
from ..models.create_agent_request import CreateAgentRequest
from ..models.create_entity_request import CreateEntityRequest
from ..models.link_bank_account_request import LinkBankAccountRequest
from ..models.link_bank_account_response import LinkBankAccountResponse
from ..models.create_event_block_request import CreateEventBlockRequest
from ..models.create_event_block_request_event_category import (
CreateEventBlockRequestEventCategory,
Expand Down Expand Up @@ -945,6 +957,41 @@ def get_account_tree(
data = self._query(graph_id, GET_LEDGER_ACCOUNT_TREE_GQL, self._scope(entity_id))
return GetLedgerAccountTree.model_validate(data).account_tree

# ── Bank accounts ───────────────────────────────────────────────────

def list_bank_accounts(
self, graph_id: str, entity_id: str | None = None
) -> LedgerBankAccountList | None:
"""The group's bank and card accounts.

Every chart account a feed books to, or that a source system types as a
bank or card account, with the entity whose chart it is in (the books
its lines go into), what writes to it (``source``: a feed provider,
``quickbooks``, or None for an account kept by hand) and the connection's
status and last sync. ``entity_id`` narrows to one entity; None is the
whole group.
"""
data = self._query(graph_id, LIST_LEDGER_BANK_ACCOUNTS_GQL, {"entityId": entity_id})
return ListLedgerBankAccounts.model_validate(data).bank_accounts

def link_bank_account(
self, graph_id: str, body: dict[str, Any]
) -> LinkBankAccountResponse:
"""Point a bank feed's account at a chart account.

``element_id`` links an existing account (its chart's entity takes the
feed); ``entity_id`` alone creates one in that entity's chart. This is
how a feed account is bound to a subsidiary: lines still in the inbox
move with it, posted entries stay where they were posted. An account
another connection already feeds is refused.
"""
request = LinkBankAccountRequest.from_dict(body)
response = op_link_bank_account(
graph_id=graph_id, body=request, client=self._get_client()
)
envelope = self._call_op("Link bank account", response)
return self._typed_result("Link bank account", envelope, LinkBankAccountResponse)

def get_account_rollups(
self,
graph_id: str,
Expand Down
Loading
Loading