@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
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type * as ynab from
|
|
3
|
-
import
|
|
4
|
-
import type { ProgressCallback } from
|
|
5
|
-
import type { ToolFactory } from
|
|
6
|
-
import type { DeltaFetcher } from
|
|
7
|
-
export type * from
|
|
8
|
-
export { analyzeReconciliation } from
|
|
9
|
-
export { findMatches, findBestMatch } from
|
|
10
|
-
export { normalizePayee, normalizedMatch, fuzzyMatch, payeeSimilarity } from
|
|
1
|
+
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
import type * as ynab from "ynab";
|
|
3
|
+
import { z } from "zod/v4";
|
|
4
|
+
import type { ProgressCallback } from "../../server/toolRegistry.js";
|
|
5
|
+
import type { ToolFactory } from "../../types/toolRegistration.js";
|
|
6
|
+
import type { DeltaFetcher } from "../deltaFetcher.js";
|
|
7
|
+
export type * from "./types.js";
|
|
8
|
+
export { analyzeReconciliation } from "./analyzer.js";
|
|
9
|
+
export { findMatches, findBestMatch } from "./matcher.js";
|
|
10
|
+
export { normalizePayee, normalizedMatch, fuzzyMatch, payeeSimilarity, } from "./payeeNormalizer.js";
|
|
11
11
|
export declare const ReconcileAccountSchema: z.ZodObject<{
|
|
12
12
|
budget_id: z.ZodString;
|
|
13
13
|
account_id: z.ZodString;
|
|
@@ -1,45 +1,45 @@
|
|
|
1
|
-
import { promises as fs } from
|
|
2
|
-
import { z } from
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { CompareTransactionsSchema, handleCompareTransactions, } from
|
|
7
|
-
import {
|
|
8
|
-
import { buildReconciliationPayload } from
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { parseCSV } from
|
|
12
|
-
import {
|
|
13
|
-
import { detectSignInversion } from
|
|
14
|
-
import { normalizeYNABTransactions } from
|
|
15
|
-
export { analyzeReconciliation } from
|
|
16
|
-
export { findMatches, findBestMatch } from
|
|
17
|
-
export { normalizePayee, normalizedMatch, fuzzyMatch, payeeSimilarity } from
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import { z } from "zod/v4";
|
|
3
|
+
import { responseFormatter } from "../../server/responseFormatter.js";
|
|
4
|
+
import { withToolErrorHandling } from "../../types/index.js";
|
|
5
|
+
import { createAdapters, createBudgetResolver } from "../adapters.js";
|
|
6
|
+
import { CompareTransactionsSchema, handleCompareTransactions, } from "../compareTransactions/index.js";
|
|
7
|
+
import { resolveDeltaFetcherArgs } from "../deltaSupport.js";
|
|
8
|
+
import { buildReconciliationPayload } from "../reconcileAdapter.js";
|
|
9
|
+
import { ToolAnnotationPresets } from "../toolCategories.js";
|
|
10
|
+
import { analyzeReconciliation } from "./analyzer.js";
|
|
11
|
+
import { parseCSV, } from "./csvParser.js";
|
|
12
|
+
import { executeReconciliation, } from "./executor.js";
|
|
13
|
+
import { detectSignInversion } from "./signDetector.js";
|
|
14
|
+
import { normalizeYNABTransactions } from "./ynabAdapter.js";
|
|
15
|
+
export { analyzeReconciliation } from "./analyzer.js";
|
|
16
|
+
export { findMatches, findBestMatch } from "./matcher.js";
|
|
17
|
+
export { normalizePayee, normalizedMatch, fuzzyMatch, payeeSimilarity, } from "./payeeNormalizer.js";
|
|
18
18
|
function getAuditDataSource(transactionsResult, forceFullRefresh) {
|
|
19
19
|
if (forceFullRefresh) {
|
|
20
|
-
return
|
|
20
|
+
return "full_api_fetch_no_delta";
|
|
21
21
|
}
|
|
22
22
|
if (transactionsResult.usedDelta) {
|
|
23
|
-
return
|
|
23
|
+
return "delta_fetch_with_merge";
|
|
24
24
|
}
|
|
25
25
|
if (transactionsResult.wasCached) {
|
|
26
|
-
return
|
|
26
|
+
return "delta_fetch_cache_hit";
|
|
27
27
|
}
|
|
28
|
-
return
|
|
28
|
+
return "delta_fetch_full_refresh";
|
|
29
29
|
}
|
|
30
30
|
function getDataFreshness(transactionsResult, forceFullRefresh) {
|
|
31
31
|
if (forceFullRefresh) {
|
|
32
|
-
return
|
|
32
|
+
return "guaranteed_fresh";
|
|
33
33
|
}
|
|
34
34
|
if (transactionsResult.wasCached) {
|
|
35
|
-
return
|
|
35
|
+
return "cache_validated_via_server_knowledge";
|
|
36
36
|
}
|
|
37
|
-
return
|
|
37
|
+
return "fresh_via_delta_fetch";
|
|
38
38
|
}
|
|
39
39
|
export const ReconcileAccountSchema = z
|
|
40
40
|
.object({
|
|
41
|
-
budget_id: z.string().min(1,
|
|
42
|
-
account_id: z.string().min(1,
|
|
41
|
+
budget_id: z.string().min(1, "Budget ID is required"),
|
|
42
|
+
account_id: z.string().min(1, "Account ID is required"),
|
|
43
43
|
csv_file_path: z.string().optional(),
|
|
44
44
|
csv_data: z.string().optional(),
|
|
45
45
|
csv_format: z
|
|
@@ -56,7 +56,7 @@ export const ReconcileAccountSchema = z
|
|
|
56
56
|
.strict()
|
|
57
57
|
.optional(),
|
|
58
58
|
statement_balance: z.number({
|
|
59
|
-
message:
|
|
59
|
+
message: "Statement balance is required and must be a number",
|
|
60
60
|
}),
|
|
61
61
|
statement_start_date: z.string().optional(),
|
|
62
62
|
statement_end_date: z.string().optional(),
|
|
@@ -75,20 +75,26 @@ export const ReconcileAccountSchema = z
|
|
|
75
75
|
invert_bank_amounts: z.boolean().optional(),
|
|
76
76
|
dry_run: z.boolean().optional().default(true),
|
|
77
77
|
balance_verification_mode: z
|
|
78
|
-
.enum([
|
|
78
|
+
.enum(["ANALYSIS_ONLY", "GUIDED_RESOLUTION", "AUTO_RESOLVE"])
|
|
79
79
|
.optional()
|
|
80
|
-
.default(
|
|
80
|
+
.default("ANALYSIS_ONLY"),
|
|
81
81
|
require_exact_match: z.boolean().optional().default(true),
|
|
82
82
|
confidence_threshold: z.number().min(0).max(1).optional().default(0.8),
|
|
83
|
-
max_resolution_attempts: z
|
|
83
|
+
max_resolution_attempts: z
|
|
84
|
+
.number()
|
|
85
|
+
.int()
|
|
86
|
+
.min(1)
|
|
87
|
+
.max(10)
|
|
88
|
+
.optional()
|
|
89
|
+
.default(5),
|
|
84
90
|
include_structured_data: z.boolean().optional().default(false),
|
|
85
91
|
force_full_refresh: z.boolean().optional().default(true),
|
|
86
92
|
})
|
|
87
93
|
.refine((data) => data.csv_file_path || data.csv_data, {
|
|
88
|
-
message:
|
|
89
|
-
path: [
|
|
94
|
+
message: "Either csv_file_path or csv_data must be provided",
|
|
95
|
+
path: ["csv_data"],
|
|
90
96
|
});
|
|
91
|
-
export async function handleReconcileAccount(ynabAPI, deltaFetcherOrParams, maybeParams, sendProgress) {
|
|
97
|
+
export async function handleReconcileAccount(ynabAPI, deltaFetcherOrParams, maybeParams, sendProgress, errorHandler) {
|
|
92
98
|
const { deltaFetcher, params } = resolveDeltaFetcherArgs(ynabAPI, deltaFetcherOrParams, maybeParams);
|
|
93
99
|
const forceFullRefresh = params.force_full_refresh ?? true;
|
|
94
100
|
return await withToolErrorHandling(async () => {
|
|
@@ -116,21 +122,23 @@ export async function handleReconcileAccount(ynabAPI, deltaFetcherOrParams, mayb
|
|
|
116
122
|
}
|
|
117
123
|
const accountName = accountData.name;
|
|
118
124
|
const accountType = accountData.type;
|
|
119
|
-
const accountIsLiability = accountType ===
|
|
120
|
-
accountType ===
|
|
121
|
-
accountType ===
|
|
122
|
-
accountType ===
|
|
123
|
-
accountType ===
|
|
124
|
-
accountType ===
|
|
125
|
-
accountType ===
|
|
126
|
-
accountType ===
|
|
127
|
-
accountType ===
|
|
128
|
-
const shouldInvertBankAmounts = params.invert_bank_amounts !== undefined
|
|
125
|
+
const accountIsLiability = accountType === "creditCard" ||
|
|
126
|
+
accountType === "lineOfCredit" ||
|
|
127
|
+
accountType === "mortgage" ||
|
|
128
|
+
accountType === "autoLoan" ||
|
|
129
|
+
accountType === "studentLoan" ||
|
|
130
|
+
accountType === "personalLoan" ||
|
|
131
|
+
accountType === "medicalDebt" ||
|
|
132
|
+
accountType === "otherDebt" ||
|
|
133
|
+
accountType === "otherLiability";
|
|
134
|
+
const shouldInvertBankAmounts = params.invert_bank_amounts !== undefined
|
|
135
|
+
? params.invert_bank_amounts
|
|
136
|
+
: accountIsLiability;
|
|
129
137
|
const adjustedStatementBalance = accountIsLiability
|
|
130
138
|
? -Math.abs(params.statement_balance)
|
|
131
139
|
: params.statement_balance;
|
|
132
140
|
const budgetResponse = await ynabAPI.budgets.getBudgetById(params.budget_id);
|
|
133
|
-
const currencyCode = budgetResponse.data.budget?.currency_format?.iso_code ??
|
|
141
|
+
const currencyCode = budgetResponse.data.budget?.currency_format?.iso_code ?? "USD";
|
|
134
142
|
const narrativeNotes = [];
|
|
135
143
|
const dateFormat = mapCsvDateFormatToHint(params.csv_format?.date_format);
|
|
136
144
|
const csvOptions = {
|
|
@@ -159,20 +167,20 @@ export async function handleReconcileAccount(ynabAPI, deltaFetcherOrParams, mayb
|
|
|
159
167
|
delimiter: params.csv_format.delimiter,
|
|
160
168
|
}),
|
|
161
169
|
};
|
|
162
|
-
let csvContent = params.csv_data ??
|
|
170
|
+
let csvContent = params.csv_data ?? "";
|
|
163
171
|
if (!csvContent && params.csv_file_path) {
|
|
164
172
|
try {
|
|
165
|
-
csvContent = await fs.readFile(params.csv_file_path,
|
|
173
|
+
csvContent = await fs.readFile(params.csv_file_path, "utf8");
|
|
166
174
|
}
|
|
167
175
|
catch (error) {
|
|
168
176
|
const message = error instanceof Error && error.message
|
|
169
177
|
? error.message
|
|
170
|
-
:
|
|
178
|
+
: "Unknown error while reading CSV file";
|
|
171
179
|
throw new Error(`Failed to read CSV file at path ${params.csv_file_path}: ${message}`);
|
|
172
180
|
}
|
|
173
181
|
}
|
|
174
182
|
if (!csvContent.trim()) {
|
|
175
|
-
throw new Error(
|
|
183
|
+
throw new Error("CSV content is empty after reading the provided source.");
|
|
176
184
|
}
|
|
177
185
|
let rawCsvResult;
|
|
178
186
|
try {
|
|
@@ -184,25 +192,25 @@ export async function handleReconcileAccount(ynabAPI, deltaFetcherOrParams, mayb
|
|
|
184
192
|
catch (error) {
|
|
185
193
|
const message = error instanceof Error && error.message
|
|
186
194
|
? error.message
|
|
187
|
-
:
|
|
195
|
+
: "Unknown error while parsing CSV";
|
|
188
196
|
throw new Error(`Failed to parse CSV data: ${message}`);
|
|
189
197
|
}
|
|
190
198
|
let sinceDate;
|
|
191
199
|
let dateWindowSource;
|
|
192
200
|
if (params.statement_start_date) {
|
|
193
201
|
sinceDate = new Date(params.statement_start_date);
|
|
194
|
-
dateWindowSource =
|
|
202
|
+
dateWindowSource = "statement_start_date";
|
|
195
203
|
}
|
|
196
204
|
else if (rawCsvResult.transactions.length > 0) {
|
|
197
205
|
sinceDate = inferSinceDateFromTransactions(rawCsvResult.transactions);
|
|
198
|
-
dateWindowSource =
|
|
206
|
+
dateWindowSource = "csv_min_date_with_buffer";
|
|
199
207
|
}
|
|
200
208
|
else {
|
|
201
209
|
sinceDate = fallbackSinceDate();
|
|
202
|
-
dateWindowSource =
|
|
203
|
-
narrativeNotes.push(
|
|
210
|
+
dateWindowSource = "fallback_90_days";
|
|
211
|
+
narrativeNotes.push("CSV contained no parsable transactions for date detection; fetched the last 90 days from YNAB.");
|
|
204
212
|
}
|
|
205
|
-
const sinceDateString = sinceDate.toISOString().split(
|
|
213
|
+
const sinceDateString = sinceDate.toISOString().split("T")[0];
|
|
206
214
|
const transactionsResult = forceFullRefresh
|
|
207
215
|
? await deltaFetcher.fetchTransactionsByAccountFull(params.budget_id, params.account_id, sinceDateString)
|
|
208
216
|
: await deltaFetcher.fetchTransactionsByAccount(params.budget_id, params.account_id, sinceDateString);
|
|
@@ -215,14 +223,17 @@ export async function handleReconcileAccount(ynabAPI, deltaFetcherOrParams, mayb
|
|
|
215
223
|
const needsInversion = detectSignInversion(rawCsvResult.transactions, normalizedYNAB);
|
|
216
224
|
if (needsInversion !== finalInvertAmounts) {
|
|
217
225
|
narrativeNotes.push(needsInversion
|
|
218
|
-
?
|
|
219
|
-
:
|
|
226
|
+
? "Detected bank CSV amounts opposite YNAB; inverting bank amounts for matching."
|
|
227
|
+
: "Detected bank CSV amounts already align with YNAB; using CSV amounts as-is.");
|
|
220
228
|
}
|
|
221
229
|
finalInvertAmounts = needsInversion;
|
|
222
230
|
}
|
|
223
231
|
const parseResult = finalInvertAmounts === false
|
|
224
232
|
? rawCsvResult
|
|
225
|
-
: parseCSV(csvContent, {
|
|
233
|
+
: parseCSV(csvContent, {
|
|
234
|
+
...csvOptions,
|
|
235
|
+
invertAmounts: finalInvertAmounts,
|
|
236
|
+
});
|
|
226
237
|
const auditMetadata = {
|
|
227
238
|
data_freshness: getDataFreshness(transactionsResult, forceFullRefresh),
|
|
228
239
|
data_source: getAuditDataSource(transactionsResult, forceFullRefresh),
|
|
@@ -263,7 +274,7 @@ export async function handleReconcileAccount(ynabAPI, deltaFetcherOrParams, mayb
|
|
|
263
274
|
params.auto_update_cleared_status ||
|
|
264
275
|
params.auto_unclear_missing ||
|
|
265
276
|
params.auto_adjust_dates ||
|
|
266
|
-
params.balance_verification_mode !==
|
|
277
|
+
params.balance_verification_mode !== "ANALYSIS_ONLY" ||
|
|
267
278
|
wantsBalanceVerification;
|
|
268
279
|
if (shouldExecute) {
|
|
269
280
|
executionData = await executeReconciliation({
|
|
@@ -295,44 +306,44 @@ export async function handleReconcileAccount(ynabAPI, deltaFetcherOrParams, mayb
|
|
|
295
306
|
human: payload.human,
|
|
296
307
|
};
|
|
297
308
|
if (params.include_structured_data) {
|
|
298
|
-
responseData[
|
|
309
|
+
responseData["structured"] = payload.structured;
|
|
299
310
|
}
|
|
300
311
|
return {
|
|
301
312
|
content: [
|
|
302
313
|
{
|
|
303
|
-
type:
|
|
314
|
+
type: "text",
|
|
304
315
|
text: responseFormatter.format(responseData),
|
|
305
316
|
},
|
|
306
317
|
],
|
|
307
318
|
};
|
|
308
|
-
},
|
|
319
|
+
}, "ynab:reconcile_account", "analyzing account reconciliation", errorHandler);
|
|
309
320
|
}
|
|
310
321
|
export const registerReconciliationTools = (registry, context) => {
|
|
311
322
|
const { adapt, adaptWithDeltaAndProgress } = createAdapters(context);
|
|
312
323
|
const budgetResolver = createBudgetResolver(context);
|
|
313
324
|
registry.register({
|
|
314
|
-
name:
|
|
315
|
-
description:
|
|
325
|
+
name: "compare_transactions",
|
|
326
|
+
description: "Compare bank transactions from CSV with YNAB transactions to find missing entries",
|
|
316
327
|
inputSchema: CompareTransactionsSchema,
|
|
317
328
|
handler: adapt(handleCompareTransactions),
|
|
318
329
|
defaultArgumentResolver: budgetResolver(),
|
|
319
330
|
metadata: {
|
|
320
331
|
annotations: {
|
|
321
332
|
...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
|
|
322
|
-
title:
|
|
333
|
+
title: "YNAB: Compare Transactions",
|
|
323
334
|
},
|
|
324
335
|
},
|
|
325
336
|
});
|
|
326
337
|
registry.register({
|
|
327
|
-
name:
|
|
328
|
-
description:
|
|
338
|
+
name: "reconcile_account",
|
|
339
|
+
description: "Guided reconciliation workflow with human narrative, insight detection, and optional execution (create/update/unclear). Set include_structured_data=true to also get full JSON output (large).",
|
|
329
340
|
inputSchema: ReconcileAccountSchema,
|
|
330
341
|
handler: adaptWithDeltaAndProgress(handleReconcileAccount),
|
|
331
342
|
defaultArgumentResolver: budgetResolver(),
|
|
332
343
|
metadata: {
|
|
333
344
|
annotations: {
|
|
334
345
|
...ToolAnnotationPresets.WRITE_EXTERNAL_UPDATE,
|
|
335
|
-
title:
|
|
346
|
+
title: "YNAB: Reconcile Account",
|
|
336
347
|
},
|
|
337
348
|
},
|
|
338
349
|
});
|
|
@@ -341,15 +352,17 @@ function mapCsvDateFormatToHint(format) {
|
|
|
341
352
|
if (!format) {
|
|
342
353
|
return undefined;
|
|
343
354
|
}
|
|
344
|
-
const normalized = format.toUpperCase().replace(/[^YMD]/g,
|
|
345
|
-
if (normalized ===
|
|
346
|
-
|
|
355
|
+
const normalized = format.toUpperCase().replace(/[^YMD]/g, "");
|
|
356
|
+
if (normalized === "YYYYMMDD" ||
|
|
357
|
+
normalized === "YYMMDD" ||
|
|
358
|
+
normalized === "YMD") {
|
|
359
|
+
return "YMD";
|
|
347
360
|
}
|
|
348
|
-
if (normalized ===
|
|
349
|
-
return
|
|
361
|
+
if (normalized === "MMDDYYYY" || normalized === "MDY") {
|
|
362
|
+
return "MDY";
|
|
350
363
|
}
|
|
351
|
-
if (normalized ===
|
|
352
|
-
return
|
|
364
|
+
if (normalized === "DDMMYYYY" || normalized === "DMY") {
|
|
365
|
+
return "DMY";
|
|
353
366
|
}
|
|
354
367
|
return undefined;
|
|
355
368
|
}
|
|
@@ -363,19 +376,19 @@ function mapCsvFormatForPayload(format) {
|
|
|
363
376
|
}
|
|
364
377
|
return String(value);
|
|
365
378
|
};
|
|
366
|
-
const delimiter = coerceString(format.delimiter,
|
|
367
|
-
const decimalSeparator =
|
|
368
|
-
const thousandsSeparator =
|
|
369
|
-
const dateFormat = coerceString(format.date_format,
|
|
379
|
+
const delimiter = coerceString(format.delimiter, ",");
|
|
380
|
+
const decimalSeparator = ".";
|
|
381
|
+
const thousandsSeparator = ",";
|
|
382
|
+
const dateFormat = coerceString(format.date_format, "MM/DD/YYYY");
|
|
370
383
|
return {
|
|
371
|
-
delimiter: delimiter ??
|
|
384
|
+
delimiter: delimiter ?? ",",
|
|
372
385
|
decimal_separator: decimalSeparator,
|
|
373
386
|
thousands_separator: thousandsSeparator,
|
|
374
|
-
date_format: dateFormat ??
|
|
387
|
+
date_format: dateFormat ?? "MM/DD/YYYY",
|
|
375
388
|
header_row: format.has_header ?? true,
|
|
376
|
-
date_column: coerceString(format.date_column,
|
|
377
|
-
amount_column: coerceString(format.amount_column,
|
|
378
|
-
payee_column: coerceString(format.description_column,
|
|
389
|
+
date_column: coerceString(format.date_column, "") ?? null,
|
|
390
|
+
amount_column: coerceString(format.amount_column, "") ?? null,
|
|
391
|
+
payee_column: coerceString(format.description_column, "") ?? null,
|
|
379
392
|
};
|
|
380
393
|
}
|
|
381
394
|
function fallbackSinceDate() {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { BankTransaction as CanonicalBankTransaction, NormalizedYNABTransaction } from
|
|
2
|
-
import {
|
|
1
|
+
import type { BankTransaction as CanonicalBankTransaction, NormalizedYNABTransaction } from "../../types/reconciliation.js";
|
|
2
|
+
import type { MatchingConfig } from "./types.js";
|
|
3
3
|
export type { MatchingConfig };
|
|
4
4
|
export interface MatchCandidate {
|
|
5
5
|
ynabTransaction: NormalizedYNABTransaction;
|
|
@@ -15,7 +15,7 @@ export interface MatchResult {
|
|
|
15
15
|
bankTransaction: CanonicalBankTransaction;
|
|
16
16
|
bestMatch: MatchCandidate | null;
|
|
17
17
|
candidates: MatchCandidate[];
|
|
18
|
-
confidence:
|
|
18
|
+
confidence: "high" | "medium" | "low" | "none";
|
|
19
19
|
confidenceScore: number;
|
|
20
20
|
}
|
|
21
21
|
export declare const DEFAULT_CONFIG: MatchingConfig;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as fuzz from
|
|
1
|
+
import * as fuzz from "fuzzball";
|
|
2
2
|
export const DEFAULT_CONFIG = {
|
|
3
3
|
weights: {
|
|
4
4
|
amount: 0.5,
|
|
@@ -20,7 +20,8 @@ export function normalizeConfig(config) {
|
|
|
20
20
|
}
|
|
21
21
|
return {
|
|
22
22
|
weights: config.weights ?? DEFAULT_CONFIG.weights,
|
|
23
|
-
amountToleranceMilliunits: config.amountToleranceMilliunits ??
|
|
23
|
+
amountToleranceMilliunits: config.amountToleranceMilliunits ??
|
|
24
|
+
DEFAULT_CONFIG.amountToleranceMilliunits,
|
|
24
25
|
dateToleranceDays: config.dateToleranceDays ?? DEFAULT_CONFIG.dateToleranceDays,
|
|
25
26
|
autoMatchThreshold: config.autoMatchThreshold ?? DEFAULT_CONFIG.autoMatchThreshold,
|
|
26
27
|
suggestedMatchThreshold: config.suggestedMatchThreshold ?? DEFAULT_CONFIG.suggestedMatchThreshold,
|
|
@@ -33,22 +34,22 @@ export function normalizeConfig(config) {
|
|
|
33
34
|
function matchSingle(bankTxn, ynabTransactions, usedIds, configInput) {
|
|
34
35
|
const config = normalizeConfig(configInput);
|
|
35
36
|
const candidates = findCandidates(bankTxn, ynabTransactions, usedIds, config);
|
|
36
|
-
const bestMatch = candidates
|
|
37
|
+
const bestMatch = candidates[0] ?? null;
|
|
37
38
|
const confidenceScore = bestMatch?.scores.combined ?? 0;
|
|
38
39
|
let confidence;
|
|
39
40
|
if (confidenceScore >= config.autoMatchThreshold) {
|
|
40
|
-
confidence =
|
|
41
|
+
confidence = "high";
|
|
41
42
|
if (bestMatch)
|
|
42
43
|
usedIds.add(bestMatch.ynabTransaction.id);
|
|
43
44
|
}
|
|
44
45
|
else if (confidenceScore >= config.suggestedMatchThreshold) {
|
|
45
|
-
confidence =
|
|
46
|
+
confidence = "medium";
|
|
46
47
|
}
|
|
47
48
|
else if (confidenceScore >= config.minimumCandidateScore) {
|
|
48
|
-
confidence =
|
|
49
|
+
confidence = "low";
|
|
49
50
|
}
|
|
50
51
|
else {
|
|
51
|
-
confidence =
|
|
52
|
+
confidence = "none";
|
|
52
53
|
}
|
|
53
54
|
return {
|
|
54
55
|
bankTransaction: bankTxn,
|
|
@@ -94,8 +95,8 @@ function findCandidates(bankTxn, ynabTransactions, usedIds, config) {
|
|
|
94
95
|
if (scoreDiff !== 0) {
|
|
95
96
|
return scoreDiff;
|
|
96
97
|
}
|
|
97
|
-
const aUncleared = a.ynabTransaction.cleared ===
|
|
98
|
-
const bUncleared = b.ynabTransaction.cleared ===
|
|
98
|
+
const aUncleared = a.ynabTransaction.cleared === "uncleared" ? 1 : 0;
|
|
99
|
+
const bUncleared = b.ynabTransaction.cleared === "uncleared" ? 1 : 0;
|
|
99
100
|
if (aUncleared !== bUncleared) {
|
|
100
101
|
return bUncleared - aUncleared;
|
|
101
102
|
}
|
|
@@ -172,28 +173,28 @@ function calculatePayeeScore(bankPayee, ynabPayee) {
|
|
|
172
173
|
function buildMatchReasons(scores, config) {
|
|
173
174
|
const reasons = [];
|
|
174
175
|
if (scores.amount === 100) {
|
|
175
|
-
reasons.push(
|
|
176
|
+
reasons.push("Exact amount match");
|
|
176
177
|
}
|
|
177
178
|
else if (scores.amount >= 95) {
|
|
178
|
-
reasons.push(
|
|
179
|
+
reasons.push("Amount within tolerance");
|
|
179
180
|
}
|
|
180
181
|
if (scores.date === 100) {
|
|
181
|
-
reasons.push(
|
|
182
|
+
reasons.push("Same date");
|
|
182
183
|
}
|
|
183
184
|
else if (scores.date >= 90) {
|
|
184
|
-
reasons.push(
|
|
185
|
+
reasons.push("Date within 1-2 days");
|
|
185
186
|
}
|
|
186
187
|
else if (scores.date >= 50) {
|
|
187
188
|
reasons.push(`Date within ${config.dateToleranceDays} days`);
|
|
188
189
|
}
|
|
189
190
|
if (scores.payee >= 95) {
|
|
190
|
-
reasons.push(
|
|
191
|
+
reasons.push("Payee exact match");
|
|
191
192
|
}
|
|
192
193
|
else if (scores.payee >= 80) {
|
|
193
|
-
reasons.push(
|
|
194
|
+
reasons.push("Payee highly similar");
|
|
194
195
|
}
|
|
195
196
|
else if (scores.payee >= 60) {
|
|
196
|
-
reasons.push(
|
|
197
|
+
reasons.push("Payee somewhat similar");
|
|
197
198
|
}
|
|
198
199
|
return reasons;
|
|
199
200
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export function normalizePayee(payee) {
|
|
2
2
|
if (!payee)
|
|
3
|
-
return
|
|
4
|
-
return payee.toLowerCase().replace(/[^a-z0-9]/g,
|
|
3
|
+
return "";
|
|
4
|
+
return payee.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
5
5
|
}
|
|
6
6
|
export function normalizedMatch(payee1, payee2) {
|
|
7
7
|
const norm1 = normalizePayee(payee1);
|
|
@@ -16,17 +16,28 @@ function levenshteinDistance(str1, str2) {
|
|
|
16
16
|
const matrix = Array(len1 + 1)
|
|
17
17
|
.fill(null)
|
|
18
18
|
.map(() => Array(len2 + 1).fill(0));
|
|
19
|
-
for (let i = 0; i <= len1; i++)
|
|
20
|
-
matrix[i]
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
for (let i = 0; i <= len1; i++) {
|
|
20
|
+
const row = matrix[i];
|
|
21
|
+
if (row)
|
|
22
|
+
row[0] = i;
|
|
23
|
+
}
|
|
24
|
+
const firstRow = matrix[0];
|
|
25
|
+
if (firstRow) {
|
|
26
|
+
for (let j = 0; j <= len2; j++) {
|
|
27
|
+
firstRow[j] = j;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
23
30
|
for (let i = 1; i <= len1; i++) {
|
|
24
31
|
for (let j = 1; j <= len2; j++) {
|
|
32
|
+
const row = matrix[i];
|
|
33
|
+
const prevRow = matrix[i - 1];
|
|
34
|
+
if (!row || !prevRow)
|
|
35
|
+
continue;
|
|
25
36
|
const cost = str1[i - 1] === str2[j - 1] ? 0 : 1;
|
|
26
|
-
|
|
37
|
+
row[j] = Math.min((prevRow[j] ?? 0) + 1, (row[j - 1] ?? 0) + 1, (prevRow[j - 1] ?? 0) + cost);
|
|
27
38
|
}
|
|
28
39
|
}
|
|
29
|
-
return matrix[len1][len2];
|
|
40
|
+
return matrix[len1]?.[len2] ?? 0;
|
|
30
41
|
}
|
|
31
42
|
export function fuzzyMatch(payee1, payee2) {
|
|
32
43
|
const norm1 = normalizePayee(payee1);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { ActionableRecommendation, RecommendationContext } from
|
|
1
|
+
import type { ActionableRecommendation, RecommendationContext } from "./types.js";
|
|
2
2
|
export declare function generateRecommendations(context: RecommendationContext): ActionableRecommendation[];
|