Files
roafacturare/docs/plan_06_editare_factura.md
Marius Mutu d9f5ca4226 docs: planurile, proiectarile si rapoartele de lucru intra in versionare
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
2026-08-11 22:17:17 +03:00

21 KiB

Plan #6 — editarea unei facturi emise, netrimisa inca in eFactura

Sursa: COMUN\docs\todos.txt punctul 6. Ordine de executie: al treilea, dupa #8 si #7.

Stare: IN LUCRU. S1-S6 gata, S5 comis in git pe 10.08.2026. Ramase: S4b partial (actiunea de sincronizare), S7 neatins, S8 partial, S9 partial. Starea la zi si ordinea recomandata: docs\handoff_punct6_dupa_s5.md. Deciziile 38-41, care corecteaza S5 fata de ce scrie mai jos: docs\handoff_s5.md.

Cerinta si directia aleasa

Factura emisa trebuie sa poata fi corectata cat timp nu a plecat in eFactura (anaf_efactura.id_fact). Problema: notele contabile si rulajele se genereaza complicat la emitere, iar editarea de azi nu le sincronizeaza.

Varianta respinsa: regenerarea prin re-emitere. Emiterea face verificari de stoc si alte protectii dependente de tipul documentului (lista de preturi, comanda, contract, retur, invoice); la regenerare acestea ar esua pe date care intre timp s-au schimbat.

Varianta aleasa: editare directa in frm_modific2024, extins cu partea de vanzari/vanzari_detalii, ca sa nu se duplice un formular de editare de note si rulaje.

Doua puncte de intrare, o singura implementare (decizie Marius, 06.08.2026)

Editarea unei facturi de vanzare trebuie sa fie posibila si din ROACONT > registru jurnal > modificare, adica exact prin procedura existenta de editare de note contabile / rulaje — nu doar dintr-o actiune noua in formularul de facturi din ROAFACTURARE.

Consecinta pentru arhitectura: lucrarea nu e "cod apelant nou in ROAFACTURARE", ci extinderea clasei comune frm_modific2024 + a partii server-side, de unde ambele puncte de intrare o primesc automat. Cazul "documentul curent e o factura de vanzare" se detecteaza in formular, nu in apelant.

ID_FACT nu se schimba — by design

id_fact se genereaza la introducerea documentului, din nract + dataact, si ramane acelasi la modificarea notelor contabile; se schimba doar cod-ul setului de note/rulaje. Legatura initiala cu ACT/RUL era vanzari.cod, dar intre timp s-a adaugat si perechea vanzari.id_fact / act.id_fact / rul.id_fact / documente.id_doc, folosita de alte functionalitati.

Deci VANZARI.ID_FACT ramane neatins la editare, iar realinierea priveste exclusiv cod-ul. Orice ipoteza de rescriere a lui id_fact e gresita si a fost scoasa din plan.

Ce s-a stabilit din cod

Formularul exista deja si e direct utilizabil

frm_modific2024 e o clasa .vcx (nu .scx monolitic), definita in COMUN\clase\omodificari.vc2 (clasa incepe la :6375, metodele proprii :12200-15319). E deja in COMUN-ul ROAFACTURARE, deci nu trebuie portata nimic: COMUN\clase e deja in SET CLASSLIB / SET PROCEDURE, iar clasa foloseste doar globale standard (gnAn, gnLuna, goExecutor, gcs, gnIdUtil, glLunaInchisa), toate setate de Programe\roafacturare.prg.

Formularul editeaza doar cursoare in memorie; do_termin (mostenit din _frmbase) nu scrie nimic, doar seteaza gnButon=1 si inchide. Scrierea e treaba codului apelant.

Acelasi formular e si "modificare registru jurnal" — apelat din afisjurcom.do_modifica (comun.vc2:2222-2563). Fluxul e deja documentat in D:\ROA\ROAGEST\COMUN\docs\flux-modificare-stergere-nota-jurnal.md, verificat linie cu linie.

Scrierea in ACT/RUL: o singura cale posibila

Nu exista niciun UPDATE/DELETE direct pe ACT sau RUL in tot codul VFP (cautat explicit). Singura cale: cursoare -> ACT_TEMP/RUL_TEMP (INSERT-uri in oscrie_in_fisiere.prg:127-136, :272-274) -> PACK_CONTAFIN.init_scriere_act_rul_local / final_scriere_act_rul_local.

Editarea nu suprascrie randul vechi: il marcheaza STERS = 1 si scrie un document nou, cu cod nou. Comportament intentionat, nu efect secundar.

Sincronizarea cu VANZARI exista deja partial — si aici e lucrarea

pack_contafin.finalizeaza_modificare_nota (COMUN\docs\PACK_CONTAFIN.pck:8601-8651) apeleaza deja pack_facturare.actualizeaza_vanzari(cod_vechi, cod_nou) cand cod-ul exista in vanzari (iar finalizeaza_stergere_nota apeleaza sterge_din_vanzari).

Dar actualizeaza_vanzari (ff_2026_03_23_04_COMUN_PACK_FACTURARE.sql:15951-15961) face exclusiv realinierea legaturii:

UPDATE VANZARI_DETALII SET STERS = 0
 WHERE ID_VANZARE IN (SELECT ID_VANZARE FROM VANZARI WHERE COD = V_COD_VECHI);
UPDATE VANZARI SET COD = V_COD_NOU, STERS = 0 WHERE COD = V_COD_VECHI;

Nu recalculeaza sume, nu atinge cantitati/preturi din VANZARI_DETALII, nu reactualizeaza totalurile denormalizate din VANZARI (total_fara_tva, total_tva, total_cu_tva, valoare_achizitie, discount_tva). Comentariul din cod (:15954) anticipeaza chiar aceasta extensie: "de modificat in caz ca il las sa stearga manual inregistrari din VANZARI_DETALII".

Deci: azi, o editare de nota rescrie corect contabilitatea si pastreaza legatura cu factura, dar lasa sumele facturii la valorile vechi. Exact divergenta pe care o descrie cerinta.

Calea de scriere in VANZARI_DETALII (stabilit 08.08.2026)

Detaliu complet: docs\cercetare\rec_cale_vanzari_detalii.md.

La emitere, liniile nu se scriu din VFP in VANZARI_DETALII. VFP cheama, per linie din crsfactura, pack_facturare.adauga_articol_factura (ofacturare.vc2:14069-14091), care insereaza in VANZARI_DETALII_TEMP; trecerea in tabela reala o face scrie_in_vanzari, cu un INSERT ... SELECT FROM VANZARI_DETALII_TEMP, potrivit pe (id_comanda, numar_act).

adauga_articol_factura nu e reutilizabila la editare: ramifica pe pack_facturare.ntip (stare de sesiune) si re-deriva pretul, TVA-ul si valuta din documentul-sursa (comanda, aviz, contract). La o factura deja emisa nu exista document-sursa de re-derivat, iar valorile editate de utilizator ar fi suprascrise. VANZARI_DETALII_TEMP e GTT ON COMMIT DELETE ROWS, deci si umplerea, si consumul ar trebui sa stea in aceeasi tranzactie.

Calea aleasa pentru #6: scriere directa in VANZARI_DETALII, fara sa treaca prin TEMP — UPDATE tintit pe ID_VANZARE_DET pentru linii modificate, STERS = 1 pentru linii scoase, INSERT pentru linii adaugate. Modelul exista deja: modifica_explicatie_articol. ID_VANZARE_DET vine automat dintr-un trigger BEFORE INSERT pe SEQ_VANZARI_DETALII, deci un INSERT de oriunde primeste cheia corect.

Alternativa (refolosirea TEMP) ar fi cerut fie atingerea lui adauga_articol_factura / scrie_in_vanzari — cod critic de emitere — fie duplicarea lor, adica exact tiparul care a produs problema reparata la #8.

Stergerea unei singure linii nu exista azi: sterge_factura / sterge_proforma marcheaza STERS = 1 pe toate liniile documentului deodata. E functionalitate noua pentru #6.

Punctul de agatare si garda existenta

  • Sablonul arhitectural cel mai apropiat e frm_facturi.do_sterge (COMUN\clase\ofacturare_comun.vc2:4503-4719): incarca cursoarele pe cod, porneste tranzactie manuala, ruleaza oscrie_in_fisiere, apoi pack_contafin.finalizeaza_stergere_nota. Se cloneaza acesta, nu do_modifica (care doar deschide un formular de metadate).
  • Garda "nu edita daca a plecat in eFactura" exista deja, dar in do_modifica, nu in do_sterge (ofacturare_comun.vc2:4426-4430: select count(*) from anaf_efactura where id_fact = ... + poRec.sters = 0 AND NVL(pnEFactura,0) = 0). Azi e o simpla conditie de If care deschide sau nu dialogul; pentru editare trebuie sa devina un Return cu mesaj, ca celelalte garzi, si sa fie extrasa in COMUN\programe\ ca sa fie apelabila si din afisjurcom, care nu o are deloc.
  • Gating-ul de drepturi pe frm_facturi nu e prin nid_cw, ci prin This.lactiv3 / lactiv4 + tokeni in globala gcAcces. (Difera de ofundal_facturare, unde se foloseste nid_cw — de nu confundat cu planurile #11/#10.)

Stories

S1 — Actiunea noua pe formularul de facturi (punctul de intrare din ROAFACTURARE)

Al doilea punct de intrare, ROACONT > registru jurnal > modificare, exista deja si nu are nevoie de actiune noua — dar garzile de mai jos trebuie sa fie in formular sau in codul comun, ca sa se aplice si acolo, nu doar in ROAFACTURARE.

Cloneaza structura din do_sterge (ofacturare_comun.vc2:4503-4719) intr-o actiune noua de editare: garda eFactura + sters=0 (refolosita din :4428-4432), plus garzile pe care do_modifica nu le are azi dar do_sterge le are si care devin obligatorii cand se ating sumele: luna inchisa (glLunaInchisa, :4518-4520), luna curenta (:4543-4546) si referinte de incasari/plati (ReferinteDocumenteNota, :4565-4569). Drepturi: sub tokenul "3" existent (decizia din 08.08.2026) — butonul nou se adauga in cbuton3 alaturi de but_modifica1/But_modifica2, si actiunea trece prin This.lactiv3. Fara token nou, fara interventie in tabelele de drepturi. Gata cand: actiunea apare, e vizibila doar cu drept, si refuza corect facturile trimise in eFactura, sterse, din luna inchisa sau referentiate.

S2 — Incarcarea cursoarelor notei

Dupa modelul afisjurcom.do_modifica (comun.vc2:2222-2563): incarca vact_tot / vrul_tot / vrul_obinv_tot in cursoarele tact / trul / trul_obinv, filtrate pe cod + an + luna (acelasi filtru ca la stergere). Gata cand: pentru o factura data se incarca exact randurile ei de nota si de rulaj. Depinde de: S1.

S3 — Deschiderea frm_modific2024 pe factura

Instantiaza clasa din COMUN\clase\omodificari.vc2 cu cursoarele din S2. Cod apelant ~40 de linii, fara modificari in clasa. Gata cand: formularul se deschide din ROAFACTURARE si arata nota facturii; gnButon=1 la confirmare. Depinde de: S2.

S4 — Pagina noua de articole factura in frm_modific2024

Editarea randului din vanzari si a randurilor din vanzari_detalii intra in acelasi formular, pe o pagina noua a pageframe-ului existent, dupa modelul paginilor de rulaje.

Scop complet (decizia lui Marius, 08.08.2026): linii sterse, adaugate si modificate (articol, cantitate, pret, flagul pret_cu_tva — vezi planul #7), plus discountul de document (VANZARI.DISCOUNT) pe zona de antet.

Fara plafon de cantitate si fara verificare de stoc. Corectia unei facturi emise e raspunderea utilizatorului; formularul nu-l opreste. Asta inchide intrebarea lasata deschisa de #7 (de unde vine plafonul cand cursorul de stoc din formularul de compunere nu exista) — raspunsul e ca nu mai e nevoie de plafon. In schimb, obligatia se muta pe helpere si verificari, vezi S4b: totaluri calculate, propuneri de sincronizare si semnalarea necorelarilor cu notele si rulajele.

Drepturi: sub tokenul "3" existent (modificare). Fara token nou.

Liniile din seturi se trateaza ca orice alta linie — fara ramura speciala in formular si fara blocarea facturilor care le contin. Consecinta pentru S5: agregarea din scrie_in_vanzari are ramura proprie pe VANZARI_SETURI_TEMP, deci recalculul trebuie sa acopere si liniile de set, altfel totalurile diverg tacut exact pe facturile cu seturi.

Ancora concreta: pgfArticole din omodificari.vc2 are azi PAGE1.Caption = "Rulaje materii prime, materiale, marfuri, obiecte inventar (303)" cu grdRulaje si PAGE2.Caption = "Rulaje obiecte inventar in folosinta (8039)" cu grdRulajeObinv (:8598-8601). Se adauga PAGE3 cu un grid de articole legat de cursorul de vanzari_detalii, plus zona de antet pentru randul din vanzari.

Pagina se afiseaza doar cand documentul curent are rand in vanzari; altfel formularul arata exact ca azi. Asta e conditia ca registrul jurnal din ROACONT/ROAGEST sa nu se schimbe pentru documentele care nu sunt facturi.

Respecta COMUN\docs\conventie_ux_formulare.md si, pentru 3 griduri, COMUN\docs\capcana_grid_controlsource.md. Clasa e in COMUN\, deci schimbarea e cross-project. Gata cand: pagina apare pe o factura de vanzare deschisa din ambele puncte de intrare, si lipseste pe un document care nu e factura. Depinde de: S3.

S4b — Helpere, totaluri si verificari de corelatie, explicit, niciodata silentios

Cerinta lui Marius: preturile si cantitatile din rulaje si cele din articolele facturii se pot desincroniza la editare, iar programul nu are voie sa le alinieze pe tacute. Trebuie sa arate utilizatorului ce nu se potriveste si sa propuna sincronizarea, ca actiune constienta.

Odata cu decizia din 08.08.2026 (S4 fara plafon si fara verificare de stoc), S4b devine partea grea a lui #6: singura plasa de siguranta a utilizatorului. Nu mai e un adaos peste S4, ci conditia care face editarea libera acceptabila. Acopera si liniile adaugate sau sterse, nu doar valorile modificate.

Continut:

  • Totaluri de control vizibile in formular, pe trei surse: suma din RUL, suma din VANZARI_DETALII si suma din ACT. Cand cele trei nu coincid, diferenta se vede, nu se ascunde.
  • Indicator de stare a sincronizarii (label/culoare), nu doar un mesaj la salvare.
  • Actiune explicita de sincronizare, cu enumerarea liniilor care s-ar modifica si a valorilor vechi/noi inainte de aplicare.
  • Sincronizarea nu se declanseaza automat la do_termin.

Directia sincronizarii (rulajul e sursa sau articolul e sursa) se stabileste la implementare, dar alegerea trebuie sa fie a utilizatorului, nu implicita. Gata cand: o editare care desincronizeaza cele trei surse produce un indicator vizibil si o propunere enumerata, iar refuzul propunerii lasa datele nemodificate. Depinde de: S4.

S5 — Scrierea sumelor editate in VANZARI (partea Oracle)

Procedura sora noua (ex. recalculeaza_totaluri_vanzari), apelata din finalizeaza_modificare_nota dupa actualizeaza_vanzari — nu extinderea in-place a lui actualizeaza_vanzari. Motivul: actualizeaza_vanzari ruleaza azi la ORICE editare de nota al carei cod exista in VANZARI, inclusiv din registrul jurnal ROAGEST/ROACONT pe editari care nu ating sumele; recalculul in-place ar propaga riscul in toata suita. Detaliu si sursa curenta: docs\cercetare\rec_s5_oracle_vanzari.md.

Refoloseste acelasi calcul ca scrie_in_vanzari — nu o formula paralela, altfel cele doua cai diverg exact ca in problema de la #8. Partea per-linie e deja extrasa in calculeaza_total_fara_tva_fact / calculeaza_total_tva_fact, care ramifica deja pe PRET_CU_TVA; de extras ramane doar blocul de agregare, parametrizat pe sursa (VANZARI_DETALII_TEMP la emitere, VANZARI_DETALII la editare).

Doua constrangeri gasite la cercetare:

  • SERIE_INCASAT / NR_INCASAT / SUMA_INCASAT / TIP_INCASAT nu se recalculeaza. Vin din stare de sesiune specifica emiterii unei facturi-cu-incasare si n-au sursa persistenta; o copiere naiva a intregului UPDATE din scrie_in_vanzari le-ar pune NULL la fiecare editare, rupand legatura cu incasarea. Se scriu doar cele 11 coloane de totaluri/curs.
  • VALVAL / TVAVAL / TOTVAL (totaluri in valuta) intra in recalcul, desi planul initial nu le enumera: sunt in acelasi bloc de agregare, costa zero in plus si altfel raman incoerente pe documentele in valuta.

Discountul de document e editabil (decizia din 08.08.2026), deci intra ca valoare noua in recalcul, nu se citeste ca invariant din VANZARI.DISCOUNT. Script de migrare conform COMUN\docs\scripturi-migrare-db.md: CRLF, idempotent, nivel Oracle 10.2, versiune_db.txt actualizat. Gata cand: dupa o editare de cantitate, total_fara_tva/total_tva/total_cu_tva din VANZARI corespund sumei liniilor. Depinde de: S4.

S6 — Legaturile care depind de cod

Editarea scrie un document nou cu cod nou, iar actualizeaza_vanzari realiniaza VANZARI.COD. VANZARI.ID_FACT nu se schimba (by design — vezi mai sus), deci tot ce se leaga prin id_fact / id_doc ramane valid: anaf_efactura, documente, si perechile act.id_fact / rul.id_fact. Inchis pe cod la 08.08.2026 (docs\cercetare\rec_s5_oracle_vanzari.md, sectiunea C): toate legaturile trec prin ID_FACT (ACT.id_factd/id_factc, DOCUMENTE.ID_DOC, ANAF_EFACTURA.ID_FACT) sau prin ID_VANZARE (vanzari_coresp, marcheaza_facturat), niciodata prin cod. actualizeaza_vanzari rescrie doar COD pe acelasi rand — ID_VANZARE nu se schimba niciodata. ReferinteDocumenteNota e o garda pre-editare, nu o legatura persistenta. Fara lucru suplimentar; ramane doar confirmarea pe fluxul real, in S8. Depinde de: S5.

S7 — Rotunjirea la reeditare

verifica_total_document (:16009-16188) insereaza automat o linie de corectie in ACT_TEMP cand totalul difera de suma din note. La editare se aplica din nou — de confirmat ca nu se acumuleaza corectii succesive la editari repetate. Gata cand: trei editari consecutive nu lasa trei linii de corectie. Depinde de: S5.

S8 — Test pe fluxul real, din ambele puncte de intrare

Test headless (COMUN\docs\depanare_testare_vfp.md) + UI (COMUN\docs\testare-ui-vfp.md), pe cate un caz din fiecare tip de sursa: lista de preturi, comanda, contract, aviz. Fiecare caz rulat de doua ori: o data pornit din formularul de facturi (ROAFACTURARE), o data din registru jurnal (ROACONT). Verifica: nota veche STERS=1, nota noua corecta, id_fact neschimbat, vanzari/vanzari_detalii sincronizate, totalurile denormalizate corecte, rulajele refacute, totalurile de control din S4b concordante, si ca nu s-au declansat verificarile de stoc de la emitere. Al treilea caz obligatoriu: un document care nu e factura, deschis din registru jurnal — pagina de articole nu apare si comportamentul e identic cu cel de azi. Depinde de: S6, S7.

S9 — Diff, review, changelog, documentatie

Diff ca fisier in docs\, commit dupa aprobare, in doua repo-uri (ROAFACTURARE si COMUN). Changelog :nou:. Propune actualizarea flux-modificare-stergere-nota-jurnal.md cu ramura de facturi.

Riscuri

  • Cel mai mare: omodificari.vc2 e in COMUN si serveste si registrul jurnal din ROAGEST/ROACONT. O regresie acolo e mai grava decat lipsa functionalitatii aici. S4 trebuie sa pastreze modul "doar nota" complet neschimbat.
  • Modelul "sterge + scrie document nou cu cod nou" inseamna ca o factura editata isi schimba cod-ul — orice raport sau integrare care tine minte cod-ul vechi il pierde. S6 exista tocmai pentru asta.
  • Fara garzile de luna inchisa / luna curenta / referinte (azi absente pe do_modifica), editarea de sume ar putea modifica o perioada deja raportata. Sunt obligatorii, nu optionale.
  • Interactiunea cu #7: daca flagul pret_cu_tva devine editabil pe linie, editarea ulterioara trebuie sa il trateze la fel ca introducerea.

Dependente

  • #7 — S4 include editarea flagului pret_cu_tva pe linie; se face dupa ce #7 stabileste comportamentul la introducere.
  • #8 — S5 refoloseste calculul totalurilor din scrie_in_vanzari, care se atinge in #8.

Ce preda #7 catre S6 (consemnat 07.08.2026, decizia lui Marius)

Din #7 s-a livrat editarea flagului pret_cu_tva doar pe factura in curs de compunere (inainte de salvare), prin frm_facturare_articole.do_modifica (buton But_modifica1 + dublu-clic pe grd_factura), care redeschide dialogul frm_articol_factura. Detaliu complet: docs\cercetare\rec_s2b_aplicare.md.

Editarea aceluiasi flag pe o factura deja salvata a fost mutata explicit in #6, prin decizia lui Marius din 07.08.2026. Motivul, exact: butonul existent din frm_facturi (But_modifica2 -> do_modifica_explicatie, nu do_modifica) deschide frm_modifica_articol_factura, care editeaza doar explicatie si taxcode — doua campuri care nu ating nicio suma (pack_facturare.modifica_explicatie_articol e un UPDATE de doua coloane). Cealalta actiune, do_modifica, deschide frm_modifica_factura (antet: ruta, delegat, agent, serie/numar, date) — tot fara sume. Flagul pret_cu_tva e de alta natura: schimbarea lui reimparte baza si TVA-ul pe linie, deci muta totalurile denormalizate din VANZARI si notele contabile — exact problema de fond a punctului 6.

#7 lasa mostenire un model gata facut si verificat pentru partea de UI a acestei editari: maparea de intrare crsfactura -> poArticol (AddProperty/RemoveProperty pe 7 perechi de nume) si drumul invers (Gather+Replace), documentate in docs\cercetare\rec_s2b_aplicare.md. Deci S4 nu trebuie sa reproiecteze dialogul, ci sa rezolve partea de persistenta/recalcul in Oracle (vezi S5).

Limitarea ncantitatemax din #7 a fost eliminata tot in #7, prin decizia lui Marius din 07.08.2026: do_modifica recalculeaza plafonul din cursorul de stoc in loc sa il fixeze pe cantitatea curenta a liniei, reconstituind cantitatea disponibila de dinainte ca linia curenta sa o fi consumat. Mecanismul, pe scurt: regasire dupa id_c in crsarticole/crsarticole1 (alegerea cursorului dupa opt_facturare), apoi reversul decrementarii pe care do_adauga_articol a aplicat-o deja — cantitate + poArticol.cantitate la tipurile de document cu verificare de stoc, cantitate - poArticol.cantitate la retur; fallback pe cantitatea liniei daca randul nu mai e in cursor. Detaliu complet: docs\cercetare\rec_s2c_ncantitatemax.md.

Inchis pe 08.08.2026: intrebarea era de unde vine plafonul de cantitate la o factura deja salvata, unde cursorul de stoc (crsarticole/crsarticole1) poate sa nu existe. Decizia lui Marius: #6 nu are plafon si nu verifica stocul — corectia unei facturi emise e raspunderea utilizatorului. Nu e nevoie de interogare de stoc proprie. Efortul se muta in S4b (helpere, totaluri, verificari de corelatie cu ACT si RUL).