@robosystems/client 0.2.47 → 0.2.49

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.
@@ -10,11 +10,16 @@
10
10
 
11
11
  import {
12
12
  autoMapElements,
13
+ closeFiscalPeriod,
13
14
  createClosingEntry,
15
+ createManualClosingEntry,
14
16
  createMappingAssociation,
15
17
  createSchedule,
16
18
  createStructure,
17
19
  deleteMappingAssociation,
20
+ getAccountRollups,
21
+ getClosingBookStructures,
22
+ getFiscalCalendar,
18
23
  getLedgerAccountTree,
19
24
  getLedgerEntity,
20
25
  getLedgerSummary,
@@ -26,19 +31,32 @@ import {
26
31
  getPeriodCloseStatus,
27
32
  getReportingTaxonomy,
28
33
  getScheduleFacts,
34
+ initializeLedger,
29
35
  listElements,
30
36
  listLedgerAccounts,
31
37
  listLedgerTransactions,
32
38
  listMappings,
39
+ listPeriodDrafts,
33
40
  listSchedules,
34
41
  listStructures,
42
+ reopenFiscalPeriod,
43
+ setCloseTarget,
44
+ truncateSchedule,
35
45
  } from '../sdk.gen'
36
46
  import type {
37
47
  AccountListResponse,
48
+ AccountRollupsResponse,
38
49
  AccountTreeResponse,
50
+ ClosePeriodRequest,
51
+ ClosePeriodResponse,
52
+ ClosingBookStructuresResponse,
39
53
  ClosingEntryResponse,
40
54
  CreateClosingEntryRequest,
55
+ CreateManualClosingEntryRequest,
41
56
  CreateScheduleRequest,
57
+ FiscalCalendarResponse,
58
+ InitializeLedgerRequest,
59
+ InitializeLedgerResponse,
42
60
  LedgerSummaryResponse,
43
61
  LedgerTransactionDetailResponse,
44
62
  LedgerTransactionListResponse,
@@ -46,12 +64,17 @@ import type {
46
64
  MappingDetailResponse,
47
65
  PeriodCloseItemResponse,
48
66
  PeriodCloseStatusResponse,
67
+ PeriodDraftsResponse,
68
+ ReopenPeriodRequest,
49
69
  ScheduleCreatedResponse,
50
70
  ScheduleFactResponse,
51
71
  ScheduleFactsResponse,
52
72
  ScheduleListResponse,
53
73
  ScheduleSummaryResponse,
74
+ SetCloseTargetRequest,
54
75
  TrialBalanceResponse,
76
+ TruncateScheduleRequest,
77
+ TruncateScheduleResponse,
55
78
  } from '../types.gen'
56
79
 
57
80
  // ── Friendly types ──────────────────────────────────────────────────────
@@ -133,14 +156,145 @@ export interface PeriodCloseStatus {
133
156
  totalPosted: number
134
157
  }
135
158
 
159
+ /**
160
+ * Outcome of an idempotent `createClosingEntry` call.
161
+ *
162
+ * - `created` — no prior draft, new draft created
163
+ * - `unchanged` — prior draft matches current schedule fact, no-op
164
+ * - `regenerated` — prior draft was stale, replaced with a fresh one
165
+ * - `removed` — prior draft existed but schedule no longer covers this period
166
+ * - `skipped` — no prior draft and no in-scope fact; nothing to do
167
+ */
168
+ export type ClosingEntryOutcome = 'created' | 'unchanged' | 'regenerated' | 'removed' | 'skipped'
169
+
170
+ /**
171
+ * Result of an idempotent closing-entry call. `entry_id`, `amount`, and
172
+ * related fields are null for `removed` and `skipped` outcomes.
173
+ */
136
174
  export interface ClosingEntry {
137
- entryId: string
175
+ outcome: ClosingEntryOutcome
176
+ entryId: string | null
177
+ status: string | null
178
+ postingDate: string | null
179
+ memo: string | null
180
+ debitElementId: string | null
181
+ creditElementId: string | null
182
+ amount: number | null
183
+ reason: string | null
184
+ }
185
+
186
+ export type LedgerEntryType = 'standard' | 'adjusting' | 'closing' | 'reversing'
187
+
188
+ // ── Fiscal calendar types ──────────────────────────────────────────────
189
+
190
+ export interface FiscalPeriodSummary {
191
+ name: string
192
+ startDate: string
193
+ endDate: string
138
194
  status: string
195
+ closedAt: string | null
196
+ }
197
+
198
+ export interface FiscalCalendarState {
199
+ graphId: string
200
+ fiscalYearStartMonth: number
201
+ closedThrough: string | null
202
+ closeTarget: string | null
203
+ gapPeriods: number
204
+ catchUpSequence: string[]
205
+ closeableNow: boolean
206
+ blockers: string[]
207
+ lastCloseAt: string | null
208
+ initializedAt: string | null
209
+ lastSyncAt: string | null
210
+ periods: FiscalPeriodSummary[]
211
+ }
212
+
213
+ export interface InitializeLedgerOptions {
214
+ closedThrough?: string | null
215
+ fiscalYearStartMonth?: number
216
+ earliestDataPeriod?: string | null
217
+ autoSeedSchedules?: boolean
218
+ note?: string | null
219
+ }
220
+
221
+ export interface InitializeLedgerResult {
222
+ fiscalCalendar: FiscalCalendarState
223
+ periodsCreated: number
224
+ warnings: string[]
225
+ }
226
+
227
+ export interface ClosePeriodOptions {
228
+ note?: string | null
229
+ allowStaleSync?: boolean
230
+ }
231
+
232
+ export interface ClosePeriodResult {
233
+ period: string
234
+ entriesPosted: number
235
+ targetAutoAdvanced: boolean
236
+ fiscalCalendar: FiscalCalendarState
237
+ }
238
+
239
+ export interface DraftLineItemView {
240
+ lineItemId: string
241
+ elementId: string
242
+ elementCode: string | null
243
+ elementName: string
244
+ debitAmount: number
245
+ creditAmount: number
246
+ description: string | null
247
+ }
248
+
249
+ export interface DraftEntryView {
250
+ entryId: string
251
+ postingDate: string
252
+ type: string
253
+ memo: string | null
254
+ provenance: string | null
255
+ sourceStructureId: string | null
256
+ sourceStructureName: string | null
257
+ lineItems: DraftLineItemView[]
258
+ totalDebit: number
259
+ totalCredit: number
260
+ balanced: boolean
261
+ }
262
+
263
+ export interface PeriodDraftsView {
264
+ period: string
265
+ periodStart: string
266
+ periodEnd: string
267
+ draftCount: number
268
+ totalDebit: number
269
+ totalCredit: number
270
+ allBalanced: boolean
271
+ drafts: DraftEntryView[]
272
+ }
273
+
274
+ export interface ManualClosingLineItem {
275
+ elementId: string
276
+ debitAmount?: number
277
+ creditAmount?: number
278
+ description?: string | null
279
+ }
280
+
281
+ export interface CreateManualClosingEntryOptions {
139
282
  postingDate: string
140
283
  memo: string
141
- debitElementId: string
142
- creditElementId: string
143
- amount: number
284
+ lineItems: ManualClosingLineItem[]
285
+ entryType?: LedgerEntryType
286
+ }
287
+
288
+ export interface TruncateScheduleOptions {
289
+ newEndDate: string
290
+ reason: string
291
+ }
292
+
293
+ export interface TruncateScheduleResult {
294
+ structureId: string
295
+ newEndDate: string
296
+ factsDeleted: number
297
+ reason: string
144
298
  }
145
299
 
146
300
  export interface CreateScheduleOptions {
@@ -152,7 +306,7 @@ export interface CreateScheduleOptions {
152
306
  entryTemplate: {
153
307
  debitElementId: string
154
308
  creditElementId: string
155
- entryType?: string
309
+ entryType?: LedgerEntryType
156
310
  memoTemplate?: string
157
311
  }
158
312
  taxonomyId?: string
@@ -713,7 +867,14 @@ export class LedgerClient {
713
867
  }
714
868
 
715
869
  /**
716
- * Create a draft closing entry from a schedule's facts for a period.
870
+ * Idempotently create (or refresh) a draft closing entry from a schedule's
871
+ * facts for a period. The `outcome` field describes what actually happened:
872
+ *
873
+ * - `created` — new draft
874
+ * - `unchanged` — existing draft still matches the schedule; no-op
875
+ * - `regenerated` — existing draft was stale; replaced
876
+ * - `removed` — schedule no longer covers this period; stale draft deleted
877
+ * - `skipped` — no existing draft and no in-scope fact; nothing to do
717
878
  */
718
879
  async createClosingEntry(
719
880
  graphId: string,
@@ -740,14 +901,349 @@ export class LedgerClient {
740
901
  }
741
902
 
742
903
  const data = response.data as ClosingEntryResponse
904
+ return toClosingEntry(data)
905
+ }
906
+
907
+ // ── Closing Book ─────────────────────────────────────────────────────
908
+
909
+ /**
910
+ * Get all closing book structure categories for the sidebar.
911
+ * Returns statements, account rollups, schedules, trial balance,
912
+ * and period close grouped into categories.
913
+ */
914
+ async getClosingBookStructures(graphId: string): Promise<ClosingBookStructuresResponse> {
915
+ const response = await getClosingBookStructures({
916
+ path: { graph_id: graphId },
917
+ })
918
+
919
+ if (response.error) {
920
+ throw new Error(`Get closing book structures failed: ${JSON.stringify(response.error)}`)
921
+ }
922
+
923
+ return response.data as ClosingBookStructuresResponse
924
+ }
925
+
926
+ /**
927
+ * Get account rollups — CoA accounts grouped by reporting element with balances.
928
+ * Shows how company-specific accounts roll up to standardized reporting lines.
929
+ */
930
+ async getAccountRollups(
931
+ graphId: string,
932
+ options?: { mappingId?: string; startDate?: string; endDate?: string }
933
+ ): Promise<AccountRollupsResponse> {
934
+ const response = await getAccountRollups({
935
+ path: { graph_id: graphId },
936
+ query: {
937
+ mapping_id: options?.mappingId,
938
+ start_date: options?.startDate,
939
+ end_date: options?.endDate,
940
+ },
941
+ })
942
+
943
+ if (response.error) {
944
+ throw new Error(`Get account rollups failed: ${JSON.stringify(response.error)}`)
945
+ }
946
+
947
+ return response.data as AccountRollupsResponse
948
+ }
949
+
950
+ // ── Fiscal Calendar ─────────────────────────────────────────────────
951
+
952
+ /**
953
+ * Initialize the fiscal calendar for a graph. Creates FiscalPeriod rows
954
+ * for the data window, sets `closed_through` / `close_target`, and emits
955
+ * an `initialized` audit event. Fails with 409 if already initialized.
956
+ */
957
+ async initializeLedger(
958
+ graphId: string,
959
+ options?: InitializeLedgerOptions
960
+ ): Promise<InitializeLedgerResult> {
961
+ const body: InitializeLedgerRequest = {
962
+ closed_through: options?.closedThrough ?? null,
963
+ fiscal_year_start_month: options?.fiscalYearStartMonth,
964
+ earliest_data_period: options?.earliestDataPeriod ?? null,
965
+ auto_seed_schedules: options?.autoSeedSchedules,
966
+ note: options?.note ?? null,
967
+ }
968
+
969
+ const response = await initializeLedger({
970
+ path: { graph_id: graphId },
971
+ body,
972
+ })
973
+
974
+ if (response.error) {
975
+ throw new Error(`Initialize ledger failed: ${JSON.stringify(response.error)}`)
976
+ }
977
+
978
+ const data = response.data as InitializeLedgerResponse
979
+ return {
980
+ fiscalCalendar: toFiscalCalendarState(data.fiscal_calendar),
981
+ periodsCreated: data.periods_created ?? 0,
982
+ warnings: data.warnings ?? [],
983
+ }
984
+ }
985
+
986
+ /**
987
+ * Get the current fiscal calendar state — pointers, gap, closeable status.
988
+ */
989
+ async getFiscalCalendar(graphId: string): Promise<FiscalCalendarState> {
990
+ const response = await getFiscalCalendar({
991
+ path: { graph_id: graphId },
992
+ })
993
+
994
+ if (response.error) {
995
+ throw new Error(`Get fiscal calendar failed: ${JSON.stringify(response.error)}`)
996
+ }
997
+
998
+ return toFiscalCalendarState(response.data as FiscalCalendarResponse)
999
+ }
1000
+
1001
+ /**
1002
+ * Set the close target for a graph. Validates that the target is not in
1003
+ * the future and not before `closed_through`.
1004
+ */
1005
+ async setCloseTarget(
1006
+ graphId: string,
1007
+ period: string,
1008
+ note?: string | null
1009
+ ): Promise<FiscalCalendarState> {
1010
+ const body: SetCloseTargetRequest = {
1011
+ period,
1012
+ note: note ?? null,
1013
+ }
1014
+
1015
+ const response = await setCloseTarget({
1016
+ path: { graph_id: graphId },
1017
+ body,
1018
+ })
1019
+
1020
+ if (response.error) {
1021
+ throw new Error(`Set close target failed: ${JSON.stringify(response.error)}`)
1022
+ }
1023
+
1024
+ return toFiscalCalendarState(response.data as FiscalCalendarResponse)
1025
+ }
1026
+
1027
+ /**
1028
+ * Close a fiscal period — the final commit action.
1029
+ *
1030
+ * Validates closeable gates, transitions all draft entries in the period
1031
+ * to `posted`, marks the FiscalPeriod closed, and advances `closed_through`
1032
+ * (auto-advancing `close_target` when reached).
1033
+ */
1034
+ async closePeriod(
1035
+ graphId: string,
1036
+ period: string,
1037
+ options?: ClosePeriodOptions
1038
+ ): Promise<ClosePeriodResult> {
1039
+ const body: ClosePeriodRequest = {
1040
+ note: options?.note ?? null,
1041
+ allow_stale_sync: options?.allowStaleSync,
1042
+ }
1043
+
1044
+ const response = await closeFiscalPeriod({
1045
+ path: { graph_id: graphId, period },
1046
+ body,
1047
+ })
1048
+
1049
+ if (response.error) {
1050
+ throw new Error(`Close period failed: ${JSON.stringify(response.error)}`)
1051
+ }
1052
+
1053
+ const data = response.data as ClosePeriodResponse
1054
+ return {
1055
+ period: data.period,
1056
+ entriesPosted: data.entries_posted ?? 0,
1057
+ targetAutoAdvanced: data.target_auto_advanced ?? false,
1058
+ fiscalCalendar: toFiscalCalendarState(data.fiscal_calendar),
1059
+ }
1060
+ }
1061
+
1062
+ /**
1063
+ * Reopen a closed fiscal period. Requires a non-empty `reason` for the
1064
+ * audit log. Posted entries stay posted; the period transitions to
1065
+ * `closing` so the user can post adjustments and re-close.
1066
+ */
1067
+ async reopenPeriod(
1068
+ graphId: string,
1069
+ period: string,
1070
+ reason: string,
1071
+ note?: string | null
1072
+ ): Promise<FiscalCalendarState> {
1073
+ const body: ReopenPeriodRequest = {
1074
+ reason,
1075
+ note: note ?? null,
1076
+ }
1077
+
1078
+ const response = await reopenFiscalPeriod({
1079
+ path: { graph_id: graphId, period },
1080
+ body,
1081
+ })
1082
+
1083
+ if (response.error) {
1084
+ throw new Error(`Reopen period failed: ${JSON.stringify(response.error)}`)
1085
+ }
1086
+
1087
+ return toFiscalCalendarState(response.data as FiscalCalendarResponse)
1088
+ }
1089
+
1090
+ /**
1091
+ * List all draft entries in a fiscal period for review before close.
1092
+ * Fully expanded with line items, element metadata, and per-entry balance.
1093
+ *
1094
+ * Pure read — call repeatedly without side effects.
1095
+ */
1096
+ async listPeriodDrafts(graphId: string, period: string): Promise<PeriodDraftsView> {
1097
+ const response = await listPeriodDrafts({
1098
+ path: { graph_id: graphId, period },
1099
+ })
1100
+
1101
+ if (response.error) {
1102
+ throw new Error(`List period drafts failed: ${JSON.stringify(response.error)}`)
1103
+ }
1104
+
1105
+ const data = response.data as PeriodDraftsResponse
743
1106
  return {
744
- entryId: data.entry_id,
745
- status: data.status,
746
- postingDate: data.posting_date,
747
- memo: data.memo,
748
- debitElementId: data.debit_element_id,
749
- creditElementId: data.credit_element_id,
750
- amount: data.amount,
1107
+ period: data.period,
1108
+ periodStart: data.period_start,
1109
+ periodEnd: data.period_end,
1110
+ draftCount: data.draft_count,
1111
+ totalDebit: data.total_debit,
1112
+ totalCredit: data.total_credit,
1113
+ allBalanced: data.all_balanced,
1114
+ drafts: (data.drafts ?? []).map((d) => ({
1115
+ entryId: d.entry_id,
1116
+ postingDate: d.posting_date,
1117
+ type: d.type,
1118
+ memo: d.memo ?? null,
1119
+ provenance: d.provenance ?? null,
1120
+ sourceStructureId: d.source_structure_id ?? null,
1121
+ sourceStructureName: d.source_structure_name ?? null,
1122
+ lineItems: d.line_items.map((li) => ({
1123
+ lineItemId: li.line_item_id,
1124
+ elementId: li.element_id,
1125
+ elementCode: li.element_code ?? null,
1126
+ elementName: li.element_name,
1127
+ debitAmount: li.debit_amount,
1128
+ creditAmount: li.credit_amount,
1129
+ description: li.description ?? null,
1130
+ })),
1131
+ totalDebit: d.total_debit,
1132
+ totalCredit: d.total_credit,
1133
+ balanced: d.balanced,
1134
+ })),
751
1135
  }
752
1136
  }
1137
+
1138
+ // ── Schedule mutations ──────────────────────────────────────────────
1139
+
1140
+ /**
1141
+ * Truncate a schedule — end it early by deleting facts with
1142
+ * `period_start > new_end_date`, along with any stale draft entries they
1143
+ * produced. Historical posted facts are preserved.
1144
+ *
1145
+ * `new_end_date` must be a month-end date (service enforces this).
1146
+ */
1147
+ async truncateSchedule(
1148
+ graphId: string,
1149
+ structureId: string,
1150
+ options: TruncateScheduleOptions
1151
+ ): Promise<TruncateScheduleResult> {
1152
+ const body: TruncateScheduleRequest = {
1153
+ new_end_date: options.newEndDate,
1154
+ reason: options.reason,
1155
+ }
1156
+
1157
+ const response = await truncateSchedule({
1158
+ path: { graph_id: graphId, structure_id: structureId },
1159
+ body,
1160
+ })
1161
+
1162
+ if (response.error) {
1163
+ throw new Error(`Truncate schedule failed: ${JSON.stringify(response.error)}`)
1164
+ }
1165
+
1166
+ const data = response.data as TruncateScheduleResponse
1167
+ return {
1168
+ structureId: data.structure_id,
1169
+ newEndDate: data.new_end_date,
1170
+ factsDeleted: data.facts_deleted,
1171
+ reason: data.reason,
1172
+ }
1173
+ }
1174
+
1175
+ /**
1176
+ * Create a manual draft closing entry with arbitrary balanced line items.
1177
+ * Not tied to a schedule — used for disposals, adjustments, and other
1178
+ * one-off closing events.
1179
+ *
1180
+ * Line items must sum to balanced debits/credits. Rejects entries
1181
+ * targeting an already-closed period.
1182
+ */
1183
+ async createManualClosingEntry(
1184
+ graphId: string,
1185
+ options: CreateManualClosingEntryOptions
1186
+ ): Promise<ClosingEntry> {
1187
+ const body: CreateManualClosingEntryRequest = {
1188
+ posting_date: options.postingDate,
1189
+ memo: options.memo,
1190
+ entry_type: options.entryType,
1191
+ line_items: options.lineItems.map((li) => ({
1192
+ element_id: li.elementId,
1193
+ debit_amount: li.debitAmount ?? 0,
1194
+ credit_amount: li.creditAmount ?? 0,
1195
+ description: li.description ?? null,
1196
+ })),
1197
+ }
1198
+
1199
+ const response = await createManualClosingEntry({
1200
+ path: { graph_id: graphId },
1201
+ body,
1202
+ })
1203
+
1204
+ if (response.error) {
1205
+ throw new Error(`Create manual closing entry failed: ${JSON.stringify(response.error)}`)
1206
+ }
1207
+
1208
+ return toClosingEntry(response.data as ClosingEntryResponse)
1209
+ }
1210
+ }
1211
+
1212
+ // ── Internal helpers ──────────────────────────────────────────────────
1213
+
1214
+ function toClosingEntry(data: ClosingEntryResponse): ClosingEntry {
1215
+ return {
1216
+ outcome: data.outcome as ClosingEntryOutcome,
1217
+ entryId: data.entry_id ?? null,
1218
+ status: data.status ?? null,
1219
+ postingDate: data.posting_date ?? null,
1220
+ memo: data.memo ?? null,
1221
+ debitElementId: data.debit_element_id ?? null,
1222
+ creditElementId: data.credit_element_id ?? null,
1223
+ amount: data.amount ?? null,
1224
+ reason: data.reason ?? null,
1225
+ }
1226
+ }
1227
+
1228
+ function toFiscalCalendarState(data: FiscalCalendarResponse): FiscalCalendarState {
1229
+ return {
1230
+ graphId: data.graph_id,
1231
+ fiscalYearStartMonth: data.fiscal_year_start_month,
1232
+ closedThrough: data.closed_through ?? null,
1233
+ closeTarget: data.close_target ?? null,
1234
+ gapPeriods: data.gap_periods ?? 0,
1235
+ catchUpSequence: data.catch_up_sequence ?? [],
1236
+ closeableNow: data.closeable_now ?? false,
1237
+ blockers: data.blockers ?? [],
1238
+ lastCloseAt: data.last_close_at ?? null,
1239
+ initializedAt: data.initialized_at ?? null,
1240
+ lastSyncAt: data.last_sync_at ?? null,
1241
+ periods: (data.periods ?? []).map((p) => ({
1242
+ name: p.name,
1243
+ startDate: p.start_date,
1244
+ endDate: p.end_date,
1245
+ status: p.status,
1246
+ closedAt: p.closed_at ?? null,
1247
+ })),
1248
+ }
753
1249
  }