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:
347
api/tests/test_address_rules_oracle.py
Normal file
347
api/tests/test_address_rules_oracle.py
Normal file
@@ -0,0 +1,347 @@
|
||||
"""
|
||||
Oracle Integration Tests — Regula adrese PJ/PF
|
||||
===============================================
|
||||
Verifică că comenzile importate respectă regula:
|
||||
PF (fără CUI): id_adresa_facturare = id_adresa_livrare
|
||||
PJ (cu CUI): adresa_facturare_roa se potrivește cu adresa billing GoMag
|
||||
|
||||
Testele principale sunt E2E (importă comenzi sintetice în Oracle și verifică).
|
||||
Testele de regresie verifică comenzile existente din SQLite.
|
||||
|
||||
Run:
|
||||
pytest api/tests/test_address_rules_oracle.py -v
|
||||
./test.sh oracle
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
import pytest
|
||||
|
||||
pytestmark = pytest.mark.oracle
|
||||
|
||||
_script_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")
|
||||
_project_root = os.path.dirname(_script_dir)
|
||||
|
||||
from dotenv import load_dotenv
|
||||
_env_path = os.path.join(_script_dir, ".env")
|
||||
load_dotenv(_env_path, override=True)
|
||||
|
||||
_tns_admin = os.environ.get("TNS_ADMIN", "")
|
||||
if _tns_admin and os.path.isfile(_tns_admin):
|
||||
os.environ["TNS_ADMIN"] = os.path.dirname(_tns_admin)
|
||||
elif not _tns_admin:
|
||||
os.environ["TNS_ADMIN"] = _script_dir
|
||||
|
||||
if _script_dir not in sys.path:
|
||||
sys.path.insert(0, _script_dir)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Fixtures
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def oracle_env():
|
||||
"""Re-aplică .env și actualizează settings pentru Oracle."""
|
||||
load_dotenv(_env_path, override=True)
|
||||
_tns = os.environ.get("TNS_ADMIN", "")
|
||||
if _tns and os.path.isfile(_tns):
|
||||
os.environ["TNS_ADMIN"] = os.path.dirname(_tns)
|
||||
|
||||
from app.config import settings
|
||||
settings.ORACLE_USER = os.environ.get("ORACLE_USER", "MARIUSM_AUTO")
|
||||
settings.ORACLE_PASSWORD = os.environ.get("ORACLE_PASSWORD", "ROMFASTSOFT")
|
||||
settings.ORACLE_DSN = os.environ.get("ORACLE_DSN", "ROA_CENTRAL")
|
||||
settings.TNS_ADMIN = os.environ.get("TNS_ADMIN", _script_dir)
|
||||
settings.FORCE_THIN_MODE = os.environ.get("FORCE_THIN_MODE", "") == "true"
|
||||
return settings
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def client(oracle_env):
|
||||
from fastapi.testclient import TestClient
|
||||
from app.main import app
|
||||
with TestClient(app) as c:
|
||||
yield c
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def oracle_pool(oracle_env):
|
||||
"""Pool Oracle direct pentru verificări în DB."""
|
||||
from app import database
|
||||
database.init_oracle()
|
||||
yield database.pool
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def real_codmat(client):
|
||||
"""CODMAT real din Oracle pentru liniile comenzii sintetice."""
|
||||
for term in ["01", "PH", "CA", "A"]:
|
||||
resp = client.get("/api/articles/search", params={"q": term})
|
||||
if resp.status_code == 200:
|
||||
results = resp.json().get("results", [])
|
||||
if results:
|
||||
return results[0]["codmat"]
|
||||
pytest.skip("Nu s-a găsit niciun CODMAT în Oracle pentru test")
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def app_settings(client):
|
||||
"""Setările aplicației (id_pol, id_sectie, etc.)."""
|
||||
resp = client.get("/api/sync/schedule")
|
||||
assert resp.status_code == 200
|
||||
import sqlite3
|
||||
db_path = os.environ.get("SQLITE_DB_PATH", os.path.join(_script_dir, "orders.db"))
|
||||
conn = sqlite3.connect(db_path)
|
||||
conn.row_factory = sqlite3.Row
|
||||
rows = conn.execute("SELECT key, value FROM app_settings").fetchall()
|
||||
conn.close()
|
||||
return {r["key"]: r["value"] for r in rows}
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def run_id():
|
||||
return f"pytest-addr-{int(time.time())}"
|
||||
|
||||
|
||||
def _build_pj_order(run_id, real_codmat):
|
||||
"""Comandă sintetică PJ: companie cu billing ≠ shipping."""
|
||||
from app.services.order_reader import OrderBilling, OrderShipping, OrderData, OrderItem
|
||||
billing = OrderBilling(
|
||||
firstname="Test", lastname="PJ", phone="0700000000", email="pj@pytest.local",
|
||||
address="Bld Unirii 1", city="Bucuresti", region="Bucuresti", country="RO",
|
||||
company_name="PYTEST COMPANY SRL", company_code="RO99000001", company_reg="J40/9999/2026",
|
||||
is_company=True
|
||||
)
|
||||
shipping = OrderShipping(
|
||||
firstname="Curier", lastname="Destinatar", phone="0799999999", email="ship@pytest.local",
|
||||
address="Str Livrare 99", city="Cluj-Napoca", region="Cluj", country="RO"
|
||||
)
|
||||
return OrderData(
|
||||
id=f"{run_id}-PJ",
|
||||
number=f"{run_id}-PJ",
|
||||
date="2026-01-15T10:00:00",
|
||||
status="new", status_id="1",
|
||||
billing=billing, shipping=shipping,
|
||||
items=[OrderItem(sku="PYTEST-SKU-PJ", name="Test PJ Item",
|
||||
price=10.0, quantity=1.0, vat=19.0)],
|
||||
total=10.0, delivery_cost=0.0, discount_total=0.0
|
||||
)
|
||||
|
||||
|
||||
def _build_pf_order(run_id, real_codmat):
|
||||
"""Comandă sintetică PF: persoană fizică, billing ≠ shipping (dar billing ROA trebuie = shipping)."""
|
||||
from app.services.order_reader import OrderBilling, OrderShipping, OrderData, OrderItem
|
||||
billing = OrderBilling(
|
||||
firstname="Ion", lastname="Popescu", phone="0700000001", email="pf@pytest.local",
|
||||
address="Str Alta 5", city="Timisoara", region="Timis", country="RO",
|
||||
company_name="", company_code="", company_reg="", is_company=False
|
||||
)
|
||||
shipping = OrderShipping(
|
||||
firstname="Ion", lastname="Popescu", phone="0700000001", email="pf@pytest.local",
|
||||
address="Str Livrare 10", city="Iasi", region="Iasi", country="RO"
|
||||
)
|
||||
return OrderData(
|
||||
id=f"{run_id}-PF",
|
||||
number=f"{run_id}-PF",
|
||||
date="2026-01-15T10:00:00",
|
||||
status="new", status_id="1",
|
||||
billing=billing, shipping=shipping,
|
||||
items=[OrderItem(sku="PYTEST-SKU-PF", name="Test PF Item",
|
||||
price=10.0, quantity=1.0, vat=19.0)],
|
||||
total=10.0, delivery_cost=0.0, discount_total=0.0
|
||||
)
|
||||
|
||||
|
||||
def _cleanup_test_orders(oracle_pool, run_id):
|
||||
"""Șterge comenzile de test din Oracle."""
|
||||
try:
|
||||
conn = oracle_pool.acquire()
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
"DELETE FROM comenzi WHERE comanda_externa LIKE :1",
|
||||
[f"{run_id}%"]
|
||||
)
|
||||
conn.commit()
|
||||
oracle_pool.release(conn)
|
||||
except Exception as e:
|
||||
print(f"Cleanup warning: {e}")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Test E2E: import PJ + PF sintetice în Oracle
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestAddressRulesE2E:
|
||||
"""Import comenzi sintetice și verifică adresele în Oracle."""
|
||||
|
||||
@pytest.fixture(scope="class", autouse=True)
|
||||
def cleanup(self, oracle_pool, run_id):
|
||||
yield
|
||||
_cleanup_test_orders(oracle_pool, run_id)
|
||||
|
||||
def test_pj_billing_addr_is_gomag_billing(self, oracle_pool, real_codmat, app_settings, run_id):
|
||||
"""PJ: adresa facturare în Oracle provine din GoMag billing (nu shipping)."""
|
||||
from app.services.import_service import import_single_order
|
||||
from app.services.order_reader import OrderItem
|
||||
|
||||
order = _build_pj_order(run_id, real_codmat)
|
||||
# Replace test SKU with real codmat via mapping (or just use items with real SKU)
|
||||
order.items = [OrderItem(sku=real_codmat, name="Test PJ",
|
||||
price=10.0, quantity=1.0, vat=19.0)]
|
||||
|
||||
id_pol = int(app_settings.get("id_pol") or 0) or None
|
||||
id_sectie = int(app_settings.get("id_sectie") or 0) or None
|
||||
|
||||
result = import_single_order(order, id_pol=id_pol, id_sectie=id_sectie,
|
||||
app_settings=app_settings)
|
||||
|
||||
if not result["success"]:
|
||||
pytest.skip(f"Import PJ eșuat (SKU probabil nemapat): {result.get('error')}")
|
||||
|
||||
id_fact = result["id_adresa_facturare"]
|
||||
id_livr = result["id_adresa_livrare"]
|
||||
|
||||
assert id_fact is not None, "PJ: id_adresa_facturare lipsește din result"
|
||||
assert id_livr is not None, "PJ: id_adresa_livrare lipsește din result"
|
||||
|
||||
# PJ cu billing ≠ shipping: adresele trebuie să fie DIFERITE
|
||||
assert id_fact != id_livr, (
|
||||
f"PJ cu billing≠shipping trebuie să aibă id_fact({id_fact}) ≠ id_livr({id_livr}). "
|
||||
f"Regula veche (different_person) s-ar comporta la fel, dar acum PJ folosește billing GoMag."
|
||||
)
|
||||
|
||||
# Verifică în Oracle că adresele există
|
||||
conn = oracle_pool.acquire()
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
"SELECT id_livrare, id_facturare FROM comenzi WHERE comanda_externa = :1",
|
||||
[order.number]
|
||||
)
|
||||
row = cur.fetchone()
|
||||
oracle_pool.release(conn)
|
||||
|
||||
assert row is not None, f"Comanda {order.number} nu s-a găsit în Oracle comenzi"
|
||||
assert row[0] == id_livr, f"id_livrare Oracle ({row[0]}) ≠ result ({id_livr})"
|
||||
assert row[1] == id_fact, f"id_facturare Oracle ({row[1]}) ≠ result ({id_fact})"
|
||||
|
||||
def test_pf_billing_addr_equals_shipping(self, oracle_pool, real_codmat, app_settings, run_id):
|
||||
"""PF: adresa facturare în Oracle = adresa livrare (ramburs curier)."""
|
||||
from app.services.import_service import import_single_order
|
||||
from app.services.order_reader import OrderItem
|
||||
|
||||
order = _build_pf_order(run_id, real_codmat)
|
||||
order.items = [OrderItem(sku=real_codmat, name="Test PF",
|
||||
price=10.0, quantity=1.0, vat=19.0)]
|
||||
|
||||
id_pol = int(app_settings.get("id_pol") or 0) or None
|
||||
id_sectie = int(app_settings.get("id_sectie") or 0) or None
|
||||
|
||||
result = import_single_order(order, id_pol=id_pol, id_sectie=id_sectie,
|
||||
app_settings=app_settings)
|
||||
|
||||
if not result["success"]:
|
||||
pytest.skip(f"Import PF eșuat: {result.get('error')}")
|
||||
|
||||
id_fact = result["id_adresa_facturare"]
|
||||
id_livr = result["id_adresa_livrare"]
|
||||
|
||||
assert id_fact is not None, "PF: id_adresa_facturare lipsește din result"
|
||||
assert id_livr is not None, "PF: id_adresa_livrare lipsește din result"
|
||||
|
||||
# PF: id_facturare TREBUIE să fie = id_livrare
|
||||
assert id_fact == id_livr, (
|
||||
f"PF trebuie să aibă id_fact({id_fact}) = id_livr({id_livr}) — "
|
||||
f"ramburs curier pe adresa de livrare"
|
||||
)
|
||||
|
||||
# Verifică în Oracle
|
||||
conn = oracle_pool.acquire()
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
"SELECT id_livrare, id_facturare FROM comenzi WHERE comanda_externa = :1",
|
||||
[order.number]
|
||||
)
|
||||
row = cur.fetchone()
|
||||
oracle_pool.release(conn)
|
||||
|
||||
assert row is not None, f"Comanda {order.number} nu s-a găsit în Oracle comenzi"
|
||||
assert row[1] == row[0], (
|
||||
f"Oracle: id_facturare({row[1]}) ≠ id_livrare({row[0]}) pentru PF"
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Test regresie: comenzi existente în SQLite
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestAddressRulesRegression:
|
||||
"""Verifică că comenzile existente importate după fix respectă regula PJ/PF."""
|
||||
|
||||
FIX_DATE = "2026-04-08" # data când a fost aplicat fix-ul
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
def sqlite_rows(self):
|
||||
"""Comenzi cu adrese populate importate după data fix-ului."""
|
||||
import sqlite3
|
||||
from app.config import settings
|
||||
db_path = os.environ.get("SQLITE_DB_PATH", os.path.join(_script_dir, "orders.db"))
|
||||
if not os.path.exists(db_path):
|
||||
pytest.skip(f"SQLite DB lipsă: {db_path}")
|
||||
|
||||
conn = sqlite3.connect(db_path)
|
||||
conn.row_factory = sqlite3.Row
|
||||
rows = conn.execute("""
|
||||
SELECT order_number, 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 id_adresa_facturare IS NOT NULL
|
||||
AND id_adresa_livrare IS NOT NULL
|
||||
AND first_seen_at >= ?
|
||||
""", (self.FIX_DATE,)).fetchall()
|
||||
conn.close()
|
||||
return rows
|
||||
|
||||
def test_pf_id_facturare_equals_id_livrare(self, sqlite_rows):
|
||||
"""PF noi: id_adresa_facturare = id_adresa_livrare."""
|
||||
pf_rows = [r for r in sqlite_rows if not r["cod_fiscal_gomag"]]
|
||||
if not pf_rows:
|
||||
pytest.skip(f"Nicio comandă PF importată după {self.FIX_DATE}")
|
||||
|
||||
violations = [
|
||||
f"{r['order_number']}: id_fact={r['id_adresa_facturare']} id_livr={r['id_adresa_livrare']}"
|
||||
for r in pf_rows
|
||||
if r["id_adresa_facturare"] != r["id_adresa_livrare"]
|
||||
]
|
||||
assert not violations, (
|
||||
f"PF comenzi cu id_fact ≠ id_livr ({len(violations)}):\n" + "\n".join(violations[:10])
|
||||
)
|
||||
|
||||
def test_pj_billing_roa_matches_gomag_billing(self, sqlite_rows):
|
||||
"""PJ noi: adresa_facturare_roa se potrivește cu GoMag billing address."""
|
||||
from app.services.sync_service import _addr_match
|
||||
|
||||
pj_rows = [
|
||||
r for r in sqlite_rows
|
||||
if r["cod_fiscal_gomag"] and r["adresa_facturare_gomag"] and r["adresa_facturare_roa"]
|
||||
]
|
||||
if not pj_rows:
|
||||
pytest.skip(f"Nicio comandă PJ cu adrese populate importată după {self.FIX_DATE}")
|
||||
|
||||
violations = []
|
||||
for r in pj_rows:
|
||||
if not _addr_match(r["adresa_facturare_gomag"], r["adresa_facturare_roa"]):
|
||||
violations.append(
|
||||
f"{r['order_number']}: billing_gomag={r['adresa_facturare_gomag']!r} "
|
||||
f"fact_roa={r['adresa_facturare_roa']!r}"
|
||||
)
|
||||
|
||||
assert not violations, (
|
||||
f"PJ comenzi cu adresa_facturare_roa care nu corespunde GoMag billing ({len(violations)}):\n"
|
||||
+ "\n".join(violations[:10])
|
||||
)
|
||||
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