Files
roafacturare/docs/cercetare/s8b_rutarea_scrierii.md
2026-09-09 22:19:22 +03:00

30 KiB

S8b — Proiectare: rutarea scrierii dupa ce utilizatorul a schimbat ceva

Livrabilul povestii S8b din docs\plan_13_unificare_formular_facturare.md:2890-2898 (G-bis, deciziile 6/9/25/26). Cercetare READ-ONLY — zero editari de cod, zero git_sync.ps1/txt2vcx.ps1, zero commit, zero scriere Oracle (doar SELECT, nefolosit efectiv — tot ce trebuia era in codul VFP/ PL-SQL deja exportat sau in rapoartele de referinta citate in briefing). COMUN\clase\ofacturare_comun.vc2 si COMUN\programe\ofacturare_editare.prg (perimetrul #6) doar citite, neatinse.

Verdict (rezumat, 8 randuri)

Reteta din G-bis e corecta ca directie, dar are un gol nedocumentat pana acum: modifica_date_factura nu e singura ruta care scrie cei 14 parametri de antet — 7 din 14 (id_delegat, id_masina, id_facturare, listare_detaliata, dataora_exp, id_agent, text_aditional) sunt scrisi si de calea normala de emitere (scrie_factura2, apelata direct de regenerare, decizia 35), pentru ca ambele cai citesc din acelasi obiect poDate, nu din doua reprezentari separate (sectiunea 3, dovada pe cod). Consecinta directa pentru punctul cel mai periculos al sarcinii (schimbare simultana antet+sume): cand regenerarea porneste, NU se mai cheama modifica_date_factura in plus — ar fi fie redundant (pe cele 7 campuri comune), fie periculos (ar scrie pe un ID_VANZARE care tocmai a fost soft-sters sau inca nu exista). Regenerarea este deja calea de scriere a antetului cand sumele se schimba, nu o cale separata care trebuie compusa cu ea. Explicatia de linie e acoperita direct de adauga_articol_factura (parametru V_EXPLICATIE, plus V_TAXCODE), deci regenerarea o transporta fara cod suplimentar — dar doar daca cursorul de linii incarcat la S8 e re-citit din formular, nu din snapshot-ul initial. Cel mai probabil loc de fals-pozitiv pentru „deschid si inchid fara sa modific nimic” e lookup-ul „ultimul delegat/masina al clientului” din frm_alte_date.Init (sectiunea 5) — cod construit pentru emiterea unui document nou, periculos daca ruleaza neschimbat pe calea de editare.


1. Mecanismul de detectare a schimbarii

1.1 Optiuni si ce se strica la fiecare

Optiune Ce se strica
Hash/checksum pe randuri Ascunde exact tipul de fals-pozitiv cel mai periculos aici: doua reprezentari numeric-egale dar text-diferite (Str(12.5,18,2) vs Str(12.50,18,2), .NULL. vs 0 vs "") produc hash-uri diferite desi valoarea „reala" e identica. Orice normalizare facuta inainte de hash trebuie sa fie deja perfecta — hash-ul nu adauga nimic, doar ascunde bug-urile de normalizare in loc sa le arate.
Flag-uri lModificat in evenimentele de editare E robust doar daca fiecare eveniment care poate schimba o valoare seteaza flag-ul — un ControlSource legat direct (binding automat VFP, cazul majoritatii campurilor de antet aici, vezi modifica_date_factura_parametri.md §4) nu trece printr-un eveniment scriptat, deci flag-ul ramane .F. desi valoarea s-a schimbat. Whitelist fragil: la fiecare control nou adaugat, cineva trebuie sa-si aminteasca sa cablasje flag-ul. Cel mai riscant pentru campurile din frm_alte_date (S3b), unde ControlSource=poDate.xxx e tiparul dominant.
Snapshot la incarcare + comparatie camp cu camp la confirmare (recomandat) Cere disciplina la normalizare (precizie, .NULL.), dar e singura optiune care nu poate rata o schimbare structurala — compara starea finala, nu istoricul de evenimente, deci un binding automat care a schimbat o valoare fara eveniment scriptat tot apare in diff. E si optiunea deja folosita implicit in codebase pentru un caz inrudit: chkSerieAct.Enabled = !EMPTY(NVL(poRec.numar_act,0)) (ofacturare_comun.vc2:5736) testeaza starea curenta, nu un istoric de evenimente.

Recomandare: snapshot + comparatie la confirmare, din motivul de mai sus (robustete la binding automat) — argumentat, nu doar preferat.

1.2 Ce se snapshoteaza, concret

  • Antet: o copie profunda a lui poDate (sau a subsetului de proprietati relevante) luata imediat dupa S8 (incarcare), inainte de orice lookup auto-completat (vezi sectiunea 5 — ordinea conteaza: daca lookup-ul „ultimul delegat" ruleaza inainte de snapshot, snapshot-ul insusi e deja poluat, si orice comparatie ulterioara devine inutila).
  • Linii: o copie a cursorului crsfactura (nume alias de confirmat la implementare — S4d il citeaza ca atare) imediat dupa populare la S8, cu cheia de linie id_vanzare_det (coloana deja incarcata la S8 din VANZARI_DETALII, cf. plan S8: „explicatia si taxcode pe fiecare linie" — nu exista alt candidat de cheie stabila in materialele citite; de confirmat exact numele coloanei in cursor la implementare, nu presupus mai departe aici).
  • Discountul de document (VANZARI.DISCOUNT, discount_evidentiat) — campuri de antet cu efect de suma, tratate separat de cele 14 (vezi matricea, sectiunea 2).

1.3 Capcanele reale de VFP, cu tratament explicit

Capcana Tratament recomandat
.NULL. vs 0 vs "" Comparatie prin functie de normalizare unica (NVL-style) aplicata simetric pe ambele parti (snapshot si curent) inainte de =, nu doar pe una — o comparatie Nvl(nou,0) == vechi fara acelasi tratament pe vechi e asimetrica si poate rata cazul vechi=.NULL., nou=0 ca "neschimbat" cand de fapt utilizatorul a introdus explicit un zero peste un camp gol (relevant pt. campuri ca id_ruta/id_agent, unde .NULL. si 0 pot avea semnatura diferita in Oracle — modifica_date_factura scrie orice i se da, inclusiv NULL neconditionat, deci diferenta chiar conteaza acolo).
Precizie numerica (gnPc, gnPPretV, gnPCant) Comparatia nu se face pe valoarea Double bruta, ci pe reprezentarea rotunjita la aceeasi precizie folosita la scriere — acelasi Round(x, gnPc)/Round(x, gnPPretV)/Round(x, gnPCant) aplicat pe ambele parti inainte de =. Motiv concret: adauga_articol_factura primeste pretul ca Str(..., 18, gnPc) (text), deci orice zgomot de reprezentare in binar (ex. 12.4999999999 vs 12.5) care nu apare si in textul trimis Oracle-ului nu trebuie sa declanseze regenerare — regenerarea trebuie sa porneasca de la o diferenta care ar produce efectiv un text diferit trimis la Oracle, nu de la zgomot de virgula mobila intern VFP.
Randuri sterse/adaugate, nu doar modificate Comparatie pe multimea cheilor id_vanzare_det, nu pe pozitie: chei prezente in snapshot dar absente in curent = sters; chei prezente in curent dar absente in snapshot (sau id_vanzare_det gol/0, sentinela de linie noua) = adaugat; chei prezente in ambele = potential modificat, comparat camp cu camp. Orice rand adaugat sau sters, indiferent de continutul lui, marcheaza direct pentru regenerare (tabelul din G-bis: „linii adaugate/sterse” → regenerare) — nu are sens sa se compare campuri pe un rand care oricum nu exista pe ambele parti.
Ordinea randurilor Nu conteaza pentru detectie — comparatia e pe chei (set), nu pe pozitie in grid. Ordinea ar conta doar daca reordonarea insasi ar fi o schimbare semnificativa pentru Oracle (nu e cazul — adauga_articol_factura nu are parametru de ordine vizibil in semnatura citata in s10_pret_rederivat.md §1). Rezerva: daca formularul unificat permite reordonare manuala a liniilor si aceasta conteaza pentru vreun raport (necercetat aici), ar trebui un camp explicit de ordine comparat separat — nu presupus din pozitia in cursor.

2. Matricea „ce s-a schimbat → ce ruta", exhaustiva

Sursa de adevar: G-bis (plan:812-889) + modifica_date_factura_parametri.md + rute_scriere_antet.md.

Camp / grup Ruta De ce nu una mai ieftina
Cei 14 parametri (serie, numar, data, scadenta, ruta, delegat, masina, agent, dataora_exp, adresa facturare, text aditional, listare_detaliata, tip_saft, efactura) modifica_date_factura, daca nimic altceva nu s-a schimbat in aceeasi sesiune E singura cale directa pe VANZARI+DOCUMENTE+ACT+IREG_PARTENERI+JV2007+RUL care nu atinge sumele — mai ieftina decat regenerarea (fara stergere+reemitere, fara stoc, fara nota noua). Regenerarea ar face acelasi lucru, dar cu cost mult mai mare (tranzactie stergere+reemitere, stoc, ID_VANZARE nou) pentru un camp care nu atinge nicio suma — nu se justifica.
Explicatia si taxcode pe o linie, fara nicio alta schimbare pe linii modifica_explicatie_articol Update pe doua coloane, fara sume — regenerarea ar fi disproportionata (stergere+reemitere completa pentru un text).
Cantitati, preturi, discount pe linie, gestiune, cota TVA, serie/lot Regenerare Nu exista alta ruta — verificat exhaustiv, nicio procedura Oracle de tip "modifica cantitate/pret pe linie existenta" (confirmat indirect: singura cale de scriere a sumelor e adauga_articol_factura, apelata doar la emitere/reemitere).
Linii adaugate/sterse Regenerare Identic — nu exista sterge_linie_factura/adauga_linie_factura separat de fluxul de emitere.
Discountul de document (VANZARI.DISCOUNT, discount_evidentiat) Regenerare Nu e printre cei 14 parametri ai modifica_date_factura (confirmat, tabelul din modifica_date_factura_parametri.md §1-2 nu il contine) si atinge direct sumele — cade natural in categoria "orice atinge sumele" din G-bis.
Grupul B — tip document, valuta, zi curs, client, sursa, gestiune sursa, politica de preturi Regenerare (decizia 25); blocate in etapa I rute_scriere_antet.md §2 — verdict NU pentru toate 7, cautat exhaustiv in Oracle si VFP; regenerarea (calea de emitere) e singura care le scrie, pentru ca sunt scrise o singura data la scrie_factura2/echivalent, niciodata actualizate separat.
Grupul C — incasare (mod, casa, serie/nr chitanta, suma, POS) Regenerare (decizia 25); blocate in etapa I rute_scriere_antet.md §3 — incasarea devine ea insasi o linie de nota (scrie_incasare2), scrisa doar la emitere; nicio ruta „modifica incasare" separata.
Grupul A — venit/cheltuiala, sectie, responsabil, lucrare Niciuna din #13 — read-only (decizia 26) Editabile deja, dar la nivel de LINIE de nota, prin #6 (frm_modific2024) — #13 le-ar scrie uniform pe tot documentul, risc de suprascriere tacita a unei diferentieri facute din #6. Zero suprapunere intre fire, per decizia lui Marius din 09.08.2026.
Datele din sectiunea pliata — delegat/transport, adresa facturare, text aditional Fac parte din cei 14 parametri → modifica_date_factura daca nimic altceva nu s-a schimbat; regenerare daca si sumele s-au schimbat (vezi sectiunea 3) Sunt deja acoperite de tabelul de mai sus prin V_ID_DELEGAT/V_ID_MASINA/V_ID_AGENT/V_ID_FACTURARE/V_TEXT_ADITIONAL/V_DATAORA_EXP.
Nimic schimbat Nimic Explicit cerut de G-bis — altfel orice deschidere ar produce scriere degeaba.

3. Cazurile care cad intre rute — descoperirea centrala a acestei cercetari

3.1 Antet + sume schimbate in aceeasi sesiune — verdict cu dovada, nu presupunere

Intrebarea din briefing: se cheama ambele rute, sau regenerarea le acopera pe amandoua?

Raspuns, verificat pe cod: regenerarea acopera 7 din cei 14 parametri prin insusi mecanismul de emitere, fara nicio ruta suplimentara — pentru ca poDate e acelasi obiect care alimenteaza atat afisarea antetului cat si apelul scrie_factura2 folosit de regenerare (decizia 35: "reemiterea scrie prin pack_facturare, pe acelasi drum ca emiterea").

Dovada directa, apelul scrie_factura2 (COMUN\clase\ofacturare.vc2:14345-14359, identic la :14373-14387 si :18343-18387):

lcSql = [{call pack_facturare.scrie_factura2(] + ;
    ... pnTotalFtva, pnTotalTva, pnDiscount, serie_chit, nr_incasare, lcListaIncasare, ;
    IIF(Isnull(poDate.id_delegat),[NULL],Alltrim(Str(poDate.id_delegat))) + [,] + ;
    IIF(Isnull(poDate.id_masina),[NULL],Alltrim(Str(poDate.id_masina))) + [,] + ;
    IIF(Isnull(poDate.id_facturare),[NULL],Alltrim(Str(poDate.id_facturare))) + [,] + ;
    IIF(Isnull(poDate.nListareDetaliata),[0],Alltrim(Str(poDate.nListareDetaliata))) + [,] + ;
    [to_date('] + Ttoc(poDate.dataora_exp,1) + [','YYYYMMDDHH24MISS'),] + ;
    IIF(Isnull(poDate.id_agent),[NULL],Alltrim(Str(poDate.id_agent))) + [,] + ;
    ['] + OracleSpecialCharacters(...(poDate.text_aditional...)) + [',] + ;
    Alltrim(Str(poDate.discount_evidentiat)) + [,] + ;
    ALLTRIM(Str(pnParametruAditional)) + [,?@poDate.nid_vanzare)}]

scrie_factura2 scrie deci, la fiecare regenerare, id_delegat, id_masina, id_facturare (adresa facturare), listare_detaliata, dataora_exp, id_agent, text_aditional — 7 din cei 14 parametri ai modifica_date_factura — direct din poDate, indiferent daca utilizatorul le-a schimbat sau nu in sesiunea curenta. Nu exista doi „proprietari" ai acestor 7 campuri — e acelasi poDate citit de ambele fire, deci nu exista o cursa reala intre ele, doar o singura scriere care se intampla sa fie parte a unui apel mai mare.

Verdict, cu ordinea explicita:

  1. Cand regenerarea porneste, NU se mai cheama modifica_date_factura. Ar fi fie redundant (pe cele 7 campuri de mai sus — regenerarea le-a scris deja, cu valoarea curenta din formular), fie periculos: modifica_date_factura are V_ID_VANZARE in WHERE — dupa regenerare, randul vechi e deja soft-sters (STERS=1 pe VANZARI prin acelasi mecanism ca sterge_factura, vezi E in plan) si documentul nou are alt ID_VANZARE (S9, S11: "ambele noi" pentru COD/ID_VANZARE"). Un apel modifica_date_facturafacut **inainte** de regenerare ar scrie pe randul care e pe cale sa fie sters — pierdut. Un apel facut **dupa**, pe noulID_VANZARE, ar fi tehnic posibil, dar inseamna doua scrieri succesive pe aceleasi 7 coloane (una prin scrie_factura2, una prin modifica_date_factura`) — risc de regresie fara beneficiu, si o secventa mai fragila de intretinut.
  2. Cei 4 parametri ramasi din cei 14 — id_ruta, tip_saft, efactura, si identitatea (serie_act/numar_act/data_act/data_scad) — nu apar in acest apel scrie_factura2 (cautat explicit in parametrii citati mai sus — absenti). Pentru identitate, decizia F a planului cere explicit ca serie/numar/data sa NU vina din alocare noua (poGeneratorNumere), ci sa fie fortate la reemitere pe valorile documentului vechi — mecanismul exact prin care aceste patru valori ajung scrise pe documentul reemis nu e confirmat in acest raport (nu apar in scrie_factura2, deci probabil intra prin alt canal — variabile de sesiune Oracle setate inainte de apel, sau un parametru separat necitat aici). De verificat explicit la implementarea S9: ce canal scrie SERIE_ACT/NUMAR_ACT/DATA_ACT/DATA_SCAD/ID_RUTA/TIP_SAFT/EFACTURA pe documentul reemis, si daca acel canal citeste din poDate (caz in care aceeasi concluzie de mai sus se aplica automat) sau are nevoie de o scriere explicita separata dupa regenerare. Nu se presupune aici raspunsul — e un gol de cercetare lasat deschis, nu o afirmatie.
  3. Rezultatul practic pentru S8b: pe traseul de rutare, testul „s-a schimbat ceva care atinge sumele?" trebuie evaluat inaintea testului „s-a schimbat antetul?" — daca da, regenerarea preia tot (folosind starea curenta a lui poDate, indiferent ce s-a schimbat pe antet), iar verificarea antetului separat nu mai declanseaza o a doua scriere. Ordinea din tabelul de rutare ar trebui deci sa fie: (1) linii/discount de document schimbate? → regenerare, gata; (2) altfel, antet schimbat (14 parametri)? → modifica_date_factura; (3) altfel, doar explicatie de linie? → modifica_explicatie_articol; (4) altfel → nimic. Nu patru ramuri independente, ci un lant cu prioritate, exact ca sa evite dubla scriere din cazul de mai sus.

3.2 Explicatia de linie, cand si sumele s-au schimbat

Cerinta din briefing, verificata: daca in aceeasi sesiune s-a schimbat si o cantitate, regenerarea rescrie oricum liniile — explicatia trebuie sa mearga prin regenerare, nu pe ruta ei ieftina.

Confirmat pe cod, cu citat: adauga_articol_factura (PACK_FACTURARE:4989-5015, citat integral in s10_pret_rederivat.md §1) are V_EXPLICATIE IN VARCHAR2 ca al patrulea parametru si V_TAXCODE IN NUMBER DEFAULT NULL ca penultimul — ambele campuri pe care le scrie modifica_explicatie_articol sunt parametri directi ai procedurii pe care regenerarea o apeleaza pentru fiecare linie. Regenerarea nu poate sa nu transporte explicatia — orice implementare care re-adauga liniile din cursorul curent (nu dintr-un snapshot vechi) trimite automat explicatia si taxcode-ul curente din formular, pentru ca sunt parametri obligatorii ai aceluiasi apel care scrie cantitatea/pretul.

Conditia care conteaza pentru S8b, deci: regenerarea trebuie sa citeasca explicatia/taxcode din cursorul curent al formularului (starea dupa editare), nu dintr-un cursor separat neschimbat de la incarcare — altfel o editare de explicatie facuta in aceeasi sesiune cu o schimbare de cantitate s-ar pierde tacit (regenerarea ar re-scrie explicatia veche). Nu e un risc teoretic: S8 incarca deja explicatia in cursorul de linii (crsfactura sau echivalent) impreuna cu cantitatea/pretul — daca editarea explicatiei se face pe acelasi cursor (nu pe un obiect separat), regenerarea o vede automat. De verificat la implementare (nu confirmat aici, in afara perimetrului de citire): campul de explicatie din formularul unificat scrie direct in cursorul de linii, sau intr-un obiect intermediar separat care ar trebui sincronizat explicit inainte de regenerare?

Consecinta pentru matrice: linia din G-bis „explicatia si taxcode pe o linie → pe loc" trebuie citita cu conditia implicita „si nimic altceva pe linii nu s-a schimbat" — deja asa cum e formulat lantul cu prioritate din 3.1 punctul 3 (verificarea de sume vine prima).


4. Explicatia liniei — rezumat separat (cerut explicit in briefing)

Acoperit deja in sectiunea 3.2. Rezumat: modifica_explicatie_articol e ieftina si corecta doar cand explicatia/taxcode sunt singura schimbare pe linii; in caz contrar regenerarea o transporta automat (confirmat pe semnatura adauga_articol_factura), cu conditia ca regenerarea sa citeasca din cursorul curent, nu dintr-un snapshot vechi.


5. Criteriul „deschid si inchid fara sa modific nimic → nicio scriere" — ce l-ar incalca accidental

5.1 Cel mai probabil punct de fals-pozitiv: lookup-ul „ultimul delegat/masina al clientului"

frm_alte_date.Init (COMUN\clase\ferestre_cere_date.vc2:3119-3136, citat in s3b_alte_date_analitice.md §1.1/§4.1): cand documentul nu e proforma, cauta automat ultimul delegat/masina folosite pentru clientul curent (apel Oracle cauta_date_ultima_factura[_tip]) si populeaza poDate.id_delegat/ poDate.id_masina cu rezultatul. Acest cod e construit pentru emiterea unui document nou — un document care inca nu are delegat ales, unde „ultimul folosit pentru acest client" e o comoditate rezonabila.

Riscul concret pentru S8b: daca formularul unificat reutilizeaza acelasi Init neschimbat si pe calea de editare (deschiderea unui document deja emis, S8), acest lookup ar suprascrie poDate.id_delegat/poDate.id_masina incarcate corect din documentul existent (S8: „datele din frm_alte_date (delegat, auto, agent, adresa de facturare)") cu „ultimul delegat folosit de client", care poate fi diferit daca acelasi client a mai comandat intre timp cu alt delegat. Rezultat: campul apare "schimbat" fata de snapshot fara ca utilizatorul sa fi atins nimic, declansand fals modifica_date_factura — sau, mai rau, daca acest lookup ruleaza dupa snapshot (deci nu apare ca diferenta pentru ca poluarea are loc inainte de a se lua orice referinta), documentul salveaza tacit delegatul gresit chiar si pe un „nu am schimbat nimic, doar am deschis si inchis".

Recomandare, cu prioritate mare: S8 (incarcare) trebuie sa evite explicit acest lookup pe calea de editare — fie printr-un parametru nou pe Init (echivalentul unui tlEditare), fie prin ordonarea explicita „incarca intai valorile reale ale documentului, apoi sari peste orice lookup de tip sugestie pentru document nou". Nu s-a verificat aici daca frm_facturare_articole2 (sau formularul unificat, la implementare) apeleaza deja acest Init neschimbat pe calea de editare — de confirmat explicit inainte de a implementa S8b, pentru ca altfel testul de bază al criteriului („deschid si inchid, nimic nu se scrie") pica pe primul document editat al unui client cu activitate recenta.

5.2 Alte surse de fals-pozitiv, verificate explicit

Sursa Verdict, cu dovada
Pretul re-derivat la incarcare (S10) Neinchis, semnalat: S8 incarca liniile prin cursor_retur_document(V_COPIERE=1) — cursorul chiar folosit pentru citire nu a fost analizat in acest raport (in afara perimetrului citit pana acum). s10_pret_rederivat.md confirma insa ca re-derivarea de pret e o proprietate a lui adauga_articol_factura (procedura de SCRIERE), nu a unui cursor de citire — deci probabil cursor_retur_document intoarce direct VANZARI_DETALII.PRET stocat, fara sa treaca prin logica de re-derivare. Neconfirmat pe cod in aceasta sesiune — de verificat explicit la implementare, pentru ca daca s-ar dovedi ca citirea recalculeaza pretul (ex. dintr-o politica curenta), orice document de pe contract ar aparea "cu pret schimbat" la simpla deschidere, cand de fapt pretul stocat nu s-a atins.
zi_curs reactiv (S4d) Confirmat fara risc pe valoare: poDate.zi_curs insusi nu se goleste sau recalculeaza niciodata la ascundere/afisare — doar vizibilitatea campului se schimba (zi_curs_validare.md, citat integral in s4d_zi_curs_reactiv.md §3). Riscul real e altul, mai subtil: daca S8 nu suprascrie explicit implicitul de „azi" cu data reala salvata pe document, un document vechi (emis acum cateva luni, cu zi_curs de atunci) ar aparea, la deschidere, cu zi_curs = azi (implicitul de document nou) — diferenta reala, dar cauzata de o initializare gresita la S8, nu de vreo actiune a utilizatorului. Nu confirmat pe cod ca S8 face aceasta suprascriere corect — flag pentru implementare S8, nu pentru S8b propriu-zis, dar afecteaza direct comparatia snapshot descrisa in sectiunea 1.
Rotunjiri la afisare vs. la scriere Acoperit deja in sectiunea 1.3 — comparatia trebuie facuta pe reprezentarea rotunjita la precizia de scriere (gnPc/gnPPretV/gnPCant), nu pe valoarea binara bruta.
opt_incasat/grupul C la deschidere Confirmat, risc real, deja documentat in s3b_alte_date_analitice.md §4.3: opt_incasat.Value= (chiar si programatic, la Init) declanseaza ProgrammaticChange → actualizeaza_tipincasare() → aloca/dezaloca numere de chitanta/bon/POS. Daca sectiunea pliata re-populeaza opt_incasat.Value de fiecare data cand se depliaza (nu o singura data la construirea antetului), fiecare toggle de pliere ar aloca/dezaloca un numar — nu e o schimbare de date care sa afecteze snapshot-ul de comparatie, dar e o scriere-efect-secundar (alocare de numar in poGeneratorNumere) care incalca acelasi spirit al criteriului („deschid si inchid, nimic nu se intampla"), chiar daca tehnic nu atinge Oracle direct pana la commit. Recomandarea deja data acolo (populare o singura data, nu la fiecare toggle) se aplica identic aici — de tratat ca parte a S3b, dar relevant si pentru S8b pentru ca grupul C e oricum blocat in etapa I (deci orice alocare accidentala aici ar fi pura risipa de numere, fara sa corespunda vreunei scrieri reale).
Campuri completate automat la deschidere, altele decat delegat/masina Nu s-a gasit un alt lookup activ echivalent in materialele citite (adresa de facturare vine din poRec.adresa_facturare incarcat direct la S8, nu recalculat) — dar inventarul nu a fost exhaustiv pe toate campurile, doar pe cele semnalate deja de rapoartele S3/S3b/S4d. Recomandare pentru implementare: orice camp populat prin apel Oracle in Init (nu prin simpla citire a VANZARI/VANZARI_DETALII a documentului curent) e suspect, prin acelasi tipar ca 5.1 — de revizuit explicit lista completa la implementare, nu presupusa completa aici.

6. Retragerea actiunilor vechi de pe frm_facturi

Conditia de „acoperit", propusa concret (planul spune doar „abia dupa ce formularul unificat le acopera", fara sa defineasca "acopera"):

  1. Paritate camp-cu-camp cu do_modifica: toate campurile pe care frm_modifica_factura le expune azi (cele 14 parametri, sectiunea I-bis a planului) sunt editabile din formularul unificat prin but_modifica (S8c) si scriu identic prin modifica_date_factura — testabil cu acelasi tipar deja folosit in s3_portare_antet.md §7 si s3b_alte_date_analitice.md §10.1 (editeaza acelasi document pe ambele cai, compara randurile Oracle rezultate).
  2. Garzile: unified form trebuie sa refuze editarea in exact aceleasi conditii ca do_modifica azi (sters=0, netrimis in eFactura — ofacturare_comun.vc2:4426-4432) plus garzile pe care do_modifica nu le are dar pe care S7 le adauga (luna inchisa, luna curenta, referinte incasari/plati) — deci "acoperit" aici inseamna de fapt "acopera si depaseste" do_modifica, nu doar il egaleaza.
  3. Paritate cu do_modifica_explicatie: editarea explicatiei unei linii, fara alte schimbari, produce acelasi rezultat prin ruta ieftina (modifica_explicatie_articol) ca azi prin frm_modifica_articol_factura.
  4. Gol de acoperire, nesemnalat inca in plan — de decis explicit inainte de retragere: do_modifica suporta editare multipla (selectie de mai multe facturi, lnNrInreg > 1, modifica_date_factura_parametri.md §3: ramura Otherwise face Scatter ... Blank si aplica acelasi antet pe toate randurile selectate din SCAN). Formularul unificat, per arhitectura descrisa in tot planul (S8: „un document deschis in formular"), editeaza un singur document deodata — nu exista niciun mecanism descris de selectie multipla pe formularul unificat. Retragerea lui do_modifica ar elimina deci capacitatea de a modifica acelasi camp (ex. ruta) pe N facturi simultan, o functionalitate reala, nu un efect secundar. Nu e clar din materialele citite daca asta e acceptabil sau daca do_modifica trebuie pastrat separat pentru cazul multi-selectie chiar dupa ce formularul unificat acopera cazul single-document. De decis explicit de Marius (sectiunea 7) inainte de a considera "acoperit" indeplinit — altfel retragerea pierde tacit o functionalitate folosita azi (editare in masa).

7. Riscuri si de decis de Marius

Stabilit cu dovada in acest raport (nu de redeschis):

  • Reteta G-bis e corecta ca matrice de baza (sectiunea 2), dar rutarea trebuie implementata ca lant cu prioritate (sume întâi), nu ca patru teste independente — altfel risc de dubla scriere pe cele 7 campuri comune scrie_factura2/modifica_date_factura (sectiunea 3.1).
  • Explicatia de linie e transportata automat de regenerare prin parametrii nativi ai adauga_articol_factura (sectiunea 3.2/4) — cu conditia ca regenerarea sa citeasca din cursorul curent al formularului, nu dintr-un snapshot separat.
  • Lookup-ul „ultimul delegat/masina" din frm_alte_date.Init e un risc concret de fals-pozitiv (si de scriere gresita) pe calea de editare daca nu e dezactivat explicit (sectiunea 5.1) — cel mai probabil candidat pentru a sparge criteriul de baza al S8b.
  • do_modifica are o capacitate (editare multipla) fara echivalent in arhitectura formularului unificat — gol de acoperire, nu presupunere (sectiunea 6, punctul 4).

Ramase de decis de Marius:

  1. Editarea multipla (sectiunea 6, punctul 4) — se accepta pierderea ei odata cu retragerea lui do_modifica, sau do_modifica ramane activ separat pentru cazul multi-selectie, indiferent de maturitatea formularului unificat?
  2. Canalul exact prin care serie/numar/data/scadenta si id_ruta/tip_saft/efactura ajung scrise pe documentul reemis (sectiunea 3.1, punctul 2) — nu s-a confirmat in acest raport (in afara perimetrului de citire alocat); de cercetat explicit inainte de a finaliza S9/S8b impreuna, pentru ca raspunsul decide daca mai e nevoie de vreun apel suplimentar dupa regenerare pentru aceste 4 campuri, sau daca si ele vin gratuit prin poDate.
  3. Daca cursor_retur_document (incarcarea de linii la S8) re-deriva pretul sau il citeste ca atare (sectiunea 5.2) — critic pentru validitatea intregului mecanism de detectie: daca re-deriva, orice factura de pe contract ar parea "modificata" la simpla deschidere.
  4. Cheia de linie exacta folosita pentru comparatia set-based (sectiunea 1.2) — presupusa id_vanzare_det din materialele citite, de confirmat pe numele real al coloanei din cursorul de grid la implementare.

Ce nu s-a putut stabili si de ce

  • Continutul exact al cursor_retur_document (procedura de citire folosita la S8) nu a fost citit in aceasta sesiune — perimetrul alocat (docs de cercetare deja existente + apelurile scrie_factura2 pentru dovada din sectiunea 3) nu a inclus acest fisier PL/SQL specific; risc semnalat, nu inchis.
  • Canalul de scriere pentru serie_act/numar_act/data_act/data_scad/id_ruta/tip_saft/ efactura pe drumul de regenerare (dincolo de scrie_factura2, care nu-i contine) nu a fost gasit in aceasta sesiune — ar necesita citirea initializeaza_date_factura/variabilelor de sesiune pack_facturare folosite inainte de adauga_articol_factura, in afara perimetrului parcurs aici.

Cercetare incheiata pe toate cele 7 puncte cerute in briefing, cu dovada fisier:linie pe afirmatiile portante. Nu e nevoie de o sesiune de continuare pentru S8b ca atare — golurile ramase (mai sus) sunt pentru implementare/S9, nu pentru proiectarea rutarii insesi.