@accounter/server 0.0.8-alpha-20251104081410-ad15de052a742b1353c38e22ebbcae3e9e0b4d90 → 0.0.8-alpha-20251104115901-2a484be84dde31c9a4be251038269ba4823b6dc9
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 +27 -5
- package/dist/green-invoice-graphql/src/mesh-artifacts/index.d.ts +5 -5
- package/dist/green-invoice-graphql/src/mesh-artifacts/sources/GreenInvoice/schemaWithAnnotations.js +3 -3
- package/dist/green-invoice-graphql/src/mesh-artifacts/sources/GreenInvoice/schemaWithAnnotations.js.map +1 -1
- package/dist/green-invoice-graphql/src/mesh-artifacts/sources/GreenInvoice/types.d.ts +3 -3
- package/dist/server/src/__generated__/types.d.ts +11 -4
- package/dist/server/src/__generated__/types.js.map +1 -1
- package/dist/server/src/modules/app-providers/green-invoice-client.d.ts +1 -1
- package/dist/server/src/modules/charges-matcher/__generated__/types.d.ts +3 -1
- package/dist/server/src/modules/charges-matcher/__generated__/types.js.map +1 -1
- package/dist/server/src/modules/charges-matcher/__tests__/auto-match-integration.test.js +0 -1
- package/dist/server/src/modules/charges-matcher/__tests__/auto-match-integration.test.js.map +1 -1
- package/dist/server/src/modules/charges-matcher/__tests__/test-helpers.d.ts +2 -2
- package/dist/server/src/modules/charges-matcher/__tests__/test-helpers.js.map +1 -1
- package/dist/server/src/modules/charges-matcher/providers/auto-match.provider.js +10 -1
- package/dist/server/src/modules/charges-matcher/providers/auto-match.provider.js.map +1 -1
- package/dist/server/src/modules/charges-matcher/providers/charges-matcher.provider.js +22 -10
- package/dist/server/src/modules/charges-matcher/providers/charges-matcher.provider.js.map +1 -1
- package/dist/server/src/modules/charges-matcher/resolvers/index.js +4 -0
- package/dist/server/src/modules/charges-matcher/resolvers/index.js.map +1 -1
- package/dist/server/src/modules/charges-matcher/typeDefs/charges-matcher.graphql.js +1 -0
- package/dist/server/src/modules/charges-matcher/typeDefs/charges-matcher.graphql.js.map +1 -1
- package/dist/server/src/modules/charges-matcher/types.d.ts +5 -3
- package/dist/server/src/modules/charges-matcher/types.js +1 -1
- package/dist/server/src/modules/charges-matcher/types.js.map +1 -1
- package/package.json +1 -1
- package/src/__generated__/types.ts +7 -4
- package/src/modules/charges-matcher/__generated__/types.ts +3 -1
- package/src/modules/charges-matcher/__tests__/auto-match-integration.test.ts +0 -1
- package/src/modules/charges-matcher/__tests__/test-helpers.ts +3 -3
- package/src/modules/charges-matcher/providers/auto-match.provider.ts +11 -1
- package/src/modules/charges-matcher/providers/charges-matcher.provider.ts +63 -46
- package/src/modules/charges-matcher/resolvers/index.ts +5 -0
- package/src/modules/charges-matcher/typeDefs/charges-matcher.graphql.ts +1 -0
- package/src/modules/charges-matcher/types.ts +6 -4
|
@@ -5,7 +5,7 @@ export namespace ChargesMatcherModule {
|
|
|
5
5
|
Query: 'findChargeMatches';
|
|
6
6
|
Mutation: 'autoMatchCharges';
|
|
7
7
|
ChargeMatchesResult: 'matches';
|
|
8
|
-
ChargeMatch: 'chargeId' | 'confidenceScore';
|
|
8
|
+
ChargeMatch: 'chargeId' | 'charge' | 'confidenceScore';
|
|
9
9
|
AutoMatchChargesResult: 'totalMatches' | 'mergedCharges' | 'skippedCharges' | 'errors';
|
|
10
10
|
MergedCharge: 'chargeId' | 'confidenceScore';
|
|
11
11
|
};
|
|
@@ -16,6 +16,7 @@ export namespace ChargesMatcherModule {
|
|
|
16
16
|
export type Mutation = Pick<Types.Mutation, DefinedFields['Mutation']>;
|
|
17
17
|
export type AutoMatchChargesResult = Pick<Types.AutoMatchChargesResult, DefinedFields['AutoMatchChargesResult']>;
|
|
18
18
|
export type ChargeMatch = Pick<Types.ChargeMatch, DefinedFields['ChargeMatch']>;
|
|
19
|
+
export type Charge = Types.Charge;
|
|
19
20
|
export type MergedCharge = Pick<Types.MergedCharge, DefinedFields['MergedCharge']>;
|
|
20
21
|
|
|
21
22
|
export type QueryResolvers = Pick<Types.QueryResolvers, DefinedFields['Query']>;
|
|
@@ -53,6 +54,7 @@ export namespace ChargesMatcherModule {
|
|
|
53
54
|
ChargeMatch?: {
|
|
54
55
|
'*'?: gm.Middleware[];
|
|
55
56
|
chargeId?: gm.Middleware[];
|
|
57
|
+
charge?: gm.Middleware[];
|
|
56
58
|
confidenceScore?: gm.Middleware[];
|
|
57
59
|
};
|
|
58
60
|
AutoMatchChargesResult?: {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
Transaction,
|
|
3
3
|
Document,
|
|
4
|
-
|
|
4
|
+
ChargeMatchProto,
|
|
5
5
|
AggregatedTransaction,
|
|
6
6
|
AggregatedDocument,
|
|
7
7
|
ConfidenceScores,
|
|
@@ -125,8 +125,8 @@ export function createMockConfidenceScores(
|
|
|
125
125
|
/**
|
|
126
126
|
* Factory for creating mock charge matches
|
|
127
127
|
*/
|
|
128
|
-
export function createMockChargeMatch(overrides: Partial<
|
|
129
|
-
const defaultMatch:
|
|
128
|
+
export function createMockChargeMatch(overrides: Partial<ChargeMatchProto> = {}): ChargeMatchProto {
|
|
129
|
+
const defaultMatch: ChargeMatchProto = {
|
|
130
130
|
chargeId: '00000000-0000-0000-0000-000000000099',
|
|
131
131
|
confidenceScore: 0.95,
|
|
132
132
|
};
|
|
@@ -146,8 +146,10 @@ export function determineMergeDirection(
|
|
|
146
146
|
charge1: ChargeWithData,
|
|
147
147
|
charge2: ChargeWithData,
|
|
148
148
|
): [ChargeWithData, ChargeWithData] {
|
|
149
|
+
const charge1HasDescription = charge1.description && charge1.description.length > 0;
|
|
149
150
|
const charge1HasTransactions = charge1.transactions && charge1.transactions.length > 0;
|
|
150
151
|
const charge1HasDocuments = charge1.documents && charge1.documents.length > 0;
|
|
152
|
+
const charge2HasDescription = charge2.description && charge2.description.length > 0;
|
|
151
153
|
const charge2HasTransactions = charge2.transactions && charge2.transactions.length > 0;
|
|
152
154
|
const charge2HasDocuments = charge2.documents && charge2.documents.length > 0;
|
|
153
155
|
|
|
@@ -162,7 +164,15 @@ export function determineMergeDirection(
|
|
|
162
164
|
return [charge1, charge2]; // Merge charge1 INTO charge2 (keep charge2)
|
|
163
165
|
}
|
|
164
166
|
|
|
165
|
-
// Rule 2: Both unmatched - keep the one with
|
|
167
|
+
// Rule 2: Both unmatched - keep the one with description
|
|
168
|
+
if (charge1HasDescription && !charge2HasDescription) {
|
|
169
|
+
return [charge2, charge1]; // Merge charge2 INTO charge1 (keep description charge)
|
|
170
|
+
}
|
|
171
|
+
if (charge2HasDescription && !charge1HasDescription) {
|
|
172
|
+
return [charge1, charge2]; // Merge charge1 INTO charge2 (keep description charge)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Rule 3: Neither has description or both have description - keep the one with transactions
|
|
166
176
|
if (charge1HasTransactions && !charge2HasTransactions) {
|
|
167
177
|
return [charge2, charge1]; // Merge charge2 INTO charge1 (keep transaction charge)
|
|
168
178
|
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* Integrates with existing modules: charges, transactions, and documents.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { subYears } from 'date-fns';
|
|
8
9
|
import { Injectable, Scope } from 'graphql-modules';
|
|
9
10
|
import { mergeChargesExecutor } from '@modules/charges/helpers/merge-charges.hepler.js';
|
|
10
11
|
import { ChargesProvider } from '@modules/charges/providers/charges.provider.js';
|
|
@@ -116,36 +117,42 @@ export class ChargesMatcherProvider {
|
|
|
116
117
|
// Step 6: Load transactions and documents for all candidate charges
|
|
117
118
|
const candidateChargesWithData: Array<TransactionCharge | DocumentCharge> = [];
|
|
118
119
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
120
|
+
await Promise.all(
|
|
121
|
+
candidateCharges.map(async candidate => {
|
|
122
|
+
// Skip the source charge itself
|
|
123
|
+
if (candidate.id === chargeId) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
124
126
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
127
|
+
const candidateTransactionsPromise = transactionsProvider.transactionsByChargeIDLoader.load(
|
|
128
|
+
candidate.id,
|
|
129
|
+
) as Promise<Transaction[]>;
|
|
130
|
+
const candidateDocumentsPromise = documentsProvider.getDocumentsByChargeIdLoader.load(
|
|
131
|
+
candidate.id,
|
|
132
|
+
) as Promise<Document[]>;
|
|
133
|
+
const [candidateTransactions, candidateDocuments] = await Promise.all([
|
|
134
|
+
candidateTransactionsPromise,
|
|
135
|
+
candidateDocumentsPromise,
|
|
136
|
+
]);
|
|
137
|
+
|
|
138
|
+
const hasTxs = candidateTransactions && candidateTransactions.length > 0;
|
|
139
|
+
const hasDocs = candidateDocuments && candidateDocuments.length > 0;
|
|
140
|
+
|
|
141
|
+
// Only include unmatched charges (not both types)
|
|
142
|
+
if (hasTxs && !hasDocs) {
|
|
143
|
+
candidateChargesWithData.push({
|
|
144
|
+
chargeId: candidate.id,
|
|
145
|
+
transactions: candidateTransactions,
|
|
146
|
+
});
|
|
147
|
+
} else if (hasDocs && !hasTxs) {
|
|
148
|
+
candidateChargesWithData.push({
|
|
149
|
+
chargeId: candidate.id,
|
|
150
|
+
documents: candidateDocuments,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
// Skip matched charges (have both) and empty charges (have neither)
|
|
154
|
+
}),
|
|
155
|
+
);
|
|
149
156
|
|
|
150
157
|
// Step 7: Build source charge object for findMatches
|
|
151
158
|
let sourceChargeData: TransactionCharge | DocumentCharge;
|
|
@@ -208,30 +215,40 @@ export class ChargesMatcherProvider {
|
|
|
208
215
|
const documentsProvider = injector.get(DocumentsProvider);
|
|
209
216
|
|
|
210
217
|
// Step 1: Load all charges for this user
|
|
218
|
+
const prevYear = dateToTimelessDateString(subYears(new Date(), 1));
|
|
211
219
|
const allCharges = await chargesProvider.getChargesByFilters({
|
|
212
220
|
ownerIds: [adminBusinessId],
|
|
221
|
+
fromAnyDate: prevYear,
|
|
213
222
|
});
|
|
214
223
|
|
|
215
224
|
// Step 2: Load transactions and documents for all charges
|
|
216
225
|
const chargesWithData: ChargeWithData[] = [];
|
|
217
226
|
const mergedChargeIds = new Set<string>(); // Track merged charges to exclude from processing
|
|
218
227
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
228
|
+
await Promise.all(
|
|
229
|
+
allCharges.map(async charge => {
|
|
230
|
+
const transactionsPromise = transactionsProvider.transactionsByChargeIDLoader.load(
|
|
231
|
+
charge.id,
|
|
232
|
+
) as Promise<Transaction[]>;
|
|
233
|
+
const documentsPromise = documentsProvider.getDocumentsByChargeIdLoader.load(
|
|
234
|
+
charge.id,
|
|
235
|
+
) as Promise<Document[]>;
|
|
236
|
+
|
|
237
|
+
const [transactions, documents] = await Promise.all([
|
|
238
|
+
transactionsPromise,
|
|
239
|
+
documentsPromise,
|
|
240
|
+
]);
|
|
241
|
+
|
|
242
|
+
chargesWithData.push({
|
|
243
|
+
chargeId: charge.id,
|
|
244
|
+
ownerId: charge.owner_id ?? adminBusinessId,
|
|
245
|
+
type: ChargeType.TRANSACTION_ONLY, // Will be determined by processChargeForAutoMatch
|
|
246
|
+
description: charge.user_description ?? undefined,
|
|
247
|
+
transactions: transactions || [],
|
|
248
|
+
documents: documents || [],
|
|
249
|
+
});
|
|
250
|
+
}),
|
|
251
|
+
);
|
|
235
252
|
|
|
236
253
|
// Step 3: Filter to get only unmatched charges
|
|
237
254
|
const unmatchedCharges = chargesWithData.filter(charge => {
|
|
@@ -288,7 +305,7 @@ export class ChargesMatcherProvider {
|
|
|
288
305
|
// Track successful merge
|
|
289
306
|
result.totalMatches++;
|
|
290
307
|
result.mergedCharges.push({
|
|
291
|
-
chargeId:
|
|
308
|
+
chargeId: targetToKeep.chargeId,
|
|
292
309
|
confidenceScore: processResult.match.confidenceScore,
|
|
293
310
|
});
|
|
294
311
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ChargesProvider } from '@modules/charges/providers/charges.provider.js';
|
|
1
2
|
import type { ChargesMatcherModule } from '../types.js';
|
|
2
3
|
import { autoMatchChargesResolver } from './auto-match-charges.resolver.js';
|
|
3
4
|
import { findChargeMatchesResolver } from './find-charge-matches.resolver.js';
|
|
@@ -9,4 +10,8 @@ export const chargesMatcherResolvers: ChargesMatcherModule.Resolvers = {
|
|
|
9
10
|
Mutation: {
|
|
10
11
|
...autoMatchChargesResolver.Mutation,
|
|
11
12
|
},
|
|
13
|
+
ChargeMatch: {
|
|
14
|
+
charge: async ({ chargeId }, _args, { injector }) =>
|
|
15
|
+
injector.get(ChargesProvider).getChargeByIdLoader.load(chargeId),
|
|
16
|
+
},
|
|
12
17
|
};
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import type { currency, document_type, IGetAllDocumentsResult } from '@modules/documents/types.js';
|
|
2
2
|
import type { IGetTransactionsByIdsResult } from '@modules/transactions/types.js';
|
|
3
3
|
|
|
4
|
-
export * from './__generated__/types.js';
|
|
5
|
-
|
|
6
4
|
/**
|
|
7
5
|
* Re-export shared types from other modules
|
|
8
6
|
*/
|
|
@@ -26,7 +24,7 @@ export type Document = IGetAllDocumentsResult;
|
|
|
26
24
|
/**
|
|
27
25
|
* Represents a single charge match with its confidence score
|
|
28
26
|
*/
|
|
29
|
-
export interface
|
|
27
|
+
export interface ChargeMatchProto {
|
|
30
28
|
/** UUID of the matched charge */
|
|
31
29
|
chargeId: string;
|
|
32
30
|
/** Confidence score between 0.00 and 1.00 (two decimal precision) */
|
|
@@ -38,7 +36,7 @@ export interface ChargeMatch {
|
|
|
38
36
|
*/
|
|
39
37
|
export interface ChargeMatchesResult {
|
|
40
38
|
/** Array of up to 5 matches, ordered by confidence score (highest first) */
|
|
41
|
-
matches:
|
|
39
|
+
matches: ChargeMatchProto[];
|
|
42
40
|
}
|
|
43
41
|
|
|
44
42
|
/**
|
|
@@ -149,6 +147,8 @@ export interface ChargeWithData {
|
|
|
149
147
|
ownerId: string;
|
|
150
148
|
/** Charge classification */
|
|
151
149
|
type: ChargeType;
|
|
150
|
+
/** Charge description */
|
|
151
|
+
description?: string;
|
|
152
152
|
/** Associated transactions (if any) */
|
|
153
153
|
transactions: Transaction[];
|
|
154
154
|
/** Associated documents (if any) */
|
|
@@ -198,3 +198,5 @@ export interface DocumentCharge {
|
|
|
198
198
|
/** Array of documents in the charge */
|
|
199
199
|
documents: Document[];
|
|
200
200
|
}
|
|
201
|
+
|
|
202
|
+
export * from './__generated__/types.js';
|