Acasa = ecran de import (tab Import scos, ?tab=import->Acasa). Bara status compacta pe 2 randuri cu bife accesibile (glife + text) + data formatata. 'Coada'->'Trimiteri': coloane RO, stare umana, detaliu la click in panou dedicat. Mapari pe 3 sectiuni (de rezolvat / op salvate / formate coloane), Cont doar cheie+creds. Filtrare Trimiteri, corectie inline needs_data cu re-enqueue + detectie coliziune idempotency, badge contoare pe tab-uri. Helper pur partajat payload_view.py (web + GET /v1/prezentari). Backend trimitere (worker/idempotenta/mapping/schema) neatins. 483 teste. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
89 lines
3.1 KiB
Python
89 lines
3.1 KiB
Python
"""Teste US-008 (PRD 3.5): preview-ul de import arata MOTIVUL randurilor respinse.
|
|
|
|
Un rand needs_data (ex. lipsa odometru) trebuie sa apara cu motivul explicit
|
|
(mesajul de validare), nu doar numarat la "blocate".
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import csv
|
|
import io
|
|
import os
|
|
import re
|
|
import tempfile
|
|
|
|
import pytest
|
|
|
|
|
|
@pytest.fixture()
|
|
def client(monkeypatch):
|
|
tmp = tempfile.mkdtemp()
|
|
monkeypatch.setenv("AUTOPASS_DB_PATH", os.path.join(tmp, "prev.db"))
|
|
monkeypatch.setenv("AUTOPASS_WEB_AUTH_REQUIRED", "false")
|
|
from app.config import get_settings
|
|
get_settings.cache_clear()
|
|
from app.main import app
|
|
from fastapi.testclient import TestClient
|
|
with TestClient(app) as c:
|
|
yield c
|
|
get_settings.cache_clear()
|
|
|
|
|
|
def _csv_bytes(rows: list[dict]) -> bytes:
|
|
buf = io.StringIO()
|
|
writer = csv.DictWriter(buf, fieldnames=list(rows[0].keys()), delimiter=";")
|
|
writer.writeheader()
|
|
writer.writerows(rows)
|
|
return buf.getvalue().encode("utf-8")
|
|
|
|
|
|
def _seed_mapping_op1() -> None:
|
|
"""Mapeaza OP-1 -> R-FRANE (cont dev id=1) ca randurile sa nu fie needs_mapping."""
|
|
from app.db import get_connection
|
|
conn = get_connection()
|
|
try:
|
|
conn.execute("INSERT OR REPLACE INTO nomenclator_rar (cod_prestatie, nume_prestatie) VALUES ('R-FRANE','Reparatie frane')")
|
|
conn.execute(
|
|
"INSERT OR IGNORE INTO operations_mapping (account_id, cod_op_service, cod_prestatie, auto_send) "
|
|
"VALUES (1, 'OP-1', 'R-FRANE', 1)"
|
|
)
|
|
conn.commit()
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
def test_preview_arata_motiv_needs_data(client):
|
|
"""Un rand fara odometru apare in preview cu motivul, nu doar numarat la blocate."""
|
|
_seed_mapping_op1()
|
|
rows = [
|
|
# rand valid
|
|
{"VIN": "WVWZZZ1KZAW000123", "Nr inmatriculare": "B001TST",
|
|
"Data prestatie": "15.06.2026", "Odometru final": "123456", "Operatie": "OP-1"},
|
|
# rand fara odometru -> needs_data
|
|
{"VIN": "WVWZZZ1KZAW000456", "Nr inmatriculare": "B002TST",
|
|
"Data prestatie": "15.06.2026", "Odometru final": "", "Operatie": "OP-1"},
|
|
]
|
|
data = _csv_bytes(rows)
|
|
|
|
# Upload -> formular mapare
|
|
r = client.post("/_import/upload", files={"file": ("test.csv", data, "text/csv")})
|
|
assert r.status_code == 200
|
|
m = re.search(r"/_import/(\d+)/mapare-coloane", r.text)
|
|
assert m, "Nu am gasit import_id in formularul de mapare"
|
|
import_id = int(m.group(1))
|
|
|
|
# Salveaza maparea -> preview
|
|
r = client.post(f"/_import/{import_id}/mapare-coloane", data={
|
|
"colname": ["VIN", "Nr inmatriculare", "Data prestatie", "Odometru final", "Operatie"],
|
|
"canon": ["vin", "nr_inmatriculare", "data_prestatie", "odometru_final", "operatie"],
|
|
"format_data": "DD.MM.YYYY",
|
|
})
|
|
assert r.status_code == 200
|
|
html = r.text
|
|
|
|
# Trebuie sa apara starea needs_data si MOTIVUL (mesajul de validare odometru)
|
|
assert "needs_data" in html, "Randul fara odometru trebuia marcat needs_data"
|
|
assert "odometruFinal" in html, (
|
|
"Preview-ul nu arata motivul (mesajul de validare) pentru randul fara odometru"
|
|
)
|