prd 3.6
This commit is contained in:
543
docs/prd/prd-3.6-editare-preview-acasa-unificata.md
Normal file
543
docs/prd/prd-3.6-editare-preview-acasa-unificata.md
Normal file
@@ -0,0 +1,543 @@
|
||||
<!-- /autoplan restore point: /home/claude/.gstack/projects/romfast-rar-autopass/main-autoplan-restore-20260619-093652.md -->
|
||||
# PRD 3.6 — Editare celule in preview + Acasa unificata (Trimiteri inline, upload slim, Mapari tabelar)
|
||||
|
||||
**Stare**: aprobat (post-autoplan 2026-06-19 — raport de review la finalul fisierului; stories revizuite cu fix-urile aplicate)
|
||||
|
||||
> Proces complet: `docs/ROADMAP.md` §5. Contract RAR (sursa de adevar): `docs/api-rar-contract.md`.
|
||||
> Starea trece: `draft → aprobat → in-executie → verify-pass → inchis` (actualizata de lead).
|
||||
> Continua 3.5 ([prd-3.5](prd-3.5-dashboard-compact-trimiteri-mapari.md)). **Backend trimitere (worker,
|
||||
> masina stari, idempotenta-logica, mapping-rezolvare) NU se atinge** — doar canalul de import si stratul web.
|
||||
> Exceptie schema decisa la poarta autoplan: UNA coloana nullable `import_rows.override_json` (Approach B), cu
|
||||
> migrare defensiva `_migrate` ca la 3.3b/3.5. Worker/idempotenta/mapare raman neatinse.
|
||||
|
||||
## 1. Obiectiv
|
||||
|
||||
Dupa upload, utilizatorul putea citi randurile cu probleme dar nu le putea **corecta inainte de trimitere** —
|
||||
singura editare de continut era post-confirmare, in alt tab. Livram: (a) editare de celule direct in preview
|
||||
(buton "Editeaza" pe rand), si (b) reunificarea fluxului pe pagina **Acasa** — Trimiterile devin o sectiune
|
||||
permanenta sub upload (tab-ul "Trimiteri" dispare), zona de upload se comprima la o bara slim, iar "Mapari"
|
||||
trece pe format tabelar compact cu eticheta auto-send reformulata ca un comutator Automat/Manual.
|
||||
|
||||
Motivatie: microcopia spune "vezi mai jos trimiterile", dar trimiterile erau intr-un alt tab; cazul real de
|
||||
utilizare (fisier cu o data/VIN lipsa sau gresit) cerea re-upload in loc de o corectie pe loc.
|
||||
|
||||
## 2. Non-Goals (anti scope-creep)
|
||||
|
||||
- **Fara editare a operatiei/codului RAR in celula** — codul de operatie ramane rezolvat prin panoul de mapare
|
||||
existent (`needs_mapping`). Editarea de celule acopera campurile de continut: VIN, nr. inmatriculare,
|
||||
data prestatie, odometru initial/final. (Operatia se schimba din panoul de mapare, nu din tabel.)
|
||||
- **Fara editare in bloc / multi-rand** — un rand pe rand, mod editare explicit.
|
||||
- **Schema: o singura coloana noua** — `import_rows.override_json` (nullable, criptat Fernet), patch CANONIC peste
|
||||
randul mapat (Approach B). NU se modifica `raw_json` (ramane cheiat pe anteturile fisierului). Restul schemei neatins.
|
||||
- **Fara atingerea logicii de idempotenta, validare, mapare sau a worker-ului** — editarea = mutatie pura de stocare;
|
||||
recalculul de stare merge OBLIGATORIU prin `_resolve_row_for_preview` (un singur clasificator, fara drift).
|
||||
- **Fara reorganizarea tab-urilor ramase** (Mapari/Cont/Nomenclator) dincolo de scoaterea tab-ului Trimiteri.
|
||||
- **Fara paginare noua pe Trimiteri** — sectiunea de pe Acasa refoloseste filtrarea existenta (US-009 din 3.5).
|
||||
|
||||
## 3. Stories atomice
|
||||
|
||||
> Backend + UI pentru acelasi comportament = stories separate. Toate rutele web noi sunt sub `require_login`
|
||||
> si **scoped pe contul din sesiune** (gard cross-account 404, identic cu rutele existente). CSRF pe toate POST-urile.
|
||||
|
||||
### US-001: Backend — persista editarea unui rand de preview
|
||||
**Ca** utilizator care vede un rand cu date gresite/lipsa in preview **vreau** sa salvez valori corectate
|
||||
pentru acel rand **pentru ca** sa-l trimit fara re-upload de fisier.
|
||||
|
||||
- **Depinde de**: —
|
||||
- **Fisiere**: `app/schema.sql` (coloana `override_json` + `_migrate`), `app/api/v1/import_router.py`
|
||||
(`_resolve_row_for_preview` + `commit_import` aplica override; ruta noua), `tests/test_import_edit_row.py`,
|
||||
`tests/fixtures/import_antet_necanonic.csv` (NOU), `tests/fixtures/import_lipsa_coloana.csv` (NOU) (~5 fisiere)
|
||||
- **Stocare (Approach B, decis la poarta autoplan):** editarea scrie un dict CANONIC in `import_rows.override_json`
|
||||
(nullable, criptat Fernet). `_resolve_row_for_preview` si `commit_import` aplica `mapped.update(override_json)`
|
||||
ULTIMUL, dupa maparea `json_mapare`. Astfel se poate completa si un camp a carui coloana LIPSESTE din fisier
|
||||
(cazul "completez informatii lipsa"), iar `raw_json`/idempotency raman neatinse.
|
||||
- **Test intai (RED)**: `tests/test_import_edit_row.py` —
|
||||
`test_editeaza_rand_antet_necanonic_devine_ok` (fixture cu antet `Serie sasiu`/`Data` — prinde bug-ul de stocare),
|
||||
`test_editeaza_completeaza_coloana_absenta` (fisier fara coloana data → editarea adauga data → ok),
|
||||
`test_editeaza_status_identic_cu_GET_preview` (ruta editare NU re-deriva status; egal cu `GET /preview`),
|
||||
`test_editeaza_rand_scoped_alt_cont_404`, `test_editeaza_batch_inexistent_404`,
|
||||
`test_editeaza_row_index_invalid_pe_batch_valid_404`,
|
||||
`test_editeaza_pastreaza_campuri_neatinse` (operatie/prestatii raman),
|
||||
`test_editeaza_batch_committed_409` (guard post-commit),
|
||||
`test_editeaza_raw_corupt_no_op` (decrypt fail → 422/no-op, fara crash),
|
||||
`test_editeaza_empty_input_sterge_campul` (semantica empty = CLEAR, pentru cazul "corectez o valoare gresita").
|
||||
- **Acceptance criteria**:
|
||||
- [ ] Migrare: `import_rows.override_json TEXT` (nullable), `_migrate` defensiv (idempotent, ca `is_admin` in 3.3b).
|
||||
- [ ] Ruta `POST /v1/import/{import_id}/rand/{row_index}/editeaza` (`resolve_account_id`) + alias web
|
||||
`POST /_import/{import_id}/rand/{row_index}/editeaza` (`require_login`). Campuri: `vin`, `nr_inmatriculare`,
|
||||
`data_prestatie`, `odometru_initial`, `odometru_final`.
|
||||
- [ ] **Mutatie pura**: decripteaza `override_json` curent (sau {}), aplica campurile (vezi semantica empty), re-cripteaza,
|
||||
`UPDATE`. NU recalculeaza statusul in ruta — preview-ul il rederiva via `_resolve_row_for_preview`.
|
||||
- [ ] **Semantica empty**: input gol = STERGE cheia din override (revine la valoarea din fisier daca exista). Documentat + testat.
|
||||
- [ ] **Scoping intr-o singura interogare**: `import_rows r JOIN import_batches b ON b.id=r.batch_id
|
||||
WHERE b.id=? AND b.account_id=? AND r.row_index=?` → 404 pe gol (acopera alt cont, batch inexistent, row_index invalid).
|
||||
- [ ] **Guard committed**: batch cu `status='committed'` → 409 (editarea n-ar avea efect downstream).
|
||||
- [ ] `decrypt_creds` → None/exceptie → 422/no-op defensiv (ca import_router.py:602-606), niciodata scriere goala.
|
||||
- [ ] Coercion: nu se afirma `canonicalize_row` pe `odometru_initial` (normeaza doar `_final`); validarea (`_parse_int`)
|
||||
tolereaza ".0" — testul verifica prin validare, nu prin canonicalize.
|
||||
- [ ] NU atinge `submissions`.
|
||||
- **Verificare E2E**: TestClient — (a) fixture cu antet ne-canonic, rand needs_data → editeaza → preview = `ok`;
|
||||
(b) fixture fara coloana data → editeaza data → `ok`.
|
||||
|
||||
### US-002: UI — buton "Editeaza" pe rand in tabelul de preview
|
||||
**Ca** utilizator **vreau** sa pun un rand in mod editare si sa-i corectez celulele pe loc **pentru ca**
|
||||
sa nu reincarc tot fisierul pentru o singura valoare.
|
||||
|
||||
- **Depinde de**: US-001
|
||||
- **Fisiere**: `app/web/templates/_preview_import.html`, `app/web/templates/_preview_rand.html` (NOU — fragment rand),
|
||||
`app/web/routes.py` (handler fragment rand), `tests/test_preview_edit_ui.py`
|
||||
- **Test intai (RED)**: `tests/test_preview_edit_ui.py` —
|
||||
`test_preview_are_buton_editeaza_pe_rand`,
|
||||
`test_editeaza_intra_in_mod_editare_form_propriu` (randul devine FORM separat, NU in `#confirm-form`),
|
||||
`test_salveaza_reda_doar_randul` (raspuns = fragment rand + OOB contoare, NU tot `#import-section`),
|
||||
`test_enter_in_camp_editare_nu_declanseaza_confirm`,
|
||||
`test_eroare_validare_pastreaza_valorile_introduse` (data invalida → ramane in editare, mesaj pe camp).
|
||||
- **Acceptance criteria**:
|
||||
- [ ] Fiecare rand are un buton "Editeaza" (coloana de actiuni la final).
|
||||
- [ ] **Swap pe rand, NU pe sectiune (D-3.1):** Editeaza/Salveaza tintesc randul `<tr>` (`hx-target` pe rand,
|
||||
`hx-swap="outerHTML"`), iar rezumatul + bara de confirmare se actualizeaza prin **OOB swap**. NU se re-randeaza
|
||||
`#import-section` (altfel se pierd bifele `reviewed_rows`, `n_confirmat`, filtrul activ, alte randuri in editare).
|
||||
- [ ] **Form propriu (D-3.3):** input-urile de editare stau intr-un `<form>` separat (sau `form=` attribute),
|
||||
NU in `#confirm-form`. Butonul "Trimite la RAR" e DEZACTIVAT cat un rand e in mod editare. Enter intr-un camp
|
||||
de editare salveaza randul, niciodata nu declanseaza confirmarea (ireversibila).
|
||||
- [ ] **Refolosire `_trimitere_detaliu.html` (DRY, rezolva mobil+eroare):** modul de editare foloseste aceeasi grila
|
||||
responsiva `repeat(auto-fit, minmax(200px,1fr))` + error-map + scroll-to-row ca formul de corectie existent.
|
||||
Pe viewport ingust randul devine card stacked (label deasupra input), nu celule in scroll orizontal.
|
||||
- [ ] **Stari (D-2.1/D-2.2):** `hx-indicator` "se salveaza…" pe rand + butoane dezactivate in timpul cererii;
|
||||
`hx-on::response-error` pastreaza randul + valorile introduse + banner ne-distructiv la 500/CSRF/timeout.
|
||||
- [ ] **Mutual-exclusion (D-3.2/D-3.6):** cat un rand e in editare, butoanele Editeaza ale celorlalte randuri sunt
|
||||
dezactivate; "Incarca alt fisier" / schimbarea de tab cu editare dirty cer confirmare (sau auto-cancel cu toast).
|
||||
- [ ] La eroare de validare a valorii, randul ramane in editare cu mesajul pe campul vinovat (tipar `corectie_errors`).
|
||||
- [ ] Dupa Salveaza: scroll la + evidentiaza randul editat (reuse scriptul de outline din `_trimitere_detaliu.html`).
|
||||
- [ ] "Anuleaza" inchide editarea fara scriere. Accesibil: butoane min 44px, `aria-label` per camp cu nr. rand + VIN.
|
||||
- **Verificare E2E**: browser HTMX pe `/` (Playwright MCP) — upload fixture cu un rand fara data → Editeaza →
|
||||
completeaza data → Salveaza → DOAR randul se schimba pe `ok`, contorul "gata de trimis" creste, bifele altui rand raman.
|
||||
|
||||
### US-003: Acasa unificata — Trimiteri ca sectiune permanenta, fara tab "Trimiteri"
|
||||
**Ca** utilizator **vreau** sa vad trimiterile pe aceeasi pagina cu upload-ul **pentru ca** "vezi mai jos
|
||||
trimiterile" sa fie adevarat si sa nu navighez intre tab-uri.
|
||||
|
||||
- **Depinde de**: —
|
||||
- **Fisiere**: `app/web/templates/dashboard.html`, `app/web/templates/_acasa.html`, `app/web/routes.py`,
|
||||
`tests/test_acasa_trimiteri.py`
|
||||
- **Test intai (RED)**: `tests/test_acasa_trimiteri.py` —
|
||||
`test_tab_bar_fara_trimiteri`,
|
||||
`test_acasa_contine_sectiunea_trimiteri` (tabel + filtre),
|
||||
`test_tab_coada_redirect_la_acasa` (`?tab=coada` nu da 404, serveste Acasa),
|
||||
`test_acasa_fara_linkuri_ajutor` (randul de linkuri catre tab-uri eliminat),
|
||||
`test_badge_trimiteri_scoped_pe_acasa`.
|
||||
- **Test intai (RED)** (in plus): `test_fragment_coada_serveste_acasa` (fragmentul, nu doar pagina),
|
||||
`test_sectiune_trimiteri_are_heading` ("Trimiterile tale"), `test_acasa_pastreaza_wayfinding_mapari_coduri`.
|
||||
- **Acceptance criteria**:
|
||||
- [ ] Tab-ul "Trimiteri" (`coada`) eliminat din `tab-bar`; raman Acasa·Mapari·Cont·Nomenclator.
|
||||
- [ ] `_acasa.html` randeaza, sub upload + primii pasi, sectiunea Trimiteri completa cu heading **"Trimiterile tale"**
|
||||
(divizor vizual fata de upload): filtrele (US-009), tabelul (`_submissions.html`), panoul `#trimitere-detaliu`.
|
||||
- [ ] **Un singur sticky bar pe ecran (D-1.2):** cat un preview de import e activ (`#import-section` randat),
|
||||
sectiunea Trimiteri e ascunsa/colapsata; dupa commit, se reveleaza si scroll la ea. Nu coexista doua bare sticky.
|
||||
- [ ] **Wayfinding pastrat (D-5.3):** se scoate DOAR linkul redundant "Trimiteri"; "Mapari" si "Coduri RAR" raman
|
||||
ca o linie de ajutor discreta (utile pentru operatori non-tehnici).
|
||||
- [ ] `GET /?tab=coada` **si** `GET /_fragments/coada` nu dau 404 — ambele servesc continutul Acasa (fragmentul nu mai
|
||||
randeaza `_coada.html` orfan; `_coada.html` sters/repurposat).
|
||||
- [ ] **Anti dublu-poll (M5):** poll-ul de trimiteri din sectiune are `hx-trigger` gated pe
|
||||
`document.visibilityState==='visible'` (sau aliniat la 15s ca status-ul) — nu doua timere perpetue pe pagina mereu deschisa.
|
||||
- [ ] Contorul de atentie (badge) se reflecta in heading-ul sectiunii, nu pe un tab disparut.
|
||||
- [ ] Microcopia post-confirmare tinteste sectiunea "Trimiterile tale" de pe Acasa.
|
||||
- **Verificare E2E**: browser pe `/` — dupa confirmarea unui import, trimiterile apar in aceeasi pagina sub upload, cu heading;
|
||||
click pe rand blocat deschide corectia inline; `?tab=coada` si `/_fragments/coada` servesc Acasa.
|
||||
|
||||
### US-004: UI — zona de upload comprimata la o bara slim
|
||||
**Ca** utilizator **vreau** un upload care ocupa putin spatiu **pentru ca** trimiterile de sub el sa fie vizibile fara scroll.
|
||||
|
||||
- **Depinde de**: US-003 (pentru a avea ce sta sub bara)
|
||||
- **Fisiere**: `app/web/templates/_upload.html`, `tests/test_upload_slim.py`
|
||||
- **Test intai (RED)**: `tests/test_upload_slim.py` —
|
||||
`test_upload_slim_pe_un_rand` (markup compact, fara caseta mare drop-zone dominanta),
|
||||
`test_upload_pastreaza_drag_drop_si_input` (input file + handler drag-drop raman),
|
||||
`test_upload_pastreaza_select_foaie` (cazul multi-sheet inca functioneaza).
|
||||
- **Acceptance criteria**:
|
||||
- [ ] Upload-ul devine o bara pe un rand: eticheta "Importa:" + buton "Alege fisier (xlsx/csv)" + zona "sau trage aici",
|
||||
microcopy scurt ("NU se trimite nimic pana confirmi" pastrat, dar discret).
|
||||
- [ ] **Slim DAR accentuat (D-1.1/D-5.2):** bara pastreaza un tratament distinct (border/fundal de accent) ca sa ramana
|
||||
punctul de intrare evident chiar cu un tabel lung dedesubt.
|
||||
- [ ] **First-run pastreaza hero (D-5.1):** daca `not are_trimiteri`, bara ramane usor mai inalta cu copy-ul
|
||||
"Primul fisier? Trage-l aici."; se colapseaza la slim doar dupa ce contul are trimiteri. Empty-state-ul redundant
|
||||
al tabelului Trimiteri se suprima cand sunt zero trimiteri (bara de upload acopera deja CTA-ul).
|
||||
- [ ] Drag-drop pe bara si `input[type=file]` ascuns raman functionale (JS-ul existent refolosit).
|
||||
- [ ] Cazul multi-sheet (select foaie) inca apare cand fisierul are mai multe foi.
|
||||
- **Verificare E2E**: browser pe `/` — bara de upload ocupa ~1 rand cand exista trimiteri; first-run pastreaza hero-ul.
|
||||
|
||||
### US-005: UI — Mapari "De rezolvat" + "Operatii salvate" ca tabel
|
||||
**Ca** utilizator cu multe mapari **vreau** sa le vad tabelar **pentru ca** stiva de carduri/forme ocupa prea mult loc.
|
||||
|
||||
- **Depinde de**: —
|
||||
- **Fisiere**: `app/web/templates/_mapari.html`, `tests/test_mapari_tabel.py`
|
||||
- **Test intai (RED)**: `tests/test_mapari_tabel.py` —
|
||||
`test_mapari_de_rezolvat_in_tabel`,
|
||||
`test_mapari_salvate_in_tabel`,
|
||||
`test_mapari_salvare_si_stergere_inca_functioneaza` (POST-urile `/mapari`, `/mapari/salvate`, `/mapari/salvate/sterge` neschimbate).
|
||||
- **Acceptance criteria**:
|
||||
- [ ] Sectiunea "De rezolvat" (operatii `needs_mapping`) randata ca tabel: coloane operatie/denumire + nr. blocate,
|
||||
sugestii, select cod RAR, comutator (US-007), actiune Salveaza — un rand de tabel per operatie.
|
||||
- [ ] Sectiunea "Mapari operatii salvate" randata ca tabel cu aceleasi coloane + Sterge.
|
||||
- [ ] **Starea stocata redata (H4):** comutatorul (US-007) din tabelul salvate reflecta valoarea `auto_send` STOCATA
|
||||
per mapare (din `_load_saved_op_mappings`, routes.py:738), nu un default hard "Automat".
|
||||
- [ ] Comportamentul existent neschimbat: re-rezolvare automata a blocatelor la salvare/edit cod; endpoints identice; csrf.
|
||||
- [ ] Tabelele folosesc `.tablewrap` (scroll orizontal pe mobil) ca Trimiteri.
|
||||
- **Verificare E2E**: browser pe tab Mapari — operatiile nemapate si cele salvate apar ca tabele; salvare + stergere OK.
|
||||
|
||||
### US-006: UI — Formate de coloane ca tabel
|
||||
**Ca** utilizator **vreau** formatele de coloane salvate ca tabel **pentru ca** sa le compar dintr-o privire.
|
||||
|
||||
- **Depinde de**: —
|
||||
- **Fisiere**: `app/web/templates/_mapari.html`, `tests/test_formate_tabel.py`
|
||||
- **Test intai (RED)**: `tests/test_formate_tabel.py` —
|
||||
`test_formate_coloane_in_tabel`,
|
||||
`test_formate_editare_data_si_stergere_inca_functioneaza`.
|
||||
- **Acceptance criteria**:
|
||||
- [ ] Sectiunea "Formate de coloane salvate" randata ca tabel: nr. coloane / maparile col→camp / format data (editabil) / Sterge.
|
||||
- [ ] POST-urile `/formate-coloane/editeaza` si `/formate-coloane/sterge` neschimbate; csrf pastrat.
|
||||
- [ ] `.tablewrap` pentru consistenta cu celelalte tabele.
|
||||
- **Verificare E2E**: browser pe tab Mapari — formatele apar ca tabel; editarea formatului de data + stergerea functioneaza.
|
||||
|
||||
### US-007: UI — comutator pe COADA in loc de bifa "auto-send" (framing sigur, nu "Manual/trimitere")
|
||||
**Ca** operator **vreau** sa stiu clar ce se intampla cu o operatie la fisierele viitoare **pentru ca**
|
||||
"auto-send" e jargon, iar "Manual/Automat" suna ca si cum sistemul ar trimite singur la RAR (fals — periculos).
|
||||
|
||||
> **Decis la poarta autoplan (UC-A):** framing pe **punerea in coada**, NU pe trimitere. Toti reviewerii (CEO/Eng/Design):
|
||||
> "Automat/Manual" citit global peste declaratii ireversibile = risc de send-safety. Etichetele poarta singure sensul.
|
||||
|
||||
- **Depinde de**: US-005 (acelasi markup de mapari)
|
||||
- **Fisiere**: `app/web/templates/_mapari.html`, `app/web/templates/_preview_import.html`, `tests/test_autosend_toggle.py`
|
||||
- **Test intai (RED)**: `tests/test_autosend_toggle.py` —
|
||||
`test_comutator_coada_prezent` (textul contine "in coada" / "verificare", NU "trimite"/"Manual" gol),
|
||||
`test_eticheta_scoped_pe_operatie` (microcopy contine "aceasta operatie"),
|
||||
`test_pune_automat_mapeaza_auto_send_true`,
|
||||
`test_tine_pentru_verificare_mapeaza_auto_send_false`,
|
||||
`test_default_pune_automat` (comportament identic cu `checked` de azi).
|
||||
- **Acceptance criteria**:
|
||||
- [ ] Bifa `auto_send` inlocuita cu un comutator cu doua stari, etichetat pe COADA, in: panoul de mapare din preview
|
||||
(`_preview_import.html`) si ambele locuri din `_mapari.html`. Antet: **"La fisierele viitoare cu aceasta operatie:"**
|
||||
Optiuni: **"Pune automat in coada"** / **"Tine pentru verificare"**.
|
||||
- [ ] Microcopy scoped pe operatie (NU global): "...doar pentru aceasta operatie; nimic nu pleaca la RAR pana confirmi."
|
||||
Niciun cuvant "Manual"/"trimite" izolat care sa implice bypass al confirmarii RAR. Caption prezent si in preview
|
||||
(azi checkbox-ul din preview nu are caption).
|
||||
- [ ] Maparea valoare→backend pastreaza semantica `auto_send` existenta ("Pune automat"=true, "Tine"=false);
|
||||
default **"Pune automat in coada"** (mirror la `checked` de azi).
|
||||
- [ ] **`name="auto_send"` pastrat** cu `value`-uri ce produc bool corect — zero atingere backend (cale aleasa).
|
||||
- **Verificare E2E**: browser — salvez o operatie pe "Tine pentru verificare" → la urmatorul import randul cu acea operatie
|
||||
ramane blocat (nu intra automat in coada); pe "Pune automat in coada" → intra in coada (tot cu gate-ul de confirmare la trimitere).
|
||||
|
||||
## 4. Riscuri
|
||||
|
||||
- **Stocare editare (REZOLVAT prin Approach B)**: `raw_json` e cheiat pe anteturile fisierului; o editare pe cheie
|
||||
canonica ar fi ignorata pe fisiere cu antet ne-canonic, iar un camp fara coloana-sursa n-ar putea fi completat.
|
||||
Mitigare: `override_json` (patch canonic aplicat ultimul). Test obligatoriu cu fixture antet ne-canonic + coloana absenta.
|
||||
- **Swap pe rand vs sectiune (REZOLVAT)**: editarea tinteste randul + OOB contoare, NU `#import-section`; form propriu;
|
||||
confirm dezactivat la editare. Previne pierderea starii si Enter→trimitere ireversibila.
|
||||
- **Dublu-poll pe Acasa**: status 15s + trimiteri 10s pe pagina mereu deschisa. Mitigare: poll-ul de trimiteri gated pe
|
||||
`document.visibilityState` (sau 15s). Nu doua timere perpetue.
|
||||
- **`?tab=coada` + `/_fragments/coada` vechi**: fallback la continutul Acasa (nu 404, nu fragment orfan).
|
||||
- **Export "randuri cu probleme"**: trebuie sa reflecte valorile editate. Recalculul prin `_resolve_row_for_preview`
|
||||
(cu override aplicat) => export consistent automat.
|
||||
- **Recalcul preview pe fisier mare**: cu swap pe rand, doar randul + contoarele se re-randeaza (nu toate 5000).
|
||||
|
||||
## 5. Intrebari deschise
|
||||
|
||||
> Rezolvate cu utilizatorul inainte de executie (poarta de aprobare PRD). Toate cele de mai jos sunt **inchise**
|
||||
> prin sesiunea de planificare (AskUserQuestion 2026-06-19):
|
||||
- Editare preview: **buton "Editeaza" pe rand** (mod editare explicit pe rand), nu click-pe-celula. [INCHIS]
|
||||
- Trimiteri pe Acasa: **mutare completa**, tab-ul "Trimiteri" eliminat (linkul redundant "Trimiteri" scos;
|
||||
wayfinding "Mapari"/"Coduri RAR" pastrat). [INCHIS]
|
||||
- Upload: **bara slim pe un rand**, accentuata + hero pastrat la first-run. [INCHIS]
|
||||
- auto-send: framing pe **coada** ("Pune automat in coada" / "Tine pentru verificare"), NU "Automat/Manual" global. [INCHIS — poarta autoplan UC-A]
|
||||
- Stocare editare: **Approach B** (`override_json`), relaxeaza Non-Goal-ul de schema. [INCHIS — poarta autoplan]
|
||||
- Structura: **un singur PRD 3.6, valuri secventiate** (Acasa intai, editare dupa storage). [INCHIS — poarta autoplan]
|
||||
|
||||
## 6. Valuri de executie (secventiate post-autoplan)
|
||||
|
||||
```
|
||||
Val 1 (Acasa unificata — livreaza 80% din valoare): [US-003] [US-004]
|
||||
Val 2 (Editare preview — dupa storage redesign B): [US-001] → [US-002]
|
||||
Val 3 (Cosmetic mapari + toggle sigur): [US-005] → [US-006] [US-007]
|
||||
```
|
||||
|
||||
> Secventiere ceruta la poarta autoplan: US-003 (+US-004 ca sub-task, ii face loc) intai — fixeaza microcopia care
|
||||
> minte azi. US-001/002 dupa ce `override_json` (Approach B) e in loc. US-005 si US-007 ating `_mapari.html`/`_preview_import.html`
|
||||
> impreuna — acelasi worker (sectiuni distincte). Regula de aur: la fiecare val, regresia `pytest -q` verde + bifare in PRD.
|
||||
|
||||
---
|
||||
|
||||
## Raport VERIFY
|
||||
|
||||
> Completat de subagentul verificator (context curat) in faza VERIFY — vezi ROADMAP §5.6.
|
||||
> PASS/FAIL per criteriu, cu dovezi (output pytest citat, E2E pe RAR test). Lipseste pana la VERIFY.
|
||||
|
||||
---
|
||||
|
||||
# AUTOPLAN REVIEW — APROBAT (2026-06-19)
|
||||
|
||||
> Pipeline /autoplan: CEO -> Design -> Eng. Mod: SELECTIVE EXPANSION. Vocile: codex `[indisponibil — usage limit]`
|
||||
> + subagent Claude independent per faza. 12 corecturi auto-decise (audit trail mai jos) + 3 decizii la poarta,
|
||||
> toate aprobate de user: (UC-A) US-007 framing pe coada; (poarta) Approach B `override_json`; (poarta) un PRD,
|
||||
> valuri secventiate. **Stories revizuite cu toate fix-urile** (sectiunile 2-6 de mai sus reflecta decizia finala).
|
||||
|
||||
## FAZA 1 — CEO (strategie & scope)
|
||||
|
||||
### 0A. Premise Challenge
|
||||
- **Premisa centrala** ("operatorii trebuie sa corecteze valori in preview, inainte de trimitere"):
|
||||
VALIDA si confirmata de durerea reala — azi un VIN/data lipsa cere re-upload de fisier intreg.
|
||||
- **Premisa de implementare** ("editarea = suprascriere `import_rows.raw_json`"): SUSPECTA.
|
||||
`raw_json` e cheiat pe **numele coloanelor din fisier** (antet original), nu pe campuri canonice;
|
||||
`_resolve_row_for_preview` (import_router.py:138-140) si commit-ul (import_router.py:922-945) re-aplica
|
||||
`json_mapare` peste raw_json. O editare care scrie cheia canonica `vin` e ignorata pe orice fisier al
|
||||
carui antet difera de numele canonic. Fixture-ul `tests/fixtures/test_data_mapping.csv` are din intamplare
|
||||
antet = nume canonice -> testele ar trece, realul ar esua tacit. (vezi Eng pentru fix.)
|
||||
- **Premisa "completez informatiile lipsa"**: cazul in care campul lipsa e o **coloana inexistenta in fisier**
|
||||
nu are `col_f` in care sa scrie -> raw_json nu poate exprima valoarea. Premisa promisa userului depaseste
|
||||
ce poate face stocarea actuala fara un strat de override. (escaladata la poarta — vezi UC-1.)
|
||||
- **Premisa "muta Trimiteri pe Acasa"**: VALIDA — microcopia "vezi mai jos trimiterile" deja minte azi.
|
||||
- **Premisa de impachetare** (4 schimbari intr-un PRD): cele 4 NU sunt cuplate tehnic. Singura legatura e
|
||||
"incap pe ecranul Acasa". Risc de scope: editarea celulelor (cu fix-ul de stocare) e de departe cea mai
|
||||
riscanta; restul sunt cosmetice. (vezi 0F + decizie de gust DG-1: split.)
|
||||
|
||||
### 0B. Existing Code Leverage (ce exista deja)
|
||||
| Sub-problema | Cod existent care o rezolva partial/total |
|
||||
|---|---|
|
||||
| Editare campuri de continut + re-validare + re-enqueue | **US-010 corectie inline** (`routes.py:583` `post_corectie_trimitere`, `_trimitere_detaliu.html:44-84`) — exact aceleasi campuri (vin/nr/data/odo), validare, detectie coliziune idempotency. **US-001/002 trebuie sa REFOLOSEASCA acest tipar, nu sa-l reconstruiasca.** |
|
||||
| Tabele compacte | `.tablewrap` + `_submissions.html` (deja tabel cu scroll orizontal) |
|
||||
| Sectiunea Trimiteri pe Acasa | `_submissions.html` + filtre US-009 + `#trimitere-detaliu` — se includ ca atare |
|
||||
| Upload drag-drop | JS din `_upload.html:74-107` — se pastreaza, doar markup-ul se comprima |
|
||||
| Re-randare preview cu stare recalculata | `web_preview_import` + `_resolve_row_for_preview` — editarea doar schimba sursa |
|
||||
|
||||
Concluzie: PRD-ul e in mare parte re-cablare a unor fluxuri existente. Singurul cod nou real e ruta de
|
||||
editare (US-001) + modul-editare pe rand (US-002). Bun pentru DRY, dar US-001 NU trebuie sa-si scrie propria
|
||||
logica de validare/idempotency — o are in `corectie`.
|
||||
|
||||
### 0C. Dream State
|
||||
```
|
||||
CURRENT THIS PLAN 12-MONTH IDEAL
|
||||
Import = ecran read-only; Editezi randul in preview; Import = foaie editabila live,
|
||||
Trimiteri in alt tab; --> Trimiteri sub upload pe Acasa; --> mapare AI (Etapa 4.1),
|
||||
corectie doar post-trimitere; un singur ecran operational o singura suprafata, zero re-upload
|
||||
```
|
||||
Planul muta sistemul CATRE idealul "o singura suprafata operationala". Aliniat.
|
||||
|
||||
### 0C-bis. Implementation Alternatives (pentru editarea de celule — nucleul riscant)
|
||||
```
|
||||
APPROACH A: Suprascriere raw_json cu reverse-map canonical->col_fisier
|
||||
Summary: ruta editare mapeaza campul canonic inapoi la coloana de fisier via json_mapare, scrie raw_json.
|
||||
Effort: M Risk: High
|
||||
Pros: fara schema noua (respecta Non-Goal); o singura sursa de adevar
|
||||
Cons: NU poate completa un camp a carui coloana lipseste din fisier (cazul "informatii lipsa" real);
|
||||
reverse-map ambiguu daca doua coloane mapeaza pe acelasi camp; fragil
|
||||
|
||||
APPROACH B: Coloana noua import_rows.override_json (patch canonic), aplicata ultima in resolver+commit
|
||||
Summary: editarea scrie un dict canonic de override; resolver si commit il suprapun peste mapped.
|
||||
Effort: M Risk: Low
|
||||
Pros: exprima ORICE camp, inclusiv cele absente din fisier; nu atinge semantica raw_json/idempotency;
|
||||
migrare defensiva _migrate ca la PRD-urile 3.3b/3.5
|
||||
Cons: incalca Non-Goal "fara modificari schema" (1 ALTER, mic); 2 puncte unde se aplica override-ul
|
||||
|
||||
APPROACH C: Fara editare in preview — extinde US-010 corectie sa fie accesibila si pre-commit
|
||||
Summary: confirma intai, apoi corecteaza in Trimiteri (fluxul existent), fara editare in preview.
|
||||
Effort: S Risk: Low
|
||||
Cons: contrazice decizia explicita a userului (editare IN preview); pune o trimitere "rea" in coada intai
|
||||
```
|
||||
**RECOMMENDATION: Approach B** — singurul care onoreaza promisiunea "completez informatii lipsa" (campuri
|
||||
absente din fisier), cu risc mic si fara sa atinga idempotency/worker. Costul: relaxarea Non-Goal-ului de schema
|
||||
(1 coloana nullable + migrare defensiva). -> poarta finala UC-1.
|
||||
|
||||
### 0D. Selective Expansion — complexity check
|
||||
- 7 stories, ~8 fisiere atinse, 0 clase/servicii noi. Sub pragul de smell (8 fisiere / 2 clase). OK.
|
||||
- Minimum care livreaza valoare: US-001+US-002 (editarea) SAU US-003 (Acasa unificata) — sunt independente.
|
||||
- Expansiuni candidate (cherry-pick, auto-decise spre DEFER per P3/P6): buton "repara toate needs_review",
|
||||
editare bulk, undo editare. Toate -> TODOS (nu in blast radius imediat).
|
||||
|
||||
### 0E. Temporal Interrogation
|
||||
- HOUR 1: userul importa, vede un rand needs_data, da Editeaza, completeaza data, Salveaza -> ok. Functioneaza
|
||||
DOAR daca stocarea editarii e corecta (Approach B). Cu Approach A + fisier cu antet ne-canonic: editarea pare
|
||||
ca se salveaza dar randul ramane needs_data -> incredere distrusa.
|
||||
- HOUR 6+: zeci de fisiere cu formate diverse de antet. Approach A esueaza pe majoritatea; Approach B tine.
|
||||
|
||||
### 0F. Mode Selection: **SELECTIVE EXPANSION** confirmat. Scope-ul de baza tinut; o singura expansiune ceruta
|
||||
(override_json), restul deferite. Decizie de structura (split 4-in-1) -> DG-1 la poarta.
|
||||
|
||||
### Step 0.5 — Voci duale CEO
|
||||
- **Codex (CLI)**: `[codex-unavailable: usage limit]` — a citit contextul dar a esuat la analiza pe limita de cont.
|
||||
- **Subagent Claude (independent)**: a confirmat din cod premisa centrala (`web_confirma_import` -> `_upload.html`
|
||||
cu "urmareste coada de mai jos", dar coada e in alt tab => microcopia minte azi). Findings principale:
|
||||
US-001 dubleaza motorul existent `post_corectie_trimitere` (routes.py:583-717); US-007 "Automat/Manual"
|
||||
risca interpretare gresita de send-safety (auto_send e per-operatie, nu global, peste declaratii ireversibile);
|
||||
US-004 e consecinta US-003, nu peer; Acasa ajunge sa ruleze DOUA poll-uri (status 15s + trimiteri ~10s);
|
||||
recomanda split (groful de valuri din PRD demonstreaza independenta).
|
||||
|
||||
```
|
||||
CEO DUAL VOICES — CONSENSUS TABLE (Codex N/A — subagent-only)
|
||||
═══════════════════════════════════════════════════════════════
|
||||
Dimensiune Claude Codex Consensus
|
||||
─────────────────────────────────── ─────── ─────── ─────────
|
||||
1. Premise valide? partial N/A DG: editare-in-staging vs reuse corectie
|
||||
2. Problema corecta? da N/A CONFIRMAT (US-003 e valoarea reala)
|
||||
3. Scope calibrat? nu N/A FLAG: 4-in-1, recomandare split (DG-1)
|
||||
4. Alternative explorate? nu N/A FLAG: reuse post_corectie_trimitere ne-analizat
|
||||
5. Riscuri (send-safety)? da N/A FLAG CRITIC: US-007 Automat/Manual (UC-2)
|
||||
6. Traiectorie 6 luni sound? partial N/A US-003 da; US-001 = tax de mentenanta daca dubleaza
|
||||
═══════════════════════════════════════════════════════════════
|
||||
Single critical din subagent (US-007 send-safety, US-001 DRY) = flagged regardless.
|
||||
```
|
||||
|
||||
### CEO Sections 1-10 (rezumat per sectiune — examinat, nu doar numit)
|
||||
1. **Strategic fit**: US-003 aliniat la idealul "o suprafata operationala"; restul cosmetic. OK.
|
||||
2. **Error & Rescue**: editarea poate introduce valori care re-blocheaza randul (data invalida) — trebuie eroare
|
||||
pe camp, nu pierdere de input (acoperit US-002 AC). Nicio cale de eroare tacuta noua daca se reda preview-ul.
|
||||
3. **DRY (CRITIC)**: US-001 vs `post_corectie_trimitere` — aceeasi validare/canonicalizare/idempotency. Reuse obligatoriu.
|
||||
4. **Send-safety (CRITIC)**: US-007 — "Manual" suna global peste un sistem cu declaratii ireversibile. Reformulare ceruta.
|
||||
5. **Observability**: editarea ar trebui sa lase un log (cine/cand a editat un rand de staging) — minor, GDPR-friendly.
|
||||
6. **Scope/impachetare**: cele 4 sunt independente (graf valuri). Recomandare split (DG-1).
|
||||
7. **Reuse UI**: `.tablewrap`, `_submissions.html`, filtre US-009, upload JS — reutilizate corect.
|
||||
8. **Page-weight**: dublu-poll pe Acasa (status 15s + trimiteri 10s) — de consolidat/confirmat (Eng#4).
|
||||
9. **Migrare deep-link**: `?tab=coada` trebuie sa nu dea 404 (acoperit US-003).
|
||||
10. **6-month**: US-001 fara reuse = doua locuri de intretinut; US-005/006 cosmetice = risc minim.
|
||||
|
||||
### Mandatory outputs — FAZA 1
|
||||
**NOT in scope (deferite la TODOS):** buton "repara toate needs_review", editare bulk multi-rand, undo editare,
|
||||
log de audit pe editare de staging. **Ce exista deja (refolosit):** `post_corectie_trimitere`, `.tablewrap`,
|
||||
`_submissions.html`+filtre US-009, upload JS, `_resolve_row_for_preview`. **Dream delta:** planul muta catre
|
||||
"o singura suprafata"; gap ramas = mapare AI (Etapa 4.1). **Failure modes:** (a) editare ignorata tacit pe fisier
|
||||
cu antet ne-canonic [CRITIC, Eng]; (b) camp lipsa fara coloana sursa nu poate fi completat [CRITIC]; (c) "Manual"
|
||||
interpretat global [CRITIC]; (d) dublu-poll pe pagina mereu deschisa [mediu].
|
||||
|
||||
> **FAZA 1 COMPLETE.** Codex: indisponibil (usage limit). Subagent Claude: 6 findings (2 critice).
|
||||
> Consensus: 1/6 confirmat clar, 4 flag-uri -> poarta. Trec la Faza 2 (Design).
|
||||
|
||||
## FAZA 3 — Eng (arhitectura, teste, securitate)
|
||||
|
||||
> Codex `[codex-unavailable: usage limit]`. Subagent Claude independent (context curat).
|
||||
|
||||
### Step 0.5 — Eng consensus
|
||||
```
|
||||
ENG DUAL VOICES — CONSENSUS TABLE (Codex N/A — subagent-only)
|
||||
═══════════════════════════════════════════════════════════════
|
||||
Dimensiune Claude Codex Consensus
|
||||
─────────────────────────────────── ─────── ─────── ─────────
|
||||
1. Arhitectura sound? nu N/A FLAG: US-001 stocare gresita (C1)
|
||||
2. Acoperire teste suficienta? nu N/A FLAG: fixture mascheaza bug; lipsesc anteturi ne-canonice
|
||||
3. Riscuri performanta? partial N/A FLAG: dublu-poll Acasa (M5)
|
||||
4. Securitate (scoping)? da N/A OK cu nota: query scoped JOIN (M7)
|
||||
5. Cai de eroare? nu N/A FLAG: decrypt fail, empty edit, batch committed (L9-L11)
|
||||
6. Risc deploy? da N/A OK: 1 ALTER defensiv (override_json) ca 3.3b/3.5
|
||||
═══════════════════════════════════════════════════════════════
|
||||
```
|
||||
|
||||
### Findings Eng (cu dovezi cod)
|
||||
- **C1 [CRITIC] — US-001 stocare gresita.** `raw_json` cheiat pe anteturi fisier; resolver+commit re-aplica
|
||||
`json_mapare` (import_router.py:138-140, 928-930). Scrierea cheii canonice e ignorata pe antet ne-canonic;
|
||||
camp fara coloana-sursa nu poate fi exprimat deloc. Fix = **Approach B** (`import_rows.override_json` nullable,
|
||||
Fernet, aplicat ULTIMUL in resolver + commit). Cere rescrierea AC US-001 (liniile actuale incoded Approach A)
|
||||
+ relaxare Non-Goal schema (1 ALTER + `_migrate` defensiv). -> poarta UC-1.
|
||||
- **H2 [HIGH] — DRY.** US-001 nu trebuie sa-si re-deriveze statusul; recalcul OBLIGATORIU prin `_resolve_row_for_preview`
|
||||
(altfel drift de clasificare vs preview, iar clasificarea controleaza send-ul). Ruta editare = mutatie pura de stocare.
|
||||
- **H3 [HIGH] — US-007 eticheta.** Scopeaza la operatie: "Aceasta operatie: Automat / Manual" + microcopy "pentru
|
||||
aceasta operatie". `name="auto_send"` pastrat (zero backend) e corect.
|
||||
- **H4 [MED] — US-005/007.** Tabelul "mapari salvate" trebuie sa randeze starea `auto_send` STOCATA (routes.py:738),
|
||||
nu sa hard-defaulteze Automat.
|
||||
- **M5 [MED] — dublu-poll Acasa.** status 15s (dashboard.html:14-17) + submissions 10s (_coada.html:43-45) pe pagina
|
||||
mereu deschisa. Fix: aliniaza submissions la 15s SAU `hx-trigger` gated pe `document.visibilityState==='visible'`.
|
||||
- **M6 [MED] — `/_fragments/coada`.** `?tab=coada` cade deja pe Acasa (routes.py:266), dar fragmentul `/_fragments/coada`
|
||||
(routes.py:309-313) inca randeaza `_coada.html` orfan. Fix: fragmentul redirectioneaza la continutul Acasa; test dedicat.
|
||||
- **M7 [MED] — scoping editare.** O singura interogare scoped JOIN `import_rows r JOIN import_batches b ... WHERE
|
||||
b.account_id=? AND r.row_index=?` -> 404 pe gol. Web alias sub `require_login`, `/v1/...` sub `resolve_account_id`.
|
||||
- **M8 [MED] — coercion.** `canonicalize_row` normeaza DOAR `odometru_final` (idempotency.py:44), nu `_initial`.
|
||||
Testul de coercion sa nu afirme strip prin canonicalize pe `odometru_initial`.
|
||||
- **L9-L12 [LOW/MED]:** test decrypt-fail (raw corupt -> 422/no-op); semantica empty-input (ignore vs clear — defineste
|
||||
explicit, cazul "completez" poate cere clear); guard editare pe batch `committed` (409, ca import_router.py:822-823);
|
||||
setup test panel preview (seed unmapped_op).
|
||||
|
||||
### Mandatory outputs — FAZA 3
|
||||
**Diagrama arhitectura (override_json, Approach B):**
|
||||
```
|
||||
upload ─▶ import_rows(raw_json[antet fisier], override_json[canonic]=NULL)
|
||||
│
|
||||
editare rand ─────────┘ scrie override_json[camp_canonic]=valoare (mutatie pura)
|
||||
│
|
||||
GET preview ─▶ _resolve_row_for_preview:
|
||||
mapped = json_mapare(raw_json) ◀── pas existent
|
||||
mapped.update(override_json) ◀── PAS NOU (aplicat ultimul)
|
||||
─▶ validate ─▶ status (ok/needs_data/...)
|
||||
│
|
||||
commit ─▶ acelasi merge override peste mapped ─▶ submissions
|
||||
```
|
||||
**Test diagram (coverage):** randuri noi de acoperit — (a) fisier antet ne-canonic editat -> ok [LIPSESTE azi];
|
||||
(b) camp fara coloana-sursa completat -> ok [LIPSESTE]; (c) edit == GET preview status [LIPSESTE]; (d) decrypt
|
||||
fail; (e) empty-input semantics; (f) batch committed -> 409; (g) scoped 404 alt cont; (h) `/_fragments/coada` fallback.
|
||||
**NOT in scope:** undo, bulk. **Ce exista:** `_resolve_row_for_preview`, `post_corectie_trimitere`, `_migrate`.
|
||||
**Failure modes critice:** C1 (acoperit de Approach B); fixture verde-dar-stricat (acoperit de fixture antet ne-canonic).
|
||||
|
||||
> **FAZA 3 COMPLETE.** Subagent: 1 critic, 3 high, 4 medium, 4 low. Trec la sinteza + poarta.
|
||||
|
||||
## FAZA 2 — Design (ierarhie, stari, interactiune)
|
||||
|
||||
> Codex `[codex-unavailable]`. Subagent Claude independent (a citit toate cele 8 template-uri).
|
||||
|
||||
### Design litmus scorecard (0-10)
|
||||
| Dimensiune | Scor | Nota |
|
||||
|---|---|---|
|
||||
| Ierarhie informatie | 5 | upload demotat la slim + tabel sub el => monitorizarea domina actiunea principala |
|
||||
| Stari (loading/error/partial) | 4 | lipsesc spinner save, eroare server, scroll-to-row, empty unificat |
|
||||
| Interactiune edit-mode | 3 | cea mai slaba zona — swap pe toata sectiunea distruge starea (CRITIC) |
|
||||
| Copy Automat/Manual | 4 | "Manual" citit global => contrazice promisiunea de siguranta |
|
||||
| First-run vs returning | 5 | first-run pierde hero "Primul fisier?", 3 nudge-uri redundante |
|
||||
|
||||
### Findings Design (severitate)
|
||||
- **D-3.1 [CRITIC] — swap distructiv.** `hx-target="#import-section"` + `outerHTML` la editare re-randeaza tot;
|
||||
pierde bife `reviewed_rows`, `n_confirmat`, filtrul activ, alte randuri in editare. Fix: swap DOAR randul `<tr>`
|
||||
+ OOB pe rezumat/contoare.
|
||||
- **D-3.3 [HIGH] — Enter = trimitere ireversibila.** Input-urile de editare in acelasi `#confirm-form` cu butonul
|
||||
"Trimite la RAR". Fix: randul de editare = FORM PROPRIU; dezactiveaza confirm cat un rand e in editare. Test:
|
||||
Enter in camp editare NU declanseaza confirm.
|
||||
- **D-2.1 [HIGH] — fara stare "se salveaza".** Re-randare completa fara `hx-indicator`/buton dezactivat. Fix: spinner pe rand.
|
||||
- **D-2.2 [HIGH] — eroare server = pierdere tacuta de date.** `outerHTML` pe sectiune la 500/CSRF/timeout poate goli
|
||||
`#import-section`. Fix: `hx-on::response-error` pastreaza randul + banner; test dedicat.
|
||||
- **D-3.4 [HIGH] — integritate N la editare.** Re-randarea reseteaza `n_confirmat`. Fix: blocheaza confirm la editare + recalcul autoritar.
|
||||
- **D-3.5 [HIGH] — mobil inutilizabil.** ~10 coloane cu input-uri in container cu scroll orizontal. Fix: pe viewport ingust,
|
||||
randul de editare = card stacked (refoloseste grila din `_trimitere_detaliu.html`).
|
||||
- **D-4.1 [HIGH] — "Automat/Manual" contrazice "nu se trimite nimic pana confirmi".** Fix: framing pe COADA, nu pe trimitere:
|
||||
"La fisierele viitoare cu aceasta operatie: [Pune automat in coada] / [Tine pentru verificare]". -> poarta UC-2.
|
||||
- **D-1.1/1.2 [HIGH/MED] — upload demotat + coexistenta preview vs Trimiteri.** Fix: upload slim DAR accentuat/distinct;
|
||||
ascunde Trimiteri cat un preview de import e activ (un singur sticky bar pe ecran); heading "Trimiterile tale".
|
||||
- **D-3.2/3.6/5.1/5.3 [MED/LOW]:** mutual-exclusion + debounce pe Editeaza; warn pe navigare cu editare dirty;
|
||||
first-run pastreaza hero "Primul fisier?" cand nu exista trimiteri (suprima empty-state redundant); pastreaza wayfinding Mapari/Coduri.
|
||||
- **Lauda:** refolosirea literala a markup-ului din `_trimitere_detaliu.html` (grila responsiva + error-map + scroll-to-row)
|
||||
rezolva D-2.2, D-3.5, D-2.5 gratis. -> AC HARD pe US-002.
|
||||
|
||||
```
|
||||
DESIGN CONSENSUS (Codex N/A — subagent-only): 1 critic, 7 high, 5 med, 3 low.
|
||||
Gate de design: D-3.1 + D-3.3 (swap distructiv + Enter-trimite) = problema de corectitudine/siguranta, nu rafinament.
|
||||
```
|
||||
|
||||
> **FAZA 2 COMPLETE.** Trec la sinteza cross-faza + poarta finala.
|
||||
|
||||
## SINTEZA CROSS-FAZA
|
||||
|
||||
**Teme care apar in 2+ faze (semnal de incredere mare):**
|
||||
1. **Refolosire `post_corectie_trimitere` / `_trimitere_detaliu.html`** — CEO (0B), Eng (H2), Design (lauda). Reuse = AC hard pe US-001/US-002.
|
||||
2. **US-007 "Automat/Manual" periculos** — CEO (UC-2), Eng (H3), Design (D-4.1). Reformulare ceruta. -> poarta UC-A.
|
||||
3. **Stocare editare gresita (C1)** — CEO (0A), Eng (C1). Approach B (override_json) + fixture cu antet ne-canonic.
|
||||
|
||||
## DECISION AUDIT TRAIL (auto-decise pe cele 6 principii)
|
||||
| # | Faza | Decizie | Clasificare | Principiu | Rationament |
|
||||
|---|---|---|---|---|---|
|
||||
| 1 | Eng | C1: adopta Approach B (override_json) in loc de raw_json | Auto | P1,P5 | Approach A e stricat (ignora editari pe antet ne-canonic; nu poate completa coloane absente) |
|
||||
| 2 | Eng | US-001 = mutatie pura; status recalculat prin `_resolve_row_for_preview` | Auto | P4 DRY | evita drift de clasificare care controleaza send-ul |
|
||||
| 3 | Design | Edit swap pe rand `<tr>` + OOB contoare, NU pe `#import-section` | Auto | P1,P5 | swap pe sectiune distruge starea neclickata |
|
||||
| 4 | Design | Rand editare = FORM propriu + confirm dezactivat la editare | Auto | P1 | previne Enter->trimitere ireversibila |
|
||||
| 5 | Design | Refolosire grila `_trimitere_detaliu.html` (responsiv + error-map + scroll) | Auto | P4 DRY | rezolva mobil+eroare+scroll gratis |
|
||||
| 6 | Design | Spinner save + `hx-on::response-error` pastreaza randul | Auto | P1 | fara pierdere tacuta de date |
|
||||
| 7 | Eng | M5: poll trimiteri gated pe `document.visibilityState` (sau 15s) | Auto | P3 | reduce load perpetuu pe pagina mereu deschisa |
|
||||
| 8 | Eng | M6: `/_fragments/coada` -> continut Acasa (nu doar `?tab=coada`) | Auto | P1 | evita fragment orfan din bookmark vechi |
|
||||
| 9 | Eng | M7: query scoped JOIN import_rows×import_batches -> 404 | Auto | P1 sec | fara scope-leak/TOCTOU |
|
||||
| 10 | Eng | M8: scoate afirmatia canonicalize pe odometru_initial | Auto | P5 | `canonicalize_row` normeaza doar `_final` |
|
||||
| 11 | Eng | L11: guard editare pe batch `committed` -> 409 | Auto | P1 | editare post-commit nu are efect downstream |
|
||||
| 12 | Design | First-run pastreaza hero "Primul fisier?"; slim accentuat; pastreaza wayfinding Mapari/Coduri | Auto | P1 | discoverability first-run |
|
||||
| UC-A | CEO/Design | US-007 reformulare labels (user a ales explicit "Automat/Manual") | USER CHALLENGE | — | toti reviewerii: risc send-safety. User decide. |
|
||||
| DG-1 | CEO | Split: ship US-003(+004) intai, US-001/002 dupa reuse, US-005/006 batch, US-007 redesign | Taste | P6 | cele 4 sunt independente (graf valuri) |
|
||||
5
tests/fixtures/test_data_mapping.csv
vendored
5
tests/fixtures/test_data_mapping.csv
vendored
@@ -1,6 +1,9 @@
|
||||
vin,nr_inmatriculare,data_prestatie,odometru_final,cod_op_service,denumire
|
||||
vin,nr_inmatriculare,data_prestatie,odometru_final,cod_op_service,denumire,odometru_initial
|
||||
1G1FB1S52D1234567,AB123CD,2026-06-10,45000,OP-MOTOR,Reparatie Motor
|
||||
1G1FB1S52D1234567,AB123CD,2026-06-10,45000,OP-REV,Revizie
|
||||
1G1FB1S52D1234567,AB123CD,2026-06-10,45000,OP-FRANE,Schimbare placute frane
|
||||
WVWZZZ3CZ9E123456,TM789BC,2026-05-15,82500,OP-ITP,Inspecție Tehnică Periodică
|
||||
2B1FB1S39C2345678,CJ456DE,2026-04-20,125000,OP-TURBO,Reparatie Turbo
|
||||
JTHBP5C20D5123456,OT567FG,2026-03-08,38000,OP-ULEI,Schimb Ulei
|
||||
5TDJZRFH2LS123456,VN678HI,2026-02-14,156000,OP-PNEU,Inlocuire Anvelope
|
||||
5TDJZRFH2LS123456,VN678HI,2026-02-14,156000,OP-ODO-NOU,Schimbare odometru
|
||||
|
Reference in New Issue
Block a user