@dizzlkheinz/ynab-mcpb 0.15.0 → 0.15.1
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.
- package/dist/bundle/index.cjs +25 -25
- package/dist/tools/reconciliation/analyzer.d.ts +5 -1
- package/dist/tools/reconciliation/analyzer.js +10 -8
- package/dist/tools/reconciliation/index.js +1 -1
- package/package.json +1 -1
- package/src/tools/reconciliation/analyzer.ts +18 -6
- package/src/tools/reconciliation/index.ts +7 -6
|
@@ -2,4 +2,8 @@ import type * as ynab from 'ynab';
|
|
|
2
2
|
import { type ParseCSVOptions, type CSVParseResult } from './csvParser.js';
|
|
3
3
|
import { type MatchingConfig } from './matcher.js';
|
|
4
4
|
import type { ReconciliationAnalysis } from './types.js';
|
|
5
|
-
export declare function analyzeReconciliation(csvContentOrParsed: string | CSVParseResult, _csvFilePath: string | undefined, ynabTransactions: ynab.TransactionDetail[], statementBalance: number, config?: MatchingConfig, currency?: string, accountId?: string, budgetId?: string, invertBankAmounts?: boolean, csvOptions?: ParseCSVOptions
|
|
5
|
+
export declare function analyzeReconciliation(csvContentOrParsed: string | CSVParseResult, _csvFilePath: string | undefined, ynabTransactions: ynab.TransactionDetail[], statementBalance: number, config?: MatchingConfig, currency?: string, accountId?: string, budgetId?: string, invertBankAmounts?: boolean, csvOptions?: ParseCSVOptions, accountSnapshot?: {
|
|
6
|
+
balance?: number;
|
|
7
|
+
cleared_balance?: number;
|
|
8
|
+
uncleared_balance?: number;
|
|
9
|
+
}): ReconciliationAnalysis;
|
|
@@ -29,20 +29,22 @@ function mapToTransactionMatch(result) {
|
|
|
29
29
|
}
|
|
30
30
|
return match;
|
|
31
31
|
}
|
|
32
|
-
function calculateBalances(ynabTransactions, statementBalanceDecimal, currency) {
|
|
33
|
-
let
|
|
34
|
-
let
|
|
32
|
+
function calculateBalances(ynabTransactions, statementBalanceDecimal, currency, accountSnapshot) {
|
|
33
|
+
let computedCleared = 0;
|
|
34
|
+
let computedUncleared = 0;
|
|
35
35
|
for (const txn of ynabTransactions) {
|
|
36
36
|
const amount = txn.amount;
|
|
37
37
|
if (txn.cleared === 'cleared' || txn.cleared === 'reconciled') {
|
|
38
|
-
|
|
38
|
+
computedCleared += amount;
|
|
39
39
|
}
|
|
40
40
|
else {
|
|
41
|
-
|
|
41
|
+
computedUncleared += amount;
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
|
+
const clearedBalance = accountSnapshot?.cleared_balance ?? computedCleared;
|
|
45
|
+
const unclearedBalance = accountSnapshot?.uncleared_balance ?? computedUncleared;
|
|
46
|
+
const totalBalance = accountSnapshot?.balance ?? clearedBalance + unclearedBalance;
|
|
44
47
|
const statementBalanceMilli = Math.round(statementBalanceDecimal * 1000);
|
|
45
|
-
const totalBalance = clearedBalance + unclearedBalance;
|
|
46
48
|
const discrepancy = clearedBalance - statementBalanceMilli;
|
|
47
49
|
return {
|
|
48
50
|
current_cleared: toMoneyValue(clearedBalance, currency),
|
|
@@ -220,7 +222,7 @@ function detectInsights(unmatchedBank, _summary, balances, currency, csvErrors =
|
|
|
220
222
|
}
|
|
221
223
|
return insights.slice(0, 5);
|
|
222
224
|
}
|
|
223
|
-
export function analyzeReconciliation(csvContentOrParsed, _csvFilePath, ynabTransactions, statementBalance, config = DEFAULT_CONFIG, currency = 'USD', accountId, budgetId, invertBankAmounts = false, csvOptions) {
|
|
225
|
+
export function analyzeReconciliation(csvContentOrParsed, _csvFilePath, ynabTransactions, statementBalance, config = DEFAULT_CONFIG, currency = 'USD', accountId, budgetId, invertBankAmounts = false, csvOptions, accountSnapshot) {
|
|
224
226
|
let parseResult;
|
|
225
227
|
if (typeof csvContentOrParsed === 'string') {
|
|
226
228
|
parseResult = parseCSV(csvContentOrParsed, {
|
|
@@ -254,7 +256,7 @@ export function analyzeReconciliation(csvContentOrParsed, _csvFilePath, ynabTran
|
|
|
254
256
|
matchedYnabIds.add(m.ynabTransaction.id);
|
|
255
257
|
});
|
|
256
258
|
const unmatchedYNAB = newYNABTransactions.filter((t) => !matchedYnabIds.has(t.id));
|
|
257
|
-
const balances = calculateBalances(newYNABTransactions, statementBalance, currency);
|
|
259
|
+
const balances = calculateBalances(newYNABTransactions, statementBalance, currency, accountSnapshot);
|
|
258
260
|
const summary = generateSummary(matches.map((m) => m.bankTransaction), newYNABTransactions, autoMatches, suggestedMatches, unmatchedBank, unmatchedYNAB, balances);
|
|
259
261
|
const nextSteps = generateNextSteps(summary);
|
|
260
262
|
const insights = detectInsights(unmatchedBank, summary, balances, currency, csvParseErrors, csvParseWarnings);
|
|
@@ -230,12 +230,12 @@ export async function handleReconcileAccount(ynabAPI, deltaFetcherOrParams, mayb
|
|
|
230
230
|
delta_merge_applied: transactionsResult.usedDelta,
|
|
231
231
|
},
|
|
232
232
|
};
|
|
233
|
-
const analysis = analyzeReconciliation(parseResult ?? csvContent, params.csv_file_path, ynabTransactions, adjustedStatementBalance, config, currencyCode, params.account_id, params.budget_id, finalInvertAmounts, csvOptions);
|
|
234
233
|
const initialAccount = {
|
|
235
234
|
balance: accountData.balance,
|
|
236
235
|
cleared_balance: accountData.cleared_balance,
|
|
237
236
|
uncleared_balance: accountData.uncleared_balance,
|
|
238
237
|
};
|
|
238
|
+
const analysis = analyzeReconciliation(parseResult ?? csvContent, params.csv_file_path, ynabTransactions, adjustedStatementBalance, config, currencyCode, params.account_id, params.budget_id, finalInvertAmounts, csvOptions, initialAccount);
|
|
239
239
|
let executionData;
|
|
240
240
|
const wantsBalanceVerification = Boolean(params.statement_date);
|
|
241
241
|
const shouldExecute = params.auto_create_transactions ||
|
package/package.json
CHANGED
|
@@ -61,22 +61,28 @@ function calculateBalances(
|
|
|
61
61
|
ynabTransactions: YNABTransaction[],
|
|
62
62
|
statementBalanceDecimal: number,
|
|
63
63
|
currency: string,
|
|
64
|
+
accountSnapshot?: { balance?: number; cleared_balance?: number; uncleared_balance?: number },
|
|
64
65
|
): BalanceInfo {
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
// Compute from the fetched transactions, but prefer the authoritative account snapshot
|
|
67
|
+
// because we usually fetch a limited date window.
|
|
68
|
+
let computedCleared = 0;
|
|
69
|
+
let computedUncleared = 0;
|
|
67
70
|
|
|
68
71
|
for (const txn of ynabTransactions) {
|
|
69
72
|
const amount = txn.amount; // Milliunits
|
|
70
73
|
|
|
71
74
|
if (txn.cleared === 'cleared' || txn.cleared === 'reconciled') {
|
|
72
|
-
|
|
75
|
+
computedCleared += amount;
|
|
73
76
|
} else {
|
|
74
|
-
|
|
77
|
+
computedUncleared += amount;
|
|
75
78
|
}
|
|
76
79
|
}
|
|
77
80
|
|
|
81
|
+
const clearedBalance = accountSnapshot?.cleared_balance ?? computedCleared;
|
|
82
|
+
const unclearedBalance = accountSnapshot?.uncleared_balance ?? computedUncleared;
|
|
83
|
+
const totalBalance = accountSnapshot?.balance ?? clearedBalance + unclearedBalance;
|
|
84
|
+
|
|
78
85
|
const statementBalanceMilli = Math.round(statementBalanceDecimal * 1000);
|
|
79
|
-
const totalBalance = clearedBalance + unclearedBalance;
|
|
80
86
|
const discrepancy = clearedBalance - statementBalanceMilli;
|
|
81
87
|
|
|
82
88
|
return {
|
|
@@ -342,6 +348,7 @@ export function analyzeReconciliation(
|
|
|
342
348
|
budgetId?: string,
|
|
343
349
|
invertBankAmounts: boolean = false,
|
|
344
350
|
csvOptions?: ParseCSVOptions,
|
|
351
|
+
accountSnapshot?: { balance?: number; cleared_balance?: number; uncleared_balance?: number },
|
|
345
352
|
): ReconciliationAnalysis {
|
|
346
353
|
// Step 1: Parse bank CSV using new Parser (or use provided result)
|
|
347
354
|
let parseResult: CSVParseResult;
|
|
@@ -398,7 +405,12 @@ export function analyzeReconciliation(
|
|
|
398
405
|
const unmatchedYNAB = newYNABTransactions.filter((t) => !matchedYnabIds.has(t.id));
|
|
399
406
|
|
|
400
407
|
// Step 6: Calculate balances
|
|
401
|
-
const balances = calculateBalances(
|
|
408
|
+
const balances = calculateBalances(
|
|
409
|
+
newYNABTransactions,
|
|
410
|
+
statementBalance,
|
|
411
|
+
currency,
|
|
412
|
+
accountSnapshot,
|
|
413
|
+
);
|
|
402
414
|
|
|
403
415
|
// Step 7: Generate summary
|
|
404
416
|
const summary = generateSummary(
|
|
@@ -349,6 +349,12 @@ export async function handleReconcileAccount(
|
|
|
349
349
|
},
|
|
350
350
|
};
|
|
351
351
|
|
|
352
|
+
const initialAccount: AccountSnapshot = {
|
|
353
|
+
balance: accountData.balance,
|
|
354
|
+
cleared_balance: accountData.cleared_balance,
|
|
355
|
+
uncleared_balance: accountData.uncleared_balance,
|
|
356
|
+
};
|
|
357
|
+
|
|
352
358
|
// Perform analysis
|
|
353
359
|
const analysis = analyzeReconciliation(
|
|
354
360
|
parseResult ?? csvContent,
|
|
@@ -361,14 +367,9 @@ export async function handleReconcileAccount(
|
|
|
361
367
|
params.budget_id,
|
|
362
368
|
finalInvertAmounts, // Use smart-detected value
|
|
363
369
|
csvOptions,
|
|
370
|
+
initialAccount,
|
|
364
371
|
);
|
|
365
372
|
|
|
366
|
-
const initialAccount: AccountSnapshot = {
|
|
367
|
-
balance: accountData.balance,
|
|
368
|
-
cleared_balance: accountData.cleared_balance,
|
|
369
|
-
uncleared_balance: accountData.uncleared_balance,
|
|
370
|
-
};
|
|
371
|
-
|
|
372
373
|
let executionData: LegacyReconciliationResult | undefined;
|
|
373
374
|
const wantsBalanceVerification = Boolean(params.statement_date);
|
|
374
375
|
const shouldExecute =
|