@robosystems/client 0.2.48 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/extensions/InvestorClient.d.ts +91 -0
  2. package/extensions/InvestorClient.js +223 -0
  3. package/extensions/InvestorClient.ts +501 -0
  4. package/extensions/LedgerClient.d.ts +213 -165
  5. package/extensions/LedgerClient.js +315 -393
  6. package/extensions/LedgerClient.ts +887 -564
  7. package/extensions/ReportClient.d.ts +77 -115
  8. package/extensions/ReportClient.js +124 -211
  9. package/extensions/ReportClient.ts +298 -358
  10. package/extensions/config.js +1 -1
  11. package/extensions/config.ts +1 -1
  12. package/extensions/graphql/client.d.ts +46 -0
  13. package/extensions/graphql/client.js +75 -0
  14. package/extensions/graphql/client.ts +82 -0
  15. package/extensions/graphql/generated/graphql.d.ts +1786 -0
  16. package/extensions/graphql/generated/graphql.js +2671 -0
  17. package/extensions/graphql/generated/graphql.ts +4491 -0
  18. package/extensions/graphql/queries/investor/holdings.d.ts +8 -0
  19. package/extensions/graphql/queries/investor/holdings.js +36 -0
  20. package/extensions/graphql/queries/investor/holdings.ts +34 -0
  21. package/extensions/graphql/queries/investor/portfolio.d.ts +4 -0
  22. package/extensions/graphql/queries/investor/portfolio.js +21 -0
  23. package/extensions/graphql/queries/investor/portfolio.ts +19 -0
  24. package/extensions/graphql/queries/investor/portfolios.d.ts +4 -0
  25. package/extensions/graphql/queries/investor/portfolios.js +29 -0
  26. package/extensions/graphql/queries/investor/portfolios.ts +27 -0
  27. package/extensions/graphql/queries/investor/position.d.ts +4 -0
  28. package/extensions/graphql/queries/investor/position.js +33 -0
  29. package/extensions/graphql/queries/investor/position.ts +31 -0
  30. package/extensions/graphql/queries/investor/positions.d.ts +8 -0
  31. package/extensions/graphql/queries/investor/positions.js +57 -0
  32. package/extensions/graphql/queries/investor/positions.ts +55 -0
  33. package/extensions/graphql/queries/investor/securities.d.ts +8 -0
  34. package/extensions/graphql/queries/investor/securities.js +50 -0
  35. package/extensions/graphql/queries/investor/securities.ts +48 -0
  36. package/extensions/graphql/queries/investor/security.d.ts +4 -0
  37. package/extensions/graphql/queries/investor/security.js +26 -0
  38. package/extensions/graphql/queries/investor/security.ts +24 -0
  39. package/extensions/graphql/queries/ledger/accountRollups.d.ts +7 -0
  40. package/extensions/graphql/queries/ledger/accountRollups.js +36 -0
  41. package/extensions/graphql/queries/ledger/accountRollups.ts +34 -0
  42. package/extensions/graphql/queries/ledger/accountTree.d.ts +11 -0
  43. package/extensions/graphql/queries/ledger/accountTree.js +61 -0
  44. package/extensions/graphql/queries/ledger/accountTree.ts +59 -0
  45. package/extensions/graphql/queries/ledger/accounts.d.ts +8 -0
  46. package/extensions/graphql/queries/ledger/accounts.js +45 -0
  47. package/extensions/graphql/queries/ledger/accounts.ts +43 -0
  48. package/extensions/graphql/queries/ledger/closingBookStructures.d.ts +6 -0
  49. package/extensions/graphql/queries/ledger/closingBookStructures.js +27 -0
  50. package/extensions/graphql/queries/ledger/closingBookStructures.ts +25 -0
  51. package/extensions/graphql/queries/ledger/elements.d.ts +6 -0
  52. package/extensions/graphql/queries/ledger/elements.js +56 -0
  53. package/extensions/graphql/queries/ledger/elements.ts +54 -0
  54. package/extensions/graphql/queries/ledger/entities.d.ts +7 -0
  55. package/extensions/graphql/queries/ledger/entities.js +31 -0
  56. package/extensions/graphql/queries/ledger/entities.ts +29 -0
  57. package/extensions/graphql/queries/ledger/entity.d.ts +14 -0
  58. package/extensions/graphql/queries/ledger/entity.js +55 -0
  59. package/extensions/graphql/queries/ledger/entity.ts +54 -0
  60. package/extensions/graphql/queries/ledger/fiscalCalendar.d.ts +8 -0
  61. package/extensions/graphql/queries/ledger/fiscalCalendar.js +35 -0
  62. package/extensions/graphql/queries/ledger/fiscalCalendar.ts +33 -0
  63. package/extensions/graphql/queries/ledger/mappedTrialBalance.d.ts +9 -0
  64. package/extensions/graphql/queries/ledger/mappedTrialBalance.js +29 -0
  65. package/extensions/graphql/queries/ledger/mappedTrialBalance.ts +27 -0
  66. package/extensions/graphql/queries/ledger/mapping.d.ts +5 -0
  67. package/extensions/graphql/queries/ledger/mapping.js +35 -0
  68. package/extensions/graphql/queries/ledger/mapping.ts +33 -0
  69. package/extensions/graphql/queries/ledger/mappingCoverage.d.ts +5 -0
  70. package/extensions/graphql/queries/ledger/mappingCoverage.js +22 -0
  71. package/extensions/graphql/queries/ledger/mappingCoverage.ts +20 -0
  72. package/extensions/graphql/queries/ledger/mappings.d.ts +6 -0
  73. package/extensions/graphql/queries/ledger/mappings.js +23 -0
  74. package/extensions/graphql/queries/ledger/mappings.ts +21 -0
  75. package/extensions/graphql/queries/ledger/periodCloseStatus.d.ts +6 -0
  76. package/extensions/graphql/queries/ledger/periodCloseStatus.js +29 -0
  77. package/extensions/graphql/queries/ledger/periodCloseStatus.ts +27 -0
  78. package/extensions/graphql/queries/ledger/periodDrafts.d.ts +8 -0
  79. package/extensions/graphql/queries/ledger/periodDrafts.js +45 -0
  80. package/extensions/graphql/queries/ledger/periodDrafts.ts +43 -0
  81. package/extensions/graphql/queries/ledger/publishList.d.ts +5 -0
  82. package/extensions/graphql/queries/ledger/publishList.js +29 -0
  83. package/extensions/graphql/queries/ledger/publishList.ts +27 -0
  84. package/extensions/graphql/queries/ledger/publishLists.d.ts +5 -0
  85. package/extensions/graphql/queries/ledger/publishLists.js +29 -0
  86. package/extensions/graphql/queries/ledger/publishLists.ts +27 -0
  87. package/extensions/graphql/queries/ledger/report.d.ts +5 -0
  88. package/extensions/graphql/queries/ledger/report.js +40 -0
  89. package/extensions/graphql/queries/ledger/report.ts +38 -0
  90. package/extensions/graphql/queries/ledger/reportingTaxonomy.d.ts +6 -0
  91. package/extensions/graphql/queries/ledger/reportingTaxonomy.js +27 -0
  92. package/extensions/graphql/queries/ledger/reportingTaxonomy.ts +25 -0
  93. package/extensions/graphql/queries/ledger/reports.d.ts +8 -0
  94. package/extensions/graphql/queries/ledger/reports.js +45 -0
  95. package/extensions/graphql/queries/ledger/reports.ts +43 -0
  96. package/extensions/graphql/queries/ledger/scheduleFacts.d.ts +7 -0
  97. package/extensions/graphql/queries/ledger/scheduleFacts.js +24 -0
  98. package/extensions/graphql/queries/ledger/scheduleFacts.ts +22 -0
  99. package/extensions/graphql/queries/ledger/schedules.d.ts +6 -0
  100. package/extensions/graphql/queries/ledger/schedules.js +24 -0
  101. package/extensions/graphql/queries/ledger/schedules.ts +22 -0
  102. package/extensions/graphql/queries/ledger/statement.d.ts +9 -0
  103. package/extensions/graphql/queries/ledger/statement.js +43 -0
  104. package/extensions/graphql/queries/ledger/statement.ts +41 -0
  105. package/extensions/graphql/queries/ledger/structures.d.ts +5 -0
  106. package/extensions/graphql/queries/ledger/structures.js +22 -0
  107. package/extensions/graphql/queries/ledger/structures.ts +20 -0
  108. package/extensions/graphql/queries/ledger/summary.d.ts +7 -0
  109. package/extensions/graphql/queries/ledger/summary.js +25 -0
  110. package/extensions/graphql/queries/ledger/summary.ts +23 -0
  111. package/extensions/graphql/queries/ledger/taxonomies.d.ts +5 -0
  112. package/extensions/graphql/queries/ledger/taxonomies.js +28 -0
  113. package/extensions/graphql/queries/ledger/taxonomies.ts +26 -0
  114. package/extensions/graphql/queries/ledger/transaction.d.ts +6 -0
  115. package/extensions/graphql/queries/ledger/transaction.js +49 -0
  116. package/extensions/graphql/queries/ledger/transaction.ts +47 -0
  117. package/extensions/graphql/queries/ledger/transactions.d.ts +7 -0
  118. package/extensions/graphql/queries/ledger/transactions.js +49 -0
  119. package/extensions/graphql/queries/ledger/transactions.ts +47 -0
  120. package/extensions/graphql/queries/ledger/trialBalance.d.ts +7 -0
  121. package/extensions/graphql/queries/ledger/trialBalance.js +28 -0
  122. package/extensions/graphql/queries/ledger/trialBalance.ts +26 -0
  123. package/extensions/graphql/queries/ledger/unmappedElements.d.ts +8 -0
  124. package/extensions/graphql/queries/ledger/unmappedElements.js +29 -0
  125. package/extensions/graphql/queries/ledger/unmappedElements.ts +27 -0
  126. package/extensions/hooks.d.ts +1 -1
  127. package/extensions/index.d.ts +5 -1
  128. package/extensions/index.js +13 -1
  129. package/extensions/index.ts +14 -0
  130. package/index.ts +2 -2
  131. package/package.json +14 -5
  132. package/sdk/index.d.ts +2 -2
  133. package/sdk/index.js +38 -65
  134. package/sdk/index.ts +2 -2
  135. package/sdk/sdk.gen.d.ts +101 -220
  136. package/sdk/sdk.gen.js +251 -445
  137. package/sdk/sdk.gen.ts +216 -409
  138. package/sdk/types.gen.d.ts +4586 -7045
  139. package/sdk/types.gen.ts +4702 -7334
  140. package/sdk.gen.d.ts +101 -220
  141. package/sdk.gen.js +251 -445
  142. package/sdk.gen.ts +216 -409
  143. package/types.gen.d.ts +4586 -7045
  144. package/types.gen.ts +4702 -7334
  145. package/extensions/AgentClient.test.ts +0 -403
  146. package/extensions/LedgerClient.test.ts +0 -1138
  147. package/extensions/OperationClient.test.ts +0 -365
  148. package/extensions/QueryClient.test.ts +0 -432
  149. package/extensions/ReportClient.test.ts +0 -828
  150. package/extensions/SSEClient.test.ts +0 -375
  151. package/extensions/config.test.ts +0 -199
  152. package/extensions/hooks.test.ts +0 -373
  153. package/extensions/index.test.ts +0 -420
  154. package/sdk-extensions/AgentClient.d.ts +0 -82
  155. package/sdk-extensions/AgentClient.js +0 -218
  156. package/sdk-extensions/AgentClient.test.ts +0 -403
  157. package/sdk-extensions/AgentClient.ts +0 -321
  158. package/sdk-extensions/LedgerClient.d.ts +0 -242
  159. package/sdk-extensions/LedgerClient.js +0 -496
  160. package/sdk-extensions/LedgerClient.test.ts +0 -1138
  161. package/sdk-extensions/LedgerClient.ts +0 -800
  162. package/sdk-extensions/OperationClient.d.ts +0 -65
  163. package/sdk-extensions/OperationClient.js +0 -251
  164. package/sdk-extensions/OperationClient.test.ts +0 -365
  165. package/sdk-extensions/OperationClient.ts +0 -324
  166. package/sdk-extensions/QueryClient.d.ts +0 -52
  167. package/sdk-extensions/QueryClient.js +0 -313
  168. package/sdk-extensions/QueryClient.test.ts +0 -432
  169. package/sdk-extensions/QueryClient.ts +0 -420
  170. package/sdk-extensions/README.md +0 -901
  171. package/sdk-extensions/ReportClient.d.ts +0 -151
  172. package/sdk-extensions/ReportClient.js +0 -278
  173. package/sdk-extensions/ReportClient.test.ts +0 -828
  174. package/sdk-extensions/ReportClient.ts +0 -486
  175. package/sdk-extensions/SSEClient.d.ts +0 -62
  176. package/sdk-extensions/SSEClient.js +0 -154
  177. package/sdk-extensions/SSEClient.test.ts +0 -375
  178. package/sdk-extensions/SSEClient.ts +0 -210
  179. package/sdk-extensions/config.d.ts +0 -57
  180. package/sdk-extensions/config.js +0 -152
  181. package/sdk-extensions/config.test.ts +0 -199
  182. package/sdk-extensions/config.ts +0 -175
  183. package/sdk-extensions/hooks.d.ts +0 -110
  184. package/sdk-extensions/hooks.js +0 -384
  185. package/sdk-extensions/hooks.test.ts +0 -373
  186. package/sdk-extensions/hooks.ts +0 -459
  187. package/sdk-extensions/index.d.ts +0 -63
  188. package/sdk-extensions/index.js +0 -164
  189. package/sdk-extensions/index.test.ts +0 -420
  190. package/sdk-extensions/index.ts +0 -203
@@ -3,105 +3,269 @@
3
3
  /**
4
4
  * Ledger Client for RoboSystems API
5
5
  *
6
- * High-level client for all ledger concerns: chart of accounts, transactions,
7
- * trial balance, taxonomy, mappings, and AI auto-mapping. This is the
8
- * operational backbone reports consume these as inputs.
6
+ * High-level facade for everything the RoboLedger domain exposes:
7
+ * entity, chart of accounts, transactions, taxonomy + mappings, fiscal
8
+ * calendar, schedules, reports, and publish lists.
9
+ *
10
+ * **Transport split:**
11
+ * - **Reads** go through GraphQL at `/extensions/{graph_id}/graphql`
12
+ * (via `graphql-request`, with typed documents produced by GraphQL
13
+ * Code Generator). The graph is in the URL, not in the query.
14
+ * - **Writes** go through named command operations at
15
+ * `/extensions/roboledger/{graph_id}/operations/{operation_name}`
16
+ * (via the OpenAPI-generated `opXxx` functions in `../sdk/sdk.gen`).
17
+ * Each command returns an `OperationEnvelope`; the facade unwraps
18
+ * `envelope.result` and returns a friendly camelCase type.
19
+ *
20
+ * Consumers don't need to know which transport a method uses — the
21
+ * facade signature stays stable. The only trick is that write method
22
+ * results are cast from the envelope's untyped `result` field.
9
23
  */
10
24
 
25
+ import type { TypedDocumentNode } from '@graphql-typed-document-node/core'
26
+ import { ClientError } from 'graphql-request'
11
27
  import {
12
- autoMapElements,
13
- createClosingEntry,
14
- createMappingAssociation,
15
- createSchedule,
16
- createStructure,
17
- deleteMappingAssociation,
18
- getAccountRollups,
19
- getClosingBookStructures,
20
- getLedgerAccountTree,
21
- getLedgerEntity,
22
- getLedgerSummary,
23
- getLedgerTransaction,
24
- getLedgerTrialBalance,
25
- getMappedTrialBalance,
26
- getMappingCoverage,
27
- getMappingDetail,
28
- getPeriodCloseStatus,
29
- getReportingTaxonomy,
30
- getScheduleFacts,
31
- listElements,
32
- listLedgerAccounts,
33
- listLedgerTransactions,
34
- listMappings,
35
- listSchedules,
36
- listStructures,
28
+ opAutoMapElements,
29
+ opClosePeriod,
30
+ opCreateClosingEntry,
31
+ opCreateManualClosingEntry,
32
+ opCreateMappingAssociation,
33
+ opCreateSchedule,
34
+ opCreateStructure,
35
+ opCreateTaxonomy,
36
+ opDeleteMappingAssociation,
37
+ opInitializeLedger,
38
+ opReopenPeriod,
39
+ opSetCloseTarget,
40
+ opTruncateSchedule,
41
+ opUpdateEntity,
37
42
  } from '../sdk.gen'
38
43
  import type {
39
- AccountListResponse,
40
- AccountRollupsResponse,
41
- AccountTreeResponse,
42
- ClosingBookStructuresResponse,
43
- ClosingEntryResponse,
44
- CreateClosingEntryRequest,
44
+ AutoMapElementsOperation,
45
+ ClosePeriodOperation,
46
+ CreateClosingEntryOperation,
47
+ CreateManualClosingEntryRequest,
48
+ CreateMappingAssociationOperation,
45
49
  CreateScheduleRequest,
46
- LedgerSummaryResponse,
47
- LedgerTransactionDetailResponse,
48
- LedgerTransactionListResponse,
49
- MappingCoverageResponse,
50
- MappingDetailResponse,
51
- PeriodCloseItemResponse,
52
- PeriodCloseStatusResponse,
53
- ScheduleCreatedResponse,
54
- ScheduleFactResponse,
55
- ScheduleFactsResponse,
56
- ScheduleListResponse,
57
- ScheduleSummaryResponse,
58
- TrialBalanceResponse,
50
+ CreateStructureRequest,
51
+ CreateTaxonomyRequest,
52
+ DeleteMappingAssociationOperation,
53
+ InitializeLedgerRequest,
54
+ OperationEnvelope,
55
+ ReopenPeriodOperation,
56
+ SetCloseTargetOperation,
57
+ TruncateScheduleOperation,
58
+ UpdateEntityRequest,
59
59
  } from '../types.gen'
60
+ import { GraphQLClientCache } from './graphql/client'
61
+ import {
62
+ GetLedgerAccountRollupsDocument,
63
+ GetLedgerAccountTreeDocument,
64
+ GetLedgerClosingBookStructuresDocument,
65
+ GetLedgerEntityDocument,
66
+ GetLedgerFiscalCalendarDocument,
67
+ GetLedgerMappedTrialBalanceDocument,
68
+ GetLedgerMappingCoverageDocument,
69
+ GetLedgerMappingDocument,
70
+ GetLedgerPeriodCloseStatusDocument,
71
+ GetLedgerPeriodDraftsDocument,
72
+ GetLedgerReportingTaxonomyDocument,
73
+ GetLedgerScheduleFactsDocument,
74
+ GetLedgerSummaryDocument,
75
+ GetLedgerTransactionDocument,
76
+ GetLedgerTrialBalanceDocument,
77
+ ListLedgerAccountsDocument,
78
+ ListLedgerElementsDocument,
79
+ ListLedgerEntitiesDocument,
80
+ ListLedgerMappingsDocument,
81
+ ListLedgerSchedulesDocument,
82
+ ListLedgerStructuresDocument,
83
+ ListLedgerTaxonomiesDocument,
84
+ ListLedgerTransactionsDocument,
85
+ ListLedgerUnmappedElementsDocument,
86
+ type GetLedgerAccountRollupsQuery,
87
+ type GetLedgerAccountTreeQuery,
88
+ type GetLedgerClosingBookStructuresQuery,
89
+ type GetLedgerEntityQuery,
90
+ type GetLedgerFiscalCalendarQuery,
91
+ type GetLedgerMappedTrialBalanceQuery,
92
+ type GetLedgerMappingCoverageQuery,
93
+ type GetLedgerMappingQuery,
94
+ type GetLedgerPeriodCloseStatusQuery,
95
+ type GetLedgerPeriodDraftsQuery,
96
+ type GetLedgerReportingTaxonomyQuery,
97
+ type GetLedgerScheduleFactsQuery,
98
+ type GetLedgerSummaryQuery,
99
+ type GetLedgerTransactionQuery,
100
+ type GetLedgerTrialBalanceQuery,
101
+ type ListLedgerAccountsQuery,
102
+ type ListLedgerElementsQuery,
103
+ type ListLedgerEntitiesQuery,
104
+ type ListLedgerMappingsQuery,
105
+ type ListLedgerSchedulesQuery,
106
+ type ListLedgerStructuresQuery,
107
+ type ListLedgerTaxonomiesQuery,
108
+ type ListLedgerTransactionsQuery,
109
+ type ListLedgerUnmappedElementsQuery,
110
+ } from './graphql/generated/graphql'
111
+
112
+ // ── Friendly types derived from GraphQL codegen ────────────────────────
113
+ //
114
+ // These are the single source of truth for read payload shapes. Write
115
+ // methods also return these where the operation result is semantically
116
+ // the same thing (e.g. close-period returns the updated fiscal calendar,
117
+ // the same shape as the fiscalCalendar read).
118
+
119
+ export type LedgerEntity = NonNullable<GetLedgerEntityQuery['entity']>
120
+ export type LedgerEntitySummary = ListLedgerEntitiesQuery['entities'][number]
121
+
122
+ export type LedgerSummary = NonNullable<GetLedgerSummaryQuery['summary']>
123
+
124
+ export type LedgerAccountList = NonNullable<ListLedgerAccountsQuery['accounts']>
125
+ export type LedgerAccount = LedgerAccountList['accounts'][number]
126
+ export type LedgerAccountTree = NonNullable<GetLedgerAccountTreeQuery['accountTree']>
127
+ export type LedgerAccountRollups = NonNullable<GetLedgerAccountRollupsQuery['accountRollups']>
128
+
129
+ export type LedgerTrialBalance = NonNullable<GetLedgerTrialBalanceQuery['trialBalance']>
130
+ export type LedgerMappedTrialBalance = NonNullable<
131
+ GetLedgerMappedTrialBalanceQuery['mappedTrialBalance']
132
+ >
133
+
134
+ export type LedgerTransactionList = NonNullable<ListLedgerTransactionsQuery['transactions']>
135
+ export type LedgerTransactionListItem = LedgerTransactionList['transactions'][number]
136
+ export type LedgerTransaction = NonNullable<GetLedgerTransactionQuery['transaction']>
137
+
138
+ export type LedgerReportingTaxonomy = NonNullable<
139
+ GetLedgerReportingTaxonomyQuery['reportingTaxonomy']
140
+ >
141
+ export type LedgerTaxonomyList = NonNullable<ListLedgerTaxonomiesQuery['taxonomies']>
142
+ export type LedgerTaxonomy = LedgerTaxonomyList['taxonomies'][number]
143
+
144
+ export type LedgerElementList = NonNullable<ListLedgerElementsQuery['elements']>
145
+ export type LedgerElement = LedgerElementList['elements'][number]
146
+ export type LedgerUnmappedElement = ListLedgerUnmappedElementsQuery['unmappedElements'][number]
147
+
148
+ export type LedgerStructureList = NonNullable<ListLedgerStructuresQuery['structures']>
149
+ export type LedgerStructure = LedgerStructureList['structures'][number]
150
+
151
+ export type LedgerMappingList = NonNullable<ListLedgerMappingsQuery['mappings']>
152
+ export type LedgerMappingInfo = LedgerMappingList['structures'][number]
153
+ export type LedgerMapping = NonNullable<GetLedgerMappingQuery['mapping']>
154
+ export type LedgerMappingCoverage = NonNullable<GetLedgerMappingCoverageQuery['mappingCoverage']>
155
+
156
+ export type LedgerScheduleList = NonNullable<ListLedgerSchedulesQuery['schedules']>
157
+ export type LedgerSchedule = LedgerScheduleList['schedules'][number]
158
+ export type LedgerScheduleFacts = NonNullable<GetLedgerScheduleFactsQuery['scheduleFacts']>
159
+ export type LedgerScheduleFact = LedgerScheduleFacts['facts'][number]
160
+
161
+ export type LedgerPeriodCloseStatus = NonNullable<
162
+ GetLedgerPeriodCloseStatusQuery['periodCloseStatus']
163
+ >
164
+ export type LedgerPeriodCloseItem = LedgerPeriodCloseStatus['schedules'][number]
165
+ export type LedgerPeriodDrafts = NonNullable<GetLedgerPeriodDraftsQuery['periodDrafts']>
166
+ export type LedgerDraftEntry = LedgerPeriodDrafts['drafts'][number]
167
+ export type LedgerDraftLineItem = LedgerDraftEntry['lineItems'][number]
168
+
169
+ export type LedgerClosingBookStructures = NonNullable<
170
+ GetLedgerClosingBookStructuresQuery['closingBookStructures']
171
+ >
172
+
173
+ export type LedgerFiscalCalendar = NonNullable<GetLedgerFiscalCalendarQuery['fiscalCalendar']>
174
+ export type LedgerFiscalPeriod = LedgerFiscalCalendar['periods'][number]
175
+
176
+ // ── Write result shapes (envelope.result payloads) ─────────────────────
177
+ //
178
+ // Backend Pydantic models serialize these write results in snake_case.
179
+ // The facade converts to camelCase where the result is meaningful to
180
+ // consumers; for simple ack/ack-with-id payloads we keep the raw shape.
181
+
182
+ /** Snake-case shape returned in envelope.result for fiscal calendar writes. */
183
+ interface RawFiscalCalendar {
184
+ graph_id: string
185
+ fiscal_year_start_month: number
186
+ closed_through: string | null
187
+ close_target: string | null
188
+ gap_periods: number
189
+ catch_up_sequence: string[]
190
+ closeable_now: boolean
191
+ blockers: string[]
192
+ last_close_at: string | null
193
+ initialized_at: string | null
194
+ last_sync_at: string | null
195
+ periods: Array<{
196
+ name: string
197
+ start_date: string
198
+ end_date: string
199
+ status: string
200
+ closed_at: string | null
201
+ }>
202
+ }
60
203
 
61
- // ── Friendly types ──────────────────────────────────────────────────────
204
+ interface RawInitializeLedgerResult {
205
+ fiscal_calendar: RawFiscalCalendar
206
+ periods_created: number
207
+ warnings: string[]
208
+ }
62
209
 
63
- export interface LedgerEntity {
64
- id: string
65
- name: string
66
- legalName: string | null
67
- entityType: string | null
68
- industry: string | null
210
+ interface RawClosePeriodResult {
211
+ period: string
212
+ entries_posted: number
213
+ target_auto_advanced: boolean
214
+ fiscal_calendar: RawFiscalCalendar
215
+ }
216
+
217
+ interface RawClosingEntryResult {
218
+ outcome: ClosingEntryOutcome
219
+ entry_id: string | null
69
220
  status: string | null
221
+ posting_date: string | null
222
+ memo: string | null
223
+ debit_element_id: string | null
224
+ credit_element_id: string | null
225
+ amount: number | null
226
+ reason: string | null
70
227
  }
71
228
 
72
- export interface MappingInfo {
73
- id: string
229
+ interface RawScheduleCreatedResult {
230
+ structure_id: string
74
231
  name: string
75
- description: string | null
76
- structureType: string
77
- taxonomyId: string
78
- isActive: boolean
232
+ taxonomy_id: string
233
+ total_periods: number
234
+ total_facts: number
79
235
  }
80
236
 
81
- export interface MappingCoverage {
82
- totalCoaElements: number
83
- mappedCount: number
84
- unmappedCount: number
85
- coveragePercent: number
86
- highConfidence: number
87
- mediumConfidence: number
88
- lowConfidence: number
237
+ interface RawTruncateScheduleResult {
238
+ structure_id: string
239
+ new_end_date: string
240
+ facts_deleted: number
241
+ reason: string
89
242
  }
90
243
 
91
- export interface Structure {
92
- id: string
93
- name: string
94
- structureType: string
244
+ export interface InitializeLedgerResult {
245
+ fiscalCalendar: LedgerFiscalCalendar
246
+ periodsCreated: number
247
+ warnings: string[]
95
248
  }
96
249
 
97
- export interface Schedule {
98
- structureId: string
99
- name: string
100
- taxonomyName: string
101
- entryTemplate: Record<string, unknown> | null
102
- scheduleMetadata: Record<string, unknown> | null
103
- totalPeriods: number
104
- periodsWithEntries: number
250
+ export interface ClosePeriodResult {
251
+ period: string
252
+ entriesPosted: number
253
+ targetAutoAdvanced: boolean
254
+ fiscalCalendar: LedgerFiscalCalendar
255
+ }
256
+
257
+ export type ClosingEntryOutcome = 'created' | 'unchanged' | 'regenerated' | 'removed' | 'skipped'
258
+
259
+ export interface ClosingEntry {
260
+ outcome: ClosingEntryOutcome
261
+ entryId: string | null
262
+ status: string | null
263
+ postingDate: string | null
264
+ memo: string | null
265
+ debitElementId: string | null
266
+ creditElementId: string | null
267
+ amount: number | null
268
+ reason: string | null
105
269
  }
106
270
 
107
271
  export interface ScheduleCreated {
@@ -112,39 +276,47 @@ export interface ScheduleCreated {
112
276
  totalFacts: number
113
277
  }
114
278
 
115
- export interface ScheduleFact {
116
- elementId: string
117
- elementName: string
118
- value: number
119
- periodStart: string
120
- periodEnd: string
279
+ export interface TruncateScheduleResult {
280
+ structureId: string
281
+ newEndDate: string
282
+ factsDeleted: number
283
+ reason: string
121
284
  }
122
285
 
123
- export interface PeriodCloseItem {
124
- structureId: string
125
- structureName: string
126
- amount: number
127
- status: string
128
- entryId: string | null
286
+ export type LedgerEntryType = 'standard' | 'adjusting' | 'closing' | 'reversing'
287
+
288
+ // ── Caller-facing option interfaces ────────────────────────────────────
289
+
290
+ export interface InitializeLedgerOptions {
291
+ closedThrough?: string | null
292
+ fiscalYearStartMonth?: number
293
+ earliestDataPeriod?: string | null
294
+ autoSeedSchedules?: boolean
295
+ note?: string | null
129
296
  }
130
297
 
131
- export interface PeriodCloseStatus {
132
- fiscalPeriodStart: string
133
- fiscalPeriodEnd: string
134
- periodStatus: string
135
- schedules: PeriodCloseItem[]
136
- totalDraft: number
137
- totalPosted: number
298
+ export interface ClosePeriodOptions {
299
+ note?: string | null
300
+ allowStaleSync?: boolean
138
301
  }
139
302
 
140
- export interface ClosingEntry {
141
- entryId: string
142
- status: string
303
+ export interface ManualClosingLineItem {
304
+ elementId: string
305
+ debitAmount?: number
306
+ creditAmount?: number
307
+ description?: string | null
308
+ }
309
+
310
+ export interface CreateManualClosingEntryOptions {
143
311
  postingDate: string
144
312
  memo: string
145
- debitElementId: string
146
- creditElementId: string
147
- amount: number
313
+ lineItems: ManualClosingLineItem[]
314
+ entryType?: LedgerEntryType
315
+ }
316
+
317
+ export interface TruncateScheduleOptions {
318
+ newEndDate: string
319
+ reason: string
148
320
  }
149
321
 
150
322
  export interface CreateScheduleOptions {
@@ -156,7 +328,7 @@ export interface CreateScheduleOptions {
156
328
  entryTemplate: {
157
329
  debitElementId: string
158
330
  creditElementId: string
159
- entryType?: string
331
+ entryType?: LedgerEntryType
160
332
  memoTemplate?: string
161
333
  }
162
334
  taxonomyId?: string
@@ -171,230 +343,259 @@ export interface CreateScheduleOptions {
171
343
 
172
344
  // ── Client ──────────────────────────────────────────────────────────────
173
345
 
346
+ interface LedgerClientConfig {
347
+ baseUrl: string
348
+ credentials?: 'include' | 'same-origin' | 'omit'
349
+ headers?: Record<string, string>
350
+ token?: string
351
+ }
352
+
174
353
  export class LedgerClient {
175
- private config: {
176
- baseUrl: string
177
- credentials?: 'include' | 'same-origin' | 'omit'
178
- headers?: Record<string, string>
179
- token?: string
180
- }
181
-
182
- constructor(config: {
183
- baseUrl: string
184
- credentials?: 'include' | 'same-origin' | 'omit'
185
- headers?: Record<string, string>
186
- token?: string
187
- }) {
354
+ private config: LedgerClientConfig
355
+
356
+ /**
357
+ * Per-graph GraphQL client cache. The first call for a given graph
358
+ * creates a `GraphQLClient` bound to `/extensions/{graph_id}/graphql`;
359
+ * subsequent calls reuse it.
360
+ */
361
+ private gql: GraphQLClientCache
362
+
363
+ constructor(config: LedgerClientConfig) {
188
364
  this.config = config
365
+ this.gql = new GraphQLClientCache(config)
189
366
  }
190
367
 
191
368
  // ── Entity ──────────────────────────────────────────────────────────
192
369
 
193
370
  /**
194
371
  * Get the entity (company/organization) for this graph.
372
+ * Returns null when the ledger has no entity yet.
195
373
  */
196
374
  async getEntity(graphId: string): Promise<LedgerEntity | null> {
197
- const response = await getLedgerEntity({
198
- path: { graph_id: graphId },
199
- })
200
-
201
- if (response.response.status === 404) return null
202
- if (response.error) {
203
- throw new Error(`Get entity failed: ${JSON.stringify(response.error)}`)
204
- }
205
-
206
- const data = response.data as Record<string, unknown>
207
- return {
208
- id: data.id as string,
209
- name: data.name as string,
210
- legalName: (data.legal_name as string) ?? null,
211
- entityType: (data.entity_type as string) ?? null,
212
- industry: (data.industry as string) ?? null,
213
- status: (data.status as string) ?? null,
214
- }
375
+ return this.gqlQuery(
376
+ graphId,
377
+ GetLedgerEntityDocument,
378
+ undefined,
379
+ 'Get entity',
380
+ (data) => data.entity
381
+ )
215
382
  }
216
383
 
217
- // ── Accounts (Chart of Accounts) ───────────────────────────────────
384
+ /** List all entities for this graph, optionally filtered by source system. */
385
+ async listEntities(
386
+ graphId: string,
387
+ options?: { source?: string }
388
+ ): Promise<LedgerEntitySummary[]> {
389
+ return this.gqlQuery(
390
+ graphId,
391
+ ListLedgerEntitiesDocument,
392
+ { source: options?.source ?? null },
393
+ 'List entities',
394
+ (data) => data.entities
395
+ )
396
+ }
218
397
 
219
398
  /**
220
- * List accounts (flat).
399
+ * Update the entity for this graph. Only non-null fields are applied.
400
+ * Returns the updated entity.
221
401
  */
222
- async listAccounts(graphId: string): Promise<AccountListResponse> {
223
- const response = await listLedgerAccounts({
224
- path: { graph_id: graphId },
225
- })
402
+ async updateEntity(graphId: string, updates: UpdateEntityRequest): Promise<LedgerEntity> {
403
+ const envelope = await this.callOperation(
404
+ 'Update entity',
405
+ opUpdateEntity({
406
+ path: { graph_id: graphId },
407
+ body: updates,
408
+ })
409
+ )
410
+ return envelope.result as unknown as LedgerEntity
411
+ }
226
412
 
227
- if (response.error) {
228
- throw new Error(`List accounts failed: ${JSON.stringify(response.error)}`)
229
- }
413
+ // ── Summary ────────────────────────────────────────────────────────
230
414
 
231
- return response.data as AccountListResponse
415
+ /** Ledger rollup counts + QB sync metadata. */
416
+ async getSummary(graphId: string): Promise<LedgerSummary | null> {
417
+ return this.gqlQuery(
418
+ graphId,
419
+ GetLedgerSummaryDocument,
420
+ undefined,
421
+ 'Get summary',
422
+ (data) => data.summary
423
+ )
232
424
  }
233
425
 
234
- /**
235
- * Get the account tree (hierarchical).
236
- */
237
- async getAccountTree(graphId: string): Promise<AccountTreeResponse> {
238
- const response = await getLedgerAccountTree({
239
- path: { graph_id: graphId },
240
- })
426
+ // ── Accounts (Chart of Accounts) ───────────────────────────────────
241
427
 
242
- if (response.error) {
243
- throw new Error(`Get account tree failed: ${JSON.stringify(response.error)}`)
428
+ /** List CoA accounts with optional filters and pagination. */
429
+ async listAccounts(
430
+ graphId: string,
431
+ options?: {
432
+ classification?: string
433
+ isActive?: boolean
434
+ limit?: number
435
+ offset?: number
244
436
  }
245
-
246
- return response.data as AccountTreeResponse
437
+ ): Promise<LedgerAccountList | null> {
438
+ return this.gqlQuery(
439
+ graphId,
440
+ ListLedgerAccountsDocument,
441
+ {
442
+ classification: options?.classification ?? null,
443
+ isActive: options?.isActive ?? null,
444
+ limit: options?.limit ?? 100,
445
+ offset: options?.offset ?? 0,
446
+ },
447
+ 'List accounts',
448
+ (data) => data.accounts
449
+ )
247
450
  }
248
451
 
249
- // ── Transactions ────────────────────────────────────────────────────
452
+ /** Hierarchical Chart of Accounts (up to 4 levels deep). */
453
+ async getAccountTree(graphId: string): Promise<LedgerAccountTree | null> {
454
+ return this.gqlQuery(
455
+ graphId,
456
+ GetLedgerAccountTreeDocument,
457
+ undefined,
458
+ 'Get account tree',
459
+ (data) => data.accountTree
460
+ )
461
+ }
250
462
 
251
- /**
252
- * List transactions with optional date filters.
253
- */
254
- async listTransactions(
463
+ /** Accounts rolled up to reporting concepts via a mapping structure. */
464
+ async getAccountRollups(
255
465
  graphId: string,
256
- options?: { startDate?: string; endDate?: string; limit?: number; offset?: number }
257
- ): Promise<LedgerTransactionListResponse> {
258
- const response = await listLedgerTransactions({
259
- path: { graph_id: graphId },
260
- query: {
261
- start_date: options?.startDate,
262
- end_date: options?.endDate,
263
- limit: options?.limit,
264
- offset: options?.offset,
466
+ options?: { mappingId?: string; startDate?: string; endDate?: string }
467
+ ): Promise<LedgerAccountRollups | null> {
468
+ return this.gqlQuery(
469
+ graphId,
470
+ GetLedgerAccountRollupsDocument,
471
+ {
472
+ mappingId: options?.mappingId ?? null,
473
+ startDate: options?.startDate ?? null,
474
+ endDate: options?.endDate ?? null,
265
475
  },
266
- })
267
-
268
- if (response.error) {
269
- throw new Error(`List transactions failed: ${JSON.stringify(response.error)}`)
270
- }
271
-
272
- return response.data as LedgerTransactionListResponse
476
+ 'Get account rollups',
477
+ (data) => data.accountRollups
478
+ )
273
479
  }
274
480
 
275
- /**
276
- * Get transaction detail with entries and line items.
277
- */
278
- async getTransaction(
481
+ // ── Transactions ────────────────────────────────────────────────────
482
+
483
+ /** List transactions with optional type + date filters and pagination. */
484
+ async listTransactions(
279
485
  graphId: string,
280
- transactionId: string
281
- ): Promise<LedgerTransactionDetailResponse> {
282
- const response = await getLedgerTransaction({
283
- path: { graph_id: graphId, transaction_id: transactionId },
284
- })
285
-
286
- if (response.error) {
287
- throw new Error(`Get transaction failed: ${JSON.stringify(response.error)}`)
486
+ options?: {
487
+ type?: string
488
+ startDate?: string
489
+ endDate?: string
490
+ limit?: number
491
+ offset?: number
288
492
  }
493
+ ): Promise<LedgerTransactionList | null> {
494
+ return this.gqlQuery(
495
+ graphId,
496
+ ListLedgerTransactionsDocument,
497
+ {
498
+ type: options?.type ?? null,
499
+ startDate: options?.startDate ?? null,
500
+ endDate: options?.endDate ?? null,
501
+ limit: options?.limit ?? 100,
502
+ offset: options?.offset ?? 0,
503
+ },
504
+ 'List transactions',
505
+ (data) => data.transactions
506
+ )
507
+ }
289
508
 
290
- return response.data as LedgerTransactionDetailResponse
509
+ /** Get transaction detail with entries + line items. */
510
+ async getTransaction(graphId: string, transactionId: string): Promise<LedgerTransaction | null> {
511
+ return this.gqlQuery(
512
+ graphId,
513
+ GetLedgerTransactionDocument,
514
+ { transactionId },
515
+ 'Get transaction',
516
+ (data) => data.transaction
517
+ )
291
518
  }
292
519
 
293
520
  // ── Trial Balance ──────────────────────────────────────────────────
294
521
 
295
- /**
296
- * Get trial balance (CoA-level debits/credits).
297
- */
522
+ /** Trial balance by raw CoA account. */
298
523
  async getTrialBalance(
299
524
  graphId: string,
300
525
  options?: { startDate?: string; endDate?: string }
301
- ): Promise<TrialBalanceResponse> {
302
- const response = await getLedgerTrialBalance({
303
- path: { graph_id: graphId },
304
- query: {
305
- start_date: options?.startDate,
306
- end_date: options?.endDate,
526
+ ): Promise<LedgerTrialBalance | null> {
527
+ return this.gqlQuery(
528
+ graphId,
529
+ GetLedgerTrialBalanceDocument,
530
+ {
531
+ startDate: options?.startDate ?? null,
532
+ endDate: options?.endDate ?? null,
307
533
  },
308
- })
309
-
310
- if (response.error) {
311
- throw new Error(`Get trial balance failed: ${JSON.stringify(response.error)}`)
312
- }
313
-
314
- return response.data as TrialBalanceResponse
534
+ 'Get trial balance',
535
+ (data) => data.trialBalance
536
+ )
315
537
  }
316
538
 
317
- /**
318
- * Get mapped trial balance (CoA rolled up to GAAP concepts).
319
- */
539
+ /** Trial balance rolled up to GAAP reporting concepts via a mapping. */
320
540
  async getMappedTrialBalance(
321
541
  graphId: string,
322
- options?: { mappingId?: string; startDate?: string; endDate?: string }
323
- ): Promise<unknown> {
324
- const response = await getMappedTrialBalance({
325
- path: { graph_id: graphId },
326
- query: {
327
- mapping_id: options?.mappingId,
328
- start_date: options?.startDate,
329
- end_date: options?.endDate,
542
+ mappingId: string,
543
+ options?: { startDate?: string; endDate?: string }
544
+ ): Promise<LedgerMappedTrialBalance | null> {
545
+ return this.gqlQuery(
546
+ graphId,
547
+ GetLedgerMappedTrialBalanceDocument,
548
+ {
549
+ mappingId,
550
+ startDate: options?.startDate ?? null,
551
+ endDate: options?.endDate ?? null,
330
552
  },
331
- })
332
-
333
- if (response.error) {
334
- throw new Error(`Get mapped trial balance failed: ${JSON.stringify(response.error)}`)
335
- }
336
-
337
- return response.data
338
- }
339
-
340
- // ── Summary ────────────────────────────────────────────────────────
341
-
342
- /**
343
- * Get ledger summary (account count, transaction count, date range).
344
- */
345
- async getSummary(graphId: string): Promise<LedgerSummaryResponse> {
346
- const response = await getLedgerSummary({
347
- path: { graph_id: graphId },
348
- })
349
-
350
- if (response.error) {
351
- throw new Error(`Get summary failed: ${JSON.stringify(response.error)}`)
352
- }
353
-
354
- return response.data as LedgerSummaryResponse
553
+ 'Get mapped trial balance',
554
+ (data) => data.mappedTrialBalance
555
+ )
355
556
  }
356
557
 
357
558
  // ── Taxonomy ────────────────────────────────────────────────────────
358
559
 
359
- /**
360
- * Get the reporting taxonomy (US GAAP seed).
361
- */
362
- async getReportingTaxonomy(graphId: string): Promise<unknown> {
363
- const response = await getReportingTaxonomy({
364
- path: { graph_id: graphId },
365
- })
366
-
367
- if (response.error) {
368
- throw new Error(`Get reporting taxonomy failed: ${JSON.stringify(response.error)}`)
369
- }
370
-
371
- return response.data
560
+ /** Get the locked US GAAP reporting taxonomy for this graph. */
561
+ async getReportingTaxonomy(graphId: string): Promise<LedgerReportingTaxonomy | null> {
562
+ return this.gqlQuery(
563
+ graphId,
564
+ GetLedgerReportingTaxonomyDocument,
565
+ undefined,
566
+ 'Get reporting taxonomy',
567
+ (data) => data.reportingTaxonomy
568
+ )
372
569
  }
373
570
 
374
- /**
375
- * List reporting structures (IS, BS, CF) for a taxonomy.
376
- */
377
- async listStructures(graphId: string, taxonomyId?: string): Promise<Structure[]> {
378
- const response = await listStructures({
379
- path: { graph_id: graphId },
380
- query: taxonomyId ? { taxonomy_id: taxonomyId } : undefined,
381
- })
382
-
383
- if (response.error) {
384
- throw new Error(`List structures failed: ${JSON.stringify(response.error)}`)
385
- }
571
+ /** List active taxonomies with optional type filter. */
572
+ async listTaxonomies(
573
+ graphId: string,
574
+ options?: { taxonomyType?: string }
575
+ ): Promise<LedgerTaxonomy[]> {
576
+ const list = await this.gqlQuery(
577
+ graphId,
578
+ ListLedgerTaxonomiesDocument,
579
+ { taxonomyType: options?.taxonomyType ?? null },
580
+ 'List taxonomies',
581
+ (data) => data.taxonomies
582
+ )
583
+ return list?.taxonomies ?? []
584
+ }
386
585
 
387
- const data = response.data as { structures?: Array<Record<string, unknown>> }
388
- return (data.structures ?? []).map((s) => ({
389
- id: s.id as string,
390
- name: s.name as string,
391
- structureType: s.structure_type as string,
392
- }))
586
+ /** Create a new taxonomy (used for CoA + mapping taxonomies). */
587
+ async createTaxonomy(
588
+ graphId: string,
589
+ body: CreateTaxonomyRequest
590
+ ): Promise<Record<string, unknown>> {
591
+ const envelope = await this.callOperation(
592
+ 'Create taxonomy',
593
+ opCreateTaxonomy({ path: { graph_id: graphId }, body })
594
+ )
595
+ return (envelope.result ?? {}) as Record<string, unknown>
393
596
  }
394
597
 
395
- /**
396
- * List elements (CoA accounts, GAAP concepts, etc.).
397
- */
598
+ /** List elements (CoA accounts, GAAP concepts, etc.) with filters. */
398
599
  async listElements(
399
600
  graphId: string,
400
601
  options?: {
@@ -405,184 +606,183 @@ export class LedgerClient {
405
606
  limit?: number
406
607
  offset?: number
407
608
  }
408
- ): Promise<unknown> {
409
- const response = await listElements({
410
- path: { graph_id: graphId },
411
- query: {
412
- taxonomy_id: options?.taxonomyId,
413
- source: options?.source,
414
- classification: options?.classification,
415
- is_abstract: options?.isAbstract,
416
- limit: options?.limit,
417
- offset: options?.offset,
609
+ ): Promise<LedgerElementList | null> {
610
+ return this.gqlQuery(
611
+ graphId,
612
+ ListLedgerElementsDocument,
613
+ {
614
+ taxonomyId: options?.taxonomyId ?? null,
615
+ source: options?.source ?? null,
616
+ classification: options?.classification ?? null,
617
+ isAbstract: options?.isAbstract ?? null,
618
+ limit: options?.limit ?? 100,
619
+ offset: options?.offset ?? 0,
418
620
  },
419
- })
420
-
421
- if (response.error) {
422
- throw new Error(`List elements failed: ${JSON.stringify(response.error)}`)
423
- }
621
+ 'List elements',
622
+ (data) => data.elements
623
+ )
624
+ }
424
625
 
425
- return response.data
626
+ /** CoA elements not yet mapped to a reporting concept. */
627
+ async listUnmappedElements(
628
+ graphId: string,
629
+ options?: { mappingId?: string }
630
+ ): Promise<LedgerUnmappedElement[]> {
631
+ return this.gqlQuery(
632
+ graphId,
633
+ ListLedgerUnmappedElementsDocument,
634
+ { mappingId: options?.mappingId ?? null },
635
+ 'List unmapped elements',
636
+ (data) => data.unmappedElements
637
+ )
426
638
  }
427
639
 
428
- // ── Mappings ────────────────────────────────────────────────────────
640
+ // ── Structures ──────────────────────────────────────────────────────
429
641
 
430
- /**
431
- * Create a new CoA→GAAP mapping structure.
432
- * Returns the created mapping info.
433
- */
434
- async createMappingStructure(
642
+ /** List reporting structures (IS, BS, CF, schedules) with optional filters. */
643
+ async listStructures(
435
644
  graphId: string,
436
- options?: { name?: string; description?: string; taxonomyId?: string }
437
- ): Promise<MappingInfo> {
438
- const response = await createStructure({
439
- path: { graph_id: graphId },
440
- body: {
441
- name: options?.name ?? 'CoA to Reporting',
442
- description: options?.description ?? 'Map Chart of Accounts to US GAAP reporting concepts',
443
- structure_type: 'coa_mapping',
444
- taxonomy_id: options?.taxonomyId ?? 'tax_usgaap_reporting',
645
+ options?: { taxonomyId?: string; structureType?: string }
646
+ ): Promise<LedgerStructure[]> {
647
+ const list = await this.gqlQuery(
648
+ graphId,
649
+ ListLedgerStructuresDocument,
650
+ {
651
+ taxonomyId: options?.taxonomyId ?? null,
652
+ structureType: options?.structureType ?? null,
445
653
  },
446
- })
447
-
448
- if (response.error) {
449
- throw new Error(`Create mapping structure failed: ${JSON.stringify(response.error)}`)
450
- }
451
-
452
- const data = response.data as Record<string, unknown>
453
- return {
454
- id: data.id as string,
455
- name: data.name as string,
456
- description: (data.description as string) ?? null,
457
- structureType: data.structure_type as string,
458
- taxonomyId: data.taxonomy_id as string,
459
- isActive: (data.is_active as boolean) ?? true,
460
- }
654
+ 'List structures',
655
+ (data) => data.structures
656
+ )
657
+ return list?.structures ?? []
461
658
  }
462
659
 
463
660
  /**
464
- * List available CoA→GAAP mapping structures.
661
+ * Create a new structure. Most common use is a CoA→reporting mapping
662
+ * structure; also used for custom statement + schedule structures.
465
663
  */
466
- async listMappings(graphId: string): Promise<MappingInfo[]> {
467
- const response = await listMappings({
468
- path: { graph_id: graphId },
469
- })
470
-
471
- if (response.error) {
472
- throw new Error(`List mappings failed: ${JSON.stringify(response.error)}`)
473
- }
474
-
475
- const data = response.data as { structures?: Array<Record<string, unknown>> }
476
- return (data.structures ?? []).map((s) => ({
477
- id: s.id as string,
478
- name: s.name as string,
479
- description: (s.description as string) ?? null,
480
- structureType: s.structure_type as string,
481
- taxonomyId: s.taxonomy_id as string,
482
- isActive: (s.is_active as boolean) ?? true,
483
- }))
664
+ async createStructure(graphId: string, body: CreateStructureRequest): Promise<LedgerMappingInfo> {
665
+ const envelope = await this.callOperation(
666
+ 'Create structure',
667
+ opCreateStructure({ path: { graph_id: graphId }, body })
668
+ )
669
+ return envelope.result as unknown as LedgerMappingInfo
484
670
  }
485
671
 
486
- /**
487
- * Get mapping detail — all associations with element names.
488
- */
489
- async getMappingDetail(graphId: string, mappingId: string): Promise<MappingDetailResponse> {
490
- const response = await getMappingDetail({
491
- path: { graph_id: graphId, mapping_id: mappingId },
492
- })
493
-
494
- if (response.error) {
495
- throw new Error(`Get mapping detail failed: ${JSON.stringify(response.error)}`)
496
- }
672
+ // ── Mappings ────────────────────────────────────────────────────────
497
673
 
498
- return response.data as MappingDetailResponse
674
+ /** List active CoA→reporting mapping structures. */
675
+ async listMappings(graphId: string): Promise<LedgerMappingInfo[]> {
676
+ const list = await this.gqlQuery(
677
+ graphId,
678
+ ListLedgerMappingsDocument,
679
+ undefined,
680
+ 'List mappings',
681
+ (data) => data.mappings
682
+ )
683
+ return list?.structures ?? []
499
684
  }
500
685
 
501
- /**
502
- * Get mapping coverage how many CoA elements are mapped.
503
- */
504
- async getMappingCoverage(graphId: string, mappingId: string): Promise<MappingCoverage> {
505
- const response = await getMappingCoverage({
506
- path: { graph_id: graphId, mapping_id: mappingId },
507
- })
508
-
509
- if (response.error) {
510
- throw new Error(`Get mapping coverage failed: ${JSON.stringify(response.error)}`)
511
- }
512
-
513
- const data = response.data as MappingCoverageResponse
514
- return {
515
- totalCoaElements: data.total_coa_elements ?? 0,
516
- mappedCount: data.mapped_count ?? 0,
517
- unmappedCount: data.unmapped_count ?? 0,
518
- coveragePercent: data.coverage_percent ?? 0,
519
- highConfidence: data.high_confidence ?? 0,
520
- mediumConfidence: data.medium_confidence ?? 0,
521
- lowConfidence: data.low_confidence ?? 0,
522
- }
686
+ /** Get a mapping structure with all its associations. */
687
+ async getMapping(graphId: string, mappingId: string): Promise<LedgerMapping | null> {
688
+ return this.gqlQuery(
689
+ graphId,
690
+ GetLedgerMappingDocument,
691
+ { mappingId },
692
+ 'Get mapping',
693
+ (data) => data.mapping
694
+ )
523
695
  }
524
696
 
525
- /**
526
- * Create a manual mapping association (CoA element → GAAP element).
527
- */
528
- async createMapping(
697
+ /** Mapping coverage stats — how many CoA elements are mapped. */
698
+ async getMappingCoverage(
529
699
  graphId: string,
530
- mappingId: string,
531
- fromElementId: string,
532
- toElementId: string,
533
- confidence?: number
534
- ): Promise<void> {
535
- const response = await createMappingAssociation({
536
- path: { graph_id: graphId, mapping_id: mappingId },
537
- body: {
538
- from_element_id: fromElementId,
539
- to_element_id: toElementId,
540
- confidence: confidence ?? 1.0,
541
- },
542
- })
543
-
544
- if (response.error) {
545
- throw new Error(`Create mapping failed: ${JSON.stringify(response.error)}`)
546
- }
700
+ mappingId: string
701
+ ): Promise<LedgerMappingCoverage | null> {
702
+ return this.gqlQuery(
703
+ graphId,
704
+ GetLedgerMappingCoverageDocument,
705
+ { mappingId },
706
+ 'Get mapping coverage',
707
+ (data) => data.mappingCoverage
708
+ )
547
709
  }
548
710
 
549
- /**
550
- * Delete a mapping association.
551
- */
552
- async deleteMapping(graphId: string, mappingId: string, associationId: string): Promise<void> {
553
- const response = await deleteMappingAssociation({
554
- path: { graph_id: graphId, mapping_id: mappingId, association_id: associationId },
555
- })
711
+ /** Create a manual mapping association between two elements. */
712
+ async createMappingAssociation(
713
+ graphId: string,
714
+ body: CreateMappingAssociationOperation
715
+ ): Promise<Record<string, unknown>> {
716
+ const envelope = await this.callOperation(
717
+ 'Create mapping association',
718
+ opCreateMappingAssociation({ path: { graph_id: graphId }, body })
719
+ )
720
+ return (envelope.result ?? {}) as Record<string, unknown>
721
+ }
556
722
 
557
- if (response.error) {
558
- throw new Error(`Delete mapping failed: ${JSON.stringify(response.error)}`)
559
- }
723
+ /** Delete a mapping association. */
724
+ async deleteMappingAssociation(
725
+ graphId: string,
726
+ body: DeleteMappingAssociationOperation
727
+ ): Promise<{ deleted: boolean }> {
728
+ const envelope = await this.callOperation(
729
+ 'Delete mapping association',
730
+ opDeleteMappingAssociation({ path: { graph_id: graphId }, body })
731
+ )
732
+ return (envelope.result ?? { deleted: true }) as { deleted: boolean }
560
733
  }
561
734
 
562
735
  /**
563
- * Trigger AI auto-mapping (MappingAgent).
564
- * Returns immediately — the agent runs in the background.
736
+ * Trigger the AI MappingAgent. Returns the operation id — async; the
737
+ * agent runs in the background. Consumers can subscribe to progress
738
+ * via `/v1/operations/{operationId}/stream`.
565
739
  */
566
- async autoMap(graphId: string, mappingId: string): Promise<{ operationId?: string }> {
567
- const response = await autoMapElements({
568
- path: { graph_id: graphId, mapping_id: mappingId },
569
- })
740
+ async autoMapElements(
741
+ graphId: string,
742
+ body: AutoMapElementsOperation
743
+ ): Promise<{ operationId: string; status: OperationEnvelope['status'] }> {
744
+ const envelope = await this.callOperation(
745
+ 'Auto-map elements',
746
+ opAutoMapElements({ path: { graph_id: graphId }, body })
747
+ )
748
+ return { operationId: envelope.operationId, status: envelope.status }
749
+ }
570
750
 
571
- if (response.error) {
572
- throw new Error(`Auto-map failed: ${JSON.stringify(response.error)}`)
573
- }
751
+ // ── Schedules ──────────────────────────────────────────────────────
574
752
 
575
- const data = response.data as Record<string, unknown> | undefined
576
- return {
577
- operationId: data?.operation_id as string | undefined,
578
- }
753
+ /** List all schedule structures with metadata. */
754
+ async listSchedules(graphId: string): Promise<LedgerSchedule[]> {
755
+ const list = await this.gqlQuery(
756
+ graphId,
757
+ ListLedgerSchedulesDocument,
758
+ undefined,
759
+ 'List schedules',
760
+ (data) => data.schedules
761
+ )
762
+ return list?.schedules ?? []
579
763
  }
580
764
 
581
- // ── Schedules ──────────────────────────────────────────────────────
765
+ /** Schedule facts optionally filtered by period window. */
766
+ async getScheduleFacts(
767
+ graphId: string,
768
+ structureId: string,
769
+ options?: { periodStart?: string; periodEnd?: string }
770
+ ): Promise<LedgerScheduleFact[]> {
771
+ const facts = await this.gqlQuery(
772
+ graphId,
773
+ GetLedgerScheduleFactsDocument,
774
+ {
775
+ structureId,
776
+ periodStart: options?.periodStart ?? null,
777
+ periodEnd: options?.periodEnd ?? null,
778
+ },
779
+ 'Get schedule facts',
780
+ (data) => data.scheduleFacts
781
+ )
782
+ return facts?.facts ?? []
783
+ }
582
784
 
583
- /**
584
- * Create a schedule with pre-generated monthly facts.
585
- */
785
+ /** Create a new schedule with pre-generated monthly facts. */
586
786
  async createSchedule(graphId: string, options: CreateScheduleOptions): Promise<ScheduleCreated> {
587
787
  const body: CreateScheduleRequest = {
588
788
  name: options.name,
@@ -607,117 +807,75 @@ export class LedgerClient {
607
807
  }
608
808
  : undefined,
609
809
  }
610
-
611
- const response = await createSchedule({
612
- path: { graph_id: graphId },
613
- body,
614
- })
615
-
616
- if (response.error) {
617
- throw new Error(`Create schedule failed: ${JSON.stringify(response.error)}`)
618
- }
619
-
620
- const data = response.data as ScheduleCreatedResponse
810
+ const envelope = await this.callOperation(
811
+ 'Create schedule',
812
+ opCreateSchedule({ path: { graph_id: graphId }, body })
813
+ )
814
+ const raw = envelope.result as unknown as RawScheduleCreatedResult
621
815
  return {
622
- structureId: data.structure_id,
623
- name: data.name,
624
- taxonomyId: data.taxonomy_id,
625
- totalPeriods: data.total_periods,
626
- totalFacts: data.total_facts,
816
+ structureId: raw.structure_id,
817
+ name: raw.name,
818
+ taxonomyId: raw.taxonomy_id,
819
+ totalPeriods: raw.total_periods,
820
+ totalFacts: raw.total_facts,
627
821
  }
628
822
  }
629
823
 
630
- /**
631
- * List all schedules for a graph.
632
- */
633
- async listSchedules(graphId: string): Promise<Schedule[]> {
634
- const response = await listSchedules({
635
- path: { graph_id: graphId },
636
- })
637
-
638
- if (response.error) {
639
- throw new Error(`List schedules failed: ${JSON.stringify(response.error)}`)
640
- }
641
-
642
- const data = response.data as ScheduleListResponse
643
- return (data.schedules ?? []).map((s: ScheduleSummaryResponse) => ({
644
- structureId: s.structure_id,
645
- name: s.name,
646
- taxonomyName: s.taxonomy_name,
647
- entryTemplate: s.entry_template ?? null,
648
- scheduleMetadata: s.schedule_metadata ?? null,
649
- totalPeriods: s.total_periods,
650
- periodsWithEntries: s.periods_with_entries,
651
- }))
652
- }
653
-
654
- /**
655
- * Get facts for a schedule, optionally filtered by period.
656
- */
657
- async getScheduleFacts(
824
+ /** Truncate a schedule — end it early at `newEndDate`. */
825
+ async truncateSchedule(
658
826
  graphId: string,
659
827
  structureId: string,
660
- periodStart?: string,
661
- periodEnd?: string
662
- ): Promise<ScheduleFact[]> {
663
- const response = await getScheduleFacts({
664
- path: { graph_id: graphId, structure_id: structureId },
665
- query: {
666
- period_start: periodStart ?? null,
667
- period_end: periodEnd ?? null,
668
- },
669
- })
670
-
671
- if (response.error) {
672
- throw new Error(`Get schedule facts failed: ${JSON.stringify(response.error)}`)
828
+ options: TruncateScheduleOptions
829
+ ): Promise<TruncateScheduleResult> {
830
+ const body: TruncateScheduleOperation = {
831
+ structure_id: structureId,
832
+ new_end_date: options.newEndDate,
833
+ reason: options.reason,
834
+ }
835
+ const envelope = await this.callOperation(
836
+ 'Truncate schedule',
837
+ opTruncateSchedule({ path: { graph_id: graphId }, body })
838
+ )
839
+ const raw = envelope.result as unknown as RawTruncateScheduleResult
840
+ return {
841
+ structureId: raw.structure_id,
842
+ newEndDate: raw.new_end_date,
843
+ factsDeleted: raw.facts_deleted,
844
+ reason: raw.reason,
673
845
  }
674
-
675
- const data = response.data as ScheduleFactsResponse
676
- return (data.facts ?? []).map((f: ScheduleFactResponse) => ({
677
- elementId: f.element_id,
678
- elementName: f.element_name,
679
- value: f.value,
680
- periodStart: f.period_start,
681
- periodEnd: f.period_end,
682
- }))
683
846
  }
684
847
 
685
- /**
686
- * Get close status for all schedules in a fiscal period.
687
- */
848
+ // ── Period close ────────────────────────────────────────────────────
849
+
850
+ /** Close status for all schedules in a fiscal period. */
688
851
  async getPeriodCloseStatus(
689
852
  graphId: string,
690
853
  periodStart: string,
691
854
  periodEnd: string
692
- ): Promise<PeriodCloseStatus> {
693
- const response = await getPeriodCloseStatus({
694
- path: { graph_id: graphId },
695
- query: { period_start: periodStart, period_end: periodEnd },
696
- })
697
-
698
- if (response.error) {
699
- throw new Error(`Get period close status failed: ${JSON.stringify(response.error)}`)
700
- }
855
+ ): Promise<LedgerPeriodCloseStatus | null> {
856
+ return this.gqlQuery(
857
+ graphId,
858
+ GetLedgerPeriodCloseStatusDocument,
859
+ { periodStart, periodEnd },
860
+ 'Get period close status',
861
+ (data) => data.periodCloseStatus
862
+ )
863
+ }
701
864
 
702
- const data = response.data as PeriodCloseStatusResponse
703
- return {
704
- fiscalPeriodStart: data.fiscal_period_start,
705
- fiscalPeriodEnd: data.fiscal_period_end,
706
- periodStatus: data.period_status,
707
- schedules: (data.schedules ?? []).map((s: PeriodCloseItemResponse) => ({
708
- structureId: s.structure_id,
709
- structureName: s.structure_name,
710
- amount: s.amount,
711
- status: s.status,
712
- entryId: s.entry_id ?? null,
713
- })),
714
- totalDraft: data.total_draft,
715
- totalPosted: data.total_posted,
716
- }
865
+ /** All draft entries in a period, fully expanded for review pre-close. */
866
+ async listPeriodDrafts(graphId: string, period: string): Promise<LedgerPeriodDrafts | null> {
867
+ return this.gqlQuery(
868
+ graphId,
869
+ GetLedgerPeriodDraftsDocument,
870
+ { period },
871
+ 'List period drafts',
872
+ (data) => data.periodDrafts
873
+ )
717
874
  }
718
875
 
719
876
  /**
720
- * Create a draft closing entry from a schedule's facts for a period.
877
+ * Idempotently create (or refresh) a draft closing entry from a
878
+ * schedule for a period. See `ClosingEntryOutcome` for semantics.
721
879
  */
722
880
  async createClosingEntry(
723
881
  graphId: string,
@@ -727,74 +885,239 @@ export class LedgerClient {
727
885
  periodEnd: string,
728
886
  memo?: string
729
887
  ): Promise<ClosingEntry> {
730
- const body: CreateClosingEntryRequest = {
888
+ const body: CreateClosingEntryOperation = {
889
+ structure_id: structureId,
731
890
  posting_date: postingDate,
732
891
  period_start: periodStart,
733
892
  period_end: periodEnd,
734
893
  memo: memo ?? undefined,
735
894
  }
895
+ const envelope = await this.callOperation(
896
+ 'Create closing entry',
897
+ opCreateClosingEntry({ path: { graph_id: graphId }, body })
898
+ )
899
+ return rawToClosingEntry(envelope.result as unknown as RawClosingEntryResult)
900
+ }
736
901
 
737
- const response = await createClosingEntry({
738
- path: { graph_id: graphId, structure_id: structureId },
739
- body,
740
- })
741
-
742
- if (response.error) {
743
- throw new Error(`Create closing entry failed: ${JSON.stringify(response.error)}`)
902
+ /**
903
+ * Create a manual balanced closing entry (not tied to a schedule).
904
+ * Used for disposals, adjustments, and one-off closing events.
905
+ */
906
+ async createManualClosingEntry(
907
+ graphId: string,
908
+ options: CreateManualClosingEntryOptions
909
+ ): Promise<ClosingEntry> {
910
+ const body: CreateManualClosingEntryRequest = {
911
+ posting_date: options.postingDate,
912
+ memo: options.memo,
913
+ entry_type: options.entryType,
914
+ line_items: options.lineItems.map((li) => ({
915
+ element_id: li.elementId,
916
+ debit_amount: li.debitAmount ?? 0,
917
+ credit_amount: li.creditAmount ?? 0,
918
+ description: li.description ?? null,
919
+ })),
744
920
  }
921
+ const envelope = await this.callOperation(
922
+ 'Create manual closing entry',
923
+ opCreateManualClosingEntry({ path: { graph_id: graphId }, body })
924
+ )
925
+ return rawToClosingEntry(envelope.result as unknown as RawClosingEntryResult)
926
+ }
927
+
928
+ // ── Closing book ───────────────────────────────────────────────────
929
+
930
+ /** Grouped closing book structures for the close-screen sidebar. */
931
+ async getClosingBookStructures(graphId: string): Promise<LedgerClosingBookStructures | null> {
932
+ return this.gqlQuery(
933
+ graphId,
934
+ GetLedgerClosingBookStructuresDocument,
935
+ undefined,
936
+ 'Get closing book structures',
937
+ (data) => data.closingBookStructures
938
+ )
939
+ }
940
+
941
+ // ── Fiscal Calendar ────────────────────────────────────────────────
942
+
943
+ /** Current fiscal calendar state — pointers, gap, closeable status. */
944
+ async getFiscalCalendar(graphId: string): Promise<LedgerFiscalCalendar | null> {
945
+ return this.gqlQuery(
946
+ graphId,
947
+ GetLedgerFiscalCalendarDocument,
948
+ undefined,
949
+ 'Get fiscal calendar',
950
+ (data) => data.fiscalCalendar
951
+ )
952
+ }
745
953
 
746
- const data = response.data as ClosingEntryResponse
954
+ /** One-time ledger initialization — seed fiscal calendar + periods. */
955
+ async initializeLedger(
956
+ graphId: string,
957
+ options?: InitializeLedgerOptions
958
+ ): Promise<InitializeLedgerResult> {
959
+ const body: InitializeLedgerRequest = {
960
+ closed_through: options?.closedThrough ?? null,
961
+ fiscal_year_start_month: options?.fiscalYearStartMonth,
962
+ earliest_data_period: options?.earliestDataPeriod ?? null,
963
+ auto_seed_schedules: options?.autoSeedSchedules,
964
+ note: options?.note ?? null,
965
+ }
966
+ const envelope = await this.callOperation(
967
+ 'Initialize ledger',
968
+ opInitializeLedger({ path: { graph_id: graphId }, body })
969
+ )
970
+ const raw = envelope.result as unknown as RawInitializeLedgerResult
747
971
  return {
748
- entryId: data.entry_id,
749
- status: data.status,
750
- postingDate: data.posting_date,
751
- memo: data.memo,
752
- debitElementId: data.debit_element_id,
753
- creditElementId: data.credit_element_id,
754
- amount: data.amount,
972
+ fiscalCalendar: rawFiscalCalendarToCamel(raw.fiscal_calendar),
973
+ periodsCreated: raw.periods_created ?? 0,
974
+ warnings: raw.warnings ?? [],
755
975
  }
756
976
  }
757
977
 
758
- // ── Closing Book ─────────────────────────────────────────────────────
759
-
760
- /**
761
- * Get all closing book structure categories for the sidebar.
762
- * Returns statements, account rollups, schedules, trial balance,
763
- * and period close grouped into categories.
764
- */
765
- async getClosingBookStructures(graphId: string): Promise<ClosingBookStructuresResponse> {
766
- const response = await getClosingBookStructures({
767
- path: { graph_id: graphId },
768
- })
978
+ /** Set the user-controlled close target (YYYY-MM). */
979
+ async setCloseTarget(
980
+ graphId: string,
981
+ period: string,
982
+ note?: string | null
983
+ ): Promise<LedgerFiscalCalendar> {
984
+ const body: SetCloseTargetOperation = { period, note: note ?? null }
985
+ const envelope = await this.callOperation(
986
+ 'Set close target',
987
+ opSetCloseTarget({ path: { graph_id: graphId }, body })
988
+ )
989
+ return rawFiscalCalendarToCamel(envelope.result as unknown as RawFiscalCalendar)
990
+ }
769
991
 
770
- if (response.error) {
771
- throw new Error(`Get closing book structures failed: ${JSON.stringify(response.error)}`)
992
+ /** Close a fiscal period — the final commit action. */
993
+ async closePeriod(
994
+ graphId: string,
995
+ period: string,
996
+ options?: ClosePeriodOptions
997
+ ): Promise<ClosePeriodResult> {
998
+ const body: ClosePeriodOperation = {
999
+ period,
1000
+ note: options?.note ?? null,
1001
+ allow_stale_sync: options?.allowStaleSync,
772
1002
  }
1003
+ const envelope = await this.callOperation(
1004
+ 'Close period',
1005
+ opClosePeriod({ path: { graph_id: graphId }, body })
1006
+ )
1007
+ const raw = envelope.result as unknown as RawClosePeriodResult
1008
+ return {
1009
+ period: raw.period,
1010
+ entriesPosted: raw.entries_posted ?? 0,
1011
+ targetAutoAdvanced: raw.target_auto_advanced ?? false,
1012
+ fiscalCalendar: rawFiscalCalendarToCamel(raw.fiscal_calendar),
1013
+ }
1014
+ }
773
1015
 
774
- return response.data as ClosingBookStructuresResponse
1016
+ /** Reopen a closed fiscal period. Requires a reason for the audit log. */
1017
+ async reopenPeriod(
1018
+ graphId: string,
1019
+ period: string,
1020
+ reason: string,
1021
+ note?: string | null
1022
+ ): Promise<LedgerFiscalCalendar> {
1023
+ const body: ReopenPeriodOperation = {
1024
+ period,
1025
+ reason,
1026
+ note: note ?? null,
1027
+ }
1028
+ const envelope = await this.callOperation(
1029
+ 'Reopen period',
1030
+ opReopenPeriod({ path: { graph_id: graphId }, body })
1031
+ )
1032
+ return rawFiscalCalendarToCamel(envelope.result as unknown as RawFiscalCalendar)
775
1033
  }
776
1034
 
1035
+ // Reports, statements, and publish lists live on `ReportClient`.
1036
+
1037
+ // ── Internal helpers ────────────────────────────────────────────────
1038
+
777
1039
  /**
778
- * Get account rollups CoA accounts grouped by reporting element with balances.
779
- * Shows how company-specific accounts roll up to standardized reporting lines.
1040
+ * Run a typed GraphQL query against the per-graph endpoint and
1041
+ * translate ClientError into a readable facade error.
780
1042
  */
781
- async getAccountRollups(
1043
+ private async gqlQuery<TData, TVars extends object, TResult>(
782
1044
  graphId: string,
783
- options?: { mappingId?: string; startDate?: string; endDate?: string }
784
- ): Promise<AccountRollupsResponse> {
785
- const response = await getAccountRollups({
786
- path: { graph_id: graphId },
787
- query: {
788
- mapping_id: options?.mappingId,
789
- start_date: options?.startDate,
790
- end_date: options?.endDate,
791
- },
792
- })
1045
+ document: TypedDocumentNode<TData, TVars>,
1046
+ variables: TVars | undefined,
1047
+ label: string,
1048
+ pick: (data: TData) => TResult
1049
+ ): Promise<TResult> {
1050
+ try {
1051
+ const client = this.gql.get(graphId)
1052
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1053
+ const raw = client.request as (doc: unknown, vars?: unknown) => Promise<any>
1054
+ // graphql-request's overloads don't cleanly resolve for generic
1055
+ // helpers wrapping codegen's `Exact<>` var types, so we bypass
1056
+ // the typed overload with a narrow cast of `request` itself.
1057
+ const data = (await raw.call(client, document, variables)) as TData
1058
+ return pick(data)
1059
+ } catch (err) {
1060
+ if (err instanceof ClientError) {
1061
+ throw new Error(`${label} failed: ${JSON.stringify(err.response.errors ?? err.message)}`)
1062
+ }
1063
+ throw err
1064
+ }
1065
+ }
793
1066
 
794
- if (response.error) {
795
- throw new Error(`Get account rollups failed: ${JSON.stringify(response.error)}`)
1067
+ /**
1068
+ * Await an SDK-generated `opXxx(...)` call, throw a readable error on
1069
+ * non-2xx, and return the `OperationEnvelope` on success.
1070
+ */
1071
+ private async callOperation(
1072
+ label: string,
1073
+ call: Promise<{ data?: OperationEnvelope; error?: unknown }>
1074
+ ): Promise<OperationEnvelope> {
1075
+ const response = await call
1076
+ if (response.error !== undefined) {
1077
+ throw new Error(`${label} failed: ${JSON.stringify(response.error)}`)
796
1078
  }
1079
+ if (response.data === undefined) {
1080
+ throw new Error(`${label} failed: empty response`)
1081
+ }
1082
+ return response.data
1083
+ }
1084
+ }
1085
+
1086
+ // ── Module-private conversion helpers ─────────────────────────────────
1087
+
1088
+ function rawToClosingEntry(data: RawClosingEntryResult): ClosingEntry {
1089
+ return {
1090
+ outcome: data.outcome,
1091
+ entryId: data.entry_id ?? null,
1092
+ status: data.status ?? null,
1093
+ postingDate: data.posting_date ?? null,
1094
+ memo: data.memo ?? null,
1095
+ debitElementId: data.debit_element_id ?? null,
1096
+ creditElementId: data.credit_element_id ?? null,
1097
+ amount: data.amount ?? null,
1098
+ reason: data.reason ?? null,
1099
+ }
1100
+ }
797
1101
 
798
- return response.data as AccountRollupsResponse
1102
+ function rawFiscalCalendarToCamel(raw: RawFiscalCalendar): LedgerFiscalCalendar {
1103
+ return {
1104
+ graphId: raw.graph_id,
1105
+ fiscalYearStartMonth: raw.fiscal_year_start_month,
1106
+ closedThrough: raw.closed_through ?? null,
1107
+ closeTarget: raw.close_target ?? null,
1108
+ gapPeriods: raw.gap_periods ?? 0,
1109
+ catchUpSequence: raw.catch_up_sequence ?? [],
1110
+ closeableNow: raw.closeable_now ?? false,
1111
+ blockers: raw.blockers ?? [],
1112
+ lastCloseAt: raw.last_close_at ?? null,
1113
+ initializedAt: raw.initialized_at ?? null,
1114
+ lastSyncAt: raw.last_sync_at ?? null,
1115
+ periods: (raw.periods ?? []).map((p) => ({
1116
+ name: p.name,
1117
+ startDate: p.start_date,
1118
+ endDate: p.end_date,
1119
+ status: p.status,
1120
+ closedAt: p.closed_at ?? null,
1121
+ })),
799
1122
  }
800
1123
  }