# Plan #13 — formular unificat de facturare + editare prin regenerare Sursa: `COMUN\docs\todos.txt` punctul 13. Mockup: `docs\mockup_13_formular_unificat.html` (versiunea 5). Cercetare pe cod, toata in `docs\cercetare\`: `inventar_controale_formulare.md`, `proforma_copiere_puncte_intrare.md`, `import_roris_roaacnpro.md`, `discount_pe_articol.md` si `discount_verificare2.md` (a doua il corecteaza pe primul), `modifica_antet_bifa.md`, `valuta_si_curs.md`, `buton_comutator_picture.md`, `modifica_date_factura_parametri.md`, `retur_si_lista_preturi.md`, `factura_retur_document.md`, `roaauto_facturi.md`, `roaauto_articole_lista_preturi.md`, `cont_venit_articol_fara_politica.md`. Stare: **propunere, neinceput**. Analiza facuta pe cod la 09.08.2026, in sase runde. ## Cerinta, asa cum a fost formulata Trei lucruri intr-un singur punct: 1. **Unificarea formularelor** — `date_factura` / `date_aviz` sa intre in formularul de facturare, ca sa nu mai fie doua ferestre pentru acelasi document. 2. **Fara incarcarea prealabila a tuturor articolelor** din toate politicile de preturi — pot fi mii si dureaza mult aducerea lor de pe server. 3. **Editarea facturii / avizului prin regenerare**, in toate variantele (politici de preturi, comanda, contract, aviz): formularul se redeschide completat ca inainte de salvarea initiala, se fac modificarile ca la introducere, iar la confirmare documentul initial se marcheaza `sters = 1` si se salveaza unul nou — ca sa se vada ce s-a modificat. Decizia lui Marius, 09.08.2026: **intai unificarea, apoi editarea prin regenerare.** Explicit **nu** pe calea editarii directe a notelor / rulajelor / articolelor. ## Deciziile lui Marius, 09.08.2026 — luate, nu de reluat 1. **#13 coexista cu #6**, nu il inlocuieste. 2. **`ID_FACT` se pastreaza**, ca la orice modificare. Documentul reemis nu-si schimba identitatea. 3. **Toate datele se pastreaza, inclusiv numarul documentului.** Scopul e modificarea documentului, nu emiterea altuia. 4. **Antetul care intra in generarea lui `ID_FACT` (serie, numar, data) are cale proprie de modificare** — nu trece prin regenerare. 5. **Un singur formular, aceeasi infatisare la introducere si la modificare.** Fara banda de avertisment, fara coloane cu valorile initiale, fara panou de diferente. 6. **Se integreaza si modificarea de antet, si modificarea de articol care exista azi** — sa nu ramana trei actiuni de modificare pe acelasi document. ## Deciziile lui Marius, runda 3 (09.08.2026) — luate, nu de reluat 7. **`frm_alte_date` intra in formular**, in sectiunea pliata. Se inchide intrebarea ramasa deschisa in runda 2 (recomandarea de atunci — „ramane dialog” — **cade**). 8. **Sectiunea pliata primeste in plus analiticele**: venit / cheltuiala, sectie, responsabil (si, prin simetrie, lucrare). Antetul vizibil ramane aerisit, cu **controalele grupate** pe intelesuri, nu insirate. 9. **Un singur buton comutator pe antet.** Antetul se deschide blocat. Un singur `but_modifica` (creionul) il deblocheaza si **isi schimba imaginea in discheta lui `but_salvare`**; a doua apasare salveaza **doar antetul** si il blocheaza la loc. Un singur control, nu bifa plus buton — mai compact. **Toata factura se salveaza in continuare din `Termina`.** Rostul ramane acelasi: antetul se editeaza intentionat, nu din greseala, si se poate corecta fara a trece prin articole. **Butonul singur deschide tot antetul. Nu mai exista bife individuale** — nici cele patru de azi (serie / numar / data / scadenta). Un singur control comanda toata protectia antetului. *Istoric, ca sa nu se reia:* runda 3 propusese un buton `Modificare / Salveaza` care bloca **tot documentul** — respins, protectia e doar pe antet. Runda 4 propusese **bifa + buton separat** — respins la 09.08.2026 in favoarea butonului comutator. Runda 5 propusese **pastrarea celor patru bife individuale sub buton** — respins la 09.08.2026: bifele dispar cu totul. **Verificat pe cod la 09.08.2026** (`docs\cercetare\buton_comutator_picture.md`): **tiparul exista deja in suita si se copiaza, nu se inventeaza.** `frm_rulaje.se_modifica_assign` (`COMUN\clase\rulaje.vc2:4716-4773`) comuta exact asa **o singura instanta** de `but_modifica` intre creion si discheta — nu instantiaza a doua clasa. Trei lucruri de retinut din el: - se rescriu **impreuna** `.cpicturedown`, `.cpictureup` **si** `.Picture`, plus `.ToolTipText`, urmate de `.Refresh()`. Numai `.Picture` nu ajunge: hover-ul standard din `buton.MouseEnter` / `MouseLeave` (`_cmd_base.vc2:88-97`) rescrie `Picture` din `cPictureUp` / `cPictureDown`, deci iconita veche ar reveni la primul mouse-over; - numele de fisier se dau **fara cale** (`"save_sus.bmp"`), rezolvate prin `SET PATH` — care include si `GRAFICE`, si `COMUN\GRAFICE` (`Programe\roafacturare.prg:85-108`). Calea relativa `..\grafice\...` din clasa e buna doar la design-time; - imaginile exista: `COMUN\grafice\save_sus.bmp` / `save_jos.bmp` (discheta), `modific_sus.bmp` / `modific_jos.bmp` (creion, perechea folosita de clasa). Clasa de salvare din biblioteca se numeste **`but_salveaza`**, nu `but_salvare` (`cmd_butoane.vc2:340-352`); e sursa numelor de imagini, dar nu se instantiaza. Clasa `but_modifica` e la `cmd_butoane.vc2:184-198`. Butoanele **nu** au `do_activeaza` / `do_dezactiveaza` — pe ele se lucreaza direct pe `.Enabled`. **Consecinta asupra celor patru bife individuale** (serie / numar / data / scadenta, vezi I): **se elimina**, si **odata cu ele se abandoneaza si conditia lor** de azi (`chkSerieAct.Enabled = !EMPTY(NVL(poRec.numar_act,0))`, `ofacturare_comun.vc2:5736-5750`). Decis de Marius la 09.08.2026: **butonul deschide tot**, fara conditii pe camp. Un singur control, o singura regula. Cat de departe merge „tot” — vezi **decizia 19**: exact cat scrie `modifica_date_factura`, parametru cu parametru. 10. **Proforma foloseste acelasi formular.** 11. **Copierea foloseste acelasi formular**, ca document nou, cu antetul editabil. 12. **Formularul trebuie sa *poata* fi apelat cu sursa deja completata** — din pagina de comenzi cu comanda, si din programul de contracte cu contractul, fara ca utilizatorul sa mai aleaga ulterior. **Nu se integreaza contractele acum**; nu depinde de #10. 13. **Un singur buton de adaugare a articolelor, cu `xmenu()`** — nu doua butoane separate „adauga tot” si „alege”. Optiunile din meniu se schimba dupa sursa si acopera **comanda, contractul si avizele**, cu alegere selectiva acolo unde are sens (de exemplu o singura rata de contract). Butoanele de linie stau deasupra tabelului. 14. **Discountul pe articol, procent si valoare absoluta**, amandoua accesibile. 15. **Data cursului valutar apare doar cand are sens.** Sunt doua concepte diferite, care azi impart acelasi camp mereu vizibil: (a) **factura in valuta**, unde si articolele au pret in valuta, si (b) **factura in lei cu articole care pot avea pret in valuta**, convertite in lei la cursul din data cursului, cu documentul si contabilitatea in lei. Formularul trebuie sa fie **ergonomic si simplu** — campul nu apare cand nu e nimic de convertit. ### Deciziile lui Marius, runda 6 (09.08.2026) 16. **Lista de preturi e disponibila mereu, indiferent de sursa — si liniile suplimentare se pot si sterge.** Pe o factura din comanda sau din contract trebuie sa se poata **adauga si sterge** articole libere din lista de preturi, nu doar articole din sursa. Cazul real, formulat de Marius: clientul a comandat ceva, iar la facturare mai vrea ceva in plus sau vrea sa schimbe — deci documentul trebuie sa poata devia de la comanda, in ambele sensuri. Sursa umple documentul, nu il inchide. Optiunea „Cauta in lista de preturi…" ramane in meniul butonului de adaugare **pentru toate sursele**, inclusiv pentru documentele deja emise care se modifica. **Verificat pe cod** (`docs\cercetare\retur_si_lista_preturi.md`, B): **contractul o are deja** (`crsarticole` e populat de `cursor_preturi` / `cursor_contract` cu lista intreaga, al doilea grid e un adaos, nu o restrictie), **comanda nu** — `cursor_comanda` umple `crsarticole` **doar** cu articolele comenzii (`ofacturare.prg:266-308`). Deci golul real e pe comanda, si e in **continutul cursorului**, nu in vreun `Visible` de buton. Reteta exista deja in produs: ramura de copiere adauga lista de preturi peste cursorul sursei cu `APPEND FROM` (`ofacturare.prg:454-473`) — se generalizeaza ea, nu se inventeaza alta. 17. **Returul intra in formularul unificat, cu tot cu alegerea facturilor sursa.** Factura de retur facuta din facturi anterioare e un caz de acoperit explicit — lipsea si din mockup, si din proiectare. *Vezi sectiunea N.* **Sunt doua mecanisme distincte, si prima cercetare l-a vazut doar pe al doilea:** (a) **factura de retur ca document** (tipurile 8, 9, si avizul 24) — alege facturile sursa **la nivel de document**, cu selectie multipla, si isi populeaza liniile din ele; **gestiunea si pretul de achizitie vin neschimbate din linia originala**, verificat in `cursor_retur_document`. **Exista deja si merge** — nu se reproiecteaza. Vezi N.1; (b) **`But_retur`** — retur de articole intr-o factura de vanzare normala (tipurile 1, 5, 7, 10), unde factura sursa se alege **per articol**, iar gestiunea nu se mosteneste, ci se alege. Vezi N.2. Sunt doua fluxuri de cod independente, fara punct comun. Decizia 17 le duce pe amandoua in formularul unificat; ce se proiecteaza nou e (b) ridicat la nivel de document, nu (a). 18. **Facturile emise din ROAAUTO intra in perimetru la modificare.** ROAAUTO le emite din formularul lui, dar scrie tot in `VANZARI_DETALII`, si pe ele trebuie sa se poata adauga articole din lista de preturi. **Emiterea ramane la ROAAUTO** — se unifica doar modificarea. **Verificat pe cod** (`docs\cercetare\roaauto_facturi.md`): acelasi `PACK_FACTURARE`, acelasi drum `VANZARI_DETALII_TEMP` -> `VANZARI_DETALII`, tip de document **`-12`**, pe care editorul lui #6 il **vede deja**, testat pe date reale. **Precizarea lui Marius, 09.08.2026, verificata pe cod** (`docs\cercetare\roaauto_articole_lista_preturi.md`): in ROAAUTO **exista deja adaugarea de articole reale**, pe langa liniile generice — mecanismul „Alte servicii” din `frm_incasare_finala`. Deci descrierea „doar linii sintetice cu `id_articol` negativ” din primul raport e **incompleta**. Nuanta care conteaza pentru proiectare: sursa lui e **nomenclatorul brut**, nu lista de preturi, articolele oferite sunt **fara stoc** (`in_stoc = 0 and in_crm = 1`), iar **pretul se tasteaza manual** — nu vine din politici. Ce se cere e ca **acelasi lucru sa fie posibil si la modificarea documentului**, nu doar la emitere, **si nu numai pentru facturile auto, ci pentru orice tip**, cu **ambele surse**: lista de preturi (cu pret calculat) sau nomenclatorul (cu pret tastat). **Vezi O.** 19. **Toti parametrii lui `modifica_date_factura` trebuie sa fie modificabili din butonul de antet.** Butonul comutator nu deschide un subset ales de noi: daca procedura Oracle stie sa scrie un camp de antet, formularul trebuie sa aiba controlul prin care acel camp se poate schimba. **Verificat pe cod** (`docs\cercetare\modifica_date_factura_parametri.md`): sunt 15 parametri, din care **14 sunt campuri** — al 15-lea, `V_ID_VANZARE`, e identitatea randului si nu se editeaza. Din cele 14: - **13 au deja control** in `frm_modifica_factura` si se muta ca atare in antetul unificat; - **`V_EFACTURA` nu are niciun control nicaieri** (`ofacturare_comun.vc2:4576` il forteaza `0` la selectie multipla, altfel vine din `Scatter`) — **primeste unul**, e singurul camp nou-nout cerut de decizia asta; - `V_TIP_SAFT` are control, dar ascuns dupa `gl406` (`:5739-5741`) — ramane conditionat, nu se forteaza vizibil. Vezi I-bis pentru contractul procedurii, care impune si **cum** se cheama, nu doar ce se trimite. 20. **Din nomenclator se aleg si articole gestionabile, si negestionabile.** Filtrul `in_stoc = 0` al lui ROAAUTO **nu se preia** — acolo e o ocolire a subiectului gestiunii, nu o regula de produs. Consecinta acceptata: pe documentele auto vor coexista linii care descarca stoc cu linii care nu descarca, caz care azi nu exista nicaieri. *Vezi J si O-bis, intrebarea 2.* **Intrebarea care insotea decizia — „cu ce cont de venit intra un articol fara politica de pret” — s-a inchis prin cercetare: nu exista cont de venit pe linie.** Vezi **J-bis**. ### Deciziile lui Marius, runda 7 (09.08.2026) 21. **Cand `NOM_ARTICOLE.CONT` e gol pe un articol negestionabil, se aplica un fallback la un cont implicit.** Nu se accepta `NULL` (comportamentul de azi al liniilor „Alte servicii" din ROAAUTO) si nu se refuza adaugarea articolului. Contul anume se alege la implementare. **Atentie la perimetru:** decizia priveste **contul de gestiune** (`VANZARI_DETALII.CONT`). Cand a fost luata nu se stia inca faptul, stabilit mai tarziu in aceeasi runda, ca pe linie exista **doua** conturi cu surse diferite — cel de venit (`NOTE_CONTABILE.SCC`, prin politica de pret) nu e acoperit de aceasta decizie si e inca deschis. *Vezi J-bis.* Ipoteza de la care a pornit Marius — corespondenta 3xx -> 7xx dintr-un tabel — **nu s-a confirmat ca tabel**, dar intentia ei da: legatura articol -> cont de venit exista, prin politica de pret si `NOTE_CONTABILE`, configurata manual de contabil. 22. **Linia de retur fara factura originala e permisa.** Pe tipurile 8, 9, 24, „Cauta in lista de preturi…" si „Alege din nomenclator…" se comporta ca pe orice alt document — nu apar doar pentru corectii si nu sunt marcate special. Consecinta acceptata: gestiunea si pretul de achizitie se aleg (nu se mostenesc), iar maximul returnabil de pe server nu se aplica acelei linii. *Vezi N.3.* 23. **Secventierea deciziei 18: `pack_auto` se cerceteaza acum**, inainte de orice estimare, nu cand ii vine randul la livrare. *Vezi O, „Secventierea".* **Rezultat:** riscul tehnic nu exista — `PACK_AUTO` nu citeste `VANZARI` / `VANZARI_DETALII` deloc. Decizia 18 nu mai trebuie sa fie ultima. 24. ~~**Articolul ales din nomenclator primeste `id_pol`-ul unei politici de pret implicite.**~~ **RETRASA in runda 8, inlocuita de decizia 27.** Ramane in plan doar ca sa nu fie reintrodusa: politica implicita ar fi evitat codul nou in `pack_facturare`, dar cu pretul unei intrebari de configurare cu contabilul si al unui cont de venit uniform pentru orice articol adaugat asa. Marius a ales in loc corespondentele `CORESP_CONT_VENCHELT`. *Vezi decizia 27 si J-quater.* 25. **Butonul de antet deschide tot; ce nu se poate salva pe loc forteaza regenerare.** La a doua apasare, cei **14 parametri** merg prin `modifica_date_factura`, iar orice camp schimbat din **grupul B** (tip document, valuta, zi curs, client, sursa, gestiune sursa, politica de preturi) sau **C** (incasare) **marcheaza documentul pentru regenerare**, aplicata la `Termina`. Decizia 9 ramane intacta ca intentie — butonul chiar deschide tot antetul —, dar ruta de scriere se bifurca dupa camp, nu dupa buton. *Vezi G-bis.* **Consecinta pe etape:** in **etapa I** regenerarea nu exista inca, deci acele campuri nu au unde sa se salveze. **Planul le tine blocate in etapa I**, cu explicatie la hover, si le deschide odata cu etapa II. Motivul alegerii, in lipsa unei instructiuni contrare: un camp care se deschide dar nu se salveaza e o capcana — pierderea tacuta a unei modificari e mai rea decat un camp inca blocat. 26. **Analiticele raman read-only in #13; editarea lor e a lui #6.** Venit/cheltuiala, sectie, responsabil si lucrare se **afiseaza** in sectiunea pliata, cu valoarea de antet, dar nu se editeaza din formularul unificat — cine vrea sa le schimbe trece prin editarea notei contabile. Motivul: sunt deja editabile acolo, **la nivel de linie de nota**, iar #13 le-ar scrie uniform pe tot documentul — doua ferestre care scriu acelasi camp cu semantici diferite, cu risc ca #13 sa suprascrie tacit o diferentiere facuta din #6. **Zero suprapunere intre fire.** *Vezi G-bis.* Asta **nuanteaza decizia 8**: analiticele intra in sectiunea pliata ca **afisare**, nu ca editare. ### Deciziile lui Marius, runda 8 (09.08.2026) 27. **Contul de venit vine din corespondente si din nomenclator, nu dintr-o politica de pret implicita. Decizia 24 se retrage.** Regula, pe doua ramuri: - **articol gestionabil** (cont de gestiune de clasa 3xx): contul de venit se ia din tabelul de corespondente `CORESP_CONT_VENCHELT` — `select id_ccv, cont, cont_chelt, cont_venit, sters, dataoras, cont_aprovizionare, cont_diferente from coresp_cont_venchelt`, cautand pe `CONT` (contul de gestiune al liniei) si luand `CONT_VENIT`; - **articol negestionabil**: se foloseste `NOM_ARTICOLE.CONT` **daca e deja un cont de clasa 7xx sau 6xx**; altfel, implicit **704**. Motivul respingerii politicii implicite: ea muta problema in configurare (cine creeaza politica, cu ce `SCC`, una sau mai multe) si obliga la o intrebare cu contabilul inainte de orice livrare. Corespondentele exista deja ca date de productie si leaga contul de gestiune de cel de venit — exact relatia cautata, doar ca sub alt nume decat s-a cautat in runda 7. 27-bis. ~~**Regula din 27 se implementeaza FARA cod nou in `pack_facturare`.**~~ **RELAXATA de decizia 34 si inchisa de decizia 35** — pachetul se modifica punctual, si e singurul cod de contare, si la emitere si la editare. Textul de mai jos ramane doar ca istoric al rationamentului. (Marius, runda 8, dupa prima formulare a lui 27). Motivul: pachetul e mare si e folosit de toate produsele suitei — o ramura noua acolo e un risc peste tot, nu doar in ROAFACTURARE. Deci **regula de derivare a contului de venit se aplica pe partea VFP**, inainte ca articolul sa plece spre Oracle, iar `contabilizeaza_articol` ruleaza **neschimbata**. Calea de verificat: VFP calculeaza contul de venit dupa regula de mai sus si alege un `id_pol` a carui nota are deja acel `SCC`, astfel incat `FACT-024` sa nu se mai poata declansa. **Modificarea pachetului ramane planul B**, folosit doar daca se dovedeste ca nu exista nicio cale dinspre VFP. *Vezi J-quater;* fezabilitatea: `docs\cercetare\coresp_cont_venchelt.md`, punctul 8. **PREMISA A FOST VERIFICATA (Marius, runda 8):** „in ROAACNPRO, dar si la factura din comanda / contract, se adauga articole fara politica de preturi — de ce nu se poate si aici?" **Raspuns: observatia e reala, dar niciunul din cele doua exemple nu e un articol fara politica.** ROAACNPRO nu cheama deloc `contabilizeaza_articol` (are propria contabilizare, `pack_acn.salveaza_regdoc`); pe contract, articolele vin din `cursor_contract` / `cursor_preturi`, care le livreaza **cu `id_pol` atasat**. `FACT-024` ramane blocantul. *Detalii, cu dovezi: J-quater, punctul 1.* **REZULTAT: calea VFP exista si e mai buna decat modificarea pachetului** — se sprijina pe un RPC existent (`pack_preturi.adauga_politica_pret_art`) si aduce, pe langa `SCC`, si `CU_TVA` / `IN_VALUTA`, pe care un fallback in pachet ar fi trebuit sa le hardcodeze. *Vezi J-quater, punctul 3.* **Planul B (modificarea `pack_facturare`) se abandoneaza.** 28. **Pe factura ROAAUTO pot exista si alte articole decat cele de pe deviz.** Nepotrivirea de afisare dintre ecranul de deviz si factura retiparita (O, intrebarea 1) **e acceptata**: cerinta e ca **MANOPERA si MATERIALE sa fie conform devizului**, plus orice alte articole adaugate. Nu se cere cod nou in ROAAUTO ca sa aduca ecranul de deviz la zi. *Vezi O, intrebarea 1.* 29. **Stergerea unei linii venite din comanda ramane fara protectie.** Linia se sterge ca oricare alta; comanda va aparea, corect, ca **facturata partial**. Nu se adauga confirmare, nu se marcheaza „refuzat", nu se ajusteaza numararea acoperirii. *Vezi S4e.* 30. **Nu se coordoneaza cu firul #6.** #13 **incepe dupa ce #6 se termina**, deci suprapunerea pe fisiere nu se poate produce si nu e nevoie de impartire de perimetru. Consecinta pe interdictii: cele doua fisiere ale lui #6 raman intangibile **cat timp #6 e in lucru**, dar restrictia expira odata cu el, nu cere negociere. Decizia 26 (analiticele read-only in #13) ramane in picioare — ea tine de semantica, nu de coliziunea de lucru. *Vezi „Relatia cu #6".* ### Deciziile lui Marius, runda 9 (10.08.2026) **31. Datele din Dev nu sunt baza de proiectare.** Fiecare client isi defineste propriile politici de pret, fiecare cu nota ei contabila de vanzare salvata pe un `ID_SET`. Orice masurare pe `MARIUSM_AUTO` vale ca **dovada ca un mecanism exista** si ca sursa de concluzii **structurale**, niciodata ca harta a ce e configurat la client. Nicio decizie de proiectare nu se sprijina pe numarul de politici sau de note gasite pe Dev. *(Generalizeaza regula „zero cazuri in date nu e dovada" in ambele sensuri: nici prezenta nu e dovada.)* **32. Intrebarea de raspuns inainte de orice implementare a contului de venit:** cum alege programul nota contabila de vanzare la **factura pe baza de comanda**, care **nu are politica de pret**? Daca acel mecanism se poate refolosi pentru articolul adaugat ad-hoc, **reteta in 4 pasi din J-quater se abandoneaza** in favoarea lui. Vezi J-quater, „Intrebarea deschisa care poate anula toata reteta". **34. `contabilizeaza_articol` primeste un parametru de cont contabil. Decizia 27-bis e RELAXATA.** Marius, 10.08.2026, textual: *„poți să adaugi parametrul contul contabil la `contabilizeaza_articol`."* Consecinta: **reteta in 4 pasi din J-quater se abandoneaza.** Nu mai e nevoie de politica tehnica, nici de interogarea inversa pe `SCC`, nici de inserarea articolului in politica. VFP calculeaza contul dupa regula deciziei 27 si **il trimite direct**. **Ce rămâne de proiectat, si nu e „doar un parametru":** contul nu e singurul lucru care venea de pe nota. `cursor_articol` aducea si `SCD`, `CU_TVA`, `IN_VALUTA`, `EXPLICATIE`, `ID_VENCHELT`, `ID_SECTIE`, iar bucla care il consuma apeleaza **si** `scrie_nota` **si** `descarca_gestiune`. Pe un articol fara politica, `FACT-024` sare inainte. Deci e nevoie de **parametru + ramura fara politica**, care sa furnizeze ce furniza nota si sa ruleze `descarca_gestiune` **exact o data**. Obiectia rundei 8 („`CU_TVA` si `IN_VALUTA` n-au sursa in afara lui `NOTE_CONTABILE`") **nu mai e blocanta, e proiectare** — de reevaluat daca se pot deriva din document. **Constrangeri de forma, ca sa nu se strice suita:** parametru nou **cu `DEFAULT NULL`**, la finalul listei, si ramura inerta cand lipseste — apelanții existenți nu se schimba. `pack_facturare` e comun **intregii suite**, deci cere regresie pe ROACONT / ROAGEST / ROACONTRACTE / ROAAUTO / ROAACNPRO, nu doar ROAFACTURARE. **Ramura noua nu moșteneste bug-ul de set multi-rand** din L.0-ter. **PROIECTATA** — `docs\cercetare\canal_cont_venit_fara_politica.md`, sectiunea „Proiectarea parametrului de cont contabil", liniile 13-227. Verdictul, in trei randuri, pentru ca schimba forma modificarii: `contabilizeaza_articol` primeste azi **un singur parametru**, `detalii_articol VANZARI_DETALII_TEMP%ROWTYPE` — deci contul **nu intra literal pe ea**, ar fi cosmetic. Intra ca **coloana noua `VANZARI_DETALII_TEMP.CONT_VENIT VARCHAR2(4) NULL`**, populata printr-un parametru nou `V_CONT_VENIT IN VARCHAR2 DEFAULT NULL` la **coada lui `adauga_articol_factura`** (procedura chemata direct din VFP), pe care `contabilizeaza_articol` o primeste automat: cei trei apelanti interni fac `SELECT * BULK COLLECT` intr-un `TABLE OF ...%ROWTYPE`, deci `scrie_factura2`, `scrie_factura_avize_retur` si `scrie_aviz_retur` **nu se ating deloc**. > **Corectie de nume, runda 11 — mecanismul insa rezista.** Al doilea apelant e **`scrie_factura_avize`** > (`:6708-6749`), nu `scrie_factura_avize_retur`, care nici nu cheama `contabilizeaza_articol`. > **Verificat pe cod ca toti trei apelantii reali** — `scrie_factura2` (`:6039-6063`), > `scrie_factura_avize` (`:6708-6749`), `scrie_aviz_retur` (`:7097-7106`) — fac intr-adevar > `SELECT * BULK COLLECT INTO` un `TABLE OF VANZARI_DETALII_TEMP%ROWTYPE`, **fara lista explicita de > coloane**. Deci coloana noua curge automat prin toti trei si **niciunul nu se atinge**; concluzia > proiectarii sta, doar doua din cele trei nume erau gresite. In `scrie_factura_avize`, `tab_detalii(i)` > e modificat inainte de apel doar pe `.cantitate` / `.id_rata` — `CONT_VENIT` ramane neatins. Efectul cerut de Marius e exact acelasi; doar mecanica de livrare difera de formularea literala. Precedentul e in aceeasi semnatura: `V_TAXCODE` si `V_LOT` sunt deja doi parametri adaugati ulterior, la coada, cu `DEFAULT NULL`, iar apelul VFP e pozitional si se opreste la `V_LOT`. Ramura noua se activeaza pe `detalii_articol.cont_venit IS NOT NULL`, **infasoara si blocul `FACT-024`** (garda ramane litera cu litera pe ramura veche: o linie fara politica **si** fara cont trimis cade in continuare cu `FACT-024`), n-are cursor deloc — deci `descarca_gestiune` ruleaza **exact o data prin constructie** si bug-ul de set multi-rand nu se mosteneste, fara sa se repare ramura veche. **Doua hardcodari raman decizii deschise pentru Marius**, nu descoperiri: `SCD = '4111'` si `CU_TVA = 1` — vezi „Ce ramane de decis" mai jos. **VERIFICATA ADVERSARIAL** — `docs\cercetare\parametru_cont_contabilizeaza_articol.md`. Proiectarea rezista; patru rezultate schimba insa detalii de executie: - **Parametrul se cableaza in DOUA locuri VFP, nu unul.** `ofacturare.vc2:14069` si `:18089` **nu** sunt o duplicare a aceleiasi metode, cum se presupusese: sunt **doua clase distincte**, `frm_facturare_articole.do_scrie_articole` (`:13967-14195`) si `frm_facturare_articole2.do_scrie_articole` (`:18003-18221`), fiecare construindu-si separat apelul RPC. - **`CU_TVA = 1` nu e inofensiv, cum spunea proiectarea.** Actualizarea lui `nproc_tva_max` / `nid_jtva_coloana` / `nTaxCode` e chiar in interiorul lui `IF V_CU_TVA = 1` (`:12537-12558`), iar comparatia `nproc_tva_max < V_PTVA` **se uita doar la rata, nu la suma**. O linie fallback cu TVA real 0% ar intra deci in comparatia de maxim si, daca e prima linie a documentului (`nproc_tva_max` porneste `-1`, `:1885`), **castiga** — iar coloana si taxcode-ul ei ajung sa descrie **linia de discount a intregii facturi** (`:6164-6184`). Combinatia e ingusta (linie scutita + discount global + acea linie e „maximul" de pana atunci), dar efectul e real. Motivul pentru care decizia ii apartine lui Marius e acum concret, nu formal. - **`INSERT INTO VANZARI_DETALII ... FROM VANZARI_DETALII_TEMP` are lista de coloane explicita** — 24 de coloane, `PACK_FACTURARE:13705-13757`, in `scrie_in_vanzari`. Deci daca se vrea `CONT_VENIT` pastrat si dupa fapt in `VANZARI_DETALII`, **lista trebuie extinsa explicit**; altfel coloana traieste doar in `VANZARI_DETALII_TEMP` si `ACT_TEMP`. - **Articolul compus nu e o gaura in proiectare** — e exclus structural, nu prin presupunere. `COMPUS`, asa cum il citeste `contabilizeaza_articol` (`:7279-7283`), e definit in `VCRM_POLITICI_PRET_ART` ca proprietate a **perechii (articol, politica)**, identificata prin `ID_POL_ART`. O linie fara politica n-are `ID_POL_ART`, deci intrebarea nu se poate pune. **Golul `ntip = 4` — INCHIS in runda 11.** Cercetat (`docs\cercetare\gol_ntip4_factura_din_avize.md`) si **verificat adversarial** (`docs\cercetare\verif_goluri_ntip_aviz.md`). Trei rezultate, dintre care doua schimba ce trebuie scris in pachet: - **`ntip = 4` e exclus structural — dar nu prin garda pe care o presupunea proiectarea.** Blocajul nu e `FACT-024` din `contabilizeaza_articol`, ci **o functie mai devreme**: `adauga_articol_factura`, ramura `WHEN ntip = 4` (`:5080-5103`), cauta randul sursa din `VANZARI_DETALII` prin `A.ID_POL = V_ID_POL` **fara handler de exceptie**. Cu `V_ID_POL = NULL` — exact cazul liniei fara politica — comparatia nu se poate potrivi niciodata, deci apelul cade cu `ORA-01403` **la adaugarea articolului**, in bucla `do_scrie_articole` (`ofacturare.vc2:13967-14106`), care ruleaza intotdeauna inaintea `Do Case`-ului (`:14282`). Linia nu ajunge niciodata la `contabilizeaza_articol`; ramura noua n-are nimic de tratat acolo, si **nu are nevoie de garda defensiva** (ar fi redundanta peste doua straturi independente). - **GOL REAL, mai ingust decat parea, dar efectiv atins: `SCD` pe avize.** Ramurile de aviz care chiar ajung la `contabilizeaza_articol` — `ntip IN (28,29)` → `SCD = '461'`, restul avizelor „simple" (`21, 22, 24, 27`) → `SCD = '418'` (`:7413-7422`) — **sunt accesibile cu `cont_venit` populat**, pentru ca `adauga_articol_factura` nu cere `id_pol` pe niciunul din ele. Ramura noua, care ia `SCD` din optiunea deciziei 36 (`4111`), l-ar scrie **necontitionat de `ntip`** → **cont contabil gresit pe aviz**. Deci ramura noua **alege `SCD` dupa `ntip`**: `'461'` pentru `IN (28,29)`, `'418'` pentru celelalte avize atinse, si abia pe restul optiunea `RF_CONT_ART_FARA_POL`. **Verificarea a restrans lista:** `ntip IN (23,25,30,41,42,47)` **nu ajung deloc** la `contabilizeaza_articol` — sunt deviate mai devreme, in `scrie_factura2`, spre `transfera_articol` (`23,25,30,41`) sau direct spre `descarca_gestiune` (`42,47`). Golul nu li se aplica. - **Al doilea gol confirmat: garda `ntip = 46`.** `nTipNotaPlata` **este** `46`, iar `scrie_nota` e sarita azi intentionat pentru el (`IF ntip <> nTipNotaPlata`, `:7441`). Un articol fara politica poate ajunge si pe acest tip. **Ramura noua trebuie sa reproduca garda** — altfel scrie o nota care azi e sarita deliberat. **CORECTIE la proiectare — al doilea apelant intern e numit gresit peste tot.** Nu `scrie_factura_avize_retur`, ci **`scrie_factura_avize`** (`:6692-7058`) — care e chiar handler-ul Oracle al lui `ntip = 4`, apelat din `ofacturare.vc2:14318`; apelul catre `contabilizeaza_articol` e la `:6858`, in interiorul lui `IF articole_aviz(j).custodie = 1`. `scrie_factura_avize_retur` (`:6264-6657`) **nu cheama deloc** `contabilizeaza_articol` — insereaza direct in `VANZARI_DETALII_TEMP` din `VANZARI_DETALII`. Iar `scrie_aviz_retur` (`:7085-7171`) **n-are niciun apelant VFP gasit** in tot `COMUN\` si in tot proiectul — posibil cod mort sau apelata din alt produs. Greseala e propagata in `canal_cont_venit_fara_politica.md:67` si `nota_contabila_fara_politica.md:103`; se citeaza de acum lista corectata de mai sus. **Nota de metoda, valabila pentru toate rapoartele:** presupunerea ca liniile din exportul `PACK_FACTURARE` au un **offset constant `+17`** fata de rapoartele vechi e **falsa**. Nu exista offset universal (masurat: `+9` fata de `nota_contabila_fara_politica.md`, `0` fata de alte rapoarte). Numerele de linie se **re-verifica direct pe fisier**, niciodata prin corectie presupusa. **Nu se implementeaza** — decizia 30 sta, #13 incepe dupa #6. **33. Mockup-ul nu se republica la v7.** Se duce la **v8** cu rezultatele verificarilor rundei 9 si abia atunci se republica — o singura citire a HTML-ului, o singura republicare. Url-ul artifact rămâne la v6 pana atunci. ### Deciziile lui Marius, runda 10 (10.08.2026) **35. Un singur cod de scriere contabila: `pack_facturare`, si la emitere si la editare. `oscrie_in_fisiere` NU se foloseste in #13.** Marius, 10.08.2026, textual: *„nu doresc folosirea scrie in fisiere ci doar `pack_facturare` la emitere si la editare pentru ca nu vreau sa intretin doua coduri."* **Ce anuleaza:** impartirea propusa la finalul rundei 9 — „emiterea prin parametrul deciziei 34, editarea prin canalul generic catre `ACT_TEMP`, care nu cere nimic in pachet" — e **respinsa**. Canalul `actactan` / `tact` → `COMUN\programe\oscrie_in_fisiere.prg` → `pack_contafin.SCRIE_IN_ACT` exista si e folosit azi in productie de fluxul de editare al lui #6, dar folosirea lui in #13 ar insemna **doua implementari ale aceleiasi reguli de contare** — una in pachet, pe emitere, alta in VFP, pe editare — tinute in pas manual la fiecare schimbare a regulii deciziei 27. Motivul e de **intretinere**, nu tehnic, si nu se reargumenteaza cu „canalul exista deja". **Ce impune:** editarea prin regenerare (etapa II) scrie pe **exact acelasi drum** ca emiterea — `scrie_factura2` → `contabilizeaza_articol`, cu parametrul de cont de la decizia 34. Nu exista ramura de scriere separata pentru documentul reemis si nu exista editare directa a randului din `ACT_TEMP` din #13. **Ce nu atinge decizia 35:** - **fluxul lui #6** ramane cum e (`ofacturare_comun.vc2:3796-3821` → `frm_modific2024`) — decizia priveste #13; retragerea lui e alt subiect si alt perimetru — **inchis de decizia 38: coexista, se decide dupa ce #13 livreaza**; - **stergerea** documentului vechi din S9 ramane pe drumul existent (`do_sterge` → `oscrie_in_fisiere` + `pack_contafin.finalizeaza_stergere_nota`), pentru ca acolo **nu exista al doilea cod de intretinut**: `pack_facturare` nu are echivalent de stergere a notei, iar drumul de stergere e comun intregii suite. *Presupunere declarata, de infirmat daca Marius vrea si stergerea mutata in pachet — ar fi alt mandat si alta suprafata de risc.* **Ce castiga:** regula deciziei 27 traieste intr-un singur loc, si o corectie pe ea nu trebuie facuta de doua ori. **Ce costa:** etapa II nu mai are cale ieftina — depinde de parametrul deciziei 34 exact cat depinde etapa I, deci **S9 nu poate porni inaintea parametrului**, iar regresia pe suita se plateste o singura data, dar obligatoriu. **36. Cele doua campuri ramase fara sursa pe ramura fara politica — decis.** Verificarea a stabilit intai ca **nu exista in cod nicio sursa alternativa** pentru ele: nici optiune de firma existenta, nici cont pe partener, nici flag de scutire pe articol sau client (`parametru_cont_contabilizeaza_articol.md`, sectiunea 4). Deci sunt decizii de produs, si Marius le-a luat asa: - **`SCD` — fix, dar citit din configurare, nu hardcodat.** O optiune de firma, cu **`4111` ca implicit**. **PROIECTATA** — `docs\cercetare\optiune_firma_cont_debit.md`. **Costul e mult mai mic decat parea: tiparul exact exista deja in productie, in acelasi pachet, pe acelasi camp.** `pack_facturare.scrie_incasare2` (`PACK_FACTURARE:13161-13234`) isi ia `V_SCD` in **trei niveluri**: parametru explicit daca a fost dat → **optiunea de firma** (`RF_CONT_INCASARE_BONFISCAL` s.a.m.d., cate una per tip de incasare) → **constanta hardcodata** daca optiunea lipseste. Se copiaza identic, si se inlocuieste punctual linia `SCD := '4111'` din proiectarea ramurii noi. Mecanismul are 15+ ani: tabelul `OPTIUNI` (458 randuri azi), `PACK_SESIUNE.getoptiunefirma`, si un **ecran de editare complet generic** peste tabel (`frm_optiuni` / `frm_optiuni_nou`, `COMUN\clase\oOptiuni.vc2`) — deci **cheia noua nu cere niciun cod VFP nou**, doar randul in tabel. Nu exista `ID_FIRMA`: fiecare firma are schema Oracle proprie, deci `OPTIUNI` din schema de conexiune **este** deja „optiunile firmei curente". **Dubla plasa de siguranta, si asta acopera integral cerinta lui Marius:** implicitul `4111` traieste si in randul din `OPTIUNI` (scris de migrare, editabil fara recompilare), si hardcodat langa `getoptiunefirma` — deci si o instalare veche fara migrare, si un rand golit din ecran cad tot pe `4111`. `getoptiunefirma` intoarce `''` cand nu gaseste, ceea ce in Oracle e `IS NULL`, deci ambele cazuri se trateaza cu aceeasi conditie. **`ASCD` nu trebuie atins** — se calculeaza deja din `V_SCD`, deci primeste automat valoarea corecta indiferent de sursa. **`PROGRAME` se pune larg, nu doar `ROAFACTURARE`** — intrebarea a ramas deschisa in raport, dar se inchide combinand-o cu masuratoarea de regresie: apelul catre `adauga_articol_factura` traieste in `COMUN\clase\ofacturare.vc2`, fisier prezent si folosit in **toate cele sapte produse**, deci ramura noua e atinsa de toata suita, exact ca `RF_CONT_INCASARE_*` (care listeaza sase produse). - **`CU_TVA` — derivat din cota liniei**, nu fixat: `1` daca `proc_tvav > 0`, altfel `0`. Asta **elimina prin constructie** efectul gasit la verificare: o linie scutita nu mai intra in comparatia de maxim din `nproc_tva_max`, deci nu mai poate imprumuta coloana si taxcode-ul ei liniei de discount a intregii facturi. Pretul e o regula in plus in pachet si un comportament **diferit de ce face azi nota** pentru o linie scutita — diferenta e intentionata, nu accidentala, si se noteaza ca atare la testare. Deci lista parametrilor noi ramane la **unul singur** (`V_CONT_VENIT`): `SCD` vine din configurare, iar `CU_TVA` se deriva in pachet din date deja prezente pe rand. **37. Cheia optiunii si validarea — decise (10.08.2026).** - **Cheia: `RF_CONT_ART_FARA_POL`**, aliniata la familia `RF_CONT_INCASARE_*` — adica exact optiunile care alimenteaza azi `SCD` in acelasi pachet. Cele cinci apar astfel **grupate alaturi in ecranul de optiuni**, ceea ce le face inteligibile impreuna. Incape in `OPTIUNI.VARNAME` (`VARCHAR2(30)`). `VARTYPE = 'CHARACTER'`, `VARVALUE = '4111'`, `PROGRAM = 'ROAFACTURARE'`, `PROGRAME` larg (vezi 36). - **Fara validare de cont**, nici la salvare, nici la citire — se copiaza tiparul existent. Motivul: niciuna dintre optiunile de tip cont nu e validata azi nicaieri, iar `RF_CONT_INCASARE_*` traieste asa de 15 ani; a introduce validare aici ar fi o imbunatatire noua, nu continuarea unui tipar, si ar cere fie cod nou in pachet, fie prima logica per-cheie din ecranul generic `COMUN\clase\oOptiuni.vc2` — fisier al intregii suite. **Garda gratuita ramane lungimea:** un `VARVALUE` peste 4 caractere da `ORA-12899` la `INSERT INTO ACT_TEMP`, deci esec zgomotos, nu cont tacut gresit. *De consemnat la testare ca risc acceptat constient:* un cont inexistent in planul de conturi, scris din greseala in ecran, ajunge pe nota neschimbat — acelasi risc pe care produsul il are deja, nu unul nou. ### Deciziile lui Marius, runda 11 (10.08.2026) **38. Fluxul de editare al lui #6 nu se retrage odata cu #13. Coexista, si se decide mai tarziu.** Intrebarea pusa la finalul rundei 10 — daca decizia 35 („un singur cod de scriere contabila") obliga la rerutarea editarii lui #6 pe acelasi drum — primeste raspuns: **nu acum**. #6 ramane pe editarea directa a randului din `ACT_TEMP` (`ofacturare_comun.vc2:3796-3821` → `frm_modific2024` → `oscrie_in_fisiere`), #13 merge pe regenerare prin `pack_facturare`. Cele doua traiesc separat, pe povesti separate. **Ce inseamna concret:** decizia 35 se citeste strict ca perimetru al lui #13 — nu se proiecteaza si nu se planifica nicio retragere a fluxului lui #6 in aceasta poveste, si nu se adauga in #13 nicio piesa care sa pregateasca acea retragere. Subiectul **nu e inchis definitiv**: se redeschide dupa ce #13 livreaza regenerarea si se vede in practica daca intretinerea celor doua drumuri doare cu adevarat. Pana atunci nu se reargumenteaza in niciun sens. **39. S4 ramane integrala: se desface si registrul de cantitate ramasa.** Proiectarea rundei 11 a aratat ca `crsarticole` nu e doar sursa gridului, ci **registrul cantitatii ramase de facturat** — scris de `do_sterge` la stergerea unei linii (`ofacturare.vc2:14640-14669`) si citit prin `Calculate Sum(cantitate) To lnCantitateRamasa` ca sa se decida **inchiderea automata** a comenzii / avizului (`:14303-14311`, `:14334-14338`). Varianta ieftina ar fi fost sa se restranga S4 la lista de preturi; Marius a ales varianta intreaga: **bookkeeping-ul se decupleaza de cursorul incarcat in masa**, ca sa poata disparea incarcarea si pe comanda si pe aviz. **Consecinta, declarata explicit:** S4 nu mai e o poveste de performanta pe formular, ci atinge **inchiderea automata a documentelor sursa** — cea mai mare suprafata de regresie din etapa I, si singura care poate lasa o comanda deschisa (sau o poate inchide prematur) fara ca operatorul sa vada ceva. Criteriul de „gata" al lui S4 include de acum, obligatoriu, **paritate pe inchiderea automata**: acelasi document sursa, aceleasi linii facturate partial, acelasi `INCHISA` la final, pe fiecare tip cu document sursa. Registrul nou trebuie sa fie sursa unica — nu o a doua copie tinuta in pas cu prima, altfel povestea introduce exact tipul de dublura pe care decizia 35 il refuza in alta parte. **40. Bug-ul de dezalocare POS se repara in S3b, in trecere.** Vezi `L.4`. Consecinta acceptata: diff-ul lui S3b **nu mai e o mutare pur mecanica**, deci testarea lui trebuie sa acopere si dezalocarea POS pe calea veche, nu doar paritatea de emitere. **41. Sectiunea pliata are DOUA comutatoare, nu unul si nu cinci.** Grupul de **incasare** — singurul cu efecte laterale reale (alocare / dezalocare de numere) si singurul blocat pe document emis prin decizia 25 — primeste comutator propriu. Delegat/transport, adresa, text aditional si analiticele stau impreuna sub al doilea. Motivul e izolarea riscului: garda de non-alocare la toggle (`opt_incasat.ProgrammaticChange`) se scrie si se testeaza **intr-un singur loc**, nu pe cinci stari. **42. Validarea de curs valutar se restrange la valuta articolului cautat.** Pe varianta filtrata a cursoarelor, `verifica_cursuri_valute` nu mai ruleaza global, ci doar pe valuta randului adus. **Schimbare de comportament asumata:** un curs lipsa pe **alta** valuta nu mai e semnalat la deschiderea formularului, ci abia cand se ajunge la un articol pe acea valuta. Se consemneaza la testare ca diferenta intentionata fata de azi, nu ca regresie. #### Puncte marunte ramase din proiectarile rundei 11 — se merge pe recomandare daca Marius nu spune altfel Nu blocheaza nimic; se inchid la implementarea povestii respective. Enumerate ca sa nu se piarda intre runde. | # | Punct | Poveste | Se merge pe | |---|---|---|---| | a | Textul exact al tooltip-urilor pentru analiticele read-only si pentru grupul de incasare blocat | S3b | formularea propusa in raport, sectiunile 2.2 si 5.3 | | b | Eager vs. lazy pentru lookup-urile Oracle din `Init` (delegat / masina ultimei facturi, casa) | S3b, S3 | **lazy**, ca sa nu se plateasca la fiecare depliere; deschis si in `s3_portare_antet.md` §5 | | c | `_checkbox1` vs. `chkDetaliat` — care devine campul unic de „listare detaliata" | S3b, S1 | **INCHIS (runda 12)**, pe cod: sunt **doua controale distincte** in `frm_alte_date`, nu o duplicare — nu exista nimic de ales. `docs\cercetare\s4c_discount_in_grid.md`, sectiunea 11 | | d | Forma lui `toSursa`: obiect scatter (duck-typing) sau clasa dedicata | S3c | **duck-typing**, ca azi — o clasa noua n-ar schimba nimic functional | | e | Conversia comenzii si a contractului: un commit sau doua | S3c | **un singur commit** — ating aceleasi trei fisiere comune, separarea nu reduce regresia, doar amana testarea pe ROACONTRACTE | | f | `goComanda = ''` redundant la `Cw3.do_actiune` dupa conversie | S3c | **se lasa**, marcat explicit „intentionat" in diff, ca sa nu para omisiune la review | | g | Contractul are doua meniuri (articole vs. rate): eticheta generica sau diferentiata pe `OPT_FACTURARE` | S4b | **diferentiata** — „Alege ratele de facturat…" e gresita pe contractele cu articole | | h | „Alege facturile de returnat…" ramane doar la antet sau capata incarcare aditiva din bara | S4b | **ramane la antet** in etapa I; mutarea e o poveste separata | | i | `But_renunt1` / `But_reset1` primesc `Caption` pentru consistenta cu bara etichetata | S4b | **da** — decizia 13 cere etichete, nu iconite mute; exceptia ar fi inconsecventa | | j | Ordinea si formularea optiunilor din `xmenu()` per sursa | S4b | tabelul din sectiunea 3 a raportului, ca propunere | | k | UX-ul contractului cu doua surse pe acelasi grid conceptual (`crsarticole` filtrat + `crsarticole1`) | S4 | de confirmat vizual la mockup, nu pe hartie | ### Ce s-a dovedit ca exista deja (deciziile 10, 11, 12, 14) Verificat pe cod la 09.08.2026; rapoartele complete sunt in `docs\cercetare\` (`proforma_copiere_puncte_intrare.md`, `discount_pe_articol.md`, `inventar_controale_formulare.md`, `import_roris_roaacnpro.md`). - **Proforma nu e tip de document si nu are formular propriu.** E valoarea `PROFORMA` din combo-ul „Tip document” (`ct_clb_fdoc._combobox1`, `RowSource = "FACTURA,PROFORMA,BON FISCAL"`, `ofacturare.vc2:8745-8754`), care seteaza `nIdTipDoc = 23` si, prin setter, `eProforma = 1` (`ofacturare_comun.prg:593-599`, `nIdTipDocProforma = 23` la `:104`). Deci intra in formularul unificat **fara nimic de portat**. Specific proformei si de pastrat: serie si numar proprii, realocate la comutarea din combo (`ofacturare.vc2:9415,9427-9428`); **fara nota contabila si fara atasamente**, prin garzile `poDate.eProforma = 0` (`ofacturare.prg:1960,1996,2052,2063,2103`); raport propriu (`:1638-1643`, `COMUN\Rapoarte\proforma.fr2`); relistarea pe cale separata (`ofacturare_comun.vc2:7230-7264`). - **Copierea foloseste deja acelasi formular, cu antetul editabil.** `do_copiaza` -> `copiere_factura` -> `factureaza(tip, toFactura)` -> `frm_date_factura` precompletat de `completeaza_setari_document(toFactura, .T.)` (`ofacturare_comun.prg:362-412`). Numar nou se aloca **intotdeauna**, neconditionat de copiere (`ofacturare.prg:208,211`). Nimic din traseu nu blocheaza controale de antet. - **Butonul de facturare din pagina de comenzi exista deja in ROAFACTURARE**: `ct_comenzi.do_factura` (`ocomenzi.vc2:1580-1596`) face `SCATTER NAME goComanda MEMO` si cheama `facturare_comenzi` -> `factureaza(3)`; butonul `But_factura1` (`ocomenzi.vc2:932-937`) e ascuns cand comanda e deja facturata (`:2199-2203`), si e montat pe Page5 (`ofundal_facturare.vc2:604`, `Ferestre\fundal.sc2:206`). - **Precompletarea contractului vine din programul de contracte** (`ROACONTRACTE`): `ferestre_contracte.vc2:1538-1549` si `:1605` -> `facturare_contracte` -> `factureaza(2/6/52)`, cu contractul deja completat — exact ce cere decizia 12, si **functioneaza azi**. In ROAFACTURARE exista, in plus, calea din meniu **fara** precompletare (`ofundal_facturare.vc2:886-897`), unde utilizatorul alege contractul in formular. Cele doua coexista; #13 nu adauga o lista de contracte in ROAFACTURARE si **nu depinde de #10**. - **Discountul pe articol are deja si procent, si valoare — dar intr-un dialog separat.** Vezi K. - **Deblocarea antetului exista deja**, dar sub forma a patru bife individuale in `frm_modifica_factura`; in formularul unificat ele sunt inlocuite de un singur buton comutator. Vezi I. - **Data cursului valutar nu e conditionata azi de valuta.** Vezi M. ### Canalul de precompletare e un global, nu un parametru `factureaza(tnTip, toFactura)` (`ofacturare.prg:81-82`) nu are parametru pentru sursa: comanda si contractul se transmit prin globalele `goComanda` / `goContract`, citite in `oDateFactura.Init` (`ofacturare_comun.prg:301-328` comanda, `:261-297` contract). De aceea calea generica de comanda e obligata sa scrie explicit `goComanda = ''` inainte de apel (`ofundal_facturare.vc2:899-902`), altfel ar ramane precompletata cu ce era in sesiune. **Calea generica de contract nu face resetarea simetrica** (`:886-897`) — de verificat daca `goContract` poate ramane populat intr-o sesiune si precompleta tacit un document nou. Decizia 12 se implementeaza corect prin **parametru explicit**, nu prin inca un global. ## Relatia cu #6 — transata: #13 incepe dupa #6 `plan_06_editare_factura.md` rezolva **aceeasi problema de fond** (o factura emisa nu se poate corecta fara ca notele si rulajele sa ramana in urma) pe calea opusa: editare directa in `frm_modific2024`, cu `id_fact` pastrat. Planul #6 a respins explicit regenerarea, cu motivul ca *"emiterea face verificari de stoc si alte protectii dependente de tipul documentului; la regenerare acestea ar esua pe date care intre timp s-au schimbat"*. Argumentul acela **cade partial** daca stergerea si reemiterea se fac in aceeasi tranzactie, in ordinea corecta — vezi sectiunea "Stocul" mai jos. Dar nu cade complet: raman verificarile care nu tin de stoc (configurare politica / contract / cota TVA, vezi `adauga_articol_factura`). Cele doua nu se exclud tehnic, dar **se suprapun pe aceleasi fisiere** si, mai important, produc doua semantici diferite pentru "am corectat factura": | | #6 — editare directa | #13 — regenerare | |---|---|---| | Punct de intrare | registru jurnal + formular facturi | formularul de facturi | | Utilizator vizat | contabil | operatorul de facturare | | `ID_FACT` | pastrat prin constructie | **pastrat, prin cerinta** (vezi E) | | Serie, numar, data | neschimbate | neschimbate (cale proprie, vezi F) | | `COD`, `ID_VANZARE` | `COD` nou, `ID_VANZARE` pastrat | ambele noi | | Notele si rulajele | rescrise din formularul de note | **regenerate din articole**, ca la emitere | | Documentul sursa (comanda/aviz/contract) | neatins | **eliberat si reconsumat** | | Efort | mediu (in curs, S1–S3 livrate) | mare | **Decizia lui Marius (09.08.2026): coexista, cu roluri separate.** #6 ramane calea contabilului pentru corectii pe nota — si e singura cale posibila din registrul jurnal din ROACONT/ROAGEST, unde nu exista contextul de facturare (`poDate`, `crsfactura`, politici, serii). #13 devine calea operatorului de facturare: modific documentul, notele si rulajele se refac singure. **Coliziune de lucru: nu exista. DECIS (decizia 30): #13 incepe dupa ce #6 se termina.** Sesiunea de la #6 a atins `COMUN\clase\ofacturare_comun.vc2` (`do_editare_factura`, `:3715-3869`), a creat `COMUN\programe\ofacturare_editare.prg` si a adus `omodificari.vcx` in proiect (`97d1613`) — dar implementarea lui #13 nu porneste cat timp acestea sunt in lucru, deci **nu e nevoie nici de impartire de perimetru, nici de coordonare intre fire.** Cele doua buguri semnalate (L.0 si L.0-bis) raman semnalate catre #6, nu reparate din #13. Ce **nu** decade odata cu coordonarea: **decizia 26** — analiticele raman read-only in #13. Motivul ei e semantic (#6 le editeaza la nivel de linie de nota, #13 le-ar scrie uniform pe tot documentul), nu o problema de calendar. --- ## Ce s-a stabilit din cod ### A. Formularul unificat exista deja ca prototip, oprit din 2017 `frm_facturare_articole2` (`COMUN\clase\ofacturare.vc2:15741-19355`) **este** formularul cerut la punctul 13, la nivel de controale: - are deja campurile de antet mutate din `frm_date_factura`: `Clb_serie_act1`, `Clb_nract`, `Clb_dataact`, `Clb_zi_curs`, `Clb_data_scadenta`, `Ct_clb_nume_client`, `Ct_clb_responsabil`, `Ct_clb_sectie`, `Ct_clb_lucrare`, `Ct_clb_venchelt`, `Ct_clb_altele`, `Ct_clb_valuta`, `clb_fdoc`, `Ed_tx_simplu1` (`:15744-15812`); - **nu** are `grd_articole`, `grd_contracte`, `cb_politici_preturi`, `cb_contracte`, casetele de cautare `txtCodmat` / `txtArticole` — adica exact partea care azi consuma incarcarea in masa; - are grid editabil in linie, cu combouri `combosql` din `cautare.vcx` pe `cCodMat.cboCodmat`, `cDenumire.cCboDenumire`, `cGestiune.cCboGestiune` (`:16751-16881`), plus `But_nou1`; - e lansat de `factureaza2` (`COMUN\programe\ofacturare.prg:590-1080`), care se cheama numai daca `gnFacturareNou = 1` **si** utilizatorul raspunde DA la un `AMESSAGEBOX` (`:87-92`). **Cat de mort e prototipul.** In `factureaza2`, deschiderea formularului de date si incarcarea articolelor sunt inchise cu `If .F.`: `:707-711` (formularul de date), `:826-829` (executia cursorului de articole), `:838` (verificarea de "nu exista articole"), `:985-1008` (`crspolitici` si `crscontracte`). `Init`-ul lui `frm_facturare_articole2` are 92 de linii (`:18988-19080`) fata de 368 la `frm_facturare_articole` (`:14976-15344`) si **nu completeaza niciun camp de antet** — controalele sunt acolo, logica nu. **Drift-ul dintre `factureaza` si `factureaza2`** (fork din 08.06.2017, nesincronizat): lipsesc din `factureaza2` tipurile 51 si 52, ramura `llCopiere` / `toFactura`, completarea `codmatc` / `codnc8` / `codcpv`, `GetInstitutiePublica`, `GetSoldClient`, filtrul `RORTC` pe `jtva_coloane`, tratarea `eProforma`, `verifica_numar(16, ...)` pentru chitanta, `cursor_retur_document`, `cursor_avize` cu tip 23. **Concluzie: nu se continua cu doua proceduri.** Se merge pe una singura, cu un parametru de mod, altfel divergenta se reia imediat (exact tiparul care a produs problema reparata la #8). ### B. Nu doua formulare, ci patru Fluxul de azi are **patru** ferestre. Runda 2 numarase trei; a patra, `frm_articol_factura` (`ofacturare.vc2:1108-2659`), se deschide **pentru fiecare articol adaugat**, din `do_adauga_articol` (`:12873`, `ofrmadarticol.Show(1)`, doar daca `!tlImplicit`) — si e locul in care se introduce azi discountul pe linie (vezi K). Formularul unificat o desfiinteaza, deci campurile ei trebuie sa aiba unde sa se mute. Celelalte trei: 1. `frm_date_factura` (`ofacturare.vc2:8482-9869`) sau `frm_date_aviz` (`:6566-7618`) — 17, respectiv 13 metode `do_cauta_*`, plus `do_schimba_tipdoc`, `inainte_de_do_termin` si un `Init` de 234 / 247 de linii; 2. `frm_facturare_articole` (`:10968-15739`) — compunerea; 3. `frm_alte_date` (`COMUN\clase\ferestre_cere_date.vc2:2219-3353`) — **aratat dupa** confirmarea articolelor, din `inainte_de_do_termin` (`ofacturare.vc2:14921-14941`): delegat, auto, agent, adresa de facturare, incasare (numerar / chitanta / bon fiscal / POS), text aditional. Aloca si numere proprii (bon fiscal, POS, chitanta). Punctul 13 vorbeste doar despre primele doua. **Decizia 7 le aduce pe toate trei**: `frm_alte_date` intra in formular, ca zona pliata. Recomandarea din runda 2 („ramane dialog in prima etapa”) e respinsa. Ce inseamna concret, din inventarul de controale: se muta patru grupuri — delegat si transport (`Ct_clb_delegat`, `Ct_clb_masina`, `Ct_clb_agent`, `Clb_dataora_exp`), incasare (radiogrupul `opt_incasat` cu patru optiuni, `Cb_casa`, `Clb_serie_chit`, `Clb_nrchit`, `Clb_incasat`, `cmdModificaBon`, `chkPOS`, `chkDetaliat`, `cboTipFactura`), adresa de facturare (`clb_adresa_facturare`) si textul aditional (`Ed_tx_simplu1`) — `ferestre_cere_date.vc2:2343-2638`. **Masina de stari se muta ca atare, nu se rescrie**: `actualizeaza_tipincasare` (`:2698-2856`) comuta vizibilitatea pe cele patru valori si aloca sau dezaloca numarul de chitanta (`:2783`), de bon fiscal (`:2807`) sau de POS (`:2844`). Riscul semnalat in runda 2 nu dispare, dar e localizat: **alocarea de numere trebuie sa ramana legata de aceleasi evenimente**, nu de deschiderea formularului. Peste ele coboara analiticele din antet (decizia 8): `Ct_clb_venchelt`, `Ct_clb_sectie`, `Ct_clb_responsabil`, `Ct_clb_lucrare`. ### C. Incarcarea articolelor — de ce dureaza si ce inlocuieste `factureaza` executa, inainte de a deschide compunerea, **un singur apel** care aduce toate articolele disponibile: `pack_facturare.cursor_preturi` (`ofacturare.prg:281-286`, corp la `PACK_FACTURARE:2121+`), respectiv `cursor_contract` / `cursor_comanda` / `cursor_avize` / `cursor_gestiune` / `cursor_retur` dupa tip (`ofacturare.prg:265-311`). Rezultatul intra in `crsarticole` si tine gridul de sus. `cursor_preturi` **nu are filtru pe articol** — semnatura e `(V_DATA_CURS, V_TIP, V_ID_VALUTA, V_ID_GESTIUNE_INIT, V_LUNA, V_AN, V_ID_UTIL, V_ID_SUCURSALA)` (`PACK_FACTURARE:335-343`). De aici vin miile de randuri. Mecanismul de inlocuire **exista deja**, in prototip: `combosql` cauta pe server, incremental, cu `csourcesql = select codmat, denumire, um, grupa, subgrupa, codbare, id_articol from vnom_articole` si `csourcewhere = inactiv = 0` (`ofacturare.vc2:16751-16766`). **Dar `vnom_articole` e nomenclatorul, nu lista de preturi.** Nu are pret, nota contabila, cota TVA, valuta, `id_pol`, `gestionabil`, `pret_cu_tva` — toate vin azi din `cursor_preturi`. Deci partea Oracle a lucrarii e o **varianta filtrata pe articol** a cursoarelor de facturare, apelata la alegerea liniei, nu la deschiderea formularului. Nu e o simplificare: ramurile pe tip din `cursor_preturi` (restaurant, custodie, retur, contract, comanda) trebuie pastrate identic, altfel pretul difera intre cele doua cai. **Legatura cu #12.** Daca articolul poate purta el insusi pretul si nota contabila (varianta C din `plan_12`), cautarea in linie devine directa si nu mai are nevoie de o rezolvare separata a listei de preturi. #13 nu depinde de #12, dar cine face #12 dupa #13 va rescrie exact bucata asta. ### D. Regenerarea — piesele exista, montajul nu **1. Stergerea elibereaza deja documentul sursa.** `pack_facturare.sterge_factura` (`PACK_FACTURARE:5415-5591`) face, pe langa `sters = 1` pe `VANZARI` si `VANZARI_DETALII`: - `FACTURAT = 0, ID_UTILFACT = NULL, DATA_FACTURAT = NULL` pe avizele facturate (tip 4 si 24); - `STERS = 1` pe `VANZARI_CANTITATI` (cantitatile consumate de pe avize); - `STERS = 1` pe `COMENZI_ELEMENTE` cu `CANTITATE < 0` (consumul din comanda); - `STERS = 1` pe `VANZARI_CORESP` si pe `CTR_RATE_FACTURI` (contracte in rate, tip 2/6/52). Adica **exact reversul consumului sursei** — conditia ca reemiterea sa poata reconsuma aceeasi comanda / acelasi aviz / aceeasi rata. Sunt si garzi: refuza stergerea daca s-au emis facturi sau avize de retur peste document (`:5432-5477`). **2. Calea VFP de stergere completa exista.** `frm_facturi.do_sterge` (`COMUN\clase\ofacturare_comun.vc2:4658-4874`): tranzactie manuala, `oscrie_in_fisiere`, apoi `pack_contafin.finalizeaza_stergere_nota` (care cheama `sterge_din_vanzari`). Rulajele si notele dispar pe aceasta cale, nu prin `sterge_factura`. **3. Reconstituirea liniilor exista.** `pack_facturare.cursor_retur_document(..., V_COPIERE = 1, ...)` (`PACK_FACTURARE:3932-4045`) reface liniile din `VANZARI_DETALII`, cu `explicatie`, `id_gestiune`, `pret_achizitie`, `pretd`, `id_jtva_coloana`, `id_jtva_coloana_ex`, `lot`, `serie`, `id_pol`, `pret_cu_tva`, plus `curs` / `multiplicator` din `VANZARI_CURSURI`. E deja folosita, exact asa, la copierea unei facturi. **4. "Redeschide formularul precompletat" exista.** `frm_facturi.do_copiaza` (`ofacturare_comun.vc2:3640-3713`) -> `copiere_factura` (`oproceduri_facturare.prg:150-153`) -> `factureaza(tip, toFactura)`, iar in `factureaza` ramura `llCopiere` (`ofacturare.prg:255-257`, `:458-473`) incarca liniile documentului si adauga peste ele lista de preturi, ca sa se poata adauga articole noi. `oDateFactura.completeaza_setari_document(toFactura, .T.)` (`COMUN\programe\ofacturare_comun.prg:362-412`) precompleteaza antetul. **Ce nu se poate refolosi ca atare:** `do_copiaza` **degradeaza intentionat tipul** — factura din contract / comanda / aviz devine `tip = 1`, avizele devin `22`, transferurile `10` (`ofacturare_comun.vc2:3696-3710`), cu comentariul *"nu are sens sa fie copiata, dar o tratez ca pe o factura din lista de preturi"*. La regenerare, degradarea e **exact ce nu trebuie sa se intample**: documentul nou trebuie sa ramana pe acelasi tip, ca sa reconsume sursa. Deci se cloneaza structura lui `do_copiaza`, nu comportamentul. ### E. `ID_FACT` — cerinta si ce presupune ea **Cerinta (decizia 2):** documentul reemis pastreaza `ID_FACT`. **Starea de azi, verificata:** `ID_FACT` se genereaza cu secventa, neconditionat. `PACK_CONTAFIN.SET_IDFACT(V_GCS)` e `SELECT SEQ_IdFact.NEXTVAL` (`COMUN\docs\PACK_CONTAFIN.pck:3037-3040`), citit cu `get_idFact()` (`:725`), iar `DOCUMENTE` primeste rand nou cu acel `ID_DOC` (`:796-808`). **Dar intuitia „id_fact depinde de numar si data" e corecta istoric:** exista in pachet varianta `SET_IDFACT(tdDataAct, tcSerie_Act, tnNrAct, tnId_Ctr)` care cauta intai in `DOCUMENTE` dupa `NRACT` + `SERIE_ACT` + `DATAACT` + `ID_CTR` si ia secventa doar la `NO_DATA_FOUND` (`:3016-3035`) — **comentata**, deci inactiva. **Ce inseamna pentru #13** (de proiectat in S9, dupa verificarea din handoff): - fie se reactiveaza cautarea, sub un comutator folosit **numai** de regenerare — modificarea lui `SET_IDFACT` fara comutator ar schimba comportamentul pentru toata suita, ceea ce nu se face; - fie se transmite `ID_FACT`-ul cunoscut, ca parametru, pe drumul de scriere; - **capcana de ordine**: cautarea filtreaza `STERS = 0`. Daca documentul vechi e marcat sters inainte, cautarea nu-l mai gaseste si se genereaza id nou. Deci `ID_FACT`-ul se citeste **inainte** de stergere, in aceeasi tranzactie. Cu `ID_FACT` pastrat, ce ramane de inventariat e mult mai putin decat in varianta cu id nou: `ATASAMENTE_VANZARI` si `marcheaza_facturat` merg pe `ID_VANZARE` (care **se schimba**), `ANAF_EFACTURA` e gol prin garda, iar `DOCUMENTE` / `ACT` / `RUL` / `JV2007` / `IREG_PARTENERI` merg pe `ID_FACT` (pastrat). **`ID_VANZARE` nou ramane singura discontinuitate reala** — de verificat daca `DOCUMENTE` primeste un al doilea rand pe acelasi `ID_DOC` sau il refoloseste. ### F. Serie, numar, data — cale proprie, nu regenerare **Cerinta (deciziile 3 si 4):** toate datele se pastreaza, inclusiv numarul; iar antetul care intra in identitatea documentului se modifica pe cale proprie. Calea exista deja si e exact ce trebuie: `pack_facturare.modifica_date_factura` (`PACK_FACTURARE:14392-14463`) face `UPDATE` in loc si **propaga serie / numar / data / scadenta pe `VANZARI`, `DOCUMENTE`, `ACT`, `IREG_PARTENERI`, `JV2007`, `RUL`, dupa `ID_FACT`** (`:14427-14460`). Tot ea scrie delegat, agent, masina, adresa de facturare, text aditional, `dataora_exp`, `listare_detaliata`, `tip_saft`, `efactura`. E procedura din spatele actiunii de azi `frm_facturi.do_modifica` -> `frm_modifica_factura`. **Deci in formularul unificat:** campurile de antet se scriu prin `modifica_date_factura`, nu prin regenerare. Regenerarea porneste doar pentru ce atinge sumele. Vezi tabelul din G-bis. `oGeneratorNumere` (`COMUN\programe\oserii_numere.prg:227-233`) nu incurca: la modificare nu se aloca numar nou, deci nu e nimic de dezalocat. Intrebarea despre o eventuala constrangere de unicitate pe `VANZARI(SERIE_ACT, NUMAR_ACT)` **dispare** — nu mai exista doua documente nesterse cu acelasi numar, pentru ca numarul nu se realoca; ramane doar cazul „vechi sters + nou nesters", care e exact situatia de azi de la orice stergere si reintroducere. ### G-bis. Cele trei modificari de azi, si ce se scrie la confirmare Pe `frm_facturi` exista azi trei actiuni care ating acelasi document, fiecare cu fereastra ei: | Actiune de azi | Formular | Ce atinge | Sursa | |---|---|---|---| | `do_modifica` | `frm_modifica_factura` | antet: ruta, delegat, agent, serie, numar, date | `ofacturare_comun.vc2:4538-4637` | | `do_modifica_explicatie` | `frm_modifica_articol_factura` | `explicatie` + `taxcode` pe o linie | `:4639-4656` | | `do_editare_factura` (#6) | `frm_modific2024` | nota contabila si rulajele | `:3715-3869` | | — | — | **cantitati, preturi, linii: nicaieri** | — | Decizia 6 le comaseaza in formularul unificat. Ruta de scriere se alege dupa ce s-a schimbat: | Ce s-a schimbat | Cum se scrie | Efect | |---|---|---| | Serie, numar, data, scadenta, delegat, auto, agent, adresa facturare, text aditional | pe loc, `modifica_date_factura` | propaga dupa `ID_FACT` | | Explicatia si `taxcode` pe o linie | pe loc, `modifica_explicatie_articol` (`PACK_FACTURARE:14464-14472`) | doua coloane, nicio suma | | Cantitati, preturi, linii adaugate/sterse, discount, gestiune, cota TVA | **regenerare** | vechiul `sters = 1`, documentul nou scris pe drumul de emitere, note si rulaje refacute | | Nimic | nimic | documentul nu se rescrie degeaba | Ultima linie nu e cosmetica: fara ea, orice deschidere a formularului ar produce un document nou si ar umple `VANZARI` cu randuri sterse. **Gol descoperit la S1 (runda 7): tabelul de mai sus nu acopera tot antetul.** Inventarul camp-cu-camp (`docs\S1_inventar_campuri_formular_unificat.md`) a gasit trei grupuri de campuri de antet care **nu sunt printre cei 14 parametri** ai lui `modifica_date_factura` si pentru care nu s-a gasit alta ruta: - **analiticele** — venit/cheltuiala, sectie, responsabil, lucrare (grupul „Pliat — analitice” din I); - **identitate/sursa, altele decat serie/numar/data/scadenta** — tip document, valuta, zi curs, client, sursa/„altele”, gestiune sursa, politica de preturi; - **grupul de incasare** — mod incasare, casa, serie si numar de chitanta/bon, suma incasata, POS. **Verificat (runda 7): `docs\cercetare\rute_scriere_antet.md`.** Cautare exhaustiva in ambele straturi — Oracle (pachetul de facturare curent, plus `PACK_CONTAFIN`, `PACK_UPDATE`, `PACK_MIGRARE`; toate cele 13 aparitii de `UPDATE VANZARI` inspectate individual) si VFP (indexul complet: 316 fisiere, 1128 clase, 9281 metode). Rezultatul e neuniform pe cele trei grupuri: | Grup | Verdict | Ce inseamna | |---|---|---| | **B** — tip document, valuta, zi curs, client, sursa, gestiune sursa, politica de preturi | **NU**, toate sapte | Controalele traiesc **exclusiv** in wizardul de emitere; nu exista niciun `UPDATE` care sa le atinga dupa emitere. Un „nu" verificabil, nu o absenta de cautare. | | **C** — mod incasare, casa, serie/nr chitanta, suma, POS | **NU direct** | Nu exista coloane proprii pe `VANZARI`: incasarea devine ea insasi **o linie de nota contabila** (`scrie_incasare2`). Nicio procedura de tip „modifica incasare". | | **A** — sectie, responsabil, lucrare | **DA** | Dar nu prin `modifica_date_factura`, ci prin **mecanismul lui #6**, aterizat chiar pe branch-ul asta (`97d1613`). Vezi mai jos. | | **A** — venit/cheltuiala (`ID_VENCHELT`) | **neconfirmat** | Gridul afiseaza coloana `dst_chlt`, dar `do_modifica` nu are caz pentru ea. | **Descoperirea care conteaza: analiticele sunt deja editabile, dar din #6.** `frm_facturi.do_editare_factura` (`ofacturare_comun.vc2:3690-3869`) e acum cablat la `frm_modific2024` (`omodificari.vc2`), incarca nota contabila a documentului emis si o rescrie prin `OSCRIE_IN_FISIERE` -> `pack_contafin.finalizeaza_modificare_nota`, care **resincronizeaza `VANZARI`**. `do_modifica` (`omodificari.vc2:13934-13943`) trateaza explicit `id_lucrare` si `id_responsabil`. **Doua nuante care schimba proiectarea, nu doar inventarul:** 1. **Editarea e la nivel de LINIE de nota, nu de antet.** La emitere, cele patru analitice se scriu uniform pe tot documentul, din variabilele de sesiune. Prin editorul lui #6, fiecare linie se poate edita separat — deci un document poate ajunge cu **sectii diferite pe linii diferite**, stare imposibila la emitere. Conteaza pentru orice raportare care presupune „un singur `ID_SECTIE` per factura". 2. **Bug suspectat pe `sectie`** (`omodificari.vc2:13941`): `replace ... id_valuta with loCauta.id_sectie` — pare sa scrie in `id_valuta` in loc de `id_sectie`. Daca e real, textul afisat se schimba dar coloana nu. **E in perimetrul lui #6** — se semnaleaza, nu se repara aici. Pana la verificare, „DA" pentru `ID_SECTIE` e un da cu rezerva. **Consecinta pentru decizia 9**, acum pe fapte: `but_modifica` **poate salva pe loc doar cei 14 parametri**. Pentru grupurile B si C nu exista alta cale decat **regenerarea**, care le rescrie oricum pe toate, fiind chiar drumul de emitere. Pentru grupul A exista o a treia cale, dar e a lui #6 si lucreaza pe alt nivel (linie de nota). **Tabelul de rutare, completat (deciziile 25 si 26):** | Ce s-a schimbat | Cum se scrie | |---|---| | cei **14 parametri** (serie, numar, data, scadenta, ruta, delegat, masina, agent, `dataora_exp`, adresa facturare, text aditional, `listare_detaliata`, `tip_saft`, `efactura`) | **pe loc**, `modifica_date_factura` | | explicatia si `taxcode` pe o linie | **pe loc**, `modifica_explicatie_articol` | | cantitati, preturi, linii, discount, gestiune, cota TVA | **regenerare** | | **grupul B** — tip document, valuta, zi curs, client, sursa, gestiune sursa, politica de preturi | **regenerare** (decizia 25); blocate in etapa I | | **grupul C** — incasare | **regenerare** (decizia 25); blocate in etapa I | | **grupul A** — venit/cheltuiala, sectie, responsabil, lucrare | **niciuna din #13** — read-only, editarea e a lui #6 (decizia 26) | | nimic | nimic | ### G. Stocul — de ce argumentul lui #6 nu inchide subiectul Verificarea de stoc **nu** e in `adauga_articol_factura`: acolo erorile sunt de configurare ("Nu a fost gasita cota de TVA", politica / contract lipsa — `PACK_FACTURARE:5125-5180`). Descarcarea efectiva de gestiune se face la emitere, in `contabilizeaza_articol` -> `descarca_gestiune` (`:7631+`). Plafonul pe cantitate pe care il vede utilizatorul azi vine din **cursorul client** `crsarticole` (`frm_facturare_articole.do_adauga_articol`, `ofacturare.vc2:12813-13086`) — cursor care in formularul unificat **nu mai exista**, pentru ca nu se mai incarca in masa. Deci, pe #13: - in timpul compunerii **nu exista plafon** — coincide cu decizia deja luata de Marius pe #6 la 08.08.2026 ("editarea unei facturi emise nu are plafon si nu verifica stocul"); - la finalizare, daca **stergerea ruleaza inaintea reemiterii, in aceeasi tranzactie**, stocul consumat de documentul initial e deja eliberat cand se descarca gestiunea pentru documentul nou. **Aici e riscul tehnic principal.** Azi stergerea si emiterea deschid fiecare propria tranzactie (`do_deschide_tranzactie` / `do_inchide_tranzactie` in ambele cai). O tranzactie nu poate fi tinuta deschisa peste minutele in care utilizatorul editeaza formularul. Ordinea corecta e deci: **citire (fara tranzactie) -> editare -> o singura tranzactie la confirmare, care contine intai stergerea, apoi scrierea.** Asta cere mutarea apelului de stergere in interiorul tranzactiei deschise de `do_scrie_articole`, nu inaintea ei. Atentie si la `VANZARI_DETALII_TEMP`, care e GTT `ON COMMIT DELETE ROWS`: umplerea si consumul trebuie sa ramana in aceeasi tranzactie — ceea ce e compatibil cu schema de mai sus, dar interzice un `commit` intermediar dupa stergere. ### H. Pretul rescris de server la reemitere `adauga_articol_factura` (`PACK_FACTURARE:4972-5268`) ramifica pe `V_OPT_FACTURARE` si, pe unele ramuri, **re-deriva pretul, cota TVA si valuta din documentul sursa** (ex. `V_OPT_FACTURARE = 3`, preturile de pe contract, `:5129-5168`), in timp ce pe ramura implicita valoarea venita din VFP castiga (`V_PRET := V_PRET_TEMP`, `:5183-5186`). Planul #6 semnalase deja acelasi lucru. **Consecinta pentru #13:** pe facturile din contract (si posibil pe alte ramuri), pretul editat de utilizator poate fi suprascris tacut la reemitere. De inventariat ramura cu ramura, si de decis daca regenerarea trece un flag "preturile vin din formular, nu se re-deriva". **Nu se porneste implementarea inainte de acest inventar** — e singurul punct din care poate iesi o factura reemisa cu alte sume decat cele confirmate pe ecran. ### I. Asezarea antetului (deciziile 8 si 9) **Doua grupuri vizibile, restul pliat.** Grupurile si etichetele reale, din inventar: | Grup | Controale | Note | |---|---|---| | Identitatea documentului | `Ct_clb_fdoc` („Tip document”: FACTURA / PROFORMA / BON FISCAL), `Clb_serie_act`, `Clb_nract`, `Clb_dataact`, `Clb_data_scadenta`, `Clb_zi_curs`, `Ct_clb_valuta` | scadenta e **dezactivata**, nu eliminata, cand `gnScadentaAutomata = 1` (`ofacturare.vc2:9713-9715`); ziua de curs se elimina pe retur (`:9717-9722`, tipurile 8 si 9 — nu si 24); valuta se elimina cand `in_valuta = 0` (`:9725-9728`) | | Partener si sursa | `Ct_clb_nume_client`, `txtCodFiscal` + `But_verifica1` (ANAF), `txtSoldLei`, `Ct_clb_altele` (sursa), `Ct_clb_gestiune_init`, pe aviz `Ct_clb_politici_preturi` | codul fiscal si soldul sunt read-only si derivate | | **Pliat** — analitice | `Ct_clb_venchelt`, `Ct_clb_sectie`, `Ct_clb_responsabil`, `Ct_clb_lucrare` | responsabilul si gestiunea sursa se elimina azi cand `gnScadereStoc = 0` sau tipul e 4/7/8/9/48/49 (`ofacturare.vc2:9646-9707`) | | **Pliat** — alte date | cele patru grupuri din `frm_alte_date`, vezi B | | **Butonul de modificare a antetului (decizia 9).** Mecanismul **exista deja**, si e mai fin decat credeam: `frm_modifica_factura` (`ofacturare_comun.vc2:5262-5774`) are **patru bife individuale** — `chkSerieAct`, `chkNrAct`, `chkDataAct`, `chkDataScad` — fiecare deblocand exact textbox-ul ei (`thisform.txtDataAct.Enabled = this.Value`, `:5758-5772`), si toate patru sunt ele insele inactive daca documentul nu are numar (`this.chkSerieAct.Enabled = !EMPTY(NVL(poRec.numar_act,0))`, `:5736-5750`). **Restul campurilor de antet nu sunt blocate deloc** — delegat, agent, masina, ruta, adresa, dataora expediere, text aditional sunt mereu editabile. In formularul unificat (**decizia 9, forma finala — un singur buton comutator, fara nicio bifa**): - in bara panoului de antet sta **un singur `but_modifica`**; apasat, deblocheaza **tot antetul** — si campurile „moi”, si cele patru de identitate — si **isi schimba imaginea in discheta lui `but_salvare`**; documentul deschis pentru consultare ramane inert; - **cele patru bife individuale dispar.** Nu se transpun in formularul unificat sub nicio forma: butonul e singurul control de protectie a antetului; - **si conditia lor se abandoneaza.** Azi bifele de serie si numar sunt ele insele inactive dupa `!EMPTY(NVL(poRec.numar_act,0))` (`:5736-5750`); regula asta nu se mai transpune pe campuri. Cu antetul deschis, **toate campurile de antet sunt editabile, fara exceptie si fara conditie de stare**. E o pierdere deliberata de protectie, in schimbul unei singure reguli inteligibile: antet blocat sau antet deschis, atat; - a doua apasare **salveaza doar antetul**. E posibil pentru ca `do_modifica` cheama azi **exclusiv** `pack_facturare.modifica_date_factura`, cu 15 parametri toti de antet, si niciun apel spre articole, note sau `oscrie_in_fisiere` (`ofacturare_comun.vc2:4587-4630`). Acopera cazul „vreau sa corectez doar delegatul”. **Precizare, ca sa nu se citeasca gresit:** „nu atinge notele si rulajele” e adevarat **la nivel de apel VFP**, nu si in Oracle. Corpul procedurii propaga seria, numarul si datele in `JV2007` si `RUL` (`PACK_FACTURARE.pck:14428-14459`) — coloane de **identitate**, nu sume: nicio valoare nu se recalculeaza. Pentru celelalte zece campuri, scrierea e strict in `VANZARI`. Vezi I-bis. - **`Termina` ramane salvarea intregului document**, cu rutarea din G-bis. **Ce nu exista si trebuie scris:** containerele efectiv folosite in antet nu au un mecanism gata facut de blocare. `ct_clb_cautare` are `do_activeaza` / `do_dezactiveaza` (`caut_ora.vc2:780-806`), dar acestea doar ascund iconita de cautare, iar in `ofacturare_comun.vc2` nu sunt apelate nicaieri; `clb_tx_simplu` (`lb_tx.vc2:551-585`) si `clb_serie_act` (`serii_numere.vc2:7`) **nu au deloc** metode de activare. Singura reteta completa din biblioteca e `clb_tx_data.dezactiveaza()` / `reactiveaza()` (`lb_tx.vc2:521-538`: `ReadOnly` + `TabStop` + butonul de calendar) — se generalizeaza de la ea, nu se inventeaza alta. Decizia 5 ramane in picioare: **asezarea e identica** la introducere si la modificare; difera doar starea antetului. ### I-bis. Contractul lui `modifica_date_factura` (decizia 19) Raport complet: `docs\cercetare\modifica_date_factura_parametri.md`. Spec `PACK_FACTURARE.pck:921-935`, corp `:14392-14462`, singurul apel VFP la `ofacturare_comun.vc2:4599-4614`. Procedura are **doua regimuri de scriere**, si diferenta dintre ele decide cum se cheama din formularul unificat: | Parametri | Regim | Unde ajung | |---|---|---| | `V_ID_VANZARE` | doar `WHERE` | identitatea randului; sursa lui `lnIdFact` (`:14424-14426`) | | `V_ID_RUTA`, `V_ID_DELEGAT`, `V_ID_AGENT`, `V_ID_MASINA`, `V_DATAORA_EXP`, `V_ID_FACTURARE`, `V_LISTARE_DETALIATA`, `V_TEXT_ADITIONAL`, `V_TIP_SAFT`, `V_EFACTURA` | **scrise neconditionat**, inclusiv cu `NULL` | `VANZARI`, randul curent (`:14414-14423`) | | `V_DATA_ACT`, `V_DATA_SCAD`, `V_NUMAR_ACT`, `V_SERIE_ACT` | scrise **doar daca** sunt `NOT NULL` **si** difera de valoarea curenta | `VANZARI` + propagare in `DOCUMENTE`, `ACT`, `IREG_PARTENERI`, `JV2007`, `RUL` (si `RUL.DATAOUT`), toate **dupa `ID_FACT`**, nu dupa `ID_VANZARE` (`:14428-14459`) | **Consecinta 1 — apelul trimite intotdeauna antetul intreg.** Cele zece campuri din regimul neconditionat se scriu cu ce primesc, deci un apel cu `poRec` completat partial **goleste** in baza campurile netrimise. Butonul de antet nu poate trimite „doar ce s-a schimbat”: incarca antetul curent, aplica modificarile peste el, trimite tot. **Consecinta 2 — cele patru campuri de identitate ating cinci tabele.** Propagarea dupa `ID_FACT` inseamna ca schimbarea seriei sau a numarului dintr-un document atinge toate randurile legate de acelasi `ID_FACT`. E acelasi mecanism pe care se sprijina S9 (reemiterea cu `ID_FACT` pastrat) si acelasi motiv pentru care ordinea operatiilor conteaza acolo. **Consecinta 3 — un camp nou.** `V_EFACTURA` se scrie neconditionat dar nu are niciun control in niciun formular; azi valoarea vine din `Scatter` sau e fortata `0` (`ofacturare_comun.vc2:4576`). In antetul unificat primeste control, langa `V_TIP_SAFT` (ambele tin de raportare, nu de document). **Unde stau cele 14 in formularul unificat** (raportat la gruparea din I): cele patru de identitate in antetul vizibil; ruta, delegatul, agentul, masina, `dataora_exp` si adresa de facturare in sectiunea pliata, la „delegat si transport” / „adresa de facturare”; `text_aditional` si `listare_detaliata` tot in pliat; `tip_saft` si `efactura` formeaza acolo un **grup nou, de raportare**. Niciunul dintre cei 14 nu vine azi din `frm_alte_date` — clasa aceea nu e implicata deloc in fluxul `do_modifica`, deci sectiunea B si I-bis nu se suprapun. ### J. Butoanele de linie si adaugarea din sursa (decizia 13) **Cum sunt azi.** In `frm_facturare_articole` butoanele sunt imprastiate lateral, intre cele doua griduri, si sunt iconite de 30x27 fara text: `But_modifica1` / `But_sterge1` la 773/61 si 811/61, `But_reset1` la 303/295, `But_urmator1` la 343/348, `But_urmator_tot1` la 343/378, `But_retur` la 343/407 (`ofacturare.vc2:11192-11257`). **`But_urmator_tot1` — „adauga tot” — nu are nici caption nici `ToolTipText`** (`:11257`); metoda lui, `do_adauga_tot` (`:13169-13198`), face SCAN peste `crsarticole` si cheama `do_adauga_articol(.T.)` pe fiecare linie. **In prototip sunt deja unde trebuie**: `But_nou1` si `But_sterge1` la `Top = 175`, gridul la `Top = 204` (`:15936`, `:15955`) — adica **deasupra tabelului**, exact cum cere decizia 13. `But_nou1.Click -> do_adauga` (`:17118-17122`) e `APPEND BLANK` + focus in grid. **Ce lipseste: contractele.** `But_urmator_tot1` e vizibil pe `eProforma = 1`, `lCopiere`, tip 3 (comanda), tip 4 (din avize), 21/28/42/47, 25, 8/9, 24 (`:15113-15245`). **Tipurile de contract (2, 6, 26, 52) nu sunt in lista.** Deci „adauga tot” acopera deja avizele, dar nu contractele. **Alegerea selectiva — tiparul importului RORIS.** In ROAACNPRO, `cmdImport` („Import Roris & Contracte”, `oacnpro.vc2:6894-6905`) face exact ce s-a cerut aici, si merita copiat ca structura: 1. **buton activ conditionat** de document editabil **si** tip care are sursa (`llFacturaEditabila and llRorisSauContracte`, `oacnpro.vc2:8117-8145`); 2. **o metoda unica de intrare** care ramifica pe tip (`do_executa` -> `factura_import`, `proceduri_acnpro.prg:3151-3212`); 3. **dialog modal de selectie** cu coloana de bifat si criterii de cautare (`frm_tranzit`, coloana `cAles` legata de `crsConvoaie.ales`, `oacnpro.vc2:14500-14515`, `:14801`), urmat de un al doilea nivel de calcul / confirmare; 4. **populare aditiva** prin `INSERT INTO` in cursorul local de articole, fara nicio procedura Oracle — pachetele intra abia la salvare (`proceduri_acnpro.prg:3331-3467`); 5. **protectie minimala la dublu-import**: butonul se dezactiveaza dupa un import reusit (`oacnpro.vc2:7827-7834`), plus avertisment neblocant daca sursa a mai fost folosita (`:14915-14930`). Ce **nu** se copiaza: sursele de date si calculele ACN (`ips_*`, tarife, ecluzari), formularele lor, si valorile `poDate.cTip`. Ce trebuie facut mai bine: la zero rezultate, dialogul RORIS nu spune nimic — butonul pare ca nu face nimic (`oacnpro.vc2:14898-14943`). **Bara propusa, deasupra gridului (decizia 13):** linie noua · sterge linia · detalii linie ‖ **un singur buton „Adauga articole”**, care deschide un `xmenu()` cu optiunile potrivite sursei — nu doua butoane separate. Tiparul e deja folosit in produs: `frm_facturi.but_modifica1` -> `inainte_de_do_modifica` -> `xmenu("Modificare date factura;Editare factura (articole, cantitati, preturi)")` (`ofacturare_comun.vc2:4925-4934`). | Sursa documentului | Optiunile din meniu | |---|---| | comanda | Adauga tot din comanda · Alege din comanda… · Cauta in lista de preturi… | | contract | Adauga tot din contract · **Alege ratele de facturat…** · Cauta in lista de preturi… | | avize | Adauga tot din avize · Alege avizele… · Alege liniile din avize… · Cauta in lista de preturi… | | lista de preturi | Cauta in lista de preturi… · Retur de articole… | | retur (tip 8, 9, 24) | Alege facturile de returnat… · Alege liniile de returnat… · Cauta in lista de preturi… | **Constanta meniului (deciziile 16 si 18): ultimele doua optiuni sunt aceleasi peste tot** — **„Cauta in lista de preturi…”** (cu pret din politici) si **„Alege din nomenclator…”** (cu pret tastat, ca „Alte servicii” din ROAAUTO, O-bis). Apar inclusiv pe documentele cu sursa, pe cele de retur si pe cele deja emise care se modifica. Sursa umple documentul, nu il inchide. **Decizia 20 (Marius, 09.08.2026): din nomenclator se pot alege si articole gestionabile, si negestionabile.** Nu se preia filtrul `in_stoc = 0` al lui ROAAUTO — acolo e o ocolire a subiectului gestiunii, nu o regula de produs. Consecinta directa: pe documentele auto vor putea aparea linii care descarca stoc langa linii care nu descarca, caz care azi nu exista nicaieri (O-bis, intrebarea 2). #### J-bis. Contul de venit al liniei — intrebarea lui Marius era corect pusa Doua cercetari, a doua o corecteaza pe prima. **Valabil e al doilea raport:** `docs\cercetare\cont_venit_corespondente.md`. Primul (`cont_venit_articol_fara_politica.md`) ramane util pentru contul de **gestiune**, dar concluzia lui pe venit e gresita si nu se citeaza. **Ce a gresit primul raport.** A cautat literalii `707` / `704` / `706` / `708` in `pack_facturare`, nu i-a gasit (corect — nu sunt hardcodati nicaieri) si a conchis ca **nu exista cont de venit pe linie**. Exista. E doar **indirect**, si sta chiar in `pack_facturare`, nu intr-un pachet de contabilitate separat. **Sunt doua conturi pe aceeasi linie de vanzare, cu surse complet diferite:** | | Contul de **gestiune** | Contul de **venit** | |---|---|---| | Unde ajunge | `VANZARI_DETALII.CONT` (`varchar(4)`) | `SCC` in randul de nota contabila | | De unde vine | `NOM_ARTICOLE.CONT` / `STOC.CONT` / `NOM_GESTIUNI.CONT` | `NOTE_CONTABILE.SCC` | | Prin ce | alegerea lotului (`do_alege_stoc`) | **politica de pret** a articolului | | Cine il consuma | `descarca_gestiune` (`ff_...:7485-7507`) | `scrie_nota` (`ff_...:7452-7476`) | Lantul care aduce venitul, in `pack_facturare.contabilizeaza_articol` (`ff_...:7182-7556`, cursorul `cursor_articol` la `:7227-7280`): **`CRM_POLITICI_PRET_ART` -> `CRM_POLITICI_PRETURI.ID_NOTA` -> `CRM_NOTE_VANZARI.ID_SET` -> `NOTE_CONTABILE.SCD` / `SCC`** Pentru o factura normala (`ntip <= 20`), `SCD` e contul debitor (creanta) si **`SCC` e contul de venit efectiv** al liniei (`ff_...:7407-7437`). Nu e derivat din nimic: perechea SCD/SCC e **scrisa manual de un contabil**, per sablon de nota, din ecranul „Configurare note contabile" (`COMUN\ferestre\frm_config_note_contabile[2007].sc2`, clasa `onote_contabile.vc2:2401-2435`, salvare la `:315-322`, `:486-493`). **Nu exista tabel de corespondenta 3xx -> 7xx.** Cautat in `SCRIPTURI_CLAR` sub toate numele plauzibile — negativ. Ipoteza lui Marius era corecta ca **intentie** (exista un mecanism care leaga articolul de contul de venit), dar mecanismul e **configurare manuala pe politica de pret**, nu derivare automata din contul de stoc. **Consecinta care conteaza pentru #13 — si care valideaza intrebarea initiala a lui Marius.** Daca contul de venit vine **prin politica de pret**, atunci un articol adaugat **direct din nomenclator, fara politica** (exact ce cere decizia 16 / 20) nu are `ID_POL`, deci cursorul de mai sus — filtrat `WHERE A.ID_POL = detalii_articol.id_pol` — nu intoarce niciun rand, si `SCD` / `SCC` raman `NULL` (sunt `LEFT JOIN`-uri). **Intrebarea „cu ce cont de venit intra un articol fara politica de pret" era deci exact intrebarea corecta.** #### J-ter. Raspunsul: nu „cu niciunul”, ci **eroare** — si nu exista precedent Verificat: `docs\cercetare\nota_contabila_fara_politica.md`. Doua rezultate, amandoua mai dure decat se anticipase. **1. Codul nu scrie tacut un cont gol — refuza documentul.** `contabilizeaza_articol` cauta politica articolului **inainte** de cursorul care aduce `SCC` (`ff_...:7286-7311`), si pe `NO_DATA_FOUND` ridica `RAISE_APPLICATION_ERROR` cu **`FACT-024`** („Articolul … nu este definit in politica de preturi …”). Cu `id_pol` `NULL`, `ID_POL = detalii_articol.id_pol` nu poate potrivi nimic, deci **mereu** se intra pe exceptie. Cursorul cu `SCC` nu se mai deschide. Nu exista ramura de default, nu exista fallback, si `scrie_nota` (`ff_...:12338-12570`) **nu are nicio validare pe cont** — dar nici nu ajunge sa fie chemat. Concluzie: o linie fara politica trecuta prin codul de azi **opreste tranzactia cu eroare**, nu produce o nota incompleta. Din perspectiva datelor e vestea buna; din perspectiva lui #13, e blocantul dur. **2. „Alte servicii" din ROAAUTO NU e precedent** — premisa pe care se sprijinea decizia 18 cade. Liniile acelea chiar ajung cu `id_pol` gol (confirmat: nici `crsalteserv`, nici `crsvanztemp`, nici semnatura lui `adauga_articol_factura_deviz` nu au `id_pol`, iar `INSERT`-ul in `VANZARI_DETALII_TEMP` nu include coloana). **Dar ele nu trec niciodata prin `contabilizeaza_articol`**: fluxul ROAAUTO cheama `initializeaza_date_factura` -> `adauga_articol_factura_deviz` -> `scrie_in_vanzari` -> `pack_auto.actualizeaza_deviz` (`oproceduri_devize.prg:1211-1310`), iar `scrie_in_vanzari` (`ff_...:13497-13962`, citit cap-coada) **nu genereaza nicio nota contabila de venit** — copiaza `ID_POL` (deci `NULL`) direct in `VANZARI_DETALII` si actualizeaza totalurile. Deci ROAAUTO nu demonstreaza ca articolele fara politica sunt tolerate; demonstreaza ca **exista o cale paralela de facturare care ocoleste contabilizarea de venit prin `pack_facturare`**. Unde capata acele documente contul de venit — daca il capata — **nu se vede in codul cercetat**; `actualizeaza_deviz` presupune deja existenta unui rand in `ACT`, a carui sursa nu a fost gasita. **3. Apelantii sunt trei, si acopera fluxul principal** (lasat neverificat de raportul 2): `scrie_factura2` (`ff_...:6150`) — factura normala si majoritatea tipurilor —, `scrie_factura_avize_retur` (`:6867`) si `scrie_aviz_retur` (`:7149`). Nu exista al patrulea. **4. `NOM_ARTICOLE.CONT` ca sursa de venit: NU.** Grep pe `NOM_ARTICOLE.CONT` in tot pachetul — zero potriviri. `V_SCC` vine exclusiv din `D.SCC` al lantului de politica. Varianta propusa de Marius nu exista nici macar ca ramura moarta. **Ce inseamna asta pentru perimetru.** „Alege din nomenclator…" nu mai e o adaugare de UI peste un mecanism existent: **cere modificare in `pack_facturare`**, adica in COMUN, cu impact peste toata suita — sau evitarea completa a lui `contabilizeaza_articol`, ca la ROAAUTO, ceea ce inseamna document fara nota de venit. **Aceasta e o schimbare de cost, nu un detaliu**, si se decide de Marius (vezi intrebarea deschisa de mai jos). **`NOM_ARTICOLE.CONT` nu e restrans la clasa 3** — confirmat: `verific_cont` (`COMUN\programe\oproceduri_comune.prg:2389-2415`) verifica doar ca acel cont exista in planul de conturi al anului (`vplcont_sintetic`), fara filtru de clasa; nu exista `CHECK CONSTRAINT` pe coloana si nici vreo ramificare pe `Left(cont,1)` aplicata articolelor. Deci un articol negestionabil **poate** purta legitim un cont 6xx / 7xx, cum a spus Marius. **Dar codul de azi nu-l foloseste asa**: acel camp merge in `VANZARI_DETALII.CONT` si de acolo la `descarca_gestiune`, niciodata la `SCC`. Ca sugestia lui Marius sa devina comportament, ar trebui **cod nou in `pack_facturare`** — adica in COMUN, cu impact peste toata suita. *Vezi intrebarea deschisa de mai jos.* **`ID_VENCHELT` / `NOM_VENIT_CHELTUIELI` nu e sursa contului.** Tabelul nu are coloana de cont; `ID_VENCHELT` se rezolva separat (`NVL(nid_venchelt, NVL(A.ID_VENCHELT, C.ID_VENCHELT))`, `ff_...:7205`) si pleaca la `scrie_nota` ca **parametru propriu**, alaturi de `SCD`/`SCC` — e o a treia dimensiune analitica (centru de venituri/cheltuieli, pentru raportare), nu contul insusi. Presupunerea din runda 7 ca „venitul vine din `Ct_clb_venchelt`" **se retrage**. De unde vine `CONT`, pe cele doua cai: | Cale | Sursa contului | Fallback | |---|---|---| | **gestionabil, cu stoc** — trece prin `do_alege_stoc` (`ofacturare.vc2:13239-13345`) | `STOC.CONT`, de pe lotul ales (`cursor_gestiuni_articol`) | **niciunul** — lot cu `CONT` gol ramane `NULL` | | **gestionabil, fara stoc** (`RF_FACTURARE_FARA_STOC = 1`) | `NOM_GESTIUNI.CONT` -> ultimul `STOC.CONT` -> `'371'` hardcodat (`cursor_gestiuni_articol_stoc0`) | singurul loc din pachet cu cascada si default | | **negestionabil** — `frm_articol_factura` direct (`:12871-12880`) | `NOM_ARTICOLE.CONT`, citit la cautarea articolului | **niciunul**; gol -> sentinela `'XXXX'` -> `NULL` la INSERT | `adauga_articol_factura` **nu recalculeaza** contul: il primeste ca parametru `V_CONT` si il scrie ca atare. **Nu exista nicio exceptie pe `CONT`** in tot pachetul — spre deosebire de cota de TVA, unde lipsa produce explicit `FACT-012` / `FACT-013` / `FACT-018`. O linie fara cont se scrie tacut. **Precedentul ROAAUTO nu ofera o regula, ci absenta ei.** „Alte servicii” (O-bis) trimite literal `''` catre `adauga_articol_factura_deviz` (`oproceduri_devize.prg:1256`), care ajunge `NULL` in Oracle, fara `NVL` si fara fallback. Liniile de articole reale din ROAAUTO **ajung deci cu `CONT = NULL`**. **Decizia 21 (Marius, 09.08.2026)** acopera **contul de gestiune**: cand `NOM_ARTICOLE.CONT` e gol pe un articol negestionabil ales din nomenclator, se aplica un **fallback la un cont implicit** — nu se accepta `NULL` ca la ROAAUTO, nu se refuza adaugarea. Precedentul de forma exista in pachet: `cursor_gestiuni_articol_stoc0` cade in cascada pana la `'371'` hardcodat. Contul anume ramane de ales la implementare. **Decizia 21 nu rezolva insa contul de venit** — sunt doua campuri diferite, cum arata tabelul de mai sus; la momentul cand a fost luata, distinctia nu era inca stabilita. ~~**Decizia 24: articolul ales din nomenclator primeste `id_pol`-ul unei politici de pret implicite.**~~ **RETRASA in runda 8.** Argumentul ei — zero cod Oracle nou, COMUN neatins — ramane valabil ca argument, dar pretul lui era o intrebare de configurare cu contabilul (care politica, cu ce `SCC`, una sau mai multe) si un cont de venit uniform pentru orice articol adaugat asa. Marius a ales in loc derivarea contului. **Nu se reintroduce.** *Vezi J-quater.* Ramane valabil de aici, si e important: **alegerea politicii de catre operator** a fost si ea respinsa — muta costul pe fiecare adaugare si cere operatorului sa stie ce inseamna o nota contabila. **Ce se stie sigur pana atunci:** decizia 16 si decizia 20 **nu sunt anulate** de subiectul asta — pe articolele **cu** politica de pret (lista de preturi, comanda, contract, retur) contul de venit vine ca azi si nimic nu se schimba. Blocajul e strict pe ramura **„Alege din nomenclator…"**, adica pe partea din S4g care adauga articole fara politica. **Unde e efectiv de lucru pentru asta:** doar pe **comanda**. Verificat (`docs\cercetare\retur_si_lista_preturi.md`, B): pe contract, `crsarticole` contine deja lista de preturi intreaga, deci adaugarea libera **merge azi**; pe comanda, `cursor_comanda` umple `crsarticole` strict cu articolele comenzii (`ofacturare.prg:266-308`), deci nu merge. Restrictia **nu** e pe buton si nu e la validare — e in continutul cursorului. Se rezolva ca la copiere: `APPEND FROM` peste `crsarticole` cu rezultatul lui `cursor_preturi` (`ofacturare.prg:454-473`). #### J-quater. Deciziile 27 / 27-bis — contul de venit derivat, aplicat din VFP > **DEPASITA IN PARTE — de citit cu avertismentul asta in fata.** **Decizia 34** relaxeaza 27-bis > (`contabilizeaza_articol` primeste parametru de cont) si **decizia 35** cere ca `pack_facturare` sa fie > singurul cod de contare, si la emitere si la editare. Prin urmare: > - **punctul 1** (de ce ROAACNPRO si contractul nu sunt contraexemple) si **punctul 2** (derivarea > contului din `CORESP_CONT_VENCHELT` — regula deciziei 27) **raman valabile integral**; > - **punctul 3, „reteta in 4 pasi"** — politica tehnica per `SCC`, interogarea inversa, > `pack_preturi.adauga_politica_pret_art`, inserarea articolului in politica — e **ABANDONAT**. Se > pastreaza doar ca trasabilitate a rationamentului. **Nu se implementeaza si nu se reargumenteaza.** > - **cele trei variante** de la finalul sectiunii (reteta / nota pe antet / nota pe antet ca fallback) > sunt **caduce**: alegerea a fost facuta prin decizia 34. > > Proiectarea in vigoare e parametrul deciziei 34 — `docs\cercetare\parametru_cont_contabilizeaza_articol.md`. Raport: `docs\cercetare\coresp_cont_venchelt.md`. Trei lucruri s-au stabilit, in ordinea in care conteaza. **1. Intrebarea lui Marius („in ROAACNPRO si pe factura din comanda / contract se adauga articole fara politica — de ce nu si aici?") a primit raspuns: observatia e reala, dar niciunul din cele doua exemple nu e un contraexemplu.** `FACT-024` ramane blocantul. - **ROAACNPRO nu foloseste deloc `contabilizeaza_articol`.** Salvarea facturii ACN (`proceduri_acnpro.prg:3331-3467`) merge pe `initializeaza_date_factura` -> **`adauga_articol_factura_deviz`** -> **`scrie_in_vanzari`** -> **`pack_acn.salveaza_regdoc`**. Cautare directa dupa `contabilizeaza_articol` in acel fisier: **zero rezultate**. Nota contabila o scrie o procedura proprie ACN, care nici nu primeste `id_pol` printre parametri. Acelasi tipar ca ROAAUTO „Alte servicii" (J-ter, punctul 2): **o cale de facturare paralela**, nu o dovada ca articolele fara politica sunt tolerate. - **Pe contract, articolul „liber" nu e fara politica.** `crsarticole` e populat de `pack_facturare.cursor_contract` / `cursor_preturi`, care **selecteaza explicit `A.ID_POL`** (`ff_...:2162-2167`) si care ruleaza la fiecare apel `completare_politica_stoc` (`ff_...:2151`). Deci fiecare rand din grila vine deja **cu o politica reala atasata**. „Adaugare libera pe contract" inseamna „orice articol din lista de preturi, nu doar cele din contract" — nu „articol fara politica". **Diferenta reala e cursorul-sursa al grilei, nu tipul de document:** `caut_articol` (`COMUN\programe\ocautare.prg:1636-1735`, cautarea in nomenclator) **nu are coloana `id_pol` in nicio ramura**; `cursor_preturi` / `cursor_contract` o au, pentru ca pleaca de la politica, nu de la nomenclator. Povestea #13 cere exact ce nu exista azi nicaieri. - **Ce verifica de fapt `FACT-024`:** `SELECT COMPUS, ID_POL_ART FROM VCRM_POLITICI_PRET_ART WHERE ID_ARTICOL = ... AND ID_POL = ...` (`ff_...:7278-7302`) — deci **apartenenta articolului la politica de pe linie**. Ipoteza din runda 8 e **partial** confirmata: verificarea e de apartenenta, dar `id_pol` **nu** vine din antet ca valoare unica — e parametru per linie, trimis de VFP. Doua cauze diferite (`id_pol` gol; articol nemembru) produc aceeasi eroare, iar codul nu le distinge. **2. `CORESP_CONT_VENCHELT` exista, e populat si e folosit in productie — dar niciodata pentru `CONT_VENIT`.** Toti consumatorii gasiti (`pack_vin`, `pack_devize`, `gestiune_pack_gest_import`, rapoarte de gestiune) citesc `CONT_CHELT` sau `CONT_APROVIZIONARE`. Coloana `CONT_VENIT` **are date reale** dar **zero consumatori**, nici Oracle nici VFP. Cheia de join e stabila si testata: pe `CONT`, cu `STERS = 0`, prin `LEFT JOIN` — tiparul se copiaza identic. Deci regula lui Marius e implementabila; e insa **un consumator nou**, nu o reteta deja rulata. **Masurat pe baza vie (10.08.2026, `MARIUSM_AUTO`) — `docs\cercetare\verif_baza_vie_cont_venit.md`:** tabelul are **41 de randuri active**, din care exact **20 au `CONT_VENIT`** populat; restul 21 sunt conturile de ajustari (`391`…`398`, toate cu `CONT_CHELT = 681`), care nu poarta marfa. **Niciun `CONT` nu e duplicat**, deci pasul 1 al retetei e determinist — desi schema nu are unique pe `CONT`, doar `PK` pe `ID_CCV`. Si, decisiv: **zero articole active cad pe un rand cu `CONT_VENIT` gol**, deci ramura „cont derivat gol" se trateaza defensiv dar nu are cazuri reale. DDL-ul complet e in raport. **3. Calea VFP (decizia 27-bis) exista si e construita din piese functionale.** Patru pasi. > **Clarificare ceruta de Marius (10.08.2026) — de citit inainte de cei patru pasi.** Politica **nu e > necesara ca sa se afle contul.** Regula deciziei 27 il determina complet, in VFP: corespondentele pe > clasa 3, contul de pe articol daca e 6xx / 7xx, `704` altfel. Aia e **pasul 1**, si e verificata pe date. > Politica e necesara **doar ca canal de transport** catre Oracle: `contabilizeaza_articol` **nu are > parametru de cont de venit** — primeste `V_ID_POL` si deduce singura contul prin lantul politica → nota → > `NOTE_CONTABILE.SCC`. Deci **pasii 2-4 nu decid nimic**, doar convertesc un cont pe care VFP il stie deja > intr-o forma pe care pachetul o accepta. Indirectarea e consecinta deciziei **27-bis**, nu a regulii lui > Marius: daca pachetul ar putea fi modificat, un parametru de cont ar face pasii 2-4 sa dispara. > **INCHIS (runda 9 + decizia 35).** Cautarea canalului direct s-a terminat — > `docs\cercetare\canal_cont_venit_fara_politica.md`. Verdict: canalul **exista** (`actactan` / `tact` → > `oscrie_in_fisiere` → `pack_contafin.SCRIE_IN_ACT`), dar e cablat pe **editarea unei note deja scrise**, > nu pe emitere; emiterea trece exclusiv prin `scrie_factura2` → `contabilizeaza_articol`. Pistele > `V_CONT` (e contul de gestiune), setterele de sesiune, `ID_VENCHELT` si `GetAnaliticByGrupUtilizatori` > sunt **toate NU**. > **Si nu se foloseste oricum:** decizia 35 il respinge explicit in #13, ca sa nu existe doua coduri de > contare. Reteta in 4 pasi cade prin **decizia 34** (parametru de cont in pachet), nu prin canal. 1. **VFP calculeaza `SCC`-ul** dupa regula deciziei 27: `CORESP_CONT_VENCHELT.CONT_VENIT` pe contul de gestiune al liniei (articol gestionabil), `NOM_ARTICOLE.CONT` daca e 6xx / 7xx, altfel `'704'`. 2. **VFP cauta o politica a carei nota are deja acel `SCC`** — interogarea inversa pe acelasi lant: `CRM_POLITICI_PRETURI -> CRM_NOTE_VANZARI -> NOTE_CONTABILE`, `WHERE NC.SCC = :cont_calculat`. Interogare **noua**, dar pe coloane si relatii deja confirmate. **Corectat pe baza vie:** legatura nu e politica -> nota, ci politica -> **set de note** — `CRM_POLITICI_PRETURI.ID_NOTA` -> `CRM_NOTE_VANZARI.ID_SET` -> `NOTE_CONTABILE.ID_SET`. Pe cele 7 note de vanzari active fiecare set are **un** rand, dar in tabel exista seturi cu pana la **30** de randuri, deci „nota politicii" e bine definita doar cat timp seturile de vanzari rămân cu un rand. **Si mai important: filtrarea pe `SCC` singur nu e suficienta** — vezi „Ce nu e gratuit" mai jos. 3. **VFP se asigura ca articolul e membru al acelei politici** — RPC **existent** `pack_preturi.adauga_politica_pret_art`, deja folosit exact asa (verifica intai, insereaza daca lipseste) in `ofacturare.vc2:15551-15587`, la modificarea listei de preturi de la NIR. **`pack_preturi` nu e `pack_facturare`** — nu se atinge nimic din pachetul comun de facturare. 4. **VFP trimite acel `id_pol`** in `adauga_articol_factura` (`V_ID_POL` e parametru de intrare explicit, `ff_...:4989-5015`). `contabilizeaza_articol` ruleaza **neschimbata**. **Modelul „politica tehnica, configurata o data, populata automat" exista deja in pachet:** `completare_politica_stoc` (`ff_...:2093-2120`) face `MERGE` in `CRM_POLITICI_PRET_ART` pentru toate articolele `IN_STOC = 1` sub politica `pack_facturare.nid_politica_stoc`, alimentata din optiunea globala `gnId_pol_pret_stoc` (`ofacturare.vc2:21733-21753`). Nu e direct reutilizabila — are o singura nota pentru toate articolele, iar decizia 27 cere pana la sase conturi diferite — dar arata ca tiparul e acceptat in produs. **Calea VFP e mai completa decat modificarea pachetului**, nu doar mai ieftina: politica reala aduce si `CU_TVA`, `IN_VALUTA`, `EXPLICATIE`, `ASCD`, `ASCC` din nota configurata. Un fallback in `contabilizeaza_articol` ar da doar `SCC`; `CU_TVA` si `IN_VALUTA` **nu au nicio sursa alternativa** in afara lui `NOTE_CONTABILE`, deci ar trebui hardcodate — cu risc de calcul gresit al bazei si al TVA. **Ce nu e gratuit, si trebuie stiut inainte de S4g:** - **Pasul 2 depinde de configurarea fiecarui client — deci nu se poate proiecta pe acoperire.** Decizia lui Marius, 10.08.2026: **datele din Dev nu sunt baza de proiectare.** Fiecare client isi defineste propriile politici de pret, fiecare cu nota ei de vanzare salvata pe un `ID_SET`. Pe schema de dezvoltare masurarea a dat `704` cu 19 politici valabile, `707` cu 4, `7015` cu 1, si zero pentru `711` / `702` / `703` / `7018` — **dar aceste numere nu se folosesc ca argument**, pe o baza de client distributia poate fi complet alta. Ce se pastreaza din masurare e strict **structural** (forma lantului, absenta unique-ului pe `CONT`, bug-ul de set multi-rand) plus un fapt negativ: **afirmatia rundei 8 ca „pentru `704` nu exista nicio nota configurata" nu se susține ca regula** — pe Dev exista patru, deci pasul „se creeaza nota pentru 704" nu poate fi pus in plan ca obligatoriu. **Consecinta de proiectare:** reteta nu are voie sa presupuna ca gaseste o politica pentru `SCC`-ul calculat. Ori garanteaza singura existenta a ceea ce foloseste (politica tehnica creata de produs), ori are o cale care **nu trece prin politica deloc** — vezi mai jos. - **`PTVA` de pe nota e inofensiv — verificat pe cod, nu presupus.** `cursor_articol` (`PACK_FACTURARE:7218-7271`) **nu selecteaza `D.PTVA`**; cota folosita efectiv vine din `detalii_articol.proc_tvav * 100 - 100` (`:7464`), adica din articol / document. Deci o nota cu `PTVA = 5` pe un articol cu 21% **nu produce nimic** — coloana e moarta pentru aceasta procedura. Ingrijorarea initiala („criteriul trebuie sa fie `(SCC, PTVA)`") **se retrage**. `IN_VALUTA = 1` de pe nota pe un document in lei nu da nici eroare, nici suma greșita — doar populeaza redundant `ACT_TEMP.SUMA_VAL` la curs 1 (`scrie_nota:12391-12404`, `:12492-12507`): inconsistenta cosmetica, nu contabila. `ASCD` / `ASCC` nule au fallback automat prin `GetAnaliticByGrupUtilizatori` (`:7409-7428`), iar `ID_PARTD` / `ID_PARTC` **nu se citesc de pe nota** deloc — vin din contul si din contextul documentului (`:12510-12530`). - **In schimb, `ID_SET` cu mai multe randuri dubleaza venitul SI descarcarea de gestiune.** Asta e riscul real, si e mai grav decat cel presupus. `cursor_articol` face `LEFT JOIN NOTE_CONTABILE D ON C.ID_SET = D.ID_SET` **fara `ROWNUM`, fara agregare, fara filtru**, iar bucla care il consuma (`:7393-7542`) executa `scrie_nota` **si** `descarca_gestiune` **o data pentru fiecare rand al setului, cu aceeasi cantitate si acelasi pret intreg de fiecare data** — nu impartite. Nu exista nicio logica de distributie: `ORDINE` nu apare in tot pachetul. Deci pasul 2 nu trebuie doar sa gaseasca un rand cu `SCC`-ul potrivit, ci **sa garanteze ca setul ales are exact un rand**. Cele 7 note de vanzari active au, azi, exact un rand fiecare — dar 30 din cele 40 de seturi din baza au mai multe, cu maxim 30. **Conditia care face reteta sigura e o coincidenta a datelor curente, nu o garanție a codului.** Sursa: `docs\cercetare\s10_pret_rederivat.md`, „Completare: nota contabila a politicii", punctul 1. - **Pasul 3 poluează o lista de preturi de producție.** Politica **este** lista de preturi: candidatele reale contin `STOC PRODUSE` cu **6310** articole si `LISTA PRETURI LEI` cu **909**, plus liste cu nume de client si de sezon (`CHIOSC`, `SERVICE AUTO`, `S7 CRACIUN`…). Un articol adaugat ad-hoc pe factura ar aparea de acum in acea lista, cu pretul cu care a fost facturat o data. **Deci pasul 2 nu trebuie sa caute orice politica potrivita, ci o politica tehnica dedicata, una per `SCC`** — modelul `gnId_pol_pret_stoc` extins de la una la sapte. Cele **sase** politici cu 0 articole din baza vie arata ca o politica goala e o stare acceptata de produs. - **Ambiguitatea celor 19 politici pe `704` e de lista de preturi, nu de rezultat contabil** — majoritatea trimit la aceeasi `id_nota = 1`, deci toate dau `704` / `PTVA = 21` / lei. Cu atat mai mult, criteriul de departajare corect e „care lista de preturi accept sa murdaresc", iar raspunsul e „niciuna dintre cele existente". - **Politica tehnica devine vizibila utilizatorului** in `caut_politici_curente_util()` — aceeasi functie ca la cautarea normala. Ori se filtreaza explicit (flag / prefix dedicat), ori se accepta riscul ca cineva sa o aleaga din greseala pe o factura normala. **De decis in S4g.** - **`id_pol` devine populat** acolo unde azi ar fi gol. Nu s-a gasit cod care sa presupuna „`id_pol` gol = articol fara politica", dar nici nu s-a cautat exhaustiv. - **Doua politici active nu au `ID_NOTA`** (`32 HOTEL TAXE`, `33 HOTEL CAZARE`) — `id_pol` valid, nota absenta. Ce face `contabilizeaza_articol` in acest caz e intrebare deschisa, trimisa pe cod. E o gaura care **exista deja azi**, independenta de #13. **RASPUNS LA DECIZIA 32 — reteta RAMANE. `docs\cercetare\idpol_comanda_contract.md`.** Intrebarea era: cum alege programul nota contabila la factura din comanda, care „nu are politica de pret"? **Raspuns: are. Structural nu poate sa nu aiba.** `COMENZI_ELEMENTE.ID_POL` e **`NOT NULL`** la nivel de schema (constrangerea `SYS_C0015376`, plus `FK_COMENZI_ELEMENTE_002`) — verificat direct: **0 din 7108 randuri** au `ID_POL` nul sau zero, 9 politici distincte in uz, toate cu nota atasata. Deci nu exista nicio ruta alternativa catre nota, si **nu exista contradictie cu concluzia rundei 8**: pe comanda cazul „`id_pol` gol" e imposibil, nu doar neintalnit. Ce percepe operatorul ca „n-am ales nicio politica" e faptul ca politica se alege **o data pe comanda** — manual sau moștenita dintr-o optiune legata de tipul comenzii — si de atunci fiecare articol adaugat o primeste automat, din `com_vpreturi_utilizator`, view care filtreaza `id_pol IS NOT NULL` la sursa. Pe **contract** tiparul e acelasi, dar cheia stocata e `CTR_ARTICOLE.ID_POL_ART` — FK direct la randul din `CRM_POLITICI_PRET_ART`, **nullable**, cu cazuri reale. **De ce `FACT-024` nu se declanseaza pe comanda / contract:** nu pentru ca ar exista un fallback, ci pentru ca **articolul nu e ales niciodata din nomenclator** — e ales dintr-o lista deja filtrata pe politica, deci apartenenta e garantata **prin construcția listei**, nu prin validare. Exact aici e diferenta cu #13, unde `caut_articol` ofera nomenclatorul intreg. **Deci refolosirea nu simplifica nimic:** mecanismul de jos — RPC de inserare + apartenenta garantata — e **exact** ce propune reteta in 4 pasi. Ce ar simplifica cu adevarat („ia politica curenta fixa si pune articolul in ea") e **decizia 24, retrasa**, si motivul retragerii sta: o politica unica da **un singur** cont de venit oricarui articol, indiferent de natura lui — opusul deciziei 27. Comanda si contractul nu raspund niciodata la intrebarea „**care** politica pentru **acest** articol", pentru ca la ele politica vine gata aleasa, o data, de operator. **CORECTIE, a doua tura a aceluiasi agent — premisa lui Marius ERA corecta, dar pe contract, nu pe comanda.** Exista o ruta reala catre nota contabila **complet in afara lui `id_pol`**: la **contractele cu rate / scadentar** (`OPT_FACTURARE IN (1, 2)`), o functie separata — **`contabilizeaza_rata`** — scrie nota direct din **`CONTRACTE.ID_NOTA`**, fara sa treaca prin `CRM_POLITICI_PRETURI`. Confirmat pe cod de agent si pe o factura reala (`id_vanzare 360`: `SCD = 4111` / `SCC = 704` in `ACT`, pe o linie fara articol si fara politica). **Structura confirmata independent de sesiunea principala:** `CONTRACTE.ID_NOTA` exista, e nullable, si e **populat pe 19 din 242 de contracte**, rezolvand la note reale — `1 NOTA 1` → `SCC 704` (5 contracte), `2 DISCOUNT` → `SCC 4111` (5), `5 VANZARE MARFA` → `SCC 707` (1). Deci nota **pe antetul documentului** nu e o ipoteza, e un mecanism in uz. **Ce inseamna asta pentru #13, si de ce e o decizie de produs, nu una tehnica.** Tiparul „nota pe antet" ar fi **mai simplu decat reteta in 4 pasi**: n-ar mai fi nevoie nici de gasirea politicii, nici de inserarea articolului in ea, nici de politica tehnica — antetul ar purta nota, iar liniile ad-hoc ar moșteni-o. **Dar cere cod nou in `pack_facturare`**, pentru ca `contabilizeaza_rata` trateaza rate, nu articole de factura. Adica **incalca exact decizia 27-bis** („`pack_facturare` nu se atinge"). **Deci alegerea e a lui Marius, intre trei variante:** 1. **reteta in 4 pasi** (decizia 27-bis respectata, `pack_facturare` neatins, dar cere politica tehnica per `SCC` si o interogare inversa noua); 2. **nota pe antet ca la `contabilizeaza_rata`** (mult mai simplu si mai curat conceptual, dar **relaxeaza decizia 27-bis** — cod nou in pachetul comun intregii suite); 3. **nota pe antet doar ca fallback** pentru articolele fara politica, lasand restul neschimbat. **Nu se implementeaza nimic pana la aceasta alegere.** Detalii: `idpol_comanda_contract.md`, sectiunea E-bis. **Doua observatii din date, gasite la confirmarea verdictului:** - **Precedentul cerut de decizia 27 exista deja in producție, si e mai bun decat `gnId_pol_pret_stoc`.** Politica `7 DISCOUNT` apare pe **870 de linii de comanda** si trimite la nota `2 DISCOUNT` (`SCD = 667`, `SCC = 4111`). E o politica de pret folosita **exclusiv ca mecanism de rutare contabila**, nu ca lista comerciala de preturi. Deci „politica tehnica, dedicata unui cont, populata automat" nu e o invenție a lui #13 — **produsul o face deja**, pentru discount. Argument direct pentru politica tehnica per `SCC`. - **Garantia prin construcția listei nu e etanșă in date: 37 de linii de comanda au un articol care NU e membru al politicii de pe linie.** **VERIFICAT (runda 10)** — `docs\cercetare\linii_comanda_articol_nemembru.md`. **Decizia 32 NU se redeschide in sensul periculos: nu exista cale de cod care sa ocoleasca `FACT-024`.** Patru rezultate: - **Cifra 37 se reconfirma, dar numitorul din plan era gresit** — `6868` era alt numitor; cel corect pentru linii cu `ID_POL NOT NULL` e **7108**. - **Premisa „37 de linii ar cadea la facturare" era partial gresita.** Un rand cu `CANTITATE < 0` **nu dovedeste ca linia a fost facturata**: singurul loc care il insereaza e `inchide_comanda` (`PACK_FACTURARE:5769-5820`), care scrie *diferenta ramasa* la **inchiderea** comenzii, chiar si cand nimic nu s-a facturat pe acea linie. **34 din 35 de linii negative n-au nicio factura in spate, nici macar stearsa** — comenzile au fost inchise, nu facturate. - **Una singura chiar a ajuns intr-o factura emisa** — comanda `497`, articolul `4294507522` („VOUCHER DISCOUNT") pe politica `7` („DISCOUNT"), in factura `ID_VANZARE = 1028`, `20.03.2026`. Si codul **chiar a rulat** `contabilizeaza_articol` pentru ea: apelul e in ramura `ELSE` a lui `CASE pack_facturare.ntip` din `scrie_factura2`, care exclude doar transferurile, avizele de custodie si facturile cu rate — tipul 3 cade in `ELSE`. Deci **fie articolul era membru al politicii la 20.03.2026 si a fost scos ulterior, fie verificarea a fost ocolita altfel; dovada pentru a doua varianta nu exista.** - **Cauza e NEDETERMINABILA DIN DATE, structural.** `CRM_POLITICI_PRET_ART` **n-are coloana `STERS`** si n-are tabel de istoric — stergerile sunt **fizice, fara urma**, deci nu se poate reconstitui starea de la 20.03.2026. Coloanele de audit ale facturii (`VANZARI.ID_UTILS` / `DATAORAS` si cele de pe cele doua linii) sunt **toate NULL**, deci nici o editare ulterioara nu s-a inregistrat. Indiciu de tipar, nu dovada: aceeasi pereche articol-politica apare pe **35 de comenzi diferite**, ceea ce sustine o **curatare de catalog** mai degraba decat 35 de greseli independente de introducere. **Ce ramane, si cum se formuleaza:** pentru celelalte 36 de linii, „zero facturi in date" **nu demonstreaza ca nu se pot factura** — demonstreaza doar ca nu s-a intamplat. Iar cu starea de azi a lui `CRM_POLITICI_PRET_ART`, toate cele 37 **ar** cadea cu `FACT-024` daca ar trece acum prin `contabilizeaza_articol` (verificat pe view-ul `VCRM_POLITICI_PRET_ART`, care n-are filtru propriu, deci vede exact aceleasi perechi ca tabelul). **Concluzia pentru #13 e neschimbata:** garantia prin constructia listei e reala cat timp catalogul nu se editeaza sub documente deja emise. **Verificarile pe baza vie sunt facute** (10.08.2026) — `docs\cercetare\verif_baza_vie_cont_venit.md`: interogarea inversa pe fiecare `SCC` candidat, DDL-ul lui `CORESP_CONT_VENCHELT`, distributia articolelor pe ramurile deciziei 27 (6125 din 6430 pe ramura gestionabila), efectele colaterale ale pasului 3 si trei fragilitati de structura. **Rămâne deschisa doar partea de cod**, listata mai sus. ### K. Discountul pe linie (decizia 14) Verificat de doua ori, pe formularul real si pe DDL. Raspunsul la intrebarea „e doar procentual?” e **nu, si nici doar valoric — sunt amandoua, dar in alta fereastra**. **Intrarea pentru operator exista deja, in dialogul de articol.** `frm_articol_factura` (`ofacturare.vc2:1108-2659`), deschis la fiecare adaugare de articol, are trei campuri legate: `Clb_procent_discount` (procent), `Clb_discount_unitar` (suma, in lei sau valuta) si `Clb_discountctva` (varianta cu TVA). Toate cheama `frm_articol_factura.do_calculeaza_discount(valoare, tip)` (`:1874-1976`) cu `tip = 1` procent, `tip = 2` lei, `tip = 3` valuta (`:2602-2649`) — tastezi in oricare, se recalculeaza celelalte. Rezultatul intra in `poArticol.discount_unitar` si variantele lui, apoi in `crsfactura` prin `Replace` (`:12956-12957`). **Se stocheaza o singura coloana, si e valoare.** `VANZARI_DETALII.DISCOUNT_UNITAR NUMBER(22,6)` (`ff_2024_06_13_02_COMUN_FACTURARE.sql:3`; simetric pe `VANZARI_DETALII_TEMP` la `:9` si pe `CRM_POLITICI_PRET_ART` la `:16` — **trei tabele diferite, nu aceeasi coloana de trei ori**). **Nu exista coloana de procent** pe `VANZARI_DETALII` — procentul e doar mod de introducere. Parametru Oracle: `V_DISCOUNT_UNITAR` in `adauga_articol_factura` (`ff_2026_03_23_04_COMUN_PACK_FACTURARE.sql:4986`), scazut din pretul fara TVA inainte de TVA (`:15794-15847`). **In grid, azi, comportamentul e inconsecvent.** Pe factura in lei ramane `cDiscountCTva`, care e `ReadOnly = .T.` (`ofacturare.vc2:12311-12317`); pe factura in valuta ramane `cVdiscountftva`, care **e editabila** — dar nu are niciun `Valid` / `InteractiveChange`, deci o editare directa in celula schimba valoarea bruta fara sa refaca totalurile. Excluderea reciproca e la `:15269-15278`. **Corectie fata de runda 3:** afirmasem ca prototipul are deja procent pe linie, ca argument pentru pornirea de la el. **Fals.** `procdisc` apare o singura data in tot fisierul, ca `ControlSource` de coloana (`:16721`), fara niciun `Replace` care sa-l populeze si fara corespondent Oracle — e schela moarta. (Restul aparitiilor de „procdisc” sunt variabila de optiune `gnMemProcDisc`, altceva.) Argumentele pentru prototip raman celelalte doua: controalele de antet si butoanele deasupra gridului. **Ce ramane de facut:** - cele doua campuri din dialogul de articol devin **coloane in grid** — procent si valoare unitara — cu **acelasi calcul reciproc** din `do_calculeaza_discount`, mutat pe evenimentele coloanelor; - se repara astfel si inconsecventa de mai sus: aceleasi validari in ambele monede; - se pastreaza excluderea pe `in_valuta`; - modelul de date **nu se atinge**; - **de verificat separat, blocant**: daca `discount_unitar` apare pe rapoartele `.frx` si daca e transmis in eFactura (UBL) si SAF-T. Daca nu apare pe hartie, documentul tiparit nu explica totalul. #### K-bis. Discountul de DOCUMENT — cota, explicatia si atribuirea pe cote Cercetare terminata, verificata la sursa (doua runde, a doua a corectat concluzii ale primei — vezi rezervele de mai jos). Detaliu complet: `docs\cercetare\discount_document_cota_tva.md`. **Cota discountului de document = cota MAXIMA de pe factura, ca regula implicita.** Nimeni n-o alege: nu exista control in formular si nicio coloana pe `VANZARI` pentru ea. `Calculate Max(proc_tvav)` — `COMUN\programe\oproceduri_facturare.prg:1387`, `COMUN\clase\ofacturare_comun.vc2:4495` si `:7294`, `COMUN\programe\ofacturare_stoc.prg:582`. Discountul intra ca **pseudo-linie negativa** printr-un rand-sentinela `Replicate('Z',20)` (`COMUN\programe\ofacturare_comun.prg:1886-1897`), care devine linia „Discount NN.NN % Factura" (`:1273-1392`). **Explicatia nu exista ca notiune — e o constanta.** In XML, `AllowanceChargeReason="Discount"` si `ReasonCode="95"` sunt hardcodate (`COMUN\programe\xmlefactura.prg:774-776`), la fel `currencyID="RON"` (`:778`). Textul construit in VFP („Discount 10.00 % Factura") nu ajunge in XML. **Regula e implementata de doua ori, independent — nu intr-un singur loc.** In VFP (mai sus) si in PL/SQL: `PACK_FACTURARE.recalculeaza_totaluri_vanzari`, `MAX(ROUND(discount*(a1.proc_tvav-1),...)) as DISC_TVA_RON` — `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:16081-16092`, scris in `VANZARI.DISCOUNT_TVA` si de acolo in `TOTAL_TVA` / `TOTAL_CU_TVA` (`:16214-16227`). **Pentru #13: doua locuri de schimbat, nu unul** — daca se schimba doar unul, `VANZARI.TOTAL_TVA` si TVA-ul din XML diverg pe facturile cu cote mixte. **Defectul dovedit e de atribuire fiscala, nu de validare.** Pe cote mixte, tot TVA-ul discountului se scade din cota maxima. Exemplu: 1000 lei la 21% + 1000 lei la 11%, discount de document 10% (200 lei) → se declara 10 lei TVA in minus, mereu in acelasi sens (TVA colectat subdeclarat). Aceeasi valoare gresita ajunge si in nota contabila si in jurnalul de TVA. Caz-limita real, masurat: daca liniile de la cota maxima sunt o mica parte din total, `TaxSubtotal` iese cu baza negativa (`TaxableAmount = -410.00`). **Ipoteza bazei negative — verdict nuantat, asa cum a iesit din verificare, nu simplificat.** Temerea: pseudo-linia, avand `id_jtva_coloana = 0`, iese din `LEFT JOIN` cu `coloana_jv` NULL si formeaza grup propriu in `C_TVA_FACTURA`. **Infirmata pe factura obisnuita** — masurat headless, `IIF()` din VFP intoarce ramura falsa, nu `.NULL.`, cand conditia e nula, deci pseudo-linia se contopeste corect. **Confirmata insa pe facturile scutite / taxare inversa / intracomunitare**, unde liniile reale au `scutit = 1` si `expltva` completat iar pseudo-linia are `0` si gol: apar doua grupuri, deci un `TaxSubtotal` in plus cu baza negativa si categoria `Z`, iar `agettipcota(1)` (`xmlefactura.prg:782-786`) devine ambiguu intre `E` si `Z`. **Nu blocheaza factura — validat, nu presupus.** Validat offline cu DUKIntegrator, 6 scenarii, inclusiv un control negativ care chiar iese cu erori (`BR-CO-13` + `BR-CO-15`) — deci cele „ok" inseamna ceva. Trece inclusiv un caz cu `TaxableAmount = -400.00`. Regulile pe categorii (`BR-S-08` / `BR-E-08` / `BR-Z-08`) se satisfac trivial: aritmetica inchide, modelarea fiscala e cea gresita, si asta niciun schematron nu prinde. **Rezerva de scris explicit:** validatorul local e din 2022; validatorul ANAF online n-a fost apelat (interdictie). **`currencyID="RON"` hardcodat e neconformitate reala si activa, dar fara dovada ca ar cauza respingere.** Dovedit pe fisier: XML-ul in EUR de la VENDING_MASTER e identic pe SHA256 cu cel din `TRIMISE` (deci exact ce a plecat la ANAF) si a fost acceptat. Respingerea din `ERORI` e a unei incarcari anterioare, pentru alte reguli. **Nu s-a putut proba pe date reale, si asta ramane netransat.** In baza accesibila sunt 3 facturi cu discount de document (toate cu o singura cota, anterioare eFacturii) si 34 cu cote mixte fara discount — intersectia e goala. Cele 4 XML-uri de productie cu alocare de document sunt, dupa trei indicii independente, discounturi pe linie, nu de document. Absenta din date nu e dovada (regula casei). Ramane nedovedit prin rulare: reproducerea end-to-end prin program a scenariului „factura scutita + discount de document". **Recomandarea cercetarii pentru #13:** repartizare proportionala automata pe cote, **nu se cere utilizatorului cota**. Discountul de document nu are o cota proprie de ales — natura lui de TVA e determinata de liniile pe care le reduce; a pune operatorul sa aleaga inseamna a-i cere o decizie fiscala pe care datele o dau deja. Pentru explicatie: un singur camp text optional pe document, folosit ca `AllowanceChargeReason` in locul constantei (`ReasonCode` ramane `95`). **Doua completari obligatorii daca se implementeaza, altfel reparatia e degeaba:** - bucatile de discount trebuie sa primeasca **`id_jtva_coloana` al grupului pe care il reduc**, nu doar cota — cheia de grupare are cinci campuri si `expltva` se completeaza tot prin `id_jtva_coloana`. O implementare care seteaza doar `proc_tva` lasa grupul orfan exact unde e azi; - **eFactura nu are nevoie de nicio modificare** — `xmlefactura.prg:758-792` grupeaza deja `GROUP BY proc_tva` si emite cate un `AllowanceCharge` per cota. Doua consecinte vizibile daca se implementeaza: factura tiparita va arata N randuri „Discount X % Factura" in loc de unul, si nota contabila primeste TVA-ul discountului spart pe cote. **Decizia a fost luata — decizia 59 (runda 16): repartizare proportionala pe cote.** Vezi sectiunea „Decizia 59" de la deciziile rundei 16, cu toate consecintele de implementare. **Cele doua puncte ramase s-au inchis si ele, tot in runda 16.** Campul text optional de motiv — **decizia 61**: da, se adauga (`AllowanceChargeReason`, `ReasonCode` ramane `95`), cu stocare noua pe `VANZARI` si un control nou in formular; ramane deschisa doar asezarea lui pe rand. Retroactivi- tatea la relistare/retrimitere — **decizia 62**: restrictia de modificare e strict `EsteInEFactura`, fara legatura cu nicio data; ramane insa deschis, separat, cazul relistarii (nu editarii) unei facturi vechi deja trimise, vezi decizia 62. ### M. Valuta si data cursului (decizia 15) **Cele doua concepte exista in cod, distinct** — decizia 15 nu introduce o distinctie noua, o face vizibila: - **(a) factura in valuta**: `poDate.in_valuta` / `Curs` / `multiplicator` / `id_valuta`, un singur curs pentru tot documentul; - **(b) articol cu pret in valuta pe document in lei**: `poArticol.tip_valuta = 1`, proprietate a politicii de pret, independenta de `in_valuta`. Conversia se face **in VFP**, cu cursul zilei: `poArticol.pretftva = Round(poArticol.pretftva_val * poArticol.Curs / poArticol.multiplicator, gnPPretV)` (`ofacturare.vc2:1989`, `:2017`, `:2953`). Cursurile zilei se incarca exact pe ramura „document in lei”: `If poDate.in_valuta = 1 ... Else citeste_cursuri_zi(poDate.zi_curs)` (`ofacturare.prg:407-418`). **In baza, exact cum ai descris:** `VANZARI_DETALII` **nu are coloana `CURS`**; pastreaza `PRET` (in lei, valoarea de facturare) plus `PRETD` si `ID_VALUTAD` ca urma a pretului original in valuta. Cursurile ajung in `VANZARI_CURSURI`, scrise neconditionat la emitere pentru **orice** valuta straina aparuta pe linii — deci si pe o factura in lei (`PACK_FACTURARE.sql:14491-14501`, `scrie_cursuri`). **`in_valuta` nu e o alegere, e o proprietate a tipului de document.** Se seteaza o singura data, in `Init`: `If Inlist(tnIdSet,223,225,226,227,271) OR INLIST(m.tnTip, 5,6,7,9,10,52) -> .in_valuta = 1` (`ofacturare_comun.prg:248-250`), si nu se mai schimba. Selectorul de valuta e chiar eliminat din formular cand `in_valuta = 0` (`ofacturare.vc2:9725-9728`), deci operatorul nici nu-l poate alege. **Problema semnalata, confirmata:** `Clb_zi_curs` e eliminat **doar** pe retur (tip 8, 9), la `ofacturare.vc2:9717-9722` — si niciodata in functie de valuta, desi blocul imediat urmator, `:9725-9728`, face exact asta pentru selectorul de valuta. Deci **azi campul apare si pe facturi in lei**, unde de multe ori nu are ce cauta. Validarea e la randul ei asimetrica: `frm_date_factura` cere ziua cursului doar cand `in_valuta = 1` (`:9484`), dar `frm_date_aviz_lucrare` o cere **neconditionat** (`:8076`). **Regula propusa:** campul apare cand tipul nu e retur **si** (documentul e in valuta **sau** exista pe document macar un articol cu pret in valuta). **Unificarea face regula posibila.** Azi cazul (b) nu se poate sti la deschiderea antetului — se afla abia dupa incarcarea articolelor, cand antetul e deja inchis si eliberat (`Release ofrmceredate`, `ofacturare.prg:248`). In formularul unificat antetul si gridul sunt in aceeasi fereastra, deci campul poate aparea in clipa in care intra pe grid primul articol cu pret in valuta. **Din acelasi motiv dispare si mecanismul lui #16**: bug-ul de focus si de renumerotare apare la revenirea din `frm_curs` intr-un antet care tocmai s-a inchis — in formularul unificat nu mai exista un antet inchis la care sa te intorci. `frm_curs` e in `COMUN\clase\onom_curs.vc2:537`, deschis din `vizualizeaza_curs` (`oproceduri_curs.prg:8-42`) ca recuperare la eroarea Oracle 20005 (`ofacturare.prg:313-317`). **Capcana:** `poDate.zi_curs` se trimite **neconditionat** ca prim parametru catre `cursor_preturi` / `cursor_articole_k` / `cursor_gestiune` / `cursor_lucrare` (`ofacturare.prg:272-303`), inclusiv pe tip 1. Ascunderea campului nu inseamna golirea valorii — valoarea implicita (data documentului) trebuie sa ramana. Iar pe avizul de lucrare, ascunderea fara repararea validarii de la `:8076` ar bloca finalizarea antetului cerand un camp care nu mai e pe ecran. ### N. Returul din facturi anterioare (decizia 17) Rapoarte: `docs\cercetare\factura_retur_document.md` (mecanismul principal) si `retur_si_lista_preturi.md`, A (al doilea mecanism). **Sunt doua fluxuri de cod independente**, care ating aceeasi clasa de formular prin metode si proceduri Oracle diferite. Nu au niciun punct comun in afara conventiei de semn a cantitatii. #### N.1 Factura de retur ca document (tipurile 8, 9 — si avizul de retur, 24) **Asta e mecanismul pe care il descrie Marius, si functioneaza deja cap-coada.** - **Intrare:** tile-ul de pe ecranul de facturare (`ofundal_facturare.vc2:882-884`) -> `politica.mpr` -> `factureaza(8)` / `factureaza(9)` (`Meniuri\politica.mn2:14-15, 45-46`). - **Alegerea facturilor, la nivel de document:** `frm_date_factura.do_cauta_facturi` (`ofacturare.vc2:9173-9212`) cheama `caut_facturi_multiple_client` (`oproceduri_facturare.prg:2091-2121`) — browse cu **selectie multipla**, titlu „Alegeti facturile (mouse-click pe numar sau apasati SPACE)”. Filtrare pe **client** (obligatoriu ales inainte) si **valuta**; sursele exclud tipurile de retur, deci nu se face retur dintr-un retur. Id-urile alese se aduna intr-un CSV in `poDate.listaid`, numerele in `poDate.descriere`, iar antetul primeste automat textul „RETUR FACTURA …”. Fara alegere nu se poate continua (`:9523-9526`). - **Popularea liniilor:** `pack_facturare.cursor_retur` -> `cursor_retur_document` (`ff_2026_08_06_10_COMUN_PACK_FACTURARE.sql:3943-3956`, `:3958-4071`) selecteaza direct din `VANZARI_DETALII` liniile facturilor alese si le pune in acelasi `crsarticole` pe care celelalte tipuri il umplu cu lista de preturi (`ofacturare.prg:306-311`). **Confirmat exact ce spunea Marius:** `ID_GESTIUNE` vine din linia originala (`:4034`, `:4055`), `PRET_ACHIZITIE` la fel, **neschimbat** (`:4035`, `:4056`), iar pretul de vanzare se recalculeaza pe curs doar daca moneda nu e nationala (`:4016-4030`). `GESTIONABIL` e `NVL2(A1.ID_GESTIUNE,1,0)` — gestionabil doar daca originalul avea gestiune. - **Ce se poate face manual:** **stergerea liniilor aduse merge** si cantitatea redevine disponibila in cursorul sursa (`do_sterge`, `ofacturare.vc2:14608-14693`, ramura de retur la `:14658-14659`); **returul partial merge** (`do_verifica_articol`, `:14743-14754`, fata de maximul calculat pe server). - **Avizul de retur (24)** foloseste acelasi `cursor_retur` si aceleasi ramuri; difera doar antetul — `nIdTipDoc = 6` si `frm_date_aviz` in loc de `frm_date_factura` (`ofacturare.prg:194-195`, `:225`). **Singurul gol real, si e acelasi ca la comanda:** pe un document de retur **nu se poate adauga o linie din afara facturilor sursa**, pentru ca `crsarticole` **este** rezultatul lui `cursor_retur` (`do_adauga_articol` ia articolul mereu din el, `ofacturare.vc2:12843-12851`). Nu e o interdictie explicita, e continutul cursorului — exact cauza de la comanda (J). Se rezolva la fel, prin `APPEND FROM`, si abia asta face reala decizia 16 pe documentele de retur. #### N.2 Retur de articole intr-o factura de vanzare normala (`But_retur`) Butonul (`cmd_butoane.vc2:324-338`, `caction = do_retur`) e vizibil **doar** pe tipurile 1, 5, 7, 10 (`ofacturare.vc2:15122-15127`) — pe un document de retur nu exista deloc. Aici factura sursa se alege **per articol**, la fiecare linie: `caut_facturi_multiple_client_articol` (`oproceduri_facturare.prg:2124-2159`), filtrat suplimentar pe articolul curent. Perechile `ID_ARTICOL:ID_VANZARE` se acumuleaza in `thisform.cListaIdArticoleRetur` (`:12888`) si pleaca ca `poDate.listaid` (`:13977-13979`). Gestiunea se alege prin `pack_facturare.cursor_gestiuni_articol_retur`, nu vine din factura sursa — **diferenta de fond fata de N.1**. **Legatura linie-de-retur -> linie originala: neverificat** pe schema. In VFP nu exista coloana de linie sursa; la nivel de antet exista `poDate.nid_vanzare_retur` (`ofacturare.vc2:14448`, `:14509`). Conteaza doar pentru **afisare** — daca formularul poate arata din ce factura vine fiecare linie. #### N.3 Ce se schimba in formularul unificat - **N.1 se muta ca atare.** Alegerea facturilor devine o optiune in meniul butonului de adaugare („Alege facturile de returnat…”), cu acelasi dialog, aceleasi filtre si aceeasi populare din `cursor_retur`. **Nu se reproiecteaza nimic din ce merge**, si in special nu se atinge preluarea gestiunii si a pretului de achizitie din facturile originale. - **N.2 capata si alegerea la nivel de document**, dupa modelul lui N.1, pastrand calea per-articol pentru cazul cu un singur articol. - **Lista de preturi devine disponibila si pe documentele de retur** (decizia 16), prin acelasi `APPEND FROM` ca la comanda. Consecinta de proiectat: pe acelasi document vor coexista linii aduse din facturi sursa (cu gestiune si pret de achizitie mostenite) si linii libere, care nu au factura sursa. **Decizia 22 (Marius, 09.08.2026): linia de retur fara factura originala e permisa, ca pe orice alt document.** Returul nu face exceptie de la regula deciziei 16 — sursa umple documentul, nu il inchide, si nici nu apare doar „pentru corectii”. Consecintele de proiectat, acum ferme: - **gestiunea si pretul de achizitie nu se pot mosteni** pe o linie libera, pentru ca nu exista linie originala. Se aleg, ca la N.2 (`cursor_gestiuni_articol_retur`), nu se preiau ca la N.1; - **maximul returnabil calculat pe server nu se aplica** unei linii fara factura sursa — nu exista cantitate originala fata de care sa se limiteze; - afisarea „din ce factura vine linia” trebuie sa suporte si valoarea goala (vezi mai sus, legatura linie-de-retur -> linie originala, inca neverificata pe schema). *De pastrat cum e:* excluderea returului dintr-un retur, filtrarea pe client si valuta, maximul returnabil calculat pe server, si mostenirea gestiunii si a pretului de achizitie. Nu se ating. ### O. Facturile din ROAAUTO (decizia 18) Raport: `docs\cercetare\roaauto_facturi.md`. > **Precizare la descrierea liniilor de mai jos.** „Liniile unei facturi ROAAUTO nu sunt articole” e > adevarat doar pentru liniile **generate din deviz**. Langa ele pot sta articole reale, adaugate > prin mecanismul **„Alte servicii”** — vezi O-bis, verificat pe cod > (`docs\cercetare\roaauto_articole_lista_preturi.md`). Restul sectiunii ramane valabil: tipul `-12`, > drumul comun prin `PACK_FACTURARE`, faptul ca editorul lui #6 le vede, si blocarea modificarii > dupa facturare — ultima **reconfirmata**. **Ce se confirma.** ROAAUTO nu are cale proprie spre baza: `factureaza_deviz` (`ROAAUTO\Programe\oproceduri_devize.prg:846`) cheama acelasi `PACK_FACTURARE` partajat — `initializeaza_date_factura` (`:1211`), `adauga_articol_factura_deviz` (`:1240`), `oscrie_in_fisiere` (`:1269`), `scrie_in_vanzari` (`:1302`) — deci liniile trec prin acelasi `VANZARI_DETALII_TEMP` si ajung in acelasi `VANZARI_DETALII` ca orice factura ROA. Tipul documentului e **`-12`** (`:922`). Editorul lui #6 le vede deja, fara cod de recunoastere a sursei, verificat pe date reale (`id_vanzare = 1047`). **Deci partea de „le vede” e gratuita.** **Ce nu se confirma — si asta schimba dimensiunea deciziei.** „Se pot modifica ulterior” **nu e adevarat azi, in niciun produs**: - in ROAAUTO, formularul de devize isi dezactiveaza butonul de modificare de indata ce comanda are numar de factura (`oviz_devize.vc2:4536`); singura actiune post-facturare gasita e re-listarea (`oproceduri_devize.prg:1516`), care nu scrie nimic. Niciun apel din ROAAUTO catre `modifica_date_factura`; - in ROAFACTURARE, gridul de articole din editorul nou (`frm_modific2024`, `COMUN\clase\omodificari.vc2`) e **read-only prin design**, la nivel de grid si pe fiecare `Text1`. Deci decizia 18 nu extinde o cale existenta, **creeaza o capacitate care nu exista nicaieri**. **Cum arata liniile.** `factureaza_deviz` agrega sumele devizului si insereaza cate o linie sintetica per categorie, cu `id_articol` **negativ**: `-100000` MANOPERA (`:965-967`), `-100003` MATERIALE (`:947-949`), `-100001` discount manopera, `-100005` / `-100006` avans si stornare avans, `-100007` / `-100008` inspectie tehnica si spalare (`:936-1027`). Optional, cu `gnAUTOIdArticolReparatii` setat, toate se cumuleaza intr-o singura linie cu un articol real (`:989-1004`). Langa ele pot sta **articole reale**, prin „Alte servicii” — vezi O-bis. ### O-bis. „Alte servicii” — cum se adauga azi articole reale in ROAAUTO Raport: `docs\cercetare\roaauto_articole_lista_preturi.md`. **Asta e mecanismul de refolosit.** - **Unde:** formularul `frm_incasare_finala` (`ROAAUTO\Clase\oviz_devize.vc2:5435-7143`), aratat **la emiterea facturii finale** (`do_factureaza_final`, `:2569`) — nu pe devizul propriu-zis. - **Sursa articolelor:** `cauta_nom_articole` pe `vnom_articole_toate` (`COMUN\programe\ocautare.prg:1781-1823`), filtrat `in_stoc = 0 and in_crm = 1` si cu id-urile sintetice excluse (`oviz_devize.vc2:6539-6580`). **Nomenclatorul brut, si numai articole fara stoc** — nu lista de preturi, nu politici. - **Pretul se tasteaza manual.** `do_adauga` nu completeaza pretul si cantitatea; operatorul le pune in grid, iar `do_modifica_alteserv` recalculeaza valoarea (`:6692-6698`, `:7064-7070`). Nu exista preluare automata de pret pe calea asta. - **Liniile raman separate.** Cumularea in „REPARATII AUTO” include doar id-urile sintetice (`oproceduri_devize.prg:999-1012`); randurile din `crsalteserv` se insereaza dupa, cu `id_articol` real, si ajung ca atare in `VANZARI_DETALII_TEMP` (`:1036-1041`, `:1240-1257`). - **Gestiune: tot zero.** `id_gestiune` nu e completat de niciun `INSERT` si pleaca `0` spre Oracle (`:1201-1206`, `:1255`) — consistent cu filtrul `in_stoc = 0`. Deci **nici articolele reale din ROAAUTO nu descarca gestiune**. Diferenta fata de liniile sintetice e doar `id_articol`. - **Stergere si modificare:** exista, dar **doar cat timp formularul e deschis, inainte de emitere** (`do_sterge`, `:6730-6741`). - **Contul pleaca gol.** `crsvanztemp` are coloana `Cont c(4)`, dar `INSERT`-ul care il umple nu o include (`oproceduri_devize.prg:1190-1206`); apelul trimite literal `''` (`:1256`), care ajunge `NULL` in `VANZARI_DETALII_TEMP.CONT`, fara `NVL` si fara fallback. Deci **liniile de articole reale din ROAAUTO se scriu azi fara cont** de gestiune, tacut. Vezi J-bis. - **Si fara politica de pret — dar pe alta cale decat credeam.** `id_pol` lipseste peste tot pe firul asta (nu e nici in `crsalteserv`, nici in `crsvanztemp`, nici in semnatura lui `adauga_articol_factura_deviz`). **Motivul pentru care asta nu produce `FACT-024`** e ca fluxul ROAAUTO **nu trece prin `contabilizeaza_articol`**: merge pe `scrie_in_vanzari`, care nu genereaza nicio nota de venit. Vezi **J-ter**. > **Corectie importanta la temeiul deciziei 18.** Ideea ca „«Alte servicii» face deja jumatate din ce > cerem, deci se generalizeaza” **nu se sustine**. Mecanismul acela functioneaza tocmai pentru ca > ocoleste contabilizarea de venit; generalizat pe fluxul ROAFACTURARE, unde `contabilizeaza_articol` > **este** apelata (`scrie_factura2`, `ff_...:6150`), s-ar lovi de `FACT-024`. Ce ramane valabil din > O-bis e descrierea UI (articole reale langa linii sintetice, pret tastat, fara gestiune) — nu si > concluzia ca partea grea e deja rezolvata. **Ce inseamna asta pentru decizia 18.** Doua lucruri, in directii opuse: - **usureaza** partea de model: un articol real langa linii sintetice **nu e o noutate**, e deja cazul normal al facturilor auto. Intrebarea „cum coexista” are deja un raspuns in produs; - **nu rezolva** cerinta: mecanismul traieste in formularul de emitere al ROAAUTO si moare odata cu el. Ce cere Marius e aceeasi capacitate **la modificare**, care nu exista nici acolo, nici aici. **Intrebarile ramase, in ordinea in care blocheaza:** 1. ~~**Ce se intampla cu totalul devizului**~~ — **RASPUNS (runda 7)**, raport `docs\cercetare\pack_auto_actualizeaza_deviz.md`. Premisa intrebarii era gresita: **nu exista niciun `SUM` peste `VANZARI_DETALII`, pentru ca `PACK_AUTO` nu citeste deloc `VANZARI` / `VANZARI_DETALII`** — zero potriviri pe `VANZARI` in tot pachetul (1804 linii). `actualizeaza_deviz` (`ff_2026_03_31_02_AUTO_PACK_AUTO.sql:692-733`) face trei lucruri, toate pe structura proprie ROAAUTO: scrie cota TVA pe `DEV_ORDL`, stampileaza `ID_FACT` pe `RUL`, si — doar pe anumite `id_set` — pe `NOM_LUCRARI`. E o legatura **deviz -> document**, nu un recalcul **document -> total deviz**. Nu e nici macar specifica facturarii: aceleasi trei apeluri o folosesc si la inchiderea de productie si de regie, care scriu note contabile, nu facturi. **Deci o linie noua pe `tip = -12` nu poate dezechilibra nimic pe partea Oracle**, si nu e nevoie de niciun apel suplimentar catre ROAAUTO. Precedentul „Alte servicii” confirma pe date live: liniile reale coexista de mult cu cele sintetice, fara ca `actualizeaza_deviz` sa faca distinctie. **Ce ramane, si e de alt fel:** o **desincronizare de afisare**. Totalul devizului aratat in ROAAUTO se calculeaza din `RUL` si din cursoarele de facturare (`oviz_devize.vc2:7816-7823`), **niciodata** din `VANZARI_DETALII` — deci nu va arata linia adaugata, nici azi, nici dupa #13. In schimb **relistarea o va arata**: `relisteaza_factura_deviz` citeste `fact_vfacturi_detalii` (`oproceduri_devize.prg:1564`), un view neconditionat peste `VANZARI_DETALII` (`ff_2024_06_04_01_COMUN_FACTURARE.sql:8-94`). Factura retiparita si ecranul de deviz vor spune lucruri diferite — **DECIS (decizia 28): e acceptabil.** Cerinta lui Marius e ca **MANOPERA si MATERIALE sa fie conform devizului**; restul articolelor pot exista pe factura fara sa apara in ecranul de deviz. Nu se cere cod nou in ROAAUTO si nu se mai pune intrebarea. 2. **Gestiunea, pentru articole cu stoc. DECIS (decizia 20):** se admit si articolele gestionabile. ROAAUTO ocoleste subiectul filtrand `in_stoc = 0`; filtrul acela **nu se preia**. Deci pe documentele auto vor coexista linii care descarca stoc cu linii care nu descarca — caz nou, de proiectat, nu de evitat. 3. **Cine ramane proprietarul documentului** dupa ce ROAFACTURARE ii adauga o linie — mai poate ROAAUTO sa-l relisteze corect (`relisteaza_factura_deviz` citeste din `fact_vfacturi`)? **Reconfirmat:** dupa emitere nu exista cale de intoarcere in ROAAUTO. `verifica_stornare` (`oviz_devize.vc2:4513-4514`) e o metoda **goala**; `do_storneaza_avans` priveste doar avansul. Singurul instrument gasit asupra unei facturi emise e stergerea totala (soft-delete) prin ecranul generic din COMUN — nu o editare. **Secventierea — reevaluata dupa raport (runda 7).** Marius a cerut ca `pack_auto` sa fie cercetat inainte de orice estimare (decizia 23), si a avut dreptate: raspunsul **rastoarna recomandarea initiala**. Motivul pentru care decizia 18 urma sa se livreze ultima era riscul tehnic de a scrie pe un produs pe care #13 nu-l controleaza. **Acel risc nu exista** — `PACK_AUTO` nu citeste `VANZARI` / `VANZARI_DETALII` deloc, deci nu are ce sa se dezechilibreze (vezi intrebarea 1 de mai sus). Ce ramane nu mai e risc tehnic, ci **o singura intrebare de produs**: 1. ~~e acceptabil ca ecranul de deviz sa nu arate linia adaugata din #13~~ — **RASPUNS, decizia 28: da, e acceptabil.** Conditia e alta: MANOPERA si MATERIALE conform devizului. 2. cine ramane proprietarul documentului (intrebarea 3 de mai jos) — nu s-a schimbat. **Decizia 18 nu mai trebuie sa fie ultima din motive tehnice.** Ordinea de lucru din S4g ramane insa valabila din alt motiv, mai bun: se face intai pe documentele ROAFACTURARE, unde scrierea e a noastra, pentru ca acolo se aseaza mecanismul; `tip = -12` vine dupa, ca **aplicare**, nu ca risc. **Perimetru:** gridul read-only din `frm_modific2024` si `ofacturare_editare.prg` sunt ale lui **#6**. Deschiderea lui la scriere nu se face din #13 fara intelegere explicita — un singur scriitor pe fisier. ### L. Observatii colaterale, gasite in timpul verificarii Nu fac parte din #13 si nu se repara aici — se semnaleaza ca sa nu se piarda. 0-ter. **(runda 9) O nota de vanzari cu set multi-rand inregistreaza venitul si descarca gestiunea de N ori.** In `pack_facturare.contabilizeaza_articol`, `cursor_articol` face `LEFT JOIN NOTE_CONTABILE D ON C.ID_SET = D.ID_SET` **fara `ROWNUM`, fara agregare, fara filtru** (`ff_2026_08_09_01_...:7218-7271`), iar bucla care il consuma (`:7393-7542`) executa `scrie_nota` **si** `descarca_gestiune` **o data pentru fiecare rand al setului, cu aceeasi cantitate si acelasi pret intreg** — nu impartite. Nu exista nicio logica de distributie: `ORDINE` nu apare in tot pachetul. Azi nu se manifesta pentru ca toate cele 7 note de vanzari active au exact un rand pe set — dar **30 din cele 40 de seturi din baza au mai multe, cu maxim 30 de randuri**. Deci e o bomba armata de configurare: cine ataseaza unei politici de pret o nota cu set multi-rand obtine **dublare de venit si dublare de descarcare de gestiune**, silentios. **E in `PACK_FACTURARE`, adica in COMUN** — se semnaleaza, nu se repara din #13. Conteaza direct pentru reteta din J-quater: vezi acolo. Sursa: `docs\cercetare\s10_pret_rederivat.md`, „Completare", p. 1 + `verif_baza_vie_cont_venit.md` p. 6. 0-quater. **(runda 9) Politica de pret fara `ID_NOTA` nu are nicio plasa de siguranta.** Spre deosebire de articolul lipsa din politica (`FACT-024`), aici nu exista cod de eroare: lantul de `LEFT JOIN` din `cursor_articol` supravietuieste inelului lipsa, cursorul intoarce **un rand cu totul `NULL`**, iar `scrie_nota` insereaza in `ACT_TEMP` un rand cu conturile de debit si credit **nule**. Daca `ACT_TEMP` are `NOT NULL` pe ele, iese un `ORA-01400` generic in loc de un mesaj `FACT-0xx` — **neverificat**, DDL-ul lui `ACT_TEMP` nu e in export. Starea exista in baza vie **azi**: politicile active `32 HOTEL TAXE` si `33 HOTEL CAZARE` n-au `ID_NOTA`. Independent de #13. 0-bis. **(runda 7) `do_modifica` pare sa scrie `id_valuta` in loc de `id_sectie`.** In `frm_modific2024.do_modifica` (`COMUN\clase\omodificari.vc2:13941`), ramura `CASE m.lcControl = 'sectie'` face `replace sectie with loCauta.sectie, id_valuta with loCauta.id_sectie`. Daca e ce pare, textul afisat al sectiei se schimba dar coloana `id_sectie` nu — si, in plus, se strica `id_valuta`. **Necitit pe date, doar pe cod.** Fisierul e in **perimetrul lui #6** (`omodificari.vcx` a intrat in proiect prin `97d1613`) — se semnaleaza acolo, nu se repara de aici. Conteaza pentru #13 pentru ca sustine partial verdictul „`ID_SECTIE` e editabil": teoretic da, practic poate nu. 0. **(runda 7) Handler-ul lui `FACT-024` se auto-saboteaza cand `id_pol` e `NULL`.** In `pack_facturare.contabilizeaza_articol` (`ff_2026_08_06_10_COMUN_PACK_FACTURARE.sql:7286-7311`), ramura `WHEN NO_DATA_FOUND` construieste mesajul cu inca doua `SELECT ... INTO`, dintre care al doilea e `FROM CRM_POLITICI_PRETURI WHERE id_pol = detalii_articol.id_pol`. Cu `id_pol` `NULL`, **si acel `SELECT` da `NO_DATA_FOUND`**, iar in handler nu exista al doilea nivel de tratare — deci utilizatorul primeste un `ORA-01403` generic in loc de mesajul `FACT-024` formatat. Articolul apucase sa fie rezolvat, deci informatia nu se pierde de tot, dar diagnosticul e mult mai prost. **E in `PACK_FACTURARE`, adica in COMUN** — se semnaleaza, nu se repara din #13. Devine relevant direct daca se alege varianta 3 din J-bis. 1. **`do_copiaza`, ramura de avize: variabila gresita.** La `ofacturare_comun.vc2:3702-3703`, ramura `CASE INLIST(loFactura.tip, T21, T24, T26, T30, ...)` scrie `lnTip = T22` in loc de `loFactura.tip = T22`, deci degradarea de tip **nu se aplica** pe acea ramura. **Fisierul e in perimetrul lui #6** — nu se atinge de aici; efectul nu a fost verificat pe date. 2. **„Proforma -> factura” merge prin omisiune.** Copierea nu propaga `nIdTipDoc` pentru ca linia e comentata (`ofacturare_comun.prg:370`), deci copia unei proforme porneste implicit pe FACTURA. Functioneaza, dar ca efect secundar, nu ca ramura dedicata. Daca #13 atinge copierea, merita transformat in intentie explicita. 3. **Asimetria de resetare a globalelor** — vezi „Canalul de precompletare”, mai sus. 4. **Modificarea in bloc pare sa goleasca campuri pe care nu le-ai atins.** Pe selectie multipla (`lnNrInreg > 1`), `do_modifica` face `Scatter Name poRec Memo Blank` si reseteaza explicit ruta, delegatul, agentul, masina, `dataora_exp`, `id_facturare`, `listare_detaliata`, `tip_saft`, `text_aditional` si `efactura` (`ofacturare_comun.vc2:4565-4580`). Cum acesti zece parametri se scriu **neconditionat** in `VANZARI` (I-bis), un `SCAN` peste facturile alese pare sa scrie valorile goale pe **fiecare** dintre ele — deci schimbarea rutei pe trei facturi ar sterge delegatul si textul aditional de pe toate trei. **De confirmat pe date inainte de a fi numit bug**: e posibil ca fluxul sa presupuna ca operatorul completeaza tot ce vrea propagat. **Fisierul e in perimetrul lui #6** — se semnaleaza, nu se repara de aici. Conteaza si pentru #13: formularul unificat lucreaza pe un singur document si trebuie sa trimita antetul intreg (I-bis, consecinta 1), deci nu mosteneste problema. **L.4 (runda 11). Numarul de POS nu se dezaloca la Renunt.** In `frm_alte_date`, la anulare se dezaloca numarul de chitanta (16) si cel de bon fiscal (3), dar **nu si cel de POS (26)** — `docs\cercetare\s3b_alte_date_analitice.md`, sectiunea 9. Bug preexistent, in productie, **nu introdus de #13**. Conteaza pentru S3b pentru ca „`actualizeaza_tipincasare` se muta ca atare" **l-ar propaga in formularul unificat**. Se repara in trecere sau se lasa ca azi si se preia separat — punct de decis, vezi lista de la finalul rundei 11. --- ## Stories Ordinea e strict pe risc: unificarea intai (livrabil de sine statator, util si fara regenerare), regenerarea peste ea. ## Metoda de executie (Marius, runda 14) — obligatorie, nu recomandare **Planul asta e un document de proiectare, nu un plan de executie. La implementare se SPARGE PE STORIES, si fiecare story se executa ca livrare de sine statatoare.** Nu se porneste implementarea pe tot #13 deodata, nu se acumuleaza modificari nelivrate peste mai multe povesti, si nu se trece la povestea urmatoare cu cea precedenta netestata si nerevizuita. ### Ce inseamna „spart pe stories" - **O story = o unitate de livrare.** Are perimetru propriu de fisiere, criteriu „gata cand" propriu (fiecare story de mai jos il are deja scris) si se incheie cu diff + review + commit propriu. - **Inainte de prima linie de cod dintr-o story**, se scrie o **nota de executie** in `docs\` care transpune proiectarea in pasi concreti: fisierele atinse, metodele atinse, ordinea editarilor, si ce se testeaza dupa fiecare. Proiectarea spune *ce* si *de ce*; nota de executie spune *unde* si *in ce ordine*. Rapoartele din `docs\cercetare\` sunt materia prima — nu se reface cercetarea. - **Povestile cu dependente declarate** (`*Depinde de:*`, la fiecare story) **nu se pornesc in paralel cu cele de care depind.** Unde nu e dependenta, se pot rula in paralel de agenti diferiti — dar **un singur scriitor pe fisier**, niciodata doi agenti pe aceeasi metoda. - **O story care se dovedeste prea mare la executie se sparge mai departe**, cu acordul lui Marius, si se noteaza in plan. Mai bine cinci livrari mici decat una care nu se poate revizui. ### Teste la fiecare pas — nu doar la S6 si S12 `S6` si `S12` raman testele **pe flux real**, la capatul fiecarei etape. **Ele nu inlocuiesc testarea per story.** Fiecare story se incheie cu teste proprii, rulate si trecute, **inainte** de review: - **Harness headless** (`COMUN\docs\depanare_testare_vfp.md`) pentru logica — cazurile minime ale povestii, plus **cel putin o proba de neregresie** pe calea veche, care trebuie sa ramana neatinsa. - **Harness UI vizibil** (`COMUN\docs\testare-ui-vfp.md`) unde povestea atinge formulare sau griduri — coloanele de grid **nu se materializeaza headless**, deci acolo verificarea headless nu e concludenta. - **Rezultatul asteptat se declara INAINTE de rulare**, inclusiv pentru cazurile care trebuie sa dea eroare. Un test scris dupa ce s-a vazut rezultatul nu dovedeste nimic. - **Un esec documentat e livrabil valid** — se raporteaza, nu se ascunde si nu se reia la nesfarsit. - Testele fiecarei povesti **se pastreaza**, si intra in suita rulata de S6 / S12. Nu se scriu de unica folosinta. ### Code review dupa implementare, inainte de commit — pentru fiecare story **Nicio story nu se comite fara review, si review-ul vine dupa implementare si dupa teste, nu in loc de ele.** Ordinea, fixa: 1. **Implementare** (delegata unui subagent, conform modului de lucru din `CLAUDE.md`). 2. **Teste** — rulate, trecute, cu rezultatele raportate. 3. **Diff ca fisier in `docs\`** (`diff_s_.patch`), inclusiv partea din `COMUN`. 4. **Code review pe diff**, de catre **un agent care nu a scris codul** — altfel isi revizuieste propriile presupuneri. Review-ul verifica cel putin: regresia pe calea veche, conventiile per zona atinsa (`COMUN\docs\reguli_lucru.md`, punctul 7 — encoding `cp1250` la `.vc2`/`.sc2`, `GO` pe `Recno()`, `goExecutor` + `ALTER TABLE`, UX formulare/griduri), comentariile (istoric **numai** in antetul fisierului, max o linie in cod), si ca write-back-ul text→binar e facut pentru fiecare fisier atins. 5. **Aprobarea lui Marius.** 6. **Commit** — in **ambele** repo-uri unde e cazul (ROAFACTURARE si COMUN), cu changelog. **Afirmatiile review-ului se verifica, nu se cred.** Un review poate citi structura corect si presupune semantica gresit; cand semnaleaza un defect, se deschide codul citat inainte sa fie acceptat — la fel cum se procedeaza cu rapoartele de cercetare. **Ce NU face review-ul:** nu reargumenteaza deciziile luate (lista lor e in acest plan), nu extinde perimetrul povestii, si nu propune refactorizari colaterale. Ce gaseste in afara perimetrului se **raporteaza separat**, nu se repara in trecere — exact cum s-a procedat cu defectul `lnTip` din `do_copiaza`. ### Etapa I — formularul unificat #### S1 — Inventarul de campuri si alegerea bazei Deciziile de perimetru sunt luate (vezi listele de la inceput). Ramane: **se porneste de la prototipul `frm_facturare_articole2` sau de la `frm_facturare_articole` extins?** Recomandare: **de la prototip**, si acum cu trei argumente in plus fata de runda 2 — are deja controalele de antet, are butoanele **deasupra** gridului (decizia 13), si are coloanele de discount editabile plus procentul pe linie (decizia 14). Partea grea de layout e deja acolo; logica lipsa se porteaza in el. Livrabil: tabel camp-cu-camp `frm_date_factura` / `frm_date_aviz` / `frm_modifica_factura` / `frm_alte_date` -> formular unificat, cu ce se pastreaza, **in ce grup din cele patru intra** (I), ce se pliaza, ce dispare, si **cu ruta de scriere a fiecarui camp** (pe loc sau prin regenerare — vezi G-bis). Baza de pornire: `docs\cercetare\inventar_controale_formulare.md`, care are deja etichetele reale si conditiile de vizibilitate. **Livrat (runda 7): `docs\S1_inventar_campuri_formular_unificat.md`** — 30 de campuri de antet, cu grupul din I, conditiile de vizibilitate si ruta de scriere pentru fiecare, plus randul dedicat lui `V_EFACTURA` (singurul camp fara control azi, nicaieri) si cele patru bife marcate ca disparute. Tabelul a scos la iveala **golul de rute de scriere** din G-bis — vezi acolo. Are si o sectiune „Neclarificate" cu opt puncte, dintre care doua ambiguitati de nume (`chkDetaliat`, `cboTipFactura` apar in doua formulare si nu s-a confirmat pe cod ca scriu acelasi camp). **Coloana „ruta de scriere" e completa acum** — `rute_scriere_antet.md` a inchis cele trei grupuri lasate neclarificate, iar deciziile 25 si 26 le-au dat ruta. Tabelul se citeste **impreuna cu tabelul de rutare din G-bis**, care e sursa de adevar pentru ruta fiecarui camp; sectiunea „Neclarificate" din S1 ramane valabila doar pentru punctele **4, 5, 7 si 8** (ambiguitatile `chkDetaliat` / `cboTipFactura`, liniile exacte de definire a controalelor, si dependenta de drepturi). *Gata cand:* tabelul e aprobat. **APROBAT de Marius, 09.08.2026 — S1 e incheiata.** Tabelul devine referinta pentru S3 si S3b; modificarile ulterioare se fac in el, nu se rescrie de la zero. **ASEZAREA ZONEI DE JOS — INCHISA prin decizia 57 (Marius, runda 15): VARIANTA D.** Cerinta initiala (runda 14), formulata de el: *„totalurile de jos sa fie mai compacte, si sa includa si discount-ul; formularul trebuie sa fie compact si aerisit; incasarea si alte date jos de tot, eventual 2 butoane pe acelasi rand — vezi modelul Saga; in centrul atentiei sa fie datele facturii"*. Referinta vizuala: `{06D747B8-0824-488C-8832-DEB9AE661974}.png`. Rundele 14-15 au dat patru variante (A / B / C, apoi D). **Aleasa: D.** Vezi „Decizia 57" la deciziile rundei 15, care are enuntul complet, cele patru consecinte de implementare si punctul lasat deschis. Ce e de retinut aici, pentru executia lui S1: - **Antetul se strange la doua randuri**, fara titluri de grup — campurile se recunosc dupa eticheta. - **Panoul separat „Discount pe document" dispare**; discountul devine rand editabil in banda de totaluri, cu procentul pe loc. - **Banda de totaluri** e pe toata latimea, lipita de grid: baza, discount articole, discount document cu procentul, TVA — desfacute pe un rand — si totalul mare singur la dreapta. - **Incasare si Alte date NU sunt dialoguri** — sunt doua sectiuni colapsabile in formular, **una langa alta pe acelasi rand**, sub totaluri, fiecare cu rezumatul continutului pe randul inchis. - **Bara de comenzi de jos nu exista**: `but_renunt` / `but_termin` raman in banda de titlu. **Cota de TVA a discountului de document s-a decis — decizia 59, runda 16** (vezi sectiunea K-bis): repartizare proportionala automata pe cote, discountul **NU cere cota de la utilizator**, deci **nu e nevoie de o a treia sectiune pentru cota**. Ce mai cere UI e **un camp text optional de motiv** (pentru `AllowanceChargeReason`), mult mai mic decat o sectiune. **Decizia 61 (runda 16) l-a materializat: Marius vrea campul. Decizia 64 (runda 16) a inchis si asezarea: randul se imparte in trei** — incasare, alte date si motivul discountului, pe acelasi rand (varianta grea, cota + explicatie proprii, ramane exclusa, ca mai sus). Pagina cu variantele: **https://claude.ai/code/artifact/dfc70d38-856f-48fd-b6af-3da78e9da4f7** — **nu mai are copie pe disc** (cerinta lui Marius, runda 15: „doar online, ca sa nu mai intretii 2 variante"). Procedura de modificare: `WebFetch` pe URL → fisier de lucru **in scratchpad, nu in `docs\`** → `Artifact` cu `url` = link-ul de mai sus. #### S2 — O singura procedura `factureaza` Se elimina `factureaza2` ca procedura paralela; `factureaza` primeste un mod (`gnFacturareNou` ramane comutatorul de rulare, dar nu mai duce la alt cod duplicat). Se pastreaza integral ramurile care lipsesc azi din `factureaza2` (lista din A). **PROIECTAT (runda 9) — `docs\cercetare\s2_factureaza_unificare.md`.** **S2 e mult mai ieftina decat arata planul.** `factureaza2` nu e a doua implementare functionala care cere fuziune atenta — e un fork din 08.06.2017 la care **executia interogarii de articole e dezactivata** (`lnSucces = 1` hardcodat, `goExecutor.oExecute` niciodata apelat) si mai multe blocuri intregi sunt inchise cu `If .F.`. Are **exact un apelant** in tot codul — `ofacturare.prg:90`, din interiorul lui `factureaza`, in spatele unui `AMESSAGEBOX` de confirmare — deci **zero utilizatori reali**. Ambele sunt proceduri globale in `COMUN\programe\ofacturare.prg` (`:81-577` si `:583-1081`, ~500 de linii fiecare), fara omonime. Singura diferenta functionala care justifica existenta lui `factureaza2` e formularul de articole (`frm_facturare_articole2` vs `frm_facturare_articole`). **Obstacolul nu e tehnic:** formularul spre care duce e tot un prototip neterminat, deci unificarea procedurilor **nu** face formularul nou utilizabil — doar elimina duplicarea. Aia rămâne treaba lui S3. **Trei corectii la lista din sectiunea A:** `verifica_numar(16, ...)` pentru chitanta **nu lipseste** din `factureaza2` (e identic, `:502-504` vs `:1018-1020`) — planul greseste; „`cursor_avize` cu tip 23" e imprecis — tipul 23 nu lipseste, e **rutat diferit** (`cursor_preturi` in `factureaza` vs `cursor_gestiune` in `factureaza2`), ceea ce e mai grav decat o omisiune; si lipsesc din plan **tipul 52** pe ramura de contract si **tipul 24** pe ramura de retur. Semnatura propusa, ramificarea interna, ordinea in care se face fara sa strice suita si criteriul de „gata" verificabil sunt in raport, sectiunile 5-7. **`ofacturare.prg` e cod comun al intregii suite** (copie in fiecare produs ROA, sincronizata manual) — la fel ca S3c, nu se face impreuna cu alta modificare. *Gata cand:* `factureaza2` nu mai exista, iar comutatorul alege formularul, nu procedura. *Depinde de:* S1. #### S3 — Portarea logicii de antet in formularul unificat Cele 17 + 13 metode `do_cauta_*`, `do_schimba_tipdoc`, validarile din `inainte_de_do_termin` (`ofacturare.vc2:9455-9561` si `:7277-7352`) si `Init`-urile. Se porteaza **o singura data**, cu ramificare pe factura / aviz in interior, nu doua copii. Punct de atentie cunoscut: **#16 din `todos.txt`** — la revenirea din formularul de curs valutar, focusul sare pe tip document si iesirea din serie regenereaza numarul actului. **PROIECTAT (runda 9) — `docs\cercetare\s3_portare_antet.md`.** Doua rezultate schimba povestea: - **Portarea „o singura data cu ramificare" nu e posibila ca atare.** `Init`, `inainte_de_do_termin` si partial `do_cauta_fdoc` sunt **omonime cu semantica total diferita pe toate cele patru formulare-sursa** (antet vs. articole vs. alte-date). Deci nu e o metoda cu ramificare interna, sunt **patru fuziuni de metoda**. Obstacolul principal al lui S3 nu e codul de validare — majoritatea e `Do Case` / `amessagebox` mecanic — ci **reconcilierea a patru cicluri de viata `Init` / `inainte_de_do_termin` independente intr-unul singur**, cu ordinea de dependente din sectiunea 5 a raportului. Numarul real de `do_cauta_*` e **29, nu 30**. Lista completa a omonimelor e la sectiunea 2 — **se citeste inainte de orice editare**, altfel portarea le confunda (capcana deja platita o data cu `do_calculeaza_discount`). - **#16 nu e un bug de focus, si nu e in perimetrul in care il caută planul.** Reprodus pe cod pana la linia exacta: bucla de reincercare din `factureaza` / `factureaza2` (`ofacturare.prg:174-571`) trateaza **orice esec SQL** — nu doar cursul valutar lipsa — ca pe un „DA, continui cu alt document", si redeschide formularul de antet de la zero. Deci **unificarea nu-l reproduce automat**; poate chiar sa-l elimine ca efect secundar, daca antetul nu se mai reconstruieste din `Init` dupa un esec Oracle in pasul urmator. **Cade premisa „ori se rezolva #16 aici, ori se reproduce bug-ul".** Si leaga #16 de **S2**, nu de S3 — bucla e in procedura, nu in formular. *Gata cand:* un document se emite integral din formularul unificat, pe tip 1 (lista de preturi), cu acelasi rezultat in `vanzari` / `act` / `rul` ca pe calea veche — criteriul rescris verificabil e la sectiunea 7 a raportului, iar ce **nu** se poate testa headless la sectiunea 8. *Depinde de:* S2. #### S3b — Sectiunea pliata: alte date + analitice Deciziile 7 si 8. Se muta in formular cele patru grupuri din `frm_alte_date` (B) si coboara acolo analiticele din antet. `actualizeaza_tipincasare` se muta ca atare; **alocarea si dezalocarea numerelor** de chitanta / bon fiscal / POS raman legate de aceleasi evenimente ca azi, nu de deschiderea formularului. `frm_alte_date` nu se sterge cat timp calea veche mai e in uz. **Doua precizari din runda 7, amandoua restrang povestea:** - **analiticele coboara ca AFISARE, nu ca editare** (decizia 26) — venit/cheltuiala, sectie, responsabil, lucrare sunt read-only in formularul unificat; se editeaza din editorul de nota al lui **#6**, care le trateaza pe linie. Eticheta / tooltip trebuie sa spuna asta, altfel campul pare stricat; - **pe un document deja emis, grupul de incasare e blocat in etapa I** (decizia 25) — nu are ruta de scriere pe loc, iar regenerarea nu exista inca. La emitere se comporta ca azi. **PROIECTAT (runda 11) — `docs\cercetare\s3b_alte_date_analitice.md`.** S3b nu e o mutare de controale. Trei rezultate schimba povestea: - **Riscul din criteriul de „gata" e real, si are mecanism.** Alocarea numarului de chitanta nu porneste din clicul utilizatorului, ci din **orice atribuire programatica** a lui `.Value`: `opt_incasat.ProgrammaticChange` (`ferestre_cere_date.vc2:3351-3352`) cheama acelasi `actualizeaza_tipincasare()` ca `.Click` (`:3250-3251`). Consecinta neasteptata: **azi, deja**, `Init` (`:3150`) seteaza singur `opt_incasat.Value = 2` cand documentul soseste cu `poDate.incasat<>0` (cazul copierii), deci deschiderea dialogului aloca un numar fara ca cineva sa atinga ceva. Intr-un formular unde zona se plieaza si se deplieaza repetat pe acelasi `poDate` persistent, o repopulare naiva a starii ar aloca si dezaloca **la fiecare toggle**. Solutia proiectata: populare **o singura data**, iar plierea strict pe `Visible`/`Height` — niciodata pe reasignare de `.Value`. Criteriul de non-alocare e formulat ca **assert headless pe `poGeneratorNumere`**, pe doua scenarii de start (document nou **si** document copiat cu incasare presetata — al doilea e cel care azi chiar aloca). - **Nu exista mecanism de pliere de refolosit.** Cautare exhaustiva in `ofacturare.vc2`: zero potriviri in prototipul `frm_facturare_articole2`. Cel mai apropiat tipar din suita e `frm_modific2024.afiseaza_rulaje` (`omodificari.vc2:13169-13199`, perimetrul #6 — **citit, neatins**), acelasi idiom sus/jos validat deja pentru `but_modifica`/`but_salveaza` la decizia 9. Deci **cod nou**, dupa un tipar existent, nu o clasa reutilizabila. - **Decizia 26 cere mai mult decat mecanismul existent.** `ct_clb_cautare.do_dezactiveaza()` (`caut_ora.vc2:800-806`) ascunde **doar lupa de cautare**; textbox-ul ramane tastabil. Read-only real cere `ReadOnly` explicit pe langa el — nesemnalat pana acum. **Bug preexistent, gasit in trecere:** la Renunt se dezaloca chitanta (16) si bonul fiscal (3), dar **nu si POS (26)**. Nu e introdus de S3b, dar „`actualizeaza_tipincasare` se muta ca atare" **l-ar propaga**. **Decizia 40: se repara aici, in trecere** — deci diff-ul lui S3b nu mai e o mutare pur mecanica, si testarea acopera si dezalocarea POS pe calea veche. **Decizia 41 fixeaza granularitatea plierii: doua comutatoare.** Grupul de **incasare** are comutator propriu — e singurul cu efecte laterale (alocare/dezalocare) si singurul blocat pe document emis prin decizia 25 — iar delegat/transport, adresa, text aditional si analiticele stau impreuna sub al doilea. Garda de non-alocare la toggle se scrie si se testeaza astfel **intr-un singur loc**. *Gata cand:* criteriul rescris verificabil e la sectiunea 10 a raportului, in cinci puncte — mai strans decat formularea de mai jos pe trei dintre ele: paritatea de emitere se cere pe **toate patru** tipurile de incasare (nu doar bon fiscal), non-alocarea la toggle se cere pe **ambele** scenarii de start, iar blocarea grupului de incasare si read-only-ul analiticelor se cer pe **ambele stari ale antetului** (inainte si dupa `but_modifica`), nu doar la deschidere. Formularea initiala, pastrata ca rezumat: un document cu incasare prin bon fiscal emis din formularul unificat produce aceleasi randuri si acelasi numar de bon ca pe calea veche; deschiderea si inchiderea formularului fara a atinge zona de incasare **nu aloca si nu dezaloca niciun numar**; iar pe un document emis analiticele se vad dar nu se pot edita. *Depinde de:* S3. #### S3c — Sursa ca parametru, nu ca global Decizia 12. Punctele de intrare exista deja (comanda: `ocomenzi.vc2:1580-1596`; contract: din ROACONTRACTE), dar transmit prin globalele `goComanda` / `goContract`. Se adauga parametru explicit pe `factureaza` si pe `oDateFactura`, cu globalele pastrate ca sursa de rezerva pana se convertesc toti apelantii (inclusiv cei din ROACONTRACTE si ROAGEST — e cod comun). Se verifica intai asimetria de resetare semnalata la L.3. **PROIECTAT (runda 11) — `docs\cercetare\s3c_sursa_ca_parametru.md`.** Se poate face, e o interventie mica — dar **planul supraestimeaza cat de „globala" e problema azi**, si in doua sensuri opuse: - **`goContract` nu e scris nicaieri in ROAFACTURARE** — nici in codul produsului, nici in `COMUN`-ul lui. Deci ramura de precompletare din contract (`ofacturare_comun.prg:261-297`) e **cod mort in acest produs**, iar **asimetria de resetare de la L.3** (`ofundal_facturare.vc2:886-902`) exista textual dar e **inerta**: n-are ce sa lase nereseta, pentru ca n-are scriitor. Devine risc real abia daca cineva adauga in viitor un scriitor in ROAFACTURARE — moment in care lipsa resetarii s-ar activa **tacut**. - **`goComanda` chiar e viu, si are DOI scriitori**, nu unul: clicul pe „Factureaza" (`ocomenzi.vc2:1583`) **si** navigarea in grid (`:2199`, doar ca sa decida vizibilitatea unui buton). Al doilea n-are nicio legatura cu facturarea si **ramane si dupa conversie** — deci globala nu dispare. - **Criteriul „pana se convertesc toti apelantii" nu se poate citi ca „pana dispare globala".** In ROACONTRACTE, `goContract` e **bufferul de editare al intregului ecran de contracte** (peste 100 de `ControlSource` legate de el), populat de navigarea in grid, independent de facturare. Nu dispare la aceasta poveste si nici la vreuna rezonabila urmatoare; S3c schimba **doar canalul** prin care valoarea ajunge la `oDateFactura.Init`. **Suprafata de regresie, masurata:** doi scriitori de convertit (`ocomenzi.vc2:1580-1596`; `ferestre_contracte.vc2:1538-1549` + `:1598-1606` in ROACONTRACTE), **trei** fisiere comune atinse o singura data fiecare (`ofacturare.prg`, `oproceduri_facturare.prg`, `ofacturare_comun.prg`), si **zero schimbari** la celelalte ~31 de puncte de intrare din inventarul S2 — toate cheama `factureaza(N)` cu un singur argument. Cele cinci fisiere sunt **identice pe MD5** in cele sapte produse, cu o singura exceptie: **ROAIMOB**, o linie lipsa in `ofacturare_comun.prg` — acolo diff-ul se aplica **manual**, nu prin copiere mecanica. **Semnatura propusa:** `factureaza(tnTip, toFactura, toSursa)` si `oDateFactura::Init(tnIdSet, tnTip, toSursa)` — parametru nou la coada, cu implicit, fallback pe global cand lipseste. **Capcana de limbaj, semnalata explicit:** garda se scrie `Type('toSursa') = 'O'`, **nu** `Type('toSursa') <> 'U'`. Un parametru VFP nepasat **nu** e `'U'` (aia e pentru variabile nedeclarate), ci `'L'` cu `.F.` — o garda `<> 'U'` ar trece mereu adevarat si ar incerca sa citeasca `.id_part` de pe `.F.`, eroare la primul apel neconvertit. Raportul cere verificarea comportamentului `Type()` pe un `.prg` de proba **inainte** de a atinge fisierele reale. *Gata cand:* criteriul rescris e la sectiunea 10 punctul 4 al raportului, pe patru probe — una structurala (parametrul prezent cu semnatura identica in toate cele trei fisiere comune, in toate cele sapte produse) si trei comportamentale: **calea convertita** cu globala deliberat „murdara" dintr-o navigare anterioara produce documentul dat prin parametru, nu pe cel din globala; **calea neconvertita** (oricare din cele ~31 de apeluri cu un singur argument) produce acelasi document ca inainte; iar **ROACONTRACTE** da aceeasi precompletare ca azi, verificat manual pe build separat. *Depinde de:* S2. **Atinge cod comun intregii suite** — nu se face impreuna cu alta modificare. #### S4 — Cautarea articolelor pe server, in linie Partea Oracle: variante filtrate pe articol ale cursoarelor de facturare, cu aceleasi ramuri pe tip ca `cursor_preturi` / `cursor_contract` / `cursor_comanda` / `cursor_avize` / `cursor_gestiune`. Partea VFP: `combosql` pe cod si pe denumire completeaza linia cu pret, cota TVA, valuta, `id_pol`, `gestionabil`, `pret_cu_tva`; `crsarticole` nu se mai incarca in masa. Se pastreaza "adauga tot" pentru tipurile care au document sursa (comanda, aviz, contract) — acolo setul e marginit si incarcarea lui e legitima. **PROIECTAT (runda 11) — `docs\cercetare\s4_cautare_articole_server.md`. Criteriul de mai jos nu e atingibil ca atare, si motivul nu e UX.** - **`crsarticole` nu e doar sursa de populare a gridului — e un registru al cantitatii ramase de facturat.** E citit **si scris** de `do_adauga_tot`, `do_sterge` si `do_scrie_factura` pentru toate tipurile cu document sursa: stergerea unei linii **reface** cantitatea in `crsarticole` (`ofacturare.vc2:14640-14669`), iar la scriere se face `Calculate Sum(cantitate) To lnCantitateRamasa` peste el ca sa se decida daca se inchide automat comanda / avizul (`:14303-14311`, `:14334-14338`). Deci pe **comanda, aviz si contract** incarcarea in masa **nu poate disparea**, indiferent de decizia de UX privind „adauga tot" — bookkeeping-ul e cablat pe cursorul incarcat. **S4 se aplica curat doar pe ramurile de lista de preturi** (`cursor_preturi`, plus jumatate din `cursor_contract` si `cursor_gestiune`). **DEPASIT de proiectarea punctului 2 (runda 12)** — se citeste doar ca istoric al deciziei 39. Concluzia „incarcarea in masa nu poate disparea" cadea pentru ca `crsarticole` era tratat ca un registru omogen; sunt de fapt **doua mecanisme distincte** (Rol A / Rol B), si niciunul nu cere cursorul incarcat. Vezi mai jos. - **`combosql` e un prototip la jumatate, si e singura lui utilizare din suita.** `grd_factura.cCodMat.cboCodmat` / `cCboDenumire` (`ofacturare.vc2:16751-16797`, wiring la `:19288-19334`) chiar cauta pe server, dar scrie **doar** `codmat` / `denumire` / `id_articol`, pentru ca sursa lui (`vnom_articole`) n-are pret, TVA, valuta, `id_pol`, `gestionabil`. Cautare in `COMUNROA` si `ROAGEST`: **zero alte utilizari** — nu exista de unde copia un exemplu complet. Confirma insa exact punctul C: de facut e **varianta filtrata pe articol a celor cinci cursoare**, nu o cautare noua. - **`cursor_contract` produce deja doua cursoare** — `V_CURSOR` (`crsarticole`, prin delegare la `cursor_preturi`) si `V_CURSOR2` (`crsarticole1`, articole **sau** rate). Filtrarea se aplica curat doar pe jumatatea `crsarticole`; **randurile de rata n-au `id_articol`** si raman needitate. - **Vizibilitatea de azi a butonului „adauga tot" coincide aproape exact cu impartirea utila pentru S4** (`ofacturare.vc2:15108-15248`: ascuns pe lista de preturi, vizibil pe comanda / aviz / retur) — confirmare pe cod, nu presupunere. - **Legatura cu S10, confirmata:** pretul se cauta **o singura data**, la alegerea liniei, si se transmite mai departe neschimbat — exact ce face azi `adauga_articol_factura`, care il primeste ca parametru in loc sa-l re-derive. - **Efect secundar al filtrarii, rezolvat prin decizia 42:** `verifica_cursuri_valute` e azi neconditionata in procedura, deci pe varianta filtrata s-ar declansa la **fiecare** cautare de articol in loc de o data la deschidere — cu `-20005` posibil pe o valuta pe care operatorul n-o foloseste, inainte sa vada vreun rand. Se **restrange la valuta articolului cautat**. - **Cele doua puncte „de inchis inainte de implementare" sunt INCHISE (runda 12)** — `docs\cercetare\s4_puncte_deschise.md`. Raspunsul de baza e la amandoua „filtrarea nu schimba nimic", dar **fiecare lasa in urma o cerinta de implementare, nu doar o bifa**: - **`id_jtva_coloana` chiar lipseste** din `cursor_preturi`, din `cursor_gestiune` si din `V_CURSOR2` al lui `cursor_contract`, pe toate ramurile — confirmat pe SQL. **Nu e bug:** `do_initializeaza_articol` pune `0`, iar `frm_articol_factura.Init` (mostenit si de `frm_articol_gest_factura`) **il rederiva mereu** din `proc_tvav` via `jtva_coloane`, pe orice linie adaugata prin `do_adauga_articol`. **Cerinta care rezulta pentru S4:** derivarea tine **numai** daca linia trece prin `do_adauga_articol`. `combosql`-ul prototip de azi (`ofacturare.vc2:19288-19306`) face `REPLACE` direct in `crsfactura` si **ocoleste toata derivarea**; daca S4 extinde acel `REPLACE` fara sa treaca prin `do_adauga_articol`, `id_jtva_coloana` ramane nederivat si Oracle (`adauga_articol_factura`, ramura `ELSE`) arunca **`-20000 FACT-013`** sau scrie cota gresita. **Bug nou posibil, introdus de S4** — intra ca cerinta explicita in proiectare, nu ca observatie. - **`but_urmator_tot1` are `Visible = .F.` la design** (`ofacturare.vc2:11265`); tipurile 23 si 41 au `Case` propriu, dar **niciunul nu-l face vizibil** — confirmat, nu presupus. Nu exista alt mecanism de „adauga tot"; exista insa `but_urmator1` (adaugare rand-cu-rand), **neconditionat de tip**, care merge prin acelasi `do_adauga_articol` — deci dupa S4 **nu se pierde nimic** pe aceste tipuri. - **Corectie de rutare fata de ce presupunea planul:** pe formularul **standard** (`factureaza`, `ofacturare.prg:266-308`) doar **tipul 41** cheama `cursor_gestiune`; **tipul 23 cheama de fapt `cursor_preturi`** (grupat cu lista de preturi). Tipurile **45, 48, 49 nu ating deloc** `cursor_gestiune` (45 → `cursor_preturi`, 48/49 → `cursor_articole_k`, alta procedura). Doar pe **prototip** (`factureaza2`, opt-in `gnFacturareNou`) 23 si 41 merg impreuna pe `cursor_gestiune` — **divergenta reala intre cele doua formulare**, semnalata, neatinsa, in afara perimetrului S4. **Decizia 39 largeste povestea peste ce propunea raportul.** Raportul recomanda restrangerea la lista de preturi, tocmai pentru ca registrul de cantitate ramasa blocheaza restul; Marius a ales sa se desfaca si registrul. Deci S4 are de acum **doua bucati, nu una**: 1. **varianta filtrata a cursoarelor** + completarea liniei din `combosql` (partea proiectata in raport); 2. **decuplarea bookkeeping-ului de cantitate ramasa** de cursorul incarcat in masa — sursa unica, nu o a doua copie tinuta in pas cu prima. Atinge `do_adauga_tot`, `do_sterge`, `do_scrie_factura` si **inchiderea automata** a comenzii / avizului. **Punctul 2 — PROIECTAT (runda 12): `docs\cercetare\s4_punct2_registru_cantitate_ramasa.md`.** Iese mult mai ieftin decat parea, pentru un motiv care nu se vedea din raportul punctului 1. - **`crsarticole.cantitate` are DOUA roluri, nu unul** — si numai unul e „registrul" temut. **Rol A — cantitate ramasa de facturat dintr-un document sursa** (comanda `3,21,25,28,42,47`; avize `4`). **Rol B — plafon de cantitate in sesiune** (lista de preturi gestionabila `1,22,29` si jumatatea de contract, transfer `23,41`, retur `8,9,24`): impiedica operatorul sa adauge mai mult decat vede pe ecran. **Rol B nu alimenteaza nicio decizie Oracle** — e plafon de UI, nu registru de business. - **Pe Rolul A, cursorul VFP e o copie redundanta a unui calcul pe care Oracle il repeta oricum.** `do_scrie_factura` sumeaza `crsarticole` doar ca sa decida ce trimite in `pnParametruAditional`, iar Oracle **recalculeaza independent, din tabele reale**, in `inchide_comanda()` / `marcheaza_facturat()`, chiar in procedura care scrie factura. - **Nu exista coloana `INCHISA`.** Cautare in tot pachetul: **zero potriviri** (verificat separat de sesiunea principala). „Comanda inchisa" e o stare **derivata** din `COMENZI_ELEMENTE` vs `VANZARI_DETALII`; `inchide_comanda()` insereaza un **rand compensator**, nu seteaza un flag. Pe comanda, `pnParametruAditional` e strict **binar** — „s-a cerut fortarea inchiderii?" —, iar cantitatea trimisa de VFP **nu participa** la calculul Oracle. - **Pe avize e altfel, si aici sta subtilitatea:** `VANZARI.FACTURAT` **e** un flag persistat, iar `V_VERIFICARE` (= `pnParametruAditional`) alege intre „**increde-te in VFP** si marcheaza toate avizele referite" (`0`) si „**recalculeaza per-aviz** din tabele reale si marcheaza doar cele cu `ramas = 0`" (`1`). Deci pe aviz suma din VFP schimba semantica, nu doar declanseaza o actiune. - **Un bug preexistent, de REPRODUS identic, nu de reparat in trecere:** cand un `crsarticole` agrega mai multe avize, suma globala poate da `0` desi un aviz are ramas si altul are exces care-l compenseaza — caz in care toate se marcheaza facturate, inclusiv cel cu ramas real. Varianta noua pastreaza aceeasi conditie de declansare (suma globala pe tranzactie, nu per document). - **`23` si `41` nu apar deloc in `CASE`-ul de finalizare** — cad pe `ELSE`, fara nicio inchidere: transferul n-are document sursa de inchis, doar plafon (Rol B). Contractul (`2,6,52`) intra pe `scrie_rate_factura`, care **nu e o inchidere**, e alta operatie. - **Recomandarea: (b) pentru Rolul A, (c) pentru Rolul B.** **(b)** cele doua `Calculate Sum` din `do_scrie_factura` se inlocuiesc cu un apel Oracle facut **dupa** `do_scrie_articole()` (cand `VANZARI_DETALII_TEMP` contine exact liniile pe cale sa fie scrise) si **inainte** de `Do Case`-ul care alege procedura de scriere. Cele doua functii Oracle noi sunt o **extragere** a interogarii pe care `inchide_comanda` / `marcheaza_facturat` o ruleaza oricum, nu logica noua. Efect secundar gratuit: dispare si un bug latent de concurenta — cursorul local nu vede azi ce a facturat intre timp alt operator din aceeasi comanda. **(c)** plafonul Rol B se cere pe server **la fiecare adaugare/editare**, minus ce e deja in `crsfactura` — deci nu mai exista a doua copie de tinut in sincron. Varianta cu un cursor propriu, minimal, a fost **respinsa motivat**: ramane tot o a doua copie manuala, doar mai ingusta. Nu e „sursa unica". - **Cazul limita cerut in plan** (adauga si sterge inainte de salvare) devine **trivial**, nu doar acoperit: liniile adaugate-si-sterse nu ajung niciodata in `VANZARI_DETALII_TEMP`, deci nu influenteaza calculul, fara nicio actiune de refacere. - **CORECTIE asupra punctului 1:** pasul lui 5 (`s4_cautare_articole_server.md:417-424`) opreste incarcarea in masa si pe `23,41`, presupunand ca n-au bookkeeping — **au Rol B**, confirmat pe cod. Punctul 1 **nu se poate aplica pe `23,41`** inainte ca punctul 2 sa acopere Rolul B pe ele. **Toate trei sunt INCHISE (runda 13). Nu se mai reiau.** 1. ~~**Asimetria din `do_modifica`**~~ — azi, pe grupul `1,22,29` + contract-lista, plafonul **nu** se ajusteaza la editarea cantitatii unei linii deja adaugate. E preexistenta. **INCHIS — decizia 45 (runda 13, pe recomandare):** se lasa sa se **corecteze de la sine** prin recalculul la cerere, cost zero, dar corectia se **declara explicit in changelog** — S4 schimba atunci un comportament punctual, nu doar „decupleaza". 2. ~~**Corectia pe `23,41`**~~ — **INCHIS — decizia 46 (Marius, runda 13): asteapta punctul 2.** Punctul 1 **nu se redeschide** acum; aplicarea lui pe `23,41` vine odata cu recalculul pe server, care acopera Rolul B. Nu se pierde nimic: ordinea de implementare oricum le pune dupa. 3. ~~**Contract `26,52`**: nu s-a gasit dovada nici de prezenta, nici de absenta a Rolului B.~~ **INCHIS de proiectarea S5** (`docs\cercetare\s5_acoperire_tipuri.md`): `26` si `52` **n-au niciun bookkeeping**, nici Rol A, nici Rol B — excluderea e totala, pe `Do Case` exhaustiv fara ramura implicita. Deci „dovedit absent", nu „nepresupus". Nu mai e o decizie de luat. **Cerinta de revizuire**: cele doua functii Oracle noi sunt o extragere din proceduri existente, dar raman **PL/SQL nou in `PACK_FACTURARE`**, cu `JOIN`-urile reproduse **din citire, nu din executie**. Se verifica pe Oracle inainte de a continua — e primul pas al implementarii, nu o formalitate. **Nota de executie (decizia 60):** pe documentele de tip **48/49** (custodie), cautarea pe server in linie ramane restransa la articole `IN_STOC = 0` — nu se ofera articole gestionabile pe aceste tipuri. Siguranta regenerarii la editarea custodiei (decizia 60, `docs\cercetare\custodie_48_49_stergere_reemitere.md`) atarna de acest invariant. *Gata cand:* patru probe, nu una. **(a)** Deschiderea formularului nu mai executa niciun cursor de articole, **pe niciun tip** — nu doar pe lista de preturi. **(b)** Alegerea unei linii produce aceleasi valori ca randul corespunzator din `crsarticole` de azi, pe fiecare tip; maparea camp-cu-camp e la sectiunea 6 a raportului, cu coloanele semnalate explicit acolo unde varianta filtrata **nu** poate produce aceeasi valoare. **(c)** **Paritate pe inchiderea automata** (decizia 39): acelasi document sursa, aceleasi linii facturate partial, **aceeasi stare finala** — pe comanda, acelasi rand compensator in `COMENZI_ELEMENTE` (nu exista flag `INCHISA` de comparat); pe aviz, exact aceleasi `VANZARI.FACTURAT` marcate, inclusiv in cazul in care avizele agregate se compenseaza intre ele — pe fiecare tip cu document sursa — inclusiv cazul in care operatorul adauga si apoi sterge linii inainte de a salva, care azi trece prin refacerea cantitatii in `crsarticole`. **(d)** O linie adaugata prin cautarea in grid ajunge in `crsfactura` cu **`id_jtva_coloana` derivat** — adica trece prin `do_adauga_articol`, nu prin `REPLACE` direct ca prototipul de azi; proba e ca documentul se scrie fara `FACT-013` si cu aceeasi cota ca pe calea veche. **(e)** Pe un document de tip 48/49, cautarea pe server nu ofera si nu permite adaugarea unui articol cu `IN_STOC <> 0` (decizia 60). *Depinde de:* S3. **Punctul 2 nu e proiectat** — se proiecteaza separat inainte de implementare. #### S4b — Bara de butoane si meniul de adaugare Decizia 13, proiectata in J. Trei bucati: 1. **Butoanele de linie deasupra gridului**, cu eticheta, nu iconite mute: linie noua (`but_nou`), sterge linia (`but_sterge`), detalii linie. 2. **Un singur buton „Adauga articole” cu `xmenu()`**, cu optiunile din tabelul din J. Inlocuieste `But_urmator_tot1` (azi fara caption si fara `ToolTipText`) si butonul separat de alegere. **Acopera si contractele** — tipurile 2, 6, 26, 52 lipsesc azi din conditiile de vizibilitate (`ofacturare.vc2:15113-15245`), desi avizele sunt acolo. 3. **Alegerea selectiva**, dupa tiparul RORIS: dialog modal cu coloana de bifat si criterii de cautare, populare aditiva in cursorul local, nicio scriere in baza pana la salvare. Pe contract, unitatea de selectie e **rata** — cazul explicit cerut. Spre deosebire de modelul RORIS, la zero rezultate se spune de ce, nu se inchide in tacere. **PROIECTAT (runda 11) — `docs\cercetare\s4b_bara_butoane_meniu.md`.** Implementabila fara cod nou major, cu **patru corectii** fata de textul de mai sus: - **Golul de contract e mai mare decat „lipseste `but_urmator_tot1.Visible`".** Pe **tipul 52** formularul nu intra in **niciun** `Case` al `Do Case`-ului (`ofacturare.vc2:15109-15248`) — deci pierde si titlul, si eliminarea coloanei `cSerie`, nu doar butonul „tot". - **Nu se porneste de la tiparul RORIS.** `frm_tranzit` e specific ROAACNPRO si ar insemna formular nou; ROAFACTURARE are deja `cauta_alfa(..., tnTipReturn=1)` — mecanism **generic** de selectie multipla cu bifare, criterii de cautare si populare aditiva, folosit **chiar in acest formular** pentru returul multi-factura (`do_cauta_facturi`). Mai ieftin, si deja dovedit in productie. - **„Unitatea de selectie e rata" e adevarat doar pe jumatate.** `crsarticole1` are **doua ramuri disjuncte** in SQL — `OPT_FACTURARE = 3` (articole reale) si `OPT_FACTURARE IN (1,2)` (rate de scadentar) — iar un contract e mereu pe una singura, niciodata pe amandoua. Eticheta „Alege ratele de facturat…" e corecta doar pe a doua ramura; **contractul are deci doua meniuri, nu unul**. - **Un gol de cod, nu doar de vizibilitate:** `do_adauga_tot` parcurge azi **exclusiv `crsarticole`, niciodata `crsarticole1`**. Fara extindere, „adauga tot" pe contract fie n-ar face nimic, fie ar aduce liniile gresite — deci pe contract butonul trebuie **scris**, nu doar facut vizibil. **Echivalenta „tot" = „alege total"** tine azi prin constructie, pe o singura rutina comuna de adaugare pe rand (`do_adauga_articol`) — se pastreaza asa, iar pe contract devine adevarata abia dupa extinderea de mai sus. *Gata cand:* pe fiecare sursa (comanda, contract, avize, document returnat) meniul ofera si „tot" si „alege", iar rezultatul in `crsfactura` e identic pe cele doua cai cand selectia e totala — **inclusiv pe contract**, unde azi „tot" nu parcurge cursorul potrivit, si **inclusiv pe tipul 52**, care azi nu intra in nicio ramura. Pasii cu criterii verificabile sunt la sectiunea 7 a raportului; ce nu se poate testa headless, la sectiunea 8. *Depinde de:* S4. #### S4c — Discountul pe linie, mutat din dialog in grid Decizia 14, proiectata in K. Dialogul de articol dispare odata cu unificarea, deci cele doua campuri de discount ale lui — **procent** si **valoare unitara** — devin coloane in grid, cu acelasi calcul reciproc din `frm_articol_factura.do_calculeaza_discount` mutat pe evenimentele coloanelor. Se repara in acelasi timp inconsecventa de azi (read-only in lei, editabil fara recalcul in valuta). Se pastreaza excluderea pe `in_valuta`. Modelul de date **nu se atinge** — se stocheaza tot valoarea. **VERIFICAT — S4c e deblocata, dar capcana e alta decat se credea.** Raport: `docs\cercetare\discount_in_rapoarte_si_efactura.md`. - **Niciun raport de factura nu tipareste discountul pe linie** — nici valoare, nici procent. Cautare in toate `.fr2` de factura / proforma / invoice din `COMUN\Rapoarte`: **zero potriviri** pe „disc". Singurele doua `.fr2` cu „DISCOUNT" sunt de NIR, nu de facturi emise. Se tipareste `pretftva` si `valftva`, **deja nete de discount** (`prelucreaza_factura`, `ofacturare_comun.prg:1055-1059`, `:1156-1248`, in cursorul `crsfacttemp`). - **eFactura foloseste exact acelasi cursor** (`xmlefactura.prg`) — `LineExtensionAmount` si `PriceAmount` sunt aceleasi valori nete, cu optiunea (dezactivata implicit) de a adauga si un `cac:AllowanceCharge` informativ pe linie. - **SAF-T (D406) nu exista in ROAFACTURARE** — doar tabele de nomenclator cu prefix `saft_` (coduri TVA / plata), pe partea de achizitii, fara legatura cu `DISCOUNT_UNITAR`. - **Deci ingrijorarea initiala („utilizatorul schimba totalul fara sa se vada pe hartie") era gresit tintita:** discountul nu se vede pe hartie **prin design**, iar totalul se vede corect pentru ca pretul tiparit e deja net. **PROIECTAT (runda 12) — `docs\cercetare\s4c_discount_in_grid.md`.** Implementabila, cu **capcana retintita**: formularea de mai jos, din rundele anterioare, era in acelasi timp prea alarmista si prea vaga. - **Baza de date nu e in pericol.** `do_scrie_articole` trimite spre Oracle **direct** din `discountftva` / `discountctva` / `vdiscountftva` / `vdiscountctva` (`ofacturare.vc2:14081-14083`, ales pe `cu_tva` si `tip_valuta`) — verificat pe cod, nu presupus. Deci `VANZARI_DETALII.DISCOUNT_UNITAR` iese **mereu corect**, indiferent de starea campurilor agregate. - **Riscul e strict local, si e o inconsistenta, nu o valoare veche.** `valdiminuatftva` / `valdiminuatctva` sunt agregate citite de `prelucreaza_factura` **din acelasi `crsfactura` din memorie**, nereincarcat din Oracle. Netratate, factura tiparita poate iesi cu `pretftva` corect si `valftva` inconsistent — **pret x cantitate diferit de valoare**, ceea ce se vede pe hartie. - **Exista deja azi calea care demonstreaza gaura:** editarea `vdiscountftva` in grid, pe factura in valuta, **nu declanseaza recalculul** agregatelor (`discount_verificare2.md`, punctul 3). - **Calculul e deja generic si refolosibil**, nu trebuie rescris: `do_calculeaza_discount` (`ofacturare.vc2:1874-1976`) plus `calculeaza_totaluri()` (`oproceduri_facturare.prg:2258-2381`), care ruleaza pe `Scatter Name`, **fara dialog**. - **Evenimentul recomandat pe coloanele noi e `Text1.LostFocus`**, nu `InteractiveChange` sau `Valid` — tiparul e deja folosit in acelasi fisier, pe `frm_avizare_lucrare.grd_articole.cCantitate` / `cPret` (`:6549-6562`). - **Punctul deschis din S1 e inchis in trecere:** `_checkbox1` si `chkDetaliat` sunt **doua controale distincte** in `frm_alte_date`, nu o duplicare — subiectul nu are legatura cu S4c. *Gata cand:* tastarea in oricare din cele doua coloane produce aceleasi valori in `crsfactura` ca dialogul de azi, pe ambele monede; totalurile se refac imediat; discountul venit din politica de pret se comporta ca azi; **si `valdiminuatftva` / `valdiminuatctva` se recalculeaza la fiecare editare**, astfel incat pe factura tiparita `pret x cantitate` sa dea exact valoarea — verificat prin retiparire si prin XML-ul de eFactura, nu doar pe ecran. Pasii cu criterii verificabile sunt la sectiunea 7 a raportului; ce nu se poate testa headless, la sectiunea 9. *Depinde de:* S4. #### S4d — Data cursului valutar, numai cand are sens Decizia 15, proiectata in M. Campul apare cand tipul nu e retur **si** (documentul e in valuta **sau** a intrat pe grid macar un articol cu pret in valuta) — evaluat reactiv, ceea ce devine posibil abia in formularul unificat. **VERIFICAT — S4d e deblocata.** Raport: `docs\cercetare\zi_curs_validare.md`. Ascunderea selectorului **nu** poate lasa documentul fara curs, cu o conditie deja indeplinita de cod: - **Implicitul exista si e neconditionat.** `poDate.zi_curs` primeste data documentului chiar in `oDateFactura.Init` / `Reset` (`COMUN\programe\ofacturare_comun.prg:247`, `:496`), **inainte** ca formularul sa decida ce ascunde. **Nicaieri codul nu goleste `zi_curs` cand controlul e ascuns.** Deci „valoarea implicita ramane" nu e ceva de construit — e comportamentul actual, de **nestricat**. - **Precedentul cerut exista deja**, in alt formular: `frm_date_factura`, tipurile 8 / 9 (retur), unde `clb_zi_curs` se elimina **neconditionat** si documentul se salveaza corect — in principal pentru ca `cursor_retur` nici nu foloseste `poDate.zi_curs`. - **Linia `:8076` nu e in formularul de factura.** Apartine lui `frm_date_aviz_lucrare` (`inainte_de_do_termin`, `ofacturare.vc2:8054-8119`), un formular restrans pentru **aviz pe lucrare / aviz pe NIR** (tipurile 27 si 30), care nici nu are control de valuta. Valideaza `zi_curs` pentru ca tipul 27 are nevoie de curs pentru articolele din comanda, **independent de `poDate.in_valuta`** — deci nu e o inconsecventa de reparat orbeste, are un motiv. Se aliniaza doar daca formularul unificat preia si tipurile 27 / 30. **Riscul real e in alta parte, si nu tine de vizibilitatea campului:** `pack_facturare.verifica_cursuri_valute` (chemata din `cursor_preturi`) ruleaza **neconditionat de `in_valuta`** si exclude doar moneda nationala. Deci un `zi_curs` implicit (azi) fara curs setat in `CURS` pentru o valuta prezenta in listele de preturi ale utilizatorului **poate pica oricum** (`-20005`, „Nu este setat cursul…"), indiferent daca selectorul e vizibil sau nu. **Ascunderea nu introduce riscul asta si nici nu-l rezolva** — dar il face mai greu de inteles pentru operator, care nu mai vede campul din cauza caruia primeste eroarea. De tratat in mesajul de eroare, nu in vizibilitate. **PROIECTAT (runda 12) — `docs\cercetare\s4d_zi_curs_reactiv.md`.** Nu se inventeaza nimic; se reevalueaza vizibilitatea unui control care exista deja. - **Cheia reactivitatii e un camp deja prezent in cursorul gridului: `crsfactura.tip_valuta`**, interogabil cu **exact tiparul deja folosit in cod** (`ofacturare.prg:1656`, `:1730`: `Select Distinct ... From crsfactura Where tip_valuta = 1`). Nu e nevoie de o structura noua. - **Prototipul are deja `Clb_zi_curs` propriu, editabil, legat la `poDate.zi_curs`** (`ofacturare.vc2:16285-16305`) — se schimba doar **cand** e vizibil. - **Mesajul `-20005` contine DEJA data si numele valutei lipsa** (`STRINGAGG` peste toate valutele fara curs) — deci partea de „sa spuna care valuta si ce zi" nu e de construit. **Decizia 42 nu schimba continutul mesajului, ii schimba domeniul**: il restrange la valuta articolului cautat, dar **numai pe varianta filtrata** (S4 punctul 1); pe caile cu document sursa incarcarea in masa ramane neconditionata, deci acolo eroarea poate inca numi o valuta straina de documentul curent. - **Ce lipseste cu adevarat, si intra ca pas de implementare, nu ca optiune:** sectiunea M cere ca la aceasta eroare campul sa **revina vizibil** — altfel operatorul primeste o eroare despre un camp pe care nu-l vede. - **#16 nu e in perimetrul S4d.** E legat de S2 (bucla de reincercare din `ofacturare.prg`), cu verdict separat in `s3_portare_antet.md`. S4d doar **confirma** ca antetul persistent ii inlatura mecanismul, cu conditia ca S2/S3 sa nu recreeze formularul la eroare. - ~~**De confirmat cu Marius (mic, de UX):** ce se intampla cand se sterge **ultimul** articol in valuta?~~ **INCHIS — decizia 47 (Marius, runda 13): simetrie.** Campul **se ascunde la loc** cand dispare ultimul articol in valuta, pe tiparul `lb_cursuri`. „Clipitul" la adaugari/stergeri repetate e acceptat ca pret al unei reguli unice: aparitia si disparitia urmeaza aceeasi conditie, nu doua. *Gata cand:* o factura in lei fara articole in valuta nu arata campul si se emite corect; aceeasi factura, dupa adaugarea unui articol cu pret in valuta, arata campul cu data implicita completata; factura in valuta se comporta ca azi; iar la `-20005` **campul redevine vizibil**, cu mesajul de azi (care deja numeste valuta si ziua). Pasii cu criterii verificabile sunt la sectiunea 7 a raportului; ce nu se poate testa headless, la sectiunea 9. *Depinde de:* S4. **#16 se urmareste in S2/S3, nu aici** — vezi M. #### S4e — Lista de preturi disponibila si pe factura din comanda Decizia 16, proiectata in J. Singurul gol real: pe contract merge deja, pe comanda nu, pentru ca `cursor_comanda` umple `crsarticole` doar cu articolele comenzii. Se adauga lista de preturi peste cursorul sursei, exact ca la copiere (`ofacturare.prg:454-473`), si optiunea „Cauta in lista de preturi…” intra in meniu pe toate sursele. **Si stergerea intra aici**, nu doar adaugarea: cazul cerut e „clientul mai vrea ceva sau vrea sa schimbe”, deci o linie adaugata trebuie sa poata fi si scoasa. **DECIS (decizia 29): stergerea unei linii venite din comanda ramane fara protectie.** Se sterge ca oricare alta; comanda ramane cu cantitatea nefacturata si va aparea **facturata partial**, ceea ce e si starea corecta. Nu se cere confirmare, nu se marcheaza „refuzat", nu se ajusteaza numararea acoperirii. Ramane un singur efect lateral de tratat, si e pe **adaugare**, nu pe stergere: capul de coloana „Cantitate comandata” si mesajul „A fost facturata intreaga cantitate comandata” (`ofacturare.vc2:15144-15150`) nu sunt adevarate pentru liniile libere adaugate langa cele din comanda. **PROIECTAT (runda 12) — `docs\cercetare\s4e_lista_preturi_pe_sursa.md`. Reteta din decizia 16 NU se generalizeaza literal** — si asta e rezultatul principal al proiectarii. - **`APPEND FROM` peste cursorul sursei ar strica doua lucruri pe comanda**, ambele tacut: **(1)** `id_c` e `ROWNUM` per executie Oracle, deci randurile din lista de preturi ar **coliziona** cu cele ale comenzii, iar `do_sterge` ajusteaza cantitatea `For id_c = poArticol.id_c` (`:14652-14655`) — ar atinge randul gresit; **(2)** `do_scrie_factura` face `Sum(cantitate)` peste `crsarticole` pe exact aceste tipuri (`:14332-14338`), iar in `cursor_preturi` **`cantitate` inseamna STOC**, nu „ramas de facturat" — suma care decide inchiderea comenzii ar fi poluata. - **De ce merge totusi pe contract azi**, verificat: lista de preturi si articolele contractului sunt **doua cursoare separate** de la bun inceput (`crsarticole` / `crsarticole1`), iar `cursor_contract` emite `id_c` ca **`rownum - 10000`** (`PACK_FACTURARE:2722` — confirmat direct pe export de sesiunea principala), adica autorii au tratat coliziunea de `id_c` ca risc real. In plus, tipurile de contract **nu au deloc** ramura cu `Sum(cantitate)` in `do_scrie_factura` — cad pe `Otherwise`. Nu e un tipar de copiat, e o coincidenta favorabila. - **Solutia curata, verificata fezabila pe cod: liniile libere NU intra deloc in `crsarticole`.** Se adauga direct in `crsfactura` prin `APPEND BLANK` + `combosql` — tipar **deja existent** in `frm_facturare_articole2.do_adauga` si pe linia de discount (`ofacturare.vc2:14531`). Atunci `id_c` ramane `0` implicit, si **`do_sterge` devine no-op prin constructie**, fara nicio modificare de cod. - **Se aplica identic pe AVIZE (tip 4), nu doar pe comanda** — `cursor_avize` are aceeasi semantica „ramas de facturat" si aceeasi ramura de `Sum`. Titlul povestii spune „din comanda", perimetrul real e „orice sursa cu registru". - **Avertisment care traverseaza in S4f:** pe returul ca document (`8,9,24`) riscul de coliziune `id_c` **ramane** (`do_sterge` ajusteaza cu semn opus, urmarind maximul returnabil), desi **fara** poluarea sumei de inchidere. Deci S4f foloseste acelasi mecanism, nu `APPEND FROM`. - **Validarea de cantitate pe liniile din comanda e satisfacuta prin constructie** — drumul `do_adauga_articol` → `do_verifica_articol` nu e atins, fiindca liniile libere nu modifica `crsarticole`. - **Gol real ramas, neacoperit de S4 si S4b:** nu exista mecanism de validare a cantitatii/stocului pentru un rand ales prin `combosql` — ambele rapoarte se opresc la maparea campurilor. `do_verifica_articol` nu se poate refolosi ca atare (cere un `poArticol` scatter-uit dintr-un cursor sursa incarcat). **DECIS — decizia 44 (Marius, runda 13): `poArticol` devine parametru explicit.** Se pastreaza un **singur loc de validare**: `do_verifica_articol` primeste `poArticol` ca **parametru**, in loc sa citeasca variabila `Private` populata de apelant. Schimbarea de contract a metodei e **aprobata ca atare**; consecinta obligatorie e actualizarea **tuturor apelantilor existenti**, inventariati inainte de prima editare. Aceeasi decizie acopera si `do_alege_stoc` / `frm_articol_gest_factura` din S4f (R7) — o singura solutie pentru amandoua, cum cerea raportul. *Gata cand:* pe o factura la comanda **si pe una din avize** se poate adauga un articol care nu e in sursa, cu pretul din lista de preturi, si se poate sterge o linie adaugata, fara ca liniile din sursa sa-si piarda validarea de cantitate **si fara ca suma care decide inchiderea automata sa se schimbe** — proba directa: aceeasi comanda, cu si fara linii libere adaugate, produce acelasi rezultat de inchidere. *Depinde de:* S2. **Interactioneaza cu S4 punctul 2** (registrul) si **avertizeaza S4f**. #### S4f — Returul in formularul unificat Decizia 17, proiectata in N. **Partea grea nu e ce credeam.** Factura de retur ca document (tipurile 8, 9, 24) functioneaza deja cap-coada: alegere multipla a facturilor sursa, populare din `cursor_retur`, gestiune si pret de achizitie mostenite din liniile originale, stergere si retur partial. Munca e: 1. **mutarea lui N.1 ca sursa in meniul de adaugare**, fara sa se atinga popularea — acelasi dialog, aceleasi filtre, acelasi `cursor_retur`; 2. **ridicarea lui N.2 (`But_retur`) la nivel de document**, dupa modelul lui N.1, cu calea per-articol pastrata; 3. **lista de preturi pe documentele de retur**, prin acelasi `APPEND FROM` ca la S4e. Nu se ating: excluderea returului dintr-un retur, filtrarea pe client si valuta, maximul returnabil calculat pe server, mostenirea gestiunii si a pretului de achizitie. **DECIS (decizia 22):** linia de retur care nu vine din nicio factura originala **e permisa**, ca pe orice alt document. Deci punctul 3 nu mai are conditie de intrare. Ce trebuie proiectat in schimb, pe liniile libere: gestiunea si pretul de achizitie **se aleg** (ca la N.2), nu se mostenesc, iar maximul returnabil calculat pe server **nu se aplica** — nu exista cantitate originala. **Afisarea provenientei — VERIFICAT, si raspunsul e „nu la nivel de linie".** Raport: `docs\cercetare\legatura_linie_retur.md`. Deci **S4f se livreaza fara coloana de provenienta pe linie**; nu se inventeaza. Ce s-a stabilit, ca sa nu se reia: - **Legatura se pierde chiar in cursorul care aduce datele.** `cursor_retur_document` (`ff_...:3949-4062`) foloseste `V_LISTAID` **doar ca filtru** (`WHERE A1.ID_VANZARE IN (...)`, `:4054-4055`); in lista de coloane a `SELECT`-ului extern (`:3965-4028`) **nu apar nici `ID_VANZARE`, nici `ID_VANZARE_DET`**. `crsarticole` nu are de unde sti din ce factura vine randul. - **`INSERT`-ul in `VANZARI_DETALII` nu are nicio coloana de sursa.** - **Exista insa o legatura la nivel de DOCUMENT, si nu era cunoscuta in plan: `VANZARI_CORESP`.** `scrie_corespondente_vanzari(3)` (`ff_...:14834-14836`, din `finalizeaza_factura`) scrie `(ID_VANZARE_FACT = documentul de retur, ID_VANZARE_AVIZ = fiecare factura sursa, TIP = 3)` (`:15481-15516`) — **cate un rand per factura sursa aleasa**, nu per linie. Numele coloanei e generic, reutilizat si pentru perechi aviz-factura (`TIP = 1/2`). - **Consecinta pentru UI:** cand s-a ales **o singura** factura sursa, se poate afisa corect „documentul asta de retur provine din factura Y" — la nivel de antet, nu de linie. Cand s-au ales **mai multe** (selectie multipla, suportata explicit de dialog), `VANZARI_CORESP` da **multimea** de facturi posibile, deci nici macar antetul nu poate arata o sursa unica. **De asezat in UI ca informatie de document, niciodata ca proprietate de linie.** - **N.2 (`But_retur`) nu scrie nicio legatura.** `listaid` (perechi `ID_ARTICOL:ID_VANZARE`) e folosit in `pack_facturare` (`ff_...:8142-8212`) exclusiv ca filtru pe `RUL`, ca sa calculeze cantitatea inca disponibila din acea vanzare. Sursa nu ramane atribut al liniei noi. **PROIECTAT (runda 12) — `docs\cercetare\s4f_retur_formular_unificat.md`**, cu **trei corectii** fata de textul de mai sus: - **Punctul 1 se restrange.** Alegerea facturilor sursa **nu se muta** in bara de butoane — ramane la antet / in sectiunea pliata (confirmat impotriva `s4b_bara_butoane_meniu.md` §9.3 si a tabelului de meniu de acolo). Ce se muta in meniul de adaugare e doar **„adauga tot din facturile alese"** si **„alege liniile"**. - **Un risc concret, netratat nicaieri pana acum (R1):** `frm_articol_gest_factura` (`ofacturare.vc2:4094-4103`) face, sub `If Thisform.lRetur`, `poArticol.pretftva = pretv` — adica **suprascrie pretul de vanzare cu pretul din stoc pe ORICE linie de retur** (verificat direct de sesiunea principala). Corect pentru liniile **mostenite** dintr-o factura sursa; **gresit pentru liniile libere** permise de decizia 22, care trebuie sa pastreze pretul din lista de preturi. Raportul propune fixul. - **Distinctia „linie libera" vs. „linie mostenita" nu cere camp nou: `crsfactura.id_c = 0`.** Oracle nu produce niciodata `id_c = 0` din `ROWNUM`, deci valoarea implicita e ea insasi semnalul. Vine din mecanismul impus de S4e: liniile libere intra direct in `crsfactura` (`APPEND BLANK` + `combosql`), **nu** in `crsarticole` — asa ca ajustarea din `do_sterge` (care pe retur urmareste maximul returnabil) devine **no-op prin constructie**. Acelasi semnal dezactiveaza si suprascrierea de pret de mai sus. - **Gol nou (R7), pe care S4e nu-l are** (comanda si avizele n-au dialog de gestiune): azi se intra in `do_alege_stoc` / `frm_articol_gest_factura` **doar** dintr-un `poArticol` scatter-uit din `crsarticole`. O linie libera din `crsfactura` nu are asa ceva, iar decizia 22 cere ca gestiunea **sa se aleaga**. Deci trebuie un **punct de intrare nou** in dialog. **Se unifica cu golul echivalent din S4e** (validarea cantitatii, `do_verifica_articol`): e aceeasi problema structurala — dialogurile sunt cuplate de `crsarticole` —, deci merita **o singura solutie**, nu doua. **INCHIS prin decizia 44** (runda 13): `poArticol` devine **parametru explicit** si aici, nu variabila `Private` populata de apelant — aceeasi solutie ca in S4e, cum cerea raportul. - **R4 e inchis** (verificare independenta): `scrie_corespondente_vanzari(3)` e gatata pe `ntip IN (8,9)`, nu pe `listaid`. - **`VANZARI_CORESP` nu e afectata de liniile libere** — se scrie din `poDate.listaid`, fixat la antet. **De confirmat (R4):** pentru `But_retur` ridicat la nivel de document, raspunsul pare a fi „fara corespondenta persistata", pentru ca scrierea e gatata pe `ntip IN (8,9)`, nu pe `listaid`. *Gata cand:* un document de retur deschis in formularul unificat aduce liniile facturilor alese cu aceleasi valori ca azi — inclusiv gestiunea si pretul de achizitie —, permite stergere si cantitate partiala, iar pe o factura normala se poate face retur alegand facturile o singura data. **In plus: o linie libera pe un document de retur isi pastreaza pretul din lista de preturi**, adica nu trece prin suprascrierea de la `:4094-4103`. Pasii cu criterii verificabile sunt la sectiunea 9 a raportului; ce nu se poate testa headless, la sectiunea 11; riscurile R1-R6, la sectiunea 12. *Depinde de:* S2, S4e. #### S4g — Adaugarea de articole la modificarea oricarui document, inclusiv auto Decizia 18, proiectata in O si O-bis. **Nu se porneste de la zero:** „Alte servicii” din ROAAUTO face deja jumatate — articole reale langa linii sintetice, cu pret tastat, fara gestiune — doar ca traieste in formularul de emitere si moare odata cu el. Se generalizeaza in formularul unificat, ca a doua sursa din meniu („Alege din nomenclator…”), disponibila **si la modificare**, pe orice tip de document, nu doar pe cele auto. **Ordinea in care se lucreaza**, ca sa nu se blocheze tot: intai pe documentele ROAFACTURARE, unde scrierea e a noastra; abia apoi pe `tip = -12`. **Decizia 20 se aplica aici:** din nomenclator se ofera **si articole gestionabile, si negestionabile** — nu se copiaza filtrul `in_stoc = 0` al lui ROAAUTO. **Blocantul real e contul de venit, si el priveste doar ramura „Alege din nomenclator…”** (J-bis, J-ter). Contul de venit vine din `NOTE_CONTABILE.SCC` **prin politica de pret**; un articol fara `ID_POL` nu ajunge la cont gol, ci **la eroare** — `contabilizeaza_articol` ridica `FACT-024` si opreste tranzactia. Pe ramura „Cauta in lista de preturi…” nu se schimba nimic: acolo politica exista. **Premisa de la care pornea povestea asta a cazut:** „Alte servicii” din ROAAUTO **nu face deja jumatate** din treaba — acele linii ocolesc complet `contabilizeaza_articol`, printr-o cale de facturare paralela care nu genereaza nota de venit. Nu e un mecanism de generalizat, e o exceptie. **DECIS (decizia 27, care inlocuieste 24):** contul de venit se **deriva**, nu se ia prin politica — din `CORESP_CONT_VENCHELT` pentru articolele gestionabile (pe contul de gestiune al liniei), din `NOM_ARTICOLE.CONT` daca e 6xx / 7xx pentru cele negestionabile, altfel **704**. **Derivarea se face in VFP**, dar **transportul s-a schimbat la decizia 34**: contul calculat se trimite **direct, ca parametru nou al lui `contabilizeaza_articol`**. Ocolul prin `id_pol` cu nota potrivita — politica tehnica, interogarea inversa pe `SCC`, `pack_preturi.adauga_politica_pret_art` — e **abandonat**; J-quater punctul 3 se citeste doar ca trasabilitate. **Decizia 35** adauga ca acelasi drum serveste si editarea prin regenerare, deci nu se proiecteaza aici o a doua ruta de contare. ~~**De decis in aceasta poveste, nu inainte:** ce se intampla cand linia are **si** politica, **si** cont trimis din VFP — cine castiga.~~ **PROIECTAT (runda 13) — `docs\cercetare\s4g_adaugare_articole_modificare.md`.** Intrebarile despre politica tehnica in ecranul de cautare a politicilor **au disparut** odata cu reteta. **Raspunsul: niciuna dintre cele trei variante pure — parametrul castiga, dar combinatia ambigua e oprita explicit, nu rezolvata tacit.** - **Combinatia nu poate aparea in fluxul normal, si asta e dovedit, nu presupus.** `crsfactura.id_pol` e `N(20) Null` (`COMUN\programe\ofacturare_comun.prg`, `creeaza_facturacrs` — **verificat direct**), deci la `APPEND BLANK` ramane `.NULL.`, si ajunge la Oracle ca literalul `NULL` (`ofacturare.vc2:14072`). Cursorul de cautare din nomenclator **n-are deloc coloana `id_pol`**, deci nu exista punct in care o linie „din nomenclator" sa-l poata popula. - **Singurul scenariu real de ambiguitate e regenerarea** (decizia 35): daca derivarea contului ar rula **necondiționat** pe toate liniile documentului, si nu doar pe cele fara `id_pol`, o linie cu politica reala ar primi si cont derivat. **E un risc de implementare VFP, nu Oracle** — dar proiectarea Oracle nu trebuie sa-l faca invizibil. - **Deci: `cont_venit IS NOT NULL` intra pe ramura noua; daca in acel moment `id_pol` e si el populat, se ridica eroare** (cod nou, distinct de `FACT-024`, care ramane pentru cazul „nici politica, nici cont"). Variantele „politica castiga" si „fallback la `NO_DATA_FOUND`" au fost respinse motivat: prima defineste castigatorul pe **prezenta** campului, nu pe rezolvarea lui, si ar reintroduce `FACT-024` acolo unde VFP a oferit deja o solutie; a doua e semantic cea mai curata, dar cere **restructurarea interna** a functiei (un flag propagat din `EXCEPTION` pana la punctul de decizie), fata de un singur `IF` la intrarea in ramura deja proiectata. **Ambele ascund o eroare de date in loc s-o semnaleze.** - **Regresie zero pe apelantii de azi**, prin constructie: cand `cont_venit` e `NULL` — adica toti apelantii existenti, care nu cunosc parametrul —, executia intra direct pe `ELSE`, garda nu se evalueaza niciodata, comportamentul e identic cu cel de azi. - **De ales de Marius:** numarul concret al codului de eroare nou (`FACT-0xx`). Separat, contul de **gestiune** e rezolvat ca regula (decizia 21): fallback, nu `NULL`, nu refuz. **`pack_auto` nu mai e blocant** — `PACK_AUTO` nu citeste `VANZARI` / `VANZARI_DETALII` deloc (O, intrebarea 1). Desincronizarea de afisare e **acceptata** (decizia 28); conditia e MANOPERA si MATERIALE conform devizului. **Blocantul netehnic a cazut si el:** gridul read-only din `frm_modific2024` e al lui #6, dar #13 incepe dupa ce #6 se termina (decizia 30). **Restul proiectarii S4g, pe scurt** (detaliile in raport): - **Suprafata pe Oracle**: `contabilizeaza_articol` primeste contul prin `VANZARI_DETALII_TEMP%ROWTYPE` (`cont_venit`), deci semnatura functiei ramane aceeasi — se extinde **tipul de rand**. Consecinta de livrare: **DB inainte de EXE**. - **Derivarea in VFP** urmeaza fix decizia 27: `CORESP_CONT_VENCHELT` pentru gestionabile (pe contul de gestiune al liniei), `NOM_ARTICOLE.CONT` daca e 6xx/7xx pentru negestionabile, altfel **704**. Ruleaza **doar pe liniile fara `id_pol`** — vezi garda de mai sus; aici se leaga cele doua. - **Fluxul in formular** refoloseste exact mecanismul lui S4e: linia „din nomenclator" intra prin `APPEND BLANK` + `combosql` **direct in `crsfactura`**, niciodata in `crsarticole`. - **Ramane deschis, mostenit din S4e:** validarea de cantitate/stoc pentru linia libera — se rezolva prin decizia 44 (`poArticol` ca parametru explicit), aceeasi solutie pe ambele surse. - **De re-rulat inainte de implementare, nu de presupus incheiat:** cautarea apelantilor lui `adauga_articol_factura` in restul suitei (ROAGEST, ROAAUTO, ROAACNPRO, ROAIMOB). Risc asteptat zero — folosesc proceduri separate sau alt pachet —, dar cautarea **nu s-a terminat in nicio runda**. - **Trei hardcodari raman de confirmat de Marius, nu de presupus inofensive:** `CU_TVA = 1` (are efect masurat prin `nproc_tva_max`, pe linie scutita + discount global), numele cheii de optiune de firma pentru `SCD` (decizia 36) si domeniul ei `PROGRAME`, si trasabilitatea `CONT_VENIT` pe `VANZARI_DETALII` (optionala pentru functionare, dar fara ea coloana nu se pastreaza dupa fapt). **Nota de executie (decizia 60):** pe documentele de tip **48/49** (custodie), „Alege din nomenclator…" ramane restrans la articole `IN_STOC = 0`, la fel ca ramura „Cauta in lista de preturi…" (S4) — decizia 20 (si articole gestionabile, si negestionabile) **nu se aplica** pe tipurile 48/49. Altfel se rupe invariantul pe care se sprijina siguranta regenerarii la editarea custodiei (decizia 60, `docs\cercetare\custodie_48_49_stergere_reemitere.md`). *Gata cand:* pe un document deja emis se poate adauga o linie noua, din lista de preturi sau din nomenclator, si documentul se salveaza consistent — pe fluxurile ROAFACTURARE, cu partea auto livrata separat, dupa (1). Pe un document 48/49, „Alege din nomenclator…" nu ofera si nu permite adaugarea unui articol cu `IN_STOC <> 0`. *Depinde de:* S4e, si de deciziile de mai sus. **Nu blocheaza etapa I.** #### S5 — Acoperirea tuturor tipurilor `frm_facturare_articole.Init` are 20 de ramuri pe `poDate.tip` (`:15107-15250`) care schimba titlul, capul coloanei de cantitate, mesajul de stoc, vizibilitatea discountului, prezenta seriei. Toate trebuie sa existe si in formularul unificat. Tipurile speciale raman pe calea lor: `tip = 27` (`frm_avizare_lucrare`), `tip = 30` (aviz din NIR, formular nevizibil). **PROIECTAT (runda 12) — `docs\cercetare\s5_acoperire_tipuri.md`. Povestea e mult mai mare decat „porteaza cele 20 de ramuri", si motivul e ca baza de pornire aleasa nu are aproape nimic de portat.** - **`Do Case`-ul de azi acopera 21 de valori de `tip`** (in 14 ramuri). **Patru tipuri reale, reachable prin `factureaza()`, nu intra in niciun `Case`: `45` (factura restaurant), `48` / `49` (custodie), `52` (contract, factura fiscala valuta)** — pierd titlu, cap de coloana, mesaj de stoc, vizibilitatea discountului si eliminarea coloanei `cSerie`. **Nu doar `52`**, cum semnalase S4b. Rutarea cursorului le recunoaste (`ofacturare.prg:271-282`); doar `Init` nu le-a „prins" niciodata. **Nota de executie (decizia 60):** randul de configurare pentru `48`/`49` trebuie sa pastreze restrictia la articole `IN_STOC = 0` mostenita de la sursa lor de azi (`cursor_articole_k`, `PACK:3595-3701`, `:3695`) — vezi notele echivalente la S4 si S4g; e conditia de siguranta pentru regenerarea la editarea custodiei (decizia 60, `docs\cercetare\custodie_48_49_stergere_reemitere.md`). - **Cinci tipuri (`43,44,46,50,51`) nu ajung deloc la acest formular** — zero potriviri in tot arborele `D:\ROA`. `50` e marcat „in lucru" in sursa. Se declara explicit ramase in afara. - **Descoperirea care schimba estimarea: prototipul nu e o versiune partiala a `Do Case`-ului — e aproape gol.** `frm_facturare_articole2.Init` (`:18988-19080`) alege pe tip **doar** cuvantul „factura"/„aviz", pe o lista mai scurta (lipseste `24`). Nu seteaza titlu (nu exista `lb_titlu_alb_b121` in tot prototipul), nu schimba capul coloanei, nu schimba mesajul de stoc, nu ascunde discountul, si **n-are deloc conceptul de coloana `cSerie`**. Daca formularul unificat porneste de la prototip — cum decide S1 —, **toata diferentierea pe tip se reconstruieste de la zero**, nu se completeaza. Asta e cel mai mare cost ascuns al etapei I descoperit pana acum. - **Rutarea cursorului diverge pe TREI tipuri, nu unul:** pe prototip, ramura de contract e `Inlist(tnTip, 2, 26, 6)` (`ofacturare.prg:762`) fata de `Inlist(tnTip, 2, 26, 6, 52)` pe standard (`:283`) — **verificat direct de sesiunea principala** —, iar ramura de retur omite `24`. Pe aceste tipuri, prin prototip, `lcSqlCursor` ar ramane **nedefinit**: eroare, nu doar comportament diferit. - **Punctul lasat deschis de S4 punctul 2 se inchide aici:** tipurile **`26` si `52` n-au niciun bookkeeping** `crsarticole` — nici Rol A, nici Rol B. Excluderea e totala, pe `Do Case` exhaustiv fara ramura implicita, deci e „dovedit absent", nu „neconfirmat". - **`30` nu e un formular separat**, cum spunea planul: e **acelasi `frm_facturare_articole`**, trecut prin acelasi `Init`, dar niciodata aratat (`ofacturare.prg:444-453` — calculeaza totalurile, apasa programatic `but_termin1.Click()`, apoi `Release()`, fara `Show()`). Deci **e afectat de golurile din `Do Case`** ca oricare alt tip; doar ca defectele nu se vad pe ecran. `27` chiar ramane pe calea lui. - **Alegerea de proiectare centrala, recomandata: tabel de configurare per tip**, un rand per `tip`, cu exact proprietatile pe care le seteaza azi `Do Case`-ul (titlu, cap cantitate, mesaj stoc, discount vizibil, are serie, tip doc, butoane, grup-sursa pentru meniul S4b). Motivul nu e estetic: **un `Do Case` fara `Otherwise` nu semnaleaza niciodata un tip lipsa** — exact mecanismul care a lasat patru tipuri pierdute ani la rand. Cu tabel, un tip necunoscut devine **eroare la deschidere**, iar completitudinea se verifica **mecanic**, cu un `SELECT` fata de `tipuri_documente_facturare.md`, fara sa porneasca formularul. Variantele „completeaza `Do Case`-ul" si „metoda per grup" au fost respinse motivat: amandoua raman implicite, deci nu adauga detectie. - **DECIS — decizia 48 (Marius, runda 13): tabelul e un CURSOR GENERAT IN COD la pornire**, nu `DBF` static (recomandarea raportului) si nu tabela pe Oracle. Ambele au fost puse pe masa cu argumentele lor si respinse: `DBF`-ul adauga un fisier de intretinut, de livrat la fiecare update si de tinut sincron intre produsele ROA; tabela Oracle ar cere script de migrare si o citire in plus la deschiderea formularului. **Ce NU se pierde prin alegerea asta:** detectia ramane intacta — tip necunoscut = **eroare la deschidere**, exact castigul pentru care exista propunerea —, iar proba mecanica de completitudine ramane posibila, doar ca ruleaza **din aplicatie**, nu cu un `SELECT` din afara. **Ce se accepta:** orice tip nou de document cere **recompilare si versiune noua de exe**, ca azi. Forma concreta: un `.prg` cu `CREATE CURSOR` + `INSERT`-uri, un rand per tip, incarcat o singura data si citit de `Init` prin `SEEK` pe `tip`. *Gata cand:* fiecare tip din `COMUN\docs\tipuri_documente_facturare.md` fie e acoperit, fie e declarat explicit ramas pe calea veche — **verificat prin proba mecanica de la sectiunea 8 a raportului**, nu prin citire. Include cele patru tipuri lipsa azi (`45,48,49,52`) si alinierea rutarii de cursor intre standard si prototip. Pe `48`/`49`, in plus: restrictia `IN_STOC = 0` mostenita ramane activa (decizia 60) — nu se poate adauga un articol gestionabil. *Depinde de:* S4. #### S5b — Proforma si copierea pe formularul unificat Deciziile 10 si 11. Amandoua vin **aproape gratuit**, pentru ca folosesc deja acest drum: proforma e o valoare in combo-ul de tip document, copierea e `factureaza(tip, toFactura)` cu antetul precompletat. De facut, concret: - combo-ul `Ct_clb_fdoc` ramane in antetul unificat, cu realocarea de serie si numar la comutare; - garzile `eProforma = 0` de pe notele contabile si atasamente raman intacte, iar raportul propriu de proforma continua sa fie ales; - copierea deschide formularul in starea „document nou”, cu antetul editabil (comportamentul de azi); - **degradarea de tip din `do_copiaza` ramane pentru copiere si nu se aplica la regenerare** (D) — sunt doua moduri distincte ale aceluiasi formular, nu acelasi comportament. *Gata cand:* o proforma emisa din formularul unificat nu produce nota contabila si se listeaza pe raportul ei; o copie produce un document nou cu numar nou si acelasi continut. **VERIFICAT (runda 9) — `docs\cercetare\s5b_proforma_descarcare_gestiune.md`. Verificarea 4 e inchisa.** **NU, gestiunea nu se descarca pe proforma** — pentru niciun articol, indiferent daca e gestionabil in nomenclator. Si nu e un efect colateral al ascunderii stocului, e **prin design**: VFP marcheaza toate liniile proformei negestionabile inainte de compunerea documentului, ceea ce le trimite cu sentinela `id_gestiune = -1000`, iar pe Oracle `contabilizeaza_articol` sare apelul catre `descarca_gestiune` exact pe acest sentinel. Intentia e explicita in changelog (12.03.2021 / 2.7.x): *„Articolele din proforma sunt marcate 'negestionabile', astfel incat nu mai este necesara existenta articolelor in stoc pentru a genera proforma."* — deci cerinta a fost „proforma trebuie sa mearga si fara stoc", nu „nu arata plafonul". **Se corecteaza presupunerea din plan** ca zeroizarea `gestionabil` ar fi doar cosmetica. Consecinta pentru S5b: sentinela `-1000` e contractul care trebuie pastrat — vezi sectiunile 6 si 7 ale raportului pentru constrangerile de proiectare si ce lipseste la copiere. **PROIECTAT (runda 12) — `docs\cercetare\s5b_proiectare_proforma_copiere.md`. „Aproape gratuit" nu mai tine: proiectarea a scos la iveala un risc de date care nu era semnalat nicaieri.** - **Mecanismul real care tine proforma curata nu e sentinela, ci routing-ul.** In `do_scrie_factura` (`ofacturare.vc2:14282-14300`), `eProforma = 1` alege `scrie_proforma`, care **nu cheama niciodata** `contabilizeaza_articol` — comentariul din cod o spune direct: *„salveaza doar in vanzari, nu si in contabilitate"* (verificat de sesiunea principala). Sentinela `-1000` e al doilea strat, nu primul. - **RISCUL CENTRAL — „drumul invers", nesemnalat pana acum si cu consecinta pe date.** Operatorul adauga linii cu documentul pe **Proforma** (deci marcate `gestionabil = 0` / `id_gestiune = -1000`), apoi comuta combo-ul inapoi pe **Factura** inainte de „Termina". Atunci routing-ul alege `scrie_factura2`, care **chiar** cheama `contabilizeaza_articol` — dar acesta sare `descarca_gestiune` exact pe sentinela `-1000` (`PACK:7472-7476`). Rezultatul: **o factura reala iese cu stocul nedescarcat, silentios** — `-1000` e o valoare valida, nu ridica nicio exceptie. **Azi nu exista nicio plasa pentru acest caz**, si nici nu putea exista: combo-ul traieste in dialogul separat `frm_date_factura`, inchis **inainte** sa existe vreo linie. Riscul se naste **din unificare**. - **De aici si de ce marcarea negestionabila nu mai poate fi un singur `UPDATE` de masa:** in formularul unificat trebuie extrasa intr-o metoda refolosita din **trei** puncte — la incarcare, la adaugarea unei linii, si la comutarea tipului cu linii deja prezente. - **`id_c` la copiere: SIGUR, cu dovada.** `do_copiaza` degradeaza tipul spre grupul-tinta **`{1,5,7,10,22,23}`**, care nu intra niciodata in ramurile Rol A din `do_scrie_factura` / `do_sterge`. Coliziunea tehnica exista in date, dar **n-are efect observabil**. (Intrebarea venea din corectia lui S4e — vezi acolo.) > **CORECTIE (runda 13, verificata direct pe cod).** Grupul-tinta scris pana acum in plan > (`{1,5,10,22}`) era **gresit**, si la fel era si `{1,5,7,10}` din raportul S5c. Setul real e cel de > mai sus, citit din primul `CASE` al lui `frm_facturi.do_copiaza` > (`COMUN\clase\ofacturare_comun.vc2:3693-3694`), care lasa neatinse exact `T1,T5,T7,T10,T22,T23`. > Concluzia „copierea e sigura" **nu se schimba** — `7` si `23` nu au bookkeeping Rol A —, dar cifrele > se corecteaza peste tot unde apar. Aceeasi corectie se aplica lui > `s5b_proiectare_proforma_copiere.md` §2.1 / §7. - **DEFECT PREEXISTENT, gasit in trecere la verificarea de mai sus — de raportat, NU de reparat acum.** In `frm_facturi.do_copiaza`, ramura de avize scrie `lnTip = T22` (`COMUN\clase\ofacturare_comun.vc2:3703`) in loc de `loFactura.tip = T22` — **singura ramura din tot `Do Case`-ul care nu atribuie in obiect**; toate celelalte cinci scriu `loFactura.tip`. Consecinta: la copierea unui aviz (`21,24,26,30,-7,-9,-10,-13,28,29,42`) **degradarea nu se produce**, iar `copiere_factura` cheama `factureaza(toFactura.Tip, ...)` (`COMUN\programe\oproceduri_facturare.prg:150-152`) cu tipul **original** — deci copia unui „aviz pe baza de comanda" reintra pe ruta de comanda, nu pe cea de lista de preturi. **Verificat ca `factureaza` nu citeste un `lnTip` privat** (`ofacturare.prg:101` declara `lnTipTemp`, nu `lnTip`), deci atribuirea chiar se pierde; in plus `lnTip` e nedeclarat in metoda, deci poate suprascrie un `lnTip` al apelantului. **Fisierul e in perimetrul interzis (#6) — se raporteaza, nu se atinge.** **VERIFICAT (runda 12, la cererea lui Marius) — `docs\cercetare\verif_proforma_alegere_stoc.md`. Pe proforma NU se alege stoc azi, si asta e deliberat.** - **Un singur mecanism activ, si e in VFP, la incarcare:** `ofacturare.prg:333-336` face `UPDATE (lcCursor) SET gestionabil = 0` cand `eProforma = 1`, **inainte** sa se deschida gridul. Comentariul din cod spune intentia direct: *„Daca este o proforma, consider toate articolele negestionabile, **pentru a nu mai alege din stoc**"* (12.03.2021). Deci `Do Case`-ul de la `ofacturare.vc2:13803` vede mereu `gestionabil = 0` → **`do_alege_stoc` nu ruleaza niciodata**. Valabil pe **toate** cursoarele de creare directa a unei proforme; **niciunul** n-are parametru `V_PROFORMA` (verificat pe ~9 proceduri `cursor_` din pachet). - **Mecanismul Oracle exista, dar e mort in fluxul curent:** `cursor_retur_document` (`PACK:3993-4000`) are `CASE V_PROFORMA = 1 THEN 0`, insa se cheama doar la **copiere**, unde `V_PROFORMA` trimis e `eProforma` al documentului **nou** — mereu `0`. **Nu e o contradictie intre rapoarte**: sunt doua mecanisme reale, doar unul activ. - **`pret_achizitie` depinde de sursa:** pe calea principala (lista de preturi) ramane `0` — `cursor_preturi` nici nu-l selecteaza. Pe surse care carata un document existent (avize, copiere) vine real din `VANZARI_DETALII.PRET_ACHIZITIE`. - **Daca liniile ar pastra gestiunea reala pana la salvare, nu s-ar strica nimic pe contabilizare sau stoc:** `adauga_articol_factura` se cheama oricum si pentru proforma, dar `scrie_proforma` **nu** cheama `contabilizeaza_articol`, deci `descarca_gestiune` tot n-ar rula; iar **`do_alege_stoc` nu rezerva stoc** (doar `SELECT` + scadere locala in memorie, zero scriere Oracle). Singurul loc unde s-ar vedea o diferenta e la **relistare** (`crsDetaliiListare` / `fact_vfacturi2` citesc `id_gestiune` fara filtru pe `eproforma`) — **neconfirmat** daca vreun raport chiar il tipareste. **Consecinta care schimba forma deciziei: cele doua sensuri nu sunt simetrice.** - **FACTURA → PROFORMA cu linii deja adaugate:** liniile au trecut deja prin `do_alege_stoc`, deci au gestiune reala si pret de achizitie corect. **E sigur, si sentinela se poate aplica abia la salvare** — exact varianta ceruta de Marius. **Fara atentionare.** - **PROFORMA → FACTURA cu linii deja adaugate:** liniile au fost adaugate **fara** alegere de stoc (`gestionabil` fortat `0`), deci **nu au gestiune**. Aici e riscul „drumului invers". - **A forta alegerea stocului si pe proforma NU e o optiune** — ar regresa cerinta din 12.03.2021 (*„nu mai este necesara existenta articolelor in stoc pentru a genera proforma"*): un articol fara stoc n-ar mai putea intra pe proforma, pentru ca `do_alege_stoc` ar cere un lot inexistent. ### Decizia 43 (Marius, runda 12) — proforma NU alege stoc, si factura se face DIN proforma **Pe proforma nu se alege stoc, si asta e cerinta, nu efect colateral.** Motivul, in cuvintele lui Marius: *„sa dau o proforma chiar si in absenta stocului, pentru ca ma intereseaza doar pretul de vanzare, nu si cel de achizitie din stoc"*. Deci: - **Varianta (b) — realegerea gestiunii linie cu linie la comutare — e RESPINSA.** Nu se mai reargumenteaza. - **Comutarea PROFORMA → FACTURA cu linii prezente se blocheaza**, cu mesaj explicit. Comportamentul de azi (`gestionabil = 0` fortat la incarcare, `ofacturare.prg:333-336`) se **pastreaza**, nu se rafineaza. - **Sensul FACTURA → PROFORMA ramane liber**, fara atentionare, cu sentinela aplicata **la salvare** — liniile au deja gestiune reala, deci nu se pierde nimic. **Cerinta noua: „ulterior o sa vreau si o factura din proforma".** Nu prin comutarea tipului pe acelasi document, ci ca **document nou**, generat din proforma. **Mecanismul pare sa existe deja pe calea de copiere** — `cursor_retur_document` reface gestionabilitatea reala cand `V_COPIERE = 1` (`GESTIONABIL = B.IN_STOC`, `PACK:3993-4000`), deci liniile venite dintr-o proforma **redevin gestionabile**, trec prin `do_alege_stoc` la adaugare, primesc `id_gestiune` real si descarca gestiune normal (`docs\cercetare\s5b_proiectare_proforma_copiere.md` §2.6). **De verificat inainte de a te baza pe asta**, pentru ca §2.6 descria copierea in general, nu cazul „sursa e o proforma": 1. poate fi azi o **proforma** aleasa ca sursa de copiere, sau e exclusa undeva? 2. `do_copiaza` degradeaza tipul spre `{1,5,10,22}` — ce tip rezulta dintr-o proforma si e cel dorit? 3. ~~se pastreaza legatura proforma → factura?~~ **RASPUNS (Marius, runda 12): DA — „ar fi bine sa aiba urma sursei, la fel ca factura din aviz".** Deci trasabilitatea **e ceruta**, si tiparul de urmat e cel deja existent pentru aviz → factura: `scrie_corespondente_vanzari(1)`, chemata din `finalizeaza_factura`, care scrie in **`VANZARI_CORESP`** perechea `(ID_VANZARE_FACT = factura, ID_VANZARE_AVIZ = documentul sursa, TIP = 1)`. Numele coloanei e generic, deja reutilizat pentru mai multe feluri de perechi (`TIP = 1/2` aviz-factura, `TIP = 3` retur), deci **structura nu se schimba — se adauga o valoare noua de `TIP`** pentru proforma → factura. **De proiectat, nu de presupus:** ce valoare de `TIP` se aloca; de unde stie fluxul de copiere ca sursa a fost o proforma (azi `do_copiaza` degradeaza tipul, deci informatia s-ar putea pierde **inainte** de scriere — vezi punctul 2); si daca `marcheaza_facturat` / `FACTURAT` trebuie sau nu atinse (pe aviz sunt, pe proforma probabil **nu**, pentru ca proforma nu e un document de livrare — **de confirmat, e exact genul de detaliu care se copiaza gresit din tiparul avizului**). ~~**Aceasta e prima sarcina de proiectare a rundei 13.**~~ **LIVRATA — vezi S5c mai jos.** *Depinde de:* S5. #### S5c — Factura din proforma (decizia 43, cerinta noua) **PROIECTAT (runda 13) — `docs\cercetare\s5c_factura_din_proforma.md`. Toate cele trei intrebari si ambele capcane au raspuns cu dovada. Vestea buna: mecanismul de baza chiar exista, si nu se atinge.** - **(a) O proforma poate fi azi aleasa ca sursa de copiere, fara nicio excludere.** `IsCopy` returneaza **necondiționat `.T.`** (`COMUN\clase\ofacturare_comun.vc2:4969`, cu comentariul explicit *„POT SA COPIEZ ORICE, TRATEZ TIPURILE IN DO_COPIAZA"*; filtrul vechi pe tip e comentat dedesubt, la `:4971`) — **verificat direct**. Nici vizibilitatea butonului, nici filtrul de grid „Facturi&Avize / Proforme" nu se uita la `eproforma`. Deci nu e nimic de deblocat. - **(b) Tipul rezultat e corect, fara schimbare.** Copierea produce un document real (`nIdTipDoc = 5`, `eproforma = 0`) — factura fiscala, exact ce trebuie. Degradarea lasa neatins grupul-tinta **`{1,5,7,10,22,23}`** (vezi corectia de cifre de mai sus). - **(c) `TIP = 4` e liber in `VANZARI_CORESP`** si se aloca pentru „factura din proforma". Confirmat **cod + date** pe schema vie: tabela are un **singur scriitor in toata baza** (`pack_facturare.scrie_corespondente_vanzari`), iar azi se folosesc doar `1/2/3`. **Structura nu se schimba.** - **Capcana (i) — confirmata, si e miezul poveștii.** `id_vanzare` al proformei **supravietuieste** copierii (prin `poDate.listaid`), dar faptul ca **sursa era o proforma se pierde**: `completeaza_setari_document` copiaza `.listaid`, dar **nu** si `eproforma` (`COMUN\programe\ofacturare_comun.prg:387` — **verificat direct: `eproforma` nu apare nicaieri in tot fisierul**). De aceea legatura **nu se poate agata de `CASE`-ul din `finalizeaza_factura`**, care e cheiat pe `ntip` — `ntip` nu poarta distinctia. Solutia: un semnal nou, client-side (`poDate.lProformaSursa`), capturat exact acolo unde informatia mai exista, plus un **apel Oracle explicit separat** care reutilizeaza `scrie_corespondente_vanzari(4)` **neschimbata**. Zero cod PL/SQL nou pe calea recomandata. - **Capcana (ii) — INFIRMATA presupunerea comoda, cu patru argumente: `marcheaza_facturat` NU se cheama pe proforma.** Proforma n-are `VANZARI_CANTITATI`, n-are ramura de reversare la stergere in `sterge_factura`, nimic nu filtreaza dupa `FACTURAT` pe proforme, si oricum calea aleasa e in afara `CASE`-ului care il cupleaza azi. Era exact detaliul care se copia gresit din tiparul avizului. **Ce NU se schimba:** `IsCopy`, vizibilitatea butonului de copiere, filtrul de grid, degradarea de tip din `do_copiaza`, `cursor_retur_document` (`GESTIONABIL = B.IN_STOC`), si `scrie_corespondente_vanzari` insasi. **De decis de Marius (cinci puncte, niciunul blocant):** 1. **`TIP = 4`** — liber azi, dar alocarea e **ireversibila in date** odata intrata in productie. De confirmat explicit, nu tacit. 2. **Apel pe starea de sesiune a pachetului (`clistaid` / `nid_vanzare`) vs. procedura noua cu parametri expliciti.** *Recomandarea raportului:* varianta simpla (zero cod Oracle nou), **cu conditia** verificata la implementare ca niciun apel Oracle intercalat nu reseteaza starea intre scrierea facturii si scrierea corespondentei. 3. **Aceeasi proforma poate fi copiata de N ori**, fiecare copie cu randul ei `TIP = 4`. E comportamentul implicit al oricarei copieri de azi. *Recomandare:* daca deranjeaza, avertisment — **nu** blocare. 4. **Garda simetrica la stergere** (*„nu poti sterge o proforma care are deja factura generata din ea"*), pe tiparul `TIP IN (1,2,3)` din `sterge_factura` — de decis daca se doreste. 5. **Afisarea „provine din proforma X"** pe factura noua — vine gratis din legatura scrisa, la nivel de **document** (ca la retur), nu de linie. De decis daca intra acum sau mai tarziu. *Gata cand:* dintr-o proforma emisa se genereaza o factura reala, cu numar nou, care descarca gestiunea normal, are rand `TIP = 4` in `VANZARI_CORESP` catre proforma sursa, iar proforma **nu** e marcata `FACTURAT`. *Depinde de:* S5b. ### Decizia 49 (Marius, runda 13) — cele doua formulare merg IN PARALEL, si ALEGE UTILIZATORUL **Cerinta, in cuvintele lui Marius:** *„utilizatorul sa poata accesa alternativ, daca doreste"*. Deci **nu** o setare care ruteaza tacit pe un flux sau altul, si **nu** un pilot pe cativa oameni: ambele formulare raman accesibile, iar **alegerea o face omul, in momentul in care factureaza**. Rostul e sa existe mereu **un flux despre care se stie ca functioneaza**, cat timp cel nou se stabilizeaza. > **Corectie a rundei 13:** prima formulare a acestei decizii descria un pilot per utilizator, prin > optiunea de firma. **Gresit** — Marius a corectat: optiunea **nu alege**, ci doar **face alegerea > disponibila**. Nu se reargumenteaza in varianta veche. **Mecanismul exista deja in produs si face exact asta**, nu se inventeaza: - `factureaza` (`COMUN\programe\ofacturare.prg:87-93`) verifica `gnFacturareNou` si, cand e pornita, **intreaba la fiecare facturare**: `AMESSAGEBOX('Facturare noua (DA) sau standard (NU)?', 4+32, ...)`. Optiunea deschide alegerea; raspunsul il da utilizatorul, de fiecare data. - `gnFacturareNou` e o **optiune de firma**: `optiuni_firma` (`COMUN\programe\oinit_optiuni.prg:225-275`) cheama `SCRIE_OPTIUNI(gcUserName)`, parcurge `v_optiuni` si **declara dinamic** globalele publice dupa tip (`Public gn&lcvarname` pentru `NUMERIC`), cu filtru optional pe program (`Isnull(programe) Or gcNumeProgram $ programe`). **Deci se activeaza si se dezactiveaza din date, fara livrare de exe.** - **Fluxul vechi ramane intreg:** `factureaza` + `frm_facturare_articole`. S2 sterge doar **`factureaza2`**, un fork mort din 2017 (executia interogarii dezactivata, `lnSucces = 1` hardcodat, zero utilizatori reali) — **nu** calea veche. **S2 nu are voie sa desfiinteze alegerea.** - **Pentru etapa II plasa e alta, si exista deja:** editarea prin regenerare e o **actiune noua**, deci „fluxul vechi" pentru ea e **fluxul lui #6**, pe care decizia 38 il pastreaza oricum. **Cum se prezinta alegerea — de ales la implementare, ambele satisfac cerinta:** - **(A) intrebarea de azi**, modal la fiecare facturare. Exista deja, zero cod. Neajuns: intreaba si cand utilizatorul stie de o luna ce vrea. - **(B) doua intrari distincte** in meniu / doua butoane („Facturare" si „Facturare (nou)"). Aceeasi libertate, fara modal la fiecare document. **Recomandat.** Se poate porni cu (A), care e gata, si trece la (B). **Limita, si trebuie spusa explicit ca sa nu creeze o falsa siguranta: alegerea acopera VFP-ul, nu Oracle.** Modificarile din pachete — parametrul nou al lui `contabilizeaza_articol` (decizia 34), variabila noua din `SET_IDFACT`, si `INSERT` → `MERGE` in `DOCUMENTE` (S9) — sunt **cod comun al intregii suite** si se aplica **tuturor deodata**, indiferent pe ce formular alege omul sa lucreze. Proiectarea le face **inerte prin constructie** (parametru `NULL` → executie identica cu azi; `WHEN MATCHED` care nu se declanseaza pe drumul normal), dar asta e o garantie de regresie zero, **nu** o cale de intoarcere. Consecinta practica: pe formular si procedura intoarcerea e imediata; pe baza de date, siguranta vine din **testarea unui ciclu normal de scriere in fiecare produs** (deja ceruta in S9). ### Decizia 50 (Marius, runda 13) — se editeaza documente curente, nu documente dintr-un lant **Formularea lui Marius:** *„in principiu nu se poate edita un document pentru care s-a facut retur; de principiu se pot modifica documente curente, nu cele care fac parte dintr-un lant"*. **Deci garda de azi ramane, si devine regula declarata, nu limitare tolerata.** `sterge_factura` arunca `ORA-20000` cand documentul are deja facturi / avize de retur emise peste el; S7 **semnaleaza conditia inainte de intrarea in formular**, cu mesaj clar, nu ca eroare Oracle la final. Utilizatorul care chiar vrea sa editeze sterge intai documentele-copil — comportament existent, nu ceva de construit. **PRECIZAT de Marius (runda 13), si inchide punctul: „lantul" se citeste IN AMONTE, nu in ambele sensuri.** In cuvintele lui: *„daca este generata factura din aviz, avizul nu se mai poate modifica, factura da, pentru ca este documentul curent; ma refeream la documentele din lant anterioare"*. Deci regula, in forma finala: - **Documentul care are urmasi se blocheaza** — avizul din care s-a facut factura, factura peste care s-a emis retur. Are deja o reprezentare in cod: garda din `sterge_factura`. - **Documentul de la capatul lantului ramane editabil** — el e „documentul curent". **Factura din aviz (`ntip = 4`) ESTE editabila**, chiar daca are parinte. - Prin urmare **perimetrul etapei II nu se restrange**, iar intrebarea deschisa din S10 despre `ntip = 4` (aceeasi re-derivare tacuta ca pe contract, dar cu alta sursa de comparat) **ramane in picioare** — nu dispare, cum s-ar fi intamplat la citirea stricta. **VERIFICAT in runda 14 — si premisa era gresita.** Raport: `docs\cercetare\garda_aviz_facturat.md`. Se credea ca garda de azi blocheaza documentul doar cand are **retururi** peste el. **Nu e asa:** a doua garda din `sterge_factura` testeaza `TIP IN (1, 2)`, iar **`TIP = 1` este corespondenta aviz → factura normala, nu retur** (`EXPORT:5464-5476`, semantica citita ramura cu ramura din `CASE`-ul lui `finalizeaza_factura`, `EXPORT:14818-14839`). Formularea gresita venea din citirea comentariului `-- verific daca exista facturi sau avize de retur`, in care „de retur" se distribuia si peste „facturi"; codul zice altceva. **Deci regula ceruta de decizia 50 e deja implementata pentru avize** — nu e nevoie de nicio garda noua ca *regula*. **Dar sunt trei garzi, nu doua, si a treia schimba enuntul deciziei 50:** | garda | `EXPORT` | ce blocheaza | |---|---|---| | 1 | 5452-5462 | **factura** care are facturi de retur peste ea (`TIP = 3`) | | 2 | 5466-5476 | **aviz** care are factura (`TIP = 1`) sau aviz de retur (`TIP = 2`) peste el | | 3 | 5480-5494 | **factura din aviz** ale carei avize-sursa au primit intre timp aviz de retur | Prin urmare **„factura din aviz ESTE editabila" nu e neconditionat**: e editabila doar cat timp niciunul dintre avizele ei nu a primit aviz de retur. Nu e „capat de lant" prin definitie. **Ce lipseste nu e regula, e MOMENTUL.** Garda traieste in interiorul lui `sterge_factura`, deci se manifesta ca `ORA-20000` **in mijlocul** pasului de stergere din regenerare — dupa ce utilizatorul a completat formularul si dupa deschiderea tranzactiei. S7 are nevoie de un **pre-flight read-only** pe **exact aceeasi conditie**, fara reimplementarea regulii (vezi S7). Interogarea se face pe `VANZARI_CORESP`, **nu** pe `VANZARI.FACTURAT`: `FACTURAT` e un marcaj derivat, scris si resetat, dar **necitit de nicio garda** — semnalul autoritar e `VANZARI_CORESP`. **GOLUL REAL NU E PE AVIZ, E PE PROFORMA — si a devenit relevant chiar in runda 14, odata cu decizia 51.** `pack_facturare.sterge_proforma` (`EXPORT:5610-5635`) **nu are nicio garda**: corpul ei e doua `UPDATE ... SET STERS = 1`. E o procedura **complet separata**, care nu cheama `sterge_factura`, deci garda existenta **nu se extinde automat** la `TIP = 4`. O proforma din care s-a emis factura se poate sterge azi fara niciun avertisment. Calea VFP iese devreme din `do_sterge` (`ofacturare_comun.vc2:4707-4719`), inainte de restul verificarilor. **Asta e continutul concret al punctului deschis 4 din S5c** („garda simetrica la stergere") — nu mai e o intrebare de principiu, e o garda de scris intr-o procedura care azi n-are niciuna. **Comanda si contractul nu trec prin `VANZARI_CORESP`** si n-au garda de tip „are urmasi" pe factura: comanda se leaga prin `VANZARI.ID_COMANDA` + `inchide_comanda`, iar garda ei e **in VFP si pe comanda**, nu pe factura (`COMUN\clase\ocomenzi.vc2:1806-1807` la modificare, `:2065-2066` la stergere); contractul se leaga prin `VANZARI.ID_CTR` + `CTR_RATE_FACTURI`. ### Deciziile lui Marius, runda 13 (10.08.2026) — luate, nu de reluat Cele cinci puncte neblocante ramase din runda 12. **Patru din cinci au mers pe recomandare; al cincilea (48) a respins-o motivat.** Fiecare e scrisa si la locul ei, in povestea careia ii apartine. 44. **`poArticol` devine parametru explicit** al dialogurilor de linie (`do_verifica_articol`, `do_alege_stoc` / `frm_articol_gest_factura`), in loc de variabila `Private` populata de apelant. **O singura solutie pentru golul din S4e si cel din S4f (R7)** — e aceeasi problema structurala. Schimbare de contract, **aprobata ca atare**; cere inventarul si actualizarea tuturor apelantilor existenti inainte de prima editare. (S4e, S4f) 45. **Asimetria din `do_modifica`** se lasa sa se **corecteze de la sine** prin recalculul la cerere, dar corectia se **declara explicit in changelog**. (S4, punctul 2) 46. **Corectia pe tipurile `23,41` asteapta punctul 2** (recalculul pe server, care acopera Rolul B). Punctul 1 **nu se redeschide** acum. (S4, punctul 2) 47. **`zi_curs`: simetrie.** Campul se ascunde la loc la stergerea ultimului articol in valuta; „clipitul" e acceptat ca pret al unei reguli unice. (S4d) 48. **Tabelul de configurare per tip = cursor generat in cod la pornire**, nu `DBF` static, nu tabela Oracle. Detectia tipului necunoscut (eroare la deschidere) se pastreaza; se accepta recompilarea la orice tip nou. (S5) ### Deciziile lui Marius, runda 14 (11.08.2026) — luate, nu de reluat 51. **`TIP = 4` in `VANZARI_CORESP` = „factura scrisa dintr-o proforma".** Confirmat explicit, dupa ce i s-a explicat ce inseamna `1/2/3` (`TIP` = **natura legaturii parinte-copil**, nu tipul documentului: `1` = aviz → factura, `2` = aviz → aviz de retur, `3` = factura → factura de retur). `4` intra in aceeasi familie cu `1`. Valoarea e libera pe ambele fronturi (niciun apel cu `4` in pachet, zero randuri in date), tabela are un singur scriitor in toata suita, structura nu se schimba. **Alocarea e ireversibila odata cu primele date de productie** — acceptat ca atare. (S5c) 52. **`do_modifica` ramane activ pentru multi-selectie.** Formularul unificat preia cazul cu un singur document; calea veche ramane pentru modificarea in bloc. **Nicio capacitate nu se pierde la retragere** — dar consecinta e ca S2 nu poate desfiinta nici aceasta ruta, nu doar alegerea de la decizia 49. (S8b, punctul 10) 53. **Atasamentul PDF se sterge la reemitere**, nu se remigreaza si nu se marcheaza „versiune inlocuita". Documentul reemis porneste curat; PDF-ul se regenereaza la prima listare. **Consecinta semnalata explicit lui Marius si acceptata de el:** urma a ceea ce s-a trimis efectiv clientului dispare din sistem. Pasul `UPDATE ATASAMENTE_VANZARI SET id_vanzare = :nou`, adaugat in S9 la runda 13, **se inlocuieste cu stergere**. (S11, punctul 15) 54. **La reemitere se scriu valorile din formular, nu se reciteste sursa.** Formularea lui Marius: *„nu inteleg de ce se schimba valorile fata de ce se incarca din factura sursa care se modifica? asta este comportamentul pe care il doresc — modific sursa"*. **Asta rastoarna S10:** intrebarea nu mai e „ce garda punem peste re-derivare", ci **„re-derivarea chiar se produce pe calea de reemitere?"** — si daca da, se **elimina**, nu se avertizeaza. Avertizarea + confirmarea propuse de raportul S10 sunt **respinse**: nu se cere utilizatorului sa confirme o schimbare pe care n-a cerut-o. Vezi S10, rescris. (S10, punctele 11 si 12) 56. **„DA LA TOATE" — Marius a acceptat in bloc toate recomandarile deschise, runda 14.** Formularea lui: *„da la toate, mai putin"* cele doua puncte pe care le-a intrebat separat (48/49 si voiajele). **Nu se mai reintreaba niciunul dintre punctele de mai jos** — sunt luate, si fiecare e scris si la locul lui, in povestea careia ii apartine: - **S8** — canalul de citire a liniilor: **(B), procedura noua `cursor_editare_document`** (nu se extinde `cursor_retur_document`, ca sa nu se schimbe comportamentul copierii; nu `FACT_VFACTURI_DETALII`, care pierde `ID_POL`, `PRETD` si tratamentul valutar — argument intarit de faptul ca **`VVANZARI_ARTICOLE` nu expune `ID_POL` / `ID_CTR`**, vezi S10). - **S8** — `GESTIONABIL` la editare: **din document**, nu din nomenclatorul curent. - **S8** — `text_aditional`: **se normalizeaza la incarcare** (`Chr(170)` → `CR+LF`) si se re-normalizeaza la comparatie. Formele diferite salvate de cele doua rute de scriere se **semnaleaza separat** ca defect preexistent. - **S8** — `zi_curs` pe calea de editare: **ascuns** (precedent: tipurile 8/9). Abatere mica de la decizia 5, asumata. - **S8** — `poDate.lEditare`: **proprietate pe `oDateFactura`**, nu parametru (patru locuri au nevoie de semnal; `lCopiere` e deja acolo cu acelasi rol). - **S8** — `id_ruta`: **proprietate noua pe `oDateFactura`** (fara ea S8c nu poate implementa unul din cei 14 parametri). - **S8** — defectul de prefixare `text_aditional` la `Init` pentru contracte: **se ocoleste in #13** prin `lEditare` si **se semnaleaza separat**, nu se repara pe calea de emitere. - **S5c** — apelul Oracle pentru legatura proforma → factura: **varianta simpla**, pe starea de sesiune a pachetului, zero cod PL/SQL nou — **cu conditia verificata la implementare** ca niciun apel intercalat nu reseteaza starea intre scrierea facturii si scrierea corespondentei. - **S5c** — aceeasi proforma facturata de N ori: **avertisment, nu blocare**. - **S5c** — garda simetrica la stergerea proformei: **se scrie**. Nu e reutilizare — `sterge_proforma` n-are azi nicio garda (vezi decizia 50, sectiunea rescrisa). - **S5c** — afisarea „provine din proforma X": **da**, la nivel de document. - **S4g** — `CU_TVA = 1` hardcodat: **se verifica pe date inainte de implementare**, nu se presupune inofensiv. Efectul prin `nproc_tva_max` e masurat, pe linie scutita + discount global. - **S4g** — trasabilitatea `CONT_VENIT` pe `VANZARI_DETALII`: **da**, se pastreaza coloana. - **Decizia 54, cele trei consecinte: acceptate toate trei.** `IN_STOC` vine din formular (si **S8 trebuie sa-l incarce** — vezi S10, consecinta 1); **se pierde validarea** liniei fata de comanda pe ramura comenzi, asumat; **`PROC_TVAV` ramane derivat** — reemiterea dupa o modificare legala de cota va da cota noua, **acceptat deocamdata**; transformarea lui in parametru ramane o decizie separata, daca se cere vreodata reproducere exacta si peste asta. - **Defectul `lnTip` din `do_copiaza`** (runda 13): **se repara la #6**, fisierul fiind al lui. - **Punctele pur interne** (numarul codului de eroare `FACT-0xx`, numele cheii de optiune `FACT_SCD_ARTFPRET`, forma semnalului de regenerare) — **lasate la latitudinea implementarii**, cu recomandarile deja scrise in povestile lor. **Raman deschise doar doua**, si amandoua din motive proprii, nu din lipsa de raspuns: **(a) tipurile 48/49** (custodie) — Marius a cerut sa stie implicatiile, i s-au explicat, decizia n-a fost inca data. *Inchis intre timp — decizia 60, runda 16: da, sunt editabile prin #13, cu executia conditionata de verificarea custodiei in curs.* **(b) cota si explicatia de TVA a discountului de document** — **cercetare TERMINATA** (sectiunea K-bis): cota era regula implicita „cota maxima de pe factura", explicatia e azi o constanta hardcodata (`ReasonCode="95"`, text „Discount"). **Partea de repartizare s-a decis — decizia 59, runda 16: proportional pe cote.** Ramane deschis doar campul text optional de motiv. *Alegerea variantei de asezare, listata aici ca a treia, s-a inchis intre timp — decizia 57.* 55. **Metoda de executie e obligatorie, si e scrisa in plan.** La implementare planul **se sparge pe stories**, fiecare story fiind o livrare de sine statatoare; **fiecare pas se testeaza**, nu doar capetele de etapa (S6 / S12); si **fiecare story trece prin code review dupa implementare si dupa teste, inainte de commit**. Sectiunea **„Metoda de executie"**, imediat inainte de Etapa I. S13 nu mai e momentul review-ului, ci al inchiderii. ### Deciziile lui Marius, runda 15 (11.08.2026) — luate, nu de reluat 57. **Asezarea zonei de jos: VARIANTA D.** Aprobata explicit („sunt de acord cu varianta D"), dupa doua corectii cerute de el pe drum: (a) *„imi place linia de totaluri de la varianta C, dar incasarea si alte date le vreau tot in acelasi formular, colapsate, mai jos de totaluri"*; (b) *„sectiunile incasare si alte date colapsate trebuie sa fie pe acelasi rand — este destul loc pentru amandoua, detaliile pot sa fie grupate pe orizontala, nu pe verticala"*. A / B / C **cad**. **Ce inseamna D, concret, pentru S1 si S3:** 1. **Banda de totaluri** (preluata din C): pe toata latimea, lipita de grid, cu baza, discountul pe articole, discountul de document **cu procentul editabil pe loc** si TVA desfacute pe un rand; totalul mare, singur, la dreapta. 2. **Incasare si Alte date sunt sectiuni colapsabile in formular, nu dialoguri modale** — **una langa alta pe acelasi rand**, sub totaluri, fiecare pe jumatate de latime. Randul inchis arata **rezumatul continutului** („NUMERAR · 5 570,55 lei"), nu doar un titlu. Independente: se poate tine deschisa doar una. Campurile dinauntru se aseaza **pe orizontala**, doua randuri fiecare. 3. **Nu exista bara de comenzi jos.** `but_renunt` / `but_termin` raman unde sunt azi — in banda de titlu, sus in dreapta (`COMUN\clase\cmd_butoane.vc2:288` si `:386`, butoane-imagine cu `Top = 1`, `Anchor = 8/9`, tooltip „Renuntare (ESC)" / „Terminare (CTRL+F)"). 4. **Antetul strans la doua randuri**, fara titluri de grup, si **panoul „Discount pe document" dispare** ca panou separat. **Patru consecinte de dus in executie, nu de redescoperit:** - **Dispare „renunt doar la incasare".** Fara `Accept` propriu pe dialog, ce se completeaza in sectiune se scrie la `Termina`, impreuna cu documentul. **I-a fost spus explicit** inainte de aprobare si a acceptat. - **Validarea incasarii nu mai are moment propriu** — se muta in `Termina`, langa restul verificarilor. **De scris explicit in S9.** - **Starea deschis/inchis a sectiunilor** — de decis daca se retine intre documente sau porneste mereu inchisa. Nu blocheaza nimic; recomandare: porneste inchisa, se retine per utilizator. - **Caption pe cele doua butoane.** Sus in dreapta, langa `✕`-ul ferestrei, un `✓` verde fara text e ambiguu. Punctul era deja notat in v8 pentru `But_renunt1` / `But_reset1`; D il face mai apasat, pentru ca acum ele sunt **singura** cale de iesire. **INCHIS.** Cercetarea despre cota si explicatia de TVA a discountului de document (punctul deschis (b) de la decizia 56) **s-a terminat** — vezi sectiunea K-bis — si repartizarea s-a decis (**decizia 59, runda 16**: proportional pe cote, fara sa se ceara cota de la utilizator), deci a treia sectiune „grea" (cota + explicatie proprii) nu mai e in discutie. Ramane varianta minimala: **decizia 61 (runda 16) a materializat-o** — Marius vrea campul text optional de motiv — si **decizia 64 (runda 16) a inchis si asezarea: randul se imparte in trei**, ~440 px fiecare la 1366 px, strans. D ramane intr-un etaj. 58. **Mockup-ul asezarii ramane doar online.** Formularea lui: *„nu vreau artifactul html, doar online, ca sa nu mai intretii 2 variante"*. `docs\mockup_13_variante_asezare_jos.html` **a fost scos din `docs\`**; sursa de adevar e **https://claude.ai/code/artifact/dfc70d38-856f-48fd-b6af-3da78e9da4f7**. Ca sa-l modifici: `WebFetch` pe URL → scrie HTML-ul intr-un fisier de lucru **in scratchpad, nu in `docs\`** → `Artifact` cu **`url` = link-ul de mai sus** (fara `url` se creeaza link nou). **`docs\mockup_13_formular_unificat.html` (v8) nu intra sub regula asta** — cerinta a fost data numai pentru mockup-ul asezarii. ### Decizia 59 (Marius, runda 16) — luata, nu de reluat 59. **Discountul de DOCUMENT se repartizeaza PROPORTIONAL PE COTE.** Formularea lui: *„discount pe document repartizat proportional pe cote"*. Se adopta recomandarea cercetarii din **K-bis**: regula de azi („toata valoarea discountului primeste cota MAXIMA de pe factura", prin `Calculate Max(proc_tvav)`) **se inlocuieste** cu repartizarea proportionala cu baza fiecarei cote de pe factura. **Nu se cere utilizatorului cota** — repartizarea e automata. **Ce atrage dupa sine, tot din K-bis:** 1. **Doua locuri de schimbat, nu unul, si in ACEEASI livrare:** `prelucreaza_facturacrs` (VFP, `COMUN\programe\ofacturare_comun.prg:1886-1897`) trebuie sa insereze cate un rand-sentinela **per cota** in loc de unul singur; si `recalculeaza_totaluri_vanzari` (PL/SQL, `ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:16081-16092`) trebuie sa inlocuiasca `MAX(...)` cu suma repartizarii. Daca se schimba doar unul, `VANZARI.TOTAL_TVA` si TVA-ul din eFactura **diverg** pe facturile cu cote mixte. **Nu e optional si nu se poate esalona.** 2. **Bucatile de discount trebuie sa primeasca `id_jtva_coloana` al grupului pe care il reduc**, nu doar cota. Cheia de grupare din `xmlefactura.prg:246` are cinci campuri si `expltva` se completeaza tot prin `id_jtva_coloana`. O implementare care seteaza doar `proc_tva` **lasa grupul orfan exact unde e azi** — repara jumatate din defect si o lasa pe cealalta. 3. **eFactura nu cere nicio modificare** — `xmlefactura.prg:758-792` grupeaza deja `GROUP BY proc_tva` si emite cate un `AllowanceCharge` per cota. 4. **Diferenta de rotunjire cade pe cota cu baza cea mai mare**, ca suma bucatilor sa fie exact `VANZARI.DISCOUNT`. E recomandarea cercetarii, luata ca implicita — Marius a fost instiintat ca se merge asa fara sa mai fie intrebat, deci se schimba doar daca obiecteaza. (Raportul spune pe alocuri „ultima cota preia diferenta"; **regula care se implementeaza e cea de aici**, ca sa nu ramana doua formulari in circulatie.) 5. **Doua consecinte vizibile, acceptate implicit prin decizie:** factura tiparita va arata **N randuri** „Discount X % Factura" in loc de unul, pe facturile cu cote mixte; si **nota contabila** primeste TVA-ul discountului spart pe cote. 6. **Rezolva si grupul orfan** de pe facturile scutite / taxare inversa / intracomunitare (K-bis), fara garda separata, si desfiinteaza ambiguitatea lui `agettipcota(1)`. **Raman DESCHISE, nu de presupus rezolvate:** - **campul text optional de motiv** (`AllowanceChargeReason` in locul constantei „Discount"; `ReasonCode` ramane `95`) — Marius **nu s-a pronuntat**; de el atarna si intrebarea de asezare din S1 / decizia 57 (a treia sectiune pe rand sau rand propriu); - **retroactivitatea la relistare / retrimitere**: o factura veche relistata sau retrimisa in eFactura ar genera alt XML decat cel trimis initial. **Nedecis.** ### Deciziile 60-65 (Marius, runda 16) — luate, nu de reluat 60. **Facturile de marfa in CUSTODIE (tipurile 48 si 49) SUNT editabile prin #13.** Formularea lui: *„vreau sa fie posibila editarea si a facturilor in custodie"*. Intra deci in perimetrul **etapei II**, contrar recomandarii „nu acum" din raportul S8 (intrebarea 7, §8.2). **Inchide** punctul **(a)** de la **decizia 56** (tipurile 48/49, singurul ramas deschis din blocul „da la toate") si **necunoscuta N5** din `docs\cercetare\s8_incarcare_document.md` §8.1, plus restul intrebarii 7 din §8.2. **Ramificatie noua pentru S8:** pe tipurile 48/49, controlul `ct_clb_altele` e scos **neconditionat** la copiere (`ofacturare.vc2:9705-9712`), spre deosebire de tipurile 1/5/10 unde copierea il **pastreaza** si il reeticheteaza (`s8_incarcare_document.md` §3.2, ramura 3). Calea de **editare** trebuie sa-l pastreze si pe custodie — ramura 48/49 a copierii nu poate fi refolosita ca atare. **VERIFICAT — blocantul CADE, dar premisa initiala era gresita.** Raportul: `docs\cercetare\custodie_48_49_stergere_reemitere.md`. `scrie_fact_aviz_custodie` **nu are legatura cu tipurile 48/49** — are un singur apel in tot pachetul (`PACK:7521`), pe ramura `pack_facturare.ntip <> 4` (`:7472`), deci serveste `ntip = 4` (factura din avize), nu custodia; numele procedurii a indus in eroare, si premisa gresita a circulat pana in aceasta decizie. Emiterea unui document 48/49 **nu atinge deloc stocul**: sursa lor unica de articole e `cursor_articole_k` (`PACK:3595-3701`), restransa explicit la `WHERE C.IN_STOC = 0` (`:3695`), iar `descarca_gestiune` se cheama doar cand `in_stoc = 1` (garda dubla, la apelant `:7472-7475` si in corpul procedurii `:7789-7797`). `sterge_factura` (`:5432-5607`) nu are ramura dedicata pentru 48/49, si nici cele trei garzi de refuz al stergerii (`:5452-5494`) nu le prind — dar **n-are ce reversa**, fiindca nimic legat de stoc n-a fost scris la emitere. **Regenerarea e sigura pe 48/49**, nu pentru ca reversarea ar functiona, ci pentru ca nu exista nimic de reversat. **Rezerva, de scris, nu de ascuns:** siguranta atarna de un invariant azi impus doar de sursa de articole — „documentele 48/49 contin numai articole cu `IN_STOC = 0`". Invariantul **nu s-a verificat exhaustiv**, doar constatat pe sursa curenta. #13 schimba modul de adaugare a articolelor (**S4** — cautare pe server, in linie; **S4g** — adaugare de articole la modificarea oricarui document): daca formularul unificat ajunge sa permita adaugarea unui articol **gestionabil** pe un document 48/49, invariantul se rupe si concluzia de siguranta pica. **Cerinta pentru S4/S4g/S5: pe tipurile 48/49 se pastreaza restrictia la articole `IN_STOC = 0`** — vezi notele de executie la acele povesti; devine **criteriu de test**, nu presupunere. Pentru **emitere**, tipurile 48/49 erau oricum deja acoperite de etapa I (S5 completeaza cele patru randuri lipsa din `Do Case`: 45, 48, 49, 52) — decizia 60 priveste doar **editarea**. 61. **Discountul de document primeste un CAMP TEXT OPTIONAL de motiv.** Formularea lui: *„la discount, da, un camp text optional"*. **Inchide** ultimul punct ramas deschis din **K-bis** si din **decizia 56 (b)** — campul de motiv nu mai e „Marius nu s-a pronuntat". **Ce inseamna concret:** - inlocuieste constanta hardcodata `"Discount"` ca `cbc:AllowanceChargeReason` (`COMUN\programe\xmlefactura.prg:774-776`); **`ReasonCode` ramane `95`**; - cere **stocare noua** — azi nu exista nicio coloana pe `VANZARI` pentru motivul discountului de document; - cere **un control nou in formular** — singura bucata din reparatia discountului (K-bis / decizia 59) care chiar cere UI nou, restul fiind repartizare automata fara interactiune. **Redeschisese, in varianta minimala, intrebarea de asezare de la decizia 57 — INCHISA acum prin decizia 64 (runda 16): randul de jos se imparte in trei** (~440 px fiecare la 1366 px, strans); varianta D ramane intr-un etaj. Paragrafele din **S1** si din **decizia 57** sunt actualizate in acest sens. 62. **Restrictia de modificare e „documentul sa NU fi fost trimis in eFactura" — si de aici retroactivitatea NU mai e o intrebare.** Formularea lui: *„singura restrictie pentru modificare este sa nu fie trimisa in eFactura, deci nu are legatura retroactivitatea"*. Garda insasi nu e noua — **S7** o are deja, reutilizata din #6 (`COMUN\programe\ofacturare_editare.prg:19-28`, functia `EsteInEFactura`; apelata din `COMUN\clase\ofacturare_comun.vc2:3764`). Ce era intrebare deschisa era doar partea lasata de **decizia 59**: daca o factura veche, retrimisa, ar trebui tratata altfel fata de una noua. Raspunsul lui Marius **inchide** acea intrebare: nu se leaga de nicio data si de niciun flag suplimentar — verificarea `EsteInEFactura` ramane singurul criteriu de modificare. **Ramane un rest neacoperit, semnalat lui Marius, nedecis inca:** o factura **veche, deja trimisa**, daca e doar **relistata** (tiparita din nou, nu editata), trece iar prin `prelucreaza_facturacrs` si — dupa decizia 59 — ar produce **N randuri de discount in loc de unul** pe facturile cu cote mixte, deci hartia ar diferi de originalul trimis in eFactura. Relistarea nu e modificare, deci garda `EsteInEFactura` nu o acopera. 63. **CERINTA NOUA DE AUDIT pe documente.** Formularea lui: *„este doar util de stiut data crearii, data modificarii daca este cazul, utilizatorul crearii si al modificarii daca este cazul, respectiv data stergere si utilizator stergere daca este cazul, pentru audit"*. Sase informatii: **data + utilizator** pentru **creare**, **modificare** si **stergere**. Scrisa initial ca cerinta, cu cercetarea in curs. **Cercetarea TERMINATA**, raport: `docs\cercetare\audit_vanzari_creare_modificare_stergere.md`. **Verdict, in doua randuri:** patru din cele sase informatii cerute **exista deja** pe `VANZARI` si se scriu consecvent (`ID_UTIL`/`DATAORA` la creare, `ID_UTILS`/`DATAORAS` la stergere); **lipseste complet perechea de modificare** (nicio coloana, verificat cu filtru `%MODIF%` pe `all_tab_columns`), iar editarea prin stergere+reemitere (S9) ar **suprascrie tacit** perechea de creare cu utilizatorul si data regenerarii, daca nu se transporta explicit — acelasi risc deja rezolvat pentru `ID_FACT` (sectiunea „E. `ID_FACT`", linia 762). **Poveste noua, proiectata: S14** — verdict complet, recomandare si punctele ramase de decis cu Marius acolo. 64. **Asezarea campului de motiv: RANDUL SE IMPARTE IN TREI.** Formularea lui: *„campul de motiv imparte randul in 3"*. **Inchide** ultimul punct ramas deschis din decizia 57 (varianta D de asezare), reluat de decizia 61: randul de jos al formularului unificat are **trei sectiuni pe acelasi rand** — incasare, alte date si motivul discountului — nu coboara pe rand propriu, nu devine D cu doua etaje. La 1366 px inseamna ~440 px de sectiune, strans, si asumat ca atare. 65. **Auditul (decizia 63) se afiseaza in gridul din `frm_facturi`, nu in formularul facturii.** Formularea lui: *„auditul presupun coloanele dataora si utilizator adaugat/modificat/sters, le vreau pe grid-ul din formularul frm_facturi, nu in formularul facturii propriu-zise - este posibil sa fie deja coloane"*. Confirma continutul cerintei de audit (decizia 63): trei perechi **data + utilizator**, pentru **adaugat**, **modificat** si **sters**. Locul de afisare e **gridul din `frm_facturi`**, nu formularul unificat — deci **S14 nu cere controale noi in formularul unificat**, cere **coloane in gridul listei de facturi**. E o cerinta mai usoara decat presupunea S14, si e de spus explicit. **Sarcina de verificat la implementarea lui S14, adaugata ca prim pas al povestii:** Marius banuieste ca unele coloane exista deja in acel grid. **Nu s-a verificat.** S14 incepe deci cu **inventarul gridului din `frm_facturi`** — ce coloane de audit sunt deja acolo, ce surse au — si abia apoi adauga ce lipseste. Nu se presupune nici ca exista, nici ca nu. #### S6 — Test pe fluxul real, formularul unificat Headless (`COMUN\docs\depanare_testare_vfp.md`) + UI (`COMUN\docs\testare-ui-vfp.md`), cate un caz pe fiecare sursa: lista de preturi, comanda, contract, aviz, retur, transfer, valuta. Comparatie directa cu documentul emis pe calea veche: `vanzari`, `vanzari_detalii`, `act`, `rul`, totalurile denormalizate, listarea. *Depinde de:* S5. ### Etapa II — editarea prin regenerare #### S7 — Actiunea si garzile Actiune noua pe `frm_facturi`, sub tokenul de drepturi existent. Garzi refolosite din `COMUN\programe\ofacturare_editare.prg` (`EsteInEFactura:12`, plus luna inchisa, luna curenta, `ReferinteDocumenteNota`) — **nu se rescriu**, sunt deja extrase de #6. Plus garzile proprii regenerarii: refuz daca documentul are **urmasi**. **PRECIZAT in runda 14, pe cod** (`docs\cercetare\garda_aviz_facturat.md`): regula exista deja integral in Oracle, in **trei** garzi consecutive din `sterge_factura` — factura cu retururi (`TIP = 3`, `EXPORT:5452-5462`), **aviz cu factura sau aviz de retur** (`TIP IN (1,2)`, `EXPORT:5466-5476`), si factura din aviz ale carei avize-sursa au primit intre timp aviz de retur (`EXPORT:5480-5494`). **S7 nu reimplementeaza regula** — o citeste **mai devreme**, read-only, inainte de intrarea in formular: ```sql select count(*) from vanzari_coresp where sters = 0 and id_vanzare_aviz = :id_vanzare and tip in (1, 2, 3) ``` plus subinterogarea din garda 3 pentru cazul „factura din aviz". Se interogheaza `VANZARI_CORESP`, **nu `VANZARI.FACTURAT`** — `FACTURAT` e derivat si necitit de nicio garda. Garda din `sterge_factura` **ramane pe loc ca plasa de siguranta**: nu se muta, nu se slabeste. **Gol de acoperit, iesit tot in runda 14:** pentru **proforma** (`TIP = 4`, decizia 51) garda **nu exista deloc** — `sterge_proforma` (`EXPORT:5610-5635`) e o procedura separata fara nicio verificare, iar calea VFP iese din `do_sterge` inainte de restul (`ofacturare_comun.vc2:4707-4719`). Garda simetrica ceruta de S5c e **cod nou**, nu reutilizare. *Gata cand:* actiunea refuza corect si cu mesaj clar documentele trimise in eFactura, sterse, din luna inchisa, referentiate, sau care au urmasi (retur, factura din aviz, factura din proforma) — **inainte** de deschiderea formularului, nu ca `ORA-20000` la final. *Depinde de:* S6, si de inchiderea perimetrului cu #6 (fisier partajat). #### S8 — Incarcarea documentului in formular > **PROIECTAT INTEGRAL in runda 14 — `docs\cercetare\s8_incarcare_document.md` (52 KB, 8 sectiuni).** > **Schita de mai jos e corecta ca directie, dar gresita in piese, si nu marunt:** > 1. **`completeaza_setari_document(toFactura, .T.)` copiaza 16 proprietati din ~120** > (`COMUN\programe\ofacturare_comun.prg:361-411`) si **niciuna de identitate**. Lipsesc serie, > numar, data, scadenta, `text_aditional`, `id_facturare`, `id_ruta`, `tip_saft`, `efactura`, > `listare_detaliata`, `dataora_exp`, discountul de document, `discount_evidentiat`, `eproforma`, > valuta, cursul. > 2. **`cursor_retur_document(V_COPIERE = 1)` nu umple documentul, ci selectorul-sursa.** Rezultatul > intra in `crsarticole` (gridul din stanga); `crsfactura` se creeaza **gol si ramane gol** — > comentariul din cod e explicit (`COMUN\programe\ofacturare.prg:338`, `:455-457`). Transferul se > face doar prin `do_adauga_tot` → `do_adauga_articol`, care pentru articolele gestionabile trece > **prin dialogul de alegere din stoc**. > 3. **`cursor_retur_document` nu intoarce `ID_VANZARE_DET` si nici `TAXCODE`** > (`PACK_FACTURARE:3949-4054`). Fara `ID_VANZARE_DET`, cheia de linie presupusa de S8b **nu exista**, > iar ruta ieftina `modifica_explicatie_articol` **devine neapelabila** — primul ei parametru *este* > `V_ID_VANZARE_DET` (`PACK_FACTURARE:14511-14519`). > > **Recomandarea centrala:** S8 se construieste pe precedentul care face deja exact asta — > `relisteaza_ofacturare_stoc` (`COMUN\programe\ofacturare_stoc.prg:456-742`), care reconstituie > `poDate` + cursorul de linii dintr-un document salvat, prin `FACT_VFACTURI` + `FACT_VFACTURI_DETALII` > (ambele au `ID_VANZARE_DET` si `TAXCODE`). Canalul final e intrebarea 1 din §8.2 al raportului > (*recomandat:* procedura noua `cursor_editare_document`, ca sa nu se schimbe comportamentul copierii). > > **CORECTIE la raportul S8, facuta de sesiunea principala (runda 14):** raportul lasa deschis (N6, > intrebarea 7) daca „`frm_facturare_articole2` (varianta paralela)" intra in perimetru, si recomanda > „nu acum". **Premisa e gresita: `frm_facturare_articole2` NU e o varianta paralela de exclus — e > PROTOTIPUL pe care se construieste formularul unificat**, conform S1 („se porneste de la prototip", > `ofacturare.vc2:15741-19355`). Deci S8 il tinteste pe el, nu pe `frm_facturare_articole`. Faptul ca > are `do_adauga_tot` / `do_adauga_articol` proprii, cu logica divergenta (fara testul `llGestionabil`, > `ofacturare.vc2:17476`, `:17124`), **nu e un motiv de excludere, e exact driftul din 2017 pe care S2 > il inchide** — cele doua se unifica, nu se aleg. Din intrebarea 7 ramane deschisa **numai** partea de > tipuri 48/49. > **Cerinta noua din S8b e mai blanda decat se credea:** lookup-ul „ultimul delegat / ultima masina" > **are deja o garda** — `Empty(id_delegat) And Empty(id_masina)` (`ferestre_cere_date.vc2:3111`). > Riscul ramane real, dar **numai** pe documentele fara delegat si fara masina. Raportul da inventarul > complet al celorlalte initializari „pentru document nou" care trebuie sarite (§2.2). `completeaza_setari_document` pentru antet + `cursor_retur_document(V_COPIERE = 1)` pentru linii, **fara** degradarea de tip din `do_copiaza`. Se incarca in plus, fata de copiere: serie / numar / data / scadenta reale, discountul de document (`VANZARI.DISCOUNT`), `discount_evidentiat`, textul aditional, datele din `frm_alte_date` (delegat, auto, agent, adresa de facturare), explicatia si `taxcode` pe fiecare linie, si legatura cu sursa (`id_comanda`, `id_ctr`, lista de avize din `vanzari_coresp`) — ca reemiterea sa reconsume aceeasi sursa. **Campul de sursa exista deja** — nu e de adaugat, ci de pastrat vizibil: e `Ct_clb_altele`, cu eticheta schimbata pe tip de `do_schimba_explicatia` („Nr. contract” / „Nr. comanda” / „Nr. factura” / „Nr. facturi” / „Locatie”, `ofacturare.vc2:9633-9643`), eliminat azi din formular cand `gnScadereStoc = 0` si tipul e 1/5/10 fara copiere. La modificare e blocat: schimbarea sursei ar insemna alt document, nu o corectie. Formularul arata **identic** cu cel de introducere: fara banda de avertizare, fara coloane cu valorile initiale, fara panou de diferente (decizia 5). Se schimba titlul ferestrei si **butonul principal** (decizia 9, vezi S8c). *Gata cand:* formularul deschis pe un document existent arata exact documentul, pe fiecare tip de sursa, si nu se distinge vizual de formularul de introducere. *Depinde de:* S7. #### S8b — Rutarea scrierii dupa ce s-a schimbat Comasarea celor trei actiuni de modificare (G-bis): la confirmare se compara starea din formular cu cea incarcata si se alege ruta — `modifica_date_factura` pentru antet, `modifica_explicatie_articol` pentru explicatia liniei, regenerare pentru orice atinge sumele, nimic daca nu s-a schimbat nimic. Cele trei actiuni vechi de pe `frm_facturi` (`do_modifica`, `do_modifica_explicatie`) se retrag abia dupa ce formularul unificat le acopera, nu inainte. **PROIECTAT (runda 13) — `docs\cercetare\s8b_rutarea_scrierii.md`. Reteta G-bis e corecta ca directie, dar avea un gol nedocumentat, si el schimba forma solutiei.** - **Cele patru rute NU sunt teste independente, ci un lant cu prioritate — sumele primele.** Motivul e concret: **`modifica_date_factura` nu e singura ruta care scrie cei 14 parametri de antet.** **Sapte din 14** (`id_delegat`, `id_masina`, `id_facturare`, `listare_detaliata`, `dataora_exp`, `id_agent`, `text_aditional`) sunt scrisi **si** de calea normala de emitere, prin `scrie_factura2`, direct din `poDate` — **verificat direct de sesiunea principala** pe apelul de la `COMUN\clase\ofacturare.vc2:14345-14359`. Nu exista doi proprietari ai acestor campuri: e **acelasi obiect `poDate`** citit de ambele cai. - **Deci, cand regenerarea porneste, `modifica_date_factura` NU se mai cheama.** Inainte de regenerare ar scrie pe randul care urmeaza sa fie sters — pierdut. Dupa, ar fi a doua scriere pe aceleasi sapte coloane, pe alt `ID_VANZARE`. Regenerarea **este** deja calea de scriere a antetului cand sumele se schimba, nu o cale care trebuie compusa cu alta. - **Ordinea rutarii:** (1) s-au schimbat liniile / discountul de document? → **regenerare, gata**; (2) altfel, antet? → `modifica_date_factura`; (3) altfel, doar explicatie de linie? → `modifica_explicatie_articol`; (4) altfel → **nimic**. - **Explicatia de linie e transportata gratuit de regenerare**, prin parametrii nativi ai lui `adauga_articol_factura` (`V_EXPLICATIE`, `V_TAXCODE`) — **cu conditia** ca regenerarea sa citeasca din cursorul curent al formularului, nu dintr-un snapshot separat. - **Cel mai probabil fals pozitiv al criteriului „deschid si inchid → nicio scriere":** lookup-ul „ultimul delegat / masina al clientului" din `frm_alte_date.Init` (`COMUN\clase\ferestre_cere_date.vc2:3119-3136`). E cod gandit pentru **document nou**. Pe calea de editare ar suprascrie delegatul **incarcat corect din document** cu ultimul folosit de client — deci ori documentul apare „modificat" fara ca nimeni sa fi atins nimic, ori, daca ruleaza inainte de snapshot, **salveaza tacit delegatul gresit**. **S8 trebuie sa-l sara explicit pe calea de editare.** - **Comparatia se face pe valori rotunjite la precizia de scriere** (`gnPc` / `gnPPretV` / `gnPCant`), nu pe valoarea binara — altfel rotunjirea singura produce diferente. **De decis de Marius:** 1. **Editarea multipla** — `do_modifica` de azi lucreaza pe multi-selectie, capacitate fara echivalent in formularul unificat. Se accepta pierderea ei la retragere, sau `do_modifica` ramane activ separat exact pentru cazul asta? **Doua goluri de inchis inainte de implementare (semnalate, nu presupuse):** - **Ce canal scrie `serie_act` / `numar_act` / `data_act` / `data_scad` / `id_ruta` / `tip_saft` / `efactura`** pe documentul reemis — **nu apar in `scrie_factura2`**, deci vin pe alt drum. Raspunsul decide daca mai e nevoie de o scriere separata dupa regenerare. **Se inchide in S9.** - ~~**`cursor_retur_document` re-deriva pretul la incarcare?**~~ **INCHIS (runda 13): NU** — pretul vine din `VANZARI_DETALII.PRET` stocat, fara `JOIN` catre contract sau politici (`PACK_FACTURARE:3960-4062`, verificat direct). **Mecanismul de detectie e valid.** Detaliul despre rotunjire si `DIFERENTA` — in S10. *Gata cand:* fiecare din cele patru situatii din tabelul G-bis produce exact scrierile din tabel si nimic in plus; in special, deschiderea si inchiderea fara modificari nu scrie nimic. *Depinde de:* S8. #### S8c — Butonul comutator de antet si salvarea separata a antetului Decizia 9, proiectata in I. Antetul unui document existent porneste **blocat**; un singur `but_modifica` il deschide si isi schimba imaginea in discheta de salvare; a doua apasare cheama `modifica_date_factura` si **nu atinge articolele** — exact ce face azi `do_modifica`, dar din formularul unificat, dupa care antetul se blocheaza la loc si butonul revine la creion. **Fara nicio bifa**: butonul deschide tot antetul, inclusiv serie / numar / data / scadenta; cele patru bife de azi nu se transpun. `Termina` ramane salvarea intregului document. Se generalizeaza reteta de blocare din `clb_tx_data.dezactiveaza()` / `reactiveaza()` (`lb_tx.vc2:521-538`) la containerele de antet care azi nu au niciuna (I), si se aplica **uniform** tuturor campurilor de antet — asta e ce face posibila eliminarea bifelor. Comutarea imaginii butonului se copiaza din `frm_rulaje.se_modifica_assign` (`rulaje.vc2:4716-4773`): o singura instanta `but_modifica`, cu `.cpicturedown` / `.cpictureup` / `.Picture` rescrise impreuna si `.Refresh()` — vezi decizia 9 pentru capcana hover-ului. **Ce deschide efectiv butonul (decizia 25, verificat pe cod in G-bis).** „Tot antetul” nu inseamna ca totul se salveaza pe aceeasi ruta: - **cei 14 parametri** — pe loc, prin `modifica_date_factura`. Asta livreaza S8c; - **grupul B** (tip document, valuta, zi curs, client, sursa, gestiune sursa, politica de preturi) si **grupul C** (incasare) — **nu au ruta de scriere pe loc**, deci raman **blocate in etapa I**, cu explicatie la hover; se deschid in etapa II, cand modificarea lor marcheaza documentul pentru regenerare; - **grupul A** (analiticele) — **read-only permanent in #13** (decizia 26), editarea e a lui #6. Deci in etapa I butonul deschide **exact cei 14**, si asta nu e o abatere de la decizia 9, ci consecinta ei corecta pe starea de azi a codului: restul campurilor n-ar avea unde sa se scrie. *Gata cand:* se poate corecta delegatul unui document emis, salva antetul si inchide formularul, **fara nicio scriere in `VANZARI_DETALII` si fara recalcul de sume in note sau rulaje**; se poate corecta si data scadentei prin acelasi buton, fara alt control intermediar; iar un document deschis si inchis fara a apasa butonul nu produce nicio scriere. *Atentie la formularea criteriului:* „nicio scriere in note si rulaje” ar fi un criteriu imposibil — schimbarea seriei / numarului / datei propaga coloanele de identitate in `JV2007` si `RUL` prin procedura insasi (I-bis). Ce se verifica e ca **nu se ating sumele**, nu ca nu se atinge tabelul. *Depinde de:* S8. #### S9 — Stergerea si reemiterea intr-o singura tranzactie, cu `ID_FACT` pastrat Partea grea. Doua lucruri simultan: **Constrangere de la decizia 35, inainte de orice:** reemiterea **scrie prin `pack_facturare`**, pe acelasi drum ca emiterea (`scrie_factura2` → `contabilizeaza_articol`, cu parametrul de cont de la decizia 34). `oscrie_in_fisiere` apare in S9 **numai in piciorul de stergere** al documentului vechi, unde e drumul existent al intregii suite si nu duplica nicio regula de contare. **Nu se scrie si nu se corecteaza niciun rand din `ACT_TEMP` direct din VFP.** Consecinta de secventiere: **S9 nu poate porni inaintea parametrului deciziei 34** — nu mai exista varianta „editarea nu cere nimic in pachet". **VERIFICAT ca decizia 35 e realizabila** (`docs\cercetare\parametru_cont_contabilizeaza_articol.md`, sectiunea 1): pe partea Oracle, **a doua emitere a aceluiasi document merge pe cod identic**. `scrie_factura2` cheama `initializeaza_date_factura` (`PACK_FACTURARE:1808-1846`) la fiecare apel, iar aceasta face `DELETE FROM VANZARI_DETALII_TEMP` (`:1835`) si `nid_act := 0` (`:1836`) — contorul folosit de `scrie_nota` / `scrie_discount` / `scrie_tva` la fiecare `INSERT INTO ACT_TEMP` **porneste curat si la reemitere**. Nimic din `contabilizeaza_articol`, ramura noua inclusa, nu citeste vreo stare care sa presupuna „documentul e nou": variabilele de sesiune sunt **citite**, nu comparate cu o stare anterioara. **Singurul obstacol real al regenerarii e in alt pachet** — `PACK_CONTAFIN`, prin `SET_IDFACT` si `PK_DOCUMENTE`, exact ce e deja analizat mai jos in acest S9. Adica: „cod separat" inseamna **alta procedura, in alt pachet**, nu o a doua ruta de contare — deci **decizia 35 nu e contrazisa**. - **Tranzactia.** Apelul de stergere (`oscrie_in_fisiere` + `finalizeaza_stergere_nota` + `sterge_factura`) se muta in interiorul tranzactiei deschise de `do_scrie_articole`, inaintea primului `adauga_articol_factura`. Fara `commit` intermediar (`VANZARI_DETALII_TEMP` e GTT `ON COMMIT DELETE ROWS`). La orice eroare, rollback total. - **Tripleta de mai sus e si ce face ciclul NEUTRU fata de stoc — nu se simplifica.** `sterge_factura` **nu** reverseaza rulajele. Revenirea stocului vine din `PACK_CONTAFIN.STERGE_DIN_RUL` (`UPDATE RUL SET STERS = 1, ID_UTILS = ..., DATAORAS = ...`, `ff_2026_07_29_03_COMUN_PACK_CONTAFIN.sql:1867-1882`, apelata la `:8473`), la care se ajunge prin `finalizeaza_scriere_act_rul(tnScrieSterge = 2)` ← `oscrie_in_fisiere(2, ...)`. Adica **stocul e derivat** din miscari nesterse, iar marcarea lor `STERS = 1` inchide subiectul — dar **numai daca piciorul de stergere chiar trece prin `oscrie_in_fisiere`**. O implementare care „simplifica" pasul la un apel direct de `sterge_factura` ar lasa rulajele in picioare si reemiterea ar descarca gestiunea **a doua oara**, tacut. Verificat pe sursa pachetului. `docs\cercetare\stoc_la_stergere_si_reemitere.md`. - **`ID_FACT`.** Se citeste inainte de stergere (vezi capcana `STERS = 0` din E) si se impune documentului nou, printr-un comutator folosit **numai** de regenerare. `SET_IDFACT` nu se schimba neconditionat — e cod comun intregii suite. - **`ID_UTIL` / `DATAORA` (audit de creare, S14).** Acelasi tipar ca la `ID_FACT`: se citesc din documentul vechi **inainte** de stergere si se impun explicit documentului nou — altfel `INSERT INTO VANZARI` de la reemitere le rescrie cu utilizatorul si momentul curente, si „data/utilizatorul crearii" se pierde tacut. Detaliu si recomandare de coloane: S14. **VERIFICAT — se poate, dar cere TREI schimbari simultane, nu una.** Raport: `docs\cercetare\idfact_refolosire_si_documente.md`. Verdict: **DA, cu conditii.** 1. **Varianta comentata din `SET_IDFACT` NU rezolva S9** — nu se reactiveaza. (`PACK_CONTAFIN.pck:3016-3035`) cauta documentul pe `NRACT + SERIE_ACT + DATAACT + ID_CTR` cu `STERS = 0`; dupa soft-delete `STERS` e deja 1, deci **nu-l gaseste** si cade tot pe secventa. In plus e vulnerabila la coliziuni cu un document viitor neinrudit care are aceleasi patru campuri. `ID_FACT`-ul se **citeste explicit inainte de stergere** (VFP il are in cursor) si se **transmite**. 2. **`SET_IDFACT` are nevoie de o cale de intrare noua, inerta implicit.** Azi **nu exista niciun canal**: `pack_contafin.nIdFact` e scrisa neconditionat de fiecare apel, si nu exista alt global sau parametru de sesiune. Solutia cu suprafata minima: **variabila noua de pachet** („ID_FACT fortat", implicit `NULL`), citita **doar in corpul activ** al lui `SET_IDFACT`, consumata si resetata la prima folosire. **Nu se atinge semnatura `SET_IDFACT(V_GCS)`** — altfel s-ar schimba apelul pentru toti. 3. **Blocajul real nu e in `SET_IDFACT`, ci in `DOCUMENTE`** — si asta e descoperirea care schimba estimarea. Scrierea de azi (`PACK_CONTAFIN.pck:796-817`) e un **`INSERT` simplu** pe `ID_DOC`, care e **PRIMARY KEY** (`PK_DOCUMENTE`, `fn_script.sql:5192-5198`). Stergerea (`STERGE_DIN_ACT:1835-1855`) e **soft-delete pur** — randul vechi ramane fizic in tabel cu `STERS = 1`. Deci reemiterea cu acelasi `ID_FACT`, cu codul de azi, **arunca `ORA-00001`**. Dovada, nu presupunere. `MERGE`-ul deja comentat (`:818-847`) **nu ajuta**: are doar `WHEN NOT MATCHED`, deci ar ignora tacit randul existent si documentul reemis ar ramane cu `STERS = 1`. E nevoie de **upsert real**, cu `WHEN MATCHED THEN UPDATE SET STERS = 0, ...` pe restul coloanelor de identitate. **Succesiunea corecta**, in aceeasi tranzactie: citeste `ID_FACT` **si lista sursa** -> sterge documentul vechi (soft-delete, ca azi) -> seteaza variabila fortata -> scrie documentul nou pe drumul obisnuit -> **remigreaza atasamentele** -> commit doar daca toate au reusit; orice eroare -> rollback total, documentul vechi intact. **DOI PASI ADAUGATI DE S11 (runda 13) — niciunul nu „vine gratis" din drumul normal:** 1. **Citirea listei sursa INAINTE de stergere, si refurnizarea ei la reemitere.** `VANZARI_CORESP` si `marcheaza_facturat` **se rescriu singure** prin `CASE`-ul pe `ntip` din `finalizeaza_factura` — dar **numai daca** `ntip` si lista sursa (`clistaid` / `clistaid_avize`) sunt refurnizate. Ele traiesc in randurile `VANZARI_CORESP` ale documentului **vechi**, care dupa stergere nu mai sunt disponibile. **Fara acest pas nu apare nicio eroare — corespondenta si `FACTURAT` pur si simplu nu se scriu.** Scriere lipsa silentioasa, exact tipul de defect care trece de o verificare vizuala. 2. ~~**`UPDATE ATASAMENTE_VANZARI SET id_vanzare = :nou`**~~ — **INLOCUIT de decizia 53 (runda 14): atasamentele NU se remigreaza, se sterg la reemitere.** Documentul reemis porneste curat; PDF-ul se regenereaza la prima listare. Pasul ramane **cod nou**, dar e o **stergere**, nu un `UPDATE` (`STERS = 1`, ca sa ramana consistent cu view-ul `VATASAMENTE_VANZARI`, care filtreaza `b.sters = 0` — vezi S11). Consecinta acceptata explicit de Marius: **urma PDF-ului efectiv trimis clientului dispare din sistem.** **Suprafata de risc pentru suita**, masurata: **~25-30 cai de apel** — cate o copie a lui `COMUN\programe\oscrie_in_fisiere.prg` in fiecare produs ROA, plus trei variante care cheama `SCRIE_IN_ACT` direct. Toate converg in acelasi punct, `SET_IDFACT(V_GCS)`. **Garantia e structurala, nu prin inspectie:** cu variabila implicit `NULL` si citita strict local, toate cele ~25-30 de cai raman identice cu azi — nu trebuie verificat fiecare apelant. **Dar `INSERT` -> `MERGE` in `DOCUMENTE` e cod comun apelat de toata suita.** Practic ramura noua e inerta (secventa e monoton crescatoare, `WHEN MATCHED` nu se declanseaza niciodata pe drumul normal), **dar se testeaza un ciclu normal de scriere in fiecare produs care scrie facturi sau note**, nu doar in ROAFACTURARE. *Gata cand:* o editare care mareste cantitatea peste stocul curent trece (stocul documentului initial e eliberat inainte); documentul reemis are **acelasi `ID_FACT`, serie, numar si data** ca inainte; **randul din `DOCUMENTE` e acelasi, revenit la `STERS = 0`**, nu unul nou; o eroare la scriere lasa documentul initial intact; si un ciclu normal de scriere ramane neschimbat in celelalte produse. *Depinde de:* S8b. #### S10 — Pretul care nu trebuie re-derivat Inventarul ramurilor din `adauga_articol_factura` (H) si, unde e nevoie, un mod in care preturile vin din formular. **VERIFICAT (runda 9) — `docs\cercetare\s10_pret_rederivat.md`. Verificarea 3 e inchisa.** **Exista exact o ramura care suprascrie pretul: contractul.** Cu `OPT_FACTURARE = 3` (sau `NULL`, care se implicit-eaza tot la 3), daca `CTR_ARTICOLE.PRET_UNITAR` e diferit de 0, acea valoare **inlocuieste** pretul trimis de VFP — `DECODE(A.PRET_UNITAR, 0, V_PRET_TEMP, A.PRET_UNITAR)` — necondiționat de faptul ca linia a fost sau nu atinsa pe ecran. Procedura **nu are nicio notiune de „reemitere"**: la fiecare apel cu acelasi `V_ID_CTR` se reface derivarea din contract. Pe celelalte ramuri (implicita, restaurant) pretul trimis de VFP e respectat ca atare. **Nu exista niciun parametru sau flag care sa opreasca re-derivarea** — deci S10 nu poate „cere pachetului sa nu re-derive", trebuie sa lucreze in jurul ramurii de contract. Riscul se materializeaza doar cand pretul din contract s-a schimbat intre emitere si reemitere; pana atunci rezultatul e identic si nimeni nu observa. Detaliile pe discount, TVA si curs sunt in sectiunile 6 si 7 ale raportului. **PROIECTAT (runda 13) — `docs\cercetare\s10_pret_contract_reemitere.md`. Faptul portant al rundei 9 se reconfirma la sursa, dar problema e mai larga decat „pretul".** - **Nu e doar pretul: acelasi `SELECT` de pe ramura de contract alimenteaza CINCI valori.** **Verificat direct de sesiunea principala** (`PACK_FACTURARE:5149-5166`): `DECODE(A.PRET_UNITAR, 0, V_PRET_TEMP, A.PRET_UNITAR)` **impreuna cu** `B.PROC_TVAV`, `B.ID_VALUTA`, `A.PRET_CU_TVA` si `C.IN_STOC` intra in aceeasi interogare. Raportul semnalase trei (pret, TVA, valuta); sunt **cinci** — se adauga flagul „preturi cu TVA" si **gestionabilitatea**, care vine din **nomenclatorul curent**, nu din document. **Orice garda trebuie sa acopere tot setul**, nu doar pretul. - **Nu exista alt loc care suprascrie pretul mai departe in lant** — `scrie_in_vanzari` copiaza `PRET` neschimbat din `VANZARI_DETALII_TEMP` in `VANZARI_DETALII`. - **Divergenta e reala, nu ipotetica:** pe baza de dezvoltare, **3 din 11** linii deja facturate pe contract au azi alt pret in contract decat cel facturat. Esantionul e mic si **nu se extrapoleaza** la productie — dar demonstreaza ca situatia se intampla. - **Discountul ramane passthrough** — acolo nu e nicio problema. - **Recomandarea raportului: varianta (c) — se avertizeaza si se cere confirmare, cu diferenta afisata.** Satisface literal criteriul „decis explicit, nu accidental", se implementeaza **integral in VFP**, fara sa se atinga `pack_facturare`. Variantele: (a) accepta tacit — respinsa, e exact ce cere criteriul sa nu se intample; (b) blocheaza — mai sigura, dar frustranta daca divergentele sunt dese; (d) ocoleste ramura trimitand `V_ID_CTR = NULL` — **respinsa motivat**, pierde **permanent** legatura `VANZARI_DETALII.ID_CTR`. - **„Reemitere identica" nu e garantata uniform:** pe ramurile `ELSE` / restaurant, da; pe **comenzi** esueaza **tare** (eroare, deci vizibila); pe **contract** si pe **aviz (`ntip = 4`)** esueaza **tacit**. Riscul pe aviz **nu fusese semnalat pana acum** ca decizie explicita. **GOLUL LUI S8b E INCHIS — `cursor_retur_document` NU re-deriva pretul.** Verificat direct de sesiunea principala (`PACK_FACTURARE:3960-4062`): pretul vine din **`VANZARI_DETALII.PRET`** stocat, printr-un `FROM VANZARI_DETALII A1` fara niciun `JOIN` catre `CTR_ARTICOLE` sau catre politici; `PROC_TVAV` la fel, din document. **Deci mecanismul de detectare a schimbarii din S8b e valid** — un document deschis nu apare „modificat" din cauza citirii. **Confirmat independent si pe partea VFP** (raportul S10): in `ofacturare.prg:266-283`, ramura `Case m.llCopiere` are **prioritate indiferent de tip** — pentru un document de contract (`2,6,26,52`) **nu se ajunge deloc** la `cursor_contract` la incarcare. Citirea si scrierea sunt guvernate de **cursoare complet diferite, fara cod comun**: riscul de suprascriere tacita apare **strict la re-scriere**, niciodata la deschidere. O garda pusa la scriere nu afecteaza citirea si nu e afectata de ea. > **Dar citirea nu e o copie curata, si asta conteaza pentru S12.** Pretul returnat e > `ROUND(A.PRET, nzecimale_pretv) + A.DIFERENTA` (pe valuta: `ROUND(CURS * ROUND(PRET, ...) / > MULTIPLICATOR, ...) + DIFERENTA`). Adica **rotunjit la precizia de sesiune si cu `DIFERENTA` pliata > inauntru**. La reemitere se trimite inapoi valoarea pliata, deci **impartirea `PRET` / `DIFERENTA` se > reface**, nu se conserva. De verificat in S12 ca o reemitere identica reproduce **suma**, chiar daca > nu reproduce neaparat aceeasi impartire — si ca `DIFERENTA` nu se acumuleaza la reemiteri repetate. ### RASTURNAT de decizia 54 (Marius, runda 14) — nu garda, ci eliminarea re-derivarii Cele trei intrebari de mai sus (blocare vs. avertizare; ramura de aviz; masurarea frecventei) **sunt inchise, si nu prin alegerea uneia dintre variante — prin respingerea premisei lor.** Formularea lui Marius: *„nu inteleg de ce se schimba valorile fata de ce se incarca din factura sursa care se modifica? asta este comportamentul pe care il doresc — modific sursa"*. **Deci:** la reemitere se scriu **valorile din formular** (incarcate din documentul editat), nu se reciteste sursa. Nu se cere utilizatorului sa confirme o schimbare pe care n-a cerut-o. Variantele (b) si (c) **cad amandoua**; ramane obiectivul (a-invers): re-derivarea, unde exista pe calea de reemitere, se **elimina**. **Si aici e problema reala, spusa pe fata:** runda 9 a stabilit deja ca **„nu exista niciun parametru sau flag care sa opreasca re-derivarea"**, iar singura varianta care ocolea ramura — (d), `V_ID_CTR = NULL` — a fost **respinsa motivat**, pentru ca pierde permanent legatura `VANZARI_DETALII.ID_CTR`. Prin urmare decizia 54 **cere, cel mai probabil, o modificare in `pack_facturare`** (un semnal „nu re-deriva, foloseste ce ti-am trimis" pe `adauga_articol_factura`), nu o solutie curat VFP. Asta atinge un pachet din `COMUN`, partajat de toata suita — **iar modificarile de pachet se aplica tuturor deodata, fara cale de intoarcere prin alegerea de la decizia 49.** Trebuie proiectata ca inerta pentru apelantii de azi (implicit = comportamentul actual), exact ca la S4g. **VERIFICAT in runda 14 — `docs\cercetare\s10_rederivare_pe_calea_reemiterii.md`.** Confirmat pe fisier **si** pe DB (`all_source`, corp VALID, `last_ddl_time = 2026-08-09 20:03:50`). **Se pierd intr-un SINGUR loc:** `adauga_articol_factura`, in `CASE`-ul de la `PF:5052-5220`, adica **inainte** de `INSERT INTO VANZARI_DETALII_TEMP` (`PF:5222`). Tot ce urmeaza — copierea temp → `VANZARI_DETALII` (`PF:13705-13757`) — e **1:1**, deci „copiaza `PRET` neschimbat" e adevarat dar **nu e o protectie**: dauna e amonte de temp. **RAMURILE CARE RE-DERIVA SUNT TREI, NU UNA. Raportul rundei 9 gresea.** | valoare | **contract** (2/6/26/52) | **aviz** (`ntip = 4`) | **comenzi** (3/21/28/42/47) | restaurant (45) | `ELSE` | |---|---|---|---|---|---| | `PRET` | re-derivat din `CTR_ARTICOLE.PRET_UNITAR` daca `<> 0`; temp daca `= 0`; **NULL daca e NULL** (`PF:5149`) | **re-derivat neconditionat** din avizul sursa (`PF:5082`) | temp de facto — `WHERE A.PRET = V_PRET_TEMP` (`PF:5077`) | temp | temp | | `PROC_TVAV` | re-derivat din `CRM_POLITICI_PRET_ART` | re-derivat din aviz | re-derivat din `COMENZI_ELEMENTE.PTVA` | derivat din `JTVA_COLOANE` | derivat din `JTVA_COLOANE` pe `ID_JTVA_COLOANA` **din formular** | | `ID_VALUTA` | re-derivat | re-derivat | re-derivat | temp | temp | | `PRET_CU_TVA` | re-derivat | re-derivat | re-derivat | temp | temp | | `IN_STOC` | re-derivat din `NOM_ARTICOLE` | re-derivat | re-derivat | temp | temp | - **Ramura AVIZ e cea mai agresiva din tot `CASE`-ul** (`PF:5080-5103`): `PRET` se inlocuieste **neconditionat**, nu exista nici `DECODE`, nici `AND A.PRET = V_PRET_TEMP`, **si nu exista bloc `EXCEPTION`**. Un pret modificat pe ecran e inlocuit tacit. In plus **nu filtreaza `A.STERS = 0`**, desi coloana exista — linii sterse ale avizului sursa pot fi citite. Si depinde de `clistaid`, care la reemitere **trebuie repopulata** (leaga direct de pasul 1 adaugat in S9). - **Ramura comenzi esueaza tare, nu tacit** — fara `EXCEPTION`, o linie care nu se mai potriveste da **ORA-01403**, vizibila. Mai putin periculoasa, dar tot re-derivare. - **Ramura contract are o portita** — `EXCEPTION WHEN NO_DATA_FOUND` (`PF:5167-5185`) pune **toate cele cinci** pe valorile din formular. **Comportamentul cerut de decizia 54 exista deja in cod**, dar numai pe calea de exceptie. Aviz si comenzi **nu au** asa ceva. - **Capcana NULL, dedusa din semantica `DECODE`, nerulata:** daca `CTR_ARTICOLE.PRET_UNITAR` e `NULL` (nu `0`), `DECODE` nu potriveste literalul `0` → rezultatul e **NULL**, deci pretul din formular se pierde complet si linia intra cu `PRET` gol. In dev cazul nu apare (27 randuri, 0 NULL) — **ceea ce nu spune nimic despre productie**. - **`PROC_TVAV` nu vine din formular pe NICIO ramura** — nu e parametru al procedurii. Pe calea „buna" se deriva din `JTVA_COLOANE` pe `ID_JTVA_COLOANA`-ul trimis de formular, deci reproduce documentul **doar cat timp cota nu s-a schimbat**. La o modificare legala de cota, reemiterea schimba TVA-ul **chiar si pe `ELSE`**. - **`IN_STOC` nu ajunge in `VANZARI_DETALII`** — coloana nu exista (verificat pe DB). Traieste doar in temp, unde decide **descarcarea de gestiune**. - **Dupa insertul in temp nimic nu mai suprascrie cele cinci valori.** `adauga_diferente_pret` atinge numai `DIFERENTA`, `scrie_factura_avize` numai `CANTITATE`, `scrie_seturi` numai `ID_VANZARE_SET`. ### Cum se implementeaza decizia 54 **Curat VFP nu se poate — confirmat cu dovada pozitiva, nu prin absenta.** Semnatura n-are comutator (`PF:4989-5015`, 27 de parametri, toti date de linie); globalele pachetului n-au (`PF:126-212`); poarta `CASE`-ului se decide pe `ntip` (care ajunge in `VANZARI.TIP`, deci nu se poate falsifica) si pe `CONTRACTE.OPT_FACTURARE` (date de contract, nu parametru). Singurele parghii VFP care ar devia pe `NO_DATA_FOUND` sunt `V_ID_CTR = NULL` — **deja respinsa** — si `V_ID_POL = NULL`, care are **exact acelasi defect pe alta coloana** (`PF:5258` → `temp.ID_POL` → `PF:13737` → `VANZARI_DETALII.ID_POL`) si in plus ar rupe ramura aviz, care potriveste pe `A.ID_POL`. **Notata aici tocmai ca sa nu fie redescoperita ca „solutie" intr-o runda urmatoare.** **Deci decizia 54 cere obligatoriu modificare in `pack_facturare`** — dar mica, si care nu adauga comportament nou: - **Semnalul:** variabila noua de pachet („regenerare in curs"), implicit `0` = comportamentul de azi, scrisa de VFP **dupa** `initializeaza_date_factura` (`ofacturare.vc2:13981`) si inainte de bucla de `adauga_articol_factura`. *Recomandata* fata de un parametru `DEFAULT 0`, din trei motive: se potriveste cu stilul pachetului (deja o punga de stare de sesiune); se scrie o data pe document, nu o data pe linie, in toate produsele; si **punctul de resetare exista deja** — `initializeaza_date_factura` (`PF:1808-1917`) reinitializeaza ~40 de globale si goleste temp (`PF:1835`), deci flag-ul nu poate scapa in documentul urmator. - **Ce face:** un `WHEN` nou, **primul** in `CASE`-ul de la `PF:5052`, cu **acelasi corp ca `ELSE`-ul de azi** (`PF:5189-5203`). Nimic inventat — se forteaza ramura care exista deja. **Acopera dintr-o data toate trei ramurile problematice**, fiind in acelasi `CASE`. `INSERT`-ul de la `PF:5222` ramane neatins. - **Inert prin constructie** pentru toti apelantii de azi, exact ca la S4g. **Consecinta de livrare: DB inainte de EXE.** ### Trei consecinte de acceptat explicit, nu de descoperit la S12 1. **`IN_STOC` ar veni din formular.** Nu murdareste documentul (coloana nu exista), dar schimba **comportamentul de stoc** al reemiterii. Ca reemiterea sa fie identica, **S8 trebuie sa incarce valoarea cu care s-a scris documentul initial** — si **azi nu o incarca**: loader-ul lui #6 o citeste din nomenclatorul curent (`ofacturare_editare.prg:302-303`). **Flag-ul singur nu rezolva asta.** 2. **Se pierde o validare pe ramura comenzi.** Azi, `A.PRET = V_PRET_TEMP` + lipsa lui `EXCEPTION` fac ca o linie care nu se mai potriveste cu comanda sa **opreasca** scrierea. Cu flag-ul pornit, reemiterea unei facturi din comanda nu mai verifica linia fata de comanda. Decizia 54 pare sa vrea exact asta, dar e schimbare de comportament — **se declara, nu se descopera**. 3. **`PROC_TVAV` ramane derivat, nu preluat.** Reproducerea exacta cere ca S8 sa pastreze `ID_JTVA_COLOANA` **si** ca `JTVA_COLOANE` sa nu se fi schimbat. Daca se cere reproducere exacta si peste o modificare legala de cota, `PROC_TVAV` trebuie sa devina **parametru** — schimbare mai mare decat flag-ul, **de decis separat**. ### Obstacol pentru S9, gasit in treacat **`VVANZARI_ARTICOLE` nu expune `ID_POL` si nu expune `ID_CTR`** (lista de coloane interogata pe DB) — exact cei doi pe care `adauga_articol_factura` ii cere (`V_ID_POL`, `V_ID_CTR`) si pe care `VANZARI_DETALII` ii pastreaza. **Reemiterea nu poate folosi acest view ca atare**: ori se completeaza view-ul, ori loader-ul citeste direct din `VANZARI_DETALII`. Se leaga de intrebarea 1 din S8 (canalul de citire a liniilor) — **un argument in plus pentru varianta (B), procedura noua.** *Gata cand:* pentru fiecare tip, documentul reemis are **exact valorile din formular** — pret, TVA, valuta, flag „preturi cu TVA" — iar o reemitere fara modificari reproduce documentul identic, inclusiv dupa ce pretul din contract s-a schimbat intre timp. *Depinde de:* S9. #### S11 — Legaturile care raman pe `ID_VANZARE` `ID_FACT`, seria, numarul si data se pastreaza (E, F), deci legaturile prin `ID_FACT` sunt in regula. Ramane discontinuitatea pe **`ID_VANZARE`**, care se schimba: `ATASAMENTE_VANZARI`, `marcheaza_facturat`, `vanzari_coresp`, si eventualele referinte de incasari / plati. De verificat si daca `DOCUMENTE` primeste un al doilea rand pe acelasi `ID_DOC` sau il refoloseste. **PROIECTAT (runda 13) — `docs\cercetare\s11_legaturi_id_vanzare.md`. Inventarul s-a facut prin FK-urile reale din Oracle, nu prin grep — si asta a scos doi consumatori pe care nicio cautare de text nu-i gasise.** - **Premisa centrala, corectata: mecanismul lui #6 NU se mosteneste de S9.** La #6, documentul isi **pastreaza randul** din `VANZARI` — `actualizeaza_vanzari` face `UPDATE ... SET COD = nou, STERS = 0`, deci **`ID_VANZARE` nu se schimba**, doar `COD`, si atasamentele se realiniaza in acelasi gest. La S9, reemiterea merge pe drumul normal de emitere (`INSERT ... RETURNING ID_VANZARE`), deci **rand nou, cheie noua, si `actualizeaza_vanzari` nu se declanseaza deloc.** Tot ce e protejat azi tacit de #6 e **expus** la regenerare. Asta nu era in plan. - **Sapte tabele au FK declarat pe `VANZARI.ID_VANZARE`. Doua nu erau in nicio lista:** **`REST_NOTE_PLATA`** (modul restaurant) si **`IPS_VOYAGES_VANZARI`** (modul specific unui client). Clasificate **(A) suspecte** — pachetele lor PL/SQL n-au fost citite integral. **INCHIS de Marius, runda 14 — riscul dispare, si nu prin presupunere.** Intrebat direct, a raspuns: *„daca este vorba despre facturarea voiajelor din ROAACNPRO, acolo nu merge modificarea de genul acesta, ci doar editarea de la #6"*. **Documentele acestor module sunt IN AFARA domeniului regenerabil al #13** — pentru ele ramane calea #6 (modificare pe loc). Pachetele lor **nu mai trebuie citite**. Doua consecinte de dus mai departe: **(a) garda lui S7 trebuie sa le refuze EXPLICIT**, nu doar sa nu le trateze — altfel „in afara domeniului" e o intentie, nu o garantie; **(b)** conditia se declara in S13, la actualizarea `tipuri_documente_facturare.md`. - **`ATASAMENTE_VANZARI` — corectie de premisa, in favoarea noastra.** **Nu** sunt documente incarcate de utilizator: sunt **PDF-uri auto-generate la listare** (`poDate.scrieAtasamente()` dupa `export2pdf`; cursorul nu vine din niciun dialog de fisier — cautat explicit, zero potriviri). Deci **nu e pierdere ireparabila**, cum presupusese briefingul. Se rup totusi real: view-ul `VATASAMENTE_VANZARI` filtreaza pe `b.sters = 0`, deci dupa regenerare atasamentele vechi **dispar tacit** din toate interogarile (inclusiv din ROAGEST / ROAIMOB), desi BLOB-ul ramane fizic. ~~**(A) — remigrare printr-un simplu `UPDATE ... SET id_vanzare = :nou`**~~ — **INCHIS altfel, decizia 53 (runda 14): se STERG la reemitere.** Marius a ales a treia varianta, nici remigrare, nici marcaj „versiune inlocuita". Documentul reemis porneste curat. **Consecinta i-a fost spusa explicit si a acceptat-o:** motivatia care sustinea remigrarea — *pastrarea urmei PDF-ului trimis clientului* — **cade odata cu ea**; dupa editare nu mai exista in sistem dovada a ce a primit clientul. Punctul 15 se inchide aici. - **Incasarile si platile nu sunt afectate — si dovada e structurala, nu statistica:** niciun tabel de incasari/plati n-are FK pe `ID_VANZARE`, si nici referinta text. Se leaga prin **`ID_FACT`**, care se pastreaza. **(C)**, cu dovada dubla. - **eFactura, `DOCUMENTE`, listarile, `VANZARI_CANTITATI` — (C)**, toate cheiate pe `ID_FACT` sau rescrise automat. **De decis de Marius:** 1. **Editarea unui document care are deja retur emis pe el e azi BLOCATA, nu doar riscanta** — `sterge_factura` arunca `ORA-20000`. Garda e corecta (protejeaza lantul), iar **S7 planifica deja sa semnaleze conditia inainte de intrarea in formular**, nu ca eroare Oracle la final. Ce ramane de decis: se accepta ca **limitare declarata** (utilizatorul sterge intai documentele-copil), sau se vrea alt comportament? 2. **Atasamentul vechi arata continutul dinainte de editare.** Remigrat pe documentul nou, factura editata ajunge sa aiba atasat PDF-ul **vechi**. De decis daca asta e exact ce se vrea (audit trail), sau daca vechiul ar trebui marcat cumva ca „versiune inlocuita". 3. **`REST_NOTE_PLATA` / `IPS_VOYAGES_VANZARI`** — de confirmat ca tipurile lor sunt in afara domeniului regenerabil. *Gata cand:* documentul reemis se regaseste corect in borderoul eFactura, in listari, in atasamente si in rapoartele care il cauta dupa numar. *Depinde de:* S9. #### S12 — Test pe fluxul real, regenerarea Cate un caz pe fiecare sursa, rulat de doua ori (o data fara modificari — reemitere identica, o data cu modificari de cantitate, pret si linii adaugate / sterse). Se verifica: documentul initial `sters = 1` cu nota si rulajele lui, documentul nou corect, sursa eliberata **si** reconsumata (comanda redevine facturabila si redevine facturata), stocul corect dupa ciclu, totalurile denormalizate din `VANZARI`, si ca o reemitere identica produce **exact** aceleasi sume. **Probe adaugate de runda 13:** - **Reemitere repetata de trei ori pe acelasi document** — `DIFERENTA` **nu se acumuleaza**. Motivul: `cursor_retur_document` intoarce pretul deja **rotunjit si cu `DIFERENTA` pliata inauntru** (S10), iar reemiterea trimite inapoi valoarea pliata, deci impartirea `PRET` / `DIFERENTA` se **reface** la fiecare ciclu. Se verifica **suma**, nu impartirea — dar se verifica si ca impartirea nu deriveaza. - **„Reemitere identica" nu e garantata uniform** (S10): pe `ELSE` / restaurant da; pe **comenzi** esueaza **tare** (eroare vizibila); pe **contract** si pe **aviz (`ntip = 4`)** esueaza **tacit**. Fiecare din cele patru situatii isi are cazul lui de test, cu rezultatul asteptat **declarat dinainte** — inclusiv cele care trebuie sa dea eroare. - **Deschid si inchid fara sa modific nimic → nicio scriere** (S8b), rulat pe un document al unui client **cu activitate recenta** — cazul in care lookup-ul „ultimul delegat / masina" ar polua antetul. *Depinde de:* S10, S11. #### S14 — Audit: creare, modificare si stergere pe document **Cerinta (decizia 63):** pentru fiecare document, sase informatii de audit — data + utilizator pentru creare, pentru modificare (daca e cazul) si pentru stergere (daca e cazul). **Cercetarea s-a terminat** — `docs\cercetare\audit_vanzari_creare_modificare_stergere.md`. Verificat pe `all_tab_columns` (`ROA_CENTRAL`, schema live) si pe cod. **Ce exista deja, pe `VANZARI`:** - `ID_UTIL` + `DATAORA` — **creare**, ambele `NOT NULL`, scrise consecvent la fiecare `INSERT` (`PACK_FACTURARE.scrie_in_vanzari`, `EXPORT:13598-13640`, si a doua ruta din `finalizeaza_avize_lucrare`, `EXPORT:14930`). Niciun `UPDATE VANZARI SET ID_UTIL/DATAORA` in tot pachetul (cautare directa, zero rezultate). - `ID_UTILS` + `DATAORAS` — **stergere**, nullable, scrise consecvent de `sterge_factura` (`EXPORT:5496-5499`) si `sterge_proforma`. - (neceruta, dar arata conventia) `ID_UTILFACT` + `DATA_FACTURAT` — facturare din aviz. - Pe `VANZARI_DETALII`: acelasi tipar de creare/stergere, plus `ID_UTIL_VALID` + `DATAORA_VALID` (validare). **Conventia casei:** o pereche utilizator + data per eveniment. Patru din cele sase informatii cerute exista deja si se scriu consecvent. **Ce lipseste, integral:** nicio coloana de **modificare**, nici pe `VANZARI`, nici pe `VANZARI_DETALII`. Interogat explicit `all_tab_columns` cu filtru pe `%MODIF%` — zero rezultate. `DATA_ACT` nu e echivalentul: e data contabila, propagata in `ACT`/`DOCUMENTE`/`JV2007`/`RUL` (verificat in `modifica_date_factura`, `EXPORT:14439-14500`), nu un marcaj de audit. **Capcana, si e miezul povestii:** editarea din #13 se face prin stergere + reemitere (S9), iar documentul nou se scrie pe **acelasi `INSERT`** ca o creare normala (`scrie_in_vanzari`). Fara un pas explicit, `ID_UTIL` / `DATAORA` ale documentului reemis ar deveni tacut utilizatorul si data **modificarii**, iar informatia despre creare s-ar pierde — exact ce cere decizia 63 sa nu se intample. **Precedentul de rezolvare exista deja in plan, pentru `ID_FACT`** (sectiunea „E. `ID_FACT`", linia 762, si S9: se citeste din documentul vechi inainte de stergere si se impune celui nou). **Niciun pas echivalent nu e proiectat azi pentru `ID_UTIL` / `DATAORA`** — cautare directa in tot planul, zero mentiuni. **Atenuare, nu solutie:** la regenerare randul vechi ramane cu `STERS = 1` si cu `ID_UTILS` / `DATAORAS` completate — iar acea stergere **este** momentul modificarii. Cum `ID_FACT` se pastreaza peste regenerare, lantul vechi → nou e parcurgibil, deci o parte din istoric e reconstruibila din randurile existente, fara coloane noi. O pereche explicita pe documentul curent ramane insa preferabila: se citeste direct, fara parcurgerea lantului. **Complicatie reala, verificata pe cod livrat de #6:** editarea de linie (`COMUN\programe\ofacturare_editare.prg`) scrie `id_utils` / `dataoras` pe `VANZARI_DETALII` si pe randuri care **nu sunt sterse** — `ofacturare_editare.prg:492-494` (corect, cu `sters = 1`), `:501-505` (odata cu `sters = 0`, pe rand viu) si `:522-525` (pe un rand proaspat inserat). Acolo perechea de stergere e folosita ca marcaj „cine a umblat ultima data", nu strict ca „sters de/la". **Consecinta:** un raport de audit nu poate citi `ID_UTILS` / `DATAORAS` ca „sters de/la" fara sa puna si `STERS` in conditie. `ofacturare_editare.prg` e perimetrul lui #6, in lucru — semnalat aici, neatins. **Recomandare (de confirmat, nu decisa):** 1. **O singura pereche noua pe `VANZARI`**, urmand conventia casei — nume propus `ID_UTILM` / `DATAORAM` (utilizator si data ultimei modificari). Numele e de confirmat. 2. **La regenerare (S9): `ID_UTIL` / `DATAORA` se transporta din documentul vechi in cel nou**, exact ca `ID_FACT` — vezi nota adaugata la S9. Perechea noua de modificare primeste utilizatorul curent si `sysdate`. Fara acest pas, creare si modificare se suprapun. 3. **Perechea de stergere nu are nevoie de nimic** — exista si e scrisa consecvent. 4. **Cere migrare de DB** (`ALTER TABLE` pe `VANZARI`) — **DB inainte de EXE**, ca la S10. Scriptul intra in `D:\ROA\DATABASE\SCRIPTURI_CLAR\`, `versiune_db.txt` se bumpeaza. **De decis cu Marius — AMBELE INCHISE prin decizia 65 (runda 16):** - **(a)** Auditul e cerut numai pe antet (`VANZARI`) sau si pe linii (`VANZARI_DETALII`)? **Raspuns prin deductie, nu spus explicit de Marius:** locul de afisare ales de el e gridul din `frm_facturi`, care listeaza **documente** — deci auditul e pe **antet**. - **(b)** Se afiseaza undeva in interfata (formular / lista), sau ramane doar pentru interogare? **Da, se afiseaza** — in **gridul din `frm_facturi`**, nu in formularul facturii propriu-zise. Prim pas de implementare: inventarul acelui grid, posibil sa existe deja coloane de audit. *Gata cand:* documentul reemis pastreaza `ID_UTIL` / `DATAORA` ale creatiei originale; perechea noua de modificare e completata la fiecare regenerare, cu utilizatorul si momentul curente; perechea de stergere ramane neschimbata fata de azi; migrarea DB e aplicata inainte de EXE. *Depinde de:* S9. #### S13 — Diff, review, changelog, documentatie **Atentie: S13 NU e momentul in care se face review-ul.** Conform metodei de executie, **fiecare story isi are propriile teste, propriul diff si propriul code review, inainte de commit-ul ei** — S13 nu strange la final ce n-a fost revizuit pe parcurs. Ce ramane aici e **inchiderea**, adica exact ce nu se poate face per story: - **Review de ansamblu**, pe suma povestilor: coerenta intre ele, cai ramase orfane, cod mort din fluxul vechi care trebuia retras si n-a fost, si verificarea ca alegerea de la decizia 49 chiar functioneaza in ambele sensuri. - **Changelog** `:nou:`, cu mentiunile declarate explicit pe parcurs: asimetria `do_modifica` corectata prin recalculul la cerere (decizia 45), si faptul ca `do_modifica` ramane activ pentru multi-selectie (decizia 52). - **Documentatie**: `COMUN\docs\tipuri_documente_facturare.md` — ce tipuri sunt regenerabile si ce tipuri sunt **declarate explicit ca neacoperite** (48/49 si `frm_facturare_articole2`, daca se merge pe recomandarea din S8), plus confirmarea din S11 pentru `REST_NOTE_PLATA` si `IPS_VOYAGES_VANZARI`. - **Rularea suitei complete** — toate testele scrise per story, la un loc, nu doar cele ale ultimei povesti. - **Commit-urile finale**, in **ambele** repo-uri (ROAFACTURARE si COMUN), dupa aprobare. --- ## Riscuri - ~~**Cel mai mare: perimetrul comun cu #6.**~~ **Inchis prin decizia 30** — implementarea lui #13 incepe dupa terminarea lui #6, deci nu exista doi scriitori simultan pe `ofacturare_comun.vc2` / `ofacturare_editare.prg`. Riscul revine doar daca ordinea se schimba. - ~~**Riscul „cod nou in `pack_facturare`" e evitat prin constructie (decizia 27-bis).**~~ **Caduc dupa deciziile 34 si 35, si riscul revine ca risc principal.** Politica tehnica per `SCC` si scrierea in `CRM_POLITICI_PRET_ART` nu se mai fac — deci cade si riscul ca o politica tehnica sa apara in `caut_politici_curente_util()`. In locul lui: **`pack_facturare` se modifica** (parametru de cont + ramura fara politica in `contabilizeaza_articol`), iar pachetul e **comun intregii suite** — ROACONT, ROAGEST, ROACONTRACTE, ROAAUTO, ROAACNPRO. Mitigarea e structurala, nu prin inspectie: parametru **`DEFAULT NULL` la finalul listei** si ramura inerta cand lipseste, astfel incat apelantii existenti sa fie identici cu azi. **MASURAT — `docs\cercetare\suprafata_regresie_contabilizeaza_articol.md`. Riscul e mai mic decat parea.** `contabilizeaza_articol` are **trei apelanti, toti interni pachetului**, toti pozitionali cu acelasi singur argument — **zero apelanti externi, nici PL/SQL, nici VFP, in niciunul din cele sapte produse**. `adauga_articol_factura` e apelata **pozitional peste tot**, niciodata cu notatie pe nume (`=>`), iar cele sapte copii `COMUN\clase\ofacturare.vc2` sunt **acelasi sit de apel duplicat**, nu sapte implementari care ar putea diverge: fisierele difera intre ele (patru variante distincte pe MD5), dar **textul apelului e identic cuvant cu cuvant**, doar offsetul de linie difera. Exista in plus doua implementari proprii reale — ROAGEST (`Programe\ofactureaza.prg:264`) si ramura `_deviz` din ROAAUTO / ROAACNPRO. **Precedentul e activ, nu teoretic:** ROAGEST apeleaza azi `adauga_articol_factura` cu **24 din 25 de parametri**, omitand `V_TAXCODE` si `V_LOT` tocmai pentru ca au `DEFAULT NULL`. Tiparul propus e deja in uz in productie. **Ce ramane:** toate cele **sapte** produse emit efectiv prin acest pachet, deci aria de regresie e toata suita chiar daca niciun apelant nu se modifica. - ~~**Anomalie preexistenta: `scrie_factura2` apelata cu 16 din 17 parametri.**~~ **INCHISA — fals pozitiv, nu intra ca risc.** `oExecuta` (`COMUN\programe\oproceduri_comune.prg:121-159`) deleaga la `oExecute` (`:173-504`), care face `SQLExec(lnHandle, lcSql, lcCursor)` (`:330`) — **textul SQL nu e rescris**, deci al 17-lea parametru chiar n-are placeholder. Explicatia e tiparul cunoscut al driverului ODBC Oracle: cand ultimul parametru declarat e un `REF CURSOR OUT`, driverul il ia din catalog, nu din textul apelului, si intoarce randurile ca *result set* — captat exact de al treilea argument al lui `SQLExec`. *Nuanta de pastrat:* mecanismul din interiorul driverului **nu poate fi confirmat din codebase**, e verificabil doar prin comportament; dovada e indirecta, dar consistenta — acelasi tipar in toate cele patru situri, neschimbat de la `v 2.0.13` la `v 2.0.93`, iar ecranul de verificare depinde de acel cursor populat la fiecare emitere, deci un apel esuat s-ar fi vazut demult. - **Decizia 35 leaga etapa II de aceeasi modificare de pachet.** Editarea prin regenerare nu mai are cale proprie (canalul `oscrie_in_fisiere` e respins ca al doilea cod de contare), deci **un blocaj pe parametrul deciziei 34 blocheaza si S9-S12**, nu doar etapa I. In schimb dispare riscul de divergenta intre regula de contare de la emitere si cea de la editare. - **`ofacturare.vc2` e in COMUN si are 843 KB / 25 de clase**, folosite de toata suita. Formularul vechi trebuie sa ramana functional pana cand cel nou acopera toate tipurile — deci comutator, nu inlocuire. - **`ID_FACT` pastrat cere atingerea unui punct comun intregii suite.** `SET_IDFACT` e in `PACK_CONTAFIN` si e chemat de fiecare scriere de note din toate produsele ROA. Comutatorul din S9 trebuie sa fie inert pentru toate celelalte cai; altfel regresia nu e in ROAFACTURARE, ci peste tot. - **Formularul identic la introducere si la modificare taie o plasa de siguranta.** Decizia 9 o repune **doar pe antet** — butonul protejeaza campurile de antet, nu si liniile. Pe articole nu exista niciun semnal ca modificarea va sterge si va rescrie documentul; confirmarea de la `Termina` ramane singurul moment in care se poate spune ce urmeaza sa se intample. De formulat cu grija. - **Ascunderea datei de curs poate lasa un document fara curs corectabil.** `poDate.zi_curs` se trimite neconditionat catre cursoarele de articole; daca lipseste cursul pentru acea zi, Oracle da eroarea 20005 si se deschide formularul de curs — dar utilizatorul nu mai are unde sa corecteze data daca i-am ascuns campul. Regula din M trebuie sa aduca inapoi campul in exact acest caz. - **Sectiunea pliata ascunde campuri care schimba documentul.** Analiticele si datele de incasare intra sub un panou inchis implicit. Daca un camp obligatoriu pe un anumit tip ajunge acolo, utilizatorul primeste eroarea fara sa vada campul. Panoul trebuie sa se deschida singur cand contine un camp necompletat si obligatoriu, si sa arate in antet ca are ceva completat. - **Mutarea `frm_alte_date` muta si alocarea de numere.** Bonul fiscal, POS-ul si chitanta primesc numar din masina de stari a incasarii. Daca ea ajunge sa ruleze la deschiderea formularului in loc de la alegerea tipului de incasare, se aloca numere pentru documente care nu se mai emit. - **`S3c` si `S4c` ating cod comun.** Parametrizarea sursei atinge `factureaza` (toata suita), iar discountul editabil atinge listarile si eventual eFactura. Fiecare merge separat, cu regresie proprie. - **Reemiterea nu e idempotenta prin constructie.** Daca la reemitere pretul se re-deriva (H) sau cursul valutar al zilei difera, documentul "nemodificat" iese cu alte sume decat originalul. Testul din S12 (reemitere identica) exista tocmai ca sa prinda asta. - **`verifica_total_document`** (`PACK_FACTURARE:16009+`) insereaza automat o linie de corectie cand totalul difera de suma notelor. La regenerari repetate trebuie confirmat ca nu se acumuleaza — aceeasi intrebare ca S7 din #6. - **Cautarea in linie schimba un obicei.** Utilizatorii care lucreaza azi cu gridul de articole vizibil si filtrare locala pierd vederea de ansamblu. Merita verificat pe un client inainte de a scoate definitiv gridul vechi. ## Dependente - **#7 (pret cu TVA pe linie)** — flagul `pret_cu_tva` pe linie apare in gridul unificat; se pastreaza comportamentul stabilit acolo. - **#12 (nomenclator ca lista de preturi)** — S4 construieste cautarea in linie peste politici de preturi; daca #12 muta pretul in nomenclator, S4 se rescrie. - **#16** — bug-ul de focus / renumerotare la revenirea din cursul valutar. Confirmat ca apare **dupa** ce antetul s-a inchis si s-a eliberat (`ofacturare.prg:248`), la recuperarea erorii Oracle 20005. In formularul unificat nu mai exista un antet inchis la care sa te intorci, deci **mecanismul lui dispare** — de confirmat in S4d, nu de presupus. - **#6** — **dependenta de calendar, nu de perimetru** (decizia 30): implementarea lui #13 incepe dupa ce #6 se termina. Proiectarea si cercetarea merg mai departe in paralel. ## Preconditii de mediu - Orice DDL / modificare `PACK_FACTURARE`: sursa de referinta e `MARIUSM_AUTO` pe `ROA_CENTRAL`, nu productia (`COMUN\docs\scripturi-migrare-db.md`). Scripturi nivel Oracle 10.2, CRLF, idempotente, `versiune_db.txt` actualizat. - Sursa completa `PACK_FACTURARE` nu e in working copy; copia pe disc e `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\03\ff_2026_03_23_04_COMUN_PACK_FACTURARE.sql` (16 948 linii), cu alta numerotare decat exportul din baza. - Editarile pe `.vc2` / `.sc2` trec prin `git_sync.ps1` + `txt2vcx.ps1`, cu atentie la `COMUN\docs\conventie_encoding_cp1252.md` (diacriticele din `.vc2` sunt cp1250 — vezi si memoria proiectului).