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

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,10 @@
1
+ # [1.6.0](https://github.com/tomerh2001/israeli-banks-actual-budget-importer/compare/v1.5.3...v1.6.0) (2025-12-02)
2
+
3
+
4
+ ### Features
5
+
6
+ * enhance reconciliation process ([5ebf9a6](https://github.com/tomerh2001/israeli-banks-actual-budget-importer/commit/5ebf9a6453aa8db1e6f23fd81d114fbef5c85e5c))
7
+
1
8
  ## [1.5.3](https://github.com/tomerh2001/israeli-banks-actual-budget-importer/compare/v1.5.2...v1.5.3) (2025-11-24)
2
9
 
3
10
 
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.5.3",
2
+ "version": "1.6.0",
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 */
@@ -74,27 +75,68 @@ export async function scrapeAndImportTransactions({companyId, bank}: ScrapeTrans
74
75
 
75
76
  const currentBalance = actual.utils.integerToAmount(await actual.getAccountBalance(bank.actualAccountId));
76
77
  const balanceDiff = accountBalance - currentBalance;
78
+
79
+ // Use a stable imported_id per account so we can find and update/delete the same
80
+ // reconciliation transaction instead of creating a new one every run.
81
+ const reconciliationImportedId = `reconciliation-${bank.actualAccountId}`;
82
+
83
+ // Fetch all transactions for this account and look for an existing reconciliation.
84
+ // Use a wide date range so we always find it if it exists.
85
+ const allAccountTxns: TransactionEntity[] = await actual.getTransactions(
86
+ bank.actualAccountId,
87
+ '2000-01-01',
88
+ moment().add(1, 'year').format('YYYY-MM-DD'),
89
+ );
90
+
91
+ const existingReconciliation = allAccountTxns.find(txn => txn.imported_id === reconciliationImportedId);
92
+
93
+ // If balances are already in sync, remove any existing reconciliation and exit.
77
94
  if (balanceDiff === 0) {
95
+ if (existingReconciliation) {
96
+ stdout.mute();
97
+ await actual.deleteTransaction(existingReconciliation.id);
98
+ stdout.unmute();
99
+ log('RECONCILIATION_REMOVED');
100
+ }
101
+
78
102
  return;
79
103
  }
80
104
 
81
- log('RECONCILIATION', {from: currentBalance, to: accountBalance, diff: balanceDiff});
105
+ log('RECONCILIATION', {
106
+ from: currentBalance,
107
+ to: accountBalance,
108
+ diff: balanceDiff,
109
+ });
82
110
 
83
- stdout.mute();
84
- const reconciliationResult = await actual.importTransactions(bank.actualAccountId, [{
111
+ const reconciliationTxn = {
112
+ account: bank.actualAccountId,
85
113
  date: moment().format('YYYY-MM-DD'),
86
114
  amount: actual.utils.amountToInteger(balanceDiff),
87
- payee: null,
115
+ payee: undefined,
88
116
  imported_payee: 'Reconciliation',
89
117
  notes: `Reconciliation from ${currentBalance.toLocaleString()} to ${accountBalance.toLocaleString()}`,
90
- imported_id: `reconciliation-${moment().format('YYYY-MM-DD HH:mm:ss')}`,
91
- }]);
92
- stdout.unmute();
118
+ imported_id: reconciliationImportedId,
119
+ };
93
120
 
94
- if (_.isEmpty(reconciliationResult)) {
95
- console.error('Reconciliation errors', reconciliationResult.errors);
121
+ stdout.mute();
122
+ if (existingReconciliation) {
123
+ // Update the single reconciliation transaction
124
+ await actual.updateTransaction(existingReconciliation.id, reconciliationTxn);
125
+ stdout.unmute();
126
+ log('RECONCILIATION_UPDATED', {transactionId: existingReconciliation.id});
96
127
  } else {
97
- log('RECONCILIATION_ADDED', {transactions: reconciliationResult.added.length});
128
+ // Create the reconciliation transaction for the first time
129
+ const reconciliationResult = await actual.importTransactions(
130
+ bank.actualAccountId,
131
+ [reconciliationTxn],
132
+ );
133
+ stdout.unmute();
134
+
135
+ if (!reconciliationResult || _.isEmpty(reconciliationResult.added)) {
136
+ console.error('Reconciliation errors', reconciliationResult?.errors);
137
+ } else {
138
+ log('RECONCILIATION_ADDED', {transactions: reconciliationResult.added.length});
139
+ }
98
140
  }
99
141
  } catch (error) {
100
142
  console.error('Error', companyId, error);
@@ -102,4 +144,3 @@ export async function scrapeAndImportTransactions({companyId, bank}: ScrapeTrans
102
144
  log('DONE');
103
145
  }
104
146
  }
105
-