@@ -16,6 +16,7 @@ import * as ynab from 'ynab';
16
16
const YNAB_DATE_FORMAT = 'YYYY-MM-DD' ;
17
17
const NOW = moment ( ) ;
18
18
const MIN_YNAB_ACCESS_TOKEN_LENGTH = 43 ;
19
+ const MAX_YNAB_IMPORT_ID_LENGTH = 36 ;
19
20
20
21
const categoriesMap = new Map < string , Pick < ynab . Category , 'id' | 'name' | 'category_group_id' > > ( ) ;
21
22
const transactionsFromYnab = new Map < Date , ynab . TransactionDetail [ ] > ( ) ;
@@ -109,7 +110,6 @@ export function getPayeeName(transaction: EnrichedTransaction, payeeNameMaxLengt
109
110
function convertTransactionToYnabFormat ( originalTransaction : EnrichedTransaction ) : ynab . SaveTransaction {
110
111
const amount = Math . round ( originalTransaction . chargedAmount * 1000 ) ;
111
112
const date = convertTimestampToYnabDateFormat ( originalTransaction ) ;
112
-
113
113
return {
114
114
account_id : getYnabAccountIdByAccountNumberFromTransaction ( originalTransaction . accountNumber ) ,
115
115
date, // "2019-01-17",
@@ -119,12 +119,20 @@ function convertTransactionToYnabFormat(originalTransaction: EnrichedTransaction
119
119
category_id : getYnabCategoryIdFromCategoryName ( originalTransaction . category ) ,
120
120
memo : originalTransaction . memo ,
121
121
cleared : ynab . SaveTransaction . ClearedEnum . Cleared ,
122
+ import_id : buildImportId ( originalTransaction ) , // [date][amount][description]
122
123
// "approved": true,
123
124
// "flag_color": "red",
124
125
// "import_id": buildImportId(originalTransaction.description, amount, date) // 'YNAB:[milliunit_amount]:[iso_date]:[occurrence]'
125
126
} ;
126
127
}
127
128
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
+
128
136
function getYnabAccountIdByAccountNumberFromTransaction ( transactionAccountNumber : string ) : string {
129
137
const ynabAccountId = ynabConfig ! . options . accountNumbersToYnabAccountIds [ transactionAccountNumber ] ;
130
138
if ( ! ynabAccountId ) {
@@ -189,14 +197,16 @@ export function isSameTransaction(
189
197
transactionFromYnab : ynab . TransactionDetail ,
190
198
) {
191
199
const isATransferTransaction = ! ! transactionFromYnab . transfer_account_id ;
200
+ const isTransactionsImportIdEqual = isSameImportId ( transactionToCreate , transactionFromYnab ) ;
192
201
return (
193
202
transactionToCreate . account_id === transactionFromYnab . account_id &&
194
203
transactionToCreate . date === transactionFromYnab . date &&
195
204
// @ts -expect-error error TS18049: 'transactionToCreate.amount' is possibly 'null' or 'undefined'
196
205
Math . abs ( transactionToCreate . amount - transactionFromYnab . amount ) < 1000 &&
197
206
// In a transfer transaction the payee name changes, but we still consider this the same transaction
198
207
( areStringsEqualIgnoreCaseAndWhitespace ( transactionToCreate . payee_name , transactionFromYnab . payee_name ) ||
199
- isATransferTransaction )
208
+ isATransferTransaction ||
209
+ isTransactionsImportIdEqual )
200
210
) ;
201
211
}
202
212
@@ -321,3 +331,10 @@ export const ynabOutputVendor: OutputVendor = {
321
331
init,
322
332
exportTransactions : createTransactions ,
323
333
} ;
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