@accounter/server 0.0.8-alpha-20251102200443-d7162b8ce1dfc629b8b454df17dcec9ed005a052 → 0.0.8-alpha-20251103003648-f6467c8cb9c739ec4439c260bccc7325f6a761ae
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/CHANGELOG.md +47 -7
- package/dist/green-invoice-graphql/src/mesh-artifacts/index.d.ts +7 -7
- package/dist/server/src/__generated__/types.d.ts +77 -0
- package/dist/server/src/__generated__/types.js.map +1 -1
- package/dist/server/src/modules/charges-matcher/__generated__/types.d.ts +68 -0
- package/dist/server/src/modules/charges-matcher/__generated__/types.js +7 -0
- package/dist/server/src/modules/charges-matcher/__generated__/types.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/amount-confidence.test.d.ts +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/amount-confidence.test.js +218 -0
- package/dist/server/src/modules/charges-matcher/__tests__/amount-confidence.test.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/auto-match-integration.test.d.ts +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/auto-match-integration.test.js +645 -0
- package/dist/server/src/modules/charges-matcher/__tests__/auto-match-integration.test.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/auto-match.test.d.ts +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/auto-match.test.js +530 -0
- package/dist/server/src/modules/charges-matcher/__tests__/auto-match.test.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/business-confidence.test.d.ts +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/business-confidence.test.js +143 -0
- package/dist/server/src/modules/charges-matcher/__tests__/business-confidence.test.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/candidate-filter.test.d.ts +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/candidate-filter.test.js +186 -0
- package/dist/server/src/modules/charges-matcher/__tests__/candidate-filter.test.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/charge-validator.test.d.ts +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/charge-validator.test.js +301 -0
- package/dist/server/src/modules/charges-matcher/__tests__/charge-validator.test.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/currency-confidence.test.d.ts +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/currency-confidence.test.js +127 -0
- package/dist/server/src/modules/charges-matcher/__tests__/currency-confidence.test.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/date-confidence.test.d.ts +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/date-confidence.test.js +246 -0
- package/dist/server/src/modules/charges-matcher/__tests__/date-confidence.test.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/document-aggregator.test.d.ts +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/document-aggregator.test.js +475 -0
- package/dist/server/src/modules/charges-matcher/__tests__/document-aggregator.test.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/document-amount.test.d.ts +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/document-amount.test.js +287 -0
- package/dist/server/src/modules/charges-matcher/__tests__/document-amount.test.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/document-business.test.d.ts +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/document-business.test.js +151 -0
- package/dist/server/src/modules/charges-matcher/__tests__/document-business.test.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/match-scorer.test.d.ts +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/match-scorer.test.js +550 -0
- package/dist/server/src/modules/charges-matcher/__tests__/match-scorer.test.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/overall-confidence.test.d.ts +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/overall-confidence.test.js +410 -0
- package/dist/server/src/modules/charges-matcher/__tests__/overall-confidence.test.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/single-match-integration.test.d.ts +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/single-match-integration.test.js +504 -0
- package/dist/server/src/modules/charges-matcher/__tests__/single-match-integration.test.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/single-match.test.d.ts +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/single-match.test.js +483 -0
- package/dist/server/src/modules/charges-matcher/__tests__/single-match.test.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/test-helpers.d.ts +46 -0
- package/dist/server/src/modules/charges-matcher/__tests__/test-helpers.js +143 -0
- package/dist/server/src/modules/charges-matcher/__tests__/test-helpers.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/test-infrastructure.spec.d.ts +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/test-infrastructure.spec.js +137 -0
- package/dist/server/src/modules/charges-matcher/__tests__/test-infrastructure.spec.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/transaction-aggregator.test.d.ts +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/transaction-aggregator.test.js +415 -0
- package/dist/server/src/modules/charges-matcher/__tests__/transaction-aggregator.test.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/helpers/amount-confidence.helper.d.ts +7 -0
- package/dist/server/src/modules/charges-matcher/helpers/amount-confidence.helper.js +70 -0
- package/dist/server/src/modules/charges-matcher/helpers/amount-confidence.helper.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/helpers/business-confidence.helper.d.ts +7 -0
- package/dist/server/src/modules/charges-matcher/helpers/business-confidence.helper.js +19 -0
- package/dist/server/src/modules/charges-matcher/helpers/business-confidence.helper.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/helpers/candidate-filter.helper.d.ts +24 -0
- package/dist/server/src/modules/charges-matcher/helpers/candidate-filter.helper.js +45 -0
- package/dist/server/src/modules/charges-matcher/helpers/candidate-filter.helper.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/helpers/charge-validator.helper.d.ts +33 -0
- package/dist/server/src/modules/charges-matcher/helpers/charge-validator.helper.js +65 -0
- package/dist/server/src/modules/charges-matcher/helpers/charge-validator.helper.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/helpers/currency-confidence.helper.d.ts +7 -0
- package/dist/server/src/modules/charges-matcher/helpers/currency-confidence.helper.js +18 -0
- package/dist/server/src/modules/charges-matcher/helpers/currency-confidence.helper.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/helpers/date-confidence.helper.d.ts +7 -0
- package/dist/server/src/modules/charges-matcher/helpers/date-confidence.helper.js +35 -0
- package/dist/server/src/modules/charges-matcher/helpers/date-confidence.helper.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/helpers/document-amount.helper.d.ts +49 -0
- package/dist/server/src/modules/charges-matcher/helpers/document-amount.helper.js +58 -0
- package/dist/server/src/modules/charges-matcher/helpers/document-amount.helper.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/helpers/document-business.helper.d.ts +13 -0
- package/dist/server/src/modules/charges-matcher/helpers/document-business.helper.js +37 -0
- package/dist/server/src/modules/charges-matcher/helpers/document-business.helper.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/helpers/overall-confidence.helper.d.ts +42 -0
- package/dist/server/src/modules/charges-matcher/helpers/overall-confidence.helper.js +77 -0
- package/dist/server/src/modules/charges-matcher/helpers/overall-confidence.helper.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/index.d.ts +3 -0
- package/dist/server/src/modules/charges-matcher/index.js +15 -0
- package/dist/server/src/modules/charges-matcher/index.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/providers/auto-match.provider.d.ts +48 -0
- package/dist/server/src/modules/charges-matcher/providers/auto-match.provider.js +133 -0
- package/dist/server/src/modules/charges-matcher/providers/auto-match.provider.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/providers/charges-matcher.provider.d.ts +38 -0
- package/dist/server/src/modules/charges-matcher/providers/charges-matcher.provider.js +248 -0
- package/dist/server/src/modules/charges-matcher/providers/charges-matcher.provider.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/providers/document-aggregator.d.ts +61 -0
- package/dist/server/src/modules/charges-matcher/providers/document-aggregator.js +153 -0
- package/dist/server/src/modules/charges-matcher/providers/document-aggregator.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/providers/match-scorer.provider.d.ts +25 -0
- package/dist/server/src/modules/charges-matcher/providers/match-scorer.provider.js +114 -0
- package/dist/server/src/modules/charges-matcher/providers/match-scorer.provider.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/providers/single-match.provider.d.ts +39 -0
- package/dist/server/src/modules/charges-matcher/providers/single-match.provider.js +189 -0
- package/dist/server/src/modules/charges-matcher/providers/single-match.provider.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/providers/transaction-aggregator.d.ts +54 -0
- package/dist/server/src/modules/charges-matcher/providers/transaction-aggregator.js +93 -0
- package/dist/server/src/modules/charges-matcher/providers/transaction-aggregator.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/resolvers/auto-match-charges.resolver.d.ts +2 -0
- package/dist/server/src/modules/charges-matcher/resolvers/auto-match-charges.resolver.js +22 -0
- package/dist/server/src/modules/charges-matcher/resolvers/auto-match-charges.resolver.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/resolvers/find-charge-matches.resolver.d.ts +2 -0
- package/dist/server/src/modules/charges-matcher/resolvers/find-charge-matches.resolver.js +24 -0
- package/dist/server/src/modules/charges-matcher/resolvers/find-charge-matches.resolver.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/resolvers/index.d.ts +2 -0
- package/dist/server/src/modules/charges-matcher/resolvers/index.js +11 -0
- package/dist/server/src/modules/charges-matcher/resolvers/index.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/typeDefs/charges-matcher.graphql.d.ts +2 -0
- package/dist/server/src/modules/charges-matcher/typeDefs/charges-matcher.graphql.js +47 -0
- package/dist/server/src/modules/charges-matcher/typeDefs/charges-matcher.graphql.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/types.d.ts +179 -0
- package/dist/server/src/modules/charges-matcher/types.js +14 -0
- package/dist/server/src/modules/charges-matcher/types.js.map +1 -0
- package/dist/server/src/modules/documents/resolvers/document-suggestions.resolver.js +2 -2
- package/dist/server/src/modules/documents/resolvers/document-suggestions.resolver.js.map +1 -1
- package/dist/server/src/modules/green-invoice/helpers/contract-to-draft.helper.js +1 -1
- package/dist/server/src/modules/green-invoice/helpers/contract-to-draft.helper.js.map +1 -1
- package/dist/server/src/modules/green-invoice/helpers/green-invoice.helper.d.ts +1 -1
- package/dist/server/src/modules/green-invoice/helpers/green-invoice.helper.js +1 -1
- package/dist/server/src/modules/green-invoice/helpers/green-invoice.helper.js.map +1 -1
- package/dist/server/src/modules-app.js +2 -0
- package/dist/server/src/modules-app.js.map +1 -1
- package/dist/server/src/shared/types/index.d.ts +1 -1
- package/package.json +4 -4
- package/src/__generated__/types.ts +87 -0
- package/src/modules/charges-matcher/README.md +279 -0
- package/src/modules/charges-matcher/__generated__/types.ts +71 -0
- package/src/modules/charges-matcher/__tests__/amount-confidence.test.ts +260 -0
- package/src/modules/charges-matcher/__tests__/auto-match-integration.test.ts +714 -0
- package/src/modules/charges-matcher/__tests__/auto-match.test.ts +621 -0
- package/src/modules/charges-matcher/__tests__/business-confidence.test.ts +177 -0
- package/src/modules/charges-matcher/__tests__/candidate-filter.test.ts +238 -0
- package/src/modules/charges-matcher/__tests__/charge-validator.test.ts +374 -0
- package/src/modules/charges-matcher/__tests__/currency-confidence.test.ts +164 -0
- package/src/modules/charges-matcher/__tests__/date-confidence.test.ts +291 -0
- package/src/modules/charges-matcher/__tests__/document-aggregator.test.ts +614 -0
- package/src/modules/charges-matcher/__tests__/document-amount.test.ts +352 -0
- package/src/modules/charges-matcher/__tests__/document-business.test.ts +192 -0
- package/src/modules/charges-matcher/__tests__/match-scorer.test.ts +659 -0
- package/src/modules/charges-matcher/__tests__/overall-confidence.test.ts +502 -0
- package/src/modules/charges-matcher/__tests__/single-match-integration.test.ts +556 -0
- package/src/modules/charges-matcher/__tests__/single-match.test.ts +608 -0
- package/src/modules/charges-matcher/__tests__/test-helpers.ts +174 -0
- package/src/modules/charges-matcher/__tests__/test-infrastructure.spec.ts +177 -0
- package/src/modules/charges-matcher/__tests__/transaction-aggregator.test.ts +547 -0
- package/src/modules/charges-matcher/documentation/README.md +331 -0
- package/src/modules/charges-matcher/documentation/SPEC.md +1503 -0
- package/src/modules/charges-matcher/documentation/TODO.md +799 -0
- package/src/modules/charges-matcher/helpers/amount-confidence.helper.ts +88 -0
- package/src/modules/charges-matcher/helpers/business-confidence.helper.ts +23 -0
- package/src/modules/charges-matcher/helpers/candidate-filter.helper.ts +56 -0
- package/src/modules/charges-matcher/helpers/charge-validator.helper.ts +100 -0
- package/src/modules/charges-matcher/helpers/currency-confidence.helper.ts +22 -0
- package/src/modules/charges-matcher/helpers/date-confidence.helper.ts +41 -0
- package/src/modules/charges-matcher/helpers/document-amount.helper.ts +77 -0
- package/src/modules/charges-matcher/helpers/document-business.helper.ts +54 -0
- package/src/modules/charges-matcher/helpers/overall-confidence.helper.ts +90 -0
- package/src/modules/charges-matcher/index.ts +17 -0
- package/src/modules/charges-matcher/providers/auto-match.provider.ts +176 -0
- package/src/modules/charges-matcher/providers/charges-matcher.provider.ts +322 -0
- package/src/modules/charges-matcher/providers/document-aggregator.ts +211 -0
- package/src/modules/charges-matcher/providers/match-scorer.provider.ts +154 -0
- package/src/modules/charges-matcher/providers/single-match.provider.ts +252 -0
- package/src/modules/charges-matcher/providers/transaction-aggregator.ts +131 -0
- package/src/modules/charges-matcher/resolvers/auto-match-charges.resolver.ts +23 -0
- package/src/modules/charges-matcher/resolvers/find-charge-matches.resolver.ts +25 -0
- package/src/modules/charges-matcher/resolvers/index.ts +12 -0
- package/src/modules/charges-matcher/typeDefs/charges-matcher.graphql.ts +47 -0
- package/src/modules/charges-matcher/types.ts +200 -0
- package/src/modules/documents/resolvers/document-suggestions.resolver.ts +2 -2
- package/src/modules/green-invoice/helpers/contract-to-draft.helper.ts +1 -1
- package/src/modules/green-invoice/helpers/green-invoice.helper.ts +1 -1
- package/src/modules-app.ts +2 -0
- package/src/shared/types/index.ts +1 -1
|
@@ -0,0 +1,556 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { createMockTransaction, createMockDocument } from './test-helpers.js';
|
|
3
|
+
|
|
4
|
+
// Mock the module imports to avoid dependency issues
|
|
5
|
+
vi.mock('graphql-modules', () => ({
|
|
6
|
+
Injectable: () => (target: any) => target,
|
|
7
|
+
Inject: () => (target: any, propertyKey: string | symbol, parameterIndex: number) => {},
|
|
8
|
+
Injector: class {},
|
|
9
|
+
Scope: { Operation: 'Operation' },
|
|
10
|
+
CONTEXT: Symbol('CONTEXT'),
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
vi.mock('@modules/charges/providers/charges.provider.js', () => ({
|
|
14
|
+
ChargesProvider: class {},
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
vi.mock('@modules/documents/providers/documents.provider.js', () => ({
|
|
18
|
+
DocumentsProvider: class {},
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
vi.mock('@modules/transactions/providers/transactions.provider.js', () => ({
|
|
22
|
+
TransactionsProvider: class {},
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
vi.mock('@shared/helpers', () => ({
|
|
26
|
+
dateToTimelessDateString: (date: Date) => date.toISOString().split('T')[0],
|
|
27
|
+
}));
|
|
28
|
+
|
|
29
|
+
// Import after mocking
|
|
30
|
+
const { ChargesMatcherProvider } = await import('../providers/charges-matcher.provider.js');
|
|
31
|
+
|
|
32
|
+
type Injector = {
|
|
33
|
+
get: (token: any) => any;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// Test constants
|
|
37
|
+
const ADMIN_BUSINESS_ID = 'user-123';
|
|
38
|
+
|
|
39
|
+
// Mock charge object
|
|
40
|
+
function createCharge(id: string, ownerId: string) {
|
|
41
|
+
return {
|
|
42
|
+
id,
|
|
43
|
+
owner_id: ownerId,
|
|
44
|
+
transactions_min_event_date: new Date('2024-01-15'),
|
|
45
|
+
transactions_max_event_date: new Date('2024-01-15'),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
describe('ChargesMatcherProvider - Integration Tests', () => {
|
|
50
|
+
describe('findMatchesForCharge', () => {
|
|
51
|
+
it('should find matches for transaction charge with mock data', async () => {
|
|
52
|
+
// Setup mock data
|
|
53
|
+
const sourceChargeId = 'tx-charge-1';
|
|
54
|
+
const sourceTransactions = [
|
|
55
|
+
createMockTransaction({
|
|
56
|
+
charge_id: sourceChargeId,
|
|
57
|
+
amount: "100",
|
|
58
|
+
currency: 'USD',
|
|
59
|
+
event_date: new Date('2024-01-15'),
|
|
60
|
+
}),
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
const candidateCharge1Id = 'doc-charge-1';
|
|
64
|
+
const candidateDocuments1 = [
|
|
65
|
+
createMockDocument({
|
|
66
|
+
charge_id: candidateCharge1Id,
|
|
67
|
+
total_amount: 100,
|
|
68
|
+
currency_code: 'USD',
|
|
69
|
+
date: new Date('2024-01-15'),
|
|
70
|
+
}),
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
const candidateCharge2Id = 'doc-charge-2';
|
|
74
|
+
const candidateDocuments2 = [
|
|
75
|
+
createMockDocument({
|
|
76
|
+
charge_id: candidateCharge2Id,
|
|
77
|
+
total_amount: 110,
|
|
78
|
+
currency_code: 'USD',
|
|
79
|
+
date: new Date('2024-01-16'),
|
|
80
|
+
}),
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
// Mock providers
|
|
84
|
+
const mockChargesProvider = {
|
|
85
|
+
getChargeByIdLoader: {
|
|
86
|
+
load: vi.fn((id: string) => {
|
|
87
|
+
if (id === sourceChargeId) return Promise.resolve(createCharge(id, ADMIN_BUSINESS_ID));
|
|
88
|
+
if (id === candidateCharge1Id) return Promise.resolve(createCharge(id, ADMIN_BUSINESS_ID));
|
|
89
|
+
if (id === candidateCharge2Id) return Promise.resolve(createCharge(id, ADMIN_BUSINESS_ID));
|
|
90
|
+
return Promise.resolve(new Error('Charge not found'));
|
|
91
|
+
}),
|
|
92
|
+
},
|
|
93
|
+
getChargesByFilters: vi.fn(() =>
|
|
94
|
+
Promise.resolve([
|
|
95
|
+
createCharge(candidateCharge1Id, ADMIN_BUSINESS_ID),
|
|
96
|
+
createCharge(candidateCharge2Id, ADMIN_BUSINESS_ID),
|
|
97
|
+
]),
|
|
98
|
+
),
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const mockTransactionsProvider = {
|
|
102
|
+
transactionsByChargeIDLoader: {
|
|
103
|
+
load: vi.fn((id: string) => {
|
|
104
|
+
if (id === sourceChargeId) return Promise.resolve(sourceTransactions);
|
|
105
|
+
return Promise.resolve([]);
|
|
106
|
+
}),
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const mockDocumentsProvider = {
|
|
111
|
+
getDocumentsByChargeIdLoader: {
|
|
112
|
+
load: vi.fn((id: string) => {
|
|
113
|
+
if (id === sourceChargeId) return Promise.resolve([]);
|
|
114
|
+
if (id === candidateCharge1Id) return Promise.resolve(candidateDocuments1);
|
|
115
|
+
if (id === candidateCharge2Id) return Promise.resolve(candidateDocuments2);
|
|
116
|
+
return Promise.resolve([]);
|
|
117
|
+
}),
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const mockInjector = {
|
|
122
|
+
get: vi.fn((token: any) => {
|
|
123
|
+
if (token.name === 'ChargesProvider') return mockChargesProvider;
|
|
124
|
+
if (token.name === 'TransactionsProvider') return mockTransactionsProvider;
|
|
125
|
+
if (token.name === 'DocumentsProvider') return mockDocumentsProvider;
|
|
126
|
+
return null;
|
|
127
|
+
}),
|
|
128
|
+
} as unknown as Injector;
|
|
129
|
+
|
|
130
|
+
// Execute
|
|
131
|
+
const provider = new ChargesMatcherProvider();
|
|
132
|
+
const result = await provider.findMatchesForCharge(sourceChargeId, { adminContext: { defaultAdminBusinessId: ADMIN_BUSINESS_ID }, injector: mockInjector } as any);
|
|
133
|
+
|
|
134
|
+
// Verify
|
|
135
|
+
expect(result.matches).toHaveLength(2);
|
|
136
|
+
expect(result.matches[0].chargeId).toBe(candidateCharge1Id); // Perfect match
|
|
137
|
+
expect(result.matches[0].confidenceScore).toBeGreaterThan(0.95);
|
|
138
|
+
expect(result.matches[1].chargeId).toBe(candidateCharge2Id); // Partial match
|
|
139
|
+
expect(result.matches[1].confidenceScore).toBeLessThan(0.95);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('should find matches for document charge with mock data', async () => {
|
|
143
|
+
// Setup mock data
|
|
144
|
+
const sourceChargeId = 'doc-charge-1';
|
|
145
|
+
const sourceDocuments = [
|
|
146
|
+
createMockDocument({
|
|
147
|
+
charge_id: sourceChargeId,
|
|
148
|
+
total_amount: 200,
|
|
149
|
+
currency_code: 'USD',
|
|
150
|
+
date: new Date('2024-02-15'),
|
|
151
|
+
}),
|
|
152
|
+
];
|
|
153
|
+
|
|
154
|
+
const candidateCharge1Id = 'tx-charge-1';
|
|
155
|
+
const candidateTransactions1 = [
|
|
156
|
+
createMockTransaction({
|
|
157
|
+
charge_id: candidateCharge1Id,
|
|
158
|
+
amount: "200",
|
|
159
|
+
currency: 'USD',
|
|
160
|
+
event_date: new Date('2024-02-15'),
|
|
161
|
+
}),
|
|
162
|
+
];
|
|
163
|
+
|
|
164
|
+
const candidateCharge2Id = 'tx-charge-2';
|
|
165
|
+
const candidateTransactions2 = [
|
|
166
|
+
createMockTransaction({
|
|
167
|
+
charge_id: candidateCharge2Id,
|
|
168
|
+
amount: "195",
|
|
169
|
+
currency: 'USD',
|
|
170
|
+
event_date: new Date('2024-02-16'),
|
|
171
|
+
}),
|
|
172
|
+
];
|
|
173
|
+
|
|
174
|
+
// Mock providers
|
|
175
|
+
const mockChargesProvider = {
|
|
176
|
+
getChargeByIdLoader: {
|
|
177
|
+
load: vi.fn((id: string) => {
|
|
178
|
+
if (id === sourceChargeId) return Promise.resolve(createCharge(id, ADMIN_BUSINESS_ID));
|
|
179
|
+
return Promise.resolve(createCharge(id, ADMIN_BUSINESS_ID));
|
|
180
|
+
}),
|
|
181
|
+
},
|
|
182
|
+
getChargesByFilters: vi.fn(() =>
|
|
183
|
+
Promise.resolve([
|
|
184
|
+
createCharge(candidateCharge1Id, ADMIN_BUSINESS_ID),
|
|
185
|
+
createCharge(candidateCharge2Id, ADMIN_BUSINESS_ID),
|
|
186
|
+
]),
|
|
187
|
+
),
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const mockTransactionsProvider = {
|
|
191
|
+
transactionsByChargeIDLoader: {
|
|
192
|
+
load: vi.fn((id: string) => {
|
|
193
|
+
if (id === candidateCharge1Id) return Promise.resolve(candidateTransactions1);
|
|
194
|
+
if (id === candidateCharge2Id) return Promise.resolve(candidateTransactions2);
|
|
195
|
+
return Promise.resolve([]);
|
|
196
|
+
}),
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const mockDocumentsProvider = {
|
|
201
|
+
getDocumentsByChargeIdLoader: {
|
|
202
|
+
load: vi.fn((id: string) => {
|
|
203
|
+
if (id === sourceChargeId) return Promise.resolve(sourceDocuments);
|
|
204
|
+
return Promise.resolve([]);
|
|
205
|
+
}),
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
const mockInjector = {
|
|
210
|
+
get: vi.fn((token: any) => {
|
|
211
|
+
if (token.name === 'ChargesProvider') return mockChargesProvider;
|
|
212
|
+
if (token.name === 'TransactionsProvider') return mockTransactionsProvider;
|
|
213
|
+
if (token.name === 'DocumentsProvider') return mockDocumentsProvider;
|
|
214
|
+
return null;
|
|
215
|
+
}),
|
|
216
|
+
} as unknown as Injector;
|
|
217
|
+
|
|
218
|
+
// Execute
|
|
219
|
+
const provider = new ChargesMatcherProvider();
|
|
220
|
+
const result = await provider.findMatchesForCharge(sourceChargeId, { adminContext: { defaultAdminBusinessId: ADMIN_BUSINESS_ID }, injector: mockInjector } as any);
|
|
221
|
+
|
|
222
|
+
// Verify
|
|
223
|
+
expect(result.matches).toHaveLength(2);
|
|
224
|
+
expect(result.matches[0].chargeId).toBe(candidateCharge1Id);
|
|
225
|
+
expect(result.matches[0].confidenceScore).toBeGreaterThan(0.95);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it('should throw error if charge not found', async () => {
|
|
229
|
+
const mockChargesProvider = {
|
|
230
|
+
getChargeByIdLoader: {
|
|
231
|
+
load: vi.fn(() => Promise.resolve(new Error('Not found'))),
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const mockInjector = {
|
|
236
|
+
get: vi.fn((token: any) => {
|
|
237
|
+
if (token.name === 'ChargesProvider') return mockChargesProvider;
|
|
238
|
+
return null;
|
|
239
|
+
}),
|
|
240
|
+
} as unknown as Injector;
|
|
241
|
+
|
|
242
|
+
const provider = new ChargesMatcherProvider();
|
|
243
|
+
|
|
244
|
+
await expect(
|
|
245
|
+
provider.findMatchesForCharge('non-existent', { adminContext: { defaultAdminBusinessId: ADMIN_BUSINESS_ID }, injector: mockInjector } as any),
|
|
246
|
+
).rejects.toThrow(/Source charge not found/);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it('should throw error if charge is already matched', async () => {
|
|
250
|
+
const chargeId = 'matched-charge';
|
|
251
|
+
|
|
252
|
+
const mockChargesProvider = {
|
|
253
|
+
getChargeByIdLoader: {
|
|
254
|
+
load: vi.fn(() => Promise.resolve(createCharge(chargeId, ADMIN_BUSINESS_ID))),
|
|
255
|
+
},
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
const mockTransactionsProvider = {
|
|
259
|
+
transactionsByChargeIDLoader: {
|
|
260
|
+
load: vi.fn(() => Promise.resolve([createMockTransaction()])),
|
|
261
|
+
},
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
const mockDocumentsProvider = {
|
|
265
|
+
getDocumentsByChargeIdLoader: {
|
|
266
|
+
load: vi.fn(() => Promise.resolve([createMockDocument()])),
|
|
267
|
+
},
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
const mockInjector = {
|
|
271
|
+
get: vi.fn((token: any) => {
|
|
272
|
+
if (token.name === 'ChargesProvider') return mockChargesProvider;
|
|
273
|
+
if (token.name === 'TransactionsProvider') return mockTransactionsProvider;
|
|
274
|
+
if (token.name === 'DocumentsProvider') return mockDocumentsProvider;
|
|
275
|
+
return null;
|
|
276
|
+
}),
|
|
277
|
+
} as unknown as Injector;
|
|
278
|
+
|
|
279
|
+
const provider = new ChargesMatcherProvider();
|
|
280
|
+
|
|
281
|
+
await expect(provider.findMatchesForCharge(chargeId, { adminContext: { defaultAdminBusinessId: ADMIN_BUSINESS_ID }, injector: mockInjector } as any)).rejects.toThrow(
|
|
282
|
+
/already matched/,
|
|
283
|
+
);
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
it('should throw error if charge has no transactions or documents', async () => {
|
|
287
|
+
const chargeId = 'empty-charge';
|
|
288
|
+
|
|
289
|
+
const mockChargesProvider = {
|
|
290
|
+
getChargeByIdLoader: {
|
|
291
|
+
load: vi.fn(() => Promise.resolve(createCharge(chargeId, ADMIN_BUSINESS_ID))),
|
|
292
|
+
},
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
const mockTransactionsProvider = {
|
|
296
|
+
transactionsByChargeIDLoader: {
|
|
297
|
+
load: vi.fn(() => Promise.resolve([])),
|
|
298
|
+
},
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
const mockDocumentsProvider = {
|
|
302
|
+
getDocumentsByChargeIdLoader: {
|
|
303
|
+
load: vi.fn(() => Promise.resolve([])),
|
|
304
|
+
},
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
const mockInjector = {
|
|
308
|
+
get: vi.fn((token: any) => {
|
|
309
|
+
if (token.name === 'ChargesProvider') return mockChargesProvider;
|
|
310
|
+
if (token.name === 'TransactionsProvider') return mockTransactionsProvider;
|
|
311
|
+
if (token.name === 'DocumentsProvider') return mockDocumentsProvider;
|
|
312
|
+
return null;
|
|
313
|
+
}),
|
|
314
|
+
} as unknown as Injector;
|
|
315
|
+
|
|
316
|
+
const provider = new ChargesMatcherProvider();
|
|
317
|
+
|
|
318
|
+
await expect(provider.findMatchesForCharge(chargeId, { adminContext: { defaultAdminBusinessId: ADMIN_BUSINESS_ID }, injector: mockInjector } as any)).rejects.toThrow(
|
|
319
|
+
/no transactions or documents/,
|
|
320
|
+
);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
it('should return empty matches if no candidates found', async () => {
|
|
324
|
+
const sourceChargeId = 'tx-charge-only';
|
|
325
|
+
|
|
326
|
+
const mockChargesProvider = {
|
|
327
|
+
getChargeByIdLoader: {
|
|
328
|
+
load: vi.fn(() => Promise.resolve(createCharge(sourceChargeId, ADMIN_BUSINESS_ID))),
|
|
329
|
+
},
|
|
330
|
+
getChargesByFilters: vi.fn(() => Promise.resolve([])),
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
const mockTransactionsProvider = {
|
|
334
|
+
transactionsByChargeIDLoader: {
|
|
335
|
+
load: vi.fn(() => Promise.resolve([createMockTransaction()])),
|
|
336
|
+
},
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
const mockDocumentsProvider = {
|
|
340
|
+
getDocumentsByChargeIdLoader: {
|
|
341
|
+
load: vi.fn(() => Promise.resolve([])),
|
|
342
|
+
},
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
const mockInjector = {
|
|
346
|
+
get: vi.fn((token: any) => {
|
|
347
|
+
if (token.name === 'ChargesProvider') return mockChargesProvider;
|
|
348
|
+
if (token.name === 'TransactionsProvider') return mockTransactionsProvider;
|
|
349
|
+
if (token.name === 'DocumentsProvider') return mockDocumentsProvider;
|
|
350
|
+
return null;
|
|
351
|
+
}),
|
|
352
|
+
} as unknown as Injector;
|
|
353
|
+
|
|
354
|
+
const provider = new ChargesMatcherProvider();
|
|
355
|
+
const result = await provider.findMatchesForCharge(sourceChargeId, { adminContext: { defaultAdminBusinessId: ADMIN_BUSINESS_ID }, injector: mockInjector } as any);
|
|
356
|
+
|
|
357
|
+
expect(result.matches).toEqual([]);
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
it('should filter out matched candidates (having both transactions and documents)', async () => {
|
|
361
|
+
const sourceChargeId = 'tx-charge-1';
|
|
362
|
+
const matchedCandidateId = 'matched-charge';
|
|
363
|
+
const validCandidateId = 'doc-charge-1';
|
|
364
|
+
|
|
365
|
+
const mockChargesProvider = {
|
|
366
|
+
getChargeByIdLoader: {
|
|
367
|
+
load: vi.fn(() => Promise.resolve(createCharge(sourceChargeId, ADMIN_BUSINESS_ID))),
|
|
368
|
+
},
|
|
369
|
+
getChargesByFilters: vi.fn(() =>
|
|
370
|
+
Promise.resolve([
|
|
371
|
+
createCharge(matchedCandidateId, ADMIN_BUSINESS_ID),
|
|
372
|
+
createCharge(validCandidateId, ADMIN_BUSINESS_ID),
|
|
373
|
+
]),
|
|
374
|
+
),
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
const mockTransactionsProvider = {
|
|
378
|
+
transactionsByChargeIDLoader: {
|
|
379
|
+
load: vi.fn((id: string) => {
|
|
380
|
+
if (id === sourceChargeId) return Promise.resolve([createMockTransaction()]);
|
|
381
|
+
if (id === matchedCandidateId) return Promise.resolve([createMockTransaction()]); // Has transactions
|
|
382
|
+
return Promise.resolve([]);
|
|
383
|
+
}),
|
|
384
|
+
},
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
const mockDocumentsProvider = {
|
|
388
|
+
getDocumentsByChargeIdLoader: {
|
|
389
|
+
load: vi.fn((id: string) => {
|
|
390
|
+
if (id === matchedCandidateId) return Promise.resolve([createMockDocument()]); // Also has documents - MATCHED
|
|
391
|
+
if (id === validCandidateId) return Promise.resolve([createMockDocument()]); // Only documents - VALID
|
|
392
|
+
return Promise.resolve([]);
|
|
393
|
+
}),
|
|
394
|
+
},
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
const mockInjector = {
|
|
398
|
+
get: vi.fn((token: any) => {
|
|
399
|
+
if (token.name === 'ChargesProvider') return mockChargesProvider;
|
|
400
|
+
if (token.name === 'TransactionsProvider') return mockTransactionsProvider;
|
|
401
|
+
if (token.name === 'DocumentsProvider') return mockDocumentsProvider;
|
|
402
|
+
return null;
|
|
403
|
+
}),
|
|
404
|
+
} as unknown as Injector;
|
|
405
|
+
|
|
406
|
+
const provider = new ChargesMatcherProvider();
|
|
407
|
+
const result = await provider.findMatchesForCharge(sourceChargeId, { adminContext: { defaultAdminBusinessId: ADMIN_BUSINESS_ID }, injector: mockInjector } as any);
|
|
408
|
+
|
|
409
|
+
// Should only include validCandidateId, not matchedCandidateId
|
|
410
|
+
expect(result.matches).toHaveLength(1);
|
|
411
|
+
expect(result.matches[0].chargeId).toBe(validCandidateId);
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
it('should handle date window filtering correctly', async () => {
|
|
415
|
+
const sourceChargeId = 'tx-charge-1';
|
|
416
|
+
const sourceDate = new Date('2024-06-15');
|
|
417
|
+
|
|
418
|
+
const withinWindowId = 'doc-charge-within';
|
|
419
|
+
const outsideWindowId = 'doc-charge-outside';
|
|
420
|
+
|
|
421
|
+
const mockChargesProvider = {
|
|
422
|
+
getChargeByIdLoader: {
|
|
423
|
+
load: vi.fn(() => Promise.resolve(createCharge(sourceChargeId, ADMIN_BUSINESS_ID))),
|
|
424
|
+
},
|
|
425
|
+
getChargesByFilters: vi.fn(params => {
|
|
426
|
+
// Verify date window parameters
|
|
427
|
+
expect(params.fromAnyDate).toBeDefined();
|
|
428
|
+
expect(params.toAnyDate).toBeDefined();
|
|
429
|
+
return Promise.resolve([
|
|
430
|
+
createCharge(withinWindowId, ADMIN_BUSINESS_ID),
|
|
431
|
+
createCharge(outsideWindowId, ADMIN_BUSINESS_ID),
|
|
432
|
+
]);
|
|
433
|
+
}),
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
const mockTransactionsProvider = {
|
|
437
|
+
transactionsByChargeIDLoader: {
|
|
438
|
+
load: vi.fn((id: string) => {
|
|
439
|
+
if (id === sourceChargeId) {
|
|
440
|
+
return Promise.resolve([createMockTransaction({ event_date: sourceDate })]);
|
|
441
|
+
}
|
|
442
|
+
return Promise.resolve([]);
|
|
443
|
+
}),
|
|
444
|
+
},
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
const mockDocumentsProvider = {
|
|
448
|
+
getDocumentsByChargeIdLoader: {
|
|
449
|
+
load: vi.fn((id: string) => {
|
|
450
|
+
if (id === sourceChargeId) return Promise.resolve([]);
|
|
451
|
+
if (id === withinWindowId) {
|
|
452
|
+
// Within 12-month window
|
|
453
|
+
return Promise.resolve([
|
|
454
|
+
createMockDocument({ date: new Date('2024-06-20') }),
|
|
455
|
+
]);
|
|
456
|
+
}
|
|
457
|
+
if (id === outsideWindowId) {
|
|
458
|
+
// Outside 12-month window
|
|
459
|
+
return Promise.resolve([
|
|
460
|
+
createMockDocument({ date: new Date('2025-07-15') }),
|
|
461
|
+
]);
|
|
462
|
+
}
|
|
463
|
+
return Promise.resolve([]);
|
|
464
|
+
}),
|
|
465
|
+
},
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
const mockInjector = {
|
|
469
|
+
get: vi.fn((token: any) => {
|
|
470
|
+
if (token.name === 'ChargesProvider') return mockChargesProvider;
|
|
471
|
+
if (token.name === 'TransactionsProvider') return mockTransactionsProvider;
|
|
472
|
+
if (token.name === 'DocumentsProvider') return mockDocumentsProvider;
|
|
473
|
+
return null;
|
|
474
|
+
}),
|
|
475
|
+
} as unknown as Injector;
|
|
476
|
+
|
|
477
|
+
const provider = new ChargesMatcherProvider();
|
|
478
|
+
const result = await provider.findMatchesForCharge(sourceChargeId, { adminContext: { defaultAdminBusinessId: ADMIN_BUSINESS_ID }, injector: mockInjector } as any);
|
|
479
|
+
|
|
480
|
+
// Should only include candidate within window
|
|
481
|
+
expect(result.matches).toHaveLength(1);
|
|
482
|
+
expect(result.matches[0].chargeId).toBe(withinWindowId);
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
it('should throw error if user ID not in context', async () => {
|
|
486
|
+
const mockInjector = {
|
|
487
|
+
get: vi.fn(() => null), // No user
|
|
488
|
+
} as unknown as Injector;
|
|
489
|
+
|
|
490
|
+
const provider = new ChargesMatcherProvider();
|
|
491
|
+
|
|
492
|
+
await expect(provider.findMatchesForCharge('any-id', { adminContext: { defaultAdminBusinessId: null }, injector: mockInjector } as any)).rejects.toThrow(
|
|
493
|
+
/Admin business not found in context/,
|
|
494
|
+
);
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
it('should return top 5 matches when more candidates exist', async () => {
|
|
498
|
+
const sourceChargeId = 'tx-charge-1';
|
|
499
|
+
|
|
500
|
+
// Create 7 candidate charges
|
|
501
|
+
const candidateIds = Array.from({ length: 7 }, (_, i) => `doc-charge-${i + 1}`);
|
|
502
|
+
|
|
503
|
+
const mockChargesProvider = {
|
|
504
|
+
getChargeByIdLoader: {
|
|
505
|
+
load: vi.fn(() => Promise.resolve(createCharge(sourceChargeId, ADMIN_BUSINESS_ID))),
|
|
506
|
+
},
|
|
507
|
+
getChargesByFilters: vi.fn(() =>
|
|
508
|
+
Promise.resolve(candidateIds.map(id => createCharge(id, ADMIN_BUSINESS_ID))),
|
|
509
|
+
),
|
|
510
|
+
};
|
|
511
|
+
|
|
512
|
+
const mockTransactionsProvider = {
|
|
513
|
+
transactionsByChargeIDLoader: {
|
|
514
|
+
load: vi.fn((id: string) => {
|
|
515
|
+
if (id === sourceChargeId) {
|
|
516
|
+
return Promise.resolve([createMockTransaction({ amount: "100" })]);
|
|
517
|
+
}
|
|
518
|
+
return Promise.resolve([]);
|
|
519
|
+
}),
|
|
520
|
+
},
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
const mockDocumentsProvider = {
|
|
524
|
+
getDocumentsByChargeIdLoader: {
|
|
525
|
+
load: vi.fn((id: string) => {
|
|
526
|
+
if (id === sourceChargeId) return Promise.resolve([]);
|
|
527
|
+
// Create documents with varying amounts for different confidence scores
|
|
528
|
+
const index = parseInt(id.split('-')[2]);
|
|
529
|
+
return Promise.resolve([createMockDocument({ total_amount: 100 + index })]);
|
|
530
|
+
}),
|
|
531
|
+
},
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
const mockInjector = {
|
|
535
|
+
get: vi.fn((token: any) => {
|
|
536
|
+
if (token.name === 'ChargesProvider') return mockChargesProvider;
|
|
537
|
+
if (token.name === 'TransactionsProvider') return mockTransactionsProvider;
|
|
538
|
+
if (token.name === 'DocumentsProvider') return mockDocumentsProvider;
|
|
539
|
+
return null;
|
|
540
|
+
}),
|
|
541
|
+
} as unknown as Injector;
|
|
542
|
+
|
|
543
|
+
const provider = new ChargesMatcherProvider();
|
|
544
|
+
const result = await provider.findMatchesForCharge(sourceChargeId, { adminContext: { defaultAdminBusinessId: ADMIN_BUSINESS_ID }, injector: mockInjector } as any);
|
|
545
|
+
|
|
546
|
+
// Should return maximum of 5 matches
|
|
547
|
+
expect(result.matches).toHaveLength(5);
|
|
548
|
+
// Verify sorted by confidence (descending)
|
|
549
|
+
for (let i = 0; i < result.matches.length - 1; i++) {
|
|
550
|
+
expect(result.matches[i].confidenceScore).toBeGreaterThanOrEqual(
|
|
551
|
+
result.matches[i + 1].confidenceScore,
|
|
552
|
+
);
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
});
|
|
556
|
+
});
|