israeli-banks-actual-budget-importer 1.5.3 → 1.6.1

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 CHANGED
@@ -1,3 +1,17 @@
1
+ ## [1.6.1](https://github.com/tomerh2001/israeli-banks-actual-budget-importer/compare/v1.6.0...v1.6.1) (2025-12-04)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * update transaction notes to use memo instead of status ([1483fdb](https://github.com/tomerh2001/israeli-banks-actual-budget-importer/commit/1483fdbd35c2e23106ab561926df79797c5b09f8))
7
+
8
+ # [1.6.0](https://github.com/tomerh2001/israeli-banks-actual-budget-importer/compare/v1.5.3...v1.6.0) (2025-12-02)
9
+
10
+
11
+ ### Features
12
+
13
+ * enhance reconciliation process ([5ebf9a6](https://github.com/tomerh2001/israeli-banks-actual-budget-importer/commit/5ebf9a6453aa8db1e6f23fd81d114fbef5c85e5c))
14
+
1
15
  ## [1.5.3](https://github.com/tomerh2001/israeli-banks-actual-budget-importer/compare/v1.5.2...v1.5.3) (2025-11-24)
2
16
 
3
17
 
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.5.3",
2
+ "version": "1.6.1",
3
3
  "name": "israeli-banks-actual-budget-importer",
4
4
  "module": "index.ts",
5
5
  "type": "module",
package/src/utils.ts CHANGED
@@ -1,3 +1,4 @@
1
+ /* eslint-disable @typescript-eslint/no-unsafe-argument */
1
2
  /* eslint-disable @typescript-eslint/no-unsafe-assignment */
2
3
  /* eslint-disable @typescript-eslint/naming-convention */
3
4
  /* eslint-disable @typescript-eslint/no-unsafe-call */
@@ -53,10 +54,13 @@ export async function scrapeAndImportTransactions({companyId, bank}: ScrapeTrans
53
54
  amount: actual.utils.amountToInteger(x.chargedAmount),
54
55
  payee: _.find(payees, {name: x.description})?.id ?? (await actual.createPayee({name: x.description})),
55
56
  imported_payee: x.description,
56
- notes: x.status,
57
+ notes: x.memo,
57
58
  imported_id: `${x.identifier}-${moment(x.date).format('YYYY-MM-DD HH:mm:ss')}`,
58
59
  }));
59
60
 
61
+ // Print first 5 mapped transactions for debugging
62
+ log('MAPPED_TRANSACTIONS_SAMPLE', {sample: await Promise.all(mappedTransactions.slice(0, 5))});
63
+
60
64
  stdout.mute();
61
65
  const importResult = await actual.importTransactions(bank.actualAccountId, await Promise.all(mappedTransactions), {defaultCleared: true});
62
66
  stdout.unmute();
@@ -74,27 +78,68 @@ export async function scrapeAndImportTransactions({companyId, bank}: ScrapeTrans
74
78
 
75
79
  const currentBalance = actual.utils.integerToAmount(await actual.getAccountBalance(bank.actualAccountId));
76
80
  const balanceDiff = accountBalance - currentBalance;
81
+
82
+ // Use a stable imported_id per account so we can find and update/delete the same
83
+ // reconciliation transaction instead of creating a new one every run.
84
+ const reconciliationImportedId = `reconciliation-${bank.actualAccountId}`;
85
+
86
+ // Fetch all transactions for this account and look for an existing reconciliation.
87
+ // Use a wide date range so we always find it if it exists.
88
+ const allAccountTxns: TransactionEntity[] = await actual.getTransactions(
89
+ bank.actualAccountId,
90
+ '2000-01-01',
91
+ moment().add(1, 'year').format('YYYY-MM-DD'),
92
+ );
93
+
94
+ const existingReconciliation = allAccountTxns.find(txn => txn.imported_id === reconciliationImportedId);
95
+
96
+ // If balances are already in sync, remove any existing reconciliation and exit.
77
97
  if (balanceDiff === 0) {
98
+ if (existingReconciliation) {
99
+ stdout.mute();
100
+ await actual.deleteTransaction(existingReconciliation.id);
101
+ stdout.unmute();
102
+ log('RECONCILIATION_REMOVED');
103
+ }
104
+
78
105
  return;
79
106
  }
80
107
 
81
- log('RECONCILIATION', {from: currentBalance, to: accountBalance, diff: balanceDiff});
108
+ log('RECONCILIATION', {
109
+ from: currentBalance,
110
+ to: accountBalance,
111
+ diff: balanceDiff,
112
+ });
82
113
 
83
- stdout.mute();
84
- const reconciliationResult = await actual.importTransactions(bank.actualAccountId, [{
114
+ const reconciliationTxn = {
115
+ account: bank.actualAccountId,
85
116
  date: moment().format('YYYY-MM-DD'),
86
117
  amount: actual.utils.amountToInteger(balanceDiff),
87
- payee: null,
118
+ payee: undefined,
88
119
  imported_payee: 'Reconciliation',
89
120
  notes: `Reconciliation from ${currentBalance.toLocaleString()} to ${accountBalance.toLocaleString()}`,
90
- imported_id: `reconciliation-${moment().format('YYYY-MM-DD HH:mm:ss')}`,
91
- }]);
92
- stdout.unmute();
121
+ imported_id: reconciliationImportedId,
122
+ };
93
123
 
94
- if (_.isEmpty(reconciliationResult)) {
95
- console.error('Reconciliation errors', reconciliationResult.errors);
124
+ stdout.mute();
125
+ if (existingReconciliation) {
126
+ // Update the single reconciliation transaction
127
+ await actual.updateTransaction(existingReconciliation.id, reconciliationTxn);
128
+ stdout.unmute();
129
+ log('RECONCILIATION_UPDATED', {transactionId: existingReconciliation.id});
96
130
  } else {
97
- log('RECONCILIATION_ADDED', {transactions: reconciliationResult.added.length});
131
+ // Create the reconciliation transaction for the first time
132
+ const reconciliationResult = await actual.importTransactions(
133
+ bank.actualAccountId,
134
+ [reconciliationTxn],
135
+ );
136
+ stdout.unmute();
137
+
138
+ if (!reconciliationResult || _.isEmpty(reconciliationResult.added)) {
139
+ console.error('Reconciliation errors', reconciliationResult?.errors);
140
+ } else {
141
+ log('RECONCILIATION_ADDED', {transactions: reconciliationResult.added.length});
142
+ }
98
143
  }
99
144
  } catch (error) {
100
145
  console.error('Error', companyId, error);
@@ -102,4 +147,3 @@ export async function scrapeAndImportTransactions({companyId, bank}: ScrapeTrans
102
147
  log('DONE');
103
148
  }
104
149
  }
105
-