Skip to content

Commit 4d9c251

Browse files
committed
fix: skip pending transactions during import
1 parent c2b4e5b commit 4d9c251

2 files changed

Lines changed: 26 additions & 4 deletions

File tree

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ This project provides an importer from Israeli banks (via [israeli-bank-scrapers
2727
6. **Concurrent Processing**
2828
Uses a queue (via [p-queue](https://www.npmjs.com/package/p-queue)) to manage scraping tasks concurrently.
2929

30+
7. **Pending transactions are skipped**
31+
Only completed transactions are imported into Actual, which avoids provisional card amounts and pre-conversion foreign-currency rows landing in the final ledger.
32+
3033
## Installation
3134

3235
### Docker
@@ -279,4 +282,4 @@ This project is open-source. Please see the [LICENSE](./LICENSE) file for licens
279282

280283
- **israeli-bank-scrapers:** Thanks to the contributors of the bank scraper libraries.
281284
- **Actual App:** For providing a powerful budgeting API.
282-
- **Open-source Community:** Your support and contributions are appreciated.
285+
- **Open-source Community:** Your support and contributions are appreciated.

src/importer.ts

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,16 +112,35 @@ export async function scrapeAndImportTransactions({companyId, bank}: ScrapeTrans
112112
for (const target of targets) {
113113
const selectedAccounts = selectScraperAccounts(result.accounts as any[], target.accounts);
114114

115-
// Transactions to import: selected accounts with txns.
115+
// Pending transactions can still carry provisional amounts or FX values,
116+
// so only completed transactions should be imported into the final ledger.
116117
const transactions = _(selectedAccounts)
117118
.filter(a => Array.isArray(a.txns) && a.txns.length > 0)
118119
.flatMap(a => a.txns.map((t: any) => ({txn: t, accountNumber: String(a.accountNumber)})))
119120
.value();
121+
const pendingTransactions = transactions.filter(({txn}) => txn?.status === 'pending');
122+
const completedTransactions = transactions.filter(({txn}) => txn?.status !== 'pending');
120123

121-
if (transactions.length === 0) {
124+
if (pendingTransactions.length > 0) {
125+
log('SKIPPED_PENDING_TRANSACTIONS', {
126+
actualAccountId: target.actualAccountId,
127+
count: pendingTransactions.length,
128+
sample: pendingTransactions.slice(0, 5).map(({txn, accountNumber}) => ({
129+
accountNumber,
130+
date: moment(txn?.date).format('YYYY-MM-DD'),
131+
description: txn?.description,
132+
originalAmount: txn?.originalAmount,
133+
originalCurrency: txn?.originalCurrency,
134+
chargedAmount: txn?.chargedAmount,
135+
chargedCurrency: txn?.chargedCurrency,
136+
})),
137+
});
138+
}
139+
140+
if (completedTransactions.length === 0) {
122141
log('NO_TRANSACTIONS', {actualAccountId: target.actualAccountId});
123142
} else {
124-
const mappedTransactions = transactions.map(async ({txn, accountNumber}) => stripUndefined({
143+
const mappedTransactions = completedTransactions.map(async ({txn, accountNumber}) => stripUndefined({
125144
date: moment(txn.date).format('YYYY-MM-DD'),
126145
amount: actual.utils.amountToInteger(txn.chargedAmount),
127146
payee: _.find(payees, {name: txn.description})?.id ?? (await actual.createPayee({name: txn.description})),

0 commit comments

Comments
 (0)