@firela/api-types 0.0.0-canary.a3c2cb48 → 0.0.0-canary.a4e21680

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.
@@ -8,6 +8,12 @@ export const $CreateAccountDto = {
8
8
  description: 'Account path (hierarchical, colon-separated)',
9
9
  example: 'Assets:CN:Bank:ICBC:Checking'
10
10
  },
11
+ displayName: {
12
+ type: 'string',
13
+ description:
14
+ 'Display name to distinguish accounts at the same path (default: "")',
15
+ example: '工资卡'
16
+ },
11
17
  openDate: {
12
18
  format: 'date-time',
13
19
  type: 'string',
@@ -86,6 +92,12 @@ export const $AccountResponseDto = {
86
92
  description: 'Account path (hierarchical, colon-separated)',
87
93
  example: 'Assets:CN:Bank:ICBC:Checking'
88
94
  },
95
+ displayName: {
96
+ type: 'string',
97
+ description:
98
+ 'Display name distinguishing multiple accounts at the same path',
99
+ example: '工资卡'
100
+ },
89
101
  type: {
90
102
  type: 'string',
91
103
  description: 'Account type (root segment)',
@@ -180,6 +192,7 @@ export const $AccountResponseDto = {
180
192
  required: [
181
193
  'id',
182
194
  'path',
195
+ 'displayName',
183
196
  'type',
184
197
  'status',
185
198
  'openDate',
@@ -212,6 +225,11 @@ export const $AccountListResponseDto = {
212
225
  export const $UpdateAccountDto = {
213
226
  type: 'object',
214
227
  properties: {
228
+ displayName: {
229
+ type: 'string',
230
+ description: 'Display name to distinguish accounts at the same path',
231
+ example: '招行工资卡'
232
+ },
215
233
  currencies: {
216
234
  description: 'Allowed currencies (null = no restriction)',
217
235
  example: ['CNY', 'USD'],
@@ -251,7 +269,7 @@ export const $UpdateAccountDto = {
251
269
  }
252
270
  },
253
271
  platformId: {
254
- type: 'object',
272
+ type: 'string',
255
273
  description:
256
274
  'Platform ID (references Platform.id), null to clear association',
257
275
  example: 'c98e5d4a-2f71-4a5a-bb3c-92c9f231d5e2',
@@ -292,6 +310,222 @@ export const $ReopenAccountDto = {
292
310
  }
293
311
  } as const;
294
312
 
313
+ export const $AccountStandardResponseDto = {
314
+ type: 'object',
315
+ properties: {
316
+ path: {
317
+ type: 'string',
318
+ description: 'Account path (hierarchical, colon-separated)',
319
+ example: 'Assets:CN:Bank:ICBC:Checking'
320
+ },
321
+ type: {
322
+ type: 'string',
323
+ description: 'Account type in Beancount hierarchy',
324
+ enum: ['Assets', 'Liabilities', 'Income', 'Expenses', 'Equity'],
325
+ example: 'Assets'
326
+ },
327
+ i18nKey: {
328
+ type: 'string',
329
+ description: 'i18n key for localized display name',
330
+ example: 'account.assets.cn.bank.icbc.checking'
331
+ },
332
+ name: {
333
+ type: 'string',
334
+ description: 'Short localized display name',
335
+ example: 'Housing Fund'
336
+ },
337
+ description: {
338
+ type: 'string',
339
+ description: 'Account description (stable semantics only)',
340
+ example: 'ICBC checking account for daily transactions'
341
+ },
342
+ tags: {
343
+ description: 'Account tags for categorization',
344
+ example: ['bank', 'checking', 'primary'],
345
+ type: 'array',
346
+ items: {
347
+ type: 'string'
348
+ }
349
+ },
350
+ icon: {
351
+ type: 'string',
352
+ description: 'Icon identifier for UI display',
353
+ example: 'bank-icbc'
354
+ }
355
+ },
356
+ required: ['path', 'type', 'i18nKey', 'description', 'tags', 'icon']
357
+ } as const;
358
+
359
+ export const $AccountStandardListResponseDto = {
360
+ type: 'object',
361
+ properties: {
362
+ items: {
363
+ description: 'Array of account templates',
364
+ type: 'array',
365
+ items: {
366
+ $ref: '#/components/schemas/AccountStandardResponseDto'
367
+ }
368
+ },
369
+ total: {
370
+ type: 'number',
371
+ description: 'Total number of account templates',
372
+ example: 150
373
+ },
374
+ region: {
375
+ type: 'string',
376
+ description: 'Region code',
377
+ example: 'CN'
378
+ }
379
+ },
380
+ required: ['items', 'total', 'region']
381
+ } as const;
382
+
383
+ export const $TemplateMetadataDto = {
384
+ type: 'object',
385
+ properties: {
386
+ extendable: {
387
+ type: 'boolean',
388
+ description: 'Whether this path can be extended',
389
+ example: true
390
+ },
391
+ rootType: {
392
+ type: 'string',
393
+ description: 'Root account type',
394
+ example: 'Assets'
395
+ }
396
+ },
397
+ required: ['extendable', 'rootType']
398
+ } as const;
399
+
400
+ export const $TemplateMetadataResponseDto = {
401
+ type: 'object',
402
+ properties: {
403
+ metadata: {
404
+ $ref: '#/components/schemas/TemplateMetadataDto'
405
+ }
406
+ }
407
+ } as const;
408
+
409
+ export const $RegionConfigDto = {
410
+ type: 'object',
411
+ properties: {
412
+ currency: {
413
+ type: 'string',
414
+ example: 'EUR'
415
+ },
416
+ dateFormat: {
417
+ type: 'string',
418
+ example: 'DD.MM.YYYY'
419
+ },
420
+ locale: {
421
+ type: 'string',
422
+ example: 'de-DE'
423
+ }
424
+ },
425
+ required: ['currency', 'dateFormat', 'locale']
426
+ } as const;
427
+
428
+ export const $RegionInfoDto = {
429
+ type: 'object',
430
+ properties: {
431
+ code: {
432
+ type: 'string',
433
+ example: 'de'
434
+ },
435
+ displayName: {
436
+ type: 'string',
437
+ example: 'Germany'
438
+ },
439
+ parent: {
440
+ type: 'string'
441
+ },
442
+ chain: {
443
+ example: ['de'],
444
+ type: 'array',
445
+ items: {
446
+ type: 'string'
447
+ }
448
+ },
449
+ config: {
450
+ $ref: '#/components/schemas/RegionConfigDto'
451
+ }
452
+ },
453
+ required: ['code', 'displayName', 'chain', 'config']
454
+ } as const;
455
+
456
+ export const $RegionsMetadataResponseDto = {
457
+ type: 'object',
458
+ properties: {
459
+ regions: {
460
+ type: 'array',
461
+ items: {
462
+ $ref: '#/components/schemas/RegionInfoDto'
463
+ }
464
+ }
465
+ },
466
+ required: ['regions']
467
+ } as const;
468
+
469
+ export const $CostSpecDto = {
470
+ type: 'object',
471
+ properties: {
472
+ mode: {
473
+ type: 'string',
474
+ enum: ['per-unit', 'total', 'date', 'label', 'auto'],
475
+ description: 'Cost specification mode (mirrors engine CostSpec)'
476
+ },
477
+ numberPerUnit: {
478
+ type: 'string',
479
+ description: 'Per-unit cost (required when mode is "per-unit")',
480
+ example: '240'
481
+ },
482
+ totalNumber: {
483
+ type: 'string',
484
+ description: 'Total cost for all units (required when mode is "total")',
485
+ example: '12000'
486
+ },
487
+ currency: {
488
+ type: 'string',
489
+ description: 'Cost currency (required in all modes)',
490
+ example: 'USD'
491
+ },
492
+ date: {
493
+ type: 'string',
494
+ description:
495
+ 'Lot acquisition date, ISO 8601 (required when mode is "date")',
496
+ example: '2024-01-15'
497
+ },
498
+ label: {
499
+ type: 'string',
500
+ description:
501
+ 'Lot label (required when mode is "label"; optional tag in buy modes)'
502
+ },
503
+ merge: {
504
+ type: 'boolean',
505
+ description: 'Merge lots for AVERAGE booking (mode: auto)'
506
+ }
507
+ },
508
+ required: ['mode', 'currency']
509
+ } as const;
510
+
511
+ export const $AmountDto = {
512
+ type: 'object',
513
+ properties: {
514
+ number: {
515
+ type: 'string',
516
+ description:
517
+ 'Amount as decimal string (max 15 integer + 15 decimal digits)',
518
+ example: '170.50'
519
+ },
520
+ currency: {
521
+ type: 'string',
522
+ description: 'Currency/commodity code',
523
+ example: 'USD'
524
+ }
525
+ },
526
+ required: ['number', 'currency']
527
+ } as const;
528
+
295
529
  export const $CreatePostingDto = {
296
530
  type: 'object',
297
531
  properties: {
@@ -321,6 +555,33 @@ export const $CreatePostingDto = {
321
555
  example: {
322
556
  'tax-lot': 'Q1-2024'
323
557
  }
558
+ },
559
+ cost: {
560
+ description:
561
+ 'Cost basis (Beancount `{...}`). Maps to engine costSpec. Required for commodity holdings so they carry a monetary weight that can balance.',
562
+ example: {
563
+ mode: 'per-unit',
564
+ numberPerUnit: '240',
565
+ currency: 'USD'
566
+ },
567
+ allOf: [
568
+ {
569
+ $ref: '#/components/schemas/CostSpecDto'
570
+ }
571
+ ]
572
+ },
573
+ price: {
574
+ description:
575
+ 'Price annotation (Beancount `@...`). Maps to engine price. Used for valuation; cost takes priority for balance weight.',
576
+ example: {
577
+ number: '170',
578
+ currency: 'USD'
579
+ },
580
+ allOf: [
581
+ {
582
+ $ref: '#/components/schemas/AmountDto'
583
+ }
584
+ ]
324
585
  }
325
586
  },
326
587
  required: ['account']
@@ -399,6 +660,32 @@ export const $CreateTransactionDto = {
399
660
  required: ['date', 'narration', 'postings']
400
661
  } as const;
401
662
 
663
+ export const $CostDetailDto = {
664
+ type: 'object',
665
+ properties: {
666
+ number: {
667
+ type: 'string',
668
+ description: 'Per-unit cost basis (mirrors engine Cost.number)',
669
+ example: '240'
670
+ },
671
+ currency: {
672
+ type: 'string',
673
+ description: 'Cost currency',
674
+ example: 'USD'
675
+ },
676
+ date: {
677
+ type: 'string',
678
+ description: 'Lot acquisition date (ISO yyyy-mm-dd)',
679
+ example: '2024-01-15'
680
+ },
681
+ label: {
682
+ type: 'string',
683
+ description: 'Lot label',
684
+ example: 'lot-2024-01'
685
+ }
686
+ }
687
+ } as const;
688
+
402
689
  export const $PostingResponseDto = {
403
690
  type: 'object',
404
691
  properties: {
@@ -409,13 +696,23 @@ export const $PostingResponseDto = {
409
696
  },
410
697
  units: {
411
698
  type: 'string',
412
- description: 'Amount (may be null if interpolated)',
699
+ description:
700
+ 'Amount as decimal string. Typed optional but always present in responses: interpolation fills any MISSING posting before it is persisted or returned.',
413
701
  example: '100.50'
414
702
  },
415
703
  currency: {
416
704
  type: 'string',
417
705
  description: 'Currency',
418
706
  example: 'USD'
707
+ },
708
+ cost: {
709
+ description:
710
+ 'Booking-resolved cost (mirrors engine Cost). Undefined when the posting has no cost basis.',
711
+ allOf: [
712
+ {
713
+ $ref: '#/components/schemas/CostDetailDto'
714
+ }
715
+ ]
419
716
  }
420
717
  },
421
718
  required: ['account']
@@ -607,27 +904,166 @@ export const $ApiProblemResponseDto = {
607
904
  required: ['type', 'title', 'status', 'detail']
608
905
  } as const;
609
906
 
610
- export const $PostingDetailDto = {
907
+ export const $BatchCreateTransactionDto = {
611
908
  type: 'object',
612
909
  properties: {
613
- id: {
614
- type: 'string',
615
- description: 'Posting ID',
616
- example: 'clh1234567890abcdef'
910
+ transactions: {
911
+ description: 'Array of transactions to create',
912
+ minItems: 1,
913
+ maxItems: 100,
914
+ type: 'array',
915
+ items: {
916
+ $ref: '#/components/schemas/CreateTransactionDto'
917
+ }
918
+ }
919
+ },
920
+ required: ['transactions']
921
+ } as const;
922
+
923
+ export const $BatchTransactionErrorDto = {
924
+ type: 'object',
925
+ properties: {
926
+ index: {
927
+ type: 'number',
928
+ description: 'Index of failed transaction in the input array',
929
+ example: 0
617
930
  },
618
- accountId: {
931
+ error: {
619
932
  type: 'string',
620
- description: 'Account ID',
621
- example: 'clh1234567890abcdef'
933
+ description: 'Error message describing the failure',
934
+ example: 'Transaction does not balance'
622
935
  },
623
- accountName: {
936
+ errorCode: {
624
937
  type: 'string',
625
- description: 'Account name',
938
+ description: 'Structured error code for programmatic handling',
939
+ example: 'INSUFFICIENT_QUANTITY'
940
+ }
941
+ },
942
+ required: ['index', 'error']
943
+ } as const;
944
+
945
+ export const $BatchTransactionResponseDto = {
946
+ type: 'object',
947
+ properties: {
948
+ succeeded: {
949
+ description: 'Successfully created transactions',
950
+ type: 'array',
951
+ items: {
952
+ $ref: '#/components/schemas/TransactionResponseDto'
953
+ }
954
+ },
955
+ failed: {
956
+ description: 'Failed transactions with error details',
957
+ type: 'array',
958
+ items: {
959
+ $ref: '#/components/schemas/BatchTransactionErrorDto'
960
+ }
961
+ }
962
+ },
963
+ required: ['succeeded', 'failed']
964
+ } as const;
965
+
966
+ export const $CorrectTransactionDto = {
967
+ type: 'object',
968
+ properties: {
969
+ date: {
970
+ type: 'string',
971
+ description: 'Transaction date (ISO 8601 format)',
972
+ example: '2024-11-28'
973
+ },
974
+ flag: {
975
+ type: 'string',
976
+ description: 'Transaction flag: * (cleared), ! (pending)',
977
+ enum: ['*', '!'],
978
+ example: '*'
979
+ },
980
+ payee: {
981
+ type: 'string',
982
+ description: 'Payee name',
983
+ example: 'Whole Foods Market'
984
+ },
985
+ narration: {
986
+ type: 'string',
987
+ description: 'Transaction narration/description',
988
+ example: 'Grocery shopping'
989
+ },
990
+ tags: {
991
+ description: 'Transaction tags (without # prefix)',
992
+ example: ['vacation', 'personal'],
993
+ type: 'array',
994
+ items: {
995
+ type: 'string'
996
+ }
997
+ },
998
+ links: {
999
+ description: 'Transaction links (without ^ prefix)',
1000
+ example: ['invoice-123'],
1001
+ type: 'array',
1002
+ items: {
1003
+ type: 'string'
1004
+ }
1005
+ },
1006
+ postings: {
1007
+ description:
1008
+ 'Transaction postings (minimum 1, typically 2 for double-entry)',
1009
+ type: 'array',
1010
+ items: {
1011
+ $ref: '#/components/schemas/CreatePostingDto'
1012
+ }
1013
+ },
1014
+ meta: {
1015
+ type: 'object',
1016
+ description: 'Transaction-level metadata',
1017
+ example: {
1018
+ invoice: '12345'
1019
+ }
1020
+ },
1021
+ idempotencyKey: {
1022
+ type: 'string',
1023
+ description:
1024
+ 'Unique key for idempotent transaction creation. If provided, duplicate requests with the same key will return the existing transaction.',
1025
+ example: 'import-2024-01-15-batch-001',
1026
+ maxLength: 128
1027
+ },
1028
+ autoCreateAccounts: {
1029
+ type: 'boolean',
1030
+ description:
1031
+ 'Auto-create accounts if not found. When true, missing accounts will be automatically created. When false (default for API), missing accounts will cause a validation error. Set to true for quick entry scenarios where you want to create accounts on-the-fly.',
1032
+ default: true,
1033
+ example: true
1034
+ },
1035
+ correctionReason: {
1036
+ type: 'string',
1037
+ description: 'Reason for correcting/superseding the original transaction',
1038
+ example: 'Wrong amount — corrected from receipt',
1039
+ maxLength: 500
1040
+ }
1041
+ },
1042
+ required: ['date', 'narration', 'postings']
1043
+ } as const;
1044
+
1045
+ export const $PostingDetailDto = {
1046
+ type: 'object',
1047
+ properties: {
1048
+ id: {
1049
+ type: 'string',
1050
+ description: 'Posting ID',
1051
+ example: 'clh1234567890abcdef'
1052
+ },
1053
+ accountId: {
1054
+ type: 'string',
1055
+ description: 'Account ID',
1056
+ example: 'clh1234567890abcdef'
1057
+ },
1058
+ account: {
1059
+ type: 'string',
1060
+ description: 'Fully-qualified Beancount account path',
626
1061
  example: 'Assets:Bank:Checking'
627
1062
  },
628
1063
  units: {
629
1064
  type: 'string',
630
- description: 'Amount (may be null if interpolated)',
1065
+ description:
1066
+ 'Amount as decimal string. Typed optional but always present in responses: interpolation fills any MISSING posting before it is persisted or returned.',
631
1067
  example: '100.50'
632
1068
  },
633
1069
  currency: {
@@ -650,6 +1086,15 @@ export const $PostingDetailDto = {
650
1086
  description: 'Cost date',
651
1087
  example: '2024-01-15'
652
1088
  },
1089
+ cost: {
1090
+ description:
1091
+ 'Booking-resolved cost (mirrors engine Cost). Undefined when the posting has no cost basis.',
1092
+ allOf: [
1093
+ {
1094
+ $ref: '#/components/schemas/CostDetailDto'
1095
+ }
1096
+ ]
1097
+ },
653
1098
  priceAmount: {
654
1099
  type: 'string',
655
1100
  description: 'Price amount',
@@ -670,7 +1115,7 @@ export const $PostingDetailDto = {
670
1115
  description: 'Posting metadata'
671
1116
  }
672
1117
  },
673
- required: ['id', 'accountId', 'accountName']
1118
+ required: ['id', 'accountId', 'account']
674
1119
  } as const;
675
1120
 
676
1121
  export const $TransactionDetailDto = {
@@ -742,8 +1187,8 @@ export const $TransactionDetailDto = {
742
1187
  },
743
1188
  sourceType: {
744
1189
  type: 'string',
745
- description: 'Source type (how the transaction was created)',
746
- enum: ['NLP', 'CSV', 'OCR', 'API']
1190
+ description:
1191
+ 'Source type (free-form string from transaction metadata, e.g. import, api)'
747
1192
  },
748
1193
  sourcePlatform: {
749
1194
  type: 'string',
@@ -776,6 +1221,18 @@ export const $TransactionDetailDto = {
776
1221
  type: 'string',
777
1222
  description: 'Correction reason (if voided or superseded)',
778
1223
  example: 'Duplicate entry'
1224
+ },
1225
+ supersededBy: {
1226
+ type: 'string',
1227
+ description:
1228
+ 'ID of the transaction that supersedes this one (set when status=SUPERSEDED)',
1229
+ example: 'clh1234567890abcdef'
1230
+ },
1231
+ originalTxn: {
1232
+ type: 'string',
1233
+ description:
1234
+ 'ID of the transaction this one corrected/replaced (back-link on the replacement)',
1235
+ example: 'clh1234567890abcdef'
779
1236
  }
780
1237
  },
781
1238
  required: [
@@ -790,6 +1247,77 @@ export const $TransactionDetailDto = {
790
1247
  ]
791
1248
  } as const;
792
1249
 
1250
+ export const $BalanceByCurrencyDto = {
1251
+ type: 'object',
1252
+ properties: {
1253
+ currency: {
1254
+ type: 'string',
1255
+ description: 'ISO 4217 currency code',
1256
+ example: 'CNY'
1257
+ },
1258
+ balance: {
1259
+ type: 'string',
1260
+ description: 'Balance amount',
1261
+ example: '50000.00'
1262
+ }
1263
+ },
1264
+ required: ['currency', 'balance']
1265
+ } as const;
1266
+
1267
+ export const $ExchangeRateWarningDto = {
1268
+ type: 'object',
1269
+ properties: {
1270
+ type: {
1271
+ type: 'string',
1272
+ description: 'Warning type',
1273
+ example: 'MISSING_EXCHANGE_RATE'
1274
+ },
1275
+ currency: {
1276
+ type: 'string',
1277
+ description: 'Currency without exchange rate',
1278
+ example: 'EUR'
1279
+ },
1280
+ totalAmount: {
1281
+ type: 'string',
1282
+ description: 'Total amount affected',
1283
+ example: '1000.00'
1284
+ }
1285
+ },
1286
+ required: ['type', 'currency', 'totalAmount']
1287
+ } as const;
1288
+
1289
+ export const $TransactionListSummaryDto = {
1290
+ type: 'object',
1291
+ properties: {
1292
+ totalAmount: {
1293
+ type: 'string',
1294
+ description:
1295
+ 'Partial converted total in base currency (rated currencies only, raw Beancount sign). When warnings is non-empty this excludes currencies missing an FX rate; may be "0.00" if ALL non-base currencies lack a rate. Converted at the dateTo (or current) available rate.',
1296
+ example: '-6000.00'
1297
+ },
1298
+ currency: {
1299
+ type: 'string',
1300
+ description: 'Base currency (ISO 4217)',
1301
+ example: 'CNY'
1302
+ },
1303
+ balanceByCurrency: {
1304
+ description: 'Raw (unconverted) balance per currency',
1305
+ type: 'array',
1306
+ items: {
1307
+ $ref: '#/components/schemas/BalanceByCurrencyDto'
1308
+ }
1309
+ },
1310
+ warnings: {
1311
+ description: 'Currencies missing an FX rate (omitted when empty)',
1312
+ type: 'array',
1313
+ items: {
1314
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
1315
+ }
1316
+ }
1317
+ },
1318
+ required: ['totalAmount', 'currency', 'balanceByCurrency']
1319
+ } as const;
1320
+
793
1321
  export const $TransactionListResponseDto = {
794
1322
  type: 'object',
795
1323
  properties: {
@@ -814,11 +1342,51 @@ export const $TransactionListResponseDto = {
814
1342
  type: 'number',
815
1343
  description: 'Number of items skipped',
816
1344
  example: 0
1345
+ },
1346
+ summary: {
1347
+ description:
1348
+ 'Amount summary for the full filtered set (#514). Present only when the request has a single account OR category viewpoint; omitted for search-only / plain-list / dual-perspective requests.',
1349
+ allOf: [
1350
+ {
1351
+ $ref: '#/components/schemas/TransactionListSummaryDto'
1352
+ }
1353
+ ]
817
1354
  }
818
1355
  },
819
1356
  required: ['data', 'total', 'limit', 'offset']
820
1357
  } as const;
821
1358
 
1359
+ export const $TagSuggestionDto = {
1360
+ type: 'object',
1361
+ properties: {
1362
+ tag: {
1363
+ type: 'string',
1364
+ description: 'Tag name',
1365
+ example: 'Monthly'
1366
+ },
1367
+ count: {
1368
+ type: 'number',
1369
+ description: 'Usage count across ACTIVE transactions',
1370
+ example: 12
1371
+ }
1372
+ },
1373
+ required: ['tag', 'count']
1374
+ } as const;
1375
+
1376
+ export const $TagSuggestionsResponseDto = {
1377
+ type: 'object',
1378
+ properties: {
1379
+ data: {
1380
+ description: 'Tag suggestions sorted as requested',
1381
+ type: 'array',
1382
+ items: {
1383
+ $ref: '#/components/schemas/TagSuggestionDto'
1384
+ }
1385
+ }
1386
+ },
1387
+ required: ['data']
1388
+ } as const;
1389
+
822
1390
  export const $UpdateTransactionDto = {
823
1391
  type: 'object',
824
1392
  properties: {
@@ -876,166 +1444,40 @@ export const $UpdateTransactionDto = {
876
1444
  }
877
1445
  } as const;
878
1446
 
879
- export const $AccountStandardResponseDto = {
1447
+ export const $BalanceResponseDto = {
880
1448
  type: 'object',
881
1449
  properties: {
882
- path: {
1450
+ account: {
883
1451
  type: 'string',
884
- description: 'Account path (hierarchical, colon-separated)',
885
- example: 'Assets:CN:Bank:ICBC:Checking'
1452
+ description: 'Account name',
1453
+ example: 'Assets:Bank:Checking'
886
1454
  },
887
- type: {
1455
+ balance: {
888
1456
  type: 'string',
889
- description: 'Account type in Beancount hierarchy',
890
- enum: ['Assets', 'Liabilities', 'Income', 'Expenses', 'Equity'],
891
- example: 'Assets'
1457
+ description: 'Balance amount (decimal string for precision)',
1458
+ example: '12345.67'
892
1459
  },
893
- i18nKey: {
1460
+ currency: {
894
1461
  type: 'string',
895
- description: 'i18n key for localized display name',
896
- example: 'account.assets.cn.bank.icbc.checking'
1462
+ description: 'Currency code',
1463
+ example: 'USD'
897
1464
  },
898
- description: {
1465
+ date: {
899
1466
  type: 'string',
900
- description: 'Account description',
901
- example: 'ICBC checking account for daily transactions'
902
- },
903
- tags: {
904
- description: 'Account tags for categorization',
905
- example: ['bank', 'checking', 'primary'],
906
- type: 'array',
907
- items: {
908
- type: 'string'
909
- }
910
- },
911
- icon: {
912
- type: 'string',
913
- description: 'Icon identifier for UI display',
914
- example: 'bank-icbc'
915
- }
916
- },
917
- required: ['path', 'type']
918
- } as const;
919
-
920
- export const $AccountStandardListResponseDto = {
921
- type: 'object',
922
- properties: {
923
- items: {
924
- description: 'Array of account templates',
925
- type: 'array',
926
- items: {
927
- $ref: '#/components/schemas/AccountStandardResponseDto'
928
- }
929
- },
930
- total: {
931
- type: 'number',
932
- description: 'Total number of account templates',
933
- example: 150
934
- },
935
- region: {
936
- type: 'string',
937
- description: 'Region code',
938
- example: 'CN'
939
- }
940
- },
941
- required: ['items', 'total', 'region']
942
- } as const;
943
-
944
- export const $RegionConfigDto = {
945
- type: 'object',
946
- properties: {
947
- currency: {
948
- type: 'string',
949
- example: 'EUR'
950
- },
951
- dateFormat: {
952
- type: 'string',
953
- example: 'DD.MM.YYYY'
954
- },
955
- locale: {
956
- type: 'string',
957
- example: 'de-DE'
958
- }
959
- },
960
- required: ['currency', 'dateFormat', 'locale']
961
- } as const;
962
-
963
- export const $RegionInfoDto = {
964
- type: 'object',
965
- properties: {
966
- code: {
967
- type: 'string',
968
- example: 'de'
969
- },
970
- displayName: {
971
- type: 'string',
972
- example: 'Germany'
973
- },
974
- parent: {
975
- type: 'string',
976
- example: 'eu-core'
977
- },
978
- chain: {
979
- example: ['eu-core', 'de'],
980
- type: 'array',
981
- items: {
982
- type: 'string'
983
- }
984
- },
985
- config: {
986
- $ref: '#/components/schemas/RegionConfigDto'
987
- }
988
- },
989
- required: ['code', 'displayName', 'chain', 'config']
990
- } as const;
991
-
992
- export const $RegionsMetadataResponseDto = {
993
- type: 'object',
994
- properties: {
995
- regions: {
996
- type: 'array',
997
- items: {
998
- $ref: '#/components/schemas/RegionInfoDto'
999
- }
1000
- }
1001
- },
1002
- required: ['regions']
1003
- } as const;
1004
-
1005
- export const $BalanceResponseDto = {
1006
- type: 'object',
1007
- properties: {
1008
- account: {
1009
- type: 'string',
1010
- description: 'Account name',
1011
- example: 'Assets:Bank:Checking'
1012
- },
1013
- balance: {
1014
- type: 'string',
1015
- description: 'Balance amount (decimal string for precision)',
1016
- example: '12345.67'
1017
- },
1018
- currency: {
1019
- type: 'string',
1020
- description: 'Currency code',
1021
- example: 'USD'
1022
- },
1023
- date: {
1024
- type: 'string',
1025
- description: 'Date of the balance calculation (ISO 8601)',
1026
- example: '2024-12-31T00:00:00.000Z'
1027
- }
1028
- },
1029
- required: ['account', 'balance', 'currency', 'date']
1030
- } as const;
1031
-
1032
- export const $MultiCurrencyBalanceResponseDto = {
1033
- type: 'object',
1034
- properties: {
1035
- account: {
1036
- type: 'string',
1037
- description: 'Account name',
1038
- example: 'Assets:Bank:Checking'
1467
+ description: 'Date of the balance calculation (ISO 8601)',
1468
+ example: '2024-12-31T00:00:00.000Z'
1469
+ }
1470
+ },
1471
+ required: ['account', 'balance', 'currency', 'date']
1472
+ } as const;
1473
+
1474
+ export const $MultiCurrencyBalanceResponseDto = {
1475
+ type: 'object',
1476
+ properties: {
1477
+ account: {
1478
+ type: 'string',
1479
+ description: 'Account name',
1480
+ example: 'Assets:Bank:Checking'
1039
1481
  },
1040
1482
  balances: {
1041
1483
  type: 'object',
@@ -1095,8 +1537,8 @@ export const $TransactionSummaryDto = {
1095
1537
  },
1096
1538
  sourceType: {
1097
1539
  type: 'string',
1098
- description: 'Source type (NLP, CSV, OCR, API)',
1099
- enum: ['NLP', 'CSV', 'OCR', 'API']
1540
+ description:
1541
+ 'Source type (free-form string from transaction metadata, e.g. import, api)'
1100
1542
  },
1101
1543
  sourcePlatform: {
1102
1544
  type: 'string',
@@ -1136,8 +1578,10 @@ export const $ReviewSummaryDto = {
1136
1578
  },
1137
1579
  confidenceLevel: {
1138
1580
  type: 'string',
1139
- description: 'Confidence level derived from score',
1140
- enum: ['HIGH', 'MEDIUM', 'LOW']
1581
+ description:
1582
+ 'Confidence level derived from score. Null for error-type reviews (ACCOUNT_VALIDATION/PIPELINE_ERROR) which carry no confidence.',
1583
+ enum: ['HIGH', 'MEDIUM', 'LOW'],
1584
+ nullable: true
1141
1585
  },
1142
1586
  summaryKey: {
1143
1587
  type: 'string',
@@ -1161,7 +1605,8 @@ export const $ReviewSummaryDto = {
1161
1605
  },
1162
1606
  sourceType: {
1163
1607
  type: 'string',
1164
- description: 'Source type (NLP, CSV, OCR, API)'
1608
+ description:
1609
+ 'Source type (free-form string from transaction metadata, e.g. import, api)'
1165
1610
  },
1166
1611
  sourcePlatform: {
1167
1612
  type: 'string',
@@ -1271,7 +1716,20 @@ export const $DecisionOptionDto = {
1271
1716
  properties: {
1272
1717
  value: {
1273
1718
  type: 'string',
1274
- description: 'The action value to submit (e.g., UPGRADE_REPLACE, ACCEPT)'
1719
+ description: 'The action value to submit (e.g., UPGRADE_REPLACE, ACCEPT)',
1720
+ enum: [
1721
+ 'UPGRADE_REPLACE',
1722
+ 'LINK_KEEP_BOTH',
1723
+ 'IGNORE_NEW',
1724
+ 'CONFIRM_DIFFERENT',
1725
+ 'ACCEPT',
1726
+ 'REJECT',
1727
+ 'ACCEPT_AND_LEARN',
1728
+ 'CHOOSE_OTHER',
1729
+ 'CANCEL',
1730
+ 'FIX',
1731
+ 'IGNORE'
1732
+ ]
1275
1733
  },
1276
1734
  labelKey: {
1277
1735
  type: 'string',
@@ -1319,8 +1777,10 @@ export const $ReviewDetailDto = {
1319
1777
  },
1320
1778
  confidenceLevel: {
1321
1779
  type: 'string',
1322
- description: 'Confidence level derived from score',
1323
- enum: ['HIGH', 'MEDIUM', 'LOW']
1780
+ description:
1781
+ 'Confidence level derived from score. Null for error-type reviews (ACCOUNT_VALIDATION/PIPELINE_ERROR) which carry no confidence.',
1782
+ enum: ['HIGH', 'MEDIUM', 'LOW'],
1783
+ nullable: true
1324
1784
  },
1325
1785
  summaryKey: {
1326
1786
  type: 'string',
@@ -1344,7 +1804,8 @@ export const $ReviewDetailDto = {
1344
1804
  },
1345
1805
  sourceType: {
1346
1806
  type: 'string',
1347
- description: 'Source type (NLP, CSV, OCR, API)'
1807
+ description:
1808
+ 'Source type (free-form string from transaction metadata, e.g. import, api)'
1348
1809
  },
1349
1810
  sourcePlatform: {
1350
1811
  type: 'string',
@@ -1418,131 +1879,334 @@ export const $ReviewDetailDto = {
1418
1879
  ]
1419
1880
  } as const;
1420
1881
 
1421
- export const $PayeeResponseDto = {
1882
+ export const $ResolveReviewDto = {
1422
1883
  type: 'object',
1423
1884
  properties: {
1424
- id: {
1885
+ action: {
1425
1886
  type: 'string',
1426
- description: 'Unique identifier (UUID)',
1427
- example: 'uuid-123-456'
1887
+ description:
1888
+ 'Decision action. Valid actions vary by review type — see DecisionOptionDto.value returned by the review detail endpoint.',
1889
+ enum: [
1890
+ 'UPGRADE_REPLACE',
1891
+ 'LINK_KEEP_BOTH',
1892
+ 'IGNORE_NEW',
1893
+ 'CONFIRM_DIFFERENT',
1894
+ 'ACCEPT',
1895
+ 'REJECT',
1896
+ 'ACCEPT_AND_LEARN',
1897
+ 'CHOOSE_OTHER',
1898
+ 'CANCEL',
1899
+ 'FIX',
1900
+ 'IGNORE'
1901
+ ],
1902
+ example: 'ACCEPT'
1428
1903
  },
1429
- userId: {
1430
- type: 'string',
1431
- description: 'User ID (owner of this payee mapping)',
1432
- example: 'user-123'
1904
+ data: {
1905
+ type: 'object',
1906
+ description:
1907
+ 'Additional data for the decision (e.g., selected account ID)',
1908
+ example: {
1909
+ accountId: 'acc-123'
1910
+ }
1911
+ }
1912
+ },
1913
+ required: ['action']
1914
+ } as const;
1915
+
1916
+ export const $ResolveResultDto = {
1917
+ type: 'object',
1918
+ properties: {
1919
+ success: {
1920
+ type: 'boolean',
1921
+ description: 'Whether resolution was successful'
1433
1922
  },
1434
- payee: {
1923
+ messageKey: {
1435
1924
  type: 'string',
1436
- description: "User's original payee name (e.g., 'Starbucks', 'McDonald')",
1437
- example: 'Starbucks'
1438
- },
1439
- payeeProfileId: {
1440
- type: 'object',
1441
1925
  description:
1442
- 'Reference to global PayeeProfile (merchant info, i18n keys, categories)',
1443
- example: 'uuid-789',
1444
- nullable: true
1926
+ 'i18n message key for result message (e.g., review.payee.result.mapped)'
1445
1927
  },
1446
- customCategory: {
1928
+ messageParams: {
1447
1929
  type: 'object',
1448
1930
  description:
1449
- "User's custom category (overrides PayeeProfile category if set)",
1450
- example: 'Dining:Coffee',
1451
- nullable: true
1452
- },
1453
- customTags: {
1454
- description: "User's custom tags (e.g., ['favorite', 'work_meal'])",
1455
- example: ['favorite', 'work_meal'],
1456
- type: 'array',
1457
- items: {
1931
+ 'Parameters for message interpolation (e.g., { name: "PayeeName" })',
1932
+ additionalProperties: {
1458
1933
  type: 'string'
1459
1934
  }
1460
1935
  },
1461
- useCount: {
1462
- type: 'number',
1463
- description:
1464
- 'Usage count (number of times this payee was used in transactions)',
1465
- example: 42
1466
- },
1467
- lastUsedAt: {
1468
- format: 'date-time',
1936
+ resolutionId: {
1469
1937
  type: 'string',
1470
- description: 'Last used timestamp',
1471
- example: '2024-11-20T10:00:00Z'
1472
- },
1473
- meta: {
1474
- type: 'object',
1475
- description: 'Extended metadata (location, notes, contact info, etc.)',
1476
- example: {
1477
- location: 'Zhongguancun',
1478
- note: 'Near subway station',
1479
- favorite: true
1480
- }
1938
+ description:
1939
+ 'Resolution ID for undo. Absent when the resolver rejected the decision (review stayed PENDING).'
1481
1940
  },
1482
- isActive: {
1941
+ canUndo: {
1483
1942
  type: 'boolean',
1484
- description: 'Active status (inactive payees hidden from autocomplete)',
1485
- example: true
1943
+ description: 'Whether this decision can be undone'
1486
1944
  },
1487
- createdAt: {
1945
+ undoDeadline: {
1488
1946
  format: 'date-time',
1489
1947
  type: 'string',
1490
- description: 'Creation timestamp (first time this payee was used)',
1491
- example: '2024-01-01T10:00:00Z'
1948
+ description: 'Deadline for undo (24h from resolution)'
1492
1949
  },
1493
- updatedAt: {
1494
- format: 'date-time',
1950
+ learnedRuleId: {
1495
1951
  type: 'string',
1496
- description: 'Last update timestamp',
1497
- example: '2024-11-20T10:00:00Z'
1952
+ description:
1953
+ 'Rule ID if learning was triggered (ACCEPT_AND_LEARN actions). Use this to deep-link to the rule management page.',
1954
+ example: 'rule_01HXK5V8N2M3P4Q5R6S7T8U9V0'
1498
1955
  }
1499
1956
  },
1500
- required: [
1501
- 'id',
1502
- 'userId',
1503
- 'payee',
1504
- 'customTags',
1505
- 'useCount',
1506
- 'lastUsedAt',
1507
- 'meta',
1508
- 'isActive',
1509
- 'createdAt',
1510
- 'updatedAt'
1511
- ]
1957
+ required: ['success']
1512
1958
  } as const;
1513
1959
 
1514
- export const $PayeeListResponseDto = {
1960
+ export const $UndoResultDto = {
1515
1961
  type: 'object',
1516
1962
  properties: {
1517
- items: {
1518
- description: 'List of payees',
1519
- type: 'array',
1520
- items: {
1521
- $ref: '#/components/schemas/PayeeResponseDto'
1522
- }
1963
+ success: {
1964
+ type: 'boolean',
1965
+ description: 'Whether undo was successful'
1523
1966
  },
1524
- total: {
1525
- type: 'number',
1526
- description: 'Total number of payees',
1527
- example: 42
1967
+ message: {
1968
+ type: 'string',
1969
+ description: 'Message'
1970
+ },
1971
+ reviewId: {
1972
+ type: 'string',
1973
+ description: 'Review item ID that was restored'
1528
1974
  }
1529
1975
  },
1530
- required: ['items', 'total']
1976
+ required: ['success', 'reviewId']
1531
1977
  } as const;
1532
1978
 
1533
- export const $PayeeAutocompleteResponseDto = {
1979
+ export const $BatchResolveDto = {
1534
1980
  type: 'object',
1535
1981
  properties: {
1536
- suggestions: {
1537
- description: 'List of matching payee names',
1538
- example: ['Starbucks', 'Starbucks Coffee', 'Starbucks Reserve'],
1982
+ reviewIds: {
1983
+ description: 'Review item IDs to resolve',
1984
+ example: ['review-1', 'review-2', 'review-3'],
1539
1985
  type: 'array',
1540
1986
  items: {
1541
1987
  type: 'string'
1542
1988
  }
1543
- }
1544
- },
1545
- required: ['suggestions']
1989
+ },
1990
+ action: {
1991
+ type: 'string',
1992
+ description: 'Decision action to apply to all items',
1993
+ enum: [
1994
+ 'UPGRADE_REPLACE',
1995
+ 'LINK_KEEP_BOTH',
1996
+ 'IGNORE_NEW',
1997
+ 'CONFIRM_DIFFERENT',
1998
+ 'ACCEPT',
1999
+ 'REJECT',
2000
+ 'ACCEPT_AND_LEARN',
2001
+ 'CHOOSE_OTHER',
2002
+ 'CANCEL',
2003
+ 'FIX',
2004
+ 'IGNORE'
2005
+ ],
2006
+ example: 'ACCEPT'
2007
+ },
2008
+ data: {
2009
+ type: 'object',
2010
+ description: 'Additional data for the decision'
2011
+ }
2012
+ },
2013
+ required: ['reviewIds', 'action']
2014
+ } as const;
2015
+
2016
+ export const $BatchResolveResultDto = {
2017
+ type: 'object',
2018
+ properties: {
2019
+ successCount: {
2020
+ type: 'number',
2021
+ description: 'Number of successfully resolved items'
2022
+ },
2023
+ failedCount: {
2024
+ type: 'number',
2025
+ description: 'Number of failed items'
2026
+ },
2027
+ results: {
2028
+ description: 'Details for each item',
2029
+ type: 'array',
2030
+ items: {
2031
+ type: 'string'
2032
+ }
2033
+ }
2034
+ },
2035
+ required: ['successCount', 'failedCount', 'results']
2036
+ } as const;
2037
+
2038
+ export const $CreatePayeeDto = {
2039
+ type: 'object',
2040
+ properties: {
2041
+ payee: {
2042
+ type: 'string',
2043
+ description:
2044
+ "User's original payee name (e.g., 'Starbucks', 'McDonald'). This is the raw payee string as entered by the user.",
2045
+ example: 'Starbucks',
2046
+ maxLength: 200
2047
+ },
2048
+ payeeProfileId: {
2049
+ type: 'string',
2050
+ description:
2051
+ 'Optional reference to global PayeeProfile for standardized data (merchant info, i18n keys, categories)',
2052
+ example: 'uuid-123',
2053
+ format: 'uuid'
2054
+ },
2055
+ customCategory: {
2056
+ type: 'string',
2057
+ description:
2058
+ "User's custom category for this payee (overrides PayeeProfile category)",
2059
+ example: 'Dining:Coffee',
2060
+ maxLength: 100
2061
+ },
2062
+ customTags: {
2063
+ description:
2064
+ "User's custom tags for this payee (e.g., ['favorite', 'work_meal'])",
2065
+ example: ['favorite', 'work_meal'],
2066
+ type: 'array',
2067
+ items: {
2068
+ type: 'string'
2069
+ }
2070
+ },
2071
+ meta: {
2072
+ type: 'object',
2073
+ description:
2074
+ 'Metadata for extended information (location, notes, contact info, etc.)',
2075
+ example: {
2076
+ location: 'Zhongguancun',
2077
+ note: 'Near subway station',
2078
+ favorite: true
2079
+ }
2080
+ }
2081
+ },
2082
+ required: ['payee']
2083
+ } as const;
2084
+
2085
+ export const $PayeeResponseDto = {
2086
+ type: 'object',
2087
+ properties: {
2088
+ id: {
2089
+ type: 'string',
2090
+ description: 'Unique identifier (UUID)',
2091
+ example: 'uuid-123-456'
2092
+ },
2093
+ userId: {
2094
+ type: 'string',
2095
+ description: 'User ID (owner of this payee mapping)',
2096
+ example: 'user-123'
2097
+ },
2098
+ payee: {
2099
+ type: 'string',
2100
+ description: "User's original payee name (e.g., 'Starbucks', 'McDonald')",
2101
+ example: 'Starbucks'
2102
+ },
2103
+ payeeProfileId: {
2104
+ type: 'string',
2105
+ description:
2106
+ 'Reference to global PayeeProfile (merchant info, i18n keys, categories)',
2107
+ example: 'uuid-789',
2108
+ nullable: true
2109
+ },
2110
+ customCategory: {
2111
+ type: 'string',
2112
+ description:
2113
+ "User's custom category (overrides PayeeProfile category if set)",
2114
+ example: 'Dining:Coffee',
2115
+ nullable: true
2116
+ },
2117
+ customTags: {
2118
+ description: "User's custom tags (e.g., ['favorite', 'work_meal'])",
2119
+ example: ['favorite', 'work_meal'],
2120
+ type: 'array',
2121
+ items: {
2122
+ type: 'string'
2123
+ }
2124
+ },
2125
+ useCount: {
2126
+ type: 'number',
2127
+ description:
2128
+ 'Usage count (number of times this payee was used in transactions)',
2129
+ example: 42
2130
+ },
2131
+ lastUsedAt: {
2132
+ format: 'date-time',
2133
+ type: 'string',
2134
+ description: 'Last used timestamp',
2135
+ example: '2024-11-20T10:00:00Z'
2136
+ },
2137
+ meta: {
2138
+ type: 'object',
2139
+ description: 'Extended metadata (location, notes, contact info, etc.)',
2140
+ example: {
2141
+ location: 'Zhongguancun',
2142
+ note: 'Near subway station',
2143
+ favorite: true
2144
+ }
2145
+ },
2146
+ isActive: {
2147
+ type: 'boolean',
2148
+ description: 'Active status (inactive payees hidden from autocomplete)',
2149
+ example: true
2150
+ },
2151
+ createdAt: {
2152
+ format: 'date-time',
2153
+ type: 'string',
2154
+ description: 'Creation timestamp (first time this payee was used)',
2155
+ example: '2024-01-01T10:00:00Z'
2156
+ },
2157
+ updatedAt: {
2158
+ format: 'date-time',
2159
+ type: 'string',
2160
+ description: 'Last update timestamp',
2161
+ example: '2024-11-20T10:00:00Z'
2162
+ }
2163
+ },
2164
+ required: [
2165
+ 'id',
2166
+ 'userId',
2167
+ 'payee',
2168
+ 'customTags',
2169
+ 'useCount',
2170
+ 'lastUsedAt',
2171
+ 'meta',
2172
+ 'isActive',
2173
+ 'createdAt',
2174
+ 'updatedAt'
2175
+ ]
2176
+ } as const;
2177
+
2178
+ export const $PayeeListResponseDto = {
2179
+ type: 'object',
2180
+ properties: {
2181
+ items: {
2182
+ description: 'List of payees',
2183
+ type: 'array',
2184
+ items: {
2185
+ $ref: '#/components/schemas/PayeeResponseDto'
2186
+ }
2187
+ },
2188
+ total: {
2189
+ type: 'number',
2190
+ description: 'Total number of payees',
2191
+ example: 42
2192
+ }
2193
+ },
2194
+ required: ['items', 'total']
2195
+ } as const;
2196
+
2197
+ export const $PayeeAutocompleteResponseDto = {
2198
+ type: 'object',
2199
+ properties: {
2200
+ suggestions: {
2201
+ description: 'List of matching payee names',
2202
+ example: ['Starbucks', 'Starbucks Coffee', 'Starbucks Reserve'],
2203
+ type: 'array',
2204
+ items: {
2205
+ type: 'string'
2206
+ }
2207
+ }
2208
+ },
2209
+ required: ['suggestions']
1546
2210
  } as const;
1547
2211
 
1548
2212
  export const $PayeeStatsResponseDto = {
@@ -1568,21 +2232,64 @@ export const $PayeeStatsResponseDto = {
1568
2232
  required: ['payee', 'transactionCount', 'lastUsedAt']
1569
2233
  } as const;
1570
2234
 
1571
- export const $PayeeProfileResponseDto = {
2235
+ export const $UpdatePayeeDto = {
1572
2236
  type: 'object',
1573
2237
  properties: {
1574
- id: {
2238
+ payeeProfileId: {
1575
2239
  type: 'string',
1576
- description: 'Unique identifier (UUID)',
1577
- example: '550e8400-e29b-41d4-a716-446655440000'
2240
+ description:
2241
+ 'Optional reference to global PayeeProfile for standardized data (merchant info, i18n keys, categories)',
2242
+ example: 'uuid-123',
2243
+ format: 'uuid'
2244
+ },
2245
+ customCategory: {
2246
+ type: 'string',
2247
+ description:
2248
+ "User's custom category for this payee (overrides PayeeProfile category)",
2249
+ example: 'Dining:Coffee',
2250
+ maxLength: 100
2251
+ },
2252
+ customTags: {
2253
+ description:
2254
+ "User's custom tags for this payee (e.g., ['favorite', 'work_meal'])",
2255
+ example: ['favorite', 'work_meal'],
2256
+ type: 'array',
2257
+ items: {
2258
+ type: 'string'
2259
+ }
1578
2260
  },
2261
+ meta: {
2262
+ type: 'object',
2263
+ description:
2264
+ 'Metadata for extended information (location, notes, contact info, etc.). Will merge with existing metadata.',
2265
+ example: {
2266
+ location: 'Zhongguancun',
2267
+ note: 'Updated note',
2268
+ favorite: true
2269
+ }
2270
+ },
2271
+ isActive: {
2272
+ type: 'boolean',
2273
+ description:
2274
+ 'Enable or disable this payee. Disabled payees will not appear in autocomplete suggestions.',
2275
+ example: true
2276
+ }
2277
+ }
2278
+ } as const;
2279
+
2280
+ export const $CreatePayeeProfileDto = {
2281
+ type: 'object',
2282
+ properties: {
1579
2283
  canonical: {
1580
2284
  type: 'string',
1581
- description: 'Canonical payee name (unique, case-insensitive)',
1582
- example: 'Starbucks'
2285
+ description:
2286
+ 'Canonical payee name (unique, case-insensitive). This is the primary identifier for the payee.',
2287
+ example: 'Starbucks',
2288
+ maxLength: 200
1583
2289
  },
1584
2290
  aliases: {
1585
- description: 'Multi-language aliases',
2291
+ description:
2292
+ 'Multi-language aliases for the payee. Used for matching user input in different languages.',
1586
2293
  example: ['Starbucks Coffee', 'SBUX'],
1587
2294
  type: 'array',
1588
2295
  items: {
@@ -1590,14 +2297,15 @@ export const $PayeeProfileResponseDto = {
1590
2297
  }
1591
2298
  },
1592
2299
  i18nKey: {
1593
- type: 'object',
1594
- description: 'Translation key for i18n',
2300
+ type: 'string',
2301
+ description:
2302
+ 'Translation key for i18n integration (XLIFF translation system)',
1595
2303
  example: 'payee.starbucks',
1596
- nullable: true
2304
+ maxLength: 100
1597
2305
  },
1598
2306
  category: {
1599
2307
  type: 'string',
1600
- description: 'Payee category',
2308
+ description: 'Payee category classification',
1601
2309
  enum: [
1602
2310
  'RESTAURANT',
1603
2311
  'CAFE',
@@ -1626,13 +2334,14 @@ export const $PayeeProfileResponseDto = {
1626
2334
  example: 'CAFE'
1627
2335
  },
1628
2336
  subCategory: {
1629
- type: 'object',
1630
- description: 'Sub-category',
2337
+ type: 'string',
2338
+ description: 'Sub-category for more specific classification',
1631
2339
  example: 'coffee_chain',
1632
- nullable: true
2340
+ maxLength: 100
1633
2341
  },
1634
2342
  countries: {
1635
- description: 'Country codes where payee operates',
2343
+ description:
2344
+ 'Country/region codes where the payee operates (ISO 3166-1 alpha-2)',
1636
2345
  example: ['CN', 'US', 'JP'],
1637
2346
  type: 'array',
1638
2347
  items: {
@@ -1640,33 +2349,153 @@ export const $PayeeProfileResponseDto = {
1640
2349
  }
1641
2350
  },
1642
2351
  primaryCountry: {
1643
- type: 'object',
1644
- description: 'Primary operating country',
2352
+ type: 'string',
2353
+ description: 'Primary operating country (ISO 3166-1 alpha-2)',
1645
2354
  example: 'US',
1646
- nullable: true
2355
+ maxLength: 2
1647
2356
  },
1648
2357
  keywords: {
1649
- description: 'Search keywords',
1650
- example: ['coffee', 'cafe'],
2358
+ description: 'Search keywords for fuzzy matching',
2359
+ example: ['coffee', 'cafe', 'drinks'],
1651
2360
  type: 'array',
1652
2361
  items: {
1653
2362
  type: 'string'
1654
2363
  }
1655
2364
  },
1656
2365
  logoUrl: {
1657
- type: 'object',
1658
- description: 'Logo URL',
1659
- example: 'https://example.com/logo.png',
1660
- nullable: true
2366
+ type: 'string',
2367
+ description: 'Payee logo URL',
2368
+ example: 'https://example.com/logo.png'
1661
2369
  },
1662
2370
  website: {
2371
+ type: 'string',
2372
+ description: 'Official website URL',
2373
+ example: 'https://www.starbucks.com'
2374
+ },
2375
+ description: {
2376
+ type: 'string',
2377
+ description: 'Payee description',
2378
+ example: 'Global coffeehouse chain headquartered in Seattle',
2379
+ maxLength: 1000
2380
+ },
2381
+ meta: {
1663
2382
  type: 'object',
2383
+ description:
2384
+ 'Extended metadata (business hours, contact info, additional details)',
2385
+ example: {
2386
+ businessHours: '07:00-22:00',
2387
+ phone: '+1-800-782-7282'
2388
+ }
2389
+ },
2390
+ dataSource: {
2391
+ type: 'string',
2392
+ description: 'Data source for this profile',
2393
+ enum: ['MANUAL', 'IMPORT', 'API', 'CROWDSOURCED'],
2394
+ default: 'MANUAL'
2395
+ }
2396
+ },
2397
+ required: ['canonical', 'category']
2398
+ } as const;
2399
+
2400
+ export const $PayeeProfileResponseDto = {
2401
+ type: 'object',
2402
+ properties: {
2403
+ id: {
2404
+ type: 'string',
2405
+ description: 'Unique identifier (UUID)',
2406
+ example: '550e8400-e29b-41d4-a716-446655440000'
2407
+ },
2408
+ canonical: {
2409
+ type: 'string',
2410
+ description: 'Canonical payee name (unique, case-insensitive)',
2411
+ example: 'Starbucks'
2412
+ },
2413
+ aliases: {
2414
+ description: 'Multi-language aliases',
2415
+ example: ['Starbucks Coffee', 'SBUX'],
2416
+ type: 'array',
2417
+ items: {
2418
+ type: 'string'
2419
+ }
2420
+ },
2421
+ i18nKey: {
2422
+ type: 'string',
2423
+ description: 'Translation key for i18n',
2424
+ example: 'payee.starbucks',
2425
+ nullable: true
2426
+ },
2427
+ category: {
2428
+ type: 'string',
2429
+ description: 'Payee category',
2430
+ enum: [
2431
+ 'RESTAURANT',
2432
+ 'CAFE',
2433
+ 'FAST_FOOD',
2434
+ 'BAR',
2435
+ 'SUPERMARKET',
2436
+ 'CONVENIENCE_STORE',
2437
+ 'SHOPPING_MALL',
2438
+ 'ONLINE_SHOPPING',
2439
+ 'TAXI',
2440
+ 'RIDE_SHARING',
2441
+ 'PUBLIC_TRANSPORT',
2442
+ 'PARKING',
2443
+ 'GAS_STATION',
2444
+ 'UTILITIES',
2445
+ 'TELECOM',
2446
+ 'STREAMING',
2447
+ 'HEALTHCARE',
2448
+ 'EDUCATION',
2449
+ 'ENTERTAINMENT',
2450
+ 'SPORTS',
2451
+ 'TRAVEL',
2452
+ 'HOTEL',
2453
+ 'OTHER'
2454
+ ],
2455
+ example: 'CAFE'
2456
+ },
2457
+ subCategory: {
2458
+ type: 'string',
2459
+ description: 'Sub-category',
2460
+ example: 'coffee_chain',
2461
+ nullable: true
2462
+ },
2463
+ countries: {
2464
+ description: 'Country codes where payee operates',
2465
+ example: ['CN', 'US', 'JP'],
2466
+ type: 'array',
2467
+ items: {
2468
+ type: 'string'
2469
+ }
2470
+ },
2471
+ primaryCountry: {
2472
+ type: 'string',
2473
+ description: 'Primary operating country',
2474
+ example: 'US',
2475
+ nullable: true
2476
+ },
2477
+ keywords: {
2478
+ description: 'Search keywords',
2479
+ example: ['coffee', 'cafe'],
2480
+ type: 'array',
2481
+ items: {
2482
+ type: 'string'
2483
+ }
2484
+ },
2485
+ logoUrl: {
2486
+ type: 'string',
2487
+ description: 'Logo URL',
2488
+ example: 'https://example.com/logo.png',
2489
+ nullable: true
2490
+ },
2491
+ website: {
2492
+ type: 'string',
1664
2493
  description: 'Official website',
1665
2494
  example: 'https://www.starbucks.com',
1666
2495
  nullable: true
1667
2496
  },
1668
2497
  description: {
1669
- type: 'object',
2498
+ type: 'string',
1670
2499
  description: 'Description',
1671
2500
  example: 'Global coffeehouse chain',
1672
2501
  nullable: true
@@ -1682,9 +2511,10 @@ export const $PayeeProfileResponseDto = {
1682
2511
  example: 'MANUAL'
1683
2512
  },
1684
2513
  verifiedAt: {
1685
- type: 'object',
2514
+ type: 'string',
1686
2515
  description: 'Verification timestamp (null if not verified)',
1687
2516
  example: '2025-01-01T00:00:00.000Z',
2517
+ format: 'date-time',
1688
2518
  nullable: true
1689
2519
  },
1690
2520
  isActive: {
@@ -1739,6 +2569,164 @@ export const $PayeeProfileListResponseDto = {
1739
2569
  required: ['items', 'total']
1740
2570
  } as const;
1741
2571
 
2572
+ export const $UpdatePayeeProfileDto = {
2573
+ type: 'object',
2574
+ properties: {
2575
+ aliases: {
2576
+ description:
2577
+ 'Multi-language aliases for the payee. Used for matching user input in different languages.',
2578
+ example: ['Starbucks Coffee', 'SBUX'],
2579
+ type: 'array',
2580
+ items: {
2581
+ type: 'string'
2582
+ }
2583
+ },
2584
+ i18nKey: {
2585
+ type: 'string',
2586
+ description:
2587
+ 'Translation key for i18n integration (XLIFF translation system)',
2588
+ example: 'payee.starbucks',
2589
+ maxLength: 100
2590
+ },
2591
+ category: {
2592
+ type: 'string',
2593
+ description: 'Payee category classification',
2594
+ enum: [
2595
+ 'RESTAURANT',
2596
+ 'CAFE',
2597
+ 'FAST_FOOD',
2598
+ 'BAR',
2599
+ 'SUPERMARKET',
2600
+ 'CONVENIENCE_STORE',
2601
+ 'SHOPPING_MALL',
2602
+ 'ONLINE_SHOPPING',
2603
+ 'TAXI',
2604
+ 'RIDE_SHARING',
2605
+ 'PUBLIC_TRANSPORT',
2606
+ 'PARKING',
2607
+ 'GAS_STATION',
2608
+ 'UTILITIES',
2609
+ 'TELECOM',
2610
+ 'STREAMING',
2611
+ 'HEALTHCARE',
2612
+ 'EDUCATION',
2613
+ 'ENTERTAINMENT',
2614
+ 'SPORTS',
2615
+ 'TRAVEL',
2616
+ 'HOTEL',
2617
+ 'OTHER'
2618
+ ],
2619
+ example: 'CAFE'
2620
+ },
2621
+ subCategory: {
2622
+ type: 'string',
2623
+ description: 'Sub-category for more specific classification',
2624
+ example: 'coffee_chain',
2625
+ maxLength: 100
2626
+ },
2627
+ countries: {
2628
+ description:
2629
+ 'Country/region codes where the payee operates (ISO 3166-1 alpha-2)',
2630
+ example: ['CN', 'US', 'JP'],
2631
+ type: 'array',
2632
+ items: {
2633
+ type: 'string'
2634
+ }
2635
+ },
2636
+ primaryCountry: {
2637
+ type: 'string',
2638
+ description: 'Primary operating country (ISO 3166-1 alpha-2)',
2639
+ example: 'US',
2640
+ maxLength: 2
2641
+ },
2642
+ keywords: {
2643
+ description: 'Search keywords for fuzzy matching',
2644
+ example: ['coffee', 'cafe', 'drinks'],
2645
+ type: 'array',
2646
+ items: {
2647
+ type: 'string'
2648
+ }
2649
+ },
2650
+ logoUrl: {
2651
+ type: 'string',
2652
+ description: 'Payee logo URL',
2653
+ example: 'https://example.com/logo.png'
2654
+ },
2655
+ website: {
2656
+ type: 'string',
2657
+ description: 'Official website URL',
2658
+ example: 'https://www.starbucks.com'
2659
+ },
2660
+ description: {
2661
+ type: 'string',
2662
+ description: 'Payee description',
2663
+ example: 'Global coffeehouse chain headquartered in Seattle',
2664
+ maxLength: 1000
2665
+ },
2666
+ meta: {
2667
+ type: 'object',
2668
+ description:
2669
+ 'Extended metadata (business hours, contact info, additional details)',
2670
+ example: {
2671
+ businessHours: '07:00-22:00',
2672
+ phone: '+1-800-782-7282'
2673
+ }
2674
+ },
2675
+ dataSource: {
2676
+ type: 'string',
2677
+ description: 'Data source for this profile',
2678
+ enum: ['MANUAL', 'IMPORT', 'API', 'CROWDSOURCED'],
2679
+ default: 'MANUAL'
2680
+ },
2681
+ isActive: {
2682
+ type: 'boolean',
2683
+ description: 'Whether the payee profile is active (soft delete)',
2684
+ example: true
2685
+ },
2686
+ verifiedAt: {
2687
+ type: 'string',
2688
+ description:
2689
+ 'Verification timestamp. Set to current time to verify, or null to unverify.',
2690
+ example: '2025-01-01T00:00:00.000Z',
2691
+ format: 'date-time',
2692
+ nullable: true
2693
+ }
2694
+ }
2695
+ } as const;
2696
+
2697
+ export const $CreateCommodityDto = {
2698
+ type: 'object',
2699
+ properties: {
2700
+ symbol: {
2701
+ type: 'string',
2702
+ description:
2703
+ 'Commodity symbol (e.g., AAPL, USD, BTC) - corresponds to Beancount currency field',
2704
+ example: 'AAPL',
2705
+ maxLength: 50
2706
+ },
2707
+ date: {
2708
+ type: 'string',
2709
+ description:
2710
+ 'Commodity definition date (ISO 8601, required per Beancount spec). Represents when this commodity was first defined in the accounting system.',
2711
+ example: '2024-01-01',
2712
+ format: 'date'
2713
+ },
2714
+ metadata: {
2715
+ type: 'object',
2716
+ description:
2717
+ 'Metadata (corresponds to Beancount meta field). Can contain name, assetClass, precision, note, tags, etc.',
2718
+ example: {
2719
+ name: 'Apple Inc.',
2720
+ assetClass: 'stock',
2721
+ precision: 2,
2722
+ note: 'Long-term investment',
2723
+ tags: ['tech', 'dividend']
2724
+ }
2725
+ }
2726
+ },
2727
+ required: ['symbol', 'date']
2728
+ } as const;
2729
+
1742
2730
  export const $CommodityResponseDto = {
1743
2731
  type: 'object',
1744
2732
  properties: {
@@ -1748,7 +2736,7 @@ export const $CommodityResponseDto = {
1748
2736
  example: 'uuid-123-456'
1749
2737
  },
1750
2738
  userId: {
1751
- type: 'object',
2739
+ type: 'string',
1752
2740
  description: 'User ID (owner of the commodity)',
1753
2741
  example: 'user-123',
1754
2742
  nullable: true
@@ -1777,13 +2765,6 @@ export const $CommodityResponseDto = {
1777
2765
  source: 'AUTO_CREATED'
1778
2766
  }
1779
2767
  },
1780
- symbolProfileId: {
1781
- type: 'object',
1782
- description:
1783
- 'Reference to SymbolProfile (market data integration, SaaS feature)',
1784
- example: 'uuid-789',
1785
- nullable: true
1786
- },
1787
2768
  createdAt: {
1788
2769
  format: 'date-time',
1789
2770
  type: 'string',
@@ -1819,14 +2800,138 @@ export const $CommodityListResponseDto = {
1819
2800
  required: ['items', 'total']
1820
2801
  } as const;
1821
2802
 
1822
- export const $RecurringRuleResponseDto = {
2803
+ export const $UpdateCommodityDto = {
1823
2804
  type: 'object',
1824
2805
  properties: {
1825
- id: {
2806
+ date: {
1826
2807
  type: 'string',
1827
- description: 'Rule ID'
2808
+ description:
2809
+ 'Commodity definition date (ISO 8601). Represents when this commodity was first defined in the accounting system.',
2810
+ example: '2024-01-01',
2811
+ format: 'date'
1828
2812
  },
1829
- userId: {
2813
+ metadata: {
2814
+ type: 'object',
2815
+ description:
2816
+ 'Metadata (corresponds to Beancount meta field). Will merge with existing metadata. Can contain name, assetClass, precision, note, tags, etc.',
2817
+ example: {
2818
+ name: 'Updated Apple Inc.',
2819
+ assetClass: 'equity',
2820
+ precision: 4,
2821
+ note: 'Updated investment strategy',
2822
+ lastReviewed: '2024-11-03'
2823
+ }
2824
+ }
2825
+ }
2826
+ } as const;
2827
+
2828
+ export const $CreateRecurringRuleDto = {
2829
+ type: 'object',
2830
+ properties: {
2831
+ name: {
2832
+ type: 'string',
2833
+ description: 'Rule name (unique per user)',
2834
+ maxLength: 100
2835
+ },
2836
+ icon: {
2837
+ type: 'string',
2838
+ description: 'Icon emoji',
2839
+ maxLength: 10
2840
+ },
2841
+ frequency: {
2842
+ type: 'string',
2843
+ description: 'Recurring frequency',
2844
+ enum: [
2845
+ 'WEEKLY',
2846
+ 'BIWEEKLY',
2847
+ 'MONTHLY',
2848
+ 'BIMONTHLY',
2849
+ 'QUARTERLY',
2850
+ 'YEARLY',
2851
+ 'CUSTOM'
2852
+ ]
2853
+ },
2854
+ expectedAmount: {
2855
+ type: 'number',
2856
+ description: 'Expected amount (positive number)',
2857
+ minimum: 0
2858
+ },
2859
+ expectedDay: {
2860
+ type: 'number',
2861
+ description: 'Expected day of month (1-31)',
2862
+ minimum: 1,
2863
+ maximum: 31
2864
+ },
2865
+ customIntervalDays: {
2866
+ type: 'number',
2867
+ description: 'Custom interval in days (required for CUSTOM frequency)',
2868
+ minimum: 1
2869
+ },
2870
+ currency: {
2871
+ type: 'string',
2872
+ description: 'Currency code',
2873
+ default: 'CNY',
2874
+ maxLength: 10
2875
+ },
2876
+ matchPayeePattern: {
2877
+ type: 'string',
2878
+ description: 'Payee matching pattern (supports wildcards)',
2879
+ maxLength: 200
2880
+ },
2881
+ matchAmountTolerance: {
2882
+ type: 'number',
2883
+ description: 'Amount tolerance percentage (0-1)',
2884
+ default: 0.075,
2885
+ minimum: 0,
2886
+ maximum: 1
2887
+ },
2888
+ defaultExpenseAccount: {
2889
+ type: 'string',
2890
+ description: 'Default expense account for auto-create',
2891
+ maxLength: 200
2892
+ },
2893
+ defaultPaymentAccount: {
2894
+ type: 'string',
2895
+ description: 'Default payment account for auto-create',
2896
+ maxLength: 200
2897
+ },
2898
+ defaultPayee: {
2899
+ type: 'string',
2900
+ description: 'Default payee for auto-create',
2901
+ maxLength: 200
2902
+ },
2903
+ autoCreate: {
2904
+ type: 'boolean',
2905
+ description: 'Auto-create transaction when expected date arrives',
2906
+ default: false
2907
+ },
2908
+ startDate: {
2909
+ type: 'string',
2910
+ description: 'Rule start date (ISO format)'
2911
+ },
2912
+ endDate: {
2913
+ type: 'string',
2914
+ description: 'Rule end date (ISO format)'
2915
+ }
2916
+ },
2917
+ required: [
2918
+ 'name',
2919
+ 'frequency',
2920
+ 'expectedAmount',
2921
+ 'currency',
2922
+ 'matchAmountTolerance',
2923
+ 'autoCreate'
2924
+ ]
2925
+ } as const;
2926
+
2927
+ export const $RecurringRuleResponseDto = {
2928
+ type: 'object',
2929
+ properties: {
2930
+ id: {
2931
+ type: 'string',
2932
+ description: 'Rule ID'
2933
+ },
2934
+ userId: {
1830
2935
  type: 'string',
1831
2936
  description: 'User ID'
1832
2937
  },
@@ -1930,6 +3035,37 @@ export const $RecurringRuleResponseDto = {
1930
3035
  ]
1931
3036
  } as const;
1932
3037
 
3038
+ export const $CreateRuleFromTransactionDto = {
3039
+ type: 'object',
3040
+ properties: {
3041
+ frequency: {
3042
+ type: 'string',
3043
+ description: 'Recurring frequency',
3044
+ enum: [
3045
+ 'WEEKLY',
3046
+ 'BIWEEKLY',
3047
+ 'MONTHLY',
3048
+ 'BIMONTHLY',
3049
+ 'QUARTERLY',
3050
+ 'YEARLY',
3051
+ 'CUSTOM'
3052
+ ],
3053
+ example: 'MONTHLY'
3054
+ },
3055
+ name: {
3056
+ type: 'string',
3057
+ description: 'Optional name override (default: transaction payee)',
3058
+ maxLength: 100
3059
+ },
3060
+ icon: {
3061
+ type: 'string',
3062
+ description: 'Optional icon emoji',
3063
+ maxLength: 10
3064
+ }
3065
+ },
3066
+ required: ['frequency']
3067
+ } as const;
3068
+
1933
3069
  export const $RecurringRuleWithStatsResponseDto = {
1934
3070
  type: 'object',
1935
3071
  properties: {
@@ -2088,6 +3224,94 @@ export const $RecurringRuleWithStatsResponseDto = {
2088
3224
  ]
2089
3225
  } as const;
2090
3226
 
3227
+ export const $UpdateRecurringRuleDto = {
3228
+ type: 'object',
3229
+ properties: {
3230
+ name: {
3231
+ type: 'string',
3232
+ description: 'Rule name',
3233
+ maxLength: 100
3234
+ },
3235
+ icon: {
3236
+ type: 'string',
3237
+ description: 'Icon emoji',
3238
+ maxLength: 10
3239
+ },
3240
+ frequency: {
3241
+ type: 'string',
3242
+ description: 'Recurring frequency',
3243
+ enum: [
3244
+ 'WEEKLY',
3245
+ 'BIWEEKLY',
3246
+ 'MONTHLY',
3247
+ 'BIMONTHLY',
3248
+ 'QUARTERLY',
3249
+ 'YEARLY',
3250
+ 'CUSTOM'
3251
+ ]
3252
+ },
3253
+ expectedAmount: {
3254
+ type: 'number',
3255
+ description: 'Expected amount',
3256
+ minimum: 0
3257
+ },
3258
+ expectedDay: {
3259
+ type: 'number',
3260
+ description: 'Expected day of month (1-31)',
3261
+ minimum: 1,
3262
+ maximum: 31
3263
+ },
3264
+ customIntervalDays: {
3265
+ type: 'number',
3266
+ description: 'Custom interval in days',
3267
+ minimum: 1
3268
+ },
3269
+ currency: {
3270
+ type: 'string',
3271
+ description: 'Currency code',
3272
+ maxLength: 10
3273
+ },
3274
+ matchPayeePattern: {
3275
+ type: 'string',
3276
+ description: 'Payee matching pattern',
3277
+ maxLength: 200
3278
+ },
3279
+ matchAmountTolerance: {
3280
+ type: 'number',
3281
+ description: 'Amount tolerance percentage (0-1)',
3282
+ minimum: 0,
3283
+ maximum: 1
3284
+ },
3285
+ defaultExpenseAccount: {
3286
+ type: 'string',
3287
+ description: 'Default expense account',
3288
+ maxLength: 200
3289
+ },
3290
+ defaultPaymentAccount: {
3291
+ type: 'string',
3292
+ description: 'Default payment account',
3293
+ maxLength: 200
3294
+ },
3295
+ defaultPayee: {
3296
+ type: 'string',
3297
+ description: 'Default payee',
3298
+ maxLength: 200
3299
+ },
3300
+ autoCreate: {
3301
+ type: 'boolean',
3302
+ description: 'Auto-create transaction'
3303
+ },
3304
+ isActive: {
3305
+ type: 'boolean',
3306
+ description: 'Rule active status'
3307
+ },
3308
+ endDate: {
3309
+ type: 'string',
3310
+ description: 'Rule end date (ISO format)'
3311
+ }
3312
+ }
3313
+ } as const;
3314
+
2091
3315
  export const $ExpectedTransactionRuleDto = {
2092
3316
  type: 'object',
2093
3317
  properties: {
@@ -2204,6 +3428,50 @@ export const $ExpectedTransactionListResponseDto = {
2204
3428
  required: ['items', 'total']
2205
3429
  } as const;
2206
3430
 
3431
+ export const $ConfirmMatchDto = {
3432
+ type: 'object',
3433
+ properties: {
3434
+ transactionId: {
3435
+ type: 'string',
3436
+ description: 'Transaction ID to match with'
3437
+ }
3438
+ },
3439
+ required: ['transactionId']
3440
+ } as const;
3441
+
3442
+ export const $EnterNowDto = {
3443
+ type: 'object',
3444
+ properties: {
3445
+ expenseAccount: {
3446
+ type: 'string',
3447
+ description:
3448
+ 'Override expense account (uses rule default if not provided)',
3449
+ maxLength: 200
3450
+ },
3451
+ paymentAccount: {
3452
+ type: 'string',
3453
+ description:
3454
+ 'Override payment account (uses rule default if not provided)',
3455
+ maxLength: 200
3456
+ },
3457
+ amount: {
3458
+ type: 'number',
3459
+ description: 'Override amount (uses expected amount if not provided)',
3460
+ minimum: 0
3461
+ },
3462
+ payee: {
3463
+ type: 'string',
3464
+ description: 'Override payee (uses rule default if not provided)',
3465
+ maxLength: 200
3466
+ },
3467
+ narration: {
3468
+ type: 'string',
3469
+ description: 'Optional narration',
3470
+ maxLength: 500
3471
+ }
3472
+ }
3473
+ } as const;
3474
+
2207
3475
  export const $ForecastItemDto = {
2208
3476
  type: 'object',
2209
3477
  properties: {
@@ -2228,7 +3496,7 @@ export const $ForecastItemDto = {
2228
3496
  example: '2024-04-01'
2229
3497
  },
2230
3498
  icon: {
2231
- type: 'object',
3499
+ type: 'string',
2232
3500
  description: 'Rule icon emoji',
2233
3501
  example: '🏠',
2234
3502
  nullable: true
@@ -2328,40 +3596,141 @@ export const $ForecastResponseDto = {
2328
3596
  ]
2329
3597
  } as const;
2330
3598
 
2331
- export const $TransactionRuleResponseDto = {
3599
+ export const $CreateTransactionRuleDto = {
2332
3600
  type: 'object',
2333
3601
  properties: {
2334
- id: {
2335
- type: 'string',
2336
- description: 'Rule ID'
2337
- },
2338
3602
  name: {
2339
3603
  type: 'string',
2340
- description: 'Rule name'
3604
+ minLength: 1,
3605
+ maxLength: 100
2341
3606
  },
2342
3607
  description: {
2343
3608
  type: 'string',
2344
- description: 'Rule description'
3609
+ maxLength: 500
2345
3610
  },
2346
3611
  narrationKeywords: {
2347
- description: 'Keywords to match in transaction narration',
2348
- type: 'array',
2349
3612
  items: {
2350
- type: 'string'
2351
- }
3613
+ type: 'array'
3614
+ },
3615
+ maxItems: 50,
3616
+ type: 'array'
2352
3617
  },
2353
3618
  payeeKeywords: {
2354
- description: 'Keywords to match in payee name',
2355
- type: 'array',
2356
3619
  items: {
2357
- type: 'string'
2358
- }
3620
+ type: 'array'
3621
+ },
3622
+ maxItems: 50,
3623
+ type: 'array'
2359
3624
  },
2360
3625
  categoryKeywords: {
2361
- description: 'Keywords to match in category',
2362
- type: 'array',
2363
3626
  items: {
2364
- type: 'string'
3627
+ type: 'array'
3628
+ },
3629
+ maxItems: 50,
3630
+ type: 'array'
3631
+ },
3632
+ methodKeywords: {
3633
+ items: {
3634
+ type: 'array'
3635
+ },
3636
+ maxItems: 50,
3637
+ description: 'Payment method keywords (e.g., HuaBei, YuEBao)',
3638
+ type: 'array'
3639
+ },
3640
+ categoryAccount: {
3641
+ type: 'string',
3642
+ maxLength: 200,
3643
+ description:
3644
+ 'Destination account for expenses/income (e.g., Expenses:Food:Coffee)'
3645
+ },
3646
+ matchLogic: {
3647
+ type: 'string',
3648
+ enum: ['OR', 'AND'],
3649
+ default: 'OR'
3650
+ },
3651
+ amountMin: {
3652
+ type: 'number',
3653
+ minimum: 0,
3654
+ description: 'Minimum transaction amount (inclusive)'
3655
+ },
3656
+ amountMax: {
3657
+ type: 'number',
3658
+ minimum: 0,
3659
+ description: 'Maximum transaction amount (inclusive)'
3660
+ },
3661
+ priority: {
3662
+ type: 'number',
3663
+ default: 50,
3664
+ minimum: 0,
3665
+ maximum: 1000
3666
+ },
3667
+ additionalTags: {
3668
+ items: {
3669
+ type: 'array'
3670
+ },
3671
+ maxItems: 20,
3672
+ type: 'array'
3673
+ },
3674
+ additionalMetadata: {
3675
+ type: 'object'
3676
+ },
3677
+ upsertByPayee: {
3678
+ type: 'boolean',
3679
+ description:
3680
+ 'If true, update existing rule with matching payeeKeywords[0] instead of creating new rule'
3681
+ }
3682
+ },
3683
+ required: ['name', 'matchLogic', 'priority']
3684
+ } as const;
3685
+
3686
+ export const $AmountRangeDto = {
3687
+ type: 'object',
3688
+ properties: {
3689
+ min: {
3690
+ type: 'number',
3691
+ description: 'Minimum amount'
3692
+ },
3693
+ max: {
3694
+ type: 'number',
3695
+ description: 'Maximum amount'
3696
+ }
3697
+ }
3698
+ } as const;
3699
+
3700
+ export const $TransactionRuleResponseDto = {
3701
+ type: 'object',
3702
+ properties: {
3703
+ id: {
3704
+ type: 'string',
3705
+ description: 'Rule ID'
3706
+ },
3707
+ name: {
3708
+ type: 'string',
3709
+ description: 'Rule name'
3710
+ },
3711
+ description: {
3712
+ type: 'string',
3713
+ description: 'Rule description'
3714
+ },
3715
+ narrationKeywords: {
3716
+ description: 'Keywords to match in transaction narration',
3717
+ type: 'array',
3718
+ items: {
3719
+ type: 'string'
3720
+ }
3721
+ },
3722
+ payeeKeywords: {
3723
+ description: 'Keywords to match in payee name',
3724
+ type: 'array',
3725
+ items: {
3726
+ type: 'string'
3727
+ }
3728
+ },
3729
+ categoryKeywords: {
3730
+ description: 'Keywords to match in category',
3731
+ type: 'array',
3732
+ items: {
3733
+ type: 'string'
2365
3734
  }
2366
3735
  },
2367
3736
  methodKeywords: {
@@ -2382,12 +3751,12 @@ export const $TransactionRuleResponseDto = {
2382
3751
  example: 'OR'
2383
3752
  },
2384
3753
  amountRange: {
2385
- type: 'object',
2386
3754
  description: 'Amount range for matching',
2387
- example: {
2388
- min: 0,
2389
- max: 100
2390
- }
3755
+ allOf: [
3756
+ {
3757
+ $ref: '#/components/schemas/AmountRangeDto'
3758
+ }
3759
+ ]
2391
3760
  },
2392
3761
  priority: {
2393
3762
  type: 'number',
@@ -2402,6 +3771,7 @@ export const $TransactionRuleResponseDto = {
2402
3771
  type: 'string',
2403
3772
  description: 'Learning source: NLP, REVIEW_CENTER, or null for manual',
2404
3773
  enum: ['NLP', 'REVIEW_CENTER'],
3774
+ nullable: true,
2405
3775
  example: 'REVIEW_CENTER'
2406
3776
  },
2407
3777
  autoApplyEnabled: {
@@ -2423,7 +3793,9 @@ export const $TransactionRuleResponseDto = {
2423
3793
  additionalMetadata: {
2424
3794
  type: 'object',
2425
3795
  description: 'Additional metadata',
2426
- example: {}
3796
+ additionalProperties: {
3797
+ type: 'string'
3798
+ }
2427
3799
  },
2428
3800
  createdAt: {
2429
3801
  format: 'date-time',
@@ -2596,6 +3968,64 @@ export const $ValidateRuleResponseDto = {
2596
3968
  required: ['valid', 'errors', 'warnings']
2597
3969
  } as const;
2598
3970
 
3971
+ export const $BulkCreateRulesDto = {
3972
+ type: 'object',
3973
+ properties: {
3974
+ rules: {
3975
+ items: {
3976
+ type: 'array'
3977
+ },
3978
+ description: 'Array of rules to import',
3979
+ type: 'array'
3980
+ },
3981
+ conflictStrategy: {
3982
+ type: 'string',
3983
+ enum: ['replace', 'skip'],
3984
+ default: 'skip',
3985
+ description:
3986
+ 'Conflict handling strategy: skip (default) ignores duplicates, replace soft-deletes existing rule'
3987
+ }
3988
+ },
3989
+ required: ['rules', 'conflictStrategy']
3990
+ } as const;
3991
+
3992
+ export const $BulkCreateRulesResponseDto = {
3993
+ type: 'object',
3994
+ properties: {
3995
+ successCount: {
3996
+ type: 'number',
3997
+ description: 'Number of successfully created rules'
3998
+ },
3999
+ failureCount: {
4000
+ type: 'number',
4001
+ description: 'Number of failed rules'
4002
+ },
4003
+ errors: {
4004
+ type: 'array',
4005
+ description: 'Error details for failed rules',
4006
+ items: {
4007
+ type: 'object',
4008
+ properties: {
4009
+ index: {
4010
+ type: 'number'
4011
+ },
4012
+ message: {
4013
+ type: 'string'
4014
+ }
4015
+ }
4016
+ }
4017
+ },
4018
+ createdRuleIds: {
4019
+ description: 'IDs of successfully created rules',
4020
+ type: 'array',
4021
+ items: {
4022
+ type: 'string'
4023
+ }
4024
+ }
4025
+ },
4026
+ required: ['successCount', 'failureCount', 'errors', 'createdRuleIds']
4027
+ } as const;
4028
+
2599
4029
  export const $ExportRulesResponseDto = {
2600
4030
  type: 'object',
2601
4031
  properties: {
@@ -2676,6 +4106,89 @@ export const $RuleStatisticsResponseDto = {
2676
4106
  ]
2677
4107
  } as const;
2678
4108
 
4109
+ export const $UpdateTransactionRuleDto = {
4110
+ type: 'object',
4111
+ properties: {
4112
+ name: {
4113
+ type: 'string',
4114
+ minLength: 1,
4115
+ maxLength: 100
4116
+ },
4117
+ description: {
4118
+ type: 'string',
4119
+ maxLength: 500
4120
+ },
4121
+ narrationKeywords: {
4122
+ items: {
4123
+ type: 'array'
4124
+ },
4125
+ maxItems: 50,
4126
+ type: 'array'
4127
+ },
4128
+ payeeKeywords: {
4129
+ items: {
4130
+ type: 'array'
4131
+ },
4132
+ maxItems: 50,
4133
+ type: 'array'
4134
+ },
4135
+ categoryKeywords: {
4136
+ items: {
4137
+ type: 'array'
4138
+ },
4139
+ maxItems: 50,
4140
+ type: 'array'
4141
+ },
4142
+ methodKeywords: {
4143
+ items: {
4144
+ type: 'array'
4145
+ },
4146
+ maxItems: 50,
4147
+ description: 'Payment method keywords (e.g., HuaBei, YuEBao)',
4148
+ type: 'array'
4149
+ },
4150
+ categoryAccount: {
4151
+ type: 'string',
4152
+ maxLength: 200,
4153
+ description:
4154
+ 'Destination account for expenses/income (e.g., Expenses:Food:Coffee)'
4155
+ },
4156
+ matchLogic: {
4157
+ type: 'string',
4158
+ enum: ['OR', 'AND']
4159
+ },
4160
+ amountMin: {
4161
+ type: 'number',
4162
+ minimum: 0,
4163
+ description: 'Minimum transaction amount (inclusive)'
4164
+ },
4165
+ amountMax: {
4166
+ type: 'number',
4167
+ minimum: 0,
4168
+ description: 'Maximum transaction amount (inclusive)'
4169
+ },
4170
+ priority: {
4171
+ type: 'number',
4172
+ minimum: 0,
4173
+ maximum: 1000
4174
+ },
4175
+ enabled: {
4176
+ type: 'boolean',
4177
+ description: 'Enable or disable the rule'
4178
+ },
4179
+ additionalTags: {
4180
+ items: {
4181
+ type: 'array'
4182
+ },
4183
+ maxItems: 20,
4184
+ type: 'array'
4185
+ },
4186
+ additionalMetadata: {
4187
+ type: 'object'
4188
+ }
4189
+ }
4190
+ } as const;
4191
+
2679
4192
  export const $TestRuleDto = {
2680
4193
  type: 'object',
2681
4194
  properties: {
@@ -2868,35 +4381,386 @@ export const $UpdateUserSettingDto = {
2868
4381
  }
2869
4382
  } as const;
2870
4383
 
2871
- export const $FileImportDto = {
4384
+ export const $UpdatePropertyDto = {
2872
4385
  type: 'object',
2873
4386
  properties: {
2874
- file: {
4387
+ value: {
2875
4388
  type: 'string',
2876
- format: 'binary',
2877
- description: 'Bill file to import (CSV, PDF, OFX, etc.)',
2878
- example: 'alipay.csv'
4389
+ description: 'Property value'
2879
4390
  }
2880
4391
  },
2881
- required: ['file']
4392
+ required: ['value']
2882
4393
  } as const;
2883
4394
 
2884
- export const $ImportErrorDto = {
4395
+ export const $CreateBeanEventDto = {
2885
4396
  type: 'object',
2886
4397
  properties: {
2887
- index: {
2888
- type: 'number',
2889
- description: 'Index of failed transaction in the file',
2890
- example: 5
4398
+ date: {
4399
+ type: 'string',
4400
+ description: 'Life event date (ISO 8601)',
4401
+ example: '2024-03-15'
2891
4402
  },
2892
- error: {
4403
+ type: {
2893
4404
  type: 'string',
2894
- description: 'Error message',
2895
- example: 'Transaction does not balance: -100 USD != 0'
2896
- }
2897
- },
2898
- required: ['index', 'error']
2899
- } as const;
4405
+ description:
4406
+ 'Life event type (e.g., "employer", "location", "marital-status") user-defined, no enum constraint at engine layer',
4407
+ example: 'employer'
4408
+ },
4409
+ description: {
4410
+ type: 'string',
4411
+ description:
4412
+ 'Life event description. Empty string is a VALID value (distinct from absence).',
4413
+ example: 'Acme Corp'
4414
+ },
4415
+ meta: {
4416
+ type: 'object',
4417
+ description:
4418
+ 'Product-side metadata (lives in BeanEvent.meta JSON, never in engine Event fields)',
4419
+ example: {
4420
+ note: 'Promotion'
4421
+ }
4422
+ }
4423
+ },
4424
+ required: ['date', 'type', 'description']
4425
+ } as const;
4426
+
4427
+ export const $EventResponseDto = {
4428
+ type: 'object',
4429
+ properties: {
4430
+ id: {
4431
+ type: 'string',
4432
+ description: 'Unique identifier',
4433
+ example: 'uuid-123-456'
4434
+ },
4435
+ userId: {
4436
+ type: 'string',
4437
+ description: 'User ID (owner of the life event)',
4438
+ example: 'user-123'
4439
+ },
4440
+ date: {
4441
+ type: 'string',
4442
+ description: 'Life event date (ISO 8601 format)',
4443
+ example: '2024-03-15',
4444
+ format: 'date'
4445
+ },
4446
+ type: {
4447
+ type: 'string',
4448
+ description:
4449
+ 'Life event type (user-defined, e.g., "employer", "location")',
4450
+ example: 'employer'
4451
+ },
4452
+ description: {
4453
+ type: 'string',
4454
+ description:
4455
+ 'Life event description. May be an empty string (a valid value distinct from absence).',
4456
+ example: 'Acme Corp'
4457
+ },
4458
+ meta: {
4459
+ type: 'object',
4460
+ description: 'Product-side metadata (free-form JSON)',
4461
+ example: {
4462
+ note: 'Promotion'
4463
+ }
4464
+ },
4465
+ createdAt: {
4466
+ format: 'date-time',
4467
+ type: 'string',
4468
+ description: 'Creation timestamp',
4469
+ example: '2024-03-15T10:00:00Z'
4470
+ },
4471
+ updatedAt: {
4472
+ format: 'date-time',
4473
+ type: 'string',
4474
+ description:
4475
+ 'Last update timestamp. Also emitted as the ETag response header for If-Match optimistic concurrency.',
4476
+ example: '2024-03-15T10:00:00Z'
4477
+ }
4478
+ },
4479
+ required: [
4480
+ 'id',
4481
+ 'userId',
4482
+ 'date',
4483
+ 'type',
4484
+ 'description',
4485
+ 'meta',
4486
+ 'createdAt',
4487
+ 'updatedAt'
4488
+ ]
4489
+ } as const;
4490
+
4491
+ export const $EventListResponseDto = {
4492
+ type: 'object',
4493
+ properties: {
4494
+ items: {
4495
+ description: 'List of life events',
4496
+ type: 'array',
4497
+ items: {
4498
+ $ref: '#/components/schemas/EventResponseDto'
4499
+ }
4500
+ },
4501
+ total: {
4502
+ type: 'number',
4503
+ description: 'Total number of life events matching the query',
4504
+ example: 42
4505
+ }
4506
+ },
4507
+ required: ['items', 'total']
4508
+ } as const;
4509
+
4510
+ export const $UpdateBeanEventDto = {
4511
+ type: 'object',
4512
+ properties: {
4513
+ date: {
4514
+ type: 'string',
4515
+ description: 'Life event date (ISO 8601)'
4516
+ },
4517
+ type: {
4518
+ type: 'string',
4519
+ description: 'Life event type (user-defined)'
4520
+ },
4521
+ description: {
4522
+ type: 'string',
4523
+ description:
4524
+ 'Life event description. Empty string is a VALID value (distinct from absence).'
4525
+ },
4526
+ meta: {
4527
+ type: 'object',
4528
+ description: 'Product-side metadata (free-form JSON)'
4529
+ }
4530
+ }
4531
+ } as const;
4532
+
4533
+ export const $ActualBalanceDto = {
4534
+ type: 'object',
4535
+ properties: {
4536
+ amount: {
4537
+ type: 'string',
4538
+ description:
4539
+ 'Actual balance amount as a decimal string (preserves precision for tolerance inference).',
4540
+ example: '1234.56'
4541
+ },
4542
+ ccy: {
4543
+ type: 'string',
4544
+ description: 'Currency code (ISO 4217 or commodity ticker).',
4545
+ example: 'CNY'
4546
+ }
4547
+ },
4548
+ required: ['amount', 'ccy']
4549
+ } as const;
4550
+
4551
+ export const $ComputeReconciliationDto = {
4552
+ type: 'object',
4553
+ properties: {
4554
+ accountId: {
4555
+ type: 'string',
4556
+ description: 'BeanAccount id to reconcile.'
4557
+ },
4558
+ asOfDate: {
4559
+ type: 'string',
4560
+ description: 'Assertion date (ISO 8601, e.g. "2026-07-24").',
4561
+ example: '2026-07-24'
4562
+ },
4563
+ actualBalance: {
4564
+ description: 'Actual balance from the external statement.',
4565
+ allOf: [
4566
+ {
4567
+ $ref: '#/components/schemas/ActualBalanceDto'
4568
+ }
4569
+ ]
4570
+ }
4571
+ },
4572
+ required: ['accountId', 'asOfDate', 'actualBalance']
4573
+ } as const;
4574
+
4575
+ export const $ReconciliationComputeResultDto = {
4576
+ type: 'object',
4577
+ properties: {
4578
+ accountId: {
4579
+ type: 'string'
4580
+ },
4581
+ asOfDate: {
4582
+ type: 'string'
4583
+ },
4584
+ bookBalance: {
4585
+ type: 'string',
4586
+ description: 'System-computed book balance (decimal string).'
4587
+ },
4588
+ actualBalance: {
4589
+ type: 'string',
4590
+ description: 'User-entered actual balance (decimal string).'
4591
+ },
4592
+ currency: {
4593
+ type: 'string'
4594
+ },
4595
+ diff: {
4596
+ type: 'string',
4597
+ description: 'Diff = book − actual (decimal string).'
4598
+ },
4599
+ tolerance: {
4600
+ type: 'string',
4601
+ description: 'Applied tolerance (decimal string).'
4602
+ },
4603
+ withinTolerance: {
4604
+ type: 'boolean',
4605
+ description: 'true when |diff| ≤ tolerance.'
4606
+ },
4607
+ suggestedAction: {
4608
+ type: 'string',
4609
+ enum: ['assert', 'pad'],
4610
+ description:
4611
+ 'Suggested next action: assert when within tolerance, pad otherwise.'
4612
+ }
4613
+ },
4614
+ required: [
4615
+ 'accountId',
4616
+ 'asOfDate',
4617
+ 'bookBalance',
4618
+ 'actualBalance',
4619
+ 'currency',
4620
+ 'diff',
4621
+ 'tolerance',
4622
+ 'withinTolerance',
4623
+ 'suggestedAction'
4624
+ ]
4625
+ } as const;
4626
+
4627
+ export const $AssertReconciliationDto = {
4628
+ type: 'object',
4629
+ properties: {
4630
+ accountId: {
4631
+ type: 'string',
4632
+ description: 'BeanAccount id to reconcile.'
4633
+ },
4634
+ asOfDate: {
4635
+ type: 'string',
4636
+ description: 'Assertion date (ISO 8601, e.g. "2026-07-24").',
4637
+ example: '2026-07-24'
4638
+ },
4639
+ actualBalance: {
4640
+ description: 'Actual balance from the external statement.',
4641
+ allOf: [
4642
+ {
4643
+ $ref: '#/components/schemas/ActualBalanceDto'
4644
+ }
4645
+ ]
4646
+ },
4647
+ tolerance: {
4648
+ type: 'string',
4649
+ description:
4650
+ 'Optional explicit tolerance override. Omit to infer from amount precision (Beancount default).',
4651
+ example: '0.01'
4652
+ }
4653
+ },
4654
+ required: ['accountId', 'asOfDate', 'actualBalance']
4655
+ } as const;
4656
+
4657
+ export const $ReconciliationRecordDto = {
4658
+ type: 'object',
4659
+ properties: {
4660
+ id: {
4661
+ type: 'string'
4662
+ },
4663
+ accountId: {
4664
+ type: 'string'
4665
+ },
4666
+ date: {
4667
+ type: 'string'
4668
+ },
4669
+ amount: {
4670
+ type: 'string',
4671
+ description: 'Asserted (actual) amount.'
4672
+ },
4673
+ currency: {
4674
+ type: 'string'
4675
+ },
4676
+ tolerance: {
4677
+ type: 'string'
4678
+ },
4679
+ diffAmount: {
4680
+ type: 'string',
4681
+ description: 'book − actual.'
4682
+ },
4683
+ diffCurrency: {
4684
+ type: 'string'
4685
+ },
4686
+ createdAt: {
4687
+ type: 'string'
4688
+ }
4689
+ },
4690
+ required: ['id', 'accountId', 'date', 'amount', 'currency', 'createdAt']
4691
+ } as const;
4692
+
4693
+ export const $PadReconciliationDto = {
4694
+ type: 'object',
4695
+ properties: {
4696
+ accountId: {
4697
+ type: 'string',
4698
+ description: 'BeanAccount id to reconcile.'
4699
+ },
4700
+ asOfDate: {
4701
+ type: 'string',
4702
+ description: 'Assertion date (ISO 8601, e.g. "2026-07-24").',
4703
+ example: '2026-07-24'
4704
+ },
4705
+ actualBalance: {
4706
+ description: 'Actual balance from the external statement.',
4707
+ allOf: [
4708
+ {
4709
+ $ref: '#/components/schemas/ActualBalanceDto'
4710
+ }
4711
+ ]
4712
+ },
4713
+ sourceAccount: {
4714
+ type: 'string',
4715
+ description:
4716
+ 'Pad source account. Defaults to Equity:Opening-Balances (official Beancount convention).',
4717
+ example: 'Equity:Opening-Balances',
4718
+ default: 'Equity:Opening-Balances'
4719
+ }
4720
+ },
4721
+ required: ['accountId', 'asOfDate', 'actualBalance']
4722
+ } as const;
4723
+
4724
+ export const $PadResultDto = {
4725
+ type: 'object',
4726
+ properties: {
4727
+ transactionId: {
4728
+ type: 'string',
4729
+ description: 'Created pad adjusting transaction id.'
4730
+ }
4731
+ },
4732
+ required: ['transactionId']
4733
+ } as const;
4734
+
4735
+ export const $FileImportDto = {
4736
+ type: 'object',
4737
+ properties: {
4738
+ file: {
4739
+ type: 'string',
4740
+ format: 'binary',
4741
+ description: 'Bill file to import (CSV, PDF, OFX, etc.)',
4742
+ example: 'alipay.csv'
4743
+ }
4744
+ },
4745
+ required: ['file']
4746
+ } as const;
4747
+
4748
+ export const $ImportErrorDto = {
4749
+ type: 'object',
4750
+ properties: {
4751
+ index: {
4752
+ type: 'number',
4753
+ description: 'Index of failed transaction in the file',
4754
+ example: 5
4755
+ },
4756
+ error: {
4757
+ type: 'string',
4758
+ description: 'Error message',
4759
+ example: 'Transaction does not balance: -100 USD != 0'
4760
+ }
4761
+ },
4762
+ required: ['index', 'error']
4763
+ } as const;
2900
4764
 
2901
4765
  export const $ReviewItemPreviewDto = {
2902
4766
  type: 'object',
@@ -3214,18 +5078,193 @@ export const $ImporterConfigDto = {
3214
5078
  ]
3215
5079
  } as const;
3216
5080
 
3217
- export const $ProviderSyncConfigDto = {
5081
+ export const $UpdateMapperDefaultsDto = {
3218
5082
  type: 'object',
3219
5083
  properties: {
3220
5084
  sourceAccount: {
3221
5085
  type: 'string',
3222
- description: 'Source account for the first posting',
3223
- example: 'Assets:Bank:Chase'
5086
+ description: 'Source account for transactions (Beancount format)',
5087
+ example: 'Assets:Alipay:Balance',
5088
+ pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
3224
5089
  },
3225
- defaultCurrency: {
5090
+ currency: {
3226
5091
  type: 'string',
3227
- description: 'Default currency for transactions',
3228
- example: 'USD'
5092
+ description: 'Default currency (ISO 4217 code)',
5093
+ example: 'CNY',
5094
+ minLength: 3,
5095
+ maxLength: 3,
5096
+ pattern: '^[A-Z]{3}$'
5097
+ },
5098
+ expenseAccount: {
5099
+ type: 'string',
5100
+ description: 'Default expense account (optional)',
5101
+ example: 'Expenses:Unknown',
5102
+ pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
5103
+ },
5104
+ incomeAccount: {
5105
+ type: 'string',
5106
+ description: 'Default income account (optional)',
5107
+ example: 'Income:Unknown',
5108
+ pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
5109
+ },
5110
+ methodAccountMapping: {
5111
+ type: 'object',
5112
+ description:
5113
+ 'Payment method to source account mapping. Maps payment method keywords to Beancount account paths. Used by Alipay/WeChat importers to determine sourceAccount based on payment method (e.g., HuaBei, CreditCard).',
5114
+ example: {
5115
+ HuaBei: 'Liabilities:Alipay:Huabei',
5116
+ CreditCard: 'Liabilities:CreditCard'
5117
+ }
5118
+ }
5119
+ }
5120
+ } as const;
5121
+
5122
+ export const $UpdateConfigDataDto = {
5123
+ type: 'object',
5124
+ properties: {
5125
+ defaults: {
5126
+ description: 'Mapper defaults configuration',
5127
+ allOf: [
5128
+ {
5129
+ $ref: '#/components/schemas/UpdateMapperDefaultsDto'
5130
+ }
5131
+ ]
5132
+ }
5133
+ }
5134
+ } as const;
5135
+
5136
+ export const $UpdateImporterConfigDto = {
5137
+ type: 'object',
5138
+ properties: {
5139
+ data: {
5140
+ description: 'Configuration data (v1 schema)',
5141
+ allOf: [
5142
+ {
5143
+ $ref: '#/components/schemas/UpdateConfigDataDto'
5144
+ }
5145
+ ]
5146
+ }
5147
+ }
5148
+ } as const;
5149
+
5150
+ export const $CreatePlatformDto = {
5151
+ type: 'object',
5152
+ properties: {
5153
+ name: {
5154
+ type: 'string',
5155
+ description: 'Platform name',
5156
+ example: 'Binance'
5157
+ },
5158
+ canonical: {
5159
+ type: 'string',
5160
+ description: 'Platform canonical identifier (lowercase, kebab-case)',
5161
+ example: 'binance'
5162
+ },
5163
+ aliases: {
5164
+ description: 'Platform aliases (multi-language names for lookup)',
5165
+ example: ['Binance', 'Binance Exchange', 'BNB'],
5166
+ type: 'array',
5167
+ items: {
5168
+ type: 'string'
5169
+ }
5170
+ },
5171
+ url: {
5172
+ type: 'string',
5173
+ description: 'Platform URL',
5174
+ example: 'https://www.binance.com'
5175
+ },
5176
+ type: {
5177
+ type: 'string',
5178
+ description: 'Platform type',
5179
+ enum: [
5180
+ 'BANK',
5181
+ 'BROKERAGE',
5182
+ 'CRYPTO_EXCHANGE',
5183
+ 'PAYMENT',
5184
+ 'INVESTMENT',
5185
+ 'INSURANCE',
5186
+ 'OTHER'
5187
+ ],
5188
+ example: 'CRYPTO_EXCHANGE'
5189
+ },
5190
+ logoUrl: {
5191
+ type: 'string',
5192
+ description: 'Platform logo URL',
5193
+ example: 'https://example.com/logos/binance.png'
5194
+ },
5195
+ isActive: {
5196
+ type: 'boolean',
5197
+ description: 'Whether the platform is active',
5198
+ default: true
5199
+ }
5200
+ },
5201
+ required: ['name', 'canonical', 'aliases', 'url', 'type']
5202
+ } as const;
5203
+
5204
+ export const $UpdatePlatformDto = {
5205
+ type: 'object',
5206
+ properties: {
5207
+ name: {
5208
+ type: 'string',
5209
+ description: 'Platform name',
5210
+ example: 'Binance'
5211
+ },
5212
+ canonical: {
5213
+ type: 'string',
5214
+ description: 'Platform canonical identifier (lowercase, kebab-case)',
5215
+ example: 'binance'
5216
+ },
5217
+ aliases: {
5218
+ description: 'Platform aliases (multi-language names for lookup)',
5219
+ example: ['Binance', 'Binance Exchange', 'BNB'],
5220
+ type: 'array',
5221
+ items: {
5222
+ type: 'string'
5223
+ }
5224
+ },
5225
+ url: {
5226
+ type: 'string',
5227
+ description: 'Platform URL',
5228
+ example: 'https://www.binance.com'
5229
+ },
5230
+ type: {
5231
+ type: 'string',
5232
+ description: 'Platform type',
5233
+ enum: [
5234
+ 'BANK',
5235
+ 'BROKERAGE',
5236
+ 'CRYPTO_EXCHANGE',
5237
+ 'PAYMENT',
5238
+ 'INVESTMENT',
5239
+ 'INSURANCE',
5240
+ 'OTHER'
5241
+ ],
5242
+ example: 'CRYPTO_EXCHANGE'
5243
+ },
5244
+ logoUrl: {
5245
+ type: 'string',
5246
+ description: 'Platform logo URL',
5247
+ example: 'https://example.com/logos/binance.png'
5248
+ },
5249
+ isActive: {
5250
+ type: 'boolean',
5251
+ description: 'Whether the platform is active'
5252
+ }
5253
+ }
5254
+ } as const;
5255
+
5256
+ export const $ProviderSyncConfigDto = {
5257
+ type: 'object',
5258
+ properties: {
5259
+ sourceAccount: {
5260
+ type: 'string',
5261
+ description: 'Source account for the first posting',
5262
+ example: 'Assets:Bank:Chase'
5263
+ },
5264
+ defaultCurrency: {
5265
+ type: 'string',
5266
+ description: 'Default currency for transactions',
5267
+ example: 'USD'
3229
5268
  },
3230
5269
  defaultExpenseAccount: {
3231
5270
  type: 'string',
@@ -3365,6 +5404,11 @@ export const $ParserTelemetryReportDto = {
3365
5404
  properties: {}
3366
5405
  } as const;
3367
5406
 
5407
+ export const $UncoveredFormatMissDto = {
5408
+ type: 'object',
5409
+ properties: {}
5410
+ } as const;
5411
+
3368
5412
  export const $ProcessNlpDto = {
3369
5413
  type: 'object',
3370
5414
  properties: {
@@ -3379,6 +5423,16 @@ export const $ProcessNlpDto = {
3379
5423
  description:
3380
5424
  'Session ID for multi-turn conversation (auto-generated if not provided)',
3381
5425
  example: 'session_abc123'
5426
+ },
5427
+ parsedData: {
5428
+ type: 'object',
5429
+ description:
5430
+ 'Parsed data from previous NLP response for session recovery. Send back the parsedData received in confirm_payee/confirm responses.',
5431
+ example: {
5432
+ amount: 35,
5433
+ currency: 'CNY',
5434
+ payee: 'Starbucks'
5435
+ }
3382
5436
  }
3383
5437
  },
3384
5438
  required: ['message']
@@ -4157,23 +6211,6 @@ export const $NlpResponseDto = {
4157
6211
  required: ['status', 'action']
4158
6212
  } as const;
4159
6213
 
4160
- export const $BalanceByCurrencyDto = {
4161
- type: 'object',
4162
- properties: {
4163
- currency: {
4164
- type: 'string',
4165
- description: 'ISO 4217 currency code',
4166
- example: 'CNY'
4167
- },
4168
- balance: {
4169
- type: 'string',
4170
- description: 'Balance amount',
4171
- example: '50000.00'
4172
- }
4173
- },
4174
- required: ['currency', 'balance']
4175
- } as const;
4176
-
4177
6214
  export const $NetWorthByCurrencyDto = {
4178
6215
  type: 'object',
4179
6216
  properties: {
@@ -4243,28 +6280,6 @@ export const $ConvertedNetWorthDto = {
4243
6280
  ]
4244
6281
  } as const;
4245
6282
 
4246
- export const $ExchangeRateWarningDto = {
4247
- type: 'object',
4248
- properties: {
4249
- type: {
4250
- type: 'string',
4251
- description: 'Warning type',
4252
- example: 'MISSING_EXCHANGE_RATE'
4253
- },
4254
- currency: {
4255
- type: 'string',
4256
- description: 'Currency without exchange rate',
4257
- example: 'EUR'
4258
- },
4259
- totalAmount: {
4260
- type: 'string',
4261
- description: 'Total amount affected',
4262
- example: '1000.00'
4263
- }
4264
- },
4265
- required: ['type', 'currency', 'totalAmount']
4266
- } as const;
4267
-
4268
6283
  export const $NetWorthResponseDto = {
4269
6284
  type: 'object',
4270
6285
  properties: {
@@ -4366,6 +6381,12 @@ export const $AccountItemDto = {
4366
6381
  type: 'string',
4367
6382
  description: 'Currency code',
4368
6383
  example: 'CNY'
6384
+ },
6385
+ convertedBalance: {
6386
+ type: 'string',
6387
+ description:
6388
+ 'FX-converted balance in base currency; omitted when not convertible',
6389
+ example: '50000.00'
4369
6390
  }
4370
6391
  },
4371
6392
  required: ['id', 'name', 'displayName', 'balance', 'currency']
@@ -4392,11 +6413,66 @@ export const $PlatformGroupDto = {
4392
6413
  },
4393
6414
  totalBalance: {
4394
6415
  type: 'string',
4395
- description: 'Total balance across all accounts in platform',
6416
+ description: 'FX-converted total balance in base currency',
6417
+ example: '100000.00'
6418
+ },
6419
+ balanceByCurrency: {
6420
+ description: 'Raw (unconverted) balances grouped by currency',
6421
+ type: 'array',
6422
+ items: {
6423
+ $ref: '#/components/schemas/BalanceByCurrencyDto'
6424
+ }
6425
+ },
6426
+ convertedBalance: {
6427
+ type: 'string',
6428
+ description:
6429
+ 'Converted balance in base currency (omitted when no currency is convertible)',
4396
6430
  example: '100000.00'
6431
+ },
6432
+ sharePct: {
6433
+ type: 'number',
6434
+ description:
6435
+ 'Share of the grand converted total (0-100); 0 when grand total is 0',
6436
+ example: 42.5
6437
+ }
6438
+ },
6439
+ required: [
6440
+ 'platformId',
6441
+ 'platformName',
6442
+ 'accounts',
6443
+ 'totalBalance',
6444
+ 'balanceByCurrency',
6445
+ 'sharePct'
6446
+ ]
6447
+ } as const;
6448
+
6449
+ export const $AccountExchangeRateWarningDto = {
6450
+ type: 'object',
6451
+ properties: {
6452
+ type: {
6453
+ type: 'string',
6454
+ description: 'Warning type',
6455
+ example: 'MISSING_EXCHANGE_RATE'
6456
+ },
6457
+ currency: {
6458
+ type: 'string',
6459
+ description: 'Currency without exchange rate',
6460
+ example: 'USD'
6461
+ },
6462
+ accounts: {
6463
+ description: 'Affected account paths',
6464
+ type: 'array',
6465
+ items: {
6466
+ type: 'string'
6467
+ }
6468
+ },
6469
+ totalAmount: {
6470
+ type: 'string',
6471
+ description: 'Total amount in this currency',
6472
+ example: '5000.00'
4397
6473
  }
4398
6474
  },
4399
- required: ['platformId', 'platformName', 'accounts', 'totalBalance']
6475
+ required: ['type', 'currency', 'accounts', 'totalAmount']
4400
6476
  } as const;
4401
6477
 
4402
6478
  export const $AccountsSummaryDto = {
@@ -4409,9 +6485,21 @@ export const $AccountsSummaryDto = {
4409
6485
  totalPlatforms: {
4410
6486
  type: 'number',
4411
6487
  description: 'Total number of platforms'
6488
+ },
6489
+ baseCurrency: {
6490
+ type: 'string',
6491
+ description: 'Base currency for conversion',
6492
+ example: 'CNY'
6493
+ },
6494
+ warnings: {
6495
+ description: 'Per-account exchange rate warnings',
6496
+ type: 'array',
6497
+ items: {
6498
+ $ref: '#/components/schemas/AccountExchangeRateWarningDto'
6499
+ }
4412
6500
  }
4413
6501
  },
4414
- required: ['totalAccounts', 'totalPlatforms']
6502
+ required: ['totalAccounts', 'totalPlatforms', 'baseCurrency']
4415
6503
  } as const;
4416
6504
 
4417
6505
  export const $AccountsResponseDto = {
@@ -4463,6 +6551,12 @@ export const $AccountItemWithAssetClassDto = {
4463
6551
  description: 'Currency code',
4464
6552
  example: 'CNY'
4465
6553
  },
6554
+ convertedBalance: {
6555
+ type: 'string',
6556
+ description:
6557
+ 'FX-converted balance in base currency; omitted when not convertible',
6558
+ example: '50000.00'
6559
+ },
4466
6560
  assetClass: {
4467
6561
  type: 'string',
4468
6562
  description: 'Asset class',
@@ -4482,6 +6576,12 @@ export const $AccountItemWithAssetClassDto = {
4482
6576
  type: 'string',
4483
6577
  description: 'Risk level',
4484
6578
  example: 'LOW'
6579
+ },
6580
+ source: {
6581
+ type: 'string',
6582
+ description:
6583
+ 'ADR-0105 classification provenance (holding level always; account level only on FALLBACK)',
6584
+ enum: ['USER_META', 'FIAT_CURRENCY', 'OPENBB_MAPPING', 'FALLBACK']
4485
6585
  }
4486
6586
  },
4487
6587
  required: ['id', 'name', 'displayName', 'balance', 'currency', 'assetClass']
@@ -4536,211 +6636,824 @@ export const $AssetClassGroupDto = {
4536
6636
  required: ['assetClass', 'accounts', 'balanceByCurrency']
4537
6637
  } as const;
4538
6638
 
4539
- export const $AccountExchangeRateWarningDto = {
6639
+ export const $AssetClassSummaryDto = {
4540
6640
  type: 'object',
4541
6641
  properties: {
4542
- type: {
4543
- type: 'string',
4544
- description: 'Warning type',
4545
- example: 'MISSING_EXCHANGE_RATE'
6642
+ totalAccounts: {
6643
+ type: 'number',
6644
+ description: 'Total number of accounts'
4546
6645
  },
4547
- currency: {
4548
- type: 'string',
4549
- description: 'Currency without exchange rate',
4550
- example: 'USD'
6646
+ totalAssetClasses: {
6647
+ type: 'number',
6648
+ description: 'Total number of asset classes'
4551
6649
  },
4552
- accounts: {
4553
- description: 'Affected account paths',
6650
+ baseCurrency: {
6651
+ type: 'string',
6652
+ description: 'Base currency for conversion',
6653
+ example: 'CNY'
6654
+ },
6655
+ warnings: {
6656
+ description: 'Exchange rate warnings',
4554
6657
  type: 'array',
4555
6658
  items: {
4556
- type: 'string'
6659
+ $ref: '#/components/schemas/AccountExchangeRateWarningDto'
4557
6660
  }
4558
6661
  },
4559
- totalAmount: {
4560
- type: 'string',
4561
- description: 'Total amount in this currency',
4562
- example: '5000.00'
6662
+ fallback: {
6663
+ type: 'object',
6664
+ description:
6665
+ 'ADR-0105 §4 fallback provenance stats (holding level only). valueRatio is the grey-area share of total converted value; count is the number of source=FALLBACK holdings.'
4563
6666
  }
4564
6667
  },
4565
- required: ['type', 'currency', 'accounts', 'totalAmount']
6668
+ required: ['totalAccounts', 'totalAssetClasses', 'baseCurrency']
4566
6669
  } as const;
4567
6670
 
4568
- export const $AssetClassSummaryDto = {
6671
+ export const $AssetClassAccountsResponseDto = {
4569
6672
  type: 'object',
4570
6673
  properties: {
4571
- totalAccounts: {
4572
- type: 'number',
4573
- description: 'Total number of accounts'
6674
+ groups: {
6675
+ description: 'Account groups by asset class',
6676
+ type: 'array',
6677
+ items: {
6678
+ $ref: '#/components/schemas/AssetClassGroupDto'
6679
+ }
4574
6680
  },
4575
- totalAssetClasses: {
6681
+ summary: {
6682
+ description: 'Summary statistics',
6683
+ allOf: [
6684
+ {
6685
+ $ref: '#/components/schemas/AssetClassSummaryDto'
6686
+ }
6687
+ ]
6688
+ },
6689
+ uncategorized: {
6690
+ description:
6691
+ 'ADR-0105 §6 holding-level grey-area bucket (source=FALLBACK holdings peeled out of groups). Present only for groupBy=holdingAssetClass when FALLBACK holdings exist.',
6692
+ allOf: [
6693
+ {
6694
+ $ref: '#/components/schemas/AssetClassGroupDto'
6695
+ }
6696
+ ]
6697
+ }
6698
+ },
6699
+ required: ['groups', 'summary']
6700
+ } as const;
6701
+
6702
+ export const $HoldingAssetClassAccountSliceDto = {
6703
+ type: 'object',
6704
+ properties: {
6705
+ accountId: {
6706
+ type: 'string',
6707
+ description: 'Account ID'
6708
+ },
6709
+ accountPath: {
6710
+ type: 'string',
6711
+ description: 'Full account path',
6712
+ example: 'Assets:US:Investments:Brokerage'
6713
+ },
6714
+ accountCurrency: {
6715
+ type: 'string',
6716
+ description:
6717
+ 'Currency of the holding with the largest converted base value; undefined when no holding is convertible',
6718
+ example: 'USD'
6719
+ },
6720
+ marketValueBase: {
6721
+ type: 'string',
6722
+ description:
6723
+ "Account's market value in base currency (Σ converted holdings; grey bucket included)",
6724
+ example: '50000.00'
6725
+ },
6726
+ shareOfTotalPct: {
4576
6727
  type: 'number',
4577
- description: 'Total number of asset classes'
6728
+ description:
6729
+ 'Share of the global total (0-100). 0 when globalTotal is zero (no NaN/Infinity).',
6730
+ example: 42.5
6731
+ },
6732
+ groups: {
6733
+ description: 'Per-account asset-class breakdown',
6734
+ type: 'array',
6735
+ items: {
6736
+ $ref: '#/components/schemas/AssetClassGroupDto'
6737
+ }
6738
+ },
6739
+ uncategorized: {
6740
+ description:
6741
+ 'Per-account grey bucket (source=FALLBACK holdings, incl. broker cash)',
6742
+ allOf: [
6743
+ {
6744
+ $ref: '#/components/schemas/AssetClassGroupDto'
6745
+ }
6746
+ ]
6747
+ },
6748
+ holdings: {
6749
+ description:
6750
+ 'Every holding row for this account (account ID in each row’s `id` field)',
6751
+ type: 'array',
6752
+ items: {
6753
+ $ref: '#/components/schemas/AccountItemWithAssetClassDto'
6754
+ }
6755
+ }
6756
+ },
6757
+ required: [
6758
+ 'accountId',
6759
+ 'accountPath',
6760
+ 'marketValueBase',
6761
+ 'shareOfTotalPct',
6762
+ 'groups',
6763
+ 'holdings'
6764
+ ]
6765
+ } as const;
6766
+
6767
+ export const $HoldingAssetClassCrossAccountResponseDto = {
6768
+ type: 'object',
6769
+ properties: {
6770
+ global: {
6771
+ description: 'Merged cross-account holding aggregation',
6772
+ allOf: [
6773
+ {
6774
+ $ref: '#/components/schemas/AssetClassAccountsResponseDto'
6775
+ }
6776
+ ]
6777
+ },
6778
+ byAccount: {
6779
+ description: 'Per-account slices',
6780
+ type: 'array',
6781
+ items: {
6782
+ $ref: '#/components/schemas/HoldingAssetClassAccountSliceDto'
6783
+ }
6784
+ }
6785
+ },
6786
+ required: ['global', 'byAccount']
6787
+ } as const;
6788
+
6789
+ export const $CashFlowByCurrencyDto = {
6790
+ type: 'object',
6791
+ properties: {
6792
+ income: {
6793
+ description: 'Income by currency',
6794
+ type: 'array',
6795
+ items: {
6796
+ $ref: '#/components/schemas/BalanceByCurrencyDto'
6797
+ }
6798
+ },
6799
+ expense: {
6800
+ description: 'Expense by currency',
6801
+ type: 'array',
6802
+ items: {
6803
+ $ref: '#/components/schemas/BalanceByCurrencyDto'
6804
+ }
4578
6805
  },
6806
+ netSavings: {
6807
+ description: 'Net savings by currency',
6808
+ type: 'array',
6809
+ items: {
6810
+ $ref: '#/components/schemas/BalanceByCurrencyDto'
6811
+ }
6812
+ }
6813
+ },
6814
+ required: ['income', 'expense', 'netSavings']
6815
+ } as const;
6816
+
6817
+ export const $ConvertedCashFlowDto = {
6818
+ type: 'object',
6819
+ properties: {
4579
6820
  baseCurrency: {
4580
6821
  type: 'string',
4581
6822
  description: 'Base currency for conversion',
4582
6823
  example: 'CNY'
4583
6824
  },
6825
+ income: {
6826
+ type: 'string',
6827
+ description: 'Converted income',
6828
+ example: '25000.00'
6829
+ },
6830
+ expense: {
6831
+ type: 'string',
6832
+ description: 'Converted expense',
6833
+ example: '18000.00'
6834
+ },
6835
+ netSavings: {
6836
+ type: 'string',
6837
+ description: 'Converted net savings',
6838
+ example: '7000.00'
6839
+ },
6840
+ exchangeRates: {
6841
+ type: 'object',
6842
+ description: 'Exchange rates used for conversion',
6843
+ example: {
6844
+ USD: '7.200000'
6845
+ }
6846
+ }
6847
+ },
6848
+ required: ['baseCurrency', 'income', 'expense', 'netSavings', 'exchangeRates']
6849
+ } as const;
6850
+
6851
+ export const $CashFlowResponseDto = {
6852
+ type: 'object',
6853
+ properties: {
6854
+ period: {
6855
+ type: 'string',
6856
+ description: 'Period identifier (YYYY-MM)',
6857
+ example: '2024-06'
6858
+ },
6859
+ income: {
6860
+ type: 'string',
6861
+ description: 'Total income for the period (converted)',
6862
+ example: '25000.00'
6863
+ },
6864
+ expense: {
6865
+ type: 'string',
6866
+ description: 'Total expenses for the period (converted)',
6867
+ example: '18000.00'
6868
+ },
6869
+ netSavings: {
6870
+ type: 'string',
6871
+ description: 'Net savings (income - expense, converted)',
6872
+ example: '7000.00'
6873
+ },
6874
+ savingsRate: {
6875
+ type: 'string',
6876
+ description: 'Savings rate percentage (netSavings / income * 100)',
6877
+ example: '28.00'
6878
+ },
6879
+ currency: {
6880
+ type: 'string',
6881
+ description: 'Base currency code',
6882
+ example: 'CNY'
6883
+ },
6884
+ byCurrency: {
6885
+ description: 'Cash flow grouped by original currency',
6886
+ allOf: [
6887
+ {
6888
+ $ref: '#/components/schemas/CashFlowByCurrencyDto'
6889
+ }
6890
+ ]
6891
+ },
6892
+ converted: {
6893
+ description: 'Converted values in base currency',
6894
+ allOf: [
6895
+ {
6896
+ $ref: '#/components/schemas/ConvertedCashFlowDto'
6897
+ }
6898
+ ]
6899
+ },
4584
6900
  warnings: {
4585
6901
  description: 'Exchange rate warnings',
4586
6902
  type: 'array',
4587
6903
  items: {
4588
- $ref: '#/components/schemas/AccountExchangeRateWarningDto'
6904
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
4589
6905
  }
4590
6906
  }
4591
6907
  },
4592
- required: ['totalAccounts', 'totalAssetClasses', 'baseCurrency']
6908
+ required: [
6909
+ 'period',
6910
+ 'income',
6911
+ 'expense',
6912
+ 'netSavings',
6913
+ 'savingsRate',
6914
+ 'currency'
6915
+ ]
4593
6916
  } as const;
4594
6917
 
4595
- export const $AssetClassAccountsResponseDto = {
6918
+ export const $CategoryGroupDto = {
4596
6919
  type: 'object',
4597
6920
  properties: {
6921
+ category: {
6922
+ type: 'string',
6923
+ description:
6924
+ 'Functional category (account-path Group segment); regional and universal account paths merge under it',
6925
+ example: 'Food'
6926
+ },
6927
+ totalExpense: {
6928
+ type: 'string',
6929
+ description:
6930
+ 'Converted total for this category in base currency (expense amount when flow=expense, income amount when flow=income)',
6931
+ example: '1200.00'
6932
+ },
6933
+ sharePct: {
6934
+ type: 'number',
6935
+ description: 'Share of grand total (0-100); 0 when grand total is 0',
6936
+ example: 42.5
6937
+ },
6938
+ balanceByCurrency: {
6939
+ description: 'Raw (unconverted) expense per currency',
6940
+ type: 'array',
6941
+ items: {
6942
+ $ref: '#/components/schemas/BalanceByCurrencyDto'
6943
+ }
6944
+ },
6945
+ convertedBalance: {
6946
+ type: 'string',
6947
+ description:
6948
+ 'Converted total in base currency (omitted when FX missing for all currencies in this category)',
6949
+ example: '1200.00'
6950
+ }
6951
+ },
6952
+ required: ['category', 'totalExpense', 'sharePct', 'balanceByCurrency']
6953
+ } as const;
6954
+
6955
+ export const $ExpensesByCategorySummaryDto = {
6956
+ type: 'object',
6957
+ properties: {
6958
+ totalExpense: {
6959
+ type: 'string',
6960
+ description:
6961
+ 'Total across all categories, converted (convertible categories only); expense totals when flow=expense, income totals when flow=income',
6962
+ example: '5000.00'
6963
+ },
6964
+ categoryCount: {
6965
+ type: 'number',
6966
+ description: 'Number of categories',
6967
+ example: 8
6968
+ }
6969
+ },
6970
+ required: ['totalExpense', 'categoryCount']
6971
+ } as const;
6972
+
6973
+ export const $ExpensesByCategoryResponseDto = {
6974
+ type: 'object',
6975
+ properties: {
6976
+ period: {
6977
+ type: 'string',
6978
+ description: 'Period requested',
6979
+ example: '1m'
6980
+ },
6981
+ baseCurrency: {
6982
+ type: 'string',
6983
+ description: 'Base currency for converted values',
6984
+ example: 'CNY'
6985
+ },
4598
6986
  groups: {
4599
- description: 'Account groups by asset class',
6987
+ description:
6988
+ 'Expense groups by functional category, sorted by converted total desc',
4600
6989
  type: 'array',
4601
6990
  items: {
4602
- $ref: '#/components/schemas/AssetClassGroupDto'
6991
+ $ref: '#/components/schemas/CategoryGroupDto'
6992
+ }
6993
+ },
6994
+ summary: {
6995
+ description: 'Summary statistics',
6996
+ allOf: [
6997
+ {
6998
+ $ref: '#/components/schemas/ExpensesByCategorySummaryDto'
6999
+ }
7000
+ ]
7001
+ },
7002
+ warnings: {
7003
+ description: 'Exchange rate warnings (e.g. missing rate for a currency)',
7004
+ type: 'array',
7005
+ items: {
7006
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
4603
7007
  }
7008
+ }
7009
+ },
7010
+ required: ['period', 'baseCurrency', 'groups', 'summary']
7011
+ } as const;
7012
+
7013
+ export const $MonetaryDto = {
7014
+ type: 'object',
7015
+ properties: {
7016
+ amount: {
7017
+ type: 'string',
7018
+ description: 'Amount (Decimal string)',
7019
+ example: '3000'
7020
+ },
7021
+ currency: {
7022
+ type: 'string',
7023
+ description: 'ISO 4217 currency',
7024
+ example: 'USD'
7025
+ },
7026
+ baseCcyEquivalent: {
7027
+ type: 'object',
7028
+ description: 'Converted to user base currency (Decimal string)',
7029
+ example: '21600',
7030
+ nullable: true
7031
+ }
7032
+ },
7033
+ required: ['amount', 'currency']
7034
+ } as const;
7035
+
7036
+ export const $CurrentPriceDto = {
7037
+ type: 'object',
7038
+ properties: {
7039
+ amount: {
7040
+ type: 'string',
7041
+ description: 'Price amount (Decimal string)',
7042
+ example: '250'
7043
+ },
7044
+ currency: {
7045
+ type: 'string',
7046
+ description: 'Price currency (ISO 4217)',
7047
+ example: 'USD'
7048
+ },
7049
+ date: {
7050
+ type: 'string',
7051
+ description: 'Price date (ISO 8601)',
7052
+ example: '2024-06-01'
7053
+ },
7054
+ source: {
7055
+ type: 'string',
7056
+ description: 'Price source',
7057
+ example: 'USER_OVERRIDE',
7058
+ enum: ['USER_OVERRIDE', 'OPENBB_EQUITY', 'OPENBB_CURRENCY']
7059
+ }
7060
+ },
7061
+ required: ['amount', 'currency', 'date', 'source']
7062
+ } as const;
7063
+
7064
+ export const $FxRateDto = {
7065
+ type: 'object',
7066
+ properties: {
7067
+ from: {
7068
+ type: 'string',
7069
+ example: 'USD'
7070
+ },
7071
+ to: {
7072
+ type: 'string',
7073
+ example: 'CNY'
7074
+ },
7075
+ rate: {
7076
+ type: 'string',
7077
+ description: 'FX rate (Decimal string)',
7078
+ example: '7.2'
7079
+ },
7080
+ date: {
7081
+ type: 'string',
7082
+ description: 'Rate date (ISO 8601)',
7083
+ example: '2024-01-15'
7084
+ }
7085
+ },
7086
+ required: ['from', 'to', 'rate', 'date']
7087
+ } as const;
7088
+
7089
+ export const $HoldingPnlRowDto = {
7090
+ type: 'object',
7091
+ properties: {
7092
+ accountId: {
7093
+ type: 'string',
7094
+ description: 'Account UUID',
7095
+ example: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'
7096
+ },
7097
+ accountPath: {
7098
+ type: 'string',
7099
+ description: 'Full account path',
7100
+ example: 'Assets:US:Broker:AAPL'
7101
+ },
7102
+ accountCcy: {
7103
+ type: 'object',
7104
+ description: 'Account settlement currency (ISO 4217), from cost currency',
7105
+ nullable: true,
7106
+ example: 'USD'
7107
+ },
7108
+ brokerType: {
7109
+ type: 'object',
7110
+ description: 'Broker type derived from Platform.type',
7111
+ nullable: true,
7112
+ example: 'broker'
7113
+ },
7114
+ symbol: {
7115
+ type: 'string',
7116
+ description: 'Commodity symbol',
7117
+ example: 'AAPL'
7118
+ },
7119
+ chartToken: {
7120
+ type: 'string',
7121
+ description: 'Chart segment token (libs/common resolver)',
7122
+ example: 'equity',
7123
+ enum: ['equity', 'fund', 'bond', 'cash', 'other']
7124
+ },
7125
+ assetClass: {
7126
+ type: 'string',
7127
+ example: 'EQUITY'
7128
+ },
7129
+ assetSubClass: {
7130
+ type: 'object',
7131
+ nullable: true,
7132
+ example: 'STOCK'
7133
+ },
7134
+ units: {
7135
+ type: 'string',
7136
+ description: 'Net held units (Decimal string)',
7137
+ example: '12'
7138
+ },
7139
+ averageCostPerUnit: {
7140
+ description:
7141
+ 'Average cost per unit; null when cost currency conflicts or no cost',
7142
+ nullable: true,
7143
+ allOf: [
7144
+ {
7145
+ $ref: '#/components/schemas/MonetaryDto'
7146
+ }
7147
+ ]
7148
+ },
7149
+ costBasis: {
7150
+ description: 'Cost basis of held units',
7151
+ nullable: true,
7152
+ allOf: [
7153
+ {
7154
+ $ref: '#/components/schemas/MonetaryDto'
7155
+ }
7156
+ ]
7157
+ },
7158
+ marketValue: {
7159
+ description: 'Market value at asOf price',
7160
+ nullable: true,
7161
+ allOf: [
7162
+ {
7163
+ $ref: '#/components/schemas/MonetaryDto'
7164
+ }
7165
+ ]
7166
+ },
7167
+ currentPrice: {
7168
+ description: 'Price used for market value',
7169
+ nullable: true,
7170
+ allOf: [
7171
+ {
7172
+ $ref: '#/components/schemas/CurrentPriceDto'
7173
+ }
7174
+ ]
7175
+ },
7176
+ unrealizedPnlBase: {
7177
+ type: 'object',
7178
+ description:
7179
+ 'Unrealized P&L in base currency (Decimal string); null when any FX/price missing',
7180
+ nullable: true,
7181
+ example: '6000'
7182
+ },
7183
+ unrealizedPnlPct: {
7184
+ type: 'object',
7185
+ description: 'Unrealized P&L % (Decimal string)',
7186
+ nullable: true,
7187
+ example: '25'
7188
+ },
7189
+ costFxRate: {
7190
+ description: 'Historical FX rate applied to cost basis',
7191
+ nullable: true,
7192
+ allOf: [
7193
+ {
7194
+ $ref: '#/components/schemas/FxRateDto'
7195
+ }
7196
+ ]
7197
+ },
7198
+ marketFxRate: {
7199
+ description: 'FX rate applied to market value',
7200
+ nullable: true,
7201
+ allOf: [
7202
+ {
7203
+ $ref: '#/components/schemas/FxRateDto'
7204
+ }
7205
+ ]
7206
+ },
7207
+ pctOfInvestedAssets: {
7208
+ type: 'object',
7209
+ description:
7210
+ 'Share of invested assets % (Decimal string); only for invested chartTokens',
7211
+ nullable: true,
7212
+ example: '40'
7213
+ },
7214
+ realizedPnl: {
7215
+ description:
7216
+ 'Cumulative realized P&L on sold lots (asOf-date cutoff); null when the method has no applicable sells, a sell lacks a price, or any required FX rate is missing (never-mix). When a sell spans multiple currencies (cross-currency sale), amount and currency reflect the base currency; baseCcyEquivalent is always the authoritative dual-FX figure',
7217
+ nullable: true,
7218
+ allOf: [
7219
+ {
7220
+ $ref: '#/components/schemas/MonetaryDto'
7221
+ }
7222
+ ]
7223
+ }
7224
+ },
7225
+ required: [
7226
+ 'accountId',
7227
+ 'accountPath',
7228
+ 'symbol',
7229
+ 'chartToken',
7230
+ 'assetClass',
7231
+ 'units'
7232
+ ]
7233
+ } as const;
7234
+
7235
+ export const $HoldingPnlWarningDto = {
7236
+ type: 'object',
7237
+ properties: {
7238
+ type: {
7239
+ type: 'string',
7240
+ description: 'Warning type',
7241
+ example: 'MISSING_COST_FX_RATE',
7242
+ enum: [
7243
+ 'MISSING_COST_FX_RATE',
7244
+ 'MISSING_MARKET_FX_RATE',
7245
+ 'MISSING_SALE_PRICE',
7246
+ 'MISSING_REALIZED_FX_RATE',
7247
+ 'OVERSOLD_LOTS',
7248
+ 'NO_PRICE',
7249
+ 'MIXED_COST_CURRENCY'
7250
+ ]
7251
+ },
7252
+ symbol: {
7253
+ type: 'object',
7254
+ nullable: true
7255
+ },
7256
+ accountId: {
7257
+ type: 'object',
7258
+ nullable: true
4604
7259
  },
4605
- summary: {
4606
- description: 'Summary statistics',
4607
- allOf: [
4608
- {
4609
- $ref: '#/components/schemas/AssetClassSummaryDto'
4610
- }
4611
- ]
7260
+ currency: {
7261
+ type: 'object',
7262
+ nullable: true
4612
7263
  }
4613
7264
  },
4614
- required: ['groups', 'summary']
7265
+ required: ['type']
4615
7266
  } as const;
4616
7267
 
4617
- export const $CashFlowByCurrencyDto = {
7268
+ export const $HoldingPnlResponseDto = {
4618
7269
  type: 'object',
4619
7270
  properties: {
4620
- income: {
4621
- description: 'Income by currency',
4622
- type: 'array',
4623
- items: {
4624
- $ref: '#/components/schemas/BalanceByCurrencyDto'
4625
- }
7271
+ asOfDate: {
7272
+ type: 'string',
7273
+ example: '2026-07-08'
4626
7274
  },
4627
- expense: {
4628
- description: 'Expense by currency',
7275
+ baseCurrency: {
7276
+ type: 'string',
7277
+ example: 'CNY'
7278
+ },
7279
+ method: {
7280
+ type: 'string',
7281
+ description:
7282
+ 'Realized-P&L lot-matching method (FIFO or average). Unrealized cost basis remains average regardless of this value (#473).',
7283
+ enum: ['average', 'FIFO'],
7284
+ example: 'average'
7285
+ },
7286
+ rows: {
4629
7287
  type: 'array',
4630
7288
  items: {
4631
- $ref: '#/components/schemas/BalanceByCurrencyDto'
7289
+ $ref: '#/components/schemas/HoldingPnlRowDto'
4632
7290
  }
4633
7291
  },
4634
- netSavings: {
4635
- description: 'Net savings by currency',
7292
+ warnings: {
4636
7293
  type: 'array',
4637
7294
  items: {
4638
- $ref: '#/components/schemas/BalanceByCurrencyDto'
7295
+ $ref: '#/components/schemas/HoldingPnlWarningDto'
4639
7296
  }
4640
7297
  }
4641
7298
  },
4642
- required: ['income', 'expense', 'netSavings']
7299
+ required: ['asOfDate', 'baseCurrency', 'method', 'rows', 'warnings']
4643
7300
  } as const;
4644
7301
 
4645
- export const $ConvertedCashFlowDto = {
7302
+ export const $CreateBeanPriceDto = {
4646
7303
  type: 'object',
4647
7304
  properties: {
4648
- baseCurrency: {
7305
+ currency: {
4649
7306
  type: 'string',
4650
- description: 'Base currency for conversion',
4651
- example: 'CNY'
7307
+ description: 'Currency being priced (e.g., USD, AAPL, BTC)',
7308
+ example: 'USD'
4652
7309
  },
4653
- income: {
7310
+ quoteCurrency: {
4654
7311
  type: 'string',
4655
- description: 'Converted income',
4656
- example: '25000.00'
7312
+ description: 'Quote currency (pricing currency, e.g., CNY, EUR)',
7313
+ example: 'CNY'
4657
7314
  },
4658
- expense: {
4659
- type: 'string',
4660
- description: 'Converted expense',
4661
- example: '18000.00'
7315
+ amount: {
7316
+ type: 'number',
7317
+ description:
7318
+ 'Price amount (MUST be >= 0 per Beancount spec, supports up to 15 decimal places). Zero allowed for conversion entries, negative strictly prohibited.',
7319
+ example: 175.5,
7320
+ minimum: 0
4662
7321
  },
4663
- netSavings: {
7322
+ date: {
4664
7323
  type: 'string',
4665
- description: 'Converted net savings',
4666
- example: '7000.00'
7324
+ description: 'Price date (ISO 8601 format)',
7325
+ example: '2024-11-05'
4667
7326
  },
4668
- exchangeRates: {
7327
+ metadata: {
4669
7328
  type: 'object',
4670
- description: 'Exchange rates used for conversion',
7329
+ description:
7330
+ 'Metadata (validated by Zod schema, max field lengths enforced)',
4671
7331
  example: {
4672
- USD: '7.200000'
7332
+ source: 'MANUAL',
7333
+ note: 'Bank valuation report',
7334
+ confidence: 0.95
4673
7335
  }
4674
7336
  }
4675
7337
  },
4676
- required: ['baseCurrency', 'income', 'expense', 'netSavings', 'exchangeRates']
7338
+ required: ['currency', 'quoteCurrency', 'amount', 'date']
4677
7339
  } as const;
4678
7340
 
4679
- export const $CashFlowResponseDto = {
7341
+ export const $PriceResponseDto = {
4680
7342
  type: 'object',
4681
7343
  properties: {
4682
- period: {
7344
+ id: {
4683
7345
  type: 'string',
4684
- description: 'Period identifier (YYYY-MM)',
4685
- example: '2024-06'
7346
+ description: 'Unique identifier',
7347
+ example: 'uuid-123-456'
4686
7348
  },
4687
- income: {
7349
+ userId: {
4688
7350
  type: 'string',
4689
- description: 'Total income for the period (converted)',
4690
- example: '25000.00'
7351
+ description: 'User ID (owner of the price)',
7352
+ example: 'user-123'
4691
7353
  },
4692
- expense: {
7354
+ currency: {
4693
7355
  type: 'string',
4694
- description: 'Total expenses for the period (converted)',
4695
- example: '18000.00'
7356
+ description: 'Currency being priced (e.g., USD, AAPL, BTC)',
7357
+ example: 'BTC'
4696
7358
  },
4697
- netSavings: {
7359
+ quoteCurrency: {
4698
7360
  type: 'string',
4699
- description: 'Net savings (income - expense, converted)',
4700
- example: '7000.00'
7361
+ description: 'Quote currency (pricing currency, e.g., USD, CNY)',
7362
+ example: 'USD'
4701
7363
  },
4702
- savingsRate: {
4703
- type: 'string',
4704
- description: 'Savings rate percentage (netSavings / income * 100)',
4705
- example: '28.00'
7364
+ amount: {
7365
+ type: 'number',
7366
+ description:
7367
+ 'Price amount (corresponds to Beancount Amount.number). Supports up to 15 decimal places.',
7368
+ example: 50000
4706
7369
  },
4707
- currency: {
7370
+ date: {
4708
7371
  type: 'string',
4709
- description: 'Base currency code',
4710
- example: 'CNY'
7372
+ description:
7373
+ 'Price date (ISO 8601 format). Represents the date this price was valid.',
7374
+ example: '2024-01-01',
7375
+ format: 'date'
4711
7376
  },
4712
- byCurrency: {
4713
- description: 'Cash flow grouped by original currency',
4714
- allOf: [
4715
- {
4716
- $ref: '#/components/schemas/CashFlowByCurrencyDto'
4717
- }
4718
- ]
7377
+ meta: {
7378
+ type: 'object',
7379
+ description:
7380
+ 'Metadata (corresponds to Beancount meta field). Contains source, confidence, note, etc.',
7381
+ example: {
7382
+ source: 'MANUAL',
7383
+ note: 'User-defined price',
7384
+ confidence: 1
7385
+ }
4719
7386
  },
4720
- converted: {
4721
- description: 'Converted values in base currency',
4722
- allOf: [
4723
- {
4724
- $ref: '#/components/schemas/ConvertedCashFlowDto'
4725
- }
4726
- ]
7387
+ createdAt: {
7388
+ format: 'date-time',
7389
+ type: 'string',
7390
+ description: 'Creation timestamp',
7391
+ example: '2024-11-03T10:00:00Z'
4727
7392
  },
4728
- warnings: {
4729
- description: 'Exchange rate warnings',
7393
+ updatedAt: {
7394
+ format: 'date-time',
7395
+ type: 'string',
7396
+ description: 'Last update timestamp',
7397
+ example: '2024-11-03T10:00:00Z'
7398
+ }
7399
+ },
7400
+ required: [
7401
+ 'id',
7402
+ 'userId',
7403
+ 'currency',
7404
+ 'quoteCurrency',
7405
+ 'amount',
7406
+ 'date',
7407
+ 'meta',
7408
+ 'createdAt',
7409
+ 'updatedAt'
7410
+ ]
7411
+ } as const;
7412
+
7413
+ export const $PriceListResponseDto = {
7414
+ type: 'object',
7415
+ properties: {
7416
+ items: {
7417
+ description: 'List of prices',
4730
7418
  type: 'array',
4731
7419
  items: {
4732
- $ref: '#/components/schemas/ExchangeRateWarningDto'
7420
+ $ref: '#/components/schemas/PriceResponseDto'
4733
7421
  }
7422
+ },
7423
+ total: {
7424
+ type: 'number',
7425
+ description: 'Total number of prices',
7426
+ example: 42
4734
7427
  }
4735
7428
  },
4736
- required: [
4737
- 'period',
4738
- 'income',
4739
- 'expense',
4740
- 'netSavings',
4741
- 'savingsRate',
4742
- 'currency'
4743
- ]
7429
+ required: ['items', 'total']
7430
+ } as const;
7431
+
7432
+ export const $UpdateBeanPriceDto = {
7433
+ type: 'object',
7434
+ properties: {
7435
+ currency: {
7436
+ type: 'string',
7437
+ description: 'Currency being priced'
7438
+ },
7439
+ quoteCurrency: {
7440
+ type: 'string',
7441
+ description: 'Quote currency (pricing currency)'
7442
+ },
7443
+ amount: {
7444
+ type: 'number',
7445
+ description: 'Price amount (MUST be >= 0 per Beancount spec)',
7446
+ minimum: 0
7447
+ },
7448
+ date: {
7449
+ type: 'string',
7450
+ description: 'Price date (ISO 8601 format)'
7451
+ },
7452
+ metadata: {
7453
+ type: 'object',
7454
+ description: 'Metadata'
7455
+ }
7456
+ }
4744
7457
  } as const;
4745
7458
 
4746
7459
  export const $CurrencyBalanceDto = {
@@ -4778,6 +7491,16 @@ export const $TimeSeriesPointDto = {
4778
7491
  description: 'Change from previous point',
4779
7492
  example: '5000.00'
4780
7493
  },
7494
+ assets: {
7495
+ type: 'string',
7496
+ description: 'Total assets at this date (in base currency)',
7497
+ example: '494338.00'
7498
+ },
7499
+ liabilities: {
7500
+ type: 'string',
7501
+ description: 'Total liabilities at this date (in base currency)',
7502
+ example: '310098.00'
7503
+ },
4781
7504
  byCurrency: {
4782
7505
  description: 'Multi-currency breakdown for this point',
4783
7506
  type: 'array',
@@ -4887,6 +7610,111 @@ export const $PortfolioTrendsResponseDto = {
4887
7610
  required: ['series', 'summary', 'period', 'granularity', 'currency']
4888
7611
  } as const;
4889
7612
 
7613
+ export const $CashFlowPointDto = {
7614
+ type: 'object',
7615
+ properties: {
7616
+ month: {
7617
+ type: 'string',
7618
+ description: 'Month key (YYYY-MM)',
7619
+ example: '2024-03'
7620
+ },
7621
+ income: {
7622
+ type: 'string',
7623
+ description: 'Income in base currency (absolute, converted)',
7624
+ example: '10000.00'
7625
+ },
7626
+ expense: {
7627
+ type: 'string',
7628
+ description: 'Expense in base currency (absolute, converted)',
7629
+ example: '5000.00'
7630
+ },
7631
+ netSavings: {
7632
+ type: 'string',
7633
+ description: 'netSavings = income − expense (savings positive)',
7634
+ example: '5000.00'
7635
+ }
7636
+ },
7637
+ required: ['month', 'income', 'expense', 'netSavings']
7638
+ } as const;
7639
+
7640
+ export const $CashFlowTrendSummaryDto = {
7641
+ type: 'object',
7642
+ properties: {
7643
+ totalIncome: {
7644
+ type: 'string',
7645
+ description: 'Total income across the period',
7646
+ example: '60000.00'
7647
+ },
7648
+ totalExpense: {
7649
+ type: 'string',
7650
+ description: 'Total expense across the period',
7651
+ example: '30000.00'
7652
+ },
7653
+ totalNetSavings: {
7654
+ type: 'string',
7655
+ description: 'income − expense across the period',
7656
+ example: '30000.00'
7657
+ },
7658
+ averageMonthlyNetSavings: {
7659
+ type: 'string',
7660
+ description:
7661
+ 'totalNetSavings divided by the window length (N months, incl. zero-filled)',
7662
+ example: '5000.00'
7663
+ }
7664
+ },
7665
+ required: [
7666
+ 'totalIncome',
7667
+ 'totalExpense',
7668
+ 'totalNetSavings',
7669
+ 'averageMonthlyNetSavings'
7670
+ ]
7671
+ } as const;
7672
+
7673
+ export const $CashFlowTrendsResponseDto = {
7674
+ type: 'object',
7675
+ properties: {
7676
+ series: {
7677
+ description:
7678
+ 'Monthly cash-flow series (fixed N-month window, zero-filled)',
7679
+ type: 'array',
7680
+ items: {
7681
+ $ref: '#/components/schemas/CashFlowPointDto'
7682
+ }
7683
+ },
7684
+ summary: {
7685
+ description: 'Period totals',
7686
+ allOf: [
7687
+ {
7688
+ $ref: '#/components/schemas/CashFlowTrendSummaryDto'
7689
+ }
7690
+ ]
7691
+ },
7692
+ period: {
7693
+ type: 'string',
7694
+ description: 'Period requested',
7695
+ example: '6m'
7696
+ },
7697
+ granularity: {
7698
+ type: 'string',
7699
+ description: 'Data granularity (v1 returns month buckets)',
7700
+ example: 'month'
7701
+ },
7702
+ currency: {
7703
+ type: 'string',
7704
+ description: 'Base currency for converted values',
7705
+ example: 'CNY'
7706
+ },
7707
+ warnings: {
7708
+ description: 'Exchange rate warnings (e.g. missing rate for a currency)',
7709
+ type: 'array',
7710
+ items: {
7711
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
7712
+ }
7713
+ }
7714
+ },
7715
+ required: ['series', 'summary', 'period', 'granularity', 'currency']
7716
+ } as const;
7717
+
4890
7718
  export const $GenerateSnapshotBody = {
4891
7719
  type: 'object',
4892
7720
  properties: {}