Files
roafacturare/docs/plan_13_unificare_formular_facturare.md
Marius Mutu ca3c5d7eea docs: runda 6 - cercetari, propuneri si conventia mediului Oracle
Cercetarile si propunerile rundelor 4-6 pe editarea facturii emise. Starea
rundei 6 si ce s-a stabilit intra in progres.md.

Nou: docs/conventii_mediu_oracle.md - pe dev/test se lucreaza numai cu
ROA_CENTRAL si schemele CONTAFIN_ORACLE si MARIUSM_AUTO; schema ACN nu se
foloseste. Scripturile de pachet sunt necalificate, deci schema tinta o
decide sirul de conectare, nu fisierul.

roafacturare.pj2 regenerat de git_sync.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013gYCNE26G1G7UoGu2ju7aS
2026-08-20 16:35:03 +03:00

326 KiB
Raw Blame History

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

  1. frm_alte_date intra in formular, in sectiunea pliata. Se inchide intrebarea ramasa deschisa in runda 2 (recomandarea de atunci — „ramane dialog” — cade).

  2. 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.

  3. 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.

  4. Proforma foloseste acelasi formular.

  5. Copierea foloseste acelasi formular, ca document nou, cu antetul editabil.

  6. 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.

  7. 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.

  8. Discountul pe articol, procent si valoare absoluta, amandoua accesibile.

  9. 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)

  1. 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.
  2. 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).
  3. 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.
  4. 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.
  5. 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)

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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)

  1. 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.

  2. 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.

  3. 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.

  4. 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. Asezarea lui s-a inchis in runda 17 — decizia 66: sta in banda de totaluri, langa discount. 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<N>_<subiect>.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. Asezarea lui s-a inchis definitiv abia in runda 17 — decizia 64 (a treia sectiune jos) a fost rasturnata de decizia 66: motivul sta in banda de totaluri, langa suma pe care o explica, iar randul de jos ramane cu doua sectiuni, ca la decizia 57. Varianta grea (cota + explicatie proprii) ramane exclusa, ca mai sus.

Cele doua pagini online ale lui #13 — nu se confunda:

  • mockup-ul formularului (fisier pe disc, docs\mockup_13_formular_unificat.html, v9 din runda 17, cu asezarea D si motivul discountului in banda de totaluri): https://claude.ai/code/artifact/e9d73e86-7518-4f50-a998-43c49e083142. Are copie pe disc, si ea e sursa de adevar; artifactul se republica din ea (WebFetch pe URL intai, apoi Artifact cu url). Decizia 33 e consumata — URL-ul e la zi.
  • pagina cu variantele de asezare (A/B/C/D), fara copie pe disc, deci artifactul e sursa de adevar:

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.

INCHISE, nu de reintrebat (marcaj pus in runda 17). Cele cinci puncte de mai jos au primit raspuns: punctul 1 (TIP = 4) prin decizia 51, restul in bloc prin decizia 56 („da la toate"). Lista ramane ca inventar al recomandarilor acceptate, ca sa se stie ce s-a decis si de ce — nu ca intrebari:

  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.

  1. 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)
  2. 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)
  3. Corectia pe tipurile 23,41 asteapta punctul 2 (recalculul pe server, care acopera Rolul B). Punctul 1 nu se redeschide acum. (S4, punctul 2)
  4. zi_curs: simetrie. Campul se ascunde la loc la stergerea ultimului articol in valuta; „clipitul" e acceptat ca pret al unei reguli unice. (S4d)
  5. 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

  1. 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)

  2. 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)

  3. 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)

  4. 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)

  5. „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 — inchis si el: decizia 61 (da, se adauga), cu asezarea fixata de decizia 66 (in banda de totaluri, langa discount).

  6. 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

  1. 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. Asezarea lui a oscilat o data: decizia 64 il facea a treia sectiune jos, decizia 66 (runda 17) o rastoarna si il muta in banda de totaluri, langa discount. Punctul 2 de mai sus ramane deci exact cum a fost aprobat: doua sectiuni jos, fiecare pe jumatate de latime. D ramane intr-un etaj.

  2. 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

  1. 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

  1. 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.

  2. 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 DEFINITIV PRIN DECIZIA 66 (runda 17): campul sta in banda de totaluri, langa discount. Decizia 64, care il facea a treia sectiune jos, e rasturnata — randul de jos ramane cu doua sectiuni, ca la decizia 57. Varianta D ramane intr-un etaj. Paragrafele din S1 si din decizia 57 sunt actualizate in acest sens.

  3. 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.

  4. 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.

  5. Asezarea campului de motiv: RANDUL SE IMPARTE IN TREI. RASTURNATA DE DECIZIA 66 (runda 17) — NU MAI E IN VIGOARE. Formularea de atunci: „campul de motiv imparte randul in 3"; randul de jos ar fi avut trei sectiuni (incasare, alte date, motivul discountului), ~440 px fiecare la 1366 px.

    Se pastreaza ca istorie, nu ca regula, si merita pastrata: varianta a fost construita in mockup (v9) si respinsa dupa ce Marius a vazut-o — „motiv discount vreau sa fie langa discount, nu a treia coloana". E argumentul cel mai bun din tot planul pentru de ce se face mockup inainte de cod: decizia luata pe descriere s-a intors la prima privire pe forma desenata. Ce e in vigoare: decizia 66, mai jos.

  6. 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.

Decizia 66 (Marius, runda 17) — luata, nu de reluat. RASTOARNA DECIZIA 64.

  1. Motivul discountului sta LANGA DISCOUNT, in banda de totaluri — nu ca a treia sectiune jos. Formularea lui: „motiv discount vreau sa fie langa discount, nu a treia coloana".

    Anuleaza decizia 64 (randul de jos se imparte in trei). Randul de jos revine la doua sectiuni colapsabile — incasare si alte date — adica exact la ce aprobase decizia 57, punctul 2, inainte ca decizia 61 sa redeschida discutia. Decizia 57 ramane intreaga; nu se mai atinge.

    Ce inseamna concret, pentru S1 si S3:

    • campul de motiv e un control in banda de totaluri, imediat dupa suma discountului de document, inainte de TVA; ia latimea ramasa pana la totalul mare, care sta la dreapta;
    • cele doua sectiuni de jos redevin pe jumatate de latime (~660 px la 1366 px), nu ~440 — argumentul „strans, asumat ca atare" din decizia 64 cade odata cu ea;
    • regula de activare, adaugata de proiectare, nu ceruta explicit: campul e activ numai cand discountul de document nu e zero. Un motiv fara discount n-are ce explica, si ar ajunge in AllowanceChargeReason pe un AllowanceCharge inexistent. Daca Marius vrea altfel, e o linie de schimbat.

    Consecinta de asezare, de stiut la implementare: banda de totaluri devine plina — baza, discount articole, discount document (procent + suma + bifa „evidentiat"), motiv, TVA, total. La latimi mici se rupe pe doua randuri (flex-wrap), si asta e acceptat: alternativa ar fi scoaterea bifei „evidentiat" din banda, care n-a fost ceruta.

    Restul deciziei 61 (stocare noua pe VANZARI, AllowanceChargeReason, ReasonCode ramane 95) e neatins — se schimba doar locul controlului in formular.

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:

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. Partea de tipuri 48/49 din intrebarea 7 s-a inchis prin decizia 60: sunt editabile. Intrebarea 7 e deci inchisa integral.

CERINTA DE EXECUTIE ADAUGATA IN RUNDA 17 — IN_STOC se incarca din document, nu din nomenclator. Vine din consecinta 1 a deciziei 54 (vezi S10, „Trei consecinte"), si se prinde aici, nu la S12. Cu flag-ul de regenerare pornit, IN_STOC nu mai e re-derivat de adauga_articol_factura, ci vine din formular — deci valoarea pe care o incarca S8 devine valoarea care decide descarcarea de gestiune la reemitere. Azi loader-ul lui #6 o citeste din nomenclatorul curent (ofacturare_editare.prg:302-303), deci un articol devenit intre timp gestionabil (sau invers) ar face reemiterea sa atinga alt stoc decat documentul initial, tacut.

Trei consecinte concrete pentru canalul de citire (intrebarea 1 din §8.2, recomandat (B), cursor_editare_document):

  • canalul trebuie sa intoarca IN_STOC asa cum a fost la emitere, nu GESTIONABIL = B.IN_STOC din nomenclatorul de azi, cum face cursor_retur_document (PACK:3993-4000). E un al treilea argument pentru procedura noua, langa ID_VANZARE_DET / TAXCODE si langa ID_POL / ID_CTR lipsa din VVANZARI_ARTICOLE;
  • valoarea istorica nu e stocata nicaieri: IN_STOC nu e coloana pe VANZARI_DETALII (verificat pe DB, vezi S10), traieste doar in temp. Deci primul pas al lui S8 pe aceasta cerinta e sa stabileasca de unde se reconstituie — fie din urma lasata in rulaje / gestiune pentru documentul respectiv, fie se accepta nomenclatorul curent ca aproximatie declarata explicit, fie se adauga coloana (migrare DB, deci DB inainte de EXE, ca la S10). Nu se presupune niciuna dintre variante; e o preconditie de proiectare a lui S8, nu un detaliu de implementare;
  • pe tipurile 48/49 cerinta se intalneste cu decizia 60: acolo invariantul e IN_STOC = 0 prin constructie, deci valoarea incarcata trebuie sa fie 0 indiferent ce zice nomenclatorul azi.

Criteriu de test (intra in „gata cand" al lui S8): un document emis cu un articol caruia i s-a schimbat intre timp IN_STOC in nomenclator, deschis in formular, poarta valoarea de la emitere; iar reemiterea lui lasa stocul agregat neschimbat (masurat inainte / dupa, nu prin inspectia codului).

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; si liniile incarcate poarta IN_STOC de la emitere, nu din nomenclatorul de azi (cerinta rundei 17, mai sus). 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.

INCHIS, nu de reintrebat (marcaj pus in runda 17). Punctul de mai jos are raspuns: decizia 52 — do_modifica ramane activ pentru multi-selectie, iar formularul unificat preia doar cazul cu un singur document. Se pastreaza enuntul, nu intrebarea:

  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. PRELUAT (runda 17) ca cerinta de executie in S8, cu cele trei consecinte pentru canalul de citire si criteriul de test — vezi caseta „CERINTA DE EXECUTIE ADAUGATA IN RUNDA 17" din S8. Aici nu mai e nimic de decis.
  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.

INCHISE, nu de reintrebat (marcaj pus in runda 17). Punctul 1 e transat de cercetarea garzilor din runda 14 (garda exista si e corecta; S7 o muta in pre-flight read-only) — ramane limitare declarata. Punctul 2 e rasturnat de decizia 53: atasamentul vechi nu se remigreaza, se sterge, deci premisa „factura editata ajunge cu PDF-ul vechi" nu se mai produce. Punctul 3 e inchis in runda 14, cu cerinta ca S7 sa refuze explicit cele doua tipuri. Se pastreaza ca inventar:

  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).