06/05 Optimize app: add Plaid reset sync
This commit is contained in:
@@ -369,6 +369,25 @@ def refresh_liabilities_view(item_db_id):
|
||||
return redirect(url_for('plaid.index'))
|
||||
|
||||
|
||||
# ── Full Resync (clear cursor so next sync fetches full history) ──────────────
|
||||
|
||||
@plaid_bp.route('/resync/<int:item_db_id>', methods=['POST'])
|
||||
@login_required
|
||||
def full_resync(item_db_id):
|
||||
item = db.get_or_404(PlaidItem, item_db_id)
|
||||
item.cursor = None
|
||||
item.last_synced_at = None
|
||||
for pa in item.accounts.filter_by(is_active=True).all():
|
||||
pa.last_sync_date = None
|
||||
db.session.commit()
|
||||
flash(
|
||||
f'{item.institution_name}: cursor reset. '
|
||||
f'Next sync will fetch full available history — duplicates will be skipped automatically.',
|
||||
'info'
|
||||
)
|
||||
return redirect(url_for('plaid.index'))
|
||||
|
||||
|
||||
# ── Disconnect ────────────────────────────────────────────────────────────────
|
||||
|
||||
@plaid_bp.route('/disconnect/<int:item_db_id>', methods=['POST'])
|
||||
|
||||
@@ -222,6 +222,51 @@ def edit(id):
|
||||
title='Edit Transaction')
|
||||
|
||||
|
||||
@transactions_bp.route('/bulk-action', methods=['POST'])
|
||||
@login_required
|
||||
def bulk_action():
|
||||
"""AJAX endpoint: delete or set-category for a list of transaction IDs."""
|
||||
data = request.get_json(silent=True) or {}
|
||||
ids = data.get('ids', [])
|
||||
action = data.get('action', '')
|
||||
|
||||
if not ids or not isinstance(ids, list):
|
||||
return jsonify({'ok': False, 'error': 'No IDs provided'}), 400
|
||||
|
||||
# Validate all IDs are integers
|
||||
try:
|
||||
ids = [int(i) for i in ids]
|
||||
except (ValueError, TypeError):
|
||||
return jsonify({'ok': False, 'error': 'Invalid IDs'}), 400
|
||||
|
||||
txns = Transaction.query.filter(Transaction.id.in_(ids)).all()
|
||||
if not txns:
|
||||
return jsonify({'ok': False, 'error': 'No transactions found'}), 404
|
||||
|
||||
if action == 'delete':
|
||||
affected_accounts = {t.account_id for t in txns}
|
||||
for txn in txns:
|
||||
db.session.delete(txn)
|
||||
db.session.commit()
|
||||
for acct_id in affected_accounts:
|
||||
if acct_id:
|
||||
calc_balance(acct_id)
|
||||
return jsonify({'ok': True, 'deleted': len(txns)})
|
||||
|
||||
elif action == 'set_category':
|
||||
cat_raw = data.get('category_id')
|
||||
cat_id = int(cat_raw) if cat_raw else None
|
||||
# Validate category exists
|
||||
if cat_id and not Category.query.get(cat_id):
|
||||
return jsonify({'ok': False, 'error': 'Invalid category'}), 400
|
||||
for txn in txns:
|
||||
txn.category_id = cat_id
|
||||
db.session.commit()
|
||||
return jsonify({'ok': True, 'updated': len(txns)})
|
||||
|
||||
return jsonify({'ok': False, 'error': 'Unknown action'}), 400
|
||||
|
||||
|
||||
@transactions_bp.route('/<int:id>/set-category', methods=['POST'])
|
||||
@login_required
|
||||
def set_category(id):
|
||||
|
||||
Reference in New Issue
Block a user