@robosystems/client 0.3.9 → 0.3.11

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.
@@ -29,86 +29,73 @@ import {
29
29
  opAutoMapElements,
30
30
  opBuildFactGrid,
31
31
  opClosePeriod,
32
- opCreateAssociations,
33
- opCreateClosingEntry,
34
- opCreateElement,
32
+ opCreateAgent,
33
+ opCreateEventBlock,
34
+ opCreateEventHandler,
35
35
  opCreateInformationBlock,
36
- opCreateJournalEntry,
37
- opCreateManualClosingEntry,
38
36
  opCreateMappingAssociation,
39
37
  opCreatePublishList,
40
38
  opCreateReport,
41
- opCreateStructure,
42
- opCreateTaxonomy,
43
- opCreateTransaction,
44
- opDeleteAssociation,
45
- opDeleteElement,
39
+ opCreateTaxonomyBlock,
46
40
  opDeleteInformationBlock,
47
41
  opDeleteJournalEntry,
48
42
  opDeleteMappingAssociation,
49
43
  opDeletePublishList,
50
44
  opDeleteReport,
51
- opDeleteStructure,
52
- opDeleteTaxonomy,
45
+ opDeleteTaxonomyBlock,
46
+ opEvaluateRules,
47
+ opFinancialStatementAnalysis,
53
48
  opInitializeLedger,
54
49
  opLinkEntityTaxonomy,
50
+ opLiveFinancialStatement,
51
+ opPreviewEventBlock,
55
52
  opRegenerateReport,
56
53
  opRemovePublishListMember,
57
54
  opReopenPeriod,
58
- opReverseJournalEntry,
59
55
  opSetCloseTarget,
60
56
  opShareReport,
61
- opTruncateSchedule,
62
- opUpdateAssociation,
63
- opUpdateElement,
57
+ opUpdateAgent,
64
58
  opUpdateEntity,
59
+ opUpdateEventBlock,
60
+ opUpdateEventHandler,
65
61
  opUpdateInformationBlock,
66
62
  opUpdateJournalEntry,
67
63
  opUpdatePublishList,
68
- opUpdateStructure,
69
- opUpdateTaxonomy,
64
+ opUpdateTaxonomyBlock,
70
65
  } from '../sdk.gen'
71
66
  import type {
72
67
  AddPublishListMembersOperation,
73
68
  AutoMapElementsOperation,
74
- BulkAssociationItem,
75
- BulkCreateAssociationsRequest,
76
69
  ClosePeriodOperation,
77
- CreateClosingEntryOperation,
78
- CreateElementRequest,
70
+ CreateAgentRequest,
71
+ CreateEventBlockRequest,
72
+ CreateEventHandlerRequest,
79
73
  CreateInformationBlockRequest,
80
- CreateJournalEntryRequest,
81
- CreateManualClosingEntryRequest,
82
74
  CreateMappingAssociationOperation,
83
75
  CreatePublishListRequest,
84
76
  CreateReportRequest,
85
- CreateStructureRequest,
86
- CreateTaxonomyRequest,
87
- CreateTransactionRequest,
77
+ CreateTaxonomyBlockRequest,
88
78
  CreateViewRequest,
89
- DeleteAssociationRequest,
90
- DeleteElementRequest,
91
79
  DeleteInformationBlockRequest,
92
80
  DeleteJournalEntryRequest,
93
81
  DeleteMappingAssociationOperation,
94
- DeleteStructureRequest,
95
- DeleteTaxonomyRequest,
82
+ DeleteTaxonomyBlockRequest,
83
+ EvaluateRulesRequest,
84
+ FinancialStatementAnalysisRequest,
96
85
  InitializeLedgerRequest,
97
- JournalEntryLineItemInput,
98
86
  LinkEntityTaxonomyRequest,
87
+ LiveFinancialStatementRequest,
99
88
  OperationEnvelope,
100
89
  ReopenPeriodOperation,
101
- ReverseJournalEntryRequest,
102
90
  SetCloseTargetOperation,
103
- TruncateScheduleOperation,
104
- UpdateAssociationRequest,
105
- UpdateElementRequest,
91
+ UpdateAgentRequest,
106
92
  UpdateEntityRequest,
93
+ UpdateEventBlockRequest,
94
+ UpdateEventHandlerRequest,
107
95
  UpdateInformationBlockRequest,
108
96
  UpdateJournalEntryRequest,
109
97
  UpdatePublishListOperation,
110
- UpdateStructureRequest,
111
- UpdateTaxonomyRequest,
98
+ UpdateTaxonomyBlockRequest,
112
99
  } from '../types.gen'
113
100
  import type { TokenProvider } from './graphql/client'
114
101
  import { GraphQLClientCache } from './graphql/client'
@@ -315,18 +302,6 @@ interface RawClosePeriodResult {
315
302
  fiscal_calendar: RawFiscalCalendar
316
303
  }
317
304
 
318
- interface RawClosingEntryResult {
319
- outcome: ClosingEntryOutcome
320
- entry_id: string | null
321
- status: string | null
322
- posting_date: string | null
323
- memo: string | null
324
- debit_element_id: string | null
325
- credit_element_id: string | null
326
- amount: number | null
327
- reason: string | null
328
- }
329
-
330
305
  interface RawScheduleCreatedResult {
331
306
  structure_id: string
332
307
  name: string
@@ -335,13 +310,6 @@ interface RawScheduleCreatedResult {
335
310
  total_facts: number
336
311
  }
337
312
 
338
- interface RawTruncateScheduleResult {
339
- structure_id: string
340
- new_end_date: string
341
- facts_deleted: number
342
- reason: string
343
- }
344
-
345
313
  export interface InitializeLedgerResult {
346
314
  fiscalCalendar: LedgerFiscalCalendar
347
315
  periodsCreated: number
@@ -355,20 +323,6 @@ export interface ClosePeriodResult {
355
323
  fiscalCalendar: LedgerFiscalCalendar
356
324
  }
357
325
 
358
- export type ClosingEntryOutcome = 'created' | 'unchanged' | 'regenerated' | 'removed' | 'skipped'
359
-
360
- export interface ClosingEntry {
361
- outcome: ClosingEntryOutcome
362
- entryId: string | null
363
- status: string | null
364
- postingDate: string | null
365
- memo: string | null
366
- debitElementId: string | null
367
- creditElementId: string | null
368
- amount: number | null
369
- reason: string | null
370
- }
371
-
372
326
  export interface ScheduleCreated {
373
327
  structureId: string
374
328
  name: string
@@ -377,13 +331,6 @@ export interface ScheduleCreated {
377
331
  totalFacts: number
378
332
  }
379
333
 
380
- export interface TruncateScheduleResult {
381
- structureId: string
382
- newEndDate: string
383
- factsDeleted: number
384
- reason: string
385
- }
386
-
387
334
  export type LedgerEntryType = 'standard' | 'adjusting' | 'closing' | 'reversing'
388
335
 
389
336
  // ── Caller-facing option interfaces ────────────────────────────────────
@@ -401,25 +348,6 @@ export interface ClosePeriodOptions {
401
348
  allowStaleSync?: boolean
402
349
  }
403
350
 
404
- export interface ManualClosingLineItem {
405
- elementId: string
406
- debitAmount?: number
407
- creditAmount?: number
408
- description?: string | null
409
- }
410
-
411
- export interface CreateManualClosingEntryOptions {
412
- postingDate: string
413
- memo: string
414
- lineItems: ManualClosingLineItem[]
415
- entryType?: LedgerEntryType
416
- }
417
-
418
- export interface TruncateScheduleOptions {
419
- newEndDate: string
420
- reason: string
421
- }
422
-
423
351
  export interface CreateScheduleOptions {
424
352
  name: string
425
353
  elementIds: string[]
@@ -641,56 +569,6 @@ export class LedgerClient {
641
569
  )
642
570
  }
643
571
 
644
- /**
645
- * Create a standalone business-event Transaction without entries.
646
- *
647
- * Returns a `transaction_id` that can be passed to `createJournalEntry`
648
- * to attach entries to this event. Use when a single event (invoice,
649
- * payment, deposit) produces multiple entries over its lifecycle.
650
- *
651
- * `amount` is in minor currency units (cents).
652
- * `type` is free-form: invoice, payment, bill, expense, deposit, etc.
653
- */
654
- async createTransaction(
655
- graphId: string,
656
- options: {
657
- type: string
658
- date: string
659
- amount: number
660
- currency?: string
661
- description?: string
662
- merchantName?: string
663
- referenceNumber?: string
664
- number?: string
665
- category?: string
666
- dueDate?: string
667
- status?: 'pending' | 'posted'
668
- idempotencyKey?: string
669
- }
670
- ): Promise<Record<string, unknown>> {
671
- const body: CreateTransactionRequest = {
672
- type: options.type,
673
- date: options.date,
674
- amount: options.amount,
675
- currency: options.currency ?? 'USD',
676
- status: options.status ?? 'pending',
677
- description: options.description ?? null,
678
- merchant_name: options.merchantName ?? null,
679
- reference_number: options.referenceNumber ?? null,
680
- number: options.number ?? null,
681
- category: options.category ?? null,
682
- due_date: options.dueDate ?? null,
683
- }
684
- const headers = options.idempotencyKey
685
- ? { 'Idempotency-Key': options.idempotencyKey }
686
- : undefined
687
- const envelope = await this.callOperation(
688
- 'Create transaction',
689
- opCreateTransaction({ path: { graph_id: graphId }, body, headers })
690
- )
691
- return (envelope.result ?? {}) as Record<string, unknown>
692
- }
693
-
694
572
  // ── Trial Balance ──────────────────────────────────────────────────
695
573
 
696
574
  /** Trial balance by raw CoA account. */
@@ -757,53 +635,60 @@ export class LedgerClient {
757
635
  return list?.taxonomies ?? []
758
636
  }
759
637
 
760
- /** Create a new taxonomy (used for CoA + mapping taxonomies). */
761
- async createTaxonomy(
638
+ /**
639
+ * Link the graph's entity to a taxonomy (ENTITY_HAS_TAXONOMY edge).
640
+ * Idempotent — returns existing linkage if already present.
641
+ */
642
+ async linkEntityTaxonomy(
762
643
  graphId: string,
763
- body: CreateTaxonomyRequest
644
+ body: LinkEntityTaxonomyRequest
764
645
  ): Promise<Record<string, unknown>> {
765
646
  const envelope = await this.callOperation(
766
- 'Create taxonomy',
767
- opCreateTaxonomy({ path: { graph_id: graphId }, body })
647
+ 'Link entity taxonomy',
648
+ opLinkEntityTaxonomy({ path: { graph_id: graphId }, body })
768
649
  )
769
650
  return (envelope.result ?? {}) as Record<string, unknown>
770
651
  }
771
652
 
772
- /** Update mutable fields on a taxonomy. `taxonomy_type` is immutable. */
773
- async updateTaxonomy(
653
+ /**
654
+ * Create a taxonomy block atomically (taxonomy + structures + elements +
655
+ * associations + rules in one envelope).
656
+ */
657
+ async createTaxonomyBlock(
774
658
  graphId: string,
775
- body: UpdateTaxonomyRequest
659
+ body: CreateTaxonomyBlockRequest,
660
+ idempotencyKey?: string
776
661
  ): Promise<Record<string, unknown>> {
662
+ const headers = idempotencyKey ? { 'Idempotency-Key': idempotencyKey } : undefined
777
663
  const envelope = await this.callOperation(
778
- 'Update taxonomy',
779
- opUpdateTaxonomy({ path: { graph_id: graphId }, body })
664
+ 'Create taxonomy block',
665
+ opCreateTaxonomyBlock({ path: { graph_id: graphId }, body, headers })
780
666
  )
781
667
  return (envelope.result ?? {}) as Record<string, unknown>
782
668
  }
783
669
 
784
- /** Soft-delete a taxonomy (`is_active=false`). */
785
- async deleteTaxonomy(graphId: string, taxonomyId: string): Promise<Record<string, unknown>> {
786
- const body: DeleteTaxonomyRequest = { taxonomy_id: taxonomyId }
670
+ /** Update a taxonomy block — add/update/remove elements, structures, associations, or rules. */
671
+ async updateTaxonomyBlock(
672
+ graphId: string,
673
+ body: UpdateTaxonomyBlockRequest
674
+ ): Promise<Record<string, unknown>> {
787
675
  const envelope = await this.callOperation(
788
- 'Delete taxonomy',
789
- opDeleteTaxonomy({ path: { graph_id: graphId }, body })
676
+ 'Update taxonomy block',
677
+ opUpdateTaxonomyBlock({ path: { graph_id: graphId }, body })
790
678
  )
791
679
  return (envelope.result ?? {}) as Record<string, unknown>
792
680
  }
793
681
 
794
- /**
795
- * Link the graph's entity to a taxonomy (ENTITY_HAS_TAXONOMY edge).
796
- * Idempotent — returns existing linkage if already present.
797
- */
798
- async linkEntityTaxonomy(
682
+ /** Delete a taxonomy block. Cascades through elements, structures, and associations. */
683
+ async deleteTaxonomyBlock(
799
684
  graphId: string,
800
- body: LinkEntityTaxonomyRequest
801
- ): Promise<Record<string, unknown>> {
685
+ body: DeleteTaxonomyBlockRequest
686
+ ): Promise<{ deleted: boolean }> {
802
687
  const envelope = await this.callOperation(
803
- 'Link entity taxonomy',
804
- opLinkEntityTaxonomy({ path: { graph_id: graphId }, body })
688
+ 'Delete taxonomy block',
689
+ opDeleteTaxonomyBlock({ path: { graph_id: graphId }, body })
805
690
  )
806
- return (envelope.result ?? {}) as Record<string, unknown>
691
+ return (envelope.result ?? { deleted: true }) as { deleted: boolean }
807
692
  }
808
693
 
809
694
  /** List elements (CoA accounts, GAAP concepts, etc.) with filters. */
@@ -848,40 +733,6 @@ export class LedgerClient {
848
733
  )
849
734
  }
850
735
 
851
- /** Create a new element within a taxonomy (native CoA account, etc.). */
852
- async createElement(
853
- graphId: string,
854
- body: CreateElementRequest
855
- ): Promise<Record<string, unknown>> {
856
- const envelope = await this.callOperation(
857
- 'Create element',
858
- opCreateElement({ path: { graph_id: graphId }, body })
859
- )
860
- return (envelope.result ?? {}) as Record<string, unknown>
861
- }
862
-
863
- /** Update mutable fields on an element. `taxonomy_id` and `source` are immutable. */
864
- async updateElement(
865
- graphId: string,
866
- body: UpdateElementRequest
867
- ): Promise<Record<string, unknown>> {
868
- const envelope = await this.callOperation(
869
- 'Update element',
870
- opUpdateElement({ path: { graph_id: graphId }, body })
871
- )
872
- return (envelope.result ?? {}) as Record<string, unknown>
873
- }
874
-
875
- /** Soft-delete an element (`is_active=false`). */
876
- async deleteElement(graphId: string, elementId: string): Promise<Record<string, unknown>> {
877
- const body: DeleteElementRequest = { element_id: elementId }
878
- const envelope = await this.callOperation(
879
- 'Delete element',
880
- opDeleteElement({ path: { graph_id: graphId }, body })
881
- )
882
- return (envelope.result ?? {}) as Record<string, unknown>
883
- }
884
-
885
736
  // ── Structures ──────────────────────────────────────────────────────
886
737
 
887
738
  /** List reporting structures (IS, BS, CF, schedules) with optional filters. */
@@ -902,62 +753,6 @@ export class LedgerClient {
902
753
  return list?.structures ?? []
903
754
  }
904
755
 
905
- /**
906
- * Create a new structure. Most common use is a CoA→reporting mapping
907
- * structure; also used for custom statement + schedule structures.
908
- */
909
- async createStructure(graphId: string, body: CreateStructureRequest): Promise<LedgerMappingInfo> {
910
- const envelope = await this.callOperation(
911
- 'Create structure',
912
- opCreateStructure({ path: { graph_id: graphId }, body })
913
- )
914
- return envelope.result as unknown as LedgerMappingInfo
915
- }
916
-
917
- /** Convenience wrapper — create a CoA→GAAP mapping structure. */
918
- async createMappingStructure(
919
- graphId: string,
920
- options?: {
921
- name?: string
922
- description?: string | null
923
- taxonomyId?: string
924
- }
925
- ): Promise<LedgerMappingInfo> {
926
- const body: CreateStructureRequest = {
927
- name: options?.name ?? 'CoA to Reporting',
928
- structure_type: 'coa_mapping',
929
- taxonomy_id: options?.taxonomyId ?? 'tax_usgaap_reporting',
930
- description: options?.description ?? 'Map Chart of Accounts to US GAAP reporting concepts',
931
- }
932
- const envelope = await this.callOperation(
933
- 'Create mapping structure',
934
- opCreateStructure({ path: { graph_id: graphId }, body })
935
- )
936
- return envelope.result as unknown as LedgerMappingInfo
937
- }
938
-
939
- /** Update mutable fields on a structure. `structure_type` is immutable. */
940
- async updateStructure(
941
- graphId: string,
942
- body: UpdateStructureRequest
943
- ): Promise<Record<string, unknown>> {
944
- const envelope = await this.callOperation(
945
- 'Update structure',
946
- opUpdateStructure({ path: { graph_id: graphId }, body })
947
- )
948
- return (envelope.result ?? {}) as Record<string, unknown>
949
- }
950
-
951
- /** Soft-delete a structure (`is_active=false`). */
952
- async deleteStructure(graphId: string, structureId: string): Promise<Record<string, unknown>> {
953
- const body: DeleteStructureRequest = { structure_id: structureId }
954
- const envelope = await this.callOperation(
955
- 'Delete structure',
956
- opDeleteStructure({ path: { graph_id: graphId }, body })
957
- )
958
- return (envelope.result ?? {}) as Record<string, unknown>
959
- }
960
-
961
756
  // ── Mappings ────────────────────────────────────────────────────────
962
757
 
963
758
  /** List active CoA→reporting mapping structures. */
@@ -1037,42 +832,6 @@ export class LedgerClient {
1037
832
  return { operationId: envelope.operationId, status: envelope.status }
1038
833
  }
1039
834
 
1040
- /** Bulk create associations within a single structure, atomically. */
1041
- async createAssociations(
1042
- graphId: string,
1043
- structureId: string,
1044
- associations: BulkAssociationItem[]
1045
- ): Promise<Record<string, unknown>> {
1046
- const body: BulkCreateAssociationsRequest = { structure_id: structureId, associations }
1047
- const envelope = await this.callOperation(
1048
- 'Create associations',
1049
- opCreateAssociations({ path: { graph_id: graphId }, body })
1050
- )
1051
- return (envelope.result ?? {}) as Record<string, unknown>
1052
- }
1053
-
1054
- /** Update mutable fields on an association (order, weight, confidence). */
1055
- async updateAssociation(
1056
- graphId: string,
1057
- body: UpdateAssociationRequest
1058
- ): Promise<Record<string, unknown>> {
1059
- const envelope = await this.callOperation(
1060
- 'Update association',
1061
- opUpdateAssociation({ path: { graph_id: graphId }, body })
1062
- )
1063
- return (envelope.result ?? {}) as Record<string, unknown>
1064
- }
1065
-
1066
- /** Hard-delete an association (any type: presentation, calculation, mapping). */
1067
- async deleteAssociation(graphId: string, associationId: string): Promise<{ deleted: boolean }> {
1068
- const body: DeleteAssociationRequest = { association_id: associationId }
1069
- const envelope = await this.callOperation(
1070
- 'Delete association',
1071
- opDeleteAssociation({ path: { graph_id: graphId }, body })
1072
- )
1073
- return (envelope.result ?? { deleted: true }) as { deleted: boolean }
1074
- }
1075
-
1076
835
  // ── Information Blocks ─────────────────────────────────────────────
1077
836
 
1078
837
  /**
@@ -1194,28 +953,57 @@ export class LedgerClient {
1194
953
  return (envelope.result ?? { deleted: true }) as { deleted: boolean }
1195
954
  }
1196
955
 
1197
- /** Truncate a schedule — end it early at `newEndDate`. */
1198
- async truncateSchedule(
956
+ /**
957
+ * Dispose of a schedule asset — atomically truncates forward facts,
958
+ * deletes the SumEquals rule, and posts a balanced disposal entry.
959
+ *
960
+ * Routes through `create-event-block` with `event_type='asset_disposed'`.
961
+ * `occurred_at` is required and represents the disposal date.
962
+ */
963
+ async disposeSchedule(
1199
964
  graphId: string,
1200
- structureId: string,
1201
- options: TruncateScheduleOptions
1202
- ): Promise<TruncateScheduleResult> {
1203
- const body: TruncateScheduleOperation = {
1204
- structure_id: structureId,
1205
- new_end_date: options.newEndDate,
1206
- reason: options.reason,
965
+ options: {
966
+ scheduleId: string
967
+ occurredAt: string
968
+ proceeds?: number
969
+ proceedsElementId?: string | null
970
+ gainLossElementId?: string | null
971
+ memo?: string | null
972
+ reason?: string
973
+ }
974
+ ): Promise<Record<string, unknown>> {
975
+ const body: CreateEventBlockRequest = {
976
+ event_type: 'asset_disposed',
977
+ event_category: 'adjustment',
978
+ source: 'native',
979
+ occurred_at: options.occurredAt,
980
+ apply_handlers: true,
981
+ metadata: {
982
+ schedule_id: options.scheduleId,
983
+ proceeds: options.proceeds ?? 0,
984
+ proceeds_element_id: options.proceedsElementId ?? null,
985
+ gain_loss_element_id: options.gainLossElementId ?? null,
986
+ memo: options.memo ?? null,
987
+ reason: options.reason ?? 'asset_disposed_event',
988
+ },
1207
989
  }
1208
990
  const envelope = await this.callOperation(
1209
- 'Truncate schedule',
1210
- opTruncateSchedule({ path: { graph_id: graphId }, body })
991
+ 'Dispose schedule',
992
+ opCreateEventBlock({ path: { graph_id: graphId }, body })
1211
993
  )
1212
- const raw = envelope.result as unknown as RawTruncateScheduleResult
1213
- return {
1214
- structureId: raw.structure_id,
1215
- newEndDate: raw.new_end_date,
1216
- factsDeleted: raw.facts_deleted,
1217
- reason: raw.reason,
1218
- }
994
+ return (envelope.result ?? {}) as Record<string, unknown>
995
+ }
996
+
997
+ /** Evaluate taxonomy rules against facts in a structure. */
998
+ async evaluateRules(
999
+ graphId: string,
1000
+ body: EvaluateRulesRequest
1001
+ ): Promise<Record<string, unknown>> {
1002
+ const envelope = await this.callOperation(
1003
+ 'Evaluate rules',
1004
+ opEvaluateRules({ path: { graph_id: graphId }, body })
1005
+ )
1006
+ return (envelope.result ?? {}) as Record<string, unknown>
1219
1007
  }
1220
1008
 
1221
1009
  // ── Period close ────────────────────────────────────────────────────
@@ -1248,7 +1036,12 @@ export class LedgerClient {
1248
1036
 
1249
1037
  /**
1250
1038
  * Idempotently create (or refresh) a draft closing entry from a
1251
- * schedule for a period. See `ClosingEntryOutcome` for semantics.
1039
+ * schedule for a period.
1040
+ *
1041
+ * Routes through `create-event-block` with
1042
+ * `event_type='schedule_entry_due'`. Returns the EventBlockEnvelope —
1043
+ * the underlying handler is idempotent and dispatches to one of
1044
+ * created / unchanged / regenerated / removed / skipped internally.
1252
1045
  */
1253
1046
  async createClosingEntry(
1254
1047
  graphId: string,
@@ -1257,45 +1050,26 @@ export class LedgerClient {
1257
1050
  periodStart: string,
1258
1051
  periodEnd: string,
1259
1052
  memo?: string
1260
- ): Promise<ClosingEntry> {
1261
- const body: CreateClosingEntryOperation = {
1262
- structure_id: structureId,
1263
- posting_date: postingDate,
1264
- period_start: periodStart,
1265
- period_end: periodEnd,
1266
- memo: memo ?? undefined,
1053
+ ): Promise<Record<string, unknown>> {
1054
+ const body: CreateEventBlockRequest = {
1055
+ event_type: 'schedule_entry_due',
1056
+ event_category: 'recognition',
1057
+ source: 'scheduled',
1058
+ occurred_at: `${postingDate}T00:00:00Z`,
1059
+ apply_handlers: true,
1060
+ metadata: {
1061
+ schedule_id: structureId,
1062
+ posting_date: postingDate,
1063
+ period_start: periodStart,
1064
+ period_end: periodEnd,
1065
+ memo: memo ?? null,
1066
+ },
1267
1067
  }
1268
1068
  const envelope = await this.callOperation(
1269
1069
  'Create closing entry',
1270
- opCreateClosingEntry({ path: { graph_id: graphId }, body })
1271
- )
1272
- return rawToClosingEntry(envelope.result as unknown as RawClosingEntryResult)
1273
- }
1274
-
1275
- /**
1276
- * Create a manual balanced closing entry (not tied to a schedule).
1277
- * Used for disposals, adjustments, and one-off closing events.
1278
- */
1279
- async createManualClosingEntry(
1280
- graphId: string,
1281
- options: CreateManualClosingEntryOptions
1282
- ): Promise<ClosingEntry> {
1283
- const body: CreateManualClosingEntryRequest = {
1284
- posting_date: options.postingDate,
1285
- memo: options.memo,
1286
- entry_type: options.entryType,
1287
- line_items: options.lineItems.map((li) => ({
1288
- element_id: li.elementId,
1289
- debit_amount: li.debitAmount ?? 0,
1290
- credit_amount: li.creditAmount ?? 0,
1291
- description: li.description ?? null,
1292
- })),
1293
- }
1294
- const envelope = await this.callOperation(
1295
- 'Create manual closing entry',
1296
- opCreateManualClosingEntry({ path: { graph_id: graphId }, body })
1070
+ opCreateEventBlock({ path: { graph_id: graphId }, body })
1297
1071
  )
1298
- return rawToClosingEntry(envelope.result as unknown as RawClosingEntryResult)
1072
+ return (envelope.result ?? {}) as Record<string, unknown>
1299
1073
  }
1300
1074
 
1301
1075
  // ── Closing book ───────────────────────────────────────────────────
@@ -1410,35 +1184,48 @@ export class LedgerClient {
1410
1184
  /**
1411
1185
  * Create a journal entry with balanced line items (DR=CR enforced).
1412
1186
  *
1413
- * Defaults to `status='draft'`. Pass `status='posted'` for historical
1414
- * data import where entries represent already-happened business events.
1187
+ * Routes through `create-event-block` with
1188
+ * `event_type='journal_entry_recorded'` the Python handler forwards
1189
+ * to the internal journal-entry command. Defaults to `status='draft'`;
1190
+ * pass `status='posted'` for historical data imports.
1415
1191
  *
1416
1192
  * Supply `idempotencyKey` to make the call safe to retry — replays
1417
1193
  * within 24 hours return the same envelope. Reusing the key with a
1418
1194
  * different body returns HTTP 409.
1195
+ *
1196
+ * Returns the EventBlockEnvelope (event row fields); query the ledger
1197
+ * separately if you need the resulting entry_id.
1419
1198
  */
1420
1199
  async createJournalEntry(
1421
1200
  graphId: string,
1422
1201
  options: CreateJournalEntryOptions
1423
1202
  ): Promise<Record<string, unknown>> {
1424
- const body: CreateJournalEntryRequest = {
1425
- posting_date: options.postingDate,
1426
- memo: options.memo,
1427
- line_items: options.lineItems.map(
1428
- (li): JournalEntryLineItemInput => ({
1203
+ const body: CreateEventBlockRequest = {
1204
+ event_type: 'journal_entry_recorded',
1205
+ event_category: 'adjustment',
1206
+ source: 'native',
1207
+ occurred_at: `${options.postingDate}T00:00:00Z`,
1208
+ apply_handlers: true,
1209
+ metadata: {
1210
+ posting_date: options.postingDate,
1211
+ memo: options.memo,
1212
+ line_items: options.lineItems.map((li) => ({
1429
1213
  element_id: li.elementId,
1430
1214
  debit_amount: li.debitAmount,
1431
1215
  credit_amount: li.creditAmount,
1432
1216
  description: li.description ?? null,
1433
- })
1434
- ),
1435
- type: options.type,
1436
- status: options.status,
1437
- transaction_id: options.transactionId ?? null,
1217
+ })),
1218
+ type: options.type ?? 'standard',
1219
+ status: options.status ?? 'draft',
1220
+ transaction_id: options.transactionId ?? null,
1221
+ },
1438
1222
  }
1223
+ const headers = options.idempotencyKey
1224
+ ? { 'Idempotency-Key': options.idempotencyKey }
1225
+ : undefined
1439
1226
  const envelope = await this.callOperation(
1440
1227
  'Create journal entry',
1441
- opCreateJournalEntry({ path: { graph_id: graphId }, body })
1228
+ opCreateEventBlock({ path: { graph_id: graphId }, body, headers })
1442
1229
  )
1443
1230
  return (envelope.result ?? {}) as Record<string, unknown>
1444
1231
  }
@@ -1468,20 +1255,177 @@ export class LedgerClient {
1468
1255
  /**
1469
1256
  * Reverse a posted journal entry — creates an offsetting entry and marks
1470
1257
  * the original as reversed.
1258
+ *
1259
+ * Routes through `create-event-block` with
1260
+ * `event_type='journal_entry_reversed'`.
1471
1261
  */
1472
1262
  async reverseJournalEntry(
1473
1263
  graphId: string,
1474
1264
  entryId: string,
1475
- options?: { postingDate?: string | null; memo?: string | null }
1265
+ options?: {
1266
+ postingDate?: string | null
1267
+ memo?: string | null
1268
+ reason?: string | null
1269
+ }
1476
1270
  ): Promise<Record<string, unknown>> {
1477
- const body: ReverseJournalEntryRequest = {
1478
- entry_id: entryId,
1479
- posting_date: options?.postingDate ?? null,
1480
- memo: options?.memo ?? null,
1271
+ const occurredDate = options?.postingDate ?? new Date().toISOString().slice(0, 10)
1272
+ const body: CreateEventBlockRequest = {
1273
+ event_type: 'journal_entry_reversed',
1274
+ event_category: 'adjustment',
1275
+ source: 'native',
1276
+ occurred_at: `${occurredDate}T00:00:00Z`,
1277
+ apply_handlers: true,
1278
+ metadata: {
1279
+ entry_id: entryId,
1280
+ posting_date: options?.postingDate ?? null,
1281
+ memo: options?.memo ?? null,
1282
+ reason: options?.reason ?? null,
1283
+ },
1481
1284
  }
1482
1285
  const envelope = await this.callOperation(
1483
1286
  'Reverse journal entry',
1484
- opReverseJournalEntry({ path: { graph_id: graphId }, body })
1287
+ opCreateEventBlock({ path: { graph_id: graphId }, body })
1288
+ )
1289
+ return (envelope.result ?? {}) as Record<string, unknown>
1290
+ }
1291
+
1292
+ // ── Event blocks (generic preview + status transitions) ──────────────
1293
+
1294
+ /**
1295
+ * Dry-run an event block — resolve the handler, evaluate metadata, and
1296
+ * return the planned GL rows without writing anything. Companion to
1297
+ * `createJournalEntry` / `reverseJournalEntry` / `createClosingEntry` /
1298
+ * `disposeSchedule`: pass the same body you'd send to those methods
1299
+ * (the underlying `CreateEventBlockRequest`) and inspect what the
1300
+ * handler would do.
1301
+ */
1302
+ async previewEventBlock(
1303
+ graphId: string,
1304
+ body: CreateEventBlockRequest
1305
+ ): Promise<Record<string, unknown>> {
1306
+ const envelope = await this.callOperation(
1307
+ 'Preview event block',
1308
+ opPreviewEventBlock({ path: { graph_id: graphId }, body })
1309
+ )
1310
+ return (envelope.result ?? {}) as Record<string, unknown>
1311
+ }
1312
+
1313
+ /**
1314
+ * Apply a status transition and/or field corrections to an existing
1315
+ * event block. Use for posting drafts (`classified` → `committed` →
1316
+ * `fulfilled`), voiding, superseding (correction chains), or patching
1317
+ * `description` / `effective_at` / `metadata`.
1318
+ */
1319
+ async updateEventBlock(
1320
+ graphId: string,
1321
+ body: UpdateEventBlockRequest
1322
+ ): Promise<Record<string, unknown>> {
1323
+ const envelope = await this.callOperation(
1324
+ 'Update event block',
1325
+ opUpdateEventBlock({ path: { graph_id: graphId }, body })
1326
+ )
1327
+ return (envelope.result ?? {}) as Record<string, unknown>
1328
+ }
1329
+
1330
+ // ── Agents (REA counterparties) ───────────────────────────────────────
1331
+
1332
+ /**
1333
+ * Create an agent — REA counterparty (customer, vendor, employee, etc.)
1334
+ * referenced by event blocks via `agent_id`. `(source, external_id)` is
1335
+ * unique when `external_id` is provided, so external-source ingestion is
1336
+ * idempotent at the DB level.
1337
+ */
1338
+ async createAgent(
1339
+ graphId: string,
1340
+ body: CreateAgentRequest,
1341
+ idempotencyKey?: string
1342
+ ): Promise<Record<string, unknown>> {
1343
+ const headers = idempotencyKey ? { 'Idempotency-Key': idempotencyKey } : undefined
1344
+ const envelope = await this.callOperation(
1345
+ 'Create agent',
1346
+ opCreateAgent({ path: { graph_id: graphId }, body, headers })
1347
+ )
1348
+ return (envelope.result ?? {}) as Record<string, unknown>
1349
+ }
1350
+
1351
+ /**
1352
+ * Update an agent. `metadata_patch` is a partial merge into the existing
1353
+ * metadata object; all other fields replace.
1354
+ */
1355
+ async updateAgent(graphId: string, body: UpdateAgentRequest): Promise<Record<string, unknown>> {
1356
+ const envelope = await this.callOperation(
1357
+ 'Update agent',
1358
+ opUpdateAgent({ path: { graph_id: graphId }, body })
1359
+ )
1360
+ return (envelope.result ?? {}) as Record<string, unknown>
1361
+ }
1362
+
1363
+ // ── Event handlers (DSL handler registry) ────────────────────────────
1364
+
1365
+ /**
1366
+ * Register a tenant-configurable event handler — DSL row in the
1367
+ * `event_handlers` table that drives `create-event-block` for event
1368
+ * types not covered by a Python handler. Match selectors plus a
1369
+ * `transaction_template` describing the GL rows to produce.
1370
+ */
1371
+ async createEventHandler(
1372
+ graphId: string,
1373
+ body: CreateEventHandlerRequest
1374
+ ): Promise<Record<string, unknown>> {
1375
+ const envelope = await this.callOperation(
1376
+ 'Create event handler',
1377
+ opCreateEventHandler({ path: { graph_id: graphId }, body })
1378
+ )
1379
+ return (envelope.result ?? {}) as Record<string, unknown>
1380
+ }
1381
+
1382
+ /**
1383
+ * Update a registered event handler. Pass `approve: true` to flip an
1384
+ * AI-suggested handler from unapproved to active.
1385
+ */
1386
+ async updateEventHandler(
1387
+ graphId: string,
1388
+ body: UpdateEventHandlerRequest
1389
+ ): Promise<Record<string, unknown>> {
1390
+ const envelope = await this.callOperation(
1391
+ 'Update event handler',
1392
+ opUpdateEventHandler({ path: { graph_id: graphId }, body })
1393
+ )
1394
+ return (envelope.result ?? {}) as Record<string, unknown>
1395
+ }
1396
+
1397
+ // ── Financial statements (graph-backed) ──────────────────────────────
1398
+
1399
+ /**
1400
+ * Live financial statement — pulls facts directly from the graph for
1401
+ * an explicit period window (or fiscal year) and returns the statement
1402
+ * shape without a persisted Report row. Useful for ad-hoc previews and
1403
+ * dashboards.
1404
+ */
1405
+ async liveFinancialStatement(
1406
+ graphId: string,
1407
+ body: LiveFinancialStatementRequest
1408
+ ): Promise<Record<string, unknown>> {
1409
+ const envelope = await this.callOperation(
1410
+ 'Live financial statement',
1411
+ opLiveFinancialStatement({ path: { graph_id: graphId }, body })
1412
+ )
1413
+ return (envelope.result ?? {}) as Record<string, unknown>
1414
+ }
1415
+
1416
+ /**
1417
+ * Run a financial statement analysis against an existing report.
1418
+ * On shared-repo graphs (e.g. SEC), `ticker` is required; on tenant
1419
+ * graphs it's ignored. Either pass an explicit `report_id` or let the
1420
+ * server auto-resolve via `fiscal_year` + `period_type`.
1421
+ */
1422
+ async financialStatementAnalysis(
1423
+ graphId: string,
1424
+ body: FinancialStatementAnalysisRequest
1425
+ ): Promise<Record<string, unknown>> {
1426
+ const envelope = await this.callOperation(
1427
+ 'Financial statement analysis',
1428
+ opFinancialStatementAnalysis({ path: { graph_id: graphId }, body })
1485
1429
  )
1486
1430
  return (envelope.result ?? {}) as Record<string, unknown>
1487
1431
  }
@@ -1814,20 +1758,6 @@ export class LedgerClient {
1814
1758
 
1815
1759
  // ── Module-private conversion helpers ─────────────────────────────────
1816
1760
 
1817
- function rawToClosingEntry(data: RawClosingEntryResult): ClosingEntry {
1818
- return {
1819
- outcome: data.outcome,
1820
- entryId: data.entry_id ?? null,
1821
- status: data.status ?? null,
1822
- postingDate: data.posting_date ?? null,
1823
- memo: data.memo ?? null,
1824
- debitElementId: data.debit_element_id ?? null,
1825
- creditElementId: data.credit_element_id ?? null,
1826
- amount: data.amount ?? null,
1827
- reason: data.reason ?? null,
1828
- }
1829
- }
1830
-
1831
1761
  function rawFiscalCalendarToCamel(raw: RawFiscalCalendar): LedgerFiscalCalendar {
1832
1762
  return {
1833
1763
  graphId: raw.graph_id,