Release v4928.1.4.2 stable
This commit is contained in:
79
scripts/cleanup_stale_reconciliation_items.py
Executable file
79
scripts/cleanup_stale_reconciliation_items.py
Executable file
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Ignore reconciliation items outside the intended working window.
|
||||
|
||||
Dry-run by default. Use this after an external sync staged too much history.
|
||||
|
||||
Examples:
|
||||
PYTHONPATH=. python scripts/cleanup_stale_reconciliation_items.py --days 3
|
||||
PYTHONPATH=. python scripts/cleanup_stale_reconciliation_items.py --days 3 --apply
|
||||
PYTHONPATH=. python scripts/cleanup_stale_reconciliation_items.py --source odoo --days 3 --apply
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
|
||||
from sqlalchemy import text
|
||||
|
||||
from app.db import engine
|
||||
from app.reconciliation_service import (
|
||||
cleanup_reconciliation_outside_window,
|
||||
ensure_reconciliation_schema,
|
||||
)
|
||||
|
||||
|
||||
def _preview(*, days: int, source_system: str | None, limit: int) -> dict:
|
||||
ensure_reconciliation_schema()
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
days = max(int(days or 3), 1)
|
||||
cutoff = (datetime.now(timezone.utc).date() - timedelta(days=days)).isoformat()
|
||||
params = {"cutoff": cutoff, "limit": int(limit)}
|
||||
source_sql = ""
|
||||
if source_system:
|
||||
source_sql = "AND source_system = :source_system"
|
||||
params["source_system"] = source_system
|
||||
|
||||
with engine.begin() as conn:
|
||||
rows = conn.execute(text(f"""
|
||||
SELECT id::text, source_system, external_type, document_number,
|
||||
customer_name, document_date, title, status
|
||||
FROM reconciliation_items
|
||||
WHERE status IN ('open', 'needs_review')
|
||||
{source_sql}
|
||||
AND document_date IS NOT NULL
|
||||
AND document_date < CAST(:cutoff AS DATE)
|
||||
ORDER BY document_date ASC, updated_at DESC
|
||||
LIMIT :limit
|
||||
"""), params).mappings().all()
|
||||
return {"days": days, "cutoff": cutoff, "source_system": source_system or "all", "matched": len(rows), "items": [dict(r) for r in rows]}
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="Ignore stale reconciliation candidates outside a recent working window.")
|
||||
parser.add_argument("--jasmin", action="store_true", help="same as --source jasmin")
|
||||
parser.add_argument("--source", choices=["jasmin", "odoo", "packlink", "manual"], help="only clean one source system")
|
||||
parser.add_argument("--days", type=int, default=3, help="keep open items from the last N days")
|
||||
parser.add_argument("--limit", type=int, default=1000, help="maximum rows to inspect/update")
|
||||
parser.add_argument("--apply", action="store_true", help="apply changes; otherwise dry-run")
|
||||
args = parser.parse_args()
|
||||
|
||||
source = "jasmin" if args.jasmin else args.source
|
||||
if args.apply:
|
||||
result = cleanup_reconciliation_outside_window(days=args.days, source_system=source, limit=args.limit, actor="cleanup_stale_reconciliation_items")
|
||||
else:
|
||||
result = _preview(days=args.days, source_system=source, limit=args.limit)
|
||||
|
||||
print(f"Janela operacional: manter itens >= {result['cutoff']} ({result['days']} dias)")
|
||||
print(f"Fonte: {result['source_system']}")
|
||||
print(f"Encontrados para ignorar: {result['matched']}")
|
||||
for row in result.get("items", [])[:50]:
|
||||
print(f"- {row.get('document_date')} · {row.get('source_system')} · {row.get('external_type')} · {row.get('document_number')} · {row.get('customer_name') or ''}")
|
||||
|
||||
if args.apply:
|
||||
print(f"Aplicado: {result['ignored']} itens marcados como ignored")
|
||||
else:
|
||||
print("Dry-run. Para aplicar, repetir com --apply")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user