06/05 Optimize app
This commit is contained in:
@@ -52,3 +52,25 @@ class TellerAccount(db.Model):
|
|||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f'<TellerAccount {self.account_name} → PFM #{self.pfm_account_id}>'
|
return f'<TellerAccount {self.account_name} → PFM #{self.pfm_account_id}>'
|
||||||
|
|
||||||
|
|
||||||
|
class TellerSyncPreview(db.Model):
|
||||||
|
"""
|
||||||
|
Temporary server-side storage for sync preview data.
|
||||||
|
Replaces cookie-based session storage which has a 4 KB limit —
|
||||||
|
large accounts (100+ transactions) exceeded that limit and caused
|
||||||
|
silent data loss on the confirm step.
|
||||||
|
One row per teller_account_id; overwritten on each new sync preview.
|
||||||
|
"""
|
||||||
|
__tablename__ = 'teller_sync_previews'
|
||||||
|
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
teller_account_id = db.Column(
|
||||||
|
db.Integer, db.ForeignKey('teller_accounts.id'),
|
||||||
|
nullable=False, unique=True, index=True,
|
||||||
|
)
|
||||||
|
data_json = db.Column(db.Text, nullable=False)
|
||||||
|
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f'<TellerSyncPreview account_id={self.teller_account_id}>'
|
||||||
|
|||||||
+30
-7
@@ -7,7 +7,7 @@ from flask import (Blueprint, render_template, redirect, url_for, flash,
|
|||||||
request, jsonify, current_app, session)
|
request, jsonify, current_app, session)
|
||||||
from flask_login import login_required, current_user
|
from flask_login import login_required, current_user
|
||||||
from app.extensions import db
|
from app.extensions import db
|
||||||
from app.models.teller_enrollment import TellerEnrollment, TellerAccount
|
from app.models.teller_enrollment import TellerEnrollment, TellerAccount, TellerSyncPreview
|
||||||
from app.models.account import Account
|
from app.models.account import Account
|
||||||
from app.services.teller_service import (
|
from app.services.teller_service import (
|
||||||
get_accounts, get_balance, sync_preview, import_transactions,
|
get_accounts, get_balance, sync_preview, import_transactions,
|
||||||
@@ -183,13 +183,14 @@ def sync_preview_view(teller_account_id):
|
|||||||
flash(f'Sync failed: {e}', 'danger')
|
flash(f'Sync failed: {e}', 'danger')
|
||||||
return redirect(url_for('teller.index'))
|
return redirect(url_for('teller.index'))
|
||||||
|
|
||||||
# Store preview in session for confirm step
|
# Store preview in DB (not session cookie — cookie limit is 4 KB;
|
||||||
session['teller_preview'] = [
|
# large accounts with 100+ transactions silently exceeded it).
|
||||||
|
preview_data = [
|
||||||
{
|
{
|
||||||
'teller_id': p['teller_id'],
|
'teller_id': p['teller_id'],
|
||||||
'date': p['date'].isoformat(),
|
'date': p['date'].isoformat(),
|
||||||
'transaction_type': p['transaction_type'],
|
'transaction_type': p['transaction_type'],
|
||||||
'amount': p['amount'],
|
'amount': float(p['amount']),
|
||||||
'description': p['description'],
|
'description': p['description'],
|
||||||
'account_id': p['account_id'],
|
'account_id': p['account_id'],
|
||||||
'category_id': p['category_id'],
|
'category_id': p['category_id'],
|
||||||
@@ -197,6 +198,19 @@ def sync_preview_view(teller_account_id):
|
|||||||
}
|
}
|
||||||
for p in preview
|
for p in preview
|
||||||
]
|
]
|
||||||
|
# Upsert: one preview row per teller account (overwrite any stale preview)
|
||||||
|
existing_sp = TellerSyncPreview.query.filter_by(teller_account_id=teller_account_id).first()
|
||||||
|
if existing_sp:
|
||||||
|
existing_sp.data_json = json.dumps(preview_data)
|
||||||
|
existing_sp.created_at = __import__('datetime').datetime.utcnow()
|
||||||
|
sp = existing_sp
|
||||||
|
else:
|
||||||
|
sp = TellerSyncPreview(teller_account_id=teller_account_id,
|
||||||
|
data_json=json.dumps(preview_data))
|
||||||
|
db.session.add(sp)
|
||||||
|
db.session.commit()
|
||||||
|
# Only the tiny row ID goes into the session cookie
|
||||||
|
session['teller_preview_id'] = sp.id
|
||||||
session['teller_account_id'] = teller_account_id
|
session['teller_account_id'] = teller_account_id
|
||||||
|
|
||||||
from app.models.category import Category
|
from app.models.category import Category
|
||||||
@@ -213,13 +227,22 @@ def sync_preview_view(teller_account_id):
|
|||||||
@login_required
|
@login_required
|
||||||
def sync_confirm():
|
def sync_confirm():
|
||||||
"""Import selected previewed transactions with optional type overrides."""
|
"""Import selected previewed transactions with optional type overrides."""
|
||||||
raw = session.pop('teller_preview', [])
|
preview_id = session.pop('teller_preview_id', None)
|
||||||
ta_id = session.pop('teller_account_id', None)
|
ta_id = session.pop('teller_account_id', None)
|
||||||
|
|
||||||
if not raw or not ta_id:
|
if not preview_id or not ta_id:
|
||||||
flash('No pending import. Please sync again.', 'warning')
|
flash('No pending import. Please sync again.', 'warning')
|
||||||
return redirect(url_for('teller.index'))
|
return redirect(url_for('teller.index'))
|
||||||
|
|
||||||
|
sp = db.session.get(TellerSyncPreview, preview_id)
|
||||||
|
if not sp:
|
||||||
|
flash('Preview expired or already imported. Please sync again.', 'warning')
|
||||||
|
return redirect(url_for('teller.index'))
|
||||||
|
|
||||||
|
raw = json.loads(sp.data_json)
|
||||||
|
db.session.delete(sp)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
ta = db.get_or_404(TellerAccount, ta_id)
|
ta = db.get_or_404(TellerAccount, ta_id)
|
||||||
|
|
||||||
selected_ids = set(request.form.getlist('selected'))
|
selected_ids = set(request.form.getlist('selected'))
|
||||||
|
|||||||
Reference in New Issue
Block a user