diff --git a/app/services/teller_service.py b/app/services/teller_service.py index 4565740..7604492 100644 --- a/app/services/teller_service.py +++ b/app/services/teller_service.py @@ -181,17 +181,17 @@ def parse_transaction(teller_txn, pfm_account_id, category_id_map): Returns dict with keys matching Transaction model fields. Teller amounts: - - Positive = money leaving the account (expense / debit) - - Negative = money entering the account (income / credit) + - Positive = money entering the account (income / credit) + - Negative = money leaving the account (expense / debit) """ amount_raw = float(teller_txn.get('amount', 0)) - # Teller: positive = outflow (expense), negative = inflow (income) - if amount_raw > 0: + # Teller: positive = inflow/credit (income), negative = outflow/debit (expense) + if amount_raw < 0: txn_type = 'expense' - amount = amount_raw + amount = abs(amount_raw) else: txn_type = 'income' - amount = abs(amount_raw) + amount = amount_raw description = teller_txn.get('description', '').strip() or 'Teller transaction' # Use enriched counterparty name if available @@ -287,7 +287,7 @@ def import_transactions(parsed_txns, teller_account): affected_accounts = set() for p in parsed_txns: - # Duplicate check: match on teller_id in notes OR date+amount+description + # Duplicate check: match on teller_id in notes teller_id = p['teller_id'] existing = Transaction.query.filter( Transaction.notes.like(f'%{teller_id}%') @@ -319,10 +319,25 @@ def import_transactions(parsed_txns, teller_account): teller_account.enrollment.last_synced_at = now if parsed_txns: teller_account.last_teller_txn_id = parsed_txns[0]['teller_id'] - from app.extensions import db as _db - _db.session.commit() + db.session.commit() - for account_id in affected_accounts: - calc_balance(account_id) + # Refresh balance from Teller live API (source of truth for linked accounts). + # Fall back to transaction-computed balance only if the API call fails. + if teller_account.pfm_account_id: + try: + bal_data = get_balance(teller_account.enrollment.access_token, + teller_account.teller_account_id) + available = float(bal_data.get('available') or bal_data.get('ledger') or 0) + ledger = float(bal_data.get('ledger') or bal_data.get('available') or 0) + is_credit = teller_account.pfm_account.account_type == 'credit_card' + teller_account.pfm_account.balance = -abs(ledger) if is_credit else available + db.session.commit() + log.info('[teller] balance refreshed after sync for %s: %.2f', + teller_account.account_name, float(teller_account.pfm_account.balance)) + except Exception as e: + log.warning('[teller] live balance unavailable after sync, falling back to ' + 'calc_balance: %s', e) + for account_id in affected_accounts: + calc_balance(account_id) return imported, skipped