- test_address_rules_oracle.py: E2E tests import synthetic PJ+PF orders and verify id_facturare/id_livrare in Oracle; regression tests check SQLite orders imported after fix date - verify_address_rules.py: standalone script to audit PJ/PF address compliance on existing SQLite orders (--days N / --all / --status) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
171 lines
5.7 KiB
Python
171 lines
5.7 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Verifică regula adrese PJ/PF pe comenzile importate din SQLite.
|
|
|
|
Logica:
|
|
PF (cod_fiscal_gomag IS NULL): id_adresa_facturare = id_adresa_livrare
|
|
PJ (cod_fiscal_gomag IS NOT NULL): adresa_facturare_roa se potriveste cu GoMag billing
|
|
(nu cu GoMag shipping)
|
|
|
|
Rulare:
|
|
python3 scripts/verify_address_rules.py
|
|
python3 scripts/verify_address_rules.py --days 7 # ultimele 7 zile
|
|
python3 scripts/verify_address_rules.py --all # toate comenzile
|
|
python3 scripts/verify_address_rules.py --status IMPORTED
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
import os
|
|
import sqlite3
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
# Add api/ to path for app imports
|
|
_repo_root = Path(__file__).resolve().parent.parent
|
|
sys.path.insert(0, str(_repo_root / "api"))
|
|
|
|
from dotenv import load_dotenv
|
|
load_dotenv(_repo_root / "api" / ".env")
|
|
|
|
from app.services.sync_service import _addr_match
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Verifică regula adrese PJ/PF în SQLite")
|
|
parser.add_argument("--days", type=int, default=30,
|
|
help="Număr de zile în urmă (default: 30)")
|
|
parser.add_argument("--all", action="store_true",
|
|
help="Toate comenzile, indiferent de dată")
|
|
parser.add_argument("--status", default=None,
|
|
help="Filtrează după status (ex: IMPORTED)")
|
|
args = parser.parse_args()
|
|
|
|
_raw_path = os.environ.get("SQLITE_DB_PATH", "data/import.db")
|
|
db_path = _raw_path if os.path.isabs(_raw_path) else str(_repo_root / "api" / _raw_path)
|
|
if not Path(db_path).exists():
|
|
print(f"EROARE: SQLite DB nu există: {db_path}")
|
|
sys.exit(1)
|
|
|
|
conn = sqlite3.connect(db_path)
|
|
conn.row_factory = sqlite3.Row
|
|
|
|
# Build query
|
|
where_clauses = ["id_adresa_facturare IS NOT NULL", "id_adresa_livrare IS NOT NULL"]
|
|
params = []
|
|
|
|
if not args.all:
|
|
where_clauses.append("first_seen_at >= datetime('now', ?)")
|
|
params.append(f"-{args.days} days")
|
|
|
|
if args.status:
|
|
where_clauses.append("status = ?")
|
|
params.append(args.status)
|
|
|
|
where_sql = " AND ".join(where_clauses)
|
|
rows = conn.execute(f"""
|
|
SELECT order_number, status, cod_fiscal_gomag,
|
|
id_adresa_facturare, id_adresa_livrare,
|
|
adresa_facturare_gomag, adresa_livrare_gomag,
|
|
adresa_facturare_roa, adresa_livrare_roa,
|
|
first_seen_at
|
|
FROM orders
|
|
WHERE {where_sql}
|
|
ORDER BY first_seen_at DESC
|
|
""", params).fetchall()
|
|
|
|
conn.close()
|
|
|
|
if not rows:
|
|
scope = "toate comenzile" if args.all else f"ultimele {args.days} zile"
|
|
print(f"Nicio comandă cu adrese populate ({scope}).")
|
|
sys.exit(0)
|
|
|
|
pf_ok = pf_err = pj_ok = pj_err = pj_skip = 0
|
|
violations = []
|
|
|
|
for r in rows:
|
|
is_pj = bool(r["cod_fiscal_gomag"])
|
|
id_fact = r["id_adresa_facturare"]
|
|
id_livr = r["id_adresa_livrare"]
|
|
order = r["order_number"]
|
|
date = (r["first_seen_at"] or "")[:10]
|
|
|
|
if not is_pj:
|
|
# PF: id_facturare trebuie = id_livrare
|
|
if id_fact == id_livr:
|
|
pf_ok += 1
|
|
else:
|
|
pf_err += 1
|
|
violations.append({
|
|
"order": order, "date": date, "type": "PF",
|
|
"issue": f"id_fact={id_fact} != id_livr={id_livr}",
|
|
"detail": None,
|
|
})
|
|
else:
|
|
# PJ: adresa_facturare_roa trebuie sa se potriveasca cu GoMag billing
|
|
fact_roa = r["adresa_facturare_roa"]
|
|
fact_gomag = r["adresa_facturare_gomag"]
|
|
livr_gomag = r["adresa_livrare_gomag"]
|
|
|
|
if not fact_roa or not fact_gomag:
|
|
pj_skip += 1
|
|
continue
|
|
|
|
# Check 1: billing ROA matches GoMag billing
|
|
billing_match = _addr_match(fact_gomag, fact_roa)
|
|
# Check 2: billing ROA does NOT match GoMag shipping (wrong old behavior)
|
|
shipping_match = _addr_match(livr_gomag, fact_roa) if livr_gomag else False
|
|
|
|
if billing_match:
|
|
pj_ok += 1
|
|
else:
|
|
pj_err += 1
|
|
detail = "billing_ROA matches shipping GoMag" if shipping_match else "billing_ROA mismatch"
|
|
violations.append({
|
|
"order": order, "date": date, "type": "PJ",
|
|
"issue": detail,
|
|
"detail": f"billing_gomag={_short(fact_gomag)} | fact_roa={fact_roa}",
|
|
})
|
|
|
|
# Output
|
|
total = len(rows)
|
|
print(f"\n{'='*60}")
|
|
scope = "toate" if args.all else f"ultimele {args.days} zile"
|
|
print(f" Verificare adrese PJ/PF ({scope}, {total} comenzi cu adrese)")
|
|
print(f"{'='*60}")
|
|
print(f" PF (fara CUI): {pf_ok:4d} OK | {pf_err:4d} ERORI")
|
|
print(f" PJ (cu CUI): {pj_ok:4d} OK | {pj_err:4d} ERORI | {pj_skip:4d} skip (date lipsa)")
|
|
print(f"{'='*60}")
|
|
|
|
if not violations:
|
|
print(" ✓ Toate comenzile respecta regula PJ/PF.\n")
|
|
else:
|
|
print(f"\n VIOLARI ({len(violations)}):\n")
|
|
for v in violations[:20]:
|
|
print(f" [{v['date']}] {v['order']:25s} {v['type']} {v['issue']}")
|
|
if v["detail"]:
|
|
print(f" {v['detail']}")
|
|
if len(violations) > 20:
|
|
print(f" ... si inca {len(violations)-20} violari.")
|
|
print()
|
|
|
|
sys.exit(1 if violations else 0)
|
|
|
|
|
|
def _short(json_str):
|
|
"""Returnează un rezumat scurt al unui JSON de adresă."""
|
|
if not json_str:
|
|
return "(null)"
|
|
try:
|
|
d = json.loads(json_str)
|
|
return f"{d.get('address','?')}, {d.get('city','?')}"
|
|
except Exception:
|
|
return json_str[:40]
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|