@dizzlkheinz/ynab-mcpb 0.18.4 → 0.19.0
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/CLAUDE.md +87 -8
- package/bin/ynab-mcp-server.cjs +2 -2
- package/bin/ynab-mcp-server.js +3 -3
- package/biome.json +39 -0
- package/dist/bundle/index.cjs +67 -67
- package/dist/index.d.ts +1 -1
- package/dist/index.js +27 -27
- package/dist/server/YNABMCPServer.d.ts +3 -4
- package/dist/server/YNABMCPServer.js +111 -116
- package/dist/server/budgetResolver.d.ts +6 -5
- package/dist/server/budgetResolver.js +46 -36
- package/dist/server/cacheKeys.js +6 -6
- package/dist/server/cacheManager.js +14 -11
- package/dist/server/completions.d.ts +2 -2
- package/dist/server/completions.js +20 -15
- package/dist/server/config.d.ts +10 -5
- package/dist/server/config.js +24 -7
- package/dist/server/deltaCache.d.ts +2 -2
- package/dist/server/deltaCache.js +22 -16
- package/dist/server/deltaCache.merge.d.ts +2 -2
- package/dist/server/diagnostics.d.ts +4 -4
- package/dist/server/diagnostics.js +38 -32
- package/dist/server/errorHandler.d.ts +5 -12
- package/dist/server/errorHandler.js +219 -217
- package/dist/server/prompts.d.ts +2 -2
- package/dist/server/prompts.js +45 -45
- package/dist/server/rateLimiter.js +4 -4
- package/dist/server/requestLogger.d.ts +1 -1
- package/dist/server/requestLogger.js +40 -35
- package/dist/server/resources.d.ts +3 -3
- package/dist/server/resources.js +55 -52
- package/dist/server/responseFormatter.js +6 -6
- package/dist/server/securityMiddleware.d.ts +2 -2
- package/dist/server/securityMiddleware.js +22 -20
- package/dist/server/serverKnowledgeStore.js +1 -1
- package/dist/server/toolRegistry.d.ts +3 -3
- package/dist/server/toolRegistry.js +47 -40
- package/dist/tools/__tests__/deltaTestUtils.d.ts +3 -3
- package/dist/tools/__tests__/deltaTestUtils.js +2 -2
- package/dist/tools/accountTools.d.ts +9 -8
- package/dist/tools/accountTools.js +47 -47
- package/dist/tools/adapters.d.ts +13 -8
- package/dist/tools/adapters.js +21 -11
- package/dist/tools/budgetTools.d.ts +8 -7
- package/dist/tools/budgetTools.js +22 -22
- package/dist/tools/categoryTools.d.ts +9 -8
- package/dist/tools/categoryTools.js +68 -59
- package/dist/tools/compareTransactions/formatter.d.ts +3 -3
- package/dist/tools/compareTransactions/formatter.js +9 -9
- package/dist/tools/compareTransactions/index.d.ts +6 -6
- package/dist/tools/compareTransactions/index.js +58 -43
- package/dist/tools/compareTransactions/matcher.d.ts +1 -1
- package/dist/tools/compareTransactions/matcher.js +28 -15
- package/dist/tools/compareTransactions/parser.d.ts +2 -2
- package/dist/tools/compareTransactions/parser.js +144 -138
- package/dist/tools/compareTransactions/types.d.ts +4 -4
- package/dist/tools/compareTransactions.d.ts +1 -1
- package/dist/tools/compareTransactions.js +1 -1
- package/dist/tools/deltaFetcher.d.ts +2 -2
- package/dist/tools/deltaFetcher.js +16 -15
- package/dist/tools/deltaSupport.d.ts +4 -4
- package/dist/tools/deltaSupport.js +35 -41
- package/dist/tools/exportTransactions.d.ts +5 -4
- package/dist/tools/exportTransactions.js +61 -59
- package/dist/tools/monthTools.d.ts +7 -6
- package/dist/tools/monthTools.js +31 -29
- package/dist/tools/payeeTools.d.ts +7 -6
- package/dist/tools/payeeTools.js +28 -28
- package/dist/tools/reconcileAdapter.d.ts +2 -2
- package/dist/tools/reconcileAdapter.js +19 -12
- package/dist/tools/reconciliation/analyzer.d.ts +4 -4
- package/dist/tools/reconciliation/analyzer.js +73 -59
- package/dist/tools/reconciliation/csvParser.d.ts +3 -3
- package/dist/tools/reconciliation/csvParser.js +128 -104
- package/dist/tools/reconciliation/executor.d.ts +4 -4
- package/dist/tools/reconciliation/executor.js +148 -109
- package/dist/tools/reconciliation/index.d.ts +10 -10
- package/dist/tools/reconciliation/index.js +96 -83
- package/dist/tools/reconciliation/matcher.d.ts +3 -3
- package/dist/tools/reconciliation/matcher.js +17 -16
- package/dist/tools/reconciliation/payeeNormalizer.js +19 -8
- package/dist/tools/reconciliation/recommendationEngine.d.ts +1 -1
- package/dist/tools/reconciliation/recommendationEngine.js +40 -40
- package/dist/tools/reconciliation/reportFormatter.d.ts +2 -2
- package/dist/tools/reconciliation/reportFormatter.js +59 -58
- package/dist/tools/reconciliation/signDetector.d.ts +1 -1
- package/dist/tools/reconciliation/types.d.ts +16 -16
- package/dist/tools/reconciliation/ynabAdapter.d.ts +2 -2
- package/dist/tools/schemas/common.d.ts +1 -1
- package/dist/tools/schemas/common.js +1 -1
- package/dist/tools/schemas/outputs/accountOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/accountOutputs.js +24 -18
- package/dist/tools/schemas/outputs/budgetOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/budgetOutputs.js +14 -11
- package/dist/tools/schemas/outputs/categoryOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/categoryOutputs.js +49 -29
- package/dist/tools/schemas/outputs/comparisonOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/comparisonOutputs.js +12 -12
- package/dist/tools/schemas/outputs/index.d.ts +14 -14
- package/dist/tools/schemas/outputs/index.js +14 -14
- package/dist/tools/schemas/outputs/monthOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/monthOutputs.js +56 -41
- package/dist/tools/schemas/outputs/payeeOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/payeeOutputs.js +10 -10
- package/dist/tools/schemas/outputs/reconciliationOutputs.d.ts +2 -2
- package/dist/tools/schemas/outputs/reconciliationOutputs.js +45 -45
- package/dist/tools/schemas/outputs/transactionMutationOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/transactionMutationOutputs.js +28 -22
- package/dist/tools/schemas/outputs/transactionOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/transactionOutputs.js +43 -35
- package/dist/tools/schemas/outputs/utilityOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/utilityOutputs.js +5 -3
- package/dist/tools/schemas/shared/commonOutputs.d.ts +1 -1
- package/dist/tools/schemas/shared/commonOutputs.js +15 -9
- package/dist/tools/transactionReadTools.d.ts +11 -0
- package/dist/tools/transactionReadTools.js +202 -0
- package/dist/tools/transactionSchemas.d.ts +7 -7
- package/dist/tools/transactionSchemas.js +77 -57
- package/dist/tools/transactionTools.d.ts +6 -24
- package/dist/tools/transactionTools.js +7 -1499
- package/dist/tools/transactionUtils.d.ts +6 -6
- package/dist/tools/transactionUtils.js +78 -63
- package/dist/tools/transactionWriteTools.d.ts +20 -0
- package/dist/tools/transactionWriteTools.js +1342 -0
- package/dist/tools/utilityTools.d.ts +5 -4
- package/dist/tools/utilityTools.js +11 -11
- package/dist/types/index.d.ts +7 -7
- package/dist/types/index.js +6 -6
- package/dist/types/reconciliation.d.ts +1 -1
- package/dist/types/toolRegistration.d.ts +14 -12
- package/dist/utils/amountUtils.js +1 -1
- package/dist/utils/dateUtils.js +4 -4
- package/dist/utils/errors.d.ts +3 -3
- package/dist/utils/errors.js +4 -4
- package/dist/utils/money.d.ts +2 -2
- package/dist/utils/money.js +8 -8
- package/dist/utils/validationError.d.ts +1 -1
- package/dist/utils/validationError.js +1 -1
- package/docs/assets/examples/reconciliation-with-recommendations.json +66 -66
- package/docs/assets/schemas/reconciliation-v2.json +360 -336
- package/esbuild.config.mjs +53 -50
- package/meta.json +12548 -12548
- package/package.json +98 -111
- package/scripts/analyze-bundle.mjs +33 -30
- package/scripts/create-pr-description.js +169 -120
- package/scripts/run-all-tests.js +178 -169
- package/scripts/run-domain-integration-tests.js +28 -18
- package/scripts/run-generate-mcpb.js +19 -17
- package/scripts/run-throttled-integration-tests.js +92 -83
- package/scripts/test-delta-params.mjs +149 -120
- package/scripts/test-recommendations.ts +36 -32
- package/scripts/tmpTransaction.ts +80 -43
- package/scripts/validate-env.js +98 -91
- package/scripts/verify-build.js +78 -76
- package/src/__tests__/comprehensive.integration.test.ts +1281 -1154
- package/src/__tests__/performance.test.ts +723 -671
- package/src/__tests__/setup.ts +442 -395
- package/src/__tests__/smoke.e2e.test.ts +41 -39
- package/src/__tests__/testRunner.ts +314 -295
- package/src/__tests__/testUtils.ts +456 -364
- package/src/__tests__/tools/reconciliation/csvParser.integration.test.ts +109 -107
- package/src/__tests__/tools/reconciliation/real-world.integration.test.ts +41 -41
- package/src/index.ts +68 -59
- package/src/server/CLAUDE.md +480 -0
- package/src/server/YNABMCPServer.ts +821 -794
- package/src/server/__tests__/YNABMCPServer.integration.test.ts +929 -893
- package/src/server/__tests__/YNABMCPServer.test.ts +903 -899
- package/src/server/__tests__/budgetResolver.test.ts +466 -423
- package/src/server/__tests__/cacheManager.test.ts +891 -874
- package/src/server/__tests__/completions.integration.test.ts +115 -106
- package/src/server/__tests__/completions.test.ts +334 -313
- package/src/server/__tests__/config.test.ts +98 -86
- package/src/server/__tests__/deltaCache.merge.test.ts +774 -703
- package/src/server/__tests__/deltaCache.swr.test.ts +198 -153
- package/src/server/__tests__/deltaCache.test.ts +946 -759
- package/src/server/__tests__/diagnostics.test.ts +825 -792
- package/src/server/__tests__/errorHandler.integration.test.ts +512 -462
- package/src/server/__tests__/errorHandler.test.ts +402 -397
- package/src/server/__tests__/prompts.test.ts +424 -347
- package/src/server/__tests__/rateLimiter.test.ts +313 -309
- package/src/server/__tests__/requestLogger.test.ts +443 -403
- package/src/server/__tests__/resources.template.test.ts +196 -185
- package/src/server/__tests__/resources.test.ts +294 -288
- package/src/server/__tests__/security.integration.test.ts +487 -421
- package/src/server/__tests__/securityMiddleware.test.ts +519 -444
- package/src/server/__tests__/server-startup.integration.test.ts +509 -490
- package/src/server/__tests__/serverKnowledgeStore.test.ts +174 -173
- package/src/server/__tests__/toolRegistration.test.ts +239 -210
- package/src/server/__tests__/toolRegistry.test.ts +907 -845
- package/src/server/budgetResolver.ts +221 -181
- package/src/server/cacheKeys.ts +6 -6
- package/src/server/cacheManager.ts +498 -484
- package/src/server/completions.ts +267 -243
- package/src/server/config.ts +35 -14
- package/src/server/deltaCache.merge.ts +146 -128
- package/src/server/deltaCache.ts +352 -309
- package/src/server/diagnostics.ts +257 -242
- package/src/server/errorHandler.ts +747 -744
- package/src/server/prompts.ts +181 -176
- package/src/server/rateLimiter.ts +131 -129
- package/src/server/requestLogger.ts +350 -322
- package/src/server/resources.ts +442 -374
- package/src/server/responseFormatter.ts +41 -37
- package/src/server/securityMiddleware.ts +223 -205
- package/src/server/serverKnowledgeStore.ts +67 -67
- package/src/server/toolRegistry.ts +508 -474
- package/src/tools/CLAUDE.md +604 -0
- package/src/tools/__tests__/accountTools.delta.integration.test.ts +128 -111
- package/src/tools/__tests__/accountTools.integration.test.ts +129 -111
- package/src/tools/__tests__/accountTools.test.ts +685 -638
- package/src/tools/__tests__/adapters.test.ts +142 -108
- package/src/tools/__tests__/budgetTools.delta.integration.test.ts +73 -73
- package/src/tools/__tests__/budgetTools.integration.test.ts +132 -124
- package/src/tools/__tests__/budgetTools.test.ts +442 -413
- package/src/tools/__tests__/categoryTools.delta.integration.test.ts +76 -68
- package/src/tools/__tests__/categoryTools.integration.test.ts +314 -288
- package/src/tools/__tests__/categoryTools.test.ts +656 -625
- package/src/tools/__tests__/compareTransactions/formatter.test.ts +535 -462
- package/src/tools/__tests__/compareTransactions/index.test.ts +378 -358
- package/src/tools/__tests__/compareTransactions/matcher.test.ts +497 -398
- package/src/tools/__tests__/compareTransactions/parser.test.ts +765 -747
- package/src/tools/__tests__/compareTransactions.test.ts +352 -332
- package/src/tools/__tests__/compareTransactions.window.test.ts +150 -146
- package/src/tools/__tests__/deltaFetcher.scheduled.integration.test.ts +69 -65
- package/src/tools/__tests__/deltaFetcher.test.ts +325 -265
- package/src/tools/__tests__/deltaSupport.test.ts +211 -184
- package/src/tools/__tests__/deltaTestUtils.ts +37 -33
- package/src/tools/__tests__/exportTransactions.test.ts +205 -200
- package/src/tools/__tests__/monthTools.delta.integration.test.ts +68 -68
- package/src/tools/__tests__/monthTools.integration.test.ts +178 -166
- package/src/tools/__tests__/monthTools.test.ts +561 -512
- package/src/tools/__tests__/payeeTools.delta.integration.test.ts +68 -68
- package/src/tools/__tests__/payeeTools.integration.test.ts +158 -142
- package/src/tools/__tests__/payeeTools.test.ts +486 -434
- package/src/tools/__tests__/transactionSchemas.test.ts +1202 -1186
- package/src/tools/__tests__/transactionTools.integration.test.ts +875 -825
- package/src/tools/__tests__/transactionTools.test.ts +4923 -4366
- package/src/tools/__tests__/transactionUtils.test.ts +1004 -977
- package/src/tools/__tests__/utilityTools.integration.test.ts +32 -32
- package/src/tools/__tests__/utilityTools.test.ts +68 -58
- package/src/tools/accountTools.ts +293 -271
- package/src/tools/adapters.ts +120 -63
- package/src/tools/budgetTools.ts +121 -116
- package/src/tools/categoryTools.ts +379 -339
- package/src/tools/compareTransactions/formatter.ts +131 -119
- package/src/tools/compareTransactions/index.ts +249 -214
- package/src/tools/compareTransactions/matcher.ts +259 -209
- package/src/tools/compareTransactions/parser.ts +517 -487
- package/src/tools/compareTransactions/types.ts +38 -38
- package/src/tools/compareTransactions.ts +1 -1
- package/src/tools/deltaFetcher.ts +281 -260
- package/src/tools/deltaSupport.ts +264 -259
- package/src/tools/exportTransactions.ts +230 -218
- package/src/tools/monthTools.ts +180 -165
- package/src/tools/payeeTools.ts +152 -140
- package/src/tools/reconcileAdapter.ts +297 -252
- package/src/tools/reconciliation/CLAUDE.md +506 -0
- package/src/tools/reconciliation/__tests__/adapter.causes.test.ts +133 -124
- package/src/tools/reconciliation/__tests__/adapter.test.ts +249 -230
- package/src/tools/reconciliation/__tests__/analyzer.test.ts +408 -400
- package/src/tools/reconciliation/__tests__/csvParser.test.ts +71 -69
- package/src/tools/reconciliation/__tests__/executor.integration.test.ts +348 -323
- package/src/tools/reconciliation/__tests__/executor.progress.test.ts +503 -457
- package/src/tools/reconciliation/__tests__/executor.test.ts +898 -831
- package/src/tools/reconciliation/__tests__/matcher.test.ts +667 -663
- package/src/tools/reconciliation/__tests__/payeeNormalizer.test.ts +296 -276
- package/src/tools/reconciliation/__tests__/recommendationEngine.integration.test.ts +692 -624
- package/src/tools/reconciliation/__tests__/recommendationEngine.test.ts +1008 -989
- package/src/tools/reconciliation/__tests__/reconciliation.delta.integration.test.ts +187 -146
- package/src/tools/reconciliation/__tests__/reportFormatter.test.ts +583 -533
- package/src/tools/reconciliation/__tests__/scenarios/adapterCurrency.scenario.test.ts +75 -74
- package/src/tools/reconciliation/__tests__/scenarios/extremes.scenario.test.ts +70 -62
- package/src/tools/reconciliation/__tests__/scenarios/repeatAmount.scenario.test.ts +102 -88
- package/src/tools/reconciliation/__tests__/schemaUrl.test.ts +56 -55
- package/src/tools/reconciliation/__tests__/signDetector.test.ts +209 -206
- package/src/tools/reconciliation/__tests__/ynabAdapter.test.ts +66 -60
- package/src/tools/reconciliation/analyzer.ts +564 -504
- package/src/tools/reconciliation/csvParser.ts +656 -609
- package/src/tools/reconciliation/executor.ts +1290 -1128
- package/src/tools/reconciliation/index.ts +580 -528
- package/src/tools/reconciliation/matcher.ts +256 -240
- package/src/tools/reconciliation/payeeNormalizer.ts +92 -78
- package/src/tools/reconciliation/recommendationEngine.ts +357 -345
- package/src/tools/reconciliation/reportFormatter.ts +343 -307
- package/src/tools/reconciliation/signDetector.ts +89 -83
- package/src/tools/reconciliation/types.ts +164 -159
- package/src/tools/reconciliation/ynabAdapter.ts +17 -15
- package/src/tools/schemas/CLAUDE.md +546 -0
- package/src/tools/schemas/common.ts +1 -1
- package/src/tools/schemas/outputs/__tests__/accountOutputs.test.ts +410 -409
- package/src/tools/schemas/outputs/__tests__/budgetOutputs.test.ts +305 -299
- package/src/tools/schemas/outputs/__tests__/categoryOutputs.test.ts +431 -430
- package/src/tools/schemas/outputs/__tests__/comparisonOutputs.test.ts +510 -495
- package/src/tools/schemas/outputs/__tests__/dateValidation.test.ts +179 -153
- package/src/tools/schemas/outputs/__tests__/discrepancyDirection.test.ts +293 -254
- package/src/tools/schemas/outputs/__tests__/monthOutputs.test.ts +457 -457
- package/src/tools/schemas/outputs/__tests__/payeeOutputs.test.ts +362 -356
- package/src/tools/schemas/outputs/__tests__/reconciliationOutputs.test.ts +402 -399
- package/src/tools/schemas/outputs/__tests__/transactionMutationSchemas.test.ts +225 -211
- package/src/tools/schemas/outputs/__tests__/transactionOutputs.test.ts +457 -454
- package/src/tools/schemas/outputs/__tests__/utilityOutputs.test.ts +316 -315
- package/src/tools/schemas/outputs/accountOutputs.ts +40 -34
- package/src/tools/schemas/outputs/budgetOutputs.ts +24 -19
- package/src/tools/schemas/outputs/categoryOutputs.ts +76 -56
- package/src/tools/schemas/outputs/comparisonOutputs.ts +192 -169
- package/src/tools/schemas/outputs/index.ts +163 -163
- package/src/tools/schemas/outputs/monthOutputs.ts +95 -80
- package/src/tools/schemas/outputs/payeeOutputs.ts +18 -18
- package/src/tools/schemas/outputs/reconciliationOutputs.ts +386 -373
- package/src/tools/schemas/outputs/transactionMutationOutputs.ts +259 -231
- package/src/tools/schemas/outputs/transactionOutputs.ts +81 -71
- package/src/tools/schemas/outputs/utilityOutputs.ts +90 -84
- package/src/tools/schemas/shared/commonOutputs.ts +27 -19
- package/src/tools/toolCategories.ts +114 -114
- package/src/tools/transactionReadTools.ts +327 -0
- package/src/tools/transactionSchemas.ts +322 -291
- package/src/tools/transactionTools.ts +84 -2246
- package/src/tools/transactionUtils.ts +507 -422
- package/src/tools/transactionWriteTools.ts +2110 -0
- package/src/tools/utilityTools.ts +46 -41
- package/src/types/CLAUDE.md +477 -0
- package/src/types/__tests__/index.test.ts +51 -51
- package/src/types/index.ts +43 -39
- package/src/types/integration-tests.d.ts +26 -26
- package/src/types/reconciliation.ts +29 -29
- package/src/types/toolAnnotations.ts +30 -30
- package/src/types/toolRegistration.ts +43 -32
- package/src/utils/CLAUDE.md +508 -0
- package/src/utils/__tests__/dateUtils.test.ts +174 -168
- package/src/utils/__tests__/money.test.ts +193 -187
- package/src/utils/amountUtils.ts +5 -5
- package/src/utils/baseError.ts +5 -5
- package/src/utils/dateUtils.ts +29 -26
- package/src/utils/errors.ts +14 -14
- package/src/utils/money.ts +66 -52
- package/src/utils/validationError.ts +1 -1
- package/tsconfig.json +29 -29
- package/tsconfig.prod.json +16 -16
- package/vitest-reporters/split-json-reporter.ts +247 -204
- package/vitest.config.ts +99 -95
- package/.prettierignore +0 -10
- package/.prettierrc.json +0 -10
- package/eslint.config.js +0 -49
package/dist/tools/payeeTools.js
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import { z } from
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { ToolAnnotationPresets } from
|
|
1
|
+
import { z } from "zod/v4";
|
|
2
|
+
import { CacheKeys } from "../server/cacheKeys.js";
|
|
3
|
+
import { CACHE_TTLS, CacheManager, cacheManager, } from "../server/cacheManager.js";
|
|
4
|
+
import { responseFormatter } from "../server/responseFormatter.js";
|
|
5
|
+
import { withToolErrorHandling } from "../types/index.js";
|
|
6
|
+
import { createAdapters, createBudgetResolver } from "./adapters.js";
|
|
7
|
+
import { resolveDeltaFetcherArgs } from "./deltaSupport.js";
|
|
8
|
+
import { ToolAnnotationPresets } from "./toolCategories.js";
|
|
9
9
|
export const ListPayeesSchema = z
|
|
10
10
|
.object({
|
|
11
|
-
budget_id: z.string().min(1,
|
|
11
|
+
budget_id: z.string().min(1, "Budget ID is required"),
|
|
12
12
|
limit: z.number().int().positive().optional(),
|
|
13
13
|
})
|
|
14
14
|
.strict();
|
|
15
15
|
export const GetPayeeSchema = z
|
|
16
16
|
.object({
|
|
17
|
-
budget_id: z.string().min(1,
|
|
18
|
-
payee_id: z.string().min(1,
|
|
17
|
+
budget_id: z.string().min(1, "Budget ID is required"),
|
|
18
|
+
payee_id: z.string().min(1, "Payee ID is required"),
|
|
19
19
|
})
|
|
20
20
|
.strict();
|
|
21
|
-
export async function handleListPayees(ynabAPI, deltaFetcherOrParams, maybeParams) {
|
|
21
|
+
export async function handleListPayees(ynabAPI, deltaFetcherOrParams, maybeParams, errorHandler) {
|
|
22
22
|
const { deltaFetcher, params } = resolveDeltaFetcherArgs(ynabAPI, deltaFetcherOrParams, maybeParams);
|
|
23
23
|
return await withToolErrorHandling(async () => {
|
|
24
24
|
const result = await deltaFetcher.fetchPayees(params.budget_id);
|
|
@@ -31,7 +31,7 @@ export async function handleListPayees(ynabAPI, deltaFetcherOrParams, maybeParam
|
|
|
31
31
|
return {
|
|
32
32
|
content: [
|
|
33
33
|
{
|
|
34
|
-
type:
|
|
34
|
+
type: "text",
|
|
35
35
|
text: responseFormatter.format({
|
|
36
36
|
payees: payees.map((payee) => ({
|
|
37
37
|
id: payee.id,
|
|
@@ -43,17 +43,17 @@ export async function handleListPayees(ynabAPI, deltaFetcherOrParams, maybeParam
|
|
|
43
43
|
returned_count: payees.length,
|
|
44
44
|
cached: wasCached,
|
|
45
45
|
cache_info: wasCached
|
|
46
|
-
? `Data retrieved from cache for improved performance${result.usedDelta ?
|
|
47
|
-
:
|
|
46
|
+
? `Data retrieved from cache for improved performance${result.usedDelta ? " (delta merge applied)" : ""}`
|
|
47
|
+
: "Fresh data retrieved from YNAB API",
|
|
48
48
|
}),
|
|
49
49
|
},
|
|
50
50
|
],
|
|
51
51
|
};
|
|
52
|
-
},
|
|
52
|
+
}, "ynab:list_payees", "listing payees", errorHandler);
|
|
53
53
|
}
|
|
54
|
-
export async function handleGetPayee(ynabAPI, params) {
|
|
54
|
+
export async function handleGetPayee(ynabAPI, params, errorHandler) {
|
|
55
55
|
return await withToolErrorHandling(async () => {
|
|
56
|
-
const cacheKey = CacheManager.generateKey(CacheKeys.PAYEES,
|
|
56
|
+
const cacheKey = CacheManager.generateKey(CacheKeys.PAYEES, "get", params.budget_id, params.payee_id);
|
|
57
57
|
const wasCached = cacheManager.has(cacheKey);
|
|
58
58
|
const payee = await cacheManager.wrap(cacheKey, {
|
|
59
59
|
ttl: CACHE_TTLS.PAYEES,
|
|
@@ -65,7 +65,7 @@ export async function handleGetPayee(ynabAPI, params) {
|
|
|
65
65
|
return {
|
|
66
66
|
content: [
|
|
67
67
|
{
|
|
68
|
-
type:
|
|
68
|
+
type: "text",
|
|
69
69
|
text: responseFormatter.format({
|
|
70
70
|
payee: {
|
|
71
71
|
id: payee.id,
|
|
@@ -75,40 +75,40 @@ export async function handleGetPayee(ynabAPI, params) {
|
|
|
75
75
|
},
|
|
76
76
|
cached: wasCached,
|
|
77
77
|
cache_info: wasCached
|
|
78
|
-
?
|
|
79
|
-
:
|
|
78
|
+
? "Data retrieved from cache for improved performance"
|
|
79
|
+
: "Fresh data retrieved from YNAB API",
|
|
80
80
|
}),
|
|
81
81
|
},
|
|
82
82
|
],
|
|
83
83
|
};
|
|
84
|
-
},
|
|
84
|
+
}, "ynab:get_payee", "getting payee details", errorHandler);
|
|
85
85
|
}
|
|
86
86
|
export const registerPayeeTools = (registry, context) => {
|
|
87
87
|
const { adapt, adaptWithDelta } = createAdapters(context);
|
|
88
88
|
const budgetResolver = createBudgetResolver(context);
|
|
89
89
|
registry.register({
|
|
90
|
-
name:
|
|
91
|
-
description:
|
|
90
|
+
name: "list_payees",
|
|
91
|
+
description: "List all payees for a specific budget",
|
|
92
92
|
inputSchema: ListPayeesSchema,
|
|
93
93
|
handler: adaptWithDelta(handleListPayees),
|
|
94
94
|
defaultArgumentResolver: budgetResolver(),
|
|
95
95
|
metadata: {
|
|
96
96
|
annotations: {
|
|
97
97
|
...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
|
|
98
|
-
title:
|
|
98
|
+
title: "YNAB: List Payees",
|
|
99
99
|
},
|
|
100
100
|
},
|
|
101
101
|
});
|
|
102
102
|
registry.register({
|
|
103
|
-
name:
|
|
104
|
-
description:
|
|
103
|
+
name: "get_payee",
|
|
104
|
+
description: "Get detailed information for a specific payee",
|
|
105
105
|
inputSchema: GetPayeeSchema,
|
|
106
106
|
handler: adapt(handleGetPayee),
|
|
107
107
|
defaultArgumentResolver: budgetResolver(),
|
|
108
108
|
metadata: {
|
|
109
109
|
annotations: {
|
|
110
110
|
...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
|
|
111
|
-
title:
|
|
111
|
+
title: "YNAB: Get Payee Details",
|
|
112
112
|
},
|
|
113
113
|
},
|
|
114
114
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
1
|
+
import type { LegacyReconciliationResult } from "./reconciliation/executor.js";
|
|
2
|
+
import type { ReconciliationAnalysis } from "./reconciliation/types.js";
|
|
3
3
|
interface AdapterOptions {
|
|
4
4
|
accountName?: string;
|
|
5
5
|
accountId?: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { toMoneyValue, toMoneyValueFromDecimal } from
|
|
2
|
-
import { formatHumanReadableReport, } from
|
|
3
|
-
const OUTPUT_VERSION =
|
|
4
|
-
const SCHEMA_URL =
|
|
1
|
+
import { toMoneyValue, toMoneyValueFromDecimal } from "../utils/money.js";
|
|
2
|
+
import { formatHumanReadableReport, } from "./reconciliation/reportFormatter.js";
|
|
3
|
+
const OUTPUT_VERSION = "2.0";
|
|
4
|
+
const SCHEMA_URL = "https://raw.githubusercontent.com/dizzlkheinz/ynab-mcp-mcpb/master/docs/schemas/reconciliation-v2.json";
|
|
5
5
|
const toBankTransactionView = (txn, currency) => ({
|
|
6
6
|
...txn,
|
|
7
7
|
amount_money: toMoneyValue(txn.amount, currency),
|
|
@@ -38,7 +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 ??
|
|
41
|
+
ynab_in_range_count: analysis.summary.ynab_in_range_count ??
|
|
42
|
+
analysis.summary.ynab_transactions_count,
|
|
42
43
|
ynab_outside_range_count: analysis.summary.ynab_outside_range_count ?? 0,
|
|
43
44
|
auto_matched: analysis.summary.auto_matched,
|
|
44
45
|
suggested_matches: analysis.summary.suggested_matches,
|
|
@@ -51,7 +52,11 @@ const convertSummary = (analysis) => ({
|
|
|
51
52
|
});
|
|
52
53
|
const convertBalanceInfo = (analysis) => {
|
|
53
54
|
const discrepancyMilli = analysis.balance_info.discrepancy.value_milliunits;
|
|
54
|
-
const direction = discrepancyMilli === 0
|
|
55
|
+
const direction = discrepancyMilli === 0
|
|
56
|
+
? "balanced"
|
|
57
|
+
: discrepancyMilli > 0
|
|
58
|
+
? "ynab_higher"
|
|
59
|
+
: "bank_higher";
|
|
55
60
|
return {
|
|
56
61
|
current_cleared: analysis.balance_info.current_cleared,
|
|
57
62
|
current_uncleared: analysis.balance_info.current_uncleared,
|
|
@@ -133,8 +138,10 @@ const buildHumanNarrative = (analysis, options, execution) => {
|
|
|
133
138
|
return formatHumanReadableReport(analysis, formatterOptions, execution);
|
|
134
139
|
};
|
|
135
140
|
export const buildReconciliationPayload = (analysis, options = {}, execution) => {
|
|
136
|
-
const currency = options.currencyCode ??
|
|
137
|
-
const executionView = execution
|
|
141
|
+
const currency = options.currencyCode ?? "USD";
|
|
142
|
+
const executionView = execution
|
|
143
|
+
? convertExecution(execution, currency)
|
|
144
|
+
: undefined;
|
|
138
145
|
const structured = {
|
|
139
146
|
version: OUTPUT_VERSION,
|
|
140
147
|
schema_url: SCHEMA_URL,
|
|
@@ -158,16 +165,16 @@ export const buildReconciliationPayload = (analysis, options = {}, execution) =>
|
|
|
158
165
|
},
|
|
159
166
|
};
|
|
160
167
|
if (analysis.recommendations && analysis.recommendations.length > 0) {
|
|
161
|
-
structured[
|
|
168
|
+
structured["recommendations"] = analysis.recommendations;
|
|
162
169
|
}
|
|
163
170
|
if (options.csvFormat) {
|
|
164
|
-
structured[
|
|
171
|
+
structured["csv_format"] = options.csvFormat;
|
|
165
172
|
}
|
|
166
173
|
if (executionView) {
|
|
167
|
-
structured[
|
|
174
|
+
structured["execution"] = executionView;
|
|
168
175
|
}
|
|
169
176
|
if (options.auditMetadata) {
|
|
170
|
-
structured[
|
|
177
|
+
structured["audit"] = options.auditMetadata;
|
|
171
178
|
}
|
|
172
179
|
return {
|
|
173
180
|
human: buildHumanNarrative(analysis, options, execution),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type * as ynab from
|
|
2
|
-
import { type
|
|
3
|
-
import { type MatchingConfig } from
|
|
4
|
-
import type { ReconciliationAnalysis } from
|
|
1
|
+
import type * as ynab from "ynab";
|
|
2
|
+
import { type CSVParseResult, type ParseCSVOptions } from "./csvParser.js";
|
|
3
|
+
import { type MatchingConfig } from "./matcher.js";
|
|
4
|
+
import type { ReconciliationAnalysis } from "./types.js";
|
|
5
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
6
|
balance?: number;
|
|
7
7
|
cleared_balance?: number;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { parseCSV } from
|
|
2
|
-
import { findMatches, normalizeConfig,
|
|
3
|
-
import { normalizeYNABTransactions } from
|
|
4
|
-
import { toMoneyValue } from
|
|
5
|
-
import { generateRecommendations } from
|
|
1
|
+
import { parseCSV, } from "./csvParser.js";
|
|
2
|
+
import { DEFAULT_CONFIG, findMatches, normalizeConfig, } from "./matcher.js";
|
|
3
|
+
import { normalizeYNABTransactions } from "./ynabAdapter.js";
|
|
4
|
+
import { toMoneyValue } from "../../utils/money.js";
|
|
5
|
+
import { generateRecommendations } from "./recommendationEngine.js";
|
|
6
6
|
function calculateDateRange(bankTransactions) {
|
|
7
7
|
if (bankTransactions.length === 0) {
|
|
8
8
|
return null;
|
|
@@ -14,20 +14,25 @@ function calculateDateRange(bankTransactions) {
|
|
|
14
14
|
if (dates.length === 0) {
|
|
15
15
|
return null;
|
|
16
16
|
}
|
|
17
|
+
const minDate = dates[0];
|
|
18
|
+
const maxDate = dates[dates.length - 1];
|
|
19
|
+
if (!minDate || !maxDate) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
17
22
|
return {
|
|
18
|
-
minDate
|
|
19
|
-
maxDate
|
|
23
|
+
minDate,
|
|
24
|
+
maxDate,
|
|
20
25
|
};
|
|
21
26
|
}
|
|
22
27
|
function filterByDateRange(ynabTransactions, dateRange, dateToleranceDays = 7) {
|
|
28
|
+
const safeToleranceDays = dateToleranceDays < 0 ? 0 : dateToleranceDays;
|
|
23
29
|
if (dateToleranceDays < 0) {
|
|
24
30
|
console.warn(`[filterByDateRange] dateToleranceDays must be non-negative, got ${dateToleranceDays}. Using 0.`);
|
|
25
|
-
dateToleranceDays = 0;
|
|
26
31
|
}
|
|
27
32
|
const inRange = [];
|
|
28
33
|
const outsideRange = [];
|
|
29
|
-
const minParts = dateRange.minDate.split(
|
|
30
|
-
const maxParts = dateRange.maxDate.split(
|
|
34
|
+
const minParts = dateRange.minDate.split("-").map(Number);
|
|
35
|
+
const maxParts = dateRange.maxDate.split("-").map(Number);
|
|
31
36
|
if (minParts.length !== 3 ||
|
|
32
37
|
maxParts.length !== 3 ||
|
|
33
38
|
minParts.some((n) => !Number.isFinite(n)) ||
|
|
@@ -37,10 +42,10 @@ function filterByDateRange(ynabTransactions, dateRange, dateToleranceDays = 7) {
|
|
|
37
42
|
}
|
|
38
43
|
const [minYear, minMonth, minDay] = minParts;
|
|
39
44
|
const [maxYear, maxMonth, maxDay] = maxParts;
|
|
40
|
-
const minDateWithBuffer = new Date(Date.UTC(minYear, minMonth - 1, minDay -
|
|
41
|
-
const minDateStr = minDateWithBuffer.toISOString().split(
|
|
42
|
-
const maxDateWithBuffer = new Date(Date.UTC(maxYear, maxMonth - 1, maxDay +
|
|
43
|
-
const maxDateStr = maxDateWithBuffer.toISOString().split(
|
|
45
|
+
const minDateWithBuffer = new Date(Date.UTC(minYear, minMonth - 1, minDay - safeToleranceDays));
|
|
46
|
+
const minDateStr = minDateWithBuffer.toISOString().split("T")[0] ?? "";
|
|
47
|
+
const maxDateWithBuffer = new Date(Date.UTC(maxYear, maxMonth - 1, maxDay + safeToleranceDays));
|
|
48
|
+
const maxDateStr = maxDateWithBuffer.toISOString().split("T")[0] ?? "";
|
|
44
49
|
for (const txn of ynabTransactions) {
|
|
45
50
|
if (txn.date >= minDateStr && txn.date <= maxDateStr) {
|
|
46
51
|
inRange.push(txn);
|
|
@@ -55,16 +60,16 @@ function mapToTransactionMatch(result) {
|
|
|
55
60
|
const candidates = result.candidates.map((c) => ({
|
|
56
61
|
ynab_transaction: c.ynabTransaction,
|
|
57
62
|
confidence: c.scores.combined,
|
|
58
|
-
match_reason: c.matchReasons.join(
|
|
59
|
-
explanation: c.matchReasons.join(
|
|
63
|
+
match_reason: c.matchReasons.join(", "),
|
|
64
|
+
explanation: c.matchReasons.join(", "),
|
|
60
65
|
}));
|
|
61
66
|
const match = {
|
|
62
67
|
bankTransaction: result.bankTransaction,
|
|
63
68
|
candidates,
|
|
64
69
|
confidence: result.confidence,
|
|
65
70
|
confidenceScore: result.confidenceScore,
|
|
66
|
-
matchReason: result.bestMatch?.matchReasons.join(
|
|
67
|
-
actionHint: result.confidence ===
|
|
71
|
+
matchReason: result.bestMatch?.matchReasons.join(", ") ?? "No match found",
|
|
72
|
+
actionHint: result.confidence === "high" ? "approve" : "review",
|
|
68
73
|
};
|
|
69
74
|
if (result.bestMatch) {
|
|
70
75
|
match.ynabTransaction = result.bestMatch.ynabTransaction;
|
|
@@ -72,8 +77,9 @@ function mapToTransactionMatch(result) {
|
|
|
72
77
|
if (result.candidates[0]) {
|
|
73
78
|
match.topConfidence = result.candidates[0].scores.combined;
|
|
74
79
|
}
|
|
75
|
-
if (result.confidence ===
|
|
76
|
-
match.recommendation =
|
|
80
|
+
if (result.confidence === "none") {
|
|
81
|
+
match.recommendation =
|
|
82
|
+
"This bank transaction is not in YNAB. Consider adding it.";
|
|
77
83
|
}
|
|
78
84
|
return match;
|
|
79
85
|
}
|
|
@@ -82,7 +88,7 @@ function calculateBalances(ynabTransactions, statementBalanceDecimal, currency,
|
|
|
82
88
|
let computedUncleared = 0;
|
|
83
89
|
for (const txn of ynabTransactions) {
|
|
84
90
|
const amount = txn.amount;
|
|
85
|
-
if (txn.cleared ===
|
|
91
|
+
if (txn.cleared === "cleared" || txn.cleared === "reconciled") {
|
|
86
92
|
computedCleared += amount;
|
|
87
93
|
}
|
|
88
94
|
else {
|
|
@@ -105,11 +111,11 @@ function calculateBalances(ynabTransactions, statementBalanceDecimal, currency,
|
|
|
105
111
|
}
|
|
106
112
|
function generateSummary(bankTransactions, ynabTransactionsInRange, ynabTransactionsOutsideRange, autoMatches, suggestedMatches, unmatchedBank, unmatchedYNAB, balances) {
|
|
107
113
|
const dates = bankTransactions.map((t) => t.date).sort();
|
|
108
|
-
const dateRange = dates.length > 0 ? `${dates[0]} to ${dates[dates.length - 1]}` :
|
|
114
|
+
const dateRange = dates.length > 0 ? `${dates[0]} to ${dates[dates.length - 1]}` : "Unknown";
|
|
109
115
|
const totalYnabCount = ynabTransactionsInRange.length + ynabTransactionsOutsideRange.length;
|
|
110
|
-
let discrepancyExplanation =
|
|
116
|
+
let discrepancyExplanation = "";
|
|
111
117
|
if (balances.on_track) {
|
|
112
|
-
discrepancyExplanation =
|
|
118
|
+
discrepancyExplanation = "Cleared balance matches statement";
|
|
113
119
|
}
|
|
114
120
|
else {
|
|
115
121
|
const actionsNeeded = [];
|
|
@@ -123,7 +129,9 @@ function generateSummary(bankTransactions, ynabTransactionsInRange, ynabTransact
|
|
|
123
129
|
actionsNeeded.push(`review ${unmatchedYNAB.length} unmatched YNAB`);
|
|
124
130
|
}
|
|
125
131
|
discrepancyExplanation =
|
|
126
|
-
actionsNeeded.length > 0
|
|
132
|
+
actionsNeeded.length > 0
|
|
133
|
+
? `Need to ${actionsNeeded.join(", ")}`
|
|
134
|
+
: "Manual review required";
|
|
127
135
|
}
|
|
128
136
|
return {
|
|
129
137
|
statement_date_range: dateRange,
|
|
@@ -156,20 +164,20 @@ function generateNextSteps(summary) {
|
|
|
156
164
|
steps.push(`Decide what to do with ${summary.unmatched_ynab} unmatched YNAB transactions (unclear/delete/ignore)`);
|
|
157
165
|
}
|
|
158
166
|
if (steps.length === 0) {
|
|
159
|
-
steps.push(
|
|
167
|
+
steps.push("All transactions matched! Review and approve to complete reconciliation");
|
|
160
168
|
}
|
|
161
169
|
return steps;
|
|
162
170
|
}
|
|
163
|
-
function formatCurrency(amountMilli, currency =
|
|
164
|
-
const formatter = new Intl.NumberFormat(
|
|
165
|
-
style:
|
|
171
|
+
function formatCurrency(amountMilli, currency = "USD") {
|
|
172
|
+
const formatter = new Intl.NumberFormat("en-US", {
|
|
173
|
+
style: "currency",
|
|
166
174
|
currency: currency,
|
|
167
175
|
minimumFractionDigits: 2,
|
|
168
176
|
maximumFractionDigits: 2,
|
|
169
177
|
});
|
|
170
178
|
return formatter.format(amountMilli / 1000);
|
|
171
179
|
}
|
|
172
|
-
function repeatAmountInsights(unmatchedBank, currency =
|
|
180
|
+
function repeatAmountInsights(unmatchedBank, currency = "USD") {
|
|
173
181
|
const insights = [];
|
|
174
182
|
if (unmatchedBank.length === 0) {
|
|
175
183
|
return insights;
|
|
@@ -188,13 +196,15 @@ function repeatAmountInsights(unmatchedBank, currency = 'USD') {
|
|
|
188
196
|
return insights;
|
|
189
197
|
}
|
|
190
198
|
const top = repeated[0];
|
|
199
|
+
if (!top) {
|
|
200
|
+
return insights;
|
|
201
|
+
}
|
|
191
202
|
insights.push({
|
|
192
203
|
id: `repeat-${top.amount}`,
|
|
193
|
-
type:
|
|
194
|
-
severity: top.txns.length >= 4 ?
|
|
204
|
+
type: "repeat_amount",
|
|
205
|
+
severity: top.txns.length >= 4 ? "critical" : "warning",
|
|
195
206
|
title: `${top.txns.length} unmatched transactions at ${formatCurrency(top.amount, currency)}`,
|
|
196
|
-
description: `The bank statement shows ${top.txns.length} unmatched transaction(s) at ${formatCurrency(top.amount, currency)}.
|
|
197
|
-
'Repeated amounts are usually the quickest wins — reconcile these first.',
|
|
207
|
+
description: `The bank statement shows ${top.txns.length} unmatched transaction(s) at ${formatCurrency(top.amount, currency)}. Repeated amounts are usually the quickest wins — reconcile these first.`,
|
|
198
208
|
evidence: {
|
|
199
209
|
amount: top.amount,
|
|
200
210
|
occurrences: top.txns.length,
|
|
@@ -209,9 +219,9 @@ function anomalyInsights(balances) {
|
|
|
209
219
|
const discrepancyAbs = Math.abs(balances.discrepancy.value_milliunits);
|
|
210
220
|
if (discrepancyAbs >= 1000) {
|
|
211
221
|
insights.push({
|
|
212
|
-
id:
|
|
213
|
-
type:
|
|
214
|
-
severity: discrepancyAbs >= 100000 ?
|
|
222
|
+
id: "balance-gap",
|
|
223
|
+
type: "anomaly",
|
|
224
|
+
severity: discrepancyAbs >= 100000 ? "critical" : "warning",
|
|
215
225
|
title: `Cleared balance off by ${balances.discrepancy.value_display}`,
|
|
216
226
|
description: `YNAB cleared balance is ${balances.current_cleared.value_display} but the statement expects ` +
|
|
217
227
|
`${balances.target_statement.value_display}. Focus on closing this gap.`,
|
|
@@ -235,14 +245,15 @@ function detectInsights(unmatchedBank, _summary, balances, currency, csvErrors =
|
|
|
235
245
|
};
|
|
236
246
|
if (csvErrors.length > 0) {
|
|
237
247
|
addUnique({
|
|
238
|
-
id:
|
|
239
|
-
type:
|
|
240
|
-
severity: csvErrors.length >= 5 ?
|
|
248
|
+
id: "csv-parse-errors",
|
|
249
|
+
type: "anomaly",
|
|
250
|
+
severity: csvErrors.length >= 5 ? "critical" : "warning",
|
|
241
251
|
title: `${csvErrors.length} CSV parsing error(s)`,
|
|
242
252
|
description: csvErrors
|
|
243
253
|
.slice(0, 3)
|
|
244
254
|
.map((e) => `Row ${e.row}: ${e.message}`)
|
|
245
|
-
.join(
|
|
255
|
+
.join("; ") +
|
|
256
|
+
(csvErrors.length > 3 ? ` (+${csvErrors.length - 3} more)` : ""),
|
|
246
257
|
evidence: {
|
|
247
258
|
error_count: csvErrors.length,
|
|
248
259
|
errors: csvErrors.slice(0, 5),
|
|
@@ -251,14 +262,15 @@ function detectInsights(unmatchedBank, _summary, balances, currency, csvErrors =
|
|
|
251
262
|
}
|
|
252
263
|
if (csvWarnings.length > 0) {
|
|
253
264
|
addUnique({
|
|
254
|
-
id:
|
|
255
|
-
type:
|
|
256
|
-
severity:
|
|
265
|
+
id: "csv-parse-warnings",
|
|
266
|
+
type: "anomaly",
|
|
267
|
+
severity: "info",
|
|
257
268
|
title: `${csvWarnings.length} CSV parsing warning(s)`,
|
|
258
269
|
description: csvWarnings
|
|
259
270
|
.slice(0, 3)
|
|
260
271
|
.map((w) => `Row ${w.row}: ${w.message}`)
|
|
261
|
-
.join(
|
|
272
|
+
.join("; ") +
|
|
273
|
+
(csvWarnings.length > 3 ? ` (+${csvWarnings.length - 3} more)` : ""),
|
|
262
274
|
evidence: {
|
|
263
275
|
warning_count: csvWarnings.length,
|
|
264
276
|
warnings: csvWarnings.slice(0, 5),
|
|
@@ -273,9 +285,9 @@ function detectInsights(unmatchedBank, _summary, balances, currency, csvErrors =
|
|
|
273
285
|
}
|
|
274
286
|
return insights.slice(0, 5);
|
|
275
287
|
}
|
|
276
|
-
export function analyzeReconciliation(csvContentOrParsed, _csvFilePath, ynabTransactions, statementBalance, config = DEFAULT_CONFIG, currency =
|
|
288
|
+
export function analyzeReconciliation(csvContentOrParsed, _csvFilePath, ynabTransactions, statementBalance, config = DEFAULT_CONFIG, currency = "USD", accountId, budgetId, invertBankAmounts = false, csvOptions, accountSnapshot) {
|
|
277
289
|
let parseResult;
|
|
278
|
-
if (typeof csvContentOrParsed ===
|
|
290
|
+
if (typeof csvContentOrParsed === "string") {
|
|
279
291
|
parseResult = parseCSV(csvContentOrParsed, {
|
|
280
292
|
...csvOptions,
|
|
281
293
|
invertAmounts: invertBankAmounts,
|
|
@@ -304,21 +316,23 @@ export function analyzeReconciliation(csvContentOrParsed, _csvFilePath, ynabTran
|
|
|
304
316
|
const normalizedConfig = normalizeConfig(config);
|
|
305
317
|
const newMatches = findMatches(newBankTransactions, ynabInRange, normalizedConfig);
|
|
306
318
|
const matches = newMatches.map(mapToTransactionMatch);
|
|
307
|
-
const autoMatches = matches.filter((m) => m.confidence ===
|
|
319
|
+
const autoMatches = matches.filter((m) => m.confidence === "high");
|
|
308
320
|
const autoMatchedYnabIds = new Set();
|
|
309
|
-
autoMatches
|
|
310
|
-
if (
|
|
311
|
-
autoMatchedYnabIds.add(
|
|
312
|
-
|
|
313
|
-
|
|
321
|
+
for (const match of autoMatches) {
|
|
322
|
+
if (match.ynabTransaction) {
|
|
323
|
+
autoMatchedYnabIds.add(match.ynabTransaction.id);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
const suggestedMatches = matches.filter((m) => m.confidence === "medium" &&
|
|
314
327
|
(!m.ynabTransaction || !autoMatchedYnabIds.has(m.ynabTransaction.id)));
|
|
315
|
-
const unmatchedBankMatches = matches.filter((m) => m.confidence ===
|
|
328
|
+
const unmatchedBankMatches = matches.filter((m) => m.confidence === "low" || m.confidence === "none");
|
|
316
329
|
const unmatchedBank = unmatchedBankMatches.map((m) => m.bankTransaction);
|
|
317
330
|
const matchedYnabIds = new Set();
|
|
318
|
-
matches
|
|
319
|
-
if (
|
|
320
|
-
matchedYnabIds.add(
|
|
321
|
-
|
|
331
|
+
for (const match of matches) {
|
|
332
|
+
if (match.ynabTransaction) {
|
|
333
|
+
matchedYnabIds.add(match.ynabTransaction.id);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
322
336
|
const unmatchedYNAB = ynabInRange.filter((t) => !matchedYnabIds.has(t.id));
|
|
323
337
|
const balances = calculateBalances(allYNABTransactions, statementBalance, currency, accountSnapshot);
|
|
324
338
|
const summary = generateSummary(matches.map((m) => m.bankTransaction), ynabInRange, ynabOutsideRange, autoMatches, suggestedMatches, unmatchedBank, unmatchedYNAB, balances);
|
|
@@ -326,7 +340,7 @@ export function analyzeReconciliation(csvContentOrParsed, _csvFilePath, ynabTran
|
|
|
326
340
|
const insights = detectInsights(unmatchedBank, summary, balances, currency, csvParseErrors, csvParseWarnings);
|
|
327
341
|
const analysis = {
|
|
328
342
|
success: true,
|
|
329
|
-
phase:
|
|
343
|
+
phase: "analysis",
|
|
330
344
|
summary,
|
|
331
345
|
auto_matches: autoMatches,
|
|
332
346
|
suggested_matches: suggestedMatches,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BankTransaction } from
|
|
1
|
+
import type { BankTransaction } from "../../types/reconciliation.js";
|
|
2
2
|
export interface CSVParseResult {
|
|
3
3
|
transactions: BankTransaction[];
|
|
4
4
|
errors: ParseError[];
|
|
@@ -29,7 +29,7 @@ export interface BankPreset {
|
|
|
29
29
|
creditColumn?: string;
|
|
30
30
|
descriptionColumn: string | string[];
|
|
31
31
|
amountMultiplier?: number;
|
|
32
|
-
dateFormat?:
|
|
32
|
+
dateFormat?: "YMD" | "MDY" | "DMY";
|
|
33
33
|
header?: boolean;
|
|
34
34
|
}
|
|
35
35
|
export declare const BANK_PRESETS: Record<string, BankPreset>;
|
|
@@ -46,7 +46,7 @@ export interface ParseCSVOptions {
|
|
|
46
46
|
credit?: string;
|
|
47
47
|
description?: string;
|
|
48
48
|
};
|
|
49
|
-
dateFormat?:
|
|
49
|
+
dateFormat?: "YMD" | "MDY" | "DMY";
|
|
50
50
|
header?: boolean;
|
|
51
51
|
maxRows?: number;
|
|
52
52
|
maxBytes?: number;
|