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
4 changes: 4 additions & 0 deletions robosystems_client/graphql/generated/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@
from .get_ledger_fiscal_calendar import (
GetLedgerFiscalCalendar,
GetLedgerFiscalCalendarFiscalCalendar,
GetLedgerFiscalCalendarFiscalCalendarPendingObligationSample,
GetLedgerFiscalCalendarFiscalCalendarPeriods,
GetLedgerFiscalCalendarFiscalCalendarStrandedObligationSample,
)
from .get_ledger_mapped_trial_balance import (
GetLedgerMappedTrialBalance,
Expand Down Expand Up @@ -455,7 +457,9 @@
"GetLedgerEventBlockEventBlock",
"GetLedgerFiscalCalendar",
"GetLedgerFiscalCalendarFiscalCalendar",
"GetLedgerFiscalCalendarFiscalCalendarPendingObligationSample",
"GetLedgerFiscalCalendarFiscalCalendarPeriods",
"GetLedgerFiscalCalendarFiscalCalendarStrandedObligationSample",
"GetLedgerMappedTrialBalance",
"GetLedgerMappedTrialBalanceMappedTrialBalance",
"GetLedgerMappedTrialBalanceMappedTrialBalanceRows",
Expand Down
24 changes: 24 additions & 0 deletions robosystems_client/graphql/generated/get_ledger_fiscal_calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,36 @@ class GetLedgerFiscalCalendarFiscalCalendar(BaseModel):
catch_up_sequence: list[str] = Field(alias="catchUpSequence")
closeable_now: bool = Field(alias="closeableNow")
blockers: list[str]
pending_obligation_count: int = Field(alias="pendingObligationCount")
pending_obligation_sample: list[
"GetLedgerFiscalCalendarFiscalCalendarPendingObligationSample"
] = Field(alias="pendingObligationSample")
earliest_pending_period: Optional[str] = Field(alias="earliestPendingPeriod")
stranded_obligation_count: int = Field(alias="strandedObligationCount")
stranded_obligation_sample: list[
"GetLedgerFiscalCalendarFiscalCalendarStrandedObligationSample"
] = Field(alias="strandedObligationSample")
sync_stale_days: Optional[int] = Field(alias="syncStaleDays")
last_close_at: Optional[str] = Field(alias="lastCloseAt")
initialized_at: Optional[str] = Field(alias="initializedAt")
last_sync_at: Optional[str] = Field(alias="lastSyncAt")
periods: list["GetLedgerFiscalCalendarFiscalCalendarPeriods"]


class GetLedgerFiscalCalendarFiscalCalendarPendingObligationSample(BaseModel):
event_id: str = Field(alias="eventId")
schedule_id: Optional[str] = Field(alias="scheduleId")
schedule_name: Optional[str] = Field(alias="scheduleName")
period: str


class GetLedgerFiscalCalendarFiscalCalendarStrandedObligationSample(BaseModel):
event_id: str = Field(alias="eventId")
schedule_id: Optional[str] = Field(alias="scheduleId")
schedule_name: Optional[str] = Field(alias="scheduleName")
period: str


class GetLedgerFiscalCalendarFiscalCalendarPeriods(BaseModel):
name: str
start_date: str = Field(alias="startDate")
Expand Down
16 changes: 16 additions & 0 deletions robosystems_client/graphql/generated/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,22 @@
catchUpSequence
closeableNow
blockers
pendingObligationCount
pendingObligationSample {
eventId
scheduleId
scheduleName
period
}
earliestPendingPeriod
strandedObligationCount
strandedObligationSample {
eventId
scheduleId
scheduleName
period
}
syncStaleDays
lastCloseAt
initializedAt
lastSyncAt
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ query GetLedgerFiscalCalendar {
fiscalCalendar {
graphId fiscalYearStartMonth closedThrough closeTarget
gapPeriods catchUpSequence closeableNow blockers
pendingObligationCount
pendingObligationSample { eventId scheduleId scheduleName period }
earliestPendingPeriod
strandedObligationCount
strandedObligationSample { eventId scheduleId scheduleName period }
syncStaleDays
lastCloseAt initializedAt lastSyncAt
periods { name startDate endDate status closedAt }
}
Expand Down
4 changes: 2 additions & 2 deletions robosystems_client/graphql/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ type Query {
openReceivablesByAgent: [OpenBalanceByAgent!]!
openPayablesByAgent: [OpenBalanceByAgent!]!
eventBlock(id: String!): EventBlock
eventBlocks(eventType: String = null, eventCategory: String = null, status: String = null, agentId: String = null, source: String = null, payloadDrift: Boolean = null, limit: Int = null, offset: Int = null): [EventBlock!]!
eventBlocks(eventType: String = null, eventCategory: String = null, status: String = null, agentId: String = null, source: String = null, isReconcilingItem: Boolean = null, limit: Int = null, offset: Int = null): [EventBlock!]!
summary: LedgerSummary
accounts(classification: String = null, isActive: Boolean = null, limit: Int = null, offset: Int = null): AccountList
accountTree(includeInactive: Boolean = null): AccountTree
Expand Down Expand Up @@ -636,7 +636,7 @@ type EventBlock {
currency: String!
description: String
metadata: JSON!
payloadDrift: Boolean!
isReconcilingItem: Boolean!
dimensionIds: [String!]!
agentId: String
resourceType: String
Expand Down
22 changes: 12 additions & 10 deletions robosystems_client/models/event_block_envelope.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,12 @@ class EventBlockEnvelope:
amount (int | None | Unset): Economic value in **cents** of `currency`, signed (inflows positive, outflows
negative). `null` for non-economic events.
description (None | str | Unset): Free-text human-readable summary.
payload_drift (bool | Unset): True when a source re-sync surfaced a changed upstream payload for an event whose
GL is already posted (committed/fulfilled are immutable to sync). The live payload and GL are untouched; the
incoming payload is stashed in `metadata.drift_payload` with `metadata.drift_detected_at`. Drifted events need
operator reconciliation — the local books no longer mirror the source. Default: False.
is_reconciling_item (bool | Unset): True when this event is a reconciling item: a source re-sync surfaced a
changed upstream payload for an event whose GL is already posted (committed/fulfilled are immutable to sync) —
the local books legitimately no longer mirror the source, and the difference awaits an explicit disposition
(restate the affected months, or book a catch-up entry in the open period). The live payload and GL are
untouched; the incoming payload is stashed in `metadata.drift_payload` with `metadata.drift_detected_at`.
Default: False.
event_action (EventBlockEnvelopeEventActionType0 | None | Unset): Canonical action verb refining
`event_category`. Null when the source adapter or capture path didn't supply one.
agent_id (None | str | Unset): Counterparty agent ID, when the event involves one.
Expand Down Expand Up @@ -94,7 +96,7 @@ class EventBlockEnvelope:
external_url: None | str | Unset = UNSET
amount: int | None | Unset = UNSET
description: None | str | Unset = UNSET
payload_drift: bool | Unset = False
is_reconciling_item: bool | Unset = False
event_action: EventBlockEnvelopeEventActionType0 | None | Unset = UNSET
agent_id: None | str | Unset = UNSET
resource_type: None | str | Unset = UNSET
Expand Down Expand Up @@ -162,7 +164,7 @@ def to_dict(self) -> dict[str, Any]:
else:
description = self.description

payload_drift = self.payload_drift
is_reconciling_item = self.is_reconciling_item

event_action: None | str | Unset
if isinstance(self.event_action, Unset):
Expand Down Expand Up @@ -242,8 +244,8 @@ def to_dict(self) -> dict[str, Any]:
field_dict["amount"] = amount
if description is not UNSET:
field_dict["description"] = description
if payload_drift is not UNSET:
field_dict["payload_drift"] = payload_drift
if is_reconciling_item is not UNSET:
field_dict["is_reconciling_item"] = is_reconciling_item
if event_action is not UNSET:
field_dict["event_action"] = event_action
if agent_id is not UNSET:
Expand Down Expand Up @@ -345,7 +347,7 @@ def _parse_description(data: object) -> None | str | Unset:

description = _parse_description(d.pop("description", UNSET))

payload_drift = d.pop("payload_drift", UNSET)
is_reconciling_item = d.pop("is_reconciling_item", UNSET)

def _parse_event_action(
data: object,
Expand Down Expand Up @@ -455,7 +457,7 @@ def _parse_discharges_event_id(data: object) -> None | str | Unset:
external_url=external_url,
amount=amount,
description=description,
payload_drift=payload_drift,
is_reconciling_item=is_reconciling_item,
event_action=event_action,
agent_id=agent_id,
resource_type=resource_type,
Expand Down
54 changes: 54 additions & 0 deletions tests/test_ledger_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,12 @@ def test_get_fiscal_calendar(self, mock_execute, mock_config, graph_id):
"catchUpSequence": [],
"closeableNow": True,
"blockers": [],
"pendingObligationCount": 0,
"pendingObligationSample": [],
"earliestPendingPeriod": None,
"strandedObligationCount": 0,
"strandedObligationSample": [],
"syncStaleDays": None,
"lastCloseAt": None,
"initializedAt": "2026-01-01T00:00:00Z",
"lastSyncAt": None,
Expand All @@ -265,6 +271,54 @@ def test_get_fiscal_calendar(self, mock_execute, mock_config, graph_id):
assert cal.closed_through == "2026-02"
assert cal.closeable_now is True
assert cal.fiscal_year_start_month == 1
assert cal.stranded_obligation_count == 0

@patch("robosystems_client.graphql.client.GraphQLClient.execute")
def test_get_fiscal_calendar_names_blocking_obligations(
self, mock_execute, mock_config, graph_id
):
"""A blocked calendar carries the detail needed to act on the blocker.

`blockers` alone says a close is held but not by what; the sample names
the schedule so a caller can promote or void it without a second query.
"""
mock_execute.return_value = {
"fiscalCalendar": {
"graphId": graph_id,
"fiscalYearStartMonth": 1,
"closedThrough": "2026-02",
"closeTarget": "2026-03",
"gapPeriods": 1,
"catchUpSequence": ["2026-03"],
"closeableNow": False,
"blockers": ["stranded_obligations"],
"pendingObligationCount": 0,
"pendingObligationSample": [],
"earliestPendingPeriod": None,
"strandedObligationCount": 2,
"strandedObligationSample": [
{
"eventId": "evt_1",
"scheduleId": "str_prepaid",
"scheduleName": "Prepaid Insurance",
"period": "2026-03",
}
],
"syncStaleDays": None,
"lastCloseAt": None,
"initializedAt": "2026-01-01T00:00:00Z",
"lastSyncAt": None,
"periods": [],
}
}
client = LedgerClient(mock_config)
cal = client.get_fiscal_calendar(graph_id)
assert cal is not None
assert cal.closeable_now is False
assert cal.blockers == ["stranded_obligations"]
assert cal.stranded_obligation_count == 2
assert cal.stranded_obligation_sample[0].schedule_name == "Prepaid Insurance"
assert cal.stranded_obligation_sample[0].period == "2026-03"


# ── Writes (Operation envelope) ────────────────────────────────────────
Expand Down