Skip to content

Commit 97f0909

Browse files
authored
Fix: Added import id for YNAB (#626)
2 parents 6ca612f + df479a8 commit 97f0909

File tree

2 files changed

+53
-2
lines changed

2 files changed

+53
-2
lines changed

packages/main/src/backend/export/outputVendors/ynab/ynab.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,40 @@ import ClearedEnum = SaveTransaction.ClearedEnum;
77

88
describe('ynab', () => {
99
describe('isSameTransaction', () => {
10+
test('Two transactions with different payee names should be considered the same if they have the same import id', () => {
11+
const differentPayeeTransactionFromYnab: TransactionDetail = {
12+
id: '579ae642-d161-4bbe-9d54-ae3322c93cf7',
13+
date: '2019-06-27',
14+
amount: -1000000,
15+
memo: null,
16+
cleared: SaveTransaction.ClearedEnum.Cleared,
17+
approved: true,
18+
flag_color: null,
19+
account_id: 'SOME_ACCOUNT_ID',
20+
account_name: 'My great account',
21+
payee_id: 'fd7f187c-0633-434f-aaxe-1fevd68492cb',
22+
payee_name: 'שיק',
23+
category_id: null,
24+
category_name: null,
25+
transfer_account_id: null,
26+
transfer_transaction_id: null,
27+
matched_transaction_id: null,
28+
import_id: '2019-06-27-1000000שיק',
29+
deleted: false,
30+
subtransactions: [],
31+
};
32+
const transactionFromFinancialAccount: SaveTransaction = {
33+
account_id: 'SOME_ACCOUNT_ID',
34+
date: '2019-06-27',
35+
amount: -1000000,
36+
payee_name: 'גן',
37+
category_id: '4e0ttc69-b4f6-420b-8d07-986c8225a3d4',
38+
cleared: ClearedEnum.Cleared,
39+
import_id: '2019-06-27-1000000שיק',
40+
};
41+
42+
expect(ynab.isSameTransaction(transactionFromFinancialAccount, differentPayeeTransactionFromYnab)).toBeTruthy();
43+
});
1044
test('Two transactions with different payee names should be considered the same if one of them is a transfer transaction', () => {
1145
const transferTransactionFromYnab: TransactionDetail = {
1246
id: '579ae642-d161-4bbe-9d54-ae3322c93cf7',

packages/main/src/backend/export/outputVendors/ynab/ynab.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import * as ynab from 'ynab';
1616
const YNAB_DATE_FORMAT = 'YYYY-MM-DD';
1717
const NOW = moment();
1818
const MIN_YNAB_ACCESS_TOKEN_LENGTH = 43;
19+
const MAX_YNAB_IMPORT_ID_LENGTH = 36;
1920

2021
const categoriesMap = new Map<string, Pick<ynab.Category, 'id' | 'name' | 'category_group_id'>>();
2122
const transactionsFromYnab = new Map<Date, ynab.TransactionDetail[]>();
@@ -109,7 +110,6 @@ export function getPayeeName(transaction: EnrichedTransaction, payeeNameMaxLengt
109110
function convertTransactionToYnabFormat(originalTransaction: EnrichedTransaction): ynab.SaveTransaction {
110111
const amount = Math.round(originalTransaction.chargedAmount * 1000);
111112
const date = convertTimestampToYnabDateFormat(originalTransaction);
112-
113113
return {
114114
account_id: getYnabAccountIdByAccountNumberFromTransaction(originalTransaction.accountNumber),
115115
date, // "2019-01-17",
@@ -119,12 +119,20 @@ function convertTransactionToYnabFormat(originalTransaction: EnrichedTransaction
119119
category_id: getYnabCategoryIdFromCategoryName(originalTransaction.category),
120120
memo: originalTransaction.memo,
121121
cleared: ynab.SaveTransaction.ClearedEnum.Cleared,
122+
import_id: buildImportId(originalTransaction), // [date][amount][description]
122123
// "approved": true,
123124
// "flag_color": "red",
124125
// "import_id": buildImportId(originalTransaction.description, amount, date) // 'YNAB:[milliunit_amount]:[iso_date]:[occurrence]'
125126
};
126127
}
127128

129+
function buildImportId(transaction: EnrichedTransaction): string {
130+
return `${transaction.date.substring(0, 10)}${transaction.chargedAmount}${transaction.description}`.substring(
131+
0,
132+
MAX_YNAB_IMPORT_ID_LENGTH,
133+
);
134+
}
135+
128136
function getYnabAccountIdByAccountNumberFromTransaction(transactionAccountNumber: string): string {
129137
const ynabAccountId = ynabConfig!.options.accountNumbersToYnabAccountIds[transactionAccountNumber];
130138
if (!ynabAccountId) {
@@ -189,14 +197,16 @@ export function isSameTransaction(
189197
transactionFromYnab: ynab.TransactionDetail,
190198
) {
191199
const isATransferTransaction = !!transactionFromYnab.transfer_account_id;
200+
const isTransactionsImportIdEqual = isSameImportId(transactionToCreate, transactionFromYnab);
192201
return (
193202
transactionToCreate.account_id === transactionFromYnab.account_id &&
194203
transactionToCreate.date === transactionFromYnab.date &&
195204
// @ts-expect-error error TS18049: 'transactionToCreate.amount' is possibly 'null' or 'undefined'
196205
Math.abs(transactionToCreate.amount - transactionFromYnab.amount) < 1000 &&
197206
// In a transfer transaction the payee name changes, but we still consider this the same transaction
198207
(areStringsEqualIgnoreCaseAndWhitespace(transactionToCreate.payee_name, transactionFromYnab.payee_name) ||
199-
isATransferTransaction)
208+
isATransferTransaction ||
209+
isTransactionsImportIdEqual)
200210
);
201211
}
202212

@@ -321,3 +331,10 @@ export const ynabOutputVendor: OutputVendor = {
321331
init,
322332
exportTransactions: createTransactions,
323333
};
334+
335+
function isSameImportId(
336+
transactionToCreate: ynab.SaveTransaction,
337+
transactionFromYnab: ynab.TransactionDetail,
338+
): boolean {
339+
return !!transactionToCreate.import_id && transactionToCreate.import_id === transactionFromYnab.import_id;
340+
}

0 commit comments

Comments
 (0)