diff --git a/robosystems_client/api/robo_ledger_ledger_events/record_statement_balance.py b/robosystems_client/api/robo_ledger_ledger_events/record_statement_balance.py index de43f76..25f175a 100644 --- a/robosystems_client/api/robo_ledger_ledger_events/record_statement_balance.py +++ b/robosystems_client/api/robo_ledger_ledger_events/record_statement_balance.py @@ -117,16 +117,19 @@ def sync_detailed( account, and reconcile the account to it. Give the balance as the statement shows it, as a positive number in the account's normal direction, with the statement's ending date. The ledger's balance at that date is set beside it, counting the drafts the close will post, and the result is recorded on - the account's statement reconciliation for the period the statement ends in. Attach the statement as - evidence by passing the `document_id` of its stored file (create-document-upload, then complete- - document-upload); recording it again with another document lapses a sign-off. Writes no books. The - first statement recorded for an account creates its reconciliation, which does not hold the close: - turn `required_for_close` on with set-reconciliation-policy to make every period's close wait for a - statement on that account. Recording the same account and date again replaces the earlier balance; - when more than one statement ends in a period, the one with the latest date stands. Recording a - balance that differs from one already signed off lapses that sign-off. A difference is not explained - here: it is activity one side has and the other does not yet, or an error on either. Returns the - reconciliation's standing for the period. + the account's statement reconciliation for the period the statement ends in. On an account a bank + feed keeps, ledger lines that did not come from the feed and are dated on or before the statement + are listed as outstanding and adjust the statement balance, since the bank had not cleared them; a + statement ending before the period's last day is carried to it by the feed's own lines + (`roll_forward`). Attach the statement as evidence by passing the `document_id` of its stored file + (create-document-upload, then complete-document-upload); recording it again with another document + lapses a sign-off. Writes no books. The first statement recorded for an account creates its + reconciliation, which does not hold the close: turn `required_for_close` on with set-reconciliation- + policy to make every period's close wait for a statement on that account. Recording the same account + and date again replaces the earlier balance; when more than one statement ends in a period, the one + with the latest date stands. Recording a balance that differs from one already signed off lapses + that sign-off. A difference left after the outstanding lines is activity one side has and the other + does not yet, or an error on either. Returns the reconciliation's standing for the period. **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. @@ -171,16 +174,19 @@ def sync( account, and reconcile the account to it. Give the balance as the statement shows it, as a positive number in the account's normal direction, with the statement's ending date. The ledger's balance at that date is set beside it, counting the drafts the close will post, and the result is recorded on - the account's statement reconciliation for the period the statement ends in. Attach the statement as - evidence by passing the `document_id` of its stored file (create-document-upload, then complete- - document-upload); recording it again with another document lapses a sign-off. Writes no books. The - first statement recorded for an account creates its reconciliation, which does not hold the close: - turn `required_for_close` on with set-reconciliation-policy to make every period's close wait for a - statement on that account. Recording the same account and date again replaces the earlier balance; - when more than one statement ends in a period, the one with the latest date stands. Recording a - balance that differs from one already signed off lapses that sign-off. A difference is not explained - here: it is activity one side has and the other does not yet, or an error on either. Returns the - reconciliation's standing for the period. + the account's statement reconciliation for the period the statement ends in. On an account a bank + feed keeps, ledger lines that did not come from the feed and are dated on or before the statement + are listed as outstanding and adjust the statement balance, since the bank had not cleared them; a + statement ending before the period's last day is carried to it by the feed's own lines + (`roll_forward`). Attach the statement as evidence by passing the `document_id` of its stored file + (create-document-upload, then complete-document-upload); recording it again with another document + lapses a sign-off. Writes no books. The first statement recorded for an account creates its + reconciliation, which does not hold the close: turn `required_for_close` on with set-reconciliation- + policy to make every period's close wait for a statement on that account. Recording the same account + and date again replaces the earlier balance; when more than one statement ends in a period, the one + with the latest date stands. Recording a balance that differs from one already signed off lapses + that sign-off. A difference left after the outstanding lines is activity one side has and the other + does not yet, or an error on either. Returns the reconciliation's standing for the period. **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. @@ -220,16 +226,19 @@ async def asyncio_detailed( account, and reconcile the account to it. Give the balance as the statement shows it, as a positive number in the account's normal direction, with the statement's ending date. The ledger's balance at that date is set beside it, counting the drafts the close will post, and the result is recorded on - the account's statement reconciliation for the period the statement ends in. Attach the statement as - evidence by passing the `document_id` of its stored file (create-document-upload, then complete- - document-upload); recording it again with another document lapses a sign-off. Writes no books. The - first statement recorded for an account creates its reconciliation, which does not hold the close: - turn `required_for_close` on with set-reconciliation-policy to make every period's close wait for a - statement on that account. Recording the same account and date again replaces the earlier balance; - when more than one statement ends in a period, the one with the latest date stands. Recording a - balance that differs from one already signed off lapses that sign-off. A difference is not explained - here: it is activity one side has and the other does not yet, or an error on either. Returns the - reconciliation's standing for the period. + the account's statement reconciliation for the period the statement ends in. On an account a bank + feed keeps, ledger lines that did not come from the feed and are dated on or before the statement + are listed as outstanding and adjust the statement balance, since the bank had not cleared them; a + statement ending before the period's last day is carried to it by the feed's own lines + (`roll_forward`). Attach the statement as evidence by passing the `document_id` of its stored file + (create-document-upload, then complete-document-upload); recording it again with another document + lapses a sign-off. Writes no books. The first statement recorded for an account creates its + reconciliation, which does not hold the close: turn `required_for_close` on with set-reconciliation- + policy to make every period's close wait for a statement on that account. Recording the same account + and date again replaces the earlier balance; when more than one statement ends in a period, the one + with the latest date stands. Recording a balance that differs from one already signed off lapses + that sign-off. A difference left after the outstanding lines is activity one side has and the other + does not yet, or an error on either. Returns the reconciliation's standing for the period. **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. @@ -272,16 +281,19 @@ async def asyncio( account, and reconcile the account to it. Give the balance as the statement shows it, as a positive number in the account's normal direction, with the statement's ending date. The ledger's balance at that date is set beside it, counting the drafts the close will post, and the result is recorded on - the account's statement reconciliation for the period the statement ends in. Attach the statement as - evidence by passing the `document_id` of its stored file (create-document-upload, then complete- - document-upload); recording it again with another document lapses a sign-off. Writes no books. The - first statement recorded for an account creates its reconciliation, which does not hold the close: - turn `required_for_close` on with set-reconciliation-policy to make every period's close wait for a - statement on that account. Recording the same account and date again replaces the earlier balance; - when more than one statement ends in a period, the one with the latest date stands. Recording a - balance that differs from one already signed off lapses that sign-off. A difference is not explained - here: it is activity one side has and the other does not yet, or an error on either. Returns the - reconciliation's standing for the period. + the account's statement reconciliation for the period the statement ends in. On an account a bank + feed keeps, ledger lines that did not come from the feed and are dated on or before the statement + are listed as outstanding and adjust the statement balance, since the bank had not cleared them; a + statement ending before the period's last day is carried to it by the feed's own lines + (`roll_forward`). Attach the statement as evidence by passing the `document_id` of its stored file + (create-document-upload, then complete-document-upload); recording it again with another document + lapses a sign-off. Writes no books. The first statement recorded for an account creates its + reconciliation, which does not hold the close: turn `required_for_close` on with set-reconciliation- + policy to make every period's close wait for a statement on that account. Recording the same account + and date again replaces the earlier balance; when more than one statement ends in a period, the one + with the latest date stands. Recording a balance that differs from one already signed off lapses + that sign-off. A difference left after the outstanding lines is activity one side has and the other + does not yet, or an error on either. Returns the reconciliation's standing for the period. **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. diff --git a/robosystems_client/api/robo_ledger_ledger_events/refresh_reconciliations.py b/robosystems_client/api/robo_ledger_ledger_events/refresh_reconciliations.py index 7b96435..82225b2 100644 --- a/robosystems_client/api/robo_ledger_ledger_events/refresh_reconciliations.py +++ b/robosystems_client/api/robo_ledger_ledger_events/refresh_reconciliations.py @@ -118,15 +118,15 @@ def sync_detailed( Run every reconciliation that applies at a period end and record each result on its block. A ledger synced from QuickBooks is compared with QuickBooks' own trial balance, on one block for the whole ledger. Each asset account a schedule carries a balance on is compared with what its schedules say - it holds, on a block of its own. Each account with a statement balance recorded in the period - (record-statement-balance) is compared with that balance. A block reconciles for the period when its - difference is within its materiality. Running it again replaces the period's comparison, so the - answer is always as of the last run. A block is created the first time its check applies, and from - then on the period's close waits on it until set-reconciliation-policy releases it. The one - exception is an account block whose first comparison does not tie: it is created without holding the - close, so a difference found on first contact is reported and becomes a close requirement only when - you turn it on. Returns every reconciliation's standing for the period. Use preview-reconciliations - to see a comparison without recording it. + it holds, on a block of its own. Each account with a statement covering the period (record- + statement-balance; the latest within its statement cycle) is compared with that balance. A block + reconciles for the period when its difference is within its materiality. Running it again replaces + the period's comparison, so the answer is always as of the last run. A block is created the first + time its check applies, and from then on the period's close waits on it until set-reconciliation- + policy releases it. The one exception is an account block whose first comparison does not tie: it is + created without holding the close, so a difference found on first contact is reported and becomes a + close requirement only when you turn it on. Returns every reconciliation's standing for the period. + Use preview-reconciliations to see a comparison without recording 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. @@ -170,15 +170,15 @@ def sync( Run every reconciliation that applies at a period end and record each result on its block. A ledger synced from QuickBooks is compared with QuickBooks' own trial balance, on one block for the whole ledger. Each asset account a schedule carries a balance on is compared with what its schedules say - it holds, on a block of its own. Each account with a statement balance recorded in the period - (record-statement-balance) is compared with that balance. A block reconciles for the period when its - difference is within its materiality. Running it again replaces the period's comparison, so the - answer is always as of the last run. A block is created the first time its check applies, and from - then on the period's close waits on it until set-reconciliation-policy releases it. The one - exception is an account block whose first comparison does not tie: it is created without holding the - close, so a difference found on first contact is reported and becomes a close requirement only when - you turn it on. Returns every reconciliation's standing for the period. Use preview-reconciliations - to see a comparison without recording it. + it holds, on a block of its own. Each account with a statement covering the period (record- + statement-balance; the latest within its statement cycle) is compared with that balance. A block + reconciles for the period when its difference is within its materiality. Running it again replaces + the period's comparison, so the answer is always as of the last run. A block is created the first + time its check applies, and from then on the period's close waits on it until set-reconciliation- + policy releases it. The one exception is an account block whose first comparison does not tie: it is + created without holding the close, so a difference found on first contact is reported and becomes a + close requirement only when you turn it on. Returns every reconciliation's standing for the period. + Use preview-reconciliations to see a comparison without recording 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. @@ -217,15 +217,15 @@ async def asyncio_detailed( Run every reconciliation that applies at a period end and record each result on its block. A ledger synced from QuickBooks is compared with QuickBooks' own trial balance, on one block for the whole ledger. Each asset account a schedule carries a balance on is compared with what its schedules say - it holds, on a block of its own. Each account with a statement balance recorded in the period - (record-statement-balance) is compared with that balance. A block reconciles for the period when its - difference is within its materiality. Running it again replaces the period's comparison, so the - answer is always as of the last run. A block is created the first time its check applies, and from - then on the period's close waits on it until set-reconciliation-policy releases it. The one - exception is an account block whose first comparison does not tie: it is created without holding the - close, so a difference found on first contact is reported and becomes a close requirement only when - you turn it on. Returns every reconciliation's standing for the period. Use preview-reconciliations - to see a comparison without recording it. + it holds, on a block of its own. Each account with a statement covering the period (record- + statement-balance; the latest within its statement cycle) is compared with that balance. A block + reconciles for the period when its difference is within its materiality. Running it again replaces + the period's comparison, so the answer is always as of the last run. A block is created the first + time its check applies, and from then on the period's close waits on it until set-reconciliation- + policy releases it. The one exception is an account block whose first comparison does not tie: it is + created without holding the close, so a difference found on first contact is reported and becomes a + close requirement only when you turn it on. Returns every reconciliation's standing for the period. + Use preview-reconciliations to see a comparison without recording 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. @@ -267,15 +267,15 @@ async def asyncio( Run every reconciliation that applies at a period end and record each result on its block. A ledger synced from QuickBooks is compared with QuickBooks' own trial balance, on one block for the whole ledger. Each asset account a schedule carries a balance on is compared with what its schedules say - it holds, on a block of its own. Each account with a statement balance recorded in the period - (record-statement-balance) is compared with that balance. A block reconciles for the period when its - difference is within its materiality. Running it again replaces the period's comparison, so the - answer is always as of the last run. A block is created the first time its check applies, and from - then on the period's close waits on it until set-reconciliation-policy releases it. The one - exception is an account block whose first comparison does not tie: it is created without holding the - close, so a difference found on first contact is reported and becomes a close requirement only when - you turn it on. Returns every reconciliation's standing for the period. Use preview-reconciliations - to see a comparison without recording it. + it holds, on a block of its own. Each account with a statement covering the period (record- + statement-balance; the latest within its statement cycle) is compared with that balance. A block + reconciles for the period when its difference is within its materiality. Running it again replaces + the period's comparison, so the answer is always as of the last run. A block is created the first + time its check applies, and from then on the period's close waits on it until set-reconciliation- + policy releases it. The one exception is an account block whose first comparison does not tie: it is + created without holding the close, so a difference found on first contact is reported and becomes a + close requirement only when you turn it on. Returns every reconciliation's standing for the period. + Use preview-reconciliations to see a comparison without recording 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. diff --git a/robosystems_client/api/robo_ledger_ledger_events/set_reconciliation_policy.py b/robosystems_client/api/robo_ledger_ledger_events/set_reconciliation_policy.py index 3a5a3ba..f9d5ac1 100644 --- a/robosystems_client/api/robo_ledger_ledger_events/set_reconciliation_policy.py +++ b/robosystems_client/api/robo_ledger_ledger_events/set_reconciliation_policy.py @@ -120,8 +120,11 @@ def sync_detailed( whether the close also waits for a sign-off (`review_required`); and whether the reviewer must be someone other than the person who ran the comparison (`separate_reviewer`, which can only be turned on while the graph has at least two members who can write; if it later has one, turn it off or add a - member). Omitted fields keep their value. The next refresh-reconciliations uses the new materiality; - comparisons already recorded are not re-judged. + member). For a statement reconciliation, `statement_cycle` says how often the statement is issued + (monthly, quarterly or annual): a period is covered by the latest statement ending within the cycle + that ends with it, so a quarterly statement still covers the months between statements. Omitted + fields keep their value. The next refresh-reconciliations uses the new materiality; comparisons + already recorded are not re-judged. **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. @@ -167,8 +170,11 @@ def sync( whether the close also waits for a sign-off (`review_required`); and whether the reviewer must be someone other than the person who ran the comparison (`separate_reviewer`, which can only be turned on while the graph has at least two members who can write; if it later has one, turn it off or add a - member). Omitted fields keep their value. The next refresh-reconciliations uses the new materiality; - comparisons already recorded are not re-judged. + member). For a statement reconciliation, `statement_cycle` says how often the statement is issued + (monthly, quarterly or annual): a period is covered by the latest statement ending within the cycle + that ends with it, so a quarterly statement still covers the months between statements. Omitted + fields keep their value. The next refresh-reconciliations uses the new materiality; comparisons + already recorded are not re-judged. **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. @@ -209,8 +215,11 @@ async def asyncio_detailed( whether the close also waits for a sign-off (`review_required`); and whether the reviewer must be someone other than the person who ran the comparison (`separate_reviewer`, which can only be turned on while the graph has at least two members who can write; if it later has one, turn it off or add a - member). Omitted fields keep their value. The next refresh-reconciliations uses the new materiality; - comparisons already recorded are not re-judged. + member). For a statement reconciliation, `statement_cycle` says how often the statement is issued + (monthly, quarterly or annual): a period is covered by the latest statement ending within the cycle + that ends with it, so a quarterly statement still covers the months between statements. Omitted + fields keep their value. The next refresh-reconciliations uses the new materiality; comparisons + already recorded are not re-judged. **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. @@ -254,8 +263,11 @@ async def asyncio( whether the close also waits for a sign-off (`review_required`); and whether the reviewer must be someone other than the person who ran the comparison (`separate_reviewer`, which can only be turned on while the graph has at least two members who can write; if it later has one, turn it off or add a - member). Omitted fields keep their value. The next refresh-reconciliations uses the new materiality; - comparisons already recorded are not re-judged. + member). For a statement reconciliation, `statement_cycle` says how often the statement is issued + (monthly, quarterly or annual): a period is covered by the latest statement ending within the cycle + that ends with it, so a quarterly statement still covers the months between statements. Omitted + fields keep their value. The next refresh-reconciliations uses the new materiality; comparisons + already recorded are not re-judged. **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. diff --git a/robosystems_client/clients/ledger_client.py b/robosystems_client/clients/ledger_client.py index c0c4b10..184a936 100644 --- a/robosystems_client/clients/ledger_client.py +++ b/robosystems_client/clients/ledger_client.py @@ -545,6 +545,9 @@ ) from ..models.resolve_reconciling_item_response import ResolveReconcilingItemResponse from ..models.set_reconciliation_policy_request import SetReconciliationPolicyRequest +from ..models.set_reconciliation_policy_request_statement_cycle_type_0 import ( + SetReconciliationPolicyRequestStatementCycleType0, +) from ..models.sign_off_reconciliation_request import SignOffReconciliationRequest from ..models.compute_metrics_request import ComputeMetricsRequest from ..models.compute_metrics_response import ComputeMetricsResponse @@ -2647,9 +2650,13 @@ def set_reconciliation_policy( materiality: float | None = None, review_required: bool | None = None, separate_reviewer: bool | None = None, + statement_cycle: SetReconciliationPolicyRequestStatementCycleType0 + | str + | None = None, ) -> ReconciliationPolicyResponse: """Change how much the close cares about one reconciliation. An omitted - field keeps its value.""" + field keeps its value. ``statement_cycle`` (``monthly``, ``quarterly`` or + ``annual``) applies to statement reconciliations only.""" body = SetReconciliationPolicyRequest( structure_id=structure_id, required_for_close=( @@ -2658,6 +2665,11 @@ def set_reconciliation_policy( materiality=materiality if materiality is not None else UNSET, review_required=review_required if review_required is not None else UNSET, separate_reviewer=(separate_reviewer if separate_reviewer is not None else UNSET), + statement_cycle=( + SetReconciliationPolicyRequestStatementCycleType0(statement_cycle) + if statement_cycle is not None + else UNSET + ), ) response = op_set_reconciliation_policy( graph_id=graph_id, body=body, client=self._get_client() diff --git a/robosystems_client/graphql/generated/__init__.py b/robosystems_client/graphql/generated/__init__.py index aeaa6a0..86024ed 100644 --- a/robosystems_client/graphql/generated/__init__.py +++ b/robosystems_client/graphql/generated/__init__.py @@ -281,6 +281,8 @@ ListLedgerReconciliationsReconciliationsReconciliationsComponents, ListLedgerReconciliationsReconciliationsReconciliationsDifferences, ListLedgerReconciliationsReconciliationsReconciliationsDifferencesComponents, + ListLedgerReconciliationsReconciliationsReconciliationsDifferencesRollForward, + ListLedgerReconciliationsReconciliationsReconciliationsRollForward, ) from .list_ledger_reports import ( ListLedgerReports, @@ -670,6 +672,8 @@ "ListLedgerReconciliationsReconciliationsReconciliationsComponents", "ListLedgerReconciliationsReconciliationsReconciliationsDifferences", "ListLedgerReconciliationsReconciliationsReconciliationsDifferencesComponents", + "ListLedgerReconciliationsReconciliationsReconciliationsDifferencesRollForward", + "ListLedgerReconciliationsReconciliationsReconciliationsRollForward", "ListLedgerReports", "ListLedgerReportsReports", "ListLedgerReportsReportsReports", diff --git a/robosystems_client/graphql/generated/list_ledger_reconciliations.py b/robosystems_client/graphql/generated/list_ledger_reconciliations.py index bcd1e82..34a24d0 100644 --- a/robosystems_client/graphql/generated/list_ledger_reconciliations.py +++ b/robosystems_client/graphql/generated/list_ledger_reconciliations.py @@ -24,6 +24,7 @@ class ListLedgerReconciliationsReconciliationsReconciliations(BaseModel): element_id: Optional[str] = Field(alias="elementId") required_for_close: bool = Field(alias="requiredForClose") materiality: float + statement_cycle: Optional[str] = Field(alias="statementCycle") period: str as_of: str = Field(alias="asOf") status: str @@ -34,6 +35,9 @@ class ListLedgerReconciliationsReconciliationsReconciliations(BaseModel): independent_balance: Optional[float] = Field(alias="independentBalance") balance_as_of: Optional[str] = Field(alias="balanceAsOf") components: list["ListLedgerReconciliationsReconciliationsReconciliationsComponents"] + roll_forward: Optional[ + "ListLedgerReconciliationsReconciliationsReconciliationsRollForward" + ] = Field(alias="rollForward") source: Optional[str] compared_at: Optional[str] = Field(alias="comparedAt") fact_set_id: Optional[str] = Field(alias="factSetId") @@ -52,12 +56,27 @@ class ListLedgerReconciliationsReconciliationsReconciliations(BaseModel): class ListLedgerReconciliationsReconciliationsReconciliationsComponents(BaseModel): name: str amount: float + kind: Optional[str] + posting_date: Optional[str] = Field(alias="postingDate") + entry_id: Optional[str] = Field(alias="entryId") structure_id: Optional[str] = Field(alias="structureId") event_id: Optional[str] = Field(alias="eventId") document_id: Optional[str] = Field(alias="documentId") note: Optional[str] +class ListLedgerReconciliationsReconciliationsReconciliationsRollForward(BaseModel): + statement_as_of: str = Field(alias="statementAsOf") + through: str + bank_lines: int = Field(alias="bankLines") + bank_activity: float = Field(alias="bankActivity") + bank_balance: float = Field(alias="bankBalance") + ledger_balance: float = Field(alias="ledgerBalance") + outstanding: float + feed_balance: Optional[float] = Field(alias="feedBalance") + feed_balance_read_on: Optional[str] = Field(alias="feedBalanceReadOn") + + class ListLedgerReconciliationsReconciliationsReconciliationsDifferences(BaseModel): element_id: Optional[str] = Field(alias="elementId") account_code: Optional[str] = Field(alias="accountCode") @@ -72,6 +91,9 @@ class ListLedgerReconciliationsReconciliationsReconciliationsDifferences(BaseMod components: list[ "ListLedgerReconciliationsReconciliationsReconciliationsDifferencesComponents" ] + roll_forward: Optional[ + "ListLedgerReconciliationsReconciliationsReconciliationsDifferencesRollForward" + ] = Field(alias="rollForward") class ListLedgerReconciliationsReconciliationsReconciliationsDifferencesComponents( @@ -79,12 +101,29 @@ class ListLedgerReconciliationsReconciliationsReconciliationsDifferencesComponen ): name: str amount: float + kind: Optional[str] + posting_date: Optional[str] = Field(alias="postingDate") + entry_id: Optional[str] = Field(alias="entryId") structure_id: Optional[str] = Field(alias="structureId") event_id: Optional[str] = Field(alias="eventId") document_id: Optional[str] = Field(alias="documentId") note: Optional[str] +class ListLedgerReconciliationsReconciliationsReconciliationsDifferencesRollForward( + BaseModel +): + statement_as_of: str = Field(alias="statementAsOf") + through: str + bank_lines: int = Field(alias="bankLines") + bank_activity: float = Field(alias="bankActivity") + bank_balance: float = Field(alias="bankBalance") + ledger_balance: float = Field(alias="ledgerBalance") + outstanding: float + feed_balance: Optional[float] = Field(alias="feedBalance") + feed_balance_read_on: Optional[str] = Field(alias="feedBalanceReadOn") + + ListLedgerReconciliations.model_rebuild() ListLedgerReconciliationsReconciliations.model_rebuild() ListLedgerReconciliationsReconciliationsReconciliations.model_rebuild() diff --git a/robosystems_client/graphql/generated/operations.py b/robosystems_client/graphql/generated/operations.py index affdf02..24a259d 100644 --- a/robosystems_client/graphql/generated/operations.py +++ b/robosystems_client/graphql/generated/operations.py @@ -1666,6 +1666,7 @@ elementId requiredForClose materiality + statementCycle period asOf status @@ -1678,11 +1679,25 @@ components { name amount + kind + postingDate + entryId structureId eventId documentId note } + rollForward { + statementAsOf + through + bankLines + bankActivity + bankBalance + ledgerBalance + outstanding + feedBalance + feedBalanceReadOn + } source comparedAt factSetId @@ -1707,11 +1722,25 @@ components { name amount + kind + postingDate + entryId structureId eventId documentId note } + rollForward { + statementAsOf + through + bankLines + bankActivity + bankBalance + ledgerBalance + outstanding + feedBalance + feedBalanceReadOn + } } } } diff --git a/robosystems_client/graphql/operations/ledger/ListLedgerReconciliations.graphql b/robosystems_client/graphql/operations/ledger/ListLedgerReconciliations.graphql index cc61263..78afa9b 100644 --- a/robosystems_client/graphql/operations/ledger/ListLedgerReconciliations.graphql +++ b/robosystems_client/graphql/operations/ledger/ListLedgerReconciliations.graphql @@ -3,17 +3,29 @@ query ListLedgerReconciliations($period: String!, $entityId: String) { period asOf notes reconciliations { structureId name scope method elementId - requiredForClose materiality + requiredForClose materiality statementCycle period asOf status unreconciledDifference accountsCompared accountsDifferent ledgerBalance independentBalance balanceAsOf - components { name amount structureId eventId documentId note } + components { + name amount kind postingDate entryId structureId eventId documentId note + } + rollForward { + statementAsOf through bankLines bankActivity bankBalance + ledgerBalance outstanding feedBalance feedBalanceReadOn + } source comparedAt factSetId comparedBy comparedVia reviewRequired separateReviewer reviewedBy reviewedAt selfReviewed differences { elementId accountCode accountName sourceAccountId statement ledgerBalance independentBalance difference status asOf - components { name amount structureId eventId documentId note } + components { + name amount kind postingDate entryId structureId eventId documentId note + } + rollForward { + statementAsOf through bankLines bankActivity bankBalance + ledgerBalance outstanding feedBalance feedBalanceReadOn + } } } } diff --git a/robosystems_client/graphql/schema.graphql b/robosystems_client/graphql/schema.graphql index 300c341..9f5e326 100644 --- a/robosystems_client/graphql/schema.graphql +++ b/robosystems_client/graphql/schema.graphql @@ -1844,6 +1844,11 @@ type ReconciliationSummary { """A difference up to this amount still counts as reconciled.""" materiality: Float! + """ + `statement` blocks only: how often the account's statement is issued, `monthly`, `quarterly` or `annual`. + """ + statementCycle: String + """The period, as YYYY-MM.""" period: String! @@ -1877,15 +1882,20 @@ type ReconciliationSummary { independentBalance: Float """ - Account-scope only: the date the two balances are stated at. The period's last day, unless a statement ended earlier in the period. + Account-scope only: the date the two balances are stated at. The period's last day, unless the statement covering it ended earlier. """ balanceAsOf: Date """ - Account-scope only: what makes up the independent balance. One entry per schedule for `schedule_register`; the recorded statement for `statement`. + Account-scope only: what makes up the independent balance. One entry per schedule for `schedule_register`; the recorded statement for `statement`, then each outstanding line. """ components: [ReconciliationComponent!]! + """ + `statement` on a bank-fed account whose statement ends before the period's last day: the statement carried to it by the feed's lines. + """ + rollForward: ReconciliationRollForward + """The system the independent side was read from.""" source: String @@ -1932,19 +1942,35 @@ type ReconciliationSummary { """ One part of an account's independent balance: what a single schedule -says the account carries, or a recorded statement balance. +says the account carries, a recorded statement balance, or a ledger line +outstanding at the statement's date. """ type ReconciliationComponent { - """The schedule's name, or the statement and its date.""" + """ + The schedule's name, the statement and its date, or the outstanding line's description. + """ name: String! """What this part says the account holds, debit-positive.""" amount: Float! + """ + `schedule`, `statement`, or `outstanding`: a ledger line on a bank-fed account that did not come from the feed, dated on or before the statement, so the bank had not cleared it. + """ + kind: String + + """An `outstanding` line's posting date.""" + postingDate: Date + + """The journal entry an `outstanding` line belongs to.""" + entryId: String + """The schedule, for a `schedule_register` part.""" structureId: String - """The recorded balance, for a `statement` part.""" + """ + The recorded balance, for a `statement` part; the event behind an `outstanding` line, when it has one. + """ eventId: String """The statement document given as evidence, when one was.""" @@ -1956,6 +1982,47 @@ type ReconciliationComponent { note: String } +""" +A statement carried from its ending date to the period's last day on a +bank-fed account, where every line the feed brought is the bank's own. +""" +type ReconciliationRollForward { + """The statement's ending date.""" + statementAsOf: Date! + + """The period's last day.""" + through: Date! + + """Booked feed lines dated after the statement, to the period end.""" + bankLines: Int! + + """Their net effect on the account, debit-positive.""" + bankActivity: Float! + + """ + The statement balance carried to the period end by those lines: what the bank held then, debit-positive. + """ + bankBalance: Float! + + """ + The ledger's balance at the period end, as the close will leave it, debit-positive. + """ + ledgerBalance: Float! + + """ + Ledger lines not from the feed and dated on or before the period end, net: the ledger minus the carried bank balance. + """ + outstanding: Float! + + """ + A cross-check, never the figure reconciled: the bank feed's own balance from the first reading on or after the period end, less the booked feed lines between the period end and that reading. Null when the feed has no reading within ten days. + """ + feedBalance: Float + + """The day of the feed reading `feedBalance` starts from.""" + feedBalanceReadOn: Date +} + """ One account: the ledger's balance, the independent balance, the difference. @@ -1984,7 +2051,9 @@ type ReconciliationRow { """ ledgerBalance: Float! - """What the independent source says.""" + """ + What the independent source says. For a `statement` on a bank-fed account, the statement balance adjusted by its outstanding lines. + """ independentBalance: Float! """Ledger minus independent.""" @@ -2001,9 +2070,14 @@ type ReconciliationRow { asOf: Date """ - Account-scope methods only: what makes up the independent balance. One entry per schedule for `schedule_register`; the recorded statement for `statement`. + Account-scope methods only: what makes up the independent balance. One entry per schedule for `schedule_register`; the recorded statement for `statement`, then each outstanding line. """ components: [ReconciliationComponent!]! + + """ + `statement` on a bank-fed account whose statement ends before the period's last day: the statement carried to it by the feed's lines. + """ + rollForward: ReconciliationRollForward } """ diff --git a/robosystems_client/models/__init__.py b/robosystems_client/models/__init__.py index 57be5a7..06bab51 100644 --- a/robosystems_client/models/__init__.py +++ b/robosystems_client/models/__init__.py @@ -859,9 +859,16 @@ from .reconciliation_mechanics import ReconciliationMechanics from .reconciliation_mechanics_method import ReconciliationMechanicsMethod from .reconciliation_mechanics_scope import ReconciliationMechanicsScope +from .reconciliation_mechanics_statement_cycle_type_0 import ( + ReconciliationMechanicsStatementCycleType0, +) from .reconciliation_policy_response import ReconciliationPolicyResponse +from .reconciliation_policy_response_statement_cycle_type_0 import ( + ReconciliationPolicyResponseStatementCycleType0, +) from .reconciliation_preview_response import ReconciliationPreviewResponse from .reconciliation_preview_response_method import ReconciliationPreviewResponseMethod +from .reconciliation_roll_forward import ReconciliationRollForward from .reconciliation_row import ReconciliationRow from .reconciliation_summary import ReconciliationSummary from .reconciling_item_catch_up import ReconcilingItemCatchUp @@ -959,6 +966,9 @@ from .service_offerings_response import ServiceOfferingsResponse from .set_close_target_operation import SetCloseTargetOperation from .set_reconciliation_policy_request import SetReconciliationPolicyRequest +from .set_reconciliation_policy_request_statement_cycle_type_0 import ( + SetReconciliationPolicyRequestStatementCycleType0, +) from .set_selected_graph_request import SetSelectedGraphRequest from .set_write_policy_request import SetWritePolicyRequest from .set_write_policy_request_write_policy import SetWritePolicyRequestWritePolicy @@ -1686,9 +1696,12 @@ "ReconciliationMechanics", "ReconciliationMechanicsMethod", "ReconciliationMechanicsScope", + "ReconciliationMechanicsStatementCycleType0", "ReconciliationPolicyResponse", + "ReconciliationPolicyResponseStatementCycleType0", "ReconciliationPreviewResponse", "ReconciliationPreviewResponseMethod", + "ReconciliationRollForward", "ReconciliationRow", "ReconciliationSummary", "ReconcilingItemCatchUp", @@ -1764,6 +1777,7 @@ "ServiceOfferingSummary", "SetCloseTargetOperation", "SetReconciliationPolicyRequest", + "SetReconciliationPolicyRequestStatementCycleType0", "SetSelectedGraphRequest", "SetWritePolicyRequest", "SetWritePolicyRequestWritePolicy", diff --git a/robosystems_client/models/preview_reconciliations_request.py b/robosystems_client/models/preview_reconciliations_request.py index 01b7a2a..d62ad25 100644 --- a/robosystems_client/models/preview_reconciliations_request.py +++ b/robosystems_client/models/preview_reconciliations_request.py @@ -23,8 +23,8 @@ class PreviewReconciliationsRequest: method (PreviewReconciliationsRequestMethod | Unset): Which check to preview. `source_ledger` compares every account with the synced accounting system's own trial balance. `schedule_register` compares each asset account a schedule carries a balance on with what its schedules say it holds. `statement` compares each account that has a - statement balance recorded in the period with that balance. Default: - PreviewReconciliationsRequestMethod.SOURCE_LEDGER. + statement covering the period with that balance, adjusted on a bank-fed account by the lines the bank had not + cleared. Default: PreviewReconciliationsRequestMethod.SOURCE_LEDGER. include_tied (bool | Unset): Also return the accounts that tie. Off by default: the differences are the work, and the counts cover the rest. Default: False. entity_id (None | str | Unset): The entity whose books to compare, by id. Omit for the group parent. diff --git a/robosystems_client/models/reconciliation_component.py b/robosystems_client/models/reconciliation_component.py index cb2ffc6..2765713 100644 --- a/robosystems_client/models/reconciliation_component.py +++ b/robosystems_client/models/reconciliation_component.py @@ -1,5 +1,6 @@ from __future__ import annotations +import datetime from collections.abc import Mapping from typing import Any, TypeVar, cast @@ -14,13 +15,19 @@ @_attrs_define class ReconciliationComponent: """One part of an account's independent balance: what a single schedule - says the account carries, or a recorded statement balance. + says the account carries, a recorded statement balance, or a ledger line + outstanding at the statement's date. Attributes: - name (str): The schedule's name, or the statement and its date. + name (str): The schedule's name, the statement and its date, or the outstanding line's description. amount (float): What this part says the account holds, debit-positive. + kind (None | str | Unset): `schedule`, `statement`, or `outstanding`: a ledger line on a bank-fed account that + did not come from the feed, dated on or before the statement, so the bank had not cleared it. + posting_date (datetime.date | None | Unset): An `outstanding` line's posting date. + entry_id (None | str | Unset): The journal entry an `outstanding` line belongs to. structure_id (None | str | Unset): The schedule, for a `schedule_register` part. - event_id (None | str | Unset): The recorded balance, for a `statement` part. + event_id (None | str | Unset): The recorded balance, for a `statement` part; the event behind an `outstanding` + line, when it has one. document_id (None | str | Unset): The statement document given as evidence, when one was. note (None | str | Unset): Why a schedule carries nothing (disposed of, or ended early), or the note recorded with a statement balance. @@ -28,6 +35,9 @@ class ReconciliationComponent: name: str amount: float + kind: None | str | Unset = UNSET + posting_date: datetime.date | None | Unset = UNSET + entry_id: None | str | Unset = UNSET structure_id: None | str | Unset = UNSET event_id: None | str | Unset = UNSET document_id: None | str | Unset = UNSET @@ -39,6 +49,26 @@ def to_dict(self) -> dict[str, Any]: amount = self.amount + kind: None | str | Unset + if isinstance(self.kind, Unset): + kind = UNSET + else: + kind = self.kind + + posting_date: None | str | Unset + if isinstance(self.posting_date, Unset): + posting_date = UNSET + elif isinstance(self.posting_date, datetime.date): + posting_date = self.posting_date.isoformat() + else: + posting_date = self.posting_date + + entry_id: None | str | Unset + if isinstance(self.entry_id, Unset): + entry_id = UNSET + else: + entry_id = self.entry_id + structure_id: None | str | Unset if isinstance(self.structure_id, Unset): structure_id = UNSET @@ -71,6 +101,12 @@ def to_dict(self) -> dict[str, Any]: "amount": amount, } ) + if kind is not UNSET: + field_dict["kind"] = kind + if posting_date is not UNSET: + field_dict["posting_date"] = posting_date + if entry_id is not UNSET: + field_dict["entry_id"] = entry_id if structure_id is not UNSET: field_dict["structure_id"] = structure_id if event_id is not UNSET: @@ -89,6 +125,41 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: amount = d.pop("amount") + def _parse_kind(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + kind = _parse_kind(d.pop("kind", UNSET)) + + def _parse_posting_date(data: object) -> datetime.date | None | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + try: + if not isinstance(data, str): + raise TypeError() + posting_date_type_0 = datetime.date.fromisoformat(data) + + return posting_date_type_0 + except (TypeError, ValueError, AttributeError, KeyError): + pass + return cast(datetime.date | None | Unset, data) + + posting_date = _parse_posting_date(d.pop("posting_date", UNSET)) + + def _parse_entry_id(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + entry_id = _parse_entry_id(d.pop("entry_id", UNSET)) + def _parse_structure_id(data: object) -> None | str | Unset: if data is None: return data @@ -128,6 +199,9 @@ def _parse_note(data: object) -> None | str | Unset: reconciliation_component = cls( name=name, amount=amount, + kind=kind, + posting_date=posting_date, + entry_id=entry_id, structure_id=structure_id, event_id=event_id, document_id=document_id, diff --git a/robosystems_client/models/reconciliation_mechanics.py b/robosystems_client/models/reconciliation_mechanics.py index 15ad84f..59b01a7 100644 --- a/robosystems_client/models/reconciliation_mechanics.py +++ b/robosystems_client/models/reconciliation_mechanics.py @@ -8,6 +8,9 @@ from ..models.reconciliation_mechanics_method import ReconciliationMechanicsMethod from ..models.reconciliation_mechanics_scope import ReconciliationMechanicsScope +from ..models.reconciliation_mechanics_statement_cycle_type_0 import ( + ReconciliationMechanicsStatementCycleType0, +) from ..types import UNSET, Unset T = TypeVar("T", bound="ReconciliationMechanics") @@ -34,6 +37,9 @@ class ReconciliationMechanics: reconcile. Default: False. separate_reviewer (bool | Unset): Whether the person who signs off must be someone other than the person who ran the comparison. Default: False. + statement_cycle (None | ReconciliationMechanicsStatementCycleType0 | Unset): `statement` blocks only: how often + the account's statement is issued; unset is monthly. A period is covered by the latest statement ending within + the cycle that ends with it. """ scope: ReconciliationMechanicsScope @@ -44,6 +50,7 @@ class ReconciliationMechanics: materiality: float | Unset = 0.0 review_required: bool | Unset = False separate_reviewer: bool | Unset = False + statement_cycle: None | ReconciliationMechanicsStatementCycleType0 | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: @@ -67,6 +74,14 @@ def to_dict(self) -> dict[str, Any]: separate_reviewer = self.separate_reviewer + statement_cycle: None | str | Unset + if isinstance(self.statement_cycle, Unset): + statement_cycle = UNSET + elif isinstance(self.statement_cycle, ReconciliationMechanicsStatementCycleType0): + statement_cycle = self.statement_cycle.value + else: + statement_cycle = self.statement_cycle + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update( @@ -87,6 +102,8 @@ def to_dict(self) -> dict[str, Any]: field_dict["review_required"] = review_required if separate_reviewer is not UNSET: field_dict["separate_reviewer"] = separate_reviewer + if statement_cycle is not UNSET: + field_dict["statement_cycle"] = statement_cycle return field_dict @@ -118,6 +135,25 @@ def _parse_element_id(data: object) -> None | str | Unset: separate_reviewer = d.pop("separate_reviewer", UNSET) + def _parse_statement_cycle( + data: object, + ) -> None | ReconciliationMechanicsStatementCycleType0 | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + try: + if not isinstance(data, str): + raise TypeError() + statement_cycle_type_0 = ReconciliationMechanicsStatementCycleType0(data) + + return statement_cycle_type_0 + except (TypeError, ValueError, AttributeError, KeyError): + pass + return cast(None | ReconciliationMechanicsStatementCycleType0 | Unset, data) + + statement_cycle = _parse_statement_cycle(d.pop("statement_cycle", UNSET)) + reconciliation_mechanics = cls( scope=scope, method=method, @@ -127,6 +163,7 @@ def _parse_element_id(data: object) -> None | str | Unset: materiality=materiality, review_required=review_required, separate_reviewer=separate_reviewer, + statement_cycle=statement_cycle, ) reconciliation_mechanics.additional_properties = d diff --git a/robosystems_client/models/reconciliation_mechanics_statement_cycle_type_0.py b/robosystems_client/models/reconciliation_mechanics_statement_cycle_type_0.py new file mode 100644 index 0000000..fd5446b --- /dev/null +++ b/robosystems_client/models/reconciliation_mechanics_statement_cycle_type_0.py @@ -0,0 +1,10 @@ +from enum import Enum + + +class ReconciliationMechanicsStatementCycleType0(str, Enum): + ANNUAL = "annual" + MONTHLY = "monthly" + QUARTERLY = "quarterly" + + def __str__(self) -> str: + return str(self.value) diff --git a/robosystems_client/models/reconciliation_policy_response.py b/robosystems_client/models/reconciliation_policy_response.py index 132a312..377615d 100644 --- a/robosystems_client/models/reconciliation_policy_response.py +++ b/robosystems_client/models/reconciliation_policy_response.py @@ -1,11 +1,16 @@ 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 ..models.reconciliation_policy_response_statement_cycle_type_0 import ( + ReconciliationPolicyResponseStatementCycleType0, +) +from ..types import UNSET, Unset + T = TypeVar("T", bound="ReconciliationPolicyResponse") @@ -19,6 +24,8 @@ class ReconciliationPolicyResponse: materiality (float): review_required (bool): separate_reviewer (bool): + statement_cycle (None | ReconciliationPolicyResponseStatementCycleType0 | Unset): `statement` blocks only: how + often the statement is issued. """ structure_id: str @@ -26,6 +33,9 @@ class ReconciliationPolicyResponse: materiality: float review_required: bool separate_reviewer: bool + statement_cycle: None | ReconciliationPolicyResponseStatementCycleType0 | Unset = ( + UNSET + ) additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: @@ -39,6 +49,16 @@ def to_dict(self) -> dict[str, Any]: separate_reviewer = self.separate_reviewer + statement_cycle: None | str | Unset + if isinstance(self.statement_cycle, Unset): + statement_cycle = UNSET + elif isinstance( + self.statement_cycle, ReconciliationPolicyResponseStatementCycleType0 + ): + statement_cycle = self.statement_cycle.value + else: + statement_cycle = self.statement_cycle + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update( @@ -50,6 +70,8 @@ def to_dict(self) -> dict[str, Any]: "separate_reviewer": separate_reviewer, } ) + if statement_cycle is not UNSET: + field_dict["statement_cycle"] = statement_cycle return field_dict @@ -66,12 +88,32 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: separate_reviewer = d.pop("separate_reviewer") + def _parse_statement_cycle( + data: object, + ) -> None | ReconciliationPolicyResponseStatementCycleType0 | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + try: + if not isinstance(data, str): + raise TypeError() + statement_cycle_type_0 = ReconciliationPolicyResponseStatementCycleType0(data) + + return statement_cycle_type_0 + except (TypeError, ValueError, AttributeError, KeyError): + pass + return cast(None | ReconciliationPolicyResponseStatementCycleType0 | Unset, data) + + statement_cycle = _parse_statement_cycle(d.pop("statement_cycle", UNSET)) + reconciliation_policy_response = cls( structure_id=structure_id, required_for_close=required_for_close, materiality=materiality, review_required=review_required, separate_reviewer=separate_reviewer, + statement_cycle=statement_cycle, ) reconciliation_policy_response.additional_properties = d diff --git a/robosystems_client/models/reconciliation_policy_response_statement_cycle_type_0.py b/robosystems_client/models/reconciliation_policy_response_statement_cycle_type_0.py new file mode 100644 index 0000000..fa535c7 --- /dev/null +++ b/robosystems_client/models/reconciliation_policy_response_statement_cycle_type_0.py @@ -0,0 +1,10 @@ +from enum import Enum + + +class ReconciliationPolicyResponseStatementCycleType0(str, Enum): + ANNUAL = "annual" + MONTHLY = "monthly" + QUARTERLY = "quarterly" + + def __str__(self) -> str: + return str(self.value) diff --git a/robosystems_client/models/reconciliation_roll_forward.py b/robosystems_client/models/reconciliation_roll_forward.py new file mode 100644 index 0000000..bccfb0d --- /dev/null +++ b/robosystems_client/models/reconciliation_roll_forward.py @@ -0,0 +1,170 @@ +from __future__ import annotations + +import datetime +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="ReconciliationRollForward") + + +@_attrs_define +class ReconciliationRollForward: + """A statement carried from its ending date to the period's last day on a + bank-fed account, where every line the feed brought is the bank's own. + + Attributes: + statement_as_of (datetime.date): The statement's ending date. + through (datetime.date): The period's last day. + bank_lines (int): Booked feed lines dated after the statement, to the period end. + bank_activity (float): Their net effect on the account, debit-positive. + bank_balance (float): The statement balance carried to the period end by those lines: what the bank held then, + debit-positive. + ledger_balance (float): The ledger's balance at the period end, as the close will leave it, debit-positive. + outstanding (float): Ledger lines not from the feed and dated on or before the period end, net: the ledger minus + the carried bank balance. + feed_balance (float | None | Unset): A cross-check, never the figure reconciled: the bank feed's own balance + from the first reading on or after the period end, less the booked feed lines between the period end and that + reading. Null when the feed has no reading within ten days. + feed_balance_read_on (datetime.date | None | Unset): The day of the feed reading `feed_balance` starts from. + """ + + statement_as_of: datetime.date + through: datetime.date + bank_lines: int + bank_activity: float + bank_balance: float + ledger_balance: float + outstanding: float + feed_balance: float | None | Unset = UNSET + feed_balance_read_on: datetime.date | None | Unset = UNSET + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + statement_as_of = self.statement_as_of.isoformat() + + through = self.through.isoformat() + + bank_lines = self.bank_lines + + bank_activity = self.bank_activity + + bank_balance = self.bank_balance + + ledger_balance = self.ledger_balance + + outstanding = self.outstanding + + feed_balance: float | None | Unset + if isinstance(self.feed_balance, Unset): + feed_balance = UNSET + else: + feed_balance = self.feed_balance + + feed_balance_read_on: None | str | Unset + if isinstance(self.feed_balance_read_on, Unset): + feed_balance_read_on = UNSET + elif isinstance(self.feed_balance_read_on, datetime.date): + feed_balance_read_on = self.feed_balance_read_on.isoformat() + else: + feed_balance_read_on = self.feed_balance_read_on + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update( + { + "statement_as_of": statement_as_of, + "through": through, + "bank_lines": bank_lines, + "bank_activity": bank_activity, + "bank_balance": bank_balance, + "ledger_balance": ledger_balance, + "outstanding": outstanding, + } + ) + if feed_balance is not UNSET: + field_dict["feed_balance"] = feed_balance + if feed_balance_read_on is not UNSET: + field_dict["feed_balance_read_on"] = feed_balance_read_on + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + statement_as_of = datetime.date.fromisoformat(d.pop("statement_as_of")) + + through = datetime.date.fromisoformat(d.pop("through")) + + bank_lines = d.pop("bank_lines") + + bank_activity = d.pop("bank_activity") + + bank_balance = d.pop("bank_balance") + + ledger_balance = d.pop("ledger_balance") + + outstanding = d.pop("outstanding") + + def _parse_feed_balance(data: object) -> float | None | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(float | None | Unset, data) + + feed_balance = _parse_feed_balance(d.pop("feed_balance", UNSET)) + + def _parse_feed_balance_read_on(data: object) -> datetime.date | None | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + try: + if not isinstance(data, str): + raise TypeError() + feed_balance_read_on_type_0 = datetime.date.fromisoformat(data) + + return feed_balance_read_on_type_0 + except (TypeError, ValueError, AttributeError, KeyError): + pass + return cast(datetime.date | None | Unset, data) + + feed_balance_read_on = _parse_feed_balance_read_on( + d.pop("feed_balance_read_on", UNSET) + ) + + reconciliation_roll_forward = cls( + statement_as_of=statement_as_of, + through=through, + bank_lines=bank_lines, + bank_activity=bank_activity, + bank_balance=bank_balance, + ledger_balance=ledger_balance, + outstanding=outstanding, + feed_balance=feed_balance, + feed_balance_read_on=feed_balance_read_on, + ) + + reconciliation_roll_forward.additional_properties = d + return reconciliation_roll_forward + + @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/reconciliation_row.py b/robosystems_client/models/reconciliation_row.py index fbc7a59..035baff 100644 --- a/robosystems_client/models/reconciliation_row.py +++ b/robosystems_client/models/reconciliation_row.py @@ -11,6 +11,7 @@ if TYPE_CHECKING: from ..models.reconciliation_component import ReconciliationComponent + from ..models.reconciliation_roll_forward import ReconciliationRollForward T = TypeVar("T", bound="ReconciliationRow") @@ -27,7 +28,8 @@ class ReconciliationRow: ledger_balance (float): What the ledger holds. For `source_ledger`, landed entries only. For `schedule_register`, the balance as the period's close will leave it: landed entries, drafts awaiting the close, and schedule entries not yet drafted. - independent_balance (float): What the independent source says. + independent_balance (float): What the independent source says. For a `statement` on a bank-fed account, the + statement balance adjusted by its outstanding lines. difference (float): Ledger minus independent. status (str): `tied`: both sides agree to the cent. `different`: both know the account and disagree. `not_in_ledger`: the source reports an account the ledger has none for. `not_in_source`: the ledger holds a @@ -41,7 +43,10 @@ class ReconciliationRow: as_of (datetime.date | None | Unset): The date both balances are stated at, when it is not the period's last day: a statement that ends mid-period is compared with the ledger at the statement's own date. components (list[ReconciliationComponent] | Unset): Account-scope methods only: what makes up the independent - balance. One entry per schedule for `schedule_register`; the recorded statement for `statement`. + balance. One entry per schedule for `schedule_register`; the recorded statement for `statement`, then each + outstanding line. + roll_forward (None | ReconciliationRollForward | Unset): `statement` on a bank-fed account whose statement ends + before the period's last day: the statement carried to it by the feed's lines. """ account_name: str @@ -55,9 +60,12 @@ class ReconciliationRow: statement: None | str | Unset = UNSET as_of: datetime.date | None | Unset = UNSET components: list[ReconciliationComponent] | Unset = UNSET + roll_forward: None | ReconciliationRollForward | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: + from ..models.reconciliation_roll_forward import ReconciliationRollForward + account_name = self.account_name ledger_balance = self.ledger_balance @@ -107,6 +115,14 @@ def to_dict(self) -> dict[str, Any]: components_item = components_item_data.to_dict() components.append(components_item) + roll_forward: dict[str, Any] | None | Unset + if isinstance(self.roll_forward, Unset): + roll_forward = UNSET + elif isinstance(self.roll_forward, ReconciliationRollForward): + roll_forward = self.roll_forward.to_dict() + else: + roll_forward = self.roll_forward + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update( @@ -130,12 +146,15 @@ def to_dict(self) -> dict[str, Any]: field_dict["as_of"] = as_of if components is not UNSET: field_dict["components"] = components + if roll_forward is not UNSET: + field_dict["roll_forward"] = roll_forward return field_dict @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: from ..models.reconciliation_component import ReconciliationComponent + from ..models.reconciliation_roll_forward import ReconciliationRollForward d = dict(src_dict) account_name = d.pop("account_name") @@ -210,6 +229,23 @@ def _parse_as_of(data: object) -> datetime.date | None | Unset: components.append(components_item) + def _parse_roll_forward(data: object) -> None | ReconciliationRollForward | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + try: + if not isinstance(data, dict): + raise TypeError() + roll_forward_type_0 = ReconciliationRollForward.from_dict(data) + + return roll_forward_type_0 + except (TypeError, ValueError, AttributeError, KeyError): + pass + return cast(None | ReconciliationRollForward | Unset, data) + + roll_forward = _parse_roll_forward(d.pop("roll_forward", UNSET)) + reconciliation_row = cls( account_name=account_name, ledger_balance=ledger_balance, @@ -222,6 +258,7 @@ def _parse_as_of(data: object) -> datetime.date | None | Unset: statement=statement, as_of=as_of, components=components, + roll_forward=roll_forward, ) reconciliation_row.additional_properties = d diff --git a/robosystems_client/models/reconciliation_summary.py b/robosystems_client/models/reconciliation_summary.py index 09c329b..b33255c 100644 --- a/robosystems_client/models/reconciliation_summary.py +++ b/robosystems_client/models/reconciliation_summary.py @@ -11,6 +11,7 @@ if TYPE_CHECKING: from ..models.reconciliation_component import ReconciliationComponent + from ..models.reconciliation_roll_forward import ReconciliationRollForward from ..models.reconciliation_row import ReconciliationRow @@ -39,6 +40,8 @@ class ReconciliationSummary: review_required (bool): Whether the close also waits for a sign-off. separate_reviewer (bool): Whether the reviewer must be someone other than the person who ran the comparison. element_id (None | str | Unset): The account reconciled; null for a ledger-scope block. + statement_cycle (None | str | Unset): `statement` blocks only: how often the account's statement is issued, + `monthly`, `quarterly` or `annual`. unreconciled_difference (float | None | Unset): What is left unexplained at the last comparison; null when the period has not been compared. For a ledger-scope block, the sum of every account's absolute difference. For an account-scope block, the ledger balance minus the independent one. @@ -49,9 +52,12 @@ class ReconciliationSummary: independent_balance (float | None | Unset): Account-scope only: what the independent source said at the last comparison, debit-positive. balance_as_of (datetime.date | None | Unset): Account-scope only: the date the two balances are stated at. The - period's last day, unless a statement ended earlier in the period. + period's last day, unless the statement covering it ended earlier. components (list[ReconciliationComponent] | Unset): Account-scope only: what makes up the independent balance. - One entry per schedule for `schedule_register`; the recorded statement for `statement`. + One entry per schedule for `schedule_register`; the recorded statement for `statement`, then each outstanding + line. + roll_forward (None | ReconciliationRollForward | Unset): `statement` on a bank-fed account whose statement ends + before the period's last day: the statement carried to it by the feed's lines. source (None | str | Unset): The system the independent side was read from. compared_at (datetime.datetime | None | Unset): When the two sides were last compared. fact_set_id (None | str | Unset): The FactSet holding the period's comparison. @@ -79,6 +85,7 @@ class ReconciliationSummary: review_required: bool separate_reviewer: bool element_id: None | str | Unset = UNSET + statement_cycle: None | str | Unset = UNSET unreconciled_difference: float | None | Unset = UNSET accounts_compared: int | None | Unset = UNSET accounts_different: int | None | Unset = UNSET @@ -86,6 +93,7 @@ class ReconciliationSummary: independent_balance: float | None | Unset = UNSET balance_as_of: datetime.date | None | Unset = UNSET components: list[ReconciliationComponent] | Unset = UNSET + roll_forward: None | ReconciliationRollForward | Unset = UNSET source: None | str | Unset = UNSET compared_at: datetime.datetime | None | Unset = UNSET fact_set_id: None | str | Unset = UNSET @@ -98,6 +106,8 @@ class ReconciliationSummary: additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: + from ..models.reconciliation_roll_forward import ReconciliationRollForward + structure_id = self.structure_id name = self.name @@ -126,6 +136,12 @@ def to_dict(self) -> dict[str, Any]: else: element_id = self.element_id + statement_cycle: None | str | Unset + if isinstance(self.statement_cycle, Unset): + statement_cycle = UNSET + else: + statement_cycle = self.statement_cycle + unreconciled_difference: float | None | Unset if isinstance(self.unreconciled_difference, Unset): unreconciled_difference = UNSET @@ -171,6 +187,14 @@ def to_dict(self) -> dict[str, Any]: components_item = components_item_data.to_dict() components.append(components_item) + roll_forward: dict[str, Any] | None | Unset + if isinstance(self.roll_forward, Unset): + roll_forward = UNSET + elif isinstance(self.roll_forward, ReconciliationRollForward): + roll_forward = self.roll_forward.to_dict() + else: + roll_forward = self.roll_forward + source: None | str | Unset if isinstance(self.source, Unset): source = UNSET @@ -249,6 +273,8 @@ def to_dict(self) -> dict[str, Any]: ) if element_id is not UNSET: field_dict["element_id"] = element_id + if statement_cycle is not UNSET: + field_dict["statement_cycle"] = statement_cycle if unreconciled_difference is not UNSET: field_dict["unreconciled_difference"] = unreconciled_difference if accounts_compared is not UNSET: @@ -263,6 +289,8 @@ def to_dict(self) -> dict[str, Any]: field_dict["balance_as_of"] = balance_as_of if components is not UNSET: field_dict["components"] = components + if roll_forward is not UNSET: + field_dict["roll_forward"] = roll_forward if source is not UNSET: field_dict["source"] = source if compared_at is not UNSET: @@ -287,6 +315,7 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: from ..models.reconciliation_component import ReconciliationComponent + from ..models.reconciliation_roll_forward import ReconciliationRollForward from ..models.reconciliation_row import ReconciliationRow d = dict(src_dict) @@ -321,6 +350,15 @@ def _parse_element_id(data: object) -> None | str | Unset: element_id = _parse_element_id(d.pop("element_id", UNSET)) + def _parse_statement_cycle(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + statement_cycle = _parse_statement_cycle(d.pop("statement_cycle", UNSET)) + def _parse_unreconciled_difference(data: object) -> float | None | Unset: if data is None: return data @@ -396,6 +434,23 @@ def _parse_balance_as_of(data: object) -> datetime.date | None | Unset: components.append(components_item) + def _parse_roll_forward(data: object) -> None | ReconciliationRollForward | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + try: + if not isinstance(data, dict): + raise TypeError() + roll_forward_type_0 = ReconciliationRollForward.from_dict(data) + + return roll_forward_type_0 + except (TypeError, ValueError, AttributeError, KeyError): + pass + return cast(None | ReconciliationRollForward | Unset, data) + + roll_forward = _parse_roll_forward(d.pop("roll_forward", UNSET)) + def _parse_source(data: object) -> None | str | Unset: if data is None: return data @@ -506,6 +561,7 @@ def _parse_self_reviewed(data: object) -> bool | None | Unset: review_required=review_required, separate_reviewer=separate_reviewer, element_id=element_id, + statement_cycle=statement_cycle, unreconciled_difference=unreconciled_difference, accounts_compared=accounts_compared, accounts_different=accounts_different, @@ -513,6 +569,7 @@ def _parse_self_reviewed(data: object) -> bool | None | Unset: independent_balance=independent_balance, balance_as_of=balance_as_of, components=components, + roll_forward=roll_forward, source=source, compared_at=compared_at, fact_set_id=fact_set_id, diff --git a/robosystems_client/models/set_reconciliation_policy_request.py b/robosystems_client/models/set_reconciliation_policy_request.py index 64695ad..85eac3a 100644 --- a/robosystems_client/models/set_reconciliation_policy_request.py +++ b/robosystems_client/models/set_reconciliation_policy_request.py @@ -6,6 +6,9 @@ from attrs import define as _attrs_define from attrs import field as _attrs_field +from ..models.set_reconciliation_policy_request_statement_cycle_type_0 import ( + SetReconciliationPolicyRequestStatementCycleType0, +) from ..types import UNSET, Unset T = TypeVar("T", bound="SetReconciliationPolicyRequest") @@ -24,6 +27,9 @@ class SetReconciliationPolicyRequest: separate_reviewer (bool | None | Unset): Whether the person who signs off must be someone other than the person who ran the comparison. It can only be turned on when the graph has at least two members who can write. Omit to keep. + statement_cycle (None | SetReconciliationPolicyRequestStatementCycleType0 | Unset): `statement` blocks only: how + often the account's statement is issued. A period is covered by the latest statement ending within the cycle + that ends with it, so a quarterly statement covers the two months before the next one. Omit to keep. """ structure_id: str @@ -31,6 +37,9 @@ class SetReconciliationPolicyRequest: materiality: float | None | Unset = UNSET review_required: bool | None | Unset = UNSET separate_reviewer: bool | None | Unset = UNSET + statement_cycle: None | SetReconciliationPolicyRequestStatementCycleType0 | Unset = ( + UNSET + ) additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: @@ -60,6 +69,16 @@ def to_dict(self) -> dict[str, Any]: else: separate_reviewer = self.separate_reviewer + statement_cycle: None | str | Unset + if isinstance(self.statement_cycle, Unset): + statement_cycle = UNSET + elif isinstance( + self.statement_cycle, SetReconciliationPolicyRequestStatementCycleType0 + ): + statement_cycle = self.statement_cycle.value + else: + statement_cycle = self.statement_cycle + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update( @@ -75,6 +94,8 @@ def to_dict(self) -> dict[str, Any]: field_dict["review_required"] = review_required if separate_reviewer is not UNSET: field_dict["separate_reviewer"] = separate_reviewer + if statement_cycle is not UNSET: + field_dict["statement_cycle"] = statement_cycle return field_dict @@ -119,12 +140,34 @@ def _parse_separate_reviewer(data: object) -> bool | None | Unset: separate_reviewer = _parse_separate_reviewer(d.pop("separate_reviewer", UNSET)) + def _parse_statement_cycle( + data: object, + ) -> None | SetReconciliationPolicyRequestStatementCycleType0 | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + try: + if not isinstance(data, str): + raise TypeError() + statement_cycle_type_0 = SetReconciliationPolicyRequestStatementCycleType0(data) + + return statement_cycle_type_0 + except (TypeError, ValueError, AttributeError, KeyError): + pass + return cast( + None | SetReconciliationPolicyRequestStatementCycleType0 | Unset, data + ) + + statement_cycle = _parse_statement_cycle(d.pop("statement_cycle", UNSET)) + set_reconciliation_policy_request = cls( structure_id=structure_id, required_for_close=required_for_close, materiality=materiality, review_required=review_required, separate_reviewer=separate_reviewer, + statement_cycle=statement_cycle, ) set_reconciliation_policy_request.additional_properties = d diff --git a/robosystems_client/models/set_reconciliation_policy_request_statement_cycle_type_0.py b/robosystems_client/models/set_reconciliation_policy_request_statement_cycle_type_0.py new file mode 100644 index 0000000..2078120 --- /dev/null +++ b/robosystems_client/models/set_reconciliation_policy_request_statement_cycle_type_0.py @@ -0,0 +1,10 @@ +from enum import Enum + + +class SetReconciliationPolicyRequestStatementCycleType0(str, Enum): + ANNUAL = "annual" + MONTHLY = "monthly" + QUARTERLY = "quarterly" + + def __str__(self) -> str: + return str(self.value) diff --git a/tests/test_ledger_client.py b/tests/test_ledger_client.py index 6007549..67df6ea 100644 --- a/tests/test_ledger_client.py +++ b/tests/test_ledger_client.py @@ -2882,6 +2882,9 @@ def test_list_reconciliations(self, mock_execute, mock_config, graph_id): component = { "name": "Statement ending 2026-08-31", "amount": 4800.0, + "kind": "statement", + "postingDate": None, + "entryId": None, "structureId": None, "eventId": "evt_1", "documentId": None, @@ -2901,6 +2904,7 @@ def test_list_reconciliations(self, mock_execute, mock_config, graph_id): "elementId": "elem_loan", "requiredForClose": False, "materiality": 0.0, + "statementCycle": "monthly", "period": "2026-08", "asOf": "2026-08-31", "status": "unreconciled", @@ -2911,6 +2915,7 @@ def test_list_reconciliations(self, mock_execute, mock_config, graph_id): "independentBalance": 4800.0, "balanceAsOf": "2026-08-31", "components": [component], + "rollForward": None, "source": "statement", "comparedAt": "2026-09-02T08:00:00Z", "factSetId": "fs_1", @@ -2934,6 +2939,7 @@ def test_list_reconciliations(self, mock_execute, mock_config, graph_id): "status": "different", "asOf": "2026-08-31", "components": [component], + "rollForward": None, } ], } @@ -2948,6 +2954,7 @@ def test_list_reconciliations(self, mock_execute, mock_config, graph_id): (rec,) = result.reconciliations assert (rec.status, rec.unreconciled_difference) == ("unreconciled", 200.0) assert rec.components[0].event_id == "evt_1" + assert (rec.statement_cycle, rec.roll_forward) == ("monthly", None) assert rec.differences[0].account_name == "Equipment Loan" assert mock_execute.call_args.args[2] == {"period": "2026-08"} @@ -2959,6 +2966,99 @@ def test_list_reconciliations_on_a_graph_without_a_ledger( client = LedgerClient(mock_config) assert client.list_reconciliations(graph_id, "2026-08") is None + @patch("robosystems_client.graphql.client.GraphQLClient.execute") + def test_list_reconciliations_carries_a_bank_statements_parts( + self, mock_execute, mock_config, graph_id + ): + mock_execute.return_value = { + "reconciliations": { + "period": "2026-09", + "asOf": "2026-09-30", + "notes": [], + "reconciliations": [ + { + "structureId": "struct_cash", + "name": "Checking (statement)", + "scope": "account", + "method": "statement", + "elementId": "elem_cash", + "requiredForClose": True, + "materiality": 0.0, + "statementCycle": "monthly", + "period": "2026-09", + "asOf": "2026-09-30", + "status": "reconciled", + "unreconciledDifference": 0.0, + "accountsCompared": None, + "accountsDifferent": None, + "ledgerBalance": 1450.0, + "independentBalance": 1450.0, + "balanceAsOf": "2026-09-05", + "components": [ + { + "name": "Statement ending 2026-09-05", + "amount": 1500.0, + "kind": "statement", + "postingDate": None, + "entryId": None, + "structureId": None, + "eventId": "evt_1", + "documentId": "doc_1", + "note": None, + }, + { + "name": "Vendor payment", + "amount": -50.0, + "kind": "outstanding", + "postingDate": "2026-09-04", + "entryId": "je_1", + "structureId": None, + "eventId": None, + "documentId": None, + "note": None, + }, + ], + "rollForward": { + "statementAsOf": "2026-09-05", + "through": "2026-09-30", + "bankLines": 1, + "bankActivity": -200.0, + "bankBalance": 1300.0, + "ledgerBalance": 1250.0, + "outstanding": -50.0, + "feedBalance": 1300.0, + "feedBalanceReadOn": "2026-10-03", + }, + "source": "statement", + "comparedAt": "2026-10-10T08:00:00Z", + "factSetId": "fs_1", + "comparedBy": "usr_1", + "comparedVia": "operation", + "reviewRequired": False, + "separateReviewer": False, + "reviewedBy": None, + "reviewedAt": None, + "selfReviewed": None, + "differences": [], + } + ], + } + } + client = LedgerClient(mock_config) + + result = client.list_reconciliations(graph_id, "2026-09") + + assert result is not None + (rec,) = result.reconciliations + outstanding = rec.components[1] + assert (outstanding.kind, outstanding.entry_id) == ("outstanding", "je_1") + assert str(outstanding.posting_date) == "2026-09-04" + assert rec.roll_forward is not None + assert (rec.roll_forward.bank_balance, rec.roll_forward.outstanding) == ( + 1300.0, + -50.0, + ) + @patch("robosystems_client.clients.ledger_client.op_preview_reconciliations") def test_preview_reconciliations_names_the_check( self, mock_op, mock_config, graph_id @@ -3070,6 +3170,27 @@ def test_set_reconciliation_policy_sends_only_what_changes( "required_for_close": True, } + @patch("robosystems_client.clients.ledger_client.op_set_reconciliation_policy") + def test_set_reconciliation_policy_sets_a_statement_cycle( + self, mock_op, mock_config, graph_id + ): + mock_op.return_value = _mock_response( + _envelope( + "set-reconciliation-policy", + {"structure_id": "struct_cash", "statement_cycle": "quarterly"}, + ) + ) + client = LedgerClient(mock_config) + + client.set_reconciliation_policy( + graph_id, "struct_cash", statement_cycle="quarterly" + ) + + assert mock_op.call_args.kwargs["body"].to_dict() == { + "structure_id": "struct_cash", + "statement_cycle": "quarterly", + } + @patch("robosystems_client.clients.ledger_client.op_set_reconciliation_policy") def test_set_reconciliation_policy_can_turn_a_setting_off( self, mock_op, mock_config, graph_id