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
Original file line number Diff line number Diff line change
@@ -0,0 +1,294 @@
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_journal_entry_response import (
OperationEnvelopeJournalEntryResponse,
)
from ...models.record_opening_balance_request import RecordOpeningBalanceRequest
from ...types import UNSET, Response, Unset


def _get_kwargs(
graph_id: str,
*,
body: RecordOpeningBalanceRequest,
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/record-opening-balance".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 | OperationEnvelopeJournalEntryResponse | None:
if response.status_code == 200:
response_200 = OperationEnvelopeJournalEntryResponse.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 | OperationEnvelopeJournalEntryResponse]:
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: RecordOpeningBalanceRequest,
idempotency_key: None | str | Unset = UNSET,
) -> Response[ErrorResponse | OperationEnvelopeJournalEntryResponse]:
"""Record Opening Balance

Record the balance sheet an entity's books start from, when its history does not come with them: one
balance per balance-sheet account (assets, liabilities, equity), in cents, debit or credit in the
account's normal direction. It posts as one `opening` entry dated the day before the entity's first
fiscal month, so every month's opening balances include it and no month's activity does: the cash
flow and the equity statement start from it rather than counting it. Name the equity account that
takes any difference between debits and credits (`equity_element_id`, usually Opening Balance
Equity); unbalanced balances without one are refused, and an income or expense account is refused,
since income before the books begin is in retained earnings. One per entity: recording it again
replaces it, until the entity's first close, after which a correction is an adjusting entry in an
open month. Set up the fiscal calendar first; moving its start (change-calendar-start) moves the
opening with it.

**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 (RecordOpeningBalanceRequest): The balance sheet a ledger starts from.

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

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: RecordOpeningBalanceRequest,
idempotency_key: None | str | Unset = UNSET,
) -> ErrorResponse | OperationEnvelopeJournalEntryResponse | None:
"""Record Opening Balance

Record the balance sheet an entity's books start from, when its history does not come with them: one
balance per balance-sheet account (assets, liabilities, equity), in cents, debit or credit in the
account's normal direction. It posts as one `opening` entry dated the day before the entity's first
fiscal month, so every month's opening balances include it and no month's activity does: the cash
flow and the equity statement start from it rather than counting it. Name the equity account that
takes any difference between debits and credits (`equity_element_id`, usually Opening Balance
Equity); unbalanced balances without one are refused, and an income or expense account is refused,
since income before the books begin is in retained earnings. One per entity: recording it again
replaces it, until the entity's first close, after which a correction is an adjusting entry in an
open month. Set up the fiscal calendar first; moving its start (change-calendar-start) moves the
opening with it.

**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 (RecordOpeningBalanceRequest): The balance sheet a ledger starts from.

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

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: RecordOpeningBalanceRequest,
idempotency_key: None | str | Unset = UNSET,
) -> Response[ErrorResponse | OperationEnvelopeJournalEntryResponse]:
"""Record Opening Balance

Record the balance sheet an entity's books start from, when its history does not come with them: one
balance per balance-sheet account (assets, liabilities, equity), in cents, debit or credit in the
account's normal direction. It posts as one `opening` entry dated the day before the entity's first
fiscal month, so every month's opening balances include it and no month's activity does: the cash
flow and the equity statement start from it rather than counting it. Name the equity account that
takes any difference between debits and credits (`equity_element_id`, usually Opening Balance
Equity); unbalanced balances without one are refused, and an income or expense account is refused,
since income before the books begin is in retained earnings. One per entity: recording it again
replaces it, until the entity's first close, after which a correction is an adjusting entry in an
open month. Set up the fiscal calendar first; moving its start (change-calendar-start) moves the
opening with it.

**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 (RecordOpeningBalanceRequest): The balance sheet a ledger starts from.

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

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: RecordOpeningBalanceRequest,
idempotency_key: None | str | Unset = UNSET,
) -> ErrorResponse | OperationEnvelopeJournalEntryResponse | None:
"""Record Opening Balance

Record the balance sheet an entity's books start from, when its history does not come with them: one
balance per balance-sheet account (assets, liabilities, equity), in cents, debit or credit in the
account's normal direction. It posts as one `opening` entry dated the day before the entity's first
fiscal month, so every month's opening balances include it and no month's activity does: the cash
flow and the equity statement start from it rather than counting it. Name the equity account that
takes any difference between debits and credits (`equity_element_id`, usually Opening Balance
Equity); unbalanced balances without one are refused, and an income or expense account is refused,
since income before the books begin is in retained earnings. One per entity: recording it again
replaces it, until the entity's first close, after which a correction is an adjusting entry in an
open month. Set up the fiscal calendar first; moving its start (change-calendar-start) moves the
opening with it.

**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 (RecordOpeningBalanceRequest): The balance sheet a ledger starts from.

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

return (
await asyncio_detailed(
graph_id=graph_id,
client=client,
body=body,
idempotency_key=idempotency_key,
)
).parsed
38 changes: 38 additions & 0 deletions robosystems_client/clients/ledger_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@
from ..api.robo_ledger_ledger_events.preview_reconciliations import (
sync_detailed as op_preview_reconciliations,
)
from ..api.robo_ledger_ledger_events.record_opening_balance import (
sync_detailed as op_record_opening_balance,
)
from ..api.robo_ledger_ledger_events.record_statement_balance import (
sync_detailed as op_record_statement_balance,
)
Expand Down Expand Up @@ -545,6 +548,8 @@
from ..models.update_rollforward_arm import UpdateRollforwardArm
from ..models.link_entity_taxonomy_request import LinkEntityTaxonomyRequest
from ..models.update_journal_entry_request import UpdateJournalEntryRequest
from ..models.opening_balance_input import OpeningBalanceInput
from ..models.record_opening_balance_request import RecordOpeningBalanceRequest
from ..models.update_schedule_arm import UpdateScheduleArm
from ..models.update_schedule_request import UpdateScheduleRequest
from ..models.close_period_operation import ClosePeriodOperation
Expand Down Expand Up @@ -2136,6 +2141,39 @@ def reverse_journal_entry(
envelope = self._call_op("Reverse journal entry", response)
return self._typed_result("Reverse journal entry", envelope, EventBlockEnvelope)

def record_opening_balance(
self,
graph_id: str,
*,
balances: list[dict[str, Any]],
equity_element_id: str | None = None,
memo: str | None = None,
entity_id: str | None = None,
) -> JournalEntryResponse:
"""Record the balance sheet an entity's books start from.

``balances`` is one ``{"element_id", "debit_amount", "credit_amount"}``
per balance-sheet account, in cents, in the account's normal direction.
It posts as one ``opening`` entry dated the day before the first fiscal
month, so every month's opening balances include it and no month's
activity does. ``equity_element_id`` (usually Opening Balance Equity)
takes any difference between debits and credits; unbalanced balances
without one are refused. Recording again replaces the entry until the
entity's first close. ``entity_id`` names the entity; omitted, the group
parent.
"""
body = RecordOpeningBalanceRequest(
balances=[OpeningBalanceInput.from_dict(b) for b in balances],
entity_id=entity_id if entity_id is not None else UNSET,
equity_element_id=(equity_element_id if equity_element_id is not None else UNSET),
memo=memo if memo is not None else UNSET,
)
response = op_record_opening_balance(
graph_id=graph_id, body=body, client=self._get_client()
)
envelope = self._call_op("Record opening balance", response)
return self._typed_result("Record opening balance", envelope, JournalEntryResponse)

# ── Event blocks (generic preview + status transitions) ──────────────

def create_event_block(
Expand Down
4 changes: 4 additions & 0 deletions robosystems_client/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,7 @@
OfferingRepositoryPlanRateLimitsType0,
)
from .oidc_provider_info import OIDCProviderInfo
from .opening_balance_input import OpeningBalanceInput
from .operation_costs import OperationCosts
from .operation_costs_ai_operations import OperationCostsAiOperations
from .operation_costs_token_pricing import OperationCostsTokenPricing
Expand Down Expand Up @@ -914,6 +915,7 @@
ReconcilingItemPlanDefaultDisposition,
)
from .reconciling_item_regenerated import ReconcilingItemRegenerated
from .record_opening_balance_request import RecordOpeningBalanceRequest
from .record_statement_balance_request import RecordStatementBalanceRequest
from .recovery_codes_request import RecoveryCodesRequest
from .recovery_codes_request_assertion_type_0 import RecoveryCodesRequestAssertionType0
Expand Down Expand Up @@ -1551,6 +1553,7 @@
"OfferingRepositoryPlan",
"OfferingRepositoryPlanRateLimitsType0",
"OIDCProviderInfo",
"OpeningBalanceInput",
"OperationCosts",
"OperationCostsAiOperations",
"OperationCostsTokenPricing",
Expand Down Expand Up @@ -1774,6 +1777,7 @@
"ReconcilingItemPlan",
"ReconcilingItemPlanDefaultDisposition",
"ReconcilingItemRegenerated",
"RecordOpeningBalanceRequest",
"RecordStatementBalanceRequest",
"RecoveryCodesRequest",
"RecoveryCodesRequestAssertionType0",
Expand Down
Loading
Loading