@firela/api-types 0.0.0-canary.209966 → 0.0.0-canary.285599dd

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,28 +904,167 @@ 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'
617
- },
618
- accountId: {
619
- type: 'string',
620
- description: 'Account ID',
621
- 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
622
930
  },
623
- accountName: {
931
+ error: {
624
932
  type: 'string',
625
- description: 'Account name',
626
- example: 'Assets:Bank:Checking'
933
+ description: 'Error message describing the failure',
934
+ example: 'Transaction does not balance'
627
935
  },
628
- units: {
936
+ errorCode: {
629
937
  type: 'string',
630
- description: 'Amount (may be null if interpolated)',
631
- example: '100.50'
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',
1061
+ example: 'Assets:Bank:Checking'
1062
+ },
1063
+ units: {
1064
+ type: 'string',
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.',
1067
+ example: '100.50'
632
1068
  },
633
1069
  currency: {
634
1070
  type: 'string',
@@ -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: [
@@ -819,6 +1276,37 @@ export const $TransactionListResponseDto = {
819
1276
  required: ['data', 'total', 'limit', 'offset']
820
1277
  } as const;
821
1278
 
1279
+ export const $TagSuggestionDto = {
1280
+ type: 'object',
1281
+ properties: {
1282
+ tag: {
1283
+ type: 'string',
1284
+ description: 'Tag name',
1285
+ example: 'Monthly'
1286
+ },
1287
+ count: {
1288
+ type: 'number',
1289
+ description: 'Usage count across ACTIVE transactions',
1290
+ example: 12
1291
+ }
1292
+ },
1293
+ required: ['tag', 'count']
1294
+ } as const;
1295
+
1296
+ export const $TagSuggestionsResponseDto = {
1297
+ type: 'object',
1298
+ properties: {
1299
+ data: {
1300
+ description: 'Tag suggestions sorted as requested',
1301
+ type: 'array',
1302
+ items: {
1303
+ $ref: '#/components/schemas/TagSuggestionDto'
1304
+ }
1305
+ }
1306
+ },
1307
+ required: ['data']
1308
+ } as const;
1309
+
822
1310
  export const $UpdateTransactionDto = {
823
1311
  type: 'object',
824
1312
  properties: {
@@ -876,71 +1364,6 @@ export const $UpdateTransactionDto = {
876
1364
  }
877
1365
  } as const;
878
1366
 
879
- export const $AccountStandardResponseDto = {
880
- type: 'object',
881
- properties: {
882
- path: {
883
- type: 'string',
884
- description: 'Account path (hierarchical, colon-separated)',
885
- example: 'Assets:CN:Bank:ICBC:Checking'
886
- },
887
- type: {
888
- type: 'string',
889
- description: 'Account type in Beancount hierarchy',
890
- enum: ['Assets', 'Liabilities', 'Income', 'Expenses', 'Equity'],
891
- example: 'Assets'
892
- },
893
- i18nKey: {
894
- type: 'string',
895
- description: 'i18n key for localized display name',
896
- example: 'account.assets.cn.bank.icbc.checking'
897
- },
898
- description: {
899
- 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
1367
  export const $BalanceResponseDto = {
945
1368
  type: 'object',
946
1369
  properties: {
@@ -1034,8 +1457,8 @@ export const $TransactionSummaryDto = {
1034
1457
  },
1035
1458
  sourceType: {
1036
1459
  type: 'string',
1037
- description: 'Source type (NLP, CSV, OCR, API)',
1038
- enum: ['NLP', 'CSV', 'OCR', 'API']
1460
+ description:
1461
+ 'Source type (free-form string from transaction metadata, e.g. import, api)'
1039
1462
  },
1040
1463
  sourcePlatform: {
1041
1464
  type: 'string',
@@ -1075,12 +1498,23 @@ export const $ReviewSummaryDto = {
1075
1498
  },
1076
1499
  confidenceLevel: {
1077
1500
  type: 'string',
1078
- description: 'Confidence level derived from score',
1079
- enum: ['HIGH', 'MEDIUM', 'LOW']
1501
+ description:
1502
+ 'Confidence level derived from score. Null for error-type reviews (ACCOUNT_VALIDATION/PIPELINE_ERROR) which carry no confidence.',
1503
+ enum: ['HIGH', 'MEDIUM', 'LOW'],
1504
+ nullable: true
1080
1505
  },
1081
- summary: {
1506
+ summaryKey: {
1082
1507
  type: 'string',
1083
- description: 'Human-readable summary of the review item'
1508
+ description:
1509
+ 'i18n message key for summary (e.g., review.summary.duplicate). Translate on frontend with summaryParams.'
1510
+ },
1511
+ summaryParams: {
1512
+ type: 'object',
1513
+ description:
1514
+ 'Parameters for summary message interpolation (e.g., { date: "2024-01-15", amount: "50" })',
1515
+ additionalProperties: {
1516
+ type: 'string'
1517
+ }
1084
1518
  },
1085
1519
  matchReasons: {
1086
1520
  description: 'Human-readable reasons for branching',
@@ -1091,7 +1525,8 @@ export const $ReviewSummaryDto = {
1091
1525
  },
1092
1526
  sourceType: {
1093
1527
  type: 'string',
1094
- description: 'Source type (NLP, CSV, OCR, API)'
1528
+ description:
1529
+ 'Source type (free-form string from transaction metadata, e.g. import, api)'
1095
1530
  },
1096
1531
  sourcePlatform: {
1097
1532
  type: 'string',
@@ -1140,7 +1575,7 @@ export const $ReviewSummaryDto = {
1140
1575
  'status',
1141
1576
  'confidence',
1142
1577
  'confidenceLevel',
1143
- 'summary',
1578
+ 'summaryKey',
1144
1579
  'matchReasons',
1145
1580
  'sourceType',
1146
1581
  'createdAt'
@@ -1201,7 +1636,20 @@ export const $DecisionOptionDto = {
1201
1636
  properties: {
1202
1637
  value: {
1203
1638
  type: 'string',
1204
- description: 'The action value to submit (e.g., UPGRADE_REPLACE, ACCEPT)'
1639
+ description: 'The action value to submit (e.g., UPGRADE_REPLACE, ACCEPT)',
1640
+ enum: [
1641
+ 'UPGRADE_REPLACE',
1642
+ 'LINK_KEEP_BOTH',
1643
+ 'IGNORE_NEW',
1644
+ 'CONFIRM_DIFFERENT',
1645
+ 'ACCEPT',
1646
+ 'REJECT',
1647
+ 'ACCEPT_AND_LEARN',
1648
+ 'CHOOSE_OTHER',
1649
+ 'CANCEL',
1650
+ 'FIX',
1651
+ 'IGNORE'
1652
+ ]
1205
1653
  },
1206
1654
  labelKey: {
1207
1655
  type: 'string',
@@ -1249,12 +1697,23 @@ export const $ReviewDetailDto = {
1249
1697
  },
1250
1698
  confidenceLevel: {
1251
1699
  type: 'string',
1252
- description: 'Confidence level derived from score',
1253
- enum: ['HIGH', 'MEDIUM', 'LOW']
1700
+ description:
1701
+ 'Confidence level derived from score. Null for error-type reviews (ACCOUNT_VALIDATION/PIPELINE_ERROR) which carry no confidence.',
1702
+ enum: ['HIGH', 'MEDIUM', 'LOW'],
1703
+ nullable: true
1254
1704
  },
1255
- summary: {
1705
+ summaryKey: {
1256
1706
  type: 'string',
1257
- description: 'Human-readable summary of the review item'
1707
+ description:
1708
+ 'i18n message key for summary (e.g., review.summary.duplicate). Translate on frontend with summaryParams.'
1709
+ },
1710
+ summaryParams: {
1711
+ type: 'object',
1712
+ description:
1713
+ 'Parameters for summary message interpolation (e.g., { date: "2024-01-15", amount: "50" })',
1714
+ additionalProperties: {
1715
+ type: 'string'
1716
+ }
1258
1717
  },
1259
1718
  matchReasons: {
1260
1719
  description: 'Human-readable reasons for branching',
@@ -1265,7 +1724,8 @@ export const $ReviewDetailDto = {
1265
1724
  },
1266
1725
  sourceType: {
1267
1726
  type: 'string',
1268
- description: 'Source type (NLP, CSV, OCR, API)'
1727
+ description:
1728
+ 'Source type (free-form string from transaction metadata, e.g. import, api)'
1269
1729
  },
1270
1730
  sourcePlatform: {
1271
1731
  type: 'string',
@@ -1330,7 +1790,7 @@ export const $ReviewDetailDto = {
1330
1790
  'status',
1331
1791
  'confidence',
1332
1792
  'confidenceLevel',
1333
- 'summary',
1793
+ 'summaryKey',
1334
1794
  'matchReasons',
1335
1795
  'sourceType',
1336
1796
  'createdAt',
@@ -1339,39 +1799,242 @@ export const $ReviewDetailDto = {
1339
1799
  ]
1340
1800
  } as const;
1341
1801
 
1342
- export const $PayeeResponseDto = {
1802
+ export const $ResolveReviewDto = {
1343
1803
  type: 'object',
1344
1804
  properties: {
1345
- id: {
1346
- type: 'string',
1347
- description: 'Unique identifier (UUID)',
1348
- example: 'uuid-123-456'
1349
- },
1350
- userId: {
1351
- type: 'string',
1352
- description: 'User ID (owner of this payee mapping)',
1353
- example: 'user-123'
1354
- },
1355
- payee: {
1805
+ action: {
1356
1806
  type: 'string',
1357
- description: "User's original payee name (e.g., 'Starbucks', 'McDonald')",
1358
- example: 'Starbucks'
1359
- },
1360
- payeeProfileId: {
1361
- type: 'object',
1362
1807
  description:
1363
- 'Reference to global PayeeProfile (merchant info, i18n keys, categories)',
1364
- example: 'uuid-789',
1365
- nullable: true
1808
+ 'Decision action. Valid actions vary by review type — see DecisionOptionDto.value returned by the review detail endpoint.',
1809
+ enum: [
1810
+ 'UPGRADE_REPLACE',
1811
+ 'LINK_KEEP_BOTH',
1812
+ 'IGNORE_NEW',
1813
+ 'CONFIRM_DIFFERENT',
1814
+ 'ACCEPT',
1815
+ 'REJECT',
1816
+ 'ACCEPT_AND_LEARN',
1817
+ 'CHOOSE_OTHER',
1818
+ 'CANCEL',
1819
+ 'FIX',
1820
+ 'IGNORE'
1821
+ ],
1822
+ example: 'ACCEPT'
1366
1823
  },
1367
- customCategory: {
1824
+ data: {
1368
1825
  type: 'object',
1369
1826
  description:
1370
- "User's custom category (overrides PayeeProfile category if set)",
1371
- example: 'Dining:Coffee',
1372
- nullable: true
1373
- },
1374
- customTags: {
1827
+ 'Additional data for the decision (e.g., selected account ID)',
1828
+ example: {
1829
+ accountId: 'acc-123'
1830
+ }
1831
+ }
1832
+ },
1833
+ required: ['action']
1834
+ } as const;
1835
+
1836
+ export const $ResolveResultDto = {
1837
+ type: 'object',
1838
+ properties: {
1839
+ success: {
1840
+ type: 'boolean',
1841
+ description: 'Whether resolution was successful'
1842
+ },
1843
+ messageKey: {
1844
+ type: 'string',
1845
+ description:
1846
+ 'i18n message key for result message (e.g., review.payee.result.mapped)'
1847
+ },
1848
+ messageParams: {
1849
+ type: 'object',
1850
+ description:
1851
+ 'Parameters for message interpolation (e.g., { name: "PayeeName" })',
1852
+ additionalProperties: {
1853
+ type: 'string'
1854
+ }
1855
+ },
1856
+ resolutionId: {
1857
+ type: 'string',
1858
+ description:
1859
+ 'Resolution ID for undo. Absent when the resolver rejected the decision (review stayed PENDING).'
1860
+ },
1861
+ canUndo: {
1862
+ type: 'boolean',
1863
+ description: 'Whether this decision can be undone'
1864
+ },
1865
+ undoDeadline: {
1866
+ format: 'date-time',
1867
+ type: 'string',
1868
+ description: 'Deadline for undo (24h from resolution)'
1869
+ },
1870
+ learnedRuleId: {
1871
+ type: 'string',
1872
+ description:
1873
+ 'Rule ID if learning was triggered (ACCEPT_AND_LEARN actions). Use this to deep-link to the rule management page.',
1874
+ example: 'rule_01HXK5V8N2M3P4Q5R6S7T8U9V0'
1875
+ }
1876
+ },
1877
+ required: ['success']
1878
+ } as const;
1879
+
1880
+ export const $UndoResultDto = {
1881
+ type: 'object',
1882
+ properties: {
1883
+ success: {
1884
+ type: 'boolean',
1885
+ description: 'Whether undo was successful'
1886
+ },
1887
+ message: {
1888
+ type: 'string',
1889
+ description: 'Message'
1890
+ },
1891
+ reviewId: {
1892
+ type: 'string',
1893
+ description: 'Review item ID that was restored'
1894
+ }
1895
+ },
1896
+ required: ['success', 'reviewId']
1897
+ } as const;
1898
+
1899
+ export const $BatchResolveDto = {
1900
+ type: 'object',
1901
+ properties: {
1902
+ reviewIds: {
1903
+ description: 'Review item IDs to resolve',
1904
+ example: ['review-1', 'review-2', 'review-3'],
1905
+ type: 'array',
1906
+ items: {
1907
+ type: 'string'
1908
+ }
1909
+ },
1910
+ action: {
1911
+ type: 'string',
1912
+ description: 'Decision action to apply to all items',
1913
+ enum: [
1914
+ 'UPGRADE_REPLACE',
1915
+ 'LINK_KEEP_BOTH',
1916
+ 'IGNORE_NEW',
1917
+ 'CONFIRM_DIFFERENT',
1918
+ 'ACCEPT',
1919
+ 'REJECT',
1920
+ 'ACCEPT_AND_LEARN',
1921
+ 'CHOOSE_OTHER',
1922
+ 'CANCEL',
1923
+ 'FIX',
1924
+ 'IGNORE'
1925
+ ],
1926
+ example: 'ACCEPT'
1927
+ },
1928
+ data: {
1929
+ type: 'object',
1930
+ description: 'Additional data for the decision'
1931
+ }
1932
+ },
1933
+ required: ['reviewIds', 'action']
1934
+ } as const;
1935
+
1936
+ export const $BatchResolveResultDto = {
1937
+ type: 'object',
1938
+ properties: {
1939
+ successCount: {
1940
+ type: 'number',
1941
+ description: 'Number of successfully resolved items'
1942
+ },
1943
+ failedCount: {
1944
+ type: 'number',
1945
+ description: 'Number of failed items'
1946
+ },
1947
+ results: {
1948
+ description: 'Details for each item',
1949
+ type: 'array',
1950
+ items: {
1951
+ type: 'string'
1952
+ }
1953
+ }
1954
+ },
1955
+ required: ['successCount', 'failedCount', 'results']
1956
+ } as const;
1957
+
1958
+ export const $CreatePayeeDto = {
1959
+ type: 'object',
1960
+ properties: {
1961
+ payee: {
1962
+ type: 'string',
1963
+ description:
1964
+ "User's original payee name (e.g., 'Starbucks', 'McDonald'). This is the raw payee string as entered by the user.",
1965
+ example: 'Starbucks',
1966
+ maxLength: 200
1967
+ },
1968
+ payeeProfileId: {
1969
+ type: 'string',
1970
+ description:
1971
+ 'Optional reference to global PayeeProfile for standardized data (merchant info, i18n keys, categories)',
1972
+ example: 'uuid-123',
1973
+ format: 'uuid'
1974
+ },
1975
+ customCategory: {
1976
+ type: 'string',
1977
+ description:
1978
+ "User's custom category for this payee (overrides PayeeProfile category)",
1979
+ example: 'Dining:Coffee',
1980
+ maxLength: 100
1981
+ },
1982
+ customTags: {
1983
+ description:
1984
+ "User's custom tags for this payee (e.g., ['favorite', 'work_meal'])",
1985
+ example: ['favorite', 'work_meal'],
1986
+ type: 'array',
1987
+ items: {
1988
+ type: 'string'
1989
+ }
1990
+ },
1991
+ meta: {
1992
+ type: 'object',
1993
+ description:
1994
+ 'Metadata for extended information (location, notes, contact info, etc.)',
1995
+ example: {
1996
+ location: 'Zhongguancun',
1997
+ note: 'Near subway station',
1998
+ favorite: true
1999
+ }
2000
+ }
2001
+ },
2002
+ required: ['payee']
2003
+ } as const;
2004
+
2005
+ export const $PayeeResponseDto = {
2006
+ type: 'object',
2007
+ properties: {
2008
+ id: {
2009
+ type: 'string',
2010
+ description: 'Unique identifier (UUID)',
2011
+ example: 'uuid-123-456'
2012
+ },
2013
+ userId: {
2014
+ type: 'string',
2015
+ description: 'User ID (owner of this payee mapping)',
2016
+ example: 'user-123'
2017
+ },
2018
+ payee: {
2019
+ type: 'string',
2020
+ description: "User's original payee name (e.g., 'Starbucks', 'McDonald')",
2021
+ example: 'Starbucks'
2022
+ },
2023
+ payeeProfileId: {
2024
+ type: 'string',
2025
+ description:
2026
+ 'Reference to global PayeeProfile (merchant info, i18n keys, categories)',
2027
+ example: 'uuid-789',
2028
+ nullable: true
2029
+ },
2030
+ customCategory: {
2031
+ type: 'string',
2032
+ description:
2033
+ "User's custom category (overrides PayeeProfile category if set)",
2034
+ example: 'Dining:Coffee',
2035
+ nullable: true
2036
+ },
2037
+ customTags: {
1375
2038
  description: "User's custom tags (e.g., ['favorite', 'work_meal'])",
1376
2039
  example: ['favorite', 'work_meal'],
1377
2040
  type: 'array',
@@ -1489,21 +2152,64 @@ export const $PayeeStatsResponseDto = {
1489
2152
  required: ['payee', 'transactionCount', 'lastUsedAt']
1490
2153
  } as const;
1491
2154
 
1492
- export const $PayeeProfileResponseDto = {
2155
+ export const $UpdatePayeeDto = {
1493
2156
  type: 'object',
1494
2157
  properties: {
1495
- id: {
2158
+ payeeProfileId: {
1496
2159
  type: 'string',
1497
- description: 'Unique identifier (UUID)',
1498
- example: '550e8400-e29b-41d4-a716-446655440000'
2160
+ description:
2161
+ 'Optional reference to global PayeeProfile for standardized data (merchant info, i18n keys, categories)',
2162
+ example: 'uuid-123',
2163
+ format: 'uuid'
2164
+ },
2165
+ customCategory: {
2166
+ type: 'string',
2167
+ description:
2168
+ "User's custom category for this payee (overrides PayeeProfile category)",
2169
+ example: 'Dining:Coffee',
2170
+ maxLength: 100
1499
2171
  },
2172
+ customTags: {
2173
+ description:
2174
+ "User's custom tags for this payee (e.g., ['favorite', 'work_meal'])",
2175
+ example: ['favorite', 'work_meal'],
2176
+ type: 'array',
2177
+ items: {
2178
+ type: 'string'
2179
+ }
2180
+ },
2181
+ meta: {
2182
+ type: 'object',
2183
+ description:
2184
+ 'Metadata for extended information (location, notes, contact info, etc.). Will merge with existing metadata.',
2185
+ example: {
2186
+ location: 'Zhongguancun',
2187
+ note: 'Updated note',
2188
+ favorite: true
2189
+ }
2190
+ },
2191
+ isActive: {
2192
+ type: 'boolean',
2193
+ description:
2194
+ 'Enable or disable this payee. Disabled payees will not appear in autocomplete suggestions.',
2195
+ example: true
2196
+ }
2197
+ }
2198
+ } as const;
2199
+
2200
+ export const $CreatePayeeProfileDto = {
2201
+ type: 'object',
2202
+ properties: {
1500
2203
  canonical: {
1501
2204
  type: 'string',
1502
- description: 'Canonical payee name (unique, case-insensitive)',
1503
- example: 'Starbucks'
2205
+ description:
2206
+ 'Canonical payee name (unique, case-insensitive). This is the primary identifier for the payee.',
2207
+ example: 'Starbucks',
2208
+ maxLength: 200
1504
2209
  },
1505
2210
  aliases: {
1506
- description: 'Multi-language aliases',
2211
+ description:
2212
+ 'Multi-language aliases for the payee. Used for matching user input in different languages.',
1507
2213
  example: ['Starbucks Coffee', 'SBUX'],
1508
2214
  type: 'array',
1509
2215
  items: {
@@ -1511,14 +2217,15 @@ export const $PayeeProfileResponseDto = {
1511
2217
  }
1512
2218
  },
1513
2219
  i18nKey: {
1514
- type: 'object',
1515
- description: 'Translation key for i18n',
2220
+ type: 'string',
2221
+ description:
2222
+ 'Translation key for i18n integration (XLIFF translation system)',
1516
2223
  example: 'payee.starbucks',
1517
- nullable: true
2224
+ maxLength: 100
1518
2225
  },
1519
2226
  category: {
1520
2227
  type: 'string',
1521
- description: 'Payee category',
2228
+ description: 'Payee category classification',
1522
2229
  enum: [
1523
2230
  'RESTAURANT',
1524
2231
  'CAFE',
@@ -1547,13 +2254,14 @@ export const $PayeeProfileResponseDto = {
1547
2254
  example: 'CAFE'
1548
2255
  },
1549
2256
  subCategory: {
1550
- type: 'object',
1551
- description: 'Sub-category',
2257
+ type: 'string',
2258
+ description: 'Sub-category for more specific classification',
1552
2259
  example: 'coffee_chain',
1553
- nullable: true
2260
+ maxLength: 100
1554
2261
  },
1555
2262
  countries: {
1556
- description: 'Country codes where payee operates',
2263
+ description:
2264
+ 'Country/region codes where the payee operates (ISO 3166-1 alpha-2)',
1557
2265
  example: ['CN', 'US', 'JP'],
1558
2266
  type: 'array',
1559
2267
  items: {
@@ -1561,297 +2269,582 @@ export const $PayeeProfileResponseDto = {
1561
2269
  }
1562
2270
  },
1563
2271
  primaryCountry: {
1564
- type: 'object',
1565
- description: 'Primary operating country',
2272
+ type: 'string',
2273
+ description: 'Primary operating country (ISO 3166-1 alpha-2)',
1566
2274
  example: 'US',
1567
- nullable: true
2275
+ maxLength: 2
1568
2276
  },
1569
2277
  keywords: {
1570
- description: 'Search keywords',
1571
- example: ['coffee', 'cafe'],
2278
+ description: 'Search keywords for fuzzy matching',
2279
+ example: ['coffee', 'cafe', 'drinks'],
1572
2280
  type: 'array',
1573
2281
  items: {
1574
2282
  type: 'string'
1575
2283
  }
1576
2284
  },
1577
2285
  logoUrl: {
1578
- type: 'object',
1579
- description: 'Logo URL',
1580
- example: 'https://example.com/logo.png',
1581
- nullable: true
2286
+ type: 'string',
2287
+ description: 'Payee logo URL',
2288
+ example: 'https://example.com/logo.png'
1582
2289
  },
1583
2290
  website: {
1584
- type: 'object',
1585
- description: 'Official website',
1586
- example: 'https://www.starbucks.com',
1587
- nullable: true
2291
+ type: 'string',
2292
+ description: 'Official website URL',
2293
+ example: 'https://www.starbucks.com'
1588
2294
  },
1589
2295
  description: {
1590
- type: 'object',
1591
- description: 'Description',
1592
- example: 'Global coffeehouse chain',
1593
- nullable: true
2296
+ type: 'string',
2297
+ description: 'Payee description',
2298
+ example: 'Global coffeehouse chain headquartered in Seattle',
2299
+ maxLength: 1000
1594
2300
  },
1595
2301
  meta: {
1596
2302
  type: 'object',
1597
- description: 'Extended metadata'
2303
+ description:
2304
+ 'Extended metadata (business hours, contact info, additional details)',
2305
+ example: {
2306
+ businessHours: '07:00-22:00',
2307
+ phone: '+1-800-782-7282'
2308
+ }
1598
2309
  },
1599
2310
  dataSource: {
1600
2311
  type: 'string',
1601
- description: 'Data source',
2312
+ description: 'Data source for this profile',
1602
2313
  enum: ['MANUAL', 'IMPORT', 'API', 'CROWDSOURCED'],
1603
- example: 'MANUAL'
1604
- },
1605
- verifiedAt: {
1606
- type: 'object',
1607
- description: 'Verification timestamp (null if not verified)',
1608
- example: '2025-01-01T00:00:00.000Z',
1609
- nullable: true
1610
- },
1611
- isActive: {
1612
- type: 'boolean',
1613
- description: 'Whether the profile is active',
1614
- example: true
1615
- },
1616
- createdAt: {
1617
- format: 'date-time',
1618
- type: 'string',
1619
- description: 'Creation timestamp',
1620
- example: '2025-01-01T00:00:00.000Z'
1621
- },
1622
- updatedAt: {
1623
- format: 'date-time',
1624
- type: 'string',
1625
- description: 'Last update timestamp',
1626
- example: '2025-01-01T00:00:00.000Z'
2314
+ default: 'MANUAL'
1627
2315
  }
1628
2316
  },
1629
- required: [
1630
- 'id',
1631
- 'canonical',
1632
- 'aliases',
1633
- 'category',
1634
- 'countries',
1635
- 'keywords',
1636
- 'meta',
1637
- 'dataSource',
1638
- 'isActive',
1639
- 'createdAt',
1640
- 'updatedAt'
1641
- ]
2317
+ required: ['canonical', 'category']
1642
2318
  } as const;
1643
2319
 
1644
- export const $PayeeProfileListResponseDto = {
2320
+ export const $PayeeProfileResponseDto = {
1645
2321
  type: 'object',
1646
2322
  properties: {
1647
- items: {
1648
- description: 'List of payee profiles',
2323
+ id: {
2324
+ type: 'string',
2325
+ description: 'Unique identifier (UUID)',
2326
+ example: '550e8400-e29b-41d4-a716-446655440000'
2327
+ },
2328
+ canonical: {
2329
+ type: 'string',
2330
+ description: 'Canonical payee name (unique, case-insensitive)',
2331
+ example: 'Starbucks'
2332
+ },
2333
+ aliases: {
2334
+ description: 'Multi-language aliases',
2335
+ example: ['Starbucks Coffee', 'SBUX'],
1649
2336
  type: 'array',
1650
2337
  items: {
1651
- $ref: '#/components/schemas/PayeeProfileResponseDto'
2338
+ type: 'string'
1652
2339
  }
1653
2340
  },
1654
- total: {
1655
- type: 'number',
1656
- description: 'Total count of matching records',
1657
- example: 100
1658
- }
1659
- },
1660
- required: ['items', 'total']
1661
- } as const;
1662
-
1663
- export const $CommodityResponseDto = {
1664
- type: 'object',
1665
- properties: {
1666
- id: {
2341
+ i18nKey: {
1667
2342
  type: 'string',
1668
- description: 'Unique identifier',
1669
- example: 'uuid-123-456'
1670
- },
1671
- userId: {
1672
- type: 'object',
1673
- description: 'User ID (owner of the commodity)',
1674
- example: 'user-123',
2343
+ description: 'Translation key for i18n',
2344
+ example: 'payee.starbucks',
1675
2345
  nullable: true
1676
2346
  },
1677
- symbol: {
2347
+ category: {
1678
2348
  type: 'string',
1679
- description: 'Commodity symbol (corresponds to Beancount currency field)',
1680
- example: 'AAPL'
2349
+ description: 'Payee category',
2350
+ enum: [
2351
+ 'RESTAURANT',
2352
+ 'CAFE',
2353
+ 'FAST_FOOD',
2354
+ 'BAR',
2355
+ 'SUPERMARKET',
2356
+ 'CONVENIENCE_STORE',
2357
+ 'SHOPPING_MALL',
2358
+ 'ONLINE_SHOPPING',
2359
+ 'TAXI',
2360
+ 'RIDE_SHARING',
2361
+ 'PUBLIC_TRANSPORT',
2362
+ 'PARKING',
2363
+ 'GAS_STATION',
2364
+ 'UTILITIES',
2365
+ 'TELECOM',
2366
+ 'STREAMING',
2367
+ 'HEALTHCARE',
2368
+ 'EDUCATION',
2369
+ 'ENTERTAINMENT',
2370
+ 'SPORTS',
2371
+ 'TRAVEL',
2372
+ 'HOTEL',
2373
+ 'OTHER'
2374
+ ],
2375
+ example: 'CAFE'
1681
2376
  },
1682
- date: {
2377
+ subCategory: {
1683
2378
  type: 'string',
1684
- description:
1685
- 'Commodity definition date (required per Beancount spec). Represents when this commodity was first defined in the accounting system.',
1686
- example: '2024-01-01',
1687
- format: 'date'
2379
+ description: 'Sub-category',
2380
+ example: 'coffee_chain',
2381
+ nullable: true
1688
2382
  },
1689
- metadata: {
1690
- type: 'object',
1691
- description:
1692
- 'Metadata (corresponds to Beancount meta field). Contains name, assetClass, precision, note, tags, etc.',
1693
- example: {
1694
- name: 'Apple Inc.',
1695
- assetClass: 'stock',
1696
- precision: 2,
1697
- note: 'Tech stock',
1698
- source: 'AUTO_CREATED'
2383
+ countries: {
2384
+ description: 'Country codes where payee operates',
2385
+ example: ['CN', 'US', 'JP'],
2386
+ type: 'array',
2387
+ items: {
2388
+ type: 'string'
2389
+ }
2390
+ },
2391
+ primaryCountry: {
2392
+ type: 'string',
2393
+ description: 'Primary operating country',
2394
+ example: 'US',
2395
+ nullable: true
2396
+ },
2397
+ keywords: {
2398
+ description: 'Search keywords',
2399
+ example: ['coffee', 'cafe'],
2400
+ type: 'array',
2401
+ items: {
2402
+ type: 'string'
1699
2403
  }
1700
2404
  },
1701
- symbolProfileId: {
2405
+ logoUrl: {
2406
+ type: 'string',
2407
+ description: 'Logo URL',
2408
+ example: 'https://example.com/logo.png',
2409
+ nullable: true
2410
+ },
2411
+ website: {
2412
+ type: 'string',
2413
+ description: 'Official website',
2414
+ example: 'https://www.starbucks.com',
2415
+ nullable: true
2416
+ },
2417
+ description: {
2418
+ type: 'string',
2419
+ description: 'Description',
2420
+ example: 'Global coffeehouse chain',
2421
+ nullable: true
2422
+ },
2423
+ meta: {
1702
2424
  type: 'object',
1703
- description:
1704
- 'Reference to SymbolProfile (market data integration, SaaS feature)',
1705
- example: 'uuid-789',
2425
+ description: 'Extended metadata'
2426
+ },
2427
+ dataSource: {
2428
+ type: 'string',
2429
+ description: 'Data source',
2430
+ enum: ['MANUAL', 'IMPORT', 'API', 'CROWDSOURCED'],
2431
+ example: 'MANUAL'
2432
+ },
2433
+ verifiedAt: {
2434
+ type: 'string',
2435
+ description: 'Verification timestamp (null if not verified)',
2436
+ example: '2025-01-01T00:00:00.000Z',
2437
+ format: 'date-time',
1706
2438
  nullable: true
1707
2439
  },
2440
+ isActive: {
2441
+ type: 'boolean',
2442
+ description: 'Whether the profile is active',
2443
+ example: true
2444
+ },
1708
2445
  createdAt: {
1709
2446
  format: 'date-time',
1710
2447
  type: 'string',
1711
2448
  description: 'Creation timestamp',
1712
- example: '2024-11-03T10:00:00Z'
2449
+ example: '2025-01-01T00:00:00.000Z'
1713
2450
  },
1714
2451
  updatedAt: {
1715
2452
  format: 'date-time',
1716
2453
  type: 'string',
1717
2454
  description: 'Last update timestamp',
1718
- example: '2024-11-03T10:00:00Z'
2455
+ example: '2025-01-01T00:00:00.000Z'
1719
2456
  }
1720
2457
  },
1721
- required: ['id', 'symbol', 'date', 'metadata', 'createdAt', 'updatedAt']
2458
+ required: [
2459
+ 'id',
2460
+ 'canonical',
2461
+ 'aliases',
2462
+ 'category',
2463
+ 'countries',
2464
+ 'keywords',
2465
+ 'meta',
2466
+ 'dataSource',
2467
+ 'isActive',
2468
+ 'createdAt',
2469
+ 'updatedAt'
2470
+ ]
1722
2471
  } as const;
1723
2472
 
1724
- export const $CommodityListResponseDto = {
2473
+ export const $PayeeProfileListResponseDto = {
1725
2474
  type: 'object',
1726
2475
  properties: {
1727
2476
  items: {
1728
- description: 'List of commodities',
2477
+ description: 'List of payee profiles',
1729
2478
  type: 'array',
1730
2479
  items: {
1731
- $ref: '#/components/schemas/CommodityResponseDto'
2480
+ $ref: '#/components/schemas/PayeeProfileResponseDto'
1732
2481
  }
1733
2482
  },
1734
2483
  total: {
1735
2484
  type: 'number',
1736
- description: 'Total number of commodities',
1737
- example: 42
2485
+ description: 'Total count of matching records',
2486
+ example: 100
1738
2487
  }
1739
2488
  },
1740
2489
  required: ['items', 'total']
1741
2490
  } as const;
1742
2491
 
1743
- export const $RecurringRuleResponseDto = {
2492
+ export const $UpdatePayeeProfileDto = {
1744
2493
  type: 'object',
1745
2494
  properties: {
1746
- id: {
2495
+ aliases: {
2496
+ description:
2497
+ 'Multi-language aliases for the payee. Used for matching user input in different languages.',
2498
+ example: ['Starbucks Coffee', 'SBUX'],
2499
+ type: 'array',
2500
+ items: {
2501
+ type: 'string'
2502
+ }
2503
+ },
2504
+ i18nKey: {
1747
2505
  type: 'string',
1748
- description: 'Rule ID'
2506
+ description:
2507
+ 'Translation key for i18n integration (XLIFF translation system)',
2508
+ example: 'payee.starbucks',
2509
+ maxLength: 100
1749
2510
  },
1750
- userId: {
2511
+ category: {
1751
2512
  type: 'string',
1752
- description: 'User ID'
2513
+ description: 'Payee category classification',
2514
+ enum: [
2515
+ 'RESTAURANT',
2516
+ 'CAFE',
2517
+ 'FAST_FOOD',
2518
+ 'BAR',
2519
+ 'SUPERMARKET',
2520
+ 'CONVENIENCE_STORE',
2521
+ 'SHOPPING_MALL',
2522
+ 'ONLINE_SHOPPING',
2523
+ 'TAXI',
2524
+ 'RIDE_SHARING',
2525
+ 'PUBLIC_TRANSPORT',
2526
+ 'PARKING',
2527
+ 'GAS_STATION',
2528
+ 'UTILITIES',
2529
+ 'TELECOM',
2530
+ 'STREAMING',
2531
+ 'HEALTHCARE',
2532
+ 'EDUCATION',
2533
+ 'ENTERTAINMENT',
2534
+ 'SPORTS',
2535
+ 'TRAVEL',
2536
+ 'HOTEL',
2537
+ 'OTHER'
2538
+ ],
2539
+ example: 'CAFE'
1753
2540
  },
1754
- name: {
2541
+ subCategory: {
1755
2542
  type: 'string',
1756
- description: 'Rule name'
2543
+ description: 'Sub-category for more specific classification',
2544
+ example: 'coffee_chain',
2545
+ maxLength: 100
1757
2546
  },
1758
- icon: {
1759
- type: 'object',
1760
- description: 'Icon emoji'
2547
+ countries: {
2548
+ description:
2549
+ 'Country/region codes where the payee operates (ISO 3166-1 alpha-2)',
2550
+ example: ['CN', 'US', 'JP'],
2551
+ type: 'array',
2552
+ items: {
2553
+ type: 'string'
2554
+ }
1761
2555
  },
1762
- frequency: {
2556
+ primaryCountry: {
1763
2557
  type: 'string',
1764
- description: 'Recurring frequency'
2558
+ description: 'Primary operating country (ISO 3166-1 alpha-2)',
2559
+ example: 'US',
2560
+ maxLength: 2
1765
2561
  },
1766
- expectedAmount: {
2562
+ keywords: {
2563
+ description: 'Search keywords for fuzzy matching',
2564
+ example: ['coffee', 'cafe', 'drinks'],
2565
+ type: 'array',
2566
+ items: {
2567
+ type: 'string'
2568
+ }
2569
+ },
2570
+ logoUrl: {
2571
+ type: 'string',
2572
+ description: 'Payee logo URL',
2573
+ example: 'https://example.com/logo.png'
2574
+ },
2575
+ website: {
2576
+ type: 'string',
2577
+ description: 'Official website URL',
2578
+ example: 'https://www.starbucks.com'
2579
+ },
2580
+ description: {
2581
+ type: 'string',
2582
+ description: 'Payee description',
2583
+ example: 'Global coffeehouse chain headquartered in Seattle',
2584
+ maxLength: 1000
2585
+ },
2586
+ meta: {
2587
+ type: 'object',
2588
+ description:
2589
+ 'Extended metadata (business hours, contact info, additional details)',
2590
+ example: {
2591
+ businessHours: '07:00-22:00',
2592
+ phone: '+1-800-782-7282'
2593
+ }
2594
+ },
2595
+ dataSource: {
2596
+ type: 'string',
2597
+ description: 'Data source for this profile',
2598
+ enum: ['MANUAL', 'IMPORT', 'API', 'CROWDSOURCED'],
2599
+ default: 'MANUAL'
2600
+ },
2601
+ isActive: {
2602
+ type: 'boolean',
2603
+ description: 'Whether the payee profile is active (soft delete)',
2604
+ example: true
2605
+ },
2606
+ verifiedAt: {
2607
+ type: 'string',
2608
+ description:
2609
+ 'Verification timestamp. Set to current time to verify, or null to unverify.',
2610
+ example: '2025-01-01T00:00:00.000Z',
2611
+ format: 'date-time',
2612
+ nullable: true
2613
+ }
2614
+ }
2615
+ } as const;
2616
+
2617
+ export const $CreateCommodityDto = {
2618
+ type: 'object',
2619
+ properties: {
2620
+ symbol: {
2621
+ type: 'string',
2622
+ description:
2623
+ 'Commodity symbol (e.g., AAPL, USD, BTC) - corresponds to Beancount currency field',
2624
+ example: 'AAPL',
2625
+ maxLength: 50
2626
+ },
2627
+ date: {
2628
+ type: 'string',
2629
+ description:
2630
+ 'Commodity definition date (ISO 8601, required per Beancount spec). Represents when this commodity was first defined in the accounting system.',
2631
+ example: '2024-01-01',
2632
+ format: 'date'
2633
+ },
2634
+ metadata: {
2635
+ type: 'object',
2636
+ description:
2637
+ 'Metadata (corresponds to Beancount meta field). Can contain name, assetClass, precision, note, tags, etc.',
2638
+ example: {
2639
+ name: 'Apple Inc.',
2640
+ assetClass: 'stock',
2641
+ precision: 2,
2642
+ note: 'Long-term investment',
2643
+ tags: ['tech', 'dividend']
2644
+ }
2645
+ }
2646
+ },
2647
+ required: ['symbol', 'date']
2648
+ } as const;
2649
+
2650
+ export const $CommodityResponseDto = {
2651
+ type: 'object',
2652
+ properties: {
2653
+ id: {
2654
+ type: 'string',
2655
+ description: 'Unique identifier',
2656
+ example: 'uuid-123-456'
2657
+ },
2658
+ userId: {
2659
+ type: 'string',
2660
+ description: 'User ID (owner of the commodity)',
2661
+ example: 'user-123',
2662
+ nullable: true
2663
+ },
2664
+ symbol: {
2665
+ type: 'string',
2666
+ description: 'Commodity symbol (corresponds to Beancount currency field)',
2667
+ example: 'AAPL'
2668
+ },
2669
+ date: {
2670
+ type: 'string',
2671
+ description:
2672
+ 'Commodity definition date (required per Beancount spec). Represents when this commodity was first defined in the accounting system.',
2673
+ example: '2024-01-01',
2674
+ format: 'date'
2675
+ },
2676
+ metadata: {
2677
+ type: 'object',
2678
+ description:
2679
+ 'Metadata (corresponds to Beancount meta field). Contains name, assetClass, precision, note, tags, etc.',
2680
+ example: {
2681
+ name: 'Apple Inc.',
2682
+ assetClass: 'stock',
2683
+ precision: 2,
2684
+ note: 'Tech stock',
2685
+ source: 'AUTO_CREATED'
2686
+ }
2687
+ },
2688
+ createdAt: {
2689
+ format: 'date-time',
2690
+ type: 'string',
2691
+ description: 'Creation timestamp',
2692
+ example: '2024-11-03T10:00:00Z'
2693
+ },
2694
+ updatedAt: {
2695
+ format: 'date-time',
2696
+ type: 'string',
2697
+ description: 'Last update timestamp',
2698
+ example: '2024-11-03T10:00:00Z'
2699
+ }
2700
+ },
2701
+ required: ['id', 'symbol', 'date', 'metadata', 'createdAt', 'updatedAt']
2702
+ } as const;
2703
+
2704
+ export const $CommodityListResponseDto = {
2705
+ type: 'object',
2706
+ properties: {
2707
+ items: {
2708
+ description: 'List of commodities',
2709
+ type: 'array',
2710
+ items: {
2711
+ $ref: '#/components/schemas/CommodityResponseDto'
2712
+ }
2713
+ },
2714
+ total: {
1767
2715
  type: 'number',
1768
- description: 'Expected amount'
2716
+ description: 'Total number of commodities',
2717
+ example: 42
2718
+ }
2719
+ },
2720
+ required: ['items', 'total']
2721
+ } as const;
2722
+
2723
+ export const $UpdateCommodityDto = {
2724
+ type: 'object',
2725
+ properties: {
2726
+ date: {
2727
+ type: 'string',
2728
+ description:
2729
+ 'Commodity definition date (ISO 8601). Represents when this commodity was first defined in the accounting system.',
2730
+ example: '2024-01-01',
2731
+ format: 'date'
1769
2732
  },
1770
- expectedDay: {
2733
+ metadata: {
1771
2734
  type: 'object',
1772
- description: 'Expected day of month'
2735
+ description:
2736
+ 'Metadata (corresponds to Beancount meta field). Will merge with existing metadata. Can contain name, assetClass, precision, note, tags, etc.',
2737
+ example: {
2738
+ name: 'Updated Apple Inc.',
2739
+ assetClass: 'equity',
2740
+ precision: 4,
2741
+ note: 'Updated investment strategy',
2742
+ lastReviewed: '2024-11-03'
2743
+ }
2744
+ }
2745
+ }
2746
+ } as const;
2747
+
2748
+ export const $CreateRecurringRuleDto = {
2749
+ type: 'object',
2750
+ properties: {
2751
+ name: {
2752
+ type: 'string',
2753
+ description: 'Rule name (unique per user)',
2754
+ maxLength: 100
2755
+ },
2756
+ icon: {
2757
+ type: 'string',
2758
+ description: 'Icon emoji',
2759
+ maxLength: 10
2760
+ },
2761
+ frequency: {
2762
+ type: 'string',
2763
+ description: 'Recurring frequency',
2764
+ enum: [
2765
+ 'WEEKLY',
2766
+ 'BIWEEKLY',
2767
+ 'MONTHLY',
2768
+ 'BIMONTHLY',
2769
+ 'QUARTERLY',
2770
+ 'YEARLY',
2771
+ 'CUSTOM'
2772
+ ]
2773
+ },
2774
+ expectedAmount: {
2775
+ type: 'number',
2776
+ description: 'Expected amount (positive number)',
2777
+ minimum: 0
2778
+ },
2779
+ expectedDay: {
2780
+ type: 'number',
2781
+ description: 'Expected day of month (1-31)',
2782
+ minimum: 1,
2783
+ maximum: 31
1773
2784
  },
1774
2785
  customIntervalDays: {
1775
- type: 'object',
1776
- description: 'Custom interval in days'
2786
+ type: 'number',
2787
+ description: 'Custom interval in days (required for CUSTOM frequency)',
2788
+ minimum: 1
1777
2789
  },
1778
2790
  currency: {
1779
2791
  type: 'string',
1780
- description: 'Currency code'
2792
+ description: 'Currency code',
2793
+ default: 'CNY',
2794
+ maxLength: 10
1781
2795
  },
1782
2796
  matchPayeePattern: {
1783
- type: 'object',
1784
- description: 'Payee matching pattern'
2797
+ type: 'string',
2798
+ description: 'Payee matching pattern (supports wildcards)',
2799
+ maxLength: 200
1785
2800
  },
1786
2801
  matchAmountTolerance: {
1787
2802
  type: 'number',
1788
- description: 'Amount tolerance percentage'
2803
+ description: 'Amount tolerance percentage (0-1)',
2804
+ default: 0.075,
2805
+ minimum: 0,
2806
+ maximum: 1
1789
2807
  },
1790
2808
  defaultExpenseAccount: {
1791
- type: 'object',
1792
- description: 'Default expense account'
2809
+ type: 'string',
2810
+ description: 'Default expense account for auto-create',
2811
+ maxLength: 200
1793
2812
  },
1794
2813
  defaultPaymentAccount: {
1795
- type: 'object',
1796
- description: 'Default payment account'
2814
+ type: 'string',
2815
+ description: 'Default payment account for auto-create',
2816
+ maxLength: 200
1797
2817
  },
1798
2818
  defaultPayee: {
1799
- type: 'object',
1800
- description: 'Default payee'
1801
- },
1802
- isActive: {
1803
- type: 'boolean',
1804
- description: 'Whether rule is active'
1805
- },
1806
- startDate: {
1807
2819
  type: 'string',
1808
- description: 'Rule start date (YYYY-MM-DD)'
1809
- },
1810
- endDate: {
1811
- type: 'object',
1812
- description: 'Rule end date (YYYY-MM-DD)'
2820
+ description: 'Default payee for auto-create',
2821
+ maxLength: 200
1813
2822
  },
1814
2823
  autoCreate: {
1815
2824
  type: 'boolean',
1816
- description: 'Auto-create transaction on expected date'
1817
- },
1818
- lastOccurrence: {
1819
- type: 'object',
1820
- description: 'Last matched occurrence date (YYYY-MM-DD)'
1821
- },
1822
- totalCount: {
1823
- type: 'number',
1824
- description: 'Total matched transactions count'
2825
+ description: 'Auto-create transaction when expected date arrives',
2826
+ default: false
1825
2827
  },
1826
- createdAt: {
1827
- format: 'date-time',
2828
+ startDate: {
1828
2829
  type: 'string',
1829
- description: 'Created at timestamp'
2830
+ description: 'Rule start date (ISO format)'
1830
2831
  },
1831
- updatedAt: {
1832
- format: 'date-time',
2832
+ endDate: {
1833
2833
  type: 'string',
1834
- description: 'Updated at timestamp'
2834
+ description: 'Rule end date (ISO format)'
1835
2835
  }
1836
2836
  },
1837
2837
  required: [
1838
- 'id',
1839
- 'userId',
1840
2838
  'name',
1841
2839
  'frequency',
1842
2840
  'expectedAmount',
1843
2841
  'currency',
1844
2842
  'matchAmountTolerance',
1845
- 'isActive',
1846
- 'startDate',
1847
- 'autoCreate',
1848
- 'totalCount',
1849
- 'createdAt',
1850
- 'updatedAt'
2843
+ 'autoCreate'
1851
2844
  ]
1852
2845
  } as const;
1853
2846
 
1854
- export const $RecurringRuleWithStatsResponseDto = {
2847
+ export const $RecurringRuleResponseDto = {
1855
2848
  type: 'object',
1856
2849
  properties: {
1857
2850
  id: {
@@ -1943,19 +2936,161 @@ export const $RecurringRuleWithStatsResponseDto = {
1943
2936
  format: 'date-time',
1944
2937
  type: 'string',
1945
2938
  description: 'Updated at timestamp'
1946
- },
1947
- pendingCount: {
1948
- type: 'number',
1949
- description: 'Number of pending expected transactions'
1950
- },
1951
- overdueCount: {
1952
- type: 'number',
1953
- description: 'Number of overdue expected transactions'
1954
- },
1955
- nextExpectedDate: {
1956
- type: 'object',
1957
- description: 'Next expected date (YYYY-MM-DD)'
1958
- },
2939
+ }
2940
+ },
2941
+ required: [
2942
+ 'id',
2943
+ 'userId',
2944
+ 'name',
2945
+ 'frequency',
2946
+ 'expectedAmount',
2947
+ 'currency',
2948
+ 'matchAmountTolerance',
2949
+ 'isActive',
2950
+ 'startDate',
2951
+ 'autoCreate',
2952
+ 'totalCount',
2953
+ 'createdAt',
2954
+ 'updatedAt'
2955
+ ]
2956
+ } as const;
2957
+
2958
+ export const $CreateRuleFromTransactionDto = {
2959
+ type: 'object',
2960
+ properties: {
2961
+ frequency: {
2962
+ type: 'string',
2963
+ description: 'Recurring frequency',
2964
+ enum: [
2965
+ 'WEEKLY',
2966
+ 'BIWEEKLY',
2967
+ 'MONTHLY',
2968
+ 'BIMONTHLY',
2969
+ 'QUARTERLY',
2970
+ 'YEARLY',
2971
+ 'CUSTOM'
2972
+ ],
2973
+ example: 'MONTHLY'
2974
+ },
2975
+ name: {
2976
+ type: 'string',
2977
+ description: 'Optional name override (default: transaction payee)',
2978
+ maxLength: 100
2979
+ },
2980
+ icon: {
2981
+ type: 'string',
2982
+ description: 'Optional icon emoji',
2983
+ maxLength: 10
2984
+ }
2985
+ },
2986
+ required: ['frequency']
2987
+ } as const;
2988
+
2989
+ export const $RecurringRuleWithStatsResponseDto = {
2990
+ type: 'object',
2991
+ properties: {
2992
+ id: {
2993
+ type: 'string',
2994
+ description: 'Rule ID'
2995
+ },
2996
+ userId: {
2997
+ type: 'string',
2998
+ description: 'User ID'
2999
+ },
3000
+ name: {
3001
+ type: 'string',
3002
+ description: 'Rule name'
3003
+ },
3004
+ icon: {
3005
+ type: 'object',
3006
+ description: 'Icon emoji'
3007
+ },
3008
+ frequency: {
3009
+ type: 'string',
3010
+ description: 'Recurring frequency'
3011
+ },
3012
+ expectedAmount: {
3013
+ type: 'number',
3014
+ description: 'Expected amount'
3015
+ },
3016
+ expectedDay: {
3017
+ type: 'object',
3018
+ description: 'Expected day of month'
3019
+ },
3020
+ customIntervalDays: {
3021
+ type: 'object',
3022
+ description: 'Custom interval in days'
3023
+ },
3024
+ currency: {
3025
+ type: 'string',
3026
+ description: 'Currency code'
3027
+ },
3028
+ matchPayeePattern: {
3029
+ type: 'object',
3030
+ description: 'Payee matching pattern'
3031
+ },
3032
+ matchAmountTolerance: {
3033
+ type: 'number',
3034
+ description: 'Amount tolerance percentage'
3035
+ },
3036
+ defaultExpenseAccount: {
3037
+ type: 'object',
3038
+ description: 'Default expense account'
3039
+ },
3040
+ defaultPaymentAccount: {
3041
+ type: 'object',
3042
+ description: 'Default payment account'
3043
+ },
3044
+ defaultPayee: {
3045
+ type: 'object',
3046
+ description: 'Default payee'
3047
+ },
3048
+ isActive: {
3049
+ type: 'boolean',
3050
+ description: 'Whether rule is active'
3051
+ },
3052
+ startDate: {
3053
+ type: 'string',
3054
+ description: 'Rule start date (YYYY-MM-DD)'
3055
+ },
3056
+ endDate: {
3057
+ type: 'object',
3058
+ description: 'Rule end date (YYYY-MM-DD)'
3059
+ },
3060
+ autoCreate: {
3061
+ type: 'boolean',
3062
+ description: 'Auto-create transaction on expected date'
3063
+ },
3064
+ lastOccurrence: {
3065
+ type: 'object',
3066
+ description: 'Last matched occurrence date (YYYY-MM-DD)'
3067
+ },
3068
+ totalCount: {
3069
+ type: 'number',
3070
+ description: 'Total matched transactions count'
3071
+ },
3072
+ createdAt: {
3073
+ format: 'date-time',
3074
+ type: 'string',
3075
+ description: 'Created at timestamp'
3076
+ },
3077
+ updatedAt: {
3078
+ format: 'date-time',
3079
+ type: 'string',
3080
+ description: 'Updated at timestamp'
3081
+ },
3082
+ pendingCount: {
3083
+ type: 'number',
3084
+ description: 'Number of pending expected transactions'
3085
+ },
3086
+ overdueCount: {
3087
+ type: 'number',
3088
+ description: 'Number of overdue expected transactions'
3089
+ },
3090
+ nextExpectedDate: {
3091
+ type: 'object',
3092
+ description: 'Next expected date (YYYY-MM-DD)'
3093
+ },
1959
3094
  totalAmount: {
1960
3095
  type: 'number',
1961
3096
  description: 'Total amount of all matched transactions'
@@ -2009,6 +3144,94 @@ export const $RecurringRuleWithStatsResponseDto = {
2009
3144
  ]
2010
3145
  } as const;
2011
3146
 
3147
+ export const $UpdateRecurringRuleDto = {
3148
+ type: 'object',
3149
+ properties: {
3150
+ name: {
3151
+ type: 'string',
3152
+ description: 'Rule name',
3153
+ maxLength: 100
3154
+ },
3155
+ icon: {
3156
+ type: 'string',
3157
+ description: 'Icon emoji',
3158
+ maxLength: 10
3159
+ },
3160
+ frequency: {
3161
+ type: 'string',
3162
+ description: 'Recurring frequency',
3163
+ enum: [
3164
+ 'WEEKLY',
3165
+ 'BIWEEKLY',
3166
+ 'MONTHLY',
3167
+ 'BIMONTHLY',
3168
+ 'QUARTERLY',
3169
+ 'YEARLY',
3170
+ 'CUSTOM'
3171
+ ]
3172
+ },
3173
+ expectedAmount: {
3174
+ type: 'number',
3175
+ description: 'Expected amount',
3176
+ minimum: 0
3177
+ },
3178
+ expectedDay: {
3179
+ type: 'number',
3180
+ description: 'Expected day of month (1-31)',
3181
+ minimum: 1,
3182
+ maximum: 31
3183
+ },
3184
+ customIntervalDays: {
3185
+ type: 'number',
3186
+ description: 'Custom interval in days',
3187
+ minimum: 1
3188
+ },
3189
+ currency: {
3190
+ type: 'string',
3191
+ description: 'Currency code',
3192
+ maxLength: 10
3193
+ },
3194
+ matchPayeePattern: {
3195
+ type: 'string',
3196
+ description: 'Payee matching pattern',
3197
+ maxLength: 200
3198
+ },
3199
+ matchAmountTolerance: {
3200
+ type: 'number',
3201
+ description: 'Amount tolerance percentage (0-1)',
3202
+ minimum: 0,
3203
+ maximum: 1
3204
+ },
3205
+ defaultExpenseAccount: {
3206
+ type: 'string',
3207
+ description: 'Default expense account',
3208
+ maxLength: 200
3209
+ },
3210
+ defaultPaymentAccount: {
3211
+ type: 'string',
3212
+ description: 'Default payment account',
3213
+ maxLength: 200
3214
+ },
3215
+ defaultPayee: {
3216
+ type: 'string',
3217
+ description: 'Default payee',
3218
+ maxLength: 200
3219
+ },
3220
+ autoCreate: {
3221
+ type: 'boolean',
3222
+ description: 'Auto-create transaction'
3223
+ },
3224
+ isActive: {
3225
+ type: 'boolean',
3226
+ description: 'Rule active status'
3227
+ },
3228
+ endDate: {
3229
+ type: 'string',
3230
+ description: 'Rule end date (ISO format)'
3231
+ }
3232
+ }
3233
+ } as const;
3234
+
2012
3235
  export const $ExpectedTransactionRuleDto = {
2013
3236
  type: 'object',
2014
3237
  properties: {
@@ -2125,6 +3348,50 @@ export const $ExpectedTransactionListResponseDto = {
2125
3348
  required: ['items', 'total']
2126
3349
  } as const;
2127
3350
 
3351
+ export const $ConfirmMatchDto = {
3352
+ type: 'object',
3353
+ properties: {
3354
+ transactionId: {
3355
+ type: 'string',
3356
+ description: 'Transaction ID to match with'
3357
+ }
3358
+ },
3359
+ required: ['transactionId']
3360
+ } as const;
3361
+
3362
+ export const $EnterNowDto = {
3363
+ type: 'object',
3364
+ properties: {
3365
+ expenseAccount: {
3366
+ type: 'string',
3367
+ description:
3368
+ 'Override expense account (uses rule default if not provided)',
3369
+ maxLength: 200
3370
+ },
3371
+ paymentAccount: {
3372
+ type: 'string',
3373
+ description:
3374
+ 'Override payment account (uses rule default if not provided)',
3375
+ maxLength: 200
3376
+ },
3377
+ amount: {
3378
+ type: 'number',
3379
+ description: 'Override amount (uses expected amount if not provided)',
3380
+ minimum: 0
3381
+ },
3382
+ payee: {
3383
+ type: 'string',
3384
+ description: 'Override payee (uses rule default if not provided)',
3385
+ maxLength: 200
3386
+ },
3387
+ narration: {
3388
+ type: 'string',
3389
+ description: 'Optional narration',
3390
+ maxLength: 500
3391
+ }
3392
+ }
3393
+ } as const;
3394
+
2128
3395
  export const $ForecastItemDto = {
2129
3396
  type: 'object',
2130
3397
  properties: {
@@ -2149,7 +3416,7 @@ export const $ForecastItemDto = {
2149
3416
  example: '2024-04-01'
2150
3417
  },
2151
3418
  icon: {
2152
- type: 'object',
3419
+ type: 'string',
2153
3420
  description: 'Rule icon emoji',
2154
3421
  example: '🏠',
2155
3422
  nullable: true
@@ -2249,6 +3516,107 @@ export const $ForecastResponseDto = {
2249
3516
  ]
2250
3517
  } as const;
2251
3518
 
3519
+ export const $CreateTransactionRuleDto = {
3520
+ type: 'object',
3521
+ properties: {
3522
+ name: {
3523
+ type: 'string',
3524
+ minLength: 1,
3525
+ maxLength: 100
3526
+ },
3527
+ description: {
3528
+ type: 'string',
3529
+ maxLength: 500
3530
+ },
3531
+ narrationKeywords: {
3532
+ items: {
3533
+ type: 'array'
3534
+ },
3535
+ maxItems: 50,
3536
+ type: 'array'
3537
+ },
3538
+ payeeKeywords: {
3539
+ items: {
3540
+ type: 'array'
3541
+ },
3542
+ maxItems: 50,
3543
+ type: 'array'
3544
+ },
3545
+ categoryKeywords: {
3546
+ items: {
3547
+ type: 'array'
3548
+ },
3549
+ maxItems: 50,
3550
+ type: 'array'
3551
+ },
3552
+ methodKeywords: {
3553
+ items: {
3554
+ type: 'array'
3555
+ },
3556
+ maxItems: 50,
3557
+ description: 'Payment method keywords (e.g., HuaBei, YuEBao)',
3558
+ type: 'array'
3559
+ },
3560
+ categoryAccount: {
3561
+ type: 'string',
3562
+ maxLength: 200,
3563
+ description:
3564
+ 'Destination account for expenses/income (e.g., Expenses:Food:Coffee)'
3565
+ },
3566
+ matchLogic: {
3567
+ type: 'string',
3568
+ enum: ['OR', 'AND'],
3569
+ default: 'OR'
3570
+ },
3571
+ amountMin: {
3572
+ type: 'number',
3573
+ minimum: 0,
3574
+ description: 'Minimum transaction amount (inclusive)'
3575
+ },
3576
+ amountMax: {
3577
+ type: 'number',
3578
+ minimum: 0,
3579
+ description: 'Maximum transaction amount (inclusive)'
3580
+ },
3581
+ priority: {
3582
+ type: 'number',
3583
+ default: 50,
3584
+ minimum: 0,
3585
+ maximum: 1000
3586
+ },
3587
+ additionalTags: {
3588
+ items: {
3589
+ type: 'array'
3590
+ },
3591
+ maxItems: 20,
3592
+ type: 'array'
3593
+ },
3594
+ additionalMetadata: {
3595
+ type: 'object'
3596
+ },
3597
+ upsertByPayee: {
3598
+ type: 'boolean',
3599
+ description:
3600
+ 'If true, update existing rule with matching payeeKeywords[0] instead of creating new rule'
3601
+ }
3602
+ },
3603
+ required: ['name', 'matchLogic', 'priority']
3604
+ } as const;
3605
+
3606
+ export const $AmountRangeDto = {
3607
+ type: 'object',
3608
+ properties: {
3609
+ min: {
3610
+ type: 'number',
3611
+ description: 'Minimum amount'
3612
+ },
3613
+ max: {
3614
+ type: 'number',
3615
+ description: 'Maximum amount'
3616
+ }
3617
+ }
3618
+ } as const;
3619
+
2252
3620
  export const $TransactionRuleResponseDto = {
2253
3621
  type: 'object',
2254
3622
  properties: {
@@ -2303,12 +3671,12 @@ export const $TransactionRuleResponseDto = {
2303
3671
  example: 'OR'
2304
3672
  },
2305
3673
  amountRange: {
2306
- type: 'object',
2307
3674
  description: 'Amount range for matching',
2308
- example: {
2309
- min: 0,
2310
- max: 100
2311
- }
3675
+ allOf: [
3676
+ {
3677
+ $ref: '#/components/schemas/AmountRangeDto'
3678
+ }
3679
+ ]
2312
3680
  },
2313
3681
  priority: {
2314
3682
  type: 'number',
@@ -2323,6 +3691,7 @@ export const $TransactionRuleResponseDto = {
2323
3691
  type: 'string',
2324
3692
  description: 'Learning source: NLP, REVIEW_CENTER, or null for manual',
2325
3693
  enum: ['NLP', 'REVIEW_CENTER'],
3694
+ nullable: true,
2326
3695
  example: 'REVIEW_CENTER'
2327
3696
  },
2328
3697
  autoApplyEnabled: {
@@ -2344,7 +3713,9 @@ export const $TransactionRuleResponseDto = {
2344
3713
  additionalMetadata: {
2345
3714
  type: 'object',
2346
3715
  description: 'Additional metadata',
2347
- example: {}
3716
+ additionalProperties: {
3717
+ type: 'string'
3718
+ }
2348
3719
  },
2349
3720
  createdAt: {
2350
3721
  format: 'date-time',
@@ -2517,6 +3888,64 @@ export const $ValidateRuleResponseDto = {
2517
3888
  required: ['valid', 'errors', 'warnings']
2518
3889
  } as const;
2519
3890
 
3891
+ export const $BulkCreateRulesDto = {
3892
+ type: 'object',
3893
+ properties: {
3894
+ rules: {
3895
+ items: {
3896
+ type: 'array'
3897
+ },
3898
+ description: 'Array of rules to import',
3899
+ type: 'array'
3900
+ },
3901
+ conflictStrategy: {
3902
+ type: 'string',
3903
+ enum: ['replace', 'skip'],
3904
+ default: 'skip',
3905
+ description:
3906
+ 'Conflict handling strategy: skip (default) ignores duplicates, replace soft-deletes existing rule'
3907
+ }
3908
+ },
3909
+ required: ['rules', 'conflictStrategy']
3910
+ } as const;
3911
+
3912
+ export const $BulkCreateRulesResponseDto = {
3913
+ type: 'object',
3914
+ properties: {
3915
+ successCount: {
3916
+ type: 'number',
3917
+ description: 'Number of successfully created rules'
3918
+ },
3919
+ failureCount: {
3920
+ type: 'number',
3921
+ description: 'Number of failed rules'
3922
+ },
3923
+ errors: {
3924
+ type: 'array',
3925
+ description: 'Error details for failed rules',
3926
+ items: {
3927
+ type: 'object',
3928
+ properties: {
3929
+ index: {
3930
+ type: 'number'
3931
+ },
3932
+ message: {
3933
+ type: 'string'
3934
+ }
3935
+ }
3936
+ }
3937
+ },
3938
+ createdRuleIds: {
3939
+ description: 'IDs of successfully created rules',
3940
+ type: 'array',
3941
+ items: {
3942
+ type: 'string'
3943
+ }
3944
+ }
3945
+ },
3946
+ required: ['successCount', 'failureCount', 'errors', 'createdRuleIds']
3947
+ } as const;
3948
+
2520
3949
  export const $ExportRulesResponseDto = {
2521
3950
  type: 'object',
2522
3951
  properties: {
@@ -2597,6 +4026,89 @@ export const $RuleStatisticsResponseDto = {
2597
4026
  ]
2598
4027
  } as const;
2599
4028
 
4029
+ export const $UpdateTransactionRuleDto = {
4030
+ type: 'object',
4031
+ properties: {
4032
+ name: {
4033
+ type: 'string',
4034
+ minLength: 1,
4035
+ maxLength: 100
4036
+ },
4037
+ description: {
4038
+ type: 'string',
4039
+ maxLength: 500
4040
+ },
4041
+ narrationKeywords: {
4042
+ items: {
4043
+ type: 'array'
4044
+ },
4045
+ maxItems: 50,
4046
+ type: 'array'
4047
+ },
4048
+ payeeKeywords: {
4049
+ items: {
4050
+ type: 'array'
4051
+ },
4052
+ maxItems: 50,
4053
+ type: 'array'
4054
+ },
4055
+ categoryKeywords: {
4056
+ items: {
4057
+ type: 'array'
4058
+ },
4059
+ maxItems: 50,
4060
+ type: 'array'
4061
+ },
4062
+ methodKeywords: {
4063
+ items: {
4064
+ type: 'array'
4065
+ },
4066
+ maxItems: 50,
4067
+ description: 'Payment method keywords (e.g., HuaBei, YuEBao)',
4068
+ type: 'array'
4069
+ },
4070
+ categoryAccount: {
4071
+ type: 'string',
4072
+ maxLength: 200,
4073
+ description:
4074
+ 'Destination account for expenses/income (e.g., Expenses:Food:Coffee)'
4075
+ },
4076
+ matchLogic: {
4077
+ type: 'string',
4078
+ enum: ['OR', 'AND']
4079
+ },
4080
+ amountMin: {
4081
+ type: 'number',
4082
+ minimum: 0,
4083
+ description: 'Minimum transaction amount (inclusive)'
4084
+ },
4085
+ amountMax: {
4086
+ type: 'number',
4087
+ minimum: 0,
4088
+ description: 'Maximum transaction amount (inclusive)'
4089
+ },
4090
+ priority: {
4091
+ type: 'number',
4092
+ minimum: 0,
4093
+ maximum: 1000
4094
+ },
4095
+ enabled: {
4096
+ type: 'boolean',
4097
+ description: 'Enable or disable the rule'
4098
+ },
4099
+ additionalTags: {
4100
+ items: {
4101
+ type: 'array'
4102
+ },
4103
+ maxItems: 20,
4104
+ type: 'array'
4105
+ },
4106
+ additionalMetadata: {
4107
+ type: 'object'
4108
+ }
4109
+ }
4110
+ } as const;
4111
+
2600
4112
  export const $TestRuleDto = {
2601
4113
  type: 'object',
2602
4114
  properties: {
@@ -2789,31 +4301,180 @@ export const $UpdateUserSettingDto = {
2789
4301
  }
2790
4302
  } as const;
2791
4303
 
2792
- export const $FileImportDto = {
4304
+ export const $UpdatePropertyDto = {
2793
4305
  type: 'object',
2794
4306
  properties: {
2795
- file: {
4307
+ value: {
2796
4308
  type: 'string',
2797
- format: 'binary',
2798
- description: 'Bill file to import (CSV, PDF, OFX, etc.)',
2799
- example: 'alipay.csv'
4309
+ description: 'Property value'
2800
4310
  }
2801
4311
  },
2802
- required: ['file']
4312
+ required: ['value']
2803
4313
  } as const;
2804
4314
 
2805
- export const $ImportErrorDto = {
4315
+ export const $CreateBeanEventDto = {
2806
4316
  type: 'object',
2807
4317
  properties: {
2808
- index: {
2809
- type: 'number',
2810
- description: 'Index of failed transaction in the file',
2811
- example: 5
4318
+ date: {
4319
+ type: 'string',
4320
+ description: 'Life event date (ISO 8601)',
4321
+ example: '2024-03-15'
2812
4322
  },
2813
- error: {
4323
+ type: {
2814
4324
  type: 'string',
2815
- description: 'Error message',
2816
- example: 'Transaction does not balance: -100 USD != 0'
4325
+ description:
4326
+ 'Life event type (e.g., "employer", "location", "marital-status") — user-defined, no enum constraint at engine layer',
4327
+ example: 'employer'
4328
+ },
4329
+ description: {
4330
+ type: 'string',
4331
+ description:
4332
+ 'Life event description. Empty string is a VALID value (distinct from absence).',
4333
+ example: 'Acme Corp'
4334
+ },
4335
+ meta: {
4336
+ type: 'object',
4337
+ description:
4338
+ 'Product-side metadata (lives in BeanEvent.meta JSON, never in engine Event fields)',
4339
+ example: {
4340
+ note: 'Promotion'
4341
+ }
4342
+ }
4343
+ },
4344
+ required: ['date', 'type', 'description']
4345
+ } as const;
4346
+
4347
+ export const $EventResponseDto = {
4348
+ type: 'object',
4349
+ properties: {
4350
+ id: {
4351
+ type: 'string',
4352
+ description: 'Unique identifier',
4353
+ example: 'uuid-123-456'
4354
+ },
4355
+ userId: {
4356
+ type: 'string',
4357
+ description: 'User ID (owner of the life event)',
4358
+ example: 'user-123'
4359
+ },
4360
+ date: {
4361
+ type: 'string',
4362
+ description: 'Life event date (ISO 8601 format)',
4363
+ example: '2024-03-15',
4364
+ format: 'date'
4365
+ },
4366
+ type: {
4367
+ type: 'string',
4368
+ description:
4369
+ 'Life event type (user-defined, e.g., "employer", "location")',
4370
+ example: 'employer'
4371
+ },
4372
+ description: {
4373
+ type: 'string',
4374
+ description:
4375
+ 'Life event description. May be an empty string (a valid value distinct from absence).',
4376
+ example: 'Acme Corp'
4377
+ },
4378
+ meta: {
4379
+ type: 'object',
4380
+ description: 'Product-side metadata (free-form JSON)',
4381
+ example: {
4382
+ note: 'Promotion'
4383
+ }
4384
+ },
4385
+ createdAt: {
4386
+ format: 'date-time',
4387
+ type: 'string',
4388
+ description: 'Creation timestamp',
4389
+ example: '2024-03-15T10:00:00Z'
4390
+ },
4391
+ updatedAt: {
4392
+ format: 'date-time',
4393
+ type: 'string',
4394
+ description:
4395
+ 'Last update timestamp. Also emitted as the ETag response header for If-Match optimistic concurrency.',
4396
+ example: '2024-03-15T10:00:00Z'
4397
+ }
4398
+ },
4399
+ required: [
4400
+ 'id',
4401
+ 'userId',
4402
+ 'date',
4403
+ 'type',
4404
+ 'description',
4405
+ 'meta',
4406
+ 'createdAt',
4407
+ 'updatedAt'
4408
+ ]
4409
+ } as const;
4410
+
4411
+ export const $EventListResponseDto = {
4412
+ type: 'object',
4413
+ properties: {
4414
+ items: {
4415
+ description: 'List of life events',
4416
+ type: 'array',
4417
+ items: {
4418
+ $ref: '#/components/schemas/EventResponseDto'
4419
+ }
4420
+ },
4421
+ total: {
4422
+ type: 'number',
4423
+ description: 'Total number of life events matching the query',
4424
+ example: 42
4425
+ }
4426
+ },
4427
+ required: ['items', 'total']
4428
+ } as const;
4429
+
4430
+ export const $UpdateBeanEventDto = {
4431
+ type: 'object',
4432
+ properties: {
4433
+ date: {
4434
+ type: 'string',
4435
+ description: 'Life event date (ISO 8601)'
4436
+ },
4437
+ type: {
4438
+ type: 'string',
4439
+ description: 'Life event type (user-defined)'
4440
+ },
4441
+ description: {
4442
+ type: 'string',
4443
+ description:
4444
+ 'Life event description. Empty string is a VALID value (distinct from absence).'
4445
+ },
4446
+ meta: {
4447
+ type: 'object',
4448
+ description: 'Product-side metadata (free-form JSON)'
4449
+ }
4450
+ }
4451
+ } as const;
4452
+
4453
+ export const $FileImportDto = {
4454
+ type: 'object',
4455
+ properties: {
4456
+ file: {
4457
+ type: 'string',
4458
+ format: 'binary',
4459
+ description: 'Bill file to import (CSV, PDF, OFX, etc.)',
4460
+ example: 'alipay.csv'
4461
+ }
4462
+ },
4463
+ required: ['file']
4464
+ } as const;
4465
+
4466
+ export const $ImportErrorDto = {
4467
+ type: 'object',
4468
+ properties: {
4469
+ index: {
4470
+ type: 'number',
4471
+ description: 'Index of failed transaction in the file',
4472
+ example: 5
4473
+ },
4474
+ error: {
4475
+ type: 'string',
4476
+ description: 'Error message',
4477
+ example: 'Transaction does not balance: -100 USD != 0'
2817
4478
  }
2818
4479
  },
2819
4480
  required: ['index', 'error']
@@ -3088,7 +4749,8 @@ export const $ImporterConfigDto = {
3088
4749
  'cmbc-credit',
3089
4750
  'icbc',
3090
4751
  'icbc-credit',
3091
- 'hsbc-hk'
4752
+ 'hsbc-hk-credit',
4753
+ 'hsbc-hk-debit'
3092
4754
  ]
3093
4755
  },
3094
4756
  version: {
@@ -3134,6 +4796,181 @@ export const $ImporterConfigDto = {
3134
4796
  ]
3135
4797
  } as const;
3136
4798
 
4799
+ export const $UpdateMapperDefaultsDto = {
4800
+ type: 'object',
4801
+ properties: {
4802
+ sourceAccount: {
4803
+ type: 'string',
4804
+ description: 'Source account for transactions (Beancount format)',
4805
+ example: 'Assets:Alipay:Balance',
4806
+ pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
4807
+ },
4808
+ currency: {
4809
+ type: 'string',
4810
+ description: 'Default currency (ISO 4217 code)',
4811
+ example: 'CNY',
4812
+ minLength: 3,
4813
+ maxLength: 3,
4814
+ pattern: '^[A-Z]{3}$'
4815
+ },
4816
+ expenseAccount: {
4817
+ type: 'string',
4818
+ description: 'Default expense account (optional)',
4819
+ example: 'Expenses:Unknown',
4820
+ pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
4821
+ },
4822
+ incomeAccount: {
4823
+ type: 'string',
4824
+ description: 'Default income account (optional)',
4825
+ example: 'Income:Unknown',
4826
+ pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
4827
+ },
4828
+ methodAccountMapping: {
4829
+ type: 'object',
4830
+ description:
4831
+ '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).',
4832
+ example: {
4833
+ HuaBei: 'Liabilities:Alipay:Huabei',
4834
+ CreditCard: 'Liabilities:CreditCard'
4835
+ }
4836
+ }
4837
+ }
4838
+ } as const;
4839
+
4840
+ export const $UpdateConfigDataDto = {
4841
+ type: 'object',
4842
+ properties: {
4843
+ defaults: {
4844
+ description: 'Mapper defaults configuration',
4845
+ allOf: [
4846
+ {
4847
+ $ref: '#/components/schemas/UpdateMapperDefaultsDto'
4848
+ }
4849
+ ]
4850
+ }
4851
+ }
4852
+ } as const;
4853
+
4854
+ export const $UpdateImporterConfigDto = {
4855
+ type: 'object',
4856
+ properties: {
4857
+ data: {
4858
+ description: 'Configuration data (v1 schema)',
4859
+ allOf: [
4860
+ {
4861
+ $ref: '#/components/schemas/UpdateConfigDataDto'
4862
+ }
4863
+ ]
4864
+ }
4865
+ }
4866
+ } as const;
4867
+
4868
+ export const $CreatePlatformDto = {
4869
+ type: 'object',
4870
+ properties: {
4871
+ name: {
4872
+ type: 'string',
4873
+ description: 'Platform name',
4874
+ example: 'Binance'
4875
+ },
4876
+ canonical: {
4877
+ type: 'string',
4878
+ description: 'Platform canonical identifier (lowercase, kebab-case)',
4879
+ example: 'binance'
4880
+ },
4881
+ aliases: {
4882
+ description: 'Platform aliases (multi-language names for lookup)',
4883
+ example: ['Binance', 'Binance Exchange', 'BNB'],
4884
+ type: 'array',
4885
+ items: {
4886
+ type: 'string'
4887
+ }
4888
+ },
4889
+ url: {
4890
+ type: 'string',
4891
+ description: 'Platform URL',
4892
+ example: 'https://www.binance.com'
4893
+ },
4894
+ type: {
4895
+ type: 'string',
4896
+ description: 'Platform type',
4897
+ enum: [
4898
+ 'BANK',
4899
+ 'BROKERAGE',
4900
+ 'CRYPTO_EXCHANGE',
4901
+ 'PAYMENT',
4902
+ 'INVESTMENT',
4903
+ 'INSURANCE',
4904
+ 'OTHER'
4905
+ ],
4906
+ example: 'CRYPTO_EXCHANGE'
4907
+ },
4908
+ logoUrl: {
4909
+ type: 'string',
4910
+ description: 'Platform logo URL',
4911
+ example: 'https://example.com/logos/binance.png'
4912
+ },
4913
+ isActive: {
4914
+ type: 'boolean',
4915
+ description: 'Whether the platform is active',
4916
+ default: true
4917
+ }
4918
+ },
4919
+ required: ['name', 'canonical', 'aliases', 'url', 'type']
4920
+ } as const;
4921
+
4922
+ export const $UpdatePlatformDto = {
4923
+ type: 'object',
4924
+ properties: {
4925
+ name: {
4926
+ type: 'string',
4927
+ description: 'Platform name',
4928
+ example: 'Binance'
4929
+ },
4930
+ canonical: {
4931
+ type: 'string',
4932
+ description: 'Platform canonical identifier (lowercase, kebab-case)',
4933
+ example: 'binance'
4934
+ },
4935
+ aliases: {
4936
+ description: 'Platform aliases (multi-language names for lookup)',
4937
+ example: ['Binance', 'Binance Exchange', 'BNB'],
4938
+ type: 'array',
4939
+ items: {
4940
+ type: 'string'
4941
+ }
4942
+ },
4943
+ url: {
4944
+ type: 'string',
4945
+ description: 'Platform URL',
4946
+ example: 'https://www.binance.com'
4947
+ },
4948
+ type: {
4949
+ type: 'string',
4950
+ description: 'Platform type',
4951
+ enum: [
4952
+ 'BANK',
4953
+ 'BROKERAGE',
4954
+ 'CRYPTO_EXCHANGE',
4955
+ 'PAYMENT',
4956
+ 'INVESTMENT',
4957
+ 'INSURANCE',
4958
+ 'OTHER'
4959
+ ],
4960
+ example: 'CRYPTO_EXCHANGE'
4961
+ },
4962
+ logoUrl: {
4963
+ type: 'string',
4964
+ description: 'Platform logo URL',
4965
+ example: 'https://example.com/logos/binance.png'
4966
+ },
4967
+ isActive: {
4968
+ type: 'boolean',
4969
+ description: 'Whether the platform is active'
4970
+ }
4971
+ }
4972
+ } as const;
4973
+
3137
4974
  export const $ProviderSyncConfigDto = {
3138
4975
  type: 'object',
3139
4976
  properties: {
@@ -3268,7 +5105,8 @@ export const $SupportedProvidersResponseDto = {
3268
5105
  'gocardless',
3269
5106
  'simplefin',
3270
5107
  'yodlee',
3271
- 'beancount-direct'
5108
+ 'beancount-direct',
5109
+ 'parsed-bill'
3272
5110
  ],
3273
5111
  type: 'array',
3274
5112
  items: {
@@ -3279,6 +5117,16 @@ export const $SupportedProvidersResponseDto = {
3279
5117
  required: ['providers']
3280
5118
  } as const;
3281
5119
 
5120
+ export const $ParserTelemetryReportDto = {
5121
+ type: 'object',
5122
+ properties: {}
5123
+ } as const;
5124
+
5125
+ export const $UncoveredFormatMissDto = {
5126
+ type: 'object',
5127
+ properties: {}
5128
+ } as const;
5129
+
3282
5130
  export const $ProcessNlpDto = {
3283
5131
  type: 'object',
3284
5132
  properties: {
@@ -3293,6 +5141,16 @@ export const $ProcessNlpDto = {
3293
5141
  description:
3294
5142
  'Session ID for multi-turn conversation (auto-generated if not provided)',
3295
5143
  example: 'session_abc123'
5144
+ },
5145
+ parsedData: {
5146
+ type: 'object',
5147
+ description:
5148
+ 'Parsed data from previous NLP response for session recovery. Send back the parsedData received in confirm_payee/confirm responses.',
5149
+ example: {
5150
+ amount: 35,
5151
+ currency: 'CNY',
5152
+ payee: 'Starbucks'
5153
+ }
3296
5154
  }
3297
5155
  },
3298
5156
  required: ['message']
@@ -4007,116 +5865,663 @@ export const $NlpResponseDto = {
4007
5865
  'Account validation data (when action is "confirm_account"). Contains information about invalid account for user correction.',
4008
5866
  allOf: [
4009
5867
  {
4010
- $ref: '#/components/schemas/NlpAccountConfirmationDataDto'
5868
+ $ref: '#/components/schemas/NlpAccountConfirmationDataDto'
5869
+ }
5870
+ ]
5871
+ },
5872
+ payeeData: {
5873
+ description:
5874
+ 'Payee confirmation data (when action is "confirm_payee"). Contains information about medium/low confidence payee match for user confirmation.',
5875
+ allOf: [
5876
+ {
5877
+ $ref: '#/components/schemas/NlpPayeeConfirmationDataDto'
5878
+ }
5879
+ ]
5880
+ },
5881
+ confidence: {
5882
+ type: 'number',
5883
+ description: 'Overall confidence score (0-1)',
5884
+ example: 0.85
5885
+ },
5886
+ confidenceThreshold: {
5887
+ type: 'number',
5888
+ description:
5889
+ 'Confidence threshold for automatic creation (default: 0.75). When confidence < threshold, action will be "confirm" requiring user verification.',
5890
+ example: 0.75
5891
+ },
5892
+ recurringMatch: {
5893
+ description:
5894
+ 'Recurring transaction match info (when action is "created"). Contains match details when transaction matches a pending expected transaction.',
5895
+ allOf: [
5896
+ {
5897
+ $ref: '#/components/schemas/RecurringMatchInfoDto'
5898
+ }
5899
+ ]
5900
+ },
5901
+ recurringSuggestion: {
5902
+ description:
5903
+ 'Recurring rule creation suggestion (when action is "created"). Contains suggestion to create a recurring rule based on detected patterns. Only present when no existing rule matched and similar historical transactions were found.',
5904
+ allOf: [
5905
+ {
5906
+ $ref: '#/components/schemas/RecurringSuggestionDto'
5907
+ }
5908
+ ]
5909
+ },
5910
+ suggestedAccounts: {
5911
+ description:
5912
+ 'Suggested accounts for this transaction. Contains recommended source and destination accounts based on the detected intent and rules.',
5913
+ allOf: [
5914
+ {
5915
+ $ref: '#/components/schemas/NlpSuggestedAccountsDto'
5916
+ }
5917
+ ]
5918
+ },
5919
+ defaultAccounts: {
5920
+ description:
5921
+ 'Default accounts for the user/region. These are fallback accounts used when no specific suggestion is available.',
5922
+ allOf: [
5923
+ {
5924
+ $ref: '#/components/schemas/NlpDefaultAccountsDto'
5925
+ }
5926
+ ]
5927
+ }
5928
+ },
5929
+ required: ['status', 'action']
5930
+ } as const;
5931
+
5932
+ export const $BalanceByCurrencyDto = {
5933
+ type: 'object',
5934
+ properties: {
5935
+ currency: {
5936
+ type: 'string',
5937
+ description: 'ISO 4217 currency code',
5938
+ example: 'CNY'
5939
+ },
5940
+ balance: {
5941
+ type: 'string',
5942
+ description: 'Balance amount',
5943
+ example: '50000.00'
5944
+ }
5945
+ },
5946
+ required: ['currency', 'balance']
5947
+ } as const;
5948
+
5949
+ export const $NetWorthByCurrencyDto = {
5950
+ type: 'object',
5951
+ properties: {
5952
+ netWorth: {
5953
+ description: 'Net worth by currency',
5954
+ type: 'array',
5955
+ items: {
5956
+ $ref: '#/components/schemas/BalanceByCurrencyDto'
5957
+ }
5958
+ },
5959
+ assets: {
5960
+ description: 'Assets by currency',
5961
+ type: 'array',
5962
+ items: {
5963
+ $ref: '#/components/schemas/BalanceByCurrencyDto'
5964
+ }
5965
+ },
5966
+ liabilities: {
5967
+ description: 'Liabilities by currency',
5968
+ type: 'array',
5969
+ items: {
5970
+ $ref: '#/components/schemas/BalanceByCurrencyDto'
5971
+ }
5972
+ }
5973
+ },
5974
+ required: ['netWorth', 'assets', 'liabilities']
5975
+ } as const;
5976
+
5977
+ export const $ConvertedNetWorthDto = {
5978
+ type: 'object',
5979
+ properties: {
5980
+ baseCurrency: {
5981
+ type: 'string',
5982
+ description: 'Base currency for conversion',
5983
+ example: 'CNY'
5984
+ },
5985
+ netWorth: {
5986
+ type: 'string',
5987
+ description: 'Converted net worth',
5988
+ example: '500000.00'
5989
+ },
5990
+ assets: {
5991
+ type: 'string',
5992
+ description: 'Converted assets',
5993
+ example: '600000.00'
5994
+ },
5995
+ liabilities: {
5996
+ type: 'string',
5997
+ description: 'Converted liabilities',
5998
+ example: '100000.00'
5999
+ },
6000
+ exchangeRates: {
6001
+ type: 'object',
6002
+ description: 'Exchange rates used for conversion',
6003
+ example: {
6004
+ USD: '7.200000',
6005
+ EUR: '7.800000'
6006
+ }
6007
+ }
6008
+ },
6009
+ required: [
6010
+ 'baseCurrency',
6011
+ 'netWorth',
6012
+ 'assets',
6013
+ 'liabilities',
6014
+ 'exchangeRates'
6015
+ ]
6016
+ } as const;
6017
+
6018
+ export const $ExchangeRateWarningDto = {
6019
+ type: 'object',
6020
+ properties: {
6021
+ type: {
6022
+ type: 'string',
6023
+ description: 'Warning type',
6024
+ example: 'MISSING_EXCHANGE_RATE'
6025
+ },
6026
+ currency: {
6027
+ type: 'string',
6028
+ description: 'Currency without exchange rate',
6029
+ example: 'EUR'
6030
+ },
6031
+ totalAmount: {
6032
+ type: 'string',
6033
+ description: 'Total amount affected',
6034
+ example: '1000.00'
6035
+ }
6036
+ },
6037
+ required: ['type', 'currency', 'totalAmount']
6038
+ } as const;
6039
+
6040
+ export const $NetWorthResponseDto = {
6041
+ type: 'object',
6042
+ properties: {
6043
+ netWorth: {
6044
+ type: 'string',
6045
+ description:
6046
+ 'Total net worth (assets - liabilities, converted to base currency)',
6047
+ example: '500000.00'
6048
+ },
6049
+ assets: {
6050
+ type: 'string',
6051
+ description: 'Total assets value (converted)',
6052
+ example: '600000.00'
6053
+ },
6054
+ liabilities: {
6055
+ type: 'string',
6056
+ description: 'Total liabilities value (positive number, converted)',
6057
+ example: '100000.00'
6058
+ },
6059
+ monthlyReturn: {
6060
+ type: 'string',
6061
+ description: 'Monthly return (change from last month)',
6062
+ example: '15000.00'
6063
+ },
6064
+ monthlyReturnPercentage: {
6065
+ type: 'string',
6066
+ description: 'Monthly return percentage',
6067
+ example: '3.15'
6068
+ },
6069
+ currency: {
6070
+ type: 'string',
6071
+ description: 'Base currency code',
6072
+ example: 'CNY'
6073
+ },
6074
+ asOf: {
6075
+ type: 'string',
6076
+ description: 'Data as of date (ISO 8601)',
6077
+ example: '2024-06-15T00:00:00.000Z'
6078
+ },
6079
+ byCurrency: {
6080
+ description: 'Balances grouped by original currency',
6081
+ allOf: [
6082
+ {
6083
+ $ref: '#/components/schemas/NetWorthByCurrencyDto'
6084
+ }
6085
+ ]
6086
+ },
6087
+ converted: {
6088
+ description:
6089
+ 'Converted values in base currency (undefined if no exchange rates available)',
6090
+ allOf: [
6091
+ {
6092
+ $ref: '#/components/schemas/ConvertedNetWorthDto'
6093
+ }
6094
+ ]
6095
+ },
6096
+ warnings: {
6097
+ description: 'Exchange rate warnings',
6098
+ type: 'array',
6099
+ items: {
6100
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
6101
+ }
6102
+ }
6103
+ },
6104
+ required: [
6105
+ 'netWorth',
6106
+ 'assets',
6107
+ 'liabilities',
6108
+ 'monthlyReturn',
6109
+ 'monthlyReturnPercentage',
6110
+ 'currency',
6111
+ 'asOf'
6112
+ ]
6113
+ } as const;
6114
+
6115
+ export const $AccountItemDto = {
6116
+ type: 'object',
6117
+ properties: {
6118
+ id: {
6119
+ type: 'string',
6120
+ description: 'Account ID'
6121
+ },
6122
+ name: {
6123
+ type: 'string',
6124
+ description: 'Full account name',
6125
+ example: 'Assets:Bank:CMB:Savings'
6126
+ },
6127
+ displayName: {
6128
+ type: 'string',
6129
+ description: 'Display name (last part of account path)',
6130
+ example: 'Savings'
6131
+ },
6132
+ balance: {
6133
+ type: 'string',
6134
+ description: 'Account balance',
6135
+ example: '50000.00'
6136
+ },
6137
+ currency: {
6138
+ type: 'string',
6139
+ description: 'Currency code',
6140
+ example: 'CNY'
6141
+ }
6142
+ },
6143
+ required: ['id', 'name', 'displayName', 'balance', 'currency']
6144
+ } as const;
6145
+
6146
+ export const $PlatformGroupDto = {
6147
+ type: 'object',
6148
+ properties: {
6149
+ platformId: {
6150
+ type: 'string',
6151
+ description: 'Platform ID'
6152
+ },
6153
+ platformName: {
6154
+ type: 'string',
6155
+ description: 'Platform display name',
6156
+ example: 'CMB Bank'
6157
+ },
6158
+ accounts: {
6159
+ description: 'Accounts within this platform',
6160
+ type: 'array',
6161
+ items: {
6162
+ $ref: '#/components/schemas/AccountItemDto'
6163
+ }
6164
+ },
6165
+ totalBalance: {
6166
+ type: 'string',
6167
+ description: 'Total balance across all accounts in platform',
6168
+ example: '100000.00'
6169
+ }
6170
+ },
6171
+ required: ['platformId', 'platformName', 'accounts', 'totalBalance']
6172
+ } as const;
6173
+
6174
+ export const $AccountsSummaryDto = {
6175
+ type: 'object',
6176
+ properties: {
6177
+ totalAccounts: {
6178
+ type: 'number',
6179
+ description: 'Total number of accounts'
6180
+ },
6181
+ totalPlatforms: {
6182
+ type: 'number',
6183
+ description: 'Total number of platforms'
6184
+ }
6185
+ },
6186
+ required: ['totalAccounts', 'totalPlatforms']
6187
+ } as const;
6188
+
6189
+ export const $AccountsResponseDto = {
6190
+ type: 'object',
6191
+ properties: {
6192
+ groups: {
6193
+ description: 'Account groups by platform',
6194
+ type: 'array',
6195
+ items: {
6196
+ $ref: '#/components/schemas/PlatformGroupDto'
6197
+ }
6198
+ },
6199
+ summary: {
6200
+ description: 'Summary statistics',
6201
+ allOf: [
6202
+ {
6203
+ $ref: '#/components/schemas/AccountsSummaryDto'
6204
+ }
6205
+ ]
6206
+ }
6207
+ },
6208
+ required: ['groups', 'summary']
6209
+ } as const;
6210
+
6211
+ export const $AccountItemWithAssetClassDto = {
6212
+ type: 'object',
6213
+ properties: {
6214
+ id: {
6215
+ type: 'string',
6216
+ description: 'Account ID'
6217
+ },
6218
+ name: {
6219
+ type: 'string',
6220
+ description: 'Full account name',
6221
+ example: 'Assets:Bank:CMB:Savings'
6222
+ },
6223
+ displayName: {
6224
+ type: 'string',
6225
+ description: 'Display name (last part of account path)',
6226
+ example: 'Savings'
6227
+ },
6228
+ balance: {
6229
+ type: 'string',
6230
+ description: 'Account balance',
6231
+ example: '50000.00'
6232
+ },
6233
+ currency: {
6234
+ type: 'string',
6235
+ description: 'Currency code',
6236
+ example: 'CNY'
6237
+ },
6238
+ assetClass: {
6239
+ type: 'string',
6240
+ description: 'Asset class',
6241
+ example: 'LIQUIDITY'
6242
+ },
6243
+ assetSubClass: {
6244
+ type: 'string',
6245
+ description: 'Asset sub-class (Prisma-compatible)',
6246
+ example: 'RETIREMENT_ACCOUNT'
6247
+ },
6248
+ regionalSubClass: {
6249
+ type: 'string',
6250
+ description: 'Regional sub-class (region-specific, for display)',
6251
+ example: 'FOUR_ZERO_ONE_K'
6252
+ },
6253
+ riskLevel: {
6254
+ type: 'string',
6255
+ description: 'Risk level',
6256
+ example: 'LOW'
6257
+ },
6258
+ source: {
6259
+ type: 'string',
6260
+ description:
6261
+ 'ADR-0105 classification provenance (holding level always; account level only on FALLBACK)',
6262
+ enum: ['USER_META', 'FIAT_CURRENCY', 'OPENBB_MAPPING', 'FALLBACK']
6263
+ }
6264
+ },
6265
+ required: ['id', 'name', 'displayName', 'balance', 'currency', 'assetClass']
6266
+ } as const;
6267
+
6268
+ export const $AssetClassGroupDto = {
6269
+ type: 'object',
6270
+ properties: {
6271
+ assetClass: {
6272
+ type: 'string',
6273
+ description: 'Asset class name',
6274
+ example: 'LIQUIDITY',
6275
+ enum: [
6276
+ 'LIQUIDITY',
6277
+ 'EQUITY',
6278
+ 'FIXED_INCOME',
6279
+ 'PRECIOUS_METALS',
6280
+ 'COMMODITY',
6281
+ 'INSURANCE',
6282
+ 'ALTERNATIVE_INVESTMENT',
6283
+ 'PERSONAL_ASSETS',
6284
+ 'LIABILITY',
6285
+ 'REAL_ESTATE',
6286
+ 'INDEX'
6287
+ ]
6288
+ },
6289
+ assetSubClass: {
6290
+ type: 'string',
6291
+ description: 'Asset sub-class name',
6292
+ example: 'DEPOSIT'
6293
+ },
6294
+ accounts: {
6295
+ description: 'Accounts within this asset class',
6296
+ type: 'array',
6297
+ items: {
6298
+ $ref: '#/components/schemas/AccountItemWithAssetClassDto'
6299
+ }
6300
+ },
6301
+ balanceByCurrency: {
6302
+ description: 'Balances grouped by currency',
6303
+ type: 'array',
6304
+ items: {
6305
+ $ref: '#/components/schemas/BalanceByCurrencyDto'
6306
+ }
6307
+ },
6308
+ convertedBalance: {
6309
+ type: 'string',
6310
+ description: 'Converted balance in base currency',
6311
+ example: '100000.00'
6312
+ }
6313
+ },
6314
+ required: ['assetClass', 'accounts', 'balanceByCurrency']
6315
+ } as const;
6316
+
6317
+ export const $AccountExchangeRateWarningDto = {
6318
+ type: 'object',
6319
+ properties: {
6320
+ type: {
6321
+ type: 'string',
6322
+ description: 'Warning type',
6323
+ example: 'MISSING_EXCHANGE_RATE'
6324
+ },
6325
+ currency: {
6326
+ type: 'string',
6327
+ description: 'Currency without exchange rate',
6328
+ example: 'USD'
6329
+ },
6330
+ accounts: {
6331
+ description: 'Affected account paths',
6332
+ type: 'array',
6333
+ items: {
6334
+ type: 'string'
6335
+ }
6336
+ },
6337
+ totalAmount: {
6338
+ type: 'string',
6339
+ description: 'Total amount in this currency',
6340
+ example: '5000.00'
6341
+ }
6342
+ },
6343
+ required: ['type', 'currency', 'accounts', 'totalAmount']
6344
+ } as const;
6345
+
6346
+ export const $AssetClassSummaryDto = {
6347
+ type: 'object',
6348
+ properties: {
6349
+ totalAccounts: {
6350
+ type: 'number',
6351
+ description: 'Total number of accounts'
6352
+ },
6353
+ totalAssetClasses: {
6354
+ type: 'number',
6355
+ description: 'Total number of asset classes'
6356
+ },
6357
+ baseCurrency: {
6358
+ type: 'string',
6359
+ description: 'Base currency for conversion',
6360
+ example: 'CNY'
6361
+ },
6362
+ warnings: {
6363
+ description: 'Exchange rate warnings',
6364
+ type: 'array',
6365
+ items: {
6366
+ $ref: '#/components/schemas/AccountExchangeRateWarningDto'
6367
+ }
6368
+ },
6369
+ fallback: {
6370
+ type: 'object',
6371
+ description:
6372
+ '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.'
6373
+ }
6374
+ },
6375
+ required: ['totalAccounts', 'totalAssetClasses', 'baseCurrency']
6376
+ } as const;
6377
+
6378
+ export const $AssetClassAccountsResponseDto = {
6379
+ type: 'object',
6380
+ properties: {
6381
+ groups: {
6382
+ description: 'Account groups by asset class',
6383
+ type: 'array',
6384
+ items: {
6385
+ $ref: '#/components/schemas/AssetClassGroupDto'
6386
+ }
6387
+ },
6388
+ summary: {
6389
+ description: 'Summary statistics',
6390
+ allOf: [
6391
+ {
6392
+ $ref: '#/components/schemas/AssetClassSummaryDto'
4011
6393
  }
4012
6394
  ]
4013
6395
  },
4014
- payeeData: {
6396
+ uncategorized: {
4015
6397
  description:
4016
- 'Payee confirmation data (when action is "confirm_payee"). Contains information about medium/low confidence payee match for user confirmation.',
6398
+ 'ADR-0105 §6 holding-level grey-area bucket (source=FALLBACK holdings peeled out of groups). Present only for groupBy=holdingAssetClass when FALLBACK holdings exist.',
4017
6399
  allOf: [
4018
6400
  {
4019
- $ref: '#/components/schemas/NlpPayeeConfirmationDataDto'
6401
+ $ref: '#/components/schemas/AssetClassGroupDto'
4020
6402
  }
4021
6403
  ]
6404
+ }
6405
+ },
6406
+ required: ['groups', 'summary']
6407
+ } as const;
6408
+
6409
+ export const $HoldingAssetClassAccountSliceDto = {
6410
+ type: 'object',
6411
+ properties: {
6412
+ accountId: {
6413
+ type: 'string',
6414
+ description: 'Account ID'
4022
6415
  },
4023
- confidence: {
4024
- type: 'number',
4025
- description: 'Overall confidence score (0-1)',
4026
- example: 0.85
6416
+ accountPath: {
6417
+ type: 'string',
6418
+ description: 'Full account path',
6419
+ example: 'Assets:US:Investments:Brokerage'
4027
6420
  },
4028
- confidenceThreshold: {
4029
- type: 'number',
6421
+ accountCurrency: {
6422
+ type: 'string',
4030
6423
  description:
4031
- 'Confidence threshold for automatic creation (default: 0.75). When confidence < threshold, action will be "confirm" requiring user verification.',
4032
- example: 0.75
6424
+ 'Currency of the holding with the largest converted base value; undefined when no holding is convertible',
6425
+ example: 'USD'
4033
6426
  },
4034
- recurringMatch: {
6427
+ marketValueBase: {
6428
+ type: 'string',
4035
6429
  description:
4036
- 'Recurring transaction match info (when action is "created"). Contains match details when transaction matches a pending expected transaction.',
4037
- allOf: [
4038
- {
4039
- $ref: '#/components/schemas/RecurringMatchInfoDto'
4040
- }
4041
- ]
6430
+ "Account's market value in base currency (Σ converted holdings; grey bucket included)",
6431
+ example: '50000.00'
4042
6432
  },
4043
- recurringSuggestion: {
6433
+ shareOfTotalPct: {
6434
+ type: 'number',
4044
6435
  description:
4045
- 'Recurring rule creation suggestion (when action is "created"). Contains suggestion to create a recurring rule based on detected patterns. Only present when no existing rule matched and similar historical transactions were found.',
4046
- allOf: [
4047
- {
4048
- $ref: '#/components/schemas/RecurringSuggestionDto'
4049
- }
4050
- ]
6436
+ 'Share of the global total (0-100). 0 when globalTotal is zero (no NaN/Infinity).',
6437
+ example: 42.5
4051
6438
  },
4052
- suggestedAccounts: {
6439
+ groups: {
6440
+ description: 'Per-account asset-class breakdown',
6441
+ type: 'array',
6442
+ items: {
6443
+ $ref: '#/components/schemas/AssetClassGroupDto'
6444
+ }
6445
+ },
6446
+ uncategorized: {
4053
6447
  description:
4054
- 'Suggested accounts for this transaction. Contains recommended source and destination accounts based on the detected intent and rules.',
6448
+ 'Per-account grey bucket (source=FALLBACK holdings, incl. broker cash)',
4055
6449
  allOf: [
4056
6450
  {
4057
- $ref: '#/components/schemas/NlpSuggestedAccountsDto'
6451
+ $ref: '#/components/schemas/AssetClassGroupDto'
4058
6452
  }
4059
6453
  ]
4060
6454
  },
4061
- defaultAccounts: {
6455
+ holdings: {
4062
6456
  description:
4063
- 'Default accounts for the user/region. These are fallback accounts used when no specific suggestion is available.',
4064
- allOf: [
4065
- {
4066
- $ref: '#/components/schemas/NlpDefaultAccountsDto'
4067
- }
4068
- ]
6457
+ 'Every holding row for this account (account ID in each row’s `id` field)',
6458
+ type: 'array',
6459
+ items: {
6460
+ $ref: '#/components/schemas/AccountItemWithAssetClassDto'
6461
+ }
4069
6462
  }
4070
6463
  },
4071
- required: ['status', 'action']
6464
+ required: [
6465
+ 'accountId',
6466
+ 'accountPath',
6467
+ 'marketValueBase',
6468
+ 'shareOfTotalPct',
6469
+ 'groups',
6470
+ 'holdings'
6471
+ ]
4072
6472
  } as const;
4073
6473
 
4074
- export const $BalanceByCurrencyDto = {
6474
+ export const $HoldingAssetClassCrossAccountResponseDto = {
4075
6475
  type: 'object',
4076
6476
  properties: {
4077
- currency: {
4078
- type: 'string',
4079
- description: 'ISO 4217 currency code',
4080
- example: 'CNY'
6477
+ global: {
6478
+ description: 'Merged cross-account holding aggregation',
6479
+ allOf: [
6480
+ {
6481
+ $ref: '#/components/schemas/AssetClassAccountsResponseDto'
6482
+ }
6483
+ ]
4081
6484
  },
4082
- balance: {
4083
- type: 'string',
4084
- description: 'Balance amount',
4085
- example: '50000.00'
6485
+ byAccount: {
6486
+ description: 'Per-account slices',
6487
+ type: 'array',
6488
+ items: {
6489
+ $ref: '#/components/schemas/HoldingAssetClassAccountSliceDto'
6490
+ }
4086
6491
  }
4087
6492
  },
4088
- required: ['currency', 'balance']
6493
+ required: ['global', 'byAccount']
4089
6494
  } as const;
4090
6495
 
4091
- export const $NetWorthByCurrencyDto = {
6496
+ export const $CashFlowByCurrencyDto = {
4092
6497
  type: 'object',
4093
6498
  properties: {
4094
- netWorth: {
4095
- description: 'Net worth by currency',
6499
+ income: {
6500
+ description: 'Income by currency',
4096
6501
  type: 'array',
4097
6502
  items: {
4098
6503
  $ref: '#/components/schemas/BalanceByCurrencyDto'
4099
6504
  }
4100
6505
  },
4101
- assets: {
4102
- description: 'Assets by currency',
6506
+ expense: {
6507
+ description: 'Expense by currency',
4103
6508
  type: 'array',
4104
6509
  items: {
4105
6510
  $ref: '#/components/schemas/BalanceByCurrencyDto'
4106
6511
  }
4107
6512
  },
4108
- liabilities: {
4109
- description: 'Liabilities by currency',
6513
+ netSavings: {
6514
+ description: 'Net savings by currency',
4110
6515
  type: 'array',
4111
6516
  items: {
4112
6517
  $ref: '#/components/schemas/BalanceByCurrencyDto'
4113
6518
  }
4114
6519
  }
4115
6520
  },
4116
- required: ['netWorth', 'assets', 'liabilities']
6521
+ required: ['income', 'expense', 'netSavings']
4117
6522
  } as const;
4118
6523
 
4119
- export const $ConvertedNetWorthDto = {
6524
+ export const $ConvertedCashFlowDto = {
4120
6525
  type: 'object',
4121
6526
  properties: {
4122
6527
  baseCurrency: {
@@ -4124,114 +6529,78 @@ export const $ConvertedNetWorthDto = {
4124
6529
  description: 'Base currency for conversion',
4125
6530
  example: 'CNY'
4126
6531
  },
4127
- netWorth: {
6532
+ income: {
4128
6533
  type: 'string',
4129
- description: 'Converted net worth',
4130
- example: '500000.00'
6534
+ description: 'Converted income',
6535
+ example: '25000.00'
4131
6536
  },
4132
- assets: {
6537
+ expense: {
4133
6538
  type: 'string',
4134
- description: 'Converted assets',
4135
- example: '600000.00'
6539
+ description: 'Converted expense',
6540
+ example: '18000.00'
4136
6541
  },
4137
- liabilities: {
6542
+ netSavings: {
4138
6543
  type: 'string',
4139
- description: 'Converted liabilities',
4140
- example: '100000.00'
6544
+ description: 'Converted net savings',
6545
+ example: '7000.00'
4141
6546
  },
4142
6547
  exchangeRates: {
4143
6548
  type: 'object',
4144
6549
  description: 'Exchange rates used for conversion',
4145
6550
  example: {
4146
- USD: '7.200000',
4147
- EUR: '7.800000'
6551
+ USD: '7.200000'
4148
6552
  }
4149
6553
  }
4150
6554
  },
4151
- required: [
4152
- 'baseCurrency',
4153
- 'netWorth',
4154
- 'assets',
4155
- 'liabilities',
4156
- 'exchangeRates'
4157
- ]
4158
- } as const;
4159
-
4160
- export const $ExchangeRateWarningDto = {
4161
- type: 'object',
4162
- properties: {
4163
- type: {
4164
- type: 'string',
4165
- description: 'Warning type',
4166
- example: 'MISSING_EXCHANGE_RATE'
4167
- },
4168
- currency: {
4169
- type: 'string',
4170
- description: 'Currency without exchange rate',
4171
- example: 'EUR'
4172
- },
4173
- totalAmount: {
4174
- type: 'string',
4175
- description: 'Total amount affected',
4176
- example: '1000.00'
4177
- }
4178
- },
4179
- required: ['type', 'currency', 'totalAmount']
6555
+ required: ['baseCurrency', 'income', 'expense', 'netSavings', 'exchangeRates']
4180
6556
  } as const;
4181
6557
 
4182
- export const $NetWorthResponseDto = {
6558
+ export const $CashFlowResponseDto = {
4183
6559
  type: 'object',
4184
6560
  properties: {
4185
- netWorth: {
6561
+ period: {
4186
6562
  type: 'string',
4187
- description:
4188
- 'Total net worth (assets - liabilities, converted to base currency)',
4189
- example: '500000.00'
6563
+ description: 'Period identifier (YYYY-MM)',
6564
+ example: '2024-06'
4190
6565
  },
4191
- assets: {
6566
+ income: {
4192
6567
  type: 'string',
4193
- description: 'Total assets value (converted)',
4194
- example: '600000.00'
6568
+ description: 'Total income for the period (converted)',
6569
+ example: '25000.00'
4195
6570
  },
4196
- liabilities: {
6571
+ expense: {
4197
6572
  type: 'string',
4198
- description: 'Total liabilities value (positive number, converted)',
4199
- example: '100000.00'
6573
+ description: 'Total expenses for the period (converted)',
6574
+ example: '18000.00'
4200
6575
  },
4201
- monthlyReturn: {
6576
+ netSavings: {
4202
6577
  type: 'string',
4203
- description: 'Monthly return (change from last month)',
4204
- example: '15000.00'
6578
+ description: 'Net savings (income - expense, converted)',
6579
+ example: '7000.00'
4205
6580
  },
4206
- monthlyReturnPercentage: {
6581
+ savingsRate: {
4207
6582
  type: 'string',
4208
- description: 'Monthly return percentage',
4209
- example: '3.15'
6583
+ description: 'Savings rate percentage (netSavings / income * 100)',
6584
+ example: '28.00'
4210
6585
  },
4211
6586
  currency: {
4212
6587
  type: 'string',
4213
6588
  description: 'Base currency code',
4214
6589
  example: 'CNY'
4215
6590
  },
4216
- asOf: {
4217
- type: 'string',
4218
- description: 'Data as of date (ISO 8601)',
4219
- example: '2024-06-15T00:00:00.000Z'
4220
- },
4221
6591
  byCurrency: {
4222
- description: 'Balances grouped by original currency',
6592
+ description: 'Cash flow grouped by original currency',
4223
6593
  allOf: [
4224
6594
  {
4225
- $ref: '#/components/schemas/NetWorthByCurrencyDto'
6595
+ $ref: '#/components/schemas/CashFlowByCurrencyDto'
4226
6596
  }
4227
6597
  ]
4228
6598
  },
4229
6599
  converted: {
4230
- description:
4231
- 'Converted values in base currency (undefined if no exchange rates available)',
6600
+ description: 'Converted values in base currency',
4232
6601
  allOf: [
4233
6602
  {
4234
- $ref: '#/components/schemas/ConvertedNetWorthDto'
6603
+ $ref: '#/components/schemas/ConvertedCashFlowDto'
4235
6604
  }
4236
6605
  ]
4237
6606
  },
@@ -4244,417 +6613,553 @@ export const $NetWorthResponseDto = {
4244
6613
  }
4245
6614
  },
4246
6615
  required: [
4247
- 'netWorth',
4248
- 'assets',
4249
- 'liabilities',
4250
- 'monthlyReturn',
4251
- 'monthlyReturnPercentage',
4252
- 'currency',
4253
- 'asOf'
6616
+ 'period',
6617
+ 'income',
6618
+ 'expense',
6619
+ 'netSavings',
6620
+ 'savingsRate',
6621
+ 'currency'
4254
6622
  ]
4255
6623
  } as const;
4256
6624
 
4257
- export const $AccountItemDto = {
6625
+ export const $CategoryGroupDto = {
4258
6626
  type: 'object',
4259
6627
  properties: {
4260
- id: {
4261
- type: 'string',
4262
- description: 'Account ID'
4263
- },
4264
- name: {
4265
- type: 'string',
4266
- description: 'Full account name',
4267
- example: 'Assets:Bank:CMB:Savings'
4268
- },
4269
- displayName: {
4270
- type: 'string',
4271
- description: 'Display name (last part of account path)',
4272
- example: 'Savings'
4273
- },
4274
- balance: {
6628
+ category: {
4275
6629
  type: 'string',
4276
- description: 'Account balance',
4277
- example: '50000.00'
6630
+ description:
6631
+ 'Functional category (account-path Group segment); regional and universal account paths merge under it',
6632
+ example: 'Food'
4278
6633
  },
4279
- currency: {
4280
- type: 'string',
4281
- description: 'Currency code',
4282
- example: 'CNY'
4283
- }
4284
- },
4285
- required: ['id', 'name', 'displayName', 'balance', 'currency']
4286
- } as const;
4287
-
4288
- export const $PlatformGroupDto = {
4289
- type: 'object',
4290
- properties: {
4291
- platformId: {
6634
+ totalExpense: {
4292
6635
  type: 'string',
4293
- description: 'Platform ID'
6636
+ description: 'Converted total expense in base currency',
6637
+ example: '1200.00'
4294
6638
  },
4295
- platformName: {
4296
- type: 'string',
4297
- description: 'Platform display name',
4298
- example: 'CMB Bank'
6639
+ sharePct: {
6640
+ type: 'number',
6641
+ description: 'Share of grand total (0-100); 0 when grand total is 0',
6642
+ example: 42.5
4299
6643
  },
4300
- accounts: {
4301
- description: 'Accounts within this platform',
6644
+ balanceByCurrency: {
6645
+ description: 'Raw (unconverted) expense per currency',
4302
6646
  type: 'array',
4303
6647
  items: {
4304
- $ref: '#/components/schemas/AccountItemDto'
6648
+ $ref: '#/components/schemas/BalanceByCurrencyDto'
4305
6649
  }
4306
6650
  },
4307
- totalBalance: {
6651
+ convertedBalance: {
4308
6652
  type: 'string',
4309
- description: 'Total balance across all accounts in platform',
4310
- example: '100000.00'
6653
+ description:
6654
+ 'Converted total in base currency (omitted when FX missing for all currencies in this category)',
6655
+ example: '1200.00'
4311
6656
  }
4312
6657
  },
4313
- required: ['platformId', 'platformName', 'accounts', 'totalBalance']
6658
+ required: ['category', 'totalExpense', 'sharePct', 'balanceByCurrency']
4314
6659
  } as const;
4315
6660
 
4316
- export const $AccountsSummaryDto = {
6661
+ export const $ExpensesByCategorySummaryDto = {
4317
6662
  type: 'object',
4318
6663
  properties: {
4319
- totalAccounts: {
4320
- type: 'number',
4321
- description: 'Total number of accounts'
6664
+ totalExpense: {
6665
+ type: 'string',
6666
+ description:
6667
+ 'Total expense across all categories (converted; convertible categories only)',
6668
+ example: '5000.00'
4322
6669
  },
4323
- totalPlatforms: {
6670
+ categoryCount: {
4324
6671
  type: 'number',
4325
- description: 'Total number of platforms'
6672
+ description: 'Number of categories',
6673
+ example: 8
4326
6674
  }
4327
6675
  },
4328
- required: ['totalAccounts', 'totalPlatforms']
6676
+ required: ['totalExpense', 'categoryCount']
4329
6677
  } as const;
4330
6678
 
4331
- export const $AccountsResponseDto = {
6679
+ export const $ExpensesByCategoryResponseDto = {
4332
6680
  type: 'object',
4333
6681
  properties: {
6682
+ period: {
6683
+ type: 'string',
6684
+ description: 'Period requested',
6685
+ example: '1m'
6686
+ },
6687
+ baseCurrency: {
6688
+ type: 'string',
6689
+ description: 'Base currency for converted values',
6690
+ example: 'CNY'
6691
+ },
4334
6692
  groups: {
4335
- description: 'Account groups by platform',
6693
+ description:
6694
+ 'Expense groups by functional category, sorted by converted total desc',
4336
6695
  type: 'array',
4337
6696
  items: {
4338
- $ref: '#/components/schemas/PlatformGroupDto'
6697
+ $ref: '#/components/schemas/CategoryGroupDto'
4339
6698
  }
4340
6699
  },
4341
6700
  summary: {
4342
6701
  description: 'Summary statistics',
4343
6702
  allOf: [
4344
6703
  {
4345
- $ref: '#/components/schemas/AccountsSummaryDto'
6704
+ $ref: '#/components/schemas/ExpensesByCategorySummaryDto'
4346
6705
  }
4347
6706
  ]
6707
+ },
6708
+ warnings: {
6709
+ description: 'Exchange rate warnings (e.g. missing rate for a currency)',
6710
+ type: 'array',
6711
+ items: {
6712
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
6713
+ }
4348
6714
  }
4349
6715
  },
4350
- required: ['groups', 'summary']
6716
+ required: ['period', 'baseCurrency', 'groups', 'summary']
4351
6717
  } as const;
4352
6718
 
4353
- export const $AccountItemWithAssetClassDto = {
6719
+ export const $MonetaryDto = {
4354
6720
  type: 'object',
4355
6721
  properties: {
4356
- id: {
4357
- type: 'string',
4358
- description: 'Account ID'
4359
- },
4360
- name: {
4361
- type: 'string',
4362
- description: 'Full account name',
4363
- example: 'Assets:Bank:CMB:Savings'
4364
- },
4365
- displayName: {
4366
- type: 'string',
4367
- description: 'Display name (last part of account path)',
4368
- example: 'Savings'
4369
- },
4370
- balance: {
6722
+ amount: {
4371
6723
  type: 'string',
4372
- description: 'Account balance',
4373
- example: '50000.00'
6724
+ description: 'Amount (Decimal string)',
6725
+ example: '3000'
4374
6726
  },
4375
6727
  currency: {
4376
6728
  type: 'string',
4377
- description: 'Currency code',
4378
- example: 'CNY'
6729
+ description: 'ISO 4217 currency',
6730
+ example: 'USD'
4379
6731
  },
4380
- assetClass: {
6732
+ baseCcyEquivalent: {
6733
+ type: 'object',
6734
+ description: 'Converted to user base currency (Decimal string)',
6735
+ example: '21600',
6736
+ nullable: true
6737
+ }
6738
+ },
6739
+ required: ['amount', 'currency']
6740
+ } as const;
6741
+
6742
+ export const $CurrentPriceDto = {
6743
+ type: 'object',
6744
+ properties: {
6745
+ amount: {
4381
6746
  type: 'string',
4382
- description: 'Asset class',
4383
- example: 'LIQUIDITY'
6747
+ description: 'Price amount (Decimal string)',
6748
+ example: '250'
4384
6749
  },
4385
- assetSubClass: {
6750
+ currency: {
4386
6751
  type: 'string',
4387
- description: 'Asset sub-class (Prisma-compatible)',
4388
- example: 'RETIREMENT_ACCOUNT'
6752
+ description: 'Price currency (ISO 4217)',
6753
+ example: 'USD'
4389
6754
  },
4390
- regionalSubClass: {
6755
+ date: {
4391
6756
  type: 'string',
4392
- description: 'Regional sub-class (region-specific, for display)',
4393
- example: 'FOUR_ZERO_ONE_K'
6757
+ description: 'Price date (ISO 8601)',
6758
+ example: '2024-06-01'
4394
6759
  },
4395
- riskLevel: {
6760
+ source: {
4396
6761
  type: 'string',
4397
- description: 'Risk level',
4398
- example: 'LOW'
6762
+ description: 'Price source',
6763
+ example: 'USER_OVERRIDE',
6764
+ enum: ['USER_OVERRIDE', 'OPENBB_EQUITY', 'OPENBB_CURRENCY']
4399
6765
  }
4400
6766
  },
4401
- required: ['id', 'name', 'displayName', 'balance', 'currency', 'assetClass']
6767
+ required: ['amount', 'currency', 'date', 'source']
4402
6768
  } as const;
4403
6769
 
4404
- export const $AssetClassGroupDto = {
6770
+ export const $FxRateDto = {
4405
6771
  type: 'object',
4406
6772
  properties: {
4407
- assetClass: {
6773
+ from: {
4408
6774
  type: 'string',
4409
- description: 'Asset class name',
4410
- example: 'LIQUIDITY',
4411
- enum: [
4412
- 'LIQUIDITY',
4413
- 'EQUITY',
4414
- 'FIXED_INCOME',
4415
- 'PRECIOUS_METALS',
4416
- 'COMMODITY',
4417
- 'INSURANCE',
4418
- 'ALTERNATIVE_INVESTMENT',
4419
- 'PERSONAL_ASSETS',
4420
- 'LIABILITY',
4421
- 'REAL_ESTATE',
4422
- 'INDEX'
4423
- ]
6775
+ example: 'USD'
4424
6776
  },
4425
- assetSubClass: {
6777
+ to: {
4426
6778
  type: 'string',
4427
- description: 'Asset sub-class name',
4428
- example: 'DEPOSIT'
4429
- },
4430
- accounts: {
4431
- description: 'Accounts within this asset class',
4432
- type: 'array',
4433
- items: {
4434
- $ref: '#/components/schemas/AccountItemWithAssetClassDto'
4435
- }
6779
+ example: 'CNY'
4436
6780
  },
4437
- balanceByCurrency: {
4438
- description: 'Balances grouped by currency',
4439
- type: 'array',
4440
- items: {
4441
- $ref: '#/components/schemas/BalanceByCurrencyDto'
4442
- }
6781
+ rate: {
6782
+ type: 'string',
6783
+ description: 'FX rate (Decimal string)',
6784
+ example: '7.2'
4443
6785
  },
4444
- convertedBalance: {
6786
+ date: {
4445
6787
  type: 'string',
4446
- description: 'Converted balance in base currency',
4447
- example: '100000.00'
6788
+ description: 'Rate date (ISO 8601)',
6789
+ example: '2024-01-15'
4448
6790
  }
4449
6791
  },
4450
- required: ['assetClass', 'accounts', 'balanceByCurrency']
6792
+ required: ['from', 'to', 'rate', 'date']
4451
6793
  } as const;
4452
6794
 
4453
- export const $AccountExchangeRateWarningDto = {
6795
+ export const $HoldingPnlRowDto = {
4454
6796
  type: 'object',
4455
6797
  properties: {
4456
- type: {
6798
+ accountId: {
4457
6799
  type: 'string',
4458
- description: 'Warning type',
4459
- example: 'MISSING_EXCHANGE_RATE'
6800
+ description: 'Account UUID',
6801
+ example: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'
4460
6802
  },
4461
- currency: {
6803
+ accountPath: {
4462
6804
  type: 'string',
4463
- description: 'Currency without exchange rate',
6805
+ description: 'Full account path',
6806
+ example: 'Assets:US:Broker:AAPL'
6807
+ },
6808
+ accountCcy: {
6809
+ type: 'object',
6810
+ description: 'Account settlement currency (ISO 4217), from cost currency',
6811
+ nullable: true,
4464
6812
  example: 'USD'
4465
6813
  },
4466
- accounts: {
4467
- description: 'Affected account paths',
4468
- type: 'array',
4469
- items: {
4470
- type: 'string'
4471
- }
6814
+ brokerType: {
6815
+ type: 'object',
6816
+ description: 'Broker type derived from Platform.type',
6817
+ nullable: true,
6818
+ example: 'broker'
4472
6819
  },
4473
- totalAmount: {
6820
+ symbol: {
4474
6821
  type: 'string',
4475
- description: 'Total amount in this currency',
4476
- example: '5000.00'
4477
- }
4478
- },
4479
- required: ['type', 'currency', 'accounts', 'totalAmount']
4480
- } as const;
4481
-
4482
- export const $AssetClassSummaryDto = {
4483
- type: 'object',
4484
- properties: {
4485
- totalAccounts: {
4486
- type: 'number',
4487
- description: 'Total number of accounts'
6822
+ description: 'Commodity symbol',
6823
+ example: 'AAPL'
4488
6824
  },
4489
- totalAssetClasses: {
4490
- type: 'number',
4491
- description: 'Total number of asset classes'
6825
+ chartToken: {
6826
+ type: 'string',
6827
+ description: 'Chart segment token (libs/common resolver)',
6828
+ example: 'equity',
6829
+ enum: ['equity', 'fund', 'bond', 'cash', 'other']
4492
6830
  },
4493
- baseCurrency: {
6831
+ assetClass: {
4494
6832
  type: 'string',
4495
- description: 'Base currency for conversion',
4496
- example: 'CNY'
6833
+ example: 'EQUITY'
4497
6834
  },
4498
- warnings: {
4499
- description: 'Exchange rate warnings',
4500
- type: 'array',
4501
- items: {
4502
- $ref: '#/components/schemas/AccountExchangeRateWarningDto'
4503
- }
6835
+ assetSubClass: {
6836
+ type: 'object',
6837
+ nullable: true,
6838
+ example: 'STOCK'
6839
+ },
6840
+ units: {
6841
+ type: 'string',
6842
+ description: 'Net held units (Decimal string)',
6843
+ example: '12'
6844
+ },
6845
+ averageCostPerUnit: {
6846
+ description:
6847
+ 'Average cost per unit; null when cost currency conflicts or no cost',
6848
+ nullable: true,
6849
+ allOf: [
6850
+ {
6851
+ $ref: '#/components/schemas/MonetaryDto'
6852
+ }
6853
+ ]
6854
+ },
6855
+ costBasis: {
6856
+ description: 'Cost basis of held units',
6857
+ nullable: true,
6858
+ allOf: [
6859
+ {
6860
+ $ref: '#/components/schemas/MonetaryDto'
6861
+ }
6862
+ ]
6863
+ },
6864
+ marketValue: {
6865
+ description: 'Market value at asOf price',
6866
+ nullable: true,
6867
+ allOf: [
6868
+ {
6869
+ $ref: '#/components/schemas/MonetaryDto'
6870
+ }
6871
+ ]
6872
+ },
6873
+ currentPrice: {
6874
+ description: 'Price used for market value',
6875
+ nullable: true,
6876
+ allOf: [
6877
+ {
6878
+ $ref: '#/components/schemas/CurrentPriceDto'
6879
+ }
6880
+ ]
6881
+ },
6882
+ unrealizedPnlBase: {
6883
+ type: 'object',
6884
+ description:
6885
+ 'Unrealized P&L in base currency (Decimal string); null when any FX/price missing',
6886
+ nullable: true,
6887
+ example: '6000'
6888
+ },
6889
+ unrealizedPnlPct: {
6890
+ type: 'object',
6891
+ description: 'Unrealized P&L % (Decimal string)',
6892
+ nullable: true,
6893
+ example: '25'
6894
+ },
6895
+ costFxRate: {
6896
+ description: 'Historical FX rate applied to cost basis',
6897
+ nullable: true,
6898
+ allOf: [
6899
+ {
6900
+ $ref: '#/components/schemas/FxRateDto'
6901
+ }
6902
+ ]
6903
+ },
6904
+ marketFxRate: {
6905
+ description: 'FX rate applied to market value',
6906
+ nullable: true,
6907
+ allOf: [
6908
+ {
6909
+ $ref: '#/components/schemas/FxRateDto'
6910
+ }
6911
+ ]
6912
+ },
6913
+ pctOfInvestedAssets: {
6914
+ type: 'object',
6915
+ description:
6916
+ 'Share of invested assets % (Decimal string); only for invested chartTokens',
6917
+ nullable: true,
6918
+ example: '40'
6919
+ },
6920
+ realizedPnl: {
6921
+ description:
6922
+ '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',
6923
+ nullable: true,
6924
+ allOf: [
6925
+ {
6926
+ $ref: '#/components/schemas/MonetaryDto'
6927
+ }
6928
+ ]
4504
6929
  }
4505
6930
  },
4506
- required: ['totalAccounts', 'totalAssetClasses', 'baseCurrency']
6931
+ required: [
6932
+ 'accountId',
6933
+ 'accountPath',
6934
+ 'symbol',
6935
+ 'chartToken',
6936
+ 'assetClass',
6937
+ 'units'
6938
+ ]
4507
6939
  } as const;
4508
6940
 
4509
- export const $AssetClassAccountsResponseDto = {
6941
+ export const $HoldingPnlWarningDto = {
4510
6942
  type: 'object',
4511
- properties: {
4512
- groups: {
4513
- description: 'Account groups by asset class',
4514
- type: 'array',
4515
- items: {
4516
- $ref: '#/components/schemas/AssetClassGroupDto'
4517
- }
4518
- },
4519
- summary: {
4520
- description: 'Summary statistics',
4521
- allOf: [
4522
- {
4523
- $ref: '#/components/schemas/AssetClassSummaryDto'
4524
- }
6943
+ properties: {
6944
+ type: {
6945
+ type: 'string',
6946
+ description: 'Warning type',
6947
+ example: 'MISSING_COST_FX_RATE',
6948
+ enum: [
6949
+ 'MISSING_COST_FX_RATE',
6950
+ 'MISSING_MARKET_FX_RATE',
6951
+ 'MISSING_SALE_PRICE',
6952
+ 'MISSING_REALIZED_FX_RATE',
6953
+ 'OVERSOLD_LOTS',
6954
+ 'NO_PRICE',
6955
+ 'MIXED_COST_CURRENCY'
4525
6956
  ]
6957
+ },
6958
+ symbol: {
6959
+ type: 'object',
6960
+ nullable: true
6961
+ },
6962
+ accountId: {
6963
+ type: 'object',
6964
+ nullable: true
6965
+ },
6966
+ currency: {
6967
+ type: 'object',
6968
+ nullable: true
4526
6969
  }
4527
6970
  },
4528
- required: ['groups', 'summary']
6971
+ required: ['type']
4529
6972
  } as const;
4530
6973
 
4531
- export const $CashFlowByCurrencyDto = {
6974
+ export const $HoldingPnlResponseDto = {
4532
6975
  type: 'object',
4533
6976
  properties: {
4534
- income: {
4535
- description: 'Income by currency',
4536
- type: 'array',
4537
- items: {
4538
- $ref: '#/components/schemas/BalanceByCurrencyDto'
4539
- }
6977
+ asOfDate: {
6978
+ type: 'string',
6979
+ example: '2026-07-08'
4540
6980
  },
4541
- expense: {
4542
- description: 'Expense by currency',
6981
+ baseCurrency: {
6982
+ type: 'string',
6983
+ example: 'CNY'
6984
+ },
6985
+ method: {
6986
+ type: 'string',
6987
+ description:
6988
+ 'Realized-P&L lot-matching method (FIFO or average). Unrealized cost basis remains average regardless of this value (#473).',
6989
+ enum: ['average', 'FIFO'],
6990
+ example: 'average'
6991
+ },
6992
+ rows: {
4543
6993
  type: 'array',
4544
6994
  items: {
4545
- $ref: '#/components/schemas/BalanceByCurrencyDto'
6995
+ $ref: '#/components/schemas/HoldingPnlRowDto'
4546
6996
  }
4547
6997
  },
4548
- netSavings: {
4549
- description: 'Net savings by currency',
6998
+ warnings: {
4550
6999
  type: 'array',
4551
7000
  items: {
4552
- $ref: '#/components/schemas/BalanceByCurrencyDto'
7001
+ $ref: '#/components/schemas/HoldingPnlWarningDto'
4553
7002
  }
4554
7003
  }
4555
7004
  },
4556
- required: ['income', 'expense', 'netSavings']
7005
+ required: ['asOfDate', 'baseCurrency', 'method', 'rows', 'warnings']
4557
7006
  } as const;
4558
7007
 
4559
- export const $ConvertedCashFlowDto = {
7008
+ export const $CreateBeanPriceDto = {
4560
7009
  type: 'object',
4561
7010
  properties: {
4562
- baseCurrency: {
7011
+ currency: {
4563
7012
  type: 'string',
4564
- description: 'Base currency for conversion',
4565
- example: 'CNY'
7013
+ description: 'Currency being priced (e.g., USD, AAPL, BTC)',
7014
+ example: 'USD'
4566
7015
  },
4567
- income: {
7016
+ quoteCurrency: {
4568
7017
  type: 'string',
4569
- description: 'Converted income',
4570
- example: '25000.00'
7018
+ description: 'Quote currency (pricing currency, e.g., CNY, EUR)',
7019
+ example: 'CNY'
4571
7020
  },
4572
- expense: {
4573
- type: 'string',
4574
- description: 'Converted expense',
4575
- example: '18000.00'
7021
+ amount: {
7022
+ type: 'number',
7023
+ description:
7024
+ 'Price amount (MUST be >= 0 per Beancount spec, supports up to 15 decimal places). Zero allowed for conversion entries, negative strictly prohibited.',
7025
+ example: 175.5,
7026
+ minimum: 0
4576
7027
  },
4577
- netSavings: {
7028
+ date: {
4578
7029
  type: 'string',
4579
- description: 'Converted net savings',
4580
- example: '7000.00'
7030
+ description: 'Price date (ISO 8601 format)',
7031
+ example: '2024-11-05'
4581
7032
  },
4582
- exchangeRates: {
7033
+ metadata: {
4583
7034
  type: 'object',
4584
- description: 'Exchange rates used for conversion',
7035
+ description:
7036
+ 'Metadata (validated by Zod schema, max field lengths enforced)',
4585
7037
  example: {
4586
- USD: '7.200000'
7038
+ source: 'MANUAL',
7039
+ note: 'Bank valuation report',
7040
+ confidence: 0.95
4587
7041
  }
4588
7042
  }
4589
7043
  },
4590
- required: ['baseCurrency', 'income', 'expense', 'netSavings', 'exchangeRates']
7044
+ required: ['currency', 'quoteCurrency', 'amount', 'date']
4591
7045
  } as const;
4592
7046
 
4593
- export const $CashFlowResponseDto = {
7047
+ export const $PriceResponseDto = {
4594
7048
  type: 'object',
4595
7049
  properties: {
4596
- period: {
7050
+ id: {
4597
7051
  type: 'string',
4598
- description: 'Period identifier (YYYY-MM)',
4599
- example: '2024-06'
7052
+ description: 'Unique identifier',
7053
+ example: 'uuid-123-456'
4600
7054
  },
4601
- income: {
7055
+ userId: {
4602
7056
  type: 'string',
4603
- description: 'Total income for the period (converted)',
4604
- example: '25000.00'
7057
+ description: 'User ID (owner of the price)',
7058
+ example: 'user-123'
4605
7059
  },
4606
- expense: {
7060
+ currency: {
4607
7061
  type: 'string',
4608
- description: 'Total expenses for the period (converted)',
4609
- example: '18000.00'
7062
+ description: 'Currency being priced (e.g., USD, AAPL, BTC)',
7063
+ example: 'BTC'
4610
7064
  },
4611
- netSavings: {
7065
+ quoteCurrency: {
4612
7066
  type: 'string',
4613
- description: 'Net savings (income - expense, converted)',
4614
- example: '7000.00'
7067
+ description: 'Quote currency (pricing currency, e.g., USD, CNY)',
7068
+ example: 'USD'
4615
7069
  },
4616
- savingsRate: {
4617
- type: 'string',
4618
- description: 'Savings rate percentage (netSavings / income * 100)',
4619
- example: '28.00'
7070
+ amount: {
7071
+ type: 'number',
7072
+ description:
7073
+ 'Price amount (corresponds to Beancount Amount.number). Supports up to 15 decimal places.',
7074
+ example: 50000
4620
7075
  },
4621
- currency: {
7076
+ date: {
4622
7077
  type: 'string',
4623
- description: 'Base currency code',
4624
- example: 'CNY'
7078
+ description:
7079
+ 'Price date (ISO 8601 format). Represents the date this price was valid.',
7080
+ example: '2024-01-01',
7081
+ format: 'date'
4625
7082
  },
4626
- byCurrency: {
4627
- description: 'Cash flow grouped by original currency',
4628
- allOf: [
4629
- {
4630
- $ref: '#/components/schemas/CashFlowByCurrencyDto'
4631
- }
4632
- ]
7083
+ meta: {
7084
+ type: 'object',
7085
+ description:
7086
+ 'Metadata (corresponds to Beancount meta field). Contains source, confidence, note, etc.',
7087
+ example: {
7088
+ source: 'MANUAL',
7089
+ note: 'User-defined price',
7090
+ confidence: 1
7091
+ }
4633
7092
  },
4634
- converted: {
4635
- description: 'Converted values in base currency',
4636
- allOf: [
4637
- {
4638
- $ref: '#/components/schemas/ConvertedCashFlowDto'
4639
- }
4640
- ]
7093
+ createdAt: {
7094
+ format: 'date-time',
7095
+ type: 'string',
7096
+ description: 'Creation timestamp',
7097
+ example: '2024-11-03T10:00:00Z'
4641
7098
  },
4642
- warnings: {
4643
- description: 'Exchange rate warnings',
7099
+ updatedAt: {
7100
+ format: 'date-time',
7101
+ type: 'string',
7102
+ description: 'Last update timestamp',
7103
+ example: '2024-11-03T10:00:00Z'
7104
+ }
7105
+ },
7106
+ required: [
7107
+ 'id',
7108
+ 'userId',
7109
+ 'currency',
7110
+ 'quoteCurrency',
7111
+ 'amount',
7112
+ 'date',
7113
+ 'meta',
7114
+ 'createdAt',
7115
+ 'updatedAt'
7116
+ ]
7117
+ } as const;
7118
+
7119
+ export const $PriceListResponseDto = {
7120
+ type: 'object',
7121
+ properties: {
7122
+ items: {
7123
+ description: 'List of prices',
4644
7124
  type: 'array',
4645
7125
  items: {
4646
- $ref: '#/components/schemas/ExchangeRateWarningDto'
7126
+ $ref: '#/components/schemas/PriceResponseDto'
4647
7127
  }
7128
+ },
7129
+ total: {
7130
+ type: 'number',
7131
+ description: 'Total number of prices',
7132
+ example: 42
4648
7133
  }
4649
7134
  },
4650
- required: [
4651
- 'period',
4652
- 'income',
4653
- 'expense',
4654
- 'netSavings',
4655
- 'savingsRate',
4656
- 'currency'
4657
- ]
7135
+ required: ['items', 'total']
7136
+ } as const;
7137
+
7138
+ export const $UpdateBeanPriceDto = {
7139
+ type: 'object',
7140
+ properties: {
7141
+ currency: {
7142
+ type: 'string',
7143
+ description: 'Currency being priced'
7144
+ },
7145
+ quoteCurrency: {
7146
+ type: 'string',
7147
+ description: 'Quote currency (pricing currency)'
7148
+ },
7149
+ amount: {
7150
+ type: 'number',
7151
+ description: 'Price amount (MUST be >= 0 per Beancount spec)',
7152
+ minimum: 0
7153
+ },
7154
+ date: {
7155
+ type: 'string',
7156
+ description: 'Price date (ISO 8601 format)'
7157
+ },
7158
+ metadata: {
7159
+ type: 'object',
7160
+ description: 'Metadata'
7161
+ }
7162
+ }
4658
7163
  } as const;
4659
7164
 
4660
7165
  export const $CurrencyBalanceDto = {
@@ -4801,6 +7306,111 @@ export const $PortfolioTrendsResponseDto = {
4801
7306
  required: ['series', 'summary', 'period', 'granularity', 'currency']
4802
7307
  } as const;
4803
7308
 
7309
+ export const $CashFlowPointDto = {
7310
+ type: 'object',
7311
+ properties: {
7312
+ month: {
7313
+ type: 'string',
7314
+ description: 'Month key (YYYY-MM)',
7315
+ example: '2024-03'
7316
+ },
7317
+ income: {
7318
+ type: 'string',
7319
+ description: 'Income in base currency (absolute, converted)',
7320
+ example: '10000.00'
7321
+ },
7322
+ expense: {
7323
+ type: 'string',
7324
+ description: 'Expense in base currency (absolute, converted)',
7325
+ example: '5000.00'
7326
+ },
7327
+ netSavings: {
7328
+ type: 'string',
7329
+ description: 'netSavings = income − expense (savings positive)',
7330
+ example: '5000.00'
7331
+ }
7332
+ },
7333
+ required: ['month', 'income', 'expense', 'netSavings']
7334
+ } as const;
7335
+
7336
+ export const $CashFlowTrendSummaryDto = {
7337
+ type: 'object',
7338
+ properties: {
7339
+ totalIncome: {
7340
+ type: 'string',
7341
+ description: 'Total income across the period',
7342
+ example: '60000.00'
7343
+ },
7344
+ totalExpense: {
7345
+ type: 'string',
7346
+ description: 'Total expense across the period',
7347
+ example: '30000.00'
7348
+ },
7349
+ totalNetSavings: {
7350
+ type: 'string',
7351
+ description: 'income − expense across the period',
7352
+ example: '30000.00'
7353
+ },
7354
+ averageMonthlyNetSavings: {
7355
+ type: 'string',
7356
+ description:
7357
+ 'totalNetSavings divided by the window length (N months, incl. zero-filled)',
7358
+ example: '5000.00'
7359
+ }
7360
+ },
7361
+ required: [
7362
+ 'totalIncome',
7363
+ 'totalExpense',
7364
+ 'totalNetSavings',
7365
+ 'averageMonthlyNetSavings'
7366
+ ]
7367
+ } as const;
7368
+
7369
+ export const $CashFlowTrendsResponseDto = {
7370
+ type: 'object',
7371
+ properties: {
7372
+ series: {
7373
+ description:
7374
+ 'Monthly cash-flow series (fixed N-month window, zero-filled)',
7375
+ type: 'array',
7376
+ items: {
7377
+ $ref: '#/components/schemas/CashFlowPointDto'
7378
+ }
7379
+ },
7380
+ summary: {
7381
+ description: 'Period totals',
7382
+ allOf: [
7383
+ {
7384
+ $ref: '#/components/schemas/CashFlowTrendSummaryDto'
7385
+ }
7386
+ ]
7387
+ },
7388
+ period: {
7389
+ type: 'string',
7390
+ description: 'Period requested',
7391
+ example: '6m'
7392
+ },
7393
+ granularity: {
7394
+ type: 'string',
7395
+ description: 'Data granularity (v1 returns month buckets)',
7396
+ example: 'month'
7397
+ },
7398
+ currency: {
7399
+ type: 'string',
7400
+ description: 'Base currency for converted values',
7401
+ example: 'CNY'
7402
+ },
7403
+ warnings: {
7404
+ description: 'Exchange rate warnings (e.g. missing rate for a currency)',
7405
+ type: 'array',
7406
+ items: {
7407
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
7408
+ }
7409
+ }
7410
+ },
7411
+ required: ['series', 'summary', 'period', 'granularity', 'currency']
7412
+ } as const;
7413
+
4804
7414
  export const $GenerateSnapshotBody = {
4805
7415
  type: 'object',
4806
7416
  properties: {}