test(address): oracle integration tests + verify script for PJ/PF rule
- 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>
This commit is contained in:
170
scripts/verify_address_rules.py
Normal file
170
scripts/verify_address_rules.py
Normal file
@@ -0,0 +1,170 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user