@dizzlkheinz/ynab-mcpb 0.18.3 → 0.18.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/bundle/index.cjs +40 -40
  3. package/dist/tools/reconcileAdapter.js +3 -0
  4. package/dist/tools/reconciliation/analyzer.js +72 -7
  5. package/dist/tools/reconciliation/reportFormatter.js +26 -2
  6. package/dist/tools/reconciliation/types.d.ts +3 -0
  7. package/dist/tools/transactionSchemas.d.ts +309 -0
  8. package/dist/tools/transactionSchemas.js +215 -0
  9. package/dist/tools/transactionTools.d.ts +3 -281
  10. package/dist/tools/transactionTools.js +4 -559
  11. package/dist/tools/transactionUtils.d.ts +31 -0
  12. package/dist/tools/transactionUtils.js +349 -0
  13. package/docs/plans/2025-12-25-transaction-tools-refactor-design.md +211 -0
  14. package/docs/plans/2025-12-25-transaction-tools-refactor.md +905 -0
  15. package/package.json +4 -2
  16. package/scripts/run-all-tests.js +196 -0
  17. package/src/tools/__tests__/transactionSchemas.test.ts +1188 -0
  18. package/src/tools/__tests__/transactionUtils.test.ts +989 -0
  19. package/src/tools/reconcileAdapter.ts +6 -0
  20. package/src/tools/reconciliation/__tests__/adapter.causes.test.ts +22 -8
  21. package/src/tools/reconciliation/__tests__/adapter.test.ts +3 -0
  22. package/src/tools/reconciliation/__tests__/analyzer.test.ts +65 -0
  23. package/src/tools/reconciliation/__tests__/recommendationEngine.test.ts +3 -0
  24. package/src/tools/reconciliation/__tests__/reportFormatter.test.ts +4 -1
  25. package/src/tools/reconciliation/__tests__/scenarios/adapterCurrency.scenario.test.ts +3 -0
  26. package/src/tools/reconciliation/__tests__/scenarios/extremes.scenario.test.ts +5 -1
  27. package/src/tools/reconciliation/__tests__/schemaUrl.test.ts +22 -8
  28. package/src/tools/reconciliation/analyzer.ts +127 -11
  29. package/src/tools/reconciliation/reportFormatter.ts +39 -2
  30. package/src/tools/reconciliation/types.ts +6 -0
  31. package/src/tools/transactionSchemas.ts +453 -0
  32. package/src/tools/transactionTools.ts +102 -823
  33. package/src/tools/transactionUtils.ts +536 -0
@@ -38,6 +38,8 @@ const convertSummary = (analysis) => ({
38
38
  statement_date_range: analysis.summary.statement_date_range,
39
39
  bank_transactions_count: analysis.summary.bank_transactions_count,
40
40
  ynab_transactions_count: analysis.summary.ynab_transactions_count,
41
+ ynab_in_range_count: analysis.summary.ynab_in_range_count ?? analysis.summary.ynab_transactions_count,
42
+ ynab_outside_range_count: analysis.summary.ynab_outside_range_count ?? 0,
41
43
  auto_matched: analysis.summary.auto_matched,
42
44
  suggested_matches: analysis.summary.suggested_matches,
43
45
  unmatched_bank: analysis.summary.unmatched_bank,
@@ -152,6 +154,7 @@ export const buildReconciliationPayload = (analysis, options = {}, execution) =>
152
154
  unmatched: {
153
155
  bank: analysis.unmatched_bank.map((txn) => toBankTransactionView(txn, currency)),
154
156
  ynab: analysis.unmatched_ynab.map((txn) => toYNABTransactionView(txn, currency)),
157
+ ynab_outside_date_range: (analysis.ynab_outside_date_range ?? []).map((txn) => toYNABTransactionView(txn, currency)),
155
158
  },
156
159
  };
157
160
  if (analysis.recommendations && analysis.recommendations.length > 0) {
@@ -3,6 +3,54 @@ import { findMatches, normalizeConfig, DEFAULT_CONFIG } from './matcher.js';
3
3
  import { normalizeYNABTransactions } from './ynabAdapter.js';
4
4
  import { toMoneyValue } from '../../utils/money.js';
5
5
  import { generateRecommendations } from './recommendationEngine.js';
6
+ function calculateDateRange(bankTransactions) {
7
+ if (bankTransactions.length === 0) {
8
+ return null;
9
+ }
10
+ const dates = bankTransactions
11
+ .map((t) => t.date)
12
+ .filter((d) => d && /^\d{4}-\d{2}-\d{2}$/.test(d))
13
+ .sort();
14
+ if (dates.length === 0) {
15
+ return null;
16
+ }
17
+ return {
18
+ minDate: dates[0],
19
+ maxDate: dates[dates.length - 1],
20
+ };
21
+ }
22
+ function filterByDateRange(ynabTransactions, dateRange, dateToleranceDays = 7) {
23
+ if (dateToleranceDays < 0) {
24
+ console.warn(`[filterByDateRange] dateToleranceDays must be non-negative, got ${dateToleranceDays}. Using 0.`);
25
+ dateToleranceDays = 0;
26
+ }
27
+ const inRange = [];
28
+ const outsideRange = [];
29
+ const minParts = dateRange.minDate.split('-').map(Number);
30
+ const maxParts = dateRange.maxDate.split('-').map(Number);
31
+ if (minParts.length !== 3 ||
32
+ maxParts.length !== 3 ||
33
+ minParts.some((n) => !Number.isFinite(n)) ||
34
+ maxParts.some((n) => !Number.isFinite(n))) {
35
+ console.warn(`[filterByDateRange] Invalid date format in range: ${dateRange.minDate} to ${dateRange.maxDate} - returning all transactions`);
36
+ return { inRange: ynabTransactions, outsideRange: [] };
37
+ }
38
+ const [minYear, minMonth, minDay] = minParts;
39
+ const [maxYear, maxMonth, maxDay] = maxParts;
40
+ const minDateWithBuffer = new Date(Date.UTC(minYear, minMonth - 1, minDay - dateToleranceDays));
41
+ const minDateStr = minDateWithBuffer.toISOString().split('T')[0];
42
+ const maxDateWithBuffer = new Date(Date.UTC(maxYear, maxMonth - 1, maxDay + dateToleranceDays));
43
+ const maxDateStr = maxDateWithBuffer.toISOString().split('T')[0];
44
+ for (const txn of ynabTransactions) {
45
+ if (txn.date >= minDateStr && txn.date <= maxDateStr) {
46
+ inRange.push(txn);
47
+ }
48
+ else {
49
+ outsideRange.push(txn);
50
+ }
51
+ }
52
+ return { inRange, outsideRange };
53
+ }
6
54
  function mapToTransactionMatch(result) {
7
55
  const candidates = result.candidates.map((c) => ({
8
56
  ynab_transaction: c.ynabTransaction,
@@ -55,9 +103,10 @@ function calculateBalances(ynabTransactions, statementBalanceDecimal, currency,
55
103
  on_track: Math.abs(discrepancy) < 10,
56
104
  };
57
105
  }
58
- function generateSummary(bankTransactions, ynabTransactions, autoMatches, suggestedMatches, unmatchedBank, unmatchedYNAB, balances) {
106
+ function generateSummary(bankTransactions, ynabTransactionsInRange, ynabTransactionsOutsideRange, autoMatches, suggestedMatches, unmatchedBank, unmatchedYNAB, balances) {
59
107
  const dates = bankTransactions.map((t) => t.date).sort();
60
108
  const dateRange = dates.length > 0 ? `${dates[0]} to ${dates[dates.length - 1]}` : 'Unknown';
109
+ const totalYnabCount = ynabTransactionsInRange.length + ynabTransactionsOutsideRange.length;
61
110
  let discrepancyExplanation = '';
62
111
  if (balances.on_track) {
63
112
  discrepancyExplanation = 'Cleared balance matches statement';
@@ -79,7 +128,9 @@ function generateSummary(bankTransactions, ynabTransactions, autoMatches, sugges
79
128
  return {
80
129
  statement_date_range: dateRange,
81
130
  bank_transactions_count: bankTransactions.length,
82
- ynab_transactions_count: ynabTransactions.length,
131
+ ynab_transactions_count: totalYnabCount,
132
+ ynab_in_range_count: ynabTransactionsInRange.length,
133
+ ynab_outside_range_count: ynabTransactionsOutsideRange.length,
83
134
  auto_matched: autoMatches.length,
84
135
  suggested_matches: suggestedMatches.length,
85
136
  unmatched_bank: unmatchedBank.length,
@@ -236,9 +287,22 @@ export function analyzeReconciliation(csvContentOrParsed, _csvFilePath, ynabTran
236
287
  const newBankTransactions = parseResult.transactions;
237
288
  const csvParseErrors = parseResult.errors;
238
289
  const csvParseWarnings = parseResult.warnings;
239
- const newYNABTransactions = normalizeYNABTransactions(ynabTransactions);
290
+ const allYNABTransactions = normalizeYNABTransactions(ynabTransactions);
291
+ const csvDateRange = calculateDateRange(newBankTransactions);
292
+ let ynabInRange;
293
+ let ynabOutsideRange;
294
+ if (csvDateRange) {
295
+ const dateToleranceDays = config.dateToleranceDays ?? 7;
296
+ const filtered = filterByDateRange(allYNABTransactions, csvDateRange, dateToleranceDays);
297
+ ynabInRange = filtered.inRange;
298
+ ynabOutsideRange = filtered.outsideRange;
299
+ }
300
+ else {
301
+ ynabInRange = allYNABTransactions;
302
+ ynabOutsideRange = [];
303
+ }
240
304
  const normalizedConfig = normalizeConfig(config);
241
- const newMatches = findMatches(newBankTransactions, newYNABTransactions, normalizedConfig);
305
+ const newMatches = findMatches(newBankTransactions, ynabInRange, normalizedConfig);
242
306
  const matches = newMatches.map(mapToTransactionMatch);
243
307
  const autoMatches = matches.filter((m) => m.confidence === 'high');
244
308
  const autoMatchedYnabIds = new Set();
@@ -255,9 +319,9 @@ export function analyzeReconciliation(csvContentOrParsed, _csvFilePath, ynabTran
255
319
  if (m.ynabTransaction)
256
320
  matchedYnabIds.add(m.ynabTransaction.id);
257
321
  });
258
- const unmatchedYNAB = newYNABTransactions.filter((t) => !matchedYnabIds.has(t.id));
259
- const balances = calculateBalances(newYNABTransactions, statementBalance, currency, accountSnapshot);
260
- const summary = generateSummary(matches.map((m) => m.bankTransaction), newYNABTransactions, autoMatches, suggestedMatches, unmatchedBank, unmatchedYNAB, balances);
322
+ const unmatchedYNAB = ynabInRange.filter((t) => !matchedYnabIds.has(t.id));
323
+ const balances = calculateBalances(allYNABTransactions, statementBalance, currency, accountSnapshot);
324
+ const summary = generateSummary(matches.map((m) => m.bankTransaction), ynabInRange, ynabOutsideRange, autoMatches, suggestedMatches, unmatchedBank, unmatchedYNAB, balances);
261
325
  const nextSteps = generateNextSteps(summary);
262
326
  const insights = detectInsights(unmatchedBank, summary, balances, currency, csvParseErrors, csvParseWarnings);
263
327
  const analysis = {
@@ -268,6 +332,7 @@ export function analyzeReconciliation(csvContentOrParsed, _csvFilePath, ynabTran
268
332
  suggested_matches: suggestedMatches,
269
333
  unmatched_bank: unmatchedBank,
270
334
  unmatched_ynab: unmatchedYNAB,
335
+ ynab_outside_date_range: ynabOutsideRange,
271
336
  balance_info: balances,
272
337
  next_steps: nextSteps,
273
338
  insights,
@@ -59,13 +59,20 @@ function formatTransactionAnalysisSection(analysis, options) {
59
59
  lines.push('Transaction Analysis');
60
60
  lines.push(SECTION_DIVIDER);
61
61
  const summary = analysis.summary;
62
+ const outsideRangeCount = summary.ynab_outside_range_count ?? 0;
63
+ if (outsideRangeCount > 0) {
64
+ const inRangeCount = summary.ynab_in_range_count ?? summary.ynab_transactions_count;
65
+ lines.push(`Comparing ${summary.bank_transactions_count} bank transactions with ${inRangeCount} YNAB transactions within statement period.`);
66
+ lines.push(`(${outsideRangeCount} YNAB transactions outside statement period - not compared)`);
67
+ lines.push('');
68
+ }
62
69
  lines.push(`- Automatically matched: ${summary.auto_matched} of ${summary.bank_transactions_count} transactions`);
63
70
  lines.push(`- Suggested matches: ${summary.suggested_matches}`);
64
71
  lines.push(`- Unmatched bank: ${summary.unmatched_bank}`);
65
72
  lines.push(`- Unmatched YNAB: ${summary.unmatched_ynab}`);
66
73
  if (analysis.unmatched_bank.length > 0) {
67
74
  lines.push('');
68
- lines.push('Unmatched bank transactions:');
75
+ lines.push('Missing from YNAB (bank transactions without matches):');
69
76
  const maxToShow = options.maxUnmatchedToShow ?? 5;
70
77
  const toShow = analysis.unmatched_bank.slice(0, maxToShow);
71
78
  for (const txn of toShow) {
@@ -75,9 +82,21 @@ function formatTransactionAnalysisSection(analysis, options) {
75
82
  lines.push(` ... and ${analysis.unmatched_bank.length - maxToShow} more`);
76
83
  }
77
84
  }
85
+ if (analysis.unmatched_ynab.length > 0) {
86
+ lines.push('');
87
+ lines.push('Missing from bank statement (YNAB transactions without matches):');
88
+ const maxToShow = options.maxUnmatchedToShow ?? 5;
89
+ const toShow = analysis.unmatched_ynab.slice(0, maxToShow);
90
+ for (const txn of toShow) {
91
+ lines.push(formatYnabTransactionLine(txn));
92
+ }
93
+ if (analysis.unmatched_ynab.length > maxToShow) {
94
+ lines.push(` ... and ${analysis.unmatched_ynab.length - maxToShow} more`);
95
+ }
96
+ }
78
97
  if (analysis.suggested_matches.length > 0) {
79
98
  lines.push('');
80
- lines.push('Suggested matches:');
99
+ lines.push('Suggested matches (review manually):');
81
100
  const maxToShow = options.maxUnmatchedToShow ?? 3;
82
101
  const toShow = analysis.suggested_matches.slice(0, maxToShow);
83
102
  for (const match of toShow) {
@@ -89,6 +108,11 @@ function formatTransactionAnalysisSection(analysis, options) {
89
108
  }
90
109
  return lines.join('\n');
91
110
  }
111
+ function formatYnabTransactionLine(txn) {
112
+ const amountStr = formatAmount(txn.amount);
113
+ const payee = txn.payee ?? 'Unknown';
114
+ return ` ${txn.date} - ${payee.substring(0, 40).padEnd(40)} ${amountStr}`;
115
+ }
92
116
  function formatBankTransactionLine(txn) {
93
117
  const amountStr = formatAmount(txn.amount);
94
118
  return ` ${txn.date} - ${txn.payee.substring(0, 40).padEnd(40)} ${amountStr}`;
@@ -32,6 +32,8 @@ export interface ReconciliationSummary {
32
32
  statement_date_range: string;
33
33
  bank_transactions_count: number;
34
34
  ynab_transactions_count: number;
35
+ ynab_in_range_count: number;
36
+ ynab_outside_range_count: number;
35
37
  auto_matched: number;
36
38
  suggested_matches: number;
37
39
  unmatched_bank: number;
@@ -59,6 +61,7 @@ export interface ReconciliationAnalysis {
59
61
  suggested_matches: TransactionMatch[];
60
62
  unmatched_bank: BankTransaction[];
61
63
  unmatched_ynab: YNABTransaction[];
64
+ ynab_outside_date_range: YNABTransaction[];
62
65
  balance_info: BalanceInfo;
63
66
  next_steps: string[];
64
67
  insights: ReconciliationInsight[];
@@ -0,0 +1,309 @@
1
+ import { z } from 'zod/v4';
2
+ import * as ynab from 'ynab';
3
+ export declare const ListTransactionsSchema: z.ZodObject<{
4
+ budget_id: z.ZodString;
5
+ account_id: z.ZodOptional<z.ZodString>;
6
+ category_id: z.ZodOptional<z.ZodString>;
7
+ since_date: z.ZodOptional<z.ZodString>;
8
+ type: z.ZodOptional<z.ZodEnum<{
9
+ uncategorized: "uncategorized";
10
+ unapproved: "unapproved";
11
+ }>>;
12
+ }, z.core.$strict>;
13
+ export type ListTransactionsParams = z.infer<typeof ListTransactionsSchema>;
14
+ export declare const GetTransactionSchema: z.ZodObject<{
15
+ budget_id: z.ZodString;
16
+ transaction_id: z.ZodString;
17
+ }, z.core.$strict>;
18
+ export type GetTransactionParams = z.infer<typeof GetTransactionSchema>;
19
+ export declare const CreateTransactionSchema: z.ZodObject<{
20
+ budget_id: z.ZodString;
21
+ account_id: z.ZodString;
22
+ amount: z.ZodNumber;
23
+ date: z.ZodString;
24
+ payee_name: z.ZodOptional<z.ZodString>;
25
+ payee_id: z.ZodOptional<z.ZodString>;
26
+ category_id: z.ZodOptional<z.ZodString>;
27
+ memo: z.ZodOptional<z.ZodString>;
28
+ cleared: z.ZodOptional<z.ZodEnum<{
29
+ cleared: "cleared";
30
+ uncleared: "uncleared";
31
+ reconciled: "reconciled";
32
+ }>>;
33
+ approved: z.ZodOptional<z.ZodBoolean>;
34
+ flag_color: z.ZodOptional<z.ZodEnum<{
35
+ red: "red";
36
+ orange: "orange";
37
+ yellow: "yellow";
38
+ green: "green";
39
+ blue: "blue";
40
+ purple: "purple";
41
+ }>>;
42
+ import_id: z.ZodOptional<z.ZodString>;
43
+ dry_run: z.ZodOptional<z.ZodBoolean>;
44
+ subtransactions: z.ZodOptional<z.ZodArray<z.ZodObject<{
45
+ amount: z.ZodNumber;
46
+ payee_name: z.ZodOptional<z.ZodString>;
47
+ payee_id: z.ZodOptional<z.ZodString>;
48
+ category_id: z.ZodOptional<z.ZodString>;
49
+ memo: z.ZodOptional<z.ZodString>;
50
+ }, z.core.$strict>>>;
51
+ }, z.core.$strict>;
52
+ export type CreateTransactionParams = z.infer<typeof CreateTransactionSchema>;
53
+ export interface SubtransactionInput {
54
+ amount: number;
55
+ payee_name?: string;
56
+ payee_id?: string;
57
+ category_id?: string;
58
+ memo?: string;
59
+ }
60
+ export type BulkTransactionInput = Omit<CreateTransactionParams, 'budget_id' | 'dry_run' | 'subtransactions'>;
61
+ export declare const CreateTransactionsSchema: z.ZodObject<{
62
+ budget_id: z.ZodString;
63
+ transactions: z.ZodArray<z.ZodObject<{
64
+ date: z.ZodString;
65
+ cleared: z.ZodOptional<z.ZodEnum<{
66
+ cleared: "cleared";
67
+ uncleared: "uncleared";
68
+ reconciled: "reconciled";
69
+ }>>;
70
+ amount: z.ZodNumber;
71
+ memo: z.ZodOptional<z.ZodString>;
72
+ approved: z.ZodOptional<z.ZodBoolean>;
73
+ flag_color: z.ZodOptional<z.ZodEnum<{
74
+ red: "red";
75
+ orange: "orange";
76
+ yellow: "yellow";
77
+ green: "green";
78
+ blue: "blue";
79
+ purple: "purple";
80
+ }>>;
81
+ account_id: z.ZodString;
82
+ payee_id: z.ZodOptional<z.ZodString>;
83
+ category_id: z.ZodOptional<z.ZodString>;
84
+ import_id: z.ZodOptional<z.ZodString>;
85
+ payee_name: z.ZodOptional<z.ZodString>;
86
+ }, z.core.$strict>>;
87
+ dry_run: z.ZodOptional<z.ZodBoolean>;
88
+ }, z.core.$strict>;
89
+ export type CreateTransactionsParams = z.infer<typeof CreateTransactionsSchema>;
90
+ export interface BulkTransactionResult {
91
+ request_index: number;
92
+ status: 'created' | 'duplicate' | 'failed';
93
+ transaction_id?: string | undefined;
94
+ correlation_key: string;
95
+ error_code?: string | undefined;
96
+ error?: string | undefined;
97
+ }
98
+ export interface BulkCreateResponse {
99
+ success: boolean;
100
+ server_knowledge?: number;
101
+ summary: {
102
+ total_requested: number;
103
+ created: number;
104
+ duplicates: number;
105
+ failed: number;
106
+ };
107
+ results: BulkTransactionResult[];
108
+ transactions?: ynab.TransactionDetail[];
109
+ duplicate_import_ids?: string[];
110
+ message?: string;
111
+ mode?: 'full' | 'summary' | 'ids_only';
112
+ }
113
+ export declare const CreateReceiptSplitTransactionSchema: z.ZodObject<{
114
+ budget_id: z.ZodString;
115
+ account_id: z.ZodString;
116
+ payee_name: z.ZodString;
117
+ date: z.ZodOptional<z.ZodString>;
118
+ memo: z.ZodOptional<z.ZodString>;
119
+ receipt_subtotal: z.ZodOptional<z.ZodNumber>;
120
+ receipt_tax: z.ZodNumber;
121
+ receipt_total: z.ZodNumber;
122
+ categories: z.ZodArray<z.ZodObject<{
123
+ category_id: z.ZodString;
124
+ category_name: z.ZodOptional<z.ZodString>;
125
+ items: z.ZodArray<z.ZodObject<{
126
+ name: z.ZodString;
127
+ amount: z.ZodNumber;
128
+ quantity: z.ZodOptional<z.ZodNumber>;
129
+ memo: z.ZodOptional<z.ZodString>;
130
+ }, z.core.$strict>>;
131
+ }, z.core.$strict>>;
132
+ cleared: z.ZodOptional<z.ZodEnum<{
133
+ cleared: "cleared";
134
+ uncleared: "uncleared";
135
+ reconciled: "reconciled";
136
+ }>>;
137
+ approved: z.ZodOptional<z.ZodBoolean>;
138
+ flag_color: z.ZodOptional<z.ZodEnum<{
139
+ red: "red";
140
+ orange: "orange";
141
+ yellow: "yellow";
142
+ green: "green";
143
+ blue: "blue";
144
+ purple: "purple";
145
+ }>>;
146
+ dry_run: z.ZodOptional<z.ZodBoolean>;
147
+ }, z.core.$strict>;
148
+ export type CreateReceiptSplitTransactionParams = z.infer<typeof CreateReceiptSplitTransactionSchema>;
149
+ export interface ReceiptCategoryCalculation {
150
+ category_id: string;
151
+ category_name: string | undefined;
152
+ subtotal_milliunits: number;
153
+ tax_milliunits: number;
154
+ items: {
155
+ name: string;
156
+ amount_milliunits: number;
157
+ quantity: number | undefined;
158
+ memo: string | undefined;
159
+ }[];
160
+ }
161
+ export declare const UpdateTransactionSchema: z.ZodObject<{
162
+ budget_id: z.ZodString;
163
+ transaction_id: z.ZodString;
164
+ account_id: z.ZodOptional<z.ZodString>;
165
+ amount: z.ZodOptional<z.ZodNumber>;
166
+ date: z.ZodOptional<z.ZodString>;
167
+ payee_name: z.ZodOptional<z.ZodString>;
168
+ payee_id: z.ZodOptional<z.ZodString>;
169
+ category_id: z.ZodOptional<z.ZodString>;
170
+ memo: z.ZodOptional<z.ZodString>;
171
+ cleared: z.ZodOptional<z.ZodEnum<{
172
+ cleared: "cleared";
173
+ uncleared: "uncleared";
174
+ reconciled: "reconciled";
175
+ }>>;
176
+ approved: z.ZodOptional<z.ZodBoolean>;
177
+ flag_color: z.ZodOptional<z.ZodEnum<{
178
+ red: "red";
179
+ orange: "orange";
180
+ yellow: "yellow";
181
+ green: "green";
182
+ blue: "blue";
183
+ purple: "purple";
184
+ }>>;
185
+ dry_run: z.ZodOptional<z.ZodBoolean>;
186
+ }, z.core.$strict>;
187
+ export type UpdateTransactionParams = z.infer<typeof UpdateTransactionSchema>;
188
+ declare const BulkUpdateTransactionInputSchema: z.ZodObject<{
189
+ id: z.ZodString;
190
+ amount: z.ZodOptional<z.ZodNumber>;
191
+ date: z.ZodOptional<z.ZodString>;
192
+ payee_name: z.ZodOptional<z.ZodString>;
193
+ payee_id: z.ZodOptional<z.ZodString>;
194
+ category_id: z.ZodOptional<z.ZodString>;
195
+ memo: z.ZodOptional<z.ZodString>;
196
+ cleared: z.ZodOptional<z.ZodEnum<{
197
+ cleared: "cleared";
198
+ uncleared: "uncleared";
199
+ reconciled: "reconciled";
200
+ }>>;
201
+ approved: z.ZodOptional<z.ZodBoolean>;
202
+ flag_color: z.ZodOptional<z.ZodEnum<{
203
+ red: "red";
204
+ orange: "orange";
205
+ yellow: "yellow";
206
+ green: "green";
207
+ blue: "blue";
208
+ purple: "purple";
209
+ }>>;
210
+ original_account_id: z.ZodOptional<z.ZodString>;
211
+ original_date: z.ZodOptional<z.ZodString>;
212
+ }, z.core.$strict>;
213
+ export type BulkUpdateTransactionInput = z.infer<typeof BulkUpdateTransactionInputSchema>;
214
+ export declare const UpdateTransactionsSchema: z.ZodObject<{
215
+ budget_id: z.ZodString;
216
+ transactions: z.ZodArray<z.ZodObject<{
217
+ id: z.ZodString;
218
+ amount: z.ZodOptional<z.ZodNumber>;
219
+ date: z.ZodOptional<z.ZodString>;
220
+ payee_name: z.ZodOptional<z.ZodString>;
221
+ payee_id: z.ZodOptional<z.ZodString>;
222
+ category_id: z.ZodOptional<z.ZodString>;
223
+ memo: z.ZodOptional<z.ZodString>;
224
+ cleared: z.ZodOptional<z.ZodEnum<{
225
+ cleared: "cleared";
226
+ uncleared: "uncleared";
227
+ reconciled: "reconciled";
228
+ }>>;
229
+ approved: z.ZodOptional<z.ZodBoolean>;
230
+ flag_color: z.ZodOptional<z.ZodEnum<{
231
+ red: "red";
232
+ orange: "orange";
233
+ yellow: "yellow";
234
+ green: "green";
235
+ blue: "blue";
236
+ purple: "purple";
237
+ }>>;
238
+ original_account_id: z.ZodOptional<z.ZodString>;
239
+ original_date: z.ZodOptional<z.ZodString>;
240
+ }, z.core.$strict>>;
241
+ dry_run: z.ZodOptional<z.ZodBoolean>;
242
+ }, z.core.$strict>;
243
+ export type UpdateTransactionsParams = z.infer<typeof UpdateTransactionsSchema>;
244
+ export interface BulkUpdateResult {
245
+ request_index: number;
246
+ status: 'updated' | 'failed';
247
+ transaction_id: string;
248
+ correlation_key: string;
249
+ error_code?: string;
250
+ error?: string;
251
+ }
252
+ export interface BulkUpdateResponse {
253
+ success: boolean;
254
+ server_knowledge?: number;
255
+ summary: {
256
+ total_requested: number;
257
+ updated: number;
258
+ failed: number;
259
+ };
260
+ results: BulkUpdateResult[];
261
+ transactions?: ynab.TransactionDetail[];
262
+ message?: string;
263
+ mode?: 'full' | 'summary' | 'ids_only';
264
+ }
265
+ export declare const DeleteTransactionSchema: z.ZodObject<{
266
+ budget_id: z.ZodString;
267
+ transaction_id: z.ZodString;
268
+ dry_run: z.ZodOptional<z.ZodBoolean>;
269
+ }, z.core.$strict>;
270
+ export type DeleteTransactionParams = z.infer<typeof DeleteTransactionSchema>;
271
+ export interface CorrelationPayload {
272
+ account_id?: string;
273
+ date?: string;
274
+ amount?: number;
275
+ payee_id?: string | null;
276
+ payee_name?: string | null;
277
+ category_id?: string | null;
278
+ memo?: string | null;
279
+ cleared?: ynab.TransactionClearedStatus;
280
+ approved?: boolean;
281
+ flag_color?: ynab.TransactionFlagColor | null;
282
+ import_id?: string | null;
283
+ }
284
+ export interface CorrelationPayloadInput {
285
+ account_id?: string | undefined;
286
+ date?: string | undefined;
287
+ amount?: number | undefined;
288
+ payee_id?: string | null | undefined;
289
+ payee_name?: string | null | undefined;
290
+ category_id?: string | null | undefined;
291
+ memo?: string | null | undefined;
292
+ cleared?: ynab.TransactionClearedStatus | undefined;
293
+ approved?: boolean | undefined;
294
+ flag_color?: ynab.TransactionFlagColor | null | undefined;
295
+ import_id?: string | null | undefined;
296
+ }
297
+ export interface CategorySource {
298
+ category_id?: string | null;
299
+ subtransactions?: {
300
+ category_id?: string | null;
301
+ }[] | null | undefined;
302
+ }
303
+ export interface TransactionCacheInvalidationOptions {
304
+ affectedCategoryIds?: Set<string>;
305
+ invalidateAllCategories?: boolean;
306
+ accountTotalsChanged?: boolean;
307
+ invalidateMonths?: boolean;
308
+ }
309
+ export {};