mirror of
https://github.com/escalante29/WealthySmart.git
synced 2026-07-17 10:08:46 +02:00
Import dedup transparency + override; bulk transaction actions
Paste-import now returns each skipped duplicate WITH the matched existing row (id, merchant, date, amount, source) instead of a bare count, and accepts allow_duplicates to force-import false positives — the practical fix for same-day identical purchases hashing together (UX-20; supersedes the BE-08 hash change, which would have orphaned every existing hash). POST /transactions/bulk applies delete/set_category/set_deferred to up to 500 ids atomically. 82 tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -99,6 +99,46 @@ def list_transactions(
|
||||
return session.exec(query).all()
|
||||
|
||||
|
||||
class BulkActionRequest(BaseModel):
|
||||
ids: list[int]
|
||||
action: str # 'delete' | 'set_category' | 'set_deferred'
|
||||
category_id: Optional[int] = None # for set_category (None clears)
|
||||
deferred: Optional[bool] = None # for set_deferred
|
||||
|
||||
|
||||
@router.post("/bulk")
|
||||
def bulk_action(
|
||||
req: BulkActionRequest,
|
||||
session: Session = Depends(get_session),
|
||||
_user: str = Depends(get_current_user),
|
||||
):
|
||||
"""Apply one action to many transactions atomically (review 4.4)."""
|
||||
if not req.ids or len(req.ids) > 500:
|
||||
raise HTTPException(status_code=400, detail="ids must contain 1-500 entries")
|
||||
if req.action not in ("delete", "set_category", "set_deferred"):
|
||||
raise HTTPException(status_code=400, detail=f"Unknown action: {req.action}")
|
||||
if req.action == "set_deferred" and req.deferred is None:
|
||||
raise HTTPException(status_code=400, detail="deferred is required for set_deferred")
|
||||
if req.action == "set_category" and req.category_id is not None:
|
||||
if session.get(Category, req.category_id) is None:
|
||||
raise HTTPException(status_code=404, detail="Category not found")
|
||||
|
||||
txs = session.exec(
|
||||
select(Transaction).where(col(Transaction.id).in_(req.ids))
|
||||
).all()
|
||||
for tx in txs:
|
||||
if req.action == "delete":
|
||||
session.delete(tx)
|
||||
elif req.action == "set_category":
|
||||
tx.category_id = req.category_id
|
||||
session.add(tx)
|
||||
elif req.action == "set_deferred":
|
||||
tx.deferred_to_next_cycle = bool(req.deferred)
|
||||
session.add(tx)
|
||||
session.commit()
|
||||
return {"affected": len(txs)}
|
||||
|
||||
|
||||
@router.get("/export")
|
||||
def export_transactions_csv(
|
||||
source: Optional[TransactionSource] = None,
|
||||
|
||||
Reference in New Issue
Block a user