Folderul docs\ era pana acum in afara oricarui control de versiuni - nici git, nici SVN - desi contine planurile pe puncte, proiectarile si rapoartele de cercetare pe care se sprijina modificarile din cod. O stergere acolo era definitiva. Fisierele intermediare (handoff-uri intre sesiuni, diff-uri deja aplicate) au fost sterse inainte, nu versionate: ce era durabil in ele a intrat in antetele fisierelor de test la care se refereau. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SN8snvkk94KuhWwoXUUey3
319 KiB
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:
- Unificarea formularelor —
date_factura/date_avizsa intre in formularul de facturare, ca sa nu mai fie doua ferestre pentru acelasi document. - Fara incarcarea prealabila a tuturor articolelor din toate politicile de preturi — pot fi mii si dureaza mult aducerea lor de pe server.
- 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 = 1si 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
- #13 coexista cu #6, nu il inlocuieste.
ID_FACTse pastreaza, ca la orice modificare. Documentul reemis nu-si schimba identitatea.- Toate datele se pastreaza, inclusiv numarul documentului. Scopul e modificarea documentului, nu emiterea altuia.
- Antetul care intra in generarea lui
ID_FACT(serie, numar, data) are cale proprie de modificare — nu trece prin regenerare. - Un singur formular, aceeasi infatisare la introducere si la modificare. Fara banda de avertisment, fara coloane cu valorile initiale, fara panou de diferente.
- 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
-
frm_alte_dateintra in formular, in sectiunea pliata. Se inchide intrebarea ramasa deschisa in runda 2 (recomandarea de atunci — „ramane dialog” — cade). -
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.
-
Un singur buton comutator pe antet. Antetul se deschide blocat. Un singur
but_modifica(creionul) il deblocheaza si isi schimba imaginea in discheta luibut_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 dinTermina. 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 / Salveazacare 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 debut_modificaintre creion si discheta — nu instantiaza a doua clasa. Trei lucruri de retinut din el:- se rescriu impreuna
.cpicturedown,.cpictureupsi.Picture, plus.ToolTipText, urmate de.Refresh(). Numai.Picturenu ajunge: hover-ul standard dinbuton.MouseEnter/MouseLeave(_cmd_base.vc2:88-97) rescriePicturedincPictureUp/cPictureDown, deci iconita veche ar reveni la primul mouse-over; - numele de fisier se dau fara cale (
"save_sus.bmp"), rezolvate prinSET PATH— care include siGRAFICE, siCOMUN\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, nubut_salvare(cmd_butoane.vc2:340-352); e sursa numelor de imagini, dar nu se instantiaza. Clasabut_modificae lacmd_butoane.vc2:184-198. Butoanele nu audo_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 scriemodifica_date_factura, parametru cu parametru. - se rescriu impreuna
-
Proforma foloseste acelasi formular.
-
Copierea foloseste acelasi formular, ca document nou, cu antetul editabil.
-
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.
-
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. -
Discountul pe articol, procent si valoare absoluta, amandoua accesibile.
-
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)
- 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 (crsarticolee populat decursor_preturi/cursor_contractcu lista intreaga, al doilea grid e un adaos, nu o restrictie), comanda nu —cursor_comandaumplecrsarticoledoar cu articolele comenzii (ofacturare.prg:266-308). Deci golul real e pe comanda, si e in continutul cursorului, nu in vreunVisiblede buton. Reteta exista deja in produs: ramura de copiere adauga lista de preturi peste cursorul sursei cuAPPEND FROM(ofacturare.prg:454-473) — se generalizeaza ea, nu se inventeaza alta. - 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). - 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): acelasiPACK_FACTURARE, acelasi drumVANZARI_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” dinfrm_incasare_finala. Deci descrierea „doar linii sintetice cuid_articolnegativ” 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. - Toti parametrii lui
modifica_date_facturatrebuie 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_facturasi se muta ca atare in antetul unificat; V_EFACTURAnu are niciun control nicaieri (ofacturare_comun.vc2:4576il forteaza0la selectie multipla, altfel vine dinScatter) — primeste unul, e singurul camp nou-nout cerut de decizia asta;V_TIP_SAFTare control, dar ascuns dupagl406(: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.
- 13 au deja control in
- Din nomenclator se aleg si articole gestionabile, si negestionabile. Filtrul
in_stoc = 0al 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)
- Cand
NOM_ARTICOLE.CONTe gol pe un articol negestionabil, se aplica un fallback la un cont implicit. Nu se acceptaNULL(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 siNOTE_CONTABILE, configurata manual de contabil. - 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.
- Secventierea deciziei 18:
pack_autose cerceteaza acum, inainte de orice estimare, nu cand ii vine randul la livrare. Vezi O, „Secventierea". Rezultat: riscul tehnic nu exista —PACK_AUTOnu citesteVANZARI/VANZARI_DETALIIdeloc. Decizia 18 nu mai trebuie sa fie ultima. Articolul ales din nomenclator primesteRETRASA in runda 8, inlocuita de decizia 27. Ramane in plan doar ca sa nu fie reintrodusa: politica implicita ar fi evitat codul nou inid_pol-ul unei politici de pret implicite.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 corespondenteleCORESP_CONT_VENCHELT. Vezi decizia 27 si J-quater.- 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 laTermina. 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. - 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)
-
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 peCONT(contul de gestiune al liniei) si luandCONT_VENIT; - articol negestionabil: se foloseste
NOM_ARTICOLE.CONTdaca 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 inRELAXATA 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, iarpack_facturare.contabilizeaza_articolruleaza neschimbata. Calea de verificat: VFP calculeaza contul de venit dupa regula de mai sus si alege unid_pola carui nota are deja acelSCC, astfel incatFACT-024sa 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 dincursor_contract/cursor_preturi, care le livreaza cuid_polatasat.FACT-024ramane 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 langaSCC, siCU_TVA/IN_VALUTA, pe care un fallback in pachet ar fi trebuit sa le hardcodeze. Vezi J-quater, punctul 3. Planul B (modificareapack_facturare) se abandoneaza. - articol gestionabil (cont de gestiune de clasa 3xx): contul de venit se ia din tabelul de
corespondente
-
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.
-
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.
-
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), nuscrie_factura_avize_retur, care nici nu cheamacontabilizeaza_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-adevarSELECT * BULK COLLECT INTOunTABLE 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. Inscrie_factura_avize,tab_detalii(i)e modificat inainte de apel doar pe.cantitate/.id_rata—CONT_VENITramane neatins. Efectul cerut de Marius e exact acelasi; doar mecanica de livrare difera de formularea literala. Precedentul e in aceeasi semnatura:V_TAXCODEsiV_LOTsunt deja doi parametri adaugati ulterior, la coada, cuDEFAULT NULL, iar apelul VFP e pozitional si se opreste laV_LOT. Ramura noua se activeaza pedetalii_articol.cont_venit IS NOT NULL, infasoara si bloculFACT-024(garda ramane litera cu litera pe ramura veche: o linie fara politica si fara cont trimis cade in continuare cuFACT-024), n-are cursor deloc — decidescarca_gestiuneruleaza 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'siCU_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:14069si:18089nu sunt o duplicare a aceleiasi metode, cum se presupusese: sunt doua clase distincte,frm_facturare_articole.do_scrie_articole(:13967-14195) sifrm_facturare_articole2.do_scrie_articole(:18003-18221), fiecare construindu-si separat apelul RPC. CU_TVA = 1nu e inofensiv, cum spunea proiectarea. Actualizarea luinproc_tva_max/nid_jtva_coloana/nTaxCodee chiar in interiorul luiIF V_CU_TVA = 1(:12537-12558), iar comparatianproc_tva_max < V_PTVAse 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_maxporneste-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_TEMPare lista de coloane explicita — 24 de coloane,PACK_FACTURARE:13705-13757, inscrie_in_vanzari. Deci daca se vreaCONT_VENITpastrat si dupa fapt inVANZARI_DETALII, lista trebuie extinsa explicit; altfel coloana traieste doar inVANZARI_DETALII_TEMPsiACT_TEMP.- Articolul compus nu e o gaura in proiectare — e exclus structural, nu prin presupunere.
COMPUS, asa cum il citestecontabilizeaza_articol(:7279-7283), e definit inVCRM_POLITICI_PRET_ARTca proprietate a perechii (articol, politica), identificata prinID_POL_ART. O linie fara politica n-areID_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 = 4e exclus structural — dar nu prin garda pe care o presupunea proiectarea. Blocajul nu eFACT-024dincontabilizeaza_articol, ci o functie mai devreme:adauga_articol_factura, ramuraWHEN ntip = 4(:5080-5103), cauta randul sursa dinVANZARI_DETALIIprinA.ID_POL = V_ID_POLfara handler de exceptie. CuV_ID_POL = NULL— exact cazul liniei fara politica — comparatia nu se poate potrivi niciodata, deci apelul cade cuORA-01403la adaugarea articolului, in buclado_scrie_articole(ofacturare.vc2:13967-14106), care ruleaza intotdeauna inainteaDo Case-ului (:14282). Linia nu ajunge niciodata lacontabilizeaza_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:
SCDpe avize. Ramurile de aviz care chiar ajung lacontabilizeaza_articol—ntip IN (28,29)→SCD = '461', restul avizelor „simple" (21, 22, 24, 27) →SCD = '418'(:7413-7422) — sunt accesibile cucont_venitpopulat, pentru caadauga_articol_facturanu cereid_polpe niciunul din ele. Ramura noua, care iaSCDdin optiunea deciziei 36 (4111), l-ar scrie necontitionat dentip→ cont contabil gresit pe aviz. Deci ramura noua alegeSCDdupantip:'461'pentruIN (28,29),'418'pentru celelalte avize atinse, si abia pe restul optiuneaRF_CONT_ART_FARA_POL. Verificarea a restrans lista:ntip IN (23,25,30,41,42,47)nu ajung deloc lacontabilizeaza_articol— sunt deviate mai devreme, inscrie_factura2, spretransfera_articol(23,25,30,41) sau direct spredescarca_gestiune(42,47). Golul nu li se aplica. - Al doilea gol confirmat: garda
ntip = 46.nTipNotaPlataeste46, iarscrie_notae 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_facturarenu 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, cu4111ca 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 iaV_SCDin trei niveluri: parametru explicit daca a fost dat → optiunea de firma (RF_CONT_INCASARE_BONFISCALs.a.m.d., cate una per tip de incasare) → constanta hardcodata daca optiunea lipseste. Se copiaza identic, si se inlocuieste punctual liniaSCD := '4111'din proiectarea ramurii noi. Mecanismul are 15+ ani: tabelulOPTIUNI(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 existaID_FIRMA: fiecare firma are schema Oracle proprie, deciOPTIUNIdin schema de conexiune este deja „optiunile firmei curente". Dubla plasa de siguranta, si asta acopera integral cerinta lui Marius: implicitul4111traieste si in randul dinOPTIUNI(scris de migrare, editabil fara recompilare), si hardcodat langagetoptiunefirma— deci si o instalare veche fara migrare, si un rand golit din ecran cad tot pe4111.getoptiunefirmaintoarce''cand nu gaseste, ceea ce in Oracle eIS NULL, deci ambele cazuri se trateaza cu aceeasi conditie.ASCDnu trebuie atins — se calculeaza deja dinV_SCD, deci primeste automat valoarea corecta indiferent de sursa.PROGRAMEse pune larg, nu doarROAFACTURARE— intrebarea a ramas deschisa in raport, dar se inchide combinand-o cu masuratoarea de regresie: apelul catreadauga_articol_facturatraieste inCOMUN\clase\ofacturare.vc2, fisier prezent si folosit in toate cele sapte produse, deci ramura noua e atinsa de toata suita, exact caRF_CONT_INCASARE_*(care listeaza sase produse).CU_TVA— derivat din cota liniei, nu fixat:1dacaproc_tvav > 0, altfel0. Asta elimina prin constructie efectul gasit la verificare: o linie scutita nu mai intra in comparatia de maxim dinnproc_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 familiaRF_CONT_INCASARE_*— adica exact optiunile care alimenteaza aziSCDin acelasi pachet. Cele cinci apar astfel grupate alaturi in ecranul de optiuni, ceea ce le face inteligibile impreuna. Incape inOPTIUNI.VARNAME(VARCHAR2(30)).VARTYPE = 'CHARACTER',VARVALUE = '4111',PROGRAM = 'ROAFACTURARE',PROGRAMElarg (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 genericCOMUN\clase\oOptiuni.vc2— fisier al intregii suite. Garda gratuita ramane lungimea: unVARVALUEpeste 4 caractere daORA-12899laINSERT 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
PROFORMAdin combo-ul „Tip document” (ct_clb_fdoc._combobox1,RowSource = "FACTURA,PROFORMA,BON FISCAL",ofacturare.vc2:8745-8754), care seteazanIdTipDoc = 23si, prin setter,eProforma = 1(ofacturare_comun.prg:593-599,nIdTipDocProforma = 23la: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 garzilepoDate.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_facturaprecompletat decompleteaza_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) faceSCATTER NAME goComanda MEMOsi cheamafacturare_comenzi->factureaza(3); butonulBut_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-1549si: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 cautaretxtCodmat/txtArticole— adica exact partea care azi consuma incarcarea in masa; - are grid editabil in linie, cu combouri
combosqldincautare.vcxpecCodMat.cboCodmat,cDenumire.cCboDenumire,cGestiune.cCboGestiune(:16751-16881), plusBut_nou1; - e lansat de
factureaza2(COMUN\programe\ofacturare.prg:590-1080), care se cheama numai dacagnFacturareNou = 1si utilizatorul raspunde DA la unAMESSAGEBOX(: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:
frm_date_factura(ofacturare.vc2:8482-9869) saufrm_date_aviz(:6566-7618) — 17, respectiv 13 metodedo_cauta_*, plusdo_schimba_tipdoc,inainte_de_do_terminsi unInitde 234 / 247 de linii;frm_facturare_articole(:10968-15739) — compunerea;frm_alte_date(COMUN\clase\ferestre_cere_date.vc2:2219-3353) — aratat dupa confirmarea articolelor, dininainte_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 = NULLpe avizele facturate (tip 4 si 24);STERS = 1peVANZARI_CANTITATI(cantitatile consumate de pe avize);STERS = 1peCOMENZI_ELEMENTEcuCANTITATE < 0(consumul din comanda);STERS = 1peVANZARI_CORESPsi peCTR_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_IDFACTfara 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. DeciID_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:
- 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_SECTIEper factura". - Bug suspectat pe
sectie(omodificari.vc2:13941):replace ... id_valuta with loCauta.id_sectie— pare sa scrie inid_valutain loc deid_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" pentruID_SECTIEe 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 luibut_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_modificacheama azi exclusivpack_facturare.modifica_date_factura, cu 15 parametri toti de antet, si niciun apel spre articole, note sauoscrie_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 inJV2007siRUL(PACK_FACTURARE.pck:14428-14459) — coloane de identitate, nu sume: nicio valoare nu se recalculeaza. Pentru celelalte zece campuri, scrierea e strict inVANZARI. Vezi I-bis. Terminaramane 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:
- buton activ conditionat de document editabil si tip care are sursa
(
llFacturaEditabila and llRorisSauContracte,oacnpro.vc2:8117-8145); - o metoda unica de intrare care ramifica pe tip (
do_executa->factura_import,proceduri_acnpro.prg:3151-3212); - dialog modal de selectie cu coloana de bifat si criterii de cautare (
frm_tranzit, coloanacAleslegata decrsConvoaie.ales,oacnpro.vc2:14500-14515,:14801), urmat de un al doilea nivel de calcul / confirmare; - populare aditiva prin
INSERT INTOin cursorul local de articole, fara nicio procedura Oracle — pachetele intra abia la salvare (proceduri_acnpro.prg:3331-3467); - 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 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
id_pol-ul unei politici de pret
implicite.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_articolprimeste parametru de cont) si decizia 35 cere capack_facturaresa 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 peinitializeaza_date_factura->adauga_articol_factura_deviz->scrie_in_vanzari->pack_acn.salveaza_regdoc. Cautare directa dupacontabilizeaza_articolin acel fisier: zero rezultate. Nota contabila o scrie o procedura proprie ACN, care nici nu primesteid_polprintre 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.
crsarticolee populat depack_facturare.cursor_contract/cursor_preturi, care selecteaza explicitA.ID_POL(ff_...:2162-2167) si care ruleaza la fiecare apelcompletare_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 coloanaid_polin nicio ramura;cursor_preturi/cursor_contracto 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, darid_polnu vine din antet ca valoare unica — e parametru per linie, trimis de VFP. Doua cauze diferite (id_polgol; 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,
704altfel. Aia e pasul 1, si e verificata pe date. Politica e necesara doar ca canal de transport catre Oracle:contabilizeaza_articolnu are parametru de cont de venit — primesteV_ID_POLsi 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 prinscrie_factura2→contabilizeaza_articol. PisteleV_CONT(e contul de gestiune), setterele de sesiune,ID_VENCHELTsiGetAnaliticByGrupUtilizatorisunt 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.
- VFP calculeaza
SCC-ul dupa regula deciziei 27:CORESP_CONT_VENCHELT.CONT_VENITpe contul de gestiune al liniei (articol gestionabil),NOM_ARTICOLE.CONTdaca e 6xx / 7xx, altfel'704'. - 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 peSCCsingur nu e suficienta — vezi „Ce nu e gratuit" mai jos. - 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) inofacturare.vc2:15551-15587, la modificarea listei de preturi de la NIR.pack_preturinu epack_facturare— nu se atinge nimic din pachetul comun de facturare. - VFP trimite acel
id_polinadauga_articol_factura(V_ID_POLe parametru de intrare explicit,ff_...:4989-5015).contabilizeaza_articolruleaza 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 dat704cu 19 politici valabile,707cu 4,7015cu 1, si zero pentru711/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 peCONT, bug-ul de set multi-rand) plus un fapt negativ: afirmatia rundei 8 ca „pentru704nu 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 pentruSCC-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. PTVAde pe nota e inofensiv — verificat pe cod, nu presupus.cursor_articol(PACK_FACTURARE:7218-7271) nu selecteazaD.PTVA; cota folosita efectiv vine dindetalii_articol.proc_tvav * 100 - 100(:7464), adica din articol / document. Deci o nota cuPTVA = 5pe 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 = 1de pe nota pe un document in lei nu da nici eroare, nici suma greșita — doar populeaza redundantACT_TEMP.SUMA_VALla curs 1 (scrie_nota:12391-12404,:12492-12507): inconsistenta cosmetica, nu contabila.ASCD/ASCCnule au fallback automat prinGetAnaliticByGrupUtilizatori(:7409-7428), iarID_PARTD/ID_PARTCnu se citesc de pe nota deloc — vin din contul si din contextul documentului (:12510-12530).- In schimb,
ID_SETcu mai multe randuri dubleaza venitul SI descarcarea de gestiune. Asta e riscul real, si e mai grav decat cel presupus.cursor_articolfaceLEFT JOIN NOTE_CONTABILE D ON C.ID_SET = D.ID_SETfaraROWNUM, fara agregare, fara filtru, iar bucla care il consuma (:7393-7542) executascrie_notasidescarca_gestiuneo data pentru fiecare rand al setului, cu aceeasi cantitate si acelasi pret intreg de fiecare data — nu impartite. Nu exista nicio logica de distributie:ORDINEnu apare in tot pachetul. Deci pasul 2 nu trebuie doar sa gaseasca un rand cuSCC-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 PRODUSEcu 6310 articole siLISTA PRETURI LEIcu 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 perSCC— modelulgnId_pol_pret_stocextins 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
704e de lista de preturi, nu de rezultat contabil — majoritatea trimit la aceeasiid_nota = 1, deci toate dau704/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_poldevine populat acolo unde azi ar fi gol. Nu s-a gasit cod care sa presupuna „id_polgol = articol fara politica", dar nici nu s-a cautat exhaustiv.- Doua politici active nu au
ID_NOTA(32 HOTEL TAXE,33 HOTEL CAZARE) —id_polvalid, nota absenta. Ce facecontabilizeaza_articolin 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:
- reteta in 4 pasi (decizia 27-bis respectata,
pack_facturareneatins, dar cere politica tehnica perSCCsi o interogare inversa noua); - 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); - 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. Politica7 DISCOUNTapare pe 870 de linii de comanda si trimite la nota2 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 perSCC. -
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 ocoleascaFACT-024. Patru rezultate:- Cifra 37 se reconfirma, dar numitorul din plan era gresit —
6868era alt numitor; cel corect pentru linii cuID_POL NOT NULLe 7108. - Premisa „37 de linii ar cadea la facturare" era partial gresita. Un rand cu
CANTITATE < 0nu dovedeste ca linia a fost facturata: singurul loc care il insereaza einchide_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, articolul4294507522(„VOUCHER DISCOUNT") pe politica7(„DISCOUNT"), in facturaID_VANZARE = 1028,20.03.2026. Si codul chiar a rulatcontabilizeaza_articolpentru ea: apelul e in ramuraELSEa luiCASE pack_facturare.ntipdinscrie_factura2, care exclude doar transferurile, avizele de custodie si facturile cu rate — tipul 3 cade inELSE. 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_ARTn-are coloanaSTERSsi 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/DATAORASsi 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 cuFACT-024daca ar trece acum princontabilizeaza_articol(verificat pe view-ulVCRM_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. - Cifra 37 se reconfirma, dar numitorul din plan era gresit —
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_unitarapare pe rapoartele.frxsi 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_coloanaal grupului pe care il reduc, nu doar cota — cheia de grupare are cinci campuri siexpltvase completeaza tot prinid_jtva_coloana. O implementare care seteaza doarproc_tvalasa grupul orfan exact unde e azi; - eFactura nu are nevoie de nicio modificare —
xmlefactura.prg:758-792grupeaza dejaGROUP BY proc_tvasi emite cate unAllowanceChargeper cota.
Doua consecinte vizibile daca se implementeaza: factura tiparita va arata N randuri „Discount X % Factura" in loc de unul, si nota contabila primeste TVA-ul discountului spart pe cote.
Decizia a fost luata — decizia 59 (runda 16): repartizare proportionala pe cote. Vezi sectiunea „Decizia 59" de la deciziile rundei 16, cu toate consecintele de implementare.
Cele doua puncte ramase s-au inchis si ele, tot in runda 16. Campul text optional de motiv —
decizia 61: da, se adauga (AllowanceChargeReason, ReasonCode ramane 95), cu stocare noua
pe VANZARI si un control nou in formular; ramane deschisa doar asezarea lui pe rand. Retroactivi-
tatea la relistare/retrimitere — decizia 62: restrictia de modificare e strict EsteInEFactura,
fara legatura cu nicio data; ramane insa deschis, separat, cazul relistarii (nu editarii) unei
facturi vechi deja trimise, vezi decizia 62.
M. Valuta si data cursului (decizia 15)
Cele doua concepte exista in cod, distinct — decizia 15 nu introduce o distinctie noua, o face vizibila:
- (a) factura in valuta:
poDate.in_valuta/Curs/multiplicator/id_valuta, un singur curs pentru tot documentul; - (b) articol cu pret in valuta pe document in lei:
poArticol.tip_valuta = 1, proprietate a politicii de pret, independenta dein_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) cheamacaut_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 inpoDate.listaid, numerele inpoDate.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 dinVANZARI_DETALIIliniile facturilor alese si le pune in acelasicrsarticolepe care celelalte tipuri il umplu cu lista de preturi (ofacturare.prg:306-311). Confirmat exact ce spunea Marius:ID_GESTIUNEvine din linia originala (:4034,:4055),PRET_ACHIZITIEla fel, neschimbat (:4035,:4056), iar pretul de vanzare se recalculeaza pe curs doar daca moneda nu e nationala (:4016-4030).GESTIONABILeNVL2(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_retursi aceleasi ramuri; difera doar antetul —nIdTipDoc = 6sifrm_date_avizin loc defrm_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 FROMca 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).
- 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 (
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 prinPACK_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 catremodifica_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 fiecareText1.
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_articolepevnom_articole_toate(COMUN\programe\ocautare.prg:1781-1823), filtratin_stoc = 0 and in_crm = 1si 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_adauganu completeaza pretul si cantitatea; operatorul le pune in grid, iardo_modifica_alteservrecalculeaza 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 dincrsalteservse insereaza dupa, cuid_articolreal, si ajung ca atare inVANZARI_DETALII_TEMP(:1036-1041,:1240-1257). - Gestiune: tot zero.
id_gestiunenu e completat de niciunINSERTsi pleaca0spre Oracle (:1201-1206,:1255) — consistent cu filtrulin_stoc = 0. Deci nici articolele reale din ROAAUTO nu descarca gestiune. Diferenta fata de liniile sintetice e doarid_articol. - Stergere si modificare: exista, dar doar cat timp formularul e deschis, inainte de emitere
(
do_sterge,:6730-6741). - Contul pleaca gol.
crsvanztempare coloanaCont c(4), darINSERT-ul care il umple nu o include (oproceduri_devize.prg:1190-1206); apelul trimite literal''(:1256), care ajungeNULLinVANZARI_DETALII_TEMP.CONT, faraNVLsi 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_pollipseste peste tot pe firul asta (nu e nici incrsalteserv, nici incrsvanztemp, nici in semnatura luiadauga_articol_factura_deviz). Motivul pentru care asta nu produceFACT-024e ca fluxul ROAAUTO nu trece princontabilizeaza_articol: merge pescrie_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_articoleste apelata (scrie_factura2,ff_...:6150), s-ar lovi deFACT-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:
Ce se intampla cu totalul devizului— RASPUNS (runda 7), raportdocs\cercetare\pack_auto_actualizeaza_deviz.md. Premisa intrebarii era gresita: nu exista niciunSUMpesteVANZARI_DETALII, pentru caPACK_AUTOnu citeste delocVANZARI/VANZARI_DETALII— zero potriviri peVANZARIin 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 peDEV_ORDL, stampileazaID_FACTpeRUL, si — doar pe anumiteid_set— peNOM_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 petip = -12nu 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 caactualizeaza_devizsa faca distinctie. Ce ramane, si e de alt fel: o desincronizare de afisare. Totalul devizului aratat in ROAAUTO se calculeaza dinRULsi din cursoarele de facturare (oviz_devize.vc2:7816-7823), niciodata dinVANZARI_DETALII— deci nu va arata linia adaugata, nici azi, nici dupa #13. In schimb relistarea o va arata:relisteaza_factura_devizcitestefact_vfacturi_detalii(oproceduri_devize.prg:1564), un view neconditionat pesteVANZARI_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.- 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. - Cine ramane proprietarul documentului dupa ce ROAFACTURARE ii adauga o linie — mai poate
ROAAUTO sa-l relisteze corect (
relisteaza_factura_devizciteste dinfact_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:
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.- 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.
do_copiaza, ramura de avize: variabila gresita. Laofacturare_comun.vc2:3702-3703, ramuraCASE INLIST(loFactura.tip, T21, T24, T26, T30, ...)scrielnTip = T22in loc deloFactura.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.- „Proforma -> factura” merge prin omisiune. Copierea nu propaga
nIdTipDocpentru 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. - Asimetria de resetare a globalelor — vezi „Canalul de precompletare”, mai sus.
- Modificarea in bloc pare sa goleasca campuri pe care nu le-ai atins. Pe selectie multipla
(
lnNrInreg > 1),do_modificafaceScatter Name poRec Memo Blanksi reseteaza explicit ruta, delegatul, agentul, masina,dataora_exp,id_facturare,listare_detaliata,tip_saft,text_aditionalsiefactura(ofacturare_comun.vc2:4565-4580). Cum acesti zece parametri se scriu neconditionat inVANZARI(I-bis), unSCANpeste 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 dindocs\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:
- Implementare (delegata unui subagent, conform modului de lucru din
CLAUDE.md). - Teste — rulate, trecute, cu rezultatele raportate.
- Diff ca fisier in
docs\(diff_s<N>_<subiect>.patch), inclusiv partea dinCOMUN. - 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 — encodingcp1250la.vc2/.sc2,GOpeRecno(),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. - Aprobarea lui Marius.
- 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_terminraman in banda de titlu.
Cota de TVA a discountului de document s-a decis — decizia 59, runda 16 (vezi sectiunea K-bis):
repartizare proportionala automata pe cote, discountul NU cere cota de la utilizator, deci nu
e nevoie de o a treia sectiune pentru cota. Ce mai cere UI e un camp text optional de motiv
(pentru AllowanceChargeReason), mult mai mic decat o sectiune. Decizia 61 (runda 16) l-a
materializat: Marius vrea campul. Decizia 64 (runda 16) a inchis si asezarea: randul se imparte
in trei — incasare, alte date si motivul discountului, pe acelasi rand (varianta grea, cota +
explicatie proprii, ramane exclusa, ca mai sus).
Pagina cu variantele: https://claude.ai/code/artifact/dfc70d38-856f-48fd-b6af-3da78e9da4f7
— nu mai are copie pe disc (cerinta lui Marius, runda 15: „doar online, ca sa nu mai intretii 2
variante"). Procedura de modificare: WebFetch pe URL → fisier de lucru in scratchpad, nu in
docs\ → Artifact cu url = link-ul de mai sus.
S2 — O singura procedura factureaza
Se elimina factureaza2 ca procedura paralela; factureaza primeste un mod
(gnFacturareNou ramane comutatorul de rulare, dar nu mai duce la alt cod duplicat). Se pastreaza
integral ramurile care lipsesc azi din factureaza2 (lista din A).
PROIECTAT (runda 9) — docs\cercetare\s2_factureaza_unificare.md.
S2 e mult mai ieftina decat arata planul. factureaza2 nu e a doua implementare functionala care
cere fuziune atenta — e un fork din 08.06.2017 la care executia interogarii de articole e dezactivata
(lnSucces = 1 hardcodat, goExecutor.oExecute niciodata apelat) si mai multe blocuri intregi sunt
inchise cu If .F.. Are exact un apelant in tot codul — ofacturare.prg:90, din interiorul lui
factureaza, in spatele unui AMESSAGEBOX de confirmare — deci zero utilizatori reali. Ambele sunt
proceduri globale in COMUN\programe\ofacturare.prg (:81-577 si :583-1081, ~500 de linii fiecare),
fara omonime. Singura diferenta functionala care justifica existenta lui factureaza2 e formularul de
articole (frm_facturare_articole2 vs frm_facturare_articole).
Obstacolul nu e tehnic: formularul spre care duce e tot un prototip neterminat, deci unificarea
procedurilor nu face formularul nou utilizabil — doar elimina duplicarea. Aia rămâne treaba lui S3.
Trei corectii la lista din sectiunea A: verifica_numar(16, ...) pentru chitanta nu lipseste din
factureaza2 (e identic, :502-504 vs :1018-1020) — planul greseste; „cursor_avize cu tip 23" e
imprecis — tipul 23 nu lipseste, e rutat diferit (cursor_preturi in factureaza vs
cursor_gestiune in factureaza2), ceea ce e mai grav decat o omisiune; si lipsesc din plan tipul 52
pe ramura de contract si tipul 24 pe ramura de retur. Semnatura propusa, ramificarea interna, ordinea
in care se face fara sa strice suita si criteriul de „gata" verificabil sunt in raport, sectiunile 5-7.
ofacturare.prg e cod comun al intregii suite (copie in fiecare produs ROA, sincronizata manual) —
la fel ca S3c, nu se face impreuna cu alta modificare.
Gata cand: factureaza2 nu mai exista, iar comutatorul alege formularul, nu procedura.
Depinde de: S1.
S3 — Portarea logicii de antet in formularul unificat
Cele 17 + 13 metode do_cauta_*, do_schimba_tipdoc, validarile din inainte_de_do_termin
(ofacturare.vc2:9455-9561 si :7277-7352) si Init-urile. Se porteaza o singura data, cu
ramificare pe factura / aviz in interior, nu doua copii.
Punct de atentie cunoscut: #16 din todos.txt — la revenirea din formularul de curs valutar,
focusul sare pe tip document si iesirea din serie regenereaza numarul actului.
PROIECTAT (runda 9) — docs\cercetare\s3_portare_antet.md. Doua rezultate schimba povestea:
- Portarea „o singura data cu ramificare" nu e posibila ca atare.
Init,inainte_de_do_terminsi partialdo_cauta_fdocsunt 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 eDo Case/amessageboxmecanic — ci reconcilierea a patru cicluri de viataInit/inainte_de_do_terminindependente intr-unul singur, cu ordinea de dependente din sectiunea 5 a raportului. Numarul real dedo_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 cudo_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 dinInitdupa 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 acelasiactualizeaza_tipincasare()ca.Click(:3250-3251). Consecinta neasteptata: azi, deja,Init(:3150) seteaza singuropt_incasat.Value = 2cand documentul soseste cupoDate.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 acelasipoDatepersistent, o repopulare naiva a starii ar aloca si dezaloca la fiecare toggle. Solutia proiectata: populare o singura data, iar plierea strict peVisible/Height— niciodata pe reasignare de.Value. Criteriul de non-alocare e formulat ca assert headless pepoGeneratorNumere, 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 prototipulfrm_facturare_articole2. Cel mai apropiat tipar din suita efrm_modific2024.afiseaza_rulaje(omodificari.vc2:13169-13199, perimetrul #6 — citit, neatins), acelasi idiom sus/jos validat deja pentrubut_modifica/but_salveazala 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 cereReadOnlyexplicit 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:
goContractnu e scris nicaieri in ROAFACTURARE — nici in codul produsului, nici inCOMUN-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.goComandachiar 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,
goContracte bufferul de editare al intregului ecran de contracte (peste 100 deControlSourcelegate 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 laoDateFactura.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.
crsarticolenu e doar sursa de populare a gridului — e un registru al cantitatii ramase de facturat. E citit si scris dedo_adauga_tot,do_stergesido_scrie_facturapentru toate tipurile cu document sursa: stergerea unei linii reface cantitatea incrsarticole(ofacturare.vc2:14640-14669), iar la scriere se faceCalculate Sum(cantitate) To lnCantitateRamasapeste 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 dincursor_contractsicursor_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 cacrsarticoleera tratat ca un registru omogen; sunt de fapt doua mecanisme distincte (Rol A / Rol B), si niciunul nu cere cursorul incarcat. Vezi mai jos.combosqle 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 doarcodmat/denumire/id_articol, pentru ca sursa lui (vnom_articole) n-are pret, TVA, valuta,id_pol,gestionabil. Cautare inCOMUNROAsiROAGEST: 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_contractproduce deja doua cursoare —V_CURSOR(crsarticole, prin delegare lacursor_preturi) siV_CURSOR2(crsarticole1, articole sau rate). Filtrarea se aplica curat doar pe jumatateacrsarticole; randurile de rata n-auid_articolsi 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_valutee azi neconditionata in procedura, deci pe varianta filtrata s-ar declansa la fiecare cautare de articol in loc de o data la deschidere — cu-20005posibil 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_coloanachiar lipseste dincursor_preturi, dincursor_gestiunesi dinV_CURSOR2al luicursor_contract, pe toate ramurile — confirmat pe SQL. Nu e bug:do_initializeaza_articolpune0, iarfrm_articol_factura.Init(mostenit si defrm_articol_gest_factura) il rederiva mereu dinproc_tvavviajtva_coloane, pe orice linie adaugata prindo_adauga_articol. Cerinta care rezulta pentru S4: derivarea tine numai daca linia trece prindo_adauga_articol.combosql-ul prototip de azi (ofacturare.vc2:19288-19306) faceREPLACEdirect incrsfacturasi ocoleste toata derivarea; daca S4 extinde acelREPLACEfara sa treaca prindo_adauga_articol,id_jtva_coloanaramane nederivat si Oracle (adauga_articol_factura, ramuraELSE) arunca-20000 FACT-013sau scrie cota gresita. Bug nou posibil, introdus de S4 — intra ca cerinta explicita in proiectare, nu ca observatie.but_urmator_tot1areVisible = .F.la design (ofacturare.vc2:11265); tipurile 23 si 41 auCasepropriu, dar niciunul nu-l face vizibil — confirmat, nu presupus. Nu exista alt mecanism de „adauga tot"; exista insabut_urmator1(adaugare rand-cu-rand), neconditionat de tip, care merge prin acelasido_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 cheamacursor_gestiune; tipul 23 cheama de faptcursor_preturi(grupat cu lista de preturi). Tipurile 45, 48, 49 nu ating deloccursor_gestiune(45 →cursor_preturi, 48/49 →cursor_articole_k, alta procedura). Doar pe prototip (factureaza2, opt-ingnFacturareNou) 23 si 41 merg impreuna pecursor_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:
- varianta filtrata a cursoarelor + completarea liniei din
combosql(partea proiectata in raport); - 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_facturasi 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.cantitateare DOUA roluri, nu unul — si numai unul e „registrul" temut. Rol A — cantitate ramasa de facturat dintr-un document sursa (comanda3,21,25,28,42,47; avize4). Rol B — plafon de cantitate in sesiune (lista de preturi gestionabila1,22,29si jumatatea de contract, transfer23,41, retur8,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_facturasumeazacrsarticoledoar ca sa decida ce trimite inpnParametruAditional, iar Oracle recalculeaza independent, din tabele reale, ininchide_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 dinCOMENZI_ELEMENTEvsVANZARI_DETALII;inchide_comanda()insereaza un rand compensator, nu seteaza un flag. Pe comanda,pnParametruAditionale 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.FACTURATe un flag persistat, iarV_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 curamas = 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
crsarticoleagrega mai multe avize, suma globala poate da0desi 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). 23si41nu apar deloc inCASE-ul de finalizare — cad peELSE, fara nicio inchidere: transferul n-are document sursa de inchis, doar plafon (Rol B). Contractul (2,6,52) intra pescrie_rate_factura, care nu e o inchidere, e alta operatie.- Recomandarea: (b) pentru Rolul A, (c) pentru Rolul B.
(b) cele doua
Calculate Sumdindo_scrie_facturase inlocuiesc cu un apel Oracle facut dupado_scrie_articole()(candVANZARI_DETALII_TEMPcontine exact liniile pe cale sa fie scrise) si inainte deDo Case-ul care alege procedura de scriere. Cele doua functii Oracle noi sunt o extragere a interogarii pe careinchide_comanda/marcheaza_facturato 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 incrsfactura— 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 pe23,41, presupunand ca n-au bookkeeping — au Rol B, confirmat pe cod. Punctul 1 nu se poate aplica pe23,41inainte ca punctul 2 sa acopere Rolul B pe ele.
Toate trei sunt INCHISE (runda 13). Nu se mai reiau.
Asimetria din— azi, pe grupuldo_modifica1,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".Corectia pe— INCHIS — decizia 46 (Marius, runda 13): asteapta punctul 2. Punctul 1 nu se redeschide acum; aplicarea lui pe23,4123,41vine odata cu recalculul pe server, care acopera Rolul B. Nu se pierde nimic: ordinea de implementare oricum le pune dupa.ContractINCHIS de proiectarea S5 (26,52: nu s-a gasit dovada nici de prezenta, nici de absenta a Rolului B.docs\cercetare\s5_acoperire_tipuri.md):26si52n-au niciun bookkeeping, nici Rol A, nici Rol B — excluderea e totala, peDo Caseexhaustiv 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:
- Butoanele de linie deasupra gridului, cu eticheta, nu iconite mute: linie noua (
but_nou), sterge linia (but_sterge), detalii linie. - Un singur buton „Adauga articole” cu
xmenu(), cu optiunile din tabelul din J. InlocuiesteBut_urmator_tot1(azi fara caption si faraToolTipText) 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. - 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 niciunCasealDo Case-ului (ofacturare.vc2:15109-15248) — deci pierde si titlul, si eliminarea coloaneicSerie, nu doar butonul „tot". - Nu se porneste de la tiparul RORIS.
frm_tranzite specific ROAACNPRO si ar insemna formular nou; ROAFACTURARE are dejacauta_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.
crsarticole1are doua ramuri disjuncte in SQL —OPT_FACTURARE = 3(articole reale) siOPT_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_totparcurge azi exclusivcrsarticole, niciodatacrsarticole1. 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
.fr2de factura / proforma / invoice dinCOMUN\Rapoarte: zero potriviri pe „disc". Singurele doua.fr2cu „DISCOUNT" sunt de NIR, nu de facturi emise. Se tiparestepretftvasivalftva, deja nete de discount (prelucreaza_factura,ofacturare_comun.prg:1055-1059,:1156-1248, in cursorulcrsfacttemp). - eFactura foloseste exact acelasi cursor (
xmlefactura.prg) —LineExtensionAmountsiPriceAmountsunt aceleasi valori nete, cu optiunea (dezactivata implicit) de a adauga si uncac:AllowanceChargeinformativ 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 cuDISCOUNT_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_articoletrimite spre Oracle direct dindiscountftva/discountctva/vdiscountftva/vdiscountctva(ofacturare.vc2:14081-14083, ales pecu_tvasitip_valuta) — verificat pe cod, nu presupus. DeciVANZARI_DETALII.DISCOUNT_UNITARiese mereu corect, indiferent de starea campurilor agregate. - Riscul e strict local, si e o inconsistenta, nu o valoare veche.
valdiminuatftva/valdiminuatctvasunt agregate citite deprelucreaza_facturadin acelasicrsfacturadin memorie, nereincarcat din Oracle. Netratate, factura tiparita poate iesi cupretftvacorect sivalftvainconsistent — pret x cantitate diferit de valoare, ceea ce se vede pe hartie. - Exista deja azi calea care demonstreaza gaura: editarea
vdiscountftvain 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) pluscalculeaza_totaluri()(oproceduri_facturare.prg:2258-2381), care ruleaza peScatter Name, fara dialog. - Evenimentul recomandat pe coloanele noi e
Text1.LostFocus, nuInteractiveChangesauValid— tiparul e deja folosit in acelasi fisier, pefrm_avizare_lucrare.grd_articole.cCantitate/cPret(:6549-6562). - Punctul deschis din S1 e inchis in trecere:
_checkbox1sichkDetaliatsunt doua controale distincte infrm_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_cursprimeste data documentului chiar inoDateFactura.Init/Reset(COMUN\programe\ofacturare_comun.prg:247,:496), inainte ca formularul sa decida ce ascunde. Nicaieri codul nu golestezi_curscand 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), undeclb_zi_cursse elimina neconditionat si documentul se salveaza corect — in principal pentru cacursor_returnici nu folosestepoDate.zi_curs. - Linia
:8076nu e in formularul de factura. Apartine luifrm_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. Valideazazi_curspentru ca tipul 27 are nevoie de curs pentru articolele din comanda, independent depoDate.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_curspropriu, editabil, legat lapoDate.zi_curs(ofacturare.vc2:16285-16305) — se schimba doar cand e vizibil. - Mesajul
-20005contine DEJA data si numele valutei lipsa (STRINGAGGpeste 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 ins3_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 tiparullb_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 FROMpeste cursorul sursei ar strica doua lucruri pe comanda, ambele tacut: (1)id_ceROWNUMper executie Oracle, deci randurile din lista de preturi ar coliziona cu cele ale comenzii, iardo_stergeajusteaza cantitateaFor id_c = poArticol.id_c(:14652-14655) — ar atinge randul gresit; (2)do_scrie_facturafaceSum(cantitate)pestecrsarticolepe exact aceste tipuri (:14332-14338), iar incursor_preturicantitateinseamna 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), iarcursor_contractemiteid_ccarownum - 10000(PACK_FACTURARE:2722— confirmat direct pe export de sesiunea principala), adica autorii au tratat coliziunea deid_cca risc real. In plus, tipurile de contract nu au deloc ramura cuSum(cantitate)indo_scrie_factura— cad peOtherwise. 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 incrsfacturaprinAPPEND BLANK+combosql— tipar deja existent infrm_facturare_articole2.do_adaugasi pe linia de discount (ofacturare.vc2:14531). Atunciid_cramane0implicit, sido_stergedevine no-op prin constructie, fara nicio modificare de cod. - Se aplica identic pe AVIZE (tip 4), nu doar pe comanda —
cursor_avizeare aceeasi semantica „ramas de facturat" si aceeasi ramura deSum. 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 coliziuneid_cramane (do_stergeajusteaza cu semn opus, urmarind maximul returnabil), desi fara poluarea sumei de inchidere. Deci S4f foloseste acelasi mecanism, nuAPPEND FROM. - Validarea de cantitate pe liniile din comanda e satisfacuta prin constructie — drumul
do_adauga_articol→do_verifica_articolnu e atins, fiindca liniile libere nu modificacrsarticole. - 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_articolnu se poate refolosi ca atare (cere unpoArticolscatter-uit dintr-un cursor sursa incarcat). DECIS — decizia 44 (Marius, runda 13):poArticoldevine parametru explicit. Se pastreaza un singur loc de validare:do_verifica_articolprimestepoArticolca parametru, in loc sa citeasca variabilaPrivatepopulata 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 sido_alege_stoc/frm_articol_gest_facturadin 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:
- mutarea lui N.1 ca sursa in meniul de adaugare, fara sa se atinga popularea — acelasi dialog,
aceleasi filtre, acelasi
cursor_retur; - ridicarea lui N.2 (
But_retur) la nivel de document, dupa modelul lui N.1, cu calea per-articol pastrata; - lista de preturi pe documentele de retur, prin acelasi
APPEND FROMca 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) folosesteV_LISTAIDdoar ca filtru (WHERE A1.ID_VANZARE IN (...),:4054-4055); in lista de coloane aSELECT-ului extern (:3965-4028) nu apar niciID_VANZARE, niciID_VANZARE_DET.crsarticolenu are de unde sti din ce factura vine randul. -
INSERT-ul inVANZARI_DETALIInu 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, dinfinalizeaza_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_CORESPda 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(perechiID_ARTICOL:ID_VANZARE) e folosit inpack_facturare(ff_...:8142-8212) exclusiv ca filtru peRUL, 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, subIf 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 niciodataid_c = 0dinROWNUM, deci valoarea implicita e ea insasi semnalul. Vine din mecanismul impus de S4e: liniile libere intra direct incrsfactura(APPEND BLANK+combosql), nu incrsarticole— asa ca ajustarea dindo_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_facturadoar dintr-unpoArticolscatter-uit dincrsarticole. O linie libera dincrsfacturanu 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 decrsarticole—, deci merita o singura solutie, nu doua. INCHIS prin decizia 44 (runda 13):poArticoldevine parametru explicit si aici, nu variabilaPrivatepopulata de apelant — aceeasi solutie ca in S4e, cum cerea raportul. -
R4 e inchis (verificare independenta):
scrie_corespondente_vanzari(3)e gatata pentip IN (8,9), nu pelistaid. -
VANZARI_CORESPnu e afectata de liniile libere — se scrie dinpoDate.listaid, fixat la antet. De confirmat (R4): pentruBut_returridicat la nivel de document, raspunsul pare a fi „fara corespondenta persistata", pentru ca scrierea e gatata pentip IN (8,9), nu pelistaid.
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_poleN(20) Null(COMUN\programe\ofacturare_comun.prg,creeaza_facturacrs— verificat direct), deci laAPPEND BLANKramane.NULL., si ajunge la Oracle ca literalulNULL(ofacturare.vc2:14072). Cursorul de cautare din nomenclator n-are deloc coloanaid_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 NULLintra pe ramura noua; daca in acel momentid_pole si el populat, se ridica eroare (cod nou, distinct deFACT-024, care ramane pentru cazul „nici politica, nici cont"). Variantele „politica castiga" si „fallback laNO_DATA_FOUND" au fost respinse motivat: prima defineste castigatorul pe prezenta campului, nu pe rezolvarea lui, si ar reintroduceFACT-024acolo unde VFP a oferit deja o solutie; a doua e semantic cea mai curata, dar cere restructurarea interna a functiei (un flag propagat dinEXCEPTIONpana la punctul de decizie), fata de un singurIFla 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_veniteNULL— adica toti apelantii existenti, care nu cunosc parametrul —, executia intra direct peELSE, 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, nuNULL, nu refuz.pack_autonu mai e blocant —PACK_AUTOnu citesteVANZARI/VANZARI_DETALIIdeloc (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 dinfrm_modific2024e 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_articolprimeste contul prinVANZARI_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_VENCHELTpentru gestionabile (pe contul de gestiune al liniei),NOM_ARTICOLE.CONTdaca e 6xx/7xx pentru negestionabile, altfel 704. Ruleaza doar pe liniile faraid_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+combosqldirect incrsfactura, niciodata incrsarticole. - Ramane deschis, mostenit din S4e: validarea de cantitate/stoc pentru linia libera — se rezolva
prin decizia 44 (
poArticolca parametru explicit), aceeasi solutie pe ambele surse. - De re-rulat inainte de implementare, nu de presupus incheiat: cautarea apelantilor lui
adauga_articol_facturain 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 prinnproc_tva_max, pe linie scutita + discount global), numele cheii de optiune de firma pentruSCD(decizia 36) si domeniul eiPROGRAME, si trasabilitateaCONT_VENITpeVANZARI_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 detip(in 14 ramuri). Patru tipuri reale, reachable prinfactureaza(), nu intra in niciunCase:45(factura restaurant),48/49(custodie),52(contract, factura fiscala valuta) — pierd titlu, cap de coloana, mesaj de stoc, vizibilitatea discountului si eliminarea coloaneicSerie. Nu doar52, cum semnalase S4b. Rutarea cursorului le recunoaste (ofacturare.prg:271-282); doarInitnu le-a „prins" niciodata. Nota de executie (decizia 60): randul de configurare pentru48/49trebuie sa pastreze restrictia la articoleIN_STOC = 0mostenita 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 arboreleD:\ROA.50e 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 (lipseste24). Nu seteaza titlu (nu existalb_titlu_alb_b121in tot prototipul), nu schimba capul coloanei, nu schimba mesajul de stoc, nu ascunde discountul, si n-are deloc conceptul de coloanacSerie. 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 deInlist(tnTip, 2, 26, 6, 52)pe standard (:283) — verificat direct de sesiunea principala —, iar ramura de retur omite24. Pe aceste tipuri, prin prototip,lcSqlCursorar ramane nedefinit: eroare, nu doar comportament diferit. - Punctul lasat deschis de S4 punctul 2 se inchide aici: tipurile
26si52n-au niciun bookkeepingcrsarticole— nici Rol A, nici Rol B. Excluderea e totala, peDo Caseexhaustiv fara ramura implicita, deci e „dovedit absent", nu „neconfirmat". 30nu e un formular separat, cum spunea planul: e acelasifrm_facturare_articole, trecut prin acelasiInit, dar niciodata aratat (ofacturare.prg:444-453— calculeaza totalurile, apasa programaticbut_termin1.Click(), apoiRelease(), faraShow()). Deci e afectat de golurile dinDo Caseca oricare alt tip; doar ca defectele nu se vad pe ecran.27chiar 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 aziDo Case-ul (titlu, cap cantitate, mesaj stoc, discount vizibil, are serie, tip doc, butoane, grup-sursa pentru meniul S4b). Motivul nu e estetic: unDo CasefaraOtherwisenu 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 unSELECTfata detipuri_documente_facturare.md, fara sa porneasca formularul. Variantele „completeazaDo 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
DBFstatic (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 unSELECTdin afara. Ce se accepta: orice tip nou de document cere recompilare si versiune noua de exe, ca azi. Forma concreta: un.prgcuCREATE CURSOR+INSERT-uri, un rand per tip, incarcat o singura data si citit deInitprinSEEKpetip.
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_fdocramane in antetul unificat, cu realocarea de serie si numar la comutare; - garzile
eProforma = 0de 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_copiazaramane 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 sentinelaid_gestiune = -1000, iar pe Oraclecontabilizeaza_articolsare apelul catredescarca_gestiuneexact 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 zeroizareagestionabilar fi doar cosmetica. Consecinta pentru S5b: sentinela-1000e 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 = 1alegescrie_proforma, care nu cheama niciodatacontabilizeaza_articol— comentariul din cod o spune direct: „salveaza doar in vanzari, nu si in contabilitate" (verificat de sesiunea principala). Sentinela-1000e 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 alegescrie_factura2, care chiar cheamacontabilizeaza_articol— dar acesta saredescarca_gestiuneexact pe sentinela-1000(PACK:7472-7476). Rezultatul: o factura reala iese cu stocul nedescarcat, silentios —-1000e 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 separatfrm_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
UPDATEde 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_cla copiere: SIGUR, cu dovada.do_copiazadegradeaza tipul spre grupul-tinta{1,5,7,10,22,23}, care nu intra niciodata in ramurile Rol A dindo_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 primulCASEal luifrm_facturi.do_copiaza(COMUN\clase\ofacturare_comun.vc2:3693-3694), care lasa neatinse exactT1,T5,T7,T10,T22,T23. Concluzia „copierea e sigura" nu se schimba —7si23nu au bookkeeping Rol A —, dar cifrele se corecteaza peste tot unde apar. Aceeasi corectie se aplica luis5b_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 scrielnTip = T22(COMUN\clase\ofacturare_comun.vc2:3703) in loc deloFactura.tip = T22— singura ramura din totDo Case-ul care nu atribuie in obiect; toate celelalte cinci scriuloFactura.tip. Consecinta: la copierea unui aviz (21,24,26,30,-7,-9,-10,-13,28,29,42) degradarea nu se produce, iarcopiere_facturacheamafactureaza(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 cafactureazanu citeste unlnTipprivat (ofacturare.prg:101declaralnTipTemp, nulnTip), deci atribuirea chiar se pierde; in pluslnTipe nedeclarat in metoda, deci poate suprascrie unlnTipal 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-336faceUPDATE (lcCursor) SET gestionabil = 0candeProforma = 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). DeciDo Case-ul de laofacturare.vc2:13803vede mereugestionabil = 0→do_alege_stocnu ruleaza niciodata. Valabil pe toate cursoarele de creare directa a unei proforme; niciunul n-are parametruV_PROFORMA(verificat pe ~9 proceduricursor_din pachet). - Mecanismul Oracle exista, dar e mort in fluxul curent:
cursor_retur_document(PACK:3993-4000) areCASE V_PROFORMA = 1 THEN 0, insa se cheama doar la copiere, undeV_PROFORMAtrimis eeProformaal documentului nou — mereu0. Nu e o contradictie intre rapoarte: sunt doua mecanisme reale, doar unul activ. pret_achizitiedepinde de sursa: pe calea principala (lista de preturi) ramane0—cursor_preturinici nu-l selecteaza. Pe surse care carata un document existent (avize, copiere) vine real dinVANZARI_DETALII.PRET_ACHIZITIE.- Daca liniile ar pastra gestiunea reala pana la salvare, nu s-ar strica nimic pe contabilizare sau
stoc:
adauga_articol_facturase cheama oricum si pentru proforma, darscrie_proformanu cheamacontabilizeaza_articol, decidescarca_gestiunetot n-ar rula; iardo_alege_stocnu rezerva stoc (doarSELECT+ scadere locala in memorie, zero scriere Oracle). Singurul loc unde s-ar vedea o diferenta e la relistare (crsDetaliiListare/fact_vfacturi2citescid_gestiunefara filtru peeproforma) — 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
(
gestionabilfortat0), 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_stocar 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 = 0fortat 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":
- poate fi azi o proforma aleasa ca sursa de copiere, sau e exclusa undeva?
do_copiazadegradeaza tipul spre{1,5,10,22}— ce tip rezulta dintr-o proforma si e cel dorit?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 dinfinalizeaza_factura, care scrie inVANZARI_CORESPperechea(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/2aviz-factura,TIP = 3retur), deci structura nu se schimba — se adauga o valoare noua deTIPpentru proforma → factura. De proiectat, nu de presupus: ce valoare deTIPse aloca; de unde stie fluxul de copiere ca sursa a fost o proforma (azido_copiazadegradeaza tipul, deci informatia s-ar putea pierde inainte de scriere — vezi punctul 2); si dacamarcheaza_facturat/FACTURATtrebuie 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.
IsCopyreturneaza 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 laeproforma. 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 = 4e liber inVANZARI_CORESPsi 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 doar1/2/3. Structura nu se schimba. - Capcana (i) — confirmata, si e miezul poveștii.
id_vanzareal proformei supravietuieste copierii (prinpoDate.listaid), dar faptul ca sursa era o proforma se pierde:completeaza_setari_documentcopiaza.listaid, dar nu sieproforma(COMUN\programe\ofacturare_comun.prg:387— verificat direct:eproformanu apare nicaieri in tot fisierul). De aceea legatura nu se poate agata deCASE-ul dinfinalizeaza_factura, care e cheiat pentip—ntipnu poarta distinctia. Solutia: un semnal nou, client-side (poDate.lProformaSursa), capturat exact acolo unde informatia mai exista, plus un apel Oracle explicit separat care reutilizeazascrie_corespondente_vanzari(4)neschimbata. Zero cod PL/SQL nou pe calea recomandata. - Capcana (ii) — INFIRMATA presupunerea comoda, cu patru argumente:
marcheaza_facturatNU se cheama pe proforma. Proforma n-areVANZARI_CANTITATI, n-are ramura de reversare la stergere insterge_factura, nimic nu filtreaza dupaFACTURATpe proforme, si oricum calea aleasa e in afaraCASE-ului care il cupleaza azi. Era exact detaliul care se copia gresit din tiparul avizului.
Ce NU se schimba: IsCopy, vizibilitatea butonului de copiere, filtrul de grid, degradarea de tip
din do_copiaza, cursor_retur_document (GESTIONABIL = B.IN_STOC), si scrie_corespondente_vanzari
insasi.
De decis de Marius (cinci puncte, niciunul blocant):
TIP = 4— liber azi, dar alocarea e ireversibila in date odata intrata in productie. De confirmat explicit, nu tacit.- 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. - 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. - Garda simetrica la stergere („nu poti sterge o proforma care are deja factura generata din ea"),
pe tiparul
TIP IN (1,2,3)dinsterge_factura— de decis daca se doreste. - 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) verificagnFacturareNousi, 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.gnFacturareNoue o optiune de firma:optiuni_firma(COMUN\programe\oinit_optiuni.prg:225-275) cheamaSCRIE_OPTIUNI(gcUserName), parcurgev_optiunisi declara dinamic globalele publice dupa tip (Public gn&lcvarnamepentruNUMERIC), 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 doarfactureaza2, un fork mort din 2017 (executia interogarii dezactivata,lnSucces = 1hardcodat, 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.
poArticoldevine parametru explicit al dialogurilor de linie (do_verifica_articol,do_alege_stoc/frm_articol_gest_factura), in loc de variabilaPrivatepopulata 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)- Asimetria din
do_modificase lasa sa se corecteze de la sine prin recalculul la cerere, dar corectia se declara explicit in changelog. (S4, punctul 2) - Corectia pe tipurile
23,41asteapta punctul 2 (recalculul pe server, care acopera Rolul B). Punctul 1 nu se redeschide acum. (S4, punctul 2) zi_curs: simetrie. Campul se ascunde la loc la stergerea ultimului articol in valuta; „clipitul" e acceptat ca pret al unei reguli unice. (S4d)- Tabelul de configurare per tip = cursor generat in cod la pornire, nu
DBFstatic, 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
-
TIP = 4inVANZARI_CORESP= „factura scrisa dintr-o proforma". Confirmat explicit, dupa ce i s-a explicat ce inseamna1/2/3(TIP= natura legaturii parinte-copil, nu tipul documentului:1= aviz → factura,2= aviz → aviz de retur,3= factura → factura de retur).4intra in aceeasi familie cu1. Valoarea e libera pe ambele fronturi (niciun apel cu4in 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) -
do_modificaramane 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) -
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) -
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)
-
„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 extindecursor_retur_document, ca sa nu se schimbe comportamentul copierii; nuFACT_VFACTURI_DETALII, care pierdeID_POL,PRETDsi tratamentul valutar — argument intarit de faptul caVVANZARI_ARTICOLEnu expuneID_POL/ID_CTR, vezi S10). - S8 —
GESTIONABILla 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_curspe calea de editare: ascuns (precedent: tipurile 8/9). Abatere mica de la decizia 5, asumata. - S8 —
poDate.lEditare: proprietate peoDateFactura, nu parametru (patru locuri au nevoie de semnal;lCopieree deja acolo cu acelasi rol). - S8 —
id_ruta: proprietate noua peoDateFactura(fara ea S8c nu poate implementa unul din cei 14 parametri). - S8 — defectul de prefixare
text_aditionallaInitpentru contracte: se ocoleste in #13 prinlEditaresi 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_proforman-are azi nicio garda (vezi decizia 50, sectiunea rescrisa). - S5c — afisarea „provine din proforma X": da, la nivel de document.
- S4g —
CU_TVA = 1hardcodat: se verifica pe date inainte de implementare, nu se presupune inofensiv. Efectul prinnproc_tva_maxe masurat, pe linie scutita + discount global. - S4g — trasabilitatea
CONT_VENITpeVANZARI_DETALII: da, se pastreaza coloana. - Decizia 54, cele trei consecinte: acceptate toate trei.
IN_STOCvine din formular (si S8 trebuie sa-l incarce — vezi S10, consecinta 1); se pierde validarea liniei fata de comanda pe ramura comenzi, asumat;PROC_TVAVramane 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
lnTipdindo_copiaza(runda 13): se repara la #6, fisierul fiind al lui. - Punctele pur interne (numarul codului de eroare
FACT-0xx, numele cheii de optiuneFACT_SCD_ARTFPRET, forma semnalului de regenerare) — lasate la latitudinea implementarii, cu recomandarile deja scrise in povestile lor.
Raman deschise doar doua, si amandoua din motive proprii, nu din lipsa de raspuns: (a) tipurile 48/49 (custodie) — Marius a cerut sa stie implicatiile, i s-au explicat, decizia n-a fost inca data. Inchis intre timp — decizia 60, runda 16: da, sunt editabile prin #13, cu executia conditionata de verificarea custodiei in curs. (b) cota si explicatia de TVA a discountului de document — cercetare TERMINATA (sectiunea K-bis): cota era regula implicita „cota maxima de pe factura", explicatia e azi o constanta hardcodata (
ReasonCode="95", text „Discount"). Partea de repartizare s-a decis — decizia 59, runda 16: proportional pe cote. Ramane deschis doar campul text optional de motiv. Alegerea variantei de asezare, listata aici ca a treia, s-a inchis intre timp — decizia 57. - S8 — canalul de citire a liniilor: (B), procedura noua
-
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
-
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:
- 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.
- 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.
- Nu exista bara de comenzi jos.
but_renunt/but_terminraman unde sunt azi — in banda de titlu, sus in dreapta (COMUN\clase\cmd_butoane.vc2:288si:386, butoane-imagine cuTop = 1,Anchor = 8/9, tooltip „Renuntare (ESC)" / „Terminare (CTRL+F)"). - 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
Acceptpropriu pe dialog, ce se completeaza in sectiune se scrie laTermina, 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 pentruBut_renunt1/But_reset1; D il face mai apasat, pentru ca acum ele sunt singura cale de iesire.
INCHIS. Cercetarea despre cota si explicatia de TVA a discountului de document (punctul deschis (b) de la decizia 56) s-a terminat — vezi sectiunea K-bis — si repartizarea s-a decis (decizia 59, runda 16: proportional pe cote, fara sa se ceara cota de la utilizator), deci a treia sectiune „grea" (cota + explicatie proprii) nu mai e in discutie. Ramane varianta minimala: decizia 61 (runda 16) a materializat-o — Marius vrea campul text optional de motiv — si decizia 64 (runda 16) a inchis si asezarea: randul se imparte in trei, ~440 px fiecare la 1366 px, strans. D ramane intr-un etaj.
-
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.htmla fost scos dindocs\; sursa de adevar e https://claude.ai/code/artifact/dfc70d38-856f-48fd-b6af-3da78e9da4f7. Ca sa-l modifici:WebFetchpe URL → scrie HTML-ul intr-un fisier de lucru in scratchpad, nu indocs\→Artifactcuurl= link-ul de mai sus (faraurlse 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
-
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:
- 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; sirecalculeaza_totaluri_vanzari(PL/SQL,ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:16081-16092) trebuie sa inlocuiascaMAX(...)cu suma repartizarii. Daca se schimba doar unul,VANZARI.TOTAL_TVAsi TVA-ul din eFactura diverg pe facturile cu cote mixte. Nu e optional si nu se poate esalona. - Bucatile de discount trebuie sa primeasca
id_jtva_coloanaal grupului pe care il reduc, nu doar cota. Cheia de grupare dinxmlefactura.prg:246are cinci campuri siexpltvase completeaza tot prinid_jtva_coloana. O implementare care seteaza doarproc_tvalasa grupul orfan exact unde e azi — repara jumatate din defect si o lasa pe cealalta. - eFactura nu cere nicio modificare —
xmlefactura.prg:758-792grupeaza dejaGROUP BY proc_tvasi emite cate unAllowanceChargeper cota. - 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.) - 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.
- 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 (
AllowanceChargeReasonin locul constantei „Discount";ReasonCoderamane95) — 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.
- Doua locuri de schimbat, nu unul, si in ACEEASI livrare:
Deciziile 60-65 (Marius, runda 16) — luate, nu de reluat
-
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_altelee 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_custodienu are legatura cu tipurile 48/49 — are un singur apel in tot pachetul (PACK:7521), pe ramurapack_facturare.ntip <> 4(:7472), deci servestentip = 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 ecursor_articole_k(PACK:3595-3701), restransa explicit laWHERE C.IN_STOC = 0(:3695), iardescarca_gestiunese cheama doar candin_stoc = 1(garda dubla, la apelant:7472-7475si 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 articoleIN_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. -
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"cacbc:AllowanceChargeReason(COMUN\programe\xmlefactura.prg:774-776);ReasonCoderamane95; - cere stocare noua — azi nu exista nicio coloana pe
VANZARIpentru motivul discountului de document; - cere un control nou in formular — singura bucata din reparatia discountului (K-bis / decizia 59) care chiar cere UI nou, restul fiind repartizare automata fara interactiune.
Redeschisese, in varianta minimala, intrebarea de asezare de la decizia 57 — INCHISA acum prin decizia 64 (runda 16): randul de jos se imparte in trei (~440 px fiecare la 1366 px, strans); varianta D ramane intr-un etaj. Paragrafele din S1 si din decizia 57 sunt actualizate in acest sens.
- inlocuieste constanta hardcodata
-
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, functiaEsteInEFactura; apelata dinCOMUN\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 — verificareaEsteInEFacturaramane 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_facturacrssi — 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 gardaEsteInEFacturanu o acopera. -
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
VANZARIsi se scriu consecvent (ID_UTIL/DATAORAla creare,ID_UTILS/DATAORASla stergere); lipseste complet perechea de modificare (nicio coloana, verificat cu filtru%MODIF%peall_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 pentruID_FACT(sectiunea „E.ID_FACT", linia 762).Poveste noua, proiectata: S14 — verdict complet, recomandare si punctele ramase de decis cu Marius acolo.
-
Asezarea campului de motiv: RANDUL SE IMPARTE IN TREI. Formularea lui: „campul de motiv imparte randul in 3". Inchide ultimul punct ramas deschis din decizia 57 (varianta D de asezare), reluat de decizia 61: randul de jos al formularului unificat are trei sectiuni pe acelasi rand — incasare, alte date si motivul discountului — nu coboara pe rand propriu, nu devine D cu doua etaje. La 1366 px inseamna ~440 px de sectiune, strans, si asumat ca atare.
-
Auditul (decizia 63) se afiseaza in gridul din
frm_facturi, nu in formularul facturii. Formularea lui: „auditul presupun coloanele dataora si utilizator adaugat/modificat/sters, le vreau pe grid-ul din formularul frm_facturi, nu in formularul facturii propriu-zise - este posibil sa fie deja coloane".Confirma continutul cerintei de audit (decizia 63): trei perechi data + utilizator, pentru adaugat, modificat si sters. Locul de afisare e gridul din
frm_facturi, nu formularul unificat — deci S14 nu cere controale noi in formularul unificat, cere coloane in gridul listei de facturi. E o cerinta mai usoara decat presupunea S14, si e de spus explicit.Sarcina de verificat la implementarea lui S14, adaugata ca prim pas al povestii: Marius banuieste ca unele coloane exista deja in acel grid. Nu s-a verificat. S14 incepe deci cu inventarul gridului din
frm_facturi— ce coloane de audit sunt deja acolo, ce surse au — si abia apoi adauga ce lipseste. Nu se presupune nici ca exista, nici ca nu.
S6 — Test pe fluxul real, formularul unificat
Headless (COMUN\docs\depanare_testare_vfp.md) + UI (COMUN\docs\testare-ui-vfp.md), cate un caz
pe fiecare sursa: lista de preturi, comanda, contract, aviz, retur, transfer, valuta. Comparatie
directa cu documentul emis pe calea veche: vanzari, vanzari_detalii, act, rul, totalurile
denormalizate, listarea.
Depinde de: S5.
Etapa II — editarea prin regenerare
S7 — Actiunea si garzile
Actiune noua pe frm_facturi, sub tokenul de drepturi existent. Garzi refolosite din
COMUN\programe\ofacturare_editare.prg (EsteInEFactura:12, plus luna inchisa, luna curenta,
ReferinteDocumenteNota) — nu se rescriu, sunt deja extrase de #6. Plus garzile proprii
regenerarii: refuz daca documentul are urmasi.
PRECIZAT in runda 14, pe cod (docs\cercetare\garda_aviz_facturat.md): regula exista deja integral
in Oracle, in trei garzi consecutive din sterge_factura — factura cu retururi (TIP = 3,
EXPORT:5452-5462), aviz cu factura sau aviz de retur (TIP IN (1,2), EXPORT:5466-5476), si
factura din aviz ale carei avize-sursa au primit intre timp aviz de retur (EXPORT:5480-5494).
S7 nu reimplementeaza regula — o citeste mai devreme, read-only, inainte de intrarea in
formular:
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:
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.cursor_retur_document(V_COPIERE = 1)nu umple documentul, ci selectorul-sursa. Rezultatul intra incrsarticole(gridul din stanga);crsfacturase creeaza gol si ramane gol — comentariul din cod e explicit (COMUN\programe\ofacturare.prg:338,:455-457). Transferul se face doar prindo_adauga_tot→do_adauga_articol, care pentru articolele gestionabile trece prin dialogul de alegere din stoc.cursor_retur_documentnu intoarceID_VANZARE_DETsi niciTAXCODE(PACK_FACTURARE:3949-4054). FaraID_VANZARE_DET, cheia de linie presupusa de S8b nu exista, iar ruta ieftinamodifica_explicatie_articoldevine neapelabila — primul ei parametru esteV_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 reconstituiepoDate+ cursorul de linii dintr-un document salvat, prinFACT_VFACTURI+FACT_VFACTURI_DETALII(ambele auID_VANZARE_DETsiTAXCODE). Canalul final e intrebarea 1 din §8.2 al raportului (recomandat: procedura nouacursor_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_articole2NU 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 pefrm_facturare_articole. Faptul ca aredo_adauga_tot/do_adauga_articolproprii, cu logica divergenta (fara testulllGestionabil,ofacturare.vc2:17476,:17124), nu e un motiv de excludere, e exact driftul din 2017 pe care S2 il inchide — cele doua se unifica, nu se aleg. Din intrebarea 7 ramane deschisa numai partea de tipuri 48/49.
Cerinta noua din S8b e mai blanda decat se credea: lookup-ul „ultimul delegat / ultima masina" are deja o garda —
Empty(id_delegat) And Empty(id_masina)(ferestre_cere_date.vc2:3111). Riscul ramane real, dar numai pe documentele fara delegat si fara masina. Raportul da inventarul complet al celorlalte initializari „pentru document nou" care trebuie sarite (§2.2).
completeaza_setari_document pentru antet + cursor_retur_document(V_COPIERE = 1) pentru linii,
fara degradarea de tip din do_copiaza. Se incarca in plus, fata de copiere: serie / numar /
data / scadenta reale, discountul de document (VANZARI.DISCOUNT), discount_evidentiat, textul
aditional, datele din frm_alte_date (delegat, auto, agent, adresa de facturare), explicatia si
taxcode pe fiecare linie, si legatura cu sursa (id_comanda, id_ctr, lista de avize din
vanzari_coresp) — ca reemiterea sa reconsume aceeasi sursa.
Campul de sursa exista deja — nu e de adaugat, ci de pastrat vizibil: e Ct_clb_altele, cu
eticheta schimbata pe tip de do_schimba_explicatia („Nr. contract” / „Nr. comanda” / „Nr. factura” /
„Nr. facturi” / „Locatie”, ofacturare.vc2:9633-9643), eliminat azi din formular cand
gnScadereStoc = 0 si tipul e 1/5/10 fara copiere. La modificare e blocat: schimbarea sursei ar
insemna alt document, nu o corectie.
Formularul arata identic cu cel de introducere: fara banda de avertizare, fara coloane cu
valorile initiale, fara panou de diferente (decizia 5). Se schimba titlul ferestrei si butonul
principal (decizia 9, vezi S8c).
Gata cand: formularul deschis pe un document existent arata exact documentul, pe fiecare tip de
sursa, si nu se distinge vizual de formularul de introducere.
Depinde de: S7.
S8b — Rutarea scrierii dupa ce s-a schimbat
Comasarea celor trei actiuni de modificare (G-bis): la confirmare se compara starea din formular cu
cea incarcata si se alege ruta — modifica_date_factura pentru antet, modifica_explicatie_articol
pentru explicatia liniei, regenerare pentru orice atinge sumele, nimic daca nu s-a schimbat nimic.
Cele trei actiuni vechi de pe frm_facturi (do_modifica, do_modifica_explicatie) se retrag
abia dupa ce formularul unificat le acopera, nu inainte.
PROIECTAT (runda 13) — docs\cercetare\s8b_rutarea_scrierii.md. Reteta G-bis e corecta ca directie,
dar avea un gol nedocumentat, si el schimba forma solutiei.
- Cele patru rute NU sunt teste independente, ci un lant cu prioritate — sumele primele.
Motivul e concret:
modifica_date_facturanu 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, prinscrie_factura2, direct dinpoDate— verificat direct de sesiunea principala pe apelul de laCOMUN\clase\ofacturare.vc2:14345-14359. Nu exista doi proprietari ai acestor campuri: e acelasi obiectpoDatecitit de ambele cai. - Deci, cand regenerarea porneste,
modifica_date_facturaNU 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 altID_VANZARE. Regenerarea este deja calea de scriere a antetului cand sumele se schimba, nu o cale care trebuie compusa cu alta. - Ordinea rutarii: (1) s-au schimbat liniile / discountul de document? → regenerare, gata;
(2) altfel, antet? →
modifica_date_factura; (3) altfel, doar explicatie de linie? →modifica_explicatie_articol; (4) altfel → nimic. - Explicatia de linie e transportata gratuit de regenerare, prin parametrii nativi ai lui
adauga_articol_factura(V_EXPLICATIE,V_TAXCODE) — cu conditia ca regenerarea sa citeasca din cursorul curent al formularului, nu dintr-un snapshot separat. - Cel mai probabil fals pozitiv al criteriului „deschid si inchid → nicio scriere": lookup-ul
„ultimul delegat / masina al clientului" din
frm_alte_date.Init(COMUN\clase\ferestre_cere_date.vc2:3119-3136). E cod gandit pentru document nou. Pe calea de editare ar suprascrie delegatul incarcat corect din document cu ultimul folosit de client — deci ori documentul apare „modificat" fara ca nimeni sa fi atins nimic, ori, daca ruleaza inainte de snapshot, salveaza tacit delegatul gresit. S8 trebuie sa-l sara explicit pe calea de editare. - Comparatia se face pe valori rotunjite la precizia de scriere (
gnPc/gnPPretV/gnPCant), nu pe valoarea binara — altfel rotunjirea singura produce diferente.
De decis de Marius:
- Editarea multipla —
do_modificade azi lucreaza pe multi-selectie, capacitate fara echivalent in formularul unificat. Se accepta pierderea ei la retragere, saudo_modificaramane 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/efacturape documentul reemis — nu apar inscrie_factura2, deci vin pe alt drum. Raspunsul decide daca mai e nevoie de o scriere separata dupa regenerare. Se inchide in S9. INCHIS (runda 13): NU — pretul vine dincursor_retur_documentre-deriva pretul la incarcare?VANZARI_DETALII.PRETstocat, faraJOINcatre contract sau politici (PACK_FACTURARE:3960-4062, verificat direct). Mecanismul de detectie e valid. Detaliul despre rotunjire siDIFERENTA— 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 dedo_scrie_articole, inaintea primuluiadauga_articol_factura. Faracommitintermediar (VANZARI_DETALII_TEMPe GTTON 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_facturanu reverseaza rulajele. Revenirea stocului vine dinPACK_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 prinfinalizeaza_scriere_act_rul(tnScrieSterge = 2)←oscrie_in_fisiere(2, ...). Adica stocul e derivat din miscari nesterse, iar marcarea lorSTERS = 1inchide subiectul — dar numai daca piciorul de stergere chiar trece prinoscrie_in_fisiere. O implementare care „simplifica" pasul la un apel direct desterge_facturaar 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 capcanaSTERS = 0din E) si se impune documentului nou, printr-un comutator folosit numai de regenerare.SET_IDFACTnu se schimba neconditionat — e cod comun intregii suite.ID_UTIL/DATAORA(audit de creare, S14). Acelasi tipar ca laID_FACT: se citesc din documentul vechi inainte de stergere si se impun explicit documentului nou — altfelINSERT INTO VANZARIde 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.
- Varianta comentata din
SET_IDFACTNU rezolva S9 — nu se reactiveaza. (PACK_CONTAFIN.pck:3016-3035) cauta documentul peNRACT + SERIE_ACT + DATAACT + ID_CTRcuSTERS = 0; dupa soft-deleteSTERSe 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. SET_IDFACTare nevoie de o cale de intrare noua, inerta implicit. Azi nu exista niciun canal:pack_contafin.nIdFacte 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", implicitNULL), citita doar in corpul activ al luiSET_IDFACT, consumata si resetata la prima folosire. Nu se atinge semnaturaSET_IDFACT(V_GCS)— altfel s-ar schimba apelul pentru toti.- Blocajul real nu e in
SET_IDFACT, ci inDOCUMENTE— si asta e descoperirea care schimba estimarea. Scrierea de azi (PACK_CONTAFIN.pck:796-817) e unINSERTsimplu peID_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 cuSTERS = 1. Deci reemiterea cu acelasiID_FACT, cu codul de azi, aruncaORA-00001. Dovada, nu presupunere.MERGE-ul deja comentat (:818-847) nu ajuta: are doarWHEN NOT MATCHED, deci ar ignora tacit randul existent si documentul reemis ar ramane cuSTERS = 1. E nevoie de upsert real, cuWHEN 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:
- Citirea listei sursa INAINTE de stergere, si refurnizarea ei la reemitere.
VANZARI_CORESPsimarcheaza_facturatse rescriu singure prinCASE-ul pentipdinfinalizeaza_factura— dar numai dacantipsi lista sursa (clistaid/clistaid_avize) sunt refurnizate. Ele traiesc in randurileVANZARI_CORESPale documentului vechi, care dupa stergere nu mai sunt disponibile. Fara acest pas nu apare nicio eroare — corespondenta siFACTURATpur si simplu nu se scriu. Scriere lipsa silentioasa, exact tipul de defect care trece de o verificare vizuala. — 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 unUPDATE ATASAMENTE_VANZARI SET id_vanzare = :nouUPDATE(STERS = 1, ca sa ramana consistent cu view-ulVATASAMENTE_VANZARI, care filtreazab.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
SELECTde 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 cuB.PROC_TVAV,B.ID_VALUTA,A.PRET_CU_TVAsiC.IN_STOCintra 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_vanzaricopiazaPRETneschimbat dinVANZARI_DETALII_TEMPinVANZARI_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 trimitandV_ID_CTR = NULL— respinsa motivat, pierde permanent legaturaVANZARI_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 cuDIFERENTApliata inauntru. La reemitere se trimite inapoi valoarea pliata, deci impartireaPRET/DIFERENTAse reface, nu se conserva. De verificat in S12 ca o reemitere identica reproduce suma, chiar daca nu reproduce neaparat aceeasi impartire — si caDIFERENTAnu 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):PRETse inlocuieste neconditionat, nu exista niciDECODE, niciAND A.PRET = V_PRET_TEMP, si nu exista blocEXCEPTION. Un pret modificat pe ecran e inlocuit tacit. In plus nu filtreazaA.STERS = 0, desi coloana exista — linii sterse ale avizului sursa pot fi citite. Si depinde declistaid, 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: dacaCTR_ARTICOLE.PRET_UNITAReNULL(nu0),DECODEnu potriveste literalul0→ rezultatul e NULL, deci pretul din formular se pierde complet si linia intra cuPRETgol. In dev cazul nu apare (27 randuri, 0 NULL) — ceea ce nu spune nimic despre productie. PROC_TVAVnu vine din formular pe NICIO ramura — nu e parametru al procedurii. Pe calea „buna" se deriva dinJTVA_COLOANEpeID_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 peELSE.IN_STOCnu ajunge inVANZARI_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_pretatinge numaiDIFERENTA,scrie_factura_avizenumaiCANTITATE,scrie_seturinumaiID_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 dupainitializeaza_date_factura(ofacturare.vc2:13981) si inainte de bucla deadauga_articol_factura. Recomandata fata de un parametruDEFAULT 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
WHENnou, primul inCASE-ul de laPF:5052, cu acelasi corp caELSE-ul de azi (PF:5189-5203). Nimic inventat — se forteaza ramura care exista deja. Acopera dintr-o data toate trei ramurile problematice, fiind in acelasiCASE.INSERT-ul de laPF:5222ramane 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
IN_STOCar 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.- Se pierde o validare pe ramura comenzi. Azi,
A.PRET = V_PRET_TEMP+ lipsa luiEXCEPTIONfac 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. PROC_TVAVramane derivat, nu preluat. Reproducerea exacta cere ca S8 sa pastrezeID_JTVA_COLOANAsi caJTVA_COLOANEsa nu se fi schimbat. Daca se cere reproducere exacta si peste o modificare legala de cota,PROC_TVAVtrebuie 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_vanzarifaceUPDATE ... SET COD = nou, STERS = 0, deciID_VANZAREnu se schimba, doarCOD, 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, siactualizeaza_vanzarinu 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) siIPS_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 actualizareatipuri_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()dupaexport2pdf; 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-ulVATASAMENTE_VANZARIfiltreaza peb.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— 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.UPDATE ... SET id_vanzare = :nou- 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 prinID_FACT, care se pastreaza. (C), cu dovada dubla. - eFactura,
DOCUMENTE, listarile,VANZARI_CANTITATI— (C), toate cheiate peID_FACTsau rescrise automat.
De decis de Marius:
- Editarea unui document care are deja retur emis pe el e azi BLOCATA, nu doar riscanta —
sterge_facturaaruncaORA-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? - 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".
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 —
DIFERENTAnu se acumuleaza. Motivul:cursor_retur_documentintoarce pretul deja rotunjit si cuDIFERENTApliata inauntru (S10), iar reemiterea trimite inapoi valoarea pliata, deci impartireaPRET/DIFERENTAse 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, ambeleNOT NULL, scrise consecvent la fiecareINSERT(PACK_FACTURARE.scrie_in_vanzari,EXPORT:13598-13640, si a doua ruta dinfinalizeaza_avize_lucrare,EXPORT:14930). NiciunUPDATE VANZARI SET ID_UTIL/DATAORAin tot pachetul (cautare directa, zero rezultate).ID_UTILS+DATAORAS— stergere, nullable, scrise consecvent desterge_factura(EXPORT:5496-5499) sisterge_proforma.- (neceruta, dar arata conventia)
ID_UTILFACT+DATA_FACTURAT— facturare din aviz. - Pe
VANZARI_DETALII: acelasi tipar de creare/stergere, plusID_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):
- O singura pereche noua pe
VANZARI, urmand conventia casei — nume propusID_UTILM/DATAORAM(utilizator si data ultimei modificari). Numele e de confirmat. - La regenerare (S9):
ID_UTIL/DATAORAse transporta din documentul vechi in cel nou, exact caID_FACT— vezi nota adaugata la S9. Perechea noua de modificare primeste utilizatorul curent sisysdate. Fara acest pas, creare si modificare se suprapun. - Perechea de stergere nu are nevoie de nimic — exista si e scrisa consecvent.
- Cere migrare de DB (
ALTER TABLEpeVANZARI) — DB inainte de EXE, ca la S10. Scriptul intra inD:\ROA\DATABASE\SCRIPTURI_CLAR\,versiune_db.txtse 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 dinfrm_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: asimetriado_modificacorectata prin recalculul la cerere (decizia 45), si faptul cado_modificaramane 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 sifrm_facturare_articole2, daca se merge pe recomandarea din S8), plus confirmarea din S11 pentruREST_NOTE_PLATAsiIPS_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 peofacturare_comun.vc2/ofacturare_editare.prg. Riscul revine doar daca ordinea se schimba.Riscul „cod nou inCaduc dupa deciziile 34 si 35, si riscul revine ca risc principal. Politica tehnica perpack_facturare" e evitat prin constructie (decizia 27-bis).SCCsi scrierea inCRM_POLITICI_PRET_ARTnu se mai fac — deci cade si riscul ca o politica tehnica sa apara incaut_politici_curente_util(). In locul lui:pack_facturarese modifica (parametru de cont + ramura fara politica incontabilizeaza_articol), iar pachetul e comun intregii suite — ROACONT, ROAGEST, ROACONTRACTE, ROAAUTO, ROAACNPRO. Mitigarea e structurala, nu prin inspectie: parametruDEFAULT NULLla 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_articolare 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_facturae apelata pozitional peste tot, niciodata cu notatie pe nume (=>), iar cele sapte copiiCOMUN\clase\ofacturare.vc2sunt 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_devizdin ROAAUTO / ROAACNPRO. Precedentul e activ, nu teoretic: ROAGEST apeleaza aziadauga_articol_facturacu 24 din 25 de parametri, omitandV_TAXCODEsiV_LOTtocmai pentru ca auDEFAULT 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:INCHISA — fals pozitiv, nu intra ca risc.scrie_factura2apelata cu 16 din 17 parametri.oExecuta(COMUN\programe\oproceduri_comune.prg:121-159) deleaga laoExecute(:173-504), care faceSQLExec(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 unREF CURSOR OUT, driverul il ia din catalog, nu din textul apelului, si intoarce randurile ca result set — captat exact de al treilea argument al luiSQLExec. 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 lav 2.0.13lav 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_fisieree 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.vc2e 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_FACTpastrat cere atingerea unui punct comun intregii suite.SET_IDFACTe inPACK_CONTAFINsi 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
Terminaramane 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_cursse 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_datemuta 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. S3csiS4cating cod comun. Parametrizarea sursei atingefactureaza(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_tvape 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 eMARIUSM_AUTOpeROA_CENTRAL, nu productia (COMUN\docs\scripturi-migrare-db.md). Scripturi nivel Oracle 10.2, CRLF, idempotente,versiune_db.txtactualizat. - Sursa completa
PACK_FACTURAREnu e in working copy; copia pe disc eD:\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/.sc2trec pringit_sync.ps1+txt2vcx.ps1, cu atentie laCOMUN\docs\conventie_encoding_cp1252.md(diacriticele din.vc2sunt cp1250 — vezi si memoria proiectului).