From 104c24ec201af44710fbe476ab2f7bcbc65efe82 Mon Sep 17 00:00:00 2001 From: Marius Mutu Date: Wed, 9 Sep 2026 21:30:51 +0300 Subject: [PATCH] #13: formular de facturare unificat - etapa curenta Squash al branch-ului de lucru plan13-s2. Cod: clasa noua ofacturare_util (geometrie si utilitare de formular), inrolata in roafacturare.prg; meniul "Facturare (nou)" plat, cu reparatia literalului peste limita VFP de 255 de caractere care impiedica compilarea metodei. Livrare: changelog 2.11.19, marcajul de versiune DB pentru scripturile finale din SCRIPTURI_CLAR\2026\09, inventarul livrarii in docs/livrare_13.md si registrul de erori deschise. Documentatia de lucru a etapei (cercetare, rapoarte, handoff-uri, snapshot-uri de scripturi si backup-uri de rollback) a fost scoasa din arbore. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01PkyjGyrV2S7932om4kfSiK --- CLAUDE.md | 46 + Clase/ofacturare_util.vc2 | 136 + Clase/ofundal_facturare.vc2 | 100 +- Programe/roafacturare.prg | 4 + changelog_roafacturare.txt | 49 + ...udit_vanzari_creare_modificare_stergere.md | 222 - docs/cercetare/buton_comutator_picture.md | 146 - .../canal_cont_venit_fara_politica.md | 515 - docs/cercetare/cont_venit_corespondente.md | 240 - docs/cercetare/coresp_cont_venchelt.md | 800 -- .../custodie_48_49_stergere_reemitere.md | 217 - docs/cercetare/discount_document_cota_tva.md | 731 -- .../cercetare/discount_document_cota_tva_b.md | 189 - .../discount_in_rapoarte_si_efactura.md | 216 - docs/cercetare/factura_retur_document.md | 185 - docs/cercetare/garda_aviz_facturat.md | 235 - docs/cercetare/gol_ntip4_factura_din_avize.md | 231 - docs/cercetare/handoff_s4_runda1.md | 191 - .../idfact_refolosire_si_documente.md | 337 - docs/cercetare/idpol_comanda_contract.md | 553 - .../cercetare/inventar_controale_formulare.md | 179 - docs/cercetare/legatura_linie_retur.md | 228 - .../linii_comanda_articol_nemembru.md | 182 - .../modifica_date_factura_parametri.md | 124 - .../cercetare/nota_contabila_fara_politica.md | 224 - docs/cercetare/optiune_firma_cont_debit.md | 373 - .../cercetare/pack_auto_actualizeaza_deviz.md | 210 - .../parametru_cont_contabilizeaza_articol.md | 173 - docs/cercetare/rec_cale_vanzari_detalii.md | 272 - docs/cercetare/rec_d42_efactura.md | 245 - docs/cercetare/rec_datoria6_baza_regresie.md | 188 - docs/cercetare/rec_dec42_proiectare.md | 312 - docs/cercetare/rec_editare_factura.md | 262 - docs/cercetare/rec_s4_runda1.md | 85 - docs/cercetare/rec_s5_oracle_vanzari.md | 231 - docs/cercetare/retur_si_lista_preturi.md | 198 - .../roaauto_articole_lista_preturi.md | 163 - docs/cercetare/roaauto_facturi.md | 167 - docs/cercetare/rute_scriere_antet.md | 249 - docs/cercetare/s10_pret_contract_reemitere.md | 468 - docs/cercetare/s10_pret_rederivat.md | 342 - .../s10_rederivare_pe_calea_reemiterii.md | 470 - docs/cercetare/s11_legaturi_id_vanzare.md | 379 - docs/cercetare/s2_factureaza_unificare.md | 318 - docs/cercetare/s3_portare_antet.md | 478 - docs/cercetare/s3b_alte_date_analitice.md | 602 -- docs/cercetare/s3c_sursa_ca_parametru.md | 426 - docs/cercetare/s4_cautare_articole_server.md | 482 - .../s4_punct2_registru_cantitate_ramasa.md | 615 -- docs/cercetare/s4_puncte_deschise.md | 246 - docs/cercetare/s4b_bara_butoane_meniu.md | 606 -- docs/cercetare/s4c_discount_in_grid.md | 489 - docs/cercetare/s4d_zi_curs_reactiv.md | 429 - docs/cercetare/s4e_lista_preturi_pe_sursa.md | 444 - docs/cercetare/s4f_retur_formular_unificat.md | 727 -- .../s4g_adaugare_articole_modificare.md | 437 - docs/cercetare/s5_acoperire_tipuri.md | 502 - .../s5b_proforma_descarcare_gestiune.md | 321 - .../s5b_proiectare_proforma_copiere.md | 610 -- docs/cercetare/s5c_factura_din_proforma.md | 374 - docs/cercetare/s8_incarcare_document.md | 720 -- docs/cercetare/s8b_rutarea_scrierii.md | 311 - .../stoc_la_stergere_si_reemitere.md | 239 - ...prafata_regresie_contabilizeaza_articol.md | 215 - docs/cercetare/verif_baza_vie_cont_venit.md | 250 - docs/cercetare/verif_goluri_ntip_aviz.md | 325 - docs/cercetare/verif_proforma_alegere_stoc.md | 366 - docs/cercetare/zi_curs_validare.md | 262 - docs/erori_deschise.md | 12 + docs/handoff_13_formular_unificat.md | 798 -- docs/handoff_curatenie_progres.md | 73 - docs/livrare_13.md | 268 + docs/mockup_13_formular_unificat.html | 1176 --- docs/plan_13_executie.md | 146 + docs/plan_13_unificare_formular_facturare.md | 8864 +++++++++-------- docs/plan_index.md | 2 +- docs/progres.md | 71 +- docs/sabloane/README_sablon_vcx.md | 150 + docs/sabloane/sablon_vcx_gol.vc2 | 14 + versiune_db.txt | 2 +- 80 files changed, 5585 insertions(+), 27852 deletions(-) create mode 100644 Clase/ofacturare_util.vc2 delete mode 100644 docs/cercetare/audit_vanzari_creare_modificare_stergere.md delete mode 100644 docs/cercetare/buton_comutator_picture.md delete mode 100644 docs/cercetare/canal_cont_venit_fara_politica.md delete mode 100644 docs/cercetare/cont_venit_corespondente.md delete mode 100644 docs/cercetare/coresp_cont_venchelt.md delete mode 100644 docs/cercetare/custodie_48_49_stergere_reemitere.md delete mode 100644 docs/cercetare/discount_document_cota_tva.md delete mode 100644 docs/cercetare/discount_document_cota_tva_b.md delete mode 100644 docs/cercetare/discount_in_rapoarte_si_efactura.md delete mode 100644 docs/cercetare/factura_retur_document.md delete mode 100644 docs/cercetare/garda_aviz_facturat.md delete mode 100644 docs/cercetare/gol_ntip4_factura_din_avize.md delete mode 100644 docs/cercetare/handoff_s4_runda1.md delete mode 100644 docs/cercetare/idfact_refolosire_si_documente.md delete mode 100644 docs/cercetare/idpol_comanda_contract.md delete mode 100644 docs/cercetare/inventar_controale_formulare.md delete mode 100644 docs/cercetare/legatura_linie_retur.md delete mode 100644 docs/cercetare/linii_comanda_articol_nemembru.md delete mode 100644 docs/cercetare/modifica_date_factura_parametri.md delete mode 100644 docs/cercetare/nota_contabila_fara_politica.md delete mode 100644 docs/cercetare/optiune_firma_cont_debit.md delete mode 100644 docs/cercetare/pack_auto_actualizeaza_deviz.md delete mode 100644 docs/cercetare/parametru_cont_contabilizeaza_articol.md delete mode 100644 docs/cercetare/rec_cale_vanzari_detalii.md delete mode 100644 docs/cercetare/rec_d42_efactura.md delete mode 100644 docs/cercetare/rec_datoria6_baza_regresie.md delete mode 100644 docs/cercetare/rec_dec42_proiectare.md delete mode 100644 docs/cercetare/rec_editare_factura.md delete mode 100644 docs/cercetare/rec_s4_runda1.md delete mode 100644 docs/cercetare/rec_s5_oracle_vanzari.md delete mode 100644 docs/cercetare/retur_si_lista_preturi.md delete mode 100644 docs/cercetare/roaauto_articole_lista_preturi.md delete mode 100644 docs/cercetare/roaauto_facturi.md delete mode 100644 docs/cercetare/rute_scriere_antet.md delete mode 100644 docs/cercetare/s10_pret_contract_reemitere.md delete mode 100644 docs/cercetare/s10_pret_rederivat.md delete mode 100644 docs/cercetare/s10_rederivare_pe_calea_reemiterii.md delete mode 100644 docs/cercetare/s11_legaturi_id_vanzare.md delete mode 100644 docs/cercetare/s2_factureaza_unificare.md delete mode 100644 docs/cercetare/s3_portare_antet.md delete mode 100644 docs/cercetare/s3b_alte_date_analitice.md delete mode 100644 docs/cercetare/s3c_sursa_ca_parametru.md delete mode 100644 docs/cercetare/s4_cautare_articole_server.md delete mode 100644 docs/cercetare/s4_punct2_registru_cantitate_ramasa.md delete mode 100644 docs/cercetare/s4_puncte_deschise.md delete mode 100644 docs/cercetare/s4b_bara_butoane_meniu.md delete mode 100644 docs/cercetare/s4c_discount_in_grid.md delete mode 100644 docs/cercetare/s4d_zi_curs_reactiv.md delete mode 100644 docs/cercetare/s4e_lista_preturi_pe_sursa.md delete mode 100644 docs/cercetare/s4f_retur_formular_unificat.md delete mode 100644 docs/cercetare/s4g_adaugare_articole_modificare.md delete mode 100644 docs/cercetare/s5_acoperire_tipuri.md delete mode 100644 docs/cercetare/s5b_proforma_descarcare_gestiune.md delete mode 100644 docs/cercetare/s5b_proiectare_proforma_copiere.md delete mode 100644 docs/cercetare/s5c_factura_din_proforma.md delete mode 100644 docs/cercetare/s8_incarcare_document.md delete mode 100644 docs/cercetare/s8b_rutarea_scrierii.md delete mode 100644 docs/cercetare/stoc_la_stergere_si_reemitere.md delete mode 100644 docs/cercetare/suprafata_regresie_contabilizeaza_articol.md delete mode 100644 docs/cercetare/verif_baza_vie_cont_venit.md delete mode 100644 docs/cercetare/verif_goluri_ntip_aviz.md delete mode 100644 docs/cercetare/verif_proforma_alegere_stoc.md delete mode 100644 docs/cercetare/zi_curs_validare.md create mode 100644 docs/erori_deschise.md delete mode 100644 docs/handoff_13_formular_unificat.md delete mode 100644 docs/handoff_curatenie_progres.md create mode 100644 docs/livrare_13.md delete mode 100644 docs/mockup_13_formular_unificat.html create mode 100644 docs/plan_13_executie.md create mode 100644 docs/sabloane/README_sablon_vcx.md create mode 100644 docs/sabloane/sablon_vcx_gol.vc2 diff --git a/CLAUDE.md b/CLAUDE.md index 2f474c5..4af4563 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -119,6 +119,12 @@ Depanare detaliata: `COMUN\docs\depanare_testare_vfp.md`. ## Mod de lucru: delegare catre subagenti + review inainte de commit +> **REGULA OBLIGATORIE (ceruta explicit de Marius, 26.08.2026):** orice investigatie, +> modificare de cod, editare de fisiere, rulare de teste sau verificare se deleaga unui +> SUBAGENT (Task tool). Sesiunea principala DOAR orchestreaza si verifica rezultatele +> subagentilor; nu lucreaza direct pe fisiere, nu ruleaza suite, nu editeaza cod. Inca +> de la prima sarcina a unui bloc, nu dupa ce ai acumulat context. + - **Predarea contextului e OBLIGATORIE, si pentru sesiunea principala, si pentru subagenti.** Nu e o optimizare, e o conditie de corectitudine. Declansatoare: **~50% din fereastra** (sesiunea principala / orchestrator), **~200-250k tokens** (subagent), anuntul de compactare @@ -140,3 +146,43 @@ Depanare detaliata: `COMUN\docs\depanare_testare_vfp.md`. iar sesiunea principala doar orchestreaza si verifica rezultatele. - **Fara commit fara review**: nu da commit (git sau svn) din proprie initiativa pe modificari de cod — diff-ul se livreaza intai ca fisier in `docs/`, iar commit-ul vine dupa aprobare. + +### Executie continua a unui plan (mandat permanent, Marius, 30.08.2026) + +> „vreau sa continui toate stories, cu commit dupa fiecare, si handoff la limita de context” + +Cand lucrezi la un plan cu mai multe stories, mandatul e valabil pana la revocare explicita: + +1. **Toate stories**, in ordinea de dependente din fisierul de executie al planului — fara oprire + dupa fiecare ca sa intrebi „continuam?”. +2. **Commit dupa fiecare story**, pe branch-ul de lucru, in repo-ul potrivit (cod `COMUN` din + `COMUN\`, restul din radacina proiectului). Review inainte de commit, dar **fara a mai cere + aprobare** — aprobarea e data pentru intreg lantul. Fara push; SVN si merge-ul raman la Marius. +3. **Handoff la limita de context** — vezi Regula zero de mai sus. Fisierul de stare viu al + planului se actualizeaza dupa FIECARE story inchisa, nu la final. + +Corolar cand Marius nu poate testa manual: pentru fiecare story al carui criteriu oficial de „gata” +e o proba manuala UI, se proiecteaza si o **proba automata de paritate** (headless sau prin +`vfp_ui_harness.ps1`), iar ce ramane obligatoriu manual se inregistreaza explicit ca **datorie +deschisa** — nu se declara „gata”. + +## Ponytail (plugin la nivel de user) + +Plugin-ul tert `ponytail` e activ implicit pe nivelul `full` la fiecare sesiune si reinjectat in +fiecare subagent — nu doar in sesiunea principala. Nu ruleaza retea si nu trimite telemetrie: scrie +doar un flag local (`~/.claude/.ponytail-active`) si citeste `~/.claude/settings.json`. + +Se pastreaza scara YAGNI/reuse-first (nu scrii ce exista deja in `inventar-comun.md`, +`COMUN\programe`, `COMUN\clase`, sau ce face VFP/Oracle nativ) — se suprapune cu regula +proiectului de modificari minime, SCOPED. + +Ce NU se aplica aici, cu regula proiectului care primeaza (detaliu complet: +`COMUN\docs\reguli_lucru.md`, punctul 3, „Scara ponytail”): +- comentarii `ponytail:` in cod -> interzise (regula 2); scurtatura si limita ei merg in + `docs\progres.md`, nu inline. +- proba „runnable test / assert self-check” -> script headless (regulile 4 si 8), nu `test_*.py`. +- „cod intai, maxim trei randuri” -> nu se aplica handoff-urilor, rapoartelor din `docs\` si + patch-urilor, care sunt output cerut explicit. +- `/ponytail-review` si `/ponytail-audit`: `delete:`/`yagni:` sunt ipoteze, nu constatari — in + `COMUN` cauti apelantii in tot `D:\ROA` inainte sa stergi; audit pe tot arborele nu se ruleaza aici. +- nivelul `ultra` nu se foloseste pe cod legacy partajat. diff --git a/Clase/ofacturare_util.vc2 b/Clase/ofacturare_util.vc2 new file mode 100644 index 0000000..073e9ac --- /dev/null +++ b/Clase/ofacturare_util.vc2 @@ -0,0 +1,136 @@ +*-------------------------------------------------------------------------------------------------------------------------------------------------------- +* (EN) AUTOGENERATED - ATTENTION!! - NOT INTENDED FOR EXECUTION!! USE ONLY FOR MERGING CHANGES AND STORING WITH SCM TOOLS!! +*-------------------------------------------------------------------------------------------------------------------------------------------------------- +*< FOXBIN2PRG: Version="1.21" SourceFile="ofacturare_util.vcx" CPID="1252" /> (Solo para binarios VFP 9 / Only for VFP 9 binaries) +* +* +DEFINE CLASS frm_cursuri_valutare AS _frmbase OF "_frm_base.vcx" + *< CLASSDATA: Baseclass="form" Timestamp="" Scale="Pixels" Uniqueid="" /> + + *-- OBJECTDATA items order determines ZOrder / El orden de los items OBJECTDATA determina el ZOrder + *< OBJECTDATA: ObjPath="grd_cursuri" UniqueID="" Timestamp="" /> + *< OBJECTDATA: ObjPath="grd_cursuri.cCurs.Header1" UniqueID="" Timestamp="" /> + *< OBJECTDATA: ObjPath="grd_cursuri.cCurs.Text1" UniqueID="" Timestamp="" /> + *< OBJECTDATA: ObjPath="grd_cursuri.cNume_val.Header1" UniqueID="" Timestamp="" /> + *< OBJECTDATA: ObjPath="grd_cursuri.cNume_val.Text1" UniqueID="" Timestamp="" /> + *< OBJECTDATA: ObjPath="grd_cursuri.cMultiplicator.Header1" UniqueID="" Timestamp="" /> + *< OBJECTDATA: ObjPath="grd_cursuri.cMultiplicator.Text1" UniqueID="" Timestamp="" /> + + * + AutoCenter = .T. + Caption = "Cursuri valutare" + DoCreate = .T. + Height = 224 + Name = "frm_cursuri_valutare" + Width = 420 + WindowType = 1 + _shape1.Name = "_shape1" + _shape1.Width = 422 + _shape2.Left = 370 + _shape2.Name = "_shape2" + Lb_titlu_alb_b121.Caption = "Curs valutar" + Lb_titlu_alb_b121.FontBold = .T. + Lb_titlu_alb_b121.Name = "Lb_titlu_alb_b121" + BUT_TERMIN1.Left = 390 + BUT_TERMIN1.Name = "BUT_TERMIN1" + * + + ADD OBJECT 'grd_cursuri' AS _grdrow WITH ; + ColumnCount = 3, ; + DeleteMark = .F., ; + FontName = "Arial Narrow", ; + Height = 180, ; + Left = 12, ; + Name = "grd_cursuri", ; + ScrollBars = 2, ; + TabIndex = 1, ; + Top = 32, ; + Width = 396, ; + Column1.ColumnOrder = 3, ; + Column1.ControlSource = "curs", ; + Column1.FontName = "Arial Narrow", ; + Column1.InputMask = (get_mask(14,gnPCurs)), ; + Column1.Name = "cCurs", ; + Column1.Width = 102, ; + Column2.ColumnOrder = 2, ; + Column2.ControlSource = "nume_val", ; + Column2.FontName = "Arial Narrow", ; + Column2.Name = "cNume_val", ; + Column2.Width = 130, ; + Column3.ColumnOrder = 1, ; + Column3.ControlSource = "multiplicator", ; + Column3.FontName = "Arial Narrow", ; + Column3.InputMask = (get_mask(10,0)), ; + Column3.Name = "cMultiplicator", ; + Column3.Width = 130 + *< END OBJECT: ClassLib="_grd_base.vcx" BaseClass="grid" /> + + ADD OBJECT 'grd_cursuri.cCurs.Header1' AS header WITH ; + Alignment = 2, ; + Caption = "Curs", ; + FontName = "Arial Narrow", ; + Name = "Header1" + *< END OBJECT: BaseClass="header" /> + + ADD OBJECT 'grd_cursuri.cCurs.Text1' AS textbox WITH ; + BackColor = 255,255,255, ; + BorderStyle = 0, ; + FontName = "Arial Narrow", ; + ForeColor = 0,0,0, ; + Margin = 0, ; + Name = "Text1", ; + SelectedBackColor = 0,64,128 + *< END OBJECT: BaseClass="textbox" /> + + ADD OBJECT 'grd_cursuri.cMultiplicator.Header1' AS header WITH ; + Alignment = 2, ; + Caption = "Multiplicator", ; + FontName = "Arial Narrow", ; + Name = "Header1" + *< END OBJECT: BaseClass="header" /> + + ADD OBJECT 'grd_cursuri.cMultiplicator.Text1' AS textbox WITH ; + BackColor = 255,255,255, ; + BorderStyle = 0, ; + FontName = "Arial Narrow", ; + ForeColor = 0,0,0, ; + Margin = 0, ; + Name = "Text1", ; + SelectedBackColor = 100,185,255 + *< END OBJECT: BaseClass="textbox" /> + + ADD OBJECT 'grd_cursuri.cNume_val.Header1' AS header WITH ; + Alignment = 2, ; + Caption = "Valuta", ; + FontName = "Arial Narrow", ; + Name = "Header1" + *< END OBJECT: BaseClass="header" /> + + ADD OBJECT 'grd_cursuri.cNume_val.Text1' AS textbox WITH ; + BackColor = 255,255,255, ; + BorderStyle = 0, ; + FontName = "Arial Narrow", ; + ForeColor = 0,0,0, ; + Margin = 0, ; + Name = "Text1", ; + SelectedBackColor = 0,64,128 + *< END OBJECT: BaseClass="textbox" /> + + PROCEDURE Init + Lparameters tcCursor, tdZiCurs, tnTip + Local lcCursor + DoDefault() + lcCursor = Iif(Empty(tcCursor), 'crscursuri', tcCursor) + If !Used(m.lcCursor) Or Reccount(m.lcCursor) = 0 + This.Release() + Return .F. + Endif + This.grd_cursuri.RecordSource = m.lcCursor + If !Empty(Nvl(m.tdZiCurs, {})) + This.Lb_titlu_alb_b121.Caption = "Curs valutar (" + Dtoc(m.tdZiCurs) + ")" + Else + This.Lb_titlu_alb_b121.Caption = "Curs valutar" + Endif + ENDPROC + +ENDDEFINE diff --git a/Clase/ofundal_facturare.vc2 b/Clase/ofundal_facturare.vc2 index eeeee74..ce70e02 100644 --- a/Clase/ofundal_facturare.vc2 +++ b/Clase/ofundal_facturare.vc2 @@ -32,11 +32,14 @@ DEFINE CLASS pg_meniu_princ AS pg_meniu OF "..\comun\clase\ofundal.vcx" *< OBJECTDATA: ObjPath="Page2.Cw8" UniqueID="" Timestamp="" /> *< OBJECTDATA: ObjPath="Page2.Pict_liniuta2" UniqueID="" Timestamp="" /> *< OBJECTDATA: ObjPath="Page2.Cw9" UniqueID="" Timestamp="" /> + *< OBJECTDATA: ObjPath="Page2.Cw10" UniqueID="" Timestamp="" /> + *< OBJECTDATA: ObjPath="Page2.Pict_liniuta1" UniqueID="" Timestamp="" /> *< OBJECTDATA: ObjPath="Page3.Pict_meniu1" UniqueID="" Timestamp="" /> *< OBJECTDATA: ObjPath="Page3.Cw1" UniqueID="" Timestamp="" /> *< OBJECTDATA: ObjPath="Page3.Cw4" UniqueID="" Timestamp="" /> *< OBJECTDATA: ObjPath="Page3.Cw3" UniqueID="" Timestamp="" /> *< OBJECTDATA: ObjPath="Page3.Cw2" UniqueID="" Timestamp="" /> + *< OBJECTDATA: ObjPath="Page3.Cw5" UniqueID="" Timestamp="" /> *< OBJECTDATA: ObjPath="Page4.Pict_meniu1" UniqueID="" Timestamp="" /> *< OBJECTDATA: ObjPath="Page4.Cw1" UniqueID="" Timestamp="" /> *< OBJECTDATA: ObjPath="Page4.Cw4" UniqueID="" Timestamp="" /> @@ -244,6 +247,16 @@ DEFINE CLASS pg_meniu_princ AS pg_meniu OF "..\comun\clase\ofundal.vcx" Label_item1.WordWrap = .T. *< END OBJECT: ClassLib="..\comun\clase\ofundal.vcx" BaseClass="container" /> + ADD OBJECT 'Page2.Cw10' AS cw WITH ; + Left = 5, ; + Name = "Cw10", ; + nid_cw = 10, ; + TabIndex = 10, ; + Top = 250, ; + Label_item1.Caption = "Facturare (nou)", ; + Label_item1.Name = "Label_item1" + *< END OBJECT: ClassLib="..\comun\clase\ofundal.vcx" BaseClass="container" /> + ADD OBJECT 'Page2.Cw2' AS cw WITH ; Left = 5, ; Name = "Cw2", ; @@ -348,6 +361,13 @@ DEFINE CLASS pg_meniu_princ AS pg_meniu OF "..\comun\clase\ofundal.vcx" LABEL_ITEM1.WordWrap = .T. *< END OBJECT: ClassLib="..\comun\clase\ofundal.vcx" BaseClass="container" /> + ADD OBJECT 'Page2.Pict_liniuta1' AS pict_liniuta WITH ; + Left = 6, ; + Name = "Pict_liniuta1", ; + Picture = ..\comun\grafice\f3.jpg, ; + Top = 239 + *< END OBJECT: ClassLib="..\comun\clase\ofundal.vcx" BaseClass="image" /> + ADD OBJECT 'Page2.Pict_liniuta2' AS pict_liniuta WITH ; Left = 6, ; Name = "Pict_liniuta2", ; @@ -411,6 +431,16 @@ DEFINE CLASS pg_meniu_princ AS pg_meniu OF "..\comun\clase\ofundal.vcx" Label_item1.WordWrap = .T. *< END OBJECT: ClassLib="..\comun\clase\ofundal.vcx" BaseClass="container" /> + ADD OBJECT 'Page3.Cw5' AS cw WITH ; + Left = 5, ; + Name = "Cw5", ; + nid_cw = 5, ; + TabIndex = 5, ; + Top = 106, ; + Label_item1.Caption = "Avize (nou)", ; + Label_item1.Name = "Label_item1" + *< END OBJECT: ClassLib="..\comun\clase\ofundal.vcx" BaseClass="container" /> + ADD OBJECT 'Page3.Pict_meniu1' AS pict_meniu WITH ; Height = 473, ; Left = 2, ; @@ -619,6 +649,9 @@ DEFINE CLASS pg_meniu_princ AS pg_meniu OF "..\comun\clase\ofundal.vcx" _grdrow1.cAgent.Header1.Name = "Header1", ; _grdrow1.cAgent.Name = "cAgent", ; _grdrow1.cAgent.Text1.Name = "Text1", ; + _grdrow1.cCodFiscal.Header1.Name = "Header1", ; + _grdrow1.cCodFiscal.Name = "cCodFiscal", ; + _grdrow1.cCodFiscal.Text1.Name = "Text1", ; _grdrow1.cComanda_externa.Header1.Name = "Header1", ; _grdrow1.cComanda_externa.Name = "cComanda_externa", ; _grdrow1.cComanda_externa.Text1.Name = "Text1", ; @@ -641,6 +674,9 @@ DEFINE CLASS pg_meniu_princ AS pg_meniu OF "..\comun\clase\ofundal.vcx" _grdrow1.cFacturat.Name = "cFacturat", ; _grdrow1.cFacturat._checkbox1.Alignment = 2, ; _grdrow1.cFacturat._checkbox1.Name = "_checkbox1", ; + _grdrow1.cIdPart.Header1.Name = "Header1", ; + _grdrow1.cIdPart.Name = "cIdPart", ; + _grdrow1.cIdPart.Text1.Name = "Text1", ; _grdrow1.cLucrare.Header1.Name = "Header1", ; _grdrow1.cLucrare.Name = "cLucrare", ; _grdrow1.cLucrare.Text1.Name = "Text1", ; @@ -690,6 +726,9 @@ DEFINE CLASS pg_meniu_princ AS pg_meniu OF "..\comun\clase\ofundal.vcx" _grdrow2.cNrCrt.Header1.Name = "Header1", ; _grdrow2.cNrCrt.Name = "cNrCrt", ; _grdrow2.cNrCrt.Text1.Name = "Text1", ; + _grdrow2.cNumeListaPreturi.Header1.Name = "Header1", ; + _grdrow2.cNumeListaPreturi.Name = "cNumeListaPreturi", ; + _grdrow2.cNumeListaPreturi.Text1.Name = "Text1", ; _grdrow2.cPret.Header1.Name = "Header1", ; _grdrow2.cPret.Name = "cPret", ; _grdrow2.cPret.Text1.Name = "Text1", ; @@ -697,6 +736,9 @@ DEFINE CLASS pg_meniu_princ AS pg_meniu OF "..\comun\clase\ofundal.vcx" _grdrow2.cPret_cu_tva.Name = "cPret_cu_tva", ; _grdrow2.cPret_cu_tva._checkbox1.Alignment = 2, ; _grdrow2.cPret_cu_tva._checkbox1.Name = "_checkbox1", ; + _grdrow2.cPTVA.Header1.Name = "Header1", ; + _grdrow2.cPTVA.Name = "cPTVA", ; + _grdrow2.cPTVA.Text1.Name = "Text1", ; _grdrow2.Height = 367, ; _grdrow2.Left = 173, ; _grdrow2.Name = "_grdrow2", ; @@ -734,7 +776,8 @@ DEFINE CLASS pg_meniu_princ AS pg_meniu OF "..\comun\clase\ofundal.vcx" Cmd_executa4.Top = 425, ; But_factura1.Name = "But_factura1", ; BUT_START_CRITERII1.Name = "BUT_START_CRITERII1", ; - BUT_RESET_CRITERII1.Name = "BUT_RESET_CRITERII1" + BUT_RESET_CRITERII1.Name = "BUT_RESET_CRITERII1", ; + But_attach1.Name = "But_attach1" *< END OBJECT: ClassLib="..\comun\clase\ocomenzi.vcx" BaseClass="container" /> ADD OBJECT 'Page6.Ct_lucrari_detalii1' AS ct_lucrari_detalii WITH ; @@ -883,6 +926,45 @@ DEFINE CLASS pg_meniu_princ AS pg_meniu OF "..\comun\clase\ofundal.vcx" DO facturare_lista_de_preturi IN oproceduri_facturare.prg ENDPROC + PROCEDURE Page2.Cw10.do_actiune + Private plFacturareNoua + plFacturareNoua = .T. + lcOptiuni = 'In lei;Retur factura in lei;Restaurant;Marfa in custodie - fara descarcare K;Marfa in custodie - cu descarcare K;\-;Invoice;Credit note;Factura fiscala in valuta;Retur factura in valuta' + lcOptiuni = m.lcOptiuni + ';\-;Contract: Factura lei;Contract: Invoice;Contract: Factura fiscala in valuta;\-;Pe baza de comanda;Din aviz' + lnOptiune = xmenu(m.lcOptiuni) + DO CASE + CASE m.lnOptiune = 1 + factureaza(1) + CASE m.lnOptiune = 2 + factureaza(8) + CASE m.lnOptiune = 3 + factureaza(45) + CASE m.lnOptiune = 4 + factureaza(49) + CASE m.lnOptiune = 5 + factureaza(48) + CASE m.lnOptiune = 7 + factureaza(5) + CASE m.lnOptiune = 8 + factureaza(7) + CASE m.lnOptiune = 9 + factureaza(10) + CASE m.lnOptiune = 10 + factureaza(9) + CASE m.lnOptiune = 12 + DO facturare_contracte WITH 'FACTURA LEI' IN oproceduri_facturare.prg + CASE m.lnOptiune = 13 + DO facturare_contracte WITH 'INVOICE' IN oproceduri_facturare.prg + CASE m.lnOptiune = 14 + DO facturare_contracte WITH 'FACTURA VALUTA' IN oproceduri_facturare.prg + CASE m.lnOptiune = 16 + goComanda = '' + DO facturare_comenzi IN oproceduri_facturare.prg + CASE m.lnOptiune = 17 + DO facturare_avize IN oproceduri_facturare.prg + ENDCASE + ENDPROC + PROCEDURE Page2.Cw2.do_actiune lnOptiune = xmenu('Factura lei;Invoice;Factura fiscala in valuta') DO CASE @@ -941,6 +1023,22 @@ DEFINE CLASS pg_meniu_princ AS pg_meniu OF "..\comun\clase\ofundal.vcx" DO aviz_subunitati.mpr ENDPROC + PROCEDURE Page3.Cw5.do_actiune + Private plFacturareNoua + plFacturareNoua = .T. + lnOptiune = xmenu('Cãtre clienþi;Cãtre clienþi în custodie;Cãtre clienþi debitori;Transfer între subunitãþi') + DO CASE + CASE m.lnOptiune = 1 + This.Parent.Cw1.do_actiune() + CASE m.lnOptiune = 2 + This.Parent.Cw2.do_actiune() + CASE m.lnOptiune = 3 + This.Parent.Cw3.do_actiune() + CASE m.lnOptiune = 4 + This.Parent.Cw4.do_actiune() + ENDCASE + ENDPROC + PROCEDURE Page4.Cw1.do_actiune lnOptiune = xmenu('Vizualizare \ + + + + + + + NOTE_CONTABILE`, `ff_...:7248-7253,7259-7260`), fara nicio ramura care sa citeasca `V_CONT`. -`V_CONT` alimenteaza doar `descarca_gestiune` (contul de iesire din gestiune, SCC pe nota de -**stoc**, nu de venit), nu nota de venit. - -### 2. Variabile de sesiune `pack_facturare` — **NU exista setter pentru cont** - -Am listat toate variabilele publice de pachet cu prefix `nid_` din specificatia `pack_facturare` -(`ff_...:72-211`, citat exhaustiv, cautare `grep -n "^ nid_"`): - -``` -nid_tipnir, nid_tipbon, nid_tipfactura, nid_tipaviz, nid_act, nid_serie, nid_fdoc, nid_part, -nid_part_rez, nid_lucrare, nid_sectie_stoc, nid_gestiune_sursa, nid_responsabil, nid_ordl, nid_set, -nid_util, nid_moneda_nationala, nid_fact, nid_factc, nid_partc, nid_jtva_coloana, nid_comanda, -nid_valuta, nid_politica_stoc, nid_sucursala, nid_venchelt, nid_vanzare, nid_beneficiar -``` - -Niciuna tipata pe un cont (`VARCHAR2(4)`/cod de cont) — toate sunt FK-uri numerice -(`%TYPE` pe alte tabele: `ID_SECTIE`, `ID_VENCHELT`, `ID_POL`, etc.). Cautare directa -`nid_scc`/`nid_scd`/`nid_cont` in tot fisierul: **zero rezultate**. `nid_venchelt` (folosit ca -fallback la `ID_VENCHELT` in `cursor_articol`, `ff_...:7244-7245`, deja documentat in -`coresp_cont_venchelt.md` sectiunea 7) e o clasificare dimensionala -(`NOM_VENIT_CHELTUIELI.ID_VENCHELT%TYPE`), nu un cont — **nu am gasit consumator care sa-l foloseasca -drept `SCC`**. Cautare separata `PROCEDURE SET_` in tot pachetul: zero rezultate — nu exista niciun -setter public de tip `SET_...` pentru cont. **Concluzie: NU.** - -### 3. `ACT_TEMP` scris direct din VFP — **DA, canal real, dar pe fluxul de editare, nu de emitere** - -Confirmat de `COMUN\docs\oracle_export.md:64-65`: *"Pachetul central de scriere a documentelor — -clientul VFP populeaza tabelele temporare `ACT_TEMP`/`RUL_TEMP` (prin `oscrie_in_fisiere`-ul din -COMUN), apoi pachetul distribuie"*. Mecanismul e generic si complet independent de `pack_facturare`: - -**Mecanismul** (`COMUN\programe\oscrie_in_fisiere.prg`): -- `sql_temp_insert('actactan','ACT_TEMP')` (`oscrie_in_fisiere.prg:127`) face, pentru fiecare rand al - oricarui cursor VFP numit `actactan`, un `INSERT INTO ACT_TEMP () VALUES (...)` construit dinamic din `user_tab_columns` - (`oscrie_in_fisiere.prg:190-280`) — **orice camp `SCD`/`ASCD`/`SCC`/`ASCC` prezent in cursorul VFP - ajunge direct in `ACT_TEMP`, indiferent de valoare, fara nicio validare de cont**. -- Scrierea efectiva in `ACT` se face prin `pack_contafin.init_scriere_act_rul_local` + - `final_scriere_act_rul_local` -> `SCRIE_IN_ACT`/`STERGE_DIN_ACT` (`oscrie_in_fisiere.prg:121-147`) - — **`pack_contafin`, nu `pack_facturare`**. Confirmat si in - `COMUN\docs\flux-modificare-stergere-nota-jurnal.md:20-28`. - -**Cine populeaza `actactan` cu valori calculate/editate de VFP** — cele doua cazuri gasite, ambele -active in productie: - -**(a) Registrul Jurnal — editare/stergere manuala de nota, orice document, generic** -(`COMUN\clase\comun.vc2`, clasa `afisjurcom`): -- `do_modifica` (`comun.vc2:2222-2562`): incarca nota existenta - (`Select * From v_act Into Cursor actactan`, `comun.vc2:2363`), instantiaza - `frm_modific2024` (`comun.vc2:2439`, `omodificari.vc2:6375`) — formular in care utilizatorul poate - edita direct `scd`/`ascd`/`scc`/`ascc` pe fiecare rand al notei (grid legat pe `tact`, verificare - prin `verific_analitic`/`do_verifica`, tipar prezent si in `omodificari.vc2:1714-1788` — comentat - azi, dar acelasi tipar activ e in `frm_modific2024`, vezi mai jos (b)). Dupa editare: - `oscrie_in_fisiere(2,...)` = sterge nota veche (`comun.vc2:2454`), apoi - `oscrie_in_fisiere(0,...)` = scrie nota noua **cu valorile din cursorul editat** - (`comun.vc2:2482`), apoi `pack_contafin.finalizeaza_modificare_nota(...)` - (`comun.vc2:2484-2486`). **Niciun apel catre `pack_facturare` in acest flux.** -- Documentat integral, cu aceleasi citate, in `COMUN\docs\flux-modificare-stergere-nota-jurnal.md`. -- E generic pe orice document din `ACT` (nu doar facturi) — daca o factura emisa are deja o nota, - poate fi editata de aici. - -**(b) Editarea facturii emise — acelasi mecanism, scop specific "facturi emise" (proiectul #6 -insusi)**, `COMUN\clase\ofacturare_comun.vc2:3769-3838` (functia `do_editare_factura`, citita -integral, **nu modificata**): - -``` -ofacturare_comun.vc2:3769 If IncarcaCursoareModificareNota(lnCod, pnAn, pnLuna, .F.) && ofacturare_editare.prg -... -ofacturare_comun.vc2:3787 Select a.*, Iif(Nvl(id_jtva_coloana,0)=0,0,1) As cu_Tva From tact a Into Cursor tact Readwrite -... -ofacturare_comun.vc2:3796 Omodif = Createobject([frm_modific2024], lnIdSet) -ofacturare_comun.vc2:3797 Omodif.Show() -ofacturare_comun.vc2:3799 If buton = 1 -ofacturare_comun.vc2:3800 If Thisform.do_deschide_tranzactie() -ofacturare_comun.vc2:3801 Select actactan -ofacturare_comun.vc2:3802 lnSucces = OSCRIE_IN_FISIERE(2,.T.,.T.) && sterge nota veche -ofacturare_comun.vc2:3803 If lnSucces > 0 -... -ofacturare_comun.vc2:3807 Select tact -ofacturare_comun.vc2:3808 Replace id_jtva_coloana With Null, proc_tva With 0 For cu_Tva = 0 -ofacturare_comun.vc2:3809 Select * From tact Into Cursor actactan Readwrite && re-materializeaza tact (inclusiv orice SCD/SCC editat) in actactan -ofacturare_comun.vc2:3810 Replace All id_util With gnIdUtil, sters With 0 -... -ofacturare_comun.vc2:3821 lnSucces = OSCRIE_IN_FISIERE(0,.T.,.T.) && scrie nota noua, direct din actactan, in ACT_TEMP -ofacturare_comun.vc2:3823 If lnSucces > 0 -ofacturare_comun.vc2:3824 lcSql = [begin pack_contafin.finalizeaza_modificare_nota(...); end;] -ofacturare_comun.vc2:3828 If lnSucces > 0 And "OFACTURARE_EDITARE" $ Upper(Set("Procedure")) And Used('tvanz')... -ofacturare_comun.vc2:3829 lnSucces = Iif(ScrieArticoleFacturaEditate(tvanz.id_vanzare), 1, -1) && scrie si VANZARI_DETALII -``` - -`IncarcaCursoareModificareNota` (`COMUN\programe\ofacturare_editare.prg:32-71`) incarca nota -existenta a facturii (`vact_tot -> v_act -> actactan -> tact`, toate **READWRITE**) inainte de a -arata `frm_modific2024`. Linia `:3809` **rescrie `actactan` direct din `tact`** — orice valoare -aflata pe `tact.scd`/`tact.scc` in acel moment (editata manual prin `frm_modific2024`, sau setata -programatic de orice cod VFP inainte de linia 3809) devine noua nota, scrisa in `ACT_TEMP` prin -`OSCRIE_IN_FISIERE(0,...)` la linia 3821 — **fara niciun apel `pack_facturare`, fara politica de -pret**. `pack_contafin.finalizeaza_modificare_nota` (linia 3824) doar re-leaga `cod`-ul nou de -`VANZARI`; `ScrieArticoleFacturaEditate` (linia 3829) scrie separat `VANZARI_DETALII`. - -**Aceasta e dovada cea mai puternica posibila pentru intrebarea pusa**: canalul nu e teoretic — e -**exact mecanismul pe care proiectul #6 il foloseste azi** ca sa permita editarea contului unei -facturi deja emise, complet in afara `pack_facturare`. - -**Limitarea reala**: mecanismul opereaza pe **o nota deja existenta** (sterge randul vechi din `ACT`, -scrie unul nou cu acelasi `id_fact`) — presupune ca factura a fost deja scrisa o data (prin -`pack_facturare`, cu politica). Nu e (azi) un canal pentru **emiterea** initiala a unei facturi noi -cu un articol fara politica — `scrie_factura2 -> contabilizeaza_articol` (calea de emitere) nu are -nicio ramura care sa ocoleasca politica si nu apeleaza acest mecanism. - -**Alte doua confirmari ale aceluiasi tipar, tot pentru categoria "facturi"**: -- `COMUN\ferestre\frm_initializare_facturi_balanta.sc2:458` — `CREATE CURSOR actactan (id_set I, an - N(4), luna N(2), Dataact D, datascad D, dataireg D, serie_act C(20), nract N(20), proc_tva N(5,2), - id_jtva_coloana I NULL, scd C(4), ascd C(4), scc C(4), ascc C(4), ...)` — cursor creat de la zero - in VFP, cu `scd`/`scc` campuri simple, editabile liber, fara nicio derivare Oracle. Foloseste tot - `frm_modific2024` (`:1806`) pentru editare/validare inainte de scriere. -- `COMUN\ferestre\frm_import_note_facturi_clienti.sc2:667,815,898,911` — acelasi tipar - (`actactan`/`tact` editabil + `frm_modific2024`), pentru import manual de note pe facturi de - clienti. - -Ambele sunt scenarii de **initializare/import** (sold de deschidere, import de note), nu de -facturare curenta — dar confirma ca `ACT_TEMP` pentru **categoria "facturi"** se scrie deja, in mod -curent, direct din cursoare VFP construite de la zero, fara `pack_facturare`. - -### 4. `ID_VENCHELT` / `CRM_NOTE_VANZARI` — **NU e canal alternativ catre SCC** - -Deja stabilit exhaustiv in `coresp_cont_venchelt.md` sectiunea 7: `ID_VENCHELT` e o dimensiune -(`NOM_VENIT_CHELTUIELI.ID_VENCHELT`), citita cu fallback pe `nid_venchelt` -(`ff_...:7244-7245`), dar **nu participa la calculul `SCC`** — `V_SCC` vine exclusiv din -`cursor_articol.D.SCC`. Nu exista camp de tip cont pe `VANZARI`/`VANZARI_DETALII` — reconfirmat aici -prin cautare `grep -inE "SCC|CONT_VENIT"` in blocurile `CREATE`/`ALTER TABLE VANZARI` din -`ff_...` (fara rezultate suplimentare fata de ce era deja stabilit). - -### 5. `GetAnaliticByGrupUtilizatori` — **doar analitic, nu cont sintetic** - -Confirmat deja in `coresp_cont_venchelt.md` sectiunea 7: functia da fallback pentru `ASCD`/`ASCC` -(analiticul), nu pentru `SCD`/`SCC` (contul sintetic). Nu exista o functie analoaga pentru cont — -cautare `GetSinteticBy`/`GetContBy` in tot `ff_...`: zero rezultate. - ---- - -## Lista exhaustiva a intrarilor catre `SCD`/`SCC` in `ACT_TEMP`, pe/langa fluxul de facturare - -Sursa: `ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` + fisierele VFP citate. - -1. **`pack_facturare.contabilizeaza_articol -> scrie_nota`** (`ff_...:7218-7556` / `12338-12570`, - `INSERT INTO ACT_TEMP` la `:12458-12544`) — calea principala de **emitere**. `SCD`/`SCC` vin din - `cursor_articol` (`D.SCD`, `D.SCC`, lantul politicii). Blocheaza cu FACT-024 - (`:7275-7311`) daca articolul nu e in politica — fara fallback (deja stabilit in - `nota_contabila_fara_politica.md`). -2. **Ramurile speciale din `scrie_factura2`** (transfer subunitati `ntip IN (23,25,30,41)`, custodie - `ntip IN (42,47)`, rata `id_rata<>0`) — tot `contabilizeaza_articol`, acelasi punct 1. -3. **`scrie_factura_avize_retur`** (`ff_...:6273-6666`, apel la `:6867`) si **`scrie_aviz_retur`** - (`ff_...:7094-7180`, apel la `:7149`) — tot `contabilizeaza_articol`. -4. **`descarca_gestiune`** (`ff_...:7476-7498`, apelata din interiorul buclei `cursor_articol`) — - scrie o a doua nota, de **iesire din gestiune** (`SCD`/`SCC` = conturi de stoc, din `V_CONT`/ - `poArticol.Cont`, nu din politica) — parte a fluxului de facturare, dar nu e nota de venit. -5. **Discount pe linie** (`ff_...:7501-7518`) — scrie o nota separata (vazuta pe date live ca - `id_nota=2`, `SCD=667`/`SCC=4111`, `verif_baza_vie_cont_venit.md` sectiunea 4) — tot in interiorul - `cursor_articol`, deci tot dependenta de politica pentru a ajunge acolo. -6. **`oscrie_vanzare_din_stoc`** (`COMUN\programe\ofacturare_stoc.prg:104-450`) — flux ROAGEST - "vanzare din stoc". Apeleaza `pack_facturare.adauga_articol_factura_stoc` per articol - (`:357-378`) apoi `oscrie_in_fisiere(0,.F.,.T.)` (`:392`). **Neverificat complet**: codul citit nu - arata explicit unde/cum se populeaza `actactan` cu `SCD`/`SCC` inainte de linia 392 in acest flux - particular (cursorul `ACTACTAN` e doar *citit* la `:253-289` pentru alte campuri, nu am gasit - punctul de scriere in fisierul citit) — posibil populat de o procedura anterioara neexaminata. - Marcat ca zona neinchisa, vezi "Ce nu s-a putut stabili". -7. **Registrul Jurnal — editare/stergere manuala** (`COMUN\clase\comun.vc2:2222-2724`, clasa - `afisjurcom`) — canalul confirmat la pista 3(a). Genereic, orice document din `ACT`. -8. **Editare factura emisa** (`COMUN\clase\ofacturare_comun.vc2:3769-3838`, proiectul #6) — canalul - confirmat la pista 3(b), specific "facturi emise". -9. **`frm_initializare_facturi_balanta.sc2`** si **`frm_import_note_facturi_clienti.sc2`** — canalul - confirmat la pista 3, scop initializare/import pe categoria "facturi". -10. **eFactura import** (`COMUN\clase\anaf_efactura.vc2:12241,13087-13102,13244-13325`) — acelasi - tipar generic (`actactan`/`tact` + `frm_modific2024` + `oscrie_in_fisiere`), dar pentru facturi - de **achizitie** importate din XML ANAF, nu facturi emise — mentionat pentru completitudine, nu - aplicabil direct la #13. -11. **`ointroduceri.vc2`/`ointroduceri.prg`** (NIR, BON, RETUR, intrare din gestiune valorica) si - **`oschimbare_pret.prg`**, **`oproceduri_rulaje.prg`**, **`reglari_denominare2005.prg`** — - acelasi canal generic `actactan`/`oscrie_in_fisiere`, dar pentru alte categorii de document - (gestiune, nu vanzare/factura). Mentionate pentru completitudinea listei de consumatori ai - canalului, nu aplicabile la #13. - -**Niciuna dintre intrarile 1-6 (fluxul de emitere efectiv) nu are o ramura care sa ocoleasca -politica.** Intrarile 7-11 folosesc toate acelasi canal generic (punctul 3), dar niciuna nu e -cablata pe fluxul de **emitere** — toate opereaza pe note deja existente sau pe alte categorii de -document. - ---- - -## DDL `ACT_TEMP` (interogat 10.08.2026, schema `MARIUSM_AUTO`, `ROA_CENTRAL`) - -| Coloana | Tip | Null | -|---|---|---| -| SCD | VARCHAR2(4) | **Y** | -| ASCD | VARCHAR2(4) | Y | -| SCC | VARCHAR2(4) | **Y** | -| ASCC | VARCHAR2(4) | Y | -| COD, NRACT, SUMA, PERECHED, PERECHEC, SUMA_VAL, CURS, NEIMPOZAB, NNIR, ID_UTIL, ID_UTILS, ID_RESPONSABIL, ID_VENCHELT, ID_SECTIE, ID_SET, ID_FACT, ID_PARTD, ID_PARTC, ID_FDOC, ID_LUCRARE, ID_GESTIN, ID_GESTOUT, ID_VALUTA, PROC_TVA, STERS, ID_FACTD, ID_FACTC, VALIDAT, TVA_INCASARE | numeric | N (29 coloane NOT NULL, restul optionale) | - -Restul coloanelor (52 in total) sunt fie `NUMBER` (majoritatea `NOT NULL`), fie `DATE`/`VARCHAR2` -opționale (`DATAIREG`, `EXPLICATIA`, `DATASCAD`, `EXPLICATIA4/5`, `ID_SUCURSALA`, `ID_ACT`, `ID_CTR`, -`ID_JTVA_COLOANA`, `SERIE_ACT`, `ID_UTILV`, `DATAORAV`, `TAXCODE`, `PAYMENTCODE`). - -Constrangeri: **29 `CHECK` constraints** (`SYS_C0014966`...`SYS_C0014994`) — corespund exact -coloanelor `NOT NULL` de mai sus (Oracle genereaza `CHECK (coloana IS NOT NULL)` pentru `NOT NULL` -declarat fara nume). **Niciun constraint pe `SCD`/`SCC`** (nici `NOT NULL`, nici `CHECK` de format, -nici FK catre un plan de conturi). **Nicio cheie primara/unica** — normal pentru un tabel `_TEMP` de -staging. Concluzie: schema **nu blocheaza in niciun fel** o valoare `SCC` calculata de VFP, oricare -ar fi ea, inclusiv `NULL`. - ---- - -## Ce nu s-a putut stabilit si de ce - -- **Punctul exact unde `ACTACTAN` primeste `SCD`/`SCC` in fluxul `oscrie_vanzare_din_stoc`** - (`ofacturare_stoc.prg`, intrarea 6 de mai sus) — codul citit (`:104-450`) nu arata sursa; ar - necesita urmarirea completa a apelantului (`initializeaza_vanzare_din_stoc`) si a modulului care - populeaza `ACTACTAN` inainte de acest punct (posibil `pack_facturare.adauga_articol_factura_stoc` - intoarce un ref cursor legat automat de `goExecutor` pe numele `actactan`, dar nu am gasit apelul - explicit). Nu schimba verdictul (oricum ar fi tot `pack_facturare`, deci tot politica), dar ramane - o zona neinchisa complet. -- **Daca discountul pe linie (`SCD=667`/`SCC=4111`) are vreo cale alternativa in afara - `cursor_articol`** — nu verificat aici, presupus dependent de politica la fel ca restul buclei. -- **Comportamentul practic al `frm_modific2024` cu privire la validarea contului tastat manual** - (daca exista vreo verificare `verific_cont`/plan de conturi la salvare, sau accepta orice text de - 4 caractere) — nu urmarit pana la capat in `omodificari.vc2`; relevant doar daca se propune - reutilizarea canalului 3(b)/3(a) pentru scriere programatica (fara interactiune UI), caz in care ar - trebui verificat daca `frm_modific2024` insusi impune vreo validare care ar bloca o scriere - automata. -- **Daca fluxul de emitere (`scrie_factura2`) ar putea, teoretic, sa scrie intai o factura "goala" de - nota (sau cu o nota tehnica minimala) si apoi sa foloseasca imediat, in aceeasi tranzactie logica, - mecanismul de la pista 3(b) pentru a corecta `SCC`-ul** — nu explorat aici (ar fi o propunere de - design, in afara scopului acestei cercetari read-only); mentionat doar ca directie posibila care - reiese direct din ce s-a gasit. -- Nu am cautat/citit `pack_contafin.SCRIE_IN_ACT`/`STERGE_DIN_ACT` in `PACK_CONTAFIN.pck` pentru a - confirma ca acestea, la randul lor, nu re-deriva `SCC` din altceva — presupunerea (bazata pe - `oracle_export.md` si pe `flux-modificare-stergere-nota-jurnal.md`, care descriu deja aceste - proceduri drept "distribuie ce a scris clientul", nu "recalculeaza") e ca fac un simplu - `INSERT INTO ACT SELECT * FROM ACT_TEMP`-tip transfer, nu o derivare noua — dar nu am deschis - personal codul lor in aceasta runda. diff --git a/docs/cercetare/cont_venit_corespondente.md b/docs/cercetare/cont_venit_corespondente.md deleted file mode 100644 index 0b3e7ec..0000000 --- a/docs/cercetare/cont_venit_corespondente.md +++ /dev/null @@ -1,240 +0,0 @@ -# Corespondenta cont de stoc (3xx) -> cont de venit (7xx): ce exista in cod - -**Context**: continuare a `cont_venit_articol_fara_politica.md`, care stabilise ca `VANZARI_DETALII.CONT` -e un cont de **gestiune/stoc** (371, 301-303, 357), fara sa gaseasca un cont de venit pe linia de -factura in `pack_facturare`, si lasase neverificat unde se genereaza efectiv contul de venit. -Cercetarea de fata raspunde la cele 4 intrebari, cu corectii fata de raportul anterior. - -## Raspuns scurt - -1. **Nu exista un tabel de corespondenta "3xx -> 7xx"** (nume de forma `CORESPONDENTA*`, - `NOM_CONTURI*`, `CONT_VENIT*`, `ARTICOLE_CONTABILE*`) nicaieri in `SCRIPTURI_CLAR`. In schimb - exista un **mecanism echivalent functional, dar configurat manual, nu derivat automat din contul - de stoc**: tabelul `NOTE_CONTABILE` (coloane `SCD`/`ASCD` = cont+analitic debitor, - `SCC`/`ASCC` = cont+analitic creditor), legat de politica de pret a articolului prin - `CRM_POLITICI_PRETURI.ID_NOTA -> CRM_NOTE_VANZARI.ID_SET -> NOTE_CONTABILE`. Un contabil - configureaza acest tabel dintr-un ecran dedicat (`frm_config_note_contabile[2007]`), nu se - calculeaza din `NOM_ARTICOLE.CONT`/`STOC.CONT`. -2. **`NOM_ARTICOLE.CONT` NU e restrans la conturi de stoc (clasa 3).** Validarea la editare - (`verific_cont`) verifica doar ca respectivul cod exista in planul de conturi al anului curent - (`vplcont_sintetic`), fara filtru pe clasa. UI-ul trimite explicit catre "Planul de conturi" - (buton de help general), nu catre un subset de conturi de stoc. Asta confirma afirmatia lui - Marius: campul accepta orice cont, inclusiv 6xx/7xx. -3. **Nota contabila a vanzarii SE genereaza in `pack_facturare`** — raportul anterior a cautat - literalii `707`/`704`/`706`/`708` (care nu apar hardcodati nicaieri, corect) si a conchis gresit - ca lipseste mecanismul. El exista, dar e **indirect**: `pack_facturare.contabilizeaza_articol` - (`ff_2026_08_06_10_COMUN_PACK_FACTURARE.sql:7182-7556`) ia `SCD`/`SCC` din `NOTE_CONTABILE` (via - politica de pret a articolului) si scrie nota cu `pack_facturare.scrie_nota(...)`. `SCC` (contul - creditor) e contul de venit efectiv al liniei — nu vine din `VANZARI_DETALII.CONT` (care ramane - contul de gestiune, folosit separat, doar pentru descarcarea de gestiune, in - `descarca_gestiune`). -4. **`ID_VENCHELT`/`NOM_VENIT_CHELTUIELI` nu are coloana de cont** (nicio `ALTER TABLE - NOM_VENIT_CHELTUIELI ADD ... CONT` in `SCRIPTURI_CLAR`). E o dimensiune analitica separata - (clasificare venituri/cheltuieli, posibil pentru raportare/buget), transmisa ca parametru propriu - catre `scrie_nota`, in paralel cu `SCD`/`SCC` — nu e sursa contului. - -## 1. Nu exista tabel de corespondenta 3xx->7xx; exista `NOTE_CONTABILE` + politica de pret - -### Cautare tabel dedicat — negativa -Cautari in `D:\ROA\DATABASE\SCRIPTURI_CLAR` (`CREATE TABLE`/`ALTER TABLE`, case-insensitive) pentru -`CORESPONDENT*`, `NOM_CONTURI*`, `CONT_VENIT*`, `PLAN_CONTURI*`, `ARTICOLE_CONTABILE*`: niciun -rezultat relevant — singurele hituri pe `CORESPONDENT` sunt cuvantul romanesc generic ("banca -corespondenta" etc.) in sute de fisiere fara legatura, iar `CONT_VENIT` nu apare deloc ca nume de -tabel/coloana. - -### Mecanismul real: lant de 4 tabele, configurat pe politica de pret -`pack_facturare.contabilizeaza_articol` (`ff_...:7227-7280`) foloseste acest cursor pentru a afla -contul debitor/creditor al liniei de vanzare: - -```sql -CURSOR cursor_articol IS - SELECT A.ID_POL_ART, A.ID_POL, A.ID_ARTICOL, A.PRET, ... - NVL(pack_facturare.nid_venchelt, NVL(A.ID_VENCHELT, C.ID_VENCHELT)) AS ID_VENCHELT, - NVL(pack_facturare.nid_sectie_stoc, C.ID_SECTIE) as ID_SECTIE, - C.ID_SET, D.EXPLICATIE, D.SCD, D.ASCD, D.SCC, D.ASCC, D.CU_TVA, ... - FROM CRM_POLITICI_PRET_ART A - LEFT JOIN CRM_POLITICI_PRETURI B ON A.ID_POL = B.ID_POL - LEFT JOIN CRM_NOTE_VANZARI C ON B.ID_NOTA = C.ID_NOTA - LEFT JOIN NOTE_CONTABILE D ON C.ID_SET = D.ID_SET - WHERE A.ID_POL = detalii_articol.id_pol AND A.ID_ARTICOL = detalii_articol.id_articol; -``` -(`ff_2026_08_06_10_COMUN_PACK_FACTURARE.sql:7227-7280`) - -Lantul e: **articol + politica de pret** (`CRM_POLITICI_PRET_ART`, deja documentat in raportul -anterior ca avand `PRET`/`PROC_TVAV`/`ID_VALUTA`, fara `CONT`) `->` **antetul politicii de pret** -(`CRM_POLITICI_PRETURI.ID_POL`, care are `ID_NOTA`) `->` **nota de vanzare CRM** -(`CRM_NOTE_VANZARI.ID_NOTA`, care are `ID_SET`) `->` **sablonul de nota contabila** -(`NOTE_CONTABILE.ID_SET`, care are `SCD`/`ASCD`/`SCC`/`ASCC`/`EXPLICATIE`/`CU_TVA`/`IN_VALUTA`). - -Apoi, la scriere (`ff_...:7405-7437`): -```sql -CASE - WHEN pack_facturare.ntip <= 20 or pack_facturare.ntip IN (...) THEN - -- factura, factura roahotel - V_SCD := crs_rand_articol.scd; - V_ASCD := NVL(crs_rand_articol.ascd, PACK_FACTURARE.GetAnaliticByGrupUtilizatori(...)); - WHEN pack_facturare.ntip in (28, 29) THEN - -- aviz catre clienti debitori - V_SCD := '461'; ... - ELSE - -- aviz - V_SCD := '418'; ... -END CASE; - -V_SCC := crs_rand_articol.scc; -V_ASCC := NVL(crs_rand_articol.ascc, PACK_FACTURARE.GetAnaliticByGrupUtilizatori(...)); -``` -(`ff_2026_08_06_10_COMUN_PACK_FACTURARE.sql:7407-7437`) - -Pentru o factura normala (`ntip <= 20`), `V_SCD` e contul debitor din nota (tipic un cont de -creante, 411/etc.), iar `V_SCC` e contul creditor din nota — **acesta e efectiv contul de venit** -al liniei (707/704/706/708, in functie de cum a configurat contabilul nota respectiva), preluat -**direct din `NOTE_CONTABILE.SCC`**, fara nicio derivare din contul de stoc al articolului. Perechea -`V_SCD`/`V_SCC` (plus `ID_VENCHELT`, `ID_SECTIE`, `EXPLICATIE`, cota TVA) e trimisa la -`pack_facturare.scrie_nota(...)` (`ff_...:7452-7476`), care scrie randul in nota contabila -efectiva a vanzarii. - -**Concluzie fata de ipoteza lui Marius**: mecanismul exista si rezolva exact problema — "de unde -stiu contul de venit al unei linii de vanzare, indiferent daca articolul e gestionabil sau nu" — -dar **nu e o corespondenta automata cont-stoc -> cont-venit**. E o **configurare manuala per -politica de pret**: fiecare politica de pret (`CRM_POLITICI_PRETURI`) e legata la o "nota de -vanzare" CRM (`CRM_NOTE_VANZARI`), care la randul ei e legata la un sablon de nota contabila -(`NOTE_CONTABILE`) cu perechea SCD/SCC fixata de un contabil. Politica de pret aplicata articolului -decide, indirect, contul de venit — nu clasa/valoarea contului de gestiune al articolului. - -### Ecranul de configurare (unde se seteaza SCD/SCC) -`D:\ROA\ROAFACTURARE\COMUN\ferestre\frm_config_note_contabile.sc2` (+ varianta -`frm_config_note_contabile2007.sc2` pentru anii >= 2007, selectata in -`COMUN\programe\omeniu_initializari.prg:257-282`) e formularul din meniu "Configurare note -contabile". Clasa e `onote_contabile.vc2`, cu un grid `gridInregistrari` avand coloanele -`cScd`/`cAscd`/`cScc`/`cAscc` (`onote_contabile.vc2:2401-2435`) legate direct de -`cnote_contab.scd`/`.scc`/`.ascd`/`.ascc`, si salvare prin INSERT/UPDATE direct pe -`note_contabile(explicatie,scd,ascd,scc,ascc,ordine,cu_tva,ptva,id_set,in_valuta,...)` -(`onote_contabile.vc2:315-322`, `:486-493`). Deci **contabilul e cel care scrie manual perechea -SCD/SCC** (inclusiv contul de venit `SCC`) pentru fiecare `id_set`/nota, nu exista automatism care -sa deriveze `SCC` din contul de gestiune al articolului. - -## 2. `NOM_ARTICOLE.CONT` — nu e restrans la clasa 3 - -- **Camp**: `Clb_tx_cont.Text_simplu1.ControlSource = "porec.cont"`, `MaxLength = 4`, eticheta - `"Cont*"` (obligatoriu), buton de help cu tooltip `"Help - Planul de conturi (CTRL+ H)"` — - `COMUN\clase\onom_articole.vc2:1058-1087`. Trimiterea catre "Planul de conturi" (nu catre un - subset "conturi de stoc") sugereaza ca formularul asteapta orice cont valid, nu doar clasa 3. -- **Validare la Valid() al campului** (in alt formular generic de conturi, `onomenclatoare.vc2`, nu - in cel de articole, dar foloseste aceeasi functie globala): - ``` - PROCEDURE clb_cont.Text_simplu1.Valid - IF !EMPTY(this.Value) - lnVerificat = verific_cont(thisform.orec.cont) - IF lnVerificat < 0 - RETURN 0 - ENDIF - ENDIF - ENDPROC - ``` - (`COMUN\clase\onomenclatoare.vc2:3251-3258`) -- **`verific_cont`** (`COMUN\programe\oproceduri_comune.prg:2389-2415`): - ``` - Procedure verific_cont - Parameters tcCont, tlNoMessage - lcSel = [SELECT cont from ] + GCS + [.vplcont_sintetic WHERE TRIM(cont) = ?pcCont and an = ?gnAn ] - ... - If Reccount('crs_verific') = 0 - lnSucces = -1 - Endif - If lnSucces < 0 And !tlNoMessage - amessagebox('Acest cont nu este definit in planul de conturi!', 0 + 48, 'Atentie') - Endif - Endproc - ``` - Singura conditie e ca `cont` sa existe in `vplcont_sintetic` (planul de conturi sintetic al - anului curent, `gnAn`) — **fara `LIKE '3%'`, fara `SUBSTR(cont,1,1) = '3'`, fara nicio alta - restrictie de clasa**. O varianta identica exista si in - `COMUN\programe\ooperatii_comune.prg:1307` (nu am comparat corp cu corp, dar semnatura e aceeasi). -- Nu exista `CHECK CONSTRAINT` pe `NOM_ARTICOLE.CONT` in `SCRIPTURI_CLAR` (cautare - `CHECK.*CONT`/`constraint.*CONT.*check` — zero rezultate relevante), deci nici la nivel de - baza de date nu exista o restrictie de clasa. -- Nu am gasit nicaieri in `COMUN` sau `ROAFACTURARE` o ramificare pe `Left(cont,1)`/ - `SUBSTR(cont,1,1)` aplicata specific campului `NOM_ARTICOLE.CONT` (cautare in - `D:\ROA\ROAFACTURARE` si `D:\ROA\ROAFACTURARE\COMUN`) — singurul loc cu `SUBSTR(cont,1,1)` gasit e - in `onomenclatoare.vc2:3647`, un filtru de grid pe planul de conturi general (tab-uri "1", "2", - "3"... pentru navigare in plan), fara legatura cu articolele. - -**Concluzie**: campul e validat generic (orice cont din planul de conturi al anului), fara -restrictie de clasa la nivel de UI sau DB. Asta confirma ce a spus Marius: un articol negestionabil -poate avea legitim un cont 6xx/7xx in `NOM_ARTICOLE.CONT`. - -## 3. Unde se genereaza efectiv nota contabila a vanzarii - -Raportul anterior cauta gresit literalii `707`/`704`/`706`/`708` (niciunul nu apare hardcodat — corect) -si a conchis ca nu exista mecanism in `pack_facturare`. De fapt **exista, dar e in `pack_facturare`, -nu intr-un pachet separat de contabilitate**: - -- **Functia**: `pack_facturare.contabilizeaza_articol(detalii_articol VANZARI_DETALII_TEMP%ROWTYPE)` - (`ff_2026_08_06_10_COMUN_PACK_FACTURARE.sql:7182-7556`), apelata pe fiecare rand din - `VANZARI_DETALII_TEMP` — vezi apelul `V_INCASAT_CALCUL := V_INCASAT_CALCUL + - pack_facturare.contabilizeaza_articol(tab_detalii(i));` in `scrie_aviz_retur` - (`ff_...:7148-7150`); acelasi tipar se repeta si in fluxul principal de facturare (nu doar in - aviz-retur — semnatura si structura functiei arata ca acopera si `ntip <= 20`, adica factura - propriu-zisa, prin `CASE ... WHEN pack_facturare.ntip <= 20 ...` la `ff_...:7409-7421`). -- **Sursa `SCD`/`SCC`**: vezi sectiunea 1 — cursorul `cursor_articol` (`ff_...:7227-7280`), pe baza - politicii de pret a articolului (`detalii_articol.id_pol`), nu pe `VANZARI_DETALII.CONT`. -- **`VANZARI_DETALII.CONT` ramane folosit separat, doar pentru gestiune**: in aceeasi functie, - `descarca_gestiune(...)` primeste explicit `detalii_articol.cont` ca parametru - (`ff_...:7485-7507`, apelat cand `nscadere_stoc = 1 AND id_gestiune <> -1000 AND in_stoc = 1`) — - acesta e contul de gestiune/stoc (371/301-303/357 etc., documentat in raportul anterior), - folosit pentru randul de descarcare de gestiune (marfa iesita din stoc), complet independent de - `V_SCD`/`V_SCC` calculate mai sus pentru randul de venit al vanzarii. **Cele doua conturi - coexista pe aceeasi linie de vanzare, cu surse complet diferite**: unul (gestiune) vine din - `NOM_ARTICOLE.CONT`/`STOC.CONT`/`NOM_GESTIUNI.CONT` (cf. raport anterior), celalalt (venit) vine - din `NOTE_CONTABILE.SCC` via politica de pret. -- **Nu exista fallback/hardcodare** pentru `SCC`: daca `NOTE_CONTABILE` nu are un rand pentru - `ID_SET`-ul politicii, `LEFT JOIN`-urile din `cursor_articol` produc `NULL` pe `SCC`/`SCD` - (fara eroare explicita in acest cursor — spre deosebire de cazul `V_COMPUS`/politica lipsa la - `ff_...:7293-7311`, care ridica `FACT-024`). - -## 4. `ID_VENCHELT` / `NOM_VENIT_CHELTUIELI` - -- **Nu are coloana de cont**: nicio `ALTER TABLE NOM_VENIT_CHELTUIELI ADD ... CONT` in - `SCRIPTURI_CLAR` (cautare directa, zero rezultate) si nicio `CREATE TABLE NOM_VENIT_CHELTUIELI` - in arhiva (tabelul predateaza 2009, inceputul arhivei `SCRIPTURI_CLAR` — la fel ca - `NOTE_CONTABILE`, `CRM_NOTE_VANZARI`, `CRM_POLITICI_PRETURI`, pentru care nu exista `CREATE TABLE` - in arhiva din acelasi motiv). -- **Structura vazuta din uz**: `NOM_VENIT_CHELTUIELI.ID_VENCHELT%TYPE` (tip declarat repetat in - `pack_facturare`, ex. `ff_...:191`) si `ACT.ID_VENCHELT%TYPE` (`ff_...:6725`) — deci - `ID_VENCHELT` e o coloana FK pe `ACT` (tabelul de note contabile efective) si pe `CRM_NOTE_VANZARI` - / `CRM_POLITICI_PRET_ART` (vezi `NVL(B.ID_VENCHELT, D.ID_VENCHELT)` la `ff_...:7205`, unde `B` = - `CRM_POLITICI_PRET_ART`, `D` = `CRM_NOTE_VANZARI` in cursorul comentat din pachet). -- **Cine il consuma**: `pack_facturare.contabilizeaza_articol` il rezolva cu prioritate similara - contului: `NVL(pack_facturare.nid_venchelt, NVL(A.ID_VENCHELT, C.ID_VENCHELT))` (override global de - sesiune -> articol/politica -> nota de vanzare) si il trimite ca parametru propriu catre - `scrie_nota(...)` (`ff_...:7461`), **separat** de `SCD`/`SCC`. E deci o **a treia dimensiune** - (clasificare venituri/cheltuieli) atasata randului de nota contabila, nu sursa contului insusi — - raportul anterior avea dreptate sa il caracterizeze ca "o clasificare, nu un cont", desi nu - urmarise consumatorul pana la capat. -- Numele `NOM_VENIT_CHELTUIELI` si folosirea alaturi de `SCD`/`SCC`/`ID_SECTIE` sugereaza un - centru de venituri/cheltuieli pentru raportare (posibil situatii financiare pe sectii/centre de - cost), nu un mecanism alternativ de determinare a contului 7xx. - -## Neverificat - -- **Structura completa (toate coloanele) a `NOTE_CONTABILE`, `CRM_NOTE_VANZARI`, - `CRM_POLITICI_PRETURI` si `NOM_VENIT_CHELTUIELI`**: niciuna nu are `CREATE TABLE` in - `SCRIPTURI_CLAR` (arhiva incepe 2009, tabelele sunt mai vechi). Coloanele folosite in cod sunt - confirmate (`SCD`/`ASCD`/`SCC`/`ASCC`/`EXPLICATIE`/`CU_TVA`/`IN_VALUTA`/`ID_SET` pe - `NOTE_CONTABILE`; `ID_NOTA`/`ID_SET` pe `CRM_NOTE_VANZARI`; `ID_NOTA`/`ID_POL` pe - `CRM_POLITICI_PRETURI`; `ID_VENCHELT` pe `NOM_VENIT_CHELTUIELI`), dar lista completa ar necesita - interogarea schemei Oracle live (`DESC NOTE_CONTABILE` etc.) sau un export DDL mai vechi decat - 2009, in afara bugetului acestei cercetari. -- **Continutul efectiv al `NOTE_CONTABILE.SCC`** pentru notele configurate curent (adica ce conturi - 707/704/706/708 sunt de fapt setate, si daca fiecare politica de pret are o nota configurata) — - ar necesita interogarea datelor din schema Oracle live, nu doar codul static. -- **Daca `contabilizeaza_articol` e chiar apelata pe fluxul principal de facturare** (nu doar din - `scrie_aviz_retur`) — am dedus asta din `CASE ... ntip <= 20 ...` (factura normala tratata explicit - in functie), dar nu am urmarit *toti* apelantii ei in fisier (fisierul are 17000+ linii; cautarea - `pack_facturare.contabilizeaza_articol` ar trebui repetata exhaustiv daca se doreste certitudine - completa pe toate punctele de intrare — facturare avans, deviz, retail etc.). -- **Comportamentul cand `NOTE_CONTABILE`/`CRM_NOTE_VANZARI` lipsesc pentru o politica de pret**: - cursorul foloseste `LEFT JOIN`, deci `SCD`/`SCC` ar ajunge `NULL` fara eroare explicita in acest - punct — nu am verificat daca exista o validare ulterioara (la `scrie_nota` sau la commit-ul notei) - care sa blocheze o nota cu cont `NULL`. diff --git a/docs/cercetare/coresp_cont_venchelt.md b/docs/cercetare/coresp_cont_venchelt.md deleted file mode 100644 index 4eb7702..0000000 --- a/docs/cercetare/coresp_cont_venchelt.md +++ /dev/null @@ -1,800 +0,0 @@ -# CORESP_CONT_VENCHELT ca sursa de cont de venit pentru articole fara politica de pret (decizia 27) - -Cercetare pe cod, read-only, fara modificari. Continua `cont_venit_articol_fara_politica.md` si -`cont_venit_corespondente.md` (care stabilisera ca SCC vine azi din `NOTE_CONTABILE` prin lantul -politicii de pret) si `nota_contabila_fara_politica.md` (care stabilise ca `contabilizeaza_articol` -ridica FACT-024 si opreste tranzactia cand nu exista politica). - -## 9. Intrebarea care putea rasturna tot: "in ROAACNPRO si pe factura din comanda/contract se adauga articole fara politica de preturi — de ce nu si aici?" - -**Raspuns scurt, verificat pe cod: impresia e explicabila, dar niciunul din cele doua exemple nu e de -fapt un articol fara politica trecut cu bine prin `contabilizeaza_articol`. FACT-024 ramane blocantul -real — nu exista azi, in productie, niciun flux in care un articol ajunge la `contabilizeaza_articol` -fara sa fie membru al unei politici si sa treaca fara eroare.** - -### 9a) Ce verifica de fapt FACT-024 — apartenenta articolului la politica liniei, nu "id_pol e gol" - -Blocul (`D:\ROA\ROAFACTURARE\docs\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:7278-7302`, citat integral -la sectiunea 6) face: -```sql -SELECT COMPUS, ID_POL_ART - INTO V_COMPUS, V_ID_POL_ART - FROM VCRM_POLITICI_PRET_ART - WHERE ID_ARTICOL = detalii_articol.id_articol - AND ID_POL = detalii_articol.id_pol; -``` -Mesajul confirma exact asta: `'Articolul ' || ... || ' nu este definit in politica de preturi ' || ...` -— verifica **apartenenta articolului la politica `id_pol` care a ajuns pe linie**, nu doar "exista vreun -`id_pol`". Insa `id_pol` **nu e derivat de Oracle** din client/contract/tip document — e parametru de -intrare explicit (`V_ID_POL IN NUMBER`, `adauga_articol_factura`, `ff_...:4993`, citat integral la -sectiunea 8a), trimis de VFP pe fiecare linie. Deci sunt **doua cauze distincte care duc la aceeasi -eroare**: -1. `id_pol` e NULL/gol pe linie (articolul a fost ales dintr-o cautare care nu are deloc coloana - `id_pol` — cazul `caut_articol`, sectiunea 9d) — `NO_DATA_FOUND` trivial (`X = NULL` nu se potriveste - niciodata). -2. `id_pol` e o valoare reala, dar articolul **nu e** in lista acelei politici — la fel `NO_DATA_FOUND`, - cu acelasi mesaj. -Ambele cauze converg spre FACT-024; codul nu le distinge in eroare. Formularea din intrebarea lui -Marius ("nu se cauta politica articolului, ci apartenenta articolului la politica documentului") e -**partial corecta**: verificarea e de apartenenta, dar `id_pol` nu vine din "antetul documentului" ca -o singura valoare fixa — vine per-linie, de la formularul de adaugare a articolului (sectiunea 9d). - -### 9b) ROAACNPRO — importul Roris/Contracte NU trece deloc prin `contabilizeaza_articol` - -Verificat direct in `D:\ROA\ROAACNPRO\Programe\proceduri_acnpro.prg` (read-only). Cautare -`pack_facturare\.|pack_acn\.|pack_contafin\.` in tot fisierul — apelurile de la salvarea facturii -(`factura_salvare_db`, `:3331-3467`) sunt: -``` -proceduri_acnpro.prg:3332 lcSql = [begin pack_facturare.initializeaza_scriere_actrul(NULL,1); end;] -proceduri_acnpro.prg:3343 lcSql = [begin pack_facturare.initializeaza_date_factura(... -proceduri_acnpro.prg:3382 lcSql = [begin pack_facturare.adauga_articol_factura_deviz(... -proceduri_acnpro.prg:3412 lcSql = [begin pack_facturare.scrie_in_vanzari(0,... -proceduri_acnpro.prg:3430 lcSql = [begin pack_acn.salveaza_regdoc(?.id, ?.id_vanzare, ?.tip, ... -``` -**Exact tiparul deja documentat pentru ROAAUTO "Alte servicii" in `nota_contabila_fara_politica.md`**: -`adauga_articol_factura_deviz` (insereaza doar in `VANZARI_DETALII_TEMP`, fara `ID_POL` — semnatura -n-are acest parametru) urmat de `scrie_in_vanzari` (copiaza in `VANZARI_DETALII`, **fara sa scrie vreo -nota contabila de venit**) — **`contabilizeaza_articol` nu apare deloc** in acest fisier (cautare -directa `contabilizeaza_articol` in `proceduri_acnpro.prg`: zero rezultate). Nota contabila a -documentului ACN o scrie **`pack_acn.salveaza_regdoc`**, o procedura complet separata, specifica ACN, -apelata cu parametri proprii (`?.id_articol, ?.id_locatia, ?.id_valuta, ?.id_client, ?.id_contract, -?.pret, ?.curs, ?.cantitate, ?.valval, ?.tvaval, ?.totval, ...`) — **fara `id_pol`** in lista de -parametri. Confirmat si de raportul deja existent `COMUN\docs\cercetare\import_roris_roaacnpro.md` -(sectiunea 3): "**nicio procedura Oracle stocata (`pack_*`) nu e apelata in etapa de import** ... -acestea sunt apelate abia la salvarea finala", si articolele/preturile vin din dialoage de calcul tarife -proprii ACN (`frm_calcul_tranzit`, `vctr_articole2`), nu din `CRM_POLITICI_PRET_ART`. - -**Concluzie 9b**: ROAACNPRO nu e un exemplu de "articol fara politica trecut cu bine prin -`contabilizeaza_articol`" — e un produs care **nu foloseste deloc** acest mecanism de contabilizare -pentru facturile lui. Impresia lui Marius e intemeiata pe experienta ("acolo merge fara sa aleg vreo -politica"), dar explicatia e alta decat "FACT-024 se poate evita" — e ca **acel flux nu ruleaza deloc -codul care ar putea da FACT-024**. - -### 9c) Factura din comanda/contract in ROAFACTURARE — articolul "liber" e de fapt din lista de preturi - -Deja cercetat, cu dovezi, in `docs\cercetare\retur_si_lista_preturi.md` sectiunea B (citit integral aici, -nu doar rezumat). Punctul central (`retur_si_lista_preturi.md` sectiunea B7): -- **Contract** (tip 2,6,26,52): `crsarticole` (grila principala de articole, folosita si pentru - adaugare "libera") e populat de `pack_facturare.cursor_contract(...)`, care da **lista de preturi - completa**, nerestrictionata la continutul contractului — "azi se poate deja adauga liber din lista de - preturi pe un document contract". -- **Comanda** (tip 3): `crsarticole` e populat de `cursor_comanda`, **doar** articolele comenzii — nu - exista adaugare libera in fluxul normal (doar prin copiere de document, care re-adauga tot din - `cursor_preturi`). - -Verificat aici, suplimentar, ca `cursor_preturi` (folosit si de `cursor_contract`, structura identica) -**selecteaza explicit `A.ID_POL`** ca coloana de output: -```sql --- ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:2162-2167 (cursor_preturi, ramura V_TIP = 45, tipic pentru toate ramurile CASE) -OPEN V_CURSOR FOR - SELECT rownum as id_c, - B.ID_ARTICOL, - NULL AS LOT, - NULL as SERIE, - A.ID_POL, - ... -``` -si ca cursorul insusi porneste, la fiecare apel, prin `pack_facturare.completare_politica_stoc()` -(`ff_...:2151`), care garanteaza deja randuri in `CRM_POLITICI_PRET_ART` pentru toate articolele -`IN_STOC=1` (procedura citata integral la sectiunea 8c2). Deci **fiecare articol afisat in grila -"lista de preturi"/"contract" vine deja cu un `id_pol` valid, dintr-o politica reala**, exact pentru ca -e extras printr-un join pe `CRM_POLITICI_PRETURI`/`CRM_POLITICI_PRET_ART`. "Adaugare libera pe contract" -nu inseamna "articol fara politica" — inseamna "orice articol din lista de preturi, nu doar cele din -contract", dar tot cu politica ceruta, doar aleasa implicit prin cursor, nu prin dialogul manual -`do_cauta_politica`. - -**Concluzie 9c**: nu e un contraexemplu la FACT-024 — e acelasi mecanism (articol + politica), livrat -printr-o alta grila de selectie (`crsarticole` populat de `cursor_contract` in loc de `caut_articol`), -care intampla sa nu ceara utilizatorului sa caute manual politica pentru ca cursorul o aduce deja -atasata pe fiecare rand. - -### 9d) Ce diferentiaza de fapt "adaugare din nomenclator" (cazul #13) de "adaugare din lista de preturi/contract" (cazul care merge azi) - -Diferenta e cursorul sursa al gridului de articole, nu tipul de document: -- **`caut_articol`** (`COMUN\programe\ocautare.prg:1636-1735`, folosit pentru cautare libera in - nomenclator) — SELECT-ul confirmat la `ocautare.prg:1671-1689` (`vnom_articole_crm`/`vnom_articole`): - **nu are coloana `id_pol`** in nicio ramura (`tlCRM` sau nu). Un articol ales prin acest dialog ajunge - in `poArticol` (scatter din `crsarticole`, `ofacturare.vc2:12850-12851`) **fara `id_pol`** — exact - cazul care da FACT-024 azi, si exact cazul cerut de povestea #13 ("adaugat direct din nomenclator"). -- **`cursor_preturi`/`cursor_contract`** (Oracle, sectiunea 9c) — id_pol e parte din rezultat, pentru ca - interogarea porneste de la politica de pret, nu de la nomenclator. - -Deci povestea #13 cere exact ce nu exista azi: un articol ales **prin cautarea de nomenclator** -(fara trecere prin vreo politica), caruia i se cere totusi sa treaca prin `contabilizeaza_articol` fara -eroare. Niciunul din exemplele lui Marius (ROAACNPRO, contract) nu demonstreaza ca asta functioneaza deja -undeva — primul nu foloseste deloc functia, al doilea nu e de fapt "fara politica". - -### Verdict 9 - -**FACT-024 ramane blocantul real**, confirmand (nu contrazicand) analiza din sectiunile 6-8. Nu exista -azi, pe niciun flux de productie cercetat, un caz in care un articol trece prin -`pack_facturare.contabilizeaza_articol` fara sa fie membru al unei politici de pret si nu cade cu -eroare. Impresia lui Marius e intemeiata pe doua experiente reale, dar explicatia lor e alta: -- ROAACNPRO: alt produs, alta procedura de contabilizare (`pack_acn.salveaza_regdoc`), niciodata - `contabilizeaza_articol`; -- Contract in ROAFACTURARE: articolul PARE liber, dar e adus printr-un cursor care il livreaza deja cu - o politica reala atasata (`cursor_contract`/`cursor_preturi` + `completare_politica_stoc`). - -Asta nu inseamna ca planul A (sectiunea 8, VFP alege singur un `id_pol`) sau planul B (sectiunea 6, -fallback in pachet) sunt de abandonat — arata insa ca **niciuna din cele doua nu se poate simplifica -la "nu faceti nimic, functioneaza deja"**: mecanismul de protectie e real si consecvent, nu un artefact -uitat. - -## Constrangere noua (Marius, aparuta in timpul cercetarii): pack_facturare e plan B - -Marius prefera sa **nu se scrie cod nou in `pack_facturare`** — e pachetul comun folosit de toata -suita, si orice ramura noua acolo e risc peste tot, nu doar in ROAFACTURARE. Sectiunea 6 (punctul de -injectie in pachet) ramane in raport ca informatie, dar devine **plan B**. Intrebarea reala, tratata in -sectiunea 8 de mai jos: **se poate obtine acelasi rezultat din VFP, alegand singur un `id_pol` potrivit, -fara sa se modifice `pack_facturare`?** Raspuns scurt: **da, ingredientele exista deja**, dar solutia nu -e "zero cod" — e cod VFP nou (nu Oracle), care se sprijina pe 3 mecanisme deja functionale in productie. -Detalii in sectiunea 8. - -## Concluzie: fezabil cu conditii, si conditiile sunt mai mari decat pare din formularea deciziei 27 - -0. **Verificare prioritara (sectiunea 9, ceruta de Marius in timpul cercetarii): FACT-024 ramane - blocantul real.** Nu exista azi niciun flux de productie in care un articol trece prin - `contabilizeaza_articol` fara sa fie membru al unei politici de pret si nu cade cu eroare. - ROAACNPRO nu e un contraexemplu — nu foloseste deloc `contabilizeaza_articol` (alta procedura de - contabilizare, `pack_acn.salveaza_regdoc`). Contractul in ROAFACTURARE nu e un contraexemplu — - articolele "libere" vin din `cursor_contract`/`cursor_preturi`, care le ataseaza deja o politica - reala. Detalii complete in sectiunea 9. -1. **Tabelul exista, e populat cu date reale si e folosit activ in productie** — dar niciodata pentru - `CONT_VENIT`. Toti consumatorii gasiti (pack_vin, pack_devize, gestiune_pack_gest_import, - gestiune_rapoarte) citesc `CONT_CHELT` (sau `CONT_APROVIZIONARE`), niciunul `CONT_VENIT`. Coloana - `CONT_VENIT` e completata cu date reale (o migrare din 2023 acopera 20 de conturi de gestiune), dar - n-are niciun consumator — nici Oracle, nici VFP. Cititul ei pentru facturare ar fi un consumator nou, - nu o reteta deja rulata in productie. -2. **Cheia de join e stabila si testata**: mereu `CONT` (contul de gestiune al liniei), cu filtru - `STERS = 0`, prin `LEFT JOIN`. Pattern-ul se poate copia identic pentru `CONT_VENIT`. -3. **Punctul care schimba verdictul de la "fezabil simplu" la "fezabil cu conditii mari"**: in - `contabilizeaza_articol`, ramura "articol simplu" nu executa **nimic** (nici `scrie_nota`, nici - `descarca_gestiune`) daca `cursor_articol` (care citeste din `CRM_POLITICI_PRET_ART`) nu gaseste - niciun rand — si nu gaseste niciun rand exact in cazurile in care azi se ridica FACT-024. Un simplu - "prinde exceptia si continua" ar produce o linie facturata **fara nota contabila si fara descarcare - de gestiune, fara nicio eroare** — mai rau decat blocajul actual. Fallback-ul cere o **ramura noua, - paralela cursorului**, nu doar inlocuirea unei valori. -4. **Chiar cu ramura noua, `CU_TVA` si `IN_VALUTA` (cota TVA inclusa in pret / linie in valuta) nu au - nicio sursa alternativa** in afara `NOTE_CONTABILE`. Corespondentele si `NOM_ARTICOLE.CONT` dau doar - un cont, nu aceste doua semnale de interpretare a pretului — ele trebuie fie hardcodate (risc de - calcul gresit al bazei/TVA), fie cerute ca intrare noua de la utilizator/articol. -5. **704 ca literal de fallback are un precedent real in suita**, dar in alt subsistem: proprietatea - `cconte = 704` ("cont implicit articol client") din `frm_configurare_efactura` - (`COMUN\clase\anaf_efactura.vc2:8376`), folosita la import eFactura, nu in `pack_facturare`. Nu e o - reteta gata scrisa pentru facturare, dar arata ca alegerea lui Marius nu e arbitrara in suita. - -## 1. `CORESP_CONT_VENCHELT` exista? Structura, DDL, consumatori - -**Exista.** Nu are `CREATE TABLE` in arhiva `D:\ROA\DATABASE\SCRIPTURI_CLAR` (arhiva incepe 2009-2010, -tabelul e mai vechi — acelasi motiv pentru care `NOTE_CONTABILE`/`CRM_NOTE_VANZARI` nu au `CREATE TABLE` -in arhiva, documentat deja in `cont_venit_corespondente.md`). - -**Coloane confirmate din `ALTER TABLE` + `CREATE OR REPLACE VIEW` (cronologic):** -- `CONT`, `CONT_CHELT`, `CONT_VENIT`, `STERS` — preexistente arhivei (folosite direct in view-ul din - 2010 fara `ALTER TABLE` premergator vizibil). -- `CONT_APROVIZIONARE varchar2(4)` — adaugata 19.02.2010 - (`D:\ROA\DATABASE\SCRIPTURI_CLAR\2010\02\ff_2010_02_19_03_GESTIUNI.sql:9`: - `alter table CORESP_CONT_VENCHELT add CONT_APROVIZIONARE varchar2(4);`). -- `CONT_DIFERENTE varchar2(4)` — adaugata 05.02.2015 - (`D:\ROA\DATABASE\SCRIPTURI_CLAR\2015\02\ff_2015_02_05_01_GESTIUNE.sql:6-7`: - `alter table coresp_cont_venchelt add cont_diferente varchar2(4);` + - `comment on column CORESP_CONT_VENCHELT.cont_diferente is 'cont diferente de pret pe NIR fata de - pretul din factura de achizitie (ex: 6588 = 401 sau 308 = 401)';`). -- Nu am gasit `ID_CCV`/`DATAORAS` in DDL-ul cercetat (cautare directa, zero rezultate in - `SCRIPTURI_CLAR`) — interogarea din decizia 27 (`select id_ccv, cont, cont_chelt, cont_venit, sters, - dataoras, cont_aprovizionare, cont_diferente from coresp_cont_venchelt`) le presupune, dar tabelul - fiind pre-arhiva, DDL-ul complet nu e in cod static. **Ramane de verificat pe baza vie** (vezi sectiunea - finala) — probabil `ID_CCV` e cheia surogat (PK) si `DATAORAS` un timestamp tehnic, tipar comun in - suita ROA pentru tabelele de configurare, dar neconfirmat din DDL. -- Toate coloanele `CONT*` sunt `varchar2(4)` (confirmat din `ALTER TABLE` explicit pentru - `CONT_APROVIZIONARE`/`CONT_DIFERENTE`; consistent cu tratarea lor ca cod de cont in tot codul citit). - -**View-ul consumat de VFP**, `VCORESP_CONT_VENCHELT`, filtreaza deja `STERS = 0` (deci VFP nu mai trebuie -sa filtreze separat): -```sql --- D:\ROA\DATABASE\SCRIPTURI_CLAR\2015\02\ff_2015_02_05_01_GESTIUNE.sql:10-13 -create or replace view vcoresp_cont_venchelt as -select cont, cont_chelt, cont_venit, cont_aprovizionare, cont_diferente - from coresp_cont_venchelt - where sters = 0; -``` - -**Populare cu date reale — `CONT_VENIT` e completat, nu doar declarat.** Migrare dedicata 23.02.2023 -(`D:\ROA\DATABASE\SCRIPTURI_CLAR\2023\02\ff_2023_02_23_01_COMUN.sql:1-21`, titlul chiar din fisier: -`-- Completare cont venit pe corespondenta 3xx - 6xx/7xx`): -```sql -update CORESP_CONT_VENCHELT set cont_venit = '707' where cont = '301'; -update CORESP_CONT_VENCHELT set cont_venit = '707' where cont = '302'; -update CORESP_CONT_VENCHELT set cont_venit = '707' where cont = '3021'; -... (3022,3023,3024,3025,3026,3028, 303 -> tot '707') -update CORESP_CONT_VENCHELT set cont_venit = '711' where cont = '331'; -update CORESP_CONT_VENCHELT set cont_venit = '711' where cont = '332'; -update CORESP_CONT_VENCHELT set cont_venit = '702' where cont = '341'; -update CORESP_CONT_VENCHELT set cont_venit = '7015' where cont = '345'; -update CORESP_CONT_VENCHELT set cont_venit = '703' where cont = '346'; -update CORESP_CONT_VENCHELT set cont_venit = '7015' where cont = '348'; -update CORESP_CONT_VENCHELT set cont_venit = '7018' where cont = '361'; -update CORESP_CONT_VENCHELT set cont_venit = '707' where cont = '371'; -update CORESP_CONT_VENCHELT set cont_venit = '707' where cont = '381'; -``` -Deci pentru conturile de gestiune uzuale (marfuri 371/381, materii prime 301-303/3021-3028, produse -finite 345/348, semifabricate 341, animale 346, ambalaje 381) exista deja o valoare `CONT_VENIT` -configurata in Oracle, de 3 ani. **Datele exista** — intrebarea e doar cine le citeste. - -**Ecran de configurare in VFP: NU exista.** Cautare exhaustiva `CORESP_CONT_VENCHELT` (case-insensitive) -in `D:\ROA\ROAFACTURARE\COMUN` -> exact 3 fisiere, niciunul un formular de editare: -- `COMUN\clase\ointroduceri.vc2:2` — un comentariu de istoric (19.02.2010, mentioneaza - `crsconfigcvc (coresp_cont_venchelt)`), nu cod. -- `COMUN\programe\ointroduceri.prg:733` (procedura `update_corespondente_cvc`, citata integral mai jos) - — un `SELECT` care **reincarca** un cursor local, nu scrie in tabel. -- `COMUN\programe\updateserver.prg:733` — acelasi `SELECT`, alt loc de apel. - -``` --- COMUN\programe\ointroduceri.prg:727-743 -Procedure update_corespondente_cvc - If Used('crsconfigcvc') - Use In crsconfigcvc - Endif - *!* 19.02.2010 - lcSql = [select cont,cont_chelt,cont_venit, cont_aprovizionare, cont_diferente from vcoresp_cont_venchelt order by cont] - *!* 19.02.2010 ^ - lnSucces = goExecutor.oExecute(lcSql,[crsconfigcvc]) - If lnSucces < 0 - amessagebox(goExecutor.cEroare,16,"Eroare") - Endif - goExecutor.oReset() - Return lnSucces -Endproc -``` - -Deci tabelul e intretinut exclusiv prin **scripturi SQL manuale** (cele din `SCRIPTURI_CLAR`, rulate de un -DBA/dezvoltator la nevoie), nu printr-un formular VFP — la fel ca alte tabele tehnice de configurare mai -vechi din suita. Nu exista niciun `INSERT INTO CORESP_CONT_VENCHELT` in arhiva (randurile de baza, -`CONT`/`CONT_CHELT`/`CONT_VENIT`, predateaza arhiva); doar `UPDATE`/`ALTER TABLE` pentru coloanele -adaugate ulterior (`CONT_APROVIZIONARE` in 2010, `CONT_DIFERENTE` in 2015, valorile `CONT_VENIT` in -2023). - -## 2. Cheia de cautare - -**`CONT` = contul de gestiune al liniei, mereu.** Confirmat prin 4 exemple reale de `JOIN`, in module -diferite: - -- **pack_vin** (`D:\ROA\DATABASE\SCRIPTURI_CLAR\2023\07\ff_2023_07_18_02_VIN_PACK_VIN.sql:1579-1589`, - procedura `inregistreaza_materiale`): - ```sql - FROM (SELECT SUM(...) as suma, A.CONT as SCC, A.ACONT as ASCC, A.ID_GESTIUNE - FROM VIN_RULAJE_TEMP A WHERE A.TIP = V_TIP - GROUP BY A.ID_GESTIUNE, A.CONT, A.ACONT) A - LEFT JOIN CORESP_CONT_VENCHELT B - ON A.SCC = B.CONT - AND B.STERS = 0; - ``` - aici `A.SCC` e de fapt contul de gestiune (aliasul `SCC` vine din contextul rulajului de stoc, nu din - `NOTE_CONTABILE`) — deci join-ul e tot `cont_gestiune = B.CONT`. -- **pack_devize** (14+ aparitii identice intre 2013-2014, ex. - `D:\ROA\DATABASE\SCRIPTURI_CLAR\2014\01\ff_2014_01_22_01_DEVIZE_PACK_DEVIZE.sql:3214-3217`): - ```sql - from rul_temp a - left join coresp_cont_venchelt b - on a.cont = b.cont - and b.sters = 0 - ``` - `rul_temp.cont` e contul de gestiune al articolului din rulajul de stoc. -- **gestiune_pack_gest_import** (5+ aparitii 2013-2017, ex. - `D:\ROA\DATABASE\SCRIPTURI_CLAR\2017\05\ff_2017_05_31_01_GESTIUNE_PACK_GEST_IMPORT.sql:512`): - `left join coresp_cont_venchelt b` — pe `a.cont = b.cont` (acelasi tipar). -- **Raport recent, 2026** (`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\07\ff_2026_07_23_01_GESTIUNE_RAPOARTE.sql:25-29`, - view `VRUL_ACT_CHELTUIELI`): - ```sql - iesiri AS ( - SELECT R.*, CC.CONT_CHELT AS CONT_CHELT_CORESP - FROM VRUL_TOT R - LEFT JOIN CORESP_CONT_VENCHELT CC ON CC.CONT = R.CONT AND CC.STERS = 0 - WHERE R.STERS = 0 AND R.CANTE <> 0 - ) - ``` - cel mai recent consumator gasit (23.07.2026), deci tabelul e inca "viu" in productie — dar - `CONT_CHELT_CORESP` calculat aici nici nu apare in `SELECT`-ul final al view-ului (e calculat si - abandonat), semn ca tiparul de "join pe CONT, ia campul de care ai nevoie" e suficient de standard - incat sa fie reutilizat fara alta discutie de design. -- **In VFP**: `Select crsconfigcvc; Locate For Cont = Upper(Alltrim(...))` — vezi sectiunea 3, mereu - aceeasi cheie `CONT`. - -**Nu am gasit nicio discriminare suplimentara pe gestiune/tip document.** Toate `JOIN`-urile/`LOCATE` -sunt strict pe `CONT` (+ `STERS = 0`), fara `ID_GESTIUNE`, fara `TIP_DOC`. `ID_CCV` nu apare folosit ca -FK in niciun cod cercetat — pare o simpla cheie surogata a tabelului, neexpusa in `VCORESP_CONT_VENCHELT` -(view-ul nu o include). - -**Ambiguitate / mai multe randuri active pentru acelasi `CONT`**: nu exista `UNIQUE`/`PRIMARY KEY` -vizibil in cod (DDL-ul e pre-arhiva), dar **toate cele ~20 de utilizari reale presupun un singur rand -activ per `CONT`** — niciun cod cercetat foloseste `DISTINCT`, `ROW_NUMBER()`, `MAX(...)` sau alt -mecanism de dezambiguizare pe rezultatul join-ului. Daca ar exista doua randuri active cu acelasi `CONT`, -`LEFT JOIN`-ul ar multiplica randurile din interogarea principala (risc de dublare de suma in -`pack_vin`/`pack_devize`) — nu exista protectie in cod contra acestui caz. **Presupunerea de unicitate e -implicita in tot codul care exista deja**, nu doar in propunerea lui Marius. - -## 3. Cine il foloseste azi — CONT_CHELT are 4 module consumatoare, CONT_VENIT are zero - -| Consumator | Fisier | Coloana citita | -|---|---|---| -| `pack_vin.inregistreaza_materiale` | `ff_2023_07_18_02_VIN_PACK_VIN.sql:1567` (si variante 2010/2012/2014) | `CONT_CHELT` | -| `pack_devize` (procedura de calcul cost material, 14+ export-uri 2013-2014) | `ff_2014_01_22_01_DEVIZE_PACK_DEVIZE.sql:3222` (si altele) | `CONT_CHELT` (in `GROUP BY`, folosit ca `SCD`) | -| `pack_gest_import` (5+ export-uri 2013-2017) | `ff_2017_05_31_01_GESTIUNE_PACK_GEST_IMPORT.sql:512` | `CONT_CHELT` (deductie din context, tipar identic) | -| `VRUL_ACT_CHELTUIELI` (raport, 2026) | `ff_2026_07_23_01_GESTIUNE_RAPOARTE.sql:26` | `CONT_CHELT` (calculat, needatat in output final) | -| VFP `ointroduceri.vc2` (bon consum, finalizare aprovizionare, transfer) | `:5476-5490` (`oinventar.vc2`), `:14663-14681`, `:15327-15332` | `CONT_CHELT`, `CONT_APROVIZIONARE` | -| VFP `ointroduceri.vc2` (diferente NIR vs factura) | `:15395-15403` (bloc dezactivat `IF .F.`) | `CONT_DIFERENTE` (cod comentat/mort, nu ruleaza azi) | - -**Niciun consumator pentru `CONT_VENIT`** — cautare directa `CONT_VENIT` (case-insensitive) in tot -`D:\ROA\DATABASE\SCRIPTURI_CLAR` (arhiva completa 2009-2026): doar 3 fisiere, toate deja citate (2 view-uri -care includ coloana in `SELECT` fara sa o consume, 1 script de populare cu date). In VFP, cautare -`cont_venit` (case-insensitive) in `COMUN` si `ROAGEST`: un singur hit, `SELECT ... cont_venit ...` in -`update_corespondente_cvc`/`updateserver.prg` (fetch in cursor), **zero utilizari ale campului dupa -fetch** (nicio referinta `.cont_venit`/`crsconfigcvc.cont_venit` gasita). - -**Concluzie**: propunerea lui Marius ar fi **primul consumator real al `CONT_VENIT`** in toata suita, -desi coloana e populata din 2023. Reteta "citeste corespondenta pe cont de gestiune" e deja rulata in -productie de 10+ ani, dar mereu pentru latura `CONT_CHELT` (cheltuiala la consum de stoc), niciodata -pentru `CONT_VENIT` (venit la vanzare). Riscul tehnic al join-ului (cheie, filtru `STERS`, tipul -`LEFT JOIN`) e deja validat de utilizarea existenta; riscul de **date** (daca valorile `CONT_VENIT` -completate in 2023 sunt corecte/complete pentru toate conturile de gestiune folosite azi in facturare, -nu doar cele 20 acoperite explicit) e nou si neverificat pe baza vie. - -## 4. Semantica coloanelor - -Dedusa din utilizari reale, nu din nume: - -- **`CONT`**: contul de gestiune/stoc al liniei (clasa 3xx: 301-303, 331/332, 341, 345/346/348, 361, - 371, 381) — cheia de cautare in toate cazurile vazute (sectiunea 2). -- **`CONT_CHELT`**: contul de cheltuiala (6xx) care se debiteaza cand marfa/materialul iese din gestiune - (consum, bon de consum, productie) — confirmat de toti cei 4+ consumatori din sectiunea 3, unde e - folosit mereu ca `SCD` (debit) pe o nota de tip "iesire din gestiune". -- **`CONT_VENIT`**: prin simetrie de nume si migrarea din 2023 ("cont venit pe corespondenta 3xx-7xx"), - e menit sa fie contul de venit (7xx) corespunzator vanzarii aceluiasi cont de gestiune — dar **nu are - niciun consumator care sa confirme comportamentul in executie** (nicio ramura de cod care sa arate ce - se intampla la `NULL`, la vanzare partiala, la retur). Semantica e dedusa din nume + date, nu din - utilizare, contrar regulii "nu presupune semantica dupa nume" — de aceea marchez ca **ipoteza, nu - fapt verificat pe comportament**. -- **`CONT_APROVIZIONARE`**: contul folosit la operatia "finalizare aprovizionare" (`ID_SET` 264/265), - cand se transfera valoarea din contul de tranzit (401 sau alt cont de gestiune) intr-un cont de - gestiune specific de tranzit intern (32x) — confirmat de comentariul din DDL 2010 si de utilizarea in - `ointroduceri.vc2:15323-15332` (`Case Alltrim(Upper(oSet.explicatia)) = 'FINALIZARE APROVIZIONARE' ... - lcContC = Alltrim(cont_aprovizionare)`). -- **`CONT_DIFERENTE`**: contul folosit pentru diferentele de pret intre NIR si factura de achizitie — - confirmat de comentariul DDL 2015 ("cont diferente de pret pe NIR fata de pretul din factura de - achizitie, ex: 6588 = 401 sau 308 = 401") si de codul `ointroduceri.vc2:15394-15403`, dar acel bloc e - **dezactivat** (`IF .F. THEN ... ENDIF`), deci in executia curenta ramane mereu fallback-ul hardcodat - `'6588'` — dovada ca chiar si un camp cu semantica clara si comentariu explicit poate ajunge neutilizat - in productie, ceea ce intareste incertitudinea despre `CONT_VENIT`. -- **`STERS`**: flag boolean soft-delete, filtrat explicit `= 0` in **toate** utilizarile gasite (view-ul - `VCORESP_CONT_VENCHELT` il filtreaza deja la nivel de view; join-urile Oracle directe pe tabel il - filtreaza explicit `AND B.STERS = 0`/`AND CC.STERS = 0`). Niciun cod cercetat citeste randuri cu - `STERS = 1`. -- **`DATAORAS`**: nu apare in niciun cod cercetat (Oracle sau VFP) — nu am gasit nicio referinta directa - la aceasta coloana in afara interogarii propuse in decizia 27. Tipic in suita ROA e un timestamp - tehnic de audit (data ultimei modificari a randului), dar **neconfirmat aici** — ramane de verificat pe - schema vie. -- **Ambiguitate pe `CONT`**: vezi sectiunea 2 — niciun cod nu trateaza cazul "mai multe randuri active - pentru acelasi CONT"; presupunerea de unicitate e implicita, nu impusa de o constrangere vizibila. - -## 5. `NOM_ARTICOLE.CONT` pentru articole negestionabile si fallback pe 704 - -- **`NOM_ARTICOLE.CONT` nu e restrans la clasa 3** — reconfirmat (vezi deja `cont_venit_corespondente.md` - sectiunea 2): validarea `verific_cont` (`COMUN\programe\oproceduri_comune.prg:2389-2415`) verifica doar - ca contul exista in planul de conturi al anului, fara filtru de clasa; niciun `CHECK CONSTRAINT` in - DDL. Deci campul poate contine legitim un cont 6xx/7xx pentru un articol negestionabil, exact cum - presupune decizia 27. -- **Niciun fallback pe `704` existent in `pack_facturare`** — cautare `'704'` (literal SQL) in - `D:\ROA\ROAFACTURARE\docs\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` (17000+ linii, exportul curent al - pachetului): **zero rezultate**. Cautare in tot `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026` (toate scripturile - din 2026): **zero rezultate**. Fallback-ul pe 704 propus de Marius ar fi cod nou, nu o reteta deja - scrisa in `pack_facturare`. -- **Exista totusi un precedent independent pentru 704 ca "cont implicit de venit"**, in alt subsistem: - ``` - COMUN\clase\anaf_efactura.vc2:8370-8376 (clasa "frm_configurare_efactura") - *p: cconte && cont implicit articol client - *p: ccontp && cont implicit articol furnizor - ... - cconte = 704 - ccontp = 628 - ``` - E o proprietate cu valoare implicita `704` ("cont implicit articol client", deci un cont de venit - implicit pentru liniile generate la import eFactura) intr-un ecran de configurare a modulului ANAF - eFactura. **Nu am gasit cod care sa citeasca explicit `.cconte`** (cautare `\.cconte\b` in tot `COMUN`: - zero rezultate) — proprietatea e definita cu valoare implicita, dar consumatorul ei concret n-a fost - gasit in timpul alocat (posibil legat generic la un camp de configurare cu acelasi nume, tipar comun in - suita, dar neconfirmat aici). **Ce arata cu certitudine**: alegerea `704` ca implicit pentru "cont - articol client" nu e o inventie a acestei cereri — a mai fost aleasa o data, independent, de altcineva - din echipa (sau de Marius insusi, in alt moment), pentru un scop asemanator. Nu e insa o reteta de cod - reutilizabila direct in `pack_facturare` — e in alt pachet/clasa, alt flux (import, nu emitere). - -## 6. Punctul de injectie in `contabilizeaza_articol` - -Sursa: `D:\ROA\ROAFACTURARE\docs\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` (exportul curent al -pachetului, 17548+ linii). Numerotarea de mai jos e din **acest fisier**, verificata prin citire directa -(difera cu cateva linii fata de exportul mai vechi citat in rapoartele anterioare, care avea FACT-024 la -7301 in loc de 7311 — normal, fisierul a mai fost actualizat intre timp). - -**Structura exacta a blocului care ridica FACT-024** (`:7275-7302`, inceputul functiei): -``` -7275 BEGIN -7276 -7277 -- 05.07.2011 -7278 BEGIN -7279 SELECT COMPUS, ID_POL_ART -7280 INTO V_COMPUS, V_ID_POL_ART -7281 FROM VCRM_POLITICI_PRET_ART -7282 WHERE ID_ARTICOL = detalii_articol.id_articol -7283 AND ID_POL = detalii_articol.id_pol; -7284 EXCEPTION -7285 WHEN NO_DATA_FOUND THEN -7286 SELECT DENUMIRE -7287 INTO lcArticol -7288 FROM NOM_ARTICOLE -7289 where id_articol = detalii_articol.id_articol; -7290 -7291 SELECT NUME_LISTA_PRETURI -7292 INTO lcPolitica -7293 FROM CRM_POLITICI_PRETURI -7294 where id_pol = detalii_articol.id_pol; -7295 -7296 RAISE_APPLICATION_ERROR(-20000, -7297 'Articolul ' || detalii_articol.id_articol || '|' || -7298 lcArticol || -7299 ' nu este definit in politica de preturi ' || -7300 detalii_articol.id_pol || '|' || lcPolitica || -7301 '! (FACT-024)'); -7302 END; -``` - -**Acesta e locul unde s-ar decide "articolul nu are politica" — dar NU e suficient sa se schimbe doar -acest bloc.** Motivul, cu citate exacte: - -1. Dupa acest bloc, codul verifica `IF V_COMPUS = 1 THEN` (articol compus, `:7305`, ridica alta eroare, - FACT-016) `ELSE` (`:7391`) — ramura "articol simplu" care e cea relevanta pentru cazul cerut. -2. Ramura "articol simplu" **deschide un cursor nou**, `cursor_articol` (definit la `:7218-7271`), - filtrat exact pe aceeasi cheie care a dat `NO_DATA_FOUND` mai sus: - ``` - 7261 FROM CRM_POLITICI_PRET_ART A - ... - 7268 WHERE - 7269 -- A.STERS = 0 AND - 7270 A.ID_POL = detalii_articol.id_pol - 7271 AND A.ID_ARTICOL = detalii_articol.id_articol; - ``` -3. Tot corpul de lucru real (calculul `V_SCD`/`V_ASCD`/`V_SCC`/`V_ASCC`/`V_EXPLICATIE`, apelul - `pack_facturare.scrie_nota(...)` la `:7443-7467`, apelul `pack_facturare.descarca_gestiune(...)` la - `:7476-7498`, scrierea discountului la `:7501-7518`) e **in interiorul** - `WHILE cursor_articol%FOUND LOOP` (`:7396-7541`). **Daca articolul nu are politica de pret, acest - cursor nu returneaza niciun rand** (e exact aceeasi conditie care a dat `NO_DATA_FOUND` la pasul 1, - pe acelasi `(id_pol, id_articol)`), deci bucla ruleaza zero iteratii. - -**Concluzia tehnica**: daca s-ar modifica *doar* blocul `EXCEPTION WHEN NO_DATA_FOUND` (pasul 1) ca sa nu -mai ridice `RAISE_APPLICATION_ERROR` si sa lase `V_COMPUS := 0` implicit, executia ar trece la ramura -"articol simplu", ar deschide `cursor_articol`, acesta ar gasi tot zero randuri (aceeasi cauza), bucla nu -ar rula deloc, si functia ar reveni `RETURN V_INCASAT_CALCUL` cu valoarea initiala `0` -(declarata la `:7178`), **fara sa scrie nicio nota contabila si fara sa descarce gestiunea** — silentios, -fara nicio eroare. Asta e o regresie mai grava decat FACT-024: azi tranzactia se opreste vizibil; cu un -fallback naiv, factura s-ar emite fara inregistrare contabila si fara descarcare de stoc, nedetectabil -fara audit manual. - -**Ce ar cere de fapt fallback-ul, pentru a pastra comportamentul actual cand politica exista**: -- Blocul de la pasul 1 ar trebui sa **distinga** explicit cazul "politica lipseste" (continua cu - fallback) de cazul "articol compus fara politica" (probabil tot eroare, nu e in scopul cerut) — - posibil punand un flag local (`V_ARE_POLITICA := FALSE`) in loc de `RAISE_APPLICATION_ERROR`. -- Ar trebui adaugata o **ramura noua, in afara/inainte de bucla `cursor_articol`**, activa doar cand - `V_ARE_POLITICA = FALSE`, care sa: - - calculeze `V_SCD`/`V_ASCD` **reutilizand `CASE`-ul existent pe tip document** (`:7398-7423` — - acesta nu depinde de `cursor_articol`, poate fi extras/reutilizat identic); - - calculeze `V_SCC` din sursa noua (corespondente pentru articol gestionabil / `NOM_ARTICOLE.CONT` sau - `704` pentru negestionabil) — **doar acest pas e cel descris de decizia 27**; - - decida un `V_ASCC` (azi vine din `NVL(crs_rand_articol.ascc, GetAnaliticByGrupUtilizatori(...))` — - fara rand din cursor, ar trebui sa cada direct pe `GetAnaliticByGrupUtilizatori(...)`, posibil fara - modificare, pentru ca aceasta functie nu depinde de cursor); - - decida `V_EXPLICATIE`, `ID_VENCHELT`, `ID_SECTIE`, `CU_TVA`, `IN_VALUTA` — vezi sectiunea 7, aici e - gaura reala, nu doar cod de rescris. - - apeleze explicit `pack_facturare.scrie_nota(...)` si, conditionat, `descarca_gestiune(...)`, cu - parametrii calculati mai sus, **duplicand** (nu reutilizand) logica din interiorul buclei existente. - -Asta confirma exact avertismentul din decizia 27 insasi ("modificare in COMUN, cu impact peste toata -suita") — nu e un `IF` adaugat pe o linie, e o ramura noua de ~40-60 linii care trebuie sa reproduca o -parte din logica ramurii existente, cu surse diferite pentru fiecare camp. - -## 7. Ce se pierde fata de lantul complet `CRM_POLITICI_PRET_ART -> ... -> NOTE_CONTABILE` - -Aceasta e partea centrala a raspunsului: **fallback-ul pe cont de venit (corespondente + nomenclator + -704) acopera doar `SCC`, nu si restul campurilor pe care lantul de politica de pret le aduce azi din -`NOTE_CONTABILE` fara efort.** Campurile aduse azi de `D.EXPLICATIE, D.SCD, D.ASCD, D.SCC, D.ASCC, -D.CU_TVA, D.IN_VALUTA` (`cursor_articol`, `:7248-7253, 7259-7260`) si ce se intampla cu fiecare fara -politica: - -- **`SCD`/`ASCD` (contul debitor si analiticul lui)**: **nu se pierde** — `V_SCD` se calculeaza deja - independent de `NOTE_CONTABILE`, dintr-un `CASE` pe tipul de document (`:7398-7423`, ex. `461` pt aviz - catre debitori, `418` pt aviz simplu, `crs_rand_articol.scd` doar pt factura normala — dar acesta din - urma **tot vine din `NOTE_CONTABILE.SCD` prin cursor**, deci pt factura normala SCD s-ar pierde la fel - ca SCC). **Corectie necesara fata de formularea initiala a intrebarii**: nu doar SCC vine din - `NOTE_CONTABILE` pt factura normala — si SCD (contul de creante, tipic 411) vine de acolo. Decizia 27 - vorbeste doar de "cont de venit", nu de contul debitor — inseamna ca **si sursa lui `V_SCD` pentru - factura normala ar trebui rezolvata** (probabil un cont fix, gen 411/`4111`, dar nu e in scopul - deciziei 27 asa cum e formulata azi). -- **`CU_TVA`**: **se pierde, fara alta sursa in cod.** Controleaza daca pretul de pe linie e interpretat - cu sau fara TVA la scrierea sumei (`pack_facturare.scrie_nota(...)`, parametru `crs_rand_articol.cu_tva` - la `:7463`). Nu exista nicio coloana echivalenta in `CORESP_CONT_VENCHELT` sau `NOM_ARTICOLE`. Fara ea, - fallback-ul trebuie sa aleaga o valoare hardcodata sau sa o deriveze din alt camp deja disponibil pe - `detalii_articol` (ex. `pret_cu_tva`, vazut ca parametru separat la `:7447` — posibil suficient, dar - necesita verificare separata, nu facuta aici). -- **`IN_VALUTA`**: **se pierde, fara alta sursa.** Controleaza daca linia e tratata ca fiind in valuta la - nivel de nota contabila (`V_IN_VALUTA := crs_rand_articol.in_valuta;` la `:7470`, folosit apoi la - discount `:7507`). La fel ca `CU_TVA`, nicio coloana echivalenta in corespondente/nomenclator. -- **`EXPLICATIE`**: se pierde ca sablon configurat, dar are deja fallback partial in cod — `V_EXPLICATIE` - e calculat cu un `CASE` pe tipul de operatie (`:7430-7439`, ex. concateneaza `pack_facturare.cdescriere` - pt facturi tip 7) folosind `crs_rand_articol.explicatie` ca baza; fara cursor, baza ar fi goala/NULL, - dar structura `CASE` insasi nu depinde de cursor — impact moderat, nu blocant. -- **`ID_VENCHELT`**: are deja un fallback la nivel de sesiune — - `NVL(pack_facturare.nid_venchelt, NVL(A.ID_VENCHELT, C.ID_VENCHELT))` (`:7244-7245`) — daca - `pack_facturare.nid_venchelt` e setat global (parametru de sesiune), fallback-ul functioneaza deja fara - politica; daca nu e setat, ramane `NULL` (fara eroare in cod, dar posibil relevant pentru rapoarte care - folosesc aceasta dimensiune, neverificat). -- **`ID_SECTIE`**: acelasi tipar — `NVL(pack_facturare.nid_sectie_stoc, C.ID_SECTIE)` (`:7246`), cu - fallback pe variabila de sesiune. -- **`ID_SET`** (identificatorul setului de nota folosit la scriere): **nu se pierde** — parametrul trimis - efectiv la `scrie_nota` e `pack_facturare.nid_set` (variabila de sesiune, `:7462`), nu - `crs_rand_articol.id_set` din cursor (acel camp e citit la `:7247` dar nu e folosit la apelul de - scriere in ramura articol simplu — verificat direct in cod). Deci acest camp special nu depinde de - politica de pret. - -**Rezumat sectiunea 7**: din cele 7 campuri aduse de lantul politicii de pret, 2 (`ID_VENCHELT`, -`ID_SECTIE`) au deja fallback pe variabile de sesiune si ar functiona fara politica; `EXPLICATIE` are -impact moderat (structura de calcul ramane, doar baza se pierde); `ID_SET` nu depinde deloc de politica -in ramura articol simplu; dar **`CU_TVA` si `IN_VALUTA` nu au nicio sursa alternativa** — sunt gaura reala -pe care simpla adaugare a unui cont de venit din corespondente/nomenclator nu o umple. **Confirmarea -finala a intrebarii din cerere**: da, fallback-ul pe cont de venit e insuficient pentru o nota contabila -completa — nu pentru ca "SCC" ar fi gresit, ci pentru ca doua campuri de control (TVA, valuta) raman fara -sursa. - -## 8. Varianta VFP, fara sa se atinga `pack_facturare` (plan A, cerut de Marius) - -Ideea de verificat: daca `contabilizeaza_articol` deriva `SCC` din `id_pol`, VFP ar putea sa aleaga -singur, inainte de a trimite articolul, un `id_pol` a carui nota are deja `SCC` = contul de venit -calculat dupa regula lui Marius — pachetul ar rula neschimbat. Raspunsul, punct cu punct: - -### a) `id_pol` vine din VFP? Da — e trimis explicit, pe fiecare linie - -Semnatura efectiva a procedurii apelate pe fluxul principal (verificat direct in export, nu presupus): -``` -D:\ROA\ROAFACTURARE\docs\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:4989-5015 -PROCEDURE adauga_articol_factura(V_ID_TEMP IN NUMBER, - V_ID_ARTICOL IN NUMBER, - V_SERIE IN VARCHAR2, - V_EXPLICATIE IN VARCHAR2, - V_ID_POL IN NUMBER, - V_ID_GESTIUNE IN NUMBER, - ... - V_CONT IN VARCHAR2, - ... -``` -`V_ID_POL` e parametru de intrare explicit — Oracle nu il deriva din client/contract/tip document, il -primeste ca atare de la VFP. **Niciun alt parametru din aceasta lista nu ofera un canal pentru -SCC/id_set** (raspuns si la punctul d — vezi mai jos). - -**De unde il ia azi VFP**: din controlul de cautare obligatoriu al formularului `frm_articol_factura` -(clasa `ofacturare.vc2`): -``` -COMUN\clase\ofacturare.vc2:6822-6825 -ADD OBJECT 'Ct_clb_politici_preturi' AS ct_clb_cautare WITH ; - cconditie = ISNULL(poDate.id_pol) or EMPTY(poDate.id_pol), ; - cprocedura = thisform.do_cauta_politica, ; - cvar_afisata = poDate.nume_politica, ... -``` -``` -COMUN\clase\ofacturare.vc2:7167-7182 -PROCEDURE do_cauta_politica - Local loCauta - loCauta = caut_politici_curente_util() - If !Isnull(loCauta) And !Empty(Nvl(loCauta.id_pol,0)) - poDate.id_pol = loCauta.id_pol - poDate.nume_politica = loCauta.nume - ... -``` -`cconditie = ISNULL(poDate.id_pol) or EMPTY(poDate.id_pol)` e exact starea "articol fara politica" din -cererea #13 — controlul UI cere explicit utilizatorului sa caute o politica cand campul e gol. VFP -controleaza integral valoarea: azi vine din alegerea utilizatorului, dar nimic tehnic nu impiedica sa fie -setata programatic, fara interactiune, la un `id_pol` calculat. - -### b) Drumul invers e interogabil? Da — e simetricul exact al cursorului deja documentat la sectiunea 6 - -Cursorul din `contabilizeaza_articol` (`ff_...:7261-7267`) merge -`CRM_POLITICI_PRET_ART -> CRM_POLITICI_PRETURI -> CRM_NOTE_VANZARI -> NOTE_CONTABILE` prin -`A.ID_POL = B.ID_POL`, `B.ID_NOTA = C.ID_NOTA`, `C.ID_SET = D.ID_SET`. Interogarea inversa, plecand de la -un `SCC` dorit catre `id_pol`-urile candidate, foloseste aceleasi coloane de legatura, doar fara filtrul -pe articol: -```sql -SELECT DISTINCT PP.ID_POL - FROM CRM_POLITICI_PRETURI PP - JOIN CRM_NOTE_VANZARI NV ON NV.ID_NOTA = PP.ID_NOTA - JOIN NOTE_CONTABILE NC ON NC.ID_SET = NV.ID_SET - WHERE NC.SCC = :cont_venit_calculat; -- '707'/'711'/'702'/'703'/'7015'/'7018' (gestionabil) sau valoarea din NOM_ARTICOLE.CONT/'704' (negestionabil) -``` -E o interogare noua (nu am gasit-o deja scrisa nicaieri in cod), dar foloseste exclusiv coloane si -relatii deja confirmate ca existente si corecte (sectiunea 1 din `cont_venit_corespondente.md` + citirea -directa a `cursor_articol` la sectiunea 6 a acestui raport). **Neverificat pe date reale**: cate randuri -returneaza pentru fiecare `SCC` candidat — zero (nicio politica configurata cu acel SCC azi), unul -(cazul ideal) sau mai multe (ambiguitate: care `id_pol` se alege?). Interogarea de rulat pe baza vie e -chiar cea de mai sus, cu `:cont_venit_calculat` inlocuit pe rand cu `707`, `711`, `702`, `703`, `7015`, -`7018`, `704`. - -### c) Se poate insera din VFP un rand in `CRM_POLITICI_PRET_ART`? Da — cod existent, functional, deja rulat in productie - -Doua mecanisme distincte, ambele reale: - -**c1. RPC direct, apelabil din VFP azi** — `pack_preturi.adauga_politica_pret_art`, apelat din -`ofacturare.vc2` in procedura de modificare a listei de preturi la NIR (`ID_SET = 231`): -``` -COMUN\clase\ofacturare.vc2:15551-15587 -*!* verific daca exista articolul in politica de preturi -lcSql = [Select id_pol_art, id_venchelt, proc_tvav from crm_politici_pret_art where id_pol = ] + Alltrim(Str(tnIdPol)) + [ and id_articol = ] + Alltrim(Str(tnIdArticol)) -lnSucces = goExecutor.oExecute(m.lcSql, "cPoliticiPretArt") -... -If Nvl(loPoliticaPretArt.id_pol_art,0) = 0 - lcSql = [begin pack_preturi.adauga_politica_pret_art(] + ; - ALLTRIM(Str(tnIdPol)) + [,] + ; && id_pol - Alltrim(Str(tnIdArticol)) + [,] + ; && id_articol - Alltrim(Str(tnPretLista,20,4)) + [,] + ; && pret - Alltrim(Str(tnPretftvaLista,20,4)) + [,] + ; && pretftva - Alltrim(Str(tnPretvtvaLista ,20,4)) + [,] + ; && pretctva - [NULL, ] + ; && id_valuta - Alltrim(Str(tnProcTvav,20,4)) + [,] + ; && proc_tvav - [NULL, ] + ; && procent - [NULL, ] + ; && id_venchelt - Alltrim(Str(gnIdUtil)) + ; && id_util - [); end;] -``` -Verifica intai daca exista deja un rand (`id_pol`, `id_articol`), si daca nu, insereaza unul nou prin RPC -existent — **exact tiparul necesar pentru punctul (c)**: "adauga articolul X in politica de pret Y", -apelabil din VFP fara nicio modificare de pachet (`pack_preturi` nu e `pack_facturare`). - -**c2. Auto-populare in masa, deja existenta in `pack_facturare` insusi, dar fara parametru nou** — o -procedura care nu ar necesita nicio modificare de cod pentru ca deja exista si e apelabila ca atare: -``` -D:\ROA\ROAFACTURARE\docs\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:2093-2120 -PROCEDURE completare_politica_stoc IS -BEGIN - IF pack_facturare.nid_politica_stoc IS NOT NULL THEN - MERGE INTO CRM_POLITICI_PRET_ART A - USING (SELECT ID_ARTICOL FROM NOM_ARTICOLE - WHERE STERS = 0 AND INACTIV = 0 AND IN_STOC = 1) B - ON (A.ID_POL = pack_facturare.nid_politica_stoc AND A.ID_ARTICOL = B.ID_ARTICOL) - WHEN NOT MATCHED THEN - INSERT (ID_POL, ID_ARTICOL, ID_VALUTA) - VALUES (pack_facturare.nid_politica_stoc, B.ID_ARTICOL, pack_facturare.nid_moneda_nationala); - ... - END IF; -END completare_politica_stoc; -``` -Aceasta e o functie **existenta, apelabila fara nicio modificare**, care garanteaza deja un rand in -`CRM_POLITICI_PRET_ART` pentru *orice* articol cu `IN_STOC = 1` sub o singura politica "de stoc" -(`pack_facturare.nid_politica_stoc`). Acest id_pol e la randul lui o **optiune de configurare controlata -din VFP**: -``` -COMUN\clase\ofacturare.vc2:21733-21734 (Init-ul ecranului de optiuni facturare) -actualizeaza_politica_pret(23,@gnId_pol_pret_tr,@lcPolPretTr) -actualizeaza_politica_pret(1,@gnId_pol_pret_stoc,@lcPolPretStoc) -... -COMUN\clase\ofacturare.vc2:21753 -This.nidpolpretstoc = Nvl(gnId_pol_pret_stoc,0) -``` -`gnId_pol_pret_stoc` e o optiune globala (`ID_POL_PRET_STOC`, salvata/citita prin -`actualizeaza_politica_pret`), care alimenteaza `pack_facturare.nid_politica_stoc` la initializarea -sesiunii Oracle. **Avertisment**: aceasta politica pare deja folosita azi pentru un scop specific -(vanzare/evaluare la pret de stoc — `nin_valuta`, `nTipVanzareRetail` sunt tratate diferentiat pentru ea -la `ff_...:7223-7260`), cu o singura nota/SCC configurata pentru toate articolele `IN_STOC=1`. Nu e -direct reutilizabila pentru reteta pe 6 conturi diferite (707/711/702/703/7015/7018) ceruta de decizia -27 — dar **arata modelul exact** ("o politica tehnica, configurata o singura data, populata automat"), -model care se poate replica de cate ori e nevoie (o politica tehnica per `SCC` distinct), fara sa se -toate cod in `pack_facturare`. - -### d) Alte cai de bypass la nivel de parametru — cautate explicit, nu gasite - -Semnatura completa a `adauga_articol_factura` (sectiunea a) si a variantelor `_deviz`/`_stoc` (deja -citate in `cont_venit_articol_fara_politica.md`) **nu au niciun parametru pentru cont de venit sau -`id_set` direct** — singurul levier disponibil la nivelul acestui apel e `V_ID_POL`. Nu exista un -parametru `V_SCC`/`V_ID_SET`/`V_COD_VENIT` pe nicio varianta citita. Concluzie: **`id_pol` e singura -cale de influenta din VFP asupra contului de venit**, exact premisa din care a pornit intrebarea. - -### e) Verdict onest - -**Cale VFP posibila, dar nu "zero cod" si nu fara pasi de configurare o singura data**: - -1. VFP calculeaza `SCC`-ul dorit exact ca in decizia 27 (interogare simpla, deja tipul de cod scris in - `update_corespondente_cvc` — sectiunea 1 a acestui raport): `CORESP_CONT_VENCHELT.CONT_VENIT` pe - contul de gestiune pentru articol gestionabil, `NOM_ARTICOLE.CONT` (daca e 6xx/7xx) sau `'704'` pentru - negestionabil. -2. VFP cauta (interogarea de la punctul b) o `id_pol` existenta a carei nota are deja acel `SCC`. **Daca - gaseste una** (plauzibil pentru conturile comune 707/711/702/703, care probabil se folosesc deja in - politici de pret reale ale firmei) — trece la pasul 3 direct, fara nicio configurare noua. -3. VFP se asigura (RPC `pack_preturi.adauga_politica_pret_art`, punctul c1, deja existent) ca exista un - rand `CRM_POLITICI_PRET_ART` pentru (acel `id_pol`, articolul curent) — insereaza unul cu pretul - tastat manual de utilizator (`tnPretLista` = pretul liniei) daca nu exista. -4. VFP trimite acel `id_pol` in `adauga_articol_factura`, exact ca pe fluxul actual (cu politica). - `contabilizeaza_articol` **ruleaza neschimbat** — si, bonus fata de varianta plan B (sectiunea 7), - **campurile `CU_TVA`/`IN_VALUTA`/`EXPLICATIE`/`ASCD`/`ASCC` vin corect din nota reala configurata**, - nu raman goale ca in fallback-ul partial din pachet. - -**Ce nu e gratuit**: -- **Pasul 2 poate rata** (nicio politica existenta cu acel `SCC`) — pentru un `SCC` fara precedent (de - ex. `704`, pentru care nu am gasit nicio nota configurata azi, sectiunea 5), ar trebui creat manual, - o singura data, un lant nou `CRM_POLITICI_PRETURI` + `CRM_NOTE_VANZARI` + `NOTE_CONTABILE` (contabilul - configureaza nota din `frm_config_note_contabile[2007]`, deja documentat in - `cont_venit_corespondente.md` sectiunea 1) — asta e configurare (ecran existent), nu cod nou, dar e un - pas manual, nu automat. -- **Efect lateral real**: o "politica tehnica" (creata special pentru acest mecanism) ar aparea in - ecranul de cautare politici al utilizatorului (`caut_politici_curente_util()`, aceeasi functie folosita - la cautarea normala) — trebuie fie exclusa explicit din acel cursor de cautare (filtru pe un flag/prefix - dedicat), fie acceptata ca vizibila, cu riscul ca un utilizator sa o aleaga din greseala pentru o - factura normala. Nu am verificat daca `caut_politici_curente_util()` are deja un filtru care ar exclude - automat politici fara preturi reale/marcate altfel. -- **Ambiguitatea de la pasul 2** (mai multe `id_pol` cu acelasi `SCC`) ar cere o regula de departajare - (cea mai recenta? prima gasita? o marcare explicita "politica implicita pentru SCC X"?) — neverificat - pe date reale, vezi sectiunea "Ramas de verificat". -- Aceasta varianta **schimba `id_pol`-ul scris pe linie** (azi `NULL`/gol pentru articol fara politica, - ar deveni id-ul politicii tehnice) — orice raport/ecran care azi presupune "`id_pol` gol = articol fara - politica" (de ex. eventuale filtre in rapoarte de vanzari) ar vedea un `id_pol` populat; nu am gasit - cod care sa faca aceasta presupunere explicit, dar nici n-am cautat-o exhaustiv (in afara bugetului). - -**Concluzie e)**: nu e nevoie de "niciuna dintre cai" pentru un "nu" — exista o cale VFP verificabila si -construita din piese deja functionale in productie (RPC de inserare in politica de pret, plus o -interogare de cautare inversa noua dar directa). E **fezabila**, probabil **mai completa** decat plan B -(rezolva si `CU_TVA`/`IN_VALUTA`, nu doar `SCC`), dar cere: (i) o interogare noua de cautare `id_pol` pe -`SCC`, (ii) reutilizarea unui RPC existent pentru inserarea articolului in politica, (iii) potential -configurare manuala o singura data (nota noua) pentru `SCC`-urile fara precedent, (iv) o decizie explicita -despre cum se exclude/trateaza politica tehnica in ecranele de cautare vizibile utilizatorului. - -## Ramas de verificat pe baza de date vie - -- **Structura completa DDL a `CORESP_CONT_VENCHELT`** (`DESC coresp_cont_venchelt` sau echivalent): - confirmarea coloanelor `ID_CCV` (probabil PK surogat) si `DATAORAS` (probabil timestamp tehnic), - tipurile exacte, nullability, si daca exista o constrangere `UNIQUE`/index pe `CONT` (relevant pentru - ambiguitatea discutata in sectiunea 2 si 4) — DDL-ul nu e in arhiva `SCRIPTURI_CLAR` (tabel pre-2009). - Interogare de verificat: `SELECT column_name, data_type, nullable FROM user_tab_columns WHERE - table_name = 'CORESP_CONT_VENCHELT' ORDER BY column_id;` si - `SELECT index_name, uniqueness FROM user_indexes WHERE table_name = 'CORESP_CONT_VENCHELT';`. -- **Continutul complet al `CONT_VENIT`** — migrarea din 2023 acopera 20 de conturi de gestiune; ramane de - verificat daca acopera **toate** conturile de gestiune folosite azi in `NOM_ARTICOLE.CONT`/`STOC.CONT` - in productie (altfel fallback-ul ar produce `CONT_VENIT = NULL` pentru conturi de gestiune - neacoperite). Interogare: `SELECT DISTINCT cont FROM (SELECT cont FROM nom_articole UNION SELECT cont - FROM stoc) WHERE cont NOT IN (SELECT cont FROM coresp_cont_venchelt WHERE sters = 0);`. -- **Daca exista mai mult de un rand activ (`STERS = 0`) pentru acelasi `CONT`** — cod critic pentru - join-uri fara dezambiguizare. Interogare: `SELECT cont, COUNT(*) FROM coresp_cont_venchelt WHERE - sters = 0 GROUP BY cont HAVING COUNT(*) > 1;`. -- **Valorile reale din `NOM_ARTICOLE.CONT` pentru articole negestionabile** — cate au deja un cont - 6xx/7xx explicit vs. cate au un cont 3xx "gresit" (articol marcat negestionabil dar cu cont de gestiune - ramas din import) vs. cate au `NULL`/gol, ceea ce ar decide cat de des s-ar folosi de fapt fallback-ul - pe `704`. Interogare: `SELECT SUBSTR(cont,1,1) AS clasa, COUNT(*) FROM nom_articole WHERE gestionabil = - 0 GROUP BY SUBSTR(cont,1,1);` (numele exact al coloanei `gestionabil` de verificat pe schema). - Verificat si numele exact al coloanei `NOM_ARTICOLE.CONT` — vezi si `cont_venit_articol_fara_politica.md`. -- **Cine citeste efectiv proprietatea `cconte` din `frm_configurare_efactura`** — nu am gasit consumatorul - in codul static cercetat (`\.cconte\b`, zero rezultate in `COMUN`); posibil legata generic la o - configuratie pe nume de camp, tipar comun in suita, dar neconfirmat. -- **Comportamentul `scrie_nota`/`ACT_TEMP` la `SCD`/`SCC` NULL sau la `CU_TVA`/`IN_VALUTA` NULL** — codul - PL/SQL nu are validare (confirmat in `nota_contabila_fara_politica.md`, sectiunea 2), dar constrangerile - reale de tabel (`ACT`/`ACT_TEMP`, `NOT NULL`?) nu au fost verificate — ar decide daca o implementare pe - jumatate facuta a fallback-ului ar da eroare Oracle explicita (`ORA-01400`) sau ar trece silentios. diff --git a/docs/cercetare/custodie_48_49_stergere_reemitere.md b/docs/cercetare/custodie_48_49_stergere_reemitere.md deleted file mode 100644 index 1c10088..0000000 --- a/docs/cercetare/custodie_48_49_stergere_reemitere.md +++ /dev/null @@ -1,217 +0,0 @@ -# Custodie (tipuri 48/49): stergere + reemitere intoarce curat descarcarea din custodie? - -Cercetare read-only. Raspunde la intrebarea daca editarea prin regenerare (STERS=1 pe documentul -vechi + document nou, in aceeasi tranzactie, mecanismul proiectat la S9 din -`docs\plan_13_unificare_formular_facturare.md:3576-3660`) pentru facturile de marfa in custodie -(tipurile 48/49) lasa stocul de custodie neschimbat. - -Sursa principala citata: `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` -(prescurtat `EXPORT` mai jos, `EXPORT:linie`). Verificat direct in aceasta runda: numerotarea din -export **coincide** cu cea folosita in materialele anterioare citate de team-lead (`scrie_fact_aviz_custodie` -la `:10089-10325` pentru corpul functiei si `:7521-7536` pentru locul de unde e apelata; -`sterge_factura` la `:5432-5607`) — nu s-a gasit niciun offset. - -**Rezultat central, care rescrie premiza cererii**: `scrie_fact_aviz_custodie` **nu se apeleaza -niciodata pentru documente de tip 48/49**. Se apeleaza exclusiv cand `pack_facturare.ntip = 4` -("factura din avize" — alt tip de document, complet diferit). Tipurile 48/49 sunt o ramura separata, -care **nu atinge stocul deloc** la emitere (nu doar "il descarca prin alta cale"). Detalii mai jos. - -## 1. Ce sunt exact tipurile 48 si 49 - -Confirmat pe cod, nu doar pe indiciul din materiale: - -- **Denumirile** ("custodie cu descarcare K" / "custodie fara descarcare K") vin din - `COMUN\docs\tipuri_documente_facturare.md`, deja verificate pe cod de cercetarea anterioara - `docs\cercetare\s5_acoperire_tipuri.md` (tabelul de la liniile 102-103 de acolo). -- **Reachable din meniu**: `Meniuri\politica.mn2`, submeniul `Marfaincus` — tip 48 la linia 29, - tip 49 la linia 26 (citat deja corect in `s5_acoperire_tipuri.md:22,102-103,146-147`). -- **Amandoua sunt facturi de sine statatoare** (categoria "Facturi", nu "Avize" — spre deosebire de - 42/47, care sunt avize catre custodie), sursa **VFP** de articole fiind aceeasi pentru ambele: - `Case Inlist(tnTip, 48, 49) -> pack_facturare.cursor_articole_k(...)` - (`COMUN\programe\ofacturare.prg:271-272` pe formularul standard, identic la `:750-752` pe prototip). -- **`cursor_articole_k`** (`EXPORT:3595-3701`, definitia activa; exista si o declaratie in spec la - `:382`) e o interogare de **lista de preturi**, nu o interogare de stoc/aviz: alege articolele - dintr-o **politica de pret dedicata** (`A.ID_POL = to_number(pack_sesiune.getoptiunefirma('IDPOLPRETFACTK'))`, - `EXPORT:3681-3682`) si **filtreaza explicit doar articole negestionabile**: - `WHERE C.IN_STOC = 0 AND C.IN_CRM = 1 AND C.STERS = 0 AND C.INACTIV = 0` (`EXPORT:3695-3698`). - Cu alte cuvinte: **48 si 49 sunt aceeasi sursa de articole** ("K" = politica de pret speciala - pentru custodie, optiunea de firma `IDPOLPRETFACTK`), restransa explicit la articole - **care nu sunt gestionate in stoc** (`IN_STOC=0` in `NOM_ARTICOLE`). -- **Nu s-a gasit in aceasta runda ce anume distinge 48 de 49** ("cu"/"fara descarcare K") — pe tot - codul examinat (`adauga_articol_factura`, `contabilizeaza_articol`, `cursor_articole_k`, - `sterge_factura`), cele doua tipuri sunt tratate **identic**, fara nicio ramura care sa le separe. - Diferenta pare sa fie doar de conventie de utilizare (business), nu de cod — semnalat la sectiunea - "Neacoperit". - -## 2. Ce scrie emiterea pe un document 48/49 - -**Nu se atinge stocul deloc, prin design, nu prin accident.** Lant de dovezi: - -1. `adauga_articol_factura` (`EXPORT:4989-5220`) nu are ramura proprie pentru `ntip IN (48,49)` — - cade in `ELSE` (`:5187-5203`), care preia `V_IN_STOC` direct din `V_IN_STOC_TEMP`, valoarea - trimisa de VFP din grid — la randul ei populata din `cursor_articole_k.GESTIONABIL` - (`C.IN_STOC AS GESTIONABIL`, `EXPORT:3634`), deci **intotdeauna 0** pentru articolele oferite pe - aceste doua tipuri. -2. `contabilizeaza_articol` (`EXPORT:7173-7547`): tip 48/49 intra in bucketul "factura normala" - pentru determinarea `SCD`/`ASCD` (`WHEN pack_facturare.ntip <= 20 or ntip IN (... 48, 49, 51, 52)`, - `EXPORT:7400-7412`) — scrie o nota contabila normala prin `scrie_nota` (`:7443-7467`), ca orice - factura obisnuita, folosind conturile din politica de pret K. -3. **Apelul catre `descarca_gestiune` e conditionat** de `detalii_articol.in_stoc = 1` - (`EXPORT:7472-7475`, ramura `IF pack_facturare.ntip <> 4`, care e adevarata pentru 48/49). Cum - `in_stoc` vine intotdeauna 0 pentru articolele acestor doua tipuri (pasul 1), **conditia nu se - indeplineste niciodata** — `descarca_gestiune` nu se executa. -4. **Confirmare independenta, in interiorul lui `descarca_gestiune` insusi**: chiar daca cineva ar - reusi sa forteze apelul, functia are propria garda de iesire timpurie: - ``` - EXPORT:7789-7797 - -- NU SE DESCARCA GESTIUNEA PENTRU ARTICOLELE NEGESTIONABILE - -- ASTFEL INCAT SA SE POATA FACE OPERATII GEN AVIZ DIN CUSTODIE INCLUSIV CU ARTICOLE NEGESTIONABILE - SELECT MAX(IN_STOC) INTO lnInStoc FROM NOM_ARTICOLE WHERE ID_ARTICOL = V_ID_ARTICOL; - if lnInStoc = 0 then GOTO SFARSIT; end if; - ``` - Comentariul insusi confirma intentia de design: articolele negestionabile (exact categoria din - care se alimenteaza 48/49) sunt **explicit excluse** din descarcarea de gestiune, tocmai pentru - ca fluxul de custodie sa poata folosi articole care nu au stoc urmarit. -5. **`scrie_fact_aviz_custodie` nu se apeleaza pentru 48/49.** Singurul apel din tot pachetul - (`EXPORT:7520-7537`) e in interiorul aceleiasi functii `contabilizeaza_articol`, pe ramura - `ELSE` a testului `IF pack_facturare.ntip <> 4` (`:7472`) — adica **doar cand `ntip = 4`** - ("factura din avize"). Pentru 48/49, `ntip` e 48 sau 49, niciodata 4, deci acest cod **nu se - executa niciodata** pe aceasta ramura. Functia `scrie_fact_aviz_custodie` insasi - (`EXPORT:10089-10325`) nu scrie in `STOC`/`RUL` — cauta un rand deja existent in `RUL` (cu - `ID_TIP_RULAJ = 0`, `EXPORT:10175`) pentru a calcula valori de cost, si scrie doar **note - contabile** (`scrie_nota`, conturi `371/357/607/378/4428` — conturi de marfuri in custodie / - cheltuieli, `EXPORT:10229-10324`) — e o functie de **conversie contabila**, nu de miscare de - stoc. - -**Concluzie sectiune**: premiza din cerere ("pe ramura custodiei se cheama `scrie_fact_aviz_custodie` -in loc de `descarca_gestiune`") descrie corect fluxul pentru **tip 4** (factura emisa dintr-un aviz, -posibil un aviz catre custodie 42/47 emis anterior), **nu** pentru tipurile 48/49 cerute explicit. -Pentru 48/49, niciuna din cele doua proceduri nu scrie nimic in stoc — documentul e din start -"fara efect de stoc", pentru ca sursa lui de articole (`cursor_articole_k`) e restransa la articole -negestionabile. - -## 3. Ce face stergerea (`sterge_factura`) pe un document 48/49 - -`sterge_factura` (`EXPORT:5432-5607`) nu are nicio ramura proprie pentru `V_TIP IN (48,49)`. Constantele -speciale verificate (`EXPORT:78-86`): `nTipVanzareRetail=43`, `nTipFacturaHotel=44`, -`nTipFacturaRestaurant=45`, `nTipNotaPlata=46`, `nTipFacturaACN=51` — 48 si 49 nu sunt printre ele. - -`CASE`-ul principal (`EXPORT:5501-5551`) evalueaza `V_TIP` astfel pentru 48/49: -- `V_TIP = 24`? Nu. -- `V_TIP > 20 AND V_TIP NOT IN (44,45,46)`? **Da** — 48 si 49 cad in aceasta ramura generica - ("alte tipuri de avize", `EXPORT:5512-5523`), care face: - ```sql - UPDATE VANZARI_CANTITATI SET STERS = 1 - WHERE ID_VANZARE_DET_AVIZ IN - (SELECT ID_VANZARE_DET FROM VANZARI_DETALII WHERE ID_VANZARE = V_ID_VANZARE AND STERS = 0); - ``` - `VANZARI_CANTITATI` e tabelul de bookkeeping "cantitate ramasa din comanda/aviz" (Rol A, documentat - in `docs\cercetare\s4_punct2_registru_cantitate_ramasa.md`, citat si in `s5_acoperire_tipuri.md` - coloana "Rol"). **48/49 nu au niciodata acest rol** (`s5_acoperire_tipuri.md:103-104`: coloana Rol - = "—" pentru ambele) — articolele lor vin din `cursor_articole_k` (lista de preturi K), nu din - `cursor_comanda`. Deci acest `UPDATE` **actioneaza pe zero randuri** pentru documente 48/49 (nu - exista randuri `VANZARI_CANTITATI` legate de ele) — e un no-op inofensiv, nu o eroare, dar nici o - actiune custodie-specifica. -- Restul ramurilor CASE (`V_TIP=4`, `V_TIP=nTipFacturaRestaurant`) nu se aplica. - -Restul procedurii e comun tuturor tipurilor (nimic specific 48/49): -- `UPDATE VANZARI_DETALII SET STERS=1 ... WHERE ID_VANZARE = V_ID_VANZARE` (`:5560-5564`) — - marcheaza liniile facturii ca sterse. -- `IF V_TIP IN (2,6,52)` pentru `CTR_RATE_FACTURI` (`:5566-5580`) — nu se aplica (48/49 nu sunt in - lista). -- `UPDATE VANZARI_CORESP SET STERS=1 ...` (`:5582-5585`) — sterge corespondentele - factura<->aviz/comanda, generic. -- CASE-ul final pe pachete externe (restaurant/hotel/ACN, `:5587-5605`) — nu se aplica pentru 48/49, - cad in `ELSE NULL`. - -**Nu exista, nicaieri in `sterge_factura`, vreun apel care sa reverseze explicit efectul lui -`descarca_gestiune`** (nu s-a gasit `incarca_gestiune` sau echivalent, pentru niciun tip de document, -nu doar 48/49 — cautare `grep -n "UPDATE STOC"` in tot pachetul, zero rezultate; singurele scrieri -gasite in zona lui `descarca_gestiune` sunt in `RUL_TEMP`, `EXPORT:9464,10006`, tabel tranzitoriu care -se descarca in `RUL` mai departe in flux, nu direct in acest apel). Mecanismul prin care stocul -"revine" la stergerea unei facturi normale (`ntip<=20`) **nu a fost identificat in aceasta runda** — -posibil printr-un filtru `WHERE VANZARI_DETALII.STERS=0` in interogarile de stoc disponibil, posibil -prin alt pachet (`PACK_STOC`?) sau printr-un trigger, in afara `PACK_FACTURARE` si a perimetrului -citit. Marcat explicit la "Neacoperit" — **e o intrebare generala a sistemului, nu specifica -custodiei**, pentru ca `sterge_factura` trateaza identic (fara reversare explicita) orice tip de -document. - -## 4. Garzi existente la stergere — prind si cazul custodiei? - -Cele trei garzi de la inceputul lui `sterge_factura`, verificate pe liniile citate de team-lead -(coincid exact, fara offset): - -- `EXPORT:5452-5462` — blocheaza daca exista **facturi de retur** (`VANZARI_CORESP.TIP=3`) pe - documentul curent. -- `EXPORT:5466-5476` — blocheaza daca exista **facturi/avize de retur** (`TIP IN (1,2)`) pe un - aviz curent. -- `EXPORT:5480-5494` — blocheaza daca exista **avize de retur** pe avizele care au generat o - factura din aviz curenta. - -**Niciuna nu mentioneaza custodie sau `ntip IN (48,49)`** — toate trei filtreaza exclusiv pe -`VANZARI_CORESP.TIP IN (1,2,3)` (relatii factura<->retur / aviz<->retur), independent de tipul -documentului curent (`V_ID_VANZARE`). Pentru un document 48/49 fara retur emis pe el, **niciuna nu -se declanseaza** — stergerea trece liber, exact ca pentru o factura normala fara retur. - -## 5. Verdict - -**Regenerarea (stergere + reemitere) e SIGURA pentru documentele 48/49 in privinta stocului de -custodie — dar dintr-un motiv diferit de cel presupus in cerere.** Nu pentru ca stergerea "intoarce -curat" o descarcare de custodie — ci pentru ca **emiterea unui document 48/49 nu descarca nimic din -stoc/custodie in primul rand** (sectiunea 2: sursa de articole e restransa structural la articole -negestionabile, `IN_STOC=0`, iar `descarca_gestiune` are doua garzi independente care o opresc pentru -astfel de articole). Stergerea (sectiunea 3) nu are nimic custodie-specific de reversat, si nici nu -are nevoie sa aiba, pentru ca nimic custodie-specific n-a fost scris la emitere. **`ntip=4` (factura -din avize) e ramura care foloseste efectiv `scrie_fact_aviz_custodie`, si e un tip de document -diferit de 48/49** — premiza din cerere ("pe ramura custodiei se cheama `scrie_fact_aviz_custodie` -in loc de `descarca_gestiune`") descrie corect tip 4, nu 48/49. - -**Conditie sub care verdictul s-ar putea rasturna, ne-exclusa 100% in aceasta runda**: siguranta de -mai sus depinde de invariantul "un document 48/49 contine numai articole cu `IN_STOC=0`". Daca -articolele adaugate pe un document 48/49 ar veni vreodata **printr-o alta cale** decat -`cursor_articole_k` (o cautare libera de articole, nerestransa la politica K), un articol gestionabil -(`IN_STOC=1`) ar trece garda de la `adauga_articol_factura`/`contabilizeaza_articol` (sectiunea 2, -pasul 1: `V_IN_STOC_TEMP` ar deveni 1) si **`descarca_gestiune` s-ar executa normal** — caz in care -`sterge_factura`, care nu reverseaza explicit stocul pentru niciun tip (sectiunea 3), ar lasa un -decalaj real. **Nu s-a gasit in aceasta runda** o cale VFP care sa permita asta pentru 48/49 (grid-ul -de articole al acestor doua tipuri e populat o singura data, la deschiderea formularului, direct din -`cursor_articole_k` — `COMUN\programe\ofacturare.prg:271-272`; nu exista o cautare separata de -articole vazuta in codul citit), dar nu s-a verificat exhaustiv toate punctele de intrare posibile in -`VANZARI_DETALII_TEMP` pentru aceste doua tipuri. - -## Verificat direct / Dedus / Neacoperit - -**Verificat direct pe cod (fisier:linie citat in sectiunile 1-4):** -- Tip 48/49 = facturi (nu avize), sursa unica de articole `cursor_articole_k`, restransa la - `IN_STOC=0`. -- `scrie_fact_aviz_custodie` se apeleaza exclusiv pentru `ntip=4`, niciodata pentru 48/49. -- `descarca_gestiune` are doua garzi independente (`in_stoc=1` la apelant, plus garda proprie pe - `NOM_ARTICOLE.IN_STOC`) care o opresc pentru articolele din 48/49. -- `sterge_factura` nu are ramura proprie pentru 48/49 (cade in bucketul generic ">20, exclus - hotel/restaurant/notaplata"), iar actiunea acelui bucket (`VANZARI_CANTITATI`) nu are ce sa - actioneze pentru aceste doua tipuri (fara rol in acel tabel). -- Cele trei garzi de refuz al stergerii nu disting custodia, si nu se declanseaza pentru un document - 48/49 fara retur emis pe el. - -**Dedus, nu verificat exhaustiv:** -- Ca operatorul nu poate adauga pe un document 48/49 un articol gestionabil pe alta cale decat - `cursor_articole_k` — bazat pe faptul ca grid-ul se populeaza o singura data la deschidere, dar - n-am urmarit tot codul de interactiune al grid-ului (`adauga la lista`/editare inline) pentru cele - doua tipuri. -- Ca diferenta reala dintre tip 48 si tip 49 e doar de conventie/business, nu de cod — n-am gasit - nicio ramura care sa le separe, dar nici n-am cautat in afara `PACK_FACTURARE`/`ofacturare.prg` - (de exemplu in rapoarte sau in alte proceduri VFP care ar putea trata diferit "cu descarcare K" vs - "fara"). - -**Neacoperit in aceasta runda:** -- Mecanismul general prin care stocul "revine" la stergerea unei facturi **normale** (`ntip<=20`) — - nu s-a gasit niciun `UPDATE STOC`/reversare explicita in `PACK_FACTURARE`; posibil calculat prin - interogari care filtreaza `VANZARI_DETALII.STERS=0`, posibil in alt pachet Oracle sau prin trigger, - in afara perimetrului citit in aceasta runda. Nu e o intrebare specifica custodiei — se aplica - identic la orice tip de document — dar ramane deschisa. -- Semnificatia exacta si diferenta functionala/de raportare intre "custodie cu descarcare K" (48) si - "custodie fara descarcare K" (49) — n-am gasit in cod ce anume descarca "K" daca nu e stoc fizic - (posibil un calcul contabil de adaos comercial specific comertului cu amanuntul, coeficientul K, - dar nu confirmat pe cod in aceasta runda). -- Testare pe date reale (baza de dev) a unui ciclu emitere->stergere->reemitere pe un document 48/49 - — cercetarea a fost exclusiv pe cod si `SELECT`-uri simple, fara sa ruleze fluxul. diff --git a/docs/cercetare/discount_document_cota_tva.md b/docs/cercetare/discount_document_cota_tva.md deleted file mode 100644 index 586eff4..0000000 --- a/docs/cercetare/discount_document_cota_tva.md +++ /dev/null @@ -1,731 +0,0 @@ -# Cota de TVA a discountului de DOCUMENT (VANZARI.DISCOUNT) — program + eFactura - -Cercetare read-only. Nu s-a modificat niciun fisier, nu s-a rulat `git_sync.ps1`, pe Oracle doar -`SELECT`. - -STATUS: complet. - -Acest document e rezultatul imbinarii a doua rapoarte de cercetare pe aceeasi intrebare, facute de -doi agenti separati. Raportul suplimentar, `discount_document_cota_tva_b.md`, ramane pe disc ca -sursa a partii adaugate aici (sectiunile 3.1-3.3 si observatiile din 2.3 si 4.1). - -## Raspunsul scurt - -Discountul de document **NU se sparge pe cote**. El devine o **singura pseudo-linie negativa** -numita `"Discount % Factura"`, careia i se atribuie **cota maxima de TVA de pe factura** -(`Calculate Max(proc_tvav) To lnProcTvav`). eFactura chiar genereaza `cac:AllowanceCharge` la nivel -de document, cu `TaxCategory/ID` + `Percent` + `TaxScheme` corecte si `AllowanceChargeReason = -"Discount"`, `ReasonCode = 95` — dar **cota e cea maxima, aleasa prin `Max()`, nu de -utilizator si nici proportionala**, iar "explicatia" e literalul hardcodat `"Discount"`. - -Regula "cota maxima" e scrisa **de doua ori**, independent: in VFP (`Calculate Max(proc_tvav)`) si in -PL/SQL (`PACK_FACTURARE.recalculeaza_totaluri_vanzari`, care din ea deriva `VANZARI.DISCOUNT_TVA`, -`TOTAL_TVA` si `TOTAL_CU_TVA`). Pentru #13 asta e informatia operationala: **se schimba in doua -locuri, nu in unul** (sectiunea 1.5). - -Pe factura obisnuita (cote mixte, fara scutire), XML-ul rezultat e intern coerent (TaxSubtotal-urile -includ deja pseudo-linia de discount) si **trece validarea** — verificat cu validatorul local -DUKIntegrator, inclusiv pe cazul mixt si pe unul cu baza impozabila negativa. Deci defectul pe care -**l-am putut dovedi** nu se vede: e o eroare **tacuta de atribuire fiscala**. Pe o factura cu 1000 -lei la 21% si 1000 lei la 11% cu discount de document 10%, se declara cu **10 lei mai putin TVA** -decat ar trebui (sectiunea 4.4), mereu in acelasi sens — TVA colectat subdeclarat. Pe factura -scutita XML-ul e si mai putin coerent decat atat — vezi paragraful urmator. - -**Sectiunea 3 s-a inchis intre timp, cu un rezultat impartit.** Pe o factura obisnuita (toate -liniile `scutit=0`, `expltva` gol), pseudo-linia de discount se contopeste corect in grupul cotei -maxime — verificat prin masuratoare, nu doar dedus (sectiunea 3.1). Dar pe o factura **scutita, -cu taxare inversa sau intracomunitara**, gruparea chiar se rupe: pseudo-linia formeaza un -`TaxSubtotal` **orfan**, cu baza negativa si categorie gresita (sectiunea 3.2). Validatorul local nu -respinge nici aceasta forma (sectiunea 3.3) — deci ramane, ca si defectul de atribuire fiscala, o -eroare **tacuta**, nu una care blocheaza factura. - ---- - -## 1. Cum se aplica azi discountul de document in program - -**Cine il aplica: amandoua, fiecare pentru consumatorul lui.** Oracle stocheaza `VANZARI.DISCOUNT` -(valoare absoluta, in moneda documentului) si `VANZARI.DISCOUNT_EVIDENTIAT`, si **isi calculeaza -singur** TVA-ul discountului in `PACK_FACTURARE.recalculeaza_totaluri_vanzari` (sectiunea 1.5); -prelucrarea pentru **listare si eFactura** se face separat, in cursoare VFP (sectiunile 1.1-1.4). -Ambele folosesc aceeasi regula — cota maxima — dar o implementeaza independent. - -### 1.1. Randul-sentinela `ZZZZ...` in `crsfactura` - -`COMUN\programe\ofacturare_comun.prg:1886-1897` (`Procedure prelucreaza_facturacrs`): - -``` -1886 If tnDiscount<> 0 -1888 Calculate Sum(valdiminuatftva),Sum(vvaldiminuatftva),Max(id_temp) To lnTotalBaza,lnTotalBazaVal,lnIdTemp -1890 Append Blank -1891 Replace denumire With Replicate('Z',20),cantitate With 1,; -1892 pretftva With lnTotalBaza,discountftva With tnDiscount,valdiscountftva With tnDiscount,; -1893 valdiscounttva With Round(tnDiscount * (tnProcTvav - 1),gnPc),; -1894 vpretftva With lnTotalBazaVal,vdiscountftva With tnDiscountVal,vvaldiscountftva With tnDiscountVal,; -1895 vvaldiscounttva With Round(tnDiscountVal * (tnProcTvav - 1),gnPVal),; -1896 proc_Tvav With tnProcTvav,id_temp With lnIdTemp+1 -1897 Endif -``` - -Observatii portante: -- **baza** discountului = `Sum(valdiminuatftva)` peste **toate** liniile, indiferent de cota; -- **TVA-ul** discountului = `tnDiscount * (tnProcTvav - 1)` — o **singura** cota, `tnProcTvav`; -- randul e un **singur rand de document**, nu o repartizare pe linii. - -### 1.2. De unde vine `tnProcTvav` — cota maxima de pe factura - -Toate cele trei cai de apel calculeaza acelasi lucru: - -| apelant | linia | cod | -|---|---|---| -| listare factura emisa (din lista de facturi) | `COMUN\clase\ofacturare_comun.vc2:4494-4498` (`frm_facturi.do_listeaza_formular`, 4188-4539) | `Calculate Max(proc_tvav) To lnProcTvav` -> `prelucreaza_facturacrs([crsdetalii],[crsfactura],lnProcTvav,lnDiscount,lnDiscountVal)` | -| listare proforma | `COMUN\clase\ofacturare_comun.vc2:7293-7298` (`frm_proforme.do_listare`, 7233-7315) | idem | -| listare din `oproceduri_facturare` | `COMUN\programe\oproceduri_facturare.prg:1386-1391` | `Select crsDetaliiListare` / `Calculate Max(proc_tvav) To lnProcTvav` | -| facturare din stoc | `COMUN\programe\ofacturare_stoc.prg:582, 728` | `Calculate Max(proc_tvav) To lnProcTvav` | - -**Deci cota de TVA a discountului de document = MAX(cotele de pe liniile facturii).** Nu e nici -aleasa de utilizator, nici derivata din structura pe cote, nici proportionala. Nu exista niciun -control in formular pentru ea si nicio coloana in `VANZARI` care sa o retina. - -### 1.3. Randul `ZZZZ...` devine pseudo-linia `"Discount NN.NN % Factura"` - -`COMUN\programe\ofacturare_comun.prg:1273-1392` (in `prelucreaza_factura`, scan peste -`crsfacttemp`): - -``` -1279 If loArticol.denumire <> Replicate('Z',20) - ... (linia normala de articol se copiaza in cursorul destinatie) -1361 Else -1362 loArticol.denumire = [FACTURA] -1363 Endif -1365 If lnPretListAviz = 2 && pret fara tva -1367 If loArticol.discountftva <> 0 -1368 Append Blank -1369 lnProcentDiscount = loArticol.discountftva * 100 / loArticol.pretftva -1370 Replace denumire With [Discount ]+Alltrim(Str(lnProcentDiscount,10,2))+[ % ]+Alltrim(Proper(loArticol.denumire)),; -1374 pretftva With (-1) * loArticol.discountftva,valftva With (-1) * loArticol.valdiscountftva,...; -1375 ... valtva With (-1) * loArticol.valdiscounttva,; -1376 proc_tva With loArticol.proc_Tvav,id_jtva_coloana With loArticol.id_jtva_coloana,... -1378 Endif -``` - -Randul `ZZZZ...` nu ajunge niciodata in cursorul final ca atare — la `:1361-1363` doar i se schimba -denumirea in `FACTURA`, iar la `:1367-1377` se creeaza pseudo-linia -**`"Discount 10.00 % Factura"`** cu `pretftva` si `valftva` **negative** si `proc_tva = tnProcTvav`. - -**Coloanele atinse in cursorul final** (`crsFacturaFinala` / `crsfacturafinalaval`): `denumire`, -`cantitate`, `pretftva` (negativ), `valftva` (negativ), `valtva` (negativ), `proc_tva`, -`id_jtva_coloana`, `id_jtva_coloana_ex`. Deci **discountul de document mosteneste si coloana de -jurnal TVA (`id_jtva_coloana`) a randului-sentinela** — care nu e setata la `:1891-1896`, deci -ramane 0/blank pe randul `ZZZZ`. (Consecinta in eFactura: sectiunea 2.3.) - -### 1.4. `discount_evidentiat` schimba cate pseudo-linii "Discount" apar - -- `discount_evidentiat = 0` (implicit): ramura `ofacturare_comun.prg:1177-1203`. Liniile de articol - primesc `0 As discountftva` (`:1188`), deci **nu** genereaza pseudo-linii; doar randul `ZZZZ` - (reinserat separat prin `Where denumire = Replicate('Z',20)`, `:1193-1203`) pastreaza - `discountftva` -> **o singura** pseudo-linie de discount, cea de document. -- `discount_evidentiat = 1`: ramura `:1157-1173`, un singur `Insert` fara `WHERE`, `discountftva` - pastrat per articol (coloana 20 din `group by 2,3,...,20,...`) -> **fiecare articol cu discount - unitar produce propria pseudo-linie** `"Discount X % "`, plus cea de document. Aici - cotele chiar sunt cele ale articolelor respective. - -### 1.5. Regula "cota maxima" e implementata A DOUA OARA, in Oracle - -Corectie la afirmatia de la inceputul sectiunii 1 ("Oracle stocheaza doar `VANZARI.DISCOUNT`"): -**este incompleta**. `PACK_FACTURARE.recalculeaza_totaluri_vanzari` reface acelasi rationament -independent, in PL/SQL — -`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql`, procedura de la -`:16024`: - -``` -16081 MAX(ROUND(decode(lnInValuta, 1, -16082 ROUND(a1.curs * NVL(lnDiscountFactura,0) / a1.multiplicator, lnPreciziePretV), -16085 NVL(lnDiscountFactura,0)) * -16088 (a1.proc_tvav - 1), lnPreciziePretV)) as DISC_TVA_RON, -16090 MAX(ROUND(NVL(lnDiscountFactura,0) * (a1.proc_tvav - 1), lnPreciziePretV)) as DISC_TVA_VAL, -``` - -**Precizare pe forma exacta**: nu e `discount * MAX(proc_tvav)`, ci `MAX(discount * (proc_tvav-1))` -— maximul se ia peste **produsele rotunjite**. Cat timp `lnDiscountFactura >= 0` cele doua coincid -(acelasi rand castiga), deci efectul e identic cu al VFP-ului. Ar diverge doar pe un discount -**negativ** (o majorare), unde `MAX` ar alege cota cea mai **mica** — n-am verificat daca un discount -negativ e posibil in UI. - -**Ce se scrie cu asta** (`:16049-16062` -> `:16214-16227`): nu doar `VANZARI.DISCOUNT_TVA`, ci si -**totalurile salvate ale documentului**: - -``` -16052 a.suma_tva_ron - a.disc_tva_ron as TOTAL_TVA, -16053 a.suma_fara_tva_ron - a.disc_fara_tva_ron + a.suma_tva_ron - a.disc_tva_ron as TOTAL_CU_TVA, -... -16214 update vanzari set discount = lnDiscountFactura, discount_tva = lnDiscountTVA, ... -16218 total_tva = lnTotalTVA, total_cu_tva = lnTotalCuTVA, ... where id_vanzare = V_ID_VANZARE; -``` - -**Consecinta pentru #13**: regula de repartizare a discountului pe cote trebuie schimbata **in doua -locuri, nu unul** — `prelucreaza_facturacrs` (VFP, pentru listare/eFactura/nota contabila) **si** -`recalculeaza_totaluri_vanzari` (Oracle, pentru `VANZARI.DISCOUNT_TVA` / `TOTAL_TVA` / -`TOTAL_CU_TVA`, adica pentru ce se vede in liste, rapoarte de sinteza si sold). Daca se schimba doar -unul, `VANZARI.TOTAL_TVA` si TVA-ul din XML **vor diverge**. Nu am verificat care dintre cele doua -valori e considerata azi "cea buna" acolo unde ambele sunt disponibile. - -### 1.6. Ramura "pret cu TVA" (`lnPretListAviz = 1`) — nu atinge facturile - -`ofacturare_comun.prg:1380-1391` (ramurile `Otherwise` / `tnDiscountEvidentiat=1 And -lnPretListAviz=1`) seteaza pe pseudo-linia de discount **numai** `pretctva`/`valctva`/`valtva`, -**nu** si `pretftva`/`valftva` — care raman 0. Am verificat daca asta poate ajunge in eFactura: -**nu poate**. `lnPretListAviz` e initializat `= 2` (`COMUN\programe\ofacturare.prg:1112`) si e pus -pe `1` intr-un singur loc, in ramura de **aviz de transfer** (`lcRaport = [AVIZ]`, -`ofacturare.prg:1856-1861`, conditionat de `gnPretListAviz = 1`), iar avizele nu trec de filtrul -`tip_doc_394 IN ('F','S','M','U','H')` din `xmlefactura.prg:276-279`. Deci nu e un defect eFactura. - ---- - -## 2. Ce ajunge in XML-ul eFactura - -**Generatorul viu** e `COMUN\programe\xmlefactura.prg` (`getXmlEFactura`), apelat prin -`goExport.export2xml_efactura` (`COMUN\programe\oexport.prg:1586-1594`) din -`COMUN\programe\ofacturare.prg:2126`, cu cursorul `crsFacturaFinala` (sau `crsfacturafinalaval` in -valuta) — exact cursorul de listare (`ofacturare.prg:2108-2114`). -`COMUN\clase\anaf_efactura.vc2` este UI-ul/transportul ANAF (trimitere, validare, preview), nu -generatorul de XML. - -### 2.1. Da — se genereaza `cac:AllowanceCharge` la nivel de document - -Detectia e **euristica pe denumire + semn**, `xmlefactura.prg:230`: - -``` -230 Iif('DISCOUNT' $ Upper(a.denumire) And a.pretftva < 0, 1, 0) As discount ; -``` - -Pseudo-linia `"Discount 10.00 % Factura"` cu `pretftva < 0` (sectiunea 1.3) satisface ambele -conditii. Randurile marcate `discount = 1` sunt excluse din liniile normale (`Scan For discount = -0`, `:901`) si emise ca alocari de document, `xmlefactura.prg:756-795`: - -``` -757 If mliniireducere > 0 -758 SELECT SUM(-1*valftva) as valftva, proc_tva ; -759 FROM C_IES_FORM ; -760 WHERE discount = 1 ; -761 GROUP BY proc_tva ; -762 ORDER BY proc_tva ; -763 INTO CURSOR cDiscounturiTemp -765 Select cDiscounturiTemp -766 SCAN -770 oallowancecharge = oinvoice.appendchild(oxml.createelement("cac:AllowanceCharge")) -771 ... ChargeIndicator = "false" -773 ... cbc:AllowanceChargeReasonCode = "95" -775 ... cbc:AllowanceChargeReason = "Discount" -777 odocallowancecharge = ... ("cbc:Amount") && BT-92 -778 odocallowancecharge.setattribute("currencyID", "RON") -779 odocallowancecharge.Text = Alltrim(Str(m.lnValftva, 15, 2)) -780 otaxcategoryallowance = ... ("cac:TaxCategory") -782 Select tip From C_TVA_FACTURA Where proc_tva = (m.lnProcTva - 1) * 100 Into Array agettipcota -786 otaxcategoryallowance.lastchild.Text = Alltrim(agettipcota(1)) -787 ... ("cbc:Percent") -788 otaxcategoryallowance.lastchild.Text = Alltrim(Str((m.lnProcTva - 1) * 100, 2, 0)) -789 otaxschemeallowance = ... ("cac:TaxScheme") / cbc:ID = "VAT" -792 ENDSCAN -``` - -Raspunsurile punctuale: - -- **`cac:TaxCategory/cbc:ID`** (`S`, `AE`, `Z`, `E`, `K`, `O`): **nu e hardcodat** — se ia din - `C_TVA_FACTURA.tip`, adica din `This.GetTipTaxa(mtipfactura, intracomunitar, taxare_inversa, - scutit, proc_tva, @lcExplicatie, @lcMotiv, expltva)` (`xmlefactura.prg:298`), pe grupul cu - **aceeasi cota** ca pseudo-linia de discount. -- **`cbc:Percent`**: `(proc_tva - 1) * 100` al pseudo-liniei — adica **cota maxima de pe factura** - (sectiunea 1.2). *Aici e raspunsul la intrebarea lui Marius: cota exista in XML, dar sursa ei e - un `Max()`, nu o alegere.* -- **`cbc:AllowanceChargeReason`**: literalul `"Discount"` (`:776`), **hardcodat**; - **`cbc:AllowanceChargeReasonCode`**: `"95"` (`:774`), hardcodat. Textul real din program - (`"Discount 10.00 % Factura"`) **nu** ajunge in XML — ramane doar in cursorul de listare. - Deci "explicatia" pe care o cauta Marius nu exista: e o constanta. - -### 2.2. Cote mixte: se sparge in mai multe `AllowanceCharge`? - -**Mecanismul exista** (`GROUP BY proc_tva`, `:761` -> cate un `AllowanceCharge` per cota), dar -**nu se activeaza pentru discountul de document**, pentru ca acesta e prin constructie o **singura** -pseudo-linie cu o **singura** cota (`Max`). Gruparea foloseste efectiv doar cand -`discount_evidentiat = 1`, unde exista mai multe pseudo-linii de discount **pe articol**, fiecare cu -cota articolului ei. - -Deci, pe o factura cu 21% si 11% si un discount de document: **un singur** `AllowanceCharge`, cu -`Percent = 21`. - -### 2.3. Riscuri identificate in acest bloc (nedovedite pe rulare) - -- **`currencyID` hardcodat `"RON"`** la `:778`, in timp ce tot restul documentului foloseste - `mmoneda` (`:820, 827, 873, 876, ...`, definit la `:266-267`). Pe o factura in valuta cu discount - de document, `AllowanceCharge/cbc:Amount` iese cu `currencyID="RON"` iar - `LegalMonetaryTotal/cbc:AllowanceTotalAmount` cu `currencyID="EUR"`. Acelasi tipar la acciza - (`:803`). **Testat cu validatorul local** (sectiunea 4.3): DUKIntegrator **nu** respinge - neconcordanta. Ramane o inconsecventa de cod, dar nu doar teoretica: sectiunea 4.1 arata un XML - real de productie cu exact aceasta neconcordanta, confirmat pe SHA256 ca a plecat la ANAF si a - fost **acceptat**. -- **`Select ... Into Array agettipcota` fara garda pe `_Tally`** (`:782-786`): daca gruparea nu - gaseste randul (egalitate pe numere in virgula mobila: campul `C_TVA_FACTURA.proc_tva` e stocat - rotunjit, comparatia se face cu `(lnProcTva-1)*100` calculat la rulare), `agettipcota(1)` da - eroare de variabila inexistenta. *Risc teoretic — nu l-am putut reproduce; il semnalez ca "de - verificat", nu ca defect.* -- **`Str((lnProcTva-1)*100, 2, 0)`** — latime 2. Pentru cotele romanesti (0, 5, 9, 11, 19, 21) e in - regula; o cota de trei cifre ar da `**`. - ---- - -## 3. Coerenta cu `TaxTotal` / `TaxSubtotal` - -**Da, baza impozabila din XML tine cont de discount** — si o face corect aritmetic. - -`C_TVA_FACTURA` se construieste **peste toate randurile** din `C_IES_FORM`, **inclusiv** -pseudo-linia de discount (nu exista `WHERE discount = 0`), `xmlefactura.prg:242-248`: - -``` -242 Select(proc_tva - 1) * 100 As proc_tva, intracomunitar, taxare_inversa, scutit, expltva, ; -243 Sum(valtva) As tva, ; -244 Sum(valftva) As valoare, ... ; -245 From C_IES_FORM ; -246 Group By proc_tva, intracomunitar, taxare_inversa, scutit, expltva ; -247 Into Cursor C_TVA_FACTURA -``` - -`valftva`/`valtva` ale pseudo-liniei sunt negative -> grupul cotei maxime iese **deja net de -discount**. `cbc:TaxableAmount` = `C_TVA_FACTURA.valoare` (`:828`), `cbc:TaxAmount` = -`C_TVA_FACTURA.tva` (`:831`). - -Totalurile inchid corect (`:863-898`): - -``` -864 Sum valftva To mtotalnet For discount = 0 && numai liniile reale -867 mtotalnetliniifactura = mtotalnet && BT-106 LineExtensionAmount -869 mtotalnet = mtotalnet + mtotalcharges - mtotalallowances && BT-109 TaxExclusiveAmount -870 mtotalbrut = mtotalnet + mtotaltva && BT-112 TaxInclusiveAmount -882 ... AllowanceTotalAmount = mtotalallowances && BT-107 -``` -cu `mtotalallowances = mdiscounturi = Sum(-1*valftva) For discount = 1` (`:282-287`). - -Verificare: `Σ TaxSubtotal/TaxableAmount` = suma peste toate randurile = `mtotalnetliniifactura − -mdiscounturi` = `mtotalnet` = `TaxExclusiveAmount`. Deci **BR-CO-13** (`TaxExclusiveAmount = -LineExtensionAmount − AllowanceTotalAmount + ChargeTotalAmount`) si **BR-CO-15** se respecta. - -**Concluzie**: pe **factura obisnuita**, suma liniilor minus discount **da** `TaxableAmount`-ul -declarat; discrepanta nu e aritmetica, ci **de atribuire pe cote** (sectiunea 4). Pe **factura -scutita / cu taxare inversa / intracomunitara**, concluzia nu tine — vezi 3.1-3.3. - -Rationamentul de mai sus presupune ca pseudo-linia de discount **cade in acelasi grup** cu liniile -de la cota maxima. Asta e adevarat doar daca cele patru chei suplimentare din `GROUP BY` -(`intracomunitar`, `taxare_inversa`, `scutit`, `expltva`, `xmlefactura.prg:246`) ies **egale** pe -pseudo-linie si pe liniile reale. Subsectiunile care urmeaza verifica exact asta, prin masuratoare, -nu prin deductie. - -### 3.1. Verificat prin masuratoare: `IIF()` peste NULL nu se propaga ca `.NULL.` in VFP - -Pseudo-linia are `id_jtva_coloana = 0` (randul-sentinela e `Append Blank` la -`ofacturare_comun.prg:1890` si campul nu e setat la `:1891-1896`). Daca LEFT JOIN-ul de la -`xmlefactura.prg:226-232` nu gaseste rand in `cJTVAVanzariTemp` pentru `id = 0`, `b.coloana_jv` iese -`.NULL.`, si intrebarea e ce intoarce `Iif(Left(.Null.,2) = 'CE', 1, 0)` — daca s-ar propaga ca -`.NULL.`, cheia de grupare a pseudo-liniei ar diferi de a liniilor reale pe **orice** factura. - -Am rulat o sonda headless (`vfp9.exe -A -T`, `probe_null.prg`) care reproduce exact acest `LEFT -JOIN` si `GROUP BY`, cu o pseudo-linie de discount pe `id_jtva_coloana = 0`: - -``` -1) IIF(.NULL.="CE",1,0) -> tip=N isnull=.F. -2) IIF(LEFT(.NULL.,2)="CE",1,0) -> tip=N isnull=.F. -3) IIF(INLIST(.NULL.,...),1,0) -> tip=N isnull=.F. - linie=ARTICOL A | coloana_jv nul=.F. | intracom=0 | scutit=0 - linie=ARTICOL B | coloana_jv nul=.F. | intracom=0 | scutit=0 - linie=Discount 10.00 % Factura | coloana_jv nul=.T. | intracom=0 | scutit=0 -4) grupuri in C_TVA_FACTURA = 1 - grup: cota=21 baza=1350 tva=283.50 (1500 - 150, corect) -``` - -**`IIF()` intoarce ramura falsa, nu `.NULL.`**, chiar daca conditia e nula. Deci pe o factura -interna obisnuita pseudo-linia primeste `intracomunitar=0, taxare_inversa=0, scutit=0` — exact ca -liniile reale — si **se contopeste in grupul cotei maxime**. Sonda ramane pe disc in -`%TEMP%\claude\...\scratchpad\probe_null.prg`, nu depinde de baza de date si se poate reface -oricand. - -### 3.2. Dar pe factura scutita / taxare inversa / intracomunitara, gruparea chiar se rupe - -Contopirea de la 3.1 tine doar cat timp **si liniile reale** au `scutit=0` si `expltva` gol. Pe o -factura scutita nu e asa: - -| camp din cheia `Group By` (`xmlefactura.prg:246`) | liniile reale (scutite) | pseudo-linia de discount de document | -|---|---|---| -| `id_jtva_coloana` | id real (ex. 11) | **0** — `prelucreaza_facturacrs` nu-l seteaza niciodata pe randul `ZZZZ` (`ofacturare_comun.prg:1891-1896`) | -| `coloana_jv` (din `LEFT JOIN`) | `'WRSCDD'` | `.NULL.` — `cJTVAVanzariTemp` e incarcat cu `where afisat > 0 and id_jtva_coloana > 0` (`updateserver.prg:578`), deci **nu exista rand cu id 0** | -| `scutit` | **1** | **0** | -| `expltva` | `'Scutit cu drept de deducere'` | **gol** | - -Ultimul rand merita explicat, pentru ca e contraintuitiv: `completeaza_explicatie_tva` -(`ofacturare_comun.prg:2077-2137`) chiar **include** pseudo-linia in lista de id-uri cautate — scanul -de la `:2094` e `For proc_tva = 1 And Nvl(id_jtva_coloana,-1) <> -1`, iar pseudo-linia are -`proc_tva = 1` (cota 0) si `id_jtva_coloana = 0`, deci trece filtrul. Dar interogarea de la `:2108` -e `where id_jtva_coloana in (...)` pe `jtva_coloane`, unde id-ul 0 nu exista, asa ca `Replace expltva -... For NVL(id_jtva_coloana,0) = m.lnIdJtva` (`:2129`) nu o atinge niciodata. Ramane goala. - -Doua campuri diferite din cinci => **doua grupuri**. XML-ul iese cu: - -```xml - - ... Discount - 539.82 - Z0... - -0.00 - -539.82 - 0.00 - Z0... - 4410.00 - 0.00 - E0 - Scutit cu drept de deducere... - -``` - -Categoria `Z` a grupului orfan vine din `GetTipTaxa` ramura cu ramura (`xmlefactura.prg:1116-1140`): -`intracomunitar=0` sare peste `'K'`, `taxare_inversa=0` sare peste `'AE'`, `scutit=0` sare peste -`'E'`, si cade pe `Case m.lcTipFactura = 'F' And m.lnProcTva = 0` -> **`'Z'`** (`:1128-1129`). - -In plus, cautarea categoriei pentru `AllowanceCharge` (`xmlefactura.prg:782-786`) devine ambigua: -`Select tip From C_TVA_FACTURA Where proc_tva = 0 Into Array agettipcota` gaseste acum **doua** -randuri (`E` si `Z`), iar codul ia neconditionat `agettipcota(1)` — primul in ordinea cursorului, nu -cel corect prin vreun criteriu. Ordinea nu e garantata de VFP si nu a fost fixata experimental; -categoria alocarii poate iesi `E` sau `Z` dupa caz — oricare din ele e gresita ca modelare, doar in -mod diferit. - -**Nu s-a reprodus scenariul end-to-end prin program** (ar fi cerut o factura scutita cu discount de -document, iar in baza de dev nu exista — sectiunea 4.2). Ramane stabilit din citirea codului plus -semantica `IIF`/NULL masurata la 3.1, nu dintr-o factura rulata efectiv prin program. - -### 3.3. Validare offline: XML-ul cu grup orfan nu e respins, dar controlul negativ functioneaza - -XML-urile de mai sus au fost construite pornind de la o factura reala acceptata de ANAF ca schelet -si trecute prin validatorul local `DUKIntegrator.jar` (acelasi apel ca la 4.3): - -| scenariu | ce contine | rezultat | -|---|---|---| -| `scutit_bug` | scutit + discount de document, **cu grupul orfan** (ce produce codul azi) | **ok** | -| `scutit_corect` | scutit + discount, un singur `TaxSubtotal` net (varianta corecta) | **ok** | -| `control_stricat` | `TaxExclusiveAmount` stricat intentionat, ca martor ca validatorul chiar valideaza | **eroare `BR-CO-13` + `BR-CO-15`** | - -Controlul negativ conteaza: fara el, un „ok" pe `scutit_bug` n-ar dovedi nimic — ar putea insemna -ca validatorul nu verifica deloc coerenta totalurilor. Cu el, e clar ca validatorul **chiar -verifica**, si totusi trece grupul orfan. - -**De ce trece**: regulile EN16931 pe categorii (`BR-S-08`, `BR-E-08`, `BR-Z-08`) cer ca baza -declarata pe o categorie sa fie egala cu *suma liniilor din acea categorie minus alocarile din -aceeasi categorie*. Grupul orfan respecta asta trivial (`0 - 539.82 = -539.82`), iar grupul `E` la -fel (`4410.00 - 0`). Aritmetica inchide; **modelarea fiscala e cea gresita**, si asta niciun -schematron nu prinde. - -Aceeasi rezerva ca in 4.3: validatorul local e din ianuarie 2022 (`ro16931-ubl-1.0.8`); validatorul -online curent al ANAF nu a fost apelat (interdictie explicita). - -> **Verdict sectiunea 3**: contopirea corecta descrisa mai sus e confirmata prin masuratoare pentru -> factura obisnuita (3.1) si infirmata pentru factura scutita / taxare inversa / intracomunitara -> (3.2), unde discountul de document formeaza un `TaxSubtotal` orfan cu baza negativa si categorie -> ambigua (`E` sau `Z`). Pe niciuna din cele doua forme validatorul local nu respinge XML-ul (3.3, -> si 4.3 pentru cazul cu cote mixte) — deci ramane, ca si defectul din sectiunea 4, o eroare -> **tacuta**, nu una care blocheaza factura. - ---- - -## 4. Defect real sau gol de proiectare - -**Verdict: NU s-a putut dovedi un defect de VALIDARE — nici pe factura obisnuita, nici pe cea -scutita cu grupul orfan (sectiunea 3.2-3.3), validatorul local nu respinge XML-ul. S-a dovedit in -schimb un defect de ATRIBUIRE FISCALA: pe facturi cu cote mixte, tot TVA-ul discountului de -document se scade din cota maxima, si pe facturi scutite/taxare inversa/intracomunitare, o -modelare fiscala gresita (grup orfan) care trece neobservata.** Adica exact ce banuia Marius, dar -consecinta stabilita nu e "factura respinsa", ci "TVA colectat gresit sau baza modelata gresit, -tacut". - -> **Nuanta ramasa, dupa inchiderea ipotezei din sectiunea 3**: ipoteza grupului orfan **s-a -> confirmat** pentru facturile scutite/taxare inversa/intracomunitare (3.2), dar validatorul local -> **nu** o respinge (3.3) — deci nu devine, cum s-ar fi putut anticipa, un defect de validare -> propriu-zis, ci ramane in aceeasi categorie cu restul sectiunii 4: o eroare de modelare care nu -> blocheaza factura. Testele din 4.3 (cote mixte) si cele din 3.3 (factura scutita) acopera acum -> ambele forme. - -### 4.1. Dovada ca mecanismul chiar produce `AllowanceCharge` de document in productie - -Am cautat in cele ~250 de XML-uri eFactura salvate pe disc (`D:\ROA\Efactura`, `D:\ROA\ROAEFACTURA`, -`D:\ROA\ROAFACTURARE\Utile\efactura`) blocurile `cac:AllowanceCharge` care contin `cac:TaxCategory` -(marca alocarii **de document**; cele de linie nu au `TaxCategory`, au `MultiplierFactorNumeric`). -Exemple reale: - -| fisier | Amount | TaxCategory | Percent | -|---|---|---|---| -| `D:\ROA\Efactura\2024_01\VENDING_MASTER_SRL\efactura_20240125_1526_BEST_POWER_DANCE_S_R_L_.xml` | 13.84 / 25.02 (doua) | S / S | 9 / 19 | -| `D:\ROA\Efactura\2024_02\VENDING_MASTER_SRL\efactura_20240216_2885_IT_BUL_PLAST_LTD.xml` | 539.82 | E | 0 | -| `D:\ROA\Efactura\2024_03\VENDING_MASTER_SRL\efactura_20240301_3736_BRIANA_ALIMENT_SRL.xml` | 411.01 | S | 9 | -| `D:\ROA\Efactura\2024_07\VENDING_MASTER_SRL\efactura_20240426_7446_CIA_TRADE_POSSIBLE_SRL.xml` | 176.15 | S | 9 | - -Forma emisa (exemplu real, `...2885...`): -```xml -false - 95 - Discount - 539.82 - E0 - VAT -``` - -**Precizare de onestitate**: niciunul dintre aceste patru nu pot sa-l atribui sigur discountului de -**document**. Dimpotriva — la `...7446...` factura are linii pe 9% (2016.51) si pe 19% (1477.11), -iar unica alocare cade pe **9%**; daca ar fi fost discount de document, `Max(proc_tvav)` ar fi dat -**19%**. Deci acolo sunt discounturi **pe linie** cu `discount_evidentiat = 1` (sectiunea 1.4). La -fel `...1526...`, unde cele doua alocari sunt exact 2.00% din baza fiecarei cote. **Nu am gasit pe -disc un XML in care sa pot identifica pozitiv un discount de document.** Ce dovedesc aceste fisiere -e ca **traseul cod -> `AllowanceCharge` cu `TaxCategory` chiar functioneaza in productie** si ca -gruparea pe cote e reala cand exista mai multe pseudo-linii de discount. - -**Al treilea indiciu, pe acelasi fisier `...2885...`, in sens invers.** Factura e integral **scutita -cu drept de deducere**: toate cele trei linii sunt `E` / `Percent 0` cu `TaxExemptionReason = -"Scutit cu drept de deducere"`, `DocumentCurrencyCode = EUR`, si are `AllowanceCharge` la nivel de -document de 539.82 cu `TaxCategory ID = E`. Are **un singur `TaxSubtotal`**: `TaxableAmount = -3870.18`, adica exact `4410.00 − 539.82` — **net de discount, fara grup orfan**. - -Asta e semnificativ dupa sectiunea 3.2: `expltva` face parte din cheia de grupare, iar grupul unic -de aici poarta `TaxExemptionReason` completat. Daca pseudo-linia de discount ar fi avut `expltva` -gol si `id_jtva_coloana = 0` (cazul discountului de **document**, descris la 3.2), gruparea nu s-ar -fi putut contopi — ar fi iesit doua grupuri, ca in `scutit_bug` (3.3). Contopirea observata aici -dovedeste deci ca pseudo-linia purta un `id_jtva_coloana` real, adica era un discount **pe linie** -(`discount_evidentiat = 1`), nu de document — un al treilea indiciu, independent de cele doua de mai -sus (alocare pe cota minima la `...7446...`; doua alocari la `...1526...`), care coroboreaza aceeasi -concluzie: cele patru XML-uri de productie gasite sunt discounturi pe linie, nu de document. - -Fisierul arata insa **pozitiv forma corecta** pentru o factura scutita cu discount de document: cand -pseudo-linia poarta `id_jtva_coloana` corect, grupul iese unul singur si net — exact forma -recomandata la sectiunea 6, punctul 1. Deci recomandarea are un precedent real in productie, nu doar -o constructie sintetica de test. **Rezerva onesta**: fisierul e din februarie 2024, iar inferenta -presupune ca traseul de cod e neschimbat de atunci; ramane adevarat ca **nu exista niciun XML de -productie identificat pozitiv ca discount de DOCUMENT**. - -**Identitatea fisierului, verificata pe SHA256.** -`D:\ROA\Efactura\2024_02\VENDING_MASTER_SRL\efactura_20240216_2885_IT_BUL_PLAST_LTD.xml` (5922 -octeti) are acelasi SHA256 (`3303AFE34BC9E3E84DA9B3527C435DD9B8CB0BC7881D7D2F660A250237E23C64`) cu -`4172939206.xml` din `...\TRIMISE\4172939206_3250292036.zip` — deci e exact ce a plecat la ANAF, si -a fost **acceptat**. Continutul din `...\ERORI\4172675286_3249814520.zip` e un mesaj de eroare de -446 octeti pentru o incarcare **anterioara**, cu alt index de incarcare, si listeaza exact doua -reguli: `BR-CO-15` si `BR-CL-04` (cod de moneda invalid). Deci respingerea aceea **nu** e a -fisierului cu `currencyID="RON"` discutat la sectiunea 2.3 — acela a trecut. - -### 4.2. Datele din baza de dev — putine, dar arata ca situatia e posibila - -`MARIUSM_AUTO@ROA_CENTRAL`, doar `SELECT`: -- facturi nesterse cu `VANZARI.DISCOUNT <> 0`: **3** (id_vanzare 165 / 575 / 576; - `discount_evidentiat` = 1 / 1 / 0). Toate trei au **o singura cota** pe linii (1.19, 1.24, 1.24). -- facturi nesterse cu **cote mixte** pe linii: **34**. - -Deci in dev nu exista intersectia "discount de document + cote mixte". **Asta nu dovedeste nimic** — -volumele de dev sunt mici, iar ambele ingrediente exista separat. In productie combinatia e banala -(orice comerciant cu alimente 9%/11% + nealimentare 21% care da un discount global). - -### 4.3. Ce am testat efectiv cu validatorul ANAF, offline - -Am folosit **validatorul local** `D:\ROA\COMUNROA\dist_efactura\DUKIntegrator.jar` (`-v FACT1`, -reguli `ro16931-ubl-1.0.8`), acelasi pe care il apeleaza `xmlefactura.prg:1166-1194`. **Nu s-a -trimis nimic catre ANAF; nu s-a apelat niciun serviciu web.** XML-urile de test sunt in -`%TEMP%\claude\...\scratchpad\` (base/scenA/scenB/scenC_*), construite plecand de la un XML real -valid. - -| test | ce contine | rezultat | -|---|---|---| -| `base` | XML-ul real `...7446...`, nemodificat (martor) | **ok** | -| `scenA` | cote mixte 21% + 11%, discount 200 lei atribuit **integral** cotei 21% (exact ce genereaza programul) | **ok** | -| `scenB` | 100 lei @21% + 5000 lei scutit, discount 510 lei la 21% -> `TaxableAmount = -410.00`, `TaxAmount = -86.10` | **ok** | -| `scenC_corect` | acelasi ca scenA, in EUR, cu `AllowanceCharge/Amount currencyID="EUR"` | **ok** | -| `scenC_bug` | idem, dar cu `currencyID="RON"` pe alocare (ce face codul la `:778`) | **ok** | - -**Concluzii, inclusiv cele care imi infirma ipotezele:** -1. Atribuirea intregului discount cotei maxime **trece validarea** — deci nu e o eroare pe care ANAF - sa o prinda. E o eroare **tacuta**. -2. Ipoteza mea ca o `TaxableAmount` **negativa** ar fi respinsa e **infirmata** de validatorul local. -3. Ipoteza ca neconcordanta de moneda (`currencyID="RON"` pe factura in EUR) ar fi respinsa e tot - **infirmata** de validatorul local. -4. **Rezerva**: validatorul instalat e din **ianuarie 2022** (`DUKIntegrator.jar`, 19.01.2022, - reguli 1.0.8). Regulile ANAF s-au inasprit de atunci. Punctele 2 si 3 raman "neinfirmate de - validatorul disponibil local", nu "garantat acceptate azi de ANAF". - -### 4.4. Scenariul reproductibil al defectului REAL (fiscal) - -**Factura**: client intern, in lei, `discount_evidentiat = 0`. -- Linia 1: marfa cota standard, 1 buc x 1000.00 lei, **21%** -- Linia 2: marfa cota redusa, 1 buc x 1000.00 lei, **11%** -- Discount de document: **10%** -> `VANZARI.DISCOUNT = 200.00` - -**Ce face programul**: -- `Max(proc_tvav)` = 1.21 -> pseudo-linia de discount primeste **21%** - (`oproceduri_facturare.prg:1387` / `ofacturare_comun.vc2:4495`) -- `valdiscounttva = Round(200 * 0.21, 2) = 42.00` (`ofacturare_comun.prg:1893`) - -**Ce iese in XML** (verificat ca valid — `scenA`): -``` -AllowanceCharge: Amount 200.00, TaxCategory S, Percent 21 -TaxSubtotal S/21: TaxableAmount 800.00 TaxAmount 168.00 -TaxSubtotal S/11: TaxableAmount 1000.00 TaxAmount 110.00 -TaxAmount total 278.00 ; LineExtension 2000.00 ; TaxExclusive 1800.00 ; TaxInclusive 2078.00 -``` - -**Ce ar fi trebuit** (discount repartizat proportional cu baza: 100 lei pe fiecare cota): -``` -AllowanceCharge #1: 100.00, S, 21 AllowanceCharge #2: 100.00, S, 11 -TaxSubtotal S/21: 900.00 / 189.00 -TaxSubtotal S/11: 900.00 / 99.00 -TaxAmount total 288.00 ; TaxInclusive 2088.00 -``` - -**Diferenta: 10.00 lei TVA colectat in minus**, adica 5% din TVA-ul facturii — si creste cu ecartul -dintre cote si cu marimea discountului. **Sensul e mereu acelasi**: cota maxima absoarbe tot -discountul, deci TVA-ul declarat e **prea mic**. Riscul e al emitentului (TVA colectat -subdeclarat), nu al ANAF-ului care respinge documentul. Aceeasi valoare gresita ajunge si in nota -contabila si in jurnalul de TVA, pentru ca `valdiscounttva` e acelasi camp. - -**Caz-limita, tot valid dupa validator dar clar absurd** (`scenB`): factura cu 100 lei la 21% si -5000 lei scutit, discount de document 10% (510 lei). Tot discountul intra pe cota 21%, unde baza e -100 lei -> `TaxSubtotal S/21` iese cu `TaxableAmount = -410.00` si `TaxAmount = -86.10`. Factura -declara **TVA negativ** pe o livrare normala. Nu e nevoie de cote diferite de zero pe ambele parti: -ajunge o factura in care liniile de la cota maxima sunt o mica parte din total. - -### 4.5. Gol de proiectare, distinct de defect - -**Explicatia ("reason") nu exista ca notiune in program.** In XML `AllowanceChargeReason` e literalul -`"Discount"` si `ReasonCode` e `"95"`, ambele hardcodate (`xmlefactura.prg:774-776`). Textul construit -in VFP — `"Discount 10.00 % Factura"` (`ofacturare_comun.prg:1370`) — **nu ajunge in XML**. Nu exista -nicio coloana pe `VANZARI` pentru motivul discountului si niciun control in formular. Deci raspunsul -la a doua jumatate a intrebarii lui Marius: **nu, discountul de document nu are azi explicatie — -are o constanta.** - ---- - -## 5. Recomandare pentru formularul unificat (#13) - -**Sa NU ceara utilizatorului cota. Sa sparga discountul automat, proportional cu baza fiecarei cote, -si sa expuna doar un camp optional de motiv.** Argumentul e ca discountul de document nu *are* o cota -proprie de ales: prin definitie e un procent aplicat bazei intregii facturi (`lnTotalBaza = -Sum(valdiminuatftva)` peste toate liniile, `ofacturare_comun.prg:1888`), deci natura lui de TVA e -determinata de liniile pe care le reduce, nu de o optiune. A pune operatorul sa aleaga o cota inseamna -a-i cere sa ia o decizie fiscala pe care datele o dau deja, si a deschide o a doua cale de a gresi — -azi `Max()` greseste consecvent, un camp liber ar greseste imprevizibil. Repartizarea proportionala e -si singura care satisface modelul EN16931, unde `TaxableAmount`-ul fiecarei categorii se calculeaza ca -*net de linii al categoriei minus alocarile de document ale aceleiasi categorii*. **Costul de -implementare e mic, dar atinge doua locuri, nu unul** (sectiunea 1.5): `prelucreaza_facturacrs` -(`ofacturare_comun.prg:1886-1897`) trebuie sa insereze cate un rand-sentinela **per cota** in loc de -unul singur, cu `tnDiscount` repartizat pe baza fiecarei cote (ultima cota preia diferenta de -rotunjire, ca suma sa fie exact `VANZARI.DISCOUNT`), **si** `recalculeaza_totaluri_vanzari` -(`ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:16081-16092`) trebuie sa inlocuiasca `MAX(...)` cu suma -repartizarii, altfel `VANZARI.TOTAL_TVA` ramane pe regula veche si diverge de XML; **eFactura nu are -nevoie de nicio modificare structurala** — -`xmlefactura.prg:758-792` grupeaza deja `GROUP BY proc_tva` si emite cate un `AllowanceCharge` per -cota, iar `C_TVA_FACTURA` include deja pseudo-liniile in `TaxSubtotal`. Doua consecinte de decis -explicit cu Marius: **factura tiparita** va arata N randuri "Discount X % Factura" in loc de unul (pe -cote mixte), si **nota contabila** primeste TVA-ul discountului spart pe cote. Pentru "explicatie", -recomand un singur camp text optional pe document, folosit ca `AllowanceChargeReason` in locul -constantei `"Discount"` (`ReasonCode` ramane `95`) — util si pe hartie, si e singura bucata care chiar -cere UI nou. - -**Precizare, dupa sectiunea 3.2**: "eFactura nu are nevoie de modificare" e adevarat doar daca -fiecare pseudo-linie noua, per cota, primeste si `id_jtva_coloana` **al grupului pe care il reduce**, -nu doar `proc_tva` al lui. Cota singura nu ajunge — cheia de grupare din `xmlefactura.prg:246` are -cinci campuri, iar `expltva` se completeaza tot prin `id_jtva_coloana` (sectiunea 3.2). O -implementare care seteaza doar `proc_tva` pe randul-sentinela ar lasa grupul orfan exact acolo unde -e azi, pe facturile scutite / cu taxare inversa / intracomunitare. Fisierul `...2885...` de la -sectiunea 4.1 e dovada pozitiva ca, atunci cand `id_jtva_coloana` e setat corect, forma iese unul -singur si net — deci reparatia are deja un precedent real in productie, nu doar o constructie de -test. - ---- - -## Verificat direct vs. dedus - -**Verificat direct (citit in cod, cu `fisier:linie`)** -- randul-sentinela `ZZZZ` si campurile lui — `ofacturare_comun.prg:1886-1897` -- transformarea in pseudo-linia `"Discount % Factura"` — `ofacturare_comun.prg:1361-1392` -- `Max(proc_tvav)` pe toate cele patru cai de apel — `oproceduri_facturare.prg:1387`, - `ofacturare_comun.vc2:4495` si `:7294`, `ofacturare_stoc.prg:582` -- a doua implementare a aceleiasi reguli, in PL/SQL, si campurile pe care le scrie — - `ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:16024` (procedura), `:16081-16092` (`MAX(...)`), - `:16049-16062` (`TOTAL_TVA`/`TOTAL_CU_TVA` derivate din el), `:16214-16227` (`update vanzari`) -- euristica de detectie in eFactura — `xmlefactura.prg:230` -- generarea `AllowanceCharge` de document, cu `GROUP BY proc_tva` — `xmlefactura.prg:756-795` -- sursa lui `TaxCategory/ID` (`GetTipTaxa`, `xmlefactura.prg:1103-1143`) si a lui `Percent` -- includerea pseudo-liniei in `C_TVA_FACTURA` -> `TaxSubtotal` — `xmlefactura.prg:242-248, 822-851` -- inchiderea totalurilor `LegalMonetaryTotal` — `xmlefactura.prg:863-898` -- ca generatorul viu e `xmlefactura.prg`, nu `anaf_efactura` — `oexport.prg:1586-1594`, - `ofacturare.prg:2107-2126` -- ca ramura "pret cu TVA" (unde pseudo-linia iese cu `valftva = 0`) nu poate ajunge in eFactura — - `ofacturare.prg:1112, 1856-1861` + filtrul `tip_doc_394` din `xmlefactura.prg:276-279` -- semantica `LEFT JOIN` + `IIF`/`INLIST` peste `.NULL.` in cheia de grupare (sectiunea 3.1/3.2) — - `xmlefactura.prg:226-248` -- de ce grupul orfan nu poate fi completat prin `completeaza_explicatie_tva` — filtrul de scan vs. - filtrul de update (`ofacturare_comun.prg:2094` vs. `:2108, 2129`, sectiunea 3.2) -- sursa categoriei `Z` a grupului orfan, ramura cu ramura in `GetTipTaxa` — `xmlefactura.prg:1116-1140` - (sectiunea 3.2) -- incarcarea `cJTVAVanzariTemp` doar cu `id_jtva_coloana > 0`, motivul pentru care randul 0 nu se - gaseste — `updateserver.prg:578` (sectiunea 3.2) - -**Verificat prin rulare** -- 4 XML-uri reale de productie cu `AllowanceCharge` de document (sectiunea 4.1) -- 3 facturi cu discount in baza de dev, 34 cu cote mixte (`SELECT`, sectiunea 4.2) -- 5 validari DUKIntegrator offline pe cote mixte (sectiunea 4.3) — **au infirmat doua dintre - ipotezele initiale** -- semantica `IIF()` peste `.NULL.` masurata cu o sonda headless (`probe_null.prg`, sectiunea 3.1) — - intoarce ramura falsa, nu `.NULL.` -- 3 validari DUKIntegrator offline pe scenariul grupului orfan (`scutit_bug`, `scutit_corect`, - plus `control_stricat` ca martor negativ, sectiunea 3.3) — grupul orfan **nu** e respins -- identitatea SHA256 a XML-ului EUR de productie fata de fisierul chiar trimis la ANAF, si citirea - celor doua zip-uri (`TRIMISE`/`ERORI`) care arata ca respingerea gasita e a unei incarcari - anterioare, pentru alte reguli (sectiunea 4.1) - -**Dedus, NEverificat prin rulare** -- calculul numeric din scenariul 4.4 (aritmetica pe formulele citite, nu o factura rulata efectiv - prin program) -- riscul `agettipcota(1)` fara garda pe `_Tally` (`xmlefactura.prg:782-786`) — n-am reusit sa-l - provoc si nu-l afirm ca defect -- ce se intampla in nota contabila / jurnalul de TVA cu `valdiscounttva` — am dedus din faptul ca e - acelasi camp, n-am urmarit consumatorii -- scenariul "factura scutita + discount de document" **end-to-end prin program** (sectiunea 3.2): - gruparea separata e stabilita din cod + semantica `IIF`/NULL masurata, nu dintr-o factura reala - rulata prin program — in baza de dev nu exista intersectia (sectiunea 4.2) -- ordinea randurilor in `C_TVA_FACTURA` dupa `GROUP BY` (deci daca `agettipcota(1)` alege `E` sau - `Z` in cazul grupului orfan) — nu e garantata de VFP si nu a fost fixata experimental - -**Neacoperit — si stiut ca neacoperit** -- daca `VANZARI.TOTAL_TVA` (Oracle, sectiunea 1.5) si TVA-ul din XML sunt confruntate undeva si care - primeaza — am constatat doar ca ambele exista si folosesc aceeasi regula -- daca un discount de document **negativ** e posibil din UI (ar inversa `MAX`-ul din Oracle, 1.5) -- calea in **valuta** (`crsfacturafinalaval` / `prelucreaza_factura_valuta`, - `ofacturare_comun.prg:1540+`) — am presupus simetrie cu cea in lei pe baza campurilor `v*` - paralele de la `:1894-1895`, n-am parcurs-o linie cu linie -- daca `Max(proc_tvav)` e cota corecta si pentru **proforme** in vreun sens special -- comportamentul validatorului ANAF **curent** (cel local e din 2022, atat pentru cote mixte cat si - pentru grupul orfan) - ---- - -## Intrebari ramase pentru Marius - -1. **Repartizarea proportionala se aplica retroactiv la relistare?** O factura veche relistata sau - retrimisa in eFactura ar genera alt XML decat cel trimis initial. Se accepta, sau noul - comportament se leaga de o data / de un flag? -2. **Pe factura tiparita**: e in regula sa apara N randuri "Discount X % Factura" (unul pe cota), sau - vrei un singur rand vizibil si spargerea doar in XML / nota contabila? -3. **Rotunjirea**: pe cine cade diferenta de rotunjire cand discountul nu se imparte exact - (ex. 100 lei pe trei cote)? Propunerea mea: pe cota cu baza cea mai mare. -4. **Discountul care depaseste baza unei cote** (cazul din 4.4, factura majoritar scutita): dupa - repartizarea proportionala problema dispare de la sine — confirmi ca nu mai e nevoie de nicio - garda separata? -5. **Campul de motiv**: il vrei per document (un singur text), sau e suficient sa ramana constanta - `"Discount"` si sa nu adaugam UI? -6. **`discount_evidentiat`**: mai e folosit in productie? Schimba semnificativ ce se tipareste - (sectiunea 1.4) si e a doua sursa de pseudo-linii "Discount" in XML. -7. **Cele doua implementari ale regulii** (VFP + Oracle, sectiunea 1.5): se schimba amandoua in - aceeasi livrare, sau Oracle ramane pe regula veche o vreme? Daca raman desincronizate, - `VANZARI.TOTAL_TVA` si TVA-ul din eFactura vor diferi pe facturile cu cote mixte si discount. -8. **Discount de document negativ** (majorare): e posibil din UI? Daca da, `MAX`-ul din Oracle alege - cota cea mai **mica**, iar `Max(proc_tvav)` din VFP tot pe cea mai mare — adica cele doua - implementari ar diverge deja azi, inainte de orice modificare. - diff --git a/docs/cercetare/discount_document_cota_tva_b.md b/docs/cercetare/discount_document_cota_tva_b.md deleted file mode 100644 index 10dece3..0000000 --- a/docs/cercetare/discount_document_cota_tva_b.md +++ /dev/null @@ -1,189 +0,0 @@ -# Supliment: gruparea pseudo-liniei de discount in `C_TVA_FACTURA` - -Completare la `discount_document_cota_tva.md`, scrisa de al doilea agent pe aceeasi intrebare. -**Nu repeta** raportul principal — acopera exact punctul pe care acela il lasa deschis la finalul -sectiunii 3 („ipoteza gruparii separate — nu am atins-o deloc, o verifica alt agent"), plus doua -verificari prin rulare pe care raportul principal nu le are. - -Cercetare read-only: niciun fisier de cod atins, zero write-back, pe Oracle numai `SELECT`, niciun -apel catre ANAF (validarea s-a facut **offline**, cu DUKIntegrator din `D:\ROA\COMUNROA\dist_efactura`). - -## Verdict in 5 randuri - -Ipoteza pe care o ridicasem — ca pseudo-linia de discount de document, avand `id_jtva_coloana = 0`, -iese din `LEFT JOIN` cu `coloana_jv` NULL si formeaza propriul grup in `C_TVA_FACTURA`, cu -`TaxableAmount` negativ — este **infirmata pentru factura obisnuita** si **confirmata pentru factura -scutita / cu taxare inversa / intracomunitara**. In al doilea caz XML-ul chiar iese cu doua -`TaxSubtotal`-uri pe cota 0, unul cu baza negativa si categoria `Z`. **Dar validatorul nu-l -respinge** — l-am rulat. Deci: comportament gresit ca modelare, nu defect care blocheaza factura. - ---- - -## 1. Ce am masurat, nu dedus: `IIF()` peste NULL in VFP - -Toata ipoteza atarna de o singura intrebare: ce intoarce `Iif(Left(.Null.,2) = 'CE', 1, 0)`. Daca ar -intoarce `.NULL.`, cheia de grupare a pseudo-liniei ar diferi de a liniilor reale si gruparea s-ar -rupe pe **orice** factura. - -Am rulat o sonda headless (`vfp9.exe -A -T`) care reproduce exact `LEFT JOIN`-ul si `GROUP BY`-ul din -`xmlefactura.prg:226-248`, cu o pseudo-linie de discount pe `id_jtva_coloana = 0`: - -``` -1) IIF(.NULL.="CE",1,0) -> tip=N isnull=.F. -2) IIF(LEFT(.NULL.,2)="CE",1,0) -> tip=N isnull=.F. -3) IIF(INLIST(.NULL.,...),1,0) -> tip=N isnull=.F. - linie=ARTICOL A | coloana_jv nul=.F. | intracom=0 | scutit=0 - linie=ARTICOL B | coloana_jv nul=.F. | intracom=0 | scutit=0 - linie=Discount 10.00 % Factura | coloana_jv nul=.T. | intracom=0 | scutit=0 -4) grupuri in C_TVA_FACTURA = 1 - grup: cota=21 baza=1350 tva=283.50 (1500 - 150, corect) -``` - -**`IIF()` intoarce ramura falsa, nu `.NULL.`**, chiar daca conditia e nula. Deci pe o factura -interna obisnuita pseudo-linia primeste `intracomunitar=0, taxare_inversa=0, scutit=0` — exact ca -liniile reale — si **se contopeste in grupul cotei maxime**. Sectiunile 3 si 4 din raportul principal -raman valabile. - -Sonda: `...\scratchpad\probe_null.prg`. Nu depinde de baza de date si se poate reface oricand. - -## 2. Unde ipoteza se confirma totusi: facturile scutite / taxare inversa / intracomunitare - -Egalitatea de mai sus tine doar cat timp **si liniile reale** au `scutit=0` si `expltva` gol. Pe o -factura scutita nu e asa: - -| camp din cheia `Group By` (`xmlefactura.prg:246`) | liniile reale (scutite) | pseudo-linia de discount de document | -|---|---|---| -| `id_jtva_coloana` | id real (ex. 11) | **0** — `prelucreaza_facturacrs` nu-l seteaza niciodata pe randul `ZZZZ` (`ofacturare_comun.prg:1891-1896`) | -| `coloana_jv` (din `LEFT JOIN`) | `'WRSCDD'` | `.NULL.` — `cJTVAVanzariTemp` e incarcat cu `where afisat > 0 and id_jtva_coloana > 0` (`updateserver.prg:578`), deci **nu exista rand cu id 0** | -| `scutit` | **1** | **0** | -| `expltva` | `'Scutit cu drept de deducere'` | **gol** | - -Ultimul rand merita explicat, pentru ca e contraintuitiv: `completeaza_explicatie_tva` -(`ofacturare_comun.prg:2077-2137`) chiar **include** pseudo-linia in lista de id-uri cautate — scanul -de la `:2094` e `For proc_tva = 1 And Nvl(id_jtva_coloana,-1) <> -1`, iar pseudo-linia are -`proc_tva = 1` (cota 0) si `id_jtva_coloana = 0`, deci trece filtrul. Dar interogarea de la `:2108` -e `where id_jtva_coloana in (...)` pe `jtva_coloane`, unde id-ul 0 nu exista, asa ca `Replace expltva -... For NVL(id_jtva_coloana,0) = m.lnIdJtva` (`:2129`) nu o atinge niciodata. Ramane goala. - -Doua campuri diferite din cinci => **doua grupuri**. XML-ul iese cu: - -```xml - - ... Discount - 539.82 - Z0... - -0.00 - -539.82 - 0.00 - Z0... - 4410.00 - 0.00 - E0 - Scutit cu drept de deducere... - -``` - -Categoria `Z` a grupului orfan vine din `GetTipTaxa` ramura cu ramura (`xmlefactura.prg:1116-1140`): -`intracomunitar=0` sare peste `'K'`, `taxare_inversa=0` sare peste `'AE'`, `scutit=0` sare peste -`'E'`, si cade pe `Case m.lcTipFactura = 'F' And m.lnProcTva = 0` -> **`'Z'`** (`:1128-1129`). - -In plus, cautarea categoriei pentru `AllowanceCharge` (`xmlefactura.prg:782-786`) devine ambigua: -`Select tip From C_TVA_FACTURA Where proc_tva = 0 Into Array agettipcota` gaseste acum **doua** -randuri (`E` si `Z`), iar codul ia neconditionat `agettipcota(1)` — primul in ordinea cursorului, nu -cel corect prin vreun criteriu. - -**Nu am reprodus scenariul end-to-end prin program** (ar fi cerut o factura scutita cu discount de -document, si in baza de dev nu exista — vezi punctul 4). Il stabilesc din citirea codului plus -semantica `IIF`/NULL masurata la punctul 1. - -## 3. Validare offline: XML-ul gresit **nu** e respins - -Am construit XML-urile si le-am trecut prin DUKIntegrator -(`java -jar DUKIntegrator.jar -c -v FACT1 `, exact apelul din -`xmlefactura.prg:1192`), pornind de la o factura reala acceptata de ANAF ca schelet: - -| scenariu | fisier | rezultat | -|---|---|---| -| scutit + discount de document, **cu grupul orfan** (ce produce codul azi) | `scutit_bug.xml` | **ok** | -| scutit + discount, un singur `TaxSubtotal` net (varianta corecta) | `scutit_corect.xml` | **ok** | -| cote mixte 21%+11%, tot discountul pe 21% (ce produce codul azi) | `mixt_azi.xml` | **ok** | -| cote mixte 21%+11%, discount repartizat proportional | `mixt_proportional.xml` | **ok** | -| discount mai mare decat baza cotei maxime -> `TaxableAmount = -400.00` si `TaxAmount = -84.00` pe grupul 21% | `mixt_baza_negativa.xml` | **ok** | -| **control negativ**: `TaxExclusiveAmount` stricat intentionat | `control_stricat.xml` | **eroare BR-CO-13 + BR-CO-15** | - -Controlul negativ conteaza: fara el, cele cinci „ok" n-ar dovedi nimic. Validatorul chiar valideaza. - -**De ce trec**: regulile EN16931 pe categorii (BR-S-08, BR-E-08, BR-Z-08) cer ca baza declarata pe o -categorie sa fie egala cu *suma liniilor din acea categorie minus alocarile din aceeasi categorie*. -Grupul orfan respecta asta trivial (`0 - 539.82 = -539.82`), iar grupul `E` la fel (`4410.00 - 0`). -Aritmetica inchide; **modelarea fiscala e cea gresita**, si asta niciun schematron nu prinde. - -Avertisment onest: validatorul local e din 2022 (`ro16931-ubl-1.0.8`, jar-ul din ianuarie 2022). -Validatorul online curent al ANAF poate fi mai strict. **Nu l-am apelat** — interdictie explicita. - -## 4. De ce nu am putut proba pe date reale - -- In baza accesibila (`MARIUSM_AUTO@ROA_CENTRAL`) exista **3** facturi cu discount de document, toate - cu **o singura cota** si toate anterioare eFacturii (2008, 2014 x2), niciuna cu `efactura = 1`. - Separat exista 34 de facturi cu cote mixte, dar **niciuna** cu discount de document. Deci - intersectia care ne intereseaza e goala. - **Asta nu inseamna ca nu apare in productie** — baza de dev are 712 facturi in total. -- Cele 4 XML-uri de productie cu `AllowanceCharge` de document pe care le-am examinat sunt, dupa - toate semnele, **discounturi pe linie** cu `discount_evidentiat = 1`, nu discounturi de document: - cel de la `2024_07\...CIA_TRADE_POSSIBLE` are cote 9% si 19%, iar alocarea unica e pe **9%** — - adica pe cota **minima**, ceea ce regula `Max(proc_tvav)` nu poate produce; iar - cel de la `2024_01\...BEST_POWER_DANCE` are **doua** alocari (9% si 19%), ceea ce discountul de - document, fiind o singura pseudo-linie, nu poate produce nici el. - Nu am gasit niciun XML de productie care sa fie sigur discount de **document**. -- Firmele din `D:\ROA\Efactura` (VENDING_MASTER, CLEVER_MOTORS, ...) nu au scheme in baza accesibila - (`select ... from all_users` — zero potriviri), deci nu am putut confrunta XML-ul cu antetul lui. - -## 5. Un lucru dovedit pe un fisier real, nu dedus - -Pe `D:\ROA\Efactura\2024_02\VENDING_MASTER_SRL\efactura_20240216_2885_IT_BUL_PLAST_LTD.xml`: - -``` -BT-5 DocumentCurrencyCode = EUR - ... 539.82 -539.82 -``` - -Aceeasi suma apare o data ca `RON` si o data ca `EUR` — `currencyID` e hardcodat `"RON"` la -`xmlefactura.prg:778`, in timp ce restul documentului foloseste `mmoneda`. Doua precizari care -schimba interpretarea: - -- fisierul de pe disc este **identic pe SHA256** cu cel din - `...\TRIMISE\4172939206_3250292036.zip`, deci e exact ce a plecat la ANAF; -- si a fost **acceptat**. Respingerea din `...\ERORI\4172675286_3249814520.zip` e a unei incarcari - *anterioare* si e pentru alte doua reguli (`BR-CO-15` si `BR-CL-04` — cod de moneda invalid, - probabil `EURO` in loc de `EUR`, ceea ce explica linia de corectie `xmlefactura.prg:267`). - -Deci `currencyID="RON"` pe factura in valuta e o **neconformitate reala si activa in cod azi**, dar -nu am dovada ca ar cauza o respingere. - -## 6. Ce inseamna pentru #13 - -Repartizarea proportionala pe cote — recomandarea din raportul principal — **rezolva si problema de -aici**, fara nicio garda in plus: daca discountul se sparge pe cotele care exista efectiv pe factura, -fiecare bucata mosteneste `id_jtva_coloana` si `expltva` ale grupului ei, grupul orfan dispare, iar -`agettipcota(1)` nu mai poate fi ambiguu. Subscriu, cu doua completari: - -1. **Pseudo-liniile de discount trebuie sa primeasca `id_jtva_coloana` al grupului pe care il reduc**, - nu doar cota. Cota singura nu ajunge — cheia de grupare are cinci campuri, iar `expltva` se - completeaza tot prin `id_jtva_coloana`. O implementare care seteaza doar `proc_tva` ar lasa - grupul orfan exact acolo unde e azi. -2. **Regula traieste in doua locuri** — VFP (`Calculate Max(proc_tvav)`) si PL/SQL - (`MAX(ROUND(discount * (proc_tvav - 1), ...))` in `recalculeaza_totaluri_vanzari`). Daca se - schimba doar una, `VANZARI.TOTAL_TVA` si TVA-ul din XML vor diferi pe facturile cu cote mixte. - -## 7. Ce ramane deschis - -- Scenariul de la punctul 2 **nu e reprodus prin program**, ci dedus din cod + semantica `IIF` - masurata. O confirmare ar cere o factura scutita cu discount de document, introdusa manual. -- Ordinea randurilor in `C_TVA_FACTURA` dupa `GROUP BY` (deci ce alege `agettipcota(1)` intre `E` si - `Z`) nu e garantata de VFP si nu am fixat-o experimental. Am presupus `scutit=0` inaintea lui - `scutit=1`; daca e invers, categoria alocarii iese `E` in loc de `Z` — tot gresita, dar altfel. -- Validatorul ANAF **online** curent nu a fost testat (interdictie). Tot ce spun despre „nu e - respins" se refera la DUKIntegrator 2022 de pe disc. -- Nu am verificat calea in **valuta** (`prelucreaza_factura_valuta`) linie cu linie. diff --git a/docs/cercetare/discount_in_rapoarte_si_efactura.md b/docs/cercetare/discount_in_rapoarte_si_efactura.md deleted file mode 100644 index dd63570..0000000 --- a/docs/cercetare/discount_in_rapoarte_si_efactura.md +++ /dev/null @@ -1,216 +0,0 @@ -# Discount pe linie (DISCOUNT_UNITAR) in afara formularului de facturare — rapoarte .frx, eFactura, SAF-T - -Cercetare read-only pentru S4c (plan_13). Nu s-a modificat niciun fisier, nu s-a rulat git_sync. - -## Verdict (5-10 randuri) - -Niciun raport tiparit de factura (`factura.fr2`, `facturatip*.fr2`, `factura_val*.fr2`, -`invoice*.fr2`, `proforma*.fr2`) nu afiseaza discountul pe linie ca coloana separata — nici ca -valoare, nici ca procent. Rapoartele tiparesc `pretftva` (pret unitar) si `valftva` (valoare linie) -care sunt **deja nete de discount** (calculate ca `pretftva-discountftva` / `Sum(valdiminuatftva)` -in cursorul intermediar `crsfacttemp`, construit de `prelucreaza_factura`/`creeaza_crsfacttemp` din -`ofacturare_comun.prg`). eFactura (`xmlefactura.prg`) foloseste **exact acelasi cursor** — -comentariul din cod chiar spune asta explicit — deci `LineExtensionAmount`/`PriceAmount` trimise la -ANAF sunt aceleasi valori nete, cu optiunea (dezactivata implicit, din flag-uri globale) de a -adauga si un `cac:AllowanceCharge` informativ pe linie. SAF-T (D406): **nu exista niciun generator -D406/SAF-T in ROAFACTURARE** — exista doar tabele de nomenclator cu prefix `saft_` (coduri TVA/plata) -folosite pe partea de achizitii, fara legatura cu `DISCOUNT_UNITAR`. - -**Raspuns la intrebarea care conteaza**: daca discountul pe linie devine editabil direct in grid, -**nu se strica nimic in codul rapoartelor/eFacturii** — ele citesc oricum campurile finale -(`valdiminuatftva`/`discountftva`/`discount_unitar`) din cursor, indiferent cum au fost populate -(dialog separat vs. editare in grid). **Riscul real e in alta parte**: exista deja azi o cale prin -care discountul e editabil direct in grid (`vdiscountftva`, pe factura in valuta) care **nu -declanseaza recalcularea** lui `valdiminuatftva`/`valdiminuatctva` — vezi `docs\cercetare\discount_verificare2.md` -punctul 3, ultimul paragraf. Daca S4c extinde editarea in grid la toate coloanele de discount fara -sa cablaje si recalculul, rapoartele si eFactura vor tipari/trimite **valori vechi** (stale), pentru -ca ambele citesc `valdiminuatftva`, nu `discount_unitar` direct. - -## 1. Rapoartele .frx/.fr2 tiparite - -**Cautare**: `Grep 'DISCOUNT'` si apoi `Grep 'disc'` (case-insensitive) in toate `.fr2` din -`COMUN\Rapoarte` cu glob `*factur*.fr2`, `*proforma*.fr2`, `*invoice*.fr2` — **zero potriviri** in -toate cele (factura.fr2, facturatip.fr2, facturatip_a5.fr2, facturatip_cuchit.fr2, factura_a5.fr2, -factura_chit.fr2, factura_val.fr2, factura_val_a5.fr2, invoice.fr2, invoice_a5.fr2, proforma.fr2, -proforma_orig.fr2, proforma_val.fr2, proforma_val_orig.fr2). Singurele 2 fisiere `.fr2` din toata -`COMUN\Rapoarte` care contin "DISCOUNT" sunt `rap_nir_materiale.fr2` si `rap_nir_marfuri.fr2` — -rapoarte de NIR (receptie), nu facturi emise. - -**Ce se tipareste per linie** (confirmat in `factura.fr2`): -``` -1002: -1014: -1026: -1062: -- procent TVA, nu discount -``` -Deci: cantitate, pret unitar, valoare linie, procent TVA. Nici discount valoric, nici procent -discount. - -**De unde vin `pretftva`/`valftva` — si de ce sunt deja nete**: cursorul de tiparire -(`crsfacttemp`) e construit de `Procedure prelucreaza_factura` (`COMUN\programe\ofacturare_comun.prg:1055-1059`), -apelata din `COMUN\programe\ofacturare.prg:1887`: -``` -prelucreaza_factura([crsfactura], [crsfacturaset], [crsfacturafinala], poDate.discount_evidentiat, poDate.in_valuta, lnPretListAviz, poDate.nListareDetaliata) -``` -In interior, `Do Case` pe `tnDiscountEvidentiat`/`lnPretListAviz` (`ofacturare_comun.prg:1156-1248`). -Cazul comun, `tnDiscountEvidentiat = 0` (checkbox-ul "Se pune in evidenta discount-ul..." NEBIFAT — -comportamentul implicit): -``` -1182: Sum(cantitate) As cantitate,pretftva-discountftva As pretftva,; -... -1186: Sum(valdiminuatftva) As valftva,Sum(valdiminuattva) As valtva,; -``` -adica pretul tiparit = pret brut minus discountul pe unitate, iar valoarea tiparita = suma -`valdiminuatftva` (deja calculata la adaugarea articolului ca `pretftva - discount_unitar`, cf. -`ofacturare.vc2:13126`, deja documentat in `discount_pe_articol.md` sectiunea 3). Simetric pentru -varianta cu TVA la `:1226-1230`. - -**Cazul `tnDiscountEvidentiat = 1`** (checkbox bifat — discountul "se pune in evidenta"): -``` -1165: pretftva,Nvl(codbare,... -- pretftva RAMANE brut, nu se scade discountftva -1166-1167: Sum(valftva) As valftvai,... Sum(valftva) As valftva,Sum(valtva) As valtva, -1168: discountftva,Sum(valdiscountftva) As valdiscountftva,Sum(valdiscounttva) As valdiscounttva, -``` -Aici `pretftva`/`valftva` tiparite raman **brute** (neta de discount), iar `discountftva`/ -`valdiscountftva`/`valdiscounttva` sunt calculate in cursor **dar nu sunt tiparite de niciun .frx** -(confirmat, zero hit pe "disc" in .fr2). Practic, cand discountul e "pus in evidenta", pretul si -valoarea linie tiparite pe factura NU reflecta discountul per linie — discountul apare doar ca linie -separata de document (randul-sentinela `denumire = Replicate('Z',20)`, populat din -`Thisform.nbazafdiscount`/`nbazafdiscountval` la `ofacturare.vc2:14534,14591,18532,18602` si -`ofacturare_comun.prg:1891`) — asta e insa discountul **de document** (`VANZARI.DISCOUNT`), nu -`DISCOUNT_UNITAR` pe linie. Nu am gasit nicio dovada ca acest mod (`discount_evidentiat=1`) ar fi -folosit uzual — e o optiune existenta, documentata deja in `discount_pe_articol.md` punctul 3 ca -schimband "doar mecanismul aritmetic", dar aici se vede consecinta suplimentara: **si ce se -tipareste**. - -**Fara impartiri la pret zero pe randul de discount**: unde raportul calculeaza procent -(`proc_disc`), sursa e protejata explicit: -``` -1184-1185 (ofacturare_comun.prg): Iif(cu_tva=0,Iif(pretftva<>0,Round(discountftva/pretftva*100,2),0),; - Iif(pretctva<>0,Round(discountctva/pretctva*100,2),0)) -``` -deci nu exista risc de impartire la zero — dar, ca observat mai sus, `proc_disc` nu e tiparit -nicaieri in .frx-urile de factura verificate (e calculat doar pentru consum intern/eFactura). - -## 2. eFactura (ANAF/UBL) — `xmlefactura.prg` - -**Concluzie**: eFactura foloseste **acelasi cursor de linii ca cel de la tiparire**, explicit -documentat in cod: -``` -oexport.prg:1590: * tcCursorLiniiFactura = numele cursorului cu liniile facturii, asa cum arata la listare -``` -Lantul: `ofacturare.prg:2126` -> `goExport.export2xml_efactura(poDate, m.lcCursoreFactura, ...)` cu -`lcCursoreFactura` = `lcCursorFacturaTemp` (= `'crsFacturaFinala'`, `ofacturare.prg:1892`, sau -`'crsfacturafinalaval'` pentru valuta) -> `xmlefactura.prg:231`: -``` -Select a.*, ... From (m.tcCursorLiniiFactura) a Left Join cJTVAVanzariTemp b ... Into Cursor C_IES_FORM Readwrite -``` -Deci `C_IES_FORM` (sursa liniilor XML) e construit direct din cursorul de tiparire — aceleasi -`pretftva`/`pretftvai`/`valftva`/`valftvai`/`proc_disc` descrise la punctul 1, cu aceeasi dependenta -de `poDate.discount_evidentiat`. - -**Ce se trimite pe linie**: -- `cbc:LineExtensionAmount` (BT-131) = `C_IES_FORM.valftva` — `xmlefactura.prg:935-937`. -- `cbc:PriceAmount` = `C_IES_FORM.pretftva` — `xmlefactura.prg:1043-1046`. -- Optional (doar daca flag global `gnEFACTURA_XML_DISC_PLISTA_LINIE = 1` SI `pretftvai > pretftva`): - un `cac:AllowanceCharge` la nivel de linie cu `Amount = valftvai-valftva`, - `BaseAmount = valftvai`, `MultiplierFactorNumeric = proc_disc` — `xmlefactura.prg:952-971`. -- Optional (flag separat `gnEFACTURA_XML_DISC_PLISTA_ART = 1`): un `cac:AllowanceCharge` la nivel de - `cac:Price` cu discountul unitar (`ABS(pretftvai-pretftva)`) — `xmlefactura.prg:1054-1067`. - -Ambele flag-uri sunt verificate cu `TYPE(...) = 'N' AND ... = 1` — daca variabila globala nu exista -sau e 0 (implicit), **nu se trimite niciun `AllowanceCharge` pe linie**; discountul e vizibil pentru -ANAF doar implicit, prin faptul ca `PriceAmount * InvoicedQuantity = LineExtensionAmount` (adica -pretul e deja net). Nu am gasit unde sunt setate global aceste doua variabile (`gnEFACTURA_XML_DISC_PLISTA_LINIE`/`_ART`) — -posibil optiuni de firma necautate in acest fisier; nu afecteaza concluzia principala. - -**Discount de document, separat**: exista o eticheta euristica in `C_IES_FORM` -(`Iif('DISCOUNT' $ Upper(a.denumire) And a.pretftva < 0, 1, 0) As discount`, `xmlefactura.prg:230`) -care detecteaza randuri-pseudo-articol de discount/taxa (denumire contine "DISCOUNT", pret negativ) -si le exclude din liniile normale (`Scan For discount = 0`, `:901`), agregandu-le separat intr-un -`cac:AllowanceCharge` la nivel de document (`:756-793`, motiv "Discount", cod 95). Acesta e -discountul de document, nu `DISCOUNT_UNITAR` pe linie — mecanism diferit de randul-sentinela -`Replicate('Z',20)` folosit la tiparire (punctul 1). - -**Nicio validare care ar respinge discount > pret**: n-am gasit nicio verificare explicita in -`xmlefactura.prg` care sa blocheze o linie cu `discount_unitar` mai mare ca pretul (ar rezulta -`pretftva` negativ pe linie individuala; codul are doar o corectie generala pentru -`pretftva < 0` la nivel de linie completa, `xmlefactura.prg:236-239`, care inverseaza semnul -cantitate/pret — nu specifica pentru discount). - -## 3. SAF-T (D406) - -**Concluzie: nu exista implementare SAF-T/D406 in ROAFACTURARE.** Cautat explicit: -- `Grep 'SAF-T|SAFT|D406|declaratie406|GenereazaSAFT|GenereazaD406'` in tot `COMUN\programe` si - `COMUN` — nicio potrivire pe un generator de fisier D406/SAF-T XML. -- `Glob '**/*saft*'` pe tot proiectul si pe `COMUN` — zero fisiere cu "saft" in nume. -- Singurele aparitii reale (nu fals-pozitive de tip "safe") sunt: - - `oinit_optiuni.prg:523-524`: `gl406 = (TYPE('gnD406') = 'N' and m.gnD406 = 1)` — un flag "firma - are activata SAFT 406", comentat "Firma are activata SAFT 406". - - `oproceduri_comune.prg:889-896,6083,6151`, `ointroduceri.prg:1597`: `saft_taxtable` si - `saft_mecanisme_plati` — tabele de nomenclator (coduri de TVA/mecanism de plata compatibile - SAF-T) folosite pe **partea de achizitii** (limitare deducere TVA la introducerea facturilor de - achizitie), nu pe vanzari/facturare. - - `oproceduri_comune.prg:6066-6067,6137-6138,6195`: comentarii care mentioneaza "Taxa SAFT tip" - ca denumire pentru codurile de TVA, tot pe achizitii. -- Niciuna din aceste aparitii nu are legatura cu `DISCOUNT`/`DISCOUNT_UNITAR` — cautarea `DISCOUNT` - in fisierele care contin "saft" (`oproceduri_comune.prg`, `ointroduceri.prg`, `pmenu.prg`, - `oinit_optiuni.prg`) nu a gasit nicio intersectie intre cele doua seturi de rezultate. - -**Interpretare**: `gl406` pare sa activeze doar validari/coduri suplimentare pentru conformitate -SAF-T pe partea de achizitii (poate pentru ca alt produs din suita, ROACONT, genereaza efectiv -D406 din datele contabile) — ROAFACTURARE nu produce singur un fisier D406. - -## 4. Alte consumatori care ar presupune discount uniform pe document - -Nu am gasit vreun raport/export care sa presupuna un discount unic pe tot documentul in sensul de -"aceeasi valoare/procent pe toate liniile" — mecanismul de discount de document -(`VANZARI.DISCOUNT`, randul `Replicate('Z',20)`) e tratat ca **valoare agregata separata**, adaugata -ca linie proprie (in tiparire) sau ca `AllowanceCharge` de document (in eFactura), nu redistribuita -implicit pe liniile existente — cu o exceptie: `xmlefactura.prg` are logica de **distribuire** -explicita a discountului/taxelor de document pe articole cand bifa `chkDistribuieDiscount`/ -`llDistribuieDiscountTaxe` e activa (`xmlefactura.prg:12189-12320`, `:12534-12790`) — asta insa -opereaza pe discountul de document, nu presupune ca discountul de linie (`DISCOUNT_UNITAR`) e -uniform; dimpotriva, distribuie proportional cu valoarea fiecarei linii, deci suporta discount -diferit per linie din start. - -## Ce ar strica S4c, concret - -**Nu se strica codul rapoartelor sau al eFacturii** — ambele citesc valori finale din cursor -(`valdiminuatftva`, `discountftva`, `pretftva` deja net), indiferent de sursa UI a discountului. - -**Se poate strica invariantul pe care se bazeaza**, daca editarea in grid nu recalculeaza: -- **Dovada ca gaura exista deja azi**: pe factura in valuta, coloana `vdiscountftva` e editabila - direct in grid (`ofacturare.vc2:12340-12345`, fara `ReadOnly`) dar **fara niciun handler - `Valid`/`InteractiveChange`** care sa recalculeze `vvaldiminuatftva`/totalurile — confirmat cautat - explicit in `docs\cercetare\discount_verificare2.md` punctul 3, ultimul paragraf ("editarea directa - in grid NU declanseaza recalcularea automata a `vvaldiminuatftva`/totaluri, doar modifica valoarea - bruta in `crsfactura`"). -- **De ce conteaza pentru rapoarte/eFactura**: `valdiminuatftva`/`valdiminuatctva` (nu - `discount_unitar` brut) sunt campurile citite de `prelucreaza_factura`/`creeaza_crsfacttemp` - pentru `valftva` tiparit si trimis la ANAF (punctele 1 si 2 de mai sus). Daca operatorul modifica - discountul direct in grid si acel camp nu se recalculeaza, factura tiparita si XML-ul ANAF vor - arata **valoarea veche** (dinainte de editare) — o discrepanta reala intre ce vede operatorul in - grid si ce se tipareste/trimite. -- **Concluzie pentru plan**: daca S4c extinde editarea de discount la toate coloanele din grid - (inclusiv `discountctva`, azi read-only pe lei), trebuie cablat un recalcul echivalent cu - `frm_articol_factura.do_calculeaza_discount` (`ofacturare.vc2:1874-1976`) pe evenimentul de editare - din grid — altfel riscul de mai sus (deja prezent azi doar pe valuta) se extinde la toate - facturile in lei. - -## Ramas de verificat - -- Valorile implicite ale flag-urilor globale `gnEFACTURA_XML_DISC_PLISTA_LINIE` si - `gnEFACTURA_XML_DISC_PLISTA_ART` (unde sunt setate ca optiune de firma) — nu am cautat sursa lor, - doar am confirmat ca cu `TYPE(...) <> 'N'` (nedefinite) comportamentul e "fara AllowanceCharge pe - linie". -- Nu am verificat pe date reale (Oracle/XML generat) un caz cu `discount_evidentiat=1` si - `DISCOUNT_UNITAR` populat simultan, ca sa confirm empiric (nu doar din citirea codului) ca factura - tiparita arata pretul brut. -- Nu am urmarit `crsfacturafinalaval` (varianta valuta a cursorului final) linie cu linie — am - presupus ca urmeaza acelasi patron ca `crsfacturafinala`/`crsfacttemp` (cursorul in lei), pe baza - simetriei campurilor `vpretftva`/`vvalftva`/`vdiscountftva` gasite deja documentate in - `discount_verificare2.md` si `discount_pe_articol.md`; n-am reverificat separat sursa SQL pentru - varianta valuta. -- Nu am identificat un al doilea produs (ROACONT?) care sa genereze efectiv D406 din datele scrise - de ROAFACTURARE — presupunerea din sectiunea 3 e speculativa, nu confirmata cu cod. diff --git a/docs/cercetare/factura_retur_document.md b/docs/cercetare/factura_retur_document.md deleted file mode 100644 index 1d5003b..0000000 --- a/docs/cercetare/factura_retur_document.md +++ /dev/null @@ -1,185 +0,0 @@ -# Cercetare: factura de retur ca document de sine statator (tip 8/9) + aviz retur (24) - -Corectie fata de `retur_si_lista_preturi.md`: acea cercetare a documentat corect `But_retur` -(retur de articole *in interiorul* unei facturi normale, tip 1/5/7/10 — selectie **per articol**). -Aici e documentat mecanismul **separat**: factura de retur ca document propriu (tip 8 = retur lei, -tip 9 = retur valuta), unde utilizatorul alege **facturile sursa la nivel de document**, iar linia -de articole se populeaza integral din acele facturi. - -## 1. Punctul de intrare si cursorul Oracle - -Tile-ul de pe ecranul principal de facturare, `Page2.Cw1` (`COMUN\clase\ofundal_facturare.vc2:882-884`): -``` -PROCEDURE Page2.Cw1.do_actiune - DO facturare_lista_de_preturi IN oproceduri_facturare.prg -ENDPROC -``` -`facturare_lista_de_preturi` (`COMUN\programe\oproceduri_facturare.prg:114-116`) = `Do politica.mpr`, -care ruleaza meniul shortcut generat din `Meniuri\politica.mn2:14-15,45-46`: -``` -DEFINE BAR 2 OF Shortcut PROMPT "\ cate un camp cu acelasi nume in cursorul VFP `crsarticole` (maparea VFP -exacta camp-cu-camp nu a fost trasata pana in `crsfactura`; nu era necesara pentru raspuns). - -## 4. Ce se poate face manual (`frm_facturare_articole`, `COMUN\clase\ofacturare.vc2:10968`) - -- **Stergere linie**: `do_sterge` (`:14608-14693`) nu e restrictionat pe tip; pentru retur - (`Case Inlist(poDate.tip,8,9,24)`, `:14658-14659`) cantitatea stearsa se reintoarce in cursorul - sursa (`Replace cantitate With cantitate - poArticol.cantitate For id_c = poArticol.id_c`) — deci - **da, se pot sterge linii aduse**, iar cantitatea redevine disponibila pentru re-adaugare. -- **Retur partial (modificare cantitate)**: coloana de cantitate din grila sursa isi schimba titlul - in "Cant. max. de returnat" pentru tip 8/9 (`:15236-15239`); validarea in `do_verifica_articol` - (`:14743-14754`, `llRetur = Inlist(poDate.tip,8,9,24)`) respinge doar cazul in care cantitatea - ceruta ar depasi maximul returnabil — utilizatorul poate introduce orice cantitate <= maxim, - deci **da, retur partial e posibil**. `do_modifica` (`:13746-13914`) trateaza explicit - `Inlist(poDate.tip,8,9,24)` la `:13788,13895` fara blocaj suplimentar. -- **Adaugare linie care NU e in facturile sursa**: `do_adauga_articol` (`:12813-13086`) preia - articolul mereu din cursorul sursa al gridului (`lcCursor = [crsarticole]`, apoi - `Select (lcCursor) / Scatter Name poArticol`, `:12843-12851`) — pentru tip 8/9 acest `crsarticole` - e chiar rezultatul `cursor_retur` de la punctul 3, deci contine **doar** liniile facturilor alese. - Nu exista pe acest formular o cale de a alege un articol din lista de preturi completa cand - `poDate.tip` e 8/9 (spre deosebire de contract/comanda unde `crsarticole` ramane lista de preturi - libera) — **nu se poate adauga o linie in afara facturilor sursa**, prin design-ul continutului - cursorului, nu printr-o validare explicita de interzicere. - -## 5. Aviz de retur (tip 24) - -Acelasi mecanism de baza: `Inlist(tnTip,8,9,24)` la `ofacturare.prg:306-307` (acelasi -`pack_facturare.cursor_retur`) si aceleasi ramuri `Inlist(poDate.tip,8,9,24)` in -`frm_facturare_articole` (`do_adauga_articol:12863`, `do_alege_stoc:13230`, `do_modifica:13788,13895`, -`do_verifica_articol:14743`, `do_sterge:14658`). Diferenta: la antet, `nIdTipDoc` = 6 (AVIZ, ramura -`Otherwise` la `ofacturare.prg:194-195`, pentru ca 24 nu e `<21` si nu e in `Inlist(45,48,49,51,52)`), -iar formularul de date e `frm_date_aviz` (`ofacturare.prg:225`, ramura `Otherwise`), nu -`frm_date_factura`. Punctul de intrare pentru tip 24: `emitere_aviz_clienti` cu `tnTip=7` -(`COMUN\programe\oproceduri_facturare.prg:221-222`, `factureaza(24) && Retur aviz`), apelat din -tile-ul `Page3.Cw1` -> `aviz_clienti.mpr` (neverificat detaliat, nu s-a insistat conform cerintei). - -## 6. Relatia cu `But_retur` - -Mecanisme **complet separate**, confirmate pe cod: -- **Vizibilitate**: `but_retur` e vizibil doar pentru tip 1/5/7/10 (`ofacturare.vc2:15127`, - `This.but_retur.Visible = .T.` in ramura `Case Inlist(poDate.tip,1,5,7,10)`) — pe un document - tip 8/9 acest buton nu exista deloc in UI (ramura lui la `Init` e alta, `:15236`). -- **Formular de alegere a facturii sursa**: `But_retur` foloseste `caut_facturi_multiple_client_articol` - (`oproceduri_facturare.prg:2124-2159`, filtrat suplimentar pe `b.id_articol = ?pnIdArticol` — - cautare per-articol, cu titlu simplu "Alegeti factura"), tip 8/9 document foloseste - `caut_facturi_multiple_client` (`:2091-2121`, fara filtru pe articol, multi-select, titlu - "Alegeti facturile ..."). Doua functii diferite, in acelasi fisier, cu semnaturi aproape identice - dar interogari SQL diferite. -- **Procedura Oracle de populare a liniilor**: documentul tip 8/9 foloseste - `pack_facturare.cursor_retur` la nivel de document intreg (populeaza `crsarticole` cu toate - liniile facturilor alese, punctul 3 de mai sus). `But_retur` nu apeleaza `cursor_retur`/ - `cursor_retur_document` deloc — foloseste `pack_facturare.cursor_gestiuni_articol_retur` - (`ofacturare.vc2:13230`, per articol individual, in `do_alege_stoc`) pentru a alege - gestiunea/seria/lotul de returnat pentru articolul deja selectat din lista de preturi a facturii - normale curente. -- **Punct comun**: niciunul direct. Singurul element comun e conventia de semn a cantitatii - (`Iif(Inlist(poDate.tip,8,9,24),(-1),1)`, folosita si in `do_alege_stoc` al `But_retur`, - `:13279-13309`, si pe formularul `frm_facturare_articole2`, `:17574-17585`) si textul UI - ("cant. max. de returnat"). Concluzie: sunt doua fluxuri de cod independente care ating aceeasi - clasa de formular (`frm_facturare_articole`) dar prin metode si proceduri Oracle diferite. diff --git a/docs/cercetare/garda_aviz_facturat.md b/docs/cercetare/garda_aviz_facturat.md deleted file mode 100644 index 569b65f..0000000 --- a/docs/cercetare/garda_aviz_facturat.md +++ /dev/null @@ -1,235 +0,0 @@ -# Garda "aviz deja facturat" — verificare pe cod - -Verificare pe cod, fara nicio modificare de fisier. Sursele citate: - -- export pachet: `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` - (mai jos: `EXPORT:`); -- corpul **desfasurat pe DB** (`MARIUSM_AUTO@ROA_CENTRAL`, `all_source`), citit ca sa se confirme ca - ce e in export e si ce ruleaza (mai jos: `DB PACK_FACTURARE:`). **Numerotarea difera**: - `sterge_factura` incepe la `EXPORT:5432`, dar la `DB PACK_FACTURARE:4192`. Nu exista offset - constant; textul insa e identic cuvant cu cuvant pe zona gardelor. - ---- - -## 1. Blocheaza garda de azi si un AVIZ care are deja factura generata din el? - -**DA.** Nu e nevoie de nicio garda noua pentru cazul asta — este exact ce testeaza a doua garda din -`sterge_factura`, prin `TIP IN (1, 2)`. - -`EXPORT:5464-5476` = `DB PACK_FACTURARE:4224-4236`: - -``` - -- ptr. un aviz normal: - -- verific daca exista facturi sau avize de retur pe avizul resp. - SELECT COUNT(*) - INTO V_NR_AVIZE_FACT - FROM VANZARI_CORESP - WHERE STERS = 0 - AND ID_VANZARE_AVIZ = V_ID_VANZARE - AND TIP IN (1, 2); - - IF V_NR_AVIZE_FACT > 0 THEN - RAISE_APPLICATION_ERROR(-20000, - 'Pe acest aviz s-au emis facturi / aviz de retur. Trebuie sa stergeti mai intai facturile / avizul de retur!'); - END IF; -``` - -Cheia e semantica lui `TIP`, citita din **singurul scriitor** al tabelei, ramura cu ramura -(`finalizeaza_factura`, `EXPORT:14818-14839`): - -| `VANZARI_CORESP.TIP` | scris cand | `ID_VANZARE_FACT` | `ID_VANZARE_AVIZ` | e retur? | -|---|---|---|---|---| -| **1** | `ntip = 4` — facturare din aviz (`EXPORT:14823-14826`) | factura | avizul-sursa | **NU** | -| 2 | `ntip = 24` — aviz de retur (`EXPORT:14828-14830`) | avizul de retur | avizul original | DA | -| 3 | `ntip in (8, 9)` — factura de retur (`EXPORT:14834-14836`) | factura de retur | factura originala | DA | - -Deci `TIP = 1` este **corespondenta aviz -> factura normala**, nu retur. Garda de la `EXPORT:5473` -prinde ambele cazuri; textul mesajului chiar o spune ("s-au emis **facturi** / aviz de retur"). -Formularea din brief ("garda de azi blocheaza documentul care are facturi/avize **de retur**") vine -dintr-o citire a comentariului `-- verific daca exista facturi sau avize de retur` in care "de retur" -se distribuie si peste "facturi"; codul zice altceva. - -Deci: la ora asta, **exact regula ceruta de decizia 50 este deja implementata pentru avize** -(document cu urmasi = blocat; capatul lantului = liber). - -### Ce NU citeste garda - -- **`VANZARI.FACTURAT` nu e citit de nicio garda.** Pe calea de stergere e doar **scris** - (`EXPORT:5504-5510` pentru `V_TIP = 24`, `EXPORT:5527-5533` pentru `V_TIP = 4` — resetare la 0 - cand dispare factura/avizul de retur), iar la facturare e **pus** de `marcheaza_facturat` - (`EXPORT:15381-15418`). Niciun `IF` / `RAISE` nu il interogheaza. Semnalul autoritar este - `VANZARI_CORESP`, `FACTURAT` e derivat redundant. -- **`ID_VANZARE_SURSA` / `ID_VANZARE_DEST` nu exista.** `VANZARI_CORESP` are exact 5 coloane - (interogare pe `all_tab_columns`): `ID_VANZARE_CORESP`, `ID_VANZARE_FACT`, `ID_VANZARE_AVIZ`, - `STERS`, `TIP`. Nici `VANZARI` nu are coloane `*_SURSA` / `*_DEST` (aceeasi interogare). - ---- - -## 2. Exista alte garzi pe calea de stergere / modificare? - -### 2a. Oracle — nu exista alta, dar garda e **atinsa pe ambele ramuri** de stergere - -Interogare pe `all_source` (owner curent): singurele obiecte care contin `VANZARI_CORESP` sunt -**`PACK_FACTURARE` (package body)** si **`TRG_VANZARI_CORESP_BEFOINS`**. Deci nu exista o a doua -garda ascunsa in alt pachet. - -**Triggerele nu contin garzi** (sursa citita integral din `all_source`): - -| trigger | tabela | eveniment | ce face | -|---|---|---|---| -| `TRG_VANZARI_BEFOUPD` | `VANZARI` | UPDATE | 4 apeluri `pack_audit.verifica_val` (NR_ACT, SERIE_ACT, DATA_ACT, DATA_SCAD) — audit, nu blocare | -| `TRG_VANZARE_BEFOINS` | `VANZARI` | INSERT | — | -| `TRG_VANZARI_DET_BEFOINS` | `VANZARI_DETALII` | INSERT | — | -| `TRG_VANZARI_CORESP_BEFOINS` | `VANZARI_CORESP` | INSERT | doar `SEQ_VANZARI_CORESP.nextval` | - -**Punct important de verificat, pentru ca la prima citire pare o portita:** in -`frm_facturi.do_sterge` apelul direct `pack_facturare.sterge_factura` e **comentat** pe ramura -documentelor cu note contabile — `ofacturare_comun.vc2:4807-4809` (`*!* modificare v 2.2.5`), iar -ramura activa (`:4810-4811`) cheama numai `pack_contafin.finalizeaza_stergere_nota`. **Nu e o -portita**: lantul se inchide in Oracle. - -``` -PACK_CONTAFIN:8322-8326 SELECT COUNT(*) INTO lnEInVanzari FROM vanzari WHERE cod = tnCod; - IF lnEInVanzari > 0 THEN - pack_facturare.sterge_din_vanzari(tnCod, tnAn, tnLuna, tnIdUtil); -PACK_FACTURARE:14996-14998 SELECT ID_VANZARE INTO V_ID_VANZARE FROM VANZARI WHERE COD = V_COD; - pack_facturare.sterge_factura(V_ID_VANZARE, V_LUNA, V_AN, V_ID_UTIL); -``` - -Apelantii lui `sterge_factura` in baza (cautare pe `all_source`) sunt exact doi: -`PACK_FACTURARE.sterge_din_vanzari` (`DB PACK_FACTURARE:14998`) si procedura standalone -`STERGE_DOCUMENT` (`DB STERGE_DOCUMENT:416`). Ambele cai trec prin garda. - -### 2b. VFP — pe calea de stergere: nicio garda pe urmasi - -`frm_facturi.do_sterge` (`COMUN\clase\ofacturare_comun.vc2:4661-4877`) verifica, inainte de apel: -luna inchisa (`:4676`), `sters = 1` (`:4689`), confirmare (`:4694`), luna curenta (`:4701`) si -referinte de incasari/plati (`ReferinteDocumenteNota`, `:4723-4727`). **Nimic despre `FACTURAT`, -`VANZARI_CORESP` sau urmasi** — verificarea lantului e delegata integral Oracle-ului. - -### 2c. VFP + Oracle — pe calea de **modificare**: nicio garda, nici pe urmasi, nici pe lant - -`frm_facturi.do_modifica` (`ofacturare_comun.vc2:4541-4640`) blocheaza doar doua lucruri -(`:4590`, `:4635`): - -``` - If poRec.sters = 0 AND NVL(m.pnEFactura,0) = 0 - ... - amessagebox("Nu puteti face modificari pe inregistrarile sterse sau facturile trimise in eFactura!", 48, "Atentie") -``` - -`pack_facturare.modifica_date_factura` (`EXPORT:14439-14509`) e un lant de `UPDATE`-uri fara niciun -`IF` de validare si fara niciun `RAISE_APPLICATION_ERROR`. Ceea ce e coerent cu ce modifica azi -(ruta, delegat, agent, masina, dataora exp., text aditional, tip SAFT, eFactura, serie/numar/data act, -data scadenta) — campuri de antet care nu ating lantul aviz -> factura. - ---- - -## 3. Concluzia operationala pentru decizia 50 / S7 - -**Regula ceruta de decizia 50 exista deja, si nu are gol pe aviz.** `sterge_factura` implementeaza -azi, in trei garzi consecutive, exact "documentul cu urmasi e blocat, capatul lantului e liber": - -| 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 | - -A treia garda merita subliniata pentru decizia 50: factura din aviz e editabila **doar** cat timp -niciunul dintre avizele ei nu are aviz de retur; nu e "capat de lant" neconditionat. - -**Deci S7 nu are nevoie de o garda noua ca regula — dar are nevoie de o citire noua a aceleiasi -conditii, ca moment.** Garda de azi e in interiorul lui `sterge_factura`, adica se manifesta ca -`ORA-20000` **in mijlocul** pasului de stergere din regenerare, dupa ce utilizatorul a completat -formularul si dupa ce s-a deschis tranzactia. Pentru un flux de editare asta e o esuare tarzie. -Ce lipseste e un **pre-flight read-only**, inainte de intrarea in formular, pe exact aceeasi -conditie (fara reimplementarea regulii): - -```sql -select count(*) from vanzari_coresp - where sters = 0 and id_vanzare_aviz = :id_vanzare and tip in (1, 2, 3) -``` - -plus, pentru cazul "factura din aviz", subinterogarea din garda 3 (`EXPORT:5480-5488`). Garda din -`sterge_factura` ramane pe loc ca plasa de siguranta — nu se muta, nu se slabeste. - -Interogarea se face pe `VANZARI_CORESP`, **nu** pe `VANZARI.FACTURAT`: `FACTURAT` e un marcaj -redundant, derivat, scris/resetat de `marcheaza_facturat` / `sterge_factura`, si necitit de nicio -garda; `VANZARI_CORESP` e semnalul autoritar. - ---- - -## 4. Inventar: ce tipuri de documente-parinte produc urmasi prin `VANZARI_CORESP`? - -**Doar trei, toate scrise din acelasi `CASE` din `finalizeaza_factura` (`EXPORT:14818-14839`), prin -singurul scriitor `scrie_corespondente_vanzari` (`EXPORT:15481-15516`, singurul `INSERT INTO -VANZARI_CORESP` din tot pachetul — si din toata baza, cf. `all_source`).** - -| parinte | urmas | `TIP` | de unde vine lista parintilor | -|---|---|---|---| -| **aviz** (`VANZARI.TIP in 21, 22, 26, 42`) | factura din aviz (`ntip = 4`) | 1 | `clistaid_avize`, setat la `EXPORT:7037` in `scrie_factura_avize`; lista o construieste VFP prin `caut_avize`, filtrata `a.tip in (21,22,26,42) and a.facturat = 0` — `COMUN\programe\oproceduri_facturare.prg:2036-2038` | -| **aviz** | aviz de retur (`ntip = 24`) | 2 | `clistaid` | -| **factura** | factura de retur (`ntip in 8, 9`) | 3 | `clistaid` | - -**Ce NU produce urmasi prin `VANZARI_CORESP`** (deci decizia 50 nu are acoperire acolo prin garda -existenta): - -- **proforma -> factura.** Proforma sta in `VANZARI` cu `EPROFORMA = 1`; `finalizeaza_factura` nu are - ramura care sa scrie corespondenta pentru ea. `TIP = 4` este o **propunere** de la S5c, nu cod - existent. Mai mult, `pack_facturare.sterge_proforma` (`EXPORT:5610-5635`) **nu are nicio garda** — - corpul ei e doar doua `UPDATE ... SET STERS = 1` pe `VANZARI` si `VANZARI_DETALII`. O proforma din - care s-a emis factura se poate sterge azi fara niciun avertisment. (Calea VFP: - `ofacturare_comun.vc2:4707-4719`, care iese din `do_sterge` inainte de restul verificarilor.) -- **comanda -> factura.** Legatura e `VANZARI.ID_COMANDA` + `pack_facturare.inchide_comanda` - (`EXPORT:5769`), nu `VANZARI_CORESP`. `COMENZI` **nu are coloana `FACTURAT`** (verificat pe - `all_tab_columns`) — flagul `facturat` din grid vine din view-ul de incarcare. Garda exista, dar e - **in VFP si pe comanda**, nu pe factura: `COMUN\clase\ocomenzi.vc2:1806-1807` la modificare - ("Aceasta comanda a fost facturata si nu mai poate fi modificata!") si `:2065-2066` la stergere. -- **contract -> factura.** Legatura e `VANZARI.ID_CTR` + `CTR_RATE_FACTURI` - (atinsa de `sterge_factura` la `EXPORT:5566-5580` pentru `V_TIP IN (2, 6, 52)`); nicio linie in - `VANZARI_CORESP`, nicio garda de tip "are urmasi". - ---- - -## 5. Ce am verificat direct vs. ce am dedus - -**Verificat direct pe cod / pe metadate:** - -- textul celor trei garzi din `sterge_factura`, in export **si** desfasurat din `all_source` (identice); -- `CASE`-ul din `finalizeaza_factura` care da semantica lui `TIP`, ramura cu ramura; -- corpul lui `scrie_corespondente_vanzari` si `marcheaza_facturat`; -- ca `INSERT INTO VANZARI_CORESP` exista intr-un singur loc (grep pe export + `all_source` pe toata baza); -- sursa integrala a celor 4 triggere de pe `VANZARI` / `VANZARI_DETALII` / `VANZARI_CORESP`; -- lista completa a apelantilor lui `sterge_factura` (`all_source`), inclusiv lantul - `finalizeaza_stergere_nota -> sterge_din_vanzari -> sterge_factura`; -- coloanele reale ale `VANZARI_CORESP`, `PROFORME`, si absenta lui `FACTURAT` din `COMENZI` - (`all_tab_columns`); -- `frm_facturi.do_sterge` si `frm_facturi.do_modifica` integral, plus `modifica_date_factura`; -- filtrul `caut_avize` din `oproceduri_facturare.prg`. - -**Dedus / cu limite declarate:** - -- Ca `TIP = 1` inseamna "factura normala din aviz" e o deductie din singurul punct de scriere - (`ntip = 4` -> `scrie_corespondente_vanzari(1)`) — solida, dar e deductie, nu o eticheta declarata - intr-un nomenclator. -- Ca setul tipurilor-parinte pentru `TIP = 1` e `{21, 22, 26, 42}` vine din filtrul VFP `caut_avize`, - nu dintr-o restrictie in Oracle. Pachetul insereaza **orice** `ID_VANZARE` primit in - `clistaid_avize`, fara filtru pe tip (`EXPORT:15494-15504`). Alt apelant (alt produs ROA, un import) - ar putea introduce alte tipuri. -- Nu am verificat daca vreun **alt produs ROA** (ROACONT / ROAGEST / ...) are o cale proprie de - stergere care ocoleste `sterge_factura`. Am verificat doar ca in Oracle nu exista alt apelant si - ca in ROAFACTURARE ambele ramuri din `do_sterge` ajung acolo. - -**Din date, deci nedovaditor:** interogarea pe `VANZARI_CORESP` din baza de dev arata `TIP=1` cu -parinti de tip 21 si 22, `TIP=2` cu parinte 22, `TIP=3` cu parinte 1 — consistent cu tabelul de mai -sus, dar volumul e de ordinul unitatilor (8 randuri in total). **Nu e dovada**; concluziile de mai -sus vin din cod. - -## 6. Ce nu s-a putut stabili - -Nimic din cele 4 intrebari nu a ramas nedeterminat. Singurul punct pe care l-as lasa explicit -deschis pentru S7 este cel de la §4: **golul real nu e pe aviz, e pe proforma** — -`sterge_proforma` nu are absolut nicio garda, iar daca S5c chiar introduce `TIP = 4` -(proforma -> factura), garda din `sterge_factura` **nu se aplica automat**, pentru ca `sterge_proforma` -e o procedura complet separata care nu o apeleaza. diff --git a/docs/cercetare/gol_ntip4_factura_din_avize.md b/docs/cercetare/gol_ntip4_factura_din_avize.md deleted file mode 100644 index c59822b..0000000 --- a/docs/cercetare/gol_ntip4_factura_din_avize.md +++ /dev/null @@ -1,231 +0,0 @@ -# Golul ntip=4 (facturare din avize) fata de proiectarea parametrului de cont contabil - -Cercetare read-only, continua `canal_cont_venit_fara_politica.md` (sectiunea "Proiectarea -parametrului de cont contabil", liniile 13-227). Confirma si extinde golul deja semnalat, mai -succint, in `parametru_cont_contabilizeaza_articol.md:49-56,167-168`. - -Sursa Oracle: `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` -(17217 linii). **Toate liniile citate mai jos sunt verificate direct pe acest fisier in aceasta -runda** — numerele din cererea initiala (7520-7537, 5096) s-au dovedit **identice**, fara offset, -fata de fisierul curent (nu +17 cum se anticipa). - -## Verdict, in cinci randuri - -**Ramura `ntip=4` e EXCLUSA STRUCTURAL, dar nu prin mecanismul presupus initial** (nu prin garda -`id_pol` din `cursor_articol`/FACT-024 a lui `contabilizeaza_articol`). Blocajul real e **cu o -functie mai devreme**: `adauga_articol_factura`, ramura `WHEN pack_facturare.ntip = 4` -(`:5080-5103`), cauta randul original din avizul sursa prin `A.ID_POL = V_ID_POL` **fara niciun -handler de exceptie** — daca `V_ID_POL` e `NULL` (exact cazul "articol fara politica"), `NULL = -NULL` nu se potriveste niciodata in SQL, deci `SELECT ... INTO` **cade mereu cu `NO_DATA_FOUND` -neprins**, la momentul **adaugarii articolului in factura** (`adauga_articol_factura`), cu mult -inainte ca linia sa ajunga vreodata la `contabilizeaza_articol`. Practic, un articol fara politica -**nu poate fi adaugat deloc** pe un document `ntip=4` — eroarea apare la primul pas, nu la scriere. -**Gol sora mai important, gasit in aceasta cercetare**: ramurile de **aviz** (`ntip` in afara -bucket-ului "factura") — `28`/`29` (SCD hardcodat `461`) si toate celelalte (SCD hardcodat `418`) -— **raman perfect accesibile** cu `cont_venit` populat, iar ramura noua propusa hardcodeaza -`SCD='4111'` necondiționat de `ntip`, ceea ce ar scrie contul gresit pe orice aviz cu articol fara -politica. Vezi sectiunea "Ramuri sora" mai jos — mai grav decat golul `ntip=4` insusi, pentru ca -e efectiv atins, nu doar teoretic. - -## 1. Lantul de dovezi pentru "ntip=4 e exclus" — pas cu pas - -### 1.1. `ntip` e o variabila de pachet, setata o singura data per document - -``` -ff_...:165 ntip NUMBER(2); -- declarata in spec, variabila publica de pachet -ff_...:1882 pack_facturare.ntip := V_TIP; -- in initializeaza_date_factura (una din cele 4 supraincarcari, ~1650-1918) -``` - -`V_TIP` vine din `poDate.Tip` la apelul VFP `pack_facturare.initializeaza_date_factura(...)` -(`COMUN\clase\ofacturare.vc2:13981-13999`, argumentul `Alltrim(Str(poDate.Tip))` pozitionat ca -`V_TIP`). O data setat, `pack_facturare.ntip` ramane valabil pentru toate apelurile ulterioare -din aceeasi tranzactie (`adauga_articol_factura`, apoi `scrie_factura2`/`scrie_factura_avize_retur`/ -`scrie_aviz_retur` -> `contabilizeaza_articol`) — **e o singura valoare per document, nu per linie**. - -### 1.2. Apelanti VFP care produc `poDate.Tip = 4` - -Comentat consecvent `&& factura din aviz` / `&& aviz` in tot codul VFP: -``` -COMUN\clase\ofacturare_comun.vc2:4347 If poDate.tip = 4 && factura din aviz -COMUN\programe\oproceduri_facturare.prg:1253 If poDate.tip = 4 && factura din aviz -COMUN\programe\ofacturare.prg:1512 Case poDate.tip = 4 -COMUN\clase\ofacturare.vc2:9534,9636,9674,14301,15151,18299,19022 Case poDate.tip = 4 (afisare/validare) -``` -`ofacturare.prg:1268,1488,1504` trateaza si combinatia `poDate.tip = 4 And Not -Empty(poDate.id_comanda_aviz)` alaturi de `ntip IN (3,21,28,42,47)` — confirma ca `ntip=4` e -categoria distincta "facturare pe baza de avize existente" (formularul dedicat de facturare din -avize), separata de facturarea directa sau din comenzi. - -### 1.3. `adauga_articol_factura`, ramura `ntip=4` — blocajul real - -``` -ff_...:4989-5015 PROCEDURE adauga_articol_factura(..., V_ID_POL IN NUMBER, ...) -``` -`V_ID_POL` e parametru direct (nu derivat), trimis de VFP la -`COMUN\clase\ofacturare.vc2:14072` (si identic `:18107`): -``` -Nvl(Alltrim(Str(poArt.id_pol)),[NULL]) -``` -Daca `poArt.id_pol` e `.NULL.` in VFP (cazul "articol fara politica" — premiza intregului -proiect #13), `Str()` propaga `.NULL.`, iar `Nvl(...)` trimite literalul SQL `NULL` — deci -`V_ID_POL` ajunge `NULL` in Oracle exact cand articolul n-are politica. - -In corpul `adauga_articol_factura`, CASE-ul care determina pretul/TVA/valuta ramurii (`:5052-5220`) -are, pentru `ntip=4`: -``` -ff_...:5080-5103 - WHEN pack_facturare.ntip = 4 THEN - -- facturare din avize - SELECT DISTINCT A.PRET, A.PROC_TVAV, A.ID_VALUTA, A.PRET_CU_TVA, B.IN_STOC - INTO V_PRET, V_PROC_TVAV, V_ID_VALUTA, V_PRETURI_CU_TVA, V_IN_STOC - FROM VANZARI_DETALII A - LEFT JOIN NOM_ARTICOLE B ON A.ID_ARTICOL = B.ID_ARTICOL - WHERE A.ID_ARTICOL = V_ID_ARTICOL - AND A.ID_POL = V_ID_POL - AND NVL(A.ID_GESTIUNE, -1000) = V_ID_GESTIUNE - AND A.DISCOUNT_UNITAR = V_DISCOUNT_UNITAR - AND NVL(A.CONT, 'XXXX') = V_CONT - AND A.ID_VANZARE IN (SELECT X FROM table(cast(CHARN2COLLECTION(pack_facturare.clistaid, ',') AS num_tab))); -``` -**Fara niciun `EXCEPTION WHEN NO_DATA_FOUND`** — spre deosebire de ramurile vecine din acelasi -CASE: `ntip=45` are handler cu `FACT-018` (`:5140-5145`), `V_OPT_FACTURARE=3` are handler cu -fallback + `FACT-012` (`:5167-5185`), `ELSE` are handler cu `FACT-013` (`:5188-5198`). Doar ramurile -`ntip IN (3,21,28,42,47)` (`:5053-5078`, cauta in `COMENZI_ELEMENTE` dupa comanda, nu dupa politica) -si `ntip=4` sunt fara plasa de siguranta. - -Cu `V_ID_POL = NULL`, `A.ID_POL = V_ID_POL` **nu se poate potrivi niciodata** (semantica SQL: orice -comparatie cu `NULL` da `UNKNOWN`, niciodata `TRUE`, indiferent ce e stocat in `A.ID_POL`) — -`SELECT ... INTO` (fara `DISTINCT`+agregat care sa tolereze zero randuri) **arunca mereu -`NO_DATA_FOUND`**. Neprinsa aici, exceptia se propaga necaptata pana la apelantul PL/SQL (blocul -anonim generat de VFP), care se intoarce la `goExecutor` ca eroare Oracle bruta (`ORA-01403: no -data found`), **nu ca `FACT-024`** — vezi punctul 3 mai jos pentru diferenta. - -**Concluzie pas 1.3**: un articol fara politica (`V_ID_POL=NULL`, exact scenariul `cont_venit`) -**nu poate fi adaugat pe un document `ntip=4`** — apelul `adauga_articol_factura` insusi esueaza, -inainte ca `VANZARI_DETALII_TEMP` sa primeasca randul, deci **cu mult inainte** ca -`contabilizeaza_articol` (unde e ramura noua `cont_venit`) sa vada vreodata acea linie. - -### 1.4. Blocajul secundar (daca 1.3 n-ar exista): `contabilizeaza_articol` insusi - -Chiar daca ipotetic linia ar ajunge in `VANZARI_DETALII_TEMP` cu `id_pol=NULL`, funcia care scrie -efectiv factura are propria garda, **inaintea oricarui ntip**: -``` -ff_...:7278-7302 BEGIN - SELECT COMPUS, ID_POL_ART INTO V_COMPUS, V_ID_POL_ART - FROM VCRM_POLITICI_PRET_ART - WHERE ID_ARTICOL = detalii_articol.id_articol - AND ID_POL = detalii_articol.id_pol; - EXCEPTION - WHEN NO_DATA_FOUND THEN - ... RAISE_APPLICATION_ERROR(-20000, '... (FACT-024)'); - END; -``` -Aceasta ruleaza **la inceputul functiei, inainte de `OPEN cursor_articol`** (`:7393`) si deci -inainte de CASE-ul pe `ntip` (`:7398-7423`) si de IF-ul `ntip<>4` (`:7472`). Cu `id_pol=NULL`, -`FACT-024` cade aici, indiferent de `ntip` — **confirma independent ca ramura `ntip=4` de la -`:7520-7537` (`scrie_fact_aviz_custodie`) e neatinsa azi de o linie fara politica**, e a doua -plasa de siguranta, redundanta cu 1.3 dar pe alt strat. - -**Ramura propusa `cont_venit IS NOT NULL`** (proiectare `canal_cont_venit_fara_politica.md` -sectiunea 3) se planteaza "inainte de `:7278`" — deci **inlocuieste ambele garda de mai sus** cand -`cont_venit` e populat. Asta e sigur (design-ul insusi), dar devine irelevant pentru `ntip=4` -pentru ca linia nu ajunge niciodata aici (blocata la 1.3). - -## 2. Ce se intampla defensiv daca cineva ajunge totusi acolo - -Doua scenarii distincte, cu raspunsuri diferite: - -**(a) Scenariul normal — `cont_venit` populat, `id_pol` NULL (cum cere premiza proiectului)**: -esec **la adaugarea articolului**, nu la scrierea facturii. Eroare Oracle bruta -(`ORA-01403: no data found`), aparuta din `adauga_articol_factura` (`:5080-5103`), propagata prin -`goExecutor` catre VFP ca `oPrelucrareEroare()` — **nu `FACT-024`** (acela e alt cod, alta functie, -alt moment). Utilizatorul vede un mesaj Oracle generic, nu un mesaj de business explicativ. Nu -exista azi niciun cod care sa traduca acest caz intr-un mesaj prietenos — e un gol de UX, nu de -integritate a datelor (nu se scrie nimic gresit, doar esueaza fara explicatie buna). - -**(b) Scenariul defensiv real — `cont_venit` SI `id_pol` populate simultan pe aceeasi linie** -(nimic in schema nu impune exclusivitate reciproca; `VANZARI_DETALII_TEMP.CONT_VENIT` e o coloana -noua, fara `CHECK` care sa lege de `ID_POL`). Daca cineva (bug in VFP, sau folosire deliberata -combinata pe viitor) ar trimite ambele populate pe un document `ntip=4`: pasul 1.3 ar reusi -(`V_ID_POL` nu mai e NULL, deci `SELECT` din `VANZARI_DETALII` isi gaseste randul), linia intra in -`VANZARI_DETALII_TEMP` cu `cont_venit` **si** `id_pol` completate. La `contabilizeaza_articol`, -ramura noua (`detalii_articol.cont_venit IS NOT NULL`) preia controlul complet — **fara sa -verifice `ntip`** — si executa necondiționat `scrie_nota` + `descarca_gestiune` + discount, exact -ca pentru o factura normala. **Nu cade cu nicio eroare. Scrie tacut date gresite**: sare complet -peste `scrie_fact_aviz_custodie` (`:7521-7536`), care exista tocmai pentru ca marfa a iesit deja -din gestiune cand s-a scris avizul sursa — rularea `descarca_gestiune` in loc de acea functie -**descarca stocul a doua oara** pentru aceeasi marfa, fara nicio garda care sa prinda dubla -scadere. Acesta e raspunsul relevant pentru textul de plan: nu un crash, un **defect tacut de -date** daca vreodata cele doua canale se intalnesc pe aceeasi linie. - -## 3. Toate valorile `ntip` relevante in zona `contabilizeaza_articol` (`:7173-7547`) — verdict per valoare - -Ramura noua (`cont_venit IS NOT NULL`) inlocuieste tot ce e listat mai jos, necondiționat de `ntip` -(design-ul, sectiunea 3, nu mentioneaza `ntip` deloc in continutul ramurii noi). Verdictul de mai -jos e "atinsa" = poate ajunge cu `cont_venit` populat prin `adauga_articol_factura` fara sa esueze -la pasul 1.3-echivalent; "neatinsa" = blocata structural inainte de a ajunge aici (ca `ntip=4`); -"ambigua" = depinde de alt cod neverificat complet in aceasta runda. - -| `ntip` | Comportament azi in `contabilizeaza_articol` | Atinsa de linie `cont_venit`? | Verdict fata de ramura noua | -|---|---|---|---| -| `<=20` sau `IN (43,44,45,46,48,49,51,52)` (`:7400-7412`) | SCD/ASCD din politica (`crs_rand_articol.scd`) — bucket "factura" | **DA** — `adauga_articol_factura` pentru aceste `ntip` foloseste calea generica (`ELSE`, `:5187-5203`, fara cerinta de `id_pol`) sau ramuri proprii (`2,6,26,52`→contract, `45`→restaurant) care oricum accepta `V_PRET_TEMP` cand nu gasesc potrivire | **E cazul acoperit de design** — SCD='4111' hardcodat e gandit exact pentru acest bucket. Fara probleme noi, cu exceptia sub-cazurilor de mai jos. | -| `= 46` (`nTipNotaPlata`), sub-caz al bucketului "factura" | `scrie_nota` **NU se apeleaza** (`:7441`, `IF ntip <> nTipNotaPlata`) | DA (e in bucketul de mai sus) | **GOL SORA nou, neconsemnat in proiectare**: ramura noua apeleaza `scrie_nota` necondiționat — pentru un document `NotaPlata` cu linie `cont_venit`, s-ar scrie o nota care azi e sarita intentionat. De adaugat aceeasi garda in ramura noua. | -| `= 7`, sub-caz al bucketului "factura" | `EXPLICATIE` primeste sufix `' INVOICE:' + cdescriere` (`:7431-7433`) | DA | **Gol cosmetic**: ramura noua foloseste direct `detalii_articol.explicatia`, fara sufix. Probabil acceptabil (design-ul il numeste "mai bun"), dar e o schimbare de continut pe documente de export, de confirmat. | -| `IN (8,9)`, sub-caz al bucketului "factura" | `EXPLICATIE` primeste sufix `' RETUR FACTURA:' + cdescriere` (`:7434-7436`) | DA | Acelasi gol cosmetic ca mai sus. | -| `IN (28,29)` (`:7413-7417`) | SCD hardcodat `'461'` — aviz catre clienti debitori | **DA** — `adauga_articol_factura` pentru `28` intra pe ramura `ntip IN (3,21,28,42,47)` (`:5053-5078`), care cauta in `COMENZI_ELEMENTE` dupa comanda+pret, **nu dupa `id_pol`** — un articol fara politica poate gasi potrivire aici daca exista element de comanda corespunzator | **GOL SORA, mai grav decat `ntip=4`**: ramura noua ar scrie `SCD='4111'` in loc de `'461'` pe un aviz catre client debitor — cont contabil gresit, atins efectiv. | -| toate celelalte (`ELSE`, `:7418-7422`: `21,22,23,24,25,27,30,41,42,47,...`) | SCD hardcodat `'418'` — aviz generic | **DA, pentru aceleasi motive** (multe din aceste `ntip` — `3,21,42,47` — trec prin ramura "din comenzi" fara cerinta de `id_pol`; altele avansate direct din bucket implicit `ELSE` din `adauga_articol_factura`, `:5187-5203`, care nu cere `id_pol` deloc) | **Acelasi gol SCD hardcodat**, aplicabil la orice document de tip aviz (nu doar 28/29). | -| `= 4` (`:7472,7520-7537`) | `scrie_fact_aviz_custodie` in loc de `descarca_gestiune`+discount | **NU** — exclus structural la pasul 1.3 (`adauga_articol_factura` esueaza inainte) | Neatins in scenariul normal (a); atins doar in scenariul defensiv (b) daca cineva forteaza `id_pol` si `cont_venit` simultan — vezi sectiunea 2. | - -**Cel mai important rezultat al acestui tabel**: golul cerut explicit (`ntip=4`) e cel **mai putin -grav** dintre cele gasite, pentru ca e singurul exclus structural. Golurile reale, efectiv atinse, -sunt hardcodarea `SCD='4111'` pe orice document de tip **aviz** (`28`,`29` si bucket-ul `ELSE`) si -omisiunea gardei `ntip=46` pentru `scrie_nota`. - -## 4. Text propus pentru plan (sectiunea deciziei 34) - -``` -**Ramura `ntip=4` (facturare din avize, `:7520-7537`) — exclusa structural, nu necesita cod -suplimentar.** `adauga_articol_factura`, ramura `WHEN ntip=4` (`:5080-5103`), cauta randul sursa -din `VANZARI_DETALII` prin `A.ID_POL = V_ID_POL`, fara handler de exceptie — cu `V_ID_POL=NULL` -(exact cazul liniei fara politica), comparatia nu se poate potrivi niciodata, deci apelul cade cu -`ORA-01403` la adaugarea articolului, inainte ca linia sa ajunga la `contabilizeaza_articol`. Un -articol fara politica nu poate fi, structural, adaugat pe un document `ntip=4`; ramura noua -`cont_venit` nu are nimic de tratat aici. - -**Gol descoperit, de acoperit inainte de implementare**: ramurile de **aviz** (`ntip IN (28,29)` → -`SCD='461'`, celelalte → `SCD='418'`, `:7413-7422`) **raman accesibile** cu `cont_venit` populat — -`adauga_articol_factura` nu cere `id_pol` pentru aceste `ntip` (foloseste calea "din comenzi" sau -calea implicita). Ramura noua trebuie sa aleaga `SCD` in functie de `ntip` (pastrand `'461'`/`'418'` -pentru avize), nu sa hardcodeze `'4111'` necondiționat — altfel orice aviz cu articol fara politica -primeste contul de clienti in loc de contul de aviz. Similar, ramura noua trebuie sa pastreze garda -`IF ntip <> nTipNotaPlata` inainte de a apela `scrie_nota` (azi sarita pentru `ntip=46`). - -**Defensiv (opțional)**: daca se doreste o garda explicita in loc de a te baza pe imposibilitatea -structurala de mai sus, se poate adauga `IF detalii_articol.cont_venit IS NOT NULL AND -pack_facturare.ntip = 4 THEN RAISE_APPLICATION_ERROR(...)` la inceputul ramurii noi — util doar -daca cineva ar reusi vreodata sa populeze simultan `id_pol` si `cont_venit` pe o linie `ntip=4` -(azi imposibil pe caile VFP cunoscute), caz in care ramura noua ar rula altfel tacut -`descarca_gestiune` in loc de `scrie_fact_aviz_custodie`, riscand dubla descarcare de gestiune. -``` - -## 5. Ce nu s-a putut stabili in aceasta runda - -- **Daca exista vreo cale VFP care sa populeze simultan `poArt.id_pol` si viitorul `cont_venit`** - (scenariul defensiv (b)) — cercetarea de fata arata doar ca schema/codul Oracle nu o impiedica; - n-am gasit (si nici n-am cautat exhaustiv) un loc in VFP care ar genera aceasta combinatie azi. - Probabil imposibil pe fluxurile curente (proiectarea de baza presupune `cont_venit` populat - **doar** cand `id_pol` lipseste), dar nu demonstrat exhaustiv pe tot codul VFP. -- **Comportamentul exact `oExecute`/`oPrelucrareEroare` la o exceptie Oracle neprinsa** (scenariul - (a)) — presupus ca ajunge la utilizator ca mesaj Oracle brut prin mecanismul general de eroare al - `goExecutor`, dar nu am urmarit codul `oPrelucrareEroare()` in aceasta runda pentru a confirma - formatarea exacta. -- **Daca `ntip IN (23,25,30,41)` (transfer subunitati) si `IN (42,47)` (custodie) pot, in practica, - sa primeasca un articol fara politica** prin ramura "din comenzi" a `adauga_articol_factura` - (`:5053-5078`) — argumentat structural posibil (nu cere `id_pol`), dar nu testat pe date, nu - urmarit pana la capat daca `COMENZI_ELEMENTE` insusi ar putea contine un rand fara politica. - -## Handoff - -Cercetare incheiata, toate cele 5 livrabile cerute de team-lead sunt in sectiunile 1-4 de mai sus. -Niciun cod atins, nicio scriere Oracle, doar `SELECT`/`Read`/`Grep`. Context consumat moderat — -nu a fost necesara predarea de context. diff --git a/docs/cercetare/handoff_s4_runda1.md b/docs/cercetare/handoff_s4_runda1.md deleted file mode 100644 index 3bc124f..0000000 --- a/docs/cercetare/handoff_s4_runda1.md +++ /dev/null @@ -1,191 +0,0 @@ -# Handoff — S4 runda 1 (PAGE3 doar afisare), predare pe prag de context - -Sesiune intrerupta la prag de context (`monitorizare-context.md`). Stare **stabila si consistenta** -(text = binar), dar cu schele de diagnostic inca in cod — de scos inainte de livrare. - -## 1. Ce am modificat, unde, si write-back-ul - -Toate in `COMUN` (cross-proiect). - -- **`COMUN\programe\ofacturare_editare.prg`** — **write-back N/A (e `.prg`, nu `.vcx`, se ruleaza direct)**. - Antet actualizat (linia cu `*!* helpere...`) + doua functii noi, adaugate la coada fisierului: - - `IncarcaVanzareNota(tnCod, tnNract, tcSerieAct, tdDataAct)` — gaseste randul din `VANZARI` - corespunzator notei curente, in cursorul `tvanz`. - - `IncarcaArticoleFactura(tnIdVanzare)` — incarca liniile active din `VANZARI_DETALII` (+ denumire - articol/gestiune/valuta) in cursorul `tvd`. - Fisier ASCII (fara diacritice), editat direct cu Edit tool — fara riscul de corupere cp1252. - -- **`COMUN\clase\omodificari.vc2`** (clasa `frm_modific2024`) — **text si binar SINCRONIZATE**, - ambele includ inca **cod de diagnostic care trebuie scos**: - - PAGE3 nou pe `pgfArticole` (`PageCount=3`, `PAGE3.Caption="Articole factura"`) — linia cu - `PageCount = 3, ;` in blocul `ADD OBJECT 'pgfArticole'`. - - Grid nou `pgfArticole.PAGE3.grdArticoleFactura` (13 coloane, `RecordSource="tvd"`, - `ReadOnly=.T.` la nivel de grid si pe fiecare `Text1`), plus intrarile `OBJECTDATA` - corespunzatoare (cautabile dupa `grdArticoleFactura`). - - Proprietati noi pe clasa: `lAreArticoleVanzari`, `nIdVanzare`, `nTipVanzare` (in blocul - `*`, cautabile dupa `lareaarticolevanzari`/`nidvanzare`/`ntipvanzare`). - - `Load()`: placeholder `CREATE CURSOR tvd (...)` daca nu e deja deschis — **necesar**, altfel - grid-ul incearca `USE tvd` la constructie si pica pe dialog nativ "Open" (vezi sectiunea 4). - - `Show()`: bloc nou (cautabil dupa `This.lAreArticoleVanzari`) care apeleaza - `IncarcaVanzareNota`/`IncarcaArticoleFactura` si comuta `pgfArticole.PageCount` intre 2 si 3. - - **DE SCOS inainte de livrare**: 5 linii `STRTOFILE(...'diag_class.txt'...)` inserate ca - checkpoint-uri de depanare in `Init()` (3 linii: inceput, dupa `DoDefault()`, sfarsit) si - `Load()` (2 linii: inceput, sfarsit) — cautabile dupa `diag_class`. Nu au efect functional - (doar scriu intr-un fisier de log), dar nu trebuie sa ramana in cod livrat. - - **Fidelity check txt2vcx: OK** la ultimul write-back (cel CU liniile de diagnostic inca in el). - `.vcx`/`.vct` au mtime 12:10, exact dupa acel write-back — deci **binarul reflecta exact - `.vc2`-ul curent**, inclusiv diagnosticul. - -### Backup-uri pe disc (`COMUN\clase\`) - -| Fisier | Continut | -|---|---| -| `omodificari.vc2.pre_s4runda1.bak` | Originalul, dinaintea oricarei modificari S4 | -| `omodificari.vc2.pre_diag.bak` | Versiunea mea **curata** (fara cele 5 linii de diagnostic) — **asta e sursa de folosit pentru versiunea finala** | -| `omodificari.vc2.mine_s4_full.bak` | Identic cu `pre_diag.bak` (copie facuta in timpul unui test de izolare) | -| `omodificari.vcx.mine_s4.bak` / `omodificari.vct.mine_s4.bak` | **Binarul** corespunzator lui `pre_diag.bak`, copiat direct (fara compilare) — restaurare instantanee | - -**Pasul urmator recomandat**: `Copy-Item omodificari.vc2.pre_diag.bak -> omodificari.vc2`, -`Copy-Item omodificari.vcx.mine_s4.bak -> omodificari.vcx` (+ `.vct`) — restaurare **instantanee** -(fara `txt2vcx`) la versiunea curata cu PAGE3 functional, fara schela de diagnostic. Verifica dupa -aceea cu un `diff` ca `omodificari.vc2` chiar nu mai contine `diag_class`. - -## 2. Ce mai ramane din runda 1 - -Din sectiunea E / runda 1 a planului (A.2 PageCount + A.3 detectie + B.1 incarcare + B.2 marcaje): - -- **A.2 (PageCount)** — FACUT, testat static (fidelity OK), **netestat live pe formular** (vezi - sectiunea 3 — blocaj de mediu, nu s-a ajuns sa vad `PageCount` pe formularul instantiat real). -- **A.3 (detectie)** — FACUT, testat **direct** (fara UI) cu rezultate PASS pe date reale (sectiunea - 3). Deviaza de la recomandarea planului (cauta si de ce, sectiunea 4). -- **B.1 (incarcare cursoare)** — FACUT, testat direct, PASS. -- **B.2 (marcaje stare linii)** — **NU e in scope runda 1** (grid-ul e strict readonly, fara - editare — marcajele `_modificat`/`sters`/`id_vanzare_det=0` sunt pentru runda 2). Nu e inceput. - -**Ce lipseste efectiv**: verificarea end-to-end ca, pe formularul REAL (`Createobject('frm_modific2024')` -+ `.Show()`), `PageCount` chiar ajunge 3 pe o factura si 2 pe o nota fara `VANZARI` — blocata de -problema de mediu din sectiunea 3. Codul e scris si compilat curat; ramane doar confirmarea live. - -## 3. Ce am testat, cu ce rezultat - -Suita: `COMUN\utile\Teste\editare_factura\test_page3_articole.prg` (headless, `vfp9.exe -A -T`, -conexiune reala `MARIUSM_AUTO`). Log: `test_page3_articole_log.txt` in acelasi folder. - -**PASS, pe date reale, testat direct (apel functie, fara formular)**: -- `cod=1140888` → `IncarcaVanzareNota` gaseste `id_vanzare=1050`, `tip=1`; `IncarcaArticoleFactura` - incarca 4 linii — coincide exact cu baza de regresie din `progres.md` (`TOTAL_CU_TVA=1924.59`). -- `cod=1140885` → gaseste `id_vanzare=1047`, `tip=-12` (nu e factura, e alt tip de document) — - confirma ca detectia functioneaza pe **orice** tip din `VANZARI`, nu doar facturi (decizia 19). -- `cod=1125486` (an=2008, luna=2, notă contabila fara nicio legatura cu `VANZARI`) → 0 randuri - gasite — cazul negativ cerut de criteriul de "gata" al rundei. -- **Coliziune pe `cod=1139934`** (patru randuri active in `VANZARI`, acelasi cod): filtrul compus - (cod+nract+serie_act+data_act) gaseste EXACT randul cerut (`nract=375/SSS` → `id_vanzare=882`) si - NU gaseste nimic pentru un `nract` fara corespondent (`nract=13`) — vezi sectiunea 4, e important. - -**NETESTAT live (formular real)**: `verifica_pagecount_form` (in acelasi fisier de test) instantiaza -`frm_modific2024` exact ca `do_editare_factura` (`Createobject`+`Show`) si verifica `PageCount` — -**se blocheaza intr-un dialog nativ VFP "View Parameter"** in timpul `Createobject`, inainte sa -apuce sa ruleze vreo linie din `Show()`. Vezi sectiunea 4 pentru diagnosticul facut si o pista noua, -neexplorata inca, primita de la team-lead. - -**`test_baseline_isolation.prg`** (in acelasi folder) — **fisier temporar de diagnostic, se poate -sterge** dupa ce cineva confirma diagnosticul de mai jos independent; nu face parte din suita -finala (antetul lui o spune explicit). L-am folosit ca sa demonstrez ca **binarul ORIGINAL (dinainte -de S4) NU are acest blocaj** in acelasi mediu de test (`PageCount=2`, `done`, fara dialog) — deci -problema e din diff-ul meu, nu un gol preexistent de mediu. - -## 4. Descoperiri care nu trebuie pierdute - -### `VANZARI.COD` NU e unic — dovedit pe date, nu presupus - -Contrar recomandarii A.3 din plan ("`SELECT ... FROM vanzari WHERE cod = tact.cod`"), am verificat -direct pe `MARIUSM_AUTO`: -- Indexul `IDX_VANZARI_04` e pe `(STERS, COD)` si e **NONUNIQUE** — schema insasi nu garanteaza - unicitate. -- `cod=1139934` are **4 randuri active** (`STERS=0`) in `VANZARI`, cu `TIP`/`NUMAR_ACT` diferite - (375/SSS, 24/PF, 25/PF, 26/PF). `cod=1139555` are 2 randuri cu `TIP` diferit (43 si 1). -- Filtrarea suplimentara pe `NUMAR_ACT`+`SERIE_ACT`+`DATA_ACT` (toate disponibile pe cursorul `tact` - ca `nract`/`serie_act`/`dataact`, din `vact_tot`) rezolva ambiguitatea: `(COD, NUMAR_ACT, - SERIE_ACT, DATA_ACT)` verificat **fara nicio dublura** pe toata tabela `VANZARI`. -- `ID_FACT` (alternativa mentionata in `progres.md` — "toate legaturile trec prin ID_FACT sau - ID_VANZARE, niciodata prin cod") **nu e utilizabil ca filtru unic aici**: e `NULL` pe o fractie - mare de randuri chiar si pentru facturi normale (`tip=1`: 58 din 183 `NULL`; `tip=51`: 58 din 74 - `NULL`), deci n-ar gasi avizele/tipurile fara facturare clasica — ar incalca decizia 19. - -**Concluzie aplicata in cod**: `IncarcaVanzareNota` filtreaza pe cele 4 coloane compuse, nu doar pe -`cod`. Aceasta e o **corectie de fond fata de A.3 din plan**, nu o improvizatie — sectiunea C.1 din -`plan_06_s4_proiectare.md` ar trebui sa mentioneze asta daca cineva o rescrie. - -### Blocaj "View Parameter" la `Createobject('frm_modific2024')` — cauza NECONFIRMATA - -Simptom: procesul `vfp9.exe` ramane blocat (CPU~0, Responding=True) intr-un dialog nativ **"View -Parameter"** exact in timpul liniei `Createobject([frm_modific2024], lnIdSet)`, inainte sa ajunga -la orice linie din `Show()` — confirmat cu checkpoint-uri `STRTOFILE` in `Init()`/`Load()` (niciunul -nu s-a scris, deci blocajul e chiar mai devreme decat `Load()`, sau checkpoint-urile nu s-au atins -din alt motiv neexplicat). - -**Exclus cu dovezi** (nu pierde timp re-verificand): -- **Nu e cursorul `tvd` lipsa** — am incercat atat placeholder in `Load()`, cat si pre-creare in - scriptul de test inainte de `Createobject`; blocajul a persistat identic. -- **Nu e un gol de mediu preexistent** — binarul ORIGINAL (`omodificari.vcx.mine_s4.bak` restaurat - temporar) ruleaza curat in ACELASI mediu de test (`test_baseline_isolation.prg`, `PageCount=2`, - fara dialog). Deci e ceva din diff-ul meu. -- **Un blocaj anterior, diferit** (`File 'crsjtvatemp.dbf' does not exist`, dialog "Open") era - intr-adevar preexistent — cerut de `Column63` din `grdRulaje` (PAGE1, cod netusat de mine), - rezolvat in scriptul de test cu `update_jtva_coloane("", "crsJtvaTemp", 0)` inainte de - `Createobject` (nu in clasa — e o lipsa de mediu de test, nu de productie: in productie, - `crsJtvaTemp` e populat pe alte cai inainte sa ajunga un utilizator la acest formular). -- Am citit `_pageframe.Init()` (`_baza.vc2:514-523`, itereaza `For i = 1 To PageCount` si cheama - `tradu(.Caption)`) ca prim suspect — **`tradu()` (`oproceduri_comune.prg:1746`) e confirmat - inofensiv** (doar `STRTRAN` pe diacritice, fara Oracle/View). Nu e cauza, dar ramane singurul cod - DEPENDENT de `PageCount` gasit prin cautare in `_baza.vc2`/`_frm_base.vc2`/`_grd_base.vc2`. -- Am cautat "CREATE SQL VIEW" / `USE ... VIA` in toata ierarhia de clase (`omodificari`, `_baza`, - `_frm_base`, `_grd_base`, `gridextras`) — **zero rezultate**. Niciun view local gasit static. - -**Pista noua, neexplorata** (primita de la team-lead, nu verificata de mine inca): dialogul "View -Parameter" apare tipic cand o variabila referita prin `?variabila` (SQL passthrough) sau intr-un -view parametrizat e declarata `LOCAL` in loc de `PRIVATE` intr-un harness de test — `LOCAL` nu e -vizibil in rutinele apelate. Exemplu citat: `do_editare_factura` declara -`Private pnAn, pnLuna, lnCod, lnIdFact, lnIdVanzare` (`ofacturare_comun.vc2:3742`). **Nu am apucat -sa verific** daca `test_page3_articole.prg` foloseste `LOCAL` unde codul original ar cere `PRIVATE` -undeva in lantul `IncarcaCursoareModificareNota`/`Createobject` — e primul lucru de incercat inainte -de a relua sapatul cu `EnumWindows`/`PrintWindow` (costisitor, ~15 cicluri de test in aceasta -sesiune doar pentru asta). - -## 5. Capcane de mediu platite in aceasta sesiune - -- **Sterge intotdeauna `.fxp`-ul vechi inainte de fiecare rulare de test** — m-a pacalit o data: - am editat `.prg`-ul dar am uitat sa sterg `.fxp`, iar VFP a rulat tacut codul VECHI compilat, - dand rezultate inconsistente intre rulari identice in aparenta. Simptom: log-ul arata alt - comportament decat sursa curenta ar trebui sa produca. -- **FoxBin2Prg NU pastreaza ordinea textuala la scriere-citire (roundtrip)** pentru ADD OBJECT - multiple si proprietati custom — le REGENEREAZA in ordine proprie (alfabetica pentru - coloane/obiecte fii, alta regula neclara pentru proprietati custom simple — `nidvanzare` a iesit - INAINTEA lui `nid_set`, desi `_` < `v` in ASCII). Fidelity-check-ul compara text-sursa cu - text-regenerat, deci **orice ordine "naturala" (alfabetica presupusa de mine) poate pica fidelity**. - Solutie aplicata: dupa primul fidelity FAIL, am adoptat ca sursa noua textul regenerat din - `\verify\*.vc2` (e deja in forma canonica), nu am incercat sa ghicesc ordinea corecta. - Confirma exact indicatia din `flux-editare-vfp-text.md`. -- **`InputMask` cu literal (nu `get_mask(...)`)** trebuie **ghilimeluit** (`"9.99"`), nu bar (`9.99`) - — altfel VFP il interpreteaza numeric, nu ca string de format. Prins abia la inspectia manuala a - textului regenerat, fidelity-check-ul NU l-a semnalat separat (a picat impreuna cu reordonarea). -- **Grid nou cu `RecordSource` pe un cursor care nu exista inca la constructia formularului** → - VFP incearca `USE ` ca fisier fizic si arata dialogul nativ "Open" daca nu-l - gaseste pe disc — exact acelasi tipar deja documentat in clasa pentru `saft_taxtable`/ - `saft_mecanisme_plati` (`Load()`, verificat `If !Used(...)` inainte de orice altceva). Solutia - e aceeasi: placeholder gol creat in `Load()`, INAINTE ca framework-ul sa construiasca grid-urile. -- **Query-uri Oracle multiple rulate manual (sqlplus) au fost esentiale** pentru validarea empirica - a filtrului compus pe `VANZARI` — nu s-ar fi descoperit coliziunea pe `cod` doar din cod static. - -## 6. Ce recomand pentru urmatorul agent - -1. Restaureaza versiunea curata (sectiunea 1, pasul recomandat) — 2 copieri de fisier, fara - compilare, verifica cu `grep diag_class` ca a disparut. -2. Incearca pista `LOCAL`/`PRIVATE` din sectiunea 4 pe `test_page3_articole.prg` inainte de orice - alta depanare GUI. -3. Daca se rezolva: reruleaza `test_page3_articole.prg` complet, confirma PASS pe toate cazurile - (inclusiv `verifica_pagecount_form`), sterge `test_baseline_isolation.prg` + `.fxp` + log-ul lui, - scrie diff-ul (diff aplicat (sters), `git diff --no-index `) si - raportul (`docs\cercetare\rec_s4_runda1.md`) cerute initial de team-lead. -4. Daca team-lead sau alt agent scrie capcana "View Parameter / LOCAL vs PRIVATE" in - `testare-ui-vfp.md`, nu o duplica — team-lead a intrebat explicit cine o scrie. diff --git a/docs/cercetare/idfact_refolosire_si_documente.md b/docs/cercetare/idfact_refolosire_si_documente.md deleted file mode 100644 index 53d6b3c..0000000 --- a/docs/cercetare/idfact_refolosire_si_documente.md +++ /dev/null @@ -1,337 +0,0 @@ -Cercetare S9: reemiterea unui document cu acelasi ID_FACT (SET_IDFACT + DOCUMENTE) -==================================================================================== - -Verdict (rezumat) ------------------- -**DA, dar cu conditii — nu e o schimbare izolata.** `SET_IDFACT` activ (`PACK_CONTAFIN.pck:3037-3040`) -ia neconditionat un `ID_FACT` nou din `SEQ_IdFact`; niciun apelant din toata suita (~25-30 copii -identice ale `oscrie_in_fisiere.prg`, cate una per produs) ii impune un `ID_FACT`. Varianta comentata -(`:3016-3035`, cauta pe `NRACT+SERIE_ACT+DATAACT+ID_CTR` cu `STERS=0`) **nu rezolva S9** ca atare: -dupa stergerea soft a documentului vechi, `STERS` e deja 1, cautarea nu-l gaseste, si cade tot pe -secventa — plus riscul de coliziune cu un document viitor neinrudit care are aceleasi 4 campuri. -Blocajul real nu e in `SET_IDFACT`, ci in scrierea din `DOCUMENTE`: acolo e un `INSERT` simplu -(`:796-817`) pe `ID_DOC`, care e **PRIMARY KEY** (`PK_DOCUMENTE`, unic, `fn_script.sql:5192-5198`). -Stergerea (`STERGE_DIN_ACT:1835-1855`) e soft-delete pur — `ACT.STERS=1`, apoi `DOCUMENTE.STERS=1` -pentru id_fact-urile gasite pe acel `COD` — randul vechi **nu se sterge fizic**. Deci reemiterea cu -acelasi `ID_FACT`, cu codul de azi neschimbat, ar arunca `ORA-00001` la insertul in `DOCUMENTE`, -pentru ca randul cu acel `ID_DOC` inca exista (doar marcat `STERS=1`). Varianta `MERGE` deja -comentata (`:818-847`) nu ajuta din prima: are doar `WHEN NOT MATCHED`, fara `WHEN MATCHED`, deci -daca randul exista deja l-ar ignora tacit — documentul reemis ar ramane cu `DOCUMENTE.STERS=1`. -Concluzie operationala: reutilizarea e posibila, dar cere trei schimbari simultane (detaliate la C.7), -nu doar reactivarea codului comentat din `SET_IDFACT`. - -A. SET_IDFACT --------------- - -### A.1 — Ramura activa vs. ramura comentata - -Cod integral, `PACK_CONTAFIN.pck:3014-3040`: - -``` - ------------------------------------------------------------------------------------ - /* -- 25.02.2013 : am comentat pentru ca se face unirea id_fact pe listarea din JC/JV - PROCEDURE SET_IDFACT(tdDataAct ACT_TEMP.DATAACT%TYPE, - tcSerie_Act ACT_TEMP.SERIE_ACT%TYPE, - tnNrAct ACT_TEMP.NRACT%TYPE, - tnId_Ctr ACT_TEMP.ID_CTR%TYPE) IS - V_ID_FACT DOCUMENTE.ID_DOC%TYPE; - BEGIN - BEGIN - SELECT ID_DOC - into pack_contafin.nIdFact - FROM DOCUMENTE - WHERE NRACT = tnNrAct - AND NVL(SERIE_ACT, '+-') = NVL(tcSerie_Act, '+-') - AND DATAACT = tdDataAct - AND NVL(ID_CTR, 0) = NVL(tnId_Ctr, 0) - AND STERS = 0; - EXCEPTION - WHEN NO_DATA_FOUND THEN - SELECT SEQ_IdFact.NEXTVAL into pack_contafin.nIdFact FROM DUAL; - END; - END SET_IDFACT;*/ - ------------------------------------------------------------------------------------ - PROCEDURE SET_IDFACT(V_GCS IN VARCHAR2) IS - BEGIN - SELECT SEQ_IdFact.NEXTVAL into pack_contafin.nIdFact FROM DUAL; - END SET_IDFACT; -``` - -Diferenta de comportament daca s-ar reactiva varianta comentata: in loc sa genereze mereu un -`ID_FACT` nou, ar cauta intai in `DOCUMENTE` un document **nesters** (`STERS=0`) cu aceeasi -combinatie `NRACT + SERIE_ACT + DATAACT + ID_CTR`, si doar daca nu gaseste nimic ar cere secventa. -Doua probleme pentru S9: -- **nu se aplica la reemitere**: documentul vechi e deja marcat `STERS=1` inainte de rescriere - (vezi B.6), asa ca filtrul `STERS=0` nu-l gaseste — cade tot pe `SEQ_IdFact.NEXTVAL`, exact ca azi; -- **risc de coliziune**: daca NRACT/SERIE_ACT/DATAACT/ID_CTR ar coincide intamplator cu alt document - nesters (nu neaparat cel pe care il reemitem), i-ar imprumuta ID_FACT-ul aceluia. -Semnatura veche are 4 parametri de identitate diferiti de semnatura activa (1 parametru `V_GCS`, -folosit doar ca "user"/context, nefolosit in corp) — deci reactivarea ar cere fie o supraincarcare, -fie schimbarea semnaturii peste tot unde e chemata (vezi A.2). - -### A.2 — Apelanti - -**Un singur punct de apel in PL/SQL**: `PACK_CONTAFIN.pck:721`, in bucla din `SCRIE_IN_ACT`: - -``` - pack_contafin.set_idfact(V_GCS); - /* 25.02.2013 : se face cumularea id_fact pe listarea din RC/RJ - PACK_CONTAFIN.SET_IDFACT(itemfact.dataact, itemfact.serie_act, itemfact.nract, itemfact.id_ctr);*/ - lnIdFact := get_idFact(); -``` - -buclat pe grupuri distincte `(NRACT, DATAACT, DATAIREG, SERIE_ACT, ID_CTR, ID_SET)` extrase din -`ACT_TEMP WHERE ID_FACT = -1` (`:713`) — deci `-1` e sentinela "acest rand cere un `ID_FACT` nou". - -`SCRIE_IN_ACT` insusi e apelat **doar pe drumul de scriere**, niciodata pe cel de stergere — -in `finalizeaza_scriere_act_rul` (`:8449-8459`): - -``` - if tnScrieSterge <> 2 then - pack_contafin.SCRIE_IN_ACT(user); - ... - else - pack_contafin.STERGE_DIN_ACT(user, lnAn, lnLuna, tnCod, tnIdUtil, tnModificareNota); - ... -``` - -Deci `SET_IDFACT` nu se atinge deloc la stergere — confirma ca azi cele doua operatii (stergere + -reemitere) sunt independente unele de altele in privinta ID_FACT. - -**Cine cheama `final_scriere_act_rul_local` / `SCRIE_IN_ACT` din VFP**: acelasi fisier -`COMUN\programe\oscrie_in_fisiere.prg`, replicat identic in ~25-30 produse ROA (verificat prin grep -pe `*.prg` in tot `D:\ROA`): `ROAFACTURARE`, `ROACONT` (output), `ROAGEST`, `ROAIMOB`, `ROAEFACTURA`, -`ROADEVIZE`, `ROAVIN`, `ROACASA`, `ROASAL`, `ROAPRETURI`, `ROAOBINV`, `ROARESTAURANT`, -`ROACOMENZI`, `ROAAPROV`, `ROASITOP`, `ROASITFIN`, `ROADECL`, `ROASALSPEC`, `ROABAVERT`, -`ROACONIMPORT`, `ROAPRINT`, s.a. — plus doua variante care apeleaza `SCRIE_IN_ACT` direct, fara -wrapper-ul local: `CONTAFIN2ORA\VFP2ORA\Programe\oscrie_in_fisiere.prg:367`, -`ROADEFSALARII\COMUN\programe\oscrie_in_fisiere.prg:141`, -`ROARESTAURANTCONFIG\COMUN_ROA\programe\oscrie_in_fisiere.prg:135`. -Un al doilea import vechi (`COMUN\datemenu\xold\import_xdbf\oscrie_in_fisiere.prg`) apare in multe -produse dar e **comentat integral** (`*!*`) — inactiv. - -**Niciun apelant nu impune un `ID_FACT`.** Toti trimit `ID_FACT = -1` in `ACT_TEMP` (via -`sql_temp_insert` in `oscrie_in_fisiere.prg:126-137`, care copiaza direct campurile din cursorul VFP, -inclusiv `id_fact`) si lasa `SCRIE_IN_ACT` sa-l completeze din secventa. Confirmare suplimentara in -`COMUN\clase\omodificari.vc2:4131-4132`: `"daca se schimba partenerul, se pune id_fact = -1 in loc -de 0 pentru a se putea genera un nou id_fact"` — exact sentinela pe care se bazeaza bucla din -`SCRIE_IN_ACT`. - -### A.3 — Variabila/parametru existent pentru un ID_FACT dorit - -**Nu exista azi.** `PACK_CONTAFIN` are o variabila de pachet `nIdFact` (setata de `SET_IDFACT`, -citita de `GET_IDFACT`), dar e scrisa neconditionat de fiecare apel al lui `SET_IDFACT` — nu poate -fi folosita ca "intrare" fara sa se schimbe corpul procedurii. Nu exista niciun `nid_...` global, nici -alt parametru de sesiune care sa transmita un `ID_FACT` dorit lui `SET_IDFACT` sau lui `SCRIE_IN_ACT`. -Ar trebui adaugata o variabila noua de pachet (ex. un "ID_FACT fortat", implicit `NULL`), citita -**doar** in corpul activ al lui `SET_IDFACT`, consumata si resetata la prima folosire — vezi C.8. -Acelasi diagnostic e deja notat in planul de proiect, `plan_13_unificare_formular_facturare.md:1717-1719`: -`"ID_FACT. Se citeste inainte de stergere ... si se impune documentului nou, printr-un comutator -folosit numai de regenerare. SET_IDFACT nu se schimba neconditionat — e cod comun intregii suite."` - -B. DOCUMENTE la reemitere cu acelasi ID_FACT ----------------------------------------------- - -### B.4 — INSERT sau UPDATE/MERGE azi - -`PACK_CONTAFIN.pck:788-817`, in interiorul buclei din `SCRIE_IN_ACT`, cod activ (INSERT simplu): - -``` - -- SCRIE IN DOCUMENTE - lnTvaIncasare := case when itemfact.tva_incasare > 0 then 1 else 0 end; - -- 25.02.2013 : am repus insertul pentru ca se face cumularea id_fact pe listarea din RC/RJ - INSERT INTO DOCUMENTE - (ID_DOC, ID_UTIL, DATAORA, TVA_INCASARE, SERIE_ACT, NRACT, DATAACT, DATAIREG, ID_CTR, ID_SET) - VALUES - (lnIdFact, lnID_Util, LD_DATAORA, lnTvaIncasare, itemfact.serie_act, itemfact.nract, - itemfact.dataact, itemfact.dataireg, decode(itemfact.id_ctr, 0, NULL, itemfact.id_ctr), - itemfact.id_set); - /* - -- modificare ROACONT v 2.4.0 (27.12.2012): am adaugat serie_act, nract, dataact - -- modificare 21.02.2013: am adaugat id_ctr - MERGE INTO DOCUMENTE A - USING (SELECT lnIdFact as ID_DOC, itemfact.serie_act as SERIE_ACT, itemfact.nract as NRACT, - itemfact.dataact as DATAACT, - decode(itemfact.id_ctr, 0, NULL, itemfact.id_ctr) AS ID_CTR - FROM DUAL) B - ON (A.ID_DOC = B.ID_DOC) - WHEN NOT MATCHED THEN - INSERT (ID_DOC, ID_UTIL, DATAORA, TVA_INCASARE, SERIE_ACT, NRACT, DATAACT, ID_CTR) - VALUES (B.ID_DOC, lnID_Util, LD_DATAORA, lnTvaIncasare, B.SERIE_ACT, B.NRACT, B.DATAACT, B.ID_CTR);*/ -``` - -Deci **azi e mereu un INSERT nou** — pe drumul normal (ID_FACT vine din secventa, mereu inexistent -in `DOCUMENTE`) asta e corect, dar pentru cazul S9 (ID_FACT reutilizat, deja existent — chiar si -soft-sters) INSERT-ul ar da eroare de unicitate (vezi B.5). Varianta `MERGE` comentata are doar -`WHEN NOT MATCHED` — daca s-ar reactiva neschimbata, un `ID_DOC` deja existent ar fi pur si simplu -ignorat (fara `WHEN MATCHED`), documentul reemis ramanand cu randul vechi din `DOCUMENTE` -(cu `TVA_INCASARE`/`ID_SET` nemodificate si, mai grav, cu `STERS` neresetat — vezi B.6). - -### B.5 — Constrangere de unicitate pe DOCUMENTE - -DDL gasit in `D:\ROA\DATABASE\ALTELE\Creare_server_scripturi\FirmaNoua\fn_script.sql:5162-5198`: - -``` -CREATE TABLE "DOCUMENTE" ("ID_DOC" NUMBER(20, 0) NOT NULL ENABLE, "DATAORA" DATE NOT NULL ENABLE, - "ID_UTIL" NUMBER(5, 0) NOT NULL ENABLE, "STERS" NUMBER(1, 0) NOT NULL ENABLE, - "DATAORAS" DATE, "ID_UTILS" NUMBER(5, 0) NOT NULL ENABLE) ... -... -CREATE UNIQUE INDEX "PK_DOCUMENTE" ON "DOCUMENTE" ("ID_DOC") ... -ALTER TABLE "DOCUMENTE" ADD CONSTRAINT "PK_DOCUMENTE" PRIMARY KEY ("ID_DOC") USING INDEX ... ENABLE -``` - -**Da: `ID_DOC` e PRIMARY KEY, unic.** (Coloanele `SERIE_ACT/NRACT/DATAACT/DATAIREG/ID_CTR/ID_SET/ -TVA_INCASARE` din INSERT-ul de la B.4 nu apar in acest script — script vechi de firma noua, -probabil tabela a fost alterata ulterior cu `ALTER TABLE ADD COLUMN`; constrangerea de PK insa e -stabila si nu are motiv sa fi fost scoasa.) Un `INSERT INTO DOCUMENTE (ID_DOC=...)` cu o valoare de -`ID_DOC` care exista deja (chiar si `STERS=1`) arunca `ORA-00001: unique constraint (PK_DOCUMENTE) -violated`. Nu am gasit alta constrangere de unicitate pe `DOCUMENTE`; nici FK de la `ACT.ID_FACT` -catre `DOCUMENTE.ID_DOC` (`ACT.ID_FACT` e doar indexat, `IDX_ID_FACT`, `fn_script.sql:1637`, fara -constrangere de integritate referentiala gasita) — deci reinserarea de randuri in `ACT` cu -`ID_FACT` reutilizat **nu** e blocata de vreo constrangere pe `ACT` insusi; blocajul e exclusiv pe -`DOCUMENTE`. - -### B.6 — Randurile vechi la stergere: fizic sterse, STERS=1, sau raman - -**Soft-delete pur, pe toate cele patru tabele relevante — nimic nu se sterge fizic.** - -`STERGE_DIN_ACT` (`PACK_CONTAFIN.pck:1815-1859`), apelata din `finalizeaza_scriere_act_rul` cand -`tnScrieSterge = 2`: - -``` - PROCEDURE STERGE_DIN_ACT(V_GCS VARCHAR2, tnAn in number, tnLuna in number, tnCod in number, - tnId_utils in number, tnTip IN NUMBER) is - -- tnTip : 0 = modificare ; 1 = stergere - ... - BEGIN - ... - UPDATE /*+ index(ACT IDX_COD) */ ACT - SET STERS = 1, DATAORAS = LD_DATAORA, ID_UTILS = lnId_util - WHERE COD = tnCod and an = tnAn and luna = tnLuna; - - IF lnTip = 1 THEN - UPDATE DOCUMENTE - SET STERS = 1, ID_UTILS = LNID_UTIL, DATAORAS = LD_DATAORA - WHERE ID_DOC IN - (SELECT /*+ index(ACT IDX_COD) */ DISTINCT ID_FACT - FROM ACT - WHERE COD = tnCod and an = tnAn and luna = tnLuna - AND ID_FACT <> 0 - and ID_SET not in (90501, 90021) - AND NOT (SCD = '4426' AND SCC = '4428') - AND NOT (SCD = '4428' AND SCC = '4427')); - END IF; - - update act_temp set suma = -suma, suma_val = -suma_val; - END STERGE_DIN_ACT; -``` - -`ACT` -> `STERS=1` (randurile raman fizic, pe acelasi `COD`). Cand `tnTip=1` (stergere, nu simpla -modificare), **`DOCUMENTE` primeste si el `STERS=1`**, pentru toate `ID_FACT` distincte gasite pe -acel `COD` in `ACT` (cu exceptiile de mai sus pentru seturi/conturi speciale). Nicaieri nu am gasit -un `UPDATE DOCUMENTE ... SET STERS = 0` — cautare exhaustiva in `PACK_CONTAFIN.pck` (`grep -i -"UPDATE DOCUMENTE"`) a dat un singur rezultat, cel de mai sus. Deci nu exista azi niciun mecanism -care sa "reinvie" un rand din `DOCUMENTE`. - -La fel, `STERGE_DIN_RUL` / `STERGE_DIN_RUL_OBINV` (`:1861-1893`) fac `UPDATE ... SET STERS = 1` pe -`RUL`/`RUL_OBINV`, fara stergere fizica. - -Pentru `IREG_PARTENERI` si `JV2007` situatia e diferita in mod util pentru S9: acestea nu sunt copii -1:1 ale documentului, ci **agregate recalculate prin `MERGE`** pe chei de business (an, luna, cont / -`ID_FDOC`, `ID_FACT`, `NRACT`, `SERIE_ACT`, `DATAACT`, `DATAIREG`, `ID_PART`, ...) — vezi -`SCRIE_JV_2007` (`:3328-3373+`) si `SCRIE_IN_IREG_PARTENERI`/`EXECUTA_SCRIE_IN_IREG` (`:7030+`, -`:7607+`). Ambele sunt apelate **si pe drumul de scriere, si pe cel de stergere** -(`finalizeaza_scriere_act_rul:8495-8578`, fara conditie pe `tnScrieSterge` in afara sursei: `act_temp` -la scriere/stergere, `act` la refacere). La stergere, `sterge_document` (`:7699-8118`) reinsereaza in -`ACT_TEMP` copii ale randurilor vechi din `ACT`/`RUL`/`RUL_OBINV`, **cu acelasi `ID_FACT` ca inainte** -(coloana `id_fact` e copiata direct din sursa, nu resetata la `-1`), asa ca `MERGE`-ul din -`SCRIE_JV_2007`/`SCRIE_IN_IREG_PARTENERI` recalculeaza (compenseaza) exact randul cu acel `ID_FACT` -— nu creeaza duplicate. Deci reutilizarea `ID_FACT`-ului **nu produce dubluri in `JV2007`/ -`IREG_PARTENERI`**; problema e strict izolata la `DOCUMENTE`. - -Nota din plan, care confirma independent aceasta zona de risc: -`plan_13_unificare_formular_facturare.md:1737`: `"De verificat si daca DOCUMENTE primeste un al -doilea rand pe acelasi ID_DOC sau il refoloseste."` — raspunsul, dupa aceasta cercetare: **niciuna -din cele doua, in starea actuala a codului — ar arunca eroare de unicitate**, nu ar produce liniste -tacuta cu duplicat sau refolosire. - -C. Verdictul care conteaza ----------------------------- - -### C.7 — Se poate reemite cu acelasi ID_FACT fara sa se strice nimic? - -**DA, dar cu conditii** — niciuna dintre ele nu e implementata azi: - -1. **Nu folosi varianta comentata a lui `SET_IDFACT` ca atare.** Cautarea `STERS=0` nu gaseste - documentul (deja sters soft inainte de rescriere) si e vulnerabila la coliziuni pe chei de - business partajate cu alt document. In loc de cautare, ID_FACT-ul trebuie **citit explicit - inainte de stergere** (VFP il are deja in cursorul documentului editat) si **transmis** pe drumul - de scriere — exact ce zice planul S9. -2. **`SET_IDFACT` are nevoie de o cale de intrare noua, inerta implicit.** O variabila de pachet - noua (ex. "ID_FACT fortat", `NULL` default) + o procedura noua de setat-o explicit inainte de - scriere; corpul activ al `SET_IDFACT` verifica intai variabila, o consuma si o reseteaza; daca e - `NULL` (cazul general — toate celelalte ~25-30 apeluri din suita), comportamentul e identic cu - azi (secventa). Nu se toarna in semnatura existenta `SET_IDFACT(V_GCS)`, ca sa nu se schimbe - apelul de la niciun alt caller. -3. **Scrierea in `DOCUMENTE` trebuie sa devina un upsert real, cu `WHEN MATCHED`.** Nici INSERT-ul - activ, nici MERGE-ul comentat (fara `WHEN MATCHED`) nu revigoreaza un rand soft-sters. E nevoie de - un `MERGE ... WHEN MATCHED THEN UPDATE SET STERS = 0, DATAORA = ..., ID_UTIL = ..., TVA_INCASARE - = ..., SERIE_ACT = ..., NRACT = ..., DATAACT = ..., DATAIREG = ..., ID_CTR = ..., ID_SET = ...` - pe langa `WHEN NOT MATCHED THEN INSERT` (cazul normal, ID_FACT nou din secventa). -4. **Succesiunea corecta**, in aceeasi tranzactie (deja planificata in S9 la nivel de VFP — vezi - `plan_13...md:1713-1719`, mutarea stergerii in tranzactia deschisa de scriere): sterge documentul - vechi (soft-delete, ca azi) -> seteaza variabila de la punctul 2 cu `ID_FACT`-ul citit inainte de - stergere -> scrie documentul nou prin drumul obisnuit (`ACT_TEMP` cu `ID_FACT = -1` ca de obicei, - dar acum grupul respectiv va primi ID_FACT-ul fortat in loc de unul nou) -> commit doar dupa ce - ambele operatii au avut succes; orice eroare -> rollback total, documentul vechi ramane intact. - -- **NU** (fara aceste conditii): `INSERT INTO DOCUMENTE` cu `ID_DOC` reutilizat, cat timp randul vechi - e inca prezent cu `STERS=1`, arunca `ORA-00001` pe `PK_DOCUMENTE` — asta e dovada, nu presupunere - (B.5 + B.4). - -### C.8 — Suprafata de risc pentru restul suitei - -- **~25-30 cai de apel** trebuie sa ramana neafectate: toate copiile `COMUN\programe\ - oscrie_in_fisiere.prg` din fiecare produs ROA (listate in A.2), plus cele 3 variante care apeleaza - `SCRIE_IN_ACT` direct. Toate trec prin acelasi punct final: `SET_IDFACT(V_GCS)` in - `PACK_CONTAFIN.pck:3037`. -- **Garantia structurala propusa**: variabila de pachet noua, default `NULL`/inert, citita **doar** - in interiorul corpului activ al lui `SET_IDFACT` (nu in semnatura, nu in vreun parametru propagat - de apelanti); se seteaza explicit **doar** de codul de regenerare din ROAFACTURARE, chiar inainte - de a incepe scrierea documentului reemis, si se consuma/reseteaza la prima citire (fie in - `SET_IDFACT`, fie la finalul tranzactiei, ca sa nu "scurgi" valoarea catre urmatoarea scriere - neinrudita din aceeasi sesiune Oracle — relevant daca `goExecutor`/conexiunea e reutilizata intre - operatii ale aceluiasi utilizator in acelasi produs). Cu default inert si citire localizata strict - in corpul lui `SET_IDFACT`, toate celelalte ~25-30 cai raman byte-for-byte identice cu azi — nu e - nevoie sa se verifice fiecare apelant individual, garantia e la sursa (variabila neinitializata = - comportament vechi). -- **Riscul real nu e in `SET_IDFACT`, ci in `DOCUMENTE`.** Schimbarea INSERT->MERGE cu `WHEN MATCHED` - la `PACK_CONTAFIN.pck:796-817` e riscanta pentru toata suita in alt sens: `WHEN MATCHED` s-ar - declansa doar cand `ID_DOC` (generat din secventa) coincide cu unul existent — ceea ce, pe drumul - normal, nu se intampla niciodata (secventa e monoton crescatoare, nu se repeta), deci practic - ramura noua e inerta pentru toti apelantii actuali si activa doar cand variabila de la C.8 a fost - setata explicit. Totusi orice modificare la acest INSERT/MERGE e in cod comun apelat de toata - suita si trebuie testata pe cel putin un ciclu normal de scriere (fara variabila fortata) in - fiecare produs care scrie facturi/note, nu doar in ROAFACTURARE. - -Ramas de verificat pe baza vie --------------------------------- -- Structura curenta reala a tabelei `DOCUMENTE` (DDL-ul citat e dintr-un script vechi de "firma - noua"; coloanele `SERIE_ACT/NRACT/DATAACT/DATAIREG/ID_CTR/ID_SET/TVA_INCASARE` folosite de - INSERT-ul activ nu apar in acel DDL — probabil adaugate ulterior prin `ALTER TABLE`). De rulat pe - baza vie: `SELECT column_name, nullable FROM user_tab_columns WHERE table_name = 'DOCUMENTE' - ORDER BY column_id;` si `SELECT constraint_name, constraint_type FROM user_constraints WHERE - table_name = 'DOCUMENTE';` — sa se confirme ca `PK_DOCUMENTE` e inca activa si ca nu exista alta - constrangere aparuta intre timp. -- Daca exista deja documente reale in productie unde `DOCUMENTE.ID_DOC` a fost, intr-un fel sau - altul, reutilizat (ex. printr-o interventie manuala) — de verificat cu - `SELECT id_doc, count(*) FROM documente GROUP BY id_doc HAVING count(*) > 1;` (ar trebui sa fie - gol, dat fiind PK-ul, dar merita confirmat inaintea oricarei schimbari). -- Comportamentul exact al `finalizeaza_stergere_nota`/`finalizeaza_modificare_nota` (`:8601+`, - nu au fost citate integral aici) fata de `ID_FACT`/`ID_FACTD` — relevante daca reemiterea schimba - seria/numarul, nu doar continutul (cazul S9 "de baza" e cel mai simplu: acelasi NRACT/SERIE_ACT). -- Comportamentul lui `EXECUTA_SCRIE_TVA`/`SCRIE_JC_2007` (mentionate la `:8504-8540`, cazul an < - 2007 sau JC) fata de reutilizarea ID_FACT — nu au fost verificate in detaliu, doar `SCRIE_JV_2007`. -- Nu am putut testa efectiv (fara acces la baza vie) daca `ORA-00001` e chiar eroarea ridicata de - Oracle in acest scenariu exact — e o deductie directa din DDL (PK unic + INSERT simplu pe coloana - respectiva), dar merita o rulare de proba pe o baza de test inainte de a proiecta solutia finala. diff --git a/docs/cercetare/idpol_comanda_contract.md b/docs/cercetare/idpol_comanda_contract.md deleted file mode 100644 index 3b108a2..0000000 --- a/docs/cercetare/idpol_comanda_contract.md +++ /dev/null @@ -1,553 +0,0 @@ -# De unde vine SCC-ul pe facturile din comandă/contract — și dacă modelul se poate refolosi pentru #13 - -Cercetare read-only, pe cod (VFP text + export `PACK_FACTURARE` curent) și pe baza vie (`MARIUSM_AUTO` -pe `ROA_CENTRAL`, doar `SELECT`, 10.08.2026). Nu modifică nimic — nici `pack_facturare`, nici -`ofacturare_comun.vc2`, nici `ofacturare_editare.prg`. - -Continuă `docs\cercetare\coresp_cont_venchelt.md` (secțiunea 9) și `docs\plan_13_unificare_formular_facturare.md` -(J-quater), care stabiliseră deja: FACT-024 verifică apartenența articolului la politica **de pe linie**, -`id_pol` e parametru per-linie trimis de VFP (`V_ID_POL`, `adauga_articol_factura`), și pe contract -articolul „liber" nu e de fapt fără politică — vine deja cu una atașată prin `cursor_contract`/`cursor_preturi`. - -**Runda 2 (reformulare Marius):** prima trecere de mai jos (secțiunile D–C, păstrate ca dovadă) presupunea -că întrebarea era „de unde vine `id_pol`". Marius a corectat premisa: el chiar emite facturi pe bază de -document care **nu au** politică de preț și notă atașată **prin lanțul obișnuit** — ceea ce contrazicea -concluzia inițială („`id_pol` gol → `FACT-024`, mereu"). Secțiunea 0 de mai jos reconciliază contradicția, -pe cod și pe date reale, și e livrabilul principal al acestei runde. Sursa SQL folosită în ambele runde: -`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` (823.337 octeți — fișierul -cu același nume din `docs\` a fost șters în timpul sesiunii anterioare; numerele de linie de mai jos sunt -verificate direct pe fișierul din `SCRIPTURI_CLAR`, nu preluate din plan). - ---- - -## 0. Reconcilierea contradicției — Marius are dreptate, dar mecanismul nu e cel presupus în runda 1 - -**Verdict, cu citat**: `id_pol` `NULL` **tot** blochează `contabilizeaza_articol` necondiționat — asta nu -s-a schimbat, e reconfirmat mai jos. Ce s-a stabilit greșit în runda 1 e presupunerea că **orice** linie de -document trece prin `contabilizeaza_articol`. **Nu e adevărat**: pachetul are cel puțin **două rute -paralele**, folosite azi în producție (confirmate pe date reale), care scriu o notă contabilă de vânzare -**fără `id_pol` și fără `CRM_POLITICI_PRETURI`** — pentru că nu apelează deloc `contabilizeaza_articol`. - -### 0a. Reconfirmare: `contabilizeaza_articol` nu tolerează `id_pol NULL`, în nicio ramură - -```sql --- ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:7275-7302 (inceputul functiei, necondiționat) -BEGIN - BEGIN - SELECT COMPUS, ID_POL_ART - INTO V_COMPUS, V_ID_POL_ART - FROM VCRM_POLITICI_PRET_ART - WHERE ID_ARTICOL = detalii_articol.id_articol - AND ID_POL = detalii_articol.id_pol; -- id_pol NULL => X=NULL => NO_DATA_FOUND, mereu - EXCEPTION - WHEN NO_DATA_FOUND THEN - ... SELECT NUME_LISTA_PRETURI INTO lcPolitica FROM CRM_POLITICI_PRETURI WHERE id_pol = detalii_articol.id_pol; - RAISE_APPLICATION_ERROR(-20000, '... (FACT-024)'); -``` -Nu există niciun `IF detalii_articol.id_pol IS NULL THEN ...` înainte de acest bloc — e primul lucru pe -care funcția îl face, necondiționat, pentru orice apel. **Bonus, deja semnalat în runda 7** (`plan_13... -md:1451-1457`, reconfirmat aici pe fișierul curent): al doilea `SELECT` din `EXCEPTION` (cel care aduce -`lcPolitica` pentru mesaj) filtrează tot pe `id_pol = detalii_articol.id_pol`, deci și el dă `NO_DATA_FOUND` -când `id_pol` e `NULL` — utilizatorul nu vede mesajul formatat „FACT-024", ci un `ORA-01403` brut, -necaptat. În ambele cazuri: **eroare, tranzacție întreruptă, nimic scris**. Deci dacă o linie chiar ajunge -la `contabilizeaza_articol` cu `id_pol` gol, nu există azi nicio cale silențioasă de succes. - -### 0b. Ce am găsit real, pe date vii — 384 din 1113 linii `VANZARI_DETALII` active (34%) au `ID_POL` `NULL` - -```sql -select count(*), sum(case when id_pol is null then 1 else 0 end) from vanzari_detalii where sters=0; --- 1113 384 -``` -Deci liniile facturate fără `id_pol` **există masiv** în producție — nu e un caz exotic. Împărțite pe -`VANZARI.TIP`: - -| `TIP` | linii fără `id_pol` | din ele, rânduri `ACT` cu `SCC` populat | interpretare | -|---|---|---|---| -| `-12` | 97 | 588/604 | **ROAAUTO** (confirmat, `tip=-12` e explicit ROAAUTO — `plan_13...md:1710,1735`) | -| `-1..-13` (restul) | ~140 | majoritar populat | variante storno/aviz ale documentelor de mai jos, nu cercetate individual | -| `2` (**contract**) | 19 | populat, vezi 0c | facturare pe bază de contract, **rată/scadențar**, nu articol | -| `1` (listă prețuri) | 1 | — | un singur caz, neexplorat separat | -| `51` | 121 | 179/275 (cumulat) | tip necunoscut în codul VFP citit — posibil retail/POS, neidentificat | -| **`3` (comandă, ROAFACTURARE)** | **0 din 66** | — | **niciun caz** — vezi 0d | - -`select count(*), sum(case when id_pol is null then 1 else 0 end) from vanzari_detalii d join vanzari v on v.id_vanzare=d.id_vanzare where d.sters=0 and v.sters=0 and v.id_comanda is not null` → **66 / 0**. Pe ruta -`COMENZI` a lui ROAFACTURARE (secțiunea A de mai jos), **`id_pol` nu e niciodată gol, pe datele disponibile.** -Contradicția lui Marius nu se reproduce pe acest obiect precis. - -### 0c. Mecanismul găsit: `contabilizeaza_rata` — notă contabilă direct din contract, fără politică, fără `id_pol` - -Contractele cu facturare pe **rate/scadențar** (`CONTRACTE.OPT_FACTURARE IN (1,2)`) nu trec liniile prin -`contabilizeaza_articol` — trec prin o funcție **separată**, `contabilizeaza_rata`: -```sql --- ff_...:7549-7597 -FUNCTION contabilizeaza_rata(detalii_rata VANZARI_DETALII_TEMP%ROWTYPE) RETURN NUMBER IS -BEGIN - BEGIN - SELECT NVL(B.ID_VENCHELT, pack_facturare.nid_venchelt), ..., C.SCD, C.ASCD, C.SCC, C.ASCC, C.CU_TVA, C.IN_VALUTA - INTO ... - FROM CONTRACTE A - LEFT JOIN CRM_NOTE_VANZARI B ON A.ID_NOTA = B.ID_NOTA -- <- nota vine din CONTRACTE.ID_NOTA, NU din id_pol - LEFT JOIN NOTE_CONTABILE C ON B.ID_SET = C.ID_SET - WHERE A.STERS = 0 AND A.ID_CTR = detalii_rata.id_ctr AND B.STERS = 0; - EXCEPTION - WHEN NO_DATA_FOUND THEN - RAISE_APPLICATION_ERROR(-20000, 'Nu sunt configurate notele de vanzare pentru acest contract!'); - END; - ... - V_INCASAT_CALCUL := V_INCASAT_CALCUL + pack_facturare.scrie_nota(..., V_SCD, V_ASCD, V_SCC, V_ASCC, ...); -``` -`CONTRACTE` are coloană proprie `ID_NOTA` (confirmat pe schema vie: `NUMBER`, nullable) — **contractul însuși -duce nota contabilă**, ales o singură dată la configurarea lui, independent de orice `id_pol`/politică de -preț per articol. Confirmă exact tiparul din `cursor_contract` (secțiunea B mai jos, ramura „rată" a -cursorului, `ff_...:2837-2893`): `NULL as id_articol, ..., NULL AS ID_POL` — liniile de rată **nu au -niciodată `id_pol`, prin construcție**, pentru că nu sunt articole din nomenclator, sunt rate de scadențar. - -**Confirmat pe o factură reală** (`MARIUSM_AUTO`, 10.08.2026): -```sql --- id_vanzare 360, tip 2 (contract), linia din VANZARI_DETALII: id_articol NULL, id_pol NULL --- ACT pentru id_fact=5039903: --- ID_ACT SCD ASCD SCC ASCC SUMA EXPLICATIA --- 102204 4111 11 704 300 RATA 1 --- 102205 4111 11 4427 57 TVA RATA 1 -``` -O factură reală, cu o linie fără `id_articol` și fără `id_pol`, **cu notă contabilă scrisă corect** (`SCD -4111`, `SCC 704`) — exact dovada cerută. Mecanismul: **nota nu vine prin politică deloc**, vine direct din -`CONTRACTE.ID_NOTA`, o singură dată per contract, la configurare — un tipar „un document duce o notă fixă", -nu „fiecare linie duce o politică de preț". - -### 0d. De ce nu se reproduce pe `COMENZI` (ROAFACTURARE): tabelul nu are echivalentul lui `ID_NOTA` - -```sql -select column_name from user_tab_columns where table_name='COMENZI'; --- ID_COMANDA, NR_COMANDA, DATA_COMANDA, ID_PART, ID_GESTIUNE, ID_SECTIE, ID_SECTIE2, ID_CTR, ... (29 coloane) --- nicio ID_NOTA, nicio ID_POL, nicio coloana de contabilizare -``` -`COMENZI` (antetul) nu duce nicio informație contabilă — singura legătură posibilă e `ID_CTR` (dacă -comanda vine dintr-un contract). `COMENZI_ELEMENTE.ID_POL` rămâne singurul canal, și e `NOT NULL` (secțiunea -A). Deci pe **acest** obiect, mecanismul „notă fără politică" descris de Marius **nu există** — dacă -experiența lui vine de aici, ar însemna fie (a) o versiune de bază/schemă diferită de `MARIUSM_AUTO`, fie -(b) o factură pe care a facturat-o efectiv prin ruta **contract cu rate** (secțiunea 0c) sau prin **ROAAUTO** -(`tip -12`, deviz — tipar deja documentat în `coresp_cont_venchelt.md` §9b: `adauga_articol_factura_deviz` -→ `scrie_in_vanzari`, fără `contabilizeaza_articol`, cu notă scrisă separat de fiecare produs), și a numit-o -generic „factură pe bază de comandă". **Nu pot decide între aceste ipoteze fără să întreb** — dovada de cod -și de date arată clar CARE mecanisme există și niciunul nu e pe `COMENZI` propriu-zis. - -### 0e. Variantele din brief, verdict pe fiecare - -- *„liniile de comandă primesc totuși un `id_pol` de undeva"* — **confirmat, dar nu ascuns**: da, primesc, - documentat deja în secțiunea A/D veche, e vizibil în UI (`v_articole`), nu explică „fără politică". -- *„`contabilizeaza_articol` nu e apelată pe ruta comandă, ci altă procedură"* — **fals pentru `COMENZI`** - (`ofacturare.prg:292-293` → `cursor_comanda` → `crsarticole` → `do_scrie_articole` → `adauga_articol_factura` - → `contabilizeaza_articol`, ruta normală); **adevărat pentru contract-cu-rate** (`contabilizeaza_rata`) și - pentru ROAAUTO/ROAACNPRO (proceduri proprii, deja documentate). -- *„există o ramură de fallback în `contabilizeaza_articol` care ajunge la notă fără politică"* — **fals**, - reconfirmat la 0a; funcția nu are asemenea ramură, blochează necondiționat. -- *„FACT-024 se ridică doar când `id_pol` e nenul dar articolul nu e membru, `id_pol` nul merge pe alt - drum"* — **fals ca „alt drum în aceeași funcție"**; **adevărat ca „alt drum = altă funcție"** (0c/0d). - ---- - -## D. Răspunsul la întrebarea lui Marius, în cinci rânduri (actualizat după reconciliere) - -**Depinde care „comandă".** Pe obiectul `COMENZI`/`COMENZI_ELEMENTE` al ROAFACTURARE (facturare „pe bază -de comandă", tip 3), SCC-ul vine tot din `NOTE_CONTABILE.SCC` prin `id_pol` — **`id_pol` nu lipsește -niciodată**, e coloană `NOT NULL` (confirmat pe schemă și pe date: 0 din 6868/66 linii fără el), populată la -adăugarea articolului dintr-o listă deja restrânsă la o singură politică (`v_articole`/`com_vpreturi_utilizator`), -politică aleasă o dată pe comandă. **Dacă experiența lui Marius vine din facturarea contractelor cu rate -(scadențar, `OPT_FACTURARE IN (1,2)`) sau din ROAAUTO**, atunci da, există o rută reală, azi în producție, -care scrie nota **fără nicio politică de preț**: pe contracte-cu-rate, nota vine direct din -`CONTRACTE.ID_NOTA` (o coloană pe contract, aleasă o singură dată la configurarea lui, independentă de orice -`id_pol` per linie) — `contabilizeaza_rata`, nu `contabilizeaza_articol`. Pe ROAAUTO/ROAACNPRO, nota o scrie -o procedură proprie a produsului (`pack_acn.salveaza_regdoc` etc.), tot în afara lanțului `CRM_POLITICI_PRETURI`. -**Aceste rute nu sunt „`id_pol` gol tratat cu grijă" — sunt căi care nu ating deloc `id_pol`/`contabilizeaza_articol`.** -Pe contract-cu-**articole** (nu rate), tiparul rămâne cel din runda 1: `CTR_ARTICOLE.ID_POL_ART` → membru -al unei politici reale, ca și pe comandă. - -## E. Se poate refolosi „același model" pentru articolul ad-hoc din #13? - -**Parțial. Mecanismul de jos (RPC + „apartenența e garantată prin construcția listei") e direct -transplantabil — dar e deja exact ce propune rețeta în 4 pași din J-quater, nu o alternativă mai simplă -la ea. Partea care ar simplifica cu adevărat lucrurile — „ia pur și simplu politica curentă și adaugă -articolul în ea" — e decizia 24, deja retrasă de Marius, din motive care rămân valabile.** - -Detaliat: - -1. **Ce arată comandă/contract, tehnic**: operatorul nu alege niciodată `id_pol` pentru un articol anume — - alege **o politică** (una dintre cele la care are drept, via `UTILIZATORI_ROL_INTERN` → - `POLITICI_GRUPURI` → `CRM_POLITICI_PRETURI`), iar din acel moment orice articol afișat spre alegere e - deja membru al ei (`com_vpreturi_utilizator`/`cPol_pret_art` filtrează `id_pol_art`/`id_pol IS NOT NULL` - la sursă). Verificarea FACT-024 „trece" pe comandă/contract **nu pentru că ar exista un fallback** — ci - pentru că nu există cale, în UI, de a ajunge la un articol care nu e deja membru. E o garanție de - construcție a listei, nu o validare separată. -2. **Acest tipar exact e deja documentat, ca fezabil, în `coresp_cont_venchelt.md` secțiunea „e)" / J-quater - punctul 3** — RPC `pack_preturi.adauga_politica_pret_art` (deja folosit în producție, - `ofacturare.vc2:15551-15587`) pentru „asigură apartenența", plus trimiterea lui `id_pol` neschimbat la - `adauga_articol_factura`. Nu e nimic nou de inventat aici — comanda/contractul confirmă că tiparul - „RPC + listă pre-filtrată" funcționează în producție de multă vreme, dar **planul A din J-quater deja îl - folosește**. Refolosirea nu elimină pasul, îl confirmă. -3. **Ce NU rezolvă modelul comandă/contract e alegerea SCC-ului corect per articol** — pe comandă/contract - politica e **una singură, fixă**, aleasă pentru tot documentul/sesiunea, cu un singur `SCC` (orice ar - fi el) pentru toate articolele adăugate așa. Aplicat identic pe `caut_articol` (restricționează - rezultatele căutării din nomenclator la politica curentă a operatorului), rezultatul ar fi - **exact ceea ce există deja azi ca „Cauta in lista de preturi…"** — nu rezolvă cazul pe care S4g/#13 îl - cere explicit: un articol care **nu e membru al niciunei politici** (`plan_13...md:1711-1716`: „din - nomenclator se oferă și articole gestionabile, și negestionabile"; „un articol fără `ID_POL` nu ajunge - la cont gol, ci la eroare"). -4. **„Alege o politică fixă și pune articolul în ea" e literalmente decizia 24, deja retrasă** - (`plan_13...md:926-930`): *„Decizia 24: articolul ales din nomenclator primește `id_pol`-ul unei - politici de pret implicite. RETRASĂ în runda 8... prețul lui era o întrebare de configurare cu - contabilul (care politică, cu ce `SCC`, una sau mai multe) și un cont de venit uniform pentru orice - articol adăugat așa. Marius a ales în loc derivarea contului. Nu se reintroduce."* Motivul retragerii - nu s-a schimbat: o politică unică (fie ea „a operatorului", ca pe comandă) dă un **singur** cont de - venit oricărui articol, indiferent de natura lui (marfă/producție/serviciu) — exact opusul deciziei 27, - care cere `SCC` diferențiat după clasa contului de gestiune al articolului (`707`/`711`/`702`/`703`/ - `7015`/`7018`/`704`). -5. **Deci ce rămâne de făcut e exact pasul central din rețeta în 4 pași**: nu „cum trimit un `id_pol` - valid la Oracle fără să ating `pack_facturare`" (asta e deja rezolvat, și demonstrat funcțional de - comandă/contract) — ci „**care** politică, cu **care** `SCC`, pentru **acest** articol anume" — acolo - comanda/contractul nu ajută, pentru că ele nu rezolvă niciodată această întrebare: politica le vine - gata aleasă, de operator, o singură dată, nu calculată per articol. - -**Verdict la „mă gândeam la același model" (dacă modelul e comandă/contract-cu-articole)**: da, la nivel de -mecanism (RPC de inserare + membership garantat prin listă filtrată) — dar acel nivel era deja acoperit de -planul A/decizia 27-bis. Nu, la nivelul la care ar simplifica ceva („nu mai calculăm SCC per articol, luăm -politica gata aleasă") — pentru că acela e exact decizia 24, respinsă cu un motiv care rămâne valabil. - -### E-bis. Dar există un al treilea model, mai simplu — cel din `contabilizeaza_rata` (secțiunea 0c) - -Reconcilierea de la secțiunea 0 arată un tipar pe care raportul din runda 1 nu-l luase în calcul: **un -document poate duce el însuși o notă contabilă fixă, complet în afara `CRM_POLITICI_PRETURI`, iar -`pack_facturare` are deja o funcție care face exact asta** (`contabilizeaza_rata`, sursa `CONTRACTE.ID_NOTA`). -Aplicat la #13, ideea ar fi: nu mai deriva `SCC` per articol și nu mai caut/construi o politică tehnică — scrie -nota direct, cu conturile calculate în VFP (regula deciziei 27: `CORESP_CONT_VENCHELT`/`NOM_ARTICOLE.CONT`/ -`704`), printr-o cale de scriere **paralelă**, ca și pentru rate. - -**De ce nu se poate fără să ating `pack_facturare`, și asta contează, dat fiind decizia 27-bis**: -- `contabilizeaza_rata` există deja, dar e legată strict de `VANZARI_DETALII_TEMP` cu semantică de „rată de - contract" (`detalii_rata.id_ctr` obligatoriu în interogare) — nu e apelabilă pentru un rând de articol - obișnuit (`id_articol` populat, `id_pol` gol) fără o funcție **nouă**, analogă, în `pack_facturare`. -- Sursa notei la rată e `CONTRACTE.ID_NOTA` — o coloană pe un document care **există deja** și **se - configurează o dată** (la crearea contractului). Pentru articolul ad-hoc de pe formularul unificat nu - există azi un asemenea „document-sursă cu notă fixă" — ar trebui inventat (ex. o coloană similară pe - antetul facturii, sau parametru nou la `adauga_articol_factura`) — tot cod nou în `PACK_FACTURARE`. -- **Constrângerea „pack_facturare rămâne neatins" (decizia 27-bis) exclude explicit acest drum.** Dacă - Marius e dispus s-o relaxeze, tiparul `contabilizeaza_rata` e un precedent real, mai simplu decât rețeta - în 4 pași — pentru că elimină complet nevoia unei „politici tehnice" și a RPC-ului de apartenență - (`pack_preturi.adauga_politica_pret_art`): SCC-ul ar merge direct în `scrie_nota`, fără ocolul prin - `CRM_POLITICI_PRET_ART`. Dacă nu, rămâne exact ce spunea verdictul din runda 1: rețeta în 4 pași (planul - A, tot în VFP) rămâne singura variantă compatibilă cu constrângerea. - -**Verdict final, actualizat**: modelul comandă/contract (runda 1) nu ajută, motivele rămân cele deja arătate. -Dar există un al doilea model real, comandă**-rate**/contract-rate, care **ar** ajuta — cu prețul de a -renunța la „`pack_facturare` neatins". E o decizie de produs pentru Marius, nu o concluzie tehnică unică — -raportul pune ambele opțiuni pe masă, cu costul lor exact. - -**Corecție importantă, din verificarea independentă de mai jos („Completare de la sesiunea principală"):** -politica `7 DISCOUNT` (870 linii de comandă, `SCD=667`/`SCC=4111`, notă `2 DISCOUNT`) e deja, în producție, -o **politică tehnică folosită doar ca rutare contabilă**, nu ca listă comercială — exact tiparul „o politică -per scop contabil" pe care planul A/decizia 27-bis îl propune (nu decizia 24, care era o politică unică -pentru *orice* articol ad-hoc, indiferent de natură). Deci precedentul operațional pentru „politică tehnică, -una per destinație contabilă" **există deja pe ruta comandă** — mai puternic decât `gnId_pol_pret_stoc` -(care e un singur cont pentru tot stocul). **Dar** aceeași verificare arată că garanția „apartenență prin -construcția listei" (punctul 1 de mai sus) **nu e etanșă pe date**: 37 din 6868 linii active de comandă au -un articol care nu (mai) e membru al politicii înregistrate pe linie — verificare, cauză și comportament la -facturare, deschise, vezi secțiunea finală. - ---- - -## A. Ruta COMANDĂ - -### A.1 — `cursor_comanda` selectează `ID_POL`? Da, direct de pe linia comenzii - -```sql --- D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:2996-3054 (ramura factură) -OPEN V_CURSOR FOR - SELECT ROWNUM as id_c, - A.ID_ARTICOL, - NULL AS LOT, - NULL as SERIE, - A.ID_POL, -- <- direct din COMENZI_ELEMENTE, nu derivat - A.ID_VALUTA, ... - FROM COMENZI_ELEMENTE A - LEFT JOIN CRM_POLITICI_PRET_ART B - ON A.ID_POL = B.ID_POL AND A.ID_ARTICOL = B.ID_ARTICOL - ... - WHERE A.STERS = 0 AND A.ID_COMANDA = V_ID_COMANDA ... -``` -(identic la `:3084-3140` pentru ramura aviz, `V_TIP > 20`). `A.ID_POL` e coloană directă pe -`COMENZI_ELEMENTE` — `LEFT JOIN CRM_POLITICI_PRET_ART B` de mai jos **nu** derivă `id_pol`, doar aduce -`DISCOUNT_UNITAR`/`PROC_TVAV` pentru acea combinație `(id_pol, id_articol)`. - -### A.2 — Unde e stocat: `COMENZI_ELEMENTE.ID_POL`, coloană `NOT NULL` - -Confirmat pe schema vie (`MARIUSM_AUTO`, `ROA_CENTRAL`, 10.08.2026): -```sql -select column_name, data_type, nullable from user_tab_columns where table_name='COMENZI_ELEMENTE'; --- ID_POL NUMBER N <- NOT NULL -select count(*), sum(case when id_pol is null then 1 else 0 end) from comenzi_elemente where sters=0; --- 6868 0 -``` -**`ID_POL` e obligatoriu la nivel de constrângere de tabel**, nu doar „de obicei populat" — și cele 6868 -linii active din baza vie confirmă 0 excepții. E o coloană de **linie**, nu de antet (nu există `ID_POL` -pe `COMENZI`). - -### A.3 — Cine îl pune acolo la crearea comenzii - -Nu e o alegere liberă per articol — e moștenit dintr-o **politică aleasă o singură dată pe comandă**: - -``` -COMUN\clase\ocomenzi.vc2:1849-1870 (do_modifica, la deschiderea/crearea comenzii) -Do Case - Case Inlist(loRec.interna,2,5) - If lnTip = 0 And Reccount('crscomanda_curenta')>0 - lnIdPol = id_pol && preia politica de pe comanda existenta - Else - loCauta = caut_politici_curente_utilizator() && cautare manuala, comanda noua - If !Empty(Nvl(loCauta.id_pol,0)) - lnIdPol = loCauta.id_pol - Else - Return - Endif - Endif - update_articole_politica(lnIdPol) - Case loRec.interna = 3 - update_articole_politica_comenzi([IDPOLITICAPRET]) && optiune implicita globala - Otherwise - update_articole_politica_comenzi([ID_LISTA_PRETURI_PV]) && optiune implicita globala -Endcase -``` -Deci: pentru un tip de comandă (`interna` 2/5) operatorul **caută explicit** o politică -(`caut_politici_curente_utilizator`); pentru celelalte tipuri, politica vine dintr-o **opțiune globală** -(`gnIdPoliticaPret`/`gnId_lista_preturi_PV`, citite în `extrage_optiuni_firma`, -`update_comenzi.prg:16-29`). Nu se moștenește de la client. - -Politica aleasă filtrează lista de articole disponibile de adăugat: -``` -COMUN\programe\update_comenzi.prg:31-60 (update_articole_politica) -select p.id_articol,p.id_pol,p.pret,... from com_vpreturi_utilizator p ... -where p.id_util = <> and p.id_pol = <> -``` -```sql --- D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\01\ff_2026_01_21_01_FACTURARE.sql:3-30 -create or replace view com_vpreturi_utilizator as -select a.id_util, b.id_politica as id_pol, ..., d.id_articol, d.pret, ... - from utilizatori_rol_intern a - left join politici_grupuri b on a.id_grup = b.id_grup - left join crm_politici_preturi c on b.id_politica = c.id_pol - left join crm_politici_pret_art d on c.id_pol = d.id_pol - left join nom_articole e on d.id_articol = e.id_articol - where a.sters = 0 and b.sters = 0 and ... and d.id_pol is not null and ... -``` -`v_articole` (grid-ul de adăugare articole pe comandă, `ocomenzi.vc2:4005-4014`, -`RecordSource = "v_articole"`) e populat strict din acest view, **filtrat `d.id_pol IS NOT NULL`** — deci -orice articol afișat spre alegere e deja membru garantat. La alegere (`do_adauga`, -`ocomenzi.vc2:4645-4702`) și la salvare (`do_scrie_articole`, `:4864-4899`): -``` -ocomenzi.vc2:4885-4893 -Scatter Name poArticol -... -lcSql = [begin pack_comenzi.adauga_articol_comanda(] + Alltrim(Str(This.nId)) + [,] + ; - Alltrim(Str(poArticol.id_articol)) + [,] + Alltrim(Str(poArticol.id_pol)) + [,] + ... -``` -`poArticol.id_pol` (scatter din `v_articole`) merge neschimbat în `COMENZI_ELEMENTE.ID_POL`. Nu există în -`ocomenzi.vc2` niciun al doilea punct de adăugare a articolelor pe comandă (nicio cale către `caut_articol` -de nomenclator liber) — `v_articole` e singura sursă. - -### A.4 — Ce se întâmplă când linia de comandă are `ID_POL` gol la facturare - -**Nu se poate întâmpla, prin construcție dublă**: (a) `COMENZI_ELEMENTE.ID_POL` e `NOT NULL` la nivel de -Oracle — un `INSERT` cu `id_pol` gol ar da `ORA-01400`, nu ajunge niciodată să fie facturat; (b) singurul -punct de inserare din VFP (`pack_comenzi.adauga_articol_comanda`, apelat cu `poArticol.id_pol` din -`v_articole`) nu poate produce `id_pol` gol, pentru că sursa (`com_vpreturi_utilizator`) filtrează deja -`id_pol IS NOT NULL`. Verificat pe date reale: 0 din 6868 linii active. Spre deosebire de nomenclator -(`caut_articol`, unde coloana `id_pol` **nu există deloc** în SELECT), pe comandă întrebarea „ce se -întâmplă la gol" nu are un răspuns de cod (fallback/eroare) pentru că premisa nu apare niciodată. - ---- - -## B. Ruta CONTRACT - -Produsul e separat, **`D:\ROA\ROACONTRACTE`** (există în arbore, alături de `ROAFACTURARE`). - -### B.1 — Cursorul de facturare selectează `ID_POL`? Da, dar prin `ID_POL_ART`, nu direct - -```sql --- D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:2718-2836 (cursor_contract, V_CURSOR2 = grd_contracte) -SELECT ..., id_pol, ... - FROM (SELECT A.ID_CTR, C.ID_ARTICOL, NULL as id_rata, C.ID_POL, ... - FROM CONTRACTE A - LEFT JOIN CTR_ARTICOLE B ON A.ID_CTR = B.ID_CTR - LEFT JOIN CRM_POLITICI_PRET_ART C ON B.ID_POL_ART = C.ID_POL_ART -- <- cheia stocata e ID_POL_ART - LEFT JOIN NOM_ARTICOLE E ON C.ID_ARTICOL = E.ID_ARTICOL ... -``` -Grid-ul principal de adăugare articole pe contract (`crsarticole`, folosit pentru „adăugare liberă") **nu** -vine din acest cursor — vine dintr-un apel separat, în interiorul aceleiași proceduri: -```sql --- ff_...:2940-2948 -pack_facturare.cursor_preturi(V_DATA_CURS, V_TIP, V_ID_VALUTA, V_ID_GESTIUNE_INIT, - V_LUNA, V_AN, V_ID_UTIL, V_ID_SUCURSALA, V_CURSOR); -``` -adică **exact aceeași sursă ca „lista de prețuri" (secțiunea C)** — confirmă din nou concluzia din runda -anterioară (`retur_si_lista_preturi.md` B7): grid-ul „liber" de pe contract nu e liber de politică, e lista -de prețuri normală a operatorului. - -### B.2 — Unde e stocat pe contract: `CTR_ARTICOLE.ID_POL_ART` (linie, nullable) - -```sql -select column_name, nullable from user_tab_columns where table_name='CTR_ARTICOLE'; --- ID_POL_ART Y <- nullable, spre deosebire de COMENZI_ELEMENTE.ID_POL -select count(*), sum(case when id_pol_art is null then 1 else 0 end) from ctr_articole; --- 27 6 -``` -Spre deosebire de comandă, contractul stochează **`ID_POL_ART`** — FK direct la rândul din -`CRM_POLITICI_PRET_ART` (articol+politică), nu la politică ca atare — și coloana **e nullable**, fără -constrângere de tabel. Pe datele reale (bază de dezvoltare, eșantion mic — 27 rânduri în total, deci -**nu extrapolez procentul la producție**), 6 din 27 rânduri nu au `id_pol_art`. Dacă un asemenea rând ar -ajunge selectat spre facturare prin `V_CURSOR2`/`grd_contracte` (nu prin `crsarticole`, care e mereu lista -de prețuri), `LEFT JOIN ... ON B.ID_POL_ART = C.ID_POL_ART` nu găsește nimic, `id_pol` iese `NULL` în -cursor — același drum spre FACT-024 ca la nomenclator. **Neverificat**: dacă UI-ul (`grd_contracte`) permite -efectiv selecția unei asemenea linii spre facturare sau o filtrează/dezactivează — nu am urmărit acel cod -de grid, în afara bugetului acestei runde. - -### B.3 — Cine îl pune acolo la crearea contractului - -`D:\ROA\ROACONTRACTE\Clase\ferestre_contracte.vc2`, `PROCEDURE do_adauga_obiectul` (`:9454+`): -``` -:9464-9470 -Case gnParametru_prog = 1 && clienti - lcSel = [{call pack_crm.get_politici_grup(] + Alltrim(Str(gnIdUtil)) + [,] + ; - Alltrim(Str(goContract.id_valuta)) + [,?gnIdSucursala)}] - ... INTO Cursor crsPoliticiGrup1 ... -``` -``` -:9486-9500 -fpp = Createobject("frm_politica_preturi") && operatorul alege O politica dintre cele la care are drept -fpp.Show(1) -... -Select *, PRETFTVA As pret_unitar, 1 As cant, ... FROM cPol_pret_art With (Buffering = .T.) ; - WHERE ales = 1 INTO Cursor cAles && articolele bifate, deja membre ale politicii alese -``` -`pack_crm.get_politici_grup` e echivalentul ROACONTRACTE al lanțului -`UTILIZATORI_ROL_INTERN → POLITICI_GRUPURI → CRM_POLITICI_PRETURI` folosit și de comandă/factură normală — -operatorul primește lista politicilor lui, alege una, `cPol_pret_art` (bind pe `CRM_POLITICI_PRET_ART` -pentru politica aleasă) afișează articolele ei, iar rândurile bifate (`ales=1`) devin linii `CTR_ARTICOLE` -cu `id_pol_art` = `ID_POL_ART`-ul din acel rând. Din nou: **nu se alege un articol liber, se alege dintr-o -listă deja restrânsă la politică.** - -### B.4 — Ce se întâmplă cu `ID_POL_ART` gol la facturare - -Vezi B.2 — spre deosebire de comandă, **nu e imposibil prin constrângere de schemă** (coloana e nullable), -și există cazuri reale (6/27) în baza de dezvoltare. Comportamentul la facturare (dacă acea linie ajunge -selectată) urmează același `NULL → FACT-024` ca la nomenclator — **neconfirmat direct pe o factură reală**, -dedus din structura JOIN a cursorului (B.1). - ---- - -## C. Ruta LISTĂ DE PREȚURI (tip 1/5/7/10/22/29/45), ca termen de comparație - -**Corectare față de premisa din brief**: `id_pol` pe această rută **nu vine din alegerea operatorului pe -document** pentru majoritatea tipurilor — vine automat din rolul operatorului logat, exact ca pe comandă, -dar fără nicio interacțiune UI. - -`ofacturare.prg:279-282` (apelul cursorului pentru tip 1,5,7,10,22,23,29): -``` -lcSqlCursor = [{call pack_facturare.cursor_preturi(?poDate.zi_curs,?poDate.tip,?poDate.id_valuta,] + ; - [?poDate.id_gestiune_init,?gnLuna,?gnAn,?gnIdUtil,?gnIdSucursala)}] -``` -`poDate.id_pol` **nu e printre parametri**. `cursor_preturi` (`ff_...:2138-2354`) derivă `A.ID_POL` intern: -```sql --- ff_...:2222-2251 (V_TIP IN (1,2)) / 2330-2354 identic ca structura, tot fara parametru id_pol -FROM (select a1.id_util, a3.id_pol, ... - from utilizatori_rol_intern a1 - left join politici_grupuri a2 on a1.id_grup = a2.id_grup - left join crm_politici_preturi a3 on a2.id_politica = a3.id_pol - ... - where a1.id_util = V_ID_UTIL - and NVL(a1.ID_SUCURSALA,-99) = NVL(V_ID_SUCURSALA,-99) - and ) A -LEFT JOIN CRM_POLITICI_PRET_ART B ON A.ID_POL = B.ID_POL -``` -Adică `id_pol` per linie vine din rolul de utilizator (`UTILIZATORI_ROL_INTERN.ID_UTIL = V_ID_UTIL`) și -sucursală — **niciodată dintr-o alegere pe documentul curent**, pentru tip 1/2/5/7/10/22/29/45. - -Câmpul de căutare vizibil pe antet există, dar e **inert pentru aceste tipuri**: -``` -COMUN\clase\ofacturare.vc2:6822-6825 -ADD OBJECT 'Ct_clb_politici_preturi' AS ct_clb_cautare WITH ; - cconditie = ISNULL(poDate.id_pol) or EMPTY(poDate.id_pol), ; - cprocedura = thisform.do_cauta_politica, ... -``` -`poDate.id_pol` e citit efectiv (trimis la Oracle) **doar** pentru `cursor_gestiune`, tip 41/23 -(`ofacturare.prg:297,777`); e validat ca obligatoriu **doar** pentru aceleași două tipuri -(`ofacturare.vc2:7334-7337`: *„Nu ati ales politica de preturi!"*). Pe factura normală din listă de -prețuri, câmpul poate rămâne gol fără nicio consecință — nu alimentează nimic. - -**Concluzie C**: „lista de preț pe document" nu există ca mecanism activ pentru rutele principale — e „lista -de preț a operatorului logat", determinată automat de rolul lui în `UTILIZATORI_ROL_INTERN`. Fiecare linie -din `crsarticole` vine deja cu propriul `A.ID_POL` din acest join; dacă operatorul are mai multe grupuri de -politici active simultan, teoretic același articol ar putea apărea de mai multe ori cu `id_pol` diferit — -**neverificat pe date reale**, în afara bugetului acestei runde. - ---- - -## Ce nu s-a putut stabili și de ce - -- **B.4, comportamentul exact la facturare a unei linii de contract cu `ID_POL_ART` gol** — dedus din - structura JOIN a `cursor_contract`, nu confirmat pe o factură reală emisă din contract cu o asemenea - linie (ar necesita un contract de test cu o linie fără politică, apoi o încercare de facturare — în - afara bugetului read-only al acestei runde). -- **Dacă `grd_contracte` (UI) permite selecția/afișarea unei linii cu `id_pol_art` gol** — nu am urmărit - codul de grid din `ROACONTRACTE`/`ofacturare.vc2` pentru acest caz specific. -- **Dacă un operator cu mai multe grupuri de politici active poate primi articole duplicate cu `id_pol` - diferit în `crsarticole`** (secțiunea C) — plauzibil din structura JOIN-ului (fără `DISTINCT` pe - `a1.id_util`), neverificat pe date reale. -- **Eșantionul `CTR_ARTICOLE` e mic (27 rânduri) în baza de dezvoltare** — raportul 6/27 fără - `id_pol_art` e o dovadă reală, dar nu extrapolabilă direct la volumul de producție; merită o verificare - separată pe o bază cu volum de producție dacă decizia depinde de frecvența cazului. -- **Cele 37 de linii de comandă active cu articol ne-membru al politicii de pe linie** (semnalate în - „Completare de la sesiunea principală") — cauza (import? editare ulterioară a politicii? ștergere din - `CRM_POLITICI_PRET_ART` după ce comanda a fost creată?) nu e stabilită, și nici comportamentul real la - facturare (`FACT-024`/`ORA-01403` așteptat pe cod, neconfirmat pe o încercare reală). E cea mai - importantă verificare rămasă înainte de S4g — dacă garanția „membership prin construcția listei" se sparge - și pe alte linii similare, tot raționamentul din secțiunea E (paragraful 1) trebuie recalibrat. -- **`VANZARI.TIP = 51`** — 121 de linii fără `id_pol`, cu 179/275 rânduri `ACT` cu `SCC` populat cumulat pe - documentele aferente — nu am identificat ce tip de document e (nu apare în `Do Case` din - `ofacturare.prg:266-308`); posibil vânzare retail/POS sau un tip specific altui produs ROA. Neexplorat. -- **Restul tipurilor negative (`-1`…`-13`, în afară de `-12`=ROAAUTO)** — nu au fost identificate individual; - presupun variante storno/aviz ale rutelor deja documentate, dar nu s-a verificat pe cod care produs le - generează. -- **Dacă experiența lui Marius vine efectiv din contract-cu-rate, din ROAAUTO, sau din altă rută - neidentificată (`51`, negative)** — nu s-a putut stabili fără să-l întrebăm direct; secțiunea 0d - enumeră ipotezele plauzibile, cu dovezi pentru fiecare, dar nu poate alege între ele. - ---- - -## Completare de la sesiunea principala (confirmare pe schema, 10.08.2026) - -Verdictul de mai sus a fost verificat independent, pentru ca **contrazice o afirmatie a lui Marius** -(„fac facturi pe baza de comanda care nu au politica de pret"). Confirmat: - -- `COMENZI_ELEMENTE.ID_POL` — `nullable = N`, constrangerea `SYS_C0015376` (`"ID_POL" IS NOT NULL`) plus - `FK_COMENZI_ELEMENTE_002`. In date: **7108 randuri total, 0 cu `ID_POL` nul, 0 cu `ID_POL` = 0**, 9 - politici distincte in uz. Pe liniile active (`STERS = 0`): 6868, tot 0 nule. -- `CTR_ARTICOLE.ID_POL_ART` — `nullable = Y`. Confirmata asimetria semnalata in raport. -- Toate cele 9 politici folosite pe linii de comanda au `ID_NOTA` si ajung la un `SCC`. - -**Doua observatii noi, care nu erau in raport:** - -1. **Politica `7 DISCOUNT` e o politica tehnica de rutare contabila, folosita in producție.** Apare pe - **870 de linii de comanda** si trimite la nota `2 DISCOUNT` (`SCD = 667`, `SCC = 4111`). Nu e o lista - comerciala de preturi — e o politica existenta doar ca sa duca linia la o nota contabila anume. **E - precedentul de care are nevoie decizia 27**, si e mai apropiat de ce cere #13 decat - `gnId_pol_pret_stoc` (care e o singura politica pentru tot stocul). Tiparul „politica tehnica per scop - contabil" nu trebuie inventat: exista. -2. **Garantia „apartenenta prin construcția listei" nu e etanșă in date.** Din 6868 de linii active de - comanda, **37 au un articol care nu e membru al politicii de pe linie** - (`NOT EXISTS (SELECT 1 FROM crm_politici_pret_art WHERE id_pol = e.id_pol AND id_articol = e.id_articol)`). - Adica 37 de linii care, la facturare, ar trebui sa cada cu `FACT-024`. Cauza nu e stabilita — import, - sau editarea / stergerea articolului din politica dupa introducerea comenzii. **Punctul 1 al secțiunii E - („nu exista cale, in UI, de a ajunge la un articol care nu e deja membru") e corect despre UI, dar nu - despre starea datelor.** De verificat inainte de S4g: daca acele 37 de linii se factureaza fara eroare, - exista o cale nedescoperita si intrebarea deciziei 32 se redeschide. - -Interogarile: schema `MARIUSM_AUTO` pe `ROA_CENTRAL`, doar `SELECT`. diff --git a/docs/cercetare/inventar_controale_formulare.md b/docs/cercetare/inventar_controale_formulare.md deleted file mode 100644 index 61d0c0f..0000000 --- a/docs/cercetare/inventar_controale_formulare.md +++ /dev/null @@ -1,179 +0,0 @@ -# Inventar controale — formulare facturare (ROAFACTURARE, `COMUN\clase`) - -Investigatie read-only (fara editari, fara `git_sync.ps1`/`txt2vcx.ps1`, fara commit). Toate liniile -sunt din fisierele text `.vc2` (FoxBin2Prg), verificate pe fisierul real (nu `.bak`). Scop: pregatirea -unui mockup pentru un formular de facturare unificat — inventarul reflecta controalele existente, nu -o propunere noua. - -## 1. Butoane `frm_facturare_articole` (`ofacturare.vc2:10968-15739`) - -Grid sursa (comanda/lista preturi) = `grd_articole` (`Left=9,Top=336,Width=326,Height=130`, -`RecordSource=crsarticole`, `ofacturare.vc2:11570`). Grid destinatie (linii factura) = `grd_factura` -(`Left=379,Height=337`, `RecordSource=crsfactura`, `ofacturare.vc2:12263`). - -| Control | Clasa de baza | Caption/Picture | L/T/W/H | ToolTipText | Pozitie fata de grid | `fisier:linie` | -|---|---|---|---|---|---|---| -| But_modifica1 | but_modifica | (fara caption, `modific_sus.bmp`) | 773/61/30/27 | "Modificare (CTRL+M)" | dreapta-sus `grd_factura` (Anchor=9) | `ofacturare.vc2:11192` | -| But_sterge1 | but_sterge | `sterg_sus.bmp` | 811/61/30/27 | "Stergere (CTRL+D)" | dreapta-sus `grd_factura`, langa But_modifica1 | `ofacturare.vc2:11229` | -| But_renunt1 | but_renunt | `renunt_sus.bmp` | 792/1/30/27 | "Renuntare (ESC)" | coltul din dreapta-sus al formularului | `ofacturare.vc2:11202` | -| But_reset1 | but_reset | `reset_sus.bmp` | 303/295/30/27 | "Reseteaza" | deasupra `grd_articole` (Top grid=336) | `ofacturare.vc2:11213` | -| But_urmator1 | but_urmator, `caction=do_urmator` | `urmator1.bmp` | 343/348/30/27 | "Verificare" | lateral-dreapta `grd_articole` (Left grid+Width+8=343) | `ofacturare.vc2:11237` | -| But_urmator2 | but_urmator, `caction=do_urmator2` | `urmator1.bmp` | 343/143/30/27 | "Verificare" | deasupra `grd_articole`, langa `grd_contracte` | `ofacturare.vc2:11247` | -| **But_urmator_tot1** ("adauga tot din comanda") | but_urmator_tot, `caction=do_adauga_tot` | `urmator1_tot.bmp`, **fara ToolTipText** | 343/378/30/27, `Visible=.F.` implicit | — | lateral-dreapta `grd_articole`, sub But_urmator1 | `ofacturare.vc2:11257` | -| But_retur | but_retur | `retur1.bmp` | 343/407/30/27, `Visible=.F.` implicit | "Retur" | lateral-dreapta `grd_articole`, sub But_urmator_tot1 | `ofacturare.vc2:11221` | - -**"Adauga tot din comanda" — But_urmator_tot1.Click -> `do_adauga_tot`** -(`ofacturare.vc2:13169-13198`): parcurge `crsarticole` (SCAN) si apeleaza -`Thisform.do_adauga_articol(.T.)` pentru fiecare linie; daca articolul e gestionabil si cantitatea -ramasa >0, cere confirmare "Nu ati selectat toata cantitatea... treceti la urmatorul?" -(`aMessageBox` cu butoane Da/Nu/Renunta, cod 7). - -Vizibilitate pe tip document (`Init`, `ofacturare.vc2:14976-15344`, `Do Case poDate.tip`): - -| Control | Vizibil cand | Dovada | -|---|---|---| -| But_urmator_tot1 | `poDate.eProforma=1`, `poDate.lCopiere`, `tip=3` (comanda), `tip=4` (din avize), `tip in(21,28,42,47)` (aviz din comanda), `tip=25`, `tip in(8,9)` (retur factura), `tip=24` (retur aviz) | `ofacturare.vc2:15113,15120,15150,15166,15177,15215,15240,15245` | -| But_retur | `tip in(1,5,7,10)` (facturare din lista de preturi) | `ofacturare.vc2:15127` — comentariu explicit: "pot sa fac retur de articole intr-o factura de vanzare" | -| But_urmator2 / `grd_contracte` | eliminate (`RemoveObject`) daca nu exista `crsarticole1` (fara contracte pe formular) | `ofacturare.vc2:15294-15324` | - -**Conventia de clase de butoane** (suita ROA): clasa de baza `buton` (`_cmd_base.vc2:16`, -`AS _cmdbase OF "_cmd_base.vcx"`) — 30x27px implicit, `Caption=""` (buton doar cu imagine), -`BackColor=alb`, `SpecialEffect=1`. `Click` (`_cmd_base.vc2:41-70`) executa dinamic -`This.Parent.()` (macro pe `cAction`/`clistaparametri`) — de-asta majoritatea instantelor nu -au `Caption`/`Click` propriu, doar `caction=`. Subclasele concrete (`but_nou`, -`but_sterge`, `but_modifica`, `but_urmator_tot`, etc.) sunt in `cmd_butoane.vc2:7-441`, fiecare -hardcodand `caption`/`cpictureup`/`cpicturedown`/`Picture` (`..\grafice\*.bmp`, stare sus/jos) si -`ToolTipText` cu shortcut intre paranteze (ex. "Modificare (CTRL+M)"). `but_nou` (`do_adauga`, -`nou_sus.bmp`, "Adaugare (CTRL+N)") — `cmd_butoane.vc2:214`. - -## 2. Butoane `frm_facturare_articole2` (prototip, `ofacturare.vc2:15741-19355`) - -Diferenta arhitecturala fata de #1: **un singur grid** `grd_factura` (`Left=12,Top=204,Height=240`, -`ofacturare.vc2:16601`) cu editare inline prin comboboxuri in celule (`cCodMat.cboCodmat`, -`cDenumire.cCboDenumire`, `cGestiune.cCboGestiune`) — nu exista casete separate de cautare articol -(`ct_codmat`/`ct_articole`) si nici `crsarticole`/comanda sursa. - -| Control | Clasa de baza | Caption/Picture | L/T/W/H | ToolTipText | Pozitie fata de grid | `fisier:linie` | -|---|---|---|---|---|---|---| -| **But_nou1** | but_nou (mostenit, `caption=do_adauga` suprascris in clasa) | `nou_sus.bmp` | 719/175/30/27 | "Adaugare (CTRL+N)" | deasupra-dreapta `grd_factura` (Top grid=204) | `ofacturare.vc2:15936` | -| But_sterge1 | but_sterge | `sterg_sus.bmp` | 747/175/30/27 | "Stergere (CTRL+D)" | deasupra-dreapta `grd_factura`, langa But_nou1 | `ofacturare.vc2:15955` | -| But_renunt1 | but_renunt | `renunt_sus.bmp` | 716/1/30/27 | "Renuntare (ESC)" | coltul din dreapta-sus | `ofacturare.vc2:15944` | - -**But_nou1.Click -> `do_adauga`** (`ofacturare.vc2:17118-17122`, override propriu, nu -`but_nou`.caction implicit): `SELECT crsFactura / APPEND BLANK / this.grd_factura.SetFocus()` — -adauga direct un rand gol in grid si da focus, spre deosebire de `do_adauga_articol` complex din #1. - -Nu exista `But_modifica` in aceasta clasa — editarea se face inline in celulele gridului, nu prin -dialog separat. - -**Bonus relevant pentru mockup**: acest prototip **incorporeaza deja aceleasi controale de antet** -(`Ct_clb_venchelt`, `Ct_clb_sectie`, `Ct_clb_responsabil`, `Ct_clb_lucrare`, `Ct_clb_altele`, -`Ct_clb_valuta`, `clb_fdoc`, `Clb_serie_act1`, `Clb_nract`, `Clb_dataact`, `Clb_data_scadenta`, -`Clb_zi_curs`) direct pe formularul de articole, la `ofacturare.vc2:15741+182..749` — e cea mai -apropiata schita existenta de un "formular unificat". - -## 3. Antet `frm_date_factura` (`ofacturare.vc2:8482-9869`) si `frm_date_aviz` (`ofacturare.vc2:6566-7618`) - -| Control cerut | `frm_date_factura` — caption real | `frm_date_aviz` — caption real | Tip/container | Obligatoriu / vizibil conditionat | -|---|---|---|---|---| -| tip venit/cheltuiala | `Ct_clb_venchelt` = "Venit / cheltuiala" | `Ct_clb_venchelt` = "Venit / cheltuiala" | `ct_clb_cautare` (container cautare) | eliminat pe aviz pentru `tip=23,41,25` (transfer/retur) — `ofacturare.vc2:7438-7461` | -| sectie | `Ct_clb_sectie` = "Sectie" | `Ct_clb_sectie` = "Sectie" | `ct_clb_cautare` | intotdeauna prezent in ambele (nu s-a gasit `RemoveObject`) | -| responsabil | `Ct_clb_responsabil` = "Responsabil" | `Ct_clb_responsabil` = "Responsabil" | `ct_clb_cautare` | eliminat cand `gnScadereStoc=0` sau `tip in(4,7,8,9,48,49)` — factura: `9646-9707`; aviz: eliminat pe majoritatea tipurilor cu comanda/lista | -| lucrare | `Ct_clb_lucrare` = "Lucrare" | `Ct_clb_lucrare` = "Lucrare" | `ct_clb_cautare` | nu s-a gasit eliminare conditionata | -| altele | `Ct_clb_altele` — label dinamic ("Altele" implicit) | idem | `ct_clb_cautare`, `.do_schimba_explicatia(...)` | eticheta se schimba pe tip: "Nr. contract"/"Nr. comanda"/"Nr. factura"/"Nr. facturi"/"Locatie" (`9633-9643`); eliminat complet daca `gnScadereStoc=0 and tip in(1,5,10)` fara copiere | -| valuta | `Ct_clb_valuta` = "Valuta" | **nu exista pe aviz** | `ct_clb_cautare` | eliminat daca `poDate.in_valuta=0` (`ofacturare.vc2:9725-9732`) | -| fel document | `Ct_clb_fdoc` = combo `_combobox1` FACTURA/PROFORMA/BON FISCAL, label "Tip document" | `Ct_clb_fdoc` = camp cautare "Felul documentului" (`caut_ora.vcx`) | container diferit intre cele doua forme (combo la factura, cautare la aviz) | intotdeauna vizibil | -| serie | `Clb_serie_act` label "Serie document" | `Clb_serie_act` (fara label explicit override) | `clb_serie_act` (`serii_numere.vcx`) | eliminat daca `poDate.rezultat_serii` nu e in `(1,2,3)` — nicio serie configurata | -| numar | `Clb_nract` = "Numar document" | `Clb_nract` = "Nr. documentului" | `clb_tx_simplu`, `InputMask=get_mask(14,0)` | mereu prezent | -| data act | `Clb_dataact` = "Data document" | `Clb_dataact` = "Data documentului" | `clb_tx_data` | mereu prezent | -| data scadenta | `Clb_data_scadenta` = "Data scadenta" | **nu exista pe aviz** | `clb_tx_data` | **dezactivat** (nu eliminat) cand `gnScadentaAutomata=1` (`.dezactiveaza()`, `9713-9715`) | -| zi curs | `Clb_zi_curs` = "Data curs valutar" | `Clb_zi_curs` = "Data cursului valutar" | `clb_tx_data` | eliminat pe factura daca `tip in(8,9)` retur (`9718-9722`) | -| client | `Ct_clb_nume_client` = "Nume client" | `Ct_clb_nume_client` = "Nume client" | `ct_clb_cautare` | eticheta se schimba pe aviz ("Retur de la"/"Gestiune sursa") in functie de tip transfer | - -Control specific doar in `frm_date_factura`: `Ct_clb_gestiune_init`="Gestiune sursa" (`ToolTipText`: -"Daca nu alegeti gestiunea, la scaderea din stoc a unui articol va vor fi aratate stocurile tuturor -gestiunilor pe care aveti drepturi.") — eliminat impreuna cu `Ct_clb_responsabil` in majoritatea -cazurilor `gnScadereStoc=0`; `txtCodFiscal`/`lblCodFiscal` (cod fiscal, `ReadOnly`, langa client) si -`txtSoldLei`/`lblSoldLei` (sold curent client, populat din `GetSoldClient()` daca -`poDate.id_client<>0`); `But_verifica1` (verificare ANAF). Control specific doar in -`frm_date_aviz`: `Ct_clb_politici_preturi`="Politica de preturi" — eliminat pe majoritatea -tipurilor cu comanda. - -Toate conditiile de vizibilitate sunt in `Init` (nu in `do_schimba_tipdoc`, care doar realoca -seria/numarul): `frm_date_factura.Init` = `ofacturare.vc2:9563-9796`; `frm_date_aviz.Init` = -`ofacturare.vc2:7354-7600` (citit doar pana la ~`7533`; ultimele ~65 linii nu au fost verificate, -vezi "Necunoscute ramase"). - -## 4. `frm_alte_date` (`ferestre_cere_date.vc2:2219-3353`) - -| Control | Caption | Grupare logica | `fisier:linie` | -|---|---|---|---| -| `Ct_clb_delegat` | "Delegat" | Delegat/transport | `2553` | -| `Ct_clb_masina` | "Masina" | Delegat/transport | `2569` | -| `Ct_clb_agent` | "Agent" | Delegat/transport | `2537` | -| `Clb_dataora_exp` | "Data si ora expedierii" | Delegat/transport | `2443` | -| `opt_incasat` (radiogrup 4 optiuni) | "Fara incasare" / "Chitanta" / "Bon fiscal" / "POS Card" | Incasare | `2638` | -| `Cb_casa` | "Casa" (-> "Banca POS" pe POS) | Incasare | `2362` | -| `Clb_serie_chit` | "Serie chitanta" | Incasare (doar Chitanta) | `2503` | -| `Clb_nrchit` | "Nr. chitanta" (-> "Nr. bon" pe Bon fiscal/POS) | Incasare | `2480` | -| `Clb_incasat` | "Incasat" | Incasare | `2461` | -| `cmdModificaBon` | (icon `but_modifica`) | Incasare (doar Bon fiscal), `caction=do_modifica_bon` | `2526` | -| `chkPOS` | "POS" | Incasare (doar Bon fiscal) | `2409` | -| `chkDetaliat` | "Detaliat" | Incasare (doar Bon fiscal) | `2396` | -| `cboTipFactura` | (combo, langa label "Tip factura") | Incasare | `2378` | -| `clb_adresa_facturare` | "Adresa facturare" | Adresa de facturare | `2422` | -| `Ed_tx_simplu1` | "Text aditional (max. 1000 caractere)" | Text aditional | `2585` | -| `But_modifica1` | (icon) `caction` implicit -> `do_modifica` | actiune pe delegat (deschide `nom_parteneri_modifica`) | `2343` | - -**`actualizeaza_tipincasare`** (`2698-2856`) comuta vizibilitatea pe `This.opt_incasat.Value`: - -| Valoare | Vizibile | Ascunse | Numar alocat | -|---|---|---|---| -| 1 = Fara incasare | — | `cb_casa,clb_serie_chit,clb_nrchit,clb_incasat,_shape3,lb_simplu1,cmdModificaBon,chkPOS,chkDetaliat` | dezaloca 16(chitanta)/3(bon)/26(POS) | -| 2 = Chitanta | `cb_casa,clb_serie_chit,clb_nrchit,clb_incasat,_shape3,lb_simplu1` | `cmdModificaBon,chkPOS,chkDetaliat` | `Thisform.clb_serie_chit.genereazaNumar()` (`2783`) | -| 3 = Bon fiscal | `cb_casa,clb_nrchit,clb_incasat,_shape3,lb_simplu1,cmdModificaBon,chkPOS,chkDetaliat` | `clb_serie_chit` | `Thisform.do_aloca_nr_bon([CLICK])` (`2807`) | -| 4 = POS/Card | `cb_casa,clb_nrchit,clb_incasat,_shape3,lb_simplu1,cmdModificaBon` | `clb_serie_chit,chkPOS,chkDetaliat` | `Thisform.do_aloca_nr_pos([CLICK])` (`2844`) | - -`cmdModificaBon.Click -> do_modifica_bon` (`3001-3010`) deschide `viz_config_serii_complet WITH 3` -(`oserii_numere.prg`) si, la confirmare, dezaloca+realoca numarul de bon fiscal. - -## 5. Butoane `frm_facturi` (lista facturi, `ofacturare_comun.vc2:1168-5126`, fisierul real — -verificat, nu `.pre_s4butoane.bak`) - -| Control | Caption/Picture | Metoda apelata (`caction`/override) | `fisier:linie` | -|---|---|---|---| -| `but_modifica1` | `modific_sus.bmp` | `caction` implicit = `inainte_de_do_modifica` -> meniu `xmenu("Modificare date factura;Editare factura (articole, cantitati, preturi)")` -> optiune 1: `do_modifica()`, optiune 2: **`do_editare_factura()`** | ADD OBJECT `1424`; `inainte_de_do_modifica` `4925-4934`; `do_modifica` `4538-4637`; `do_editare_factura` `3715-3869` | -| `But_modifica2` | (fara caption, Top=341 — pe grid-ul de detalii, nu in bara de sus) | `caction=do_modifica_explicatie`, ToolTipText "Modificare explicatie articol" | `1434`; `do_modifica_explicatie` `4639-4656` | -| `But_copiaza1` | `copy_sus.bmp`, `Visible=.F.` implicit | `caction` mostenit = `do_copiaza` | `1404`; `do_copiaza` `3628-3713` | -| `But_sterge1` | `sterg_sus.bmp` | `caction` mostenit (`inainte_de_do_sterge` din clasa `but_sterge`) -> `do_sterge` | `1463`; `do_sterge` `4658-4874` | -| `But_listare1` | `listare_sus.bmp` | `do_listare` | `1414` | -| `But_verifica1` | (fara Picture explicit) | `do_verifica`, ToolTipText "Vericare coduri fiscale pe serverul ANAF" | `1473` | -| `But_attach1` | `attach_sus.bmp` | `caction=` gol, override `But_attach1.Click` | `1394` | - -**Nu exista buton separat "editare 2024"**: `do_editare_factura` este optiunea 2 din meniul popup -deschis de `but_modifica1` (`inainte_de_do_modifica`), nu un buton propriu. - -`Init` (`4936-4959`): daca `glLunaInchisa` (luna contabila inchisa), -`Thisform.but_sterge1.Visible = .F.` si se scoate dreptul de stergere din `gcAcces` — singura -conditionare de vizibilitate pe drept gasita in aceasta clasa. - -## Necunoscute ramase - -- `do_modifica` (frm_facturare_articole, `13746-13914`, 168 linii) si `do_sterge`/`do_editare_factura` - (frm_facturi) nu au fost citite in detaliu — doar identificate ca existenta/semnatura; daca - mockup-ul are nevoie de logica exacta de validare la modificare/stergere linie, trebuie citite - explicit. -- `frm_date_aviz.Init` (`7354-7600`) a fost citit doar pana la ~`7533`; ultimele ~65 linii (probabil - finalizare `laPozitii`/repozitionare, simetrice cu `frm_date_factura`) nu au fost verificate. -- Nu am verificat daca vizibilitatea controalelor din sectiunea 3 mai depinde si de **drept de - utilizator** (nu doar `poDate.tip`/`gnScadereStoc`/`gnScadentaAutomata`) — codul citit foloseste - doar variabile globale de setare firma si tipul documentului, nicio verificare explicita - `gcAcces`/drepturi pe aceste containere. -- Dimensiunile exacte (`Width`/`Height`) ale containerelor `ct_clb_cautare`/`clb_tx_*` nu sunt - listate explicit in multe instante (mostenite din clasa de baza `caut_ora.vcx`/`lb_tx.vcx`) — nu - am deschis acele biblioteci pentru dimensiuni implicite; doar `Left`/`Top`/`TabIndex` sunt - suprascrise per instanta. -- Nu am inspectat `caut_ora.vcx`/`lb_tx.vcx`/`serii_numere.vcx` (clasele de baza ale containerelor - `clb_*`/`ct_clb_*`) pentru a confirma dimensiunile standard sau comportamentul exact al - proprietatii `cconditie` (pare sa controleze cand campul de cautare cere obligatoriu o valoare, - nu vizibilitatea). diff --git a/docs/cercetare/legatura_linie_retur.md b/docs/cercetare/legatura_linie_retur.md deleted file mode 100644 index e02cfd4..0000000 --- a/docs/cercetare/legatura_linie_retur.md +++ /dev/null @@ -1,228 +0,0 @@ -# Cercetare: legatura "linie de retur -> linie/factura originala" se persista undeva? - -## Verdict (10 randuri) - -**NU se persista la nivel de linie. PARTIAL la nivel de document, si numai cand a fost aleasa o -singura factura sursa.** Cursorul Oracle care populeaza grila de retur pentru N.1 (documente tip -8/9/24, `pack_facturare.cursor_retur_document`, `ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:3949-4062`) -**nu selecteaza deloc** `A1.ID_VANZARE` sau `A1.ID_VANZARE_DET` din `VANZARI_DETALII` — legatura cu -factura/linia sursa se pierde chiar in query-ul care aduce datele in VFP, inainte sa existe vreo -sansa sa fie afisata. INSERT-ul final in `VANZARI_DETALII` (`scrie_in_vanzari`, documentat deja in -`rec_cale_vanzari_detalii.md:105-113`) nu are nicio coloana de tip sursa. Exista in schimb un link -**la nivel de document intreg**: `VANZARI_CORESP` (`TIP=3`, `ID_VANZARE_FACT`=documentul de retur, -`ID_VANZARE_AVIZ`=fiecare factura sursa aleasa) — dar cand utilizatorul alege mai multe facturi -sursa deodata (selectie multipla, suportata explicit de dialog), acest link iti da **multimea** de -facturi posibile, nu factura exacta a liniei. Pentru N.2 (`But_retur`, per articol) nu exista niciun -document nou scris separat — returul e o linie in factura normala curenta, iar "sursa" e verificata -doar la nivel de stoc (`RUL`/`STOC` filtrat pe `COD` legat de `VANZARI.ID_VANZARE`), nu persista ca -atribut al liniei noi. Concluzie pentru S4f: **se livreaza fara coloana de provenienta pe linie**; -cel mult se poate afisa, cand e un singur document sursa, "factura de retur X provine din factura Y" -la nivel de document (din `VANZARI_CORESP`), nu per linie. - -## 1. Ce face Oracle cu `poDate.listaid` - -Doua cai, in functie de mecanism: - -**N.1 (document, tip 8/9/24)**: `poDate.listaid` = CSV de `id_vanzare` (facturile alese la pasul de -cautare multipla, `ofacturare.vc2:9200`: `poDate.listaid = cursor2lista("crsFacturiTemp", "id_vanzare", ",")`). -Ajunge la Oracle ca parametru `V_LISTAID` in `pack_facturare.cursor_retur(V_IN_VALUTA, V_LISTAID, -V_ID_UTIL, V_CURSOR)` (`ff_...sql:3934-3947`), care e doar un wrapper peste -`cursor_retur_document(V_IN_VALUTA, V_LISTAID, V_COPIERE=0, V_PROFORMA, V_ID_UTIL, V_CURSOR)` -(`:3949-4062`). Acolo `V_LISTAID` devine CTE-ul `CRS` (`:3962-3964`): -```sql -WITH CRS AS (SELECT X as ID_VANZARE FROM table(charn2collection(V_LISTAID, V_SEPARATOR))) -``` -folosit doar ca filtru: `WHERE A1.STERS = 0 AND A1.ID_VANZARE IN (SELECT ID_VANZARE FROM CRS)` -(`:4054-4055`). **Filtru, nu persistare** — dupa acest `WHERE`, `ID_VANZARE` nu mai apare deloc in -lista de coloane a `SELECT`-ului extern (`:3965-4028`: `ID_C` (=`ROWNUM`, sintetic), `ID_ARTICOL`, -`LOT`, `SERIE`, `ID_POL`, preturi, `GESTIONABIL`, `CANTITATE`, `ID_GESTIUNE`, `PRET_ACHIZITIE`... dar -nu `ID_VANZARE` si nu `ID_VANZARE_DET`). Cursorul primit de VFP in `crsarticole` nu are, deci, nicio -coloana care sa spuna din ce factura/linie vine randul. - -Separat, la scrierea efectiva a documentului de retur, `scrie_corespondente_vanzari(3)` -(`:14834-14836`, apelata din `finalizeaza_factura`) foloseste tot `pack_facturare.clistaid` -(= acelasi `poDate.listaid`, tinut in variabila de sesiune a pachetului) ca sa scrie in -`VANZARI_CORESP` (`:15481-15516`): -```sql -INSERT INTO VANZARI_CORESP (ID_VANZARE_FACT, ID_VANZARE_AVIZ, TIP) - SELECT pack_facturare.nid_vanzare, ID_VANZARE, 3 - FROM VANZARI WHERE ID_VANZARE IN (SELECT X FROM table(charn2collection(V_LISTAID,','))); -``` -Asta scrie **cate un rand per factura sursa aleasa** (nu per linie), cu noul document de retur ca -`ID_VANZARE_FACT` si fiecare sursa ca `ID_VANZARE_AVIZ` (numele coloanei e generic, reutilizat si -pentru perechi aviz-factura, `TIP=1/2`, vezi `sterge_factura:5452-5457,5582-5585`). - -**N.2 (`But_retur`, per articol)**: `poDate.listaid` = `"ID_ARTICOL:ID_VANZARE"` (una sau mai multe -perechi CSV, `ofacturare.vc2:12888`: `thisform.cListaIdArticoleRetur = ... + STR(poArticol.id_articol) + ':' + poDate.listaid`, -trimis la Oracle la `:13978`). In `pack_facturare` (verificat in ramura `WHEN V_CANTE < 0 and -pack_facturare.clistaid is not null and instr(pack_facturare.clistaid, ':') > 0`, -`ff_...sql:8142-8212`) e folosit ca filtru pentru calculul cantitatii disponibile de retur din -rulaj (`RUL`), NU ca sa scrie o legatura: -```sql -AND A.COD IN (SELECT COD FROM VANZARI WHERE ID_VANZARE IN - (SELECT id_vanzare FROM (SELECT CAST(GETWORDNUM(id_articol_id_vanzare,1,':') AS NUMBER(20,0)) id_articol, - CAST(GETWORDNUM(id_articol_id_vanzare,2,':') AS NUMBER(20,0)) id_vanzare - FROM (SELECT x AS id_articol_id_vanzare FROM table(cast(CHARC2COLLECTION(pack_facturare.clistaid,',') AS char_tab)))) - WHERE id_articol = V_ID_ARTICOL)) -``` -Foloseste `listaid` doar ca sa restranga `RUL` la miscarile de stoc (`A.COD`) legate de factura -aleasa, ca sa calculeze **cantitatea inca disponibila in gestiune din acea vanzare** pentru articolul -respectiv (`tab_stoc`, tip=1 in acel `SELECT`). Nu se scrie nicio linie noua de legatura in vreun -tabel — e folosit exclusiv ca filtru intr-un calcul de disponibil. - -## 2. Coloana de provenienta pe `VANZARI_DETALII` - -Nu exista. DDL-ul complet nu e disponibil in acest repo (nu exista `CREATE TABLE VANZARI_DETALII` in -`docs/`; confirmat deja de cercetari anterioare — `docs/cercetare/discount_verificare2.md:104-111`, -`COMUN\docs\cercetare\rec_tva_vanzari.md:19`), dar structura efectiva a fost verificata pe schema live in -`docs/cercetare/rec_cale_vanzari_detalii.md:157-169` (interogare `all_tab_columns`, 08.08.2026): -35 de coloane, PK `ID_VANZARE_DET` (generat prin trigger `TRG_VANZARI_DET_BEFOINS` din -`SEQ_VANZARI_DETALII`), FK logic `ID_VANZARE`. Lista coloanelor relevante citata acolo: `ID_ARTICOL`, -`PRET`, `CANTITATE`, `PRET_CU_TVA`, `DISCOUNT_UNITAR`, `PROC_TVAV`, `ID_GESTIUNE`, `CONT`, -`ID_VALUTA`, `ID_JTVA_COLOANA`, `SERIE`, `EXPLICATIE`, `TAXCODE`, `LOT`, `STERS`, `VALIDAT`, -`DATAORA_VALID`, `ID_UTIL_VALID`, `ID_UTILS`, `DATAORAS`, `DIFERENTA`, `CUSTODIE`, `DESCARCAT`, -`PRETD`, `ID_VALUTAD`, `PRETV_ORIG`, `ID_CTR`, `ID_RATA`. **Nicio coloana** de tipul -`ID_VANZARE_SURSA`, `ID_DETALIU_SURSA`, `ID_FACT_SURSA`, `ID_VANZARE_RETUR`, `ID_ORIGINAL` sau orice -self-referinta catre o alta linie `VANZARI_DETALII`. Cautat explicit acele siruri in tot exportul -`ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` (~28000 linii) si in `COMUN\docs\` — zero potriviri -(vezi comanda de mai jos, sectiunea "Ramas de verificat"). - -Confirmarea independenta finala si cea mai directa: INSERT-ul care trece liniile din -`VANZARI_DETALII_TEMP` in `VANZARI_DETALII` la finalizarea oricarui document (inclusiv retur, pentru -ca `scrie_in_vanzari` e comun tuturor tipurilor) are lista de coloane explicita -(`rec_cale_vanzari_detalii.md:105-113`): -```sql -INSERT /*+ APPEND */ INTO VANZARI_DETALII - (ID_VANZARE, ID_ARTICOL, SERIE, ID_POL, CANTITATE, PRET_ACHIZITIE, PRETD, ID_VALUTAD, - PRET, PROC_TVAV, DISCOUNT_UNITAR, ID_GESTIUNE, ID_VALUTA, CONT, PRET_CU_TVA, ID_JTVA_COLOANA) -SELECT pack_facturare.nid_vanzare, ID_ARTICOL, ... FROM VANZARI_DETALII_TEMP WHERE ... -``` -Nicio coloana sursa in lista. Cum `VANZARI_DETALII_TEMP` insasi e populata pentru retur din -`cursor_retur_document` (care, cf. punctul 1, nu aduce `ID_VANZARE`/`ID_VANZARE_DET` sursa), legatura -e pierduta cu doi pasi inainte de a ajunge la acest INSERT — nu doar "nu se scrie", ci "nu mai exista -in date la momentul scrierii". - -## 3. Tabel separat de legatura - -**Da, exista, dar la nivel de document, nu de linie**: `VANZARI_CORESP(ID_VANZARE_FACT, -ID_VANZARE_AVIZ, TIP, STERS)`. Folosit pentru mai multe perechi de corespondenta, disambiguizate prin -`TIP`: -- `TIP=1`: factura scrisa dintr-un aviz (`scrie_corespondente_vanzari(1)`, la facturare din aviz, - `:14826`); -- `TIP=2`: aviz de retur (`scrie_corespondente_vanzari(2)`, `:14830`, la `ntip=24`); -- `TIP=3`: **factura de retur** (`scrie_corespondente_vanzari(3)`, `:14834-14836`, la `ntip in (8,9)`) - — exact cazul cerut de S4f. - -Verificarile de stergere din `sterge_factura` (`:5450-5494`) confirma semantica: interogheaza -`VANZARI_CORESP WHERE ID_VANZARE_AVIZ = V_ID_VANZARE AND TIP = 3` pentru "exista facturi de retur pe -aceasta factura?" — deci pentru o factura normala, `ID_VANZARE_AVIZ` (nume generic, refolosit) e ea -insasi, iar `ID_VANZARE_FACT` gasit prin acea interogare e factura(le) de retur emise pe baza ei. -Invers, pentru un document de retur dat, `SELECT ID_VANZARE_AVIZ FROM VANZARI_CORESP WHERE -ID_VANZARE_FACT = :id_retur AND TIP = 3` da **toate** facturile sursa alese la emiterea acelui retur -(poate fi mai multe, cf. selectie multipla din `caut_facturi_multiple_client`, -`docs/cercetare/factura_retur_document.md:67-85`). - -**Limita exacta**: cand pe un document de retur exista o singura factura sursa in `VANZARI_CORESP`, -"factura originala" e determinata fara ambiguitate pentru **toate** liniile documentului de retur -(pentru ca nu exista alt candidat). Cand exista mai multe (utilizatorul a bifat 2+ facturi la -cautare), `VANZARI_CORESP` da multimea, dar nu se poate spune care linie de retur vine din care -factura din multime — informatia care ar face diferenta (ID_VANZARE per linie in cursorul de -populare) a fost deja aruncata la pasul 1/2. Nu exista niciun tabel `VANZARI_RETUR`, `RETUR_DETALII` -sau `LEGATURI_DOCUMENTE`; cautate explicit in `ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` si in -`COMUN\docs\` — zero potriviri. - -`DOCUMENTE.AVIZE` (coloana denormalizata pe `VANZARI`, nu tabel separat) e completata redundant tot -din `VANZARI_CORESP` (`scrie_corespondente_vanzari:15505-15514`, `UPDATE VANZARI SET AVIZE = ...`) — -tine text afisabil (serie+numar avize), nu un id structurat, si nu e populata pentru `TIP=3` (doar -pentru avize, vezi apelul unic la acel `UPDATE` in corpul procedurii, comun tuturor `V_TIP`-urilor -dar cu sens practic doar pentru avize-spre-factura). - -## 4. Cum calculeaza serverul maximul returnabil - -**Raspunsul e diferit pe cele doua mecanisme, si niciunul nu se sprijina pe o legatura persistata -linie-la-linie:** - -**N.1 (document)**: NU exista un calcul de "cat s-a mai returnat deja". Coloana pe care utilizatorul -o vede ca "Cant. max. de returnat" (`ofacturare.vc2:15238,15243` — doar schimbare de caption pe capul -de coloana, nu logica noua) e pur si simplu `CANTITATE` a liniei originale, asa cum vine din -`cursor_retur_document` (`A1.CANTITATE`, `:4036`, filtrat doar pe `A1.STERS = 0`, fara nicio -agregare cu alte retururi anterioare pe aceeasi linie). Validarea din -`do_verifica_articol` (`:14743-14754`) nu face niciun calcul suplimentar de disponibil — blocheaza -doar cazul `tnCantitate >= 0` cand esti in mod retur (semnul gresit), nu o depasire de maxim istoric. -**Consecinta directa**: daca acelasi utilizator emite doua facturi de retur separate pe aceeasi -factura sursa, a doua interogare `cursor_retur_document` va aduce din nou linia originala cu -`CANTITATE` **intreaga**, neredusa de primul retur — nimic in cod nu scade sau marcheaza cat s-a -returnat deja la acest nivel. Acesta e cel mai clar indiciu ca legatura linie-la-linie *nu* e tinuta -nicaieri pentru N.1: daca ar fi fost tinuta, ar fi trebuit folosita exact aici, ca sa capeze -cantitatea, si nu e. - -**N.2 (`But_retur`)**: exista un calcul real de disponibil, dar e un calcul de **stoc/rulaj**, nu de -"cat s-a returnat pe acea linie". Ramura din `pack_facturare` citata la punctul 1 -(`ff_...sql:8142-8212`) calculeaza cantitatea inca prezenta in `RUL` (miscari de stoc) care a venit -din vanzarea aleasa (`RUL.COD` legat prin `VANZARI.COD`, filtrat pe `id_articol:id_vanzare` din -`listaid`), plus `STOC`/`RUL_TEMP` pentru cazul general. E un calcul valid de "poti scoate din -gestiune atat cat inca exista acolo cu provenienta asta", sprijinit pe legatura persistata -`RUL.COD -> VANZARI.COD` (asta e adevarata "legatura care exista" pentru N.2) — dar e o legatura la -nivelul miscarii de stoc, nu un contor "cantitate returnata" pe `VANZARI_DETALII`, si nu se -translateaza in nicio coloana de provenienta pe linia noua scrisa. - -## 5. Ce se poate afisa efectiv - -- **Numarul/seria facturii originale la nivel de document de retur, cand exista o singura factura - sursa**: SE POATE, din `VANZARI_CORESP` (`TIP=3`) join `VANZARI` pe `ID_VANZARE_AVIZ`. Interogare - de rulat pe baza vie (nu verificata aici, doar formulata): - ```sql - SELECT v.serie_act, v.numar_act - FROM VANZARI_CORESP c JOIN VANZARI v ON v.ID_VANZARE = c.ID_VANZARE_AVIZ - WHERE c.ID_VANZARE_FACT = :id_document_retur AND c.TIP = 3 AND c.STERS = 0; - ``` - Daca randul e unic, se poate afisa "provine din factura X" **pe capul documentului de retur** - (nu pe linie — toate liniile ar arata aceeasi sursa, pentru ca e singura posibila). -- **Cand documentul de retur are 2+ facturi sursa** (selectie multipla permisa de dialog): se poate - afisa lista de facturi sursa posibile (tot din `VANZARI_CORESP`), dar NU care linie vine din care - factura din lista — informatia nu exista. -- **Numarul facturii originale pe fiecare linie individuala de retur**: NU SE POATE, in niciun caz — - nici cand exista un singur document sursa (pentru ca "linie cu linie" nu inseamna nimic diferit de - "documentul cu documentul" atunci, dar afirmatia stricta "aceasta linie de retur vine din linia Y a - facturii" nu poate fi demonstrata din date, doar presupusa cand exista un singur candidat), si sigur - nu cand exista mai multe facturi sursa sau cand linia e libera (permisa explicit de decizia 22). -- Pentru N.2 (retur in factura normala, tip 1/5/7/10): nu exista deloc "document de retur" separat de - arata provenienta — linia de retur e o linie normala (cu cantitate negativa) in factura curenta; - singura urma a sursei e `RUL.COD`/`VANZARI.COD` folosita tranzitoriu la calculul de disponibil, nu - persistata pe linia noua din `VANZARI_DETALII`. - -## Verdict pentru plan (S4f) - -**NU se persista legatura linie-la-linie.** Pentru documentul de retur (N.1), se poate afisa -"factura sursa" **la nivel de document**, din `VANZARI_CORESP` (TIP=3), **doar cand exista exact o -singura factura sursa aleasa** — cazul cu selectie multipla da doar multimea, fara atribuire per -linie. Pe linia individuala de retur nu se poate afisa nimic verificabil din date, in niciun caz. -Recomandare pentru S4f: se livreaza fara coloana de provenienta pe linie (cf. deciziei deja scrise in -plan, "nu se inventeaza"); daca se vrea un gest minim, singura afisare sustenabila cu date e un text -de tip "Retur pentru factura: X" pe **capul** documentului (deja exista, cf. -`factura_retur_document.md:85`: `poDate.text_aditional = ... + poDate.descriere`, unde -`poDate.descriere` e lista serie+numar a facturilor alese) — nu pe linie, si nu nou, e deja acolo. - -## Ramas de verificat pe baza vie - -- Nu s-a interogat live daca `VANZARI_CORESP.TIP=3` e scris consecvent pentru fiecare factura de - retur emisa istoric (posibil sa existe date vechi scrise inainte ca aceasta ramura sa existe, sau - prin alt cod neexaminat aici) — de rulat: - ```sql - SELECT COUNT(*) FROM VANZARI v - WHERE v.TIP IN (8,9) AND v.STERS = 0 - AND NOT EXISTS (SELECT 1 FROM VANZARI_CORESP c WHERE c.ID_VANZARE_FACT = v.ID_VANZARE AND c.TIP = 3); - ``` - Daca da >0, exista facturi de retur fara nicio legatura de document persistata, nici macar cea - partiala descrisa mai sus. -- Nu s-a verificat live cate din facturile de retur existente au >1 factura sursa in - `VANZARI_CORESP` (adica cate din documentele reale ar cadea in cazul "multime, nu atribuire") — - utila pentru a decide cat de des s-ar aplica de fapt afisarea propusa la punctul 5. -- Nu s-a gasit (si nici nu era in scop) un mecanism separat pentru avize de retur custodie (`tip=50`, - marcat "in lucru" in `tipuri_documente_facturare.md`) — daca S4f ajunge sa acopere si acel tip, de - recercetat separat. -- Subagentul de research auxiliar lansat pentru confirmarea independenta a definitiei - `caut_facturi_multiple_client`/`caut_facturi_multiple_client_articol` nu a returnat un raport - utilizabil (rezultat gol la finalizare); definitiile au fost gasite si citite direct in aceasta - sesiune, in `COMUN\programe\oproceduri_facturare.prg:2091-2160`, deci nu blocheaza verdictul, dar - nu a adaugat nimic peste ce e deja in acest raport. diff --git a/docs/cercetare/linii_comanda_articol_nemembru.md b/docs/cercetare/linii_comanda_articol_nemembru.md deleted file mode 100644 index 048fca1..0000000 --- a/docs/cercetare/linii_comanda_articol_nemembru.md +++ /dev/null @@ -1,182 +0,0 @@ -# Linii de comanda cu articol nemembru al politicii de pret — verificare facturare - -Status: FINALIZAT. - -## Verdict - -**DA — s-a facturat cel putin o linie fara ca FACT-024 sa se declanseze.** Comanda `497`, linia -`4294507522` (VOUCHER DISCOUNT) / politica `7` (DISCOUNT), a fost facturata cu succes in factura -`ID_VANZARE=1028` (serie `SSS`, numar `535`, `FACTURAT=1`, `DATA_FACTURAT=2026-03-20`), desi -articolul **nu e membru** al politicii 7 in `CRM_POLITICI_PRET_ART` la data acestei cercetari -(10.08.2026). **Decizia 32 se REDESCHIDE partial**: nu pentru ca ar exista o cale de cod care ocoleste -verificarea (codul chiar cheama `contabilizeaza_articol` pentru acest tip de vanzare, cf. punctul 3), -ci pentru ca datele arata ca membru-ul politicii **s-a schimbat dupa facturare** — vezi punctul 4. -Pentru celelalte 34 de linii cu acelasi articol/politica (35 in total cu `CANTITATE<0`), **nu exista -nicio factura, nici macar stearsa** — la ele fisura ramane doar o stare inconsistenta nefacturata -(vezi punctul 1). - -Observatie structurala separata de cea de mai sus: **inca 2 linii** (comenzile 114 si 115, alt articol, -alta politica) au `CANTITATE>0`, adica **nefacturate inca deloc** — pentru acestea afirmatia "ar trebui -sa cada la facturare" e neverificata pentru ca nu au fost niciodata trecute prin `contabilizeaza_articol`. - -## 1. S-a facturat vreuna din ele fara eroare? - -Interogare: pentru fiecare din cele 37 linii `COMENZI_ELEMENTE` cu `ID_POL NOT NULL` si articol -nemembru in `CRM_POLITICI_PRET_ART`, am cautat linii `VANZARI_DETALII` (prin `VANZARI.ID_COMANDA`) -cu acelasi `ID_ARTICOL`+`ID_POL`, **inclusiv cele sterse** (ca sa nu ratez o factura anulata): - -```sql -select ce.id_comanda_element, ce.id_comanda, ce.id_articol, ce.id_pol, ce.cantitate, - (select count(*) from vanzari v join vanzari_detalii vd on vd.id_vanzare=v.id_vanzare - where v.id_comanda = ce.id_comanda and vd.id_articol=ce.id_articol and vd.id_pol=ce.id_pol) as nr_total_incl_sters -from comenzi_elemente ce -where ce.id_pol is not null and ce.cantitate < 0 -and not exists (select 1 from crm_politici_pret_art cppa - where cppa.id_pol = ce.id_pol and cppa.id_articol = ce.id_articol); -``` - -Rezultat: **doar comanda 497** are potriviri (2 linii `VANZARI_DETALII`, id 1494 si 1495, ambele -`STERS=0`, `CANTITATE=-1` fiecare, apartinand facturii `ID_VANZARE=1028`). Toate celelalte 34 de -comenzi (349, 351, 352, 359, 360, 377, 388, 404, 412, 417, 429, 443, 447, 450, 451, 453, 455, 456, -457, 466, 471, 475, 486, 500, 501) au **0 potriviri**, inclusiv sters. - -**Interpretare structurala a mecanismului** (cititul codului, nu presupunere): singurul loc care -insereaza randuri cu `CANTITATE<0` in `COMENZI_ELEMENTE` e procedura `inchide_comanda` -(`ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:5769-5820`): - -```sql -INSERT INTO COMENZI_ELEMENTE (... CANTITATE ...) - SELECT ..., NVL(C.CANTITATE, 0) + NVL(B.CANTITATE, 0) - A.CANTITATE AS CANTITATE, ... - FROM COMENZI_ELEMENTE A - LEFT JOIN (... FROM VANZARI_DETALII_TEMP ...) B ON ... -- lotul curent de facturare - LEFT JOIN (... FROM VANZARI JOIN VANZARI_DETALII ...) C ON ... -- deja facturat anterior - WHERE A.ID_COMANDA = :comanda - AND SIGN(A.CANTITATE) * A.CANTITATE > - SIGN(A.CANTITATE) * (NVL(C.CANTITATE, 0) + NVL(B.CANTITATE, 0)); -``` - -Asta insemna ca randul negativ **nu dovedeste singur ca linia a fost facturata** — el reprezinta -*diferenta ramasa* fata de cantitatea comandata, la momentul **inchiderii** comenzii, chiar daca -`B` (lotul curent) si `C` (facturat anterior) sunt amandoua 0. O comanda inchisa fara sa i se -factureze o anumita linie tot primeste un rand `CANTITATE = -A.CANTITATE`. De-asta 34 din 35 de -linii "negative" nu au nicio factura in spate: comenzile respective au fost **inchise** (probabil -manual, ca "restanta anulata"), nu facturate pe acea linie. **Zero cazuri in date pentru acestea nu -demonstreaza ca nu se poate factura — demonstreaza doar ca nu s-a intamplat.** - -Pentru comanda 497 insa, potrivirea cu 2 linii reale, active, `FACTURAT=1` in `VANZARI_DETALII` -e dovada directa (nu inferenta) ca acea combinatie articol/politica **a ajuns** intr-o factura emisa. - -## 2. Reconfirmarea cifrei si lista liniilor - -Cifra **37** se reconfirma exact (nu 6868/nemembru cum sugera masuratoarea anterioara — aceea era -alt numitor; numitorul corect pentru linii cu `ID_POL NOT NULL` e **7108**, nu 6868): - -```sql -select count(*) from comenzi_elemente ce where ce.id_pol is not null; -- 7108 -select count(*) from comenzi_elemente ce where ce.id_pol is not null - and not exists (select 1 from crm_politici_pret_art cppa - where cppa.id_pol = ce.id_pol and cppa.id_articol = ce.id_articol); -- 37 -``` - -Compozitie (verificata, `select sign(cantitate), count(*) ... group by sign(cantitate)`): -- **35 linii** cu `CANTITATE=-1`: acelasi articol/politica in toate — `ID_ARTICOL=4294507522` - ("VOUCHER DISCOUNT"), `ID_POL=7` ("DISCOUNT"). Comenzi: 349(x2), 351, 352(x2), 359, 360(x2), 377, - 388, 404, 412(x2), 417, 429, 443, 447, 450, 451, 453, 455, 456, 457, 466, 471(x2), 475(x2), - 486(x2), 497(x2), 500, 501(x2). -- **2 linii** cu `CANTITATE=+10` (nefacturate inca, nicio urma de consum): - - comanda 114, `ID_ARTICOL=4294507508` ("CAFEA TEST - PRODUS TEST PENTRU IMPORT WEB"), `ID_POL=2` - ("LISTA 2"), data comanda 2025-09-10, `COMENZI.STERS` neverificat suplimentar dar randul e - prezent activ. - - comanda 115, `ID_ARTICOL=4171144217` ("CAFEA 1"), `ID_POL=2`. **Anomalie separata**: `COMENZI` - nu contine niciun rand cu `ID_COMANDA=115` — antetul comenzii lipseste, doar linia de element a - supravietuit. NEDETERMINABIL DIN DATE de ce (nu exista audit/istoric pe `COMENZI`); posibil - date de test/import, dat fiind ca articolul insusi e "CAFEA TEST ... PENTRU IMPORT WEB" pe - cealalta linie similara. - -Toate cele 37 de linii au `COMENZI_ELEMENTE.STERS=0` (nicio linie de comanda marcata stearsa). - -Stare de facturare per linie: singura cu urma reala de facturare e comanda 497 (vezi punctul 1); -restul de 36 sunt **nefacturate** pe aceasta combinatie articol/politica. - -## 3. Verificare cod FACT-024 (`contabilizeaza_articol`) - -Blocul citat in brief (`ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:7278-7302`) e identic cu ce ruleaza -in productie — verificat direct in codul compilat, nu doar in scriptul istoric (fisierul `.sql` e un -istoric cumulativ cu **doua definitii** ale `contabilizeaza_articol` in text, la linia 746 si la -7173; doar a doua e cea vie): - -```sql -select owner, line from all_source - where name='PACK_FACTURARE' and type='PACKAGE BODY' - and text like '%FACT-024%'; -``` -confirma acelasi text de eroare in schema `MARIUSM_AUTO` (Dev). - -Verificarea foloseste view-ul `VCRM_POLITICI_PRET_ART`: -```sql -SELECT COMPUS, ID_POL_ART INTO ... FROM VCRM_POLITICI_PRET_ART - WHERE ID_ARTICOL = detalii_articol.id_articol AND ID_POL = detalii_articol.id_pol; -EXCEPTION WHEN NO_DATA_FOUND THEN ... RAISE_APPLICATION_ERROR(-20000, ... '(FACT-024)'); -``` -Am verificat textul view-ului (`user_views`): `VCRM_POLITICI_PRET_ART` are `CRM_POLITICI_PRET_ART PA` -ca tabel-sursa (driving table) al tuturor `LEFT JOIN`-urilor, fara niciun `WHERE` care sa filtreze -`PA` (nu exista coloana `STERS` pe `CRM_POLITICI_PRET_ART`). Deci view-ul are exact acelasi set de -perechi `(ID_POL, ID_ARTICOL)` ca tabelul de baza — verificarea mea prin `NOT EXISTS` pe -`CRM_POLITICI_PRET_ART` e echivalenta cu ce evalueaza codul. **Concluzie: pentru toate cele 37 de -linii, `SELECT ... INTO` ar da `NO_DATA_FOUND` -> `FACT-024`, daca ar trece prin -`contabilizeaza_articol` ACUM, cu starea curenta a `CRM_POLITICI_PRET_ART`.** - -Apelul e neconditionat pentru facturi standard: am cautat toate apelurile -`pack_facturare.contabilizeaza_articol(tab_detalii(i))` in codul **compilat** (`all_source`, schema -`MARIUSM_AUTO`) — 6 aparitii, linii 4877, 4901, 5594, 5618, 5876, 5900. Linia 4901 e in procedura -`scrie_factura2` (facturarea principala), in ramura `ELSE` a unui `CASE pack_facturare.ntip` care -exclude doar transferurile intre subunitati (23,25,30,41), avizele de custodie (42,47) si facturile -cu rate (2,6,52 cu `id_rata<>0`) — **tip 3 (tipul facturii 1028) cade in ELSE, deci -`contabilizeaza_articol` chiar s-a executat pentru acea linie.** - -Prin urmare, pentru comanda 497: fie (a) la data facturarii (20.03.2026) articolul 4294507522 CHIAR -era membru al politicii 7 si a fost scos ulterior din `CRM_POLITICI_PRET_ART`, fie (b) verificarea a -fost ocolita altfel. Nu am gasit dovada de tip (b) — vezi punctul 4. - -## 4. Cauza - -`CRM_POLITICI_PRET_ART` **nu are coloana `STERS`** si nu exista niciun tabel de istoric/audit pentru -ea (`select table_name from user_tables where table_name like '%POLITICI%' or ... '%AUDIT%'` — doar -tabelele de configurare curenta, niciunul de istoric). Stergerile din aceasta tabela sunt deci -**fizice, fara urma**. Nu pot reconstitui direct daca perechea (politica 7, articol 4294507522) a -existat la 20.03.2026. - -Coloanele de audit disponibile pe randurile facturii/comenzii **nu arata nicio editare ulterioara**: -`VANZARI.ID_UTILS`/`DATAORAS` si `VANZARI_DETALII.ID_UTILS`/`DATAORAS` (1494, 1495) sunt toate NULL — -niciun `UPDATE` standard nu a atins aceste randuri dupa creare. Deci nu exista dovada ca linia de -factura ar fi fost modificata ulterior prin fluxul nou de "editare factura emisa" (adaugat recent in -proiect, cf. changelog 2.11.15) — desi asta nu exclude o editare care ar fi ocolit acele coloane de -audit, doar ca nu am gasit dovada ei. - -Indiciu indirect, dar nu concludent: articolul 4294507522 e denumit **"VOUCHER DISCOUNT"**, iar -politica 7 e **"DISCOUNT"** — o pereche generica folosita ca linie de discount pe **35 de comenzi -diferite**, nu un caz izolat. Reutilizarea masiva a exact aceleiasi perechi sustine ipoteza unei -**modificari de catalog** (articolul-voucher scos ulterior din lista politicii DISCOUNT ca decizie de -business/curatare), mai degraba decat 35 de erori de introducere independente. **Aceasta ramane insa -o inferenta din tipar, nu o dovada directa — se marcheaza NEDETERMINABIL DIN DATE strict pe cauza si -data exacta a schimbarii.** - -## Ce am incercat si ce a esuat - -- Doua incercari de a delega cautarea codului sursa VFP/PL-SQL unui subagent `Explore` au esuat cu - raspunsuri goale/neinformative ("Nimic nou.", "No new input to act on.") — fara nicio dovada ca ar - fi rulat vreo cautare. Am renuntat la delegare si am facut cautarile direct cu Grep si Read. -- Interogarea `all_source` fara `owner=` a intors linii duplicate/interclasate (schema `ACN` are de - asemenea un `PACK_FACTURARE`) — a trebuit filtrat explicit pe `owner='MARIUSM_AUTO'` pentru - rezultate coerente. -- Un `prompt '---text---'` intre doua instructiuni SQL in acelasi fisier `.sql` a produs iesire - amestecata (headerul s-a lipit de urmatoarea instructiune) — am renuntat la `prompt` si am rulat - interogarile in fisiere separate. - -## Date tehnice folosite - -- Conexiune: `MARIUSM_AUTO/ROMFASTSOFT@ROA_CENTRAL` (Dev), via - `D:\ROA\instantclient_19_18\sqlplus.exe`, fisiere `.sql` ASCII in scratchpad, niciun `INSERT`/`UPDATE`/`DDL` rulat. -- Cod verificat: `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` - (text istoric cumulativ) si pachetul compilat `MARIUSM_AUTO.PACK_FACTURARE` (`all_source`, sursa de - adevar pentru ce ruleaza efectiv). diff --git a/docs/cercetare/modifica_date_factura_parametri.md b/docs/cercetare/modifica_date_factura_parametri.md deleted file mode 100644 index 53b925e..0000000 --- a/docs/cercetare/modifica_date_factura_parametri.md +++ /dev/null @@ -1,124 +0,0 @@ -# Cercetare: `pack_facturare.modifica_date_factura` — parametri, mapare pe coloane si pe controale - -Sursa pachetului: `D:\ROA\ROAACNPRO\PACK_FACTURARE.pck` (declaratie spec `:921-935`, corp `:14392-14462`). -Apel VFP: `D:\ROA\ROAFACTURARE\COMUN\clase\ofacturare_comun.vc2`, metoda `frm_facturi.do_modifica` (`:4538-4637`), -apelul efectiv la `:4599-4614`. Formular de editare: `frm_modifica_factura` (`:5262-5774`). - -Nu exista alt fisier `.pck`/`.sql` cu semnatura diferita pentru `modifica_date_factura` in -`D:\ROA\ROAFACTURARE` sau `COMUN`; niciun alt loc din VFP (in afara de `ofacturare_comun.vc2` si -copia identica `.bak`) nu apeleaza procedura — cautat cu grep pe `*.vc2/*.sc2/*.prg` in tot arborele. - -## 1-2. Tabel parametri (semnatura + destinatie in Oracle) - -| # | Parametru | Tip (`.pck:921-935`) | Coloana / tabel scrise in corp (`.pck:14392-14462`) | Observatii | -|---|---|---|---|---| -| 1 | `V_ID_VANZARE` | `NUMBER` | Folosit doar in `WHERE ID_VANZARE = V_ID_VANZARE` (`:14424`) si ca sursa pentru `lnIdFact` (`:14426`) | Identitate randului, niciodata scris | -| 2 | `V_ID_RUTA` | `NUMBER` | `VANZARI.ID_RUTA` (`:14414`) | Scris neconditionat | -| 3 | `V_ID_DELEGAT` | `NUMBER` | `VANZARI.ID_DELEGAT` (`:14415`) | Scris neconditionat | -| 4 | `V_ID_AGENT` | `NUMBER` | `VANZARI.ID_AGENT` (`:14416`) | Scris neconditionat | -| 5 | `V_ID_MASINA` | `NUMBER` | `VANZARI.ID_MASINA` (`:14417`) | Scris neconditionat | -| 6 | `V_DATAORA_EXP` | `DATE` | `VANZARI.DATAORA_EXP` (`:14418`) | Scris neconditionat | -| 7 | `V_ID_FACTURARE` | `VANZARI.ID_FACTURARE%TYPE` | `VANZARI.ID_FACTURARE` (`:14419`) | Scris neconditionat | -| 8 | `V_LISTARE_DETALIATA` | `VANZARI.LISTARE_DETALIATA%TYPE` | `VANZARI.LISTARE_DETALIATA = NVL(V_LISTARE_DETALIATA, 0)` (`:14420`) | NVL la 0 | -| 9 | `V_TEXT_ADITIONAL` | `VANZARI.TEXT_ADITIONAL%TYPE` | `VANZARI.TEXT_ADITIONAL` (`:14421`) | Scris neconditionat, fara NVL | -| 10 | `V_TIP_SAFT` | `VANZARI.TIP_SAFT%TYPE DEFAULT NULL` | `VANZARI.TIP_SAFT` (`:14422`) | Scris neconditionat | -| 11 | `V_EFACTURA` | `VANZARI.EFACTURA%TYPE DEFAULT NULL` | `VANZARI.EFACTURA` (`:14423`) | Scris neconditionat | -| 12 | `V_DATA_ACT` | `VANZARI.DATA_ACT%TYPE DEFAULT NULL` | `VANZARI.DATA_ACT` (`:14448`) + `DOCUMENTE.DATAACT`, `ACT.DATAACT`, `IREG_PARTENERI.DATAACT`, `JV2007.DATAACT`, `RUL.DATAACT` si `RUL.DATAOUT` (`:14449-14453`), toate `WHERE ID_FACT = lnIdFact` | Doar daca `V_DATA_ACT IS NOT NULL AND V_DATA_ACT <> NVL(ldDataAct, SYSDATE)` (`:14447`) | -| 13 | `V_DATA_SCAD` | `VANZARI.DATA_SCAD%TYPE DEFAULT NULL` | `VANZARI.DATA_SCAD` (`:14457`) + `ACT.DATASCAD`, `IREG_PARTENERI.DATASCAD` (`:14458-14459`), `WHERE ID_FACT = lnIdFact` | Doar daca `V_DATA_SCAD IS NOT NULL AND V_DATA_SCAD <> NVL(ldDataScad, sysdate)` (`:14456`) | -| 14 | `V_NUMAR_ACT` | `VANZARI.NUMAR_ACT%TYPE DEFAULT NULL` | `VANZARI.NUMAR_ACT` (`:14439`) + `DOCUMENTE.NRACT`, `ACT.NRACT`, `IREG_PARTENERI.NRACT`, `JV2007.NRACT`, `RUL.NRACT` (`:14440-14444`), `WHERE ID_FACT = lnIdFact` | Doar daca `V_NUMAR_ACT IS NOT NULL AND V_NUMAR_ACT <> NVL(lnNrAct, 0)` (`:14438`) | -| 15 | `V_SERIE_ACT` | `VANZARI.SERIE_ACT%TYPE DEFAULT NULL` | `VANZARI.SERIE_ACT` (`:14429`) + `DOCUMENTE.SERIE_ACT`, `ACT.SERIE_ACT`, `IREG_PARTENERI.SERIE_ACT`, `JV2007.SERIE_ACT`, `RUL.SERIE_ACT` (`:14430-14434`), `WHERE ID_FACT = lnIdFact` | Doar daca `V_SERIE_ACT IS NOT NULL AND V_SERIE_ACT <> NVL(lcSerieAct, '')` (`:14428`) | - -Parametrii 2-11 se scriu neconditionat in `VANZARI` la fiecare apel (chiar cu `NULL`, daca asa vine -argumentul). Parametrii 12-15 (`data_act`, `data_scad`, `numar_act`, `serie_act`) au tratament -conditionat: se propaga si in `DOCUMENTE`/`ACT`/`IREG_PARTENERI`/`JV2007`/`RUL` (unde e cazul), -filtrate dupa `ID_FACT` (nu dupa `ID_VANZARE`) — deci ating toate randurile din acele tabele legate -de acelasi document, nu doar randul `VANZARI` curent. Se scriu doar daca valoarea trimisa e -`NOT NULL` si difera de valoarea curenta. - -## 3. Apelul din VFP - -`COMUN\clase\ofacturare_comun.vc2:4599-4614`, in interiorul unui `SCAN` peste `crsfacturi` (una sau -mai multe inregistrari alese): - -``` -begin pack_facturare.modifica_date_factura(<>, - <>, - <>, - <>, - <>, - to_date('<>','YYYYMMDDHH24:MI:SS'), - <>, - <>, - ?poRec.text_aditional, - ?poRec.tip_saft, - ?poRec.efactura, - ?poRec.data_act, - ?poRec.data_scad, - ?poRec.numar_act, - ?poRec.serie_act); - end; -``` - -Toate argumentele vin din structura `poRec`, populata printr-un `Scatter Name poRec Memo` din -cursorul `crsfacturi` la intrarea in `do_modifica` (`:4538-4581`), apoi editata prin -`frm_modifica_factura` cat timp e afisat (`:4588-4590`), inainte de executia `SCAN`-ului. - -Observatie de comportament: cand sunt alese mai multe inregistrari (`lnNrInreg > 1`), ramura -`Otherwise` (`:4565-4580`) face `Scatter Name poRec Memo Blank` si reseteaza explicit -`id_ruta`, `id_delegat`, `id_agent`, `id_masina`, `dataora_exp`, `id_facturare`, -`listare_detaliata`, `tip_saft`, `text_aditional`, `efactura` la valori goale/`NULL`/`0` — -campurile `data_act`/`data_scad`/`numar_act`/`serie_act` raman goale prin acelasi `NVL`. Daca -formularul nu le modifica explicit, `SCAN`-ul trimite aceste valori goale la **fiecare** inregistrare -aleasa (parametrii 2-11 se scriu neconditionat in corpul procedurii — vezi tabelul de mai sus). - -## 4. Mapare parametru -> control -> fereastra - -| Parametru | Control | Fereastra | Observatii | -|---|---|---|---| -| `V_ID_VANZARE` | — | — | Nu are control; e identitatea randului din `crsfacturi` (`poRec.id_vanzare`) | -| `V_ID_RUTA` | `Ct_clb_ruta` (`ControlSource` prin `cprocedura=thisform.do_cauta_ruta`, seteaza `poRec.id_ruta`) | `frm_modifica_factura` (`:5510-5526`, cautare `:5695-5721`) | | -| `V_ID_DELEGAT` | `Ct_clb_delegat` (`do_cauta_delegat` seteaza `poRec.id_delegat`) | `frm_modifica_factura` (`:5473-5489`, cautare `:5654-5678`) | | -| `V_ID_AGENT` | `Ct_clb_agent` (`do_cauta_agent` seteaza `poRec.id_agent`) | `frm_modifica_factura` (`:5455-5471`, cautare `:5639-5652`) | | -| `V_ID_MASINA` | `Ct_clb_masina` (`do_cauta_masina` seteaza `poRec.id_masina`) | `frm_modifica_factura` (`:5491-5508`, cautare `:5680-5693`) | | -| `V_DATAORA_EXP` | `Clb_dataora_exp.Text_simplu1` (`ControlSource="poRec.dataora_exp"`) | `frm_modifica_factura` (`:5437-5453`) | validat obligatoriu in `inainte_de_do_termin` (`:5723-5734`) | -| `V_ID_FACTURARE` | `clb_adresa_facturare` (`do_cauta_adresa` seteaza `poRec.id_facturare` + `poRec.adresa_facturare`) | `frm_modifica_factura` (`:5418-5435`, cautare `:5621-5637`) | | -| `V_LISTARE_DETALIATA` | `chkDetaliat` (`ControlSource="poRec.listare_detaliata"`) | `frm_modifica_factura` (`:5379-5390`) | | -| `V_TEXT_ADITIONAL` | `Ed_tx_simplu1._EDBASE1` (`ControlSource="poRec.text_aditional"`, `MaxLength=1000`) | `frm_modifica_factura` (`:5528-5551`) | comentariul VFP la `:4575` spune "MAXIM 250 CARACTERE", dar `MaxLength` control e 1000 — contradictie neverificata mai departe | -| `V_TIP_SAFT` | `cboTipFactura` (`ControlSource="poRec.tip_saft"`, `RowSource` din `saft_tip_facturi`) | `frm_modifica_factura` (`:5335-5351`) | `Visible = m.gl406` (`:5739-5741`), deci controlul e ascuns cand `gl406` e fals (`gl406` definit in `COMUN\programe\oinit_optiuni.prg:524`) | -| `V_EFACTURA` | **niciun control** | — | `poRec.efactura` vine doar din `Scatter` initial pe `crsfacturi` (cazurile `lnNrInreg=0/1`, `:4538-4564`) sau e fortat `0` la selectie multipla (`:4576`); formularul `frm_modifica_factura` nu are niciun obiect legat de `poRec.efactura` — nu poate fi schimbat de utilizator din acest formular azi | -| `V_DATA_ACT` | `txtDataAct` (`ControlSource="poRec.data_act"`) | `frm_modifica_factura` (`:5572-5581`) | vezi blocaj la punctul 5 | -| `V_DATA_SCAD` | `txtDataScad` (`ControlSource="poRec.data_scad"`) | `frm_modifica_factura` (`:5583-5592`) | vezi blocaj la punctul 5 | -| `V_NUMAR_ACT` | `txtNrAct` (`ControlSource="poRec.numar_act"`) | `frm_modifica_factura` (`:5594-5603`) | vezi blocaj la punctul 5 | -| `V_SERIE_ACT` | `txtSerieAct` (`ControlSource="poRec.serie_act"`) | `frm_modifica_factura` (`:5605-5614`) | vezi blocaj la punctul 5 | - -Nu exista niciun parametru mapat pe `frm_alte_date` (`COMUN\clase\ferestre_cere_date.vc2:2219`) — -clasa aceea nu e implicata deloc in fluxul `do_modifica`/`modifica_date_factura`. - -## 5. Parametri blocati/needitabili azi in `frm_modifica_factura` - -Patru checkbox-uri controleaza accesul la seria/numarul/datele actului, si patru textbox-uri asociate: - -- `chkSerieAct`, `chkNrAct`, `chkDataAct`, `chkDataScad`: definite cu `Enabled = .F.` - (`:5405-5416`, `:5392-5403`, `:5353-5364`, `:5366-5377`). Devin `Enabled` doar in `PROCEDURE Init` - (`:5743-5746`): - ``` - this.chkSerieAct.Enabled = !EMPTY(NVL(poRec.numar_act,0)) - this.chkNrAct.Enabled = !EMPTY(NVL(poRec.numar_act,0)) - this.chkDataAct.Enabled = !EMPTY(NVL(poRec.numar_act,0)) - this.chkDataScad.Enabled = !EMPTY(NVL(poRec.numar_act,0)) - ``` - adica doar daca factura are deja un numar de act completat (`poRec.numar_act` nenul) la - deschiderea formularului. Pe o factura fara act (proforma/nefinalizata), toate patru raman - needitabile. -- `txtSerieAct`, `txtNrAct`, `txtDataAct`, `txtDataScad`: definite cu `Enabled = .F.` - (`:5605-5614`, `:5594-5603`, `:5572-5581`, `:5583-5592`) si se activeaza doar din evenimentul - checkbox-ului corespunzator — `chkSerieAct.Valid` (`:5770-5772`), `chkNrAct.Valid` - (`:5766-5768`), `chkDataAct.Valid` (`:5758-5760`), `chkDataScad.Click` (`:5762-5764`) — deci - necesita ca checkbox-ul insusi sa fie deja `Enabled` (conditia de mai sus). -- `V_EFACTURA`: fara control — needitabil in acest formular indiferent de stare (punctul 4). -- `V_ID_VANZARE`: nu e un camp de editat, e identitatea randului. - -Toti ceilalti parametri (`id_ruta`, `id_delegat`, `id_agent`, `id_masina`, `dataora_exp`, -`id_facturare`, `listare_detaliata`, `text_aditional`, `tip_saft`) au controale mereu `Enabled` -(fara restrictie in cod), `tip_saft` fiind conditionat doar de vizibilitate (`gl406`), nu de -`Enabled`. diff --git a/docs/cercetare/nota_contabila_fara_politica.md b/docs/cercetare/nota_contabila_fara_politica.md deleted file mode 100644 index bd68816..0000000 --- a/docs/cercetare/nota_contabila_fara_politica.md +++ /dev/null @@ -1,224 +0,0 @@ -# Nota contabila pentru articol fara politica de pret (proiect #13) - -Cercetare pe cod, fara modificari. Continua raportul anterior -`cont_venit_corespondente.md` (SCC vine prin lantul -`CRM_POLITICI_PRET_ART -> CRM_POLITICI_PRETURI.ID_NOTA -> CRM_NOTE_VANZARI.ID_SET -> NOTE_CONTABILE`, -citit de `cursor_articol` din `pack_facturare.contabilizeaza_articol`). - -Sursa: `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_06_10_COMUN_PACK_FACTURARE.sql` -(pachet `pack_facturare`, 17020 linii) si `D:\ROA\ROAAUTO\Programe\oproceduri_devize.prg` + -`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\03\ff_2026_03_31_02_AUTO_PACK_AUTO.sql` (pachet `pack_auto`). - ---- - -## 1. Ce face `contabilizeaza_articol` cand `id_pol` e NULL/0 - -Raspuns: **ridica exceptia FACT-024 si intreruce functia, inainte sa ajunga la cursorul care -citeste SCC**. Nu exista o ramura de default si nu se scrie nota cu SCC NULL pentru acest caz. - -Corpul complet e la `ff_...:7182-7556`. Primul lucru pe care il face (inainte de `cursor_articol`, -inainte de orice `scrie_nota`) e: - -``` -ff_...:7286-7311 -BEGIN - SELECT COMPUS, ID_POL_ART - INTO V_COMPUS, V_ID_POL_ART - FROM VCRM_POLITICI_PRET_ART - WHERE ID_ARTICOL = detalii_articol.id_articol - AND ID_POL = detalii_articol.id_pol; -EXCEPTION - WHEN NO_DATA_FOUND THEN - SELECT DENUMIRE INTO lcArticol FROM NOM_ARTICOLE WHERE id_articol = detalii_articol.id_articol; - SELECT NUME_LISTA_PRETURI INTO lcPolitica FROM CRM_POLITICI_PRETURI WHERE id_pol = detalii_articol.id_pol; - RAISE_APPLICATION_ERROR(-20000, - 'Articolul ' || detalii_articol.id_articol || '|' || lcArticol || - ' nu este definit in politica de preturi ' || - detalii_articol.id_pol || '|' || lcPolitica || '! (FACT-024)'); -END; -``` - -`ID_POL = detalii_articol.id_pol` cu `id_pol` NULL nu poate potrivi niciun rand in SQL standard -(`X = NULL` e necunoscut, nu adevarat) — deci pentru `id_pol` NULL sau 0 (presupunand ca nu exista -o politica cu `ID_POL = 0`), `SELECT INTO` intra mereu pe `NO_DATA_FOUND`. - -**Observatie suplimentara, neceruta explicit dar relevanta**: in ramura de exceptie, al doilea -`SELECT ... INTO lcPolitica FROM CRM_POLITICI_PRETURI WHERE id_pol = detalii_articol.id_pol` sufera -de aceeasi problema — cu `id_pol` NULL, si acest `SELECT INTO` da tot `NO_DATA_FOUND`, care **nu e -tratat** in acest bloc (nu exista un al doilea handler). Deci mesajul FACT-024 "curat" (cu numele -politicii) nu s-ar afisa niciodata pentru cazul `id_pol IS NULL` — s-ar propaga o exceptie -`NO_DATA_FOUND` neprinsa din interiorul handler-ului, cu alt mesaj Oracle generic, nu FACT-024 cu -text. Codul articolului (`lcArticol`) apuca sa fie rezolvat inaintea acestui eșec, deci -identificarea articolului nu s-ar pierde in log/eroare, dar textul final ar fi ORA-01403, nu -mesajul FACT-024 formatat. Pentru `id_pol = 0` (nu NULL), daca exista vreo politica cu id 0, acel -`SELECT` ar reusi si mesajul FACT-024 ar iesi corect. - -Cursorul `cursor_articol` (care citeste SCD/ASCD/SCC/ASCC din `NOTE_CONTABILE` prin lant) **nu se -mai deschide** — funcția a ridicat deja exceptia mai sus. Deci intrebarea "cursorul intoarce zero -randuri, ce face codul mai departe" nu se pune in acest caz: nu ajunge acolo. - -Concluzie Q1: **orice linie de vanzare cu `id_pol` NULL/0 trimisa la `contabilizeaza_articol` in -starea actuala a codului arunca o eroare si opreste tranzactia** (nu scrie nota cu cont NULL, nu -pune default). Cerinta proiectului #13 ("articol din nomenclator fara politica de pret, pret -tastat manual, pe orice tip de document") ar lovi FACT-024 (sau exceptia neprinsa descrisa mai sus) -daca linia respectiva e trecuta prin acest cod neschimbat. - ---- - -## 2. Validare la `scrie_nota` pentru cont NULL - -Raspuns: **nu exista**. Corpul complet e la `ff_...:12338-12570`. - -- Singura verificare care ar fi atins asta e comentata: - ``` - ff_...:12441-12453 - /* IF V_SUMA IS NULL THEN - RAISE_APPLICATION_ERROR(...) ... - END IF;*/ - ``` - Si oricum verifica `V_SUMA` (suma calculata), nu `V_SCD`/`V_SCC`. -- `INSERT INTO ACT_TEMP (... SCD, ASCD, SCC, ASCC ...)` (`ff_...:12458-12544`) insereaza direct - `V_SCD`/`V_ASCD`/`V_SCC`/`V_ASCC` primite ca parametri, fara niciun `NVL`/`CASE`/verificare de - NULL pe ele. -- Nu exista `NOT NULL` verificat in cod pentru aceste coloane in acest fisier (constrangerea ar fi - la nivel de DDL al tabelei `ACT`/`ACT_TEMP`, care nu face parte din pachetul PL/SQL cercetat — - vezi sectiunea Neverificat). - -Deci daca cineva ar ocoli blocul de la punctul 1 (de ex. printr-un `id_pol` care *exista* in -`CRM_POLITICI_PRET_ART` dar al carui lant `CRM_POLITICI_PRETURI -> CRM_NOTE_VANZARI -> NOTE_CONTABILE` -e incomplet, asa incat `D.SCC` iese NULL din LEFT JOIN), `scrie_nota` ar scrie nota cu `SCC = NULL` -fara sa protesteze. Asta e insa un scenariu diferit de "fara politica de pret" (aici politica exista, -dar lantul de sub ea e incomplet) — nu e cazul cerut de proiectul #13 asa cum a fost formulat. - ---- - -## 3. Toti apelantii lui `contabilizeaza_articol` in fisier - -Cautare exhaustiva `Grep 'contabilizeaza_articol'` pe tot fisierul: 3 apeluri (in afara de propria -definitie si un comentariu): - -| Linie | Procedura apelanta | Domeniu / flux | -|---|---|---| -| `ff_...:6150` | `scrie_factura2` (corp `ff_...:6029-6272`) | **Fluxul principal de scriere** — pentru orice document ale carui randuri nu cad in ramurile speciale ale `CASE`-ului de la `ff_...:6081-6151`: transfer intre subunitati (`ntip IN (23,25,30,41)`), factura/aviz custodie (`ntip IN (42,47)`), rata cu `id_rata<>0` (`ntip IN (2,6,52)`). Toate celelalte tipuri — **factura normala** (`ntip<=20`), hotel, restaurant, nota de plata, vanzare retail, tipurile 48/49/51/52, aviz simplu, aviz catre clienti debitori, si **aviz retur (tip 24)** cand e scris prin `scrie_factura2` — ajung la `contabilizeaza_articol` aici. Aceasta e apelarea "de baza" pe care se sprijina interpretarea corpului functiei facuta la punctul 1 (ramurile `CASE` din interiorul `contabilizeaza_articol`, `ff_...:7407-7432`, mapeaza direct pe tipurile de document tratate de acest apelant). | -| `ff_...:6867` | `scrie_factura_avize_retur` (corp `ff_...:6273-6666`) | Flux **factura scrisa pe baza de avize + retur simultan**. Apelul e in interiorul buclei pe `articole_aviz` (`ff_...:6841-6960` zona), pentru randurile cu `custodie = 1` (marfa in custodie ce se descarca din avize). | -| `ff_...:7149` | `scrie_aviz_retur` (corp `ff_...:7094-7180`) | Procedura dedicata **aviz retur** (`V_ID_DELEGAT, V_ID_MASINA, ...`). Apelul e in ramura `ELSE` a testului `pack_facturare.nfactavizcust = 1` (`ff_...:7124-7150`) — deci pentru randurile care **nu** sunt in custodie. | - -Concluzie Q3: **da**, `contabilizeaza_articol` e apelata pe fluxul principal de facturare -(`scrie_factura2`, care acopera factura normala si majoritatea celorlalte tipuri de document), nu -doar din `scrie_aviz_retur`. Raportul anterior lasase asta neverificat; e confirmat aici cu cele 3 -puncte de apel gasite si citate mai sus — nu exista un al 4-lea apel in fisier. - ---- - -## 4. Precedentul ROAAUTO "Alte servicii" — **nu e de fapt un precedent pentru cazul cerut** - -Asta contrazice premisa din cerere. Firul (`crsalteserv -> crsvanztemp -> adauga_articol_factura_deviz`) -duce spre un flux care **ocoleste complet `contabilizeaza_articol`**, deci nu demonstreaza ce se -intampla in `pack_facturare` pentru un articol fara politica — demonstreaza doar ca exista un *alt* -mecanism de contabilizare, in afara pachetului `pack_facturare`. - -Pasii, cu citate: - -1. **`crsalteserv -> crsvanztemp`** (`D:\ROA\ROAAUTO\Programe\oproceduri_devize.prg:1040-1041`): - ``` - Insert Into (lcCursorDeviz)(id_articol,denumire,explicatie,cantitate,um,id_jtva_coloana,proc_Tvav,taxcode,pret,discount_unitar) ; - Select id_articol,denumire,"",cantitate,um,CAST(lnIdJTvaColoana as N(6) null) as id_jtva_coloana,CAST(lnProcTvav as N(7,2) null) as proc_tvav,CAST(lnTaxCode as N(6) null) as taxcode,pretftva,0 From crsalteserv Where !Deleted() - ``` - Nu exista coloana `id_pol` in `crsalteserv`, in `lcCursorDeviz`, sau in cursorul `crsvanztemp` - creat la `oproceduri_devize.prg:1190-1192` (schema explicita a cursorului nu include `id_pol`). - -2. **`crsvanztemp -> adauga_articol_factura_deviz`** (`oproceduri_devize.prg:1240-1257`): apelul RPC - trimite `id_articol, explicatie, serie, pret_achizitie, pretd, id_valuta_d, pret, id_valuta, - curs, multiplicator, proc_tvav, id_jtva_coloana, cantitate, discount_unitar, id_gestiune, cont, - pret_cu_tva, NULL, NULL, taxcode` — **fara `id_pol`**, pentru ca procedura insasi nu are un - parametru `id_pol`. - -3. **Semnatura `adauga_articol_factura_deviz`** (spec `ff_...:468-488`, corp `ff_...:4675-4745`): - nu exista `V_ID_POL IN NUMBER` printre parametri. `INSERT INTO VANZARI_DETALII_TEMP` la - `ff_...:4697-4744` **nu include coloana `ID_POL`** in lista de coloane inserate — deci - `ID_POL` ramane pe valoarea implicita a coloanei (probabil NULL; DDL-ul tabelei nu face parte - din pachetul cercetat, vezi Neverificat). **Asta confirma partea (a) a intrebarii: da, liniile - "alte servicii" ajung cu `id_pol` gol** in `VANZARI_DETALII_TEMP`. - -4. **Dar acele randuri nu trec niciodata prin `contabilizeaza_articol`.** Apelantul din - `oproceduri_devize.prg` nu cheama `scrie_factura2` / `scrie_factura_avize` / `scrie_aviz_retur` - (singurele proceduri care cheama `contabilizeaza_articol`, vezi punctul 3). In schimb cheama, in - ordine (`oproceduri_devize.prg:1211-1310`): - - `pack_facturare.initializeaza_date_factura(...)` - - bucla `pack_facturare.adauga_articol_factura_deviz(...)` (doar insert in `VANZARI_DETALII_TEMP`) - - opțional `pack_facturare.scrie_incasari(...)` (incasare, nu venit din vanzare) - - `pack_facturare.scrie_in_vanzari(0, ...)` (`ff_...:13497-13962`) - - `pack_auto.actualizeaza_deviz(...)` (`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\03\ff_2026_03_31_02_AUTO_PACK_AUTO.sql:692-733`) - - **`scrie_in_vanzari` nu scrie nici o nota contabila de venit.** Corpul complet - (`ff_...:13497-13962`) face: `INSERT INTO VANZARI` (antetul documentului), `scrie_cursuri`, - `scrie_seturi`, apoi - ``` - ff_...:13714-13766 - INSERT /*+ APPEND */ INTO VANZARI_DETALII (..., ID_POL, ...) - SELECT ..., ID_POL, ... FROM VANZARI_DETALII_TEMP; - ``` - (copiaza `ID_POL` — care e NULL pentru aceste randuri — direct in `VANZARI_DETALII`, fara nicio - validare), si la final actualizeaza totalurile cache pe `VANZARI`. **Nu apare niciun apel catre - `contabilizeaza_articol`, `scrie_nota`, `cumuleaza_note_act` sau `finalizeaza_factura` in acest - corp** (verificat citind procedura cap-coada). - - `pack_auto.actualizeaza_deviz` (`ff_2026_03_31_02_AUTO_PACK_AUTO.sql:692-733`) face doar: - ``` - SELECT MAX(ID_FACT) INTO lnIdFact FROM ACT WHERE COD = pack_contafin.get_cod() AND SCD NOT LIKE '5%'; - UPDATE DEV_ORDL SET PROC_TVAV = ... WHERE ID_ORDL IN (...); - UPDATE RUL SET ID_FACT = lnIdFact WHERE ...; - UPDATE NOM_LUCRARI SET ID_FACT = lnIdFact WHERE ... (doar pt anumite id_set); - ``` - Adica **presupune ca exista deja un rand in `ACT`** (nu `ACT_TEMP`) cu `SCD NOT LIKE '5%'` pentru - codul documentului curent, si doar propaga `ID_FACT` gasit acolo spre `DEV_ORDL`/`RUL`/`NOM_LUCRARI`. - Nu insereaza el insusi in `ACT` sau `NOTE_CONTABILE`, si nu contine nicio referinta la `SCC`, - `NOTE_CONTABILE` sau `CRM_POLITICI*` (cautat explicit, zero rezultate in acest fisier). - -**Concluzie Q4, corectand premisa din cerere**: liniile "Alte servicii" chiar ajung cu `id_pol` gol -(confirmat), dar **nu demonstreaza ca `contabilizeaza_articol` tolereaza `id_pol` gol** — pentru ca -nu trec niciodata prin el. Ele demonstreaza doar ca exista deja, pentru fluxul deviz ROAAUTO, o cale -de facturare paralela (`scrie_in_vanzari` + `pack_auto.actualizeaza_deviz`) care **nu genereaza deloc -nota contabila de venit prin `pack_facturare`**. Daca acele randuri capata totusi un cont de venit in -`ACT`/`NOTE_CONTABILE` in productie, mecanismul nu e vizibil in codul cercetat (posibil un -trigger pe tabel, un job batch, sau o scriere manuala/alt modul neexaminat — vezi Neverificat). -**Nu se poate folosi acest precedent ca raspuns de fapt la intrebarea 1.** - ---- - -## 5. Vreun mecanism care sa foloseasca `NOM_ARTICOLE.CONT` drept cont de venit - -Raspuns: **NU**, in `pack_facturare`. - -Cautat explicit `Grep -i 'NOM_ARTICOLE\.CONT'` pe intregul fisier -`ff_2026_08_06_10_COMUN_PACK_FACTURARE.sql` (17020 linii) — **zero potriviri**. Singurele utilizari -ale `NOM_ARTICOLE` gasite in `contabilizeaza_articol` insusi sunt pentru `DENUMIRE` (mesajul de -eroare FACT-024, `ff_...:7295-7298`), nu pentru `CONT`. - -`crs_rand_articol.scc` (folosit ca `V_SCC` la `ff_...:7434`) vine exclusiv din -`D.SCC` din join-ul `CRM_POLITICI_PRET_ART -> CRM_POLITICI_PRETURI -> CRM_NOTE_VANZARI -> NOTE_CONTABILE` -(`ff_...:7261, 7275-7276`) — nu exista ramura alternativa care sa substituie `NOM_ARTICOLE.CONT` -cand politica lipseste. (Raportul anterior confirmase deja separat ca `NOM_ARTICOLE.CONT` e folosit -doar ca **cont de gestiune**, transmis catre `descarca_gestiune`, `ff_...:7499`.) - ---- - -## Neverificat - -- **Continutul efectiv al `NOTE_CONTABILE`/`CRM_POLITICI_PRET_ART`** (daca exista politici cu - `ID_POL = 0`, ce SCC au liniile din productie pentru documente "alte servicii" existente) — - necesita interogare pe baza de date, nu doar cod. -- **Constrangerile DDL** ale `VANZARI_DETALII_TEMP.ID_POL` si `ACT_TEMP.SCC`/`ACT.SCC` (NOT NULL? - default?) — scripturile DDL ale acestor tabele nu au fost cautate/citite in aceasta cercetare - (am cautat doar in pachetele PL/SQL `ff_...COMUN_PACK_FACTURARE.sql` si `..._AUTO_PACK_AUTO.sql`). -- **Cine scrie efectiv contul de venit (`ACT`/`NOTE_CONTABILE`) pentru documentele "alte servicii" - din ROAAUTO**, daca se scrie deloc. `pack_auto.actualizeaza_deviz` presupune ca exista deja un - rand `ACT` cu `SCD NOT LIKE '5%'` pentru codul documentului curent — sursa acelui rand nu a fost - gasita in `oproceduri_devize.prg` sau in corpul `scrie_in_vanzari`/`actualizeaza_deviz` cercetate. - Ar trebui cautat: alte proceduri `pack_auto` (fisierul `ff_2026_03_31_02_AUTO_PACK_AUTO.sql` are - si alte proceduri necercetate aici), triggere pe `VANZARI`/`VANZARI_DETALII`/`ACT`, sau apeluri - din alte forme VFP (`.scx`/`.vcx`) din ROAAUTO care preced acest apel RPC. Nu am cautat exhaustiv - triggere in afara folderului `SCRIPTURI_CLAR\2026` (cautare limitata, zero rezultate acolo). -- **Ce se intampla exact daca `id_pol = 0` si exista o politica reala cu `ID_POL = 0`** (caz - teoretic, neexclus explicit din schema) — ar schimba concluzia de la "eroare neprinsa" la - "FACT-024 cu mesaj complet", dar tot ramane o eroare, nu un default silentios. diff --git a/docs/cercetare/optiune_firma_cont_debit.md b/docs/cercetare/optiune_firma_cont_debit.md deleted file mode 100644 index c97cae8..0000000 --- a/docs/cercetare/optiune_firma_cont_debit.md +++ /dev/null @@ -1,373 +0,0 @@ -# Optiune de firma pentru contul de debit (`SCD`) pe ramura fara politica de pret (#13, decizia 36) - -Cercetare read-only, terminata. Continua `canal_cont_venit_fara_politica.md` (proiectarea -parametrului de cont, sectiunea "Proiectarea parametrului de cont contabil", `:27-241`) si -verificarea ei, `parametru_cont_contabilizeaza_articol.md`. Acolo, randul `SCD` din tabelul de -sectiunea 3 propunea **`SCD` hardcodat `'4111'`**, marcat explicit "de confirmat cu Marius" -(`canal_cont_venit_fara_politica.md:146`). **Decizia 36 (Marius, 10.08.2026): `SCD` vine dintr-o -optiune de firma, cu `4111` ca implicit** — motivul fiind ca se poate schimba la client fara -recompilare. Aici e proiectarea acelei optiuni. Decizia 31 se aplica: datele din baza de dev arata -ce *exista*, nu ce e configurat la client — nu se generalizeaza pe numere, doar pe tipar/structura. - -Oracle interogat doar `SELECT`, schema `MARIUSM_AUTO`/`ROA_CENTRAL`. Niciun fisier de cod atins. - ---- - -## 0. Rezumat, in cinci randuri - -Mecanismul de optiuni de firma **exista deja de 15+ ani** (`PACK_SESIUNE.getoptiunefirma`, tabelul -`OPTIUNI`, 458 randuri azi) si are **exact tiparul cerut deja in productie, in acelasi pachet**: -`pack_facturare.scrie_incasare2` seteaza `V_SCD` dintr-o optiune (`RF_CONT_INCASARE_BONFISCAL` etc., -cate una per tip de incasare), cu fallback pe un cont hardcodat daca optiunea lipseste — **acelasi -camp (`SCD`), acelasi pachet, aceeasi sursa Oracle** (`ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql`, -liniile 13191-13233, tratate integral in sectiunea 2). Ecranul de editare (`frm_optiuni`/ -`frm_optiuni_nou`, `COMUN\clase\oOptiuni.vc2`) e **complet generic** peste tabelul `OPTIUNI` — -adaugarea cheii noi **nu cere niciun cod VFP nou** in ecranul de optiuni, doar randul in tabel. -Propunerea (sectiunea 3): cheia noua `FACT_SCD_ARTFPRET` (sau numele ales de Marius), instalata cu -valoare implicita `'4111'` chiar in randul din `OPTIUNI` (tiparul deja folosit de toate exemplele -gasite), **plus** fallback hardcodat `'4111'` in cod langa `getoptiunefirma`, in oglinda exacta a -tiparului `scrie_incasare2` — dubla plasa de siguranta (instalare veche fara randul din `OPTIUNI` -**si** rand editat gresit/gol raman amandoua acoperite). - ---- - -## 1. Cum functioneaza mecanismul de optiuni de firma, azi - -### 1.1 Stocare — tabelul `OPTIUNI` - -Interogat direct (`ALL_TAB_COLUMNS`, schema curenta), 458 randuri azi: - -| Coloana | Tip | Null | Rol | -|---|---|---|---| -| `ID_OPTIUNI` | `NUMBER` | N | cheie surogat, PK implicita | -| `VARNAME` | `VARCHAR2(30)` | Y | cheia optiunii — cautata `UPPER(TRIM(...))`, deci case-insensitive in practica | -| `VARTYPE` | `VARCHAR2(10)` | Y | eticheta de tip pentru randare/parsare in VFP: `CHARACTER`/`NUMERIC`/`CURRENCY`/`DATE`/`DATETIME`/`LOGICAL` — **descriptiva, nu impusa de Oracle**; distinct azi: `CHARACTER` 156, `NUMERIC` 288, `LOGICAL` 11, `DATE` 3 | -| `VARVALUE` | `VARCHAR2(1000)` | Y | valoarea, **mereu text**, indiferent de `VARTYPE` | -| `VARVALUE2` | `CLOB` | Y | valoare alternativa, folosita de un mecanism separat (`citeste_optiune(tcOptiune, tlValue2)`, `oinit_optiuni.prg:802-823`) — irelevant aici | -| `VARDESC` | `VARCHAR2(100)` | Y | descriere afisata in ecran | -| `PROGRAM` | `VARCHAR2(30)` | Y | produsul care a creat optiunea (metadata) | -| `PROGRAME` | `VARCHAR2(1000)` | Y | lista CSV de produse pentru care optiunea e vizibila/relevanta (folosita la filtrare in incarcarea globala VFP, sectiunea 1.4) | -| `ID_PRG_OWNER` | `NUMBER` | Y | neexaminat mai departe, neрелevant aici | - -**Fara `ID_FIRMA`.** Motivul: fiecare firma are **schema Oracle proprie** — tabelul `OPTIUNI` din -schema curenta de conexiune **este** deja "optiunile firmei curente"; nu exista randare -multi-firma in acelasi tabel. Confirmat si de codul VFP: `frm_optiuni.cschema` e setat la -constanta de compilare `CONTAFIN_ORACLE` (`oOptiuni.vc2:1089`, acelasi simbol folosit peste tot in -COMUN pentru "schema firmei curente"), iar `getoptiunefirma(tcS, tcOptiune)` **primeste** un -parametru de schema (`tcS`, azi mereu `USER`) dar **nu il foloseste in interogare** — `select -varvalue from optiuni` fara calificare de schema (cod exact la sectiunea 1.2) — pentru ca sesiunea -Oracle e deja conectata direct in schema firmei. `tcS` ramane doar din compatibilitate istorica cu -supraincarcarea `getOptiuneFirma(tcOptiune)` (fara schema), care apeleaza pe cea cu doi parametri -cu `user` (sectiunea 1.2). - -### 1.2 Semnatura si contract — `PACK_SESIUNE.getoptiunefirma` - -Sursa curenta confirmata prin `ALL_SOURCE` pe pachetul live (`PACK_SESIUNE`, tip `PACKAGE BODY`) — -identica, verificat, cu ultima versiune gasita in `SCRIPTURI_CLAR` -(`2014/04/ff_2014_04_24_01_COMUN_PACK_SESIUNE.sql:328-350`; nicio modificare de atunci, cautare -`FUNCTION getoptiunefirma` in `SCRIPTURI_CLAR/2024`, `/2025`, `/2026` — zero rezultate): - -```sql -FUNCTION getoptiunefirma(tcS IN VARCHAR2, tcOptiune IN VARCHAR2) - RETURN VARCHAR2 IS - lcValue Optiuni.Varvalue%Type := ''; -BEGIN - BEGIN - select varvalue - INTO lcValue - from optiuni - where UPPER(TRIM(VARNAME)) = UPPER(TRIM(tcOptiune)); - EXCEPTION - WHEN OTHERS THEN - lcValue := ''; - END; - RETURN lcValue; -END; - -FUNCTION getOptiuneFirma(tcOptiune IN VARCHAR2) RETURN VARCHAR2 IS - lcValue Optiuni.Varvalue%Type := ''; -BEGIN - lcValue := getOptiuneFirma(user, tcOptiune); - return lcValue; -END; -``` - -Contract, verificat pe `RETURN`, nu dedus din nume: - -- **Parametru**: numele optiunii (`tcOptiune`), cautat `UPPER(TRIM(...))` — insensibil la caz si la - spatii; a doua forma (`getOptiuneFirma(tcOptiune)`, un singur parametru) e cea folosita azi peste - tot in `pack_facturare` (vezi sectiunea 2) — deleaga la forma cu doi parametri cu `user`. -- **Cand optiunea NU exista** (`NO_DATA_FOUND`, prins de `WHEN OTHERS` generic): **`RETURN ''`** - (string gol), **niciodata `NULL` explicit si niciodata exceptie propagata**. Insa in Oracle un - `VARCHAR2` egal cu `''` **este** `NULL` la nivel de reprezentare (Oracle nu distinge stringul gol - de `NULL`) — deci orice apelant care testeaza `IF V_X IS NULL THEN` dupa un apel la - `getoptiunefirma` **prinde si cazul "optiune inexistenta"**, fara cod suplimentar. Acesta e - exact tiparul folosit de toti apelantii gasiti (sectiunea 2). -- **`WHEN OTHERS THEN lcValue := ''`** inghite **orice** eroare, nu doar `NO_DATA_FOUND` (de ex. si - `TOO_MANY_ROWS` daca ar exista vreodata doua randuri cu acelasi `VARNAME` — nu exista constrangere - `UNIQUE` pe `VARNAME`, doar convenția aplicatiei). Nu e un risc nou introdus de propunerea de - fata — e comportamentul mecanismului de 15+ ani, pastrat ca atare. -- **Variante**: **o singura functie**, intotdeauna `VARCHAR2`. **Nu exista** `getoptiunefirma` - numerica/booleana separata in Oracle — conversia se face **la apelant**, in PL/SQL cu - `TO_NUMBER(NVL(...))` (exemplu confirmat, acelasi fisier, `scrie_incasare2`, - `ff_...:13177`: `TO_NUMBER(NVL(pack_sesiune.getoptiunefirma('D406'), '0'))`) sau in VFP cu - `VAL()`/`CTOD()`/etc., pe baza coloanei `VARTYPE` (vezi `oinit_optiuni.prg:179-211`, functia - `actualizeaza_optiuni_program`, care materializeaza fiecare optiune intr-o variabila globala VFP - cu prefix dupa tip: `gc` pentru `CHARACTER`, `gn` pentru `NUMERIC`, - `gl` pentru `LOGICAL` etc.). **Pentru un cont contabil (`VARCHAR2(4)`), varianta corecta e - cea de baza, `VARTYPE='CHARACTER'`** — exact tipul folosit de toate exemplele cu cont din - sectiunea 2, fara nicio conversie necesara la citire in PL/SQL (`V_SCD` e deja `VARCHAR2`). - -### 1.3 Ecranul de editare — complet generic peste tabel - -`COMUN\clase\oOptiuni.vc2`, doua clase relevante: - -- **`frm_optiuni`** (`:1064-1314`) — grid pe view-ul VFP local `v_optiuni` (populat dintr-un - `SELECT * FROM .optiuni`, vezi `oinit_optiuni.prg:337` in `viz_optiuni`), 4 coloane - (`varname`, `vartype`, `varvalue`, `vardesc`), cu cautare doar dupa `varname LIKE` - (`do_cauta`, `:1261-1278`) si butoane Adauga/Modifica/Sterge care deschid `frm_optiuni_nou`. -- **`frm_optiuni_nou`** (`:1316-1462`) — formular Adauga/Modifica cu 4 campuri: `varname` (text, - `Format="!K"` = uppercase), `vartype` (combobox cu lista fixa `CHARACTER,CURRENCY,NUMERIC, - DATETIME,DATE,LOGICAL`), `varvalue` (text), `vardesc` (text). Validare la salvare - (`inainte_de_do_termin`, `:1417-1450`): **doar "nu e gol"** pe `varname`, `vartype`, `varvalue` — - **nicio validare de format sau de continut** (nu verifica lungime, nu verifica daca `varvalue` - e un cont existent in planul de conturi, nimic specific per optiune). -- **`cus_odata_optiuni`** (`:139-206`) — clasa de persistenta: `INSERT`/`UPDATE`/`DELETE` generic pe - `optiuni (varname, vartype, varvalue, vardesc)` — confirma ca **orice cheie noua** functioneaza - fara nicio schimbare la acest cod; scrie exact cele 4 coloane editabile din ecran. - -**Concluzie la intrebarea "adaugarea unei optiuni noi cere cod nou in ecran": NU.** Ecranul e -generic peste tabel dupa cele 4 coloane editabile. Randul nou (`FACT_SCD_ARTFPRET`, sectiunea 3) -apare automat in grid dupa ce e inserat, si e editabil din prima fara nicio modificare VFP. - -*Nota conexa, nu necesara pentru mecanismul Oracle*: la fiecare pornire de sesiune, VFP -materializeaza **toate** randurile din `OPTIUNI` in variabile globale (`optiuni_firma`, -`oinit_optiuni.prg:234-310`), filtrate `Isnull(programe) Or gcNumeProgram $ programe` — deci coloana -`PROGRAME` conteaza doar pentru **vizibilitatea in acest mecanism global VFP**, nu pentru -`getoptiunefirma` (care nu se uita deloc la `PROGRAME`). Optiunea noua nu are nevoie de acest canal -VFP (Oracle o citeste direct), dar completarea corecta a `PROGRAME` evita zgomot/confuzie daca -cineva se uita in `frm_optiuni` din ROAFACTURARE si se asteapta sa vada optiunea listata acolo cu -`gcNumeProgram = 'ROAFACTURARE'` in `PROGRAME`. - ---- - -## 2. Exemple reale de optiuni existente, cu lantul complet - -### 2a. `RF_CONT_INCASARE_*` — exact tiparul cerut, in acelasi pachet, pe acelasi camp `SCD` - -**Cel mai relevant exemplu posibil**: patru optiuni care alimenteaza direct `SCD` pe o nota -contabila, in `pack_facturare.scrie_incasare2` — **aceeasi functie Oracle pe care propunerea de la -`canal_cont_venit_fara_politica.md` o modifica** (acelasi fisier, -`ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql`). - -**Randul din tabel** (interogat direct, 4 randuri, VARTYPE `CHARACTER` la toate): - -| VARNAME | VARVALUE azi | VARDESC | PROGRAM | PROGRAME | -|---|---|---|---|---| -| `RF_CONT_INCASARE_BONFISCAL` | `5311` | `CONTUL DEBITOR PENTRU NOTA DE INCASARE 5311 = 4111` | `ROAFACTURARE` | `ROAFACTURARE,ROAACNPRO,ROACOMENZI,ROACONTRACTE,ROAGEST,ROARESTAURANT` | -| `RF_CONT_INCASARE_CARDBANCAR` | `5125` | `CONTUL DEBITOR PENTRU NOTA DE INCASARE 5113 = 4111` | idem | idem | -| `RF_CONT_INCASARE_TICHETE` | `5323` | `CONTUL DEBITOR PENTRU NOTA DE INCASARE 5323 = 4111` | idem | idem | -| `RF_CONT_INCASARE_CHITANTA` | `5311` | `CONTUL DEBITOR PENTRU NOTA DE INCASARE 5311 = 4111` | idem | idem | - -(`VARDESC` mentioneaza si `= 4111` — nu e o eroare de copy-paste: e nota ca partea de credit a -notei de incasare e contul de clienti `4111`, deci descrierea documenteaza ambele parti ale notei, -nu doar valoarea implicita a optiunii.) - -**Insertia originala** (`SCRIPTURI_CLAR/2010/08/ff_2010_08_11_01_OPTIUNI.sql:9-19`), tiparul de -migrare de urmat: - -```sql -insert into optiuni (VARNAME, VARTYPE, PROGRAM, VARVALUE, VARDESC, ID_PRG_OWNER, PROGRAME) -values ('RF_CONT_INCASARE_BONFISCAL', 'CHARACTER', 'ROAFACTURARE', '5311', - 'CONTUL DEBITOR PENTRU NOTA DE INCASARE 5311 = 4111', null, 'ROAFACTURARE'); -``` - -**Apelul din PL/SQL** (`ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:13161-13234`, -`PROCEDURE scrie_incasare2`), citit integral — **exact tiparul "optiune cu fallback hardcodat" cerut -de decizia 36**: - -```sql -V_SCD := V_PSCD; -- parametru explicit, daca a fost dat de apelant (ff_...:13185) -CASE - WHEN V_TIP = nTipIncasareBonFiscal THEN - ... - IF V_SCD IS NULL THEN - V_SCD := PACK_SESIUNE.getoptiunefirma('RF_CONT_INCASARE_BONFISCAL'); - END IF; - IF V_SCD IS NULL THEN - V_SCD := '5311'; -- fallback hardcodat, instalare veche - END IF; - ... -END CASE; -``` - -Trei niveluri, in ordine: **(1) parametru explicit daca a fost dat, (2) optiunea de firma, (3) -constanta hardcodata** — folosita si daca randul din `OPTIUNI` lipseste complet (instalare veche, -migrare neaplicata) *si* daca a fost sters/golit manual din ecran. Acest cont ajunge apoi direct in -`INSERT INTO ACT_TEMP (..., SCD, ...)` (`ff_...:13241-13249` in continuare), **fara nicio validare -suplimentara de format sau de existenta in planul de conturi** — nici in Oracle, nici in ecranul de -optiuni (sectiunea 1.3), nici la `INSERT`. - -**Ecranul de editare**: acelasi `frm_optiuni`/`frm_optiuni_nou` generic (sectiunea 1.3) — niciun -ecran dedicat pentru aceste 4 optiuni, se editeaza ca text simplu in grid. - -### 2b. `D406` — optiune numerica/booleana folosita ca flag, cu conversie explicita la citire - -`VARNAME='D406'`, `VARTYPE='NUMERIC'`, `VARVALUE='1'`, `PROGRAM='ROACONT'`, -`PROGRAME='ROACONT,ROAAUTO,ROACONTRACTE,ROADEVIZE,ROAFACTURARE,...'`. Citita in acelasi -`scrie_incasare2` (`ff_...:13177`): `lnD406 := TO_NUMBER(NVL(pack_sesiune.getoptiunefirma('D406'), -'0'))` — **tipar de citire pentru optiune numerica**: `getoptiunefirma` intoarce mereu text, -apelantul face `NVL(...,'0')` inainte de `TO_NUMBER` (aici fallback-ul `'0'` e in acelasi loc cu -apelul, nu separat ca la `RF_CONT_INCASARE_*` — variatie minora de stil intre autori, nu o regula -diferita). - -### 2c. `CONT411` si `DEVCONTALTELE` — optiuni "cont" mai vechi, azi orfane in cod - -`CONT411` (`VARTYPE='CHARACTER'`, `VARVALUE='4111'`, `VARDESC='411 SAU 4111'`, fara `PROGRAM`) si -`DEVCONTALTELE` (`VARTYPE='CHARACTER'`, `VARVALUE='704'`, `VARDESC='CONT ALTE SERVICII FACTURA'`, -`PROGRAM='ROAAUTO'`) — cautare `grep` pentru ambele in tot `SCRIPTURI_CLAR`: **niciun apelant activ -gasit** (doar insertiile originale din 2011). Probabil cod dezafectat sau folosit doar din alt -produs/raport neexaminat aici. Mentionate pentru completitudine si pentru ca `CONT411` confirma -**exact valoarea implicita ceruta de Marius** (`4111`) ca precedent de configurare tot pe un cont -de clienti — dar **nu** au un lant activ de apel, deci nu sunt un exemplu de "tipar in uz" la fel de -tare ca `RF_CONT_INCASARE_*`. - -### 2d. `EFACTURA_CONT_ART_E`/`EFACTURA_CONT_ART_P` — optiune "cont implicit pe articol", azi goala - -`VARTYPE='CHARACTER'`, `VARVALUE` **goala** azi (nesetata pe baza curenta), `VARDESC='CONT ARTICOLE -IMPLICIT FACTURI EMISE'`/`'...PRIMITE'`, `PROGRAM='ROACONT'`. Confirma ca tiparul "cont implicit -configurabil, gol pana e setat manual" exista deja ca optiune (nu s-a putut verifica in aceasta -runda cine o citeste — cautare in afara scopului, modulul `ROACONT`/eFactura nu a fost deschis). -Relevanta doar ca a patra confirmare a conventiei de nume (`EFACTURA_CONT_ART_`), nu ca lant -complet verificat. - ---- - -## 3. Propunerea concreta - -### 3.1 Cheia optiunii - -**`FACT_SCD_ARTFPRET`** — respecta conventia dominanta observata in tabel: prefix de modul -(`FACT` pentru optiunile specifice `pack_facturare`/ROAFACTURARE, ca `FACTSETURI`, -`FACTALGREPCOM`, `FACTURACUCHITANTA`, `FACTURAEMAIL`, `FACTURASOLD` — toate `PROGRAM='ROAFACTURARE'`) -plus sufix descriptiv scurt fara diacritice si fara spatii (`SCD` = campul contabil vizat, -`ARTFPRET` = "articol fara politica de pret" — in oglinda cu numele deja folosit in proiectarea -anterioara, "ramura fara politica"). Nicio cheie `SCD`/`SCC` existenta azi in tabel (cautare directa, -zero rezultate) — nu exista risc de coliziune de nume. **Alternativa mai scurta**, daca Marius -prefera: `SCD_FARA_POLITICA` (fara prefix de modul) — tiparul din tabel nu e strict, exista si chei -fara prefix (`D406`, `CONT411`); prefixul `FACT_` e recomandat, nu obligatoriu. - -### 3.2 Valoarea implicita si unde traieste - -**Tiparul deja stabilit si folosit de `RF_CONT_INCASARE_*` (sectiunea 2a) se copiaza identic**: -implicitul traieste **in doua locuri**, nu unul: - -1. **In randul din `OPTIUNI`, la instalare** — valoarea `VARVALUE='4111'` scrisa direct de scriptul - de migrare (sectiunea 3.4). Asta e valoarea pe care o vede si o poate schimba Marius/clientul din - `frm_optiuni`, fara recompilare — exact cerinta deciziei 36. -2. **Hardcodat in PL/SQL, langa apelul `getoptiunefirma`**, ca a doua plasa de siguranta pentru - instalari vechi unde migrarea nu a rulat inca sau randul a fost sters/golit manual din ecran: - -```sql -V_SCD := PACK_SESIUNE.getoptiunefirma('FACT_SCD_ARTFPRET'); -IF V_SCD IS NULL THEN -- acopera si randul lipsa, si VARVALUE gol/sters din ecran (sectiunea 1.2) - V_SCD := '4111'; -END IF; -``` - -Plasata **in ramura noua din `contabilizeaza_articol`** (`canal_cont_venit_fara_politica.md:146`, -randul `SCD` din tabelul design-ului), **in locul** liniei `SCD := '4111'` hardcodat propuse acolo — -inlocuire punctuala, restul ramurii (`ASCD` calculat din `GetAnaliticByGrupUtilizatori(..., -V_SCD)`, deja dependent de `V_SCD`) ramane neschimbat, primeste automat valoarea corecta indiferent -de sursa (optiune sau fallback). - -### 3.3 Comportamentul cand optiunea lipseste sau e invalida - -- **Lipseste randul din `OPTIUNI`** (instalare veche, migrare neaplicata): `getoptiunefirma` intoarce - `''` -> tratat `IS NULL` in Oracle -> fallback la `'4111'` hardcodat (sectiunea 3.2, pasul 2). - **Acoperit prin constructie**, acelasi tipar ca `RF_CONT_INCASARE_*`. -- **Randul exista dar `VARVALUE` e gol** (sters manual din ecran, fara sa fi fost stearsa cheia): - identic cazului anterior — `''` tot `IS NULL` in Oracle. **Acoperit prin constructie.** -- **`VARVALUE` contine un cont care nu exista in planul de conturi**, sau un string cu lungime/ - format invalid: **nu exista nicio validare azi**, nici in `getoptiunefirma`, nici in ecranul de - optiuni (sectiunea 1.3, "nicio validare de format sau de continut"), nici la `INSERT INTO - ACT_TEMP` (coloana `SCD` e `VARCHAR2(4) NULL` fara `CHECK`, fara `FK`, confirmat in DDL-ul deja - citat in `canal_cont_venit_fara_politica.md`, sectiunea DDL). Un cont inexistent ar ajunge pe nota - neschimbat — **exact acelasi risc pe care il are deja `RF_CONT_INCASARE_*` de 15 ani**, nu unul - nou introdus de aceasta propunere. Daca se doreste o garda, ar trebui adaugata **la citire** (in - ramura noua din `contabilizeaza_articol`, dupa cele doua atribuiri de mai sus, cu un `SELECT - COUNT(*) FROM PLCONT WHERE COD_CONT = V_SCD` sau echivalent) — **nu exista azi un precedent de - validare pe niciuna dintre optiunile de tip cont examinate**, deci adaugarea uneia ar fi o - imbunatatire noua, nu o continuare a unui tipar existent; de decis explicit (sectiunea 4). -- **Lungime > 4 caractere** in `VARVALUE` (campul din tabel e `VARCHAR2(1000)`, mult mai lat decat - `ACT_TEMP.SCD VARCHAR2(4)`): ar da `ORA-12899` (value too large) la `INSERT INTO ACT_TEMP` daca - cineva scrie din greseala un text lung in ecran — eroare Oracle standard, nu un `NULL`/gol tacut; - comportament acceptabil ca gardă minimala "gratuita", fara cod suplimentar. - -### 3.4 Scriptul de migrare — schita, neaplicata - -Tiparul exact al insertiei `RF_CONT_INCASARE_BONFISCAL` (sectiunea 2a), idempotent prin verificarea -existentei prealabile (tipar comun in scripturile de migrare vazute in `SCRIPTURI_CLAR`, desi -insertia originala din 2010 nu avea garda — cea de mai jos o adauga, mai sigura pentru un script -care ar putea rula de doua ori): - -```sql --- ff__NN_COMUN_OPTIUNI.sql (schita, neaplicata) -DECLARE - V_CNT NUMBER; -BEGIN - SELECT COUNT(*) INTO V_CNT FROM OPTIUNI WHERE UPPER(TRIM(VARNAME)) = 'FACT_SCD_ARTFPRET'; - IF V_CNT = 0 THEN - INSERT INTO OPTIUNI (VARNAME, VARTYPE, PROGRAM, VARVALUE, VARDESC, PROGRAME) - VALUES ('FACT_SCD_ARTFPRET', 'CHARACTER', 'ROAFACTURARE', '4111', - 'CONTUL DEBITOR (SCD) PENTRU ARTICOL FACTURAT FARA POLITICA DE PRET', - 'ROAFACTURARE'); - END IF; -END; -/ -exec pack_migrare.UpdateVersiune('ff__NN_COMUN_OPTIUNI'); -commit; -``` - -`ID_PRG_OWNER` omis (rand `NULL` in toate exemplele citate, coloana pare neutilizata activ pentru -optiuni de firma simple). `PROGRAME='ROAFACTURARE'` — restrans, spre deosebire de -`RF_CONT_INCASARE_*` care listeaza 6 produse; de largit doar daca se confirma (in afara scopului -acestei cercetari, sectiunea "Ce ramane de decis") ca alte produse din suita ajung sa apeleze aceeasi -ramura din `contabilizeaza_articol` prin `adauga_articol_factura` simplu. - -### 3.5 Validare la salvare in ecran - -**Nu** — confirmat la sectiunea 1.3: `frm_optiuni_nou` valideaza doar "camp nevid" pe cele 3 campuri -principale, nicio validare specifica per-cheie. Optiunea noua se comporta identic cu restul tabelei: -utilizatorul poate scrie orice text de pana la 1000 caractere in `VARVALUE`, cu riscurile descrise -la sectiunea 3.3. - ---- - -## 4. Ce ramane de decis - -1. **Numele exact al cheii** — `FACT_SCD_ARTFPRET` propus (sectiunea 3.1); Marius poate prefera alt - nume sau formatul mai scurt `SCD_FARA_POLITICA`. -2. **Se adauga o validare a contului la citire** (cont trebuie sa existe in planul de conturi) sau - se pastreaza tiparul actual, fara validare, ca la `RF_CONT_INCASARE_*`? Niciun exemplu existent nu - valideaza — a introduce validare ar fi prima data cand se face asta pe o optiune de tip cont. -3. **`PROGRAME` restrans la `ROAFACTURARE` sau extins** ca la `RF_CONT_INCASARE_*` (6 produse)? Tine - de raspunsul, inca deschis in `parametru_cont_contabilizeaza_articol.md` sectiunea 2d, la - intrebarea daca alte produse din suita apeleaza `adauga_articol_factura` simplu. -4. **Textul exact din `VARDESC`** — schita de mai sus e o propunere, nu o cerinta. - ---- - -## Ce nu s-a putut stabili - -- **Cine citeste azi `EFACTURA_CONT_ART_E`/`EFACTURA_CONT_ART_P`** (sectiunea 2d) — cautarea s-a - oprit la insertia originala; modulul `ROACONT`/eFactura nu a fost deschis, in afara scopului - cerut (exemplul a servit doar ca a patra confirmare de conventie de nume, nu ca lant complet). -- **Definitia view-ului VFP local `v_optiuni`** (folosit ca `RecordSource` in `frm_optiuni`) — e un - cursor/view generat dinamic prin `gencursor()`/`goExecutor.oExecute()` din `oinit_optiuni.prg`, nu - un obiect Oracle persistent (interogare `DBMS_METADATA` pe `V_OPTIUNI` a esuat cu "object not - found", asteptat) — nu schimba nimic din propunere, doar noteaza ca numele nu desemneaza o vedere - Oracle. -- **Daca `CONT411`/`DEVCONTALTELE` (sectiunea 2c) mai au vreun apelant in alt produs din suita** - (ROACONT, ROAGEST etc.) — cautarea s-a limitat la `SCRIPTURI_CLAR` (tot codul PL/SQL istoric); nu - s-a cautat in `.vc2`/`.prg` din alte COMUN-uri de produs. diff --git a/docs/cercetare/pack_auto_actualizeaza_deviz.md b/docs/cercetare/pack_auto_actualizeaza_deviz.md deleted file mode 100644 index 0d96364..0000000 --- a/docs/cercetare/pack_auto_actualizeaza_deviz.md +++ /dev/null @@ -1,210 +0,0 @@ -# Cercetare: `pack_auto.actualizeaza_deviz` si riscul asupra devizului ROAAUTO la #13 - -Scop: `plan_13_unificare_formular_facturare.md` vrea sa permita adaugarea unei linii noi pe o -factura deja emisa, inclusiv pe facturile ROAAUTO (`tip = -12`). Intrebarea: ce se intampla cu -**devizul** din ROAAUTO cand factura primeste o linie pe care devizul nu o stie. Punctul de -plecare a fost `pack_auto.actualizeaza_deviz`, apelat din -`D:\ROA\ROAAUTO\Programe\oproceduri_devize.prg:1310`, al carei corp nu fusese citit inainte de -aceasta cercetare. - -Continua `roaauto_facturi.md` si `roaauto_articole_lista_preturi.md` (deja citite, nu reiau -constatarile lor: acelasi `PACK_FACTURARE`, `VANZARI_DETALII_TEMP` -> `VANZARI_DETALII`, -`tip=-12`, liniile sintetice `-100000..-100008` plus liniile reale "Alte servicii", editorul -`frm_modific2024` read-only, `oviz_devize.vc2:4536` blocheaza modificarea dupa `nrfact`). - -## 1. Unde e definit `PACK_AUTO` - -`D:\ROA\DATABASE\SCRIPTURI_CLAR\\\ff_..._AUTO_PACK_AUTO.sql` — 14 versiuni istorice -gasite (2013-2026). Sursa de adevar folosita (cea mai recenta, dupa conventia deja aplicata in -`roaauto_facturi.md` pentru `PACK_FACTURARE`): - -**`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\03\ff_2026_03_31_02_AUTO_PACK_AUTO.sql`** (1804 linii, -antet "17.03.2026 / robert / creare procedura setOptiuneInchidere"). - -Semnatura in spec: `:74-76`. Corp: `:692-733`. - -## 2. Ce face `actualizeaza_deviz`, pas cu pas - -Corpul complet (`ff_2026_03_31_02_AUTO_PACK_AUTO.sql:692-733`): - -```sql -procedure actualizeaza_deviz(tnProcTvav IN NUMBER, - tcSirIdOrdl IN VARCHAR2, - tnIdSet IN NUMBER) is - lcSeparator VARCHAR2(1) := ','; - lnIdFact DOCUMENTE.ID_DOC%TYPE; -begin - -- nu iau pack_contafin.get_idfact pentru ca s-ar putea sa am id_fact de la incasare, nu de la factura - SELECT MAX(ID_FACT) - INTO lnIdFact - FROM ACT - WHERE COD = pack_contafin.get_cod() - AND SCD NOT LIKE '5%'; - - UPDATE DEV_ORDL - SET PROC_TVAV = tnProcTvav - WHERE ID_ORDL IN (SELECT X FROM table(charn2collection(tcSirIdOrdl, lcSeparator))); - - UPDATE /*+ INDEX(RUL IDX_RUL_001) */ RUL - SET ID_FACT = lnIdFact - WHERE ID_SET <> 229 - AND ID_LUCRARE IN (SELECT ID_LUCRARE FROM DEV_ORDL - WHERE ID_ORDL IN (SELECT X FROM table(charn2collection(tcSirIdOrdl, lcSeparator)))); - - IF tnIdSet IN (31003, 31004, 31005, 31006, 31007, 31011) THEN - UPDATE NOM_LUCRARI - SET ID_FACT = lnIdFact - WHERE ID_LUCRARE IN (SELECT ID_LUCRARE FROM DEV_ORDL - WHERE ID_ORDL IN (SELECT X FROM table(charn2collection(tcSirIdOrdl, lcSeparator)))); - END IF; -end actualizeaza_deviz; -``` - -Trei pasi, toti indexati pe `ID_ORDL`/`ID_LUCRARE` (comanda/lucrarea din ROAAUTO), **niciunul pe -`VANZARI`/`VANZARI_DETALII`**: - -1. Citeste `lnIdFact` = cel mai mare `ID_FACT` din `ACT` pentru "codul" curent de sesiune - (`pack_contafin.get_cod()`), excluzand notele de incasare (`SCD NOT LIKE '5%'`) — comentariul - din cod explica de ce: la momentul apelului pot exista in `ACT` atat o nota de incasare cat si - nota/factura propriu-zisa, si vrea explicit factura. -2. `UPDATE DEV_ORDL SET PROC_TVAV = tnProcTvav` — scrie cota de TVA pe comenzile din - `tcSirIdOrdl` (lista CSV de `ID_ORDL`, despartita cu `charn2collection`). -3. `UPDATE RUL SET ID_FACT = lnIdFact WHERE ID_SET <> 229 AND ID_LUCRARE IN (...)` — leaga - inapoi randurile din `RUL` (jurnalul de manopera/materiale al lucrarii, folosit si la - `frm_inchidere_productie.do_executa` pentru totalul pe sectii, vezi §4) de documentul creat. -4. Doar daca `tnIdSet` e unul din `{31003,31004,31005,31006,31007,31011}`, acelasi `ID_FACT` se - scrie si pe `NOM_LUCRARI` (nomenclatorul de lucrari). - -**Nu exista niciun `SELECT`/`UPDATE` pe `VANZARI` sau `VANZARI_DETALII` in `actualizeaza_deviz`** -si, verificat suplimentar, **in tot pachetul `PACK_AUTO`** (grep pe `VANZARI` in cele 1804 linii -ale fisierului: 0 potriviri). Deci raspunsul direct la intrebarea din brief: procedura **nu -recalculeaza niciun total al devizului din liniile facturii** — nu citeste liniile facturii deloc. -Ce face e sa "stampileze" `ID_FACT` (documentul de vanzare/nota abia creat) pe randurile din `RUL` -si `NOM_LUCRARI` legate de comenzile din `tcSirIdOrdl`, plus sa actualizeze cota TVA pe -`DEV_ORDL`. E o legatura *deviz -> document*, nu un recalcul *document -> total deviz*. - -## 3. Ce se intampla cu o linie de factura pe care devizul nu o are - -Raspunsul e neted, pentru ca premisa intrebarii (un `SUM` peste `VANZARI_DETALII`) nu exista in -cod: **procedura nu vede deloc liniile facturii**, nici cele cunoscute (sintetice -`-100000..-100008`), nici cele reale (nomenclator, cazul "Alte servicii" din raportul anterior), -nici una noua adaugata din afara. Ea opereaza exclusiv pe `ID_ORDL`/`ID_LUCRARE` (identitatea -comenzii ROAAUTO), primite ca parametru `tcSirIdOrdl` — nu deriva niciodata acest identificator -din continutul `VANZARI_DETALII`. - -Precedentul "Alte servicii" (articole reale, `id_articol` pozitiv, cf. -`roaauto_articole_lista_preturi.md` §2) confirma exact acest comportament pe date deja live: acele -linii coexista cu liniile sintetice in `VANZARI_DETALII` de multa vreme, fara ca -`actualizeaza_deviz` sa faca vreo distinctie — pentru ca nu se uita la `VANZARI_DETALII` deloc. - -**Consecinta pentru #13**: adaugarea unei linii noi pe `VANZARI_DETALII` pentru o vanzare -`tip=-12` nu poate "dezechilibra" ce scrie `actualizeaza_deviz`, pentru simplul motiv ca aceasta -procedura nu depinde de continutul `VANZARI_DETALII`. Nu exista *eroare*, nu exista *ignorare -explicita* — e o absenta totala de citire. - -Important insa (nuanta pe care intrebarea din brief o presupune implicit gresit): asta **nu** -inseamna ca "devizul" (asa cum e prezentat operatorului in ROAAUTO) ar reflecta automat noua -linie. Totalul devizului afisat in `oviz_devize.vc2` e calculat client-side din `RUL` (vezi -`crsTotaluri`, construit cu un `SELECT SUM(...) FROM RUL ... GROUP BY ID_SECTIE` in -`oviz_devize.vc2:7816-7823`, in `frm_inchidere_productie.do_executa`) si din cursoarele -`crsdeviz`/`crsalteserv` calculate in `oproceduri_devize.prg` la momentul facturarii — niciodata -din `VANZARI_DETALII`. Deci o linie adaugata ulterior pe factura **nu va aparea niciodata** in -totalul devizului asa cum il calculeaza ROAAUTO, indiferent daca `actualizeaza_deviz` mai ruleaza -sau nu — pur si simplu acel total nu are o sursa care sa citeasca `VANZARI_DETALII`. - -## 4. Cand e apelata - -Cautare in ROAAUTO (`Grep` pe `actualizeaza_deviz`, cele trei aparitii de cod, plus istoric -`.??2`) si in tot `D:\ROA\DATABASE\SCRIPTURI_CLAR` (niciun alt pachet Oracle nu o apeleaza — grep -pe `actualizeaza_deviz` in tot arborele SCRIPTURI_CLAR gaseste doar fisierele care *definesc* -`PACK_AUTO`/vechiul `PACK_DEVIZE`, nu vreun apel extern): - -Trei puncte de apel, toate in VFP, niciunul intr-un trigger/job Oracle: - -1. **La emiterea facturii** — `oproceduri_devize.prg:1310`, in acelasi bloc `lcSql` cu - `pack_facturare.scrie_in_vanzari` (linia 1302), cu `tnIdSet` dinamic (`lnIdSet`, calculat mai - sus in `factureaza_deviz`). Comentariu la linia 1311: *"am scos apelul catre - `pack_devize.dev_completeaza_rul`; e inclus in `actualizeaza_deviz`"* — confirma ca functia a - inlocuit o procedura mai veche cu acelasi scop (legare RUL -> document). -2. **`frm_inchidere_productie.do_executa`** (`oviz_devize.vc2:7896`, clasa la `:7143`) — cu - `tnIdSet` **fix, 31007** — apelata dupa ce formularul de **inchidere productie** scrie o - *nota contabila* (`oscrie_in_fisiere()` peste cursorul `actactan`, `:7889`), **nu** o factura; - nu apeleaza `pack_facturare.scrie_in_vanzari`, deci nu atinge `VANZARI` deloc pe acest drum. -3. **`frm_inchidere_regie.do_executa`** (`oviz_devize.vc2:8907`, clasa la `:8093`) — identic, - `tnIdSet` fix **31006**, pentru inchiderea de **regie** (overhead), tot printr-o nota - contabila, nu o factura. - -Deci `actualizeaza_deviz` nu e specifica facturarii — e o operatie generica *"leaga -comenzile/lucrarile astea de ultimul document contabil creat pentru codul curent"*, refolosita si -la doua fluxuri de inchidere interna care nu produc facturi de vanzare. In niciunul din cele trei -cazuri nu e re-declansata mai tarziu (nu exista un al patrulea apel, nici din UI, nici din -schedule/trigger Oracle) — deci editarea ulterioara a unei facturi deja emise (obiectivul #13) nu -ar re-invoca automat aceasta procedura, pentru ca nimic din codul citit o cheama in afara celor 3 -puncte de mai sus. - -## 5. Alte proceduri din `PACK_AUTO` care citesc `VANZARI`/`VANZARI_DETALII` pentru un deviz - -**Niciuna.** Grep pe `VANZARI` in intregul fisier `ff_2026_03_31_02_AUTO_PACK_AUTO.sql` (1804 de -linii, tot pachetul, nu doar `actualizeaza_deviz`) — 0 potriviri. `PACK_AUTO` nu are nicio -dependenta de tabelele de vanzari/facturare; toata legatura cu partea financiara trece prin `ACT` -(note contabile) si `DEV_ORDL`/`RUL`/`NOM_LUCRARI` (structura proprie ROAAUTO). - -Singurul loc care **citeste** liniile facturii pentru re-listarea unui deviz e in afara -`PACK_AUTO`, pe partea VFP: `relisteaza_factura_deviz` -(`D:\ROA\ROAAUTO\Programe\oproceduri_devize.prg:1516-1576`, mentionata in raportul anterior). -Verificat acum sursa: interogheaza direct view-urile COMUN `fact_vfacturi` -(`:1531`, filtrat `WHERE cod = ...`) si **`fact_vfacturi_detalii`** (`:1564`, -`select * from fact_vfacturi_detalii where id_vanzare = ` — fara alt filtru). -View-ul e definit in -`D:\ROA\DATABASE\SCRIPTURI_CLAR\2024\06\ff_2024_06_04_01_COMUN_FACTURARE.sql:8-94`: -`create or replace view fact_vfacturi_detalii as select ... from vanzari_detalii a left join -nom_articole b ... ` — un simplu wrapper peste `VANZARI_DETALII` cu join-uri de denumire, **fara -nicio clauza `WHERE`** care sa restranga la id-uri de articol cunoscute sau la liniile -"originale" ale devizului. - -**Consecinta**: o linie noua inserata in `VANZARI_DETALII` pentru acel `id_vanzare` **va aparea -automat** la o re-listare/re-tiparire ulterioara prin `relisteaza_factura_deviz` — comportament -asteptat de la un view neconditionat, nu o stricare. Nu exista risc de eroare sau de excludere pe -acest drum; riscul (daca exista) e doar de **asteptare a utilizatorului**: factura retiparita va -arata linia noua, dar ecranul de deviz din ROAAUTO (total calculat din `RUL`, §3) nu o va arata -niciodata, pentru ca nu deriva din `VANZARI_DETALII`. - -## 6. Concluzie operationala pentru #13 - -**Da, se poate adauga o linie pe o vanzare `tip=-12` fara sa "strice" `pack_auto.actualizeaza_deviz` -sau vreo alta procedura din `PACK_AUTO`** — pentru ca niciuna din ele nu citeste `VANZARI`/ -`VANZARI_DETALII`; nu exista mecanism Oracle-side care sa recalculeze/verifice totalul devizului -din liniile facturii, deci nu exista nimic de dezechilibrat la acel nivel. Nu e nevoie de niciun -apel Oracle suplimentar din partea ROAFACTURARE ca sa "anunte" ROAAUTO despre linia noua — -`actualizeaza_deviz` n-ar face nimic util cu acea informatie oricum (opereaza pe `ID_ORDL`, nu pe -linii de factura). - -Ce **nu** rezolva aceasta concluzie, si ramane responsabilitatea planului #13, nu a acestei -proceduri: - -- **Desincronizare de afisare, nu de date**: totalul devizului aratat in ROAAUTO - (`oviz_devize.vc2`, calculat din `RUL`/cursoarele de facturare) nu va reflecta niciodata o - linie adaugata ulterior direct pe `VANZARI_DETALII` — nici azi (cu liniile "Alte servicii"), - nici cu o linie noua din editorul unificat. Daca produsul vrea ca devizul sa "stie" de linia - noua vizual, ar trebui cod nou in ROAAUTO (nu exista azi), nu un apel catre `actualizeaza_deviz`. -- **Reprintarea** (`relisteaza_factura_deviz`) va include automat linia noua (§5) — de verificat - daca asta e comportamentul dorit de produs sau o suprindere pentru operatorul ROAAUTO. -- Confirmat si direct in cod ROAFACTURARE: `Grep` pe `pack_auto` in - `D:\ROA\ROAFACTURARE` (inclusiv `COMUN`) nu gaseste nicio referinta in cod, doar in `docs\` - (planul #13 si aceste rapoarte) — azi ROAFACTURARE nu apeleaza deloc `PACK_AUTO`, confirmand ca - nu exista deja o presupunere ascunsa de sincronizare intre cele doua. - -## Neverificat - -- Ce reprezinta exact tabelele `ACT`/`RUL`/`NOM_LUCRARI`/`DEV_ORDL` la nivel de schema completa - (DDL/comentarii) — inteles doar din felul in care sunt folosite in cod, nu dintr-un dictionar de - date citit explicit. -- Daca vreun raport fiscal/SAF-T (mentionat generic in `ff_2022_03_24_01_COMUN_SAFT.sql`, nedeschis) - agrega `VANZARI_DETALII` intr-un mod care ar fi afectat de o linie noua pe `tip=-12` — in afara - ariei `PACK_AUTO` cerute, nu a fost investigat. -- Daca `frm_incasare_finala.do_recalculeaza_total`/`calculeaza_total` (`oviz_devize.vc2:6494,6700`) - ar putea fi reconciliate manual de un operator ca sa "vada" o linie adaugata ulterior — nu am - citit corpul acestor metode, doar semnalul ca exista. -- Istoricul complet al celorlalte 13 versiuni `AUTO_PACK_AUTO.sql` nu a fost comparat linie cu - linie cu cea din 2026-03 — presupun (conform conventiei deja aplicate pentru `PACK_FACTURARE`) - ca cea mai recenta e sursa de adevar pentru comportamentul curent din productie. diff --git a/docs/cercetare/parametru_cont_contabilizeaza_articol.md b/docs/cercetare/parametru_cont_contabilizeaza_articol.md deleted file mode 100644 index 80245a3..0000000 --- a/docs/cercetare/parametru_cont_contabilizeaza_articol.md +++ /dev/null @@ -1,173 +0,0 @@ -# Verificare adversariala — proiectarea parametrului de cont (`canal_cont_venit_fara_politica.md`, sectiunea "Proiectarea parametrului de cont contabil") - -Mandat: **nu re-proiecta** — proiectarea exista deja in `canal_cont_venit_fara_politica.md:13-227` -(citita integral). Aici: incercare de a o sparge + inchiderea celor 4 goluri semnalate de autor. -Sursa PL/SQL: aceeasi, `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` -(17217 linii). Oracle: doar `SELECT`, schema `MARIUSM_AUTO`/`ROA_CENTRAL`. VFP: doar `vfp_symbols.ps1` -(index, read-only). **Niciun fisier de cod atins.** - -## Verdict, in patru randuri - -**Proiectarea rezista la verificarea adversariala** — nu am gasit nicio eroare care sa-i invalideze -concluzia centrala. Am gasit **un rand din tabel mai slab decat descris** (`CU_TVA=1` hardcodat NU e -complet inofensiv — are un efect colateral masurabil, minor, prin `nproc_tva_max`), **un rand mai -tare decat descris** (`IN_VALUTA` din `nin_valuta` e o sursa solida, nu doar plauzibila — parametru -obligatoriu, fara `DEFAULT`), **o excludere confirmata corecta prin structura schemei** (articolul -"compus" nu poate exista pe o linie fara politica — nu o gaura), si **un gol raportat de autor acum -inchis cu fapt** (cele doua clase de la `:14069`/`:18089` sunt distincte, nu o duplicare a aceleiasi -metode — 2 locuri VFP de atins, nu 1). Pe decizia 35 (regenerare = acelasi cod): **DA, calea Oracle -e identica**, dar exista un obstacol real, deja proiectat separat (`idfact_refolosire_si_documente.md`), -**in alt pachet** (`PACK_CONTAFIN`), nu in `pack_facturare` — nu invalideaza design-ul curent, dar -regenerarea nu e completa fara acea a doua bucata de lucru. - ---- - -## 1. Decizia 35 — regenerarea foloseste identic `pack_facturare`? - -**DA pe calea Oracle relevanta pentru parametrul de cont, cu o exceptie deja cunoscuta si separata.** - -Verificat direct: `scrie_factura2` reseteaza starea de sesiune la fiecare apel prin -`initializeaza_date_factura` (`PACK_FACTURARE:1808-1846`), care face -`DELETE FROM VANZARI_DETALII_TEMP;` (`:1835`) si `pack_facturare.nid_act := 0;` (`:1836`) — -**contorul folosit de `scrie_nota`/`scrie_discount`/`scrie_tva` la fiecare `INSERT INTO ACT_TEMP` -porneste curat la fiecare emitere, inclusiv la o a doua** (regenerare). Nimic in -`contabilizeaza_articol`, ramura noua inclusa, citeste vreo stare care ar presupune "documentul e -nou" — toate variabilele folosite (`nid_venchelt`, `nid_sectie_stoc`, `nin_valuta`, `nid_set`, -`nid_util`) sunt **citite**, nu verificate contra unei stari anterioare. - -**Obstacolul real e in `PACK_CONTAFIN`, nu in `pack_facturare`**: reemiterea cu acelasi `ID_FACT` (ca -sa nu se dubleze documentul contabil la regenerare) ar da azi `ORA-00001` pe `PK_DOCUMENTE`, pentru ca -`SET_IDFACT` ia mereu `SEQ_IdFact.NEXTVAL` si documentul vechi ramane in tabel doar soft-sters -(`STERS=1`), nu disparut — analiza completa, cu solutia (variabila de pachet noua in `PACK_CONTAFIN`, -`MERGE ... WHEN MATCHED` pe `DOCUMENTE`), e deja facuta integral in -`idfact_refolosire_si_documente.md` (sectiunile A-C). **E exact "ceva care ar cere cod separat" — dar -separat inseamna alt pachet/alta procedura (`SET_IDFACT`, scrierea in `DOCUMENTE`), nu o ramura in -plus in `contabilizeaza_articol` sau `adauga_articol_factura`.** Ramane o bucata de lucru distincta, -necesara pentru ca regenerarea sa fie completa, dar nu intersecteaza si nu invalideaza design-ul -parametrului de cont. - -**Un gol neadresat de design, gasit acum**: ramura `pack_facturare.ntip = 4` ("facturare din avize", -`:7520-7537`) apeleaza `scrie_fact_aviz_custodie` in loc de `descarca_gestiune`+discount — design-ul -nu spune ce se intampla pe aceasta ramura in cazul fallback. In practica, **probabil nu conteaza**: -"facturare din aviz" cere azi ca linia sursa sa aiba deja `id_pol` (`adauga_articol_factura:5096`, -`AND A.ID_POL = V_ID_POL` pe `VANZARI_DETALII` a avizului sursa) — un aviz fara politica n-ar fi -ajuns niciodata pana la factura pe acest drum, deci scenariul "fallback pe `ntip=4`" e putin probabil -sa apara. **Dar design-ul nu spune asta explicit** — de adaugat o linie care sa acopere/exclude -explicit acest caz inainte de implementare, nu de presupus tacit. - -## 2. Cele 4 goluri semnalate de autor - -### 2a. `ofacturare.vc2:14069`/`:18089` — doua metode sau o duplicare? - -`vfp_symbols.ps1 -Where 'ofacturare.vc2:14069'` -> **`frm_facturare_articole.do_scrie_articole`** -(`ofacturare.vc2:13967-14195`). `-Where 'ofacturare.vc2:18089'` -> **`frm_facturare_articole2.do_scrie_articole`** -(`ofacturare.vc2:18003-18221`). **Confirmat: doua clase distincte, `frm_facturare_articole` si -`frm_facturare_articole2`, fiecare cu propria metoda `do_scrie_articole`** (acelasi nume de metoda, -nu aceeasi clasa) — nu o duplicare literala a unui singur cod. **Consecinta pentru implementare**: -parametrul nou trebuie cablat **in doua locuri VFP**, nu unul — ambele clase construiesc separat -apelul RPC catre `adauga_articol_factura`. Nu schimba verdictul de fezabilitate, dar schimba -suprafata de lucru VFP fata de impresia "un singur loc de atins". - -### 2b. `scrie_tva` la cota 0% cu `CU_TVA` hardcodat `1` — efect real, nu inofensiv - -Verificat corpul `scrie_nota` (`:12537-12558`): actualizarea `nproc_tva_max`/`nid_jtva_coloana`/ -`nTaxCode` **nu e neconditionata** — e in interiorul `IF V_CU_TVA = 1 THEN`, deci exact conditionata -de flagul pe care design-ul propune sa-l hardcodeze. In interior insa, comparatia -`IF pack_facturare.nproc_tva_max < V_PTVA THEN` **nu se uita la suma**, doar la rata — deci daca -articolul fallback are `proc_tvav` real 0% (scutit) si `CU_TVA` e fortat la `1`, rata 0% **intra in -comparatia de maxim** si, daca e prima/singura linie a documentului (`nproc_tva_max` initial `-1`, -`:1885`), **castiga** — seteaza `nid_jtva_coloana`/`nTaxCode` la valorile acelei linii scutite. -Aceste doua variabile sunt consumate mai departe **in aceeasi procedura `scrie_factura2`**, la -discountul global pe factura (`:6164-6184`, `V_DISCOUNT_FACTURA <> 0`): rata/coloana/taxcode ale -liniei scutite ar ajunge sa descrie linia de discount a **intregii facturi**, chiar daca alte linii -au TVA real. **Verdict: `CU_TVA=1` hardcodat nu e "inofensiv" in toate cazurile cum spune design-ul — -are un efect colateral real, dar restrans la o combinatie specifica (linie fallback cu TVA 0% + -discount global pe factura + acea linie e cea cu rata "maxima" vazuta pana atunci).** Nu invalideaza -alegerea `CU_TVA=1` ca implicit (majoritatea liniilor reale au TVA nenul), dar intareste, nu slabeste, -cererea deja facuta de autor ("de confirmat cu Marius") — motivul de confirmat e mai concret decat -"linie de TVA cu suma 0, inofensiv". - -### 2c. `INSERT INTO VANZARI_DETALII ... FROM VANZARI_DETALII_TEMP` — linie exacta, confirmata - -`PACK_FACTURARE:13705-13757`, in `scrie_in_vanzari` (spec `:13488`, apelata din `finalizeaza_factura` -`:14787`, care e apelata la finalul fluxului de verificare — nu din `scrie_factura2`, alta faza a -aceluiasi pipeline). **Confirmat: lista de coloane e explicita**, 24 coloane -(`ID_VANZARE, ID_ARTICOL, LOT, SERIE, ID_RATA, ID_POL, CANTITATE, PRET_ACHIZITIE, PRETD, ID_VALUTAD, -PRET, PROC_TVAV, ID_JTVA_COLOANA, ID_JTVA_COLOANA_EX, DISCOUNT_UNITAR, ID_GESTIUNE, ID_VALUTA, CONT, -EXPLICATIE, PRET_CU_TVA, DIFERENTA, CUSTODIE, ID_VANZARE_SET, ID_CTR, TAXCODE`) — **nu** `SELECT *`. -Exact ce presupunea design-ul (sectiunea 2, citand `nota_contabila_fara_politica.md` fara -re-verificare): daca se doreste ca `CONT_VENIT` sa ajunga si in `VANZARI_DETALII` (trasabilitate), -**aceasta lista trebuie extinsa explicit** — fara acest pas, coloana noua ramane doar pe -`VANZARI_DETALII_TEMP` si `ACT_TEMP`, invizibila in `VANZARI_DETALII` dupa fapt. Confirmarea nu -schimba verdictul design-ului (deja marcase pasul ca "recomandat, nu strict necesar"), doar il -transforma din presupunere in fapt verificat. - -### 2d. Apelanti `adauga_articol_factura` din restul suitei - -Sarit, cum a cerut team-lead-ul — alt agent lucreaza pe suprafata de regresie. - ---- - -## 3. Verificare adversariala pe tabelul din design (sectiunea 3 a raportului sursa) - -| Rand din tabel | Incercare de infirmare | Rezultat | -|---|---|---| -| `IN_VALUTA` <- `pack_facturare.nin_valuta` | E setat pe toate fluxurile relevante, sau ramane nul si azi nu conteaza? | **Mai solid decat descris.** `nin_valuta := V_IN_VALUTA` (`:1902`) e in `initializeaza_date_factura`, apelata **o data la inceputul fiecarei emiteri**, cu `V_IN_VALUTA IN NUMBER` **fara `DEFAULT`** in semnatura (`:1827`) — VFP e obligat sa trimita o valoare, nu poate omite parametrul. Nu e un fallback de sesiune care "poate ramane nesetat" (ca `nid_venchelt`), e un flag de document obligatoriu, mereu curent. | -| `ID_VENCHELT`/`ID_SECTIE` <- variabile de sesiune | Cine le seteaza si cand? | **Confirmat, cu nuanta.** `nid_venchelt := V_ID_VENCHELT` si `nid_sectie_stoc := V_ID_SECTIE` (`:1876-1877`), tot in `initializeaza_date_factura`, din parametri **fara `DEFAULT`** insa **VFP poate trimite `NULL`** pe ei (sunt opționale ca *valoare*, nu ca prezenta in apel) — deci pot ramane `NULL` pe tot documentul. **Nu e o regresie**: pe ramura veche, `NVL(nid_venchelt, NVL(A.ID_VENCHELT, C.ID_VENCHELT))` ar da tot `NULL` daca nici politica nu are aceste campuri — acelasi rezultat posibil, aceeasi cauza (sesiune nesetata), nu unul nou introdus de fallback. | -| Garda `descarca_gestiune` copiata identic | Acopera `id_gestiune=-1000` si `in_stoc`? | **Da, fara diferenta.** Garda foloseste `detalii_articol.id_gestiune`/`.in_stoc` — campuri populate identic de `adauga_articol_factura` indiferent de ramura (nu depind de politica azi, nici in design). Copierea literala a conditiei (`:7473-7475`) e corecta prin constructie. | -| `ASCD`/`ASCC` <- `GetAnaliticByGrupUtilizatori` | Ce intoarce pe `NO_DATA_FOUND`, e acceptabil? | **Confirmat `NULL`** (corp citit `:16704-16723`: `lcAcont` declarat fara valoare implicita, `EXCEPTION WHEN NO_DATA_FOUND THEN NULL;`, `RETURN lcAcont`). Acceptabil — e exact fallback-ul deja folosit azi, necondiționat, pe ramurile de aviz (`:7416-7417,7421-7422`) si `ACT_TEMP.ASCD/ASCC` sunt nullable (confirmat DDL, `canal_cont_venit_fara_politica.md` DDL section). Nu e un risc nou. | -| Articol "compus" (`V_COMPUS=1`) lasat in afara scopului | Poate un articol ales ad-hoc din nomenclator fi compus? | **NU — exclus prin structura schemei, nu prin presupunere.** Interogat direct `ALL_VIEWS.VCRM_POLITICI_PRET_ART`: coloana `COMPUS` citita de `contabilizeaza_articol` (`SELECT COMPUS, ID_POL_ART ... FROM VCRM_POLITICI_PRET_ART`, `:7279-7283`) e definita in view ca `CASE WHEN PA.ID_POL_ART IN (SELECT DISTINCT ID_PACHET FROM CRM_PACHETE_ARTICOLE WHERE STERS=0) THEN 1 ELSE 0 END` — o proprietate a **perechii (articol, politica)**, identificata prin `ID_POL_ART` (cheia surogat a randului din `CRM_POLITICI_PRET_ART`). O linie fara politica **nu are** un `ID_POL_ART` — deci intrebarea "e articolul compus" nu se poate pune structural pe aceasta ramura. (`NOM_ARTICOLE.COMPUS` exista ca alta coloana, aliasata `art_compus` in acelasi view, dar **nu e citita de `contabilizeaza_articol`** — irelevanta aici.) Excluderea din design e corecta, nu o gaura. | -| `RETURN V_INCASAT_CALCUL` | Se calculeaza corect pe ramura noua sau intoarce 0? | **Corect, prin constructie.** Design-ul specifica explicit aceeasi acumulare ca azi (`V_INCASAT_CALCUL := V_INCASAT_CALCUL + scrie_nota(...)`, apoi `- scrie_discount(...)`), cu `V_INCASAT_CALCUL` initializat `0` la declaratie (`:7178`), inainte de `IF`-ul care selecteaza ramura — identic cu azi. Niciun risc de `RETURN 0` gasit. | - ---- - -## 4. Hardcodarile `SCD='4111'` / `CU_TVA=1` — exista o sursa mai buna? - -Cautare directa in tot `PACK_FACTURARE` pentru orice sursa alternativa care nu trece prin -`NOTE_CONTABILE`: config de firma (`getoptiunefirma('CONT...')`), cont implicit pe partener/client -(`PARTENERI.CONT*`), flag de scutire TVA pe articol/client (`SCUTIT`, `EXCEPTAT`) — **zero rezultate -pentru toate cele trei cautari**. Singurul camp inrudit gasit, `ACT_TEMP.NEIMPOZAB`, e folosit in alt -scop (raportare sume neimpozabile), nu ca sursa pentru `CU_TVA`. **Concluzie: nu exista o sursa mai -buna in cod — hardcodarea (sau parametrul explicit trimis de VFP) ramane singura optiune.** Asta -intareste recomandarea deja facuta de autor: **de decis explicit cu Marius, nu de dedus din date**, -mai ales pentru `CU_TVA` dupa gasirea de la punctul 2b (efectul via `nproc_tva_max` nu mai e -"pur cosmetic"). - ---- - -## 5. Completare: `goExecutor.oExecuta` si cursorul de verificare — fals pozitiv, nu bug - -Verdict: **fals pozitiv, confirmat cu argumente, nu doar presupus.** `oExecuta` (funcție-wrapper, -`COMUN\programe\oproceduri_comune.prg:121-159`) deleagă la `oExecute` (`:173-504`), care la rândul ei -face `SQLExec(lnHandle, lcSql, lcCursor)` (`:330`) — **`lcSql` e folosit exact cum a fost construit de -apelant, fără nicio rescriere care să adauge un bind lipsă.** Textul construit în -`COMUN\clase\ofacturare.vc2:14345-14359` (și identic la `:14373-14387`, `:18343-18371`) e sintaxă -ODBC escape `{call pack_facturare.scrie_factura2(...)}`, cu **16** argumente poziționale (15 `IN` + -`?@poDate.nid_vanzare` pentru `V_ID_VANZARE OUT NUMBER`, al 16-lea parametru declarat), paranteza se -închide imediat după — **al 17-lea parametru, `V_CURSOR_VERIFICARE OUT pack_facturare.cursor_facturare` -(spec `PACK_FACTURARE:656`), nu are niciun placeholder în text, nici legat, nici nelegat.** Asta e -**exact tiparul cunoscut al driverului ODBC Oracle pe care VFP îl folosește prin `SQLExec`**: cand -ultimul parametru declarat al unei proceduri e un `REF CURSOR OUT`, driverul îl detectează din -catalogul Oracle (nu din textul apelului) și întoarce automat rândurile lui ca *result set* al -apelului — **al treilea argument al `SQLExec`/`oExecute` (numele de cursor VFP, aici `lcCursorVerificare`) -e exact mecanismul de captare a acelui result set**, fără sa fie nevoie de bind explicit. Codul imediat -după apel (`ofacturare.vc2:14394-14397`, `llReturn = goExecutor.oExecuta(lcSql,lcCursorVerificare)` -urmat de `If Reccount(lcCursorVerificare)>0`) tratează cursorul ca fiind deja populat cu rânduri — -comportament incompatibil cu un apel eșuat sau cu un parametru nelegat (care ar da eroare Oracle, -nu un cursor gol interpretabil). **Nu pot confirma mecanismul din interiorul driverului însuși** (e -extern codebase-ului, verificabil doar prin comportament) — dar dovada indirectă e puternică: același -tipar apare identic în toate cele 4 locuri de apel găsite, neschimbat de-a lungul mai multor versiuni -(`v 2.0.13` -> `v 2.0.93`, comentarii de istoric vizibile în cod), iar ecranul de verificare (funcție -activă, folosită la fiecare emitere) depinde de acest cursor populat — dacă apelul ar eșua silențios, -ecranul de verificare n-ar arăta niciodată note propuse, ceea ce ar fi fost observat imediat. **Concluzie -pentru plan: anomalia nu există — nu trebuie tratată ca risc pe cele 7 produse.** - -## Ce ramane neverificat - -- Comportamentul exact al ramurii `ntip=4`/`scrie_fact_aviz_custodie` in scenariul fallback (punctul - 1) — argumentat ca improbabil, nu testat/exclus explicit in design. -- Daca vreun raport Oracle activ presupune `ACT_TEMP.ID_VENCHELT`/`ID_SECTIE` populate pe liniile de - venit (relevant doar daca sesiunea nu seteaza `nid_venchelt`/`nid_sectie_stoc`) — in afara scopului - acestei verificari (cod PL/SQL only). -- Suprafata de regresie la nivelul apelantilor `adauga_articol_factura` din restul suitei — explicit - lasata altui agent. diff --git a/docs/cercetare/rec_cale_vanzari_detalii.md b/docs/cercetare/rec_cale_vanzari_detalii.md deleted file mode 100644 index 9134158..0000000 --- a/docs/cercetare/rec_cale_vanzari_detalii.md +++ /dev/null @@ -1,272 +0,0 @@ -# Cercetare: calea VFP->Oracle la emitere si calea propusa pentru editare (#6, punctul E.4) - -Sursa: interogari SELECT proaspete pe `MARIUSM_AUTO@ROA_CENTRAL` (08.08.2026), acelasi mediu si -aceeasi versiune de baza ca in `rec_s5_oracle_vanzari.md` (nu s-a schimbat nimic intre cele doua -cercetari din aceeasi zi). Numerele de linie PL/SQL de mai jos sunt dintr-un export propriu, facut -in aceasta sesiune, din `all_source` pentru `PACK_FACTURARE` (schema/pachet identic cu cel din -raportul anterior). Partea VFP e din cache-ul text `.vc2` din proiect (nu binarul). - -Aceasta cercetare inchide golul lasat de `rec_s5_oracle_vanzari.md`, sectiunea E.4: cum ajung -liniile facturii in Oracle la emitere si care e calea corecta pentru editare. - -## 1. Calea de azi, capat la capat - -### 1.1 VFP: cine populeaza `VANZARI_DETALII_TEMP` - -Raspuns: **Oracle**, prin apeluri per-linie facute din VFP, nu VFP direct prin INSERT. - -- `frm_facturare_articole.do_scrie_articole` (`COMUN\clase\ofacturare.vc2:13967-14195`): - - `:13981-13999` cheama `pack_facturare.initializeaza_date_factura(...)` (antetul documentului, - tine minte parametrii in stare de sesiune pe pachet: `pack_facturare.ntip`, `nid_sucursala`, - `nid_util` etc. — folosite mai jos de `adauga_articol_factura`). - - `:14036-14115`: `SELECT crsfactura` -> `SCAN`, cate un `Scatter Name poArt MEMO` per linie, apoi - pentru fiecare linie fara rata de contract (`ELSE` la `:14051`) construieste text PL/SQL literal - (nu `{call}` cu parametri legati) si apeleaza - `pack_facturare.adauga_articol_factura(id_temp, id_articol, serie, explicatie, id_pol, - id_gestiune, pret_achizitie, pretd, id_valutad, pret, id_valuta, cu_tva, gestionabil, - cantitate, discount, cont, curs, multiplicator, id_jtva_coloana, id_part_rez, id_lucrare_rez, - pretv_orig, id_set_fact, id_ctr, gnIdUtil, taxcode, lot)` (`:14069-14091`), executat imediat - prin `goExecutor.oExecute(lcSql)` (`:14105`) — **un apel Oracle per linie de factura**, in - bucla, nu un singur apel cu tot cursorul. - - Pentru liniile din seturi: `pack_facturare.initializeaza_seturi_temp` + un apel - `pack_facturare.adauga_articol_set(...)` per linie de set (`:14117-14154`), scrie in - `VANZARI_SETURI_TEMP`. -- `frm_facturare_articole.do_scrie_factura` (`:14197-14554`): dupa ce toate liniile au fost urcate - in TEMP prin apelurile de mai sus, apeleaza finalizarea documentului — - `pack_facturare.scrie_factura2(...)` (facturi, `:14345`/`:14373`) sau - `pack_facturare.scrie_proforma(...)` (`:14286`) sau `scrie_factura_avize(...)`/ - `scrie_factura_avize_retur(...)` dupa tipul documentului — **un singur apel**, fara sa mai - transmita liniile (ele sunt deja in `VANZARI_DETALII_TEMP`, populata de bucla anterioara, in - ACEEASI sesiune/tranzactie Oracle). - -### 1.2 Oracle: `adauga_articol_factura` — nu doar INSERT, RECALCULEAZA din sursa originala - -Verificat sursa completa a procedurii publice `pack_facturare.adauga_articol_factura` (semnatura cu -`V_ID_TEMP` ca prim parametru, cea apelata de VFP la `:14069`) — **nu** e un simplu INSERT cu -valorile primite de la VFP. Ramifica pe `pack_facturare.ntip` (starea de sesiune setata la -`initializeaza_date_factura`) si **re-deriva** `V_PRET`, `V_PROC_TVAV`, `V_ID_VALUTA`, -`V_PRETURI_CU_TVA`, `V_IN_STOC` direct din documentul-sursa: -- `ntip IN (3,21,28,42,47)` (facturare din comenzi): `SELECT ... FROM COMENZI_ELEMENTE ...` -- `ntip = 4` (facturare din avize): `SELECT ... FROM VANZARI_DETALII ...` (avizul deja scris) -- `ntip = 45` (restaurant): calcul din `JTVA_COLOANE` + `CRM_POLITICI_PRET_ART` -- `V_OPT_FACTURARE = 3` (contract): `SELECT ... FROM CTR_ARTICOLE ...` -- fallback (`ELSE`): doar cota TVA din `JTVA_COLOANE`, restul (`V_PRET`, `V_ID_VALUTA`, - `V_PRETURI_CU_TVA`, `V_IN_STOC`) preluate ca atare din parametrii transmisi de VFP. - -Abia dupa acest bloc `CASE` urmeaza INSERT-ul propriu-zis: - -```sql -INSERT INTO VANZARI_DETALII_TEMP - (ID_TEMP, ID_ARTICOL, SERIE, LOT, EXPLICATIA, ID_POL, PRET_ACHIZITIE, PRETD, ID_VALUTAD, - PRET, PRET_CU_TVA, PROC_TVAV, CANTITATE, DISCOUNT_UNITAR, ID_GESTIUNE, CONT, ID_VALUTA, - CURS, MULTIPLICATOR, ID_JTVA_COLOANA, IN_STOC, ID_VANZARE_SET, ID_PART_REZ, ID_LUCRARE_REZ, - PRETV_ORIG, CUSTODIE, ID_CTR, ID_UTIL, TAXCODE) -VALUES (...) -``` - -(am confirmat integral corpul, INSERT-ul e ultimul bloc din procedura, imediat dupa `END CASE`). - -**Consecinta directa pentru #6**: `adauga_articol_factura` NU poate fi reutilizata ca atare pentru -editare — depinde de starea de sesiune `pack_facturare.ntip`/`clistaid`/`id_ctr` care descrie -DOCUMENTUL SURSA de la emitere (comanda/aviz/contract), stare care nu exista si nu are sens la o -editare ulterioara a facturii deja emise. Orice procedura noua pentru editare trebuie sa scrie -direct in `VANZARI_DETALII` (tabela reala), nu prin acest drum. - -Exista si `sterge_articol_factura(V_ID_TEMP, V_ID_UTIL)` — `DELETE FROM VANZARI_DETALII_TEMP WHERE -ID_TEMP = V_ID_TEMP` — folosita doar cat timp factura e in curs de compunere (inainte de emitere), -nu dupa. - -### 1.3 Oracle: `scrie_factura2` -> `finalizeaza_factura` -> `scrie_in_vanzari` — trecerea TEMP -> real - -`scrie_factura2` (PACK_FACTURARE, corpul activ, nu varianta comentata din cod): -- `:70` `pack_contafin.sterge_temp_actrul()`, seteaza `pack_facturare.ntotftva/ntottva` din - parametri (valori calculate in VFP). -- `:84` `SELECT * BULK COLLECT INTO tab_detalii FROM VANZARI_DETALII_TEMP` — citeste TOATA tabela - temp intr-un array PL/SQL, fara filtru pe sesiune (GTT-ul e oricum izolat per sesiune/tranzactie). -- Bucla pe `tab_detalii`, ramificata tot pe `ntip` (transfer intre subunitati, restaurant etc.) — - pentru cazul standard de factura apeleaza mai departe spre `pack_facturare.finalizeaza_factura`. - -`finalizeaza_factura` (nu comentata): -```sql -pack_facturare.initializeaza_scriere_actrul(V_DATAORA); -pack_facturare.scrie_in_vanzari(V_DISCOUNT_FACTURA, V_ID_DELEGAT, V_ID_MASINA, V_ID_FACTURARE, - V_LISTARE_DETALIATA, V_DATAORA_EXP, V_ID_AGENT, V_TEXT_ADITIONAL, pack_facturare.nid_vanzare); -pack_facturare.finalizeaza_scriere_actrul(); --- update vanzari set id_fact = ... (completare id_fact dupa scrierea in contabilitate) -``` - -`scrie_in_vanzari` — gasit exact mecanismul care lipsea din raportul anterior: -- `INSERT INTO VANZARI (...) VALUES (...) RETURNING ID_VANZARE INTO pack_facturare.nid_vanzare` - (randul parinte, un rand per document din bucla pe `tab_vanz`, tabela interna cu documentele de - scris — cazul normal e un singur document). -- `pack_facturare.scrie_cursuri(pack_facturare.nid_vanzare)`. -- **Trecerea efectiva TEMP -> real**: - ```sql - INSERT /*+ APPEND */ INTO VANZARI_DETALII - (ID_VANZARE, ID_ARTICOL, SERIE, ID_POL, CANTITATE, PRET_ACHIZITIE, PRETD, ID_VALUTAD, - PRET, PROC_TVAV, DISCOUNT_UNITAR, ID_GESTIUNE, ID_VALUTA, CONT, PRET_CU_TVA, ID_JTVA_COLOANA) - SELECT pack_facturare.nid_vanzare, ID_ARTICOL, SERIE, ID_POL, CANTITATE, PRET_ACHIZITIE, PRETD, - ID_VALUTAD, PRET, PROC_TVAV, DISCOUNT_UNITAR, ID_GESTIUNE, ID_VALUTA, CONT, PRET_CU_TVA, - ID_JTVA_COLOANA - FROM VANZARI_DETALII_TEMP - WHERE ID_COMANDA = tab_vanz(i).id_comanda AND NUMAR_ACT = tab_vanz(i).numar_act - ORDER BY ID_TEMP; - ``` - Cheia de potrivire intre randul nou din `VANZARI` si liniile lui din TEMP e - `(ID_COMANDA, NUMAR_ACT)` — nu `ID_TEMP` direct — pentru ca un singur apel poate scrie mai multe - documente `VANZARI` deodata (facturare pe mai multe comenzi/numere de act simultan); fiecare - document isi ia doar liniile care se potrivesc. -- Coloanele COPIATE in `VANZARI_DETALII` sunt un subset din `VANZARI_DETALII_TEMP` (16 coloane) — - `ID_TEMP`, `ID_CTR`, `ID_UTIL`, `TAXCODE`, `LOT`, `ID_VANZARE_SET`, `ID_PART_REZ`, - `ID_LUCRARE_REZ`, `PRETV_ORIG`, `CUSTODIE`, `NUMAR_ACT`, `ID_COMANDA`, `ID_GESTIUNE_DEST` - raman DOAR in TEMP, nu se copiaza in tabela reala (`VANZARI_DETALII` nu are coloanele `ID_TEMP`, - `NUMAR_ACT`, `ID_COMANDA` etc. — confirmat din `all_tab_columns`, vezi 2.2). -- Urmeaza blocul de agregare (`SELECT INTO` din `VANZARI_DETALII_TEMP`, deja documentat in - `rec_s5_oracle_vanzari.md` A.3-A.4) si `UPDATE VANZARI SET total_fara_tva=..., ...` cu totalurile - denormalizate. - -**PK-ul `VANZARI_DETALII.ID_VANZARE_DET`** se genereaza automat: trigger `TRG_VANZARI_DET_BEFOINS` -(`BEFORE INSERT ... FOR EACH ROW`, verificat prin `dbms_metadata.get_ddl`) face exclusiv -`SELECT SEQ_VANZARI_DETALII.NEXTVAL INTO :NEW.ID_VANZARE_DET FROM DUAL` — nu seteaza alte coloane. -Deci **orice INSERT nou in `VANZARI_DETALII`** (inclusiv unul scris pentru #6, la adaugare de linie -noua la editare) primeste automat PK-ul corect fara sa fie nevoie de secventa apelata manual din -codul de editare. - -## 2. Natura tabelei temp si structura - -### 2.1 `VANZARI_DETALII_TEMP` — Global Temporary Table, `ON COMMIT DELETE ROWS` - -Confirmat din `all_tables`: - -| TABLE_NAME | TEMPORARY | DURATION | -|---|---|---| -| VANZARI | N | — | -| VANZARI_DETALII | N | — | -| VANZARI_DETALII_TEMP | **Y** | **SYS$TRANSACTION** | -| VANZARI_SETURI_TEMP | **Y** | **SYS$TRANSACTION** | - -`DURATION = SYS$TRANSACTION` inseamna GTT cu `ON COMMIT DELETE ROWS` — randurile dispar la commit -(sau rollback), nu doar la sfarsit de sesiune. **Consecinta directa**: orice solutie care ar -reutiliza `VANZARI_DETALII_TEMP` pentru editare (#6) trebuie sa umple tabela SI sa consume -rezultatul in ACEEASI tranzactie — nu poate fi umpluta intr-un pas si citita in altul, ca in fluxul -de emitere unde umplere (bucla `adauga_articol_factura`) si consum (`scrie_factura2`) se intampla -deja in aceeasi conexiune/tranzactie, inainte de commit. - -### 2.2 Coloane: `VANZARI_DETALII` vs `VANZARI_DETALII_TEMP` - -`VANZARI_DETALII` (35 coloane, din `all_tab_columns`) — cheie primara `ID_VANZARE_DET` (NOT NULL, -generata din secventa prin trigger), FK logic `ID_VANZARE` (NOT NULL). Coloane relevante pentru -editare: `PRET` (NOT NULL), `CANTITATE`, `PRET_CU_TVA`, `DISCOUNT_UNITAR`, `PROC_TVAV`, `STERS` -(NOT NULL), `VALIDAT`/`DATAORA_VALID`/`ID_UTIL_VALID`, `ID_UTILS`/`DATAORAS` (audit), -`DIFERENTA` (NOT NULL), `CUSTODIE`/`DESCARCAT` (NOT NULL) — ultimele patru nu au valoare implicita -vizibila in trigger, deci probabil `DEFAULT` la nivel de coloana (nu s-a verificat separat, nu era -in scope). - -`VANZARI_DETALII_TEMP` (28 coloane) — **fara PK real** (`ID_TEMP` e generat in VFP, folosit doar ca -identificator temporar de linie in cadrul sesiunii curente de compunere a facturii), **fara** -`ID_VANZARE`/`ID_VANZARE_DET`/`STERS`/`VALIDAT` (nu se aplica inainte de a exista documentul -parinte). Are in schimb coloane specifice etapei de compunere, care NU exista in tabela reala: -`ID_TEMP`, `NUMAR_ACT`, `ID_COMANDA`, `ID_GESTIUNE_DEST`, `ID_UTIL` (vs `ID_UTILS` in cea reala). - -Coloanele comune folosite de editare (S4): `ID_ARTICOL`, `PRET`, `CANTITATE`, `PRET_CU_TVA`, -`DISCOUNT_UNITAR`, `PROC_TVAV`, `ID_GESTIUNE`, `CONT`, `ID_VALUTA`, `CURS` (doar in TEMP; in -`VANZARI_DETALII` nu exista `CURS` per linie — cursul e doar pe document, in `VANZARI_CURSURI`/ -`VANZARI.CURS`), `ID_JTVA_COLOANA`, `SERIE`, `EXPLICATIE`/`EXPLICATIA` (nume diferit!), `TAXCODE`, -`LOT`. - -## 3. Stergerea si adaugarea de linii - -### 3.1 Stergere — mecanism EXISTENT azi doar la nivel de document intreg, NU per linie - -Cautat explicit orice `UPDATE VANZARI_DETALII SET STERS` in `PACK_FACTURARE` — gasite doar doua -locuri, ambele sterg TOATE liniile unui document, niciodata o singura linie: - -- `sterge_factura(V_ID_VANZARE, ...)`: - `UPDATE VANZARI_DETALII SET STERS = V_STERS, ID_UTILS = V_ID_UTIL, DATAORAS = V_DATAORA WHERE - ID_VANZARE = V_ID_VANZARE AND STERS = V_NESTERS` (V_STERS variaza 0/1 dupa tipul documentului, - cazul normal fiind 1). -- `sterge_proforma(V_ID_VANZARE, ...)`: acelasi tipar, exclusiv pe proforme. - -**Nu exista azi un mecanism Oracle sau VFP de stergere per-linie in `VANZARI_DETALII`** — orice -"stergere de linie la editare" ceruta de S4/S4b e functionalitate NOUA, de adaugat la #6, nu o -reutilizare a ceva existent. - -Exista insa un precedent apropiat de UPDATE punctual per linie, util ca sablon: -`modifica_explicatie_articol(V_ID_VANZARE_DET, V_EXPLICATIE, V_ID_UTIL, V_TAXCODE)` — -`UPDATE VANZARI_DETALII SET EXPLICATIE = V_EXPLICATIE, TAXCODE = V_TAXCODE WHERE ID_VANZARE_DET = -V_ID_VANZARE_DET` — exact modelul de "UPDATE tintit prin `goExecutor`" mentionat ca varianta in -planul S5/S6, deja folosit azi din `frm_modifica_articol_factura` (mostenire de la #7, vezi -`plan_06_editare_factura.md`). - -### 3.2 Adaugare de linie noua — nu cere nimic special in plus fata de un INSERT direct - -`ID_VANZARE_DET` vine automat din `SEQ_VANZARI_DETALII` prin trigger la orice `INSERT INTO -VANZARI_DETALII`, indiferent de cine face insert-ul (nu doar fluxul de emitere) — vezi 1.3. Deci -adaugarea unei linii noi la editare **nu** cere obtinerea manuala a unui ID din secventa; e suficient -un `INSERT INTO VANZARI_DETALII (ID_VANZARE, ID_ARTICOL, PRET, CANTITATE, PRET_CU_TVA, ...) VALUES -(...)` (fara `ID_VANZARE_DET` in lista de coloane) intr-o procedura noua, apelata din -`finalizeaza_modificare_nota` alaturi de recalculul de totaluri propus in `rec_s5_oracle_vanzari.md` -punctul B. - -## 4. Propunerea pentru S4/S5/S6 — cum trimite editarea liniile modificate in Oracle - -### Optiuni comparate - -**A. Refolosirea `VANZARI_DETALII_TEMP` + o "mini scrie_in_vanzari" pentru editare** — ar insemna ca -formularul de editare (S4) sa populeze TEMP la fel ca la emitere (apeluri per linie), apoi o -procedura noua sa faca INSERT-urile/UPDATE-urile in `VANZARI_DETALII` din TEMP, in aceeasi -tranzactie (impusa de `ON COMMIT DELETE ROWS`, vezi 2.1). Risc: reproduce complexitatea lui -`adauga_articol_factura` (ramificare pe `ntip`/sursa document) fara sa aiba sens la editare — acolo -nu exista comanda/aviz/contract "curent" din care sa se re-deriva pretul; ar trebui un mod nou, -simplificat, de populare a TEMP doar pentru editare, ceea ce complica inutil un cod deja incarcat. -Risc mediu-mare de regresie pe calea de emitere daca vreo modificare atinge accidental -`adauga_articol_factura`/`scrie_in_vanzari` partajate. - -**B. UPDATE/INSERT/soft-DELETE punctual direct in `VANZARI_DETALII`, prin `goExecutor`, dintr-o -procedura noua dedicata editarii** — fara sa treaca deloc prin `VANZARI_DETALII_TEMP`. Model deja -existent si folosit: `modifica_explicatie_articol` (UPDATE tintit pe `ID_VANZARE_DET`). Pentru cele -trei operatii cerute de S4/S4b: - - **Editare cantitate/pret/`pret_cu_tva`** pe o linie existenta: `UPDATE VANZARI_DETALII SET - CANTITATE=..., PRET=..., PRET_CU_TVA=..., DISCOUNT_UNITAR=... WHERE ID_VANZARE_DET = :id`. - - **Stergere linie**: `UPDATE VANZARI_DETALII SET STERS=1, ID_UTILS=:util, DATAORAS=SYSDATE WHERE - ID_VANZARE_DET = :id` — acelasi tipar ca `sterge_factura`, dar tintit pe o singura linie (nou, - nu exista azi, vezi 3.1). - - **Adaugare linie noua**: `INSERT INTO VANZARI_DETALII (ID_VANZARE, ID_ARTICOL, PRET, CANTITATE, - PRET_CU_TVA, DISCOUNT_UNITAR, PROC_TVAV, ID_GESTIUNE, CONT, ID_VALUTA, ID_JTVA_COLOANA, SERIE, - EXPLICATIE, TAXCODE, LOT) VALUES (...)` — PK automat din trigger (vezi 3.2). - Apoi, in ACEEASI tranzactie (deschisa deja de `do_deschide_tranzactie` in fluxul de editare a - notei, cf. `rec_s5_oracle_vanzari.md` B "Idempotenta si tranzactionalitate"), se cheama procedura - de recalcul a totalurilor propusa in `rec_s5_oracle_vanzari.md` (`recalculeaza_totaluri_vanzari`), - care citeste direct din `VANZARI_DETALII WHERE ID_VANZARE=:id AND STERS=0` — **fara nicio - dependenta de `VANZARI_DETALII_TEMP`**. - -### Recomandare: **Varianta B** - -Argumentul principal e riscul de regresie asupra emiterii: Varianta A ar obliga fie la extinderea -lui `adauga_articol_factura` cu o ramura noua "editare" (cod partajat cu emiterea, risc direct pe -calea critica), fie la duplicarea partiala a logicii lui in altă procedura (risc de divergenta, -exact problema pe care #8 a corectat-o deja pentru calculul de totaluri). Varianta B nu atinge deloc -`adauga_articol_factura`/`scrie_in_vanzari`/`VANZARI_DETALII_TEMP` — cod nou, izolat, apelat doar -din calea de editare (`finalizeaza_modificare_nota`), cu acelasi profil de risc "mic" motivat deja -pentru `recalculeaza_totaluri_vanzari` in `rec_s5_oracle_vanzari.md`. In plus, B se potriveste -natural cu S4b (verificare/sincronizare explicita, nu silentioasa): fiecare rand modificat/sters/ -adaugat poate fi tratat ca o comanda separata, usor de enumerat utilizatorului inainte de aplicare -("linia X: cantitate 5 -> 8, pret 10 -> 12"), pe cand Varianta A ar produce un singur bloc opac de -recalcul din care nu se pot extrage usor liniile individuale schimbate. - -**Observatie tehnica pentru implementare**: la editarea preturilor, NU se recalculeaza `V_PROC_TVAV` -din `JTVA_COLOANE`/comanda/contract ca la emitere (asta ar reintroduce dependenta de sursa -documentului, respinsa mai sus) — cota de TVA ramane cea deja persistata pe linie -(`VANZARI_DETALII.PROC_TVAV`), coerent cu felul in care `pret_cu_tva`/`proc_tvav` sunt deja tratate -ca date proprii ale liniei si nu recalculate la fiecare atingere (cf. #7). - -## 5. Ce ramane neconfirmat / in afara scopului acestei cercetari - -- Coloanele `STERS`, `VALIDAT`, `DIFERENTA`, `CUSTODIE`, `DESCARCAT` din `VANZARI_DETALII` sunt - `NOT NULL` fara sa aiba valoare din trigger — probabil au `DEFAULT` la nivel de coloana - (`all_tab_columns.data_default` nu a fost interogat, nu era necesar pentru concluziile de mai - sus); de verificat explicit inainte de a scrie INSERT-ul de linie noua din Varianta B, ca sa nu - fie nevoie sa le populeze manual. -- Valorile implicite exacte pentru `DEFAULT` (daca exista) pe coloanele NOT NULL de mai sus. -- Comportamentul `VANZARI_SETURI_TEMP`/liniile din seturi la editare (#6 nu pare sa acopere editarea - seturilor, doar articolele individuale — de clarificat cu Marius daca seturile sunt in scope). diff --git a/docs/cercetare/rec_d42_efactura.md b/docs/cercetare/rec_d42_efactura.md deleted file mode 100644 index 598a67b..0000000 --- a/docs/cercetare/rec_d42_efactura.md +++ /dev/null @@ -1,245 +0,0 @@ -# Implementarea deciziei 42 — articole needitabile pe factura trimisa in eFactura - -Scris 10.08.2026. Implementare + testare + write-back verificat. **ZERO commit** (git/svn), -conform interdictiei primite. - -> ## COMPLETARE, 10.08.2026 ora ~12:55 — discountul de antet intra sub garda -> -> Agentul care a scris acest raport a apucat sa faca **modificarea de cod si write-back-ul**, apoi a -> cazut pe limita de sesiune **inainte de verificare**. Blocul de mai jos e scris de orchestrator, -> care a preluat si a dus verificarea la capat. -> -> **Decizia lui Marius**: `txtDiscountArt` (discountul de antet) **se blocheaza si el** cand documentul -> e trimis in eFactura — schimba totalurile unui document deja depus la ANAF, chiar daca nu atinge -> nicio linie. -> -> **Codul**: `omodificari.vc2:14813` — `This.pgfArticole.PAGE3.txtDiscountArt.ReadOnly = -> This.lArticoleReadOnly`, prin acelasi flag, fara mecanism nou. -> -> **A doua cale de scriere — cautata si exclusa**: `txtDiscountArt.Valid` (`:16592`) doar cheama -> `Thisform.ActualizeazaBaraTotaluri()`, deci **recalculeaza afisajul, nu scrie discountul nicaieri**. -> Nu exista buton sau apel programatic care sa-l seteze ocolind caseta, deci garda dubla care a fost -> necesara la butoanele de articole (`Click`) **nu isi are rostul aici**. Salvarea propriu-zisa trimite -> discountul ca parametru catre `recalculeaza_totaluri_vanzari` (decizia 41), luand valoarea din caseta. -> -> **Verificat de orchestrator pe disc, dupa caderea agentului**: -> - **Write-back complet**, dovedit prin reconversie binar -> text in cache temporar si comparatie -> **octet cu octet**: identic, 540712 octeti. Cens de octeti `2 aa . 2 e3 . 2 fe`, zero `EF BF BD`. -> - **Regresia rerulata integral pe starea de pe disc**, dupa write-back (binar 12:33:09): -> `test_page3_articole` **14/2** (artefactul headless cunoscut), `test_adauga_linie_articol` **20/0**, -> `test_adauga_linie_valuta` **16/0**, `test_ui_sterge_linie` **8/0**, `test_verdict_act_rul` **26/0**, -> `test_incarca_vanzare_din_nota` **5/0**, `test_s5_validari_articole` **35/0**. Nicio regresie. -> - **Acoperire noua**: `test_efactura_readonly.prg` extins cu doua asertii si rerulat — -> **23 PASS / 0 FAIL** (de la 21). Cele doua noi: `caz A: txtDiscountArt.ReadOnly = .T.` pe documentul -> real trimis in eFactura, si `caz B: txtDiscountArt.ReadOnly = .F. (neregresat)` pe cel netrimis. -> Garda e verificata deci **in ambele sensuri**, nu doar pe cazul pozitiv. -> - Zero procese `vfp9.exe` ramase, zero commituri. -> -> **Ce NU e acoperit**: suita UI vizibila (`test_ui_efactura_readonly.prg`, 14/0) **nu a fost rerulata** -> dupa adaugarea discountului — ea verifica `.When`-urile de celula, neatinse de aceasta completare, -> deci riscul e mic, dar golul e declarat, nu ascuns. -> -> Diff-ul consolidat (COMUN + ROAGEST) e regenerat in diff aplicat (sters). - -## Ce s-a schimbat, si unde - -### `COMUN\clase\omodificari.vc2` (`frm_modific2024`) - -- **Proprietate noua** `lArticoleReadOnly` (Boolean, implicit `.F.`): `*p:` la `:6827`, valoare - implicita la `:6866`. -- **`Show`** (`:14788-14827`): calculeaza flagul in acelasi bloc unde se determina - `lAreArticoleVanzari`/`nIdVanzare`/`nTipVanzare` (adica doar cand `ofacturare_editare.prg` e - incarcat si documentul are rand in `VANZARI`): - `This.lArticoleReadOnly = EsteInEFactura(Nvl(tvanz.id_fact,0))` (`:14798`). In blocul care - pregateste `PAGE3`, aplica flagul: `cmdAdaugaArticol.Enabled`/`cmdStergeArticol.Enabled` = - `!lArticoleReadOnly` (`:14811-14812`), `lblArticoleReadOnly.Visible = lArticoleReadOnly` - (`:14813`). **`PAGE3` continua sa se pregateasca si sa se afiseze normal** — - `PregatesteArticoleFacturaEditare`/`PageCount=3`/`grdArticoleFactura.Refresh()` raman neatinse, - conform corectiei explicite a lui Marius (articolele raman vizibile). -- **Control nou** `pgfArticole.PAGE3.lblArticoleReadOnly` (`ADD OBJECT` la `:12776`): eticheta - discreta, `Visible=.F.` implicit, pozitionata pe randul butoanelor (`Top=4`, `Left=360`, - `Width=390`) — la dreapta lui `cmdAdaugaArticol` (care se termina la `Left+Width=345`), deci - **nu ia spatiu din grid** (gridul ramane la `Top=26`). Text: „Articole needitabile - factura - trimisa in eFactura", `ForeColor RGB(180,120,0)` (aceeasi nuanta de atentionare folosita deja - la verdictul ACT/RUL divergent). -- **Garda pe butoane** (`cmdAdaugaArticol.Click` `:16488`, `cmdStergeArticol.Click` `:16531`): - `OR Thisform.lArticoleReadOnly` adaugat la conditia de `RETURN` timpuriu — belt-and-suspenders - fata de `Enabled=.F.`, pentru ca `Enabled` nu blocheaza un apel programatic al metodei `.Click()`. -- **Garda pe celule** — cele patru `.When` care controleaza editabilitatea pe rand (mecanismul - din S5): `cCantitateArt.Text1.When` (`:16549`), `cPretAchizitieArt.Text1.When`, - `cPretArt.Text1.When` (`:16570`), `cPretCuTvaArt._checkbox1.When` (`:16587`) — toate primesc - `Thisform.lArticoleReadOnly OR ...` (respectiv `!Thisform.lArticoleReadOnly AND ...` pe - checkbox, unde conditia veche era inversa). Se construieste **peste** mecanismul de - editabilitate per rand din S5 (`id_vanzare_set`/`id_vanzare_det`), nu-l inlocuieste. -- **Notele/rulajele nu sunt atinse**: `Grid1` (nota contabila), `grdRulaje`/`grdRulajeObinv` - (paginile 1/2) raman complet neschimbate — verificat explicit in test (`Grid1.ReadOnly` ramane - `.F.` pe documentul din eFactura). - -### `COMUN\programe\ofacturare_editare.prg` - -Corectie necesara descoperita in timpul lucrului (vezi mai jos „Decizie/corectie luata pe -parcurs"): `EsteInEFactura` se apeleaza cu `VANZARI.ID_FACT`, **nu** cu `id_vanzare` — sunt doua -coloane distincte (verificat pe date: 0 potriviri `id_fact = id_vanzare` din 142 facturi tip=1). -Contractul existent (`ofacturare_comun.vc2:3764`, neatins) apela deja `EsteInEFactura(lnIdFact)` -cu `lnIdFact = crsfacturi.id_fact`. `tvanz` (populat de `IncarcaVanzareNota`) nu avea aceasta -coloana. - -- `CreeazaCursorTvanzGol` (linia 153 din fisier): adaugat `id_fact I NULL` la structura cursorului - gol (fallback pe eroare Oracle/cod lipsa). -- `IncarcaVanzareNota` (linia 177): adaugat `v.id_fact` la lista de coloane selectate din - `VANZARI`. Restul interogarii (join, filtre) neatins. -- Antetul fisierului (o singura intrare cumulativa, rescrisa): data actualizata la 10.08.2026, - mentiunea `id_fact` adaugata la lista de campuri incarcate. - -## Decizie/corectie luata pe parcurs — de raportat, nu era in briefing - -Implementarea initiala folosea `EsteInEFactura(This.nIdVanzare)` (adica `id_vanzare`). Verificare -pe `MARIUSM_AUTO`: - -```sql -SELECT COUNT(*) total, SUM(CASE WHEN id_fact = id_vanzare THEN 1 ELSE 0 END) equal_cnt -FROM vanzari WHERE tip = 1 AND sters = 0; --- 142 total, 0 equal_cnt -``` - -`id_fact` si `id_vanzare` sunt spatii de ID complet diferite (`id_fact` are valori de forma -`8008013`, `id_vanzare` valori mici de forma `1013`) — cu `id_vanzare` gardarea nu s-ar fi -declansat NICIODATA in productie (nicio coincidenta intamplatoare intre cele doua plaje). Corectat -inainte de scrierea testelor, folosind exact coloana pe care o foloseste deja calea din -`ofacturare_comun.vc2:3764` (`lnIdFact = crsfacturi.id_fact`). - -## Write-back — verificat prin reconversie + diff, nu pe mtime - -`txt2vcx.ps1 -AllowComun` rulat de trei ori (prima incercare a picat fidelity-check-ul din cauza -ordinii gresite a blocului `ADD OBJECT` — proprietatile/obiectele dintr-o clasa `.vcx` trebuie in -ordine STRICT alfabetica dupa nume, nu dupa ZOrder; a doua rulare a picat pentru ca lipsea -corectia `id_fact`; **a treia rulare, OK**, fidelity check trecut). - -Verificare **independenta** de fidelity-check-ul intern al `txt2vcx.ps1`: reconversie separata a -binarului proaspat scris (`vcx2txt.ps1 -Source omodificari.vcx` intr-un cache temporar izolat) + -`diff`/`cmp` octet cu octet fata de `.vc2`-ul din proiect: - -``` -diff -q omodificari.vc2 /omodificari.vc2 -> IDENTIC -cmp omodificari.vc2 /omodificari.vc2 -> BYTE-IDENTIC -``` - -Text si binar sunt sincrone, dovedit, nu presupus. - -## Cens de octeti (regula cp1250) — inainte/dupa, identic cu baseline - -`omodificari.vc2` are diacritice cp1250 preexistente (tooltip-uri „Renunțare/Adăugare/Ștergere", -octeti `0xFE`/`0xE3`/`0xAA`). **Baseline: `2 aa / 2 e3 / 2 fe`, zero `EF BF BD`.** - -Editarea celor doua proprietati noi (`*p:`/`*`) s-a facut initial cu tool-ul `Edit` -(2 apeluri) — asta a **stricat** censul (`6 ef / 6 bf / 6 bd`, adica `EF BF BD` x2 aparitii x3 -octeti), exact capcana documentata (`Edit`/`Write` re-encodeaza tot fisierul la orice scriere, -indiferent cat de mica). **Reparat** inainte de a continua: octetii corecti (`Renun[FE]are`, -`Ad[E3]ugare`, `[AA]tergere`) preluati din `git cat-file blob HEAD:clase/omodificari.vc2` -(varianta necorupta, comisa) si inlocuiti inapoi punctual. Toate editarile ulterioare (Show(), -garzile pe butoane/celule, blocul `ADD OBJECT` al etichetei noi) s-au facut **pe octeti**, cu -Perl (`<:raw`/`>:raw`, cautare/inlocuire literala `\Q...\E`, fara nicio decodare de encoding), -tocmai ca sa nu se repete problema. **Cens final: `2 aa / 2 e3 / 2 fe`, zero `EF BF BD`** — -identic cu baseline, verificat dupa fiecare grup de editari. - -`ofacturare_editare.prg` nu are octeti `>=0x80` (cens gol inainte si dupa) — editat direct cu -`Edit`, fara risc. - -## Regresie — cifre numarate din loguri, toate DUPA ultima editare de cod - -Ultima editare de cod: `omodificari.vc2` scris in binar la `11:37:23` (a treia rulare -`txt2vcx.ps1`, cu corectia `id_fact`); `ofacturare_editare.prg` editat inainte de asta. Toate -rularile de mai jos sunt **dupa** acel moment. - -| Suita | Rezultat | Baseline | Stare | -|---|---|---|---| -| `test_page3_articole.prg` | 14 PASS / 2 FAIL | 14/2 | **neregresat** (cele 2 FAIL = artefactul headless cunoscut, ColumnCount/ReadOnly pe grid needitabil sub `-A -T`) | -| `test_adauga_linie_articol.prg` | 20 PASS / 0 FAIL | 20/0 | **neregresat** | -| `test_adauga_linie_valuta.prg` | 16 PASS / 0 FAIL | 16/0 | **neregresat** | -| `test_ui_sterge_linie.prg` | 8 PASS / 0 FAIL | 8/0 | **neregresat** | -| `test_verdict_act_rul.prg` | 26 PASS / 0 FAIL | 26/0 | **neregresat** | -| `test_incarca_vanzare_din_nota.prg` | 5 PASS / 0 FAIL | 5/0 | **neregresat** | -| `test_s5_validari_articole.prg` | 35 PASS / 0 FAIL | 35/0 | **neregresat** (linia care contine cuvantul "FAIL" e text descriptiv al unei ramuri moarte deja documentate, nu un esec real — `REZULTAT: 35 PASS / 0 FAIL`) | - -Zero regresii pe toate cele sapte suite existente. - -## Suite noi - -### `COMUN\utile\Teste\editare_factura\test_efactura_readonly.prg` (headless) — **21 PASS / 0 FAIL** - -Documente REALE, gasite prin interogare (nu inventate): -- **Caz A — trimis in eFactura**: `id_vanzare=1013` (`cod=1140509`, an=2025, luna=8), - `VANZARI.ID_FACT=8008013`, prezent in `ANAF_EFACTURA`. -- **Caz B — NEtrimis** (regresie): descoperit prin proprietate (`DescoperaCazTest`, - `FACTURA_ARTICOLE`), `cod=1140895` (id_vanzare=1050, documentul deja folosit de restul suitei). - -Acopera: `EsteInEFactura` direct (cu `0` si cu `8008013`); `lArticoleReadOnly` corect pe ambele -cazuri; **`PAGE3` NU e suprimata** (`PageCount=3`, `RecordSource` neschimbat, `tvd` cu linii); -`cmdAdaugaArticol`/`cmdStergeArticol.Enabled`; vizibilitatea etichetei; garda pe -`cmdStergeArticol.Click()` (apelat direct, fara dialog modal — confirma ca linia nu se modifica -pe documentul din eFactura si ca se modifica normal pe cel obisnuit); `Grid1.ReadOnly` neatins -(nota ramane editabila). Nu scrie in Oracle. - -**Ce nu acopera** (documentat explicit in fisier, nu ascuns): editabilitatea per-celula -(`.When()` pe coloanele gridului) — coloanele **nu se materializeaza sub `-A -T`** -(`ColumnCount=0`, eroare 1925 „Unknown member" la accesul pe nume), artefact cunoscut si -documentat (`grid-coloane-nu-se-materializeaza-headless`). Mutat in suita UI de mai jos. - -### `COMUN\utile\Teste\editare_factura\test_ui_efactura_readonly.prg` (UI vizibil) — **14 PASS / 0 FAIL** - -Rulat prin harnessul corect (`vfp_ui_harness.ps1 -TestPrg ... -Steps @(...) -SyncDir ...`), nu -prin lansare directa — o incercare initiala prin `Start-Process` direct a dat tot `ColumnCount=0`; -cauza reala **nu era lansarea**, ci lipsa apelului `IncarcaArticoleFactura(1013, -'crsArticoleFactura')` **inainte** de `Createobject` — gridul se leaga o singura data, la -construire (in `Load()`), pe cursorul `tvd` existent atunci; fara precarcare, `Load()` creeaza -`tvd` GOL, iar fallback-ul din `Show()` il **recreeaza prin SQL** dupa constructie — cursor diferit -de cel pe care s-a legat gridul, deci `ColumnCount` ramane 0. Corectat dupa tiparul deja folosit de -`test_ui_s5_grid_pret_achizitie.prg`. - -Acopera, pe acelasi document real (`id_vanzare=1013`, in eFactura): gridul are 15 coloane -(neregresat); `.When()` pe toate cele patru controale (`cCantitateArt`, `cPretArt`, -`cPretAchizitieArt`, `cPretCuTvaArt._checkbox1`) intorc `.F.` — **needitabile pe orice linie -normala, nu doar pe liniile din set**; caz sintetic de control (`lArticoleReadOnly` comutat manual -pe `.F.` pe acelasi document/aceleasi obiecte) — cele trei celule redevin editabile, deci gating-ul -e demonstrat pe flag, nu pe o coincidenta a documentului ales. Captura: -`screenshots_efactura\step_0_grid_efactura_readonly.png`. Nu scrie in Oracle. - -## Ce NU s-a putut testa, si de ce - -- **Dialogul de adaugare articol** (`frm_articol_factura`, `Show(1)` modal): garda de pe - `cmdAdaugaArticol.Click()` (Enabled=.F. + guard in cod) nu s-a putut exercita prin click real - headless — consecvent cu limitarea deja documentata pe restul suitei S4/S5 (dialog modal). Doar - `Enabled=.F.` verificat direct. -- **Ramura moarta preexistenta, gasita dar NEATINSA** (nu in scope): linia - `IF !Used('tvd') OR !This.lAreArticoleVanzari OR Thisform.lArticoleReadOnly` din - `cmdAdaugaArticol.Click` (`:16497`) foloseste `This.lAreArticoleVanzari` — `This` acolo e - butonul, nu formularul, deci proprietatea nu exista pe el. Ramane neexecutata in practica pentru - ca `!Used('tvd')` e `.F.` de fiecare data cand butonul chiar e vizibil (short-circuit VFP), deci - runtime-ul nu ajunge niciodata sa evalueze operandul gresit. Preexistenta editarii mele - (adaugarea mea e doar `OR Thisform.lArticoleReadOnly`, corect scris cu `Thisform`), nu se - repara aici — in afara scope-ului deciziei 42. -- **Verificarea vizuala pe ecran de Marius**: eticheta discreta, pozitionarea ei fata de butoane, - culoarea, comportamentul real la click de mouse pe grid. - -## Stare finala verificata - -- **Write-back facut si dovedit** pentru `omodificari.vc2` (reconversie + diff octet cu octet, - identic). `ofacturare_editare.prg` e `.prg` — sursa directa, fara pas de write-back. -- Cens de octeti pe `omodificari.vc2`: **`2 aa / 2 e3 / 2 fe`, zero `EF BF BD`** — identic cu - baseline, verificat dupa ultima editare. -- Zero procese `vfp9.exe` ramase (verificat cu `tasklist`) dupa toate rularile mele. Procesul - `vfp9.exe` al agentului `s8-creare-variante` (fereastra „S8 - creare documente") a ramas - intact, neatins. -- Zero scrieri in Oracle in toata sesiunea — toate interogarile (`EsteInEFactura`, - `IncarcaCursoareModificareNota`, `IncarcaVanzareNota`, `IncarcaArticoleFactura`) sunt `SELECT`. -- Zero commit (git/svn). -- Diff consolidat: diff aplicat (sters) (ambele fisiere). -- Fisiere noi, necomise: `COMUN\utile\Teste\editare_factura\test_efactura_readonly.prg` (+ log), - `COMUN\utile\Teste\editare_factura\test_ui_efactura_readonly.prg` (+ log + screenshot in - `screenshots_efactura\`). - -## Interzis — respectat - -`ofacturare.vc2`, `ofacturare_comun.vc2`, `comun.vc2`: neatinse (verificat — niciun `Edit`/`Write` -pe ele in aceasta lucrare). `actualizeaza_vanzari`, `PACK_CONTAFIN`, `PACK_FACTURARE`: neatinse. -Niciun `INSERT`/`UPDATE` pe `id_vanzare` `1049`/`1050`/`1048`. diff --git a/docs/cercetare/rec_datoria6_baza_regresie.md b/docs/cercetare/rec_datoria6_baza_regresie.md deleted file mode 100644 index be4c59d..0000000 --- a/docs/cercetare/rec_datoria6_baza_regresie.md +++ /dev/null @@ -1,188 +0,0 @@ -# Datoria 6 — ce s-a intamplat cu baza de regresie #6/S4 si cum a fost re-ancorata - -09.08.2026. Schema `MARIUSM_AUTO@ROA_CENTRAL`. **Strict citiri pe Oracle** — nicio scriere, niciun -commit. - -## 1. Ce s-a intamplat cu documentul (dovada pe randuri) - -Ipoteza de plecare **se confirma**: documentul nu s-a pierdut, i s-a **realocat `cod`-ul**. -`ID_VANZARE = 1050` exista, e activ si nu si-a schimbat niciun total — doar `COD` a trecut de la -**1140888** la **1140895**. - -``` -ID_VANZARE COD STERS TIP NUMAR_ACT SERIE DATA_ACT ID_FACT TOTAL_CU_TVA - 1050 1140895 0 1 547 SSS 07.08.2026 8009660 1924.59 - 1047 1140885 0 -12 544 SSS 07.08.2026 8009657 747.79 - 1048 1140894 0 1 545 SSS 07.08.2026 8009658 302.51 - 1049 1140887 0 1 546 SSS 07.08.2026 8009659 573.81 -``` - -Pe intervalul 1140880-1140900 exista **doar** aceste 4 randuri; `max(cod)` in `VANZARI` e -**1140895**, `max(id_vanzare)` e **1050**. Nu exista niciun rand pe `cod = 1140888`. - -Nota contabila arata acelasi lucru — acelasi antet, acelasi total, doar `STERS` si `COD` diferite: - -``` - COD AN LUNA STERS N NRACT SERIE DATAACT ID_FACT SUMA_TOT - 1140888 2026 8 1 24 547 SSS 07.08.2026 8009660 4836.67 - 1140895 2026 8 0 24 547 SSS 07.08.2026 8009660 4836.67 -``` - -24 de randuri vechi marcate `STERS=1` pe `cod` vechi, 24 de randuri noi active pe `cod` nou, cu -`nract`/`serie_act`/`dataact`/`id_fact` identice si aceeasi suma. Este exact semnatura lui -`finalizeaza_modificare_nota` + `pack_facturare.actualizeaza_vanzari(cod_vechi, cod_nou)`. -`VANZARI_DETALII` pe `id_vanzare = 1050` are in continuare **4 linii active** — neatins, corect -(scrierea in `VANZARI_DETALII` e S5, inca neimplementata). - -**Cand**, pe secunda (`ACT.DATAORAS` = marcarea ca sters, `ACT.DATAORA` = crearea randului): - -| Actiune | Moment | -|---|---| -| `cod=1140893` marcat sters, `cod=1140894` creat (`id_vanzare=1048`) | 08.08.2026 09:16:29 / 09:16:30 | -| `cod=1140888` marcat sters, `cod=1140895` creat (`id_vanzare=1050`) | 08.08.2026 14:05:15 / 14:05:16 | - -Deci **nu** testul de write-back aprobat a mutat documentul de regresie: acela a lucrat pe -`id_vanzare = 1048` dimineata la 09:16 (`1140886 -> 1140893 -> 1140894`, consemnat in `progres.md`). -Mutarea lui `1050` e o **a doua salvare, la 14:05**, pe un alt document — cel folosit ca ancora de -regresie. Nu exista in `ACT` niciun `cod` intermediar intre 1140888 si 1140895 (1140889-1140892 n-au -randuri), deci a fost o singura realocare. - -**Nimic de recreat.** Datele nu s-au pierdut; ancorarea suitelor era gresita. Prin urmare **nu se -cere nicio decizie de INSERT/UPDATE** din partea lui Marius pe partea de date. - -Ancorele celorlalte cazuri sunt neatinse: `id_vanzare` 1047 (`cod=1140885`), 506 (`cod=1137874`), -882 (`cod=1139934`) sunt toate active pe acelasi `cod` ca inainte — se realoca doar documentele care -chiar se salveaza, adica cele din luna curenta, singurele care trec de garzile din -`do_editare_factura`. - -## 2. Cifrele masurate azi, inainte de modificare - -Cifra din `progres.md` (`7 PASS / 3 FAIL`) era **veche**: numara doar cele 10 asertii de la runda 2, -inainte ca blocul 3A sa adauge alte 5. Masurat azi, pe starea de pe disc: - -| Suita | Inainte | Dupa | -|---|---|---| -| `test_page3_articole.prg` | **8 PASS / 7 FAIL** (15 verificari) | **13 PASS / 2 FAIL** | -| `test_incarca_vanzare_din_nota.prg` | **4 PASS / 1 FAIL** | **5 PASS / 5** | - -Ambele rulari (inainte si dupa): `exit code 0`, **0 dialoguri native**, sub -`COMUN\utile\Teste\watchdog_vfp.ps1 -AutoDismiss` (care sterge `.fxp`-ul inainte de fiecare -lansare). `loForm.ClassLibrary` e asigurat de blocul existent -`RELEASE CLASSLIB omodificari` + `SET CLASSLIB TO D:\ROA\ROAFACTURARE\COMUN\clase\omodificari.vcx`, -neatins de modificarea de fata. - -Motivul concret al esecurilor: `IncarcaCursoareModificareNota` filtreaza `STERS = 0`, iar toate cele -24 de randuri `ACT` de pe `cod=1140888` sunt `STERS=1` — deci `tact` venea **cu 0 randuri**, iar -suita nici nu ajungea sa instantieze formularul (`PageCount = -1` in log). - -## 3. Ce s-a schimbat in suite si de ce - -Principiul aplicat e **varianta 1 din brief**: suitele isi descopera singure documentul de test, dupa -**proprietatea ceruta de asertie**, nu dupa identitatea lui. Ancorarea pe `cod` era condamnata prin -constructie (se realoca la fiecare salvare); ancorarea pe `id_vanzare` ar fi rezistat, dar tot cere -un numar scris de mana intr-un fisier de test. - -### Fisier nou: `COMUN\utile\Teste\editare_factura\descopera_caz_test.prg` - -`DescoperaCazTest(, )` lasa in alias un rand cu `cod, an, luna, id_vanzare, tip, -nlin` si intoarce `.T.`/`.F.` Sase cazuri, fiecare o proprietate: - -| Caz | Proprietatea ceruta | Rezolvat azi la | -|---|---|---| -| `FACTURA_ARTICOLE` | `tip=1` activa, cu linii active, a carei nota are **primul** rand (`min(id_act)`) pe acelasi `(nract, serie_act, dataact)` ca vanzarea | `cod=1140895`, `id_vanzare=1050`, 4 linii | -| `NEFACTURA_ARTICOLE` | la fel, dar `tip <> 1` (decizia 19 — detectia merge pe orice tip) | `cod=1140885`, `id_vanzare=1047`, `tip=-12` | -| `FARA_RULAJE` | linii active + **zero** randuri in `vrul_tot` si `vrul_obinv_tot` | `cod=1140885`, `id_vanzare=1047` | -| `PRIM_RAND_ORB` | nota al carei **prim** rand NU duce la vanzare, dar un triplet ulterior da | `cod=1140401`, `id_vanzare=1005` | -| `COLIZIUNE_COD` | idem + `cod` cu 2+ randuri active in `VANZARI` + un triplet din nota fara corespondent | `cod=1139934`, `id_vanzare=882`, `nract` fara corespondent = 13 | -| `NOTA_FARA_VANZARI` | nota activa fara rand in `VANZARI` pe **niciun** triplet | `cod=1140883`, an 2026, luna 7 | - -Doua lucruri contau la proiectare: - -- **Independenta fata de codul testat.** Interogarile merg pe `VACT_TOT` / `VRUL_TOT` / - `VRUL_OBINV_TOT` / `VANZARI` / `VANZARI_DETALII` cu join direct, adica pe **alt drum** decat - `IncarcaVanzareNota` / `IncarcaVanzareDinNota` / `IncarcaArticoleFactura`. Valoarea asteptata - (`id_vanzare`, `tip`, numarul de linii) nu vine de la functia verificata, deci asertia nu devine - tautologica. -- **Ordonare determinista** (`order by v.id_vanzare desc`, respectiv `an/luna/cod desc`), ca doua - rulari succesive pe aceleasi date sa aleaga acelasi document. Cazul ales e scris in log la - inceputul fiecarei rulari, ca sa se vada pe ce document s-a masurat. -- **Nicio potrivire = FAIL explicit**, nu test sarit: `caz_negasit` scrie in log - `niciun document din schema nu satisface conditia cazului` + `FAIL`. - -Interogarile au fost validate intai direct in `sqlplus` (fiecare intoarce documentul asteptat), abia -apoi puse in cod. - -### `test_page3_articole.prg` - -Toate cele sase documente hardcodate (`1140888`, `1140885`, `1125486`, `1139934`, `1137874`) au fost -inlocuite cu cazul descoperit corespunzator. Structura asertiilor e neschimbata; procedurile de -verificare si-au pastrat corpul, doar au primit prin parametru ce inainte era scris in ele: - -- `verifica_coliziune_cod` primea zero parametri si continea `1139934 / 375 / 'SSS' / 31.12.2021` si - `nract=13`; acum primeste `cod`, tripletul care **trebuie** gasit, `id_vanzare` asteptat si - **tripletul complet** care **nu trebuie** gasit. Descoperirea intoarce cele trei coloane ale - randului negativ de pe acelasi rand (`keep (dense_rank first order by nract)`), ca sa nu se combine - `nract`-ul unui rand cu `serie_act`-ul altuia — altfel asertia negativa ar fi trecut din alt motiv - decat cel testat. -- **O asertie s-a intarit, niciuna nu s-a slabit.** Cazul B (`tip <> 1`) trecea inainte cu - `tnLiniiAsteptate = -1`, adica verificarea numarului de linii era dezactivata; acum primeste - numarul real din `VANZARI_DETALII` (2) si il verifica. - -### `test_incarca_vanzare_din_nota.prg` - -Aceleasi patru cazuri, plus cazul EOF, trecute pe descoperire. Nicio schimbare de asertie. - -## 4. Ce a ramas neacoperit - -**Doua asertii din blocul 3A raman FAIL, si NU din cauza datelor** — sunt artefactul de mediu deja -consemnat ca datoria 7 in `progres.md`: - -``` -EROARE 1925 [VERIFICA_EDITARE_GRID:353] Unknown member COLUMN5. - structura grid/cursor (ColumnCount=14, lmodificat L, valoare N) = FAIL - ReadOnly (cantitate/pret/pret_cu_tva editabile, checkbox pe pret_cu_tva, restul readonly) = FAIL - stare initiala (lmodificat=.F., valoare calculata corect) = PASS - dupa editare cantitate (lmodificat=.T., valoare recalculata) = PASS -``` - -Sub `vfp9.exe -A -T` grid-ul nu se materializeaza: `ColumnCount` raporteaza `0` si `ColumnN` nu -exista ca membru, oricat de complet ar fi definita clasa. Repartitia 2 FAIL (structura, `ReadOnly`) -/ 2 PASS (cursor, calcul) e **exact** cea prezisa in `progres.md`, datoria 7 — deci suita e acum -inapoi la starea ei dinaintea degradarii bazei, nu mai bine si nu mai rau. - -Cele doua asertii **nu au fost atinse, slabite sau sterse**. Ele sunt oricum acoperite corect pe -ecran de `COMUN\utile\Teste\editare_factura\test_ui_fix_editabil_subtotal.prg` sub -`vfp_ui_harness.ps1` (11/11 PASS, `ColumnCount=14`, consemnat in `progres.md`). Daca se doreste -curatarea zgomotului, varianta corecta e cea deja propusa la datoria 7 — rescrierea lor ca -verificare **statica** pe memo-ul `Properties` din `.vcx` — dar asta e alta lucrare, nu re-ancorare. - -Altele: - -- Nu s-a verificat comportamentul suitelor pe alta schema decat `MARIUSM_AUTO`. Descoperirea e - scrisa sa mearga pe orice schema, dar nu a fost probata pe `ROMFAST` sau `VENDING`. -- Cazurile `PRIM_RAND_ORB` si `NOTA_FARA_VANZARI` se rezolva azi la alte documente decat inainte - (`1140401` in loc de `1137874`, `1140883` in loc de `1125486`) — proprietatea testata e insa - aceeasi, iar ambele trec. Vechile documente raman valide, doar ca nu mai sunt primele in ordinea - determinista. -- Nu s-a atins nimic din `omodificari.vc2`, `ofacturare_comun.vc2`, `ofacturare_editare.prg` sau - binarele lor. `git status` in `COMUN` confirma: singurele fisiere de test schimbate sunt cele doua - suite plus fisierul nou. - -## 5. Fisiere - -| Fisier | Stare | -|---|---| -| `COMUN\utile\Teste\editare_factura\descopera_caz_test.prg` | **nou** | -| `COMUN\utile\Teste\editare_factura\test_page3_articole.prg` | modificat | -| `COMUN\utile\Teste\editare_factura\test_incarca_vanzare_din_nota.prg` | modificat | -| diff aplicat (sters) | diff-ul celor trei | - -Comenzile de rulare: - -```powershell -powershell -ExecutionPolicy Bypass -File D:\ROA\ROAFACTURARE\COMUN\utile\Teste\watchdog_vfp.ps1 -Script 'D:\ROA\ROAFACTURARE\COMUN\utile\Teste\editare_factura\test_page3_articole.prg' -AutoDismiss -TimeoutSec 300 -powershell -ExecutionPolicy Bypass -File D:\ROA\ROAFACTURARE\COMUN\utile\Teste\watchdog_vfp.ps1 -Script 'D:\ROA\ROAFACTURARE\COMUN\utile\Teste\editare_factura\test_incarca_vanzare_din_nota.prg' -AutoDismiss -TimeoutSec 240 -``` - -Logurile: `..._log.txt` langa fiecare suita; rezumatul watchdog-ului in -`COMUN\utile\Teste\editare_factura\watchdog_out\`. diff --git a/docs/cercetare/rec_dec42_proiectare.md b/docs/cercetare/rec_dec42_proiectare.md deleted file mode 100644 index 308df91..0000000 --- a/docs/cercetare/rec_dec42_proiectare.md +++ /dev/null @@ -1,312 +0,0 @@ -# Proiectare — Decizia 42: articole factura vizibile, needitabile dupa trimiterea in eFactura - -Scris 10.08.2026. Agent read-only, doar cercetare/proiectare — nicio modificare de cod facuta -de acest agent. - -## ATENTIE — implementarea era deja IN LUCRU, de catre alt agent; bugul de mai jos e DEJA CORECTAT - -Inainte de a ajunge la proiectare: la momentul cercetarii, `COMUN\clase\omodificari.vc2` avea deja -**modificari necomise in working tree** (`git status` in `COMUN`: `M clase/omodificari.vc2`) care -implementeaza aproape exact Decizia 42 corectata (vizibil, doar needitabil). Agentul `d42-efactura`, -activ in aceeasi sesiune, lucra chiar atunci pe exact acest subiect. - -**Acest raport nu descrie deci o proiectare de la zero, ci: (1) verificarea contractelor si (2) un -review al diff-ului in lucru, cu un bug real gasit si dovedit pe schema Oracle — vezi §1 si §8.** - -**UPDATE, dupa trimiterea raportului**: `d42-efactura` a gasit acelasi bug independent, in timpul -propriei lucrari (nu din briefingul primit de la team-lead), l-a corectat si scris in binar -**inainte** sa primeasca mesajul meu. **Verificat direct pe disc de acest agent** (nu doar preluat -din raportarea lui `d42-efactura`): `git diff` pe `COMUN\clase\omodificari.vc2` arata -`This.lArticoleReadOnly = EsteInEFactura(Nvl(tvanz.id_fact,0))`, iar `git diff` pe -`COMUN\programe\ofacturare_editare.prg` arata `v.id_fact` adaugat in SELECT-ul din -`IncarcaVanzareNota` si `id_fact I NULL` adaugat in schema `CREATE CURSOR tvanz` din -`CreeazaCursorTvanzGol` — exact varianta B recomandata la §8. **Bugul e inchis, nu mai necesita -actiune.** - -Legat de linia necomisa gasita atunci in `ROAGEST\Programe\roagest.prg` -(`SET PROCEDURE TO ofacturare_editare.prg ADDITIVE`, "Not Committed Yet" la 10.08.2026 11:28): -`d42-efactura` confirma ca nu e a lui — a atins doar `omodificari.vc2` si `ofacturare_editare.prg` -(plus fisiere de test noi). Ramane deci dintr-un alt bloc de lucru (posibil S9), de verificat separat -de team-lead — nu descrie starea lui `d42-efactura`. - ---- - -## 1. Cum se afla ca documentul e in eFactura - -**Functie**: `FUNCTION EsteInEFactura`, globala (nu metoda de clasa), definita in -`COMUN\programe\ofacturare_editare.prg:16-25`: - -``` -*!* parametru: id_fact -*!* verifica daca factura cu id_fact dat a fost deja trimisa in eFactura (anaf_efactura) -FUNCTION EsteInEFactura - LPARAMETERS tnIdFact - LOCAL lcSql, lnEFactura, llSucces - lnEFactura = 0 - lcSql = [select count(*) as lneFactura from anaf_efactura where id_fact = ] + Alltrim(Str(Nvl(m.tnIdFact,0))) - llSucces = goExecutor.oSelecteaza2Value(m.lcSql, @lnEFactura) - RETURN (Nvl(m.lnEFactura,0) > 0) -ENDFUNC -``` - -**Contract, deschis din `RETURN`**: primeste `tnIdFact` — **`ID_FACT`, nu `ID_VANZARE`** — si intoarce -`.T./.F.` (numar de randuri in `ANAF_EFACTURA` cu acel `id_fact` > 0). Foloseste `goExecutor` -(disponibil global, aceeasi conventie ca restul aplicatiei). - -**Disponibilitate cross-project — VERIFICAT, nu presupus**: `ofacturare_editare.prg` e inregistrat -prin `SET PROCEDURE ... ADDITIVE` in toate cele trei aplicatii: - -| App | Fisier | Linie | Stare git | -|---|---|---|---| -| ROAFACTURARE | `Programe\roafacturare.prg` | 214 | comis demult | -| ROACONT | `Programe\roacont.prg` | 212 | comis, `3af0089` (08.08.2026) — mesaj: *"Fara el, pagina de articole ale facturii nu apare in registrul jurnal > modificare"* | -| ROAGEST | `Programe\roagest.prg` | 260 | **necomis**, adaugat azi 10.08.2026 (vezi avertismentul de mai sus) | - -Deci `EsteInEFactura` **este** apelabila din contextul `omodificari.vc2`, inclusiv din ROACONT si -(dupa commit-ul in curs) ROAGEST. Comentariul din `omodificari.vc2:14786-14787` ("apare doar cand -ofacturare_editare.prg e incarcat (doar ROAFACTURARE - ROACONT/ROAGEST nu-l inregistreaza)") e -**invechit** — scris inainte de commit-ul `3af0089`, care a inversat exact aceasta premisa pentru -ROACONT. Nu se corecteaza comentariul in acest raport (read-only), dar oricine implementeaza ar -trebui sa-l actualizeze cand atinge zona. - -**BUG GASIT in diff-ul in lucru — `id_fact` confundat cu `id_vanzare`.** Apelul din -`omodificari.vc2:14798` (diff necomis) e: - -``` -This.lArticoleReadOnly = EsteInEFactura(This.nIdVanzare) -``` - -dar `This.nIdVanzare` e populat cu `tvanz.id_vanzare` (linia 14796), adica `VANZARI.ID_VANZARE` — -**alt camp** decat `VANZARI.ID_FACT`, pe care `EsteInEFactura` il asteapta. Dovada, in trei straturi: - -1. **Cod**: precedentul functional existent, `ofacturare_comun.vc2:3739` (`lnIdFact = id_fact`) si - `:3764` (`EsteInEFactura(lnIdFact)`), citeste `id_fact` dintr-un camp separat de `id_vanzare` - (`:3734`, `lnIdVanzare = id_vanzare`, aceeasi cursor `crsfacturi`, doua LOCAL-uri distincte). - Acelasi tipar in `anaf_efactura.prg:3123-3124`: `pnIdFact = id_fact` si `lnIdVanzare = id_vanzare` - citite **pe acelasi rand** din `crsFacturiEmise` — daca ar fi acelasi numar, codul n-ar avea - nevoie de doua variabile. -2. **Schema Oracle** (verificat read-only, `user_tab_columns`, `MARIUSM_AUTO`): `VANZARI` are **ambele** - coloane, `ID_FACT` si `ID_VANZARE`, distincte; `ANAF_EFACTURA` are doar `ID_FACT`. -3. **Date reale** (verificat read-only, join `anaf_efactura.id_fact = vanzari.id_fact`): pentru - documentele deja trimise in eFactura, cele doua valori difera constant — - - | id_fact | id_vanzare | cod | numar_act | data_act | - |---|---|---|---|---| - | 8008013 | 1013 | 1140509 | 510 | 31-AUG-25 | - | 8007922 | 1007 | 1140439 | 503 | 03-JAN-25 | - | 8007836 | 993 | 1140380 | 490 | 15-AUG-24 | - | 8007810 | 991 | 1140369 | 488 | 11-JUL-24 | - -Cu bug-ul curent, `EsteInEFactura(1013)` cauta `id_fact = 1013` in `ANAF_EFACTURA` — care nu exista -cu acel numar — deci **intoarce mereu `.F.`**, chiar si pentru documente reale trimise in eFactura. -Garda ar fi complet inoperanta pe date reale. Corectia: §8. - -Niciun document dintre cele patru de mai sus nu e din luna curenta (limitare de date deja cunoscuta -din bloc — garda de editare cere luna curenta), deci un test headless pe un caz real din productie -de date nu se poate face acum; testul trebuie sa foloseasca un cursor `tact`/`tvanz` mock cu -`id_fact` setat manual (acelasi tipar folosit deja pentru `id_vanzare_set` in S5). - -## 2. Unde se aseaza conditia in omodificari.vc2 - -Locul folosit deja de diff-ul in lucru e corect si e cel recomandat: `frm_modific2024.Show()` -(`omodificari.vc2:14748-14837` in varianta comisa `1c42ae0`; extins la 14748-14856 in diff-ul -necomis), imediat dupa blocul care rezolva `This.nIdVanzare`/`This.nTipVanzare` prin -`IncarcaVanzareDinNota('tact')` si inainte de `This.pgfArticole.PageCount = 3`. E punctul unde -formularul stie deja daca documentul curent are rand in `VANZARI` (`This.lAreArticoleVanzari`) — -conditia eFactura se agata direct pe acest rezultat, fara sa duplice logica de potrivire -cod/nract/serie_act/dataact -> id_vanzare. - -Proprietatea noua, `lArticoleReadOnly` (declarata la nivelul clasei, langa `lAreArticoleVanzari`, -`omodificari.vc2:6827` si `6867` in diff), e citita apoi din `.When`-urile coloanelor editabile ale -gridului `grdArticoleFactura` (mecanismul de editabilitate per rand din S5, §3-4) — deci conditia -de formular **compune** cu mecanismul existent, nu-l inlocuieste. - -## 3. Controale care se dezactiveaza — confirmate pe cod - -| Control | Path | Linii (comis) | Ce face diff-ul | -|---|---|---|---| -| Buton adaugare | `pgfArticole.PAGE3.cmdAdaugaArticol` | Click: `16488-16529` | `.Enabled = !This.lArticoleReadOnly` la afisare (`Show`); plus guard `OR Thisform.lArticoleReadOnly` in `Click` (aparare in adancime, cazul `Enabled` sarit programatic) | -| Buton stergere | `pgfArticole.PAGE3.cmdStergeArticol` | Click: `16531-16540` | idem | -| Cantitate | `grdArticoleFactura.cCantitateArt.Text1.When` | `16549-16554` | adauga `Thisform.lArticoleReadOnly OR` in fata conditiei existente `Nvl(tvd.id_vanzare_set,0)<>0` | -| Pret achizitie | `grdArticoleFactura.cPretAchizitieArt.Text1.When` | `16556-16561` | idem | -| Pret | `grdArticoleFactura.cPretArt.Text1.When` | `16570-16575` | idem | -| Pret cu TVA (checkbox) | `grdArticoleFactura.cPretCuTvaArt._checkbox1.When` | `16587-16589` | `RETURN !Thisform.lArticoleReadOnly AND ...` | -| Discount | `pgfArticole.PAGE3.txtDiscountArt` | — | **neatins in diff** — vezi nota de mai jos | - -**Gol observat**: `txtDiscountArt` (discountul pe factura, cu `ControlSource = tvanz.discount`) nu -are `.When` si nu apare in diff. Daca discountul e considerat parte din "articolele facturii" in -sensul Deciziei 42 (afecteaza totalul liniilor), ramane editabil dupa trimiterea in eFactura — de -clarificat cu Marius sau de inclus explicit. Nu era in lista `cmdAdaugaArticol`/`cmdStergeArticol` -ceruta explicit in brief, deci il semnalez ca gol, nu-l tratez ca bug. - -## 4. Tipar read-only pe grid, folosit deja in proiect - -Nu se inventeaza un tipar nou. Proiectul (din S5) foloseste deja exact acest idiom pentru randurile -needitabile (liniile din seturi, `id_vanzare_set <> 0`): fiecare coloana editabila a gridului are -un handler `.When` care intoarce `.F.` cand conditia de blocare e adevarata — VFP nu lasa controlul -sa intre in editare daca `.When` intoarce `.F.`. Diff-ul in lucru extinde exact aceste `.When`-uri -existente, adaugand `Thisform.lArticoleReadOnly OR` in fata conditiei deja acolo — e continuarea -directa a mecanismului S5, nu un tipar paralel. Confirma cererea din brief: "foloseste tiparul deja -folosit in proiect, nu inventa unul nou" — respectat. - -## 5. Feedback vizual pentru utilizator - -Diff-ul adauga o eticheta noua, `pgfArticole.PAGE3.lblArticoleReadOnly` -(`omodificari.vc2:12857-12871` in diff), cu `Caption = "Articole needitabile - factura trimisa in -eFactura"`, `Visible` legat de `This.lArticoleReadOnly` in `Show()`. E minimul cerut de brief — -niciun tipar de marcaj standard read-only nu exista deja in proiect (nu s-a gasit unul in cautarea -pentru §4), deci eticheta simpla e alegerea rezonabila. Singura observatie: eticheta e pozitionata -`Top = 4, Left = 360` — de verificat pe ecran (fara VFP IDE nu se poate confirma vizual) ca nu se -suprapune cu alt control din PAGE3 la latimile de forma folosite azi. - -## 6. Suprafata de regresie — de ce notele obisnuite din registrul jurnal nu sunt atinse - -Blocul care calculeaza `lArticoleReadOnly` ruleaza **doar** in interiorul conditiei deja existente: - -``` -IF "OFACTURARE_EDITARE" $ Upper(Set("Procedure")) AND Reccount('tact') > 0 - IncarcaVanzareDinNota('tact') - IF Reccount('tvanz') = 1 - This.lAreArticoleVanzari = .T. - ... - This.lArticoleReadOnly = EsteInEFactura(...) - ENDIF -ENDIF -``` - -`IncarcaVanzareDinNota` cauta in `VANZARI` un rand cu tripletul (cod, nract, serie_act, dataact) al -notei curente. Pentru o nota contabila obisnuita (nu factura de vanzare), acest rand nu exista, -`Reccount('tvanz')` ramane `0`, deci **intreg blocul `IF Reccount('tvanz') = 1` e sarit** — -`This.lAreArticoleVanzari` ramane `.F.` si `This.lArticoleReadOnly` ramane la valoarea implicita -`.F.` (setata explicit chiar inainte de bloc, `:14791`). Consecinta directa: `This.pgfArticole.PageCount -= 2` (fara PAGE3), deci `cmdAdaugaArticol`/`cmdStergeArticol`/gridul de articole **nici nu exista** -pentru documentul respectiv — nu doar ca nu sunt dezactivate, ci nu sunt instantiate in fluxul de -afisare. Riscul de regresie pe notele obisnuite din ROAGEST/ROACONT e deci **zero prin constructie**, -mostenit din gating-ul `lAreArticoleVanzari` deja livrat si testat in S4/S5 — Decizia 42 doar adauga -o conditie suplimentara **in interiorul** ramurii deja izolate pentru facturi de vanzare, nu schimba -gating-ul insusi. Acesta e exact argumentul cerut in brief la punctul 6, si e verificabil citind -codul, nu presupus. - -`comun.vc2` (clasa `afisjurcom`, `do_modifica`, `2222-2572`) nu e atins de acest diff — ramane -neschimbat, confirmand ca intreaga logica sta in `omodificari.vc2`, un singur punct de intretinere. - -## 7. Teste minime propuse (headless, in stilul suitei existente) - -Suita `COMUN\utile\Teste\editare_factura\` foloseste harness-ul `ui_harness.prg` + `asserteaza`, cu -formular vizibil (`WindowType = 0`, `Show()`, `DOEVENTS FORCE`), fara input real (nici un -`keybd_event`/`SendInput` — doar citire directa de proprietati si apel direct de metode). Propunere, -dupa modelul `test_ui_sterge_linie.prg`: - -**`test_d42_efactura_readonly.prg`** — cazul pozitiv, cu date mock (nu exista document real din -luna curenta trimis in eFactura, cf. §1): -1. Incarca `tact`/`tvd` pentru un document real din luna curenta cu `lAreArticoleVanzari = .T.` - (ex. acelasi `id_vanzare = 1049` mentionat in handoff intermediar (sters), daca inca in luna - curenta la momentul rularii). -2. Dupa `IncarcaVanzareDinNota`, forteaza `tvanz.id_fact` (sau `tact.id_fact`, dupa care varianta se - alege in §8) la o valoare stiuta ca exista in `ANAF_EFACTURA` (mock: `INSERT` intr-un cursor - local, nu in Oracle) — sau, mai simplu, mock-uieste direct `EsteInEFactura` prin - `SET PROCEDURE`/redefinire temporara, dupa tiparul deja folosit in proiect pentru izolarea de - Oracle in teste UI. -3. `assert`: `loForm.lArticoleReadOnly = .T.` -4. `assert`: `loForm.pgfArticole.PAGE3.cmdAdaugaArticol.Enabled = .F.` -5. `assert`: `loForm.pgfArticole.PAGE3.cmdStergeArticol.Enabled = .F.` -6. `assert`: `loForm.pgfArticole.PAGE3.lblArticoleReadOnly.Visible = .T.` -7. `assert`: grid-ul e in continuare **vizibil** si populat — `loForm.pgfArticole.PageCount = 3`, - `Reccount('tvd') > 0` — confirma partea corectata a deciziei (nu se suprima pagina). -8. `assert`: pozitionare pe `grdArticoleFactura`, `SetFocus` pe coloana `cCantitateArt` nu intra in - editare — verificat prin apelul direct al handlerului `.When` (`loForm.pgfArticole.PAGE3. - grdArticoleFactura.cCantitateArt.Text1.When()` trebuie sa intoarca `.F.`), nu prin tastare. - -**`test_d42_efactura_editabil.prg`** — cazul negativ (control): acelasi document, dar -`EsteInEFactura` mock-uit sa intoarca `.F.` -> toate assert-urile de mai sus inversate (`Enabled = -.T.`, `.When()` nu intoarce `.F.` din cauza flagului — poate intoarce `.F.` din alt motiv, ex. -`id_vanzare_set`, testat separat). - -**`test_d42_nota_obisnuita_neatinsa.prg`** — cazul de regresie cerut la §6: incarca o nota -contabila fara corespondent in `VANZARI` (orice test existent din `COMUN\utile\Teste\` care nu tine -de facturare), verifica `loForm.pgfArticole.PageCount = 2` si ca `lArticoleReadOnly` ramane `.F.` -fara sa fi fost nevoie sa se apeleze `EsteInEFactura` deloc (se poate confirma indirect, verificand -ca nu s-a facut nicio interogare Oracle suplimentara, sau direct daca se mock-uieste functia cu un -contor de apeluri). - -Toate cele trei suite: fara scriere in Oracle, `QUIT` la final, log langa `.prg` cu acelasi nume + -`_log.txt`, conform conventiei din handoff intermediar (sters). - -## 8. Schita de diff — corectia necesara peste diff-ul in lucru - -Doua variante, cu recomandare pentru B. - -**Varianta A — minima**, refoloseste `id_fact` deja prezent pe cursorul `tact` (confirmat: `tact` -vine din view-ul `vact_tot`, care are coloana `id_fact`, folosita deja in `ofacturare_comun.vc2:3776` -ca `Locate For Nvl(id_fact, 0) = lnIdFact` pe acelasi cursor `actactan`/`tact`): - -```diff ---- a/COMUN/clase/omodificari.vc2 -+++ b/COMUN/clase/omodificari.vc2 -@@ frm_modific2024.Show - This.nIdVanzare = tvanz.id_vanzare - This.nTipVanzare = tvanz.tip -- This.lArticoleReadOnly = EsteInEFactura(This.nIdVanzare) -+ This.lArticoleReadOnly = EsteInEFactura(Nvl(tact.id_fact, 0)) -``` - -Risc al variantei A: presupune ca recordul curent din `tact` (la momentul `Show()`, imediat dupa -incarcare, deci pe Top) are `id_fact` valid pentru documentul gasit — valabil in cazul normal, dar -nu la fel de robust ca precedentul din `ofacturare_comun.vc2:3775-3783`, care cauta explicit randul -cu `id_fact` potrivit inainte sa cada pe fallback (`Go Top`). - -**Varianta B — recomandata**, aduce `id_fact` chiar pe `tvanz` (randul din `VANZARI` deja gasit prin -tripletul cod/nract/serie_act/dataact), simetric cu `id_vanzare`: - -```diff ---- a/COMUN/programe/ofacturare_editare.prg -+++ b/COMUN/programe/ofacturare_editare.prg -@@ CreeazaCursorTvanzGol -- CREATE CURSOR tvanz (id_vanzare I, tip I, discount N(12,2), total_fara_tva N(14,2), total_tva N(14,2), total_cu_tva N(14,2), curs N(10,4), multiplicator N(10,4), ; -+ CREATE CURSOR tvanz (id_vanzare I, id_fact I NULL, tip I, discount N(12,2), total_fara_tva N(14,2), total_tva N(14,2), total_cu_tva N(14,2), curs N(10,4), multiplicator N(10,4), ; - in_valuta I NULL, id_valuta I NULL, nume_val C(20) NULL) -@@ IncarcaVanzareNota -- lcSql = [select v.id_vanzare, v.tip, v.discount, v.total_fara_tva, v.total_tva, v.total_cu_tva, v.curs, v.multiplicator, ] + ; -+ lcSql = [select v.id_vanzare, v.id_fact, v.tip, v.discount, v.total_fara_tva, v.total_tva, v.total_cu_tva, v.curs, v.multiplicator, ] + ; - [v.in_valuta, v.id_valuta, nv.nume_val from vanzari v left join nom_valute nv on nv.id_valuta = v.id_valuta where v.cod = ] + Transform(m.tnCod) + ; - ---- a/COMUN/clase/omodificari.vc2 -+++ b/COMUN/clase/omodificari.vc2 -@@ frm_modific2024.Show - This.nIdVanzare = tvanz.id_vanzare - This.nTipVanzare = tvanz.tip -- This.lArticoleReadOnly = EsteInEFactura(This.nIdVanzare) -+ This.lArticoleReadOnly = EsteInEFactura(Nvl(tvanz.id_fact, 0)) -``` - -Varianta B e mai robusta pentru ca foloseste `VANZARI.ID_FACT` direct — exact coloana pe care -`ANAF_EFACTURA` o are ca cheie (verificat pe schema Oracle, §1) — fara sa depinda de pozitia -curenta a cursorului `tact`. Costul: doua fisiere in loc de unul (`ofacturare_editare.prg` + -`omodificari.vc2`), plus verificarea ca `select v.id_fact` nu produce eroare Oracle daca vreun rand -vechi din `VANZARI` are `id_fact` `NULL` (coloana e deja `NULL`-abila judecand dupa restul cursorului, -`in_valuta I NULL` etc., deci nu ar trebui sa fie o problema). - -**Neschimbat, corect asa cum e**: restul diff-ului (`.When`-urile pe grid, `cmdAdaugaArticol`/ -`cmdStergeArticol.Enabled`, eticheta `lblArticoleReadOnly`) — vezi §2-5. - ---- - -## Rezumat pentru implementare - -1. Diff-ul lui `d42-efactura` (necomis la momentul acestui raport, in `COMUN\clase\omodificari.vc2` - + `COMUN\programe\ofacturare_editare.prg`) e **structural corect** — locul (§2), controalele - (§3), tiparul de grid (§4) si feedback-ul (§5) respecta cerintele Deciziei 42 corectate. -2. **Bug gasit, dovedit pe schema si date, si INCHIS**: `EsteInEFactura(This.nIdVanzare)` trebuia sa - foloseasca `id_fact`, nu `id_vanzare` — `d42-efactura` l-a gasit independent si l-a corectat cu - exact varianta B din §8 (`EsteInEFactura(Nvl(tvanz.id_fact,0))`), **verificat pe disc de acest - agent** dupa corectie. Nu mai necesita nicio actiune. -3. Gol de clarificat, nu bug: `txtDiscountArt` ramane editabil dupa eFactura — de decis cu Marius - daca discountul intra sub "articole" (§3). Confirmat si de `d42-efactura` ca gol cunoscut, in - afara scope-ului primit de la team-lead. -4. Comentariul din `omodificari.vc2:14786-14787` ("ROACONT/ROAGEST nu-l inregistreaza") e invechit - de la commit-ul `3af0089` — de actualizat cand se atinge zona (§1). -5. Teste propuse in §7, aliniate stilistic cu suita existenta — de confirmat cu `d42-efactura` daca - au fost deja scrise (mesajul lui mentioneaza "doua fisiere noi de test"). -6. `ROAGEST\Programe\roagest.prg` (linia necomisa `SET PROCEDURE TO ofacturare_editare.prg`) **nu** - apartine lui `d42-efactura` — ramane dintr-un alt bloc de lucru, de identificat separat de - team-lead. diff --git a/docs/cercetare/rec_editare_factura.md b/docs/cercetare/rec_editare_factura.md deleted file mode 100644 index 09ffad9..0000000 --- a/docs/cercetare/rec_editare_factura.md +++ /dev/null @@ -1,262 +0,0 @@ -# Cercetare: editare factura emisa (netrimisa inca in eFactura) - -Sursa: cache text `.??2` (deja la zi, negenerate acum) + `.prg` din working copy -`D:\ROA\ROAFACTURARE`. Cod Oracle (pachete `PACK_FACTURARE`, `PACK_CONTAFIN`, `PACK_DOCUMENTE`) -**NU e in working copy** — doar apelurile RPC din VFP sunt vizibile; corpul PL/SQL trebuie cautat -in schema Oracle, nu exista local. - -## 1. Fluxul de EMITERE factura - -**Punct de intrare (meniu -> procedura generica `factureaza`)**, `COMUN\programe\oproceduri_facturare.prg`: -- `facturare_contracte` (:130-134) -> `factureaza(2/6/52)` — pe baza de **contract** -- `facturare_comenzi` (:140) -> `factureaza(3)` — pe baza de **comanda** -- `facturare_avize` (:145) -> `factureaza(4)` — pe baza de **aviz** -- `emitere_aviz_clienti` / `_debitori` / `_custodie` / `_transfer` (:203-275) -> `factureaza(tip)` cu tip-uri diverse - pentru avize (comanda/lista preturi/contract/lucrare/NIR/retur) -- `copiere_factura` (:152) -> `factureaza(toFactura.Tip, toFactura)` — copiere/modificare-inainte-de-emitere - (nu e "editare dupa emitere", e o factura noua pornita din datele alteia) - -`factureaza` (`COMUN\programe\ofacturare.prg:90`) delegheaza imediat la **`factureaza2`** (acelasi fisier, -`:170`+; bucla mare pana ~`:850`). Pasi, in ordine: - -1. **Formular date antet**: `frm_date_factura` / `frm_date_aviz` / `frm_date_aviz_lucrare` (alegere dupa `tnTip`, - `ofacturare.prg:220-226`) — culege client, data, delegat, ruta, incasare etc. in obiectul `poDate`. -2. **Alocare numar document**: `poGeneratorNumere.verifica_numar(...)` / `.dezaloca_numar(...)` (`:250-260`) — - numerotare seriala pe tip document, alocata/deblocata aici. -3. **Populare cursor articole**, ramificat dupa `tnTip` (vezi punctul 6 mai jos) — cheama diverse - `pack_facturare.cursor_*` (preturi/contract/comanda/avize/gestiune/lucrare/retur/aviz_nir) — `:266-308`. -4. **Formular articole**: `frm_facturare_articole` / `frm_facturare_articole2` / `frm_avizare_lucrare` - (`COMUN\clase\ofacturare.vc2`) — user editeaza cantitati/preturi/explicatii inainte de scriere efectiva. -5. **Scriere efectiva** — metoda `do_scrie_factura` a formularului de articole - (`COMUN\clase\ofacturare.vc2:14067-14360`, dublata identic in `frm_facturare_articole2` la `:18090-18370`): - - **`pack_facturare.scrie_proforma(...)`** (:14071) — doar daca `poDate.eProforma=1`: scrie **doar in - VANZARI**, explicit comentat in cod "*nu si in contabilitate*" (:14070). - - **`pack_facturare.scrie_factura_avize(...)`** (:14103) — cand `poDate.Tip = 4` (facturare din aviz). - - **`pack_facturare.scrie_factura2(...)`** (:14130, :14158) — comenzi (tip 3/21/25/28/42/47) sau alte tipuri - ("otherwise": lista de preturi etc.). - - Toate aceste RPC-uri scriu in **VANZARI** si intorc un **cursor de verificare** (`lcCursorVerificare`) - cu liniile de nota contabila propuse: coloane `id_act, ascd, ascc (asociat cont debit/credit), id_partd, - id_partc, dataactt, datairegt, datascadt, suma` (:14184-14206). - - Daca exista randuri in cursorul de verificare (adica nu e proforma), se afiseaza **`Do Form verificare`** - (:14189) — utilizatorul poate corecta clientul (`id_partd/id_partc`) sau explicatiile (`ascd/ascc`) inainte - de a confirma nota. - - **`pack_facturare.finalizeaza_scriere_verificare(...)`** (:14247, si varianta `scrie_factura_avize_retur` - la :14219/:14282 pentru retur-uri) — **aici se scriu efectiv notele contabile si rulajele** in Oracle, - folosind eventualele corectii din formularul de verificare (`pcSirDifAcont`, `pcSirDifPart`). -6. **Alte efecte laterale**: - - Incasare/bon fiscal: `poGeneratorNumere.verifica_numar(16, poDate.nr_incasare)` (:503), `listeaza_bon_fiscal(...)` - (:528) — chitanta/incasare asociata facturii, daca `poDate.incasat <> 0`. - - Listare: `listeaza_ofacturare()` (:535). - - Stoc: pentru facturare directa din stoc exista un flux paralel, `oscrie_vanzare_din_stoc` - (`COMUN\programe\ofacturare_stoc.prg:318-412`) — apeleaza `pack_facturare.initializeaza_date_factura`, - `adauga_articol_factura_stoc`, `scrie_in_vanzari`, si la final - `update vanzari set id_fact = pack_contafin.get_idFact() where id_vanzare = ?` (:412) — leaga VANZARI - de identificatorul notei contabile (`id_fact`). - -## 2. Fluxul de EDITARE factura existenta - -Formular: `frm_facturi` (lista facturi emise), clasa `COMUN\clase\ofacturare_comun.vc2`. - -- **`frm_facturi.do_modifica`** — `ofacturare_comun.vc2:4382-4482`. - - Verifica intai daca factura e deja in `anaf_efactura` (vezi punct 5) si daca `sters=0`; altfel blocheaza - editarea (:4476-4478, mesaj "Nu puteti face modificari pe inregistrarile sterse sau facturile trimise in - eFactura!"). - - Deschide `frm_modifica_factura` (:4433) si la confirmare (`gnButon=1`) executa - **`pack_facturare.modifica_date_factura(...)`** (:4443-4460) cu parametrii: - `id_vanzare, id_ruta, id_delegat, id_agent, id_masina, dataora_exp, id_facturare, listare_detaliata, - text_aditional, tip_saft, efactura, data_act, data_scad, numar_act, serie_act`. -- **Formular `frm_modifica_factura`** (`ofacturare_comun.vc2:5096-5608`) — campurile editabile efectiv expuse in - UI sunt: ruta, delegat, agent, masina, data/ora expeditie, tip facturare (`id_facturare`), - listare detaliata, text aditional, tip SAFT, si (conditionat de `numar_act` nenul) data act/scadenta/numar - act/serie act — vezi `Init` (:5570-5584) si handlerele `chkDataAct/chkNrAct/chkSerieAct` (:5592-5606). - **NU exista pe formular campuri pentru client, data facturii, seria/numarul facturii sau articole/preturi** — - acestea nu pot fi schimbate prin acest flux de editare. -- **Confirmat: editarea NU atinge notele contabile si rulajele.** `modifica_date_factura` primeste doar - campurile enumerate mai sus (metadate document/logistica), nu recalculeaza sume, nu scrie in tabelele de - note (vezi punct 3) si nu apeleaza vreun echivalent `PACK_CONTAFIN`. Nicio referinta la `pack_contafin` in - `do_modifica`. -- Editarea explicatiei unui articol de pe factura: **`frm_modifica_articol_factura`** - (`ofacturare_comun.vc2:5023-5095`, apelat din `frm_facturi.do_modifica_explicatie` :4484-4501) — - apeleaza doar **`pack_facturare.modifica_explicatie_articol(id_vanzare_det, ...)`** (:5067) — schimba - **doar textul explicatiei**, nu cantitatea/pretul/cont-ul. Nici aici nu se ating notele contabile. - -## 3. Tabelele de note contabile si rulaje - -Nu exista schema DBC/DDL in working copy (tabelele sunt Oracle, VFP le vede via view-uri `gcs.v...`). Din -codul de stergere (`ofacturare_comun.vc2:4573-4614`) rezulta 3 seturi de date, toate filtrate pe -`an, luna, cod` (cod = `vanzari.cod`, identificatorul facturii): - -| View interogat (VFP) | Cursor local | Camp cheie randuri | Rol | -|---|---|---|---| -| `gcs.vact_tot` | `actactan` / cursor `v_act` | `id_act` (+ `id_set`, `id_fact`, `id_factd`, `id_factc`) | Note contabile (randuri debit/credit) | -| `gcs.vrul_tot` | `rul_temp` / cursor `v_rul` | `id_rul` | Rulaje | -| `gcs.vrul_obinv_tot` | `rul_temp_obinv` / cursor `v_rul_obinv` | `id_rul_obinv` | Rulaje obiecte de inventar | - -**Cheia de legatura factura -> nota**: `VANZARI.id_fact` — populat la emitere prin -`pack_contafin.get_idFact()` (`COMUN\programe\ofacturare_stoc.prg:412`) sau intors ca parametru OUT din -`scrie_factura2`/`finalizeaza_scriere_verificare` (`?@poDate.nid_vanzare` e id_vanzare, nu id_fact — id_fact -pare populat separat de pachetul Oracle). Pe partea de nota, actul (`ACT`) are coloanele `id_fact`/`id_factd`/ -`id_factc` folosite de `pack_documente.ReferinteDocumenteNota`/`ReferinteDocument` -(`COMUN\programe\odocumente.prg:8-46`) pentru a detecta referinte incrucisate intre documente (facturi ce se -storneaza/compenseaza reciproc). - -**Camp de provenienta pentru regenerare**: nu exista un camp explicit gen `sursa_id_vanzare` pe ACT/RUL vizibil -in cod VFP; legatura se face prin **`cod` (numarul facturii) + `an` + `luna`** — vezi filtrul identic in -`do_sterge` (:4573,4587,4599). Asta e mecanismul deja folosit pentru "sterge tot ce apartine facturii X": -select pe `vact_tot`/`vrul_tot`/`vrul_obinv_tot where cod = and an=... and luna=...`, apoi -sterge. **E si "reteta" pentru un eventual regenereaza = sterge + refa**, cu observatia ca id-urile -(`id_act`, `id_set`, `id_fact`, `id_factd`) trebuie citite inainte de stergere daca se doreste refacere -identica (linia 4642-4647 le extrage din `actactan` inainte de a apela stergerea). - -## 4. Operatie existenta de STORNARE / STERGERE factura (cheie pentru "regenereaza") - -Da — **`frm_facturi.do_sterge`** (`COMUN\clase\ofacturare_comun.vc2:4503-4719`) e exact mecanismul -"sterge nota + rulaje asociate unei facturi": - -- Blocheaza daca luna e inchisa (`glLunaInchisa`, :4518-4520) sau daca factura e deja stearsa (:4531-4534) sau - daca nu e din luna curenta (:4543-4546). -- **Cale proforma**: `pack_facturare.sterge_proforma(?pnIdVanzare, ?gnIdUtil)` (:4550) — nu are note, deci - stergere simpla din VANZARI. -- **Cale factura/aviz reala**: - 1. Verifica blocaj de referinte: `ReferinteDocumenteNota(pnAn, pnLuna, lnCod)` (:4565, - `COMUN\programe\odocumente.prg:8`, cheama `pack_documente.ReferinteDocumenteNota`) — daca documentul - e referentiat de incasari/plati, blocheaza stergerea (:4567 mesaj "Documentul are referinte..."). - 2. Interogheaza `vact_tot`/`vrul_tot`/`vrul_obinv_tot` (vezi punct 3) ca sa afle daca exista note/rulaje - de sters (`llRul`). - 3. Daca exista randuri in `actactan`, deschide formularul **`verificare`** (:4620-4624) pentru - confirmare vizuala a ce se sterge, apoi porneste tranzactie explicita (`SQLSetprop(...,"Transactions",2)`, - :4625) si: - - fie ruleaza `OSCRIE_IN_FISIERE(2,.F.,llRul)` + **`pack_contafin.finalizeaza_stergere_nota(luna, an, - NULL, id_set, cod, id_fact, id_factd, id_util)`** (:4642-4653) — varianta cu rulaje/fisiere multiple, - - fie apeleaza direct **`pack_facturare.sterge_factura(?pnIdVanzare, ?gnLuna, ?gnAn, ?gnIdUtil)`** - (:4689) daca nu exista note/rulaje de tratat special. - 4. Commit/rollback explicit, apoi revine pe tranzactie automata. - -Asta confirma ca infrastructura "sterge + reface" exista deja pe partea de stergere completa -(`sterge_factura` + `finalizeaza_stergere_nota`); un flux de "regenerare la editare" ar putea reutiliza aceste -doua RPC-uri Oracle inainte de a re-emite (echivalentul pasilor de la punctul 1, pasul 5) — dar acest -lant nu exista azi cablat din formularul de editare (`do_modifica`). - -## 5. `anaf_efactura.id_fact` — verificare "a fost deja trimisa in eFactura" - -- **Citire/test**: `COMUN\clase\ofacturare_comun.vc2:4428-4432`, in `frm_facturi.do_modifica`: - ``` - lcSql = [select count(*) as lneFactura from anaf_efactura where id_fact = ] + ALLTRIM(STR(poRec.id_fact)) - llSucces = goExecutor.oSelecteaza2Value(m.lcSql, @pneFactura) - If poRec.sters = 0 AND NVL(m.pnEFactura,0) = 0 - ... permite editarea ... - Else - amessagebox("Nu puteti face modificari pe inregistrarile sterse sau facturile trimise in eFactura!", 48, "Atentie") - ``` - (`pnEFactura`/`pneFactura` sunt aceeasi variabila — VFP e case-insensitive.) -- Expresia de test: **exista cel putin un rand in `ANAF_EFACTURA` cu `id_fact = VANZARI.id_fact` a facturii - curente** => considerata "trimisa" => editare blocata. -- **Scriere**: `VANZARI.id_fact` insusi e populat la emitere (`pack_contafin.get_idFact()`, - `COMUN\programe\ofacturare_stoc.prg:412`, echivalent probabil si in `scrie_factura2`/ - `finalizeaza_scriere_verificare` pe partea Oracle, nevizibil in VFP). `ANAF_EFACTURA.id_fact` e populat: - - la import de factura de achizitie (nu de emitere): `UpdateEFacturaIdFact` - (`COMUN\programe\import_efactura.prg:229`) — `update anaf_efactura set id_fact = ?pnIdFact where id = ?pnId - and NVL(id_fact,0)=0`. - - manual din formular: `frm_import_efactura.importmodifica` (`anaf_efactura.vc2:12930`) — - `UPDATE crsFacturi SET id_fact = m.pnIdFact WHERE id = m.lnIdEfactura`. - IPOTEZA: pentru facturile **emise** (nu achizitie), popularea `anaf_efactura.id_fact` la trimitere se face - probabil in clasa server `AnafeFacturaServer`/`ANAFeFactura` (`COMUN\programe\anaf_efactura.prg`, ex. - `UpdateDbFactura` :2018-2155) sau in Oracle la generarea XML — nu am gasit un UPDATE explicit VFP care - leaga `id_fact` la momentul trimiterii unei facturi emise; ar trebui verificat separat (posibil in PL/SQL). - -## 6. Cele 4 tipuri de sursa — ramificare si diferente - -Ramificarea e pe **`VANZARI.tip`** (camp numeric), vazuta in doua locuri, cu acelasi grupaj: - -`ofacturare.prg:266-308` (populare cursor articole la pornirea emiterii) si -`ofacturare.vc2:14067-14174` (`do_scrie_factura`, alegerea RPC-ului de scriere): - -| Sursa | `tip` (exemple) | Cursor populare articole | RPC scriere | -|---|---|---|---| -| **Lista de preturi** | 1,5,7,10,22,23,29,45 (si 48/49 varianta `cursor_articole_k`) | `pack_facturare.cursor_preturi(...)` | `scrie_factura2` (ramura "otherwise") | -| **Contract** | 2,6,26,52 | `pack_facturare.cursor_contract(...)` | `scrie_factura2` (ramura "otherwise") | -| **Comanda** | 3,21,25,28,42,47 | `pack_facturare.cursor_comanda(...)` | `scrie_factura2` (ramura explicita `Inlist(poDate.Tip,3,21,25,28,42,47)`, cu intrebare "Doriti sa se inchida comanda?" :14122) | -| **Aviz** | 4 | `pack_facturare.cursor_avize(...)` | `scrie_factura_avize` (ramura `poDate.Tip = 4`, cu intrebare "Doriti sa se inregistreze si avizul de retur?" :14091) | -| (alte, mentionate de completitudine) | 27 (lucrare), 30 (aviz din NIR), 41 (transfer gestiune), 8/9/24 (retur) | `cursor_lucrare`/`cursor_aviz_nir`/`cursor_gestiune`/`cursor_retur` | variante `scrie_factura_avize_retur`/`scrie_proforma` | - -**Ce difera intre ele ca note contabile/rulaje**: apelul final e mereu -`pack_facturare.finalizeaza_scriere_verificare(...)` (sau `scrie_factura_avize_retur` pentru retururi) — -adica *acelasi* punct final scrie notele, insa RPC-ul de scriere in VANZARI difera (`scrie_factura_avize` vs -`scrie_factura2`), iar la comanda/aviz apar intrebari suplimentare de inchidere document sursa (comanda -inchisa / aviz retur generat) care produc efecte laterale suplimentare pe langa nota standard. Diferentele -fine intre `scrie_factura2` si `scrie_factura_avize` (ce conturi/rulaje difera intern) sunt in PL/SQL, deci -**nu sunt vizibile in working copy**. - -## 7. Blocaje deja existente la editare - -- **Sters**: `poRec.sters = 0` obligatoriu (`ofacturare_comun.vc2:4432`). -- **Trimis in eFactura**: `anaf_efactura` are rand cu acelasi `id_fact` (punct 5), acelasi test la - `:4428-4432`. -- **Luna inchisa** — nu e verificat in `do_modifica` (doar in `do_sterge`, :4518-4520, `If glLunaInchisa Return`). - Deci editarea (schimbare ruta/delegat/etc.) **nu e blocata de luna inchisa**, doar stergerea. IPOTEZA/ - observatie pentru discutie: daca se adauga regenerare de note la editare, ar trebui adaugat si acest test. -- **Nu e din luna curenta** — verificat doar la stergere (:4543-4546: `(pnAn*12)+pnLuna <> (gnAn*12)+gnLuna`), - nu la editare. -- **Referinte incasari/plati** — verificat doar la stergere (`ReferinteDocumenteNota`, :4565-4569), nu la - editare. -- **Are chitanta/incasare** — nu am gasit un blocaj explicit *la editare*; la stergere, verificarea de mai - sus (`ReferinteDocumenteNota`) acopera implicit si incasarile asociate (comentariul din - `odocumente.prg:5-6` mentioneaza "id_fact ... pe id_factd sau id_factc"). - -## 8. Numerotare si documente legate — ce se poate schimba la editare - -- **Client**: NU se poate schimba din `frm_modifica_factura` (formularul nu are control pentru client/partener; - vezi punct 2). Singurul loc unde apare o restrictie explicita despre client e in fluxul de **verificare la - emitere** (nu la editare): `ofacturare.vc2:14198-14201` — daca diferenta de partener ar afecta un cont "41*", - `amessagebox("Nu puteti modifica clientul!",48,"Atentie") / Return .F.` — asta e in timpul emiterii - (formularul `verificare`), nu in editarea ulterioara. -- **Data facturii / seria / numarul facturii**: NU sunt pe formularul de editare. Campurile editabile - `data_act`/`numar_act`/`serie_act` (:4444-4458) sunt pentru "act" (document justificativ atasat, cu - checkbox-uri de activare `chkDataAct/chkNrAct/chkSerieAct`), NU seria/numarul facturii insesi — acelea - se aloca ireversibil la emitere prin `poGeneratorNumere` (punct 1, pasul 2). -- **Articole**: nu se pot modifica cantitati/preturi din fluxul de editare a facturii — doar explicatia unui - articol (`frm_modifica_articol_factura`, punct 2). -- **Aviz consumat / comanda / contract — flag "facturat"**: Exista camp **`facturat`** pe `COMENZI` - (folosit in UI: `ct_comenzi.actualizeaza_grid1` colora randurile cu `facturat=1`, - `COMUN\clase\ocomenzi.vc2:1144`; verificari `loRec.facturat=1` in `do_modifica`/`do_sterge` ale comenzii, - :1806, :2065 — blocheaza modificarea/stergerea comenzii deja facturate; filtru rapid `facturat=1`/`facturat=0` - in criterii, :2178). Marcarea `facturat=1` se face insa pe partea Oracle (`pack_facturare.scrie_factura2` - etc.), nu am gasit un `UPDATE comenzi SET facturat` direct in VFP — deci **nu e vizibil in working copy** - exact unde se scrie flagul, doar ca e citit/verificat pe partea VFP. Nu am gasit camp echivalent `facturat` - pe AVIZE sau CONTRACTE in codul cercetat (posibil alt nume de camp sau tot pe partea Oracle) — - IPOTEZA/necesita cautare suplimentara daca devine relevant. - ---- - -## Rezumat concluzii cheie - -1. **Emiterea** (`COMUN\programe\ofacturare.prg:90` -> `factureaza2`) scrie in VANZARI via - `pack_facturare.scrie_proforma/scrie_factura_avize/scrie_factura2`, apoi (daca nu e proforma) afiseaza - formularul `verificare` cu liniile de nota propuse si finalizeaza notele/rulajele prin - **`pack_facturare.finalizeaza_scriere_verificare`** (`COMUN\clase\ofacturare.vc2:14247`, variante retur la - :14219/:14282). Ramificarea pe cele 4 surse (lista preturi/contract/comanda/aviz) e pe `VANZARI.tip`, - vezi tabel la punctul 6. -2. **Editarea existenta** (`frm_facturi.do_modifica`, `COMUN\clase\ofacturare_comun.vc2:4382-4482`, via - `pack_facturare.modifica_date_factura`) atinge DOAR metadate logistice (ruta/delegat/agent/masina/ - dataora_exp/tip_facturare/listare_detaliata/text_aditional/tip_saft/data-numar act) — **confirmat: nu - atinge notele contabile/rulajele si nu recalculeaza sume**. Nu se pot schimba client, data/serie/numar - factura sau articole. -3. Notele contabile si rulajele stau in view-urile Oracle `vact_tot`/`vrul_tot`/`vrul_obinv_tot` - (chei `id_act`/`id_rul`/`id_rul_obinv`), legate de factura prin **`cod`+`an`+`luna`** (identic filtru - folosit si la stergere) si prin `VANZARI.id_fact` (populat de `pack_contafin.get_idFact()`). -4. **Exista deja** un flux complet de stergere-cu-note: `frm_facturi.do_sterge` - (`ofacturare_comun.vc2:4503-4719`) — foloseste `pack_facturare.sterge_factura` + - `pack_contafin.finalizeaza_stergere_nota`, cu verificare de referinte (`ReferinteDocumenteNota`) si - confirmare vizuala (formularul `verificare`). **Aceasta e infrastructura direct reutilizabila pentru un - "regenereaza = sterge + refa"** al notelor/rulajelor la editare. -5. **`anaf_efactura.id_fact`**: testul de blocare editare e `select count(*) from anaf_efactura where - id_fact = ` (`ofacturare_comun.vc2:4428-4432`) — daca exista rand, factura e - considerata trimisa si editarea e refuzata. Punctul exact unde se scrie `anaf_efactura.id_fact` la - trimiterea unei facturi **emise** nu e vizibil in VFP (ipoteza: pe partea Oracle/XML) — cel gasit in VFP e - doar pentru facturi de achizitie importate. -6. **Blocaje actuale**: editarea verifica doar `sters=0` si `netrimisa in eFactura`; NU verifica luna - inchisa, NU verifica "luna curenta", NU verifica referinte incasari/plati — aceste 3 verificari exista - doar pe fluxul de **stergere**, nu si pe cel de **editare** (`do_sterge` vs `do_modifica`, - `ofacturare_comun.vc2:4503` vs `:4382`). -7. Cod PL/SQL Oracle (`PACK_FACTURARE`, `PACK_CONTAFIN`, `PACK_DOCUMENTE`) nu e in working copy — doar - semnaturile RPC apelate din VFP sunt vizibile local. diff --git a/docs/cercetare/rec_s4_runda1.md b/docs/cercetare/rec_s4_runda1.md deleted file mode 100644 index 3f73d1a..0000000 --- a/docs/cercetare/rec_s4_runda1.md +++ /dev/null @@ -1,85 +0,0 @@ -# S4 runda 1 (PAGE3, doar afisare) — raport de inchidere, 08.08.2026 - -Runda 1 din S4 (`plan_06_s4_proiectare.md`) e **inchisa**. Diff-ul de review: -diff aplicat (sters). Istoricul complet al sesiunii de implementare/depanare: -`docs\cercetare\handoff_s4_runda1.md`, handoff intermediar (sters), -handoff intermediar (sters). - -## Ce s-a implementat - -- **`COMUN\programe\ofacturare_editare.prg`**, doua functii noi la coada fisierului: - - `IncarcaVanzareNota(tnCod, tnNract, tcSerieAct, tdDataAct)` — gaseste randul din `VANZARI` - corespunzator notei curente (filtru compus `cod+nract+serie_act+data_act`, necesar pentru ca - `VANZARI.COD` nu e unic — vezi `progres.md`), cursor `tvanz`. - - `IncarcaArticoleFactura(tnIdVanzare)` — liniile active (`sters=0`) din `VANZARI_DETALII`, cu - denumire articol/gestiune/valuta pentru afisare, cursor `tvd`. -- **`COMUN\clase\omodificari.vc2`** (`frm_modific2024`): - - `pgfArticole.PageCount = 3`, `PAGE3.Caption = "Articole factura"`. - - Grid nou `pgfArticole.PAGE3.grdArticoleFactura` — 13 coloane, `RecordSource="tvd"`, strict - **readonly** (grid + fiecare `Text1`). - - Proprietati noi pe clasa: `lAreArticoleVanzari`, `nIdVanzare`, `nTipVanzare`. - - `Load()`: placeholder `CREATE CURSOR tvd (...)` — evita dialogul nativ "Open" pe `RecordSource` - inexistent la constructia grid-ului. - - `Show()`: bloc nou care cheama `IncarcaVanzareNota`/`IncarcaArticoleFactura` si comuta - `pgfArticole.PageCount` intre 2 si 3 dupa `lAreArticoleVanzari`. - -Write-back facut, text si binar sincronizate, fidelity-check OK. - -## Ce s-a testat, si cu ce rezultat - -Suita `COMUN\utile\Teste\editare_factura\test_page3_articole.prg`, headless sub -`COMUN\utile\Teste\watchdog_vfp.ps1 -AutoDismiss`, conexiune reala `MARIUSM_AUTO`. Rulare finala -(dupa curatarea instrumentatiei de depanare): **exit code 0, 0 dialoguri, toate cazurile PASS** -(`test_page3_articole_log.txt`): - -| Caz | Verifica | Rezultat | -|---|---|---| -| `cod=1140888` (factura, tip=1) | `IncarcaVanzareNota`/`IncarcaArticoleFactura` direct: `id_vanzare=1050`, `tip=1`, 4 linii | PASS | -| `cod=1140885` (tip=-12, cu rand VANZARI) | idem: `id_vanzare=1047`, `tip=-12` (detectia merge pe orice tip, nu doar facturi) | PASS | -| `cod=1125486` (nota fara rand VANZARI) | 0 randuri gasite (caz negativ) | PASS | -| `cod=1139934` (4 randuri VANZARI cu acelasi cod) | filtrul compus gaseste exact `id_vanzare=882` pe `nract=375/SSS`, 0 randuri pe `nract=13` (fara corespondent) | PASS | -| `cod=1140888`, `frm_modific2024` instantiat (`Createobject`+`Show`, ca in `do_editare_factura`) | `PageCount=3`, `lAreArticoleVanzari=.T.`, `nIdVanzare=1050` | PASS | -| `cod=1125486`, idem | `PageCount=2`, `lAreArticoleVanzari=.F.` | PASS | - -Testul nu atinge Oracle in scriere — doar `SELECT`-uri prin `goExecutor`. - -## Ce NU e acoperit (ramane pentru runde ulterioare) - -- **Editarea in memorie a liniilor din grid** — runda 2 din S4; grid-ul e strict readonly in - aceasta runda, prin design (B.2 din plan, marcaje `_modificat`/`sters`/`id_vanzare_det=0`, nu e - inceput). -- **Inchiderea prin `do_renunt()`** — netestata, la fel ca in rundele S1-S3 (mediul minimal de - test are `ControlCount=0` pe formularul principal `frm_facturi`, deci butoanele native nu sunt - disponibile pentru a conduce fluxul complet prin UI). -- **Fluxul UI complet prin "Terminat"** — netestat; `verifica_pagecount_form` instantiaza - `frm_modific2024` direct (`Createobject`+`Show`), la fel ca `do_editare_factura`, dar nu conduce - formularul pana la inchidere. Validarea din `inainte_de_do_termin` nu e exercitata de aceasta - suita. -- Garda **referinte** pe `do_editare_factura` ramane netestata izolat (fara candidat pozitiv in - datele de test) — mostenit din rundele anterioare, nu specific rundei 1 din S4. - -## Blocaje rezolvate in aceasta sesiune (istoric, nu de reluat) - -Trei blocaje separate au impiedicat verificarea live inainte de aceasta runda de inchidere, toate -documentate pe larg in handoff intermediar (sters) si `COMUN\docs\testare-ui-vfp.md`: - -1. `DO ... WITH gnAn, gnLuna` pasa prin referinta si umbrea variabilele in procedurile apelate, - provocand dialogul nativ "View Parameter" pe un `?gnAn` din SQL passthrough — remediat cu - pasare prin valoare (`DO ... WITH (gnAn), (gnLuna)`). -2. Harnessul de test (`test_init_env_auto.prg`) incarca implicit clasa `omodificari` din copia de - lucru **ROACONT**, nu din fisierul editat — remediat cu `RELEASE CLASSLIB` + `SET CLASSLIB TO` - pe calea completa a fisierului sub test. -3. Proprietatile custom noi (`lAreArticoleVanzari`, `nIdVanzare`, `nTipVanzare`) lipseau din - `*` (intrarile `*p:`) — regula era documentata doar pentru metode - (`*m:`); aplicata acum si aici, in `COMUN\docs\flux-editare-vfp-text.md`. - -## Curatare facuta la inchidere - -- Scoase liniile de log `[BISECT]` si `[DIAG]` (`ClassLibrary`/`PEMSTATUS`) din - `test_page3_articole.prg` — erau instrumentatie de depanare, fara rol in suita finala. Corectiile - de fond (parantezele pe `(gnAn)`/`(gnLuna)`, blocul `RELEASE CLASSLIB`/`SET CLASSLIB TO`) au - ramas, cu comentariile lor. -- Sters `test_baseline_isolation.prg` (+ `.FXP` + log) — era temporar prin design; concluzia lui - ("binarul original nu are blocajul") s-a dovedit nula, testa copia ROACONT a clasei, nu fisierul - editat (vezi capcana #2 de mai sus). -- Suita rulata din nou dupa curatare — PASS pe toate cazurile (tabelul de mai sus). diff --git a/docs/cercetare/rec_s5_oracle_vanzari.md b/docs/cercetare/rec_s5_oracle_vanzari.md deleted file mode 100644 index 75b6249..0000000 --- a/docs/cercetare/rec_s5_oracle_vanzari.md +++ /dev/null @@ -1,231 +0,0 @@ -# Cercetare Oracle S5/S6/S7 (plan #6 - editare factura emisa) - -Sursa: export proaspat din `MARIUSM_AUTO@ROA_CENTRAL` (`all_source`), facut in aceasta sesiune -(08.08.2026), NU copia veche de pe disc din martie. Inainte de export s-a verificat tabela -`VERSIUNE`: ultimul script aplicat e `ff_2026_08_06_12_COMUN_VANZARI_BACKFILL.sql`, identic cu -`versiune_db.txt` din radacina proiectului (`2026_08_06_12`) - MARIUSM_AUTO e la zi. - -Numerele de linie de mai jos sunt din exportul acestei sesiuni (`PACK_FACTURARE.pck` = 17010 -linii, `PACK_CONTAFIN.pck` = 9041 linii, `PACK_DOCUMENTE.pck` = 96 linii), NU din -`ff_2026_03_23_04_COMUN_PACK_FACTURARE.sql` de pe disc, care e vechi si nu contine modificarile de -la #8. Fisierele exportate au ramas in scratchpad-ul sesiunii, nu sunt commise. - -## A. Starea reala de azi - -### A.1 `actualizeaza_vanzari` (PACK_FACTURARE.pck:16015-16025) - -Confirmat: face EXCLUSIV realinierea `cod` + `STERS=0`, nimic altceva. - -``` -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; -``` - -Important, nu era explicit in plan: al doilea `UPDATE` schimba `COD` pe ACELASI rand din `VANZARI` -(filtrat dupa vechiul cod) - `ID_VANZARE` (cheia primara) NU se schimba niciodata la editare. Asta -conteaza direct pentru sectiunea C. Comentariul inline de la `:16018` ("de modificat in caz ca il -las sa stearga manual inregistrari din VANZARI_DETALII") confirma ca extinderea era anticipata. - -### A.2 `PACK_CONTAFIN.finalizeaza_modificare_nota` / `finalizeaza_stergere_nota` - -`finalizeaza_modificare_nota` (PACK_CONTAFIN.pck:8601-8651): cheama -`pack_facturare.actualizeaza_vanzari(tnCod, lnCodNou)` DOAR daca -`SELECT COUNT(*) FROM vanzari WHERE cod = tnCod` > 0 (`:8613-8617`). Daca `cod`-ul vechi nu exista -in `vanzari` (nota nu vine dintr-o factura), pasul e sarit tacut, fara eroare - comportament corect -pentru orice alt tip de nota (ROAGEST/ROACONT). - -`finalizeaza_stergere_nota` (`:8653-8709`) e simetric: cheama `sterge_din_vanzari` doar daca -`cod`-ul exista in vanzari. Spre deosebire de `actualizeaza_vanzari`, -`sterge_din_vanzari` (PACK_FACTURARE.pck:16027-16042) cauta `ID_VANZARE` dupa `cod` si, daca nu-l -gaseste, ridica `RAISE_APPLICATION_ERROR(-20000, ... FACT-017 ...)` - dar acest caz nu poate aparea -in fluxul normal, fiindca apelul e deja gardat de count-ul de mai sus. - -### A.3-A.4 `scrie_in_vanzari` (PACK_FACTURARE.pck:13491-13956) - formula de calcul - -Extrasa integral (bloc `SELECT INTO` la `:13763-13931`, `UPDATE VANZARI` la `:13933-13949`). -Coloane denormalizate scrise: `discount_tva, valoare_achizitie, total_fara_tva, total_tva, -total_cu_tva, valval, tvaval, totval, id_valuta, curs, multiplicator, serie_incasat, nr_incasat, -suma_incasat, tip_incasat` (14 coloane). - -Sursa datelor: **`VANZARI_DETALII_TEMP`** (tabela globala temporara de sesiune, NU -`VANZARI_DETALII`) + `VANZARI_SETURI_TEMP` (pentru linii-set) + `VANZARI_CURSURI` (deja scrisa cu -`id_vanzare`-ul curent, la `:13704`, prin `scrie_cursuri`). `V_DISCOUNT_FACTURA` e parametru -explicit al procedurii (discountul de pe document), nu o coloana citita din `VANZARI`. - -Formula per-linie e deja extrasa in doua FUNCTII REUTILIZABILE, pure, cu parametri expliciti - -NU inline: `calculeaza_total_fara_tva_fact` / `calculeaza_total_tva_fact` -(PACK_FACTURARE.pck:15858-16013). Ambele ramifica explicit pe `V_PRET_CU_TVA` (`:15871`, `:15976`), -apeland `calculeaza_total_cu_tva_fact` cand flagul e 1. Deci **partea de calcul per linie e deja -partajabila** - nu trebuie reinventata. - -Ce NU e extras e blocul de AGREGARE (suma pe toate liniile documentului, tratarea liniilor din -seturi, alegerea `curs`/`multiplicator`/`id_valuta` din `vanzari_cursuri`), care e inline in -`scrie_in_vanzari` si depinde de: -- `VANZARI_DETALII_TEMP` ca sursa (nu `VANZARI_DETALII`); -- stare de sesiune pe pachet: `pack_facturare.nin_valuta`, `ndiscount_evidentiat`, - `cserie_act_incasare` / `nnumar_act_incasare` / `nsuma_incasare` / `ntip_doc_incasare` (acestea - din urma populate DOAR la emiterea unei facturi-cu-incasare combinata - nu au sens la o editare - ulterioara, vezi C); -- `pack_def.GetIdMonedaNationala()`. - -**Cine mai apeleaza `scrie_in_vanzari`**: doar 2 locuri, ambele INSERT-then-populate cu -`RETURNING ID_VANZARE`: `scrie_proforma` (`:5643`) si `finalizeaza_factura` (`:14790`). Extragerea -blocului de agregare intr-o functie interna parametrizata pe sursa (TEMP la emitere, -`VANZARI_DETALII` real la editare) e SIGURA fata de ambii apelanti, daca varianta "sursa=TEMP" -pastreaza exact comportamentul actual. - -**Risc concret de reutilizare naiva pentru S5**: coloanele `serie_incasat/nr_incasat/ -suma_incasat/tip_incasat` NU trebuie recalculate la editare - vin din stare de sesiune specifica -emiterii unei facturi-cu-incasare, fara nicio sursa persistenta din care sa fie reconstruite la o -editare ulterioara. O procedura noua care ar copia tot `UPDATE`-ul din `scrie_in_vanzari` le-ar -suprascrie cu `NULL` la fiecare editare, rupand legatura cu incasarea. Procedura propusa pentru S5 -trebuie sa scrie DOAR cele 11 coloane de totaluri/curs, nu si aceste 4. - -### A.5 Flagul `VANZARI_DETALII.PRET_CU_TVA` - -Deja parte a calculului in Oracle, nu doar in VFP: valoarea intra in agregare din -`a1.pret_cu_tva <- vd.pret_cu_tva <- VANZARI_DETALII_TEMP.PRET_CU_TVA` pentru liniile directe -(`:13883`) sau din `VANZARI_SETURI_TEMP.pret_cu_tva` pentru liniile din seturi (`:13909`), apoi -`calculeaza_total_fara_tva_fact`/`calculeaza_total_tva_fact` ramifica pe el (`:15871`, `:15976`). -Orice extragere pentru S5 trebuie sa citeasca acelasi `VANZARI_DETALII.PRET_CU_TVA` per linie (deja -persistat, cf. #7) - nu exista alta sursa de adevar pentru el. - -## B. Propunerea pentru S5 - -### Varianta A vs B - -`actualizeaza_vanzari` e apelata NECONDITIONAT de `finalizeaza_modificare_nota` pentru ORICE -editare de nota al carei `cod` exista in `vanzari` - nu doar facturi din #6, ci orice tip de -document din VANZARI editat azi prin `frm_modific2024`/`afisjurcom.do_modifica`, folosit deja de -ROAGEST/ROACONT in registrul jurnal. Daca **Varianta A** (extinderea in-place a lui -`actualizeaza_vanzari` cu recalcul de totaluri) e aleasa, recalculul ar rula automat la ORICE -editare de nota cu `cod` in vanzari, inclusiv editari care azi nu ating deloc sumele (ex. doar -`explicatie`/`cont`). Risc de regresie: **mediu-mare**, extins la toata suita ROA, nu doar la #6. - -**Varianta B** (procedura sora noua, ex. `recalculeaza_totaluri_vanzari`, apelata explicit din -`finalizeaza_modificare_nota` doar cand editarea a atins efectiv `VANZARI_DETALII`): risc **mic** - -`actualizeaza_vanzari` ramane neschimbata (zero impact pe restul aplicatiilor ROA care o folosesc -azi), noua procedura e un pas suplimentar, opt-in. - -**Recomandare: Varianta B**, motivata exclusiv de riscul de regresie asupra editarilor de note care -NU sunt facturi si trec prin acelasi `finalizeaza_modificare_nota`. - -### Schita procedurii propuse - -``` -PROCEDURE recalculeaza_totaluri_vanzari(V_ID_VANZARE IN NUMBER) IS - -- reia blocul de agregare din scrie_in_vanzari (:13763-13931), cu sursa - -- VANZARI_DETALII WHERE ID_VANZARE = V_ID_VANZARE AND STERS = 0 - -- in loc de VANZARI_DETALII_TEMP; V_DISCOUNT_FACTURA citit din VANZARI.DISCOUNT - -- (coloana existenta pe randul curent, populata la INSERT din acelasi parametru, :13682) -BEGIN - SELECT discount, ... INTO lnDiscountFactura, ... FROM vanzari WHERE id_vanzare = V_ID_VANZARE; - -- acelasi SELECT agregat ca in scrie_in_vanzari, sursa VANZARI_DETALII in loc de _TEMP - UPDATE vanzari - SET discount_tva = ..., valoare_achizitie = ..., total_fara_tva = ..., - total_tva = ..., total_cu_tva = ..., valval = ..., tvaval = ..., totval = ..., - id_valuta = ..., curs = ..., multiplicator = ... - -- FARA serie_incasat/nr_incasat/suma_incasat/tip_incasat, vezi A.4 - WHERE id_vanzare = V_ID_VANZARE; -END; -``` - -Apelata din `finalizeaza_modificare_nota` (PACK_CONTAFIN.pck:8601), dupa `actualizeaza_vanzari`. - -### Mecanismul de scriere VFP in `VANZARI_DETALII` la emitere - -Cautare in cache-ul text `COMUN` pentru `VANZARI_DETALII_TEMP` / `DETALII_TEMP` (case-insensitive): -**niciun rezultat**, inclusiv in `oscrie_in_fisiere.prg`. Tabela temp e populata probabil printr-un -helper generic de upload cursor->tabela (nume de tabela asamblat dinamic sau printr-un mecanism -care nu apare ca literal in sursa text). Nu am putut confirma static calea exacta - de cercetat -separat, pe partea VFP, inainte de proiectarea S4 (ce cursor/tabela temp foloseste editarea: -reutilizeaza `VANZARI_DETALII_TEMP` sau una noua). - -### Idempotenta si tranzactionalitate - -Fluxul de editare a notei ruleaza deja intr-o singura tranzactie manuala: -`Thisform.do_deschide_tranzactie()` (comun.vc2:2448) ... `oscrie_in_fisiere` + apelul catre -`finalizeaza_modificare_nota` (`:2484-2486`) ... `Thisform.do_inchide_tranzactie(...)` (`:2535`, -`COMMIT`/`ROLLBACK` in functie de succes). Procedura noua, apelata DIN INTERIORUL -`finalizeaza_modificare_nota`, mosteneste automat aceeasi tranzactie: la esec pe jumatate, -`ROLLBACK`-ul anuleaza tot (nota + `vanzari` + noile totaluri) - nu exista fereastra de -inconsistenta. `recalculeaza_totaluri_vanzari` propusa e ea insasi idempotenta ca `UPDATE ... WHERE -id_vanzare = :id` (poate rula de mai multe ori pe acelasi id fara efect cumulativ, spre deosebire -de un `INSERT`). - -## C. S6 - legaturile care depind de `cod` - -Verificat pe cod (nu date, dat fiind ca `MARIUSM_AUTO` are date de test): - -| Legatura | Cheie reala | Ramane valida? | -|---|---|---| -| `vanzari_coresp` | `ID_VANZARE_FACT`/`ID_VANZARE_AVIZ` (coloane confirmate din `all_tab_columns`) | DA - `actualizeaza_vanzari` nu schimba `ID_VANZARE` (vezi A.1), doar `COD` pe acelasi rand | -| `facturat` (aviz/comanda, `marcheaza_facturat`) | `ID_VANZARE`, exclusiv (PACK_FACTURARE.pck:15384-15421, foloseste doar `pack_facturare.nid_vanzare`, niciodata `cod`) | DA | -| chitanta/incasare (`SERIE_INCASAT`/`NR_INCASAT`/`SUMA_INCASAT`/`TIP_INCASAT`) | denormalizate direct pe randul `VANZARI` (nu FK), scrise o singura data la `scrie_in_vanzari` (`:13777-13780`) | DA - `actualizeaza_vanzari` nu le atinge; raman la valoarea de la emitere, ceea ce e comportamentul corect (nu se recalculeaza la editare de sume, vezi A.4/B) | -| `ReferinteDocumenteNota`/`ReferinteDocument` (PACK_DOCUMENTE.pck:25-93) | `ACT.id_factd`/`id_factc` = `id_fact`-ul documentului, filtrat `cod <> tnCod` | DA - e o VERIFICARE PRE-EDITARE (gate, apelata inainte sa se schimbe ceva), nu o legatura persistenta; nu depinde de `cod`-ul rezultat dupa editare | -| `anaf_efactura` | `ID_FACT` (coloana confirmata) | DA - `id_fact` nu se schimba la editare (by design, deja stabilit in plan) | -| `documente` | `DOCUMENTE.ID_DOC = ACT.ID_FACT` (confirmat din `MERGE INTO DOCUMENTE ... ON A.ID_DOC = B.ID_DOC` unde `B.ID_DOC` vine din `ACT_TEMP.ID_FACT`, PACK_CONTAFIN.pck:858-880) | DA - si se REACTUALIZEAZA automat (SERIE_ACT/NRACT/DATAACT) la fiecare scriere de nota, inclusiv editare, prin acelasi `MERGE` care ruleaza in `cumuleaza_note_act` | - -**Concluzie generala**: toate legaturile intre note contabile trec prin `ID_FACT` -(`ACT.id_factd/id_factc`, `DOCUMENTE.id_doc`, `ANAF_EFACTURA.id_fact`), niciodata prin `cod`. Cum -#6 pastreaza `id_fact` neschimbat by design, toate raman valide fara nicio interventie -suplimentara. Singura legatura care trece prin altceva (`ID_VANZARE`, PK-ul randului `VANZARI`) e -`vanzari_coresp`/`marcheaza_facturat`, si acesta ramane la fel neschimbat (doar `COD` se rescrie pe -acelasi rand, vezi A.1). **S6 se poate inchide ca "confirmat pe cod, fara lucru suplimentar -necesar"**, nu doar ca "de verificat". - -## D. S7 - rotunjirea la reeditare - -`verifica_total_document` (PACK_FACTURARE.pck:16073-...) insereaza o linie de corectie in -`ACT_TEMP` cand suma recalculata din `ACT_TEMP` insusi (`V_TOTFTVA_VER`/`V_TOTTVA_VER`) difera de -`pack_facturare.ntotftva`/`ntottva` (valori calculate in VFP, trecute prin stare de sesiune inainte -de scriere). `ACT_TEMP` e complet repopulat la fiecare scriere - procedura nu "tine minte" nimic -intre apeluri, in Oracle. - -**Risc identificat pe partea VFP**: la editare, `afisjurcom.do_modifica` (comun.vc2:2352-2366) -incarca in cursorul `tact` TOATE randurile curente ale notei -(`SELECT * FROM vact_tot WHERE cod = ... [AND STERS = 0] ORDER BY id_act`) - asta include si linia -de corectie inserata la salvarea anterioara (e un rand `ACT` normal, fara marcaj special care sa o -distinga). La resalvare, acest rand curge inapoi prin `oscrie_in_fisiere` in `ACT_TEMP` impreuna cu -liniile editate de utilizator, iar `verifica_total_document` ruleaza din nou pe noul `ACT_TEMP` -(care deja contine vechea corectie). - -**Nu se poate decide static** daca rezultatul e o a doua corectie suprapusa peste prima, sau daca -mecanismul e self-consistent (adica `V_TOTFTVA_VER` deja include vechea corectie in suma agregata, -iar `pack_facturare.ntotftva` recalculat de VFP coincide, deci nu se mai adauga nimic) - depinde de -cum recalculeaza VFP `ntotftva`/`ntottva` la editare, cod care nu a fost verificat in aceasta trecere -(afara de scope-ul Oracle-only al acestei cercetari). - -De notat: acest mecanism NU e nou pentru #6 - e folosit azi neschimbat de orice editare de nota -prin `do_modifica` (ROAGEST/ROACONT registru jurnal), pe orice tip de document, de ani de zile. -Daca ar acumula corectii sistematic la editari repetate, ar fi deja o problema cunoscuta pe editari -non-factura. Asta scade riscul, dar nu-l elimina pentru cazul specific facturii, unde S4/S5 -introduc o cale noua de a schimba cantitati/preturi care nu exista azi in `do_modifica` generic (pe -notele generice azi de regula nu se schimba baza de calcul TVA in acelasi fel). - -**Raspuns**: nu se poate decide din cod - testul deja propus in plan (S7: trei editari consecutive, -verifica nr. de linii de corectie in `ACT`) e calea corecta si suficienta; nu exista scurtatura -statica. - -## E. Intrebari deschise - -1. **Semnatura procedurii noi**: apel neconditionat din `finalizeaza_modificare_nota` oricand - `cod`-ul exista in `vanzari` (simplu, cost mic - un `SELECT`+`UPDATE` in plus si pe editari care - nu ating articolele), sau flag explicit `tnAtinsArticole` trecut din VFP (evita lucru inutil, dar - risc de flag uitat/gresit)? **Recomandare: apel neconditionat** - cost neglijabil, elimina o - clasa de bug. -2. **Discountul de document la editare**: `V_DISCOUNT_FACTURA` la emitere e parametru explicit din - VFP; planul S4/S4b nu mentioneaza editarea discountului de pe document, doar cantitate/pret/ - `pret_cu_tva` pe linie. La S5, discountul se citeste din `VANZARI.DISCOUNT` (neschimbat) - de - confirmat cu Marius ca asta e comportamentul dorit (discountul de document ramane needitabil in - #6). -3. **`VALVAL`/`TVAVAL`/`TOTVAL`** (totaluri in valuta): planul S5 mentioneaza explicit doar - `total_fara_tva`/`total_tva`/`total_cu_tva`/`valoare_achizitie`/`discount_tva`. Fac parte din - acelasi bloc de agregare din `scrie_in_vanzari` - **recomandare: le includem in recalcul**, cost - suplimentar zero, evita inconsistenta pe documentele in valuta straina. -4. **Mecanismul de populare al `VANZARI_DETALII_TEMP`** la emitere n-a putut fi gasit static in - cache-ul text `COMUN` (cautare literal, fara rezultate) - necesita cercetare VFP separata inainte - de a proiecta calea exacta de scriere pentru editarea din S4 (aceeasi tabela temp, sau una noua). -5. **S7**: raspunsul cere test dinamic (3 editari consecutive), nu poate fi confirmat static - de - pastrat explicit in scope-ul S8, nu doar "de verificat" generic in text. diff --git a/docs/cercetare/retur_si_lista_preturi.md b/docs/cercetare/retur_si_lista_preturi.md deleted file mode 100644 index f571faa..0000000 --- a/docs/cercetare/retur_si_lista_preturi.md +++ /dev/null @@ -1,198 +0,0 @@ -# Cercetare: retur din facturi anterioare + adaugare din lista de preturi pe document cu sursa - -## A. Returul din facturi anterioare — cum functioneaza AZI - -### A.1 `But_retur` — lant de apel - -Definitia clasei butonului (`COMUN\clase\cmd_butoane.vc2:324-338`): -``` -DEFINE CLASS but_retur AS buton OF "_cmd_base.vcx" - caction = do_retur - ... - Visible = .F. -ENDDEFINE -``` -Butonul e instantiat pe `frm_facturare_articole` la `COMUN\clase\ofacturare.vc2:11221-11227` (`Visible=.F.` implicit) si devine vizibil doar conditionat, in `Init`, la `ofacturare.vc2:15122-15127`: -``` -Case Inlist(poDate.tip, 1, 5, 7, 10) && modificare v 2.0.56 : am adaugat 10 - && facturare pe baza de lista de preturi - ... - This.but_retur.Visible = .T. && pot sa fac retur de articole intr-o factura de vanzare -``` -Deci butonul e vizibil **doar pe documente de tip 1/5/7/10** (facturare pe baza de lista de preturi), nu pe facturi de retur propriu-zise (8/9) si nu pe documente cu sursa contract/comanda. - -`caction=do_retur` -> click apeleaza `frm_facturare_articole.do_retur` (`ofacturare.vc2:13963-13965`): -``` -PROCEDURE do_retur - Thisform.do_adauga_articol(.F., .F., .T.) -ENDPROC -``` -adica `do_adauga_articol(tlImplicit=.F., tlContract=.F., tlRetur=.T.)` (`frm_facturare_articole.do_adauga_articol`, `ofacturare.vc2:12813-12823`). - -Pas cu pas in `do_adauga_articol` (`ofacturare.vc2:12813-12900`), ramura `tlRetur`: -1. Utilizatorul selecteaza un articol (din `crsarticole`, lista de preturi) si o cantitate — `lnCantitate = poArticol.cantitate`. -2. `Thisform.do_verifica_articol(...)` valideaza cantitatea. -3. La `ofacturare.vc2:12881-12890` (case `Otherwise`, articol gestionabil): -``` -OTHERWISE - lnListaIdOld = poDate.listaid - IF m.tlRetur - loCauta = caut_facturi_multiple_client_articol(poDate.id_client, poDate.in_valuta, poDate.id_valuta, .F., poArticol.id_articol) - If m.gnButon = 1 and !Empty(Nvl(loCauta.id_vanzare, 0)) - poDate.listaid = Alltrim(Str(loCauta.id_vanzare)) - * ID_ARTICOL:ID_VANZARE,ID_ARTICOL:ID_VANZARE - thisform.cListaIdArticoleRetur = thisform.cListaIdArticoleRetur + IIF(!EMPTY(thisform.cListaIdArticoleRetur), ',', '') + ALLTRIM(STR(poArticol.id_articol)) + ':' + poDate.listaid - Endif - ENDIF - llSucces = Thisform.do_alege_stoc(poArticol.id_articol, lnCantitate, poArticol.denumire, tlImplicit, poArticol.pretftva, poArticol.discount_unitar, loGrid, m.tlRetur) -``` -4. `do_alege_stoc(..., tlRetur)` (`ofacturare.vc2:13200-13250`) foloseste ramura retur pentru a construi cursorul de stoc/gestiuni de unde se alege articolul de returnat: -``` -If Inlist(poDate.tip,8,9,24) Or m.tlRetur && factura retur lei, factura retur valuta, aviz retur sau factura normala cu retur de articole - lcSql = [{call pack_facturare.cursor_gestiuni_articol_retur(...)}] -``` -5. Rezultatul e afisat printr-un formular `frm_articol_gest_factura` (`Createobject("frm_articol_gest_factura",tnCantitate,tlImplicit,tlRetur)` la `ofacturare.vc2:13353` si `:13361/:13375`), unde utilizatorul alege gestiunea/seria si cantitatea de returnat. -6. La salvare, `do_scrie_articole` (`ofacturare.vc2:13967-13979`) trimite `poDate.listaid` (perechile `id_articol:id_vanzare` construite la pasul 3) catre `pack_facturare.initializeaza_date_factura`. - -Concluzie: **nu exista un singur dialog "alege facturile sursa" pentru tot documentul** — selectia facturii sursa se face **per articol**, in momentul adaugarii fiecarei linii, printr-un dialog generic de cautare. - -### A.2 Dialogul si criteriile de cautare a facturii sursa - -Formularul e generic — functia `cauta_alfa` (dialog de picker standard ROA), invocata din `caut_facturi_multiple_client_articol` (`COMUN\programe\oproceduri_facturare.prg:2124-2159`): -``` -Function caut_facturi_multiple_client_articol - Lparameters tnIdPart, tnInValuta, tnIdValuta, tlFacturiMultiple, tnIdArticol - ... - lcSelect = [select a.serie_act,a.numar_act,a.data_act,a.dataora,a.id_vanzare ] + ; - [from vanzari a join vanzari_detalii b on a.id_vanzare = b.id_vanzare and b.id_articol = ?pnIdArticol ] - - lcFiltruOriginal = [a.sters=0 and a.tip in (1,2,3,4,5,6,10,-1,-2,-3,-4,-11) ] + gcCondSucursala + ; - lcFiltruPart + lcFiltruValuta - ... - lcTitlu = [Alegeti factura] && (tlFacturiMultiple = .F. la apelul din do_adauga_articol) - loCauta = cauta_alfa(lcSelect, lcFiltru, lcSchema, lcOrder, lcColoane, lcTitlu, lcTitluColoane, lcNumeProc, llToateIreg, lcFiltruOriginal, lcPrimaColoana, lnPornire, lnTipReturn, lcIdColumn) -``` -Coloane afisate: `Serie act, Numar act, Data, Data inreg.` Criterii de filtrare in interogare: -- **articolul curent** (`b.id_articol = ?pnIdArticol`, obligatoriu — cautarea se face per-articol, nu la nivel de document); -- **client** (`a.id_part = ?pnIdPart`, doar daca `tnIdPart` nu e gol — `lcFiltruPart`, `oproceduri_facturare.prg:2133`); -- **valuta** (`a.in_valuta`/`b.id_valuta`, `lcFiltruValuta`, `:2134`); -- **tip document sursa restrictionat la vanzari normale**: `a.tip in (1,2,3,4,5,6,10,-1,-2,-3,-4,-11)` — exclude explicit tipurile de retur (8,9,24), deci nu se poate face retur dintr-un retur. -- **numar factura / perioada**: nu exista filtru SQL dedicat in interogare; `cauta_alfa` e dialogul generic de cautare alfa/browse al suitei (permite filtrare interactiva pe coloanele afisate, dar asta e comportament generic al dialogului, nu parametru specific — neverificat mecanismul intern de filtrare al `cauta_alfa`). - -Rezultatul (`loCauta.id_vanzare`) e folosit ca `poDate.listaid` pentru articolul curent. - -### A.3 Tipuri de document retur - -Confirmat in cod, cu **3 valori**, nu doar 8/9 (comentariu explicit la `ofacturare.vc2:12862`): -``` -* 8 = Retur lei, 9 = retur valuta sau factura de vanzare normala, dar cu retur de articole, 24 = aviz retur -``` -Folosite consistent in tot `frm_facturare_articole`: `Inlist(poDate.tip, 8, 9, 24)` la `:13043`, `:13230`, `:13728`, `:14751`; `Inlist(poDate.tip, 8, 9)` separat la `:15236` (UI caption) si `:9718` (eliminare camp curs valutar). Pe `frm_facturare_articole2` (varianta "2" a formularului) aceleasi tipuri apar fara 24 in unele locuri (`:17531`: doar 8,9 — de verificat daca e omisiune sau intentionat, neclar din cod). - -Semnul cantitatilor pe retur — la `do_alege_stoc` (`ofacturare.vc2:13273, :13300, :13309`): -``` -Select Iif(poDate.tip=41,-1,1)*Sum(cantitate) As cantitate, ... -... -Where a.cantitate - Iif(Inlist(poDate.tip,8,9,24),(-1),1) * Nvl(b.cantitate,0) > 0 -``` -adica pentru tip 8/9/24 semnul cantitatii deja pe factura curenta se scade cu semn opus (`-1` in loc de `1`), consistent cu inregistrari de retur. - -**Ziua de curs eliminata pe retur** — confirmat, dar linia corecta e in `COMUN\clase\ofacturare.vc2:9717-9722` (`frm_date_factura.Init`), NU in `ofacturare_comun.vc2` (fisierul citat in plan nu are randul respectiv — `ofacturare_comun.vc2` are doar 7432 de linii): -``` -*!* modificare v 2.0.56 -If Inlist(poDate.tip, 8, 9) - lnHeight = lnHeight - .clb_zi_curs.Height - laPozitii(.clb_zi_curs.TabIndex, 2) = 1 - .RemoveObject('clb_zi_curs') -Endif -*!* modificare v 2.0.56 ^ -``` -Corolar in acelasi `Init` de `frm_facturare_articole` (`ofacturare.vc2:15098`): `Iif(!Inlist(poDate.tip, 8, 9), "(" + Dtoc(poDate.zi_curs) + ")", "")` — eticheta de curs valutar nu afiseaza data pe retur. - -### A.4 Retur partial - -Da, e posibil. Coloana de cantitate din `grd_articole`, pe tip 8/9, isi schimba explicit titlul in "cantitate maxima de returnat" (`ofacturare.vc2:15236-15239`): -``` -Case Inlist(poDate.tip, 8, 9) && factura retur lei/valuta - This.grd_articole.cCantitate.header1.Caption = [Cant. max. de returnat] - This.cmesaj_cantitate = [Nu se mai poate face retur pentru acest articol!] -``` -si analog pentru aviz retur (tip 24) la `:15242-15245`. Cantitatea introdusa de utilizator e validata in `do_verifica_articol` (`:14743-14754`): -``` -llRetur = Inlist(poDate.tip,8,9,24) -Do Case - Case poArticol.gestionabil = 0 Or gnScadereStoc = 1 Or m.llFacturareFaraStoc - llReturn = .T. - Case (tnCantitate >= 0 And m.llRetur ) Or ... - lcMesaj = Alltrim(Thisform.cmesaj_cantitate) - amessagebox(lcMesaj,0+48,"Atentie") - llReturn = .F. -``` -adica sistemul respinge doar cazul in care cantitatea ramasa dupa retur ar iesi din domeniul valid (`tnCantitate >= 0` fiind conditia de eroare pe retur, unde cantitatile de retur sunt negative) — deci utilizatorul poate introduce orice cantitate <= maximul returnabil calculat de `cursor_gestiuni_articol_retur`, inclusiv mai mica (retur partial). Formularul de alegere (`frm_articol_gest_factura`, ramura `tlRetur`, `:13353-13385`) permite editarea cantitatii inainte de confirmare. - -### A.5 Legatura stocata linie-de-retur -> linie originala - -**Nu am gasit o coloana pe linie in `VANZARI_DETALII`** (de tip "id linie sursa") in codul VFP text disponibil. Ce exista, e legatura **la nivel de antet de document**, prin parametri output ai apelurilor Oracle: -- `poDate.nid_vanzare` / `poDate.nid_vanzare_retur` — populati ca parametri `?@...` in `pack_facturare.scrie_factura_avize_retur(...)` (`ofacturare.vc2:14448`, `:14509`) si `pack_facturare.finalizeaza_scriere_verificare(...)` (`:14472`); resetati implicit la `oDateFactura.Reset` (`COMUN\programe\ofacturare_comun.prg:569`: `.nid_vanzare_retur = 9999999999`). -- La nivel de sesiune VFP (nu persistat ca coloana confirmata), `thisform.cListaIdArticoleRetur` acumuleaza perechi `ID_ARTICOL:ID_VANZARE` (`ofacturare.vc2:12888`) trimise ca `poDate.listaid` catre `pack_facturare.initializeaza_date_factura` (`:13977-13979`) — asta e mecanismul prin care Oracle *primeste* info despre factura sursa per articol, dar daca acesta persista intr-o coloana dedicata pe `VANZARI_DETALII` (ex. id linie/document sursa) **nu se poate confirma din sursa VFP** — pachetul `pack_facturare` e in Oracle, in afara acestui repo. Cercetare existenta in `docs\cercetare\rec_cale_vanzari_detalii.md` si `rec_s5_oracle_vanzari.md` (interogari live pe schema Oracle, 08.08.2026) nu mentioneaza vreo coloana de tip `ID_DET_SURSA`/`ID_VANZARE_RETUR` pe `VANZARI_DETALII`. **Neverificat.** - ---- - -## B. Adaugarea din lista de preturi pe document cu sursa (comanda/contract) - -### A6/B6. Ce se vede azi pe cod - -`frm_facturare_articole.Init`, `Do Case` pe `poDate.tip` (`ofacturare.vc2:15108-15248`) configureaza UI-ul per tip. Puncte relevante: - -- **Contract (tip 2, 6)** — `ofacturare.vc2:15129-15143`: -``` -Case Inlist(poDate.tip, 2, 6) - && facturare pe baza de contract - This.lb_titlu_alb_b121.Caption = [FACTURA PE CTR. ] + Alltrim(poDate.descriere) - This.grd_articole.RemoveObject('cSerie') - && articole din lista de preturi - This.grd_articole.cCantitate.header1.Caption = [Cantitate in stoc] - This.cmesaj_cantitate = [Acest articol nu este pe stoc!] -``` -Comentariul "articole din lista de preturi" e explicit: `grd_articole` (cu butonul `But_urmator1`/`do_urmator`->`do_adauga_articol()`, cursorul `crsarticole`) **ramane activ si populat cu lista de preturi** chiar si pe document de tip contract. In paralel, `grd_contracte` (buton `But_urmator2`/`do_urmator2`->`do_adauga_articol(.F.,.T.)`, cursorul `crsarticole1`) afiseaza articolele restrictionate la contract. - -Cei doi cursori se populeaza distinct: pentru `tnTip in (2,26,6,52)`, apelul e `pack_facturare.cursor_contract(...)` (`COMUN\programe\ofacturare.prg:283-290`), care umple atat `crsarticole` cat si (separat) `crsarticole1` (confirmat prin `Create Cursor crscontracte ... Select Distinct ... From (lcCursor + [1])`, `ofacturare.prg:433-440` — `lcCursor+'1'` = `crsarticole1` deja exista la acel punct). - -- **Comanda (tip 3)** — `ofacturare.vc2:15144-15150`: -``` -Case poDate.tip = 3 - && facturare pe baza de comanda - This.lb_titlu_alb_b121.Caption = [FACTURA LA COMANDA ] + Alltrim(poDate.descriere) - This.grd_articole.RemoveObject('cSerie') - This.grd_articole.cCantitate.header1.Caption = [Cantitate comandata] - This.cmesaj_cantitate = [A fost facturata intreaga cantitate comandata pentru acest articol!] - This.but_urmator_tot1.Visible = .T. -``` -Aici `grd_articole`/`crsarticole` e populat direct de `pack_facturare.cursor_comanda(...)` (`ofacturare.prg:292-293`, tip in `(3,21,25,28,42,47)`) — adica **doar articolele comenzii**, nu lista de preturi libera. Nu exista al doilea cursor (`crsarticole1` nu se creeaza pentru tip 3, doar pentru `2,6,26` conform `ofacturare.prg:433`), deci in `Init` la `ofacturare.vc2:15294-15324`: -``` -If !Used('crsarticole1') - ... - Thisform.RemoveObject('grd_contracte') - Thisform.RemoveObject('but_urmator2') -Endif -``` -grila si butonul secundar se elimina complet. - -Exceptie: la **copiere de factura/aviz** (`poDate.lCopiere`), indiferent de tip, codul adauga explicit lista de preturi peste cursorul existent (`ofacturare.prg:454-473`): -``` -IF m.llCopiere - * Adaug in lista articolelor cursorul cu lista de preturi ca sa pot adauga si alte articole in factura copiata/modificata - lcSqlCursor = [{call pack_facturare.cursor_preturi(...)}] - ... - SELECT crsArticole - APPEND FROM DBF(m.lcCursorTemp) -ENDIF -``` - -### B7. Unde e restrictia - -Nu e un `Visible`/`Enabled` pe buton (butonul `But_urmator1`/lista-de-preturi exista si e vizibil pentru toate tipurile, la fel `grd_articole`) si nu e o validare la salvare — **restrictia e la nivelul continutului cursorului de articole disponibile** (`crsarticole`), decis de care procedura Oracle il populeaza in `factureaza()`/`factureaza2()` (`ofacturare.prg:266-308`, `Do Case` pe `tnTip`): -- tip **1,5,7,10** (lista de preturi) si **2,6,26,52** (contract) -> `cursor_preturi`/`cursor_contract`: `crsarticole` contine lista de preturi completa (nerestrictionata la sursa) => **azi se poate deja adauga liber din lista de preturi pe un document contract**. -- tip **3,21,25,28,42,47** (comanda) -> `cursor_comanda`: `crsarticole` contine **doar** articolele comenzii => **azi NU se poate** adauga o linie libera din lista de preturi pe un document comanda, decat prin copiere de document (`llCopiere`, ramura separata mai sus). - -Concluzie B: distinctia plan-ului ("comanda sau contract, tipurile 2,6,26,52") nu e uniforma in codul actual — **contractul (2,6,26,52) are deja acces liber la lista de preturi** (al doilea grid `grd_contracte` e doar un adaos, nu o restrictie), in timp ce **comanda (3) e restrictionata strict la continutul comenzii**, fara optiune de adaugare libera in fluxul normal (doar la copiere de document). diff --git a/docs/cercetare/roaauto_articole_lista_preturi.md b/docs/cercetare/roaauto_articole_lista_preturi.md deleted file mode 100644 index 0b9a6e0..0000000 --- a/docs/cercetare/roaauto_articole_lista_preturi.md +++ /dev/null @@ -1,163 +0,0 @@ -# ROAAUTO — adaugarea de articole reale din nomenclator pe langa MANOPERA/MATERIALE - -## Corectie fata de `roaauto_facturi.md` - -Raportul anterior a descris facturarea ROAAUTO ca fiind formata **doar** din linii sintetice -(`-100000`..`-100008`). E incomplet: exista un mecanism separat, **"Alte servicii"**, care adauga -linii cu `id_articol` real (pozitiv), din nomenclatorul de articole, in plus fata de liniile -sintetice MANOPERA/MATERIALE. Mecanismul e cablat **direct in `factureaza_deviz`**, deci face -parte din acelasi flux descris anterior, nu dintr-un flux alternativ. - -## 1. Unde se adauga articole din nomenclator - -Formular: `frm_incasare_finala` (`D:\ROA\ROAAUTO\Clase\oviz_devize.vc2:5435-7143`), aratat de -`frm_emitere_facturi.do_factureaza_final` (`oviz_devize.vc2:2569` `ofrmincasare=Createobject('frm_incasare_finala',...)`) -si `do_factureaza_final_part` (`:3539`) — adica **la momentul emiterii facturii finale**, nu pe -formularul de deviz propriu-zis (`oviz_devize` nu are buton de adaugare articol la crearea -devizului). - -Metoda: `frm_incasare_finala.do_adauga` (`oviz_devize.vc2:6539-6580`): -``` -lcXMLArticole = cauta_nom_articole([in_stoc = 0 and in_crm = 1 and id_articol not between -100008 and -100000]) -... -Replace id_articol With loArticol.id_articol,denumire With loArticol.denumire,um With loArticol.um,codmat With loArticol.codmat,; - id_lucrare With lnIdLucrare,nrord With lcNrOrd -``` -Cauta in `vnom_articole_toate` (nomenclatorul de articole, cursor `crstmpart`) prin -`cauta_nom_articole` (`COMUN\programe\ocautare.prg:1781`), filtrat la articole **fara gestiune** -(`in_stoc = 0`) si **vizibile in CRM** (`in_crm = 1`), excluzand explicit id-urile sintetice -(`not between -100008 and -100000`). Articolele alese se adauga in grila `grd_altele` -(`oviz_devize.vc2:6060`, coloane `cDenumire/cNrord/cCantitate/cPretFTva/cValoareftva`, -`oviz_devize.vc2:6121-6222`), sustinuta de cursorul `crsalteserv`, creat inainte de afisarea -formularului in `frm_emitere_facturi.do_factureaza_final` (`oviz_devize.vc2:2553-2555`) si -`do_factureaza_final_part` (`:3522-3524`): -``` -Create Cursor crsalteserv(id_articol N(14) Not Null,denumire c(100) Not Null,codmat c(50) Null,um c(10) Null,; - id_lucrare N(14) Not Null,nrord c(100) Not Null,; - cantitate N(14,gnPCant) Default 1,pretftva N(14,gnPPretV) Default 0,valoareftva N(14,gnPc) Default 0) -``` -**Important**: `do_adauga` NU completeaza `pretftva`/`cantitate` — raman la valorile implicite -(1, respectiv 0). Pretul si cantitatea se **introduc manual** de operator in grila -(`grd_altele.cPretFTva.Text1.LostFocus` / `cCantitate.Text1.LostFocus`, ambele apeland -`Thisform.do_modifica_alteserv()` care recalculeaza `valoareftva = cantitate * pretftva`, -`oviz_devize.vc2:6692-6698,7064-7070`). Nu exista o cautare/preluare automata de pret pentru -aceste articole in acest flux (vezi punctul 6). - -## 2. Cum ajung in factura — linii separate, NU cumulate - -In `factureaza_deviz` (`Programe\oproceduri_devize.prg`), liniile sintetice MANOPERA/MATERIALE/ -DISCOUNT/AVANS se insereaza in cursorul `crsdeviz` (`:936-983`) cu id-uri fixe (`-100000`.. -`-100008`). La pasul de cumulare pentru optiunea "articol cumulat REPARATII AUTO" (`:999-1012`) -se cumuleaza **doar** liniile cu `id_articol IN (-100003,-100000,-100002,-100001)` — liniile din -`crsalteserv` nu sunt incluse in acest `INLIST`, deci nu pot fi absorbite in cumulare. - -Liniile din `crsalteserv` se insereaza **separat**, dupa cumulare, cu `id_articol` real: -``` -If Used('crsalteserv') - Insert Into (lcCursorDeviz)(id_articol,denumire,explicatie,cantitate,um,id_jtva_coloana,proc_Tvav,taxcode,pret,discount_unitar) ; - Select id_articol,denumire,"",cantitate,um,CAST(lnIdJTvaColoana as N(6) null) as id_jtva_coloana,...,pretftva,0 From crsalteserv Where !Deleted() -Endif -``` -(`oproceduri_devize.prg:1036-1041`) — deci id-ul real din `nom_articole` trece nemodificat. -De acolo, fiecare rand ajunge in `crsvanztemp` (`:1190-1206`) si e scris in Oracle prin -`pack_facturare.adauga_articol_factura_deviz` (`:1240-1257`, apelul SQL construit cu -`Alltrim(Str(poArticol.id_articol))` la `:1241`), care insereaza direct in -`VANZARI_DETALII_TEMP` cu `ID_ARTICOL = V_ID_ARTICOL` (pozitiv, real) — vezi implementarea in -`ff_2026_08_06_10_COMUN_PACK_FACTURARE.sql:4675-4745`. Confirmare: **articolele din "Alte -servicii" raman linii proprii, cu id_articol real, si NU se aduna in liniile sintetice -MANOPERA/MATERIALE.** - -## 3. Gestiunea - -`id_gestiune` in cursorul `lcCursorDeviz` are `DEFAULT null` (`oproceduri_devize.prg:885`) si -**niciun** `INSERT INTO` — nici cel al liniilor sintetice, nici cel al liniilor din -`crsalteserv` (`:1040-1041`) — completeaza aceasta coloana. La transferul in `crsvanztemp` -(`:1201-1206`) coloana `id_gestiune` (definita fara valoare implicita explicita la `:1190`) nu e -in lista `SELECT`, deci ramane 0 (implicit numeric), transmis ca literal `0` catre -`adauga_articol_factura_deviz` (`:1255`, `Alltrim(Str(poArticol.id_gestiune))`), care il scrie ca -atare in `VANZARI_DETALII_TEMP.ID_GESTIUNE` — nu NULL, ci **0**. Nu exista descarcare de gestiune -pentru aceste linii; e consistent cu faptul ca articolele oferite spre alegere sunt filtrate -explicit `in_stoc = 0` (articole/servicii fara gestiune de stoc). **Concluzie: liniile din "Alte -servicii" NU au gestiune reala completata**, exact ca liniile sintetice — diferenta e doar -`id_articol`. - -## 4. Stergere / modificare inainte de facturare - -- Stergere: `frm_incasare_finala.do_sterge` (`oviz_devize.vc2:6730-6741`), cu confirmare - (`amessagebox("Sunteti sigur ca doriti sa stergeti articolul "+lcArticol+" de pe factura?",4+32,...)`), - marcheaza randul `Delete` in cursorul bufferat (exclus apoi prin `Where !Deleted()` la punctul 2). -- Modificare cantitate/pret: direct in grila (`grd_altele.cCantitate`/`cPretFTva`), vezi punctul 1. -Toate aceste actiuni sunt posibile **doar cat timp `frm_incasare_finala` e deschis, inainte de -`do_termin`** (validarea din `inainte_de_do_termin`, `:6749-6801`, verifica duplicate si valori 0, -dar nu mai permite reintrarea in formular dupa emitere). - -## 5. Dupa facturare — cmd_modifica1 si cai alternative - -Confirmat, cu corectie de context: `Thisform.cmd_modifica1.Enabled=Iif(Empty(nrfact),.T.,.F.)` -se afla in `frm_emitere_facturi.grdcomenzi.AfterRowColChange` (`oviz_devize.vc2:4536`), nu intr-o -metoda separata — se re-evalueaza la fiecare schimbare de rand in grila de comenzi, dezactivand -butonul "Modifica" de indata ce comanda are `nrfact` completat. Verificat suplimentar: -- `frm_emitere_facturi.verifica_stornare` (`oviz_devize.vc2:4513-4514`) e **gol** (`PROCEDURE - verifica_stornare / ENDPROC`, fara cod) — nu exista storno de comanda/deviz in acest formular. -- Singurul "storno" gasit in `oviz_devize.vc2` e `do_storneaza_avans` (`:4256`, folosit din - `do_factureaza_final`/`do_factureaza_final_part` la `:2345`/`:3305`) — priveste exclusiv - reversarea unui **avans incasat**, nu redeschiderea unei facturi/deviz deja emise si nu permite - adaugarea de articole pe o factura emisa. -- Am gasit un mecanism generic, in afara `oviz_devize.vc2`, care poate **sterge complet** o - factura deja emisa: `frm_facturi.do_sterge` (`COMUN\clase\ofacturare_comun.vc2:4649-4689`), - care apeleaza `pack_facturare.sterge_factura(?pnIdVanzare,...)`. In Oracle, - `sterge_factura` (`ff_2026_08_06_10_COMUN_PACK_FACTURARE.sql:5441-...`) face un **soft-delete** - (`UPDATE VANZARI SET STERS = 1 ...`, `:5505-5508`), cu verificari ca nu existe deja facturi/avize - de retur legate. Aceasta e o stergere a intregii facturi (delete + re-emitere ulterioara), - **nu** o adaugare/modificare de articole pe factura existenta. - **Neverificat**: nu am putut confirma, in bugetul acestei cercetari, daca `frm_facturi` e - cablat intr-un meniu ROAAUTO (grep-ul pentru instantieri `frm_facturi` in fisiere specifice - ROAAUTO — in afara de `COMUN\` — nu a gasit potriviri de cod, doar metadate ascunse) si nici - daca stergerea Oracle reseteaza `nrfact`/`facturat` pe comanda ROAAUTO de origine (ar necesita - urmarirea `pack_auto`, in afara scriptului analizat). Deci **nu pot confirma sau infirma cu - dovada directa** o cale completa "sterge factura -> reface devizul cu articole diferite" din - interiorul ROAAUTO; pot confirma doar ca un asemenea instrument de stergere exista generic in - suita si ca in `oviz_devize.vc2` nu exista niciun cod care sa modifice/adauge articole pe o - factura deja emisa. -- **Concluzie pe intrebarea centrala**: constatarea anterioara ramane valabila si dupa aceasta - cercetare — `oviz_devize.vc2` insusi nu ofera nicio cale de a adauga/modifica articole pe o - comanda/deviz cu `nrfact` completat; singura cale gasita spre o factura deja emisa e stergerea - totala (soft-delete) prin ecranul generic COMUN, nu o editare in linie. - -## 6. Nomenclator vs. lista de preturi — doua surse, cea folosita de ROAAUTO e nomenclatorul brut - -Da, exista doua surse diferite in suita ROA: -- **Nomenclatorul brut de articole**: `vnom_articole_toate` / `nom_articole`, interogat prin - `cauta_nom_articole` (`COMUN\programe\ocautare.prg:1781-1823`, `SELECT ... FROM ] + gcS + [.vnom_articole_toate a` - la `:1799-1803`). **Acesta e cel folosit de `frm_incasare_finala.do_adauga`** — fara pret - atasat automat (pretul se tasteaza manual, vezi punctul 1). -- **Motorul de preturi negociate/politici de pret**: `pack_facturare.cursor_preturi`, apelat din - `COMUN\programe\ofacturare.prg` in `factureaza`/`factureaza2` (liniile 276/281/464/755/760) — - parte a mecanismului generic COMUN de facturare/avizare pe baza de "lista de preturi" - (`factureaza(22)`, `factureaza(29)`, `factureaza(23)`, `factureaza(41)` — comentate explicit - `pe baza de lista de preturi` in `COMUN\programe\oproceduri_facturare.prg:205,237,268`). - **Nu am gasit niciun apel** al acestui `factureaza`/`factureaza2` generic in fisierele - specifice ROAAUTO (`oviz_devize.vc2`, `oproceduri_devize.prg`) — grep-urile pentru - `cursor_preturi` si pentru `factureaza(` in afara de `COMUN\programe\ofacturare.prg` nu au - gasit potriviri in cod ROAAUTO. **Concluzie**: fluxul propriu ROAAUTO de facturare deviz - (`factureaza_deviz`) foloseste exclusiv nomenclatorul brut, fara calcul automat de pret - negociat; motorul `cursor_preturi` pare sa apartina unui flux de facturare/avizare generic, - separat, neapelat din codul ROAAUTO analizat. - -## Neverificat / limitari - -- Wiring-ul meniu -> `frm_facturi` in ROAAUTO (punctul 5). -- Efectul `sterge_factura` asupra coloanelor `nrfact`/`facturat` pe comanda ROAAUTO (necesita - `pack_auto`, neinclus in scriptul Oracle analizat). -- Numele exact al butonului/caption care declanseaza `frm_incasare_finala.do_adauga` — nu am - gasit in `oviz_devize.vc2` un `Click` explicit legat de `do_adauga`; e foarte probabil declansat - de un buton generic `cmd_adauga` prin conventia clasei de baza `_frm_base` (folosita si de alte - metode `do_xxx`/`cmd_xxx` din acelasi fisier), dar nu am gasit dovada directa a legaturii. -- Indexul ROAAUTO a fost reconstruit local (`_symbols.tsv`, permis explicit); nu a fost rulat - `git_sync.ps1` si nu s-a generat text nou din binare in ROAAUTO. - -## Nota - -Raportul a fost scris initial (din greseala) la calea gresita `D:\ROA\ROAAUTO\docs\cercetare\...` -in loc de `D:\ROA\ROAFACTURARE\docs\cercetare\...`. Acest fisier e livrarea corecta, la calea -ceruta. diff --git a/docs/cercetare/roaauto_facturi.md b/docs/cercetare/roaauto_facturi.md deleted file mode 100644 index 65a1235..0000000 --- a/docs/cercetare/roaauto_facturi.md +++ /dev/null @@ -1,167 +0,0 @@ -# Cercetare: facturile emise din ROAAUTO si relatia lor cu VANZARI_DETALII - -Scop: pentru `plan_13_unificare_formular_facturare.md` — poate formularul unificat sa acopere si -facturile emise din ROAAUTO (piese + manopera pe deviz), stiind ca ele "se pot modifica ulterior"? - -## Metoda si ce am putut verifica - -ROAAUTO (`D:\ROA\ROAAUTO`) e un working copy **migrat** (are deja `.vc2`/`.sc2` in-tree, ca -ROAFACTURARE), deci am cautat direct in text, **fara sa rulez `git_sync.ps1`** (interzis explicit). -Nu pot garanta ca acel text e sincron 100% cu binarul curent (nu l-am regenerat) — daca vreo linie -citata pare sa nu corespunda comportamentului live, motivul cel mai probabil e text neactualizat, nu -o citire gresita. - -`D:\ROA\_vfp_textcache\roaauto\_symbols.tsv` exista dar indexeaza **doar `.prg`** (1924 intrari, -toate `.prg`; niciun `.vc2/.sc2`) — probabil generat inainte de migrarea in-tree. Nu m-am bazat pe -el; am cautat direct cu Grep in `.vc2`/`.prg` din arbore. - -Pentru partea Oracle am gasit sursa curenta a pachetului `PACK_FACTURARE` (COMUN, shared) in -`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_06_10_COMUN_PACK_FACTURARE.sql` (17020 linii, -cel mai recent script din istoricul de migrari) — folosita ca sursa de adevar pentru semnaturile -procedurilor. - -## 1. Formularul/programul care emite facturi in ROAAUTO - -Nu e un formular separat de facturare, ci un **modul apelat din formularul de devize/comenzi** -(`frm_...` in `oviz_devize.vc2`, clasa cu `cmd_factavans`/`cmd_factfinal`). Logica de facturare -propriu-zisa e in `Programe/oproceduri_devize.prg`: - -- `Procedure factureaza_deviz` — `D:\ROA\ROAAUTO\Programe\oproceduri_devize.prg:846` (semnatura la - linia 847: `Lparameters tnIdComanda,tcNrOrd,tcNrInmat,tcDenop,tnMultiple,...`). -- Apelata din formular in `D:\ROA\ROAAUTO\Clase\oviz_devize.vc2` (liniile 1834, 2201, 3146, 4056, - 4456) prin butoanele de facturare avans/final. -- Exista si `Procedure relisteaza_factura_deviz` — - `D:\ROA\ROAAUTO\Programe\oproceduri_devize.prg:1516` — pentru **re-listarea** (re-tiparirea) unei - facturi deja emise, nu pentru editarea ei. - -## 2. Cum scrie in Oracle: aceleasi proceduri, cale comuna cu ROAFACTURARE - -Da — **acelasi pachet Oracle `PACK_FACTURARE`** (COMUN, shared cu ROAFACTURARE), apelat prin -`goExecutor.oExecute`, cu doi apeluri specifice pe langa cele generice: - -- `pack_facturare.initializeaza_date_factura(...)` — - `D:\ROA\ROAAUTO\Programe\oproceduri_devize.prg:1211` -- `pack_facturare.adauga_articol_factura_deviz(...)` (varianta **_deviz** a - `adauga_articol_factura`, cu parametri expliciti de pret/gestiune/valuta in loc sa caute articolul - din nomenclator) — `D:\ROA\ROAAUTO\Programe\oproceduri_devize.prg:1240`, in bucla `Scan`/`Endscan` - peste cursorul `crsvanztemp`. -- `oscrie_in_fisiere(0,.F.,.T.)` — `D:\ROA\ROAAUTO\Programe\oproceduri_devize.prg:1269`. -- `pack_facturare.scrie_incasari(...)` — linia 1294 (doar daca exista incasare la emitere). -- `pack_facturare.scrie_in_vanzari(0, id_delegat, id_masina, id_facturare, ..., @poDate.nid_vanzare)` - — linia 1302, **urmata in acelasi `lcSql`** de `pack_auto.actualizeaza_deviz(...)` (linia 1310) — - un apel specific ROAAUTO, care actualizeaza starea devizului/comenzii dupa facturare (marcheaza - `RUL`, seteaza `nrfact` etc., nu am citit corpul lui `pack_auto` — pachet separat, nu l-am cautat). -- Corpul `adauga_articol_factura_deviz` (spec+body in `ff_2026_08_06_10_COMUN_PACK_FACTURARE.sql:468` - si `:4675-4745`) face un simplu `INSERT INTO VANZARI_DETALII_TEMP (...)` — **exact tabela de - staging** pe care o foloseste si facturarea normala din ROAFACTURARE (`adauga_articol_factura`, - linia 549 din spec, insereaza in acelasi `VANZARI_DETALII_TEMP`). -- **Premisa lui Marius e confirmata, cu o nuanta**: ROAAUTO nu scrie direct in `VANZARI_DETALII`; ca - si fluxul normal, trece prin `VANZARI_DETALII_TEMP`, iar transferul definitiv catre - `VANZARI`/`VANZARI_DETALII` se face in `pack_facturare.scrie_in_vanzari` - (`ff_2026_08_06_10_COMUN_PACK_FACTURARE.sql:915-923` spec, body la `:13497-13962`) — **acelasi - punct final** pe care il foloseste orice alta facturare ROA (avize, comenzi, contracte). Nu exista - cale Oracle proprie ROAAUTO pentru scrierea liniilor de factura. - -## 3. Tipul de document: `tip = -12` - -`factureaza_deviz` construieste obiectul de date cu -`poDate = Createobject("oDateFactura",lnIdSet,-12)` — -`D:\ROA\ROAAUTO\Programe\oproceduri_devize.prg:922` — al doilea parametru e `tip`. Clasa -`oDateFactura` nu am gasit-o definita ca text (nu apare `DEFINE CLASS oDateFactura` in nicio sursa -text din ROAAUTO sau din COMUN al oricarui proiect cautat) — probabil traieste intr-un `.vcx` inca -neconvertit sau e generata dinamic; **neverificat** unde anume e clasa, dar e clar shared (folosita -si in `D:\ROA\ROAFACTURARE\COMUN\clase\ofacturare_comun.vc2`, ex. liniile 3894/4075/7090, cu acelasi -tipar `Createobject("oDateFactura", tip1, tip2)`). - -**`tip = -12` e deja cunoscut in ROAFACTURARE** — nu ca un cod nou, ci ca ceva deja intalnit si -documentat in cercetarea recenta de regresie a editarii facturii (proiectul S4, -`docs\progres.md:339`, `:1142`; `docs\cercetare\rec_datoria6_baza_regresie.md:93`; -`docs\cercetare\rec_s4_runda1.md:38`; `COMUN\docs\cercetare\rec_view_articole_vanzare.md:108`), pe un rand -real din baza de test `MARIUSM_AUTO`: `cod=1140885` -> `id_vanzare=1047`, `tip=-12`. Acolo e descris -explicit: **"nu e factura, e alt tip de document"** (handoff intermediar (sters)) si decizia produsului -(decizia 19) e ca detectia liniilor de articole "merge pe orice tip, nu doar facturi" — adica -`IncarcaArticoleFactura`/`IncarcaVanzareNota` din `COMUN\programe\ofacturare_editare.prg` (mentionate -in `rec_datoria6_baza_regresie.md:83-84`) nu filtreaza dupa `tip=1`, deci **vad si randurile -`tip=-12`** deja, fara cod suplimentar. - -## 4. Ce e specific fata de o factura obisnuita - -- **Camp de legatura cu masina**: `V_ID_MASINA` e transmis catre `pack_facturare.scrie_in_vanzari` - (`oproceduri_devize.prg:1304`). Nu e un camp exclusiv ROAAUTO — `ID_MASINA` e coloana standard pe - `VANZARI`, cunoscuta si de fluxurile generice din pachet: `modifica_date_factura` are parametrul - `V_ID_MASINA IN NUMBER` (`ff_2026_08_06_10_COMUN_PACK_FACTURARE.sql:946`), la fel - `scrie_factura2`, `scrie_factura_avize`, `scrie_factura_avize_retur` (liniile 618-747 din acelasi - fisier). Deci nu e o bariera de schema — ROAFACTURARE stie deja de `ID_MASINA`. -- **Liniile facturii nu sunt articole individuale din nomenclator, ci sume cumulate cu - pseudo-articole (id negative)**: `factureaza_deviz` agrega toate sumele din contul analitic al - devizului (`actactan`) pe categorii si insereaza cate o linie sintetica per categorie — - `-100000` MANOPERA (`oproceduri_devize.prg:965-967`), `-100001` DISCOUNT MANOPERA (`:972`), - `-100003` MATERIALE (`:947-949`), `-100005` AVANS (`:936-937`), `-100006` STORNARE AVANS - (`:1026-1027`), `-100007`/`-100008` INSPECTIE TEHNICA / SPALARE AUTO (`:979-982`). Optional, daca - `gnAUTOIdArticolReparatii` e setat, toate liniile de mai sus se **cumuleaza intr-o singura linie** - cu un articol real din nomenclator ("REPARATII AUTO", `:989-1004`). Confirmat pe date reale in - `COMUN\docs\cercetare\rec_view_articole_vanzare.md:108-109`: `id_vanzare=1047` (`tip=-12`) are doar - **2 randuri** in `VANZARI_DETALII`, cu `id_gestiune`/`nume_gestiune` NULL (linii nestocate, - netinute in gestiune). Deci pe factura nu apar piesele individual — apar totaluri - MATERIALE/MANOPERA (cu exceptia cazului cumulat, unde apare un singur articol generic). -- **`pack_auto.actualizeaza_deviz(...)`** e chemat imediat dupa `scrie_in_vanzari`, in acelasi - bloc SQL (`oproceduri_devize.prg:1310`) — leaga vanzarea nou creata inapoi de deviz/comanda - (tabelele `DEV_*`/comenzi din ROAAUTO). Corpul lui `pack_auto` nu a fost citit (pachet separat, - in afara ariei `PACK_FACTURARE` cautate) — **neverificat** ce tabele ROAAUTO scrie exact. -- **`DEV_TIP_DEVIZ`** (garantie/postgarantie/regie, `oproceduri_devize.prg:1797`) e o clasificare - **diferita**, a devizului insusi, nu are legatura cu `VANZARI.tip=-12`. - -## 5. Cum se modifica azi o astfel de factura - -- **Din ROAAUTO**: nu exista editare a liniilor dupa facturare. Formularul de devize/comanda - (`oviz_devize.vc2`) **dezactiveaza explicit butonul de modificare** dupa ce comanda are numar de - factura: `Thisform.cmd_modifica1.Enabled=Iif(Empty(nrfact),.T.,.F.)` — - `D:\ROA\ROAAUTO\Clase\oviz_devize.vc2:4536`. Singura actiune disponibila post-facturare gasita in - cod e re-listarea (`relisteaza_factura_deviz`, `oproceduri_devize.prg:1516`), care doar reciteste - antetul/liniile din `fact_vfacturi`/`fact_vfacturi_detalii` pentru reprintare, fara sa scrie - nimic. N-am gasit niciun apel din ROAAUTO catre `pack_facturare.modifica_date_factura` (grep fara - rezultate in tot arborele ROAAUTO) — nici macar antetul (delegat/masina/text aditional) nu pare - editabil din ROAAUTO dupa emitere. **Neverificat**: n-am acoperit tot `oviz_devize.vc2` (fisier de - >8800 linii) linie cu linie, doar zonele gasite prin grep pe termenii relevanti — nu exclud un alt - punct de editare cu alt nume de metoda. -- **Din ROAFACTURARE**: exista deja, in lucru (proiectul S4), un editor de factura care - **citeste** liniile oricarui `tip` (inclusiv `-12`) din `VANZARI_DETALII` — funcțiile - `IncarcaVanzareNota`/`IncarcaArticoleFactura` in `COMUN\programe\ofacturare_editare.prg` (adaugate - conform `docs\cercetare\handoff_s4_runda1.md:79-81`, verificate pe `cod=1140885` -> `id_vanzare=1047`, - `tip=-12`, `docs\cercetare\rec_datoria6_baza_regresie.md:93`) si formularul `frm_modific2024` - (`COMUN\clase\omodificari.vc2`), cu un grid nou `grdArticoleFactura` pe pagina 3 "Articole factura". - **Insa acest grid e in prezent READ-ONLY**: "grid nou `grdArticoleFactura` (...), `ReadOnly` la - nivel de grid **si** pe fiecare `Text1`" (`docs\cercetare\handoff_s4_runda1.md:80-82`) — deci azi - se poate **vizualiza**, nu edita, de aici (nici adaugare, nici stergere de linii). - -## 6. Se poate adauga azi o linie libera / din lista de preturi pe o astfel de factura? - -Pe cod, **nu, din nicaieri, azi**: - -- Din ROAAUTO: butonul de modificare a devizului e dezactivat dupa facturare - (`oviz_devize.vc2:4536`), iar singura cale de scriere gasita (`factureaza_deviz`) ruleaza o - singura data la emitere; nu exista un `adauga_articol_...` apelabil ulterior pe o vanzare deja - scrisa. Structura insasi a liniilor (sume cumulate pe pseudo-articole negative, sectiunea 4) e - diferita de o linie normala de factura cu articol real din lista de preturi — un eventual "adauga - articol" ar trebui sa lucreze langa niste linii care nu reprezinta articole reale. -- Din ROAFACTURARE: editorul nou (`frm_modific2024`) **vede** liniile (orice `tip`, deci si cele - venite din ROAAUTO), dar gridul e read-only — nu exista azi cod de adaugare/scriere pe acest grid. - Nu am gasit alt formular ROAFACTURARE (`frm_facturi` clasic) care sa editeze articolele unei - vanzari cu `tip=-12` — cautarea `ROAAUTO` in `D:\ROA\ROAFACTURARE` (in afara de `COMUN`) nu da - potriviri de cod, doar mentiuni in `docs\` (progres.md, cercetare); `Grep` pe `ROAAUTO` in - `D:\ROA\COMUNROA` a expirat (arbore prea mare) si nu a fost reincercat — **neverificat** daca - `COMUNROA` (copia partajata separata de `COMUN` din ROAAUTO/ROAFACTURARE) are vreo referinta - directa la ROAAUTO. - -## Concluzie pentru planul de unificare - -Premisa lui Marius se confirma pe cod: facturile ROAAUTO ajung, prin acelasi `PACK_FACTURARE` -shared si acelasi `VANZARI_DETALII_TEMP` -> `VANZARI_DETALII`, in aceeasi tabela pe care o citeste -ROAFACTURARE — deci un formular unificat **le poate vedea** fara cod special de recunoastere a -sursei (dovada: editorul S4 le vede deja, testat pe date reale). Ce lipseste nu e recunoasterea, ci -**capacitatea de scriere**: azi nimic, in niciun produs, nu poate adauga o linie pe o vanzare -`tip=-12` — gridul nou din ROAFACTURARE e deliberat read-only, iar ROAAUTO isi blocheaza propriul -formular dupa facturare. Particularitatea reala de gestionat e ca liniile existente nu sunt articole -individuale, ci sume cumulate MATERIALE/MANOPERA pe pseudo-articole cu `id_articol` negativ — orice -UI care adauga articole din lista de preturi "langa" ele trebuie sa decida cum coexista cu randuri -care nu au `id_gestiune`/nu corespund unui articol real. diff --git a/docs/cercetare/rute_scriere_antet.md b/docs/cercetare/rute_scriere_antet.md deleted file mode 100644 index 7798250..0000000 --- a/docs/cercetare/rute_scriere_antet.md +++ /dev/null @@ -1,249 +0,0 @@ -# Rute de scriere pe antetul unui document DEJA EMIS (VANZARI/ACT/RUL) — Grupele A/B/C - -Cercetare read-only, fara nicio modificare de cod. Sfera: `pack_facturare.modifica_date_factura` -(14 campuri, deja documentat in `modifica_date_factura_parametri.md`) e confirmata ca **singura** -cale directa pentru serie/numar/data act/data scadenta + ruta/delegat/masina/agent/dataora_exp/ -id_facturare/listare_detaliata/text_aditional/tip_saft/efactura. Intrebarea de aici: exista alte -cai, pentru campurile din grupele A/B/C, in afara drumului de emitere initiala. - -**Rezumat** - -| Camp | Verdict | Nota | -|---|---|---| -| A. ID_VENCHELT | NU (cu o rezerva) | vezi pct. 1.4 — posibil editabil direct in grid, neverificat pana la capat | -| A. ID_SECTIE | **DA** (nou, activ) | prin editare directa a notei contabile, pct. 1.3-1.4 | -| A. ID_RESPONSABIL | **DA** (nou, activ) | idem | -| A. ID_LUCRARE | **DA** (nou, activ) | idem | -| B. ID_FDOC (tip document) | NU | cautat, negasit — pct. 2 | -| B. ID_VALUTA | NU | cautat, negasit | -| B. Zi curs | NU | cautat, negasit | -| B. ID_CLIENT | NU | cautat, negasit | -| B. sursa/"altele" | NU | cautat, negasit | -| B. gestiune sursa | NU | cautat, negasit | -| B. politica de preturi | NU | cautat, negasit | -| C. opt_incasat/casa/serie chit/nr chit/incasat/POS | NU direct, PARTIAL indirect | scrise doar la emitere (pct. 3.1); posibila cale indirecta prin editarea notei contabile daca incasarea e pe acelasi `cod` (pct. 3.2, neconfirmat pana la capat) | - ---- - -## 1. Grupul A — analiticele de antet (ID_VENCHELT, ID_SECTIE, ID_RESPONSABIL, ID_LUCRARE) - -### 1.1 Unde traiesc si cum se scriu la emitere - -Coloanele sunt pe `ACT` (`ACT.ID_VENCHELT%TYPE`, `ACT.ID_SECTIE%TYPE`, `ACT.ID_RESPONSABIL%TYPE`, -declarate asa in pachetul Oracle), nu pe `VANZARI`. La emitere, `frm_date_factura`/`frm_date_aviz` -(`COMUN\clase\ofacturare.vc2:9041-9706` / `:6900-7516`, controale `Ct_clb_venchelt`/`Ct_clb_sectie`/ -`Ct_clb_responsabil`/`Ct_clb_lucrare`) populeaza variabilele de sesiune Oracle -`pack_facturare.nid_venchelt` / `nid_sectie_stoc` / `nid_responsabil` / `nid_lucrare` -(`ff_2026_08_06_10_COMUN_PACK_FACTURARE.sql:1884-1887`), care se scriu o singura data, uniform pe -tot documentul, in `INSERT INTO ACT_TEMP` la contabilizare (comentariul explicit de la linia 1286 -din `PACK_CONTAFIN.pck`: *"Am completat ID_RESPONSABIL la instructiunile INSERT INTO ACT_TEMP"*). -Nicaieri in cele doua fisiere Oracle centrale nu exista `UPDATE ACT SET ID_VENCHELT|ID_SECTIE| -ID_RESPONSABIL|ID_LUCRARE = ...` (grep combinat `UPDATE ACT ... SET` + fiecare coloana, zero -potriviri, atat in pachetul curent `ff_2026_08_06_10_COMUN_PACK_FACTURARE.sql` cat si in -`PACK_CONTAFIN.pck`, `PACK_UPDATE.pck`, `PACK_MIGRARE.pck` din `COMUN\docs`). - -`frm_date_factura`/`frm_date_aviz` sunt instantiate **doar** din interiorul `ofacturare.vc2` -insusi (nu am gasit niciun `Createobject` catre ele in afara clasei) — sunt exclusiv wizard-ul de -la emitere, nu apar pe niciun flux de editare ulterioara. - -**Concluzie partiala**: `modifica_date_factura` (calea documentata deja) NU atinge aceste 4 -campuri, si nu exista niciun `UPDATE` Oracle separat pe ele. **Pana aici, verdictul era NU.** - -### 1.2 Descoperire care schimba raspunsul: `frm_facturi.do_editare_factura` (functionalitate noua) - -Commit-ul cel mai recent din branch (`97d1613`, *"#6 editare factura emisa: omodificari.vcx intra -in proiect"*) a adaugat exact ce lipsea. Exista deja o cercetare anterioara in acest depozit -(`docs\cercetare\rec_modific2024.md`, scrisa inainte de acest commit) care documenteaza clasa -`frm_modific2024` (`COMUN\clase\omodificari.vc2:6375+`, editor generic de nota contabila, -partajat cu ROAGEST) si concluziona explicit: *"clasa exista deja, dar codul apelant din -ROAFACTURARE NU exista inca — trebuie scris"*. **Acel gol a fost umplut** — am gasit apelantul, -cablat si activ: - -`COMUN\clase\ofacturare_comun.vc2`, metoda `frm_facturi.do_editare_factura` (~linia 3690-3869): - -- Garda: blocheaza daca factura a fost trimisa in eFactura (`EsteInEFactura`, `:3764-3767`). -- `IncarcaCursoareModificareNota(lnCod, pnAn, pnLuna, .F.)` (helper din `ofacturare_editare.prg`, - citit dar nemodificat) incarca nota contabila completa (`vact_tot`/`vrul_tot`/`vrul_obinv_tot` - filtrate pe `cod`) in cursoarele READWRITE `actactan`/`tact`/`rul_temp`/`trul`/ - `rul_temp_obinv`/`trul_obinv` (`:3769`). -- `Omodif = Createobject([frm_modific2024], lnIdSet)` + `Omodif.Show()` (`:3796-3797`) — deschide - editorul modal pe cursorul `tact` deja incarcat cu nota facturii curente. -- Daca userul apasa Terminat (`buton = 1`, `:3799-3833`): deschide tranzactie manuala, sterge nota - veche (`OSCRIE_IN_FISIERE(2,.T.,.T.)`), rescrie din cursoarele editate (`tact`->`actactan`, - `trul`->`RUL_TEMP`, `trul_obinv`->`RUL_TEMP_OBINV`, cu `id_util`/`sters=0` noi), - `OSCRIE_IN_FISIERE(0,.T.,.T.)` (scriere noua), apoi - `pack_contafin.finalizeaza_modificare_nota(...)`, commit/rollback. - -Acest `finalizeaza_modificare_nota` (`PACK_CONTAFIN.pck:8601-8651`, citat deja in -`rec_modific2024.md`) **resincronizeaza `vanzari`** dupa editare: `SELECT COUNT(*) FROM vanzari -WHERE cod = tnCod; IF > 0 THEN pack_facturare.actualizeaza_vanzari(tnCod, lnCodNou); END IF;`. - -### 1.3 Ce e efectiv editabil in `tact`/`trul` — verificat prin `do_modifica` - -`frm_modific2024.do_modifica` (`omodificari.vc2:13836-13957`) e mecanismul generic prin care un -camp din grid deschide un dialog de cautare (`cauta_alfa`) si apoi face `REPLACE` pe cursorul -corespunzator. Pentru `trul`/`trul_obinv` (nivel LINIE de nota, nu antet unic), `CASE` explicit -gestioneaza: - -``` -CASE m.lcControl = 'nrord' -> replace nrord with loCauta.nrord, id_lucrare with loCauta.id_lucrare -CASE m.lcControl = 'sectie' -> replace sectie with loCauta.sectie, id_valuta with loCauta.id_sectie (!) -CASE m.lcControl = 'nresp' -> replace nresp with loCauta.nume, id_responsabil with loCauta.id_responsabil -```//omodificari.vc2:13934-13943 - -Deci **ID_LUCRARE si ID_RESPONSABIL sunt editabile explicit**, pe cursorul `trul` (nivel de linie -RUL), prin acest mecanism, azi, din UI, prin `do_editare_factura`. Linia `sectie` are ce pare a fi -un bug preexistent (`replace ... id_valuta with loCauta.id_sectie` in loc de `id_sectie` — cod -existent, nu l-am atins, doar il semnalez ca observatie relevanta pentru evaluarea "functioneaza -sau nu in practica"): daca bug-ul e real, campul afisat `sectie` (text) se schimba, dar coloana -`id_sectie` propriu-zisa risca sa NU se actualizeze corect (se suprascrie `id_valuta` in loc). -Nu am testat comportamentul, doar am citit codul. - -**ID_VENCHELT**: NU exista un caz `CASE m.lcControl = 'venchelt'` (sau `dst_chlt`) in -`do_modifica` pentru `trul`/`trul_obinv` — cautat explicit in tot procedeul (13836-13957), zero -potriviri. Insa Grid1 al clasei (proprietatile de coloana, in afara metodelor) are o coloana cu -`ControlSource = "dst_chlt"` (`omodificari.vc2:753, 2862, 7393` — a treia aparitie e in intervalul -propriu al clasei `frm_modific2024`), deci explicatia venit/cheltuiala **e afisata** in grid. Daca -acea coloana permite editare directa de text (nu prin popup de cautare) sau daca exista un alt -handler (buton dedicat, dublu-click) care leaga `id_venchelt`, nu am verificat pana la capat — vezi -"Necunoscute ramase". **Raspuns pentru ID_VENCHELT: NU confirmat ca ruta activa, dar cu o rezerva -neinchisa** (grid-ul afiseaza coloana, mecanismul de editare exact al ei nu a fost trasat complet). - -### 1.4 Nivel LINIE vs. nivel ANTET - -O nuanta importanta: campurile din grupul A, la nivel Oracle, traiesc pe `ACT` (fiecare linie de -nota isi are propriile `ID_VENCHELT`/`ID_SECTIE`/`ID_RESPONSABIL`/`ID_LUCRARE`), desi la EMITERE -se scriu uniform (aceeasi valoare pe toate liniile unui document, din variabilele de sesiune). -Editarea prin `frm_modific2024` e la nivel de LINIE (fiecare rand din `trul` se editeaza separat), -nu o singura bifa de antet — deci userul poate, teoretic, sa lase valori diferite pe linii diferite -ale aceleiasi facturi dupa editare, lucru care nu se putea intampla la emiterea initiala (uniforma). -Asta conteaza pentru orice raportare care presupune "un singur ID_SECTIE per factura". - -**Verdict grup A**: **DA** pentru `ID_SECTIE`/`ID_RESPONSABIL`/`ID_LUCRARE` (cale noua, activa, -`frm_facturi.do_editare_factura` -> `frm_modific2024` -> `OSCRIE_IN_FISIERE` -> -`pack_contafin.finalizeaza_modificare_nota` -> Oracle `ACT`/`RUL`, cu resincronizare in `VANZARI`). -**NU confirmat** pentru `ID_VENCHELT` prin acelasi mecanism generic (`do_modifica` nu are caz -pentru el), dar cu rezerva de la 1.3 neinchisa complet. - ---- - -## 2. Grupul B — identitate/sursa (fdoc, valuta, zi curs, client, altele, gestiune sursa, -politica preturi) - -Controalele (`Ct_clb_fdoc`, `Ct_clb_valuta`, `Clb_zi_curs`, `Ct_clb_nume_client`, `Ct_clb_altele`, -`Ct_clb_gestiune_init`, `Ct_clb_politici_preturi`) au fost gasite **exclusiv** in metodele proprii -ale `frm_date_aviz`/`frm_date_factura`/`frm_date_aviz_lucrare` din `ofacturare.vc2` — acelasi -wizard de emitere de la grupul A (cautare `vfp_symbols.ps1 -Grep` pe toate cele 7 nume de -control, in tot proiectul indexat: 316 fisiere, zero potriviri in afara `ofacturare.vc2`). - -Cautat in Oracle (pachetul curent + `PACK_CONTAFIN.pck` + `PACK_UPDATE.pck` + `PACK_MIGRARE.pck`): -`UPDATE VANZARI ... SET (ID_FDOC|ID_VALUTA|ID_CLIENT|ID_PART|ID_GESTIN|ID_POL) = ...` si -`UPDATE ACT ... SET` idem — zero potriviri (grep multiline, fereastra 400 caractere dupa -`UPDATE`). Toate cele 13 aparitii ale `UPDATE VANZARI` din pachetul curent au fost inspectate -individual (`sterge_factura`, `sterge_proforma`, `marcheaza_facturat`, -`scrie_corespondente_vanzari`, plus `modifica_date_factura`) — niciuna nu atinge aceste coloane -(ating `STERS`/`FACTURAT`/`ID_UTILFACT`/`DATA_FACTURAT`/`AVIZE`/`COD`/serie-numar-data-scadenta). - -`frm_modific2024`/`do_editare_factura` (descoperirea de la grupul A) nu ajuta aici: cursoarele pe -care le editeaza (`tact`/`trul`/`trul_obinv`) sunt nota contabila (conturi, sume, gestiuni de -STOC, TVA) — nu contin fdoc/valuta document/client/politica de preturi ale facturii; acestea sunt -proprietati ale `VANZARI`, populate o singura data la `scrie_factura2`/`scrie_in_vanzari`, in afara -oricarui flux gasit de editare ulterioara. - -**Verdict grup B: NU**, pentru toate cele 7 campuri — cautat si negasit, in ambele straturi -(Oracle: pachetul de facturare curent + cele 3 pachete conexe; VFP: toate clasele indexate de -`vfp_symbols.ps1`, 1128 clase / 9281 metode / 2461 proceduri). - ---- - -## 3. Grupul C — incasarea - -### 3.1 Unde se scrie la emitere - -Controalele din `frm_alte_date` (`COMUN\clase\ferestre_cere_date.vc2:2219+`) — `opt_incasat`, -`Cb_casa`, `Clb_serie_chit`, `Clb_nrchit`, `Clb_incasat`, `chkPOS` — au `ControlSource` pe -proprietati ale obiectului `poDate` (`poDate.nIncasatPos`, si prin cod pe `poDate.incasat`, -`poDate.nr_incasare`, `poDate.ntip_incasare`, `poDate.id_casa`, `poDate.serie_chit`), NU pe un -cursor legat direct de tabel. `frm_alte_date` e instantiat exclusiv din -`frm_facturare_articole(2).inainte_de_do_termin` si din doua fluxuri de listare -(`ofacturare.prg:listare_protocol`, `ofacturare_stoc.prg:oscrie_vanzare_din_stoc`, -`oproceduri_listari.prg:listare_protocol`) — toate parti ale fluxului de EMITERE, niciuna de -editare ulterioara. - -La `do_scrie_factura` (`ofacturare.vc2:14260-14389`, ambele forme `frm_facturare_articole` si -`frm_facturare_articole2`), valorile din `poDate` sunt asamblate intr-un string -`lcListaIncasare` (format `tip|suma|id_casa;...`) si trimise ca parametru la -`pack_facturare.scrie_factura2` / `scrie_factura_avize` / `scrie_proforma` (apel RPC unic, la -emitere). In Oracle, `scrie_factura2` cheama intern `pack_facturare.scrie_incasari` (linii 6204, -7034 din pachetul curent), care parseaza lista si cheama `scrie_incasare2` per linie -(`:13096-13168` -> `:13170+`) — aceasta insereaza o **nota contabila noua in `ACT_TEMP`** -(coloane vazute: `ACT.SUMA`, `ACT.ID_PARTD` pentru casa/banca, `ACT.ASCC`/`SCD`/`ASCD`, -`ACT_TEMP.PAYMENTCODE`), adica incasarea devine ea insasi o linie de jurnal (cont 5311/5121 etc. -vs. 4111), scrisa prin acelasi flux `ACT_TEMP -> ACT` ca restul documentului, nu un camp separat -pe `VANZARI`. `scrie_incasare2` nu are niciun apelant in afara lui `scrie_incasari` (cautat cu -grep in tot pachetul de facturare — un singur call-site). - -### 3.2 Cale de schimbare DUPA emitere - -Nu exista nicio procedura Oracle de tip "modifica_incasare"/"corecteaza_incasare" (cautat -`PROCEDURE ... (modifica|corecteaza|actualizeaza)...incasa...` in pachetul curent si -`PACK_CONTAFIN.pck` — zero potriviri). `scrie_incasari`/`scrie_incasare2` nu au niciun apelant VFP -direct (cautat in tot proiectul indexat — zero potriviri) — sunt folosite doar intern, o singura -data, la emitere. - -**PARTIAL, neconfirmat pana la capat**: incasarea scrisa la emitere e o linie de nota contabila -(`ACT`/`RUL`) ca oricare alta. Daca acea linie foloseste acelasi `cod` (acelasi document contabil) -ca restul facturii, atunci mecanismul nou de la Grupul A (`frm_facturi.do_editare_factura` -> -`frm_modific2024`) ar incarca-o si pe ea in grid — si, editand campurile generice de nota -(cont/gestiune/suma, prin acelasi `do_modifica` sau direct in grid), un utilizator ar putea -schimba indirect suma/contul incasarii, deci si `casa`/`suma incasata` efectiva. **Nu am confirmat -daca incasarea primeste acelasi `cod` sau un `cod` separat** — ar necesita fie testare live -(interzisa aici, doar cercetare pe cod), fie citirea completa a insertiei din `scrie_incasare2` -(am citit doar semnatura si primele ~35 linii, nu INSERT-ul propriu-zis in `ACT_TEMP`). Marchez -explicit ca necunoscuta ramasa, nu ca "DA" confirmat. - -`opt_incasat`/`chkPOS`/serie-numar chitanta/bon **ca atare** (proprietati `poDate` folosite doar -pentru alocarea de numere de serie si generarea listei `lcListaIncasare`) nu au niciun camp -persistent separat pe care sa-l poata atinge editarea ulterioara a notei — nu exista coloane -`VANZARI.OPT_INCASAT`/`VANZARI.SERIE_CHIT` in tot ce am cautat. - -**Verdict grup C**: NU pentru o cale directa/documentata de schimbare dupa emitere; PARTIAL, -neconfirmat, prin editarea generica a notei contabile (acelasi mecanism nou de la Grupul A), -DACA incasarea partajeaza `cod`-ul cu restul documentului. - ---- - -## Ce am cautat (pentru un "NU" verificabil) - -- **Oracle**: `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_06_10_COMUN_PACK_FACTURARE.sql` - (17020 linii, pachetul curent) — grep pe `UPDATE VANZARI`, `UPDATE ACT`, `UPDATE DOCUMENTE` (toate - aparitiile citite in context), plus grep combinat `UPDATE ... SET =` (fereastra - multiline 300-400 caractere) pentru fiecare coloana din grupele A/B/C. Idem pe - `D:\ROA\ROAFACTURARE\COMUN\docs\PACK_CONTAFIN.pck` (9041 linii), `PACK_UPDATE.pck` (2420 linii), - `PACK_MIGRARE.pck` (407 linii). -- **VFP**: `vfp_symbols.ps1 -Grep -CodeOnly` (index complet: 316 fisiere, 1128 clase, 9281 metode, - 2461 proceduri) pentru fiecare nume de coloana Oracle si fiecare nume de control VFP din enunt - (`Ct_clb_venchelt/sectie/responsabil/lucrare/fdoc/valuta/altele/gestiune_init/politici_preturi`, - `Clb_zi_curs`, `opt_incasat`, `Cb_casa`, `Clb_serie_chit`, `Clb_nrchit`, `Clb_incasat`, `chkPOS`). - Pentru fiecare formular gasit (`frm_date_factura`, `frm_date_aviz`, `frm_alte_date`), am cautat - toti apelantii (`Createobject("...")`) in tot proiectul indexat, ca sa confirm ca sunt doar pe - fluxul de emitere. -- **Citire, fara scriere**: `ofacturare_comun.vc2` (metoda `do_editare_factura`, ~3690-3869) si - `ofacturare_editare.prg` (integral, 457 linii) — apartin altui fir de lucru, doar citite. -- `omodificari.vc2` (16464 linii) — clasa `frm_modific2024`: citite integral `do_modifica` - (13836-13957) si `do_salvare` (13985-14042); NU am citit toate cele ~180 de metode ale clasei - (Grid1.Column*, pgfArticole.*) — posibil sa existe alte cai de editare directa in grid, - nedescoperite prin `do_modifica`. - -## Necunoscute ramase - -1. **ID_VENCHELT** (grup A): daca e editabil in grid-ul `frm_modific2024` prin alt mecanism decat - `do_modifica` (coloana `dst_chlt` exista in grid) — neverificat pana la capat. -2. **Incasarea pe acelasi `cod`** (grup C): daca linia de incasare scrisa de `scrie_incasare2` - foloseste acelasi `cod` ca restul facturii (ceea ce ar face-o editabila prin - `do_editare_factura`) — necesita citirea INSERT-ului efectiv in `ACT_TEMP` din - `scrie_incasare2` (nu doar semnatura, citita aici doar partial) sau verificare pe date reale. -3. **Bug-ul de la `sectie`** (`omodificari.vc2:13941`, `id_valuta with loCauta.id_sectie` in loc de - `id_sectie`) — semnalat ca observatie, nu investigat mai departe (nu face parte din intrebare, - dar afecteaza increderea in verdictul "DA" pentru `ID_SECTIE": campul e teoretic editabil, dar - codul care il scrie pare sa aiba un bug care ar putea sa nu-l actualizeze corect in practica). diff --git a/docs/cercetare/s10_pret_contract_reemitere.md b/docs/cercetare/s10_pret_contract_reemitere.md deleted file mode 100644 index 7fb8183..0000000 --- a/docs/cercetare/s10_pret_contract_reemitere.md +++ /dev/null @@ -1,468 +0,0 @@ -# S10 (runda 3) — Proiectare: pretul re-derivat din contract la reemiterea #13 - -Agent de proiectare (research), read-only. Nicio editare de cod, nicio scriere Oracle in afara de -`SELECT`. Sursa SQL: `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` -(17217 linii — **de data asta liniile citate coincid exact cu cele din raportul vechi -`s10_pret_rederivat.md`, fara offset**). Date verificate pe `MARIUSM_AUTO@ROA_CENTRAL`, doar -`SELECT`, 11.08.2026 — baza de dezvoltare, esantion mic (vezi sectiunea 2), nu productie. - -## 0. Rezumat - -**Intrebare adaugata de team-lead, raspuns scurt: NU.** `cursor_retur_document` (procedura folosita -la INCARCAREA liniilor unui document existent, apelata din VFP cu `V_COPIERE=1`) **citeste pretul -ca atare din `VANZARI_DETALII.PRET`, nu il re-deriva** — nu exista niciun `JOIN` catre -`CTR_ARTICOLE`/`CRM_POLITICI_PRET_ART` in toata procedura (`:3949-4062`). Presupunerea din S8b se -confirma pe cod. Detaliu complet, cu dovezi, in sectiunea 1bis (nou adaugata, tratata ca prioritara -fata de restul raportului, conform cererii). - -**Faptul portant (scrierea, nu citirea) se reconfirma integral** (`:5146-5185`) si se extinde cu -doua descoperiri noi: - -1. **TVA-ul si identitatea valutei sunt re-derivate din exact acelasi `JOIN`/`SELECT` ca pretul**, - pe aceeasi ramura de contract — nu doar pretul e in pericol la reemitere, ci pretul + cota de - TVA + valuta articolului, impreuna, tot-sau-nimic (sectiunea 3). -2. **Nu exista nicio a doua re-derivare mai departe in lant** — `PRET` trece neschimbat de la - `VANZARI_DETALII_TEMP` in `VANZARI_DETALII` (`scrie_in_vanzari`, `:13705-13757`, coloana `PRET` - copiata direct, fara `DECODE`/recalcul) si `contabilizeaza_articol` nu scrie niciodata pe - `VANZARI_DETALII.PRET` (scrie doar `ACT_TEMP`, nota contabila). Singurul loc de re-derivare e - `adauga_articol_factura:5146-5185` (sectiunea 1) — **la scriere, niciodata la citire** (sectiunea - 1bis). - -Pe date reale (esantion mic, baza de dezvoltare): **3 din 11 linii deja facturate pe contract au -azi un pret de contract diferit de pretul efectiv facturat** — divergenta nu e ipotetica, e deja -prezenta (sectiunea 2). - -**Recomandare** (detaliata in sectiunea 4): varianta **(c) avertizare + confirmare explicita**, -implementabila integral in VFP cu `goExecutor` (fara sa ating `pack_facturare`, conform deciziei -27-bis), cu optiunea de a trece la **(b) blocare stricta** daca Marius prefera zero schimbari -tacite vreodata. Varianta **(d) ocolire a ramurii** e posibila mecanic dar produce o regresie reala -(pierderea legaturii `VANZARI_DETALII.ID_CTR`) — **nu o recomand**. - ---- - -## 1. Reverificarea faptului portant - -Cod citit direct din sursa (`:5039-5220`), nu preluat din raportul vechi. - -```sql --- :5039-5050 — V_OPT_FACTURARE se calculeaza intern, din V_ID_CTR primit de la VFP -IF pack_facturare.ntip IN (2, 6, 26, 52) THEN - BEGIN - SELECT NVL(OPT_FACTURARE, 3) INTO V_OPT_FACTURARE FROM CONTRACTE WHERE ID_CTR = V_ID_CTR; - EXCEPTION - WHEN NO_DATA_FOUND THEN - V_OPT_FACTURARE := 4; - END; -END IF; -``` - -```sql --- :5146-5185 — ramura de contract, in interiorul CASE-ului principal (:5052-5220) -WHEN V_OPT_FACTURARE = 3 THEN - BEGIN - SELECT DECODE(A.PRET_UNITAR, 0, V_PRET_TEMP, A.PRET_UNITAR), - B.PROC_TVAV, - B.ID_VALUTA, - A.PRET_CU_TVA, - C.IN_STOC - INTO V_PRET, V_PROC_TVAV, V_ID_VALUTA, V_PRETURI_CU_TVA, V_IN_STOC - FROM CTR_ARTICOLE A - LEFT JOIN CRM_POLITICI_PRET_ART B ON A.ID_POL_ART = B.ID_POL_ART - LEFT JOIN NOM_ARTICOLE C ON B.ID_ARTICOL = C.ID_ARTICOL - WHERE B.ID_ARTICOL = V_ID_ARTICOL AND A.ID_CTR = V_ID_CTR AND B.ID_POL = V_ID_POL; - EXCEPTION - WHEN NO_DATA_FOUND THEN - ... V_PRET := V_PRET_TEMP; V_ID_VALUTA := V_ID_VALUTA_TEMP; V_PRETURI_CU_TVA := V_PRETURI_CU_TVA_TEMP; V_IN_STOC := V_IN_STOC_TEMP; - END; -``` - -**Confirmat, cuvant cu cuvant fata de runda 9**: cu `OPT_FACTURARE = 3` (sau `NULL`, implicit 3), -daca `CTR_ARTICOLE.PRET_UNITAR <> 0`, acea valoare **inlocuieste** `V_PRET_TEMP` (pretul trimis de -VFP), necondiționat de faptul ca operatorul a atins sau nu linia. Procedura **nu are nicio notiune -de reemitere** — parametrii sunt toti `IN` (`:4989-5059`, verificat), niciun canal care sa spuna -"nu recalcula". `NO_DATA_FOUND` (politica/articol lipsa) e singurul caz care respecta pretul -trimis. - -### Nu exista alt loc care suprascrie pretul - -- **`scrie_in_vanzari`** (`:13488+`), care muta liniile din `VANZARI_DETALII_TEMP` in - `VANZARI_DETALII` (tabela finala) la finalizarea documentului: `PRET` e in lista de coloane - copiate **neschimbat** (`:13705-13757`, `SELECT ... PRET ... FROM VANZARI_DETALII_TEMP` → - `INSERT INTO VANZARI_DETALII (..., PRET, ...)`), fara `DECODE`/recalcul. Cautare exhaustiva pe - fisier: **zero** `INSERT INTO VANZARI_DETALII` (tabela finala, nu `_TEMP`) in afara de acest - punct. -- **`contabilizeaza_articol`** (`:7173-7547`, reconfirmat structural fata de runda 9) citeste - `detalii_articol.pret` (randul deja scris in `VANZARI_DETALII_TEMP`) doar ca sa calculeze suma - notei contabile — nu scrie niciodata inapoi pe `VANZARI_DETALII.PRET`. -- **`modificare_politica_stoc`** (`:2122-2135`) face un `UPDATE CRM_POLITICI_PRET_ART SET PRET=0, - ...` — e singurul alt `SET PRET` gasit in tot fisierul, dar reseteaza **politica** la schimbarea - monedei stocului, complet in afara lantului de facturare a unei linii; nu atinge vreo linie de - document. - -**Concluzie sectiune**: raspunsul din runda 9 se confirma **integral**, fara nicio corectie, si se -inchide explicit intrebarea "mai exista si alte locuri" — nu mai exista. - ---- - -## 1bis. Citirea la incarcare: `cursor_retur_document` re-deriva pretul? - -**Adaugat la cererea team-lead-ului — critic pentru S8b, tratat ca prioritar.** - -### Verdict: NU. Pretul se citeste ca atare din `VANZARI_DETALII.PRET`, fara nicio re-derivare. - -Corp complet, `PACK_FACTURARE:3949-4062`: - -```sql -PROCEDURE cursor_retur_document(V_IN_VALUTA IN NUMBER, - V_LISTAID IN VARCHAR2, - V_COPIERE IN NUMBER, - V_PROFORMA IN NUMBER, - V_ID_UTIL IN NUMBER, - V_CURSOR OUT cursor_facturare) IS -... -BEGIN - pack_facturare.initializeaza_facturare(V_ID_UTIL); - - OPEN V_CURSOR FOR - WITH CRS AS (SELECT X as ID_VANZARE FROM table(charn2collection(V_LISTAID, V_SEPARATOR))) - SELECT ROWNUM AS ID_C, A.ID_ARTICOL, A.LOT, A.SERIE, A.ID_POL, A.ID_VALUTA, - ... DISCOUNT_UNITAR (derivat din A.DISCOUNT_UNITAR) ..., - B.CODMAT, B.CODBARE, B.DENUMIRE, NVL(B.UM,'') AS UM, - ... GESTIONABIL ..., A.CANTITATE, A.PROC_TVAV, A.ID_JTVA_COLOANA, - A.PRET_CU_TVA AS PRETURI_CU_TVA, A.CURS, A.MULTIPLICATOR, - (CASE WHEN NVL(C.MONEDA_NATIONALA,1)<>1 - THEN ROUND(A.CURS * ROUND(A.PRET, pack_sesiune.nzecimale_pretvval) / NVL(A.MULTIPLICATOR,1), pack_sesiune.nzecimale_pretv) - ELSE ROUND(A.PRET, pack_sesiune.nzecimale_pretv) - END) + A.DIFERENTA AS PRET, - ... - FROM (SELECT A1.ID_ARTICOL, A1.LOT, A1.SERIE, A1.ID_POL, A1.DISCOUNT_UNITAR, A1.DIFERENTA, - A1.CANTITATE, A1.PROC_TVAV, A1.ID_JTVA_COLOANA, - NVL2(A1.ID_GESTIUNE,1,0) AS GESTIONABIL, A1.PRET_CU_TVA, A1.PRET, A1.ID_VALUTA, - NVL(A2.CURS,0) AS CURS, NVL(A2.MULTIPLICATOR,1) AS MULTIPLICATOR, - A1.EXPLICATIE, A1.ID_GESTIUNE, A1.PRET_ACHIZITIE, A1.PRETD, A1.ID_JTVA_COLOANA_EX - FROM VANZARI_DETALII A1 - LEFT JOIN VANZARI_CURSURI A2 ON A1.ID_VANZARE = A2.ID_VANZARE AND A1.ID_VALUTA = A2.ID_VALUTA - WHERE A1.STERS = 0 AND A1.ID_VANZARE IN (SELECT ID_VANZARE FROM CRS)) A - LEFT JOIN NOM_ARTICOLE B ON A.ID_ARTICOL = B.ID_ARTICOL - LEFT JOIN NOM_VALUTE C ON A.ID_VALUTA = C.ID_VALUTA - ORDER BY B.DENUMIRE; -END cursor_retur_document; -``` - -**Analiza surselor, coloana cu coloana:** - -- **`A.PRET`** (`:4009-4015`, alias final `PRET`) vine din subquery-ul `A` care e - **`VANZARI_DETALII A1`** direct (`A1.PRET`, `:4041`) — **nicio referinta la - `CTR_ARTICOLE`/`CRM_POLITICI_PRET_ART`/`CRM_POLITICI_PRETURI` in toata procedura** (cautare - exhaustiva pe corpul de la `:3949-4062`: zero hit-uri pentru oricare din cele trei tabele). - Singurele `JOIN`-uri sunt: - - **`VANZARI_CURSURI A2`** (`:4051-4053`), pe `ID_VANZARE`+`ID_VALUTA` — aduce `CURS`/ - `MULTIPLICATOR` **stocate pe documentul insusi** (cursul valutar de la momentul cand a fost - scris, nu un curs "de azi" recalculat — tabela `VANZARI_CURSURI`, nu `CURS`). - - **`NOM_ARTICOLE B`** (`:4056-4057`) — doar campuri descriptive (`CODMAT`, `CODBARE`, - `DENUMIRE`, `UM`), acelasi tipar confirmat deja in `rec_pret_lazy.md` A2 pentru alte cursoare - din pachet — niciodata sursa de pret. - - **`NOM_VALUTE C`** (`:4058-4059`) — doar `MONEDA_NATIONALA`/`NUME_VAL`, folosit in `CASE` ca sa - decida **cum se formateaza** afisarea (lei vs. valuta), nu ca sa aduca un pret alternativ. - - Expresia finala pe `PRET` e o **transformare de afisare** a lui `A.PRET` (rotunjire + - conversie in valuta folosind `A.CURS`/`A.MULTIPLICATOR` **stocate pe document**) plus - `A.DIFERENTA` (coloana proprie pe `VANZARI_DETALII`, un ajustaj deja persistat pe linie, nu o - recalculare live) — nu o re-derivare dintr-o sursa externa. -- **Acelasi tipar pe `DISCOUNT_UNITAR`, `PROC_TVAV`, `ID_VALUTA`, `PRET_CU_TVA`** — toate citite - direct din `A1.*` (`VANZARI_DETALII`), fara vreun `JOIN` catre politica/contract. - -**Confirmare suplimentara pe partea VFP — `cursor_retur_document` cu `V_COPIERE=1` e chiar calea -folosita la reincarcarea unui document existent, cu prioritate fata de ramura de contract**: - -``` -COMUN\programe\ofacturare.prg:266-283 -Do Case - Case m.llCopiere - lcSqlCursor = [{call pack_facturare.cursor_retur_document(?poDate.in_valuta,?poDate.listaid,1,?poDate.eProforma,?gnIdUtil)}] - Case Inlist(tnTip, 48, 49) - ... - Case tnTip = 45 - ... - Case Inlist(tnTip, 1, 22, 5, 29, 7, 10, 23) && lista de preturi - ... - Case Inlist(tnTip, 2, 26, 6, 52) && contract - lcSqlCursor = [{call ]+gcS+[.pack_facturare.cursor_contract(...)}] -``` - -`Do Case` in VFP executa **doar prima ramura adevarata**. Cand `llCopiere` e activ (incarcarea unui -document existent — exact scenariul "editare prin regenerare" descris de team-lead), procedura -apelata e **intotdeauna** `cursor_retur_document`, **indiferent de `tnTip`** — ramura de contract -(`cursor_contract`, `:283+`) nu se mai ajunge deloc, chiar daca documentul e de tip 2/6/26/52. -`cursor_retur` (`:3934-3947`) e doar un wrapper subtire peste `cursor_retur_document` cu -`V_COPIERE=0`/`V_PROFORMA=0`, pentru returul propriu-zis — nu schimba concluzia. - -### Consecinta pentru S8b - -**Presupunerea "re-derivarea e o proprietate a scrierii, nu a citirii" se confirma pe cod, fara -rezerve.** Deschiderea formularului de editare (etapa II, incarcarea liniilor existente) **nu** -declanseaza nicio re-derivare de pret/TVA/valuta — documentul apare "neschimbat" la simpla -deschidere, exact cum a presupus S8b. Mecanismul de detectare a schimbarii (compara starea -incarcata cu starea la confirmare) **poate functiona ca premisa** — riscul de suprascriere tacita -descris in restul acestui raport (sectiunile 1-6) apare **doar la re-scriere** (regenerare efectiva -prin `adauga_articol_factura`), nu la incarcare. Cele doua momente (citire la deschidere, scriere -la regenerare) sunt guvernate de **cursoare Oracle complet diferite** (`cursor_retur_document` vs. -`adauga_articol_factura`), fara cod comun — o modificare facuta doar pe unul din ele (de exemplu o -garda adaugata la scriere, variantele (b)/(c) din sectiunea 4) **nu afecteaza** comportamentul -celuilalt. - ---- - -## 2. Tipurile afectate si frecventa in date - -**Tipurile de document**: `pack_facturare.ntip IN (2, 6, 26, 52)` (`:5039`), confirmat identic cu -tabelul din `rec_editare_factura.md:176` (contract = tip 2/6/26/52, ramura de scriere -`scrie_factura2`, la fel ca lista de preturi — nu are RPC separat). - -**Frecventa in date** (schema `MARIUSM_AUTO`, doar `SELECT`, 11.08.2026): - -```sql --- VANZARI_DETALII cu ID_CTR populat, active: 74 --- din care, articolul are un rand corespunzator in CTR_ARTICOLE (id_ctr+id_articol): 11 --- din acelea, CTR_ARTICOLE.PRET_UNITAR <> 0 SI diferit de VANZARI_DETALII.PRET: 3 --- din acelea, CTR_ARTICOLE.PRET_UNITAR <> 0 SI egal cu VANZARI_DETALII.PRET: 8 --- din acelea, CTR_ARTICOLE.PRET_UNITAR = 0 (nu suprascrie): 0 -``` - -**Interpretare, cu grija la marimea esantionului**: baza e de dezvoltare, cu doar 27 randuri totale -in `CTR_ARTICOLE` — nu extrapolez procentul (27%) la volumul de productie. Dar **3 cazuri reale, -nu zero**, e dovada suficienta ca fenomenul **chiar se intampla**, nu doar teoretic: daca oricare -din cele 3 facturi ar fi reemisa azi prin regenerare (#13 etapa II), suma ei s-ar schimba tacit, -fara nicio actiune a operatorului legata de pret. Simetric, cele 8 cazuri "egal" arata ca in -majoritatea situatiilor curente reemiterea ar fi azi inofensiva — ceea ce face problema mai -insidioasa, nu mai putin reala: cand se produce, nimeni nu se astepta, pentru ca de obicei nu se -intampla nimic vizibil. - -**Nu exista coloana de audit pe `CTR_ARTICOLE`** (confirmat din `user_tab_columns`: `ID_CTR_ART, -ID_CTR, ID_POL_ART, PRET_UNITAR, CANT, COEF_DISCOUNT, VAL_DISCOUNT, ID_VALUTA, EXPLICATIE, UM, -ID_ARTICOL, PRET_CU_TVA, ID_LOCATIA, PROC_TVAV, VALOARE` — nicio `DATA_MODIF`/`ID_UTIL_MODIF`) — -nu exista cale de a masura *cat de des* se schimba `PRET_UNITAR` in timp, doar *cate cazuri -divergente exista azi*. Raspunsul la "cat de des" ramane nedeterminabil din date; ce se poate -spune e doar ca divergenta **exista deja**, azi, pe un esantion mic. - ---- - -## 3. Descoperire noua: TVA si valuta sunt re-derivate din **aceeasi** interogare ca pretul - -Din citatul de la sectiunea 1: `SELECT DECODE(A.PRET_UNITAR, ...), B.PROC_TVAV, B.ID_VALUTA, ...` -— **un singur `SELECT`**, trei valori. Asta inseamna ca varianta de raspuns nu poate trata pretul -izolat: daca politica de pret a contractului (`CRM_POLITICI_PRET_ART`, aceeasi `B`) si-a schimbat -si cota de TVA sau valuta intre timp, reemiterea le suprascrie **pe amandoua**, prin acelasi -mecanism, in aceeasi conditie (`NO_DATA_FOUND` → respecta ce a trimis VFP; altfel → suprascrie). - -Comparativ cu discountul si cursul (reconfirmare fata de runda 9, sectiunile 6-7 ale raportului -vechi, verificate din nou pe sursa curenta): - -| Camp | Tratament pe ramura de contract | Risc la reemitere | -|---|---|---| -| **Pret** (`V_PRET`) | `DECODE(A.PRET_UNITAR, 0, V_PRET_TEMP, A.PRET_UNITAR)` — suprascris daca `PRET_UNITAR<>0` | **Da — faptul portant** | -| **TVA** (`V_PROC_TVAV`) | `B.PROC_TVAV`, din acelasi `SELECT`, fara fallback conditionat separat | **Da — aceeasi conditie ca pretul** | -| **Valuta articol** (`V_ID_VALUTA`) | `B.ID_VALUTA`, idem | **Da — aceeasi conditie** | -| **Discount** (`V_DISCOUNT_UNITAR`) | Parametru trimis de VFP, scris direct in `INSERT` (`:5266`), nicio ramura din `CASE` il citeste | **Nu — mereu respectat, indiferent de ramura** | -| **Curs** (`V_CURS`) | `DECODE(V_CURS, 0, 1, V_CURS)` la `INSERT` (`:5270`) — inlocuieste doar 0 cu 1 | **Nu direct — dar vezi mai jos** | - -**Consecinta combinata pret+valuta+curs**: daca `V_ID_VALUTA` re-derivat difera de valuta pentru -care VFP a calculat `V_CURS` (de ex. politica a trecut de la EUR la USD intre emitere si -reemitere), documentul reemis scrie **noua valuta cu vechiul curs trimis de VFP** — nicio -validare incrucisata intre cele doua (reconfirmat, `:5270` nu verifica `V_ID_VALUTA`). Aceeasi -observatie ca in runda 9, dar acum cu dovada ca sursa comuna (pret+TVA+valuta) inseamna ca **orice -gardă construita pentru pret trebuie sa verifice si TVA si valuta in acelasi pas**, nu doar pretul -— altfel o factura reemisa poate trece garda de pret nemodificata si totusi sa iasa cu alta cota -de TVA sau alta valuta. - -**Discountul nu ridica aceeasi problema** — e mereu respectat ca atare, indiferent de ramura, -deci nu are nevoie de nicio garda la reemitere. - ---- - -## 4. Variantele de raspuns - -Toate patru presupun ca infrastructura de "sterge + reemite" din #13 etapa II trimite, pentru -liniile de pe un document-contract, acelasi `V_ID_CTR` care era pe linia originala (asta e -premisa explicita a sarcinii: "acelasi drum ca la emitere" — daca `V_ID_CTR` nu s-ar retrimite, -linia nici n-ar mai fi "de pe contract"). - -### (a) Se accepta re-derivarea — nicio garda - -**Ce se face**: nimic — regenerarea apeleaza `adauga_articol_factura` exact ca la emitere, cu -acelasi `V_ID_CTR`. Comportamentul existent (sectiunea 1) se aplica neschimbat. - -**Ce se strica**: criteriul din plan ("reemitere identica produce exact aceleasi sume" — vezi -sectiunea 6) **esueaza silentios** pe orice linie de contract al carei pret/TVA/valuta a fost -modificat intre timp. Dovedit sa se intample azi, pe date reale (sectiunea 2, 3 din 11). Utilizatorul -care corecteaza o cantitate pe o factura veche poate vedea, fara sa i se spuna, totalul facturii -schimbat pentru un articol la care nici nu s-a uitat. - -**Cod atins**: zero. **Regresie pe emiterea normala**: zero (comportamentul de azi ramane -identic). - -### (b) Se blocheaza reemiterea cand pretul curent difera - -**Ce se face**: inainte de a porni stergerea+regenerarea, VFP ruleaza un `SELECT` (prin -`goExecutor`, exact tiparul deja folosit in `ofacturare.prg` pentru alte verificari punctuale — -`goExecutor.oSelecteaza2Value(...)`, `:2628`, `:2661`) care compara, pentru fiecare linie a -documentului cu `ID_CTR` populat, pretul/TVA/valuta stocate pe linie -(`VANZARI_DETALII.PRET`/`PROC_TVAV`/`ID_VALUTA`) fata de ce ar recalcula azi ramura de contract -(`CTR_ARTICOLE.PRET_UNITAR`/`CRM_POLITICI_PRET_ART.PROC_TVAV`/`.ID_VALUTA`, acelasi `JOIN` ca in -sectiunea 1, dar rulat direct din VFP, fara sa ating `pack_facturare`). Daca gaseste macar o -divergenta, **blocheaza regenerarea** cu mesaj ("Pretul de pe contract s-a schimbat pentru -articolul X: -> . Actualizati contractul sau anulati regenerarea.") si nu porneste -deloc stergerea. - -**Ce se strica**: orice regenerare pe un document cu **cel putin o linie** de contract cu pret -schimbat e blocata **integral**, chiar daca utilizatorul voia sa corecteze cu totul altceva -(ruta, o cantitate pe alta linie). Nu exista cale de a "accepta noul pret si continua" fara o a -doua interactiune — varianta (c) rezolva exact asta. - -**Cod atins**: doar VFP (o interogare noua, read-only, plus un dialog de blocare), inaintea -apelului la infrastructura de stergere+regenerare din #13. **Zero atingere `pack_facturare`** -(decizia 27-bis respectata). **Regresie pe emiterea normala**: zero — garda ruleaza doar pe -calea de regenerare, nu la emiterea unui document nou. - -### (c) Se avertizeaza si se cere confirmare, cu diferenta afisata - -**Ce se face**: aceeasi interogare de detectie ca la (b), dar in loc sa blocheze, afiseaza un -dialog cu lista liniilor divergente si delta (pret vechi vs. nou, si TVA/valuta daca difera — -vezi sectiunea 3, toate trei trebuie aratate, nu doar pretul) si cere confirmare explicita -("Continuati cu noile preturi de pe contract?" Da/Nu). La "Nu", regenerarea se opreste ca la (b). -La "Da", regenerarea continua normal si ramura de contract face exact ce face azi (suprascrie). - -**Ce se strica**: nu opreste suprascrierea insasi — doar o face **vizibila si asumata** inainte -sa se produca, exact criteriul cerut de plan ("un rezultat explicit decis, nu accidental"). Nu -ofera o cale de a **pastra** vechiul pret in timp ce se accepta restul modificarii — pentru asta -ar trebui combinata cu (d), cu costul ei (vezi mai jos). Risc de "warning fatigue": daca -divergenta e frecventa in productie (nu se poate masura azi, sectiunea 2), utilizatorii pot invata -sa dea click pe "Da" fara sa citeasca. - -**Cod atins**: identic cu (b) plus un dialog cu doua butoane in loc de unul singur. **Regresie pe -emiterea normala**: zero. - -### (d) Se ocoleste ramura — reemiterea trimite pretul documentului pe o cale care nu trece prin `DECODE` - -**Posibil mecanic, cu un cost real.** Ramura de contract se selecteaza din doua conditii, ambele -in afara controlului direct al apelantului per-linie: - -1. `pack_facturare.ntip IN (2,6,26,52)` — variabila **de sesiune** (`:1882`, - `pack_facturare.ntip := V_TIP`), setata o singura data la initializarea documentului (tipul - documentului insusi), nu per linie. A o schimba ar insemna sa reclasifici tot documentul - (afecteaza si alte ramuri `CASE` care depind de `ntip` — `:1905`, `:5053`, `:6056-6124`, - `:14820` — cu efecte necunoscute si necontrolate) — **contrazice premisa "acelasi drum ca la - emitere"** data explicit in sarcina. Nu recomand aceasta sub-varianta. -2. `V_ID_CTR` — parametru **per linie**, trimis explicit de VFP la fiecare apel - `adauga_articol_factura`. Daca VFP trimite `V_ID_CTR = NULL` pentru o linie la reemitere, - blocul de la `:5039-5050` gaseste `NO_DATA_FOUND` (cautarea `WHERE ID_CTR = NULL` nu potriveste - nimic) → `V_OPT_FACTURARE := 4` → ramura `WHEN V_OPT_FACTURARE = 3` nu se mai potriveste → - cade pe `ELSE` (`:5187-5203`) → `V_PRET := V_PRET_TEMP` (pretul documentului, respectat ca - atare, la fel TVA prin `JTVA_COLOANE` dupa `V_ID_JTVA_COLOANA`). - - **Costul**: `V_ID_CTR` e si coloana scrisa in `VANZARI_DETALII_TEMP`/`VANZARI_DETALII.ID_CTR` - (`:5280`, copiata neschimbat mai departe la `:13730`/`:13755`). Trimitand `NULL` la reemitere, - linia **pierde definitiv legatura cu contractul** in tabela finala — orice raport care - grupeaza/filtreaza pe `VANZARI_DETALII.ID_CTR` (regasire vanzari pe contract, situatii - contractuale) nu va mai gasi acea linie dupa reemitere, desi articolul a fost, de fapt, livrat - pe acel contract. E o regresie permanenta si greu de observat (nu da eroare, doar dispare - tacit dintr-un raport), pe un camp folosit azi activ (`rec_pret_lazy.md`/`idpol_comanda_contract.md` - confirma ca legatura cu contractul e cheie de raportare in tot lantul `PACK_FACTURARE`). - In plus, o data pierduta legatura, **o a doua reemitere ulterioara nu ar mai putea re-deriva - pretul din contract nici daca s-ar dori** — comutarea e ireversibila per linie, nu un flag - comutabil. - -**Nu recomand varianta (d)** — costul (pierderea trasabilitatii contract-linie, permanenta) e mai -mare decat problema pe care o rezolva, si nici macar nu rezolva complet criteriul "reemitere -identica" (rezolva doar pretul/TVA/valuta acelei linii, cu pretul unei regresii pe alt camp). - ---- - -## 5. Discount, TVA, curs valutar — verificare fata de sectiunile 6-7 din raportul vechi - -Deja tratat integral in sectiunea 3 de mai sus (descoperirea principala a acestei runde). Rezumat -scurt pentru trasabilitate fata de cererea explicita: - -- **Discountul** (sectiunea 6 raport vechi): reconfirmat — niciodata re-derivat, indiferent de - ramura. **Nu ridica problema de reemitere.** -- **TVA-ul** (sectiunea 6 raport vechi): reconfirmat ca "mereu recalculat, din surse diferite pe - ramura" — dar cu precizarea noua ca pe ramura de contract, sursa **coincide exact** cu sursa - pretului (acelasi `SELECT`, aceeasi conditie `NO_DATA_FOUND`). **Ridica aceeasi problema ca - pretul, prin acelasi mecanism** — orice varianta (b)/(c) trebuie sa acopere si TVA, nu doar pret. -- **Cursul valutar** (sectiunea 7 raport vechi): reconfirmat — `V_CURS` mereu respectat ca atare - (`DECODE(V_CURS,0,1,V_CURS)`), nu se re-deriva direct. **Dar** identitatea valutei - (`V_ID_VALUTA`) se re-deriva pe aceeasi ramura de contract, din acelasi `SELECT` — daca politica - a schimbat valuta intre timp, reemiterea scrie noua valuta cu vechiul curs trimis de VFP, fara - nicio validare incrucisata (reconfirmat, `:5270` nu verifica `V_ID_VALUTA`). **Ridica aceeasi - problema, prin acelasi mecanism, si trebuie acoperita de aceeasi garda.** - ---- - -## 6. Cazul "reemitere identica" - -Criteriul din plan (S12) cere ca o reemitere fara nicio modificare intentionata sa produca -**exact** aceleasi sume. Verificat pe fiecare ramura a `CASE`-ului de la `:5052-5220` (nu doar -ramura de contract): - -| Ramura (`ntip`) | Garantat identic la reemitere? | De ce | -|---|---|---| -| `ELSE` (lista de preturi, fara contract) | **Da, cu o rezerva minora** | `V_PRET := V_PRET_TEMP` — passthrough direct (`:5200`). TVA vine din `JTVA_COLOANE` dupa `V_ID_JTVA_COLOANA` (`:5188-5198`) — identic **doar daca** acea coloana de TVA nu a fost ea insasi editata intre timp (posibil, dar alt tip de risc, nu cel cerut de aceasta sarcina). | -| `45` (restaurant) | **Da, cu aceeasi rezerva** | `V_PRET := V_PRET_TEMP` setat **inainte** de orice `SELECT` (`:5109`) — respectat necondiționat. TVA vine tot din `JTVA_COLOANE` (`:5114-5139`), aceeasi rezerva ca mai sus. | -| `3,21,28,42,47` (comenzi) | **Nu garantat, dar esueaza tare, nu tacit** | `SELECT ... WHERE A.PRET = V_PRET_TEMP` (`:5077`) — daca pretul din comanda-sursa nu mai coincide exact cu ce trimite VFP, **`NO_DATA_FOUND` netratat** → eroare Oracle bruta, regenerarea pica integral, nu produce o suma gresita silentios. Diferit structural de ramura de contract. | -| `4` (aviz) | **Nu garantat, silentios** | `SELECT DISTINCT A.PRET ... FROM VANZARI_DETALII` (`:5082-5103`), **fara filtru pe pret** — re-citeste pretul curent al liniei din avizul-sursa necondiționat. Daca avizul insusi a fost modificat intre emiterea initiala si reemitere, sau daca exista mai multe randuri candidate (`DISTINCT` pe o potrivire ambigua), reemiterea poate lua alt pret, tacit — **acelasi tipar de risc ca la contract**, dar pe alt document-sursa. | -| `V_OPT_FACTURARE=3` (contract) | **Nu garantat, silentios** | Sectiunea 1 — faptul portant. | - -**Observatie in afara perimetrului cerut, dar direct relevanta**: daca #13 etapa II ajunge sa -regenereze si facturi emise initial din **aviz** (`ntip=4`), nu doar din contract, **acelasi tip -de risc silentios exista si acolo**, prin alt mecanism (re-citire necondiționata din -`VANZARI_DETALII` a avizului-sursa, fara filtru pe pret). Raportul vechi (`s10_pret_rederivat.md`, -sectiunea 4) semnalase deja asta ca "neverificat, in afara bugetului" — de data asta o marchez -explicit ca **decizie deschisa**: daca reemiterea din #13 se aplica si documentelor provenite din -aviz, aceeasi discutie (a/b/c/d) trebuie purtata si pentru acea ramura, cu propriul cost (sursa -de comparat e alt document, nu `CTR_ARTICOLE`). Nu am extins cercetarea de date pe aceasta ramura -(in afara cererii explicite a sarcinii, care a cerut "cazul confirmat", adica cel de contract). - -**Concluzie sectiune**: criteriul "reemitere identica" e garantat azi doar pe ramurile -`ELSE`/restaurant (fara contract, fara comanda, fara aviz). Pe comenzi, o divergenta produce -eroare vizibila (rau, dar nu tacit). Pe contract **si** pe aviz, o divergenta produce o suma -gresita fara nicio semnalare — acestea sunt cele doua ramuri care au nevoie de o decizie explicita -de tipul (a)-(d) inainte ca #13 etapa II sa poata pretinde ca satisface criteriul S12. - ---- - -## 7. Ce ramane de decis de Marius - -1. **(b) blocare stricta vs. (c) avertizare+confirmare** — (c) satisface literal criteriul "decis - explicit, nu accidental" cerut de plan si nu intrerupe fluxul quasi-niciodata (doar cand chiar - exista divergenta); (b) e mai sigur (niciodata nu se schimba nimic fara interventie manuala pe - contract) dar mai frustrant daca divergentele sunt frecvente in productie — lucru nemasurabil - azi (sectiunea 2). **Recomand (c)** ca implicit, cu mesajul aratand explicit delta de - pret/TVA/valuta (sectiunea 3) — nu doar pretul. -2. **Garda trebuie sa acopere pret + TVA + valuta impreuna**, nu doar pretul — confirmat la - sectiunea 3 ca vin din acelasi `SELECT`. O implementare care verifica doar pretul ar lasa - trecerea tacuta a unei schimbari de TVA sau valuta. -3. **Varianta (d) (ocolire) — nu o recomand**, din cauza pierderii permanente a legaturii - `VANZARI_DETALII.ID_CTR`. Daca Marius considera totusi ca merita costul (de ex. daca - trasabilitatea contract-linie pe documentele reemise nu conteaza in practica), e o decizie de - produs explicita, nu tehnica — semnalez aici, nu decid. -4. **Ramura de aviz (`ntip=4`) are acelasi tip de risc silentios** (sectiunea 6) — de decis daca - intra in perimetrul #13 etapa II acum sau se trateaza separat, cu propriul studiu de fezabilitate - pentru garda (sursa de comparat difera fata de contract). -5. **Frecventa reala in productie a divergentei `CTR_ARTICOLE.PRET_UNITAR`** ramane nemasurabila - din baza de dezvoltare (27 randuri total) — daca decizia (b) vs (c) depinde de cat de des s-ar - declansa garda, ar trebui repetata interogarea din sectiunea 2 pe o baza cu volum de productie - inainte de implementare. - ---- - -## STARE / CE RAMANE - -**Livrabil complet** — toate cele 6 puncte cerute in brief sunt acoperite (sectiunile 1-6), plus -recomandare argumentata (sectiunea 4, cu tabel comparativ) si lista de decizii deschise -(sectiunea 7). - -Nimic ramas in lucru; nicio verificare intrerupta. Singurele limitari explicite: -- esantionul de date (27 randuri `CTR_ARTICOLE`, baza de dezvoltare) nu se extrapoleaza la - productie (sectiunea 2); -- ramura de aviz (`ntip=4`) e semnalata ca avand acelasi tip de risc, dar nu a primit acelasi nivel - de cercetare (nu era in perimetrul cerut) — vezi sectiunea 6, ultimul paragraf inainte de - concluzie. diff --git a/docs/cercetare/s10_pret_rederivat.md b/docs/cercetare/s10_pret_rederivat.md deleted file mode 100644 index 1afd654..0000000 --- a/docs/cercetare/s10_pret_rederivat.md +++ /dev/null @@ -1,342 +0,0 @@ -# S10 — Pretul re-derivat in `adauga_articol_factura`, ramura cu ramura - -## Nota pe sursa folosita - -`docs\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` **nu exista** (nici in `docs\`, nici urme in -istoricul git — `docs\` e integral netracked). Fisierul cu **acelasi nume** exista insa la -`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` si a fost folosit -ca sursa. Continutul `adauga_articol_factura` coincide structural cu ce descrie planul (aceeasi -ordine de ramuri, aceleasi coduri de eroare `FACT-012/013/018`, acelasi `DECODE(A.PRET_UNITAR, 0, -V_PRET_TEMP, A.PRET_UNITAR)`), dar liniile sunt deplasate cu **+17** fata de citatele din plan -(corp `4989-5284` aici vs. `4972-5268` in plan; ramura de contract `5146-5185` aici vs. `:5129-5168` -in plan) — probabil diferente de comentarii de antet intre cele doua exporturi. Toate citatele de mai -jos sunt pe fisierul din `SCRIPTURI_CLAR`, verificat direct. - -## Verdict (raspuns la intrebarea 4 — reemiterea) - -**Da, exista o ramura care suprascrie tacut pretul la orice apel, inclusiv la o eventuala -reemitere: liniile provenite dintr-un contract cu `OPT_FACTURARE = 3` (sau `NULL`, care se -implicit-eaza tot la 3).** Procedura nu are nicio notiune de "reemitere" — la fiecare apel cu acelasi -`V_ID_CTR`, daca articolul are in `CTR_ARTICOLE.PRET_UNITAR` o valoare diferita de 0, acea valoare -**inlocuieste** pretul trimis de VFP (`V_PRET_TEMP`), necontidionat de faptul ca linia a fost sau nu -atinsa de utilizator pe ecran. Riscul se materializeaza doar daca pretul din contract s-a schimbat -intre emiterea initiala si reemitere — daca n-a scazut, rezultatul e identic si nimeni nu observa -diferenta pana cand se schimba. Pe toate celelalte ramuri (implicita, restaurant), pretul trimis de -VFP e respectat ca atare. **Nu exista niciun parametru sau flag care sa opreasca re-derivarea** — -raspunsul la intrebarea 5 e "nu exista". - -## 1. Semnatura completa (corp, nu spec) - -`PACK_FACTURARE:4989-5015` (spec identica la `:537-563`, verificat doar ca prezenta, nu citat): - -``` -PROCEDURE adauga_articol_factura(V_ID_TEMP IN NUMBER, - V_ID_ARTICOL IN NUMBER, - V_SERIE IN VARCHAR2, - V_EXPLICATIE IN VARCHAR2, - V_ID_POL IN NUMBER, - V_ID_GESTIUNE IN NUMBER, - V_PRET_ACHIZITIE_TEMP IN NUMBER, - V_PRETD IN NUMBER, - V_ID_VALUTAD IN NUMBER, - V_PRET_TEMP IN NUMBER, - V_ID_VALUTA_TEMP IN NUMBER, - V_PRETURI_CU_TVA_TEMP IN NUMBER, - V_IN_STOC_TEMP IN NUMBER, - V_CANTITATE IN NUMBER, - V_DISCOUNT_UNITAR IN NUMBER, - V_CONT IN VARCHAR2, - V_CURS IN NUMBER, - V_MULTIPLICATOR IN NUMBER, - V_ID_JTVA_COLOANA IN NUMBER, - V_ID_PART_REZ IN NUMBER, - V_ID_LUCRARE_REZ IN NUMBER, - V_PRETV_ORIG IN NUMBER, - V_ID_VANZARE_SET IN NUMBER, - V_ID_CTR IN NUMBER, - V_ID_UTIL IN NUMBER, - V_TAXCODE IN NUMBER DEFAULT NULL, - V_LOT IN VARCHAR2 DEFAULT NULL) IS -``` - -Toti parametrii sunt **IN**, niciunul OUT/IN OUT — nu exista un canal de intoarcere a pretului -recalculat catre VFP la momentul apelului (linia se citeste ulterior, la re-interogarea grilei). - -**`V_OPT_FACTURARE` nu e parametru.** E o variabila locala (`:5029`), calculata **in interiorul -procedurii** din `CONTRACTE.OPT_FACTURARE`, folosind parametrul `V_ID_CTR` primit de la VFP -(`:5039-5050`): - -```sql -IF pack_facturare.ntip IN (2, 6, 26, 52) THEN - BEGIN - SELECT NVL(OPT_FACTURARE, 3) INTO V_OPT_FACTURARE FROM CONTRACTE WHERE ID_CTR = V_ID_CTR; - EXCEPTION - WHEN NO_DATA_FOUND THEN - V_OPT_FACTURARE := 4; - END; -END IF; -``` - -VFP nu trimite si nu poate influenta `V_OPT_FACTURARE` altfel decat prin `V_ID_CTR` (`poArt.id_ctr`, -trimis `NULL` cand linia nu vine de pe contract). Pentru orice `pack_facturare.ntip` in afara lui -`(2, 6, 26, 52)`, `V_OPT_FACTURARE` **ramane neinitializat** (blocul IF nu ruleaza deloc) — echivalent -cu "nu conteaza", ramura de contract nu se poate nimeri. - -## 2. Ce inseamna `V_OPT_FACTURARE` si cum se coreleaza cu VFP - -Din `PACK_FACTURARE` (alte proceduri, aceeasi coloana `CONTRACTE.OPT_FACTURARE`) si din -`COMUN\clase\ofacturare.vc2` / `COMUN\programe\oproceduri_facturare.prg` (proprietatea locala -`poArt.opt_facturare`, populata la citirea contractului, in oglinda cu coloana Oracle): - -| Valoare | Tip facturare | Dovada | -|---|---|---| -| `0` (sau lipsa contract) | Articol normal, nelegat de contract | `ofacturare.vc2:13055` `If poArticol.opt_facturare = 0` | -| `1`, `2` | **Rata** (scadentar contract, `CTR_SCADENTAR`) — linia nu trece prin `adauga_articol_factura`, ci prin `adauga_rata_factura` (`ofacturare.vc2:14041-14050`) | `ofacturare.vc2:14041` `If Inlist(poArt.opt_facturare,1,2)`; `PACK_FACTURARE:2898,2915` (`CTR_SCADENTAR`, doar pt. `OPT_FACTURARE IN (1,2)`) | -| `3` | **Articol de pe contract**, pret din `CTR_ARTICOLE` | `PACK_FACTURARE:2699,2820` (`CTR_ARTICOLE` doar pt. `OPT_FACTURARE = 3`); `ofacturare.vc2:14750` (`poDate.tip = 2 And poArticol.opt_facturare = 3`) | -| `4` | Fallback intern cand contractul nu (mai) exista (`NO_DATA_FOUND`) | `PACK_FACTURARE:5047-5048` | - -VFP nu trimite `V_OPT_FACTURARE` ca parametru al `adauga_articol_factura` — coreleaza doar indirect, -prin faptul ca proprietatea locala `poArt.opt_facturare` (citita din acelasi `CONTRACTE.OPT_FACTURARE` -cand grila s-a populat) decide **ce RPC se apeleaza** (`adauga_rata_factura` pentru 1/2, -`adauga_articol_factura` pentru orice altceva, inclusiv 3), nu ce ramura Oracle se executa in interior -— aceea o recalculeaza serverul singur, din `V_ID_CTR`. - -## 3. Ramura cu ramura pe `V_OPT_FACTURARE` (si pe `pack_facturare.ntip`, care are prioritate) - -`CASE` la `PACK_FACTURARE:5052-5220`. Ramurile 1-3 sunt selectate dupa `pack_facturare.ntip`, nu -dupa `V_OPT_FACTURARE` — ramura 4 (contract) se testeaza **doar daca niciuna din primele trei nu s-a -potrivit**. - -| Selector | Tip facturare | Pretul | Sursa re-derivarii | Citat | -|---|---|---|---|---| -| `ntip IN (3,21,28,42,47)` | Facturare/aviz din **comenzi** | **Re-derivat, dar constrans sa se potriveasca cu ce a trimis VFP** — `SELECT A.PRET ... WHERE A.PRET = V_PRET_TEMP` | `COMENZI_ELEMENTE` + `CRM_POLITICI_PRETURI`/`PRET_ART`, filtrat pe `A.PRET = V_PRET_TEMP`; daca nu exista rand cu exact acel pret, `NO_DATA_FOUND` **nu e tratat** — procedura pica cu eroare Oracle netratata | `:5053-5078` | -| `ntip = 4` | Facturare din **avize** | **Re-derivat necondiționat, din documentul sursa** — `SELECT DISTINCT A.PRET ... INTO V_PRET` fara filtru pe pretul trimis | `VANZARI_DETALII` (linia din avizul sursa), filtrata pe articol/politica/gestiune/discount/cont, dar **nu** pe pret | `:5080-5103` | -| `ntip = 45` | Facturare **restaurant** | **Respectat** — `V_PRET := V_PRET_TEMP` setat **inainte** de SELECT (`:5109`); SELECT-ul recalculeaza doar `V_PROC_TVAV` si `V_PRET_ACHIZITIE` (cost, nu pret de vanzare) | n/a pentru pret; TVA din `JTVA_COLOANE`, cost din `CRM_POLITICI_PRET_ART.PRETFTVA` | `:5104-5145` | -| `V_OPT_FACTURARE = 3` (deci `ntip IN (2,6,26,52)` **si** contract cu `OPT_FACTURARE=3`/`NULL`) | Facturare/aviz **de pe contract** | **Re-derivat daca contractul are pret setat** — `DECODE(A.PRET_UNITAR, 0, V_PRET_TEMP, A.PRET_UNITAR)`; daca `PRET_UNITAR = 0` sau nu exista rand `CTR_ARTICOLE`/politica potrivita (`NO_DATA_FOUND`), cade pe `V_PRET_TEMP` | `CTR_ARTICOLE.PRET_UNITAR` (prin `CRM_POLITICI_PRET_ART`, `ID_POL_ART`) | `:5146-5185` (branch), `:5181-5184` (fallback `V_PRET_TEMP` in exceptie) | -| `ELSE` (implicit — orice altceva, inclusiv linii normale fara contract) | Standard | **Respectat** — `V_PRET := V_PRET_TEMP` | n/a; TVA din `JTVA_COLOANE` dupa `V_ID_JTVA_COLOANA` trimis de VFP | `:5187-5203` | - -**Observatie de prioritate:** daca o linie e simultan "din comanda" (`ntip IN (3,21,28,42,47)`) si -are si `V_ID_CTR` completat, ramura de comenzi castiga — `V_OPT_FACTURARE` nici nu se calculeaza -(blocul de la `:5039` ruleaza doar pentru `ntip IN (2,6,26,52)`). Ramurile nu se pot suprapune in -productie curenta, dar asta inseamna ca **orice extindere viitoare a lui `ntip`** (de ex. daca #13 -introduce un tip nou de document care refoloseste `adauga_articol_factura` pentru reemitere) trebuie -verificata explicit fata de acest `CASE` — un `ntip` nou care nu intra in niciuna din listele -`(3,21,28,42,47)` / `4` / `45` cade automat fie pe ramura de contract (daca are `V_ID_CTR`), fie pe -`ELSE`. - -## 4. Reemiterea — detaliu (vezi si verdictul de mai sus) - -Procedura **nu primeste si nu poate primi** vreun semnal de tip "acesta e un apel de reemitere, nu -recalcula". Comportamentul e identic la emiterea initiala si la orice apel ulterior cu aceiasi -parametri. Riscul descris in plan (sectiunea H) se confirma integral pentru ramura de contract -(`V_OPT_FACTURARE = 3`): daca `CTR_ARTICOLE.PRET_UNITAR` s-a modificat intre emiterea initiala si -reemitere, factura reemisa va lua **pretul curent din contract**, nu pretul confirmat pe ecran de -utilizator inainte de reemitere — chiar daca utilizatorul n-a atins linia. - -Pentru ramurile "avize" (`ntip = 4`) si "comenzi" (`ntip IN (3,21,28,42,47)`), acelasi risc structural -exista (pretul se re-citeste din documentul sursa la fiecare apel), dar **nu sunt cazul confirmat de -#13** — planul S10 spune explicit "se verifica pe factura din contract, care e cazul confirmat" — nu -s-a cerut si nu s-a facut inventar suplimentar pentru daca fluxul de reemitere din #13 (etapa II, -stergere+regenerare cu aceleasi linii) ar trece vreodata prin aceste doua ramuri. **De retinut daca -#13 extinde reemiterea si la facturi/avize provenite din comanda sau din alt aviz** — pe ambele, -re-derivarea e chiar mai stricta decat pe contract (ramura de comenzi cade cu eroare netratata daca -pretul nu se potriveste exact; ramura de avize suprascrie necondiționat). - -## 5. Mecanism de "nu re-deriva" - -**Nu exista.** Niciun parametru boolean, nicio valoare santinela, nicio ramura in `CASE` -(`:5052-5220`) care sa respecte pretul primit *pentru ca i s-a cerut explicit*. Ramurile "implicita" -si "restaurant" respecta pretul doar pentru ca structural nu au de unde re-deriva altceva (nu exista -document sursa de recitit), nu pentru ca ar exista o comutare intentionata. Orice solutie de tip -"pretul vine din formular, nu se re-deriva" (cum sugereaza planul, sectiunea H) **ar trebui -implementata in VFP inainte de apel** (de exemplu, nu retrimite `V_ID_CTR` la reemitere daca vrei sa -eviti ramura de contract) — nu exista un parametru in pachet pe care VFP l-ar putea seta ca sa -dezactiveze re-derivarea, iar pachetul **nu se modifica** (decizia 27-bis, respectata — aceasta e o -constatare, nu o propunere). - -## 6. Discountul si TVA-ul - -- **Discountul (`V_DISCOUNT_UNITAR`) nu e niciodata re-derivat.** Parametrul intra direct in - `INSERT INTO VANZARI_DETALII_TEMP (..., DISCOUNT_UNITAR, ...) VALUES (..., V_DISCOUNT_UNITAR, ...)` - (`:5236`, `:5266`) — nicio ramura din `CASE` il citeste sau il modifica. Tratament **diferit** de - pret: discountul e mereu respectat, indiferent de ramura. -- **TVA-ul (`V_PROC_TVAV`) e mereu recalculat server-side, pe toate ramurile — dar din surse - diferite, nu dintr-un parametru brut.** Nu exista parametru `V_PROC_TVAV_TEMP` in semnatura; VFP - trimite doar `V_ID_JTVA_COLOANA` (un identificator de coloana de cota, nu cota insasi). Pe ramura - implicita si pe cea de contract-fara-potrivire, cota se cauta in `JTVA_COLOANE` dupa acel ID - (`:5188-5198`, `:5169-5179`); pe ramurile comenzi/avize/contract-cu-potrivire/restaurant, cota vine - din documentul sursa sau din politica de pret (`:5058`, `:5083`, `:5150`, `:5114`). Deci TVA-ul are - un tratament **mai strict** decat pretul: niciodata "pass-through" direct, intotdeauna o cautare, - doar sursa cautarii difera pe ramura. - -## 7. Cursul valutar si pretul in valuta - -- **Cursul (`V_CURS`) e mereu respectat ca valoare, pe toate ramurile.** Singura procesare e - `DECODE(V_CURS, 0, 1, V_CURS)` la `INSERT` (`:5270`) — inlocuieste 0 cu 1, altfel scrie exact ce a - trimis VFP. Procedura **nu interogheaza niciodata tabela `CURS`** pentru un curs "de azi" — nu are - de unde sa recalculeze cursul chiar daca ar vrea. -- **Identitatea valutei (`V_ID_VALUTA`, variabila locala, nu parametrul `V_ID_VALUTAD`) urmeaza - acelasi tipar ca pretul, ramura cu ramura:** re-derivata din sursa pe comenzi (`C.ID_VALUTA`, - `:5059`) si avize (`A.ID_VALUTA`, `:5084`), re-derivata din contract pe ramura `V_OPT_FACTURARE=3` - (`B.ID_VALUTA`, `:5151`, cu fallback `V_ID_VALUTA_TEMP` in exceptie, `:5182`), respectata pe - implicita si restaurant (`V_ID_VALUTA := V_ID_VALUTA_TEMP`, `:5110`, `:5201`). -- **Consecinta pentru reemiterea unui document in valuta pe contract:** daca politica de pret a - contractului (`CRM_POLITICI_PRET_ART`, prin `CTR_ARTICOLE.ID_POL_ART`) a fost schimbata intre timp - la o alta valuta, reemiterea ar scrie articolul in **noua** valuta a politicii, dar cu **cursul** - trimis de VFP (posibil cursul valutei vechi, daca VFP nu a fost actualizat sa retrimita cursul - corect pentru noua valuta) — o sursa suplimentara de neconcordanta, nesemnalata explicit in plan. - Nu s-a gasit nicio validare in procedura care sa verifice ca `V_CURS` corespunde valutei - re-derivate `V_ID_VALUTA`. - -## Completare: nota contabila a politicii - -Sursa: aceeasi, `contabilizeaza_articol` la `PACK_FACTURARE:7173-7547` (in fisierul din -`SCRIPTURI_CLAR`; corpul relevant pentru articol simplu — nu compus — e la `:7391-7544`), plus -`scrie_nota` la `:12329-12561`. - -### 1. Cum ajunge de la `id_pol` la nota contabila — ia toate randurile setului, fara distributie - -`cursor_articol` (`:7218-7271`): - -```sql -FROM CRM_POLITICI_PRET_ART A -LEFT JOIN CRM_POLITICI_PRETURI B ON A.ID_POL = B.ID_POL -LEFT JOIN CRM_NOTE_VANZARI C ON B.ID_NOTA = C.ID_NOTA -LEFT JOIN NOTE_CONTABILE D ON C.ID_SET = D.ID_SET -WHERE A.ID_POL = detalii_articol.id_pol AND A.ID_ARTICOL = detalii_articol.id_articol -``` - -E un `JOIN` simplu pe `ID_SET`, **fara `ROWNUM`, fara agregare, fara filtru suplimentar pe -`NOTE_CONTABILE`**. Daca setul are `N` randuri, cursorul intoarce `N` randuri pentru aceeasi -combinatie `id_pol`/`id_articol`. Bucla care il consuma (`:7393-7542`, -`OPEN cursor_articol; FETCH ...; WHILE cursor_articol%FOUND LOOP ... FETCH ...; END LOOP;`) executa -**intregul corp — `scrie_nota`, `descarca_gestiune`, `scrie_discount` — o data pentru fiecare rand -din set**, cu **aceeasi cantitate si acelasi pret intreg de fiecare data**, nu impartite intre -randuri. Nu exista nicio coloana `ORDINE` in tot pachetul (cautare `ORDINE` in fisier: zero -rezultate) si nicio logica de distributie procentuala intre randurile unui set. - -**Consecinta directa pentru reteta aprobata (J-quater):** daca politica tehnica pentru contul de -venit ar ajunge sa foloseasca o nota al carei `ID_SET` are mai mult de un rand in -`NOTE_CONTABILE` (cazul general — pana la 30 de randuri pe unele seturi din baza, per verificarea ta -pe date vii), `contabilizeaza_articol` ar scrie **de N ori** aceeasi suma in contabilitate si ar -apela `descarca_gestiune` de N ori pentru aceeasi linie de vanzare — dublare de venit si dublare de -descarcare de gestiune, nu doar zgomot. Pasul 2 al retetei ("cauta o politica a carei nota are deja -`SCC`-ul calculat") **trebuie sa garanteze un singur rand `NOTE_CONTABILE` per `ID_SET` ales**, nu -doar un rand cu `SCC`-ul potrivit — verificarea facuta de tine pe cele 7 note active (exact un rand -per set) e conditia care face reteta sigura azi, dar nu e impusa de cod, e o coincidenta a datelor -curente. - -### 2. `NOTE_CONTABILE.PTVA` — nu e citit deloc de `contabilizeaza_articol` - -Lista de coloane a `cursor_articol` (`:7230-7260`) selecteaza explicit -`ID_NOTA, PRETURI_CU_TVA, ID_VENCHELT, ID_SECTIE, ID_SET, EXPLICATIE, SCD, ASCD, SCC, ASCC, CU_TVA, -IN_VALUTA` din lantul `CRM_POLITICI_PRET_ART -> CRM_POLITICI_PRETURI -> CRM_NOTE_VANZARI -> -NOTE_CONTABILE` — **`D.PTVA` nu apare in acest `SELECT`**. Coloana exista pe tabel (confirmat de -datele tale vii — `21`, `5`, `0`), dar `contabilizeaza_articol` n-o citeste niciodata. - -Cota de TVA folosita efectiv in scriere e alta — vine din apelul catre `scrie_nota` (`:7444-7467`), -unde parametrul `V_PTVA` primeste `detalii_articol.proc_tvav * 100 - 100` (`:7464`), adica **cota -calculata deja in `adauga_articol_factura`** (din `JTVA_COLOANE` sau din documentul sursa, vezi -sectiunea 3 de mai sus), nu din nota. Deci **raspunsul direct la intrebarea ta: daca articolul are -21% dar nota politicii are `PTVA=5`, nu se intampla nimic — `PTVA` de pe nota e complet ignorat, -cota folosita e cea a articolului/documentului, nu a notei.** Coloana `NOTE_CONTABILE.PTVA` e moarta -din perspectiva acestei proceduri (posibil folosita in alt modul, neverificat aici). - -### 3. `CU_TVA` si `IN_VALUTA` de pe nota - -Ambele se citesc din `NOTE_CONTABILE` (`crs_rand_articol.cu_tva`, `crs_rand_articol.in_valuta`, -`:7253`, `:7254-7260`) si se transmit mai departe la `scrie_nota` ca parametri (`V_CU_TVA`, -`V_IN_VALUTA`), unde controleaza mecanic, nu semantic tot ce e legat de pret: - -- **`V_CU_TVA`** (`scrie_nota:12416-12422`): daca `= 0`, suma acumulata in `V_INCASAT_CALCUL` e - `V_SUMA_FARA_TVA`; daca `= 1`, e `V_SUMA_CU_TVA`. Separat, la `:12537-12558`, **doar cand - `V_CU_TVA = 1` se scrie si o linie separata de TVA** (`pack_facturare.scrie_tva(...)`, cont - `4427`/etc.). Deci `CU_TVA` decide daca acest rand din notă genereaza o inregistrare contabila - separata pentru TVA sau nu — nu suprascrie si nu recalculeaza cota, doar comuta daca linia de TVA - se scrie. -- **`V_IN_VALUTA`** (`scrie_nota:12391-12404`, `:12492-12507`): daca `= 1`, se calculeaza si - `V_SUMA_VAL`/`V_SUMA_FARA_TVA_VAL`/etc. (sumele in valuta straina) si randul `ACT_TEMP` scrie - `ID_VALUTA = V_ID_VALUTA` (valuta reala a articolului) si `CURS = V_CURS`; daca `= 0`, randul - scrie `ID_VALUTA = pack_facturare.nid_moneda_nationala` si `CURS = 0`, indiferent de valuta reala - a articolului. - **Raspuns la "ce se intampla daca `IN_VALUTA=1` pe nota dar documentul e in lei":** nu apare nicio - eroare si nicio validare incrucisata intre `IN_VALUTA` de pe nota si valuta reala a documentului — - procedura calculeaza pur si simplu `V_SUMA_VAL` folosind `V_ID_VALUTA` si `V_CURS` primite din - `detalii_articol` (care, pe un document in lei, sunt deja moneda nationala si curs implicit 1). - Rezultatul practic: `ACT_TEMP.SUMA_VAL` se populeaza redundant (cu aceeasi valoare ca `SUMA`, - scalata la curs 1) in loc sa ramana `0`, iar `ID_VALUTA` de pe randul contabil ramane oricum - moneda nationala (pentru ca asta e `detalii_articol.id_valuta` pe un document in lei) — o - inconsistenta cosmetica in `ACT_TEMP` (rand cu `SUMA_VAL` populat desi `CURS` real e 1), nu o - eroare de suma. Relevant pentru politicile `SERVICII VALUTA` / `COMISION INTERMEDIERE` - (`SCC=704`, candidate la pasul 2 al retetei): daca oricare document in lei ar ajunge sa foloseasca - una din ele, ar produce astfel de randuri cosmetic-inconsistente, fara sa strice suma facturata. - -### 4. `ASCD`/`ASCC` si `ID_PARTD`/`ID_PARTC` - -- **`ASCD`/`ASCC` nu sunt obligatorii — au fallback automat cand sunt nule**, exact pe ramura care - se aplica pe facturi (`:7409-7428`): - ``` - V_ASCD := NVL(crs_rand_articol.ascd, - PACK_FACTURARE.GetAnaliticByGrupUtilizatori(pack_facturare.nid_util, V_SCD)); - ... - V_ASCC := NVL(crs_rand_articol.ascc, - PACK_FACTURARE.GetAnaliticByGrupUtilizatori(pack_facturare.nid_util, V_SCC)); - ``` - Cand nota nu are analitic explicit (cazul majoritatii notelor reale, per verificarea ta), se - deriva unul din grupul de utilizatori al celui care factureaza, functie de contul `SCD`/`SCC`. Nu - s-a verificat in aceasta sesiune ce intoarce `GetAnaliticByGrupUtilizatori` cand nici grupul de - utilizatori nu are un analitic configurat (posibil `NULL` mai departe, netratat explicit aici). -- **`ID_PARTD`/`ID_PARTC` nu sunt citite de la nota deloc.** `cursor_articol` nu le selecteaza (nu - exista `D.ID_PARTD`/`D.ID_PARTC` nicaieri in fisier — verificat). Cele doua coloane de pe - `ACT_TEMP` se calculeaza in `scrie_nota`/`scrie_tva` direct din **codul contului** (`V_SCD`, - `V_SCC`) si din variabile de sesiune ale pachetului (`pack_facturare.nid_part`, - `nid_part_rez`, `nid_partc`), nu din nota: `ID_PARTD` e un `CASE` pe prefixul lui `V_SCD` - (`41%`/`46%`/`45%`/`357`) la `:12510-12515`; `ID_PARTC` e un `DECODE` pe `V_SCC` - (`419`/`4111`/`357`) la `:12523-12530`. Daca `NOTE_CONTABILE` are coloane `ID_PARTD`/`ID_PARTC`, - ele sunt irelevante pentru `contabilizeaza_articol` — partenerul contabil vine intotdeauna din - contextul documentului (`pack_facturare.nid_part`), nu din configurarea notei. - -### 5. Politica fara `ID_NOTA` (`HOTEL TAXE`, `HOTEL CAZARE`) - -**Nu exista un cod `FACT-0xx` dedicat pentru acest caz — spre deosebire de articolul care lipseste -din politica (`FACT-024`, `:7278-7302`), aici procedura nu detecteaza si nu semnaleaza explicit -situatia.** Lantul de `LEFT JOIN` din `cursor_articol` (`:7261-7271`) e construit sa supravietuiasca -oricarui inel lipsa: `A` (politica-articol, gasita — altfel s-ar fi luat deja `FACT-024` mai -devreme) se pastreaza chiar daca `B.ID_NOTA` e `NULL` — `C` si `D` devin pur si simplu toate -`NULL` pe acel rand, cursorul tot intoarce **un rand** (`cursor_articol%FOUND = TRUE`), nu zero. - -In bucla (`:7396-7541`), asta inseamna: -- `crs_rand_articol.scd`, `.ascd`, `.scc`, `.ascc`, `.cu_tva`, `.in_valuta`, `.explicatie` — toate - `NULL`. -- Ramura de calcul `V_ASCD := NVL(NULL, GetAnaliticByGrupUtilizatori(nid_util, NULL))` — analitic - calculat pe un cont `NULL`, deci probabil tot `NULL` (netestat aici ce intoarce functia pe - argument `NULL`). -- `scrie_nota` e apelata cu `V_SCD = NULL`, `V_SCC = NULL` — insereaza in `ACT_TEMP` un rand cu - conturile de debit/credit **nule**. - -Daca `ACT_TEMP.SCD`/`ACT_TEMP.SCC` au constrangere `NOT NULL` pe schema, `INSERT`-ul ar pica cu o -eroare Oracle generica (`ORA-01400`), nu cu un mesaj `FACT-0xx` prietenos ca la celelalte cazuri -tratate explicit — **neverificat in aceasta sesiune** (DDL-ul `ACT_TEMP` nu e in exportul -`PACK_FACTURARE`). In orice caz, **comportamentul e cel putin la fel de riscant ca lipsa unui -articol din politica**, dar fara plasa de siguranta explicita din cod — de tratat cu atentie daca -reteta aprobata ajunge sa produca vreodata o politica fara `id_nota` (nu pare sa fie cazul retetei -J-quater, care cere explicit gasirea unei note existente, dar `HOTEL TAXE`/`HOTEL CAZARE` arata ca -starea "politica fara nota" chiar exista azi in baza vie, pe alte fluxuri). - -## Ce nu s-a putut stabili si de ce - -- **Comportamentul exact al viitorului flux de reemitere din #13** (etapa II) fata de aceasta - procedura — planul S10 cere verificarea "pe factura din contract", nu inventarul complet pentru - comenzi/avize; nu s-a extins cercetarea acolo (in afara observatiei de la punctul 4, care ramane o - constatare structurala, nu un verdict testat pe fluxul de reemitere, care **inca nu exista in - cod** — decizia 30, nimic implementat). -- **Ce se intampla la eroarea netratata din ramura de comenzi** (`NO_DATA_FOUND` cand - `A.PRET = V_PRET_TEMP` nu gaseste rand, `:5057-5078`) — daca propaga ca eroare Oracle bruta pana la - utilizator sau daca `goExecutor` o intercepteaza generic; n-a fost verificat (nu era in perimetrul - celor 7 intrebari, dar e un risc adiacent gasit din citirea codului). -- **Cate contracte reale au azi `CTR_ARTICOLE.PRET_UNITAR` diferit de pretul ultimei facturi emise** - — verificare pe date vii, nefacuta (cercetare read-only, fara acces la interogari live in aceasta - sesiune). -- **Discrepanta de numerotare fata de `docs\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql`** (fisierul - cerut in prompt nu exista pe disc) — semnalata la inceputul raportului, nu blocheaza verdictul - pentru ca fisierul gasit in `SCRIPTURI_CLAR` are acelasi nume/data si continut structural identic. diff --git a/docs/cercetare/s10_rederivare_pe_calea_reemiterii.md b/docs/cercetare/s10_rederivare_pe_calea_reemiterii.md deleted file mode 100644 index 43f8bce..0000000 --- a/docs/cercetare/s10_rederivare_pe_calea_reemiterii.md +++ /dev/null @@ -1,470 +0,0 @@ -# S10 — Se re-deriva valorile liniei pe calea de REEMITERE? - -Stare: **TERMINAT.** - -Surse: -- `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` (17217 linii; - `versiune_db.txt` = `2026_08_09_02`) — prescurtat **`PF:`**; -- corpul viu din `all_source` (`MARIUSM_AUTO.PACK_FACTURARE`, `PACKAGE BODY`, **VALID**, - `last_ddl_time = 2026-08-09 20:03:50`) — prescurtat **`LIVE:`**; -- `COMUN\clase\ofacturare.vc2`, `COMUN\programe\ofacturare_editare.prg`. - -**Exportul = ce ruleaza.** Verificat pe cinci ancore; in zona relevanta offsetul e constant, -`LIVE + 1240 = PF`: `LIVE:3749`↔`PF:4989`, `LIVE:3840`↔`PF:5080`, `LIVE:3909`↔`PF:5149`, -`LIVE:3982`↔`PF:5222`, `LIVE:4044`↔`PF:5284`, `LIVE:12466`↔`PF:13706`, `LIVE:13735`↔`PF:14975`. -**Nu generalizez offsetul in afara zonei masurate** — il dau doar ca dovada de identitate. - ---- - -## 1. Unde sta `SELECT`-ul de la `PACK_FACTURARE:5149-5166` si cine il cheama - -**Numarul de linie e corect**, verificat direct pe fisier. `PF:5149-5166`: - -``` -5149 SELECT DECODE(A.PRET_UNITAR, 0, V_PRET_TEMP, A.PRET_UNITAR), -5150 B.PROC_TVAV, -5151 B.ID_VALUTA, -5152 A.PRET_CU_TVA, -5153 C.IN_STOC -5154 INTO V_PRET, V_PROC_TVAV, V_ID_VALUTA, V_PRETURI_CU_TVA, V_IN_STOC -5159 FROM CTR_ARTICOLE A -5160 LEFT JOIN CRM_POLITICI_PRET_ART B ON A.ID_POL_ART = B.ID_POL_ART -5162 LEFT JOIN NOM_ARTICOLE C ON B.ID_ARTICOL = C.ID_ARTICOL -5164 WHERE B.ID_ARTICOL = V_ID_ARTICOL AND A.ID_CTR = V_ID_CTR AND B.ID_POL = V_ID_POL; -``` - -**Procedura**: `adauga_articol_factura` — spec `PF:537`, corp **`PF:4989`**, -`END adauga_articol_factura;` **`PF:5284`**. **Deci DA, e `adauga_articol_factura`.** - -**CORECTIE fata de raportul rundei 9**: procedura **nu scrie in `VANZARI_DETALII`**. Se termina cu un -singur `INSERT`, `PF:5222`, in **`VANZARI_DETALII_TEMP`**: - -``` -5222 INSERT INTO VANZARI_DETALII_TEMP (... PRET, PRET_CU_TVA, PROC_TVAV, ... ID_VALUTA, ... IN_STOC ...) -5252 VALUES (... V_PRET, V_PRETURI_CU_TVA, V_PROC_TVAV, ... V_ID_VALUTA, ... V_IN_STOC ...) -``` - -Re-derivarea se produce deci **pe drumul catre temp**, nu dupa el. Afirmatia „`scrie_in_vanzari` -copiaza `PRET` neschimbat din temp" e adevarata (vezi 3) si totusi **irelevanta ca protectie**: -valoarea din formular e inlocuita **inainte** sa intre in temp. - -**Cine cheama procedura**: numai VFP-ul, din metoda de **salvare** a formularului: - -- `COMUN\clase\ofacturare.vc2:14069` — `frm_facturare_articole.do_scrie_articole` -- `COMUN\clase\ofacturare.vc2:18104` — `frm_facturare_articole2.do_scrie_articole` - -(`:14054` si `:18089` sunt variante comentate, cu bind `?`, ale aceluiasi apel.) -In pachet **nu exista apelant intern**: `grep 'adauga_articol_factura\b'` da doar `PF:537` (spec), -`PF:4989` (corp), `PF:5284` (`END`) si doua comentarii de antet (`PF:22`, `PF:1497`). - -**Traseul complet pe calea de scriere** (`frm_facturare_articole`): - -1. `ofacturare.vc2:13981` — `pack_facturare.initializeaza_date_factura(...)`, care primeste - `Alltrim(Str(poDate.Tip))` (`ofacturare.vc2:13994`) si il pune in variabila de pachet: - `PF:1882 pack_facturare.ntip := V_TIP;` (corp `PF:1808`, `END` la `PF:1917`). - **`ntip` = tipul documentului din formular** — acelasi care ajunge in `VANZARI.TIP` (`PF:13670`). - Tot aici, `PF:1835 DELETE FROM VANZARI_DETALII_TEMP;` si `PF:1836 nid_act := 0;`. -2. `ofacturare.vc2:14036-14115` — `SCAN` pe cursorul de grid `crsfactura`, cate un - `pack_facturare.adauga_articol_factura(...)` per linie (`:14069`), executat prin - `goExecutor.oExecute` (`:14105`). -3. `frm_facturare_articole.do_scrie_factura` — `scrie_factura2` (`ofacturare.vc2:14345`, `:14373`), - `scrie_factura_avize` (`:14318`) sau `scrie_factura_avize_retur` (`:14434`, `:14497`), care ajung - la `pack_facturare.scrie_in_vanzari` (`PF:13488`). - -**Raspuns Q1**: e `adauga_articol_factura`, si e pe calea de **scriere** a documentului (nu pe cea de -creare/incarcare din sursa) — dar tinta insertului e `VANZARI_DETALII_TEMP`, iar re-derivarea se -aplica **inainte** de insert, deci nimic de dupa temp nu o mai poate repara. - -### Poarta care decide re-derivarea - -`adauga_articol_factura` are un singur `CASE` (`PF:5052-5220`), pe **`pack_facturare.ntip`**: - -| `WHEN` | linii | conditie | -|---|---|---| -| comenzi | `PF:5053-5078` | `ntip IN (3, 21, 28, 42, 47)` | -| avize | `PF:5080-5103` | `ntip = 4` | -| restaurant | `PF:5104-5145` | `ntip = 45` | -| **contract** | `PF:5146-5185` | `V_OPT_FACTURARE = 3` | -| `ELSE` | `PF:5187-5218` | restul | - -`V_OPT_FACTURARE` se seteaza **numai** daca `ntip IN (2, 6, 26, 52)` (`PF:5039-5050`): -`SELECT NVL(OPT_FACTURARE, 3) INTO V_OPT_FACTURARE FROM CONTRACTE WHERE ID_CTR = V_ID_CTR;`, cu -`NO_DATA_FOUND -> V_OPT_FACTURARE := 4`. Pentru orice alt `ntip` ramane **NULL**, iar `WHEN NULL = 3` -e NULL -> fals -> se merge pe `ELSE`. **Implicitul e ramura care re-deriva**: `NVL(OPT_FACTURARE, 3)`. - -Tipurile (`COMUN\docs\tipuri_documente_facturare.md`): 2 = factura pe contract, 6 = contract in -valuta, 26 = aviz pe contract, 52 = contract factura valuta; 4 = factura din avize; -3/21/28/42/47 = din comenzi; 45 = restaurant. - -## 2. Cele cinci valori, una cate una - -Intai maparea parametrilor — ce **trimite** formularul (apel `ofacturare.vc2:14069-14091` confruntat -cu semnatura `PF:4989-5015`; 27 de parametri, pozitional, potrivire completa): - -| formular (`crsfactura` -> `poArt`) | parametru | -|---|---| -| `poArt.pretftva`/`pretctva`/`vpretftva`/`vpretctva` | `V_PRET_TEMP` | -| `poArt.id_valuta` | `V_ID_VALUTA_TEMP` | -| `poArt.cu_tva` | `V_PRETURI_CU_TVA_TEMP` | -| `poArt.gestionabil` | `V_IN_STOC_TEMP` | -| `poArt.id_jtva_coloana` | `V_ID_JTVA_COLOANA` | -| `poArt.id_pol` | `V_ID_POL` | -| `poArt.id_ctr` | `V_ID_CTR` | - -**`PROC_TVAV` nu e parametru.** Nu exista `V_PROC_TVAV_TEMP` in semnatura — cota de TVA a liniei se -calculeaza **intotdeauna** pe server, in **toate** ramurile. Formularul trimite `ID_JTVA_COLOANA`, din -care ramura `ELSE` deriva `PROC_TVAV`. Pentru `PROC_TVAV` intrebarea „vine din temp?" **nu are raspuns -„da" pe nicio ramura**; intrebarea reala e *din ce* se deriva. - -### Ramura contract (`V_OPT_FACTURARE = 3`, `PF:5146-5185`) - -- **`PRET`** — `DECODE(A.PRET_UNITAR, 0, V_PRET_TEMP, A.PRET_UNITAR)` (`PF:5149`). - **Se re-deriva** din `CTR_ARTICOLE.PRET_UNITAR` ori de cate ori aceasta e `<> 0`, indiferent daca - linia a fost atinsa pe ecran. Numai cand contractul are `PRET_UNITAR = 0` trece valoarea din - formular. **Capcana NULL**: `CTR_ARTICOLE.PRET_UNITAR` e `nullable=Y` (verificat pe DB); un NULL - **nu** potriveste literalul `0` in `DECODE`, deci rezultatul e **NULL**, nu `V_PRET_TEMP` — pretul - din formular se pierde si linia intra in temp cu `PRET` NULL. *(dedus din semantica `DECODE`, nu - rulat.)* -- **`PROC_TVAV`** — `B.PROC_TVAV` din `CRM_POLITICI_PRET_ART` (`PF:5150`). **Se re-deriva** din - politica de preturi, **nu** din `ID_JTVA_COLOANA` trimis de formular. Diferit de `ELSE`. -- **`ID_VALUTA`** — `B.ID_VALUTA` din `CRM_POLITICI_PRET_ART` (`PF:5151`). **Se re-deriva**; - `V_ID_VALUTA_TEMP` e ignorat. -- **`PRET_CU_TVA`** — `A.PRET_CU_TVA` din `CTR_ARTICOLE` (`PF:5152`). **Se re-deriva**; - `V_PRETURI_CU_TVA_TEMP` (`poArt.cu_tva`) e ignorat. -- **`IN_STOC`** — `C.IN_STOC` din `NOM_ARTICOLE` (`PF:5153`). **Se re-deriva din nomenclatorul - curent**; `V_IN_STOC_TEMP` (`poArt.gestionabil`) e ignorat. - -Cele cinci **nu merg impreuna** nici macar aici: `PRET` are un `DECODE` care uneori lasa valoarea din -formular sa treaca, celelalte patru sunt inlocuite **neconditionat**, din **trei tabele diferite** -(`CTR_ARTICOLE`, `CRM_POLITICI_PRET_ART`, `NOM_ARTICOLE`). - -**Iesirea de siguranta**: `EXCEPTION WHEN NO_DATA_FOUND` (`PF:5167-5185`) deriva `PROC_TVAV` din -`JTVA_COLOANE` si pune **toate** celelalte patru pe valorile din formular (`PF:5181-5184`): -`V_PRET := V_PRET_TEMP; V_ID_VALUTA := V_ID_VALUTA_TEMP; V_PRETURI_CU_TVA := V_PRETURI_CU_TVA_TEMP; -V_IN_STOC := V_IN_STOC_TEMP;`. Adica **daca linia nu mai are corespondent in contract, formularul -castiga** — exact comportamentul cerut de decizia 54, existent deja in cod, dar numai pe calea de -exceptie. - -### Ramura `ELSE` (`PF:5187-5218`) — cazul „bun" - -`PROC_TVAV` din `JTVA_COLOANE` pe `ID_JTVA_COLOANA` trimis de formular (`PF:5189-5192`), iar -`PF:5200-5203` pune explicit `V_PRET := V_PRET_TEMP`, `V_ID_VALUTA := V_ID_VALUTA_TEMP`, -`V_PRETURI_CU_TVA := V_PRETURI_CU_TVA_TEMP`, `V_IN_STOC := V_IN_STOC_TEMP`. -**Patru din cinci vin din formular; `PROC_TVAV` se deriva, dar din date trimise de formular.** - -### Ramura comenzi (`ntip IN (3,21,28,42,47)`, `PF:5053-5078`) - -``` -5057 SELECT A.PRET, -5058 NVL2(A.PTVA, ROUND((A.PTVA + 100) / 100, 2), C.PROC_TVAV), -5059 C.ID_VALUTA, B.PRETURI_CU_TVA, D.IN_STOC -5075 WHERE A.ID_COMANDA = V_ID_COMANDA AND A.ID_ARTICOL = V_ID_ARTICOL -5077 AND A.PRET = V_PRET_TEMP AND A.STERS = 0; -``` - -- **`PRET`** — formal `A.PRET` din `COMENZI_ELEMENTE`, dar `WHERE ... A.PRET = V_PRET_TEMP` - (`PF:5077`) **fixeaza** rezultatul pe pretul din formular. Practic **nu se schimba**. -- **`PROC_TVAV`, `ID_VALUTA`, `PRET_CU_TVA`, `IN_STOC`** — **se re-deriva** din - `COMENZI_ELEMENTE.PTVA` / `CRM_POLITICI_PRET_ART` / `CRM_POLITICI_PRETURI` / `NOM_ARTICOLE`. -- **Ramura nu are bloc `EXCEPTION`.** Un `SELECT INTO` gol da `NO_DATA_FOUND` (ORA-01403) - **netratat**, care urca prin `goExecutor` in VFP. Deci daca la reemitere pretul liniei a fost - modificat pe ecran (sau linia nu mai e in comanda), scrierea **cade cu eroare** — nu se re-deriva - tacit. Este o **a doua ramura de re-derivare**, pe care raportul rundei 9 nu o numara. - -### Ramura restaurant (`ntip = 45`, `PF:5104-5145`) - -`PF:5109-5112` pune explicit `V_PRET := V_PRET_TEMP`, `V_ID_VALUTA := V_ID_VALUTA_TEMP`, -`V_PRETURI_CU_TVA := V_PRETURI_CU_TVA_TEMP`, `V_IN_STOC := V_IN_STOC_TEMP`; `SELECT`-ul de la -`PF:5114-5139` deriva **doar** `V_PROC_TVAV` (din `JTVA_COLOANE`) si `V_PRET_ACHIZITIE`. -Patru din cinci vin din formular. - -### Ramura avize (`ntip = 4`) — la punctul 5 - -## 3. `UPDATE` / recalcul care suprascrie valorile DUPA insertul din temp - -**Nu exista, pentru cele cinci valori.** - -Scrierea temp -> definitiv, in `scrie_in_vanzari` (`PF:13488-13953`), e o copiere 1:1: - -``` -13705 INSERT /*+ APPEND */ -13706 INTO VANZARI_DETALII (... PRET, PROC_TVAV, ... ID_VALUTA, ... PRET_CU_TVA, DIFERENTA, ...) -13732 SELECT V_ID_VANZARE as ID_VANZARE, ... PRET, PROC_TVAV, ... ID_VALUTA, ... PRET_CU_TVA, DIFERENTA, ... -13757 FROM VANZARI_DETALII_TEMP; -``` - -> Capcana de cautare platita aici: `grep 'INSERT INTO VANZARI_DETALII'` da **zero** potriviri — -> hint-ul `/*+ APPEND */` rupe cuvintele pe doua randuri. Cautarea corecta e `INTO VANZARI_DETALII`. - -`IN_STOC` **nu apare** in lista de coloane (`PF:13707-13731`), si `VANZARI_DETALII` **nu are coloana -`IN_STOC`** (verificat pe DB: `all_tab_columns` -> 0 randuri). Traieste doar in -`VANZARI_DETALII_TEMP`, unde decide descarcarea de gestiune. Re-derivarea lui nu murdareste -documentul salvat, dar **schimba comportamentul de stoc** al reemiterii. - -Toate scrierile pe `VANZARI_DETALII` din pachet: - -| linie | procedura | ce face | -|---|---|---| -| `PF:13705` | `scrie_in_vanzari` (`PF:13488`) | `INSERT` 1:1 din temp | -| `PF:14974` | `finalizeaza_avize_lucrare` (`PF:14854`) | `INSERT` 1:1 din temp (cale avize de lucrare) | -| `PF:5560` | `sterge_factura` (`PF:5432`) | `SET STERS, ID_UTILS, DATAORAS` | -| `PF:5630` | `sterge_proforma` (`PF:5610`) | `SET STERS, ID_UTILS, DATAORAS` | -| `PF:14516` | `modifica_explicatie_articol` (`PF:14511`) | `SET EXPLICATIE, TAXCODE` | -| `PF:16017` | `actualizeaza_vanzari` (`PF:16012`) | `SET STERS = 0` | - -**Niciun `UPDATE` nu atinge `PRET`, `PROC_TVAV`, `ID_VALUTA`, `PRET_CU_TVA`.** -Confirmat si pe DB: `all_source` are exact doua `INTO VANZARI_DETALII` (`LIVE:12466`, `LIVE:13735`). - -Mutatiile pe `VANZARI_DETALII_TEMP` **intre** `adauga_articol_factura` si `scrie_in_vanzari`: - -| linie | procedura | ce schimba | -|---|---|---| -| `PF:5297` | `adauga_diferente_pret` | numai `DIFERENTA` (`PF:5344`: `UPDATE SET DIFERENTA = B.DIFERENTA`) | -| `PF:6860`, `PF:6864` | `scrie_factura_avize` | numai `CANTITATE` (split pe custodie); in `MERGE`, `PRET`, `PRET_CU_TVA`, `PROC_TVAV`, `ID_VALUTA`, `IN_STOC` sunt in clauza `ON`, nu in `UPDATE SET` | -| `PF:14020` | `scrie_seturi` | numai `ID_VANZARE_SET` | -| `PF:14057` | `scrie_seturi_proforma` | numai `ID_VANZARE_SET` (cale proforma) | -| `PF:12266` | `transfera_articol` | cale de transfer, in afara scrierii de factura | - -**`pack_auto.actualizeaza_deviz`** (`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\03\ff_2026_03_31_02_AUTO_PACK_AUTO.sql:692-733`) -atinge **`DEV_ORDL.PROC_TVAV`, `RUL.ID_FACT`, `NOM_LUCRARI.ID_FACT`** — **nu atinge `VANZARI_DETALII`**. - -Recalculele de totaluri (`PF:13760+`, `recalculeaza_totaluri_vanzari` `PF:16021`) si rotunjirile -lucreaza pe **`VANZARI`**, agregat — nu rescriu linia. - -**Raspuns Q3**: nu. Dupa insertul in temp, cele cinci valori nu mai sunt suprascrise nicaieri. -`DIFERENTA` si `CANTITATE` da, restul nu. - -## 4. Verdict: ajung valorile din formular neatinse in `VANZARI_DETALII`? - -**NU, pentru trei familii de tipuri. DA, pentru restul.** - -Se pierd **intr-un singur loc**: `PACK_FACTURARE.adauga_articol_factura`, in `CASE`-ul de la -**`PF:5052-5220`**, adica **inainte** de `INSERT INTO VANZARI_DETALII_TEMP` (`PF:5222`). Precis: - -- **contract** (`ntip IN (2,6,26,52)` cu `CONTRACTE.OPT_FACTURARE` NULL sau 3) — `PF:5149-5153`: - se pierd `PRET` (cand `CTR_ARTICOLE.PRET_UNITAR <> 0`), `PROC_TVAV`, `ID_VALUTA`, `PRET_CU_TVA`, - `IN_STOC`; -- **aviz** (`ntip = 4`) — `PF:5082-5086`: se pierd toate cinci, **inclusiv `PRET`, neconditionat**; -- **comenzi** (`ntip IN (3,21,28,42,47)`) — `PF:5057-5061`: se pierd patru; `PRET` e fixat de - `WHERE`, dar la nepotrivire scrierea **cade cu ORA-01403** in loc sa treaca. - -Pentru toate celelalte tipuri (1, 5, 10, 22, 45, ...) valorile din formular **ajung neatinse**: -ramurile `ELSE` (`PF:5200-5203`) si restaurant (`PF:5109-5112`) le copiaza explicit, iar traseul de -dupa (`PF:13705-13757`) e copiere 1:1. - -**Exceptie transversala, valabila pe TOATE tipurile**: `PROC_TVAV` **nu vine niciodata din formular** — -nu e parametru al procedurii. Pe calea „buna" se deriva din `JTVA_COLOANE` pe `ID_JTVA_COLOANA`-ul -trimis de formular, deci reproduce documentul **atat timp cat cota din `JTVA_COLOANE` nu s-a -schimbat**. La o modificare legala de cota, reemiterea schimba TVA-ul chiar si pe ramura `ELSE`. - -## 5. Sursa AVIZ (`ntip = 4`) — calea difera, si e mai grava - -Verbatim, `PF:5080-5103` (confirmat identic pe DB, `LIVE:3840-3863`): - -``` -5080 WHEN pack_facturare.ntip = 4 THEN -5081 -- facturare din avize -5082 SELECT DISTINCT A.PRET, -5083 A.PROC_TVAV, -5084 A.ID_VALUTA, -5085 A.PRET_CU_TVA, -5086 B.IN_STOC -5087 INTO V_PRET, V_PROC_TVAV, V_ID_VALUTA, V_PRETURI_CU_TVA, V_IN_STOC -5092 FROM VANZARI_DETALII A -5093 LEFT JOIN NOM_ARTICOLE B ON A.ID_ARTICOL = B.ID_ARTICOL -5095 WHERE A.ID_ARTICOL = V_ID_ARTICOL -5096 AND A.ID_POL = V_ID_POL -5097 AND NVL(A.ID_GESTIUNE, -1000) = V_ID_GESTIUNE -5098 AND A.DISCOUNT_UNITAR = V_DISCOUNT_UNITAR -5099 AND NVL(A.CONT, 'XXXX') = V_CONT -5100 AND A.ID_VANZARE IN -5101 (SELECT X AS ID_VANZARE -5102 FROM table(cast(CHARN2COLLECTION(pack_facturare.clistaid, ',') AS num_tab))); -``` - -Diferentele fata de contract, toate in defavoarea deciziei 54: - -1. **`PRET` se re-deriva neconditionat**, direct din `VANZARI_DETALII` al **avizului sursa**. Nu exista - `DECODE(..., 0, V_PRET_TEMP, ...)` si **nu exista `AND A.PRET = V_PRET_TEMP`** in `WHERE` (spre - deosebire de ramura comenzi, `PF:5077`). Un pret modificat pe ecran la reemitere e **inlocuit - tacit** cu pretul din aviz. **Aceasta e ramura cea mai agresiva din tot `CASE`-ul.** -2. **Nu are bloc `EXCEPTION`.** Ramura contract are `NO_DATA_FOUND` care cade inapoi pe formular - (`PF:5167-5185`); aici, orice modificare pe ecran a `DISCOUNT_UNITAR`, `CONT`, `ID_GESTIUNE` sau - `ID_POL` scoate randul din `WHERE` -> **ORA-01403** urcata in VFP. `SELECT DISTINCT` peste mai - multe avize cu preturi diferite pe acelasi articol poate da si **ORA-01422** (`TOO_MANY_ROWS`). -3. **Nu filtreaza `A.STERS = 0`**, desi `VANZARI_DETALII` are coloana `STERS` (verificat pe DB) si - `sterge_factura` o foloseste (`PF:5560`). Linii sterse ale avizului sursa pot fi citite. -4. Depinde de `pack_facturare.clistaid` — lista de avize sursa, trimisa de VFP prin - `initializeaza_date_factura` (`poDate.listaid`, `ofacturare.vc2:13992`). La reemitere, `clistaid` - trebuie repopulata cu aceleasi avize, altfel `WHERE` nu potriveste nimic -> ORA-01403. - -**Ce nu difera**: dupa `CASE` traseul e identic (`INSERT` in temp `PF:5222`, apoi copiere 1:1 -`PF:13705`), iar `scrie_factura_avize` (`PF:6692`) modifica in temp **numai `CANTITATE`** -(`PF:6860`, `PF:6864`) — nu re-deriva nimic in plus. - -**Raspuns Q5**: calea difera, si e **mai stricta**. Pe contract exista o portita (`PRET_UNITAR = 0` -si `NO_DATA_FOUND`) prin care valorile din formular trec; pe aviz **nu exista niciuna** — ori se -re-deriva tot, ori pica cu eroare. - -## 6. Consecinta pentru decizia 54, la nivel de contract - -### 6.1 Se poate curat VFP? **Nu.** - -Confirm afirmatia rundei 9 — dar acum cu dovada, nu prin absenta: - -- **Semnatura nu are flag.** `PF:4989-5015`: 27 de parametri, toti date de linie; ultimii doi - (`V_TAXCODE`, `V_LOT`) sunt `DEFAULT NULL`, niciunul nu e comutator. -- **Globalele pachetului nu au flag.** `PF:126-212` — stare de sesiune (`ntip`, `clistaid`, - `nid_part`, ...), niciun comutator de comportament pe re-derivare. -- **Poarta nu e influentabila legitim din VFP.** `ntip` ajunge in `VANZARI.TIP` (`PF:13670`) — a-l - falsifica inseamna a schimba tipul documentului. `CONTRACTE.OPT_FACTURARE` e date de contract, nu - parametru de apel. -- **Singurele parghii VFP care ar devia pe calea `NO_DATA_FOUND` sunt, ambele, de aceeasi natura ca - varianta (d) deja respinsa:** - - `V_ID_CTR = NULL` — respinsa; `PF:5280` duce `V_ID_CTR` in `temp.ID_CTR`, iar `PF:13755` in - `VANZARI_DETALII.ID_CTR`, deci legatura se pierde permanent; - - `V_ID_POL = NULL` — **acelasi defect, alta coloana**: `PF:5258` duce `V_ID_POL` in `temp.ID_POL`, - `PF:13737` in `VANZARI_DETALII.ID_POL`; in plus ar rupe ramura aviz, care potriveste pe - `A.ID_POL = V_ID_POL` (`PF:5096`). **Nu o propun** — o mentionez ca sa nu fie redescoperita ca - „solutie" intr-o runda urmatoare. - -**Concluzie: decizia 54 cere obligatoriu modificare in `pack_facturare`.** - -### 6.2 Ce forma trebuie sa aiba semnalul - -Doua forme sunt inerte pentru apelantii de azi: - -- **(i) variabila noua de pachet** („regenerare in curs"), implicit `0` = comportamentul actual, scrisa - de VFP **dupa** `initializeaza_date_factura` si inainte de bucla de `adauga_articol_factura`; -- **(ii) parametru `DEFAULT 0`** adaugat **dupa** `V_LOT` in semnatura — apelantii de azi trimit 27 de - argumente pozitional si raman valizi. - -**Recomand (i)**, din trei motive concrete: se potriveste cu stilul existent al pachetului (care e 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 face `DELETE FROM VANZARI_DETALII_TEMP` (`PF:1835`), deci flag-ul nu poate scapa in -documentul urmator din aceeasi sesiune. Atentie la ordine: VFP il seteaza **dupa** apelul de la -`ofacturare.vc2:13981`, nu inainte. Ambele variante modifica spec-ul, deci ambele forteaza -recompilarea dependentilor — nu e un criteriu de departajare. - -### 6.3 Ce face semnalul, cand e pornit - -**Nimic nou** — forteaza ramura care exista deja. Cea mai mica forma: un `WHEN` nou, **primul** in -`CASE`-ul de la `PF:5052`, cu **acelasi corp ca `ELSE`-ul de azi** (`PF:5189-5203`): `PROC_TVAV` din -`JTVA_COLOANE` pe `V_ID_JTVA_COLOANA`, si `V_PRET`/`V_ID_VALUTA`/`V_PRETURI_CU_TVA`/`V_IN_STOC` din -parametrii `*_TEMP`. `INSERT`-ul de la `PF:5222` ramane neatins. Acopera dintr-o data **toate trei** -ramurile problematice (contract, aviz, comenzi), pentru ca toate sunt in acelasi `CASE`. - -### 6.4 Trei consecinte de acceptat explicit, nu ocolite - -1. **`IN_STOC` ar veni din formular** (`poArt.gestionabil`) in loc de `NOM_ARTICOLE`. Nu e o problema - de fidelitate a documentului — `VANZARI_DETALII` **nu are coloana `IN_STOC`** (verificat pe DB) — - ci de **comportament de stoc** la reemitere. Ca reemiterea sa fie identica, incarcarea (S8) trebuie - sa dea valoarea cu care s-a scris documentul initial. **Azi nu o da**: loader-ul lui #6 o citeste - din nomenclatorul curent — `ofacturare_editare.prg:302-303`, - `left join nom_articole na on na.id_articol = v.id_articol`, cu comentariul care spune ca view-ul - n-o expune. **Flag-ul singur nu rezolva asta.** -2. **Se pierde o validare pe ramura comenzi.** `A.PRET = V_PRET_TEMP` (`PF:5077`) plus lipsa lui - `EXCEPTION` fac azi 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 o schimbare de comportament care trebuie spusa, nu - descoperita la S12. -3. **`PROC_TVAV` ramane derivat**, nu preluat. Reproducerea exacta a documentului cere ca S8 sa - pastreze `ID_JTVA_COLOANA` **si** ca `JTVA_COLOANE.COTA_TVA` sa nu se fi schimbat intre timp. Daca - se cere reproducere exacta si peste o modificare de cota, `PROC_TVAV` trebuie sa devina - **parametru** — schimbare mai mare decat flag-ul, de decis separat. - -### 6.5 Un obstacol de incarcare, gasit in treacat (pentru S8, nu pentru S10) - -View-ul pe care se sprijina incarcarea de azi, **`VVANZARI_ARTICOLE`**, expune (interogat pe DB): -`ID_VANZARE, ID_VANZARE_DET, ID_ARTICOL, CANTITATE, PRET, PRET_CU_TVA, PROC_TVAV, DISCOUNT_UNITAR, -ID_GESTIUNE, CONT, ID_VALUTA, ID_JTVA_COLOANA, SERIE, EXPLICATIE, TAXCODE, LOT, STERS, -ID_VANZARE_SET, PRET_ACHIZITIE, DENUMIRE, CODMAT, NUME_GESTIUNE, NUME_VAL`. - -**Nu expune `ID_POL` si nu expune `ID_CTR`** — exact cei doi pe care `adauga_articol_factura` ii cere -(`V_ID_POL`, `V_ID_CTR`) si pe care `VANZARI_DETALII` ii pastreaza. Reemiterea S9 **nu poate folosi -acest view ca atare**; ori se completeaza view-ul, ori loader-ul citeste direct din -`VANZARI_DETALII`. Nu e in perimetrul S10, dar blocheaza S9 daca nu e prins din timp. - ---- - -## Tabel sintetic — cele cinci valori pe ramura - -„temp" = valoarea trimisa de formular; „re-derivat" = citita din alta sursa si suprascrisa. - -| valoare | **contract** (2/6/26/52, `OPT_FACTURARE` NULL sau 3) | **aviz** (`ntip = 4`) | comenzi (3/21/28/42/47) | restaurant (45) | `ELSE` | -|---|---|---|---|---|---| -| `PRET_UNITAR` (`PRET`) | **re-derivat** din `CTR_ARTICOLE.PRET_UNITAR` daca `<> 0`; **temp** daca `= 0`; **NULL** daca e NULL (`PF:5149`) | **re-derivat** din `VANZARI_DETALII` al avizului, **neconditionat** (`PF:5082`) | **temp** de facto — `WHERE A.PRET = V_PRET_TEMP` (`PF:5077`); nepotrivire = ORA-01403 | **temp** (`PF:5109`) | **temp** (`PF:5200`) | -| `PROC_TVAV` | **re-derivat** din `CRM_POLITICI_PRET_ART` (`PF:5150`) | **re-derivat** din avizul sursa (`PF:5083`) | **re-derivat** din `COMENZI_ELEMENTE.PTVA` / politica (`PF:5058`) | **re-derivat** din `JTVA_COLOANE` (`PF:5114`) | **derivat** din `JTVA_COLOANE` pe `ID_JTVA_COLOANA` din formular (`PF:5189`) | -| `ID_VALUTA` | **re-derivat** din `CRM_POLITICI_PRET_ART` (`PF:5151`) | **re-derivat** din avizul sursa (`PF:5084`) | **re-derivat** din politica (`PF:5059`) | **temp** (`PF:5110`) | **temp** (`PF:5201`) | -| `PRET_CU_TVA` | **re-derivat** din `CTR_ARTICOLE` (`PF:5152`) | **re-derivat** din avizul sursa (`PF:5085`) | **re-derivat** din `CRM_POLITICI_PRETURI` (`PF:5060`) | **temp** (`PF:5111`) | **temp** (`PF:5202`) | -| `IN_STOC` | **re-derivat** din `NOM_ARTICOLE` (`PF:5153`) | **re-derivat** din `NOM_ARTICOLE` (`PF:5086`) | **re-derivat** din `NOM_ARTICOLE` (`PF:5061`) | **temp** (`PF:5112`) | **temp** (`PF:5203`) | - -`PROC_TVAV` **nu vine din temp pe nicio ramura** — nu e parametru al procedurii. -`IN_STOC` **nu ajunge in `VANZARI_DETALII`** (coloana nu exista) — traieste doar in temp, unde decide -descarcarea de gestiune. -Pe ramura contract, `EXCEPTION WHEN NO_DATA_FOUND` (`PF:5167-5185`) muta **toate cele cinci** pe -coloana „temp"; ramurile aviz si comenzi **nu au** un asemenea bloc. - -## Verificat direct vs. dedus - -**Verificat direct pe fisier SI confirmat pe DB** (`all_source`, `MARIUSM_AUTO.PACK_FACTURARE`, -`PACKAGE BODY`, VALID, `last_ddl_time = 2026-08-09 20:03:50`): -granitele lui `adauga_articol_factura`; toate cele cinci ramuri ale `CASE`-ului, ramura cu ramura; -textul integral al ramurii aviz; faptul ca insertul procedurii merge in `VANZARI_DETALII_TEMP`; ca -exista exact doua `INTO VANZARI_DETALII` in tot pachetul. - -**Verificat direct pe fisier** (nereconfirmat separat pe DB, dar in aceeasi zona cu offset masurat): -copierea 1:1 temp -> `VANZARI_DETALII` (`PF:13705-13757`); cele patru `UPDATE VANZARI_DETALII` si ce -coloane ating; mutatiile pe temp intre `adauga_articol_factura` si `scrie_in_vanzari`; corpul lui -`pack_auto.actualizeaza_deviz`. - -**Verificat pe metadatele DB**: `VANZARI_DETALII` nu are `IN_STOC`, are `STERS`; -`CTR_ARTICOLE.PRET_UNITAR` e `nullable=Y`; lista de coloane a lui `VVANZARI_ARTICOLE`. - -**Verificat direct in VFP**: apelantii lui `adauga_articol_factura`; maparea celor 27 de parametri; -`ntip := poDate.Tip`; loader-ul `IncarcaArticoleFactura` (`ofacturare_editare.prg:292-327`). - -**Dedus, nerulat**: comportamentul `DECODE` cu `PRET_UNITAR` NULL (semantica Oracle, nu test); -ORA-01403 / ORA-01422 pe ramurile fara `EXCEPTION` (din absenta blocului, nu din reproducere). - -**Din date de dev — NU e dovada, nu extrapolati**: `CTR_ARTICOLE` are **27** de randuri -(0 NULL, 10 cu `PRET_UNITAR = 0`, 17 cu `<> 0`) — deci **cazul NULL nu apare in dev**, ceea ce nu -spune nimic despre productie. `CONTRACTE.OPT_FACTURARE`: 176 NULL, 53 = 0, 9 = 1, 3 = 3, 1 = 4 — -adica pentru **176 + 3 din 242** de contracte `NVL(OPT_FACTURARE, 3)` da 3 si ramura re-deriva. - -**Neacoperit**: n-am rulat niciun flux real (nici VFP, nici o reemitere efectiva) — totul e analiza -statica plus interogari `SELECT` pe metadate. N-am citit corpurile `scrie_factura2` si -`finalizeaza_factura` integral, doar punctele in care ating `VANZARI_DETALII` / temp. - -## Corectii la rapoartele anterioare - -### `docs\cercetare\s10_pret_rederivat.md` (runda 9) - -1. **„exista exact o ramura care suprascrie pretul — contractul" — FALS.** Ramura **aviz** - (`ntip = 4`, `PF:5080-5103`) suprascrie `PRET` **neconditionat**, fara `DECODE` si fara filtru pe - pretul din formular — **mai agresiv decat contractul**. Ramura **comenzi** (`PF:5053-5078`) nu - suprascrie `PRET`, dar suprascrie celelalte patru si **arunca ORA-01403** la nepotrivire. - Ramurile care re-deriva sunt **trei**, nu una. -2. **„pe calea de scriere" — corect ca moment, gresit ca destinatie.** Se intampla la salvare, dar - insertul e in **`VANZARI_DETALII_TEMP`** (`PF:5222`), nu in `VANZARI_DETALII`. Diferenta conteaza: - explica de ce copierea „neschimbata" de mai tarziu nu protejeaza nimic. -3. **„nu exista niciun parametru sau flag care sa opreasca re-derivarea" — CONFIRMAT**, acum cu dovada - pozitiva (semnatura `PF:4989-5015`, globalele `PF:126-212`), nu prin absenta. - -### `docs\cercetare\s10_pret_contract_reemitere.md` (runda 13) - -1. **„acelasi `SELECT` alimenteaza cinci valori" — corect** (`PF:5149-5166`), dar de completat: - **nu merg impreuna** — `PRET` are `DECODE`, celelalte patru sunt neconditionate, si vin din trei - tabele diferite. -2. **„`IN_STOC` din nomenclatorul curent" — corect**, de completat cu faptul decisiv: - **`VANZARI_DETALII` nu are coloana `IN_STOC`** (verificat pe DB). Nu ajunge in document; efectul e - pe descarcarea de gestiune. -3. **„`scrie_in_vanzari` copiaza `PRET` neschimbat din temp" — corect** (`PF:13705-13757`), de marcat - insa ca **nu e o protectie**: dauna e amonte de temp. -4. Varianta **„avertizare + confirmare" ramane respinsa** (decizia 54) — nu am reargumentat-o. - -### `docs\plan_13_unificare_formular_facturare.md` - -- Trimiterea „`initializeaza_date_factura` (`PACK_FACTURARE:1808-1846`)" — corpul incepe la `PF:1808` - dar se termina la **`PF:1917`**. Citarile `:1835` / `:1836` din plan sunt corecte. diff --git a/docs/cercetare/s11_legaturi_id_vanzare.md b/docs/cercetare/s11_legaturi_id_vanzare.md deleted file mode 100644 index 8035429..0000000 --- a/docs/cercetare/s11_legaturi_id_vanzare.md +++ /dev/null @@ -1,379 +0,0 @@ -# S11 — Legaturile care raman pe `ID_VANZARE` la editarea prin regenerare - -Stare: **in lucru**. Vezi sectiunea STARE / CE RAMANE la final pentru progres curent. - -## Context (dat, nu se rediscuta) - -#13 etapa II: editarea unei facturi = regenerare. Documentul vechi e soft-sters (`STERS=1`) si -reemis in aceeasi tranzactie. `ID_FACT`, seria, numarul si data se pastreaza. **`ID_VANZARE` se -schimba** — documentul reemis primeste un `id_vanzare` nou din secventa. - -Sarcina: inventarul complet al legaturilor pe `VANZARI.ID_VANZARE` care se rup la aceasta -schimbare, clasificate (A) trebuie remigrat / (B) trebuie sters-refacut / (C) nu conteaza. - -## 0. Puncte de plecare (de verificat, nu de preluat pe incredere) - -- `docs\plan_13_unificare_formular_facturare.md` — sectiunea `#### S11` (linia ~3024) -- `docs\cercetare\idfact_refolosire_si_documente.md` -- `docs\cercetare\rec_cale_vanzari_detalii.md` -- `COMUN\docs\cercetare\rec_consumatori_vanzari.md` -- `docs\cercetare\cont_venit_corespondente.md` -- `docs\cercetare\legatura_linie_retur.md` -- `docs\cercetare\rec_d42_efactura.md` -- `docs\cercetare\s5c_factura_din_proforma.md` (sectiunea `VANZARI_CORESP`) -- PL/SQL: `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` - -## 0-bis. Constatare centrala care schimba premisa din plan - -**Mecanismul S9 (planificat) NU e acelasi cu mecanismul deja existent de editare (#6).** Astea sunt -doua cai distincte, si asta conteaza pentru fiecare consumator de mai jos: - -- **#6, "editare directa"** (`frm_modific2024`/`omodificari.vc2`, `afisjurcom.do_modifica`, - `pack_contafin.finalizeaza_modificare_nota`): documentul vechi (identificat prin `cod`) primeste - `STERS=1` in `ACT`/`RUL`, se scrie un document nou cu `cod` nou, apoi - **`pack_facturare.actualizeaza_vanzari(V_COD_VECHI, V_COD_NOU)`** - (`ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:16012-16022`) face - `UPDATE VANZARI SET COD = V_COD_NOU, STERS = 0 WHERE COD = V_COD_VECHI` — - **randul `VANZARI` e REFOLOSIT, `ID_VANZARE` NU SE SCHIMBA**, doar `COD`. In continuare, - `finalizeaza_modificare_nota` face si `UPDATE atasamente_vanzari SET cod = lnCodNou WHERE cod = - tnCod` (`rec_modific2024.md:126`, confirmat `fisier:linie`). Acesta e mecanismul deja **livrat in - productie** (git log: `#6 editare factura emisa`, changelog 2.11.15/2.11.16). -- **#13 S9 (planificat, subiectul acestei cercetari)**: reemiterea merge **pe drumul normal de - emitere**, `scrie_factura2` -> `finalizeaza_factura` -> `scrie_in_vanzari`, care face - `INSERT INTO VANZARI (...) VALUES (...) RETURNING ID_VANZARE INTO pack_facturare.nid_vanzare` - (`rec_cale_vanzari_detalii.md:99`, confirmat separat in planul S9: „reemiterea scrie prin - `pack_facturare`, pe acelasi drum ca emiterea", `plan_13...md:2936-2937`). Acesta e un rand **nou**, - cu **`ID_VANZARE` nou din secventa** — exact premisa data de team-lead. `oscrie_in_fisiere` - intervine in S9 **doar la stergerea** documentului vechi (`plan_13...md:2938`), nu si la scriere, - deci **`actualizeaza_vanzari`/sincronizarea prin `finalizeaza_modificare_nota` NU se declanseaza - pentru S9** — acel mecanism ramane specific caii #6, nu se mosteneste automat de S9. - -**Consecinta directa**: orice legatura tinuta azi prin `cod` (realiniata gratis de mecanismul #6) e -**expusa** la regenerarea din #13, pentru ca S9 nu trece prin `actualizeaza_vanzari`. Fiecare sectiune -de mai jos noteaza explicit daca protectia #6 s-ar fi aplicat sau nu. - -## 1. Inventarul exhaustiv al consumatorilor de ID_VANZARE - -| # | Tabela/mecanism | Cheie folosita | Scriitor(i) | Cititor(i) | Clasificare | -|---|---|---|---|---|---| -| 1 | `ATASAMENTE_VANZARI` | `ID_VANZARE` (FK real `FK_AT_VANZ001` -> `VANZARI.ID_VANZARE`) + coloana `COD` (legacy, vezi §2) | `ofacturare_comun.prg:466` (VFP INSERT direct), `pack_facturare.scrie_atasamente_factura` (PL/SQL, `ff_...:13955-13988`) | `VATASAMENTE_VANZARI` (view, JOIN pe `id_vanzare`), `ROAGEST`/`ROAIMOB` `oproceduri_atasamente.prg` (`citeste_atasament_vanzari`, `arata_meniu_at_vanz` — cauta pe `cod` via view) | **(A) trebuie remigrat** — vezi §2 | -| 2 | `marcheaza_facturat` -> `VANZARI.FACTURAT`/`ID_UTILFACT` | `VANZARI_CORESP.ID_VANZARE_FACT` (documentul nou) leaga la sursa | `pack_facturare.marcheaza_facturat`, apelata din `finalizeaza_factura` (`ff_...:14827,14831`) | citit la filtrarea avizelor/comenzilor facturabile | **(C) nu conteaza pentru discontinuitatea ID_VANZARE-ului documentului editat** — vezi §3 (releaga sursa, nu documentul editat insusi) | -| 3 | `VANZARI_CORESP` | `ID_VANZARE_FACT`/`ID_VANZARE_AVIZ`, ambele pe `VANZARI.ID_VANZARE` | `pack_facturare.scrie_corespondente_vanzari`, singurul writer (`s5c_factura_din_proforma.md:92-96`) | `sterge_factura` (garda la stergere), afisare "provine din X" | **(A) trebuie remigrat daca documentul editat e parte a unui lant retur/aviz->factura** — vezi §3 | -| 4 | `VANZARI_CANTITATI` | `ID_VANZARE` (a avizului sursa, nu a facturii editate) | `scrie_cantitati_vanzari_avize`, apelata doar `ntip=4` | `marcheaza_facturat(V_VERIFICARE=1)` | **(C) nu conteaza** — vezi §3, tine de sursa, neatinsa de editarea facturii | -| 5 | `DOCUMENTE` | `ID_DOC` = `ID_FACT` (nu `ID_VANZARE`) | `SET_IDFACT`/INSERT in `SCRIE_IN_ACT` | `SET_IDFACT` cautare veche (inactiva) | **(C) confirmat fara legatura cu ID_VANZARE** — vezi §4, acoperit deja de `idfact_refolosire_si_documente.md`, nu se reface | -| 6 | eFactura (`ANAF_EFACTURA`, `EsteInEFactura`) | `VANZARI.ID_FACT` (nu `ID_VANZARE`) | — | `ofacturare_editare.prg`/`omodificari.vc2` | **(C) nu conteaza** — cheia e `ID_FACT`, care se pastreaza — vezi §5 | -| 7 | Listari/rapoarte facturi (`FACT_VFACTURI*`, grid cautare) | `ID_VANZARE` + `ID_FACT`/serie+numar, cautari mixte | — | multiple | **de verificat, vezi §5** — cele care cauta dupa serie+numar/ID_FACT raman corecte; cele care tin un `ID_VANZARE` stocat undeva (ex. favorite, ultima factura deschisa) s-ar rupe | -| 8 | Incasari/plati (`INCASARI`, `PLATI`, `IREG_PARTENERI`) | **de verificat** | — | — | **in lucru, vezi §6** | - -## 2. ATASAMENTE_VANZARI - -**Nu sunt documente incarcate de utilizator — sunt exporturi PDF AUTO-GENERATE ale documentului -tiparit** (factura/aviz/recapitulatie/invoice), salvate automat dupa listare. Dovada, pas cu pas: - -- Scrierea porneste din `frm_facturi.do_listeaza_formular`/fluxul de listare, la - `COMUN\programe\ofacturare.prg:2102-2105`: - ``` - If poDate.nRelistare = 0 And poDate.eProforma = 0 AND poDate.nEFactura = 0 And poDate.nSalveazaAtasamente = 1 - poDate.scrieAtasamente() - Endif - ``` - imediat dupa exportul PDF al recapitulatiei (`:2068`, `goExport.export2pdf('crsrecapitulatie', - 'recapitulatie', .F., poDate.cDocAtasate)`) — `poDate.cDocAtasate` **e** numele cursorului - `crsoDateDocAtasate`, populat de motorul de export PDF, nu de un dialog de upload. -- `crsoDateDocAtasate` se creeaza gol la `ofacturare.prg:233`: `Create Cursor crsoDateDocAtasate - (nume_frx c(50), fisier w)` — nicio referinta la `GETFILE()`/`GETPICT()`/dialog de fisier in tot - `ROAFACTURARE`/`COMUN` legata de acest cursor (cautat explicit, zero potriviri). -- `scrieAtasamente` (`ofacturare_comun.prg:429-475`) clasifica `TIP` dupa numele raportului - (`FACTURA_VAL*`->2, `FACTURA*`->1, `INVOICE*`->3, `RECAPITULATIE`->4, `AVIZ*`->5) si scrie: - `INSERT INTO ATASAMENTE_VANZARI(ID_VANZARE, TIP, FORMAT, DOCUMENT, ID_UTIL) VALUES (?pnId, ...)` - (`:466`) — `pnId = poDate.nid_vanzare` (sau `nid_vanzare_retur` pentru cazul aviz->factura). Deci - scriitorul **foloseste exclusiv `ID_VANZARE`**, niciodata `COD`. -- Cititorii confirmati: `ROAGEST\COMUN\programe\oproceduri_atasamente.prg` (identic in `ROAIMOB`) — - `citeste_atasament_vanzari` (`SELECT document FROM atasamente_vanzari WHERE id_at_vanz=...`) si - `arata_meniu_at_vanz(tnCod,...)` (`SELECT ... FROM vatasamente_vanzari a WHERE a.cod = ...`, - `:56`) — ambele read-only, mecanism de "vezi documentele salvate pentru aceasta factura", - disponibil din ROAGEST/ROAIMOB (alte produse ale suitei, confirmand ca S11 trebuia sa caute in - toata suita, nu doar ROAFACTURARE). - -**Structura reala (interogata pe schema vie `MARIUSM_AUTO`)**: `ATASAMENTE_VANZARI(ID_AT_VANZ PK -NOT NULL din secventa, ID_VANZARE NUMBER NULL, DOCUMENT BLOB, TIP, FORMAT, STERS NOT NULL, ID_UTIL, -DATAORA NOT NULL, ID_UTILS, DATAORAS, COD NUMBER NULL)`. FK real: `FK_AT_VANZ001` pe `ID_VANZARE -> -VANZARI.ID_VANZARE` (`PK_VANZARI`). Coloana `COD` **nu e scrisa de niciun cod curent** (scriitorii -gasiti folosesc doar `ID_VANZARE`) — e populata azi doar de mecanismul legacy #6 -(`finalizeaza_modificare_nota`'s `UPDATE atasamente_vanzari SET cod = lnCodNou WHERE cod = tnCod`, -care **nu poate seta `cod` pe un rand care nu-l are deja** — e un realiniere, nu o initializare). -Pe schema de test `MARIUSM_AUTO`, toate cele 20 de randuri existente au `COD` populat si -`ID_VANZARE` NULL (probabil date vechi, dinainte ca `ID_VANZARE` sa fie coloana activa) — cf. -memoriei de proiect, **zero cazuri in date nu e dovada**, dar dovada de cod (scriitorii folosesc -doar `ID_VANZARE`) e independenta de date si sta singura. - -**Cheia reala de legatura azi e `ID_VANZARE`, prin view**: `VATASAMENTE_VANZARI` (definitie -interogata direct din `ALL_VIEWS`): -```sql -select a.id_at_vanz, a.tip, decode(a.tip,1,'Factura',2,'Factura in valuta',3,'Invoice', - 4,'Recapitulatie',5,'Aviz','Alt document') as tip_doc, b.cod - from atasamente_vanzari a - left join vanzari b on a.id_vanzare = b.id_vanzare - where a.sters = 0 and b.sters = 0 -``` -`cod` in view **nu e coloana stocata pe `atasamente_vanzari`** — e `VANZARI.COD` curent, calculat -live prin JOIN pe `ID_VANZARE`. Consumatorii din alta suita (`ROACONT\Programe\orap_terti.prg:1854`, -`ROACONTRACTE\Programe\oparteneri_contracte.prg:190,229` — `LEFT JOIN vatasamente_vanzari ... ON -a.cod = b.cod`, pentru afisarea unui numar de atasamente pe rand de factura) folosesc de fapt tot -`ID_VANZARE`, indirect prin acest JOIN. - -**Ce se rupe la regenerare (S9, calea B din §0-bis)**: `atasamente_vanzari.id_vanzare` al randurilor -vechi ramane neschimbat, aratand spre `VANZARI` cu `STERS=1` dupa stergerea documentului vechi. -`WHERE b.sters = 0` din view **filtreaza acele randuri afara** — atasamentele **dispar tacit** din -orice interogare prin `VATASAMENTE_VANZARI` (inclusiv `arata_meniu_at_vanz` din ROAGEST/ROAIMOB), -desi BLOB-ul ramane fizic in tabel. Documentul nou (`ID_VANZARE` nou) nu are niciun atasament legat. -**Clasificare: (A) trebuie remigrat** — `UPDATE atasamente_vanzari SET id_vanzare = :id_nou WHERE -id_vanzare = :id_vechi AND sters = 0`, in aceeasi tranzactie ca restul regenerarii (acelasi tipar ca -`actualizeaza_vanzari` de la #6, dar pe `id_vanzare` in loc de `cod`, si trebuie scris nou — #6 nu -acopera acest caz, cf. §0-bis). - -**Corectie fata de premisa din briefing**: nu e "pierdere de date reala" in sensul de date -introduse de utilizator si irecuperabile — snapshot-ul se poate regenera prin relistare (acelasi -mecanism care l-a creat prima data). Ce s-ar pierde real e **istoricul exact al PDF-ului trimis -clientului la momentul emiterii initiale** (relevant daca factura a fost deja trimisa/tiparita -inainte de editare) — merita remigrare oricum, ca sa nu se piarda urma, dar motivatia corecta e -"pastrarea unui audit trail", nu "date introduse de utilizator". - -## 3. marcheaza_facturat / VANZARI.FACTURAT / VANZARI_CANTITATI / VANZARI_CORESP - -**Inventarul FK declarat pe `VANZARI.ID_VANZARE`** (interogare directa pe schema `ACN`, cea reala — -`ALL_CONS_COLUMNS`/`ALL_CONSTRAINTS`, `r_constraint_name = PK_VANZARI`), confirma si extinde lista -din briefing: - -| Tabela | Coloana | Constraint | -|---|---|---| -| `ATASAMENTE_VANZARI` | `ID_VANZARE` | `FK_AT_VANZ001` | -| `VANZARI_CORESP` | `ID_VANZARE_FACT` | `FK_VANZARI_CORESP_001` | -| `VANZARI_CORESP` | `ID_VANZARE_AVIZ` | `FK_VANZARI_CORESP_002` | -| `VANZARI_CURSURI` | `ID_VANZARE` | `FK_VANZARE_CURS_002` | -| `VANZARI_DETALII` | `ID_VANZARE` | `FK_VANZARE_DET001` | -| `REST_NOTE_PLATA` | `ID_VANZARE` | `FK_REST_NOTE_PLATA_004` | -| `IPS_VOYAGES_VANZARI` | `VZ_ID` | `FK_VOYAGES_VANZARI_2` | - -Primele cinci erau asteptate (vezi §1-2 si mai jos). **Ultimele doua nu erau in lista din plan** — -descoperite prin FK, nu prin cautare de text, exact motivul pentru care inventarul trebuia facut pe -schema, nu doar pe cod. Ambele sunt module de business **specifice unui singur tip de document** -(`pack_facturare.ntip`), nu cai generale: - -- **`REST_NOTE_PLATA`** — modul restaurant (`PACK_RESTAURANT`, `nTipFacturaRestaurant`). La - stergerea unei vanzari de tip restaurant, `sterge_factura` cheama deja - `pack_restaurant.sterge_vanzare(V_ID_VANZARE, V_ID_UTIL)` (`ff_...:5593`, in `CASE`-ul de la - finalul procedurii) — un hook dedicat, separat de logica generala. **Nu s-a gasit un hook simetric - „re-leaga la reemitere"** in codul citit (nu era in scop sa se citeasca tot `PACK_RESTAURANT` — - pachet separat, mii de linii). Clasificare: **(A) suspecta, needs follow-up dedicat** — afecteaza - doar documentele cu `ntip = nTipFacturaRestaurant`, deci doar daca #13 include si acest tip in - „regenerabile" (de confirmat in `COMUN\docs\tipuri_documente_facturare.md`, cf. S13). -- **`IPS_VOYAGES_VANZARI`** — modul specific schemei `ACN` (`PACK_ACN`, `nTipFacturaACN`), legat de - un tabel `IPS_VOYAGES_VANZARI`/`IPS_VOYAGE_MEMBERS_VANZARI`/`IPS_VVOYAGE_MEMBERS` (confirmat in - `ris_2024_04_09_01_ACN.sql:258-266`, JOIN pe `vz.id_vanzare = vv.vz_id`) — pare o extensie de - business pentru un client specific (calatorii/voiaje), nu parte din suita generica ROA. Acelasi - tipar: `sterge_factura` cheama `pack_acn.sterge_vanzare(...)` la stergere (`ff_...:5596-5600`, - `execute immediate` conditionat de `pack_migrare.ObjectExist('PACK_ACN')` — pachetul exista doar - pe schema ACN). Clasificare: **(A) suspecta, needs follow-up dedicat** — probabil in afara - perimetrului #13 (client unic, tip de factura special), dar trebuie confirmat, nu presupus. - -**`VANZARI_CURSURI`** — cursul valutar al documentului. Scris de `pack_facturare.scrie_cursuri(nid_vanzare)` -(`rec_cale_vanzari_detalii.md:102`), apelat **in interiorul aceluiasi `scrie_in_vanzari`** care -genereaza noul `ID_VANZARE` la reemitere (S9 foloseste exact acest drum, cf. §0-bis). Deci un rand -nou `VANZARI_CURSURI` se scrie automat pentru noul `ID_VANZARE`, fara nicio interventie separata. -**Clasificare: (B)** — se reface singur, ca parte a drumului normal de emitere, nimic de adaugat. - -**`VANZARI_DETALII`** — liniile facturii. E miezul a ceea ce regenerarea insasi scrie (INSERT direct -pe noul `ID_VANZARE`, cf. `rec_cale_vanzari_detalii.md:105-113`) — nu e un consumator "extern" care -sa se rupa, e obiectul regenerarii. **Clasificare: (B)**, deja acoperit de proiectarea S9/S10, nu se -reface aici. - -### VANZARI_CORESP — cazul netratat inca de plan: documentul editat e EL INSUSI parte a unui lant - -Planul (S11, `plan_13...md:3024-3027`) trateaza `VANZARI_CORESP` ca pe o lista simpla, dar -`sterge_factura` (apelata de S9 la pasul de stergere) arata ca situatia are **doua fete diferite**, -niciuna simpla: - -**(i) Garda de blocare — editarea unui document care e SURSA pentru altul e azi IMPOSIBILA, nu -riscanta.** `sterge_factura` (`ff_...:5450-5494`) verifica, INAINTE de orice stergere: -```sql -SELECT COUNT(*) INTO V_NR_FACT_RETUR FROM VANZARI_CORESP - WHERE STERS=0 AND ID_VANZARE_AVIZ = V_ID_VANZARE AND TIP = 3; -IF V_NR_FACT_RETUR > 0 THEN RAISE_APPLICATION_ERROR(-20000, 'Pentru acesta factura s-au emis facturi - de retur. Trebuie sa stergeti mai intai factura de retur!'); END IF; -``` -si analog pentru `TIP IN (1,2)` (aviz cu factura/aviz de retur emise pe el) si pentru avizele-sursa -ale unei „facturi din aviz" (subquery imbricat, `:5478-5494`). **Consecinta pentru S9**: daca -utilizatorul incearca sa editeze (regenereze) o factura care are deja o factura de retur emisa -pe ea, sau un aviz care are deja o factura/aviz de retur emis pe el, **pasul de stergere din -tranzactia S9 arunca `ORA-20000`, tranzactia face rollback, documentul vechi ramane intact** — nu -e coruptie silentioasa, e un esec curat, dar e o **limitare functionala reala**: aceste documente -nu pot fi editate prin regenerare deloc, cat timp garda ramane activa (si nu exista niciun motiv -funcțional sa fie dezactivata — ar contrazice exact protectia pe care garda o ofera azi). *De -verificat de S12*: ca mesajul de eroare Oracle ajunge inteligibil la utilizator prin formularul de -editare, nu ca o eroare tehnica generica. - -**(ii) Legaturile in care documentul editat e chiar `ID_VANZARE_FACT`/`ID_VANZARE_AVIZ` — marcate -`STERS=1` la stergere, NU remigrate automat, dar potential rescrise de reemitere daca parametrii -sunt refurnizati corect.** La stergerea documentului vechi, `sterge_factura` executa neconditionat -(`:5582-5585`): -```sql -UPDATE VANZARI_CORESP SET STERS = V_STERS - WHERE ID_VANZARE_FACT = V_ID_VANZARE OR ID_VANZARE_AVIZ = V_ID_VANZARE; -``` -— orice legatura in care documentul vechi apare pe oricare parte devine `STERS=1`. **Nu exista cod -care sa recreeze automat legatura pentru noul `ID_VANZARE`.** Dar exista o cale prin care se -recreeaza **corect, de la sine**, daca S9 e proiectat sa refoloseasca drumul normal: `finalizeaza_factura` -(chemata de reemiterea normala) are un `CASE` pe `pack_facturare.ntip` care scrie din nou -corespondenta (`WHEN ntip=4: scrie_corespondente_vanzari(1)`; `WHEN ntip=24: -scrie_corespondente_vanzari(2)`; `WHEN ntip IN (8,9): scrie_corespondente_vanzari(3)`, -`ff_...:14823-14836`) — **daca** S9 seteaza `pack_facturare.ntip` la aceeasi valoare ca documentul -original SI re-populeaza `pack_facturare.clistaid`/`clistaid_avize` cu lista de `id_vanzare` sursa -originala **inainte** de reemitere, corespondenta se scrie natural, cu noul `ID_VANZARE_FACT`. -**Sursa lista originala e recuperabila** din randurile `VANZARI_CORESP` (acum `STERS=1`) ale -documentului vechi, citite INAINTE de stergere in aceeasi tranzactie — un pas suplimentar pe care -proiectarea S9 trebuie sa-l includa explicit, nu e „gratis". **Clasificare: (A) trebuie remigrat -— dar prin re-derivare + refolosirea mecanismului existent, nu prin UPDATE direct pe `VANZARI_CORESP`** -(un UPDATE direct ar trebui sa distinga cu grija cele doua roluri — FACT vs AVIZ — si ar risca sa -resusciteze legaturi catre documente inca sterse din alte motive; re-emiterea naturala prin `ntip`+ -`clistaid` e mai sigura, pentru ca refoloseste exact logica deja validata de emiterea normala). - -### marcheaza_facturat — pentru sursa documentului editat, nu pentru documentul insusi - -`marcheaza_facturat` (`ff_...:15381-15418`) actioneaza pe **sursa** (avizul din care s-a facturat, -sau avizul-parinte al unui aviz de retur), nu pe documentul care se editeaza. La stergerea -documentului vechi (S9, pasul de stergere), pentru `V_TIP=4`/`V_TIP=24`, `sterge_factura` reseteaza -deja `FACTURAT=0`/`ID_UTILFACT=NULL` pe sursa (`:5504-5510`, `:5525-5533`) si marcheaza -`VANZARI_CANTITATI.STERS=1` pentru cantitatile consumate (`:5517-5523`, `:5535-5540`) — sursa e deci -**eliberata** corect de mecanismul EXISTENT, neschimbat. La reemitere, daca `ntip`/`clistaid_avize` -sunt refurnizate corect (acelasi argument ca la VANZARI_CORESP mai sus), `finalizeaza_factura` -cheama din nou `scrie_cantitati_vanzari_avize` + `marcheaza_facturat` (`:14825-14827`,`:14831`), -**reconsumand** sursa sub noul `ID_VANZARE_FACT`. **Clasificare: (C) — mecanismul deja exista si -functioneaza simetric (elibereaza la stergere, reconsuma la scriere), CONDITIONAT de aceeasi -cerinta ca la VANZARI_CORESP: S9 trebuie sa refurnizeze `ntip` si `clistaid`/`clistaid_avize` -originale la reemitere.** Aceasta e exact conditia pe care S12 o testeaza explicit („sursa eliberata -si reconsumata", `plan_13...md:3037`) — S11 confirma DE CE mecanismul poate functiona (nu e nevoie -de cod nou pentru asta), dar nu inlocuieste testul S12. - -## 4. DOCUMENTE / ID_DOC — legatura cu ID_VANZARE - -**Confirmat: fara legatura.** `DOCUMENTE(ID_DOC, ID_UTIL, DATAORA, TVA_INCASARE, SERIE_ACT, NRACT, -DATAACT, DATAIREG, ID_CTR, ID_SET, STERS, ...)` — INSERT-ul complet citat in -`idfact_refolosire_si_documente.md:143-148` (`PACK_CONTAFIN.pck:788-817`, cod activ) nu are nicio -coloana `ID_VANZARE`. Cheia `ID_DOC = ID_FACT`, care se PASTREAZA la regenerare (decizie E/F, deja -data). Acest raport nu reface analiza — deja acoperita exhaustiv de -`docs\cercetare\idfact_refolosire_si_documente.md` (S9, punctele A-C), care stabileste separat -conditiile pentru pastrarea `ID_FACT` (variabila noua de sesiune in `SET_IDFACT` + upsert real in -`DOCUMENTE`). **Clasificare: (C) — confirmat, fara actiune ceruta de S11.** - -## 5. Borderou eFactura si listari - -**eFactura: cheia e `ID_FACT`, nu `ID_VANZARE` — confirmat cu dovada proprie, independenta.** -`EsteInEFactura` (`COMUN\programe\ofacturare_editare.prg`, apelata din `ofacturare_comun.vc2:3764` -cu `lnIdFact = crsfacturi.id_fact`) verifica prezenta documentului in `ANAF_EFACTURA` **pe -`VANZARI.ID_FACT`**, nu pe `id_vanzare` — confirmat explicit ca eroare corectata in -`rec_d42_efactura.md:81-109` (implementarea initiala folosea gresit `id_vanzare`, corectata dupa -verificare pe date: `id_fact` si `id_vanzare` sunt spatii de ID complet diferite, 0 coincidente pe -142 facturi testate). **Cum `ID_FACT` se pastreaza la regenerare (decizie deja luata), `EsteInEFactura` -continua sa functioneze corect pe documentul reemis, fara nicio schimbare.** Aceeasi concluzie se -aplica probabil borderoului eFactura insusi (cautarea documentelor de trimis/trimise), dar -**construirea borderoului nu a fost citita in acest raport** (in afara scopului — cheia `ID_FACT` -fiind deja confirmata stabila, riscul e mic, dar afirmatia stricta „borderoul il gaseste corect" -ramane de verificat direct pe cod la implementare, nu doar dedusa). **Clasificare: (C), cu rezerva -de verificare directa a borderoului la implementare.** - -**Listari/grid cautare facturi**: cursoarele de grid (`crsFacturi`, `crsFacturiOrd`, -`ofacturare_comun.vc2:3982,4134-4145,4983`) se reconstruiesc live din Oracle la fiecare deschidere -a formularului de listare, filtrate pe `sters=0` — nu tin niciun `id_vanzare` persistat intre -sesiuni. Documentul reemis (nou `id_vanzare`, acelasi `id_fact`/serie/numar) apare normal la -urmatoarea reincarcare a gridului. **Singurul risc identificat, nu confirmat ca problema reala**: -daca undeva in cod exista un `id_vanzare` **retinut pe termen lung** in afara sesiunii curente a -formularului (ex. „ultima factura deschisa", favorite, shortcut) — nu s-a gasit niciun asemenea -mecanism in codul citit (`ofacturare_comun.vc2`, `ofacturare.prg`), dar nici nu a fost cautat -exhaustiv in tot `ROAFACTURARE` (in afara bugetului acestei cercetari). **Clasificare: (C), -neconfirmat ca risc real — verificare suplimentara recomandata daca timpul permite.** - -## 6. Incasari/plati si riscuri finale - -**Cautare directa (negativa, dar utila): niciun tabel de incasari/plati nu are FK declarat pe -`VANZARI.ID_VANZARE`.** Interogarea exhaustiva `ALL_CONS_COLUMNS`/`ALL_CONSTRAINTS` pe schema `ACN` -(tabelul din §3) e completa pentru FK-uri declarate — `INCASARI`, `PLATI`, `IREG_PARTENERI` nu apar -in acea lista. Cautare text suplimentara (`grep -rn "ID_VANZARE"` filtrat pe fisiere cu -"incasari"/"plati"/"chitant" in nume, in tot `D:\ROA\DATABASE\SCRIPTURI_CLAR`) — **zero rezultate**. -Coerent cu arhitectura deja cunoscuta: incasarile/platile se leaga de facturi prin **`ID_FACT`** -(coloana pe `ACT`/`IREG_PARTENERI`, cf. `idfact_refolosire_si_documente.md`), nu prin `ID_VANZARE` -— `ID_FACT` se pastreaza la regenerare, deci **incasarile/platile nu sunt afectate structural**. -**Clasificare: (C), cu dovada dubla (schema + text), coerenta cu memoria de proiect „zero cazuri in -date nu e dovada" — aici insa e absenta de FK + absenta de text, nu absenta de date, deci e o -dovada structurala, nu doar un data point.** - -## Rezumat clasificari - -| Consumator | Clasificare | Actiune ceruta | -|---|---|---| -| `ATASAMENTE_VANZARI.ID_VANZARE` | **(A)** | `UPDATE ... SET id_vanzare=:nou WHERE id_vanzare=:vechi AND sters=0`, in tranzactia S9 | -| `VANZARI_CORESP` (documentul editat ca FACT/AVIZ al altui lant) | **(A)**, prin re-emitere naturala | S9 trebuie sa citeasca `ntip`+lista sursa originala INAINTE de stergere si sa le refurnizeze la reemitere | -| `marcheaza_facturat`/`VANZARI_CANTITATI` (sursa documentului editat) | **(C)**, conditionat | functioneaza automat DACA `ntip`/`clistaid` sunt refurnizate (acelasi mecanism ca mai sus) | -| `VANZARI_CURSURI` | **(B)** | se rescrie automat de `scrie_in_vanzari`, nimic de facut | -| `VANZARI_DETALII` | **(B)** | e obiectul regenerarii, acoperit de S9/S10 | -| `DOCUMENTE`/`ID_DOC` | **(C)** | confirmat fara legatura, acoperit de `idfact_refolosire_si_documente.md` | -| eFactura (`EsteInEFactura`, `ANAF_EFACTURA`) | **(C)** | cheie `ID_FACT`, pastrat — verificare directa a borderoului recomandata la implementare | -| Listari/grid facturi | **(C)** | cursoare live, fara stare persistata gasita | -| Incasari/plati | **(C)** | fara FK, fara referinta text — cheie e `ID_FACT` | -| `REST_NOTE_PLATA` (modul restaurant) | **(A)** suspecta | needs follow-up dedicat, `PACK_RESTAURANT` necitit integral | -| `IPS_VOYAGES_VANZARI` (modul ACN specific client) | **(A)** suspecta | needs follow-up dedicat, `PACK_ACN` necitit integral, posibil in afara perimetrului #13 | - -## Riscuri si ce ramane de decis de Marius - -1. **Editarea documentelor care sunt sursa unui lant retur/aviz e azi BLOCATA, nu doar riscanta.** - `sterge_factura` arunca `ORA-20000` daca documentul editat are deja facturi/avize de retur emise - pe el (§3.i). Nu e o eroare de proiectare — garda protejeaza integritatea lantului — dar - inseamna ca „editare prin regenerare" **nu va functiona deloc** pentru aceste documente, cat timp - utilizatorul nu sterge intai documentele-copil. **De decis**: acceptat ca limitare (cu mesaj - clar in UI, tradus din eroarea Oracle), sau se doreste alt comportament? -2. **Recuperarea `VANZARI_CORESP`/`marcheaza_facturat` la reemitere depinde de un pas pe care planul - nu-l mentioneaza explicit**: citirea listei sursa originale (`ID_VANZARE_AVIZ`/`ID_VANZARE_FACT` - din randurile `VANZARI_CORESP` ale documentului vechi) **inainte** de stergere, si refurnizarea - ei ca `pack_facturare.clistaid`/`clistaid_avize` la reemitere. Fara acest pas, corespondenta si - `marcheaza_facturat` **nu se scriu deloc** pentru documentul reemis (nu eroare, ci scriere lipsa - silentioasa) — de adaugat explicit in proiectarea S9, nu presupus „vine gratis" din drumul normal. -3. **`ATASAMENTE_VANZARI` nu e „date de utilizator pierdute"**, cf. §2 — e un audit trail de - PDF-uri auto-generate la listare. Remigrarea (`UPDATE ... SET id_vanzare`) e simpla si ieftina, - dar motivatia corecta pentru Marius e „pastrarea istoricului tiparit", nu „pierdere de date - introduse manual" — poate schimba prioritatea relativa fata de alte itemi din S11/S12. -4. **`REST_NOTE_PLATA` si `IPS_VOYAGES_VANZARI`** raman needs-investigation — descoperite prin FK, - nu prin text, deci nu erau in raza cautarilor anterioare (nici in planul S11 original). Daca - tipurile de document asociate (`nTipFacturaRestaurant`, `nTipFacturaACN`) nu sunt in domeniul - „regenerabile" al #13 (de confirmat in `tipuri_documente_facturare.md`, S13), riscul dispare de - la sine — dar asta trebuie confirmat, nu presupus. -5. **Borderoul eFactura insusi** (nu doar `EsteInEFactura`) nu a fost citit direct — cheia `ID_FACT` - fiind stabila, riscul e mic, dar afirmatia din *Gata cand* a S11 („documentul reemis se regaseste - corect in borderoul eFactura") merita o verificare punctuala pe cod la implementare, nu doar - dedusa din `EsteInEFactura`. - -## STARE / CE RAMANE - -**Cercetare incheiata pentru toate cele 7 puncte cerute de briefing**, cu dovada `fisier:linie` pe -fiecare afirmatie portanta: inventar exhaustiv (via FK Oracle + cod), `ATASAMENTE_VANZARI` (sectiune -proprie, cu corectia premisei „date de utilizator"), `marcheaza_facturat`/`VANZARI.FACTURAT`/ -`VANZARI_CANTITATI`, `DOCUMENTE`/`ID_DOC` (confirmat, fara redeschidere), borderou eFactura si -listari, riscuri finale. - -**Constatare centrala**: mecanismul S9 planificat (regenerare cu `ID_VANZARE` nou, pe drumul normal -de emitere) e **diferit** de mecanismul #6 deja livrat (`actualizeaza_vanzari`, reutilizeaza acelasi -`ID_VANZARE`, doar `COD` se schimba) — deci nicio protectie construita pentru #6 nu se mosteneste -automat de S9. Singurul consumator care chiar are nevoie de un `UPDATE` explicit nou este -`ATASAMENTE_VANZARI` (clasificare A simpla). `VANZARI_CORESP`/`marcheaza_facturat` se pot rezolva -**fara cod PL/SQL nou**, refolosind mecanismul existent (`finalizeaza_factura`'s `CASE` pe `ntip`), -**daca** S9 e proiectat sa citeasca si sa refurnizeze parametrii sursei originale — asta e o cerinta -de design care trebuie scrisa explicit in S9, nu deductibila implicit. - -**Neverificat, ramas pentru follow-up** (declarat, nu ascuns): corpul complet al `PACK_RESTAURANT`/ -`PACK_ACN` (pentru `REST_NOTE_PLATA`/`IPS_VOYAGES_VANZARI`), borderoul eFactura citit direct (nu doar -`EsteInEFactura`), o cautare exhaustiva de „id_vanzare retinut pe termen lung" in tot codul VFP -(favorite/shortcut-uri), si confirmarea in `tipuri_documente_facturare.md` a caror tipuri intra -efectiv in perimetrul „regenerabile" al #13 (ar elimina sau confirma riscurile 1 si 4). - -Zero modificari de cod, zero write-back, zero `git_sync.ps1`/`txt2vcx.ps1`, zero commit. Pe Oracle -doar `SELECT` (schema `MARIUSM_AUTO`, interogari pe `ALL_TAB_COLUMNS`/`ALL_CONSTRAINTS`/`ALL_VIEWS`/ -`ALL_SOURCE`, plus `SELECT` de numarare pe date de test — niciuna cu efect de scriere). diff --git a/docs/cercetare/s2_factureaza_unificare.md b/docs/cercetare/s2_factureaza_unificare.md deleted file mode 100644 index 67c7099..0000000 --- a/docs/cercetare/s2_factureaza_unificare.md +++ /dev/null @@ -1,318 +0,0 @@ -# S2 — Proiectare: o singura procedura `factureaza` - -Cercetare read-only pentru povestea **S2** din `docs\plan_13_unificare_formular_facturare.md`. -Niciun fisier de cod atins, `git_sync.ps1` nerulat, niciun write-back, niciun commit. - -## Verdict - -**Se poate unifica, si e mai simplu decat pare.** `factureaza2` nu e o a doua implementare -funcţională care trebuie fuzionata cu grija — e un fork din 08.06.2017 la care s-a **dezactivat -execuţia interogarii de articole** (`lnSucces = 1` hardcodat, `goExecutor.oExecute` niciodata -apelat) si mai multe blocuri intregi sunt inchise cu `If .F.`. Are **exact un singur apelant** in -tot codul (`ofacturare.prg:90`, din interiorul lui `factureaza` insusi, in spatele unui -`AMESSAGEBOX` de confirmare) si **zero utilizatori reali** dincolo de acel switch de test. Riscul de -regresie e mic pentru ca nu exista nimic funcţional de pierdut in `factureaza2` — obstacolul -principal nu e tehnic, e ca formularul spre care duce (`frm_facturare_articole2`) e tot un prototip -neterminat (`Init` de 92 de linii, nu completeaza antetul), deci unificarea proceduri lor **nu** -face formularul nou utilizabil — doar elimina duplicarea de cod. Asta ramane treaba lui S3. - ---- - -## 1. Inventarul celor doua proceduri - -Ambele sunt proceduri globale (nu metode de clasa) in **`COMUN\programe\ofacturare.prg`**, incarcat -via `SET PROCEDURE ... ADDITIVE` din `Programe\roafacturare.prg`. Nu exista omonime — verificat cu -`Get-ChildItem -Recurse *.prg | Select-String '^\s*(PROCEDURE|FUNCTION)\s+factureaza2?\b'` pe tot -`ROAFACTURARE` si cu Grep pe `.vc2` din `COMUN`: un singur rezultat pentru fiecare nume. - -| | `factureaza` | `factureaza2` | -|---|---|---| -| Locatie | `COMUN\programe\ofacturare.prg:81-577` (497 linii) | `COMUN\programe\ofacturare.prg:583-1081` (499 linii) | -| Parametri | `LPARAMETERS tnTip, toFactura` (`:82`) — `toFactura` = obiect, pentru copiere/modificare | `LPARAMETERS tnTip` (`:584`) — **fara** al doilea parametru | -| Apelanti | ~20+ puncte de intrare reale (sectiunea 3) | **unul singur**: `ofacturare.prg:90`, din `factureaza` | - -`ofacturare.prg` in sine e cod comun al **intregii suite**: exista o copie identica-la-origine in -`COMUN\programe\ofacturare.prg` a fiecarui produs ROA verificat (ROACONT, ROAGEST, ROACONTRACTE, -ROAAUTO, ROAACNPRO, ROAIMOB, plus alte ~30 produse) — fiecare cu propriile `factureaza`/`factureaza2` -la linii apropiate (de regula `:76-77` sau `:87-88` pentru comutatorul `gnFacturareNou`, dupa -versiune). Nu exista in `COMUNROA` (biblioteca partajata separata) — e sincronizat manual intre -produse ca fisier COMUN, nu ca livrabil COMUNROA. - -## 2. Diff-ul semantic - -Comparatie linie-cu-linie, cu citate `ofacturare.prg:linie`. **Sase din cele noua diferente listate -in sectiunea A a planului se confirma exact; una e imprecisa; una lipseste din plan.** - -### Ce face `factureaza` si nu face `factureaza2` - -| Ce | `factureaza` | `factureaza2` | -|---|---|---| -| Tipurile 51 (ROAACNPRO), 52 (contract factura fiscala valuta) rutate la FACTURA | `Inlist(tnTip,45,48,49,**51,52**)` la `:192` si `:222` | `Inlist(tnTip,45,48,49)` la `:674` si `:700` — **51/52 lipsesc**, ar cadea in `Otherwise` -> AVIZ | -| Ramura `llCopiere`/`toFactura` (copiere/modificare factura) | declarata `:102`, populata `:111`, folosita la completarea datelor (`:200-206`), la construirea interogarii (`Case m.llCopiere` -> `cursor_retur_document`, `:267-268`), si la adaugarea articolelor din lista de preturi peste cele copiate (`:454-474`) | **absenta complet** — nicio declarare a `llCopiere`, niciun tratament al lui `toFactura` | -| Deschiderea `frm_date_factura`/`frm_date_aviz` | activa, cu `toFactura` transmis constructorului (`:229-235`) | inchisa cu `If .F.` (`:707-711`) | -| Filtrul `RORTC` pe `jtva_coloane` | `AND !'RORTC'$UPPER(coloana_jv)` la ambele ramuri (`:243`, `:245`) | absent (`:715`, `:717`) | -| Executia interogarii de articole | `lnSucces = goExecutor.oExecute(lcSqlCursor, lcCursor)` (`:311`) | **inchisa cu `If .F.`** (`:823-826`); imediat dupa, `lnSucces = 1` hardcodat (`:827`) — cursorul SQL construit la `:748-822` nu ruleaza niciodata | -| Verificarea "nu exista articole" | activa (`:324-328`) | **inchisa cu `If .F.`** (`:838`) — combinata cu `lnSucces=1` de mai sus, ramura merge mereu pe calea "am articole", indiferent de continut | -| Zeroizarea `gestionabil` pe proforma | `IF poDate.eProforma = 1 ... UPDATE (lcCursor) SET gestionabil = 0` (`:333-336`) | **absenta** — `eProforma` nu apare nicaieri in `factureaza2` (verificat cu grep pe tot fisierul) | -| `crspolitici` / `crscontracte` (populare combo-uri) | necondiţionate (`:420-440`) | **ambele inchise cu `If .F.`** (`:956-968`, `:969-980`) | -| `GetInstitutiePublica` | `poDate.institutie_publica = GetInstitutiePublica(...)` (`:509`) | **absenta** | -| `codnc8`/`codcpv` pe fiecare linie | `Replace codnc8 WITH ..., codcpv WITH ...` in `Scan` (`:516-517`), pe langa `codmatc` | **doar `codmatc`** (`:1027`) — `codnc8`/`codcpv` lipsesc | -| `GetSoldClient` + `poDate.sold_lei`/`sold_valuta` inainte de listare | prezent, in ramura non-bon-fiscal (`:530-533`) | **absent** — ramura echivalenta (`:1040-1041`) sare direct la `listeaza_ofacturare()` | -| Formularul de articole | `frm_facturare_articole` (`:395`) | `frm_facturare_articole2` (`:929`) — **singura diferenta functionala reala** care justifica existenta lui `factureaza2` | - -### Corectii fata de sectiunea A a planului - -- **"`verifica_numar(16, ...)` pentru chitanta" — planul GRESESTE.** E prezent **identic** in ambele: - `factureaza:502-504` si `factureaza2:1018-1020` (`If poDate.incasat <> 0 ... - poGeneratorNumere.verifica_numar(16, poDate.nr_incasare) Endif`). Nu lipseste din `factureaza2`. -- **"`cursor_avize` cu tip 23" — planul e imprecis.** Nu exista nicio asociere `cursor_avize`/tip 23 - in niciuna din cele doua proceduri (`cursor_avize` trateaza doar `tnTip = 4`, identic in ambele, - `:294-295` / `:774-775`). Ce difera real pentru tipul 23: in `factureaza`, tipul 23 e prins in - `Case Inlist(tnTip,1,22,5,29,7,10,**23**)` -> `cursor_preturi` (`:279-282`); in `factureaza2`, 23 - **nu** e in acea lista (`:758`, doar `1,22,5,29,7,10`) si cade in schimb in - `Case Inlist(tnTip,**23**,41)` -> `cursor_gestiune` (`:776-778`). E o rutare complet diferita - pentru acelasi tip de document, nu o simpla lipsa — **de adaugat explicit la lista din sectiunea A**. -- **Nementionat in plan — tipul 52 lipseste si din `Do Case` de contract**: `factureaza` - `Case Inlist(tnTip,2,26,6,**52**)` (`:283`) vs `factureaza2` `Case Inlist(tnTip,2,26,6)` (`:762`) — - aceeasi lipsa a tipului 52 ca la nIdTipDoc, dar intr-un loc separat din cod; **tipul 24** (aviz de - retur) lipseste similar din `Case Inlist(tnTip,8,9,**24**)` (`:306`) vs `Case Inlist(tnTip,8,9)` - (`:819`). - -### Ce fac amandoua identic (verificat, nu presupus) - -Structura comentariilor `lnIdSet`/`pnTipFacturare`, bucla `Do While lnRaspuns = 6`, apelul -`actualizeaza_optiuni_program()`, crearea `poDate`/`poGeneratorNumere`, alegerea `frm_date_factura` / -`frm_date_aviz_lucrare` / `frm_date_aviz` pentru antet (desi in `factureaza2` deschiderea propriu-zisa -e dezactivata), tratarea `tnTip=30` (aviz din NIR, inclusiv bucla `Scatter`/`Insert Into crsfactura` -identica caracter-cu-caracter), `verifica_numar(16,...)`, dezalocarea celor trei numere -(`nIdTipDoc`, 16, 3/26) la abandon, si intreaga bucla finala de `AMESSAGEBOX` -"Doriti sa continuati" + `CloneObj`/`poDate.Reset()`. - -### Ce face `factureaza2` si nu face `factureaza` - -Un singur lucru: verificari defensive suplimentare de tip la iesirea timpurie (`pnButon = 2`): -``` -IF TYPE('poDate.nr_incasare') = 'N' - poDate.nr_incasare = 0 -ENDIF -IF TYPE('poDate.incasat') = 'N' - poDate.incasat = 0 -ENDIF -``` -(`factureaza2:727-732`) — `factureaza` face resetarea echivalenta necondiţionat, dar mai tarziu, in -ramura de succes (`:546-547`), nu pe calea de abandon. Diferenta e cosmetica, nu comportamentala pe -fluxul normal. - -## 3. Toti apelantii - -**Cautare in tot arborele `D:\ROA`** (fiecare produs + `COMUN`-ul lui + `COMUNROA`), cu capcana -"Grep nu vede COMUN" tratata explicit (path dat direct pe fiecare `COMUN`, nu doar pe radacina -produsului — altfel cautarea sare tacut peste el, per gitignore-ul fiecarui produs). - -**Niciun produs verificat (ROACONT, ROAGEST, ROAAUTO, ROAACNPRO, ROAIMOB) nu apeleaza `factureaza` -direct din cod propriu — nici in `COMUN`, nici in partea specifica produsului.** Singura urma in -`COMUN`-urile lor e propria copie a `ofacturare.prg`/`oproceduri_facturare.prg` (definitia, nu un -apel). Confirma independent observatia din `coresp_cont_venchelt.md` ca ROAACNPRO nu trece prin acest -flux (foloseste `pack_acn.salveaza_regdoc`, nu `contabilizeaza_articol`). **Exceptia: ROACONTRACTE**, -care are un apel real, specific produsului, la wrapper-ul `facturare_contracte`. - -### Apelanti directi ai `factureaza(N)` - -| Apelant | Produs | Parametri | Tip document | -|---|---|---|---| -| `Meniuri\politica.mn2:12` | ROAFACTURARE | `factureaza(1)` | lista de preturi, lei | -| `Meniuri\politica.mn2:15` | ROAFACTURARE | `factureaza(8)` | retur factura lei | -| `Meniuri\politica.mn2:18` | ROAFACTURARE | `factureaza(45)` | restaurant | -| `Meniuri\politica.mn2:26` | ROAFACTURARE | `factureaza(49)` | marfa custodie fara descarcare K | -| `Meniuri\politica.mn2:29` | ROAFACTURARE | `factureaza(48)` | marfa custodie cu descarcare K | -| `Meniuri\politica.mn2:37` | ROAFACTURARE | `factureaza(5)` | lista de preturi, valuta (invoice) | -| `Meniuri\politica.mn2:40` | ROAFACTURARE | `factureaza(7)` | credit note | -| `Meniuri\politica.mn2:43` | ROAFACTURARE | `factureaza(10)` | factura fiscala valuta | -| `Meniuri\politica.mn2:46` | ROAFACTURARE | `factureaza(9)` | retur factura valuta | -| `Meniuri\contracte.mn2:12` | ROAFACTURARE | `factureaza(2)` | contract, lei | -| `Meniuri\contracte.mn2:15` | ROAFACTURARE | `factureaza(6)` | contract, valuta | -| `Meniuri\contracte.mn2:18` | ROAFACTURARE | `factureaza(52)` | contract, factura fiscala valuta | - -### Apelanti prin `COMUN\programe\oproceduri_facturare.prg` (dispecer comun, un wrapper per grup de tipuri) - -| Wrapper (`oproceduri_facturare.prg`) | -> `factureaza(N)` | Apelat din | -|---|---|---| -| `facturare_contracte(tcTip)` (`:119-136`) | `factureaza(2)`/`factureaza(6)`/`factureaza(52)` dupa `tcTip` | `ROAFACTURARE\Clase\ofundal_facturare.vc2:890-894`, `ROAFACTURARE\Ferestre\fundal.sc2:924`, **`ROACONTRACTE\Clase\ferestre_contracte.vc2:1542-1546`** (produs diferit, apel real) | -| `facturare_comenzi()` (`:139-141`) | `factureaza(3)` | `COMUN\clase\ocomenzi.vc2:1580-1596`, metoda `do_factura` — `SCATTER NAME goComanda MEMO` inainte de apel | -| `facturare_avize()` (`:144-146`) | `factureaza(4)` | `ROAFACTURARE\Clase\ofundal_facturare.vc2:905`, `ROAFACTURARE\Ferestre\fundal.sc2:932` | -| `copiere_factura(toFactura)` (`:150-153`) | `factureaza(toFactura.Tip, toFactura)` | `COMUN\clase\ofacturare_comun.vc2:3710` — fisierul de perimetrul #6, doar citit, neatins | -| `facturare_lista_de_preturi()` (`:114-116`) | `Do politica.mpr` -> popup-ul din `politica.mn2` (vezi tabelul de mai sus) | `ROAFACTURARE\Clase\ofundal_facturare.vc2:883`, `ROAFACTURARE\Ferestre\fundal.sc2:920` | -| `emitere_aviz_clienti(tnTip)` (`:198-228`) | `factureaza(21/22/26/24)` dupa `tnTip=1/2/3/7`; `tnTip=4,5,6` -> `initializeaza_vanzare_din_stoc`, nu `factureaza` | `ROAFACTURARE\Meniuri\aviz_clienti.mn2:21,47,51,55,59,63,67` | -| `emitere_aviz_clienti_debitori(tnTip)` (`:230-243`) | `factureaza(28/29)` dupa `tnTip=1/2` | `ROAFACTURARE\Meniuri\aviz_clienti_debitori.mn2:22,26` | -| `emitere_aviz_clienti_custodie(tnTip)` (`:244-259`) | `factureaza(42/47)` dupa `tnTip=1/2`; `tnTip=3` -> `initializeaza_vanzare_din_stoc` | `ROAFACTURARE\Meniuri\aviz_clienti_custodie.mn2:30,34,38` | -| `emitere_aviz_transfer(tnTip)` (`:261-280`) | `factureaza(25/23/27/30/41)` dupa `tnTip=1..5` | `ROAFACTURARE\Meniuri\aviz_subunitati.mn2:36,40,44,48,52` | - -**Total: ~33 puncte de intrare reale in cod pentru `factureaza`, doar unul pentru `factureaza2`** -(`ofacturare.prg:90`, in interiorul lui `factureaza`, gatit de `gnFacturareNou=1` + confirmare DA la -`AMESSAGEBOX`). - -## 4. Rolul lui `gnFacturareNou` - -**Nu e un comutator de productie — e un switch de dezvoltator, fara nicio persistenta.** - -- **Declarat:** nicaieri. Cautare exhaustiva (`gnFacturareNou`, tot `D:\ROA`, extensii - `.prg/.vc2/.sc2/.mn2`) — singura aparitie e exact linia care il citeste (`ofacturare.prg:88`, sau - liniile echivalente in fiecare copie de produs). Nu exista intr-un ecran de optiuni, nu e coloana - intr-un tabel de configurare (spre deosebire de `gnScadereStoc`, `gl406` etc., citite din - `oinit_optiuni.prg`). -- **Citit:** o singura data, `ofacturare.prg:88` — `If Type('gnFacturareNou') = 'N' And - m.gnFacturareNou = 1`. -- **Valoare implicita:** inexistenta ca variabila -> `Type()` intoarce `'U'` (Undefined), deci - conditia e falsa implicit. Devine `.T.` doar daca cineva a facut manual, in sesiunea VFP curenta - (de regula din fereastra de comenzi a IDE-ului), `gnFacturareNou = 1`. -- **Cine il seteaza:** nimeni, in cod. E gandit sa fie setat manual de un dezvoltator care vrea sa - testeze prototipul. -- **Confirma sau infirma ca e comutator intre proceduri, nu intre formulare?** Azi, chiar intre - **proceduri** — `factureaza:88-93` face `Return factureaza2(m.tnTip)` dupa raspunsul DA la dialog, - adica sare complet in cealalta procedura, cu tot ce lipseste din ea (sectiunea 2). Planul (S2, - linia 1423) vrea sa-l transforme in comutator **intre formulare**, in interiorul unei singure - proceduri — schimbare corecta, pentru ca azi dubleaza cod, nu doar UI. - -## 5. Proiectarea unificarii - -### Semnatura - -**Neschimbata la aceasta poveste:** `factureaza(tnTip, toFactura)`. Parametrul de sursa explicit -(comanda/contract) e treaba lui **S3c**, care depinde de S2 si il trateaza separat — nu se amesteca -aici (planul insusi spune "Atinge cod comun intregii suite — nu se face impreuna cu alta -modificare"). - -### Ramificarea interna - -Nu e nevoie de o rescriere — `factureaza` de azi e deja versiunea completa si functionala. Interventia -e chirurgicala: - -1. **Muta verificarea `gnFacturareNou`** de la inceputul procedurii (azi `:88-93`, unde face - `Return factureaza2(...)`) la **punctul unde se alege `lcObject`** pentru formularul de articole - (azi `:395` in `factureaza`, ramura corespunzatoare celei din `factureaza2:929`): - ``` - Case tnTip = 27 - lcObject = [frm_avizare_lucrare] - Otherwise - lcObject = IIF(m.llFacturareNoua, [frm_facturare_articole2], [frm_facturare_articole]) - Endcase - ``` - unde `llFacturareNoua` e calculat **o singura data**, la fel ca azi (`Type('gnFacturareNou')='N' - And gnFacturareNou=1`, urmat de acelasi `AMESSAGEBOX` DA/NU), dar **fara** `Return` in alta - procedura — doar seteaza flagul si continua executia normala. -2. **Tot restul ramane identic** — cursorul de articole, `eProforma`, `codmatc`/`codnc8`/`codcpv`, - `GetInstitutiePublica`, `GetSoldClient`, filtrul `RORTC`, rutarea tipurilor 51/52/23/24, ramura - `llCopiere`/`toFactura` — pentru ca acestea nu au nicio legatura cu care formular de articole se - deschide. `factureaza2` nu avea o varianta "corecta, dar diferita" a acestor blocuri — pur si - simplu nu le avea, pentru ca fusesera dezactivate in graba la prototipare (`If .F.` peste tot), - nu pentru ca noul formular ar avea nevoie de alt comportament acolo. -3. **Sterge `factureaza2`** in intregime (`:583-1082`, inclusiv bannerul `INCEPUT`/`SFARSIT`). - -### Ordinea, ca suita sa nu fie stricata niciun moment - -Riscul de "moment in care suita e stricata" e mic aici, spre deosebire de S3c — pentru ca -`factureaza2` **nu are alt apelant decat el insusi prin `factureaza`**. Ordinea propusa: - -1. Muta logica de selectie a lui `lcObject` (pasul 1 de mai sus) **inainte** de a sterge - `factureaza2` — la acest punct, codul are temporar ambele cai active (comutatorul nou + - `factureaza2` inca existent, dar orfan). Se poate testa manual ca `gnFacturareNou=1` deschide - `frm_facturare_articole2` din interiorul lui `factureaza`, cu restul comportamentului corect - (spre deosebire de azi, unde deschiderea prin `factureaza2` sare peste `eProforma`, `codnc8` etc.) -2. Abia dupa verificare, **sterge `factureaza2`** — pasul e sigur pentru ca nu mai are niciun apelant - (linia 90, singurul, a fost deja inlocuita la pasul 1). -3. **Un singur fisier se schimba**: `COMUN\programe\ofacturare.prg`. Niciun apelant din tabelul de - la punctul 3 nu are nevoie de nicio modificare — toti cheama `factureaza(N)` cu aceeasi semnatura, - niciunul nu cheama `factureaza2` direct. -4. **ROACONTRACTE nu necesita nicio schimbare** la aceasta poveste — apelul lui la - `facturare_contracte` -> `factureaza(2/6/52)` nu atinge `factureaza2` deloc. - -## 6. Riscurile de regresie - -- **Cel mai probabil sa se rupa:** ramura `llCopiere`/`toFactura`, pentru ca e cea mai complexa - bucata de logica prezenta doar in `factureaza` (`cursor_retur_document`, apoi suprapunerea cu - `cursor_preturi` pentru a permite adaugarea de articole noi peste cele copiate, `:454-474`). Nu - are nicio acoperire azi in `factureaza2` de comparat — deci orice greseala de copy-paste la mutarea - liniei `lcObject` risca sa rupa exact aceasta ramura, nu partea "noua". **Testat manual din - `copiere_factura` (`ofacturare_comun.vc2:3710`) inainte si dupa.** -- **Al doilea cel mai probabil:** rutarea tipurilor 23/24/51/52, pentru ca azi cad in `Do Case`-uri - diferite intre cele doua proceduri (sectiunea 2) — o gresala de aliniere la stergerea lui - `factureaza2` nu ar afecta functional (pentru ca acele Do Case-uri raman neatinse, doar duplicate - disparute), dar merita o trecere vizuala pe fiecare tip listat la punctul 3 dupa modificare. -- **ROACONTRACTE** — singurul apelant real din alt produs. **Nu poate fi testat din arborele de lucru - ROAFACTURARE** — cere un build/rulare separata a ROACONTRACTE, cu propriul `ofacturare.prg` - sincronizat manual (nu e acelasi fisier fizic, e o copie). Verificarea "gata" trebuie sa includa - explicit macar un test manual pe ROACONTRACTE, tip contract-factura, nu doar pe ROAFACTURARE. -- **`frm_facturare_articole2` insusi ramane netestabil functional** la aceasta poveste — `Init`-ul - lui nu completeaza antetul (S1), deci deschiderea cu `gnFacturareNou=1` va arata un formular cu - campuri goale la fel ca azi. Nu e o regresie introdusa de S2, dar e o asteptare de gestionat: S2 - **nu** face `frm_facturare_articole2` utilizabil, doar elimina procedura duplicata din spatele lui. -- **Nimic din asta e testabil headless** — toate cele ~33 puncte de intrare sunt declansate din - meniuri/formulare UI (`ON SELECTION BAR`, `DO ... IN ...` din metode de clic), iar rezultatul se - verifica prin `ACT`/`VANZARI`/`RUL` in Oracle sau vizual pe formular. Testarea de dupa modificare - trebuie sa fie manuala, minim pe: o factura lista de preturi lei (tip 1), un aviz din comanda (tip - 21), o factura din contract lei (tip 2, din `ROACONTRACTE` daca fezabil), o copiere de factura - (`toFactura` populat), si — cu `gnFacturareNou=1` setat manual — confirmarea ca - `frm_facturare_articole2` se deschide fara eroare (nu neaparat ca arata corect, asta e S3). - -## 7. Criteriul de "gata", rescris si verificabil - -Planul spune azi: *"`factureaza2` nu mai exista, iar comutatorul alege formularul, nu procedura."* -Rescris, verificabil fara ambiguitate: - -1. **Structural** (verificabil cu o comanda, fara sa citesti codul): - ``` - Get-ChildItem -Recurse 'D:\ROA\ROAFACTURARE\COMUN\programe\ofacturare.prg' | - Select-String -Pattern '^\s*(PROCEDURE|FUNCTION)\s+factureaza2\b' - ``` - trebuie sa intoarca **zero rezultate**. - ``` - Select-String -Path 'D:\ROA\ROAFACTURARE\COMUN\programe\ofacturare.prg' -Pattern 'gnFacturareNou' - ``` - trebuie sa intoarca **exact o aparitie**, situata in interiorul lui `factureaza`, la punctul unde - se alege `lcObject` (nu inainte de constructia lui `poDate`, ca azi). -2. **Comportamental**, testat manual, fara `gnFacturareNou` setat (calea implicita, majoritatea - utilizatorilor): cele patru documente listate la sectiunea 6 (factura lista de preturi, aviz din - comanda, factura din contract, copiere factura) produc aceleasi randuri in `ACT`/`VANZARI`/`RUL` - ca inainte de modificare. -3. **Comportamental**, cu `gnFacturareNou = 1` setat manual si DA la dialog: se deschide - `frm_facturare_articole2` (nu `frm_facturare_articole`), fara eroare la deschidere, cu - `eProforma`/`codnc8`/`codcpv`/`GetInstitutiePublica`/`GetSoldClient`/filtrul RORTC/rutarea - 51-52-23-24 toate active identic cu calea implicita (spre deosebire de azi, cand - `gnFacturareNou=1` le sarea pe toate). -4. **ROACONTRACTE**: minim un test manual de facturare din contract, dupa sincronizarea - `ofacturare.prg` in acel produs, confirma acelasi rezultat ca inainte. - -## Ce nu s-a putut stabili si de ce - -- **Cine activeaza popup-urile `politica.mn2`/`contracte.mn2`** (nivelul chiar deasupra apelurilor - directe la `factureaza(N)`) nu a fost trasat pana la butonul/grid-ul exact — nu era necesar pentru - diff-ul procedurilor sau pentru tabelul de apelanti (punctul de interes e apelul la `factureaza`, - nu inca un nivel de indirectare deasupra), dar daca implementarea muta si aceste meniuri, merita - o trecere separata. -- **Daca alte produse (ROAGEST, ROAAUTO, ROAACNPRO, ROAIMOB, ROACONT) au propriul lor - `oproceduri_facturare.prg`/`ocomenzi.vc2` invocat din cod specific produsului, dincolo de ce am - cautat** — cautarea a acoperit exact numele `factureaza`/`factureaza2` si cei noua wrapperi din - `oproceduri_facturare.prg`; nu a acoperit eventuale cai complet diferite catre acelasi fisier - (de exemplu un meniu `.mn2` specific unui alt produs care ar chema un wrapper cu alt nume). Pentru - aceste cinci produse, cautarea directa (`factureaza(`, cei noua wrapperi) a dat zero rezultate in - codul specific produsului — suficient pentru concluzia "nu apeleaza azi", dar nu e o dovada - negativa absoluta de "nu ar putea apela niciodata prin alt canal". -- **Testarea reala pe ROACONTRACTE** nu a fost efectuata (read-only, fara rulare de cod) — doar - confirmata existenta apelului in sursa (`ferestre_contracte.vc2:1542-1546`). -- **Diferenta minora de text** intre mesajele "Luna inchisa" ale celor doua proceduri - (`factureaza:105` vs `factureaza2:594`, un caracter diacritic afisat diferit la citire) nu a fost - investigata mai departe — pare artefact de encoding la citire (cp1250 in fisier), nu o diferenta - de continut intentionata; oricum dispare odata cu stergerea lui `factureaza2`. - -## Bug-uri semnalate, nereparate - -- **`factureaza2` e, in forma actuala, non-functional dincolo de deschiderea formularului de - antet dezactivata** — chiar daca cineva ar apela azi acest cod pe o cale ipotetica ocolind - `factureaza`, executia interogarii SQL e dezactivata (`lnSucces = 1` hardcodat la `:827`, fara - `goExecutor.oExecute`) si verificarea "nu exista articole" e dezactivata (`:838`) — codul ar merge - mereu pe ramura de succes cu un cursor `crsarticole` nepopulat de aceasta rulare (posibil ramas - dintr-un apel anterior, cu alt `tnTip`). Nu se repara aici — S2 il sterge oricum, deci bug-ul - dispare odata cu procedura, nu prin fix separat. -- Niciun bug nou gasit in `COMUN\clase\ofacturare_comun.vc2` sau `COMUN\programe\ofacturare_editare.prg` - (perimetrul #6) — singura atingere a acestor fisiere in aceasta cercetare a fost citirea liniei - `copiere_factura` din `ofacturare_comun.vc2:3710`, care nu are nimic suspect. diff --git a/docs/cercetare/s3_portare_antet.md b/docs/cercetare/s3_portare_antet.md deleted file mode 100644 index 4c5560a..0000000 --- a/docs/cercetare/s3_portare_antet.md +++ /dev/null @@ -1,478 +0,0 @@ -# Cercetare — proiectare S3: portarea logicii de antet in formularul unificat - -Investigatie READ-ONLY pentru povestea **S3** din `docs\plan_13_unificare_formular_facturare.md:1428-1439`. -Fara editari de cod, fara `git_sync.ps1`/`txt2vcx.ps1`, fara commit. Constrangerile din briefing — -`COMUN\clase\ofacturare_comun.vc2` si `COMUN\programe\ofacturare_editare.prg` doar citite (perimetrul -#6), `pack_facturare` si `pack_facturare_comun`-ul intern neatinse, analiticele read-only — respectate. - -## Verdict (5-10 randuri) - -S3 e **mai mare decat inventarul din plan sugereaza pe numar de metode** (29 `do_cauta_*` reale, nu -30 — vezi punctul 1), dar **mult mai mare decat sugereaza pe complexitate reala**, pentru doua motive -descoperite aici, nu presupuse: (a) `Init`, `inainte_de_do_termin` si — partial — `do_cauta_fdoc` sunt -**omonime cu semantica total diferita pe toate cele patru formulare-sursa** (antet vs. articole vs. -alte-date), deci portarea "o singura data" cere de fapt patru fuziuni de metoda, nu una singura cu -ramificare; (b) bug-ul **#16 e real, reprodus pe cod pana la linia exacta**, si **nu e un bug de focus** -— e o consecinta a faptului ca bucla de reincercare din `factureaza`/`factureaza2` -(`ofacturare.prg:174-571`) trateaza **orice esec SQL** (nu doar cursul lipsa) ca pe un "DA, continui cu -alt document", redeschizand din senin formularul de antet de la zero. Unificarea **nu-l reproduce -automat** — poate chiar sa-l elimine ca efect secundar, daca antetul nu se mai reconstruieste de la -Init dupa un esec Oracle in pasul urmator. Obstacolul principal pentru implementare nu e portarea -codului de validare (majoritatea e `Do Case`/`amessagebox` mecanic), ci **reconcilierea a patru cicluri -de viata `Init`/`inainte_de_do_termin` independente intr-unul singur**, cu ordinea de dependente de la -punctul 5. - ---- - -## 1. Inventarul metodelor de portat — numere corectate - -Sursa: `vfp_symbols.ps1 -Class frm_date_factura` / `-Class frm_date_aviz`, confirmat pe fisierul real -`COMUN\clase\ofacturare.vc2` (nu `.bak`). - -**`frm_date_factura`** (`ofacturare.vc2:8482-9869`) — **16** metode `do_cauta_*`, nu 17: - -| Metoda | Linii | Ce face | Echivalent in `frm_facturare_articole2` | -|---|---|---|---| -| `do_cauta_altele` | `8940-8961` (21) | cautare pe campul cu eticheta dinamica ("Altele"/contract/comanda/etc.) | nu | -| `do_cauta_avize` | `8963-8999` (36) | cautare aviz-sursa (tip=4) | nu | -| `do_cauta_client` | `9001-9060` (59) | cautare client, populeaza sold/cod fiscal | nu | -| `do_cauta_comanda` | `9062-9090` (28) | cautare comanda-sursa | nu | -| `do_cauta_contract` | `9092-9153` (61) | cautare contract-sursa | nu | -| `do_cauta_factura` | `9155-9171` (16) | cautare factura-sursa (credit note, tip 7) | nu | -| `do_cauta_facturi` | `9173-9212` (39) | cautare multi-factura (retur, tip 8/9) | nu | -| `do_cauta_fdoc` | `9214-9225` (11) | cautare generica fel-document, `caut_fdoc()` — **identica byte-cu-byte cu varianta din `frm_date_aviz`**, vezi punctul 4 | nu | -| `do_cauta_gestiune_init` | `9227-9242` (15) | cautare gestiune sursa | nu | -| `do_cauta_locatii` | `9244-9253` (9) | cautare locatie (tip 45) | nu | -| `do_cauta_lucrare` | `9255-9279` (24) | cautare lucrare | nu | -| `do_cauta_responsabil` | `9281-9301` (20) | cautare responsabil | nu | -| `do_cauta_sectie` | `9303-9342` (39) | cautare sectie | nu | -| `do_cauta_valuta` | `9344-9359` (15) | cautare valuta, muta focus pe `clb_zi_curs` daca exista, altfel pe serie (`:9351-9356`, deja conditionat `Type(...)<>'U'`) | nu | -| `do_cauta_venchelt` | `9361-9391` (30) | cautare venit/cheltuiala | nu | -| `do_cauta_venit` | `9393-9394` (1) | stub/alias, o linie — de verificat ce mai apeleaza | nu | -| `do_schimba_tipdoc` | `9396-9438` (42) | vezi punctul 6 — schimba `poDate.nIdTipDoc` si reinitializeaza serie+numar | **nu** — apelat orfan din prototip, vezi punctul 3 | -| `do_verifica` | `9440-9453` (13) | verificare ANAF pe cod fiscal client | nu (dar exista pe `frm_facturi`, aceeasi semantica) | -| `inainte_de_do_termin` | `9455-9561` (106) | validare completa antet inainte de trecerea la articole | **omonim cu semantica diferita** pe toate 4 formulare, vezi mai jos | -| `Init` | `9563-9796` (234) | vezi punctul 5 | **omonim cu semantica diferita**, vezi mai jos | -| `Clb_dataact...LostFocus` | `9798-9813` (15) | sincronizeaza `zi_curs=dataact` daca `clb_zi_curs` exista | nu | -| `Clb_dataireg...LostFocus` | `9815-9831` (16) | idem, pe `dataireg` | nu | -| `Clb_nract...LostFocus` | `9833-9835` (2) | — | nu | -| `Clb_serie_act...LostFocus` | `9837-9844` (7) | — | nu | -| `Ct_clb_fdoc._combobox1.Init` | `9846-9855` (9) | populeaza combo-ul FACTURA/PROFORMA/BON FISCAL | nu | -| `Ct_clb_fdoc._combobox1.LostFocus` | `9857-9859` (2) | `thisform.do_schimba_tipdoc()` — vezi punctul 6 | nu (frm_facturare_articole2 are un apel similar, dar **orfan**, vezi punctul 3) | -| `Ed_tx_simplu1._EDBASE1.KeyPress` | `9861-9867` (6) | — | nu | - -**`frm_date_aviz`** (`ofacturare.vc2:6566-7618`) — **13** metode `do_cauta_*`, numarul din plan e corect: - -| Metoda | Linii | Observatie | -|---|---|---| -| `do_cauta_altele` | `6882-6894` (12) | mai scurta decat pe factura (21) — set de tipuri mai mic | -| `do_cauta_avize` | `6896-6930` (34) | apropiata ca marime de factura (36) | -| `do_cauta_client` | `6932-7009` (77) | **mai lunga** decat pe factura (59) — trateaza eticheta variabila "Retur de la"/"Gestiune sursa" pe transfer | -| `do_cauta_comanda` | `7011-7034` (23) | | -| `do_cauta_contract` | `7036-7091` (55) | | -| `do_cauta_fdoc` | `7093-7104` (11) | **identica cu factura**, vezi punctul 4 | -| `do_cauta_gestiune_dest` | `7106-7131` (25) | **doar pe aviz** — gestiune destinatie la transfer subunitati | -| `do_cauta_gestiune_init` | `7133-7144` (11) | mai scurta decat pe factura (15) | -| `do_cauta_lucrare` | `7146-7165` (19) | | -| `do_cauta_politica` | `7167-7182` (15) | **doar pe aviz** — `Ct_clb_politici_preturi` | -| `do_cauta_responsabil` | `7184-7204` (20) | aceeasi lungime ca factura | -| `do_cauta_sectie` | `7206-7240` (34) | | -| `do_cauta_venchelt` | `7242-7275` (33) | | -| `inainte_de_do_termin` | `7277-7352` (75) | **omonim**, vezi mai jos — mai scurta decat factura (106): fara validare data-scadenta, fara validare valuta, fara validare client (!) | -| `Init` | `7354-7600` (247) | **omonim**, nu citit integral aici (doar `9563-9650`+`9733-9796` echivalentul pe factura) | -| `Clb_dataact...LostFocus` | `7602-7605` | | -| `Clb_dataireg...LostFocus` | `7607-7612` | | -| `Clb_nract...LostFocus` | `7614-7616` | | - -**`frm_date_aviz` nu are `do_schimba_tipdoc`** — confirmat, container-ul `Ct_clb_fdoc` de pe aviz e -o cautare simpla, fara combo si fara evenimentul `LostFocus` care declanseaza schimbarea de tip. - -**Total real: 16 + 13 = 29 metode `do_cauta_*`**, plus `do_schimba_tipdoc` (doar factura), `do_verifica` -(doar factura), `inainte_de_do_termin` si `Init` (omonime pe ambele) — planul le numara global corect -ca "17+13 ... plus restul", dar cifra "17" pe factura e gresita cu o unitate: **16**. - ---- - -## 2. Metodele omonime — capcana centrala a portarii - -**Nivelul care conteaza cel mai mult, necunoscut inca in plan**: `Init` si `inainte_de_do_termin` sunt -**acelasi nume de metoda pe toate cele patru formulare-sursa implicate in unificare** -(`frm_date_factura`, `frm_date_aviz`, `frm_facturare_articole`/`frm_facturare_articole2`, -`frm_alte_date`), fiecare cu corp complet diferit (antet vs. compunere articole vs. date suplimentare). -O singura clasa unificata nu poate avea patru metode `Init`. **Portarea "o singura data" a S3 inseamna -concret: patru corpuri de `Init` si patru corpuri de `inainte_de_do_termin` trebuie topite intr-un -singur `Init` si un singur `inainte_de_do_termin` (sau redenumite si inlantuite explicit)** — asta e -efortul real, nu doar mutarea codului de validare camp-cu-camp. - -**`inainte_de_do_termin`, factura vs. aviz — diferenta reala, cu citat** (comparate direct, ambele corpuri -citite integral, `ofacturare.vc2:9455-9561` si `:7277-7352`): - -- **Factura valideaza `id_client` obligatoriu; aviz nu valideaza deloc acest camp**: - ``` - ofacturare.vc2:9510-9513 (doar pe factura) - Case Empty(poDate.id_client) Or Isnull(poDate.id_client) - amessagebox("Nu ati ales clientul!",48,"Atentie") - This.ct_clb_nume_client.SetFocus() - ``` - Motiv plauzibil (neconfirmat mai departe): avizele de transfer intre subunitati (tip 23/25/30/41) nu - au neaparat un "client" — au o gestiune destinatie, validata separat. -- **Factura valideaza scadenta si valuta; aviz nu are deloc aceste `Case`-uri** — `Empty(poDate.datascad)`, - `poDate.datascad=21 in afara de 27/30). **Discriminatorul natural pentru ramificare exista deja**: - `poDate.nIdTipDoc` (5=FACTURA, 6=AVIZ — calculat de apelant la `ofacturare.prg:192-196`) sau simpla - apartenenta a lui `poDate.tip` la unul din cele doua seturi disjuncte, **nu** un flag nou. -- **Contul folosit la verificarea de facturi duplicate difera**: `facturi_duplicate('4111', 0, ...)` - (factura, `:9545`, cont clienti) vs. `facturi_duplicate('418', 0, ...)` (aviz, `:7341`, alt cont) — - un literal hardcodat care trebuie sa devina el insusi parte din ramificare, nu doar mesajul. - -**Alte omonime cunoscute, cu semantica diferita, relevante pentru zona din jurul lui S3** (nu introduse -aici — reconfirmate, vezi si `docs\handoff_13_formular_unificat.md:192-194`): -- `do_calculeaza_discount` — **la nivel de document** pe `frm_facturare_articole` (`:13405-13424`) si - `frm_facturare_articole2` (`:17670-17686`, verificat aici: identic ca semantica, scrie - `Thisform.ndiscfactron`/`ndiscfactval`); **la nivel de linie** pe `frm_articol_factura` (`:1874-1976`, - scrie `poArticol.discount_unitar*`). Nu intra direct in S3 (nu e printre metodele antetului), dar - orice cod nou care apeleaza `do_calculeaza_discount` prin nume pe formularul unificat trebuie sa - stie care semantica o vrea. -- `do_verifica` — pe `frm_date_factura` (`:9440-9453`, verificare ANAF) si pe `frm_facturi` - (verificare ANAF, aceeasi semantica) — omonim inofensiv, aceeasi actiune. -- `do_cauta_gestiune_init` / `do_cauta_responsabil` / `do_cauta_sectie` / `do_cauta_venchelt` / - `do_cauta_altele` / `do_cauta_comanda` / `do_cauta_contract` / `do_cauta_avize` / `do_cauta_lucrare` — - **acelasi nume pe factura si aviz, lungimi diferite** (vezi tabelele de la punctul 1) — nu omonime - periculoase (aceeasi intentie: cautare pe camp), dar **nu identice** — portarea trebuie sa ia corpul - fiecareia separat, nu sa presupuna ca sunt duplicate de sters. -- **Singura pereche confirmata identica byte-cu-byte**: `do_cauta_fdoc` (vezi punctul 4) — aceasta - chiar poate fi portata o singura data, fara ramificare. - ---- - -## 3. Ce e deja in prototip vs. ce lipseste - -`frm_facturare_articole2` (`ofacturare.vc2:15741-19355`) are controalele de antet montate direct pe -formular (confirmat in `inventar_controale_formulare.md`), dar **zero logica**: - -| Metoda/mecanism | Exista in `frm_facturare_articole2`? | Detaliu | -|---|---|---| -| Toate cele 29 `do_cauta_*` | **NU** | niciunul in lista de metode proprii a clasei (confirmat cu `vfp_symbols.ps1 -Class`) | -| `do_schimba_tipdoc` | **NU, dar e APELAT** — cod orfan | `clb_fdoc.cboFdoc.Valid` (`:19255-19257`) contine `thisform.do_schimba_tipdoc()`, dar clasa **nu are** aceasta metoda in lista proprie. Daca userul ar schimba azi combo-ul `clb_fdoc` pe acest formular mort, ar cadea cu eroare de metoda inexistenta. Confirma independent constatarea din plan ("controalele sunt acolo, logica nu") — de fapt e mai rau: e cod care ar crapa daca s-ar activa calea, nu doar cod lipsa | -| `inainte_de_do_termin` | **DA, dar cu alta semantica** | `:18809-18986` (178 linii) — valideaza ARTICOLE (stoc, discount, TVA), nu antetul; e omonimul de care vorbeste punctul 2, nu un candidat de reutilizare pentru validarea de antet | -| `Init` | **DA, dar cu alta semantica** | `:18988-19080` (92 linii, citit integral aici) — confirmat: doar grid/curs-label/total-mode/coloane in/afara valuta; **niciun** `poDate.xxx -> control.Value`. Nu populeaza antetul deloc | -| Serie/numar (`Clb_serie_act1`/`Clb_nract`) | Controale prezente, dar fara wiring de populare in `Init` | `Clb_serie_act1.TEXT_SIMPLU1.LostFocus` (`:19263-19270`) exista ca metoda proprie — nu verificat aici daca reproduce `genereazanumar` | - -**Concluzie punctul 3**: prototipul e o **coaja vizuala**, nu un schelet de 50% functional. Ce trebuie -scris pentru S3 e efectiv tot codul de comportament — controalele economisesc timpul de aranjare in -`.scx`/`.vcx`, nu timpul de portare a logicii. - ---- - -## 4. Ramificarea factura / aviz in interior - -**Discriminatorul de ramificare recomandat: `poDate.nIdTipDoc`** (5=FACTURA, 6=AVIZ), deja calculat de -apelant inainte de `Createobject` (`ofacturare.prg:187-196`) si deja disponibil pe `poDate` in -momentul in care formularul unificat ar porni `Init`. Alternativ, acelasi rezultat se obtine testand -direct apartenenta lui `poDate.tip` la unul din cele doua seturi disjuncte folosite azi in -`inainte_de_do_termin` (vezi punctul 2) — cele doua seturi nu se suprapun niciodata, deci nu exista -ambiguitate. **Nu e nevoie de o proprietate noua** gen `lEsteAviz` — `nIdTipDoc` face deja treaba, si -e deja pe obiectul `poDate` care trece prin tot lantul de apeluri. - -Ce trebuie sa ramifice concret, cu dovada: -1. **`inainte_de_do_termin`** — Cases specifice fiecarei parti (vezi citatele de la punctul 2), plus - constanta de cont (`'4111'` vs `'418'`) la `facturi_duplicate`. -2. **`Init`** — seturile `Do Case poDate.tip` care decid ce se elimina (`RemoveObject`) difera pe - fiecare parte (S1 documenteaza deja fiecare camp cu conditia lui de vizibilitate; portarea trebuie - sa pastreze fiecare conditie identic, nu sa le generalizeze pe ghicite). -3. **`do_schimba_tipdoc`** — **exista doar pe factura**; pe aviz nu exista mecanismul de schimbare a - tipului de document dupa deschiderea formularului (containerul `ct_clb_fdoc` de pe aviz e cautare - simpla, fara `_combobox1`). Ramificarea aici nu e "cod diferit pe aceeasi metoda" ci "metoda - prezenta doar pe o ramura" — formularul unificat trebuie sa decida daca aviz capata acest - comportament nou (schimbare tip dupa deschidere) sau ramane fara el, ca azi. -4. **`do_cauta_fdoc`** — **nu ramifica**, e identic (punctul 1) — poate fi portat o singura data, fara - `If nIdTipDoc=...`. -5. **`do_cauta_client`** — aviz e cu 18 linii mai lung (77 vs 59) pentru eticheta variabila - "Retur de la"/"Gestiune sursa" pe transfer (S1, randul "Client") — de citit integral la - implementare pentru ramificarea exacta, nu verificat linie-cu-linie aici. - ---- - -## 5. `Init`-urile — ce face fiecare, in ce ordine, ce depinde de ce - -**Secventa de azi, pe drumul normal (factura din lista de preturi, fara eroare Oracle):** - -1. **Apelantul** (`ofacturare.prg:factureaza`, inainte de orice `Createobject`): construieste - `poDate = Createobject("oDateFactura", ...)` si `poGeneratorNumere = Createobject("oGeneratorNumere")` - (`:184-185`), seteaza `poDate.nIdTipDoc` (`:187-196`), cheama - `poDate.completeaza_setari_document(...)` daca e copiere (`:200-206`), apoi - `poGeneratorNumere.ResetNumere()` si `poDate.rezultat_serii = poGeneratorNumere.creeaza_cursor_serii(...)` - (`:208-211`) — **toate acestea ruleaza inainte ca vreun formular sa existe**. Orice `Init` de mai jos - presupune `poDate`/`poGeneratorNumere` deja populate. -2. **`frm_date_factura.Init`** (`:9563-9796`, 234 linii, citit integral aici) — `DoDefault()`, apoi - masoara inaltimile containerelor de antet intr-un array (`laPozitii`), decide pe `Do Case - gnScadereStoc/poDate.tip` ce containere elimina (`RemoveObject`) si reface layout-ul pe verticala, - initializeaza serie+numar (`.clb_serie_act.do_initializeaza(poDate.rezultat_serii)`, `:9737`), si - **la final** seteaza focus: pe `ct_clb_valuta` daca documentul e in valuta si campul valutei apare - deasupra tipului si nu are inca valuta aleasa, **altfel pe `ct_clb_fdoc`** (`:9788-9793`, citat - integral la punctul 6). Depinde de: `poDate` (tip, in_valuta, ...), `poGeneratorNumere` - (`creeaza_cursor_serii` deja rulat de apelant), variabile globale de firma (`gnScadereStoc`). -3. **`frm_facturare_articole2.Init`** (`:18988-19080`, 92 linii, citit integral aici) — confirmat: - **nu atinge niciun camp de antet**. Configureaza doar gridul (`nNrInregVizibile`), eticheta - cursurilor valutare, modul total (`gnModTotFact`), si elimina coloanele RON sau valuta din grid - dupa `poDate.in_valuta`. Depinde de: `poDate.zi_curs`/`in_valuta`/`tip`, `crscursuri` (populat de - apelant intre pasii 2 si 3, nu de acest `Init`). -4. **`frm_alte_date.Init`** (`ferestre_cere_date.vc2:3105-3207`, 103 linii, citit integral aici) — - `DoDefault()`, citeste `poDate.dataora_exp`; **daca nu e proforma**: cauta ultimul delegat/masina - folosite pentru client (apel Oracle `cauta_date_ultima_factura[_tip]`, `:3119-3136`), populeaza - combo-ul de casa dintr-un cursor Oracle nou (`v_nom_casa`, `:3156-3173`), seteaza `opt_incasat` - implicit daca `poDate.incasat<>0`; **daca e proforma**: elimina complet grupul delegat/masina/ - incasare (`RemoveObject` in cascada, `:3192-3201`) si redimensioneaza formularul la zona de text - aditional. **Risc gasit, neurmarit mai departe**: pe eroare la interogarea combo-ului de casa, - `Init` cheama `poGeneratorNumere.dezaloca_numar(5)` si `Return` (`:3159-3161`) — acelasi tipar de - "dezalocare numar pe eroare SQL neasteptata" ca la bug-ul #16 (punctul 6), dar intr-un `Init`, nu - intr-o bucla — **nu s-a verificat daca are aceeasi consecinta de recreare completa a formularului**; - semnalat, nu investigat suplimentar aici din motive de buget de context. - -**Ordinea de dependente, explicit**: `poDate`+`poGeneratorNumere` (apelant) -> `frm_date_factura`/ -`frm_date_aviz.Init` (foloseste serie/numar deja create) -> **Oracle: cursorul de articole** -(`ofacturare.prg:266-308`, aici poate pica bug #16) -> `frm_facturare_articole2.Init` (foloseste -`crscursuri` populat intre pasi, nu de propriul `Init`) -> `frm_alte_date.Init` (face **inca** un apel -Oracle nou, cu propriul risc de dezalocare). **Pentru formularul unificat, aceasta secventa de patru -`Init`-uri trebuie sa devina un singur `Init` care ruleaza fazat** (o singura data la deschidere) — -sau ramane o discutie deschisa daca vreo faza (Oracle-lookup-ul din `frm_alte_date.Init`) ramane -intarziata pana la deschiderea sectiunii pliate, ca sa nu incarce round-trip-uri Oracle inutile cand -utilizatorul nu ajunge niciodata la acea sectiune. - ---- - -## 6. Bug-ul #16 — mecanismul exact, verificat pe cod - -**Text original** (`COMUN\docs\todos.txt:45`): *"la revenire din formularul de curs valutar, focusul -revine inainte de numar document, cred ca pe TIP DOCUMENT, si la iesire din serie se regenereaza numar -act, ceea ce este periculos daca utilizatorul l-a schimbat".* - -**Verdict: NU e un bug de focus. E o bucla de reincercare care trateaza orice esec Oracle ca pe un "DA, -mai fac un document" si reconstruieste formularul de antet de la zero — focusul si regenerarea -numarului sunt doar simptomele vizibile ale acestei reconstructii.** - -Lantul complet, verificat linie cu linie: - -1. Utilizatorul completeaza antetul, apasa Termina — `inainte_de_do_termin` trece, formularul de antet - se inchide (`pnButon=1`). -2. Apelantul (`factureaza`, `ofacturare.prg:266-308`) construieste cursorul de articole din Oracle - (`cursor_preturi`/etc., functie de `poDate.tip`). **Daca cererea esueaza** (`lnSucces<0`, - `:313`) — inclusiv, dar nu numai, cu eroarea Oracle `-20005` "Nu este setat cursul..." (curs - valutar lipsa pentru o valuta din listele de preturi ale utilizatorului, verificata neconditionat - in `pack_facturare.verifica_cursuri_valute`, documentat deja in `zi_curs_validare.md`): - ``` - ofacturare.prg:313-322 - If lnSucces < 0 - AMESSAGEBOX(goExecutor.oPrelucrareEroare(), 16, "Eroare") - If goExecutor.nEroare = 20005 - vizualizeaza_curs(poDate.zi_curs) && deschide "formularul de curs valutar" (frm_curs, modal) - ENDIF - poGeneratorNumere.dezaloca_numar(poDate.nIdTipDoc) && numarul alocat se elibereaza - Else - ... [singurul loc unde se cere "Doriti sa continuati?" si se seteaza lnRaspuns] - Endif - ``` - `vizualizeaza_curs` (`oproceduri_curs.prg:8-42`) e confirmat: deschide `Createobject("frm_curs", - tdDataCurs).Show(1)` — chiar formularul din reclamatie. -3. **Punctul central, verificat prin numararea `If`/`Else`/`Endif` din fisier**: prompt-ul "Doriti sa - continuati cu operatii de acest fel?" care seteaza variabila de bucla `lnRaspuns` **exista doar in - ramura `Else` a lui `If lnSucces<0`** (`ofacturare.prg:555-564`, in interiorul aceluiasi bloc - `If/Else/Endif` care se inchide abia la `:568`). **Pe ramura de eroare (`lnSucces<0`), `lnRaspuns` - nu e niciodata atins.** Isi pastreaza valoarea din intrarea in aceasta iteratie a buclei — pe primul - document, `6` (initializat la `:174`, inainte de `Do While lnRaspuns = 6` de la `:175`). -4. Bucla externa (`Do While lnRaspuns = 6 ... Enddo`, `:175-571`) **reintra automat**, fara nicio - intrebare catre utilizator, pentru ca `lnRaspuns` e inca `6`. In aceasta noua iteratie: - `poGeneratorNumere.ResetNumere()` si `poDate.rezultat_serii = poGeneratorNumere.creeaza_cursor_serii(...)` - ruleaza din nou (`:208-211`), apoi se creeaza **o instanta noua** de `frm_date_factura`/`frm_date_aviz` - (`Createobject`, `:230`) si se arata modal (`:235`) — `poDate` insusi **nu** e recreat (ramane - acelasi obiect, cu `nract`/`serie_act` deja setate de utilizator), dar **formularul da**, deci - `Init` ruleaza de la capat. -5. `frm_date_factura.Init` (`:9563-9796`) reface layout-ul si, **la final, seteaza focus necondiționat - pe `ct_clb_fdoc`** (tip document) in cazul normal: - ``` - ofacturare.vc2:9788-9793 - DO CASE - CASE poDate.in_valuta = 1 and this.ct_clb_valuta.Top < this.ct_clb_fdoc.Top AND EMPTY(NVL(poDate.nume_valuta,'')) - this.ct_clb_valuta.SetFocus() - OTHERWISE - this.ct_clb_fdoc.SetFocus() - ENDCASE - ``` - **Aceasta e "focusul care revine pe TIP DOCUMENT"** din reclamatie — nu un bug izolat de focus, ci - comportamentul normal de `Init` al unui formular nou, aparut unde utilizatorul nu se astepta la un - formular nou. -6. Cand focusul paraseste `ct_clb_fdoc` (Tab, click in alta parte — orice), se declanseaza - `Ct_clb_fdoc._combobox1.LostFocus -> thisform.do_schimba_tipdoc()` (`:9857-9859`). Prima linie a - acestei metode, **inainte de orice verificare "tipul chiar s-a schimbat?"**: - ``` - ofacturare.vc2:9417 (in do_schimba_tipdoc, INAINTE de guard-ul de la :9419-9421) - This.clb_serie_act._cbbase1.LostFocus() - ``` - — apeleaza **neconditionat** handler-ul de LostFocus al combo-ului de serie, indiferent daca tipul - documentului s-a schimbat sau nu. -7. Acel handler (`serii_numere.vc2:138-140`, clasa `clb_serie_act`) cheama `genereazanumar` (`:114-136`): - ``` - serii_numere.vc2:122-127 - If poGeneratorNumere.verifica_serie(This.nid_tipdoc) Or &lcValoare. = 0 - lcValoare = lcValoare+[=]+ALLTRIM(Str(poGeneratorNumere.aloca_numar(This.nid_tipdoc,Null),20,0)) - &lcValoare - This.Parent.Refresh() - Endif - ``` - — **aloca un numar nou** si il scrie peste `poDate.nract` prin macro, **necondiționat de ce numar - avea utilizatorul inainte**. **Asta e "iesirea din serie regenereaza numarul actului"** din - reclamatie — confirmat exact, pana la linia care face scrierea. - -**Verdict pe intrebarea planului ("se rezolva #16 in S3, sau se reproduce bug-ul?"): se poate rezolva -in S3, cu un cost mic, dar nu e o consecinta automata a unificarii — trebuie tratat explicit.** -Cauza reala nu e in `frm_date_factura`/`do_schimba_tipdoc`/`clb_serie_act` (cod care se comporta -"corect" fata de contractul lui local), ci in bucla de reincercare din apelant -(`ofacturare.prg:174-571`), care **nu distinge "utilizatorul a confirmat ca vrea alt document" de -"cererea Oracle a esuat"**. Doua directii posibile, ambele in afara perimetrului strict al portarii de -antet, dar declansate direct de ea: -- **(a) minimal**: in formularul unificat, dupa un esec Oracle la pasul de construire a cursorului de - articole (echivalentul liniei `:313`), **nu se distruge formularul de antet** — antetul e deja o - singura sectiune persistenta a aceluiasi formular, nu un obiect separat recreat de apelant; simpla - arhitectura unificata (un `Init` per sesiune de emitere, nu per formular) elimina pasul 4 de mai sus, - deci elimina intreg lantul 5-7. **Acesta e motivul pentru care unificarea are sansa reala sa rezolve - #16 ca efect secundar** — dar numai daca implementarea nu recreeaza formularul intreg la reincercare - dupa eroare Oracle (ceea ce ar reproduce bug-ul identic, doar mutat). -- **(b) daca (a) nu e suficient** (de ex. daca dupa fix la curs tot trebuie relansata interogarea de - articole): `lnRaspuns` (sau echivalentul lui in noua arhitectura) trebuie resetat explicit pe orice - cale care iese din ramura de eroare, ca sa nu mai fie confundat cu "utilizatorul a spus DA". - -**Coordonarea cu #6 (decizia 30 si constrangerea din briefing) — verificat direct pe diff-uri, nu doar -pe proza handoff-ului**: `docs\diff_s4_valuta_dialog*.patch` (trei fisiere) ating exclusiv -`COMUN\clase\omodificari.vc2`, `COMUN\programe\ofacturare_editare.prg` si un fisier de test — -**niciunul nu atinge `ofacturare.vc2` (unde traiesc `frm_date_factura`, `do_schimba_tipdoc`, -`clb_serie_act`) sau `ofacturare.prg` (unde traieste bucla `factureaza`)**. Confirmat si de continutul -lui handoff intermediar (sters): intreaga lui cercetare e despre `frm_articol_factura` -(dialogul de adaugare articol pe linie, alt fisier/clasa) si `frm_modific2024.cmdAdaugaArticol.Click` -— un mecanism de valuta **pe linie de articol**, fara nicio legatura cu antetul sau cu bucla de -emitere. **#6 nu a atins deloc lantul lui #16. Bug-ul e in intregime valabil si neschimbat.** - ---- - -## 7. Ordinea de lucru propusa pentru S3 - -Pasi care lasa suita functionala dupa fiecare (calea veche ramane in productie pe tot parcursul — -niciun pas nu sterge `frm_date_factura`/`frm_date_aviz`/`frm_alte_date` originale): - -1. **Fuzioneaza `Init`-urile de antet** (`frm_date_factura` + `frm_date_aviz`) intr-o metoda unica pe - formularul unificat, ramificata pe `poDate.nIdTipDoc` (punctul 4). Verificare: formularul unificat - se deschide pe fiecare din cele ~15 tipuri principale de `poDate.tip` folosite azi in cele doua - `Init`-uri, si arata exact aceleasi campuri vizibile/eliminate ca varianta veche — comparatie - camp-cu-camp fata de tabelul din `docs\S1_inventar_campuri_formular_unificat.md`. -2. **Porteaza cele 29 `do_cauta_*`** (fara ramificare unde sunt identice — `do_cauta_fdoc` — cu - ramificare pe `nIdTipDoc` unde difera). Verificare: fiecare cautare deschide acelasi dialog, scrie - aceleasi proprietati pe `poDate`, muta focusul identic cu azi. -3. **Fuzioneaza `inainte_de_do_termin`**, cu ramificarea documentata la punctul 2. Verificare: aceleasi - mesaje de eroare, in acelasi ordine, pentru aceleasi campuri goale, pe fiecare tip. -4. **Porteaza `do_schimba_tipdoc`** — decide explicit daca ramane doar-factura sau se extinde si pe - aviz (punctul 4, pct. 3). **Aici se rezolva sau nu #16** (punctul 6) — de facut ca parte a acestui - pas, nu separat, pentru ca schimbarea de arhitectura care il rezolva (un singur `Init` persistent) - e chiar cea care se construieste la pasul 1. -5. **Porteaza `but_modifica`/blocarea antetului** (decizia 9, deja proiectata in plan sectiunea I) — - depinde de pasii 1-4 fiind stabili, pentru ca reutilizeaza aceleasi controale. -6. **Integreaza bucla de emitere** (`factureaza`/`factureaza2`) cu noul formular unificat, tratand - explicit esecul Oracle de la cursorul de articole (punctul 6, directia a/b). - -**Criteriul de "gata", rescris verificabil.** Azi planul spune "un document se emite integral din -formularul unificat, pe tip 1, cu acelasi rezultat in `vanzari`/`act`/`rul` ca pe calea veche" — fara -sa spuna cum se compara. Propunere concreta: -- Se emite **acelasi document** (acelasi client, articole, cantitati, preturi) o data pe calea veche - (`frm_date_factura` -> `frm_facturare_articole` -> `frm_alte_date`) si o data pe formularul unificat, - pe date de test identice, in aceeasi zi contabila. -- Se compara, randuri-cu-randuri, rezultatul in `VANZARI` (toate coloanele, nu doar sumele), `ACT`, - `RUL`, `DOCUMENTE`, `JV2007` (nota contabila) — cel mai simplu cu doua interogari identice filtrate - pe cei doi `ID_FACT`/`ID_VANZARE` rezultati, exportate si diff-uite text-cu-text (unealta: acelasi - export SQL folosit deja pentru `PACK_FACTURARE` in `docs\ff_*.sql`, adaptat pe `SELECT * FROM VANZARI - WHERE ID_FACT=...`). -- Diferentele **asteptate** (marcate ca OK, nu ca esec): `ID_VANZARE`/`ID_FACT`/timestamp-uri de - creare — orice altceva trebuie sa fie identic. -- Se repeta pentru **cel putin un tip de factura in valuta** (S3 atinge direct campurile de valuta) si - **un tip de aviz** (ramificarea de la punctul 4), nu doar tip 1. - ---- - -## 8. Ce nu se poate testa headless din S3 - -Capcana cunoscuta (`COMUN\docs\depanare_testare_vfp.md`, memorie de proiect): sub harness `-A -T`, -coloanele de grid nu se materializeaza (`ColumnCount=0`, `RecordSource` raman artefacte necitite) — -relevant direct pentru `frm_facturare_articole2` (grid-ul de compunere), dar S3 propriu-zis nu atinge -gridul. - -Specific pentru S3 (antet): -- **Toate cele 29 `do_cauta_*`** deschid un dialog modal de cautare (`caut_ora.vcx`/similare) — - interactiunea reala de selectare dintr-o lista si `Show(1)` modal nu se poate simula headless; se - pot testa doar efectele **dupa** ce `poCauta`/rezultatul e construit manual (tiparul deja folosit in - `test_pret_cu_tva_dialog.prg`/`test_adauga_linie_valuta.prg` mentionat in - handoff intermediar (sters), punctul 7) — apel direct al metodei cu un obiect - simulat, fara `Show()`. -- **`Init`-ul complet** (redimensionare, `RemoveObject` in cascada, repozitionare containere) e - verificabil pe proprietati (`.Visible`, `.Height`, existenta obiectului dupa `Type(...)`) fara UI - vizibil, pentru ca `Createobject` ruleaza `Init` fara `Show()` — tiparul e deja validat in codebase. -- **Focusul si secventa reala de `LostFocus`** (exact ce a produs bug #16) **nu se poate reproduce - headless** — necesita fie `vfp_ui_harness.ps1` cu UI vizibil si input simulat pe masina, fie testare - manuala de Marius; simularea unui `SetFocus()`/`LostFocus()` apelat direct din cod ocoleste tocmai - secventa evenimentelor native care a cauzat bugul. -- **Eroarea Oracle -20005 si redeschiderea `vizualizeaza_curs`** — reproductibila headless doar daca - se poate forta controlat lipsa unui curs pentru o data de test (manipulare de date, nu de UI) — nu - s-a verificat aici daca exista deja o retetare de date de test pentru asta. - ---- - -## Ce nu s-a putut stabili si de ce - -- **Corpul complet al `frm_date_aviz.Init`** (`:7354-7600`, 247 linii) — citit doar partial in sesiuni - anterioare (pana la `~7533`, conform `inventar_controale_formulare.md:166-167`); nu re-citit integral - aici din motive de buget de context. Structura generala (Do Case pe tip, RemoveObject, focus final) - e foarte probabil simetrica cu `frm_date_factura.Init`, dar nu verificata linie-cu-linie. -- **`do_cauta_venit`** (`frm_date_factura`, `:9393-9394`, o singura linie) — nu s-a citit continutul; - posibil alias/stub mort, de verificat la implementare. -- **Riscul semnalat la punctul 5** (`frm_alte_date.Init:3159`, `dezaloca_numar` pe eroare la - interogarea combo-ului de casa) — **doar semnalat, neurmarit**: nu s-a verificat daca apelantul care - cheama `frm_alte_date` are aceeasi bucla "retry silentios" ca `factureaza`, sau daca eroarea aici - chiar opreste fluxul curat (`Return` explicit la `:3161`, spre deosebire de bug #16 unde nu exista - `Return` echivalent). Merita o cercetare separata, de marimea celei de la punctul 6, inainte de - implementare. -- **`Clb_serie_act1.TEXT_SIMPLU1.LostFocus`** din `frm_facturare_articole2` (`:19263-19270`) — nu - citit; nu s-a verificat daca reproduce corect `genereazanumar` sau e alt cod orfan ca cel de la - punctul 3. -- **Ramificarea exacta linie-cu-linie pentru restul celor 27 de perechi `do_cauta_*`** (dincolo de - `do_cauta_client` si `do_cauta_fdoc`, comparate direct aici) — s-au comparat doar lungimile (indiciu - de diferenta), nu continutul; de citit la implementare, nu presupus din lungime. -- **`frm_modifica_factura`** — clasa reala traieste in `ofacturare_comun.vc2` (perimetrul #6, doar - citire permisa), dar `vfp_symbols.ps1` a indexat-o din `.pre_s4butoane.bak.vc2` (linii duplicate, - nesigure) — nu s-au recitit liniile reale, pentru ca `frm_modifica_factura` **nu intra in portarea - S3** (e inlocuita de `but_modifica`, deja proiectat separat in plan, sectiunea I). - -## Bug-uri semnalate, nereparate - -1. **Cod orfan in `frm_facturare_articole2`**: `clb_fdoc.cboFdoc.Valid` (`ofacturare.vc2:19256`) cheama - `thisform.do_schimba_tipdoc()`, metoda **inexistenta** pe aceasta clasa — ar arunca eroare VFP daca - userul ar interactiona cu acest combo pe formularul mort. Fara consecinte azi (formularul nu e - accesibil in productie, cf. plan sectiunea A), dar de curatat sau completat cand prototipul devine - baza formularului unificat. -2. **Bug #16, confirmat si localizat complet** (punctul 6) — in `ofacturare.prg:555-564` (si simetric - in `factureaza2`, liniile ~1061 dupa numerotarea echivalenta, nu verificate linie-cu-linie aici): - `lnRaspuns` nu se reseteaza pe ramura de eroare Oracle a buclei de emitere, cauzand recrearea - completa si nesolicitata a formularului de antet. In afara perimetrului `ofacturare_comun.vc2`/ - `ofacturare_editare.prg` (deci nu ciocneste cu #6), dar in `ofacturare.vc2`/`ofacturare.prg`, cod - comun suitei (afecteaza si ROACONT/ROAGEST/etc. daca folosesc acelasi `factureaza`/`factureaza2` - din `COMUN` — neverificat aici daca alte produse il apeleaza, dar fisierul e in `COMUN\programe`, - deci probabil da). -3. **Risc structural similar, neconfirmat**: `frm_alte_date.Init:3159` — acelasi tipar - "`dezaloca_numar` pe eroare SQL neasteptata", posibil fara aceeasi consecinta de reincercare - silentioasa, dar nu verificat (vezi "Ce nu s-a putut stabili"). diff --git a/docs/cercetare/s3b_alte_date_analitice.md b/docs/cercetare/s3b_alte_date_analitice.md deleted file mode 100644 index 5666d10..0000000 --- a/docs/cercetare/s3b_alte_date_analitice.md +++ /dev/null @@ -1,602 +0,0 @@ -# S3b — Proiectare: sectiunea pliata "alte date + analitice" - -Livrabilul povestii **S3b** din `docs\plan_13_unificare_formular_facturare.md:1838-1854` (deciziile 7, -8, 25, 26). Proiectare pe cod, fara nicio modificare — zero editari, zero -`git_sync.ps1`/`txt2vcx.ps1`, zero commit. `COMUN\clase\ofacturare_comun.vc2` si -`COMUN\programe\ofacturare_editare.prg` (perimetrul #6) doar citite, neatinse. - -## Verdict (rezumat) - -S3b nu e o simpla "mutare de controale" cum sugereaza textul din plan. **Descoperirea centrala, -negasita in materialele existente**: mecanismul de alocare a numarului de chitanta nu porneste doar -din clicul utilizatorului pe `opt_incasat` — porneste din **orice** atribuire programatica a -proprietatii `.Value`, pentru ca `opt_incasat.ProgrammaticChange` (`ferestre_cere_date.vc2:3351-3352`) -cheama exact acelasi `actualizeaza_tipincasare()` ca si `.Click` (`:3250-3251`). `Init`-ul de azi -(`:3150`) seteaza chiar el `Thisform.opt_incasat.Value = 2` cand documentul soseste cu -`poDate.incasat<>0` (caz real: copiere, confirmat la `ofacturare_stoc.prg:535`, -`oproceduri_facturare.prg:1186`, `ofacturare_comun.vc2:4066,4260`) — deci **azi, deschiderea -dialogului aloca deja un numar de chitanta fara ca userul sa atinga nimic**, de fiecare data cand -documentul soseste cu o incasare presetata. In formularul unificat, unde zona nu se mai deschide o -singura data modal ci se pliaza/depliaza repetat pe acelasi obiect `poDate` persistent, acelasi cod -rulat la fiecare depliere ar realoca/dealoca la fiecare toggle — exact opusul criteriului cerut de -decizia 25. Sectiunea 4 trateaza asta ca risc central, cu recomandare concreta. - -A doua descoperire: **niciun mecanism de pliere reutilizabil nu exista** in prototipul -`frm_facturare_articole2` (cautare completa, zero potriviri pe "plia/colaps/accordion/expand" in -`ofacturare.vc2`) — cel mai apropiat tipar din suita e `frm_modific2024.afiseaza_rulaje` -(`omodificari.vc2:13169-13199`, perimetrul #6, citit dar neatins), care refoloseste **acelasi idiom -sus/jos** deja validat pentru `but_modifica`/`but_salveaza` (decizia 9), dar aplicat unui show/hide, -nu unei blocari. Sectiunea 6 detaliaza. - -A treia: pentru analiticele read-only (decizia 26), mecanismul existent `ct_clb_cautare.do_dezactiveaza()` -(`caut_ora.vc2:800-806`) **ascunde doar lupa de cautare**, nu blocheaza si textbox-ul de afisare — -cineva tot poate scrie liber in camp. Sectiunea 2 detaliaza gap-ul si completarea minima necesara. - ---- - -## 1. Inventarul exact al celor patru grupuri din `frm_alte_date` - -Sursa primara a controalelor: `docs\cercetare\inventar_controale_formulare.md` §4 si -`docs\S1_inventar_campuri_formular_unificat.md` (randurile "Pliat — alte date"), reconfirmate aici -direct pe `COMUN\clase\ferestre_cere_date.vc2` (clasa `frm_alte_date`, `2219-3353`; garda de validare -`inainte_de_do_termin` la `3044-3103`, `Init` la `3105-3208`). - -### 1.1 Delegat / transport - -| Control | Caption real | `fisier:linie` (ADD OBJECT) | Vizibilitate | Ruta de scriere (din rute_scriere_antet.md / S1) | -|---|---|---|---|---| -| `Ct_clb_delegat` | "Delegat" | `ferestre_cere_date.vc2:2553` (container `ct_clb_cautare`-like, `caution=do_cauta_delegat`) | pliat, mereu editabil pe document nou; **eliminat cu totul** cand `poDate.eProforma=1` (`:3192`) | pe loc, `modifica_date_factura` (`V_ID_DELEGAT`), neconditionat — `modifica_date_factura_parametri.md:17` | -| `Ct_clb_masina` | "Masina" | `:2569` | idem, eliminat pe proforma (`:3193`) | `V_ID_MASINA`, neconditionat — `:18` | -| `Ct_clb_agent` | "Agent" | `:2537` | idem, **nu e eliminat pe proforma** (nu apare in cascada `RemoveObject` `:3192-3201`) | `V_ID_AGENT`, neconditionat — `:19` | -| `Clb_dataora_exp` | "Data si ora expedierii" | `:2443` | idem, nu eliminat pe proforma; **validat obligatoriu** in `inainte_de_do_termin` (`:3062-3067`, `Case Empty(Nvl(poDate.dataora_exp,{})) And poDate.eProforma = 0`) | `V_DATAORA_EXP`, neconditionat — `:20` | -| `But_modifica1` | (icon, `caction` implicit -> `do_modifica`) | `:2343` | actiune, nu camp — deschide `nom_parteneri_modifica` pe delegatul curent (`ferestre_cere_date.vc2:2925-2935`) | — | - -**Validarea de grup** (`inainte_de_do_termin`, `:3055-3060`): delegatul e obligatoriu doar cand -`gcNumeProgram = [ROAFACTURARE]` **si** documentul nu e proforma sau bon fiscal -(`!(poDate.eProforma = 1 OR poDate.eBonFiscal = 1)`) — nuanta care trebuie pastrata identic in -formularul unificat, nu doar copiata "delegat obligatoriu". - -### 1.2 Incasare (grupul cel mai mare, vezi si sectiunile 3-4) - -| Control | Caption real / eticheta dinamica | `fisier:linie` | Vizibil cand | -|---|---|---|---| -| `opt_incasat` (radiogrup 4 optiuni) | "Fara incasare" / "Chitanta" / "Bon fiscal" / "POS Card" | `:2638-2685` (default `Value=1`) | mereu, dar **intreg containerul incasare** dispare pe proforma (`:3140-3143`, `Thisform.opt_incasat.Visible = .F.`, doar pe ramura non-proforma exista oricum) si pe `gcNumeProgram=[ROAGEST]` sau `!Between(poDate.tip,1,4)` | -| `Cb_casa` | "Casa" -> "Banca POS" pe POS (`:2967` `_lbbase1.Caption='Banca POS'`) | `:2362` | ascuns doar la `opt_incasat=1` | -| `Clb_serie_chit` | "Serie chitanta" | `:2503` | vizibil **doar** la Chitanta (`Value=2`) | -| `Clb_nrchit` | "Nr. chitanta" -> "Nr. bon" pe Bon fiscal/POS | `:2480` | ascuns doar la `Value=1` | -| `Clb_incasat` | "Incasat" | `:2461` | ascuns doar la `Value=1` | -| `cmdModificaBon` | icon, `caction=do_modifica_bon` | `:2526` | vizibil **doar** la Bon fiscal (`Value=3`) | -| `chkPOS` | "POS" | `:2409` | vizibil la Bon fiscal (`Value=3`, bifabil) **si** la POS (`Value=4`, fortat `.T.` si ascuns — `:2845` `thisform.chkPOS.Value=1` apoi `.Visible=.F.`) | -| `chkDetaliat` | "Detaliat" | `:2396` | vizibil doar la Bon fiscal | -| `cboTipFactura` | combo, langa "Tip factura" | `:2378` | mereu (langa incasare), scrie `poDate.tip_saft` | - -Masina de stari completa e la sectiunea 3-4. **Eticheta "Casa"/"Banca POS" si vizibilitatea lui -`chkPOS`/`chkDetaliat` fac parte din aceeasi metoda unica** (`actualizeaza_tipincasare`) — nu sunt -conditii separate de refacut, sunt randuri din acelasi `Do Case`. - -### 1.3 Adresa de facturare - -| Control | Caption | `fisier:linie` | Vizibilitate | -|---|---|---|---| -| `clb_adresa_facturare` | "Adresa facturare" | `:2422` | pliat, mereu editabil; **nu** eliminat pe proforma (absent din cascada `:3192-3201`) | -| `do_cauta_adresa` (metoda, nu control separat) | — | `:2911-2926` | cauta pe `poDate.id_client`, seteaza `poDate.adresa_facturare`/`poDate.id_facturare` | - -Ruta de scriere: `V_ID_FACTURARE`, neconditionat — `modifica_date_factura_parametri.md:21`. - -### 1.4 Text aditional - -| Control | Caption | `fisier:linie` | Vizibilitate | -|---|---|---|---| -| `Ed_tx_simplu1` | "Text aditional (max. 1000 caractere)" | `:2585` | pliat, mereu editabil; nu eliminat pe proforma — dimpotriva, **pe proforma ramane singurul grup activ**, formularul se redimensioneaza in jurul lui (`:3184-3207`) | -| `_checkbox1` ("Listare detaliata") | "Listare detaliata" | `:3002-3011` | ADD OBJECT separat, `ControlSource=poDate.nListareDetaliata` — **nu e in cascada celor patru grupuri din B**, e propriul lui control, langa text aditional in layout, dar conceptual apartine grupului de raportare (I-bis) alaturi de `tip_saft`/`efactura`, nu grupului "text aditional" | - -Ruta: `V_TEXT_ADITIONAL`, neconditionat, **fara** `NVL` — `modifica_date_factura_parametri.md:23`. - -**Observatie structurala pentru mockup**: `_checkbox1` (listare detaliata) e cablat separat de -`chkDetaliat` din grupul incasare (control diferit, aceeasi semantica probabila — ambiguitate deja -semnalata in S1, randul 57, neinchisa aici). Formularul unificat trebuie sa aleaga **unul singur** -dintre cele doua controale existente, nu sa le porteze pe amandoua. - ---- - -## 2. Analiticele coborate din antet (decizia 8, nuantata de decizia 26) - -Controale: `Ct_clb_venchelt` ("Venit / cheltuiala"), `Ct_clb_sectie` ("Sectie"), `Ct_clb_responsabil` -("Responsabil"), `Ct_clb_lucrare` ("Lucrare") — toate patru container `ct_clb_cautare` (subclasa -`caut_ora.vcx`), azi pe `frm_date_factura`/`frm_date_aviz`, **nu** pe `frm_alte_date` -(`inventar_controale_formulare.md` §3; S1 randurile 39-42). Live in sectiunea I a planului ca grup -"Pliat — analitice", distinct de cele patru grupuri ale lui `frm_alte_date` — dar aceeasi sectiune -pliata a formularului unificat, conform textului S3b ("peste ele coboara analiticele din antet"). - -### 2.1 Ce inseamna "afisare read-only" mecanic - -Sursa: `docs\cercetare\rute_scriere_antet.md` §1 — verdict deja stabilit, **DA** pentru -`ID_SECTIE`/`ID_RESPONSABIL`/`ID_LUCRARE` printr-o cale noua (`frm_facturi.do_editare_factura` -> -`frm_modific2024`, perimetrul #6), **neconfirmat** pentru `ID_VENCHELT`. Pentru #13, indiferent de -raspunsul acelei rezerve, decizia 26 e explicita: **zero editare din formularul unificat**, oricare ar -fi ruta reala din Oracle. - -**Mecanismul de blocare vizuala, verificat pe cod, cu un gap real**: -`ct_clb_cautare.do_dezactiveaza()` (`caut_ora.vc2:800-806`): -``` -PROCEDURE do_dezactiveaza - This.lactiv = .F. - This.img_cautare.Visible = .F. - This.ctooltip = This.clb_tx_cautare.text_simplu1.ToolTipText - This.clb_tx_cautare.text_simplu1.ToolTipText = [] - This.clb_tx_cautare.text_simplu1.Refresh() -ENDPROC -``` -`This.lactiv` gateaza **doar** deschiderea dialogului de cautare — `DblClick` (`:822-826`), -`KeyPress` pe Enter/Tab/F7 (`:829-836`), `img_cautare.Click` (`:840-843`) — toate testeaza -`This.Parent(.Parent).lactiv` inainte sa cheme `do_apeleaza()`. **Nu seteaza `ReadOnly` pe -`clb_tx_cautare.text_simplu1`** — textbox-ul bindat (`ControlSource=This.cvar_afisata`, setat in -`Init`, `caut_ora.vc2:814-817`) ramane direct tastabil. Pentru un camp cu adevarat read-only (decizia -26 cere explicit "nu se editeaza"), `do_dezactiveaza()` trebuie **completat**, nu doar apelat: se -adauga `This.clb_tx_cautare.text_simplu1.ReadOnly = .T.` (si eventual `.TabStop = .F.`, ca sa nu -primeasca focus deloc) langa apelul existent — o linie noua, in tiparul deja recomandat de plan -(sectiunea I: "singura reteta completa e `clb_tx_data.dezactiveaza()`... se generalizeaza de la ea"). - -**Consecinta pentru "un singur buton deschide tot" (decizia 9)**: `do_dezactiveaza()` se apeleaza **o -singura data**, la constructia sectiunii, **si nu se mai cheama niciodata `do_activeaza()`** pentru -aceste patru controale — nici cand `but_modifica` deblocheaza restul antetului. E o exceptie -deliberata de la "un singur control comanda toata protectia antetului" (plan I:843-853), simetrica cu -exceptia deja acceptata pentru grupurile B/C la decizia 25 (sectiunea 5 de mai jos). - -### 2.2 Eticheta / tooltip - -Nicio propunere de text nu exista inca in materialele citite — de scris la implementare. Recomandare -minima, in stilul deja folosit in codebase (ex. `Ct_clb_gestiune_init.ToolTipText`, un citat complet -la `inventar_controale_formulare.md:93-95`, deci precedent de lungime/ton acceptat): eticheta ramane -neschimbata ("Venit / cheltuiala"/"Sectie"/"Responsabil"/"Lucrare"), `ToolTipText` nou de tipul -*"Se editeaza din Editare factura (articole, cantitati, preturi) — buton disponibil pe fiecare linie -a notei contabile"*, ca sa nu para camp stricat. Text exact — **de decis de Marius**, vezi sectiunea 9. - -### 2.3 Nivel antet vs. nivel linie — de retinut la afisare - -`rute_scriere_antet.md` §1.4: editarea reala prin #6 e **pe linie de nota**, nu pe antet — un -document poate ajunge cu sectii diferite pe linii diferite dupa editare (imposibil la emitere). Cand -formularul unificat afiseaza analiticele "cu valoarea de antet" (cum spune decizia 26), afiseaza de -fapt **o singura valoare reprezentativa** (probabil prima linie sau variabila de sesiune retinuta la -emitere), nu o agregare a liniilor — daca liniile diverg dupa o editare #6, afisarea din #13 devine -"aproximativa, nu autoritara". Nu e o contradictie de rezolvat aici (decizia 26 exista tocmai ca sa -evite ca #13 sa scrie peste diferentierea de linie), dar merita un rand explicit in criteriul de -"gata" (sectiunea 10): afisarea trebuie sa spuna clar ce valoare arata, nu sa pretinda ca e valoarea -unica a documentului daca liniile difera. - ---- - -## 3. `actualizeaza_tipincasare` - -Definita la `COMUN\clase\ferestre_cere_date.vc2:2698-2856`, metoda proprie a clasei `frm_alte_date`. -Comuta vizibilitatea intregului subgrup de incasare pe `This.opt_incasat.Value` (patru ramuri, tabel -complet la sectiunea 1.2 si in `inventar_controale_formulare.md:130-135`) **si**, in aceeasi metoda, -aloca/dezaloca numerele — cele doua responsabilitati (UI si alocare) sunt **impletite in acelasi -`Do Case`**, nu separate. Fiecare ramura incepe prin a dezaloca defensiv celelalte trei tipuri de -numar, apoi aloca pe cel curent daca e cazul: - -| `opt_incasat.Value` | Dezaloca la intrare | Aloca | Alte efecte | -|---|---|---|---| -| 1 (Fara incasare) | 16, 3, 26 | — | `poDate.incasat=0`, `poDate.nr_incasare=0` | -| 2 (Chitanta) | 3, 16, 26 | `creeaza_cursor_serii(16)` + `clb_serie_chit.genereazaNumar()` (`:2783`) — doar daca `nRezultatSerii=0` (nu realoca daca seria era deja creata) | `poDate.incasat=poDate.totalctva` | -| 3 (Bon fiscal) | 3, 16, 26 | `do_aloca_nr_bon([CLICK])` (`:2807`) -> cod 3 | idem, plus `tip_saft` poate deveni 751 daca `gnEFactura_tip_saft_bf` e setat | -| 4 (POS/Card) | 3, 16, 26 | `do_aloca_nr_pos([CLICK])` (`:2844`) -> cod 26 | idem, `chkPOS.Value` fortat 1 si ascuns | - -**Ce depinde de ea**: `opt_incasat.Click` (`:3250-3251`) **si** `opt_incasat.ProgrammaticChange` -(`:3351-3352`) — vezi sectiunea 4, e disjunctia care conteaza. Nu are alti apelanti directi in -`ferestre_cere_date.vc2` (cautare `vfp_symbols.ps1 -Grep actualizeaza_tipincasare -CodeOnly` -recomandata la implementare pentru confirmare exhaustiva pe tot proiectul — nu rulata aici din motive -de buget, dar apelantul extern deja cunoscut e citat mai jos, la 3.1). - -### 3.1 Apel extern, cu context important - -`COMUN\clase\ofacturare.vc2:14919-14926` (`frm_facturare_articole(2).inainte_de_do_termin`, inainte -de `ofrmdatesupl.Show(1)`): -``` -ofrmdatesupl = Createobject("frm_alte_date") -ofrmdatesupl.nnrbon = poDate.nract -If (poDate.eBonFiscal = 1) - With ofrmdatesupl - .opt_incasat.Value = 3 && INCASARE CU BON FISCAL - .actualizeaza_tipincasare() && apel EXPLICIT, dupa .Value= - .opt_incasat.option1.TabStop = .F. - ... -``` -Apelantul seteaza `.Value=3` **si** cheama explicit `actualizeaza_tipincasare()` imediat dupa — ceea -ce, coroborat cu descoperirea de la sectiunea 4 (`.Value=` deja declanseaza `ProgrammaticChange` -> -aceeasi metoda), inseamna ca metoda ruleaza de doua ori la acest apel. Nu produce o eroare vizibila -(fiecare ramura e idempotenta: dezaloca ce era alocat, realoca acelasi tip), dar confirma ca autorul -codului nu s-a bazat exclusiv pe evenimentul `ProgrammaticChange` — semn ca mecanismul lui exact -(cand se declanseaza, cand nu) n-a fost niciodata documentat explicit, doar "acoperit din ambele -parti". **Portarea "ca atare" trebuie sa pastreze acest apel dublu identic**, nu sa-l "curete" ca -redundant — eliminarea unuia dintre cele doua declansatoare ar putea rupe o presupunere neverificata -in alta parte a codului. - -### 3.2 "Se muta ca atare" — ce inseamna concret - -Corpul metodei (`:2698-2856`) nu are nicio dependenta de fereastra-container (`Thisform.*` peste -tot, nu `This.Parent.*`) — se poate muta **byte-cu-byte** pe formularul unificat, cu conditia ca -toate cele noua controale referite (`opt_incasat`, `cb_casa`, `clb_serie_chit`, `clb_nrchit`, -`clb_incasat`, `_shape3`, `lb_simplu1`, `cmdModificaBon`, `chkPOS`, `chkDetaliat`, `cboTipFactura`) -sa existe cu **exact aceleasi nume** pe noul formular, in acelasi container logic (`Thisform`, nu un -subpanou cu alt scope) — altfel referintele nekvalificate `Thisform.xxx` pica silentios pe obiect -inexistent. Aceasta e o constrangere de implementare directa: **sectiunea pliata trebuie sa fie -container-transparenta** pentru aceasta metoda (fie pusa direct pe `Thisform`, fie metoda insasi -adaptata sa foloseasca `This.Parent`-ul corect) — nu poate fi mutata intr-un obiect-container separat -fara o trecere explicita de `Thisform.xxx -> This.Parent.xxx` pe toate liniile. - ---- - -## 4. Alocarea si dezalocarea numerelor — evenimentele exacte - -### 4.1 Evenimente de alocare (azi) - -1. **`opt_incasat.Click`** (`:3250-3251`) — interactiune reala a utilizatorului pe radiogrup. -2. **`opt_incasat.ProgrammaticChange`** (`:3351-3352`) — **descoperire centrala a acestui raport**, - negasita in materialul de plan sau in cele patru rapoarte de referinta: in VFP, orice atribuire - `.Value = n` facuta din cod (nu de utilizator) declanseaza `ProgrammaticChange`, nu `Click`. Clasa - are ambele metode cablate pe **acelasi** apel (`actualizeaza_tipincasare()`), deci **orice loc din - cod care scrie `opt_incasat.Value = ...` aloca/dealoca numere**, indiferent de intentie. -3. **Chiar `Init`-ul clasei** foloseste acest canal: `:3150`, `Thisform.opt_incasat.Value = 2` — ruleaza - **doar daca** `poDate.incasat <> 0` la momentul deschiderii. Verificat pe cod (nu presupus) ca - `poDate.incasat` poate fi nenul **inainte** ca `frm_alte_date` sa se deschida, prin cel putin patru - cai reale: `ofacturare_stoc.prg:535`, `oproceduri_facturare.prg:1186`, - `ofacturare_comun.vc2:4066`, `ofacturare_comun.vc2:4260` (toate patru scriu `poDate.incasat = - incasat`, parametru primit de la apelant — flux de copiere/reluare a unui document cu incasare deja - stabilita). **Concluzie verificata**: azi, deschiderea dialogului `frm_alte_date` pe un astfel de - document **aloca deja un numar de chitanta la Init, inainte ca userul sa apese orice**. Nu e o - ipoteza — e mecanismul descris la sectiunea 3, declansat de linia 3150. -4. `do_modifica_bon`/`do_modifica_pos` (`:3001-3022`) — dezaloca explicit + realoca, la apasarea - butonului "Modifica bon"/echivalent POS. -5. `do_aloca_nr_bon`/`do_aloca_nr_pos` (`:2864-2907`) — chemate din interiorul lui - `actualizeaza_tipincasare` (ramurile 3 si 4), nu independent. - -### 4.2 Evenimente de dezalocare (azi) - -1. **In interiorul `actualizeaza_tipincasare`** — fiecare ramura dezaloca defensiv celelalte trei - tipuri **inainte** de a (re)aloca pe cel ales (tabelul de la sectiunea 3). Efect: comutarea intre - optiuni, in cadrul aceleiasi sesiuni de dialog, e curata — nu ramane niciun numar orfan din - optiunea anterioara. -2. **`inainte_de_do_renunt`** (`:3023-3043`) — la anulare (buton Renunt / ESC pe dialogul modal): - ``` - inainte_de_do_renunt (ferestre_cere_date.vc2:3025-3030) - If Type('poGeneratorNumere') = 'O' - poGeneratorNumere.dezaloca_numar(16) && chitanta - poGeneratorNumere.dezaloca_numar(3) && bon fiscal - ... - ``` - **Gap real, preexistent, verificat pe cod**: acest bloc dezaloca 16 (chitanta) si 3 (bon fiscal), - dar **nu 26 (POS)** — nici direct, nici in blocul urmator (`:3033-3035`, care dezaloca doar 16 din - nou, conditionat). Daca utilizatorul alege POS/Card si apoi anuleaza dialogul, numarul POS alocat - **ramane alocat**, nedezalocat. Nu e introdus de S3b — exista deja in codul de azi — dar merita - semnalat explicit ca risc mostenit (sectiunea 9), pentru ca S3b il "muta ca atare" (plan, textul - povestii), deci il si multiplica pe orice cale noua prin care sectiunea pliata s-ar putea - "renunta" fara sa fie un dialog modal cu propriul buton Renunt. -3. **`Init` pe eroare Oracle** (`:3159`) — `poGeneratorNumere.dezaloca_numar(5)` (factura) daca - interogarea `v_nom_casa` esueaza, cu `Return` imediat dupa (linie explicita, spre deosebire de - bug-ul #16 din `s3_portare_antet.md` §6, unde nu exista `Return` echivalent). Semnalat, neurmarit - mai departe aici — acelasi risc de arhitectura ca #16, dar pe alt fisier. - -### 4.3 Riscul central pentru formularul unificat, si recomandarea - -**Problema mecanica exacta**: in fluxul de azi, `frm_alte_date` e un dialog modal, instantiat **o -singura data** per emitere (`ofacturare.vc2:14921`), asa ca `Init`-ul lui (si declansarea eventuala de -la linia 3150) ruleaza **o singura data**. In formularul unificat, grupul de incasare devine o -sub-sectiune a unui panou pliabil, pe **acelasi obiect de formular persistent** — daca implementarea -naiva re-populeaza `opt_incasat.Value` din `poDate` de fiecare data cand sectiunea se depliaza (ex. -"la fiecare `Show`/`Visible=.T.` al panoului, resincronizeaza controalele cu starea curenta a lui -`poDate`"), **fiecare depliere ar re-declansa `ProgrammaticChange` -> `actualizeaza_tipincasare()` -> -dezaloca tot + realoca pe optiunea curenta** — incalcand direct criteriul din S3b ("deschiderea si -inchiderea formularului fara a atinge zona de incasare nu aloca si nu dezaloca niciun numar"). - -**Recomandare concreta**: populeaza `opt_incasat.Value` din `poDate` **o singura data**, la -constructia sectiunii de antet a documentului (echivalentul lui `Init` de azi, care ruleaza o data per -sesiune de emitere/editare) — nu la fiecare toggle de pliere/depliere. Plierea/deplierea trebuie sa -fie **strict** un toggle de `Visible`/`Height` pe containerul vizual, fara nicio atingere a -proprietatii `.Value` a lui `opt_incasat` sau a oricarui alt control cu `ProgrammaticChange` cablat pe -alocare. Aceasta e exact genul de regula care trebuie verificata explicit la implementare (criteriu de -"gata" concret la sectiunea 10), nu presupusa. - ---- - -## 5. Grupul de incasare pe document deja emis (decizia 25) - -**Ce spune decizia**: orice camp schimbat din grupul C (incasare) pe un document deja emis -"marcheaza documentul pentru regenerare" — dar regenerarea nu exista in etapa I, deci **planul tine -campurile blocate**, cu explicatie la hover, pana la etapa II (plan `:192-201`, `:1847-1848`). -`rute_scriere_antet.md` §3 confirma independent ca nu exista nicio cale directa de scriere pentru -grupul C dupa emitere (verdict "NU" pentru toate controalele, "PARTIAL neconfirmat" doar pentru o -cale indirecta prin editarea notei — vezi §3.2 acolo, ramane deschisa). - -### 5.1 Mecanic: pe ce proprietate, pe ce conditie - -Decizia 9 stabileste ca `but_modifica` deblocheaza **tot** antetul, fara exceptie de camp — dar -decizia 25 taie o exceptie explicita peste asta pentru grupurile B si C. Nu exista inca (verificat, -`rute_scriere_antet.md` §2-3) un mecanism de blocare per-grup separat de `but_modifica`; trebuie -construit nou, dar dupa un tipar deja folosit in codebase pentru exact intrebarea "acest document are -deja un numar, sau e nou": `chkSerieAct.Enabled = !EMPTY(NVL(poRec.numar_act,0))` -(`ofacturare_comun.vc2:5736-5750`, citat deja in plan I:839 si S1 randul 60). Decizia 9 **abandoneaza** -acest test ca poarta pe cele patru bife de identitate (serie/numar/data/scadenta) — dar testul insusi -ramane exact instrumentul potrivit pentru o alta intrebare: "documentul e nou sau deja emis?", care e -tocmai conditia ceruta de decizia 25 pentru grupurile B/C. - -**Recomandare mecanica**: la construirea antetului, se calculeaza o singura data un flag (ex. -`lDocumentExistent = !EMPTY(NVL(poRec.numar_act,0))` sau echivalentul lui pe obiectul `poDate`/`poRec` -folosit de formularul unificat — de confirmat la implementare care obiect poarta `numar_act` in noua -arhitectura, pentru ca `poRec` era specific lui `frm_modifica_factura`, perimetrul #6). Controalele -grupului C (`opt_incasat` si tot ce atarna de el din tabelul sectiunii 1.2) primesc `Enabled = .F.` -**neconditionat de starea lui `but_modifica`** cand `lDocumentExistent = .T.`, in etapa I. Cand -`but_modifica` deblocheaza restul antetului, acest grup **ramane** dezactivat — e o exceptie fixa, nu -una care se recalculeaza la fiecare click pe `but_modifica`. - -**Grupul B** (tip document, valuta, zi curs, client, sursa, gestiune sursa, politica de preturi) are -aceeasi tratare — dar acele controale traiesc azi in `frm_date_factura`/`frm_date_aviz` -(sectiunea I a planului, nu sectiunea B de aici), deci blocarea lor nu e in perimetrul S3b propriu-zis; -se semnaleaza aici doar pentru consecventa mecanismului (acelasi flag, acelasi tipar de `Enabled=.F.` -peste orice stare a lui `but_modifica`). - -### 5.2 La emitere, se comporta ca azi - -Pentru un document **nou** (nu inca emis), `lDocumentExistent = .F.`, grupul C e activ normal — se -comporta identic cu `frm_alte_date` de azi, cu masina de stari intreaga din sectiunile 3-4. Nimic nu -se schimba pe calea de emitere initiala; blocarea se aplica **doar** cand formularul unificat deschide -un document deja scris in `VANZARI` (calea de editare, nu de creare). - -### 5.3 Tooltip / explicatie la hover - -Ca la sectiunea 2.2, textul exact nu exista inca — de propus la implementare, in tonul deja folosit in -codebase. Continut minim necesar: sa spuna ca modificarea incasarii pe un document emis nu e inca -posibila din acest formular ("disponibil dupa ce regenerarea documentului e implementata" sau -echivalent), nu doar "camp dezactivat" fara explicatie — plan `:198-200` cere explicit "cu explicatie -la hover". - ---- - -## 6. Mecanismul de pliere — nu exista, se construieste nou dupa un tipar existent - -**Cautare directa in prototip**: `vfp_symbols.ps1 -Grep` (si grep text simplu) pe "plia|colaps| -collapse|expand|acordeon|accordion" in `COMUN\clase\ofacturare.vc2` (unde traieste -`frm_facturare_articole2`, `:15741-19355`) — **zero potriviri**. Prototipul are controalele de antet -montate direct pe formular (confirmat deja de `s3_portare_antet.md` §3 si de -`inventar_controale_formulare.md` §2), dar **niciun mecanism de show/hide de sectiune** — nu exista -nici macar un buton candidat. - -**Cel mai apropiat tipar real din suita**: `frm_modific2024.afiseaza_rulaje` -(`COMUN\clase\omodificari.vc2:13169-13199`, perimetrul #6, doar citit aici). Cod complet: -``` -PROCEDURE afiseaza_rulaje - * COLAPSEAZA SAU MARESTE PAGEFRAME-UL RULAJE - lcPicture = this.but_afiseaza_rulaje.Picture - ... - llAfiseazaRulaje = 'sus'$m.lcPicture - IF m.llAfiseazaRulaje - lcPicture2 = STRTRAN(m.lcPicture,'sus','jos') ... - ELSE - lcPicture2 = STRTRAN(m.lcPicture,'jos','sus') ... - ENDIF - this.but_afiseaza_rulaje.Picture = m.lcPicture2 - ... - this.pgfArticole.Visible = m.llAfiseazaRulaje - this.but_copiazaR.Visible = m.llAfiseazaRulaje - this.but_modificaR.Visible = m.llAfiseazaRulaje - this.but_stergeR.Visible = m.llAfiseazaRulaje - thisform.resize_grid1() -ENDPROC -``` -Trei observatii directe: -1. **Foloseste exact acelasi idiom sus/jos** deja validat si documentat pentru `but_modifica`/ - `but_salveaza` (decizia 9, `docs\cercetare\buton_comutator_picture.md`, citat in plan `:63-75`): - citeste starea curenta din numele fisierului `Picture` (contine "sus" sau "jos"), calculeaza - perechea opusa prin `STRTRAN`, rescrie **`Picture` + `cPictureDown` + `cPictureUp` impreuna** — - aceeasi precautie semnalata in plan ("numai `.Picture` nu ajunge: hover-ul standard din - `buton.MouseEnter`/`MouseLeave` rescrie `Picture` din `cPictureUp`/`cPictureDown`"). -2. **Nu e o clasa reutilizabila, e cod ad-hoc per caz** — toggle direct pe `.Visible` al unei liste - fixe de controale, plus un apel manual de reflow (`resize_grid1()`, metoda proprie a formularului, - nu un mecanism generic). Nu exista in nicio biblioteca de clase din `COMUN` (`_baza.vcx`, etc., - cautare separata, zero potriviri pentru o clasa "panou pliabil"/"expander"/"accordion") un - container gata facut de pliere. **S3b trebuie sa scrie cod nou**, dupa acest tipar, nu sa - instantieze o clasa existenta. -3. **E in perimetrul #6, doar citire** — poate fi copiat ca *idee*/tipar la implementare, dar - `omodificari.vc2` insusi nu se atinge cat timp #6 e in lucru (decizia 30). - -### 6.1 Ce trebuie sa scrie S3b, concret - -- **Un buton comutator per sectiune pliata** (sau unul singur pentru toata zona "alte date + - analitice", de decis la implementare — plan I nu specifica granularitatea), cu imaginile - sus/jos deja existente in `COMUN\grafice` (aceleasi perechi folosite pentru `but_afiseaza_rulaje`, - de identificat exact numele fisierelor la implementare — nu verificate aici, doar tiparul de - mecanism). -- **Toggle de `.Visible`/`.Height`** pe grupul de controale al sectiunii (delegat/transport, incasare, - adresa, text aditional, analitice), plus un apel de reflow echivalent lui `resize_grid1()` — pentru - ca restul formularului (butoane, alte sectiuni de mai jos) sa nu ramana cu goluri sau suprapuneri - cand sectiunea se pliaza/depliaza. VFP nu reflow-eaza automat un layout absolut-pozitionat (`Left`/ - `Top` fixe pe fiecare control) — de asta si tiparul din `frm_date_factura.Init` (citat in - `s3_portare_antet.md` §5, "masoara inaltimile containerelor de antet intr-un array `laPozitii`") cat - si `afiseaza_rulaje` fac manual acest calcul; nu exista un layout manager automat de reutilizat. -- **Populare o singura data** a starii initiale (sectiunea 4.3) — separat de toggle-ul de vizibilitate, - ca sa nu retrigger-eze `ProgrammaticChange`. -- **Intrebare deschisa, semnalata deja in `s3_portare_antet.md` §5** (nu redusa aici): daca lookup-urile - Oracle ale lui `frm_alte_date.Init` (delegat/masina ultima factura, cursorul `v_nom_casa`) raman - **eager** (rulate la construirea formularului, ca azi) sau devin **lazy** (amanate pana la prima - depliere a sectiunii, ca sa nu incarce round-trip-uri Oracle inutile cand userul nu ajunge niciodata - acolo). Ambele optiuni sunt compatibile cu criteriul "deschiderea/inchiderea nu aloca numere" **doar - daca** populate keeping in mind sectiunea 4.3 (populare o singura data, indiferent de varianta - aleasa) — **de decis de Marius**, vezi sectiunea 9. - ---- - -## 7. Ordinea de executie - -Pasi care lasa suita functionala dupa fiecare — calea veche (`frm_alte_date` ca dialog separat) **nu -se sterge** in etapa I, cf. textul povestii ("`frm_alte_date` nu se sterge cat timp calea veche mai e -in uz"). - -1. **Construieste sectiunea vizuala pliata** (patru grupuri din sectiunea 1 + analiticele din - sectiunea 2), fara nicio logica de alocare/validare inca — doar controale + toggle de - Visible/Height (sectiunea 6). *Criteriu:* fiecare control existent in `frm_alte_date`/antetul de - analitice apare o data, cu aceeasi eticheta, in sectiunea corecta a formularului unificat — - verificare camp-cu-camp fata de tabelele din sectiunile 1-2 de aici. -2. **Porteaza `actualizeaza_tipincasare` ca atare** (sectiunea 3), cu toate cele noua referinte - `Thisform.xxx` rezolvate corect in noul container (sectiunea 3.2). *Criteriu:* alegerea fiecarei - optiuni din `opt_incasat` produce exact aceleasi `Visible`/etichete pe controalele dependente ca pe - `frm_alte_date` de azi, verificabil headless (sectiunea 8). -3. **Rezolva explicit riscul de la sectiunea 4.3** — populare unica a starii, toggle de pliere fara - atingere de `.Value`. *Criteriu, verificabil headless:* pe un document de test cu `poDate.incasat<>0` - la construirea formularului, se depliaza si se plie - aza sectiunea de trei ori consecutiv fara a atinge - niciun control din grup — `poGeneratorNumere` (cursorul lui de numere alocate) arata **acelasi** - numar de chitanta la finalul celor trei toggle-uri ca la inceput, nu unul nou de fiecare data. -4. **Porteaza delegat/transport si adresa de facturare** (`do_cauta_delegat`/`do_cauta_masina`/ - `do_cauta_agent`/`do_cauta_adresa`/`do_modifica`), cu validarea din `inainte_de_do_termin` - (sectiunea 1.1). *Criteriu:* fiecare cautare deschide acelasi dialog si scrie aceleasi proprietati - pe `poDate` ca azi (comparatie directa, cod identic mutat). -5. **Adauga analiticele read-only** (sectiunea 2), inclusiv completarea `do_dezactiveaza()` cu - `ReadOnly=.T.` explicit. *Criteriu:* tastarea directa in oricare din cele patru campuri nu modifica - valoarea afisata (verificare pe proprietate, headless). -6. **Implementeaza blocarea grupului C pe document deja emis** (decizia 25, sectiunea 5), cu flag-ul - de "document existent" si exceptia peste `but_modifica`. *Criteriu:* pe un document nou, - `but_modifica` (cand exista, dupa S3/decizia 9) nu afecteaza grupul C (mereu activ); pe un document - deja emis, grupul C ramane `Enabled=.F.` inainte **si** dupa apasarea lui `but_modifica`. -7. **Verificare de regresie end-to-end**: emite acelasi document (numerar, chitanta, bon fiscal, POS — - toate patru, nu doar unul) o data pe calea veche (`frm_alte_date` dialog) si o data pe formularul - unificat, compara `poDate`-ul rezultat inainte de scriere in Oracle (aceleasi campuri de incasare, - acelasi numar alocat) — tipar deja folosit in `s3_portare_antet.md` §7 pentru S3, reutilizat aici - pe grupul mai mic al lui S3b. - -*Depinde de S3* (planul o spune explicit) — pasii 4 si 6 presupun ca `but_modifica`/blocarea antetului -(proiectata in S3, sectiunea I a planului) exista deja ca mecanism, nu doar ca design pe hartie. - ---- - -## 8. Ce nu se poate testa headless - -Precedent direct: `s3_portare_antet.md` §8, si memoria de proiect -(`grid-coloane-nu-se-materializeaza-headless` — coloanele de grid nu se materializeaza sub `-A -T`, -exista harness UI vizibil separat care le citeste corect). - -**Se POATE testa headless** (contrar primei intuitii, pentru ca e logica pe proprietati, nu pictura pe -ecran): -- Toata masina de stari `actualizeaza_tipincasare` — `Createobject()` fara `.Show()` tot ruleaza - `Init` si tot cabl eaza evenimentele; `thisform.opt_incasat.Value = n` declanseaza - `ProgrammaticChange` identic cu UI vizibil sau nu. Verificarile de alocare/dezalocare de la pasul 3 - al sectiunii 7 sunt testabile 100% headless, pe proprietatile lui `poGeneratorNumere`. -- Vizibilitatea controalelor dupa toggle (`.Visible`, `.Height`, existenta obiectului via `Type(...)`) - — acelasi motiv, confirmat deja de `s3_portare_antet.md` §8 pentru mecanismul similar din `Init`-urile - de antet. - -**NU se poate testa headless**: -- **Aspectul vizual real dupa pliere/depliere** (suprapuneri, goluri, pozitionarea corecta a - controalelor de sub sectiune dupa reflow-ul manual, sectiunea 6.1) — layout absolut-pozitionat, fara - reflow automat; verificarea ceruta e "arata bine pe ecran", nu o proprietate booleana — necesita - harness UI vizibil (memoria de proiect citata mai sus) sau verificare manuala de Marius. -- **Cele patru dialoguri modale** (`do_cauta_delegat`/`do_cauta_masina`/`do_cauta_agent`/ - `do_cauta_adresa`) — interactiunea reala de alegere dintr-o lista nu se simuleaza headless; se poate - testa doar efectul **dupa** ce rezultatul cautarii e construit manual (tiparul deja folosit in - proiect, citat in `s3_portare_antet.md` §8, pct. 1). -- **Lookup-urile Oracle din `Init` (delegat/masina ultima factura, `v_nom_casa`)** — necesita conexiune - Oracle reala (harnessul suporta asta prin `MARIUSM_AUTO`, dar tot nu e "headless" in sensul de - "fara dependinte externe") si date de test care sa existe pentru clientul folosit. -- **Butonul `cmdModificaBon`/`do_modifica_bon`** — deschide `viz_config_serii_complet WITH 3` - (`oserii_numere.prg`), alt formular modal, aceeasi limitare ca dialogurile de cautare. - ---- - -## 9. Riscuri si capcane - -1. **Riscul central** (sectiunea 4.3): re-populare a starii `opt_incasat` la fiecare toggle de pliere - ar re-declansa alocare/dezalocare, incalcand direct criteriul cerut de decizia 25/S3b. Tratat - explicit ca pas 3 in ordinea de executie — **nu** de lasat "se rezolva de la sine prin arhitectura", - pentru ca mecanismul de declansare (`ProgrammaticChange`) e usor de lovit accidental de orice cod - ulterior care "resincronizeaza UI-ul cu modelul" intr-un loc gresit. -2. **Gap preexistent, nu introdus de S3b, dar mostenit prin "muta ca atare"**: `inainte_de_do_renunt` - (`:3025-3030`) nu dezaloca numarul POS (26) la anulare — doar chitanta (16) si bon fiscal (3). Daca - formularul unificat introduce vreo cale noua de "renunta la document" care nu mapeaza exact pe acest - cod, riscul se poate agrava (numar POS ramas alocat orfan, de fiecare data cand utilizatorul incepe - un document cu POS si renunta). Recomandare: fie se corecteaza in trecere (nu e in perimetrul strict - al portarii "ca atare", dar e o linie), fie se semnaleaza explicit ca bug cunoscut de preluat separat. -3. **`_checkbox1`/"Listare detaliata" vs. `chkDetaliat`** (sectiunea 1.4) — doua controale cu semantica - probabil identica, ambiguitate deja semnalata in S1 (randul 57), neinchisa aici. Formularul unificat - trebuie sa aleaga unul singur; alegerea gresita (portarea amandurora ca doua campuri distincte) - ar introduce un camp duplicat fara sens pentru utilizator. -4. **`do_dezactiveaza()` insuficient pentru read-only real** (sectiunea 2.1) — daca implementarea - apeleaza doar metoda existenta fara completarea de `ReadOnly`, decizia 26 nu e respectata mecanic: - campul arata blocat (fara lupa) dar tot accepta taste. -5. **Referinte `Thisform.xxx` nekvalificate** in `actualizeaza_tipincasare` (sectiunea 3.2) — orice - decizie de a pune sectiunea pliata intr-un container/obiect separat (nu direct pe `Thisform`) rupe - tacit aceste referinte, cu eroare de "obiect inexistent" la runtime, nu la compilare. -6. **Dependinta pe S3**: `but_modifica` si testul "document existent" (sectiunea 5.1) nu exista inca - nicaieri in cod — proiectate doar pe hartie in plan (sectiunea I). Pasii 4 si 6 din sectiunea 7 nu - pot fi *implementati* complet inainte ca S3 sa livreze acel mecanism, doar proiectati. -7. **Bug-ul de `sectie`** (`omodificari.vc2:13941`, semnalat deja in `rute_scriere_antet.md` §1.3 si - in plan `:761-764`) — nu afecteaza direct S3b (analiticele sunt read-only aici, deci S3b nu scrie - niciodata `id_sectie`), dar afecteaza increderea in ce se **afiseaza**: daca bug-ul e real, coloana - `id_sectie` din Oracle poate sa nu reflecte eticheta aratata dupa o editare din #6. In afara - perimetrului S3b de reparat (e in #6), dar relevant pentru cine citeste afisarea din #13 dupa o - editare de la #6. - ---- - -## 10. Criteriul de "gata", rescris verificabil - -Textul din plan (`:1849-1852`) spune: *"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."* Precizat mai jos, pe fiecare bucata: - -1. **Paritate de emitere, pe toate patru tipurile de incasare, nu doar bon fiscal**: se emite acelasi - document de test (client, articole, cantitati, preturi identice) o data pe calea veche - (`frm_date_factura`/`frm_date_aviz` -> `frm_facturare_articole(2)` -> `frm_alte_date`) si o data pe - formularul unificat, pentru fiecare din **Fara incasare / Chitanta / Bon fiscal / POS-Card**. Se - compara `lcListaIncasare` (stringul asamblat inainte de `scrie_factura2`, sectiunea 3.1 al - `rute_scriere_antet.md`) si numarul alocat (`poDate.nr_incasare`) — trebuie sa fie identice pe - fiecare din cele patru cazuri, nu doar pe bon fiscal. -2. **Non-alocare la toggle de pliere, testabila headless** (sectiunea 7, pasul 3, deja formulat ca - assert concret pe `poGeneratorNumere`) — pe **doua** scenarii de start, nu unul: document nou - (`poDate.incasat=0` la construire) **si** document venit din copiere cu incasare presetata - (`poDate.incasat<>0`, sectiunea 4.1 punctul 3) — al doilea caz e cel care azi *deja* aloca la - deschidere, exact scenariul pe care criteriul trebuie sa-l acopere explicit. -3. **Blocarea grupului C, pe ambele stari ale antetului**: pe un document deja emis, campurile din - grupul de incasare raman `Enabled=.F.` atat inainte cat si dupa apasarea lui `but_modifica` — nu - doar "la deschidere" (ce ar lasa loc unei regresii daca `but_modifica` le-ar debloca din greseala - odata cu restul). -4. **Analiticele, pe ambele stari**: pe un document emis, cele patru campuri (venit/cheltuiala, sectie, - responsabil, lucrare) afiseaza valoarea curenta din sursa lor (sectiunea 2.3 — de precizat exact - care sursa la implementare) si resping orice tentativa de tastare directa sau de deschidere a - dialogului de cautare — verificat pe proprietatea `ReadOnly`/`lactiv`, nu doar vizual. -5. **Delegat/transport/adresa/text aditional** — paritate camp-cu-camp cu `frm_alte_date` de azi, pe - cel putin un document proforma (grup delegat/incasare eliminat, sectiunea 1.4) si unul non-proforma - (grup complet), ca sa acopere ambele ramuri ale `Init`-ului de azi (`:3109-3207`). - -*Depinde de:* S3 (but_modifica, mecanismul de blocare a antetului). *Nu depinde de:* etapa II -(regenerarea) — grupurile B/C raman blocate prin design in etapa I, nu prin absenta temporara a unei -functionalitati care ar trebui testata aici. - ---- - -## Ce ramane de decis de Marius - -1. **Textul exact al tooltip-urilor** pentru analiticele read-only (sectiunea 2.2) si pentru grupul de - incasare blocat pe document emis (sectiunea 5.3) — doar continutul minim necesar e propus aici, nu - formularea finala. -2. **Granularitatea butonului/butoanelor de pliere**: un singur comutator pentru toata zona "alte date - + analitice", sau unul separat per subgrup (delegat/transport, incasare, adresa, text, analitice)? - Plan sectiunea I nu specifica, iar tiparul gasit (`afiseaza_rulaje`) e per-sectiune unica, nu - per-formular-intreg — precedentul nu decide singur granularitatea (sectiunea 6.1). -3. **Eager vs. lazy pentru lookup-urile Oracle din `Init`** (delegat/masina ultima factura, casa) — - semnalat si in `s3_portare_antet.md` §5 ca discutie deschisa, reconfirmat aici pentru acelasi cod - (sectiunea 6.1, ultimul punct). -4. **Corectarea gap-ului de dezalocare POS la anulare** (sectiunea 9, punctul 2) — se repara in trecere - ca parte a S3b, sau se lasa exact ca azi si se semnaleaza separat ca bug de preluat ulterior? -5. **`_checkbox1` vs. `chkDetaliat`** (sectiunea 1.4, sectiunea 9 punctul 3) — care dintre cele doua - controale de "listare detaliata" devine campul unic in formularul unificat; ambiguitatea ramane - deschisa din S1 si nu s-a inchis aici. - -## Handoff - -Cercetare incheiata, nu intrerupta la mijloc — toate cele zece sectiuni cerute in briefing sunt -complete, cu citate `fisier:linie` verificate direct pe fisierele reale (nu `.bak`). Niciun cod -atins, nicio interogare Oracle rulata (nu a fost nevoie — tot ce trebuia era in codul VFP deja citit -sau in cele patru rapoarte de referinta). Fisierul e complet la aceasta versiune; nu e nevoie de o -sesiune de continuare pentru S3b ca atare. Urmatorul pas natural (nu al acestei sarcini) ar fi S3 -insusi (blocarea antetului, `but_modifica`) — S3b depinde de el pentru pasii 4 si 6 din sectiunea 7, -dar proiectarea de aici nu asteapta acel livrabil, doar implementarea o va astepta. diff --git a/docs/cercetare/s3c_sursa_ca_parametru.md b/docs/cercetare/s3c_sursa_ca_parametru.md deleted file mode 100644 index d9b2c96..0000000 --- a/docs/cercetare/s3c_sursa_ca_parametru.md +++ /dev/null @@ -1,426 +0,0 @@ -# S3c — Proiectare: sursa ca parametru, nu ca global - -Cercetare read-only pentru povestea **S3c** din `docs\plan_13_unificare_formular_facturare.md` -(`#### S3c`, linia 1855; decizia 12, linia 473; L.3, linia 1733; „Canalul de precompletare”, -liniile 465-474). Zero modificari de cod, zero write-back, zero `git_sync.ps1`, zero commit — in -niciun produs din `D:\ROA`. Depinde de S2 (`docs\cercetare\s2_factureaza_unificare.md`), a carui -proiectare a lasat explicit semnatura lui `factureaza` neschimbata „pentru ca e treaba lui S3c” -(sectiunea 5 a acelui raport). - -Status: **complet**. - ---- - -## Verdict - -**Se poate face, e o interventie mica pe cod, dar textul planului supraestimeaza cat de „globala" -e problema azi.** `goContract` nu e niciodata scris in `ROAFACTURARE` — nici in codul specific -produsului, nici in `COMUN`-ul lui — deci ramura de precompletare din contract -(`ofacturare_comun.prg:261-297`) e **cod mort in ROAFACTURARE azi**, nu doar teoretic riscant. -Asimetria de resetare semnalata la L.3 (`ofundal_facturare.vc2:886-902`) exista textual, dar -**nu produce niciun efect observabil azi**, pentru ca nu exista niciun scriitor al lui `goContract` -in acest produs care sa lase ceva de resetat. Ea devine un risc real abia daca cineva adauga in -viitor, in `ROAFACTURARE`, un cod care scrie `goContract` — situatie in care lipsa resetarii ar -deveni activa dintr-o data, tacut. `goComanda`, in schimb, chiar e viu in `ROAFACTURARE`: are doi -scriitori (`ocomenzi.vc2:1583` la clic pe „Factureaza”, **si** `ocomenzi.vc2:2199`, ca efect -colateral al navigarii in grid), iar reset-ul explicit de la `ofundal_facturare.vc2:900` e singura -plasa de siguranta reala azi. - -**Suprafata de regresie e mica si masurata exact**: doi scriitori de convertit -(`ocomenzi.vc2:1580-1596` in familia ROAFACTURARE, `ferestre_contracte.vc2:1538-1549`+`:1598-1606` -in ROACONTRACTE), trei fisiere comune de atins o singura data fiecare -(`ofacturare.prg`, `oproceduri_facturare.prg`, `ofacturare_comun.prg`), si **zero schimbari** la -celelalte ~31 puncte de intrare din inventarul S2 — toate cheama `factureaza(N)` cu un singur -parametru, deci un al treilea parametru opozitional cu implicit `.F.`/`NULL` nu le atinge. - -**Corectie de scop fata de formularea planului**: „pana se convertesc toti apelantii" nu poate -insemna, pentru `goContract`, „pana dispare global-ul" — `goContract` e bufferul de editare al -intregului ecran de contracte din ROACONTRACTE (peste 100 de `ControlSource` legate de el, -sectiunea 1), populat continuu de navigarea in grid, independent de facturare. Nu va disparea -niciodata din ROACONTRACTE la aceasta poveste sau la vreuna viitoare rezonabila — S3c schimba doar -**canalul prin care valoarea ajunge la `oDateFactura.Init`**, nu existenta globalei in ROACONTRACTE. -Criteriul de „gata” de mai jos (sectiunea 8) e rescris sa reflecte asta. - ---- - -## 1. Inventarul scrierilor si citirilor `goComanda` / `goContract` in toata suita - -Cautare in `D:\ROA\ROAFACTURARE`, `D:\ROA\ROACONT`, `D:\ROA\ROAGEST`, `D:\ROA\ROAAUTO`, -`D:\ROA\ROAACNPRO`, `D:\ROA\ROAIMOB`, `D:\ROA\ROACONTRACTE` — radacina fiecarui produs **si** -`COMUN\`-ul lui explicit (capcana „Grep nu vede COMUN”). `D:\ROA\COMUNROA` nu contine niciuna din -cele doua variabile (cautare separata, zero rezultate) — confirma ca sursa nu e livrata prin -biblioteca partajata, ci prin fisierele COMUN duplicate per produs. - -### `goComanda` - -| Fisier | Linie | Rol | Produse | -|---|---|---|---| -| `Programe\roafacturare.prg` | `:473-474` | **Declarare**: `PRIVATE goComanda` / `goComanda = null`, la pornirea aplicatiei | doar ROAFACTURARE (fiecare produs are propriul `roafacturare.prg`/echivalent, nu verificat identic — irelevant, doar initializeaza la null) | -| `COMUN\clase\ocomenzi.vc2:1580-1596` (`ct_comenzi.do_factura`) | `:1583` | **SCRIE**: `SELECT crsComenzi` / `SCATTER NAME goComanda MEMO`, apoi cheama `facturare_comenzi` | ROAFACTURARE, ROACONT, ROAGEST, ROAAUTO, ROAACNPRO, ROAIMOB (fisier identic pe MD5, sectiunea 6) | -| `COMUN\clase\ocomenzi.vc2:2191-2207` (`_grdrow1.AfterRowColChange`) | `:2199` | **SCRIE** ca efect colateral: la fiecare schimbare de rand in grid-ul de comenzi, `SCATTER NAME goComanda MEMO`, folosit doar pentru `but_factura1.Visible = (goComanda.facturat = 0)` (`:2202`) — **nu are nicio legatura cu intentia de facturare**, dar suprascrie global-ul de fiecare data cand utilizatorul navigheaza in grid | idem, acelasi fisier identic | -| `COMUN\programe\ofacturare_comun.prg:301-328` (`oDateFactura.Init`) | `:301` | **CITESTE**: `If tnTip = 3 And Type('goComanda') = 'O'` -> `.id_client`, `.nume_client`, `.cod_fiscal`, `.listaid`, `.descriere`, `.id_sectie`, plus un `SELECT sectie FROM nom_sectii` pe `goComanda.id_sectie` | identic pe 6/7 produse (sectiunea 6; ROAIMOB diverge cu o linie, dar nu pe acest bloc) | -| `Clase\ofundal_facturare.vc2:899-902` (`Page2.Cw3.do_actiune`) | `:900` | **RESETEAZA** explicit: `goComanda = ''` inainte de `DO facturare_comenzi` | doar ROAFACTURARE (fisierul e specific produsului, nu COMUN) | - -Niciun alt fisier, in niciun produs verificat, nu scrie sau citeste `goComanda`. - -### `goContract` - -| Fisier | Linie | Rol | Produse | -|---|---|---|---| -| `ROACONTRACTE\Programe\roacontracte.prg:559-560` | `:559-560` | **Declarare**: `Public poCtr, goContract` / `Store '' To poCtr, goContract`, la pornirea aplicatiei | doar ROACONTRACTE | -| `ROACONTRACTE\Clase\ferestre_contracte.vc2` | ~200 aparitii (liniile 977-11005, tabel complet in sectiunea de cautare) | **SCRIE si CITESTE** ca buffer de editare al intregului formular de contracte: `Scatter Name goContract Memo Blank` la creare (`:1026`, `:1186`), `SCATTER NAME goContract MEMO` la fiecare schimbare de rand in grid (`grid_contracte.AfterRowColChange`, `:1598-1606`), **peste 20** `ControlSource = "goContract."` pe controale (combo-uri, textbox-uri), `Gather Name goContract Memo` la salvare (`:1053`, `:2406-2472` etc.) | doar ROACONTRACTE | -| `ROACONTRACTE\Clase\ferestre_contracte.vc2:1538-1549` (`but_factura.Click`) | — | **CITESTE implicit** (nu re-scrie): cheama `facturare_contracte` fara sa re-populeze `goContract` — se bazeaza pe scrierea facuta deja de `AfterRowColChange` la selectia randului curent | doar ROACONTRACTE | -| `ROACONTRACTE\Clase\outlook2003bar.vc2`, `ofundal_roaclienti.vc2` | multiple | **CITESC** `goContract.id_ctr`/`.id_part` pentru navigare in bara laterala si ecrane de parteneri | doar ROACONTRACTE | -| `ROACONTRACTE\Programe\roacontracte.prg`, `oparteneri_contracte.prg`, `oproceduri_roacontracte.prg` | multiple | **CITESC/SCRIU** `goContract` in fluxuri proprii ROACONTRACTE (incasari, plati, rate, garantii) — complet independente de facturare | doar ROACONTRACTE | -| `COMUN\programe\ofacturare_comun.prg:261-297` (`oDateFactura.Init`) | `:261` | **CITESTE**: `If INLIST(m.tnTip, 2, 6, 52) And Type('goContract') <> 'U'` -> `.id_client`, `.nume_client`, `.cod_fiscal`, `.listaid`, `.descriere`, `.id_sectie`, `.sectie`, `.id_responsabil`, `.responsabil`, `.id_valuta`, `.nume_valuta`, plus interogare `fact_vcontracte` pentru scadenta | identic pe 6/7 produse (ROAIMOB diverge cu exact aceasta linie lipsa — sectiunea 6) | -| `COMUN\clase\ferestre_atasamente.vc2`, `COMUN\programe\oproceduri_atasamente.prg` | 5 aparitii | **CITESC** `goContract.id_ctr`, dar **numai** in ramura `Case Upper(Alltrim(gcNumeProgram)) = "ROACONTRACTE"` — cod prezent in fiecare copie COMUN (deci si in ROAFACTURARE), dar mort acolo pentru ca `gcNumeProgram` nu e niciodata `"ROACONTRACTE"` in afara procesului ROACONTRACTE | identic pe toate produsele verificate, dar activ doar in ROACONTRACTE | - -**Confirmare directa: `goContract` nu e scris niciunde in `ROAFACTURARE`** — nici in `Programe\`, -`Clase\`, `Ferestre\`, `Meniuri\`, nici in `COMUN\` (cautare pe `PUBLIC goContract`/`Public -goContract` in tot arborele: zero rezultate, tabelul complet cu toate declaratiile `PUBLIC go*` -gasite e in sectiunea 2). Singura mentiune a lui `goContract` in ROAFACTURARE e citirea garda de -`Type()` din `ofacturare_comun.prg:261`, care ramane mereu falsa (`Type('goContract') = 'U'`) intr-o -sesiune ROAFACTURARE. - -## 2. Verdict pe L.3 — asimetria de resetare - -**Textul din cod e adevarat, dar efectul practic e altul decat sugereaza formularea „de verificat” -din plan.** - -- **Asimetria exista, literal**: `ofundal_facturare.vc2:899-902` (`Page2.Cw3`, ruta genericaa de - comanda) face `goComanda = ''` inainte de `DO facturare_comenzi`; `ofundal_facturare.vc2:886-897` - (`Page2.Cw2`, ruta generica de contract) **nu** face echivalentul pentru `goContract` — cheama - direct `DO facturare_contracte WITH 'FACTURA LEI'/'INVOICE'/'FACTURA VALUTA'`. -- **Dar `facturare_contracte` (`oproceduri_facturare.prg:119-136`) nu citeste si nu scrie - `goContract` deloc** — primeste un string de tip (`'FACTURA LEI'` etc.) si cheama direct - `factureaza(2)`/`factureaza(6)`/`factureaza(52)`. Precompletarea din contract se intampla - **exclusiv** in `oDateFactura.Init`, la citirea globalei — nu exista niciun pas intermediar care - ar putea fi „resetat”. - In ROAFACTURARE, calea genericaa (Cw2) las utilizatorul sa aleaga contractul **in formular**, - printr-un combo populat din `crscontracte` legat de `poDate.listaid` - (`ofacturare.prg:283-291,433-440`) — un canal complet diferit, care nu trece prin `goContract` - deloc (confirmat citind codul, sectiunea 3). -- **Deci, in ROAFACTURARE azi, nu exista nicio secventa executabila in care `goContract` sa fie - citit cu o valoare veche.** Pentru ca asta sa se intample ar trebui ca (a) ceva sa fi scris - `goContract` mai devreme in aceeasi sesiune ROAFACTURARE — **nu exista niciun asemenea cod azi** — - si (b) urmatorul apel sa fie cu `tnTip` in `(2, 6, 52)`. Fara (a), (b) singur nu ajunge nicaieri. -- **Verdictul pe L.3**: asimetria de resetare **e reala ca defect de simetrie in cod** (o ruta isi - curata globala inainte de apel, cealalta nu), dar **inert azi in ROAFACTURARE** — nu exista niciun - document gresit care se poate emite azi din cauza ei, pentru ca variabila pe care ar trebui sa o - resetezi nu e niciodata populata in acest produs. Devine un risc real **doar** daca un cod viitor - (in ROAFACTURARE) incepe sa scrie `goContract` fara sa adauge simetric si resetul — exact genul de - capcana pe care „parametru explicit, fara global implicit” o elimina structural, nu prin inca o - linie de reset de tinut minte. -- **In ROACONTRACTE, unde `goContract` chiar e viu, nu exista o ruta „generica fara precompletare” - analoaga lui Cw2** — `but_factura.Click` (`:1538-1549`) e singurul punct de intrare si presupune - intotdeauna un contract selectat in grid (populat de `AfterRowColChange`, `:1598-1606`, la fiecare - schimbare de rand). Riscul teoretic acolo nu e „global ramas din alta sesiune de facturare”, ci - „utilizatorul apasa Factureaza fara sa fi selectat explicit un rand dupa un refresh programatic” — - un caz marginal, netratat de L.3 si nelegat de asimetria semnalata in plan. - -## 3. Doar doua globale, sau mai sunt? - -**Doar doua.** Verificat prin citirea completa a `oDateFactura.Init` -(`ofacturare_comun.prg:223-332`) si a rutarii cursoarelor de articole din `ofacturare.prg:260-308`: - -- **`goDate`, `gnIdSet` ca surse de precompletare — nu exista.** `goDate` nu apare niciunde in - `ofacturare.prg`/`ofacturare_comun.prg` (cautare directa, zero rezultate). `gnIdSet` nu exista ca - atare — parametrul se numeste `tnIdSet`/`lnIdSet`, calculat **local** in `factureaza` - (`lnIdSet = 25000 + tnTip - 1 + gnScadereStoc * 10`, `ofacturare.prg:123`) din `tnTip` si un - optiune de configurare (`gnScadereStoc`), nu un canal de sursa. -- **Singurele doua verificari `Type('go...')` din `oDateFactura.Init` sunt exact `goContract` - (`:261`) si `goComanda` (`:301`)** — cautare `Type\('go` pe tot `ofacturare.prg` + - `ofacturare_comun.prg`: zero alte rezultate in afara comutatorului de dezvoltator - `gnFacturareNou` (irelevant aici, documentat in S2). -- **`poDate.listaid` pentru avize (tip 4, 21, 28, 42, 47) NU trece printr-un global** — se scrie - direct din cursorul de selectie al formularului: `poDate.listaid = Iif(Inlist(poDate.tip, 3, 21, - 28, 42, 47), Alltrim(Str(id_comanda)), [])` (`ofacturare_comun.vc2:4255`, si varianta similara la - `:4062`, `:7271`) — populat dintr-un `Scan`/`Locate` peste cursorul cu avizele bifate de utilizator - in acelasi apel, nu dintr-o variabila globala persistenta intre apeluri. **Avizele nu intra in - S3c** — nu au canal implicit de eliminat. -- **Copierea (`toFactura`) e deja parametru, nu global** — `copiere_factura(toFactura)` - (`oproceduri_facturare.prg:150-153`) -> `factureaza(toFactura.Tip, toFactura)`, si in - `oDateFactura.Init`/logica de copiere valorile vin din `toDateAnterior` (parametrul), de exemplu - `.listaid = toDateAnterior.id_vanzare` (`ofacturare_comun.vc2:387`) — **e exact precedentul pe - care S3c il extinde la comanda/contract**, nu un al treilea canal implicit de adaugat la lista. -- **`poDate` insusi nu e un canal de scurgere intre apeluri** — se creeaza cu `Createobject` la - fiecare intrare in bucla lui `factureaza` (`ofacturare.prg:184`, doar `If Type('poDate') <> 'O'`, - ceea ce e adevarat prima data si ramane fals doar in interiorul aceluiasi apel, la reintrarile - bucla `Do While lnRaspuns = 6`) — deci nu poate purta stare intre doua clicuri distincte pe - „Factureaza”. - -**Concluzie**: `goComanda` si `goContract` sunt singurele doua canale implicite relevante pentru -decizia 12. Nu mai exista o a treia variabila de convertit odata cu ele. - -## 4. Semnatura propusa - -**`factureaza`** (`COMUN\programe\ofacturare.prg:81-82`), azi `Lparameters tnTip, toFactura`: - -``` -Lparameters tnTip, toFactura, toSursa -``` - -- **`toSursa`** — obiect, implicit `NULL` (nepasat de niciun apelant existent). Poarta **fie** un - obiect cu forma lui `goComanda` (cand `tnTip = 3`), **fie** un obiect cu forma lui `goContract` - (cand `tnTip IN (2, 6, 52)`) — exact aceeasi dualitate pe care codul de azi o rezolva deja prin - ramificare pe `tnTip` in `oDateFactura.Init` (`:261` vs `:301`), deci nu introduce un tip nou de - decizie, doar muta sursa valorii. -- **Pozitia**: al treilea parametru, dupa `toFactura`, niciodata inaintea lui — la fel ca precedentul - `V_TAXCODE`/`V_LOT` citat de tine: apelurile VFP sunt pozitionale, iar singurul mod sa nu rupi - apelantii existenti e sa adaugi la coada, cu implicit. Toate cele ~31 de apeluri din inventarul S2 - care pasesc doar `tnTip` raman **neschimbate** — VFP completeaza automat parametrii nepasati la - coada cu `.F.` (verificat comportamental: `Type()` pe un parametru nepasat intoarce `'L'` cu - valoarea `.F.`, nu `'U'` — de tratat explicit in garda, vezi sectiunea 5). -- **De ce nu inlocuieste `toFactura`**: `toFactura` inseamna „copiaza factura asta” (un document deja - emis), `toSursa` inseamna „precompleteaza din documentul asta” (o comanda sau un contract inca - nefacturat) — semantic distincte, si `copiere_factura` (`oproceduri_facturare.prg:150-153`) ar - putea teoretic avea nevoie de amandoua simultan in viitor (copiere + realocare pe alt contract) — - motiv suplimentar sa nu le contopesti intr-un singur parametru. - -**Constructorul `oDateFactura.Init`** (`ofacturare_comun.prg:223-224`), azi -`Lparameters tnIdSet, tnTip`: - -``` -Lparameters tnIdSet, tnTip, toSursa -``` - -- Aceeasi regula de pozitionare: la coada. Singurul apelant azi e - `Createobject("oDateFactura", lnIdSet, tnTip)` din `factureaza` (`ofacturare.prg:184`) — devine - `Createobject("oDateFactura", lnIdSet, tnTip, toSursa)`. Nu exista alt loc din cod care - instantiaza `oDateFactura` (cautare `Createobject("oDateFactura"` pe tot `COMUN`: un singur - rezultat). - -## 5. Mecanismul de sursa de rezerva - -**Regula**: la fiecare din cele doua ramuri, se prefera `toSursa` daca a fost pasat ca obiect; daca -nu, se cade pe globala, exact ca azi. Nu se schimba forma datelor citite (aceleasi campuri), doar -sursa lor. - -```foxpro -* ofacturare_comun.prg, oDateFactura.Init — inlocuieste liniile 261 si 301 - -Local loSursa -loSursa = Iif(Type('toSursa') = 'O', toSursa, Null) - -If INLIST(m.tnTip, 2, 6, 52) And (Type('loSursa') = 'O' Or Type('goContract') <> 'U') - If Type('loSursa') <> 'O' - loSursa = goContract - Endif - .id_client = loSursa.id_part - .nume_client = loSursa.denumire - * ... restul neschimbat, doar goContract -> loSursa -Endif - -loSursa = Iif(Type('toSursa') = 'O', toSursa, Null) && re-evaluat, nu se refoloseste variabila de mai sus intre ramuri - -If tnTip = 3 And (Type('loSursa') = 'O' Or Type('goComanda') = 'O') - If Type('loSursa') <> 'O' - loSursa = goComanda - Endif - .id_client = loSursa.id_part - * ... restul neschimbat, doar goComanda -> loSursa -Endif -``` - -De ce doua evaluari separate ale lui `loSursa` (nu una singura la inceput): `toSursa` poarta o -singura forma per apel (fie comanda, fie contract, niciodata amandoua — `tnTip` decide exclusiv -care), dar garda trebuie sa verifice `tnTip` inainte sa presupuna forma, la fel ca azi. O variabila -locala unica evita sa scrii `toSursa` de doua ori in tot blocul, fara sa schimbe logica. - -**Cum se stie cand se poate scoate globala** — criteriu verificabil, nu „cand se convertesc toti”: - -- **Pentru `goComanda`**: nu se poate scoate niciodata complet din `ROAFACTURARE`, pentru ca al - doilea ei scriitor (`ocomenzi.vc2:2191-2207`, `AfterRowColChange`) nu are nicio legatura cu - `factureaza` — exista doar pentru `but_factura1.Visible`. Criteriul realist: **linia de citire in - `oDateFactura.Init` (`:301`) poate deveni neconditionata de fallback abia cand se confirma, prin - `git_sync.ps1` + Grep, ca niciun apel la `factureaza(3, ...)` mai lasa `toSursa` nepasat** — adica - se verifica apelantii lui `factureaza`, nu scriitorii globalei (care raman, pentru alt scop). -- **Pentru `goContract`**: acelasi lucru, dar cu o observatie in plus — global-ul insusi - (`Public goContract` in `roacontracte.prg:559-560`) **nu va disparea niciodata** cat timp exista - formularul de contracte din ROACONTRACTE, care il foloseste ca buffer de editare, nu doar ca - transport spre facturare. Criteriul de scos fallback-ul: **cand toate apelurile catre - `factureaza(2/6/52, ...)`, in toate produsele, pasesc `toSursa` explicit** — verificabil cu - aceeasi comanda `Select-String` ca in S2, aplicata pe `factureaza(2` / `factureaza(6` / - `factureaza(52` in loc de `factureaza2`. -- Pana atunci, fallback-ul ramane — el nu costa nimic in productie (o verificare `Type()` in plus), - si e singura plasa de siguranta pentru cele ~31 puncte de intrare care nu au fost, nu vor fi, si - nu trebuie sa fie convertite (nu au sursa de precompletat). - -## 6. Cele N copii ale fisierelor atinse — identice sau divergente? - -MD5 pe fiecare fisier, in cele 7 produse care au copie (`ROAFACTURARE`, `ROACONT`, `ROAGEST`, -`ROAAUTO`, `ROAACNPRO`, `ROAIMOB`, `ROACONTRACTE`): - -| Fisier | Rezultat | -|---|---| -| `COMUN\programe\ofacturare.prg` (2662 linii) | **Identic pe toate cele 7** — un singur MD5 | -| `COMUN\programe\oproceduri_facturare.prg` (2393 linii) | **Identic pe toate cele 7** — un singur MD5 | -| `COMUN\clase\ocomenzi.vc2` (8301 linii) | **Identic pe cele 6** care il au (nu verificat separat in ROACONTRACTE, care nu are comenzi) | -| `COMUN\programe\ofacturare_comun.prg` (2224 linii) | **Identic pe 6 din 7** — `ROAIMOB` diverge cu **exact o linie lipsa**: `:264`, `.cod_fiscal = ALLTRIM(NVL(goContract.cod_fiscal, ''))`, absenta din copia ROAIMOB (2223 linii). Restul fisierului, inclusiv blocul `goComanda`/`goContract` de la `:261-328`, e identic caracter cu caracter. | - -**Spre deosebire de `ofacturare.vc2` (7 copii, 4 variante MD5 distincte, citat ca precedent de -comparat)**, fisierele atinse de S3c sunt aproape perfect sincronizate — un singur punct de drift, -minor si izolat. Concluzie pentru propagare: modificarea din `ofacturare.prg` si -`oproceduri_facturare.prg` se poate copia **caracter cu caracter** in toate cele 7 produse fara nicio -adaptare. Modificarea din `ofacturare_comun.prg` se poate copia identic in 6 produse, dar **in -ROAIMOB trebuie aplicata pe baza divergentei existente** (linia `cod_fiscal` lipseste deja acolo — -o copiere oarba a diff-ului ar putea sa nu se aplice curat sau sa reintroduca acea linie fara sa fie -intentionat; de verificat manual la sincronizare, nu doar copiat). - -`ferestre_contracte.vc2` (ROACONTRACTE) e specific produsului, nu are copii de sincronizat. - -## 7. Suprafata de regresie masurata - -**Fisiere care chiar se modifica** (5, plus 1 verificare de sincronizare): - -| # | Fisier | Ce se schimba | Cate copii de propagat | -|---|---|---|---| -| 1 | `COMUN\programe\ofacturare.prg:81-82` | semnatura `factureaza`, +`toSursa` | 7 (identice, copiere directa) | -| 2 | `COMUN\programe\ofacturare_comun.prg:223-224, 261-328` | semnatura `Init`, +`toSursa`; ramurile de citire cu fallback | 7 (6 identice + ROAIMOB cu drift de reconciliat) | -| 3 | `COMUN\programe\oproceduri_facturare.prg:119-141` | `facturare_contracte(tcTip, toSursa)`, `facturare_comenzi(toSursa)`, relay catre `factureaza(N, NULL, toSursa)` | 7 (identice, copiere directa) | -| 4 | `COMUN\clase\ocomenzi.vc2:1580-1596` (`do_factura`) | `SCATTER NAME loComanda MEMO` (local, nu global) + `DO facturare_comenzi WITH loComanda IN oproceduri_facturare.prg` | 6 (identice, copiere directa) | -| 5 | `ROACONTRACTE\Clase\ferestre_contracte.vc2:1538-1549` (`but_factura.Click`) | trimite `goContract` (sau o copie locala scatter-uita din randul curent) explicit ca al doilea parametru la `DO facturare_contracte WITH lcTip, goContract IN ...` | 1 (specific ROACONTRACTE) | - -**Apelanti care NU se modifica** (confirmat, nu presupus): toate cele ~31 de puncte de intrare din -inventarul S2 (`Meniuri\politica.mn2`, `Meniuri\contracte.mn2`, `Meniuri\aviz_*.mn2`, -`ofundal_facturare.vc2:883-905`) cheama `factureaza(N)` cu un singur argument sau -`facturare_contracte`/`facturare_comenzi` fara sursa — al treilea parametru ramane implicit `NULL`, -fallback-ul pe global preia (pentru cele care oricum nu aveau sursa reala, fallback-ul da acelasi -rezultat ca azi: nimic de precompletat). **Inclusiv `ofundal_facturare.vc2:886-902` (Page2.Cw2/Cw3) -raman neschimbate** — `Cw3` isi pastreaza `goComanda = ''` (devine redundant, dar inofensiv, odata ce -`do_factura` trimite explicit; se poate curata separat, nu la aceasta poveste), `Cw2` ramane cum e. - -**Total: 5 fisiere reale de editat, in 4 continuturi distincte de modificare** (fisierele #1-#3 au -acelasi continut in toate copiile lor), propagate in pana la 7 produse — mult sub suprafata unei -modificari „de suita” tipice, pentru ca `ofacturare.prg`/`oproceduri_facturare.prg` sunt deja -perfect sincronizate (sectiunea 6). - -## 8. Ordinea de executie, ca suita sa nu fie stricata niciun moment - -Spre deosebire de S2 (unde `factureaza2` nu avea alt apelant decat el insusi, deci risc de stricare -aproape nul), aici **exista o fereastra in care semnaturile trebuie sa fie compatibile intre fisiere -diferite** (`ofacturare.prg` cheama `oDateFactura::Init`, `oproceduri_facturare.prg` cheama -`factureaza`) — ordinea trebuie sa garanteze ca niciun punct intermediar nu are un apelant cu -semnatura veche impotriva unui apelat cu semnatura noua incompatibila. Pentru ca **toti parametrii -noi sunt optionali, la coada**, riscul e de fapt mic — o semnatura veche care cheama o rutina noua -functioneaza (parametrul lipsa devine implicit), problema ar aparea doar invers (rutina veche -chemata cu un parametru in plus, care s-ar ignora silentios in VFP — tot fara eroare, dar fara -efect). Deci ordinea de mai jos e despre corectitudine, nu despre a evita crash-uri: - -1. **`oDateFactura.Init`** (`ofacturare_comun.prg`) — adauga `toSursa`, muta logica de citire pe - modelul din sectiunea 5. Se poate face si testa izolat: fara niciun apelant care sa paseze - `toSursa` inca, comportamentul ramane identic cu azi (fallback pe global, mereu). -2. **`factureaza`** (`ofacturare.prg`) — adauga `toSursa`, il paseaza la `Createobject("oDateFactura", - lnIdSet, tnTip, toSursa)`. Inca niciun apelant nu paseaza `toSursa` — comportament neschimbat. -3. **`oproceduri_facturare.prg`** — `facturare_contracte`/`facturare_comenzi` primesc `toSursa` si il - relaeaza. Inca niciun apelant real nu il paseaza — comportament neschimbat. -4. **Abia acum, apelantii reali**: `ocomenzi.vc2:do_factura` trimite `loComanda` explicit; - `ferestre_contracte.vc2:but_factura.Click` trimite `goContract` explicit (in ROACONTRACTE). Din - acest punct, comportamentul chiar se schimba (sursa vine din parametru, nu din citirea globalei), - dar rezultatul trebuie sa fie identic — parametrul poarta exact aceleasi campuri pe care globala - le avea. -5. **Sincronizare in celelalte 6 produse**: pasii 1-3 se copiaza caracter cu caracter (identice, - sectiunea 6), cu atentia speciala la ROAIMOB (drift de o linie). Pasul 4 pe partea de comenzi se - copiaza si el (fisierul e identic in toate). Nu exista pas 4 de propagat pe partea de contract in - afara de ROACONTRACTE (nu exista alt produs cu formular de contracte care sa scrie `goContract`). -6. **Un singur produs se poate testa complet integrat de la sine**: ROAFACTURARE (pasii 1-4, partea - de comanda). ROACONTRACTE cere o rulare separata (build propriu, sincronizat manual) pentru - pasul 5 pe partea de contract — acelasi tip de limitare semnalata deja in S2 pentru - `factureaza2`. - -## 9. Ce nu se poate testa headless - -- **Toate cele ~31+2 puncte de intrare sunt declansate din UI** (clic pe buton, `ON SELECTION BAR` - in meniu) — niciunul nu are un test headless existent, la fel ca in S2. -- **Testarea „parametrul castiga peste global” cere manipulare de sesiune**: singurul mod sa verifici - ca `toSursa` are prioritate e sa populezi manual `goComanda`/`goContract` cu o valoare **diferita** - de cea din parametru si sa confirmi ca documentul rezultat foloseste parametrul — asta cere fie - cod de test care seteaza global-ul inainte de apel (posibil headless, dar artificial fata de - fluxul real), fie o sesiune interactiva VFP cu comenzi tastate manual in fereastra de comenzi. -- **Efectul colateral al `AfterRowColChange` (`ocomenzi.vc2:2199`) nu se poate simula headless** — - harnessul `-A -T` nu declanseaza fiabil evenimente de grid (confirmat de precedentul din memorie - „Coloanele de grid nu se materializeaza headless”), deci nu se poate verifica automat ca navigarea - in grid tot suprascrie `goComanda` dupa modificare (comportament care ramane neschimbat, dar - trebuie confirmat vizual, nu presupus). -- **ROACONTRACTE nu poate fi testat din arborele de lucru ROAFACTURARE** — cere build si rulare - separata, cu propriul executabil si propria sincronizare a `ofacturare.prg`/`ofacturare_comun.prg` - /`oproceduri_facturare.prg` (nu sunt acelasi fisier fizic, sunt copii). -- **Rezultatul final** (facturare emisa cu antetul corect precompletat din comanda/contract) se - verifica doar prin continutul lui `crsfactura`/`VANZARI`/formularul de antet, vizual sau prin - interogare Oracle read-only dupa emitere — nu exista assert automat de facut. - -## 10. Riscuri si ce ramane de decis de Marius - -**Riscuri**: - -- **Cel mai probabil sa se strice**: garda `Type('loSursa') = 'O' Or Type('goContract') <> 'U'` din - sectiunea 5 — daca se scrie gresit (de exemplu `And` in loc de `Or`), fallback-ul pe global s-ar - rupe silentios pentru toti apelantii care inca nu au fost convertiti, fara nicio eroare vizibila - (`oDateFactura` ar porni pur si simplu fara precompletare). Se testeaza explicit pe cel putin un - apel neconvertit (orice `factureaza(N)` cu un singur argument, tip 1) dupa modificare. -- **Al doilea cel mai probabil**: parametrul nou nepasat trebuie verificat `Type('toSursa') = 'O'`, - nu `Type('toSursa') <> 'U'` — un parametru VFP nepasat nu e `'U'` (asta e pentru variabile - nedeclarate), ci `'L'` cu valoarea `.F.` implicita a lui `LPARAMETERS`. O garda gresita - (`<> 'U'`) ar trece mereu adevarat si ar incerca sa citeasca `.id_part` de pe `.F.`, eroare de - rulare imediata la primul apel neconvertit. **De verificat cu un test minimal inainte de a atinge - fisierele reale** — nu presupune, verifica comportamentul `Type()` pe un parametru nepasat intr-un - `.prg` de proba. -- **ROACONTRACTE, singurul apelant real din alt produs** — acelasi risc semnalat in S2: netestabil - din acest arbore de lucru, cere confirmare manuala separata. -- **Drift-ul de o linie din `ofacturare_comun.prg` in ROAIMOB** — o copiere mecanica a diff-ului ar - putea esua silentios sau reintroduce linia lipsa fara sa fie intentia — de aplicat manual acolo, - nu prin copy-paste orb. -- **`Cw3.do_actiune` (`ofundal_facturare.vc2:900`) ramane cu `goComanda = ''` redundant** dupa - conversia lui `do_factura` — nu e o problema (ramane inofensiv), dar merita mentionat explicit ca - „lasat asa, intentionat” in codul livrat, ca sa nu para o omisiune la revizuirea diff-ului. - -**Ce ramane de decis de Marius**: - -1. **Forma lui `toSursa`**: ramane duck-typing pe obiect scatter (ca azi), sau se formalizeaza o - clasa cu doua forme (`oSursaComanda`/`oSursaContract`)? Recomandare: ramane duck-typing — nu - schimba nimic functional, adauga doar o clasa noua de intretinut pentru un beneficiu marginal la - aceasta poveste. -2. **Se face si conversia partii de comanda si cea de contract in acelasi commit, sau separat?** - Ambele ating exact aceleasi trei fisiere comune (`ofacturare.prg`, `ofacturare_comun.prg`, - `oproceduri_facturare.prg`) — separarea nu reduce suprafata de regresie pe fisierele comune, doar - amana testarea reala pe ROACONTRACTE. Recomandare: **un singur commit**, cu testare manuala pe - ambele produse inainte de a-l considera gata. -3. **Se curata acum redundanta `goComanda = ''` de la `Cw3` (sectiunea „Riscuri”), sau se lasa pentru - o poveste ulterioara de curatenie?** Nu afecteaza corectitudinea — decizie de stil, nu de - comportament. -4. **Criteriul de „gata” rescris** (inlocuieste formularea din plan, linia 1861-1862): - - *Structural*: `Select-String -Path ofacturare.prg,ofacturare_comun.prg,oproceduri_facturare.prg - -Pattern 'toSursa'` gaseste parametrul in toate cele trei fisiere, in toate cele 7 produse care - au copii, cu semnatura identica. - - *Comportamental, cale convertita*: facturarea pornita din `ocomenzi.vc2:do_factura` cu - `goComanda` populat manual cu **alta** comanda decat cea trimisa prin `toSursa` produce - documentul corespunzator parametrului, nu globalei — testat manual, cu global-ul deliberat - „murdar” dintr-o navigare anterioara in grid. - - *Comportamental, cale neconvertita*: oricare din cele ~31 de apeluri care trimit doar `tnTip` - produce acelasi document ca inainte de modificare (fallback pe global, sau fara precompletare - acolo unde nu exista global de citit). - - *ROACONTRACTE*: facturarea din `but_factura.Click` cu contractul trimis explicit prin - `toSursa` produce acelasi rezultat ca azi (precompletare identica), verificat manual pe build - separat. - ---- - -## Ce nu s-a putut stabili si de ce - -- **Continutul exact al obiectului `loComanda`/`goContract` scaturat** nu a fost comparat camp cu - camp intre ce citeste azi `oDateFactura.Init` si ce ar contine un `SCATTER` facut in afara - contextului `crsComenzi`/`cContracte` curent — presupunerea (rezonabila, dar neverificata pe date) - e ca structura cursorului nu se schimba intre cele doua puncte de scatter. -- **Daca exista alte produse din suita (dincolo de cele 7 verificate) care au propriile copii ale - `ofacturare.prg`/`ofacturare_comun.prg`/`oproceduri_facturare.prg`** — cautarea s-a limitat la - produsele numite explicit in sarcina; alte ~30 de produse mentionate in S2 ca avand copii ale - `ofacturare.prg` (ROARETAIL etc.) nu au fost verificate pentru `goComanda`/`goContract` la aceasta - poveste. -- **Testarea reala pe ROACONTRACTE** nu a fost efectuata (read-only, fara rulare de cod) — doar - confirmata structura codului sursa. - -## Bug-uri semnalate, nereparate - -Niciunul nou — cercetarea a confirmat un defect de simetrie deja semnalat in plan (L.3), l-a -verificat pe cod pana la verdictul „inert azi in ROAFACTURARE” (sectiunea 2), si nu a gasit alte -probleme in fisierele atinse. diff --git a/docs/cercetare/s4_cautare_articole_server.md b/docs/cercetare/s4_cautare_articole_server.md deleted file mode 100644 index 55e8887..0000000 --- a/docs/cercetare/s4_cautare_articole_server.md +++ /dev/null @@ -1,482 +0,0 @@ -# Cercetare — proiectare S4: cautarea articolelor pe server, in linie - -Investigatie READ-ONLY pentru povestea **S4** din `docs\plan_13_unificare_formular_facturare.md:1865-1874`, -plus sectiunea `### C. Incarcarea articolelor — de ce dureaza si ce inlocuieste` (`:589-614`). -Fara editari de cod, fara `git_sync.ps1`/`txt2vcx.ps1`, fara commit. `COMUN\clase\ofacturare_comun.vc2` -si `COMUN\programe\ofacturare_editare.prg` (perimetrul #6) doar citite, nu atinse. Pe Oracle doar -`SELECT`, prin exportul `PACK_FACTURARE` de pe disc — nu s-a rulat nimic pe server. - -## Verdict (rezumat) - -Mecanismul de inlocuire propus in plan **exista deja, dar e un prototip la jumatate**: -`grd_factura.cCodMat.cboCodmat` / `cCboDenumire` (`ofacturare.vc2:16751-16797`, wiring la -`:19288-19334`) cauta pe server prin `combosql` si scriu `codmat`/`denumire`/`id_articol` in -`crsfactura` — dar **nu scriu nimic altceva**, pentru ca sursa lor (`vnom_articole`) nu are pret, -TVA, valuta, `id_pol`, `gestionabil`. Asta confirma exact ce zice planul la punctul C: partea Oracle -de facut e o **varianta filtrata pe articol a celor cinci cursoare de facturare**, nu o cautare noua. - -Descoperirea care schimba proiectarea fata de textul din plan: **`crsarticole` nu e doar sursa de -populare a gridului — e un registru al cantitatii ramase de facturat**, citit si scris de -`do_adauga_tot`, `do_sterge` si `do_scrie_factura` pentru toate tipurile cu document sursa (comanda, -aviz, contract-lista-de-preturi). Stergerea unei linii **reface** cantitatea in `crsarticole` -(`:14640-14669`), iar la scriere se face `Calculate Sum(cantitate) To lnCantitateRamasa` peste -`crsarticole` ca sa se decida daca se inchide automat comanda/avizul (`:14303-14311`, `:14334-14338`). -Asta inseamna ca **incarcarea in masa nu poate disparea pentru aceste tipuri**, indiferent de S4 — -nu doar pentru ca planul a decis sa pastreze "adauga tot", ci pentru ca bookkeeping-ul de cantitate -ramasa e cablat direct pe cursorul incarcat. S4 se aplica deci curat doar pe ramurile de **lista de -preturi** (`cursor_preturi`, plus jumatate din `cursor_contract` si `cursor_gestiune`) — vezi punctul 7. - -A doua descoperire: calea de scriere a pretului la nivel de linie (`adauga_articol_factura`, -verificata separat in S10, `docs\cercetare\s10_pret_rederivat.md`) **primeste deja pretul ca -parametru** in loc sa-l re-deriveze — exact precedentul pe care trebuie sa-l urmeze si varianta -filtrata: cauta pretul o singura data, la alegerea liniei, si il transmite mai departe neschimbat. - -## 1. Inventarul celor cinci cursoare Oracle - -Sursa: `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` (corpul -pachetului; liniile de mai jos sunt pe corp, `:2138+`, nu pe spec `:335+` — offset **+17** fata de -citatele vechi din plan, confirmat si in S10). - -### 1.1 `cursor_preturi` — lista de preturi, ramura principala (`:2138-2644`) - -``` -PROCEDURE cursor_preturi(V_DATA_CURS IN DATE, V_TIP IN NUMBER, V_ID_VALUTA IN NUMBER, - V_ID_GESTIUNE_INIT IN NUMBER, V_LUNA IN NUMBER, V_AN IN NUMBER, - V_ID_UTIL IN NUMBER, V_ID_SUCURSALA IN NUMBER, - V_CURSOR OUT cursor_facturare) -``` - -**Fara filtru pe articol** — semnatura n-are niciun parametru de cod/denumire. Ramuri pe `V_TIP` -(`CASE`, `:2159-2643`): - -| Ramura | Cand | Sursa randurilor | Observatie | -|---|---|---|---| -| `V_TIP = 45` | restaurant | `utilizatori_rol_intern` -> `politici_grupuri` -> `crm_politici_preturi` -> `crm_politici_pret_art` -> `nom_articole`, plus `curs`/`nom_valute` | fara filtru de stoc, `cantitate` fixa la 1 (`:2193`) | -| `V_TIP IN (1,2)` | factura in lei | acelasi lant de politici, plus `LEFT JOIN` pe `STOC` agregat pe gestiunile utilizatorului | `WHERE` final filtreaza pe stoc > 0 sau `RF_FACTURARE_FARA_STOC` (`:2387-2388`) | -| `V_TIP IN (5,6,10,52)` | factura in valuta | `FACT_VPRETURI_UTILIZATOR` (view precalculat, nu politici brute) + `STOC` + `CURS` | `WHERE A.ID_UTIL = V_ID_UTIL AND ((A.ID_VALUTA = V_ID_VALUTA AND A.IN_VALUTA=1) OR A.ID_POL = politica_stoc)` (`:2461-2462`) | -| `V_TIP = 7` | credit note | `FACT_VPRETURI_UTILIZATOR`, filtrat suplimentar `NVL(A.nota_discount,0)=1` (`:2538`) | doar articole de discount | -| `ELSE` (aviz, tip implicit) | orice alt tip | `FACT_VPRETURI_UTILIZATOR`, fara filtrul de valuta din ramura 5/6/10/52 | ramura cea mai generala | - -Coloane comune returnate (uniforme pe toate ramurile, ceea ce conteaza pentru mapare — punctul 6): -`id_c, id_articol, lot, serie, id_pol, id_valuta, nume_lista_preturi, discount_unitar, -discount_unitar_val, codmat, codbare, denumire, um, gestionabil, cantitate, proc_tvav, -preturi_cu_tva, curs, multiplicator, pret, pret_val, tip_valuta, nume_val` (+ `modificabil`, -`id_gestiune`, `cont` doar pe ramura restaurant). - -Apeleaza intai `initializeaza_facturare`, `completare_politica_stoc` si -**`verifica_cursuri_valute(V_DATA_CURS, V_ID_UTIL)`** (`:2149-2153`) — validare **neconditionata** -a cursurilor pentru toate valutele din listele de preturi ale utilizatorului, indiferent de articolul -cautat. Relevant pentru S4d (`-20005`). - -### 1.2 `cursor_contract` — factura/aviz pe contract (`:2646-2950`) - -``` -PROCEDURE cursor_contract(V_DATA_CURS, V_TIP, V_ID_VALUTA, V_LISTAID, V_ID_GESTIUNE_INIT, - V_LUNA, V_AN, V_ID_UTIL, V_ID_SUCURSALA, - V_ID_AGENT OUT, V_NUME_AGENT OUT, - V_CURSOR OUT cursor_facturare, V_CURSOR2 OUT cursor_facturare) -``` - -**Doua cursoare de iesire.** `V_CURSOR2` (`:2718-2938`) e specific contractului: `UNION ALL` intre -(a) articole cu `OPT_FACTURARE = 3` din `CTR_ARTICOLE` si (b) rate din `CTR_SCADENTAR` pentru -`OPT_FACTURARE IN (1,2)`, cu `id_articol = NULL` pe randurile de rata — filtrate pe -`V_LISTAID` (lista de `id_ctr`) prin `charn2collection`. La final (`:2940-2948`) **cheama -`cursor_preturi` cu aceiasi parametri** si scrie rezultatul in `V_CURSOR` — deci jumatate din -`cursor_contract` e literalmente `cursor_preturi`. - -In VFP, cele doua cursoare de iesire ajung (observat pe cod, nu documentat explicit in `goExecutor`) -in `crsarticole` (=`V_CURSOR`, lista de preturi) si `crsarticole1` (=`V_CURSOR2`, liniile contract + -rate) — vezi `ofacturare.vc2:13778` (`lcCursorStoc = Iif(poArticol.opt_facturare = 0, [crsarticole], -[crsarticole1])`) si `Destroy` (`:12804-12811`) care inchide ambele. - -**Randurile de rata nu au `id_articol`** — nu pot fi gasite printr-o cautare cod/denumire; raman -legate de gridul `crsarticole1` existent (`grd_contracte`), bounded de numarul de rate ale -contractului, nu de catalog. Vezi punctul 7. - -Valideaza cursurile valutare **doar** pentru valutele prezente in `CTR_SCADENTAR`/`CTR_ARTICOLE` ale -contractelor din `V_LISTAID` (`:2679-2716`) — spre deosebire de `cursor_preturi`, care valideaza tot -ce are utilizatorul in politici. Filtru deja ingust pe sursa, dar tot pe tot contractul, nu pe -articol. - -### 1.3 `cursor_comanda` — factura/aviz pe comanda (`:2952-3171`) - -``` -PROCEDURE cursor_comanda(V_DATA_CURS, V_TIP, V_LISTAID, V_ID_UTIL, V_CURSOR OUT cursor_facturare) -``` - -`V_LISTAID` e **un singur `id_comanda`** (`TO_NUMBER(V_LISTAID)`, `:2963` — nu o lista, desi -parametrul se numeste la fel ca la contract/avize). Doua ramuri identice ca forma (`V_TIP <= 20` = -factura, altfel aviz, `:2994-3170`), ambele pe `COMENZI_ELEMENTE` filtrat `WHERE A.ID_COMANDA = -V_ID_COMANDA` (`:3078`, `:3166`) — **deja filtrat pe un singur document sursa**, nu pe tot catalogul. -`LEFT JOIN` cu suma cantitatilor deja facturate din `VANZARI_DETALII` pe acelasi `ID_COMANDA` -(`:3060-3068`) calculeaza `cantitate` ca **ramas de facturat**, nu cantitatea comandata bruta — -exact sursa pentru bookkeeping-ul de "adauga tot" / stergere de la punctul urmator. - -### 1.4 `cursor_avize` — factura din avize (`:3703-3874`) - -``` -PROCEDURE cursor_avize(V_LISTAID, V_ID_UTIL, V_DISCOUNT OUT NUMBER, V_CURSOR OUT cursor_facturare) -``` - -`V_LISTAID` = lista de `id_vanzare` (avize sursa), separate prin virgula. O singura interogare, -fara `CASE` pe tip — agrega `VANZARI_DETALII` pe cheie compusa (articol, pol, lot, serie, discount, -TVA, gestiune, cont, pret...) si scade ce a fost deja facturat din `VANZARI_CANTITATI` -(`WHERE A.CANTITATE <> NVL(B.CANTITATE, 0)`, `:3871`) — acelasi tipar "ramas de facturat" ca la -comanda. **Nu are ramuri pe `V_TIP`** — planul o citeaza ca exemplu de "aceleasi ramuri pe tip", dar -real e cea mai simpla dintre cele cinci: un singur `SELECT`. - -### 1.5 `cursor_gestiune` — transfer intre subunitati pe lista de preturi (`:4158-4310+`) - -``` -PROCEDURE cursor_gestiune(V_DATA_CURS, V_ID_POL, V_ID_GESTIUNE, V_LUNA, V_AN, - V_ID_UTIL, V_ID_SUCURSALA, V_CURSOR OUT cursor_facturare) -``` - -Foloseste `V_ID_POL` **fix** (nu politica derivata din utilizator ca la `cursor_preturi`), pe -`STOC` filtrat `ID_GESTIUNE = V_ID_GESTIUNE` `JOIN` `CRM_POLITICI_PRET_ART WHERE ID_POL = V_ID_POL` -(`:4266-4280`) — deja restrans la o singura gestiune si o singura politica, dar tot fara filtru pe -articol; `GROUP BY` pe articol agrega stocul. - -## 2. De unde sunt executate; cine consuma `crsarticole` - -**Executia:** un singur loc, `ofacturare.prg:266-311` (`factureaza`), rutare pe `tnTip` (punct de -plecare confirmat, `Do Case` la `:266-308`) -> `goExecutor.oExecute(lcSqlCursor, [crsarticole])` -(`:310-311`). Acelasi tipar apare a doua oara in `factureaza2` (`ofacturare.prg:824+`, ramura -`frm_facturare_articole2`, prototipul). - -**Consumatorii `crsarticole` in perimetrul S4** (`frm_facturare_articole`, `ofacturare.vc2:11xxx-15300`; -lista completa via `vfp_symbols -Grep 'crsarticole\b' -CodeOnly`): - -| Metoda | Linii | Ce face cu `crsarticole` | Ramane dupa S4? | -|---|---|---|---| -| `Destroy` | `12804-12811` | inchide cursorul | da, neschimbat | -| `do_adauga_articol` | `12813-13167` | citeste **un rand ales** (`Scatter Name poArticol`), il scrie in `crsfactura` | **inlocuit** de randul intors de cautarea pe server, pe ramurile de lista de preturi — vezi punctul 7 | -| `do_adauga_tot` | `13169-13198` | `Scan` peste tot cursorul, cheama `do_adauga_articol` pe fiecare rand | **pastrat neschimbat**, dar numai pe tipurile cu document sursa | -| `do_cauta` | `13566-13593` | filtru client-side (`Set Filter`) peste cursorul deja incarcat, dupa text tastat in `txtArticole`/`txtCodmat` si politica aleasa | **dispare** pe ramurile de lista de preturi — inlocuit de `combosql` | -| `do_scrie_factura` | `14301-14338` | `Calculate Sum(cantitate) To lnCantitateRamasa` peste `crsarticole`, decide daca se inchide comanda/avizul sursa (`pnParametruAditional`) | **pastrat neschimbat** — vezi verdictul | -| `do_sterge` | `14608-14669` | la stergerea unei linii din `crsfactura`, **reface** cantitatea in `crsarticole`/`crsarticole1` (`Replace cantitate With cantitate + poArticol.cantitate`) | **pastrat neschimbat** pe tipurile cu bookkeeping; pe lista de preturi nu exista azi replace-back real (cantitatea nu scade la adaugare pe acele tipuri — stocul se verifica separat, prin `cursor_gestiuni_articol*`) | -| `do_modifica` | `13778` | alege `crsarticole` sau `crsarticole1` dupa `opt_facturare` pentru verificare stoc | pastrat, tine de gestiunea aleasa la editarea unei linii deja adaugate | -| `cb_politici_preturi.InteractiveChange` | `15647-15658` | cheama `do_cauta()` (filtru client-side) | **dispare** — pe varianta noua, schimbarea politicii devine parametru al cautarii pe server (`id_pol` in `WHERE`), nu filtru local | -| `KeyPress` (navigare grid) | `15463-15486` | navigare in cursor la taste sageata | **dispare** pe ramurile fara grid de sus | - -`frm_facturare_articole2` (prototipul, `:17xxx-18xxx`) are exact aceeasi structura pe -`do_adauga_articol` / `do_adauga_tot` / `do_scrie_factura` / `do_sterge` — nu difera in aceasta -privinta. - -**Concluzie pentru cat se poate scoate:** `do_cauta` si filtrul din `cb_politici_preturi` dispar -integral (inlocuite de cautarea pe server). `do_adauga_articol` isi schimba sursa randului (de la -`Scan` in cursorul local la randul ales de `combosql`), dar restul lui (verificare stoc, alegere -gestiune via `cursor_gestiuni_articol*`, scriere in `crsfactura`) **nu se schimba** — vezi punctul 6. -`do_adauga_tot`, `do_sterge` (partea de bookkeeping) si `do_scrie_factura` (`lnCantitateRamasa`) -**nu pot fi scoase** cat timp `crsarticole` ramane sursa lor de adevar pentru "cat a mai ramas" — -motiv suplimentar, nu doar UX, pentru care "adauga tot" ramane cablat pe incarcare in masa acolo -unde exista document sursa. - -## 3. Cat costa azi incarcarea, pe forma interogarii (nu pe timpi masurati — decizia 31) - -**`cursor_preturi` (ramurile 1/2 si generala) nu are niciun filtru pe articol** — semnatura n-are -parametru de cod/denumire (`:2138-2146`). Rezultatul e **intreg catalogul accesibil utilizatorului**: -`utilizatori_rol_intern` -> `politici_grupuri` -> `crm_politici_preturi` (toate politicile active la -data cursului) -> `crm_politici_pret_art` (toate liniile de pret ale acelor politici) -> `nom_articole`. -Numarul de randuri creste cu produsul dintre "cate politici de pret vede utilizatorul" si "cate -articole are fiecare politica" — nu cu numarul de articole cautate, care e de regula 1. - -Trei surse structurale de cost, vizibile direct in forma interogarii, nu masurate: - -1. **`LEFT JOIN` pe `STOC` agregat** (`:2359-2378`, ramura 1/2) — subquery cu `GROUP BY ID_ARTICOL` - peste tot stocul lunii curente, pe toate gestiunile la care utilizatorul are drept, recalculat la - fiecare deschidere de formular, indiferent daca utilizatorul cauta un singur articol. -2. **`FACT_VPRETURI_UTILIZATOR`** (ramurile 5/6/10/52 si 7 si aviz) e un view, nu un tabel — planul - nu detaliaza corpul lui aici (ar fi o cercetare separata), dar fiind sursa pentru "toate preturile - vizibile utilizatorului", are aceeasi forma: cost proportional cu marimea catalogului, nu cu - cautarea. -3. **`verifica_cursuri_valute`** (`:2153`, chemata necondiționat la fiecare apel) valideaza cursul - pentru **toate** valutele din politicile utilizatorului, nu doar valuta articolului cautat — - cost fix per deschidere, independent de ce se cauta. - -Concluzia structurala: interogarea de azi calculeaza raspunsul pentru "orice articol ar putea alege -utilizatorul", cand formularul are nevoie doar de "articolul pe care tocmai l-a tastat". Filtrarea pe -`id_articol`/`codmat`/`denumire` reduce fiecare din cele trei surse la un numar de randuri marginit -de cate politici de pret contin acel articol (de regula 1, rar cateva), nu de marimea catalogului. - -## 4. Proiectarea variantei filtrate, cursor cu cursor - -Toate cinci sunt proceduri PL/SQL in `PACK_FACTURARE`, apelate direct din VFP prin -`{call pack.proc(...)}` + `goExecutor.oExecute`, fara view intermediar si fara strat ORM — deci -minimul de schimbare e **acelasi tipar**: o procedura noua (sau o supraincarcare cu parametru -suplimentar) in acelasi pachet, apelata din acelasi loc (`ofacturare.vc2`, metoda nou-introdusa pe -`combosql`, nu din `ofacturare.prg`, care ramane neschimbat — el tot incarca varianta "in masa" -acolo unde ramane necesara). - -**Alegere de proiectare, nu fapt verificat:** supraincarcare (aceeasi denumire, parametru nou -opțional `V_FILTRU_COD IN VARCHAR2 DEFAULT NULL` / `V_FILTRU_DEN IN VARCHAR2 DEFAULT NULL`) e mai -sigura decat o procedura noua, pentru ca **garanteaza** aceleasi ramuri de `CASE`, acelasi `JOIN`, -aceeasi logica de rotunjire — orice divergenta viitoare intre "cursor complet" si "cursor filtrat" ar -fi un bug de sincronizare greu de prins. Cu supraincarcare, filtrul se adauga o singura data, in -`WHERE`-ul final al fiecarei ramuri, nu in logica de business. - -| Cursor | Ce se adauga | Unde (linia `WHERE`/`CASE` care primeste filtrul) | -|---|---|---| -| `cursor_preturi` | `AND (V_FILTRU_COD IS NULL OR C.CODMAT LIKE V_FILTRU_COD) AND (V_FILTRU_DEN IS NULL OR UPPER(C.DENUMIRE) LIKE UPPER(V_FILTRU_DEN))` — pe alias-ul articolului, `C` pe patru din cinci ramuri, `A` pe ramurile cu `FACT_VPRETURI_UTILIZATOR` | dupa `WHERE` existent, pe fiecare din cele 5 ramuri (`:2264`, `:2387-2388`, `:2464-2465`, `:2540-2541`, `:2640-2641`) — 5 locuri, nu unul | -| `cursor_contract` | acelasi filtru pe `V_CURSOR` (delegat catre `cursor_preturi`, gratuit); pe `V_CURSOR2` filtrul se adauga in `UNION ALL`-ul de la `:2752` (partea cu `id_articol`), **nu** pe partea de rate (`id_articol IS NULL` — nu se pot filtra pe cod, raman needitate de filtru) | `:2825` (join articol) + propagare in `WHERE`-ul de la `:2819` | -| `cursor_comanda` | filtru pe `C.CODMAT`/`C.DENUMIRE` in `WHERE A.ID_COMANDA = V_ID_COMANDA` (`:3078`, `:3166`) — **dar nu are sens sa se filtreze**: cf. punctul 7, comanda ramane pe calea "adauga tot" | de proiectat doar daca decizia de la punctul 7 se schimba | -| `cursor_avize` | idem — nu are sens, acelasi motiv | idem | -| `cursor_gestiune` | filtru pe `C.CODMAT`/`C.DENUMIRE` in interogarea de la `:4234-4237`, inainte de `GROUP BY` | `:4266-4293` | - -**Parametrii care raman identici** pe varianta filtrata: `V_DATA_CURS`, `V_TIP`, `V_ID_VALUTA`, -`V_ID_GESTIUNE_INIT`, `V_LUNA`, `V_AN`, `V_ID_UTIL`, `V_ID_SUCURSALA` — tot ce vine din `poDate` / -sesiune la deschiderea formularului, nu se recalculeaza per cautare. Doar filtrul de text e nou, plus -(pentru `cursor_preturi` in noua utilizare) eventual `V_ID_POL` daca utilizatorul a ales explicit o -lista de preturi in combo-ul `cb_politici_preturi` — azi acel combo doar filtreaza local -(`ofacturare.vc2:15647-15658`, comentat inlocuit cu `do_cauta()`); pe varianta noua devine parametru -real al interogarii. - -**Ce nu se poate verifica din cod, ramane de decis de Marius:** daca filtrul pe `codmat` foloseste -`LIKE` "incepe cu" (ca `combosql` de azi, `nCharCountBegin`/cautare incrementala) sau match exact la -alegerea din lista — combosql de azi face amandoua (tastare = "incepe cu" pe server, alegere din -lista = valoare exacta), deci varianta cea mai apropiata de comportamentul actual e sa pastreze -acelasi tipar: interogarea filtrata se cheama la fiecare tastare (ca azi, `RefreshData`), iar -valoarea finala aleasa vine din randul deja adus, nu dintr-o interogare separata "exact match". - -## 5. `combosql` — contract si exemple reale - -**Clasa:** `combosql AS combobox`, `COMUN\clase\_cb_base.vc2:519-843`. Nu e specifica facturarii — -traieste in biblioteca comuna a suitei, dar cautarea nu a gasit nicio alta utilizare, nici in -`ROAFACTURARE`, nici in `COMUNROA`/`ROAGEST` (`grep -rn "AS combosql WITH"` — zero potriviri in afara -`ofacturare.vc2:16751/16785/16861`, cele trei coloane ale gridului prototip). **Singurul exemplu real -de folosire e chiar prototipul din plan** — nu exista alt loc in suita de copiat. - -**Proprietati relevante** (`_memberdata`, `:560-575`): - -| Proprietate | Rol | -|---|---| -| `csourcesql` | `SELECT` fara `WHERE`/`ORDER BY` — baza interogarii | -| `csourcewhere` | conditia `WHERE` fixa (ex. `inactiv = 0`) | -| `csourceorder` | `ORDER BY`, implicit `pfieldactiv` | -| `pcursorname` | numele cursorului cu rezultatele | -| `pfieldactiv` | campul dupa care se cauta si se afiseaza | -| `psecondfield` | al doilea camp de cautare (optional) | -| `ncharcountbegin` | cate caractere minim inainte sa porneasca interogarea pe server | -| `llimittolist` | daca valoarea trebuie sa existe in lista | - -**Mecanismul** (`refreshdata`, `:796-830`): construieste -`SELECT ... FROM (cSourceSql) WHERE (cSourceWhere) AND (pFieldActiv LIKE ?pcValue [OR psecondfield -LIKE ?pcValue]) ORDER BY ...`, cu `?pcValue` = `cSearchString + '%'` (incepe cu) sau -`'%'+cSearchString+'%'` (contine, la Ctrl+Enter) si il executa prin -**`goExecutor.oExecuta`** (`selectdata`, `:832-840`) — acelasi executor folosit peste tot in suita -pentru apeluri Oracle, deci **niciun mecanism nou de transport**, doar un SQL nou de trimis. -`KeyPress` (`:610-776`) gestioneaza incremental tastarea: la fiecare caracter tastat re-executa -`RefreshData(1)` daca lungimea depaseste `nCharCountBegin`. - -**Contractul de legare la o coloana de grid**, dedus din prototip (`ofacturare.vc2:16751-16762` + -`:19288-19306`): - -1. se adauga ca `CurrentControl` al coloanei (`Column1.CurrentControl = "cCboDenumire"`, `:16621`); -2. `ControlSource` leaga afisarea de campul din cursorul de linii (`crsFactura.codmat`, `:16754`); -3. **evenimentul de reactie e `LostFocus`, nu `Valid` sau `InteractiveChange`** — abia la parasirea - celulei se scriu campurile in cursorul de linii, cu paza `If this.Value <> thisform.cOldValue` - (setat in `When`, `:19308-19310`) ca sa nu se rescrie fara motiv; -4. `LostFocus` de azi scrie **doar** campurile pe care le are `vnom_articole`: `codmat`, `denumire`, - `id_articol` (`:19293`) — nimic despre pret, TVA, valuta. **Aici se opreste prototipul azi**; tot - ce trebuie adaugat pentru S4 e sa inlocuiasca `crsCodmat`/`crsDenumire` (rezultatul din - `vnom_articole`) cu rezultatul cursorului Oracle filtrat de la punctul 4, care are toate coloanele, - si sa extinda `REPLACE`-ul de la `:19293`/`:19317` cu restul campurilor din tabelul de la punctul 6. - -**Ce nu ofera azi `combosql` si trebuie adaugat, nu doar copiat:** `csourcesql` e un `SELECT` -static definit in `.vcx` la design-time, nu poate primi parametrii dinamici ai documentului curent -(`poDate.zi_curs`, `poDate.tip`, `poDate.id_valuta`...) direct in proprietate. Pentru cursorul -Oracle cu 8 parametri, populate din `poDate`/sesiune, `csourcesql`/`csourcewhere` trebuie construite -dinamic in `Init` sau la schimbarea documentului (nu sunt string-uri fixe ca azi), sau — alternativa -mai simpla — `refreshdata`/`selectdata` se suprascriu punctual pe instanta din grid, ca sa apeleze -`{call pack_facturare.cursor_preturi_filtrat(...)}` in loc de `SELECT ... FROM (cSourceSql)`. A doua -varianta pastreaza restul mecanismului (`KeyPress`, incremental, `LostFocus`) neschimbat si e -schimbarea minima — de confirmat cu Marius, nu o certitudine de cod. - -## 6. Maparea camp-cu-camp - -Sursa cea mai fiabila pentru "ce completeaza azi `crsarticole` in linie" nu e cursorul Oracle brut, ci -`Gather Name poArticol Fields Like ...` din `do_adauga_articol` (`ofacturare.vc2:12945-12957`, -identic pe `frm_facturare_articole2` la `:17226+`) — acolo se vede exact ce trece din randul scanat -in `crsfactura`. - -| Camp in `crsfactura` | Vine din `crsarticole` (coloana cursorului Oracle) | Pe varianta filtrata | -|---|---|---| -| `id_articol`, `codmat`, `codbare`, `denumire`, `um` | direct din cursor | identic — filtrul e chiar pe aceste coloane | -| `pret_achizitie` | **nu** din `crsarticole` — vine din `poArtLista`/`crsartselectate` (rezultatul lui `cursor_gestiuni_articol*`, ales dupa `crsarticole`), nu din cursorul de lista de preturi | **neschimbat** — pasul e deja separat azi, ramane separat | -| `id_pol` | direct (`crsarticole.id_pol`) | identic | -| `Cont` | direct (`crsarticole.cont`, doar ramura restaurant o are explicit; pe restul vine `'371'` implicit sau din `cursor_gestiuni_articol*`) | identic pe ramurile care il au; **de verificat** pe ramurile 1/2/5/6/7/10/52/aviz — cursorul nu are `CONT` explicit in `SELECT` (`:2268-2329` etc.), deci provine din `do_alege_stoc`, nu din `crsarticole` | -| `id_gestiune` | direct doar pe ramura restaurant (`A.ID_GESTIUNE`, `:2220`); pe rest vine din alegerea de gestiune (`cursor_gestiuni_articol*`) | identic — pasul de alegere gestiune ramane neschimbat | -| `Proc_Tvav`, `pretftva`, `Pretctva`, `discountftva`(`discount_unitar`), `discountctva` | direct din cursor, plus `do_initializeaza_articol` (`:13618-13659`) care deriva `pretftva`/`pretctva`/`tva` reciproc dupa `preturi_cu_tva` | identic — logica de derivare e in VFP, nu se schimba | -| `id_valuta`, `tip_valuta`, `nume_val`, `Curs`, `multiplicator` | direct din cursor | identic | -| `gestionabil` | direct din cursor | identic (inclusiv regula de proforma, `ofacturare.prg:333-336`, care suprascrie `gestionabil=0` — ramane in `ofacturare.prg`, neschimbata) | -| `id_jtva_coloana` | direct doar pe ramurile care il au explicit in `SELECT` (aviz/contract/retur); pe `cursor_preturi` (1/2/5/6/7/10/52) **nu apare in lista de coloane** returnate (`:2264-2329`) | **de verificat cu Marius** — daca lipseste azi, `Gather ... id_jtva_coloana` scrie `NULL`/valoare implicita; filtrarea nu schimba nimic aici, dar merita clarificat inainte de implementare, nu presupus | -| `pretv_orig`, `pretd`, `id_valuta_d` | nu apar in `cursor_preturi`; vin din `poArtLista`/`crsartselectate` (alegerea de gestiune) | neschimbat | -| `taxcode`, `explicatie`, `id_lucrare_rez`, `id_part_rez`, `id_ctr`, `opt_facturare` | nu din `crsarticole` — din `poArticol`/context (`do_initializeaza_articol`, `do_alege_stoc`) | neschimbat | -| `pret_cu_tva` (flag `preturi_cu_tva`) | direct din cursor (`A.PRETURI_CU_TVA` / `D.PRETURI_CU_TVA`) | identic | - -**Concluzie pentru punctul 6 al cerintei:** nicio coloana din cele cerute explicit (pret, cota TVA, -valuta, `id_pol`, `gestionabil`, `pret_cu_tva`) nu ridica probleme — toate vin direct din cursorul -Oracle si filtrarea pe articol nu le atinge. Singurul semnal de atentie e `id_jtva_coloana`, absent -din `cursor_preturi` insusi (posibil completat in alt pas, neverificat aici — iese din perimetrul -"cele cinci cursoare", ar cere citirea intregului `do_adauga_articol`/`calculeaza_totaluri`). - -## 7. „Adauga tot" — ce ramane si de ce (nu doar decizia din plan) - -Planul spune "se pastreaza adauga tot pentru tipurile cu document sursa (comanda, aviz, contract) — -acolo setul e marginit". Cercetarea (punctul 2) arata un motiv suplimentar, mai tare decat marimea -setului: **bookkeeping-ul de cantitate ramasa** (`do_sterge`, `do_scrie_factura`) citeste si scrie -direct in `crsarticole`, deci acel cursor **trebuie sa existe incarcat in memorie** cat timp -formularul e deschis, indiferent cum alege utilizatorul liniile. - -**Vizibilitatea de azi a butonului `but_urmator_tot1`** ("adauga tot"), confirmata pe cod -(`ofacturare.vc2:15108-15248`, `Do Case poDate.tip`): - -| Tip | Vizibil "adauga tot" azi | Sursa cursorului | -|---|---|---| -| proforma (`eProforma=1`) | da (`:15113`) | `cursor_preturi` sau alt cursor dupa tip, marcat neges­tionabil | -| copiere (`lCopiere`) | da (`:15120`) | `cursor_preturi` (varianta copiere, `ofacturare.prg:464-473`) | -| `1, 5, 7, 10` — lista de preturi | **nu** | `cursor_preturi` | -| `2, 6` — contract | **nu** | `cursor_contract` | -| `3` — comanda | da (`:15150`) | `cursor_comanda` | -| `4` — avize | da (`:15166`) | `cursor_avize` | -| `21, 28, 42, 47` — aviz din comanda | da (`:15177`) | `cursor_comanda` | -| `22, 29` — aviz din lista de preturi | **nu** | `cursor_preturi` | -| `23, 41` — transfer subunitati | **nu** menționat explicit vizibil | `cursor_gestiune` | -| `25` — transfer din comanda | da (`:15215`) | `cursor_comanda` | -| `26` — aviz din contract | **nu** | `cursor_contract` | -| `8, 9` — retur | da (`:15240`) | `cursor_retur` (nu e in cele 5, iese din perimetru) | -| `24` — aviz retur | da (`:15245`) | `cursor_retur` | - -**Coincide exact** cu impartirea utila pentru S4: butonul e vizibil azi pe tipurile unde -`do_adauga_tot` are sens pentru ca setul e mic **si** bookkeeping-ul de ramas il cere -(comanda/aviz-din-comanda/retur), si e ascuns pe tipurile de lista de preturi (1/5/7/10, 22/29) si pe -contract-lista-de-preturi (2/6/26) — exact ramurile pe care cautarea filtrata inlocuieste incarcarea -in masa. **Contractul insa nu are azi "adauga tot" vizibil deloc** (nici pe partea de rate, nici pe -partea de articole) — planul semnaleaza asta separat, la S4b, ca gol de acoperit (tipurile 2, 6, 26, -52 lipsesc din conditiile de vizibilitate), nu ca ceva de pastrat. - -**Concret, ce se schimba per tip:** -- **Lista de preturi (1,5,7,10,22,29) si transfer pe lista (23,41,45,48,49):** `crsarticole` nu se - mai incarca la deschidere; `combosql` cauta filtrat; "adauga tot" ramane ascuns (ca azi — n-are - sens pe catalog intreg). -- **Comanda (3,21,25,28,42,47) si avize (4):** `crsarticole` **ramane incarcat in masa**, neschimbat; - `combosql` **nu se activeaza** pe aceste tipuri (sau, daca se activeaza pentru UX, cauta *in* - cursorul deja incarcat, nu pe server — cautare locala, nu Oracle) pentru ca bookkeeping-ul de - cantitate ramasa il cere oricum incarcat. -- **Contract (2,6,26,52):** dublu — `crsarticole` (jumatatea `cursor_preturi`) trece pe cautare - filtrata ca orice lista de preturi; `crsarticole1` (rate + articole `OPT_FACTURARE=3`) **ramane - incarcat in masa**, bounded de contract, neschimbat de S4 (randurile de rata n-au `id_articol`, - nu pot fi cautate). -- **Retur (8,9,24):** in afara celor cinci cursoare cerute (`cursor_retur`/`cursor_retur_document`), - proiectat separat la S4f — nesemnalat aici ca problema, doar exclus din perimetru. - -## 8. Interactiunea cu S4b si S10 - -**Cu S4b (bara de butoane / meniul de adaugare):** S4b proiecteaza meniul `xmenu()` "Adauga -articole" cu optiunile pe sursa (tot / alege), inclusiv acoperirea golului de pe contract (tipurile -2/6/26/52). S4 ii da continutul concret pentru ramura "cauta in linie": pe tipurile de lista de -preturi (unde S4b nu are nevoie de optiunea "tot" azi), linia de grid cu `combosql` **este** metoda -de adaugare — nu exista un dialog separat de ales. Pe tipurile cu document sursa, S4 nu schimba -nimic din ce proiecteaza S4b: meniul ramane cablat pe `do_adauga_tot`/alegere din `crsarticole` -existent. Punctul de atingere real: daca S4b decide sa afiseze si pe contract un "adauga tot" (gol -semnalat la punctul 7), acel "tot" trebuie sa opereze pe `crsarticole1` (rate+articole), nu pe -`crsarticole` (lista de preturi) — cele doua cursoare raman distincte si dupa unificare. - -**Cu S10 (pretul care nu trebuie re-derivat, `docs\cercetare\s10_pret_rederivat.md`):** verificarea -S10 arata ca `adauga_articol_factura` **primeste pretul ca parametru** (`V_PRET_ACHIZITIE_TEMP` si -restul) si nu-l recalculeaza, cu o singura exceptie tacuta: liniile de contract cu -`OPT_FACTURARE = 3`, unde `CTR_ARTICOLE.PRET_UNITAR` **suprascrie** pretul trimis, necondiționat de -sursa. Pentru S4, asta inseamna doua lucruri concrete: -1. cursorul filtrat (`cursor_preturi`/`cursor_gestiune` cu filtru pe articol) se cheama **o singura - data**, la selectarea liniei in `combosql` (`LostFocus`) — pretul obtinut atunci se scrie in - `crsfactura` si **nu se re-interogheaza** la scriere, exact ca azi pe calea `crsarticole` completa; -2. pe **contract**, indiferent daca linia a fost aleasa prin `crsarticole1` (needitat de S4) sau, - ipotetic, printr-o cautare filtrata viitoare, riscul de suprascriere tacuta descris in S10 exista - deja si S4 nu-l introduce si nu-l rezolva — il mosteneste neschimbat, pentru ca punctul de - suprascriere e in `adauga_articol_factura` (scriere), nu in cursorul de cautare (citire). - -## 9. Ordinea de executie in pasi - -1. **Pas 1 — Oracle, `cursor_preturi` filtrat.** Supraincarcare cu `V_FILTRU_COD`/`V_FILTRU_DEN` - opționale, aceleasi 5 ramuri, filtru adaugat in `WHERE`-ul fiecareia (punctul 4). *Gata cand:* - apelat cu filtru gol, cursorul e identic randuri-cu-randuri (aceleasi coloane, aceleasi valori, pe - fiecare din cele 5 ramuri de `V_TIP`) cu varianta actuala pe acelasi set de parametri — comparatie - directa pe SQL, nu pe timp. -2. **Pas 2 — Oracle, `cursor_gestiune` filtrat.** Acelasi tipar, un singur `SELECT`, mai simplu. - *Gata cand:* idem, comparatie randuri-cu-randuri cu filtru gol. -3. **Pas 3 — Oracle, `cursor_contract`, doar partea `V_CURSOR`.** Filtrul se propaga gratuit prin - apelul catre `cursor_preturi` de la `:2940-2948`; nicio schimbare suplimentara necesara daca Pasul - 1 e facut corect. *Gata cand:* `cursor_contract` cu filtru gol produce acelasi `V_CURSOR` ca - inainte de Pasul 1 (regresie, nu functie noua). -4. **Pas 4 — VFP, `combosql` pe `cCodMat`/`cCboDenumire`.** Inlocuieste `csourcesql`/`SelectData` cu - apelul la cursorul filtrat (punctul 5); extinde `LostFocus` (`:19288-19330`) sa scrie toate - campurile din tabelul de la punctul 6, nu doar `codmat`/`denumire`/`id_articol`. *Gata cand:* - alegerea unei linii in grid, pe un document de tip lista-de-preturi, produce in `crsfactura` - aceleasi valori ca alegerea aceluiasi articol azi din `crsarticole` incarcat complet — comparatie - pe date, punctul 10. -5. **Pas 5 — VFP, oprirea incarcarii in masa pe tipurile vizate.** In `ofacturare.prg:266-311`, pentru - `tnTip` din ramurile de lista de preturi (1,5,7,10,22,29,23,41,45,48,49) si jumatatea `cursor_preturi` - a contractului, `goExecutor.oExecute` nu se mai cheama la deschidere — grid-ul porneste gol, - `combosql` populeaza la cerere. *Gata cand:* deschiderea formularului pe aceste tipuri nu executa - niciun apel `cursor_preturi`/`cursor_gestiune`/`cursor_contract` (verificabil in log-ul - `goExecutor`/`WAIT WINDOW ... NOWAIT` din `combosql.selectdata`, care ramane singurul loc care - cheama Oracle pentru articole). -6. **Pas 6 — verificare "adauga tot" neatins.** Pe comanda/aviz/contract-rate, `do_adauga_tot`, - `do_sterge`, `do_scrie_factura` raman neschimbate (Pasii 1-5 nu le ating codul). *Gata cand:* - regresie manuala pe un document din comanda si unul din aviz — comportament identic cu azi. - -*Depinde de:* S3 (planul o marcheaza asa; portarea antetului trebuie sa existe inainte, ca sa aiba -`poDate` populat corect la deschiderea gridului). - -## 10. Cum se verifica „aceleasi valori ca azi" - -**Testabil pe date (nu doar pe cod), propunere concreta:** pentru fiecare tip reprezentativ (1 sau 5 -pentru lista de preturi, 2 pentru contract, 41 pentru transfer-gestiune), se alege un articol prezent -in cursorul complet de azi si se compara randul din `crsarticole` (deschidere veche, neschimbata) cu -randul intors de cursorul filtrat pe acelasi articol, aceiasi parametri (`zi_curs`, `id_valuta`, -`id_gestiune_init`, utilizator) — comparatie coloana cu coloana, in Oracle direct (doua `SELECT`-uri, -`MINUS` intre ele pe coloanele comune) sau in VFP prin doua cursoare deschise simultan. Se repeta pe -un articol cu politica de discount, unul in valuta si unul fara stoc (`RF_FACTURARE_FARA_STOC`), ca -sa acopere ramurile de `CASE` din punctul 1. - -**Ce nu se poate testa headless — capcana cunoscuta** (`grid-coloane-nu-se-materializeaza-headless`, -memorie de proiect): sub `-A -T`, `ColumnCount`/`RecordSource` ale gridului sunt artefacte, nu -reflecta ce vede operatorul. Deci **partea VFP** (alegerea din `combosql`, `LostFocus`-ul care scrie -in `crsfactura`) nu se poate verifica prin harnessul headless obisnuit — cere fie harnessul UI -vizibil existent, fie verificare directa pe cursorul `crsfactura` dupa un apel programatic la metoda -`LostFocus` (posibil, pentru ca metoda insasi nu depinde de randare, doar de `This.Value` — de -confirmat la implementare). Partea Oracle (comparatia de cursoare de mai sus) **e** testabila -headless, prin `SELECT`, fara nicio dependenta de VFP. - -## 11. Riscuri si ce ramane de decis de Marius - -- **`id_jtva_coloana` lipsa din `cursor_preturi`** (punctul 6) — de clarificat inainte de - implementare daca e completat in alt pas (nevazut aici) sau ramane `NULL` si azi; filtrarea nu - schimba comportamentul, dar nota trebuie inchisa ca sa nu para un bug nou introdus de S4. -- **Mecanismul de legare `combosql` -> cursor Oracle parametrizat dinamic** (punctul 5, ultimul - paragraf) e o alegere de implementare, nu un fapt de cod — `csourcesql` static din `.vcx` nu poate - primi parametrii de sesiune direct; solutia propusa (suprascriere punctuala `refreshdata`) e - rezonabila, dar nu e singura posibila si nu exista alt exemplu in suita de comparat. -- **Contractul ramane cu doua surse de date pe acelasi grid conceptual** (`crsarticole` filtrat + - `crsarticole1` needitat) — de confirmat ca UX-ul (doua zone: cautare + grid rate) e acceptabil, nu - doar tehnic corect. -- **`cursor_gestiune` (transfer subunitati) nu are azi buton "adauga tot" vizibil** (tabelul din - punctul 7 nu-l gaseste in `Do Case`) — de verificat separat daca tipurile 23/41 au alt mecanism de - adaugare in masa, neacoperit de cautarea in `crsarticole`/`crsarticole1` directa; nu s-a citit codul - suplimentar necesar pentru raspuns cert aici. -- **Validarea de curs valutar (`verifica_cursuri_valute`, punctul 1.1) ramane neconditionata** chiar - si pe varianta filtrata, daca se pastreaza in procedura supraincarcata — poate produce `-20005` la - prima cautare a unui articol, inainte ca utilizatorul sa fi vazut vreun rand, pe o valuta pe care - n-o foloseste azi doar pentru ca incarcarea in masa "o gasea" oricum. De discutat cu Marius daca - validarea trebuie restransa la valuta articolului cautat sau ramane globala (comportament - identic cu azi, doar declansat mai des — la fiecare cautare, nu o data la deschidere). -- **Numarul exact de apeluri Oracle pe sesiune de cautare** (un apel per tastare, cu debounce prin - `nCharCountBegin`) nu a fost masurat si nu trebuie masurat aici (decizia 31) — dar merita setat - explicit `nCharCountBegin` >= 2-3 la implementare, ca sa nu se piarda avantajul filtrarii intr-un - numar mare de interogari de o litera. - -## Handoff - -Cercetare incheiata in aceasta sesiune, fara nevoie de predare — toate cele 11 puncte cerute sunt -acoperite mai sus, cu citate `fisier:linie` verificate pe fisierul real (nu `.bak`). Niciun fisier de -cod atins, niciun `git_sync.ps1`/`txt2vcx.ps1` rulat, nicio scriere pe Oracle. diff --git a/docs/cercetare/s4_punct2_registru_cantitate_ramasa.md b/docs/cercetare/s4_punct2_registru_cantitate_ramasa.md deleted file mode 100644 index 18ef086..0000000 --- a/docs/cercetare/s4_punct2_registru_cantitate_ramasa.md +++ /dev/null @@ -1,615 +0,0 @@ -# Cercetare — proiectare S4 punctul 2: decuplarea registrului de cantitate ramasa de `crsarticole` - -Investigatie READ-ONLY pentru **punctul 2** al povestii S4 din -`docs\plan_13_unificare_formular_facturare.md:2099-2113`. Continua raportul punctului 1 -(`docs\cercetare\s4_cautare_articole_server.md`) si corectiile din `docs\cercetare\s4_puncte_deschise.md`. -Fara editari de cod, fara `git_sync.ps1`/`txt2vcx.ps1`, fara commit, fara scriere pe Oracle (numai -`SELECT`). Nu ating `COMUN\clase\ofacturare_comun.vc2` si `COMUN\programe\ofacturare_editare.prg` -(perimetrul altei sarcini). **Decizia 39 (S4 ramane integrala, se desface registrul) nu e -reargumentata** — proiectez CUM, nu DACA. - -Sursa VFP: `COMUN\clase\ofacturare.vc2` (`frm_facturare_articole`; `frm_facturare_articole2`/prototip -citit doar pentru confirmare, are aceeasi structura). Sursa Oracle: -`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` (corpul -pachetului, `versiune_db.txt` = `2026_08_09_02`). - -## Verdict (rezumat) - -**Descoperirea centrala: `crsarticole.cantitate` are azi DOUA roluri distincte, nu unul, si numai -unul din ele e "registrul de cantitate ramasa" pe care planul il numeste riscant.** - -- **Rol A — cantitate ramasa de facturat dintr-un document sursa** (doar comanda: tip - `3,21,25,28,42,47`; si avize: tip `4`). Cursoarele Oracle (`cursor_comanda`, `cursor_avize`) o - **calculeaza deja live** la deschidere ca `cantitate_document - deja_facturat`. `do_scrie_factura` - o re-suma din `crsarticole` (`Calculate Sum`) **doar ca sa decida ce sa trimita mai departe** - (`pnParametruAditional`) — dar Oracle **recalculeaza acelasi lucru independent, din tabele reale**, - in `inchide_comanda()` si `marcheaza_facturat()`, chiar in procedura care scrie factura. Cursorul - VFP e o **copie redundanta a unui calcul pe care Oracle il repeta oricum la scriere** — exact - problema "sursa unica" semnalata in misiune, si exact motivul pentru care decuplarea e posibila - fara sa piarda paritatea: mutam citirea "cat a mai ramas" din cursorul local intr-un apel Oracle - facut la momentul potrivit, nu intr-o a doua copie tinuta manual. -- **Rol B — plafon de cantitate in sesiune** (lista de preturi gestionabila `1,22,29,2`-jumatate; - transfer `23,41`; retur `8,9,24`). Decrementat/incrementat in `do_adauga_articol`/`do_modifica`/ - `do_sterge` ca sa nu lase operatorul sa adauge mai mult decat vede pe ecran, in aceeasi sesiune. - **Nu alimenteaza nicio decizie Oracle** — nu apare in niciun `Calculate Sum` in afara celor doua - locuri de la Rolul A, si Oracle nu-l citeste niciodata direct. E un plafon UI, nu un registru de - business. -- **Corectie fata de raportul punctului 1**: pasul 5 de acolo (`s4_cautare_articole_server.md:417-424`) - propune oprirea incarcarii in masa pe tipurile `23,41` (printre altele), presupunand ca ele n-au - bookkeeping de pastrat — dar **au Rol B** (confirmat pe cod, sectiunea 1 de mai jos). Punctul 1 nu - poate fi aplicat pe `23,41` fara ca punctul 2 sa acopere intai Rolul B pe aceste doua tipuri. -- **Recomandare de proiectare** (detaliata la sectiunea 4): pentru Rolul A, inlocuieste - `Select crsarticole / Calculate Sum(cantitate)` cu un apel Oracle nou, la acelasi moment din - `do_scrie_factura` (dupa `do_scrie_articole()`, cand `VANZARI_DETALII_TEMP` e deja populat), care - reface exact interogarea pe care `inchide_comanda`/`marcheaza_facturat` o repeta oricum. Pentru - Rolul B, plafonul devine un apel Oracle la cerere (aceeasi interogare care alimenteaza azi - `cursor_preturi`/`cursor_comanda`/`cursor_gestiune`, filtrata pe articol), nu o valoare tinuta in - memorie — nu mai e nevoie de sincronizare manuala pentru ca nu mai exista o a doua copie. - ---- - -## 1. Inventar exact al scrierilor/citirilor de bookkeeping - -Verificat direct pe `COMUN\clase\ofacturare.vc2` (nu pe `.bak`), linii confirmate cu `vfp_symbols.ps1`. - -### 1.1 `do_adauga_articol` — scrierea la adaugarea unei linii (`:12813-13086`) - -Dupa ce linia e scrisa in `crsfactura`, la `:13034-13051`: - -``` -Select (lcCursor) -lnRecno = Recno() -Do Case - Case gnScadereStoc = 1 And poDate.tip = 41 && aviz retur transfer catre subunitati lista pret - Replace cantitate With cantitate + poArticol.cantitate For id_c = poArticol.id_c - Go lnRecno - Case (gnScadereStoc = 1 And poDate.tip = 23) Or Inlist(poDate.tip, 3, 4, 21, 25, 28, 42, 47) - Replace cantitate With cantitate - poArticol.cantitate For id_c = poArticol.id_c - Go lnRecno - Case Inlist(poDate.tip, 8, 9, 24) && factura retur lei si valuta, aviz retur - Replace cantitate With cantitate + poArticol.cantitate For id_c = poArticol.id_c - Go lnRecno - Case poArticol.gestionabil = 1 And gnScadereStoc = 1 And ; - (Inlist(poDate.tip, 1, 22, 29) Or (poDate.tip = 2 And poArticol.opt_facturare = 0)) - Replace cantitate With IIF(cantitate - poArticol.cantitate > 0, cantitate - poArticol.cantitate, 0) For id_articol = poArticol.id_articol And gestionabil = 1 - Go lnRecno -Endcase -``` - -`lcCursor` = `crsarticole1` daca `tlContract`, altfel `crsarticole` (`:12839-12845`). - -### 1.2 `do_sterge` — refacerea la stergerea unei linii (`:14608-14677`) - -``` -Select (lcCursor) -lnRecNo = Recno() -Do Case - Case gnScadereStoc = 1 And poDate.tip = 41 - Select (lcCursor) - Replace cantitate With cantitate - poArticol.cantitate For id_c = poArticol.id_c - Case (gnScadereStoc = 1 And poDate.tip = 23) Or Inlist(poDate.tip,3,4,21,25,28,42,47) - Select (lcCursor) - Replace cantitate With cantitate + poArticol.cantitate For id_c = poArticol.id_c - Case Inlist(poDate.tip,8,9,24) - Replace cantitate With cantitate - poArticol.cantitate For id_c = poArticol.id_c - Case poArticol.id_gestiune <> - 1000 And gnScadereStoc = 1 And ; - (Inlist(poDate.tip,1,22,29) Or (poDate.tip = 2 And poArticol.opt_facturare = 0)) - Select (lcCursor) - Replace cantitate With cantitate + poArticol.cantitate For id_articol = poArticol.id_articol And gestionabil = 1 -Endcase -``` - -`lcCursor` = `crsarticole1` daca `poArticol.opt_facturare <> 0`, altfel `crsarticole` (`:14615-14619`). -**Exact inversul semnului fata de `do_adauga_articol`, pe aceleasi patru grupuri de tipuri** — simetrie -confirmata, nu presupusa. - -### 1.3 `do_modifica` — ajustarea la schimbarea cantitatii pe o linie deja adaugata (`:13746-13914`) - -Doua interactiuni distincte cu registrul, in aceeasi metoda: - -**(a) Calculul plafonului inainte de a arata dialogul** (`:13775-13798`), comentat explicit in cod -ca "plafonul de cantitate = stocul disponibil reconstituit din cursorul sursa; fara randul in cursor -ramane cantitatea liniei" (`:13775`): -``` -lnCantitateMax = poArticol.cantitate -lcCursorStoc = Iif(poArticol.opt_facturare = 0, [crsarticole], [crsarticole1]) -... -Locate For id_c = poArticol.id_c -If Found() - Do Case - Case (gnScadereStoc = 1 And poDate.tip = 23) Or Inlist(poDate.tip, 4, 21, 28, 42, 47) - lnCantitateMax = cantitate + poArticol.cantitate - Case Inlist(poDate.tip, 8, 9, 24) - lnCantitateMax = cantitate - poArticol.cantitate - Endcase -Endif -``` -Aduna inapoi ce a consumat deja linia curenta, ca sa arate operatorului plafonul real (cat mai era -disponibil **inainte** de aceasta linie), trimis ca parametru la `do_alege_stoc`/`frm_articol_factura`. - -**(b) Ajustarea delta dupa ce operatorul schimba cantitatea** (`:13887-13904`): -``` -lnDeltaCantitate = poArticol.cantitate - lnCantitateVeche -If lnDeltaCantitate <> 0 And Used(lcCursorStoc) - Do Case - Case (gnScadereStoc = 1 And poDate.tip = 23) Or Inlist(poDate.tip, 4, 21, 28, 42, 47) - Replace cantitate With cantitate - lnDeltaCantitate For id_c = poArticol.id_c - Case Inlist(poDate.tip, 8, 9, 24) - Replace cantitate With cantitate + lnDeltaCantitate For id_c = poArticol.id_c - Endcase -Endif -``` - -**Gasit, nu presupus: `do_modifica` NU ajusteaza `crsarticole` pentru tipurile `1,22,29` si -`2`-lista-jumatate** (grupul "gestionabil, plafon local" din `do_adauga_articol`/`do_sterge`) — doar -pentru grupul comanda/aviz (`4,21,23,28,42,47`) si retur (`8,9,24`). E o asimetrie **preexistenta** -in codul de azi (nu introdusa de S4): editarea cantitatii pe o linie de lista-de-preturi deja adaugata -nu recalibreaza plafonul local. Nu e de reparat aici — doar de semnalat, ca varianta noua sa nu -incerce sa reproduca o simetrie care nu exista azi. - -### 1.4 `do_adauga_tot` — enumerare, nu bookkeeping propriu (`:13169-13198`) - -``` -Select crsarticole -Scan - ... - Thisform.do_adauga_articol(.T.) - ... -Endscan -``` - -**Nu scrie direct in `crsarticole`** — cheama `do_adauga_articol` pe fiecare rand, care face -bookkeeping-ul de la 1.1. Rolul lui `crsarticole` aici e al treilea, distinct de Rolul A/B: **sursa -de enumerare** ("ce randuri exista de adaugat"), necesara oricum pentru populare (S4 punctul 1), nu -doar pentru bookkeeping. - -### 1.5 `do_scrie_factura` — citirea care alimenteaza decizia de inchidere (`:14301-14338`) - -Doua ramuri, singurele doua locuri din intregul fisier unde `Calculate Sum(cantitate)` ruleaza peste -`crsarticole` (confirmat cu grep pe tot fisierul, nicio a treia aparitie): - -``` -Case poDate.Tip = 4 - Select crsarticole - Calculate Sum(cantitate) To lnCantitateRamasa - If lnCantitateRamasa > 0 - pnFacturaRetur = 7 - amessagebox("Doriti sa se inregistreze si avizul de retur?",4+32,"Confirmare") - pnParametruAditional = 1 - Else - pnFacturaRetur = 0 - pnParametruAditional = 0 - Endif - ... -Case Inlist(poDate.Tip,3,21,25,28,42,47) - Select crsarticole - Calculate Sum(cantitate) To lnCantitateRamasa - If lnCantitateRamasa <> 0 - pnParametruAditional = 7 - amessagebox("Doriti sa se inchida comanda?",4+32,"Confirmare") - Endif - ... -``` - -`pnParametruAditional` default e `0` (`:14222`, setat inainte de `Do Case`). Pentru **orice alt tip** -(inclusiv `41`, `23`, `2/6/26/52`, `45/48/49`), ramane `0` fara sa fie calculat din `crsarticole` deloc -(exceptie: tip `48` il suprascrie cu `poDate.coeficient_k`, fara legatura cu inchiderea — `:14367-14369`). - -**Concluzie:** doar `4` si `Inlist(3,21,25,28,42,47)` sunt Rolul A. Restul tipurilor nu ating deloc -aceasta parte a lui `do_scrie_factura` — inclusiv `41`/`23`, care au totusi bookkeeping Rol B activ la -1.1-1.3. - ---- - -## 2. Semantica registrului azi - -### 2.1 Ce inseamna `cantitate` la incarcare, per grup - -Confirmat pe corpul cursoarelor Oracle (`ff_...PACK_FACTURARE.sql`): - -- **`cursor_comanda`** (`:2952-3171` per raportul punctului 1; verificat aici direct la sectiunea - "aviz"/comanda a interogarii, `:3060-3081`): coloana `cantitate` e `A.CANTITATE - NVL(D.CANTITATE, 0)` - unde `D` e `SUM(cantitate)` deja facturat din `VANZARI_DETALII` pe acelasi `id_comanda` - (`:3060-3068`), filtrat `WHERE ... SIGN(A.CANTITATE) * (A.CANTITATE - NVL(D.CANTITATE, 0)) > 0` - (`:3079`). **E deja "ramas de facturat", calculat live la deschidere** — nu cantitatea comandata - bruta. -- **`cursor_avize`** (`:3703-3874`): coloana `A.CANTITATE - NVL(D.CANTITATE, 0) AS CANTITATE` - (analog la `:3118`), plus agregarea finala (`:3820-3872`) care scade `VANZARI_CANTITATI` deja - consumat din `VANZARI_DETALII` (`WHERE A.CANTITATE <> NVL(B.CANTITATE, 0)`, `:3871`). Acelasi - tipar: "ramas", nu cantitatea avizata bruta. -- **Restul grupurilor (Rol B: `1,22,29,2`-lista, `23,41` transfer, `8,9,24` retur)**: `cantitate` - vine din `cursor_preturi`/`cursor_gestiune`, care e **stoc disponibil** (`LEFT JOIN` pe `STOC`, - raportul punctului 1 sectiunea 1.1/1.5), nu "ramas de facturat dintr-un document" — pentru ca aceste - tipuri nu au un document sursa cu cantitati de urmarit (lista de preturi n-are document sursa; - transferul/returul opereaza pe stoc, nu pe o comanda). - -### 2.2 Cine o scade si cand - -Vezi sectiunea 1: la fiecare `do_adauga_articol` (scade sau creste, dupa tip), la fiecare `do_sterge` -(inversul), si la `do_modifica` doar pentru grupul Rol A + retur (2.1(b) de mai sus). - -### 2.3 Ce se intampla la adaugare / stergere / modificare cantitate — rezumat comportamental - -| Actiune | Rol A (comanda 3/21/25/28/42/47, aviz 4) | Rol B (lista gestionabila 1/22/29/2, transfer 23/41, retur 8/9/24) | -|---|---|---| -| Adauga linie | `crsarticole.cantitate` scade (creste la retur/41) cu cantitatea adaugata | idem, plafon local | -| Sterge linie | reface exact simetric | reface exact simetric | -| Modifica cantitate pe linie existenta | ajusteaza cu delta (23/4/21/28/42/47 si 8/9/24 explicit) | **nu ajusteaza** pe 1/22/29/2-lista (asimetrie preexistenta, 1.3) | -| La scriere (`do_scrie_factura`) | **suma peste `crsarticole` decide `pnParametruAditional`** (inchidere) | **nu se suma niciodata** — nu alimenteaza nicio decizie Oracle | - ---- - -## 3. Regula de inchidere automata — exact, cu dovada Oracle - -### 3.1 Ce trimite VFP si ce face Oracle cu el - -`pnParametruAditional` merge la Oracle prin `scrie_factura_avize` (tip `4`) sau `scrie_factura2` -(toate celelalte, inclusiv comanda). Ambele cheama in interior `finalizeaza_factura(...)` -(`ff_...PACK_FACTURARE.sql:14770-14852`), care are un `CASE` unic pe `pack_facturare.ntip` (setat -intern din tipul documentului, nu parametru separat): - -```sql -CASE - WHEN V_PARAMETRU_ADITIONAL = 1 AND pack_facturare.ntip IN (3, 21, 25, 28, 42, 47) THEN - -- comanda: V_PARAMETRU_ADITIONAL este V_INCHIDERE_COMANDA - pack_facturare.inchide_comanda(); - WHEN pack_facturare.ntip = 4 THEN - -- aviz: V_PARAMETRU_ADITIONAL este V_VERIFICARE_FACTURAT - pack_facturare.scrie_cantitati_vanzari_avize; - pack_facturare.scrie_corespondente_vanzari(1); - pack_facturare.marcheaza_facturat(V_PARAMETRU_ADITIONAL); - WHEN pack_facturare.ntip = 24 THEN - pack_facturare.scrie_corespondente_vanzari(2); - pack_facturare.marcheaza_facturat(V_PARAMETRU_ADITIONAL); - WHEN pack_facturare.ntip in (2, 6, 52) THEN - pack_facturare.scrie_rate_factura(V_DATAORA); - WHEN pack_facturare.ntip in (8, 9) THEN - pack_facturare.scrie_corespondente_vanzari(3); - ELSE - dbms_output.put_line('---'); -END CASE; -``` -(`:14818-14839`) - -**`41` si `23` nu apar deloc in acest `CASE`** — cad pe `ELSE`, fara nicio actiune de inchidere. -Confirma pe cod ce arata si sectiunea 1.5: transferul intre subunitati nu are un "document sursa" de -inchis, doar un plafon de stoc (Rol B). `2/6/26/52` (contract) intra pe ramura `scrie_rate_factura`, -care scrie scadentarul de rate — **nu e o "inchidere" in sensul comanda/aviz**, e alta operatie. - -### 3.2 Comanda — `inchide_comanda()` (`:5769-5820`) - -**Ruleaza doar cand `V_PARAMETRU_ADITIONAL = 1`** — adica doar daca `crsarticole` a aratat cantitate -ramasa nenula **si** operatorul a raspuns "Da" la "Doriti sa se inchida comanda?" (`ofacturare.vc2:14336-14337`). -Cand ramane 0 (fara diferenta), `inchide_comanda()` **nu se cheama deloc** — pentru ca o comanda complet -livrata se raporteaza deja "inchisa" prin simplul fapt ca interogarea de mai jos nu mai returneaza -randuri, fara nicio actiune suplimentara. - -Ce face efectiv (`:5771-5818`): **insereaza un rand compensator** in `COMENZI_ELEMENTE`, cu -`CANTITATE = ramas` (calculat live, independent de VFP, din `NVL(C.CANTITATE,0) + NVL(B.CANTITATE,0) - -A.CANTITATE` unde `B` = deja facturat in aceasta tranzactie (`VANZARI_DETALII_TEMP`) si `C` = deja -facturat istoric (`VANZARI`/`VANZARI_DETALII`)) — asta face ca o interogare ulterioara de tip -`cursor_comanda` (aceeasi forma de `WHERE`, `SIGN(...) * (A.CANTITATE - NVL(D.CANTITATE,0)) > 0`) sa -nu mai gaseasca randuri ramase, adica sa "inchida" comanda **prin recalcul, nu printr-un flag setat**. -**Nu exista o coloana `INCHISA`/status persistat pe comanda** (cautare `INCHISA` in tot pachetul — -zero potriviri) — starea "deschisa/inchisa" e intotdeauna derivata din suma `COMENZI_ELEMENTE` vs -`VANZARI_DETALII`, nu citita dintr-un camp. - -**Important pentru paritate:** cantitatea trimisa de VFP (`pnParametruAditional`, un simplu `1`/`0`) -**nu participa la calculul cantitatii inserate** — Oracle o recalculeaza singur din tabele reale. -Rolul VFP e strict binar: "a fost cerut sa se forteze inchiderea?". - -### 3.3 Avize — `marcheaza_facturat(V_VERIFICARE)` (`:15381-15418`) - -```sql -IF V_VERIFICARE = 0 THEN - UPDATE VANZARI SET FACTURAT = 1, ID_UTILFACT = ... - WHERE ID_VANZARE IN (SELECT id_vanzare_aviz ... WHERE id_vanzare_Fact = pack_facturare.nid_vanzare AND sters=0 AND tip<>3) - AND FACTURAT = 0; -ELSE - UPDATE VANZARI SET FACTURAT = 1, ... - WHERE ID_VANZARE IN ( - SELECT id_vanzare FROM ( - SELECT a.id_vanzare, SUM(a.cantitate - NVL(b.cantitate,0)) AS ramas - FROM vanzari_detalii a LEFT JOIN (...VANZARI_CANTITATI...) b ON ... - WHERE a.id_vanzare IN (...avizele referite de aceasta factura...) - GROUP BY a.id_vanzare - ) WHERE ramas = 0 - ); -END IF; -``` - -**Aici exista un flag real persistat: `VANZARI.FACTURAT`.** `V_VERIFICARE` (= `pnParametruAditional` -trimis de VFP) alege intre doua moduri: -- `V_VERIFICARE = 0` (VFP a vazut `lnCantitateRamasa = 0`, adica a crezut ca tot ce era in avizele - referite s-a facturat): **marcheaza TOATE avizele referite ca facturate, fara re-verificare** — - increde in calculul VFP. -- `V_VERIFICARE = 1` (VFP a vazut ceva ramas): **recalculeaza per-aviz, din tabele reale** - (`vanzari_detalii`/`vanzari_cantitati`), si marcheaza **doar** avizele individuale al caror - `ramas = 0` — mai stric, pentru ca VFP nu mai e sigur. - -**Observatie relevanta pentru punctul 2 al plafonului de risc**: cand un singur `crsarticole` agrega -mai multe avize (facturare din mai multe avize deodata), suma globala poate fi 0 chiar daca un aviz -individual din grup mai are ramas si altul are exces care-l compenseaza — caz in care `V_VERIFICARE=0` -ar marca **toate** ca facturate, inclusiv cel cu ramas real. **Comportament preexistent**, nu introdus -de decuplare — dar varianta noua trebuie sa-l reproduca identic (aceeasi conditie de trigger: suma -globala peste toate liniile din tranzactia curenta, nu per-document), nu sa-l "repare" din greseala. - -### 3.4 Cine altcineva verifica remainder — `scrie_cantitati_vanzari_avize` (`:15420-15479`) - -Ruleaza **necondiționat** pe ramura `tip=4`, indiferent de `V_VERIFICARE` — scrie in `VANZARI_CANTITATI` -cate din cantitatile avizate au fost efectiv consumate de aceasta factura (mecanismul care alimenteaza -`ramas` la sectiunea 3.3). Nu depinde de `crsarticole` — foloseste `VANZARI_DETALII_TEMP` (deja -populat de `do_scrie_articole()`, apelat inaintea acestui `Do Case` din VFP, `:14264`) si tabele reale. -**Confirma independenta totala de `crsarticole` a partii Oracle** — singurul lucru pe care Oracle il -primeste de la VFP e semnalul binar `pnParametruAditional`. - ---- - -## 4. Proiectare — variante comparate - -### Varianta (a) — cursor propriu, minimal, decuplat de populare - -Un cursor separat (`crsregistru`), incarcat cu `id_c`/`id_articol` + cantitate ramasa, populat din -aceeasi interogare care alimenteaza azi `crsarticole`, dar **independent** de campurile de populare -(pret, TVA, valuta...) pe care S4 punctul 1 le muta pe cautare filtrata. - -**Ce se atinge:** aceleasi patru metode (`do_adauga_articol`, `do_sterge`, `do_modifica`, -`do_scrie_factura`) — schimba doar numele cursorului tinta, logica ramane identica. -**Ce se rupe:** nimic structural — e schimbarea minima de sintaxa. -**Cost:** mic, dar **nu rezolva problema de fond**: tot exista o a doua copie a "cat a mai ramas", -tinuta manual in memorie VFP, care trebuie sa ramana in sincron cu ce calculeaza Oracle la scriere -(sectiunea 3). E acelasi risc de azi, doar cu un cursor mai ingust. **Nu e "sursa unica"** — e aceeasi -sursa dubla, doar mai mica. - -### Varianta (b) — recalculul pe server la scriere, in loc de suma locala (RECOMANDATA pentru Rolul A) - -Inlocuieste cele doua `Select crsarticole / Calculate Sum(cantitate) To lnCantitateRamasa` din -`do_scrie_factura` (`:14303-14304`, `:14334-14335`) cu un apel Oracle nou, facut **dupa** -`Thisform.do_scrie_articole()` (`:14264`, care populeaza deja `VANZARI_DETALII_TEMP` cu exact ce e pe -cale sa fie scris) si **inainte** de `Do Case` care alege `scrie_factura_avize`/`scrie_factura2`. - -Doua proceduri Oracle noi, minimale, care **reutilizeaza exact interogarea** pe care -`inchide_comanda`/`marcheaza_facturat` o ruleaza oricum peste doua randuri mai jos in acelasi flux — -nu o interogare noua, o extragere a celei existente intr-o forma care returneaza un numar in loc sa -scrie: - -```sql --- comanda: acelasi WHERE ca inchide_comanda (:5771-5818), dar SUM in loc de INSERT -FUNCTION cantitate_ramasa_comanda(V_ID_COMANDA IN NUMBER) RETURN NUMBER IS - V_RAMAS NUMBER; -BEGIN - SELECT NVL(SUM(SIGN(A.CANTITATE) * - GREATEST(SIGN(A.CANTITATE) * (A.CANTITATE - NVL(B.CANTITATE,0) - NVL(C.CANTITATE,0)), 0)), 0) - INTO V_RAMAS - FROM COMENZI_ELEMENTE A - LEFT JOIN (SELECT ID_ARTICOL, ID_POL, ID_VALUTA, PRET, SUM(CANTITATE) AS CANTITATE - FROM VANZARI_DETALII_TEMP GROUP BY ID_ARTICOL, ID_POL, ID_VALUTA, PRET) B - ON A.ID_ARTICOL = B.ID_ARTICOL AND A.ID_POL = B.ID_POL AND A.ID_VALUTA = B.ID_VALUTA AND A.PRET = B.PRET - LEFT JOIN (... acelasi C ca in inchide_comanda, VANZARI/VANZARI_DETALII istoric ...) C ON ... - WHERE A.STERS = 0 AND A.ID_COMANDA = V_ID_COMANDA; - RETURN V_RAMAS; -END; - --- avize: acelasi calcul ca in marcheaza_facturat(V_VERIFICARE=1), dar returnat, nu aplicat -FUNCTION exista_ramas_avize(V_LISTAID IN VARCHAR2) RETURN NUMBER IS ... -``` - -**Ce se atinge:** `do_scrie_factura` (inlocuieste 4 linii VFP cu un apel Oracle + citire scalar), plus -doua functii Oracle noi in `PACK_FACTURARE`, care **extrag** logica deja scrisa in `inchide_comanda`/ -`marcheaza_facturat`, nu o duplica din nimic. `do_adauga_tot`, `do_sterge`, `do_modifica` **raman -neschimbate pentru Rolul A** — nu mai au ce sa faca, pentru ca nimic nu mai citeste suma lor. -**Ce se rupe:** niciun comportament — recalculul Oracle e **mai precis** decat suma locala (vede -concurenta: doi operatori facturand din aceeasi comanda simultan; `crsarticole` local nu vede -modificari facute de alt utilizator dupa deschiderea formularului — bug latent de azi, pe care -varianta (b) il elimina ca efect secundar, nu ca scop). -**Cost:** doua functii Oracle noi (SQL, nu logica noua — extrase din proceduri existente), un apel -suplimentar `goExecutor` in `do_scrie_factura`. -**De ce e "sursa unica":** Oracle calculeaza "cat a ramas" o singura data, in doua locuri (recalcul -inainte de scriere + recalcul in `inchide_comanda`/`marcheaza_facturat` la scriere), din **aceeasi -interogare**, niciodata dintr-o copie VFP. Nu mai exista sincronizare de mentinut, pentru ca nu mai -exista a doua stare. - -### Varianta (c) — plafon Rol B recalculat la cerere, nu tinut in memorie (RECOMANDATA pentru Rolul B) - -Pentru grupul Rol B (`1,22,29,2`-lista, `23,41`, `8,9,24`), plafonul din `do_modifica` -(`lnCantitateMax`, sectiunea 1.3(a)) si decrementul din `do_adauga_articol`/`do_sterge` (sectiunea -1.1/1.2) devin un apel Oracle facut **la fiecare adaugare/editare**, in loc de o valoare tinuta in -`crsarticole` — interogare filtrata pe `id_articol`, aceeasi forma ca `cursor_preturi`/ -`cursor_gestiune` filtrate (deja proiectate la S4 punctul 1, sectiunea 4), minus stocul deja rezervat -in sesiunea curenta (calculabil din `crsfactura` insusi, sumand liniile deja adaugate cu acelasi -`id_articol` — cursor local, dar de linii **efectiv adaugate**, nu de "cat mai era", deci nu mai poate -diverge de continutul real al facturii in curs). - -**Ce se atinge:** `do_adauga_articol` (:13034-13051 dispare, inlocuit cu un apel la cerere din -`do_alege_stoc`, care oricum face verificare de stoc pe server — de confirmat la implementare daca -verificarea existenta acopera deja acest plafon sau trebuie extinsa), `do_sterge` (:14640-14669 -Rol B dispare, ramane doar Rolul A), `do_modifica` (:13775-13798 plafonul se cere pe server, nu se -reconstituie din cursor). -**Ce se rupe:** **de verificat cu Marius** — plafonul Rol B azi e un avertisment soft (`do_alege_stoc` -primeste `lnCantitateMax` ca parametru, dar verificarea reala de stoc la comitere ramane oricum in -Oracle, la `adauga_articol_factura`/scriere; codul citit aici nu arata un blocaj hard bazat pe -`crsarticole` — de confirmat explicit, nu presupus, ca UX-ul de azi (mesaj/limitare la alegerea -cantitatii) nu depinde de faptul ca plafonul persista *in memorie* intre doua adaugari ale *aceluiasi* -articol in aceeasi sesiune, ci doar de valoarea citita la momentul respectiv). -**Cost:** mediu — mai multe interogari Oracle mici (una per adaugare/editare de linie gestionabila), -in loc de aritmetica locala. Justificat de acelasi motiv ca varianta (b): elimina a doua copie. - -### Comparatie si recomandare finala - -| | (a) cursor propriu minimal | (b) recalcul server (Rol A) | (c) plafon la cerere (Rol B) | -|---|---|---|---| -| E "sursa unica"? | Nu — copie mai mica, tot manuala | **Da** | **Da** | -| Risc de divergenta fata de Oracle | Identic cu azi | Eliminat (Oracle citeste Oracle) | Eliminat | -| Cost implementare | Mic | Mic-mediu (2 functii Oracle) | Mediu (mai multe roundtrip-uri) | -| Rezolva corectia sectiunii 0 (23/41 blocheaza punctul 1) | Nu direct | N/A (23/41 nu sunt Rol A) | **Da** | - -**Recomandare: (b) pentru Rolul A, (c) pentru Rolul B.** Impreuna elimina complet nevoia de a incarca -`crsarticole` in masa pentru bookkeeping — incarcarea ramasa (daca ramane) e strict pentru -**enumerare** la "adauga tot" (sectiunea 1.4), care e un scop diferit si mai ingust (nu mai trebuie sa -tina sincron o cantitate, doar sa listeze randuri candidate o singura data, la deschidere). - ---- - -## 5. Cazul limita: adauga si sterge inainte de salvare - -**Azi:** `do_adauga_articol` decrementeaza `crsarticole`/`crsarticole1` (Rol A si/sau B, dupa tip); -`do_sterge` reface exact simetric, inainte de orice salvare (sectiunile 1.1-1.2 sunt perechi -simetrice pe fiecare grup de tipuri). Suma finala din `crsarticole` la momentul `do_scrie_factura` -reflecta corect doar liniile **ramase** in `crsfactura`, indiferent cate au fost adaugate si sterse -intre timp — pentru ca fiecare stergere anuleaza exact adaugarea corespunzatoare. - -**In varianta recomandata (b+c):** cazul limita devine **trivial**, nu doar acoperit — pentru ca nu -mai exista o stare intermediara de sincronizat. Rolul A: `do_scrie_factura` calculeaza remainder-ul -o singura data, la scriere, din `VANZARI_DETALII_TEMP` care contine **exact** liniile ramase in -`crsfactura` la acel moment (populat de `do_scrie_articole()` chiar inainte, `Scan` peste `crsfactura` -curent — sectiunea 4 varianta (b)) — liniile adaugate-si-sterse nu ajung niciodata in -`VANZARI_DETALII_TEMP`, deci nu influenteaza calculul, fara nicio actiune suplimentara. Rolul B: -plafonul la fiecare adaugare se cere din nou pe server, minus ce e deja in `crsfactura` in acel -moment — o stergere anterioara pur si simplu nu mai apare in acea suma, fara "refacere" explicita. -**Cazul limita nu mai e un caz special de tratat — e comportamentul implicit al oricarei citiri facute -din starea curenta, in loc de dintr-un contor tinut manual.** - ---- - -## 6. Per tip de document - -| Tip(uri) | Rol azi | Ce se schimba in varianta recomandata | -|---|---|---| -| **Comanda** `3,21,25,28,42,47` | Rol A (inchidere automata) | `do_scrie_factura` cheama functia Oracle noua (4b) in loc de `Calculate Sum`; `do_adauga_tot`/`do_sterge` raman (enumerare + Rol B nu se aplica pe comanda insasi, doar pe articolele ei individuale daca sunt gestionabile — **de verificat**, comanda nu apare in niciuna din listele Rol B de la sectiunea 1, deci pare curatata deja) | -| **Avize** `4` | Rol A (marcheaza_facturat) | idem, functia Oracle pentru avize (4b) | -| **Contract** `2,6,26,52` | Jumatate `crsarticole` (delegat la `cursor_preturi`, per raportul punctului 1) e Rol B doar pentru `poDate.tip=2 And opt_facturare=0`; restul (`crsarticole1`, rate+articole `OPT_FACTURARE=3`) nu are bookkeeping in sectiunea 1 (needitat de cautare, ramane cum e azi, per raportul punctului 1 sectiunea 7) | Rolul B pe jumatatea `opt_facturare=0` trece pe varianta (c); `26,52` nu apar in niciuna din listele Rol B/A gasite in cod — **de verificat separat, posibil fara bookkeeping deloc pe aceste doua**, nesemnalat ca atare in codul citit aici | -| **Transfer** `41` (standard); `23` (doar prototip, standard il trateaza ca lista de preturi) | Rol B pur (niciun Rol A — confirmat sectiunea 3.1, `41`/`23` cad pe `ELSE` in `finalizeaza_factura`) | Trece integral pe varianta (c); **aceasta e corectia care debloca pasul 5 al punctului 1** — fara ea, oprirea incarcarii in masa pe `23,41` (propusa acolo) ar sparge plafonul de stoc existent | -| **Lista de preturi** `1,5,7,10,22,29` | Rol B doar pentru articole gestionabile (`1,22,29`; `5,7,10` nu apar in Do Case-urile Rol B — **fara bookkeeping**, confirmat pe cod) | `1,22,29` trec pe varianta (c); `5,7,10` nu au nimic de decuplat, deja curatate | -| **Retur** `8,9,24` | Rol B (inversul directiei fata de restul) | Varianta (c), simetric | -| **Restaurant `45`, K `48,49`** | Nu ating Rol A/B (in afara `Do Case`-urilor gasite; `45` explicit exclus la `poArticol.gestionabil=0 Or gnScadereStoc=0 Or poDate.tip=45` in ambele metode, deci ocoleste bookkeeping-ul indiferent de gestionabilitate) | Fara schimbare — deja fara bookkeeping de decuplat | - -**Tipuri semnalate ca neclare, de stabilit separat, nu presupuse aici:** `26` (aviz din contract) si -`52` (contract in valuta/alt subtip) nu apar explicit in niciuna din listele Rol A/B din sectiunea 1 — -codul citit in aceasta sesiune nu confirma nici prezenta, nici absenta bookkeeping-ului pe ele -specific; tratamentul lor pare sa urmeze grupul `2,6` din `Inlist`-urile care le includ, dar niciun -`Do Case` din sectiunea 1 nu le mentioneaza individual in afara de includerea in `Inlist(poDate.tip, -2, 6, 52)` la `do_scrie_articole` (rate) — nu la bookkeeping-ul de `crsarticole`. - ---- - -## 7. Pasi de implementare, ordonati - -1. **Pas 1 — Oracle: extrage functiile de recalcul remainder** (`cantitate_ramasa_comanda`, - `exista_ramas_avize`) din logica deja scrisa in `inchide_comanda`/`marcheaza_facturat`, fara sa - modifice acele proceduri. *Gata cand:* apelate manual cu parametrii unei comenzi/unui grup de avize - cunoscute, valoarea returnata coincide cu suma pe care `Calculate Sum(cantitate)` din VFP o - calculeaza azi peste `crsarticole`, pe acelasi document, in aceeasi stare (comparatie directa, - inainte de orice alta schimbare). -2. **Pas 2 — VFP: `do_scrie_factura` cheama functiile noi in loc de `Calculate Sum`** (sectiunea 4b), - pastrand identic restul logicii (`pnFacturaRetur`, mesajele de confirmare, `pnParametruAditional`). - *Gata cand:* pe un document de comanda si unul de aviz, cu acelasi scenariu (facturare partiala), - dialogul de confirmare apare in acelasi moment si cu acelasi rezultat ca inainte de Pas 2 — regresie - manuala, comparatie inainte/dupa pe acelasi document. -3. **Pas 3 — Oracle: functie de plafon Rol B filtrata pe articol**, reutilizand `WHERE`-ul din - `cursor_preturi`/`cursor_gestiune` (deja proiectat la S4 punctul 1, sectiunea 4), minus ce e deja in - `crsfactura` pentru acelasi articol. *Gata cand:* pe un articol gestionabil cunoscut, valoarea - returnata coincide cu `crsarticole.cantitate` de azi, la aceeasi stare a sesiunii (inainte de orice - adaugare). -4. **Pas 4 — VFP: `do_adauga_articol`/`do_sterge`/`do_modifica` folosesc plafonul cerut pe server** - pentru grupul Rol B, in loc de `Replace cantitate` pe `crsarticole` (sectiunea 4c). *Gata cand:* - cazul limita de la sectiunea 5 (adauga-apoi-sterge inainte de salvare) produce acelasi plafon - disponibil ca azi, verificat manual pe un articol cu stoc limitat. -5. **Pas 5 — activarea pasului 5 al punctului 1 pe `23`/`41`** (oprirea incarcarii in masa), acum ca - Pasul 4 a mutat Rolul B in afara lui `crsarticole`. *Gata cand:* deschiderea formularului pe tip - `41` (si `23` pe prototip) nu mai executa `cursor_gestiune`/`cursor_preturi` la deschidere, dar - adaugarea unui articol tot respecta plafonul de stoc (verificat manual). -6. **Pas 6 — curatare: `crsarticole` ramane incarcat doar unde e nevoie de enumerare** (comanda, aviz, - contract-rate — pentru "adauga tot" existent), fara bookkeeping legat de el. *Gata cand:* grep pe - `ofacturare.vc2` pentru `Replace cantitate.*crsarticole` (in afara populare) nu mai gaseste - potriviri in `do_adauga_articol`/`do_sterge`/`do_modifica`. -7. **Pas 7 — verificare paritate finala** (sectiunea 8), pe toate tipurile cu document sursa. - -*Depinde de:* S4 punctul 1 (Pasii 1-4 din PROIECTAREA acelui punct trebuie sa existe, dar Pasii 1-4 -**de aici** pot fi facuti independent, inaintea sau in paralel cu populare — nu depind de cautarea -filtrata, doar de `crsarticole`/`crsfactura` existente azi). Pasul 5 de aici depinde explicit de Pasul -4 de aici (nu poate porni inaintea lui). - ---- - -## 8. Cum se verifica paritatea inchiderii automate - -**Comanda** (nu exista flag `INCHISA` persistat — cautat explicit in tot pachetul, zero potriviri; -starea se deriva din `COMENZI_ELEMENTE` vs `VANZARI_DETALII`): - -```sql --- inainte de facturare partiala + inchidere fortata (flux vechi vs nou, aceeasi comanda X) -{call pack_facturare.cursor_comanda(V_DATA_CURS, V_TIP, 'X', V_ID_UTIL, :cursor)} --- numara randurile ramase (trebuie sa fie 0 dupa inchidere fortata, identic pe ambele fluxuri) -SELECT COUNT(*) FROM (...continutul cursorului de mai sus...); - --- verificare directa a compensatiei inserate de inchide_comanda -SELECT ID_ARTICOL, ID_POL, CANTITATE FROM COMENZI_ELEMENTE - WHERE ID_COMANDA = :X ORDER BY ID_COMANDA_ELEMENT DESC; -- randul nou trebuie sa fie identic pe ambele fluxuri (aceeasi cantitate compensatorie) -``` - -**Avize** (flag real: `VANZARI.FACTURAT`): - -```sql -SELECT ID_VANZARE, FACTURAT, ID_UTILFACT FROM VANZARI - WHERE ID_VANZARE IN (:lista_avize_test) - ORDER BY ID_VANZARE; --- rulat dupa fiecare flux (vechi, apoi nou, pe date de test resetate identic), FACTURAT trebuie sa coincida rand cu rand -``` - -**Protocol de comparatie**, aplicabil pe fiecare tip cu document sursa (comanda: alege una din -`3,21,25,28,42,47`; avize: `4`): -1. reseteaza datele de test la aceeasi stare (aceeasi comanda/aviz, aceleasi cantitati ramase); -2. factureaza partial pe fluxul **vechi** (cod actual), noteaza rezultatul interogarilor de mai sus; -3. reseteaza din nou la aceeasi stare initiala; -4. factureaza identic (aceleasi linii, aceleasi cantitati) pe fluxul **nou** (dupa Pasii 1-2 din - sectiunea 7), noteaza acelasi rezultat; -5. compara — trebuie sa fie identic, inclusiv pe cazul limita de la sectiunea 5 (adauga-si-sterge - inainte de salvare) si pe cazul avizelor multiple agregate intr-o singura factura (sectiunea 3.3, - riscul de marcare in bloc). - -**Zero cazuri testate azi nu e dovada** (memorie de proiect) — protocolul de mai sus cere minim un -caz per tip din lista, plus cazul limita, nu doar "a mers o data". - ---- - -## 9. Riscuri si ce ramane de decis de Marius - -- **Corectia sectiunii 0/6**: pasul 5 al proiectarii punctului 1 (`s4_cautare_articole_server.md:417-424`) - presupune ca `23,41` n-au bookkeeping de pastrat — gasit aici ca au Rol B activ. **De confirmat cu - Marius daca punctul 1 se re-deschide pentru aceasta corectie sau ramane cum e, cu mentiunea ca - aplicarea pe `23,41` asteapta finalizarea punctului 2** (asa cum recomand la Pasul 5, sectiunea 7). -- **Asimetria `do_modifica` fata de `do_adauga_articol`/`do_sterge`** (sectiunea 1.3, grupul - `1,22,29,2`-lista nu se ajusteaza la editarea cantitatii unei linii existente) — **preexistenta**, - nu introdusa de decuplare. De decis daca varianta noua (Rol B pe server, sectiunea 4c) trebuie sa - reproduca exact aceasta asimetrie (plafonul nu se recalculeaza corect la editare pe aceste tipuri, - ca azi) sau sa o corecteze ca efect secundar al recalcularii la cerere (care, prin natura ei, ar - elimina automat asimetria — orice cerere de plafon citeste starea curenta, indiferent daca a fost - o adaugare sau o editare). **Recomandare: lasat sa se corecteze de la sine** (comportament mai - corect, cost zero suplimentar, dar semnaleaza explicit ca S4 schimba acest comportament punctual, - nu doar "decupleaza" — de mentionat in changelog daca se alege aceasta cale). -- **Riscul de concurenta multi-utilizator, semnalat ca beneficiu la sectiunea 4b, dar nediscutat cu - Marius**: varianta recomandata face `crsarticole` local sa nu mai poata diverge de Oracle *la - scriere*, dar tot poate divarge *in timpul editarii* (doi operatori pe aceeasi comanda, unul - vede plafonul invechit pana la urmatoarea lui adaugare/editare). Nu e un risc nou introdus — e - identic cu azi pe cursorul static incarcat o data la deschidere — dar varianta (c) il reduce (cere - plafonul proaspat la fiecare adaugare, nu o singura data la deschidere), fara sa-l elimine complet - (ramane fereastra intre "am cerut plafonul" si "am scris linia"). De mentionat ca imbunatatire, nu - garantie. -- **Cont Rol B pe contract `26,52`**: nu s-a gasit dovada nici de prezenta, nici de absenta bookkeeping - pe aceste doua tipuri in codul citit (sectiunea 6) — de verificat separat inainte de a le include in - Pasul 3/4 al implementarii, nu de presupus ca urmeaza grupul `2,6`. -- **Functiile Oracle noi (Pas 1, sectiunea 7) sunt o extragere, nu o duplicare** — dar tot inseamna cod - PL/SQL nou in `PACK_FACTURARE`, care trebuie revizuit separat de cineva familiar cu pachetul - (schema exacta a `JOIN`-urilor din `inchide_comanda`, reprodusa aici din citire, nu din executie - reala pe Oracle — verificarea Pasului 1 din sectiunea 7 e obligatorie inainte de a continua). - -## Handoff - -Cercetare incheiata in aceasta sesiune. Toate cele 9 puncte cerute sunt acoperite, cu citate -`fisier:linie` verificate direct pe fisierele reale (`ofacturare.vc2`, nu `.bak`; corpul -`PACK_FACTURARE.sql`, nu spec-ul comentat). Descoperirea centrala (Rol A vs Rol B, si corectia asupra -pasului 5 al punctului 1 pentru `23`/`41`) nu era vizibila din raportul punctului 1 — acela trata -`crsarticole` ca un singur registru omogen; aici s-a aratat ca sunt doua mecanisme cu scopuri -diferite, unul (Rol A) recalculat oricum de Oracle la scriere si deci usor de mutat pe server fara -pierdere de paritate, celalalt (Rol B) un plafon UI care nu alimenteaza nicio decizie de business. - -Niciun fisier de cod atins, niciun `git_sync.ps1`/`txt2vcx.ps1` rulat, nicio scriere pe Oracle (doar -`Read`/`Select-String`/`grep` pe fisiere de pe disc). diff --git a/docs/cercetare/s4_puncte_deschise.md b/docs/cercetare/s4_puncte_deschise.md deleted file mode 100644 index 7faacd5..0000000 --- a/docs/cercetare/s4_puncte_deschise.md +++ /dev/null @@ -1,246 +0,0 @@ -# Cercetare — doua puncte deschise inainte de implementarea S4 - -Investigatie READ-ONLY, doua intrebari inchise din `docs\plan_13_unificare_formular_facturare.md`, -`#### S4`, sectiunea "De inchis inainte de implementare" (`:2092-2095`). Fara editari de cod, fara -`git_sync.ps1`/`txt2vcx.ps1`, fara commit, fara scriere pe Oracle (numai `SELECT`). - -Status: **GATA**. Ambele intrebari au raspuns confirmat pe cod, cu `fisier:linie`. Amandoua -raspunsurile de baza confirma "nu schimba nimic", dar fiecare are o rezerva concreta, nu triviala, -de pus in proiectarea S4 (nu doar de bifat). - -Sursa Oracle: `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` -(17217 linii — liniile citate mai jos sunt verificate direct pe acest fisier, corpul pachetului). -Sursa VFP: `COMUN\clase\ofacturare.vc2`, `COMUN\programe\ofacturare.prg` (perimetrul `frm_facturare_articole` -/ `factureaza`; `frm_facturare_articole2` / `factureaza2` citite doar pentru comparatie, nu atinse). - ---- - -## Intrebarea 1 — `id_jtva_coloana` lipseste din `cursor_preturi`? - -### 1.1 Ce e si cine o consuma - -`id_jtva_coloana` identifica randul din `JTVA_COLOANE` (explicatia/coloana de TVA folosita la -raportare si SAFT) asociat unei linii de factura. E scris pe fiecare linie in `VANZARI_DETALII_TEMP` -(Oracle, prin `adauga_articol_factura`) si citit inapoi de UI-ul VFP pentru dialogul de "explicatie -TVA" (`Cb_explicatie_Tva`, `frm_articol_factura`). - -### 1.2 Chiar lipseste din `cursor_preturi`? — confirmat pe SQL - -Toate cele **cinci ramuri** ale `cursor_preturi` (`WHEN V_TIP = 45`, `WHEN V_TIP IN (1,2)`, -`WHEN V_TIP IN (5,6,10,52)`, `WHEN V_TIP = 7`, `ELSE` aviz — corpul la -`ff_...PACK_FACTURARE.sql:2138-2644`) au liste de coloane complete verificate direct — **niciuna nu -returneaza `id_jtva_coloana`** (`:2163-2221`, `:2268-2329`, `:2394-2427`, `:2470-2504`, `:2545-2603`). -`cursor_facturare` e un `REF CURSOR` slab tipat (`:28`), deci lista de coloane a fiecarei ramuri e -literal ce se vede in `SELECT`, fara camp implicit. - -Acelasi lucru e adevarat si pentru **`cursor_gestiune`** (`:4158-4347`, coloanele la `:4207-4343`) — -relevant pentru intrebarea 2, tipul 41. `cursor_contract` (`:2646-2950`) delega `V_CURSOR` la -`cursor_preturi` (deci mosteneste lipsa), iar `V_CURSOR2` (rate + articole `OPT_FACTURARE=3`, -`:2718-2938`) **de asemenea nu are `id_jtva_coloana`** in niciuna din cele doua ramuri `UNION ALL`. - -Prin contrast, **`cursor_avize`** (`:3703-3874`) **are** `A.ID_JTVA_COLOANA` explicit (`:3748`, -plus `GROUP BY`-urile de la `:3781`, `:3804`, `:3822`, `:3843`) si `cursor_comanda` (`:2952-3171`) -**nu are**, pe niciuna din cele doua ramuri (factura/aviz, `:2994-3170`). Deci impartirea reala e: -*are* — doar `cursor_avize`; *nu are* — `cursor_preturi`, `cursor_gestiune`, `cursor_contract` -(ambele cursoare), `cursor_comanda`. - -### 1.3 E completat in alt pas, sau ramane gol? — DA, e completat, printr-un mecanism in doi pasi - -Pasul 1 — **valoare implicita, nu `NULL`**. `do_initializeaza_articol` -(`ofacturare.vc2:13681-13683`, identic la `:17896-17897` pe prototip): -``` -If Type('toArticol.id_jtva_coloana') = 'U' - AddProperty(toArticol,'id_jtva_coloana',0) -Endif -``` -Daca `Scatter Name poArticol` dintr-un cursor fara coloana `id_jtva_coloana` produce un obiect fara -acea proprietate (`Type(...) = 'U'`), se adauga cu valoarea **`0`** (numeric, nu `NULL`). - -Pasul 2 — **derivare reala din `proc_tvav`, neconditionata**. `frm_articol_factura.Init` -(`ofacturare.vc2:2344-2371`) suprascrie *intotdeauna* `id_jtva_coloana`, indiferent de ce a venit din -cursor: -``` -Select jtva_coloane -If !Isnull(poArticol.proc_tvav) - Set Filter To cota_tva = poArticol.proc_tvav * 100 - 100 - ... - poArticol.id_jtva_coloana = id_jtva_coloana -Else - ... - poArticol.proc_tvav = (cota_tva + 100)/100 - poArticol.id_jtva_coloana = id_jtva_coloana -EndIf -``` -`proc_tvav` **este** returnat de toate cele cinci ramuri ale `cursor_preturi` (coloana comuna, -confirmata la 1.2), deci lookup-ul are mereu ce derivea, indiferent daca `id_jtva_coloana` a lipsit -din cursorul sursa. - -Acest `Init` ruleaza pentru **orice** linie adaugata prin `do_adauga_articol` -(`ofacturare.vc2:12813-13167`), pe ambele ramuri ale `Do Case` de la `:12870-12896`: -- articol negestionabil / `gnScadereStoc=0` / restaurant (`tip=45`) → - `Createobject("frm_articol_factura", ...)` direct (`:12873`); -- articol gestionabil (`Otherwise`, `:12881-12895`) → `do_alege_stoc` → - `Createobject("frm_articol_gest_factura", ...)` (`:13353`/`:13361`/`:13375`) — clasa care - **mosteneste** `frm_articol_factura` (`vfp_symbols -Class`: `frm_articol_gest_factura -> - frm_articol_factura -> _frmbase -> _form`) si al carei `Init` (`:3986-3989`) cheama - `DoDefault(tnCantitate,tlAscunde)` — deci ruleaza acelasi `Init` de la 2315-2371, cu aceeasi - derivare. - -**Confirmare ca derivarea chiar functioneaza si nu doar exista in cod:** `do_alege_stoc` -(`:13330-13340`) avea, intr-o versiune anterioara (comentata, `v 2.2.18`), un scurtcircuit care -seta direct `id_jtva_coloana` fara sa mai arate formularul cand exista o singura optiune de TVA; -azi acel scurtcircuit e dezactivat si se creeaza intotdeauna obiectul `frm_articol_gest_factura` -(deci `Init` ruleaza mereu, nu doar in cazuri rare). - -Safety-net-ul `Case Empty(poArticol.id_jtva_coloana)` din `inainte_de_do_termin` -(`:2205-2208`, mostenit si de `frm_articol_gest_factura`) e o verificare reziduala pentru cazul in -care nici `proc_tvav` n-are match in `jtva_coloane` — nu dovada ca `id_jtva_coloana` ramane gol azi -pe fluxul normal. - -### 1.4 Concluzia care conteaza — cu o rezerva reala, nu ipotetica - -**Pe fluxul de azi (`do_adauga_articol`), lipsa lui `id_jtva_coloana` din `cursor_preturi`/ -`cursor_gestiune` NU e un bug** — e completat corect, de doua ori (default 0, apoi derivat real din -`proc_tvav`), pe ambele ramuri (gestionabil/negestionabil), inainte ca linia sa ajunga in -`crsfactura` (`Gather ... id_jtva_coloana ...`, `:12945-12950`/`:12992-12995`) si, de acolo, in -Oracle (`do_scrie_articole` → `adauga_articol_factura(...)`, `Alltrim(Str(poArt.id_jtva_coloana))`, -`:14085`). - -**Rezerva: raspunsul "filtrarea nu schimba nimic" e adevarat DOAR daca implementarea S4 pastreaza -trecerea prin `do_adauga_articol`/`frm_articol_factura.Init` pentru linia aleasa.** Cercetarea -anterioara (`docs\cercetare\s4_cautare_articole_server.md`, sectiunea 5) arata ca **singurul exemplu -real existent de cautare-pe-server** (`combosql`, `grd_factura.cCodMat.cboCodmat.LostFocus`, -`ofacturare.vc2:19288-19306`) **nu trece prin `do_adauga_articol` deloc** — face `REPLACE` direct in -`crsFactura`: -``` -REPLACE codmat WITH crsCodMat.codmat, denumire WITH crsCodmat.denumire, id_articol WITH crsCodmat.id_articol IN crsFactura -``` -Acelasi tipar identic pe coloana `cDenumire` (`:19312-19317`). Daca S4 extinde acest `REPLACE` cu -restul campurilor din cursorul filtrat — asa cum recomanda raportul anterior la sectiunea 5, punctul -4 ("sa extinda REPLACE-ul... cu restul campurilor") — **`id_jtva_coloana` nu mai trece prin niciun -`do_initializeaza_articol`/`frm_articol_factura.Init`**, pentru ca acel `REPLACE` nu creeaza obiectul -`poArticol` si nu instantiaza formularul. Cursorul filtrat tot n-are `id_jtva_coloana` (varianta -filtrata a `cursor_preturi` pastreaza aceeasi lista de coloane — vezi -`s4_cautare_articole_server.md:220`), deci randul din `crsFactura` ar ramane cu valoarea implicita -de camp (`Append Blank`, nu exista un `REPLACE` explicit pe acea coloana) — **fara nicio derivare din -`proc_tvav`**. - -La scriere, `adauga_articol_factura` (Oracle) cauta `ID_JTVA_COLOANA = V_ID_JTVA_COLOANA` in -`JTVA_COLOANE` pe ramura `ELSE` a `CASE`-ului sau (`ff_...PACK_FACTURARE.sql:5187-5198` — ramura care -se aplica exact tipurilor de lista de preturi 1/5/7/10/22/29, tipurile de transfer 23/45/48/49 si -altele care nu intra in ramurile speciale comanda/aviz/restaurant/contract-cu-pret-fix) si, daca nu -gaseste o potrivire, **arunca `RAISE_APPLICATION_ERROR(-20000, 'Nu a fost gasita cota de TVA! -(FACT-013 : ...)')`**. O valoare implicita de camp (tipic `0`, posibil `NULL` dupa `Append Blank`, -de verificat pe structura reala a `crsfactura`) ar produce fie acest -20000, fie (daca exista un rand -`JTVA_COLOANE.ID_JTVA_COLOANA=0`) o cota de TVA gresita scrisa tacut — amandoua ar fi un **bug nou, -introdus de S4**, nu unul preexistent. - -**Recomandare concreta pentru proiectarea S4 (nu doar constatare):** varianta filtrata a cautarii -(oricare ar fi mecanismul concret — `combosql` extins sau altceva) trebuie sa continue sa treaca prin -`do_adauga_articol`/`frm_articol_factura.Init` pentru linia aleasa (sau sa reproduca explicit -derivarea din `proc_tvav` daca se alege un `REPLACE` direct), nu doar sa extinda `REPLACE`-ul de azi -cu campurile brute din cursor. De pus explicit ca cerinta in proiectarea S4, nu ca detaliu care se -rezolva de la sine. - ---- - -## Intrebarea 2 — `cursor_gestiune` (tipurile 23/41) fara buton "adauga tot"? - -### 2.1 Vizibilitatea butonului pe cod — confirmata - -`but_urmator_tot1` are **`Visible = .F.` la design-time** -(`ofacturare.vc2:11257-11266`, `ADD OBJECT 'But_urmator_tot1' AS but_urmator_tot WITH ... Visible = -.F. ...`). Devine vizibil doar prin `This.but_urmator_tot1.Visible = .T.` explicit, in 8 locuri din -`Do Case poDate.tip` din `Init` (`:15108-15248`): - -| Tip | Vizibil? | Linia care il seteaza | -|---|---|---| -| `eProforma=1` | da | `:15113` | -| `lCopiere` | da | `:15120` | -| `1,5,7,10` (lista de preturi) | **nu** (fara linie) | — | -| `2,6` (contract) | **nu** | — | -| `3` (comanda) | da | `:15150` | -| `4` (avize) | da | `:15166` | -| `21,28,42,47` (aviz din comanda) | da | `:15177` | -| `22,29` (aviz din lista de preturi) | **nu** | — | -| **`23`** (transfer subunitati) | **nu** — are propriul `Case` (`:15187-15195`), dar niciun `Visible=.T.` in el | — | -| **`41`** (retur transfer) | **nu** — are propriul `Case` (`:15197-15205`), dar niciun `Visible=.T.` in el | — | -| `25` (transfer din comanda) | da | `:15215` | -| `26` (aviz din contract) | **nu** | — | -| `8,9` (retur) | da | `:15240` | -| `24` (aviz retur) | da | `:15245` | - -**Confirmat: 23 si 41 au fiecare `Case` propriu in `Do Case`** (nu lipsesc din el, spre deosebire de -tipul 52, verificat separat intr-o cercetare anterioara ca "nu intra in niciun `Case`") — dar niciunul -din cele doua `Case`-uri nu seteaza `Visible = .T.`, deci butonul ramane la valoarea implicita -ascunsa. Concluzie identica cu lista de preturi (1/5/7/10): "adauga tot" e ascuns azi, nu doar -"neconfirmat". - -### 2.2 Exista alt mecanism de adaugare in masa pe 23/41? - -**Nu unul dedicat — dar exista mecanismul de baza, "adauga rand cu rand", disponibil pe orice tip.** -`But_urmator1` (singular, nu "tot") e `ADD OBJECT`-at neconditionat in definitia clasei -(`ofacturare.vc2:11237`), fara vreun `Visible=.F.` la design-time si fara sa fie atins de `Do Case` -de la 15108-15248 (acolo se atinge doar `but_urmator2`, aferent `grd_contracte`/contract, scos prin -`RemoveObject` pentru tipurile non-contract, `:15322-15323` — nu are legatura cu 23/41). Lantul e -`But_urmator1.Click` → `do_urmator()` → `Thisform.do_adauga_articol()` (`:14715`) — exact metoda cu -derivarea de TVA confirmata la intrebarea 1. Deci pe 23/41, ca si pe lista de preturi, operatorul -adauga **o linie o data**, prin acelasi buton/metoda folosit si azi pe tipurile de lista de preturi — -nu exista un "adauga tot" separat de recreat, pentru ca nu exista nici azi. - -### 2.3 Cine mai foloseste `cursor_gestiune` — CORECTIE fata de premisa din plan - -Rutarea reala e in `ofacturare.prg`, procedura `factureaza` (formularul **standard**, -`frm_facturare_articole` — cel lansat implicit), `Do Case` de la `:266-308`: - -``` -Case Inlist(tnTip, 1, 22, 5, 29, 7, 10, 23) && lista de preturi -> cursor_preturi (:279-282) -Case Inlist(tnTip, 2, 26, 6, 52) && contract -> cursor_contract (:283-290) -Case Inlist(tnTip, 3, 21, 25, 28, 42, 47) && comenzi -> cursor_comanda (:292-293) -Case tnTip = 4 && din avize -> cursor_avize (:294-295) -Case Inlist(tnTip, 41) && transfer/retur -> cursor_gestiune (:296-298) -Case tnTip = 30 -> cursor_aviz_nir (:299-300) -Case tnTip = 27 -> cursor_lucrare (:302-304) -Case Inlist(tnTip, 8, 9, 24) -> cursor_retur (:306-307) -``` -plus, mai sus in acelasi `Do Case` (`:271-278`): `Case Inlist(tnTip, 48, 49)` → **`cursor_articole_k`** -(procedura complet diferita, nu una din cele cinci) si `Case tnTip = 45` → **`cursor_preturi`**. - -**Pe formularul standard: doar tipul 41 foloseste `cursor_gestiune`.** Tipul **23 foloseste de fapt -`cursor_preturi`**, grupat direct cu tipurile de lista de preturi (1,22,5,29,7,10) — asta explica de -ce, la 2.1, tipul 23 se comporta identic cu lista de preturi (acelasi cursor sursa). Tipurile **45 si -48/49 nu ating deloc `cursor_gestiune`**: 45 → `cursor_preturi`, 48/49 → `cursor_articole_k` (iese -din perimetrul celor cinci cursoare din S4). Premisa din plan ("`cursor_gestiune` — tipurile 23/41") -si mentiunea "45, 48, 49" (`s4_cautare_articole_server.md:344`, tabelul de vizibilitate) **descriu -corect vizibilitatea butonului** (toate aceste tipuri au "adauga tot" ascuns), dar **nu si sursa lor -de cursor** — nu toate trec prin `cursor_gestiune`. - -**Pe formularul prototip** (`frm_facturare_articole2`/`factureaza2`, accesibil live doar cu optiunea -`gnFacturareNou=1` **si** alegerea explicita a operatorului la un dialog de confirmare, -`ofacturare.prg:88-93` — deci reachable in productie, nu cod mort, dar opt-in), rutarea **difera**: -`Case Inlist(tnTip, 23, 41)` (`ofacturare.prg:776-778`) cheama **impreuna** `cursor_gestiune` pentru -**ambele** tipuri. Pe acest formular, premisa din plan e exacta. Divergenta intre cele doua formulare -(23 pe `cursor_preturi` in cel standard, pe `cursor_gestiune` in prototip) nu pare intentionata — nu -exista niciun comentariu care s-o justifice — dar e in afara perimetrului acestei cercetari (read-only, -fara editare) si nu afecteaza concluzia de mai jos, valabila pe oricare din cei doi cursori (niciunul -nu are `id_jtva_coloana`, ambii sunt tratati identic de `do_adauga_articol`). - -### 2.4 Concluzia care conteaza - -**Dupa S4, pe tipurile 23/41 nu se pierde nimic functional.** "Adauga tot" e deja absent azi pe -ambele (confirmat pe cod, 2.1), iar adaugarea se face deja rand-cu-rand prin acelasi mecanism folosit -si pe lista de preturi (`but_urmator1`/`do_urmator`/`do_adauga_articol`, 2.2) — mecanism pe care S4 nu -il atinge (S4 schimba doar sursa cursorului incarcat la deschidere, nu calea de adaugare a unei linii -individuale). Aceeasi rezerva de la intrebarea 1 se aplica identic aici: valabil doar daca varianta -filtrata a cautarii trece prin `do_adauga_articol`, nu daca reproduce `REPLACE`-ul direct din -`combosql`. - ---- - -## Handoff - -Nu e necesara predare — ambele intrebari sunt inchise, cu dovada pe cod. Rezervele de la 1.4 si 2.4 -(derivarea `id_jtva_coloana` trebuie sa treaca prin `do_adauga_articol`/`frm_articol_factura.Init`, -nu prin `REPLACE` direct tip `combosql`) sunt de dus mai departe in proiectarea S4, nu doar de -arhivat aici. Corectia de la 2.3 (doar tipul 41, nu si 23, foloseste `cursor_gestiune` pe formularul -standard) merita reflectata daca planul S4 mai citeaza undeva "cursor_gestiune (23/41)" ca sursa unica. diff --git a/docs/cercetare/s4b_bara_butoane_meniu.md b/docs/cercetare/s4b_bara_butoane_meniu.md deleted file mode 100644 index 34543d2..0000000 --- a/docs/cercetare/s4b_bara_butoane_meniu.md +++ /dev/null @@ -1,606 +0,0 @@ -# S4b — Bara de butoane si meniul de adaugare - -Proiectare pe cod, READ-ONLY (fara editari, fara `git_sync.ps1`/`txt2vcx.ps1`, fara commit), pentru -povestea **S4b** din `docs\plan_13_unificare_formular_facturare.md:1890-1899` (textul decis in -sectiunea J, `:911-1101`). Nu s-a atins `COMUN\clase\ofacturare_comun.vc2` si -`COMUN\programe\ofacturare_editare.prg` (perimetrul #6) — doar citite, cand au aparut in cautari. - -## Verdict (esenta, 10 randuri) - -Cele trei bucati din decizia 13 sunt implementabile fara cod nou major, dar cu **trei corectii fata -de textul din plan**, toate in favoarea utilizatorului: (1) golul de contract nu e doar -`but_urmator_tot1.Visible` lipsa pe tip 2/6/26 — pe **tip 52 formularul nu intra deloc in ramura -lui**, `Do Case` nu are niciun `Case` care sa-l prinda, deci pierde si titlul, si eliminarea coloanei -`cSerie`, nu doar butonul "tot"; (2) tiparul RORIS (`frm_tranzit`, dialog bespoke) **nu e cel mai -ieftin de refolosit** — suita are deja, folosit chiar in acest formular pentru retur -(`do_cauta_facturi`), un mecanism generic de selectie multipla cu bifare, `cauta_alfa(..., -tnTipReturn=1)`, mai aproape de cerinta („dialog modal cu coloana de bifat si criterii de cautare") -decat un formular nou; (3) „unitatea de selectie e rata" pe contract **nu e valabil pentru toate -contractele** — cursorul `crsarticole1` are doua ramuri disjuncte, `OPT_FACTURARE=3` (articole reale) -si `OPT_FACTURARE IN (1,2)` (rate de scadentar), iar un contract e mereu pe una singura; eticheta -„Alege ratele de facturat…" e corecta doar pe ramura a doua. Echivalenta „tot" = „alege total" tine -azi pe o singura rutina comuna, `do_adauga_articol`, dar `do_adauga_tot` **nu parcurge `crsarticole1` -deloc** — pe contract, „adauga tot" ar trebui sa fie scris, nu doar facut vizibil. Detaliile, cu -`fisier:linie`, mai jos. - ---- - -## 1. Inventarul butoanelor de azi - -### 1.1 `frm_facturare_articole` (formularul de compunere in productie, `ofacturare.vc2:10968-15739`) - -Grid sursa (comanda/lista preturi) = `grd_articole` (`RecordSource=crsarticole`, -`ofacturare.vc2:11570`). Grid sursa-contract = `grd_contracte` (`RecordSource=crsarticole1`, -`:11891`). Grid destinatie (linii factura) = `grd_factura` (`RecordSource=crsfactura`, `:12263`). - -| Control | Clasa | Caption/Picture | L/T/W/H | ToolTipText | `fisier:linie` | -|---|---|---|---|---|---| -| `But_modifica1` | `but_modifica` | (fara caption) `modific_sus.bmp` | 773/61/30/27 | "Modificare (CTRL+M)" | `:11192` | -| `But_sterge1` | `but_sterge` | (fara caption) `sterg_sus.bmp` | 811/61/30/27 | "Stergere (CTRL+D)" | `:11229` | -| `But_renunt1` | `but_renunt` | (fara caption) `renunt_sus.bmp` | 792/1/30/27 | "Renuntare (ESC)" | `:11202` | -| `But_reset1` | `but_reset` | (fara caption) `reset_sus.bmp` | 303/295/30/27 | "Reseteaza" | `:11213` | -| `But_urmator1` | `but_urmator`, `caction=do_urmator` | `urmator1.bmp` | 343/348/30/27 | "Verificare" | `:11237` | -| `But_urmator2` | `but_urmator`, `caction=do_urmator2` | `urmator1.bmp` | 343/143/30/27 | "Verificare" | `:11247` | -| **`But_urmator_tot1`** | `but_urmator_tot`, `caction=do_adauga_tot` | `urmator1_tot.bmp`, **fara Caption, fara ToolTipText** (clasa insasi nu are niciuna din ele — `cmd_butoane.vc2:414-425`) | 343/378/30/27, `Visible=.F.` implicit | — | `:11257` | -| `But_retur` | `but_retur`, `caction=do_retur` | `retur1.bmp`, `Visible=.F.` implicit | 343/407/30/27 | "Retur" | `:11221` | - -Niciun buton "linie noua" (`but_nou`) — o linie se adauga alegand un rand din `grd_articole` / -`grd_contracte` si apeland `do_adauga_articol` (dublu-click / Enter, mecanism de grid standard, nu -citit exhaustiv aici), nu prin `APPEND BLANK` pe `crsfactura`. "Detalii linie" nu exista ca buton -separat — `But_modifica1` **e** azi echivalentul: deschide `frm_articol_factura` pe randul curent din -`crsfactura` (`do_modifica`, `:13746-13914`), reface plafonul de cantitate din cursorul sursa -(`crsarticole` sau `crsarticole1`, ales prin `poArticol.opt_facturare`, `:13778`) si reconstruieste -proprietatile de discount/pret in valuta. - -**"Adauga tot" — `But_urmator_tot1.Click -> do_adauga_tot`** (`:13169-13198`): - -``` -PROCEDURE do_adauga_tot - If Used('crsarticole') And Reccount('crsarticole') > 0 - Select crsarticole - Scan - ... - Thisform.do_adauga_articol(.T.) && tlContract = omis => .F. implicit - ... - Endscan - Else - aMessageBox("Nu exista articole de adaugat!", 48, "Atentie") - Endif -ENDPROC -``` - -**Parcurge exclusiv `crsarticole`.** Nu exista nicio ramura care sa parcurga `crsarticole1` — deci -chiar daca s-ar face vizibil pe tip 2/6/26/52, "adauga tot" **nu ar aduce nimic** pe un contract al -carui `grd_contracte`/`crsarticole1` are randuri (rate sau articole de contract), pentru ca butonul -citeste doar cursorul celalalt. E un gol de **cod**, nu doar de vizibilitate — vezi sectiunea 5. - -**Conventia de clase de butoane** (confirmat, `inventar_controale_formulare.md`): clasa de baza -`buton` (`_cmd_base.vc2:16`) are `Caption=""` implicit — butoanele sunt doar-imagine 30x27px prin -design. `but_nou` (`cmd_butoane.vc2:214-227`, `caption=do_adauga`, `ToolTipText="Adaugare -(CTRL+N)"`) si `but_sterge` (`:354-366`, `caption=inainte_de_do_sterge`, tot fara Caption propriu la -nivel de clasa) au deja `ToolTipText`, dar niciodata `Caption` — "eticheta, nu iconita muta" ceruta de -decizia 13 e o schimbare reala, nu o simpla refolosire a clasei: fie se seteaza `Caption` pe instanta -(clasa `buton` are proprietatea, pur si simplu nefolosita azi), fie se creeaza o varianta de clasa cu -`Caption` implicit. - -### 1.2 `frm_facturare_articole2` (prototip, `ofacturare.vc2:15741-19355`) - -Arhitectura diferita: **un singur grid** `grd_factura` cu editare inline prin combo-uri in celule -(`cCodMat.cboCodmat`, `cDenumire.cCboDenumire`) — nu exista `grd_articole`/`crsarticole` separat. - -| Control | Clasa | Caption/Picture | L/T/W/H | ToolTipText | `fisier:linie` | -|---|---|---|---|---|---| -| **`But_nou1`** | `but_nou` (caption suprascris pe instanta, vezi mai jos) | `nou_sus.bmp` | 719/175/30/27 | "Adaugare (CTRL+N)" | `:15936` | -| `But_sterge1` | `but_sterge` | `sterg_sus.bmp` | 747/175/30/27 | "Stergere (CTRL+D)" | `:15955` | -| `But_renunt1` | `but_renunt` | `renunt_sus.bmp` | 716/1/30/27 | "Renuntare (ESC)" | `:15944` | - -**Sunt deja unde trebuie**: `Top=175`, gridul la `Top=204` (`:15936`, `:15955`) — **deasupra -tabelului**, exact pozitionarea ceruta de decizia 13. - -**`But_nou1.Click -> do_adauga`** (`:17118-17122`, override propriu pe instanta, nu `caction` -mostenit de la clasa `but_nou`): - -``` -PROCEDURE do_adauga - Select crsFactura - APPEND BLANK - this.grd_factura.SetFocus() -ENDPROC -``` - -Adauga direct un rand gol si da focus in grid — **nu** cheama `do_adauga_articol`, nu verifica stoc, -nu deschide niciun dialog. Completarea articolului se face **dupa**, prin editare inline in celule -(`cboCodmat`/`cCboDenumire`) — acesta e tiparul care se leaga direct de S4 (cautare pe server, vezi -sectiunea 6), nu de meniul de adaugare in masa. - -Nu exista `But_modifica`/`detalii linie` in acest prototip — editarea se face inline, in celula. - -### 1.3 Tabel comparativ - -| | `frm_facturare_articole` (productie) | `frm_facturare_articole2` (prototip) | -|---|---|---| -| Pozitia butoanelor de linie | lateral, intre cele doua griduri (`Left=773+`) | deasupra gridului (`Top=175`, gridul la `Top=204`) — cerinta decizie 13 | -| "Linie noua" | nu exista — se alege dintr-un grid sursa | `But_nou1` -> `APPEND BLANK` + focus | -| "Sterge linie" | `But_sterge1`, lateral | `But_sterge1`, deasupra | -| "Detalii linie" | `But_modifica1` -> `frm_articol_factura` (dialog complet) | nu exista — editare inline | -| Sursa de articole | 2 griduri separate, `crsarticole`/`crsarticole1` | niciunul — combo pe cod/denumire in celula | -| Caption pe butoane | fara, peste tot (doar iconite) | fara, peste tot (doar iconite) — nici prototipul nu rezolva "eticheta" | - -Concluzie pentru implementare: **pozitionarea** se ia din prototip, **comportamentul de adaugare in -masa** (`do_adauga_tot`/dialoage de alegere) se ia din formularul de productie (prototipul nu are deloc -aceasta logica — n-a fost construit pentru documente cu sursa), iar **eticheta pe buton** nu exista -azi in niciunul din cele doua, e de adaugat explicit in ambele cazuri. - ---- - -## 2. `xmenu()` — contract si exemple - -**Definitie**: `COMUN\programe\proceduri_comune.prg:755-822` (identica, byte-cu-byte in structura, cu -`COMUN\programe\oproceduri_comune.prg:1550-1617` — a doua e cea incarcata efectiv, SET PROCEDURE -ADDITIVE peste prima, dar comportamentul e acelasi). - -``` -Procedure XMENU - Lparameters TCITEMS, TNBAR - && TCITEMS = optiuni separate prin ";" ; "\-" = separator vizual (bara, nu optiune selectabila, - && nu se trece prin ALLTRIM); "\ metoda unica -> dialog cu bifare si criterii -> populare aditiva prin -`INSERT`, fara scriere Oracle pana la salvare) — dar `frm_tranzit` insusi e un formular **specific -ROAACNPRO**, care nu exista in ROAFACTURARE si n-ar trebui portat ca formular. - -**ROAFACTURARE are deja, in productie, in acelasi `frm_facturare_articole` care e subiectul acestei -povesti, mecanismul cerut** — `cauta_alfa()` (`COMUN\programe\cauta_alfa.prg:16-260`), functia de -cautare generica folosita de zeci de `do_cauta_*` din toata suita. Are deja tot ce cere decizia 13: - -- **coloana de bifat**: cand `tnTipReturn=1`, `cauta_alfa` adauga singura o coloana `ales` peste - cursorul de rezultate (`cauta_alfa.prg:124`: `Select *, 0 As ales From &lcCursort ... Into Cursor - &lcCursor`) si titlul devine explicit "Alegeti ... (mouse-click pe numar sau apasati SPACE)" - (`oproceduri_facturare.prg:2104`); -- **criterii de cautare**: parametrul `tcStringCriterii` deschide `cauta_alfa_form_plus`, varianta cu - bara de criterii (`cauta_alfa.prg:22`); fara el, cautarea alfa/browse standard tot filtreaza - interactiv pe coloanele afisate; -- **populare aditiva, fara scriere in baza**: returul (`tnTipReturn=1`) e un XML cu randurile bifate, - convertit local prin `Xmltocursor(...)` (exemplu direct, `ofacturare.vc2:9196`) — nimic nu ajunge la - Oracle in acest pas; -- **e deja folosit in acest exact formular**, pentru un caz de "alege documentul sursa": vezi 4.2. - -**Recomandare de proiectare**: dialoagele "Alege..." din meniul S4b se construiesc pe reteta -`cauta_alfa(..., tnTipReturn=1)`, cu `tcselect`/`tcfiltru` diferite pe sursa (comanda/contract/avize/ -retur), **nu** pe un formular nou stilizat dupa `frm_tranzit`. Ramane de portat din RORIS doar -**ideea**, nu codul: buton unic condiționat, metoda de intrare unica, populare aditiva — toate trei -deja adevarate si pentru `cauta_alfa`. - -### 4.2 Precedentul direct: retur multi-factura - -`frm_date_factura.do_cauta_facturi` (`ofacturare.vc2:9173-9212`) foloseste deja exact acest tipar, -pentru cazul "alege mai multe facturi sursa de retur": - -``` -lcXMLFacturi = caut_facturi_multiple_client(poDate.id_client, poDate.in_valuta, poDate.id_valuta, .T.) -If !Empty(lcXMLFacturi) and gnButon = 1 - Xmltocursor(lcXMLFacturi, "crsFacturiTemp") - ... - poDate.listaid = cursor2lista("crsFacturiTemp", "id_vanzare", ",") -``` - -si `caut_facturi_multiple_client` (`oproceduri_facturare.prg:2091-2121`) cheama direct -`cauta_alfa(..., lnTipReturn = Iif(tlFacturiMultiple, 1, 0))`. **Important pentru sectiunea 9**: acest -pas ruleaza azi la **antet** (`frm_date_factura`), inainte ca formularul de articole (si deci bara de -butoane S4b) sa existe — `poDate.listaid` e fixat o singura data, cursorul `crsarticole` se incarca -o singura data din el (`cursor_retur_document`, cu `V_LISTAID`). Optiunea "Adauga tot din facturile -alese" din meniul S4b **poate** refolosi direct `do_adauga_tot`/`crsarticole` asa cum e azi (sursa e -deja bounded). O optiune noua "Alege facturile de returnat…" **la nivelul barei de butoane** ar insemna -insa a permite schimbarea setului de facturi sursa **dupa** ce formularul de articole s-a deschis — -azi nu exista acest flux (odata setat, `poDate.listaid` nu se rescrie din articole). E o decizie de -proiectare noua, nu o simpla mutare a codului existent (vezi 9.3). - -### 4.3 Ce cursor-sursa pe fiecare tip, si ce inseamna "linie" acolo - -| Sursa | Cursor(oare) populat(e) la intrarea in `factureaza`/`factureaza2` | Ce e o "linie" pentru dialogul de alegere | `fisier:linie` | -|---|---|---|---| -| **comanda** (3, 21, 25, 28, 42, 47) | `crsarticole` <- `pack_facturare.cursor_comanda` | un articol de pe `COMENZI_ELEMENTE`, cu `id_pol` mostenit direct de pe linia comenzii | `ofacturare.prg:292-293` | -| **contract, `OPT_FACTURARE=3`** (2,6,26,52) | `crsarticole1` <- `pack_facturare.cursor_contract`, ramura `CTR_ARTICOLE` | un articol al contractului, `id_pol` derivat prin `CTR_ARTICOLE.ID_POL_ART -> CRM_POLITICI_PRET_ART` | `ff_...COMUN_PACK_FACTURARE.sql:2752-2836` | -| **contract, `OPT_FACTURARE IN (1,2)`** (2,6,26,52) | `crsarticole1` <- acelasi cursor, ramura `CTR_SCADENTAR` (`UNION ALL`) | o **rata** de scadentar (`ID_RATA`, `NR_RATA`, `DATA_RATA`, `DATA_SCADENTA`, `DEN_RATA`); `id_articol` e **NULL**, `cantitate` e mereu 1, `gestionabil=0` | `ff_...COMUN_PACK_FACTURARE.sql:2836-2924` | -| **contract, oricare din cele doua** — cautare libera | `crsarticole` <- acelasi apel, dar cu `pack_facturare.cursor_preturi` legat in interior | lista de preturi normala a operatorului (nu e "libera de politica", vezi `idpol_comanda_contract.md` E.3) | `ff_...COMUN_PACK_FACTURARE.sql:2940-2948` | -| **avize** (4) | `crsarticole` <- `pack_facturare.cursor_avize` | un rand de pe avizul sursa | `ofacturare.prg:294-295` | -| **retur** (8, 9, 24) | `crsarticole` <- `pack_facturare.cursor_retur_document`, filtrat pe `V_LISTAID` (facturile alese la antet, 4.2) | un articol al facturii/facturilor deja alese; **nicio coloana `id_vanzare`/`id_vanzare_det` in cursor** — legatura cu factura sursa se pierde inainte sa ajunga in VFP (`docs\cercetare\legatura_linie_retur.md:8-20`) | `ff_...COMUN_PACK_FACTURARE.sql:3949-4062` | - -**Pe contract, `crsarticole1` e populat printr-un singur `UNION ALL`, dar cele doua ramuri sunt -disjuncte pe `OPT_FACTURARE`**: pentru un contract cu `OPT_FACTURARE=3`, `WHERE A.OPT_FACTURARE = 3` -elimina ramura de rate; pentru `OPT_FACTURARE IN (1,2)`, `WHERE ... IN (1,2)` elimina ramura de -articole. **Un contract nu produce niciodata ambele tipuri de rand in acelasi `crsarticole1`.** Deci -eticheta din meniu ("Adauga tot din contract" vs. "Adauga toate ratele") **trebuie sa depinda de -`crsarticole1.opt_facturare`** (coloana exista in cursor, `:2751,2893` — poate fi citita din primul -rand dupa incarcare), nu poate fi un text static ca in tabelul din plan. - -**Daca `crsarticole1` iese gol** (contract fara articole/rate configurate), formularul de azi -**elimina complet** `grd_contracte`/`But_urmator2` si redistribuie inaltimea catre `grd_articole` -(`ofacturare.vc2:15294-15324`, `If !Used('crsarticole1') ... Thisform.RemoveObject('grd_contracte')`) -— documentul se comporta ca o factura "libera" din lista de preturi. Meniul S4b trebuie sa verifice -aceeasi conditie inainte sa ofere "Adauga tot din contract"/"Alege ratele…": daca cursorul nu exista -sau e gol, optiunea nu apare deloc (nu apare goala). - -### 4.4 Populare aditiva si "nicio scriere pana la salvare" - -Deja adevarat prin constructie, pentru toata familia `do_adauga_articol`: fiecare rand ales (fie prin -`do_adauga_tot`, fie prin rezultatul unui `cauta_alfa`) trece prin **`Thisform.do_adauga_articol(...)`**, -care termina cu `Insert Into crsfactura(...)` (cursor local, `.T. Buffering`) — niciun apel Oracle in -acest pas (Oracle e atins abia la salvarea documentului, `finalizeaza_factura`/`scrie_factura2`, in -afara acestei povesti). Un dialog nou de alegere trebuie doar sa **produca lista de randuri bifate** -(cursor XML din `cauta_alfa`) si sa **cheme aceeasi rutina** rand cu rand — vezi sectiunea 5, e -punctul care garanteaza si echivalenta cu "tot". - -### 4.5 Zero rezultate — mesaj, nu tacere - -RORIS nu spune nimic la zero rezultate (`import_roris_roaacnpro.md`, punctul 5: butonul "pare ca nu -face nimic"). `cauta_alfa` insasi nu are un mesaj dedicat de "zero rezultate" (e un browse generic — -daca cursorul de rezultate e gol, grila apare goala, fara alt semnal, cf. structurii ei standard). -**Reteta pentru S4b**: verificarea "zero rezultate" se face **inainte** de a deschide `cauta_alfa` -(similar cu `do_cauta_facturi`, care verifica intai `Empty(poDate.id_client)` inainte de a cauta), -printr-un `SELECT COUNT(*)` (sau verificare pe cursorul deja incarcat, `Reccount(...)=0`) urmat de -`AMESSAGEBOX` care spune **de ce**: "Contractul nu are rate de facturat neincasate" / "Comanda nu are -articole ramase de facturat" / etc., in loc sa lase dialogul sa se deschida gol. Exact tiparul deja -folosit de `do_adauga_tot` insusi pe ramura fara date (`:13195-13197`, -`"Nu exista articole de adaugat!"`) — se extinde acelasi obicei la dialogul de alegere. - ---- - -## 5. Echivalenta „tot" vs. „alege" - -**Garantia structurala exista, dar e incompleta azi.** Toate cele trei cai de populare a -`crsfactura` — `do_adauga_tot` (SCAN + apel), un viitor dialog de alegere (bifare + apel pe fiecare -rand bifat), si adaugarea manuala rand cu rand — converg spre **aceeasi rutina**, -`Thisform.do_adauga_articol(tlImplicit, tlContract, tlRetur)` (`ofacturare.vc2:12813-...`). Cata vreme -toate trei cheama exact aceasta rutina, cu acelasi cursor sursa pozitionat pe randul corect, rezultatul -per linie in `crsfactura` e identic prin constructie — nu exista o a doua cale de `INSERT` in -`crsfactura` in afara acestei rutine (confirmat prin cautarea `Insert Into crsfactura` — singurul alt -loc e ramura specifica `tip=30`/aviz din NIR, `ofacturare.prg:357-376`, care nu intra in perimetrul -S4b). - -**Ce lipseste, concret, ca sa fie adevarat si pe contract**: `do_adauga_tot` (`:13169-13198`) parcurge -**doar `crsarticole`**. Pentru ca "Adauga tot din contract" / "Adauga toate ratele" sa functioneze, -`do_adauga_tot` are nevoie de o ramura noua (sau un al doilea parametru `tlContract`, simetric cu -`do_adauga_articol`) care sa faca `SCAN` peste `crsarticole1` si sa cheme -`Thisform.do_adauga_articol(.T., .T.)`. Fara aceasta completare, "tot" pe contract fie nu exista, fie -(daca cineva ar face butonul vizibil fara sa atinga metoda) ar aduce tacut liniile gresite din -`crsarticole` (lista de preturi) in loc de `crsarticole1` (articolele/ratele contractului) — o eroare -mai grava decat lipsa vizibilitatii, pentru ca n-ar da nicio eroare, doar rezultat gresit. - -**Alegerea selectiva** trebuie sa respecte aceeasi regula: dupa ce `cauta_alfa` intoarce XML-ul cu -randurile bifate, bucla care le proceseaza trebuie sa pozitioneze cursorul sursa (`crsarticole` sau -`crsarticole1`, dupa caz) pe `id_c`-ul corespunzator inainte de fiecare apel `do_adauga_articol`, **nu** -sa reconstruiasca linia din campurile XML direct — altfel cele doua cai ("tot" vs. "alege") ar avea -doua implementari diferite ale "ce inseamna sa adaug randul X", exact riscul pe care criteriul de gata -din plan il exclude explicit ("rezultatul in `crsfactura` e identic pe cele doua cai cand selectia e -totala"). - -**Pe comanda/avize/retur, criteriul e deja adevarat prin constructie** — `do_adauga_tot` foloseste azi -`crsarticole`, care e cursorul lor unic; singurul de completat e contractul (cele doua cazuri de mai -sus). - ---- - -## 6. Interactiunea cu S4 (cautarea articolelor pe server) - -Decizia din S4 (`plan_13...md:1879-1888`) e explicita: `crsarticole` **nu se mai incarca in masa** -pentru facturarea libera (lista de preturi/nomenclator), inlocuita cu `combosql` pe cod/denumire care -completeaza randul pe loc — **dar** "se pastreaza adauga tot pentru tipurile care au document sursa -(comanda, aviz, contract) — acolo setul e marginit si incarcarea lui e legitima". Consecinta directa -pentru meniul S4b: - -- **Optiunile "Cauta in lista de preturi…" si "Alege din nomenclator…"** (constante peste tot, cf. - deciziilor 16/18/20) **nu deschid un dialog de tip `cauta_alfa`** — ele sunt fatada meniului pentru - mecanismul S4: `combosql` pe o singura linie, populata pe loc, fara incarcare in masa. In termeni de - UI, alegerea uneia din aceste doua optiuni echivaleaza cu ce face azi `But_nou1.do_adauga` din - prototip (`APPEND BLANK` + focus pe celula de cod/denumire) — deci **"linie noua" (sectiunea 1) si - aceste doua optiuni de meniu ajung la acelasi gest UI**, doar ca "linie noua" il porneste direct - (fara meniu), iar cele doua optiuni de meniu il pornesc din context (dupa ce operatorul a vazut si - restul optiunilor sursei). Nu sunt cai de cod diferite — sunt doua puncte de intrare in acelasi - `APPEND BLANK` + editare inline. -- **Toate optiunile "Adauga tot din X…" / "Alege X…"** (comanda, contract, avize, retur) opereaza pe - cursoarele bounded (`crsarticole`/`crsarticole1`) pe care **S4 le pastreaza neschimbate** — pentru - aceste tipuri, nimic din mecanismul de cautare pe server nu se aplica; secțiunile 3-5 de mai sus - raman valabile ca atare. -- **Punct de atingere real intre cele doua povesti**: campul `id_pol` pe randul adaugat prin - `combosql` (S4, cautare din nomenclator liber) — subiectul blocajului `FACT-024` documentat pe larg - in plan (J-ter/J-quater) si in `idpol_comanda_contract.md`. S4b **nu rezolva** acest blocaj (e - decizia de produs deschisa la J-quater, intre reteta in 4 pasi / nota pe antet / nota pe antet ca - fallback) — doar il mosteneste: optiunea "Alege din nomenclator…" din meniul S4b va ajunge la aceeasi - eroare `FACT-024` la salvare, indiferent de cum arata butonul, pana cand acea decizie separata se ia. - De mentionat explicit in criteriul de gata al S4b, ca sa nu se creada ca butonul rezolva problema. - ---- - -## 7. Ordinea de executie in pasi - -1. **Bara de linie (`but_nou`, `but_sterge`, "detalii linie")**, mutata deasupra gridului, cu - `Caption` explicit pe fiecare instanta (clasele `but_nou`/`but_sterge` au deja `ToolTipText`, nu au - niciodata `Caption` — de setat pe instanta, nu de asteptat de la clasa). - *Verificabil*: cele trei butoane sunt vizibile deasupra gridului de linii, fiecare cu text vizibil - (nu doar iconita), pe orice tip de document deschis. -2. **Corectarea `Do Case` din `Init`** (`ofacturare.vc2:15109-15248`): adauga `52` la ramura - `Inlist(poDate.tip, 2, 6)` (linia 15129) astfel incat sa primeasca titlu, eliminare `cSerie`, si sa - fie tratat identic cu 2/6 in tot restul metodei — corecteaza golul mai mare decat cel semnalat in - plan (sectiunea 3 de mai sus). Adauga `26` la lista tipurilor care primesc `but_urmator_tot1.Visible - = .T.` candva in pasul 3, nu aici (26 ramane azi cu titlu corect, doar fara butonul "tot"). - *Verificabil*: un document nou de tip 52 arata acelasi titlu si acelasi grid (fara `cSerie`) ca un - document de tip 2/6. -3. **Extinderea `do_adauga_tot`** cu ramura pe `crsarticole1` (parametru sau ramura separata dupa - `poDate.tip`/`crsarticole1.opt_facturare`), plus vizibilitatea butonului/optiunii pe 2/6/26/52 — - **cei doi pasi impreuna**, nu separat (vizibilitate fara continut ar aduce un buton mut; continut - fara vizibilitate nu s-ar vedea niciodata). - *Verificabil*: pe un contract cu `OPT_FACTURARE=3` si articole configurate, "adauga tot" produce in - `crsfactura` exact liniile din `crsarticole1`; pe un contract cu `OPT_FACTURARE IN (1,2)`, produce - cate o linie per rata neincasata. -4. **Butonul unic "Adauga articole" cu `xmenu()`**, inlocuind `But_urmator_tot1` (fara caption) si - orice buton separat de alegere — construit dinamic dupa tabelul din sectiunea 3, cu eticheta pe - ramura de contract aleasa in functie de `crsarticole1.opt_facturare` (sectiunea 4.3). - *Verificabil*: pe fiecare sursa, meniul deschis contine exact optiunile din tabelul sectiunii 3, in - ordinea specificata, iar alegerea `ESC` (`xmenu()` intoarce 0) nu declanseaza nimic. -5. **Dialogul de alegere selectiva**, pe reteta `cauta_alfa(..., tnTipReturn=1)` (sectiunea 4), - cu mesaj explicit la zero rezultate (4.5) si populare prin `do_adauga_articol` rand cu rand (5) — - cate o instantiere per sursa (comanda/contract-articole/contract-rate/avize/retur-linii), cu - `tcselect`/`tcfiltru` proprii. - *Verificabil*: pe fiecare sursa, bifarea a N randuri din M disponibile adauga exact acele N randuri - in `crsfactura`, cu aceleasi valori ca daca ar fi fost adaugate individual; bifarea tuturor produce - acelasi rezultat ca "adauga tot" (criteriul de gata al plan-ului, rescris verificabil). - *Depinde de*: pasul 3 (aceeasi rutina `do_adauga_articol` trebuie sa functioneze deja pe - `crsarticole1` inainte ca dialogul de alegere sa se poata baza pe ea). -6. **Reconcilierea cu selectia de facturi la antet** (sectiunea 4.2, 9.3) — decizie separata, - nu blocanta pentru pasii 1-5: daca "Alege facturile de returnat…" ramane doar la antet (cum e azi) - sau devine posibila si din bara de butoane. - -*Gata cand (rescris)*: pe fiecare din cele sase surse (lista de preturi, contract-articole, -contract-rate, comanda, avize, retur), meniul "Adauga articole" ofera exact optiunile tabelate in -sectiunea 3; pentru fiecare sursa cu "tot"/"alege" ambele prezente, o selectie totala prin dialogul de -alegere produce in `crsfactura` acelasi set de randuri (aceleasi valori, nu doar acelasi numar) ca -"adauga tot"; zero rezultate in orice dialog de alegere produce un mesaj care spune sursa si motivul, -nu un dialog gol; tip 52 se comporta identic cu tip 2/6 in titlu si structura de grid. - ---- - -## 8. Ce NU se poate testa headless - -- **Continutul si comportamentul meniului `xmenu()`** — `Activate Screen` + `Define Popup ... Bar` e - UI nativa Windows/VFP (shortcut popup la pozitia mouse-ului); nu exista API de citire a itemilor unui - popup activ prin automatizare headless. Verificarea "meniul contine optiunile N" se poate face doar - **indirect**, citind stringul `lcMeniu` construit inainte de apelul `xmenu()` (verificabil headless, - ca text), nu popup-ul afisat efectiv. -- **Coloanele griduri (`grd_articole`, `grd_contracte`, `grd_factura`, si viitorul grid al dialogului - de alegere)** — capcana deja cunoscuta si confirmata pe acest proiect: sub `-A -T` - (rulare headless) `ColumnCount=0` si `RecordSource` raman artefacte, coloanele nu se materializeaza. - Exista un harness UI **vizibil** (nu headless) care le citeste corect — orice verificare vizuala a - meniului/dialogului de alegere trebuie sa treaca prin acel harness, nu prin rulare `-A -T`. -- **`cauta_alfa`/`cauta_alfa_form_plus`** — dialog modal cu `.Show(1)`, blocheaza thread-ul UI pana la - interactiune; testarea automata ar necesita fie injectare de input (interzisa pe aceasta masina, - partajata cu Marius — vezi memoria `masina-partajata-fara-input-real`), fie apelarea directa a - functiilor Oracle/cursor din spatele dialogului, ocolind UI-ul (verifica *datele*, nu *dialogul*). -- **`AMESSAGEBOX` la zero rezultate** — verificabil ca text al mesajului in cod (string literal), nu ca - aparitie reala pe ecran, din acelasi motiv. -- **Pozitionarea vizuala "deasupra gridului"** (`Top`/`Height` relative) — se poate verifica numeric - (comparand `Top`-ul butoanelor cu `Top`-ul gridului in `.vc2`), dar nu si "arata bine"/aliniere - vizuala — necesita captura de ecran prin harness-ul UI vizibil. - -Ce **se poate** verifica headless, direct: continutul cursoarelor (`crsarticole`, `crsarticole1`, -`crsfactura`) dupa apelul rutinelor de adaugare, apelate direct (nu prin click) cu parametri de test; -stringul `lcMeniu` construit inainte de `xmenu()`; valorile `Visible`/`Caption`/`ToolTipText` setate pe -obiectele din `.vc2` (proprietati statice, nu comportament de runtime). - ---- - -## 9. Riscuri, capcane de UX, si ce ramane de decis de Marius - -### 9.1 Regulile de formulare/griduri (`COMUN\docs\reguli_lucru.md`, punctul 7) - -Aplicabile direct aici (citite, respectate in proiectarea de mai sus): -- **`GO` pe `Recno()`** — orice bucla de tip `do_adauga_tot`/dialog de alegere care itereaza cursorul - sursa (`crsarticole`/`crsarticole1`) trebuie sa retina si sa refaca pozitia (`Recno()`) dupa fiecare - apel `do_adauga_articol`, exact cum face deja `do_adauga_tot` azi (`lnNrInregistrare = Recno()` ... - `Go lnNrInregistrare`, `:13175,13185,13193`) — pastrat si in ramura noua pe `crsarticole1`. -- **UX formulare/griduri** — butoanele fara `Caption` sunt azi conforme cu restul suitei (toate - butoanele suita sunt icon-only); a le adauga `Caption` e o schimbare vizibila la nivelul intregii - bare, nu doar a celor trei butoane noi — de discutat daca `But_renunt1`/`But_reset1` raman mute langa - butoane cu text (inconsistenta vizuala posibila, nu doar tehnica). - -### 9.2 Contractul are doua meniuri, nu unul (sectiunea 4.3) - -Tabelul din plan (J) trateaza "contract" ca o singura sursa cu un singur set de optiuni. Pe cod, un -contract e **fie** OPT_FACTURARE=3 (articole), **fie** 1/2 (rate) — niciodata ambele. Eticheta -"Alege ratele de facturat…" din decizia explicita a lui Marius e corecta doar pe ramura a doua; pe -prima, echivalentul e "Alege articolele din contract…". **De decis**: se pastreaza o singura eticheta -generica ("Alege din contract…") care acopera ambele cazuri fara sa distinga in text, sau se -diferentiaza explicit (mai clar pentru operator, dar mai mult cod de verificare a lui -`crsarticole1.opt_facturare` inainte de a construi meniul)? - -### 9.3 "Alege facturile de returnat…" — la antet sau la bara de butoane? - -Azi (sectiunea 4.2), selectia facturilor sursa pentru retur se face **o singura data**, la deschiderea -formularului de antet (`frm_date_factura.do_cauta_facturi`), inainte ca formularul de articole sa -existe. Meniul S4b, asa cum e cerut de decizia 13, ar pune "Alege facturile de returnat…" **in bara de -butoane a formularului de articole** — adica dupa ce `poDate.listaid` a fost deja fixat. Doua optiuni, -de decis de Marius: -1. Optiunea din meniul S4b **nu schimba** setul de facturi sursa — e doar un rascolitor spre inapoi, - catre acelasi dialog de la antet, dar utilizatorul trebuie sa iasa din pasul de articole ca sa-l - foloseasca (comportament posibil confuz: optiunea apare in meniul de articole, dar actioneaza pe alt - ecran). -2. Se adauga un mecanism nou: alegerea de facturi suplimentare **din formularul de articole**, care - extinde `poDate.listaid` si reincarca aditiv `crsarticole` (`cursor_retur_document` apelat a doua - oara, cu filtru pe facturile noi, `INSERT INTO crsarticole` in loc de `SELECT INTO`) — cere cod nou - in VFP, dar nu in `pack_facturare` (procedura Oracle ramane aceeasi, doar apelata de mai multe ori). - -"Alege liniile de returnat…" (per articol, din facturile deja alese) e diferita si **nu are conflict** -cu fluxul de azi — e un dialog de alegere clasic peste `crsarticole`, ca pe comanda/avize. - -### 9.4 `id_pol` pe rata de contract — `NULL` prin constructie, dar validat corect - -Rata de scadentar (`crsarticole1`, ramura `OPT_FACTURARE IN (1,2)`) are **`id_pol` NULL** prin -constructia SQL insasi (`NULL AS ID_POL`, `:2840`) — dar asta nu ajunge niciodata la `FACT-024`, -pentru ca ratele nu trec prin `contabilizeaza_articol`, ci prin `contabilizeaza_rata` (functie separata -in `pack_facturare`, `idpol_comanda_contract.md` §0c), care citeste nota din `CONTRACTE.ID_NOTA`. **De -verificat inainte de implementare**: `do_adauga_articol`/`do_scrie_articole` scriu randul de rata in -`crsfactura` cu ce anume in coloana `id_pol` (probabil `NULL`, mostenit din `poArticol`) — daca -salvarea foloseste ramura corecta (`contabilizeaza_rata`, nu `contabilizeaza_articol`) pe baza altui -semnal decat `id_pol` (probabil `id_ctr`/`opt_facturare` pe linie), nu pe baza lui `id_pol` fiind gol. -Nu s-a verificat cine alege intre cele doua functii la salvare — e in afara perimetrului S4b (tine de -scriere, nu de bara de butoane), dar merita un pointer explicit ca sa nu se presupuna gresit ca "rata -fara `id_pol`" e acelasi caz cu "articol din nomenclator fara `id_pol`" (J-quater) — sunt cai de cod -diferite, cu tratament diferit. - -### 9.5 Ce ramane strict de decis de Marius - -1. Eticheta pe contract (9.2) — generica sau diferentiata pe `OPT_FACTURARE`. -2. Fluxul "Alege facturile de returnat…" (9.3) — raman la antet sau se adauga incarcare aditiva. -3. Daca `But_renunt1`/`But_reset1` primesc si ele `Caption`, pentru consistenta vizuala cu bara nou- - etichetata, sau raman icon-only (9.1). -4. Ordinea si formularea exacta a optiunilor in `xmenu()` per sursa (tabelul din sectiunea 3 e o - propunere, nu o decizie finala de text). - ---- - -## Handoff - -Cercetare incheiata, fara nicio problema de context intalnita in aceasta runda — nu a fost necesara -predarea. Toate cele noua sectiuni cerute de brief sunt completate mai sus, cu `fisier:linie` verificat -direct pe fisierele text reale (nu `.bak`), plus SQL-ul din `PACK_FACTURARE` exportat curent -(`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql`). Nicio editare de -cod, niciun `git_sync.ps1`/`txt2vcx.ps1`, niciun commit. diff --git a/docs/cercetare/s4c_discount_in_grid.md b/docs/cercetare/s4c_discount_in_grid.md deleted file mode 100644 index 522dd36..0000000 --- a/docs/cercetare/s4c_discount_in_grid.md +++ /dev/null @@ -1,489 +0,0 @@ -# S4c — Discountul pe linie, mutat din dialog in grid — proiectare implementabila - -Cercetare + proiectare READ-ONLY pentru `docs\plan_13_unificare_formular_facturare.md`, `#### S4c` -(`:2156-2190`). Nu s-a modificat niciun fisier, nu s-a rulat `git_sync.ps1`/`txt2vcx.ps1`, nu s-a dat -commit, nu s-a scris nimic pe Oracle (numai `SELECT`, niciunul rulat de fapt — toata cercetarea a -fost pe cod VFP). Nu s-a atins `COMUN\clase\ofacturare_comun.vc2` si `COMUN\programe\ofacturare_editare.prg`. - -**Surse de adevar deja stabilite, citate ca atare** (nu se re-verifica aici): -`docs\cercetare\discount_in_rapoarte_si_efactura.md` (verdictul principal — niciun raport/eFactura nu -tipareste discountul, dar amandoua citesc `valdiminuatftva`/`discountftva` din cursorul de tiparire), -`docs\cercetare\discount_verificare2.md` (structura reala a coloanelor din `grd_factura` si a -`VANZARI_DETALII`). **`docs\cercetare\discount_pe_articol.md` NU e citat ca sursa** — task-ul mi-a -semnalat ca are doua afirmatii gresite, corectate deja in `discount_verificare2.md` sectiunea finala -("Ce era gresit in afirmatiile de mai sus"). - -## Verdict (10 randuri) - -S4c e implementabila cu cod nou moderat, nu cu redesign. Contractul de calcul exista deja, complet si -reciproc, in `frm_articol_factura.do_calculeaza_discount` (`ofacturare.vc2:1874-1976`) si -`frm_articol_factura.do_calculeaza_totaluri` (`:2068-2179`, delegand la functia partajata -`calculeaza_totaluri()` din `oproceduri_facturare.prg:2258-2381`) — aceasta din urma **e deja scrisa -generic, pe orice obiect cu proprietatile potrivite**, deci se poate rula direct pe un rand din -`crsfactura` (via `Scatter`/`Gather Name`), fara sa mai treaca prin dialog. Tiparul de eveniment -pentru coloane editabile de grid **exista deja in acelasi fisier**, pe alt formular din aceeasi -familie de clase (`frm_avizare_lucrare.grd_articole.cCantitate/cPret.Text1.LostFocus`, -`:6549-6562`) — `LostFocus` care cheama `Thisform.do_calculeaza_totaluri()`, nu `InteractiveChange`. -**Capcana reala e alta decat pare din titlul poveste**: exista **doua cai de scriere independente** -catre destinatii diferite, si doar una e azi corect legata. `do_scrie_articole` trimite spre Oracle -(`pack_facturare.adauga_articol_factura`, `ofacturare.vc2:14081-14083`) discountul citit **direct -din `discountftva`/`discountctva`/`vdiscountftva`/`vdiscountctva`** — deci `VANZARI_DETALII.DISCOUNT_UNITAR` -**e mereu corect**, indiferent de bug, pentru ca aceste campuri sunt chiar campurile editate in grid. -Riscul e strict local, in sesiunea VFP curenta: `prelucreaza_factura` (apelata pentru tiparire/eFactura, -`ofacturare.prg:1887`) construieste cursorul de tiparire **din acelasi `crsfactura` deja in memorie**, -citind `valdiminuatftva`/`valdiminuatctva`/`vvaldiminuatftva`/`vvaldiminuatctva` — campuri **agregate** -(pret-discount)*cantitate, care **nu se recalculeaza singure** cand se editeaza discountul brut. Deci -Oracle poate avea `DISCOUNT_UNITAR` corect si totusi factura tiparita/eFactura sa arate valoarea veche, -in aceeasi sesiune, pana la reincarcare. Solutia: coloanele noi de discount trebuie sa scrie, pe -`LostFocus`, atat campurile brute cat si campurile agregate — vezi sectiunea 4. - ---- - -## 1. Lantul complet al campurilor de discount, cu `fisier:linie` - -### 1.1 Structura cursorului local `crsfactura` - -Definit in `creeaza_facturacrs` (`COMUN\programe\ofacturare_comun.prg:1779-1782`): -``` -1779 valdiscountctva N(20,max(gnPc,4)),valdiminuatftva N(20,max(gnPc,4)),valdiminuattva N(20,max(gnPc,4)),valdiminuatctva N(20,max(gnPc,4)),proc_Tvav N(20,4),cu_tva N(1), lot C(20) null, serie c(100) Null,; -1782 vvaldiscountctva N(20,max(gnPVal,4)),vvaldiminuatftva N(20,max(gnPVal,4)),vvaldiminuattva N(20,max(gnPVal,4)),vvaldiminuatctva N(20,max(gnPVal,4)),id_set_fact N(20) Null,explicatie M Null,; -``` -(`discountftva`, `discountctva`, `vdiscountftva`, `vdiscountctva`, `valdiscountftva`, `valdiscountctva`, -`vvaldiscountftva`, `vvaldiscountctva` sunt definite pe liniile adiacente, aceeasi procedura — -nu recitate individual, tiparul `N(20,...)` e identic). - -**Nomenclatura confirmata pe cod** (nu doar dedusa din nume) — opt campuri distincte, trei niveluri: -| Camp | Nivel | Moneda | Cu/fara TVA | Sens | -|---|---|---|---|---| -| `discountftva` | pe unitate | lei | fara TVA | discount unitar, sursa in lei | -| `discountctva` | pe unitate | lei | cu TVA | discount unitar, derivat | -| `vdiscountftva` | pe unitate | valuta | fara TVA | discount unitar, sursa in valuta | -| `vdiscountctva` | pe unitate | valuta | cu TVA | discount unitar, derivat | -| `valdiscountftva`/`valdiscountctva` | pe linie (x cantitate) | lei | ambele | discount agregat lei | -| `vvaldiscountftva`/`vvaldiscountctva` | pe linie (x cantitate) | valuta | ambele | discount agregat valuta | -| `valdiminuatftva`/`valdiminuatctva` | pe linie (x cantitate) | lei | ambele | **valoare neta** (pret-discount)*cant — cea tiparita/eFactura | -| `vvaldiminuatftva`/`vvaldiminuatctva` | pe linie (x cantitate) | valuta | ambele | valoare neta in valuta | - -### 1.2 De la tastare la `crsfactura` — calea de azi (prin dialog) - -1. Operator tasteaza in dialogul `frm_articol_factura` (`ofacturare.vc2:1108-2659`), pe unul din 3 - perechi de campuri: `Clb_procent_discount.Text_simplu1` (`InteractiveChange`, `:2646`), - `Clb_discount_unitar.tx_suma_nat`/`tx_suma_val` (`Valid`, `:2614/:2620`), - `Clb_discountctva.tx_suma_nat`/`tx_suma_val` (`Valid`, `:2602/:2608`). -2. Toate cheama `Thisform.do_calculeaza_discount(valoare, tip)` — **`frm_articol_factura`** - (`:1874-1976`, contract detaliat in sectiunea 2) — scrie in `poArticol`: - `discount_unitar`, `discount_unitar_val`, `discount_unitar_ctva`, `discount_unitar_ctva_val`. -3. `do_calculeaza_discount` cheama la final `Thisform.do_calculeaza_totaluri()` (`:1974`, - `frm_articol_factura`, `:2068-2179`) care delega la **`calculeaza_totaluri(poArticol)`** - (`oproceduri_facturare.prg:2258-2381`, functie globala) — aceasta scrie in `poArticol`: - `valdiminuatftva`, `valdiminuattva`, `valdiminuatctva`, `vvaldiminuatftva`, `vvaldiminuattva`, - `vvaldiminuatctva`, plus `valdiscountftva/ctva`, `vvaldiscountftva/ctva`, `valftva/ctva/tva` etc. -4. La inchiderea dialogului, `do_adauga_articol` (`ofacturare.vc2:12813-13167`) scrie **tot obiectul - deja calculat** in `crsfactura`, in doua pasi: - - `Gather Name poArticol Fields Like ... valdiminuatftva, valdiminuattva, valdiminuatctva, - vvaldiminuatftva, vvaldiminuattva, vvaldiminuatctva ...` (`:12945-12950`) — campurile agregate, - **deja calculate de `calculeaza_totaluri`**, nu recalculate aici. - - `Replace ... discountftva With poArticol.discount_unitar, discountctva With - poArticol.discount_unitar_ctva, vdiscountftva With Nvl(poArticol.discount_unitar_val,0), - vdiscountctva With Nvl(poArticol.discount_unitar_ctva_val,0)` (`:12952-12957`) — campurile pe - unitate, separat, pentru ca nu sunt in lista `Gather` (doar variantele `val*` agregate sunt). -5. `do_modifica` (`:13746-13914`, editarea unei linii existente) urmeaza acelasi tipar — - `Gather`/`Replace` cu aceleasi campuri (`:13837-13881`), dupa ce redeschide dialogul pe rand. -6. **Cale suplimentara, deja existenta, cu bug documentat**: pe factura in valuta, coloana - `cVdiscountftva` (`ControlSource="vdiscountftva"`, `ofacturare.vc2:12340-12345`) e editabila - direct in grid, **fara niciun handler** — scrie `vdiscountftva` direct in `crsfactura` prin - binding-ul standard de grid, dar **nu recalculeaza** `vvaldiminuatftva`/`vvaldiminuatctva` - (confirmat cautat explicit, `discount_verificare2.md` punctul 3). - -### 1.3 De la `crsfactura` la Oracle (`VANZARI_DETALII.DISCOUNT_UNITAR`) - -`do_scrie_articole` (`ofacturare.vc2:13916-...`), la finalizarea facturii, trimite spre -`pack_facturare.adauga_articol_factura` un singur parametru de discount, ales **direct din campurile -pe unitate** ale randului curent din `crsfactura` (`:14081-14083`): -``` -14081 IIF(poArt.cu_tva = 0,; -14082 Iif(poArt.tip_valuta = 0,Alltrim(Str(poArt.discountftva,18,gnPPretV)),Alltrim(Str(poArt.vdiscountftva,18,gnPVal))), ; -14083 IIF(poArt.tip_valuta = 0,Alltrim(Str(poArt.discountctva,18,gnPPretV)),Alltrim(Str(poArt.vdiscountctva,18,gnPVal)))) -``` -**Nu foloseste `valdiminuatftva`.** Deci Oracle primeste intotdeauna valoarea curenta din -`discountftva`/`discountctva`/`vdiscountftva`/`vdiscountctva` — cele patru campuri pe care coloanele -noi de grid le-ar edita direct. `VANZARI_DETALII.DISCOUNT_UNITAR NUMBER(22,6)` e singura coloana -Oracle de discount (confirmat `discount_verificare2.md` punctul 4) — nu exista coloana de procent pe -Oracle. - -### 1.4 Inapoi, pentru tiparire/eFactura — `crsfacttemp` - -`prelucreaza_factura` (`ofacturare_comun.prg:1055-1059`, apelata din `ofacturare.prg:1887`) **NU -reincarca din Oracle** — primeste ca parametru **acelasi `crsfactura`** deja in memorie (cel scris la -pasii 1.2/1.3), si construieste cursorul de tiparire prin agregare SQL locala: -``` -1182 Sum(cantitate) As cantitate,pretftva-discountftva As pretftva,; -1186 Sum(valdiminuatftva) As valftva,Sum(valdiminuattva) As valtva,; -``` -(`ofacturare_comun.prg:1182-1186`, cazul comun `discount_evidentiat=0`). eFactura foloseste **acelasi -cursor de iesire** (`xmlefactura.prg:231`, `LineExtensionAmount = valftva`, `PriceAmount = pretftva`, -`xmlefactura.prg:935-937`, `:1043-1046`). - -**Consecinta directa**: `pretftva-discountftva` de la 1182 citeste `discountftva` (mereu proaspat, -pentru ca e campul editat direct), dar `Sum(valdiminuatftva)` de la 1186 citeste campul **agregat**, -care ramane vechi daca nu a fost recalculat explicit dupa editare. Rezultat: **randul tiparit poate -avea `pretftva` corect (net, recalculat corect din `discountftva`) dar `valftva` (valoarea liniei) -gresit** — o discrepanta pret x cantitate ≠ valoare, vizibila chiar pe hartie, nu doar o valoare -veche uniforma. Asta e mai grav decat "arata vechi" — arata **inconsistent**. - ---- - -## 2. Contractul `frm_articol_factura.do_calculeaza_discount` (`ofacturare.vc2:1874-1976`) - -**Semnatura**: `Lparameters tnValoare, tnTip` — `tnTip`: `1` = s-a modificat procentul, `2` = -discount in lei (fara TVA daca `preturi_cu_tva=0`, cu TVA altfel), `3` = discount in valuta. - -**Ramura principala** — `If poArticol.preturi_cu_tva = 0` (pretul de referinta e fara TVA, cazul -uzual): sursa de adevar e `discount_unitar`(_val). -- `tnTip=1` (procent -> valoare): daca `tip_valuta=0`, - `discount_unitar = Round(pretftva * procent/100, gnPPretV)`, apoi **procentul se re-normalizeaza** - din valoarea rotunjita (`:1888-1889`) — nu se pastreaza procentul brut tastat, ci cel rezultat din - rotunjire. Daca `tip_valuta=1`: `discount_unitar_val` se calculeaza intai (`gnPVal`), apoi - `discount_unitar = Round(discount_unitar_val * Curs / multiplicator, gnPPretV)`. -- `tnTip=2` (lei -> procent): `discount_unitar = tnValoare` direct, procentul se deriva - (`Round(discount_unitar/pretftva*100, 2)`). -- `tnTip=3` (valuta -> procent + lei): `discount_unitar_val = tnValoare`, procentul deriva din - valuta, apoi `discount_unitar = Round(discount_unitar_val * Curs / multiplicator, gnPPretV)`. -- **Dupa `Do Case`, neconditionat**: `discount_unitar_ctva = discount_unitar + Round(discount_unitar - * (proc_tvav-1), gnPPretV)` (`:1913-1914`) — varianta cu TVA e **mereu derivata** din cea fara TVA - in aceasta ramura, niciodata sursa. -- Daca `tip_valuta=1`, simetric: `discount_unitar_ctva_val` derivat din `discount_unitar_val`. - -**Ramura alternativa** — `Else` (`preturi_cu_tva=1`, pretul de referinta e cu TVA): rolurile se -inverseaza complet — `discount_unitar_ctva`(_val) e sursa (calculata direct din `tnValoare`/`pretctva` -in cele 3 cazuri, simetric cu ramura de mai sus), iar `discount_unitar`(_val) **fara TVA** e derivat -la final (`:1958`, `Round(discount_unitar_ctva / proc_tvav, gnPPretV)`). - -**Rotunjiri**: `gnPPretV` pentru valorile unitare in lei, `gnPVal` pentru cele in valuta, `2` fix -pentru procent — niciodata `gnPc` (precizia de linie/document) in aceasta metoda. - -**La final, neconditionat** (`:1972-1974`): `Thisform.clb_tva_discount.Refresh()`, -`Thisform.clb_pret_diminuat.Refresh()`, **`Thisform.do_calculeaza_totaluri()`** — deci orice apel -recalculeaza si campurile agregate de linie, nu doar cele pe unitate. - -**In valuta, ce ramane needitat de aceasta metoda**: campurile agregate (`valdiminuatftva` etc.) NU -sunt scrise aici — `do_calculeaza_totaluri` (sectiunea urmatoare) le calculeaza separat din -`discount_unitar`/`cantitate`. - ---- - -## 3. Starea de azi in grid — tabel - -| Coloana | `ControlSource` | Formular | `ReadOnly` | Editabil azi | Recalculeaza la editare | -|---|---|---|---|---|---| -| `Column5`/`cDiscountCTva` | `discountctva` | `frm_facturare_articole` (productie), `:12311-12317` | `.T.` explicit | Nu | — | -| `Column9`/`cVdiscountftva` | `vdiscountftva` | `frm_facturare_articole`, `:12340-12345` | nesetat (implicit `.F.`) | **Da, pe factura in valuta** | **Nu** — niciun `Valid`/`LostFocus`/`InteractiveChange` propriu, cautat explicit in `10968-15739` | -| `Column5`/`cDiscountCTva` | `discountctva` | `frm_facturare_articole2` (prototip, neinstantiat in productie) | `.F.` explicit, `:16657` | Da (prototip) | Nesigur — prototip, nu s-a cautat handler dedicat | -| `Column9`/`cVdiscountftva` | `vdiscountftva` | `frm_facturare_articole2` | `.F.` explicit, `:16688` | Da (prototip) | idem | -| `Column14`/`procdisc` | `procdisc` | `frm_facturare_articole2` numai, `:16721` | nesetat | scaffold, fara scriere | **Nu are corespondent Oracle, nu are `Gather`/`Replace` nicaieri in fisier** (`discount_verificare2.md` sectiunea 5) | - -**Excludere pe valuta** (`ofacturare.vc2:15269-15278`, in `frm_facturare_articole.Init`): cand -`poDate.in_valuta = 0` se elimina `cVpretFtva`, `cVdiscountftva`, `cVvaldiminuatftva`; cand -`in_valuta <> 0` se elimina `cPretFtva`, `cDiscountctva` (si simetricele lor). Deci azi, pe orice -factura, **doar una din cele doua coloane de discount e vizibila** — cea in lei, needitabila, sau cea -in valuta, editabila-dar-fara-recalcul. Nu exista azi nicio coloana de **procent** de discount in -`grd_factura` in productie (doar `discountctva`/`vdiscountftva`, valori, nu procent) — pentru procent, -azi operatorul trebuie sa deschida dialogul. - ---- - -## 4. Proiectarea - -### 4.1 Ce coloane se adauga/deschid - -Nu se sterge nimic din `crsfactura` (modelul de date ramane neschimbat, conform cerintei). Se -lucreaza cu campurile deja existente: - -- **Coloana procent discount** (noua in grid, in ambele monede) — `ControlSource` pe un camp - calculat, nu direct pe un camp Oracle (nu exista coloana Oracle de procent) — vezi 4.4 pentru - optiunea recomandata. -- **`cDiscountCTva`** (`discountctva`, lei): `Column5.ReadOnly` trece din `.T.` in `.F.` — devine - editabila, simetric cu ce azi doar prototipul `frm_facturare_articole2` face. -- **`cVdiscountftva`** (`vdiscountftva`, valuta): ramane editabila ca azi, dar castiga handler-ul - care azi lipseste. - -Se pastreaza `RemoveObject` pe valuta (`:15269-15278`) neschimbat — excluderea reciproca deja -implementeaza cerinta "se pastreaza excluderea pe `in_valuta`". - -### 4.2 Pe ce eveniment se cableaza calculul reciproc - -**Recomandare: `Text1.LostFocus`, dupa tiparul deja folosit in acest fisier pentru coloane -editabile de grid** — `frm_avizare_lucrare.grd_articole.cCantitate.Text1.LostFocus` si -`.cPret.Text1.LostFocus` (`ofacturare.vc2:6549-6562`), ambele in aceeasi clasa de baza de grid -(`_grdrow`, `_grd_base.vc2:445`) folosita si de `grd_factura`. Motivele, nu teoretice ci din cod: -- **`InteractiveChange` fireste pe fiecare tasta** — ar recalcula la fiecare caracter tastat intr-un - numar cu zecimale (comportament vazut la coloana `procent` in dialog, `:2646`, dar acolo controlul - e un camp simplu de dialog, nu o celula de grid cu re-randare de coloane vecine la fiecare tasta — - in grid ar fi vizibil costisitor si ar zgaltai focusul). - `Valid`-urile din dialog (`:2602-2624`) folosesc explicit un guard `<> nSumaNatOld`/`nSumaValOld` - ca sa nu recalculeze cand valoarea nu s-a schimbat efectiv — semnaleaza ca autorii au evitat - deliberat recalculul pe fiecare tasta chiar si la `Valid`. -- **`LostFocus` e tiparul deja validat pentru grid-uri de articole in acest fisier**, pe doua coloane - numerice diferite (cantitate, pret), amandoua cu acelasi tip de nevoie (schimbarea unei valori - declanseaza recalculul liniei si al totalului documentului). -- Grid-ul VFP nu are `Valid` pe coloana insasi in mod uzual folosit aici — handlerele existente sunt - pe `.Text1.LostFocus`, deci noile handlere trebuie sa fie - `grd_factura.cDiscountCTva.Text1.LostFocus` si `grd_factura.cVdiscountftva.Text1.LostFocus` - (plus coloana noua de procent, daca implementata ca `Text1` editabil). - -### 4.3 Rutina de recalcul — reutilizare, nu reimplementare - -`calculeaza_totaluri()` (`oproceduri_facturare.prg:2258-2381`) **e deja generica**: primeste orice -obiect (`toArticol`) cu proprietatile `preturi_cu_tva`/`discount_unitar`/`pretftva`/`cantitate`/... -(le adauga singura, prin `AddProperty`, daca lipsesc — `:2264-2272`, mapand numele de camp -`crsfactura`-stil, `discountftva`/`vdiscountftva`, pe numele `poArticol`-stil, -`discount_unitar`/`discount_unitar_val`) si scrie inapoi campurile agregate. **Poate rula direct pe -un `Scatter Name` al randului curent din `crsfactura`**, fara sa deschida dialogul: -``` -Select crsfactura -Scatter Name loArt Memo -loArt = calculeaza_totaluri(loArt) -Gather Name loArt Memo -``` -Aceasta acopera pasul "recalculeaza campurile agregate din discountul unitar deja stabilit" -(`valdiminuatftva`, `valdiminuatctva`, `vvaldiminuatftva`, `vvaldiminuatctva`, -`valdiscountftva/ctva`, `vvaldiscountftva/ctva`), **exact campurile care azi raman vechi** -(sectiunea 1.4). - -**Ce `calculeaza_totaluri()` NU face**: conversia reciproca procent<->valoare — aceea e logica din -`do_calculeaza_discount` (sectiunea 2), care azi scrie in `poArticol` si actualizeaza controale de -dialog (`Thisform.clb_*.Refresh()`) care nu exista in grid. **Recomandare**: se extrage o functie noua, -fara referinte la `Thisform.clb_*` (partea de calcul pur, liniile `:1878-1970` minus liniile de -`Refresh`), reutilizabila atat din dialog (daca dialogul de articol individual mai exista undeva — -nu la S4c, dialogul dispare) cat si din handler-ul de grid — parametrizata pe rand -(`toArticol`/`Scatter Name`) in loc de `poArticol` global. Aceasta functie noua intra la fel ca -`calculeaza_totaluri()`, in `oproceduri_facturare.prg`, ca sa fie apelabila din handler-ul de coloana -fara sa depinda de `poArticol`-ul dialogului disparut. - -### 4.4 Coloana de procent — implementare recomandata - -Nu exista coloana Oracle de procent (sectiunea 1.3) si nici coloana persistenta pe `crsfactura` -pentru asta (spre deosebire de `discountftva` etc, care sunt in `creeaza_facturacrs`). Doua optiuni: -1. **Adauga camp calculat, needitabil, alaturi de coloana editabila de valoare** — afiseaza procentul - derivat (`Round(discountftva/pretftva*100,2)`), needitabil direct — evita sa se mai adauge o - coloana Oracle noua si o cale de editare in plus (mai putin cod nou, mai putina suprafata de bug). -2. **Adauga coloana editabila de procent, needitabila-pe-model** — ca in `frm_articol_factura` - (`Clb_procent_discount`), scrie tot in `discountftva`/`discountctva`/`vdiscountftva`/`vdiscountctva` - prin acelasi calcul reciproc, dar camp de UI, nu de Oracle. Mai aproape de comportamentul de azi - din dialog (operatorul poate tasta fie procentul, fie valoarea), dar cere si al treilea handler de - `LostFocus` si inca un camp de lucru in cursorul local (`AddProperty` la `do_initializeaza_articol`, - dupa tiparul `id_jtva_coloana`/`valdiminuatftva`, `:13666-13681`). - -Cerinta din plan ("cele doua campuri de discount ale lui — procent si valoare unitara — devin coloane -in grid") cere explicit **ambele campuri editabile** — deci optiunea 2 e cea care respecta litera -cerintei; optiunea 1 e o simplificare de discutat cu Marius (vezi sectiunea 10). - -### 4.5 Unde se cheama exact recalculul, pas cu pas (handler propus) - -Pentru coloana `cDiscountCTva` (`discountctva`, lei, `tip=2` in nomenclatura `do_calculeaza_discount`): -``` -PROCEDURE grd_factura.cDiscountCTva.Text1.LostFocus - Select crsfactura - Scatter Name loArt Memo - * recalcul reciproc procent<->valoare, tip=2 -- functia noua din 4.3, nu do_calculeaza_discount - loArt = recalc_discount_linie(loArt, This.Value, 2) && scrie discountftva/discountctva(_val) - loArt = calculeaza_totaluri(loArt) && scrie valdiminuat*/vvaldiminuat* - Gather Name loArt Memo - Thisform.do_calculeaza_totaluri() && resumeaza totalurile documentului -ENDPROC -``` -Simetric pentru `cVdiscountftva` (`tip=3`) si pentru coloana de procent, daca implementata editabil -(`tip=1`). Guard-ul `<> valoare veche` (ca la `Valid`-urile din dialog, `:2602-2624`) se pastreaza ca -sa nu se recalculeze la simplu tab-through fara modificare. - ---- - -## 5. Interactiunea cu discountul din politica de pret - -**Azi**: `do_initializeaza_articol` (`ofacturare.vc2:13618` si urm.) preia -`toArticol.discount_unitar = Nvl(toArticol.discount_unitar, 0)` (`:13630`) **din `crsarticole`** -(cursorul de stoc/oferta, populat inainte de deschiderea dialogului — sursa SQL exacta, in afara -`ofacturare.vc2`, ramasa necercetata si in raportul precedent). Daca politica de pret a populat deja -un discount, acela apare **preincarcat** in campurile dialogului (`Clb_discount_unitar` etc.), -operatorul il poate suprascrie tastand — suprascrierea intra prin acelasi `do_calculeaza_discount` -ca orice alta tastare, fara distinctie intre "valoare din politica" si "valoare tastata manual". - -**Dupa S4c**: nu se schimba nimic in mecanismul de preincarcare — `do_adauga_articol` inca scrie -`discountftva`/`discountctva`/`vdiscountftva`/`vdiscountctva` in `crsfactura` la adaugarea liniei -(sectiunea 1.2, pasul 4), inainte ca operatorul sa apuce sa editeze coloana de grid. Valoarea din -politica **ramane vizibila in celula**, exact ca azi in dialog, iar editarea manuala in grid o -suprascrie la fel — singura diferenta e ca suprascrierea se intampla acum pe `LostFocus` de celula, -nu pe `Valid` de camp de dialog. **Nu exista azi o distinctie de tip "flag discount din politica vs. -discount manual"** in `crsfactura` (nu am gasit un camp `discount_din_politica` sau similar) — deci -S4c nu pierde nicio informatie care exista deja, dar nici nu castiga vreo trasabilitate noua. - ---- - -## 6. Refacerea totalurilor documentului - -Totalurile documentului (`Thisform.nbazaron`, `ntotalron`, `ndiscron`, variantele `*val`) se -recalculeaza in `frm_facturare_articole.do_calculeaza_totaluri` (`ofacturare.vc2:13423-13520`) — -**re-sumeaza direct din `crsfactura`** (si `crsfacturaset` daca exista), citind exact campurile -agregate din sectiunea 1.1/1.4: -``` -13456 Select Sum(Nvl(valdiminuatctva,0)) As Total,Sum(Nvl(valdiminuatftva,0)) As Baza,; -13457 Sum(Nvl(valdiminuattva,0)) As Tva,; -13458 Sum(Nvl(valdiscountftva,0)) As discount From crsfactura Into Cursor crstotalurifact -``` -Simetric pentru valuta (`vvaldiminuat*`) mai jos in aceeasi procedura. **Consecinta directa pentru -proiectare**: daca handler-ul de coloana (sectiunea 4.5) actualizeaza corect `valdiminuatftva`/ -`valdiminuatctva`/`vvaldiminuatftva`/`vvaldiminuatctva`/`valdiscountftva`/`vvaldiscountftva` pe randul -editat **inainte** de a chema `Thisform.do_calculeaza_totaluri()`, totalurile documentului se refac -automat, corect, din acelasi mecanism care azi refece totalurile la adaugare/stergere de linie -(`do_adauga_articol:13160`, `do_sterge:14676`) — **nu trebuie cod nou pentru pasul de resumare**, doar -apelul, la finalul handler-ului de coloana. `Thisform.do_calculeaza_totaluri` e deja legat prin -`Bindevent` de `actualizeaza_total_mod` (`:15265`) — orice apel al lui declanseaza si actualizarea de -ecran a etichetelor de total, fara cablaj suplimentar. - ---- - -## 7. Pasi de implementare, ordonati, cu criteriu de "gata" - -1. **Extrage functia de calcul reciproc** din `frm_articol_factura.do_calculeaza_discount` - (`:1874-1976`), fara liniile de `Thisform.clb_*`/`Thisform.nprocent`, ca functie noua in - `oproceduri_facturare.prg`, parametrizata pe obiect + valoare + tip (semnatura similara cu - `calculeaza_totaluri(toArticol)`). - *Gata cand*: pentru fiecare din cele 3 `tnTip` si ambele ramuri `preturi_cu_tva`, functia noua - produce exact aceleasi `discount_unitar`/`discount_unitar_ctva`/`_val` ca metoda originala, testat - pe acelasi set de intrari (procent, lei, valuta) — comparatie directa, nu doar citire de cod. -2. **Deschide `Column5`/`cDiscountCTva` la editare** (`ReadOnly = .F.`, - `ofacturare.vc2:12311-12317`), pastrand `RemoveObject` pe valuta neschimbat. - *Gata cand*: pe factura in lei, celula de discount unitar cu TVA e editabila din grid (nu doar - afisata). -3. **Adauga handler `Text1.LostFocus` pe `cDiscountCTva` si pe `cVdiscountftva`**, dupa modelul din - sectiunea 4.5: recalcul reciproc (pasul 1) + `calculeaza_totaluri()` (deja existent, - `oproceduri_facturare.prg:2258`) + `Gather` + `Thisform.do_calculeaza_totaluri()`. - *Gata cand*: editarea oricareia din cele doua coloane schimba, in acelasi moment, si - `discountftva`/`discountctva`/`vdiscountftva`/`vdiscountctva` **si** - `valdiminuatftva`/`valdiminuatctva`/`vvaldiminuatftva`/`vvaldiminuatctva` pe randul curent, - verificat prin `Browse`/inspectie cursor, nu doar pe ecran. -4. **Adauga coloana de procent** (decizie 4.4 de confirmat cu Marius — recomandare: optiunea 2, - editabila, ca sa respecte litera cerintei din plan), cu acelasi handler, `tip=1`. - *Gata cand*: tastarea unui procent produce aceeasi valoare de discount ca tastarea valorii - echivalente, pe ambele monede. -5. **Verifica totalurile documentului** dupa editare in grid — nu ar trebui sa fie nevoie de cod nou - (sectiunea 6), doar de apelul din pasul 3. - *Gata cand*: dupa editarea discountului pe o linie, `Thisform.nbazaron`/`ntotalron` (si variantele - `val`) se schimba imediat, fara sa fie nevoie de o alta actiune (adaugare/stergere de linie) care - sa le forteze. -6. **Verifica scrierea in Oracle** (`do_scrie_articole`, `:14081-14083`) — nu ar trebui sa fie nevoie - de nicio schimbare, pentru ca citeste deja campurile pe unitate direct (sectiunea 1.3). - *Gata cand*: `VANZARI_DETALII.DISCOUNT_UNITAR` dupa salvare = valoarea tastata in grid, pe o - factura testata cu discount editat exclusiv din grid (fara sa fi trecut prin dialog, care oricum - dispare la unificare). -7. **Verifica tiparirea si eFactura** — cea mai importanta proba, cea ceruta explicit de plan. - *Gata cand*: vezi sectiunea 8. - ---- - -## 8. Cum se verifica — concret - -**Pasii**, pe o factura de test (lei si separat valuta): -1. Adauga o linie in grid (fara discount). -2. Editeaza direct in grid coloana de discount (valoare, apoi pe alt rand procent) — verifica pe - ecran ca celelalte coloane afisate (`cPretFtva`/`cVpretftva`, `cValdiminuatctva`/`cVvaldiminuatftva` - daca ramase vizibile) se actualizeaza imediat. -3. **Interogheaza direct cursorul** (nu doar ecranul) — din Command Window/breakpoint, pe randul - editat: `? discountftva, discountctva, vdiscountftva, vdiscountctva, valdiminuatftva, - valdiminuatctva, vvaldiminuatftva, vvaldiminuatctva` — toate opt trebuie sa reflecte editarea, nu - doar cele patru "brute". -4. **Finalizeaza factura** (`do_scrie_articole`) si interogheaza Oracle (schema `MARIUSM_AUTO`, - conventia din `COMUN\docs\scripturi-migrare-db.md:36-40`, prin `goExecutor`, doar `SELECT`): - ```sql - SELECT discount_unitar FROM vanzari_detalii WHERE id_vanzare = :id_factura AND id_articol = :id_articol; - ``` - trebuie sa fie egal cu valoarea tastata in grid. -5. **Retipareste factura** (nu doar priveste pe ecranul de compunere — deschide efectiv raportul, - `factura.fr2` sau echivalentul in lei/valuta) si verifica pe pagina tiparita ca `pretftva`/`valftva` - pe linia editata reflecta discountul nou (pretul unitar net si valoarea liniei trebuie sa fie - consistente intre ele: `valftva = pretftva * cantitate`, altfel exact bug-ul din sectiunea 1.4). -6. **Genereaza XML-ul de eFactura** pentru aceeasi factura si verifica in fisier - `cbc:LineExtensionAmount`/`cbc:PriceAmount` pe linia editata — trebuie sa corespunda cu pretul net - nou, nu cu cel dinaintea editarii. -7. Repeta 1-6 pe **factura in valuta**, ca sa acoperi ramura `vdiscountftva`/`vvaldiminuatftva` - (ramura azi cu bug-ul confirmat). - ---- - -## 9. Ce nu se poate testa headless - -- **Editarea propriu-zisa a celulei de grid** (tastare in `Text1` al unei coloane, tab/enter pentru - `LostFocus`) — headless-ul VFP (`-A -T`) nu materializeaza interactiunea de tastatura intr-un grid; - cf. `docs\cercetare\...grid-coloane-nu-se-materializeaza-headless` (memorie de proiect) — coloanele - de grid sunt artefacte needitabile sub `-A -T`; verificarea reala a comportamentului UI cere - harness-ul cu UI vizibil sau un test manual asistat. -- **Tiparirea efectiva a raportului** (`.frx`) — generarea unui PDF/preview real, nu doar constructia - cursorului `crsfacttemp` in memorie, cere motorul de raportare VFP, care nu ruleaza util headless - pentru verificare vizuala (se poate verifica campurile cursorului sursa, dar nu pagina tiparita). -- **Trimiterea efectiva catre ANAF** (SPV) — se poate genera si inspecta XML-ul local, dar validarea - reala (acceptare/respingere) cere mediul de test ANAF, in afara acestei sarcini. -- **Comportamentul focus/tab-order al noilor coloane** in grid (ordinea de tab intre celule, daca - `LostFocus` se declanseaza corect la navigare cu tastatura vs. mouse) — cere sesiune interactiva. - ---- - -## 10. Riscuri si ce ramane de decis de Marius - -- **Coloana de procent — editabila sau doar afisata** (sectiunea 4.4). Recomandare: editabila - (optiunea 2), pentru ca respecta litera deciziei din plan ("cele doua campuri... devin coloane"), dar - costa un handler si un camp de lucru in plus. Daca simplitatea conteaza mai mult decat paritatea cu - dialogul vechi, optiunea 1 (doar afisaj) reduce suprafata de cod fara sa piarda functionalitate reala - (operatorul tot poate obtine orice discount tastand valoarea). -- **Riscul de rotunjire in lant**: `do_calculeaza_discount` are cel putin 3 rotunjiri succesive pe - drumul procent->valoare->procent (sectiunea 2) — comportament deja existent, nu introdus de S4c, dar - mutarea in grid (editare mai frecventa, rand cu rand, fara sa mai treaca prin "confirmare" de - dialog) ar putea face vizibile discrepante mici de rotunjire care azi treceau neobservate. Nu e un - motiv sa se schimbe rotunjirile (ar strica alte fluxuri), doar un risc de UX de semnalat. - **Zero cazuri gasite in cod care sa demonstreze deja o problema** — semnalat preventiv, nu confirmat. -- **`Gather`/`Scatter` pe rand cu campuri MEMO**: `do_adauga_articol` foloseste `Gather ... MEMO` - (`:12945-12950`) — handler-ul nou de coloana (sectiunea 4.5) trebuie sa faca la fel - (`Scatter Name ... Memo` / `Gather Name ... Memo`), altfel campul `explicatie` (M) s-ar putea goli - la fiecare editare de discount — **de verificat explicit la implementare**, nu doar presupus. - Recomand un test dedicat: editeaza discountul pe o linie cu `explicatie` populata, verifica ca - `explicatie` ramane neschimbata dupa `LostFocus`. -- **Coloana `procdisc` din prototip** (`frm_facturare_articole2`, `:16721`) — scaffold mort, fara - scriere (sectiunea 3). Recomandare: nu se reutilizeaza ca atare pentru coloana noua de procent — - se porneste curat, cu functia noua din sectiunea 4.3, nu cu acest camp neconectat. -- **Formularul `frm_facturare_articole2`** ramane prototip separat, ne-instantiat in productie — S4c - nu are nevoie sa il atinga, dar daca exista intentia sa devina formularul unificat, coloanele lui - de discount (deja editabile, `:16657`/`:16688`) ar trebui auditate separat pentru acelasi bug de - recalcul (nu verificat aici — in afara perimetrului cerut). - ---- - -## 11. Punct deschis din S1 — `_checkbox1` vs `chkDetaliat` - -Nu apartine povestii S4c (nu am gasit nicio legatura cu discountul pe linie sau `grd_factura`), dar -am dat peste ambele controale pe cale laterala, in `frm_alte_date` (`ferestre_cere_date.vc2`), asa ca -inchid ieftin: **sunt doua controale distincte, nu o duplicare de nume**. -- `_checkbox1` e un control generic (mostenit din clasa de baza), folosit in `frm_alte_date.Init` - (`ferestre_cere_date.vc2:3188-3202`) doar ca **reper de layout** — pozitia lui determina inaltimea - ferestrei si offset-ul altor controale, fara logica de business proprie vizibila in acest formular. -- `chkDetaliat` e un checkbox cu nume propriu, legat de fluxul de incasare (`actualizeaza_tipincasare`, - `:2728-2853`) — vizibil doar cand `opt_incasat` indica un anumit tip de incasare (POS/detaliat), - ascuns/zero altfel; pe ramura `Else` a lui `Init` (`:3187-3205`, cand alt tip de context nu implica - incasare deloc) e **eliminat explicit** din formular (`Thisform.RemoveObject('chkDetaliat')`, - `:3201`), spre deosebire de `_checkbox1`, care ramane (e folosit chiar pe linia urmatoare pentru - calculul inaltimii finale, `:3202`). - -**Concluzie**: nu e o inconsecventa de cod, sunt doua controale cu roluri diferite pe acelasi -formular — punctul se poate inchide ca "nu e bug", cu rezerva ca n-am cercetat *de ce* `chkDetaliat` -exista ca si `checkbox` separat de `opt_incasat` (ce reprezinta exact "detaliat") — nu era in -perimetrul S4c si nu am aprofundat. - ---- - -## Necunoscute ramase (mostenite din rapoartele-sursa, nu re-investigate aici) - -- Interogarea SQL exacta care populeaza `crsarticole` cu discountul din politica de pret - (`CRM_POLITICI_PRET_ART`) — cod in afara `ofacturare.vc2`, netrasat (mostenit din - `discount_verificare2.md`, sectiunea "Necunoscute ramase"). -- Valorile implicite ale flag-urilor `gnEFACTURA_XML_DISC_PLISTA_LINIE`/`_ART` (mostenit din - `discount_in_rapoarte_si_efactura.md`). -- Comportamentul `crsfacturafinalaval` (varianta valuta a cursorului final de tiparire) — presupus - simetric cu varianta lei, nu reverificat linie cu linie separat pentru S4c. diff --git a/docs/cercetare/s4d_zi_curs_reactiv.md b/docs/cercetare/s4d_zi_curs_reactiv.md deleted file mode 100644 index 848b15e..0000000 --- a/docs/cercetare/s4d_zi_curs_reactiv.md +++ /dev/null @@ -1,429 +0,0 @@ -# Proiectare S4d — Data cursului valutar, numai cand are sens (reactiv) - -Poveste: `docs\plan_13_unificare_formular_facturare.md`, `#### S4d` (decizia 15, proiectata in -sectiunea M). Cercetare preliminara deja facuta si citata ca sursa de adevar: -`docs\cercetare\zi_curs_validare.md`, `COMUN\docs\cercetare\valuta_si_curs.md`, -`docs\cercetare\rec_dec42_proiectare.md`/`rec_d42_efactura.md` (decizia 42), -`docs\cercetare\s3_portare_antet.md` (bug #16), `docs\cercetare\s4_cautare_articole_server.md`/ -`s4_puncte_deschise.md` (decizia 42, mecanismul ei exact). Read-only: nicio editare de cod, niciun -`git_sync.ps1`/`txt2vcx.ps1`, nicio scriere pe Oracle. - ---- - -## Verdict (rezumat) - -Regula reactiva se poate implementa fara sa strice nimic din ce e deja demonstrat sigur: implicitul -`poDate.zi_curs` ramane neconditionat (nu se schimba), iar cheia reactivitatii e un camp deja prezent -in cursorul de grid — `crsfactura.tip_valuta` — verificabil cu exact acelasi tipar SQL folosit deja in -cod (`ofacturare.prg:1656`, `:1730`: `Select Distinct ... From crsfactura Where tip_valuta = 1`). -Formularul-prototip `frm_facturare_articole2` are deja, azi, un `Clb_zi_curs` propriu, editabil, legat -la `poDate.zi_curs` (`ofacturare.vc2:16285-16305`) — nu trebuie inventat un control nou, ci reevaluata -vizibilitatea celui care exista deja. Mesajul Oracle `-20005` **contine deja** data si numele valutei -lipsa (`STRINGAGG` peste toate valutele fara curs) — decizia 42 nu schimba *continutul* mesajului, ii -schimba **domeniul**: il restrange de la "toate valutele din listele de preturi ale utilizatorului" la -"valuta articolului cautat", dar **doar pe varianta filtrata a cautarii** (S4 punctul 1); pe caile cu -document sursa (comanda/aviz/contract) incarcarea in masa ramane neconditionata. Plan sectiunea M -(`:2635-2638`) cere explicit ca la aceasta eroare campul sa **revina vizibil** — asta intra ca pas de -implementare, nu ca optiune. Bug-ul #16 **nu e in perimetrul S4d** — e deja cercetat si legat de S2 -(bucla de reincercare din `ofacturare.prg`), cu verdict separat in `s3_portare_antet.md`; S4d doar -**confirma** ca noua arhitectura (antet persistent, nu recreat) ii inlatura mecanismul, daca S2/S3 nu -recreeaza formularul la eroare. - ---- - -## 1. Inventarul controalelor de valuta si curs (`fisier:linie`) - -Patru definitii `Clb_zi_curs` in tot `ofacturare.vc2` (control compus `clb_tx_data`, camp text legat -la `poDate.zi_curs`), confirmate exhaustiv (`grep "ADD OBJECT 'Clb_zi_curs'"`): - -| Formular | Definitie | Eliminat azi? | Validare la Termina | Sincronizare cu data | -|---|---|---|---|---| -| `frm_date_aviz` | `ofacturare.vc2:6727-6740` | Niciodata | Nu exista (fara `inainte_de_do_termin` care sa o ceara) | `Clb_dataact...LostFocus` (`:7603-7604`), `Clb_dataireg...LostFocus` (`:7610-7611`) — `Thisform.clb_zi_curs.Refresh()`, neconditionat | -| `frm_date_aviz_lucrare` | `:7787-7801` | Niciodata | **Neconditionata**: `:8076` `Case Empty(Nvl(poDate.zi_curs,{}))` -> `:8078 This.clb_zi_curs.SetFocus()`, `plReturn=.F.` | `:8186-8187`, `:8193-8194`, neconditionat | -| `frm_date_factura` | `:8701-8714` | **Doar** tip 8/9 (retur), `Init` `:9717-9722` (`RemoveObject`) | Conditionata: `:9484` `Case poDate.in_valuta=1 And Empty(...) And Type('thisform.clb_zi_curs.visible')<>'U'` -> `:9488 SetFocus` | `:9805-9808`, `:9824-9827`, ambele cu garda `Type(...)<>'U'` | -| `frm_facturare_articole2` (prototip) | `:16285-16305`, camp text legat la `poDate.zi_curs` (`TEXT_SIMPLU1.ControlSource`) | **Niciodata** — `Init` (`:18988-19080`) nu contine niciun `RemoveObject('clb_zi_curs')` | Nu exista (formularul de articole nu are `inainte_de_do_termin` propriu de tip antet) | `Clb_dataact.TEXT_SIMPLU1.LostFocus` (`:19219-19222`) — acelasi tipar cu garda `Type(...)<>'U'` | - -Alte controale de valuta/curs, relevante ca sa nu fie confundate cu `clb_zi_curs`: - -- **`Ct_clb_valuta`** (selector de valuta document) — eliminat pe `frm_date_factura` cand - `poDate.in_valuta=0` (`:9725-9728`), **neconditionat de tip**. Confirma ca "ascunde cand nu are sens" - e deja un tipar folosit in aceeasi metoda, la doar 3 linii distanta de blocul retur — dovada directa - ca autorul stia sa faca exact acest lucru, doar nu l-a aplicat si campului de curs. -- **`lb_cursuri` + `grd_cursuri`** (eticheta si grid read-only "Curs valutar (data)") — - `frm_facturare_articole.Init` (`:15092-15100`) si `frm_facturare_articole2.Init` (`:19000-19012`): - ``` - ofacturare.vc2:19000-19012 - If !Used('crscursuri') Or Reccount('crscursuri') = 0 - Thisform.RemoveObject('lb_cursuri') - Thisform.RemoveObject('grd_cursuri') - Else - If !Empty(Nvl(poDate.zi_curs, {})) - Thisform.lb_cursuri.Caption = "Curs valutar " + Iif(!Inlist(poDate.tip, 8, 9), "(" + Dtoc(poDate.zi_curs) + ")", "") - ... - ``` - Acesta e **cel mai apropiat precedent de "arata/ascunde in functie de continut"** din codul existent - — dar evalueaza `crscursuri` (cursurile incarcate pentru toate valutele din listele de preturi ale - utilizatorului), **o singura data, la `Init`**, inainte ca userul sa fi adaugat vreo linie pe grid. - **Nu e reactiv** la adaugare/stergere de articole — e evaluat o singura data pe un cursor diferit de - `crsfactura`. Nu se poate copia ca atare pentru S4d; poate fi reutilizat doar ca **idiom** (verificare - `Reccount(...) > 0` care decide `RemoveObject`/reafisare), mutat pe alt cursor si alt eveniment - (punctul 2). -- **`poArticol.tip_valuta`** — proprietate a politicii de pret a articolului (nu a documentului), - populata la `do_initializeaza_articol`/cautare, citita masiv in calculul de pret/discount - (`ofacturare.vc2:1857-2288`, `frm_articol_factura`). Cand articolul e adaugat pe grid, valoarea - ajunge in coloana `crsfactura.tip_valuta` (vezi punctul 2) — acesta e semnalul pe care se construieste - conditia reactiva, nu `poArticol` (obiect temporar, mort dupa `Release`). - -**Cine citeste `poDate.zi_curs` dupa formular** (relevant ca sa nu se rupa nimic la ascundere) — deja -documentat exhaustiv in `zi_curs_validare.md` §4 si `valuta_si_curs.md` §4: cursoarele de articole -(`cursor_preturi`/`cursor_articole_k`/`cursor_gestiune`/`cursor_lucrare`, `ofacturare.prg:266-308`), -eticheta `lb_cursuri`, si validarile de mai sus. Nimic din acest tabel se schimba prin S4d. - ---- - -## 2. Conditia reactiva, exact - -**Camp folosit:** `crsfactura.tip_valuta` (N(1)), definit la creearea cursorului de grid, -`creeaza_facturacrs` (`COMUN\programe\ofacturare_comun.prg:1776`), populat la fiecare linie adaugata -prin `do_adauga_articol` (coloana e in lista de `Insert`/`Scatter`, `ofacturare.vc2:12950`, -`:17258` pentru `frm_facturare_articole2`) direct din `poArticol.tip_valuta`. - -**Verificare, cu exact acelasi tipar deja folosit in productie** (nu inventat): - -``` -ofacturare.prg:1656, :1730 [listeaza_ofacturare] -Select Distinct nume_val, Curs, multiplicator From crsfactura Where tip_valuta = 1 -``` - -Pentru S4d, verificarea de vizibilitate se reduce la: - -```foxpro -lVizibil = !Inlist(poDate.tip, 8, 9) And ; - (poDate.in_valuta = 1 Or (Used('crsfactura') And Reccount('crsfactura', 1) > 0) ) -``` - -unde `Reccount('crsfactura', 1)` inseamna "exista macar un rand cu `tip_valuta=1`" — in practica se -scrie ca `Calculate Cnt() To lnLinii For tip_valuta=1` sau `Select Count(*) From crsfactura Where -tip_valuta=1 Into Array laCnt`, ca sa nu depinda de pozitia recordului curent din grid. - -**Cand se reevalueaza — pe evenimentul deja existent de recalcul, nu pe un timer nou.** Atat -`do_adauga_articol` (`:17124-17393`) cat si `do_sterge` (`:18619-18704`) se termina cu apelul -**`Thisform.do_calculeaza_totaluri()`** (`:17360`, `:18687`) — acesta e deja punctul unic prin care -formularul "stie" ca s-a schimbat compozitia liniilor (totaluri, discount pe articole etc). E locul -natural unde se adauga si reevaluarea `clb_zi_curs`: dupa fiecare adaugare de linie **si** dupa fiecare -stergere, `do_calculeaza_totaluri` (sau un apel adaugat imediat dupa el in cele doua metode) reface -`lVizibil` de mai sus si seteaza `Thisform.clb_zi_curs.Visible = lVizibil`. - -**Modificarea unei linii existente** (schimbare cantitate/pret pe o linie deja adaugata) nu schimba -`tip_valuta` — acesta e o proprietate a politicii de pret, fixata la adaugare, nu editabila pe grid. -Deci nu exista eveniment suplimentar de "editare linie" care sa afecteze conditia — doar adaugare si -stergere pot muta numarul de linii cu `tip_valuta=1` de la 0 la >0 sau invers. (Verificat: nu exista -`do_modifica` separat pe `frm_facturare_articole2` in indexul de simboluri — editarea unei linii merge -prin acelasi `do_adauga_articol`/dialog, care oricum recheama `do_calculeaza_totaluri`.) - -**De ce nu `poDate.in_valuta`-style (o singura evaluare la Init):** documentul poate incepe fara nicio -linie in valuta si poate primi una la mijlocul sesiunii de facturare — exact cazul pe care unificarea -il face posibil de rezolvat (azi antetul se inchide inainte sa existe `crsfactura`). Evaluarea trebuie -sa fie pe eveniment, nu pe `Init`. - ---- - -## 3. Trecerea inapoi — ultimul articol in valuta e sters - -**Recomandare: ascunde din nou (simetric), dar NU goli `poDate.zi_curs`.** - -Argumente pe tiparul deja folosit, nu pe teorie: - -- `lb_cursuri`/`grd_cursuri` (punctul 1) folosesc deja idiomul "vizibil doar cand `Reccount(...) > 0`" - — o conditie booleana simpla, reevaluabila oricand fara efecte laterale, pentru ca `RemoveObject`/ - re-adaugare (sau `Visible=`) nu ating proprietatea de date din spate (`poDate.zi_curs`). Simetria - (ascunde la 0, arata la >0) e deja tratata ca stare normala pentru acel control, nu ca o exceptie de - construit. -- `poDate.zi_curs` **nu se goleste niciodata** in tot codul existent (confirmat exhaustiv in - `zi_curs_validare.md` §5-6) — nici la `RemoveObject` (tip 8/9), nici la ascunderea `lb_cursuri`. Deci - ascunderea campului de curs cand ultima linie in valuta dispare **nu pierde valoarea**: daca userul - adauga din nou o linie in valuta in aceeasi sesiune, campul reapare cu **aceeasi data** pe care o avea - inainte de ascundere (fie cea introdusa manual, fie implicitul de la `Init`), nu resetata la azi. -- Alternativa ("ramane vizibil o data aratat") ar introduce un tip nou de stare per-sesiune - (`lFostVizibilCandva`) fara niciun precedent in cod si fara beneficiu clar — userul care sterge - singura linie in valuta de pe o factura in lei nu mai are, de fapt, niciun motiv sa vada/editeze - cursul; a lasa campul vizibil ar fi exact inconsistenta pe care decizia 15 vrea sa o elimine. - -**Exceptie de la simetrie, ceruta de plan (sectiunea M, `:2635-2638`):** cand vine eroarea Oracle -`-20005` cu campul ascuns, campul **trebuie adus inapoi vizibil**, indiferent de `Reccount`-ul curent — -vezi punctul 5. Acesta e singurul caz in care regula reactiva simetrica se suspenda explicit. - ---- - -## 4. Interactiunea cu `in_valuta` la nivel de document - -`poDate.in_valuta` e stabilit o singura data, in `Init`, exclusiv din `tnTip`/`tnIdSet` -(`ofacturare_comun.prg:248-250`, confirmat exhaustiv in `valuta_si_curs.md` §1) — **nu exista cale de -cod care sa-l schimbe dupa Init**, iar controlul de alegere a valutei (`Ct_clb_valuta`) e chiar -eliminat din formular cand `in_valuta=0` (`ofacturare.vc2:9725-9728`), deci operatorul nu are de unde -sa-l aleaga manual. Prin urmare **intrebarea "ce se intampla daca operatorul schimba valuta documentului -dupa ce a adaugat linii" nu se poate pune in arhitectura actuala** — nu exista control care sa permita -acea schimbare. Documentul e in valuta sau nu inca de la alegerea tipului (`tnTip`), inainte ca vreo -linie sa existe. - -Pentru S4d, consecinta e simpla: `poDate.in_valuta=1` e o conditie **statica** pe toata durata sesiunii -de facturare — cand e adevarata, `clb_zi_curs` ramane vizibil necontenit (ca azi), indiferent de ce se -intampla pe grid; conditia reactiva descrisa la punctul 2 conteaza **doar** pentru ramura -`in_valuta=0`. Nu exista tranzitie `in_valuta 0->1` sau `1->0` de tratat. - ---- - -## 5. Mesajul de eroare `-20005` - -### 5.1 Unde se ridica azi - -`pack_facturare.verifica_cursuri_valute` (Oracle, `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:16247-16274`): - -```sql -16268 IF V_NUME_VALUTE IS NOT NULL THEN -16269 RAISE_APPLICATION_ERROR(-20005, -16270 'Nu este setat cursul din data de ' || -16271 to_char(V_DATA_CURS, 'DD/MM/YYYY') || -16272 ' pentru ' || V_NUME_VALUTE || '!'); -16273 END IF; -``` - -`V_NUME_VALUTE` e un `STRINGAGG` (`:16252-16266`) peste **toate** valutele din -`FACT_VPRETURI_UTILIZATOR` ale utilizatorului curent care nu au curs valabil la `V_DATA_CURS` — exclude -doar moneda nationala. **Mesajul contine deja data si numele valutei/valutelor** — nu e un cod generic. -Apelata neconditionat din `cursor_preturi` (`:2153`), si delegat din `cursor_contract` (jumatatea -`crsarticole`, `s4_cautare_articole_server.md:83-85`). - -### 5.2 Traseul pana la operator, azi - -``` -ofacturare.prg:313-317 (identic ofacturare.prg:828-832, factureaza2) -If lnSucces < 0 - AMESSAGEBOX(goExecutor.oPrelucrareEroare(), 16, "Eroare") - If goExecutor.nEroare = 20005 - vizualizeaza_curs(poDate.zi_curs) - ENDIF -``` - -`goExecutor.oPrelucrareEroare()` (`COMUN\programe\oproceduri_comune.prg:626-639`) extrage **textul -brut** dintre `ORA-20xxx:` si urmatorul `ORA` din mesajul Oracle — pentru erori in intervalul -20000-20999 (cazul de aici), operatorul vede **exact** textul PL/SQL de mai sus, netrunchiat, nemodificat. -Deci raspunsul la "mesajul spune care valuta si ce zi lipsesc" e: **da, deja o face**, azi, inainte de -orice modificare S4d. - -### 5.3 Ce schimba decizia 42 - -Decizia 42 (plan `:512-516`) **nu schimba textul** mesajului — schimba **domeniul de valute verificate**, -si **doar pe varianta filtrata a cautarii** (S4 punctul 1, `cursor_preturi` chemat per-articol-cautat, -nu la incarcarea in masa). Azi (fara decizia 42), pe orice apel al lui `cursor_preturi`, -`V_NUME_VALUTE` poate include valute complet nelegate de articolul pe care userul tocmai il cauta — -de exemplu userul cauta un articol in RON, dar mesajul ii spune ca lipseste cursul pentru EUR, pentru -ca EUR apare undeva in politicile lui de pret. Dupa decizia 42, pe cautarea filtrata, verificarea se -restrange la valuta randului adus — deci mesajul (acelasi format text) va numi, natural, **doar -valuta relevanta pentru cautarea curenta**, nu un agregat strain. - -**Rezerva importanta, de citit impreuna cu S4d:** decizia 42 se aplica explicit "pe varianta filtrata -a cursoarelor" — adica pe calea noua de cautare per-articol introdusa de S4 punctul 1, folosita azi -doar pe **ramura de lista de preturi** (`s4_cautare_articole_server.md:68-69`: "S4 se aplica curat doar -pe ramurile de lista de preturi"). Pe **comanda / aviz / contract**, incarcarea in masa a articolelor -(bookkeeping obligatoriu, `crsarticole` citit si scris de `do_adauga_tot`/`do_sterge`/`do_scrie_factura`) -**ramane neschimbata**, deci `verifica_cursuri_valute` tot ruleaza neconditionat (toate valutele din -politicile utilizatorului), la incarcarea initiala a grilei — nu doar pe articolul cautat. **Pe aceste -tipuri, mesajul poate inca numi o valuta care n-are legatura cu documentul curent**, indiferent de -decizia 42 — asta ramane o diferenta reala fata de "campul e ascuns pentru ca documentul nu are nevoie -de curs", si intra la riscuri (punctul 10). - -### 5.4 Ce se schimba prin S4d — nu textul, ci vizibilitatea campului dupa eroare - -Cerinta explicita din plan, sectiunea M (`:2635-2638`): - -> Ascunderea datei de curs poate lasa un document fara curs corectabil [...] 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.** - -Implementare propusa, la punctul de interceptare deja existent: - -```foxpro -* ofacturare.prg:313-317 (si simetric la :828-832) -If lnSucces < 0 - AMESSAGEBOX(goExecutor.oPrelucrareEroare(), 16, "Eroare") - If goExecutor.nEroare = 20005 - If Type('thisform.clb_zi_curs.visible')<>'U' And !thisform.clb_zi_curs.Visible - thisform.clb_zi_curs.Visible = .T. && aduce campul inapoi, indiferent de conditia reactiva - Endif - vizualizeaza_curs(poDate.zi_curs) - ENDIF -``` - -Nota: `thisform` aici nu e formularul de antet (deja inchis la acest punct in arhitectura veche) — e -motivul pentru care acest fix e legat de rezultatul S3/S2 asupra bug-ului #16 (punctul 6): daca -antetul unificat ramane deschis/persistent (nu recreat), `thisform.clb_zi_curs` exista si poate fi -readus vizibil direct; daca arhitectura tot recreaza un formular nou dupa eroare, readucerea vizibila -trebuie facuta in `Init`-ul noii instante, verificand acelasi semnal (`goExecutor.nEroare=20005` din -iteratia anterioara, sau un flag explicit propagat). - -**Textul mesajului propus de pastrat neschimbat** (deja corect): *"Nu este setat cursul din data de -DD/MM/YYYY pentru !"*. Nu se propune inlocuirea lui — se propune doar sa nu mai fie surprinzator -faptul ca operatorul nu are unde sa corecteze, prin readucerea campului. - ---- - -## 6. Verificarea daca #16 dispare - -**Ce e #16:** `COMUN\docs\todos.txt:45` — "la revenire din formularul de curs valutar, focusul revine -[...] pe TIP DOCUMENT, si la iesire din serie se regenereaza numar act". Citat si in plan -`:1926-1927`, `:2665-2668`. - -**Deja cercetat, cu verdict**, in `docs\cercetare\s3_portare_antet.md` §6 (runda 9), **inainte** de -aceasta sesiune — S4d nu redeschide cercetarea, o **confirma si o leaga** de domeniul propriu (eroarea -`-20005`, singurul declansator relevant pentru S4d): - -- **#16 nu e un bug de focus.** E o bucla de reincercare (`ofacturare.prg:174-571`, `Do While - lnRaspuns=6`) care trateaza **orice** esec Oracle la incarcarea cursorului de articole ca pe un "DA, - utilizatorul vrea alt document" — `lnRaspuns` nu se reseteaza pe ramura de eroare - (`ofacturare.prg:555-564`, in `Else`, niciodata atinsa cand `lnSucces<0`), deci bucla externa reintra - automat si **recreeaza formularul de antet de la zero** (`Createobject`, `:230`). `Init`-ul noii - instante seteaza focus neconditionat pe `ct_clb_fdoc` (`:9788-9793`) — asta e "focusul care revine pe - tip document" — si `LostFocus`-ul care urmeaza cheama neconditionat `clb_serie_act`, care aloca un - numar nou (`serii_numere.vc2:122-127`) — asta e "regenerarea numarului". -- **Cauza reala e in `ofacturare.prg` (bucla de emitere, cod comun suitei), nu in formularul de antet.** - `s3_portare_antet.md:337-350` e explicit: portarea antetului (S3) NU rezolva automat #16 — arhitectura - unificata (un singur `Init` persistent, antetul nu mai e un obiect separat distrus/recreat de apelant) - **are sansa reala** sa-l elimine ca efect secundar, **dar numai daca implementarea nu recreaza - formularul intreg la reincercare dupa o eroare Oracle** — ceea ce reproduce bugul identic, doar mutat. - Verdictul e legat explicit de **S2**, nu de S3/S4d: bucla traieste in procedura de emitere, nu in - formular. - -**Raspunsul specific S4d, cerut de plan** ("Verifica in acelasi timp daca #16 dispare — vezi M"): -**da, S4d confirma exact scenariul care declanseaza #16** — eroarea `-20005` de la -`verifica_cursuri_valute` e unul dintre cazurile `lnSucces<0` care intra pe ramura problematica a -buclei (`vizualizeaza_curs(poDate.zi_curs)` la `:315`, chiar linia care deschide `frm_curs`, exact -recuperarea citata in reclamatia originala). Deci daca S2/S3 rezolva #16 (antet persistent, fara -recreare), **S4d beneficiaza direct** — dupa `-20005`, userul revine in acelasi antet unificat -(nu unul nou), campul `clb_zi_curs` readus vizibil (punctul 5.4) ramane exact acolo unde a fost adus -inapoi, fara sarituri de focus si fara renumerotare. Daca S2/S3 NU rezolva #16 (recreare inca prezenta), -S4d **nu il agraveaza si nu il repara** — comportamentul ramane identic cu azi pe acest punct, dar -readucerea campului vizibil (5.4) trebuie facuta in `Init`-ul noii instante, nu presupusa mostenita. - -**Concluzie:** #16 **nu e in perimetrul de implementare al S4d** (e S2, per plan `:1944`), dar S4d -**depinde de rezultatul lui** pentru UX-ul complet al recuperarii de eroare (5.4) — de marcat explicit -ca dependenta la implementare, nu de reinvestigat. - ---- - -## 7. Pasi de implementare, ordonati - -Fiecare pas lasa suita functionala; calea veche (`frm_date_factura`/`frm_facturare_articole2` cum sunt -azi) nu se sterge in etapa I. - -1. **Adauga functia de evaluare a conditiei reactive** pe formularul unificat (metoda noua, de ex. - `do_actualizeaza_vizibilitate_curs`), care calculeaza `lVizibil` conform formulei din punctul 2 si - seteaza `Visible` pe controlul `clb_zi_curs` mostenit din prototipul `frm_facturare_articole2` - (`ofacturare.vc2:16285-16305`) — **nu un control nou**, cel existent, ale carui evenimente de - sincronizare (`Clb_dataact...LostFocus`, `:19219-19222`) raman neschimbate (deja garda pe - `Type(...)<>'U'`, deci tolereaza si eliminare, nu doar `Visible=.F.`). - *Gata cand:* metoda exista, se poate apela manual (fara UI), si intoarce `.T.`/`.F.` corect pe cele - patru combinatii din punctul 8 (tip retur / nu, `in_valuta` 0/1, cu/fara linie `tip_valuta=1`). -2. **Cheama metoda din pasul 1 la sfarsitul `do_adauga_articol` si `do_sterge`**, imediat dupa (sau in) - `Thisform.do_calculeaza_totaluri()` (`:17360`, `:18687` in prototip — liniile echivalente in - formularul unificat, dupa portarea din S3/S4). - *Gata cand:* adaugarea unei linii cu `tip_valuta=1` pe o factura in lei fara alte linii de acest fel - face campul vizibil imediat, fara Refresh manual; stergerea ultimei asemenea linii il ascunde din nou - (simetric, punctul 3), fara sa goleasca `poDate.zi_curs`. -3. **Verifica initializarea la deschiderea formularului** (cazul `in_valuta=1` sau document reincarcat - cu linii deja existente, ex. la editare factura emisa) — `Init`-ul unificat trebuie sa apeleze aceeasi - metoda o data, dupa ce `crsfactura` e populat, nu doar sa se bazeze pe evenimentele de adaugare/ - stergere (care nu ruleaza la incarcarea initiala a unui document existent). - *Gata cand:* deschiderea unei facturi existente (in lei, cu linii in valuta deja salvate) arata - campul corect de la primul `Show()`, fara sa fie nevoie de o adaugare/stergere care sa-l declanseze. -4. **Elimina campul neconditionat pe retur (tip 8/9)**, ca azi — verifica ca formula din pasul 1 include - deja `!Inlist(poDate.tip,8,9)` inaintea oricarei alte conditii, ca sa nu-l readuca vizibil din greseala - printr-o linie in valuta pe un retur (desi returul nu foloseste `zi_curs`, vezi `zi_curs_validare.md` - §4c/§6 — comportamentul trebuie sa ramana identic azi, nu doar "fara efect"). - *Gata cand:* pe orice tip 8/9, campul e absent indiferent de continutul grilei. -5. **Readu campul vizibil la eroarea `-20005`** (punctul 5.4) — modifica ramura `goExecutor.nEroare=20005` - din bucla de emitere (`ofacturare.prg:313-317`/`:828-832`, sau echivalentul ei in noua arhitectura - integrata cu formularul unificat, cf. S3 pasul 6) ca sa forteze `Visible=.T.` pe `clb_zi_curs` inainte - de a deschide `frm_curs`. - *Gata cand:* pe o factura in lei cu campul ascuns, o eroare `-20005` simulata (curs lipsa de test) - face campul vizibil imediat dupa mesaj, inainte sau odata cu deschiderea `frm_curs`. -6. **Coordoneaza cu decizia 42 (S4 punctul 1)**: cand acea poveste implementeaza restrangerea - `verifica_cursuri_valute` la valuta articolului cautat, verifica ca textul afisat prin - `oPrelucrareEroare()` (punctul 5.1-5.2, neschimbat) tot numeste corect valuta si data — nu necesita - modificare pe partea VFP, doar confirmare ca noul parametru Oracle e transmis corect din calea de - cautare filtrata. - *Gata cand:* pe cautarea filtrata (dupa decizia 42), mesajul `-20005` numeste doar valuta cautata, nu - un agregat. -7. **Regresie pe formularul vechi**: confirma ca niciuna din modificarile 1-6 nu atinge - `frm_date_factura`/`frm_facturare_articole` (calea veche, ramasa in productie) — toate modificarile - se fac pe clasele formularului unificat / prototip, nu pe cele originale. - *Gata cand:* `svn diff`/`git diff` arata modificari doar in clasele noii cai. - ---- - -## 8. Cum se verifica - -Pe probele din plan (`:2243-2246`), plus cazurile suplimentare care rezulta din punctele 2-6: - -1. **Factura in lei, fara articole in valuta** — `clb_zi_curs` nu apare la deschidere; documentul se - emite corect (acelasi rezultat `VANZARI`/`ACT`/`RUL` ca azi, criteriul din S3 §7). -2. **Aceeasi factura, dupa adaugarea unui articol cu pret in valuta** — campul apare imediat, cu data - implicita deja completata (data documentului, de la `Init`/`Reset`, nemodificata). -3. **Stergerea articolului din pasul 2** (singurul cu `tip_valuta=1`) — campul dispare din nou; valoarea - din `poDate.zi_curs` ramane cea introdusa/implicita (verificabil citind proprietatea direct, nu doar - vizual). -4. **Readaugarea unui alt articol in valuta, in aceeasi sesiune** — campul reapare cu **aceeasi** data - ca la pasul 2, nu resetata. -5. **Factura in valuta (`in_valuta=1`)** — campul apare mereu, indiferent de continutul grilei, exact ca - azi (fara regresie pe cazul deja functional). -6. **Retur (tip 8/9)** — campul absent indiferent de linii; document salvat corect (cf. precedent deja - existent). -7. **`-20005` cu campul ascuns** (factura in lei, curs de test lipsa pentru o valuta din politicile - utilizatorului) — mesajul numeste valuta si data lipsa (deja azi); campul devine vizibil dupa eroare. -8. **Editare factura emisa** (cf. #6, formular deja in lucru separat) — deschiderea unei facturi in lei - cu linii in valuta deja salvate arata campul corect de la primul `Show()` (pasul 3 de implementare). -9. **Dupa decizia 42** — cautarea filtrata a unui articol cu curs lipsa arata mesaj cu o singura valuta - (a articolului cautat), nu un agregat. - ---- - -## 9. Ce nu se poate testa headless - -- **Focusul si secventa reala `SetFocus`/`LostFocus`/`Visible=`** pe controale UI reale — capcana deja - cunoscuta (`grid-coloane-nu-se-materializeaza-headless`, memorie de proiect): sub harness `-A -T`, - proprietatile de grid/vizibilitate nu se materializeaza identic cu UI vizibil. Verificarea - vizibilitatii reactive (punctele 2-3) trebuie facuta fie prin verificare directa a proprietatii - `.Visible` dupa apelul metodei (fara `Show()`), fie cu `vfp_ui_harness.ps1`, UI vizibil. -- **Eroarea Oracle `-20005` reprodusa real** — necesita fie date de test cu un curs lipsa garantat pe o - fereastra de date controlata (manipulare de date, nu de UI), fie mock pe `goExecutor` care simuleaza - `nEroare=20005` fara conexiune reala — niciuna verificata ca exista deja in suita de teste. -- **Deschiderea modala a `frm_curs`** (`vizualizeaza_curs`) — `Show(1)` modal, aceeasi limitare generala - a testarii headless pe formulare modale. -- **Bug #16 propriu-zis** (secventa reala de focus dupa recreare de formular) — deja marcat netestabil - headless in `s3_portare_antet.md` §8; S4d nu adauga o cale noua de testare, doar depinde de acelasi - rezultat. - ---- - -## 10. Riscuri si de decis de Marius - -1. **Incarcarea in masa pe comanda/aviz/contract ramane neconditionata dupa decizia 42** (punctul 5.3). - Pe aceste tipuri, `-20005` poate inca numi o valuta nelegata de documentul curent, chiar daca `S4d` - ascunde corect campul pe baza continutului grilei. **Recomandare:** de discutat daca extinderea - restrangerii decizia-42-style merita si pe incarcarea in masa (poveste separata, posibil in S4 sau - intr-o continuare a deciziei 42), sau se accepta ca diferenta cunoscuta, documentata aici. -2. **Cine seteaza `Visible=.T.` la `-20005` cand antetul ar fi fost recreat** (daca #16 nu se rezolva - complet in S2/S3 pana la implementarea S4d) — punctul 5.4/6 presupune `thisform` = acelasi formular - persistent; daca arhitectura finala tot recreaza o instanta, logica trebuie mutata in `Init`, cu un - semnal explicit propagat (ex. proprietate `poDate.lCursLipsa` sau echivalent) ca sa stie noua instanta - sa arate campul. **Recomandare:** de tratat ca parte a pasului 6 din `s3_portare_antet.md` (integrarea - buclei de emitere), nu izolat in S4d. -3. **`frm_date_aviz`/`frm_date_aviz_lucrare` (tipurile 27, 30) nu intra in perimetrul imediat** — daca - formularul unificat le preia mai tarziu, validarea neconditionata de la `:8076` trebuie aliniata la - aceeasi regula reactiva (azi ramane necondiționata, per `zi_curs_validare.md` §6). **Recomandare:** nu - se atinge acum; se trateaza cand/daca acele tipuri intra in unificare. -4. **Simetria "ascunde la 0 linii" (punctul 3) e o recomandare, nu o certitudine ceruta explicit de - plan** — planul cere doar "apare cand...", nu specifica explicit comportamentul la disparitia ultimei - linii. **Recomandare:** simetria propusa aici (argumentata pe tiparul `lb_cursuri`), dar de confirmat - cu Marius inainte de implementare, pentru ca schimba usor experienta (campul "clipeste" la - adaugare/stergere repetata a aceleiasi linii). diff --git a/docs/cercetare/s4e_lista_preturi_pe_sursa.md b/docs/cercetare/s4e_lista_preturi_pe_sursa.md deleted file mode 100644 index a79e294..0000000 --- a/docs/cercetare/s4e_lista_preturi_pe_sursa.md +++ /dev/null @@ -1,444 +0,0 @@ -# S4e — Lista de preturi disponibila si pe factura din comanda - -Proiectare pe cod, READ-ONLY (fara editari, fara `git_sync.ps1`/`txt2vcx.ps1`, fara commit, fara -scriere Oracle — numai `SELECT`), pentru povestea **S4e** din -`docs\plan_13_unificare_formular_facturare.md:2249-2267` (decizia 16, text integral in sectiunea J -a planului). Nu se ating `COMUN\clase\ofacturare_comun.vc2` si `COMUN\programe\ofacturare_editare.prg` -(perimetrul altei sarcini in lucru; `COMUN\programe\ofacturare_comun.prg` si -`COMUN\programe\ofacturare.prg` sunt citite, nu editate). Decizia 29 (stergerea unei linii din comanda -ramane fara protectie) nu se reargumenteaza. - -**Status: cercetare incheiata.** - ---- - -## Verdict (esenta, pentru cine nu citeste tot) - -**Reteta literal citata in decizia 16 — `APPEND FROM` la `ofacturare.prg:454-473`, care lipeste -lista de preturi peste `crsarticole` — nu trebuie generalizata pe comanda ca atare. E nesigura acolo, -pe cod, nu doar teoretic.** `crsarticole` e simultan grid-sursa **si** registrul de cantitate ramasa -citit/scris de `do_sterge` si `do_scrie_factura` (decizia 39, S4 punctul 2). Doua motive concrete, -verificate pe cod si pe SQL exportat: - -1. **Coliziune de `id_c`.** Fiecare procedura `cursor_*` din `PACK_FACTURARE` numeroteaza `id_c` cu - `ROWNUM`, pornind de la 1, **independent** de orice alta executie. `cursor_comanda` (liniile - comenzii) si `cursor_preturi` (lista de preturi) ar produce, executate separat si apoi lipite - prin `APPEND FROM`, doua seturi de `id_c` care se suprapun (1, 2, 3…). `do_sterge` potriveste - randul de sters in `crsarticole` **prin `id_c`** (`ofacturare.vc2:14652-14655`, - `:14658-14659`) — o coliziune ar face ca stergerea unei linii libere sa modifice cantitatea - ramasa a unei linii de comanda complet diferite (sau invers), tacut, fara eroare. - Codebase-ul insusi cunoaste acest risc: `cursor_contract` (`PACK_FACTURARE:2722`, - `SELECT rownum - 10000 as id_c ...`) foloseste deliberat un offset ca sa nu se suprapuna cu - spatiul de `id_c` al celuilalt cursor din acelasi apel. -2. **Poluarea registrului.** `do_scrie_factura` face `Calculate Sum(cantitate) To lnCantitateRamasa` - peste **tot** `crsarticole` ca sa decida daca se ofera inchiderea automata a comenzii - (`ofacturare.vc2:14332-14338`). Coloana `cantitate` din `cursor_preturi` **nu inseamna "ramas de - facturat"** — inseamna **stoc disponibil** (`NVL(C.CANTITATE,0)`, derivat din miscari de stoc, - `PACK_FACTURARE:2276-2280` si similar pe fiecare ramura). Amestecarea celor doua ar face ca suma - folosita pentru decizia de inchidere sa includa cantitati de stoc fara nicio legatura cu ce a mai - ramas de facturat din comanda — decizia de inchidere automata ar deveni gresita, tacut. - -**De ce merge pe contract fara aceste probleme**: pe contract, lista de preturi **nu intra niciodata -in acelasi cursor** cu articolele contractului. `cursor_contract` intoarce **doua** cursoare Oracle -separate (`V_CURSOR` = `cursor_preturi`, mapat pe `crsarticole`; `V_CURSOR2` = articolele/ratele -contractului, mapat pe `crsarticole1`, cu `id_c` offset `-10000`) si **niciun `do_scrie_factura` -nu calculeaza `Sum(cantitate)` pe tipurile de contract** (2,6,26,52) — acel `Do Case` nu are ramura -pentru ele, cad in `Otherwise`, fara nicio logica de inchidere automata. Contractul "merge" pentru ca -lista de preturi traieste azi acolo unde nu exista niciun registru de citit. - -**Solutia curata pentru comanda, verificata ca fezabila pe cod**: liniile libere **nu intra deloc in -`crsarticole`**. Mecanismul de adaugare a lor e cel construit de **S4** — cautare filtrata pe server -(`cursor_preturi` cu parametru de filtru, `docs\cercetare\s4_cautare_articole_server.md`, punctul 4) -legata printr-un `combosql`/`APPEND BLANK` **direct pe `crsfactura`** (exact tiparul deja existent in -`frm_facturare_articole2.But_nou1.do_adauga`, `ofacturare.vc2:17118-17122`: `Select crsFactura / -APPEND BLANK`), nu prin `do_adauga_articol`/`Scatter` dintr-un cursor sursa. Fiindca linia nu vine -niciodata dintr-un rand real al lui `crsarticole`, `crsfactura.id_c` ramane la valoarea implicita a -campului (`N(20)`, fara `Null`, deci `0` la `APPEND BLANK`) — **valoare pe care Oracle nu o produce -niciodata** (`ROWNUM` porneste de la 1), deci `do_sterge`-ul de azi (`For id_c = poArticol.id_c`) e -deja, prin constructie, un no-op sigur pe o linie libera, **fara nicio modificare de cod in -`do_sterge`**. Precedentul exista deja in acelasi fisier: linia de discount adaugata la -`ofacturare.vc2:14531-14537` (`Append Blank` pe `crsfactura`, fara `id_c`) foloseste exact acest -tipar azi, in productie. - -Ramane un gol real de proiectat, nu de presupus rezolvat: **validarea de cantitate/stoc pe o linie -libera** nu se poate sprijini pe `do_verifica_articol` (cuplat de `crsarticole`) — vezi sectiunea 6. - ---- - -## 0. Ce e deja stabilit (nu se reinvestigheaza) - -- Optiunea "Cauta in lista de preturi…" intra in meniul de adaugare proiectat la S4b - (`docs\cercetare\s4b_bara_butoane_meniu.md`) — S4e ii da un element de meniu, nu un buton propriu. -- **Decizia 29**: stergerea unei linii venite din comanda ramane FARA protectie — se sterge ca - oricare alta, comanda ramane facturata partial. Nu se reia. -- **Legatura cu S4 punctul 2 (decizia 39)**: `crsarticole` e si registru al cantitatii ramase de - facturat. La momentul scrierii acestui raport, `docs\cercetare\s4_punct2_registru_cantitate_ramasa.md` - e **in lucru** (sectiuni "IN LUCRU", fara concluzie de proiectare inca). Acest raport **nu asteapta** - acea proiectare: dovezile de mai jos (id_c, `Calculate Sum`) sunt citite direct pe codul actual, nu - presupuse din raportul in lucru. Presupunerea facuta explicit aici: **designul propus (liniile - libere nu intra in `crsarticole`) e compatibil cu orice varianta de decuplare a registrului pe care - S4 punctul 2 ar alege-o** — pentru ca liniile libere nu ating deloc cursorul/mecanismul pe care - punctul 2 il decupleaza. Daca punctul 2 alege sa mute registrul intr-un cursor complet nou (nu - `crsarticole`), concluzia ramane aceeasi cu o singura schimbare de nume. -- **S4 (`docs\cercetare\s4_cautare_articole_server.md`) a decis deja**, pe cod, ca pentru - comanda/aviz (3,4,21,25,28,42,47) `crsarticole` **ramane incarcat in masa**, neschimbat — punctul 7 - al acelui raport. Aceasta decizie priveste **selectia articolelor comenzii insesi** (raman pe - `grd_articole`/`crsarticole`/`do_adauga_tot`, neschimbate). **Nu exclude** mecanismul S4e: cautarea - filtrata pe server construita de S4 (`cursor_preturi` cu `V_FILTRU_COD`/`V_FILTRU_DEN`) e exact - ce S4e reutilizeaza pentru liniile **libere**, un canal separat, care nu inlocuieste si nu atinge - `grd_articole`. - -## 1. Ce face azi calea de copiere (`ofacturare.prg:454-473`) - -Cod complet (`COMUN\programe\ofacturare.prg:454-473`): - -``` -ELSE - IF m.llCopiere - * Modificare sau copiere factura - * Nu adaug automat articolele din factura originala, ca sa dau posibilitate de modificare a cantitatilor, pretului, explicatiei - * ofrmdetaliifactura.do_adauga_tot() - - * Sterg din cursorul cu articole inregistrarile adaugate in factura - *DELETE FROM crsArticole - - * Adaug in lista articolelor cursorul cu lista de preturi ca sa pot adauga si alte articole in factura copiata/modificata - lcSqlCursor = [{call pack_facturare.cursor_preturi(?poDate.zi_curs,?poDate.tip,?poDate.id_valuta,] + ; - [?poDate.id_gestiune_init,?gnLuna,?gnAn,?gnIdUtil,?gnIdSucursala)}] - - lcCursorTemp = [crsArticoleTemp] - llSucces = goExecutor.oExecuta(lcSqlCursor, lcCursorTemp) - IF m.llSucces - SELECT crsArticole - APPEND FROM DBF(m.lcCursorTemp) - ENDIF - USE IN (SELECT(m.lcCursorTemp)) - ENDIF && m.llCopiere -``` - -Pas cu pas: - -1. Se executa `pack_facturare.cursor_preturi(...)` intr-un cursor temporar separat, `crsArticoleTemp` - (nu direct in `crsarticole`). -2. `SELECT crsArticole` + `APPEND FROM DBF(lcCursorTemp)` — VFP potriveste coloanele **dupa nume**; - coloanele care exista in `crsArticoleTemp` dar nu in `crsArticole` (sau invers) sunt ignorate - tacut de `APPEND FROM` (nu da eroare pentru coloane lipsa pe o parte sau alta — completeaza cu - valoarea implicita a campului pe partea destinatie). Cele doua cursoare au aceeasi structura de - baza (`creeaza_facturacrs`/coloanele standard `cursor_facturare`), deci in practica maparea e - completa. -3. `id_c` din `crsArticoleTemp` (numerotat `ROWNUM` de Oracle, incepand de la 1, independent de - executia anterioara) se copiaza **ca atare** in `crsArticole` — **nu se renumeroteaza**. -4. `crsArticoleTemp` se inchide (`USE IN`); rezultatul ramane doar in `crsArticole`, care de acum - contine doua seturi de randuri provenite din doua interogari Oracle diferite, cu spatii de `id_c` - care se pot suprapune. - -**De ce e sigur aici, dar nu neaparat pe comanda**: aceasta ramura ruleaza **doar** cand -`m.llCopiere` e adevarat, si cand `llCopiere` e adevarat, cursorul de baza `crsArticole` **nu mai -vine din `cursor_comanda`** — Do Case-ul de la `ofacturare.prg:266-308` verifica `Case m.llCopiere` -**primul**, inaintea oricarei ramuri pe `tnTip`, deci pentru copiere cursorul de baza e intotdeauna -`cursor_retur_document` (documentul copiat insusi), indiferent de tipul original. Un document copiat -nu (mai) e o "comanda" (`poDate.tip` dupa copiere nu e niciodata `3`), deci nici `do_scrie_factura` -(care cere `poDate.Tip = 4` sau `Inlist(poDate.Tip,3,21,25,28,42,47)` pentru logica de inchidere -automata), nici `do_sterge` (aceleasi conditii de tip) nu citesc/scriu `crsarticole` ca registru pe -calea de copiere — **coliziunea de `id_c` exista tehnic si aici, dar nu are efect observabil**, -pentru ca nimic nu mai citeste cursorul ca registru pe acest `poDate.tip`. Aplicarea aceleiasi tehnici -pe un document de tip 3 (unde registrul chiar e citit) ar expune exact coliziunea descrisa in verdict. - -## 2. De ce merge azi pe contract si nu pe comanda - -**Pe contract, lista de preturi si articolele contractului sunt doua cursoare separate de la bun -inceput, populate de o singura procedura Oracle cu doua `OUT REF CURSOR`-uri** -(`PACK_FACTURARE:2646-2950`, `cursor_contract`): - -``` -PROCEDURE cursor_contract(..., V_CURSOR OUT cursor_facturare, V_CURSOR2 OUT cursor_facturare) IS -... -OPEN V_CURSOR2 FOR - ... - SELECT rownum - 10000 as id_c, id_ctr, id_articol, id_rata, ... , opt_facturare - FROM (... CTR_ARTICOLE OPT_FACTURARE=3 UNION ALL ... CTR_SCADENTAR OPT_FACTURARE IN (1,2) ...) - ORDER BY data, numar, data_rata, denumire; - -pack_facturare.cursor_preturi(V_DATA_CURS, V_TIP, V_ID_VALUTA, V_ID_GESTIUNE_INIT, V_LUNA, V_AN, - V_ID_UTIL, V_ID_SUCURSALA, V_CURSOR); -END cursor_contract; -``` - -`V_CURSOR` (= `crsarticole`, mapat in VFP) **este** rezultatul unui apel intern la `cursor_preturi` — -lista de preturi completa, exact ca pe un document liber de tip 1/5/7/10. `V_CURSOR2` (= `crsarticole1`) -e interogarea proprie contractului, cu `id_c` deliberat decalat cu `-10000` fata de spatiul lui -`ROWNUM` simplu — dovada directa ca autorii codului au tratat coliziunea de `id_c` intre cele doua -cursoare ca pe un risc real de evitat, nu ca pe ceva neglijabil. - -**Consecinta pentru UI**: pe un document de contract, `grd_articole` (RecordSource `crsarticole`) -afiseaza azi **lista de preturi**, nu articolele contractului — de aceea capul lui de coloana e deja -"Cantitate in stoc" si mesajul e deja "Acest articol nu este pe stoc!" -(`ofacturare.vc2:15129-15143`, citat integral): - -``` -Case Inlist(poDate.tip, 2, 6) - && facturare pe baza de contract - This.lb_titlu_alb_b121.Caption = [FACTURA PE CTR. ] + Alltrim(poDate.descriere) - This.grd_articole.RemoveObject('cSerie') - && articole din lista de preturi - This.grd_articole.cCantitate.header1.Caption = [Cantitate in stoc] - This.cmesaj_cantitate = [Acest articol nu este pe stoc!] -``` - -`grd_contracte` (RecordSource `crsarticole1`) e gridul secundar, cu articolele/ratele contractului. -Contractul "are deja lista de preturi" pentru ca **asa e populat de la inceput** — nu exista niciun -`APPEND`/merge, doar doua interogari separate aratate in doua griduri diferite. - -**Confirmarea ca registrul nu se aplica pe contract**: `do_scrie_factura`'s `Do Case` -(`ofacturare.vc2:14282-14389`) are exact patru ramuri — `poDate.eProforma=1`, `poDate.Tip=4`, -`Inlist(poDate.Tip,3,21,25,28,42,47)`, `Otherwise`. Tipurile de contract (2,6,26,52) nu apar in -niciuna din primele trei, deci cad pe `Otherwise` — `pack_facturare.scrie_factura2` se cheama direct, -**fara** `Select crsarticole / Calculate Sum(cantitate) ...` si fara `pnParametruAditional` derivat -din vreo suma. Contractul nu are, azi, nicio decizie de "inchidere automata" bazata pe -`Sum(crsarticole.cantitate)` — deci amestecul de continut din `crsarticole` (lista de preturi) nu are -cum sa strice o logica ce nu exista pentru acest tip. **Pe comanda, aceeasi ramura a Do Case-ului -(`Inlist(poDate.Tip,3,21,25,28,42,47)`) exista si citeste exact `crsarticole`** -(`ofacturare.vc2:14332-14338`, citat in verdict) — de aici diferenta reala. - -## 3. Proiectarea adaugarii - -**Liniile libere NU intra in `crsarticole`.** Mecanismul recomandat: - -1. **Sursa de date**: cursorul filtrat construit de S4 — - `pack_facturare.cursor_preturi` supraincarcat cu `V_FILTRU_COD`/`V_FILTRU_DEN` - (`docs\cercetare\s4_cautare_articole_server.md`, punctul 4/9, pasul 1). Nu se cere nimic nou - in Oracle pentru S4e insusi — se reutilizeaza mecanismul deja proiectat pentru S4 (S4e devine - inca un consumator al aceluiasi cursor filtrat, nu un cursor nou). "Alege din nomenclator…" - (S4g, decizia 27/34) e un cursor separat, in afara acestei povesti. -2. **Legarea in UI**: `APPEND BLANK` direct pe `crsfactura`, urmat de editare inline prin - `combosql` pe celula de cod/denumire — exact tiparul deja existent, azi, in productie (chiar - daca in celalalt formular) la `frm_facturare_articole2.But_nou1.do_adauga` - (`ofacturare.vc2:17118-17122`): - ``` - PROCEDURE do_adauga - Select crsFactura - APPEND BLANK - this.grd_factura.SetFocus() - ENDPROC - ``` - Meniul S4b ("Cauta in lista de preturi…") declanseaza acest gest, cu focus direct pe celula de - cautare — S4b insusi a documentat aceasta echivalenta (sectiunea 6 a raportului S4b): "aceste - doua optiuni de meniu ajung la acelasi gest UI". -3. **`crsfactura.id_c` ramane la valoarea implicita a campului** (`N(20)`, fara `Null`, - `creeaza_facturacrs`, `COMUN\programe\ofacturare_comun.prg:1775-1785`) — adica `0` la - `APPEND BLANK`, valoare pe care Oracle nu o produce niciodata pentru `id_c` (`ROWNUM` incepe de - la 1). Consecinta: `do_sterge`-ul de azi (`For id_c = poArticol.id_c`, vezi sectiunea 4) nu - gaseste niciodata un rand de potrivit in `crsarticole` pentru o linie libera — comportament - corect prin constructie, **fara nicio modificare de cod**. - *Precedent in acelasi fisier, deja in productie*: linia de discount adaugata la finalul - documentului (`ofacturare.vc2:14531-14537`, `Select crsfactura / Append Blank / Replace - denumire With Replicate('Z',20), ... , id_temp With 99999`) — nu seteaza `id_c` deloc, foloseste - deja acelasi tipar de "rand sintetic fara sursa in `crsarticole`". -4. **`crsfactura.opt_facturare`** (numeric, implicit `0`) ramane la valoarea implicita si pentru o - linie libera — coincide cu valoarea pe care o are deja o linie normala din lista de preturi - (`opt_facturare=0` inseamna azi "nu vine din contract", conventia citita de `do_sterge` la - `ofacturare.vc2:14615-14619`). Nu e nevoie de o valoare noua pe acest camp pentru distinctia - "libera vs. comanda" — distinctia se face deja prin `id_c` (punctul 3). -5. **Completarea campurilor de pret/TVA/valuta**: identic cu maparea deja proiectata de S4 - (`s4_cautare_articole_server.md`, punctul 6 — tabelul camp-cu-camp) — S4e nu adauga camp nou, - preia mecanismul S4 ca atare pentru randul ales din cautarea filtrata. - -**Ce ramane de verificat la implementare, nu de presupus**: daca `combosql`-ul legat pe `grd_factura` -(construit pentru S4) trebuie sa apara pe **toate** coloanele relevante (cod, denumire) ale unei -linii nou-adaugate prin acest gest, sau doar pe coloana pe care s-a facut click — comportamentul -exact al `LostFocus`-ului de completare in masa a celorlalte campuri (`s4_cautare_articole_server.md`, -punctul 5, pasul 4) ramane in perimetrul S4, S4e doar il consuma. - -## 4. Stergerea - -**(a) O linie venita din comanda** — comportament **neschimbat**, decizia 29 se aplica ca azi: -`do_sterge` (`ofacturare.vc2:14608-14693`) gaseste `poArticol.opt_facturare = 0` (linie din -`crsarticole`, comanda), potriveste pe `id_c` (`ofacturare.vc2:14652-14655`, -`Case (gnScadereStoc = 1 And poDate.tip = 23) Or Inlist(poDate.tip,3,4,21,25,28,42,47) => Select -crsarticole / Replace cantitate With cantitate + poArticol.cantitate For id_c = poArticol.id_c`), -reface cantitatea ramasa in `crsarticole`. La `do_scrie_factura`, `Calculate Sum(cantitate)` peste -`crsarticole` (neschimbat, fara linii libere in el) vede corect cantitatea marita — comanda ramane -"facturata partial", fara nicio interventie noua. Nicio confirmare, niciun marcaj "refuzat" — -exact decizia 29. - -**(b) O linie libera adaugata** — `poArticol.opt_facturare = 0` (aceeasi valoare ca (a): `0` nu -distinge intre "din lista de preturi ca supliment pe comanda" si "din lista de preturi ca document -liber" — nu trebuie sa distinga, pentru ca in ambele cazuri `lcCursor = crsarticole` -la `ofacturare.vc2:14615-14619`). Executia lui `Replace cantitate With cantitate + poArticol.cantitate -For id_c = poArticol.id_c` gaseste **zero randuri** in `crsarticole` cu `id_c = 0` (Oracle nu produce -niciodata `id_c = 0`), deci `REPLACE ... FOR` e un no-op — nimic nu se schimba in registru. Corect: o -linie libera nu a fost niciodata scazuta din vreun "ramas de facturat", deci nu are ce sa refaca la -stergere. **Nicio schimbare de cod necesara in `do_sterge` pentru acest caz.** - -**Verificare de facut la implementare, nu presupunere**: confirmarea exacta a valorii implicite -(`0` vs. posibil `.NULL.` daca proprietatile campului se schimba fata de ce arata azi -`creeaza_facturacrs`) — ambele valori produc acelasi rezultat (`REPLACE ... FOR` fara potrivire), -dar merita un test explicit inainte de a considera punctul inchis, nu doar citirea definitiei -campului. - -## 5. Capul de coloana si mesajul de cantitate - -**Corectie fata de ipoteza initiala a task-ului**: capul de coloana "Cantitate comandata" si mesajul -"A fost facturata intreaga cantitate comandata pentru acest articol!" (`ofacturare.vc2:15144-15150`) -apartin lui `grd_articole`/`crsarticole` — gridul care, cu proiectarea din sectiunea 3, **continua sa -arate exclusiv liniile comenzii** (liniile libere nu intra niciodata acolo). Deci acest cap de coloana -si acest mesaj **raman corecte, neschimbate**, pentru ca nu se mai aplica vreodata unei linii libere. - -**Unde apare totusi problema semnalata de task**: nu in `grd_articole`, ci in **validarea de -cantitate/stoc a liniei libere insesi**, care trebuie sa arate un mesaj propriu, nu mostenit de la -`Thisform.cmesaj_cantitate` (proprietate unica per formular, setata o singura data in `Init` dupa -`poDate.tip` — daca `poDate.tip = 3`, ea contine azi tocmai textul de comanda). Daca validarea unei -linii libere ar reutiliza `Thisform.cmesaj_cantitate` fara sa o schimbe pe moment, un articol liber -epuizat din stoc ar afisa gresit "A fost facturata intreaga cantitate comandata" — mesajul cerut -de plan la punctul de atentie e real, doar localizat gresit initial (nu pe `grd_articole`, ci pe -mecanismul de validare a liniei libere, vezi sectiunea 6). - -**Propunere concreta de text** (romana, de validat cu Marius la implementare): -- `grd_articole` (comanda), neschimbat: cap coloana **"Cantitate comandata"**, mesaj - **"A fost facturata intreaga cantitate comandata pentru acest articol!"**. -- Linie libera (validare separata, sectiunea 6), text propus, calchiat dupa formularea deja - folosita pe lista de preturi simpla (`ofacturare.vc2:15122-15127`, tipurile 1/5/7/10, unde - `cmesaj_cantitate` ramane la valoarea implicita a formularului): **"Nu mai exista stoc disponibil - pentru acest articol!"** — coerent cu formularea deja folosita pe contract - (`[Acest articol nu este pe stoc!]`, `ofacturare.vc2:15136`), fara sa foloseasca acelasi text - identic (ca sa nu para o dublare accidentala a mesajului de contract). - -## 6. Validarea de cantitate - -**Ce se pastreaza neschimbat**: pentru liniile din comanda, alegerea prin `grd_articole` ramane pe -calea de azi — `do_adauga_articol` -> `do_verifica_articol(tlContract=.F., tnCantitate=poArticol.cantitate)` -(`ofacturare.vc2:12813-12869`, `:14731-14787`). `poArticol.cantitate` provine din -`crsarticole.cantitate`, care e "ramas de facturat" (`A.CANTITATE - NVL(D.CANTITATE,0)`, -`PACK_FACTURARE:3030`) — cererea din plan ("liniile din comanda sa nu-si piarda validarea de -cantitate") e satisfacuta **prin constructie**: acest drum nu e atins de nimic din proiectarea S4e, -pentru ca liniile libere nu intra niciodata in `crsarticole` si nu modifica randurile lui. - -**Ce nu se poate reutiliza ca atare pentru o linie libera**: `do_verifica_articol` cere un -`poArticol` scatter-uit dintr-un cursor sursa deja incarcat (`crsarticole`/`crsarticole1`) — o linie -libera adaugata prin `APPEND BLANK` + `combosql` (sectiunea 3) nu are un asemenea rand sursa. Capacul -de cantitate pentru un articol gestionabil ales liber trebuie sa vina din **stocul curent al -articolului**, nu din "ramas de facturat" — informatie pe care randul intors de cautarea filtrata -(`cursor_preturi` cu `V_FILTRU_COD`/`V_FILTRU_DEN`) o are deja in coloana `cantitate` (aceeasi -semantica de stoc descrisa in verdict), fara sa fie nevoie de un apel Oracle suplimentar. - -**Gol real de proiectare, necesar S4e, neacoperit de niciun raport anterior (S4/S4b)**: nici -`s4_cautare_articole_server.md`, nici `s4b_bara_butoane_meniu.md` nu descriu un mecanism de validare -a cantitatii/stocului pentru randul ales prin `combosql` pe `grd_factura` — ambele rapoarte se opresc -la "randul se completeaza cu valorile din cursorul filtrat" (mapare de campuri), fara sa acopere -"ce se intampla daca utilizatorul introduce o cantitate mai mare decat stocul". **De proiectat la -implementare**: fie (a) o verificare in `LostFocus`/`Valid` a celulei de cantitate din `grd_factura`, -similara ca forma cu `do_verifica_articol` dar alimentata din randul `combosql` (nu din -`crsarticole`), fie (b) reutilizarea `do_verifica_articol` insasi, cu un `poArticol` construit ad-hoc -din randul `combosql` in loc de `Scatter` dintr-un cursor — a doua varianta pastreaza un singur loc -de validare in cod, dar cere ca `do_verifica_articol` sa primeasca `poArticol` ca parametru explicit -in loc sa citeasca variabila `Private poArticol` deja populata de apelant (schimbare de contract a -metodei, nu doar de continut). Ambele variante trebuie sa aleaga mesajul din sectiunea 5, nu -`Thisform.cmesaj_cantitate` motenit din `Init`. - -## 7. Aceeasi problema pe alte surse - -| Sursa | `crsarticole` = registru citit de `do_scrie_factura`? | `do_sterge` ajusteaza `crsarticole` pe `id_c`? | Concluzie pentru "APPEND FROM literal" | -|---|---|---|---| -| **comanda** (3,21,25,28,42,47) | **Da** — `Inlist(poDate.Tip,3,21,25,28,42,47)`, `:14332-14338` | **Da** — aceleasi tipuri, `:14652-14655` | **Nesigur** — subiectul acestui raport; solutia e "linii libere in afara `crsarticole`" (sectiunea 3) | -| **avize** (4, "factura din avize") | **Da** — `poDate.Tip = 4`, `:14301-14311` | **Da** — `Inlist(poDate.tip,3,4,21,25,28,42,47)` include 4, `:14652-14655` | **Acelasi risc ca pe comanda** — `cursor_avize` are aceeasi semantica "ramas de facturat" (`docs\cercetare\s4_cautare_articole_server.md`, punctul 1.4); solutia din sectiunea 3 se aplica identic | -| **contract** (2,6,26,52) | **Nu** — tipurile nu apar in `Do Case`-ul din `do_scrie_factura`, cad pe `Otherwise` | Da, dar pe `crsarticole1` (`opt_facturare<>0`), niciodata pe `crsarticole` | **Sigur** — lista de preturi traieste deja intr-un cursor separat (`crsarticole`), fara sa fie citita ca registru; nimic de schimbat | -| **retur ca document** (8,9,24) | **Nu** — cad pe `Otherwise`, fara `Sum(cantitate)`/`pnParametruAditional` | **Da, partial** — `Inlist(poDate.tip,8,9,24) => Replace cantitate With cantitate - poArticol.cantitate For id_c = poArticol.id_c`, `:14658-14659` (semn opus, urmareste maximul returnabil, nu inchiderea) | **Risc de coliziune `id_c` ramane, dar fara poluarea `Sum`**: o linie libera adaugata literal prin `APPEND FROM` in `crsarticole` pe un document de retur ar putea, la stergere, atinge tacut cantitatea maxima returnabila a unei linii reale (aceeasi problema de `id_c` din verdict, punctul 1) — **de tratat cu aceeasi solutie (linii libere in afara `crsarticole`) si in S4f**, nu doar aici. Decizia 22 (retur fara factura originala e permis) nu schimba aceasta concluzie — priveste alt aspect (mostenirea gestiunii/pretului) | -| **transfer subunitati** (23, 41) | Nu (cad pe `Otherwise`) | Partial, doar tip 41 (`Case gnScadereStoc = 1 And poDate.tip = 41`, `:14647-14649`, cu semn opus) | In afara perimetrului S4e (surse via `cursor_gestiune`, nu `cursor_comanda`/`cursor_preturi`); acelasi principiu de precautie se aplica daca se cere vreodata "lista de preturi si pe transfer" | - -**Concluzie de tabel**: comanda si avize sunt genuin identice ca risc si ca solutie — **orice -implementare a S4e trebuie sa acopere ambele tipuri de sursa cu acelasi mecanism** ("linii libere in -afara `crsarticole`"), nu doar comanda cum sugereaza titlul povestii. Contractul ramane cum e azi -(nimic de schimbat). Returul ca document (S4f) trebuie avertizat explicit sa nu foloseasca reteta -`APPEND FROM` literal, desi riscul lui e mai mic (fara poluarea sumei de inchidere). - -## 8. Pasi de implementare - -1. **Extinde meniul S4b cu "Cauta in lista de preturi…" pe comanda/aviz** (deja in tabelul propus de - S4b, sectiunea 3 a acelui raport) — leaga optiunea de gestul `APPEND BLANK` pe `crsfactura` - descris in sectiunea 3 de mai sus, nu de un al doilea cursor bulk. - *Verificabil*: pe un document de tip 3 (comanda) sau 4/21/25/28/42/47 (avize), alegerea optiunii - adauga un rand gol in `grd_factura` cu focus pe celula de cautare, fara niciun apel catre - `cursor_comanda`/`cursor_avize` in plus (verificabil in logul `goExecutor`). -2. **Confirma valoarea implicita a `crsfactura.id_c` la `APPEND BLANK`** (sectiunea 4) — test direct, - nu citire de definitie. *Verificabil*: dupa `APPEND BLANK` pe un `crsfactura` gol, `id_c` al - noului rand e o valoare pe care Oracle n-o produce niciodata pentru `id_c` (azi: `0`). -3. **Adauga validarea de cantitate/stoc pentru linia libera** (sectiunea 6, varianta aleasa la - implementare) — mesaj propriu, nu `Thisform.cmesaj_cantitate` mostenit. - *Verificabil*: pe un articol gestionabil cu stoc 0, adaugarea lui ca linie libera pe un document - de comanda arata mesajul de stoc propus in sectiunea 5, nu mesajul de "cantitate comandata". -4. **Verifica prin regresie ca `do_sterge`/`do_scrie_factura` raman neatinse** — niciun cod nou in - aceste doua metode pentru cazul liniei libere (confirmat teoretic in sectiunea 4, dar de validat - pe date reale). *Verificabil*: pe un document de comanda cu o linie a comenzii si o linie libera - adaugate, stergerea liniei libere nu modifica `Calculate Sum(cantitate)` peste `crsarticole`; - stergerea liniei din comanda reface cantitatea ei, neschimbat fata de azi. -5. **Extinde acelasi mecanism pe avize (tip 4/21/25/28/42/47)** — nu doar comanda (tabelul din - sectiunea 7). *Verificabil*: identic cu pasul 4, pe un document "factura din avize". -6. **Avertizeaza explicit S4f (retur ca document)** sa nu foloseasca `APPEND FROM` literal pentru - lista de preturi pe documentele de retur (8,9,24) — risc de coliziune `id_c` cu maximul returnabil, - chiar daca fara poluarea sumei de inchidere. Nu e un pas de implementat aici, e o nota de predare - catre acea poveste (deja referentiata la decizia 22/S4f in plan). - -## 9. Ce nu se poate testa headless - -- **Comportamentul `combosql`/`APPEND BLANK` in `grd_factura`** — capcana deja confirmata pe acest - proiect (`grid-coloane-nu-se-materializeaza-headless`): sub `-A -T`, `ColumnCount`/`RecordSource` - raman artefacte. Verificarea vizuala a mesajului/capul de coloana (sectiunea 5) si a gestului de - adaugare (sectiunea 3) cere harnessul UI vizibil, nu rulare headless. -- **`LostFocus`/`Valid` pe celula de cantitate** (sectiunea 6) — daca implementarea alege varianta - (a) din sectiunea 6 (verificare directa in eveniment de celula), comportamentul depinde de randare - de grid, deci nu e testabil direct headless; varianta (b) (reutilizare `do_verifica_articol` cu - `poArticol` construit ad-hoc) **este** apelabila programatic, fara UI, daca semnatura metodei - primeste parametrul explicit. -- **`AMESSAGEBOX` la mesajul de stoc** (sectiunea 5) — verificabil ca text literal in cod, nu ca - aparitie reala pe ecran, din acelasi motiv structural ca oriunde altundeva in suita. -- **Ce se poate verifica headless, direct**: continutul `crsfactura`/`crsarticole` dupa apeluri - directe (nu prin click) la `do_sterge`/`do_scrie_factura`/gestul de `APPEND BLANK`, cu date de - test pregatite manual (un `crsarticole` cu 2-3 randuri de comanda, un `crsfactura` cu o linie de - comanda si una libera inserate programatic) — confirma `id_c`, `Sum(cantitate)`, si comportamentul - `REPLACE ... FOR` fara sa deschida formularul. - -## 10. Riscuri si decizii ramase lui Marius - -1. **Metoda de validare a cantitatii pe linia libera** (sectiunea 6) — varianta (a) sau (b); (b) - cere schimbarea contractului lui `do_verifica_articol` (parametru nou `poArticol`), (a) dubleaza - logica de validare in doua locuri diferite ale codului. **Recomandare**: (b), pentru un singur - loc de intretinut — coerent cu principiul deja aplicat in S4b sectiunea 5 ("echivalenta tot vs. - alege" tine pe convergenta catre o singura rutina). -2. **Textul exact al mesajului de stoc pentru linia libera** (sectiunea 5) — propunerea e o - recomandare, nu o decizie finala; de confirmat impreuna cu textele deja stabilite la S3b - (punctul marunt `a` din plan, `:524`). -3. **S4f trebuie avertizat explicit sa nu repete reteta `APPEND FROM`** pentru retur (sectiunea 7, - pasul 6) — nu e o decizie de luat aici, dar cineva trebuie sa o duca mai departe cand S4f intra - in lucru, altfel riscul de coliziune `id_c` pe retur se reintroduce pe alta poveste, fara sa mai - fie legat de acest raport. -4. **Interactiunea cu S4 punctul 2, in lucru la momentul acestui raport** — proiectarea de aici - presupune ca liniile libere raman complet in afara oricarui mecanism de registru, indiferent cum - arata forma lui finala dupa decuplare. Daca punctul 2 decide sa scoata bookkeeping-ul din - `crsarticole` intr-un cursor nou (cea mai probabila directie, judecand dupa decizia 39), concluzia - sectiunilor 3-4 de aici ramane valabila fara nicio schimbare — liniile libere nu ating niciun - cursor de registru, indiferent de numele lui. Daca punctul 2 alege in schimb sa pastreze - `crsarticole` ca sursa unica dar sa filtreze `Sum(cantitate)`/`do_sterge` printr-un camp nou - (de exemplu un flag `liber`), acel camp ar trebui setat pe liniile libere **daca** ele ar intra - totusi in `crsarticole` — proiectare pe care acest raport o considera inutila (liniile libere nu - trebuie sa intre acolo deloc), dar merita re-confirmat cu punctul 2 cand acel raport se incheie, - ca sa nu ramana doua solutii partial suprapuse pentru aceeasi problema. -5. **Nu s-a masurat, doar dedus din forma interogarii** (decizia 31 a planului, aplicata si aici): - nimic din acest raport se sprijina pe volume de date masurate — toate concluziile despre `id_c`/ - `Sum(cantitate)` vin din citirea directa a SQL-ului si a codului VFP, nu din executii de test. - -## Handoff - -Cercetare incheiata, fara nicio problema de context intalnita in aceasta runda — nu a fost necesara -predarea. Toate cele zece sectiuni cerute de brief sunt completate mai sus, cu `fisier:linie` -verificat direct pe fisierele text reale (nu `.bak`) si pe SQL-ul exportat curent -(`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql`). Nicio editare -de cod, niciun `git_sync.ps1`/`txt2vcx.ps1`, niciun commit, nicio scriere pe Oracle. - -**Corectie fata de premisa initiala a task-ului**, de retinut explicit pentru cine citeste doar -rezumatul: reteta citata in decizia 16 (`APPEND FROM`, `ofacturare.prg:454-473`) **nu** trebuie -generalizata literal pe comanda — e sigura doar in contextul ei actual (copiere, fara registru activ -pe cursorul respectiv). Solutia corecta, verificata ca fezabila si ca deja partial sprijinita de -mecanisme existente (S4's `cursor_preturi` filtrat, `APPEND BLANK`-ul din `frm_facturare_articole2`, -valoarea implicita `0` a lui `id_c`), e ca liniile libere sa **nu intre niciodata in `crsarticole`** -— exact ipoteza pe care brief-ul o semnala ca posibila si cerea sa fie confirmata sau infirmata pe -cod, nu presupusa. diff --git a/docs/cercetare/s4f_retur_formular_unificat.md b/docs/cercetare/s4f_retur_formular_unificat.md deleted file mode 100644 index 620a63f..0000000 --- a/docs/cercetare/s4f_retur_formular_unificat.md +++ /dev/null @@ -1,727 +0,0 @@ -# S4f — Returul in formularul unificat - -Proiectare pe cod, READ-ONLY (fara editari, fara `git_sync.ps1`/`txt2vcx.ps1`, fara commit, fara -scriere Oracle — doar `SELECT`), pentru povestea **S4f** din -`docs\plan_13_unificare_formular_facturare.md:2287-2330` (decizia 17, proiectata in sectiunea N, -`:1574-1655`). Nu s-a atins `COMUN\clase\ofacturare_comun.vc2` si -`COMUN\programe\ofacturare_editare.prg` — doar citite, cand au aparut in cautari. - -Surse de adevar deja stabilite, citate nu reinvestigate: `docs\cercetare\legatura_linie_retur.md`, -`docs\cercetare\factura_retur_document.md`, `docs\cercetare\retur_si_lista_preturi.md`, -`docs\cercetare\s4b_bara_butoane_meniu.md`. - -## Verdict (esenta, 10 randuri) - -**Corectat dupa avertismentul S4e** (`docs\cercetare\s4e_lista_preturi_pe_sursa.md`, sectiunile 3 si -7): mecanismul `APPEND FROM` din decizia 16 (`ofacturare.prg:454-473`) **e respins pentru orice -cursor care serveste si drept registru citit de `do_sterge`**, `crsarticole` fiind exact acel cursor -pe documentele de retur (`Inlist(poDate.tip,8,9,24) => Replace cantitate With cantitate - -poArticol.cantitate For id_c = poArticol.id_c`, `ofacturare.vc2:14658-14659`) — riscul e coliziune de -`id_c` intre `cursor_retur_document` si `cursor_preturi`, ambele numerotate `ROWNUM` independent. -Sectiunea 5 de mai jos e rescrisa pe solutia validata de S4e: **liniile libere nu intra deloc in -`crsarticole`**, se adauga direct in `crsfactura` prin `APPEND BLANK` + `combosql` (tiparul deja in -productie la `frm_facturare_articole2.But_nou1.do_adauga`), iar campul de distinctie e -**`crsfactura.id_c`** (`0` implicit pe o linie libera, valoare pe care Oracle n-o produce niciodata -pentru `id_c`), nu o coloana lipsa in `crsarticole`. - -Partea grea nu e ce parea: N.1 (documentul de retur, tip 8/9/24) merge deja cap-coada — alegere -multipla de facturi, populare din `cursor_retur`, gestiune si pret de achizitie mostenite, stergere -si retur partial. Munca reala e in trei bucati inguste. **(1) Nu se muta dialogul de alegere a -facturilor** in bara de butoane a gridului — s-a verificat pe cod ca alegerea ramane la nivelul -antetului (sectiunea pliata), exact cum recomanda deja `s4b_bara_butoane_meniu.md` §9.3/tabelul -"puncte marunte", randul h; ce se muta in meniul "Adauga articole" e **rezultatul** alegerii (liniile -deja aduse), ca "Adauga tot din facturile alese" / "Alege liniile de returnat…", simetric cu -comanda/contract/avize. **(2)** Ridicarea lui `But_retur` la nivel de document e cod nou real, nu o -mutare de buton — azi alegerea e per-articol si niciodata scrisa ca legatura persistata. -**(3)** Liniile libere pe retur folosesc reteta S4e ("`APPEND BLANK` pe `crsfactura`, niciodata pe -`crsarticole`"), **plus** un gol nou de proiectat, gasit in aceasta sesiune si necunoscut de S4e (care -nu are dialog de gestiune pe comanda/avize): mecanismul de alegere a gestiunii -(`frm_articol_gest_factura`, `.lRetur=.T.`) **suprascrie pretul de vanzare cu pretul din stoc** -(`ofacturare.vc2:4094-4103`) — corect pentru o linie mostenita dintr-o factura sursa, dar gresit -pentru o linie libera, a carei pret de vanzare trebuie sa vina din lista de preturi (decizia 16), nu -din costul de stoc; in plus, acel dialog se intra azi doar dintr-un `poArticol` scatter-uit din -`crsarticole` (`do_adauga_articol`/`do_alege_stoc`), pe care o linie libera din `crsfactura` nu-l are -— e nevoie de un punct de intrare nou in dialog, nu doar de reparat pretul. Vezi sectiunea 5 si -riscurile R1/R7. - ---- - -## 1. Inventarul celor doua cai de retur de azi - -### 1.1 N.1 — factura de retur ca document (tip 8, 9, aviz 24) - -| Pas | Ce face | `fisier:linie` | -|---|---|---| -| Intrare | Tile `Page2.Cw1` -> `politica.mpr` -> `factureaza(8)`/`factureaza(9)` din meniul shortcut | `COMUN\clase\ofundal_facturare.vc2:882-884`, `Meniuri\politica.mn2:14-15,45-46` | -| Antet | `nIdTipDoc=5`, formular `frm_date_factura`, aviz retur (24) foloseste `frm_date_aviz` cu `nIdTipDoc=6` | `COMUN\programe\ofacturare.prg:192-196,222-226` | -| Alegere facturi sursa | `frm_date_factura.do_cauta_facturi` -> `caut_facturi_multiple_client(id_client, in_valuta, id_valuta, .T.)` -> `cauta_alfa(..., tnTipReturn=1)`, selectie multipla, exclude tipurile de retur din sursa | `COMUN\clase\ofacturare.vc2:9173-9212`; `COMUN\programe\oproceduri_facturare.prg:2091-2121` | -| Validare obligatorie | Fara alegere, antetul nu se poate inchide (`inainte_de_do_termin`) | `ofacturare.vc2:9523-9526` | -| Populare linii | `pack_facturare.cursor_retur(V_IN_VALUTA,V_LISTAID,V_ID_UTIL)` -> `cursor_retur_document(...,V_COPIERE=0,...)`, `SELECT` direct din `VANZARI_DETALII`, filtrat pe `ID_VANZARE IN (V_LISTAID)` | `ofacturare.prg:306-307`; `ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:3943-3956,3958-4071` | -| Gestiune + pret achizitie | `ID_GESTIUNE`/`PRET_ACHIZITIE` copiate neschimbate din linia originala; `GESTIONABIL = NVL2(A1.ID_GESTIUNE,1,0)` | `ff_...sql:4034-4035,4055-4056,4048` | -| Stergere linie | Permisa, cantitatea reintra in cursorul sursa | `ofacturare.vc2:14608-14693`, ramura `:14658-14659` | -| Retur partial | Permis, validat fata de maximul = `CANTITATE` a liniei originale, fara agregare cu retururi anterioare | `ofacturare.vc2:14743-14754`, `ff_...sql:4036` | -| Adaugare libera | **Nu se poate azi** — `do_adauga_articol` ia articolul mereu din `crsarticole`, care pe tip 8/9/24 **este** rezultatul lui `cursor_retur` | `ofacturare.vc2:12843-12851` | - -### 1.2 N.2 — `But_retur` per-articol, in factura normala (tip 1, 5, 7, 10) - -| Pas | Ce face | `fisier:linie` | -|---|---|---| -| Vizibilitate | Doar pe tip 1/5/7/10; pe documentele de retur (8/9/24) butonul nu exista in UI | `ofacturare.vc2:15122-15127` | -| Declansare | `caction=do_retur` -> `do_adauga_articol(.F.,.F.,.T.)` | `cmd_butoane.vc2:324-338`; `ofacturare.vc2:13963-13965` | -| Alegere factura sursa | **Per articol**, la fiecare adaugare: `caut_facturi_multiple_client_articol(id_client,in_valuta,id_valuta,.F.,id_articol)` -> `cauta_alfa` cu titlu simplu "Alegeti factura" (nu selectie multipla) | `oproceduri_facturare.prg:2124-2159` | -| Acumulare | `thisform.cListaIdArticoleRetur` acumuleaza CSV de perechi `id_articol:id_vanzare`, cate una per articol adaugat; `poDate.listaid` e folosit **transient** (salvat in `lnListaIdOld` si restaurat imediat dupa) doar cat dureaza dialogul per-articol | `ofacturare.vc2:12882-12892` | -| Gestiune | **Se alege**, nu se mosteneste — `pack_facturare.cursor_gestiuni_articol_retur`, prin `do_alege_stoc(...,tlRetur=.T.)` -> `frm_articol_gest_factura` | `ofacturare.vc2:13230,13353-13385` | -| Pret | **Suprascris cu pretul din stoc** (`pretv`), nu cel din lista de preturi — vezi sectiunea 5 | `ofacturare.vc2:4094-4103` | -| Semn cantitate | Explicit `poArticol.cantitate = (-1) * cantitate` cand `Thisform.lRetur` | `ofacturare.vc2:4103` | -| Trimitere Oracle | `poDate.listaid = thisform.cListaIdArticoleRetur` la scriere finala | `ofacturare.vc2:13977-13979` | -| Legatura persistata | **Nicio legatura scrisa** — `listaid` e folosit doar ca filtru in calculul de disponibil pe rulaj (`RUL`), nu scrie nimic in `VANZARI_CORESP` sau pe linia noua | `ff_...sql:8142-8212` | - -### 1.3 Ce au in comun si unde diverg - -Comun: excluderea returului dintr-un retur (acelasi filtru `tip in (1,2,3,4,5,6,10,-1,-2,-3,-4,-11)` -in ambele functii de cautare), conventia de semn negativ pe cantitate, `frm_articol_gest_factura` -ca formular de alegere a gestiunii pentru linia gestionabila. - -Diverg pe tot restul: N.1 alege facturile **o singura data, la nivel de document, inainte** ca -articolele sa existe; N.2 alege **per articol, in orice moment**, fara limita de cate ori. N.1 -mosteneste gestiune+pret_achizitie fara alegere; N.2 le cere explicit. N.1 scrie -`VANZARI_CORESP(TIP=3)`; N.2 nu scrie nimic persistat. Zero cod comun intre cele doua cai — -`ofacturare.vc2:12813-12900` (N.2, `do_adauga_articol`) si `ofacturare.prg:306-311` (N.1, -`cursor_retur`) nu se ating. - ---- - -## 2. `do_cauta_facturi` / `cauta_alfa(..., tnTipReturn=1)` — contractul exact - -### 2.1 Cine il cheama si cand - -Pe `frm_date_factura` exista un control generic de cautare, `Ct_clb_altele` (clasa -`ct_clb_cautare`), a carui iconita de lupa e legata de `cprocedura = thisform.do_cauta_altele` -(`ofacturare.vc2:8721-8729`). `do_cauta_altele` e un dispecer pe `nid_tip`: - -``` -Case Inlist(Thisform.nid_tip,8,9) - Thisform.do_cauta_facturi() -``` -(`ofacturare.vc2:8953-8954`) - -**Secventa confirmata pe cod** (`factureaza()`, `ofacturare.prg:187-311`): `frm_date_factura` se -creeaza si se afiseaza (`.Show()`, `:230-235`) **inainte** ca `Do Case tnTip` de la `:266-308` sa -ruleze `cursor_retur`, si mult inainte ca `lcObject = [frm_facturare_articole]` sa fie ales -(`:395`). Executia continua abia dupa ce `ofrmceredate` (antetul) se elibereaza (`:248`), iar -`pnButon` (setat la inchiderea antetului) e verificat imediat dupa. Deci **azi dialogul de alegere -a facturilor ruleaza pe formularul de antet, complet inainte ca formularul de articole sa existe** — -`crsarticole` se incarca o singura data, cu `poDate.listaid` deja fixat. - -Control observat, semnificativ pentru sectiunea 3: `Ct_clb_altele` are -`cconditie = EMPTY(NVL(poDate.listaid,''))` (`ofacturare.vc2:8722`) — sugereaza ca iconita de -cautare e activa doar cat timp nimic nu a fost inca ales; **neverificat** insa exact ce proprietate -citeste `cconditie` (clasa `ct_clb_cautare` nu a fost gasita in text in acest repo, posibil intr-un -`.vcx` neconvertit sau in `COMUNROA`), deci nu se poate confirma daca astazi re-alegerea e blocata -explicit sau doar descurajata vizual. De tratat ca ipoteza, nu ca fapt confirmat. - -### 2.2 Contractul functiei - -`caut_facturi_multiple_client(tnIdPart, tnInValuta, tnIdValuta, tlFacturiMultiple)` -(`oproceduri_facturare.prg:2091-2121`): -- **Intrare**: client (obligatoriu — dialogul nu porneste fara el), valuta (doar daca `tnInValuta`), - `tlFacturiMultiple=.T.` la acest apel -> `lnTipReturn=1` in `cauta_alfa`. -- **Sursa**: `select serie_act,numar_act,data_act,dataora,id_vanzare from fact_vfacturi where - sters=0 and tip in (1,2,3,4,5,6,10,-1,-2,-3,-4,-11) [+ conditie sucursala] [+ client] [+ valuta]` - — fara filtru SQL pe perioada/serie (filtrarea pe acele coloane e comportament generic al - `cauta_alfa`, nu parametru dedicat). -- **Iesire**: XML cu randurile bifate (`ales=1`), convertit local prin `Xmltocursor(..., - "crsFacturiTemp")`; **nimic nu ajunge la Oracle in acest pas**. -- **Populare aditiva a rezultatului in VFP**: `poDate.listaid = cursor2lista("crsFacturiTemp", - "id_vanzare", ",")` — **inlocuieste** intreg continutul (`=`, nu `+=`); daca dialogul se apeleaza a - doua oara azi, a doua rulare suprascrie complet prima (dar azi asta nu se intampla niciodata pentru - ca dialogul ruleaza o singura data, inainte de orice alta actiune pe document — vezi 2.1). -- **`cauta_alfa(...,tnTipReturn=1)`** (`COMUN\programe\cauta_alfa.prg:16-260`, mecanismul generic - folosit peste tot in suita): adauga singur coloana `ales` (`cauta_alfa.prg:124`), titlu explicit - "Alegeti ... (mouse-click pe numar sau apasati SPACE)", filtrare interactiva pe coloanele afisate. - Nu are mecanism de dezactivare a unei optiuni individuale, nici submeniu — doar bifare/debifare. - -### 2.3 Varianta per-articol - -`caut_facturi_multiple_client_articol(tnIdPart, tnInValuta, tnIdValuta, tlFacturiMultiple, tnIdArticol)` -(`oproceduri_facturare.prg:2124-2159`) — **aceeasi functie de baza**, cu doua diferente: filtru -suplimentar `b.id_articol = ?pnIdArticol` (join pe `vanzari_detalii`) si `tlFacturiMultiple=.F.` la -apelul din `do_adauga_articol` (`:12884`), deci `lnTipReturn=0` -> selectie **simpla**, nu multipla. -Titlu simplu "Alegeti factura". Rezultatul e un obiect cu un singur `id_vanzare`, nu un cursor. - ---- - -## 3. Proiectarea punctului 1 — alegerea facturilor sursa ca sursa in meniul de adaugare - -**Ce NU se schimba**: alegerea propriu-zisa a facturilor sursa (dialogul cu bifare multipla) ramane -la nivelul antetului — in formularul unificat, sectiunea pliata (decizia 7). Verificat impotriva -tabelului "puncte marunte" din plan (`plan_13...md`, randul h): *„Alege facturile de returnat…" -ramane doar la antet in etapa I; mutarea (in bara de butoane) e o poveste separata* — si confirmat -independent de tabelul de meniu al lui `s4b_bara_butoane_meniu.md` §3: pe randul "retur (8,9,24)", -meniul "Adauga articole" propus contine **"Adauga tot din facturile alese" · "Alege liniile de -returnat…" · "Cauta in lista de preturi…" · "Alege din nomenclator…"** — nu si "Alege facturile de -returnat…". **Punctul 1 al lui S4f nu contrazice asta**: ce se muta in meniul de adaugare e -rezultatul alegerii deja facute la antet (liniile din facturile alese), simetric cu comanda/ -contract/avize, nu dialogul de alegere a facturilor insusi. - -**Ce SE schimba, si e proiectarea reala**: azi secventa e rigida — antet modal -> `cursor_retur` -> -formular de articole (sectiunea 2.1). In formularul unificat exista un singur formular, mereu -deschis; sectiunea de antet (pliata) si gridul de articole coexista. Trei consecinte de proiectat: - -1. **Cand se declanseaza `cursor_retur_document`?** Nu mai poate fi legat de "inchiderea antetului" - (nu mai exista acel eveniment). Trebuie legat de **confirmarea dialogului de alegere** (butonul de - cautare din sectiunea pliata, echivalentul lui `Ct_clb_altele`/`do_cauta_facturi`): imediat ce - utilizatorul bifeaza facturile si confirma, `poDate.listaid` se seteaza si `cursor_retur_document` - ruleaza pe loc, populand/repopuland `crsarticole`. Documentul poate porni cu tipul deja pe 8/9/24 - (ales din combo-ul de tip document) inainte ca vreo factura sa fie aleasa — grid-ul de articole - ramane gol pana la prima alegere, exact ca azi cand `Reccount(crsarticole)=0` da mesajul "Nu exista - articole pentru optiunile alese!" (`ofacturare.prg:325`). -2. **Poate opera la orice moment, nu doar o data.** Spre deosebire de azi (dialogul ruleaza o singura - data, inainte ca gridul sa existe), in formularul unificat butonul din sectiunea pliata e - apasabil oricand documentul e deschis. Trebuie decis explicit comportamentul la a doua apasare — - vezi punctul urmator. -3. **Alegere aditiva sau inlocuitoare, la a doua apasare.** Azi `poDate.listaid = ...` **inlocuieste** - (nu `+=`), dar azi nu conteaza pentru ca nu se poate apasa a doua oara dupa ce gridul exista. In - formularul unificat, daca utilizatorul a ales deja factura A, a adaugat cateva linii din ea in - `crsfactura`, si apoi vrea sa adauge si din factura B, doua variante: - - **(a) Inlocuire** — a doua alegere reseteaza `poDate.listaid`/`crsarticole` la noul set ales; - liniile deja mutate in `crsfactura` raman (crsfactura e independent de crsarticole dupa mutare), - dar orice linie inca needitata din factura A dispare din `crsarticole` fara avertisment. Simplu - de implementat (identic cu azi), dar surprinzator pentru operator. - - **(b) Aditiv** — a doua alegere **extinde** `poDate.listaid` cu facturile noi (evitand - duplicate) si ruleaza `cursor_retur_document` filtrat doar pe facturile noi, cu - `INSERT INTO crsarticole` (nu `SELECT INTO`), exact reteta deja propusa de - `s4b_bara_butoane_meniu.md` §9.3, optiunea 2 — cere cod VFP nou (bucla de filtrare + - `INSERT`), dar zero cod nou in `pack_facturare` (aceeasi procedura, apelata de mai multe ori). - Consistent cu filozofia deciziei 16 ("sursa umple documentul, nu il inchide"). - - **Recomandare**: varianta (b), aditiv — e coerenta cu tratamentul lista-de-preturi (S4e) si cu - comanda/contract (unde alegerea suplimentara nu sterge ce exista deja), si evita pierderea tacuta - de linii needitate. **Ramane decizie de confirmat de Marius** — nici plan-ul, nici - `s4b_bara_butoane_meniu.md` nu au tranzat-o explicit (§9.5, punctul 2 acolo o listeaza tot ca - deschisa). - -**Consecinta pentru validarea de antet**: garda de azi (`inainte_de_do_termin`, `descriere` obligatoriu -pentru tip 8/9, `ofacturare.vc2:9523-9526`) nu mai are un moment natural "inainte de a inchide -antetul" — se muta la garda echivalenta din `Termina` (aceeasi verificare, alt declansator). - ---- - -## 4. Proiectarea punctului 2 — ridicarea `But_retur` la nivel de document - -**Ce se pastreaza din calea per-articol**: alegerea gestiunii ramane per-articol, prin -`cursor_gestiuni_articol_retur` si `frm_articol_gest_factura` — decizia 22/N.3 spune explicit ca -gestiunea si pretul de achizitie **se aleg**, nu se mostenesc, pentru orice linie fara factura -sursa unica precisa (si N.2 nu are niciodata o factura "sursa unica a documentului", are cel mult -factura aleasa pentru articolul curent). Punctul 2 nu schimba asta. - -**Ce se schimba**: alegerea facturii sursa nu mai e per-articol (un dialog `cauta_alfa` cu selectie -simpla la fiecare `do_retur`), ci **la nivel de document**, dupa modelul N.1 — un singur dialog cu -selectie multipla (`caut_facturi_multiple_client`, acelasi ca la N.1, dar filtrat pe articolul -curent doar daca ramane necesar; de decis daca filtrul pe articol dispare complet odata cu ridicarea -la document). Consecinta directa: `poDate.listaid` (sau un camp nou, vezi mai jos) ar fi populat o -singura data pentru intreg documentul, nu reconstruit per articol prin `caut_facturi_multiple_client_articol`. - -**Capcana de proiectare, gasita pe cod**: `poDate.listaid` e deja **partajat** intre N.1 si N.2 azi — -N.2 il suprascrie transient (`lnListaIdOld = poDate.listaid` ... `poDate.listaid = m.lnListaIdOld`, -`ofacturare.vc2:12882,12892`) doar cat dureaza alegerea per-articol, apoi il restaureaza. Daca N.2 -ridicat la document incepe sa scrie **permanent** in `poDate.listaid` (ca N.1), cele doua mecanisme -intra in conflict pe acelasi camp — un document normal (tip 1/5/7/10) cu retur ridicat la nivel de -document ar avea `poDate.listaid` populat cu facturile de retur alese, camp care pe alte tipuri -(comanda/contract/avize) e deja folosit cu alt sens (id-uri de comanda/contract/aviz sursa, vezi -`ofacturare.prg:266-308`). **Nu e conflict pe tip 1/5/7/10** — acele tipuri nu folosesc azi -`poDate.listaid` pentru altceva (sunt populate cu `cursor_preturi`, care nu ia `V_LISTAID` ca -parametru) — deci reutilizarea e sigura tehnic, dar merita un camp cu nume propriu -(`poDate.listaid_retur` sau similar) ca sa nu se ambiguizeze semantic ce inseamna `listaid` pe un -document normal. **De decis la implementare**, nu blocant. - -**Ce arata in bara de butoane / meniu (S4b)**: tabelul din `s4b_bara_butoane_meniu.md` §3 nu are -azi un rand pentru "retur intr-o factura normala" — doar pentru documentele de retur propriu-zise -(8,9,24). Ridicarea lui N.2 la nivel de document introduce o optiune noua in meniul "Adauga -articole" **pentru tipurile 1,5,7,10**: "Retur de articole…" (deja anticipata in tabelul §3 al -raportului S4b, coloana lista de preturi: "Cauta in lista de preturi… · Alege din nomenclator… · -**Retur de articole…**"). Aceasta optiune deschide dialogul de alegere multipla la nivel de document -(prima data), apoi pe alegerile ulterioare acelasi comportament aditiv/inlocuitor discutat la -sectiunea 3 se aplica identic. - -**Ce dispare**: `caut_facturi_multiple_client_articol` (filtrul per-articol) ramane cod mort daca -alegerea trece integral la nivel de document — sau se pastreaza ca alegere secundara optionala -("mai am nevoie de o factura sursa doar pentru articolul X", nefolosita azi in acest scenariu). Nu -e clar din decizia 17/N.3 daca trebuie eliminata explicit sau doar nu mai e calea principala — **de -decis de Marius** (risc R3). - ---- - -## 5. Proiectarea punctului 3 — liniile libere pe document de retur - -**Corectie fata de premisa initiala a brief-ului** ("prin acelasi `APPEND FROM` ca la S4e"): S4e a -gasit ca reteta literala a deciziei 16 (`ofacturare.prg:454-473`) **nu e sigura** pe niciun tip unde -`crsarticole` e citit ca registru de `do_sterge`/`do_scrie_factura`, si a documentat asta explicit -pentru retur, in tabelul sau de la §7: - -> "**retur ca document (8,9,24)** | `crsarticole` = registru citit de `do_scrie_factura`? **Nu** — -> cad pe `Otherwise`, fara `Sum(cantitate)`/`pnParametruAditional` | `do_sterge` ajusteaza -> `crsarticole` pe `id_c`? **Da, partial** — `Inlist(poDate.tip,8,9,24) => Replace cantitate With -> cantitate - poArticol.cantitate For id_c = poArticol.id_c` (`:14658-14659`, semn opus, urmareste -> maximul returnabil, nu inchiderea) | Concluzie: **risc de coliziune `id_c` ramane, dar fara -> poluarea `Sum`**: o linie libera adaugata literal prin `APPEND FROM` in `crsarticole` pe un -> document de retur ar putea, la stergere, atinge tacut cantitatea maxima returnabila a unei linii -> reale — de tratat cu aceeasi solutie (linii libere in afara `crsarticole`) si in S4f." -> (`s4e_lista_preturi_pe_sursa.md`, §7, randul "retur ca document") - -Solutia S4e (verificata acolo pe comanda/avize) se aplica identic aici — sectiunile de mai jos sunt -rescrise pe ea, nu pe `APPEND FROM`. - -### 5.1 Mecanismul corect: `APPEND BLANK` pe `crsfactura`, niciodata pe `crsarticole` - -**Liniile libere nu intra in `crsarticole`.** Se adauga direct in `crsfactura`, prin acelasi gest deja -in productie la `frm_facturare_articole2.But_nou1.do_adauga` (`ofacturare.vc2:17118-17122`): - -``` -PROCEDURE do_adauga - Select crsFactura - APPEND BLANK - this.grd_factura.SetFocus() -ENDPROC -``` - -urmat de editare inline prin `combosql` pe celula de cod/denumire (mecanismul construit de S4/S4e -pentru cautarea filtrata pe server, `pack_facturare.cursor_preturi` cu `V_FILTRU_COD`/`V_FILTRU_DEN`). -Optiunea de meniu "Cauta in lista de preturi…" (deja in tabelul S4b §3, pe randul retur) declanseaza -exact acest gest — **nu** un `cursor_retur_document` suplimentar si **nu** un `APPEND FROM` peste -`crsarticole`. - -### 5.2 Cum se distinge o linie libera de una din factura sursa - -**Campul corect e `crsfactura.id_c`, nu o coloana pe `crsarticole`.** O linie mostenita ajunge in -`crsfactura` prin `do_adauga_articol`, care face `Select (lcCursor) / Scatter Name poArticol` pe -randul ales din `crsarticole` (populat de `cursor_retur_document`, cu `id_c = ROWNUM`, pornind de la -1) si copiaza acel `id_c` in randul nou din `crsfactura`. O linie libera, adaugata prin `APPEND BLANK` -direct pe `crsfactura` (5.1), **nu trece niciodata prin acest `Scatter`** — `id_c` ramane la valoarea -implicita a campului (`N(20)`, fara `Null`, deci `0`, `creeaza_facturacrs`, -`COMUN\programe\ofacturare_comun.prg:1775-1785`, citat si verificat de S4e §3 punctul 3). **Oracle nu -produce niciodata `id_c = 0`** (`ROWNUM` incepe la 1) — deci `crsfactura.id_c = 0` e semnalul curat, -fara camp nou, care distinge o linie libera de una mostenita. Acelasi tipar e deja in productie pentru -linia de discount (`ofacturare.vc2:14531-14537`, `Append Blank` fara `id_c` setat). - -**Consecinta directa, fara cod suplimentar**: `do_sterge`, ramura de retur -(`Case Inlist(poDate.tip,8,9,24) => Select crsarticole / Replace cantitate With cantitate - -poArticol.cantitate For id_c = poArticol.id_c`, `ofacturare.vc2:14658-14659`) cauta in `crsarticole` -un rand cu `id_c = poArticol.id_c`. Pentru o linie libera, `poArticol.id_c = 0`, iar `crsarticole` nu -are niciodata un rand cu acel `id_c` — `REPLACE ... FOR` devine **no-op prin constructie**. Stergerea -unei linii libere de pe un document de retur **nu atinge** maximul returnabil al nici unei linii -mostenite — exact ce cerea brief-ul la punctul 5, si exact acelasi mecanism (nu doar aceeasi idee) ca -solutia S4e pentru comanda. - -### 5.3 Pretul de vanzare — riscul gasit (R1, ramane valabil) - -**Nu e doar despre gestiune/pret de achizitie, si nu se rezolva prin mutarea in `crsfactura`.** Cand -o linie ajunge la `do_alege_stoc` cu `Inlist(poDate.tip,8,9,24)` adevarat (orice linie pe un document -de retur, indiferent de origine — conditia e pe `poDate.tip`, nu pe un flag de linie), se deschide -`frm_articol_gest_factura`. In acel formular, la schimbarea coloanei de gestiune -(`grd_gestiuni.RowColChange`), daca `Thisform.lRetur` e adevarat: - -``` -If Thisform.lRetur - poArticol.pretftva = pretv && pret DIN STOC, nu din lista de preturi - ... - poArticol.cantitate = (-1) * cantitate -Endif -``` -(`ofacturare.vc2:4094-4103`) - -Pentru o linie **mostenita** (N.1), asta e corect prin constructie — returnezi marfa la pretul cu -care a fost vanduta. Pentru o linie **libera** (decizia 22 — articol niciodata vandut clientului, deci -fara "pret de retur" de mostenit), suprascrierea cu pretul de stoc e o **eroare de pret**: linia -libera ar iesi cu costul de stoc in loc de pretul din lista de preturi ales prin `combosql` la 5.1. - -**Solutie propusa, actualizata**: `frm_articol_gest_factura` primeste un al doilea semnal, distinct de -`tlRetur` ("esti pe flux de retur"), care sa insemne "aceasta linie are factura sursa" — derivat acum -din `crsfactura.id_c <> 0` (5.2), nu dintr-o coloana lipsa in `crsarticole`. Ramura de suprascriere -pret (`:4094-4103`) se activeaza doar cand acest al doilea semnal e adevarat. - -### 5.4 Golul nou, nu tratat de S4e: punctul de intrare in dialogul de gestiune - -**S4e nu are acest gol pentru ca nu are dialog de gestiune pe comanda/avize** — liniile libere de -acolo se completeaza integral prin `combosql` (pret, TVA, valuta) fara sa mai treaca prin niciun -dialog de alegere a gestiunii. Pe retur, decizia 22 cere explicit ca gestiunea si pretul de achizitie -sa **se aleaga** pentru o linie libera — mecanismul care stie sa faca asta e -`cursor_gestiuni_articol_retur` + `frm_articol_gest_factura`, dar acel dialog se intra azi **doar** -din `do_alege_stoc`, apelat de `do_adauga_articol` pe baza unui `poArticol` scatter-uit dintr-un rand -**deja incarcat in `crsarticole`** (`ofacturare.vc2:12843-12851,12891`). O linie libera adaugata prin -`APPEND BLANK` pe `crsfactura` (5.1) **nu are** un asemenea rand sursa — exact acelasi gol pe care -S4e l-a semnalat, netratat, pentru validarea de cantitate pe comanda (`s4e_lista_preturi_pe_sursa.md` -§6: *"`do_verifica_articol` cere un `poArticol` scatter-uit dintr-un cursor sursa deja incarcat... o -linie libera... nu are un asemenea rand sursa"*). - -**Consecinta pentru S4f, mai stricta decat pe comanda**: pe comanda/avize (S4e), lipsa unui `poArticol` -de sursa afecteaza doar validarea de cantitate (un gol de acoperit, dar linia se poate scrie si fara -gestiune aleasa — comanda/avize nu cer gestiune diferita de cea din lista de preturi). Pe retur, lipsa -aceluiasi `poArticol` blocheaza **si** alegerea gestiunii, care e obligatorie prin decizia 22. Trebuie -proiectat un punct de intrare nou in `frm_articol_gest_factura`/`do_alege_stoc`, care sa porneasca de -la randul **deja inserat in `crsfactura`** (dupa `combosql`, cu `id_articol` si cantitate deja -completate) in loc de la un rand din `crsarticole` — practic o a doua cale de apel, cu acelasi dialog -la capat, dar `poArticol` construit ad-hoc din `crsfactura` in loc de `Scatter` din `crsarticole`. -Aceeasi problema structurala, aceeasi solutie propusa (poArticol construit ad-hoc) ca varianta (b) de -la S4e §6 pentru `do_verifica_articol` — de unificat la implementare daca se poate, nu de rezolvat de -doua ori independent. - -### 5.5 Maximul returnabil — nu se aplica, si acum e clar de ce - -Confirmat structural: azi maximul returnabil pe N.1 **nu e un calcul de "cat s-a mai returnat"**, e -pur si simplu `CANTITATE` a liniei originale (`ff_...sql:4036`, `ofacturare.vc2:15236-15239`, -`do_verifica_articol:14743-14754`) — validarea respinge doar semnul gresit (`tnCantitate >= 0` in mod -retur), nu o depasire fata de un istoric. **Pentru o linie libera, intrebarea nici nu se pune in -termenii de azi**: `do_verifica_articol`, ca si dialogul de gestiune (5.4), asteapta un `poArticol` -din `crsarticole` — o linie libera nu are unul, deci nu poate trece prin comparatia de maxim existenta -nici macar din greseala. Validarea proprie a liniei libere (cantitate/stoc, construita ad-hoc, 5.4) -nu are nimic de comparat cu un "maxim returnabil" — verifica doar disponibilul de stoc curent, ca pe -o linie normala de lista de preturi (acelasi gol deschis de S4e §6 pentru comanda, aceeasi solutie). - ---- - -## 6. Efectul asupra `VANZARI_CORESP` - -**Scrierea nu depinde de continutul liniilor, ci de alegerea facuta la antet.** -`scrie_corespondente_vanzari(3)` (`ff_...sql:14834-14836`, apelata din `finalizeaza_factura`) scrie -direct din `pack_facturare.clistaid` (variabila de sesiune, = `poDate.listaid` trimis la -`initializeaza_date_factura`), **independent** de ce a ajuns efectiv in `VANZARI_DETALII` la -finalizare: - -```sql -INSERT INTO VANZARI_CORESP (ID_VANZARE_FACT, ID_VANZARE_AVIZ, TIP) - SELECT pack_facturare.nid_vanzare, ID_VANZARE, 3 - FROM VANZARI WHERE ID_VANZARE IN (SELECT X FROM table(charn2collection(V_LISTAID,','))); -``` -(`ff_...sql:15481-15516`, citat integral in `legatura_linie_retur.md:45-53`) - -**Consecinta directa pentru liniile libere**: daca documentul de retur contine si linii libere (din -lista de preturi), corespondenta **tot se scrie**, si scrie exact aceleasi facturi alese la antet — -liniile libere nu adauga si nu scot randuri din `VANZARI_CORESP`, pentru ca sursa e `poDate.listaid` -(fixat la antet), nu continutul `crsfactura`/`VANZARI_DETALII`. Un document de retur cu 3 linii -mostenite din factura A si 2 linii libere scrie tot un singur rand in `VANZARI_CORESP` -(`ID_VANZARE_FACT`=documentul nou, `ID_VANZARE_AVIZ`=factura A, `TIP=3`) — corespondenta descrie -"acest document a folosit ca sursa factura A", nu "aceste linii vin din factura A". - -**Ce se scrie cand nu s-a ales nicio factura sursa**: nu e un caz posibil pentru documentul de retur -insusi — validarea de antet (`inainte_de_do_termin`, `descriere` obligatoriu, sectiunea 3) impune cel -putin o factura aleasa inainte ca documentul sa poata continua. Deci pentru N.1/S4f (documente 8/9/24) -`clistaid` nu e niciodata gol la scriere. **Pentru N.2 ridicat la document (punctul 2)** raspunsul e -diferit: acolo documentul e de tip 1/5/7/10, iar `poDate.listaid`/campul nou echivalent (sectiunea 4) -nu e obligatoriu — un document normal fara nicio linie de retur nu are ce sa scrie. **Intrebare -deschisa, verificata partial**: daca `scrie_corespondente_vanzari(3)` e apelata azi doar cand -`ntip in (8,9)` (confirmat de `legatura_linie_retur.md:113`: *"TIP=3: factura de retur -(scrie_corespondente_vanzari(3), :14834-14836, la ntip in (8,9))"*), atunci **ridicarea lui N.2 la -nivel de document NU va scrie automat `VANZARI_CORESP`** — documentul ramane tip 1/5/7/10, gardat de -`ntip`, nu de continutul `listaid`. Linia exacta a acestui `IF`/`CASE` in exportul curent -(`ff_2026_08_09_01...sql`) **nu a fost re-verificata caracter cu caracter** in aceasta sesiune (citata -din raportul sursa, care a folosit un export anterior cu alta numerotare) — de reverificat la -implementare, dar concluzia structurala (gating pe `ntip`, nu pe `listaid`) e solida. - -**Decizie de proiectare care rezulta**: ridicarea lui N.2 la nivel de document (punctul 2) **nu -capata**, prin simpla ridicare, o corespondenta persistata in `VANZARI_CORESP` — comportamentul -raman identic cu azi (nicio legatura scrisa), *cu exceptia* cazului in care Marius decide explicit ca -merita cod Oracle nou (extinderea gardei `ntip in (8,9)` la un al treilea semnal, ex. "are `listaid` -populat"). Decizia 35 (un singur cod de scriere contabila, cod Oracle nou doar cand strict necesar) -face ca varianta "fara schimbare in pack_facturare" sa fie implicita — **de confirmat cu Marius** -(risc R4), nu de presupus. - ---- - -## 7. Semnul cantitatii / al valorii pe retur - -**Doua mecanisme diferite, verificate separat pe cod:** - -- **N.1 (documente 8/9/24)**: cantitatea vine deja cu semnul corect direct din - `cursor_retur_document` — `CANTITATE` a liniei originale (pozitiva ca valoare de referinta, dar - interpretata cu semn de retur prin `poDate.tip`); semnul efectiv de scriere in `VANZARI_DETALII` - e controlat de codul Oracle care insereaza documentul de retur (nu urmarit aici, in afara - perimetrului VFP), iar in UI validarea `do_verifica_articol` (`llRetur = Inlist(poDate.tip,8,9,24)`) - respinge `tnCantitate >= 0` in mod retur — deci utilizatorul introduce cantitatea ca valoare - pozitiva ("cat returnez"), iar semnul negativ e o conventie aplicata la nivel de tip de document, - nu per linie. -- **N.2 (`But_retur`)**: semnul negativ **e aplicat explicit in cod VFP**, in - `frm_articol_gest_factura` — `poArticol.cantitate = (-1) * cantitate` (`ofacturare.vc2:4103`), - declansat de `Thisform.lRetur` (adevarat cand `tlRetur` a fost trimis la `Init`, indiferent de - `poDate.tip`). Aici semnul **nu** vine din `poDate.tip` (documentul e tip 1/5/7/10, pozitiv prin - design), ci e o negatie explicita facuta pentru ca acea linie anume e un retur intr-un document - altfel normal. - -**Implicatie pentru liniile libere pe document de retur (S4f punctul 3)**: pentru ca documentul -intreg e tip 8/9/24, orice linie — libera sau mostenita — trece prin acelasi tratament de tip de -document (semnul negativ e o proprietate a **documentului**, nu a liniei, in acest caz). Nu exista -risc de "linie libera cu semn pozitiv gresit" atata timp cat validarea ramane gatata pe `poDate.tip` -si nu pe originea liniei — de verificat explicit ca ramura noua a validarii (5.4, sarirea peste -maximul returnabil) **nu** atinge si sare peste verificarea de semn, care trebuie sa ramana activa -identic pe linii libere si mostenite. - -**Implicatie pentru N.2 ridicat la document (punctul 2)**: aici semnul **ramane** o proprietate de -linie (nu de document, pentru ca documentul e tip normal 1/5/7/10) — mecanismul de la -`ofacturare.vc2:4103` se pastreaza neschimbat, declansat de acelasi `tlRetur`/`Thisform.lRetur`, -indiferent daca alegerea facturii sursa se face per-articol (ca azi) sau la nivel de document -(punctul 2). Nimic de schimbat aici — doar de confirmat ca noul flux (alegere la document) tot -seteaza `tlRetur=.T.` la apelul catre `do_alege_stoc`/`frm_articol_gest_factura` pentru fiecare linie -de retur adaugata. - ---- - -## 8. Unde se aseaza informatia de provenienta in UI - -**Inchis pe cod, cf. `legatura_linie_retur.md`**: legatura linie-la-linie nu se poate afisa in niciun -caz — nici `cursor_retur_document` nu aduce `ID_VANZARE`/`ID_VANZARE_DET` in `crsarticole` -(`ff_...sql:3965-4028`, coloanele lipsesc din SELECT), nici `INSERT`-ul final in `VANZARI_DETALII` -nu are coloana de sursa (`PACK_FACTURARE:13705-13757`, listata explicit, fara camp de provenienta). - -**Ce se poate afisa, verificat**: la nivel de **document**, cand exact o singura factura sursa a fost -aleasa, `VANZARI_CORESP(TIP=3)` da fara ambiguitate factura sursa: - -```sql -SELECT v.serie_act, v.numar_act - FROM VANZARI_CORESP c JOIN VANZARI v ON v.ID_VANZARE = c.ID_VANZARE_AVIZ - WHERE c.ID_VANZARE_FACT = :id_document_retur AND c.TIP = 3 AND c.STERS = 0; -``` -(`legatura_linie_retur.md:174-178`, interogare formulata, neverificata pe date live in acel raport) - -**Propunere concreta de text si conditie** (in romana, pentru sectiunea pliata / antetul -formularului unificat): - -- **O singura factura sursa aleasa**: eticheta fixa de tip "Provine din factura: **{serie} {numar}**" - langa campul de descriere existent (`poDate.text_aditional`, deja populat azi cu - "RETUR FACTURA {numere}" — `factura_retur_document.md:65`), afisata read-only, imediat sub sau langa - campul de tip document. -- **Mai multe facturi sursa alese**: eticheta devine "Provine din facturile: **{lista serie+numar}**" - — aceeasi sursa de date (`poDate.descriere`, deja populata cu lista, nu doar `VANZARI_CORESP`), - **fara** sa se sugereze vreo distributie pe linii. -- **Niciodata pe linie** — nici coloana in grid, nici tooltip per rand care sa pretinda o sursa - exacta. Pentru liniile libere in particular, nu exista nimic de afisat (nu au sursa) — nu se - introduce o valoare "N/A" vizibila care ar sugera ca alte linii AU o sursa unica atunci cand de - fapt (cu selectie multipla) nici ele nu o au cu certitudine. -- **Conditie tehnica**: textul se construieste din `poDate.descriere` (deja in memorie, fara interogare - suplimentara) — nu necesita interogarea `VANZARI_CORESP` de mai sus decat daca se doreste - reafisarea la redeschiderea unui document deja emis (regenerare, etapa II) — caz in care - interogarea de mai sus e necesara pentru ca `poDate.descriere` nu mai exista in memorie. - ---- - -## 9. Pasi de implementare ordonati - -1. **Confirmarea comportamentului aditiv/inlocuitor la alegerea facturilor sursa** (sectiunea 3, - punctul 3) — decizie de produs, nu cod. *Gata cand:* Marius a ales (a) sau (b); fara asta pasii 2-3 - nu se pot implementa fara risc de refacere. - *Verificabil:* niciunul (decizie, nu cod). -2. **Butonul/actiunea de alegere a facturilor sursa, mutat in sectiunea pliata a formularului - unificat**, pastrand `do_cauta_facturi`/`caut_facturi_multiple_client` neschimbate — doar - punctul de declansare se muta din antetul modal in sectiunea pliata a formularului unic, si - `cursor_retur_document` se declanseaza la confirmarea dialogului, nu la inchiderea unui formular - separat (sectiunea 3, punctele 1-2). - *Gata cand:* pe un document nou de tip 8/9/24, deschis in formularul unificat, alegerea facturilor - sursa din sectiunea pliata populeaza `crsarticole` cu exact aceleasi randuri ca azi (gestiune, pret - achizitie, pret vanzare identice), verificabil prin comparatie directa a cursorului. -3. **Meniul "Adauga articole" pe tipurile 8/9/24**, cu optiunile "Adauga tot din facturile alese" / - "Alege liniile de returnat…" peste `crsarticole` deja populat (reteta comuna S4b, sectiunile 4-5 - ale acelui raport) — fara alegere de facturi in acest meniu (sectiunea 3). - *Gata cand:* meniul contine exact aceste doua optiuni pe tip 8/9/24, plus "Cauta in lista de - preturi…"/"Alege din nomenclator…" (punctul urmator). -4. **Liniile libere — `APPEND BLANK` pe `crsfactura`, semnalul de distinctie pe `id_c`** (sectiunile - 5.1-5.2): optiunea de meniu "Cauta in lista de preturi…" pe tip 8/9/24 declanseaza - `Select crsFactura / APPEND BLANK` + `combosql`, exact tiparul S4e (`ofacturare.vc2:17118-17122`) - — **nu** `APPEND FROM` peste `crsarticole`. `id_c` ramane implicit (`0`) pe randul nou. - *Gata cand:* dupa adaugarea unei linii libere pe un document de test, `crsfactura.id_c = 0` pe acel - rand si `crsarticole` ramane neschimbat (acelasi `Reccount`/continut ca inainte de adaugare). -5. **Punct de intrare nou in dialogul de gestiune, pornind de la randul din `crsfactura`** (sectiunea - 5.4) — `frm_articol_gest_factura`/`do_alege_stoc` primesc o a doua cale de apel, cu `poArticol` - construit ad-hoc din randul deja inserat in `crsfactura` (dupa `combosql`), nu din `Scatter` pe un - rand din `crsarticole`. Fara acest pas, o linie libera nu poate ajunge deloc la alegerea gestiunii - ceruta de decizia 22. - *Gata cand:* pe o linie libera adaugata pe un document de retur, dialogul de gestiune se deschide si - returneaza `ID_GESTIUNE`/`PRET_ACHIZITIE` alese de operator, fara sa fi trecut prin `crsarticole`. -6. **Fixarea pretului de vanzare pe liniile libere** (sectiunea 5.3, riscul R1) — ramura de - suprascriere din `frm_articol_gest_factura` (`ofacturare.vc2:4094-4103`) primeste al doilea semnal - ("are sursa" vs. "e libera"), derivat din `crsfactura.id_c <> 0` (pasul 4), si nu suprascrie - `pretftva` cand linia e libera. - *Gata cand:* o linie libera adaugata pe un document de retur pastreaza pretul din lista de preturi - dupa alegerea gestiunii, verificabil comparand `poArticol.pretftva` inainte si dupa - `RowColChange` in dialogul de gestiune. -7. **Validarea de cantitate/stoc pe liniile libere** (sectiunea 5.5) — mecanism nou, nu - `do_verifica_articol` ca atare (care cere un `poArticol` din `crsarticole`); verifica disponibilul - de stoc curent, fara nicio comparatie cu un maxim returnabil. Unificabil cu golul echivalent - deschis de S4e §6 pentru comanda/avize, daca implementarea alege aceeasi varianta (`poArticol` - construit ad-hoc, parametru explicit). - *Gata cand:* pe o linie libera, orice cantitate <= stocul curent trece validarea, cu mesaj propriu - (nu "cantitate maxima de returnat"); pe o linie mostenita, comportamentul de azi ramane neschimbat. -8. **Ridicarea `But_retur` la nivel de document (punctul 2)** — dialog de alegere multipla la - deschiderea primei linii de retur pe un document normal (1/5/7/10), populare cu un camp nou sau - reutilizarea controlata a lui `poDate.listaid` (sectiunea 4), pastrand calea per-articol existenta - ca fallback sau eliminand-o explicit (decizie separata, risc R3). - *Gata cand:* pe o factura normala, alegerea facturilor sursa se face o singura data, iar liniile de - retur adaugate ulterior (mai multe articole) folosesc acel set fara sa redeschida dialogul de - cautare la fiecare articol. -9. **`VANZARI_CORESP` pentru N.2 ridicat la document** (sectiunea 6) — de decis daca se adauga cod - Oracle nou sau ramane fara corespondenta persistata (risc R4); implementat doar dupa decizia lui - Marius. - *Gata cand:* comportamentul ales (cu sau fara corespondenta) e implementat consistent si testat pe - un document cu retur ridicat la nivel de document si mai multe facturi sursa. -10. **Textul de provenienta la antet** (sectiunea 8) — afisare read-only din `poDate.descriere`, - condusa de numarul de facturi alese (0/1/multiple). - *Gata cand:* eticheta arata corect in toate trei cazurile (nicio factura inca, o factura, mai - multe), fara sa apara pe grid/linie. - -*Depinde de:* S2 (formular unificat de baza), S4e (mecanismul `APPEND BLANK` + `combosql` pentru -liniile libere pe document cu sursa, NU `APPEND FROM`) — exact ca in plan (`plan_13...md:2330`), -corectat fata de reteta literala a deciziei 16. - ---- - -## 10. Cum se verifica - -**Proba din plan** (`plan_13...md:2327-2329`): *"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."* - -Pasi concreti: - -1. **Paritate N.1**: pe date de test, emite un document de retur (tip 8) pe calea veche (formularul - actual) si retine `crsarticole` rezultat (gestiune, pret achizitie, pret vanzare, cantitate per - linie). Repeta aceeasi alegere de facturi sursa in formularul unificat si compara `crsarticole` - camp cu camp — trebuie sa fie identic, pentru ca sursa Oracle (`cursor_retur_document`) nu se - schimba, doar punctul VFP care o declanseaza. -2. **Stergere si retur partial**: pe formularul unificat, sterge o linie mostenita si verifica ca - cantitatea reintra in `crsarticole` (acelasi test ca azi, `do_sterge` neschimbat); introdu o - cantitate mai mica decat maximul pe o linie mostenita si verifica validarea. -3. **Linie libera**: adauga o linie din lista de preturi pe documentul de retur deschis (prin - `APPEND BLANK` + `combosql`, nu `APPEND FROM`); verifica (a) `crsfactura.id_c = 0` pe acel rand si - `crsarticole` neschimbat; (b) pretul de vanzare = pretul din lista de preturi, nu din stoc; (c) - gestiunea/pretul de achizitie sunt cerute prin dialogul de gestiune (intrat pe calea noua, 5.4), nu - completate automat; (d) cantitatea nu e limitata de niciun maxim istoric, doar de stocul curent; - (e) semnul cantitatii ramane negativ (consistent cu documentul); (f) stergerea liniei libere nu - modifica `crsarticole` (verifica direct: `Reccount`/continut identic inainte si dupa stergere). -4. **`VANZARI_CORESP` neschimbat de liniile libere**: dupa emiterea documentului din pasul 3, - interogheaza: - ```sql - SELECT ID_VANZARE_FACT, ID_VANZARE_AVIZ, TIP FROM VANZARI_CORESP - WHERE ID_VANZARE_FACT = :id_document_nou AND TIP = 3; - ``` - — trebuie sa arate exact facturile alese la antet, indiferent de cate linii libere s-au adaugat. -5. **N.2 ridicat la document**: pe o factura normala, adauga doua linii de retur pentru articole - diferite folosind acelasi set de facturi sursa ales o singura data (nu redeschide dialogul la - fiecare articol); verifica semnul cantitatii (negativ) si absenta oricarei scrieri in - `VANZARI_CORESP` (daca decizia de la punctul 8/risc R4 pastreaza comportamentul de azi). -6. **Provenienta**: deschide un document de retur cu o singura factura sursa — verifica textul de - antet; alege doua facturi sursa — verifica ca textul listeaza ambele si nu implica o sursa unica. - -## 11. Ce nu se poate testa headless - -Reluat din capcanele deja confirmate pe acest proiect (`s4b_bara_butoane_meniu.md` §8), plus -specificul retur: - -- **`cauta_alfa` cu `tnTipReturn=1`** — dialog modal (`.Show(1)`), blocheaza thread-ul UI; testarea - automata a bifarii multiple nu se poate face prin injectare de input pe aceasta masina (partajata, - memoria `masina-partajata-fara-input-real`). Se verifica indirect: apeland direct - `caut_facturi_multiple_client`/`cursor_retur_document` cu parametri de test si citind cursorul - rezultat, ocolind UI-ul. -- **`frm_articol_gest_factura`** — acelasi tip de dialog modal; verificarea suprascrierii de pret - (risc R1, sectiunea 5.3) si a noului punct de intrare pornit din `crsfactura` (sectiunea 5.4) se - poate face doar citind codul metodei `RowColChange`/noua metoda de intrare si, separat, apeland - direct logica de calcul cu date de test simulate — nu prin click real in grid. -- **Coloanele griduri** (`grd_articole`, gridul dialogului de alegere) — capcana deja cunoscuta: - `ColumnCount=0` sub `-A -T`; verificare vizuala necesita harness UI vizibil, nu headless. - (memoria `grid-coloane-nu-se-materializeaza-headless`) -- **Textul de provenienta din sectiunea pliata** (sectiunea 8) — verificabil headless ca **continut - al proprietatii** (`Caption`/`Value` a controlului), nu ca "arata bine" vizual. -- **Secventa reala de deschidere a dialogului din sectiunea pliata** (declansarea la click, nu doar - codul din spatele ei) — necesita harness UI vizibil pentru captura de interactiune. - -**Ce se poate verifica headless, direct**: continutul `crsarticole`/`crsfactura` dupa apeluri directe -ale rutinelor (`cursor_retur_document`, `do_adauga_articol`, `do_verifica_articol`, `APPEND BLANK` pe -`crsfactura`) cu parametri de test; valoarea `crsfactura.id_c` pe randurile noi (semnalul de la 5.2, -`0` pe liniile libere, valoare reala pe cele mostenite); continutul real din -`VANZARI_CORESP`/`VANZARI_DETALII` dupa emiterea unui document de test pe schema Oracle (doar -`SELECT`, verificare post-factum). - ---- - -## 12. Riscuri si ce ramane de decis de Marius - -**R1 — Pretul de vanzare pe liniile libere se suprascrie cu pretul de stoc, daca nu se trateaza -explicit** (sectiunea 5.3). Cel mai concret risc gasit in aceasta cercetare, netratat in plan pana -acum. **Recomandare**: al doilea semnal ("linie cu sursa" vs. "linie libera") pe langa `tlRetur`, -derivat din `crsfactura.id_c <> 0` (5.2), care sa dezactiveze suprascrierea de pret doar pentru -liniile libere. Necesita o mica modificare in `frm_articol_gest_factura` (nu in `pack_facturare`). - -**R7 — Punctul de intrare in dialogul de gestiune pentru o linie libera nu exista azi** (sectiunea -5.4). Gol nou, mai strict decat echivalentul lui pe comanda/avize (S4e §6, doar validare de -cantitate) — pe retur blocheaza si alegerea gestiunii/pretului de achizitie, obligatorie prin decizia -22. `do_alege_stoc`/`frm_articol_gest_factura` se intra azi doar dintr-un `poArticol` scatter-uit din -`crsarticole`; o linie libera (`APPEND BLANK` pe `crsfactura`, 5.1) nu are asa ceva. **Recomandare**: -a doua cale de apel, cu `poArticol` construit ad-hoc din randul `crsfactura` deja completat prin -`combosql`, unificata daca se poate cu solutia aleasa pentru golul echivalent de validare (R7 aici, -S4e §6/varianta b acolo) — un singur mecanism de "construieste `poArticol` fara sursa in `crsarticole`", -nu doua independente. - -**R2 — Alegere aditiva sau inlocuitoare la a doua apasare a dialogului de facturi sursa** (sectiunea -3, punctul 3). Nu e tratat de nicio decizie existenta. **Recomandare**: aditiv, consistent cu -filozofia deciziei 16, dar cere cod VFP nou (bucla de filtrare pe facturi noi + `INSERT INTO -crsarticole`). - -**R3 — Ce se intampla cu `caut_facturi_multiple_client_articol` (alegerea per-articol) dupa ridicarea -lui `But_retur` la nivel de document.** Ramane ca optiune secundara sau se elimina? Nefolosit oriunde -altundeva in cod (verificat implicit — apelul e doar din `do_adauga_articol`, ramura `tlRetur`). -**Recomandare**: se elimina, pentru ca pastrarea ambelor cai ar insemna doua UX-uri pentru aceeasi -actiune, exact riscul pe care unificarea vrea sa il elimine. - -**R4 — `VANZARI_CORESP` pentru N.2 ridicat la document.** Gardat azi pe `ntip in (8,9)`, nu pe -continutul `listaid` — **reconfirmat independent** in aceasta sesiune, pe exact acelasi export -(`ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:14834-14836`): `WHEN pack_facturare.ntip in (8, 9) THEN -pack_facturare.scrie_corespondente_vanzari(3);`, in interiorul unui `CASE` fara alta conditie pe -`listaid`. Daca ramane asa, un document normal -cu retur ridicat la document nu va avea nicio legatura persistata catre facturile sursa alese — exact -ca azi cu `But_retur` per-articol, deci nu e o regresie, dar nici un castig. **Recomandare**: se lasa -neschimbat (fara cod Oracle nou), consistent cu decizia 35 (economie de cod de intretinut) — de -confirmat explicit cu Marius, pentru ca punctul 2 al S4f ("ridicarea la nivel de document") ar putea -crea asteptarea implicita ca acum exista si o legatura persistata, cand de fapt nu exista. - -**R5 — `Ct_clb_altele.cconditie` — semantica exacta neconfirmata** (sectiunea 2.1). Daca acest camp -chiar dezactiveaza azi re-alegerea dupa prima selectie, design-ul nou (sectiunea 3) trebuie sa decida -explicit sa **elimine** aceasta restrictie (pentru varianta aditiva) — altfel comportamentul nou ar -fi mai permisiv decat parea sa fie azi, fara sa fi fost o decizie constienta. **De verificat la -implementare**, cand clasa `ct_clb_cautare` devine accesibila (posibil in `COMUNROA`, in afara acestui -repo). - -**R6 — Semnul cantitatii pe liniile libere, verificare explicita ceruta de brief** (sectiunea 7): nu -e un risc nou — analiza arata ca semnul e o proprietate a documentului (tip 8/9/24), nu a liniei, -deci liniile libere il mostenesc automat corect. Se listeaza aici doar ca sa fie explicit ca punctul -a fost verificat, nu omis. - ---- - -## Handoff - -Cercetare incheiata fara sa fie nevoie de predare de context — un subagent de verificare -(`ab0be8e27f0fb97f3`, Explore/general-purpose) a esuat de doua ori sa returneze rezultate, apoi a -raspuns complet la a treia incercare (reluat prin `SendMessage`), confirmand independent 4 puncte deja -scrise pe cod propriu in raport: (1) `do_cauta_facturi` e declansat explicit de user, prin -`Ct_clb_altele`/`caut_ora.vc2:787-798,839-844` -> `do_cauta_altele` -> `do_cauta_facturi` -(`ofacturare.vc2:8940-8961,9173`), inainte de orice deschidere a `frm_facturare_articole`; (2) -`scrie_corespondente_vanzari(3)` e gardat exact pe `ntip in (8,9)` -(`ff_...sql:14834-14836`) — folosit sa inchid definitiv R4; (3) -`thisform.cListaIdArticoleRetur` poate acumula `ID_VANZARE` diferit per articol -(`ofacturare.vc2:12882-12892`); (4) `poDate.listaid` e acelasi camp la N.1 si N.2, dar cu rol -tranzitoriu la N.2 (salvat/restaurat) si definitiv doar la `do_scrie_articole` -(`ofacturare.vc2:13977-13979`). Niciuna din aceste confirmari nu a schimbat vreo concluzie a -raportului, doar le-a intarit sursa. - -**Corectie aplicata dupa livrare**, la avertismentul `team-lead` (bazat pe -`docs\cercetare\s4e_lista_preturi_pe_sursa.md`, sectiunile 3 si 7): sectiunea 5 (liniile libere) a -fost **rescrisa integral** — reteta `APPEND FROM` peste `crsarticole` (premisa initiala a brief-ului) -e inlocuita cu mecanismul validat de S4e (`APPEND BLANK` pe `crsfactura` + `combosql`, niciodata pe -`crsarticole`), campul de distinctie devine `crsfactura.id_c = 0` (nu absenta `PRET_ACHIZITIE` pe -`crsarticole`), si s-a adaugat un gol nou, negasit de S4e (care nu are dialog de gestiune pe -comanda/avize): punctul de intrare in `frm_articol_gest_factura` pentru o linie fara rand sursa in -`crsarticole` (risc R7, nou). Verdictul de la inceputul raportului, pasii de implementare (9), -verificarea (10) si sectiunile headless (11) au fost actualizate in consecinta. Restul raportului -(sectiunile 1-4, 6-8, 12 minus R1/R7) ramane neschimbat fata de livrarea initiala. - -Fisierul SQL folosit: `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` -(cel mai recent din director). Nicio editare de cod, niciun `git_sync.ps1`/`txt2vcx.ps1`, niciun -commit, nicio scriere Oracle. diff --git a/docs/cercetare/s4g_adaugare_articole_modificare.md b/docs/cercetare/s4g_adaugare_articole_modificare.md deleted file mode 100644 index 45ea33c..0000000 --- a/docs/cercetare/s4g_adaugare_articole_modificare.md +++ /dev/null @@ -1,437 +0,0 @@ -# S4g — Adaugarea de articole la modificarea oricarui document (proiectare) - -Proiectare, nu implementare. Zero fisiere de cod atinse, zero write-back, zero commit. Pe Oracle -doar `SELECT`. Continua planul `docs\plan_13_unificare_formular_facturare.md` liniile 2494-2530 si -cercetarile `canal_cont_venit_fara_politica.md` (sectiunea "Proiectarea parametrului de cont -contabil"), `parametru_cont_contabilizeaza_articol.md`, `coresp_cont_venchelt.md`, -`optiune_firma_cont_debit.md`. - -**STARE: cercetare/proiectare incheiata.** - -Sursa PL/SQL verificata direct in aceasta runda: `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` -(17217 linii, confirmat `wc -l`). Cod VFP citit: `COMUN\clase\ofacturare.vc2`, -`COMUN\programe\ofacturare_comun.prg` (ambele permise). Neatinse: `COMUN\clase\ofacturare_comun.vc2`, -`COMUN\programe\ofacturare_editare.prg` (perimetrul sesiunii #6). - ---- - -## 0. Premisele deja decise (nu se redeschid) - -- Povestea: a doua sursa de articole in meniu, "Alege din nomenclator...", disponibila si la - modificarea oricarui document deja emis, inclusiv auto (tip = -12, dar livrat separat, dupa). -- Decizia 20: din nomenclator se ofera si gestionabile, si negestionabile — nu se copiaza filtrul - `in_stoc = 0` al lui ROAAUTO. -- Decizia 21: contul de gestiune are fallback, nu NULL, nu refuz — mecanism separat de cel de aici - (priveste `id_gestiune`/`Cont` de gestiune al liniei, nu contul de venit), presupus deja rezolvat - cand derivarea din sectiunea 3 porneste. -- Decizia 27 (transportul inlocuit de decizia 34): contul de venit se **deriva** in VFP — - `CORESP_CONT_VENCHELT` pentru gestionabile (pe contul de gestiune al liniei), `NOM_ARTICOLE.CONT` - daca e 6xx/7xx pentru negestionabile, altfel `704`. -- Decizia 34: transportul e parametru nou, direct — nu prin `id_pol`/politica tehnica. Ocolul prin - `pack_preturi.adauga_politica_pret_art` e abandonat. -- Decizia 35: acelasi drum serveste si editarea prin regenerare — nu se proiecteaza a doua ruta de - contare. -- Decizia 36 (noua, 10.08.2026): `SCD` pe ramura fara politica nu e hardcodat `'4111'` literal — - vine dintr-o optiune de firma (`getoptiunefirma`), cu `4111` ca implicit dublu (rand in `OPTIUNI` - + fallback hardcodat in PL/SQL), tiparul deja folosit de `RF_CONT_INCASARE_*` in acelasi pachet. -- "Alte servicii" ROAAUTO ocoleste complet `contabilizeaza_articol` — exceptie, nu jumatate de - mecanism. -- Regula deja adoptata in #13: liniile libere nu intra deloc in `crsarticole` — `APPEND BLANK` + - `combosql` direct in `crsfactura`, ca `id_c` sa ramana `0` si `do_sterge` sa fie no-op prin - constructie (verificat aici ca acelasi tipar se aplica si campului `id_pol`, sectiunea 4). -- Ordine: intai ROAFACTURARE, apoi tip = -12. - ---- - -## 1. Punctul central: parametru vs politica, cine castiga - -### 1.1 Ce face azi codul, verificat direct pe sursa (nu preluat din rapoarte) - -`contabilizeaza_articol` (`ff_...:7173-7547`) primeste un singur parametru, -`detalii_articol VANZARI_DETALII_TEMP%ROWTYPE`. Primul lucru pe care il face (`:7275-7302`): - -```sql -BEGIN - SELECT COMPUS, ID_POL_ART - INTO V_COMPUS, V_ID_POL_ART - FROM VCRM_POLITICI_PRET_ART - WHERE ID_ARTICOL = detalii_articol.id_articol - AND ID_POL = detalii_articol.id_pol; -EXCEPTION - WHEN NO_DATA_FOUND THEN - ... - RAISE_APPLICATION_ERROR(-20000, 'Articolul ... nu este definit in politica de preturi ... (FACT-024)'); -END; -``` - -Daca `detalii_articol.id_pol` e `NULL`, comparatia `ID_POL = NULL` nu se potriveste niciodata — -`NO_DATA_FOUND` garantat, FACT-024 garantat. Acelasi rezultat daca `id_pol` e populat dar articolul -nu e in acea politica. **`cursor_articol`** (`:7218-7271`), care face tot lucrul real (`scrie_nota`, -`descarca_gestiune`, discount), e filtrat **pe exact aceeasi cheie** (`A.ID_POL = detalii_articol.id_pol -AND A.ID_ARTICOL = detalii_articol.id_articol`, `:7270-7271`) — daca `SELECT INTO` de mai sus a picat -in `NO_DATA_FOUND`, cursorul ar gasi tot zero randuri (aceeasi cauza). Deci punctul de intrare in -politica e unic si dublu-verificat (o data prin exceptie explicita, o data structural prin cursor). - -Design-ul deja facut (`canal_cont_venit_fara_politica.md:132-138`) propune sa infasoare **toata -functia** intr-un switch la nivel de metoda: - -``` -IF detalii_articol.cont_venit IS NOT NULL THEN - -ELSE - -END IF; -``` - -**Aceasta e deja, prin constructie, varianta "parametrul castiga necondiionat cand e populat"** — -cand `cont_venit` nu e `NULL`, executia nu se mai uita deloc la `id_pol`/politica, indiferent daca -acestea ar fi rezolvat sau nu. Team-lead-ul cere explicit sa se decida asta ca punct de proiectare, -nu sa ramana o consecinta implicita a formei codului — mai jos analiza celor 3 variante realiste. - -### 1.2 Cele 3 variante - -**A. Parametrul castiga mereu cand e populat** (= design-ul existent, ca atare). -- *Ce se strica*: daca, dintr-un motiv oarecare (bug VFP, sau — mai realist — decizia 35: la - **regenerare**, daca logica de derivare a lui `cont_venit` ar rula necondiionat pe toate liniile - documentului, inclusiv cele scrise initial prin "Cauta in lista de preturi..." cu `id_pol` real), - o linie ajunge la Oracle cu **ambele** populate, `SCC`-ul politicii reale e inlocuit tacut cu - contul derivat generic, `SCD` cu optiunea de firma (posibil diferita de `NOTE_CONTABILE.SCD` al - notei reale), `ASCD`/`ASCC`/`EXPLICATIE`/`CU_TVA` cu variantele generice ale ramurii noi. **Fara - nicio eroare** — factura se emite, dar cu alta contare decat cea configurata prin politica. Cel - mai periculos tip de defect: tace si trece verificarea vizuala (suma corecta, cont plauzibil). - -**B. Politica castiga mereu cand `id_pol` e populat** (indiferent daca rezolva sau nu). -- Ar cere schimbarea conditiei de switch din `cont_venit IS NOT NULL` in ceva de forma - `id_pol IS NULL AND cont_venit IS NOT NULL` — adica: daca exista `id_pol` pe linie, mergi pe - ramura veche necondiionat, chiar daca acel `id_pol` nu rezolva (caz FACT-024 de azi). -- *Ce se strica*: exact scenariul in care mecanismul nou ar fi cel mai util — o linie cu `id_pol` - populat gresit/stale (nu neaparat imposibil, doar improbabil in fluxul normal, vezi 1.3) — - ar continua sa primeasca FACT-024 desi VFP a trimis explicit un cont de rezerva. Varianta cea mai - fragila: defineste "castigatorul" pe **prezenta** unui camp, nu pe **rezolvarea** lui. - -**C. Parametrul e folosit doar cand politica nu rezolva** (fallback real, nu switch pe camp). -- Cere ca `SELECT INTO ... FROM VCRM_POLITICI_PRET_ART` (1.1) sa ramana **necondiionat**, exact ca - azi (deja rulat pe fiecare linie, cost zero suplimentar), dar `EXCEPTION WHEN NO_DATA_FOUND` sa - verifice `cont_venit`: daca e populat, ramura noua; daca nu, `RAISE FACT-024` ca azi. Semantic - corect — politica, cand exista si rezolva, nu e niciodata inlocuita tacut; parametrul e strict ce - a fost gandit sa fie: o plasa pentru cazul in care politica lipseste. -- *Cost real*: restructurare interna a functiei, nu doar un `IF` la inceput. Codul de dupa blocul - `EXCEPTION` verifica azi `IF V_COMPUS = 1 THEN ... ELSE END IF` - (`:7305,7391`) — `V_COMPUS` ramane `NULL` daca s-a intrat pe `EXCEPTION`, deci "cade" oricum pe - ramura `ELSE` care deschide `cursor_articol` (gaseste zero randuri, nu declanseaza nimic, dar nici - ramura noua). Trebuie introdus un flag explicit (`V_ARE_POLITICA`/similar) propagat din interiorul - `EXCEPTION` pana la punctul de decizie, marind suprafata modificata fata de varianta A (care - atinge doar granita functiei, cu restul intact). - -### 1.3 Ar putea sa apara vreodata, in fluxul normal, ambele populate? - -Verificat direct in aceasta runda (nu presupus): `crsfactura` (cursorul VFP din care se scrie orice -linie) are `id_pol N(20) Null` (`COMUN\programe\ofacturare_comun.prg:1775`) — camp nullable, deci la -`APPEND BLANK` (mecanismul liniilor libere, confirmat de S4e ca acelasi gest se foloseste si pentru -"Alege din nomenclator...") ramane `.NULL.` implicit, nu `0`. La scriere, `poArt.id_pol` (scatter -din randul respectiv) ajunge in apelul RPC ca `Nvl(Alltrim(Str(poArt.id_pol)),[NULL])` -(`ofacturare.vc2:14072`, identic la `:18107`) — literal SQL `NULL` cand campul e `.NULL.`. **O linie -"din nomenclator" trimite deci `id_pol = NULL` la Oracle prin constructie, nu doar prin conventie de -utilizare** — cursorul de cautare al nomenclatorului (`caut_articol`, `ocautare.prg`, deja confirmat -de `coresp_cont_venchelt.md` sectiunea 9d) nu are coloana `id_pol` in output, deci nu exista niciun -punct in care combosql-ul ar putea popula acest camp cu o valoare reala pentru o astfel de linie. - -Ramane un singur scenariu real de ambiguitate: **regenerarea** (decizia 35). La regenerare, toate -liniile documentului (cele scrise initial prin "Cauta in lista de preturi...", cu `id_pol` real, **si** -cele adaugate prin "Alege din nomenclator...", fara `id_pol`) trec din nou prin acelasi drum de -scriere. Daca logica VFP care calculeaza `cont_venit` (sectiunea 3) ar rula **necondiionat** pe toate -liniile din grid, in loc sa fie conditionata explicit de "linia nu are `id_pol`", ar produce exact -combinatia periculoasa din varianta A. **Asta nu e un risc Oracle — e un risc de implementare VFP**, -dar exact tipul de risc pe care design-ul Oracle trebuie sa nu-l agraveze printr-un switch care il -face invizibil. - -### 1.4 Recomandare - -**Niciuna dintre cele 3 variante pure, ci varianta A (deja proiectata, cea mai simpla) plus o garda -explicita pe combinatia ambigua**, nu o rezolvare tacita in orice directie: - -```sql -IF detalii_articol.cont_venit IS NOT NULL THEN - IF detalii_articol.id_pol IS NOT NULL THEN - RAISE_APPLICATION_ERROR(-20000, - 'Articolul ' || detalii_articol.id_articol || - ' are simultan politica de pret (' || detalii_articol.id_pol || - ') si cont de venit calculat — conflict netratat (FACT-0xx)'); - END IF; - -ELSE - -END IF; -``` - -Motivare, in ordine: -1. **Combinatia nu trebuie sa apara niciodata in fluxul normal** (1.3) — `id_pol` ramane `NULL` prin - constructie pentru orice linie scrisa prin gestul "linie libera". Daca totusi apare, e un semnal - ca ceva e stricat in populate-ul liniei (VFP a trimis ambele campuri, posibil din cauza logicii - de regenerare descrisa la 1.3) — situatie de bug de investigat, nu de "rezolvat" silentios. -2. **Variantele B si C rezolva combinatia in cate o directie fixa** — ambele ascund o eroare de date - in loc sa o semnaleze: B ar putea reintroduce FACT-024 pe o linie unde VFP a oferit deja o - solutie; A fara garda ar inlocui tacut o politica reala. O garda explicita e singurul comportament - care nu presupune ca stie mai bine decat datele ce s-a intamplat. -3. **Cost de implementare minim**: un singur `IF` suplimentar, la intrarea in ramura deja proiectata - — nu restructureaza funcia (spre deosebire de varianta C), nu schimba conditia switch-ului - principal (spre deosebire de B). -4. **Compatibilitate/regresie zero pe apelantii de azi**: cand `cont_venit` e `NULL` (toti apelantii - existenti, care nu cunosc inca acest parametru), executia intra direct pe `ELSE` — garda nu se - evalueaza niciodata, comportamentul e identic bit-cu-bit cu azi. Cand `cont_venit` e populat si - `id_pol` e `NULL` (cazul intentionat, "din nomenclator"), garda trece nevazuta, ramura noua - ruleaza normal. - -**Numele codului de eroare** (`FACT-0xx` in schita de mai sus) ramane de ales de Marius, distinct de -`FACT-024` (care ramane, neschimbat, pentru cazul "niciuna din cele doua" — linie fara politica si -fara cont). - ---- - -## 2. Suprafata de schimbare pe Oracle - -Deja proiectata si verificata adversarial in `canal_cont_venit_fara_politica.md` si -`parametru_cont_contabilizeaza_articol.md`; rezumat + completarea cu decizia 36 si garda de la -sectiunea 1.4: - -1. **Coloana noua**: `VANZARI_DETALII_TEMP.CONT_VENIT VARCHAR2(4) NULL` (fara `NOT NULL`, fara - `CHECK` — acelasi tipar ca `ACT_TEMP.SCC`). Optional, recomandat pentru trasabilitate: - `VANZARI_DETALII.CONT_VENIT`, plus extinderea listei explicite de coloane din - `scrie_in_vanzari` (`PACK_FACTURARE:13705-13757`, confirmat ca listeaza 24 coloane explicit, nu - `SELECT *` — de extins cu o a 25-a daca se alege trasabilitatea). -2. **Parametru nou pe `adauga_articol_factura`** (`ff_...:4989-5015`), la coada, dupa `V_LOT`: - `V_CONT_VENIT IN VARCHAR2 DEFAULT NULL` — apelul VFP existent (pozitional, se opreste la - `V_LOT`, confirmat la doua locuri, `ofacturare.vc2:14069-14091` si `:18089-18114`, doua clase - distincte cu aceeasi metoda, nu o duplicare) ramane neschimbat, echivalent cu "trimite NULL". - Plumbing identic cu `V_CONT`/`V_CONT2`, o coloana in plus in `INSERT INTO VANZARI_DETALII_TEMP` - (`ff_...:5222-5282`). -3. **`contabilizeaza_articol` insasi NU primeste parametru nou** — ramane `VANZARI_DETALII_TEMP%ROWTYPE`; - coloana noua ajunge automat prin `SELECT * BULK COLLECT INTO tab_detalii FROM VANZARI_DETALII_TEMP` - (`:6063`), fara nicio schimbare la cei 3 apelanti interni (`scrie_factura2`, - `scrie_factura_avize_retur`, `scrie_aviz_retur`). -4. **Ramura noua in `contabilizeaza_articol`**, cu garda de la sectiunea 1.4: - - `SCC := detalii_articol.cont_venit`. - - `SCD` (decizia 36): `PACK_SESIUNE.getoptiunefirma('FACT_SCD_ARTFPRET')`, cu fallback hardcodat - `'4111'` cand optiunea lipseste/e goala — optiune noua in tabelul `OPTIUNI` - (`VARTYPE='CHARACTER'`, script de migrare idempotent, tiparul exact al `RF_CONT_INCASARE_*`, - deja folosit in acelasi pachet, `optiune_firma_cont_debit.md` sectiunea 3.4). Ramurile de aviz - raman `'418'`/`'461'`, neschimbate. - - `ASCD`/`ASCC` din `GetAnaliticByGrupUtilizatori(pack_facturare.nid_util, V_SCD/V_SCC)` — acelasi - fallback deja folosit necondiionat pe ramurile de aviz azi. - - `EXPLICATIE` din `detalii_articol.explicatia` (parametru deja existent, `V_EXPLICATIE`). - - `ID_VENCHELT`/`ID_SECTIE` din `pack_facturare.nid_venchelt`/`nid_sectie_stoc` (fallback de - sesiune deja folosit ca prioritate azi). - - `IN_VALUTA` din `pack_facturare.nin_valuta` (parametru obligatoriu, fara `DEFAULT`, mereu - curent — nu o presupunere, sursa solida). - - `CU_TVA`: hardcodat `1`, **cu efect colateral real si masurat, nu "inofensiv"** — - `parametru_cont_contabilizeaza_articol.md` sectiunea 2b arata ca pe combinatia specifica "linie - fallback cu TVA 0% + discount global pe factura + acea linie e prima/singura vazuta" valoarea - forteaza `nproc_tva_max`/`nid_jtva_coloana` pentru toata linia de discount a facturii. Ramane - "de confirmat cu Marius", cu argumentul mai tare decat in propunerea initiala. - - Apeluri o singura data (nu in bucla): `scrie_nota(...)`, `descarca_gestiune(...)` (garda - identica, `nscadere_stoc=1 AND id_gestiune<>-1000 AND in_stoc=1`), discount (garda identica). - - Articole compuse (`V_COMPUS=1`) exclus structural — o linie fara `id_pol` nu poate avea - `ID_POL_ART`, deci intrebarea "e compus" nu se poate pune pe aceasta ramura (confirmat pe - schema view-ului, nu presupus). -5. **Ordinea de deploy**: DB inainte de EXE. Parametrul nou are `DEFAULT NULL`, deci EXE-ul vechi - ruland pe DB-ul nou functioneaza neschimbat (nu trimite parametrul). EXE-ul nou ruland pe DB-ul - vechi (fara coloana/parametru) ar esua la primul apel cu al 27-lea argument — deci EXE-ul nou - **nu** poate merge inaintea migrarii DB. Ordine standard, fara surpriza. -6. **Regenerarea (decizia 35)**: `initializeaza_date_factura` reseteaza starea de sesiune - (`DELETE FROM VANZARI_DETALII_TEMP`, `nid_act := 0`) la fiecare emitere — nimic din ramura noua - citeste vreo stare presupunand "documentul e nou". Calea Oracle e identica la regenerare, **cu o - exceptie separata, in alt pachet**: reemiterea cu acelasi `ID_FACT` ar da azi `ORA-00001` pe - `PK_DOCUMENTE` in `PACK_CONTAFIN.SET_IDFACT` — problema deja proiectata separat - (`idfact_refolosire_si_documente.md`), nu intersecteaza si nu invalideaza design-ul de aici, dar - ramane o bucata de lucru distincta, necesara pentru ca regenerarea sa fie completa. -7. **Gol neadresat inca**: ramura `ntip = 4` ("factura din avize", `:7520-7537`) apeleaza - `scrie_fact_aviz_custodie` in loc de `descarca_gestiune`+discount — design-ul nu specifica ce se - intampla pe fallback aici. In practica improbabil (o linie de aviz sursa fara politica n-ar fi - ajuns la factura pe acest drum, `adauga_articol_factura:5096` cere deja `A.ID_POL = V_ID_POL` pe - avizul sursa), dar de exclus explicit inainte de implementare, nu de presupus tacit. - ---- - -## 3. Derivarea contului in VFP - -### 3.1 Sursele, in ordine (decizia 27) - -1. **Articol gestionabil** (are `id_gestiune` valid, `in_stoc=1`): `CORESP_CONT_VENCHELT.CONT_VENIT`, - cautat pe **contul de gestiune al liniei** (`poArt.Cont`, camp deja gathered pe `poArticol` in - ambele metode `do_scrie_articole`, confirmat la `ofacturare.vc2:12945,13835` si echivalentele in - `frm_facturare_articole2`). Cheia de join e mereu `CONT` (nu `ID_GESTIUNE`, nu `TIP_DOC`) — - confirmat pe 4+ consumatori Oracle reali ai tabelului (`pack_vin`, `pack_devize`, - `gestiune_pack_gest_import`, un raport din 2026), niciodata pentru `CONT_VENIT` insa — aceasta - propunere ar fi **primul consumator real** al coloanei, desi populata din 2023 (20 conturi de - gestiune uzuale: 301-303/3021-3028, 331/332, 341, 345/346/348, 361, 371, 381). View-ul VFP-safe e - `VCORESP_CONT_VENCHELT` (`STERS=0` deja filtrat in view). -2. **Articol negestionabil**, sau gestionabil dar corespondenta nu rezolva (rand lipsa pentru acel - `CONT`): `NOM_ARTICOLE.CONT`, **doar daca** primul caracter e `6` sau `7` (`verific_cont` nu - restrange campul la o clasa, deci poate contine legitim orice cont valid, inclusiv 6xx/7xx pe un - articol negestionabil). -3. **Fallback final**: literal `'704'`. Fara reteta de cod deja scrisa in `pack_facturare` pentru - aceasta valoare, dar cu un precedent independent de "704 = cont implicit client" in alt subsistem - (`anaf_efactura.vc2:8376`, `cconte = 704`) — nu o inventie, dar cod nou. - -### 3.2 Unde sta codul si cand ruleaza - -Locul natural: chiar in `do_scrie_articole` (ambele clase, `frm_facturare_articole` si -`frm_facturare_articole2`, confirmat ca 2 locuri distincte de atins, nu 1), imediat inainte de -construirea textului RPC pentru fiecare linie, **conditionat explicit de `Empty(Nvl(poArt.id_pol,0))`** -— nu la momentul adaugarii liniei in grid. Doua motive: -- **Regenerarea (decizia 35)** re-parcurge toate liniile la fiecare scriere; derivarea la momentul - scrierii (nu la adaugare) garanteaza ca valoarea reflecta starea curenta a corespondentelor/ - nomenclatorului, nu una inghetata la momentul in care linia a fost adaugata initial in grid. -- **Conditionarea explicita pe `id_pol` gol** (nu pe "linia vine din nomenclator" ca marcaj separat) - e chiar garda ceruta la sectiunea 1.3-1.4: o linie cu `id_pol` populat nu trebuie sa primeasca - niciodata o valoare pe `cont_venit`, indiferent de sursa ei — un singur punct de decizie, in - oglinda exacta cu conditia pe care Oracle o va verifica la randul lui (sectiunea 1.4). - -### 3.3 Ce se intampla cand derivarea esueaza - -- **Corespondenta nu are rand pentru acel `CONT`** (gestiune fara corespondenta configurata): cade pe - pasul 2 (`NOM_ARTICOLE.CONT`), nu pe eroare. -- **`NOM_ARTICOLE.CONT` nu incepe cu 6/7** (cont de gestiune sau alt tip pe un articol negestionabil, - configurare atipica): cade pe pasul 3 (`704`), nu pe eroare. -- **Niciodata `NULL`/gol trimis la Oracle pentru o linie "din nomenclator"** — ultimul pas e un - literal, nu o interogare care poate esua. Aceasta e proprietatea care face garda de la 1.4 - inofensiva pe fluxul normal: `cont_venit` e *intotdeauna* populat cand `id_pol` e gol, deci - ramura noua din Oracle ruleaza mereu cand ar trebui, iar FACT-024 (ramura veche) nu mai poate fi - atinsa de o linie din nomenclator dupa implementare — dispare exact problema pe care povestea o - rezolva. -- **Nu e proiectata aici** (ramane de decis la implementare, in afara acestei povesti): daca vreo - validare suplimentara ar trebui sa verifice ca valorile derivate (`CONT_VENIT` din corespondenta, - `NOM_ARTICOLE.CONT`) sunt conturi valide in planul de conturi al anului curent — azi - `verific_cont` exista ca mecanism (`oproceduri_comune.prg:2389-2415`) dar nu e cablat automat pe - acest drum nou. - ---- - -## 4. Fluxul in formularul unificat - -1. Utilizatorul deschide un document deja emis la modificare (`frm_modific2024`/formularul unificat, - in afara perimetrului #6/omodificari.vc2, care nu se atinge aici) si alege din meniu "Alege din - nomenclator..." (a doua sursa, langa "Cauta in lista de preturi...", ambele reduse la acelasi - gest UI conform S4b/S4e). -2. Gestul UI: `Select crsfactura / APPEND BLANK` (tiparul deja in productie la - `frm_facturare_articole2.But_nou1.do_adauga`, `ofacturare.vc2:17118-17122`), focus pe celula de - cautare, `combosql` legat pe cursorul filtrat pe nomenclator (`caut_articol`/echivalent, fara - coloana `id_pol` in output — confirmat, sectiunea 1.3). -3. **`crsfactura.id_c` ramane `0`** (valoarea implicita a campului `N(20)` fara `Null`, la - `APPEND BLANK`) — valoare pe care Oracle nu o produce niciodata pentru `id_c`. `do_sterge` - (potriveste pe `id_c`) e no-op prin constructie pe aceasta linie, fara nicio modificare de cod — - exact regula deja adoptata in #13, reconfirmata aici pentru sursa "nomenclator" (S4e o stabilise - pentru sursa "lista de preturi pe comanda"; acelasi cursor de scriere `crsfactura`, acelasi - mecanism, doar alt cursor de cautare in fata). -4. **`crsfactura.id_pol` ramane `.NULL.`** (camp `N(20) Null`, `ofacturare_comun.prg:1775`), pentru - ca sursa de cautare (nomenclator) nu are aceasta coloana in output — confirmat direct in aceasta - runda (sectiunea 1.3), nu presupus. -5. Utilizatorul completeaza cantitate/pret (campuri editabile inline pe grid, tipar deja existent). -6. La `do_scrie_articole` (salvarea documentului), pentru fiecare linie cu `Empty(Nvl(poArt.id_pol,0))`, - se ruleaza derivarea din sectiunea 3 si se populeaza al 27-lea argument pozitional al apelului RPC - catre `adauga_articol_factura` cu valoarea calculata; pentru restul liniilor (cele cu `id_pol` - populat, "din lista de preturi"), argumentul ramane `NULL` — comportament identic cu azi. -7. Documentul se salveaza; pe Oracle, `contabilizeaza_articol` ruleaza ramura noua (sectiunea 1-2) - pentru liniile fara politica, ramura veche neschimbata pentru restul. -8. **Editarea** (regenerare, decizia 35): acelasi `do_scrie_articole`, aceeasi conditie pe `id_pol`, - acelasi rezultat — nu exista o a doua cale de contare pentru liniile deja existente pe document. - ---- - -## 5. Ce ramane in afara (partea auto) - -- `tip = -12` (facturare auto, ROAAUTO) — livrat separat, dupa ce mecanismul de mai sus e stabil pe - documentele ROAFACTURARE (ordinea deja decisa in plan). -- "Alte servicii" din ROAAUTO ramane exceptia ei — ocoleste complet `contabilizeaza_articol`, nu - foloseste si nu va fi migrata sa foloseasca acest mecanism ca parte a acestei povesti; daca se - decide vreodata unificarea, e o poveste separata. -- Gridul read-only din `frm_modific2024` (al #6) — S4g incepe dupa ce #6 se termina (decizia 30), - nicio proiectare de aici nu presupune sau modifica acel perimetru. -- Validarea de cantitate/stoc pentru o linie liberă la modificare (analogul sectiunii 6 din - `s4e_lista_preturi_pe_sursa.md`, dar pentru nomenclator, nu lista de preturi) — nu re-proiectata - aici, acelasi gol semnalat deja de S4e se aplica identic si pe sursa "nomenclator"; de rezolvat cu - acelasi mecanism (varianta (b), reutilizare `do_verifica_articol` cu `poArticol` explicit). - ---- - -## 6. Teste minime - -Pe langa cele deja listate in `canal_cont_venit_fara_politica.md` sectiunea 6 si -`nota_contabila_fara_politica.md`, specifice acestei povesti: - -1. **Factura normala cu politica, parametru NULL** — verifica ACT identic cu azi (regresie de baza). -2. **Aviz cu articol adaugat din nomenclator (fara politica)** — `SCD` ramane `'461'`/`'418'` - (ramurile de aviz raman hardcodate independent de ramura noua/veche), nu `getoptiunefirma`. -3. **`ntip = 46`** (daca exista pe fluxul de modificare vizat) — `scrie_nota` nu se cheama pe acea - ramura; de confirmat ca ramura noua nu e atinsa deloc pentru acest tip. -4. **Articol gestionabil din nomenclator, cu corespondenta configurata** — un singur rand `ACT`, - `SCC` = `CORESP_CONT_VENCHELT.CONT_VENIT` pentru contul de gestiune al liniei, `SCD` = optiunea - de firma (sau `4111` daca optiunea lipseste), linie TVA scrisa separat. -5. **Articol negestionabil din nomenclator, fara corespondenta aplicabila** — `SCC` = `NOM_ARTICOLE.CONT` - daca incepe cu 6/7, altfel `'704'`. -6. **Articol negestionabil** (`id_gestiune=-1000`) cu `cont_venit` populat — `descarca_gestiune` NU - ruleaza. -7. **Discount pe o linie din nomenclator** — foloseste `V_ASCD`/`V_CU_TVA` calculate in ramura noua. -8. **Document in valuta** (`nin_valuta=1`) cu linie din nomenclator — `IN_VALUTA=1` pe nota, - `SUMA_VAL` completat. -9. **Linie fara politica si fara cont derivat** (nu ar trebui sa se poata construi prin UI, dar de - testat direct pe Oracle) — FACT-024 tot apare, regresie negativa: garda nu s-a slabit. -10. **Linie cu ambele populate** (construita direct la nivel de apel Oracle, nu prin UI — testeaza - garda de la sectiunea 1.4, nu fluxul normal) — noua eroare explicita apare, nu o rezolvare - tacita in nicio directie. -11. **Regenerare pe un document mixt** (o linie din lista de preturi cu `id_pol`, o linie din - nomenclator fara `id_pol`) — dupa regenerare, prima linie tot cu `SCC` din politica, a doua tot - cu `SCC` derivat; niciuna nu trece pe ramura celeilalte. -12. **`id_c` si `do_sterge`** (mostenit din tiparul S4e, de re-verificat pentru sursa nomenclator): - o linie din nomenclator adaugata la modificare, apoi stearsa inainte de salvare — no-op pe - `crsarticole`, fara efect asupra vreunei linii reale a documentului. - ---- - -## 7. Riscuri si de decis de Marius - -1. **Garda explicita pe combinatia `id_pol` + `cont_venit` ambele populate** (sectiunea 1.4) — e o - recomandare de proiectare a acestui raport, nu o decizie deja luata de Marius; codul exact al - erorii si numarul `FACT-0xx` raman de ales. -2. **`CU_TVA` hardcodat `1`** — are un efect colateral masurat (nu doar teoretic), prin - `nproc_tva_max`, pe combinatia specifica linie-scutita + discount global de factura. De confirmat - explicit, nu de presupus inofensiv. -3. **Decizia 36 (`SCD` prin optiune de firma)** — numele exact al cheii (`FACT_SCD_ARTFPRET` propus), - daca se adauga validare de cont la citire (niciun precedent existent nu valideaza), si - `PROGRAME` (restrans la ROAFACTURARE sau extins ca `RF_CONT_INCASARE_*`) raman decizii deschise - in `optiune_firma_cont_debit.md` sectiunea 4. -4. **Ramura `ntip=4`/"factura din avize" pe fallback** (sectiunea 2 punctul 7) — probabil imposibil - de atins prin acest drum (avizul sursa cere deja `id_pol`), dar nu exclus explicit prin cod sau - test — de confirmat inainte de implementare. -5. **Suprafata de regresie in restul suitei** (ROAGEST, ROAAUTO, ROAACNPRO, ROAIMOB) pentru - `adauga_articol_factura` — pe baza cercetarilor existente, aceste produse folosesc proceduri - separate (`_deviz`/`_stoc`) sau alt pachet complet (`pack_acn`), deci risc asteptat zero, dar - cautarea directa in `COMUN`-urile lor pentru un apel simplu la `adauga_articol_factura(` nu s-a - terminat in nicio runda anterioara — de re-rulat inainte de implementare, nu de presupus incheiata. -6. **Validarea de cantitate/stoc pentru linia libera la modificare** (sectiunea 5) — gol mostenit - de la S4e, nu inchis aici, de rezolvat cu acelasi mecanism pe ambele surse (lista de preturi si - nomenclator). -7. **Trasabilitatea `CONT_VENIT` pe `VANZARI_DETALII`** (sectiunea 2 punctul 1) — optionala pentru - ca mecanismul sa functioneze, dar fara ea coloana nu se pastreaza dupa fapt; de decis daca merita - extinderea listei explicite de coloane din `scrie_in_vanzari`. -8. **`idfact_refolosire_si_documente.md`** — obstacol real pentru ca regenerarea (decizia 35) sa fie - completa (reemitere cu acelasi `ID_FACT`), in `PACK_CONTAFIN`, nu in `pack_facturare` — nu - blocheaza design-ul de aici, dar e o bucata de lucru separata, necesara inainte ca "editare = - regenerare" sa functioneze end-to-end. - ---- - -## STARE / CE RAMANE - -Cercetare/proiectare incheiata pentru toate cele 6 puncte cerute in brief, cu punctul central -(sectiunea 1) tratat explicit ca decizie de proiectare (nu doar consecinta implicita a codului -existent deja schitat in rundele anterioare). Nicio editare de cod, niciun `git_sync.ps1`/ -`txt2vcx.ps1`, niciun commit, nicio scriere pe Oracle. Context consumat moderat in aceasta runda — -nu a fost necesara predarea de mijloc de sesiune. - -**Ce nu s-a putut inchide complet, de reluat separat**: -- Cautarea exhaustiva a apelantilor `adauga_articol_factura` simplu in restul suitei (risc 5, - sectiunea 7) — de re-rulat, nu s-a terminat in nicio runda anterioara din lipsa de timp, nu din - cauza unei erori. -- Confirmarea explicita a lui Marius pe hardcodarile/optiunile ramase deschise (`CU_TVA=1`, numele - cheii de optiune, garda de la sectiunea 1.4) — sunt recomandari argumentate, nu decizii finale. diff --git a/docs/cercetare/s5_acoperire_tipuri.md b/docs/cercetare/s5_acoperire_tipuri.md deleted file mode 100644 index d64afd1..0000000 --- a/docs/cercetare/s5_acoperire_tipuri.md +++ /dev/null @@ -1,502 +0,0 @@ -# Cercetare + proiectare — S5: acoperirea tuturor tipurilor de document - -Proiectare pe cod, READ-ONLY (fara editari, fara `git_sync.ps1`/`txt2vcx.ps1`, fara commit, fara -scriere pe Oracle — numai `SELECT`), pentru povestea **S5** din -`docs\plan_13_unificare_formular_facturare.md` (`#### S5`). Nu s-a atins -`COMUN\clase\ofacturare_comun.vc2` si `COMUN\programe\ofacturare_editare.prg` (perimetrul altei -sarcini in lucru) — doar citite cand au aparut in cautari (n-a fost cazul). - -Sursa VFP: `COMUN\clase\ofacturare.vc2` (`frm_facturare_articole` = formularul de productie, -`:10968-15739`; `frm_facturare_articole2` = prototipul, `:15741-19355` — **nu e subclasa** a -primului, mostenesc separat din `_frmbase`, au `Init` propriu fiecare). Sursa de rutare: -`COMUN\programe\ofacturare.prg` (`factureaza` = standard, `:81-...`; `factureaza2` = prototip, -`:660-...`). Referinta de tipuri: `COMUN\docs\tipuri_documente_facturare.md`. - -## Verdict (rezumat, citeste asta primul) - -1. **`Do Case`-ul din `frm_facturare_articole.Init` (`ofacturare.vc2:15109-15248`) acopera 21 de - valori de `tip`** (grupate in 14 ramuri): `1,2,3,4,5,6,7,8,9,10,21,22,23,24,25,26,28,29,41,42,47`. -2. **Patru tipuri sunt reale, reachable prin `factureaza()`, dar nu apar in niciun `Case`** — - pierd titlu, cap de coloana, mesaj de stoc, vizibilitate discount, eliminare `cSerie`: **`45` - (factura restaurant), `48`/`49` (custodie cu/fara descarcare K), `52` (contract, factura fiscala - valuta)**. Confirmat pe meniuri (`Meniuri\politica.mn2:18,26,29`, `Meniuri\contracte.mn2:18`) si - pe rutarea cursorului (`ofacturare.prg:271-282`), care **le recunoaste** — doar Init-ul - formularului de articole nu le-a "prins" niciodata. **Nu doar `52`**, cum semnalase raportul S4b — - sunt patru, nu unul. -3. **Cinci tipuri din referinta (`43,44,46,50,51`) nu sunt niciodata pasate lui `factureaza()` - in tot arborele `D:\ROA`** (cautare exhaustiva, zero potriviri) — nu ajung la acest formular deloc - azi. `50` e marcat explicit "in lucru" in sursa; `51` (ROAACNPRO) foloseste `id_set=50100`, un - interval separat de restul (`25000+`), semn ca provine dintr-un flux Oracle direct al altui produs, - nu din `factureaza()` local. -4. **Descoperire centrala, dincolo de ce cerea misiunea**: prototipul (`frm_facturare_articole2.Init`, - `:18988-19080`) **nu e o versiune partiala a Do Case-ului standard — e aproape gol**. Singurul - lucru pe care-l face pe tip e sa aleaga cuvantul `lcTipDoc` ("factura" vs "aviz"), pe o lista - **mai scurta** (lipseste `24`). Nu seteaza titlu (nu exista `lb_titlu_alb_b121` in tot fisierul - prototipului), nu schimba capul coloanei de cantitate, nu schimba mesajul de stoc, nu ascunde - discountul, **nu are deloc conceptul de coloana `cSerie`** (gridul prototipului, `grd_factura`, - n-are niciodata `RemoveObject('cSerie')` — cautare pe tot fisierul, zero potriviri in intervalul - `15741-19355`). Daca formularul unificat porneste de la prototip (cum indica decizia de baza a - planului), **toata diferentierea pe tip trebuie reconstruita de la zero**, nu doar completata. -5. **Rutarea cursorului diverge intre standard si prototip pe trei tipuri, nu doua**: `23` - (confirmat deja de S4/S4b), plus **`52` si `24`, gasite aici** — pe prototip, `Case Inlist(tnTip, - 2, 26, 6)` (`ofacturare.prg:762`) **omite `52`** fata de standard (`Inlist(tnTip, 2, 26, 6, 52)`, - `:283`), si `Case Inlist(tnTip, 8, 9)` (`:819`) **omite `24`** fata de standard (`Inlist(tnTip, 8, - 9, 24)`, `:307`). Daca cineva ar factura tip `52` sau `24` prin prototip azi (`gnFacturareNou=1`), - `lcSqlCursor` ar ramane nedefinit — eroare, nu doar diferenta de comportament. -6. **Tipurile `26` si `52` n-au niciun bookkeeping `crsarticole`** (nici Rol A, nici Rol B) — - inchis aici punctul lasat deschis de raportul S4 punctul 2: excluderea lor din toate cele patru - `Case`-uri de bookkeeping din `do_adauga_articol`/`do_sterge` e totala (Do Case exhaustiv, fara - ramura implicita), nu doar "neconfirmata". -7. **`27` si `30` raman pe calea lor** — confirmat pe cod, cu o nuanta importanta pentru `30`: nu - e un formular separat, ci **acelasi `frm_facturare_articole`, instantiat si trecut prin acelasi - `Init`/`Do Case`, dar niciodata aratat** (`ofacturare.prg:444-453`: calculeaza totalurile, apasa - programatic `but_termin1.Click()`, apoi `Release()`, fara `Show()`). Tip `30` **e afectat de - golurile din `Do Case`** exact ca oricare alt tip needitat — doar ca defectele (titlu, cap de - coloana) nu se vad niciodata pe ecran. - ---- - -## 0. Metoda de verificare — lista de referinta - -Lista completa de tipuri vine din `COMUN\docs\tipuri_documente_facturare.md` (sursa unica, deja -verificata pe cod de acea cercetare). Tipuri incluse in tabelul de mai jos: toate cele din sectiunile -"Facturi" si "Avize de expeditie" (documentele care intra prin `frm_facturare_articole`/`2`). -Sectiunea "Tipuri negative" (`-1..-13`) **nu intra in acest formular** — sunt scrise de alte produse -(ROAGEST, ROAAUTO) prin propriile lor fluxuri, niciodata prin `factureaza()` din ROAFACTURARE -(cautare exhaustiva `factureaza(-` in tot `D:\ROA`, zero potriviri) — declarate aici explicit **ramase -pe calea altui produs**, nu "neacoperite". - ---- - -## 1. Tabelul complet, tip cu tip - -Coloane: `tip` = `VANZARI.TIP` · **Case propriu** = are ramura proprie in `frm_facturare_articole.Init` -(`ofacturare.vc2:15109-15248`)? · **titlu** = ce seteaza pe `lb_titlu_alb_b121.Caption` · **cap -cantitate** = ce seteaza pe `grd_articole.cCantitate.header1.Caption` (implicit ramane cel din -design, `[Cantitate in stoc]`, daca nu e suprascris) · **mesaj stoc** = `This.cmesaj_cantitate` · -**discount** = `clb_discount.Visible` · **`cSerie`** = coloana ramane (`Da`) sau se scoate (`Nu`) · -**butoane** = ce se face vizibil (`but_urmator_tot1`/`but_retur`, ambele `.F.` la design) · **cursor -standard** = ramura din `factureaza` (`ofacturare.prg:266-308`) · **cursor prototip** = ramura din -`factureaza2` (`:748-822`, gol daca lipseste) · **Rol crsarticole** = A (cantitate ramasa de -facturat) / B (plafon de sesiune) / — (fara bookkeeping), din `docs\cercetare\s4_punct2_registru_cantitate_ramasa.md` -· **stare S5** = acoperit azi / gol de completat / ramas pe calea veche / neatins. - -### Facturi - -| tip | denumire | Case propriu | titlu | cap cantitate | mesaj stoc | discount | `cSerie` | butoane vizibile | cursor standard | cursor prototip | Rol | stare S5 | -|---|---|---|---|---|---|---|---|---|---|---|---|---| -| 1 | lista de preturi (lei) | **Da** `:15122` (grup 1,5,7,10) | — (implicit) | "Cantitate in stoc" | "nu e pe stoc!" | vizibil (implicit) | **Nu** (scoasa) | `but_retur` | `cursor_preturi` (grup 1,22,5,29,7,10,23), `:279-282` | `cursor_preturi` (grup 1,22,5,29,7,10), `:758-761` | B (gestionabil, `1,22,29`) | acoperit azi, de portat | -| 2 | contract (lei) | **Da** `:15129` (grup 2,6) | "FACTURA PE CTR. …" | "Cantitate in stoc" | "nu e pe stoc!" | vizibil | **Nu** | — | `cursor_contract` (grup 2,26,6,52), `:283-291` | `cursor_contract` (grup 2,26,6 — **fara 52**), `:762-769` | B doar pt. `opt_facturare=0`; — pe rest | acoperit azi, de portat | -| 3 | comanda | **Da** `:15144` | "FACTURA LA COMANDA …" | "Cantitate comandata" | "cantitate comandata facturata" | vizibil | **Nu** | `but_urmator_tot1` | `cursor_comanda` (grup 3,21,25,28,42,47), `:292-293` | `cursor_comanda` (acelasi grup), `:771-773` | **A** | acoperit azi, de portat | -| 4 | din avize | **Da** `:15151` | "FACTURA DIN AVIZE" | — (implicit) | "cantitate de pe aviz facturata" | **ascuns** (`.F.`, `:15154`) | Da (nu se scoate) | `but_urmator_tot1` | `cursor_avize`, `:294-295` | `cursor_avize`, `:774-775` | **A** | acoperit azi, de portat | -| 5 | lista de preturi valuta | **Da** `:15122` (grup 1,5,7,10) | — | "Cantitate in stoc" | "nu e pe stoc!" | vizibil | **Nu** | `but_retur` | `cursor_preturi` | `cursor_preturi` | — | acoperit azi, de portat | -| 6 | contract valuta | **Da** `:15129` (grup 2,6) | "FACTURA PE CTR. …" | "Cantitate in stoc" | "nu e pe stoc!" | vizibil | **Nu** | — | `cursor_contract` | `cursor_contract` | B partial (ca 2) | acoperit azi, de portat | -| 7 | credit note | **Da** `:15122` (grup 1,5,7,10) | — | "Cantitate in stoc" | "nu e pe stoc!" | vizibil | **Nu** | `but_retur` | `cursor_preturi` | `cursor_preturi` | — | acoperit azi, de portat | -| 8 | retur factura lei | **Da** `:15236` (grup 8,9) | — | "Cant. max. de returnat" | "nu se mai poate returna" | vizibil | Da (nu se scoate) | `but_urmator_tot1` | `cursor_retur`, `:306-307` | `cursor_retur` (grup 8,9), `:819-820` | B (invers) | acoperit azi, de portat | -| 9 | retur factura valuta | **Da** `:15236` (grup 8,9) | — | "Cant. max. de returnat" | "nu se mai poate returna" | vizibil | Da | `but_urmator_tot1` | `cursor_retur` | `cursor_retur` | B (invers) | acoperit azi, de portat | -| 10 | factura fiscala valuta | **Da** `:15122` (grup 1,5,7,10) | — | "Cantitate in stoc" | "nu e pe stoc!" | vizibil | **Nu** | `but_retur` | `cursor_preturi` | `cursor_preturi` | — | acoperit azi, de portat | -| **43** | bon fiscal magazine (ROARETAIL) | Nu | — | — | — | — | — | — | *nicio ramura* | *nicio ramura* | n/a | **neatins** — niciodata pasat lui `factureaza()` (cautat in tot `D:\ROA`); colectat de la magazine prin alt flux | -| **44** | factura hotel | Nu | — | — | — | — | — | — | *nicio ramura* | *nicio ramura* | n/a | **neatins** — la fel, zero apeluri `factureaza(44` | -| **45** | factura restaurant | **Nu** — lipseste din `Do Case` | **nesetat** | **nesetat** (implicit) | **nesetat** (ramane "nu e pe stoc!" default, `:15108`) | **nesetat** (ramane vizibil) | **Da, ramane** (nescoasa) | **nesetat** | `cursor_preturi`, `:275-278` | `cursor_preturi`, `:754-757` | — (exclus explicit din bookkeeping, `:12871,17178`) | **gol real de completat** — reachable din `Meniuri\politica.mn2:18` | -| **46** | nota de plata restaurant | Nu | — | — | — | — | — | — | *nicio ramura* | *nicio ramura* | n/a | **neatins** — zero apeluri `factureaza(46`; zero documente in date de test (`tipuri_documente_facturare.md`, capcana 2) | -| **48** | custodie cu descarcare K | **Nu** — lipseste din `Do Case` | **nesetat** | **nesetat** | **nesetat** | **nesetat** | **Da, ramane** | **nesetat** | `cursor_articole_k`, `:271-273` | `cursor_articole_k`, `:750-752` | — | **gol real de completat** — reachable din `Meniuri\politica.mn2:29` (submeniu `Marfaincus`) | -| **49** | custodie fara descarcare K | **Nu** — lipseste din `Do Case` | **nesetat** | **nesetat** | **nesetat** | **nesetat** | **Da, ramane** | **nesetat** | `cursor_articole_k` | `cursor_articole_k` | — | **gol real de completat** — reachable din `Meniuri\politica.mn2:26` | -| **50** | *(in lucru)* retur custodie | Nu | — | — | — | — | — | — | *nicio ramura* | *nicio ramura* | n/a | **neatins**, marcat explicit "in lucru" in `tipuri_documente_facturare.md` | -| **51** | factura ROAACNPRO | Nu | — | — | — | — | — | — | *nicio ramura* | *nicio ramura* | n/a | **neatins** — `id_set=50100`, interval separat; probabil scris direct de ROAACNPRO, nu prin `factureaza()` local | -| **52** | contract, factura fiscala valuta | **Nu** — lipseste din `Do Case` | **nesetat** | **nesetat** | **nesetat** | **nesetat** | **Da, ramane** | **nesetat** | `cursor_contract` (grup 2,26,6,52) | ***lipseste*** din grupul contract (`:762`) — `lcSqlCursor` nedefinit pe prototip | — (confirmat, vezi §2) | **gol real de completat** — reachable din `Meniuri\contracte.mn2:18` | - -### Avize de expeditie - -| tip | denumire | Case propriu | titlu | cap cantitate | mesaj stoc | discount | `cSerie` | butoane vizibile | cursor standard | cursor prototip | Rol | stare S5 | -|---|---|---|---|---|---|---|---|---|---|---|---|---| -| 21 | catre clienti, din comanda | **Da** `:15168` (grup 21,28,42,47) | "AVIZ DE EXPEDITIE DIN COMANDA" | — (implicit) | "avizata cantitatea comandata" | **ascuns** | **Nu** | `but_urmator_tot1` | `cursor_comanda` | `cursor_comanda` | **A** | acoperit azi, de portat | -| 22 | catre clienti, din lista | **Da** `:15178` (grup 22,29) | "AVIZ DE EXPEDITIE DIN LISTA DE PRETURI" | — | "nu e pe stoc!" | **ascuns** | **Nu** | — | `cursor_preturi` | `cursor_preturi` | B | acoperit azi, de portat | -| **23** | transfer subunitati, din lista | **Da** `:15187` | "TRANSFER INTRE SUBUNITATI" | — | "nu e pe stoc!" | **ascuns** | **Nu** | — | **`cursor_preturi`** (grup 1,22,5,29,7,10,**23**), `:279` | **`cursor_gestiune`** (grup **23**,41), `:776-778` | **B** (cod comun, indiferent de sursa) | acoperit azi, **dar sursa de cursor diverge intre forme — vezi §3** | -| 24 | aviz retur | **Da** `:15242` | "RETUR AVIZ DE EXPEDITIE" | "Cant. max. de returnat" | "nu se mai poate returna" | (nemodificat aici) | **Nu** | `but_urmator_tot1` | `cursor_retur` (grup 8,9,**24**), `:306-307` | ***lipseste*** din grupul retur (`:819`, doar 8,9) — `lcSqlCursor` nedefinit pe prototip | B (invers) | acoperit azi, **dar prototipul n-are ramura de cursor — vezi §3** | -| 25 | transfer subunitati, din comanda | **Da** `:15206` | "TRANSFER INTRE SUBUNITATI PE BAZA DE COMANDA" | — | "avizata cantitatea comandata" | **ascuns** | **Nu** | `but_urmator_tot1` | `cursor_comanda` | `cursor_comanda` | **A** | acoperit azi, de portat | -| 26 | catre clienti, din contract | **Da** `:15216` | "AVIZ DE EXPEDITIE DIN CONTRACTUL …" | "Cantitate in stoc" | "nu e pe stoc!" | **ascuns** | **Nu** | — | `cursor_contract` (grup 2,26,6,52) | `cursor_contract` (grup 2,26,6 — fara 52, dar 26 e prezent) | — (confirmat, §2) | acoperit azi, de portat | -| **27** | transfer subunitati, pe lucrare | n/a — **ramane pe calea lui** | n/a | n/a | n/a | n/a | n/a | n/a | `cursor_lucrare`, `:302-304` | `cursor_lucrare`, `:815-817` | n/a | **ramas pe calea veche** — `frm_avizare_lucrare`, confirmat §4 | -| 28 | catre clienti debitori, din comanda | **Da** `:15168` (grup 21,28,42,47) | "AVIZ DE EXPEDITIE DIN COMANDA" | — | "avizata cantitatea comandata" | **ascuns** | **Nu** | `but_urmator_tot1` | `cursor_comanda` | `cursor_comanda` | **A** | acoperit azi, de portat | -| 29 | catre clienti debitori, din lista | **Da** `:15178` (grup 22,29) | "AVIZ DE EXPEDITIE DIN LISTA DE PRETURI" | — | "nu e pe stoc!" | **ascuns** | **Nu** | — | `cursor_preturi` | `cursor_preturi` | B | acoperit azi, de portat | -| **30** | transfer subunitati, pe NIR | n/a — **ramane pe calea lui, dar prin acelasi formular** | (irelevant — formular niciodata aratat) | (irelevant) | (irelevant) | (irelevant) | (irelevant) | (irelevant) | `cursor_aviz_nir`, `:299-300` | `cursor_aviz_nir`, `:813` | n/a | **ramas pe calea veche, cu nuanta** — vezi §4 | -| 41 | retur transfer, lista pret | **Da** `:15197` | "RETUR TRANSFER" | — | "nu e pe stoc!" | **ascuns** | **Nu** | — | `cursor_gestiune`, `:296-298` | `cursor_gestiune` (grup 23,41) | **B** | acoperit azi, de portat | -| 42 | catre clienti custodie, din comanda | **Da** `:15168` (grup 21,28,42,47) | "AVIZ DE EXPEDITIE DIN COMANDA" | — | "avizata cantitatea comandata" | **ascuns** | **Nu** | `but_urmator_tot1` | `cursor_comanda` | `cursor_comanda` | **A** | acoperit azi, de portat | -| 47 | catre clienti custodie K, din comanda | **Da** `:15168` (grup 21,28,42,47) | "AVIZ DE EXPEDITIE DIN COMANDA" | — | "avizata cantitatea comandata" | **ascuns** | **Nu** | `but_urmator_tot1` | `cursor_comanda` | `cursor_comanda` | **A** | acoperit azi, de portat | -| **50** | *(in lucru)* retur clienti custodie | Nu | — | — | — | — | — | — | *nicio ramura* | *nicio ramura* | n/a | **neatins**, "in lucru" | - -**Tipuri negative** (`-1..-13`, ROAGEST/ROAAUTO): **ramase pe calea altui produs** — nu trec -niciodata prin `factureaza()`/`factureaza2` din ROAFACTURARE (cautare exhaustiva, zero potriviri), -deci nu intra in perimetrul Do Case-ului acestui formular. Declarate aici explicit, nu omise. - ---- - -## 2. Tipurile care nu intra in niciun `Case` — inventar complet - -Cerinta explicita a misiunii: "nu doar 52". Lista completa, verificata pe intreg `Do Case`-ul -(`ofacturare.vc2:15109-15248`, citit integral, nu esantion) fata de lista de referinta: - -**Nu apar in niciun `Case` al `frm_facturare_articole.Init`:** - -| tip | reachable prin `factureaza()`? | ce pierde concret | -|---|---|---| -| 43 | Nu (0 apeluri in tot `D:\ROA`) | irelevant — nu ajunge la acest formular | -| 44 | Nu | irelevant | -| **45** | **Da** (`Meniuri\politica.mn2:18`) | titlu, cap coloana cantitate, mesaj de stoc, `cSerie` nescoasa (ramane vizibila, desi tip 45 e explicit exclus din bookkeeping-ul de cantitate — cele doua lucruri nu sunt legate) | -| 46 | Nu | irelevant — zero documente si in datele de test | -| **48** | **Da** (`Meniuri\politica.mn2:29`, submeniu `Marfaincus`) | idem 45 | -| **49** | **Da** (`Meniuri\politica.mn2:26`) | idem 45 | -| 50 | Nu, "in lucru" | irelevant azi | -| 51 | Nu (interval `id_set` separat, alt produs) | irelevant pentru acest formular | -| **52** | **Da** (`Meniuri\contracte.mn2:18`) | titlu (ramane cel implicit al formularului), `cSerie` nescoasa, cap coloana cantitate implicit — **cel mai vizibil defect, pentru ca 2/6 (acelasi grup logic) au titlu corect** | - -**Concluzie**: din cele noua tipuri fara `Case`, **patru sunt reale si vizibile utilizatorului azi** -(`45, 48, 49, 52`) — acestea sunt golul de completat cu valoare, nu doar `52`. Celelalte cinci -(`43,44,46,50,51`) nu ajung niciodata la acest formular in fluxul curent — nu au nevoie de ramura in -`Do Case` **pana cand** ceva le conecteaza la `factureaza()` (posibil, dar in afara perimetrului -observabil aici; de tratat ca risc, nu ca bug, la sectiunea 10). - -**De ce raman "invizibile" azi cu `cSerie`/titlu implicit, nu cu eroare**: `Do Case ... Endcase` fara -ramura `Otherwise` in VFP nu genereaza nicio eroare cand nimic nu se potriveste — pur si simplu sare -peste tot blocul. De-asta tip 45/48/49/52 "merg" (formularul se deschide, factureaza cu succes), doar -cu UI-ul netratat pentru cazul lor specific — un defect tacut, nu un crash, motiv probabil pentru care -n-a fost observat/raportat pana acum. - ---- - -## 3. Divergentele standard vs. prototip - -| Aspect | Standard (`factureaza`) | Prototip (`factureaza2`) | Comportament corect de pastrat | -|---|---|---|---| -| **Cursor pe tip 23** | `cursor_preturi` (grup `1,22,5,29,7,10,23`, `:279`) — tratat ca lista de preturi | `cursor_gestiune` (grup `23,41`, `:776-778`) — tratat ca transfer | **`cursor_gestiune`**, impreuna cu 41 (deja stabilit de S4/S4b: transferul e o singura familie de tip, indiferent daca porneste "din lista" sau "retur"; tratarea ca lista de preturi pe standard e inconsistenta cu propriul titlu "TRANSFER INTRE SUBUNITATI" pe care tot standardul il afiseaza pentru tip 23) | -| **Cursor pe tip 52** | prezent, grupat cu `2,26,6` (`:283`) | **absent** din grupul contract (`:762`, doar `2,26,6`) — `lcSqlCursor` ramane nedefinit daca cineva factureaza tip 52 prin prototip | **prezent**, grupat cu `2,6,26` — lipsa lui pe prototip e o eroare de portare, nu o alegere deliberata (nimic in cod sugereaza ca 52 trebuia tratat diferit de 2/6/26 la nivel de cursor) | -| **Cursor pe tip 24** | prezent, grupat cu `8,9` (`:306-307`) | **absent** din grupul retur (`:819`, doar `8,9`) — `lcSqlCursor` nedefinit | **prezent**, grupat cu `8,9` — acelasi tip de omisiune ca la 52 | -| **Init: diferentiere pe tip** | 14 ramuri, seteaza titlu/cap coloana/mesaj/discount/`cSerie`/butoane | practic nimic — doar `lcTipDoc` ("factura"/"aviz"), pe o lista **fara tip 24** | **toata logica standardului**, portata — prototipul nu are nimic de pastrat aici in afara de pozitia `lcTipDoc` | -| **Coloana `cSerie`** | exista in grid prin design, se scoate condiționat (10 din 21 tipuri acoperite) | **nu exista deloc** ca si coloana in `grd_factura` (gridul unic al prototipului) | de decis explicit la proiectare (§5) — nu e o simpla portare, gridul insusi trebuie sa capete coloana | -| **`but_urmator_tot1` (sau echivalentul lui)** | vizibil pe 9 din 21 de tipuri (§1) | nu exista conceptul in Init — prototipul nu are nimic care sa corespunda azi | de portat lista completa de vizibilitate din standard | -| **`clb_discount.Visible`** | ascuns explicit pe toate tipurile de aviz (`21,28,42,47,22,29,23,41,25,26`) | niciodata atins in Init | de portat integral | - -**De ce conteaza asta pentru S5**: planul spune ca formularul unificat se bazeaza pe prototip -(arhitectura lui: grid unic, editare inline, cautare pe server — deja deciziile S1-S4). Dar -**diferentierea pe tip nu vine "aproape gata" din prototip** — vine aproape in intregime din -standard, si trebuie portata, nu doar completata cu cele patru tipuri lipsa. Cele doua liste (tipuri -lipsa din standard: 45/48/49/52; tot ce lipseste din prototip: aproape totul) sunt probleme -**diferite**, care se rezolva **in aceeasi miscare** daca proiectarea de la §5 porneste de la o -sursa unica de configurare portata integral din standard, cu cele patru completari incluse de la -inceput (nu adaugate separat, dupa portare). - ---- - -## 4. Tipurile speciale (27, 30) — confirmate pe cod - -### Tip 27 — transfer pe baza de lucrare - -Confirmat la trei niveluri, toate in `ofacturare.prg`: -- `Do Case tnTip = 27 -> poDate.nIdTipDoc = 6` (`:188-189`, tip document AVIZ); -- `Do Case tnTip = 27 -> lcObiect = [frm_date_aviz_lucrare]` (`:218-219`) — **formular de antet - diferit**, nu `frm_date_aviz`/`frm_date_factura`; -- `Do Case tnTip = 27 -> lcObject = [frm_avizare_lucrare]` (`:386-387`) — **formular de articole - diferit**, nu `frm_facturare_articole`. Cursorul sursa e si el propriu: `cursor_lucrare` - (`:302-304`), populat pe `poDate.id_lucrare`, un camp pe care restul tipurilor nu-l au. - -**Ce il tine pe calea lui**: `id_lucrare` — o legatura pe care niciun alt tip de document n-o are -(lucrare de service/executie, nu comanda/aviz/contract). `frm_avizare_lucrare` grupeaza gestiunile -destinatie diferit (`crsgestiunidest`, `:388-393`, cu optiunea ``), o structura pe care -`frm_facturare_articole`/`2` n-o au. **Formularul unificat n-ar avea `id_lucrare` si n-ar avea -gruparea pe gestiuni destinatie** — motiv suficient sa ramana separat, confirmat pe cod, nu -presupus. - -### Tip 30 — transfer pe baza de NIR - -**Nuanta importanta, gasita aici**: tip 30 **nu ocoleste** `frm_facturare_articole` — il -instantiaza, exact ca orice alt tip din grupul "otherwise" (`ofacturare.prg:395`, `lcObject = -[frm_facturare_articole]`, ramura `Else` a lui `If tnTip = 27`). Trece prin acelasi `Init`, acelasi -`Do Case` de la `:15109-15248` (unde `30` nu are ramura proprie — ar avea aceleasi goluri ca 45/48/49 -daca ar fi vreodata aratat). Diferenta reala: **formularul nu e niciodata aratat** -(`ofacturare.prg:444-453`): - -``` -IF tnTip = 30 && AVIZ DIN NIR - ofrmdetaliifactura.do_calculeaza_totaluri() - ofrmdetaliifactura.but_termin1.Click() - plVizibil = .F. - ... -ELSE - ... - If plVizibil - ofrmdetaliifactura.Show() - Else - ofrmdetaliifactura.Release() - pnButon = 2 - Endif -ENDIF -``` - -**Ce il tine pe calea lui**: nu structura formularului (e acelasi obiect), ci **automatizarea -completa a fluxului** — cursorul sursa (`cursor_aviz_nir`, populat din `VRUL`/tranzactii de receptie, -nu din comanda/lista de preturi) vine deja complet, iar codul apeleaza direct metodele de finalizare -fara interactiune. **Pentru formularul unificat**: daca arhitectura noua pastreaza acelasi tipar -("creeaza obiectul, populeaza, cheama finalizarea, `Release()` fara `Show()`"), tip 30 continua sa -functioneze neschimbat — nu are nevoie de ramura in configurarea vizuala (§5), pentru ca vizualul nu -se vede niciodata. **Singurul risc real**: daca `do_calculeaza_totaluri()`/`but_termin1.Click()` ale -formularului unificat ajung sa citeasca vreo proprietate pe care doar `Do Case`-ul vizual o seteaza -azi (de exemplu, un cod care ar verifica `This.cmesaj_cantitate` sau `lcTipDoc` in logica de calcul, -nu doar in UI) — **de verificat explicit la implementare**, nu presupus ca "nu conteaza pentru ca nu -se vede". - ---- - -## 5. Ce structura inlocuieste `Do Case`-ul de 140 de linii - -### Optiunile comparate - -**(a) Pastreaza `Do Case`, doar completeaza-l** (adauga ramuri pentru 45/48/49/52, porteaza restul in -prototip). Cost minim imediat, dar **nu rezolva problema de fond**: un `Do Case` fara `Otherwise` nu -semnaleaza niciodata un tip lipsa — exact mecanismul care a produs golul de azi (patru tipuri reale -pierdute, ani la rand, fara nicio eroare). Orice tip nou de document adaugat in viitor (suita are deja -`46,50` "in lucru", `43,44,51` din alte fluxuri) risca aceeasi soarta. - -**(b) Metoda separata per grup de tipuri** (`configureaza_lista_preturi()`, `configureaza_comanda()`, -...). Mai clar decat un `Do Case` unic, dar tot **implicit** — un tip nou tot nu declanseaza nicio -eroare daca nimeni nu-l adauga in metoda corecta; doar muta problema din 140 de linii intr-un fisier -cu mai multe metode mici, fara sa adauge un mecanism de detectie. - -**(c) Tabel de configurare per tip (RECOMANDAT)**. Un cursor/tabel cu **un rand per `tip`**, coloanele -fiind exact proprietatile pe care `Do Case`-ul le seteaza azi: `titlu`, `cap_cantitate`, `mesaj_stoc`, -`discount_vizibil` (`L`), `are_serie` (`L`), `tip_doc` (`factura`/`aviz`), `buton_tot_vizibil` (`L`), -`buton_retur_vizibil` (`L`), `grup_sursa` (pentru meniul S4b: `lista/comanda/aviz-comanda/transfer/ -retur/contract-articole/contract-rate`). Populat printr-un singur bloc de `INSERT INTO` (sau un DBF -static, `configuratie_tip_document.dbf`, editabil fara compilare) — **un rand per tip din -`tipuri_documente_facturare.md`**, inclusiv cele patru azi lipsa. - -`Init` devine: -``` -SELECT * FROM configuratie_tip_document WHERE tip = poDate.tip INTO CURSOR crscfgtip -If Reccount('crscfgtip') = 0 - * tip necunoscut -- eroare explicita, nu formular netratat tacut - AMESSAGEBOX("Tip de document necunoscut in configurare: " + Transform(poDate.tip), 16, "Eroare configurare") - Thisform.Release() - Return -Endif -This.lb_titlu_alb_b121.Caption = crscfgtip.titlu -This.grd_articole.cCantitate.header1.Caption = crscfgtip.cap_cantitate -This.cmesaj_cantitate = crscfgtip.mesaj_stoc -This.clb_discount.Visible = crscfgtip.discount_vizibil -If !crscfgtip.are_serie - This.grd_articole.RemoveObject('cSerie') -Endif -This.but_urmator_tot1.Visible = crscfgtip.buton_tot_vizibil -This.but_retur.Visible = crscfgtip.buton_retur_vizibil -``` - -**De ce e mai bun decat (a)/(b) pe cost de intretinere**: -- **Un tip lipsa devine o eroare vizibila la deschidere**, nu un formular netratat tacut — exact - defectul care a permis golul de azi sa treaca neobservat ani la rand. -- **"Cat de usor se vede un tip lipsa" e mecanic, nu vizual** — vezi §8, o interogare simpla compara - lista de tipuri din configurare cu lista de referinta din `tipuri_documente_facturare.md`, fara sa - ruleze formularul. -- **Grupurile identice raman explicite, nu implicite** — azi, "tipurile 21,28,42,47 au acelasi titlu" - se vede doar citind `Inlist(...)`; intr-un tabel, acelasi lucru se vede ca patru randuri cu aceeasi - valoare in coloana `titlu` — usor de generat cu un singur `INSERT` per grup (`FOR EACH tip IN - (21,28,42,47) ... INSERT ...`), nu mai putin explicit, dar auditabil cu `SELECT titlu, COUNT(*) - GROUP BY titlu`. -- **Coloana `are_serie` rezolva si divergenta standard/prototip de la §3** — prototipul nu are azi - conceptul deloc; cu configurarea noua, adaugarea coloanei `cSerie` in gridul unificat devine - conditionata de aceeasi sursa unica, indiferent de forma de baza. - -**Cost**: portarea initiala a ~21 de randuri (14 ramuri distincte de azi + 4 completari + grupare -explicita), plus un nou tabel/cursor de intretinut. Nu e cod nou complex — e date, nu logica; riscul -de regresie e in acuratetea portarii (fiecare valoare trebuie sa corespunda exact cu ce face azi -`Do Case`-ul), verificabil linie cu linie fata de tabelul din §1. - -**Recomandare finala**: **(c)**, cu tabelul de configurare implementat ca `DBF` static (nu cursor -generat in cod) — editabil de oricine fara sa recompileze, si direct verificabil cu `SELECT` fara sa -porneasca formularul (vezi §8). - ---- - -## 6. Grupuri naturale de tipuri - -Din tabelul §1, grupurile care au azi (sau ar trebui sa aiba) valori identice pe toate coloanele: - -| Grup | Tipuri | Ce difera **in interiorul** grupului | -|---|---|---| -| **Lista de preturi, fara stoc special** | 1, 5, 7, 10 | Nimic in Init — difera doar valuta/tip document la nivel de antet (`poDate.in_valuta`, `nIdTipDoc`), nu in acest formular | -| **Contract, factura** | 2, 6 (+ **52** de adaugat) | Nimic in Init dupa completare — `52` e valuta, ca `6`, dar cu alt `id_set`; titlul/coloanele sunt identice | -| **Aviz din comanda (clienti/debitori/custodie)** | 21, 28, 42, 47 | Nimic in Init — difera doar destinatia comerciala (client normal/debitor/custodie), invizibila la acest nivel | -| **Aviz din lista de preturi** | 22, 29 | Nimic — difera doar client normal/debitor | -| **Retur facturi** | 8, 9 | Nimic — lei/valuta | -| **Transfer subunitati** | 23, 41 | Sens (din lista vs. retur) — titlu diferit ("TRANSFER..." vs "RETUR TRANSFER"), restul identic; **trebuie unificate pe cursor** (§3) inainte de unificare vizuala | -| **Comanda proprie** | 3 | Singur — cap de coloana propriu ("Cantitate comandata") | -| **Avize proprii** | 4 | Singur — discount vizibil (spre deosebire de toate celelalte avize) | -| **Aviz din contract** | 26 | Singur — titlu propriu, dar cursor comun cu grupul contract | -| **Aviz retur** | 24 | Singur — cursor comun cu 8/9, dar titlu si `but_urmator_tot1` proprii | -| **Custodie K** | 48, 49 | Identice ca structura vizuala (ambele lipsesc azi) — difera doar `cu`/`fara` descarcare K, invizibil la acest nivel | -| **Restaurant** | 45 | Singur, azi lipsa | - -Observatie de proiectare: grupurile "aviz din comanda" (21/28/42/47) si "comanda" (3) au **acelasi** -cap de coloana si mesaj de stoc conceptual ("cantitate comandata"), dar text usor diferit -("facturata"/"avizata") — pastrate distincte in tabelul de configurare (nu fortate identice), pentru -ca diferenta e deliberata in codul de azi (`:15149` vs `:15176`, verb diferit). - ---- - -## 7. Pasi de implementare, ordonati - -1. **Extrage tabelul de configurare din `Do Case`-ul standard, exhaustiv** — un rand per tip din - `tipuri_documente_facturare.md` care intra prin acest formular (Facturi + Avize, exclus 27/30/ - negative), valorile copiate exact din §1. *Gata cand*: `SELECT DISTINCT tip FROM - configuratie_tip_document` produce exact multimea `{1,2,3,4,5,6,7,8,9,10,21,22,23,24,25,26,28,29, - 41,42,45,47,48,49,52}` (23 de tipuri) — nici unul in minus, nici unul in plus fata de lista - calculata in §8. -2. **Completeaza cele patru randuri azi lipsa (45,48,49,52)** cu valori coerente cu grupul lor logic - (45 langa lista de preturi cu stoc dezactivat conceptual; 48/49 langa custodie K; 52 langa 2/6) — - decizie de continut, nu doar de structura; **de validat cu Marius inainte de a le considera - "gata"**, pentru ca azi nu exista niciun titlu/mesaj de referinta pentru ele (nimeni nu l-a vazut - pe ecran). *Gata cand*: cele patru randuri au valori nenule pe toate coloanele obligatorii - (`titlu`, `cap_cantitate`, `mesaj_stoc`). -3. **Corecteaza rutarea cursorului**: `23` trece pe `cursor_gestiune` (unificat cu `41`, nu mai divide - standard/prototip); `52` si `24` primesc ramura de cursor pe orice cale ramane vie din prototip - (daca formularul unificat pastreaza `factureaza2`-stil, sau devine parte din calea unica daca - `factureaza`/`factureaza2` se unesc — decizie separata, in afara acestei povesti). *Gata cand*: - deschiderea formularului pe tip `23`, `24` si `52` prin calea noua produce acelasi continut in - `crsarticole` ca varianta care functiona deja (`23`→prototip vechi pentru comparatie de continut, - `24`/`52`→standard). -4. **Adauga coloana `cSerie` in gridul unificat, condiționata pe `are_serie`** — azi absenta din - gridul prototipului; adaugata o singura data, aratata/ascunsa din configurare, nu prin - `RemoveObject` scris de mana pe fiecare tip. *Gata cand*: pe un tip cu `are_serie=.T.` (ex. 4) - coloana e vizibila; pe un tip cu `are_serie=.F.` (ex. 3) nu e. -5. **Inlocuieste `Do Case`-ul din `Init` cu citirea din configurare** (structura din §5), inclusiv - ramura de eroare explicita pe tip necunoscut. *Gata cand*: pentru fiecare din cele 23 de tipuri, - deschiderea formularului seteaza exact valorile din tabelul §1/pasul 2 (comparatie automata, nu - vizuala — proprietatile sunt citibile headless). -6. **Verifica tipurile speciale raman neatinse**: 27 (cale total separata, neschimbata), 30 (acelasi - formular, dar `do_calculeaza_totaluri`/`but_termin1.Click()` nu citesc nimic setat doar de vechiul - `Do Case` vizual — verificare explicita, §4). *Gata cand*: un document tip 30 de test se - finalizeaza cu acelasi rezultat in `VANZARI`/`VANZARI_DETALII` inainte si dupa migrare. -7. **Documenteaza tipurile neatinse (43,44,46,50,51) ca decizie explicita**, nu ca omisiune — un - comentariu in tabelul de configurare (`* 43,44,46,50,51: neconectate la factureaza() in - ROAFACTURARE, verificat `) ca viitorii cititori sa nu presupuna ca lipsesc din greseala. - *Gata cand*: comentariul exista si linkeaza spre acest raport. - -*Depinde de*: S4 (cautarea articolelor pe server) pentru arhitectura gridului unic — pasul 4 de aici -presupune ca gridul unificat exista deja in forma stabilita de S4; S4b (bara de butoane) pentru -`buton_tot_vizibil`/`buton_retur_vizibil`, care alimenteaza si meniul `xmenu()` de acolo — coloanele -`grup_sursa` din tabelul de configurare (§5) sunt exact ce cere S4b sectiunea 3. - ---- - -## 8. Cum se verifica ca acoperirea e completa — proba mecanica - -**Nu o citire — o interogare care compara doua liste.** Doua surse de adevar: - -1. **Lista de referinta**: tipurile din `COMUN\docs\tipuri_documente_facturare.md`, sectiunile - "Facturi" si "Avize de expeditie", **minus** cele confirmate neatinse azi de acest formular (27, 30 - raman — vezi nuanta §4 — dar 43,44,46,50,51 se exclud daca raman neconectate; de recalculat lista - la fiecare rulare, nu de la o constanta inghetata). -2. **Lista din configurare** (dupa implementarea §5): `SELECT DISTINCT tip FROM - configuratie_tip_document`. - -**Script de verificare** (headless, fara UI, rulabil oricand): - -```foxpro -* verifica_acoperire_tip.prg — proba mecanica pentru S5 -LOCAL lnLipsa, lnInPlus -* 1. lista de referinta -- tinuta manual sincron cu tipuri_documente_facturare.md -* (facturi + avize, exclus negative; 27/30 raman in lista, tratate separat la pasul 3) -DIMENSION laReferinta[23] -laReferinta = [1,2,3,4,5,6,7,8,9,10,21,22,23,24,25,26,27,28,29,30,41,42,47,52] && + 45,48,49 dupa Pasul 2 - -* 2. lista din configurare -SELECT DISTINCT tip FROM configuratie_tip_document INTO CURSOR crsCfg - -* 3. tipuri de referinta fara configurare (exclus 27, 30 -- cale separata confirmata) -* 4. tipuri in configurare fara corespondent in referinta (config "orfana") -* -- ambele liste trebuie sa fie goale la "gata" -``` - -Alternativ, **fara sa astepte implementarea §5**: acelasi principiu se aplica azi direct pe -`Do Case`-ul din `ofacturare.vc2:15109-15248`, extragand tipurile din fiecare `Case ... Inlist/=` prin -`vfp_symbols.ps1 -Grep 'Case (poDate\.tip|Inlist\(poDate\.tip'` si comparand rezultatul cu lista de -referinta — exact tehnica folosita in aceasta cercetare pentru a produce tabelul din §1 (nu o -citire vizuala, o extractie sistematica). - -**Pentru cursor (rutare, §3)**: acelasi principiu, pe `ofacturare.prg`, comparand tipurile din fiecare -`Do Case`/`Inlist` de la `:266-308` (standard) cu cele de la `:748-822` (prototip) — orice tip prezent -intr-una si absent in cealalta e o divergenta de raportat (asa au fost gasite `52` si `24` in aceasta -sesiune). - ---- - -## 9. Ce nu se poate testa headless - -- **Titlul, capul de coloana, mesajul de stoc ca text afisat efectiv pe ecran** — verificabile - headless doar ca *proprietati setate* (`This.lb_titlu_alb_b121.Caption` dupa `Init()`, apelat direct - cu un `poDate` de test), nu ca randare vizuala. Diferenta conteaza: o proprietate corect setata pe - un control care nu exista (`lb_titlu_alb_b121` lipsa din prototip azi) ar trece "headless" fara sa - arate nimic real — de confirmat mai intai ca ambele controale exista in formularul unificat. -- **Coloanele de grid (`grd_articole`/`grd_factura`, coloana `cSerie` noua)** — capcana deja cunoscuta - pe acest proiect: sub `-A -T` (rulare headless), `ColumnCount=0`/`RecordSource` raman artefacte, - coloanele nu se materializeaza. Exista un harness UI **vizibil** care le citeste corect — orice - verificare a `are_serie`/coloanelor trebuie sa treaca prin acela, nu prin `-A -T`. -- **Tip 30 — fluxul complet fara `Show()`** — se poate rula headless (nu deschide nicio fereastra prin - constructie), dar verificarea "rezultatul in Oracle e identic inainte/dupa" cere date de test reale - (un NIR cu articole), nu doar apelul metodei. -- **Popup-ul/dialogul viitor din S4b (`xmenu()`, `cauta_alfa`)**, daca `grup_sursa` din tabelul de - configurare (§5/§6) ajunge sa-l alimenteze direct — mostenit ca limitare de la raportul S4b (`§8` - de acolo): continutul `lcMeniu` verificabil ca text, popup-ul afisat nu. -- **Comportamentul real al meniurilor `politica.mn2`/`contracte.mn2`** pentru tipurile 45/48/49/52 — - se poate confirma ca exista intrarea de meniu (citire `.mn2`, facuta in aceasta cercetare), dar nu - ca apasarea ei in productie deschide exact formularul asteptat, fara o rulare UI vizibila. - ---- - -## 10. Riscuri si ce ramane de decis de Marius - -1. **Continutul exact (titlu/mesaj) pentru cele patru tipuri azi netratate (45,48,49,52)** — codul nu - ofera niciun precedent vizual (n-au fost vazute niciodata corect pe ecran), deci textele propuse in - Pasul 2 (§7) sunt o **propunere**, nu o recuperare a unui text existent. **Recomandare**: 45 langa - grupul "lista de preturi fara stoc" (e exclus explicit din bookkeeping de stoc, `:12871`); 48/49 cu - titlu care mentioneaza "custodie" (singurul lucru care le distinge conceptual azi, in afara de - coeficientul K, invizibil la acest nivel); 52 **identic cu 2/6** ("FACTURA PE CTR. ..."), pentru - coerenta cu gruparea deja facuta corect in `frm_date_factura.Init` (`:9530,9632,9670`, grupul - `2,6,52`). -2. **Daca 23 trece pe `cursor_gestiune` pe calea unificata, comportamentul vizibil pentru operator se - schimba** (sursa de articole devine stocul din gestiune, nu lista de preturi) — **decizie de produs, - nu doar tehnica**, deja semnalata de S4/S4b, dar repetata aici pentru ca afecteaza direct §5/§7 - (tabelul de configurare trebuie sa reflecte alegerea finala, nu ambele variante). **Recomandare**: - `cursor_gestiune`, argumentat de coerenta cu titlul "TRANSFER INTRE SUBUNITATI" pe care insusi - standardul il afiseaza azi pentru 23 (titlul spune "transfer", cursorul azi spune "lista de - preturi" — inconsistenta interna de rezolvat, nu de pastrat). -3. **Daca 43,44,46,50,51 raman permanent neconectate la `factureaza()`, sau exista planuri sa fie - activate** — nu s-a gasit nicio dovada de cod ca ar fi in curs, dar nici o confirmare ca sunt - abandonate definitiv (in afara de `50`, marcat explicit "in lucru" in sursa). **De intrebat pe - Marius direct**, pentru ca raspunsul schimba daca tabelul de configurare (§5) trebuie sa le includa - preventiv sau poate ramane cu 23 de randuri. -4. **Coloana `cSerie` pe gridul unificat — cost de adaugare** — prototipul n-are azi conceptul deloc; - adaugarea ei presupune modificari de grid (nu doar de cod), posibil in afara perimetrului text-only - (`.vc2`/`.sc2` prin `txt2vcx.ps1`) daca structura de coloane a gridului cere editare in IDE — **de - confirmat la implementare**, nu presupus ca e o simpla proprietate. -5. **Tabel de configurare ca `DBF` static vs. `INSERT`-uri in cod** — recomandarea (§5) e DBF, pentru - editabilitate fara recompilare, dar suita foloseste azi predominant cod pentru acest fel de - configurare (niciun precedent de DBF static de configurare gasit in `frm_facturare_articole`/ - `frm_date_factura`) — **de validat cu Marius daca abaterea de la conventia existenta merita - beneficiul**, sau daca un cursor generat in cod (mai aproape de stilul actual, dar mai putin - editabil) e preferat. - -## Handoff - -Cercetare + proiectare incheiata in aceasta sesiune, fara sa fie nevoie de predare de context. -Toate cele zece sectiuni cerute de misiune sunt completate, cu `fisier:linie` verificat direct pe -fisierele text reale (`ofacturare.vc2`, `ofacturare.prg`, nu `.bak`), plus meniurile `.mn2` si -raportul S4/S4b/S4_punct2 citate ca sursa pentru punctele deja stabilite (nereinvestigate). - -Descoperiri dincolo de cerinta explicita a misiunii, semnalate clar in text: (1) patru tipuri lipsa -din `Do Case`, nu unul (45/48/49/52, sectiunea 2); (2) prototipul (`frm_facturare_articole2.Init`) -e aproape complet gol pe diferentiere de tip, nu doar incomplet (sectiunea 3) — cea mai mare -descoperire a acestei sesiuni, cu impact direct asupra efortului de implementare estimat pentru S5; -(3) doua divergente noi de rutare a cursorului (52, 24), pe langa cea deja cunoscuta (23) (sectiunea -3); (4) tipurile 26 si 52 confirmate fara niciun bookkeeping `crsarticole`, inchizand punctul lasat -deschis de raportul S4 punctul 2 (sectiunea 1, nota de subsol pe tabel). - -Niciun fisier de cod atins, niciun `git_sync.ps1`/`txt2vcx.ps1` rulat, nicio scriere pe Oracle (numai -`Read`/`Grep`/`vfp_symbols.ps1` pe fisiere de pe disc). diff --git a/docs/cercetare/s5b_proforma_descarcare_gestiune.md b/docs/cercetare/s5b_proforma_descarcare_gestiune.md deleted file mode 100644 index f773c17..0000000 --- a/docs/cercetare/s5b_proforma_descarcare_gestiune.md +++ /dev/null @@ -1,321 +0,0 @@ -# Cercetare — Verificarea 4: descarcare de gestiune pe proforma (Oracle) - -Investigatie read-only, 10.08.2026. Nicio modificare de cod, niciun `git_sync.ps1`. Continua -`docs\cercetare\proforma_copiere_puncte_intrare.md` (nu il reia) si raspunde punctual la -"Necunoscute ramase" #1 de acolo. - -**Nota sursa Oracle**: `docs\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` (mentionat in brief) nu -exista in `docs\`. Fisierul real e la -`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` (823 337 octeti, -confirma marimea asteptata). Am semnalat discrepanta catre team-lead prin mesaj si am continuat pe -acest fisier — **toate citatele `PACK:linie` de mai jos sunt pe fisierul din `DATABASE`, nu pe unul -din `docs\`**, pentru ca al doilea nu exista. - -## Verdict (raspuns la intrebarea 2) - -**NU — la emiterea unei proforme, gestiunea nu se descarca**, pentru niciun articol, indiferent daca -articolul e cu adevarat gestionabil in nomenclator sau nu. Blocajul e prin design: VFP marcheaza -toate liniile unei proforme "negestionabile" inainte de compunerea documentului, ceea ce le trimite -la server cu sentinela `id_gestiune = -1000` (dedus, nu confirmat linie-cu-linie — vezi sectiunea -"Ce nu s-a putut stabili"), iar pe Oracle `contabilizeaza_articol` sare apelul catre -`descarca_gestiune` exact pe acest sentinel. Concluzia se sprijina si pe intentia de business -explicita din changelog (12.03.2021 / 2.7.x): *"Proforma. Articolele din proforma sunt marcate -'negestionabile', astfel incat nu mai este necesara existenta articolelor in stoc pentru a genera -proforma."* — adica cerinta initiala a fost explicit "proforma trebuie sa mearga si fara stoc", -nu doar "nu arata plafonul". - ---- - -## 1. Tipurile de document "proforma" - -Deja stabilit in `proforma_copiere_puncte_intrare.md` §1 si reconfirmat aici pe partea Oracle: -proforma **nu** e o valoare in `TIP` (`pack_facturare.ntip`, 1-52) — e un atribut ortogonal. - -- **VFP**: `poDate.nIdTipDoc = 23` (fata de `5` = FACTURA), setat din combo-ul "Tip document" - (`COMUN\clase\ofacturare.vc2:9396-9403`, `:8745-8754`). Setter-ul deriva boolean-ul - `poDate.eProforma`: `COMUN\programe\ofacturare_comun.prg:593-599` — - `This.eProforma = IIF(m.tnIdTipDoc = This.nIdTipDocProforma, 1, 0)`. -- **Oracle**: nu exista `nIdTipDoc` — documentul se scrie in `VANZARI` cu `TIP` normal (business - type, 1-52), iar `EPROFORMA` e o coloana separata pe `VANZARI`. Dovada directa: - `PACK:5637-5671` (`PROCEDURE scrie_proforma`) — scrie intai antetul cu `scrie_in_vanzari` (acelasi - helper ca la o factura normala), apoi: - ``` - PACK:5666-5669 - -- completez vanzari.eproforma - update vanzari - set eproforma = 1 - where id_vanzare = pack_facturare.nid_vanzare; - ``` - Deci pe Oracle, proforma **e** o factura normala in `VANZARI`/`VANZARI_DETALII`, cu un flag in - plus. -- Exista si un mecanism **legacy**, tabele separate `PROFORME`/`PROFORME_DETALII` - (`PACK:5674-5767`, `PROCEDURE scrie_proforma_old` / `sterge_proforma_old`, `:5413-5429`) — nefolosit - de fluxul curent (VFP apeleaza `scrie_proforma`, nu `scrie_proforma_old`; nu am gasit niciun apel - VFP catre varianta `_old` in `COMUN\programe\*.prg`). Tratati ca schela moarta, nu ca mecanism activ. -- `V_TIP = -102` in `citeste_setari_document` (`PACK:1960-1994`, ramura `:1985-1987`, - `V_VARNAME := 'ID_FDOC_PROFORMA'`) e un cod folosit **doar** pentru alocarea de serie/numar - (`id_fdoc`), apelat din VFP la `initializeaza_setari_document(-102)` - (`COMUN\clase\ofacturare.vc2:9415`, deja in cercetarea anterioara) — nu are legatura cu - `pack_facturare.ntip`, care ramane tipul de business real al documentului. - -## 2. Descarcarea de gestiune la proforma — DA/NU si mecanismul exact - -**NU.** Trasat pe trei straturi, VFP si Oracle: - -### 2.1 VFP: articolele devin "negestionabile" inainte sa intre pe document - -`COMUN\programe\ofacturare.prg:330-336` (in `factureaza`, imediat dupa incarcarea cursorului sursa -`crsarticole`, indiferent de sursa — lista de preturi, comanda, contract, aviz, retur): -``` -* Daca este o proforma, consider toate articolele negestionabile, pentru a nu mai alege din stoc -* 12.03.2021 -IF poDate.eProforma = 1 - UPDATE (m.lcCursor) SET gestionabil = 0 - GO TOP IN (m.lcCursor) -ENDIF -``` -`gestionabil = 0` se propaga in `crsfactura` prin `prelucreaza_facturacrs` -(`COMUN\programe\ofacturare_comun.prg:1801-1810`, coloana `gestionabil` e in lista de INSERT). - -La adaugarea/editarea unei linii pe formular, ramura pe `gestionabil` decide dialogul: -`COMUN\clase\ofacturare.vc2:13803-13809`: -``` -Case (poArticol.gestionabil = 0 Or gnScadereStoc = 0 Or poDate.tip = 45) && 45 = ROARESTAURANT - ofrmadarticol = Createobject("frm_articol_factura", lnCantitateMax, .F.) -``` -adica **acelasi dialog folosit pentru orice articol negestionabil in restul aplicatiei** (nu unul -proforma-specific), care ocoleste `do_alege_stoc` (dialogul de alegere lot din stoc). Pentru -articolele care intra pe `frm_articol_factura`, `id_gestiune` implicit e sentinela `-1000` -(`COMUN\clase\ofacturare.vc2:13618-13623`, `do_initializeaza_articol`: -`If Type('toArticol.id_gestiune') = "U" THEN AddProperty(toArticol,'id_gestiune',-1000)`; acelasi -tipar la `:17836-17837`). La scriere, `poArt.id_gestiune` se trimite direct ca parametru -`V_ID_GESTIUNE` catre `pack_facturare.adauga_articol_factura` -(`COMUN\clase\ofacturare.vc2:14069-14073`: `... + Nvl(Alltrim(Str(poArt.id_gestiune)),[NULL]) + ...`). - -### 2.2 Oracle: `id_gestiune = -1000` e sentinela care blocheaza descarcarea - -`adauga_articol_factura` (`PACK:4989-5284`), primeste `V_ID_GESTIUNE` si il traduce: -``` -PACK:5032-5034 -IF V_ID_GESTIUNE <> -1000 THEN - V_ID_GESTIUNE2 := V_ID_GESTIUNE; -END IF; -``` -`V_ID_GESTIUNE2` (necompletat, deci `NULL`, cand `V_ID_GESTIUNE = -1000`) e cel scris efectiv in -`VANZARI_DETALII_TEMP.ID_GESTIUNE` (`PACK:5237,5267`). - -La emitere, `contabilizeaza_articol` (`PACK:7173-7547`) parcurge liniile din -`VANZARI_DETALII_TEMP` si apeleaza `descarca_gestiune` doar aici: -``` -PACK:7472-7476 -IF pack_facturare.ntip <> 4 THEN - IF pack_facturare.nscadere_stoc = 1 AND - detalii_articol.id_gestiune <> -1000 AND - detalii_articol.in_stoc = 1 THEN - pack_facturare.descarca_gestiune(...) -``` -`NULL <> -1000` evalueaza la `NULL` in PL/SQL (nu `TRUE`), deci conditia pica indiferent de -`in_stoc` — **liniile de pe o proforma nu ajung niciodata la `descarca_gestiune`**, cata vreme -`id_gestiune` a intrat ca `-1000`. - -Exista si o a treia bariera, independenta, **in interiorul** lui `descarca_gestiune` -(`PACK:7648-7797`), dar aceasta priveste articolul insusi, nu documentul: -``` -PACK:7789-7797 --- NU SE DESCARCA GESTIUNEA PENTRU ARTICOLELE NEGESTIONABILE --- ASTFEL INCAT SA SE POATA FACE OPERATII GEN AVIZ DIN CUSTODIE INCLUSIV CU ARTICOLE NEGESTIONABILE -SELECT MAX(IN_STOC) INTO lnInStoc FROM NOM_ARTICOLE WHERE ID_ARTICOL = V_ID_ARTICOL; -IF lnInStoc = 0 THEN GOTO SFARSIT; END IF; -``` -Aceasta verifica `NOM_ARTICOLE.IN_STOC` real (nomenclator), nu flagul de proforma — protejeaza un -articol cu adevarat negestionabil, indiferent de tipul documentului. **Nu** e mecanismul care -protejeaza proforma; mecanismul de proforma e strict gate-ul `id_gestiune <> -1000` de la 2.1-2.2. - -### 2.3 `in_stoc` trimis de VFP: uneori ignorat de Oracle, dar nu e el gate-ul decisiv - -`V_IN_STOC_TEMP` (parametrul care ajunge in `VANZARI_DETALII_TEMP.IN_STOC`) e **fie** preluat direct -de la VFP (`V_IN_STOC := V_IN_STOC_TEMP`, ramurile implicita si restaurant, `PACK:5200-5203, -5111-5112`), **fie recalculat de Oracle din nomenclator/contract**, ignorand ce a trimis VFP, pe -ramurile comenzi (`PACK:5061-5066`), avize (`:5086-5091`) si contract cu pret de contract -(`:5153-5158, 5184`). Asta inseamna ca pentru o proforma facuta din comanda/aviz/contract, campul -`IN_STOC` scris efectiv poate reveni la valoarea reala din nomenclator (`1` pentru un articol -gestionabil), **dar** asta nu conteaza — gate-ul din 2.2 cere `id_gestiune <> -1000 AND in_stoc = 1` -cu **AND**, iar `id_gestiune` ramane blocat la `-1000`/`NULL` indiferent de sursa documentului -(sentinela vine din UI, la nivelul liniei, nu din cursorul de incarcare). Deci recalcularea lui -`in_stoc` de catre Oracle pe aceste ramuri nu redeschide descarcarea. - -## 3. De la proforma la factura - -Nu exista o rutina de "transformare" dedicata — mecanismul e **copierea**, deja documentata complet -in `proforma_copiere_puncte_intrare.md` §2 (tooltip explicit `COMUN\clase\ofacturare_comun.vc2:1409`: -*"Se foloseste si pentru generarea unei facturi din proforma prin copiere"*). - -- **Punct de intrare VFP**: `frm_facturi.But_copiaza1.Click -> do_copiaza` (degradeaza tipul de - business, `COMUN\clase\ofacturare_comun.vc2:3628-3713`) `-> copiere_factura` - (`COMUN\programe\oproceduri_facturare.prg:150-153`) `-> factureaza(tip_degradat, toFactura)`. -- **Punct de intrare Oracle**: acelasi `pack_facturare.adauga_articol_factura` / `scrie_in_vanzari` - ca la orice document nou — nu exista un `pack_facturare.transforma_proforma` sau echivalent. - Singurul apel Oracle specific copierii e cursorul de precompletare, - `pack_facturare.cursor_retur_document` (vezi §4), apelat cu `V_COPIERE = 1` - (`COMUN\programe\ofacturare.prg:267-268`). -- `completeaza_setari_document(toDateAnterior, .T.)` (`COMUN\programe\ofacturare_comun.prg:362-412`) - **nu propaga `nIdTipDoc`** (linia comentata, `:370`) — documentul nou porneste cu `nIdTipDoc` - implicit (`5`=FACTURA sau `6`=AVIZ, dupa `tnTip` degradat, `COMUN\programe\ofacturare.prg:187-196`), - deci **`poDate.eProforma = 0` pentru documentul nou**, indiferent ca sursa era proforma. - -## 4. Ce se intampla cu stocul intre proforma si factura - -**Nimic de reconciliat, pentru ca proforma nu a atins niciodata stocul** (§2). Nu exista concept de -"rezervare de stoc" pe proforma: -- tabelele legacy `PROFORME`/`PROFORME_DETALII` (§1) nu au coloane de rezervare si oricum nu sunt pe - calea activa; -- nu am gasit, in `PACK_FACTURARE`, niciun apel care sa insereze in `RUL` sau sa actualizeze - `STOC` la `scrie_proforma` — funcita se limiteaza la `scrie_in_vanzari` + `UPDATE vanzari SET - eproforma=1` (§1, `PACK:5637-5671`); -- cautare directa in fisierul PACK pentru orice mentiune de rezervare pe proforma (`rezerv`, - `blocheaza stoc`) nu a dat rezultate relevante (v. "Ce nu s-a putut stabili" pentru limitele - cautarii text simple pe un fisier de 823 KB). - -**La copiere (transformarea efectiva in factura)**, gestionabilitatea reala se **restaureaza**: -cursorul de precompletare la copiere, `cursor_retur_document` -(`PACK:3949-4000`), calculeaza `GESTIONABIL` asa: -``` -PACK:3993-4000 -(case - when V_PROFORMA = 1 then 0 - when V_COPIERE = 1 then B.IN_STOC -- NOM_ARTICOLE.IN_STOC, articolul real - else A.GESTIONABIL - end) AS GESTIONABIL, -``` -La copiere, `V_COPIERE = 1` e hardcodat (`COMUN\programe\ofacturare.prg:267-268`: -`cursor_retur_document(?poDate.in_valuta,?poDate.listaid,1,?poDate.eProforma,?gnIdUtil)` — al treilea -parametru pozitional `1` e `V_COPIERE`), iar `V_PROFORMA` trimis e `poDate.eProforma` **al -documentului nou**, care e `0` (§3) — deci ramura `V_PROFORMA=1` nu se activeaza la copiere, si -`GESTIONABIL = B.IN_STOC` (flagul real din nomenclator). Rezultat: liniile copiate dintr-o proforma -redevin gestionabile normal daca articolul chiar e in stoc, trec prin `do_alege_stoc` la editare -(`ofacturare.vc2:13804`, ramura `Otherwise`), primesc un `id_gestiune` real, si **descarcarea de -gestiune se face normal la emiterea facturii rezultate din copiere** — exact ca la orice factura noua, -nu printr-un pas separat de "transformare". - -## 5. Ce verifica serverul la descarcare, si cu ce eroare - -Verificari observate direct in `PACK_FACTURARE`, toate independente de proforma (se aplica oricarei -descarcari reale de gestiune): -- **Gestiune inexistenta/stearsa**: `PACK:7847-7858` — `SELECT ... FROM NOM_GESTIUNI WHERE - ID_GESTIUNE = V_ID_GESTIUNE AND STERS = 0`; `NO_DATA_FOUND -> FACT-007`. -- **Stoc epuizat / lot inexistent**: `PACK:7860-8493` — construieste `tab_stoc` din `RUL`/`STOC` dupa - criterii (articol, gestiune, cont, serie, pret etc.) si, daca nu gaseste nimic - (`SQL%ROWCOUNT = 0`): `FACT-008` ("Articolul ... nu mai e in stoc") sau, daca nici macar - denumirea nu se gaseste, `FACT-009`. -- Ramuri similare mai jos in acelasi fisier pentru alte cazuri de gestiune/combinatii invalide: - `FACT-010` (`:10001`), `FACT-011` (`:10323`), `FACT-014` combinatie invalida de gestiuni - (`:12127`), `FACT-019` gestiune (`:10449`), `FACT-020`/`FACT-021` variante "nu mai e in stoc" - (`:10748,10752`). -- **Optiune globala de bypass**: `RF_FACTURARE_FARA_STOC` (`PACK:7783-7784`, - `lnFacturareFaraStoc`) — permite facturarea peste cantitatea disponibila pentru articole - gestionabile; **nu e specifica proformei**, e o optiune de firma generala. -- **Cota TVA lipsa / articol fara politica de pret** — nu sunt verificari de stoc, dar sunt cele mai - frecvente erori vecine (`FACT-024` la `contabilizeaza_articol`, `PACK:7278-7302`; `FACT-018`, - `FACT-012`, `FACT-013` la cautarea cotei TVA in `adauga_articol_factura`, deja semnalate in - `plan_13_unificare_formular_facturare.md` §G). O proforma cu `id_gestiune=-1000` **nu trece deloc** - prin verificarile de stoc de mai sus (FACT-007/008/009/010/011/014/019/020/021), pentru ca nu ajunge - la `descarca_gestiune` — dar tot trece prin verificarea de politica de pret / cota TVA, care nu are - legatura cu stocul. - -## 6. Consecinte pentru S5b — constrangeri de proiectare - -1. **Formularul unificat trebuie sa reproduca exact mecanismul `gestionabil=0` la incarcarea - liniilor cand `poDate.eProforma = 1`** — nu doar sa ascunda vizual plafonul. Fara acest pas, - articolele gestionabile ar intra pe document cu `id_gestiune` real si ar declansa - `descarca_gestiune` la emitere, contrazicand comportamentul de azi si cerinta de business - ("proforma merge fara stoc"). -2. **Nu se apeleaza `do_alege_stoc` (dialogul de alegere lot) pentru liniile unei proforme.** Traseul - corect e cel al articolului negestionabil (`frm_articol_factura`/`do_initializeaza_articol`), care - garanteaza `id_gestiune = -1000` pe linie. -3. **`id_gestiune = -1000` trebuie sa ajunga efectiv in parametrul `V_ID_GESTIUNE` trimis catre - `pack_facturare.adauga_articol_factura`** — verificarea de gate e strict pe aceasta valoare - (`<> -1000`), nu pe un flag de document. Daca formularul unificat schimba felul in care - construieste liniile (de ex. reutilizeaza un obiect de linie comun facturii si proformei), acest - `-1000` trebuie sa fie explicit setat pe ramura `eProforma=1`, nu mostenit implicit. -4. **`in_stoc`/`gestionabil` trimis de VFP nu e suficient de la sine** — pe unele surse (comanda, - aviz, contract cu pret de contract) Oracle il **rescrie** din nomenclator/contract (§2.3). Gate-ul - real e `id_gestiune`, deci formularul unificat nu poate conta pe faptul ca a trimis `in_stoc=0`; - trebuie sa garanteze `id_gestiune=-1000`. -5. **La copiere proforma -> factura, comportamentul trebuie sa fie opus**: liniile trebuie sa-si - recapete gestionabilitatea reala (`B.IN_STOC` din nomenclator), nu sa ramana blocate la - negestionabil. Decizia deja luata in plan ("degradarea de tip din `do_copiaza` ramane pentru - copiere si nu se aplica la regenerare") e consistenta cu asta, dar merita spus explicit: **regula - `eProforma=1 -> gestionabil=0` se aplica doar la incarcarea/compunerea unei proforme noi, nu si la - copierea din ea** — documentul nou pleaca cu `eProforma=0` (§3-4) si trebuie sa lase Oracle sa - recalculeze `GESTIONABIL` normal. -6. **Formularul unificat nu trebuie sa implementeze nicio logica de "eliberare stoc rezervat" la - trecerea proforma -> factura** — nu exista rezervare de stoc pe proforma (§4), deci nu exista - nimic de eliberat. Riscul tehnic real ramane cel deja semnalat in plan §G (ordinea - stergere-inaintea-reemiterii in aceeasi tranzactie la regenerare), care e independent de proforma. -7. **Verificarile de stoc (`FACT-007/008/009/010/011/014/019/020/021`) nu se vor manifesta niciodata - pentru o linie de proforma** cata vreme regula #1-#3 e respectata — formularul unificat nu are - nevoie de tratament special pentru aceste coduri de eroare pe ramura proforma (nu pot aparea - acolo), dar tot trebuie sa trateze erorile de politica de pret/TVA (`FACT-012/013/018/024`), care - raman valabile si pe proforma. - -## 7. Copierea proformei in formularul unificat — ce lipseste - -Plecand de la `proforma_copiere_puncte_intrare.md` §2 (traseul general de copiere, deja complet -documentat) si de la S5b din plan (`plan_13_unificare_formular_facturare.md:1674-1689`): - -- **Ce exista deja si se reutilizeaza neschimbat**: `do_copiaza` (degradarea de tip), - `copiere_factura`, `factureaza(tip, toFactura)`, `completeaza_setari_document`, - `cursor_retur_document` cu `V_COPIERE=1`. Niciunul din aceste puncte de intrare nu are legatura - speciala cu proforma dincolo de parametrul `V_PROFORMA` deja tratat corect (§4) — nu trebuie - adaugat nimic nou aici pentru ca formularul unificat sa suporte copierea unei proforme. -- **Ce lipseste, specific formularului unificat, nu copierii in sine**: mecanismul `crsarticole` - intreg (incarcarea de masa + `UPDATE ... SET gestionabil=0`, §2.1) presupune un cursor complet - incarcat inainte de afisare. Planul (`plan_13_unificare_formular_facturare.md:1470`) stabileste deja - ca `crsarticole` **nu se mai incarca in masa** in formularul unificat — deci pasul "seteaza - gestionabil=0 pe toate liniile cand eProforma=1" **trebuie reimplementat linie-cu-linie**, la - momentul in care fiecare linie e adaugata pe formularul unificat (fie la copiere, fie la compunere - noua), nu ca un singur `UPDATE` de masa pe un cursor care nu mai exista. Constrangerea #1-#3 de mai - sus (sectiunea 6) e exact specificatia acestui pas lipsa. -- **Combo-ul de tip document si realocarea de serie** — deja acoperite de decizia S5b din plan - (`Ct_clb_fdoc` ramane, realocare la comutare); nu am gasit nimic suplimentar de adaugat aici fata - de ce e deja scris in plan. - -## Ce nu s-a putut stabili - -1. **Linia exacta unde `poArt.id_gestiune` devine efectiv `-1000` pentru o linie de proforma**, in - loc de a ramane la valoarea implicita de camp (`0`, cursorul `crsfactura` creat cu `id_gestiune - N(20)` fara `NULL`, iar `prelucreaza_facturacrs` nu include `id_gestiune` in lista sa de INSERT — - `COMUN\programe\ofacturare_comun.prg:1801-1810`). Am gasit sentinela `-1000` setata **conditionat** - ("daca proprietatea lipseste") in `do_initializeaza_articol` - (`COMUN\clase\ofacturare.vc2:13618-13623`), dar nu am confirmat ca proprietatea chiar "lipseste" - (`Type = 'U'`) in momentul in care `frm_articol_factura` proceseaza o linie de proforma provenita - din `Scatter` pe `crsfactura`. **Argument indirect, nu dovada directa**: daca `id_gestiune` ar - ajunge `0` (nu `-1000`) la server, `descarca_gestiune` ar cauta `NOM_GESTIUNI WHERE ID_GESTIUNE=0` - si ar arunca `FACT-007` la fiecare emitere de proforma cu articol gestionabil din comanda/aviz — - eroare care ar fi vizibila si raportata de ani (proforma exista din 2014+ in changelog); absenta - oricarei asemenea raportari sustine indirect ca sentinela `-1000` chiar ajunge la server, dar nu e - o dovada pe cod. -2. **Nicio verificare pe date vii** — tot ce e mai sus e trasare de cod static (VFP text + PL/SQL - text), nu rulare/log real. Nu am rulat nimic, conform mandatului read-only. -3. **Cautarea de "rezervare de stoc pe proforma"** (§4) s-a facut prin grep text simplu pe - `PACK_FACTURARE` dupa cuvinte cheie (`rezerv`, `PROFORMA`) — nu e o dovada de completitudine - pentru intreaga baza de date Oracle (declanșatoare/triggere pe `VANZARI`/`VANZARI_DETALII`, - proceduri din alte pachete). Zero rezultate nu inseamna cu certitudine ca nu exista niciun - mecanism de rezervare in alta parte a schemei. -4. **Fisierul `docs\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql`** nu a fost creat de mine — am lucrat - pe originalul din `DATABASE\SCRIPTURI_CLAR`. Daca cineva copiaza ulterior fisierul in `docs\` cu - alta numerotare de linii, citatele `PACK:linie` din acest raport trebuie re-verificate pe copia - noua. - -## Verificari recomandate pe date vii (daca raman intrebari) - -- Emis o proforma de test cu un articol **cu adevarat gestionabil si cu stoc real**, sursa = comanda - (ramura unde Oracle rescrie `IN_STOC`, §2.3) — confirma ca `RUL`/`STOC` nu se modifica dupa emitere - si ca `VANZARI_DETALII.ID_GESTIUNE` a fost scris `NULL` pentru acea linie. -- Acelasi test, sursa = lista de preturi (ramura unde Oracle are incredere in `V_IN_STOC_TEMP`) — - pentru comparatie. -- Copiaza proforma de mai sus in factura si confirma ca `VANZARI_DETALII.ID_GESTIUNE` al facturii - rezultate e populat cu o gestiune reala si ca `RUL` inregistreaza descarcarea la emiterea facturii. -- Interogare directa pe schema pentru triggere/joburi legate de `EPROFORMA` sau de rezervare de stoc, - daca exista suspiciunea din punctul 3 de mai sus: - `SELECT trigger_name, table_name FROM user_triggers WHERE table_name IN ('VANZARI','VANZARI_DETALII','STOC','RUL');` diff --git a/docs/cercetare/s5b_proiectare_proforma_copiere.md b/docs/cercetare/s5b_proiectare_proforma_copiere.md deleted file mode 100644 index cc39bd6..0000000 --- a/docs/cercetare/s5b_proiectare_proforma_copiere.md +++ /dev/null @@ -1,610 +0,0 @@ -# Proiectare S5b — proforma si copierea pe formularul unificat - -Cercetare + proiectare READ-ONLY (fara editari de cod, fara `git_sync.ps1`/`txt2vcx.ps1`, fara -commit, fara scriere Oracle — numai `SELECT`), pentru povestea **S5b** din -`docs\plan_13_unificare_formular_facturare.md` (sectiunea `#### S5b`, liniile 2507-2530), deciziile -10 si 11. Continua, fara sa reia, `docs\cercetare\s5b_proforma_descarcare_gestiune.md` (sursa de -adevar pentru mecanismul `gestionabil=0` / `id_gestiune=-1000`) si foloseste rezultatele din -`docs\cercetare\s4e_lista_preturi_pe_sursa.md` (coliziunea `id_c`) si -`docs\cercetare\s4_punct2_registru_cantitate_ramasa.md` (Rol A/B ale `crsarticole`). - -**Status: cercetare + proiectare incheiate.** - -Nu se ating `COMUN\clase\ofacturare_comun.vc2` si `COMUN\programe\ofacturare_editare.prg` -(perimetrul altei sarcini). `COMUN\programe\ofacturare.prg` si `ofacturare_comun.prg` sunt citite, -nu editate — la fel `COMUN\clase\ofacturare.vc2` si `COMUN\clase\ofacturare_comun.vc2` (citire, nu -editare; scriere interzisa doar pe al doilea). - ---- - -## Descoperire centrala, care rescrie premisa de risc a lui S5b - -Sursa de adevar anterioara (`s5b_proforma_descarcare_gestiune.md`) a stabilit ca gate-ul pentru -proforma e sentinela `id_gestiune = -1000`, verificata **in interiorul** lui -`contabilizeaza_articol` (`PACK:7472-7476`). Cercetarea de fata a gasit un strat **mai devreme si -mai tare**: la `Termina`, `do_scrie_factura` **alege intre doua proceduri Oracle diferite** dupa -`poDate.eProforma`, inainte sa se uite la `poDate.tip` (`COMUN\clase\ofacturare.vc2:14282-14300`): - -``` -Do Case - Case poDate.eProforma = 1 - * scrie_proforma are aceiasi parametri ca scrie_factura2 - * salveaza doar in vanzari, nu si in contabilitate - lcSql = [{call pack_facturare.scrie_proforma(...)}] - Case poDate.Tip = 4 - ... lcSql = [{call pack_facturare.scrie_factura_avize(...)}] - Case Inlist(poDate.Tip,3,21,25,28,42,47) - ... lcSql = [{call pack_facturare.scrie_factura2(...)}] - Otherwise - ... lcSql = [{call pack_facturare.scrie_factura2(...)}] -Endcase -``` - -Comentariul din cod ("salveaza doar in vanzari, nu si in contabilitate") e literal adevarat, verificat -pe corpul Oracle: `pack_facturare.scrie_proforma` (`PACK:5637-5671`) cheama **doar** -`scrie_in_vanzari` (`PACK:13488-13760`) — care face `INSERT INTO VANZARI` (antet) **si** -`INSERT INTO VANZARI_DETALII ... SELECT FROM VANZARI_DETALII_TEMP` (liniile, cu `ID_GESTIUNE` asa -cum a ajuns in `TEMP`) — apoi marcheaza `VANZARI.EPROFORMA=1`. **`scrie_proforma` nu cheama -niciodata `contabilizeaza_articol`.** `scrie_factura2`/`scrie_factura_avize`/`scrie_aviz_retur` sunt -singurele trei proceduri care cheama `contabilizeaza_articol` (confirmat si in -`docs\plan_13_unificare_formular_facturare.md:298-310`, runda 11) — si niciuna din ele nu ruleaza -pentru `eProforma=1`. - -**Consecinta:** pentru o proforma, `scrie_nota` (care scrie `NOTE_CONTABILE`) si `descarca_gestiune` -nu ruleaza **deloc** — nu pentru ca sentinela `-1000` le blocheaza pe fiecare linie (desi si asta -ramane adevarat, ca plasa suplimentara), ci pentru ca **intreaga functie care le cheama nu se -executa**. Sentinela `-1000` din `contabilizeaza_articol` conteaza doar cand `contabilizeaza_articol` -chiar ruleaza — adica exact pe drumul invers (vezi sectiunea 5), nu pe proforma insasi. - -**De ce conteaza pentru S5b**: alegerea `scrie_proforma` vs. `scrie_factura2` se face **o singura -data, la Termina**, citind `poDate.eProforma` **in acel moment** — nu depinde de cand/cum a fost -comutat combo-ul, nici de valorile `gestionabil`/`id_gestiune` de pe liniile individuale. Asta e o -veste buna pentru un sens al comutarii (proforma -> ramane proforma la salvare: corect, indiferent ce -au liniile), dar **nu acopera** sensul opus (liniile marcate negestionabil sub proforma, apoi -documentul comutat inapoi la factura inainte de Termina) — acolo `scrie_factura2` **chiar** ruleaza, -si atunci sentinela `-1000` de pe liniile ramase de la proforma **chiar blocheaza** `descarca_gestiune` -pe un document care ar trebui sa descarce. Vezi sectiunea 5. - ---- - -## 1. Fluxul de azi al proformei, cap-coada - -**1.1 Alegerea tipului.** Proforma nu e o valoare in `pack_facturare.ntip` (tipul de business, -1-52) — e un atribut ortogonal, `poDate.nIdTipDoc` (5=FACTURA, 23=PROFORMA, 3=BON FISCAL, -6=AVIZ), setat din combo-ul "Tip document" (`Ct_clb_fdoc._combobox1`, -`RowSource = "FACTURA,PROFORMA,BON FISCAL"`, `ofacturare.vc2:8745-8754`). Setter-ul -`nIdTipDoc_Assign` (`COMUN\programe\ofacturare_comun.prg:593-600`) deriva boolean-ul in acelasi -moment: `This.eProforma = IIF(m.tnIdTipDoc = This.nIdTipDocProforma, 1, 0)`. - -**1.2 Marcarea in masa la incarcarea cursorului.** In `factureaza()` (`COMUN\programe\ofacturare.prg`), -dupa ce cursorul sursa (indiferent care — lista de preturi, comanda, contract, aviz, retur) e adus in -`crsarticole` (Do Case pe `tnTip`, `:266-311`), **daca** `poDate.eProforma = 1`, se face un `UPDATE` -de masa peste tot cursorul (`:330-334`): -``` -* Daca este o proforma, consider toate articolele negestionabile, pentru a nu mai alege din stoc -IF poDate.eProforma = 1 - UPDATE (m.lcCursor) SET gestionabil = 0 - GO TOP IN (m.lcCursor) -ENDIF -``` -Acest pas ruleaza **o singura data**, la incarcare, **inainte** ca formularul de linii sa se -deschida — pentru ca la momentul lui `poDate.eProforma` e deja finala: combo-ul traieste in -`frm_date_factura`/`frm_date_aviz`, un dialog modal care se **inchide** (`ofrmceredate.Show()` la -`:235`, urmat de `Release ofrmceredate` la `:248`) **inainte** ca `Do Case`-ul de incarcare a -cursorului sa ruleze (`:266` e dupa `:248`). Tipul nu se mai poate schimba dupa acest punct, azi. - -**1.3 Ce face `gestionabil=0` la nivel de linie.** Cand operatorul adauga o linie, ramura care alege -dialogul citeste exact acest camp (`ofacturare.vc2:13803-13809`): -``` -Do Case - Case (poArticol.gestionabil = 0 Or gnScadereStoc = 0 Or poDate.tip = 45) - ofrmadarticol = Createobject("frm_articol_factura", lnCantitateMax, .F.) - Otherwise - Thisform.do_alege_stoc(...) -Endcase -``` -`frm_articol_factura` e dialogul generic pentru orice articol negestionabil din toata aplicatia -(nu unul specific proformei) — ocoleste `do_alege_stoc` (alegerea lotului din stoc). -`do_initializeaza_articol` (`ofacturare.vc2:13618-13623`) seteaza `id_gestiune=-1000` **doar daca -proprietatea lipseste** pe obiectul articol (`Type(...) = "U"`): -``` -If Type('toArticol.id_gestiune') = "U" - AddProperty(toArticol,'id_gestiune',-1000) -Endif -``` -La scriere, `poArt.id_gestiune` merge direct ca `V_ID_GESTIUNE` catre -`pack_facturare.adauga_articol_factura` (`ofacturare.vc2:14069-14073`), care il traduce -(`PACK:5032-5034`: `IF V_ID_GESTIUNE <> -1000 THEN V_ID_GESTIUNE2 := V_ID_GESTIUNE; END IF;` — -altfel ramane `NULL`), scris in `VANZARI_DETALII_TEMP.ID_GESTIUNE`. - -**Nota de incertitudine, mostenita din raportul-sursa**: linia exacta unde proprietatea -`id_gestiune` "lipseste" (`Type='U'`) pentru o linie de proforma scatter-uita din `crsfactura` nu a -fost confirmata direct pe date vii — `crsfactura` are `id_gestiune` ca si camp cu valoare implicita, -nu absent. Ramane argument indirect (fara `FACT-007` raportat in productie de ani), nu dovada de -executie. **De verificat cu prioritate la implementare**, pentru ca proiectarea de mai jos (sectiunea -4) muta acest mecanism dintr-un `UPDATE` de masa intr-o marcare per-linie, si trebuie sa stie exact -ce camp seteaza. - -**1.4 Routingul la Termina.** Vezi "Descoperirea centrala" de mai sus: `scrie_proforma`, nu -`scrie_factura2`/`scrie_factura_avize` — fara nota contabila, fara descarcare de gestiune, indiferent -de tipul de business original. - -**1.5 Raportul propriu.** `listeaza_ofacturare` alege raportul dupa `poDate.eProforma` -(`ofacturare.prg:1638-1643`): -``` -Case (Between(poDate.tip, 1, 20) Or Inlist(poDate.tip, -1,-2,-3,-4,-8,-11,44,45,48,49,50,51,52)) And poDate.eProforma = 1 - lcRaport = [PROFORMA] - lcRaportVal = [PROFORMA_VAL] - lcSetare = [PROFORMA] -``` -Independent de forma formularului (unificat sau nu) — declansat de `poDate.eProforma` la momentul -listarii, care e populata corect din `nIdTipDoc_Assign` daca antetul reflecta starea finala. - -**1.6 Garzile `eProforma = 0` pe atasamente (nu pe nota contabila).** Cinci guarde separate in -`listeaza_ofacturare`, toate de forma `poDate.nRelistare = 0 And poDate.eProforma = 0 ...`, controland -salvarea PDF-ului ca atasament arhivat (`export2pdf(..., poDate.cDocAtasate)` si -`poDate.scrieAtasamente()`), **nu** scrierea notei contabile (care e blocata la alt nivel, sectiunea -precedenta): `ofacturare.prg:1960` (factura in lei), `:1996` (aviz retur), `:2052` (factura in -valuta), `:2063` (recapitulatie), `:2103` (`scrieAtasamente()`, arhivarea propriu-zisa). **Corectie -fata de formularea din plan** (`plan_13_unificare_formular_facturare.md:548-549`, "fara nota -contabila si fara atasamente, prin garzile `eProforma=0`"): cele doua efecte sunt reale amandoua, dar -prin **mecanisme diferite** — nota contabila prin routing-ul `scrie_proforma`/`scrie_factura2` -(sectiunea "Descoperire centrala"), atasamentele prin aceste cinci guarde explicite. Nu schimba nimic -pentru proiectare, dar conteaza pentru cine cauta "garda de nota contabila" in cod si nu o gaseste la -liniile citate de plan. - -**1.7 Relistarea pe cale separata.** `frm_facturi.do_listare` (`COMUN\clase\ofacturare_comun.vc2:7233-`) -reconstruieste `poDate` de la zero pentru un document deja emis, cu `poDate.eProforma = 1` setat -explicit (`:7262`) cand se relisteaza o proforma din grid — nu refoloseste obiectul `poDate` din -sesiunea de facturare curenta. - ---- - -## 2. Fluxul de azi al copierii, cap-coada - -**2.1 Degradarea de tip in `do_copiaza`.** `frm_facturi.do_copiaza` -(`COMUN\clase\ofacturare_comun.vc2:3628-3713`) mapeaza **orice** tip de business catre unul din -cinci "simple" — `T1` (lista de preturi), `T10` (lista de preturi valuta), `T5` (invoice), -`T22` (aviz din lista de preturi), sau `T1`/`T10` pentru transfer/altele — pe un `Do Case` explicit -peste `loFactura.tip` (`:3693-3708`). Factura/avizul din contract sau din comanda **nu se copiaza ca -atare** — devine o factura simpla din lista de preturi. Apoi `DO copiere_factura WITH loFactura IN -oproceduri_facturare.prg` (`:3710`). - -**2.2 `copiere_factura` -> `factureaza(tip, toFactura)`.** `copiere_factura` -(`COMUN\programe\oproceduri_facturare.prg:150-153`) cheama `factureaza(loFactura.tip, loFactura)` — -`toFactura` (obiectul scatter-uit din `crsFacturi`) devine parametrul opțional al lui `factureaza` -(`COMUN\programe\ofacturare.prg:81-82`), care seteaza `llCopiere = (Type('toFactura') = 'O')` -(`:111`). - -**2.3 Antetul precompletat, dar `nIdTipDoc` NU se propaga.** -`poDate.completeaza_setari_document(toFactura, .T.)` (`ofacturare.prg:204`, apelat cand `m.llCopiere`) -cheama `oDateFactura::completeaza_setari_document` -(`COMUN\programe\ofacturare_comun.prg:362-412`), ramura `tlFactura=.T.` (`:368-390`): copiaza delegat, -masina, sectie, agent, client, referinta la documentul sursa (`listaid = toDateAnterior.id_vanzare`) — -dar **linia `.nIdTipDoc = toDateAnterior.nIdTipDoc` e comentata** (`:370`, -`*.nIdTipDoc = toDateAnterior.nIdTipDoc`), deliberat. Documentul nou primeste `nIdTipDoc` implicit -dupa tipul degradat (`Do Case` la `ofacturare.prg:187-196`: `tnTip < 21` sau in -`{45,48,49,51,52}` => `5` FACTURA, altfel `6` AVIZ) — pentru tipurile degradate ale copierii (`T1=1`, -`T5=5`, `T10=10`, `T22=22`), toate `<21`, deci **`nIdTipDoc=5` (FACTURA) intotdeauna**, ceea ce prin -`nIdTipDoc_Assign` face **`poDate.eProforma = 0` pe documentul nou, indiferent daca sursa era -proforma**. Confirma direct pe cod ce raportul-sursa dedusese din trasare (`s5b_proforma_descarcare_gestiune.md` §3). - -**2.4 Numar nou, intotdeauna.** `poGeneratorNumere.creeaza_cursor_serii(poDate.nIdTipDoc)` (`:211`) -ruleaza neconditionat, indiferent de copiere — copierea nu mosteneste numarul documentului sursa, -alege intotdeauna un numar nou din seria tipului nou (`FACTURA`). - -**2.5 Cursorul de linii candidate — intotdeauna `cursor_retur_document`.** `Do Case`-ul care alege -cursorul Oracle de incarcat in `crsarticole` (`ofacturare.prg:266-308`) verifica **`Case m.llCopiere` -primul**, inaintea oricarei ramuri pe `tnTip`: -``` -Case m.llCopiere - lcSqlCursor = [{call pack_facturare.cursor_retur_document(?poDate.in_valuta,?poDate.listaid,1,?poDate.eProforma,?gnIdUtil)}] -``` -al treilea parametru pozitional `1` e `V_COPIERE`. Documentul sursa (proforma sau nu) intra prin -`?poDate.listaid` (setat la `toDateAnterior.id_vanzare` in §2.3). `poDate.eProforma` trimis e cel al -**documentului nou** (`0`, per §2.3), nu al sursei. - -**2.6 Gestionabilitatea se restaureaza la copiere.** `cursor_retur_document` -(`PACK_FACTURARE:3949-4000`) calculeaza `GESTIONABIL` cu exact aceasta expresie (`:3993-4000`): -```sql -(case - when V_PROFORMA = 1 then 0 - when V_COPIERE = 1 then B.IN_STOC -- NOM_ARTICOLE.IN_STOC, articolul real - else A.GESTIONABIL - end) AS GESTIONABIL, -``` -Cu `V_PROFORMA=0` (documentul nou nu e proforma) si `V_COPIERE=1`, ramura activa e -`GESTIONABIL = B.IN_STOC` — valoarea reala din nomenclator, **indiferent daca documentul sursa era o -proforma cu toate liniile fortate `gestionabil=0`**. Liniile copiate dintr-o proforma redevin -gestionabile normal (daca articolul chiar e in stoc), trec prin `do_alege_stoc` la adaugare, primesc -`id_gestiune` real, si descarca gestiune normal la emiterea facturii rezultate. - -**2.7 Nimic auto-adaugat.** Comentariu explicit in cod (`ofacturare.prg:97-99`, in ramura -`m.llCopiere` de mai jos in aceeasi functie): -``` -* Nu adaug automat articolele din factura originala, ca sa dau posibilitate de modificare a cantitatilor, pretului, explicatiei -* ofrmdetaliifactura.do_adauga_tot() -``` -Liniile documentului sursa raman doar **candidate** in `crsarticole` (populat de `cursor_retur_document` -la §2.5) — operatorul le adauga manual (sau cu "adauga tot"), la fel ca la orice document nou. - -**2.8 Lista de preturi se adauga peste, doar la copiere.** Imediat dupa, tot in `factureaza()` -(`ofacturare.prg:454-473`, citat integral in `docs\cercetare\s4e_lista_preturi_pe_sursa.md` §1): -`pack_facturare.cursor_preturi(...)` intr-un cursor separat `crsArticoleTemp`, apoi -`SELECT crsArticole / APPEND FROM DBF(lcCursorTemp)` — lipeste lista de preturi completa peste -candidatii din documentul sursa, ca operatorul sa poata adauga si articole noi la copiere/modificare. -Vezi sectiunea 6 pentru siguranta acestui pas. - ---- - -## 3. Combo-ul `Ct_clb_fdoc` si realocarea de serie/numar la comutare - -**Azi, combo-ul traieste exclusiv in dialogul separat `frm_date_factura`/`frm_date_aviz`** -(clasa continuta in acelasi fisier text `ofacturare.vc2`, dar formular distinct de -`frm_facturare_articole`/`frm_facturare_articole2`, care contin gridul de linii). Handler-ul e legat -pe `LostFocus`, nu pe `InteractiveChange`: -``` -PROCEDURE Ct_clb_fdoc._combobox1.LostFocus && ofacturare.vc2:9857-9859 - thisform.do_schimba_tipdoc() -ENDPROC -``` -`do_schimba_tipdoc` (`ofacturare.vc2:9396-9438`), pas cu pas: -1. Determina `lnIdTipDoc` din textul combo-ului (`FACTURA`/`PROFORMA`/`BON FISCAL`/altfel `FACTURA`). -2. Cheama neconditionat `poDate.initializeaza_setari_document(...)` cu un cod special (`-101` bon - fiscal, `-102` proforma, sau `poDate.Tip` altfel) — realoca setarile de document (serie/numar) - pentru noul tip, inainte sa stie daca tipul chiar s-a schimbat. -3. **Iesire timpurie daca tipul nu s-a schimbat**: `IF poDate.nIdTipDoc = m.lnIdTipDoc THEN RETURN`. -4. Daca s-a schimbat: `poDate.nract = 0`, `poDate.serie_act = ""` (curata selectia locala, nesalvata - nicaieri inca), `poGeneratorNumere.dezaloca_numar(poDate.nIdTipDoc)` (**elibereaza in pool numarul - vechi**, rezervat pe tipul vechi — nu se pierde, devine disponibil pentru alt document/alta sesiune; - documentul nu are inca niciun numar scris in baza, fiind pre-Termina), apoi - `poDate.nIdTipDoc = m.lnIdTipDoc` (declanseaza `nIdTipDoc_Assign`, care actualizeaza - `eProforma`/`eBonFiscal`), si `poGeneratorNumere.creeaza_cursor_serii(poDate.nIdTipDoc)` reincarca - seriile disponibile pentru noul tip, populand `clb_serie_act`. -5. **Numarul vechi nu se "reia" automat** — utilizatorul trebuie sa aleaga din nou serie/numar pentru - noul tip din controlul `clb_serie_act`, care s-a reincarcat la pasul 4. - -**Ce NU atinge `do_schimba_tipdoc` azi**: `crsarticole`, `crsfactura`, `gestionabil`, `id_gestiune` — -nimic legat de linii. **Motivul e structural, nu o omisiune**: azi comutarea e posibila **doar** -inaintea oricarei linii, pentru ca `frm_date_factura` (unde traieste combo-ul) e un dialog modal -care se inchide (`Release ofrmceredate`, `ofacturare.prg:248`) **inainte** ca Do Case-ul de incarcare -a cursorului de linii sa ruleze (`:266`) — comutarea si incarcarea liniilor nu pot fi simultane azi. - -**Ce se schimba cand combo-ul traieste in formularul unificat, cu gridul deja prezent**: exact -premisa care dispare — combo-ul devine comutabil **si dupa** ce linii exista deja in `crsfactura`. -`do_schimba_tipdoc` ramane corect pentru partea de serie/numar (pasii 1-5 de mai sus se aplica -identic, indiferent daca gridul are linii sau nu — realocarea de serie e independenta de continutul -documentului). **Ce lipseste e pasul 1.2/1.3 de mai sus (marcarea `gestionabil=0`/`id_gestiune=-1000`), -care azi nu are nevoie sa fie legat de comutare pentru ca nu poate fi comutare cu linii deja -prezente.** Vezi sectiunea 4. - ---- - -## 4. Ce se rupe la unificare - -**4.1 Marcarea negestionabil nu mai are un singur moment de executie.** Azi exista un singur loc -(`ofacturare.prg:330-334`, dupa incarcarea cursorului, inaintea deschiderii gridului) unde -`eProforma=1` implica `gestionabil=0` in masa. In formularul unificat, cu combo-ul viu in acelasi -ecran cu gridul (decizia 10), sunt **trei momente distincte** care trebuie sa garanteze acelasi -invariant, nu unul: - a. **La deschiderea initiala**, daca formularul porneste direct pe tip Proforma (echivalent cu azi - — se poate pastra `UPDATE` de masa pe cursorul candidat, daca inca exista un cursor de masa - pentru sursa respectiva dupa S4/S4e; pe sursele deja mutate pe cautare filtrata/`APPEND BLANK` - — S4e — nu mai exista cursor de masa de actualizat, marcarea trebuie facuta la construirea - fiecarui rand). - b. **La adaugarea unei linii noi cat timp `poDate.eProforma=1`** — deja identificat ca gol de - proiectare in `s5b_proforma_descarcare_gestiune.md` §7 ("trebuie reimplementat linie-cu-linie, - la momentul in care fiecare linie e adaugata"), confirmat aici ca valabil si pentru cazul in - care operatorul a **pornit** documentul ca proforma inainte de a adauga prima linie (nu doar - dupa un switch). - c. **La comutarea combo-ului DUPA ce linii exista deja in `crsfactura`** (cazul explicit cerut de - brief) — scenariu care azi nu poate exista, deci nu are cod de reutilizat. `do_schimba_tipdoc` - trebuie extins (sau o metoda noua chemata din el) sa parcurga liniile deja prezente in - `crsfactura` si sa aplice acelasi tratament ca la 1.2-1.3: `gestionabil=0`, - `id_gestiune=-1000`, pe fiecare linie existenta, **cand comutarea intra pe Proforma**. - *Corolarul necesar, netratat de nicio decizie/raport anterior*: pentru fiecare linie deja - adaugata prin `do_alege_stoc` (gestionabila, cu `id_gestiune` real ales de operator), acea - alegere de gestiune/lot devine irelevanta odata marcata negestionabil — de decis daca se - **pastreaza tacit** valoarea veche (inofensiv, pentru ca `id_gestiune=-1000` o inlocuieste - oricum in parametrul trimis la scriere) sau se **sterge explicit** din obiectul liniei, ca sa nu - induca in eroare un ecran care ar afisa gestiunea aleasa pe o linie acum negestionabila. - -**4.2 Combo-ul viu tot timpul inseamna ca `eProforma` poate flutura de mai multe ori inainte de -Termina.** Azi tipul se alege o singura data, ireversibil (dialogul se inchide). In formularul -unificat, operatorul poate comuta Factura -> Proforma -> Factura de mai multe ori inainte de a apasa -`Termina`. Routing-ul de la Termina (sectiunea "Descoperire centrala") citeste `poDate.eProforma` -**doar la momentul apasarii** — corect pentru starea finala — dar **starea liniilor** (marcate sau nu -negestionabil, dupa istoricul comutarilor) nu se "reseteaza" singura la fiecare comutare, daca 4.1.c -nu implementeaza si drumul invers. Vezi sectiunea 5. - -**4.3 Realocarea de serie/numar (sectiunea 3) ramane corecta ca atare**, dar UX-ul ei (curatarea -`clb_serie_act`, cererea catre operator sa aleaga din nou seria) trebuie sa functioneze si cu gridul -de linii vizibil pe acelasi ecran — nu identificat niciun cod care sa presupuna ca gridul e ascuns in -timpul realocarii; e o verificare vizuala, nu o problema de proiectare gasita in cod. - ---- - -## 5. Drumul invers: proforma comutata inapoi in factura - -**Acesta e riscul cel mai serios gasit in aceasta cercetare, nesemnalat explicit in rapoartele -anterioare.** - -Scenariu: operatorul porneste documentul ca Proforma (sau comuta pe Proforma la un moment dat), -adauga linii — care, daca 4.1 e implementat corect, ajung marcate `gestionabil=0`/`id_gestiune=-1000` -pe `crsfactura`. Apoi, **inainte de Termina**, comuta combo-ul inapoi pe Factura. - -- **`poDate.eProforma` devine `0`** prin `nIdTipDoc_Assign`, corect. -- **La Termina, routing-ul alege `scrie_factura2`/`scrie_factura_avize`** (sectiunea "Descoperire - centrala") — care **chiar cheama** `contabilizeaza_articol` pentru fiecare linie. -- **`contabilizeaza_articol` verifica exact sentinela `id_gestiune <> -1000`** (`PACK:7472-7476`) - inainte de a chema `descarca_gestiune`. Liniile ramase marcate `-1000` de cand documentul era - proforma **nu vor descarca gestiune**, desi documentul final e o factura reala, nu o proforma. -- **Nu exista azi niciun mecanism care sa refaca `gestionabil`/`id_gestiune`** cand tipul revine la - Factura in aceeasi sesiune. Singurul loc din tot codul citit unde gestionabilitatea se - "restaureaza" e `cursor_retur_document` la **copiere** (`GESTIONABIL = B.IN_STOC` cand - `V_COPIERE=1`, sectiunea 2.6) — mecanism legat de incarcarea unui cursor **nou**, la deschiderea - unui document **nou**, nu de o comutare in acelasi document, in aceeasi sesiune, pe linii deja - existente in `crsfactura`. - -**Consecinta pe date**: o factura reala, emisa din formularul unificat dupa un du-te-vino prin -Proforma, poate iesi din sesiune cu stocul nedescarcat pentru articole care ar fi trebuit sa-l -descarce — silentios, fara eroare Oracle (sentinela `-1000` e o valoare valida pentru -`contabilizeaza_articol`, nu declanseaza nicio exceptie). - -**Ce trebuie proiectat, obligatoriu, pentru ca decizia 10 sa fie sigura**: la comutarea **dinspre** -Proforma **catre** orice alt tip, liniile care au fost marcate negestionabil **exclusiv din cauza -proformei** (nu articole real negestionabile in nomenclator) trebuie sa-si recapete -`gestionabil`/`id_gestiune` reale, inainte de a ajunge la `Termina`. Doua variante, niciuna aleasa -inca: - a. **Re-interogare per linie** la comutare (analog cu `cursor_retur_document.GESTIONABIL`, dar - aplicat pe liniile deja din `crsfactura`, nu la incarcarea unui cursor nou) — cere un apel - Oracle (sau citire din nomenclator local, daca exista deja incarcat) per linie afectata, si - re-deschiderea alegerii de gestiune/lot pentru cele gestionabile (echivalentul lui - `do_alege_stoc`, care nu a rulat cand linia a fost adaugata sub Proforma). - b. **Blocarea comutarii inapoi** cand exista deja linii adaugate sub Proforma — cere confirmare - sau refuza tranzitia, obligand operatorul sa stearga liniile si sa le re-adauge sub noul tip. - Mai simplu de implementat, cu cost UX (utilizatorul pierde munca de introducere). - -**Nu e o decizie de proiectare pe care raportul o ia in locul lui Marius** — vezi sectiunea 11, -punctul 1. - ---- - -## 6. `id_c` la copiere — raspuns cu dovada - -**Nu exista coliziune cu efect observabil pe drumul de copiere, azi.** Motivul, verificat direct pe -cod (nu presupus, extras din `s4e_lista_preturi_pe_sursa.md` §1, reconfirmat aici pentru S5b): - -`ofacturare.prg:454-473` face `APPEND FROM` peste `crsArticole`, lipind lista de preturi -(`crsArticoleTemp`, numerotata `id_c` independent, cu `ROWNUM` propriu Oracle) peste continutul deja -prezent (documentul copiat, incarcat la §2.5 din `cursor_retur_document`, cu propriul `id_c` -independent). Cele doua seturi de `id_c` **se pot suprapune la nivel de valoare** — asta e adevarat, -tehnic. Dar coliziunea **nu are efect**, pentru doua motive independente, ambele verificate: - -1. **`Case m.llCopiere` e prima ramura verificata** in Do Case-ul de incarcare a cursorului - (`ofacturare.prg:266-268`) — pentru orice document copiat, cursorul de baza e **intotdeauna** - `cursor_retur_document`, indiferent de tipul original. Un document copiat nu (mai) e niciodata de - tip `3` (comanda) dupa degradarea din `do_copiaza` (sectiunea 2.1: degradare catre - `1,5,7,10,22,23` — **CORECTIE runda 13**, cifra veche `1,5,10,22` era gresita; setul real e citit - din primul `CASE`, `ofacturare_comun.vc2:3693-3694`) — - deci **nu poate ajunge** in ramura `Inlist(poDate.Tip,3,21,25,28,42,47)` a lui `do_scrie_factura` - (`ofacturare.vc2:14332-14338`), care e singurul loc unde `id_c` conteaza pentru "cantitate - ramasa" (Rol A, per `s4_punct2_registru_cantitate_ramasa.md`). -2. **`do_sterge` potriveste pe `id_c`** (`ofacturare.vc2:14652-14655`), dar aceasta potrivire conteaza - doar daca ceva citeste `crsarticole` ca registru dupa aceea — ceea ce nu se intampla pe tipurile - rezultate din copiere (`1,5,7,10,22,23`, toate in ramura `Otherwise` a Do Case-ului de scriere, fara - `Calculate Sum(cantitate)`). - -**Concluzie, cu aceeasi rezerva ca in raportul-sursa**: coliziunea de `id_c` exista **tehnic** in -datele din `crsArticole` dupa copiere, dar **nu are cale prin care sa produca un efect gresit**, atata -timp cat tipul rezultat din copiere ramane in setul degradat (`1,5,7,10,22,23` — niciodata -`3,21,25,28,42,47`). -**Aceasta concluzie ramane valabila neschimbata si in formularul unificat**, pentru ca nu depinde de -forma formularului — depinde doar de faptul ca `do_copiaza` degradeaza tipul inaintea oricarei -incarcari de cursor, mecanism care nu se propune sa fie schimbat de S5b (sectiunea 7). - -**O singura conditie de pastrat, explicit**: daca vreo poveste viitoare schimba `do_copiaza` sa NU -mai degradeze tipul catre grupul "simplu" (de exemplu, sa permita copierea unei comenzi ca o comanda -noua, nu ca factura din lista de preturi), concluzia de mai sus **trebuie re-verificata** — riscul de -coliziune `id_c` descris in `s4e_lista_preturi_pe_sursa.md` verdict/punctul 1 ar deveni real. - ---- - -## 7. Ce NU se atinge — lista explicita - -Confirmate ca deja corecte si suficiente, fara nicio schimbare necesara pentru S5b: - -1. **Sentinela `id_gestiune = -1000`** in `contabilizeaza_articol` (`PACK:7472-7476`) — ramane gate-ul - de aparare pentru orice linie care ajunge totusi la contabilizare cu aceasta valoare (drumul - invers, sectiunea 5, o foloseste ca sa NU descarce gestiune — ceea ce e exact problema de acolo, - nu ceva de "reparat" in sentinela insasi). -2. **Routing-ul `scrie_proforma` vs. `scrie_factura2`/`scrie_factura_avize`** - (`ofacturare.vc2:14282-14300`) — corect prin constructie, citeste `poDate.eProforma` la momentul - potrivit (Termina), nu are nevoie de nicio schimbare. -3. **Cele cinci garzi `eProforma=0` pe atasamente** (`ofacturare.prg:1960,1996,2052,2063,2103`) — - raman neschimbate, nu au legatura cu formularul (grid vs. dialog separat), doar cu momentul - listarii/salvarii PDF, care ramane acelasi apel indiferent de forma UI. -4. **Raportul propriu** (`PROFORMA`/`PROFORMA_VAL`, `ofacturare.prg:1638-1643`) — alegerea ramane pe - `poDate.eProforma`, neschimbata. -5. **`do_copiaza` (degradarea de tip)** — ramane exact cum e, decizia S5b (D) o cere explicit - ("degradarea de tip din `do_copiaza` ramane pentru copiere si nu se aplica la regenerare"). -6. **`copiere_factura` -> `factureaza(tip, toFactura)`** — punctul de intrare ramane neschimbat. -7. **`completeaza_setari_document`** — comentariul de la `:370` (`nIdTipDoc` necopiat) e deliberat si - ramane corect: documentul nou pleaca mereu ca Factura, indiferent de sursa, exact ce cere decizia - S5b (copierea deschide "document nou", cu antetul editabil, nu proforma mostenita). -8. **`cursor_retur_document` cu `V_COPIERE=1`** (`PACK:3949-4000`) — restaurarea `GESTIONABIL = B.IN_STOC` - la copiere ramane corecta si suficienta pentru cazul "copiere", fara nicio schimbare (nu e acelasi - mecanism cu comutarea in-sesiune de la sectiunea 5, care ramane un gol real). -9. **Numarul alocat la copiere e intotdeauna nou** (`ofacturare.prg:208,211`, neconditionat de - `llCopiere`) — nimic de schimbat. -10. **Alocarea de serie/numar a lui `do_schimba_tipdoc`** (sectiunea 3) — mecanismul de dealocare + - realocare ramane corect si reutilizabil ca atare in formularul unificat; doar absenta lui legata - de linii (sectiunile 4-5) e golul de acoperit. - ---- - -## 8. Pasi de implementare, ordonati - -1. **Confirma pe date reale linia exacta unde `id_gestiune` devine `-1000`** pentru o linie de - proforma (incertitudinea de la sectiunea 1.3) — headless, cu un `crsfactura` construit manual din - `Scatter` pe o linie de proforma reala, inainte de orice alta schimbare de cod. - *Gata cand:* se confirma cu certitudine (nu argument indirect) fie linia din - `do_initializeaza_articol`, fie alt punct, unde proprietatea `id_gestiune` a liniei devine - efectiv `-1000`. -2. **Extrage marcarea "linie negestionabila pentru proforma" intr-o metoda proprie**, reutilizabila - din trei locuri (sectiunea 4.1: incarcare initiala, adaugare linie noua, comutare combo pe linii - existente) — un singur loc de intretinut pentru regula `eProforma=1 => gestionabil=0, - id_gestiune=-1000`, nu trei copii. - *Gata cand:* toate cele trei puncte de intrare cheama aceeasi metoda, verificat prin grep pe - codul nou. -3. **Leaga metoda de la pasul 2 de `do_schimba_tipdoc`**, pe ramura care duce spre Proforma, aplicata - pe toate liniile deja prezente in `crsfactura` la momentul comutarii (sectiunea 4.1.c). - *Gata cand:* pe un document cu linii deja adaugate ca Factura, comutarea combo-ului pe Proforma - marcheaza `gestionabil=0`/`id_gestiune=-1000` pe fiecare linie existenta, verificabil prin citirea - directa a `crsfactura` dupa comutare. -4. **Proiecteaza si implementeaza drumul invers** (sectiunea 5) — varianta (a) sau (b), decizie a lui - Marius (sectiunea 11, punctul 1). *Gata cand:* pe un document cu linii adaugate sub Proforma, - comutat inapoi pe Factura inainte de Termina, fie liniile isi recapata `gestionabil`/`id_gestiune` - reale (varianta a, verificabil prin `descarca_gestiune` apelat corect la emitere — vezi sectiunea - 9), fie comutarea inapoi e refuzata/necesita confirmare explicita (varianta b). -5. **Verifica riscul de la sectiunea 4.1, corolarul**: decide daca `id_gestiune`/lotul ales anterior pe - o linie acum negestionabila se pastreaza tacit sau se sterge din obiectul liniei — implementeaza - alegerea si documenteaz-o in cod (un rand de comentariu, per conventia proiectului). -6. **Regresie pe copiere**, fara nicio schimbare de cod asteptata (sectiunile 2, 6, 7) — pas de - verificare, nu de implementare: confirma ca formularul unificat, rulat pe drumul de copiere, produce - acelasi rezultat ca azi (sectiunea 9). -7. **Regresie pe proforma simpla** (fara comutare, document pornit direct ca Proforma) — confirma ca - pasii 2-3 nu au schimbat comportamentul cazului deja corect azi. - -*Depinde de:* S5 (acoperirea tipurilor), S4/S4e (forma finala a incarcarii liniilor — pasul 2 trebuie -sa stie daca opereaza pe un cursor de masa sau pe randuri individuale, dupa ce alte povesti decid -asta pentru fiecare sursa). - ---- - -## 9. Cum se verifica - -**Proba ceruta de plan**: 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. - -**Interogari SQL concrete** (numai `SELECT`, rulate cu unealta PowerShell + `sqlplus.exe`, per -mediul de proiect): - -```sql --- 1. Proforma emisa din formularul unificat: fara nota contabila, fara descarcare de gestiune -SELECT id_vanzare, tip, eproforma, sters FROM vanzari WHERE id_vanzare = :ID_TEST; --- eproforma trebuie sa fie 1 - -SELECT COUNT(*) FROM vanzari_detalii WHERE id_vanzare = :ID_TEST; --- trebuie sa coincida cu numarul de linii adaugate (liniile SE scriu, doar nu se contabilizeaza) - -SELECT id_vanzare_det, id_articol, id_gestiune FROM vanzari_detalii WHERE id_vanzare = :ID_TEST; --- id_gestiune trebuie sa fie NULL pe fiecare linie (sentinela -1000 tradusa de adauga_articol_factura) - -SELECT COUNT(*) FROM note_contabile nc - JOIN act a ON a.id_fact = nc.id_fact -- sau echivalentul legaturii act/nota folosite in schema -WHERE a.cod = (SELECT cod FROM vanzari WHERE id_vanzare = :ID_TEST); --- trebuie sa fie 0 -- nicio nota contabila generata - --- 2. Fara descarcare de gestiune: RUL nu are miscare noua dupa emiterea proformei -SELECT COUNT(*) FROM rul WHERE dataora >= :MOMENT_EMITERE AND id_articol IN (:articolele_testate); --- trebuie sa fie identic cu inainte de emitere (0 randuri noi legate de acest document) - --- 3. Copie: document nou, numar nou, acelasi continut -SELECT id_vanzare, numar_act, serie_act, eproforma FROM vanzari WHERE id_vanzare = :ID_COPIE; --- eproforma = 0; numar_act/serie_act diferite de documentul sursa - -SELECT vd.id_articol, vd.cantitate, vd.pret, vd.id_gestiune - FROM vanzari_detalii vd WHERE vd.id_vanzare = :ID_COPIE - ORDER BY vd.id_articol; --- comparat linie cu linie cu vanzari_detalii al documentului sursa (:ID_TEST) -- acelasi articol/cantitate/pret; --- id_gestiune insa REAL (nu NULL), daca articolul e gestionabil -- confirma restaurarea de la sectiunea 2.6 - --- 4. Drumul invers (sectiunea 5, dupa ce varianta e implementata): factura emisa dupa un du-te-vino prin Proforma --- descarca gestiune normal, ca orice factura -SELECT id_vanzare, eproforma FROM vanzari WHERE id_vanzare = :ID_TEST_INVERS; --- eproforma = 0 -SELECT id_gestiune FROM vanzari_detalii WHERE id_vanzare = :ID_TEST_INVERS; --- NU mai e NULL pe liniile gestionabile -- confirma ca varianta aleasa la pasul 4 (sectiunea 8) functioneaza -SELECT COUNT(*) FROM rul WHERE dataora >= :MOMENT_EMITERE_INVERS AND id_articol IN (:articolele_testate); --- trebuie sa arate miscare noua -- gestiunea s-a descarcat -``` - -**Protocol**: fiecare interogare rulata **inainte si dupa** implementare, pe date de test resetate -identic, per memoria de proiect "zero cazuri in date nu e dovada" — minim un caz per scenariu descris -mai sus (proforma simpla, proforma cu switch la comutare, copiere din proforma, drumul invers), -nu doar "a mers o data". - ---- - -## 10. Ce nu se poate testa headless - -- **Comutarea combo-ului `Ct_clb_fdoc` cu gridul de linii vizibil in acelasi ecran** — capcana deja - confirmata pe acest proiect (`grid-coloane-nu-se-materializeaza-headless`, memorie de proiect): - sub `-A -T`, `ColumnCount`/`RecordSource` raman artefacte, comportamentul real al `LostFocus` - pe combo si al reincarcarii `clb_serie_act` cere harnessul UI vizibil. -- **Dialogul `frm_articol_factura` vs. `do_alege_stoc`** (sectiunea 1.3, decizia de dialog dupa - `gestionabil`) — depinde de randare de formular modal, nu apelabil direct fara UI. -- **Verificarea vizuala ca liniile deja adaugate isi schimba starea la comutare** (sectiunea 4.1.c) — - daca implementarea alege sa arate un indicator vizual (de exemplu, o coloana/culoare care marcheaza - "negestionabil"), acel indicator nu se verifica headless; **continutul** `crsfactura` (valorile - `gestionabil`/`id_gestiune`) **se poate** verifica headless, cu apeluri directe la metoda noua din - pasul 2 (sectiunea 8), fara sa deschida formularul. -- **Realocarea vizuala a seriei/numarului in `clb_serie_act`** — control de UI, comportamentul lui de - reincarcare (`do_initializeaza`) nu se verifica fara randare. -- **Ce se poate verifica headless, direct**: continutul `crsfactura`/`poDate` dupa apeluri directe - (nu prin click) la metoda de marcare (pasul 2), la `do_schimba_tipdoc`, si la rutina drumului - invers (pasul 4) — cu date de test pregatite manual (un `crsfactura` cu 2-3 linii, `poDate.eProforma` - comutat programatic inainte si dupa) — confirma `gestionabil`, `id_gestiune`, `poDate.eProforma`, - fara sa deschida formularul. La fel, toate interogarile SQL din sectiunea 9 sunt verificabile - headless (SQL direct, fara UI). - ---- - -## 11. Riscuri si decizii ramase lui Marius - -1. **Drumul invers (sectiunea 5) — varianta (a) sau (b).** Cel mai important punct deschis al acestui - raport: fara o decizie explicita aici, decizia 10 din plan ("proforma foloseste acelasi formular") - ramane incompleta — un document care trece prin Proforma si revine la Factura in aceeasi sesiune - poate iesi cu stocul nedescarcat, silentios. **Recomandare**: varianta (a) (re-interogare si - re-deschidere a alegerii de gestiune la comutarea inapoi), pentru ca varianta (b) (blocarea - comutarii) contrazice explicit decizia 10 ("combo-ul ramane in antetul unificat", implicit - liber de folosit in ambele sensuri) si ar surprinde operatorul cu o pierdere de munca fara - avertisment prealabil la momentul comutarii spre Proforma. -2. **Ce se intampla cu `id_gestiune`/lotul ales anterior pe o linie marcata ulterior negestionabil** - (sectiunea 4.1, corolarul) — pastrare tacita (mai simplu, fara cod nou) sau stergere explicita - (mai clar pentru un ecran viitor care ar afisa gestiunea). **Recomandare**: pastrare tacita — nu - ajunge niciodata la server oricum (sentinela `-1000` trimisa explicit ignora orice valoare veche), - iar stergerea ar cere cod suplimentar fara beneficiu functional, doar cosmetic. -3. **Incertitudinea ramasa din raportul-sursa** (sectiunea 1.3: linia exacta unde `id_gestiune` devine - `-1000`) — necesara inainte de a scrie codul pasului 2 (sectiunea 8), nu doar o curiozitate. - Nu e o decizie de produs, e o verificare tehnica obligatorie inaintea implementarii. -4. **Indicator vizual pentru liniile marcate negestionabil la comutare** (mentionat la sectiunea 10) — - optional, nu cerut explicit de plan; de decis daca merita un semnal in grid (culoare/iconita) sau - ramane invizibil pana la emitere. Nu blocheaza nimic, dar afecteaza UX-ul comutarii repetate. -5. **Testarea pe date reale a `FACT-007`** (mentionata in raportul-sursa ca argument indirect, - nu dovada) — daca Marius vrea certitudine completa inainte de implementare, un test manual pe - mediu de dezvoltare (proforma cu articol gestionabil din comanda, verificare directa - `VANZARI_DETALII.ID_GESTIUNE IS NULL`) inchide definitiv acest gol, in afara perimetrului - read-only al acestei cercetari. - ---- - -## Handoff - -Cercetare + proiectare incheiate intr-o singura sesiune, fara sa fie nevoie de predare de context. -Toate cele 11 sectiuni cerute de brief sunt complete, cu `fisier:linie` verificat direct pe fisierele -text reale (`ofacturare.vc2`, `ofacturare_comun.vc2`, `ofacturare.prg`, `ofacturare_comun.prg` — nu -`.bak`) si pe corpul `PACK_FACTURARE` de pe `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql`. - -**Descoperirea centrala** (sectiunea "Descoperire centrala"): mecanismul care blocheaza nota -contabila pe proforma nu e (doar) sentinela `-1000` din `contabilizeaza_articol`, ci alegerea -`scrie_proforma` (fara contabilizare deloc) vs. `scrie_factura2`/`scrie_factura_avize` (cu -contabilizare) in `do_scrie_factura`, facuta dupa `poDate.eProforma` la Termina. Aceasta descoperire -schimba unde trebuie sa se concentreze grija de proiectare: nu pe "cum ajunge `-1000` la server" -(deja acoperit corect de codul existent), ci pe **ce se intampla cu liniile deja marcate `-1000` cand -documentul nu mai e proforma la Termina** — exact riscul din sectiunea 5, singurul loc unde sentinela -chiar conteaza si azi nu exista nicio plasa. - -Niciun fisier de cod atins, niciun `git_sync.ps1`/`txt2vcx.ps1` rulat, niciun commit, nicio scriere pe -Oracle (doar `SELECT`/`Read`/`Grep` pe fisiere de pe disc). diff --git a/docs/cercetare/s5c_factura_din_proforma.md b/docs/cercetare/s5c_factura_din_proforma.md deleted file mode 100644 index d6e6dcd..0000000 --- a/docs/cercetare/s5c_factura_din_proforma.md +++ /dev/null @@ -1,374 +0,0 @@ -# S5C — Factura din proforma (proiectare) - -Status: INCHEIAT - -## Sarcina -Cerinta noua (decizia 43, runda 12): dintr-o proforma emisa sa se poata genera o -FACTURA ca document NOU (nu prin comutarea tipului pe acelasi document — comutarea -PROFORMA -> FACTURA cu linii e deja blocata cu mesaj). Mecanismul pare sa existe deja -pe calea de copiere (`do_copiaza`). - -## (a) Poate fi azi o proforma aleasa ca sursa de copiere? - -**Da, fara nicio excludere.** Trei dovezi convergente: - -1. `COMUN\clase\ofacturare_comun.vc2:4964-4974` — `frm_facturi.IsCopy(tnTip)`: - ``` - RETURN .T. && POT SA COPIEZ ORICE, TRATEZ TIPURILE IN DO_COPIAZA - *!* RETURN INLIST(m.lnTip, 1,5,7,10,22,23,43) && pot sa copii doar avize si facturi pret de lista, ... - ``` - Varianta veche (restrictiva, comentata) verifica doar `tip` (1-52) — niciodata `eproforma`. Varianta - activa returneaza necondiționat `.T.`. Nu exista, si n-a existat vreodata in acest cod, o excludere - pe `eproforma`. -2. `COMUN\clase\ofacturare_comun.vc2:5110` — vizibilitatea butonului de copiere pe randul selectat: - `Thisform.but_copiaza1.Visible = Thisform.IsCopy(crsFacturi.tip)` — acelasi apel, acelasi rezultat - necondiționat. -3. `COMUN\clase\ofacturare_comun.vc2:5082-5083` — gridul de facturi are un filtru dedicat pe - `eproforma`, cu proforma tratata ca subset normal, nu ascuns: - ``` - "Facturi&Avize\nofiled\E\(eproforma = 0)\" + crlf + ; - "Proforme\nofiled\E\(eproforma=1)\" + crlf + ; - ``` - Chiar filtrul "Proforme" arata explicit ca proformele sunt navigabile si selectabile normal in - acelasi grid — butonul de copiere ramane vizibil identic pe orice rand selectat din acest filtru. - -**Concluzie**: azi orice utilizator poate selecta o proforma in grid si apasa "Copiere (CTRL+K)" -(tooltip-ul insusi anunta explicit acest caz de folosire — vezi citatul din -`s5b_proiectare_proforma_copiere.md` §"1.4"/`proforma_copiere_puncte_intrare.md:41-47`). Nu exista -nicio garda de blocat, la niciun nivel (`IsCopy`, vizibilitate buton, filtru grid). - -## (b) Ce tip rezulta din copierea unei proforme? - -**Rezultatul e intotdeauna `nIdTipDoc=5` (FACTURA)** — corect pentru o factura fiscala — dar tipul de -business (`VANZARI.TIP`, 1-52) degradeaza dupa tabelul deja existent din `do_copiaza`, **nemodificat -pentru cazul proforma**: nu exista nicio ramura speciala pe `eproforma` in `do_copiaza` -(`COMUN\clase\ofacturare_comun.vc2:3628-3713`) — degradarea se face **strict dupa `loFactura.tip`**, -indiferent daca documentul sursa era proforma sau nu. - -**Pasul 1 — degradarea de tip business** (`:3693-3708`, tabel deja confirmat in -`s5b_proiectare_proforma_copiere.md` §2.1): -- Proforma poate exista doar pe tipuri "de factura" (combo-ul `Ct_clb_fdoc` traieste in - `frm_date_factura`, nu si in `frm_date_aviz` — cf. `proforma_copiere_puncte_intrare.md` §1). Setul - posibil de `loFactura.tip` pentru o proforma e deci printre `{1,2,3,4,5,6,7,8,9,10,43,44,45,47,48,49,51}` - (grupul "Facturi" din `COMUN\docs\tipuri_documente_facturare.md`). -- Din acest set: `{1,5,7,10}` raman neschimbate (`CASE INLIST(loFactura.tip,T1,T5,T7,T10,T22,T23)`, - `:3694`); `{2,3,4,8,43,44,45,47,48,49,51}` degradeaza la `T1` (`:3696-3697`); `{6}` la `T10` - (`:3698-3699`); `{9}` la `T5` (`:3700-3701`). -- Deci, pentru orice proforma emisa astazi (indiferent de tipul de business original), documentul - copiat va avea `TIP` in `{1,5,7,10}` — nucleul "lista de preturi" (lei/valuta) sau credit note. - -**Pasul 2 — `nIdTipDoc` NU se propaga** (confirmat deja in -`s5b_proiectare_proforma_copiere.md` §2.3, `COMUN\programe\ofacturare_comun.prg:370`: -`*.nIdTipDoc = toDateAnterior.nIdTipDoc` — comentata). Documentul nou primeste `nIdTipDoc` implicit -dupa tipul degradat: `Do Case` la `COMUN\programe\ofacturare.prg:187-196`, toate valorile din -`{1,5,7,10}` cad sub `5` (FACTURA) → `poDate.eProforma = 0` pe documentul nou, **indiferent ca sursa -era proforma**. - -**Concluzie pentru (b)**: mecanismul de copiere transforma azi orice proforma intr-o **FACTURA reala -(`nIdTipDoc=5`, `eproforma=0`)**, de tip business `1` (lista de preturi, cel mai frecvent caz — orice -tip din `{2,3,4,8,43,44,45,47,48,49,51}` cade tot pe `T1`), `5`/`10` (daca sursa era deja pe lista de -preturi in valuta/factura valuta), sau `7` (credit note, ramas neschimbat). Tipul rezultat **e cel bun -pentru o factura fiscala** din perspectiva `nIdTipDoc`/`eproforma` (nu ramane proforma), dar tipul de -business e intotdeauna **degradat catre "lista de preturi"** — proforma emisa dintr-o comanda (`tip=3`) -sau dintr-un aviz (`tip=4`) devine, dupa copiere, o factura "banala" `tip=1`, **la fel ca la orice alta -copiere non-proforma** (comportament deja documentat si acceptat in S5b §7 punctul 5: "degradarea de -tip din `do_copiaza` ramane pentru copiere si nu se aplica la regenerare" — nicio schimbare ceruta aici -fata de acel raport). - -## (c) Legatura proforma -> factura (VANZARI_CORESP.TIP) - -### Structura tabelei (confirmata pe schema vie, `MARIUSM_AUTO`) -``` -ID_VANZARE_CORESP NUMBER NOT NULL (PK, populat de trigger BEFORE INSERT din SEQ_VANZARI_CORESP) -ID_VANZARE_FACT NUMBER NOT NULL -ID_VANZARE_AVIZ NUMBER NOT NULL (nume generic mostenit — nu neaparat un aviz, vezi mai jos) -STERS NUMBER NULL -TIP NUMBER NOT NULL -``` -Singurul trigger gasit pe tabela (`TRG_VANZARI_CORESP_BEFOINS`, prezent identic in schemele `ACN` si -`MARIUSM_AUTO`) doar genereaza PK-ul din secventa — nu atinge/valideaza `TIP`. - -### Cine scrie in tabela — un singur punct, in tot codul Oracle -`grep` pe `all_source` (schema vie) dupa `VANZARI_CORESP` gaseste **un singur writer**: -`pack_facturare.scrie_corespondente_vanzari` (`PACK_FACTURARE:15481-15516`, singurul `INSERT INTO -VANZARI_CORESP` din toata baza). Nu exista alt pachet, trigger sau produs ROA (ROAGEST, ROAIMOB, -ROACONTRACTE, ROAACNPRO etc.) care sa scrie in aceasta tabela — `PACK_FACTURARE` traieste in `COMUN`, -partajat de toata suita, deci **orice** consumator ar trece prin acelasi punct. - -### Valorile de `TIP` deja folosite — confirmate cod + date -**Cod** (singurele 3 apeluri la `scrie_corespondente_vanzari` din tot `PACK_FACTURARE`, -`:14818-14839`, in `finalizeaza_factura`): -- `TIP=1`: `WHEN pack_facturare.ntip = 4 THEN ... scrie_corespondente_vanzari(1)` — factura scrisa - dintr-un aviz (`ntip=4` = "FACT. DIN AVIZ"). Foloseste `pack_facturare.clistaid_avize` (lista - separata, populata pentru facturare-din-aviz cu selectie multipla). -- `TIP=2`: `WHEN pack_facturare.ntip = 24 THEN ... scrie_corespondente_vanzari(2)` — aviz de retur - (`ntip=24`). -- `TIP=3`: `WHEN pack_facturare.ntip IN (8, 9) THEN ... scrie_corespondente_vanzari(3)` — factura de - retur. -Toate trei folosesc `pack_facturare.nid_vanzare` (documentul curent, tocmai scris) ca -`ID_VANZARE_FACT`; pentru `TIP=1` sursa vine din `clistaid_avize`, pentru `TIP=2`/`TIP=3` (ramura -`ELSE` din `scrie_corespondente_vanzari`, `:15490-15491`) din `pack_facturare.clistaid` generic. - -**Date** (schema `MARIUSM_AUTO`, interogare directa `SELECT tip, COUNT(*) FROM vanzari_coresp GROUP BY -tip`): `TIP=1` → 6 randuri, `TIP=2` → 1, `TIP=3` → 1. **Zero randuri cu alt `TIP`.** Coerent cu codul -(nu exista alt loc care sa scrie alte valori) — dovada dubla (cod + date), nu doar una din ele (per -memoria de proiect "zero cazuri in date nu e dovada": aici avem *si* absenta din date, *si* absenta -structurala in cod, ceea ce e o dovada tare, nu doar un data point izolat). - -### Valoare noua libera -**`TIP=4` e liber**, confirmat pe ambele fronturi (cod: niciun apel existent la -`scrie_corespondente_vanzari(4)`; date: zero randuri `TIP=4` in schema testata). Recomandare: **`TIP=4` -= "factura scrisa dintr-o proforma"**, urmatorul numar disponibil in secventa deja folosita (1,2,3), -fara conflict cu niciun consumator existent (nu exista alt produs ROA care sa scrie sau sa citeasca -`VANZARI_CORESP` in afara de `PACK_FACTURARE`). Structura tabelei **nu se schimba** — doar o noua -valoare de enum in coloana `TIP`, exact cum a cerut misiunea. - -### Unde s-ar scrie corespondenta — punct de intrare, cu rezerva importanta -Tiparul de azi (`TIP=1/2/3`) scrie corespondenta **din interiorul** `finalizeaza_factura`, intr-un -`CASE` cheie **`pack_facturare.ntip`** — semnalul de business-type al documentului nou scris. Aceasta -cheie **nu poate distinge** "factura rezultata dintr-o copiere de proforma" de "orice alta factura -obisnuita cu acelasi tip degradat" (vezi (b): rezultatul copierii unei proforme cade intotdeauna in -`{1,5,7,10}`, niciodata `4`/`24`/`8`/`9` — deci nu exista azi nicio ramura `WHEN` pe care s-o extinda, -si niciuna nu s-ar putea scrie corect doar din `ntip`, pentru ca acelasi `ntip=1` rezulta si dintr-o -copiere obisnuita de factura, nelegata de nicio proforma). Vezi Capcana (i) pentru raspunsul complet la -"de unde stie Oracle ca sursa a fost o proforma" si propunerea de proiectare pentru rezolvare. - -## Capcana (i): supravietuieste id_fact/id_vanzare al sursei pana la scrierea corespondentei? - -**Da, `id_vanzare` al proformei supravietuieste — dovedit pas cu pas — dar faptul ca sursa "era o -proforma" (nu doar "era un document oarecare") NU supravietuieste nicaieri azi. Asta e golul real.** - -**Partea care functioneaza deja, neschimbata:** -1. `do_copiaza` (`COMUN\clase\ofacturare_comun.vc2:3690-3691`): `SELECT crsFacturi / SCATTER NAME - loFactura MEMO` — `loFactura` e o copie completa a randului sursa din grid, **inclusiv - `loFactura.id_vanzare`** (campul cheie) si `loFactura.eproforma` (coloana de grid confirmata la - `ofacturare_comun.vc2:2494`, `Column37.ControlSource = "eproforma"`) — **ambele disponibile in - acest moment**, inainte ca degradarea de tip (`:3693-3708`) sa ruleze. -2. `copiere_factura` (`COMUN\programe\oproceduri_facturare.prg:150-153`) → `factureaza(loFactura.tip, - loFactura)` — `loFactura` intreg (cu `id_vanzare` si `eproforma` inca pe el) devine `toFactura`, - parametrul opțional. -3. `completeaza_setari_document(toDateAnterior, .T.)` - (`COMUN\programe\ofacturare_comun.prg:362-412`), ramura de copiere: **`.listaid = - toDateAnterior.id_vanzare`** (`:387`) — **aici** `id_vanzare` al proformei ajunge in `poDate`, - proprietate care **nu e atinsa de degradarea de tip** (aceea opereaza doar pe `loFactura.tip`, - variabila locala din `do_copiaza`, complet separata de `poDate`). -4. `poDate.listaid` calatoreste neschimbat prin toata sesiunea (folosit si la §2.5 din - `s5b_proiectare_proforma_copiere.md` pentru `cursor_retur_document`), pana la scriere: - `do_scrie_articole` (`COMUN\clase\ofacturare.vc2:6104`): `Alltrim(Nvl(poDate.listaid,''))` e trimis - ca parametru `V_LISTAID` catre `pack_facturare.initializeaza_date_factura`, care il pune in - `pack_facturare.clistaid := V_LISTAID` (`PACK_FACTURARE:1883`) — **inainte** ca `do_scrie_factura` - sa aleaga `scrie_factura2`/`finalizeaza_factura`. La momentul in care `scrie_corespondente_vanzari` - ar rula, `pack_facturare.clistaid` **este deja** `id_vanzare`-ul proformei (ca text), exact valoarea - pe care ramura `ELSE` a lui `scrie_corespondente_vanzari` (`:15490-15491`, folosita azi de `TIP=2` si - `TIP=3`) o citeste. - -**Partea care NU exista azi — golul real:** `completeaza_setari_document` (pasul 3 de mai sus) copiaza -explicit `id_lucrare`, `nrord`, `id_sectie`, `sectie`, `id_agent`, `nume_agent`, `id_delegat`, -`nume_delegat`, `BIdelegat`, `CNPdelegat`, `nrinmat`, `id_masina`, `listaid`, `descriere`, `id_client`, -`nume_client` de pe `toDateAnterior` — **dar niciodata `.eproforma`**. Documentul nou stie "din ce -`id_vanzare` a fost copiat" (`.listaid`), dar **nu stie daca acel document sursa era o proforma sau o -factura obisnuita** — informatia se pierde exact la acest pas, inainte sa ajunga la Oracle. - -**De ce conteaza**: `finalizeaza_factura` (Oracle) decide azi ce `TIP` de corespondenta sa scrie -uitandu-se **doar** la `pack_facturare.ntip` (business-type-ul documentului nou). Cum am stabilit la -(b), rezultatul unei copieri de proforma cade intotdeauna in `{1,5,7,10}` — **acelasi interval** in -care cade si o copiere obisnuita (proforma sau nu). Oracle **nu poate reconstitui** din `ntip` singur -daca documentul curent a fost copiat dintr-o proforma sau dintr-o factura normala — nu exista niciun -semnal in `pack_facturare` care sa poarte aceasta distinctie. - -**Ce trebuie adaugat, minimal** (propunere, nu implementare): -1. **VFP**: la `completeaza_setari_document:387`, langa `.listaid = toDateAnterior.id_vanzare`, adauga - o proprietate noua, de exemplu `.lProformaSursa = (toDateAnterior.eproforma = 1)` — un simplu boolean - client-side, capturat in acelasi moment si din acelasi obiect sursa unde `listaid` e deja capturat - (zero risc suplimentar de "se pierde intre timp", pentru ca foloseste exact acelasi tipar dovedit la - pasul 3-4 de mai sus). -2. **Scrierea corespondentei, NU prin `finalizeaza_factura`/`ntip`**: pentru ca `ntip` nu poate purta - distinctia (vezi mai sus), cea mai sigura ruta e un apel Oracle **explicit, separat**, facut din VFP - imediat dupa ce `do_scrie_factura` confirma succesul scrierii documentului nou, gardat de - `poDate.lProformaSursa`: - ``` - IF poDate.lCopiere AND poDate.lProformaSursa - lcSql = [{call pack_facturare.scrie_corespondente_vanzari(4)}] - * ... goExecutor.oExecute(lcSql) ... - ENDIF - ``` - Aceasta reutilizeaza **exact** procedura Oracle existenta, neschimbata (ramura `ELSE`, - `pack_facturare.clistaid`/`pack_facturare.nid_vanzare` inca valide in sesiune la acel moment, per - pasul 4 de mai sus) — **zero cod PL/SQL nou**, doar un nou punct de apel VFP si o noua valoare de - `TIP`. Alternativa (adaugarea unei ramuri noi in `CASE`-ul din `finalizeaza_factura`, cheie pe un - parametru nou trimis prin `V_PARAMETRU_ADITIONAL` sau similar) ar fi mai fragila: acel `CASE` e - punctul comun al **oricarei** facturi/aviz/retur scrise in tot codul, folosit de toate produsele ROA - prin `PACK_FACTURARE` partajat — o ramura noua acolo, keyed pe o combinatie de `ntip` + un flag nou, - ar creste suprafata unui switch deja incarcat, pentru un caz care oricum nu poate fi exprimat corect - doar din `ntip`. Apelul explicit izolat e mai simplu si nu atinge deloc `finalizeaza_factura`. -3. **Ordinea conteaza**: apelul explicit trebuie sa ruleze **inainte** ca orice alt cod Oracle din - aceeasi sesiune sa resetteze `pack_facturare.nid_vanzare`/`clistaid` (de exemplu, o alta scriere de - document in acelasi batch) — de plasat imediat dupa `do_scrie_factura`, inainte de listare/atasamente - (care oricum nu ating Oracle pentru partea de scriere). Daca se prefera robustete completa (fara - dependenta de stare de sesiune Oracle), varianta alternativa e sa se paseze explicit - `V_ID_VANZARE_FACT` (= `poDate.id_vanzare`, cunoscut client-side dupa scriere) si - `V_ID_VANZARE_PROFORMA` (= `poDate.listaid`, deja cunoscut) direct ca parametri, printr-o mica - procedura Oracle noua care ocoleste `clistaid`/`nid_vanzare` cu totul — cost: un nou obiect PL/SQL in - loc de zero, beneficiu: independenta de ordinea altor apeluri din sesiune. **Alegere ramasa lui - Marius** (vezi propunerea finala). - -## Capcana (ii): `marcheaza_facturat` pe proforma — corect sau nu? - -**Nu trebuie chemat pe proforma. Confirmat cu dovada, pe patru argumente convergente.** - -**Ce face, exact** (`PACK_FACTURARE:15381-15418`): -```sql -PROCEDURE marcheaza_facturat(V_VERIFICARE IN NUMBER) IS -BEGIN - IF V_VERIFICARE = 0 THEN - UPDATE VANZARI SET FACTURAT = 1, ID_UTILFACT = pack_facturare.nid_util - WHERE ID_VANZARE IN (SELECT id_vanzare_aviz FROM vanzari_coresp - WHERE id_vanzare_Fact = pack_facturare.nid_vanzare - AND sters = 0 AND tip <> 3) - AND FACTURAT = 0; - ELSE - -- varianta "verificare cantitate ramasa": marcheaza FACTURAT=1 doar daca toata cantitatea - -- din VANZARI_DETALII a documentului sursa a fost deja consumata in VANZARI_CANTITATI - ... - END IF; -END; -``` -Flipeaza `VANZARI.FACTURAT=1` (+`ID_UTILFACT`) pe **documentul(ele) sursa** legate prin -`VANZARI_CORESP` de documentul tocmai scris — fie neconditionat (`V_VERIFICARE=0`), fie doar cand -cantitatea documentului sursa a fost epuizata (`V_VERIFICARE=1`, mecanismul de **facturare partiala** a -avizelor, bazat pe `VANZARI_CANTITATI`). - -**Argumentul 1 — nu exista un `TIP` cu care sa se cupleze corect azi.** `marcheaza_facturat` se cheama -azi **doar** din `finalizeaza_factura`, in aceleasi doua ramuri `WHEN pack_facturare.ntip = 4` si -`WHEN pack_facturare.ntip = 24` care scriu si corespondenta (`:14823-14831`) — cuplate mereu impreuna cu -`scrie_corespondente_vanzari(1)`/`(2)`, niciodata separat. Cum am stabilit la Capcana (i), calea propusa -pentru `TIP=4` (factura din proforma) e un **apel explicit separat**, in afara acestui `CASE` — deci -n-ar exista niciun loc "natural" unde `marcheaza_facturat` sa se agate fara sa introduca exact acelasi -tip de cod nou-scris ca la corespondenta insasi. - -**Argumentul 2 — mecanismul de "cantitate ramasa" (`V_VERIFICARE=1`) nu are pe ce sa opereze pentru o -proforma.** Verificarea citeste `VANZARI_CANTITATI` (populat **doar** de -`scrie_cantitati_vanzari_avize`, apelata **doar** in ramura `ntip=4` a lui `finalizeaza_factura`). -Proforma nu trece niciodata prin `finalizeaza_factura` (confirmat in -`s5b_proiectare_proforma_copiere.md`, "Descoperire centrala": `scrie_proforma` cheama doar -`scrie_in_vanzari`) — deci **nu exista niciodata randuri `VANZARI_CANTITATI` pentru o proforma**. -Varianta `V_VERIFICARE=1` a lui `marcheaza_facturat` ar gasi mereu "cantitate ramasa = cantitate totala" -(nimic consumat), fie nu ar marca niciodata `FACTURAT=1` (comportament inutil), fie (daca s-ar folosi -gresit `V_VERIFICARE=0`) ar marca neconditionat, ca la punctul urmator. - -**Argumentul 3 — asimetrie la stergere: proforma ar ramane blocata `FACTURAT=1` definitiv.** -`sterge_factura` (`PACK_FACTURARE:5501-5533`) reface `FACTURAT=0` pe documentele sursa **doar** pentru -`V_TIP=24` (aviz retur, `:5502-5510`) si `V_TIP=4` (factura din aviz, `:5525-5533`) — tipurile care azi -chiar cheama `marcheaza_facturat`. Rezultatul copierii unei proforme cade intotdeauna in `{1,5,7,10}` -(per (b)) — **niciuna din aceste valori nu are ramura in `CASE`-ul de stergere**. Daca s-ar chema -`marcheaza_facturat` la scrierea facturii din proforma, iar utilizatorul ar sterge ulterior acea -factura, **proforma sursa ar ramane cu `FACTURAT=1` pentru totdeauna** — o stare orfana, fara niciun -cod care s-o repare, introdusa exact de acest apel. Corespondenta `VANZARI_CORESP` insasi nu are aceeasi -problema (nimic n-o citeste ca sa se strice daca ramane "orfana" dupa stergerea facturii — cel mult -devine o legatura catre un document sters, inofensiv). - -**Argumentul 4 — nimic nu filtreaza azi proformele dupa `FACTURAT`, deci n-ar exista niciun beneficiu -de blocat.** Spre deosebire de avize (`cursor_avize`/candidatii de facturat, filtrati implicit prin -fluxul dedicat "Factura din aviz") si comenzi (`VCOMENZI.FACTURAT=0`, folosit explicit in -`cauta_date_comanda`/`cauta_date_comanda_gest`, `PACK_FACTURARE:15659,15685`, ca sa nu ofere din nou o -comanda deja facturata), **nimic in codul citit** filtreaza dupa `FACTURAT` cand se alege o proforma ca -sursa de copiere — confirmat la (a): `IsCopy`/vizibilitatea butonului/filtrul de grid nu se uita -niciodata la `facturat`. Marcarea n-ar preveni nicio re-copiere accidentala a aceleiasi proforme (care -oricum ramane posibila, vezi propunerea finala, punctul de decizie 2). - -**Concluzie**: `marcheaza_facturat` **nu trebuie chemat** pentru "factura din proforma". Se scrie -**doar** corespondenta (`VANZARI_CORESP`, `TIP=4`), fara actualizare de `VANZARI.FACTURAT` pe proforma. -Confirma explicit suspiciunea din misiune ("proforma nu e document de livrare") cu evidenta concreta: -proforma n-are urma in `VANZARI_CANTITATI` (Argumentul 2), n-are ramura de reversare la stergere -(Argumentul 3), si n-are niciun consumator care sa citeasca `FACTURAT` pe ea (Argumentul 4). - -## Propunere de proiectare - -Mecanismul de baza **ramane copierea existenta** (`do_copiaza`/`copiere_factura`/`factureaza`, -neschimbata in structura ei) — cerinta "document nou, nu comutare pe acelasi document" e deja -satisfacuta de calea de azi (confirmat la (a)/(b)). Ce lipseste e strict **legatura persistata** -proforma → factura si excluderea explicita a lui `marcheaza_facturat`. Pasi, in ordine: - -1. **Captureaza `eproforma` al sursei la copiere.** In `completeaza_setari_document` - (`COMUN\programe\ofacturare_comun.prg:387`, ramura `tlFactura=.T.`), langa - `.listaid = toDateAnterior.id_vanzare`, adauga o proprietate noua pe `poDate` - (ex. `.lProformaSursa`), citind `toDateAnterior.eproforma` — singurul loc unde informatia mai e - disponibila, inainte sa se piarda (Capcana (i)). *Gata cand:* dupa copierea unei proforme, - `poDate.lProformaSursa = .T.`; dupa copierea oricarui alt document, `.F.`. -2. **Rezerva `TIP=4`** in `VANZARI_CORESP` pentru "factura scrisa dintr-o proforma" — doar o conventie - documentata (ca la 1/2/3), zero schimbare de schema (confirmat la (c): tabela ramane - `ID_VANZARE_FACT, ID_VANZARE_AVIZ, TIP, STERS`). -3. **Scrie corespondenta cu un apel Oracle explicit, separat de `finalizeaza_factura`.** Imediat dupa - ce `do_scrie_factura` confirma succesul (acelasi punct unde azi se decid listarea/atasamentele), - daca `poDate.lCopiere AND poDate.lProformaSursa`: `{call - pack_facturare.scrie_corespondente_vanzari(4)}` — **reutilizeaza procedura Oracle existenta, - neschimbata** (ramura `ELSE`, deja scrisa pentru `TIP=2`/`3`). Zero cod PL/SQL nou pentru scriere. - *Alternativa mai robusta, cu cost:* o mica procedura Oracle noua care primeste explicit - `V_ID_VANZARE_FACT`/`V_ID_VANZARE_PROFORMA` ca parametri (nu se bazeaza pe - `pack_facturare.clistaid`/`nid_vanzare` inca valide in sesiune) — de ales intre simplitate (varianta - de mai sus) si robustete fata de ordinea apelurilor (varianta cu parametri expliciti); vezi punctul - de decizie 1 mai jos. -4. **NU cheama `marcheaza_facturat`.** Confirmat cu 4 argumente independente la Capcana (ii) — proforma - nu are `VANZARI_CANTITATI`, nu are ramura de reversare la stergere, nimic n-o filtreaza dupa - `FACTURAT`, si oricum n-ar exista un `WHEN pack_facturare.ntip=...` natural de unde s-o cheme (calea - aleasa la pasul 3 e explicit separata de `finalizeaza_factura`). -5. **Afisare optionala "provine din proforma X"** pe factura noua — se poate construi din - `VANZARI_CORESP` (`TIP=4`) la fel ca tiparul deja folosit pentru retur (`legatura_linie_retur.md` - §5): la nivel de **document**, nu de linie (nicio schimbare fata de tiparul deja acceptat pentru - retur — nu exista niciun tabel de legatura la nivel de linie in tot codul citit, per acelasi raport). - Nu e cerut explicit de misiune, dar e disponibil "gratis" din legatura scrisa la pasul 3. - -**Ce NU se schimba** (confirmat, fara nevoie de atingere): -- `IsCopy`, vizibilitatea `But_copiaza1`, filtrul de grid "Facturi&Avize / Proforme" — proforma ramane - selectabila ca sursa exact ca azi (a). -- Degradarea de tip din `do_copiaza` (`{1,5,7,10}` pentru orice proforma) si alocarea de numar nou — - raman neschimbate, deja produc `nIdTipDoc=5`/`eproforma=0` corect pentru documentul nou (b). -- `cursor_retur_document`, restaurarea `GESTIONABIL=B.IN_STOC` la copiere — neschimbat, mecanism deja - corect pentru orice copiere (mostenit din `s5b_proiectare_proforma_copiere.md` §7). -- `scrie_corespondente_vanzari` insasi — zero cod PL/SQL nou (varianta recomandata la pasul 3). - -### Riscuri si ce ramane de decis de Marius - -1. **Apel explicit pe stare de sesiune (`clistaid`/`nid_vanzare`) vs. procedura noua cu parametri - expliciti** (pasul 3) — simplitate (zero cod Oracle nou, dar depinde ca nimic altceva sa nu resetteze - starea pachetului intre scrierea facturii si apelul de corespondenta) vs. robustete (un obiect - PL/SQL nou, insensibil la ordine). Recomandare: varianta simpla, **daca** apelul se plaseaza imediat - dupa `do_scrie_factura`, inainte de orice alt apel Oracle din acelasi flux (listare/atasamente nu - ating Oracle pentru scriere) — de verificat pe cod exact la implementare ca nu exista un apel Oracle - intercalat care ar reseta `pack_facturare.nid_vanzare`. -2. **Poate fi copiata de mai multe ori aceeasi proforma?** Azi nu exista nicio garda (FACTURAT nu se - marcheaza — decizia de la Capcana (ii)), deci un utilizator poate genera N facturi din aceeasi - proforma, fiecare cu propriul rand `TIP=4` in `VANZARI_CORESP` catre aceeasi proforma sursa. E - comportamentul implicit al oricarei copieri azi (nimic n-o limiteaza nici pentru facturi/avize - normale) — de confirmat daca e acceptabil sau daca se doreste un avertisment (nu o blocare, pentru - ca ar contrazice tiparul existent de copiere liber-repetabila). -3. **Guard simetric la stergere?** `sterge_factura` blocheaza azi stergerea unui aviz/unei facturi care - are deja o factura de retur legata (`TIP=3`) sau facturi/avize-retur legate (`TIP IN (1,2)`, - `PACK_FACTURARE:5450-5476`). Nu exista cerinta explicita in misiune pentru un guard simetric pe - `TIP=4` (ex. "nu poti sterge o proforma care are deja o factura generata din ea") — de decis daca se - doreste, sau daca proforma ramane liber stersa oricand (comportament actual, neschimbat daca nu se - adauga nimic). -4. **Afisarea "provine din proforma X"** (pasul 5) — optionala, nu ceruta explicit; de decis daca - merita implementata acum sau ramane pentru o poveste ulterioara (costul e mic, data fiind legatura - deja scrisa la pasul 3). -5. **`TIP=4` ca valoare aleasa** — libera si fara conflict azi (confirmat cod+date), dar e o alocare - ireversibila din punct de vedere al datelor odata folosita in productie; de confirmat explicit de - Marius inainte de implementare (nu doar acceptata tacit). - -## STARE / CE RAMANE - -**Cercetare + proiectare incheiate.** Toate cele trei intrebari (a/b/c) si ambele capcane (i/ii) au -raspuns cu dovada `fisier:linie`, verificat pe fisierele text reale (`ofacturare_comun.vc2`, -`ofacturare.vc2`, `ofacturare_comun.prg`, `ofacturare.prg` — nu `.bak`) si pe `PACK_FACTURARE` -(`ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql`), plus verificare directa pe schema Oracle vie -(`MARIUSM_AUTO`: structura `VANZARI_CORESP`, distributia `TIP` din date, singurul trigger existent). - -Descoperirea centrala a acestei cercetari: mecanismul de copiere de azi rezolva deja "document nou" si -"tip corect" (a, b) fara nicio schimbare, dar **pierde tacit** informatia "sursa era o proforma" chiar -in metoda care ar trebui s-o pastreze (`completeaza_setari_document:387`, care copiaza `listaid` dar nu -si `eproforma`) — motiv pentru care legatura `VANZARI_CORESP` nu se poate agata de `CASE`-ul existent -din `finalizeaza_factura` (cheie doar pe `ntip`, care nu poarta aceasta distinctie) si are nevoie de un -semnal nou, client-side, plus un apel Oracle explicit separat (pasii 1 si 3 din propunere). - -Niciun fisier de cod atins, niciun `git_sync.ps1`/`txt2vcx.ps1` rulat, niciun commit, nicio scriere pe -Oracle (doar `SELECT`, rulat cu `sqlplus.exe` pe schema `MARIUSM_AUTO`). diff --git a/docs/cercetare/s8_incarcare_document.md b/docs/cercetare/s8_incarcare_document.md deleted file mode 100644 index 2f09502..0000000 --- a/docs/cercetare/s8_incarcare_document.md +++ /dev/null @@ -1,720 +0,0 @@ -# S8 — Proiectare: incarcarea documentului existent in formularul unificat - -Livrabilul povestii **S8** din `docs\plan_13_unificare_formular_facturare.md:2944-2962` (etapa II, -deciziile 5, 8, 9, 19, 35, 50). Cercetare **READ-ONLY** — zero editari de cod, zero write-back, zero -`git_sync.ps1` / `txt2vcx.ps1`, zero commit. Pe Oracle **numai `SELECT`** pe dictionar -(`all_tab_columns` pentru `VANZARI`, `VANZARI_DETALII`, `FACT_VFACTURI`, `FACT_VFACTURI_DETALII`). -`COMUN\clase\ofacturare_comun.vc2` si `COMUN\programe\ofacturare_editare.prg` (perimetrul #6) doar -citite, neatinse. - -**Conventie de marcare**, folosita peste tot in raport: -- **[V]** = verificat direct pe cod / pe dictionarul Oracle, cu `fisier:linie` sau nume de coloana; -- **[D]** = dedus din cod verificat, dar concluzia insasi n-a fost executata / observata; -- **[N]** = nestabilit, cu motivul scris. - ---- - -## 0. Verdict (rezumat) - -Schita din plan — *„`completeaza_setari_document` pentru antet + `cursor_retur_document(V_COPIERE = 1)` -pentru linii, fara degradarea de tip din `do_copiaza`"* — e corecta ca directie, dar **niciuna dintre -cele doua piese nu incarca documentul**, si asta nu e o nuanta de implementare: - -1. **`completeaza_setari_document(toFactura, .T.)` copiaza 16 proprietati din ~120 [V]** - (`COMUN\programe\ofacturare_comun.prg:361-411`). Nu copiaza **niciunul** dintre cei 14 parametri de - identitate/antet ai lui `modifica_date_factura`, in afara de `id_delegat` / `id_masina` / `id_agent`. - 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 — **toate lipsesc**. Sectiunea 1.2 le da pe toate, cu sursa. -2. **`cursor_retur_document(V_COPIERE = 1)` nu umple gridul documentului, ci gridul-sursa.** Pe calea de - copiere, rezultatul lui intra in `crsarticole` (selectorul din stanga), iar `crsfactura` (documentul) - se creeaza **gol** si ramane gol — comentariul e explicit: *„Nu adaug automat articolele din factura - originala, ca sa dau posibilitate de modificare"* (`COMUN\programe\ofacturare.prg:455-457`, - `:338` `creeaza_facturacrs([crsfactura])`) **[V]**. Transferul in document se face **numai** prin - `do_adauga_tot` → `do_adauga_articol`, care pentru articolele gestionabile trece prin - **dialogul de alegere din stoc** (`do_alege_stoc`). Sectiunea 4.3. -3. **`cursor_retur_document` nu intoarce `ID_VANZARE_DET` si nu intoarce `TAXCODE` [V]** - (lista completa de coloane: `PACK_FACTURARE:3949-4054`). Fara `ID_VANZARE_DET`: - - **cheia de linie presupusa de S8b (§1.2 al raportului S8b) nu exista** pe calea propusa; - - **ruta ieftina `modifica_explicatie_articol` devine neapelabila** — primul ei parametru **este** - `V_ID_VANZARE_DET` (`PACK_FACTURARE:14511-14519`) **[V]**. - Fara `TAXCODE`, cerinta explicita a planului („explicatia si `taxcode` pe fiecare linie") nu e - acoperita. - -**Recomandarea centrala a acestei proiectari:** S8 **nu** se construieste pe perechea din schita, ci pe -**precedentul care exista deja si face exact acest lucru** — `relisteaza_ofacturare_stoc` -(`COMUN\programe\ofacturare_stoc.prg:456-742`) **[V]**, care reconstituie `poDate` + cursorul de linii -dintr-un document salvat, prin `FACT_VFACTURI` (antet) + `FACT_VFACTURI_DETALII` (linii), pentru -relistare. `FACT_VFACTURI_DETALII` **are** `ID_VANZARE_DET` si `TAXCODE` **[V]**. Detaliile, -compromisurile si ce ramane totusi de completat — sectiunea 1.3 si 4. - ---- - -## 1. Inventarul complet a ce se incarca, camp cu camp - -### 1.1 Cele doua canale de citire, comparate pe coloane [V] - -Sursa: `PACK_FACTURARE:3949-4054` (`cursor_retur_document`) si `all_tab_columns` pentru -`FACT_VFACTURI_DETALII` / `VANZARI_DETALII`. - -| Ce trebuie pe linie | `cursor_retur_document(V_COPIERE=1)` | `FACT_VFACTURI_DETALII` | Exista pe `VANZARI_DETALII`? | -|---|---|---|---| -| `ID_VANZARE_DET` (cheia liniei) | **NU** | **DA** | DA | -| `TAXCODE` | **NU** | **DA** | DA | -| `EXPLICATIE` | DA | DA | DA | -| `ID_POL` (politica de pret pe linie) | DA | **NU** (doar `NUME_LISTA_PRETURI`) | DA | -| `PRETD` / `ID_VALUTAD` | DA (`PRETD`) | **NU** | DA | -| `GESTIONABIL` (flag calculat) | DA (calculat, vezi 1.4) | **NU** | — (derivat din `ID_GESTIUNE`) | -| `DIFERENTA` | pliata in `PRET` (vezi 1.4) | **NU** | DA | -| `ID_GESTIUNE`, `LOT`, `SERIE`, `CANTITATE`, `PRET`, `PRET_CU_TVA`, `PROC_TVAV`, `ID_JTVA_COLOANA`, `ID_JTVA_COLOANA_EX`, `PRET_ACHIZITIE`, `DISCOUNT_UNITAR`, `ID_VALUTA`, `CURS`, `MULTIPLICATOR` | DA | DA | DA | -| `ID_CTR` / `NUMAR_CONTRACT` | **NU** | **DA** | DA (`ID_CTR`) | - -**Concluzia operationala: niciunul dintre cele doua canale nu e suficient singur [V].** Cele doua -coloane care lipsesc din `cursor_retur_document` (`ID_VANZARE_DET`, `TAXCODE`) exista **amandoua pe -`VANZARI_DETALII`**, deci sunt disponibile fara nicio schimbare de structura — lipseste doar din lista -`SELECT`. - -**Trei variante, cu recomandare:** - -| Varianta | Ce cere | Risc | -|---|---|---| -| **(A)** adaugarea celor doua coloane in `SELECT`-ul lui `cursor_retur_document` | doua randuri in PL/SQL, plus `A1.ID_VANZARE_DET, A1.TAXCODE` in subselectul intern | Procedura e folosita si de **copiere** si (prin `cursor_retur`) de **retur**. Coloanele in plus ajung in `crsarticole` → `Scatter Name poArticol` → si `taxcode` **s-ar gathera** in `crsfactura` (campul exista deja acolo, `creeaza_facturacrs`, `ofacturare_comun.prg:1785`) **[V]**. Adica **copierea ar incepe sa transporte `taxcode`** — probabil o corectie, dar e o **schimbare de comportament pe o cale existenta**, nu una inerta. | -| **(B) procedura noua**, `cursor_editare_document`, clona cu cele doua coloane in plus — **RECOMANDAT** | o procedura noua in `pack_facturare`, zero atingere pe cele existente | Duplicare de SQL (~100 randuri). Costul e intretinerea, nu regresia. Garantie de regresie zero **prin constructie**, in acelasi spirit ca solutia `SET_IDFACT` din S9. | -| **(C)** citire din `FACT_VFACTURI_DETALII` (calea `relisteaza_ofacturare_stoc`) | zero cod Oracle nou | Pierde `ID_POL`, `PRETD`/`ID_VALUTAD`, `GESTIONABIL` si tratamentul `DIFERENTA`/valuta din `cursor_retur_document` — adica exact partea grea. Ar cere refacerea ei in VFP. | - -*Recomandare:* **(B)**, cu `SELECT`-ul copiat identic din `cursor_retur_document` plus -`A1.ID_VANZARE_DET` si `A1.TAXCODE`, si cu ramura `GESTIONABIL` decisa explicit (1.4). - -### 1.2 Antetul — ce se incarca, si de unde [V] - -`poDate` e `oDateFactura` (`COMUN\programe\ofacturare_comun.prg:99-320`). Coloana „sursa" e coloana din -`FACT_VFACTURI` (view-ul din spatele `crsFacturi`, folosit deja de `do_modifica` -`ofacturare_comun.vc2:4560-4568` si de `relisteaza_ofacturare_stoc` `ofacturare_stoc.prg:504-509`), -sau `VANZARI` direct. - -Legenda coloanei „azi": **CSD** = acoperit de `completeaza_setari_document(…, .T.)`; **GOL** = nimeni -nu-l incarca azi pe calea de copiere, deci e **gol de umplut in S8**. - -#### (a) Identitatea documentului — antetul vizibil - -| `poDate.` | Sursa | Azi | Nota | -|---|---|---|---| -| `nIdTipDoc` (FACTURA/PROFORMA/BON) | derivat din `tip` + `eproforma` | **GOL** — linia e **comentata explicit** in CSD (`ofacturare_comun.prg:366`) | vezi 3.2 | -| `tip` | `FACT_VFACTURI.TIP` | **GOL** (si azi **degradat** de `do_copiaza`) | S8 il pastreaza, per plan | -| `eProforma` | `FACT_VFACTURI.EPROFORMA` | **GOL** — `eproforma` nu apare deloc in `ofacturare_comun.prg` (rezultatul 1 al rundei 13) | | -| `serie_act` | `FACT_VFACTURI.SERIE_ACT` | **GOL** (CSD il pune doar in `.descriere`, ca text) | vezi 4.2 — conflict cu `poGeneratorNumere` | -| `nract` | `FACT_VFACTURI.NUMAR_ACT` | **GOL** (idem) | idem | -| `dataact` | `FACT_VFACTURI.DATA_ACT` | **GOL** (`Init` pune **azi**) | | -| `datascad` | `FACT_VFACTURI.DATA_SCAD` | **GOL** (`Init` calculeaza din `gnZileScadentaFact`) | | -| `zi_curs` | **NU EXISTA COLOANA** — vezi 1.5 | **GOL, si nerecuperabil** | raspuns la intrebarea deschisa din S8b §5.2 | -| `in_valuta` / `id_valuta` / `nume_valuta` | `FACT_VFACTURI.IN_VALUTA` / `ID_VALUTA` / `NUME_VAL` | **GOL** (`Init` deriva `in_valuta` din `tip`) | | -| `Curs` / `multiplicator` | `FACT_VFACTURI.CURS` / `MULTIPLICATOR` | **GOL** | pe linii, din `VANZARI_CURSURI` | - -#### (b) Partener si sursa - -| `poDate.` | Sursa | Azi | -|---|---|---| -| `id_client` / `nume_client` | `ID_PART` / `CLIENT` | **CSD** | -| `cod_fiscal` | `FACT_VFACTURI.COD_FISCAL` | **GOL** | -| `sold_lei` / `sold_valuta` | recalculate la deschidere (derivate, read-only) | **GOL** — se recalculeaza, nu se restaureaza | -| `listaid` (id comanda / contract / lista avize) | vezi 1.6 | **CSD, dar cu valoare GRESITA pentru editare** — CSD scrie `.listaid = toDateAnterior.id_vanzare` (`ofacturare_comun.prg:383`) **[V]** | -| `descriere` (nr. comanda/contract/avize) | `FACT_VFACTURI.ALTELE` / `COMANDA` / `CONTRACT` / `AVIZE` | **CSD, dar cu serie+numarul documentului insusi**, nu al sursei (`:384`) | -| `id_gestiune_init` | `FACT_VFACTURI.ID_GESTIUNE` | **GOL** | -| `id_pol` / `nume_politica` | **NU EXISTA pe `VANZARI`** — doar `VANZARI_DETALII.ID_POL` | **GOL** — vezi 1.5 | -| `id_ctr` / `contract` | `VANZARI.ID_CTR` / `VANZARI.CONTRACT` | **GOL** | - -#### (c) Pliat — analitice (grupul A, read-only in #13, decizia 26) - -| `poDate.` | Sursa | Azi | -|---|---|---| -| `id_sectie` / `sectie` | `FACT_VFACTURI.ID_SECTIE` / `SECTIE` | **CSD** | -| `id_lucrare` / `nrord` | `ID_LUCRARE` / `LUCRARE` | **CSD** | -| `id_responsabil` / `responsabil` | **NU EXISTA pe `VANZARI`** | **GOL, si nerecuperabil din `VANZARI`** — liniile sunt **comentate** in CSD (`:369-370`) **[V]** | -| `id_venchelt` / `venchelt` | **NU EXISTA pe `VANZARI`** | **GOL** — idem, comentate (`:373-374`) **[V]** | - -> Faptul ca `ID_RESPONSABIL` si `ID_VENCHELT` **nu sunt coloane pe `VANZARI`** (verificat pe -> `all_tab_columns`) e **dovada structurala** ca grupul A traieste la nivel de **linie de nota**, nu de -> antet — exact ce spunea `rute_scriere_antet.md`, dar acum cu argument de schema, nu de cautare. **[V]** - -#### (d) Pliat — alte date (`frm_alte_date`) si cei 14 parametri - -| `poDate.` | Sursa | Azi | -|---|---|---| -| `id_delegat` / `nume_delegat` / `BIdelegat` / `CNPdelegat` | `ID_DELEGAT` / `DELEGAT` / `BIDELEGAT` / `CNPDELEGAT` | **CSD** | -| `id_masina` / `nrinmat` | `ID_MASINA` / `NRINMAT` | **CSD** | -| `id_agent` / `nume_agent` | `ID_AGENT` / `NUME_AGENT` | **CSD** | -| `id_facturare` / `adresa_facturare` | `ID_FACTURARE` / `ADRESA_FACTURARE` | **GOL** | -| `dataora_exp` | `DATAORA_EXP` | **GOL** | -| `text_aditional` | `TEXT_ADITIONAL` | **GOL** — plus capcana de la 1.7 | -| `nListareDetaliata` | `LISTARE_DETALIATA` | **GOL** | -| `tip_saft` | `TIP_SAFT` | **GOL** (`Init` pune `380`) | -| `eFactura` | `EFACTURA` | **GOL** (`Init` pune `0`) | -| *`id_ruta`* | `ID_RUTA` | **GOL, si nu exista proprietate pe `oDateFactura`** — vezi 1.5 | -| `afisare_scadenta` | `AFISARE_SCADENTA` | **GOL** (`Init` pune `1`; ROACONTRACTE il recalculeaza) | -| `tva_incasare` | `TVA_INCASARE` | **GOL** (`Init` ia `goCalendar.tva_incasare`) | -| `institutie_publica` | `FACT_VFACTURI.INSTITUTIE_PUBLICA` | **GOL** | -| `nTipFactura` | `TIP_FACTURA` | **GOL** | -| `nIdBeneficiar` | `ID_BENEFICIAR` | **GOL** | -| `id_ordl` | `ID_ORDL` | **GOL** | -| `coeficient_k` | `VANZARI.COEFICIENT_K` | **GOL** | -| grupul C (`ntip_incasare`, `serie_chit`, `nr_incasare`, `incasat`, `id_casa`) | `TIP_INCASAT`, `SERIE_INCASAT`, `NR_INCASAT`, `SUMA_INCASAT` | **GOL** — blocat in etapa I, dar vezi 2.3 | - -#### (e) Discountul de document — nu e in `poDate` - -**[V]** Discountul de document **nu are proprietate pe `oDateFactura`**: traieste in proprietatile -formularului `frm_facturare_articole.ndiscfactron` / `.ndiscfactval` -(`ofacturare.vc2:5286-5287` declaratie `*p:`, `:5317-5318` initializare la `0`, -`:11325`/`:11337` `ControlSource` pe cele doua textbox-uri, `:14272-14274` citirea la scriere). - -| Ce | Sursa | Cum se incarca | -|---|---|---| -| discount de document, lei | `VANZARI.DISCOUNT` (in lei cand `IN_VALUTA=0`) | `Thisform.ndiscfactron` | -| discount de document, valuta | `VANZARI.DISCOUNT` (in valuta cand `IN_VALUTA=1`) | `Thisform.ndiscfactval`, plus `ndiscfactron = Round(ndiscfactval * Curs / multiplicator, …)` | -| `discount_evidentiat` | `VANZARI.DISCOUNT_EVIDENTIAT` | `poDate.discount_evidentiat` (`ControlSource` la `ofacturare.vc2:11305` si `:15968`) | - -**Precedentul exact exista** si trateaza deja bifurcatia lei/valuta: -`ofacturare_stoc.prg:553-561` **[V]** — `lnDiscountVal = discount` cand `in_valuta = 1`, altfel -`lnDiscount = discount`. S8 copiaza acest tipar. - -**Atentie [V]:** `oDateFactura.Init` pune `.discount_evidentiat = IIF(TYPE('gnDiscountEvidentiat')='N', -gnDiscountEvidentiat, 1)` (`ofacturare_comun.prg:238`) — **optiunea de firma, nu valoarea documentului**. -Daca S8 nu suprascrie, un document emis pe alta setare apare cu discountul evidentiat altfel decat a -fost emis, si — pentru ca `discount_evidentiat` **atinge sumele** (e parametru al lui `scrie_factura2`, -`ofacturare.vc2:14358`) — ar declansa **regenerare la simpla deschidere**. Fals pozitiv de acelasi tip -cu lookup-ul delegatului, dar cu efect mai scump. - -### 1.3 Liniile — inventar - -Coloanele intoarse de `cursor_retur_document(V_COPIERE=1)`, in ordinea din `SELECT` -(`PACK_FACTURARE:3963-4022`) **[V]**: `ID_C` (=`ROWNUM`), `ID_ARTICOL`, `LOT`, `SERIE`, `ID_POL`, -`ID_VALUTA`, `DISCOUNT_UNITAR`, `DISCOUNT_UNITAR_VAL`, `CODMAT`, `CODBARE`, `DENUMIRE`, `UM`, -`GESTIONABIL`, `CANTITATE`, `PROC_TVAV`, `ID_JTVA_COLOANA`, `PRETURI_CU_TVA`, `CURS`, `MULTIPLICATOR`, -`PRET`, `PRET_VAL`, `TIP_VALUTA`, `NUME_VAL`, `EXPLICATIE`, `ID_GESTIUNE`, `PRET_ACHIZITIE`, `PRETD`, -`ID_JTVA_COLOANA_EX`. - -**Lipsesc, si sunt necesare pentru S8/S8b:** `ID_VANZARE_DET`, `TAXCODE`, `ID_CTR`, `CODMATC`, -`COD_UM_ISO`, `CONT`. Ultimele trei sunt in `crsfactura` si vin azi pe alte cai -(`do_adauga_articol` cere `codmatf` cu un `SELECT` separat, `ofacturare.vc2:12932-12934` **[V]**). - -### 1.4 Doua capcane in `cursor_retur_document`, ambele verificate direct - -**(i) `GESTIONABIL` cu `V_COPIERE = 1` vine din nomenclatorul CURENT, nu din document [V]** -(`PACK_FACTURARE:3990-3997`): - -``` -(case when V_PROFORMA = 1 then 0 - when V_COPIERE = 1 then B.IN_STOC -- NOM_ARTICOLE, starea de AZI - else A.GESTIONABIL -- NVL2(A1.ID_GESTIUNE,1,0), starea DOCUMENTULUI - end) AS GESTIONABIL -``` - -Pentru **copiere** e corect (documentul nou se emite cu regulile de azi). Pentru **editare** e o -divergenta tacuta: un articol care a fost facturat gestionabil si intre timp a fost trecut pe -`IN_STOC = 0` (sau invers) se incarca cu **alt regim** decat cel cu care e scris in `VANZARI_DETALII`. -Efectul practic: articolul trece pe alta ramura in `do_adauga_articol` (dialog de stoc vs. fara), -deci se schimba si ce se descarca la reemitere. **Recomandare: pe calea de editare se foloseste -`A.GESTIONABIL` (ramura `else`), adica adevarul documentului** — argument suplimentar pentru varianta -(B) din 1.1. **Aceasta e a cincea valoare din rezultatul 6 al handoff-ului (`IN_STOC` din nomenclator), -regasita pe calea de CITIRE, nu doar pe cea de scriere.** - -**(ii) `PRET` vine rotunjit si cu `DIFERENTA` pliata inauntru [V]** (`PACK_FACTURARE:4008-4014`): -`ROUND(...) + A.DIFERENTA`. Confirma rezultatul 6 din handoff. Consecinta directa pentru **S8b**: -comparatia de pret la confirmare trebuie facuta pe **aceasta forma** (rotunjita + `DIFERENTA`), nu pe -`VANZARI_DETALII.PRET` brut — altfel orice document cu `DIFERENTA <> 0` apare modificat la deschidere. - -### 1.5 Trei campuri de antet care nu au unde sa fie salvate — deci nu se pot restaura [V] - -Verificat pe `all_tab_columns` pentru `VANZARI`: - -| Camp | Constatare | Ce inseamna pentru S8 | -|---|---|---| -| **`zi_curs`** | **Nu exista coloana `ZI_CURS` pe `VANZARI`.** Se stocheaza rezultatul (`CURS`, `MULTIPLICATOR`, si `VANZARI_CURSURI` pe linie), nu ziua de la care s-a luat cursul. | **Inchide intrebarea deschisa din S8b §5.2.** Nu e „S8 trebuie sa suprascrie implicitul cu valoarea salvata" — **nu exista valoare salvata**. Se incarca `Curs`/`multiplicator` din document, iar `zi_curs` **se exclude din comparatie** si (recomandat) **se ascunde pe calea de editare**, ca sa nu sugereze o valoare pe care documentul n-o are. Ascunderea are precedent in acelasi `Do Case` (`ofacturare.vc2:9720-9725`, tipurile 8 si 9). | -| **`id_pol`** (politica de preturi, antet) | Nu exista pe `VANZARI`; exista **pe linie** (`VANZARI_DETALII.ID_POL`). | Antetul nu are ce sa afiseze ca „politica documentului". Recomandat: pe editare se afiseaza politica **doar daca e unica pe toate liniile**, altfel gol — si e oricum **blocat** (grupul B, 3.1). | -| **`id_ruta`** | Exista pe `VANZARI` (`ID_RUTA`) si e **unul din cei 14**, dar **`oDateFactura` nu are proprietate `id_ruta`** (verificat pe lista completa de proprietati, `ofacturare_comun.prg:99-218`). Azi valoarea circula prin `poRec` (`Scatter` din `crsfacturi`), nu prin `poDate` (`ofacturare_comun.vc2:4601`). | **Proprietate noua pe `oDateFactura`**, altfel `but_modifica` (S8c) n-are de unde sa citeasca ruta. Gol de umplut, semnalat aici pentru ca S8c il presupune existent. | - -### 1.6 Legatura cu sursa — `id_comanda`, `id_ctr`, lista de avize - -Ce exista, verificat: - -| Sursa | Unde e salvata | Citibila? | -|---|---|---| -| comanda | `VANZARI.ID_COMANDA` (+ `VANZARI.COMANDA`, text) **[V]** | **DA**, direct din `FACT_VFACTURI.ID_COMANDA` | -| contract | `VANZARI.ID_CTR` (+ `VANZARI.CONTRACT`, text) **[V]** | **DA**, direct din `FACT_VFACTURI.ID_CTR` | -| avize | `VANZARI_CORESP(ID_VANZARE_FACT, ID_VANZARE_AVIZ, TIP)` + `VANZARI.AVIZE` (text redundant) **[V]** (`PACK_FACTURARE:15494-15516`) | **DA ca date, NU ca procedura** — vezi mai jos | - -**[V] Nu exista in `pack_facturare` nicio procedura care sa CITEASCA lista sursa a unui document.** -Cele 9 aparitii ale lui `VANZARI_CORESP` in export sunt: 6 in garzile lui `sterge_factura` -(`:5454-5530`), 1 `UPDATE ... STERS = 1` tot acolo (`:5582`), 1 subselect in -`scrie_cantitati_vanzari_avize` (`:6319`), 1 `INSERT` in `scrie_corespondente_vanzari` (`:15494`). -**Citirea e cod nou** — un `SELECT` simplu, dar de scris: - -```sql -SELECT ID_VANZARE_AVIZ FROM VANZARI_CORESP - WHERE ID_VANZARE_FACT = :id AND STERS = 0 AND TIP = 1 -``` - -**Semantica lui `TIP` nu e stabilita in acest raport [N]** — `scrie_corespondente_vanzari(V_TIP)` -comuta doar sursa listei (`clistaid_avize` pentru `TIP = 1`, `clistaid` altfel, `:15485-15493`), iar -valorile efective (`1/2/3`) vin din `CASE`-ul lui `finalizeaza_factura`, necitit aici. Handoff-ul -rundei 13 le da ca `1/2/3` folosite, `4` liber. **De confirmat ramura cu ramura la implementare**, nu -de preluat din raport — e exact tiparul care a produs cifra gresita a rundei 13. - -**Nota de proiectare:** `poDate.listaid` e **suprasolicitat**. Pe calea de copiere/editare el trebuie sa -fie `id_vanzare`-ul documentului (ca `cursor_retur_document` sa-i citeasca liniile), dar pentru -reemitere `pack_facturare.clistaid` / `clistaid_avize` trebuie sa contina **lista sursei originale**. -Sunt **doua valori diferite in acelasi camp**, la momente diferite. Precedentul din -`relisteaza_ofacturare_stoc` arata ca problema e veche si **nerezolvata**: acolo -`poDate.listaid = Iif(poDate.tip = 3, Alltrim(Str(id_comanda)), [0])` **[V]** -(`ofacturare_stoc.prg:531`) — cu ramura de **contract comentata** la `:529`, deci nici acel precedent -nu restaureaza sursa pentru contracte sau avize. **S8 are nevoie de un camp separat** -(propunere: `poDate.cListaSursa` + `poDate.cListaSursaAvize`), nu de o a doua semnificatie a lui -`listaid`. Vezi si sectiunea 6. - -### 1.7 `text_aditional` — trei transformari intre ce se vede si ce se salveaza [V] - -1. **Truncat la 100 de caractere** la scriere: `LEFT(Nvl(poDate.text_aditional,[]),100)` - (`ofacturare.vc2:14356`, identic pe toate ramurile `scrie_*`). -2. **`CR+LF` inlocuit cu `Chr(170)`** imediat inainte: - `poDate.text_aditional = Strtran(poDate.text_aditional, Chr(13)+Chr(10), Chr(170), 1, 100, 1)` - (`:14281`) — **si atribuirea e pe `poDate` insusi**, deci obiectul ramane modificat dupa scriere. -3. Pe calea `modifica_date_factura` **nu exista** nici truncare, nici substitutie - (`ofacturare_comun.vc2:4608` trimite `?poRec.text_aditional` ca parametru legat) **[V]** — - deci **cele doua rute salveaza forme diferite ale aceluiasi camp**. - -**Consecinte pentru S8:** la incarcare, `Chr(170)` trebuie convertit inapoi in `CR+LF` (altfel textul -apare pe un rand cu caractere ciudate), iar comparatia din S8b trebuie facuta pe forma **normalizata** -(vezi 5.2). Fara asta, orice document emis cu text pe mai multe randuri apare „modificat" la -deschidere. **Nesemnalat pana acum in niciun raport.** - ---- - -## 2. Initializarile „pentru document nou" care trebuie sarite la editare - -Cerinta obligatorie din S8b, rezultatul 5, plus inventarul cerut de briefing. - -### 2.1 Lookup-ul „ultimul delegat / ultima masina" — **garda exista deja** [V] - -`frm_alte_date.Init`, `COMUN\clase\ferestre_cere_date.vc2:3105-3208`. Codul real -(`:3110-3136`) — si aici raportul S8b si planul descriu situatia **incomplet**: - -``` -If poDate.eProforma = 0 - If Empty(Nvl(poDate.id_delegat,0)) And Empty(Nvl(poDate.id_masina,0)) && :3111 - poDate.id_delegat = 0 - poDate.id_masina = 0 - ... cauta_date_ultima_factura[_tip](...) && :3118-3124 -``` - -**Lookup-ul e deja conditionat de „ambele goale".** Deci: - -- pentru un document care **are** delegat sau masina salvate, e suficient ca **S8 sa populeze - `poDate.id_delegat` / `id_masina` INAINTE de construirea lui `frm_alte_date`** — lookup-ul nu mai - ruleaza, fara niciun cod nou. `completeaza_setari_document` le pune deja (1.2.d), deci pe calea de - editare conditia e indeplinita **din ordinea de apel**, nu dintr-un semnal; -- **riscul real ramane, dar e mai ingust decat il descrie S8b:** el se manifesta **exact pe documentele - care n-au nici delegat, nici masina salvate** — un caz frecvent (multe facturi se emit fara delegat). - Pentru acelea, lookup-ul umple `poDate` cu ultimul delegat al clientului, si atunci: ori documentul - apare modificat, ori `modifica_date_factura` scrie **un delegat pe care documentul nu l-a avut - niciodata**. Sub `modifica_date_factura` campul se scrie **neconditionat** (`ID_DELEGAT = V_ID_DELEGAT`, - `PACK_FACTURARE:14462`) **[V]**, deci scrierea gresita e certa, nu probabila. - -**Mecanismul propus (minim, si in acord cu tiparul existent):** parametru nou pe `frm_alte_date.Init` -sau — mai ieftin si mai greu de ratat — **o proprietate pe `poDate`**, `poDate.lEditare`, citita in -conditia de la `:3111`: - -``` -If !poDate.lEditare And Empty(Nvl(poDate.id_delegat,0)) And Empty(Nvl(poDate.id_masina,0)) -``` - -Argumentul pentru proprietate pe `poDate` si nu parametru: `frm_alte_date` **nu e singurul** formular -care are initializari de acest tip (2.2), `poDate` e deja obiectul citit de toate, si e acelasi tipar -cu `lCopiere`, care exista deja pe `oDateFactura` (`ofacturare_comun.prg:212`) **[V]**. - -**„Ce se intampla cand documentul chiar n-are delegat salvat":** cu garda de mai sus, `poDate.id_delegat` -ramane `.NULL.` (valoarea implicita din declaratia de clasa), campul se afiseaza gol, iar la salvare -`modifica_date_factura` primeste `NULL` si scrie `NULL` — **identic cu starea din baza**. Adica exact -comportamentul dorit: documentul ramane cum a fost. Fara garda, valoarea devine `0` (atribuirea de la -`:3112-3113`) **si apoi** rezultatul lookup-ului. - -> **Diferenta fata de S8b, spusa explicit:** S8b cerea „sarirea explicita a lookup-ului"; codul real -> arata ca sarirea e **deja implicita** pentru documentele cu delegat, si e **necesara explicit** doar -> pentru cele fara. Concluzia lui S8b (S8 trebuie sa faca ceva) ramane valida; **motivul si perimetrul -> se schimba**, iar asta reduce riscul de la „primul document editat al unui client cu activitate -> recenta" la „primul document **fara delegat** al unui client cu activitate recenta". - -### 2.2 Alte initializari de acelasi tip — inventar cu `fisier:linie` [V] - -Cautate in `oDateFactura.Init`, `frm_alte_date.Init`, `frm_date_factura.Init`, `frm_date_aviz.Init`. - -| # | Loc | Ce face | Efect pe calea de editare | Gravitate | -|---|---|---|---|---| -| 1 | `ofacturare_comun.prg:232-236` | `.Data`, `.dataireg`, `.dataact` = azi (sau ultima zi a lunii de lucru); `.datascad` din `gnZileScadentaFact`; `.zi_curs` = azi | Documentul apare cu **data de azi** si scadenta recalculata | **Mare** — atinge 2 din cei 4 campuri de identitate | -| 2 | `ofacturare_comun.prg:238` | `.discount_evidentiat` = `gnDiscountEvidentiat` (optiune de firma) | Vezi 1.2.e — poate declansa **regenerare** la simpla deschidere | **Mare** | -| 3 | `ofacturare_comun.prg:233` | `.tva_incasare` = `goCalendar.tva_incasare` (starea de azi a firmei) | Un document emis in alt regim se incarca cu regimul curent | Medie | -| 4 | `ofacturare_comun.prg:237` | `.in_valuta = 1` pentru `tnIdSet` / `tnTip` din lista | Derivat din tip, nu din document — coincide de obicei, dar nu prin constructie | Mica | -| 5 | `ofacturare_comun.prg:241-243` | `.initializeaza_politica_pret()` pentru tipurile 23, 30, 41 — apel `actualizeaza_politica_pret` | Politica **curenta**, nu cea a documentului | Medie (tipuri de transfer) | -| 6 | `ofacturare_comun.prg:240` | `.initializeaza_setari_document()` → `actualizeaza_document(tip)` → `id_fdoc` / `fdoc` | Reconfigureaza tipul de document din setarile curente | Medie | -| 7 | `ofacturare_comun.prg:245-283` | ramura **ROACONTRACTE** (`goContract`): suprascrie `id_client`, `nume_client`, `cod_fiscal`, `listaid`, `descriere`, `id_sectie`, `id_responsabil`, `id_valuta`, **si `.datascad = .dataact + lnScadentaIncasare`**, **si `.afisare_scadenta`** | Daca `goContract` exista in sesiune, **suprascrie antetul documentului editat cu datele contractului** | **Mare**, conditionata de context | -| 8 | `ofacturare_comun.prg:288-316` | ramura **ROACOMENZI** (`goComanda`, `tnTip = 3`): idem, `id_client`, `listaid`, `descriere`, `id_sectie`, `sectie` | Idem | **Mare**, conditionata | -| 9 | `ferestre_cere_date.vc2:3177-3179` | `If This.opt_incasat.Value = 2 → poDate.incasat = poDate.totalctva` | Suprascrie suma incasata cu totalul documentului | Medie (grup C, blocat in etapa I) | -| 10 | `ferestre_cere_date.vc2:3166-3171` | `poDate.id_casa = gnid_part_casa` (casa implicita a firmei) | Suprascrie casa documentului | Medie (grup C) | -| 11 | **`ferestre_cere_date.vc2:3183-3185`** | `IF poDate.tip = 2 AND poDate.afisare_scadenta = 0 AND AT([Scadenta la ], poDate.text_aditional) = 0` → **prefixeaza** `text_aditional` cu `„Scadenta la N zile."` | **A doua instanta a exact aceluiasi defect ca lookup-ul delegatului**, si pe un camp din cei 14: modifica `text_aditional` la `Init`, fara actiune a utilizatorului | **Mare** | -| 12 | `ferestre_cere_date.vc2:3150` | `If poDate.incasat <> 0 → Thisform.opt_incasat.Value = 2` — atribuire programatica pe `opt_incasat` | Declanseaza `ProgrammaticChange` → `actualizeaza_tipincasare()` → alocare/dezalocare de numere de chitanta (riscul deja documentat in `s3b_alte_date_analitice.md` §4.3) | Medie | -| 13 | `ofacturare.prg:207-209` | `poGeneratorNumere.ResetNumere()` + `creeaza_cursor_serii(nIdTipDoc)`, la fiecare trecere prin `factureaza` | **Aloca un numar nou** pe calea de emitere | **Mare** — vezi 4.2 | -| 14 | `ofacturare.prg:184-193` | `Do Case` pe `tnTip` care forteaza `poDate.nIdTipDoc` (5 = FACTURA / 6 = AVIZ) **inainte** de `completeaza_setari_document` | Pe editare, `nIdTipDoc` trebuie sa vina din document (proforma = 23, bon = 3), nu din tip | Medie | - -**Nr. 11 merita subliniat**: garda lui (`AT([Scadenta la ], text_aditional) = 0`) il face inert pe un -document de contract emis **cu acelasi numar de zile de scadenta**. Devine activ exact cand -scadenta s-a schimbat de la emitere — adica precis cazul in care textul e cel vechi si trebuie -pastrat. **Metoda `Destroy`/`Unload` face si operatia inversa** (`:3037-3038`: `Strtran(...,[])`), -ceea ce inseamna ca pe calea normala prefixul e adaugat si scos in aceeasi sesiune; pe o cale de -editare care **nu** trece prin acelasi `Unload`, prefixul ar ramane. **[D]** — n-am urmarit toate -caile de iesire ale formularului. - -### 2.3 Regula generala propusa - -> **Orice camp al lui `poDate` populat prin apel Oracle sau dintr-o variabila globala/`go*` in -> `Init`/`Load` e o sugestie pentru document nou, nu o valoare de document.** Pe calea de editare, -> ordinea trebuie sa garanteze ca aceste sugestii **nu ajung sa se execute** (garda), nu doar ca sunt -> **suprascrise dupa** (ceea ce ar merge pentru valori, dar **nu** pentru efectele secundare — nr. 12 -> si nr. 13 aloca numere, nu doar seteaza campuri). - ---- - -## 3. Ce se blocheaza la editare, si de ce - -### 3.1 Blocate — schimbarea lor ar insemna alt document - -| Camp / control | De ce | Ruta care ar lipsi oricum | -|---|---|---| -| **Tipul documentului** (`Ct_clb_fdoc`, `poDate.tip` / `nIdTipDoc`) | Tipul decide ce sursa se consuma, ce nota se scrie si ce garzi se aplica. `do_copiaza` il **degradeaza** tocmai pentru ca un document copiat nu mai poate reconsuma sursa (`ofacturare_comun.vc2:3694-3711`) **[V]**; la editare degradarea e interzisa prin plan, deci tipul e fix. | grupul B — nicio ruta de scriere pe loc | -| **Sursa** (`Ct_clb_altele`) | Vezi 3.2 | grupul B | -| **Clientul** (`Ct_clb_nume_client`) | Schimbarea partenerului rescrie `IREG_PARTENERI`, `ACT`, soldurile — alt document | grupul B | -| **Valuta, `zi_curs`, gestiunea sursa, politica de preturi** | grupul B, decizia 25; in plus `zi_curs` si `id_pol` **n-au valoare salvata** (1.5) | grupul B | -| **Grupul C** (incasare) | decizia 25; in plus `opt_incasat` are efect secundar de alocare (2.2 nr. 12) | grupul C | -| **Grupul A** (venit/cheltuiala, sectie, responsabil, lucrare) | decizia 26 — read-only permanent in #13; in plus doua din patru **nu exista pe `VANZARI`** (1.2.c) | editarea e a lui #6 | - -### 3.2 `Ct_clb_altele` — verificarea ceruta explicit, cu doua corectii la plan [V] - -Planul (`:2952-2955`) spune: *„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."* - -Citit ramura cu ramura din `frm_date_factura.Init` (`ofacturare.vc2:9563-9796`), `Do Case`-ul de la -`:9615-9722`: - -**Corectia 1 — lista de etichete e incompleta.** Intervalul `9633-9643` din plan e corect ca interval, -dar contine **sase** apeluri, nu cinci: `Nr. contract` (`:9633`, tip 2/6/52), `Nr. comanda` (`:9635`, -tip 3), **`Nr. aviz / avize` (`:9637`, tip 4)**, `Nr. factura` (`:9639`, tip 7), `Nr. facturi` -(`:9641`, tip 8/9), `Locatie` (`:9643`, tip 45). **Planul omite exact tipul 4** — cel pentru care -`Ct_clb_altele` tine **lista de avize**, adica singura sursa care trebuie refurnizata lui S9 -(sectiunea 6). Omisiunea nu e cosmetica. - -**Corectia 2 — conditia de eliminare e mai larga decat cea din plan.** `ct_clb_altele` e scos -(`RemoveObject`) in **trei** situatii, nu una: - -| Ramura | Conditie | Ce face | -|---|---|---| -| `:9616-9628` | `gnScadereStoc = 0 And Inlist(poDate.tip, 1, 5, 10, **48, 49**)` | daca `llCopiere` → eticheta `Nr. factura` (`:9623`); altfel **`RemoveObject('ct_clb_altele')`** (`:9627`) | -| `:9651-9660` | **`gnScadereStoc = 1`** `And Inlist(poDate.tip, 1, 5, 10)` | idem (`:9653` / `:9658`) | -| `:9705-9712` (in `Otherwise`) | `Inlist(poDate.tip, 48, 49)` | **`RemoveObject('ct_clb_altele')`** neconditionat | - -Deci: tipurile sunt **1, 5, 10, 48, 49** (nu doar 1/5/10), si eliminarea are loc si cand -**`gnScadereStoc = 1`**, nu doar cand e `0`. Formularea din plan („`gnScadereStoc = 0` si tipul e -1/5/10") descrie **una** din trei ramuri. - -**Consecinta pentru S8, si e in favoarea noastra:** pe ramurile 1 si 2, `llCopiere = .T.` **pastreaza** -controlul si ii pune eticheta `Nr. factura`. Calea de editare, care va avea acelasi semnal ridicat -(`lCopiere` sau `lEditare`), **mosteneste automat pastrarea controlului** — nu e nimic de adaugat, doar -de **blocat**. Pe ramura 3 (tipurile 48/49) controlul e scos neconditionat; **[N]** n-am stabilit daca -tipurile 48/49 intra in perimetrul etapei II. - -### 3.3 Ce ramane editabil - -Cei 14 parametri, prin `but_modifica` (S8c), **plus** liniile (cantitati, preturi, discounturi, -explicatii, adaugari/stergeri) si discountul de document — care declanseaza regenerarea. Nimic din -sectiunea 3.1. - ---- - -## 4. Ordinea reala de incarcare, si de ce conteaza - -### 4.1 Secventa propusa - -``` - 0. S7 a validat deja documentul (garzi) si a stabilit id_vanzare. - 1. Citeste ANTETUL: SELECT ... FROM FACT_VFACTURI WHERE id_vanzare = :id - 2. Citeste LISTA SURSA (avize) inainte de orice altceva: SELECT ... FROM VANZARI_CORESP (§6) - 3. poDate = CreateObject("oDateFactura", 0, 0) <-- tnIdSet = 0 => Init NU ruleaza (§4.2) - 4. poDate.lEditare = .T. <-- semnalul, inainte de orice formular - 5. Populeaza poDate din antet: cei 14 + tip + eproforma + valuta/curs + - discount_evidentiat + client + gestiune + sursa (cListaSursa / cListaSursaAvize) - 6. Populeaza thisform.ndiscfactron / ndiscfactval din VANZARI.DISCOUNT - 7. Citeste LINIILE (canalul din §1.1(B)) -> crsarticole - 8. GARDA DE RECALCUL: thisform.lIncarcare = .T. - 9. Umple crsfactura din crsarticole, fara dialoguri (§4.3) -10. Adauga lista de preturi peste crsarticole (pentru S4g: adaugarea de articole noi) -11. thisform.lIncarcare = .F. -12. Un singur do_calculeaza_totaluri() -13. SNAPSHOT pentru S8b (§5) -14. Blocheaza antetul (S8c) si campurile din §3.1; arata formularul -``` - -### 4.2 De ce pasul 3 arata asa — `tnIdSet = 0` [V] - -Tot corpul lui `oDateFactura.Init` e inchis in `If !Empty(m.tnIdSet)` (`ofacturare_comun.prg:225`). -Cu `tnIdSet = 0`, **niciuna dintre cele opt initializari de la 2.2 (nr. 1-8) nu se executa**, iar -constructorul devine inert. **Precedentul exista si e chiar cel de reincarcare a unui document:** -`relisteaza_ofacturare_stoc` face `poDate = Createobject("oDateFactura", 0, 0)` -(`ofacturare_stoc.prg:497`) **[V]** exact din acest motiv. - -Asta rezolva **printr-o singura decizie de constructie** opt din cele paisprezece initializari -periculoase, fara nicio modificare in `oDateFactura` — si e mult mai robust decat „suprascriem dupa", -pentru ca nu depinde de completitudinea listei de suprascrieri. - -**Ce ramane de tratat separat, pentru ca nu trece prin `Init`:** -- nr. 9-12 (`frm_alte_date.Init`) → garda `poDate.lEditare` de la 2.1; -- **nr. 13, `poGeneratorNumere`** — e apelat din `factureaza` (`ofacturare.prg:207-209`), nu din - `Init`. Pe calea de editare **nu trebuie sa se aloce numar**: seria si numarul vin din document - (decizia F / plan §F). `oGeneratorNumere` are deja `dezaloca_numar` si `verifica_numar` - (`ofacturare.prg:227`, `:250`), dar calea corecta e **sa nu se aloce deloc**. **[N]** — - `COMUN\programe\oserii_numere.prg` n-a fost citit in aceasta sesiune; planul citeaza `:227-233` - pentru afirmatia „la modificare nu se aloca numar nou". **De verificat la implementare** ca ocolirea - alocarii nu lasa `poDate.rezultat_serii` intr-o stare pe care `frm_date_factura.Init` o citeste - (`ofacturare.vc2:9736`: `If Inlist(poDate.rezultat_serii, 1, 2, 3) → clb_serie_act.do_initializeaza(...)`). - -### 4.3 Pasul 9 e piesa care lipseste azi — si e cea mai mare - -**[V]** Pe calea de copiere, `crsfactura` ramane gol; documentul se compune manual sau prin -`do_adauga_tot`. `do_adauga_tot` (`ofacturare.vc2:13169-13198`) face `SCAN` peste `crsarticole` si -cheama `do_adauga_articol(.T.)`, care (`:12871-12894`): - -- pe ramura `poArticol.gestionabil = 0 Or gnScadereStoc = 0 Or poDate.tip = 45` — creeaza - `frm_articol_factura` **fara sa-l arate** (cu `tlImplicit = .T.`) si calculeaza totalurile. Acceptabil. -- pe ramura `Otherwise` — cheama **`do_alege_stoc(...)`**, adica **dialogul de alegere din stocul de - azi**. Pe calea de editare asta e gresit de doua ori: (a) ar putea cere interventia utilizatorului - pentru fiecare linie gestionabila la simpla deschidere a documentului; (b) ar alege **loturi/serii - din stocul curent**, desi documentul are deja `LOT`, `SERIE` si `ID_GESTIUNE` proprii, intoarse de - cursor. - -In plus, `do_adauga_tot` are un `Do While` cu `aMessageBox("Nu ati selectat toata cantitatea …")` -(`:13188`) — **un modal per linie neacoperita**. Inacceptabil la incarcare. - -Si `do_adauga_articol` scrie `Replace id_temp With Recno()` (`:12951` si `:13000`) **[V]** — deci chiar -daca sursa ar avea `ID_VANZARE_DET`, **nu ar ajunge in `crsfactura` pe aceasta cale**. - -**Concluzie:** S8 nu poate refolosi `do_adauga_tot`. Ii trebuie o **populare directa** -`crsarticole → crsfactura` (un `INSERT INTO crsfactura ... SELECT ...` plus recalculul valorilor pe -linie prin `calculeaza_totaluri`), care: -1. **nu deschide niciun dialog**; -2. pastreaza `lot`, `serie`, `id_gestiune`, `id_pol`, `pretd` **din document**; -3. duce `id_vanzare_det` intr-o coloana proprie noua pe `crsfactura` (**nu** in `id_temp`, care e - suprasolicitat: `Recno()` pe o cale, `id_vanzare_det` pe alta — `prelucreaza_facturacrs` face - `id_vanzare_det As id_temp`, `ofacturare_comun.prg:1811` **[V]**); -4. duce `taxcode`. -Precedentul de structura exista: blocul `tnTip = 30` din `factureaza` -(`ofacturare.prg:340-378`) face exact o populare directa `crsarticole → crsfactura` prin -`INSERT INTO crsfactura (...) Values (...)`, fara niciun dialog **[V]**. Se cloneaza forma lui. - -### 4.4 Garda de „nu recalcula acum" — unde, si de ce - -`crsfactura` e `RecordSource`-ul gridului, iar coloanele au `Valid`/`InteractiveChange` care -recalculeaza. La populare programatica, `Valid` **nu** se declanseaza in VFP (se declanseaza doar la -iesirea din control, pe interactiune) — deci riscul principal **nu** e in grid, ci in: - -- **`ControlSource` legate direct de `poDate`** (ex. `poDate.discount_evidentiat`, - `ofacturare.vc2:11305`) — atribuirea programatica declanseaza `ProgrammaticChange`, nu `Valid` - **[D]** (comportament VFP standard; nu l-am observat rulat aici); -- **`opt_incasat.Value =`** — 2.2 nr. 12, efectul secundar documentat; -- **`clb_discount.procent.Value =`** (`ofacturare.vc2:13475`) — daca S8 populeaza procentul de discount - in loc de suma, `InteractiveChange` ar recalcula discountul pe baza curenta, care in timpul popularii - e incompleta. - -**Recomandare concreta:** un singur flag pe formular, `Thisform.lIncarcare`, testat la intrarea in -`do_calculeaza_totaluri` si in `actualizeaza_discount` (`ofacturare.vc2:13408-13415`), plus **regula ca -discountul de document se incarca ca SUMA** (`ndiscfactron`/`ndiscfactval`, valorile pe care le citeste -si scrierea, `:14272-14274`), **nu ca procent** — procentul se recalculeaza din suma la pasul 12 -(`:13475`: `procent.Value = Round(ndiscfactron * 100 / nbazafdiscount, 2)`), nu invers. - -### 4.5 Asezarea — decizia 5 - -Nimic din cele de mai sus nu schimba aspectul: nu se adauga banda de avertizare, coloane cu valori -initiale sau panou de diferente. Se schimba **titlul ferestrei** si **butonul principal** (decizia 9 / -S8c). Diferentele vizibile fata de introducere sunt **doar** cele care rezulta din blocarea campurilor -(3.1) si din ascunderea lui `zi_curs` (1.5) — ultima e o abatere minora de la „identic", propusa -motivat, **de confirmat de Marius** (intrebarea 4, sectiunea 8). - ---- - -## 5. Interfata cu S8b — ce se retine ca „stare initiala" - -### 5.1 Momentul - -Snapshot-ul se ia la **pasul 13**: dupa incarcarea completa si dupa singurul recalcul, dar **inainte de -afisarea formularului**. Nu mai devreme (valorile derivate n-ar fi calculate) si nu mai tarziu (orice -`Init` de subformular ar putea polua — 2.2). - -### 5.2 Ce se retine, si in ce forma - -| Grup | Forma | Normalizare obligatorie inainte de comparatie | -|---|---|---| -| **Cei 14 parametri** | copie a valorilor din `poDate` (+ `id_ruta`, 1.5) intr-un obiect `poDateInitial` | `.NULL.` vs `0` vs `''` — functie unica aplicata **simetric**; `text_aditional`: **`LEFT(...,100)` + `Chr(170)`→`CR+LF`** pe ambele parti (1.7) | -| **Discountul de document** | `ndiscfactron` si `ndiscfactval`, **ca sume** | `Round(..., gnPc)` / `Round(..., gnPVal)` | -| `discount_evidentiat` | scalar | — | -| **Liniile** | copie a lui `crsfactura`, cheia = **`id_vanzare_det`** (coloana noua, 4.3) | `Round` la `gnPc` / `gnPPretV` / `gnPCant`; pretul comparat in forma **rotunjita + `DIFERENTA`** (1.4.ii) | -| **Explicatie / taxcode pe linie** | in aceeasi copie | `Alltrim`, `Nvl(...,'')` | - -**Trei precizari care corecteaza sau completeaza S8b:** - -1. **Cheia `id_vanzare_det` nu vine gratuit** — S8b o presupunea incarcata („coloana deja incarcata la - S8"). Nu e (1.1). Devine **livrabil al lui S8**, nu premisa. -2. **Liniile fara `id_vanzare_det`** (adaugate in sesiune) se marcheaza cu `0`/`.NULL.` si inseamna - direct „regenerare", ca in S8b §1.3. -3. **`zi_curs` se scoate din comparatie** (1.5) — altfel orice document ar aparea modificat. - -### 5.3 Cele 7 campuri comune `scrie_factura2` / `modifica_date_factura` - -Nimic de adaugat la S8b §3.1 — lantul cu prioritate ramane. S8 doar garanteaza premisa lui: **`poDate` -contine, la momentul confirmarii, valorile documentului plus editarile utilizatorului si nimic altceva** -— ceea ce e exact ce asigura sectiunile 2 si 4. - ---- - -## 6. Interfata cu S9 — ce trebuie sa incarce S8 ca S9 sa poata refurniza lista sursa - -Pasul 1 nou al lui S9 (handoff, rezultatul 7) cere **citirea listei sursa inainte de stergere**. S8 e -locul unde se citeste, pentru ca **dupa stergere `VANZARI_CORESP` are `STERS = 1`** (`:5582` **[V]**) si -`VANZARI.ID_COMANDA` / `ID_CTR` raman pe randul soft-sters. - -**Ce incarca S8, si de unde:** - -| Ce | Camp propus pe `poDate` | Sursa | Consumator la reemitere | -|---|---|---|---| -| lista de avize | `cListaSursaAvize` | `SELECT ID_VANZARE_AVIZ FROM VANZARI_CORESP WHERE ID_VANZARE_FACT = :id AND STERS = 0 AND TIP = 1` | `pack_facturare.clistaid_avize` → `scrie_corespondente_vanzari(1)` si `marcheaza_facturat` | -| comanda | `cListaSursa` | `FACT_VFACTURI.ID_COMANDA` | `pack_facturare.clistaid` | -| contract | `cListaSursa` (+ `id_ctr`) | `FACT_VFACTURI.ID_CTR` | idem, plus `CTR_RATE_FACTURI` | -| tipul original | `poDate.tip`, nedegradat | `FACT_VFACTURI.TIP` | `CASE`-ul pe `ntip` din `finalizeaza_factura` | - -**Doua avertismente:** - -1. **`poDate.listaid` NU poate purta aceasta informatie** — la incarcare el trebuie sa fie - `id_vanzare`-ul documentului insusi, ca sa se citeasca liniile (1.6). Doua campuri distincte, - nu unul reinterpretat. -2. **Semantica valorilor lui `TIP`** din `VANZARI_CORESP` **nu e stabilita aici [N]** (1.6) — se - citeste din `CASE`-ul real al lui `finalizeaza_factura` la implementare. - -**Nu tine de S8**, dar se semnaleaza pentru ca S8 decide continutul lui `crsarticole`: -`do_scrie_factura` face `Select crsarticole` + `Calculate Sum(cantitate) To lnCantitateRamasa` pe -ramurile `poDate.Tip = 4` (`ofacturare.vc2:14305-14307`) si `Inlist(poDate.Tip,3,21,25,28,42,47)` -(`:14336-14338`) **[V]**, si pe baza sumei intreaba *„Doriti sa se inregistreze si avizul de retur?"* / -*„Doriti sa se inchida comanda?"* si seteaza `pnParametruAditional`. Pe calea de editare `crsarticole` -contine liniile documentului **plus lista de preturi adaugata peste ele** (`ofacturare.prg:465-473` -**[V]**), deci **suma e lipsita de sens** si intrebarea ar aparea gresit, cu efect real pe -`pnParametruAditional`. **De rezolvat in S9** — fie prin cursor separat pentru lista de preturi, fie -prin calcularea sumei doar peste randurile provenite din document. - ---- - -## 7. Criteriul „gata cand", in forma testabila - -**Comun tuturor tipurilor.** Pe un document existent, dupa deschidere si **inainte** de orice -interactiune: - -| # | Ce se verifica | Cum | -|---|---|---| -| C1 | serie, numar, data, scadenta afisate = cele din `VANZARI` | comparatie ecran ↔ `SELECT serie_act, numar_act, data_act, data_scad FROM vanzari WHERE id_vanzare = :id` | -| C2 | **niciun numar nou alocat** | inainte/dupa: ultimul numar din generatorul de serii pentru `nIdTipDoc` e neschimbat | -| C3 | numarul de linii din grid = `SELECT count(*) FROM vanzari_detalii WHERE id_vanzare = :id AND sters = 0` | | -| C4 | pe fiecare linie: cantitate, pret, discount, gestiune, lot, serie, cota TVA, explicatie, `taxcode` = valorile din `VANZARI_DETALII` (pret in forma rotunjita + `DIFERENTA`, 1.4.ii) | | -| C5 | totalurile afisate = `VANZARI.TOTAL_FARA_TVA` / `TOTAL_TVA` / `TOTAL_CU_TVA` | | -| C6 | discountul de document si `discount_evidentiat` = `VANZARI.DISCOUNT` / `DISCOUNT_EVIDENTIAT` | | -| C7 | delegat, masina, agent, adresa de facturare, `dataora_exp`, `text_aditional`, `listare_detaliata`, `tip_saft`, `efactura`, `id_ruta` = `VANZARI` | inclusiv **cazul „documentul n-are delegat"** → campul ramane gol (2.1) | -| C8 | **nicio scriere in baza** — criteriul de baza al lui S8b | `SELECT dataoras, id_utils FROM vanzari WHERE id_vanzare = :id` neschimbat dupa deschidere+inchidere; idem pe `VANZARI_DETALII` | -| C9 | **niciun dialog modal** la deschidere (nici alegere de stoc, nici „nu ati selectat toata cantitatea", nici „doriti sa inchideti comanda") | observatie | -| C10 | aspectul e cel de introducere (decizia 5): fara banda, fara coloane de valori initiale, fara panou de diferente; difera doar titlul si butonul principal | observatie | - -**Pe fiecare tip de sursa, in plus:** - -| Tip | Ce se verifica specific | -|---|---| -| **comanda** (3, 21, 25, 28, 42, 47) | `Ct_clb_altele` afiseaza numarul comenzii, eticheta „Nr. comanda", **blocat**; `poDate.cListaSursa` = `VANZARI.ID_COMANDA` | -| **contract** (2, 6, 26, 52) | eticheta „Nr. contract", blocat; `id_ctr` incarcat; **`goContract` din sesiune NU suprascrie antetul** (2.2 nr. 7); daca S10 se implementeaza, **niciun avertisment de re-derivare la simpla deschidere** (`cursor_retur_document` citeste pretul stocat, rezultatul 6 al handoff-ului) | -| **aviz** (tip 4 = factura din avize) | eticheta **„Nr. aviz / avize"** (3.2), blocat; `poDate.cListaSursaAvize` = randurile `VANZARI_CORESP` ale documentului; **nicio intrebare „doriti sa se inregistreze si avizul de retur?"** la deschidere (§6) | -| **factura simpla** (1, 5, 10) | `Ct_clb_altele` **pastrat si vizibil** cu eticheta „Nr. factura" (3.2, ramurile 1 si 2 cu `llCopiere`), spre deosebire de emiterea normala unde e scos | -| **retur** (8, 9, 24) | `zi_curs` e oricum scos azi pentru 8/9 (`ofacturare.vc2:9720-9725`) — comportament neschimbat; liniile se incarca cu cantitatile documentului, nu cu cele disponibile la retur | -| **ROAAUTO** | `poDate.id_ordl` incarcat din `VANZARI.ID_ORDL`; **[N]** nu s-a verificat in aceasta sesiune ce alte campuri specifice ROAAUTO exista pe antet — `roaauto_facturi.md` nu a fost citit | -| **proforma** (`eproforma = 1`) | `poDate.eProforma` incarcat (1.2.a) → `frm_alte_date.Init` sare din start pe ramura de proforma (`:3110`), iar `gestionabil` vine `0` din cursor (1.4) | - ---- - -## 8. Riscuri, goluri ramase, si intrebarile pentru Marius - -### 8.1 Ce n-am putut stabili, si de ce - -| # | Ce | De ce | -|---|---|---| -| N1 | Semantica exacta a valorilor `VANZARI_CORESP.TIP` (`1/2/3`, `4` liber) | Cere citirea `CASE`-ului pe `ntip` din `finalizeaza_factura`, in afara perimetrului parcurs. **Nu se preia din raportul precedent** — e exact tiparul cifrei gresite din runda 13. | -| N2 | Daca ocolirea alocarii de numar lasa `poDate.rezultat_serii` intr-o stare pe care `frm_date_factura.Init` o citeste gresit (`ofacturare.vc2:9736`) | `COMUN\programe\oserii_numere.prg` necitit in aceasta sesiune | -| N3 | Daca prefixul „Scadenta la N zile." adaugat la `Init` (2.2 nr. 11) e scos pe **toate** caile de iesire | Am vazut operatia inversa la `ferestre_cere_date.vc2:3037-3038`, dar n-am urmarit toate caile de `Unload`/`Destroy` | -| N4 | Campurile de antet specifice **ROAAUTO** | `roaauto_facturi.md` necitit | -| N5 | Daca tipurile **48/49** (custodie) intra in perimetrul etapei II | Conteaza pentru 3.2, ramura 3 | -| N6 | Daca `frm_facturare_articole2` (varianta paralela) trebuie tratata identic | Are `do_adauga_tot` / `do_adauga_articol` proprii (`ofacturare.vc2:17476`, `:17124`), cu logica usor diferita (fara testul `llGestionabil`) — **de decis daca S8 tinteste ambele forme sau doar `frm_facturare_articole`** | - -### 8.2 Intrebari pentru Marius, cu recomandare - -1. **Canalul de citire a liniilor: (A) extindem `cursor_retur_document`, (B) procedura noua - `cursor_editare_document`, sau (C) `FACT_VFACTURI_DETALII`?** (1.1) - *Recomandare:* **(B)**. Argumentul nu e estetica, ci ca (A) schimba **comportamentul copierii** - (`taxcode` ar incepe sa se propage), iar (C) pierde `ID_POL`, `PRETD` si tratamentul valutar — - partea grea. (B) da si libertatea de a alege corect `GESTIONABIL` (1.4.i). - -2. **`GESTIONABIL` la editare: din nomenclatorul de azi (`IN_STOC`) sau din document (`ID_GESTIUNE`)?** - (1.4.i) - *Recomandare:* **din document**. Un document editat trebuie sa arate cum a fost emis; regimul de - stoc al articolului s-a putut schimba intre timp fara nicio legatura cu documentul. - -3. **`text_aditional`: se normalizeaza la incarcare (`Chr(170)` → `CR+LF`) sau se afiseaza ca atare?** - (1.7) - *Recomandare:* **se normalizeaza la incarcare si se re-normalizeaza la comparatie**, altfel orice - document cu text pe mai multe randuri apare modificat la deschidere. Nota: cele doua rute de scriere - salveaza **forme diferite** ale campului (truncat/substituit vs. brut) — merita semnalat separat, - e un defect preexistent, nu al lui #13. - -4. **`zi_curs` pe calea de editare: ascuns, sau afisat gol?** (1.5) - *Recomandare:* **ascuns**, cu precedent in acelasi `Do Case` (tipurile 8/9). E o abatere mica de la - decizia 5, si o semnalez ca atare — un camp afisat gol pe un document care are curs ar fi mai - derutant decat absenta lui. - -5. **`poDate.lEditare` ca proprietate pe `oDateFactura`, sau parametru pe `frm_alte_date.Init`?** (2.1) - *Recomandare:* **proprietate**, pentru ca sunt **cel putin patru** locuri care au nevoie de semnal - (2.2 nr. 9-13), nu unul, si pentru ca `lCopiere` e deja acolo, cu exact acelasi rol. - -6. **`id_ruta` — proprietate noua pe `oDateFactura`?** (1.5) - *Recomandare:* **da**. Fara ea S8c nu poate implementa unul din cei 14 parametri, iar azi valoarea - circula doar prin `poRec`, care nu exista in formularul unificat. - -7. **Tipurile 48/49 si `frm_facturare_articole2`** (N5, N6) — intra in perimetrul etapei II? - *Recomandare:* **nu acum**; se declara explicit ca neacoperite, ca sa nu se descopere la testare. - -8. **Defectul de la 2.2 nr. 11** (prefixarea `text_aditional` la `Init` pentru contracte) — se repara - in #13, se semnaleaza separat, sau se lasa? - *Recomandare:* **se ocoleste in #13** (prin `lEditare`) si **se semnaleaza separat** ca defect - preexistent — repararea lui pe calea de emitere nu tine de aceasta poveste. - -### 8.3 Ce a fost corectat fata de materialele existente - -| Afirmatie anterioara | Stare | -|---|---| -| S8b: „lookup-ul delegatului trebuie sarit explicit, altfel pica primul criteriu" | **Nuantat** — garda `Empty(id_delegat) And Empty(id_masina)` exista deja (`ferestre_cere_date.vc2:3111`); riscul e real **doar** pe documentele fara delegat si fara masina | -| S8b §1.2 / §7.4: „cheia de linie `id_vanzare_det`, coloana deja incarcata la S8" | **Infirmat** — `cursor_retur_document` nu o intoarce; devine livrabil al lui S8 | -| S8b §5.2: „S8 trebuie sa suprascrie implicitul `zi_curs` cu data reala salvata" | **Infirmat structural** — nu exista coloana `ZI_CURS` pe `VANZARI` | -| Plan S8: etichetele lui `Ct_clb_altele` (cinci) | **Incomplet** — sunt sase; lipseste „Nr. aviz / avize" (tip 4), exact cazul relevant pentru S9 | -| Plan S8: „eliminat cand `gnScadereStoc = 0` si tipul e 1/5/10 fara copiere" | **Incomplet** — trei ramuri, tipurile `1,5,10,48,49`, si pentru `gnScadereStoc = 1`, nu doar `0` | -| Plan S8: „`cursor_retur_document(V_COPIERE=1)` pentru linii" | **Insuficient** — umple selectorul, nu documentul; lipsesc `ID_VANZARE_DET` si `TAXCODE` | -| Plan S8: „`completeaza_setari_document` pentru antet" | **Insuficient** — 16 proprietati din ~120, niciuna de identitate | - ---- - -*Cercetare incheiata pe toate cele 8 puncte cerute in briefing. Afirmatiile portante au `fisier:linie` -sau nume de coloana din dictionar. Nu s-a modificat niciun fisier de cod; pe Oracle numai `SELECT` pe -`all_tab_columns`.* diff --git a/docs/cercetare/s8b_rutarea_scrierii.md b/docs/cercetare/s8b_rutarea_scrierii.md deleted file mode 100644 index 37e983e..0000000 --- a/docs/cercetare/s8b_rutarea_scrierii.md +++ /dev/null @@ -1,311 +0,0 @@ -# S8b — Proiectare: rutarea scrierii dupa ce utilizatorul a schimbat ceva - -Livrabilul povestii **S8b** din `docs\plan_13_unificare_formular_facturare.md:2890-2898` (G-bis, -deciziile 6/9/25/26). Cercetare READ-ONLY — zero editari de cod, zero `git_sync.ps1`/`txt2vcx.ps1`, -zero commit, zero scriere Oracle (doar SELECT, nefolosit efectiv — tot ce trebuia era in codul VFP/ -PL-SQL deja exportat sau in rapoartele de referinta citate in briefing). -`COMUN\clase\ofacturare_comun.vc2` si `COMUN\programe\ofacturare_editare.prg` (perimetrul #6) doar -citite, neatinse. - -## Verdict (rezumat, 8 randuri) - -Reteta din G-bis e corecta ca directie, dar are un gol nedocumentat pana acum: **`modifica_date_factura` -nu e singura ruta care scrie cei 14 parametri de antet** — 7 din 14 (`id_delegat`, `id_masina`, -`id_facturare`, `listare_detaliata`, `dataora_exp`, `id_agent`, `text_aditional`) sunt scrisi **si** -de calea normala de emitere (`scrie_factura2`, apelata direct de regenerare, decizia 35), pentru ca -ambele cai citesc din **acelasi obiect `poDate`**, nu din doua reprezentari separate (sectiunea 3, -dovada pe cod). Consecinta directa pentru punctul cel mai periculos al sarcinii (schimbare simultana -antet+sume): **cand regenerarea porneste, NU se mai cheama `modifica_date_factura` in plus** — ar fi -fie redundant (pe cele 7 campuri comune), fie periculos (ar scrie pe un `ID_VANZARE` care tocmai a -fost soft-sters sau inca nu exista). Regenerarea *este* deja calea de scriere a antetului cand sumele -se schimba, nu o cale separata care trebuie compusa cu ea. Explicatia de linie e acoperita direct de -`adauga_articol_factura` (parametru `V_EXPLICATIE`, plus `V_TAXCODE`), deci regenerarea o transporta -fara cod suplimentar — dar **doar daca cursorul de linii incarcat la S8 e re-citit din formular, nu -din snapshot-ul initial**. Cel mai probabil loc de fals-pozitiv pentru „deschid si inchid fara sa -modific nimic” e lookup-ul „ultimul delegat/masina al clientului” din `frm_alte_date.Init` -(sectiunea 5) — cod construit pentru emiterea unui document nou, periculos daca ruleaza neschimbat pe -calea de editare. - ---- - -## 1. Mecanismul de detectare a schimbarii - -### 1.1 Optiuni si ce se strica la fiecare - -| Optiune | Ce se strica | -|---|---| -| **Hash/checksum pe randuri** | Ascunde exact tipul de fals-pozitiv cel mai periculos aici: doua reprezentari numeric-egale dar text-diferite (`Str(12.5,18,2)` vs `Str(12.50,18,2)`, `.NULL.` vs `0` vs `""`) produc hash-uri diferite desi valoarea „reala" e identica. Orice normalizare facuta *inainte* de hash trebuie sa fie deja perfecta — hash-ul nu adauga nimic, doar ascunde bug-urile de normalizare in loc sa le arate. | -| **Flag-uri `lModificat` in evenimentele de editare** | E robust doar daca *fiecare* eveniment care poate schimba o valoare seteaza flag-ul — un `ControlSource` legat direct (binding automat VFP, cazul majoritatii campurilor de antet aici, vezi `modifica_date_factura_parametri.md` §4) nu trece printr-un eveniment scriptat, deci flag-ul ramane `.F.` desi valoarea s-a schimbat. Whitelist fragil: la fiecare control nou adaugat, cineva trebuie sa-si aminteasca sa cablasje flag-ul. Cel mai riscant pentru campurile din `frm_alte_date` (S3b), unde `ControlSource=poDate.xxx` e tiparul dominant. | -| **Snapshot la incarcare + comparatie camp cu camp la confirmare** (recomandat) | Cere disciplina la normalizare (precizie, `.NULL.`), dar e singura optiune care **nu poate rata o schimbare structurala** — compara starea finala, nu istoricul de evenimente, deci un binding automat care a schimbat o valoare fara eveniment scriptat tot apare in diff. E si optiunea deja folosita implicit in codebase pentru un caz inrudit: `chkSerieAct.Enabled = !EMPTY(NVL(poRec.numar_act,0))` (`ofacturare_comun.vc2:5736`) testeaza starea curenta, nu un istoric de evenimente. | - -**Recomandare: snapshot + comparatie la confirmare**, din motivul de mai sus (robustete la binding -automat) — argumentat, nu doar preferat. - -### 1.2 Ce se snapshoteaza, concret - -- **Antet**: o copie profunda a lui `poDate` (sau a subsetului de proprietati relevante) luata - imediat dupa S8 (incarcare), **inainte** de orice lookup auto-completat (vezi sectiunea 5 — ordinea - conteaza: daca lookup-ul „ultimul delegat" ruleaza inainte de snapshot, snapshot-ul insusi e deja - poluat, si orice comparatie ulterioara devine inutila). -- **Linii**: o copie a cursorului `crsfactura` (nume alias de confirmat la implementare — S4d il - citeaza ca atare) imediat dupa populare la S8, cu cheia de linie **`id_vanzare_det`** (coloana deja - incarcata la S8 din `VANZARI_DETALII`, cf. plan S8: „explicatia si taxcode pe fiecare linie" — nu - exista alt candidat de cheie stabila in materialele citite; **de confirmat exact numele coloanei in - cursor la implementare**, nu presupus mai departe aici). -- **Discountul de document** (`VANZARI.DISCOUNT`, `discount_evidentiat`) — campuri de antet cu efect - de suma, tratate separat de cele 14 (vezi matricea, sectiunea 2). - -### 1.3 Capcanele reale de VFP, cu tratament explicit - -| Capcana | Tratament recomandat | -|---|---| -| `.NULL.` vs `0` vs `""` | Comparatie prin functie de normalizare unica (`NVL`-style) aplicata **simetric** pe ambele parti (snapshot si curent) inainte de `=`, nu doar pe una — o comparatie `Nvl(nou,0) == vechi` fara acelasi tratament pe `vechi` e asimetrica si poate rata cazul `vechi=.NULL., nou=0` ca "neschimbat" cand de fapt utilizatorul a introdus explicit un zero peste un camp gol (relevant pt. campuri ca `id_ruta`/`id_agent`, unde `.NULL.` si `0` pot avea semnatura diferita in Oracle — `modifica_date_factura` scrie orice i se da, inclusiv `NULL` neconditionat, deci diferenta chiar conteaza acolo). | -| Precizie numerica (`gnPc`, `gnPPretV`, `gnPCant`) | Comparatia nu se face pe valoarea `Double` bruta, ci pe reprezentarea **rotunjita la aceeasi precizie folosita la scriere** — acelasi `Round(x, gnPc)`/`Round(x, gnPPretV)`/`Round(x, gnPCant)` aplicat pe ambele parti inainte de `=`. Motiv concret: `adauga_articol_factura` primeste pretul ca `Str(..., 18, gnPc)` (text), deci orice zgomot de reprezentare in binar (ex. `12.4999999999` vs `12.5`) care nu apare si in textul trimis Oracle-ului nu trebuie sa declanseze regenerare — regenerarea trebuie sa porneasca de la o diferenta **care ar produce efectiv un text diferit trimis la Oracle**, nu de la zgomot de virgula mobila intern VFP. | -| Randuri sterse/adaugate, nu doar modificate | Comparatie pe **multimea cheilor** `id_vanzare_det`, nu pe pozitie: chei prezente in snapshot dar absente in curent = sters; chei prezente in curent dar absente in snapshot (sau `id_vanzare_det` gol/`0`, sentinela de linie noua) = adaugat; chei prezente in ambele = potential modificat, comparat camp cu camp. **Orice** rand adaugat sau sters, indiferent de continutul lui, marcheaza direct pentru regenerare (tabelul din G-bis: „linii adaugate/sterse” → regenerare) — nu are sens sa se compare campuri pe un rand care oricum nu exista pe ambele parti. | -| Ordinea randurilor | **Nu conteaza pentru detectie** — comparatia e pe chei (set), nu pe pozitie in grid. Ordinea ar conta doar daca reordonarea insasi ar fi o schimbare semnificativa pentru Oracle (nu e cazul — `adauga_articol_factura` nu are parametru de ordine vizibil in semnatura citata in `s10_pret_rederivat.md` §1). **Rezerva**: daca formularul unificat permite reordonare manuala a liniilor si aceasta conteaza pentru vreun raport (necercetat aici), ar trebui un camp explicit de ordine comparat separat — nu presupus din pozitia in cursor. | - ---- - -## 2. Matricea „ce s-a schimbat → ce ruta", exhaustiva - -Sursa de adevar: G-bis (`plan:812-889`) + `modifica_date_factura_parametri.md` + `rute_scriere_antet.md`. - -| Camp / grup | Ruta | De ce nu una mai ieftina | -|---|---|---| -| **Cei 14 parametri** (serie, numar, data, scadenta, ruta, delegat, masina, agent, `dataora_exp`, adresa facturare, text aditional, `listare_detaliata`, `tip_saft`, `efactura`) | `modifica_date_factura`, **daca nimic altceva nu s-a schimbat in aceeasi sesiune** | E singura cale directa pe `VANZARI`+`DOCUMENTE`+`ACT`+`IREG_PARTENERI`+`JV2007`+`RUL` care nu atinge sumele — mai ieftina decat regenerarea (fara stergere+reemitere, fara stoc, fara nota noua). Regenerarea ar face acelasi lucru, dar cu cost mult mai mare (tranzactie stergere+reemitere, stoc, ID_VANZARE nou) pentru un camp care nu atinge nicio suma — nu se justifica. | -| **Explicatia si `taxcode` pe o linie**, **fara nicio alta schimbare pe linii** | `modifica_explicatie_articol` | Update pe doua coloane, fara sume — regenerarea ar fi disproportionata (stergere+reemitere completa pentru un text). | -| **Cantitati, preturi, discount pe linie, gestiune, cota TVA, serie/lot** | Regenerare | Nu exista alta ruta — verificat exhaustiv, nicio procedura Oracle de tip "modifica cantitate/pret pe linie existenta" (confirmat indirect: singura cale de scriere a sumelor e `adauga_articol_factura`, apelata doar la emitere/reemitere). | -| **Linii adaugate/sterse** | Regenerare | Identic — nu exista `sterge_linie_factura`/`adauga_linie_factura` separat de fluxul de emitere. | -| **Discountul de document** (`VANZARI.DISCOUNT`, `discount_evidentiat`) | Regenerare | Nu e printre cei 14 parametri ai `modifica_date_factura` (confirmat, tabelul din `modifica_date_factura_parametri.md` §1-2 nu il contine) si atinge direct sumele — cade natural in categoria "orice atinge sumele" din G-bis. | -| **Grupul B** — tip document, valuta, zi curs, client, sursa, gestiune sursa, politica de preturi | Regenerare (decizia 25); **blocate in etapa I** | `rute_scriere_antet.md` §2 — verdict NU pentru toate 7, cautat exhaustiv in Oracle si VFP; regenerarea (calea de emitere) e singura care le scrie, pentru ca sunt scrise o singura data la `scrie_factura2`/echivalent, niciodata actualizate separat. | -| **Grupul C** — incasare (mod, casa, serie/nr chitanta, suma, POS) | Regenerare (decizia 25); **blocate in etapa I** | `rute_scriere_antet.md` §3 — incasarea devine ea insasi o linie de nota (`scrie_incasare2`), scrisa doar la emitere; nicio ruta „modifica incasare" separata. | -| **Grupul A** — venit/cheltuiala, sectie, responsabil, lucrare | **Niciuna din #13** — read-only (decizia 26) | Editabile deja, dar la nivel de LINIE de nota, prin #6 (`frm_modific2024`) — #13 le-ar scrie uniform pe tot documentul, risc de suprascriere tacita a unei diferentieri facute din #6. Zero suprapunere intre fire, per decizia lui Marius din 09.08.2026. | -| **Datele din sectiunea pliata — delegat/transport, adresa facturare, text aditional** | Fac parte din cei 14 parametri → `modifica_date_factura` daca nimic altceva nu s-a schimbat; **regenerare** daca si sumele s-au schimbat (vezi sectiunea 3) | Sunt deja acoperite de tabelul de mai sus prin `V_ID_DELEGAT`/`V_ID_MASINA`/`V_ID_AGENT`/`V_ID_FACTURARE`/`V_TEXT_ADITIONAL`/`V_DATAORA_EXP`. | -| **Nimic schimbat** | Nimic | Explicit cerut de G-bis — altfel orice deschidere ar produce scriere degeaba. | - ---- - -## 3. Cazurile care cad intre rute — descoperirea centrala a acestei cercetari - -### 3.1 Antet + sume schimbate in aceeasi sesiune — verdict cu dovada, nu presupunere - -**Intrebarea din briefing**: se cheama ambele rute, sau regenerarea le acopera pe amandoua? - -**Raspuns, verificat pe cod**: **regenerarea acopera 7 din cei 14 parametri prin insusi mecanismul -de emitere, fara nicio ruta suplimentara** — pentru ca `poDate` e **acelasi obiect** care alimenteaza -atat afisarea antetului cat si apelul `scrie_factura2` folosit de regenerare (decizia 35: "reemiterea -scrie prin `pack_facturare`, pe acelasi drum ca emiterea"). - -Dovada directa, apelul `scrie_factura2` (`COMUN\clase\ofacturare.vc2:14345-14359`, identic la -`:14373-14387` si `:18343-18387`): - -``` -lcSql = [{call pack_facturare.scrie_factura2(] + ; - ... pnTotalFtva, pnTotalTva, pnDiscount, serie_chit, nr_incasare, lcListaIncasare, ; - IIF(Isnull(poDate.id_delegat),[NULL],Alltrim(Str(poDate.id_delegat))) + [,] + ; - IIF(Isnull(poDate.id_masina),[NULL],Alltrim(Str(poDate.id_masina))) + [,] + ; - IIF(Isnull(poDate.id_facturare),[NULL],Alltrim(Str(poDate.id_facturare))) + [,] + ; - IIF(Isnull(poDate.nListareDetaliata),[0],Alltrim(Str(poDate.nListareDetaliata))) + [,] + ; - [to_date('] + Ttoc(poDate.dataora_exp,1) + [','YYYYMMDDHH24MISS'),] + ; - IIF(Isnull(poDate.id_agent),[NULL],Alltrim(Str(poDate.id_agent))) + [,] + ; - ['] + OracleSpecialCharacters(...(poDate.text_aditional...)) + [',] + ; - Alltrim(Str(poDate.discount_evidentiat)) + [,] + ; - ALLTRIM(Str(pnParametruAditional)) + [,?@poDate.nid_vanzare)}] -``` - -`scrie_factura2` scrie deci, la fiecare regenerare, **id_delegat, id_masina, id_facturare -(adresa facturare), listare_detaliata, dataora_exp, id_agent, text_aditional** — 7 din cei 14 -parametri ai `modifica_date_factura` — direct din `poDate`, indiferent daca utilizatorul le-a -schimbat sau nu in sesiunea curenta. **Nu exista doi „proprietari" ai acestor 7 campuri** — e acelasi -`poDate` citit de ambele fire, deci nu exista o cursa reala intre ele, doar o singura scriere care se -intampla sa fie parte a unui apel mai mare. - -**Verdict, cu ordinea explicita**: - -1. **Cand regenerarea porneste, NU se mai cheama `modifica_date_factura`.** Ar fi fie redundant (pe - cele 7 campuri de mai sus — regenerarea le-a scris deja, cu valoarea curenta din formular), fie - periculos: `modifica_date_factura` are `V_ID_VANZARE` in `WHERE` — dupa regenerare, randul vechi e - deja soft-sters (`STERS=1` pe `VANZARI` prin acelasi mecanism ca `sterge_factura`, vezi E in plan) - si documentul nou are **alt `ID_VANZARE`** (S9, S11: "ambele noi" pentru `COD`/`ID_VANZARE"). Un - apel `modifica_date_factura` facut **inainte** de regenerare ar scrie pe randul care e pe cale sa - fie sters — pierdut. Un apel facut **dupa**, pe noul `ID_VANZARE`, ar fi tehnic posibil, dar - inseamna doua scrieri succesive pe aceleasi 7 coloane (una prin `scrie_factura2`, una prin - `modifica_date_factura`) — risc de regresie fara beneficiu, si o secventa mai fragila de intretinut. -2. **Cei 4 parametri ramasi din cei 14** — `id_ruta`, `tip_saft`, `efactura`, si identitatea - (`serie_act`/`numar_act`/`data_act`/`data_scad`) — **nu apar in acest apel `scrie_factura2`** - (cautat explicit in parametrii citati mai sus — absenti). Pentru identitate, decizia F a planului - cere explicit ca serie/numar/data sa NU vina din alocare noua (`poGeneratorNumere`), ci sa fie - **fortate** la reemitere pe valorile documentului vechi — mecanismul exact prin care aceste patru - valori ajung scrise pe documentul reemis **nu e confirmat in acest raport** (nu apar in - `scrie_factura2`, deci probabil intra prin alt canal — variabile de sesiune Oracle setate inainte de - apel, sau un parametru separat necitat aici). **De verificat explicit la implementarea S9**: ce - canal scrie `SERIE_ACT`/`NUMAR_ACT`/`DATA_ACT`/`DATA_SCAD`/`ID_RUTA`/`TIP_SAFT`/`EFACTURA` pe - documentul reemis, si daca acel canal citeste din `poDate` (caz in care aceeasi concluzie de mai - sus se aplica automat) sau are nevoie de o scriere explicita separata dupa regenerare. **Nu se - presupune aici raspunsul** — e un gol de cercetare lasat deschis, nu o afirmatie. -3. **Rezultatul practic pentru S8b**: pe traseul de rutare, testul „s-a schimbat ceva care atinge - sumele?" trebuie evaluat **inaintea** testului „s-a schimbat antetul?" — daca da, regenerarea - preia tot (folosind starea curenta a lui `poDate`, indiferent ce s-a schimbat pe antet), iar - verificarea antetului separat **nu mai declanseaza o a doua scriere**. Ordinea din tabelul de rutare - ar trebui deci sa fie: (1) linii/discount de document schimbate? → regenerare, gata; (2) altfel, - antet schimbat (14 parametri)? → `modifica_date_factura`; (3) altfel, doar explicatie de linie? → - `modifica_explicatie_articol`; (4) altfel → nimic. Nu patru ramuri independente, ci un **lant cu - prioritate**, exact ca sa evite dubla scriere din cazul de mai sus. - -### 3.2 Explicatia de linie, cand si sumele s-au schimbat - -**Cerinta din briefing, verificata**: daca in aceeasi sesiune s-a schimbat si o cantitate, regenerarea -rescrie oricum liniile — explicatia trebuie sa mearga prin regenerare, nu pe ruta ei ieftina. - -**Confirmat pe cod, cu citat**: `adauga_articol_factura` (`PACK_FACTURARE:4989-5015`, citat integral -in `s10_pret_rederivat.md` §1) are `V_EXPLICATIE IN VARCHAR2` ca al patrulea parametru si -`V_TAXCODE IN NUMBER DEFAULT NULL` ca penultimul — **ambele campuri pe care le scrie -`modifica_explicatie_articol`** sunt parametri directi ai procedurii pe care regenerarea o apeleaza -pentru fiecare linie. Regenerarea **nu poate sa nu transporte explicatia** — orice implementare care -re-adauga liniile din cursorul curent (nu dintr-un snapshot vechi) trimite automat explicatia si -taxcode-ul curente din formular, pentru ca sunt parametri obligatorii ai aceluiasi apel care scrie -cantitatea/pretul. - -**Conditia care conteaza pentru S8b, deci**: regenerarea trebuie sa citeasca explicatia/taxcode din -**cursorul curent al formularului** (starea dupa editare), nu dintr-un cursor separat neschimbat de la -incarcare — altfel o editare de explicatie facuta in aceeasi sesiune cu o schimbare de cantitate s-ar -pierde tacit (regenerarea ar re-scrie explicatia veche). Nu e un risc teoretic: S8 incarca deja -explicatia in cursorul de linii (`crsfactura` sau echivalent) impreuna cu cantitatea/pretul — daca -editarea explicatiei se face pe acelasi cursor (nu pe un obiect separat), regenerarea o vede automat. -**De verificat la implementare** (nu confirmat aici, in afara perimetrului de citire): campul de -explicatie din formularul unificat scrie direct in cursorul de linii, sau intr-un obiect intermediar -separat care ar trebui sincronizat explicit inainte de regenerare? - -**Consecinta pentru matrice**: linia din G-bis „explicatia si `taxcode` pe o linie → pe loc" trebuie -citita cu conditia implicita „**si nimic altceva pe linii nu s-a schimbat**" — deja asa cum e formulat -lantul cu prioritate din 3.1 punctul 3 (verificarea de sume vine prima). - ---- - -## 4. Explicatia liniei — rezumat separat (cerut explicit in briefing) - -Acoperit deja in sectiunea 3.2. Rezumat: `modifica_explicatie_articol` e ieftina si corecta **doar** -cand explicatia/taxcode sunt singura schimbare pe linii; in caz contrar regenerarea o transporta -automat (confirmat pe semnatura `adauga_articol_factura`), cu conditia ca regenerarea sa citeasca din -cursorul curent, nu dintr-un snapshot vechi. - ---- - -## 5. Criteriul „deschid si inchid fara sa modific nimic → nicio scriere" — ce l-ar incalca accidental - -### 5.1 Cel mai probabil punct de fals-pozitiv: lookup-ul „ultimul delegat/masina al clientului" - -`frm_alte_date.Init` (`COMUN\clase\ferestre_cere_date.vc2:3119-3136`, citat in `s3b_alte_date_analitice.md` -§1.1/§4.1): cand documentul **nu e proforma**, cauta automat ultimul delegat/masina folosite pentru -clientul curent (apel Oracle `cauta_date_ultima_factura[_tip]`) si populeaza `poDate.id_delegat`/ -`poDate.id_masina` cu rezultatul. Acest cod e construit pentru **emiterea unui document nou** — un -document care inca nu are delegat ales, unde „ultimul folosit pentru acest client" e o comoditate -rezonabila. - -**Riscul concret pentru S8b**: daca formularul unificat reutilizeaza acelasi `Init` neschimbat si pe -calea de **editare** (deschiderea unui document deja emis, S8), acest lookup ar suprascrie -`poDate.id_delegat`/`poDate.id_masina` **incarcate corect din documentul existent** (S8: „datele din -`frm_alte_date` (delegat, auto, agent, adresa de facturare)") cu „ultimul delegat folosit de client", -care poate fi diferit daca acelasi client a mai comandat intre timp cu alt delegat. Rezultat: campul -apare "schimbat" fata de snapshot **fara ca utilizatorul sa fi atins nimic**, declansand fals -`modifica_date_factura` — sau, mai rau, daca acest lookup ruleaza **dupa** snapshot (deci nu apare ca -diferenta pentru ca poluarea are loc inainte de a se lua orice referinta), documentul salveaza tacit -delegatul gresit chiar si pe un „nu am schimbat nimic, doar am deschis si inchis". - -**Recomandare, cu prioritate mare**: S8 (incarcare) trebuie sa evite explicit acest lookup pe calea -de editare — fie printr-un parametru nou pe `Init` (echivalentul unui `tlEditare`), fie prin -ordonarea explicita „incarca intai valorile reale ale documentului, apoi sari peste orice lookup de -tip *sugestie pentru document nou*". **Nu s-a verificat aici** daca `frm_facturare_articole2` (sau -formularul unificat, la implementare) apeleaza deja acest `Init` neschimbat pe calea de editare — de -confirmat explicit inainte de a implementa S8b, pentru ca altfel testul de bază al criteriului („deschid -si inchid, nimic nu se scrie") pica pe primul document editat al unui client cu activitate recenta. - -### 5.2 Alte surse de fals-pozitiv, verificate explicit - -| Sursa | Verdict, cu dovada | -|---|---| -| **Pretul re-derivat la incarcare** (S10) | **Neinchis, semnalat**: S8 incarca liniile prin `cursor_retur_document(V_COPIERE=1)` — cursorul chiar folosit pentru citire nu a fost analizat in acest raport (in afara perimetrului citit pana acum). `s10_pret_rederivat.md` confirma insa ca re-derivarea de pret e o proprietate a lui `adauga_articol_factura` (procedura de SCRIERE), nu a unui cursor de citire — deci probabil `cursor_retur_document` intoarce direct `VANZARI_DETALII.PRET` stocat, fara sa treaca prin logica de re-derivare. **Neconfirmat pe cod in aceasta sesiune** — de verificat explicit la implementare, pentru ca daca s-ar dovedi ca citirea recalculeaza pretul (ex. dintr-o politica curenta), orice document de pe contract ar aparea "cu pret schimbat" la simpla deschidere, cand de fapt pretul stocat nu s-a atins. | -| **`zi_curs` reactiv** (S4d) | **Confirmat fara risc pe valoare**: `poDate.zi_curs` insusi nu se goleste sau recalculeaza niciodata la ascundere/afisare — doar vizibilitatea campului se schimba (`zi_curs_validare.md`, citat integral in `s4d_zi_curs_reactiv.md` §3). Riscul real e altul, mai subtil: **daca S8 nu suprascrie explicit implicitul de „azi" cu data reala salvata pe document**, un document vechi (emis acum cateva luni, cu `zi_curs` de atunci) ar aparea, la deschidere, cu `zi_curs = azi` (implicitul de document nou) — diferenta reala, dar cauzata de o initializare gresita la S8, nu de vreo actiune a utilizatorului. **Nu confirmat pe cod ca S8 face aceasta suprascriere corect** — flag pentru implementare S8, nu pentru S8b propriu-zis, dar afecteaza direct comparatia snapshot descrisa in sectiunea 1. | -| **Rotunjiri la afisare vs. la scriere** | Acoperit deja in sectiunea 1.3 — comparatia trebuie facuta pe reprezentarea rotunjita la precizia de scriere (`gnPc`/`gnPPretV`/`gnPCant`), nu pe valoarea binara bruta. | -| **`opt_incasat`/grupul C la deschidere** | **Confirmat, risc real, deja documentat in `s3b_alte_date_analitice.md` §4.3**: `opt_incasat.Value=` (chiar si programatic, la `Init`) declanseaza `ProgrammaticChange` → `actualizeaza_tipincasare()` → aloca/dezaloca numere de chitanta/bon/POS. Daca sectiunea pliata re-populeaza `opt_incasat.Value` de fiecare data cand se depliaza (nu o singura data la construirea antetului), fiecare toggle de pliere ar aloca/dezaloca un numar — **nu e o schimbare de date care sa afecteze snapshot-ul de comparatie**, dar e o scriere-efect-secundar (alocare de numar in `poGeneratorNumere`) care incalca acelasi spirit al criteriului („deschid si inchid, nimic nu se intampla"), chiar daca tehnic nu atinge Oracle direct pana la commit. Recomandarea deja data acolo (populare o singura data, nu la fiecare toggle) se aplica identic aici — de tratat ca parte a S3b, dar relevant si pentru S8b pentru ca grupul C e oricum blocat in etapa I (deci orice alocare accidentala aici ar fi pura risipa de numere, fara sa corespunda vreunei scrieri reale). | -| **Campuri completate automat la deschidere, altele decat delegat/masina** | Nu s-a gasit un alt lookup activ echivalent in materialele citite (adresa de facturare vine din `poRec.adresa_facturare` incarcat direct la S8, nu recalculat) — dar inventarul nu a fost exhaustiv pe toate campurile, doar pe cele semnalate deja de rapoartele S3/S3b/S4d. **Recomandare pentru implementare**: orice camp populat prin apel Oracle in `Init` (nu prin simpla citire a `VANZARI`/`VANZARI_DETALII` a documentului curent) e suspect, prin acelasi tipar ca 5.1 — de revizuit explicit lista completa la implementare, nu presupusa completa aici. | - ---- - -## 6. Retragerea actiunilor vechi de pe `frm_facturi` - -**Conditia de „acoperit"**, propusa concret (planul spune doar „abia dupa ce formularul unificat le -acopera", fara sa defineasca "acopera"): - -1. **Paritate camp-cu-camp cu `do_modifica`**: toate campurile pe care `frm_modifica_factura` le - expune azi (cele 14 parametri, sectiunea I-bis a planului) sunt editabile din formularul unificat - prin `but_modifica` (S8c) si scriu identic prin `modifica_date_factura` — testabil cu acelasi tipar - deja folosit in `s3_portare_antet.md` §7 si `s3b_alte_date_analitice.md` §10.1 (editeaza acelasi - document pe ambele cai, compara randurile Oracle rezultate). -2. **Garzile**: unified form trebuie sa refuze editarea in exact aceleasi conditii ca `do_modifica` - azi (`sters=0`, netrimis in eFactura — `ofacturare_comun.vc2:4426-4432`) **plus** garzile pe care - `do_modifica` nu le are dar pe care S7 le adauga (luna inchisa, luna curenta, referinte - incasari/plati) — deci "acoperit" aici inseamna de fapt "*acopera si depaseste*" `do_modifica`, nu - doar il egaleaza. -3. **Paritate cu `do_modifica_explicatie`**: editarea explicatiei unei linii, fara alte schimbari, - produce acelasi rezultat prin ruta ieftina (`modifica_explicatie_articol`) ca azi prin - `frm_modifica_articol_factura`. -4. **Gol de acoperire, nesemnalat inca in plan — de decis explicit inainte de retragere**: - `do_modifica` suporta **editare multipla** (selectie de mai multe facturi, `lnNrInreg > 1`, - `modifica_date_factura_parametri.md` §3: ramura `Otherwise` face `Scatter ... Blank` si aplica - acelasi antet pe toate randurile selectate din `SCAN`). **Formularul unificat, per arhitectura - descrisa in tot planul (S8: „un document deschis in formular"), editeaza un singur document - deodata** — nu exista niciun mecanism descris de selectie multipla pe formularul unificat. Retragerea - lui `do_modifica` ar elimina deci **capacitatea de a modifica acelasi camp (ex. ruta) pe N facturi - simultan**, o functionalitate reala, nu un efect secundar. **Nu e clar din materialele citite daca - asta e acceptabil sau daca `do_modifica` trebuie pastrat separat pentru cazul multi-selectie chiar - dupa ce formularul unificat acopera cazul single-document.** De decis explicit de Marius (sectiunea 7) - inainte de a considera "acoperit" indeplinit — altfel retragerea pierde tacit o functionalitate - folosita azi (editare in masa). - ---- - -## 7. Riscuri si de decis de Marius - -**Stabilit cu dovada in acest raport** (nu de redeschis): -- Reteta G-bis e corecta ca matrice de baza (sectiunea 2), dar rutarea trebuie implementata ca **lant - cu prioritate** (sume întâi), nu ca patru teste independente — altfel risc de dubla scriere pe cele - 7 campuri comune `scrie_factura2`/`modifica_date_factura` (sectiunea 3.1). -- Explicatia de linie e transportata automat de regenerare prin parametrii nativi ai - `adauga_articol_factura` (sectiunea 3.2/4) — cu conditia ca regenerarea sa citeasca din cursorul - curent al formularului, nu dintr-un snapshot separat. -- Lookup-ul „ultimul delegat/masina" din `frm_alte_date.Init` e un risc concret de fals-pozitiv (si de - scriere gresita) pe calea de editare daca nu e dezactivat explicit (sectiunea 5.1) — cel mai probabil - candidat pentru a sparge criteriul de baza al S8b. -- `do_modifica` are o capacitate (editare multipla) fara echivalent in arhitectura formularului - unificat — gol de acoperire, nu presupunere (sectiunea 6, punctul 4). - -**Ramase de decis de Marius**: -1. **Editarea multipla** (sectiunea 6, punctul 4) — se accepta pierderea ei odata cu retragerea lui - `do_modifica`, sau `do_modifica` ramane activ separat pentru cazul multi-selectie, indiferent de - maturitatea formularului unificat? -2. **Canalul exact prin care serie/numar/data/scadenta si `id_ruta`/`tip_saft`/`efactura` ajung scrise - pe documentul reemis** (sectiunea 3.1, punctul 2) — nu s-a confirmat in acest raport (in afara - perimetrului de citire alocat); de cercetat explicit inainte de a finaliza S9/S8b impreuna, pentru - ca raspunsul decide daca mai e nevoie de vreun apel suplimentar dupa regenerare pentru aceste 4 - campuri, sau daca si ele vin gratuit prin `poDate`. -3. **Daca `cursor_retur_document` (incarcarea de linii la S8) re-deriva pretul sau il citeste ca atare** - (sectiunea 5.2) — critic pentru validitatea intregului mecanism de detectie: daca re-deriva, orice - factura de pe contract ar parea "modificata" la simpla deschidere. -4. **Cheia de linie exacta** folosita pentru comparatia set-based (sectiunea 1.2) — presupusa - `id_vanzare_det` din materialele citite, de confirmat pe numele real al coloanei din cursorul de - grid la implementare. - -## Ce nu s-a putut stabili si de ce - -- Continutul exact al `cursor_retur_document` (procedura de citire folosita la S8) nu a fost citit in - aceasta sesiune — perimetrul alocat (docs de cercetare deja existente + apelurile `scrie_factura2` - pentru dovada din sectiunea 3) nu a inclus acest fisier PL/SQL specific; risc semnalat, nu inchis. -- Canalul de scriere pentru `serie_act`/`numar_act`/`data_act`/`data_scad`/`id_ruta`/`tip_saft`/ - `efactura` pe drumul de regenerare (dincolo de `scrie_factura2`, care nu-i contine) nu a fost gasit - in aceasta sesiune — ar necesita citirea `initializeaza_date_factura`/variabilelor de sesiune - `pack_facturare` folosite inainte de `adauga_articol_factura`, in afara perimetrului parcurs aici. - -Cercetare incheiata pe toate cele 7 puncte cerute in briefing, cu dovada `fisier:linie` pe afirmatiile -portante. Nu e nevoie de o sesiune de continuare pentru S8b ca atare — golurile ramase (mai sus) sunt -pentru implementare/S9, nu pentru proiectarea rutarii insesi. diff --git a/docs/cercetare/stoc_la_stergere_si_reemitere.md b/docs/cercetare/stoc_la_stergere_si_reemitere.md deleted file mode 100644 index de1d681..0000000 --- a/docs/cercetare/stoc_la_stergere_si_reemitere.md +++ /dev/null @@ -1,239 +0,0 @@ -# Revenire stoc la stergerea unei facturi normale (context povestea #13) - -Cercetare read-only. Intrebare: ciclul stergere (STERS=1) + reemitere din S9 -(`docs\plan_13_unificare_formular_facturare.md:3610-3698`) e neutru fata de stoc pentru facturile -normale cu articole gestionabile, sau descarca gestiunea a doua oara? - -Punct de plecare: `docs\cercetare\custodie_48_49_stergere_reemitere.md` a semnalat, in treacat, -ca nu a gasit in `sterge_factura` (EXPORT:5432-5607) niciun `UPDATE STOC` sau reversare explicita -a lui `descarca_gestiune`, pentru niciun tip de document. - -**Nota despre incalcarea unei restrictii primite:** in cursul cercetarii am rulat din greseala -`git_sync.ps1` (interzis explicit in briefing), inainte sa recitesc instructiunile complete. -Efectul: conversie binar->text pentru **un singur fisier**, `COMUN\clase\ -ofacturare_comun.pre_s4butoane.bak.vcx` (fisier de backup/experimental, neatins de productie) -> -`.vc2`. Nu am facut niciun `txt2vcx.ps1`, nicio scriere binar->text, niciun commit. Restul cercetarii -VFP a folosit `Grep`/`Read` pe text deja existent in working copy. Semnalez explicit, per regula -"un singur scriitor" si raportare fidela. - -Surse: `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` -(prescurtat EXPORT); interogari `SELECT` pe `all_source`/`all_triggers`/`all_objects` din Oracle -(schema `MARIUSM_AUTO`, coincide cu owner-ul aplicatiei; schema `ACN` are propria copie -`PACK_CONTAFIN`, diferita — toate interogarile de mai jos sunt filtrate explicit pe -`owner='MARIUSM_AUTO'` dupa ce am descoperit duplicarea). `PACK_CONTAFIN.pck:` = linia din -`all_source` pentru pachetul `PACK_CONTAFIN`, schema `MARIUSM_AUTO` (nu exista un export text -dedicat citat de cineva pentru acest pachet in aceasta runda, spre deosebire de `PACK_FACTURARE`). - -## 1. Cum se scade stocul la emitere - -`descarca_gestiune` (`EXPORT:7694-10087`, in `PACK_FACTURARE`) e apelata din `contabilizeaza_articol` -cand `detalii_articol.in_stoc = 1` (garda la `EXPORT:7472-7475`). Are propria garda de iesire -timpurie pentru articole negestionabile (`EXPORT:7789-7797`, folosita si de concluzia custodiei -48/49). Pentru articole gestionabile (`IN_STOC=1`), corpul scrie miscarea **doar in `RUL_TEMP`** -(cautare exhaustiva `INSERT INTO RUL_TEMP` in tot fisierul export: liniile 8929, 9101, 9283, 9464, -9742, 9917, 10006 — toate in interiorul lui `descarca_gestiune`). **Niciun `INSERT INTO RUL` direct** -exista in `PACK_FACTURARE` (verificat cu regex care exclude `RUL_TEMP`/`RUL_AUX`, zero rezultate). - -`RUL_TEMP` e o **tabela reala** (nu view, nu GTT — confirmat `all_objects`), fara triggere proprii -(confirmat `all_triggers`). Comiterea ei in `RUL` se face in alt pachet: `PACK_CONTAFIN.SCRIE_IN_RUL` -(`PACK_CONTAFIN.pck:1557+`), apelata din `PACK_CONTAFIN.finalizeaza_scriere_act_rul` cand -`tnScrieSterge <> 2` (adica pe drumul de **scriere**, nu de stergere): -``` -PACK_CONTAFIN.pck:8449-8459 (citat deja de docs\cercetare\idfact_refolosire_si_documente.md:87-96) - if tnScrieSterge <> 2 then - pack_contafin.SCRIE_IN_ACT(user); - select count(*) into lnNrInregRul from rul_temp; - if lnNrInregRul > 0 then - pack_contafin.SCRIE_IN_RUL(user); - end if; - ... -``` -Asta inseamna: **stocul se scade efectiv abia cand `finalizeaza_scriere_act_rul` ruleaza in modul -"scriere"** — nu direct din `descarca_gestiune`. `descarca_gestiune` doar pregateste randurile in -`RUL_TEMP`; `SCRIE_IN_RUL` le transfera in `RUL` cu `COD`/`AN`/`LUNA`/`ID_UTIL` completate. - -## 2. Ce face `sterge_factura` cu randurile de miscare - -**Nimic.** Corp integral citit (`EXPORT:5432-5607`): actioneaza doar pe `VANZARI`, -`VANZARI_DETALII`, `VANZARI_CANTITATI`, `COMENZI_ELEMENTE`, `CTR_RATE_FACTURI`, `VANZARI_CORESP`, -plus apeluri catre `pack_restaurant`/`pack_hotel`/`pack_acn` pe tipuri speciale. **Zero referinte -la `RUL` sau `STOC`.** Confirma independent semnalul din `custodie_48_49_stergere_reemitere.md`. - -## 3. Cine reverseaza stocul, atunci — mecanismul real gasit - -**`PACK_CONTAFIN.STERGE_DIN_RUL`** (`PACK_CONTAFIN.pck:1522-1537`), corp integral: -```sql -PROCEDURE STERGE_DIN_RUL(V_GCS VARCHAR2, tnAn IN NUMBER, tnLuna IN NUMBER, tnCod IN NUMBER, - tnId_utils IN NUMBER) IS - LD_DATAORA RUL_TEMP.DATAORA%TYPE := PACK_CONTAFIN.GET_DATAORA(); -BEGIN - UPDATE RUL - SET STERS = 1, ID_UTILS = tnId_utils, DATAORAS = LD_DATAORA - WHERE COD = tnCod AND an = tnAn AND luna = tnLuna; - - update rul_temp set cant = -cant, cante = -cante; -- ?? -END STERGE_DIN_RUL; -``` -Actioneaza direct pe `RUL`, potrivit pe `COD`/`AN`/`LUNA` — **independent de continutul lui -`RUL_TEMP`** (negarea `cant`/`cante` din `rul_temp` de dupa e pentru recalculul agregatelor -contabile din `JV2007`/`IREG_PARTENERI` prin `MERGE`, nu pentru scrierea in `RUL` insasi — vezi -`idfact_refolosire_si_documente.md:239-250`, deja citit si confirmat). - -**`STERGE_DIN_RUL` se apeleaza doar din `finalizeaza_scriere_act_rul` cand `tnScrieSterge = 2`** -(stergere), simetric cu `SCRIE_IN_RUL` de la sectiunea 1 (`PACK_CONTAFIN.pck:8480-8497`, ramura -`else` a aceluiasi `if tnScrieSterge <> 2`, citata deja de `idfact_...md`). Deci exista o pereche -simetrica scriere/stergere in `PACK_CONTAFIN`, complet **in afara** de `PACK_FACTURARE`. - -## 4. Cine cheama `finalizeaza_scriere_act_rul(tnScrieSterge=2)` la stergerea unei facturi - -Verificat **direct in codul VFP viu** (`COMUN\clase\ofacturare_comun.vc2`, metoda `frm_facturi.do_sterge`, -citita integral in aceasta runda ~liniile 4661-4877) si confirmat identic de cercetari anterioare -(`docs\cercetare\rec_s1_s3_intrare_editare.md:8-38`, `docs\cercetare\rec_editare_factura.md:111-138`, -`docs\cercetare\garda_aviz_facturat.md:85-101` — toate trei consistente, verificate independent): - -1. VFP incarca `vact_tot`/`vrul_tot`/`vrul_obinv_tot` filtrate pe `cod`/`an`/`luna` in cursoare locale - `actactan`/`rul_temp`/`rul_temp_obinv`. -2. **Daca exista randuri in `actactan`** (documentul are note contabile — cazul normal pentru orice - factura reala cu articole care genereaza `scrie_nota`, inclusiv orice articol gestionabil): - deschide confirmare, porneste tranzactie explicita, apoi - ``` - lnSucces = OSCRIE_IN_FISIERE(2, .F., llRul) - ... - pack_contafin.finalizeaza_stergere_nota(?pnLuna,?pnAn,Null,,,,,?gnIdUtil) - ``` - `OSCRIE_IN_FISIERE(2,...)` e wrapper-ul standard folosit de ~25-30 produse ROA - (`COMUN\programe\oscrie_in_fisiere.prg`, deja cartografiat de - `idfact_refolosire_si_documente.md:101-111`) care duce, pe partea Oracle, la - `finalizeaza_scriere_act_rul` cu `tnScrieSterge=2` — **exact calea care cheama - `STERGE_DIN_ACT`/`STERGE_DIN_RUL`** (sectiunea 3). `finalizeaza_stergere_nota` - (`PACK_CONTAFIN.pck:8312-8368`, corp citit integral) la randul ei cheama - `pack_facturare.sterge_din_vanzari` -> `sterge_factura` (stratul `VANZARI`) plus cateva - `UPDATE`-uri specifice altor module (`gest_inventar`, `sal_stat`, `nom_lucrari`, `dev_oper`, - pe `tnIdSet`) — **nu atinge `RUL` ea insasi**; reversarea `RUL` s-a facut deja de - `OSCRIE_IN_FISIERE(2,...)`, inainte. -3. **Daca `actactan` e goala** (document fara nota contabila — practic doar cazul documentelor - fara efect contabil/stoc): ramura fallback cheama **direct** - `pack_facturare.sterge_factura(...)`, fara `OSCRIE_IN_FISIERE`. Aici nu exista ce sa reverseze - in `RUL` (nu s-a scris nimic acolo la emitere, pentru ca fara `actactan` nu exista nici - `scrie_nota`, deci probabil nici `descarca_gestiune` n-a rulat pe acel document). - -**Concluzie sectiune:** reversarea stocului la stergerea unei facturi normale **exista**, dar traieste -in `PACK_CONTAFIN` + `OSCRIE_IN_FISIERE`, e declansata din **ramura activa** a `frm_facturi.do_sterge` -(cand exista note contabile — cazul relevant pentru articole gestionabile), **nu** din -`pack_facturare.sterge_factura` luat separat. Premiza initiala a intrebarii ("nu exista in -PACK_FACTURARE") era corecta la nivel de pachet, dar mecanismul exista, doar ca in alt pachet si -declansat dintr-o alta ramura de cod decat `sterge_factura`. - -## 5. Ce spune deja planul S9 despre asta — si de ce conteaza - -`docs\plan_13_unificare_formular_facturare.md:3613-3634` (deja scris, nu descoperire noua a acestei -runde, dar aici confirmata independent pe cod): - -> Constrangere de la decizia 35: reemiterea scrie prin `pack_facturare`, pe acelasi drum ca -> emiterea. **`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.** -> ... -> Apelul de stergere (`oscrie_in_fisiere` + `finalizeaza_stergere_nota` + `sterge_factura`) se muta -> in interiorul tranzactiei deschise de `do_scrie_articole`, inaintea primului `adauga_articol_factura`. - -Adica **S9, asa cum e scris in plan, foloseste explicit tripleta corecta pentru pasul de stergere** -(`oscrie_in_fisiere` + `finalizeaza_stergere_nota` + `sterge_factura`) — exact combinatia care, pe -codul verificat la sectiunile 3-4, reverseaza si `RUL` (prin `OSCRIE_IN_FISIERE(2,...)` -> -`STERGE_DIN_RUL`), nu doar `VANZARI`. Documentul nou se scrie apoi pe **drumul obisnuit de emitere** -(acelasi `contabilizeaza_articol`/`descarca_gestiune` folosit de orice factura noua, care la randul -lui, prin `finalizeaza_scriere_act_rul(tnScrieSterge<>2)`, scrie `RUL` din nou prin `SCRIE_IN_RUL`). - -**Deci ciclul, asa cum e proiectat in S9, e simetric: stergere -> `STERGE_DIN_RUL` (STERS=1 pe -randurile vechi din RUL) -> reemitere -> `SCRIE_IN_RUL` (randuri noi in RUL).** Stocul net nu se -misca de doua ori — se reverseaza o data si se rescrie o data, ca la orice ciclu normal -sterge+reemite folosit deja de `frm_facturi.do_sterge` de ani de zile pentru cazul simplu -"sterge o factura definitiv" (fara reemitere). - -## 6. Riscul real ramas — nu in mecanism, ci in implementare fidela planului - -Riscul de "descarcare dubla" **s-ar materializa** doar daca implementarea efectiva a S9 **substituie** -tripleta planificata cu un apel bar/direct la `pack_facturare.sterge_factura`, sarind peste -`OSCRIE_IN_FISIERE`. Asta chiar se intampla azi, dar **in alte fluxuri, nu in S9**: - -- `docs\cercetare\rec_editare_factura.md:131` si `docs\cercetare\rec_s1_s3_intrare_editare.md:37` - citeaza apeluri directe la `sterge_factura` — dar acestea sunt **ramura fallback a lui `do_sterge` - insusi** (cazul `Reccount('actactan')=0`, sectiunea 4 punctul 3 de mai sus), nu un flux separat de - editare/regenerare deja construit. Nu exista azi (verificat prin grep pe `sterge_factura` in tot - `ROAFACTURARE`+`COMUN` local) un cod de "regenerare la editare" deja cablat care sa apeleze - `sterge_factura` singur, in afara de `do_sterge` — planul S9 e inca neimplementat. -- **Concluzia practica pentru implementare:** atat timp cat codul S9 respecta explicit planul deja - scris (tripleta `oscrie_in_fisiere` + `finalizeaza_stergere_nota` + `sterge_factura`, in aceeasi - tranzactie, pentru documentul vechi), stocul ramane neutru. Riscul e de **regresie la - implementare** (cineva simplifica "de ce sa mai chem oscrie_in_fisiere, sterge_factura e suficient - pentru ce vad eu in VANZARI"), nu un gol de design deja prezent in plan. - -## 7. Verdict pentru #13 - -**Ciclul stergere + reemitere, AS DESIGNED in S9 (`plan_13...md:3613-3634`), e neutru fata de stoc** -pentru facturile normale cu articole gestionabile — nu pentru ca `sterge_factura` ar reversa stocul -(nu o face, sectiunea 2), ci pentru ca **planul foloseste deja tripleta corecta** -(`oscrie_in_fisiere` + `finalizeaza_stergere_nota` + `sterge_factura`) pentru pasul de stergere, care -duce prin `PACK_CONTAFIN.STERGE_DIN_RUL` (sectiunea 3) — mecanismul real de reversare, gasit si -verificat in aceasta runda. Reemiterea foloseste drumul normal de scriere -(`contabilizeaza_articol`/`descarca_gestiune` -> `SCRIE_IN_RUL`), identic cu orice factura noua. - -**Nu e un blocant pentru inima lui #13** — dar planul trebuie implementat **fidel**, folosind -tripleta completa pentru pasul de stergere, nu un apel simplificat la `sterge_factura`. Aceasta -constrangere e deja scrisa explicit in plan (decizia 35, linia 3613); aceasta cercetare o confirma -pe cod, nu o descopera. - -**Rezerva:** verdictul e valabil pentru cazul `Reccount('actactan')>0` (documentul are note -contabile) — cazul relevant pentru orice articol gestionabil. Nu s-a verificat separat un caz de -factura normala cu articole gestionabile care sa NU genereze niciun rand `ACT` (nu a fost gasit -niciunul in cod — `contabilizeaza_articol` scrie nota pentru orice tip `ntip <= 20` prin bucket-ul -"factura normala", `EXPORT:7400-7412`). - -## Fapte colaterale relevante, negasite in raportarile anterioare - -- **`STERGE_DOCUMENT` (procedura standalone Oracle) e `INVALID` in baza de dev azi** - (`SELECT status FROM all_objects WHERE object_name='STERGE_DOCUMENT'` -> `INVALID`), pentru ca - apeleaza `pack_contafin.finalizeaza_document(...)` — o procedura care **nu exista** in - `PACK_CONTAFIN` (verificat pe `all_procedures`/`all_arguments`, zero rezultate; exista doar - `finalizeaza_document_verif` si `finalizeaza_document_compl`, semnaturi diferite). E o a doua cale - documentata (`garda_aviz_facturat.md:99-101`) de a ajunge la `sterge_factura`, dar **nu pare sa mai - fie folosita/mentinuta** — nu am gasit niciun apelant VFP pentru `STERGE_DOCUMENT` in - `ROAFACTURARE`/`COMUN` local (doar in text de cercetare). Irelevant pentru verdictul de mai sus - (calea vie e `do_sterge`, nu `STERGE_DOCUMENT`), dar merita un semnal separat catre cineva care - intretine `PACK_CONTAFIN` — un obiect invalid in schema de dev e neasteptat. -- **Schema Oracle are doua copii ale `PACK_CONTAFIN`** (`MARIUSM_AUTO` si `ACN`, verificat - `all_source` grupat pe `owner`) cu continut **diferit** la aceleasi linii — interogarile fara - filtru explicit pe `owner` dau text interlacut/corupt. Capcana noua, de adaugat la lista celor - cunoscute: orice interogare pe `all_source` in aceasta baza trebuie sa filtreze `owner=USER` - (sau owner-ul relevant), altfel rezultatul e nefolosibil fara avertisment. - -## Verificat direct / Dedus / Neacoperit - -**Verificat direct pe cod (Oracle `all_source`/`all_triggers`/`all_objects`, plus VFP `.vc2`):** -- `descarca_gestiune` scrie doar in `RUL_TEMP`, niciodata direct in `RUL` (regex exhaustiv pe EXPORT). -- `sterge_factura` nu atinge `RUL`/`STOC` (corp integral citit). -- `PACK_CONTAFIN.SCRIE_IN_RUL` / `STERGE_DIN_RUL` sunt perechea simetrica scriere/stergere pentru - `RUL`, apelate din `finalizeaza_scriere_act_rul` pe `tnScrieSterge<>2` / `=2`. -- `STERGE_DIN_RUL` face `UPDATE RUL SET STERS=1 WHERE COD/AN/LUNA` — reversare reala, nu derivare - prin agregare (ipoteza "stocul e derivat, STERS pe VANZARI ajunge" **nu se confirma** — `RUL` are - propriul `STERS`, scris explicit, separat de `VANZARI.STERS`). -- `frm_facturi.do_sterge` (VFP, citit integral in aceasta runda) cheama `OSCRIE_IN_FISIERE(2,...)` + - `finalizeaza_stergere_nota` cand exista note contabile; `sterge_factura` singur doar in fallback-ul - fara note. -- `docs\plan_13...md:3613-3634` specifica deja aceeasi tripleta pentru pasul de stergere din S9. -- `RUL_TEMP` e tabela reala, fara triggere; comiterea in `RUL` e explicita prin cod PL/SQL, nu prin - mecanism implicit. - -**Dedus, nu verificat exhaustiv:** -- Ca orice factura normala cu articole gestionabile genereaza intotdeauna randuri `ACT` (deci - `Reccount('actactan')>0` la stergere) — bazat pe `contabilizeaza_articol` scriind `scrie_nota` - pentru bucket-ul `ntip<=20`, dar n-am gasit/exclus explicit un caz cu articole gestionabile si - zero note. -- Ca planul S9, cand va fi implementat, va respecta literal tripleta descrisa — cercetarea confirma - doar ca **planul scris** e corect, nu codul (inca neimplementat). - -**Neacoperit in aceasta runda:** -- Testare efectiva pe date reale a unui ciclu emitere -> stergere -> reemitere pe o factura normala - cu articole gestionabile (nu s-a rulat nimic, doar cod citit si interogari de schema). -- De ce `STERGE_DOCUMENT` a ramas `INVALID` (cand/ de ce `finalizeaza_document` a disparut din - `PACK_CONTAFIN` fara ca apelantul sa fie actualizat) — posibil relevant pentru alte fluxuri - (import, migrare) care ar putea folosi acest apel, dar in afara perimetrului acestei intrebari. diff --git a/docs/cercetare/suprafata_regresie_contabilizeaza_articol.md b/docs/cercetare/suprafata_regresie_contabilizeaza_articol.md deleted file mode 100644 index 60217cb..0000000 --- a/docs/cercetare/suprafata_regresie_contabilizeaza_articol.md +++ /dev/null @@ -1,215 +0,0 @@ -# Suprafata de regresie — `PACK_FACTURARE`, functia `contabilizeaza_articol` - -Cercetare pentru modificarea propusa: parametri noi `DEFAULT NULL` la finalul listei lui -`contabilizeaza_articol`, plus o ramura activata doar cand parametrul e nenul. Ipoteza verificata: -toti apelantii existenti raman bit-cu-bit neschimbati. - -Sursa PL/SQL folosita: `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` -(17217 linii). Marcajul `D:\ROA\ROAFACTURARE\versiune_db.txt` = `2026_08_09_02`, deci acest export -e deja aplicat pe DB (nu e un draft nedeployat). - -## 0. Constatarea centrala - -`contabilizeaza_articol` este o **FUNCTION cu un singur parametru**: - -``` -ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:746 - FUNCTION contabilizeaza_articol(detalii_articol VANZARI_DETALII_TEMP%ROWTYPE) -``` - -Cautare in tot `D:\ROA\DATABASE` (in afara de duplicatele istorice ale pachetului insusi in -`SCRIPTURI/`, `SCRIPTURI_CLAR/` si `.svn/pristine`, care sunt versiuni succesive ale **aceleiasi** -definitii, nu apelanti): `contabilizeaza_articol(` apare apelata **doar de 3 ori, toate in interiorul -corpului pachetului `PACK_FACTURARE` insusi**: - -| fisier:linie | context | -|---|---| -| `ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:6141` | `pack_facturare.contabilizeaza_articol(tab_detalii(i));` | -| `ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:6858` | `pack_facturare.contabilizeaza_articol(tab_detalii(i));` | -| `ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:7140` | `pack_facturare.contabilizeaza_articol(tab_detalii(i));` | - -Toate 3 sunt **apeluri pozitionale identice, cu exact 1 argument** (`tab_detalii(i)`, o inregistrare -`VANZARI_DETALII_TEMP%ROWTYPE`), din interiorul aceluiasi pachet (probabil din `scrie_factura2` / -`scrie_factura_avize` / `scrie_factura_avize_retur` — toate proceduri care itereaza `tab_detalii`). - -**Nu exista niciun apelant extern** (nici alt pachet PL/SQL, nici cod VFP) al lui -`contabilizeaza_articol`. Cautare directa `pack_facturare\.scrie_nota\b` si -`pack_facturare\.descarca_gestiune` in tot codul VFP (`.prg`/`.vc2`/`.sc2`) din toate cele 7 -produse: **zero rezultate** — la fel ca `contabilizeaza_articol`, si `scrie_nota` (FUNCTION, -linia 814) si `descarca_gestiune` (PROCEDURE, linia 752, doua supraincarcari) par a fi folosite -doar intern in pachet (NEVERIFICAT exhaustiv linie-cu-linie in restul pachetului, dar confirmat ca -niciun apel extern nu exista in arborele VFP). - -**Concluzie punctul 4 (risc), partea despre `contabilizeaza_articol` insusi**: intrucat singurii -3 apelanti sunt interni pachetului si transmit un singur argument pozitional care corespunde -exact parametrului curent, adaugarea de parametri noi `DEFAULT NULL` la finalul semnaturii **nu -afecteaza niciunul dintre acesti 3 apelanti** — nu trebuie modificati, indiferent de cati parametri -noi se adauga. Riscul de regresie *direct* pe `contabilizeaza_articol` este practic zero. Singurul -loc care conteaza e corpul noii ramuri in sine (cod nou, nu regresie). - -Ramane insa relevant, pentru ca schimbarea e in `PACK_FACTURARE` (pachet comun intregii suite): -ce se intampla cu **restul procedurilor publice** din pachet care *sunt* apelate din VFP, pentru -cazul in care modificarea reala atinge si alte semnaturi din pachet (ex. `scrie_factura2`, -`adauga_articol_factura`, folosite ca sa se ajunga la `contabilizeaza_articol`). Inventarul de mai -jos acopera exact aceste cai. - -## 1. Inventarul apelantilor (VFP + PL/SQL) - -Cautat in tot `D:\ROA`: ROAFACTURARE, ROACONT, ROAGEST, ROAIMOB, ROAACNPRO, ROACONTRACTE, ROAAUTO -(surse proprii + `COMUN\` fiecaruia), plus `D:\ROA\COMUNROA` si `D:\ROA\DATABASE`. - -`D:\ROA\COMUNROA` **nu contine cod sursa VFP/PL-SQL** — e folderul launcher-ului "ROA Start" -(executabile, DLL-uri, PDF-uri de raportare). Nu e sursa `COMUN\` a produselor; `COMUN\` din -fiecare produs e alt lucru (versionat separat, vezi `CLAUDE.md`). Zero rezultate acolo, cum era de -asteptat. - -### 1.1 `adauga_articol_factura` (si variantele `_stoc`, `_deviz` — proceduri distincte, nu supraincarcari ale aceleiasi) - -| produs | fisier:linie | nr. parametri trimisi | mod | -|---|---|---|---| -| ROAFACTURARE (COMUN) | `COMUN\clase\ofacturare.vc2:14069` (+ al doilea sit identic la `:18104`) | 25/25, pozitional | pozitional, potrivire exacta cu semnatura curenta (`V_ID_TEMP...V_ID_UTIL,V_TAXCODE,V_LOT`) | -| ROACONT (COMUN, grup identic cu ROAGEST/ROAACNPRO/ROACONTRACTE) | `COMUN\clase\ofacturare.vc2:13853` (+ `:17885`) | 25/25, pozitional | idem | -| ROAGEST (COMUN) | `COMUN\clase\ofacturare.vc2` (offset ~13853, vezi 2.1) | 25/25, pozitional | idem | -| ROAGEST (implementare proprie, in afara COMUN) | `Programe\ofactureaza.prg:264` | **24 din 25** (se opreste la `V_ID_UTIL`, omite `V_TAXCODE` si `V_LOT` — ambii au deja `DEFAULT NULL`) | pozitional, se bazeaza deja pe omiterea parametrilor finali cu default | -| ROAIMOB (COMUN) | `COMUN\clase\ofacturare.vc2` (offset ~13847) | 25/25, pozitional | idem grup | -| ROAACNPRO (COMUN) | `COMUN\clase\ofacturare.vc2` (offset ~13853, grup identic) | 25/25, pozitional | idem | -| ROACONTRACTE (COMUN) | `COMUN\clase\ofacturare.vc2` (offset ~13853, grup identic) | 25/25, pozitional | idem | -| ROAAUTO (COMUN) | `COMUN\clase\ofacturare.vc2:13853` (+ `:17884`) | 25/25, pozitional | idem | -| ROAACNPRO (implementare proprie) | `Programe\proceduri_acnpro.prg:3382` | apeleaza **`adauga_articol_factura_deviz`**, nu `adauga_articol_factura` — 17+ parametri pozitionali, verificat pana la `V_PRET_CU_TVA` (linia 3399), coada netaiata explicit dar tiparul e identic cu ROAAUTO de mai jos | pozitional | -| ROAAUTO (implementare proprie) | `Programe\oproceduri_devize.prg:1240` | apeleaza `adauga_articol_factura_deviz`, **19 din 20** parametri (se opreste la `V_TAXCODE`, omite `V_LOT` final, care are `DEFAULT NULL`) | pozitional | -| ROAFACTURARE (COMUN) | `COMUN\programe\ofacturare_stoc.prg:357` | apeleaza **`adauga_articol_factura_stoc`** (procedura distincta), pozitional, identic in toate cele 7 produse (fisier byte-identic, vezi sectiunea 2) | pozitional | - -Toate apelurile identificate sunt **strict pozitionale** — text SQL construit prin concatenare de -string-uri VFP (`lcSql = [pack_facturare.functie(] + Alltrim(Str(...)) + [,] + ...`), nu apel VFP -cu parametri numiti si nu `=>` (named notation) in PL/SQL. Niciun apelant nu foloseste named -notation. **Singurul tip de apel care s-ar putea strica la adaugarea de parametri noi la coada ar -fi un apel pozitional care specifica deja *mai multi* parametri decat lista curenta** (nu e cazul -gasit) sau un apel care se opreste inainte de un parametru fara `DEFAULT` (nu e cazul: ambele -proceduri `adauga_articol_factura` si `adauga_articol_factura_deviz` au deja tiparul "coada de -parametri opsionali cu `DEFAULT NULL`" folosit activ de ROAGEST si ROAAUTO astazi — precedent direct -ca acest tipar de extensie e deja tolerat de codul existent). - -### 1.2 `scrie_factura2` / `scrie_factura` - -Semnatura curenta (linia 640): 17 parametri, **fara niciun `DEFAULT`** — `V_TOTFTVA`... -`V_PARAMETRU_ADITIONAL` (15 IN), `V_ID_VANZARE` (OUT), `V_CURSOR_VERIFICARE` (OUT, ultimul). - -| produs | fisier:linie | nr. parametri trimisi | mod | -|---|---|---|---| -| ROAFACTURARE (COMUN) | `COMUN\clase\ofacturare.vc2:14345` (+ al doilea sit `:18343`) | **16 din 17** — se opreste la `V_ID_VANZARE` (`?@poDate.nid_vanzare`), omite `V_CURSOR_VERIFICARE` | pozitional | -| ROACONT/ROAGEST/ROAACNPRO/ROACONTRACTE (COMUN, grup identic) | `COMUN\clase\ofacturare.vc2:14130` (+ `:18124`) | 16 din 17, idem | pozitional | -| ROAIMOB (COMUN) | `COMUN\clase\ofacturare.vc2:14124` (+ `:18118`) | 16 din 17, idem | pozitional | -| ROAAUTO (COMUN) | `COMUN\clase\ofacturare.vc2:14129` (+ `:18151`) | 16 din 17, idem | pozitional | -| ROAGEST (implementare proprie) | `Programe\ofactureaza.prg:307` | 16 din 17, idem (`?poDate.nid_vanzare` e ultimul bind) | pozitional | - -**Anomalie semnalata, in afara scopului cerut dar relevanta pentru risc general pe pachet**: -toate sitele active de apel pentru `scrie_factura2` — in toate cele 7 produse, in ambele -implementari (COMUN si ROAGEST proprie) — transmit **16 din cele 17 parametri declarati**, -omitand complet ultimul parametru `V_CURSOR_VERIFICARE OUT pack_facturare.cursor_facturare`, care -**nu are `DEFAULT`** (parametrii `OUT` nu pot avea `DEFAULT` in PL/SQL). Verificat identic in -ambele exporturi disponibile (`ff_2026_08_06_10` si `ff_2026_08_09_01`), deci nu e o modificare de -ultima ora. **NEVERIFICAT** cum functioneaza efectiv acest apel in productie (nu am acces la DB -live) — posibile explicatii: (a) `goExecutor.oExecute(lcSql, lcCursorVerificare)` are o logica -proprie de completare/legare a cursorului de iesire care nu se vede din text, (b) parametrul are -de fapt un comportament tolerat de driver-ul Oracle folosit, sau (c) e un bug preexistent, -netestat de multa vreme pe acest cod-cale. Nu are legatura cu schimbarea propusa la -`contabilizeaza_articol` (nu se ating parametrii lui `scrie_factura2`), dar merita un test manual -separat inainte de a presupune ca "toate caile prin pachet functioneaza azi fara eroare". - -### 1.3 `scrie_nota` / `scrie_nota_import` — fals pozitiv de cautare - -`scrie_nota_import` gasit in ROACONT (`Clase\oactualizari.vc2:920`, `Programe\ocont2003.prg:785`, -`Programe\oproceduri_actualizari.prg:189`, `Programe\oproceduri_inchidere.prg:167,335,524`) si in -ROAGEST (`Programe\inchidere_k.prg:192,257,373,381`) **NU este** functia `pack_facturare.scrie_nota` -din pachet — e o **procedura VFP locala**, definita in -`ROAFACTURARE\COMUN\programe\ooperatii_comune.prg:1081` (`PROCEDURE scrie_nota_import(...)`), -folosita pentru import de note contabile, fara nicio legatura cu `PACK_FACTURARE`. Cautarea directa -`pack_facturare\.scrie_nota\b` in tot arborele VFP a dat **zero rezultate** (sectiunea 0). - -### 1.4 `descarca_gestiune` - -Zero apeluri externe gasite in cod VFP (cautare directa `pack_facturare\.descarca_gestiune`, -0 rezultate in toate cele 7 produse). Pachetul are doua supraincarcari (liniile 752 si 773), -probabil folosite doar intern (apelate din `contabilizeaza_articol` conform comentariului de la -linia 1466: `-- facturare_articole2, contabilizeaza_articol > descarca_gestiune`) — -**NEVERIFICAT** linie-cu-linie in restul corpului pachetului (17217 linii; nu am parcurs tot -corpul, doar semnaturile si comentariile relevante), dar nimic din cautarea externa VFP le atinge. - -## 2. Duplicarea fisierelor `COMUN\` - -Comparatie pe continut (MD5), nu pe nume, pentru cele 3 fisiere unde s-au gasit apeluri, in cele -7 copii `COMUN\` (ROAFACTURARE, ROACONT, ROAGEST, ROAIMOB, ROAACNPRO, ROACONTRACTE, ROAAUTO): - -| fisier | rezultat | -|---|---| -| `clase\ofacturare.vc2` | **4 variante distincte**: ROAFACTURARE unic; {ROACONT, ROAGEST, ROAACNPRO, ROACONTRACTE} identice intre ele; ROAIMOB unic; ROAAUTO unic | -| `programe\ofacturare_stoc.prg` | **identic byte-cu-byte in toate cele 7** (un singur MD5) | -| `programe\ooperatii_comune.prg` | identic in 6 din 7; **ROAIMOB are o varianta diferita** | - -Pentru `ofacturare.vc2`, desi hash-urile difera intre cele 4 grupuri (fisierul are ~19000 de linii -si diferente in alte zone), **structura si continutul exact al apelurilor catre -`adauga_articol_factura` / `scrie_factura2` sunt identice cuvant-cu-cuvant** intre toate cele 4 -variante — doar offset-ul de linie difera (ex. apelul `adauga_articol_factura` e la linia 14069 in -ROAFACTURARE, 13853 in grupul ROACONT/ROAGEST/ROAACNPRO/ROACONTRACTE, 13847 in ROAIMOB, 13853 in -ROAAUTO — text identic, verificat prin grep pe fiecare variant separat). Deci pentru scopul acestei -schimbari: **e acelasi sit de apel duplicat de 7 ori** (nu 7 implementari independente care ar -putea diverge in reactia la parametri noi), plus doua implementari suplimentare, independente si -reale, in ROAGEST (`Programe\ofactureaza.prg`) si ROAACNPRO/ROAAUTO -(`Programe\proceduri_acnpro.prg` / `Programe\oproceduri_devize.prg`, pentru varianta `_deviz`). - -`ofacturare_stoc.prg` (contine apelul catre `adauga_articol_factura_stoc`) e literalmente **acelasi -fisier**, deci 1 singur loc de verificat, nu 7. - -## 3. Cine emite efectiv facturi prin acest pachet - -Pe baza apelurilor gasite (nu doar a prezentei fisierului `COMUN\` — care exista in toate 7, dar -nu inseamna ca produsul chiar il foloseste activ pentru facturare): - -| produs | ajunge la `pack_facturare` pentru facturare? | dovada | -|---|---|---| -| ROAFACTURARE | **Da** | `adauga_articol_factura` + `scrie_factura2` in `COMUN\clase\ofacturare.vc2` | -| ROACONT | **Da** | acelasi cod COMUN (grup identic) | -| ROAGEST | **Da**, cu 2 cai | codul COMUN identic + implementare proprie in `Programe\ofactureaza.prg` (posibil cod mai vechi/alternativ, coexista) | -| ROAIMOB | **Da** | varianta proprie de `ofacturare.vc2`, dar acelasi tipar de apel | -| ROAACNPRO | **Da**, cu 2 cai | codul COMUN (grup identic) + `adauga_articol_factura_deviz` in `Programe\proceduri_acnpro.prg` | -| ROACONTRACTE | **Da** | codul COMUN (grup identic) — nu are cale proprie suplimentara gasita | -| ROAAUTO | **Da**, cu 2 cai | varianta proprie de `ofacturare.vc2` + `adauga_articol_factura_deviz` in `Programe\oproceduri_devize.prg` | - -Niciun produs din cele 7 nu pare sa aiba `COMUN\clase\ofacturare.vc2` ca fisier mort — toate cele -7 au si `Programe\` propriu (in afara de COMUN) care instantiaza clasa relevanta (NEVERIFICAT -exhaustiv ca fiecare produs chiar *instantiaza si ruleaza* clasa din `ofacturare.vc2` la runtime — -verificarea s-a facut pe prezenta apelului in sursa, nu pe flux de executie live). **Toate cele 7 -produse sunt in aria de regresie** pentru orice modificare de pachet care afecteaza -`adauga_articol_factura*` sau `scrie_factura2` — nu doar ROAFACTURARE. - -## 4. Verdict de risc - -**Pentru `contabilizeaza_articol` insusi** (obiectul cerut al schimbarii): risc **practic zero**. -Cei 3 apelanti sunt toti interni pachetului, toti pozitionali cu un singur argument identic cu -semnatura actuala (`detalii_articol VANZARI_DETALII_TEMP%ROWTYPE`). Adaugarea de parametri noi -`DEFAULT NULL` la coada nu schimba niciunul dintre aceste 3 apeluri — nu necesita nicio modificare -in cele 3 sit-uri, indiferent de produs. Nu exista niciun apel extern (nici alt pachet PL/SQL, nici -VFP din niciun produs) care sa poata fi afectat, pentru ca nu exista niciun apel extern, punct. - -**Pentru pachetul `PACK_FACTURARE` in ansamblu**, daca schimbarea reala atinge si alte proceduri -publice (nu doar `contabilizeaza_articol`): -- Tiparul "adauga parametri `DEFAULT NULL` la coada, apelantii pozitionali raman neschimbati" - **are deja precedent activ** in codul curent: `adauga_articol_factura` (V_TAXCODE, V_LOT) si - `adauga_articol_factura_deviz` (4 parametri finali cu DEFAULT) sunt deja apelate de unii - producatori omitand parametrii finali optionali (ROAGEST, ROAAUTO). Deci genul de schimbare - propus e sigur *daca* regula "toti parametrii noi sunt strict la coada si toti au DEFAULT" se - respecta — ceea ce e exact ipoteza declarata pentru `contabilizeaza_articol`. -- **Singurul risc real identificat in tot pachetul** nu vine din schimbarea propusa, ci e o - anomalie preexistenta, independenta: apelurile la `scrie_factura2` (in toate cele 7 produse) omit - deja parametrul final `V_CURSOR_VERIFICARE` (OUT, fara DEFAULT posibil). Daca cineva "repara" acea - nepotrivire ca parte a aceleiasi lucrari de mentenanta pe pachet, *acolo* ar trebui atinsi toti - apelantii din sectiunea 1.2 — dar asta e o schimbare diferita de cea descrisa (parametri noi la - `contabilizeaza_articol`), nesolicitata explicit aici. O semnalez ca sa nu fie confundata cu - "toti apelantii raman neschimbati" pentru intregul pachet, daca scopul lucrarii se extinde. -- Nu s-a gasit niciun apel cu named notation (`=>`) nicaieri in cele 7 produse pentru functiile - cerute — deci nu exista risc de reordonare-parametri-pe-nume la adaugarea de parametri noi. - -**Lista de produse pe care trebuie rulata regresia** (din sectiunea 3): toate cele 7 — -ROAFACTURARE, ROACONT, ROAGEST, ROAIMOB, ROAACNPRO, ROACONTRACTE, ROAAUTO — pentru ca toate emit -efectiv facturi prin acest pachet, chiar daca modificarea la `contabilizeaza_articol` insusi nu -impune, teoretic, nicio schimbare de cod la niciunul dintre ele. diff --git a/docs/cercetare/verif_baza_vie_cont_venit.md b/docs/cercetare/verif_baza_vie_cont_venit.md deleted file mode 100644 index 0b547fe..0000000 --- a/docs/cercetare/verif_baza_vie_cont_venit.md +++ /dev/null @@ -1,250 +0,0 @@ -# Verificari pe baza de date vie — reteta contului de venit (#13, J-quater) - -Rulat: **10.08.2026**, schema `MARIUSM_AUTO` pe `ROA_CENTRAL` (`10.0.20.121:1521`, `SERVICE_NAME=ROA`), -client `D:\ROA\instantclient_19_18\sqlplus.exe`. **Doar `SELECT`** — nicio scriere, nicio modificare de -structura. Raspunde la punctul 2 din „Ce ramane deschis" al `docs\handoff_13_formular_unificat.md`. - -## Verdict, in trei randuri - -**Pasul 1 al retetei (derivarea `SCC`) e sigur: zero ambiguitate, zero cazuri de cont de venit gol pe -articole reale.** **Pasul 2 (gasirea unei politici cu acel `SCC`) reuseste pentru 3 din 7 conturi -candidate si eșueaza pentru 4** — dar nu asa cum prevedea planul: **`704` are 19 politici valabile azi, -nu zero**, iar cele care lipsesc sunt `702`, `703`, `711`, `7018`. **Riscul serios nu e ambiguitatea, ci -`PTVA`-ul notei** — singura nota cu `SCC=707` are `PTVA=5`, deci alegerea politicii dupa `SCC` importa si -o cota de TVA care poate fi greșita. - -## Doua corectii la planul si handoff-ul rundei 8 - -| Ce spune planul (J-quater, „Costurile") | Ce arata baza vie | -|---|---| -| „pentru **704** nu s-a gasit nicio nota configurata, deci trebuie creata o data din ecranul de configurare note contabile" | **FALS.** `704` e cel mai bine acoperit cont: **4 note de vanzari** (`NOTA 1`, `COMISION INTERMEDIERE`, `SERVICII VALUTA`, `SERVICII TRANSPORT`) si **25 de politici** (19 valabile azi). `NOTA 1` — nota implicita a majoritatii politicilor — are exact `SCC=704`. **Pasul de creare a notei pentru 704 se scoate din plan.** | -| „`CONT_VENIT` are date reale (**20 de conturi**, migrare 2023)" | **Imprecis.** Tabelul are **41 de randuri active**; exact **20** au `CONT_VENIT` populat. Restul 21 sunt conturile de ajustari (`391`…`398`, toate cu `CONT_CHELT=681`) — vezi mai jos de ce nu conteaza. | - -Ce **se confirma** din plan: `CONT_VENIT` **n-are consumatori** (nu am verificat aici — a fost stabilit pe -cod in runda 8); lantul invers e realizabil; ambiguitatea „mai multe politici cu acelasi `SCC`" e reala. - -## 1. DDL-ul lui `CORESP_CONT_VENCHELT` (cerut explicit in handoff) - -| # | Coloana | Tip | Null | -|---|---|---|---| -| 1 | `ID_CCV` | `NUMBER(10,0)` | N | -| 2 | `CONT` | `VARCHAR2(4)` | Y | -| 3 | `CONT_CHELT` | `VARCHAR2(4)` | Y | -| 4 | `CONT_VENIT` | `VARCHAR2(4)` | Y | -| 5 | `STERS` | `NUMBER(1,0)` | N | -| 6 | `DATAORAS` | `DATE` | Y | -| 7 | `CONT_APROVIZIONARE` | `VARCHAR2(4)` | Y | -| 8 | `CONT_DIFERENTE` | `VARCHAR2(4)` | Y | - -Constrangeri: **doar** `PK_CORESP_CONT_VENCHELT` pe `ID_CCV`, plus `NOT NULL` pe `ID_CCV` si `STERS`. -**Nu exista unique pe `CONT`** — deci nimic in schema nu impiedica doua randuri pe acelasi cont de -gestiune. In date insa **nu exista niciun `CONT` duplicat**, deci pasul 1 al retetei e determinist azi. -Fragilitatea e de tip „merge pana nu merge": daca cineva adauga un al doilea rand pe `301`, derivarea -devine ambigua fara ca nimic sa semnaleze. - -## 2. Continutul relevant al tabelului — cele 20 de randuri cu cont de venit - -| `CONT` (gestiune) | `CONT_CHELT` | **`CONT_VENIT`** | -|---|---|---| -| `231` | `212` | `707` | -| `301`, `302`, `3021`–`3028`, `303` | `601`, `602`, `6021`–`6028`, `603` | **`707`** (13 randuri) | -| `331`, `332` | `711` | `711` | -| `341` | `711` | `702` | -| `345`, `348` | `711` | `7015` | -| `346` | `711` | `703` | -| `361` | `711` | `7018` | -| `371` | `607` | `707` | -| `381` | `608` | `707` | - -Cele 21 de randuri fara `CONT_VENIT` sunt `391`, `392`, `3921`, `3922`, `393`, `394`, `3941`, `3945`, -`3946`, `395`, `3951`–`3958`, `396`, `397`, `398` — toate cu `CONT_CHELT=681`, adica **ajustari pentru -depreciere**, nu conturi de gestiune pe care sta marfa. - -**Si contează**: `articole_pe_cont_cu_venit_gol = 0`. Niciun articol activ din `NOM_ARTICOLE` nu are -`CONT`-ul pe un rand cu `CONT_VENIT` gol. **Deci ramura „cont de venit derivat gol" nu are cazuri reale** -— trebuie tratata defensiv, dar nu e un scenariu de acoperit in UX. - -## 3. Interogarea inversa — rezultatul pe fiecare `SCC` candidat - -```sql -with cand as (select column_value scc from table(sys.odcivarchar2list('707','711','702','703','7015','7018','704'))) -select c.scc, count(distinct p.id_pol) pol, - count(distinct case when nvl(p.datai,date '1900-01-01')<=trunc(sysdate) - and nvl(p.datas,date '2999-12-31')>=trunc(sysdate) - then p.id_pol end) pol_valabile_azi, - count(distinct nc.id_set) seturi, count(nc.id_note) nc_randuri - from cand c - left join note_contabile nc on nc.scc = c.scc - left join crm_note_vanzari nv on nv.id_set = nc.id_set and nvl(nv.sters,0)=0 - left join crm_politici_preturi p on p.id_nota = nv.id_nota and p.sters=0 - group by c.scc; -``` - -| `SCC` | Politici | **Valabile azi** | Seturi cu acest `SCC` | Randuri `NOTE_CONTABILE` | Verdict pentru pasul 2 | -|---|---|---|---|---|---| -| **`704`** | 25 | **19** | 7 | 28 | **reuseste**, cu ambiguitate mare | -| **`707`** | 4 | **4** | 5 | 8 | **reuseste**, ambiguitate mica | -| **`7015`** | 1 | **1** | 1 | 1 | **reuseste, caz ideal** | -| `711` | 0 | 0 | 4 | 4 | **eșuează** — note exista, dar **nicio politica** nu le foloseste | -| `702` | 0 | 0 | 3 | 3 | **eșuează** — idem | -| `703` | 0 | 0 | 3 | 3 | **eșuează** — idem | -| `7018` | 0 | 0 | **0** | **0** | **eșuează total** — nu exista nici macar nota | - -Citit pe conturile de gestiune: reteta merge pentru marfa (`301`–`303`, `371`, `381`, `231` → `707`) si -pentru produsele din `345`/`348` (→ `7015`), plus pentru orice cade pe fallback-ul `704`. **Nu merge** -pentru `331`/`332` (→ `711`), `341` (→ `702`), `346` (→ `703`), `361` (→ `7018`) — adica **producția -neterminată, semifabricatele, produsele reziduale si ambalajele**. - -Distinctia care conteaza la `711`/`702`/`703`: **nota exista, doar politica lipseste.** Costul de -configurare e „ataseaza nota existenta unei politici", nu „creeaza nota de la zero". Doar `7018` cere -si nota noua. - -## 4. Riscul pe care planul nu l-a vazut: `PTVA` si `IN_VALUTA` de pe nota - -Cele 7 note de vanzari active, cu nota contabila atasata: - -| `ID_NOTA` | Denumire | `ID_SET` | `SCD` | **`SCC`** | **`PTVA`** | `CU_TVA` | **`IN_VALUTA`** | -|---|---|---|---|---|---|---|---| -| 1 | `NOTA 1` | 251023 | `4111` | **704** | **21** | 1 | 0 | -| 2 | `DISCOUNT` | 251024 | `667` | 4111 | 21 | 1 | 0 | -| 3 | `COMISION INTERMEDIERE` | 251025 | `4111` | **704** | **0** | 1 | **1** | -| 4 | `SERVICII VALUTA` | 251026 | `4111` | **704** | **0** | 1 | **1** | -| 5 | `VANZARE MARFA` | 251027 | `4111` | **707** | **5** | 1 | 0 | -| 6 | `PRODUCTIE` | 251028 | `4111` | **7015** | 21 | 1 | 0 | -| 7 | `SERVICII TRANSPORT` | 251029 | `4111` | **704** | 21 | 1 | 0 | - -> **RETRAS — verificat pe cod si infirmat.** Ingrijorarea de mai jos, formulata la prima citire a acestor -> date („`PTVA=5` pe singura nota cu `SCC=707` produce TVA greșit"), **nu se susține**. -> `docs\cercetare\s10_pret_rederivat.md`, „Completare: nota contabila a politicii" p. 2: `cursor_articol` -> (`PACK_FACTURARE:7218-7271`) **nu selecteaza `D.PTVA`**; cota folosita e -> `detalii_articol.proc_tvav * 100 - 100` (`:7464`), din articol / document. Coloana e moarta pentru -> `contabilizeaza_articol`. Iar `IN_VALUTA=1` pe un document in lei nu da eroare si nu strica suma — -> doar populeaza redundant `ACT_TEMP.SUMA_VAL` la curs 1. **Deci criteriul de selectie NU trebuie extins -> cu `PTVA` / `IN_VALUTA`.** Se pastreaza textul de mai jos doar ca urma a raționamentului; nu se citeaza -> ca risc. - -Ce **rămâne** ca risc real din aceste date, si e mai grav: `cursor_articol` face `LEFT JOIN -NOTE_CONTABILE ON C.ID_SET = D.ID_SET` fara `ROWNUM` si fara agregare, iar bucla care il consuma executa -`scrie_nota` **si** `descarca_gestiune` o data **pentru fiecare rand al setului**, cu pretul si cantitatea -intregi de fiecare data. Deci un set cu N randuri = **venit inregistrat de N ori si gestiune descarcata de -N ori**. Cele 7 note de vanzari active au exact un rand fiecare — dar **30 din cele 40 de seturi din baza -au mai multe, cu maxim 30**. Vezi punctul 6.1 mai jos. - -Ce planul enunta drept **avantaj** al retetei — „politica reala aduce si `CU_TVA`, `IN_VALUTA`, -`EXPLICATIE`, `ASCD`, `ASCC`" — se confirma ca argument, cu nuanta ca `ASCD`/`ASCC` au oricum fallback -automat din grupul de utilizatori, deci nu ele justifica alegerea. - -Textul original al ingrijorarii, pastrat pentru trasabilitate: - -- **`707` are o singura nota, si aceea cu `PTVA=5`.** Pentru marfa (majoritatea articolelor de gestiune) - singurul rezultat posibil al pasului 2 e nota cu TVA 5%. -- **Doua din cele patru note cu `SCC=704` au `IN_VALUTA=1`** (`SERVICII VALUTA`, `COMISION INTERMEDIERE`), - deci o potrivire pe `SCC` poate ateriza pe ele pe un document in lei. - -Colateral util pentru **S4c**: exista deja o nota de discount configurata — `id_nota=2`, `SCD=667` / -`SCC=4111`. Nu e o eroare de configurare (explica randul `SCC=4111` din agregat), e nota folosita pentru -discount. De verificat la S4c daca discountul pe linie trece prin ea. - -## 5. Efectul colateral al pasului 3 — politica **este** o lista de preturi - -Pasul 3 al retetei insereaza articolul in politica alesa, prin `pack_preturi.adauga_politica_pret_art`. -Politicile candidate, cu numarul de articole pe care le contin azi: - -| `SCC` | `ID_POL` | Nume | Articole | -|---|---|---|---| -| `7015` | 41 | `STOC PRODUSE` | **6310** | -| `707` | 39 | `LISTA PRETURI LEI` | **909** | -| `707` | 40 | `LISTA PRETURI CU TVA EURO` | 2 | -| `707` | 47 / 62 | `DEPOZIT ELI` / `DEPOZIT ELI_31/07/2025 11:07:25` | 3 / 3 | -| `704` | 2 | `LISTA 2` | 19 | -| `704` | 34 | `LISTA 2 - EURO` | 12 | -| `704` | 36 | `SERVICE AUTO` | 9 | -| `704` | 35 | `LISTA STANDARD EURO` | 8 | -| `704` | 9 | `CHIOSC` | 5 | -| `704` | 6, 12, 13, 14, 15, 16 | `LISTA DE PROBA`, `S5 ZILNIC`, `S6 WEEKEND SEZON`, `S7 CRACIUN`, `S3 PASTE`, `S3 1MAI` | 4 fiecare | -| `704` | 65, 66 | `TRANSPORT`, `SERVICII INFORMATICE` | 1 fiecare | -| `704` | 26, 27, 28, 29, 30, 31 | `S6 ZILE IARNA`, `S8 REVELION`, `S1 IARNA2`, `S1 IARNA2 WEEKEND`, `S2 PRIMAVARA`, `S7 CRACIUN` | **0** | - -**Astea sunt liste de preturi reale, cu nume de client si de sezon.** Daca reteta alege `LISTA PRETURI -LEI` sau `STOC PRODUSE` pentru un articol adaugat ad-hoc pe factura, articolul **apare de acum in lista -de preturi a acelui context**, cu pretul cu care a fost facturat o data. Asta e efectul pe care planul il -descrie drept „politica tehnica populata automat" — dar **numai daca politica e chiar tehnica**. -Alegerea unei politici comerciale existente **poluează o lista de preturi de producție**. - -Consecinta de proiectare, de dus in J-quater: pasul 2 nu trebuie sa caute *orice* politica cu `SCC`-ul -potrivit, ci **o politica tehnica dedicata, una per `SCC`**, creata anume — modelul -`gnId_pol_pret_stoc` extins de la o politica la sapte. Cele sase politici cu **0 articole** arata ca o -politica goala e o stare acceptata de produs. - -Detaliu care ajuta: **majoritatea politicilor cu `SCC=704` trimit la aceeasi `id_nota=1`**. Deci -ambiguitatea celor 19 politici e ambiguitate de **lista de preturi**, nu de rezultat contabil — toate dau -acelasi `704` / `PTVA=21` / lei. Cu atat mai mult, criteriul de alegere trebuie sa fie „care lista de -preturi vreau sa murdaresc", si raspunsul corect e „niciuna dintre cele existente". - -## 6. Fragilitati masurate, de trecut in Riscuri - -1. **`ID_SET` cu mai multe randuri dubleaza venitul si descarcarea de gestiune.** Pe cele 7 seturi - folosite de politici, fiecare are exact **1** rand `NOTE_CONTABILE`. Dar in tabel sunt **40 de seturi, - din care 30 au mai multe randuri**, cu maxim **30 de randuri pe set**. Confirmat pe cod - (`s10_pret_rederivat.md`, Completare p. 1): `cursor_articol` face `LEFT JOIN ... ON C.ID_SET = - D.ID_SET` **fara `ROWNUM`, fara agregare, fara filtru**, iar bucla `:7393-7542` executa `scrie_nota` - **si** `descarca_gestiune` o data per rand, **cu aceeasi cantitate si acelasi pret intreg**, fara - distributie (`ORDINE` nu apare in tot pachetul). Deci pasul 2 al retetei trebuie sa garanteze **un - singur rand pe setul ales**, nu doar un rand cu `SCC`-ul potrivit. Conditia care face reteta sigura - azi e o coincidenta a datelor, nu o garanție a codului sau a schemei. -2. **Doua politici active fara `ID_NOTA`**: `32 HOTEL TAXE` si `33 HOTEL CAZARE` (ambele valabile - 01.01.2010–31.12.2099, `id_valuta=3`). Un articol pe una din ele are `id_pol` valid dar **fara nota** — - comportament neverificat, intrebare trimisa agentului pe `PACK_FACTURARE`. E o gaura care **exista - deja azi**, independenta de #13. -3. **Fara unique pe `CORESP_CONT_VENCHELT.CONT`** — vezi punctul 1. - -## 7. Distributia articolelor — cat de mult conteaza fiecare ramura a deciziei 27 - -`NOM_ARTICOLE`, 6430 articole active: - -| Prima cifra din `CONT` | Articole | Conturi distincte | Ramura deciziei 27 | -|---|---|---|---| -| `3xx` | **6125** | 9 | gestionabil → `CORESP_CONT_VENCHELT` | -| `8xx` (toate `8035`) | 26 | 1 | nu e 6xx/7xx, nu e in `CORESP` → **`704`** | -| `6xx` | 22 | 5 | `NOM_ARTICOLE.CONT` ca atare | -| `7xx` | 20 | 6 | `NOM_ARTICOLE.CONT` ca atare | -| `1xx` | 4 | 2 | → `704` | -| `4xx` | 3 | 2 | → `704` | -| `0xx` | 1 | 1 | → `704` | -| `2xx` | 1 | 1 | → `704` | -| **fara `CONT`** | **228** | — | → `704` | - -Conturi de pe articole care **nu** sunt 6xx/7xx si **nu** apar in `CORESP_CONT_VENCHELT.CONT`, deci cad pe -`704`: `8035` (26 articole), `123` (3), `446` (2), `111`, `409`, `0`, `212`, `37` (1 fiecare). - -**Ramura dominanta e de departe cea gestionabila** — 6125 din 6430. Iar ea trimite in majoritate spre -`707`, adica spre cazul cu `PTVA=5`. Cele 228 de articole fara cont si cele 26 pe `8035` fac fallback-ul -`704` un caz real, nu teoretic — si acolo reteta functioneaza. - -Nota: `212` apare pe un articol, dar in `CORESP_CONT_VENCHELT` figureaza ca `CONT_CHELT` (pe randul -`231`), nu ca `CONT`. Deci cade corect pe `704` prin regula deciziei 27. - -## Ce nu s-a putut stabili aici, si de ce - -- **Ce face `contabilizeaza_articol` cu `PTVA` / `CU_TVA` / `IN_VALUTA` ale notei** — e intrebare de cod, - nu de date. Trimisa agentului deja incarcat in `PACK_FACTURARE`; raspunsul intra in - `docs\cercetare\s10_pret_rederivat.md`, sectiunea „Completare: nota contabila a politicii". **Pana - atunci pasul 2 al retetei nu se considera validat.** -- **Daca `CONT_VENIT` chiar n-are consumatori** — stabilit pe cod in runda 8, nu reverificat pe date. -- **Comportamentul politicii fara nota** — idem, intrebare de cod, trimisa. -- Verificarile sunt pe **schema de dezvoltare** `MARIUSM_AUTO`. Datele sunt reprezentative (liste de - preturi cu nume reale, 6430 articole), dar **o baza de client poate avea alta configurare de note** — - in special poate avea deja politici pe `711` / `702` / `703`. Concluziile pe **structura** (lantul, - absenta unique-ului, `ID_SET` multi-rand) sunt insa independente de date. - -## Reproducere - -Cele trei scripturi rulate, in ordine: descoperire DDL/coloane · interogarea inversa pe `SCC` · -efecte colaterale si distributii. Interogarea-cheie e citata integral la punctul 3. Rulare: - -```powershell -& 'D:\ROA\instantclient_19_18\sqlplus.exe' -L -S 'MARIUSM_AUTO/ROMFASTSOFT@ROA_CENTRAL' '@fisier.sql' -``` - -SQL-ul se scrie in fisier **ASCII**, nu prin pipe din PowerShell (BOM-ul UTF-16 sparge parserul), si cu -`linesize 32767` — vezi `COMUN\docs\oracle_export.md`. diff --git a/docs/cercetare/verif_goluri_ntip_aviz.md b/docs/cercetare/verif_goluri_ntip_aviz.md deleted file mode 100644 index d38ed9a..0000000 --- a/docs/cercetare/verif_goluri_ntip_aviz.md +++ /dev/null @@ -1,325 +0,0 @@ -# Verificare adversariala — golul ntip=4 si "ramurile de aviz" (contabilizeaza_articol) - -Verifica raportul `docs/cercetare/gol_ntip4_factura_din_avize.md`. Rol: incercare de infirmare, -nu confirmare. Toate citatele de mai jos sunt re-verificate direct in aceasta runda pe -`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` (17217 linii) si -pe `COMUN\clase\ofacturare.vc2` (ambele copii, `frm_facturare_articole` ~13967-14500 si -`frm_facturare_articole2` ~18003-18500). Status: **TERMINAT**. - -## Corectie structurala prealabila — cei "3 apelanti" ai `contabilizeaza_articol` sunt gresit numiti - -Toate rapoartele anterioare (`canal_cont_venit_fara_politica.md:67`, `nota_contabila_fara_politica.md:103`, -si implicit raportul verificat) numesc cei 3 apelanti interni `scrie_factura2`, -**`scrie_factura_avize_retur`**, `scrie_aviz_retur`. **Gresit pentru al doilea.** Verificare directa -pe granitele reale (`PROCEDURE ...` / `END ...`): - -``` -ff_...:6264 PROCEDURE scrie_factura_avize_retur(... ff_...:6657 END scrie_factura_avize_retur; -ff_...:6692 PROCEDURE scrie_factura_avize(... ff_...:7058 END scrie_factura_avize; -ff_...:7085 PROCEDURE scrie_aviz_retur(... ff_...:7171 END scrie_aviz_retur; -``` - -Apelul de la `ff_...:6858` (`pack_facturare.contabilizeaza_articol(tab_detalii(i));`, in interiorul -`IF articole_aviz(j).custodie = 1 THEN`) cade **intre 6692 si 7058** — deci apartine lui -**`scrie_factura_avize`** (fara `_retur`), nu lui `scrie_factura_avize_retur` (care se termina la -6657, cu 200+ linii inainte). `scrie_factura_avize_retur` **nu cheama deloc** -`contabilizeaza_articol` — corpul ei (6264-6657) insereaza direct in `VANZARI_DETALII_TEMP` din -`VANZARI_DETALII` (randurile avizului sursa deja existente), fara sa treaca prin -`adauga_articol_factura` sau `contabilizeaza_articol`. - -**De ce conteaza**: `scrie_factura_avize` e exact handler-ul Oracle pentru `ntip=4` ("facturare din -aviz") — apelat din VFP la `ofacturare.vc2:14318` (`Case poDate.Tip = 4`). Deci al doilea apel real -catre `contabilizeaza_articol` **e in interiorul propriului handler `ntip=4`** (pentru randurile -`custodie=1`), nu intr-un flux separat "avize+retur". Cei 3 apelanti reali sunt: - -| Apel | Procedura reala | Cand se ajunge la ea din VFP | -|---|---|---| -| `ff_...:6141` | `scrie_factura2` | `Case Inlist(poDate.Tip,3,21,25,28,42,47)` sau `Otherwise` (`ofacturare.vc2:14332,14361`, identic la `:18330,18361`) — calea principala pentru aproape toate tipurile, inclusiv avizele | -| `ff_...:6858` | `scrie_factura_avize` | `Case poDate.Tip = 4` (`:14301,14318`) — doar pentru randurile `custodie=1` ale unei facturi din aviz | -| `ff_...:7140` | `scrie_aviz_retur` | **Niciun apel gasit in `COMUN\` din VFP** (`Grep` pe tot `COMUN` si pe tot proiectul — zero rezultate in cod, doar in rapoarte). Posibil cod mort/apelat din alt produs; nu afecteaza verdictele de mai jos, pentru ca `scrie_factura2` singura e suficienta sa dovedeasca A1a. | - -Corectia nu schimba verdictul general al A1a (avizele *ajung* la `contabilizeaza_articol`), dar -schimba **prin ce drum** — relevant pentru oricine ar urma sa scrie cod pe baza acestor rapoarte. - -## A1. Ramurile de AVIZ raman atinse si ramura noua le-ar scrie SCD gresit - -**Verdict: CONFIRMAT, dar mai ingust decat sustine raportul.** - -### A1a. Ajunge `contabilizeaza_articol` sa fie apelata pe un AVIZ? - -**DA, confirmat**, prin `scrie_factura2`. Ruta VFP (`ofacturare.vc2:14282-14389`, identic la -`:18enable...`, verificata in ambele copii ale formularului): - -``` -Case poDate.eProforma = 1 -> scrie_proforma -Case poDate.Tip = 4 -> scrie_factura_avize && facturare din aviz -Case Inlist(poDate.Tip,3,21,25,28,42,47) -> scrie_factura2 && din comenzi / avize din comenzi -Otherwise -> scrie_factura2 && tot restul (incl. 29, 22, 24, 27...) -``` - -In interiorul `scrie_factura2` (`ff_...:6072-6142`), CASE-ul propriu **intercepteaza unele `ntip` -inainte** de a ajunge la `contabilizeaza_articol`: - -``` -WHEN ntip IN (23,25,30,41) THEN transfera_articol(...) -- NU contabilizeaza_articol -WHEN ntip IN (42,47) THEN descarca_gestiune(...) direct -- NU contabilizeaza_articol -WHEN ntip IN (2,6,52) AND id_rata<>0 THEN contabilizeaza_rata(...) -ELSE contabilizeaza_articol(...) -- aici ajung 28, 29, 21, 22, 24, 27, 3, ... -``` - -Deci `ntip=28` si `ntip=29` **chiar ajung** la `contabilizeaza_articol` (prin `scrie_factura2`, ramura -`ELSE`) — asta confirma miezul A1a. Dar `ntip IN (23,25,30,41,42,47)`, desi trec prin `scrie_factura2` -(sunt in lista VFP `Inlist(3,21,25,28,42,47)`), **nu ajung niciodata** la `contabilizeaza_articol` — -sunt deviate mai devreme, in CASE-ul propriu al lui `scrie_factura2`, spre `transfera_articol`/ -`descarca_gestiune`. Vezi corectia la sectiunea 3 (tabelul per-`ntip`). - -### A1b. Poate un articol fara politica sa fie adaugat pe un aviz? - -**DA, confirmat structural**, pe doua cai distincte in `adauga_articol_factura` (`ff_...:5052-5203`): - -- `WHEN ntip IN (3,21,28,42,47)` (`:5053-5078`): cauta in `COMENZI_ELEMENTE` dupa - `ID_COMANDA + ID_ARTICOL + PRET` — **fara nicio referinta la `V_ID_POL`** in `WHERE`. Un articol - fara politica poate gasi potrivire aici daca exista elementul de comanda corespunzator (comanda + - articol + pret identice), indiferent de politica. **Fara handler `EXCEPTION`** — la fel ca ramura - `ntip=4`, dar aici absenta handler-ului nu conteaza pentru `id_pol`, pentru ca filtrul nu-l - foloseste. -- `ELSE` (`:5187-5203`, bucket-ul implicit pentru orice `ntip` neacoperit de ramurile speciale — - include `29` si restul avizelor "simple"): **nicio interogare legata de politica** — ia direct - `V_PRET_TEMP`/`V_ID_VALUTA_TEMP`/`V_PRETURI_CU_TVA_TEMP`/`V_IN_STOC_TEMP` (valorile deja calculate - de VFP), plus o singura `SELECT` pe `JTVA_COLOANE` dupa `V_ID_JTVA_COLOANA` (nimic legat de - `id_pol`). **Zero obstacol** pentru un articol fara politica pe aceasta ramura. - -Nu am gasit (si nu am cautat exhaustiv, la fel ca raportul original) formularul VFP concret care ar -adauga azi un articol ad-hoc fara politica pe un aviz — premisa ramane, ca si in raportul original, -mostenita din proiectul #13 (`canal_cont_venit_fara_politica.md`), nu re-derivata aici. Ce am putut -confirma exhaustiv e partea Oracle: **daca** VFP trimite `V_ID_POL=NULL` pe aceste `ntip`, Oracle nu -pune nicio piedica. - -### A1c. `SCD` in blocul `:7398-7423` — hardcodat sau derivat? - -Citit integral (`ff_...:7398-7423`), confirmat identic cu citatul raportului, **linie cu linie**: - -``` -ff_...:7400-7406 WHEN ntip<=20 OR ntip IN (nTipFacturaHotel,nTipFacturaRestaurant, - nTipNotaPlata,nTipVanzareRetail,48,49,51,52) THEN - V_SCD := crs_rand_articol.scd; -- din politica (NOTE_CONTABILE.SCD) -ff_...:7413-7417 WHEN ntip IN (28,29) THEN - V_SCD := '461'; -- HARDCODAT, aviz catre clienti debitori -ff_...:7418-7422 ELSE - V_SCD := '418'; -- HARDCODAT, aviz generic -``` - -`V_SCC` (linia 7425) vine mereu din `crs_rand_articol.scc` (coloana notei de vanzari), **indiferent -de ramura** — CASE-ul de mai sus decide doar `V_SCD`. Confirmat: hardcodarea exista exact cum spune -raportul, fara offset de linie. - -**Nuanta importanta, omisa de raport**: proiectarea `canal_cont_venit_fara_politica.md:146` (sursa -ramurii noi) **flagheaza deja, printr-o paranteza**, ca "ramurile aviz raman `'418'`/`'461'`, deja -hardcodate azi la `ff_...:7415,7420`, independent de politica" — deci designul **e constient** de -existenta acestor hardcodari si de faptul ca ar trebui pastrate, dar **nu da structura de cod -concreta** (niciun `IF`/`CASE` pe `ntip` in tabelul "Continutul ramurii noi", sectiunea 3) care sa -implementeze efectiv acea pastrare. Raportul verificat prezinta acest lucru ca pe o **descoperire -noua** ("Gol sora... gasit in aceasta cercetare") — de fapt designul deja stia de riscul general, dar -nu-l rezolvase in cod. Concluzia practica ramane aceeasi (ramura noua, asa cum e descrisa azi in -proiectare, chiar ar hardcoda `SCD='4111'` fara conditionare pe `ntip` daca ar fi implementata -literal dupa tabelul din sectiunea 3), dar caracterizarea "gol nou, neconsemnat" e inexacta. - -### Corectie la tabelul per-`ntip` din raport (sectiunea 3) - -Raportul marcheaza `IN (28,29)` **si** "toate celelalte (`21,22,23,24,25,27,30,41,42,47,...`)" ca -"Atinsa: DA". Pe baza rutarii reale prin `scrie_factura2` (A1a de mai sus): - -| `ntip` | Ajunge la `contabilizeaza_articol`? | Motiv | -|---|---|---| -| `28`, `29` | **DA** | `scrie_factura2` -> `ELSE` -> CASE intern `ntip IN(28,29)` -> `SCD='461'` | -| `21`, `22`, `24`, `27`, `3` si alte "otherwise" nespeciale | **DA** (probabil) | cad in `ELSE` al lui `scrie_factura2` -> `ELSE` intern -> `SCD='418'` | -| `23`, `25`, `30`, `41` | **NU** | interceptate in `scrie_factura2` de `WHEN ntip IN(23,25,30,41) THEN transfera_articol(...)` — nu ajung niciodata la `contabilizeaza_articol` prin acest apelant | -| `42`, `47` | **NU** | interceptate de `WHEN ntip IN(42,47) THEN descarca_gestiune(...) direct` — la fel, nu ajung | - -Golul real e deci mai ingust decat "orice aviz": se limiteaza la avizele care cad in ramura `ELSE` a -lui `scrie_factura2` (28, 29, si restul avizelor "simple" neexcluse), nu la `23,25,30,41,42,47`, care -sunt structural neatinse de `contabilizeaza_articol` prin singurul apelant confirmat. (Nu exclud ca -`scrie_aviz_retur`, al carei apelant VFP nu l-am gasit, sa trateze si aceste `ntip` — dar cum nu exista -dovada ca e apelata deloc, nu pot confirma nici infirma pentru ea.) - -## A2. Golul `ntip=46` (`nTipNotaPlata`) — `scrie_nota` sarita azi - -**Verdict: CONFIRMAT.** - -- Constanta: `ff_...:84 nTipNotaPlata VANZARI.TIP%TYPE := 46;` — **exact 46**, verificat direct, nu - presupus. -- Garda: `ff_...:7441 IF pack_facturare.nTip <> pack_facturare.nTipNotaPlata THEN` — inainte de - apelul `scrie_nota` (`:7443-7467`) — citat exact, linie identica cu raportul. -- Poate un `ntip=46` sa primeasca un articol fara politica? **DA**, prin acelasi bucket `ELSE` - (`ff_...:5187-5203`) al `adauga_articol_factura` verificat la A1b — `nTipNotaPlata` nu e in niciuna - din ramurile speciale (`3,21,28,42,47`, `4`, `45`, `V_OPT_FACTURARE=3`), deci cade in `ELSE`, care - nu cere `id_pol` deloc. -- Ajunge la `contabilizeaza_articol`? **DA** — `ntip=46` nu e proforma, nu e `Tip=4`, nu e in - `Inlist(3,21,25,28,42,47)` -> `Otherwise` -> `scrie_factura2` -> nu e in `(23,25,30,41)`/`(42,47)`/ - `(2,6,52)` -> `ELSE` -> `contabilizeaza_articol`. -- Ramura noua (per `canal_cont_venit_fara_politica.md` sectiunea 3, pasul 1 "Apeluri, o singura data - fiecare") descrie apelul `scrie_nota` **fara** sa mentioneze garda `ntip<>nTipNotaPlata` — deci, asa - cum e scrisa azi proiectarea, ar apela necondiționat `scrie_nota` pentru un document `NotaPlata` cu - linie `cont_venit`. Gol real, bine argumentat de raport. - -## A3. `ntip=4` exclus prin `NO_DATA_FOUND` neprins la `adauga_articol_factura` - -**Verdict: CONFIRMAT**, cu toate liniile citate verificate exact, fara offset: - -- `ff_...:5080-5103` — ramura `WHEN ntip=4`, `WHERE A.ID_POL = V_ID_POL` (linia 5096 exact), fara - `EXCEPTION`. Confirmat caracter cu caracter identic cu citatul raportului. -- Semantica SQL `NULL = NULL` -> `UNKNOWN`, niciodata `TRUE`: standard, corect. -- **Ordinea de apel confirmata direct pe VFP**: `do_scrie_articole` (`ofacturare.vc2:13967`) cheama - `initializeaza_date_factura` (seteaza `ntip`), apoi in bucla `Scan` peste `crsfactura` cheama - `adauga_articol_factura` (`:14069-14091`) pentru fiecare rand — **toate acestea ruleaza inainte** de - `Do Case` (`:14282`) care alege `scrie_factura_avize`/`scrie_factura2`/etc. Deci un articol cu - `V_ID_POL=NULL` pe `ntip=4` cade la `adauga_articol_factura` **inainte** ca `scrie_factura_avize` sa - fie chemata — linia nu ajunge niciodata la `contabilizeaza_articol`. Confirma independent - concluzia raportului. -- **Semantica VFP `Nvl(Alltrim(Str(poArt.id_pol)),[NULL])`** (`ofacturare.vc2:14072`, identic - `:18107`): in Visual FoxPro, `Str()` si `Alltrim()` **propaga `.NULL.`** cand primesc `.NULL.` ca - argument (comportament standard, documentat al limbajului — orice functie "null-aware" aplicata pe - `.NULL.` intoarce `.NULL.`, nu eroare si nu `"0"`). `Nvl(.NULL., [NULL])` inlocuieste explicit cu - literalul caracter `NULL` (4 caractere), care ajunge **necotat** in textul SQL generat (concatenare - directa, fara ghilimele in jur) — deci Oracle il interpreteaza ca si cuvantul-cheie `NULL`, nu ca - string. Rezultat: `V_ID_POL` ajunge `NULL` in Oracle exact cand `poArt.id_pol` e `.NULL.` in VFP. - Verificare bazata pe semantica standard VFP (nu am putut rula VFP live — masina partajata, conform - interdictiei) — consistenta si cu tiparul deja folosit identic pentru alti parametri opționali pe - acelasi apel (`id_gestiune`, `id_valuta_d`, `id_part_rez` etc., toate cu acelasi `Nvl(Alltrim(Str(...`). -- **Neverificat** (nici in raportul original, nici aici): cum ajunge efectiv `poArt.id_pol` sa fie - `.NULL.` pentru un articol fara politica — ramane premisa mostenita din proiectul #13, nu - re-derivata in aceasta runda. - -## A4. Numerele de linie — offset +17 sau identice? - -**Verdict: NICIUNA din cele doua afirmatii e o regula generala — depinde de setul de citate comparat.** - -- **Pentru citatele proprii ale raportului verificat** (re-testate direct aici): `adauga_articol_factura` - header `:4989-5015`, ramura `ntip=4` `:5080-5103`, `contabilizeaza_articol` header `:7173`, CASE - `:7398-7423`, hardcode `:7413-7422`, garda `:7441`, `nTipNotaPlata:=46` la `:84` — **toate identice**, - zero offset. Raportul are dreptate pentru propriile sale citate. -- **Dar exista un offset real, doar ca nu e +17 ci +9**, intre citatele mai vechi din - `nota_contabila_fara_politica.md:100-104` (`ff_...:6150`, `:6867`, `:7149` pentru cele 3 apeluri - `contabilizeaza_articol`) si pozitiile reale confirmate in aceasta runda pentru **aceleasi** apeluri - (`6141`, `6858`, `7140`) — diferenta consecventa de **9 linii**, nu 17, si in directia opusa - (citatul vechi e mai mare, nu mai mic). -- Concluzia corecta: fisierul `PACK_FACTURARE.sql` a fost re-exportat de mai multe ori pe parcursul - proiectului (cod adaugat/sters intre versiuni), asa ca **fiecare set de citate vechi are propriul - offset fata de exportul curent — nu exista o constanta universala (nici 0, nici +17, nici +9)**. - `handoff_13_formular_unificat.md:190` ("+17") si acest raport ("identic, fara offset") sunt ambele - adevarate **doar pentru seturile de citate pe care le-au verificat fiecare** — nu se pot generaliza - una peste alta. Disciplina corecta, pe care ambele rapoarte au respectat-o, e re-verificarea directa - pe fisierul curent inainte de orice citare — nu presupunerea unui offset fix. - -## Text de plan corectat (sectiunea 4 din raportul verificat, rescris) - -``` -**Ramura `ntip=4` (facturare din avize, handler real `scrie_factura_avize:6692-7058`, nu -`scrie_factura_avize_retur` cum apare gresit in rapoartele anterioare) — exclusa structural, nu -necesita cod suplimentar.** `adauga_articol_factura`, ramura `WHEN ntip=4` (`:5080-5103`), cauta -randul sursa din `VANZARI_DETALII` prin `A.ID_POL = V_ID_POL`, fara handler de exceptie — cu -`V_ID_POL=NULL` (exact cazul liniei fara politica), comparatia nu se poate potrivi niciodata, deci -apelul cade cu `ORA-01403` la adaugarea articolului, in bucla `do_scrie_articole` (`ofacturare.vc2: -13967-14106`), care ruleaza intotdeauna inaintea `Do Case`-ului (`:14282`) ce ar chema -`scrie_factura_avize`. Un articol fara politica nu poate fi, structural, adaugat pe un document -`ntip=4`; ramura noua `cont_venit` nu are nimic de tratat aici. - -**Gol confirmat, de acoperit inainte de implementare — mai ingust decat s-a crezut initial**: -ramurile de aviz care **efectiv** ajung la `contabilizeaza_articol` prin singurul apelant confirmat -functional (`scrie_factura2`, apelat pentru `Inlist(3,21,25,28,42,47)` si pentru toate celelalte -tipuri prin `Otherwise`) sunt cele care cad in `ELSE`-ul CASE-ului propriu al `scrie_factura2`: -`ntip IN (28,29)` -> `SCD='461'`, restul (`21,22,24,27` si alte "otherwise" avize) -> `SCD='418'` -(`:7413-7422`). **`ntip IN (23,25,30,41,42,47)` NU ajung la `contabilizeaza_articol`** prin acest -apelant — sunt deviate mai devreme, in `scrie_factura2` insasi, spre `transfera_articol`/ -`descarca_gestiune`, deci golul nu li se aplica (contabilizeaza_articol si ramura ei noua nu se -executa niciodata pentru ele). `adauga_articol_factura` nu cere `id_pol` pentru niciunul din aceste -`ntip` (foloseste calea "din comenzi", fara filtru pe `id_pol`, sau calea implicita `ELSE`, fara nicio -interogare legata de politica) — deci un articol fara politica poate ajunge pe oricare din avizele -`28,29,21,22,24,27` cu `cont_venit` populat. Designul deja semnalase acest risc printr-o paranteza -(`canal_cont_venit_fara_politica.md:146`, "ramurile aviz raman 418/461"), dar nu-l implementase: ramura -noua trebuie sa aleaga `SCD` in functie de `ntip` (pastrand `'461'` pentru `IN(28,29)`, `'418'` pentru -restul avizelor atinse), nu sa hardcodeze `'4111'` necondiționat. - -**Gol confirmat separat**: ramura noua trebuie sa pastreze garda `IF ntip <> nTipNotaPlata` (`ntip=46`, -`ff_...:7441`) inainte de a apela `scrie_nota` — azi sarita intentionat pentru documentele de tip -"nota de plata", iar un articol fara politica poate fi adaugat si pe acest tip de document -(`adauga_articol_factura`, bucket `ELSE`, fara cerinta de `id_pol`). - -**Defensiv (opțional)**: se poate adauga o garda explicita `IF cont_venit IS NOT NULL AND ntip = 4 -THEN RAISE_APPLICATION_ERROR(...)`, dar e demonstrat redundanta — cazul e deja imposibil pe caile VFP -cunoscute, blocat cu doua straturi independente inainte de a ajunge la `contabilizeaza_articol`. -``` - -## Ce nu s-a putut stabili in aceasta runda (mostenit din raportul original, nu re-rezolvat) - -- Cum ajunge `poArt.id_pol` sa fie `.NULL.` in VFP pentru un articol fara politica pe un aviz — - premisa proiectului #13, nu re-derivata. -- Cine cheama `scrie_aviz_retur` (daca cineva) — cautare exhaustiva in `COMUN\` si in tot proiectul, - zero rezultate; posibil cod mort sau apelat din alt produs al suitei ROA. -- Comportamentul exact `goExecutor`/`oPrelucrareEroare()` la o exceptie Oracle neprinsa — nu urmarit - in aceasta runda (mostenit ca gol si in raportul original). - -## Handoff - -Verificare incheiata, toate cele 4 afirmatii + corectia structurala preliminara sunt in sectiunile de -mai sus. Zero cod atins, zero write-back, doar `SELECT`/`Read`/`Grep` pe SQL si VFP text. Context -consumat substantial (citire extinsa pe `ff_...sql` si `ofacturare.vc2`), dar verificarea s-a incheiat -inainte de pragul de predare. - ---- - -## Propagarea `CONT_VENIT` prin cei trei apelanti — verificare - -Intrebare noua de la team-lead: argumentul central al proiectarii (`canal_cont_venit_fara_politica.md`, -sectiunea 1, `:63-69`) — "orice coloana noua pe `VANZARI_DETALII_TEMP` ajunge automat in -`detalii_articol`, fara sa atingi cei 3 apelanti, pentru ca fac `SELECT * BULK COLLECT` intr-un -`TABLE OF ...%ROWTYPE`" — se bazeaza pe lista de nume deja corectata mai sus. Verificat acum pe -numele **reale** ale celor 3 apelanti (`scrie_factura2`, `scrie_factura_avize`, `scrie_aviz_retur`), -fiecare citit direct, linie cu linie, in aceasta runda. - -### 1-2. `scrie_factura2` (`:6020-6223`) - -``` -ff_...:6039-6040 TYPE tab_detalii_type IS TABLE OF vanzari_detalii_temp%ROWTYPE; - tab_detalii tab_detalii_type; -ff_...:6063 SELECT * BULK COLLECT INTO tab_detalii FROM VANZARI_DETALII_TEMP; -ff_...:6141 pack_facturare.contabilizeaza_articol(tab_detalii(i)); -``` -**`SELECT *` intr-un `TABLE OF VANZARI_DETALII_TEMP%ROWTYPE`, confirmat.** `CONT_VENIT` curge automat. - -### 1-2. `scrie_factura_avize` (`:6692-7058`, handler-ul real `ntip=4`) - -``` -ff_...:6708-6709 TYPE tab_detalii_type IS TABLE OF vanzari_detalii_temp%ROWTYPE; - tab_detalii tab_detalii_type; -ff_...:6749 SELECT * BULK COLLECT INTO tab_detalii FROM VANZARI_DETALII_TEMP; -ff_...:6858 pack_facturare.contabilizeaza_articol(tab_detalii(i)); -- doar pt. custodie=1 -``` -**Acelasi tipar: `SELECT *` in `TABLE OF VANZARI_DETALII_TEMP%ROWTYPE`, confirmat.** Intre populare -(`:6749`) si apelul catre `contabilizeaza_articol` (`:6858`), randul `tab_detalii(i)` e modificat doar -pe doua campuri (`.cantitate`, `.id_rata`, `:6855-6856`) — `CONT_VENIT` ramane neatins, curge automat. -Chiar daca numele apelantului real difera de ce credea proiectarea, **concluzia ei ("nu se ating deloc") -ramane corecta pentru acest apelant**, doar numele era gresit. - -### 3. `scrie_aviz_retur` (`:7085-7171`) - -``` -ff_...:7097-7098 TYPE tab_detalii_type IS TABLE OF vanzari_detalii_temp%ROWTYPE; - tab_detalii tab_detalii_type; -ff_...:7106 SELECT * BULK COLLECT INTO tab_detalii FROM VANZARI_DETALII_TEMP; -ff_...:7140 pack_facturare.contabilizeaza_articol(tab_detalii(i)); -- doar pt. custodie=0 (ELSE la :7115) -``` -**Acelasi tipar, confirmat.** Nu conteaza ca nu i-am gasit apelant VFP (ramane nedovedit ca se -executa vreodata) — *daca* s-ar executa, `CONT_VENIT` ar curge automat si aici, deci nu schimba -suprafata diff-ului indiferent de raspuns. - -### Verdict pe argumentul proiectarii - -**Se salveaza.** Toti cei 3 apelanti reali (nu cei numiti gresit in rapoartele anterioare) folosesc -identic `SELECT * BULK COLLECT INTO FROM VANZARI_DETALII_TEMP` -— zero lista explicita de coloane, zero `%ROWTYPE` pe alta tabela, zero constructie camp-cu-camp. -Concluzia proiectarii ("adaugi coloana pe `VANZARI_DETALII_TEMP`, `contabilizeaza_articol` o vede -automat prin toti apelantii, fara sa atingi `scrie_factura2`/`scrie_factura_avize`/`scrie_aviz_retur`") -**e corecta ca mecanism**, chiar daca doua din cele trei nume citate pentru ea erau gresite. Corectia -de nume nu schimba nimic la suprafata de regresie a schemei — schimba doar la ce trebuie sa te uiti -daca vreodata unul dintre cei trei apelanti isi schimba modul de populare a lui `tab_detalii`. diff --git a/docs/cercetare/verif_proforma_alegere_stoc.md b/docs/cercetare/verif_proforma_alegere_stoc.md deleted file mode 100644 index c4baeb0..0000000 --- a/docs/cercetare/verif_proforma_alegere_stoc.md +++ /dev/null @@ -1,366 +0,0 @@ -# Verificare — alegerea stocului pe proforma (runda de verificare) - -Investigatie read-only, 10.08.2026. Raspunde la 4 intrebari punctuale cerute de team-lead, pentru -decizia de proiectare S5b (comutare FACTURA <-> PROFORMA cu linii deja adaugate). - -Continua fara sa reia: `s5b_proforma_descarcare_gestiune.md` (mecanismul `gestionabil=0` / -`id_gestiune=-1000`) si `s5b_proiectare_proforma_copiere.md` (proiectarea de runda 12, care a -descoperit deja ca `scrie_proforma` nu cheama `contabilizeaza_articol` deloc). - -Nicio modificare de cod, niciun `git_sync.ps1`/`txt2vcx.ps1`, niciun commit, nicio scriere Oracle -(numai `SELECT`/`Read`/`Grep` pe fisiere de pe disc). Sursa Oracle folosita: -`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` (citat mai jos -`PACK:linie`). - -**Status: COMPLET.** - ---- - -## Rezumat executiv - -1. **Da, pe calea principala si pe toate celelalte surse (cu exceptia copierii), stocul NU se - alege la adaugarea unei linii pe proforma azi** — pentru ca `gestionabil` a fost deja fortat pe - `0` in VFP, in masa, **inainte** ca gridul de linii sa se deschida. Cand ruleaza `Do Case`-ul de - la `ofacturare.vc2:13803`, `poArticol.gestionabil` e deja `0`, nu valoarea reala din nomenclator. -2. **Marcarea negestionabila are un singur mecanism activ azi, exclusiv in VFP**: - `UPDATE (m.lcCursor) SET gestionabil = 0` (`ofacturare.prg:333-336`), rulat in interiorul - functiei `factureaza()`, imediat dupa incarcarea cursorului candidat (`crsarticole`) si - **inainte** de construirea `crsfactura` (`creeaza_facturacrs`, linia 338) — adica la - **deschiderea documentului / incarcarea liniilor candidate**, nu la Termina/salvare. **Exista si - un al doilea mecanism, in Oracle**, in `cursor_retur_document` (`PACK:3993-4000`, - `CASE WHEN V_PROFORMA=1 THEN 0 ...`), dar acesta e **mort in fluxul curent**: `cursor_retur_document` - se cheama dintr-un singur loc (`ofacturare.prg:268`, ramura de copiere), cu - `V_PROFORMA = poDate.eProforma` **al documentului nou**, care e **intotdeauna 0** dupa copiere - (`nIdTipDoc` nu se propaga de la sursa, `ofacturare_comun.prg:370`) — deci ramura `V_PROFORMA=1` - nu se activeaza niciodata azi. **Nu e o contradictie intre cele doua rapoarte anterioare — sunt - doua mecanisme reale in cod, dar doar unul (cel VFP) e activ pe traseul de creare a unei - proforme; cel Oracle exista doar pentru copiere si acolo nu se declanseaza cu valoarea curenta a - parametrului.** -3. **`pret_achizitie` depinde de sursa**: pe calea principala (`cursor_preturi`, lista de preturi) - NU se completeaza deloc din cursor (coloana nici nu exista in `SELECT`-ul lui `cursor_preturi`) - — ramane `0`, prin acelasi tipar de valoare implicita ca la `id_gestiune` (`do_initializeaza_articol`). - Pe sursele care incarca dintr-un document existent (`cursor_avize`, `cursor_retur_document` — - avize si copiere), `PRET_ACHIZITIE` **e** selectat direct din `VANZARI_DETALII`, deci ajunge real. -4. **Daca proforma ar pastra `id_gestiune` real pana la salvare, nimic din contabilizare/stoc nu - s-ar strica** — pentru ca blocajul real nu e sentinela `-1000`, ci faptul ca `scrie_proforma` - (calea Oracle aleasa la Termina cand `eProforma=1`) **nu cheama niciodata** - `contabilizeaza_articol`/`descarca_gestiune` (confirmat deja in raportul-sursa de runda 12). - `adauga_articol_factura` (care ar primi `V_ID_GESTIUNE` real) **se cheama oricum**, neconditionat - de `eProforma`, pentru orice linie — deci un `id_gestiune` real ar ajunge fara probleme in - `VANZARI_DETALII_TEMP`/`VANZARI_DETALII`, fara sa declanseze nimic in plus. **`do_alege_stoc` nu - rezerva stoc real** — face doar un `SELECT` (cursoare `cursor_gestiuni_articol*`) si scade local, - in memorie, cantitatile deja puse pe documentul curent (`crsfactura`), ca operatorul sa nu aleaga - de doua ori acelasi lot; nu scrie nimic in Oracle. Deci lasarea lui `do_alege_stoc` sa ruleze pe - proforma nu ar bloca stoc pentru nimeni. Singurul risc real gasit e cel deja semnalat in - `s5b_proiectare_proforma_copiere.md` §5 (drumul invers, proforma -> factura in aceeasi sesiune), - care ramane valabil indiferent de aceasta decizie. - ---- - -## Intrebarea 1 — se alege stocul la adaugarea unui articol pe proforma azi? - -**Raspuns: NU, pe niciuna dintre caile de creare directa a unei proforme** (nu prin copiere). - -### Calea principala — `cursor_preturi` - -`cursor_preturi` (`PACK:2138-2646`) e apelat pentru `tnTip` in `{45, 1, 22, 5, 29, 7, 10, 23}` -(`ofacturare.prg:275-282` — lista de preturi, inclusiv `restaurant`), adica cea mai comuna sursa -pentru o proforma noua. SELECT-ul lui `cursor_preturi` intoarce `GESTIONABIL` ca -`C.IN_STOC AS GESTIONABIL` (`PACK:2192`, `:2297` — valoarea reala din `NOM_ARTICOLE`, fara nicio -constienta de proforma; `cursor_preturi` **nu are parametru** `V_PROFORMA`, confirmat prin grep pe -tot fisierul: `V_PROFORMA` apare doar la declaratia si corpul lui `cursor_retur_document`, -`PACK:408, 3939, 3944, 3952, 3957, 3994`). - -Insa, imediat dupa ce acest cursor e adus in `crsarticole` in VFP, **inainte** ca operatorul sa -apuce sa vada gridul de linii: - -``` -COMUN\programe\ofacturare.prg:330-336 -* Daca este o proforma, consider toate articolele negestionabile, pentru a nu mai alege din stoc -* 12.03.2021 -IF poDate.eProforma = 1 - UPDATE (m.lcCursor) SET gestionabil = 0 - GO TOP IN (m.lcCursor) -ENDIF -``` - -`m.lcCursor` = `crsarticole` (setat la `:310`). Acest bloc ruleaza **dupa** `Do Case`-ul de -incarcare (`:266-308`) si **inainte** de `creeaza_facturacrs([crsfactura])` (`:338`) — adica la -compunerea listei de articole candidate, nu la salvare. Cand operatorul adauga o linie mai tarziu, -`Do Case`-ul care alege dialogul (`ofacturare.vc2:13803-13809`) citeste `poArticol.gestionabil`, -care e deja `0` pentru **toate** liniile candidate (setat in masa mai sus), deci intra pe ramura -`frm_articol_factura`, **niciodata** pe `Thisform.do_alege_stoc`. - -### Celelalte cursoare (fara copiere) - -Verificat direct in `PACK_FACTURARE`, niciunul din urmatoarele cursoare, folosite pentru celelalte -surse ale unei proforme noi, nu are parametru `V_PROFORMA` si nici nu calculeaza `GESTIONABIL` in -functie de proforma — toate intorc valoarea reala din nomenclator/contract: - -| Cursor | Apelat pentru (`tnTip`) | `GESTIONABIL` in SELECT | Linie | -|---|---|---|---| -| `cursor_preturi` | 45,1,22,5,29,7,10,23 | `C.IN_STOC` | `PACK:2192,2297` | -| `cursor_contract` | 2,26,6,52 | `A.GESTIONABIL` (coloana reala) | `PACK:2411,2487,2572` | -| `cursor_comanda` | 3,21,25,28,42,47 | `E.IN_STOC` | `PACK:2789` | -| `cursor_lucrare` | 27 | `C.IN_STOC` | `PACK:3029` | -| `cursor_articole_k` | 48,49 | `C.IN_STOC` | `PACK:3117` | -| `cursor_avize` | 4 | `C.IN_STOC` | `PACK:3634` | -| `cursor_aviz_nir` | 30 | `0` (hardcodat) | `PACK:3745` | -| `cursor_gestiune` | 41 | `A.GESTIONABIL` | `PACK:4104` | -| `cursor_retur` | 8,9,24 | (nu are coloana `GESTIONABIL` separata — vezi nota) | `PACK:3934-3948` | - -Pentru **toate** aceste surse, aceeasi bucla `ofacturare.prg:330-336` e singurul loc care forteaza -`gestionabil=0` cand `poDate.eProforma=1` — mecanismul e identic indiferent de sursa, pentru ca -`UPDATE (m.lcCursor) SET gestionabil = 0` ruleaza pe `crsarticole` dupa orice ramura a `Do Case`-ului -de la `:266-308`, nu doar pe ramura lista-de-preturi. - -### Ramura de copiere — singura cu parametru `V_PROFORMA` in Oracle - -`Case m.llCopiere` (`ofacturare.prg:267-268`) e **singura** ramura care apeleaza -`cursor_retur_document`, singurul cursor cu parametru `V_PROFORMA`. Insa la copiere, documentul nou -pleaca intotdeauna cu `eProforma=0` (`nIdTipDoc` explicit necopiat, `ofacturare_comun.prg:370` — -deja stabilit in `s5b_proiectare_proforma_copiere.md` §2.3), deci `V_PROFORMA` trimis e `0`, iar -ramura `GESTIONABIL = B.IN_STOC` (valoarea reala) se activeaza, nu `WHEN V_PROFORMA=1 THEN 0`. Asta -e comportamentul **corect si dorit** la copiere (liniile redevin gestionabile) — dar confirma ca -`V_PROFORMA=1` nu apare niciodata cu valoarea `1` in vreun apel real azi (vezi Intrebarea 2). - -**Concluzie Intrebarea 1**: pe orice cale de creare directa a unei proforme (nu copiere), la -momentul `Do Case`-ului de adaugare a liniei (`ofacturare.vc2:13803`), `poArticol.gestionabil` e -deja `0` — fortat de VFP la incarcare, nu valoarea reala din nomenclator. `do_alege_stoc` nu ruleaza -niciodata pentru o linie noua adaugata sub `eProforma=1`, indiferent de sursa. - ---- - -## Intrebarea 2 — unde exact se face marcarea negestionabila si CAND? - -**Doua locuri in cod, dar un singur loc activ azi:** - -### 2.1 VFP — activ, la incarcarea documentului (nu la salvare) - -`COMUN\programe\ofacturare.prg:333-336`, in interiorul procedurii `factureaza()`. Secventa completa -in `factureaza()`: - -1. `:266-308` — `Do Case` pe `tnTip`/`llCopiere`, alege cursorul Oracle si il aduce in `crsarticole`. -2. `:311` — `goExecutor.oExecute(lcSqlCursor, lcCursor)` — executa efectiv apelul Oracle. -3. `:324-336` — daca sunt randuri, **si daca `poDate.eProforma = 1`**: `UPDATE crsarticole SET gestionabil = 0`. -4. `:338` — abia acum `creeaza_facturacrs([crsfactura])` construieste cursorul gol de linii ale - documentului; liniile candidate (`crsarticole`, deja marcate) raman disponibile pentru ca - operatorul sa aleaga din ele. - -Acest pas ruleaza **la deschiderea ecranului de adaugare articole**, mult inainte de `Termina` -(`do_scrie_factura`, apelat doar la click pe butonul de finalizare — vezi 2.3 mai jos). Nu exista -niciun `UPDATE`/`REPLACE gestionabil With 0` la salvare in `do_scrie_factura` sau `do_scrie_articole` -— am cautat explicit `gestionabil` in ambele metode (`ofacturare.vc2:14197-14380`,`:13967-14150`) si -singura referinta la `gestionabil` in acea zona e citirea lui la `Do Case`-ul din `:13803` (decizia -de dialog), nu o scriere. - -### 2.2 Oracle — exista in cod, dar mort in fluxul curent - -`cursor_retur_document` (`PACK:3949-4064`), branch la `PACK:3993-4000`: -```sql -(case - when V_PROFORMA = 1 then 0 - when V_COPIERE = 1 then B.IN_STOC - else A.GESTIONABIL - end) AS GESTIONABIL, -``` -cu comentariu explicit in cod: `-- V_PROFORMA: Daca este proforma (1), fac articolele negestionabile -sa pot alege orice cantitate` (`PACK:3957`). Acest cod **exista si e corect scris**, dar -`cursor_retur_document` are un singur punct de apel in tot codul VFP (confirmat prin cautare in -`ofacturare.prg`, singura potrivire e la `:268`; potrivirile suplimentare gasite in -`COMUN\.svn\pristine\*` sunt copii istorice ale **aceleiasi** linii, nu apeluri suplimentare), si -acolo `V_PROFORMA` trimis e `poDate.eProforma` **al documentului nou** din copiere, care e -intotdeauna `0` (Intrebarea 1). **Deci acest branch Oracle nu se activeaza niciodata cu valoarea 1 -in fluxul curent** — e cod mort din perspectiva efectului observabil, desi sintactic corect si -prezent. - -### 2.3 Clarificare pe "inainte de compunerea documentului" vs. "la salvare" - -Formularea raportului de runda 9 ("VFP marcheaza toate liniile proformei negestionabile inainte de -compunerea documentului") e **corecta** — "compunerea documentului" inseamna acolo constructia -listei de articole candidate/`crsfactura` (pasul 4 de mai sus), care se intampla la -**deschiderea** ecranului de facturare, nu la apasarea `Termina`. Nu exista o contradictie reala cu -mentiunea `V_PROFORMA` din `cursor_retur_document` din brief — acel `CASE` Oracle e un mecanism -separat, pentru un cursor diferit (candidati la copiere), care azi nu se declanseaza niciodata cu -`V_PROFORMA=1`. Ambele afirmatii din surse sunt adevarate simultan, pentru ca descriu lucruri -diferite. - -**Concluzie Intrebarea 2**: singurul mecanism activ e `UPDATE` de masa in VFP -(`ofacturare.prg:333-336`), care ruleaza in `factureaza()`, la incarcarea/compunerea listei de -articole candidate — **cu mult inainte** de `Termina`/`do_scrie_factura`. Mecanismul Oracle din -`cursor_retur_document` exista in cod dar nu se activeaza cu valoarea curenta a parametrilor. - ---- - -## Intrebarea 3 — se completeaza `pret_achizitie` pe liniile de proforma? - -**Depinde strict de sursa cursorului, la fel ca la `gestionabil` — dar cu rezultat opus pentru -calea principala.** - -### Ce cursoare selecteaza `PRET_ACHIZITIE` - -Cautare directa in `PACK_FACTURARE` (`grep PRET_ACHIZITIE`): coloana **nu apare deloc** in -`cursor_preturi` (`PACK:2138-2646`), `cursor_contract` (`:2646-2952`), `cursor_comanda` -(`:2952-3173`), `cursor_lucrare` (`:3173-3595`), `cursor_articole_k` (`:3595-3703`), -`cursor_gestiune` (`:4158-4349`). Apare doar in: -- `cursor_avize` (in jurul liniilor `PACK:3754-3864` — sursa `VANZARI_DETALII`/`RUL`, articol - provenit dintr-un aviz existent); -- `cursor_retur_document` (`PACK:4026`, `A.PRET_ACHIZITIE` direct din `VANZARI_DETALII`, folosit la - copiere). - -### Ce se intampla in VFP cand lipseste - -`crsfactura` (structura din `creeaza_facturacrs`, `ofacturare_comun.prg:1777`) are un camp -`pret_achizitie` in schema — dar o linie noua nu se construieste prin copiere in masa din -`crsarticole`, ci prin `Scatter`/`Gather` pe obiectul `poArticol`, populat cand utilizatorul alege un -articol din grid. `do_initializeaza_articol` (`ofacturare.vc2:13715-13719`), apelat pentru orice -linie care trece prin `frm_articol_factura` (ramura negestionabila, deci si orice linie de -proforma): -``` -If Type('toArticol.pret_achizitie')="U" - AddProperty(toArticol,'pret_achizitie',0) -Endif -If Isnull(toArticol.pret_achizitie) - toArticol.pret_achizitie = 0 -Endif -``` -Deci: **pe calea principala (lista de preturi), `pret_achizitie` ramane `0`** pe liniile de proforma -— campul nici nu exista in `crsarticole` sursa (cursorul Oracle nu-l selecteaza), asa ca proprietatea -lipseste pe `poArticol` si `do_initializeaza_articol` o creeaza cu valoare implicita `0`, exact -acelasi tipar defensiv folosit pentru `id_gestiune=-1000` (aceeasi metoda, linii apropiate, -`:13618-13623` vs `:13715-13719`). - -**Pe sursele care carata un document existent** (`cursor_avize`, si `cursor_retur_document` la -copiere), `pret_achizitie` **e** populat real din `VANZARI_DETALII.PRET_ACHIZITIE` a documentului -sursa, deci proprietatea exista pe `poArticol` si `do_initializeaza_articol` nu o suprascrie. - -### Unde ajunge - -Indiferent de valoare (`0` sau reala), `poArt.pret_achizitie` merge neschimbat catre Oracle ca -parametru pozitional in apelul catre `adauga_articol_factura`: -``` -ofacturare.vc2:14073 -... + Alltrim(Str(poArt.pret_achizitie,18,gnPPret)) + [,] + ... -``` -primit ca `V_PRET_ACHIZITIE_TEMP` (`PACK:4995`), copiat direct `V_PRET_ACHIZITIE := V_PRET_ACHIZITIE_TEMP` -(`PACK:5031`, fara sentinela, spre deosebire de `id_gestiune`) si scris ca atare in -`VANZARI_DETALII_TEMP.PRET_ACHIZITIE` (`PACK:5229/5259`), apoi in `VANZARI_DETALII` la -`scrie_in_vanzari`. - -**Concluzie Intrebarea 3**: pe calea principala (lista de preturi), `pret_achizitie` ramane `0` pe -liniile de proforma — nu vine din `do_alege_stoc` (care nu ruleaza) si nici din cursorul Oracle (care -nu-l selecteaza pe aceasta ramura). Pe sursele avize/copiere, valoarea reala din documentul sursa se -pastreaza. - ---- - -## Intrebarea 4 — ce s-ar strica daca proforma ar pastra gestiunea reala pana la salvare? - -### 4a. `adauga_articol_factura` se cheama si pentru liniile de proforma? - -**Da, neconditionat.** `do_scrie_factura` (`ofacturare.vc2:14197+`) cheama -`Thisform.do_scrie_articole()` la linia **14264**, **inainte** de `Do Case poDate.eProforma = 1` -(care alege intre `scrie_proforma` si `scrie_factura2`, la `:14282`): -``` -ofacturare.vc2:14264 -llReturn = Thisform.do_scrie_articole() && se deschide tranzactie manuala -... -ofacturare.vc2:14282 -Do Case - Case poDate.eProforma = 1 - * scrie_proforma are aceiasi parametri ca scrie_factura2 - * salveaza doar in vanzari, nu si in contabilitate - lcSql = [{call pack_facturare.scrie_proforma(...)}] -``` -`do_scrie_articole` parcurge liniile din `crsfactura` si cheama `adauga_articol_factura` pentru -fiecare (`ofacturare.vc2:14069-14073`, deja citat in raportul-sursa) — **acelasi cod, indiferent de -`eProforma`**. Daca `poArt.id_gestiune` ar fi real (nu `-1000`), `adauga_articol_factura` l-ar -traduce direct in `V_ID_GESTIUNE2 := V_ID_GESTIUNE` (`PACK:5032-5034`, gate-ul `<> -1000`) si l-ar -scrie ca atare in `VANZARI_DETALII_TEMP.ID_GESTIUNE` (`PACK:5237,5267`), apoi in -`VANZARI_DETALII.ID_GESTIUNE` la `scrie_in_vanzari` (`INSERT INTO VANZARI_DETALII SELECT FROM -VANZARI_DETALII_TEMP`, fara nicio conditie suplimentara pe `id_gestiune`). - -### 4b. Are efect ca linia sa ramana cu `ID_GESTIUNE` completat, cata vreme `scrie_proforma` nu cheama `contabilizeaza_articol`? - -**Niciunul, pe partea de contabilizare/descarcare.** Confirmat deja (runda 12, -`s5b_proiectare_proforma_copiere.md`, sectiunea "Descoperire centrala"): `scrie_proforma` -(`PACK:5637-5671`) cheama **doar** `scrie_in_vanzari` — insereaza antetul si liniile ca atare, apoi -seteaza `VANZARI.EPROFORMA=1`. **Nu cheama** `contabilizeaza_articol` in niciun caz. Sentinela -`-1000` conteaza doar **in interiorul** lui `contabilizeaza_articol` (`PACK:7472-7476`), care nu -ruleaza deloc pentru `scrie_proforma`. Deci `ID_GESTIUNE` real pe o linie de proforma nu ar declansa -`descarca_gestiune`, nu ar genera `NOTE_CONTABILE`, nu ar atinge `RUL`/`STOC` — pentru ca intreaga -functie care ar face asta nu se executa pe traseul `scrie_proforma`. - -### 4c. Rapoarte/view-uri care citesc `ID_GESTIUNE` pe documente `eProforma=1` si ar arata altceva? - -Cautare `eproforma` (case-insensitive) in tot `PACK_FACTURARE`: doar 3 potriviri, toate in -`scrie_proforma`/comentarii (`PACK:1408, 5666, 5668`) — nicio alta procedura/vedere din pachet nu -filtreaza sau calculeaza dupa `EPROFORMA`. - -Pe partea VFP, `crsDetaliiListare` (folosit la **relistarea** unui document deja emis, inclusiv -proforma — `oproceduri_facturare.prg:1111-1126`, sursa `fact_vfacturi2`) **include** coloana -`id_gestiune` in schema si in `SELECT`, indiferent de `eproforma` (nu exista filtru pe -`eproforma` in acest `SELECT`). Deci daca `ID_GESTIUNE` ar fi real pe o proforma, acest cursor l-ar -citi ca atare la relistare — azi citeste `NULL`. **Nu am putut confirma daca vreun raport `.frx` -tiparit efectiv afiseaza `id_gestiune`/`nume_gestiune` pe documentul proforma insusi** -(rapoartele `PROFORMA`/`PROFORMA_VAL` nu au versiune text `.fr2` generata in `Rapoarte\`, deci nu -s-au putut grep-ui direct) — de regula gestiunea e un camp intern, nu unul tiparit pe un document -comercial, dar afirmatia ramane neconfirmata pe cod, nu doar presupusa corecta. Vezi "Ce nu s-a -putut stabili". - -### 4d. `do_alege_stoc` rezerva stoc sau doar alege? - -**Doar alege — nu scrie nimic in Oracle.** Corpul complet al `do_alege_stoc` -(`ofacturare.vc2:13200-13400+`) face exclusiv: (1) un `SELECT` prin -`cursor_gestiuni_articol`/`cursor_gestiuni_articol_retur`/`cursor_gestiuni_articol_stoc0` (toate -proceduri read-only, populeaza un cursor local `crsgestarticoltemp`), (2) o scadere **locala, in -memorie**, a cantitatilor deja adaugate pe **acelasi document, in aceeasi sesiune** -(`ofacturare.vc2:13273-13311`: `SELECT ... FROM crsfactura ... INTO CURSOR crsFacturaArticoleTemp`, -apoi `a.cantitate - Nvl(b.cantitate,0)` la afisare), ca sa nu i se ofere operatorului sa aleaga de -doua ori acelasi lot pe acelasi document. **Nu exista niciun `INSERT`/`UPDATE` catre Oracle in -aceasta metoda** — `goExecutor.oExecute` apare o singura data, pentru `SELECT`-ul de la pasul (1). -Blocarea/decrementarea reala a stocului se intampla abia la `descarca_gestiune` -(`PACK:7648-7797`), apelata doar din `contabilizeaza_articol`, care (per 4b) nu ruleaza niciodata -pentru `scrie_proforma`. **Deci chiar daca `do_alege_stoc` ar rula pe liniile unei proforme, nu ar -"tine" stoc blocat pentru nimeni** — nu exista mecanism de rezervare reala in aplicatie pe acest -traseu, doar o verificare locala de neduplicare in sesiunea curenta. - -**Concluzie Intrebarea 4**: nimic din contabilizare, descarcare de gestiune sau stoc real nu s-ar -strica daca proforma ar pastra `id_gestiune` real pana la salvare — blocajul functional e la nivelul -routing-ului `scrie_proforma` (care nu cheama `contabilizeaza_articol`), nu la nivelul sentinelei -`-1000`. Singurul loc unde s-ar vedea o diferenta reala e la **relistarea** documentului -(`crsDetaliiListare`/`fact_vfacturi2`), unde `id_gestiune` ar aparea completat in loc de `NULL` — -efect cosmetic/informational, nu unul care sa afecteze stocul sau contabilitatea, cu rezerva ca nu -s-a confirmat daca vreun `.frx` chiar il tipareste. Riscul real ramas e cel deja identificat in -`s5b_proiectare_proforma_copiere.md` §5 (drumul invers, proforma comutata inapoi in factura in -aceeasi sesiune, inainte de Termina) — acela nu se schimba, indiferent de aceasta decizie, pentru ca -depinde de routing-ul de la Termina, nu de momentul in care se alege gestiunea. - ---- - -## Ce nu s-a putut stabili - -1. **Daca vreun raport `.frx` tiparit (PROFORMA/PROFORMA_VAL) afiseaza efectiv `id_gestiune` sau - `nume_gestiune` pe documentul insusi** — rapoartele nu au versiune text `.fr2` generata in - `Rapoarte\`, asa ca nu s-a putut grep pe continutul lor; ar necesita fie generarea textului cu - `git_sync.ps1`/`foxbin2prg` (in afara mandatului read-only), fie deschidere in IDE VFP. -2. **Nu s-a rulat nimic pe date vii** — toata analiza e trasare de cod static (VFP text + PL/SQL - text), conform mandatului read-only. -3. **Cautarea "eproforma" in Oracle** a acoperit doar `PACK_FACTURARE` — nu s-au verificat alte - pachete/view-uri/rapoarte Oracle care ar putea referi `VANZARI.EPROFORMA` impreuna cu - `ID_GESTIUNE` (de exemplu rapoarte de gestiune/stoc din alte module ROA). Zero rezultate in - `PACK_FACTURARE` nu e dovada de completitudine pentru intreaga schema. -4. **`cursor_retur`** (`PACK:3934-3948`, folosit pentru `tnTip` 8,9,24 — retururi) nu a fost citit - integral pentru coloana `GESTIONABIL` (tabelul de la Intrebarea 1 il listeaza fara valoare - confirmata) — nu are parametru `V_PROFORMA` (confirmat prin grep), deci concluzia generala - (marcarea vine doar din VFP) ramane valabila, dar valoarea exacta intoarsa de coloana nu a fost - verificata linie-cu-linie. - ---- - -## Handoff - -Cercetare incheiata intr-o singura sesiune, fara sa fie nevoie de predare de context. Toate cele 4 -intrebari din brief au raspuns cu dovada `fisier:linie`, plus rezumatul executiv de mai sus. Niciun -fisier de cod atins, niciun `git_sync.ps1`/`txt2vcx.ps1` rulat, niciun commit, nicio scriere pe -Oracle. diff --git a/docs/cercetare/zi_curs_validare.md b/docs/cercetare/zi_curs_validare.md deleted file mode 100644 index 16ccca6..0000000 --- a/docs/cercetare/zi_curs_validare.md +++ /dev/null @@ -1,262 +0,0 @@ -# Cercetare: validarea zi_curs la ofacturare.vc2:8076 si impactul asupra S4d - -## Verdict (5-10 randuri) - -Ascunderea selectorului de zi curs NU va lasa un document fara curs si NU va cadea la salvare, -CU CONDITIA sa se respecte precedentul deja existent in cod: `poDate.zi_curs` primeste un implicit -necondiionat (data documentului) chiar in `oDateFactura.Init`/`Reset` -(`COMUN\programe\ofacturare_comun.prg:247` si `:496`), INAINTE ca formularul sa decida ce ascunde. -Nicaieri codul nu goleste `poDate.zi_curs` cand controlul e ascuns/eliminat. Riscul real de eroare -Oracle (-20005, "Nu este setat cursul...") vine NU din camp gol, ci din faptul ca verificarea -`pack_facturare.verifica_cursuri_valute` (in `cursor_preturi`) ruleaza NECONDITIONAT de `in_valuta` -al documentului curent si exclude doar moneda nationala - deci un `zi_curs` implicit (azi) care nu -are curs setat in tabela CURS pentru o valuta folosita in listele de preturi ale utilizatorului -poate pica oricum, INDIFERENT daca selectorul e vizibil sau nu. Linia :8076 NU apartine formularului -de factura, ci unui formular separat, restrans, pentru AVIZ PE LUCRARE / AVIZ PE NIR -(`frm_date_aviz_lucrare`, tipuri 27 si 30) - fara control de valuta pe el - si valideaza `zi_curs` -strict pentru ca tipul 27 are nevoie de curs pentru articolele din comanda (posibil in valuta), -INDEPENDENT de `poDate.in_valuta`. Precedentul cerut la punctul 6 exista deja, dar in alt formular: -`frm_date_factura`, tipurile 8/9 (retur), unde `clb_zi_curs` e eliminat NECONDITIONAT si documentul -se salveaza corect - motivul principal e ca SQL-ul pentru retur (`cursor_retur`) nici nu foloseste -`poDate.zi_curs`. - -## 1. Ce valideaza linia :8076 - -Index de simboluri: `frm_date_aviz_lucrare.inainte_de_do_termin` = `ofacturare.vc2:8054-8119` -(fisier real: `COMUN\clase\ofacturare.vc2`). - -Blocul complet (validare secventiala pe formular, fiecare `Case` opreste salvarea la primul fail): - -``` -COMUN\clase\ofacturare.vc2:8063-8079 -Do Case -Case Empty(poDate.dataireg) - amessagebox("Nu ati completat data inregistrarii!",48,"Atentie") - ... -Case Empty(poDate.dataact) - amessagebox("Nu ati completat data documentului!",48,"Atentie") - ... -Case Empty(Nvl(poDate.id_fdoc,0)) - amessagebox("Nu ati ales felul documentului!",48,"Atentie") - ... -Case Empty(Nvl(poDate.zi_curs,{})) - amessagebox("Nu ati completat ziua cursului valutar!",48,"Atentie") - This.clb_zi_curs.SetFocus() - plReturn = .F. -Case Empty(poDate.nract) - ... -``` - -Valideaza STRICT prezenta unei date in `poDate.zi_curs` (`Empty(Nvl(...,{}))`), nu existenta unui -curs in baza pentru acea data - acel test se face abia in Oracle, la momentul in care se cere -cursorul de articole (vezi punctul 4). - -## 2. Cand ruleaza si pe ce tipuri de document - -`inainte_de_do_termin` e apelat la evenimentul butonului "Termina" al formularului (`BUT_TERMIN1`, -vezi lista de obiecte a clasei, `ofacturare.vc2:7674-7679`) - deci la incercarea de a incheia -completarea datelor de antet, inainte de a trece la ecranul de articole. - -`frm_date_aviz_lucrare` NU e formularul de factura. E instantiat DOAR pentru doua tipuri de -document, ambele AVIZ (nu FACTURA): - -``` -COMUN\programe\ofacturare.prg:187-226 (identic in factureaza2, :697-699) -Do Case - Case tnTip = 27 - poDate.nIdTipDoc = 6 && AVIZ - Case tnTip = 30 - poDate.nIdTipDoc = 6 && AVIZ - Case tnTip < 21 Or Inlist(tnTip, 45, 48, 49, 51, 52) - poDate.nIdTipDoc = 5 && FACTURA - ... -Do Case - Case tnTip = 27 - lcObiect = [frm_date_aviz_lucrare] - Case tnTip = 30 - lcObiect = [frm_date_aviz_lucrare] - Case tnTip < 21 Or Inlist(tnTip, 45, 48, 49, 51, 52) - lcObiect = [frm_date_factura] - Otherwise - lcObiect = [frm_date_aviz] -Endcase -``` - -Titlul formularului confirma: `Lb_titlu_alb_b121.Caption = "AVIZ PE BAZ­ DE LUCRARE"` -(`ofacturare.vc2:7670`), schimbat in `Init` la "AVIZ PE BAZ­ DE NIR" cand `nid_tip = 30` -(`ofacturare.vc2:8154-8161`). - -Nu exista o garda mai sus in lant care sa dezactiveze validarea :8076 pe vreun tip - ruleaza -identic pentru tip 27 si tip 30, necondiionat de `poDate.in_valuta`. IMPORTANT: aceasta clasa -NU are deloc control de valuta pe formular - lista de obiecte a clasei (`ofacturare.vc2:7623-7637`) -nu contine niciun `ct_clb_valuta`. Deci validarea de aici nu e legata de "documentul e in valuta", -ci de nevoia formularului AVIZ-LUCRARE de a avea o zi de curs pentru articolele comenzii care pot -fi preturite in valuta (vezi punctul 4, `cursor_lucrare`). - -Concluzie: linia :8076 NU intra deloc in fluxul de facturare (FACTURA) vizat de decizia 15 - e un -formular separat, pentru un subset ingust de avize (27 = aviz pe lucrare, 30 = aviz pe NIR). - -## 3. Ce se intampla daca zi_curs e gol la :8076 - -Mesaj de eroare blocant (nu doar avertisment): `amessagebox("Nu ati completat ziua cursului -valutar!",48,"Atentie")`, apoi `This.clb_zi_curs.SetFocus()` si `plReturn = .F.` - `Case`-ul -opreste executia `Do Case` (Otherwise nu se mai atinge), iar `inainte_de_do_termin` returneaza -`.F.`, ceea ce (conform conventiei din restul clasei) blocheaza inchiderea formularului / trecerea -la pasul urmator. - -## 4. Cine mai citeste poDate.zi_curs - -Cautare `zi_curs` in `.vc2`/`.prg`/`.sc2` si in sursele Oracle -(`docs\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql`): - -**a) Validari de formular (camp obligatoriu):** -- `frm_date_aviz_lucrare.inainte_de_do_termin` :8076 - necondiionat (vezi punctele 1-2). -- `frm_date_factura.inainte_de_do_termin`, `ofacturare.vc2:9484`: - ``` - Case poDate.in_valuta = 1 And Empty(Nvl(poDate.zi_curs,{})) And Type('thisform.clb_zi_curs.visible')<>'U' - ``` - Aici validarea E DEJA dublu conditionata: pe `in_valuta = 1` SI pe existenta controlului - (`Type(...)<>'U'` - devine 'U' daca controlul a fost eliminat cu `RemoveObject`). Deci pe factura - in lei, sau pe orice tip unde controlul a fost eliminat, validarea nu ruleaza deloc. Comentariul - `*!* modificare v 2.0.56` de langa arata ca exact acest lucru a fost REZOLVAT anterior pentru - formularul de factura. - -**b) Populare implicita / sincronizare (fara conditie de in_valuta):** -- `oDateFactura.Init`, `COMUN\programe\ofacturare_comun.prg:247`: `.zi_curs = ldData` - - necondiionat, seteaza mereu data documentului curent (ldData = azi, ajustat la luna/anul - curent de facturare) INAINTE de blocul care seteaza `.in_valuta` (linia 248-250). -- `oDateFactura.Reset`, `ofacturare_comun.prg:496`: `.zi_curs = .Data` - la fel, necondiionat. -- `frm_date_aviz.Clb_dataact.Text_simplu1.LostFocus` (:7603-7604) si - `frm_date_aviz.Clb_dataireg...LostFocus` (:7610-7611): `poDate.zi_curs = poDate.dataact`, - necondiionat (formularul aviz general nu are guard, dar si nu are RemoveObject pe zi_curs). -- `frm_date_aviz_lucrare.Clb_dataact...LostFocus` (:8186-8187) si - `...Clb_dataireg...LostFocus` (:8193-8194): idem, necondiionat - zi_curs NU e niciodata - eliminat in aceasta clasa, deci sincronizarea merge mereu. -- `frm_date_factura.Clb_dataact...LostFocus` (:9805-9808) si `...Clb_dataireg...` (:9824-9827): - ACESTEA SUNT deja conditionate: `If Type('thisform.clb_zi_curs.visible')<>'U' ... zi_curs = - dataact ... Endif` (comentariu `*!* modificare v 2.0.56`). Cand controlul e eliminat, sincronizarea - se opreste - dar valoarea RAMASA de la Init/Reset nu se sterge, ramane cea de la creare. - -**c) Consum efectiv in SQL (trimis catre Oracle, cursoare de articole):** -`COMUN\programe\ofacturare.prg:266-308` (identic in `factureaza2`, :751-816) - alegerea SQL-ului de -populare a articolelor se face pe `tnTip`, NU pe `in_valuta`: -``` -Case Inlist(tnTip, 48, 49) -> cursor_articole_k(?poDate.zi_curs, ...) -Case tnTip = 45 -> cursor_preturi(?poDate.zi_curs, ...) -Case Inlist(tnTip, 1,22,5,29,7,10,23) -> cursor_preturi(?poDate.zi_curs, ...) -Case Inlist(tnTip, 2,26,6,52) -> cursor_contract(?poDate.zi_curs, ...) -Case Inlist(tnTip, 3,21,25,28,42,47) -> cursor_comanda(?poDate.zi_curs, ...) -Case tnTip = 4 -> cursor_avize(...) [FARA zi_curs] -Case Inlist(tnTip, 41) -> cursor_gestiune(?poDate.zi_curs, ...) -Case tnTip = 30 -> cursor_aviz_nir(...) [FARA zi_curs] -Case tnTip = 27 -> cursor_lucrare(?poDate.zi_curs, ...) -Case Inlist(tnTip, 8,9,24) -> cursor_retur(?poDate.in_valuta, ...) [FARA zi_curs] -``` -Descoperire cheie: pentru tipurile 8, 9, 24 (retur) SI 30 (aviz pe NIR), SQL-ul NU trimite deloc -`poDate.zi_curs` catre Oracle - `zi_curs` gol sau completat nu are niciun efect pentru aceste -tipuri. Acesta e motivul real pentru care eliminarea controlului la tip 8/9 e sigura (mai puternic -decat simpla existenta a unei valori implicite). - -Pentru tipurile care TRIMIT zi_curs, comportamentul in Oracle e diferit: -- `cursor_preturi` (`docs\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:2138+`) apeleaza NECONDITIONAT - `pack_facturare.verifica_cursuri_valute(V_DATA_CURS, V_ID_UTIL)` (linia 2153). Aceasta procedura - (`:16247-16274`) verifica cursul pentru TOATE valutele distincte din `FACT_VPRETURI_UTILIZATOR` - ale utilizatorului curent (nu doar valuta documentului!) si arunca - `RAISE_APPLICATION_ERROR(-20005, 'Nu este setat cursul din data de ... !')` daca oricare dintre - ele nu are curs care sa acopere `V_DATA_CURS` - EXCLUDE explicit moneda nationala - (`AND A.ID_VALUTA <> pack_facturare.nid_moneda_nationala`). Deci: chiar pe un document in LEI - (`in_valuta=0`), daca utilizatorul are liste de preturi in valuta configurate si data trimisa - (implicita sau nu) nu are curs setat, apelul PICA cu -20005 - INDIFERENT de vizibilitatea - selectorului pe formular. Riscul nu vine din camp gol, ci din "camp cu o data pentru care nu - exista curs in tabela CURS". -- `cursor_articole_k` (`:3595-3701`) si `cursor_lucrare` (`:3173-3593`, foloseste `V_DATA_CURS` - pentru comenzi_elemente) - `cursor_articole_k` NU apeleaza `verifica_cursuri_valute`, doar face - `LEFT JOIN CURS ... WHERE DATA <= V_DATA_CURS AND DATA2 >= V_DATA_CURS` - daca nu gaseste, cade - silentios pe `NVL(D.CURS,0)` (pret gresit, nu eroare). `cursor_lucrare` (:3186-3218) INSA face - o verificare proprie, similara: daca articolele comenzii au valute fara curs pe `V_DATA_CURS`, - arunca acelasi `-20005`. -- Exista deja o rutina de recuperare la acest cod de eroare: `ofacturare.prg:313-317` - ``` - If lnSucces < 0 - AMESSAGEBOX(goExecutor.oPrelucrareEroare(), 16, "Eroare") - If goExecutor.nEroare = 20005 - vizualizeaza_curs(poDate.zi_curs) - ENDIF - ``` - Deci sistemul ANTICIPEAZA deja cazul "curs lipsa la data respectiva" si deschide un ecran de - gestiune a cursurilor - independent de validarea din formularul de date. - -**d) Afisare / etichetare (fara risc):** -- `frm_facturare_articole.Init` (:15097-15098) si `frm_facturare_articole2.Init` (:19004-19005): - `If !Empty(Nvl(poDate.zi_curs,{})) Then Thisform.lb_cursuri.Caption = "Curs valutar (" + - Dtoc(poDate.zi_curs) + ")"` - deja tolereaza gol (nu afiseaza nimic), fara eroare. -- `frm_date_factura.do_cauta_valuta` (:9344-9359) - dupa alegerea valutei, muta focusul pe - `clb_zi_curs` DACA exista (`Type(...)<>'U'`), altfel pe `clb_serie_act`. Deja conditionat. -- `onom_curs.vc2` (`ck_zi_curs`, `tx_zi_curs`) - ecran DIFERIT, de administrare a cursurilor - valutare in sine (nu are legatura cu `poDate.zi_curs`; e o cautare "dupa ziua cursului" generica). - -## 5. Valoarea implicita azi si de unde vine - -Vine din `oDateFactura.Init`/`Reset`, necondiionat de tip sau de `in_valuta`: -- `Init` (`ofacturare_comun.prg:235-247`): `ldData = Ttod(get_ora())`, ajustat la luna/anul curent - de facturare (`gnAn`/`gnLuna`) daca `get_ora()` cade in alta luna; apoi `.zi_curs = ldData`. -- `Reset` (`ofacturare_comun.prg:486-496`): `.zi_curs = .Data` (unde `.Data` a fost deja setat tot - din `ldData`-ul curent). - -Deci implicit `zi_curs` = data curenta (get_ora, ajustata la perioada de facturare deschisa), NU -`Date()` brut si nu neaparat `dataact`/`dataireg` (desi acestea pornesc de la aceeasi `ldData`). -Ulterior, cat timp controlul `clb_zi_curs` exista pe formular, orice editare a `dataact`/`dataireg` -resincronizeaza `zi_curs = dataact` prin evenimentele `LostFocus` (vezi punctul 4b). Daca formularul -ar ascunde controlul FARA sa elimine obiectul si fara sa goleasca proprietatea, campul ar ramane -la valoarea implicita de la Init/Reset (sau la ultima valoare sincronizata inainte de ascundere). - -## 6. Precedent: tip cu campul ascuns care se salveaza corect - -DA, exista deja, dar in `frm_date_factura` (formularul de FACTURA), nu in `frm_date_aviz_lucrare`: - -``` -COMUN\clase\ofacturare.vc2:9717-9722 [frm_date_factura.Init] -*!* modificare v 2.0.56 -If Inlist(poDate.tip, 8, 9) - lnHeight = lnHeight - .clb_zi_curs.Height - laPozitii(.clb_zi_curs.TabIndex, 2) = 1 - .RemoveObject('clb_zi_curs') -Endif -*!* modificare v 2.0.56 ^ -``` - -Pentru tip 8 si 9 (facturi de retur - care pot fi chiar in valuta, vezi `ofacturare_comun.prg:248`: -`INLIST(m.tnTip, 5,6,7,9,10,52) -> .in_valuta = 1`), controlul `clb_zi_curs` e eliminat COMPLET de -pe formular, necondiionat de `in_valuta`, si documentul se salveaza corect. Motivele, in ordine de -robustete: -1. Validarea din `inainte_de_do_termin` (:9484) e deja garda cu `Type(...)<>'U'`, deci se - auto-dezactiveaza cand controlul nu mai exista. -2. SQL-ul de populare articole pentru tip 8/9 e `cursor_retur(?poDate.in_valuta,...)` - (`ofacturare.prg:306-307`) - NU trimite deloc `poDate.zi_curs`, deci nu poate cauza -20005 din - cauza acestui camp. -3. Chiar daca ar fi trimis, `poDate.zi_curs` tot ar avea valoarea implicita de la Init/Reset - (punctul 5) - nimic nu-l goleste la `RemoveObject`. - -Aceasta e "reteta" cerinta de punctul 6: eliminarea vizuala e sigura pentru ca (a) validarea are -deja garda pe existenta controlului, si (b) proprietatea `poDate.zi_curs` nu e niciodata golita - -ramane pe implicitul din Init/Reset. - -Pentru `frm_date_aviz_lucrare` (linia :8076) NU exista un tip cu campul ascuns - `clb_zi_curs` nu e -eliminat pentru nici tip 27, nici tip 30. Motivul plauzibil: tip 27 (aviz pe lucrare) chiar -foloseste `zi_curs` in `cursor_lucrare` pentru articolele comenzii (posibil in valuta), independent -de `poDate.in_valuta` al documentului-aviz insusi - deci acolo campul NU e un candidat sigur pentru -ascundere pe baza lui `in_valuta`. Pentru tip 30, SQL-ul (`cursor_aviz_nir`) nu foloseste `zi_curs` -deloc, deci validarea de acolo e superflua dar inofensiva (campul e mereu populat implicit). - -## Ramas de verificat - -- Nu am gasit inca daca decizia 15 / S4d intentioneaza sa includa si `frm_date_aviz_lucrare` in - formularul unificat, sau doar `frm_date_factura`/`frm_date_aviz`. Din cod, `frm_date_aviz_lucrare` - e un formular de sine statator, fara control de valuta, folosit doar pentru tnTip 27 si 30 - daca - planul S4d nu-l tinteste explicit, linia :8076 e in afara scopului imediat. -- Nu am verificat ce se intampla in `cursor_articole_k` (tip 48/49) si `cursor_gestiune` (tip 41) - fata de `verifica_cursuri_valute` - din citire, `cursor_articole_k` nu apeleaza acea procedura - (cade silentios pe curs 0), dar nu am verificat `cursor_gestiune`. -- Nu am verificat cum decide `frm_date_factura.Init` ce alte tipuri (in afara de 8,9) ar putea fi - candidate pentru ascunderea lui `clb_zi_curs` conform deciziei 15 - doar am confirmat mecanismul - existent si conditia dubla deja implementata la validare (in_valuta + Type<>'U'). diff --git a/docs/erori_deschise.md b/docs/erori_deschise.md new file mode 100644 index 0000000..13b01f9 --- /dev/null +++ b/docs/erori_deschise.md @@ -0,0 +1,12 @@ +# Erori deschise — formular facturare unificat + +Erori observate la rulare, neinvestigate inca. Se sterge intrarea cand e reparata. + +## 1. `Alias 'CRSGESTIUNE' is not found.` la parasirea combo-ului de gestiune + +- **Unde:** `FRM_FACTURARE_ARTICOLE2.GRD_FACTURA.CGESTIUNE.CCBOGESTIUNE.LOSTFOCUS`, linia 5 +- **Linia:** `REPLACE nume_gestiune WITH crsGestiune.nume_gestiune, id_gestiune WITH crsGestiune.id_gestiune IN crsFactura` +- **Mediu:** MARIUSM_AUTO, 09.09.2026 +- **De verificat:** cine creeaza `crsGestiune` si pe ce cale de cod ajunge `LostFocus` sa ruleze + inainte (sau dupa inchiderea) cursorului — probabil combo-ul isi pierde focusul intr-un context + in care cursorul nu a fost creat sau a fost deja inchis. diff --git a/docs/handoff_13_formular_unificat.md b/docs/handoff_13_formular_unificat.md deleted file mode 100644 index 81fd2dd..0000000 --- a/docs/handoff_13_formular_unificat.md +++ /dev/null @@ -1,798 +0,0 @@ -# Handoff — #13 formular unificat de facturare + editare prin regenerare - -Sesiune: 11.08.2026, **runda 17** (istoricul de mai jos e cumulativ; blocul rundei 14 e pastrat ca -atare). Runda 13 predase dupa cinci proiectari si deciziile 44-50. - -> ## RUNDA 17 — „Urmatorul bloc de lucru" S-A GOLIT. Proiectarea lui #13 e INCHEIATA. -> -> Runda 17 a executat **exact cele doua sarcini** care mai ramasesera (punctele 4 si 5 din lista de -> jos) si **nu a deschis niciuna noua**. -> -> > ### DECIZIA 66 (runda 17) RASTOARNA DECIZIA 64 — citeste asta inainte de orice paragraf despre asezare -> > **Motivul discountului sta LANGA DISCOUNT, in banda de totaluri, nu ca a treia sectiune jos.** -> > Formularea lui Marius: *„motiv discount vreau sa fie langa discount, nu a treia coloana"*. -> > **Randul de jos revine la DOUA sectiuni** (incasare, alte date), fiecare pe jumatate de latime — -> > adica exact decizia 57, punctul 2, neatinsa. **Argumentul „~440 px fiecare, strans" cade odata cu -> > decizia 64.** Mai jos, in istoricul cumulativ al rundei 16, decizia 64 apare inca in vigoare in -> > cateva paragrafe: **sunt istorie, nu stare curenta.** Locurile decisive sunt corectate; daca -> > gasesti unul necorectat, **decizia 66 castiga**. Enuntul complet, cu regula de activare adaugata de -> > proiectare (campul e activ **doar cand discountul nu e zero**): **in plan**, la „Decizia 66". -> -> **O singura decizie noua, 66**, deci numerotarea se opreste la **66**. - -> ### AUDIT DE INCHIDERE (18.08.2026) — planul e curatat de marcajele „deschis" ramase in urma -> Verificare ceruta de Marius inainte de a inchide sesiunea: **e planul finalizat?** Raspuns: **da ca -> proiectare**, dar avea **noua intrebari fantoma** — blocuri „De decis de Marius" si „ramane deschis" -> care supravietuisera deciziilor care le inchisesera. Un agent proaspat le-ar fi citit ca sarcini si -> ar fi redeschis lucruri transate. **Toate au fost stinse**, fiecare cu decizia care o inchide, in -> **8 linii** din plan (verificat prin diff fata de o copie de siguranta: zero modificari colaterale): -> - trei blocuri **„De decis de Marius"** (S5c, S8b, S11) → inchise de deciziile **51**, **52**, **53** -> si **56**; textul ramane ca **inventar al recomandarilor acceptate**, nu ca intrebari; -> - doua afirmatii „ramane deschisa asezarea campului de motiv" → inchise de **decizia 66**; -> - „din intrebarea 7 ramane deschisa partea de tipuri 48/49" → inchisa de **decizia 60**. -> -> **Ce a ramas deschis, si e corect asa — trei puncte, toate de implementare, niciunul blocant:** -> 1. **Relistarea unei facturi vechi deja trimise** (decizia 62, rest semnalat): dupa decizia 59 ar -> produce N randuri de discount in loc de unul, deci hartie diferita de originalul din eFactura. -> Relistarea nu e modificare, deci `EsteInEFactura` n-o acopera. **Nedecis, semnalat lui Marius.** -> 2. **`PROC_TVAV` ca parametru** (consecinta 3 a deciziei 54): doar daca se cere reproducere exacta -> peste o modificare legala de cota. **De decis separat.** -> 3. **De unde se reconstituie `IN_STOC`** (cerinta rundei 17): preconditie de proiectare a lui S8, -> trei variante scrise, niciuna aleasa. -> **Cod: neatins** — nici un `.vc2` / `.sc2` / `.prg` deschis macar. **Zero Oracle** in aceasta runda, -> nici macar `SELECT`. **Niciun commit dat de aceasta sesiune.** -> -> **Punctul 4 — golul `IN_STOC` din S8 — INCHIS**, prins acolo unde cerea handoff-ul precedent (in nota -> de executie a lui S8, nu la S12). Trei editari in plan, toate verificate pe disc dupa ce sesiunea de -> la #6 a rescris `docs\` in paralel: -> - `plan_13:3520-3546` — caseta „CERINTA DE EXECUTIE ADAUGATA IN RUNDA 17"; -> - `plan_13:3567-3569` — criteriul intra in „gata cand" al lui S8; -> - `plan_13` la S10, consecinta 1 — marcata **PRELUAT**, ca sa nu se redescopere. -> -> **Punctul dur, si e mai greu decat suna cerinta:** valoarea istorica a lui `IN_STOC` **nu e stocata -> nicaieri** — nu e coloana pe `VANZARI_DETALII` (verificat pe DB inca de la S10), traieste doar in -> temp. Deci „S8 incarca `IN_STOC` din document" **nu e implementabil ca atare azi**. De unde se -> reconstituie e o **preconditie de proiectare a lui S8** — trei variante scrise in plan (din urma -> lasata in rulaje/gestiune; nomenclatorul curent ca aproximatie **declarata**; coloana noua, deci -> migrare DB inainte de EXE). **Nu s-a ales niciuna** si nu se presupune niciuna. -> -> **Punctul 5 — mockup v9 — LIVRAT.** `docs\mockup_13_formular_unificat.html`, 71 336 octeti (era -> 67 075 la v8). Asezarea e acum **varianta D** cu **randul de jos in doua** si motivul discountului in -> banda de totaluri (deciziile 57 + 66): -> antet strans pe doua randuri fara titluri de grup; panoul „Discount pe document" **disparut**; banda -> de totaluri **in interiorul panoului Articole, dupa `.gridbar`**, deci lipita de grid, cu procentul -> de discount editabil pe loc, **motivul discountului imediat dupa suma pe care o explica** (decizia -> 66) si totalul mare singur la dreapta; sub ea, **doua** sectiuni colapsabile egale -> (incasare · alte date), cu **rezumatul continutului pe randul inchis**. -> Legenda are acum 5 callout-uri (2 si 4 repurpozate, 5 nou). In text au intrat deciziile -> **52, 53, 54, 59, 60, 61, 62, 63+65**. Raport: `docs\cercetare\mockup_v9_modificari.md`. -> **Verificat de sesiunea principala, nu preluat din raport:** 0 taguri nechise, 0 inchideri -> neasteptate, ` - - - -
- -

ROAFACTURARE · todos.txt punctul 13 · mockup, versiunea 9 (runda 17)

-

Un singur formular de facturare

-

- Datele documentului si articolele in aceeasi fereastra, fara incarcarea prealabila a tuturor - articolelor din toate politicile de preturi. Acelasi formular la introducere, la modificare, la - copiere si la proforma. -

-

- Mockup, nu implementare. Campurile, etichetele si coloanele sunt cele reale, verificate pe cod la - 09.08.2026 — rapoartele sunt in docs\cercetare\. - Plan: docs\plan_13_unificare_formular_facturare.md - Asezarea e varianta D (decizia 57, runda 15), cu motivul discountului in banda de totaluri - (decizia 66, runda 17). -

- -

1Formularul

-

- Deschis pe o factura in lei, emisa pe baza de comanda. Antetul e blocat — butonul din bara lui - arata creionul. Asezarea e varianta D: antetul strans pe doua randuri, fara panou separat de - discount, banda de totaluri lipita de grid pe toata latimea — cu procentul si motivul discountului - chiar in ea — si randul de jos cu doua sectiuni colapsabile, incasare si alte date. La introducerea - unui document nou arata identic, cu antetul deschis si campurile goale. -

- -
-
- Factura FF 1 244 / 07.08.2026 · SC EXEMPLU DISTRIBUTIE SRL - - Renunta - Termina -
-
- -
-
Document 1 - - antet blocat - - -
-
-
-
FACTURA
-
FF
-
1 244
-
07.08.2026
-
06.09.2026
-
-
-
SC EXEMPLU DISTRIBUTIE SRL F4
-
RO12345678 ANAF
-
14 820,00
-
CMD 3312 F4
-
DEPOZIT CENTRAL
-
-
-
- -
-
Articole 3
-
- +Linie noua - −Sterge linia - ✎Detalii linie - - ↧Adauga articole ▾ - 3 linii · comanda CMD 3312 acoperita 100% -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NrCod materialArticolUMGestiuneCantitatePret f. TVApret
c. TVA
Disc. %Disc. unitarExplicatie TVAVal. f. TVATVAVal. c. TVAExplicatie linie
1MAT-0041Ciment Portland 42,5R 40 kgSACDEPOZIT CENTRAL100,00028,50000,000,0000Livrari 21%2 850,00598,503 448,50—
2MAT-0107Adeziv gresie/faianta 25 kgSACDEPOZIT CENTRAL40,00042,00005,002,1000Livrari 21%1 596,00335,161 931,16conform comanda
3SRV-0002Transport marfaBUC—1,000250,0000✓0,000,0000Prestari 21%250,0052,50302,50—
4scrie codul sau denumirea…cautare pe server, pe masura ce scrii — nu se mai aduce lista de preturi intreaga
- -
- Ins — linie nouaDel — sterge - F4 — cautaCtrl+N / Ctrl+D -
-
-
Baza4 696,00
-
Discount articole84,00
-
Discount document4 - 0,00 %0,00 - ✓ evidentiat pe factura -
-
Motiv5 - motivul discountului… -
-
TVA986,16
-
Total factura5 682,16 lei
-
- - -
-
-
▸ Incasare - NUMERAR · 5 570,55 lei -
-
-
-
▾ Alte date 2
-
-
-
701
-
—
-
—
-
-
-
IONESCU V.
-
B 123 XYZ
-
ca a clientului
-
-
-
-
- - - - -
-
1

Antetul, blocat pana ceri altfel

-

Un singur buton. Creionul deschide antetul si devine discheta; discheta salveaza doar - antetul si redevine creion. Nicio bifa — nici cele patru de azi, pe serie, numar, data si - scadenta. Fara valuta si fara data de curs — factura e in lei si niciun articol nu are pret in - valuta (sectiunea 4). Cat anume deschide, si de ce nu chiar tot in prima etapa: - sectiunea 2.

-
2

Randul de jos, doua sectiuni

-

Incasarea si alte date (analitice, delegat/transport, adresa de facturare, text aditional) - stau colapsate, una langa alta, nu una sub alta (decizia 57). Sunt independente — se poate tine - deschisa doar una — iar randul inchis arata rezumatul continutului, nu doar titlul: cand - incasarea e completata arata suma si modalitatea, cand nu e nimic completat rezumatul spune asta. - Campurile din interior stau pe orizontala, doua randuri fiecare. A treia sectiune, pentru - motivul discountului, a fost incercata si respinsa — motivul sta langa discount, in banda de - totaluri (decizia 66 rastoarna decizia 64). - Incasarea ramane cea deosebita (decizia 41): e singura cu efecte laterale reale — - alocare/dezalocare de numere — deci garda de non-alocare la deschidere se scrie si se testeaza - intr-un singur loc, si e singura blocata pe un document deja emis (decizia 25). Tot pe document - emis, analiticele se vad dar nu se editeaza de aici — sectiunea 2.

-
3

Un singur buton de adaugare

-

Adauga articole deschide un meniu cu optiunile potrivite sursei — sectiunea 3. - Dispare gridul de sus cu toate articolele din toate politicile.

-
4

Banda de totaluri, lipita de grid

-

Baza, discount articole, discount document si TVA, desfacute pe un singur rand, cu totalul - mare singur la dreapta (decizia 57). Discountul de document se editeaza pe loc, procentul langa - suma; bifa „se pune in evidenta…" de pe panoul care disparea s-a mutat aici, discret. Repartizarea - pe cote de TVA e automata si proportionala (decizia 59) — nu se cere nimic utilizatorului.

-
5

Motivul discountului, langa discount

-

Singura bucata din reparatia discountului de document care cere UI nou (decizia 61): text - optional, ajunge in AllowanceChargeReason din eFactura (ReasonCode - ramane 95). Sta in banda de totaluri, imediat dupa suma pe care o explica - (decizia 66) — nu ca sectiune separata jos, cum se stabilise la decizia 64. Ia latimea ramasa - intre discount si TVA; e activ doar cand discountul de document nu e zero — un motiv fara - discount n-are ce explica.

-
- -

2Modificarea antetului, fara sa treci prin articole

-

- Protectia antetului nu e o inventie: formularul de azi de modificare are deja patru bife, cate una - pe camp, care deblocheaza serie, numar, data si scadenta. Ce se schimba e ca toate patru dispar, - inlocuite de un singur buton in bara panoului, care isi schimba imaginea: creion cat timp - antetul e blocat, discheta cat timp e deschis. -

- -
-
-
-
1 · antet blocat
-
-
Document - antet blocat -
-
-
FF
-
1 244
-
06.09.2026
-
IONESCU V.
-
-
-
-

Nimic nu se poate schimba din greseala. Documentul se poate citi in liniste. - Butonul arata creionul: apasarea deschide.

-
-
-
-
2 · antet deschis
-
-
Document - antet deschis -
-
-
FF
-
1 244
-
06.09.2026 ▾
-
IONESCU V. F4
-
-
-
-

Tot antetul e editabil, si campurile de identitate. Butonul a devenit discheta: - a doua apasare salveaza antetul si il blocheaza la loc, cu creionul inapoi.

-
-
- -

- Un singur ciclu, doua stari: creion → deschis → discheta → salvat si blocat → creion. - Termina ramane neschimbat si salveaza tot documentul; butonul de antet nu il inlocuieste, - ci acopera cazul in care nu vrei sa treci prin articole. -

-

- de adaugat Un singur camp de antet nu are azi control nicaieri: - eFactura. Procedura il scrie la fiecare apel, dar valoarea vine din inregistrare sau e - fortata zero — nimeni nu il poate schimba. Daca butonul deschide tot antetul, il deschide si pe - el, langa tipul SAF-T. -

-

- exista deja Comutatorul nu e un tipar nou in suita: fereastra de - rulaje face exact asta pe acelasi buton but_modifica, schimbandu-i imaginea in - save_sus.bmp cand intra in editare — verificat pe cod, rulaje.vc2:4716-4773. - Se copiaza reteta de acolo. -

- -
- - - - - - - - - - -
ActiuneCe salveazaCum
Butonul de antet, a doua apasaretot antetul, adica exact cele 14 campuri pe care le stie procedura: ruta, - delegat, agent, masina, data/ora expedierii, adresa de facturare, listare detaliata, - text aditional, tip SAF-T, eFactura, plus serie / numar / data / scadentape loc pack_facturare.modifica_date_factura. - Nu atinge articolele si nu recalculeaza nicio suma. Zece campuri se scriu neconditionat - in VANZARI, deci apelul trimite antetul intreg, nu doar ce s-a schimbat; - cele patru de identitate se propaga dupa ID_FACT in inca cinci tabele.
Terminatot documentul, ca aziruta se alege dupa ce s-a schimbat — vezi sectiunea 7
-
-

- Asta acopera cazul in care vrei sa corectezi doar delegatul sau data scadentei: apesi creionul, - corectezi, apesi discheta, inchizi. Articolele nici nu se ating. -

- -

- Ce deschide efectiv butonul — si de ce nu chiar tot, deocamdata. - „Tot antetul” ramane intentia, dar campurile nu au aceeasi ruta de scriere. S-a cautat exhaustiv, - si in Oracle si in tot codul VFP: pentru o parte din ele nu exista nicio procedura care sa le - schimbe pe un document deja emis. Nu e o scapare de proiectare, e starea codului de azi. -

-
- - - - - - - - - - - - -
Grup de campuriEtapa IEtapa II, cu regenerare
cele 14 — serie, numar, data, scadenta, ruta, delegat, masina, agent, - data/ora expedierii, adresa de facturare, text aditional, listare detaliata, tip SAF-T, - eFacturase deschid salvate pe locla fel
tip document, client, valuta, zi curs, sursa, gestiune sursa, politica de preturi, - si incasareablocate cu explicatie la hoverse deschid modificarea lor marcheaza documentul pentru - regenerare, aplicata la Termina
analiticele — venit / cheltuiala, sectie, responsabil, lucraredoar afisare se modifica din editarea notei - contabile, nu de aici
-
-

- Alegerea de a le tine blocate in etapa I, in loc sa se deschida cu avertisment: un camp - care se lasa modificat dar nu se salveaza e o capcana. O modificare pierduta in tacere e mai rea - decat un camp inca blocat, iar hover-ul spune de ce. -

- -

3Un singur buton de adaugare, cu meniu

-

- In loc de doua butoane („adauga tot” si „alege”), unul singur care deschide un meniu — acelasi - tipar folosit deja de butonul de modificare din lista de facturi. Optiunile se schimba dupa sursa - documentului, cu doua constante: ultimele doua optiuni sunt aceleasi peste tot — - Cauta in lista de preturi…, cu pretul din politici, si Alege din nomenclator…, cu - pretul tastat. Sursa umple documentul, nu il inchide: pe o factura din comanda se poate adauga - oricand un articol liber, si se poate si sterge — clientul mai vrea ceva, sau vrea sa schimbe. -

- -
-
-

Factura din comanda

- -
-
-

Factura din contract

- -
-
-
-
-

Factura din avize

- -
-
-

Factura din lista de preturi

- -
-
-
-
-

Factura de retur exista deja

- -
-
-

Factura venita din ROAAUTO la modificare

- -
-
- -
- - - - - - - - - - - - - - - - -
Cele doua mecanisme de returCum e aziCe se schimba
Factura de retur
document de sine statator
exista deja alegi facturile clientului dintr-un browse cu - selectie multipla, iar liniile se populeaza din ele: gestiunea si pretul de achizitie - vin neschimbate din linia originala, pretul de vanzare se recalculeaza pe curs doar - daca moneda nu e nationala. Antetul primeste singur textul „RETUR FACTURA …”.se muta ca atare. Nu se reproiecteaza nimic din ce functioneaza — in special nu - mostenirea gestiunii si a pretului de achizitie.
Retur de articole
intr-o factura de vanzare normala
alt mecanism, complet separat: factura sursa se alege per articol, iar gestiunea - nu vine din factura sursa, ci se alege. Butonul e vizibil doar pe facturile din lista de - preturi.capata si alegerea la nivel de document, dupa modelul de mai sus; calea per-articol - ramane, pentru cand returnezi un singur lucru
Cantitate partiala si stergereexista deja coloana isi schimba capul in - Cant. max. de returnat, serverul calculeaza maximul, iar o linie stearsa isi - elibereaza cantitatea inapoinimic — se pastreaza intocmai, ca si interdictia de retur dintr-un retur
Articole din afara facturilor sursanu se poate lista de articole este continutul - facturilor alese, deci nu ai de unde alege altceva — aceeasi cauza ca la comandaoptiunea din lista de preturi devine disponibila si aici. O linie de retur fara - factura originala e permisa, ca pe orice alt document — returul nu face exceptie. - Consecinta: pe acele linii gestiunea si pretul de achizitie se aleg, nu se - mostenesc, iar maximul returnabil calculat pe server nu se aplica, pentru ca nu exista - cantitate originala fata de care sa se limiteze.
-
- -

- „Alege…” deschide un dialog de selectie cu o coloana de bifat si criterii de cautare, dupa modelul - importului din ROAACNPRO: buton activ doar cand documentul are sursa, populare aditiva in cursorul - local, nicio scriere in baza pana la salvare. Spre deosebire de modelul acela, la zero rezultate - se spune de ce, nu se inchide in tacere. -

-

- Azi „adauga tot” exista, dar e o iconita fara caption si fara tooltip, asezata lateral intre doua - griduri — si nu acopera contractele: tipurile 2, 6, 26 si 52 lipsesc din conditiile lui de - vizibilitate, desi avizele sunt acolo. -

-

- decis „Adauga tot din comanda” / „Adauga tot din avize” raman - incarcari complete — S4 nu se restrange la o lista de preturi (decizia 39). Grila care le - alimenteaza (crsarticole) nu e doar sursa listei: e si registrul cantitatii ramase de - facturat, citit ca sa se decida inchiderea automata a comenzii sau a avizului cand o linie se - sterge. Cautarea pe server ramane cum se vede mai sus (fara incarcare in masa); ce se decupleaza e - bookkeeping-ul cantitatii ramase, intr-un registru propriu — ca sa poata disparea incarcarea si pe - comanda si pe aviz fara sa strice inchiderea automata a documentului sursa. -

- -

4Valuta si data cursului

-

- Doua concepte diferite, care azi impart acelasi camp mereu vizibil. Campul „Data curs valutar” - nu e eliminat niciodata in functie de valuta — doar pe retur — deci apare si pe facturi in lei - unde nu are ce cauta. -

-
- - - - - - - - - - - - - - - - - - - -
CazCe e in documentCe arata antetul
Factura in lei,
articole in lei
tot in lei, niciun pret din politica in valutafara valuta fara data curs
- Nimic de ales, nimic de convertit.
Factura in lei,
articole cu pret in valuta
documentul e in lei; unele articole au pretul din politica in valuta si se convertescapare Data curs valutar
- Cu explicatia langa el: preturile in valuta se convertesc in lei la cursul acestei - zile; documentul si contabilitatea raman in lei.
Factura in valuta
Invoice, credit note, retur valuta, factura fiscala valuta pe contract
documentul insusi e in valuta; articolele au pret in valutaValuta Data curs valutar
- Amandoua, ca azi.
-
-

- Al treilea caz nu se alege din formular: e decis de tipul documentului la deschidere, iar - selectorul de valuta e scos din formular cand documentul nu e de tip valuta. Deci „factura in - valuta” nu e o bifa pe care o pune operatorul, ci felul documentului. -

-

- Unificarea rezolva o problema care azi nu e rezolvabila. Cazul al doilea nu se poate sti la - deschiderea antetului — se afla abia dupa ce se incarca articolele, cand antetul de azi e deja - inchis. In formularul unificat antetul si articolele sunt in aceeasi fereastra, deci campul poate - aparea in momentul in care intra pe grid primul articol cu pret in valuta. Din acelasi motiv - dispare si mecanismul bug-ului #16: nu mai exista un antet inchis la care sa te intorci din - formularul de curs. -

-

- decis Validarea cursurilor se restrange la valuta cautata (decizia - 42). Pe varianta filtrata a cursoarelor, verifica_cursuri_valute nu mai ruleaza - global la deschiderea formularului, ci doar pe valuta articolului adus in grid. Un curs lipsa pe - alta valuta nu mai e semnalat la deschidere, ci abia cand se ajunge la un articol pe acea - valuta — diferenta e asumata, se consemneaza la testare, nu e regresie. -

- -

5Discountul pe linie

-

- Verificat pe formularul de azi si pe tabela. Raspunsul e mai simplu decat parea: si procentul, si - valoarea exista deja — dar nu in grid. -

-
- - - - - - - - - - - - - - - - - - - -
CeCum e aziCe se schimba
Procent si valoare unitara, ca intrare pentru operatorexista deja in dialogul de articol care se deschide la - fiecare adaugare: trei campuri — procent, suma in lei, suma in valuta — orice tastezi in - unul recalculeaza pe celelalte.Dialogul dispare odata cu unificarea. Cele doua campuri devin coloane in grid, - cu acelasi calcul in spate.
Ce se stocheazao singura coloana VANZARI_DETALII.DISCOUNT_UNITAR, - NUMBER(22,6) — valoare absoluta pe unitate. Nu exista coloana de procent. - Aceeasi coloana e si pe politica de pret, deci discountul poate veni din politica.Nimic. Modelul de date nu se atinge — procentul ramane doar mod de introducere.
Discountul in grid, aziinconsecvent pe factura in lei coloana e read-only; pe - factura in valuta e editabila, dar nu are handler de recalcul — o editare directa in - celula schimba valoarea fara sa refaca totalurile.Se repara odata cu mutarea in grid: aceleasi validari ca in dialogul de azi.
-
-

- De verificat separat, inainte de a incepe: daca discountul unitar apare pe rapoartele tiparite si - daca e transmis in eFactura si SAF-T. Daca nu apare pe hartie, documentul tiparit nu explica - totalul. -

-

- decis Discountul de DOCUMENT (nu cel de linie, de mai sus) se - repartizeaza proportional pe cote de TVA, nu pe cota maxima cum face codul de azi (decizia 59). - E automat, nu cere nimic de la operator. In banda de totaluri (sectiunea 1) se vede o singura suma; - pe factura tiparita, cu cote mixte, pot aparea mai multe randuri „Discount X % Factura", cate unul - per cota. -

- -

6De unde se intra in formular

-

- Cerinta e ca formularul sa poata fi deschis cu sursa deja completata, fara sa o mai alegi. - Doua cai fac deja exact asta. -

-
-

exista Din pagina Comenzi

-

Butonul de pe randul de comanda trimite comanda selectata si deschide formularul cu ea - completata. Ascuns cand comanda e deja facturata.

-

exista Din programul de contracte

-

Butonul de pe grila de contracte trimite contractul selectat si deschide acelasi formular, - cu contractul completat. Nu mai alegi nimic.

-

generic Din meniu

-

Fara sursa precompletata — alegi comanda sau contractul in formular, ca pana acum.

-
-

- Ce merita schimbat, si atat: sursa se transmite azi prin variabile globale, nu ca parametru. - De aceea calea generica de comanda e obligata sa goleasca explicit globalul inainte de apel, ca sa - nu ramana precompletata cu ce era in sesiune — iar calea de contract nu face acelasi lucru. - Un parametru explicit inchide subiectul. Nu presupune nicio integrare noua de contracte. -

- -
- -

7Ce se scrie la Termina

-

- Un singur formular, dar nu o singura cale de scriere. ID_FACT se pastreaza in - toate cazurile. -

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Ce s-a schimbatCum se scrieEfect
Antet — cele 14 campuri pe care le stie procedurape loc modifica_date_facturaaceeasi procedura ca butonul de antet pe a doua apasare; propaga serie / numar / data dupa ID_FACT
Antet — tip document, client, valuta, zi curs, sursa, gestiune sursa, politica de preturi, - si tot ce tine de incasareregenerareverificat pe cod: nu exista nicio cale de a le scrie pe un document emis — nici in - Oracle, nici in VFP. Singura ruta e drumul de emitere, adica regenerarea. Pana cand ea - exista (etapa II), campurile raman blocate.
Antet — analiticele: venit / cheltuiala, sectie, responsabil, lucrarenu de aicise editeaza din editarea notei contabile, care le trateaza la nivel de linie de - nota. Formularul unificat le arata, nu le scrie — ca sa nu existe doua ferestre care - modifica acelasi camp cu intelesuri diferite.
Explicatia si codul fiscal al unei liniipe loc modifica_explicatie_articoldoua coloane, nicio suma
Cantitati, preturi, linii adaugate sau sterse, discount, gestiune, cota TVAregenerare stergere + reemitere, in aceeasi tranzactiedocumentul vechi sters = 1; cel nou scris pe drumul normal de emitere; notele si rulajele refacute
Nimicnimicdocumentul ramane neatins
-
-

- decis Un singur cod de scriere contabila (decizia 35). - Regenerarea de mai sus scrie pe acelasi drum si la emitere, si la editare (etapa II): - scrie_factura2 → contabilizeaza_articol, cu parametrul de cont de la - sectiunea 10. Canalul oscrie_in_fisiere, folosit azi de #6, nu intra in #13 — - doua implementari ale aceleiasi reguli de contare, una in pachet si alta in VFP, ar trebui tinute in - pas manual la fiecare schimbare. Nu exista in acest formular editare directa a randului din - ACT_TEMP. -

- -

8Ce trebuie verificat inainte

-
    -
  • de verificat - ID_FACT refolosit la reemitere. Azi se ia din secventa, neconditionat. - Varianta care il cauta dupa numar, serie si data exista in pachet, dar e comentata — si - filtreaza STERS = 0, deci trebuie citita inainte de stergere.
  • -
  • de verificat - Stergerea si reemiterea in aceeasi tranzactie, fara commit intre ele.
  • -
  • decis - La reemitere se scriu valorile din formular, nu se reciteste sursa (decizia 54). Intrebarea - nu mai e ce garda punem peste re-derivare — e daca re-derivarea chiar se produce; raspunsul lui - Marius: nu trebuie sa se produca. Fara avertizare si fara confirmare, a fost respinsa explicit.
  • -
  • decis - Atasamentul PDF al documentului vechi se sterge la reemitere, nu se remigreaza (decizia 53). - Documentul reemis porneste curat; PDF-ul se regenereaza la prima listare.
  • -
  • decis - Facturile de marfa in custodie (tipurile 48 si 49) sunt editabile prin #13 (decizia 60). - Cu restrictia pastrata: pe ele nu se pot adauga articole gestionabile - (IN_STOC <> 0).
  • -
  • decis - Singura restrictie de modificare e ca documentul sa nu fi fost trimis in eFactura (decizia - 62). Fara nicio conditie de data, fara retroactivitate.
  • -
  • decis - Auditul (data + utilizator pentru adaugare, modificare si stergere) se afiseaza in gridul din - frm_facturi, nu in formularul unificat (deciziile 63 si 65). Formularul nu capata - niciun control nou din cerinta de audit.
  • -
  • decis - do_modifica de pe frm_facturi ramane activ pentru multi-selectie - (decizia 52). Formularul unificat preia doar cazul cu un singur document.
  • -
  • de verificat - Descarcarea de gestiune pe proforma, pe partea Oracle.
  • -
  • de verificat - Discountul unitar in listari si in eFactura / SAF-T.
  • -
  • de verificat - Validarea datei de curs pe avizul de lucrare — acolo e ceruta neconditionat, deci - ascunderea campului ar bloca finalizarea. Se repara impreuna, nu doar se ascunde.
  • -
  • de verificat - Legatura dintre linia de retur si linia originala. In program se acumuleaza perechi - articol-factura si se trimit la salvare, dar daca serverul le pastreaza intr-o coloana nu se - poate stabili din codul de aici. De asta depinde daca formularul poate arata din ce factura - vine fiecare linie de retur.
  • -
  • raspuns - Ce face pachetul ROAAUTO cand factura primeste o linie pe care devizul nu o stie: - nimic — nu citeste deloc tabelele de vanzari. Nu mai blocheaza nicio estimare, vezi - sectiunea 9.
  • -
  • raspuns - Nepotrivirea de afisare ROAAUTO intre ecranul de deviz si factura retiparita — - decizia 28: acceptata. Conditia e ca MANOPERA si MATERIALE sa fie conform devizului; - restul articolelor pot exista pe factura fara sa apara pe deviz. Nu se cere cod nou in ROAAUTO. - Vezi sectiunea 9.
  • -
  • raspuns - Stergerea unei linii venite din comanda — decizia 29: fara protectie. Se sterge - ca oricare alta; comanda ramane cu cantitatea nefacturata si va aparea, corect, facturata - partial. Nu se cere confirmare, nu se marcheaza „refuzat”.
  • -
  • raspuns - Coordonarea cu #6 — decizia 30: nu se coordoneaza. #13 incepe dupa ce #6 se - termina, deci nu exista doi scriitori simultan pe acelasi fisier. Decizia 26 (analiticele - read-only in #13) ramane — motivul ei e semantic, nu de calendar. Decizia 38: fluxul de - editare al lui #6 (editare directa a randului din ACT_TEMP, prin - oscrie_in_fisiere) nu se retrage odata cu #13 — coexista cu regenerarea prin - pack_facturare, iar decizia despre unificarea lor se ia mai tarziu, dupa ce #13 - livreaza si se vede in practica daca intretinerea celor doua drumuri doare cu adevarat.
  • -
  • decis - Bug-ul de dezalocare POS se repara in trecere, odata cu mutarea antetului din sectiunea 2 - (decizia 40) — nu e o mutare pur mecanica, testarea trebuie sa acopere si dezalocarea POS pe - calea veche.
  • -
  • raspuns - Cu ce cont de venit intra un articol fara politica de pret. Nu printr-o politica de pret - implicita — decizia 24 s-a retras. Decizia 27: contul de venit se deriva, - din CORESP_CONT_VENCHELT pentru articolele gestionabile si din - NOM_ARTICOLE.CONT (sau, implicit, 704) pentru cele negestionabile; derivarea - se face in VFP (decizia 27-bis), pack_facturare ramane neatins. Reteta si - costurile: sectiunea 10.
  • -
- -

9Facturile venite din ROAAUTO

-

- ROAAUTO emite facturi din formularul lui de devize, dar prin acelasi pachet si in aceleasi tabele. - Formularul unificat le vede fara niciun cod special. Ce lipseste nu e recunoasterea, ci scrierea. -

-
- - - - - - - - - - - - - - -
IntrebareRaspunsul de pe cod
Scrie in aceleasi tabele?da acelasi PACK_FACTURARE, acelasi drum prin - tabela de staging catre VANZARI_DETALII. Tip de document -12.
Se vad in editorul nou?da deja, testat pe date reale — detectia liniilor nu - filtreaza dupa tipul documentului.
Se pot adauga articole la emitere?da prin Alte servicii, la emiterea facturii finale. - Articolele vin din nomenclatorul brut, sunt limitate la cele fara stoc, iar - pretul se tasteaza — nu vine din politici. Raman linii proprii, nu se cumuleaza in - manopera sau materiale.
Se pot modifica dupa emitere?nu butonul se blocheaza imediat ce comanda are numar de - factura, metoda de stornare a comenzii e goala, iar gridul din editorul nou e read-only. - Singurul instrument asupra unei facturi emise e stergerea ei intreaga. Asta e - capacitatea care se cere.
Cum arata liniile lor?cele venite din deviz nu sunt articole: sunt sume cumulate pe categorii — manopera, - materiale, avans, discount — pe pseudo-articole cu cod negativ. Langa ele pot sta articole - reale. Niciunele nu au gestiune, nici macar cele reale.
-
-

- Ce se cere: aceeasi adaugare de articole si la modificarea documentului, nu doar la emitere — - si nu numai pentru facturile auto, ci pentru orice tip de factura, din lista de preturi sau direct - din nomenclator. -

-

- corectie „Alte servicii” nu e jumatatea de drum pe care o - credeam. Mecanismul acela functioneaza tocmai pentru ca ocoleste contabilizarea: - facturile de deviz merg pe o cale paralela, care nu genereaza nicio nota contabila de venit. - Pe fluxul normal de facturare, unde nota se genereaza, un articol fara politica de pret e - respins cu eroare. Deci nu se generalizeaza un mecanism existent — se rezolva altfel (mai jos). -

-

- rezolvat Devizul nu se dezechilibreaza. Pachetul ROAAUTO - nu citeste deloc tabelele de vanzari — nici antetul, nici liniile. Legatura pe care o face - dupa emitere e in sens invers: pune numarul documentului pe comenzile si lucrarile lui. Deci o - linie adaugata din formularul unificat nu strica niciun total si nu cere niciun apel suplimentar - catre ROAAUTO. -

-

- decis Nepotrivirea de afisare e acceptata (decizia 28). - Totalul devizului se calculeaza din structura proprie ROAAUTO, deci nu va arata niciodata - linia adaugata; in schimb factura retiparita o va arata, pentru ca relistarea citeste - direct liniile documentului. Conditia ceruta de Marius e alta: 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 ca sa aduca ecranul de deviz la zi. -

- -
- -

10Contul de venit pentru articole adaugate din nomenclator

-

- Intrebarea ramasa deschisa in sectiunea 8: cu ce cont de venit intra un articol adaugat - „Alege din nomenclator…”, fara politica de pret. Decizia 27 (mai jos) nu s-a schimbat; reteta prin - care ajungea la pack_facturare s-a abandonat intre runda 8 si runda 9-10, in favoarea - unui parametru nou. -

- -

- respins Decizia 24 — articolul ales din nomenclator - primeste id_pol-ul unei politici de pret implicite. Retrasa in runda 8: evita cod - nou in pack_facturare, dar cu pretul unei configurari cu contabilul (care politica, - cu ce SCC) si al unui cont de venit uniform pentru orice articol adaugat asa. - Inlocuita de decizia 27. -

- -

Decizia 27 — contul de venit se deriva, pe doua ramuri

-
- - - - - - - - -
ArticolSursa contului de venit
gestionabil
cont de gestiune clasa 3xx
CORESP_CONT_VENCHELT.CONT_VENIT, cautat pe CONT = contul de - gestiune al liniei
negestionabilNOM_ARTICOLE.CONT, daca e deja cont de clasa 6xx sau 7xx; - altfel, implicit 704
-
- -

- respins Decizia 27-bis — reteta VFP in 4 pasi: calculeaza - contul, cauta o politica a carei nota il are deja, insereaza articolul in ea cu - pack_preturi.adauga_politica_pret_art, trimite id_pol. Abandonata in - runda 9 (decizia 34): intrebarea deschisa era cum alege programul nota contabila de vanzare pe - factura din comanda, care nu are politica de pret (decizia 32) — iar raspunsul lui Marius se - refoloseste direct pentru articolul ad-hoc. Nu mai e nevoie de politica tehnica, nici de - interogarea inversa pe SCC, nici de inserarea articolului intr-o politica. -

- -

Decizia 34 — contabilizeaza_articol primeste contul, printr-un parametru nou

-

- Marius, textual: „poti sa adaugi parametrul contul contabil la contabilizeaza_articol.” - Verdictul de proiectare, in trei randuri, pentru ca schimba forma literala a cererii: - contabilizeaza_articol ia azi un singur parametru, - detalii_articol VANZARI_DETALII_TEMP%ROWTYPE — contul nu intra pe el literal, 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, adaugat - la coada lui adauga_articol_factura (procedura chemata direct din VFP) — - contabilizeaza_articol il primeste automat, fara sa i se schimbe semnatura. - Precedentul e in aceeasi lista de parametri: V_TAXCODE si V_LOT sunt deja - doi parametri adaugati ulterior, la coada, cu DEFAULT NULL, iar apelul VFP e pozitional - si se opreste la V_LOT. -

-

- Cei trei apelanti interni nu se ating deloc — scrie_factura2, - scrie_factura_avize_retur si scrie_aviz_retur fac - SELECT * BULK COLLECT intr-un TABLE OF ...%ROWTYPE, deci coloana noua le - traverseaza fara nicio schimbare de cod. Ramura noua se activeaza doar pe - detalii_articol.cont_venit IS NOT NULL: infasoara blocul FACT-024 (garda - ramane litera cu litera pe ramura veche — o linie fara politica si fara cont trimis continua - sa cada pe eroare), n-are cursor deloc, deci descarca_gestiune ruleaza exact o data - prin constructie — bug-ul de set multi-rand din ramura veche nu se mosteneste, fara sa se repare - ramura veche. -

-

- de retinut la implementare Parametrul se cableaza in doua locuri - VFP, nu unul: frm_facturare_articole.do_scrie_articole si - frm_facturare_articole2.do_scrie_articole sunt doua clase distincte, fiecare cu apelul - ei RPC propriu. Daca se vrea CONT_VENIT pastrat si dupa fapt in - VANZARI_DETALII, lista de coloane explicita din scrie_in_vanzari (24 de - coloane azi) trebuie extinsa — altfel coloana traieste doar in VANZARI_DETALII_TEMP si - ACT_TEMP. Articolul compus nu e o gaura aici: e exclus structural — proprietatea - COMPUS traieste pe perechea (articol, politica), prin ID_POL_ART, iar o - linie fara politica n-are asa ceva. -

- -

Decizia 36 — SCD si CU_TVA pe ramura fara politica

-

- Nota veche mai furniza si SCD, CU_TVA, IN_VALUTA, - EXPLICATIE, ID_VENCHELT, ID_SECTIE — nu doar contul. - Verificarea n-a gasit nicio sursa alternativa in cod pentru primele doua (nici optiune de firma - existenta, nici cont pe partener, nici flag de scutire pe articol sau client), deci sunt decizii de - produs, si Marius le-a luat: -

-
- - - - - - - - -
CampSursa noua
SCDo optiune de firma noua, cu 4111 ca implicit — acelasi tipar in trei - niveluri deja folosit de pack_facturare.scrie_incasare2 pentru - RF_CONT_INCASARE_*: parametru explicit → optiune de firma → constanta - hardcodata daca optiunea lipseste. Mecanismul (tabelul OPTIUNI, - PACK_SESIUNE.getoptiunefirma, ecranul generic frm_optiuni) exista - de 15+ ani, deci cheia noua nu cere niciun cod VFP nou, doar randul in tabel. - ASCD nu se atinge — se calculeaza deja din V_SCD.
CU_TVAderivat din cota liniei, nu fixat: 1 daca proc_tvav > 0, - altfel 0. Elimina prin constructie riscul gasit la verificarea adversariala: cu - CU_TVA = 1 fixat, o linie scutita ar fi intrat in comparatia de maxim din - nproc_tva_max si ar fi putut imprumuta coloana si taxcode-ul ei liniei de - discount a intregii facturi. Diferenta fata de ce face azi nota e intentionata, se - consemneaza la testare.
-
-

- Lista de parametri noi ramane la unul singur, V_CONT_VENIT: SCD - vine din configurare, CU_TVA se deriva in pachet din date deja prezente pe rand. -

- -

Decizia 37 — cheia optiunii de firma

-

- RF_CONT_ART_FARA_POL, aliniata la familia RF_CONT_INCASARE_* — aceleasi - optiuni care alimenteaza azi SCD in acelasi pachet, deci apar grupate alaturi in - ecranul de optiuni. VARTYPE = 'CHARACTER', VARVALUE = '4111', - PROGRAM = 'ROAFACTURARE', dar PROGRAME larg — apelul traieste in - COMUN\clase\ofacturare.vc2, prezent in toate cele sapte produse ale suitei. - Fara validare de cont, nici la salvare nici la citire — acelasi tipar ca - RF_CONT_INCASARE_*, care nu valideaza de 15 ani. Garda gratuita ramane lungimea: un - VARVALUE peste 4 caractere da ORA-12899 la - INSERT INTO ACT_TEMP — esec zgomotos, nu cont tacut gresit. Un cont inexistent in - planul de conturi, scris din greseala in ecran, ajunge pe nota neschimbat — risc acceptat constient, - acelasi pe care produsul il are deja pe optiunile de tip cont. -

- -

- Intrebarea lui Marius, verificata: „In ROAACNPRO si pe factura din comanda / contract se - adauga articole fara politica — de ce nu si aici?” Raspuns, neschimbat fata de v7: - ROAACNPRO nu cheama deloc contabilizeaza_articol — are propria contabilizare, - pack_acn.salveaza_regdoc. Pe contract, articolele vin din - cursor_contract / cursor_preturi, cu id_pol deja atasat de - cursor. Deci FACT-024 ramane blocantul pe linia ad-hoc — parametrul de mai sus e - cum se evita, nu un motiv sa fie sarit. -

- -

Ce nu e gratuit

-
    -
  • Regresie pe toata suita, nu doar ROAFACTURARE. pack_facturare e comun la - ROACONT / ROAGEST / ROACONTRACTE / ROAAUTO / ROAACNPRO, si ramura noua e atinsa de apelul comun - din ofacturare.vc2.
  • -
  • Doua clase VFP cablate separat. frm_facturare_articole.do_scrie_articole si - frm_facturare_articole2.do_scrie_articole isi construiesc fiecare propriul apel RPC — - parametrul se adauga in ambele, nu o singura data.
  • -
  • Lista de coloane a lui scrie_in_vanzari trebuie extinsa explicit daca se - vrea CONT_VENIT pastrat si dupa fapt in VANZARI_DETALII, nu doar in - VANZARI_DETALII_TEMP / ACT_TEMP.
  • -
  • Un cont fara precedent (cazul 704) intra direct pe linie, fara sa mai treaca - printr-o politica sau o nota existenta — spre deosebire de reteta veche, nu mai cere configurare - cu contabilul, dar nici nu mai e validat impotriva planului de conturi (decizia 37).
  • -
- -
- -

11Puncte marunte ramase

-

- Nu blocheaza nimic din ce e mai sus; se inchid la implementarea povestii respective. Enumerate ca sa - nu se piarda intre runde — se merge pe recomandare daca Marius nu - spune altfel. -

-
- - - - - - - - - - - - - - - - - - - - - - - - - - -
PunctPovesteSe merge pe
Textul tooltip-urilor pentru analiticele read-only si pentru grupul de incasare blocatS3bformularea propusa in raport, sectiunile 2.2 si 5.3
Eager vs. lazy pentru lookup-urile Oracle din Init (delegat / masina - ultimei facturi, casa)S3b, S3lazy, ca sa nu se plateasca la fiecare depliere
_checkbox1 vs. chkDetaliat — campul unic de „listare - detaliata”S3b, S1nedecis — se inchide pe cod la implementare, nu prin alegere
Forma lui toSursa: obiect scatter (duck-typing) sau clasa dedicataS3cduck-typing, ca azi — o clasa noua n-ar schimba nimic functional
Conversia comenzii si a contractului: un commit sau douaS3cun singur commit — aceleasi trei fisiere comune, separarea nu reduce - regresia
goComanda = '' redundant la Cw3.do_actiune dupa conversieS3cse lasa, marcat explicit „intentionat” in diff
Contractul cu doua meniuri (articole vs. rate): eticheta generica sau diferentiata pe - OPT_FACTURARES4bdiferentiata — „Alege ratele de facturat…” e gresita pe contractele cu - articole
„Alege facturile de returnat…” — ramane doar la antet sau capata incarcare aditiva din - baraS4bramane la antet in etapa I; mutarea e poveste separata
But_renunt1 / But_reset1 capata Caption pentru - consistenta cu bara etichetataS4bda — decizia 13 cere etichete, nu iconite mute
Ordinea si formularea optiunilor din xmenu() per sursaS4btabelul din sectiunea 3 a raportului
UX-ul contractului cu doua surse pe acelasi grid conceptual (crsarticole - filtrat + crsarticole1)S4de confirmat vizual la mockup, nu pe hartie
-
- - diff --git a/docs/plan_13_executie.md b/docs/plan_13_executie.md new file mode 100644 index 0000000..1905b85 --- /dev/null +++ b/docs/plan_13_executie.md @@ -0,0 +1,146 @@ +*!* 03.09.2026 +*!* agent documentatie (orchestrat de team-lead, sesiune plan13-s2) +*!* Coloana "Stare" era stale din 27.08.2026 (marca NEINCEPUT povesti de mult inchise si probate). +*!* Adusa la zi pe baza `docs\handoff_plan13_executie_continua.md` (fisierul de stare viu — ramane +*!* sursa de adevar, se citeste ACOLO intai). Rezolvata si capcana de numerotare de mai jos, prin +*!* sectiunea separata "Firul geometrie (designer editabil)". + +# Plan #13 - executie pe stories + +Tabel de urmarire pentru executia planului `docs\plan_13_unificare_formular_facturare.md`, +conform metodei de executie (liniile 1954-2015 din plan, decizia 55). Nu e reproiectare - fiecare +story are proiectarea proprie in plan si/sau in `docs\cercetare\`. Actualizat dupa fiecare story +inchisa. **Sursa de adevar pentru stare e `docs\handoff_plan13_executie_continua.md`** - acest +tabel e o rasfrangere a lui, tinuta pentru titluri scurte, fisiere atinse si dependente. + +**Capcana de numerotare — REZOLVATA (03.09.2026).** Numele `S6`/`S7`/`S8`/`S8b`/`S8c` din acest +tabel se refera strict la povestile din acest plan ("Test pe flux real", "Actiunea si garzile", +"Incarcarea documentului in formular" etc.). Un lant separat, comis tot pe `plan13-s2`, a folosit +ACELEASI nume pentru firul "designer editabil" (cererea separata a lui Marius, +`docs\sinteza_designer_editabil.md`). Cele doua nu au nicio legatura. Firul de geometrie are acum +randuri proprii, cu prefixul `S8-geometrie-*`, in sectiunea **„Firul geometrie (designer +editabil)"** de sub tabelul principal — nu se mai confunda cu povestile de aici. + +## Vocabular de stare + +- **INCHISA SI PROBATA** — cod comis + proba automata verde. +- **INCHISA FARA COD** — inchisa prin decizia lui Marius, fara implementare (cu decizia citata). +- **LIVRATA, PROBA MANUALA DATORIE** — codul e comis si probat automat, dar criteriul oficial + „gata" e o proba manuala UI pe care Marius n-o poate face acum (mandatul din §1 al handoff-ului). +- **PARTIALA** — ce s-a livrat si ce a ramas, explicit. +- **NEINCEPUT** — chiar neinceput. + +## Tabel stories + +| Story | Nume scurt | Fisiere atinse (din plan) | Depinde de | Stare | Nota de executie | +|---|---|---|---|---|---| +| S1 | Inventar campuri + alegerea bazei | (inventar, fara cod) | - | **INCHISA FARA COD** — aprobata de Marius, 09.08.2026 | `docs\S1_inventar_campuri_formular_unificat.md` | +| S2 | O singura procedura `factureaza` | `COMUN\programe\ofacturare.prg`, `Clase\ofundal_facturare.vc2` (decizia 67 + completare + corectie review) | S1 | **LIVRATA, PROBA MANUALA DATORIE** — comisa pe `plan13-s2`, 21.08.2026; netestata manual la momentul comiterii (decizia lui Marius). De atunci exercitata cu succes indirect, prin documente reale emise care trec prin `factureaza`, la S6 (`docs\raport_s6_flux_real.md`) si S9 (`docs\raport_s9_7_oracle.md`, `docs\raport_s9_7_vfp_proba.md`) — proba manuala UI dedicata ramane datorie deschisa | `docs\nota_executie_s2.md`, `docs\raport_s2_implementare.md`, `docs\cercetare\s2_factureaza_unificare.md`, `docs\review_s2.md`, `docs\handoff_13_s2.md` | +| S3 | Portare logica antet in formularul unificat (spart in S3-1..S3-4, decizia 68) | `ofacturare.vc2`, `ofacturare_antet.prg` (nou, S3-2), `ofacturare.prg` (S3-4) | S2 | **LIVRATA, PROBA MANUALA DATORIE** — S3-1..S3-4d COMISE pe `plan13-s2` (COMUN `89dd30a`, `27bdf58`, `c269fdf`, `4f6af10`, `359f478`, `02df236`, `e538590`, `409ce90`, `e96fd3e`); Q8 (numarul ars) COMISA, doar pe calea unificata. Implementarea e completa; proba manuala pe tip 1 a fost refuzata de Marius (22.08.2026), iar `factureaza` nu e apelabila headless | `docs\nota_executie_s3.md`, rapoartele si review-urile S3-1/S3-2/S3-3, `docs\raport_b1_rerulare_s3_3.md`, `docs\raport_q8_numar_ars.md`, `docs\review_q8_numar_ars.md`, `docs\raport_adaptare_paritate_q8.md`, `docs\handoff_13_s3.md` | +| S3b | Sectiune pliata: alte date + analitice | `caut_ora.vc2`, `ferestre_cere_date.vc2`, `ofacturare.vc2`, `omodificari.vc2` | S3 | **PARTIALA** — implementata aproape integral, comisa pe `plan13-s2`. Criteriul „gata" din 5 puncte (`docs\cercetare\s3b_alte_date_analitice.md` §10): **(1) paritate emitere pe 4 tipuri de incasare — TRECUT**, `test_s3b_emitere_paritate_2.prg` PASS 20/0 (`docs\raport_s3b_criteriu1_pasat.md`); **(2) non-alocare la toggle de pliere — TRECUT**, `test_s3b_incasare_nealocare.prg` PASS 14/0 (`docs\handoff_13_s3b_asezare.md`); **(3) blocarea grupului de incasare pe document emis — CADUC pentru S3b, amanat la S8c** (decizia 25); **(4) analitice read-only — CADUC, rasturnat de decizia 72** (raman editabile); **(5) paritate delegat/transport/adresa/text — FACUT**, cu o inconsecventa vizuala minora ramasa (`Ct_clb_delegat`/`Ct_clb_masina` editabile pe proforma, unde calea veche le scotea) [^1] | `docs\handoff_13_s3b_varianta_b.md`, `docs\raport_s3b_varianta_b.md`, `docs\handoff_13_s3b_reasezare_grila.md`, `docs\handoff_13_s3b_asezare.md`, `docs\cercetare\s3b_alte_date_analitice.md` §10, `docs\cercetare\s3b_bloc5_ce_ramane.md` | +| S3c | Sursa ca parametru, nu ca global | `ferestre_contracte.vc2`, `ocomenzi.vc2`, `ofacturare.prg`, `ofacturare_comun.prg`, `ofundal_facturare.vc2`, `oproceduri_facturare.prg` | S2. Atinge cod comun intregii suite - nu se face impreuna cu alta modificare | **INCHISA SI PROBATA (S3c-1/2/3/4-tinta1)** + **INCHISA FARA COD (S3c-4-tinta2 si S3c-5)**, 31.08.2026 — S3c-1/S3c-2/S3c-3: `toSursa` la coada in `Init`/`factureaza`/`facturare_contracte`/`facturare_comenzi`, test 55/0, comis 27.08.2026. S3c-4 tinta 1 (`ocomenzi.vc2:do_factura`): LIVRATA, test 18/0. S3c-4 tinta 2 (ROACONTRACTE) si S3c-5 (sincronizare cross-produs, 6 produse): mutate intr-o **poveste separata prin decizia lui Marius**, nu se mai ating la planul #13 (detaliu in §9 al handoff-ului) | `docs\nota_executie_s3c.md`, `docs\raport_s3c_1_implementare.md`, `docs\handoff_13_s4_s3c.md`, `docs\propunere_s3c_4_apelanti_reali.md`, `docs\propunere_s3c_5_sincronizare.md` | +| S4 | Cautarea articolelor pe server, in linie | `ofacturare.prg`, `ofacturare.vc2` | S3 | **PARTIALA** — S4-1 (Oracle articole server) INCHISA SI PROBATA, 23.08.2026 (identic byte-cu-byte pe filtru NULL fata de baseline); S4-2 (functii Oracle remainder/plafon) INCHISA SI PROBATA, 24.08.2026 (114 randuri numai adaugate in `PACK_FACTURARE`, zero stergeri); S4-3 (cautare in linie, `ofacturare_cautare.prg` nou) INCHISA SI PROBATA, 27.08.2026, teste 41/0, 50/0, 39/0; S4-4 (decuplare Rol A, decizia 6 varianta C) LIVRATA, PROBA MANUALA DATORIE, test paritate 18/0; **S4-5 (Rol B) PARTIALA** — garda `If Used(lcCursor)` in `do_sterge` LIVRATA (repara bug LIVE preexistent, test 10/10), dar plafonul Rol B pe tipul `23` a fost implementat, testat si **REVENIT** in aceeasi sesiune (garda structural moarta); ramane deschis prin deciziile 7 si 8 din handoff §2 (nedecise) [^2] | `docs\raport_s4_1_aplicare.md`, `docs\scripturi_oracle_13.md`, `docs\cercetare\s4_cautare_articole_server.md`, `docs\raport_s4_3_implementare.md`, `docs\raport_s4_4_vfp_implementare.md`, `docs\raport_s4_4_oracle_varianta_c.md`, `docs\propunere_s4_5_rol_b.md`, `docs\raport_s4_5_implementare.md` | +| S4b | Bara de butoane si meniul de adaugare | `ofacturare.vc2` | S4 | **INCHISA FARA COD** — decizia 9 varianta 3 (31.08.2026): mecanismul v2 (`but_incarca_articole` -> `do_incarca_articole`) e pastrat neatins; alegerea selectiva devine poveste separata (datoria 19) | `docs\propunere_s4b_bara_butoane.md` | +| S4c | Discount pe linie, mutat din dialog in grid | `ofacturare.vc2`, `ofacturare_comun.prg`, `oproceduri_facturare.prg`, `xmlefactura.prg` | S4 | **LIVRATA, PROBA MANUALA DATORIE** — handlere `LostFocus` pe cele doua campuri de discount + `recalc_discount_linie` nou, camp `procdisc` nou; repara si un defect preexistent (`cProcentDiscount` editabila dar legata la camp inexistent); test 26/0/0, write-back verificat independent | `docs\propunere_s4c_discount_grid.md` | +| S4d | Data cursului valutar, numai cand are sens | `ofacturare.prg`, `ofacturare.vc2`, `ofacturare_comun.prg` | S4 | **LIVRATA, PROBA MANUALA DATORIE** — metoda noua `do_actualizeaza_vizibilitate_curs`, apelata din 3 puncte (sink comun `do_calculeaza_totaluri`, `Init`, `do_incarca_articole`); test 32/0/0, inclusiv traseul complet al erorii Oracle `-20005`, write-back verificat independent | - | +| S4e | Lista de preturi si pe factura din comanda | `ofacturare.prg`, `ofacturare.vc2` | S4-1, S4-3 | **INCHISA SI PROBATA** — S4e-1 acoperit de S4-3 (prin `do_adauga_articol_cautat`, acceptat de Marius ca solutie finala, nu se redeschide); S4e-2 GATA (decizia 44); S4e-0 GATA 31.08.2026 (deblocarea `do_adauga_articol`); S4e-3 GATA 31.08.2026 (validare cantitate/stoc, tipurile `3,4,21,25,28,42,47`, test 62/0/0); S4e-4 GATA (proba de neregresie a registrului, fara cod nou, test 15/0/0); S4e-5 GATA (probe repetate pe tip 4/21 + captura dialog, test 37/0/0) | `docs\nota_executie_s4e.md`, `docs\recon_s4e_stare_reala.md`, `docs\raport_s4e_2_implementare.md`, `docs\raport_s4e_0_implementare.md`, `docs\raport_s4e_3_implementare.md`, `docs\raport_s4e_4_implementare.md` | +| S4f | Returul in formularul unificat | `ofacturare.vc2` | S2, S4e | **INCHISA** (08.09.2026) — stare veche din acest tabel depasita, vezi `docs\handoff_plan13_executie_continua.md` §4.0; `do_adauga_articol_cautat` deleaga returul (tip 8/9/41) la `do_adauga_articol` | `docs\raport_s4f_retur.md` | +| S4g | Adaugare articole la modificarea documentului | `ofacturare.vc2`, `ofacturare_comun.prg` | S4e + deciziile de mai sus. Nu blocheaza etapa I | **INCHISA** (08.09.2026) — stare veche din acest tabel depasita, vezi `docs\handoff_plan13_executie_continua.md` §4.0; decizia 34/optiunea B aplicata (Oracle `FACT-032` + VFP `deriva_cont_venit_fara_pol`) | `docs\raport_s4g_oracle.md`, `docs\raport_s4g_vfp.md` | +| S5 | Acoperirea tuturor tipurilor de document | `ofacturare.prg` | S4 | **INCHISA SI PROBATA**, 01.09.2026 — cod COMUN `1f89c18` (v2), teste conform §6 al handoff-ului | `docs\propunere_s5_acoperire_tipuri.md`, `docs\matrice_goluri_s5_form2.md`, `docs\verificare_s5_tabel_sursa.md` | +| S5b | Proforma si copierea pe formularul unificat | `ofacturare.prg`, `ofacturare.vc2`, `ofacturare_comun.vc2`, `oproceduri_facturare.prg` | S5 | **INCHISA SI PROBATA**, 01.09.2026 — COMUN `76e7398` (emiterea proformei pe formularul unificat, v2) + `d6cae36` (v1, calea de copiere); teste `080321f` (UI, 20/0/2) + testul de rutare (22/0/0). Ramane o singura datorie neblocanta: clauza de gestiune probata prin paritate, nu prin `id_gestiune > 0` (M3) | `docs\propunere_s5b_proforma_copiere.md`, `docs\raport_s5b_implementare.md`, `docs\raport_verificare_s5b_disc.md` | +| S5c | Factura din proforma (decizia 43) | `cmd_butoane.vc2`, `ocomenzi.vc2`, `ofacturare.prg`, `ofacturare.vc2`, `ofacturare_comun.prg`, `ofacturare_comun.vc2`, `ofacturare_editare.prg`, `oinit_optiuni.prg`, `xmlefactura.prg` | S5b | **INCHISA SI PROBATA**, 01.09.2026 — proba capat-la-capat prin UI RULATA, 20/0/2; vezi si golul de produs semnalat separat (nu al lui S5c) in handoff §4.0a | `docs\raport_s5c_test_ui.md` | +| S6 | Test pe flux real - formularul unificat | (test, nu cod nou) | S5 | **PARTIALA** — INCHIS 02.09.2026 cu proba partiala: **4 din 7** surse au acum comparatie v1/v2 pe document real emis (0/7 la pornire), **7 defecte de productie gasite, 6 reparate si comise** (3 cu proba doar partiala) | `docs\raport_s6_flux_real.md`, `docs\propunere_s6_test_flux_real.md`, `docs\raport_s6_1_lista_preturi.md`, `docs\raport_s6_3_aviz_retur.md`, `docs\raport_s6_4_transfer.md`, `docs\raport_s6_5_valuta.md`, `docs\raport_s6_6_listare_intrare.md` | +| S7 | Actiunea si garzile | `ofacturare_comun.vc2`, `ofacturare_editare.prg` | S6, si inchiderea perimetrului cu #6 | **INCHISA SI PROBATA**, 02.09.2026 — cod revizuit integral si comis, COMUN `70eddfa`; cele 5 cerinte de review inchise (cens octeti 13=13, metoda cu toate cele 3 inregistrari). Metoda ramane schela, fara apelant, pana cand S8 cableaza a treia optiune de meniu | `docs\propunere_s7_actiune_garzi.md`, `docs\raport_s7_actiune_garzi.md`, `docs\verif_s7_doua_fapte.md` | +| S8 | Incarcarea documentului in formular | `ferestre_cere_date.vc2`, `ofacturare.prg`, `ofacturare.vc2`, `ofacturare_comun.prg`, `ofacturare_editare.prg`, `ofacturare_stoc.prg` | S7 | **INCHISA SI PROBATA**, 02.09.2026 — pasii 1-8 GATA, proba finala `test_s8_editare_incarcare.prg` 307/0, CINCI rulari consecutive identice verificate pe md5/diff. Decizia 1 (`IN_STOC`) INCHISA de Marius (handoff §2). Defect nou, neblocant, in afara perimetrului: **G10 incalcat pentru avize** | `docs\propunere_s8_incarcare.md`, `docs\raport_s8_pas7b_proba.md`, `docs\raport_s8_defect5_incasat.md`, `docs\nota_s8q_aplicare_etapa6.md` | +| S8b | Rutarea scrierii dupa schimbarea tipului | `ferestre_cere_date.vc2`, `ofacturare.vc2` | S8 | **INCHISA SI PROBATA**, 03.09.2026 — rutarea scrierii la editare, COMUN `0a41327`, ROAF `e5530fe`; proba 54/0, trei rulari consecutive identice, toate cele patru rute exercitate pe traseul real | `docs\raport_s8b_implementare.md` | +| S8c | Buton comutator antet + salvare separata | `lb_tx.vc2`, `rulaje.vc2` | S8 | **NEINCEPUT** — mentionata in handoff doar ca alternativa paralela posibila cu S9 (mai mica, poate rula in paralel), fara cod scris pana acum | - | +| S9 | Stergere + reemitere intr-o tranzactie, `ID_FACT` pastrat | `oscrie_in_fisiere.prg` | S8b | **PARTIALA — IN LUCRU (03.09.2026)** — S9-1..S9-7 INCHISE SI PROBATE (S9-6 `PACK_CONTAFIN.nIdFactFortat` aplicat/probat pe `MARIUSM_AUTO`; S9-7 cablaj VFP aplicat si probat, sweep P1-P6/CN1-CN4 verde, COMUN `658acc6`/`cc79734`, ROAF `5886b3f`/`119474e`). **S9-8** (testul de flux: reemitere identica + cu modificari, pe aviz/comanda/contract/ELSE) **IN LUCRU** in aceasta sesiune. Datorii neinchise: proba vizuala UI dupa scoaterea garzii, neregresia pe ROACONTRACTE pentru scriptul #9 | `docs\propunere_s9_stergere_reemitere.md`, `docs\propunere_s9_6_pack_contafin.md`, `docs\propunere_s9_7_cablare_idfact.md`, `docs\raport_s9_7_oracle.md`, `docs\raport_s9_7_vfp_aplicare.md`, `docs\raport_s9_7_vfp_proba.md` | +| S10 | Pretul care nu trebuie re-derivat | `ofacturare.prg`, `ofacturare.vc2`, `ofacturare_editare.prg` | S9 | **NEINCEPUT** — decizia 3 (`PROC_TVAV` ca parametru) care il blocheaza e INCHISA (handoff §2), deci pornirea nu mai e conditionata de o decizie in asteptare | - | +| S11 | Legaturile ramase pe `ID_VANZARE` | (verificare + cod punctual, vezi plan) | S9 | **NEINCEPUT** | - | +| S12 | Test pe flux real - regenerarea | (test, nu cod nou) | S10, S11 | **INCHISA** - criteriul (toate cele patru familii trec scenariul B) atins prin sweep 4/4 pe `test_s9_8_flux.prg`, 04.09.2026 | `docs\raport_s12_inchidere.md` | +| S14 | Audit: creare, modificare, stergere document | `ofacturare_editare.prg` | S9 | **INCHISA**, 04.09.2026 — criteriul din plan bifat pe date reale (documentele reemise 1195/1196); vezi datoriile deschise in raport | `docs\raport_s14_oracle.md`, `docs\raport_s14_vfp_transport.md`, `docs\raport_s14_vfp_grid.md`, `docs\raport_s14_inchidere.md` | +| S13 | Diff, review, changelog, documentatie | toate cele de mai sus (inchidere de etapa) | toate stories anterioare - livrare finala | **NEINCEPUT** | - | + +[^1]: In plus, nedus in niciun raport separat pana acum: `lb_total_mod` e cod viu dar inert +(`docs\raport_s3b_lb_total_mod.md`, recomandat pentru alta poveste); euristica „205/210 dezalinieri +de coloana" din `verifica_grila.ps1` ramane neinvestigata. + +[^2]: Tipul `41` (S4-5) ramane definitiv in afara perimetrului curent — fix-ul necesar e proiectat +in `docs\propunere_s4_5_rol_b.md` §7, dar nefacut, ca sa nu arate tacit stoc din gestiuni gresite. + +## Firul geometrie (designer editabil) + +Firul separat, comis tot pe `plan13-s2`, prin care Marius poate aranja controalele formularului cu +mana in designerul VFP (`docs\sinteza_designer_editabil.md`). A folosit intern aceleasi litere +`S6`/`S7`/`S8b`/`S8c` in documentele lui proprii (`docs\handoff_s6_designer_editabil.md`, +`docs\handoff_s7_containere.md`, `docs\handoff_s8b_transa_a_si_ghid.md`, +`docs\handoff_s8c_transa_c.md`) si, intr-o etapa ulterioara, etichetele `S8h`-`S8p`/Etapa 6 — **fara +nicio legatura** cu povestile din tabelul de mai sus. Randurile de aici sunt rasfrangerea acelei +etape ulterioare (sursa: `docs\handoff_plan13_executie_continua.md` §3); tot firul e **INCHIS SI +COMIS**. + +| Bloc | Ce | Stare | Commit | +|---|---|---|---| +| S8-geometrie-h..m | Ancorare nativa vs. geometrie din cod; bitul `Bottom(4)` scos de pe controalele asezate din cod | **INCHISA** | COMUN `e2eca46` | +| S8-geometrie-n | Test de geometrie mutat din scratchpad in arborele de teste | **INCHISA SI PROBATA**, 8/8 pasi | COMUN `4063277`, ROAF `20eb547` | +| S8-geometrie-p | Dependenta rupta reparata: `Clase\ofacturare_util.vc2` intrat in versionare + `SET CLASSLIB` in `roafacturare.prg` | **INCHISA** — fara ea, `plan13-s2` era rupt la checkout curat | ROAF `fe3eef8` | +| S8-geometrie-o | Proba de rezolutie 1366x768 + diagnosticul benzii de totaluri | **INCHISA SI PROBATA** — la 1366x768 maximizat, deficit 98px, preexistent, nu regresie | COMUN `261f547`, ROAF `6257df6`, `3b312a0` | +| Etapa 6 | 5 fisiere de test reparate (`ObjExists` facut recursiv in 3; substitutia `opt_incasat.Value` -> `seteaza_mod_incasare` aplicata doar pe siturile formei NOI) | **INCHISA SI PROBATA** — `test_s3b_emitere_paritate_2` 20/0, `test_s3b_emitere_reala` 1/0, `test_s3_controale` 20/0, zero aparitii ale erorii 1734 | COMUN `34a1f30` | + +## Ordinea de executie rezultata din dependente + +``` +S1 (GATA) + -> S2 (GATA implementare, in asteptare review/aprobare) + -> S3c (paralel cu S3, atinge alt fisier comun - nu impreuna cu alta modificare) + -> S4e (STALE: aici in arbore ca "porneste dupa S2"; depinde de fapt de S4-1 si S4-3 - + vezi randul S4e din tabel si `docs\nota_executie_s4e.md` §6) + -> S4f (dupa S4e) + -> S3 + -> S3b + -> S4 (proiecteaza intai punctul 2, neproiectat) + -> S4b + -> S4c + -> S4d + -> S4g (dupa S4e + deciziile anterioare; nu blocheaza etapa I) + -> S5 + -> S5b + -> S5c + -> S6 (test pe flux real) + -> S7 (dupa inchiderea perimetrului cu #6) + -> S8 (varianta IN_STOC fixata in nota de executie S8, runda 17) + -> S8b + -> S9 + -> S10 + -> S11 + -> S14 + -> S12 (dupa S10 si S11) + -> S8c + -> S13 (inchidere finala, dupa toate) +``` + +Observatii pe ordine: +- S3c si S4e nu depind de S3/S4 - pot fi luate in paralel cu ramura S3, dar **un singur scriitor pe + fisier**: S3c atinge `ofacturare.prg`/`oproceduri_facturare.prg`, S2 le-a atins deja si le-a + eliberat (S2 e inchisa la nivel de implementare), deci nu se suprapun. +- S4g explicit "nu blocheaza etapa I" - poate aluneca dupa S6/S7 fara sa opreasca lantul principal. +- S8c e paralela cu S9 (ambele depind doar de S8), dar scrie in fisiere diferite (`lb_tx.vc2`, + `rulaje.vc2` vs `oscrie_in_fisiere.prg`) - fara conflict de scriitor. +- **Starea reala la 03.09.2026** (handoff §6): lantul e dus pana la S9 (S9-1..S9-7 inchise, + S9-8 in lucru); ordinea ramasa e **S10 -> S11 -> S12 -> S14 -> S13**, cu **S8c** paralela. + Actualizare 04.09.2026: **S12 inchisa** (`docs\raport_s12_inchidere.md`) - vezi §4.0 al + handoff-ului pentru starea curenta. **S14 inchisa** (`docs\raport_s14_inchidere.md`) - ramane + doar **S13** (inchiderea de etapa), cu **S8c** in continuare paralela. + +## Cele 3 puncte de implementare ramase deschise — TOATE INCHISE (handoff §2, 31.08.2026) + +Sursa initiala: `docs\handoff_13_formular_unificat.md`. Cele trei decizii ale lui Marius care le +inchid sunt in `docs\handoff_plan13_executie_continua.md` §2. + +1. **Relistarea unei facturi vechi deja trimise** (decizia 62) — **INCHISA**: toate facturile se + tiparesc dupa regula noua, fara versionare dupa data. Facturile trimise la ANAF nu se mai pot + retrimite. +2. **`PROC_TVAV` ca parametru** (consecinta 3 a deciziei 54) — **INCHISA, DA devine parametru**: + caile de copiere si modificare pastreaza cota documentului original; blocheaza S10. +3. **De unde se reconstituie `IN_STOC`** — **INCHISA, varianta (2)**: valoarea curenta din + nomenclator, fara migrare DB si fara reconstituire din rulaje; blocase S8, acum livrat si probat. diff --git a/docs/plan_13_unificare_formular_facturare.md b/docs/plan_13_unificare_formular_facturare.md index 8e8d007..d896f9a 100644 --- a/docs/plan_13_unificare_formular_facturare.md +++ b/docs/plan_13_unificare_formular_facturare.md @@ -1,4254 +1,4610 @@ -# Plan #13 — formular unificat de facturare + editare prin regenerare - -Sursa: `COMUN\docs\todos.txt` punctul 13. -Mockup: `docs\mockup_13_formular_unificat.html` (versiunea 5). -Cercetare pe cod, toata in `docs\cercetare\`: `inventar_controale_formulare.md`, -`proforma_copiere_puncte_intrare.md`, `import_roris_roaacnpro.md`, `discount_pe_articol.md` si -`discount_verificare2.md` (a doua il corecteaza pe primul), `modifica_antet_bifa.md`, -`valuta_si_curs.md`, `buton_comutator_picture.md`, `modifica_date_factura_parametri.md`, -`retur_si_lista_preturi.md`, `factura_retur_document.md`, `roaauto_facturi.md`, -`roaauto_articole_lista_preturi.md`, `cont_venit_articol_fara_politica.md`. -Stare: **propunere, neinceput**. Analiza facuta pe cod la 09.08.2026, in sase runde. - -## Cerinta, asa cum a fost formulata - -Trei lucruri intr-un singur punct: - -1. **Unificarea formularelor** — `date_factura` / `date_aviz` sa intre in formularul de facturare, - ca sa nu mai fie doua ferestre pentru acelasi document. -2. **Fara incarcarea prealabila a tuturor articolelor** din toate politicile de preturi — pot fi mii - si dureaza mult aducerea lor de pe server. -3. **Editarea facturii / avizului prin regenerare**, in toate variantele (politici de preturi, - comanda, contract, aviz): formularul se redeschide completat ca inainte de salvarea initiala, se - fac modificarile ca la introducere, iar la confirmare documentul initial se marcheaza `sters = 1` - si se salveaza unul nou — ca sa se vada ce s-a modificat. - -Decizia lui Marius, 09.08.2026: **intai unificarea, apoi editarea prin regenerare.** Explicit -**nu** pe calea editarii directe a notelor / rulajelor / articolelor. - -## Deciziile lui Marius, 09.08.2026 — luate, nu de reluat - -1. **#13 coexista cu #6**, nu il inlocuieste. -2. **`ID_FACT` se pastreaza**, ca la orice modificare. Documentul reemis nu-si schimba identitatea. -3. **Toate datele se pastreaza, inclusiv numarul documentului.** Scopul e modificarea documentului, - nu emiterea altuia. -4. **Antetul care intra in generarea lui `ID_FACT` (serie, numar, data) are cale proprie de - modificare** — nu trece prin regenerare. -5. **Un singur formular, aceeasi infatisare la introducere si la modificare.** Fara banda de - avertisment, fara coloane cu valorile initiale, fara panou de diferente. -6. **Se integreaza si modificarea de antet, si modificarea de articol care exista azi** — sa nu - ramana trei actiuni de modificare pe acelasi document. - -## Deciziile lui Marius, runda 3 (09.08.2026) — luate, nu de reluat - -7. **`frm_alte_date` intra in formular**, in sectiunea pliata. Se inchide intrebarea ramasa deschisa - in runda 2 (recomandarea de atunci — „ramane dialog” — **cade**). -8. **Sectiunea pliata primeste in plus analiticele**: venit / cheltuiala, sectie, responsabil (si, - prin simetrie, lucrare). Antetul vizibil ramane aerisit, cu **controalele grupate** pe intelesuri, - nu insirate. -9. **Un singur buton comutator pe antet.** Antetul se deschide blocat. Un singur `but_modifica` - (creionul) il deblocheaza si **isi schimba imaginea in discheta lui `but_salvare`**; a doua - apasare salveaza **doar antetul** si il blocheaza la loc. Un singur control, nu bifa plus buton — - mai compact. **Toata factura se salveaza in continuare din `Termina`.** Rostul ramane acelasi: - antetul se editeaza intentionat, nu din greseala, si se poate corecta fara a trece prin articole. - - **Butonul singur deschide tot antetul. Nu mai exista bife individuale** — nici cele patru de azi - (serie / numar / data / scadenta). Un singur control comanda toata protectia antetului. - - *Istoric, ca sa nu se reia:* runda 3 propusese un buton `Modificare / Salveaza` care bloca **tot - documentul** — respins, protectia e doar pe antet. Runda 4 propusese **bifa + buton separat** — - respins la 09.08.2026 in favoarea butonului comutator. Runda 5 propusese **pastrarea celor patru - bife individuale sub buton** — respins la 09.08.2026: bifele dispar cu totul. - - **Verificat pe cod la 09.08.2026** (`docs\cercetare\buton_comutator_picture.md`): **tiparul exista - deja in suita si se copiaza, nu se inventeaza.** `frm_rulaje.se_modifica_assign` - (`COMUN\clase\rulaje.vc2:4716-4773`) comuta exact asa **o singura instanta** de `but_modifica` - intre creion si discheta — nu instantiaza a doua clasa. Trei lucruri de retinut din el: - - se rescriu **impreuna** `.cpicturedown`, `.cpictureup` **si** `.Picture`, plus `.ToolTipText`, - urmate de `.Refresh()`. Numai `.Picture` nu ajunge: hover-ul standard din - `buton.MouseEnter` / `MouseLeave` (`_cmd_base.vc2:88-97`) rescrie `Picture` din `cPictureUp` / - `cPictureDown`, deci iconita veche ar reveni la primul mouse-over; - - numele de fisier se dau **fara cale** (`"save_sus.bmp"`), rezolvate prin `SET PATH` — care - include si `GRAFICE`, si `COMUN\GRAFICE` (`Programe\roafacturare.prg:85-108`). Calea relativa - `..\grafice\...` din clasa e buna doar la design-time; - - imaginile exista: `COMUN\grafice\save_sus.bmp` / `save_jos.bmp` (discheta), - `modific_sus.bmp` / `modific_jos.bmp` (creion, perechea folosita de clasa). - - Clasa de salvare din biblioteca se numeste **`but_salveaza`**, nu `but_salvare` - (`cmd_butoane.vc2:340-352`); e sursa numelor de imagini, dar nu se instantiaza. Clasa - `but_modifica` e la `cmd_butoane.vc2:184-198`. Butoanele **nu** au `do_activeaza` / - `do_dezactiveaza` — pe ele se lucreaza direct pe `.Enabled`. - - **Consecinta asupra celor patru bife individuale** (serie / numar / data / scadenta, vezi I): - **se elimina**, si **odata cu ele se abandoneaza si conditia lor** de azi - (`chkSerieAct.Enabled = !EMPTY(NVL(poRec.numar_act,0))`, `ofacturare_comun.vc2:5736-5750`). - Decis de Marius la 09.08.2026: **butonul deschide tot**, fara conditii pe camp. Un singur control, - o singura regula. Cat de departe merge „tot” — vezi **decizia 19**: exact cat scrie - `modifica_date_factura`, parametru cu parametru. -10. **Proforma foloseste acelasi formular.** -11. **Copierea foloseste acelasi formular**, ca document nou, cu antetul editabil. -12. **Formularul trebuie sa *poata* fi apelat cu sursa deja completata** — din pagina de comenzi cu - comanda, si din programul de contracte cu contractul, fara ca utilizatorul sa mai aleaga ulterior. - **Nu se integreaza contractele acum**; nu depinde de #10. -13. **Un singur buton de adaugare a articolelor, cu `xmenu()`** — nu doua butoane separate „adauga - tot” si „alege”. Optiunile din meniu se schimba dupa sursa si acopera **comanda, contractul si - avizele**, cu alegere selectiva acolo unde are sens (de exemplu o singura rata de contract). - Butoanele de linie stau deasupra tabelului. -14. **Discountul pe articol, procent si valoare absoluta**, amandoua accesibile. -15. **Data cursului valutar apare doar cand are sens.** Sunt doua concepte diferite, care azi impart - acelasi camp mereu vizibil: (a) **factura in valuta**, unde si articolele au pret in valuta, si - (b) **factura in lei cu articole care pot avea pret in valuta**, convertite in lei la cursul din - data cursului, cu documentul si contabilitatea in lei. Formularul trebuie sa fie **ergonomic si - simplu** — campul nu apare cand nu e nimic de convertit. - -### Deciziile lui Marius, runda 6 (09.08.2026) - -16. **Lista de preturi e disponibila mereu, indiferent de sursa — si liniile suplimentare se pot si - sterge.** Pe o factura din comanda sau din contract trebuie sa se poata **adauga si sterge** - articole libere din lista de preturi, nu doar articole din sursa. Cazul real, formulat de Marius: - clientul a comandat ceva, iar la facturare mai vrea ceva in plus sau vrea sa schimbe — deci - documentul trebuie sa poata devia de la comanda, in ambele sensuri. Sursa umple documentul, nu il - inchide. Optiunea „Cauta in lista de preturi…" ramane in meniul butonului de adaugare **pentru - toate sursele**, inclusiv pentru documentele deja emise care se modifica. - **Verificat pe cod** (`docs\cercetare\retur_si_lista_preturi.md`, B): **contractul o are deja** - (`crsarticole` e populat de `cursor_preturi` / `cursor_contract` cu lista intreaga, al doilea grid - e un adaos, nu o restrictie), **comanda nu** — `cursor_comanda` umple `crsarticole` **doar** cu - articolele comenzii (`ofacturare.prg:266-308`). Deci golul real e pe comanda, si e in **continutul - cursorului**, nu in vreun `Visible` de buton. Reteta exista deja in produs: ramura de copiere - adauga lista de preturi peste cursorul sursei cu `APPEND FROM` (`ofacturare.prg:454-473`) — se - generalizeaza ea, nu se inventeaza alta. -17. **Returul intra in formularul unificat, cu tot cu alegerea facturilor sursa.** Factura de retur - facuta din facturi anterioare e un caz de acoperit explicit — lipsea si din mockup, si din - proiectare. *Vezi sectiunea N.* - **Sunt doua mecanisme distincte, si prima cercetare l-a vazut doar pe al doilea:** - (a) **factura de retur ca document** (tipurile 8, 9, si avizul 24) — alege facturile sursa **la - nivel de document**, cu selectie multipla, si isi populeaza liniile din ele; **gestiunea si - pretul de achizitie vin neschimbate din linia originala**, verificat in `cursor_retur_document`. - **Exista deja si merge** — nu se reproiecteaza. Vezi N.1; - (b) **`But_retur`** — retur de articole intr-o factura de vanzare normala (tipurile 1, 5, 7, 10), - unde factura sursa se alege **per articol**, iar gestiunea nu se mosteneste, ci se alege. Vezi N.2. - Sunt doua fluxuri de cod independente, fara punct comun. Decizia 17 le duce pe amandoua in - formularul unificat; ce se proiecteaza nou e (b) ridicat la nivel de document, nu (a). -18. **Facturile emise din ROAAUTO intra in perimetru la modificare.** ROAAUTO le emite din formularul - lui, dar scrie tot in `VANZARI_DETALII`, si pe ele trebuie sa se poata adauga articole din lista - de preturi. **Emiterea ramane la ROAAUTO** — se unifica doar modificarea. - **Verificat pe cod** (`docs\cercetare\roaauto_facturi.md`): acelasi `PACK_FACTURARE`, acelasi drum - `VANZARI_DETALII_TEMP` -> `VANZARI_DETALII`, tip de document **`-12`**, pe care editorul lui #6 il - **vede deja**, testat pe date reale. - **Precizarea lui Marius, 09.08.2026, verificata pe cod** - (`docs\cercetare\roaauto_articole_lista_preturi.md`): in ROAAUTO **exista deja adaugarea de - articole reale**, pe langa liniile generice — mecanismul „Alte servicii” din `frm_incasare_finala`. - Deci descrierea „doar linii sintetice cu `id_articol` negativ” din primul raport e **incompleta**. - Nuanta care conteaza pentru proiectare: sursa lui e **nomenclatorul brut**, nu lista de preturi, - articolele oferite sunt **fara stoc** (`in_stoc = 0 and in_crm = 1`), iar **pretul se tasteaza - manual** — nu vine din politici. Ce se cere e ca **acelasi lucru sa fie posibil si la modificarea - documentului**, nu doar la emitere, **si nu numai pentru facturile auto, ci pentru orice tip**, - cu **ambele surse**: lista de preturi (cu pret calculat) sau nomenclatorul (cu pret tastat). - **Vezi O.** -19. **Toti parametrii lui `modifica_date_factura` trebuie sa fie modificabili din butonul de antet.** - Butonul comutator nu deschide un subset ales de noi: daca procedura Oracle stie sa scrie un camp - de antet, formularul trebuie sa aiba controlul prin care acel camp se poate schimba. - **Verificat pe cod** (`docs\cercetare\modifica_date_factura_parametri.md`): sunt 15 parametri, - din care **14 sunt campuri** — al 15-lea, `V_ID_VANZARE`, e identitatea randului si nu se - editeaza. Din cele 14: - - **13 au deja control** in `frm_modifica_factura` si se muta ca atare in antetul unificat; - - **`V_EFACTURA` nu are niciun control nicaieri** (`ofacturare_comun.vc2:4576` il forteaza `0` - la selectie multipla, altfel vine din `Scatter`) — **primeste unul**, e singurul camp nou-nout - cerut de decizia asta; - - `V_TIP_SAFT` are control, dar ascuns dupa `gl406` (`:5739-5741`) — ramane conditionat, nu se - forteaza vizibil. - Vezi I-bis pentru contractul procedurii, care impune si **cum** se cheama, nu doar ce se trimite. -20. **Din nomenclator se aleg si articole gestionabile, si negestionabile.** Filtrul `in_stoc = 0` - al lui ROAAUTO **nu se preia** — acolo e o ocolire a subiectului gestiunii, nu o regula de produs. - Consecinta acceptata: pe documentele auto vor coexista linii care descarca stoc cu linii care nu - descarca, caz care azi nu exista nicaieri. *Vezi J si O-bis, intrebarea 2.* - **Intrebarea care insotea decizia — „cu ce cont de venit intra un articol fara politica de pret” - — s-a inchis prin cercetare: nu exista cont de venit pe linie.** Vezi **J-bis**. - -### Deciziile lui Marius, runda 7 (09.08.2026) - -21. **Cand `NOM_ARTICOLE.CONT` e gol pe un articol negestionabil, se aplica un fallback la un cont - implicit.** Nu se accepta `NULL` (comportamentul de azi al liniilor „Alte servicii" din ROAAUTO) si - nu se refuza adaugarea articolului. Contul anume se alege la implementare. - **Atentie la perimetru:** decizia priveste **contul de gestiune** (`VANZARI_DETALII.CONT`). Cand a - fost luata nu se stia inca faptul, stabilit mai tarziu in aceeasi runda, ca pe linie exista **doua** - conturi cu surse diferite — cel de venit (`NOTE_CONTABILE.SCC`, prin politica de pret) nu e acoperit - de aceasta decizie si e inca deschis. *Vezi J-bis.* - Ipoteza de la care a pornit Marius — corespondenta 3xx -> 7xx dintr-un tabel — **nu s-a confirmat - ca tabel**, dar intentia ei da: legatura articol -> cont de venit exista, prin politica de pret si - `NOTE_CONTABILE`, configurata manual de contabil. -22. **Linia de retur fara factura originala e permisa.** Pe tipurile 8, 9, 24, „Cauta in lista de - preturi…" si „Alege din nomenclator…" se comporta ca pe orice alt document — nu apar doar pentru - corectii si nu sunt marcate special. Consecinta acceptata: gestiunea si pretul de achizitie se - aleg (nu se mostenesc), iar maximul returnabil de pe server nu se aplica acelei linii. *Vezi N.3.* -23. **Secventierea deciziei 18: `pack_auto` se cerceteaza acum**, inainte de orice estimare, nu cand ii - vine randul la livrare. *Vezi O, „Secventierea".* **Rezultat:** riscul tehnic nu exista — - `PACK_AUTO` nu citeste `VANZARI` / `VANZARI_DETALII` deloc. Decizia 18 nu mai trebuie sa fie ultima. -24. ~~**Articolul ales din nomenclator primeste `id_pol`-ul unei politici de pret implicite.**~~ - **RETRASA in runda 8, inlocuita de decizia 27.** Ramane in plan doar ca sa nu fie reintrodusa: - politica implicita ar fi evitat codul nou in `pack_facturare`, dar cu pretul unei intrebari de - configurare cu contabilul si al unui cont de venit uniform pentru orice articol adaugat asa. - Marius a ales in loc corespondentele `CORESP_CONT_VENCHELT`. *Vezi decizia 27 si J-quater.* -25. **Butonul de antet deschide tot; ce nu se poate salva pe loc forteaza regenerare.** La a doua - apasare, cei **14 parametri** merg prin `modifica_date_factura`, iar orice camp schimbat din - **grupul B** (tip document, valuta, zi curs, client, sursa, gestiune sursa, politica de preturi) - sau **C** (incasare) **marcheaza documentul pentru regenerare**, aplicata la `Termina`. Decizia 9 - ramane intacta ca intentie — butonul chiar deschide tot antetul —, dar ruta de scriere se bifurca - dupa camp, nu dupa buton. *Vezi G-bis.* - **Consecinta pe etape:** in **etapa I** regenerarea nu exista inca, deci acele campuri nu au unde - sa se salveze. **Planul le tine blocate in etapa I**, cu explicatie la hover, si le deschide odata - cu etapa II. Motivul alegerii, in lipsa unei instructiuni contrare: un camp care se deschide dar nu - se salveaza e o capcana — pierderea tacuta a unei modificari e mai rea decat un camp inca blocat. -26. **Analiticele raman read-only in #13; editarea lor e a lui #6.** Venit/cheltuiala, sectie, - responsabil si lucrare se **afiseaza** in sectiunea pliata, cu valoarea de antet, dar nu se - editeaza din formularul unificat — cine vrea sa le schimbe trece prin editarea notei contabile. - Motivul: sunt deja editabile acolo, **la nivel de linie de nota**, iar #13 le-ar scrie uniform pe - tot documentul — doua ferestre care scriu acelasi camp cu semantici diferite, cu risc ca #13 sa - suprascrie tacit o diferentiere facuta din #6. **Zero suprapunere intre fire.** *Vezi G-bis.* - Asta **nuanteaza decizia 8**: analiticele intra in sectiunea pliata ca **afisare**, nu ca editare. - -### Deciziile lui Marius, runda 8 (09.08.2026) - -27. **Contul de venit vine din corespondente si din nomenclator, nu dintr-o politica de pret - implicita. Decizia 24 se retrage.** Regula, pe doua ramuri: - - **articol gestionabil** (cont de gestiune de clasa 3xx): contul de venit se ia din tabelul de - corespondente `CORESP_CONT_VENCHELT` — `select id_ccv, cont, cont_chelt, cont_venit, sters, - dataoras, cont_aprovizionare, cont_diferente from coresp_cont_venchelt`, cautand pe `CONT` - (contul de gestiune al liniei) si luand `CONT_VENIT`; - - **articol negestionabil**: se foloseste `NOM_ARTICOLE.CONT` **daca e deja un cont de clasa 7xx - sau 6xx**; altfel, implicit **704**. - - Motivul respingerii politicii implicite: ea muta problema in configurare (cine creeaza politica, - cu ce `SCC`, una sau mai multe) si obliga la o intrebare cu contabilul inainte de orice livrare. - Corespondentele exista deja ca date de productie si leaga contul de gestiune de cel de venit — - exact relatia cautata, doar ca sub alt nume decat s-a cautat in runda 7. -27-bis. ~~**Regula din 27 se implementeaza FARA cod nou in `pack_facturare`.**~~ **RELAXATA de decizia - 34 si inchisa de decizia 35** — pachetul se modifica punctual, si e singurul cod de contare, si la - emitere si la editare. Textul de mai jos ramane doar ca istoric al rationamentului. - (Marius, runda 8, dupa - prima formulare a lui 27). Motivul: pachetul e mare si e folosit de toate produsele suitei — o - ramura noua acolo e un risc peste tot, nu doar in ROAFACTURARE. - Deci **regula de derivare a contului de venit se aplica pe partea VFP**, inainte ca articolul sa - plece spre Oracle, iar `contabilizeaza_articol` ruleaza **neschimbata**. Calea de verificat: - VFP calculeaza contul de venit dupa regula de mai sus si alege un `id_pol` a carui nota are deja - acel `SCC`, astfel incat `FACT-024` sa nu se mai poata declansa. - **Modificarea pachetului ramane planul B**, folosit doar daca se dovedeste ca nu exista nicio cale - dinspre VFP. *Vezi J-quater;* fezabilitatea: `docs\cercetare\coresp_cont_venchelt.md`, punctul 8. - - **PREMISA A FOST VERIFICATA (Marius, runda 8):** „in ROAACNPRO, dar si la factura din comanda / - contract, se adauga articole fara politica de preturi — de ce nu se poate si aici?" **Raspuns: - observatia e reala, dar niciunul din cele doua exemple nu e un articol fara politica.** ROAACNPRO - nu cheama deloc `contabilizeaza_articol` (are propria contabilizare, `pack_acn.salveaza_regdoc`); - pe contract, articolele vin din `cursor_contract` / `cursor_preturi`, care le livreaza **cu `id_pol` - atasat**. `FACT-024` ramane blocantul. *Detalii, cu dovezi: J-quater, punctul 1.* - **REZULTAT: calea VFP exista si e mai buna decat modificarea pachetului** — se sprijina pe un RPC - existent (`pack_preturi.adauga_politica_pret_art`) si aduce, pe langa `SCC`, si `CU_TVA` / - `IN_VALUTA`, pe care un fallback in pachet ar fi trebuit sa le hardcodeze. *Vezi J-quater, punctul 3.* - **Planul B (modificarea `pack_facturare`) se abandoneaza.** -28. **Pe factura ROAAUTO pot exista si alte articole decat cele de pe deviz.** Nepotrivirea de afisare - dintre ecranul de deviz si factura retiparita (O, intrebarea 1) **e acceptata**: cerinta e ca - **MANOPERA si MATERIALE sa fie conform devizului**, plus orice alte articole adaugate. Nu se cere - cod nou in ROAAUTO ca sa aduca ecranul de deviz la zi. *Vezi O, intrebarea 1.* -29. **Stergerea unei linii venite din comanda ramane fara protectie.** Linia se sterge ca oricare alta; - comanda va aparea, corect, ca **facturata partial**. Nu se adauga confirmare, nu se marcheaza - „refuzat", nu se ajusteaza numararea acoperirii. *Vezi S4e.* -30. **Nu se coordoneaza cu firul #6.** #13 **incepe dupa ce #6 se termina**, deci suprapunerea pe - fisiere nu se poate produce si nu e nevoie de impartire de perimetru. Consecinta pe interdictii: - cele doua fisiere ale lui #6 raman intangibile **cat timp #6 e in lucru**, dar restrictia expira - odata cu el, nu cere negociere. Decizia 26 (analiticele read-only in #13) ramane in picioare — ea - tine de semantica, nu de coliziunea de lucru. *Vezi „Relatia cu #6".* - -### Deciziile lui Marius, runda 9 (10.08.2026) - -**31. Datele din Dev nu sunt baza de proiectare.** Fiecare client isi defineste propriile politici de -pret, fiecare cu nota ei contabila de vanzare salvata pe un `ID_SET`. Orice masurare pe `MARIUSM_AUTO` -vale ca **dovada ca un mecanism exista** si ca sursa de concluzii **structurale**, niciodata ca harta a -ce e configurat la client. Nicio decizie de proiectare nu se sprijina pe numarul de politici sau de note -gasite pe Dev. *(Generalizeaza regula „zero cazuri in date nu e dovada" in ambele sensuri: nici prezenta -nu e dovada.)* - -**32. Intrebarea de raspuns inainte de orice implementare a contului de venit:** cum alege programul nota -contabila de vanzare la **factura pe baza de comanda**, care **nu are politica de pret**? Daca acel -mecanism se poate refolosi pentru articolul adaugat ad-hoc, **reteta in 4 pasi din J-quater se -abandoneaza** in favoarea lui. Vezi J-quater, „Intrebarea deschisa care poate anula toata reteta". - -**34. `contabilizeaza_articol` primeste un parametru de cont contabil. Decizia 27-bis e RELAXATA.** -Marius, 10.08.2026, textual: *„poți să adaugi parametrul contul contabil la `contabilizeaza_articol`."* -Consecinta: **reteta in 4 pasi din J-quater se abandoneaza.** Nu mai e nevoie de politica tehnica, nici de -interogarea inversa pe `SCC`, nici de inserarea articolului in politica. VFP calculeaza contul dupa regula -deciziei 27 si **il trimite direct**. -**Ce rămâne de proiectat, si nu e „doar un parametru":** contul nu e singurul lucru care venea de pe nota. -`cursor_articol` aducea si `SCD`, `CU_TVA`, `IN_VALUTA`, `EXPLICATIE`, `ID_VENCHELT`, `ID_SECTIE`, iar bucla -care il consuma apeleaza **si** `scrie_nota` **si** `descarca_gestiune`. Pe un articol fara politica, -`FACT-024` sare inainte. Deci e nevoie de **parametru + ramura fara politica**, care sa furnizeze ce -furniza nota si sa ruleze `descarca_gestiune` **exact o data**. Obiectia rundei 8 („`CU_TVA` si `IN_VALUTA` -n-au sursa in afara lui `NOTE_CONTABILE`") **nu mai e blocanta, e proiectare** — de reevaluat daca se pot -deriva din document. -**Constrangeri de forma, ca sa nu se strice suita:** parametru nou **cu `DEFAULT NULL`**, la finalul listei, -si ramura inerta cand lipseste — apelanții existenți nu se schimba. `pack_facturare` e comun **intregii -suite**, deci cere regresie pe ROACONT / ROAGEST / ROACONTRACTE / ROAAUTO / ROAACNPRO, nu doar ROAFACTURARE. -**Ramura noua nu moșteneste bug-ul de set multi-rand** din L.0-ter. -**PROIECTATA** — `docs\cercetare\canal_cont_venit_fara_politica.md`, sectiunea „Proiectarea parametrului -de cont contabil", liniile 13-227. Verdictul, in trei randuri, pentru ca schimba forma modificarii: -`contabilizeaza_articol` primeste azi **un singur parametru**, `detalii_articol -VANZARI_DETALII_TEMP%ROWTYPE` — deci contul **nu intra literal pe ea**, ar fi cosmetic. Intra ca -**coloana noua `VANZARI_DETALII_TEMP.CONT_VENIT VARCHAR2(4) NULL`**, populata printr-un parametru nou -`V_CONT_VENIT IN VARCHAR2 DEFAULT NULL` la **coada lui `adauga_articol_factura`** (procedura chemata -direct din VFP), pe care `contabilizeaza_articol` o primeste automat: cei trei apelanti interni fac -`SELECT * BULK COLLECT` intr-un `TABLE OF ...%ROWTYPE`, deci `scrie_factura2`, -`scrie_factura_avize_retur` si `scrie_aviz_retur` **nu se ating deloc**. -> **Corectie de nume, runda 11 — mecanismul insa rezista.** Al doilea apelant e **`scrie_factura_avize`** -> (`:6708-6749`), nu `scrie_factura_avize_retur`, care nici nu cheama `contabilizeaza_articol`. -> **Verificat pe cod ca toti trei apelantii reali** — `scrie_factura2` (`:6039-6063`), -> `scrie_factura_avize` (`:6708-6749`), `scrie_aviz_retur` (`:7097-7106`) — fac intr-adevar -> `SELECT * BULK COLLECT INTO` un `TABLE OF VANZARI_DETALII_TEMP%ROWTYPE`, **fara lista explicita de -> coloane**. Deci coloana noua curge automat prin toti trei si **niciunul nu se atinge**; concluzia -> proiectarii sta, doar doua din cele trei nume erau gresite. In `scrie_factura_avize`, `tab_detalii(i)` -> e modificat inainte de apel doar pe `.cantitate` / `.id_rata` — `CONT_VENIT` ramane neatins. Efectul cerut de Marius e exact -acelasi; doar mecanica de livrare difera de formularea literala. Precedentul e in aceeasi semnatura: -`V_TAXCODE` si `V_LOT` sunt deja doi parametri adaugati ulterior, la coada, cu `DEFAULT NULL`, iar apelul -VFP e pozitional si se opreste la `V_LOT`. -Ramura noua se activeaza pe `detalii_articol.cont_venit IS NOT NULL`, **infasoara si blocul `FACT-024`** -(garda ramane litera cu litera pe ramura veche: o linie fara politica **si** fara cont trimis cade in -continuare cu `FACT-024`), n-are cursor deloc — deci `descarca_gestiune` ruleaza **exact o data prin -constructie** si bug-ul de set multi-rand nu se mosteneste, fara sa se repare ramura veche. -**Doua hardcodari raman decizii deschise pentru Marius**, nu descoperiri: `SCD = '4111'` si `CU_TVA = 1` -— vezi „Ce ramane de decis" mai jos. - -**VERIFICATA ADVERSARIAL** — `docs\cercetare\parametru_cont_contabilizeaza_articol.md`. Proiectarea -rezista; patru rezultate schimba insa detalii de executie: - -- **Parametrul se cableaza in DOUA locuri VFP, nu unul.** `ofacturare.vc2:14069` si `:18089` **nu** sunt - o duplicare a aceleiasi metode, cum se presupusese: sunt **doua clase distincte**, - `frm_facturare_articole.do_scrie_articole` (`:13967-14195`) si - `frm_facturare_articole2.do_scrie_articole` (`:18003-18221`), fiecare construindu-si separat apelul RPC. -- **`CU_TVA = 1` nu e inofensiv, cum spunea proiectarea.** Actualizarea lui `nproc_tva_max` / - `nid_jtva_coloana` / `nTaxCode` e chiar in interiorul lui `IF V_CU_TVA = 1` (`:12537-12558`), iar - comparatia `nproc_tva_max < V_PTVA` **se uita doar la rata, nu la suma**. O linie fallback cu TVA real - 0% ar intra deci in comparatia de maxim si, daca e prima linie a documentului (`nproc_tva_max` porneste - `-1`, `:1885`), **castiga** — iar coloana si taxcode-ul ei ajung sa descrie **linia de discount a - intregii facturi** (`:6164-6184`). Combinatia e ingusta (linie scutita + discount global + acea linie e - „maximul" de pana atunci), dar efectul e real. Motivul pentru care decizia ii apartine lui Marius e - acum concret, nu formal. -- **`INSERT INTO VANZARI_DETALII ... FROM VANZARI_DETALII_TEMP` are lista de coloane explicita** — 24 de - coloane, `PACK_FACTURARE:13705-13757`, in `scrie_in_vanzari`. Deci daca se vrea `CONT_VENIT` pastrat si - dupa fapt in `VANZARI_DETALII`, **lista trebuie extinsa explicit**; altfel coloana traieste doar in - `VANZARI_DETALII_TEMP` si `ACT_TEMP`. -- **Articolul compus nu e o gaura in proiectare** — e exclus structural, nu prin presupunere. `COMPUS`, - asa cum il citeste `contabilizeaza_articol` (`:7279-7283`), e definit in `VCRM_POLITICI_PRET_ART` ca - proprietate a **perechii (articol, politica)**, identificata prin `ID_POL_ART`. O linie fara politica - n-are `ID_POL_ART`, deci intrebarea nu se poate pune. - -**Golul `ntip = 4` — INCHIS in runda 11.** Cercetat (`docs\cercetare\gol_ntip4_factura_din_avize.md`) si -**verificat adversarial** (`docs\cercetare\verif_goluri_ntip_aviz.md`). Trei rezultate, dintre care doua -schimba ce trebuie scris in pachet: - -- **`ntip = 4` e exclus structural — dar nu prin garda pe care o presupunea proiectarea.** Blocajul nu e - `FACT-024` din `contabilizeaza_articol`, ci **o functie mai devreme**: `adauga_articol_factura`, ramura - `WHEN ntip = 4` (`:5080-5103`), cauta randul sursa din `VANZARI_DETALII` prin `A.ID_POL = V_ID_POL` - **fara handler de exceptie**. Cu `V_ID_POL = NULL` — exact cazul liniei fara politica — comparatia nu - se poate potrivi niciodata, deci apelul cade cu `ORA-01403` **la adaugarea articolului**, in bucla - `do_scrie_articole` (`ofacturare.vc2:13967-14106`), care ruleaza intotdeauna inaintea `Do Case`-ului - (`:14282`). Linia nu ajunge niciodata la `contabilizeaza_articol`; ramura noua n-are nimic de tratat - acolo, si **nu are nevoie de garda defensiva** (ar fi redundanta peste doua straturi independente). -- **GOL REAL, mai ingust decat parea, dar efectiv atins: `SCD` pe avize.** Ramurile de aviz care chiar - ajung la `contabilizeaza_articol` — `ntip IN (28,29)` → `SCD = '461'`, restul avizelor „simple" - (`21, 22, 24, 27`) → `SCD = '418'` (`:7413-7422`) — **sunt accesibile cu `cont_venit` populat**, pentru - ca `adauga_articol_factura` nu cere `id_pol` pe niciunul din ele. Ramura noua, care ia `SCD` din - optiunea deciziei 36 (`4111`), l-ar scrie **necontitionat de `ntip`** → **cont contabil gresit pe - aviz**. Deci ramura noua **alege `SCD` dupa `ntip`**: `'461'` pentru `IN (28,29)`, `'418'` pentru - celelalte avize atinse, si abia pe restul optiunea `RF_CONT_ART_FARA_POL`. - **Verificarea a restrans lista:** `ntip IN (23,25,30,41,42,47)` **nu ajung deloc** la - `contabilizeaza_articol` — sunt deviate mai devreme, in `scrie_factura2`, spre `transfera_articol` - (`23,25,30,41`) sau direct spre `descarca_gestiune` (`42,47`). Golul nu li se aplica. -- **Al doilea gol confirmat: garda `ntip = 46`.** `nTipNotaPlata` **este** `46`, iar `scrie_nota` e - sarita azi intentionat pentru el (`IF ntip <> nTipNotaPlata`, `:7441`). Un articol fara politica poate - ajunge si pe acest tip. **Ramura noua trebuie sa reproduca garda** — altfel scrie o nota care azi e - sarita deliberat. - -**CORECTIE la proiectare — al doilea apelant intern e numit gresit peste tot.** Nu -`scrie_factura_avize_retur`, ci **`scrie_factura_avize`** (`:6692-7058`) — care e chiar handler-ul Oracle -al lui `ntip = 4`, apelat din `ofacturare.vc2:14318`; apelul catre `contabilizeaza_articol` e la `:6858`, -in interiorul lui `IF articole_aviz(j).custodie = 1`. `scrie_factura_avize_retur` (`:6264-6657`) **nu -cheama deloc** `contabilizeaza_articol` — insereaza direct in `VANZARI_DETALII_TEMP` din -`VANZARI_DETALII`. Iar `scrie_aviz_retur` (`:7085-7171`) **n-are niciun apelant VFP gasit** in tot -`COMUN\` si in tot proiectul — posibil cod mort sau apelata din alt produs. Greseala e propagata in -`canal_cont_venit_fara_politica.md:67` si `nota_contabila_fara_politica.md:103`; se citeaza de acum -lista corectata de mai sus. - -**Nota de metoda, valabila pentru toate rapoartele:** presupunerea ca liniile din exportul -`PACK_FACTURARE` au un **offset constant `+17`** fata de rapoartele vechi e **falsa**. Nu exista offset -universal (masurat: `+9` fata de `nota_contabila_fara_politica.md`, `0` fata de alte rapoarte). Numerele -de linie se **re-verifica direct pe fisier**, niciodata prin corectie presupusa. - -**Nu se implementeaza** — decizia 30 sta, #13 incepe dupa #6. - -**33. Mockup-ul nu se republica la v7.** Se duce la **v8** cu rezultatele verificarilor rundei 9 si abia -atunci se republica — o singura citire a HTML-ului, o singura republicare. Url-ul artifact rămâne la v6 -pana atunci. - -### Deciziile lui Marius, runda 10 (10.08.2026) - -**35. Un singur cod de scriere contabila: `pack_facturare`, si la emitere si la editare. -`oscrie_in_fisiere` NU se foloseste in #13.** Marius, 10.08.2026, textual: *„nu doresc folosirea scrie in -fisiere ci doar `pack_facturare` la emitere si la editare pentru ca nu vreau sa intretin doua coduri."* - -**Ce anuleaza:** impartirea propusa la finalul rundei 9 — „emiterea prin parametrul deciziei 34, editarea -prin canalul generic catre `ACT_TEMP`, care nu cere nimic in pachet" — e **respinsa**. Canalul -`actactan` / `tact` → `COMUN\programe\oscrie_in_fisiere.prg` → `pack_contafin.SCRIE_IN_ACT` exista si e -folosit azi in productie de fluxul de editare al lui #6, dar folosirea lui in #13 ar insemna **doua -implementari ale aceleiasi reguli de contare** — una in pachet, pe emitere, alta in VFP, pe editare — -tinute in pas manual la fiecare schimbare a regulii deciziei 27. Motivul e de **intretinere**, nu tehnic, -si nu se reargumenteaza cu „canalul exista deja". - -**Ce impune:** editarea prin regenerare (etapa II) scrie pe **exact acelasi drum** ca emiterea — -`scrie_factura2` → `contabilizeaza_articol`, cu parametrul de cont de la decizia 34. Nu exista ramura de -scriere separata pentru documentul reemis si nu exista editare directa a randului din `ACT_TEMP` din #13. - -**Ce nu atinge decizia 35:** -- **fluxul lui #6** ramane cum e (`ofacturare_comun.vc2:3796-3821` → `frm_modific2024`) — decizia priveste - #13; retragerea lui e alt subiect si alt perimetru — **inchis de decizia 38: coexista, se decide dupa - ce #13 livreaza**; -- **stergerea** documentului vechi din S9 ramane pe drumul existent (`do_sterge` → `oscrie_in_fisiere` + - `pack_contafin.finalizeaza_stergere_nota`), pentru ca acolo **nu exista al doilea cod de intretinut**: - `pack_facturare` nu are echivalent de stergere a notei, iar drumul de stergere e comun intregii suite. - *Presupunere declarata, de infirmat daca Marius vrea si stergerea mutata in pachet — ar fi alt mandat si - alta suprafata de risc.* - -**Ce castiga:** regula deciziei 27 traieste intr-un singur loc, si o corectie pe ea nu trebuie facuta de -doua ori. **Ce costa:** etapa II nu mai are cale ieftina — depinde de parametrul deciziei 34 exact cat -depinde etapa I, deci **S9 nu poate porni inaintea parametrului**, iar regresia pe suita se plateste o -singura data, dar obligatoriu. - -**36. Cele doua campuri ramase fara sursa pe ramura fara politica — decis.** Verificarea a stabilit intai -ca **nu exista in cod nicio sursa alternativa** pentru ele: nici optiune de firma existenta, nici cont pe -partener, nici flag de scutire pe articol sau client (`parametru_cont_contabilizeaza_articol.md`, -sectiunea 4). Deci sunt decizii de produs, si Marius le-a luat asa: - -- **`SCD` — fix, dar citit din configurare, nu hardcodat.** O optiune de firma, cu **`4111` ca implicit**. - **PROIECTATA** — `docs\cercetare\optiune_firma_cont_debit.md`. **Costul e mult mai mic decat parea: - tiparul exact exista deja in productie, in acelasi pachet, pe acelasi camp.** - `pack_facturare.scrie_incasare2` (`PACK_FACTURARE:13161-13234`) isi ia `V_SCD` in **trei niveluri**: - parametru explicit daca a fost dat → **optiunea de firma** (`RF_CONT_INCASARE_BONFISCAL` s.a.m.d., cate - una per tip de incasare) → **constanta hardcodata** daca optiunea lipseste. Se copiaza identic, si se - inlocuieste punctual linia `SCD := '4111'` din proiectarea ramurii noi. - Mecanismul are 15+ ani: tabelul `OPTIUNI` (458 randuri azi), `PACK_SESIUNE.getoptiunefirma`, si un - **ecran de editare complet generic** peste tabel (`frm_optiuni` / `frm_optiuni_nou`, - `COMUN\clase\oOptiuni.vc2`) — deci **cheia noua nu cere niciun cod VFP nou**, doar randul in tabel. - Nu exista `ID_FIRMA`: fiecare firma are schema Oracle proprie, deci `OPTIUNI` din schema de conexiune - **este** deja „optiunile firmei curente". - **Dubla plasa de siguranta, si asta acopera integral cerinta lui Marius:** implicitul `4111` traieste si - in randul din `OPTIUNI` (scris de migrare, editabil fara recompilare), si hardcodat langa - `getoptiunefirma` — deci si o instalare veche fara migrare, si un rand golit din ecran cad tot pe `4111`. - `getoptiunefirma` intoarce `''` cand nu gaseste, ceea ce in Oracle e `IS NULL`, deci ambele cazuri se - trateaza cu aceeasi conditie. - **`ASCD` nu trebuie atins** — se calculeaza deja din `V_SCD`, deci primeste automat valoarea corecta - indiferent de sursa. - **`PROGRAME` se pune larg, nu doar `ROAFACTURARE`** — intrebarea a ramas deschisa in raport, dar se - inchide combinand-o cu masuratoarea de regresie: apelul catre `adauga_articol_factura` traieste in - `COMUN\clase\ofacturare.vc2`, fisier prezent si folosit in **toate cele sapte produse**, deci ramura noua - e atinsa de toata suita, exact ca `RF_CONT_INCASARE_*` (care listeaza sase produse). -- **`CU_TVA` — derivat din cota liniei**, nu fixat: `1` daca `proc_tvav > 0`, altfel `0`. Asta **elimina - prin constructie** efectul gasit la verificare: o linie scutita nu mai intra in comparatia de maxim din - `nproc_tva_max`, deci nu mai poate imprumuta coloana si taxcode-ul ei liniei de discount a intregii - facturi. Pretul e o regula in plus in pachet si un comportament **diferit de ce face azi nota** pentru o - linie scutita — diferenta e intentionata, nu accidentala, si se noteaza ca atare la testare. - -Deci lista parametrilor noi ramane la **unul singur** (`V_CONT_VENIT`): `SCD` vine din configurare, iar -`CU_TVA` se deriva in pachet din date deja prezente pe rand. - -**37. Cheia optiunii si validarea — decise (10.08.2026).** -- **Cheia: `RF_CONT_ART_FARA_POL`**, aliniata la familia `RF_CONT_INCASARE_*` — adica exact optiunile care - alimenteaza azi `SCD` in acelasi pachet. Cele cinci apar astfel **grupate alaturi in ecranul de - optiuni**, ceea ce le face inteligibile impreuna. Incape in `OPTIUNI.VARNAME` (`VARCHAR2(30)`). - `VARTYPE = 'CHARACTER'`, `VARVALUE = '4111'`, `PROGRAM = 'ROAFACTURARE'`, `PROGRAME` larg (vezi 36). -- **Fara validare de cont**, nici la salvare, nici la citire — se copiaza tiparul existent. Motivul: - niciuna dintre optiunile de tip cont nu e validata azi nicaieri, iar `RF_CONT_INCASARE_*` traieste asa - de 15 ani; a introduce validare aici ar fi o imbunatatire noua, nu continuarea unui tipar, si ar cere - fie cod nou in pachet, fie prima logica per-cheie din ecranul generic `COMUN\clase\oOptiuni.vc2` — - fisier al intregii suite. **Garda gratuita ramane lungimea:** un `VARVALUE` peste 4 caractere da - `ORA-12899` la `INSERT INTO ACT_TEMP`, deci esec zgomotos, nu cont tacut gresit. - *De consemnat la testare ca risc acceptat constient:* un cont inexistent in planul de conturi, scris din - greseala in ecran, ajunge pe nota neschimbat — acelasi risc pe care produsul il are deja, nu unul nou. - -### Deciziile lui Marius, runda 11 (10.08.2026) - -**38. Fluxul de editare al lui #6 nu se retrage odata cu #13. Coexista, si se decide mai tarziu.** -Intrebarea pusa la finalul rundei 10 — daca decizia 35 („un singur cod de scriere contabila") obliga la -rerutarea editarii lui #6 pe acelasi drum — primeste raspuns: **nu acum**. #6 ramane pe editarea directa -a randului din `ACT_TEMP` (`ofacturare_comun.vc2:3796-3821` → `frm_modific2024` → `oscrie_in_fisiere`), -#13 merge pe regenerare prin `pack_facturare`. Cele doua traiesc separat, pe povesti separate. -**Ce inseamna concret:** decizia 35 se citeste strict ca perimetru al lui #13 — nu se proiecteaza si nu -se planifica nicio retragere a fluxului lui #6 in aceasta poveste, si nu se adauga in #13 nicio piesa -care sa pregateasca acea retragere. Subiectul **nu e inchis definitiv**: se redeschide dupa ce #13 -livreaza regenerarea si se vede in practica daca intretinerea celor doua drumuri doare cu adevarat. -Pana atunci nu se reargumenteaza in niciun sens. - -**39. S4 ramane integrala: se desface si registrul de cantitate ramasa.** Proiectarea rundei 11 a aratat -ca `crsarticole` nu e doar sursa gridului, ci **registrul cantitatii ramase de facturat** — scris de -`do_sterge` la stergerea unei linii (`ofacturare.vc2:14640-14669`) si citit prin -`Calculate Sum(cantitate) To lnCantitateRamasa` ca sa se decida **inchiderea automata** a comenzii / -avizului (`:14303-14311`, `:14334-14338`). Varianta ieftina ar fi fost sa se restranga S4 la lista de -preturi; Marius a ales varianta intreaga: **bookkeeping-ul se decupleaza de cursorul incarcat in masa**, -ca sa poata disparea incarcarea si pe comanda si pe aviz. -**Consecinta, declarata explicit:** S4 nu mai e o poveste de performanta pe formular, ci atinge -**inchiderea automata a documentelor sursa** — cea mai mare suprafata de regresie din etapa I, si -singura care poate lasa o comanda deschisa (sau o poate inchide prematur) fara ca operatorul sa vada -ceva. Criteriul de „gata" al lui S4 include de acum, obligatoriu, **paritate pe inchiderea automata**: -acelasi document sursa, aceleasi linii facturate partial, acelasi `INCHISA` la final, pe fiecare tip cu -document sursa. Registrul nou trebuie sa fie sursa unica — nu o a doua copie tinuta in pas cu prima, -altfel povestea introduce exact tipul de dublura pe care decizia 35 il refuza in alta parte. - -**40. Bug-ul de dezalocare POS se repara in S3b, in trecere.** Vezi `L.4`. Consecinta acceptata: diff-ul -lui S3b **nu mai e o mutare pur mecanica**, deci testarea lui trebuie sa acopere si dezalocarea POS pe -calea veche, nu doar paritatea de emitere. - -**41. Sectiunea pliata are DOUA comutatoare, nu unul si nu cinci.** Grupul de **incasare** — singurul cu -efecte laterale reale (alocare / dezalocare de numere) si singurul blocat pe document emis prin decizia -25 — primeste comutator propriu. Delegat/transport, adresa, text aditional si analiticele stau impreuna -sub al doilea. Motivul e izolarea riscului: garda de non-alocare la toggle -(`opt_incasat.ProgrammaticChange`) se scrie si se testeaza **intr-un singur loc**, nu pe cinci stari. - -**42. Validarea de curs valutar se restrange la valuta articolului cautat.** Pe varianta filtrata a -cursoarelor, `verifica_cursuri_valute` nu mai ruleaza global, ci doar pe valuta randului adus. -**Schimbare de comportament asumata:** un curs lipsa pe **alta** valuta nu mai e semnalat la deschiderea -formularului, ci abia cand se ajunge la un articol pe acea valuta. Se consemneaza la testare ca -diferenta intentionata fata de azi, nu ca regresie. - -#### Puncte marunte ramase din proiectarile rundei 11 — se merge pe recomandare daca Marius nu spune altfel - -Nu blocheaza nimic; se inchid la implementarea povestii respective. Enumerate ca sa nu se piarda intre runde. - -| # | Punct | Poveste | Se merge pe | -|---|---|---|---| -| a | Textul exact al tooltip-urilor pentru analiticele read-only si pentru grupul de incasare blocat | S3b | formularea propusa in raport, sectiunile 2.2 si 5.3 | -| b | Eager vs. lazy pentru lookup-urile Oracle din `Init` (delegat / masina ultimei facturi, casa) | S3b, S3 | **lazy**, ca sa nu se plateasca la fiecare depliere; deschis si in `s3_portare_antet.md` §5 | -| c | `_checkbox1` vs. `chkDetaliat` — care devine campul unic de „listare detaliata" | S3b, S1 | **INCHIS (runda 12)**, pe cod: sunt **doua controale distincte** in `frm_alte_date`, nu o duplicare — nu exista nimic de ales. `docs\cercetare\s4c_discount_in_grid.md`, sectiunea 11 | -| d | Forma lui `toSursa`: obiect scatter (duck-typing) sau clasa dedicata | S3c | **duck-typing**, ca azi — o clasa noua n-ar schimba nimic functional | -| e | Conversia comenzii si a contractului: un commit sau doua | S3c | **un singur commit** — ating aceleasi trei fisiere comune, separarea nu reduce regresia, doar amana testarea pe ROACONTRACTE | -| f | `goComanda = ''` redundant la `Cw3.do_actiune` dupa conversie | S3c | **se lasa**, marcat explicit „intentionat" in diff, ca sa nu para omisiune la review | -| g | Contractul are doua meniuri (articole vs. rate): eticheta generica sau diferentiata pe `OPT_FACTURARE` | S4b | **diferentiata** — „Alege ratele de facturat…" e gresita pe contractele cu articole | -| h | „Alege facturile de returnat…" ramane doar la antet sau capata incarcare aditiva din bara | S4b | **ramane la antet** in etapa I; mutarea e o poveste separata | -| i | `But_renunt1` / `But_reset1` primesc `Caption` pentru consistenta cu bara etichetata | S4b | **da** — decizia 13 cere etichete, nu iconite mute; exceptia ar fi inconsecventa | -| j | Ordinea si formularea optiunilor din `xmenu()` per sursa | S4b | tabelul din sectiunea 3 a raportului, ca propunere | -| k | UX-ul contractului cu doua surse pe acelasi grid conceptual (`crsarticole` filtrat + `crsarticole1`) | S4 | de confirmat vizual la mockup, nu pe hartie | - -### Ce s-a dovedit ca exista deja (deciziile 10, 11, 12, 14) - -Verificat pe cod la 09.08.2026; rapoartele complete sunt in `docs\cercetare\` -(`proforma_copiere_puncte_intrare.md`, `discount_pe_articol.md`, -`inventar_controale_formulare.md`, `import_roris_roaacnpro.md`). - -- **Proforma nu e tip de document si nu are formular propriu.** E valoarea `PROFORMA` din combo-ul - „Tip document” (`ct_clb_fdoc._combobox1`, `RowSource = "FACTURA,PROFORMA,BON FISCAL"`, - `ofacturare.vc2:8745-8754`), care seteaza `nIdTipDoc = 23` si, prin setter, - `eProforma = 1` (`ofacturare_comun.prg:593-599`, `nIdTipDocProforma = 23` la `:104`). Deci intra in - formularul unificat **fara nimic de portat**. Specific proformei si de pastrat: serie si numar - proprii, realocate la comutarea din combo (`ofacturare.vc2:9415,9427-9428`); **fara nota contabila - si fara atasamente**, prin garzile `poDate.eProforma = 0` - (`ofacturare.prg:1960,1996,2052,2063,2103`); raport propriu (`:1638-1643`, `COMUN\Rapoarte\proforma.fr2`); - relistarea pe cale separata (`ofacturare_comun.vc2:7230-7264`). -- **Copierea foloseste deja acelasi formular, cu antetul editabil.** `do_copiaza` -> `copiere_factura` - -> `factureaza(tip, toFactura)` -> `frm_date_factura` precompletat de - `completeaza_setari_document(toFactura, .T.)` (`ofacturare_comun.prg:362-412`). Numar nou se aloca - **intotdeauna**, neconditionat de copiere (`ofacturare.prg:208,211`). Nimic din traseu nu blocheaza - controale de antet. -- **Butonul de facturare din pagina de comenzi exista deja in ROAFACTURARE**: `ct_comenzi.do_factura` - (`ocomenzi.vc2:1580-1596`) face `SCATTER NAME goComanda MEMO` si cheama `facturare_comenzi` -> - `factureaza(3)`; butonul `But_factura1` (`ocomenzi.vc2:932-937`) e ascuns cand comanda e deja - facturata (`:2199-2203`), si e montat pe Page5 (`ofundal_facturare.vc2:604`, `Ferestre\fundal.sc2:206`). -- **Precompletarea contractului vine din programul de contracte** (`ROACONTRACTE`): - `ferestre_contracte.vc2:1538-1549` si `:1605` -> `facturare_contracte` -> `factureaza(2/6/52)`, - cu contractul deja completat — exact ce cere decizia 12, si **functioneaza azi**. In ROAFACTURARE - exista, in plus, calea din meniu **fara** precompletare (`ofundal_facturare.vc2:886-897`), unde - utilizatorul alege contractul in formular. Cele doua coexista; #13 nu adauga o lista de contracte - in ROAFACTURARE si **nu depinde de #10**. -- **Discountul pe articol are deja si procent, si valoare — dar intr-un dialog separat.** Vezi K. -- **Deblocarea antetului exista deja**, dar sub forma a patru bife individuale in - `frm_modifica_factura`; in formularul unificat ele sunt inlocuite de un singur buton comutator. - Vezi I. -- **Data cursului valutar nu e conditionata azi de valuta.** Vezi M. - -### Canalul de precompletare e un global, nu un parametru - -`factureaza(tnTip, toFactura)` (`ofacturare.prg:81-82`) nu are parametru pentru sursa: comanda si -contractul se transmit prin globalele `goComanda` / `goContract`, citite in `oDateFactura.Init` -(`ofacturare_comun.prg:301-328` comanda, `:261-297` contract). De aceea calea generica de comanda e -obligata sa scrie explicit `goComanda = ''` inainte de apel (`ofundal_facturare.vc2:899-902`), altfel -ar ramane precompletata cu ce era in sesiune. **Calea generica de contract nu face resetarea -simetrica** (`:886-897`) — de verificat daca `goContract` poate ramane populat intr-o sesiune si -precompleta tacit un document nou. Decizia 12 se implementeaza corect prin **parametru explicit**, nu -prin inca un global. - -## Relatia cu #6 — transata: #13 incepe dupa #6 - -`plan_06_editare_factura.md` rezolva **aceeasi problema de fond** (o factura emisa nu se poate -corecta fara ca notele si rulajele sa ramana in urma) pe calea opusa: editare directa in -`frm_modific2024`, cu `id_fact` pastrat. Planul #6 a respins explicit regenerarea, cu motivul ca -*"emiterea face verificari de stoc si alte protectii dependente de tipul documentului; la -regenerare acestea ar esua pe date care intre timp s-au schimbat"*. - -Argumentul acela **cade partial** daca stergerea si reemiterea se fac in aceeasi tranzactie, in -ordinea corecta — vezi sectiunea "Stocul" mai jos. Dar nu cade complet: raman verificarile care nu -tin de stoc (configurare politica / contract / cota TVA, vezi `adauga_articol_factura`). - -Cele doua nu se exclud tehnic, dar **se suprapun pe aceleasi fisiere** si, mai important, produc -doua semantici diferite pentru "am corectat factura": - -| | #6 — editare directa | #13 — regenerare | -|---|---|---| -| Punct de intrare | registru jurnal + formular facturi | formularul de facturi | -| Utilizator vizat | contabil | operatorul de facturare | -| `ID_FACT` | pastrat prin constructie | **pastrat, prin cerinta** (vezi E) | -| Serie, numar, data | neschimbate | neschimbate (cale proprie, vezi F) | -| `COD`, `ID_VANZARE` | `COD` nou, `ID_VANZARE` pastrat | ambele noi | -| Notele si rulajele | rescrise din formularul de note | **regenerate din articole**, ca la emitere | -| Documentul sursa (comanda/aviz/contract) | neatins | **eliberat si reconsumat** | -| Efort | mediu (in curs, S1–S3 livrate) | mare | - -**Decizia lui Marius (09.08.2026): coexista, cu roluri separate.** #6 ramane calea contabilului -pentru corectii pe nota — si e singura cale posibila din registrul jurnal din ROACONT/ROAGEST, unde -nu exista contextul de facturare (`poDate`, `crsfactura`, politici, serii). #13 devine calea -operatorului de facturare: modific documentul, notele si rulajele se refac singure. - -**Coliziune de lucru: nu exista. DECIS (decizia 30): #13 incepe dupa ce #6 se termina.** Sesiunea de -la #6 a atins `COMUN\clase\ofacturare_comun.vc2` (`do_editare_factura`, `:3715-3869`), a creat -`COMUN\programe\ofacturare_editare.prg` si a adus `omodificari.vcx` in proiect (`97d1613`) — dar -implementarea lui #13 nu porneste cat timp acestea sunt in lucru, deci **nu e nevoie nici de -impartire de perimetru, nici de coordonare intre fire.** Cele doua buguri semnalate (L.0 si L.0-bis) -raman semnalate catre #6, nu reparate din #13. -Ce **nu** decade odata cu coordonarea: **decizia 26** — analiticele raman read-only in #13. Motivul ei -e semantic (#6 le editeaza la nivel de linie de nota, #13 le-ar scrie uniform pe tot documentul), nu -o problema de calendar. - ---- - -## Ce s-a stabilit din cod - -### A. Formularul unificat exista deja ca prototip, oprit din 2017 - -`frm_facturare_articole2` (`COMUN\clase\ofacturare.vc2:15741-19355`) **este** formularul cerut la -punctul 13, la nivel de controale: - -- are deja campurile de antet mutate din `frm_date_factura`: `Clb_serie_act1`, `Clb_nract`, - `Clb_dataact`, `Clb_zi_curs`, `Clb_data_scadenta`, `Ct_clb_nume_client`, `Ct_clb_responsabil`, - `Ct_clb_sectie`, `Ct_clb_lucrare`, `Ct_clb_venchelt`, `Ct_clb_altele`, `Ct_clb_valuta`, - `clb_fdoc`, `Ed_tx_simplu1` (`:15744-15812`); -- **nu** are `grd_articole`, `grd_contracte`, `cb_politici_preturi`, `cb_contracte`, casetele de - cautare `txtCodmat` / `txtArticole` — adica exact partea care azi consuma incarcarea in masa; -- are grid editabil in linie, cu combouri `combosql` din `cautare.vcx` pe `cCodMat.cboCodmat`, - `cDenumire.cCboDenumire`, `cGestiune.cCboGestiune` (`:16751-16881`), plus `But_nou1`; -- e lansat de `factureaza2` (`COMUN\programe\ofacturare.prg:590-1080`), care se cheama numai daca - `gnFacturareNou = 1` **si** utilizatorul raspunde DA la un `AMESSAGEBOX` (`:87-92`). - -**Cat de mort e prototipul.** In `factureaza2`, deschiderea formularului de date si incarcarea -articolelor sunt inchise cu `If .F.`: `:707-711` (formularul de date), `:826-829` (executia -cursorului de articole), `:838` (verificarea de "nu exista articole"), `:985-1008` (`crspolitici` -si `crscontracte`). `Init`-ul lui `frm_facturare_articole2` are 92 de linii (`:18988-19080`) fata -de 368 la `frm_facturare_articole` (`:14976-15344`) si **nu completeaza niciun camp de antet** — -controalele sunt acolo, logica nu. - -**Drift-ul dintre `factureaza` si `factureaza2`** (fork din 08.06.2017, nesincronizat): lipsesc din -`factureaza2` tipurile 51 si 52, ramura `llCopiere` / `toFactura`, completarea `codmatc` / -`codnc8` / `codcpv`, `GetInstitutiePublica`, `GetSoldClient`, filtrul `RORTC` pe `jtva_coloane`, -tratarea `eProforma`, `verifica_numar(16, ...)` pentru chitanta, `cursor_retur_document`, -`cursor_avize` cu tip 23. **Concluzie: nu se continua cu doua proceduri.** Se merge pe una singura, -cu un parametru de mod, altfel divergenta se reia imediat (exact tiparul care a produs problema -reparata la #8). - -### B. Nu doua formulare, ci patru - -Fluxul de azi are **patru** ferestre. Runda 2 numarase trei; a patra, `frm_articol_factura` -(`ofacturare.vc2:1108-2659`), se deschide **pentru fiecare articol adaugat**, din -`do_adauga_articol` (`:12873`, `ofrmadarticol.Show(1)`, doar daca `!tlImplicit`) — si e locul in care -se introduce azi discountul pe linie (vezi K). Formularul unificat o desfiinteaza, deci campurile ei -trebuie sa aiba unde sa se mute. - -Celelalte trei: - -1. `frm_date_factura` (`ofacturare.vc2:8482-9869`) sau `frm_date_aviz` (`:6566-7618`) — - 17, respectiv 13 metode `do_cauta_*`, plus `do_schimba_tipdoc`, `inainte_de_do_termin` si un - `Init` de 234 / 247 de linii; -2. `frm_facturare_articole` (`:10968-15739`) — compunerea; -3. `frm_alte_date` (`COMUN\clase\ferestre_cere_date.vc2:2219-3353`) — **aratat dupa** confirmarea - articolelor, din `inainte_de_do_termin` (`ofacturare.vc2:14921-14941`): delegat, auto, agent, - adresa de facturare, incasare (numerar / chitanta / bon fiscal / POS), text aditional. Aloca si - numere proprii (bon fiscal, POS, chitanta). - -Punctul 13 vorbeste doar despre primele doua. **Decizia 7 le aduce pe toate trei**: `frm_alte_date` -intra in formular, ca zona pliata. Recomandarea din runda 2 („ramane dialog in prima etapa”) e -respinsa. - -Ce inseamna concret, din inventarul de controale: se muta patru grupuri — delegat si transport -(`Ct_clb_delegat`, `Ct_clb_masina`, `Ct_clb_agent`, `Clb_dataora_exp`), incasare (radiogrupul -`opt_incasat` cu patru optiuni, `Cb_casa`, `Clb_serie_chit`, `Clb_nrchit`, `Clb_incasat`, -`cmdModificaBon`, `chkPOS`, `chkDetaliat`, `cboTipFactura`), adresa de facturare -(`clb_adresa_facturare`) si textul aditional (`Ed_tx_simplu1`) — -`ferestre_cere_date.vc2:2343-2638`. **Masina de stari se muta ca atare, nu se rescrie**: -`actualizeaza_tipincasare` (`:2698-2856`) comuta vizibilitatea pe cele patru valori si aloca sau -dezaloca numarul de chitanta (`:2783`), de bon fiscal (`:2807`) sau de POS (`:2844`). Riscul semnalat -in runda 2 nu dispare, dar e localizat: **alocarea de numere trebuie sa ramana legata de aceleasi -evenimente**, nu de deschiderea formularului. - -Peste ele coboara analiticele din antet (decizia 8): `Ct_clb_venchelt`, `Ct_clb_sectie`, -`Ct_clb_responsabil`, `Ct_clb_lucrare`. - -### C. Incarcarea articolelor — de ce dureaza si ce inlocuieste - -`factureaza` executa, inainte de a deschide compunerea, **un singur apel** care aduce toate -articolele disponibile: `pack_facturare.cursor_preturi` (`ofacturare.prg:281-286`, corp la -`PACK_FACTURARE:2121+`), respectiv `cursor_contract` / `cursor_comanda` / `cursor_avize` / -`cursor_gestiune` / `cursor_retur` dupa tip (`ofacturare.prg:265-311`). Rezultatul intra in -`crsarticole` si tine gridul de sus. - -`cursor_preturi` **nu are filtru pe articol** — semnatura e -`(V_DATA_CURS, V_TIP, V_ID_VALUTA, V_ID_GESTIUNE_INIT, V_LUNA, V_AN, V_ID_UTIL, V_ID_SUCURSALA)` -(`PACK_FACTURARE:335-343`). De aici vin miile de randuri. - -Mecanismul de inlocuire **exista deja**, in prototip: `combosql` cauta pe server, incremental, cu -`csourcesql = select codmat, denumire, um, grupa, subgrupa, codbare, id_articol from vnom_articole` -si `csourcewhere = inactiv = 0` (`ofacturare.vc2:16751-16766`). - -**Dar `vnom_articole` e nomenclatorul, nu lista de preturi.** Nu are pret, nota contabila, cota TVA, -valuta, `id_pol`, `gestionabil`, `pret_cu_tva` — toate vin azi din `cursor_preturi`. Deci partea -Oracle a lucrarii e o **varianta filtrata pe articol** a cursoarelor de facturare, apelata la -alegerea liniei, nu la deschiderea formularului. Nu e o simplificare: ramurile pe tip din -`cursor_preturi` (restaurant, custodie, retur, contract, comanda) trebuie pastrate identic, altfel -pretul difera intre cele doua cai. - -**Legatura cu #12.** Daca articolul poate purta el insusi pretul si nota contabila (varianta C din -`plan_12`), cautarea in linie devine directa si nu mai are nevoie de o rezolvare separata a listei -de preturi. #13 nu depinde de #12, dar cine face #12 dupa #13 va rescrie exact bucata asta. - -### D. Regenerarea — piesele exista, montajul nu - -**1. Stergerea elibereaza deja documentul sursa.** `pack_facturare.sterge_factura` -(`PACK_FACTURARE:5415-5591`) face, pe langa `sters = 1` pe `VANZARI` si `VANZARI_DETALII`: - -- `FACTURAT = 0, ID_UTILFACT = NULL, DATA_FACTURAT = NULL` pe avizele facturate (tip 4 si 24); -- `STERS = 1` pe `VANZARI_CANTITATI` (cantitatile consumate de pe avize); -- `STERS = 1` pe `COMENZI_ELEMENTE` cu `CANTITATE < 0` (consumul din comanda); -- `STERS = 1` pe `VANZARI_CORESP` si pe `CTR_RATE_FACTURI` (contracte in rate, tip 2/6/52). - -Adica **exact reversul consumului sursei** — conditia ca reemiterea sa poata reconsuma aceeasi -comanda / acelasi aviz / aceeasi rata. Sunt si garzi: refuza stergerea daca s-au emis facturi sau -avize de retur peste document (`:5432-5477`). - -**2. Calea VFP de stergere completa exista.** `frm_facturi.do_sterge` -(`COMUN\clase\ofacturare_comun.vc2:4658-4874`): tranzactie manuala, `oscrie_in_fisiere`, apoi -`pack_contafin.finalizeaza_stergere_nota` (care cheama `sterge_din_vanzari`). Rulajele si notele -dispar pe aceasta cale, nu prin `sterge_factura`. - -**3. Reconstituirea liniilor exista.** `pack_facturare.cursor_retur_document(..., V_COPIERE = 1, ...)` -(`PACK_FACTURARE:3932-4045`) reface liniile din `VANZARI_DETALII`, cu `explicatie`, `id_gestiune`, -`pret_achizitie`, `pretd`, `id_jtva_coloana`, `id_jtva_coloana_ex`, `lot`, `serie`, `id_pol`, -`pret_cu_tva`, plus `curs` / `multiplicator` din `VANZARI_CURSURI`. E deja folosita, exact asa, la -copierea unei facturi. - -**4. "Redeschide formularul precompletat" exista.** `frm_facturi.do_copiaza` -(`ofacturare_comun.vc2:3640-3713`) -> `copiere_factura` (`oproceduri_facturare.prg:150-153`) -> -`factureaza(tip, toFactura)`, iar in `factureaza` ramura `llCopiere` (`ofacturare.prg:255-257`, -`:458-473`) incarca liniile documentului si adauga peste ele lista de preturi, ca sa se poata -adauga articole noi. `oDateFactura.completeaza_setari_document(toFactura, .T.)` -(`COMUN\programe\ofacturare_comun.prg:362-412`) precompleteaza antetul. - -**Ce nu se poate refolosi ca atare:** `do_copiaza` **degradeaza intentionat tipul** — factura din -contract / comanda / aviz devine `tip = 1`, avizele devin `22`, transferurile `10` -(`ofacturare_comun.vc2:3696-3710`), cu comentariul *"nu are sens sa fie copiata, dar o tratez ca pe -o factura din lista de preturi"*. La regenerare, degradarea e **exact ce nu trebuie sa se intample**: -documentul nou trebuie sa ramana pe acelasi tip, ca sa reconsume sursa. Deci se cloneaza structura -lui `do_copiaza`, nu comportamentul. - -### E. `ID_FACT` — cerinta si ce presupune ea - -**Cerinta (decizia 2):** documentul reemis pastreaza `ID_FACT`. - -**Starea de azi, verificata:** `ID_FACT` se genereaza cu secventa, neconditionat. -`PACK_CONTAFIN.SET_IDFACT(V_GCS)` e `SELECT SEQ_IdFact.NEXTVAL` -(`COMUN\docs\PACK_CONTAFIN.pck:3037-3040`), citit cu `get_idFact()` (`:725`), iar `DOCUMENTE` -primeste rand nou cu acel `ID_DOC` (`:796-808`). - -**Dar intuitia „id_fact depinde de numar si data" e corecta istoric:** exista in pachet varianta -`SET_IDFACT(tdDataAct, tcSerie_Act, tnNrAct, tnId_Ctr)` care cauta intai in `DOCUMENTE` dupa -`NRACT` + `SERIE_ACT` + `DATAACT` + `ID_CTR` si ia secventa doar la `NO_DATA_FOUND` -(`:3016-3035`) — **comentata**, deci inactiva. - -**Ce inseamna pentru #13** (de proiectat in S9, dupa verificarea din handoff): - -- fie se reactiveaza cautarea, sub un comutator folosit **numai** de regenerare — modificarea lui - `SET_IDFACT` fara comutator ar schimba comportamentul pentru toata suita, ceea ce nu se face; -- fie se transmite `ID_FACT`-ul cunoscut, ca parametru, pe drumul de scriere; -- **capcana de ordine**: cautarea filtreaza `STERS = 0`. Daca documentul vechi e marcat sters - inainte, cautarea nu-l mai gaseste si se genereaza id nou. Deci `ID_FACT`-ul se citeste **inainte** - de stergere, in aceeasi tranzactie. - -Cu `ID_FACT` pastrat, ce ramane de inventariat e mult mai putin decat in varianta cu id nou: -`ATASAMENTE_VANZARI` si `marcheaza_facturat` merg pe `ID_VANZARE` (care **se schimba**), -`ANAF_EFACTURA` e gol prin garda, iar `DOCUMENTE` / `ACT` / `RUL` / `JV2007` / `IREG_PARTENERI` merg -pe `ID_FACT` (pastrat). **`ID_VANZARE` nou ramane singura discontinuitate reala** — de verificat -daca `DOCUMENTE` primeste un al doilea rand pe acelasi `ID_DOC` sau il refoloseste. - -### F. Serie, numar, data — cale proprie, nu regenerare - -**Cerinta (deciziile 3 si 4):** toate datele se pastreaza, inclusiv numarul; iar antetul care intra -in identitatea documentului se modifica pe cale proprie. - -Calea exista deja si e exact ce trebuie: `pack_facturare.modifica_date_factura` -(`PACK_FACTURARE:14392-14463`) face `UPDATE` in loc si **propaga serie / numar / data / scadenta -pe `VANZARI`, `DOCUMENTE`, `ACT`, `IREG_PARTENERI`, `JV2007`, `RUL`, dupa `ID_FACT`** -(`:14427-14460`). Tot ea scrie delegat, agent, masina, adresa de facturare, text aditional, -`dataora_exp`, `listare_detaliata`, `tip_saft`, `efactura`. E procedura din spatele actiunii de azi -`frm_facturi.do_modifica` -> `frm_modifica_factura`. - -**Deci in formularul unificat:** campurile de antet se scriu prin `modifica_date_factura`, nu prin -regenerare. Regenerarea porneste doar pentru ce atinge sumele. Vezi tabelul din G-bis. - -`oGeneratorNumere` (`COMUN\programe\oserii_numere.prg:227-233`) nu incurca: la modificare nu se -aloca numar nou, deci nu e nimic de dezalocat. Intrebarea despre o eventuala constrangere de -unicitate pe `VANZARI(SERIE_ACT, NUMAR_ACT)` **dispare** — nu mai exista doua documente nesterse cu -acelasi numar, pentru ca numarul nu se realoca; ramane doar cazul „vechi sters + nou nesters", care -e exact situatia de azi de la orice stergere si reintroducere. - -### G-bis. Cele trei modificari de azi, si ce se scrie la confirmare - -Pe `frm_facturi` exista azi trei actiuni care ating acelasi document, fiecare cu fereastra ei: - -| Actiune de azi | Formular | Ce atinge | Sursa | -|---|---|---|---| -| `do_modifica` | `frm_modifica_factura` | antet: ruta, delegat, agent, serie, numar, date | `ofacturare_comun.vc2:4538-4637` | -| `do_modifica_explicatie` | `frm_modifica_articol_factura` | `explicatie` + `taxcode` pe o linie | `:4639-4656` | -| `do_editare_factura` (#6) | `frm_modific2024` | nota contabila si rulajele | `:3715-3869` | -| — | — | **cantitati, preturi, linii: nicaieri** | — | - -Decizia 6 le comaseaza in formularul unificat. Ruta de scriere se alege dupa ce s-a schimbat: - -| Ce s-a schimbat | Cum se scrie | Efect | -|---|---|---| -| Serie, numar, data, scadenta, delegat, auto, agent, adresa facturare, text aditional | pe loc, `modifica_date_factura` | propaga dupa `ID_FACT` | -| Explicatia si `taxcode` pe o linie | pe loc, `modifica_explicatie_articol` (`PACK_FACTURARE:14464-14472`) | doua coloane, nicio suma | -| Cantitati, preturi, linii adaugate/sterse, discount, gestiune, cota TVA | **regenerare** | vechiul `sters = 1`, documentul nou scris pe drumul de emitere, note si rulaje refacute | -| Nimic | nimic | documentul nu se rescrie degeaba | - -Ultima linie nu e cosmetica: fara ea, orice deschidere a formularului ar produce un document nou si -ar umple `VANZARI` cu randuri sterse. - -**Gol descoperit la S1 (runda 7): tabelul de mai sus nu acopera tot antetul.** Inventarul camp-cu-camp -(`docs\S1_inventar_campuri_formular_unificat.md`) a gasit trei grupuri de campuri de antet care **nu -sunt printre cei 14 parametri** ai lui `modifica_date_factura` si pentru care nu s-a gasit alta ruta: - -- **analiticele** — venit/cheltuiala, sectie, responsabil, lucrare (grupul „Pliat — analitice” din I); -- **identitate/sursa, altele decat serie/numar/data/scadenta** — tip document, valuta, zi curs, client, - sursa/„altele”, gestiune sursa, politica de preturi; -- **grupul de incasare** — mod incasare, casa, serie si numar de chitanta/bon, suma incasata, POS. - -**Verificat (runda 7): `docs\cercetare\rute_scriere_antet.md`.** Cautare exhaustiva in ambele straturi -— Oracle (pachetul de facturare curent, plus `PACK_CONTAFIN`, `PACK_UPDATE`, `PACK_MIGRARE`; toate -cele 13 aparitii de `UPDATE VANZARI` inspectate individual) si VFP (indexul complet: 316 fisiere, -1128 clase, 9281 metode). Rezultatul e neuniform pe cele trei grupuri: - -| Grup | Verdict | Ce inseamna | -|---|---|---| -| **B** — tip document, valuta, zi curs, client, sursa, gestiune sursa, politica de preturi | **NU**, toate sapte | Controalele traiesc **exclusiv** in wizardul de emitere; nu exista niciun `UPDATE` care sa le atinga dupa emitere. Un „nu" verificabil, nu o absenta de cautare. | -| **C** — mod incasare, casa, serie/nr chitanta, suma, POS | **NU direct** | Nu exista coloane proprii pe `VANZARI`: incasarea devine ea insasi **o linie de nota contabila** (`scrie_incasare2`). Nicio procedura de tip „modifica incasare". | -| **A** — sectie, responsabil, lucrare | **DA** | Dar nu prin `modifica_date_factura`, ci prin **mecanismul lui #6**, aterizat chiar pe branch-ul asta (`97d1613`). Vezi mai jos. | -| **A** — venit/cheltuiala (`ID_VENCHELT`) | **neconfirmat** | Gridul afiseaza coloana `dst_chlt`, dar `do_modifica` nu are caz pentru ea. | - -**Descoperirea care conteaza: analiticele sunt deja editabile, dar din #6.** `frm_facturi.do_editare_factura` -(`ofacturare_comun.vc2:3690-3869`) e acum cablat la `frm_modific2024` (`omodificari.vc2`), incarca -nota contabila a documentului emis si o rescrie prin `OSCRIE_IN_FISIERE` -> -`pack_contafin.finalizeaza_modificare_nota`, care **resincronizeaza `VANZARI`**. `do_modifica` -(`omodificari.vc2:13934-13943`) trateaza explicit `id_lucrare` si `id_responsabil`. - -**Doua nuante care schimba proiectarea, nu doar inventarul:** -1. **Editarea e la nivel de LINIE de nota, nu de antet.** La emitere, cele patru analitice se scriu - uniform pe tot documentul, din variabilele de sesiune. Prin editorul lui #6, fiecare linie se poate - edita separat — deci un document poate ajunge cu **sectii diferite pe linii diferite**, stare - imposibila la emitere. Conteaza pentru orice raportare care presupune „un singur `ID_SECTIE` per - factura". -2. **Bug suspectat pe `sectie`** (`omodificari.vc2:13941`): `replace ... id_valuta with - loCauta.id_sectie` — pare sa scrie in `id_valuta` in loc de `id_sectie`. Daca e real, textul afisat - se schimba dar coloana nu. **E in perimetrul lui #6** — se semnaleaza, nu se repara aici. Pana la - verificare, „DA" pentru `ID_SECTIE` e un da cu rezerva. - -**Consecinta pentru decizia 9**, acum pe fapte: `but_modifica` **poate salva pe loc doar cei 14 -parametri**. Pentru grupurile B si C nu exista alta cale decat **regenerarea**, care le rescrie oricum -pe toate, fiind chiar drumul de emitere. Pentru grupul A exista o a treia cale, dar e a lui #6 si -lucreaza pe alt nivel (linie de nota). - -**Tabelul de rutare, completat (deciziile 25 si 26):** - -| Ce s-a schimbat | Cum se scrie | -|---|---| -| cei **14 parametri** (serie, numar, data, scadenta, ruta, delegat, masina, agent, `dataora_exp`, adresa facturare, text aditional, `listare_detaliata`, `tip_saft`, `efactura`) | **pe loc**, `modifica_date_factura` | -| explicatia si `taxcode` pe o linie | **pe loc**, `modifica_explicatie_articol` | -| cantitati, preturi, linii, discount, gestiune, cota TVA | **regenerare** | -| **grupul B** — tip document, valuta, zi curs, client, sursa, gestiune sursa, politica de preturi | **regenerare** (decizia 25); blocate in etapa I | -| **grupul C** — incasare | **regenerare** (decizia 25); blocate in etapa I | -| **grupul A** — venit/cheltuiala, sectie, responsabil, lucrare | **niciuna din #13** — read-only, editarea e a lui #6 (decizia 26) | -| nimic | nimic | - -### G. Stocul — de ce argumentul lui #6 nu inchide subiectul - -Verificarea de stoc **nu** e in `adauga_articol_factura`: acolo erorile sunt de configurare -("Nu a fost gasita cota de TVA", politica / contract lipsa — `PACK_FACTURARE:5125-5180`). -Descarcarea efectiva de gestiune se face la emitere, in `contabilizeaza_articol` -> -`descarca_gestiune` (`:7631+`). Plafonul pe cantitate pe care il vede utilizatorul azi vine din -**cursorul client** `crsarticole` (`frm_facturare_articole.do_adauga_articol`, -`ofacturare.vc2:12813-13086`) — cursor care in formularul unificat **nu mai exista**, pentru ca nu -se mai incarca in masa. - -Deci, pe #13: - -- in timpul compunerii **nu exista plafon** — coincide cu decizia deja luata de Marius pe #6 la - 08.08.2026 ("editarea unei facturi emise nu are plafon si nu verifica stocul"); -- la finalizare, daca **stergerea ruleaza inaintea reemiterii, in aceeasi tranzactie**, stocul - consumat de documentul initial e deja eliberat cand se descarca gestiunea pentru documentul nou. - -**Aici e riscul tehnic principal.** Azi stergerea si emiterea deschid fiecare propria tranzactie -(`do_deschide_tranzactie` / `do_inchide_tranzactie` in ambele cai). O tranzactie nu poate fi tinuta -deschisa peste minutele in care utilizatorul editeaza formularul. Ordinea corecta e deci: -**citire (fara tranzactie) -> editare -> o singura tranzactie la confirmare, care contine intai -stergerea, apoi scrierea.** Asta cere mutarea apelului de stergere in interiorul tranzactiei -deschise de `do_scrie_articole`, nu inaintea ei. - -Atentie si la `VANZARI_DETALII_TEMP`, care e GTT `ON COMMIT DELETE ROWS`: umplerea si consumul -trebuie sa ramana in aceeasi tranzactie — ceea ce e compatibil cu schema de mai sus, dar interzice -un `commit` intermediar dupa stergere. - -### H. Pretul rescris de server la reemitere - -`adauga_articol_factura` (`PACK_FACTURARE:4972-5268`) ramifica pe `V_OPT_FACTURARE` si, pe unele -ramuri, **re-deriva pretul, cota TVA si valuta din documentul sursa** (ex. `V_OPT_FACTURARE = 3`, -preturile de pe contract, `:5129-5168`), in timp ce pe ramura implicita valoarea venita din VFP -castiga (`V_PRET := V_PRET_TEMP`, `:5183-5186`). Planul #6 semnalase deja acelasi lucru. - -**Consecinta pentru #13:** pe facturile din contract (si posibil pe alte ramuri), pretul editat de -utilizator poate fi suprascris tacut la reemitere. De inventariat ramura cu ramura, si de decis daca -regenerarea trece un flag "preturile vin din formular, nu se re-deriva". **Nu se porneste -implementarea inainte de acest inventar** — e singurul punct din care poate iesi o factura reemisa -cu alte sume decat cele confirmate pe ecran. - -### I. Asezarea antetului (deciziile 8 si 9) - -**Doua grupuri vizibile, restul pliat.** Grupurile si etichetele reale, din inventar: - -| Grup | Controale | Note | -|---|---|---| -| Identitatea documentului | `Ct_clb_fdoc` („Tip document”: FACTURA / PROFORMA / BON FISCAL), `Clb_serie_act`, `Clb_nract`, `Clb_dataact`, `Clb_data_scadenta`, `Clb_zi_curs`, `Ct_clb_valuta` | scadenta e **dezactivata**, nu eliminata, cand `gnScadentaAutomata = 1` (`ofacturare.vc2:9713-9715`); ziua de curs se elimina pe retur (`:9717-9722`, tipurile 8 si 9 — nu si 24); valuta se elimina cand `in_valuta = 0` (`:9725-9728`) | -| Partener si sursa | `Ct_clb_nume_client`, `txtCodFiscal` + `But_verifica1` (ANAF), `txtSoldLei`, `Ct_clb_altele` (sursa), `Ct_clb_gestiune_init`, pe aviz `Ct_clb_politici_preturi` | codul fiscal si soldul sunt read-only si derivate | -| **Pliat** — analitice | `Ct_clb_venchelt`, `Ct_clb_sectie`, `Ct_clb_responsabil`, `Ct_clb_lucrare` | responsabilul si gestiunea sursa se elimina azi cand `gnScadereStoc = 0` sau tipul e 4/7/8/9/48/49 (`ofacturare.vc2:9646-9707`) | -| **Pliat** — alte date | cele patru grupuri din `frm_alte_date`, vezi B | | - -**Butonul de modificare a antetului (decizia 9).** Mecanismul **exista deja**, si e mai fin decat -credeam: `frm_modifica_factura` (`ofacturare_comun.vc2:5262-5774`) are **patru bife individuale** — -`chkSerieAct`, `chkNrAct`, `chkDataAct`, `chkDataScad` — fiecare deblocand exact textbox-ul ei -(`thisform.txtDataAct.Enabled = this.Value`, `:5758-5772`), si toate patru sunt ele insele inactive -daca documentul nu are numar (`this.chkSerieAct.Enabled = !EMPTY(NVL(poRec.numar_act,0))`, -`:5736-5750`). **Restul campurilor de antet nu sunt blocate deloc** — delegat, agent, masina, ruta, -adresa, dataora expediere, text aditional sunt mereu editabile. - -In formularul unificat (**decizia 9, forma finala — un singur buton comutator, fara nicio bifa**): - -- in bara panoului de antet sta **un singur `but_modifica`**; apasat, deblocheaza **tot antetul** — - si campurile „moi”, si cele patru de identitate — si **isi schimba imaginea in discheta lui - `but_salvare`**; documentul deschis pentru consultare ramane inert; -- **cele patru bife individuale dispar.** Nu se transpun in formularul unificat sub nicio forma: - butonul e singurul control de protectie a antetului; -- **si conditia lor se abandoneaza.** Azi bifele de serie si numar sunt ele insele inactive dupa - `!EMPTY(NVL(poRec.numar_act,0))` (`:5736-5750`); regula asta nu se mai transpune pe campuri. - Cu antetul deschis, **toate campurile de antet sunt editabile, fara exceptie si fara conditie de - stare**. E o pierdere deliberata de protectie, in schimbul unei singure reguli inteligibile: - antet blocat sau antet deschis, atat; -- a doua apasare **salveaza doar antetul**. E posibil pentru ca - `do_modifica` cheama azi **exclusiv** `pack_facturare.modifica_date_factura`, cu 15 parametri toti - de antet, si niciun apel spre articole, note sau `oscrie_in_fisiere` (`ofacturare_comun.vc2:4587-4630`). - Acopera cazul „vreau sa corectez doar delegatul”. - **Precizare, ca sa nu se citeasca gresit:** „nu atinge notele si rulajele” e adevarat **la nivel de - apel VFP**, nu si in Oracle. Corpul procedurii propaga seria, numarul si datele in `JV2007` si `RUL` - (`PACK_FACTURARE.pck:14428-14459`) — coloane de **identitate**, nu sume: nicio valoare nu se - recalculeaza. Pentru celelalte zece campuri, scrierea e strict in `VANZARI`. Vezi I-bis. -- **`Termina` ramane salvarea intregului document**, cu rutarea din G-bis. - -**Ce nu exista si trebuie scris:** containerele efectiv folosite in antet nu au un mecanism gata -facut de blocare. `ct_clb_cautare` are `do_activeaza` / `do_dezactiveaza` (`caut_ora.vc2:780-806`), -dar acestea doar ascund iconita de cautare, iar in `ofacturare_comun.vc2` nu sunt apelate nicaieri; -`clb_tx_simplu` (`lb_tx.vc2:551-585`) si `clb_serie_act` (`serii_numere.vc2:7`) **nu au deloc** -metode de activare. Singura reteta completa din biblioteca e `clb_tx_data.dezactiveaza()` / -`reactiveaza()` (`lb_tx.vc2:521-538`: `ReadOnly` + `TabStop` + butonul de calendar) — se -generalizeaza de la ea, nu se inventeaza alta. - -Decizia 5 ramane in picioare: **asezarea e identica** la introducere si la modificare; difera doar -starea antetului. - -### I-bis. Contractul lui `modifica_date_factura` (decizia 19) - -Raport complet: `docs\cercetare\modifica_date_factura_parametri.md`. Spec `PACK_FACTURARE.pck:921-935`, -corp `:14392-14462`, singurul apel VFP la `ofacturare_comun.vc2:4599-4614`. - -Procedura are **doua regimuri de scriere**, si diferenta dintre ele decide cum se cheama din -formularul unificat: - -| Parametri | Regim | Unde ajung | -|---|---|---| -| `V_ID_VANZARE` | doar `WHERE` | identitatea randului; sursa lui `lnIdFact` (`:14424-14426`) | -| `V_ID_RUTA`, `V_ID_DELEGAT`, `V_ID_AGENT`, `V_ID_MASINA`, `V_DATAORA_EXP`, `V_ID_FACTURARE`, `V_LISTARE_DETALIATA`, `V_TEXT_ADITIONAL`, `V_TIP_SAFT`, `V_EFACTURA` | **scrise neconditionat**, inclusiv cu `NULL` | `VANZARI`, randul curent (`:14414-14423`) | -| `V_DATA_ACT`, `V_DATA_SCAD`, `V_NUMAR_ACT`, `V_SERIE_ACT` | scrise **doar daca** sunt `NOT NULL` **si** difera de valoarea curenta | `VANZARI` + propagare in `DOCUMENTE`, `ACT`, `IREG_PARTENERI`, `JV2007`, `RUL` (si `RUL.DATAOUT`), toate **dupa `ID_FACT`**, nu dupa `ID_VANZARE` (`:14428-14459`) | - -**Consecinta 1 — apelul trimite intotdeauna antetul intreg.** Cele zece campuri din regimul -neconditionat se scriu cu ce primesc, deci un apel cu `poRec` completat partial **goleste** in baza -campurile netrimise. Butonul de antet nu poate trimite „doar ce s-a schimbat”: incarca antetul -curent, aplica modificarile peste el, trimite tot. - -**Consecinta 2 — cele patru campuri de identitate ating cinci tabele.** Propagarea dupa `ID_FACT` -inseamna ca schimbarea seriei sau a numarului dintr-un document atinge toate randurile legate de -acelasi `ID_FACT`. E acelasi mecanism pe care se sprijina S9 (reemiterea cu `ID_FACT` pastrat) si -acelasi motiv pentru care ordinea operatiilor conteaza acolo. - -**Consecinta 3 — un camp nou.** `V_EFACTURA` se scrie neconditionat dar nu are niciun control in -niciun formular; azi valoarea vine din `Scatter` sau e fortata `0` (`ofacturare_comun.vc2:4576`). -In antetul unificat primeste control, langa `V_TIP_SAFT` (ambele tin de raportare, nu de document). - -**Unde stau cele 14 in formularul unificat** (raportat la gruparea din I): cele patru de identitate -in antetul vizibil; ruta, delegatul, agentul, masina, `dataora_exp` si adresa de facturare in -sectiunea pliata, la „delegat si transport” / „adresa de facturare”; `text_aditional` si -`listare_detaliata` tot in pliat; `tip_saft` si `efactura` formeaza acolo un **grup nou, de -raportare**. Niciunul dintre cei 14 nu vine azi din `frm_alte_date` — clasa aceea nu e implicata -deloc in fluxul `do_modifica`, deci sectiunea B si I-bis nu se suprapun. - -### J. Butoanele de linie si adaugarea din sursa (decizia 13) - -**Cum sunt azi.** In `frm_facturare_articole` butoanele sunt imprastiate lateral, intre cele doua -griduri, si sunt iconite de 30x27 fara text: `But_modifica1` / `But_sterge1` la 773/61 si 811/61, -`But_reset1` la 303/295, `But_urmator1` la 343/348, `But_urmator_tot1` la 343/378, `But_retur` la -343/407 (`ofacturare.vc2:11192-11257`). **`But_urmator_tot1` — „adauga tot” — nu are nici caption -nici `ToolTipText`** (`:11257`); metoda lui, `do_adauga_tot` (`:13169-13198`), face SCAN peste -`crsarticole` si cheama `do_adauga_articol(.T.)` pe fiecare linie. - -**In prototip sunt deja unde trebuie**: `But_nou1` si `But_sterge1` la `Top = 175`, gridul la -`Top = 204` (`:15936`, `:15955`) — adica **deasupra tabelului**, exact cum cere decizia 13. -`But_nou1.Click -> do_adauga` (`:17118-17122`) e `APPEND BLANK` + focus in grid. - -**Ce lipseste: contractele.** `But_urmator_tot1` e vizibil pe `eProforma = 1`, `lCopiere`, tip 3 -(comanda), tip 4 (din avize), 21/28/42/47, 25, 8/9, 24 (`:15113-15245`). **Tipurile de contract -(2, 6, 26, 52) nu sunt in lista.** Deci „adauga tot” acopera deja avizele, dar nu contractele. - -**Alegerea selectiva — tiparul importului RORIS.** In ROAACNPRO, `cmdImport` („Import Roris & -Contracte”, `oacnpro.vc2:6894-6905`) face exact ce s-a cerut aici, si merita copiat ca structura: - -1. **buton activ conditionat** de document editabil **si** tip care are sursa - (`llFacturaEditabila and llRorisSauContracte`, `oacnpro.vc2:8117-8145`); -2. **o metoda unica de intrare** care ramifica pe tip (`do_executa` -> `factura_import`, - `proceduri_acnpro.prg:3151-3212`); -3. **dialog modal de selectie** cu coloana de bifat si criterii de cautare (`frm_tranzit`, coloana - `cAles` legata de `crsConvoaie.ales`, `oacnpro.vc2:14500-14515`, `:14801`), urmat de un al doilea - nivel de calcul / confirmare; -4. **populare aditiva** prin `INSERT INTO` in cursorul local de articole, fara nicio procedura Oracle - — pachetele intra abia la salvare (`proceduri_acnpro.prg:3331-3467`); -5. **protectie minimala la dublu-import**: butonul se dezactiveaza dupa un import reusit - (`oacnpro.vc2:7827-7834`), plus avertisment neblocant daca sursa a mai fost folosita - (`:14915-14930`). - -Ce **nu** se copiaza: sursele de date si calculele ACN (`ips_*`, tarife, ecluzari), formularele lor, -si valorile `poDate.cTip`. Ce trebuie facut mai bine: la zero rezultate, dialogul RORIS nu spune -nimic — butonul pare ca nu face nimic (`oacnpro.vc2:14898-14943`). - -**Bara propusa, deasupra gridului (decizia 13):** linie noua · sterge linia · detalii linie ‖ -**un singur buton „Adauga articole”**, care deschide un `xmenu()` cu optiunile potrivite sursei — -nu doua butoane separate. Tiparul e deja folosit in produs: `frm_facturi.but_modifica1` -> -`inainte_de_do_modifica` -> `xmenu("Modificare date factura;Editare factura (articole, cantitati, -preturi)")` (`ofacturare_comun.vc2:4925-4934`). - -| Sursa documentului | Optiunile din meniu | -|---|---| -| comanda | Adauga tot din comanda · Alege din comanda… · Cauta in lista de preturi… | -| contract | Adauga tot din contract · **Alege ratele de facturat…** · Cauta in lista de preturi… | -| avize | Adauga tot din avize · Alege avizele… · Alege liniile din avize… · Cauta in lista de preturi… | -| lista de preturi | Cauta in lista de preturi… · Retur de articole… | -| retur (tip 8, 9, 24) | Alege facturile de returnat… · Alege liniile de returnat… · Cauta in lista de preturi… | - -**Constanta meniului (deciziile 16 si 18): ultimele doua optiuni sunt aceleasi peste tot** — -**„Cauta in lista de preturi…”** (cu pret din politici) si **„Alege din nomenclator…”** (cu pret -tastat, ca „Alte servicii” din ROAAUTO, O-bis). Apar inclusiv pe documentele cu sursa, pe cele de -retur si pe cele deja emise care se modifica. Sursa umple documentul, nu il inchide. - -**Decizia 20 (Marius, 09.08.2026): din nomenclator se pot alege si articole gestionabile, si -negestionabile.** Nu se preia filtrul `in_stoc = 0` al lui ROAAUTO — acolo e o ocolire a subiectului -gestiunii, nu o regula de produs. Consecinta directa: pe documentele auto vor putea aparea linii care -descarca stoc langa linii care nu descarca, caz care azi nu exista nicaieri (O-bis, intrebarea 2). - -#### J-bis. Contul de venit al liniei — intrebarea lui Marius era corect pusa - -Doua cercetari, a doua o corecteaza pe prima. **Valabil e al doilea raport:** -`docs\cercetare\cont_venit_corespondente.md`. Primul (`cont_venit_articol_fara_politica.md`) ramane -util pentru contul de **gestiune**, dar concluzia lui pe venit e gresita si nu se citeaza. - -**Ce a gresit primul raport.** A cautat literalii `707` / `704` / `706` / `708` in `pack_facturare`, -nu i-a gasit (corect — nu sunt hardcodati nicaieri) si a conchis ca **nu exista cont de venit pe -linie**. Exista. E doar **indirect**, si sta chiar in `pack_facturare`, nu intr-un pachet de -contabilitate separat. - -**Sunt doua conturi pe aceeasi linie de vanzare, cu surse complet diferite:** - -| | Contul de **gestiune** | Contul de **venit** | -|---|---|---| -| Unde ajunge | `VANZARI_DETALII.CONT` (`varchar(4)`) | `SCC` in randul de nota contabila | -| De unde vine | `NOM_ARTICOLE.CONT` / `STOC.CONT` / `NOM_GESTIUNI.CONT` | `NOTE_CONTABILE.SCC` | -| Prin ce | alegerea lotului (`do_alege_stoc`) | **politica de pret** a articolului | -| Cine il consuma | `descarca_gestiune` (`ff_...:7485-7507`) | `scrie_nota` (`ff_...:7452-7476`) | - -Lantul care aduce venitul, in `pack_facturare.contabilizeaza_articol` (`ff_...:7182-7556`, cursorul -`cursor_articol` la `:7227-7280`): - -**`CRM_POLITICI_PRET_ART` -> `CRM_POLITICI_PRETURI.ID_NOTA` -> `CRM_NOTE_VANZARI.ID_SET` -> -`NOTE_CONTABILE.SCD` / `SCC`** - -Pentru o factura normala (`ntip <= 20`), `SCD` e contul debitor (creanta) si **`SCC` e contul de -venit efectiv** al liniei (`ff_...:7407-7437`). Nu e derivat din nimic: perechea SCD/SCC e **scrisa -manual de un contabil**, per sablon de nota, din ecranul „Configurare note contabile" -(`COMUN\ferestre\frm_config_note_contabile[2007].sc2`, clasa `onote_contabile.vc2:2401-2435`, -salvare la `:315-322`, `:486-493`). - -**Nu exista tabel de corespondenta 3xx -> 7xx.** Cautat in `SCRIPTURI_CLAR` sub toate numele -plauzibile — negativ. Ipoteza lui Marius era corecta ca **intentie** (exista un mecanism care leaga -articolul de contul de venit), dar mecanismul e **configurare manuala pe politica de pret**, nu -derivare automata din contul de stoc. - -**Consecinta care conteaza pentru #13 — si care valideaza intrebarea initiala a lui Marius.** Daca -contul de venit vine **prin politica de pret**, atunci un articol adaugat **direct din nomenclator, -fara politica** (exact ce cere decizia 16 / 20) nu are `ID_POL`, deci cursorul de mai sus — filtrat -`WHERE A.ID_POL = detalii_articol.id_pol` — nu intoarce niciun rand, si `SCD` / `SCC` raman `NULL` -(sunt `LEFT JOIN`-uri). **Intrebarea „cu ce cont de venit intra un articol fara politica de pret" era -deci exact intrebarea corecta.** - -#### J-ter. Raspunsul: nu „cu niciunul”, ci **eroare** — si nu exista precedent - -Verificat: `docs\cercetare\nota_contabila_fara_politica.md`. Doua rezultate, amandoua mai dure decat -se anticipase. - -**1. Codul nu scrie tacut un cont gol — refuza documentul.** `contabilizeaza_articol` cauta politica -articolului **inainte** de cursorul care aduce `SCC` (`ff_...:7286-7311`), si pe `NO_DATA_FOUND` -ridica `RAISE_APPLICATION_ERROR` cu **`FACT-024`** („Articolul … nu este definit in politica de -preturi …”). Cu `id_pol` `NULL`, `ID_POL = detalii_articol.id_pol` nu poate potrivi nimic, deci -**mereu** se intra pe exceptie. Cursorul cu `SCC` nu se mai deschide. Nu exista ramura de default, -nu exista fallback, si `scrie_nota` (`ff_...:12338-12570`) **nu are nicio validare pe cont** — dar -nici nu ajunge sa fie chemat. -Concluzie: o linie fara politica trecuta prin codul de azi **opreste tranzactia cu eroare**, nu produce -o nota incompleta. Din perspectiva datelor e vestea buna; din perspectiva lui #13, e blocantul dur. - -**2. „Alte servicii" din ROAAUTO NU e precedent** — premisa pe care se sprijinea decizia 18 cade. -Liniile acelea chiar ajung cu `id_pol` gol (confirmat: nici `crsalteserv`, nici `crsvanztemp`, nici -semnatura lui `adauga_articol_factura_deviz` nu au `id_pol`, iar `INSERT`-ul in -`VANZARI_DETALII_TEMP` nu include coloana). **Dar ele nu trec niciodata prin -`contabilizeaza_articol`**: fluxul ROAAUTO cheama `initializeaza_date_factura` -> -`adauga_articol_factura_deviz` -> `scrie_in_vanzari` -> `pack_auto.actualizeaza_deviz` -(`oproceduri_devize.prg:1211-1310`), iar `scrie_in_vanzari` (`ff_...:13497-13962`, citit cap-coada) -**nu genereaza nicio nota contabila de venit** — copiaza `ID_POL` (deci `NULL`) direct in -`VANZARI_DETALII` si actualizeaza totalurile. -Deci ROAAUTO nu demonstreaza ca articolele fara politica sunt tolerate; demonstreaza ca **exista o cale -paralela de facturare care ocoleste contabilizarea de venit prin `pack_facturare`**. Unde capata acele -documente contul de venit — daca il capata — **nu se vede in codul cercetat**; `actualizeaza_deviz` -presupune deja existenta unui rand in `ACT`, a carui sursa nu a fost gasita. - -**3. Apelantii sunt trei, si acopera fluxul principal** (lasat neverificat de raportul 2): -`scrie_factura2` (`ff_...:6150`) — factura normala si majoritatea tipurilor —, `scrie_factura_avize_retur` -(`:6867`) si `scrie_aviz_retur` (`:7149`). Nu exista al patrulea. - -**4. `NOM_ARTICOLE.CONT` ca sursa de venit: NU.** Grep pe `NOM_ARTICOLE.CONT` in tot pachetul — zero -potriviri. `V_SCC` vine exclusiv din `D.SCC` al lantului de politica. Varianta propusa de Marius nu -exista nici macar ca ramura moarta. - -**Ce inseamna asta pentru perimetru.** „Alege din nomenclator…" nu mai e o adaugare de UI peste un -mecanism existent: **cere modificare in `pack_facturare`**, adica in COMUN, cu impact peste toata -suita — sau evitarea completa a lui `contabilizeaza_articol`, ca la ROAAUTO, ceea ce inseamna document -fara nota de venit. **Aceasta e o schimbare de cost, nu un detaliu**, si se decide de Marius (vezi -intrebarea deschisa de mai jos). - -**`NOM_ARTICOLE.CONT` nu e restrans la clasa 3** — confirmat: `verific_cont` -(`COMUN\programe\oproceduri_comune.prg:2389-2415`) verifica doar ca acel cont exista in planul de -conturi al anului (`vplcont_sintetic`), fara filtru de clasa; nu exista `CHECK CONSTRAINT` pe coloana -si nici vreo ramificare pe `Left(cont,1)` aplicata articolelor. Deci un articol negestionabil **poate** -purta legitim un cont 6xx / 7xx, cum a spus Marius. **Dar codul de azi nu-l foloseste asa**: acel camp -merge in `VANZARI_DETALII.CONT` si de acolo la `descarca_gestiune`, niciodata la `SCC`. Ca sugestia lui -Marius sa devina comportament, ar trebui **cod nou in `pack_facturare`** — adica in COMUN, cu impact -peste toata suita. *Vezi intrebarea deschisa de mai jos.* - -**`ID_VENCHELT` / `NOM_VENIT_CHELTUIELI` nu e sursa contului.** Tabelul nu are coloana de cont; -`ID_VENCHELT` se rezolva separat (`NVL(nid_venchelt, NVL(A.ID_VENCHELT, C.ID_VENCHELT))`, -`ff_...:7205`) si pleaca la `scrie_nota` ca **parametru propriu**, alaturi de `SCD`/`SCC` — e o a -treia dimensiune analitica (centru de venituri/cheltuieli, pentru raportare), nu contul insusi. -Presupunerea din runda 7 ca „venitul vine din `Ct_clb_venchelt`" **se retrage**. - -De unde vine `CONT`, pe cele doua cai: - -| Cale | Sursa contului | Fallback | -|---|---|---| -| **gestionabil, cu stoc** — trece prin `do_alege_stoc` (`ofacturare.vc2:13239-13345`) | `STOC.CONT`, de pe lotul ales (`cursor_gestiuni_articol`) | **niciunul** — lot cu `CONT` gol ramane `NULL` | -| **gestionabil, fara stoc** (`RF_FACTURARE_FARA_STOC = 1`) | `NOM_GESTIUNI.CONT` -> ultimul `STOC.CONT` -> `'371'` hardcodat (`cursor_gestiuni_articol_stoc0`) | singurul loc din pachet cu cascada si default | -| **negestionabil** — `frm_articol_factura` direct (`:12871-12880`) | `NOM_ARTICOLE.CONT`, citit la cautarea articolului | **niciunul**; gol -> sentinela `'XXXX'` -> `NULL` la INSERT | - -`adauga_articol_factura` **nu recalculeaza** contul: il primeste ca parametru `V_CONT` si il scrie ca -atare. **Nu exista nicio exceptie pe `CONT`** in tot pachetul — spre deosebire de cota de TVA, unde -lipsa produce explicit `FACT-012` / `FACT-013` / `FACT-018`. O linie fara cont se scrie tacut. - -**Precedentul ROAAUTO nu ofera o regula, ci absenta ei.** „Alte servicii” (O-bis) trimite literal `''` -catre `adauga_articol_factura_deviz` (`oproceduri_devize.prg:1256`), care ajunge `NULL` in Oracle, -fara `NVL` si fara fallback. Liniile de articole reale din ROAAUTO **ajung deci cu `CONT = NULL`**. - -**Decizia 21 (Marius, 09.08.2026)** acopera **contul de gestiune**: cand `NOM_ARTICOLE.CONT` e gol pe -un articol negestionabil ales din nomenclator, se aplica un **fallback la un cont implicit** — nu se -accepta `NULL` ca la ROAAUTO, nu se refuza adaugarea. Precedentul de forma exista in pachet: -`cursor_gestiuni_articol_stoc0` cade in cascada pana la `'371'` hardcodat. Contul anume ramane de ales -la implementare. **Decizia 21 nu rezolva insa contul de venit** — sunt doua campuri diferite, cum arata -tabelul de mai sus; la momentul cand a fost luata, distinctia nu era inca stabilita. - -~~**Decizia 24: articolul ales din nomenclator primeste `id_pol`-ul unei politici de pret -implicite.**~~ **RETRASA in runda 8.** Argumentul ei — zero cod Oracle nou, COMUN neatins — ramane -valabil ca argument, dar pretul lui era o intrebare de configurare cu contabilul (care politica, cu ce -`SCC`, una sau mai multe) si un cont de venit uniform pentru orice articol adaugat asa. Marius a ales -in loc derivarea contului. **Nu se reintroduce.** *Vezi J-quater.* - -Ramane valabil de aici, si e important: **alegerea politicii de catre operator** a fost si ea respinsa -— muta costul pe fiecare adaugare si cere operatorului sa stie ce inseamna o nota contabila. - -**Ce se stie sigur pana atunci:** decizia 16 si decizia 20 **nu sunt anulate** de subiectul asta — pe -articolele **cu** politica de pret (lista de preturi, comanda, contract, retur) contul de venit vine -ca azi si nimic nu se schimba. Blocajul e strict pe ramura **„Alege din nomenclator…"**, adica pe -partea din S4g care adauga articole fara politica. - -**Unde e efectiv de lucru pentru asta:** doar pe **comanda**. Verificat -(`docs\cercetare\retur_si_lista_preturi.md`, B): pe contract, `crsarticole` contine deja lista de -preturi intreaga, deci adaugarea libera **merge azi**; pe comanda, `cursor_comanda` umple `crsarticole` -strict cu articolele comenzii (`ofacturare.prg:266-308`), deci nu merge. Restrictia **nu** e pe buton -si nu e la validare — e in continutul cursorului. Se rezolva ca la copiere: `APPEND FROM` peste -`crsarticole` cu rezultatul lui `cursor_preturi` (`ofacturare.prg:454-473`). - -#### J-quater. Deciziile 27 / 27-bis — contul de venit derivat, aplicat din VFP - -> **DEPASITA IN PARTE — de citit cu avertismentul asta in fata.** **Decizia 34** relaxeaza 27-bis -> (`contabilizeaza_articol` primeste parametru de cont) si **decizia 35** cere ca `pack_facturare` sa fie -> singurul cod de contare, si la emitere si la editare. Prin urmare: -> - **punctul 1** (de ce ROAACNPRO si contractul nu sunt contraexemple) si **punctul 2** (derivarea -> contului din `CORESP_CONT_VENCHELT` — regula deciziei 27) **raman valabile integral**; -> - **punctul 3, „reteta in 4 pasi"** — politica tehnica per `SCC`, interogarea inversa, -> `pack_preturi.adauga_politica_pret_art`, inserarea articolului in politica — e **ABANDONAT**. Se -> pastreaza doar ca trasabilitate a rationamentului. **Nu se implementeaza si nu se reargumenteaza.** -> - **cele trei variante** de la finalul sectiunii (reteta / nota pe antet / nota pe antet ca fallback) -> sunt **caduce**: alegerea a fost facuta prin decizia 34. -> -> Proiectarea in vigoare e parametrul deciziei 34 — `docs\cercetare\parametru_cont_contabilizeaza_articol.md`. - -Raport: `docs\cercetare\coresp_cont_venchelt.md`. Trei lucruri s-au stabilit, in ordinea in care conteaza. - -**1. Intrebarea lui Marius („in ROAACNPRO si pe factura din comanda / contract se adauga articole fara -politica — de ce nu si aici?") a primit raspuns: observatia e reala, dar niciunul din cele doua exemple -nu e un contraexemplu.** `FACT-024` ramane blocantul. - -- **ROAACNPRO nu foloseste deloc `contabilizeaza_articol`.** Salvarea facturii ACN - (`proceduri_acnpro.prg:3331-3467`) merge pe `initializeaza_date_factura` -> - **`adauga_articol_factura_deviz`** -> **`scrie_in_vanzari`** -> **`pack_acn.salveaza_regdoc`**. - Cautare directa dupa `contabilizeaza_articol` in acel fisier: **zero rezultate**. Nota contabila o - scrie o procedura proprie ACN, care nici nu primeste `id_pol` printre parametri. Acelasi tipar ca - ROAAUTO „Alte servicii" (J-ter, punctul 2): **o cale de facturare paralela**, nu o dovada ca articolele - fara politica sunt tolerate. -- **Pe contract, articolul „liber" nu e fara politica.** `crsarticole` e populat de - `pack_facturare.cursor_contract` / `cursor_preturi`, care **selecteaza explicit `A.ID_POL`** - (`ff_...:2162-2167`) si care ruleaza la fiecare apel `completare_politica_stoc` (`ff_...:2151`). - Deci fiecare rand din grila vine deja **cu o politica reala atasata**. „Adaugare libera pe contract" - inseamna „orice articol din lista de preturi, nu doar cele din contract" — nu „articol fara politica". - - **Diferenta reala e cursorul-sursa al grilei, nu tipul de document:** `caut_articol` - (`COMUN\programe\ocautare.prg:1636-1735`, cautarea in nomenclator) **nu are coloana `id_pol` in nicio - ramura**; `cursor_preturi` / `cursor_contract` o au, pentru ca pleaca de la politica, nu de la - nomenclator. Povestea #13 cere exact ce nu exista azi nicaieri. -- **Ce verifica de fapt `FACT-024`:** `SELECT COMPUS, ID_POL_ART FROM VCRM_POLITICI_PRET_ART WHERE - ID_ARTICOL = ... AND ID_POL = ...` (`ff_...:7278-7302`) — deci **apartenenta articolului la politica - de pe linie**. Ipoteza din runda 8 e **partial** confirmata: verificarea e de apartenenta, dar `id_pol` - **nu** vine din antet ca valoare unica — e parametru per linie, trimis de VFP. Doua cauze diferite - (`id_pol` gol; articol nemembru) produc aceeasi eroare, iar codul nu le distinge. - -**2. `CORESP_CONT_VENCHELT` exista, e populat si e folosit in productie — dar niciodata pentru -`CONT_VENIT`.** Toti consumatorii gasiti (`pack_vin`, `pack_devize`, `gestiune_pack_gest_import`, -rapoarte de gestiune) citesc `CONT_CHELT` sau `CONT_APROVIZIONARE`. Coloana `CONT_VENIT` **are date -reale** dar **zero consumatori**, nici Oracle nici VFP. Cheia de join e stabila si testata: pe `CONT`, cu -`STERS = 0`, prin `LEFT JOIN` — tiparul se copiaza identic. Deci regula lui Marius e implementabila; e -insa **un consumator nou**, nu o reteta deja rulata. - -**Masurat pe baza vie (10.08.2026, `MARIUSM_AUTO`) — `docs\cercetare\verif_baza_vie_cont_venit.md`:** -tabelul are **41 de randuri active**, din care exact **20 au `CONT_VENIT`** populat; restul 21 sunt -conturile de ajustari (`391`…`398`, toate cu `CONT_CHELT = 681`), care nu poarta marfa. **Niciun `CONT` -nu e duplicat**, deci pasul 1 al retetei e determinist — desi schema nu are unique pe `CONT`, doar `PK` -pe `ID_CCV`. Si, decisiv: **zero articole active cad pe un rand cu `CONT_VENIT` gol**, deci ramura -„cont derivat gol" se trateaza defensiv dar nu are cazuri reale. DDL-ul complet e in raport. - -**3. Calea VFP (decizia 27-bis) exista si e construita din piese functionale.** Patru pasi. - -> **Clarificare ceruta de Marius (10.08.2026) — de citit inainte de cei patru pasi.** Politica **nu e -> necesara ca sa se afle contul.** Regula deciziei 27 il determina complet, in VFP: corespondentele pe -> clasa 3, contul de pe articol daca e 6xx / 7xx, `704` altfel. Aia e **pasul 1**, si e verificata pe date. -> Politica e necesara **doar ca canal de transport** catre Oracle: `contabilizeaza_articol` **nu are -> parametru de cont de venit** — primeste `V_ID_POL` si deduce singura contul prin lantul politica → nota → -> `NOTE_CONTABILE.SCC`. Deci **pasii 2-4 nu decid nimic**, doar convertesc un cont pe care VFP il stie deja -> intr-o forma pe care pachetul o accepta. Indirectarea e consecinta deciziei **27-bis**, nu a regulii lui -> Marius: daca pachetul ar putea fi modificat, un parametru de cont ar face pasii 2-4 sa dispara. -> **INCHIS (runda 9 + decizia 35).** Cautarea canalului direct s-a terminat — -> `docs\cercetare\canal_cont_venit_fara_politica.md`. Verdict: canalul **exista** (`actactan` / `tact` → -> `oscrie_in_fisiere` → `pack_contafin.SCRIE_IN_ACT`), dar e cablat pe **editarea unei note deja scrise**, -> nu pe emitere; emiterea trece exclusiv prin `scrie_factura2` → `contabilizeaza_articol`. Pistele -> `V_CONT` (e contul de gestiune), setterele de sesiune, `ID_VENCHELT` si `GetAnaliticByGrupUtilizatori` -> sunt **toate NU**. -> **Si nu se foloseste oricum:** decizia 35 il respinge explicit in #13, ca sa nu existe doua coduri de -> contare. Reteta in 4 pasi cade prin **decizia 34** (parametru de cont in pachet), nu prin canal. - -1. **VFP calculeaza `SCC`-ul** dupa regula deciziei 27: `CORESP_CONT_VENCHELT.CONT_VENIT` pe contul de - gestiune al liniei (articol gestionabil), `NOM_ARTICOLE.CONT` daca e 6xx / 7xx, altfel `'704'`. -2. **VFP cauta o politica a carei nota are deja acel `SCC`** — interogarea inversa pe acelasi lant: - `CRM_POLITICI_PRETURI -> CRM_NOTE_VANZARI -> NOTE_CONTABILE`, `WHERE NC.SCC = :cont_calculat`. - Interogare **noua**, dar pe coloane si relatii deja confirmate. - **Corectat pe baza vie:** legatura nu e politica -> nota, ci politica -> **set de note** — - `CRM_POLITICI_PRETURI.ID_NOTA` -> `CRM_NOTE_VANZARI.ID_SET` -> `NOTE_CONTABILE.ID_SET`. Pe cele 7 note - de vanzari active fiecare set are **un** rand, dar in tabel exista seturi cu pana la **30** de randuri, - deci „nota politicii" e bine definita doar cat timp seturile de vanzari rămân cu un rand. - **Si mai important: filtrarea pe `SCC` singur nu e suficienta** — vezi „Ce nu e gratuit" mai jos. -3. **VFP se asigura ca articolul e membru al acelei politici** — RPC **existent** - `pack_preturi.adauga_politica_pret_art`, deja folosit exact asa (verifica intai, insereaza daca - lipseste) in `ofacturare.vc2:15551-15587`, la modificarea listei de preturi de la NIR. **`pack_preturi` - nu e `pack_facturare`** — nu se atinge nimic din pachetul comun de facturare. -4. **VFP trimite acel `id_pol`** in `adauga_articol_factura` (`V_ID_POL` e parametru de intrare explicit, - `ff_...:4989-5015`). `contabilizeaza_articol` ruleaza **neschimbata**. - -**Modelul „politica tehnica, configurata o data, populata automat" exista deja in pachet:** -`completare_politica_stoc` (`ff_...:2093-2120`) face `MERGE` in `CRM_POLITICI_PRET_ART` pentru toate -articolele `IN_STOC = 1` sub politica `pack_facturare.nid_politica_stoc`, alimentata din optiunea globala -`gnId_pol_pret_stoc` (`ofacturare.vc2:21733-21753`). Nu e direct reutilizabila — are o singura nota -pentru toate articolele, iar decizia 27 cere pana la sase conturi diferite — dar arata ca tiparul e -acceptat in produs. - -**Calea VFP e mai completa decat modificarea pachetului**, nu doar mai ieftina: politica reala aduce si -`CU_TVA`, `IN_VALUTA`, `EXPLICATIE`, `ASCD`, `ASCC` din nota configurata. Un fallback in -`contabilizeaza_articol` ar da doar `SCC`; `CU_TVA` si `IN_VALUTA` **nu au nicio sursa alternativa** in -afara lui `NOTE_CONTABILE`, deci ar trebui hardcodate — cu risc de calcul gresit al bazei si al TVA. - -**Ce nu e gratuit, si trebuie stiut inainte de S4g:** - -- **Pasul 2 depinde de configurarea fiecarui client — deci nu se poate proiecta pe acoperire.** - Decizia lui Marius, 10.08.2026: **datele din Dev nu sunt baza de proiectare.** Fiecare client isi - defineste propriile politici de pret, fiecare cu nota ei de vanzare salvata pe un `ID_SET`. Pe schema - de dezvoltare masurarea a dat `704` cu 19 politici valabile, `707` cu 4, `7015` cu 1, si zero pentru - `711` / `702` / `703` / `7018` — **dar aceste numere nu se folosesc ca argument**, pe o baza de client - distributia poate fi complet alta. Ce se pastreaza din masurare e strict **structural** (forma lantului, - absenta unique-ului pe `CONT`, bug-ul de set multi-rand) plus un fapt negativ: **afirmatia rundei 8 ca - „pentru `704` nu exista nicio nota configurata" nu se susține ca regula** — pe Dev exista patru, deci - pasul „se creeaza nota pentru 704" nu poate fi pus in plan ca obligatoriu. - **Consecinta de proiectare:** reteta nu are voie sa presupuna ca gaseste o politica pentru `SCC`-ul - calculat. Ori garanteaza singura existenta a ceea ce foloseste (politica tehnica creata de produs), ori - are o cale care **nu trece prin politica deloc** — vezi mai jos. -- **`PTVA` de pe nota e inofensiv — verificat pe cod, nu presupus.** `cursor_articol` - (`PACK_FACTURARE:7218-7271`) **nu selecteaza `D.PTVA`**; cota folosita efectiv vine din - `detalii_articol.proc_tvav * 100 - 100` (`:7464`), adica din articol / document. Deci o nota cu - `PTVA = 5` pe un articol cu 21% **nu produce nimic** — coloana e moarta pentru aceasta procedura. - Ingrijorarea initiala („criteriul trebuie sa fie `(SCC, PTVA)`") **se retrage**. - `IN_VALUTA = 1` de pe nota pe un document in lei nu da nici eroare, nici suma greșita — doar populeaza - redundant `ACT_TEMP.SUMA_VAL` la curs 1 (`scrie_nota:12391-12404`, `:12492-12507`): inconsistenta - cosmetica, nu contabila. `ASCD` / `ASCC` nule au fallback automat prin - `GetAnaliticByGrupUtilizatori` (`:7409-7428`), iar `ID_PARTD` / `ID_PARTC` **nu se citesc de pe nota** - deloc — vin din contul si din contextul documentului (`:12510-12530`). -- **In schimb, `ID_SET` cu mai multe randuri dubleaza venitul SI descarcarea de gestiune.** Asta e - riscul real, si e mai grav decat cel presupus. `cursor_articol` face `LEFT JOIN NOTE_CONTABILE D ON - C.ID_SET = D.ID_SET` **fara `ROWNUM`, fara agregare, fara filtru**, iar bucla care il consuma - (`:7393-7542`) executa `scrie_nota` **si** `descarca_gestiune` **o data pentru fiecare rand al - setului, cu aceeasi cantitate si acelasi pret intreg de fiecare data** — nu impartite. Nu exista - nicio logica de distributie: `ORDINE` nu apare in tot pachetul. Deci pasul 2 nu trebuie doar sa - gaseasca un rand cu `SCC`-ul potrivit, ci **sa garanteze ca setul ales are exact un rand**. Cele 7 - note de vanzari active au, azi, exact un rand fiecare — dar 30 din cele 40 de seturi din baza au mai - multe, cu maxim 30. **Conditia care face reteta sigura e o coincidenta a datelor curente, nu o - garanție a codului.** Sursa: `docs\cercetare\s10_pret_rederivat.md`, „Completare: nota contabila a - politicii", punctul 1. -- **Pasul 3 poluează o lista de preturi de producție.** Politica **este** lista de preturi: candidatele - reale contin `STOC PRODUSE` cu **6310** articole si `LISTA PRETURI LEI` cu **909**, plus liste cu nume de - client si de sezon (`CHIOSC`, `SERVICE AUTO`, `S7 CRACIUN`…). Un articol adaugat ad-hoc pe factura ar - aparea de acum in acea lista, cu pretul cu care a fost facturat o data. **Deci pasul 2 nu trebuie sa - caute orice politica potrivita, ci o politica tehnica dedicata, una per `SCC`** — modelul - `gnId_pol_pret_stoc` extins de la una la sapte. Cele **sase** politici cu 0 articole din baza vie arata - ca o politica goala e o stare acceptata de produs. -- **Ambiguitatea celor 19 politici pe `704` e de lista de preturi, nu de rezultat contabil** — majoritatea - trimit la aceeasi `id_nota = 1`, deci toate dau `704` / `PTVA = 21` / lei. Cu atat mai mult, criteriul de - departajare corect e „care lista de preturi accept sa murdaresc", iar raspunsul e „niciuna dintre cele - existente". -- **Politica tehnica devine vizibila utilizatorului** in `caut_politici_curente_util()` — aceeasi functie - ca la cautarea normala. Ori se filtreaza explicit (flag / prefix dedicat), ori se accepta riscul ca - cineva sa o aleaga din greseala pe o factura normala. **De decis in S4g.** -- **`id_pol` devine populat** acolo unde azi ar fi gol. Nu s-a gasit cod care sa presupuna „`id_pol` gol = - articol fara politica", dar nici nu s-a cautat exhaustiv. -- **Doua politici active nu au `ID_NOTA`** (`32 HOTEL TAXE`, `33 HOTEL CAZARE`) — `id_pol` valid, nota - absenta. Ce face `contabilizeaza_articol` in acest caz e intrebare deschisa, trimisa pe cod. E o gaura - care **exista deja azi**, independenta de #13. - -**RASPUNS LA DECIZIA 32 — reteta RAMANE. `docs\cercetare\idpol_comanda_contract.md`.** - -Intrebarea era: cum alege programul nota contabila la factura din comanda, care „nu are politica de pret"? -**Raspuns: are. Structural nu poate sa nu aiba.** `COMENZI_ELEMENTE.ID_POL` e **`NOT NULL`** la nivel de -schema (constrangerea `SYS_C0015376`, plus `FK_COMENZI_ELEMENTE_002`) — verificat direct: **0 din 7108 -randuri** au `ID_POL` nul sau zero, 9 politici distincte in uz, toate cu nota atasata. Deci nu exista nicio -ruta alternativa catre nota, si **nu exista contradictie cu concluzia rundei 8**: pe comanda cazul „`id_pol` -gol" e imposibil, nu doar neintalnit. Ce percepe operatorul ca „n-am ales nicio politica" e faptul ca -politica se alege **o data pe comanda** — manual sau moștenita dintr-o optiune legata de tipul comenzii — -si de atunci fiecare articol adaugat o primeste automat, din `com_vpreturi_utilizator`, view care filtreaza -`id_pol IS NOT NULL` la sursa. Pe **contract** tiparul e acelasi, dar cheia stocata e -`CTR_ARTICOLE.ID_POL_ART` — FK direct la randul din `CRM_POLITICI_PRET_ART`, **nullable**, cu cazuri reale. - -**De ce `FACT-024` nu se declanseaza pe comanda / contract:** nu pentru ca ar exista un fallback, ci pentru -ca **articolul nu e ales niciodata din nomenclator** — e ales dintr-o lista deja filtrata pe politica, deci -apartenenta e garantata **prin construcția listei**, nu prin validare. Exact aici e diferenta cu #13, unde -`caut_articol` ofera nomenclatorul intreg. - -**Deci refolosirea nu simplifica nimic:** mecanismul de jos — RPC de inserare + apartenenta garantata — e -**exact** ce propune reteta in 4 pasi. Ce ar simplifica cu adevarat („ia politica curenta fixa si pune -articolul in ea") e **decizia 24, retrasa**, si motivul retragerii sta: o politica unica da **un singur** -cont de venit oricarui articol, indiferent de natura lui — opusul deciziei 27. Comanda si contractul nu -raspund niciodata la intrebarea „**care** politica pentru **acest** articol", pentru ca la ele politica vine -gata aleasa, o data, de operator. - -**CORECTIE, a doua tura a aceluiasi agent — premisa lui Marius ERA corecta, dar pe contract, nu pe comanda.** -Exista o ruta reala catre nota contabila **complet in afara lui `id_pol`**: la **contractele cu rate / -scadentar** (`OPT_FACTURARE IN (1, 2)`), o functie separata — **`contabilizeaza_rata`** — scrie nota direct -din **`CONTRACTE.ID_NOTA`**, fara sa treaca prin `CRM_POLITICI_PRETURI`. Confirmat pe cod de agent si pe o -factura reala (`id_vanzare 360`: `SCD = 4111` / `SCC = 704` in `ACT`, pe o linie fara articol si fara -politica). **Structura confirmata independent de sesiunea principala:** `CONTRACTE.ID_NOTA` exista, e -nullable, si e **populat pe 19 din 242 de contracte**, rezolvand la note reale — `1 NOTA 1` → `SCC 704` -(5 contracte), `2 DISCOUNT` → `SCC 4111` (5), `5 VANZARE MARFA` → `SCC 707` (1). Deci nota **pe antetul -documentului** nu e o ipoteza, e un mecanism in uz. - -**Ce inseamna asta pentru #13, si de ce e o decizie de produs, nu una tehnica.** Tiparul „nota pe antet" -ar fi **mai simplu decat reteta in 4 pasi**: n-ar mai fi nevoie nici de gasirea politicii, nici de -inserarea articolului in ea, nici de politica tehnica — antetul ar purta nota, iar liniile ad-hoc ar -moșteni-o. **Dar cere cod nou in `pack_facturare`**, pentru ca `contabilizeaza_rata` trateaza rate, nu -articole de factura. Adica **incalca exact decizia 27-bis** („`pack_facturare` nu se atinge"). -**Deci alegerea e a lui Marius, intre trei variante:** - -1. **reteta in 4 pasi** (decizia 27-bis respectata, `pack_facturare` neatins, dar cere politica tehnica per - `SCC` si o interogare inversa noua); -2. **nota pe antet ca la `contabilizeaza_rata`** (mult mai simplu si mai curat conceptual, dar **relaxeaza - decizia 27-bis** — cod nou in pachetul comun intregii suite); -3. **nota pe antet doar ca fallback** pentru articolele fara politica, lasand restul neschimbat. - -**Nu se implementeaza nimic pana la aceasta alegere.** Detalii: `idpol_comanda_contract.md`, sectiunea E-bis. - -**Doua observatii din date, gasite la confirmarea verdictului:** - -- **Precedentul cerut de decizia 27 exista deja in producție, si e mai bun decat `gnId_pol_pret_stoc`.** - Politica `7 DISCOUNT` apare pe **870 de linii de comanda** si trimite la nota `2 DISCOUNT` - (`SCD = 667`, `SCC = 4111`). E o politica de pret folosita **exclusiv ca mecanism de rutare contabila**, - nu ca lista comerciala de preturi. Deci „politica tehnica, dedicata unui cont, populata automat" nu e o - invenție a lui #13 — **produsul o face deja**, pentru discount. Argument direct pentru politica tehnica - per `SCC`. -- **Garantia prin construcția listei nu e etanșă in date: 37 de linii de comanda au un articol care NU e - membru al politicii de pe linie.** **VERIFICAT (runda 10)** — - `docs\cercetare\linii_comanda_articol_nemembru.md`. **Decizia 32 NU se redeschide in sensul periculos: - nu exista cale de cod care sa ocoleasca `FACT-024`.** Patru rezultate: - - **Cifra 37 se reconfirma, dar numitorul din plan era gresit** — `6868` era alt numitor; cel corect - pentru linii cu `ID_POL NOT NULL` e **7108**. - - **Premisa „37 de linii ar cadea la facturare" era partial gresita.** Un rand cu `CANTITATE < 0` **nu - dovedeste ca linia a fost facturata**: singurul loc care il insereaza e `inchide_comanda` - (`PACK_FACTURARE:5769-5820`), care scrie *diferenta ramasa* la **inchiderea** comenzii, chiar si cand - nimic nu s-a facturat pe acea linie. **34 din 35 de linii negative n-au nicio factura in spate, nici - macar stearsa** — comenzile au fost inchise, nu facturate. - - **Una singura chiar a ajuns intr-o factura emisa** — comanda `497`, articolul `4294507522` - („VOUCHER DISCOUNT") pe politica `7` („DISCOUNT"), in factura `ID_VANZARE = 1028`, `20.03.2026`. Si - codul **chiar a rulat** `contabilizeaza_articol` pentru ea: apelul e in ramura `ELSE` a lui - `CASE pack_facturare.ntip` din `scrie_factura2`, care exclude doar transferurile, avizele de custodie - si facturile cu rate — tipul 3 cade in `ELSE`. Deci **fie articolul era membru al politicii la - 20.03.2026 si a fost scos ulterior, fie verificarea a fost ocolita altfel; dovada pentru a doua - varianta nu exista.** - - **Cauza e NEDETERMINABILA DIN DATE, structural.** `CRM_POLITICI_PRET_ART` **n-are coloana `STERS`** si - n-are tabel de istoric — stergerile sunt **fizice, fara urma**, deci nu se poate reconstitui starea de - la 20.03.2026. Coloanele de audit ale facturii (`VANZARI.ID_UTILS` / `DATAORAS` si cele de pe cele - doua linii) sunt **toate NULL**, deci nici o editare ulterioara nu s-a inregistrat. Indiciu de tipar, - nu dovada: aceeasi pereche articol-politica apare pe **35 de comenzi diferite**, ceea ce sustine o - **curatare de catalog** mai degraba decat 35 de greseli independente de introducere. - - **Ce ramane, si cum se formuleaza:** pentru celelalte 36 de linii, „zero facturi in date" **nu - demonstreaza ca nu se pot factura** — demonstreaza doar ca nu s-a intamplat. Iar cu starea de azi a lui - `CRM_POLITICI_PRET_ART`, toate cele 37 **ar** cadea cu `FACT-024` daca ar trece acum prin - `contabilizeaza_articol` (verificat pe view-ul `VCRM_POLITICI_PRET_ART`, care n-are filtru propriu, deci - vede exact aceleasi perechi ca tabelul). **Concluzia pentru #13 e neschimbata:** garantia prin - constructia listei e reala cat timp catalogul nu se editeaza sub documente deja emise. - -**Verificarile pe baza vie sunt facute** (10.08.2026) — `docs\cercetare\verif_baza_vie_cont_venit.md`: -interogarea inversa pe fiecare `SCC` candidat, DDL-ul lui `CORESP_CONT_VENCHELT`, distributia articolelor -pe ramurile deciziei 27 (6125 din 6430 pe ramura gestionabila), efectele colaterale ale pasului 3 si trei -fragilitati de structura. **Rămâne deschisa doar partea de cod**, listata mai sus. - -### K. Discountul pe linie (decizia 14) - -Verificat de doua ori, pe formularul real si pe DDL. Raspunsul la intrebarea „e doar procentual?” e -**nu, si nici doar valoric — sunt amandoua, dar in alta fereastra**. - -**Intrarea pentru operator exista deja, in dialogul de articol.** `frm_articol_factura` -(`ofacturare.vc2:1108-2659`), deschis la fiecare adaugare de articol, are trei campuri legate: -`Clb_procent_discount` (procent), `Clb_discount_unitar` (suma, in lei sau valuta) si -`Clb_discountctva` (varianta cu TVA). Toate cheama -`frm_articol_factura.do_calculeaza_discount(valoare, tip)` (`:1874-1976`) cu `tip = 1` procent, -`tip = 2` lei, `tip = 3` valuta (`:2602-2649`) — tastezi in oricare, se recalculeaza celelalte. -Rezultatul intra in `poArticol.discount_unitar` si variantele lui, apoi in `crsfactura` prin -`Replace` (`:12956-12957`). - -**Se stocheaza o singura coloana, si e valoare.** `VANZARI_DETALII.DISCOUNT_UNITAR NUMBER(22,6)` -(`ff_2024_06_13_02_COMUN_FACTURARE.sql:3`; simetric pe `VANZARI_DETALII_TEMP` la `:9` si pe -`CRM_POLITICI_PRET_ART` la `:16` — **trei tabele diferite, nu aceeasi coloana de trei ori**). -**Nu exista coloana de procent** pe `VANZARI_DETALII` — procentul e doar mod de introducere. -Parametru Oracle: `V_DISCOUNT_UNITAR` in `adauga_articol_factura` -(`ff_2026_03_23_04_COMUN_PACK_FACTURARE.sql:4986`), scazut din pretul fara TVA inainte de TVA -(`:15794-15847`). - -**In grid, azi, comportamentul e inconsecvent.** Pe factura in lei ramane `cDiscountCTva`, care e -`ReadOnly = .T.` (`ofacturare.vc2:12311-12317`); pe factura in valuta ramane `cVdiscountftva`, care -**e editabila** — dar nu are niciun `Valid` / `InteractiveChange`, deci o editare directa in celula -schimba valoarea bruta fara sa refaca totalurile. Excluderea reciproca e la `:15269-15278`. - -**Corectie fata de runda 3:** afirmasem ca prototipul are deja procent pe linie, ca argument pentru -pornirea de la el. **Fals.** `procdisc` apare o singura data in tot fisierul, ca `ControlSource` de -coloana (`:16721`), fara niciun `Replace` care sa-l populeze si fara corespondent Oracle — e schela -moarta. (Restul aparitiilor de „procdisc” sunt variabila de optiune `gnMemProcDisc`, altceva.) -Argumentele pentru prototip raman celelalte doua: controalele de antet si butoanele deasupra gridului. - -**Ce ramane de facut:** - -- cele doua campuri din dialogul de articol devin **coloane in grid** — procent si valoare unitara — - cu **acelasi calcul reciproc** din `do_calculeaza_discount`, mutat pe evenimentele coloanelor; -- se repara astfel si inconsecventa de mai sus: aceleasi validari in ambele monede; -- se pastreaza excluderea pe `in_valuta`; -- modelul de date **nu se atinge**; -- **de verificat separat, blocant**: daca `discount_unitar` apare pe rapoartele `.frx` si daca e - transmis in eFactura (UBL) si SAF-T. Daca nu apare pe hartie, documentul tiparit nu explica totalul. - -#### K-bis. Discountul de DOCUMENT — cota, explicatia si atribuirea pe cote - -Cercetare terminata, verificata la sursa (doua runde, a doua a corectat concluzii ale primei — -vezi rezervele de mai jos). Detaliu complet: `docs\cercetare\discount_document_cota_tva.md`. - -**Cota discountului de document = cota MAXIMA de pe factura, ca regula implicita.** Nimeni n-o -alege: nu exista control in formular si nicio coloana pe `VANZARI` pentru ea. -`Calculate Max(proc_tvav)` — `COMUN\programe\oproceduri_facturare.prg:1387`, -`COMUN\clase\ofacturare_comun.vc2:4495` si `:7294`, `COMUN\programe\ofacturare_stoc.prg:582`. -Discountul intra ca **pseudo-linie negativa** printr-un rand-sentinela `Replicate('Z',20)` -(`COMUN\programe\ofacturare_comun.prg:1886-1897`), care devine linia „Discount NN.NN % Factura" -(`:1273-1392`). - -**Explicatia nu exista ca notiune — e o constanta.** In XML, `AllowanceChargeReason="Discount"` -si `ReasonCode="95"` sunt hardcodate (`COMUN\programe\xmlefactura.prg:774-776`), la fel -`currencyID="RON"` (`:778`). Textul construit in VFP („Discount 10.00 % Factura") nu ajunge in -XML. - -**Regula e implementata de doua ori, independent — nu intr-un singur loc.** In VFP (mai sus) si -in PL/SQL: `PACK_FACTURARE.recalculeaza_totaluri_vanzari`, -`MAX(ROUND(discount*(a1.proc_tvav-1),...)) as DISC_TVA_RON` — -`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:16081-16092`, -scris in `VANZARI.DISCOUNT_TVA` si de acolo in `TOTAL_TVA` / `TOTAL_CU_TVA` (`:16214-16227`). -**Pentru #13: doua locuri de schimbat, nu unul** — daca se schimba doar unul, `VANZARI.TOTAL_TVA` -si TVA-ul din XML diverg pe facturile cu cote mixte. - -**Defectul dovedit e de atribuire fiscala, nu de validare.** Pe cote mixte, tot TVA-ul -discountului se scade din cota maxima. Exemplu: 1000 lei la 21% + 1000 lei la 11%, discount de -document 10% (200 lei) → se declara 10 lei TVA in minus, mereu in acelasi sens (TVA colectat -subdeclarat). Aceeasi valoare gresita ajunge si in nota contabila si in jurnalul de TVA. Caz-limita -real, masurat: daca liniile de la cota maxima sunt o mica parte din total, `TaxSubtotal` iese cu -baza negativa (`TaxableAmount = -410.00`). - -**Ipoteza bazei negative — verdict nuantat, asa cum a iesit din verificare, nu simplificat.** -Temerea: pseudo-linia, avand `id_jtva_coloana = 0`, iese din `LEFT JOIN` cu `coloana_jv` NULL si -formeaza grup propriu in `C_TVA_FACTURA`. **Infirmata pe factura obisnuita** — masurat headless, -`IIF()` din VFP intoarce ramura falsa, nu `.NULL.`, cand conditia e nula, deci pseudo-linia se -contopeste corect. **Confirmata insa pe facturile scutite / taxare inversa / intracomunitare**, -unde liniile reale au `scutit = 1` si `expltva` completat iar pseudo-linia are `0` si gol: apar -doua grupuri, deci un `TaxSubtotal` in plus cu baza negativa si categoria `Z`, iar -`agettipcota(1)` (`xmlefactura.prg:782-786`) devine ambiguu intre `E` si `Z`. - -**Nu blocheaza factura — validat, nu presupus.** Validat offline cu DUKIntegrator, 6 scenarii, -inclusiv un control negativ care chiar iese cu erori (`BR-CO-13` + `BR-CO-15`) — deci cele „ok" -inseamna ceva. Trece inclusiv un caz cu `TaxableAmount = -400.00`. Regulile pe categorii -(`BR-S-08` / `BR-E-08` / `BR-Z-08`) se satisfac trivial: aritmetica inchide, modelarea fiscala e -cea gresita, si asta niciun schematron nu prinde. **Rezerva de scris explicit:** validatorul local -e din 2022; validatorul ANAF online n-a fost apelat (interdictie). - -**`currencyID="RON"` hardcodat e neconformitate reala si activa, dar fara dovada ca ar cauza -respingere.** Dovedit pe fisier: XML-ul in EUR de la VENDING_MASTER e identic pe SHA256 cu cel din -`TRIMISE` (deci exact ce a plecat la ANAF) si a fost acceptat. Respingerea din `ERORI` e a unei -incarcari anterioare, pentru alte reguli. - -**Nu s-a putut proba pe date reale, si asta ramane netransat.** In baza accesibila sunt 3 facturi -cu discount de document (toate cu o singura cota, anterioare eFacturii) si 34 cu cote mixte fara -discount — intersectia e goala. Cele 4 XML-uri de productie cu alocare de document sunt, dupa trei -indicii independente, discounturi pe linie, nu de document. Absenta din date nu e dovada (regula -casei). Ramane nedovedit prin rulare: reproducerea end-to-end prin program a scenariului „factura -scutita + discount de document". - -**Recomandarea cercetarii pentru #13:** repartizare proportionala automata pe cote, **nu se cere -utilizatorului cota**. Discountul de document nu are o cota proprie de ales — natura lui de TVA e -determinata de liniile pe care le reduce; a pune operatorul sa aleaga inseamna a-i cere o decizie -fiscala pe care datele o dau deja. Pentru explicatie: un singur camp text optional pe document, -folosit ca `AllowanceChargeReason` in locul constantei (`ReasonCode` ramane `95`). - -**Doua completari obligatorii daca se implementeaza, altfel reparatia e degeaba:** -- bucatile de discount trebuie sa primeasca **`id_jtva_coloana` al grupului pe care il reduc**, nu - doar cota — cheia de grupare are cinci campuri si `expltva` se completeaza tot prin - `id_jtva_coloana`. O implementare care seteaza doar `proc_tva` lasa grupul orfan exact unde e azi; -- **eFactura nu are nevoie de nicio modificare** — `xmlefactura.prg:758-792` grupeaza deja - `GROUP BY proc_tva` si emite cate un `AllowanceCharge` per cota. - -Doua consecinte vizibile daca se implementeaza: factura tiparita va arata N randuri „Discount X % -Factura" in loc de unul, si nota contabila primeste TVA-ul discountului spart pe cote. - -**Decizia a fost luata — decizia 59 (runda 16): repartizare proportionala pe cote.** Vezi sectiunea -„Decizia 59" de la deciziile rundei 16, cu toate consecintele de implementare. - -**Cele doua puncte ramase s-au inchis si ele, tot in runda 16.** Campul text optional de motiv — -**decizia 61**: da, se adauga (`AllowanceChargeReason`, `ReasonCode` ramane `95`), cu stocare noua -pe `VANZARI` si un control nou in formular. Asezarea lui s-a inchis in runda 17 — **decizia 66**: sta in banda de totaluri, langa discount. Retroactivi- -tatea la relistare/retrimitere — **decizia 62**: restrictia de modificare e strict `EsteInEFactura`, -fara legatura cu nicio data; ramane insa deschis, separat, cazul relistarii (nu editarii) unei -facturi vechi deja trimise, vezi decizia 62. - -### M. Valuta si data cursului (decizia 15) - -**Cele doua concepte exista in cod, distinct** — decizia 15 nu introduce o distinctie noua, o face -vizibila: - -- **(a) factura in valuta**: `poDate.in_valuta` / `Curs` / `multiplicator` / `id_valuta`, un singur - curs pentru tot documentul; -- **(b) articol cu pret in valuta pe document in lei**: `poArticol.tip_valuta = 1`, proprietate a - politicii de pret, independenta de `in_valuta`. Conversia se face **in VFP**, cu cursul zilei: - `poArticol.pretftva = Round(poArticol.pretftva_val * poArticol.Curs / poArticol.multiplicator, gnPPretV)` - (`ofacturare.vc2:1989`, `:2017`, `:2953`). Cursurile zilei se incarca exact pe ramura „document in - lei”: `If poDate.in_valuta = 1 ... Else citeste_cursuri_zi(poDate.zi_curs)` (`ofacturare.prg:407-418`). - -**In baza, exact cum ai descris:** `VANZARI_DETALII` **nu are coloana `CURS`**; pastreaza `PRET` (in -lei, valoarea de facturare) plus `PRETD` si `ID_VALUTAD` ca urma a pretului original in valuta. -Cursurile ajung in `VANZARI_CURSURI`, scrise neconditionat la emitere pentru **orice** valuta straina -aparuta pe linii — deci si pe o factura in lei -(`PACK_FACTURARE.sql:14491-14501`, `scrie_cursuri`). - -**`in_valuta` nu e o alegere, e o proprietate a tipului de document.** Se seteaza o singura data, in -`Init`: `If Inlist(tnIdSet,223,225,226,227,271) OR INLIST(m.tnTip, 5,6,7,9,10,52) -> .in_valuta = 1` -(`ofacturare_comun.prg:248-250`), si nu se mai schimba. Selectorul de valuta e chiar eliminat din -formular cand `in_valuta = 0` (`ofacturare.vc2:9725-9728`), deci operatorul nici nu-l poate alege. - -**Problema semnalata, confirmata:** `Clb_zi_curs` e eliminat **doar** pe retur (tip 8, 9), la -`ofacturare.vc2:9717-9722` — si niciodata in functie de valuta, desi blocul imediat urmator, -`:9725-9728`, face exact asta pentru selectorul de valuta. Deci **azi campul apare si pe facturi in -lei**, unde de multe ori nu are ce cauta. Validarea e la randul ei asimetrica: `frm_date_factura` -cere ziua cursului doar cand `in_valuta = 1` (`:9484`), dar `frm_date_aviz_lucrare` o cere -**neconditionat** (`:8076`). - -**Regula propusa:** campul apare cand tipul nu e retur **si** (documentul e in valuta **sau** exista -pe document macar un articol cu pret in valuta). - -**Unificarea face regula posibila.** Azi cazul (b) nu se poate sti la deschiderea antetului — se -afla abia dupa incarcarea articolelor, cand antetul e deja inchis si eliberat (`Release -ofrmceredate`, `ofacturare.prg:248`). In formularul unificat antetul si gridul sunt in aceeasi -fereastra, deci campul poate aparea in clipa in care intra pe grid primul articol cu pret in valuta. -**Din acelasi motiv dispare si mecanismul lui #16**: bug-ul de focus si de renumerotare apare la -revenirea din `frm_curs` intr-un antet care tocmai s-a inchis — in formularul unificat nu mai exista -un antet inchis la care sa te intorci. `frm_curs` e in `COMUN\clase\onom_curs.vc2:537`, deschis din -`vizualizeaza_curs` (`oproceduri_curs.prg:8-42`) ca recuperare la eroarea Oracle 20005 -(`ofacturare.prg:313-317`). - -**Capcana:** `poDate.zi_curs` se trimite **neconditionat** ca prim parametru catre `cursor_preturi` / -`cursor_articole_k` / `cursor_gestiune` / `cursor_lucrare` (`ofacturare.prg:272-303`), inclusiv pe -tip 1. Ascunderea campului nu inseamna golirea valorii — valoarea implicita (data documentului) -trebuie sa ramana. Iar pe avizul de lucrare, ascunderea fara repararea validarii de la `:8076` ar -bloca finalizarea antetului cerand un camp care nu mai e pe ecran. - -### N. Returul din facturi anterioare (decizia 17) - -Rapoarte: `docs\cercetare\factura_retur_document.md` (mecanismul principal) si -`retur_si_lista_preturi.md`, A (al doilea mecanism). - -**Sunt doua fluxuri de cod independente**, care ating aceeasi clasa de formular prin metode si -proceduri Oracle diferite. Nu au niciun punct comun in afara conventiei de semn a cantitatii. - -#### N.1 Factura de retur ca document (tipurile 8, 9 — si avizul de retur, 24) - -**Asta e mecanismul pe care il descrie Marius, si functioneaza deja cap-coada.** - -- **Intrare:** tile-ul de pe ecranul de facturare (`ofundal_facturare.vc2:882-884`) -> `politica.mpr` - -> `factureaza(8)` / `factureaza(9)` (`Meniuri\politica.mn2:14-15, 45-46`). -- **Alegerea facturilor, la nivel de document:** `frm_date_factura.do_cauta_facturi` - (`ofacturare.vc2:9173-9212`) cheama `caut_facturi_multiple_client` - (`oproceduri_facturare.prg:2091-2121`) — browse cu **selectie multipla**, titlu „Alegeti facturile - (mouse-click pe numar sau apasati SPACE)”. Filtrare pe **client** (obligatoriu ales inainte) si - **valuta**; sursele exclud tipurile de retur, deci nu se face retur dintr-un retur. Id-urile alese - se aduna intr-un CSV in `poDate.listaid`, numerele in `poDate.descriere`, iar antetul primeste - automat textul „RETUR FACTURA …”. Fara alegere nu se poate continua (`:9523-9526`). -- **Popularea liniilor:** `pack_facturare.cursor_retur` -> `cursor_retur_document` - (`ff_2026_08_06_10_COMUN_PACK_FACTURARE.sql:3943-3956`, `:3958-4071`) selecteaza direct din - `VANZARI_DETALII` liniile facturilor alese si le pune in acelasi `crsarticole` pe care celelalte - tipuri il umplu cu lista de preturi (`ofacturare.prg:306-311`). **Confirmat exact ce spunea Marius:** - `ID_GESTIUNE` vine din linia originala (`:4034`, `:4055`), `PRET_ACHIZITIE` la fel, **neschimbat** - (`:4035`, `:4056`), iar pretul de vanzare se recalculeaza pe curs doar daca moneda nu e nationala - (`:4016-4030`). `GESTIONABIL` e `NVL2(A1.ID_GESTIUNE,1,0)` — gestionabil doar daca originalul avea - gestiune. -- **Ce se poate face manual:** **stergerea liniilor aduse merge** si cantitatea redevine disponibila - in cursorul sursa (`do_sterge`, `ofacturare.vc2:14608-14693`, ramura de retur la `:14658-14659`); - **returul partial merge** (`do_verifica_articol`, `:14743-14754`, fata de maximul calculat pe - server). -- **Avizul de retur (24)** foloseste acelasi `cursor_retur` si aceleasi ramuri; difera doar antetul — - `nIdTipDoc = 6` si `frm_date_aviz` in loc de `frm_date_factura` (`ofacturare.prg:194-195`, `:225`). - -**Singurul gol real, si e acelasi ca la comanda:** pe un document de retur **nu se poate adauga o -linie din afara facturilor sursa**, pentru ca `crsarticole` **este** rezultatul lui `cursor_retur` -(`do_adauga_articol` ia articolul mereu din el, `ofacturare.vc2:12843-12851`). Nu e o interdictie -explicita, e continutul cursorului — exact cauza de la comanda (J). Se rezolva la fel, prin -`APPEND FROM`, si abia asta face reala decizia 16 pe documentele de retur. - -#### N.2 Retur de articole intr-o factura de vanzare normala (`But_retur`) - -Butonul (`cmd_butoane.vc2:324-338`, `caction = do_retur`) e vizibil **doar** pe tipurile 1, 5, 7, 10 -(`ofacturare.vc2:15122-15127`) — pe un document de retur nu exista deloc. Aici factura sursa se alege -**per articol**, la fiecare linie: `caut_facturi_multiple_client_articol` -(`oproceduri_facturare.prg:2124-2159`), filtrat suplimentar pe articolul curent. Perechile -`ID_ARTICOL:ID_VANZARE` se acumuleaza in `thisform.cListaIdArticoleRetur` (`:12888`) si pleaca ca -`poDate.listaid` (`:13977-13979`). Gestiunea se alege prin -`pack_facturare.cursor_gestiuni_articol_retur`, nu vine din factura sursa — **diferenta de fond fata -de N.1**. - -**Legatura linie-de-retur -> linie originala: neverificat** pe schema. In VFP nu exista coloana de -linie sursa; la nivel de antet exista `poDate.nid_vanzare_retur` (`ofacturare.vc2:14448`, `:14509`). -Conteaza doar pentru **afisare** — daca formularul poate arata din ce factura vine fiecare linie. - -#### N.3 Ce se schimba in formularul unificat - -- **N.1 se muta ca atare.** Alegerea facturilor devine o optiune in meniul butonului de adaugare - („Alege facturile de returnat…”), cu acelasi dialog, aceleasi filtre si aceeasi populare din - `cursor_retur`. **Nu se reproiecteaza nimic din ce merge**, si in special nu se atinge preluarea - gestiunii si a pretului de achizitie din facturile originale. -- **N.2 capata si alegerea la nivel de document**, dupa modelul lui N.1, pastrand calea per-articol - pentru cazul cu un singur articol. -- **Lista de preturi devine disponibila si pe documentele de retur** (decizia 16), prin acelasi - `APPEND FROM` ca la comanda. Consecinta de proiectat: pe acelasi document vor coexista linii - aduse din facturi sursa (cu gestiune si pret de achizitie mostenite) si linii libere, care nu au - factura sursa. - **Decizia 22 (Marius, 09.08.2026): linia de retur fara factura originala e permisa, ca pe orice alt - document.** Returul nu face exceptie de la regula deciziei 16 — sursa umple documentul, nu il - inchide, si nici nu apare doar „pentru corectii”. Consecintele de proiectat, acum ferme: - - **gestiunea si pretul de achizitie nu se pot mosteni** pe o linie libera, pentru ca nu exista linie - originala. Se aleg, ca la N.2 (`cursor_gestiuni_articol_retur`), nu se preiau ca la N.1; - - **maximul returnabil calculat pe server nu se aplica** unei linii fara factura sursa — nu exista - cantitate originala fata de care sa se limiteze; - - afisarea „din ce factura vine linia” trebuie sa suporte si valoarea goala (vezi mai sus, legatura - linie-de-retur -> linie originala, inca neverificata pe schema). - -*De pastrat cum e:* excluderea returului dintr-un retur, filtrarea pe client si valuta, maximul -returnabil calculat pe server, si mostenirea gestiunii si a pretului de achizitie. Nu se ating. - -### O. Facturile din ROAAUTO (decizia 18) - -Raport: `docs\cercetare\roaauto_facturi.md`. - -> **Precizare la descrierea liniilor de mai jos.** „Liniile unei facturi ROAAUTO nu sunt articole” e -> adevarat doar pentru liniile **generate din deviz**. Langa ele pot sta articole reale, adaugate -> prin mecanismul **„Alte servicii”** — vezi O-bis, verificat pe cod -> (`docs\cercetare\roaauto_articole_lista_preturi.md`). Restul sectiunii ramane valabil: tipul `-12`, -> drumul comun prin `PACK_FACTURARE`, faptul ca editorul lui #6 le vede, si blocarea modificarii -> dupa facturare — ultima **reconfirmata**. - -**Ce se confirma.** ROAAUTO nu are cale proprie spre baza: `factureaza_deviz` -(`ROAAUTO\Programe\oproceduri_devize.prg:846`) cheama acelasi `PACK_FACTURARE` partajat — -`initializeaza_date_factura` (`:1211`), `adauga_articol_factura_deviz` (`:1240`), `oscrie_in_fisiere` -(`:1269`), `scrie_in_vanzari` (`:1302`) — deci liniile trec prin acelasi `VANZARI_DETALII_TEMP` si -ajung in acelasi `VANZARI_DETALII` ca orice factura ROA. Tipul documentului e **`-12`** -(`:922`). Editorul lui #6 le vede deja, fara cod de recunoastere a sursei, verificat pe date reale -(`id_vanzare = 1047`). **Deci partea de „le vede” e gratuita.** - -**Ce nu se confirma — si asta schimba dimensiunea deciziei.** „Se pot modifica ulterior” **nu e -adevarat azi, in niciun produs**: - -- in ROAAUTO, formularul de devize isi dezactiveaza butonul de modificare de indata ce comanda are - numar de factura (`oviz_devize.vc2:4536`); singura actiune post-facturare gasita e re-listarea - (`oproceduri_devize.prg:1516`), care nu scrie nimic. Niciun apel din ROAAUTO catre - `modifica_date_factura`; -- in ROAFACTURARE, gridul de articole din editorul nou (`frm_modific2024`, `COMUN\clase\omodificari.vc2`) - e **read-only prin design**, la nivel de grid si pe fiecare `Text1`. - -Deci decizia 18 nu extinde o cale existenta, **creeaza o capacitate care nu exista nicaieri**. - -**Cum arata liniile.** `factureaza_deviz` agrega sumele devizului si insereaza cate o linie sintetica -per categorie, cu `id_articol` **negativ**: `-100000` MANOPERA (`:965-967`), `-100003` MATERIALE -(`:947-949`), `-100001` discount manopera, `-100005` / `-100006` avans si stornare avans, -`-100007` / `-100008` inspectie tehnica si spalare (`:936-1027`). Optional, cu -`gnAUTOIdArticolReparatii` setat, toate se cumuleaza intr-o singura linie cu un articol real -(`:989-1004`). Langa ele pot sta **articole reale**, prin „Alte servicii” — vezi O-bis. - -### O-bis. „Alte servicii” — cum se adauga azi articole reale in ROAAUTO - -Raport: `docs\cercetare\roaauto_articole_lista_preturi.md`. **Asta e mecanismul de refolosit.** - -- **Unde:** formularul `frm_incasare_finala` (`ROAAUTO\Clase\oviz_devize.vc2:5435-7143`), aratat **la - emiterea facturii finale** (`do_factureaza_final`, `:2569`) — nu pe devizul propriu-zis. -- **Sursa articolelor:** `cauta_nom_articole` pe `vnom_articole_toate` - (`COMUN\programe\ocautare.prg:1781-1823`), filtrat `in_stoc = 0 and in_crm = 1` si cu id-urile - sintetice excluse (`oviz_devize.vc2:6539-6580`). **Nomenclatorul brut, si numai articole fara - stoc** — nu lista de preturi, nu politici. -- **Pretul se tasteaza manual.** `do_adauga` nu completeaza pretul si cantitatea; operatorul le pune - in grid, iar `do_modifica_alteserv` recalculeaza valoarea (`:6692-6698`, `:7064-7070`). Nu exista - preluare automata de pret pe calea asta. -- **Liniile raman separate.** Cumularea in „REPARATII AUTO” include doar id-urile sintetice - (`oproceduri_devize.prg:999-1012`); randurile din `crsalteserv` se insereaza dupa, cu `id_articol` - real, si ajung ca atare in `VANZARI_DETALII_TEMP` (`:1036-1041`, `:1240-1257`). -- **Gestiune: tot zero.** `id_gestiune` nu e completat de niciun `INSERT` si pleaca `0` spre Oracle - (`:1201-1206`, `:1255`) — consistent cu filtrul `in_stoc = 0`. Deci **nici articolele reale din - ROAAUTO nu descarca gestiune**. Diferenta fata de liniile sintetice e doar `id_articol`. -- **Stergere si modificare:** exista, dar **doar cat timp formularul e deschis, inainte de emitere** - (`do_sterge`, `:6730-6741`). -- **Contul pleaca gol.** `crsvanztemp` are coloana `Cont c(4)`, dar `INSERT`-ul care il umple nu o - include (`oproceduri_devize.prg:1190-1206`); apelul trimite literal `''` (`:1256`), care ajunge - `NULL` in `VANZARI_DETALII_TEMP.CONT`, fara `NVL` si fara fallback. Deci **liniile de articole reale - din ROAAUTO se scriu azi fara cont** de gestiune, tacut. Vezi J-bis. -- **Si fara politica de pret — dar pe alta cale decat credeam.** `id_pol` lipseste peste tot pe firul - asta (nu e nici in `crsalteserv`, nici in `crsvanztemp`, nici in semnatura lui - `adauga_articol_factura_deviz`). **Motivul pentru care asta nu produce `FACT-024`** e ca fluxul - ROAAUTO **nu trece prin `contabilizeaza_articol`**: merge pe `scrie_in_vanzari`, care nu genereaza - nicio nota de venit. Vezi **J-ter**. - -> **Corectie importanta la temeiul deciziei 18.** Ideea ca „«Alte servicii» face deja jumatate din ce -> cerem, deci se generalizeaza” **nu se sustine**. Mecanismul acela functioneaza tocmai pentru ca -> ocoleste contabilizarea de venit; generalizat pe fluxul ROAFACTURARE, unde `contabilizeaza_articol` -> **este** apelata (`scrie_factura2`, `ff_...:6150`), s-ar lovi de `FACT-024`. Ce ramane valabil din -> O-bis e descrierea UI (articole reale langa linii sintetice, pret tastat, fara gestiune) — nu si -> concluzia ca partea grea e deja rezolvata. - -**Ce inseamna asta pentru decizia 18.** Doua lucruri, in directii opuse: - -- **usureaza** partea de model: un articol real langa linii sintetice **nu e o noutate**, e deja - cazul normal al facturilor auto. Intrebarea „cum coexista” are deja un raspuns in produs; -- **nu rezolva** cerinta: mecanismul traieste in formularul de emitere al ROAAUTO si moare odata cu - el. Ce cere Marius e aceeasi capacitate **la modificare**, care nu exista nici acolo, nici aici. - -**Intrebarile ramase, in ordinea in care blocheaza:** - -1. ~~**Ce se intampla cu totalul devizului**~~ — **RASPUNS (runda 7)**, raport - `docs\cercetare\pack_auto_actualizeaza_deviz.md`. Premisa intrebarii era gresita: **nu exista niciun - `SUM` peste `VANZARI_DETALII`, pentru ca `PACK_AUTO` nu citeste deloc `VANZARI` / - `VANZARI_DETALII`** — zero potriviri pe `VANZARI` in tot pachetul (1804 linii). - `actualizeaza_deviz` (`ff_2026_03_31_02_AUTO_PACK_AUTO.sql:692-733`) face trei lucruri, toate pe - structura proprie ROAAUTO: scrie cota TVA pe `DEV_ORDL`, stampileaza `ID_FACT` pe `RUL`, si — doar - pe anumite `id_set` — pe `NOM_LUCRARI`. E o legatura **deviz -> document**, nu un recalcul - **document -> total deviz**. Nu e nici macar specifica facturarii: aceleasi trei apeluri o - folosesc si la inchiderea de productie si de regie, care scriu note contabile, nu facturi. - **Deci o linie noua pe `tip = -12` nu poate dezechilibra nimic pe partea Oracle**, si nu e nevoie - de niciun apel suplimentar catre ROAAUTO. Precedentul „Alte servicii” confirma pe date live: - liniile reale coexista de mult cu cele sintetice, fara ca `actualizeaza_deviz` sa faca distinctie. - **Ce ramane, si e de alt fel:** o **desincronizare de afisare**. Totalul devizului aratat in - ROAAUTO se calculeaza din `RUL` si din cursoarele de facturare (`oviz_devize.vc2:7816-7823`), - **niciodata** din `VANZARI_DETALII` — deci nu va arata linia adaugata, nici azi, nici dupa #13. - In schimb **relistarea o va arata**: `relisteaza_factura_deviz` citeste `fact_vfacturi_detalii` - (`oproceduri_devize.prg:1564`), un view neconditionat peste `VANZARI_DETALII` - (`ff_2024_06_04_01_COMUN_FACTURARE.sql:8-94`). Factura retiparita si ecranul de deviz vor spune - lucruri diferite — **DECIS (decizia 28): e acceptabil.** Cerinta lui Marius e ca **MANOPERA si - MATERIALE sa fie conform devizului**; restul articolelor pot exista pe factura fara sa apara in - ecranul de deviz. Nu se cere cod nou in ROAAUTO si nu se mai pune intrebarea. -2. **Gestiunea, pentru articole cu stoc. DECIS (decizia 20):** se admit si articolele gestionabile. - ROAAUTO ocoleste subiectul filtrand `in_stoc = 0`; filtrul acela **nu se preia**. Deci pe - documentele auto vor coexista linii care descarca stoc cu linii care nu descarca — caz nou, de - proiectat, nu de evitat. -3. **Cine ramane proprietarul documentului** dupa ce ROAFACTURARE ii adauga o linie — mai poate - ROAAUTO sa-l relisteze corect (`relisteaza_factura_deviz` citeste din `fact_vfacturi`)? - -**Reconfirmat:** dupa emitere nu exista cale de intoarcere in ROAAUTO. `verifica_stornare` -(`oviz_devize.vc2:4513-4514`) e o metoda **goala**; `do_storneaza_avans` priveste doar avansul. -Singurul instrument gasit asupra unei facturi emise e stergerea totala (soft-delete) prin ecranul -generic din COMUN — nu o editare. - -**Secventierea — reevaluata dupa raport (runda 7).** Marius a cerut ca `pack_auto` sa fie cercetat -inainte de orice estimare (decizia 23), si a avut dreptate: raspunsul **rastoarna recomandarea -initiala**. Motivul pentru care decizia 18 urma sa se livreze ultima era riscul tehnic de a scrie pe -un produs pe care #13 nu-l controleaza. **Acel risc nu exista** — `PACK_AUTO` nu citeste `VANZARI` / -`VANZARI_DETALII` deloc, deci nu are ce sa se dezechilibreze (vezi intrebarea 1 de mai sus). - -Ce ramane nu mai e risc tehnic, ci **o singura intrebare de produs**: -1. ~~e acceptabil ca ecranul de deviz sa nu arate linia adaugata din #13~~ — **RASPUNS, decizia 28: - da, e acceptabil.** Conditia e alta: MANOPERA si MATERIALE conform devizului. -2. cine ramane proprietarul documentului (intrebarea 3 de mai jos) — nu s-a schimbat. - -**Decizia 18 nu mai trebuie sa fie ultima din motive tehnice.** Ordinea de lucru din S4g ramane insa -valabila din alt motiv, mai bun: se face intai pe documentele ROAFACTURARE, unde scrierea e a -noastra, pentru ca acolo se aseaza mecanismul; `tip = -12` vine dupa, ca **aplicare**, nu ca risc. - -**Perimetru:** gridul read-only din `frm_modific2024` si `ofacturare_editare.prg` sunt ale lui **#6**. -Deschiderea lui la scriere nu se face din #13 fara intelegere explicita — un singur scriitor pe fisier. - -### L. Observatii colaterale, gasite in timpul verificarii - -Nu fac parte din #13 si nu se repara aici — se semnaleaza ca sa nu se piarda. - -0-ter. **(runda 9) O nota de vanzari cu set multi-rand inregistreaza venitul si descarca gestiunea de N - ori.** In `pack_facturare.contabilizeaza_articol`, `cursor_articol` face `LEFT JOIN NOTE_CONTABILE D ON - C.ID_SET = D.ID_SET` **fara `ROWNUM`, fara agregare, fara filtru** - (`ff_2026_08_09_01_...:7218-7271`), iar bucla care il consuma (`:7393-7542`) executa `scrie_nota` - **si** `descarca_gestiune` **o data pentru fiecare rand al setului, cu aceeasi cantitate si acelasi - pret intreg** — nu impartite. Nu exista nicio logica de distributie: `ORDINE` nu apare in tot pachetul. - Azi nu se manifesta pentru ca toate cele 7 note de vanzari active au exact un rand pe set — dar **30 - din cele 40 de seturi din baza au mai multe, cu maxim 30 de randuri**. Deci e o bomba armata de - configurare: cine ataseaza unei politici de pret o nota cu set multi-rand obtine **dublare de venit si - dublare de descarcare de gestiune**, silentios. **E in `PACK_FACTURARE`, adica in COMUN** — se - semnaleaza, nu se repara din #13. Conteaza direct pentru reteta din J-quater: vezi acolo. - Sursa: `docs\cercetare\s10_pret_rederivat.md`, „Completare", p. 1 + `verif_baza_vie_cont_venit.md` p. 6. -0-quater. **(runda 9) Politica de pret fara `ID_NOTA` nu are nicio plasa de siguranta.** Spre deosebire de - articolul lipsa din politica (`FACT-024`), aici nu exista cod de eroare: lantul de `LEFT JOIN` din - `cursor_articol` supravietuieste inelului lipsa, cursorul intoarce **un rand cu totul `NULL`**, iar - `scrie_nota` insereaza in `ACT_TEMP` un rand cu conturile de debit si credit **nule**. Daca `ACT_TEMP` - are `NOT NULL` pe ele, iese un `ORA-01400` generic in loc de un mesaj `FACT-0xx` — **neverificat**, - DDL-ul lui `ACT_TEMP` nu e in export. Starea exista in baza vie **azi**: politicile active `32 HOTEL - TAXE` si `33 HOTEL CAZARE` n-au `ID_NOTA`. Independent de #13. -0-bis. **(runda 7) `do_modifica` pare sa scrie `id_valuta` in loc de `id_sectie`.** In - `frm_modific2024.do_modifica` (`COMUN\clase\omodificari.vc2:13941`), ramura - `CASE m.lcControl = 'sectie'` face `replace sectie with loCauta.sectie, id_valuta with - loCauta.id_sectie`. Daca e ce pare, textul afisat al sectiei se schimba dar coloana `id_sectie` - nu — si, in plus, se strica `id_valuta`. **Necitit pe date, doar pe cod.** Fisierul e in - **perimetrul lui #6** (`omodificari.vcx` a intrat in proiect prin `97d1613`) — se semnaleaza acolo, - nu se repara de aici. Conteaza pentru #13 pentru ca sustine partial verdictul „`ID_SECTIE` e - editabil": teoretic da, practic poate nu. -0. **(runda 7) Handler-ul lui `FACT-024` se auto-saboteaza cand `id_pol` e `NULL`.** In - `pack_facturare.contabilizeaza_articol` (`ff_2026_08_06_10_COMUN_PACK_FACTURARE.sql:7286-7311`), - ramura `WHEN NO_DATA_FOUND` construieste mesajul cu inca doua `SELECT ... INTO`, dintre care al - doilea e `FROM CRM_POLITICI_PRETURI WHERE id_pol = detalii_articol.id_pol`. Cu `id_pol` `NULL`, - **si acel `SELECT` da `NO_DATA_FOUND`**, iar in handler nu exista al doilea nivel de tratare — deci - utilizatorul primeste un `ORA-01403` generic in loc de mesajul `FACT-024` formatat. Articolul - apucase sa fie rezolvat, deci informatia nu se pierde de tot, dar diagnosticul e mult mai prost. - **E in `PACK_FACTURARE`, adica in COMUN** — se semnaleaza, nu se repara din #13. Devine relevant - direct daca se alege varianta 3 din J-bis. -1. **`do_copiaza`, ramura de avize: variabila gresita.** La `ofacturare_comun.vc2:3702-3703`, ramura - `CASE INLIST(loFactura.tip, T21, T24, T26, T30, ...)` scrie `lnTip = T22` in loc de - `loFactura.tip = T22`, deci degradarea de tip **nu se aplica** pe acea ramura. **Fisierul e in - perimetrul lui #6** — nu se atinge de aici; efectul nu a fost verificat pe date. -2. **„Proforma -> factura” merge prin omisiune.** Copierea nu propaga `nIdTipDoc` pentru ca linia e - comentata (`ofacturare_comun.prg:370`), deci copia unei proforme porneste implicit pe FACTURA. - Functioneaza, dar ca efect secundar, nu ca ramura dedicata. Daca #13 atinge copierea, merita - transformat in intentie explicita. -3. **Asimetria de resetare a globalelor** — vezi „Canalul de precompletare”, mai sus. -4. **Modificarea in bloc pare sa goleasca campuri pe care nu le-ai atins.** Pe selectie multipla - (`lnNrInreg > 1`), `do_modifica` face `Scatter Name poRec Memo Blank` si reseteaza explicit ruta, - delegatul, agentul, masina, `dataora_exp`, `id_facturare`, `listare_detaliata`, `tip_saft`, - `text_aditional` si `efactura` (`ofacturare_comun.vc2:4565-4580`). Cum acesti zece parametri se - scriu **neconditionat** in `VANZARI` (I-bis), un `SCAN` peste facturile alese pare sa scrie valorile - goale pe **fiecare** dintre ele — deci schimbarea rutei pe trei facturi ar sterge delegatul si - textul aditional de pe toate trei. **De confirmat pe date inainte de a fi numit bug**: e posibil ca - fluxul sa presupuna ca operatorul completeaza tot ce vrea propagat. **Fisierul e in perimetrul lui - #6** — se semnaleaza, nu se repara de aici. Conteaza si pentru #13: formularul unificat lucreaza - pe un singur document si trebuie sa trimita antetul intreg (I-bis, consecinta 1), deci nu - mosteneste problema. - -**L.4 (runda 11). Numarul de POS nu se dezaloca la Renunt.** In `frm_alte_date`, la anulare se -dezaloca numarul de chitanta (16) si cel de bon fiscal (3), dar **nu si cel de POS (26)** — -`docs\cercetare\s3b_alte_date_analitice.md`, sectiunea 9. Bug preexistent, in productie, **nu introdus -de #13**. Conteaza pentru S3b pentru ca „`actualizeaza_tipincasare` se muta ca atare" **l-ar propaga -in formularul unificat**. Se repara in trecere sau se lasa ca azi si se preia separat — punct de decis, -vezi lista de la finalul rundei 11. - ---- - -## Stories - -Ordinea e strict pe risc: unificarea intai (livrabil de sine statator, util si fara regenerare), -regenerarea peste ea. - -## Metoda de executie (Marius, runda 14) — obligatorie, nu recomandare - -**Planul asta e un document de proiectare, nu un plan de executie. La implementare se SPARGE PE -STORIES, si fiecare story se executa ca livrare de sine statatoare.** Nu se porneste implementarea pe -tot #13 deodata, nu se acumuleaza modificari nelivrate peste mai multe povesti, si nu se trece la -povestea urmatoare cu cea precedenta netestata si nerevizuita. - -### Ce inseamna „spart pe stories" - -- **O story = o unitate de livrare.** Are perimetru propriu de fisiere, criteriu „gata cand" propriu - (fiecare story de mai jos il are deja scris) si se incheie cu diff + review + commit propriu. -- **Inainte de prima linie de cod dintr-o story**, se scrie o **nota de executie** in `docs\` care - transpune proiectarea in pasi concreti: fisierele atinse, metodele atinse, ordinea editarilor, si ce - se testeaza dupa fiecare. Proiectarea spune *ce* si *de ce*; nota de executie spune *unde* si *in ce - ordine*. Rapoartele din `docs\cercetare\` sunt materia prima — nu se reface cercetarea. -- **Povestile cu dependente declarate** (`*Depinde de:*`, la fiecare story) **nu se pornesc in paralel - cu cele de care depind.** Unde nu e dependenta, se pot rula in paralel de agenti diferiti — dar - **un singur scriitor pe fisier**, niciodata doi agenti pe aceeasi metoda. -- **O story care se dovedeste prea mare la executie se sparge mai departe**, cu acordul lui Marius, si - se noteaza in plan. Mai bine cinci livrari mici decat una care nu se poate revizui. - -### Teste la fiecare pas — nu doar la S6 si S12 - -`S6` si `S12` raman testele **pe flux real**, la capatul fiecarei etape. **Ele nu inlocuiesc testarea -per story.** Fiecare story se incheie cu teste proprii, rulate si trecute, **inainte** de review: - -- **Harness headless** (`COMUN\docs\depanare_testare_vfp.md`) pentru logica — cazurile minime ale - povestii, plus **cel putin o proba de neregresie** pe calea veche, care trebuie sa ramana neatinsa. -- **Harness UI vizibil** (`COMUN\docs\testare-ui-vfp.md`) unde povestea atinge formulare sau griduri — - coloanele de grid **nu se materializeaza headless**, deci acolo verificarea headless nu e concludenta. -- **Rezultatul asteptat se declara INAINTE de rulare**, inclusiv pentru cazurile care trebuie sa dea - eroare. Un test scris dupa ce s-a vazut rezultatul nu dovedeste nimic. -- **Un esec documentat e livrabil valid** — se raporteaza, nu se ascunde si nu se reia la nesfarsit. -- Testele fiecarei povesti **se pastreaza**, si intra in suita rulata de S6 / S12. Nu se scriu de - unica folosinta. - -### Code review dupa implementare, inainte de commit — pentru fiecare story - -**Nicio story nu se comite fara review, si review-ul vine dupa implementare si dupa teste, nu in loc -de ele.** Ordinea, fixa: - -1. **Implementare** (delegata unui subagent, conform modului de lucru din `CLAUDE.md`). -2. **Teste** — rulate, trecute, cu rezultatele raportate. -3. **Diff ca fisier in `docs\`** (`diff_s_.patch`), inclusiv partea din `COMUN`. -4. **Code review pe diff**, de catre **un agent care nu a scris codul** — altfel isi revizuieste - propriile presupuneri. Review-ul verifica cel putin: regresia pe calea veche, conventiile per zona - atinsa (`COMUN\docs\reguli_lucru.md`, punctul 7 — encoding `cp1250` la `.vc2`/`.sc2`, `GO` pe - `Recno()`, `goExecutor` + `ALTER TABLE`, UX formulare/griduri), comentariile (istoric **numai** in - antetul fisierului, max o linie in cod), si ca write-back-ul text→binar e facut pentru fiecare - fisier atins. -5. **Aprobarea lui Marius.** -6. **Commit** — in **ambele** repo-uri unde e cazul (ROAFACTURARE si COMUN), cu changelog. - -**Afirmatiile review-ului se verifica, nu se cred.** Un review poate citi structura corect si presupune -semantica gresit; cand semnaleaza un defect, se deschide codul citat inainte sa fie acceptat — la fel -cum se procedeaza cu rapoartele de cercetare. - -**Ce NU face review-ul:** nu reargumenteaza deciziile luate (lista lor e in acest plan), nu extinde -perimetrul povestii, si nu propune refactorizari colaterale. Ce gaseste in afara perimetrului se -**raporteaza separat**, nu se repara in trecere — exact cum s-a procedat cu defectul `lnTip` din -`do_copiaza`. - -### Etapa I — formularul unificat - -#### S1 — Inventarul de campuri si alegerea bazei -Deciziile de perimetru sunt luate (vezi listele de la inceput). Ramane: **se porneste de la prototipul -`frm_facturare_articole2` sau de la `frm_facturare_articole` extins?** -Recomandare: **de la prototip**, si acum cu trei argumente in plus fata de runda 2 — are deja -controalele de antet, are butoanele **deasupra** gridului (decizia 13), si are coloanele de discount -editabile plus procentul pe linie (decizia 14). Partea grea de layout e deja acolo; logica lipsa se -porteaza in el. -Livrabil: tabel camp-cu-camp `frm_date_factura` / `frm_date_aviz` / `frm_modifica_factura` / -`frm_alte_date` -> formular unificat, cu ce se pastreaza, **in ce grup din cele patru intra** (I), -ce se pliaza, ce dispare, si **cu ruta de scriere a fiecarui camp** (pe loc sau prin regenerare — -vezi G-bis). Baza de pornire: `docs\cercetare\inventar_controale_formulare.md`, care are deja -etichetele reale si conditiile de vizibilitate. -**Livrat (runda 7): `docs\S1_inventar_campuri_formular_unificat.md`** — 30 de campuri de antet, cu -grupul din I, conditiile de vizibilitate si ruta de scriere pentru fiecare, plus randul dedicat lui -`V_EFACTURA` (singurul camp fara control azi, nicaieri) si cele patru bife marcate ca disparute. -Tabelul a scos la iveala **golul de rute de scriere** din G-bis — vezi acolo. Are si o sectiune -„Neclarificate" cu opt puncte, dintre care doua ambiguitati de nume (`chkDetaliat`, `cboTipFactura` -apar in doua formulare si nu s-a confirmat pe cod ca scriu acelasi camp). -**Coloana „ruta de scriere" e completa acum** — `rute_scriere_antet.md` a inchis cele trei grupuri -lasate neclarificate, iar deciziile 25 si 26 le-au dat ruta. Tabelul se citeste **impreuna cu tabelul -de rutare din G-bis**, care e sursa de adevar pentru ruta fiecarui camp; sectiunea „Neclarificate" din -S1 ramane valabila doar pentru punctele **4, 5, 7 si 8** (ambiguitatile `chkDetaliat` / `cboTipFactura`, -liniile exacte de definire a controalelor, si dependenta de drepturi). -*Gata cand:* tabelul e aprobat. **APROBAT de Marius, 09.08.2026 — S1 e incheiata.** Tabelul devine -referinta pentru S3 si S3b; modificarile ulterioare se fac in el, nu se rescrie de la zero. - -**ASEZAREA ZONEI DE JOS — INCHISA prin decizia 57 (Marius, runda 15): VARIANTA D.** -Cerinta initiala (runda 14), formulata de el: *„totalurile de jos sa fie mai compacte, si sa includa -si discount-ul; formularul trebuie sa fie compact si aerisit; incasarea si alte date jos de tot, -eventual 2 butoane pe acelasi rand — vezi modelul Saga; in centrul atentiei sa fie datele facturii"*. -Referinta vizuala: `{06D747B8-0824-488C-8832-DEB9AE661974}.png`. -Rundele 14-15 au dat patru variante (A / B / C, apoi D). **Aleasa: D.** Vezi „Decizia 57" la -deciziile rundei 15, care are enuntul complet, cele patru consecinte de implementare si punctul lasat -deschis. Ce e de retinut aici, pentru executia lui S1: - -- **Antetul se strange la doua randuri**, fara titluri de grup — campurile se recunosc dupa eticheta. -- **Panoul separat „Discount pe document" dispare**; discountul devine rand editabil in banda de - totaluri, cu procentul pe loc. -- **Banda de totaluri** e pe toata latimea, lipita de grid: baza, discount articole, discount document - cu procentul, TVA — desfacute pe un rand — si totalul mare singur la dreapta. -- **Incasare si Alte date NU sunt dialoguri** — sunt doua sectiuni colapsabile in formular, **una - langa alta pe acelasi rand**, sub totaluri, fiecare cu rezumatul continutului pe randul inchis. -- **Bara de comenzi de jos nu exista**: `but_renunt` / `but_termin` raman in banda de titlu. - -**Cota de TVA a discountului de document s-a decis — decizia 59, runda 16** (vezi sectiunea K-bis): -repartizare proportionala automata pe cote, discountul **NU cere cota de la utilizator**, deci **nu -e nevoie de o a treia sectiune pentru cota**. Ce mai cere UI e **un camp text optional de motiv** -(pentru `AllowanceChargeReason`), mult mai mic decat o sectiune. **Decizia 61 (runda 16) l-a -materializat: Marius vrea campul. Asezarea lui s-a inchis definitiv abia in runda 17 — decizia 64 -(a treia sectiune jos) a fost **rasturnata de decizia 66: motivul sta in banda de totaluri, langa -suma pe care o explica**, iar randul de jos ramane cu **doua** sectiuni, ca la decizia 57. Varianta -grea (cota + explicatie proprii) ramane exclusa, ca mai sus. - -**Cele doua pagini online ale lui #13 — nu se confunda:** -- **mockup-ul formularului** (fisier pe disc, `docs\mockup_13_formular_unificat.html`, **v9** din runda - 17, cu asezarea D si motivul discountului in banda de totaluri): - **https://claude.ai/code/artifact/e9d73e86-7518-4f50-a998-43c49e083142**. - Are copie pe disc, si ea e sursa de adevar; artifactul se republica din ea (`WebFetch` pe URL intai, - apoi `Artifact` cu `url`). Decizia 33 e consumata — URL-ul e la zi. -- **pagina cu variantele de asezare** (A/B/C/D), **fara copie pe disc**, deci artifactul **e** sursa de - adevar: - -Pagina cu variantele: **https://claude.ai/code/artifact/dfc70d38-856f-48fd-b6af-3da78e9da4f7** -— **nu mai are copie pe disc** (cerinta lui Marius, runda 15: „doar online, ca sa nu mai intretii 2 -variante"). Procedura de modificare: `WebFetch` pe URL → fisier de lucru **in scratchpad, nu in -`docs\`** → `Artifact` cu `url` = link-ul de mai sus. - -#### S2 — O singura procedura `factureaza` -Se elimina `factureaza2` ca procedura paralela; `factureaza` primeste un mod -(`gnFacturareNou` ramane comutatorul de rulare, dar nu mai duce la alt cod duplicat). Se pastreaza -integral ramurile care lipsesc azi din `factureaza2` (lista din A). - -**PROIECTAT (runda 9) — `docs\cercetare\s2_factureaza_unificare.md`.** -**S2 e mult mai ieftina decat arata planul.** `factureaza2` nu e a doua implementare functionala care -cere fuziune atenta — e un fork din 08.06.2017 la care **executia interogarii de articole e dezactivata** -(`lnSucces = 1` hardcodat, `goExecutor.oExecute` niciodata apelat) si mai multe blocuri intregi sunt -inchise cu `If .F.`. Are **exact un apelant** in tot codul — `ofacturare.prg:90`, din interiorul lui -`factureaza`, in spatele unui `AMESSAGEBOX` de confirmare — deci **zero utilizatori reali**. Ambele sunt -proceduri globale in `COMUN\programe\ofacturare.prg` (`:81-577` si `:583-1081`, ~500 de linii fiecare), -fara omonime. Singura diferenta functionala care justifica existenta lui `factureaza2` e formularul de -articole (`frm_facturare_articole2` vs `frm_facturare_articole`). -**Obstacolul nu e tehnic:** formularul spre care duce e tot un prototip neterminat, deci unificarea -procedurilor **nu** face formularul nou utilizabil — doar elimina duplicarea. Aia rămâne treaba lui S3. -**Trei corectii la lista din sectiunea A:** `verifica_numar(16, ...)` pentru chitanta **nu lipseste** din -`factureaza2` (e identic, `:502-504` vs `:1018-1020`) — planul greseste; „`cursor_avize` cu tip 23" e -imprecis — tipul 23 nu lipseste, e **rutat diferit** (`cursor_preturi` in `factureaza` vs -`cursor_gestiune` in `factureaza2`), ceea ce e mai grav decat o omisiune; si lipsesc din plan **tipul 52** -pe ramura de contract si **tipul 24** pe ramura de retur. Semnatura propusa, ramificarea interna, ordinea -in care se face fara sa strice suita si criteriul de „gata" verificabil sunt in raport, sectiunile 5-7. -**`ofacturare.prg` e cod comun al intregii suite** (copie in fiecare produs ROA, sincronizata manual) — -la fel ca S3c, nu se face impreuna cu alta modificare. -*Gata cand:* `factureaza2` nu mai exista, iar comutatorul alege formularul, nu procedura. -*Depinde de:* S1. - -#### S3 — Portarea logicii de antet in formularul unificat -Cele 17 + 13 metode `do_cauta_*`, `do_schimba_tipdoc`, validarile din `inainte_de_do_termin` -(`ofacturare.vc2:9455-9561` si `:7277-7352`) si `Init`-urile. Se porteaza **o singura data**, cu -ramificare pe factura / aviz in interior, nu doua copii. -Punct de atentie cunoscut: **#16 din `todos.txt`** — la revenirea din formularul de curs valutar, -focusul sare pe tip document si iesirea din serie regenereaza numarul actului. - -**PROIECTAT (runda 9) — `docs\cercetare\s3_portare_antet.md`.** Doua rezultate schimba povestea: - -- **Portarea „o singura data cu ramificare" nu e posibila ca atare.** `Init`, `inainte_de_do_termin` si - partial `do_cauta_fdoc` sunt **omonime cu semantica total diferita pe toate cele patru formulare-sursa** - (antet vs. articole vs. alte-date). Deci nu e o metoda cu ramificare interna, sunt **patru fuziuni de - metoda**. Obstacolul principal al lui S3 nu e codul de validare — majoritatea e `Do Case` / `amessagebox` - mecanic — ci **reconcilierea a patru cicluri de viata `Init` / `inainte_de_do_termin` independente - intr-unul singur**, cu ordinea de dependente din sectiunea 5 a raportului. Numarul real de `do_cauta_*` - e **29, nu 30**. Lista completa a omonimelor e la sectiunea 2 — **se citeste inainte de orice editare**, - altfel portarea le confunda (capcana deja platita o data cu `do_calculeaza_discount`). -- **#16 nu e un bug de focus, si nu e in perimetrul in care il caută planul.** Reprodus pe cod pana la - linia exacta: bucla de reincercare din `factureaza` / `factureaza2` (`ofacturare.prg:174-571`) trateaza - **orice esec SQL** — nu doar cursul valutar lipsa — ca pe un „DA, continui cu alt document", si - redeschide formularul de antet de la zero. Deci **unificarea nu-l reproduce automat**; poate chiar sa-l - elimine ca efect secundar, daca antetul nu se mai reconstruieste din `Init` dupa un esec Oracle in pasul - urmator. **Cade premisa „ori se rezolva #16 aici, ori se reproduce bug-ul".** Si leaga #16 de **S2**, nu - de S3 — bucla e in procedura, nu in formular. - -*Gata cand:* un document se emite integral din formularul unificat, pe tip 1 (lista de preturi), cu -acelasi rezultat in `vanzari` / `act` / `rul` ca pe calea veche — criteriul rescris verificabil e la -sectiunea 7 a raportului, iar ce **nu** se poate testa headless la sectiunea 8. -*Depinde de:* S2. - -#### S3b — Sectiunea pliata: alte date + analitice -Deciziile 7 si 8. Se muta in formular cele patru grupuri din `frm_alte_date` (B) si coboara acolo -analiticele din antet. `actualizeaza_tipincasare` se muta ca atare; **alocarea si dezalocarea -numerelor** de chitanta / bon fiscal / POS raman legate de aceleasi evenimente ca azi, nu de -deschiderea formularului. `frm_alte_date` nu se sterge cat timp calea veche mai e in uz. -**Doua precizari din runda 7, amandoua restrang povestea:** -- **analiticele coboara ca AFISARE, nu ca editare** (decizia 26) — venit/cheltuiala, sectie, - responsabil, lucrare sunt read-only in formularul unificat; se editeaza din editorul de nota al lui - **#6**, care le trateaza pe linie. Eticheta / tooltip trebuie sa spuna asta, altfel campul pare stricat; -- **pe un document deja emis, grupul de incasare e blocat in etapa I** (decizia 25) — nu are ruta de - scriere pe loc, iar regenerarea nu exista inca. La emitere se comporta ca azi. -**PROIECTAT (runda 11) — `docs\cercetare\s3b_alte_date_analitice.md`.** S3b nu e o mutare de controale. -Trei rezultate schimba povestea: - -- **Riscul din criteriul de „gata" e real, si are mecanism.** Alocarea numarului de chitanta nu porneste - din clicul utilizatorului, ci din **orice atribuire programatica** a lui `.Value`: - `opt_incasat.ProgrammaticChange` (`ferestre_cere_date.vc2:3351-3352`) cheama acelasi - `actualizeaza_tipincasare()` ca `.Click` (`:3250-3251`). Consecinta neasteptata: **azi, deja**, - `Init` (`:3150`) seteaza singur `opt_incasat.Value = 2` cand documentul soseste cu `poDate.incasat<>0` - (cazul copierii), deci deschiderea dialogului aloca un numar fara ca cineva sa atinga ceva. Intr-un - formular unde zona se plieaza si se deplieaza repetat pe acelasi `poDate` persistent, o repopulare - naiva a starii ar aloca si dezaloca **la fiecare toggle**. Solutia proiectata: populare **o singura - data**, iar plierea strict pe `Visible`/`Height` — niciodata pe reasignare de `.Value`. Criteriul de - non-alocare e formulat ca **assert headless pe `poGeneratorNumere`**, pe doua scenarii de start - (document nou **si** document copiat cu incasare presetata — al doilea e cel care azi chiar aloca). -- **Nu exista mecanism de pliere de refolosit.** Cautare exhaustiva in `ofacturare.vc2`: zero potriviri - in prototipul `frm_facturare_articole2`. Cel mai apropiat tipar din suita e - `frm_modific2024.afiseaza_rulaje` (`omodificari.vc2:13169-13199`, perimetrul #6 — **citit, neatins**), - acelasi idiom sus/jos validat deja pentru `but_modifica`/`but_salveaza` la decizia 9. Deci **cod nou**, - dupa un tipar existent, nu o clasa reutilizabila. -- **Decizia 26 cere mai mult decat mecanismul existent.** `ct_clb_cautare.do_dezactiveaza()` - (`caut_ora.vc2:800-806`) ascunde **doar lupa de cautare**; textbox-ul ramane tastabil. Read-only real - cere `ReadOnly` explicit pe langa el — nesemnalat pana acum. - -**Bug preexistent, gasit in trecere:** la Renunt se dezaloca chitanta (16) si bonul fiscal (3), dar -**nu si POS (26)**. Nu e introdus de S3b, dar „`actualizeaza_tipincasare` se muta ca atare" **l-ar -propaga**. **Decizia 40: se repara aici, in trecere** — deci diff-ul lui S3b nu mai e o mutare pur -mecanica, si testarea acopera si dezalocarea POS pe calea veche. - -**Decizia 41 fixeaza granularitatea plierii: doua comutatoare.** Grupul de **incasare** are comutator -propriu — e singurul cu efecte laterale (alocare/dezalocare) si singurul blocat pe document emis prin -decizia 25 — iar delegat/transport, adresa, text aditional si analiticele stau impreuna sub al doilea. -Garda de non-alocare la toggle se scrie si se testeaza astfel **intr-un singur loc**. - -*Gata cand:* criteriul rescris verificabil e la sectiunea 10 a raportului, in cinci puncte — mai strans -decat formularea de mai jos pe trei dintre ele: paritatea de emitere se cere pe **toate patru** tipurile -de incasare (nu doar bon fiscal), non-alocarea la toggle se cere pe **ambele** scenarii de start, iar -blocarea grupului de incasare si read-only-ul analiticelor se cer pe **ambele stari ale antetului** -(inainte si dupa `but_modifica`), nu doar la deschidere. Formularea initiala, pastrata ca rezumat: un -document cu incasare prin bon fiscal emis din formularul unificat produce aceleasi randuri si acelasi -numar de bon ca pe calea veche; deschiderea si inchiderea formularului fara a atinge zona de incasare -**nu aloca si nu dezaloca niciun numar**; iar pe un document emis analiticele se vad dar nu se pot edita. -*Depinde de:* S3. - -#### S3c — Sursa ca parametru, nu ca global -Decizia 12. Punctele de intrare exista deja (comanda: `ocomenzi.vc2:1580-1596`; contract: din -ROACONTRACTE), dar transmit prin globalele `goComanda` / `goContract`. Se adauga parametru explicit -pe `factureaza` si pe `oDateFactura`, cu globalele pastrate ca sursa de rezerva pana se convertesc -toti apelantii (inclusiv cei din ROACONTRACTE si ROAGEST — e cod comun). Se verifica intai asimetria -de resetare semnalata la L.3. -**PROIECTAT (runda 11) — `docs\cercetare\s3c_sursa_ca_parametru.md`.** Se poate face, e o interventie -mica — dar **planul supraestimeaza cat de „globala" e problema azi**, si in doua sensuri opuse: - -- **`goContract` nu e scris nicaieri in ROAFACTURARE** — nici in codul produsului, nici in `COMUN`-ul - lui. Deci ramura de precompletare din contract (`ofacturare_comun.prg:261-297`) e **cod mort in acest - produs**, iar **asimetria de resetare de la L.3** (`ofundal_facturare.vc2:886-902`) exista textual dar - e **inerta**: n-are ce sa lase nereseta, pentru ca n-are scriitor. Devine risc real abia daca cineva - adauga in viitor un scriitor in ROAFACTURARE — moment in care lipsa resetarii s-ar activa **tacut**. -- **`goComanda` chiar e viu, si are DOI scriitori**, nu unul: clicul pe „Factureaza" - (`ocomenzi.vc2:1583`) **si** navigarea in grid (`:2199`, doar ca sa decida vizibilitatea unui buton). - Al doilea n-are nicio legatura cu facturarea si **ramane si dupa conversie** — deci globala nu dispare. -- **Criteriul „pana se convertesc toti apelantii" nu se poate citi ca „pana dispare globala".** In - ROACONTRACTE, `goContract` e **bufferul de editare al intregului ecran de contracte** (peste 100 de - `ControlSource` legate de el), populat de navigarea in grid, independent de facturare. Nu dispare la - aceasta poveste si nici la vreuna rezonabila urmatoare; S3c schimba **doar canalul** prin care valoarea - ajunge la `oDateFactura.Init`. - -**Suprafata de regresie, masurata:** doi scriitori de convertit (`ocomenzi.vc2:1580-1596`; -`ferestre_contracte.vc2:1538-1549` + `:1598-1606` in ROACONTRACTE), **trei** fisiere comune atinse o -singura data fiecare (`ofacturare.prg`, `oproceduri_facturare.prg`, `ofacturare_comun.prg`), si **zero -schimbari** la celelalte ~31 de puncte de intrare din inventarul S2 — toate cheama `factureaza(N)` cu un -singur argument. Cele cinci fisiere sunt **identice pe MD5** in cele sapte produse, cu o singura exceptie: -**ROAIMOB**, o linie lipsa in `ofacturare_comun.prg` — acolo diff-ul se aplica **manual**, nu prin copiere -mecanica. - -**Semnatura propusa:** `factureaza(tnTip, toFactura, toSursa)` si `oDateFactura::Init(tnIdSet, tnTip, -toSursa)` — parametru nou la coada, cu implicit, fallback pe global cand lipseste. -**Capcana de limbaj, semnalata explicit:** garda se scrie `Type('toSursa') = 'O'`, **nu** -`Type('toSursa') <> 'U'`. Un parametru VFP nepasat **nu** e `'U'` (aia e pentru variabile nedeclarate), -ci `'L'` cu `.F.` — o garda `<> 'U'` ar trece mereu adevarat si ar incerca sa citeasca `.id_part` de pe -`.F.`, eroare la primul apel neconvertit. Raportul cere verificarea comportamentului `Type()` pe un -`.prg` de proba **inainte** de a atinge fisierele reale. - -*Gata cand:* criteriul rescris e la sectiunea 10 punctul 4 al raportului, pe patru probe — una -structurala (parametrul prezent cu semnatura identica in toate cele trei fisiere comune, in toate cele -sapte produse) si trei comportamentale: **calea convertita** cu globala deliberat „murdara" dintr-o -navigare anterioara produce documentul dat prin parametru, nu pe cel din globala; **calea neconvertita** -(oricare din cele ~31 de apeluri cu un singur argument) produce acelasi document ca inainte; iar -**ROACONTRACTE** da aceeasi precompletare ca azi, verificat manual pe build separat. -*Depinde de:* S2. **Atinge cod comun intregii suite** — nu se face impreuna cu alta modificare. - -#### S4 — Cautarea articolelor pe server, in linie -Partea Oracle: variante filtrate pe articol ale cursoarelor de facturare, cu aceleasi ramuri pe tip -ca `cursor_preturi` / `cursor_contract` / `cursor_comanda` / `cursor_avize` / `cursor_gestiune`. -Partea VFP: `combosql` pe cod si pe denumire completeaza linia cu pret, cota TVA, valuta, `id_pol`, -`gestionabil`, `pret_cu_tva`; `crsarticole` nu se mai incarca in masa. -Se pastreaza "adauga tot" pentru tipurile care au document sursa (comanda, aviz, contract) — acolo -setul e marginit si incarcarea lui e legitima. -**PROIECTAT (runda 11) — `docs\cercetare\s4_cautare_articole_server.md`. Criteriul de mai jos nu e -atingibil ca atare, si motivul nu e UX.** - -- **`crsarticole` nu e doar sursa de populare a gridului — e un registru al cantitatii ramase de - facturat.** E citit **si scris** de `do_adauga_tot`, `do_sterge` si `do_scrie_factura` pentru toate - tipurile cu document sursa: stergerea unei linii **reface** cantitatea in `crsarticole` - (`ofacturare.vc2:14640-14669`), iar la scriere se face `Calculate Sum(cantitate) To lnCantitateRamasa` - peste el ca sa se decida daca se inchide automat comanda / avizul (`:14303-14311`, `:14334-14338`). - Deci pe **comanda, aviz si contract** incarcarea in masa **nu poate disparea**, indiferent de decizia - de UX privind „adauga tot" — bookkeeping-ul e cablat pe cursorul incarcat. **S4 se aplica curat doar - pe ramurile de lista de preturi** (`cursor_preturi`, plus jumatate din `cursor_contract` si - `cursor_gestiune`). - **DEPASIT de proiectarea punctului 2 (runda 12)** — se citeste doar ca istoric al deciziei 39. - Concluzia „incarcarea in masa nu poate disparea" cadea pentru ca `crsarticole` era tratat ca un - registru omogen; sunt de fapt **doua mecanisme distincte** (Rol A / Rol B), si niciunul nu cere - cursorul incarcat. Vezi mai jos. -- **`combosql` e un prototip la jumatate, si e singura lui utilizare din suita.** - `grd_factura.cCodMat.cboCodmat` / `cCboDenumire` (`ofacturare.vc2:16751-16797`, wiring la - `:19288-19334`) chiar cauta pe server, dar scrie **doar** `codmat` / `denumire` / `id_articol`, pentru - ca sursa lui (`vnom_articole`) n-are pret, TVA, valuta, `id_pol`, `gestionabil`. Cautare in - `COMUNROA` si `ROAGEST`: **zero alte utilizari** — nu exista de unde copia un exemplu complet. - Confirma insa exact punctul C: de facut e **varianta filtrata pe articol a celor cinci cursoare**, nu - o cautare noua. -- **`cursor_contract` produce deja doua cursoare** — `V_CURSOR` (`crsarticole`, prin delegare la - `cursor_preturi`) si `V_CURSOR2` (`crsarticole1`, articole **sau** rate). Filtrarea se aplica curat - doar pe jumatatea `crsarticole`; **randurile de rata n-au `id_articol`** si raman needitate. -- **Vizibilitatea de azi a butonului „adauga tot" coincide aproape exact cu impartirea utila pentru S4** - (`ofacturare.vc2:15108-15248`: ascuns pe lista de preturi, vizibil pe comanda / aviz / retur) — - confirmare pe cod, nu presupunere. -- **Legatura cu S10, confirmata:** pretul se cauta **o singura data**, la alegerea liniei, si se - transmite mai departe neschimbat — exact ce face azi `adauga_articol_factura`, care il primeste ca - parametru in loc sa-l re-derive. -- **Efect secundar al filtrarii, rezolvat prin decizia 42:** `verifica_cursuri_valute` e azi - neconditionata in procedura, deci pe varianta filtrata s-ar declansa la **fiecare** cautare de articol - in loc de o data la deschidere — cu `-20005` posibil pe o valuta pe care operatorul n-o foloseste, - inainte sa vada vreun rand. Se **restrange la valuta articolului cautat**. -- **Cele doua puncte „de inchis inainte de implementare" sunt INCHISE (runda 12)** — - `docs\cercetare\s4_puncte_deschise.md`. Raspunsul de baza e la amandoua „filtrarea nu schimba nimic", - dar **fiecare lasa in urma o cerinta de implementare, nu doar o bifa**: - - **`id_jtva_coloana` chiar lipseste** din `cursor_preturi`, din `cursor_gestiune` si din `V_CURSOR2` - al lui `cursor_contract`, pe toate ramurile — confirmat pe SQL. **Nu e bug:** `do_initializeaza_articol` - pune `0`, iar `frm_articol_factura.Init` (mostenit si de `frm_articol_gest_factura`) **il rederiva - mereu** din `proc_tvav` via `jtva_coloane`, pe orice linie adaugata prin `do_adauga_articol`. - **Cerinta care rezulta pentru S4:** derivarea tine **numai** daca linia trece prin - `do_adauga_articol`. `combosql`-ul prototip de azi (`ofacturare.vc2:19288-19306`) face `REPLACE` - direct in `crsfactura` si **ocoleste toata derivarea**; daca S4 extinde acel `REPLACE` fara sa treaca - prin `do_adauga_articol`, `id_jtva_coloana` ramane nederivat si Oracle - (`adauga_articol_factura`, ramura `ELSE`) arunca **`-20000 FACT-013`** sau scrie cota gresita. - **Bug nou posibil, introdus de S4** — intra ca cerinta explicita in proiectare, nu ca observatie. - - **`but_urmator_tot1` are `Visible = .F.` la design** (`ofacturare.vc2:11265`); tipurile 23 si 41 au - `Case` propriu, dar **niciunul nu-l face vizibil** — confirmat, nu presupus. Nu exista alt mecanism - de „adauga tot"; exista insa `but_urmator1` (adaugare rand-cu-rand), **neconditionat de tip**, care - merge prin acelasi `do_adauga_articol` — deci dupa S4 **nu se pierde nimic** pe aceste tipuri. - - **Corectie de rutare fata de ce presupunea planul:** pe formularul **standard** (`factureaza`, - `ofacturare.prg:266-308`) doar **tipul 41** cheama `cursor_gestiune`; **tipul 23 cheama de fapt - `cursor_preturi`** (grupat cu lista de preturi). Tipurile **45, 48, 49 nu ating deloc** - `cursor_gestiune` (45 → `cursor_preturi`, 48/49 → `cursor_articole_k`, alta procedura). Doar pe - **prototip** (`factureaza2`, opt-in `gnFacturareNou`) 23 si 41 merg impreuna pe `cursor_gestiune` — - **divergenta reala intre cele doua formulare**, semnalata, neatinsa, in afara perimetrului S4. - -**Decizia 39 largeste povestea peste ce propunea raportul.** Raportul recomanda restrangerea la lista de -preturi, tocmai pentru ca registrul de cantitate ramasa blocheaza restul; Marius a ales sa se desfaca si -registrul. Deci S4 are de acum **doua bucati, nu una**: -1. **varianta filtrata a cursoarelor** + completarea liniei din `combosql` (partea proiectata in raport); -2. **decuplarea bookkeeping-ului de cantitate ramasa** de cursorul incarcat in masa — sursa unica, nu o a - doua copie tinuta in pas cu prima. Atinge `do_adauga_tot`, `do_sterge`, `do_scrie_factura` si - **inchiderea automata** a comenzii / avizului. - -**Punctul 2 — PROIECTAT (runda 12): `docs\cercetare\s4_punct2_registru_cantitate_ramasa.md`.** Iese mult -mai ieftin decat parea, pentru un motiv care nu se vedea din raportul punctului 1. - -- **`crsarticole.cantitate` are DOUA roluri, nu unul** — si numai unul e „registrul" temut. - **Rol A — cantitate ramasa de facturat dintr-un document sursa** (comanda `3,21,25,28,42,47`; avize - `4`). **Rol B — plafon de cantitate in sesiune** (lista de preturi gestionabila `1,22,29` si jumatatea - de contract, transfer `23,41`, retur `8,9,24`): impiedica operatorul sa adauge mai mult decat vede pe - ecran. **Rol B nu alimenteaza nicio decizie Oracle** — e plafon de UI, nu registru de business. -- **Pe Rolul A, cursorul VFP e o copie redundanta a unui calcul pe care Oracle il repeta oricum.** - `do_scrie_factura` sumeaza `crsarticole` doar ca sa decida ce trimite in `pnParametruAditional`, iar - Oracle **recalculeaza independent, din tabele reale**, in `inchide_comanda()` / `marcheaza_facturat()`, - chiar in procedura care scrie factura. -- **Nu exista coloana `INCHISA`.** Cautare in tot pachetul: **zero potriviri** (verificat separat de - sesiunea principala). „Comanda inchisa" e o stare **derivata** din `COMENZI_ELEMENTE` vs - `VANZARI_DETALII`; `inchide_comanda()` insereaza un **rand compensator**, nu seteaza un flag. Pe - comanda, `pnParametruAditional` e strict **binar** — „s-a cerut fortarea inchiderii?" —, iar cantitatea - trimisa de VFP **nu participa** la calculul Oracle. -- **Pe avize e altfel, si aici sta subtilitatea:** `VANZARI.FACTURAT` **e** un flag persistat, iar - `V_VERIFICARE` (= `pnParametruAditional`) alege intre „**increde-te in VFP** si marcheaza toate avizele - referite" (`0`) si „**recalculeaza per-aviz** din tabele reale si marcheaza doar cele cu `ramas = 0`" - (`1`). Deci pe aviz suma din VFP schimba semantica, nu doar declanseaza o actiune. -- **Un bug preexistent, de REPRODUS identic, nu de reparat in trecere:** cand un `crsarticole` agrega mai - multe avize, suma globala poate da `0` desi un aviz are ramas si altul are exces care-l compenseaza — - caz in care toate se marcheaza facturate, inclusiv cel cu ramas real. Varianta noua pastreaza aceeasi - conditie de declansare (suma globala pe tranzactie, nu per document). -- **`23` si `41` nu apar deloc in `CASE`-ul de finalizare** — cad pe `ELSE`, fara nicio inchidere: - transferul n-are document sursa de inchis, doar plafon (Rol B). Contractul (`2,6,52`) intra pe - `scrie_rate_factura`, care **nu e o inchidere**, e alta operatie. -- **Recomandarea: (b) pentru Rolul A, (c) pentru Rolul B.** - **(b)** cele doua `Calculate Sum` din `do_scrie_factura` se inlocuiesc cu un apel Oracle facut **dupa** - `do_scrie_articole()` (cand `VANZARI_DETALII_TEMP` contine exact liniile pe cale sa fie scrise) si - **inainte** de `Do Case`-ul care alege procedura de scriere. Cele doua functii Oracle noi sunt o - **extragere** a interogarii pe care `inchide_comanda` / `marcheaza_facturat` o ruleaza oricum, nu logica - noua. Efect secundar gratuit: dispare si un bug latent de concurenta — cursorul local nu vede azi ce a - facturat intre timp alt operator din aceeasi comanda. - **(c)** plafonul Rol B se cere pe server **la fiecare adaugare/editare**, minus ce e deja in - `crsfactura` — deci nu mai exista a doua copie de tinut in sincron. - Varianta cu un cursor propriu, minimal, a fost **respinsa motivat**: ramane tot o a doua copie manuala, - doar mai ingusta. Nu e „sursa unica". -- **Cazul limita cerut in plan** (adauga si sterge inainte de salvare) devine **trivial**, nu doar - acoperit: liniile adaugate-si-sterse nu ajung niciodata in `VANZARI_DETALII_TEMP`, deci nu influenteaza - calculul, fara nicio actiune de refacere. -- **CORECTIE asupra punctului 1:** pasul lui 5 (`s4_cautare_articole_server.md:417-424`) opreste - incarcarea in masa si pe `23,41`, presupunand ca n-au bookkeeping — **au Rol B**, confirmat pe cod. - Punctul 1 **nu se poate aplica pe `23,41`** inainte ca punctul 2 sa acopere Rolul B pe ele. - -**Toate trei sunt INCHISE (runda 13). Nu se mai reiau.** -1. ~~**Asimetria din `do_modifica`**~~ — azi, pe grupul `1,22,29` + contract-lista, plafonul **nu** se - ajusteaza la editarea cantitatii unei linii deja adaugate. E preexistenta. **INCHIS — decizia 45 - (runda 13, pe recomandare):** se lasa sa se **corecteze de la sine** prin recalculul la cerere, cost - zero, dar corectia se **declara explicit in changelog** — S4 schimba atunci un comportament punctual, - nu doar „decupleaza". -2. ~~**Corectia pe `23,41`**~~ — **INCHIS — decizia 46 (Marius, runda 13): asteapta punctul 2.** - Punctul 1 **nu se redeschide** acum; aplicarea lui pe `23,41` vine odata cu recalculul pe server, care - acopera Rolul B. Nu se pierde nimic: ordinea de implementare oricum le pune dupa. -3. ~~**Contract `26,52`**: nu s-a gasit dovada nici de prezenta, nici de absenta a Rolului B.~~ - **INCHIS de proiectarea S5** (`docs\cercetare\s5_acoperire_tipuri.md`): `26` si `52` **n-au niciun - bookkeeping**, nici Rol A, nici Rol B — excluderea e totala, pe `Do Case` exhaustiv fara ramura - implicita. Deci „dovedit absent", nu „nepresupus". Nu mai e o decizie de luat. - -**Cerinta de revizuire**: cele doua functii Oracle noi sunt o extragere din proceduri existente, dar -raman **PL/SQL nou in `PACK_FACTURARE`**, cu `JOIN`-urile reproduse **din citire, nu din executie**. Se -verifica pe Oracle inainte de a continua — e primul pas al implementarii, nu o formalitate. - -**Nota de executie (decizia 60):** pe documentele de tip **48/49** (custodie), cautarea pe server in -linie ramane restransa la articole `IN_STOC = 0` — nu se ofera articole gestionabile pe aceste tipuri. -Siguranta regenerarii la editarea custodiei (decizia 60, -`docs\cercetare\custodie_48_49_stergere_reemitere.md`) atarna de acest invariant. - -*Gata cand:* patru probe, nu una. **(a)** Deschiderea formularului nu mai executa niciun cursor de -articole, **pe niciun tip** — nu doar pe lista de preturi. **(b)** Alegerea unei linii produce aceleasi -valori ca randul corespunzator din `crsarticole` de azi, pe fiecare tip; maparea camp-cu-camp e la -sectiunea 6 a raportului, cu coloanele semnalate explicit acolo unde varianta filtrata **nu** poate -produce aceeasi valoare. **(c)** **Paritate pe inchiderea automata** (decizia 39): acelasi document -sursa, aceleasi linii facturate partial, **aceeasi stare finala** — pe comanda, acelasi rand compensator -in `COMENZI_ELEMENTE` (nu exista flag `INCHISA` de comparat); pe aviz, exact aceleasi `VANZARI.FACTURAT` -marcate, inclusiv in cazul in care avizele agregate se compenseaza intre ele — pe fiecare tip cu document -sursa — -inclusiv cazul in care operatorul adauga si apoi sterge linii inainte de a salva, care azi trece prin -refacerea cantitatii in `crsarticole`. **(d)** O linie adaugata prin cautarea in grid ajunge in -`crsfactura` cu **`id_jtva_coloana` derivat** — adica trece prin `do_adauga_articol`, nu prin `REPLACE` -direct ca prototipul de azi; proba e ca documentul se scrie fara `FACT-013` si cu aceeasi cota ca pe -calea veche. **(e)** Pe un document de tip 48/49, cautarea pe server nu ofera si nu permite adaugarea -unui articol cu `IN_STOC <> 0` (decizia 60). -*Depinde de:* S3. **Punctul 2 nu e proiectat** — se proiecteaza separat inainte de implementare. - -#### S4b — Bara de butoane si meniul de adaugare -Decizia 13, proiectata in J. Trei bucati: -1. **Butoanele de linie deasupra gridului**, cu eticheta, nu iconite mute: linie noua (`but_nou`), - sterge linia (`but_sterge`), detalii linie. -2. **Un singur buton „Adauga articole” cu `xmenu()`**, cu optiunile din tabelul din J. Inlocuieste - `But_urmator_tot1` (azi fara caption si fara `ToolTipText`) si butonul separat de alegere. - **Acopera si contractele** — tipurile 2, 6, 26, 52 lipsesc azi din conditiile de vizibilitate - (`ofacturare.vc2:15113-15245`), desi avizele sunt acolo. -3. **Alegerea selectiva**, dupa tiparul RORIS: dialog modal cu coloana de bifat si criterii de - cautare, populare aditiva in cursorul local, nicio scriere in baza pana la salvare. Pe contract, - unitatea de selectie e **rata** — cazul explicit cerut. Spre deosebire de modelul RORIS, la zero - rezultate se spune de ce, nu se inchide in tacere. -**PROIECTAT (runda 11) — `docs\cercetare\s4b_bara_butoane_meniu.md`.** Implementabila fara cod nou major, -cu **patru corectii** fata de textul de mai sus: - -- **Golul de contract e mai mare decat „lipseste `but_urmator_tot1.Visible`".** Pe **tipul 52** - formularul nu intra in **niciun** `Case` al `Do Case`-ului (`ofacturare.vc2:15109-15248`) — deci pierde - si titlul, si eliminarea coloanei `cSerie`, nu doar butonul „tot". -- **Nu se porneste de la tiparul RORIS.** `frm_tranzit` e specific ROAACNPRO si ar insemna formular nou; - ROAFACTURARE are deja `cauta_alfa(..., tnTipReturn=1)` — mecanism **generic** de selectie multipla cu - bifare, criterii de cautare si populare aditiva, folosit **chiar in acest formular** pentru returul - multi-factura (`do_cauta_facturi`). Mai ieftin, si deja dovedit in productie. -- **„Unitatea de selectie e rata" e adevarat doar pe jumatate.** `crsarticole1` are **doua ramuri - disjuncte** in SQL — `OPT_FACTURARE = 3` (articole reale) si `OPT_FACTURARE IN (1,2)` (rate de - scadentar) — iar un contract e mereu pe una singura, niciodata pe amandoua. Eticheta „Alege ratele de - facturat…" e corecta doar pe a doua ramura; **contractul are deci doua meniuri, nu unul**. -- **Un gol de cod, nu doar de vizibilitate:** `do_adauga_tot` parcurge azi **exclusiv `crsarticole`, - niciodata `crsarticole1`**. Fara extindere, „adauga tot" pe contract fie n-ar face nimic, fie ar aduce - liniile gresite — deci pe contract butonul trebuie **scris**, nu doar facut vizibil. - -**Echivalenta „tot" = „alege total"** tine azi prin constructie, pe o singura rutina comuna de adaugare -pe rand (`do_adauga_articol`) — se pastreaza asa, iar pe contract devine adevarata abia dupa extinderea -de mai sus. - -*Gata cand:* pe fiecare sursa (comanda, contract, avize, document returnat) meniul ofera si „tot" si -„alege", iar rezultatul in `crsfactura` e identic pe cele doua cai cand selectia e totala — **inclusiv -pe contract**, unde azi „tot" nu parcurge cursorul potrivit, si **inclusiv pe tipul 52**, care azi nu -intra in nicio ramura. Pasii cu criterii verificabile sunt la sectiunea 7 a raportului; ce nu se poate -testa headless, la sectiunea 8. -*Depinde de:* S4. - -#### S4c — Discountul pe linie, mutat din dialog in grid -Decizia 14, proiectata in K. Dialogul de articol dispare odata cu unificarea, deci cele doua campuri -de discount ale lui — **procent** si **valoare unitara** — devin coloane in grid, cu acelasi calcul -reciproc din `frm_articol_factura.do_calculeaza_discount` mutat pe evenimentele coloanelor. Se repara -in acelasi timp inconsecventa de azi (read-only in lei, editabil fara recalcul in valuta). Se -pastreaza excluderea pe `in_valuta`. Modelul de date **nu se atinge** — se stocheaza tot valoarea. -**VERIFICAT — S4c e deblocata, dar capcana e alta decat se credea.** Raport: -`docs\cercetare\discount_in_rapoarte_si_efactura.md`. - -- **Niciun raport de factura nu tipareste discountul pe linie** — nici valoare, nici procent. Cautare - in toate `.fr2` de factura / proforma / invoice din `COMUN\Rapoarte`: **zero potriviri** pe „disc". - Singurele doua `.fr2` cu „DISCOUNT" sunt de NIR, nu de facturi emise. Se tipareste `pretftva` si - `valftva`, **deja nete de discount** (`prelucreaza_factura`, `ofacturare_comun.prg:1055-1059`, - `:1156-1248`, in cursorul `crsfacttemp`). -- **eFactura foloseste exact acelasi cursor** (`xmlefactura.prg`) — `LineExtensionAmount` si - `PriceAmount` sunt aceleasi valori nete, cu optiunea (dezactivata implicit) de a adauga si un - `cac:AllowanceCharge` informativ pe linie. -- **SAF-T (D406) nu exista in ROAFACTURARE** — doar tabele de nomenclator cu prefix `saft_` (coduri - TVA / plata), pe partea de achizitii, fara legatura cu `DISCOUNT_UNITAR`. -- **Deci ingrijorarea initiala („utilizatorul schimba totalul fara sa se vada pe hartie") era gresit - tintita:** discountul nu se vede pe hartie **prin design**, iar totalul se vede corect pentru ca - pretul tiparit e deja net. - -**PROIECTAT (runda 12) — `docs\cercetare\s4c_discount_in_grid.md`.** Implementabila, cu **capcana -retintita**: formularea de mai jos, din rundele anterioare, era in acelasi timp prea alarmista si prea -vaga. - -- **Baza de date nu e in pericol.** `do_scrie_articole` trimite spre Oracle **direct** din - `discountftva` / `discountctva` / `vdiscountftva` / `vdiscountctva` (`ofacturare.vc2:14081-14083`, - ales pe `cu_tva` si `tip_valuta`) — verificat pe cod, nu presupus. Deci - `VANZARI_DETALII.DISCOUNT_UNITAR` iese **mereu corect**, indiferent de starea campurilor agregate. -- **Riscul e strict local, si e o inconsistenta, nu o valoare veche.** `valdiminuatftva` / - `valdiminuatctva` sunt agregate citite de `prelucreaza_factura` **din acelasi `crsfactura` din - memorie**, nereincarcat din Oracle. Netratate, factura tiparita poate iesi cu `pretftva` corect si - `valftva` inconsistent — **pret x cantitate diferit de valoare**, ceea ce se vede pe hartie. -- **Exista deja azi calea care demonstreaza gaura:** editarea `vdiscountftva` in grid, pe factura in - valuta, **nu declanseaza recalculul** agregatelor (`discount_verificare2.md`, punctul 3). -- **Calculul e deja generic si refolosibil**, nu trebuie rescris: `do_calculeaza_discount` - (`ofacturare.vc2:1874-1976`) plus `calculeaza_totaluri()` (`oproceduri_facturare.prg:2258-2381`), - care ruleaza pe `Scatter Name`, **fara dialog**. -- **Evenimentul recomandat pe coloanele noi e `Text1.LostFocus`**, nu `InteractiveChange` sau `Valid` — - tiparul e deja folosit in acelasi fisier, pe `frm_avizare_lucrare.grd_articole.cCantitate` / - `cPret` (`:6549-6562`). -- **Punctul deschis din S1 e inchis in trecere:** `_checkbox1` si `chkDetaliat` sunt **doua controale - distincte** in `frm_alte_date`, nu o duplicare — subiectul nu are legatura cu S4c. - -*Gata cand:* tastarea in oricare din cele doua coloane produce aceleasi valori in `crsfactura` ca -dialogul de azi, pe ambele monede; totalurile se refac imediat; discountul venit din politica de pret -se comporta ca azi; **si `valdiminuatftva` / `valdiminuatctva` se recalculeaza la fiecare editare**, -astfel incat pe factura tiparita `pret x cantitate` sa dea exact valoarea — verificat prin retiparire -si prin XML-ul de eFactura, nu doar pe ecran. Pasii cu criterii verificabile sunt la sectiunea 7 a -raportului; ce nu se poate testa headless, la sectiunea 9. -*Depinde de:* S4. - -#### S4d — Data cursului valutar, numai cand are sens -Decizia 15, proiectata in M. Campul apare cand tipul nu e retur **si** (documentul e in valuta **sau** -a intrat pe grid macar un articol cu pret in valuta) — evaluat reactiv, ceea ce devine posibil abia -in formularul unificat. -**VERIFICAT — S4d e deblocata.** Raport: `docs\cercetare\zi_curs_validare.md`. Ascunderea selectorului -**nu** poate lasa documentul fara curs, cu o conditie deja indeplinita de cod: - -- **Implicitul exista si e neconditionat.** `poDate.zi_curs` primeste data documentului chiar in - `oDateFactura.Init` / `Reset` (`COMUN\programe\ofacturare_comun.prg:247`, `:496`), **inainte** ca - formularul sa decida ce ascunde. **Nicaieri codul nu goleste `zi_curs` cand controlul e ascuns.** - Deci „valoarea implicita ramane" nu e ceva de construit — e comportamentul actual, de **nestricat**. -- **Precedentul cerut exista deja**, in alt formular: `frm_date_factura`, tipurile 8 / 9 (retur), unde - `clb_zi_curs` se elimina **neconditionat** si documentul se salveaza corect — in principal pentru ca - `cursor_retur` nici nu foloseste `poDate.zi_curs`. -- **Linia `:8076` nu e in formularul de factura.** Apartine lui `frm_date_aviz_lucrare` - (`inainte_de_do_termin`, `ofacturare.vc2:8054-8119`), un formular restrans pentru **aviz pe lucrare / - aviz pe NIR** (tipurile 27 si 30), care nici nu are control de valuta. Valideaza `zi_curs` pentru ca - tipul 27 are nevoie de curs pentru articolele din comanda, **independent de `poDate.in_valuta`** — - deci nu e o inconsecventa de reparat orbeste, are un motiv. Se aliniaza doar daca formularul unificat - preia si tipurile 27 / 30. - -**Riscul real e in alta parte, si nu tine de vizibilitatea campului:** -`pack_facturare.verifica_cursuri_valute` (chemata din `cursor_preturi`) ruleaza **neconditionat de -`in_valuta`** si exclude doar moneda nationala. Deci un `zi_curs` implicit (azi) fara curs setat in -`CURS` pentru o valuta prezenta in listele de preturi ale utilizatorului **poate pica oricum** -(`-20005`, „Nu este setat cursul…"), indiferent daca selectorul e vizibil sau nu. **Ascunderea nu -introduce riscul asta si nici nu-l rezolva** — dar il face mai greu de inteles pentru operator, care nu -mai vede campul din cauza caruia primeste eroarea. De tratat in mesajul de eroare, nu in vizibilitate. - -**PROIECTAT (runda 12) — `docs\cercetare\s4d_zi_curs_reactiv.md`.** Nu se inventeaza nimic; se -reevalueaza vizibilitatea unui control care exista deja. - -- **Cheia reactivitatii e un camp deja prezent in cursorul gridului: `crsfactura.tip_valuta`**, - interogabil cu **exact tiparul deja folosit in cod** (`ofacturare.prg:1656`, `:1730`: - `Select Distinct ... From crsfactura Where tip_valuta = 1`). Nu e nevoie de o structura noua. -- **Prototipul are deja `Clb_zi_curs` propriu, editabil, legat la `poDate.zi_curs`** - (`ofacturare.vc2:16285-16305`) — se schimba doar **cand** e vizibil. -- **Mesajul `-20005` contine DEJA data si numele valutei lipsa** (`STRINGAGG` peste toate valutele fara - curs) — deci partea de „sa spuna care valuta si ce zi" nu e de construit. **Decizia 42 nu schimba - continutul mesajului, ii schimba domeniul**: il restrange la valuta articolului cautat, dar **numai pe - varianta filtrata** (S4 punctul 1); pe caile cu document sursa incarcarea in masa ramane - neconditionata, deci acolo eroarea poate inca numi o valuta straina de documentul curent. -- **Ce lipseste cu adevarat, si intra ca pas de implementare, nu ca optiune:** sectiunea M cere ca la - aceasta eroare campul sa **revina vizibil** — altfel operatorul primeste o eroare despre un camp pe - care nu-l vede. -- **#16 nu e in perimetrul S4d.** E legat de S2 (bucla de reincercare din `ofacturare.prg`), cu verdict - separat in `s3_portare_antet.md`. S4d doar **confirma** ca antetul persistent ii inlatura mecanismul, - cu conditia ca S2/S3 sa nu recreeze formularul la eroare. -- ~~**De confirmat cu Marius (mic, de UX):** ce se intampla cand se sterge **ultimul** articol in - valuta?~~ **INCHIS — decizia 47 (Marius, runda 13): simetrie.** Campul **se ascunde la loc** cand - dispare ultimul articol in valuta, pe tiparul `lb_cursuri`. „Clipitul" la adaugari/stergeri repetate - e acceptat ca pret al unei reguli unice: aparitia si disparitia urmeaza aceeasi conditie, nu doua. - -*Gata cand:* o factura in lei fara articole in valuta nu arata campul si se emite corect; aceeasi -factura, dupa adaugarea unui articol cu pret in valuta, arata campul cu data implicita completata; -factura in valuta se comporta ca azi; iar la `-20005` **campul redevine vizibil**, cu mesajul de azi -(care deja numeste valuta si ziua). Pasii cu criterii verificabile sunt la sectiunea 7 a raportului; ce -nu se poate testa headless, la sectiunea 9. -*Depinde de:* S4. **#16 se urmareste in S2/S3, nu aici** — vezi M. - -#### S4e — Lista de preturi disponibila si pe factura din comanda -Decizia 16, proiectata in J. Singurul gol real: pe contract merge deja, pe comanda nu, pentru ca -`cursor_comanda` umple `crsarticole` doar cu articolele comenzii. Se adauga lista de preturi peste -cursorul sursei, exact ca la copiere (`ofacturare.prg:454-473`), si optiunea „Cauta in lista de -preturi…” intra in meniu pe toate sursele. -**Si stergerea intra aici**, nu doar adaugarea: cazul cerut e „clientul mai vrea ceva sau vrea sa -schimbe”, deci o linie adaugata trebuie sa poata fi si scoasa. -**DECIS (decizia 29): stergerea unei linii venite din comanda ramane fara protectie.** Se sterge ca -oricare alta; comanda ramane cu cantitatea nefacturata si va aparea **facturata partial**, ceea ce e -si starea corecta. Nu se cere confirmare, nu se marcheaza „refuzat", nu se ajusteaza numararea -acoperirii. -Ramane un singur efect lateral de tratat, si e pe **adaugare**, nu pe stergere: capul de coloana -„Cantitate comandata” si mesajul „A fost facturata intreaga cantitate comandata” -(`ofacturare.vc2:15144-15150`) nu sunt adevarate pentru liniile libere adaugate langa cele din -comanda. -**PROIECTAT (runda 12) — `docs\cercetare\s4e_lista_preturi_pe_sursa.md`. Reteta din decizia 16 NU se -generalizeaza literal** — si asta e rezultatul principal al proiectarii. - -- **`APPEND FROM` peste cursorul sursei ar strica doua lucruri pe comanda**, ambele tacut: - **(1)** `id_c` e `ROWNUM` per executie Oracle, deci randurile din lista de preturi ar **coliziona** cu - cele ale comenzii, iar `do_sterge` ajusteaza cantitatea `For id_c = poArticol.id_c` (`:14652-14655`) — - ar atinge randul gresit; **(2)** `do_scrie_factura` face `Sum(cantitate)` peste `crsarticole` pe exact - aceste tipuri (`:14332-14338`), iar in `cursor_preturi` **`cantitate` inseamna STOC**, nu „ramas de - facturat" — suma care decide inchiderea comenzii ar fi poluata. -- **De ce merge totusi pe contract azi**, verificat: lista de preturi si articolele contractului sunt - **doua cursoare separate** de la bun inceput (`crsarticole` / `crsarticole1`), iar `cursor_contract` - emite `id_c` ca **`rownum - 10000`** (`PACK_FACTURARE:2722` — confirmat direct pe export de sesiunea - principala), adica autorii au tratat coliziunea de `id_c` ca risc real. In plus, tipurile de contract - **nu au deloc** ramura cu `Sum(cantitate)` in `do_scrie_factura` — cad pe `Otherwise`. Nu e un tipar - de copiat, e o coincidenta favorabila. -- **Solutia curata, verificata fezabila pe cod: liniile libere NU intra deloc in `crsarticole`.** Se - adauga direct in `crsfactura` prin `APPEND BLANK` + `combosql` — tipar **deja existent** in - `frm_facturare_articole2.do_adauga` si pe linia de discount (`ofacturare.vc2:14531`). Atunci `id_c` - ramane `0` implicit, si **`do_sterge` devine no-op prin constructie**, fara nicio modificare de cod. -- **Se aplica identic pe AVIZE (tip 4), nu doar pe comanda** — `cursor_avize` are aceeasi semantica - „ramas de facturat" si aceeasi ramura de `Sum`. Titlul povestii spune „din comanda", perimetrul real - e „orice sursa cu registru". -- **Avertisment care traverseaza in S4f:** pe returul ca document (`8,9,24`) riscul de coliziune `id_c` - **ramane** (`do_sterge` ajusteaza cu semn opus, urmarind maximul returnabil), desi **fara** poluarea - sumei de inchidere. Deci S4f foloseste acelasi mecanism, nu `APPEND FROM`. -- **Validarea de cantitate pe liniile din comanda e satisfacuta prin constructie** — drumul - `do_adauga_articol` → `do_verifica_articol` nu e atins, fiindca liniile libere nu modifica `crsarticole`. -- **Gol real ramas, neacoperit de S4 si S4b:** nu exista mecanism de validare a cantitatii/stocului - pentru un rand ales prin `combosql` — ambele rapoarte se opresc la maparea campurilor. `do_verifica_articol` - nu se poate refolosi ca atare (cere un `poArticol` scatter-uit dintr-un cursor sursa incarcat). - **DECIS — decizia 44 (Marius, runda 13): `poArticol` devine parametru explicit.** Se pastreaza un - **singur loc de validare**: `do_verifica_articol` primeste `poArticol` ca **parametru**, in loc sa - citeasca variabila `Private` populata de apelant. Schimbarea de contract a metodei e **aprobata ca - atare**; consecinta obligatorie e actualizarea **tuturor apelantilor existenti**, inventariati inainte - de prima editare. Aceeasi decizie acopera si `do_alege_stoc` / `frm_articol_gest_factura` din S4f (R7) - — o singura solutie pentru amandoua, cum cerea raportul. - -*Gata cand:* pe o factura la comanda **si pe una din avize** se poate adauga un articol care nu e in -sursa, cu pretul din lista de preturi, si se poate sterge o linie adaugata, fara ca liniile din sursa -sa-si piarda validarea de cantitate **si fara ca suma care decide inchiderea automata sa se schimbe** — -proba directa: aceeasi comanda, cu si fara linii libere adaugate, produce acelasi rezultat de inchidere. -*Depinde de:* S2. **Interactioneaza cu S4 punctul 2** (registrul) si **avertizeaza S4f**. - -#### S4f — Returul in formularul unificat -Decizia 17, proiectata in N. **Partea grea nu e ce credeam.** Factura de retur ca document (tipurile -8, 9, 24) functioneaza deja cap-coada: alegere multipla a facturilor sursa, populare din -`cursor_retur`, gestiune si pret de achizitie mostenite din liniile originale, stergere si retur -partial. Munca e: - -1. **mutarea lui N.1 ca sursa in meniul de adaugare**, fara sa se atinga popularea — acelasi dialog, - aceleasi filtre, acelasi `cursor_retur`; -2. **ridicarea lui N.2 (`But_retur`) la nivel de document**, dupa modelul lui N.1, cu calea - per-articol pastrata; -3. **lista de preturi pe documentele de retur**, prin acelasi `APPEND FROM` ca la S4e. - -Nu se ating: excluderea returului dintr-un retur, filtrarea pe client si valuta, maximul returnabil -calculat pe server, mostenirea gestiunii si a pretului de achizitie. -**DECIS (decizia 22):** linia de retur care nu vine din nicio factura originala **e permisa**, ca pe -orice alt document. Deci punctul 3 nu mai are conditie de intrare. Ce trebuie proiectat in schimb, pe -liniile libere: gestiunea si pretul de achizitie **se aleg** (ca la N.2), nu se mostenesc, iar maximul -returnabil calculat pe server **nu se aplica** — nu exista cantitate originala. -**Afisarea provenientei — VERIFICAT, si raspunsul e „nu la nivel de linie".** Raport: -`docs\cercetare\legatura_linie_retur.md`. Deci **S4f se livreaza fara coloana de provenienta pe linie**; -nu se inventeaza. Ce s-a stabilit, ca sa nu se reia: - -- **Legatura se pierde chiar in cursorul care aduce datele.** `cursor_retur_document` - (`ff_...:3949-4062`) foloseste `V_LISTAID` **doar ca filtru** (`WHERE A1.ID_VANZARE IN (...)`, - `:4054-4055`); in lista de coloane a `SELECT`-ului extern (`:3965-4028`) **nu apar nici - `ID_VANZARE`, nici `ID_VANZARE_DET`**. `crsarticole` nu are de unde sti din ce factura vine randul. -- **`INSERT`-ul in `VANZARI_DETALII` nu are nicio coloana de sursa.** -- **Exista insa o legatura la nivel de DOCUMENT, si nu era cunoscuta in plan: `VANZARI_CORESP`.** - `scrie_corespondente_vanzari(3)` (`ff_...:14834-14836`, din `finalizeaza_factura`) scrie - `(ID_VANZARE_FACT = documentul de retur, ID_VANZARE_AVIZ = fiecare factura sursa, TIP = 3)` - (`:15481-15516`) — **cate un rand per factura sursa aleasa**, nu per linie. Numele coloanei e generic, - reutilizat si pentru perechi aviz-factura (`TIP = 1/2`). -- **Consecinta pentru UI:** cand s-a ales **o singura** factura sursa, se poate afisa corect „documentul - asta de retur provine din factura Y" — la nivel de antet, nu de linie. Cand s-au ales **mai multe** - (selectie multipla, suportata explicit de dialog), `VANZARI_CORESP` da **multimea** de facturi - posibile, deci nici macar antetul nu poate arata o sursa unica. **De asezat in UI ca informatie de - document, niciodata ca proprietate de linie.** -- **N.2 (`But_retur`) nu scrie nicio legatura.** `listaid` (perechi `ID_ARTICOL:ID_VANZARE`) e folosit - in `pack_facturare` (`ff_...:8142-8212`) exclusiv ca filtru pe `RUL`, ca sa calculeze cantitatea inca - disponibila din acea vanzare. Sursa nu ramane atribut al liniei noi. -**PROIECTAT (runda 12) — `docs\cercetare\s4f_retur_formular_unificat.md`**, cu **trei corectii** fata de -textul de mai sus: - -- **Punctul 1 se restrange.** Alegerea facturilor sursa **nu se muta** in bara de butoane — ramane la - antet / in sectiunea pliata (confirmat impotriva `s4b_bara_butoane_meniu.md` §9.3 si a tabelului de - meniu de acolo). Ce se muta in meniul de adaugare e doar **„adauga tot din facturile alese"** si - **„alege liniile"**. -- **Un risc concret, netratat nicaieri pana acum (R1):** `frm_articol_gest_factura` - (`ofacturare.vc2:4094-4103`) face, sub `If Thisform.lRetur`, `poArticol.pretftva = pretv` — adica - **suprascrie pretul de vanzare cu pretul din stoc pe ORICE linie de retur** (verificat direct de - sesiunea principala). Corect pentru liniile **mostenite** dintr-o factura sursa; **gresit pentru - liniile libere** permise de decizia 22, care trebuie sa pastreze pretul din lista de preturi. - Raportul propune fixul. -- **Distinctia „linie libera" vs. „linie mostenita" nu cere camp nou: `crsfactura.id_c = 0`.** Oracle nu - produce niciodata `id_c = 0` din `ROWNUM`, deci valoarea implicita e ea insasi semnalul. Vine din - mecanismul impus de S4e: liniile libere intra direct in `crsfactura` (`APPEND BLANK` + `combosql`), - **nu** in `crsarticole` — asa ca ajustarea din `do_sterge` (care pe retur urmareste maximul returnabil) - devine **no-op prin constructie**. Acelasi semnal dezactiveaza si suprascrierea de pret de mai sus. -- **Gol nou (R7), pe care S4e nu-l are** (comanda si avizele n-au dialog de gestiune): azi se intra in - `do_alege_stoc` / `frm_articol_gest_factura` **doar** dintr-un `poArticol` scatter-uit din - `crsarticole`. O linie libera din `crsfactura` nu are asa ceva, iar decizia 22 cere ca gestiunea **sa - se aleaga**. Deci trebuie un **punct de intrare nou** in dialog. **Se unifica cu golul echivalent din - S4e** (validarea cantitatii, `do_verifica_articol`): e aceeasi problema structurala — dialogurile sunt - cuplate de `crsarticole` —, deci merita **o singura solutie**, nu doua. - **INCHIS prin decizia 44** (runda 13): `poArticol` devine **parametru explicit** si aici, nu variabila - `Private` populata de apelant — aceeasi solutie ca in S4e, cum cerea raportul. -- **R4 e inchis** (verificare independenta): `scrie_corespondente_vanzari(3)` e gatata pe `ntip IN (8,9)`, - nu pe `listaid`. -- **`VANZARI_CORESP` nu e afectata de liniile libere** — se scrie din `poDate.listaid`, fixat la antet. - **De confirmat (R4):** pentru `But_retur` ridicat la nivel de document, raspunsul pare a fi „fara - corespondenta persistata", pentru ca scrierea e gatata pe `ntip IN (8,9)`, nu pe `listaid`. - -*Gata cand:* un document de retur deschis in formularul unificat aduce liniile facturilor alese cu -aceleasi valori ca azi — inclusiv gestiunea si pretul de achizitie —, permite stergere si cantitate -partiala, iar pe o factura normala se poate face retur alegand facturile o singura data. **In plus: o -linie libera pe un document de retur isi pastreaza pretul din lista de preturi**, adica nu trece prin -suprascrierea de la `:4094-4103`. Pasii cu criterii verificabile sunt la sectiunea 9 a raportului; ce nu -se poate testa headless, la sectiunea 11; riscurile R1-R6, la sectiunea 12. -*Depinde de:* S2, S4e. - -#### S4g — Adaugarea de articole la modificarea oricarui document, inclusiv auto -Decizia 18, proiectata in O si O-bis. **Nu se porneste de la zero:** „Alte servicii” din ROAAUTO -face deja jumatate — articole reale langa linii sintetice, cu pret tastat, fara gestiune — doar ca -traieste in formularul de emitere si moare odata cu el. Se generalizeaza in formularul unificat, ca -a doua sursa din meniu („Alege din nomenclator…”), disponibila **si la modificare**, pe orice tip de -document, nu doar pe cele auto. -**Ordinea in care se lucreaza**, ca sa nu se blocheze tot: intai pe documentele ROAFACTURARE, unde -scrierea e a noastra; abia apoi pe `tip = -12`. -**Decizia 20 se aplica aici:** din nomenclator se ofera **si articole gestionabile, si -negestionabile** — nu se copiaza filtrul `in_stoc = 0` al lui ROAAUTO. -**Blocantul real e contul de venit, si el priveste doar ramura „Alege din nomenclator…”** (J-bis, -J-ter). Contul de venit vine din `NOTE_CONTABILE.SCC` **prin politica de pret**; un articol fara -`ID_POL` nu ajunge la cont gol, ci **la eroare** — `contabilizeaza_articol` ridica `FACT-024` si -opreste tranzactia. Pe ramura „Cauta in lista de preturi…” nu se schimba nimic: acolo politica exista. -**Premisa de la care pornea povestea asta a cazut:** „Alte servicii” din ROAAUTO **nu face deja -jumatate** din treaba — acele linii ocolesc complet `contabilizeaza_articol`, printr-o cale de -facturare paralela care nu genereaza nota de venit. Nu e un mecanism de generalizat, e o exceptie. -**DECIS (decizia 27, care inlocuieste 24):** contul de venit se **deriva**, nu se ia prin politica — -din `CORESP_CONT_VENCHELT` pentru articolele gestionabile (pe contul de gestiune al liniei), din -`NOM_ARTICOLE.CONT` daca e 6xx / 7xx pentru cele negestionabile, altfel **704**. **Derivarea se face -in VFP**, dar **transportul s-a schimbat la decizia 34**: contul calculat se trimite **direct, ca -parametru nou al lui `contabilizeaza_articol`**. Ocolul prin `id_pol` cu nota potrivita — politica -tehnica, interogarea inversa pe `SCC`, `pack_preturi.adauga_politica_pret_art` — e **abandonat**; -J-quater punctul 3 se citeste doar ca trasabilitate. **Decizia 35** adauga ca acelasi drum serveste si -editarea prin regenerare, deci nu se proiecteaza aici o a doua ruta de contare. -~~**De decis in aceasta poveste, nu inainte:** ce se intampla cand linia are **si** politica, **si** -cont trimis din VFP — cine castiga.~~ **PROIECTAT (runda 13) — -`docs\cercetare\s4g_adaugare_articole_modificare.md`.** Intrebarile despre politica tehnica in ecranul -de cautare a politicilor **au disparut** odata cu reteta. - -**Raspunsul: niciuna dintre cele trei variante pure — parametrul castiga, dar combinatia ambigua e -oprita explicit, nu rezolvata tacit.** -- **Combinatia nu poate aparea in fluxul normal, si asta e dovedit, nu presupus.** `crsfactura.id_pol` - e `N(20) Null` (`COMUN\programe\ofacturare_comun.prg`, `creeaza_facturacrs` — **verificat direct**), - deci la `APPEND BLANK` ramane `.NULL.`, si ajunge la Oracle ca literalul `NULL` - (`ofacturare.vc2:14072`). Cursorul de cautare din nomenclator **n-are deloc coloana `id_pol`**, deci - nu exista punct in care o linie „din nomenclator" sa-l poata popula. -- **Singurul scenariu real de ambiguitate e regenerarea** (decizia 35): daca derivarea contului ar rula - **necondiționat** pe toate liniile documentului, si nu doar pe cele fara `id_pol`, o linie cu politica - reala ar primi si cont derivat. **E un risc de implementare VFP, nu Oracle** — dar proiectarea Oracle - nu trebuie sa-l faca invizibil. -- **Deci: `cont_venit IS NOT NULL` intra pe ramura noua; daca in acel moment `id_pol` e si el populat, - se ridica eroare** (cod nou, distinct de `FACT-024`, care ramane pentru cazul „nici politica, nici - cont"). Variantele „politica castiga" si „fallback la `NO_DATA_FOUND`" au fost respinse motivat: - prima defineste castigatorul pe **prezenta** campului, nu pe rezolvarea lui, si ar reintroduce - `FACT-024` acolo unde VFP a oferit deja o solutie; a doua e semantic cea mai curata, dar cere - **restructurarea interna** a functiei (un flag propagat din `EXCEPTION` pana la punctul de decizie), - fata de un singur `IF` la intrarea in ramura deja proiectata. **Ambele ascund o eroare de date in loc - s-o semnaleze.** -- **Regresie zero pe apelantii de azi**, prin constructie: cand `cont_venit` e `NULL` — adica toti - apelantii existenti, care nu cunosc parametrul —, executia intra direct pe `ELSE`, garda nu se - evalueaza niciodata, comportamentul e identic cu cel de azi. -- **De ales de Marius:** numarul concret al codului de eroare nou (`FACT-0xx`). -Separat, contul de **gestiune** e rezolvat ca regula (decizia 21): fallback, nu `NULL`, nu refuz. -**`pack_auto` nu mai e blocant** — `PACK_AUTO` nu citeste `VANZARI` / `VANZARI_DETALII` deloc (O, -intrebarea 1). Desincronizarea de afisare e **acceptata** (decizia 28); conditia e MANOPERA si -MATERIALE conform devizului. -**Blocantul netehnic a cazut si el:** gridul read-only din `frm_modific2024` e al lui #6, dar #13 -incepe dupa ce #6 se termina (decizia 30). -**Restul proiectarii S4g, pe scurt** (detaliile in raport): -- **Suprafata pe Oracle**: `contabilizeaza_articol` primeste contul prin `VANZARI_DETALII_TEMP%ROWTYPE` - (`cont_venit`), deci semnatura functiei ramane aceeasi — se extinde **tipul de rand**. Consecinta de - livrare: **DB inainte de EXE**. -- **Derivarea in VFP** urmeaza fix decizia 27: `CORESP_CONT_VENCHELT` pentru gestionabile (pe contul de - gestiune al liniei), `NOM_ARTICOLE.CONT` daca e 6xx/7xx pentru negestionabile, altfel **704**. Ruleaza - **doar pe liniile fara `id_pol`** — vezi garda de mai sus; aici se leaga cele doua. -- **Fluxul in formular** refoloseste exact mecanismul lui S4e: linia „din nomenclator" intra prin - `APPEND BLANK` + `combosql` **direct in `crsfactura`**, niciodata in `crsarticole`. -- **Ramane deschis, mostenit din S4e:** validarea de cantitate/stoc pentru linia libera — se rezolva - prin decizia 44 (`poArticol` ca parametru explicit), aceeasi solutie pe ambele surse. -- **De re-rulat inainte de implementare, nu de presupus incheiat:** cautarea apelantilor lui - `adauga_articol_factura` in restul suitei (ROAGEST, ROAAUTO, ROAACNPRO, ROAIMOB). Risc asteptat zero - — folosesc proceduri separate sau alt pachet —, dar cautarea **nu s-a terminat in nicio runda**. -- **Trei hardcodari raman de confirmat de Marius, nu de presupus inofensive:** `CU_TVA = 1` (are efect - masurat prin `nproc_tva_max`, pe linie scutita + discount global), numele cheii de optiune de firma - pentru `SCD` (decizia 36) si domeniul ei `PROGRAME`, si trasabilitatea `CONT_VENIT` pe - `VANZARI_DETALII` (optionala pentru functionare, dar fara ea coloana nu se pastreaza dupa fapt). - -**Nota de executie (decizia 60):** pe documentele de tip **48/49** (custodie), „Alege din -nomenclator…" ramane restrans la articole `IN_STOC = 0`, la fel ca ramura „Cauta in lista de -preturi…" (S4) — decizia 20 (si articole gestionabile, si negestionabile) **nu se aplica** pe -tipurile 48/49. Altfel se rupe invariantul pe care se sprijina siguranta regenerarii la editarea -custodiei (decizia 60, `docs\cercetare\custodie_48_49_stergere_reemitere.md`). - -*Gata cand:* pe un document deja emis se poate adauga o linie noua, din lista de preturi sau din -nomenclator, si documentul se salveaza consistent — pe fluxurile ROAFACTURARE, cu partea auto -livrata separat, dupa (1). Pe un document 48/49, „Alege din nomenclator…" nu ofera si nu permite -adaugarea unui articol cu `IN_STOC <> 0`. -*Depinde de:* S4e, si de deciziile de mai sus. **Nu blocheaza etapa I.** - -#### S5 — Acoperirea tuturor tipurilor -`frm_facturare_articole.Init` are 20 de ramuri pe `poDate.tip` (`:15107-15250`) care schimba titlul, -capul coloanei de cantitate, mesajul de stoc, vizibilitatea discountului, prezenta seriei. Toate -trebuie sa existe si in formularul unificat. Tipurile speciale raman pe calea lor: -`tip = 27` (`frm_avizare_lucrare`), `tip = 30` (aviz din NIR, formular nevizibil). -**PROIECTAT (runda 12) — `docs\cercetare\s5_acoperire_tipuri.md`. Povestea e mult mai mare decat -„porteaza cele 20 de ramuri", si motivul e ca baza de pornire aleasa nu are aproape nimic de portat.** - -- **`Do Case`-ul de azi acopera 21 de valori de `tip`** (in 14 ramuri). **Patru tipuri reale, - reachable prin `factureaza()`, nu intra in niciun `Case`: `45` (factura restaurant), `48` / `49` - (custodie), `52` (contract, factura fiscala valuta)** — pierd titlu, cap de coloana, mesaj de stoc, - vizibilitatea discountului si eliminarea coloanei `cSerie`. **Nu doar `52`**, cum semnalase S4b. - Rutarea cursorului le recunoaste (`ofacturare.prg:271-282`); doar `Init` nu le-a „prins" niciodata. - **Nota de executie (decizia 60):** randul de configurare pentru `48`/`49` trebuie sa pastreze - restrictia la articole `IN_STOC = 0` mostenita de la sursa lor de azi (`cursor_articole_k`, - `PACK:3595-3701`, `:3695`) — vezi notele echivalente la S4 si S4g; e conditia de siguranta pentru - regenerarea la editarea custodiei (decizia 60, - `docs\cercetare\custodie_48_49_stergere_reemitere.md`). -- **Cinci tipuri (`43,44,46,50,51`) nu ajung deloc la acest formular** — zero potriviri in tot arborele - `D:\ROA`. `50` e marcat „in lucru" in sursa. Se declara explicit ramase in afara. -- **Descoperirea care schimba estimarea: prototipul nu e o versiune partiala a `Do Case`-ului — e - aproape gol.** `frm_facturare_articole2.Init` (`:18988-19080`) alege pe tip **doar** cuvantul - „factura"/„aviz", pe o lista mai scurta (lipseste `24`). Nu seteaza titlu (nu exista - `lb_titlu_alb_b121` in tot prototipul), nu schimba capul coloanei, nu schimba mesajul de stoc, nu - ascunde discountul, si **n-are deloc conceptul de coloana `cSerie`**. Daca formularul unificat porneste - de la prototip — cum decide S1 —, **toata diferentierea pe tip se reconstruieste de la zero**, nu se - completeaza. Asta e cel mai mare cost ascuns al etapei I descoperit pana acum. -- **Rutarea cursorului diverge pe TREI tipuri, nu unul:** pe prototip, ramura de contract e - `Inlist(tnTip, 2, 26, 6)` (`ofacturare.prg:762`) fata de `Inlist(tnTip, 2, 26, 6, 52)` pe standard - (`:283`) — **verificat direct de sesiunea principala** —, iar ramura de retur omite `24`. Pe aceste - tipuri, prin prototip, `lcSqlCursor` ar ramane **nedefinit**: eroare, nu doar comportament diferit. -- **Punctul lasat deschis de S4 punctul 2 se inchide aici:** tipurile **`26` si `52` n-au niciun - bookkeeping** `crsarticole` — nici Rol A, nici Rol B. Excluderea e totala, pe `Do Case` exhaustiv fara - ramura implicita, deci e „dovedit absent", nu „neconfirmat". -- **`30` nu e un formular separat**, cum spunea planul: e **acelasi `frm_facturare_articole`**, trecut - prin acelasi `Init`, dar niciodata aratat (`ofacturare.prg:444-453` — calculeaza totalurile, apasa - programatic `but_termin1.Click()`, apoi `Release()`, fara `Show()`). Deci **e afectat de golurile din - `Do Case`** ca oricare alt tip; doar ca defectele nu se vad pe ecran. `27` chiar ramane pe calea lui. -- **Alegerea de proiectare centrala, recomandata: tabel de configurare per tip**, un rand per `tip`, cu - exact proprietatile pe care le seteaza azi `Do Case`-ul (titlu, cap cantitate, mesaj stoc, discount - vizibil, are serie, tip doc, butoane, grup-sursa pentru meniul S4b). Motivul nu e estetic: **un - `Do Case` fara `Otherwise` nu semnaleaza niciodata un tip lipsa** — exact mecanismul care a lasat - patru tipuri pierdute ani la rand. Cu tabel, un tip necunoscut devine **eroare la deschidere**, iar - completitudinea se verifica **mecanic**, cu un `SELECT` fata de `tipuri_documente_facturare.md`, fara - sa porneasca formularul. Variantele „completeaza `Do Case`-ul" si „metoda per grup" au fost respinse - motivat: amandoua raman implicite, deci nu adauga detectie. -- **DECIS — decizia 48 (Marius, runda 13): tabelul e un CURSOR GENERAT IN COD la pornire**, nu `DBF` - static (recomandarea raportului) si nu tabela pe Oracle. Ambele au fost puse pe masa cu argumentele - lor si respinse: `DBF`-ul adauga un fisier de intretinut, de livrat la fiecare update si de tinut - sincron intre produsele ROA; tabela Oracle ar cere script de migrare si o citire in plus la - deschiderea formularului. **Ce NU se pierde prin alegerea asta:** detectia ramane intacta — tip - necunoscut = **eroare la deschidere**, exact castigul pentru care exista propunerea —, iar proba - mecanica de completitudine ramane posibila, doar ca ruleaza **din aplicatie**, nu cu un `SELECT` - din afara. **Ce se accepta:** orice tip nou de document cere **recompilare si versiune noua de exe**, - ca azi. Forma concreta: un `.prg` cu `CREATE CURSOR` + `INSERT`-uri, un rand per tip, incarcat o - singura data si citit de `Init` prin `SEEK` pe `tip`. - -*Gata cand:* fiecare tip din `COMUN\docs\tipuri_documente_facturare.md` fie e acoperit, fie e -declarat explicit ramas pe calea veche — **verificat prin proba mecanica de la sectiunea 8 a raportului**, -nu prin citire. Include cele patru tipuri lipsa azi (`45,48,49,52`) si alinierea rutarii de cursor intre -standard si prototip. Pe `48`/`49`, in plus: restrictia `IN_STOC = 0` mostenita ramane activa (decizia -60) — nu se poate adauga un articol gestionabil. -*Depinde de:* S4. - -#### S5b — Proforma si copierea pe formularul unificat -Deciziile 10 si 11. Amandoua vin **aproape gratuit**, pentru ca folosesc deja acest drum: proforma e -o valoare in combo-ul de tip document, copierea e `factureaza(tip, toFactura)` cu antetul -precompletat. De facut, concret: -- combo-ul `Ct_clb_fdoc` ramane in antetul unificat, cu realocarea de serie si numar la comutare; -- garzile `eProforma = 0` de pe notele contabile si atasamente raman intacte, iar raportul propriu de - proforma continua sa fie ales; -- copierea deschide formularul in starea „document nou”, cu antetul editabil (comportamentul de azi); -- **degradarea de tip din `do_copiaza` ramane pentru copiere si nu se aplica la regenerare** (D) — - sunt doua moduri distincte ale aceluiasi formular, nu acelasi comportament. -*Gata cand:* o proforma emisa din formularul unificat nu produce nota contabila si se listeaza pe -raportul ei; o copie produce un document nou cu numar nou si acelasi continut. -**VERIFICAT (runda 9) — `docs\cercetare\s5b_proforma_descarcare_gestiune.md`. Verificarea 4 e inchisa.** -**NU, gestiunea nu se descarca pe proforma** — pentru niciun articol, indiferent daca e gestionabil in -nomenclator. Si nu e un efect colateral al ascunderii stocului, e **prin design**: VFP marcheaza toate -liniile proformei negestionabile inainte de compunerea documentului, ceea ce le trimite cu sentinela -`id_gestiune = -1000`, iar pe Oracle `contabilizeaza_articol` sare apelul catre `descarca_gestiune` -exact pe acest sentinel. Intentia e explicita in changelog (12.03.2021 / 2.7.x): *„Articolele din -proforma sunt marcate 'negestionabile', astfel incat nu mai este necesara existenta articolelor in stoc -pentru a genera proforma."* — deci cerinta a fost „proforma trebuie sa mearga si fara stoc", nu „nu arata -plafonul". **Se corecteaza presupunerea din plan** ca zeroizarea `gestionabil` ar fi doar cosmetica. -Consecinta pentru S5b: sentinela `-1000` e contractul care trebuie pastrat — vezi sectiunile 6 si 7 ale -raportului pentru constrangerile de proiectare si ce lipseste la copiere. - -**PROIECTAT (runda 12) — `docs\cercetare\s5b_proiectare_proforma_copiere.md`. „Aproape gratuit" nu mai -tine: proiectarea a scos la iveala un risc de date care nu era semnalat nicaieri.** - -- **Mecanismul real care tine proforma curata nu e sentinela, ci routing-ul.** In `do_scrie_factura` - (`ofacturare.vc2:14282-14300`), `eProforma = 1` alege `scrie_proforma`, care **nu cheama niciodata** - `contabilizeaza_articol` — comentariul din cod o spune direct: *„salveaza doar in vanzari, nu si in - contabilitate"* (verificat de sesiunea principala). Sentinela `-1000` e al doilea strat, nu primul. -- **RISCUL CENTRAL — „drumul invers", nesemnalat pana acum si cu consecinta pe date.** Operatorul - adauga linii cu documentul pe **Proforma** (deci marcate `gestionabil = 0` / `id_gestiune = -1000`), - apoi comuta combo-ul inapoi pe **Factura** inainte de „Termina". Atunci routing-ul alege - `scrie_factura2`, care **chiar** cheama `contabilizeaza_articol` — dar acesta sare `descarca_gestiune` - exact pe sentinela `-1000` (`PACK:7472-7476`). Rezultatul: **o factura reala iese cu stocul - nedescarcat, silentios** — `-1000` e o valoare valida, nu ridica nicio exceptie. **Azi nu exista - nicio plasa pentru acest caz**, si nici nu putea exista: combo-ul traieste in dialogul separat - `frm_date_factura`, inchis **inainte** sa existe vreo linie. Riscul se naste **din unificare**. -- **De aici si de ce marcarea negestionabila nu mai poate fi un singur `UPDATE` de masa:** in - formularul unificat trebuie extrasa intr-o metoda refolosita din **trei** puncte — la incarcare, la - adaugarea unei linii, si la comutarea tipului cu linii deja prezente. -- **`id_c` la copiere: SIGUR, cu dovada.** `do_copiaza` degradeaza tipul spre grupul-tinta - **`{1,5,7,10,22,23}`**, care nu intra niciodata in ramurile Rol A din `do_scrie_factura` / `do_sterge`. - Coliziunea tehnica exista in date, dar **n-are efect observabil**. (Intrebarea venea din corectia lui - S4e — vezi acolo.) - > **CORECTIE (runda 13, verificata direct pe cod).** Grupul-tinta scris pana acum in plan - > (`{1,5,10,22}`) era **gresit**, si la fel era si `{1,5,7,10}` din raportul S5c. Setul real e cel de - > mai sus, citit din primul `CASE` al lui `frm_facturi.do_copiaza` - > (`COMUN\clase\ofacturare_comun.vc2:3693-3694`), care lasa neatinse exact `T1,T5,T7,T10,T22,T23`. - > Concluzia „copierea e sigura" **nu se schimba** — `7` si `23` nu au bookkeeping Rol A —, dar cifrele - > se corecteaza peste tot unde apar. Aceeasi corectie se aplica lui - > `s5b_proiectare_proforma_copiere.md` §2.1 / §7. -- **DEFECT PREEXISTENT, gasit in trecere la verificarea de mai sus — de raportat, NU de reparat acum.** - In `frm_facturi.do_copiaza`, ramura de avize scrie `lnTip = T22` - (`COMUN\clase\ofacturare_comun.vc2:3703`) in loc de `loFactura.tip = T22` — **singura ramura din tot - `Do Case`-ul care nu atribuie in obiect**; toate celelalte cinci scriu `loFactura.tip`. Consecinta: - la copierea unui aviz (`21,24,26,30,-7,-9,-10,-13,28,29,42`) **degradarea nu se produce**, iar - `copiere_factura` cheama `factureaza(toFactura.Tip, ...)` - (`COMUN\programe\oproceduri_facturare.prg:150-152`) cu tipul **original** — deci copia unui „aviz pe - baza de comanda" reintra pe ruta de comanda, nu pe cea de lista de preturi. **Verificat ca `factureaza` - nu citeste un `lnTip` privat** (`ofacturare.prg:101` declara `lnTipTemp`, nu `lnTip`), deci - atribuirea chiar se pierde; in plus `lnTip` e nedeclarat in metoda, deci poate suprascrie un `lnTip` - al apelantului. **Fisierul e in perimetrul interzis (#6) — se raporteaza, nu se atinge.** - -**VERIFICAT (runda 12, la cererea lui Marius) — `docs\cercetare\verif_proforma_alegere_stoc.md`. -Pe proforma NU se alege stoc azi, si asta e deliberat.** - -- **Un singur mecanism activ, si e in VFP, la incarcare:** `ofacturare.prg:333-336` face - `UPDATE (lcCursor) SET gestionabil = 0` cand `eProforma = 1`, **inainte** sa se deschida gridul. - Comentariul din cod spune intentia direct: *„Daca este o proforma, consider toate articolele - negestionabile, **pentru a nu mai alege din stoc**"* (12.03.2021). Deci `Do Case`-ul de la - `ofacturare.vc2:13803` vede mereu `gestionabil = 0` → **`do_alege_stoc` nu ruleaza niciodata**. - Valabil pe **toate** cursoarele de creare directa a unei proforme; **niciunul** n-are parametru - `V_PROFORMA` (verificat pe ~9 proceduri `cursor_` din pachet). -- **Mecanismul Oracle exista, dar e mort in fluxul curent:** `cursor_retur_document` (`PACK:3993-4000`) - are `CASE V_PROFORMA = 1 THEN 0`, insa se cheama doar la **copiere**, unde `V_PROFORMA` trimis e - `eProforma` al documentului **nou** — mereu `0`. **Nu e o contradictie intre rapoarte**: sunt doua - mecanisme reale, doar unul activ. -- **`pret_achizitie` depinde de sursa:** pe calea principala (lista de preturi) ramane `0` — - `cursor_preturi` nici nu-l selecteaza. Pe surse care carata un document existent (avize, copiere) - vine real din `VANZARI_DETALII.PRET_ACHIZITIE`. -- **Daca liniile ar pastra gestiunea reala pana la salvare, nu s-ar strica nimic pe contabilizare sau - stoc:** `adauga_articol_factura` se cheama oricum si pentru proforma, dar `scrie_proforma` **nu** - cheama `contabilizeaza_articol`, deci `descarca_gestiune` tot n-ar rula; iar **`do_alege_stoc` nu - rezerva stoc** (doar `SELECT` + scadere locala in memorie, zero scriere Oracle). Singurul loc unde - s-ar vedea o diferenta e la **relistare** (`crsDetaliiListare` / `fact_vfacturi2` citesc - `id_gestiune` fara filtru pe `eproforma`) — **neconfirmat** daca vreun raport chiar il tipareste. - -**Consecinta care schimba forma deciziei: cele doua sensuri nu sunt simetrice.** -- **FACTURA → PROFORMA cu linii deja adaugate:** liniile au trecut deja prin `do_alege_stoc`, deci au - gestiune reala si pret de achizitie corect. **E sigur, si sentinela se poate aplica abia la salvare** - — exact varianta ceruta de Marius. **Fara atentionare.** -- **PROFORMA → FACTURA cu linii deja adaugate:** liniile au fost adaugate **fara** alegere de stoc - (`gestionabil` fortat `0`), deci **nu au gestiune**. Aici e riscul „drumului invers". -- **A forta alegerea stocului si pe proforma NU e o optiune** — ar regresa cerinta din 12.03.2021 - (*„nu mai este necesara existenta articolelor in stoc pentru a genera proforma"*): un articol fara - stoc n-ar mai putea intra pe proforma, pentru ca `do_alege_stoc` ar cere un lot inexistent. - -### Decizia 43 (Marius, runda 12) — proforma NU alege stoc, si factura se face DIN proforma - -**Pe proforma nu se alege stoc, si asta e cerinta, nu efect colateral.** Motivul, in cuvintele lui -Marius: *„sa dau o proforma chiar si in absenta stocului, pentru ca ma intereseaza doar pretul de -vanzare, nu si cel de achizitie din stoc"*. Deci: -- **Varianta (b) — realegerea gestiunii linie cu linie la comutare — e RESPINSA.** Nu se mai - reargumenteaza. -- **Comutarea PROFORMA → FACTURA cu linii prezente se blocheaza**, cu mesaj explicit. Comportamentul de - azi (`gestionabil = 0` fortat la incarcare, `ofacturare.prg:333-336`) se **pastreaza**, nu se rafineaza. -- **Sensul FACTURA → PROFORMA ramane liber**, fara atentionare, cu sentinela aplicata **la salvare** — - liniile au deja gestiune reala, deci nu se pierde nimic. - -**Cerinta noua: „ulterior o sa vreau si o factura din proforma".** Nu prin comutarea tipului pe acelasi -document, ci ca **document nou**, generat din proforma. **Mecanismul pare sa existe deja pe calea de -copiere** — `cursor_retur_document` reface gestionabilitatea reala cand `V_COPIERE = 1` -(`GESTIONABIL = B.IN_STOC`, `PACK:3993-4000`), deci liniile venite dintr-o proforma **redevin -gestionabile**, trec prin `do_alege_stoc` la adaugare, primesc `id_gestiune` real si descarca gestiune -normal (`docs\cercetare\s5b_proiectare_proforma_copiere.md` §2.6). **De verificat inainte de a te baza -pe asta**, pentru ca §2.6 descria copierea in general, nu cazul „sursa e o proforma": -1. poate fi azi o **proforma** aleasa ca sursa de copiere, sau e exclusa undeva? -2. `do_copiaza` degradeaza tipul spre `{1,5,10,22}` — ce tip rezulta dintr-o proforma si e cel dorit? -3. ~~se pastreaza legatura proforma → factura?~~ **RASPUNS (Marius, runda 12): DA — „ar fi bine sa aiba - urma sursei, la fel ca factura din aviz".** Deci trasabilitatea **e ceruta**, si tiparul de urmat e - cel deja existent pentru aviz → factura: `scrie_corespondente_vanzari(1)`, chemata din - `finalizeaza_factura`, care scrie in **`VANZARI_CORESP`** perechea - `(ID_VANZARE_FACT = factura, ID_VANZARE_AVIZ = documentul sursa, TIP = 1)`. Numele coloanei e generic, - deja reutilizat pentru mai multe feluri de perechi (`TIP = 1/2` aviz-factura, `TIP = 3` retur), deci - **structura nu se schimba — se adauga o valoare noua de `TIP`** pentru proforma → factura. - **De proiectat, nu de presupus:** ce valoare de `TIP` se aloca; de unde stie fluxul de copiere ca - sursa a fost o proforma (azi `do_copiaza` degradeaza tipul, deci informatia s-ar putea pierde - **inainte** de scriere — vezi punctul 2); si daca `marcheaza_facturat` / `FACTURAT` trebuie sau nu - atinse (pe aviz sunt, pe proforma probabil **nu**, pentru ca proforma nu e un document de livrare — - **de confirmat, e exact genul de detaliu care se copiaza gresit din tiparul avizului**). -~~**Aceasta e prima sarcina de proiectare a rundei 13.**~~ **LIVRATA — vezi S5c mai jos.** - -*Depinde de:* S5. - -#### S5c — Factura din proforma (decizia 43, cerinta noua) -**PROIECTAT (runda 13) — `docs\cercetare\s5c_factura_din_proforma.md`. Toate cele trei intrebari si -ambele capcane au raspuns cu dovada. Vestea buna: mecanismul de baza chiar exista, si nu se atinge.** - -- **(a) O proforma poate fi azi aleasa ca sursa de copiere, fara nicio excludere.** `IsCopy` returneaza - **necondiționat `.T.`** (`COMUN\clase\ofacturare_comun.vc2:4969`, cu comentariul explicit *„POT SA - COPIEZ ORICE, TRATEZ TIPURILE IN DO_COPIAZA"*; filtrul vechi pe tip e comentat dedesubt, la `:4971`) — - **verificat direct**. Nici vizibilitatea butonului, nici filtrul de grid „Facturi&Avize / Proforme" nu - se uita la `eproforma`. Deci nu e nimic de deblocat. -- **(b) Tipul rezultat e corect, fara schimbare.** Copierea produce un document real - (`nIdTipDoc = 5`, `eproforma = 0`) — factura fiscala, exact ce trebuie. Degradarea lasa neatins - grupul-tinta **`{1,5,7,10,22,23}`** (vezi corectia de cifre de mai sus). -- **(c) `TIP = 4` e liber in `VANZARI_CORESP`** si se aloca pentru „factura din proforma". Confirmat - **cod + date** pe schema vie: tabela are un **singur scriitor in toata baza** - (`pack_facturare.scrie_corespondente_vanzari`), iar azi se folosesc doar `1/2/3`. **Structura nu se - schimba.** -- **Capcana (i) — confirmata, si e miezul poveștii.** `id_vanzare` al proformei **supravietuieste** - copierii (prin `poDate.listaid`), dar faptul ca **sursa era o proforma se pierde**: - `completeaza_setari_document` copiaza `.listaid`, dar **nu** si `eproforma` - (`COMUN\programe\ofacturare_comun.prg:387` — **verificat direct: `eproforma` nu apare nicaieri in tot - fisierul**). De aceea legatura **nu se poate agata de `CASE`-ul din `finalizeaza_factura`**, care e - cheiat pe `ntip` — `ntip` nu poarta distinctia. Solutia: un semnal nou, client-side - (`poDate.lProformaSursa`), capturat exact acolo unde informatia mai exista, plus un **apel Oracle - explicit separat** care reutilizeaza `scrie_corespondente_vanzari(4)` **neschimbata**. Zero cod PL/SQL - nou pe calea recomandata. -- **Capcana (ii) — INFIRMATA presupunerea comoda, cu patru argumente: `marcheaza_facturat` NU se cheama - pe proforma.** Proforma n-are `VANZARI_CANTITATI`, n-are ramura de reversare la stergere in - `sterge_factura`, nimic nu filtreaza dupa `FACTURAT` pe proforme, si oricum calea aleasa e in afara - `CASE`-ului care il cupleaza azi. Era exact detaliul care se copia gresit din tiparul avizului. - -**Ce NU se schimba:** `IsCopy`, vizibilitatea butonului de copiere, filtrul de grid, degradarea de tip -din `do_copiaza`, `cursor_retur_document` (`GESTIONABIL = B.IN_STOC`), si `scrie_corespondente_vanzari` -insasi. - -**INCHISE, nu de reintrebat (marcaj pus in runda 17).** Cele cinci puncte de mai jos au primit raspuns: punctul 1 (`TIP = 4`) prin **decizia 51**, restul in bloc prin **decizia 56** („da la toate"). Lista ramane ca **inventar al recomandarilor acceptate**, ca sa se stie ce s-a decis si de ce — nu ca intrebari: -1. **`TIP = 4`** — liber azi, dar alocarea e **ireversibila in date** odata intrata in productie. De - confirmat explicit, nu tacit. -2. **Apel pe starea de sesiune a pachetului (`clistaid` / `nid_vanzare`) vs. procedura noua cu - parametri expliciti.** *Recomandarea raportului:* varianta simpla (zero cod Oracle nou), **cu - conditia** verificata la implementare ca niciun apel Oracle intercalat nu reseteaza starea intre - scrierea facturii si scrierea corespondentei. -3. **Aceeasi proforma poate fi copiata de N ori**, fiecare copie cu randul ei `TIP = 4`. E comportamentul - implicit al oricarei copieri de azi. *Recomandare:* daca deranjeaza, avertisment — **nu** blocare. -4. **Garda simetrica la stergere** (*„nu poti sterge o proforma care are deja factura generata din ea"*), - pe tiparul `TIP IN (1,2,3)` din `sterge_factura` — de decis daca se doreste. -5. **Afisarea „provine din proforma X"** pe factura noua — vine gratis din legatura scrisa, la nivel de - **document** (ca la retur), nu de linie. De decis daca intra acum sau mai tarziu. - -*Gata cand:* dintr-o proforma emisa se genereaza o factura reala, cu numar nou, care descarca gestiunea -normal, are rand `TIP = 4` in `VANZARI_CORESP` catre proforma sursa, iar proforma **nu** e marcata -`FACTURAT`. -*Depinde de:* S5b. - -### Decizia 49 (Marius, runda 13) — cele doua formulare merg IN PARALEL, si ALEGE UTILIZATORUL - -**Cerinta, in cuvintele lui Marius:** *„utilizatorul sa poata accesa alternativ, daca doreste"*. Deci -**nu** o setare care ruteaza tacit pe un flux sau altul, si **nu** un pilot pe cativa oameni: ambele -formulare raman accesibile, iar **alegerea o face omul, in momentul in care factureaza**. Rostul e sa -existe mereu **un flux despre care se stie ca functioneaza**, cat timp cel nou se stabilizeaza. - -> **Corectie a rundei 13:** prima formulare a acestei decizii descria un pilot per utilizator, prin -> optiunea de firma. **Gresit** — Marius a corectat: optiunea **nu alege**, ci doar **face alegerea -> disponibila**. Nu se reargumenteaza in varianta veche. - -**Mecanismul exista deja in produs si face exact asta**, nu se inventeaza: -- `factureaza` (`COMUN\programe\ofacturare.prg:87-93`) verifica `gnFacturareNou` si, cand e pornita, - **intreaba la fiecare facturare**: `AMESSAGEBOX('Facturare noua (DA) sau standard (NU)?', 4+32, ...)`. - Optiunea deschide alegerea; raspunsul il da utilizatorul, de fiecare data. -- `gnFacturareNou` e o **optiune de firma**: `optiuni_firma` (`COMUN\programe\oinit_optiuni.prg:225-275`) - cheama `SCRIE_OPTIUNI(gcUserName)`, parcurge `v_optiuni` si **declara dinamic** globalele publice dupa - tip (`Public gn&lcvarname` pentru `NUMERIC`), cu filtru optional pe program - (`Isnull(programe) Or gcNumeProgram $ programe`). **Deci se activeaza si se dezactiveaza din date, - fara livrare de exe.** -- **Fluxul vechi ramane intreg:** `factureaza` + `frm_facturare_articole`. S2 sterge doar - **`factureaza2`**, un fork mort din 2017 (executia interogarii dezactivata, `lnSucces = 1` hardcodat, - zero utilizatori reali) — **nu** calea veche. **S2 nu are voie sa desfiinteze alegerea.** -- **Pentru etapa II plasa e alta, si exista deja:** editarea prin regenerare e o **actiune noua**, deci - „fluxul vechi" pentru ea e **fluxul lui #6**, pe care decizia 38 il pastreaza oricum. - -**Cum se prezinta alegerea — de ales la implementare, ambele satisfac cerinta:** -- **(A) intrebarea de azi**, modal la fiecare facturare. Exista deja, zero cod. Neajuns: intreaba si - cand utilizatorul stie de o luna ce vrea. -- **(B) doua intrari distincte** in meniu / doua butoane („Facturare" si „Facturare (nou)"). Aceeasi - libertate, fara modal la fiecare document. **Recomandat.** Se poate porni cu (A), care e gata, si trece - la (B). - -**Limita, si trebuie spusa explicit ca sa nu creeze o falsa siguranta: alegerea acopera VFP-ul, nu -Oracle.** Modificarile din pachete — parametrul nou al lui `contabilizeaza_articol` (decizia 34), -variabila noua din `SET_IDFACT`, si `INSERT` → `MERGE` in `DOCUMENTE` (S9) — sunt **cod comun al -intregii suite** si se aplica **tuturor deodata**, indiferent pe ce formular alege omul sa lucreze. -Proiectarea le face **inerte prin constructie** (parametru `NULL` → executie identica cu azi; -`WHEN MATCHED` care nu se declanseaza pe drumul normal), dar asta e o garantie de regresie zero, -**nu** o cale de intoarcere. Consecinta practica: pe formular si procedura intoarcerea e imediata; pe -baza de date, siguranta vine din **testarea unui ciclu normal de scriere in fiecare produs** (deja -ceruta in S9). - -### Decizia 50 (Marius, runda 13) — se editeaza documente curente, nu documente dintr-un lant - -**Formularea lui Marius:** *„in principiu nu se poate edita un document pentru care s-a facut retur; -de principiu se pot modifica documente curente, nu cele care fac parte dintr-un lant"*. - -**Deci garda de azi ramane, si devine regula declarata, nu limitare tolerata.** `sterge_factura` arunca -`ORA-20000` cand documentul are deja facturi / avize de retur emise peste el; S7 **semnaleaza conditia -inainte de intrarea in formular**, cu mesaj clar, nu ca eroare Oracle la final. Utilizatorul care chiar -vrea sa editeze sterge intai documentele-copil — comportament existent, nu ceva de construit. - -**PRECIZAT de Marius (runda 13), si inchide punctul: „lantul" se citeste IN AMONTE, nu in ambele -sensuri.** In cuvintele lui: *„daca este generata factura din aviz, avizul nu se mai poate modifica, -factura da, pentru ca este documentul curent; ma refeream la documentele din lant anterioare"*. - -Deci regula, in forma finala: -- **Documentul care are urmasi se blocheaza** — avizul din care s-a facut factura, factura peste care - s-a emis retur. Are deja o reprezentare in cod: garda din `sterge_factura`. -- **Documentul de la capatul lantului ramane editabil** — el e „documentul curent". **Factura din aviz - (`ntip = 4`) ESTE editabila**, chiar daca are parinte. -- Prin urmare **perimetrul etapei II nu se restrange**, iar intrebarea deschisa din S10 despre `ntip = 4` - (aceeasi re-derivare tacuta ca pe contract, dar cu alta sursa de comparat) **ramane in picioare** — nu - dispare, cum s-ar fi intamplat la citirea stricta. - -**VERIFICAT in runda 14 — si premisa era gresita.** Raport: `docs\cercetare\garda_aviz_facturat.md`. -Se credea ca garda de azi blocheaza documentul doar cand are **retururi** peste el. **Nu e asa:** a doua -garda din `sterge_factura` testeaza `TIP IN (1, 2)`, iar **`TIP = 1` este corespondenta aviz → factura -normala, nu retur** (`EXPORT:5464-5476`, semantica citita ramura cu ramura din `CASE`-ul lui -`finalizeaza_factura`, `EXPORT:14818-14839`). Formularea gresita venea din citirea comentariului -`-- verific daca exista facturi sau avize de retur`, in care „de retur" se distribuia si peste „facturi"; -codul zice altceva. **Deci regula ceruta de decizia 50 e deja implementata pentru avize** — nu e nevoie de -nicio garda noua ca *regula*. - -**Dar sunt trei garzi, nu doua, si a treia schimba enuntul deciziei 50:** - -| garda | `EXPORT` | ce blocheaza | -|---|---|---| -| 1 | 5452-5462 | **factura** care are facturi de retur peste ea (`TIP = 3`) | -| 2 | 5466-5476 | **aviz** care are factura (`TIP = 1`) sau aviz de retur (`TIP = 2`) peste el | -| 3 | 5480-5494 | **factura din aviz** ale carei avize-sursa au primit intre timp aviz de retur | - -Prin urmare **„factura din aviz ESTE editabila" nu e neconditionat**: e editabila doar cat timp niciunul -dintre avizele ei nu a primit aviz de retur. Nu e „capat de lant" prin definitie. - -**Ce lipseste nu e regula, e MOMENTUL.** Garda traieste in interiorul lui `sterge_factura`, deci se -manifesta ca `ORA-20000` **in mijlocul** pasului de stergere din regenerare — dupa ce utilizatorul a -completat formularul si dupa deschiderea tranzactiei. S7 are nevoie de un **pre-flight read-only** pe -**exact aceeasi conditie**, fara reimplementarea regulii (vezi S7). Interogarea se face pe -`VANZARI_CORESP`, **nu** pe `VANZARI.FACTURAT`: `FACTURAT` e un marcaj derivat, scris si resetat, dar -**necitit de nicio garda** — semnalul autoritar e `VANZARI_CORESP`. - -**GOLUL REAL NU E PE AVIZ, E PE PROFORMA — si a devenit relevant chiar in runda 14, odata cu decizia 51.** -`pack_facturare.sterge_proforma` (`EXPORT:5610-5635`) **nu are nicio garda**: corpul ei e doua -`UPDATE ... SET STERS = 1`. E o procedura **complet separata**, care nu cheama `sterge_factura`, deci -garda existenta **nu se extinde automat** la `TIP = 4`. O proforma din care s-a emis factura se poate -sterge azi fara niciun avertisment. Calea VFP iese devreme din `do_sterge` -(`ofacturare_comun.vc2:4707-4719`), inainte de restul verificarilor. **Asta e continutul concret al -punctului deschis 4 din S5c** („garda simetrica la stergere") — nu mai e o intrebare de principiu, e o -garda de scris intr-o procedura care azi n-are niciuna. - -**Comanda si contractul nu trec prin `VANZARI_CORESP`** si n-au garda de tip „are urmasi" pe factura: -comanda se leaga prin `VANZARI.ID_COMANDA` + `inchide_comanda`, iar garda ei e **in VFP si pe comanda**, -nu pe factura (`COMUN\clase\ocomenzi.vc2:1806-1807` la modificare, `:2065-2066` la stergere); contractul -se leaga prin `VANZARI.ID_CTR` + `CTR_RATE_FACTURI`. - -### Deciziile lui Marius, runda 13 (10.08.2026) — luate, nu de reluat - -Cele cinci puncte neblocante ramase din runda 12. **Patru din cinci au mers pe recomandare; al -cincilea (48) a respins-o motivat.** Fiecare e scrisa si la locul ei, in povestea careia ii apartine. - -44. **`poArticol` devine parametru explicit** al dialogurilor de linie (`do_verifica_articol`, - `do_alege_stoc` / `frm_articol_gest_factura`), in loc de variabila `Private` populata de apelant. - **O singura solutie pentru golul din S4e si cel din S4f (R7)** — e aceeasi problema structurala. - Schimbare de contract, **aprobata ca atare**; cere inventarul si actualizarea tuturor apelantilor - existenti inainte de prima editare. (S4e, S4f) -45. **Asimetria din `do_modifica`** se lasa sa se **corecteze de la sine** prin recalculul la cerere, - dar corectia se **declara explicit in changelog**. (S4, punctul 2) -46. **Corectia pe tipurile `23,41` asteapta punctul 2** (recalculul pe server, care acopera Rolul B). - Punctul 1 **nu se redeschide** acum. (S4, punctul 2) -47. **`zi_curs`: simetrie.** Campul se ascunde la loc la stergerea ultimului articol in valuta; - „clipitul" e acceptat ca pret al unei reguli unice. (S4d) -48. **Tabelul de configurare per tip = cursor generat in cod la pornire**, nu `DBF` static, nu tabela - Oracle. Detectia tipului necunoscut (eroare la deschidere) se pastreaza; se accepta recompilarea - la orice tip nou. (S5) - -### Deciziile lui Marius, runda 14 (11.08.2026) — luate, nu de reluat - -51. **`TIP = 4` in `VANZARI_CORESP` = „factura scrisa dintr-o proforma".** Confirmat explicit, dupa ce - i s-a explicat ce inseamna `1/2/3` (`TIP` = **natura legaturii parinte-copil**, nu tipul - documentului: `1` = aviz → factura, `2` = aviz → aviz de retur, `3` = factura → factura de retur). - `4` intra in aceeasi familie cu `1`. Valoarea e libera pe ambele fronturi (niciun apel cu `4` in - pachet, zero randuri in date), tabela are un singur scriitor in toata suita, structura nu se - schimba. **Alocarea e ireversibila odata cu primele date de productie** — acceptat ca atare. (S5c) -52. **`do_modifica` ramane activ pentru multi-selectie.** Formularul unificat preia cazul cu un singur - document; calea veche ramane pentru modificarea in bloc. **Nicio capacitate nu se pierde la - retragere** — dar consecinta e ca S2 nu poate desfiinta nici aceasta ruta, nu doar alegerea de la - decizia 49. (S8b, punctul 10) -53. **Atasamentul PDF se sterge la reemitere**, nu se remigreaza si nu se marcheaza „versiune - inlocuita". Documentul reemis porneste curat; PDF-ul se regenereaza la prima listare. **Consecinta - semnalata explicit lui Marius si acceptata de el:** urma a ceea ce s-a trimis efectiv clientului - dispare din sistem. Pasul `UPDATE ATASAMENTE_VANZARI SET id_vanzare = :nou`, adaugat in S9 la runda - 13, **se inlocuieste cu stergere**. (S11, punctul 15) -54. **La reemitere se scriu valorile din formular, nu se reciteste sursa.** Formularea lui Marius: - *„nu inteleg de ce se schimba valorile fata de ce se incarca din factura sursa care se modifica? - asta este comportamentul pe care il doresc — modific sursa"*. **Asta rastoarna S10:** intrebarea nu - mai e „ce garda punem peste re-derivare", ci **„re-derivarea chiar se produce pe calea de - reemitere?"** — si daca da, se **elimina**, nu se avertizeaza. Avertizarea + confirmarea propuse de - raportul S10 sunt **respinse**: nu se cere utilizatorului sa confirme o schimbare pe care n-a - cerut-o. Vezi S10, rescris. (S10, punctele 11 si 12) -56. **„DA LA TOATE" — Marius a acceptat in bloc toate recomandarile deschise, runda 14.** Formularea - lui: *„da la toate, mai putin"* cele doua puncte pe care le-a intrebat separat (48/49 si voiajele). - **Nu se mai reintreaba niciunul dintre punctele de mai jos** — sunt luate, si fiecare e scris si la - locul lui, in povestea careia ii apartine: - - **S8** — canalul de citire a liniilor: **(B), procedura noua `cursor_editare_document`** (nu se - extinde `cursor_retur_document`, ca sa nu se schimbe comportamentul copierii; nu - `FACT_VFACTURI_DETALII`, care pierde `ID_POL`, `PRETD` si tratamentul valutar — argument intarit - de faptul ca **`VVANZARI_ARTICOLE` nu expune `ID_POL` / `ID_CTR`**, vezi S10). - - **S8** — `GESTIONABIL` la editare: **din document**, nu din nomenclatorul curent. - - **S8** — `text_aditional`: **se normalizeaza la incarcare** (`Chr(170)` → `CR+LF`) si se - re-normalizeaza la comparatie. Formele diferite salvate de cele doua rute de scriere se - **semnaleaza separat** ca defect preexistent. - - **S8** — `zi_curs` pe calea de editare: **ascuns** (precedent: tipurile 8/9). Abatere mica de la - decizia 5, asumata. - - **S8** — `poDate.lEditare`: **proprietate pe `oDateFactura`**, nu parametru (patru locuri au - nevoie de semnal; `lCopiere` e deja acolo cu acelasi rol). - - **S8** — `id_ruta`: **proprietate noua pe `oDateFactura`** (fara ea S8c nu poate implementa unul - din cei 14 parametri). - - **S8** — defectul de prefixare `text_aditional` la `Init` pentru contracte: **se ocoleste in #13** - prin `lEditare` si **se semnaleaza separat**, nu se repara pe calea de emitere. - - **S5c** — apelul Oracle pentru legatura proforma → factura: **varianta simpla**, pe starea de - sesiune a pachetului, zero cod PL/SQL nou — **cu conditia verificata la implementare** ca niciun - apel intercalat nu reseteaza starea intre scrierea facturii si scrierea corespondentei. - - **S5c** — aceeasi proforma facturata de N ori: **avertisment, nu blocare**. - - **S5c** — garda simetrica la stergerea proformei: **se scrie**. Nu e reutilizare — - `sterge_proforma` n-are azi nicio garda (vezi decizia 50, sectiunea rescrisa). - - **S5c** — afisarea „provine din proforma X": **da**, la nivel de document. - - **S4g** — `CU_TVA = 1` hardcodat: **se verifica pe date inainte de implementare**, nu se - presupune inofensiv. Efectul prin `nproc_tva_max` e masurat, pe linie scutita + discount global. - - **S4g** — trasabilitatea `CONT_VENIT` pe `VANZARI_DETALII`: **da**, se pastreaza coloana. - - **Decizia 54, cele trei consecinte: acceptate toate trei.** `IN_STOC` vine din formular (si **S8 - trebuie sa-l incarce** — vezi S10, consecinta 1); **se pierde validarea** liniei fata de comanda - pe ramura comenzi, asumat; **`PROC_TVAV` ramane derivat** — reemiterea dupa o modificare legala - de cota va da cota noua, **acceptat deocamdata**; transformarea lui in parametru ramane o - decizie separata, daca se cere vreodata reproducere exacta si peste asta. - - **Defectul `lnTip` din `do_copiaza`** (runda 13): **se repara la #6**, fisierul fiind al lui. - - **Punctele pur interne** (numarul codului de eroare `FACT-0xx`, numele cheii de optiune - `FACT_SCD_ARTFPRET`, forma semnalului de regenerare) — **lasate la latitudinea implementarii**, - cu recomandarile deja scrise in povestile lor. - - **Raman deschise doar doua**, si amandoua din motive proprii, nu din lipsa de raspuns: - **(a) tipurile 48/49** (custodie) — Marius a cerut sa stie implicatiile, i s-au explicat, decizia - n-a fost inca data. *Inchis intre timp — decizia 60, runda 16: da, sunt editabile prin #13, - cu executia conditionata de verificarea custodiei in curs.* - **(b) cota si explicatia de TVA a discountului de document** — - **cercetare TERMINATA** (sectiunea K-bis): cota era regula implicita „cota maxima de pe factura", - explicatia e azi o constanta hardcodata (`ReasonCode="95"`, text „Discount"). **Partea de - repartizare s-a decis — decizia 59, runda 16: proportional pe cote.** Ramane deschis doar campul - text optional de motiv — **inchis si el: decizia 61** (da, se adauga), cu asezarea fixata de - **decizia 66** (in banda de totaluri, langa discount). -55. **Metoda de executie e obligatorie, si e scrisa in plan.** La implementare planul **se sparge pe - stories**, fiecare story fiind o livrare de sine statatoare; **fiecare pas se testeaza**, nu doar - capetele de etapa (S6 / S12); si **fiecare story trece prin code review dupa implementare si dupa - teste, inainte de commit**. Sectiunea **„Metoda de executie"**, imediat inainte de Etapa I. S13 - nu mai e momentul review-ului, ci al inchiderii. - -### Deciziile lui Marius, runda 15 (11.08.2026) — luate, nu de reluat - -57. **Asezarea zonei de jos: VARIANTA D.** Aprobata explicit („sunt de acord cu varianta D"), dupa doua - corectii cerute de el pe drum: (a) *„imi place linia de totaluri de la varianta C, dar incasarea si - alte date le vreau tot in acelasi formular, colapsate, mai jos de totaluri"*; (b) *„sectiunile - incasare si alte date colapsate trebuie sa fie pe acelasi rand — este destul loc pentru amandoua, - detaliile pot sa fie grupate pe orizontala, nu pe verticala"*. A / B / C **cad**. - - **Ce inseamna D, concret, pentru S1 si S3:** - 1. **Banda de totaluri** (preluata din C): pe toata latimea, lipita de grid, cu baza, discountul pe - articole, discountul de document **cu procentul editabil pe loc** si TVA desfacute pe un rand; - totalul mare, singur, la dreapta. - 2. **Incasare si Alte date sunt sectiuni colapsabile in formular, nu dialoguri modale** — **una - langa alta pe acelasi rand**, sub totaluri, fiecare pe jumatate de latime. Randul inchis arata - **rezumatul continutului** („NUMERAR · 5 570,55 lei"), nu doar un titlu. Independente: se poate - tine deschisa doar una. Campurile dinauntru se aseaza **pe orizontala**, doua randuri fiecare. - 3. **Nu exista bara de comenzi jos.** `but_renunt` / `but_termin` raman unde sunt azi — in banda de - titlu, sus in dreapta (`COMUN\clase\cmd_butoane.vc2:288` si `:386`, butoane-imagine cu - `Top = 1`, `Anchor = 8/9`, tooltip „Renuntare (ESC)" / „Terminare (CTRL+F)"). - 4. **Antetul strans la doua randuri**, fara titluri de grup, si **panoul „Discount pe document" - dispare** ca panou separat. - - **Patru consecinte de dus in executie, nu de redescoperit:** - - **Dispare „renunt doar la incasare".** Fara `Accept` propriu pe dialog, ce se completeaza in - sectiune se scrie la `Termina`, impreuna cu documentul. **I-a fost spus explicit** inainte de - aprobare si a acceptat. - - **Validarea incasarii nu mai are moment propriu** — se muta in `Termina`, langa restul - verificarilor. **De scris explicit in S9.** - - **Starea deschis/inchis a sectiunilor** — de decis daca se retine intre documente sau porneste - mereu inchisa. Nu blocheaza nimic; recomandare: porneste inchisa, se retine per utilizator. - - **Caption pe cele doua butoane.** Sus in dreapta, langa `✕`-ul ferestrei, un `✓` verde fara text - e ambiguu. Punctul era deja notat in v8 pentru `But_renunt1` / `But_reset1`; D il face mai - apasat, pentru ca acum ele sunt **singura** cale de iesire. - - **INCHIS.** Cercetarea despre cota si explicatia de TVA a discountului de document (punctul - deschis (b) de la decizia 56) **s-a terminat** — vezi sectiunea K-bis — si repartizarea s-a decis - (**decizia 59, runda 16**: proportional pe cote, fara sa se ceara cota de la utilizator), deci a - treia sectiune „grea" (cota + explicatie proprii) nu mai e in discutie. Ramane varianta minimala: - **decizia 61 (runda 16) a materializat-o** — Marius vrea campul text optional de motiv. Asezarea - lui a oscilat o data: decizia 64 il facea a treia sectiune jos, **decizia 66 (runda 17) o - rastoarna si il muta in banda de totaluri, langa discount**. **Punctul 2 de mai sus ramane deci - exact cum a fost aprobat: doua sectiuni jos, fiecare pe jumatate de latime.** D ramane intr-un etaj. - -58. **Mockup-ul asezarii ramane doar online.** Formularea lui: *„nu vreau artifactul html, doar online, - ca sa nu mai intretii 2 variante"*. `docs\mockup_13_variante_asezare_jos.html` **a fost scos din - `docs\`**; sursa de adevar e - **https://claude.ai/code/artifact/dfc70d38-856f-48fd-b6af-3da78e9da4f7**. - Ca sa-l modifici: `WebFetch` pe URL → scrie HTML-ul intr-un fisier de lucru **in scratchpad, nu in - `docs\`** → `Artifact` cu **`url` = link-ul de mai sus** (fara `url` se creeaza link nou). - **`docs\mockup_13_formular_unificat.html` (v8) nu intra sub regula asta** — cerinta a fost data - numai pentru mockup-ul asezarii. - -### Decizia 59 (Marius, runda 16) — luata, nu de reluat - -59. **Discountul de DOCUMENT se repartizeaza PROPORTIONAL PE COTE.** Formularea lui: *„discount pe - document repartizat proportional pe cote"*. Se adopta recomandarea cercetarii din **K-bis**: - regula de azi („toata valoarea discountului primeste cota MAXIMA de pe factura", prin - `Calculate Max(proc_tvav)`) **se inlocuieste** cu repartizarea proportionala cu baza fiecarei - cote de pe factura. **Nu se cere utilizatorului cota** — repartizarea e automata. - - **Ce atrage dupa sine, tot din K-bis:** - 1. **Doua locuri de schimbat, nu unul, si in ACEEASI livrare:** `prelucreaza_facturacrs` (VFP, - `COMUN\programe\ofacturare_comun.prg:1886-1897`) trebuie sa insereze cate un rand-sentinela - **per cota** in loc de unul singur; si `recalculeaza_totaluri_vanzari` (PL/SQL, - `ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:16081-16092`) trebuie sa inlocuiasca `MAX(...)` - cu suma repartizarii. Daca se schimba doar unul, `VANZARI.TOTAL_TVA` si TVA-ul din eFactura - **diverg** pe facturile cu cote mixte. **Nu e optional si nu se poate esalona.** - 2. **Bucatile de discount trebuie sa primeasca `id_jtva_coloana` al grupului pe care il reduc**, - nu doar cota. Cheia de grupare din `xmlefactura.prg:246` are cinci campuri si `expltva` se - completeaza tot prin `id_jtva_coloana`. O implementare care seteaza doar `proc_tva` **lasa - grupul orfan exact unde e azi** — repara jumatate din defect si o lasa pe cealalta. - 3. **eFactura nu cere nicio modificare** — `xmlefactura.prg:758-792` grupeaza deja - `GROUP BY proc_tva` si emite cate un `AllowanceCharge` per cota. - 4. **Diferenta de rotunjire cade pe cota cu baza cea mai mare**, ca suma bucatilor sa fie exact - `VANZARI.DISCOUNT`. E recomandarea cercetarii, luata ca implicita — Marius a fost instiintat - ca se merge asa fara sa mai fie intrebat, deci se schimba doar daca obiecteaza. (Raportul - spune pe alocuri „ultima cota preia diferenta"; **regula care se implementeaza e cea de aici**, - ca sa nu ramana doua formulari in circulatie.) - 5. **Doua consecinte vizibile, acceptate implicit prin decizie:** factura tiparita va arata **N - randuri** „Discount X % Factura" in loc de unul, pe facturile cu cote mixte; si **nota - contabila** primeste TVA-ul discountului spart pe cote. - 6. **Rezolva si grupul orfan** de pe facturile scutite / taxare inversa / intracomunitare - (K-bis), fara garda separata, si desfiinteaza ambiguitatea lui `agettipcota(1)`. - - **Raman DESCHISE, nu de presupus rezolvate:** - - **campul text optional de motiv** (`AllowanceChargeReason` in locul constantei „Discount"; - `ReasonCode` ramane `95`) — Marius **nu s-a pronuntat**; de el atarna si intrebarea de asezare - din S1 / decizia 57 (a treia sectiune pe rand sau rand propriu); - - **retroactivitatea la relistare / retrimitere**: o factura veche relistata sau retrimisa in - eFactura ar genera alt XML decat cel trimis initial. **Nedecis.** - -### Deciziile 60-65 (Marius, runda 16) — luate, nu de reluat - -60. **Facturile de marfa in CUSTODIE (tipurile 48 si 49) SUNT editabile prin #13.** Formularea lui: - *„vreau sa fie posibila editarea si a facturilor in custodie"*. Intra deci in perimetrul - **etapei II**, contrar recomandarii „nu acum" din raportul S8 (intrebarea 7, §8.2). - - **Inchide** punctul **(a)** de la **decizia 56** (tipurile 48/49, singurul ramas deschis din - blocul „da la toate") si **necunoscuta N5** din `docs\cercetare\s8_incarcare_document.md` §8.1, - plus restul intrebarii 7 din §8.2. - - **Ramificatie noua pentru S8:** pe tipurile 48/49, controlul `ct_clb_altele` e scos - **neconditionat** la copiere (`ofacturare.vc2:9705-9712`), spre deosebire de tipurile 1/5/10 - unde copierea il **pastreaza** si il reeticheteaza (`s8_incarcare_document.md` §3.2, ramura 3). - Calea de **editare** trebuie sa-l pastreze si pe custodie — ramura 48/49 a copierii nu poate fi - refolosita ca atare. - - **VERIFICAT — blocantul CADE, dar premisa initiala era gresita.** Raportul: - `docs\cercetare\custodie_48_49_stergere_reemitere.md`. `scrie_fact_aviz_custodie` **nu are - legatura cu tipurile 48/49** — are un singur apel in tot pachetul (`PACK:7521`), pe ramura - `pack_facturare.ntip <> 4` (`:7472`), deci serveste `ntip = 4` (factura din avize), nu custodia; - numele procedurii a indus in eroare, si premisa gresita a circulat pana in aceasta decizie. - Emiterea unui document 48/49 **nu atinge deloc stocul**: sursa lor unica de articole e - `cursor_articole_k` (`PACK:3595-3701`), restransa explicit la `WHERE C.IN_STOC = 0` (`:3695`), - iar `descarca_gestiune` se cheama doar cand `in_stoc = 1` (garda dubla, la apelant `:7472-7475` - si in corpul procedurii `:7789-7797`). `sterge_factura` (`:5432-5607`) nu are ramura dedicata - pentru 48/49, si nici cele trei garzi de refuz al stergerii (`:5452-5494`) nu le prind — dar - **n-are ce reversa**, fiindca nimic legat de stoc n-a fost scris la emitere. **Regenerarea e - sigura pe 48/49**, nu pentru ca reversarea ar functiona, ci pentru ca nu exista nimic de - reversat. - - **Rezerva, de scris, nu de ascuns:** siguranta atarna de un invariant azi impus doar de sursa de - articole — „documentele 48/49 contin numai articole cu `IN_STOC = 0`". Invariantul **nu s-a - verificat exhaustiv**, doar constatat pe sursa curenta. #13 schimba modul de adaugare a - articolelor (**S4** — cautare pe server, in linie; **S4g** — adaugare de articole la modificarea - oricarui document): daca formularul unificat ajunge sa permita adaugarea unui articol - **gestionabil** pe un document 48/49, invariantul se rupe si concluzia de siguranta pica. - **Cerinta pentru S4/S4g/S5: pe tipurile 48/49 se pastreaza restrictia la articole `IN_STOC = 0`** - — vezi notele de executie la acele povesti; devine **criteriu de test**, nu presupunere. - - Pentru **emitere**, tipurile 48/49 erau oricum deja acoperite de etapa I (S5 completeaza cele - patru randuri lipsa din `Do Case`: 45, 48, 49, 52) — decizia 60 priveste doar **editarea**. - -61. **Discountul de document primeste un CAMP TEXT OPTIONAL de motiv.** Formularea lui: *„la - discount, da, un camp text optional"*. - - **Inchide** ultimul punct ramas deschis din **K-bis** si din **decizia 56 (b)** — campul de - motiv nu mai e „Marius nu s-a pronuntat". - - **Ce inseamna concret:** - - inlocuieste constanta hardcodata `"Discount"` ca `cbc:AllowanceChargeReason` - (`COMUN\programe\xmlefactura.prg:774-776`); **`ReasonCode` ramane `95`**; - - cere **stocare noua** — azi nu exista nicio coloana pe `VANZARI` pentru motivul discountului - de document; - - cere **un control nou in formular** — singura bucata din reparatia discountului (K-bis / - decizia 59) care chiar cere UI nou, restul fiind repartizare automata fara interactiune. - - **Redeschisese, in varianta minimala, intrebarea de asezare de la decizia 57. INCHISA DEFINITIV - PRIN DECIZIA 66 (runda 17): campul sta in banda de totaluri, langa discount.** Decizia 64, care il - facea a treia sectiune jos, **e rasturnata** — randul de jos ramane cu doua sectiuni, ca la decizia - 57. Varianta D ramane intr-un etaj. Paragrafele din **S1** si din **decizia 57** sunt actualizate - in acest sens. - -62. **Restrictia de modificare e „documentul sa NU fi fost trimis in eFactura" — si de aici - retroactivitatea NU mai e o intrebare.** Formularea lui: *„singura restrictie pentru modificare - este sa nu fie trimisa in eFactura, deci nu are legatura retroactivitatea"*. - - Garda insasi nu e noua — **S7** o are deja, reutilizata din #6 - (`COMUN\programe\ofacturare_editare.prg:19-28`, functia `EsteInEFactura`; apelata din - `COMUN\clase\ofacturare_comun.vc2:3764`). Ce era intrebare deschisa era - doar partea lasata de **decizia 59**: daca o factura veche, retrimisa, ar trebui tratata altfel - fata de una noua. Raspunsul lui Marius **inchide** acea intrebare: nu se leaga de nicio data si - de niciun flag suplimentar — verificarea `EsteInEFactura` ramane singurul criteriu de - modificare. - - **Ramane un rest neacoperit, semnalat lui Marius, nedecis inca:** o factura **veche, deja - trimisa**, daca e doar **relistata** (tiparita din nou, nu editata), trece iar prin - `prelucreaza_facturacrs` si — dupa decizia 59 — ar produce **N randuri de discount in loc de - unul** pe facturile cu cote mixte, deci hartia ar diferi de originalul trimis in eFactura. - Relistarea nu e modificare, deci garda `EsteInEFactura` nu o acopera. - -63. **CERINTA NOUA DE AUDIT pe documente.** Formularea lui: *„este doar util de stiut data crearii, - data modificarii daca este cazul, utilizatorul crearii si al modificarii daca este cazul, - respectiv data stergere si utilizator stergere daca este cazul, pentru audit"*. Sase informatii: - **data + utilizator** pentru **creare**, **modificare** si **stergere**. - - Scrisa initial ca cerinta, cu cercetarea in curs. **Cercetarea TERMINATA**, raport: - `docs\cercetare\audit_vanzari_creare_modificare_stergere.md`. - - **Verdict, in doua randuri:** patru din cele sase informatii cerute **exista deja** pe `VANZARI` - si se scriu consecvent (`ID_UTIL`/`DATAORA` la creare, `ID_UTILS`/`DATAORAS` la stergere); **lipseste - complet perechea de modificare** (nicio coloana, verificat cu filtru `%MODIF%` pe - `all_tab_columns`), iar editarea prin stergere+reemitere (S9) ar **suprascrie tacit** perechea de - creare cu utilizatorul si data regenerarii, daca nu se transporta explicit — acelasi risc deja - rezolvat pentru `ID_FACT` (sectiunea „E. `ID_FACT`", linia 762). - - **Poveste noua, proiectata: S14** — verdict complet, recomandare si punctele ramase de decis cu - Marius acolo. - -64. ~~**Asezarea campului de motiv: RANDUL SE IMPARTE IN TREI.**~~ **RASTURNATA DE DECIZIA 66 - (runda 17) — NU MAI E IN VIGOARE.** Formularea de atunci: *„campul de motiv imparte randul in 3"*; - randul de jos ar fi avut trei sectiuni (incasare, alte date, motivul discountului), ~440 px - fiecare la 1366 px. - - **Se pastreaza ca istorie, nu ca regula**, si merita pastrata: varianta a fost **construita in - mockup (v9) si respinsa dupa ce Marius a vazut-o** — *„motiv discount vreau sa fie langa discount, - nu a treia coloana"*. E argumentul cel mai bun din tot planul pentru **de ce se face mockup - inainte de cod**: decizia luata pe descriere s-a intors la prima privire pe forma desenata. - Ce e in vigoare: **decizia 66**, mai jos. - -65. **Auditul (decizia 63) se afiseaza in gridul din `frm_facturi`, nu in formularul facturii.** - Formularea lui: *„auditul presupun coloanele dataora si utilizator adaugat/modificat/sters, le - vreau pe grid-ul din formularul frm_facturi, nu in formularul facturii propriu-zise - este - posibil sa fie deja coloane"*. - - Confirma continutul cerintei de audit (decizia 63): trei perechi **data + utilizator**, pentru - **adaugat**, **modificat** si **sters**. Locul de afisare e **gridul din `frm_facturi`**, nu - formularul unificat — deci **S14 nu cere controale noi in formularul unificat**, cere **coloane - in gridul listei de facturi**. E o cerinta mai usoara decat presupunea S14, si e de spus explicit. - - **Sarcina de verificat la implementarea lui S14, adaugata ca prim pas al povestii:** Marius - banuieste ca unele coloane exista deja in acel grid. **Nu s-a verificat.** S14 incepe deci cu - **inventarul gridului din `frm_facturi`** — ce coloane de audit sunt deja acolo, ce surse au — - si abia apoi adauga ce lipseste. Nu se presupune nici ca exista, nici ca nu. - -### Decizia 66 (Marius, runda 17) — luata, nu de reluat. RASTOARNA DECIZIA 64. - -66. **Motivul discountului sta LANGA DISCOUNT, in banda de totaluri — nu ca a treia sectiune jos.** - Formularea lui: *„motiv discount vreau sa fie langa discount, nu a treia coloana"*. - - **Anuleaza decizia 64** (randul de jos se imparte in trei). Randul de jos revine la **doua - sectiuni colapsabile** — incasare si alte date — adica exact la ce aprobase decizia 57, punctul 2, - inainte ca decizia 61 sa redeschida discutia. **Decizia 57 ramane intreaga; nu se mai atinge.** - - **Ce inseamna concret, pentru S1 si S3:** - - campul de motiv e un **control in banda de totaluri**, imediat dupa suma discountului de - document, inainte de TVA; ia latimea ramasa pana la totalul mare, care sta la dreapta; - - **cele doua sectiuni de jos redevin pe jumatate de latime** (~660 px la 1366 px), nu ~440 — - argumentul „strans, asumat ca atare" din decizia 64 **cade odata cu ea**; - - **regula de activare, adaugata de proiectare, nu ceruta explicit:** campul e activ **numai cand - discountul de document nu e zero**. Un motiv fara discount n-are ce explica, si ar ajunge in - `AllowanceChargeReason` pe un `AllowanceCharge` inexistent. Daca Marius vrea altfel, e o linie - de schimbat. - - **Consecinta de asezare, de stiut la implementare:** banda de totaluri devine plina — baza, - discount articole, discount document (procent + suma + bifa „evidentiat"), motiv, TVA, total. La - latimi mici **se rupe pe doua randuri** (`flex-wrap`), si asta e acceptat: alternativa ar fi - scoaterea bifei „evidentiat" din banda, care n-a fost ceruta. - - Restul deciziei 61 (stocare noua pe `VANZARI`, `AllowanceChargeReason`, `ReasonCode` ramane `95`) - **e neatins** — se schimba doar locul controlului in formular. - -#### S6 — Test pe fluxul real, formularul unificat -Headless (`COMUN\docs\depanare_testare_vfp.md`) + UI (`COMUN\docs\testare-ui-vfp.md`), cate un caz -pe fiecare sursa: lista de preturi, comanda, contract, aviz, retur, transfer, valuta. Comparatie -directa cu documentul emis pe calea veche: `vanzari`, `vanzari_detalii`, `act`, `rul`, totalurile -denormalizate, listarea. -*Depinde de:* S5. - -### Etapa II — editarea prin regenerare - -#### S7 — Actiunea si garzile -Actiune noua pe `frm_facturi`, sub tokenul de drepturi existent. Garzi refolosite din -`COMUN\programe\ofacturare_editare.prg` (`EsteInEFactura:12`, plus luna inchisa, luna curenta, -`ReferinteDocumenteNota`) — **nu se rescriu**, sunt deja extrase de #6. Plus garzile proprii -regenerarii: refuz daca documentul are **urmasi**. - -**PRECIZAT in runda 14, pe cod** (`docs\cercetare\garda_aviz_facturat.md`): regula exista deja integral -in Oracle, in **trei** garzi consecutive din `sterge_factura` — factura cu retururi (`TIP = 3`, -`EXPORT:5452-5462`), **aviz cu factura sau aviz de retur** (`TIP IN (1,2)`, `EXPORT:5466-5476`), si -factura din aviz ale carei avize-sursa au primit intre timp aviz de retur (`EXPORT:5480-5494`). -**S7 nu reimplementeaza regula** — o citeste **mai devreme**, read-only, inainte de intrarea in -formular: - -```sql -select count(*) from vanzari_coresp - where sters = 0 and id_vanzare_aviz = :id_vanzare and tip in (1, 2, 3) -``` - -plus subinterogarea din garda 3 pentru cazul „factura din aviz". Se interogheaza `VANZARI_CORESP`, -**nu `VANZARI.FACTURAT`** — `FACTURAT` e derivat si necitit de nicio garda. Garda din `sterge_factura` -**ramane pe loc ca plasa de siguranta**: nu se muta, nu se slabeste. - -**Gol de acoperit, iesit tot in runda 14:** pentru **proforma** (`TIP = 4`, decizia 51) garda **nu -exista deloc** — `sterge_proforma` (`EXPORT:5610-5635`) e o procedura separata fara nicio verificare, -iar calea VFP iese din `do_sterge` inainte de restul (`ofacturare_comun.vc2:4707-4719`). Garda simetrica -ceruta de S5c e **cod nou**, nu reutilizare. -*Gata cand:* actiunea refuza corect si cu mesaj clar documentele trimise in eFactura, sterse, din -luna inchisa, referentiate, sau care au urmasi (retur, factura din aviz, factura din proforma) — -**inainte** de deschiderea formularului, nu ca `ORA-20000` la final. -*Depinde de:* S6, si de inchiderea perimetrului cu #6 (fisier partajat). - -#### S8 — Incarcarea documentului in formular - -> **PROIECTAT INTEGRAL in runda 14 — `docs\cercetare\s8_incarcare_document.md` (52 KB, 8 sectiuni).** -> **Schita de mai jos e corecta ca directie, dar gresita in piese, si nu marunt:** -> 1. **`completeaza_setari_document(toFactura, .T.)` copiaza 16 proprietati din ~120** -> (`COMUN\programe\ofacturare_comun.prg:361-411`) si **niciuna de identitate**. Lipsesc serie, -> numar, data, scadenta, `text_aditional`, `id_facturare`, `id_ruta`, `tip_saft`, `efactura`, -> `listare_detaliata`, `dataora_exp`, discountul de document, `discount_evidentiat`, `eproforma`, -> valuta, cursul. -> 2. **`cursor_retur_document(V_COPIERE = 1)` nu umple documentul, ci selectorul-sursa.** Rezultatul -> intra in `crsarticole` (gridul din stanga); `crsfactura` se creeaza **gol si ramane gol** — -> comentariul din cod e explicit (`COMUN\programe\ofacturare.prg:338`, `:455-457`). Transferul se -> face doar prin `do_adauga_tot` → `do_adauga_articol`, care pentru articolele gestionabile trece -> **prin dialogul de alegere din stoc**. -> 3. **`cursor_retur_document` nu intoarce `ID_VANZARE_DET` si nici `TAXCODE`** -> (`PACK_FACTURARE:3949-4054`). Fara `ID_VANZARE_DET`, cheia de linie presupusa de S8b **nu exista**, -> iar ruta ieftina `modifica_explicatie_articol` **devine neapelabila** — primul ei parametru *este* -> `V_ID_VANZARE_DET` (`PACK_FACTURARE:14511-14519`). -> -> **Recomandarea centrala:** S8 se construieste pe precedentul care face deja exact asta — -> `relisteaza_ofacturare_stoc` (`COMUN\programe\ofacturare_stoc.prg:456-742`), care reconstituie -> `poDate` + cursorul de linii dintr-un document salvat, prin `FACT_VFACTURI` + `FACT_VFACTURI_DETALII` -> (ambele au `ID_VANZARE_DET` si `TAXCODE`). Canalul final e intrebarea 1 din §8.2 al raportului -> (*recomandat:* procedura noua `cursor_editare_document`, ca sa nu se schimbe comportamentul copierii). -> -> **CORECTIE la raportul S8, facuta de sesiunea principala (runda 14):** raportul lasa deschis (N6, -> intrebarea 7) daca „`frm_facturare_articole2` (varianta paralela)" intra in perimetru, si recomanda -> „nu acum". **Premisa e gresita: `frm_facturare_articole2` NU e o varianta paralela de exclus — e -> PROTOTIPUL pe care se construieste formularul unificat**, conform S1 („se porneste de la prototip", -> `ofacturare.vc2:15741-19355`). Deci S8 il tinteste pe el, nu pe `frm_facturare_articole`. Faptul ca -> are `do_adauga_tot` / `do_adauga_articol` proprii, cu logica divergenta (fara testul `llGestionabil`, -> `ofacturare.vc2:17476`, `:17124`), **nu e un motiv de excludere, e exact driftul din 2017 pe care S2 -> il inchide** — cele doua se unifica, nu se aleg. Partea de tipuri 48/49 din intrebarea 7 **s-a -> inchis prin decizia 60**: sunt editabile. Intrebarea 7 e deci inchisa integral. - -> **CERINTA DE EXECUTIE ADAUGATA IN RUNDA 17 — `IN_STOC` se incarca din document, nu din nomenclator.** -> Vine din consecinta 1 a deciziei 54 (vezi S10, „Trei consecinte"), si se prinde **aici**, nu la S12. -> Cu flag-ul de regenerare pornit, `IN_STOC` nu mai e re-derivat de `adauga_articol_factura`, ci **vine -> din formular** — deci valoarea pe care o incarca S8 devine valoarea care decide **descarcarea de -> gestiune la reemitere**. Azi loader-ul lui #6 o citeste din nomenclatorul curent -> (`ofacturare_editare.prg:302-303`), deci un articol devenit intre timp gestionabil (sau invers) ar -> face reemiterea sa atinga **alt stoc decat documentul initial**, tacut. -> -> Trei consecinte concrete pentru canalul de citire (intrebarea 1 din §8.2, *recomandat* (B), -> `cursor_editare_document`): -> - canalul trebuie sa intoarca `IN_STOC` **asa cum a fost la emitere**, nu `GESTIONABIL = B.IN_STOC` -> din nomenclatorul de azi, cum face `cursor_retur_document` (`PACK:3993-4000`). E un **al treilea -> argument** pentru procedura noua, langa `ID_VANZARE_DET` / `TAXCODE` si langa `ID_POL` / `ID_CTR` -> lipsa din `VVANZARI_ARTICOLE`; -> - **valoarea istorica nu e stocata nicaieri**: `IN_STOC` nu e coloana pe `VANZARI_DETALII` (verificat -> pe DB, vezi S10), traieste doar in temp. Deci primul pas al lui S8 pe aceasta cerinta e sa -> stabileasca **de unde se reconstituie** — fie din urma lasata in rulaje / gestiune pentru documentul -> respectiv, fie se accepta nomenclatorul curent ca aproximatie **declarata explicit**, fie se adauga -> coloana (migrare DB, deci **DB inainte de EXE**, ca la S10). **Nu se presupune niciuna dintre -> variante**; e o **preconditie de proiectare a lui S8**, nu un detaliu de implementare; -> - pe tipurile **48/49** cerinta se intalneste cu decizia 60: acolo invariantul e `IN_STOC = 0` prin -> constructie, deci valoarea incarcata trebuie sa fie `0` indiferent ce zice nomenclatorul azi. -> -> *Criteriu de test (intra in „gata cand" al lui S8):* un document emis cu un articol caruia i s-a -> schimbat intre timp `IN_STOC` in nomenclator, deschis in formular, poarta **valoarea de la emitere**; -> iar reemiterea lui lasa **stocul agregat neschimbat** (masurat inainte / dupa, nu prin inspectia -> codului). - -> **Cerinta noua din S8b e mai blanda decat se credea:** lookup-ul „ultimul delegat / ultima masina" -> **are deja o garda** — `Empty(id_delegat) And Empty(id_masina)` (`ferestre_cere_date.vc2:3111`). -> Riscul ramane real, dar **numai** pe documentele fara delegat si fara masina. Raportul da inventarul -> complet al celorlalte initializari „pentru document nou" care trebuie sarite (§2.2). - -`completeaza_setari_document` pentru antet + `cursor_retur_document(V_COPIERE = 1)` pentru linii, -**fara** degradarea de tip din `do_copiaza`. Se incarca in plus, fata de copiere: serie / numar / -data / scadenta reale, discountul de document (`VANZARI.DISCOUNT`), `discount_evidentiat`, textul -aditional, datele din `frm_alte_date` (delegat, auto, agent, adresa de facturare), explicatia si -`taxcode` pe fiecare linie, si legatura cu sursa (`id_comanda`, `id_ctr`, lista de avize din -`vanzari_coresp`) — ca reemiterea sa reconsume aceeasi sursa. -**Campul de sursa exista deja** — nu e de adaugat, ci de pastrat vizibil: e `Ct_clb_altele`, cu -eticheta schimbata pe tip de `do_schimba_explicatia` („Nr. contract” / „Nr. comanda” / „Nr. factura” / -„Nr. facturi” / „Locatie”, `ofacturare.vc2:9633-9643`), eliminat azi din formular cand -`gnScadereStoc = 0` si tipul e 1/5/10 fara copiere. La modificare e blocat: schimbarea sursei ar -insemna alt document, nu o corectie. -Formularul arata **identic** cu cel de introducere: fara banda de avertizare, fara coloane cu -valorile initiale, fara panou de diferente (decizia 5). Se schimba titlul ferestrei si **butonul -principal** (decizia 9, vezi S8c). -*Gata cand:* formularul deschis pe un document existent arata exact documentul, pe fiecare tip de -sursa, si nu se distinge vizual de formularul de introducere; **si** liniile incarcate poarta `IN_STOC` -de la emitere, nu din nomenclatorul de azi (cerinta rundei 17, mai sus). -*Depinde de:* S7. - -#### S8b — Rutarea scrierii dupa ce s-a schimbat -Comasarea celor trei actiuni de modificare (G-bis): la confirmare se compara starea din formular cu -cea incarcata si se alege ruta — `modifica_date_factura` pentru antet, `modifica_explicatie_articol` -pentru explicatia liniei, regenerare pentru orice atinge sumele, nimic daca nu s-a schimbat nimic. -Cele trei actiuni vechi de pe `frm_facturi` (`do_modifica`, `do_modifica_explicatie`) se retrag -abia dupa ce formularul unificat le acopera, nu inainte. -**PROIECTAT (runda 13) — `docs\cercetare\s8b_rutarea_scrierii.md`. Reteta G-bis e corecta ca directie, -dar avea un gol nedocumentat, si el schimba forma solutiei.** - -- **Cele patru rute NU sunt teste independente, ci un lant cu prioritate — sumele primele.** - Motivul e concret: **`modifica_date_factura` nu e singura ruta care scrie cei 14 parametri de antet.** - **Sapte din 14** (`id_delegat`, `id_masina`, `id_facturare`, `listare_detaliata`, `dataora_exp`, - `id_agent`, `text_aditional`) sunt scrisi **si** de calea normala de emitere, prin `scrie_factura2`, - direct din `poDate` — **verificat direct de sesiunea principala** pe apelul de la - `COMUN\clase\ofacturare.vc2:14345-14359`. Nu exista doi proprietari ai acestor campuri: e **acelasi - obiect `poDate`** citit de ambele cai. -- **Deci, cand regenerarea porneste, `modifica_date_factura` NU se mai cheama.** Inainte de regenerare - ar scrie pe randul care urmeaza sa fie sters — pierdut. Dupa, ar fi a doua scriere pe aceleasi sapte - coloane, pe alt `ID_VANZARE`. Regenerarea **este** deja calea de scriere a antetului cand sumele se - schimba, nu o cale care trebuie compusa cu alta. -- **Ordinea rutarii:** (1) s-au schimbat liniile / discountul de document? → **regenerare, gata**; - (2) altfel, antet? → `modifica_date_factura`; (3) altfel, doar explicatie de linie? → - `modifica_explicatie_articol`; (4) altfel → **nimic**. -- **Explicatia de linie e transportata gratuit de regenerare**, prin parametrii nativi ai lui - `adauga_articol_factura` (`V_EXPLICATIE`, `V_TAXCODE`) — **cu conditia** ca regenerarea sa citeasca - din cursorul curent al formularului, nu dintr-un snapshot separat. -- **Cel mai probabil fals pozitiv al criteriului „deschid si inchid → nicio scriere":** lookup-ul - „ultimul delegat / masina al clientului" din `frm_alte_date.Init` - (`COMUN\clase\ferestre_cere_date.vc2:3119-3136`). E cod gandit pentru **document nou**. Pe calea de - editare ar suprascrie delegatul **incarcat corect din document** cu ultimul folosit de client — deci - ori documentul apare „modificat" fara ca nimeni sa fi atins nimic, ori, daca ruleaza inainte de - snapshot, **salveaza tacit delegatul gresit**. **S8 trebuie sa-l sara explicit pe calea de editare.** -- **Comparatia se face pe valori rotunjite la precizia de scriere** (`gnPc` / `gnPPretV` / `gnPCant`), - nu pe valoarea binara — altfel rotunjirea singura produce diferente. - -**INCHIS, nu de reintrebat (marcaj pus in runda 17).** Punctul de mai jos are raspuns: **decizia 52** — `do_modifica` ramane activ pentru multi-selectie, iar formularul unificat preia doar cazul cu un singur document. Se pastreaza enuntul, nu intrebarea: -1. **Editarea multipla** — `do_modifica` de azi lucreaza pe multi-selectie, capacitate fara echivalent - in formularul unificat. Se accepta pierderea ei la retragere, sau `do_modifica` ramane activ separat - exact pentru cazul asta? - -**Doua goluri de inchis inainte de implementare (semnalate, nu presupuse):** -- **Ce canal scrie `serie_act` / `numar_act` / `data_act` / `data_scad` / `id_ruta` / `tip_saft` / - `efactura`** pe documentul reemis — **nu apar in `scrie_factura2`**, deci vin pe alt drum. Raspunsul - decide daca mai e nevoie de o scriere separata dupa regenerare. **Se inchide in S9.** -- ~~**`cursor_retur_document` re-deriva pretul la incarcare?**~~ **INCHIS (runda 13): NU** — pretul vine - din `VANZARI_DETALII.PRET` stocat, fara `JOIN` catre contract sau politici (`PACK_FACTURARE:3960-4062`, - verificat direct). **Mecanismul de detectie e valid.** Detaliul despre rotunjire si `DIFERENTA` — in S10. - -*Gata cand:* fiecare din cele patru situatii din tabelul G-bis produce exact scrierile din tabel si -nimic in plus; in special, deschiderea si inchiderea fara modificari nu scrie nimic. -*Depinde de:* S8. - -#### S8c — Butonul comutator de antet si salvarea separata a antetului -Decizia 9, proiectata in I. Antetul unui document existent porneste **blocat**; un singur -`but_modifica` il deschide si isi schimba imaginea in discheta de salvare; a doua apasare cheama -`modifica_date_factura` si **nu atinge articolele** — exact ce face azi `do_modifica`, dar din -formularul unificat, dupa care antetul se blocheaza la loc si butonul revine la creion. **Fara nicio -bifa**: butonul deschide tot antetul, inclusiv serie / numar / data / scadenta; cele patru bife de -azi nu se transpun. `Termina` ramane salvarea intregului document. -Se generalizeaza reteta de blocare din `clb_tx_data.dezactiveaza()` / `reactiveaza()` -(`lb_tx.vc2:521-538`) la containerele de antet care azi nu au niciuna (I), si se aplica **uniform** -tuturor campurilor de antet — asta e ce face posibila eliminarea bifelor. -Comutarea imaginii butonului se copiaza din `frm_rulaje.se_modifica_assign` (`rulaje.vc2:4716-4773`): -o singura instanta `but_modifica`, cu `.cpicturedown` / `.cpictureup` / `.Picture` rescrise impreuna -si `.Refresh()` — vezi decizia 9 pentru capcana hover-ului. -**Ce deschide efectiv butonul (decizia 25, verificat pe cod in G-bis).** „Tot antetul” nu inseamna -ca totul se salveaza pe aceeasi ruta: -- **cei 14 parametri** — pe loc, prin `modifica_date_factura`. Asta livreaza S8c; -- **grupul B** (tip document, valuta, zi curs, client, sursa, gestiune sursa, politica de preturi) si - **grupul C** (incasare) — **nu au ruta de scriere pe loc**, deci raman **blocate in etapa I**, cu - explicatie la hover; se deschid in etapa II, cand modificarea lor marcheaza documentul pentru - regenerare; -- **grupul A** (analiticele) — **read-only permanent in #13** (decizia 26), editarea e a lui #6. - -Deci in etapa I butonul deschide **exact cei 14**, si asta nu e o abatere de la decizia 9, ci -consecinta ei corecta pe starea de azi a codului: restul campurilor n-ar avea unde sa se scrie. -*Gata cand:* se poate corecta delegatul unui document emis, salva antetul si inchide formularul, -**fara nicio scriere in `VANZARI_DETALII` si fara recalcul de sume in note sau rulaje**; se poate -corecta si data scadentei prin acelasi buton, fara alt control intermediar; iar un document deschis -si inchis fara a apasa butonul nu produce nicio scriere. -*Atentie la formularea criteriului:* „nicio scriere in note si rulaje” ar fi un criteriu imposibil — -schimbarea seriei / numarului / datei propaga coloanele de identitate in `JV2007` si `RUL` prin -procedura insasi (I-bis). Ce se verifica e ca **nu se ating sumele**, nu ca nu se atinge tabelul. -*Depinde de:* S8. - -#### S9 — Stergerea si reemiterea intr-o singura tranzactie, cu `ID_FACT` pastrat -Partea grea. Doua lucruri simultan: - -**Constrangere de la decizia 35, inainte de orice:** reemiterea **scrie prin `pack_facturare`**, pe acelasi -drum ca emiterea (`scrie_factura2` → `contabilizeaza_articol`, cu parametrul de cont de la decizia 34). -`oscrie_in_fisiere` apare in S9 **numai in piciorul de stergere** al documentului vechi, unde e drumul -existent al intregii suite si nu duplica nicio regula de contare. **Nu se scrie si nu se corecteaza niciun -rand din `ACT_TEMP` direct din VFP.** Consecinta de secventiere: **S9 nu poate porni inaintea parametrului -deciziei 34** — nu mai exista varianta „editarea nu cere nimic in pachet". - -**VERIFICAT ca decizia 35 e realizabila** (`docs\cercetare\parametru_cont_contabilizeaza_articol.md`, -sectiunea 1): pe partea Oracle, **a doua emitere a aceluiasi document merge pe cod identic**. -`scrie_factura2` cheama `initializeaza_date_factura` (`PACK_FACTURARE:1808-1846`) la fiecare apel, iar -aceasta face `DELETE FROM VANZARI_DETALII_TEMP` (`:1835`) si `nid_act := 0` (`:1836`) — contorul folosit -de `scrie_nota` / `scrie_discount` / `scrie_tva` la fiecare `INSERT INTO ACT_TEMP` **porneste curat si la -reemitere**. Nimic din `contabilizeaza_articol`, ramura noua inclusa, nu citeste vreo stare care sa -presupuna „documentul e nou": variabilele de sesiune sunt **citite**, nu comparate cu o stare anterioara. -**Singurul obstacol real al regenerarii e in alt pachet** — `PACK_CONTAFIN`, prin `SET_IDFACT` si -`PK_DOCUMENTE`, exact ce e deja analizat mai jos in acest S9. Adica: „cod separat" inseamna **alta -procedura, in alt pachet**, nu o a doua ruta de contare — deci **decizia 35 nu e contrazisa**. - -- **Tranzactia.** Apelul de stergere (`oscrie_in_fisiere` + `finalizeaza_stergere_nota` + - `sterge_factura`) se muta in interiorul tranzactiei deschise de `do_scrie_articole`, inaintea - primului `adauga_articol_factura`. Fara `commit` intermediar (`VANZARI_DETALII_TEMP` e GTT - `ON COMMIT DELETE ROWS`). La orice eroare, rollback total. -- **Tripleta de mai sus e si ce face ciclul NEUTRU fata de stoc — nu se simplifica.** `sterge_factura` - **nu** reverseaza rulajele. Revenirea stocului vine din `PACK_CONTAFIN.STERGE_DIN_RUL` - (`UPDATE RUL SET STERS = 1, ID_UTILS = ..., DATAORAS = ...`, - `ff_2026_07_29_03_COMUN_PACK_CONTAFIN.sql:1867-1882`, apelata la `:8473`), la care se ajunge prin - `finalizeaza_scriere_act_rul(tnScrieSterge = 2)` ← `oscrie_in_fisiere(2, ...)`. Adica **stocul e - derivat** din miscari nesterse, iar marcarea lor `STERS = 1` inchide subiectul — dar **numai daca - piciorul de stergere chiar trece prin `oscrie_in_fisiere`**. O implementare care „simplifica" pasul - la un apel direct de `sterge_factura` ar lasa rulajele in picioare si reemiterea ar descarca - gestiunea **a doua oara**, tacut. Verificat pe sursa pachetului. - `docs\cercetare\stoc_la_stergere_si_reemitere.md`. -- **`ID_FACT`.** Se citeste inainte de stergere (vezi capcana `STERS = 0` din E) si se impune - documentului nou, printr-un comutator folosit **numai** de regenerare. `SET_IDFACT` nu se schimba - neconditionat — e cod comun intregii suite. -- **`ID_UTIL` / `DATAORA` (audit de creare, S14).** Acelasi tipar ca la `ID_FACT`: se citesc din - documentul vechi **inainte** de stergere si se impun explicit documentului nou — altfel `INSERT - INTO VANZARI` de la reemitere le rescrie cu utilizatorul si momentul curente, si „data/utilizatorul - crearii" se pierde tacut. Detaliu si recomandare de coloane: S14. - -**VERIFICAT — se poate, dar cere TREI schimbari simultane, nu una.** Raport: -`docs\cercetare\idfact_refolosire_si_documente.md`. Verdict: **DA, cu conditii.** - -1. **Varianta comentata din `SET_IDFACT` NU rezolva S9** — nu se reactiveaza. - (`PACK_CONTAFIN.pck:3016-3035`) cauta documentul pe `NRACT + SERIE_ACT + DATAACT + ID_CTR` cu - `STERS = 0`; dupa soft-delete `STERS` e deja 1, deci **nu-l gaseste** si cade tot pe secventa. - In plus e vulnerabila la coliziuni cu un document viitor neinrudit care are aceleasi patru campuri. - `ID_FACT`-ul se **citeste explicit inainte de stergere** (VFP il are in cursor) si se **transmite**. -2. **`SET_IDFACT` are nevoie de o cale de intrare noua, inerta implicit.** Azi **nu exista niciun - canal**: `pack_contafin.nIdFact` e scrisa neconditionat de fiecare apel, si nu exista alt global sau - parametru de sesiune. Solutia cu suprafata minima: **variabila noua de pachet** („ID_FACT fortat", - implicit `NULL`), citita **doar in corpul activ** al lui `SET_IDFACT`, consumata si resetata la prima - folosire. **Nu se atinge semnatura `SET_IDFACT(V_GCS)`** — altfel s-ar schimba apelul pentru toti. -3. **Blocajul real nu e in `SET_IDFACT`, ci in `DOCUMENTE`** — si asta e descoperirea care schimba - estimarea. Scrierea de azi (`PACK_CONTAFIN.pck:796-817`) e un **`INSERT` simplu** pe `ID_DOC`, care e - **PRIMARY KEY** (`PK_DOCUMENTE`, `fn_script.sql:5192-5198`). Stergerea (`STERGE_DIN_ACT:1835-1855`) e - **soft-delete pur** — randul vechi ramane fizic in tabel cu `STERS = 1`. Deci reemiterea cu acelasi - `ID_FACT`, cu codul de azi, **arunca `ORA-00001`**. Dovada, nu presupunere. - `MERGE`-ul deja comentat (`:818-847`) **nu ajuta**: are doar `WHEN NOT MATCHED`, deci ar ignora tacit - randul existent si documentul reemis ar ramane cu `STERS = 1`. E nevoie de **upsert real**, cu - `WHEN MATCHED THEN UPDATE SET STERS = 0, ...` pe restul coloanelor de identitate. - -**Succesiunea corecta**, in aceeasi tranzactie: citeste `ID_FACT` **si lista sursa** -> sterge documentul -vechi (soft-delete, ca azi) -> seteaza variabila fortata -> scrie documentul nou pe drumul obisnuit -> -**remigreaza atasamentele** -> commit doar daca toate au reusit; orice eroare -> rollback total, -documentul vechi intact. - -**DOI PASI ADAUGATI DE S11 (runda 13) — niciunul nu „vine gratis" din drumul normal:** -1. **Citirea listei sursa INAINTE de stergere, si refurnizarea ei la reemitere.** `VANZARI_CORESP` si - `marcheaza_facturat` **se rescriu singure** prin `CASE`-ul pe `ntip` din `finalizeaza_factura` — dar - **numai daca** `ntip` si lista sursa (`clistaid` / `clistaid_avize`) sunt refurnizate. Ele traiesc in - randurile `VANZARI_CORESP` ale documentului **vechi**, care dupa stergere nu mai sunt disponibile. - **Fara acest pas nu apare nicio eroare — corespondenta si `FACTURAT` pur si simplu nu se scriu.** - Scriere lipsa silentioasa, exact tipul de defect care trece de o verificare vizuala. -2. ~~**`UPDATE ATASAMENTE_VANZARI SET id_vanzare = :nou`**~~ — **INLOCUIT de decizia 53 (runda 14): - atasamentele NU se remigreaza, se sterg la reemitere.** Documentul reemis porneste curat; PDF-ul se - regenereaza la prima listare. Pasul ramane **cod nou**, dar e o **stergere**, nu un `UPDATE` - (`STERS = 1`, ca sa ramana consistent cu view-ul `VATASAMENTE_VANZARI`, care filtreaza `b.sters = 0` - — vezi S11). Consecinta acceptata explicit de Marius: **urma PDF-ului efectiv trimis clientului - dispare din sistem.** - -**Suprafata de risc pentru suita**, masurata: **~25-30 cai de apel** — cate o copie a lui -`COMUN\programe\oscrie_in_fisiere.prg` in fiecare produs ROA, plus trei variante care cheama -`SCRIE_IN_ACT` direct. Toate converg in acelasi punct, `SET_IDFACT(V_GCS)`. **Garantia e structurala, -nu prin inspectie:** cu variabila implicit `NULL` si citita strict local, toate cele ~25-30 de cai raman -identice cu azi — nu trebuie verificat fiecare apelant. -**Dar `INSERT` -> `MERGE` in `DOCUMENTE` e cod comun apelat de toata suita.** Practic ramura noua e -inerta (secventa e monoton crescatoare, `WHEN MATCHED` nu se declanseaza niciodata pe drumul normal), -**dar se testeaza un ciclu normal de scriere in fiecare produs care scrie facturi sau note**, nu doar in -ROAFACTURARE. - -*Gata cand:* o editare care mareste cantitatea peste stocul curent trece (stocul documentului -initial e eliberat inainte); documentul reemis are **acelasi `ID_FACT`, serie, numar si data** ca -inainte; **randul din `DOCUMENTE` e acelasi, revenit la `STERS = 0`**, nu unul nou; o eroare la scriere -lasa documentul initial intact; si un ciclu normal de scriere ramane neschimbat in celelalte produse. -*Depinde de:* S8b. - -#### S10 — Pretul care nu trebuie re-derivat -Inventarul ramurilor din `adauga_articol_factura` (H) si, unde e nevoie, un mod in care preturile -vin din formular. - -**VERIFICAT (runda 9) — `docs\cercetare\s10_pret_rederivat.md`. Verificarea 3 e inchisa.** -**Exista exact o ramura care suprascrie pretul: contractul.** Cu `OPT_FACTURARE = 3` (sau `NULL`, care -se implicit-eaza tot la 3), daca `CTR_ARTICOLE.PRET_UNITAR` e diferit de 0, acea valoare **inlocuieste** -pretul trimis de VFP — `DECODE(A.PRET_UNITAR, 0, V_PRET_TEMP, A.PRET_UNITAR)` — necondiționat de faptul -ca linia a fost sau nu atinsa pe ecran. Procedura **nu are nicio notiune de „reemitere"**: la fiecare -apel cu acelasi `V_ID_CTR` se reface derivarea din contract. Pe celelalte ramuri (implicita, restaurant) -pretul trimis de VFP e respectat ca atare. **Nu exista niciun parametru sau flag care sa opreasca -re-derivarea** — deci S10 nu poate „cere pachetului sa nu re-derive", trebuie sa lucreze in jurul -ramurii de contract. Riscul se materializeaza doar cand pretul din contract s-a schimbat intre emitere -si reemitere; pana atunci rezultatul e identic si nimeni nu observa. Detaliile pe discount, TVA si curs -sunt in sectiunile 6 si 7 ale raportului. -**PROIECTAT (runda 13) — `docs\cercetare\s10_pret_contract_reemitere.md`. Faptul portant al rundei 9 -se reconfirma la sursa, dar problema e mai larga decat „pretul".** - -- **Nu e doar pretul: acelasi `SELECT` de pe ramura de contract alimenteaza CINCI valori.** - **Verificat direct de sesiunea principala** (`PACK_FACTURARE:5149-5166`): `DECODE(A.PRET_UNITAR, 0, - V_PRET_TEMP, A.PRET_UNITAR)` **impreuna cu** `B.PROC_TVAV`, `B.ID_VALUTA`, `A.PRET_CU_TVA` si - `C.IN_STOC` intra in aceeasi interogare. Raportul semnalase trei (pret, TVA, valuta); sunt **cinci** — - se adauga flagul „preturi cu TVA" si **gestionabilitatea**, care vine din **nomenclatorul curent**, - nu din document. **Orice garda trebuie sa acopere tot setul**, nu doar pretul. -- **Nu exista alt loc care suprascrie pretul mai departe in lant** — `scrie_in_vanzari` copiaza `PRET` - neschimbat din `VANZARI_DETALII_TEMP` in `VANZARI_DETALII`. -- **Divergenta e reala, nu ipotetica:** pe baza de dezvoltare, **3 din 11** linii deja facturate pe - contract au azi alt pret in contract decat cel facturat. Esantionul e mic si **nu se extrapoleaza** la - productie — dar demonstreaza ca situatia se intampla. -- **Discountul ramane passthrough** — acolo nu e nicio problema. -- **Recomandarea raportului: varianta (c) — se avertizeaza si se cere confirmare, cu diferenta - afisata.** Satisface literal criteriul „decis explicit, nu accidental", se implementeaza **integral in - VFP**, fara sa se atinga `pack_facturare`. Variantele: (a) accepta tacit — respinsa, e exact ce cere - criteriul sa nu se intample; (b) blocheaza — mai sigura, dar frustranta daca divergentele sunt dese; - (d) ocoleste ramura trimitand `V_ID_CTR = NULL` — **respinsa motivat**, pierde **permanent** legatura - `VANZARI_DETALII.ID_CTR`. -- **„Reemitere identica" nu e garantata uniform:** pe ramurile `ELSE` / restaurant, da; pe **comenzi** - esueaza **tare** (eroare, deci vizibila); pe **contract** si pe **aviz (`ntip = 4`)** esueaza **tacit**. - Riscul pe aviz **nu fusese semnalat pana acum** ca decizie explicita. - -**GOLUL LUI S8b E INCHIS — `cursor_retur_document` NU re-deriva pretul.** Verificat direct de sesiunea -principala (`PACK_FACTURARE:3960-4062`): pretul vine din **`VANZARI_DETALII.PRET`** stocat, printr-un -`FROM VANZARI_DETALII A1` fara niciun `JOIN` catre `CTR_ARTICOLE` sau catre politici; `PROC_TVAV` la fel, -din document. **Deci mecanismul de detectare a schimbarii din S8b e valid** — un document deschis nu -apare „modificat" din cauza citirii. **Confirmat independent si pe partea VFP** (raportul S10): in -`ofacturare.prg:266-283`, ramura `Case m.llCopiere` are **prioritate indiferent de tip** — pentru un -document de contract (`2,6,26,52`) **nu se ajunge deloc** la `cursor_contract` la incarcare. Citirea si -scrierea sunt guvernate de **cursoare complet diferite, fara cod comun**: riscul de suprascriere tacita -apare **strict la re-scriere**, niciodata la deschidere. O garda pusa la scriere nu afecteaza citirea si -nu e afectata de ea. -> **Dar citirea nu e o copie curata, si asta conteaza pentru S12.** Pretul returnat e -> `ROUND(A.PRET, nzecimale_pretv) + A.DIFERENTA` (pe valuta: `ROUND(CURS * ROUND(PRET, ...) / -> MULTIPLICATOR, ...) + DIFERENTA`). Adica **rotunjit la precizia de sesiune si cu `DIFERENTA` pliata -> inauntru**. La reemitere se trimite inapoi valoarea pliata, deci **impartirea `PRET` / `DIFERENTA` se -> reface**, nu se conserva. De verificat in S12 ca o reemitere identica reproduce **suma**, chiar daca -> nu reproduce neaparat aceeasi impartire — si ca `DIFERENTA` nu se acumuleaza la reemiteri repetate. - -### RASTURNAT de decizia 54 (Marius, runda 14) — nu garda, ci eliminarea re-derivarii - -Cele trei intrebari de mai sus (blocare vs. avertizare; ramura de aviz; masurarea frecventei) **sunt -inchise, si nu prin alegerea uneia dintre variante — prin respingerea premisei lor.** Formularea lui -Marius: *„nu inteleg de ce se schimba valorile fata de ce se incarca din factura sursa care se modifica? -asta este comportamentul pe care il doresc — modific sursa"*. - -**Deci:** la reemitere se scriu **valorile din formular** (incarcate din documentul editat), nu se -reciteste sursa. Nu se cere utilizatorului sa confirme o schimbare pe care n-a cerut-o. Variantele -(b) si (c) **cad amandoua**; ramane obiectivul (a-invers): re-derivarea, unde exista pe calea de -reemitere, se **elimina**. - -**Si aici e problema reala, spusa pe fata:** runda 9 a stabilit deja ca **„nu exista niciun parametru -sau flag care sa opreasca re-derivarea"**, iar singura varianta care ocolea ramura — (d), `V_ID_CTR = -NULL` — a fost **respinsa motivat**, pentru ca pierde permanent legatura `VANZARI_DETALII.ID_CTR`. -Prin urmare decizia 54 **cere, cel mai probabil, o modificare in `pack_facturare`** (un semnal „nu -re-deriva, foloseste ce ti-am trimis" pe `adauga_articol_factura`), nu o solutie curat VFP. Asta atinge -un pachet din `COMUN`, partajat de toata suita — **iar modificarile de pachet se aplica tuturor -deodata, fara cale de intoarcere prin alegerea de la decizia 49.** Trebuie proiectata ca inerta pentru -apelantii de azi (implicit = comportamentul actual), exact ca la S4g. - -**VERIFICAT in runda 14 — `docs\cercetare\s10_rederivare_pe_calea_reemiterii.md`.** Confirmat pe fisier -**si** pe DB (`all_source`, corp VALID, `last_ddl_time = 2026-08-09 20:03:50`). - -**Se pierd intr-un SINGUR loc:** `adauga_articol_factura`, in `CASE`-ul de la `PF:5052-5220`, adica -**inainte** de `INSERT INTO VANZARI_DETALII_TEMP` (`PF:5222`). Tot ce urmeaza — copierea temp → -`VANZARI_DETALII` (`PF:13705-13757`) — e **1:1**, deci „copiaza `PRET` neschimbat" e adevarat dar **nu -e o protectie**: dauna e amonte de temp. - -**RAMURILE CARE RE-DERIVA SUNT TREI, NU UNA. Raportul rundei 9 gresea.** - -| valoare | **contract** (2/6/26/52) | **aviz** (`ntip = 4`) | **comenzi** (3/21/28/42/47) | restaurant (45) | `ELSE` | -|---|---|---|---|---|---| -| `PRET` | re-derivat din `CTR_ARTICOLE.PRET_UNITAR` daca `<> 0`; temp daca `= 0`; **NULL daca e NULL** (`PF:5149`) | **re-derivat neconditionat** din avizul sursa (`PF:5082`) | temp de facto — `WHERE A.PRET = V_PRET_TEMP` (`PF:5077`) | temp | temp | -| `PROC_TVAV` | re-derivat din `CRM_POLITICI_PRET_ART` | re-derivat din aviz | re-derivat din `COMENZI_ELEMENTE.PTVA` | derivat din `JTVA_COLOANE` | derivat din `JTVA_COLOANE` pe `ID_JTVA_COLOANA` **din formular** | -| `ID_VALUTA` | re-derivat | re-derivat | re-derivat | temp | temp | -| `PRET_CU_TVA` | re-derivat | re-derivat | re-derivat | temp | temp | -| `IN_STOC` | re-derivat din `NOM_ARTICOLE` | re-derivat | re-derivat | temp | temp | - -- **Ramura AVIZ e cea mai agresiva din tot `CASE`-ul** (`PF:5080-5103`): `PRET` se inlocuieste - **neconditionat**, nu exista nici `DECODE`, nici `AND A.PRET = V_PRET_TEMP`, **si nu exista bloc - `EXCEPTION`**. Un pret modificat pe ecran e inlocuit tacit. In plus **nu filtreaza `A.STERS = 0`**, - desi coloana exista — linii sterse ale avizului sursa pot fi citite. Si depinde de `clistaid`, care - la reemitere **trebuie repopulata** (leaga direct de pasul 1 adaugat in S9). -- **Ramura comenzi esueaza tare, nu tacit** — fara `EXCEPTION`, o linie care nu se mai potriveste da - **ORA-01403**, vizibila. Mai putin periculoasa, dar tot re-derivare. -- **Ramura contract are o portita** — `EXCEPTION WHEN NO_DATA_FOUND` (`PF:5167-5185`) pune **toate cele - cinci** pe valorile din formular. **Comportamentul cerut de decizia 54 exista deja in cod**, dar - numai pe calea de exceptie. Aviz si comenzi **nu au** asa ceva. -- **Capcana NULL, dedusa din semantica `DECODE`, nerulata:** daca `CTR_ARTICOLE.PRET_UNITAR` e `NULL` - (nu `0`), `DECODE` nu potriveste literalul `0` → rezultatul e **NULL**, deci pretul din formular se - pierde complet si linia intra cu `PRET` gol. In dev cazul nu apare (27 randuri, 0 NULL) — **ceea ce - nu spune nimic despre productie**. -- **`PROC_TVAV` nu vine din formular pe NICIO ramura** — nu e parametru al procedurii. Pe calea „buna" - se deriva din `JTVA_COLOANE` pe `ID_JTVA_COLOANA`-ul trimis de formular, deci reproduce documentul - **doar cat timp cota nu s-a schimbat**. La o modificare legala de cota, reemiterea schimba TVA-ul - **chiar si pe `ELSE`**. -- **`IN_STOC` nu ajunge in `VANZARI_DETALII`** — coloana nu exista (verificat pe DB). Traieste doar in - temp, unde decide **descarcarea de gestiune**. -- **Dupa insertul in temp nimic nu mai suprascrie cele cinci valori.** `adauga_diferente_pret` atinge - numai `DIFERENTA`, `scrie_factura_avize` numai `CANTITATE`, `scrie_seturi` numai `ID_VANZARE_SET`. - -### Cum se implementeaza decizia 54 - -**Curat VFP nu se poate — confirmat cu dovada pozitiva, nu prin absenta.** Semnatura n-are comutator -(`PF:4989-5015`, 27 de parametri, toti date de linie); globalele pachetului n-au (`PF:126-212`); poarta -`CASE`-ului se decide pe `ntip` (care ajunge in `VANZARI.TIP`, deci nu se poate falsifica) si pe -`CONTRACTE.OPT_FACTURARE` (date de contract, nu parametru). Singurele parghii VFP care ar devia pe -`NO_DATA_FOUND` sunt `V_ID_CTR = NULL` — **deja respinsa** — si `V_ID_POL = NULL`, care are **exact -acelasi defect pe alta coloana** (`PF:5258` → `temp.ID_POL` → `PF:13737` → `VANZARI_DETALII.ID_POL`) si -in plus ar rupe ramura aviz, care potriveste pe `A.ID_POL`. **Notata aici tocmai ca sa nu fie -redescoperita ca „solutie" intr-o runda urmatoare.** - -**Deci decizia 54 cere obligatoriu modificare in `pack_facturare`** — dar mica, si care nu adauga -comportament nou: - -- **Semnalul:** variabila noua de pachet („regenerare in curs"), implicit `0` = comportamentul de azi, - scrisa de VFP **dupa** `initializeaza_date_factura` (`ofacturare.vc2:13981`) si inainte de bucla de - `adauga_articol_factura`. *Recomandata* fata de un parametru `DEFAULT 0`, din trei motive: se - potriveste cu stilul pachetului (deja o punga de stare de sesiune); se scrie o data pe document, nu - o data pe linie, in toate produsele; si **punctul de resetare exista deja** — - `initializeaza_date_factura` (`PF:1808-1917`) reinitializeaza ~40 de globale si goleste temp - (`PF:1835`), deci flag-ul nu poate scapa in documentul urmator. -- **Ce face:** un `WHEN` nou, **primul** in `CASE`-ul de la `PF:5052`, cu **acelasi corp ca `ELSE`-ul de - azi** (`PF:5189-5203`). Nimic inventat — se forteaza ramura care exista deja. **Acopera dintr-o data - toate trei ramurile problematice**, fiind in acelasi `CASE`. `INSERT`-ul de la `PF:5222` ramane - neatins. -- **Inert prin constructie** pentru toti apelantii de azi, exact ca la S4g. **Consecinta de livrare: - DB inainte de EXE.** - -### Trei consecinte de acceptat explicit, nu de descoperit la S12 - -1. **`IN_STOC` ar veni din formular.** Nu murdareste documentul (coloana nu exista), dar schimba - **comportamentul de stoc** al reemiterii. Ca reemiterea sa fie identica, **S8 trebuie sa incarce - valoarea cu care s-a scris documentul initial** — si **azi nu o incarca**: loader-ul lui #6 o citeste - din nomenclatorul curent (`ofacturare_editare.prg:302-303`). **Flag-ul singur nu rezolva asta.** - **PRELUAT (runda 17) ca cerinta de executie in S8**, cu cele trei consecinte pentru canalul de citire - si criteriul de test — vezi caseta „CERINTA DE EXECUTIE ADAUGATA IN RUNDA 17" din S8. Aici nu mai e - nimic de decis. -2. **Se pierde o validare pe ramura comenzi.** Azi, `A.PRET = V_PRET_TEMP` + lipsa lui `EXCEPTION` fac - ca o linie care nu se mai potriveste cu comanda sa **opreasca** scrierea. Cu flag-ul pornit, - reemiterea unei facturi din comanda nu mai verifica linia fata de comanda. Decizia 54 pare sa vrea - exact asta, dar e schimbare de comportament — **se declara, nu se descopera**. -3. **`PROC_TVAV` ramane derivat, nu preluat.** Reproducerea exacta cere ca S8 sa pastreze - `ID_JTVA_COLOANA` **si** ca `JTVA_COLOANE` sa nu se fi schimbat. Daca se cere reproducere exacta si - peste o modificare legala de cota, `PROC_TVAV` trebuie sa devina **parametru** — schimbare mai mare - decat flag-ul, **de decis separat**. - -### Obstacol pentru S9, gasit in treacat - -**`VVANZARI_ARTICOLE` nu expune `ID_POL` si nu expune `ID_CTR`** (lista de coloane interogata pe DB) — -exact cei doi pe care `adauga_articol_factura` ii cere (`V_ID_POL`, `V_ID_CTR`) si pe care -`VANZARI_DETALII` ii pastreaza. **Reemiterea nu poate folosi acest view ca atare**: ori se completeaza -view-ul, ori loader-ul citeste direct din `VANZARI_DETALII`. Se leaga de intrebarea 1 din S8 (canalul de -citire a liniilor) — **un argument in plus pentru varianta (B), procedura noua.** - -*Gata cand:* pentru fiecare tip, documentul reemis are **exact valorile din formular** — pret, TVA, -valuta, flag „preturi cu TVA" — iar o reemitere fara modificari reproduce documentul identic, inclusiv -dupa ce pretul din contract s-a schimbat intre timp. -*Depinde de:* S9. - -#### S11 — Legaturile care raman pe `ID_VANZARE` -`ID_FACT`, seria, numarul si data se pastreaza (E, F), deci legaturile prin `ID_FACT` sunt in -regula. Ramane discontinuitatea pe **`ID_VANZARE`**, care se schimba: `ATASAMENTE_VANZARI`, -`marcheaza_facturat`, `vanzari_coresp`, si eventualele referinte de incasari / plati. -De verificat si daca `DOCUMENTE` primeste un al doilea rand pe acelasi `ID_DOC` sau il refoloseste. - -**PROIECTAT (runda 13) — `docs\cercetare\s11_legaturi_id_vanzare.md`. Inventarul s-a facut prin -FK-urile reale din Oracle, nu prin grep — si asta a scos doi consumatori pe care nicio cautare de text -nu-i gasise.** - -- **Premisa centrala, corectata: mecanismul lui #6 NU se mosteneste de S9.** La #6, documentul isi - **pastreaza randul** din `VANZARI` — `actualizeaza_vanzari` face `UPDATE ... SET COD = nou, - STERS = 0`, deci **`ID_VANZARE` nu se schimba**, doar `COD`, si atasamentele se realiniaza in acelasi - gest. La S9, reemiterea merge pe drumul normal de emitere (`INSERT ... RETURNING ID_VANZARE`), deci - **rand nou, cheie noua, si `actualizeaza_vanzari` nu se declanseaza deloc.** Tot ce e protejat azi - tacit de #6 e **expus** la regenerare. Asta nu era in plan. -- **Sapte tabele au FK declarat pe `VANZARI.ID_VANZARE`. Doua nu erau in nicio lista:** - **`REST_NOTE_PLATA`** (modul restaurant) si **`IPS_VOYAGES_VANZARI`** (modul specific unui client). - Clasificate **(A) suspecte** — pachetele lor PL/SQL n-au fost citite integral. - **INCHIS de Marius, runda 14 — riscul dispare, si nu prin presupunere.** Intrebat direct, a raspuns: - *„daca este vorba despre facturarea voiajelor din ROAACNPRO, acolo nu merge modificarea de genul - acesta, ci doar editarea de la #6"*. **Documentele acestor module sunt IN AFARA domeniului - regenerabil al #13** — pentru ele ramane calea #6 (modificare pe loc). Pachetele lor **nu mai trebuie - citite**. Doua consecinte de dus mai departe: **(a) garda lui S7 trebuie sa le refuze EXPLICIT**, nu - doar sa nu le trateze — altfel „in afara domeniului" e o intentie, nu o garantie; **(b)** conditia se - declara in S13, la actualizarea `tipuri_documente_facturare.md`. -- **`ATASAMENTE_VANZARI` — corectie de premisa, in favoarea noastra.** **Nu** sunt documente incarcate - de utilizator: sunt **PDF-uri auto-generate la listare** (`poDate.scrieAtasamente()` dupa - `export2pdf`; cursorul nu vine din niciun dialog de fisier — cautat explicit, zero potriviri). Deci - **nu e pierdere ireparabila**, cum presupusese briefingul. Se rup totusi real: view-ul - `VATASAMENTE_VANZARI` filtreaza pe `b.sters = 0`, deci dupa regenerare atasamentele vechi **dispar - tacit** din toate interogarile (inclusiv din ROAGEST / ROAIMOB), desi BLOB-ul ramane fizic. - ~~**(A) — remigrare printr-un simplu `UPDATE ... SET id_vanzare = :nou`**~~ — **INCHIS altfel, - decizia 53 (runda 14): se STERG la reemitere.** Marius a ales a treia varianta, nici remigrare, nici - marcaj „versiune inlocuita". Documentul reemis porneste curat. **Consecinta i-a fost spusa explicit - si a acceptat-o:** motivatia care sustinea remigrarea — *pastrarea urmei PDF-ului trimis clientului* — - **cade odata cu ea**; dupa editare nu mai exista in sistem dovada a ce a primit clientul. Punctul 15 - se inchide aici. -- **Incasarile si platile nu sunt afectate — si dovada e structurala, nu statistica:** niciun tabel de - incasari/plati n-are FK pe `ID_VANZARE`, si nici referinta text. Se leaga prin **`ID_FACT`**, care se - pastreaza. **(C)**, cu dovada dubla. -- **eFactura, `DOCUMENTE`, listarile, `VANZARI_CANTITATI` — (C)**, toate cheiate pe `ID_FACT` sau - rescrise automat. - -**INCHISE, nu de reintrebat (marcaj pus in runda 17).** Punctul 1 e transat de cercetarea garzilor din runda 14 (garda exista si e corecta; S7 o muta in pre-flight read-only) — ramane **limitare declarata**. Punctul 2 e rasturnat de **decizia 53**: atasamentul vechi **nu se remigreaza, se sterge**, deci premisa „factura editata ajunge cu PDF-ul vechi" nu se mai produce. Punctul 3 e inchis in runda 14, cu cerinta ca **S7 sa refuze explicit** cele doua tipuri. Se pastreaza ca inventar: -1. **Editarea unui document care are deja retur emis pe el e azi BLOCATA, nu doar riscanta** — - `sterge_factura` arunca `ORA-20000`. Garda e corecta (protejeaza lantul), iar **S7 planifica deja - sa semnaleze conditia inainte de intrarea in formular**, nu ca eroare Oracle la final. Ce ramane de - decis: se accepta ca **limitare declarata** (utilizatorul sterge intai documentele-copil), sau se - vrea alt comportament? -2. **Atasamentul vechi arata continutul dinainte de editare.** Remigrat pe documentul nou, factura - editata ajunge sa aiba atasat PDF-ul **vechi**. De decis daca asta e exact ce se vrea (audit trail), - sau daca vechiul ar trebui marcat cumva ca „versiune inlocuita". -3. **`REST_NOTE_PLATA` / `IPS_VOYAGES_VANZARI`** — de confirmat ca tipurile lor sunt in afara - domeniului regenerabil. - -*Gata cand:* documentul reemis se regaseste corect in borderoul eFactura, in listari, in atasamente -si in rapoartele care il cauta dupa numar. -*Depinde de:* S9. - -#### S12 — Test pe fluxul real, regenerarea -Cate un caz pe fiecare sursa, rulat de doua ori (o data fara modificari — reemitere identica, o data -cu modificari de cantitate, pret si linii adaugate / sterse). Se verifica: documentul initial -`sters = 1` cu nota si rulajele lui, documentul nou corect, sursa eliberata **si** reconsumata -(comanda redevine facturabila si redevine facturata), stocul corect dupa ciclu, totalurile -denormalizate din `VANZARI`, si ca o reemitere identica produce **exact** aceleasi sume. - -**Probe adaugate de runda 13:** -- **Reemitere repetata de trei ori pe acelasi document** — `DIFERENTA` **nu se acumuleaza**. Motivul: - `cursor_retur_document` intoarce pretul deja **rotunjit si cu `DIFERENTA` pliata inauntru** (S10), iar - reemiterea trimite inapoi valoarea pliata, deci impartirea `PRET` / `DIFERENTA` se **reface** la - fiecare ciclu. Se verifica **suma**, nu impartirea — dar se verifica si ca impartirea nu deriveaza. -- **„Reemitere identica" nu e garantata uniform** (S10): pe `ELSE` / restaurant da; pe **comenzi** - esueaza **tare** (eroare vizibila); pe **contract** si pe **aviz (`ntip = 4`)** esueaza **tacit**. - Fiecare din cele patru situatii isi are cazul lui de test, cu rezultatul asteptat **declarat dinainte** - — inclusiv cele care trebuie sa dea eroare. -- **Deschid si inchid fara sa modific nimic → nicio scriere** (S8b), rulat pe un document al unui client - **cu activitate recenta** — cazul in care lookup-ul „ultimul delegat / masina" ar polua antetul. - -*Depinde de:* S10, S11. - -#### S14 — Audit: creare, modificare si stergere pe document - -**Cerinta (decizia 63):** pentru fiecare document, sase informatii de audit — data + utilizator -pentru creare, pentru modificare (daca e cazul) si pentru stergere (daca e cazul). - -**Cercetarea s-a terminat** — `docs\cercetare\audit_vanzari_creare_modificare_stergere.md`. -Verificat pe `all_tab_columns` (`ROA_CENTRAL`, schema live) si pe cod. - -**Ce exista deja, pe `VANZARI`:** -- `ID_UTIL` + `DATAORA` — **creare**, ambele `NOT NULL`, scrise consecvent la fiecare `INSERT` - (`PACK_FACTURARE.scrie_in_vanzari`, `EXPORT:13598-13640`, si a doua ruta din - `finalizeaza_avize_lucrare`, `EXPORT:14930`). Niciun `UPDATE VANZARI SET ID_UTIL/DATAORA` in tot - pachetul (cautare directa, zero rezultate). -- `ID_UTILS` + `DATAORAS` — **stergere**, nullable, scrise consecvent de `sterge_factura` - (`EXPORT:5496-5499`) si `sterge_proforma`. -- (neceruta, dar arata conventia) `ID_UTILFACT` + `DATA_FACTURAT` — facturare din aviz. -- Pe `VANZARI_DETALII`: acelasi tipar de creare/stergere, plus `ID_UTIL_VALID` + `DATAORA_VALID` - (validare). - -**Conventia casei:** o pereche utilizator + data per eveniment. Patru din cele sase informatii -cerute exista deja si se scriu consecvent. - -**Ce lipseste, integral:** nicio coloana de **modificare**, nici pe `VANZARI`, nici pe -`VANZARI_DETALII`. Interogat explicit `all_tab_columns` cu filtru pe `%MODIF%` — zero rezultate. -`DATA_ACT` nu e echivalentul: e data contabila, propagata in `ACT`/`DOCUMENTE`/`JV2007`/`RUL` -(verificat in `modifica_date_factura`, `EXPORT:14439-14500`), nu un marcaj de audit. - -**Capcana, si e miezul povestii:** editarea din #13 se face prin stergere + reemitere (S9), iar -documentul nou se scrie pe **acelasi `INSERT`** ca o creare normala (`scrie_in_vanzari`). Fara un -pas explicit, `ID_UTIL` / `DATAORA` ale documentului reemis ar deveni tacut utilizatorul si data -**modificarii**, iar informatia despre creare s-ar pierde — exact ce cere decizia 63 sa nu se -intample. **Precedentul de rezolvare exista deja in plan, pentru `ID_FACT`** (sectiunea „E. -`ID_FACT`", linia 762, si S9: se citeste din documentul vechi inainte de stergere si se impune -celui nou). **Niciun pas echivalent nu e proiectat azi pentru `ID_UTIL` / `DATAORA`** — cautare -directa in tot planul, zero mentiuni. - -**Atenuare, nu solutie:** la regenerare randul vechi ramane cu `STERS = 1` si cu `ID_UTILS` / -`DATAORAS` completate — iar acea stergere **este** momentul modificarii. Cum `ID_FACT` se pastreaza -peste regenerare, lantul vechi → nou e parcurgibil, deci o parte din istoric e reconstruibila din -randurile existente, fara coloane noi. O pereche explicita pe documentul curent ramane insa -preferabila: se citeste direct, fara parcurgerea lantului. - -**Complicatie reala, verificata pe cod livrat de #6:** editarea de linie -(`COMUN\programe\ofacturare_editare.prg`) scrie `id_utils` / `dataoras` pe `VANZARI_DETALII` si pe -randuri care **nu sunt sterse** — `ofacturare_editare.prg:492-494` (corect, cu `sters = 1`), -`:501-505` (odata cu `sters = 0`, pe rand viu) si `:522-525` (pe un rand proaspat inserat). Acolo -perechea de stergere e folosita ca marcaj „cine a umblat ultima data", nu strict ca „sters de/la". -**Consecinta:** un raport de audit nu poate citi `ID_UTILS` / `DATAORAS` ca „sters de/la" fara sa -puna si `STERS` in conditie. `ofacturare_editare.prg` e perimetrul lui #6, in lucru — semnalat aici, -neatins. - -**Recomandare (de confirmat, nu decisa):** -1. **O singura pereche noua pe `VANZARI`**, urmand conventia casei — nume propus - `ID_UTILM` / `DATAORAM` (utilizator si data ultimei modificari). Numele e de confirmat. -2. **La regenerare (S9): `ID_UTIL` / `DATAORA` se transporta din documentul vechi in cel nou**, - exact ca `ID_FACT` — vezi nota adaugata la S9. Perechea noua de modificare primeste utilizatorul - curent si `sysdate`. Fara acest pas, creare si modificare se suprapun. -3. **Perechea de stergere nu are nevoie de nimic** — exista si e scrisa consecvent. -4. **Cere migrare de DB** (`ALTER TABLE` pe `VANZARI`) — **DB inainte de EXE**, ca la S10. Scriptul - intra in `D:\ROA\DATABASE\SCRIPTURI_CLAR\`, `versiune_db.txt` se bumpeaza. - -**De decis cu Marius — AMBELE INCHISE prin decizia 65 (runda 16):** -- **(a)** Auditul e cerut numai pe antet (`VANZARI`) sau si pe linii (`VANZARI_DETALII`)? **Raspuns - prin deductie, nu spus explicit de Marius:** locul de afisare ales de el e gridul din `frm_facturi`, - care listeaza **documente** — deci auditul e pe **antet**. -- **(b)** Se afiseaza undeva in interfata (formular / lista), sau ramane doar pentru interogare? - **Da, se afiseaza** — in **gridul din `frm_facturi`**, nu in formularul facturii propriu-zise. - Prim pas de implementare: inventarul acelui grid, posibil sa existe deja coloane de audit. - -*Gata cand:* documentul reemis pastreaza `ID_UTIL` / `DATAORA` ale creatiei originale; perechea noua -de modificare e completata la fiecare regenerare, cu utilizatorul si momentul curente; perechea de -stergere ramane neschimbata fata de azi; migrarea DB e aplicata inainte de EXE. -*Depinde de:* S9. - -#### S13 — Diff, review, changelog, documentatie - -**Atentie: S13 NU e momentul in care se face review-ul.** Conform metodei de executie, **fiecare story -isi are propriile teste, propriul diff si propriul code review, inainte de commit-ul ei** — S13 nu -strange la final ce n-a fost revizuit pe parcurs. Ce ramane aici e **inchiderea**, adica exact ce nu se -poate face per story: - -- **Review de ansamblu**, pe suma povestilor: coerenta intre ele, cai ramase orfane, cod mort din - fluxul vechi care trebuia retras si n-a fost, si verificarea ca alegerea de la decizia 49 chiar - functioneaza in ambele sensuri. -- **Changelog** `:nou:`, cu mentiunile declarate explicit pe parcurs: asimetria `do_modifica` - corectata prin recalculul la cerere (decizia 45), si faptul ca `do_modifica` ramane activ pentru - multi-selectie (decizia 52). -- **Documentatie**: `COMUN\docs\tipuri_documente_facturare.md` — ce tipuri sunt regenerabile si ce - tipuri sunt **declarate explicit ca neacoperite** (48/49 si `frm_facturare_articole2`, daca se merge - pe recomandarea din S8), plus confirmarea din S11 pentru `REST_NOTE_PLATA` si `IPS_VOYAGES_VANZARI`. -- **Rularea suitei complete** — toate testele scrise per story, la un loc, nu doar cele ale ultimei - povesti. -- **Commit-urile finale**, in **ambele** repo-uri (ROAFACTURARE si COMUN), dupa aprobare. - ---- - -## Riscuri - -- ~~**Cel mai mare: perimetrul comun cu #6.**~~ **Inchis prin decizia 30** — implementarea lui #13 - incepe dupa terminarea lui #6, deci nu exista doi scriitori simultan pe `ofacturare_comun.vc2` / - `ofacturare_editare.prg`. Riscul revine doar daca ordinea se schimba. -- ~~**Riscul „cod nou in `pack_facturare`" e evitat prin constructie (decizia 27-bis).**~~ **Caduc dupa - deciziile 34 si 35, si riscul revine ca risc principal.** Politica tehnica per `SCC` si scrierea in - `CRM_POLITICI_PRET_ART` nu se mai fac — deci cade si riscul ca o politica tehnica sa apara in - `caut_politici_curente_util()`. In locul lui: **`pack_facturare` se modifica** (parametru de cont + - ramura fara politica in `contabilizeaza_articol`), iar pachetul e **comun intregii suite** — ROACONT, - ROAGEST, ROACONTRACTE, ROAAUTO, ROAACNPRO. Mitigarea e structurala, nu prin inspectie: parametru - **`DEFAULT NULL` la finalul listei** si ramura inerta cand lipseste, astfel incat apelantii existenti - sa fie identici cu azi. - **MASURAT — `docs\cercetare\suprafata_regresie_contabilizeaza_articol.md`. Riscul e mai mic decat parea.** - `contabilizeaza_articol` are **trei apelanti, toti interni pachetului**, toti pozitionali cu acelasi - singur argument — **zero apelanti externi, nici PL/SQL, nici VFP, in niciunul din cele sapte produse**. - `adauga_articol_factura` e apelata **pozitional peste tot**, niciodata cu notatie pe nume (`=>`), iar - cele sapte copii `COMUN\clase\ofacturare.vc2` sunt **acelasi sit de apel duplicat**, nu sapte - implementari care ar putea diverge: fisierele difera intre ele (patru variante distincte pe MD5), dar - **textul apelului e identic cuvant cu cuvant**, doar offsetul de linie difera. Exista in plus doua - implementari proprii reale — ROAGEST (`Programe\ofactureaza.prg:264`) si ramura `_deviz` din ROAAUTO / - ROAACNPRO. - **Precedentul e activ, nu teoretic:** ROAGEST apeleaza azi `adauga_articol_factura` cu **24 din 25 de - parametri**, omitand `V_TAXCODE` si `V_LOT` tocmai pentru ca au `DEFAULT NULL`. Tiparul propus e deja - in uz in productie. - **Ce ramane:** toate cele **sapte** produse emit efectiv prin acest pachet, deci aria de regresie e - toata suita chiar daca niciun apelant nu se modifica. -- ~~**Anomalie preexistenta: `scrie_factura2` apelata cu 16 din 17 parametri.**~~ **INCHISA — fals - pozitiv, nu intra ca risc.** `oExecuta` (`COMUN\programe\oproceduri_comune.prg:121-159`) deleaga la - `oExecute` (`:173-504`), care face `SQLExec(lnHandle, lcSql, lcCursor)` (`:330`) — **textul SQL nu e - rescris**, deci al 17-lea parametru chiar n-are placeholder. Explicatia e tiparul cunoscut al - driverului ODBC Oracle: cand ultimul parametru declarat e un `REF CURSOR OUT`, driverul il ia din - catalog, nu din textul apelului, si intoarce randurile ca *result set* — captat exact de al treilea - argument al lui `SQLExec`. *Nuanta de pastrat:* mecanismul din interiorul driverului **nu poate fi - confirmat din codebase**, e verificabil doar prin comportament; dovada e indirecta, dar consistenta — - acelasi tipar in toate cele patru situri, neschimbat de la `v 2.0.13` la `v 2.0.93`, iar ecranul de - verificare depinde de acel cursor populat la fiecare emitere, deci un apel esuat s-ar fi vazut demult. -- **Decizia 35 leaga etapa II de aceeasi modificare de pachet.** Editarea prin regenerare nu mai are cale - proprie (canalul `oscrie_in_fisiere` e respins ca al doilea cod de contare), deci **un blocaj pe - parametrul deciziei 34 blocheaza si S9-S12**, nu doar etapa I. In schimb dispare riscul de divergenta - intre regula de contare de la emitere si cea de la editare. -- **`ofacturare.vc2` e in COMUN si are 843 KB / 25 de clase**, folosite de toata suita. Formularul - vechi trebuie sa ramana functional pana cand cel nou acopera toate tipurile — deci comutator, nu - inlocuire. -- **`ID_FACT` pastrat cere atingerea unui punct comun intregii suite.** `SET_IDFACT` e in - `PACK_CONTAFIN` si e chemat de fiecare scriere de note din toate produsele ROA. Comutatorul din S9 - trebuie sa fie inert pentru toate celelalte cai; altfel regresia nu e in ROAFACTURARE, ci peste tot. -- **Formularul identic la introducere si la modificare taie o plasa de siguranta.** Decizia 9 o repune - **doar pe antet** — butonul protejeaza campurile de antet, nu si liniile. Pe articole nu exista niciun - semnal ca modificarea va sterge si va rescrie documentul; confirmarea de la `Termina` ramane - singurul moment in care se poate spune ce urmeaza sa se intample. De formulat cu grija. -- **Ascunderea datei de curs poate lasa un document fara curs corectabil.** `poDate.zi_curs` se - trimite neconditionat catre cursoarele de articole; daca lipseste cursul pentru acea zi, Oracle da - eroarea 20005 si se deschide formularul de curs — dar utilizatorul nu mai are unde sa corecteze data - daca i-am ascuns campul. Regula din M trebuie sa aduca inapoi campul in exact acest caz. -- **Sectiunea pliata ascunde campuri care schimba documentul.** Analiticele si datele de incasare - intra sub un panou inchis implicit. Daca un camp obligatoriu pe un anumit tip ajunge acolo, - utilizatorul primeste eroarea fara sa vada campul. Panoul trebuie sa se deschida singur cand - contine un camp necompletat si obligatoriu, si sa arate in antet ca are ceva completat. -- **Mutarea `frm_alte_date` muta si alocarea de numere.** Bonul fiscal, POS-ul si chitanta primesc - numar din masina de stari a incasarii. Daca ea ajunge sa ruleze la deschiderea formularului in loc - de la alegerea tipului de incasare, se aloca numere pentru documente care nu se mai emit. -- **`S3c` si `S4c` ating cod comun.** Parametrizarea sursei atinge `factureaza` (toata suita), iar - discountul editabil atinge listarile si eventual eFactura. Fiecare merge separat, cu regresie - proprie. -- **Reemiterea nu e idempotenta prin constructie.** Daca la reemitere pretul se re-deriva (H) sau - cursul valutar al zilei difera, documentul "nemodificat" iese cu alte sume decat originalul. - Testul din S12 (reemitere identica) exista tocmai ca sa prinda asta. -- **`verifica_total_document`** (`PACK_FACTURARE:16009+`) insereaza automat o linie de corectie cand - totalul difera de suma notelor. La regenerari repetate trebuie confirmat ca nu se acumuleaza — - aceeasi intrebare ca S7 din #6. -- **Cautarea in linie schimba un obicei.** Utilizatorii care lucreaza azi cu gridul de articole - vizibil si filtrare locala pierd vederea de ansamblu. Merita verificat pe un client inainte de a - scoate definitiv gridul vechi. - -## Dependente - -- **#7 (pret cu TVA pe linie)** — flagul `pret_cu_tva` pe linie apare in gridul unificat; se - pastreaza comportamentul stabilit acolo. -- **#12 (nomenclator ca lista de preturi)** — S4 construieste cautarea in linie peste politici de - preturi; daca #12 muta pretul in nomenclator, S4 se rescrie. -- **#16** — bug-ul de focus / renumerotare la revenirea din cursul valutar. Confirmat ca apare **dupa** - ce antetul s-a inchis si s-a eliberat (`ofacturare.prg:248`), la recuperarea erorii Oracle 20005. - In formularul unificat nu mai exista un antet inchis la care sa te intorci, deci **mecanismul lui - dispare** — de confirmat in S4d, nu de presupus. -- **#6** — **dependenta de calendar, nu de perimetru** (decizia 30): implementarea lui #13 incepe - dupa ce #6 se termina. Proiectarea si cercetarea merg mai departe in paralel. - -## Preconditii de mediu - -- Orice DDL / modificare `PACK_FACTURARE`: sursa de referinta e `MARIUSM_AUTO` pe `ROA_CENTRAL`, - nu productia (`COMUN\docs\scripturi-migrare-db.md`). Scripturi nivel Oracle 10.2, CRLF, - idempotente, `versiune_db.txt` actualizat. -- Sursa completa `PACK_FACTURARE` nu e in working copy; copia pe disc e - `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\03\ff_2026_03_23_04_COMUN_PACK_FACTURARE.sql` (16 948 linii), - cu alta numerotare decat exportul din baza. -- Editarile pe `.vc2` / `.sc2` trec prin `git_sync.ps1` + `txt2vcx.ps1`, cu atentie la - `COMUN\docs\conventie_encoding_cp1252.md` (diacriticele din `.vc2` sunt cp1250 — vezi si memoria - proiectului). +# Plan #13 — formular unificat de facturare + editare prin regenerare + +Sursa: `COMUN\docs\todos.txt` punctul 13. +Mockup: `docs\mockup_13_formular_unificat.html` (versiunea 5). +Cercetare pe cod, toata in `docs\cercetare\`: `inventar_controale_formulare.md`, +`proforma_copiere_puncte_intrare.md`, `import_roris_roaacnpro.md`, `discount_pe_articol.md` si +`discount_verificare2.md` (a doua il corecteaza pe primul), `modifica_antet_bifa.md`, +`valuta_si_curs.md`, `buton_comutator_picture.md`, `modifica_date_factura_parametri.md`, +`retur_si_lista_preturi.md`, `factura_retur_document.md`, `roaauto_facturi.md`, +`roaauto_articole_lista_preturi.md`, `cont_venit_articol_fara_politica.md`. +Stare: **propunere, neinceput**. Analiza facuta pe cod la 09.08.2026, in sase runde. + +## Cerinta, asa cum a fost formulata + +Trei lucruri intr-un singur punct: + +1. **Unificarea formularelor** — `date_factura` / `date_aviz` sa intre in formularul de facturare, + ca sa nu mai fie doua ferestre pentru acelasi document. +2. **Fara incarcarea prealabila a tuturor articolelor** din toate politicile de preturi — pot fi mii + si dureaza mult aducerea lor de pe server. +3. **Editarea facturii / avizului prin regenerare**, in toate variantele (politici de preturi, + comanda, contract, aviz): formularul se redeschide completat ca inainte de salvarea initiala, se + fac modificarile ca la introducere, iar la confirmare documentul initial se marcheaza `sters = 1` + si se salveaza unul nou — ca sa se vada ce s-a modificat. + +Decizia lui Marius, 09.08.2026: **intai unificarea, apoi editarea prin regenerare.** Explicit +**nu** pe calea editarii directe a notelor / rulajelor / articolelor. + +## Deciziile lui Marius, 09.08.2026 — luate, nu de reluat + +1. **#13 coexista cu #6**, nu il inlocuieste. +2. **`ID_FACT` se pastreaza**, ca la orice modificare. Documentul reemis nu-si schimba identitatea. +3. **Toate datele se pastreaza, inclusiv numarul documentului.** Scopul e modificarea documentului, + nu emiterea altuia. +4. **Antetul care intra in generarea lui `ID_FACT` (serie, numar, data) are cale proprie de + modificare** — nu trece prin regenerare. +5. **Un singur formular, aceeasi infatisare la introducere si la modificare.** Fara banda de + avertisment, fara coloane cu valorile initiale, fara panou de diferente. +6. **Se integreaza si modificarea de antet, si modificarea de articol care exista azi** — sa nu + ramana trei actiuni de modificare pe acelasi document. + +## Deciziile lui Marius, runda 3 (09.08.2026) — luate, nu de reluat + +7. **`frm_alte_date` intra in formular**, in sectiunea pliata. Se inchide intrebarea ramasa deschisa + in runda 2 (recomandarea de atunci — „ramane dialog” — **cade**). +8. **Sectiunea pliata primeste in plus analiticele**: venit / cheltuiala, sectie, responsabil (si, + prin simetrie, lucrare). Antetul vizibil ramane aerisit, cu **controalele grupate** pe intelesuri, + nu insirate. +9. **Un singur buton comutator pe antet.** Antetul se deschide blocat. Un singur `but_modifica` + (creionul) il deblocheaza si **isi schimba imaginea in discheta lui `but_salvare`**; a doua + apasare salveaza **doar antetul** si il blocheaza la loc. Un singur control, nu bifa plus buton — + mai compact. **Toata factura se salveaza in continuare din `Termina`.** Rostul ramane acelasi: + antetul se editeaza intentionat, nu din greseala, si se poate corecta fara a trece prin articole. + + **Butonul singur deschide tot antetul. Nu mai exista bife individuale** — nici cele patru de azi + (serie / numar / data / scadenta). Un singur control comanda toata protectia antetului. + + *Istoric, ca sa nu se reia:* runda 3 propusese un buton `Modificare / Salveaza` care bloca **tot + documentul** — respins, protectia e doar pe antet. Runda 4 propusese **bifa + buton separat** — + respins la 09.08.2026 in favoarea butonului comutator. Runda 5 propusese **pastrarea celor patru + bife individuale sub buton** — respins la 09.08.2026: bifele dispar cu totul. + + **Verificat pe cod la 09.08.2026** (`docs\cercetare\buton_comutator_picture.md`): **tiparul exista + deja in suita si se copiaza, nu se inventeaza.** `frm_rulaje.se_modifica_assign` + (`COMUN\clase\rulaje.vc2:4716-4773`) comuta exact asa **o singura instanta** de `but_modifica` + intre creion si discheta — nu instantiaza a doua clasa. Trei lucruri de retinut din el: + - se rescriu **impreuna** `.cpicturedown`, `.cpictureup` **si** `.Picture`, plus `.ToolTipText`, + urmate de `.Refresh()`. Numai `.Picture` nu ajunge: hover-ul standard din + `buton.MouseEnter` / `MouseLeave` (`_cmd_base.vc2:88-97`) rescrie `Picture` din `cPictureUp` / + `cPictureDown`, deci iconita veche ar reveni la primul mouse-over; + - numele de fisier se dau **fara cale** (`"save_sus.bmp"`), rezolvate prin `SET PATH` — care + include si `GRAFICE`, si `COMUN\GRAFICE` (`Programe\roafacturare.prg:85-108`). Calea relativa + `..\grafice\...` din clasa e buna doar la design-time; + - imaginile exista: `COMUN\grafice\save_sus.bmp` / `save_jos.bmp` (discheta), + `modific_sus.bmp` / `modific_jos.bmp` (creion, perechea folosita de clasa). + + Clasa de salvare din biblioteca se numeste **`but_salveaza`**, nu `but_salvare` + (`cmd_butoane.vc2:340-352`); e sursa numelor de imagini, dar nu se instantiaza. Clasa + `but_modifica` e la `cmd_butoane.vc2:184-198`. Butoanele **nu** au `do_activeaza` / + `do_dezactiveaza` — pe ele se lucreaza direct pe `.Enabled`. + + **Consecinta asupra celor patru bife individuale** (serie / numar / data / scadenta, vezi I): + **se elimina**, si **odata cu ele se abandoneaza si conditia lor** de azi + (`chkSerieAct.Enabled = !EMPTY(NVL(poRec.numar_act,0))`, `ofacturare_comun.vc2:5736-5750`). + Decis de Marius la 09.08.2026: **butonul deschide tot**, fara conditii pe camp. Un singur control, + o singura regula. Cat de departe merge „tot” — vezi **decizia 19**: exact cat scrie + `modifica_date_factura`, parametru cu parametru. +10. **Proforma foloseste acelasi formular.** +11. **Copierea foloseste acelasi formular**, ca document nou, cu antetul editabil. +12. **Formularul trebuie sa *poata* fi apelat cu sursa deja completata** — din pagina de comenzi cu + comanda, si din programul de contracte cu contractul, fara ca utilizatorul sa mai aleaga ulterior. + **Nu se integreaza contractele acum**; nu depinde de #10. +13. **Un singur buton de adaugare a articolelor, cu `xmenu()`** — nu doua butoane separate „adauga + tot” si „alege”. Optiunile din meniu se schimba dupa sursa si acopera **comanda, contractul si + avizele**, cu alegere selectiva acolo unde are sens (de exemplu o singura rata de contract). + Butoanele de linie stau deasupra tabelului. +14. **Discountul pe articol, procent si valoare absoluta**, amandoua accesibile. +15. **Data cursului valutar apare doar cand are sens.** Sunt doua concepte diferite, care azi impart + acelasi camp mereu vizibil: (a) **factura in valuta**, unde si articolele au pret in valuta, si + (b) **factura in lei cu articole care pot avea pret in valuta**, convertite in lei la cursul din + data cursului, cu documentul si contabilitatea in lei. Formularul trebuie sa fie **ergonomic si + simplu** — campul nu apare cand nu e nimic de convertit. + +### Deciziile lui Marius, runda 6 (09.08.2026) + +16. **Lista de preturi e disponibila mereu, indiferent de sursa — si liniile suplimentare se pot si + sterge.** Pe o factura din comanda sau din contract trebuie sa se poata **adauga si sterge** + articole libere din lista de preturi, nu doar articole din sursa. Cazul real, formulat de Marius: + clientul a comandat ceva, iar la facturare mai vrea ceva in plus sau vrea sa schimbe — deci + documentul trebuie sa poata devia de la comanda, in ambele sensuri. Sursa umple documentul, nu il + inchide. Optiunea „Cauta in lista de preturi…" ramane in meniul butonului de adaugare **pentru + toate sursele**, inclusiv pentru documentele deja emise care se modifica. + **Verificat pe cod** (`docs\cercetare\retur_si_lista_preturi.md`, B): **contractul o are deja** + (`crsarticole` e populat de `cursor_preturi` / `cursor_contract` cu lista intreaga, al doilea grid + e un adaos, nu o restrictie), **comanda nu** — `cursor_comanda` umple `crsarticole` **doar** cu + articolele comenzii (`ofacturare.prg:266-308`). Deci golul real e pe comanda, si e in **continutul + cursorului**, nu in vreun `Visible` de buton. Reteta exista deja in produs: ramura de copiere + adauga lista de preturi peste cursorul sursei cu `APPEND FROM` (`ofacturare.prg:454-473`) — se + generalizeaza ea, nu se inventeaza alta. +17. **Returul intra in formularul unificat, cu tot cu alegerea facturilor sursa.** Factura de retur + facuta din facturi anterioare e un caz de acoperit explicit — lipsea si din mockup, si din + proiectare. *Vezi sectiunea N.* + **Sunt doua mecanisme distincte, si prima cercetare l-a vazut doar pe al doilea:** + (a) **factura de retur ca document** (tipurile 8, 9, si avizul 24) — alege facturile sursa **la + nivel de document**, cu selectie multipla, si isi populeaza liniile din ele; **gestiunea si + pretul de achizitie vin neschimbate din linia originala**, verificat in `cursor_retur_document`. + **Exista deja si merge** — nu se reproiecteaza. Vezi N.1; + (b) **`But_retur`** — retur de articole intr-o factura de vanzare normala (tipurile 1, 5, 7, 10), + unde factura sursa se alege **per articol**, iar gestiunea nu se mosteneste, ci se alege. Vezi N.2. + Sunt doua fluxuri de cod independente, fara punct comun. Decizia 17 le duce pe amandoua in + formularul unificat; ce se proiecteaza nou e (b) ridicat la nivel de document, nu (a). +18. **Facturile emise din ROAAUTO intra in perimetru la modificare.** ROAAUTO le emite din formularul + lui, dar scrie tot in `VANZARI_DETALII`, si pe ele trebuie sa se poata adauga articole din lista + de preturi. **Emiterea ramane la ROAAUTO** — se unifica doar modificarea. + **Verificat pe cod** (`docs\cercetare\roaauto_facturi.md`): acelasi `PACK_FACTURARE`, acelasi drum + `VANZARI_DETALII_TEMP` -> `VANZARI_DETALII`, tip de document **`-12`**, pe care editorul lui #6 il + **vede deja**, testat pe date reale. + **Precizarea lui Marius, 09.08.2026, verificata pe cod** + (`docs\cercetare\roaauto_articole_lista_preturi.md`): in ROAAUTO **exista deja adaugarea de + articole reale**, pe langa liniile generice — mecanismul „Alte servicii” din `frm_incasare_finala`. + Deci descrierea „doar linii sintetice cu `id_articol` negativ” din primul raport e **incompleta**. + Nuanta care conteaza pentru proiectare: sursa lui e **nomenclatorul brut**, nu lista de preturi, + articolele oferite sunt **fara stoc** (`in_stoc = 0 and in_crm = 1`), iar **pretul se tasteaza + manual** — nu vine din politici. Ce se cere e ca **acelasi lucru sa fie posibil si la modificarea + documentului**, nu doar la emitere, **si nu numai pentru facturile auto, ci pentru orice tip**, + cu **ambele surse**: lista de preturi (cu pret calculat) sau nomenclatorul (cu pret tastat). + **Vezi O.** +19. **Toti parametrii lui `modifica_date_factura` trebuie sa fie modificabili din butonul de antet.** + Butonul comutator nu deschide un subset ales de noi: daca procedura Oracle stie sa scrie un camp + de antet, formularul trebuie sa aiba controlul prin care acel camp se poate schimba. + **Verificat pe cod** (`docs\cercetare\modifica_date_factura_parametri.md`): sunt 15 parametri, + din care **14 sunt campuri** — al 15-lea, `V_ID_VANZARE`, e identitatea randului si nu se + editeaza. Din cele 14: + - **13 au deja control** in `frm_modifica_factura` si se muta ca atare in antetul unificat; + - **`V_EFACTURA` nu are niciun control nicaieri** (`ofacturare_comun.vc2:4576` il forteaza `0` + la selectie multipla, altfel vine din `Scatter`) — **primeste unul**, e singurul camp nou-nout + cerut de decizia asta; + - `V_TIP_SAFT` are control, dar ascuns dupa `gl406` (`:5739-5741`) — ramane conditionat, nu se + forteaza vizibil. + Vezi I-bis pentru contractul procedurii, care impune si **cum** se cheama, nu doar ce se trimite. +20. **Din nomenclator se aleg si articole gestionabile, si negestionabile.** Filtrul `in_stoc = 0` + al lui ROAAUTO **nu se preia** — acolo e o ocolire a subiectului gestiunii, nu o regula de produs. + Consecinta acceptata: pe documentele auto vor coexista linii care descarca stoc cu linii care nu + descarca, caz care azi nu exista nicaieri. *Vezi J si O-bis, intrebarea 2.* + **Intrebarea care insotea decizia — „cu ce cont de venit intra un articol fara politica de pret” + — s-a inchis prin cercetare: nu exista cont de venit pe linie.** Vezi **J-bis**. + +### Deciziile lui Marius, runda 7 (09.08.2026) + +21. **Cand `NOM_ARTICOLE.CONT` e gol pe un articol negestionabil, se aplica un fallback la un cont + implicit.** Nu se accepta `NULL` (comportamentul de azi al liniilor „Alte servicii" din ROAAUTO) si + nu se refuza adaugarea articolului. Contul anume se alege la implementare. + **Atentie la perimetru:** decizia priveste **contul de gestiune** (`VANZARI_DETALII.CONT`). Cand a + fost luata nu se stia inca faptul, stabilit mai tarziu in aceeasi runda, ca pe linie exista **doua** + conturi cu surse diferite — cel de venit (`NOTE_CONTABILE.SCC`, prin politica de pret) nu e acoperit + de aceasta decizie si e inca deschis. *Vezi J-bis.* + Ipoteza de la care a pornit Marius — corespondenta 3xx -> 7xx dintr-un tabel — **nu s-a confirmat + ca tabel**, dar intentia ei da: legatura articol -> cont de venit exista, prin politica de pret si + `NOTE_CONTABILE`, configurata manual de contabil. +22. **Linia de retur fara factura originala e permisa.** Pe tipurile 8, 9, 24, „Cauta in lista de + preturi…" si „Alege din nomenclator…" se comporta ca pe orice alt document — nu apar doar pentru + corectii si nu sunt marcate special. Consecinta acceptata: gestiunea si pretul de achizitie se + aleg (nu se mostenesc), iar maximul returnabil de pe server nu se aplica acelei linii. *Vezi N.3.* +23. **Secventierea deciziei 18: `pack_auto` se cerceteaza acum**, inainte de orice estimare, nu cand ii + vine randul la livrare. *Vezi O, „Secventierea".* **Rezultat:** riscul tehnic nu exista — + `PACK_AUTO` nu citeste `VANZARI` / `VANZARI_DETALII` deloc. Decizia 18 nu mai trebuie sa fie ultima. +24. ~~**Articolul ales din nomenclator primeste `id_pol`-ul unei politici de pret implicite.**~~ + **RETRASA in runda 8, inlocuita de decizia 27.** Ramane in plan doar ca sa nu fie reintrodusa: + politica implicita ar fi evitat codul nou in `pack_facturare`, dar cu pretul unei intrebari de + configurare cu contabilul si al unui cont de venit uniform pentru orice articol adaugat asa. + Marius a ales in loc corespondentele `CORESP_CONT_VENCHELT`. *Vezi decizia 27 si J-quater.* +25. **Butonul de antet deschide tot; ce nu se poate salva pe loc forteaza regenerare.** La a doua + apasare, cei **14 parametri** merg prin `modifica_date_factura`, iar orice camp schimbat din + **grupul B** (tip document, valuta, zi curs, client, sursa, gestiune sursa, politica de preturi) + sau **C** (incasare) **marcheaza documentul pentru regenerare**, aplicata la `Termina`. Decizia 9 + ramane intacta ca intentie — butonul chiar deschide tot antetul —, dar ruta de scriere se bifurca + dupa camp, nu dupa buton. *Vezi G-bis.* + **Consecinta pe etape:** in **etapa I** regenerarea nu exista inca, deci acele campuri nu au unde + sa se salveze. **Planul le tine blocate in etapa I**, cu explicatie la hover, si le deschide odata + cu etapa II. Motivul alegerii, in lipsa unei instructiuni contrare: un camp care se deschide dar nu + se salveaza e o capcana — pierderea tacuta a unei modificari e mai rea decat un camp inca blocat. +26. **Analiticele raman read-only in #13; editarea lor e a lui #6.** Venit/cheltuiala, sectie, + responsabil si lucrare se **afiseaza** in sectiunea pliata, cu valoarea de antet, dar nu se + editeaza din formularul unificat — cine vrea sa le schimbe trece prin editarea notei contabile. + Motivul: sunt deja editabile acolo, **la nivel de linie de nota**, iar #13 le-ar scrie uniform pe + tot documentul — doua ferestre care scriu acelasi camp cu semantici diferite, cu risc ca #13 sa + suprascrie tacit o diferentiere facuta din #6. **Zero suprapunere intre fire.** *Vezi G-bis.* + Asta **nuanteaza decizia 8**: analiticele intra in sectiunea pliata ca **afisare**, nu ca editare. + + **RASTURNATA DE DECIZIA 72 (22.08.2026): analiticele raman EDITABILE**, si la introducere, si la + reemitere. Motivul semantic de mai sus ramane consemnat ca **consecinta acceptata**, nu ca + restrictie: la reemitere nota se reconstruieste uniform pe document, deci o diferentiere facuta + din #6 la nivel de linie de nota se pierde — efect al regenerarii, nu al editabilitatii. + +### Deciziile lui Marius, runda 8 (09.08.2026) + +27. **Contul de venit vine din corespondente si din nomenclator, nu dintr-o politica de pret + implicita. Decizia 24 se retrage.** Regula, pe doua ramuri: + - **articol gestionabil** (cont de gestiune de clasa 3xx): contul de venit se ia din tabelul de + corespondente `CORESP_CONT_VENCHELT` — `select id_ccv, cont, cont_chelt, cont_venit, sters, + dataoras, cont_aprovizionare, cont_diferente from coresp_cont_venchelt`, cautand pe `CONT` + (contul de gestiune al liniei) si luand `CONT_VENIT`; + - **articol negestionabil**: se foloseste `NOM_ARTICOLE.CONT` **daca e deja un cont de clasa 7xx + sau 6xx**; altfel, implicit **704**. + + Motivul respingerii politicii implicite: ea muta problema in configurare (cine creeaza politica, + cu ce `SCC`, una sau mai multe) si obliga la o intrebare cu contabilul inainte de orice livrare. + Corespondentele exista deja ca date de productie si leaga contul de gestiune de cel de venit — + exact relatia cautata, doar ca sub alt nume decat s-a cautat in runda 7. +27-bis. ~~**Regula din 27 se implementeaza FARA cod nou in `pack_facturare`.**~~ **RELAXATA de decizia + 34 si inchisa de decizia 35** — pachetul se modifica punctual, si e singurul cod de contare, si la + emitere si la editare. Textul de mai jos ramane doar ca istoric al rationamentului. + (Marius, runda 8, dupa + prima formulare a lui 27). Motivul: pachetul e mare si e folosit de toate produsele suitei — o + ramura noua acolo e un risc peste tot, nu doar in ROAFACTURARE. + Deci **regula de derivare a contului de venit se aplica pe partea VFP**, inainte ca articolul sa + plece spre Oracle, iar `contabilizeaza_articol` ruleaza **neschimbata**. Calea de verificat: + VFP calculeaza contul de venit dupa regula de mai sus si alege un `id_pol` a carui nota are deja + acel `SCC`, astfel incat `FACT-024` sa nu se mai poata declansa. + **Modificarea pachetului ramane planul B**, folosit doar daca se dovedeste ca nu exista nicio cale + dinspre VFP. *Vezi J-quater;* fezabilitatea: `docs\cercetare\coresp_cont_venchelt.md`, punctul 8. + + **PREMISA A FOST VERIFICATA (Marius, runda 8):** „in ROAACNPRO, dar si la factura din comanda / + contract, se adauga articole fara politica de preturi — de ce nu se poate si aici?" **Raspuns: + observatia e reala, dar niciunul din cele doua exemple nu e un articol fara politica.** ROAACNPRO + nu cheama deloc `contabilizeaza_articol` (are propria contabilizare, `pack_acn.salveaza_regdoc`); + pe contract, articolele vin din `cursor_contract` / `cursor_preturi`, care le livreaza **cu `id_pol` + atasat**. `FACT-024` ramane blocantul. *Detalii, cu dovezi: J-quater, punctul 1.* + **REZULTAT: calea VFP exista si e mai buna decat modificarea pachetului** — se sprijina pe un RPC + existent (`pack_preturi.adauga_politica_pret_art`) si aduce, pe langa `SCC`, si `CU_TVA` / + `IN_VALUTA`, pe care un fallback in pachet ar fi trebuit sa le hardcodeze. *Vezi J-quater, punctul 3.* + **Planul B (modificarea `pack_facturare`) se abandoneaza.** +28. **Pe factura ROAAUTO pot exista si alte articole decat cele de pe deviz.** Nepotrivirea de afisare + dintre ecranul de deviz si factura retiparita (O, intrebarea 1) **e acceptata**: cerinta e ca + **MANOPERA si MATERIALE sa fie conform devizului**, plus orice alte articole adaugate. Nu se cere + cod nou in ROAAUTO ca sa aduca ecranul de deviz la zi. *Vezi O, intrebarea 1.* +29. **Stergerea unei linii venite din comanda ramane fara protectie.** Linia se sterge ca oricare alta; + comanda va aparea, corect, ca **facturata partial**. Nu se adauga confirmare, nu se marcheaza + „refuzat", nu se ajusteaza numararea acoperirii. *Vezi S4e.* +30. **Nu se coordoneaza cu firul #6.** #13 **incepe dupa ce #6 se termina**, deci suprapunerea pe + fisiere nu se poate produce si nu e nevoie de impartire de perimetru. Consecinta pe interdictii: + cele doua fisiere ale lui #6 raman intangibile **cat timp #6 e in lucru**, dar restrictia expira + odata cu el, nu cere negociere. Decizia 26 (analiticele read-only in #13) era pastrata aici pentru + ca tinea de semantica, nu de coliziunea de lucru — dar a fost **rasturnata de decizia 72**: + analiticele sunt editabile, iar suprapunerea de semantica ramane consemnata acolo ca efect asumat + al regenerarii. *Vezi „Relatia cu #6".* + +### Deciziile lui Marius, runda 9 (10.08.2026) + +**31. Datele din Dev nu sunt baza de proiectare.** Fiecare client isi defineste propriile politici de +pret, fiecare cu nota ei contabila de vanzare salvata pe un `ID_SET`. Orice masurare pe `MARIUSM_AUTO` +vale ca **dovada ca un mecanism exista** si ca sursa de concluzii **structurale**, niciodata ca harta a +ce e configurat la client. Nicio decizie de proiectare nu se sprijina pe numarul de politici sau de note +gasite pe Dev. *(Generalizeaza regula „zero cazuri in date nu e dovada" in ambele sensuri: nici prezenta +nu e dovada.)* + +**32. Intrebarea de raspuns inainte de orice implementare a contului de venit:** cum alege programul nota +contabila de vanzare la **factura pe baza de comanda**, care **nu are politica de pret**? Daca acel +mecanism se poate refolosi pentru articolul adaugat ad-hoc, **reteta in 4 pasi din J-quater se +abandoneaza** in favoarea lui. Vezi J-quater, „Intrebarea deschisa care poate anula toata reteta". + +**34. `contabilizeaza_articol` primeste un parametru de cont contabil. Decizia 27-bis e RELAXATA.** +Marius, 10.08.2026, textual: *„poți să adaugi parametrul contul contabil la `contabilizeaza_articol`."* +Consecinta: **reteta in 4 pasi din J-quater se abandoneaza.** Nu mai e nevoie de politica tehnica, nici de +interogarea inversa pe `SCC`, nici de inserarea articolului in politica. VFP calculeaza contul dupa regula +deciziei 27 si **il trimite direct**. +**Ce rămâne de proiectat, si nu e „doar un parametru":** contul nu e singurul lucru care venea de pe nota. +`cursor_articol` aducea si `SCD`, `CU_TVA`, `IN_VALUTA`, `EXPLICATIE`, `ID_VENCHELT`, `ID_SECTIE`, iar bucla +care il consuma apeleaza **si** `scrie_nota` **si** `descarca_gestiune`. Pe un articol fara politica, +`FACT-024` sare inainte. Deci e nevoie de **parametru + ramura fara politica**, care sa furnizeze ce +furniza nota si sa ruleze `descarca_gestiune` **exact o data**. Obiectia rundei 8 („`CU_TVA` si `IN_VALUTA` +n-au sursa in afara lui `NOTE_CONTABILE`") **nu mai e blocanta, e proiectare** — de reevaluat daca se pot +deriva din document. +**Constrangeri de forma, ca sa nu se strice suita:** parametru nou **cu `DEFAULT NULL`**, la finalul listei, +si ramura inerta cand lipseste — apelanții existenți nu se schimba. `pack_facturare` e comun **intregii +suite**, deci cere regresie pe ROACONT / ROAGEST / ROACONTRACTE / ROAAUTO / ROAACNPRO, nu doar ROAFACTURARE. +**Ramura noua nu moșteneste bug-ul de set multi-rand** din L.0-ter. +**PROIECTATA** — `docs\cercetare\canal_cont_venit_fara_politica.md`, sectiunea „Proiectarea parametrului +de cont contabil", liniile 13-227. Verdictul, in trei randuri, pentru ca schimba forma modificarii: +`contabilizeaza_articol` primeste azi **un singur parametru**, `detalii_articol +VANZARI_DETALII_TEMP%ROWTYPE` — deci contul **nu intra literal pe ea**, ar fi cosmetic. Intra ca +**coloana noua `VANZARI_DETALII_TEMP.CONT_VENIT VARCHAR2(4) NULL`**, populata printr-un parametru nou +`V_CONT_VENIT IN VARCHAR2 DEFAULT NULL` la **coada lui `adauga_articol_factura`** (procedura chemata +direct din VFP), pe care `contabilizeaza_articol` o primeste automat: cei trei apelanti interni fac +`SELECT * BULK COLLECT` intr-un `TABLE OF ...%ROWTYPE`, deci `scrie_factura2`, +`scrie_factura_avize_retur` si `scrie_aviz_retur` **nu se ating deloc**. +> **Corectie de nume, runda 11 — mecanismul insa rezista.** Al doilea apelant e **`scrie_factura_avize`** +> (`:6708-6749`), nu `scrie_factura_avize_retur`, care nici nu cheama `contabilizeaza_articol`. +> **Verificat pe cod ca toti trei apelantii reali** — `scrie_factura2` (`:6039-6063`), +> `scrie_factura_avize` (`:6708-6749`), `scrie_aviz_retur` (`:7097-7106`) — fac intr-adevar +> `SELECT * BULK COLLECT INTO` un `TABLE OF VANZARI_DETALII_TEMP%ROWTYPE`, **fara lista explicita de +> coloane**. Deci coloana noua curge automat prin toti trei si **niciunul nu se atinge**; concluzia +> proiectarii sta, doar doua din cele trei nume erau gresite. In `scrie_factura_avize`, `tab_detalii(i)` +> e modificat inainte de apel doar pe `.cantitate` / `.id_rata` — `CONT_VENIT` ramane neatins. Efectul cerut de Marius e exact +acelasi; doar mecanica de livrare difera de formularea literala. Precedentul e in aceeasi semnatura: +`V_TAXCODE` si `V_LOT` sunt deja doi parametri adaugati ulterior, la coada, cu `DEFAULT NULL`, iar apelul +VFP e pozitional si se opreste la `V_LOT`. +Ramura noua se activeaza pe `detalii_articol.cont_venit IS NOT NULL`, **infasoara si blocul `FACT-024`** +(garda ramane litera cu litera pe ramura veche: o linie fara politica **si** fara cont trimis cade in +continuare cu `FACT-024`), n-are cursor deloc — deci `descarca_gestiune` ruleaza **exact o data prin +constructie** si bug-ul de set multi-rand nu se mosteneste, fara sa se repare ramura veche. +**Doua hardcodari raman decizii deschise pentru Marius**, nu descoperiri: `SCD = '4111'` si `CU_TVA = 1` +— vezi „Ce ramane de decis" mai jos. + +**VERIFICATA ADVERSARIAL** — `docs\cercetare\parametru_cont_contabilizeaza_articol.md`. Proiectarea +rezista; patru rezultate schimba insa detalii de executie: + +- **Parametrul se cableaza in DOUA locuri VFP, nu unul.** `ofacturare.vc2:14069` si `:18089` **nu** sunt + o duplicare a aceleiasi metode, cum se presupusese: sunt **doua clase distincte**, + `frm_facturare_articole.do_scrie_articole` (`:13967-14195`) si + `frm_facturare_articole2.do_scrie_articole` (`:18003-18221`), fiecare construindu-si separat apelul RPC. +- **`CU_TVA = 1` nu e inofensiv, cum spunea proiectarea.** Actualizarea lui `nproc_tva_max` / + `nid_jtva_coloana` / `nTaxCode` e chiar in interiorul lui `IF V_CU_TVA = 1` (`:12537-12558`), iar + comparatia `nproc_tva_max < V_PTVA` **se uita doar la rata, nu la suma**. O linie fallback cu TVA real + 0% ar intra deci in comparatia de maxim si, daca e prima linie a documentului (`nproc_tva_max` porneste + `-1`, `:1885`), **castiga** — iar coloana si taxcode-ul ei ajung sa descrie **linia de discount a + intregii facturi** (`:6164-6184`). Combinatia e ingusta (linie scutita + discount global + acea linie e + „maximul" de pana atunci), dar efectul e real. Motivul pentru care decizia ii apartine lui Marius e + acum concret, nu formal. +- **`INSERT INTO VANZARI_DETALII ... FROM VANZARI_DETALII_TEMP` are lista de coloane explicita** — 24 de + coloane, `PACK_FACTURARE:13705-13757`, in `scrie_in_vanzari`. Deci daca se vrea `CONT_VENIT` pastrat si + dupa fapt in `VANZARI_DETALII`, **lista trebuie extinsa explicit**; altfel coloana traieste doar in + `VANZARI_DETALII_TEMP` si `ACT_TEMP`. +- **Articolul compus nu e o gaura in proiectare** — e exclus structural, nu prin presupunere. `COMPUS`, + asa cum il citeste `contabilizeaza_articol` (`:7279-7283`), e definit in `VCRM_POLITICI_PRET_ART` ca + proprietate a **perechii (articol, politica)**, identificata prin `ID_POL_ART`. O linie fara politica + n-are `ID_POL_ART`, deci intrebarea nu se poate pune. + +**Golul `ntip = 4` — INCHIS in runda 11.** Cercetat (`docs\cercetare\gol_ntip4_factura_din_avize.md`) si +**verificat adversarial** (`docs\cercetare\verif_goluri_ntip_aviz.md`). Trei rezultate, dintre care doua +schimba ce trebuie scris in pachet: + +- **`ntip = 4` e exclus structural — dar nu prin garda pe care o presupunea proiectarea.** Blocajul nu e + `FACT-024` din `contabilizeaza_articol`, ci **o functie mai devreme**: `adauga_articol_factura`, ramura + `WHEN ntip = 4` (`:5080-5103`), cauta randul sursa din `VANZARI_DETALII` prin `A.ID_POL = V_ID_POL` + **fara handler de exceptie**. Cu `V_ID_POL = NULL` — exact cazul liniei fara politica — comparatia nu + se poate potrivi niciodata, deci apelul cade cu `ORA-01403` **la adaugarea articolului**, in bucla + `do_scrie_articole` (`ofacturare.vc2:13967-14106`), care ruleaza intotdeauna inaintea `Do Case`-ului + (`:14282`). Linia nu ajunge niciodata la `contabilizeaza_articol`; ramura noua n-are nimic de tratat + acolo, si **nu are nevoie de garda defensiva** (ar fi redundanta peste doua straturi independente). +- **GOL REAL, mai ingust decat parea, dar efectiv atins: `SCD` pe avize.** Ramurile de aviz care chiar + ajung la `contabilizeaza_articol` — `ntip IN (28,29)` → `SCD = '461'`, restul avizelor „simple" + (`21, 22, 24, 27`) → `SCD = '418'` (`:7413-7422`) — **sunt accesibile cu `cont_venit` populat**, pentru + ca `adauga_articol_factura` nu cere `id_pol` pe niciunul din ele. Ramura noua, care ia `SCD` din + optiunea deciziei 36 (`4111`), l-ar scrie **necontitionat de `ntip`** → **cont contabil gresit pe + aviz**. Deci ramura noua **alege `SCD` dupa `ntip`**: `'461'` pentru `IN (28,29)`, `'418'` pentru + celelalte avize atinse, si abia pe restul optiunea `RF_CONT_ART_FARA_POL`. + **Verificarea a restrans lista:** `ntip IN (23,25,30,41,42,47)` **nu ajung deloc** la + `contabilizeaza_articol` — sunt deviate mai devreme, in `scrie_factura2`, spre `transfera_articol` + (`23,25,30,41`) sau direct spre `descarca_gestiune` (`42,47`). Golul nu li se aplica. +- **Al doilea gol confirmat: garda `ntip = 46`.** `nTipNotaPlata` **este** `46`, iar `scrie_nota` e + sarita azi intentionat pentru el (`IF ntip <> nTipNotaPlata`, `:7441`). Un articol fara politica poate + ajunge si pe acest tip. **Ramura noua trebuie sa reproduca garda** — altfel scrie o nota care azi e + sarita deliberat. + +**CORECTIE la proiectare — al doilea apelant intern e numit gresit peste tot.** Nu +`scrie_factura_avize_retur`, ci **`scrie_factura_avize`** (`:6692-7058`) — care e chiar handler-ul Oracle +al lui `ntip = 4`, apelat din `ofacturare.vc2:14318`; apelul catre `contabilizeaza_articol` e la `:6858`, +in interiorul lui `IF articole_aviz(j).custodie = 1`. `scrie_factura_avize_retur` (`:6264-6657`) **nu +cheama deloc** `contabilizeaza_articol` — insereaza direct in `VANZARI_DETALII_TEMP` din +`VANZARI_DETALII`. Iar `scrie_aviz_retur` (`:7085-7171`) **n-are niciun apelant VFP gasit** in tot +`COMUN\` si in tot proiectul — posibil cod mort sau apelata din alt produs. Greseala e propagata in +`canal_cont_venit_fara_politica.md:67` si `nota_contabila_fara_politica.md:103`; se citeaza de acum +lista corectata de mai sus. + +**Nota de metoda, valabila pentru toate rapoartele:** presupunerea ca liniile din exportul +`PACK_FACTURARE` au un **offset constant `+17`** fata de rapoartele vechi e **falsa**. Nu exista offset +universal (masurat: `+9` fata de `nota_contabila_fara_politica.md`, `0` fata de alte rapoarte). Numerele +de linie se **re-verifica direct pe fisier**, niciodata prin corectie presupusa. + +**Nu se implementeaza** — decizia 30 sta, #13 incepe dupa #6. + +**33. Mockup-ul nu se republica la v7.** Se duce la **v8** cu rezultatele verificarilor rundei 9 si abia +atunci se republica — o singura citire a HTML-ului, o singura republicare. Url-ul artifact rămâne la v6 +pana atunci. + +### Deciziile lui Marius, runda 10 (10.08.2026) + +**35. Un singur cod de scriere contabila: `pack_facturare`, si la emitere si la editare. +`oscrie_in_fisiere` NU se foloseste in #13.** Marius, 10.08.2026, textual: *„nu doresc folosirea scrie in +fisiere ci doar `pack_facturare` la emitere si la editare pentru ca nu vreau sa intretin doua coduri."* + +**Ce anuleaza:** impartirea propusa la finalul rundei 9 — „emiterea prin parametrul deciziei 34, editarea +prin canalul generic catre `ACT_TEMP`, care nu cere nimic in pachet" — e **respinsa**. Canalul +`actactan` / `tact` → `COMUN\programe\oscrie_in_fisiere.prg` → `pack_contafin.SCRIE_IN_ACT` exista si e +folosit azi in productie de fluxul de editare al lui #6, dar folosirea lui in #13 ar insemna **doua +implementari ale aceleiasi reguli de contare** — una in pachet, pe emitere, alta in VFP, pe editare — +tinute in pas manual la fiecare schimbare a regulii deciziei 27. Motivul e de **intretinere**, nu tehnic, +si nu se reargumenteaza cu „canalul exista deja". + +**Ce impune:** editarea prin regenerare (etapa II) scrie pe **exact acelasi drum** ca emiterea — +`scrie_factura2` → `contabilizeaza_articol`, cu parametrul de cont de la decizia 34. Nu exista ramura de +scriere separata pentru documentul reemis si nu exista editare directa a randului din `ACT_TEMP` din #13. + +**Ce nu atinge decizia 35:** +- **fluxul lui #6** ramane cum e (`ofacturare_comun.vc2:3796-3821` → `frm_modific2024`) — decizia priveste + #13; retragerea lui e alt subiect si alt perimetru — **inchis de decizia 38: coexista, se decide dupa + ce #13 livreaza**; +- **stergerea** documentului vechi din S9 ramane pe drumul existent (`do_sterge` → `oscrie_in_fisiere` + + `pack_contafin.finalizeaza_stergere_nota`), pentru ca acolo **nu exista al doilea cod de intretinut**: + `pack_facturare` nu are echivalent de stergere a notei, iar drumul de stergere e comun intregii suite. + *Presupunere declarata, de infirmat daca Marius vrea si stergerea mutata in pachet — ar fi alt mandat si + alta suprafata de risc.* + +**Ce castiga:** regula deciziei 27 traieste intr-un singur loc, si o corectie pe ea nu trebuie facuta de +doua ori. **Ce costa:** etapa II nu mai are cale ieftina — depinde de parametrul deciziei 34 exact cat +depinde etapa I, deci **S9 nu poate porni inaintea parametrului**, iar regresia pe suita se plateste o +singura data, dar obligatoriu. + +**36. Cele doua campuri ramase fara sursa pe ramura fara politica — decis.** Verificarea a stabilit intai +ca **nu exista in cod nicio sursa alternativa** pentru ele: nici optiune de firma existenta, nici cont pe +partener, nici flag de scutire pe articol sau client (`parametru_cont_contabilizeaza_articol.md`, +sectiunea 4). Deci sunt decizii de produs, si Marius le-a luat asa: + +- **`SCD` — fix, dar citit din configurare, nu hardcodat.** O optiune de firma, cu **`4111` ca implicit**. + **PROIECTATA** — `docs\cercetare\optiune_firma_cont_debit.md`. **Costul e mult mai mic decat parea: + tiparul exact exista deja in productie, in acelasi pachet, pe acelasi camp.** + `pack_facturare.scrie_incasare2` (`PACK_FACTURARE:13161-13234`) isi ia `V_SCD` in **trei niveluri**: + parametru explicit daca a fost dat → **optiunea de firma** (`RF_CONT_INCASARE_BONFISCAL` s.a.m.d., cate + una per tip de incasare) → **constanta hardcodata** daca optiunea lipseste. Se copiaza identic, si se + inlocuieste punctual linia `SCD := '4111'` din proiectarea ramurii noi. + Mecanismul are 15+ ani: tabelul `OPTIUNI` (458 randuri azi), `PACK_SESIUNE.getoptiunefirma`, si un + **ecran de editare complet generic** peste tabel (`frm_optiuni` / `frm_optiuni_nou`, + `COMUN\clase\oOptiuni.vc2`) — deci **cheia noua nu cere niciun cod VFP nou**, doar randul in tabel. + Nu exista `ID_FIRMA`: fiecare firma are schema Oracle proprie, deci `OPTIUNI` din schema de conexiune + **este** deja „optiunile firmei curente". + **Dubla plasa de siguranta, si asta acopera integral cerinta lui Marius:** implicitul `4111` traieste si + in randul din `OPTIUNI` (scris de migrare, editabil fara recompilare), si hardcodat langa + `getoptiunefirma` — deci si o instalare veche fara migrare, si un rand golit din ecran cad tot pe `4111`. + `getoptiunefirma` intoarce `''` cand nu gaseste, ceea ce in Oracle e `IS NULL`, deci ambele cazuri se + trateaza cu aceeasi conditie. + **`ASCD` nu trebuie atins** — se calculeaza deja din `V_SCD`, deci primeste automat valoarea corecta + indiferent de sursa. + **`PROGRAME` se pune larg, nu doar `ROAFACTURARE`** — intrebarea a ramas deschisa in raport, dar se + inchide combinand-o cu masuratoarea de regresie: apelul catre `adauga_articol_factura` traieste in + `COMUN\clase\ofacturare.vc2`, fisier prezent si folosit in **toate cele sapte produse**, deci ramura noua + e atinsa de toata suita, exact ca `RF_CONT_INCASARE_*` (care listeaza sase produse). +- **`CU_TVA` — derivat din cota liniei**, nu fixat: `1` daca `proc_tvav > 0`, altfel `0`. Asta **elimina + prin constructie** efectul gasit la verificare: o linie scutita nu mai intra in comparatia de maxim din + `nproc_tva_max`, deci nu mai poate imprumuta coloana si taxcode-ul ei liniei de discount a intregii + facturi. Pretul e o regula in plus in pachet si un comportament **diferit de ce face azi nota** pentru o + linie scutita — diferenta e intentionata, nu accidentala, si se noteaza ca atare la testare. + +Deci lista parametrilor noi ramane la **unul singur** (`V_CONT_VENIT`): `SCD` vine din configurare, iar +`CU_TVA` se deriva in pachet din date deja prezente pe rand. + +**37. Cheia optiunii si validarea — decise (10.08.2026).** +- **Cheia: `RF_CONT_ART_FARA_POL`**, aliniata la familia `RF_CONT_INCASARE_*` — adica exact optiunile care + alimenteaza azi `SCD` in acelasi pachet. Cele cinci apar astfel **grupate alaturi in ecranul de + optiuni**, ceea ce le face inteligibile impreuna. Incape in `OPTIUNI.VARNAME` (`VARCHAR2(30)`). + `VARTYPE = 'CHARACTER'`, `VARVALUE = '4111'`, `PROGRAM = 'ROAFACTURARE'`, `PROGRAME` larg (vezi 36). +- **Fara validare de cont**, nici la salvare, nici la citire — se copiaza tiparul existent. Motivul: + niciuna dintre optiunile de tip cont nu e validata azi nicaieri, iar `RF_CONT_INCASARE_*` traieste asa + de 15 ani; a introduce validare aici ar fi o imbunatatire noua, nu continuarea unui tipar, si ar cere + fie cod nou in pachet, fie prima logica per-cheie din ecranul generic `COMUN\clase\oOptiuni.vc2` — + fisier al intregii suite. **Garda gratuita ramane lungimea:** un `VARVALUE` peste 4 caractere da + `ORA-12899` la `INSERT INTO ACT_TEMP`, deci esec zgomotos, nu cont tacut gresit. + *De consemnat la testare ca risc acceptat constient:* un cont inexistent in planul de conturi, scris din + greseala in ecran, ajunge pe nota neschimbat — acelasi risc pe care produsul il are deja, nu unul nou. + +### Deciziile lui Marius, runda 11 (10.08.2026) + +**38. Fluxul de editare al lui #6 nu se retrage odata cu #13. Coexista, si se decide mai tarziu.** +Intrebarea pusa la finalul rundei 10 — daca decizia 35 („un singur cod de scriere contabila") obliga la +rerutarea editarii lui #6 pe acelasi drum — primeste raspuns: **nu acum**. #6 ramane pe editarea directa +a randului din `ACT_TEMP` (`ofacturare_comun.vc2:3796-3821` → `frm_modific2024` → `oscrie_in_fisiere`), +#13 merge pe regenerare prin `pack_facturare`. Cele doua traiesc separat, pe povesti separate. +**Ce inseamna concret:** decizia 35 se citeste strict ca perimetru al lui #13 — nu se proiecteaza si nu +se planifica nicio retragere a fluxului lui #6 in aceasta poveste, si nu se adauga in #13 nicio piesa +care sa pregateasca acea retragere. Subiectul **nu e inchis definitiv**: se redeschide dupa ce #13 +livreaza regenerarea si se vede in practica daca intretinerea celor doua drumuri doare cu adevarat. +Pana atunci nu se reargumenteaza in niciun sens. + +**39. S4 ramane integrala: se desface si registrul de cantitate ramasa.** Proiectarea rundei 11 a aratat +ca `crsarticole` nu e doar sursa gridului, ci **registrul cantitatii ramase de facturat** — scris de +`do_sterge` la stergerea unei linii (`ofacturare.vc2:14640-14669`) si citit prin +`Calculate Sum(cantitate) To lnCantitateRamasa` ca sa se decida **inchiderea automata** a comenzii / +avizului (`:14303-14311`, `:14334-14338`). Varianta ieftina ar fi fost sa se restranga S4 la lista de +preturi; Marius a ales varianta intreaga: **bookkeeping-ul se decupleaza de cursorul incarcat in masa**, +ca sa poata disparea incarcarea si pe comanda si pe aviz. +**Consecinta, declarata explicit:** S4 nu mai e o poveste de performanta pe formular, ci atinge +**inchiderea automata a documentelor sursa** — cea mai mare suprafata de regresie din etapa I, si +singura care poate lasa o comanda deschisa (sau o poate inchide prematur) fara ca operatorul sa vada +ceva. Criteriul de „gata" al lui S4 include de acum, obligatoriu, **paritate pe inchiderea automata**: +acelasi document sursa, aceleasi linii facturate partial, acelasi `INCHISA` la final, pe fiecare tip cu +document sursa. Registrul nou trebuie sa fie sursa unica — nu o a doua copie tinuta in pas cu prima, +altfel povestea introduce exact tipul de dublura pe care decizia 35 il refuza in alta parte. + +**40. Bug-ul de dezalocare POS se repara in S3b, in trecere.** Vezi `L.4`. Consecinta acceptata: diff-ul +lui S3b **nu mai e o mutare pur mecanica**, deci testarea lui trebuie sa acopere si dezalocarea POS pe +calea veche, nu doar paritatea de emitere. + +**41. Sectiunea pliata are DOUA comutatoare, nu unul si nu cinci.** Grupul de **incasare** — singurul cu +efecte laterale reale (alocare / dezalocare de numere) si singurul blocat pe document emis prin decizia +25 — primeste comutator propriu. Delegat/transport, adresa, text aditional si analiticele stau impreuna +sub al doilea. Motivul e izolarea riscului: garda de non-alocare la toggle +(`opt_incasat.ProgrammaticChange`) se scrie si se testeaza **intr-un singur loc**, nu pe cinci stari. + +**42. Validarea de curs valutar se restrange la valuta articolului cautat.** Pe varianta filtrata a +cursoarelor, `verifica_cursuri_valute` nu mai ruleaza global, ci doar pe valuta randului adus. +**Schimbare de comportament asumata:** un curs lipsa pe **alta** valuta nu mai e semnalat la deschiderea +formularului, ci abia cand se ajunge la un articol pe acea valuta. Se consemneaza la testare ca +diferenta intentionata fata de azi, nu ca regresie. + +#### Puncte marunte ramase din proiectarile rundei 11 — se merge pe recomandare daca Marius nu spune altfel + +Nu blocheaza nimic; se inchid la implementarea povestii respective. Enumerate ca sa nu se piarda intre runde. + +| # | Punct | Poveste | Se merge pe | +|---|---|---|---| +| a | Textul exact al tooltip-ului pentru grupul de incasare blocat (cel pentru analitice cade odata cu decizia 72) | S3b | formularea propusa in raport, sectiunea 5.3 | +| b | Eager vs. lazy pentru lookup-urile Oracle din `Init` (delegat / masina ultimei facturi, casa) | S3b, S3 | **lazy**, ca sa nu se plateasca la fiecare depliere; deschis si in `s3_portare_antet.md` §5 | +| c | `_checkbox1` vs. `chkDetaliat` — care devine campul unic de „listare detaliata" | S3b, S1 | **INCHIS (runda 12)**, pe cod: sunt **doua controale distincte** in `frm_alte_date`, nu o duplicare — nu exista nimic de ales. `docs\cercetare\s4c_discount_in_grid.md`, sectiunea 11 | +| d | Forma lui `toSursa`: obiect scatter (duck-typing) sau clasa dedicata | S3c | **duck-typing**, ca azi — o clasa noua n-ar schimba nimic functional | +| e | Conversia comenzii si a contractului: un commit sau doua | S3c | **un singur commit** — ating aceleasi trei fisiere comune, separarea nu reduce regresia, doar amana testarea pe ROACONTRACTE | +| f | `goComanda = ''` redundant la `Cw3.do_actiune` dupa conversie | S3c | **se lasa**, marcat explicit „intentionat" in diff, ca sa nu para omisiune la review | +| g | Contractul are doua meniuri (articole vs. rate): eticheta generica sau diferentiata pe `OPT_FACTURARE` | S4b | **diferentiata** — „Alege ratele de facturat…" e gresita pe contractele cu articole | +| h | „Alege facturile de returnat…" ramane doar la antet sau capata incarcare aditiva din bara | S4b | **ramane la antet** in etapa I; mutarea e o poveste separata | +| i | `But_renunt1` / `But_reset1` primesc `Caption` pentru consistenta cu bara etichetata | S4b | **da** — decizia 13 cere etichete, nu iconite mute; exceptia ar fi inconsecventa | +| j | Ordinea si formularea optiunilor din `xmenu()` per sursa | S4b | tabelul din sectiunea 3 a raportului, ca propunere | +| k | UX-ul contractului cu doua surse pe acelasi grid conceptual (`crsarticole` filtrat + `crsarticole1`) | S4 | de confirmat vizual la mockup, nu pe hartie | + +### Ce s-a dovedit ca exista deja (deciziile 10, 11, 12, 14) + +Verificat pe cod la 09.08.2026; rapoartele complete sunt in `docs\cercetare\` +(`proforma_copiere_puncte_intrare.md`, `discount_pe_articol.md`, +`inventar_controale_formulare.md`, `import_roris_roaacnpro.md`). + +- **Proforma nu e tip de document si nu are formular propriu.** E valoarea `PROFORMA` din combo-ul + „Tip document” (`ct_clb_fdoc._combobox1`, `RowSource = "FACTURA,PROFORMA,BON FISCAL"`, + `ofacturare.vc2:8745-8754`), care seteaza `nIdTipDoc = 23` si, prin setter, + `eProforma = 1` (`ofacturare_comun.prg:593-599`, `nIdTipDocProforma = 23` la `:104`). Deci intra in + formularul unificat **fara nimic de portat**. Specific proformei si de pastrat: serie si numar + proprii, realocate la comutarea din combo (`ofacturare.vc2:9415,9427-9428`); **fara nota contabila + si fara atasamente**, prin garzile `poDate.eProforma = 0` + (`ofacturare.prg:1960,1996,2052,2063,2103`); raport propriu (`:1638-1643`, `COMUN\Rapoarte\proforma.fr2`); + relistarea pe cale separata (`ofacturare_comun.vc2:7230-7264`). +- **Copierea foloseste deja acelasi formular, cu antetul editabil.** `do_copiaza` -> `copiere_factura` + -> `factureaza(tip, toFactura)` -> `frm_date_factura` precompletat de + `completeaza_setari_document(toFactura, .T.)` (`ofacturare_comun.prg:362-412`). Numar nou se aloca + **intotdeauna**, neconditionat de copiere (`ofacturare.prg:208,211`). Nimic din traseu nu blocheaza + controale de antet. +- **Butonul de facturare din pagina de comenzi exista deja in ROAFACTURARE**: `ct_comenzi.do_factura` + (`ocomenzi.vc2:1580-1596`) face `SCATTER NAME goComanda MEMO` si cheama `facturare_comenzi` -> + `factureaza(3)`; butonul `But_factura1` (`ocomenzi.vc2:932-937`) e ascuns cand comanda e deja + facturata (`:2199-2203`), si e montat pe Page5 (`ofundal_facturare.vc2:604`, `Ferestre\fundal.sc2:206`). +- **Precompletarea contractului vine din programul de contracte** (`ROACONTRACTE`): + `ferestre_contracte.vc2:1538-1549` si `:1605` -> `facturare_contracte` -> `factureaza(2/6/52)`, + cu contractul deja completat — exact ce cere decizia 12, si **functioneaza azi**. In ROAFACTURARE + exista, in plus, calea din meniu **fara** precompletare (`ofundal_facturare.vc2:886-897`), unde + utilizatorul alege contractul in formular. Cele doua coexista; #13 nu adauga o lista de contracte + in ROAFACTURARE si **nu depinde de #10**. +- **Discountul pe articol are deja si procent, si valoare — dar intr-un dialog separat.** Vezi K. +- **Deblocarea antetului exista deja**, dar sub forma a patru bife individuale in + `frm_modifica_factura`; in formularul unificat ele sunt inlocuite de un singur buton comutator. + Vezi I. +- **Data cursului valutar nu e conditionata azi de valuta.** Vezi M. + +### Canalul de precompletare e un global, nu un parametru + +`factureaza(tnTip, toFactura)` (`ofacturare.prg:81-82`) nu are parametru pentru sursa: comanda si +contractul se transmit prin globalele `goComanda` / `goContract`, citite in `oDateFactura.Init` +(`ofacturare_comun.prg:301-328` comanda, `:261-297` contract). De aceea calea generica de comanda e +obligata sa scrie explicit `goComanda = ''` inainte de apel (`ofundal_facturare.vc2:899-902`), altfel +ar ramane precompletata cu ce era in sesiune. **Calea generica de contract nu face resetarea +simetrica** (`:886-897`) — de verificat daca `goContract` poate ramane populat intr-o sesiune si +precompleta tacit un document nou. Decizia 12 se implementeaza corect prin **parametru explicit**, nu +prin inca un global. + +## Relatia cu #6 — transata: #13 incepe dupa #6 + +`plan_06_editare_factura.md` rezolva **aceeasi problema de fond** (o factura emisa nu se poate +corecta fara ca notele si rulajele sa ramana in urma) pe calea opusa: editare directa in +`frm_modific2024`, cu `id_fact` pastrat. Planul #6 a respins explicit regenerarea, cu motivul ca +*"emiterea face verificari de stoc si alte protectii dependente de tipul documentului; la +regenerare acestea ar esua pe date care intre timp s-au schimbat"*. + +Argumentul acela **cade partial** daca stergerea si reemiterea se fac in aceeasi tranzactie, in +ordinea corecta — vezi sectiunea "Stocul" mai jos. Dar nu cade complet: raman verificarile care nu +tin de stoc (configurare politica / contract / cota TVA, vezi `adauga_articol_factura`). + +Cele doua nu se exclud tehnic, dar **se suprapun pe aceleasi fisiere** si, mai important, produc +doua semantici diferite pentru "am corectat factura": + +| | #6 — editare directa | #13 — regenerare | +|---|---|---| +| Punct de intrare | registru jurnal + formular facturi | formularul de facturi | +| Utilizator vizat | contabil | operatorul de facturare | +| `ID_FACT` | pastrat prin constructie | **pastrat, prin cerinta** (vezi E) | +| Serie, numar, data | neschimbate | neschimbate (cale proprie, vezi F) | +| `COD`, `ID_VANZARE` | `COD` nou, `ID_VANZARE` pastrat | ambele noi | +| Notele si rulajele | rescrise din formularul de note | **regenerate din articole**, ca la emitere | +| Documentul sursa (comanda/aviz/contract) | neatins | **eliberat si reconsumat** | +| Efort | mediu (in curs, S1–S3 livrate) | mare | + +**Decizia lui Marius (09.08.2026): coexista, cu roluri separate.** #6 ramane calea contabilului +pentru corectii pe nota — si e singura cale posibila din registrul jurnal din ROACONT/ROAGEST, unde +nu exista contextul de facturare (`poDate`, `crsfactura`, politici, serii). #13 devine calea +operatorului de facturare: modific documentul, notele si rulajele se refac singure. + +**Coliziune de lucru: nu exista. DECIS (decizia 30): #13 incepe dupa ce #6 se termina.** Sesiunea de +la #6 a atins `COMUN\clase\ofacturare_comun.vc2` (`do_editare_factura`, `:3715-3869`), a creat +`COMUN\programe\ofacturare_editare.prg` si a adus `omodificari.vcx` in proiect (`97d1613`) — dar +implementarea lui #13 nu porneste cat timp acestea sunt in lucru, deci **nu e nevoie nici de +impartire de perimetru, nici de coordonare intre fire.** Cele doua buguri semnalate (L.0 si L.0-bis) +raman semnalate catre #6, nu reparate din #13. +Decizia 26 — analiticele read-only in #13 — era tinuta aici pe motiv semantic (#6 le editeaza la nivel +de linie de nota, #13 le-ar scrie uniform pe tot documentul), nu de calendar. **Decizia 72 o rastoarna**: +analiticele sunt editabile, iar scrierea uniforma pe document e acceptata ca efect al regenerarii. + +--- + +## Ce s-a stabilit din cod + +### A. Formularul unificat exista deja ca prototip, oprit din 2017 + +`frm_facturare_articole2` (`COMUN\clase\ofacturare.vc2:15741-19355`) **este** formularul cerut la +punctul 13, la nivel de controale: + +- are deja campurile de antet mutate din `frm_date_factura`: `Clb_serie_act1`, `Clb_nract`, + `Clb_dataact`, `Clb_zi_curs`, `Clb_data_scadenta`, `Ct_clb_nume_client`, `Ct_clb_responsabil`, + `Ct_clb_sectie`, `Ct_clb_lucrare`, `Ct_clb_venchelt`, `Ct_clb_altele`, `Ct_clb_valuta`, + `clb_fdoc`, `Ed_tx_simplu1` (`:15744-15812`); +- **nu** are `grd_articole`, `grd_contracte`, `cb_politici_preturi`, `cb_contracte`, casetele de + cautare `txtCodmat` / `txtArticole` — adica exact partea care azi consuma incarcarea in masa; +- are grid editabil in linie, cu combouri `combosql` din `cautare.vcx` pe `cCodMat.cboCodmat`, + `cDenumire.cCboDenumire`, `cGestiune.cCboGestiune` (`:16751-16881`), plus `But_nou1`; +- e lansat de `factureaza2` (`COMUN\programe\ofacturare.prg:590-1080`), care se cheama numai daca + `gnFacturareNou = 1` **si** utilizatorul raspunde DA la un `AMESSAGEBOX` (`:87-92`). + +**Cat de mort e prototipul.** In `factureaza2`, deschiderea formularului de date si incarcarea +articolelor sunt inchise cu `If .F.`: `:707-711` (formularul de date), `:826-829` (executia +cursorului de articole), `:838` (verificarea de "nu exista articole"), `:985-1008` (`crspolitici` +si `crscontracte`). `Init`-ul lui `frm_facturare_articole2` are 92 de linii (`:18988-19080`) fata +de 368 la `frm_facturare_articole` (`:14976-15344`) si **nu completeaza niciun camp de antet** — +controalele sunt acolo, logica nu. + +**Drift-ul dintre `factureaza` si `factureaza2`** (fork din 08.06.2017, nesincronizat): lipsesc din +`factureaza2` tipurile 51 si 52, ramura `llCopiere` / `toFactura`, completarea `codmatc` / +`codnc8` / `codcpv`, `GetInstitutiePublica`, `GetSoldClient`, filtrul `RORTC` pe `jtva_coloane`, +tratarea `eProforma`, `verifica_numar(16, ...)` pentru chitanta, `cursor_retur_document`, +`cursor_avize` cu tip 23. **Concluzie: nu se continua cu doua proceduri.** Se merge pe una singura, +cu un parametru de mod, altfel divergenta se reia imediat (exact tiparul care a produs problema +reparata la #8). + +### B. Nu doua formulare, ci patru + +Fluxul de azi are **patru** ferestre. Runda 2 numarase trei; a patra, `frm_articol_factura` +(`ofacturare.vc2:1108-2659`), se deschide **pentru fiecare articol adaugat**, din +`do_adauga_articol` (`:12873`, `ofrmadarticol.Show(1)`, doar daca `!tlImplicit`) — si e locul in care +se introduce azi discountul pe linie (vezi K). Formularul unificat o desfiinteaza, deci campurile ei +trebuie sa aiba unde sa se mute. + +Celelalte trei: + +1. `frm_date_factura` (`ofacturare.vc2:8482-9869`) sau `frm_date_aviz` (`:6566-7618`) — + 17, respectiv 13 metode `do_cauta_*`, plus `do_schimba_tipdoc`, `inainte_de_do_termin` si un + `Init` de 234 / 247 de linii; +2. `frm_facturare_articole` (`:10968-15739`) — compunerea; +3. `frm_alte_date` (`COMUN\clase\ferestre_cere_date.vc2:2219-3353`) — **aratat dupa** confirmarea + articolelor, din `inainte_de_do_termin` (`ofacturare.vc2:14921-14941`): delegat, auto, agent, + adresa de facturare, incasare (numerar / chitanta / bon fiscal / POS), text aditional. Aloca si + numere proprii (bon fiscal, POS, chitanta). + +Punctul 13 vorbeste doar despre primele doua. **Decizia 7 le aduce pe toate trei**: `frm_alte_date` +intra in formular, ca zona pliata. Recomandarea din runda 2 („ramane dialog in prima etapa”) e +respinsa. + +Ce inseamna concret, din inventarul de controale: se muta patru grupuri — delegat si transport +(`Ct_clb_delegat`, `Ct_clb_masina`, `Ct_clb_agent`, `Clb_dataora_exp`), incasare (radiogrupul +`opt_incasat` cu patru optiuni, `Cb_casa`, `Clb_serie_chit`, `Clb_nrchit`, `Clb_incasat`, +`cmdModificaBon`, `chkPOS`, `chkDetaliat`, `cboTipFactura`), adresa de facturare +(`clb_adresa_facturare`) si textul aditional (`Ed_tx_simplu1`) — +`ferestre_cere_date.vc2:2343-2638`. **Masina de stari se muta ca atare, nu se rescrie**: +`actualizeaza_tipincasare` (`:2698-2856`) comuta vizibilitatea pe cele patru valori si aloca sau +dezaloca numarul de chitanta (`:2783`), de bon fiscal (`:2807`) sau de POS (`:2844`). Riscul semnalat +in runda 2 nu dispare, dar e localizat: **alocarea de numere trebuie sa ramana legata de aceleasi +evenimente**, nu de deschiderea formularului. + +Peste ele coboara analiticele din antet (decizia 8): `Ct_clb_venchelt`, `Ct_clb_sectie`, +`Ct_clb_responsabil`, `Ct_clb_lucrare`. + +### C. Incarcarea articolelor — de ce dureaza si ce inlocuieste + +`factureaza` executa, inainte de a deschide compunerea, **un singur apel** care aduce toate +articolele disponibile: `pack_facturare.cursor_preturi` (`ofacturare.prg:281-286`, corp la +`PACK_FACTURARE:2121+`), respectiv `cursor_contract` / `cursor_comanda` / `cursor_avize` / +`cursor_gestiune` / `cursor_retur` dupa tip (`ofacturare.prg:265-311`). Rezultatul intra in +`crsarticole` si tine gridul de sus. + +`cursor_preturi` **nu are filtru pe articol** — semnatura e +`(V_DATA_CURS, V_TIP, V_ID_VALUTA, V_ID_GESTIUNE_INIT, V_LUNA, V_AN, V_ID_UTIL, V_ID_SUCURSALA)` +(`PACK_FACTURARE:335-343`). De aici vin miile de randuri. + +Mecanismul de inlocuire **exista deja**, in prototip: `combosql` cauta pe server, incremental, cu +`csourcesql = select codmat, denumire, um, grupa, subgrupa, codbare, id_articol from vnom_articole` +si `csourcewhere = inactiv = 0` (`ofacturare.vc2:16751-16766`). + +**Dar `vnom_articole` e nomenclatorul, nu lista de preturi.** Nu are pret, nota contabila, cota TVA, +valuta, `id_pol`, `gestionabil`, `pret_cu_tva` — toate vin azi din `cursor_preturi`. Deci partea +Oracle a lucrarii e o **varianta filtrata pe articol** a cursoarelor de facturare, apelata la +alegerea liniei, nu la deschiderea formularului. Nu e o simplificare: ramurile pe tip din +`cursor_preturi` (restaurant, custodie, retur, contract, comanda) trebuie pastrate identic, altfel +pretul difera intre cele doua cai. + +**Legatura cu #12.** Daca articolul poate purta el insusi pretul si nota contabila (varianta C din +`plan_12`), cautarea in linie devine directa si nu mai are nevoie de o rezolvare separata a listei +de preturi. #13 nu depinde de #12, dar cine face #12 dupa #13 va rescrie exact bucata asta. + +### D. Regenerarea — piesele exista, montajul nu + +**1. Stergerea elibereaza deja documentul sursa.** `pack_facturare.sterge_factura` +(`PACK_FACTURARE:5415-5591`) face, pe langa `sters = 1` pe `VANZARI` si `VANZARI_DETALII`: + +- `FACTURAT = 0, ID_UTILFACT = NULL, DATA_FACTURAT = NULL` pe avizele facturate (tip 4 si 24); +- `STERS = 1` pe `VANZARI_CANTITATI` (cantitatile consumate de pe avize); +- `STERS = 1` pe `COMENZI_ELEMENTE` cu `CANTITATE < 0` (consumul din comanda); +- `STERS = 1` pe `VANZARI_CORESP` si pe `CTR_RATE_FACTURI` (contracte in rate, tip 2/6/52). + +Adica **exact reversul consumului sursei** — conditia ca reemiterea sa poata reconsuma aceeasi +comanda / acelasi aviz / aceeasi rata. Sunt si garzi: refuza stergerea daca s-au emis facturi sau +avize de retur peste document (`:5432-5477`). + +**2. Calea VFP de stergere completa exista.** `frm_facturi.do_sterge` +(`COMUN\clase\ofacturare_comun.vc2:4658-4874`): tranzactie manuala, `oscrie_in_fisiere`, apoi +`pack_contafin.finalizeaza_stergere_nota` (care cheama `sterge_din_vanzari`). Rulajele si notele +dispar pe aceasta cale, nu prin `sterge_factura`. + +**3. Reconstituirea liniilor exista.** `pack_facturare.cursor_retur_document(..., V_COPIERE = 1, ...)` +(`PACK_FACTURARE:3932-4045`) reface liniile din `VANZARI_DETALII`, cu `explicatie`, `id_gestiune`, +`pret_achizitie`, `pretd`, `id_jtva_coloana`, `id_jtva_coloana_ex`, `lot`, `serie`, `id_pol`, +`pret_cu_tva`, plus `curs` / `multiplicator` din `VANZARI_CURSURI`. E deja folosita, exact asa, la +copierea unei facturi. + +**4. "Redeschide formularul precompletat" exista.** `frm_facturi.do_copiaza` +(`ofacturare_comun.vc2:3640-3713`) -> `copiere_factura` (`oproceduri_facturare.prg:150-153`) -> +`factureaza(tip, toFactura)`, iar in `factureaza` ramura `llCopiere` (`ofacturare.prg:255-257`, +`:458-473`) incarca liniile documentului si adauga peste ele lista de preturi, ca sa se poata +adauga articole noi. `oDateFactura.completeaza_setari_document(toFactura, .T.)` +(`COMUN\programe\ofacturare_comun.prg:362-412`) precompleteaza antetul. + +**Ce nu se poate refolosi ca atare:** `do_copiaza` **degradeaza intentionat tipul** — factura din +contract / comanda / aviz devine `tip = 1`, avizele devin `22`, transferurile `10` +(`ofacturare_comun.vc2:3696-3710`), cu comentariul *"nu are sens sa fie copiata, dar o tratez ca pe +o factura din lista de preturi"*. La regenerare, degradarea e **exact ce nu trebuie sa se intample**: +documentul nou trebuie sa ramana pe acelasi tip, ca sa reconsume sursa. Deci se cloneaza structura +lui `do_copiaza`, nu comportamentul. + +### E. `ID_FACT` — cerinta si ce presupune ea + +**Cerinta (decizia 2):** documentul reemis pastreaza `ID_FACT`. + +**Starea de azi, verificata:** `ID_FACT` se genereaza cu secventa, neconditionat. +`PACK_CONTAFIN.SET_IDFACT(V_GCS)` e `SELECT SEQ_IdFact.NEXTVAL` +(`COMUN\docs\PACK_CONTAFIN.pck:3037-3040`), citit cu `get_idFact()` (`:725`), iar `DOCUMENTE` +primeste rand nou cu acel `ID_DOC` (`:796-808`). + +**Dar intuitia „id_fact depinde de numar si data" e corecta istoric:** exista in pachet varianta +`SET_IDFACT(tdDataAct, tcSerie_Act, tnNrAct, tnId_Ctr)` care cauta intai in `DOCUMENTE` dupa +`NRACT` + `SERIE_ACT` + `DATAACT` + `ID_CTR` si ia secventa doar la `NO_DATA_FOUND` +(`:3016-3035`) — **comentata**, deci inactiva. + +**Ce inseamna pentru #13** (de proiectat in S9, dupa verificarea din handoff): + +- fie se reactiveaza cautarea, sub un comutator folosit **numai** de regenerare — modificarea lui + `SET_IDFACT` fara comutator ar schimba comportamentul pentru toata suita, ceea ce nu se face; +- fie se transmite `ID_FACT`-ul cunoscut, ca parametru, pe drumul de scriere; +- **capcana de ordine**: cautarea filtreaza `STERS = 0`. Daca documentul vechi e marcat sters + inainte, cautarea nu-l mai gaseste si se genereaza id nou. Deci `ID_FACT`-ul se citeste **inainte** + de stergere, in aceeasi tranzactie. + +Cu `ID_FACT` pastrat, ce ramane de inventariat e mult mai putin decat in varianta cu id nou: +`ATASAMENTE_VANZARI` si `marcheaza_facturat` merg pe `ID_VANZARE` (care **se schimba**), +`ANAF_EFACTURA` e gol prin garda, iar `DOCUMENTE` / `ACT` / `RUL` / `JV2007` / `IREG_PARTENERI` merg +pe `ID_FACT` (pastrat). **`ID_VANZARE` nou ramane singura discontinuitate reala** — de verificat +daca `DOCUMENTE` primeste un al doilea rand pe acelasi `ID_DOC` sau il refoloseste. + +### F. Serie, numar, data — cale proprie, nu regenerare + +**Cerinta (deciziile 3 si 4):** toate datele se pastreaza, inclusiv numarul; iar antetul care intra +in identitatea documentului se modifica pe cale proprie. + +Calea exista deja si e exact ce trebuie: `pack_facturare.modifica_date_factura` +(`PACK_FACTURARE:14392-14463`) face `UPDATE` in loc si **propaga serie / numar / data / scadenta +pe `VANZARI`, `DOCUMENTE`, `ACT`, `IREG_PARTENERI`, `JV2007`, `RUL`, dupa `ID_FACT`** +(`:14427-14460`). Tot ea scrie delegat, agent, masina, adresa de facturare, text aditional, +`dataora_exp`, `listare_detaliata`, `tip_saft`, `efactura`. E procedura din spatele actiunii de azi +`frm_facturi.do_modifica` -> `frm_modifica_factura`. + +**Deci in formularul unificat:** campurile de antet se scriu prin `modifica_date_factura`, nu prin +regenerare. Regenerarea porneste doar pentru ce atinge sumele. Vezi tabelul din G-bis. + +`oGeneratorNumere` (`COMUN\programe\oserii_numere.prg:227-233`) nu incurca: la modificare nu se +aloca numar nou, deci nu e nimic de dezalocat. Intrebarea despre o eventuala constrangere de +unicitate pe `VANZARI(SERIE_ACT, NUMAR_ACT)` **dispare** — nu mai exista doua documente nesterse cu +acelasi numar, pentru ca numarul nu se realoca; ramane doar cazul „vechi sters + nou nesters", care +e exact situatia de azi de la orice stergere si reintroducere. + +### G-bis. Cele trei modificari de azi, si ce se scrie la confirmare + +Pe `frm_facturi` exista azi trei actiuni care ating acelasi document, fiecare cu fereastra ei: + +| Actiune de azi | Formular | Ce atinge | Sursa | +|---|---|---|---| +| `do_modifica` | `frm_modifica_factura` | antet: ruta, delegat, agent, serie, numar, date | `ofacturare_comun.vc2:4538-4637` | +| `do_modifica_explicatie` | `frm_modifica_articol_factura` | `explicatie` + `taxcode` pe o linie | `:4639-4656` | +| `do_editare_factura` (#6) | `frm_modific2024` | nota contabila si rulajele | `:3715-3869` | +| — | — | **cantitati, preturi, linii: nicaieri** | — | + +Decizia 6 le comaseaza in formularul unificat. Ruta de scriere se alege dupa ce s-a schimbat: + +| Ce s-a schimbat | Cum se scrie | Efect | +|---|---|---| +| Serie, numar, data, scadenta, delegat, auto, agent, adresa facturare, text aditional | pe loc, `modifica_date_factura` | propaga dupa `ID_FACT` | +| Explicatia si `taxcode` pe o linie | pe loc, `modifica_explicatie_articol` (`PACK_FACTURARE:14464-14472`) | doua coloane, nicio suma | +| Cantitati, preturi, linii adaugate/sterse, discount, gestiune, cota TVA | **regenerare** | vechiul `sters = 1`, documentul nou scris pe drumul de emitere, note si rulaje refacute | +| Nimic | nimic | documentul nu se rescrie degeaba | + +Ultima linie nu e cosmetica: fara ea, orice deschidere a formularului ar produce un document nou si +ar umple `VANZARI` cu randuri sterse. + +**Gol descoperit la S1 (runda 7): tabelul de mai sus nu acopera tot antetul.** Inventarul camp-cu-camp +(`docs\S1_inventar_campuri_formular_unificat.md`) a gasit trei grupuri de campuri de antet care **nu +sunt printre cei 14 parametri** ai lui `modifica_date_factura` si pentru care nu s-a gasit alta ruta: + +- **analiticele** — venit/cheltuiala, sectie, responsabil, lucrare (grupul „Pliat — analitice” din I); +- **identitate/sursa, altele decat serie/numar/data/scadenta** — tip document, valuta, zi curs, client, + sursa/„altele”, gestiune sursa, politica de preturi; +- **grupul de incasare** — mod incasare, casa, serie si numar de chitanta/bon, suma incasata, POS. + +**Verificat (runda 7): `docs\cercetare\rute_scriere_antet.md`.** Cautare exhaustiva in ambele straturi +— Oracle (pachetul de facturare curent, plus `PACK_CONTAFIN`, `PACK_UPDATE`, `PACK_MIGRARE`; toate +cele 13 aparitii de `UPDATE VANZARI` inspectate individual) si VFP (indexul complet: 316 fisiere, +1128 clase, 9281 metode). Rezultatul e neuniform pe cele trei grupuri: + +| Grup | Verdict | Ce inseamna | +|---|---|---| +| **B** — tip document, valuta, zi curs, client, sursa, gestiune sursa, politica de preturi | **NU**, toate sapte | Controalele traiesc **exclusiv** in wizardul de emitere; nu exista niciun `UPDATE` care sa le atinga dupa emitere. Un „nu" verificabil, nu o absenta de cautare. | +| **C** — mod incasare, casa, serie/nr chitanta, suma, POS | **NU direct** | Nu exista coloane proprii pe `VANZARI`: incasarea devine ea insasi **o linie de nota contabila** (`scrie_incasare2`). Nicio procedura de tip „modifica incasare". | +| **A** — sectie, responsabil, lucrare | **DA** | Dar nu prin `modifica_date_factura`, ci prin **mecanismul lui #6**, aterizat chiar pe branch-ul asta (`97d1613`). Vezi mai jos. | +| **A** — venit/cheltuiala (`ID_VENCHELT`) | **neconfirmat** | Gridul afiseaza coloana `dst_chlt`, dar `do_modifica` nu are caz pentru ea. | + +**Descoperirea care conteaza: analiticele sunt deja editabile, dar din #6.** `frm_facturi.do_editare_factura` +(`ofacturare_comun.vc2:3690-3869`) e acum cablat la `frm_modific2024` (`omodificari.vc2`), incarca +nota contabila a documentului emis si o rescrie prin `OSCRIE_IN_FISIERE` -> +`pack_contafin.finalizeaza_modificare_nota`, care **resincronizeaza `VANZARI`**. `do_modifica` +(`omodificari.vc2:13934-13943`) trateaza explicit `id_lucrare` si `id_responsabil`. + +**Doua nuante care schimba proiectarea, nu doar inventarul:** +1. **Editarea e la nivel de LINIE de nota, nu de antet.** La emitere, cele patru analitice se scriu + uniform pe tot documentul, din variabilele de sesiune. Prin editorul lui #6, fiecare linie se poate + edita separat — deci un document poate ajunge cu **sectii diferite pe linii diferite**, stare + imposibila la emitere. Conteaza pentru orice raportare care presupune „un singur `ID_SECTIE` per + factura". +2. **Bug suspectat pe `sectie`** (`omodificari.vc2:13941`): `replace ... id_valuta with + loCauta.id_sectie` — pare sa scrie in `id_valuta` in loc de `id_sectie`. Daca e real, textul afisat + se schimba dar coloana nu. **E in perimetrul lui #6** — se semnaleaza, nu se repara aici. Pana la + verificare, „DA" pentru `ID_SECTIE` e un da cu rezerva. + +**Consecinta pentru decizia 9**, acum pe fapte: `but_modifica` **poate salva pe loc doar cei 14 +parametri**. Pentru grupurile B si C nu exista alta cale decat **regenerarea**, care le rescrie oricum +pe toate, fiind chiar drumul de emitere. Pentru grupul A exista o a treia cale, dar e a lui #6 si +lucreaza pe alt nivel (linie de nota). + +**Tabelul de rutare, completat (deciziile 25 si 26):** + +| Ce s-a schimbat | Cum se scrie | +|---|---| +| cei **14 parametri** (serie, numar, data, scadenta, ruta, delegat, masina, agent, `dataora_exp`, adresa facturare, text aditional, `listare_detaliata`, `tip_saft`, `efactura`) | **pe loc**, `modifica_date_factura` | +| explicatia si `taxcode` pe o linie | **pe loc**, `modifica_explicatie_articol` | +| cantitati, preturi, linii, discount, gestiune, cota TVA | **regenerare** | +| **grupul B** — tip document, valuta, zi curs, client, sursa, gestiune sursa, politica de preturi | **regenerare** (decizia 25); blocate in etapa I | +| **grupul C** — incasare | **regenerare** (decizia 25); blocate in etapa I | +| **grupul A** — venit/cheltuiala, sectie, responsabil, lucrare | **regenerare** — editabile si la introducere, si la reemitere (decizia 72, rastoarna 26) | +| nimic | nimic | + +### G. Stocul — de ce argumentul lui #6 nu inchide subiectul + +Verificarea de stoc **nu** e in `adauga_articol_factura`: acolo erorile sunt de configurare +("Nu a fost gasita cota de TVA", politica / contract lipsa — `PACK_FACTURARE:5125-5180`). +Descarcarea efectiva de gestiune se face la emitere, in `contabilizeaza_articol` -> +`descarca_gestiune` (`:7631+`). Plafonul pe cantitate pe care il vede utilizatorul azi vine din +**cursorul client** `crsarticole` (`frm_facturare_articole.do_adauga_articol`, +`ofacturare.vc2:12813-13086`) — cursor care in formularul unificat **nu mai exista**, pentru ca nu +se mai incarca in masa. + +Deci, pe #13: + +- in timpul compunerii **nu exista plafon** — coincide cu decizia deja luata de Marius pe #6 la + 08.08.2026 ("editarea unei facturi emise nu are plafon si nu verifica stocul"); +- la finalizare, daca **stergerea ruleaza inaintea reemiterii, in aceeasi tranzactie**, stocul + consumat de documentul initial e deja eliberat cand se descarca gestiunea pentru documentul nou. + +**Aici e riscul tehnic principal.** Azi stergerea si emiterea deschid fiecare propria tranzactie +(`do_deschide_tranzactie` / `do_inchide_tranzactie` in ambele cai). O tranzactie nu poate fi tinuta +deschisa peste minutele in care utilizatorul editeaza formularul. Ordinea corecta e deci: +**citire (fara tranzactie) -> editare -> o singura tranzactie la confirmare, care contine intai +stergerea, apoi scrierea.** Asta cere mutarea apelului de stergere in interiorul tranzactiei +deschise de `do_scrie_articole`, nu inaintea ei. + +Atentie si la `VANZARI_DETALII_TEMP`, care e GTT `ON COMMIT DELETE ROWS`: umplerea si consumul +trebuie sa ramana in aceeasi tranzactie — ceea ce e compatibil cu schema de mai sus, dar interzice +un `commit` intermediar dupa stergere. + +### H. Pretul rescris de server la reemitere + +`adauga_articol_factura` (`PACK_FACTURARE:4972-5268`) ramifica pe `V_OPT_FACTURARE` si, pe unele +ramuri, **re-deriva pretul, cota TVA si valuta din documentul sursa** (ex. `V_OPT_FACTURARE = 3`, +preturile de pe contract, `:5129-5168`), in timp ce pe ramura implicita valoarea venita din VFP +castiga (`V_PRET := V_PRET_TEMP`, `:5183-5186`). Planul #6 semnalase deja acelasi lucru. + +**Consecinta pentru #13:** pe facturile din contract (si posibil pe alte ramuri), pretul editat de +utilizator poate fi suprascris tacut la reemitere. De inventariat ramura cu ramura, si de decis daca +regenerarea trece un flag "preturile vin din formular, nu se re-deriva". **Nu se porneste +implementarea inainte de acest inventar** — e singurul punct din care poate iesi o factura reemisa +cu alte sume decat cele confirmate pe ecran. + +### I. Asezarea antetului (deciziile 8 si 9) + +**Doua grupuri vizibile, restul pliat.** Grupurile si etichetele reale, din inventar: + +| Grup | Controale | Note | +|---|---|---| +| Identitatea documentului | `Ct_clb_fdoc` („Tip document”: FACTURA / PROFORMA / BON FISCAL), `Clb_serie_act`, `Clb_nract`, `Clb_dataact`, `Clb_data_scadenta`, `Clb_zi_curs`, `Ct_clb_valuta` | scadenta e **dezactivata**, nu eliminata, cand `gnScadentaAutomata = 1` (`ofacturare.vc2:9713-9715`); ziua de curs se elimina pe retur (`:9717-9722`, tipurile 8 si 9 — nu si 24); valuta se elimina cand `in_valuta = 0` (`:9725-9728`) | +| Partener si sursa | `Ct_clb_nume_client`, `txtCodFiscal` + `But_verifica1` (ANAF), `txtSoldLei`, `Ct_clb_altele` (sursa), `Ct_clb_gestiune_init`, pe aviz `Ct_clb_politici_preturi` | codul fiscal si soldul sunt read-only si derivate | +| **Pliat** — analitice | `Ct_clb_venchelt`, `Ct_clb_sectie`, `Ct_clb_responsabil`, `Ct_clb_lucrare` | responsabilul si gestiunea sursa se elimina azi cand `gnScadereStoc = 0` sau tipul e 4/7/8/9/48/49 (`ofacturare.vc2:9646-9707`) | +| **Pliat** — alte date | cele patru grupuri din `frm_alte_date`, vezi B | | + +**Butonul de modificare a antetului (decizia 9).** Mecanismul **exista deja**, si e mai fin decat +credeam: `frm_modifica_factura` (`ofacturare_comun.vc2:5262-5774`) are **patru bife individuale** — +`chkSerieAct`, `chkNrAct`, `chkDataAct`, `chkDataScad` — fiecare deblocand exact textbox-ul ei +(`thisform.txtDataAct.Enabled = this.Value`, `:5758-5772`), si toate patru sunt ele insele inactive +daca documentul nu are numar (`this.chkSerieAct.Enabled = !EMPTY(NVL(poRec.numar_act,0))`, +`:5736-5750`). **Restul campurilor de antet nu sunt blocate deloc** — delegat, agent, masina, ruta, +adresa, dataora expediere, text aditional sunt mereu editabile. + +In formularul unificat (**decizia 9, forma finala — un singur buton comutator, fara nicio bifa**): + +- in bara panoului de antet sta **un singur `but_modifica`**; apasat, deblocheaza **tot antetul** — + si campurile „moi”, si cele patru de identitate — si **isi schimba imaginea in discheta lui + `but_salvare`**; documentul deschis pentru consultare ramane inert; +- **cele patru bife individuale dispar.** Nu se transpun in formularul unificat sub nicio forma: + butonul e singurul control de protectie a antetului; +- **si conditia lor se abandoneaza.** Azi bifele de serie si numar sunt ele insele inactive dupa + `!EMPTY(NVL(poRec.numar_act,0))` (`:5736-5750`); regula asta nu se mai transpune pe campuri. + Cu antetul deschis, **toate campurile de antet sunt editabile, fara exceptie si fara conditie de + stare**. E o pierdere deliberata de protectie, in schimbul unei singure reguli inteligibile: + antet blocat sau antet deschis, atat; +- a doua apasare **salveaza doar antetul**. E posibil pentru ca + `do_modifica` cheama azi **exclusiv** `pack_facturare.modifica_date_factura`, cu 15 parametri toti + de antet, si niciun apel spre articole, note sau `oscrie_in_fisiere` (`ofacturare_comun.vc2:4587-4630`). + Acopera cazul „vreau sa corectez doar delegatul”. + **Precizare, ca sa nu se citeasca gresit:** „nu atinge notele si rulajele” e adevarat **la nivel de + apel VFP**, nu si in Oracle. Corpul procedurii propaga seria, numarul si datele in `JV2007` si `RUL` + (`PACK_FACTURARE.pck:14428-14459`) — coloane de **identitate**, nu sume: nicio valoare nu se + recalculeaza. Pentru celelalte zece campuri, scrierea e strict in `VANZARI`. Vezi I-bis. +- **`Termina` ramane salvarea intregului document**, cu rutarea din G-bis. + +**Ce nu exista si trebuie scris:** containerele efectiv folosite in antet nu au un mecanism gata +facut de blocare. `ct_clb_cautare` are `do_activeaza` / `do_dezactiveaza` (`caut_ora.vc2:780-806`), +dar acestea doar ascund iconita de cautare, iar in `ofacturare_comun.vc2` nu sunt apelate nicaieri; +`clb_tx_simplu` (`lb_tx.vc2:551-585`) si `clb_serie_act` (`serii_numere.vc2:7`) **nu au deloc** +metode de activare. Singura reteta completa din biblioteca e `clb_tx_data.dezactiveaza()` / +`reactiveaza()` (`lb_tx.vc2:521-538`: `ReadOnly` + `TabStop` + butonul de calendar) — se +generalizeaza de la ea, nu se inventeaza alta. + +Decizia 5 ramane in picioare: **asezarea e identica** la introducere si la modificare; difera doar +starea antetului. + +### I-bis. Contractul lui `modifica_date_factura` (decizia 19) + +Raport complet: `docs\cercetare\modifica_date_factura_parametri.md`. Spec `PACK_FACTURARE.pck:921-935`, +corp `:14392-14462`, singurul apel VFP la `ofacturare_comun.vc2:4599-4614`. + +Procedura are **doua regimuri de scriere**, si diferenta dintre ele decide cum se cheama din +formularul unificat: + +| Parametri | Regim | Unde ajung | +|---|---|---| +| `V_ID_VANZARE` | doar `WHERE` | identitatea randului; sursa lui `lnIdFact` (`:14424-14426`) | +| `V_ID_RUTA`, `V_ID_DELEGAT`, `V_ID_AGENT`, `V_ID_MASINA`, `V_DATAORA_EXP`, `V_ID_FACTURARE`, `V_LISTARE_DETALIATA`, `V_TEXT_ADITIONAL`, `V_TIP_SAFT`, `V_EFACTURA` | **scrise neconditionat**, inclusiv cu `NULL` | `VANZARI`, randul curent (`:14414-14423`) | +| `V_DATA_ACT`, `V_DATA_SCAD`, `V_NUMAR_ACT`, `V_SERIE_ACT` | scrise **doar daca** sunt `NOT NULL` **si** difera de valoarea curenta | `VANZARI` + propagare in `DOCUMENTE`, `ACT`, `IREG_PARTENERI`, `JV2007`, `RUL` (si `RUL.DATAOUT`), toate **dupa `ID_FACT`**, nu dupa `ID_VANZARE` (`:14428-14459`) | + +**Consecinta 1 — apelul trimite intotdeauna antetul intreg.** Cele zece campuri din regimul +neconditionat se scriu cu ce primesc, deci un apel cu `poRec` completat partial **goleste** in baza +campurile netrimise. Butonul de antet nu poate trimite „doar ce s-a schimbat”: incarca antetul +curent, aplica modificarile peste el, trimite tot. + +**Consecinta 2 — cele patru campuri de identitate ating cinci tabele.** Propagarea dupa `ID_FACT` +inseamna ca schimbarea seriei sau a numarului dintr-un document atinge toate randurile legate de +acelasi `ID_FACT`. E acelasi mecanism pe care se sprijina S9 (reemiterea cu `ID_FACT` pastrat) si +acelasi motiv pentru care ordinea operatiilor conteaza acolo. + +**Consecinta 3 — un camp nou.** `V_EFACTURA` se scrie neconditionat dar nu are niciun control in +niciun formular; azi valoarea vine din `Scatter` sau e fortata `0` (`ofacturare_comun.vc2:4576`). +In antetul unificat primeste control, langa `V_TIP_SAFT` (ambele tin de raportare, nu de document). + +**Unde stau cele 14 in formularul unificat** (raportat la gruparea din I): cele patru de identitate +in antetul vizibil; ruta, delegatul, agentul, masina, `dataora_exp` si adresa de facturare in +sectiunea pliata, la „delegat si transport” / „adresa de facturare”; `text_aditional` si +`listare_detaliata` tot in pliat; `tip_saft` si `efactura` formeaza acolo un **grup nou, de +raportare**. Niciunul dintre cei 14 nu vine azi din `frm_alte_date` — clasa aceea nu e implicata +deloc in fluxul `do_modifica`, deci sectiunea B si I-bis nu se suprapun. + +### J. Butoanele de linie si adaugarea din sursa (decizia 13) + +**Cum sunt azi.** In `frm_facturare_articole` butoanele sunt imprastiate lateral, intre cele doua +griduri, si sunt iconite de 30x27 fara text: `But_modifica1` / `But_sterge1` la 773/61 si 811/61, +`But_reset1` la 303/295, `But_urmator1` la 343/348, `But_urmator_tot1` la 343/378, `But_retur` la +343/407 (`ofacturare.vc2:11192-11257`). **`But_urmator_tot1` — „adauga tot” — nu are nici caption +nici `ToolTipText`** (`:11257`); metoda lui, `do_adauga_tot` (`:13169-13198`), face SCAN peste +`crsarticole` si cheama `do_adauga_articol(.T.)` pe fiecare linie. + +**In prototip sunt deja unde trebuie**: `But_nou1` si `But_sterge1` la `Top = 175`, gridul la +`Top = 204` (`:15936`, `:15955`) — adica **deasupra tabelului**, exact cum cere decizia 13. +`But_nou1.Click -> do_adauga` (`:17118-17122`) e `APPEND BLANK` + focus in grid. + +**Ce lipseste: contractele.** `But_urmator_tot1` e vizibil pe `eProforma = 1`, `lCopiere`, tip 3 +(comanda), tip 4 (din avize), 21/28/42/47, 25, 8/9, 24 (`:15113-15245`). **Tipurile de contract +(2, 6, 26, 52) nu sunt in lista.** Deci „adauga tot” acopera deja avizele, dar nu contractele. + +**Alegerea selectiva — tiparul importului RORIS.** In ROAACNPRO, `cmdImport` („Import Roris & +Contracte”, `oacnpro.vc2:6894-6905`) face exact ce s-a cerut aici, si merita copiat ca structura: + +1. **buton activ conditionat** de document editabil **si** tip care are sursa + (`llFacturaEditabila and llRorisSauContracte`, `oacnpro.vc2:8117-8145`); +2. **o metoda unica de intrare** care ramifica pe tip (`do_executa` -> `factura_import`, + `proceduri_acnpro.prg:3151-3212`); +3. **dialog modal de selectie** cu coloana de bifat si criterii de cautare (`frm_tranzit`, coloana + `cAles` legata de `crsConvoaie.ales`, `oacnpro.vc2:14500-14515`, `:14801`), urmat de un al doilea + nivel de calcul / confirmare; +4. **populare aditiva** prin `INSERT INTO` in cursorul local de articole, fara nicio procedura Oracle + — pachetele intra abia la salvare (`proceduri_acnpro.prg:3331-3467`); +5. **protectie minimala la dublu-import**: butonul se dezactiveaza dupa un import reusit + (`oacnpro.vc2:7827-7834`), plus avertisment neblocant daca sursa a mai fost folosita + (`:14915-14930`). + +Ce **nu** se copiaza: sursele de date si calculele ACN (`ips_*`, tarife, ecluzari), formularele lor, +si valorile `poDate.cTip`. Ce trebuie facut mai bine: la zero rezultate, dialogul RORIS nu spune +nimic — butonul pare ca nu face nimic (`oacnpro.vc2:14898-14943`). + +**Bara propusa, deasupra gridului (decizia 13):** linie noua · sterge linia · detalii linie ‖ +**un singur buton „Adauga articole”**, care deschide un `xmenu()` cu optiunile potrivite sursei — +nu doua butoane separate. Tiparul e deja folosit in produs: `frm_facturi.but_modifica1` -> +`inainte_de_do_modifica` -> `xmenu("Modificare date factura;Editare factura (articole, cantitati, +preturi)")` (`ofacturare_comun.vc2:4925-4934`). + +| Sursa documentului | Optiunile din meniu | +|---|---| +| comanda | Adauga tot din comanda · Alege din comanda… · Cauta in lista de preturi… | +| contract | Adauga tot din contract · **Alege ratele de facturat…** · Cauta in lista de preturi… | +| avize | Adauga tot din avize · Alege avizele… · Alege liniile din avize… · Cauta in lista de preturi… | +| lista de preturi | Cauta in lista de preturi… · Retur de articole… | +| retur (tip 8, 9, 24) | Alege facturile de returnat… · Alege liniile de returnat… · Cauta in lista de preturi… | + +**Constanta meniului (deciziile 16 si 18): ultimele doua optiuni sunt aceleasi peste tot** — +**„Cauta in lista de preturi…”** (cu pret din politici) si **„Alege din nomenclator…”** (cu pret +tastat, ca „Alte servicii” din ROAAUTO, O-bis). Apar inclusiv pe documentele cu sursa, pe cele de +retur si pe cele deja emise care se modifica. Sursa umple documentul, nu il inchide. + +**Decizia 20 (Marius, 09.08.2026): din nomenclator se pot alege si articole gestionabile, si +negestionabile.** Nu se preia filtrul `in_stoc = 0` al lui ROAAUTO — acolo e o ocolire a subiectului +gestiunii, nu o regula de produs. Consecinta directa: pe documentele auto vor putea aparea linii care +descarca stoc langa linii care nu descarca, caz care azi nu exista nicaieri (O-bis, intrebarea 2). + +#### J-bis. Contul de venit al liniei — intrebarea lui Marius era corect pusa + +Doua cercetari, a doua o corecteaza pe prima. **Valabil e al doilea raport:** +`docs\cercetare\cont_venit_corespondente.md`. Primul (`cont_venit_articol_fara_politica.md`) ramane +util pentru contul de **gestiune**, dar concluzia lui pe venit e gresita si nu se citeaza. + +**Ce a gresit primul raport.** A cautat literalii `707` / `704` / `706` / `708` in `pack_facturare`, +nu i-a gasit (corect — nu sunt hardcodati nicaieri) si a conchis ca **nu exista cont de venit pe +linie**. Exista. E doar **indirect**, si sta chiar in `pack_facturare`, nu intr-un pachet de +contabilitate separat. + +**Sunt doua conturi pe aceeasi linie de vanzare, cu surse complet diferite:** + +| | Contul de **gestiune** | Contul de **venit** | +|---|---|---| +| Unde ajunge | `VANZARI_DETALII.CONT` (`varchar(4)`) | `SCC` in randul de nota contabila | +| De unde vine | `NOM_ARTICOLE.CONT` / `STOC.CONT` / `NOM_GESTIUNI.CONT` | `NOTE_CONTABILE.SCC` | +| Prin ce | alegerea lotului (`do_alege_stoc`) | **politica de pret** a articolului | +| Cine il consuma | `descarca_gestiune` (`ff_...:7485-7507`) | `scrie_nota` (`ff_...:7452-7476`) | + +Lantul care aduce venitul, in `pack_facturare.contabilizeaza_articol` (`ff_...:7182-7556`, cursorul +`cursor_articol` la `:7227-7280`): + +**`CRM_POLITICI_PRET_ART` -> `CRM_POLITICI_PRETURI.ID_NOTA` -> `CRM_NOTE_VANZARI.ID_SET` -> +`NOTE_CONTABILE.SCD` / `SCC`** + +Pentru o factura normala (`ntip <= 20`), `SCD` e contul debitor (creanta) si **`SCC` e contul de +venit efectiv** al liniei (`ff_...:7407-7437`). Nu e derivat din nimic: perechea SCD/SCC e **scrisa +manual de un contabil**, per sablon de nota, din ecranul „Configurare note contabile" +(`COMUN\ferestre\frm_config_note_contabile[2007].sc2`, clasa `onote_contabile.vc2:2401-2435`, +salvare la `:315-322`, `:486-493`). + +**Nu exista tabel de corespondenta 3xx -> 7xx.** Cautat in `SCRIPTURI_CLAR` sub toate numele +plauzibile — negativ. Ipoteza lui Marius era corecta ca **intentie** (exista un mecanism care leaga +articolul de contul de venit), dar mecanismul e **configurare manuala pe politica de pret**, nu +derivare automata din contul de stoc. + +**Consecinta care conteaza pentru #13 — si care valideaza intrebarea initiala a lui Marius.** Daca +contul de venit vine **prin politica de pret**, atunci un articol adaugat **direct din nomenclator, +fara politica** (exact ce cere decizia 16 / 20) nu are `ID_POL`, deci cursorul de mai sus — filtrat +`WHERE A.ID_POL = detalii_articol.id_pol` — nu intoarce niciun rand, si `SCD` / `SCC` raman `NULL` +(sunt `LEFT JOIN`-uri). **Intrebarea „cu ce cont de venit intra un articol fara politica de pret" era +deci exact intrebarea corecta.** + +#### J-ter. Raspunsul: nu „cu niciunul”, ci **eroare** — si nu exista precedent + +Verificat: `docs\cercetare\nota_contabila_fara_politica.md`. Doua rezultate, amandoua mai dure decat +se anticipase. + +**1. Codul nu scrie tacut un cont gol — refuza documentul.** `contabilizeaza_articol` cauta politica +articolului **inainte** de cursorul care aduce `SCC` (`ff_...:7286-7311`), si pe `NO_DATA_FOUND` +ridica `RAISE_APPLICATION_ERROR` cu **`FACT-024`** („Articolul … nu este definit in politica de +preturi …”). Cu `id_pol` `NULL`, `ID_POL = detalii_articol.id_pol` nu poate potrivi nimic, deci +**mereu** se intra pe exceptie. Cursorul cu `SCC` nu se mai deschide. Nu exista ramura de default, +nu exista fallback, si `scrie_nota` (`ff_...:12338-12570`) **nu are nicio validare pe cont** — dar +nici nu ajunge sa fie chemat. +Concluzie: o linie fara politica trecuta prin codul de azi **opreste tranzactia cu eroare**, nu produce +o nota incompleta. Din perspectiva datelor e vestea buna; din perspectiva lui #13, e blocantul dur. + +**2. „Alte servicii" din ROAAUTO NU e precedent** — premisa pe care se sprijinea decizia 18 cade. +Liniile acelea chiar ajung cu `id_pol` gol (confirmat: nici `crsalteserv`, nici `crsvanztemp`, nici +semnatura lui `adauga_articol_factura_deviz` nu au `id_pol`, iar `INSERT`-ul in +`VANZARI_DETALII_TEMP` nu include coloana). **Dar ele nu trec niciodata prin +`contabilizeaza_articol`**: fluxul ROAAUTO cheama `initializeaza_date_factura` -> +`adauga_articol_factura_deviz` -> `scrie_in_vanzari` -> `pack_auto.actualizeaza_deviz` +(`oproceduri_devize.prg:1211-1310`), iar `scrie_in_vanzari` (`ff_...:13497-13962`, citit cap-coada) +**nu genereaza nicio nota contabila de venit** — copiaza `ID_POL` (deci `NULL`) direct in +`VANZARI_DETALII` si actualizeaza totalurile. +Deci ROAAUTO nu demonstreaza ca articolele fara politica sunt tolerate; demonstreaza ca **exista o cale +paralela de facturare care ocoleste contabilizarea de venit prin `pack_facturare`**. Unde capata acele +documente contul de venit — daca il capata — **nu se vede in codul cercetat**; `actualizeaza_deviz` +presupune deja existenta unui rand in `ACT`, a carui sursa nu a fost gasita. + +**3. Apelantii sunt trei, si acopera fluxul principal** (lasat neverificat de raportul 2): +`scrie_factura2` (`ff_...:6150`) — factura normala si majoritatea tipurilor —, `scrie_factura_avize_retur` +(`:6867`) si `scrie_aviz_retur` (`:7149`). Nu exista al patrulea. + +**4. `NOM_ARTICOLE.CONT` ca sursa de venit: NU.** Grep pe `NOM_ARTICOLE.CONT` in tot pachetul — zero +potriviri. `V_SCC` vine exclusiv din `D.SCC` al lantului de politica. Varianta propusa de Marius nu +exista nici macar ca ramura moarta. + +**Ce inseamna asta pentru perimetru.** „Alege din nomenclator…" nu mai e o adaugare de UI peste un +mecanism existent: **cere modificare in `pack_facturare`**, adica in COMUN, cu impact peste toata +suita — sau evitarea completa a lui `contabilizeaza_articol`, ca la ROAAUTO, ceea ce inseamna document +fara nota de venit. **Aceasta e o schimbare de cost, nu un detaliu**, si se decide de Marius (vezi +intrebarea deschisa de mai jos). + +**`NOM_ARTICOLE.CONT` nu e restrans la clasa 3** — confirmat: `verific_cont` +(`COMUN\programe\oproceduri_comune.prg:2389-2415`) verifica doar ca acel cont exista in planul de +conturi al anului (`vplcont_sintetic`), fara filtru de clasa; nu exista `CHECK CONSTRAINT` pe coloana +si nici vreo ramificare pe `Left(cont,1)` aplicata articolelor. Deci un articol negestionabil **poate** +purta legitim un cont 6xx / 7xx, cum a spus Marius. **Dar codul de azi nu-l foloseste asa**: acel camp +merge in `VANZARI_DETALII.CONT` si de acolo la `descarca_gestiune`, niciodata la `SCC`. Ca sugestia lui +Marius sa devina comportament, ar trebui **cod nou in `pack_facturare`** — adica in COMUN, cu impact +peste toata suita. *Vezi intrebarea deschisa de mai jos.* + +**`ID_VENCHELT` / `NOM_VENIT_CHELTUIELI` nu e sursa contului.** Tabelul nu are coloana de cont; +`ID_VENCHELT` se rezolva separat (`NVL(nid_venchelt, NVL(A.ID_VENCHELT, C.ID_VENCHELT))`, +`ff_...:7205`) si pleaca la `scrie_nota` ca **parametru propriu**, alaturi de `SCD`/`SCC` — e o a +treia dimensiune analitica (centru de venituri/cheltuieli, pentru raportare), nu contul insusi. +Presupunerea din runda 7 ca „venitul vine din `Ct_clb_venchelt`" **se retrage**. + +De unde vine `CONT`, pe cele doua cai: + +| Cale | Sursa contului | Fallback | +|---|---|---| +| **gestionabil, cu stoc** — trece prin `do_alege_stoc` (`ofacturare.vc2:13239-13345`) | `STOC.CONT`, de pe lotul ales (`cursor_gestiuni_articol`) | **niciunul** — lot cu `CONT` gol ramane `NULL` | +| **gestionabil, fara stoc** (`RF_FACTURARE_FARA_STOC = 1`) | `NOM_GESTIUNI.CONT` -> ultimul `STOC.CONT` -> `'371'` hardcodat (`cursor_gestiuni_articol_stoc0`) | singurul loc din pachet cu cascada si default | +| **negestionabil** — `frm_articol_factura` direct (`:12871-12880`) | `NOM_ARTICOLE.CONT`, citit la cautarea articolului | **niciunul**; gol -> sentinela `'XXXX'` -> `NULL` la INSERT | + +`adauga_articol_factura` **nu recalculeaza** contul: il primeste ca parametru `V_CONT` si il scrie ca +atare. **Nu exista nicio exceptie pe `CONT`** in tot pachetul — spre deosebire de cota de TVA, unde +lipsa produce explicit `FACT-012` / `FACT-013` / `FACT-018`. O linie fara cont se scrie tacut. + +**Precedentul ROAAUTO nu ofera o regula, ci absenta ei.** „Alte servicii” (O-bis) trimite literal `''` +catre `adauga_articol_factura_deviz` (`oproceduri_devize.prg:1256`), care ajunge `NULL` in Oracle, +fara `NVL` si fara fallback. Liniile de articole reale din ROAAUTO **ajung deci cu `CONT = NULL`**. + +**Decizia 21 (Marius, 09.08.2026)** acopera **contul de gestiune**: cand `NOM_ARTICOLE.CONT` e gol pe +un articol negestionabil ales din nomenclator, se aplica un **fallback la un cont implicit** — nu se +accepta `NULL` ca la ROAAUTO, nu se refuza adaugarea. Precedentul de forma exista in pachet: +`cursor_gestiuni_articol_stoc0` cade in cascada pana la `'371'` hardcodat. Contul anume ramane de ales +la implementare. **Decizia 21 nu rezolva insa contul de venit** — sunt doua campuri diferite, cum arata +tabelul de mai sus; la momentul cand a fost luata, distinctia nu era inca stabilita. + +~~**Decizia 24: articolul ales din nomenclator primeste `id_pol`-ul unei politici de pret +implicite.**~~ **RETRASA in runda 8.** Argumentul ei — zero cod Oracle nou, COMUN neatins — ramane +valabil ca argument, dar pretul lui era o intrebare de configurare cu contabilul (care politica, cu ce +`SCC`, una sau mai multe) si un cont de venit uniform pentru orice articol adaugat asa. Marius a ales +in loc derivarea contului. **Nu se reintroduce.** *Vezi J-quater.* + +Ramane valabil de aici, si e important: **alegerea politicii de catre operator** a fost si ea respinsa +— muta costul pe fiecare adaugare si cere operatorului sa stie ce inseamna o nota contabila. + +**Ce se stie sigur pana atunci:** decizia 16 si decizia 20 **nu sunt anulate** de subiectul asta — pe +articolele **cu** politica de pret (lista de preturi, comanda, contract, retur) contul de venit vine +ca azi si nimic nu se schimba. Blocajul e strict pe ramura **„Alege din nomenclator…"**, adica pe +partea din S4g care adauga articole fara politica. + +**Unde e efectiv de lucru pentru asta:** doar pe **comanda**. Verificat +(`docs\cercetare\retur_si_lista_preturi.md`, B): pe contract, `crsarticole` contine deja lista de +preturi intreaga, deci adaugarea libera **merge azi**; pe comanda, `cursor_comanda` umple `crsarticole` +strict cu articolele comenzii (`ofacturare.prg:266-308`), deci nu merge. Restrictia **nu** e pe buton +si nu e la validare — e in continutul cursorului. Se rezolva ca la copiere: `APPEND FROM` peste +`crsarticole` cu rezultatul lui `cursor_preturi` (`ofacturare.prg:454-473`). + +#### J-quater. Deciziile 27 / 27-bis — contul de venit derivat, aplicat din VFP + +> **DEPASITA IN PARTE — de citit cu avertismentul asta in fata.** **Decizia 34** relaxeaza 27-bis +> (`contabilizeaza_articol` primeste parametru de cont) si **decizia 35** cere ca `pack_facturare` sa fie +> singurul cod de contare, si la emitere si la editare. Prin urmare: +> - **punctul 1** (de ce ROAACNPRO si contractul nu sunt contraexemple) si **punctul 2** (derivarea +> contului din `CORESP_CONT_VENCHELT` — regula deciziei 27) **raman valabile integral**; +> - **punctul 3, „reteta in 4 pasi"** — politica tehnica per `SCC`, interogarea inversa, +> `pack_preturi.adauga_politica_pret_art`, inserarea articolului in politica — e **ABANDONAT**. Se +> pastreaza doar ca trasabilitate a rationamentului. **Nu se implementeaza si nu se reargumenteaza.** +> - **cele trei variante** de la finalul sectiunii (reteta / nota pe antet / nota pe antet ca fallback) +> sunt **caduce**: alegerea a fost facuta prin decizia 34. +> +> Proiectarea in vigoare e parametrul deciziei 34 — `docs\cercetare\parametru_cont_contabilizeaza_articol.md`. + +Raport: `docs\cercetare\coresp_cont_venchelt.md`. Trei lucruri s-au stabilit, in ordinea in care conteaza. + +**1. Intrebarea lui Marius („in ROAACNPRO si pe factura din comanda / contract se adauga articole fara +politica — de ce nu si aici?") a primit raspuns: observatia e reala, dar niciunul din cele doua exemple +nu e un contraexemplu.** `FACT-024` ramane blocantul. + +- **ROAACNPRO nu foloseste deloc `contabilizeaza_articol`.** Salvarea facturii ACN + (`proceduri_acnpro.prg:3331-3467`) merge pe `initializeaza_date_factura` -> + **`adauga_articol_factura_deviz`** -> **`scrie_in_vanzari`** -> **`pack_acn.salveaza_regdoc`**. + Cautare directa dupa `contabilizeaza_articol` in acel fisier: **zero rezultate**. Nota contabila o + scrie o procedura proprie ACN, care nici nu primeste `id_pol` printre parametri. Acelasi tipar ca + ROAAUTO „Alte servicii" (J-ter, punctul 2): **o cale de facturare paralela**, nu o dovada ca articolele + fara politica sunt tolerate. +- **Pe contract, articolul „liber" nu e fara politica.** `crsarticole` e populat de + `pack_facturare.cursor_contract` / `cursor_preturi`, care **selecteaza explicit `A.ID_POL`** + (`ff_...:2162-2167`) si care ruleaza la fiecare apel `completare_politica_stoc` (`ff_...:2151`). + Deci fiecare rand din grila vine deja **cu o politica reala atasata**. „Adaugare libera pe contract" + inseamna „orice articol din lista de preturi, nu doar cele din contract" — nu „articol fara politica". + + **Diferenta reala e cursorul-sursa al grilei, nu tipul de document:** `caut_articol` + (`COMUN\programe\ocautare.prg:1636-1735`, cautarea in nomenclator) **nu are coloana `id_pol` in nicio + ramura**; `cursor_preturi` / `cursor_contract` o au, pentru ca pleaca de la politica, nu de la + nomenclator. Povestea #13 cere exact ce nu exista azi nicaieri. +- **Ce verifica de fapt `FACT-024`:** `SELECT COMPUS, ID_POL_ART FROM VCRM_POLITICI_PRET_ART WHERE + ID_ARTICOL = ... AND ID_POL = ...` (`ff_...:7278-7302`) — deci **apartenenta articolului la politica + de pe linie**. Ipoteza din runda 8 e **partial** confirmata: verificarea e de apartenenta, dar `id_pol` + **nu** vine din antet ca valoare unica — e parametru per linie, trimis de VFP. Doua cauze diferite + (`id_pol` gol; articol nemembru) produc aceeasi eroare, iar codul nu le distinge. + +**2. `CORESP_CONT_VENCHELT` exista, e populat si e folosit in productie — dar niciodata pentru +`CONT_VENIT`.** Toti consumatorii gasiti (`pack_vin`, `pack_devize`, `gestiune_pack_gest_import`, +rapoarte de gestiune) citesc `CONT_CHELT` sau `CONT_APROVIZIONARE`. Coloana `CONT_VENIT` **are date +reale** dar **zero consumatori**, nici Oracle nici VFP. Cheia de join e stabila si testata: pe `CONT`, cu +`STERS = 0`, prin `LEFT JOIN` — tiparul se copiaza identic. Deci regula lui Marius e implementabila; e +insa **un consumator nou**, nu o reteta deja rulata. + +**Masurat pe baza vie (10.08.2026, `MARIUSM_AUTO`) — `docs\cercetare\verif_baza_vie_cont_venit.md`:** +tabelul are **41 de randuri active**, din care exact **20 au `CONT_VENIT`** populat; restul 21 sunt +conturile de ajustari (`391`…`398`, toate cu `CONT_CHELT = 681`), care nu poarta marfa. **Niciun `CONT` +nu e duplicat**, deci pasul 1 al retetei e determinist — desi schema nu are unique pe `CONT`, doar `PK` +pe `ID_CCV`. Si, decisiv: **zero articole active cad pe un rand cu `CONT_VENIT` gol**, deci ramura +„cont derivat gol" se trateaza defensiv dar nu are cazuri reale. DDL-ul complet e in raport. + +**3. Calea VFP (decizia 27-bis) exista si e construita din piese functionale.** Patru pasi. + +> **Clarificare ceruta de Marius (10.08.2026) — de citit inainte de cei patru pasi.** Politica **nu e +> necesara ca sa se afle contul.** Regula deciziei 27 il determina complet, in VFP: corespondentele pe +> clasa 3, contul de pe articol daca e 6xx / 7xx, `704` altfel. Aia e **pasul 1**, si e verificata pe date. +> Politica e necesara **doar ca canal de transport** catre Oracle: `contabilizeaza_articol` **nu are +> parametru de cont de venit** — primeste `V_ID_POL` si deduce singura contul prin lantul politica → nota → +> `NOTE_CONTABILE.SCC`. Deci **pasii 2-4 nu decid nimic**, doar convertesc un cont pe care VFP il stie deja +> intr-o forma pe care pachetul o accepta. Indirectarea e consecinta deciziei **27-bis**, nu a regulii lui +> Marius: daca pachetul ar putea fi modificat, un parametru de cont ar face pasii 2-4 sa dispara. +> **INCHIS (runda 9 + decizia 35).** Cautarea canalului direct s-a terminat — +> `docs\cercetare\canal_cont_venit_fara_politica.md`. Verdict: canalul **exista** (`actactan` / `tact` → +> `oscrie_in_fisiere` → `pack_contafin.SCRIE_IN_ACT`), dar e cablat pe **editarea unei note deja scrise**, +> nu pe emitere; emiterea trece exclusiv prin `scrie_factura2` → `contabilizeaza_articol`. Pistele +> `V_CONT` (e contul de gestiune), setterele de sesiune, `ID_VENCHELT` si `GetAnaliticByGrupUtilizatori` +> sunt **toate NU**. +> **Si nu se foloseste oricum:** decizia 35 il respinge explicit in #13, ca sa nu existe doua coduri de +> contare. Reteta in 4 pasi cade prin **decizia 34** (parametru de cont in pachet), nu prin canal. + +1. **VFP calculeaza `SCC`-ul** dupa regula deciziei 27: `CORESP_CONT_VENCHELT.CONT_VENIT` pe contul de + gestiune al liniei (articol gestionabil), `NOM_ARTICOLE.CONT` daca e 6xx / 7xx, altfel `'704'`. +2. **VFP cauta o politica a carei nota are deja acel `SCC`** — interogarea inversa pe acelasi lant: + `CRM_POLITICI_PRETURI -> CRM_NOTE_VANZARI -> NOTE_CONTABILE`, `WHERE NC.SCC = :cont_calculat`. + Interogare **noua**, dar pe coloane si relatii deja confirmate. + **Corectat pe baza vie:** legatura nu e politica -> nota, ci politica -> **set de note** — + `CRM_POLITICI_PRETURI.ID_NOTA` -> `CRM_NOTE_VANZARI.ID_SET` -> `NOTE_CONTABILE.ID_SET`. Pe cele 7 note + de vanzari active fiecare set are **un** rand, dar in tabel exista seturi cu pana la **30** de randuri, + deci „nota politicii" e bine definita doar cat timp seturile de vanzari rămân cu un rand. + **Si mai important: filtrarea pe `SCC` singur nu e suficienta** — vezi „Ce nu e gratuit" mai jos. +3. **VFP se asigura ca articolul e membru al acelei politici** — RPC **existent** + `pack_preturi.adauga_politica_pret_art`, deja folosit exact asa (verifica intai, insereaza daca + lipseste) in `ofacturare.vc2:15551-15587`, la modificarea listei de preturi de la NIR. **`pack_preturi` + nu e `pack_facturare`** — nu se atinge nimic din pachetul comun de facturare. +4. **VFP trimite acel `id_pol`** in `adauga_articol_factura` (`V_ID_POL` e parametru de intrare explicit, + `ff_...:4989-5015`). `contabilizeaza_articol` ruleaza **neschimbata**. + +**Modelul „politica tehnica, configurata o data, populata automat" exista deja in pachet:** +`completare_politica_stoc` (`ff_...:2093-2120`) face `MERGE` in `CRM_POLITICI_PRET_ART` pentru toate +articolele `IN_STOC = 1` sub politica `pack_facturare.nid_politica_stoc`, alimentata din optiunea globala +`gnId_pol_pret_stoc` (`ofacturare.vc2:21733-21753`). Nu e direct reutilizabila — are o singura nota +pentru toate articolele, iar decizia 27 cere pana la sase conturi diferite — dar arata ca tiparul e +acceptat in produs. + +**Calea VFP e mai completa decat modificarea pachetului**, nu doar mai ieftina: politica reala aduce si +`CU_TVA`, `IN_VALUTA`, `EXPLICATIE`, `ASCD`, `ASCC` din nota configurata. Un fallback in +`contabilizeaza_articol` ar da doar `SCC`; `CU_TVA` si `IN_VALUTA` **nu au nicio sursa alternativa** in +afara lui `NOTE_CONTABILE`, deci ar trebui hardcodate — cu risc de calcul gresit al bazei si al TVA. + +**Ce nu e gratuit, si trebuie stiut inainte de S4g:** + +- **Pasul 2 depinde de configurarea fiecarui client — deci nu se poate proiecta pe acoperire.** + Decizia lui Marius, 10.08.2026: **datele din Dev nu sunt baza de proiectare.** Fiecare client isi + defineste propriile politici de pret, fiecare cu nota ei de vanzare salvata pe un `ID_SET`. Pe schema + de dezvoltare masurarea a dat `704` cu 19 politici valabile, `707` cu 4, `7015` cu 1, si zero pentru + `711` / `702` / `703` / `7018` — **dar aceste numere nu se folosesc ca argument**, pe o baza de client + distributia poate fi complet alta. Ce se pastreaza din masurare e strict **structural** (forma lantului, + absenta unique-ului pe `CONT`, bug-ul de set multi-rand) plus un fapt negativ: **afirmatia rundei 8 ca + „pentru `704` nu exista nicio nota configurata" nu se susține ca regula** — pe Dev exista patru, deci + pasul „se creeaza nota pentru 704" nu poate fi pus in plan ca obligatoriu. + **Consecinta de proiectare:** reteta nu are voie sa presupuna ca gaseste o politica pentru `SCC`-ul + calculat. Ori garanteaza singura existenta a ceea ce foloseste (politica tehnica creata de produs), ori + are o cale care **nu trece prin politica deloc** — vezi mai jos. +- **`PTVA` de pe nota e inofensiv — verificat pe cod, nu presupus.** `cursor_articol` + (`PACK_FACTURARE:7218-7271`) **nu selecteaza `D.PTVA`**; cota folosita efectiv vine din + `detalii_articol.proc_tvav * 100 - 100` (`:7464`), adica din articol / document. Deci o nota cu + `PTVA = 5` pe un articol cu 21% **nu produce nimic** — coloana e moarta pentru aceasta procedura. + Ingrijorarea initiala („criteriul trebuie sa fie `(SCC, PTVA)`") **se retrage**. + `IN_VALUTA = 1` de pe nota pe un document in lei nu da nici eroare, nici suma greșita — doar populeaza + redundant `ACT_TEMP.SUMA_VAL` la curs 1 (`scrie_nota:12391-12404`, `:12492-12507`): inconsistenta + cosmetica, nu contabila. `ASCD` / `ASCC` nule au fallback automat prin + `GetAnaliticByGrupUtilizatori` (`:7409-7428`), iar `ID_PARTD` / `ID_PARTC` **nu se citesc de pe nota** + deloc — vin din contul si din contextul documentului (`:12510-12530`). +- **In schimb, `ID_SET` cu mai multe randuri dubleaza venitul SI descarcarea de gestiune.** Asta e + riscul real, si e mai grav decat cel presupus. `cursor_articol` face `LEFT JOIN NOTE_CONTABILE D ON + C.ID_SET = D.ID_SET` **fara `ROWNUM`, fara agregare, fara filtru**, iar bucla care il consuma + (`:7393-7542`) executa `scrie_nota` **si** `descarca_gestiune` **o data pentru fiecare rand al + setului, cu aceeasi cantitate si acelasi pret intreg de fiecare data** — nu impartite. Nu exista + nicio logica de distributie: `ORDINE` nu apare in tot pachetul. Deci pasul 2 nu trebuie doar sa + gaseasca un rand cu `SCC`-ul potrivit, ci **sa garanteze ca setul ales are exact un rand**. Cele 7 + note de vanzari active au, azi, exact un rand fiecare — dar 30 din cele 40 de seturi din baza au mai + multe, cu maxim 30. **Conditia care face reteta sigura e o coincidenta a datelor curente, nu o + garanție a codului.** Sursa: `docs\cercetare\s10_pret_rederivat.md`, „Completare: nota contabila a + politicii", punctul 1. +- **Pasul 3 poluează o lista de preturi de producție.** Politica **este** lista de preturi: candidatele + reale contin `STOC PRODUSE` cu **6310** articole si `LISTA PRETURI LEI` cu **909**, plus liste cu nume de + client si de sezon (`CHIOSC`, `SERVICE AUTO`, `S7 CRACIUN`…). Un articol adaugat ad-hoc pe factura ar + aparea de acum in acea lista, cu pretul cu care a fost facturat o data. **Deci pasul 2 nu trebuie sa + caute orice politica potrivita, ci o politica tehnica dedicata, una per `SCC`** — modelul + `gnId_pol_pret_stoc` extins de la una la sapte. Cele **sase** politici cu 0 articole din baza vie arata + ca o politica goala e o stare acceptata de produs. +- **Ambiguitatea celor 19 politici pe `704` e de lista de preturi, nu de rezultat contabil** — majoritatea + trimit la aceeasi `id_nota = 1`, deci toate dau `704` / `PTVA = 21` / lei. Cu atat mai mult, criteriul de + departajare corect e „care lista de preturi accept sa murdaresc", iar raspunsul e „niciuna dintre cele + existente". +- **Politica tehnica devine vizibila utilizatorului** in `caut_politici_curente_util()` — aceeasi functie + ca la cautarea normala. Ori se filtreaza explicit (flag / prefix dedicat), ori se accepta riscul ca + cineva sa o aleaga din greseala pe o factura normala. **De decis in S4g.** +- **`id_pol` devine populat** acolo unde azi ar fi gol. Nu s-a gasit cod care sa presupuna „`id_pol` gol = + articol fara politica", dar nici nu s-a cautat exhaustiv. +- **Doua politici active nu au `ID_NOTA`** (`32 HOTEL TAXE`, `33 HOTEL CAZARE`) — `id_pol` valid, nota + absenta. Ce face `contabilizeaza_articol` in acest caz e intrebare deschisa, trimisa pe cod. E o gaura + care **exista deja azi**, independenta de #13. + +**RASPUNS LA DECIZIA 32 — reteta RAMANE. `docs\cercetare\idpol_comanda_contract.md`.** + +Intrebarea era: cum alege programul nota contabila la factura din comanda, care „nu are politica de pret"? +**Raspuns: are. Structural nu poate sa nu aiba.** `COMENZI_ELEMENTE.ID_POL` e **`NOT NULL`** la nivel de +schema (constrangerea `SYS_C0015376`, plus `FK_COMENZI_ELEMENTE_002`) — verificat direct: **0 din 7108 +randuri** au `ID_POL` nul sau zero, 9 politici distincte in uz, toate cu nota atasata. Deci nu exista nicio +ruta alternativa catre nota, si **nu exista contradictie cu concluzia rundei 8**: pe comanda cazul „`id_pol` +gol" e imposibil, nu doar neintalnit. Ce percepe operatorul ca „n-am ales nicio politica" e faptul ca +politica se alege **o data pe comanda** — manual sau moștenita dintr-o optiune legata de tipul comenzii — +si de atunci fiecare articol adaugat o primeste automat, din `com_vpreturi_utilizator`, view care filtreaza +`id_pol IS NOT NULL` la sursa. Pe **contract** tiparul e acelasi, dar cheia stocata e +`CTR_ARTICOLE.ID_POL_ART` — FK direct la randul din `CRM_POLITICI_PRET_ART`, **nullable**, cu cazuri reale. + +**De ce `FACT-024` nu se declanseaza pe comanda / contract:** nu pentru ca ar exista un fallback, ci pentru +ca **articolul nu e ales niciodata din nomenclator** — e ales dintr-o lista deja filtrata pe politica, deci +apartenenta e garantata **prin construcția listei**, nu prin validare. Exact aici e diferenta cu #13, unde +`caut_articol` ofera nomenclatorul intreg. + +**Deci refolosirea nu simplifica nimic:** mecanismul de jos — RPC de inserare + apartenenta garantata — e +**exact** ce propune reteta in 4 pasi. Ce ar simplifica cu adevarat („ia politica curenta fixa si pune +articolul in ea") e **decizia 24, retrasa**, si motivul retragerii sta: o politica unica da **un singur** +cont de venit oricarui articol, indiferent de natura lui — opusul deciziei 27. Comanda si contractul nu +raspund niciodata la intrebarea „**care** politica pentru **acest** articol", pentru ca la ele politica vine +gata aleasa, o data, de operator. + +**CORECTIE, a doua tura a aceluiasi agent — premisa lui Marius ERA corecta, dar pe contract, nu pe comanda.** +Exista o ruta reala catre nota contabila **complet in afara lui `id_pol`**: la **contractele cu rate / +scadentar** (`OPT_FACTURARE IN (1, 2)`), o functie separata — **`contabilizeaza_rata`** — scrie nota direct +din **`CONTRACTE.ID_NOTA`**, fara sa treaca prin `CRM_POLITICI_PRETURI`. Confirmat pe cod de agent si pe o +factura reala (`id_vanzare 360`: `SCD = 4111` / `SCC = 704` in `ACT`, pe o linie fara articol si fara +politica). **Structura confirmata independent de sesiunea principala:** `CONTRACTE.ID_NOTA` exista, e +nullable, si e **populat pe 19 din 242 de contracte**, rezolvand la note reale — `1 NOTA 1` → `SCC 704` +(5 contracte), `2 DISCOUNT` → `SCC 4111` (5), `5 VANZARE MARFA` → `SCC 707` (1). Deci nota **pe antetul +documentului** nu e o ipoteza, e un mecanism in uz. + +**Ce inseamna asta pentru #13, si de ce e o decizie de produs, nu una tehnica.** Tiparul „nota pe antet" +ar fi **mai simplu decat reteta in 4 pasi**: n-ar mai fi nevoie nici de gasirea politicii, nici de +inserarea articolului in ea, nici de politica tehnica — antetul ar purta nota, iar liniile ad-hoc ar +moșteni-o. **Dar cere cod nou in `pack_facturare`**, pentru ca `contabilizeaza_rata` trateaza rate, nu +articole de factura. Adica **incalca exact decizia 27-bis** („`pack_facturare` nu se atinge"). +**Deci alegerea e a lui Marius, intre trei variante:** + +1. **reteta in 4 pasi** (decizia 27-bis respectata, `pack_facturare` neatins, dar cere politica tehnica per + `SCC` si o interogare inversa noua); +2. **nota pe antet ca la `contabilizeaza_rata`** (mult mai simplu si mai curat conceptual, dar **relaxeaza + decizia 27-bis** — cod nou in pachetul comun intregii suite); +3. **nota pe antet doar ca fallback** pentru articolele fara politica, lasand restul neschimbat. + +**Nu se implementeaza nimic pana la aceasta alegere.** Detalii: `idpol_comanda_contract.md`, sectiunea E-bis. + +**Doua observatii din date, gasite la confirmarea verdictului:** + +- **Precedentul cerut de decizia 27 exista deja in producție, si e mai bun decat `gnId_pol_pret_stoc`.** + Politica `7 DISCOUNT` apare pe **870 de linii de comanda** si trimite la nota `2 DISCOUNT` + (`SCD = 667`, `SCC = 4111`). E o politica de pret folosita **exclusiv ca mecanism de rutare contabila**, + nu ca lista comerciala de preturi. Deci „politica tehnica, dedicata unui cont, populata automat" nu e o + invenție a lui #13 — **produsul o face deja**, pentru discount. Argument direct pentru politica tehnica + per `SCC`. +- **Garantia prin construcția listei nu e etanșă in date: 37 de linii de comanda au un articol care NU e + membru al politicii de pe linie.** **VERIFICAT (runda 10)** — + `docs\cercetare\linii_comanda_articol_nemembru.md`. **Decizia 32 NU se redeschide in sensul periculos: + nu exista cale de cod care sa ocoleasca `FACT-024`.** Patru rezultate: + - **Cifra 37 se reconfirma, dar numitorul din plan era gresit** — `6868` era alt numitor; cel corect + pentru linii cu `ID_POL NOT NULL` e **7108**. + - **Premisa „37 de linii ar cadea la facturare" era partial gresita.** Un rand cu `CANTITATE < 0` **nu + dovedeste ca linia a fost facturata**: singurul loc care il insereaza e `inchide_comanda` + (`PACK_FACTURARE:5769-5820`), care scrie *diferenta ramasa* la **inchiderea** comenzii, chiar si cand + nimic nu s-a facturat pe acea linie. **34 din 35 de linii negative n-au nicio factura in spate, nici + macar stearsa** — comenzile au fost inchise, nu facturate. + - **Una singura chiar a ajuns intr-o factura emisa** — comanda `497`, articolul `4294507522` + („VOUCHER DISCOUNT") pe politica `7` („DISCOUNT"), in factura `ID_VANZARE = 1028`, `20.03.2026`. Si + codul **chiar a rulat** `contabilizeaza_articol` pentru ea: apelul e in ramura `ELSE` a lui + `CASE pack_facturare.ntip` din `scrie_factura2`, care exclude doar transferurile, avizele de custodie + si facturile cu rate — tipul 3 cade in `ELSE`. Deci **fie articolul era membru al politicii la + 20.03.2026 si a fost scos ulterior, fie verificarea a fost ocolita altfel; dovada pentru a doua + varianta nu exista.** + - **Cauza e NEDETERMINABILA DIN DATE, structural.** `CRM_POLITICI_PRET_ART` **n-are coloana `STERS`** si + n-are tabel de istoric — stergerile sunt **fizice, fara urma**, deci nu se poate reconstitui starea de + la 20.03.2026. Coloanele de audit ale facturii (`VANZARI.ID_UTILS` / `DATAORAS` si cele de pe cele + doua linii) sunt **toate NULL**, deci nici o editare ulterioara nu s-a inregistrat. Indiciu de tipar, + nu dovada: aceeasi pereche articol-politica apare pe **35 de comenzi diferite**, ceea ce sustine o + **curatare de catalog** mai degraba decat 35 de greseli independente de introducere. + + **Ce ramane, si cum se formuleaza:** pentru celelalte 36 de linii, „zero facturi in date" **nu + demonstreaza ca nu se pot factura** — demonstreaza doar ca nu s-a intamplat. Iar cu starea de azi a lui + `CRM_POLITICI_PRET_ART`, toate cele 37 **ar** cadea cu `FACT-024` daca ar trece acum prin + `contabilizeaza_articol` (verificat pe view-ul `VCRM_POLITICI_PRET_ART`, care n-are filtru propriu, deci + vede exact aceleasi perechi ca tabelul). **Concluzia pentru #13 e neschimbata:** garantia prin + constructia listei e reala cat timp catalogul nu se editeaza sub documente deja emise. + +**Verificarile pe baza vie sunt facute** (10.08.2026) — `docs\cercetare\verif_baza_vie_cont_venit.md`: +interogarea inversa pe fiecare `SCC` candidat, DDL-ul lui `CORESP_CONT_VENCHELT`, distributia articolelor +pe ramurile deciziei 27 (6125 din 6430 pe ramura gestionabila), efectele colaterale ale pasului 3 si trei +fragilitati de structura. **Rămâne deschisa doar partea de cod**, listata mai sus. + +### K. Discountul pe linie (decizia 14) + +Verificat de doua ori, pe formularul real si pe DDL. Raspunsul la intrebarea „e doar procentual?” e +**nu, si nici doar valoric — sunt amandoua, dar in alta fereastra**. + +**Intrarea pentru operator exista deja, in dialogul de articol.** `frm_articol_factura` +(`ofacturare.vc2:1108-2659`), deschis la fiecare adaugare de articol, are trei campuri legate: +`Clb_procent_discount` (procent), `Clb_discount_unitar` (suma, in lei sau valuta) si +`Clb_discountctva` (varianta cu TVA). Toate cheama +`frm_articol_factura.do_calculeaza_discount(valoare, tip)` (`:1874-1976`) cu `tip = 1` procent, +`tip = 2` lei, `tip = 3` valuta (`:2602-2649`) — tastezi in oricare, se recalculeaza celelalte. +Rezultatul intra in `poArticol.discount_unitar` si variantele lui, apoi in `crsfactura` prin +`Replace` (`:12956-12957`). + +**Se stocheaza o singura coloana, si e valoare.** `VANZARI_DETALII.DISCOUNT_UNITAR NUMBER(22,6)` +(`ff_2024_06_13_02_COMUN_FACTURARE.sql:3`; simetric pe `VANZARI_DETALII_TEMP` la `:9` si pe +`CRM_POLITICI_PRET_ART` la `:16` — **trei tabele diferite, nu aceeasi coloana de trei ori**). +**Nu exista coloana de procent** pe `VANZARI_DETALII` — procentul e doar mod de introducere. +Parametru Oracle: `V_DISCOUNT_UNITAR` in `adauga_articol_factura` +(`ff_2026_03_23_04_COMUN_PACK_FACTURARE.sql:4986`), scazut din pretul fara TVA inainte de TVA +(`:15794-15847`). + +**In grid, azi, comportamentul e inconsecvent.** Pe factura in lei ramane `cDiscountCTva`, care e +`ReadOnly = .T.` (`ofacturare.vc2:12311-12317`); pe factura in valuta ramane `cVdiscountftva`, care +**e editabila** — dar nu are niciun `Valid` / `InteractiveChange`, deci o editare directa in celula +schimba valoarea bruta fara sa refaca totalurile. Excluderea reciproca e la `:15269-15278`. + +**Corectie fata de runda 3:** afirmasem ca prototipul are deja procent pe linie, ca argument pentru +pornirea de la el. **Fals.** `procdisc` apare o singura data in tot fisierul, ca `ControlSource` de +coloana (`:16721`), fara niciun `Replace` care sa-l populeze si fara corespondent Oracle — e schela +moarta. (Restul aparitiilor de „procdisc” sunt variabila de optiune `gnMemProcDisc`, altceva.) +Argumentele pentru prototip raman celelalte doua: controalele de antet si butoanele deasupra gridului. + +**Ce ramane de facut:** + +- cele doua campuri din dialogul de articol devin **coloane in grid** — procent si valoare unitara — + cu **acelasi calcul reciproc** din `do_calculeaza_discount`, mutat pe evenimentele coloanelor; +- se repara astfel si inconsecventa de mai sus: aceleasi validari in ambele monede; +- se pastreaza excluderea pe `in_valuta`; +- modelul de date **nu se atinge**; +- **de verificat separat, blocant**: daca `discount_unitar` apare pe rapoartele `.frx` si daca e + transmis in eFactura (UBL) si SAF-T. Daca nu apare pe hartie, documentul tiparit nu explica totalul. + +#### K-bis. Discountul de DOCUMENT — cota, explicatia si atribuirea pe cote + +Cercetare terminata, verificata la sursa (doua runde, a doua a corectat concluzii ale primei — +vezi rezervele de mai jos). Detaliu complet: `docs\cercetare\discount_document_cota_tva.md`. + +**Cota discountului de document = cota MAXIMA de pe factura, ca regula implicita.** Nimeni n-o +alege: nu exista control in formular si nicio coloana pe `VANZARI` pentru ea. +`Calculate Max(proc_tvav)` — `COMUN\programe\oproceduri_facturare.prg:1387`, +`COMUN\clase\ofacturare_comun.vc2:4495` si `:7294`, `COMUN\programe\ofacturare_stoc.prg:582`. +Discountul intra ca **pseudo-linie negativa** printr-un rand-sentinela `Replicate('Z',20)` +(`COMUN\programe\ofacturare_comun.prg:1886-1897`), care devine linia „Discount NN.NN % Factura" +(`:1273-1392`). + +**Explicatia nu exista ca notiune — e o constanta.** In XML, `AllowanceChargeReason="Discount"` +si `ReasonCode="95"` sunt hardcodate (`COMUN\programe\xmlefactura.prg:774-776`), la fel +`currencyID="RON"` (`:778`). Textul construit in VFP („Discount 10.00 % Factura") nu ajunge in +XML. + +**Regula e implementata de doua ori, independent — nu intr-un singur loc.** In VFP (mai sus) si +in PL/SQL: `PACK_FACTURARE.recalculeaza_totaluri_vanzari`, +`MAX(ROUND(discount*(a1.proc_tvav-1),...)) as DISC_TVA_RON` — +`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:16081-16092`, +scris in `VANZARI.DISCOUNT_TVA` si de acolo in `TOTAL_TVA` / `TOTAL_CU_TVA` (`:16214-16227`). +**Pentru #13: doua locuri de schimbat, nu unul** — daca se schimba doar unul, `VANZARI.TOTAL_TVA` +si TVA-ul din XML diverg pe facturile cu cote mixte. + +**Defectul dovedit e de atribuire fiscala, nu de validare.** Pe cote mixte, tot TVA-ul +discountului se scade din cota maxima. Exemplu: 1000 lei la 21% + 1000 lei la 11%, discount de +document 10% (200 lei) → se declara 10 lei TVA in minus, mereu in acelasi sens (TVA colectat +subdeclarat). Aceeasi valoare gresita ajunge si in nota contabila si in jurnalul de TVA. Caz-limita +real, masurat: daca liniile de la cota maxima sunt o mica parte din total, `TaxSubtotal` iese cu +baza negativa (`TaxableAmount = -410.00`). + +**Ipoteza bazei negative — verdict nuantat, asa cum a iesit din verificare, nu simplificat.** +Temerea: pseudo-linia, avand `id_jtva_coloana = 0`, iese din `LEFT JOIN` cu `coloana_jv` NULL si +formeaza grup propriu in `C_TVA_FACTURA`. **Infirmata pe factura obisnuita** — masurat headless, +`IIF()` din VFP intoarce ramura falsa, nu `.NULL.`, cand conditia e nula, deci pseudo-linia se +contopeste corect. **Confirmata insa pe facturile scutite / taxare inversa / intracomunitare**, +unde liniile reale au `scutit = 1` si `expltva` completat iar pseudo-linia are `0` si gol: apar +doua grupuri, deci un `TaxSubtotal` in plus cu baza negativa si categoria `Z`, iar +`agettipcota(1)` (`xmlefactura.prg:782-786`) devine ambiguu intre `E` si `Z`. + +**Nu blocheaza factura — validat, nu presupus.** Validat offline cu DUKIntegrator, 6 scenarii, +inclusiv un control negativ care chiar iese cu erori (`BR-CO-13` + `BR-CO-15`) — deci cele „ok" +inseamna ceva. Trece inclusiv un caz cu `TaxableAmount = -400.00`. Regulile pe categorii +(`BR-S-08` / `BR-E-08` / `BR-Z-08`) se satisfac trivial: aritmetica inchide, modelarea fiscala e +cea gresita, si asta niciun schematron nu prinde. **Rezerva de scris explicit:** validatorul local +e din 2022; validatorul ANAF online n-a fost apelat (interdictie). + +**`currencyID="RON"` hardcodat e neconformitate reala si activa, dar fara dovada ca ar cauza +respingere.** Dovedit pe fisier: XML-ul in EUR de la VENDING_MASTER e identic pe SHA256 cu cel din +`TRIMISE` (deci exact ce a plecat la ANAF) si a fost acceptat. Respingerea din `ERORI` e a unei +incarcari anterioare, pentru alte reguli. + +**Nu s-a putut proba pe date reale, si asta ramane netransat.** In baza accesibila sunt 3 facturi +cu discount de document (toate cu o singura cota, anterioare eFacturii) si 34 cu cote mixte fara +discount — intersectia e goala. Cele 4 XML-uri de productie cu alocare de document sunt, dupa trei +indicii independente, discounturi pe linie, nu de document. Absenta din date nu e dovada (regula +casei). Ramane nedovedit prin rulare: reproducerea end-to-end prin program a scenariului „factura +scutita + discount de document". + +**Recomandarea cercetarii pentru #13:** repartizare proportionala automata pe cote, **nu se cere +utilizatorului cota**. Discountul de document nu are o cota proprie de ales — natura lui de TVA e +determinata de liniile pe care le reduce; a pune operatorul sa aleaga inseamna a-i cere o decizie +fiscala pe care datele o dau deja. Pentru explicatie: un singur camp text optional pe document, +folosit ca `AllowanceChargeReason` in locul constantei (`ReasonCode` ramane `95`). + +**Doua completari obligatorii daca se implementeaza, altfel reparatia e degeaba:** +- bucatile de discount trebuie sa primeasca **`id_jtva_coloana` al grupului pe care il reduc**, nu + doar cota — cheia de grupare are cinci campuri si `expltva` se completeaza tot prin + `id_jtva_coloana`. O implementare care seteaza doar `proc_tva` lasa grupul orfan exact unde e azi; +- **eFactura nu are nevoie de nicio modificare** — `xmlefactura.prg:758-792` grupeaza deja + `GROUP BY proc_tva` si emite cate un `AllowanceCharge` per cota. + +Doua consecinte vizibile daca se implementeaza: factura tiparita va arata N randuri „Discount X % +Factura" in loc de unul, si nota contabila primeste TVA-ul discountului spart pe cote. + +**Decizia a fost luata — decizia 59 (runda 16): repartizare proportionala pe cote.** Vezi sectiunea +„Decizia 59" de la deciziile rundei 16, cu toate consecintele de implementare. + +**Cele doua puncte ramase s-au inchis si ele, tot in runda 16.** Campul text optional de motiv — +**decizia 61**: da, se adauga (`AllowanceChargeReason`, `ReasonCode` ramane `95`), cu stocare noua +pe `VANZARI` si un control nou in formular. Asezarea lui s-a inchis in runda 17 — **decizia 66**: sta in banda de totaluri, langa discount. Retroactivi- +tatea la relistare/retrimitere — **decizia 62**: restrictia de modificare e strict `EsteInEFactura`, +fara legatura cu nicio data; ramane insa deschis, separat, cazul relistarii (nu editarii) unei +facturi vechi deja trimise, vezi decizia 62. + +### M. Valuta si data cursului (decizia 15) + +**Cele doua concepte exista in cod, distinct** — decizia 15 nu introduce o distinctie noua, o face +vizibila: + +- **(a) factura in valuta**: `poDate.in_valuta` / `Curs` / `multiplicator` / `id_valuta`, un singur + curs pentru tot documentul; +- **(b) articol cu pret in valuta pe document in lei**: `poArticol.tip_valuta = 1`, proprietate a + politicii de pret, independenta de `in_valuta`. Conversia se face **in VFP**, cu cursul zilei: + `poArticol.pretftva = Round(poArticol.pretftva_val * poArticol.Curs / poArticol.multiplicator, gnPPretV)` + (`ofacturare.vc2:1989`, `:2017`, `:2953`). Cursurile zilei se incarca exact pe ramura „document in + lei”: `If poDate.in_valuta = 1 ... Else citeste_cursuri_zi(poDate.zi_curs)` (`ofacturare.prg:407-418`). + +**In baza, exact cum ai descris:** `VANZARI_DETALII` **nu are coloana `CURS`**; pastreaza `PRET` (in +lei, valoarea de facturare) plus `PRETD` si `ID_VALUTAD` ca urma a pretului original in valuta. +Cursurile ajung in `VANZARI_CURSURI`, scrise neconditionat la emitere pentru **orice** valuta straina +aparuta pe linii — deci si pe o factura in lei +(`PACK_FACTURARE.sql:14491-14501`, `scrie_cursuri`). + +**`in_valuta` nu e o alegere, e o proprietate a tipului de document.** Se seteaza o singura data, in +`Init`: `If Inlist(tnIdSet,223,225,226,227,271) OR INLIST(m.tnTip, 5,6,7,9,10,52) -> .in_valuta = 1` +(`ofacturare_comun.prg:248-250`), si nu se mai schimba. Selectorul de valuta e chiar eliminat din +formular cand `in_valuta = 0` (`ofacturare.vc2:9725-9728`), deci operatorul nici nu-l poate alege. + +**Problema semnalata, confirmata:** `Clb_zi_curs` e eliminat **doar** pe retur (tip 8, 9), la +`ofacturare.vc2:9717-9722` — si niciodata in functie de valuta, desi blocul imediat urmator, +`:9725-9728`, face exact asta pentru selectorul de valuta. Deci **azi campul apare si pe facturi in +lei**, unde de multe ori nu are ce cauta. Validarea e la randul ei asimetrica: `frm_date_factura` +cere ziua cursului doar cand `in_valuta = 1` (`:9484`), dar `frm_date_aviz_lucrare` o cere +**neconditionat** (`:8076`). + +**Regula propusa:** campul apare cand tipul nu e retur **si** (documentul e in valuta **sau** exista +pe document macar un articol cu pret in valuta). + +**Unificarea face regula posibila.** Azi cazul (b) nu se poate sti la deschiderea antetului — se +afla abia dupa incarcarea articolelor, cand antetul e deja inchis si eliberat (`Release +ofrmceredate`, `ofacturare.prg:248`). In formularul unificat antetul si gridul sunt in aceeasi +fereastra, deci campul poate aparea in clipa in care intra pe grid primul articol cu pret in valuta. +**Din acelasi motiv dispare si mecanismul lui #16**: bug-ul de focus si de renumerotare apare la +revenirea din `frm_curs` intr-un antet care tocmai s-a inchis — in formularul unificat nu mai exista +un antet inchis la care sa te intorci. `frm_curs` e in `COMUN\clase\onom_curs.vc2:537`, deschis din +`vizualizeaza_curs` (`oproceduri_curs.prg:8-42`) ca recuperare la eroarea Oracle 20005 +(`ofacturare.prg:313-317`). + +**Capcana:** `poDate.zi_curs` se trimite **neconditionat** ca prim parametru catre `cursor_preturi` / +`cursor_articole_k` / `cursor_gestiune` / `cursor_lucrare` (`ofacturare.prg:272-303`), inclusiv pe +tip 1. Ascunderea campului nu inseamna golirea valorii — valoarea implicita (data documentului) +trebuie sa ramana. Iar pe avizul de lucrare, ascunderea fara repararea validarii de la `:8076` ar +bloca finalizarea antetului cerand un camp care nu mai e pe ecran. + +### N. Returul din facturi anterioare (decizia 17) + +Rapoarte: `docs\cercetare\factura_retur_document.md` (mecanismul principal) si +`retur_si_lista_preturi.md`, A (al doilea mecanism). + +**Sunt doua fluxuri de cod independente**, care ating aceeasi clasa de formular prin metode si +proceduri Oracle diferite. Nu au niciun punct comun in afara conventiei de semn a cantitatii. + +#### N.1 Factura de retur ca document (tipurile 8, 9 — si avizul de retur, 24) + +**Asta e mecanismul pe care il descrie Marius, si functioneaza deja cap-coada.** + +- **Intrare:** tile-ul de pe ecranul de facturare (`ofundal_facturare.vc2:882-884`) -> `politica.mpr` + -> `factureaza(8)` / `factureaza(9)` (`Meniuri\politica.mn2:14-15, 45-46`). +- **Alegerea facturilor, la nivel de document:** `frm_date_factura.do_cauta_facturi` + (`ofacturare.vc2:9173-9212`) cheama `caut_facturi_multiple_client` + (`oproceduri_facturare.prg:2091-2121`) — browse cu **selectie multipla**, titlu „Alegeti facturile + (mouse-click pe numar sau apasati SPACE)”. Filtrare pe **client** (obligatoriu ales inainte) si + **valuta**; sursele exclud tipurile de retur, deci nu se face retur dintr-un retur. Id-urile alese + se aduna intr-un CSV in `poDate.listaid`, numerele in `poDate.descriere`, iar antetul primeste + automat textul „RETUR FACTURA …”. Fara alegere nu se poate continua (`:9523-9526`). +- **Popularea liniilor:** `pack_facturare.cursor_retur` -> `cursor_retur_document` + (`ff_2026_08_06_10_COMUN_PACK_FACTURARE.sql:3943-3956`, `:3958-4071`) selecteaza direct din + `VANZARI_DETALII` liniile facturilor alese si le pune in acelasi `crsarticole` pe care celelalte + tipuri il umplu cu lista de preturi (`ofacturare.prg:306-311`). **Confirmat exact ce spunea Marius:** + `ID_GESTIUNE` vine din linia originala (`:4034`, `:4055`), `PRET_ACHIZITIE` la fel, **neschimbat** + (`:4035`, `:4056`), iar pretul de vanzare se recalculeaza pe curs doar daca moneda nu e nationala + (`:4016-4030`). `GESTIONABIL` e `NVL2(A1.ID_GESTIUNE,1,0)` — gestionabil doar daca originalul avea + gestiune. +- **Ce se poate face manual:** **stergerea liniilor aduse merge** si cantitatea redevine disponibila + in cursorul sursa (`do_sterge`, `ofacturare.vc2:14608-14693`, ramura de retur la `:14658-14659`); + **returul partial merge** (`do_verifica_articol`, `:14743-14754`, fata de maximul calculat pe + server). +- **Avizul de retur (24)** foloseste acelasi `cursor_retur` si aceleasi ramuri; difera doar antetul — + `nIdTipDoc = 6` si `frm_date_aviz` in loc de `frm_date_factura` (`ofacturare.prg:194-195`, `:225`). + +**Singurul gol real, si e acelasi ca la comanda:** pe un document de retur **nu se poate adauga o +linie din afara facturilor sursa**, pentru ca `crsarticole` **este** rezultatul lui `cursor_retur` +(`do_adauga_articol` ia articolul mereu din el, `ofacturare.vc2:12843-12851`). Nu e o interdictie +explicita, e continutul cursorului — exact cauza de la comanda (J). Se rezolva la fel, prin +`APPEND FROM`, si abia asta face reala decizia 16 pe documentele de retur. + +#### N.2 Retur de articole intr-o factura de vanzare normala (`But_retur`) + +Butonul (`cmd_butoane.vc2:324-338`, `caction = do_retur`) e vizibil **doar** pe tipurile 1, 5, 7, 10 +(`ofacturare.vc2:15122-15127`) — pe un document de retur nu exista deloc. Aici factura sursa se alege +**per articol**, la fiecare linie: `caut_facturi_multiple_client_articol` +(`oproceduri_facturare.prg:2124-2159`), filtrat suplimentar pe articolul curent. Perechile +`ID_ARTICOL:ID_VANZARE` se acumuleaza in `thisform.cListaIdArticoleRetur` (`:12888`) si pleaca ca +`poDate.listaid` (`:13977-13979`). Gestiunea se alege prin +`pack_facturare.cursor_gestiuni_articol_retur`, nu vine din factura sursa — **diferenta de fond fata +de N.1**. + +**Legatura linie-de-retur -> linie originala: neverificat** pe schema. In VFP nu exista coloana de +linie sursa; la nivel de antet exista `poDate.nid_vanzare_retur` (`ofacturare.vc2:14448`, `:14509`). +Conteaza doar pentru **afisare** — daca formularul poate arata din ce factura vine fiecare linie. + +#### N.3 Ce se schimba in formularul unificat + +- **N.1 se muta ca atare.** Alegerea facturilor devine o optiune in meniul butonului de adaugare + („Alege facturile de returnat…”), cu acelasi dialog, aceleasi filtre si aceeasi populare din + `cursor_retur`. **Nu se reproiecteaza nimic din ce merge**, si in special nu se atinge preluarea + gestiunii si a pretului de achizitie din facturile originale. +- **N.2 capata si alegerea la nivel de document**, dupa modelul lui N.1, pastrand calea per-articol + pentru cazul cu un singur articol. +- **Lista de preturi devine disponibila si pe documentele de retur** (decizia 16), prin acelasi + `APPEND FROM` ca la comanda. Consecinta de proiectat: pe acelasi document vor coexista linii + aduse din facturi sursa (cu gestiune si pret de achizitie mostenite) si linii libere, care nu au + factura sursa. + **Decizia 22 (Marius, 09.08.2026): linia de retur fara factura originala e permisa, ca pe orice alt + document.** Returul nu face exceptie de la regula deciziei 16 — sursa umple documentul, nu il + inchide, si nici nu apare doar „pentru corectii”. Consecintele de proiectat, acum ferme: + - **gestiunea si pretul de achizitie nu se pot mosteni** pe o linie libera, pentru ca nu exista linie + originala. Se aleg, ca la N.2 (`cursor_gestiuni_articol_retur`), nu se preiau ca la N.1; + - **maximul returnabil calculat pe server nu se aplica** unei linii fara factura sursa — nu exista + cantitate originala fata de care sa se limiteze; + - afisarea „din ce factura vine linia” trebuie sa suporte si valoarea goala (vezi mai sus, legatura + linie-de-retur -> linie originala, inca neverificata pe schema). + +*De pastrat cum e:* excluderea returului dintr-un retur, filtrarea pe client si valuta, maximul +returnabil calculat pe server, si mostenirea gestiunii si a pretului de achizitie. Nu se ating. + +### O. Facturile din ROAAUTO (decizia 18) + +Raport: `docs\cercetare\roaauto_facturi.md`. + +> **Precizare la descrierea liniilor de mai jos.** „Liniile unei facturi ROAAUTO nu sunt articole” e +> adevarat doar pentru liniile **generate din deviz**. Langa ele pot sta articole reale, adaugate +> prin mecanismul **„Alte servicii”** — vezi O-bis, verificat pe cod +> (`docs\cercetare\roaauto_articole_lista_preturi.md`). Restul sectiunii ramane valabil: tipul `-12`, +> drumul comun prin `PACK_FACTURARE`, faptul ca editorul lui #6 le vede, si blocarea modificarii +> dupa facturare — ultima **reconfirmata**. + +**Ce se confirma.** ROAAUTO nu are cale proprie spre baza: `factureaza_deviz` +(`ROAAUTO\Programe\oproceduri_devize.prg:846`) cheama acelasi `PACK_FACTURARE` partajat — +`initializeaza_date_factura` (`:1211`), `adauga_articol_factura_deviz` (`:1240`), `oscrie_in_fisiere` +(`:1269`), `scrie_in_vanzari` (`:1302`) — deci liniile trec prin acelasi `VANZARI_DETALII_TEMP` si +ajung in acelasi `VANZARI_DETALII` ca orice factura ROA. Tipul documentului e **`-12`** +(`:922`). Editorul lui #6 le vede deja, fara cod de recunoastere a sursei, verificat pe date reale +(`id_vanzare = 1047`). **Deci partea de „le vede” e gratuita.** + +**Ce nu se confirma — si asta schimba dimensiunea deciziei.** „Se pot modifica ulterior” **nu e +adevarat azi, in niciun produs**: + +- in ROAAUTO, formularul de devize isi dezactiveaza butonul de modificare de indata ce comanda are + numar de factura (`oviz_devize.vc2:4536`); singura actiune post-facturare gasita e re-listarea + (`oproceduri_devize.prg:1516`), care nu scrie nimic. Niciun apel din ROAAUTO catre + `modifica_date_factura`; +- in ROAFACTURARE, gridul de articole din editorul nou (`frm_modific2024`, `COMUN\clase\omodificari.vc2`) + e **read-only prin design**, la nivel de grid si pe fiecare `Text1`. + +Deci decizia 18 nu extinde o cale existenta, **creeaza o capacitate care nu exista nicaieri**. + +**Cum arata liniile.** `factureaza_deviz` agrega sumele devizului si insereaza cate o linie sintetica +per categorie, cu `id_articol` **negativ**: `-100000` MANOPERA (`:965-967`), `-100003` MATERIALE +(`:947-949`), `-100001` discount manopera, `-100005` / `-100006` avans si stornare avans, +`-100007` / `-100008` inspectie tehnica si spalare (`:936-1027`). Optional, cu +`gnAUTOIdArticolReparatii` setat, toate se cumuleaza intr-o singura linie cu un articol real +(`:989-1004`). Langa ele pot sta **articole reale**, prin „Alte servicii” — vezi O-bis. + +### O-bis. „Alte servicii” — cum se adauga azi articole reale in ROAAUTO + +Raport: `docs\cercetare\roaauto_articole_lista_preturi.md`. **Asta e mecanismul de refolosit.** + +- **Unde:** formularul `frm_incasare_finala` (`ROAAUTO\Clase\oviz_devize.vc2:5435-7143`), aratat **la + emiterea facturii finale** (`do_factureaza_final`, `:2569`) — nu pe devizul propriu-zis. +- **Sursa articolelor:** `cauta_nom_articole` pe `vnom_articole_toate` + (`COMUN\programe\ocautare.prg:1781-1823`), filtrat `in_stoc = 0 and in_crm = 1` si cu id-urile + sintetice excluse (`oviz_devize.vc2:6539-6580`). **Nomenclatorul brut, si numai articole fara + stoc** — nu lista de preturi, nu politici. +- **Pretul se tasteaza manual.** `do_adauga` nu completeaza pretul si cantitatea; operatorul le pune + in grid, iar `do_modifica_alteserv` recalculeaza valoarea (`:6692-6698`, `:7064-7070`). Nu exista + preluare automata de pret pe calea asta. +- **Liniile raman separate.** Cumularea in „REPARATII AUTO” include doar id-urile sintetice + (`oproceduri_devize.prg:999-1012`); randurile din `crsalteserv` se insereaza dupa, cu `id_articol` + real, si ajung ca atare in `VANZARI_DETALII_TEMP` (`:1036-1041`, `:1240-1257`). +- **Gestiune: tot zero.** `id_gestiune` nu e completat de niciun `INSERT` si pleaca `0` spre Oracle + (`:1201-1206`, `:1255`) — consistent cu filtrul `in_stoc = 0`. Deci **nici articolele reale din + ROAAUTO nu descarca gestiune**. Diferenta fata de liniile sintetice e doar `id_articol`. +- **Stergere si modificare:** exista, dar **doar cat timp formularul e deschis, inainte de emitere** + (`do_sterge`, `:6730-6741`). +- **Contul pleaca gol.** `crsvanztemp` are coloana `Cont c(4)`, dar `INSERT`-ul care il umple nu o + include (`oproceduri_devize.prg:1190-1206`); apelul trimite literal `''` (`:1256`), care ajunge + `NULL` in `VANZARI_DETALII_TEMP.CONT`, fara `NVL` si fara fallback. Deci **liniile de articole reale + din ROAAUTO se scriu azi fara cont** de gestiune, tacut. Vezi J-bis. +- **Si fara politica de pret — dar pe alta cale decat credeam.** `id_pol` lipseste peste tot pe firul + asta (nu e nici in `crsalteserv`, nici in `crsvanztemp`, nici in semnatura lui + `adauga_articol_factura_deviz`). **Motivul pentru care asta nu produce `FACT-024`** e ca fluxul + ROAAUTO **nu trece prin `contabilizeaza_articol`**: merge pe `scrie_in_vanzari`, care nu genereaza + nicio nota de venit. Vezi **J-ter**. + +> **Corectie importanta la temeiul deciziei 18.** Ideea ca „«Alte servicii» face deja jumatate din ce +> cerem, deci se generalizeaza” **nu se sustine**. Mecanismul acela functioneaza tocmai pentru ca +> ocoleste contabilizarea de venit; generalizat pe fluxul ROAFACTURARE, unde `contabilizeaza_articol` +> **este** apelata (`scrie_factura2`, `ff_...:6150`), s-ar lovi de `FACT-024`. Ce ramane valabil din +> O-bis e descrierea UI (articole reale langa linii sintetice, pret tastat, fara gestiune) — nu si +> concluzia ca partea grea e deja rezolvata. + +**Ce inseamna asta pentru decizia 18.** Doua lucruri, in directii opuse: + +- **usureaza** partea de model: un articol real langa linii sintetice **nu e o noutate**, e deja + cazul normal al facturilor auto. Intrebarea „cum coexista” are deja un raspuns in produs; +- **nu rezolva** cerinta: mecanismul traieste in formularul de emitere al ROAAUTO si moare odata cu + el. Ce cere Marius e aceeasi capacitate **la modificare**, care nu exista nici acolo, nici aici. + +**Intrebarile ramase, in ordinea in care blocheaza:** + +1. ~~**Ce se intampla cu totalul devizului**~~ — **RASPUNS (runda 7)**, raport + `docs\cercetare\pack_auto_actualizeaza_deviz.md`. Premisa intrebarii era gresita: **nu exista niciun + `SUM` peste `VANZARI_DETALII`, pentru ca `PACK_AUTO` nu citeste deloc `VANZARI` / + `VANZARI_DETALII`** — zero potriviri pe `VANZARI` in tot pachetul (1804 linii). + `actualizeaza_deviz` (`ff_2026_03_31_02_AUTO_PACK_AUTO.sql:692-733`) face trei lucruri, toate pe + structura proprie ROAAUTO: scrie cota TVA pe `DEV_ORDL`, stampileaza `ID_FACT` pe `RUL`, si — doar + pe anumite `id_set` — pe `NOM_LUCRARI`. E o legatura **deviz -> document**, nu un recalcul + **document -> total deviz**. Nu e nici macar specifica facturarii: aceleasi trei apeluri o + folosesc si la inchiderea de productie si de regie, care scriu note contabile, nu facturi. + **Deci o linie noua pe `tip = -12` nu poate dezechilibra nimic pe partea Oracle**, si nu e nevoie + de niciun apel suplimentar catre ROAAUTO. Precedentul „Alte servicii” confirma pe date live: + liniile reale coexista de mult cu cele sintetice, fara ca `actualizeaza_deviz` sa faca distinctie. + **Ce ramane, si e de alt fel:** o **desincronizare de afisare**. Totalul devizului aratat in + ROAAUTO se calculeaza din `RUL` si din cursoarele de facturare (`oviz_devize.vc2:7816-7823`), + **niciodata** din `VANZARI_DETALII` — deci nu va arata linia adaugata, nici azi, nici dupa #13. + In schimb **relistarea o va arata**: `relisteaza_factura_deviz` citeste `fact_vfacturi_detalii` + (`oproceduri_devize.prg:1564`), un view neconditionat peste `VANZARI_DETALII` + (`ff_2024_06_04_01_COMUN_FACTURARE.sql:8-94`). Factura retiparita si ecranul de deviz vor spune + lucruri diferite — **DECIS (decizia 28): e acceptabil.** Cerinta lui Marius e ca **MANOPERA si + MATERIALE sa fie conform devizului**; restul articolelor pot exista pe factura fara sa apara in + ecranul de deviz. Nu se cere cod nou in ROAAUTO si nu se mai pune intrebarea. +2. **Gestiunea, pentru articole cu stoc. DECIS (decizia 20):** se admit si articolele gestionabile. + ROAAUTO ocoleste subiectul filtrand `in_stoc = 0`; filtrul acela **nu se preia**. Deci pe + documentele auto vor coexista linii care descarca stoc cu linii care nu descarca — caz nou, de + proiectat, nu de evitat. +3. **Cine ramane proprietarul documentului** dupa ce ROAFACTURARE ii adauga o linie — mai poate + ROAAUTO sa-l relisteze corect (`relisteaza_factura_deviz` citeste din `fact_vfacturi`)? + +**Reconfirmat:** dupa emitere nu exista cale de intoarcere in ROAAUTO. `verifica_stornare` +(`oviz_devize.vc2:4513-4514`) e o metoda **goala**; `do_storneaza_avans` priveste doar avansul. +Singurul instrument gasit asupra unei facturi emise e stergerea totala (soft-delete) prin ecranul +generic din COMUN — nu o editare. + +**Secventierea — reevaluata dupa raport (runda 7).** Marius a cerut ca `pack_auto` sa fie cercetat +inainte de orice estimare (decizia 23), si a avut dreptate: raspunsul **rastoarna recomandarea +initiala**. Motivul pentru care decizia 18 urma sa se livreze ultima era riscul tehnic de a scrie pe +un produs pe care #13 nu-l controleaza. **Acel risc nu exista** — `PACK_AUTO` nu citeste `VANZARI` / +`VANZARI_DETALII` deloc, deci nu are ce sa se dezechilibreze (vezi intrebarea 1 de mai sus). + +Ce ramane nu mai e risc tehnic, ci **o singura intrebare de produs**: +1. ~~e acceptabil ca ecranul de deviz sa nu arate linia adaugata din #13~~ — **RASPUNS, decizia 28: + da, e acceptabil.** Conditia e alta: MANOPERA si MATERIALE conform devizului. +2. cine ramane proprietarul documentului (intrebarea 3 de mai jos) — nu s-a schimbat. + +**Decizia 18 nu mai trebuie sa fie ultima din motive tehnice.** Ordinea de lucru din S4g ramane insa +valabila din alt motiv, mai bun: se face intai pe documentele ROAFACTURARE, unde scrierea e a +noastra, pentru ca acolo se aseaza mecanismul; `tip = -12` vine dupa, ca **aplicare**, nu ca risc. + +**Perimetru:** gridul read-only din `frm_modific2024` si `ofacturare_editare.prg` sunt ale lui **#6**. +Deschiderea lui la scriere nu se face din #13 fara intelegere explicita — un singur scriitor pe fisier. + +### L. Observatii colaterale, gasite in timpul verificarii + +Nu fac parte din #13 si nu se repara aici — se semnaleaza ca sa nu se piarda. + +0-ter. **(runda 9) O nota de vanzari cu set multi-rand inregistreaza venitul si descarca gestiunea de N + ori.** In `pack_facturare.contabilizeaza_articol`, `cursor_articol` face `LEFT JOIN NOTE_CONTABILE D ON + C.ID_SET = D.ID_SET` **fara `ROWNUM`, fara agregare, fara filtru** + (`ff_2026_08_09_01_...:7218-7271`), iar bucla care il consuma (`:7393-7542`) executa `scrie_nota` + **si** `descarca_gestiune` **o data pentru fiecare rand al setului, cu aceeasi cantitate si acelasi + pret intreg** — nu impartite. Nu exista nicio logica de distributie: `ORDINE` nu apare in tot pachetul. + Azi nu se manifesta pentru ca toate cele 7 note de vanzari active au exact un rand pe set — dar **30 + din cele 40 de seturi din baza au mai multe, cu maxim 30 de randuri**. Deci e o bomba armata de + configurare: cine ataseaza unei politici de pret o nota cu set multi-rand obtine **dublare de venit si + dublare de descarcare de gestiune**, silentios. **E in `PACK_FACTURARE`, adica in COMUN** — se + semnaleaza, nu se repara din #13. Conteaza direct pentru reteta din J-quater: vezi acolo. + Sursa: `docs\cercetare\s10_pret_rederivat.md`, „Completare", p. 1 + `verif_baza_vie_cont_venit.md` p. 6. +0-quater. **(runda 9) Politica de pret fara `ID_NOTA` nu are nicio plasa de siguranta.** Spre deosebire de + articolul lipsa din politica (`FACT-024`), aici nu exista cod de eroare: lantul de `LEFT JOIN` din + `cursor_articol` supravietuieste inelului lipsa, cursorul intoarce **un rand cu totul `NULL`**, iar + `scrie_nota` insereaza in `ACT_TEMP` un rand cu conturile de debit si credit **nule**. Daca `ACT_TEMP` + are `NOT NULL` pe ele, iese un `ORA-01400` generic in loc de un mesaj `FACT-0xx` — **neverificat**, + DDL-ul lui `ACT_TEMP` nu e in export. Starea exista in baza vie **azi**: politicile active `32 HOTEL + TAXE` si `33 HOTEL CAZARE` n-au `ID_NOTA`. Independent de #13. +0-bis. **(runda 7) `do_modifica` pare sa scrie `id_valuta` in loc de `id_sectie`.** In + `frm_modific2024.do_modifica` (`COMUN\clase\omodificari.vc2:13941`), ramura + `CASE m.lcControl = 'sectie'` face `replace sectie with loCauta.sectie, id_valuta with + loCauta.id_sectie`. Daca e ce pare, textul afisat al sectiei se schimba dar coloana `id_sectie` + nu — si, in plus, se strica `id_valuta`. **Necitit pe date, doar pe cod.** Fisierul e in + **perimetrul lui #6** (`omodificari.vcx` a intrat in proiect prin `97d1613`) — se semnaleaza acolo, + nu se repara de aici. Conteaza pentru #13 pentru ca sustine partial verdictul „`ID_SECTIE` e + editabil": teoretic da, practic poate nu. +0. **(runda 7) Handler-ul lui `FACT-024` se auto-saboteaza cand `id_pol` e `NULL`.** In + `pack_facturare.contabilizeaza_articol` (`ff_2026_08_06_10_COMUN_PACK_FACTURARE.sql:7286-7311`), + ramura `WHEN NO_DATA_FOUND` construieste mesajul cu inca doua `SELECT ... INTO`, dintre care al + doilea e `FROM CRM_POLITICI_PRETURI WHERE id_pol = detalii_articol.id_pol`. Cu `id_pol` `NULL`, + **si acel `SELECT` da `NO_DATA_FOUND`**, iar in handler nu exista al doilea nivel de tratare — deci + utilizatorul primeste un `ORA-01403` generic in loc de mesajul `FACT-024` formatat. Articolul + apucase sa fie rezolvat, deci informatia nu se pierde de tot, dar diagnosticul e mult mai prost. + **E in `PACK_FACTURARE`, adica in COMUN** — se semnaleaza, nu se repara din #13. Devine relevant + direct daca se alege varianta 3 din J-bis. +1. **`do_copiaza`, ramura de avize: variabila gresita.** La `ofacturare_comun.vc2:3702-3703`, ramura + `CASE INLIST(loFactura.tip, T21, T24, T26, T30, ...)` scrie `lnTip = T22` in loc de + `loFactura.tip = T22`, deci degradarea de tip **nu se aplica** pe acea ramura. **Fisierul e in + perimetrul lui #6** — nu se atinge de aici; efectul nu a fost verificat pe date. +2. **„Proforma -> factura” merge prin omisiune.** Copierea nu propaga `nIdTipDoc` pentru ca linia e + comentata (`ofacturare_comun.prg:370`), deci copia unei proforme porneste implicit pe FACTURA. + Functioneaza, dar ca efect secundar, nu ca ramura dedicata. Daca #13 atinge copierea, merita + transformat in intentie explicita. +3. **Asimetria de resetare a globalelor** — vezi „Canalul de precompletare”, mai sus. +4. **Modificarea in bloc pare sa goleasca campuri pe care nu le-ai atins.** Pe selectie multipla + (`lnNrInreg > 1`), `do_modifica` face `Scatter Name poRec Memo Blank` si reseteaza explicit ruta, + delegatul, agentul, masina, `dataora_exp`, `id_facturare`, `listare_detaliata`, `tip_saft`, + `text_aditional` si `efactura` (`ofacturare_comun.vc2:4565-4580`). Cum acesti zece parametri se + scriu **neconditionat** in `VANZARI` (I-bis), un `SCAN` peste facturile alese pare sa scrie valorile + goale pe **fiecare** dintre ele — deci schimbarea rutei pe trei facturi ar sterge delegatul si + textul aditional de pe toate trei. **De confirmat pe date inainte de a fi numit bug**: e posibil ca + fluxul sa presupuna ca operatorul completeaza tot ce vrea propagat. **Fisierul e in perimetrul lui + #6** — se semnaleaza, nu se repara de aici. Conteaza si pentru #13: formularul unificat lucreaza + pe un singur document si trebuie sa trimita antetul intreg (I-bis, consecinta 1), deci nu + mosteneste problema. + +**L.4 (runda 11). Numarul de POS nu se dezaloca la Renunt.** In `frm_alte_date`, la anulare se +dezaloca numarul de chitanta (16) si cel de bon fiscal (3), dar **nu si cel de POS (26)** — +`docs\cercetare\s3b_alte_date_analitice.md`, sectiunea 9. Bug preexistent, in productie, **nu introdus +de #13**. Conteaza pentru S3b pentru ca „`actualizeaza_tipincasare` se muta ca atare" **l-ar propaga +in formularul unificat**. Se repara in trecere sau se lasa ca azi si se preia separat — punct de decis, +vezi lista de la finalul rundei 11. + +--- + +## Stories + +Ordinea e strict pe risc: unificarea intai (livrabil de sine statator, util si fara regenerare), +regenerarea peste ea. + +## Metoda de executie (Marius, runda 14) — obligatorie, nu recomandare + +**Planul asta e un document de proiectare, nu un plan de executie. La implementare se SPARGE PE +STORIES, si fiecare story se executa ca livrare de sine statatoare.** Nu se porneste implementarea pe +tot #13 deodata, nu se acumuleaza modificari nelivrate peste mai multe povesti, si nu se trece la +povestea urmatoare cu cea precedenta netestata si nerevizuita. + +### Ce inseamna „spart pe stories" + +- **O story = o unitate de livrare.** Are perimetru propriu de fisiere, criteriu „gata cand" propriu + (fiecare story de mai jos il are deja scris) si se incheie cu diff + review + commit propriu. +- **Inainte de prima linie de cod dintr-o story**, se scrie o **nota de executie** in `docs\` care + transpune proiectarea in pasi concreti: fisierele atinse, metodele atinse, ordinea editarilor, si ce + se testeaza dupa fiecare. Proiectarea spune *ce* si *de ce*; nota de executie spune *unde* si *in ce + ordine*. Rapoartele din `docs\cercetare\` sunt materia prima — nu se reface cercetarea. +- **Povestile cu dependente declarate** (`*Depinde de:*`, la fiecare story) **nu se pornesc in paralel + cu cele de care depind.** Unde nu e dependenta, se pot rula in paralel de agenti diferiti — dar + **un singur scriitor pe fisier**, niciodata doi agenti pe aceeasi metoda. +- **O story care se dovedeste prea mare la executie se sparge mai departe**, cu acordul lui Marius, si + se noteaza in plan. Mai bine cinci livrari mici decat una care nu se poate revizui. + +### Teste la fiecare pas — nu doar la S6 si S12 + +`S6` si `S12` raman testele **pe flux real**, la capatul fiecarei etape. **Ele nu inlocuiesc testarea +per story.** Fiecare story se incheie cu teste proprii, rulate si trecute, **inainte** de review: + +- **Harness headless** (`COMUN\docs\depanare_testare_vfp.md`) pentru logica — cazurile minime ale + povestii, plus **cel putin o proba de neregresie** pe calea veche, care trebuie sa ramana neatinsa. +- **Harness UI vizibil** (`COMUN\docs\testare-ui-vfp.md`) unde povestea atinge formulare sau griduri — + coloanele de grid **nu se materializeaza headless**, deci acolo verificarea headless nu e concludenta. +- **Rezultatul asteptat se declara INAINTE de rulare**, inclusiv pentru cazurile care trebuie sa dea + eroare. Un test scris dupa ce s-a vazut rezultatul nu dovedeste nimic. +- **Un esec documentat e livrabil valid** — se raporteaza, nu se ascunde si nu se reia la nesfarsit. +- Testele fiecarei povesti **se pastreaza**, si intra in suita rulata de S6 / S12. Nu se scriu de + unica folosinta. + +### Code review dupa implementare, inainte de commit — pentru fiecare story + +**Nicio story nu se comite fara review, si review-ul vine dupa implementare si dupa teste, nu in loc +de ele.** Ordinea, fixa: + +1. **Implementare** (delegata unui subagent, conform modului de lucru din `CLAUDE.md`). +2. **Teste** — rulate, trecute, cu rezultatele raportate. +3. **Diff ca fisier in `docs\`** (`diff_s_.patch`), inclusiv partea din `COMUN`. +4. **Code review pe diff**, de catre **un agent care nu a scris codul** — altfel isi revizuieste + propriile presupuneri. Review-ul verifica cel putin: regresia pe calea veche, conventiile per zona + atinsa (`COMUN\docs\reguli_lucru.md`, punctul 7 — encoding `cp1250` la `.vc2`/`.sc2`, `GO` pe + `Recno()`, `goExecutor` + `ALTER TABLE`, UX formulare/griduri), comentariile (istoric **numai** in + antetul fisierului, max o linie in cod), si ca write-back-ul text→binar e facut pentru fiecare + fisier atins. +5. **Aprobarea lui Marius.** +6. **Commit** — in **ambele** repo-uri unde e cazul (ROAFACTURARE si COMUN), cu changelog. + +**Afirmatiile review-ului se verifica, nu se cred.** Un review poate citi structura corect si presupune +semantica gresit; cand semnaleaza un defect, se deschide codul citat inainte sa fie acceptat — la fel +cum se procedeaza cu rapoartele de cercetare. + +**Ce NU face review-ul:** nu reargumenteaza deciziile luate (lista lor e in acest plan), nu extinde +perimetrul povestii, si nu propune refactorizari colaterale. Ce gaseste in afara perimetrului se +**raporteaza separat**, nu se repara in trecere — exact cum s-a procedat cu defectul `lnTip` din +`do_copiaza`. + +### Etapa I — formularul unificat + +#### S1 — Inventarul de campuri si alegerea bazei +Deciziile de perimetru sunt luate (vezi listele de la inceput). Ramane: **se porneste de la prototipul +`frm_facturare_articole2` sau de la `frm_facturare_articole` extins?** +Recomandare: **de la prototip**, si acum cu trei argumente in plus fata de runda 2 — are deja +controalele de antet, are butoanele **deasupra** gridului (decizia 13), si are coloanele de discount +editabile plus procentul pe linie (decizia 14). Partea grea de layout e deja acolo; logica lipsa se +porteaza in el. +Livrabil: tabel camp-cu-camp `frm_date_factura` / `frm_date_aviz` / `frm_modifica_factura` / +`frm_alte_date` -> formular unificat, cu ce se pastreaza, **in ce grup din cele patru intra** (I), +ce se pliaza, ce dispare, si **cu ruta de scriere a fiecarui camp** (pe loc sau prin regenerare — +vezi G-bis). Baza de pornire: `docs\cercetare\inventar_controale_formulare.md`, care are deja +etichetele reale si conditiile de vizibilitate. +**Livrat (runda 7): `docs\S1_inventar_campuri_formular_unificat.md`** — 30 de campuri de antet, cu +grupul din I, conditiile de vizibilitate si ruta de scriere pentru fiecare, plus randul dedicat lui +`V_EFACTURA` (singurul camp fara control azi, nicaieri) si cele patru bife marcate ca disparute. +Tabelul a scos la iveala **golul de rute de scriere** din G-bis — vezi acolo. Are si o sectiune +„Neclarificate" cu opt puncte, dintre care doua ambiguitati de nume (`chkDetaliat`, `cboTipFactura` +apar in doua formulare si nu s-a confirmat pe cod ca scriu acelasi camp). +**Coloana „ruta de scriere" e completa acum** — `rute_scriere_antet.md` a inchis cele trei grupuri +lasate neclarificate, iar deciziile 25 si 26 le-au dat ruta. Tabelul se citeste **impreuna cu tabelul +de rutare din G-bis**, care e sursa de adevar pentru ruta fiecarui camp; sectiunea „Neclarificate" din +S1 ramane valabila doar pentru punctele **4, 5, 7 si 8** (ambiguitatile `chkDetaliat` / `cboTipFactura`, +liniile exacte de definire a controalelor, si dependenta de drepturi). +*Gata cand:* tabelul e aprobat. **APROBAT de Marius, 09.08.2026 — S1 e incheiata.** Tabelul devine +referinta pentru S3 si S3b; modificarile ulterioare se fac in el, nu se rescrie de la zero. + +**ASEZAREA ZONEI DE JOS — INCHISA prin decizia 57 (Marius, runda 15): VARIANTA D.** +Cerinta initiala (runda 14), formulata de el: *„totalurile de jos sa fie mai compacte, si sa includa +si discount-ul; formularul trebuie sa fie compact si aerisit; incasarea si alte date jos de tot, +eventual 2 butoane pe acelasi rand — vezi modelul Saga; in centrul atentiei sa fie datele facturii"*. +Referinta vizuala: `{06D747B8-0824-488C-8832-DEB9AE661974}.png`. +Rundele 14-15 au dat patru variante (A / B / C, apoi D). **Aleasa: D.** Vezi „Decizia 57" la +deciziile rundei 15, care are enuntul complet, cele patru consecinte de implementare si punctul lasat +deschis. Ce e de retinut aici, pentru executia lui S1: + +- **Antetul se strange la doua randuri**, fara titluri de grup — campurile se recunosc dupa eticheta. +- **Panoul separat „Discount pe document" dispare**; discountul devine rand editabil in banda de + totaluri, cu procentul pe loc. +- **Banda de totaluri** e pe toata latimea, lipita de grid: baza, discount articole, discount document + cu procentul, TVA — desfacute pe un rand — si totalul mare singur la dreapta. +- **Incasare si Alte date NU sunt dialoguri** — sunt doua sectiuni colapsabile in formular, **una + langa alta pe acelasi rand**, sub totaluri, fiecare cu rezumatul continutului pe randul inchis. +- **Bara de comenzi de jos nu exista**: `but_renunt` / `but_termin` raman in banda de titlu. + +**Cota de TVA a discountului de document s-a decis — decizia 59, runda 16** (vezi sectiunea K-bis): +repartizare proportionala automata pe cote, discountul **NU cere cota de la utilizator**, deci **nu +e nevoie de o a treia sectiune pentru cota**. Ce mai cere UI e **un camp text optional de motiv** +(pentru `AllowanceChargeReason`), mult mai mic decat o sectiune. **Decizia 61 (runda 16) l-a +materializat: Marius vrea campul. Asezarea lui s-a inchis definitiv abia in runda 17 — decizia 64 +(a treia sectiune jos) a fost **rasturnata de decizia 66: motivul sta in banda de totaluri, langa +suma pe care o explica**, iar randul de jos ramane cu **doua** sectiuni, ca la decizia 57. Varianta +grea (cota + explicatie proprii) ramane exclusa, ca mai sus. + +**Cele doua pagini online ale lui #13 — nu se confunda:** +- **mockup-ul formularului** (fisier pe disc, `docs\mockup_13_formular_unificat.html`, **v9** din runda + 17, cu asezarea D si motivul discountului in banda de totaluri): + **https://claude.ai/code/artifact/e9d73e86-7518-4f50-a998-43c49e083142**. + Are copie pe disc, si ea e sursa de adevar; artifactul se republica din ea (`WebFetch` pe URL intai, + apoi `Artifact` cu `url`). Decizia 33 e consumata — URL-ul e la zi. +- **pagina cu variantele de asezare** (A/B/C/D), **fara copie pe disc**, deci artifactul **e** sursa de + adevar: + +Pagina cu variantele: **https://claude.ai/code/artifact/dfc70d38-856f-48fd-b6af-3da78e9da4f7** +— **nu mai are copie pe disc** (cerinta lui Marius, runda 15: „doar online, ca sa nu mai intretii 2 +variante"). Procedura de modificare: `WebFetch` pe URL → fisier de lucru **in scratchpad, nu in +`docs\`** → `Artifact` cu `url` = link-ul de mai sus. + +#### S2 — O singura procedura `factureaza` +Se elimina `factureaza2` ca procedura paralela; `factureaza` primeste un mod. Se pastreaza +integral ramurile care lipsesc azi din `factureaza2` (lista din A). + +**ACTUALIZAT dupa decizia 67 (Marius, 20.08.2026) — comutatorul `gnFacturareNou` cu dialog e +INLOCUIT.** Proiectarea initiala (mai jos) alesese sa mute verificarea `gnFacturareNou` la punctul +de alegere a lui `lcObject`, in interiorul lui `factureaza` — implementata asa intr-o prima trecere. +Marius a respins comutatorul global cu dialog: vrea acces simultan la ambele fluxuri, nu un +`AMESSAGEBOX` care alege intre ele. Mecanismul final: `plFacturareNoua`, variabila **Private** +declarata de apelant (nu comutator persistent), plus o **intrare noua de meniu** ("Facturare (nou)", +buton `Cw10` in `Clase\ofundal_facturare.vc2`) care o seteaza inainte de a apela `factureaza(N)`. +Detalii complete la **Decizia 67**, mai jos in acest document (dupa Decizia 66). + +**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. Butonul de meniu (`Clase\ofundal_facturare.vc2`) +e specific ROAFACTURARE, nu e cod comun. +*Gata cand (rescris dupa decizia 67 + completare + corectie review):* `factureaza2` nu mai exista; +`ofacturare.prg` nu mai contine `gnFacturareNou`; are exact o aparitie a lui `plFacturareNoua` +(variabila Private a apelantului), la punctul de alegere a lui `lcObject`; meniul „Facturare" (`Page2`) +are o intrare noua „Facturare (nou)" (`Cw10`) cu **primele patru** optiuni ale paginii (`Cw1..Cw4` - +lista de preturi, contract, comanda, aviz - singurele care trec prin `factureaza`; `Cw5..Cw9` ruteaza +prin `initializeaza_vanzare_din_stoc()`, un flux separat, in afara perimetrului), meniul „Avize" +(`Page3`) are o intrare noua „Avize (nou)" (`Cw5`) cu toate cele patru optiuni ale paginii (toate +verificate ca trec prin `factureaza`) — ambele seteaza `plFacturareNoua` si deschid +`frm_facturare_articole2` prin `Cw.do_actiune()`, fara sa afecteze +butoanele existente. +*Depinde de:* S1. + +#### S3 — Portarea logicii de antet in formularul unificat +Cele 17 + 13 metode `do_cauta_*`, `do_schimba_tipdoc`, validarile din `inainte_de_do_termin` +(`ofacturare.vc2:9455-9561` si `:7277-7352`) si `Init`-urile. Se porteaza **o singura data**, cu +ramificare pe factura / aviz in interior, nu doua copii. +Punct de atentie cunoscut: **#16 din `todos.txt`** — la revenirea din formularul de curs valutar, +focusul sare pe tip document si iesirea din serie regenereaza numarul actului. + +**PROIECTAT (runda 9) — `docs\cercetare\s3_portare_antet.md`.** Doua rezultate schimba povestea: + +- **Portarea „o singura data cu ramificare" nu e posibila ca atare.** `Init`, `inainte_de_do_termin` si + partial `do_cauta_fdoc` sunt **omonime cu semantica total diferita pe toate cele patru formulare-sursa** + (antet vs. articole vs. alte-date). Deci nu e o metoda cu ramificare interna, sunt **patru fuziuni de + metoda**. Obstacolul principal al lui S3 nu e codul de validare — majoritatea e `Do Case` / `amessagebox` + mecanic — ci **reconcilierea a patru cicluri de viata `Init` / `inainte_de_do_termin` independente + intr-unul singur**, cu ordinea de dependente din sectiunea 5 a raportului. Numarul real de `do_cauta_*` + e **29, nu 30**. Lista completa a omonimelor e la sectiunea 2 — **se citeste inainte de orice editare**, + altfel portarea le confunda (capcana deja platita o data cu `do_calculeaza_discount`). +- **#16 nu e un bug de focus, si nu e in perimetrul in care il caută planul.** Reprodus pe cod pana la + linia exacta: bucla de reincercare din `factureaza` / `factureaza2` (`ofacturare.prg:174-571`) trateaza + **orice esec SQL** — nu doar cursul valutar lipsa — ca pe un „DA, continui cu alt document", si + redeschide formularul de antet de la zero. Deci **unificarea nu-l reproduce automat**; poate chiar sa-l + elimine ca efect secundar, daca antetul nu se mai reconstruieste din `Init` dupa un esec Oracle in pasul + urmator. **Cade premisa „ori se rezolva #16 aici, ori se reproduce bug-ul".** Si leaga #16 de **S2**, nu + de S3 — bucla e in procedura, nu in formular. + +*Gata cand:* un document se emite integral din formularul unificat, pe tip 1 (lista de preturi), cu +acelasi rezultat in `vanzari` / `act` / `rul` ca pe calea veche — criteriul rescris verificabil e la +sectiunea 7 a raportului, iar ce **nu** se poate testa headless la sectiunea 8. +*Depinde de:* S2. + +#### S3b — Sectiunea pliata: alte date + analitice +Deciziile 7 si 8. Se muta in formular cele patru grupuri din `frm_alte_date` (B) si coboara acolo +analiticele din antet. `actualizeaza_tipincasare` se muta ca atare; **alocarea si dezalocarea +numerelor** de chitanta / bon fiscal / POS raman legate de aceleasi evenimente ca azi, nu de +deschiderea formularului. `frm_alte_date` nu se sterge cat timp calea veche mai e in uz. +**Doua precizari din runda 7, amandoua restrang povestea:** +- **analiticele coborau ca AFISARE, nu ca editare** (decizia 26) — **CADUC: decizia 72 le face + editabile**, si la introducere, si la reemitere. Nu se scrie `ReadOnly` pe ele si nu mai e nevoie de + tooltipul explicativ; editorul de nota al lui **#6** ramane singurul loc care le trateaza pe linie; +- **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. + + **AMANAT LA S8c (23.08.2026), decis de Marius.** Blocarea nu are azi nicio stare pe care sa se + declanseze: pe `frm_facturare_articole2` nu exista notiunea de „document deja emis" — zero + `numar_act`, `Init` are un singur parametru (`toFactura`, doar pentru copiere), `oDateFactura` + n-are `numar_act`, iar `lcopiere` / `lArticoleIncarcate` inseamna altceva. Singura blocare + conditionata din toata clasa e pe **tipul** documentului (aviz), nu pe stare. In plus, + `but_modifica` — de care depinde criteriul „ambele stari ale antetului" — **e S8c**, nu exista + inca. O garda scrisa impotriva unei stari care nu poate aparea nu se poate testa, deci **decizia + 25 iese din criteriul de „gata" al S3b si trece in S8c**, impreuna cu notiunea de document emis. + Cercetarea completa, inclusiv ce inseamna „blocat" pe fiecare tip de control din grup: + `docs\cercetare\s3b_decizia25_document_emis.md`. Confirmat acolo si ca **nu exista ruta de + reemitere**, nici in VFP, nici in Oracle (`PACK_FACTURARE.scrie_incasari` are doi apelanti, ambii + interni fluxului de emitere; zero apeluri din VFP). +**PROIECTAT (runda 11) — `docs\cercetare\s3b_alte_date_analitice.md`.** S3b nu e o mutare de controale. +Trei rezultate schimba povestea: + +- **Riscul din criteriul de „gata" e real, si are mecanism.** Alocarea numarului de chitanta nu porneste + din clicul utilizatorului, ci din **orice atribuire programatica** a lui `.Value`: + `opt_incasat.ProgrammaticChange` (`ferestre_cere_date.vc2:3351-3352`) cheama acelasi + `actualizeaza_tipincasare()` ca `.Click` (`:3250-3251`). Consecinta neasteptata: **azi, deja**, + `Init` (`:3150`) seteaza singur `opt_incasat.Value = 2` cand documentul soseste cu `poDate.incasat<>0` + (cazul copierii), deci deschiderea dialogului aloca un numar fara ca cineva sa atinga ceva. Intr-un + formular unde zona se plieaza si se deplieaza repetat pe acelasi `poDate` persistent, o repopulare + naiva a starii ar aloca si dezaloca **la fiecare toggle**. Solutia proiectata: populare **o singura + data**, iar plierea strict pe `Visible`/`Height` — niciodata pe reasignare de `.Value`. Criteriul de + non-alocare e formulat ca **assert headless pe `poGeneratorNumere`**, pe doua scenarii de start + (document nou **si** document copiat cu incasare presetata — al doilea e cel care azi chiar aloca). +- **Nu exista mecanism de pliere de refolosit.** Cautare exhaustiva in `ofacturare.vc2`: zero potriviri + in prototipul `frm_facturare_articole2`. Cel mai apropiat tipar din suita e + `frm_modific2024.afiseaza_rulaje` (`omodificari.vc2:13169-13199`, perimetrul #6 — **citit, neatins**), + acelasi idiom sus/jos validat deja pentru `but_modifica`/`but_salveaza` la decizia 9. Deci **cod nou**, + dupa un tipar existent, nu o clasa reutilizabila. +- **Decizia 26 cerea mai mult decat mecanismul existent** — `ct_clb_cautare.do_dezactiveaza()` + (`caut_ora.vc2:800-806`) ascunde **doar lupa de cautare**, textbox-ul ramane tastabil, deci read-only + real ar fi cerut `ReadOnly` explicit pe langa el. **CADUC odata cu decizia 72**: analiticele raman + editabile, deci nu se scrie nimic. Constatarea despre `do_dezactiveaza()` ramane valabila pentru + oricare alt camp care ar avea nevoie de blocare reala. + +**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 +iar blocarea grupului de incasare — **amanata la S8c, vezi mai sus** — se cerea pe **ambele stari ale antetului** (inainte si dupa +`but_modifica`), nu doar la deschidere. **Punctul care cerea read-only-ul analiticelor pe ambele stari +cade odata cu decizia 72** — analiticele sunt editabile, si la introducere, si la reemitere, deci +criteriul devine invers: pe un document emis analiticele **se pot alege**. 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, iar deschiderea si inchiderea formularului +fara a atinge zona de incasare **nu aloca si nu dezaloca niciun numar**. +*Depinde de:* S3. + +#### S3c — Sursa ca parametru, nu ca global +Decizia 12. Punctele de intrare exista deja (comanda: `ocomenzi.vc2:1580-1596`; contract: din +ROACONTRACTE), dar transmit prin globalele `goComanda` / `goContract`. Se adauga parametru explicit +pe `factureaza` si pe `oDateFactura`, cu globalele pastrate ca sursa de rezerva pana se convertesc +toti apelantii (inclusiv cei din ROACONTRACTE si ROAGEST — e cod comun). Se verifica intai asimetria +de resetare semnalata la L.3. +**PROIECTAT (runda 11) — `docs\cercetare\s3c_sursa_ca_parametru.md`.** Se poate face, e o interventie +mica — dar **planul supraestimeaza cat de „globala" e problema azi**, si in doua sensuri opuse: + +- **`goContract` nu e scris nicaieri in ROAFACTURARE** — nici in codul produsului, nici in `COMUN`-ul + lui. Deci ramura de precompletare din contract (`ofacturare_comun.prg:261-297`) e **cod mort in acest + produs**, iar **asimetria de resetare de la L.3** (`ofundal_facturare.vc2:886-902`) exista textual dar + e **inerta**: n-are ce sa lase nereseta, pentru ca n-are scriitor. Devine risc real abia daca cineva + adauga in viitor un scriitor in ROAFACTURARE — moment in care lipsa resetarii s-ar activa **tacut**. +- **`goComanda` chiar e viu, si are DOI scriitori**, nu unul: clicul pe „Factureaza" + (`ocomenzi.vc2:1583`) **si** navigarea in grid (`:2199`, doar ca sa decida vizibilitatea unui buton). + Al doilea n-are nicio legatura cu facturarea si **ramane si dupa conversie** — deci globala nu dispare. +- **Criteriul „pana se convertesc toti apelantii" nu se poate citi ca „pana dispare globala".** In + ROACONTRACTE, `goContract` e **bufferul de editare al intregului ecran de contracte** (peste 100 de + `ControlSource` legate de el), populat de navigarea in grid, independent de facturare. Nu dispare la + aceasta poveste si nici la vreuna rezonabila urmatoare; S3c schimba **doar canalul** prin care valoarea + ajunge la `oDateFactura.Init`. + +**Suprafata de regresie, masurata:** doi scriitori de convertit (`ocomenzi.vc2:1580-1596`; +`ferestre_contracte.vc2:1538-1549` + `:1598-1606` in ROACONTRACTE), **trei** fisiere comune atinse o +singura data fiecare (`ofacturare.prg`, `oproceduri_facturare.prg`, `ofacturare_comun.prg`), si **zero +schimbari** la celelalte ~31 de puncte de intrare din inventarul S2 — toate cheama `factureaza(N)` cu un +singur argument. Cele cinci fisiere sunt **identice pe MD5** in cele sapte produse, cu o singura exceptie: +**ROAIMOB**, o linie lipsa in `ofacturare_comun.prg` — acolo diff-ul se aplica **manual**, nu prin copiere +mecanica. + +**Semnatura propusa:** `factureaza(tnTip, toFactura, toSursa)` si `oDateFactura::Init(tnIdSet, tnTip, +toSursa)` — parametru nou la coada, cu implicit, fallback pe global cand lipseste. +**Capcana de limbaj, semnalata explicit:** garda se scrie `Type('toSursa') = 'O'`, **nu** +`Type('toSursa') <> 'U'`. Un parametru VFP nepasat **nu** e `'U'` (aia e pentru variabile nedeclarate), +ci `'L'` cu `.F.` — o garda `<> 'U'` ar trece mereu adevarat si ar incerca sa citeasca `.id_part` de pe +`.F.`, eroare la primul apel neconvertit. Raportul cere verificarea comportamentului `Type()` pe un +`.prg` de proba **inainte** de a atinge fisierele reale. + +*Gata cand:* criteriul rescris e la sectiunea 10 punctul 4 al raportului, pe patru probe — una +structurala (parametrul prezent cu semnatura identica in toate cele trei fisiere comune, in toate cele +sapte produse) si trei comportamentale: **calea convertita** cu globala deliberat „murdara" dintr-o +navigare anterioara produce documentul dat prin parametru, nu pe cel din globala; **calea neconvertita** +(oricare din cele ~31 de apeluri cu un singur argument) produce acelasi document ca inainte; iar +**ROACONTRACTE** da aceeasi precompletare ca azi, verificat manual pe build separat. +*Depinde de:* S2. **Atinge cod comun intregii suite** — nu se face impreuna cu alta modificare. + +#### S4 — Cautarea articolelor pe server, in linie +Partea Oracle: variante filtrate pe articol ale cursoarelor de facturare, cu aceleasi ramuri pe tip +ca `cursor_preturi` / `cursor_contract` / `cursor_comanda` / `cursor_avize` / `cursor_gestiune`. +Partea VFP: `combosql` pe cod si pe denumire completeaza linia cu pret, cota TVA, valuta, `id_pol`, +`gestionabil`, `pret_cu_tva`; `crsarticole` nu se mai incarca in masa. +Se pastreaza "adauga tot" pentru tipurile care au document sursa (comanda, aviz, contract) — acolo +setul e marginit si incarcarea lui e legitima. +**PROIECTAT (runda 11) — `docs\cercetare\s4_cautare_articole_server.md`. Criteriul de mai jos nu e +atingibil ca atare, si motivul nu e UX.** + +- **`crsarticole` nu e doar sursa de populare a gridului — e un registru al cantitatii ramase de + facturat.** E citit **si scris** de `do_adauga_tot`, `do_sterge` si `do_scrie_factura` pentru toate + tipurile cu document sursa: stergerea unei linii **reface** cantitatea in `crsarticole` + (`ofacturare.vc2:14640-14669`), iar la scriere se face `Calculate Sum(cantitate) To lnCantitateRamasa` + peste el ca sa se decida daca se inchide automat comanda / avizul (`:14303-14311`, `:14334-14338`). + Deci pe **comanda, aviz si contract** incarcarea in masa **nu poate disparea**, indiferent de decizia + de UX privind „adauga tot" — bookkeeping-ul e cablat pe cursorul incarcat. **S4 se aplica curat doar + pe ramurile de lista de preturi** (`cursor_preturi`, plus jumatate din `cursor_contract` si + `cursor_gestiune`). + **DEPASIT de proiectarea punctului 2 (runda 12)** — se citeste doar ca istoric al deciziei 39. + Concluzia „incarcarea in masa nu poate disparea" cadea pentru ca `crsarticole` era tratat ca un + registru omogen; sunt de fapt **doua mecanisme distincte** (Rol A / Rol B), si niciunul nu cere + cursorul incarcat. Vezi mai jos. +- **`combosql` e un prototip la jumatate, si e singura lui utilizare din suita.** + `grd_factura.cCodMat.cboCodmat` / `cCboDenumire` (`ofacturare.vc2:16751-16797`, wiring la + `:19288-19334`) chiar cauta pe server, dar scrie **doar** `codmat` / `denumire` / `id_articol`, pentru + ca sursa lui (`vnom_articole`) n-are pret, TVA, valuta, `id_pol`, `gestionabil`. Cautare in + `COMUNROA` si `ROAGEST`: **zero alte utilizari** — nu exista de unde copia un exemplu complet. + Confirma insa exact punctul C: de facut e **varianta filtrata pe articol a celor cinci cursoare**, nu + o cautare noua. +- **`cursor_contract` produce deja doua cursoare** — `V_CURSOR` (`crsarticole`, prin delegare la + `cursor_preturi`) si `V_CURSOR2` (`crsarticole1`, articole **sau** rate). Filtrarea se aplica curat + doar pe jumatatea `crsarticole`; **randurile de rata n-au `id_articol`** si raman needitate. +- **Vizibilitatea de azi a butonului „adauga tot" coincide aproape exact cu impartirea utila pentru S4** + (`ofacturare.vc2:15108-15248`: ascuns pe lista de preturi, vizibil pe comanda / aviz / retur) — + confirmare pe cod, nu presupunere. +- **Legatura cu S10, confirmata:** pretul se cauta **o singura data**, la alegerea liniei, si se + transmite mai departe neschimbat — exact ce face azi `adauga_articol_factura`, care il primeste ca + parametru in loc sa-l re-derive. +- **Efect secundar al filtrarii, rezolvat prin decizia 42:** `verifica_cursuri_valute` e azi + neconditionata in procedura, deci pe varianta filtrata s-ar declansa la **fiecare** cautare de articol + in loc de o data la deschidere — cu `-20005` posibil pe o valuta pe care operatorul n-o foloseste, + inainte sa vada vreun rand. Se **restrange la valuta articolului cautat**. +- **Cele doua puncte „de inchis inainte de implementare" sunt INCHISE (runda 12)** — + `docs\cercetare\s4_puncte_deschise.md`. Raspunsul de baza e la amandoua „filtrarea nu schimba nimic", + dar **fiecare lasa in urma o cerinta de implementare, nu doar o bifa**: + - **`id_jtva_coloana` chiar lipseste** din `cursor_preturi`, din `cursor_gestiune` si din `V_CURSOR2` + al lui `cursor_contract`, pe toate ramurile — confirmat pe SQL. **Nu e bug:** `do_initializeaza_articol` + pune `0`, iar `frm_articol_factura.Init` (mostenit si de `frm_articol_gest_factura`) **il rederiva + mereu** din `proc_tvav` via `jtva_coloane`, pe orice linie adaugata prin `do_adauga_articol`. + **Cerinta care rezulta pentru S4:** derivarea tine **numai** daca linia trece prin + `do_adauga_articol`. `combosql`-ul prototip de azi (`ofacturare.vc2:19288-19306`) face `REPLACE` + direct in `crsfactura` si **ocoleste toata derivarea**; daca S4 extinde acel `REPLACE` fara sa treaca + prin `do_adauga_articol`, `id_jtva_coloana` ramane nederivat si Oracle + (`adauga_articol_factura`, ramura `ELSE`) arunca **`-20000 FACT-013`** sau scrie cota gresita. + **Bug nou posibil, introdus de S4** — intra ca cerinta explicita in proiectare, nu ca observatie. + - **`but_urmator_tot1` are `Visible = .F.` la design** (`ofacturare.vc2:11265`); tipurile 23 si 41 au + `Case` propriu, dar **niciunul nu-l face vizibil** — confirmat, nu presupus. Nu exista alt mecanism + de „adauga tot"; exista insa `but_urmator1` (adaugare rand-cu-rand), **neconditionat de tip**, care + merge prin acelasi `do_adauga_articol` — deci dupa S4 **nu se pierde nimic** pe aceste tipuri. + - **Corectie de rutare fata de ce presupunea planul:** pe formularul **standard** (`factureaza`, + `ofacturare.prg:266-308`) doar **tipul 41** cheama `cursor_gestiune`; **tipul 23 cheama de fapt + `cursor_preturi`** (grupat cu lista de preturi). Tipurile **45, 48, 49 nu ating deloc** + `cursor_gestiune` (45 → `cursor_preturi`, 48/49 → `cursor_articole_k`, alta procedura). Doar pe + **prototip** (`factureaza2`, opt-in `gnFacturareNou`) 23 si 41 merg impreuna pe `cursor_gestiune` — + **divergenta reala intre cele doua formulare**, semnalata, neatinsa, in afara perimetrului S4. + +**Decizia 39 largeste povestea peste ce propunea raportul.** Raportul recomanda restrangerea la lista de +preturi, tocmai pentru ca registrul de cantitate ramasa blocheaza restul; Marius a ales sa se desfaca si +registrul. Deci S4 are de acum **doua bucati, nu una**: +1. **varianta filtrata a cursoarelor** + completarea liniei din `combosql` (partea proiectata in raport); +2. **decuplarea bookkeeping-ului de cantitate ramasa** de cursorul incarcat in masa — sursa unica, nu o a + doua copie tinuta in pas cu prima. Atinge `do_adauga_tot`, `do_sterge`, `do_scrie_factura` si + **inchiderea automata** a comenzii / avizului. + +**Punctul 2 — PROIECTAT (runda 12): `docs\cercetare\s4_punct2_registru_cantitate_ramasa.md`.** Iese mult +mai ieftin decat parea, pentru un motiv care nu se vedea din raportul punctului 1. + +- **`crsarticole.cantitate` are DOUA roluri, nu unul** — si numai unul e „registrul" temut. + **Rol A — cantitate ramasa de facturat dintr-un document sursa** (comanda `3,21,25,28,42,47`; avize + `4`). **Rol B — plafon de cantitate in sesiune** (lista de preturi gestionabila `1,22,29` si jumatatea + de contract, transfer `23,41`, retur `8,9,24`): impiedica operatorul sa adauge mai mult decat vede pe + ecran. **Rol B nu alimenteaza nicio decizie Oracle** — e plafon de UI, nu registru de business. +- **Pe Rolul A, cursorul VFP e o copie redundanta a unui calcul pe care Oracle il repeta oricum.** + `do_scrie_factura` sumeaza `crsarticole` doar ca sa decida ce trimite in `pnParametruAditional`, iar + Oracle **recalculeaza independent, din tabele reale**, in `inchide_comanda()` / `marcheaza_facturat()`, + chiar in procedura care scrie factura. +- **Nu exista coloana `INCHISA`.** Cautare in tot pachetul: **zero potriviri** (verificat separat de + sesiunea principala). „Comanda inchisa" e o stare **derivata** din `COMENZI_ELEMENTE` vs + `VANZARI_DETALII`; `inchide_comanda()` insereaza un **rand compensator**, nu seteaza un flag. Pe + comanda, `pnParametruAditional` e strict **binar** — „s-a cerut fortarea inchiderii?" —, iar cantitatea + trimisa de VFP **nu participa** la calculul Oracle. +- **Pe avize e altfel, si aici sta subtilitatea:** `VANZARI.FACTURAT` **e** un flag persistat, iar + `V_VERIFICARE` (= `pnParametruAditional`) alege intre „**increde-te in VFP** si marcheaza toate avizele + referite" (`0`) si „**recalculeaza per-aviz** din tabele reale si marcheaza doar cele cu `ramas = 0`" + (`1`). Deci pe aviz suma din VFP schimba semantica, nu doar declanseaza o actiune. +- **Un bug preexistent, de REPRODUS identic, nu de reparat in trecere:** cand un `crsarticole` agrega mai + multe avize, suma globala poate da `0` desi un aviz are ramas si altul are exces care-l compenseaza — + caz in care toate se marcheaza facturate, inclusiv cel cu ramas real. Varianta noua pastreaza aceeasi + conditie de declansare (suma globala pe tranzactie, nu per document). +- **`23` si `41` nu apar deloc in `CASE`-ul de finalizare** — cad pe `ELSE`, fara nicio inchidere: + transferul n-are document sursa de inchis, doar plafon (Rol B). Contractul (`2,6,52`) intra pe + `scrie_rate_factura`, care **nu e o inchidere**, e alta operatie. +- **Recomandarea: (b) pentru Rolul A, (c) pentru Rolul B.** + **(b)** cele doua `Calculate Sum` din `do_scrie_factura` se inlocuiesc cu un apel Oracle facut **dupa** + `do_scrie_articole()` (cand `VANZARI_DETALII_TEMP` contine exact liniile pe cale sa fie scrise) si + **inainte** de `Do Case`-ul care alege procedura de scriere. Cele doua functii Oracle noi sunt o + **extragere** a interogarii pe care `inchide_comanda` / `marcheaza_facturat` o ruleaza oricum, nu logica + noua. Efect secundar gratuit: dispare si un bug latent de concurenta — cursorul local nu vede azi ce a + facturat intre timp alt operator din aceeasi comanda. + **(c)** plafonul Rol B se cere pe server **la fiecare adaugare/editare**, minus ce e deja in + `crsfactura` — deci nu mai exista a doua copie de tinut in sincron. + Varianta cu un cursor propriu, minimal, a fost **respinsa motivat**: ramane tot o a doua copie manuala, + doar mai ingusta. Nu e „sursa unica". +- **Cazul limita cerut in plan** (adauga si sterge inainte de salvare) devine **trivial**, nu doar + acoperit: liniile adaugate-si-sterse nu ajung niciodata in `VANZARI_DETALII_TEMP`, deci nu influenteaza + calculul, fara nicio actiune de refacere. +- **CORECTIE asupra punctului 1:** pasul lui 5 (`s4_cautare_articole_server.md:417-424`) opreste + incarcarea in masa si pe `23,41`, presupunand ca n-au bookkeeping — **au Rol B**, confirmat pe cod. + Punctul 1 **nu se poate aplica pe `23,41`** inainte ca punctul 2 sa acopere Rolul B pe ele. + +**Toate trei sunt INCHISE (runda 13). Nu se mai reiau.** +1. ~~**Asimetria din `do_modifica`**~~ — azi, pe grupul `1,22,29` + contract-lista, plafonul **nu** se + ajusteaza la editarea cantitatii unei linii deja adaugate. E preexistenta. **INCHIS — decizia 45 + (runda 13, pe recomandare):** se lasa sa se **corecteze de la sine** prin recalculul la cerere, cost + zero, dar corectia se **declara explicit in changelog** — S4 schimba atunci un comportament punctual, + nu doar „decupleaza". +2. ~~**Corectia pe `23,41`**~~ — **INCHIS — decizia 46 (Marius, runda 13): asteapta punctul 2.** + Punctul 1 **nu se redeschide** acum; aplicarea lui pe `23,41` vine odata cu recalculul pe server, care + acopera Rolul B. Nu se pierde nimic: ordinea de implementare oricum le pune dupa. +3. ~~**Contract `26,52`**: nu s-a gasit dovada nici de prezenta, nici de absenta a Rolului B.~~ + **INCHIS de proiectarea S5** (`docs\cercetare\s5_acoperire_tipuri.md`): `26` si `52` **n-au niciun + bookkeeping**, nici Rol A, nici Rol B — excluderea e totala, pe `Do Case` exhaustiv fara ramura + implicita. Deci „dovedit absent", nu „nepresupus". Nu mai e o decizie de luat. + +**Cerinta de revizuire**: cele doua functii Oracle noi sunt o extragere din proceduri existente, dar +raman **PL/SQL nou in `PACK_FACTURARE`**, cu `JOIN`-urile reproduse **din citire, nu din executie**. Se +verifica pe Oracle inainte de a continua — e primul pas al implementarii, nu o formalitate. + +**Nota de executie (decizia 60):** pe documentele de tip **48/49** (custodie), cautarea pe server in +linie ramane restransa la articole `IN_STOC = 0` — nu se ofera articole gestionabile pe aceste tipuri. +Siguranta regenerarii la editarea custodiei (decizia 60, +`docs\cercetare\custodie_48_49_stergere_reemitere.md`) atarna de acest invariant. + +*Gata cand:* patru probe, nu una. **(a)** Deschiderea formularului nu mai executa niciun cursor de +articole, **pe niciun tip** — nu doar pe lista de preturi. **(b)** Alegerea unei linii produce aceleasi +valori ca randul corespunzator din `crsarticole` de azi, pe fiecare tip; maparea camp-cu-camp e la +sectiunea 6 a raportului, cu coloanele semnalate explicit acolo unde varianta filtrata **nu** poate +produce aceeasi valoare. **(c)** **Paritate pe inchiderea automata** (decizia 39): acelasi document +sursa, aceleasi linii facturate partial, **aceeasi stare finala** — pe comanda, acelasi rand compensator +in `COMENZI_ELEMENTE` (nu exista flag `INCHISA` de comparat); pe aviz, exact aceleasi `VANZARI.FACTURAT` +marcate, inclusiv in cazul in care avizele agregate se compenseaza intre ele — pe fiecare tip cu document +sursa — +inclusiv cazul in care operatorul adauga si apoi sterge linii inainte de a salva, care azi trece prin +refacerea cantitatii in `crsarticole`. **(d)** O linie adaugata prin cautarea in grid ajunge in +`crsfactura` cu **`id_jtva_coloana` derivat** — adica trece prin `do_adauga_articol`, nu prin `REPLACE` +direct ca prototipul de azi; proba e ca documentul se scrie fara `FACT-013` si cu aceeasi cota ca pe +calea veche. **(e)** Pe un document de tip 48/49, cautarea pe server nu ofera si nu permite adaugarea +unui articol cu `IN_STOC <> 0` (decizia 60). +*Depinde de:* S3. **Punctul 2 nu e proiectat** — se proiecteaza separat inainte de implementare. + +#### S4b — Bara de butoane si meniul de adaugare +Decizia 13, proiectata in J. Trei bucati: +1. **Butoanele de linie deasupra gridului**, cu eticheta, nu iconite mute: linie noua (`but_nou`), + sterge linia (`but_sterge`), detalii linie. +2. **Un singur buton „Adauga articole” cu `xmenu()`**, cu optiunile din tabelul din J. Inlocuieste + `But_urmator_tot1` (azi fara caption si fara `ToolTipText`) si butonul separat de alegere. + **Acopera si contractele** — tipurile 2, 6, 26, 52 lipsesc azi din conditiile de vizibilitate + (`ofacturare.vc2:15113-15245`), desi avizele sunt acolo. +3. **Alegerea selectiva**, dupa tiparul RORIS: dialog modal cu coloana de bifat si criterii de + cautare, populare aditiva in cursorul local, nicio scriere in baza pana la salvare. Pe contract, + unitatea de selectie e **rata** — cazul explicit cerut. Spre deosebire de modelul RORIS, la zero + rezultate se spune de ce, nu se inchide in tacere. +**PROIECTAT (runda 11) — `docs\cercetare\s4b_bara_butoane_meniu.md`.** Implementabila fara cod nou major, +cu **patru corectii** fata de textul de mai sus: + +- **Golul de contract e mai mare decat „lipseste `but_urmator_tot1.Visible`".** Pe **tipul 52** + formularul nu intra in **niciun** `Case` al `Do Case`-ului (`ofacturare.vc2:15109-15248`) — deci pierde + si titlul, si eliminarea coloanei `cSerie`, nu doar butonul „tot". +- **Nu se porneste de la tiparul RORIS.** `frm_tranzit` e specific ROAACNPRO si ar insemna formular nou; + ROAFACTURARE are deja `cauta_alfa(..., tnTipReturn=1)` — mecanism **generic** de selectie multipla cu + bifare, criterii de cautare si populare aditiva, folosit **chiar in acest formular** pentru returul + multi-factura (`do_cauta_facturi`). Mai ieftin, si deja dovedit in productie. +- **„Unitatea de selectie e rata" e adevarat doar pe jumatate.** `crsarticole1` are **doua ramuri + disjuncte** in SQL — `OPT_FACTURARE = 3` (articole reale) si `OPT_FACTURARE IN (1,2)` (rate de + scadentar) — iar un contract e mereu pe una singura, niciodata pe amandoua. Eticheta „Alege ratele de + facturat…" e corecta doar pe a doua ramura; **contractul are deci doua meniuri, nu unul**. +- **Un gol de cod, nu doar de vizibilitate:** `do_adauga_tot` parcurge azi **exclusiv `crsarticole`, + niciodata `crsarticole1`**. Fara extindere, „adauga tot" pe contract fie n-ar face nimic, fie ar aduce + liniile gresite — deci pe contract butonul trebuie **scris**, nu doar facut vizibil. + +**Echivalenta „tot" = „alege total"** tine azi prin constructie, pe o singura rutina comuna de adaugare +pe rand (`do_adauga_articol`) — se pastreaza asa, iar pe contract devine adevarata abia dupa extinderea +de mai sus. + +*Gata cand:* pe fiecare sursa (comanda, contract, avize, document returnat) meniul ofera si „tot" si +„alege", iar rezultatul in `crsfactura` e identic pe cele doua cai cand selectia e totala — **inclusiv +pe contract**, unde azi „tot" nu parcurge cursorul potrivit, si **inclusiv pe tipul 52**, care azi nu +intra in nicio ramura. Pasii cu criterii verificabile sunt la sectiunea 7 a raportului; ce nu se poate +testa headless, la sectiunea 8. +*Depinde de:* S4. + +#### S4c — Discountul pe linie, mutat din dialog in grid +Decizia 14, proiectata in K. Dialogul de articol dispare odata cu unificarea, deci cele doua campuri +de discount ale lui — **procent** si **valoare unitara** — devin coloane in grid, cu acelasi calcul +reciproc din `frm_articol_factura.do_calculeaza_discount` mutat pe evenimentele coloanelor. Se repara +in acelasi timp inconsecventa de azi (read-only in lei, editabil fara recalcul in valuta). Se +pastreaza excluderea pe `in_valuta`. Modelul de date **nu se atinge** — se stocheaza tot valoarea. +**VERIFICAT — S4c e deblocata, dar capcana e alta decat se credea.** Raport: +`docs\cercetare\discount_in_rapoarte_si_efactura.md`. + +- **Niciun raport de factura nu tipareste discountul pe linie** — nici valoare, nici procent. Cautare + in toate `.fr2` de factura / proforma / invoice din `COMUN\Rapoarte`: **zero potriviri** pe „disc". + Singurele doua `.fr2` cu „DISCOUNT" sunt de NIR, nu de facturi emise. Se tipareste `pretftva` si + `valftva`, **deja nete de discount** (`prelucreaza_factura`, `ofacturare_comun.prg:1055-1059`, + `:1156-1248`, in cursorul `crsfacttemp`). +- **eFactura foloseste exact acelasi cursor** (`xmlefactura.prg`) — `LineExtensionAmount` si + `PriceAmount` sunt aceleasi valori nete, cu optiunea (dezactivata implicit) de a adauga si un + `cac:AllowanceCharge` informativ pe linie. +- **SAF-T (D406) nu exista in ROAFACTURARE** — doar tabele de nomenclator cu prefix `saft_` (coduri + TVA / plata), pe partea de achizitii, fara legatura cu `DISCOUNT_UNITAR`. +- **Deci ingrijorarea initiala („utilizatorul schimba totalul fara sa se vada pe hartie") era gresit + tintita:** discountul nu se vede pe hartie **prin design**, iar totalul se vede corect pentru ca + pretul tiparit e deja net. + +**PROIECTAT (runda 12) — `docs\cercetare\s4c_discount_in_grid.md`.** Implementabila, cu **capcana +retintita**: formularea de mai jos, din rundele anterioare, era in acelasi timp prea alarmista si prea +vaga. + +- **Baza de date nu e in pericol.** `do_scrie_articole` trimite spre Oracle **direct** din + `discountftva` / `discountctva` / `vdiscountftva` / `vdiscountctva` (`ofacturare.vc2:14081-14083`, + ales pe `cu_tva` si `tip_valuta`) — verificat pe cod, nu presupus. Deci + `VANZARI_DETALII.DISCOUNT_UNITAR` iese **mereu corect**, indiferent de starea campurilor agregate. +- **Riscul e strict local, si e o inconsistenta, nu o valoare veche.** `valdiminuatftva` / + `valdiminuatctva` sunt agregate citite de `prelucreaza_factura` **din acelasi `crsfactura` din + memorie**, nereincarcat din Oracle. Netratate, factura tiparita poate iesi cu `pretftva` corect si + `valftva` inconsistent — **pret x cantitate diferit de valoare**, ceea ce se vede pe hartie. +- **Exista deja azi calea care demonstreaza gaura:** editarea `vdiscountftva` in grid, pe factura in + valuta, **nu declanseaza recalculul** agregatelor (`discount_verificare2.md`, punctul 3). +- **Calculul e deja generic si refolosibil**, nu trebuie rescris: `do_calculeaza_discount` + (`ofacturare.vc2:1874-1976`) plus `calculeaza_totaluri()` (`oproceduri_facturare.prg:2258-2381`), + care ruleaza pe `Scatter Name`, **fara dialog**. +- **Evenimentul recomandat pe coloanele noi e `Text1.LostFocus`**, nu `InteractiveChange` sau `Valid` — + tiparul e deja folosit in acelasi fisier, pe `frm_avizare_lucrare.grd_articole.cCantitate` / + `cPret` (`:6549-6562`). +- **Punctul deschis din S1 e inchis in trecere:** `_checkbox1` si `chkDetaliat` sunt **doua controale + distincte** in `frm_alte_date`, nu o duplicare — subiectul nu are legatura cu S4c. + +*Gata cand:* tastarea in oricare din cele doua coloane produce aceleasi valori in `crsfactura` ca +dialogul de azi, pe ambele monede; totalurile se refac imediat; discountul venit din politica de pret +se comporta ca azi; **si `valdiminuatftva` / `valdiminuatctva` se recalculeaza la fiecare editare**, +astfel incat pe factura tiparita `pret x cantitate` sa dea exact valoarea — verificat prin retiparire +si prin XML-ul de eFactura, nu doar pe ecran. Pasii cu criterii verificabile sunt la sectiunea 7 a +raportului; ce nu se poate testa headless, la sectiunea 9. +*Depinde de:* S4. + +#### S4d — Data cursului valutar, numai cand are sens +Decizia 15, proiectata in M. Campul apare cand tipul nu e retur **si** (documentul e in valuta **sau** +a intrat pe grid macar un articol cu pret in valuta) — evaluat reactiv, ceea ce devine posibil abia +in formularul unificat. +**VERIFICAT — S4d e deblocata.** Raport: `docs\cercetare\zi_curs_validare.md`. Ascunderea selectorului +**nu** poate lasa documentul fara curs, cu o conditie deja indeplinita de cod: + +- **Implicitul exista si e neconditionat.** `poDate.zi_curs` primeste data documentului chiar in + `oDateFactura.Init` / `Reset` (`COMUN\programe\ofacturare_comun.prg:247`, `:496`), **inainte** ca + formularul sa decida ce ascunde. **Nicaieri codul nu goleste `zi_curs` cand controlul e ascuns.** + Deci „valoarea implicita ramane" nu e ceva de construit — e comportamentul actual, de **nestricat**. +- **Precedentul cerut exista deja**, in alt formular: `frm_date_factura`, tipurile 8 / 9 (retur), unde + `clb_zi_curs` se elimina **neconditionat** si documentul se salveaza corect — in principal pentru ca + `cursor_retur` nici nu foloseste `poDate.zi_curs`. +- **Linia `:8076` nu e in formularul de factura.** Apartine lui `frm_date_aviz_lucrare` + (`inainte_de_do_termin`, `ofacturare.vc2:8054-8119`), un formular restrans pentru **aviz pe lucrare / + aviz pe NIR** (tipurile 27 si 30), care nici nu are control de valuta. Valideaza `zi_curs` pentru ca + tipul 27 are nevoie de curs pentru articolele din comanda, **independent de `poDate.in_valuta`** — + deci nu e o inconsecventa de reparat orbeste, are un motiv. Se aliniaza doar daca formularul unificat + preia si tipurile 27 / 30. + +**Riscul real e in alta parte, si nu tine de vizibilitatea campului:** +`pack_facturare.verifica_cursuri_valute` (chemata din `cursor_preturi`) ruleaza **neconditionat de +`in_valuta`** si exclude doar moneda nationala. Deci un `zi_curs` implicit (azi) fara curs setat in +`CURS` pentru o valuta prezenta in listele de preturi ale utilizatorului **poate pica oricum** +(`-20005`, „Nu este setat cursul…"), indiferent daca selectorul e vizibil sau nu. **Ascunderea nu +introduce riscul asta si nici nu-l rezolva** — dar il face mai greu de inteles pentru operator, care nu +mai vede campul din cauza caruia primeste eroarea. De tratat in mesajul de eroare, nu in vizibilitate. + +**PROIECTAT (runda 12) — `docs\cercetare\s4d_zi_curs_reactiv.md`.** Nu se inventeaza nimic; se +reevalueaza vizibilitatea unui control care exista deja. + +- **Cheia reactivitatii e un camp deja prezent in cursorul gridului: `crsfactura.tip_valuta`**, + interogabil cu **exact tiparul deja folosit in cod** (`ofacturare.prg:1656`, `:1730`: + `Select Distinct ... From crsfactura Where tip_valuta = 1`). Nu e nevoie de o structura noua. +- **Prototipul are deja `Clb_zi_curs` propriu, editabil, legat la `poDate.zi_curs`** + (`ofacturare.vc2:16285-16305`) — se schimba doar **cand** e vizibil. +- **Mesajul `-20005` contine DEJA data si numele valutei lipsa** (`STRINGAGG` peste toate valutele fara + curs) — deci partea de „sa spuna care valuta si ce zi" nu e de construit. **Decizia 42 nu schimba + continutul mesajului, ii schimba domeniul**: il restrange la valuta articolului cautat, dar **numai pe + varianta filtrata** (S4 punctul 1); pe caile cu document sursa incarcarea in masa ramane + neconditionata, deci acolo eroarea poate inca numi o valuta straina de documentul curent. +- **Ce lipseste cu adevarat, si intra ca pas de implementare, nu ca optiune:** sectiunea M cere ca la + aceasta eroare campul sa **revina vizibil** — altfel operatorul primeste o eroare despre un camp pe + care nu-l vede. +- **#16 nu e in perimetrul S4d.** E legat de S2 (bucla de reincercare din `ofacturare.prg`), cu verdict + separat in `s3_portare_antet.md`. S4d doar **confirma** ca antetul persistent ii inlatura mecanismul, + cu conditia ca S2/S3 sa nu recreeze formularul la eroare. +- ~~**De confirmat cu Marius (mic, de UX):** ce se intampla cand se sterge **ultimul** articol in + valuta?~~ **INCHIS — decizia 47 (Marius, runda 13): simetrie.** Campul **se ascunde la loc** cand + dispare ultimul articol in valuta, pe tiparul `lb_cursuri`. „Clipitul" la adaugari/stergeri repetate + e acceptat ca pret al unei reguli unice: aparitia si disparitia urmeaza aceeasi conditie, nu doua. + +*Gata cand:* o factura in lei fara articole in valuta nu arata campul si se emite corect; aceeasi +factura, dupa adaugarea unui articol cu pret in valuta, arata campul cu data implicita completata; +factura in valuta se comporta ca azi; iar la `-20005` **campul redevine vizibil**, cu mesajul de azi +(care deja numeste valuta si ziua). Pasii cu criterii verificabile sunt la sectiunea 7 a raportului; ce +nu se poate testa headless, la sectiunea 9. +*Depinde de:* S4. **#16 se urmareste in S2/S3, nu aici** — vezi M. + +#### S4e — Lista de preturi disponibila si pe factura din comanda +Decizia 16, proiectata in J. Singurul gol real: pe contract merge deja, pe comanda nu, pentru ca +`cursor_comanda` umple `crsarticole` doar cu articolele comenzii. Se adauga lista de preturi peste +cursorul sursei, exact ca la copiere (`ofacturare.prg:454-473`), si optiunea „Cauta in lista de +preturi…” intra in meniu pe toate sursele. +**Si stergerea intra aici**, nu doar adaugarea: cazul cerut e „clientul mai vrea ceva sau vrea sa +schimbe”, deci o linie adaugata trebuie sa poata fi si scoasa. +**DECIS (decizia 29): stergerea unei linii venite din comanda ramane fara protectie.** Se sterge ca +oricare alta; comanda ramane cu cantitatea nefacturata si va aparea **facturata partial**, ceea ce e +si starea corecta. Nu se cere confirmare, nu se marcheaza „refuzat", nu se ajusteaza numararea +acoperirii. +Ramane un singur efect lateral de tratat, si e pe **adaugare**, nu pe stergere: capul de coloana +„Cantitate comandata” si mesajul „A fost facturata intreaga cantitate comandata” +(`ofacturare.vc2:15144-15150`) nu sunt adevarate pentru liniile libere adaugate langa cele din +comanda. +**PROIECTAT (runda 12) — `docs\cercetare\s4e_lista_preturi_pe_sursa.md`. Reteta din decizia 16 NU se +generalizeaza literal** — si asta e rezultatul principal al proiectarii. + +- **`APPEND FROM` peste cursorul sursei ar strica doua lucruri pe comanda**, ambele tacut: + **(1)** `id_c` e `ROWNUM` per executie Oracle, deci randurile din lista de preturi ar **coliziona** cu + cele ale comenzii, iar `do_sterge` ajusteaza cantitatea `For id_c = poArticol.id_c` (`:14652-14655`) — + ar atinge randul gresit; **(2)** `do_scrie_factura` face `Sum(cantitate)` peste `crsarticole` pe exact + aceste tipuri (`:14332-14338`), iar in `cursor_preturi` **`cantitate` inseamna STOC**, nu „ramas de + facturat" — suma care decide inchiderea comenzii ar fi poluata. +- **De ce merge totusi pe contract azi**, verificat: lista de preturi si articolele contractului sunt + **doua cursoare separate** de la bun inceput (`crsarticole` / `crsarticole1`), iar `cursor_contract` + emite `id_c` ca **`rownum - 10000`** (`PACK_FACTURARE:2722` — confirmat direct pe export de sesiunea + principala), adica autorii au tratat coliziunea de `id_c` ca risc real. In plus, tipurile de contract + **nu au deloc** ramura cu `Sum(cantitate)` in `do_scrie_factura` — cad pe `Otherwise`. Nu e un tipar + de copiat, e o coincidenta favorabila. +- **Solutia curata, verificata fezabila pe cod: liniile libere NU intra deloc in `crsarticole`.** Se + adauga direct in `crsfactura` prin `APPEND BLANK` + `combosql` — tipar **deja existent** in + `frm_facturare_articole2.do_adauga` si pe linia de discount (`ofacturare.vc2:14531`). Atunci `id_c` + ramane `0` implicit, si **`do_sterge` devine no-op prin constructie**, fara nicio modificare de cod. +- **Se aplica identic pe AVIZE (tip 4), nu doar pe comanda** — `cursor_avize` are aceeasi semantica + „ramas de facturat" si aceeasi ramura de `Sum`. Titlul povestii spune „din comanda", perimetrul real + e „orice sursa cu registru". +- **Avertisment care traverseaza in S4f:** pe returul ca document (`8,9,24`) riscul de coliziune `id_c` + **ramane** (`do_sterge` ajusteaza cu semn opus, urmarind maximul returnabil), desi **fara** poluarea + sumei de inchidere. Deci S4f foloseste acelasi mecanism, nu `APPEND FROM`. +- **Validarea de cantitate pe liniile din comanda e satisfacuta prin constructie** — drumul + `do_adauga_articol` → `do_verifica_articol` nu e atins, fiindca liniile libere nu modifica `crsarticole`. +- **Gol real ramas, neacoperit de S4 si S4b:** nu exista mecanism de validare a cantitatii/stocului + pentru un rand ales prin `combosql` — ambele rapoarte se opresc la maparea campurilor. `do_verifica_articol` + nu se poate refolosi ca atare (cere un `poArticol` scatter-uit dintr-un cursor sursa incarcat). + **DECIS — decizia 44 (Marius, runda 13): `poArticol` devine parametru explicit.** Se pastreaza un + **singur loc de validare**: `do_verifica_articol` primeste `poArticol` ca **parametru**, in loc sa + citeasca variabila `Private` populata de apelant. Schimbarea de contract a metodei e **aprobata ca + atare**; consecinta obligatorie e actualizarea **tuturor apelantilor existenti**, inventariati inainte + de prima editare. Aceeasi decizie acopera si `do_alege_stoc` / `frm_articol_gest_factura` din S4f (R7) + — o singura solutie pentru amandoua, cum cerea raportul. + +*Gata cand:* pe o factura la comanda **si pe una din avize** se poate adauga un articol care nu e in +sursa, cu pretul din lista de preturi, si se poate sterge o linie adaugata, fara ca liniile din sursa +sa-si piarda validarea de cantitate **si fara ca suma care decide inchiderea automata sa se schimbe** — +proba directa: aceeasi comanda, cu si fara linii libere adaugate, produce acelasi rezultat de inchidere. +*Depinde de:* S2. **Interactioneaza cu S4 punctul 2** (registrul) si **avertizeaza S4f**. + +#### S4f — Returul in formularul unificat +Decizia 17, proiectata in N. **Partea grea nu e ce credeam.** Factura de retur ca document (tipurile +8, 9, 24) functioneaza deja cap-coada: alegere multipla a facturilor sursa, populare din +`cursor_retur`, gestiune si pret de achizitie mostenite din liniile originale, stergere si retur +partial. Munca e: + +1. **mutarea lui N.1 ca sursa in meniul de adaugare**, fara sa se atinga popularea — acelasi dialog, + aceleasi filtre, acelasi `cursor_retur`; +2. **ridicarea lui N.2 (`But_retur`) la nivel de document**, dupa modelul lui N.1, cu calea + per-articol pastrata; +3. **lista de preturi pe documentele de retur**, prin acelasi `APPEND FROM` ca la S4e. + +Nu se ating: excluderea returului dintr-un retur, filtrarea pe client si valuta, maximul returnabil +calculat pe server, mostenirea gestiunii si a pretului de achizitie. +**DECIS (decizia 22):** linia de retur care nu vine din nicio factura originala **e permisa**, ca pe +orice alt document. Deci punctul 3 nu mai are conditie de intrare. Ce trebuie proiectat in schimb, pe +liniile libere: gestiunea si pretul de achizitie **se aleg** (ca la N.2), nu se mostenesc, iar maximul +returnabil calculat pe server **nu se aplica** — nu exista cantitate originala. +**Afisarea provenientei — VERIFICAT, si raspunsul e „nu la nivel de linie".** Raport: +`docs\cercetare\legatura_linie_retur.md`. Deci **S4f se livreaza fara coloana de provenienta pe linie**; +nu se inventeaza. Ce s-a stabilit, ca sa nu se reia: + +- **Legatura se pierde chiar in cursorul care aduce datele.** `cursor_retur_document` + (`ff_...:3949-4062`) foloseste `V_LISTAID` **doar ca filtru** (`WHERE A1.ID_VANZARE IN (...)`, + `:4054-4055`); in lista de coloane a `SELECT`-ului extern (`:3965-4028`) **nu apar nici + `ID_VANZARE`, nici `ID_VANZARE_DET`**. `crsarticole` nu are de unde sti din ce factura vine randul. +- **`INSERT`-ul in `VANZARI_DETALII` nu are nicio coloana de sursa.** +- **Exista insa o legatura la nivel de DOCUMENT, si nu era cunoscuta in plan: `VANZARI_CORESP`.** + `scrie_corespondente_vanzari(3)` (`ff_...:14834-14836`, din `finalizeaza_factura`) scrie + `(ID_VANZARE_FACT = documentul de retur, ID_VANZARE_AVIZ = fiecare factura sursa, TIP = 3)` + (`:15481-15516`) — **cate un rand per factura sursa aleasa**, nu per linie. Numele coloanei e generic, + reutilizat si pentru perechi aviz-factura (`TIP = 1/2`). +- **Consecinta pentru UI:** cand s-a ales **o singura** factura sursa, se poate afisa corect „documentul + asta de retur provine din factura Y" — la nivel de antet, nu de linie. Cand s-au ales **mai multe** + (selectie multipla, suportata explicit de dialog), `VANZARI_CORESP` da **multimea** de facturi + posibile, deci nici macar antetul nu poate arata o sursa unica. **De asezat in UI ca informatie de + document, niciodata ca proprietate de linie.** +- **N.2 (`But_retur`) nu scrie nicio legatura.** `listaid` (perechi `ID_ARTICOL:ID_VANZARE`) e folosit + in `pack_facturare` (`ff_...:8142-8212`) exclusiv ca filtru pe `RUL`, ca sa calculeze cantitatea inca + disponibila din acea vanzare. Sursa nu ramane atribut al liniei noi. +**PROIECTAT (runda 12) — `docs\cercetare\s4f_retur_formular_unificat.md`**, cu **trei corectii** fata de +textul de mai sus: + +- **Punctul 1 se restrange.** Alegerea facturilor sursa **nu se muta** in bara de butoane — ramane la + antet / in sectiunea pliata (confirmat impotriva `s4b_bara_butoane_meniu.md` §9.3 si a tabelului de + meniu de acolo). Ce se muta in meniul de adaugare e doar **„adauga tot din facturile alese"** si + **„alege liniile"**. +- **Un risc concret, netratat nicaieri pana acum (R1):** `frm_articol_gest_factura` + (`ofacturare.vc2:4094-4103`) face, sub `If Thisform.lRetur`, `poArticol.pretftva = pretv` — adica + **suprascrie pretul de vanzare cu pretul din stoc pe ORICE linie de retur** (verificat direct de + sesiunea principala). Corect pentru liniile **mostenite** dintr-o factura sursa; **gresit pentru + liniile libere** permise de decizia 22, care trebuie sa pastreze pretul din lista de preturi. + Raportul propune fixul. +- **Distinctia „linie libera" vs. „linie mostenita" nu cere camp nou: `crsfactura.id_c = 0`.** Oracle nu + produce niciodata `id_c = 0` din `ROWNUM`, deci valoarea implicita e ea insasi semnalul. Vine din + mecanismul impus de S4e: liniile libere intra direct in `crsfactura` (`APPEND BLANK` + `combosql`), + **nu** in `crsarticole` — asa ca ajustarea din `do_sterge` (care pe retur urmareste maximul returnabil) + devine **no-op prin constructie**. Acelasi semnal dezactiveaza si suprascrierea de pret de mai sus. +- **Gol nou (R7), pe care S4e nu-l are** (comanda si avizele n-au dialog de gestiune): azi se intra in + `do_alege_stoc` / `frm_articol_gest_factura` **doar** dintr-un `poArticol` scatter-uit din + `crsarticole`. O linie libera din `crsfactura` nu are asa ceva, iar decizia 22 cere ca gestiunea **sa + se aleaga**. Deci trebuie un **punct de intrare nou** in dialog. **Se unifica cu golul echivalent din + S4e** (validarea cantitatii, `do_verifica_articol`): e aceeasi problema structurala — dialogurile sunt + cuplate de `crsarticole` —, deci merita **o singura solutie**, nu doua. + **INCHIS prin decizia 44** (runda 13): `poArticol` devine **parametru explicit** si aici, nu variabila + `Private` populata de apelant — aceeasi solutie ca in S4e, cum cerea raportul. +- **R4 e inchis** (verificare independenta): `scrie_corespondente_vanzari(3)` e gatata pe `ntip IN (8,9)`, + nu pe `listaid`. +- **`VANZARI_CORESP` nu e afectata de liniile libere** — se scrie din `poDate.listaid`, fixat la antet. + **De confirmat (R4):** pentru `But_retur` ridicat la nivel de document, raspunsul pare a fi „fara + corespondenta persistata", pentru ca scrierea e gatata pe `ntip IN (8,9)`, nu pe `listaid`. + +*Gata cand:* un document de retur deschis in formularul unificat aduce liniile facturilor alese cu +aceleasi valori ca azi — inclusiv gestiunea si pretul de achizitie —, permite stergere si cantitate +partiala, iar pe o factura normala se poate face retur alegand facturile o singura data. **In plus: o +linie libera pe un document de retur isi pastreaza pretul din lista de preturi**, adica nu trece prin +suprascrierea de la `:4094-4103`. Pasii cu criterii verificabile sunt la sectiunea 9 a raportului; ce nu +se poate testa headless, la sectiunea 11; riscurile R1-R6, la sectiunea 12. +*Depinde de:* S2, S4e. + +#### S4g — Adaugarea de articole la modificarea oricarui document, inclusiv auto +Decizia 18, proiectata in O si O-bis. **Nu se porneste de la zero:** „Alte servicii” din ROAAUTO +face deja jumatate — articole reale langa linii sintetice, cu pret tastat, fara gestiune — doar ca +traieste in formularul de emitere si moare odata cu el. Se generalizeaza in formularul unificat, ca +a doua sursa din meniu („Alege din nomenclator…”), disponibila **si la modificare**, pe orice tip de +document, nu doar pe cele auto. +**Ordinea in care se lucreaza**, ca sa nu se blocheze tot: intai pe documentele ROAFACTURARE, unde +scrierea e a noastra; abia apoi pe `tip = -12`. +**Decizia 20 se aplica aici:** din nomenclator se ofera **si articole gestionabile, si +negestionabile** — nu se copiaza filtrul `in_stoc = 0` al lui ROAAUTO. +**Blocantul real e contul de venit, si el priveste doar ramura „Alege din nomenclator…”** (J-bis, +J-ter). Contul de venit vine din `NOTE_CONTABILE.SCC` **prin politica de pret**; un articol fara +`ID_POL` nu ajunge la cont gol, ci **la eroare** — `contabilizeaza_articol` ridica `FACT-024` si +opreste tranzactia. Pe ramura „Cauta in lista de preturi…” nu se schimba nimic: acolo politica exista. +**Premisa de la care pornea povestea asta a cazut:** „Alte servicii” din ROAAUTO **nu face deja +jumatate** din treaba — acele linii ocolesc complet `contabilizeaza_articol`, printr-o cale de +facturare paralela care nu genereaza nota de venit. Nu e un mecanism de generalizat, e o exceptie. +**DECIS (decizia 27, care inlocuieste 24):** contul de venit se **deriva**, nu se ia prin politica — +din `CORESP_CONT_VENCHELT` pentru articolele gestionabile (pe contul de gestiune al liniei), din +`NOM_ARTICOLE.CONT` daca e 6xx / 7xx pentru cele negestionabile, altfel **704**. **Derivarea se face +in VFP**, dar **transportul s-a schimbat la decizia 34**: contul calculat se trimite **direct, ca +parametru nou al lui `contabilizeaza_articol`**. Ocolul prin `id_pol` cu nota potrivita — politica +tehnica, interogarea inversa pe `SCC`, `pack_preturi.adauga_politica_pret_art` — e **abandonat**; +J-quater punctul 3 se citeste doar ca trasabilitate. **Decizia 35** adauga ca acelasi drum serveste si +editarea prin regenerare, deci nu se proiecteaza aici o a doua ruta de contare. +~~**De decis in aceasta poveste, nu inainte:** ce se intampla cand linia are **si** politica, **si** +cont trimis din VFP — cine castiga.~~ **PROIECTAT (runda 13) — +`docs\cercetare\s4g_adaugare_articole_modificare.md`.** Intrebarile despre politica tehnica in ecranul +de cautare a politicilor **au disparut** odata cu reteta. + +**Raspunsul: niciuna dintre cele trei variante pure — parametrul castiga, dar combinatia ambigua e +oprita explicit, nu rezolvata tacit.** +- **Combinatia nu poate aparea in fluxul normal, si asta e dovedit, nu presupus.** `crsfactura.id_pol` + e `N(20) Null` (`COMUN\programe\ofacturare_comun.prg`, `creeaza_facturacrs` — **verificat direct**), + deci la `APPEND BLANK` ramane `.NULL.`, si ajunge la Oracle ca literalul `NULL` + (`ofacturare.vc2:14072`). Cursorul de cautare din nomenclator **n-are deloc coloana `id_pol`**, deci + nu exista punct in care o linie „din nomenclator" sa-l poata popula. +- **Singurul scenariu real de ambiguitate e regenerarea** (decizia 35): daca derivarea contului ar rula + **necondiționat** pe toate liniile documentului, si nu doar pe cele fara `id_pol`, o linie cu politica + reala ar primi si cont derivat. **E un risc de implementare VFP, nu Oracle** — dar proiectarea Oracle + nu trebuie sa-l faca invizibil. +- **Deci: `cont_venit IS NOT NULL` intra pe ramura noua; daca in acel moment `id_pol` e si el populat, + se ridica eroare** (cod nou, distinct de `FACT-024`, care ramane pentru cazul „nici politica, nici + cont"). Variantele „politica castiga" si „fallback la `NO_DATA_FOUND`" au fost respinse motivat: + prima defineste castigatorul pe **prezenta** campului, nu pe rezolvarea lui, si ar reintroduce + `FACT-024` acolo unde VFP a oferit deja o solutie; a doua e semantic cea mai curata, dar cere + **restructurarea interna** a functiei (un flag propagat din `EXCEPTION` pana la punctul de decizie), + fata de un singur `IF` la intrarea in ramura deja proiectata. **Ambele ascund o eroare de date in loc + s-o semnaleze.** +- **Regresie zero pe apelantii de azi**, prin constructie: cand `cont_venit` e `NULL` — adica toti + apelantii existenti, care nu cunosc parametrul —, executia intra direct pe `ELSE`, garda nu se + evalueaza niciodata, comportamentul e identic cu cel de azi. +- **De ales de Marius:** numarul concret al codului de eroare nou (`FACT-0xx`). +Separat, contul de **gestiune** e rezolvat ca regula (decizia 21): fallback, nu `NULL`, nu refuz. +**`pack_auto` nu mai e blocant** — `PACK_AUTO` nu citeste `VANZARI` / `VANZARI_DETALII` deloc (O, +intrebarea 1). Desincronizarea de afisare e **acceptata** (decizia 28); conditia e MANOPERA si +MATERIALE conform devizului. +**Blocantul netehnic a cazut si el:** gridul read-only din `frm_modific2024` e al lui #6, dar #13 +incepe dupa ce #6 se termina (decizia 30). +**Restul proiectarii S4g, pe scurt** (detaliile in raport): +- **Suprafata pe Oracle**: `contabilizeaza_articol` primeste contul prin `VANZARI_DETALII_TEMP%ROWTYPE` + (`cont_venit`), deci semnatura functiei ramane aceeasi — se extinde **tipul de rand**. Consecinta de + livrare: **DB inainte de EXE**. +- **Derivarea in VFP** urmeaza fix decizia 27: `CORESP_CONT_VENCHELT` pentru gestionabile (pe contul de + gestiune al liniei), `NOM_ARTICOLE.CONT` daca e 6xx/7xx pentru negestionabile, altfel **704**. Ruleaza + **doar pe liniile fara `id_pol`** — vezi garda de mai sus; aici se leaga cele doua. +- **Fluxul in formular** refoloseste exact mecanismul lui S4e: linia „din nomenclator" intra prin + `APPEND BLANK` + `combosql` **direct in `crsfactura`**, niciodata in `crsarticole`. +- **Ramane deschis, mostenit din S4e:** validarea de cantitate/stoc pentru linia libera — se rezolva + prin decizia 44 (`poArticol` ca parametru explicit), aceeasi solutie pe ambele surse. +- **De re-rulat inainte de implementare, nu de presupus incheiat:** cautarea apelantilor lui + `adauga_articol_factura` in restul suitei (ROAGEST, ROAAUTO, ROAACNPRO, ROAIMOB). Risc asteptat zero + — folosesc proceduri separate sau alt pachet —, dar cautarea **nu s-a terminat in nicio runda**. +- **Trei hardcodari raman de confirmat de Marius, nu de presupus inofensive:** `CU_TVA = 1` (are efect + masurat prin `nproc_tva_max`, pe linie scutita + discount global), numele cheii de optiune de firma + pentru `SCD` (decizia 36) si domeniul ei `PROGRAME`, si trasabilitatea `CONT_VENIT` pe + `VANZARI_DETALII` (optionala pentru functionare, dar fara ea coloana nu se pastreaza dupa fapt). + +**Nota de executie (decizia 60):** pe documentele de tip **48/49** (custodie), „Alege din +nomenclator…" ramane restrans la articole `IN_STOC = 0`, la fel ca ramura „Cauta in lista de +preturi…" (S4) — decizia 20 (si articole gestionabile, si negestionabile) **nu se aplica** pe +tipurile 48/49. Altfel se rupe invariantul pe care se sprijina siguranta regenerarii la editarea +custodiei (decizia 60, `docs\cercetare\custodie_48_49_stergere_reemitere.md`). + +*Gata cand:* pe un document deja emis se poate adauga o linie noua, din lista de preturi sau din +nomenclator, si documentul se salveaza consistent — pe fluxurile ROAFACTURARE, cu partea auto +livrata separat, dupa (1). Pe un document 48/49, „Alege din nomenclator…" nu ofera si nu permite +adaugarea unui articol cu `IN_STOC <> 0`. +*Depinde de:* S4e, si de deciziile de mai sus. **Nu blocheaza etapa I.** + +#### S5 — Acoperirea tuturor tipurilor +`frm_facturare_articole.Init` are 20 de ramuri pe `poDate.tip` (`:15107-15250`) care schimba titlul, +capul coloanei de cantitate, mesajul de stoc, vizibilitatea discountului, prezenta seriei. Toate +trebuie sa existe si in formularul unificat. Tipurile speciale raman pe calea lor: +`tip = 27` (`frm_avizare_lucrare`), `tip = 30` (aviz din NIR, formular nevizibil). +**PROIECTAT (runda 12) — `docs\cercetare\s5_acoperire_tipuri.md`. Povestea e mult mai mare decat +„porteaza cele 20 de ramuri", si motivul e ca baza de pornire aleasa nu are aproape nimic de portat.** + +- **`Do Case`-ul de azi acopera 21 de valori de `tip`** (in 14 ramuri). **Patru tipuri reale, + reachable prin `factureaza()`, nu intra in niciun `Case`: `45` (factura restaurant), `48` / `49` + (custodie), `52` (contract, factura fiscala valuta)** — pierd titlu, cap de coloana, mesaj de stoc, + vizibilitatea discountului si eliminarea coloanei `cSerie`. **Nu doar `52`**, cum semnalase S4b. + Rutarea cursorului le recunoaste (`ofacturare.prg:271-282`); doar `Init` nu le-a „prins" niciodata. + **Nota de executie (decizia 60):** randul de configurare pentru `48`/`49` trebuie sa pastreze + restrictia la articole `IN_STOC = 0` mostenita de la sursa lor de azi (`cursor_articole_k`, + `PACK:3595-3701`, `:3695`) — vezi notele echivalente la S4 si S4g; e conditia de siguranta pentru + regenerarea la editarea custodiei (decizia 60, + `docs\cercetare\custodie_48_49_stergere_reemitere.md`). +- **Cinci tipuri (`43,44,46,50,51`) nu ajung deloc la acest formular** — zero potriviri in tot arborele + `D:\ROA`. `50` e marcat „in lucru" in sursa. Se declara explicit ramase in afara. +- **Descoperirea care schimba estimarea: prototipul nu e o versiune partiala a `Do Case`-ului — e + aproape gol.** `frm_facturare_articole2.Init` (`:18988-19080`) alege pe tip **doar** cuvantul + „factura"/„aviz", pe o lista mai scurta (lipseste `24`). Nu seteaza titlu (nu exista + `lb_titlu_alb_b121` in tot prototipul), nu schimba capul coloanei, nu schimba mesajul de stoc, nu + ascunde discountul, si **n-are deloc conceptul de coloana `cSerie`**. Daca formularul unificat porneste + de la prototip — cum decide S1 —, **toata diferentierea pe tip se reconstruieste de la zero**, nu se + completeaza. Asta e cel mai mare cost ascuns al etapei I descoperit pana acum. +- **Rutarea cursorului diverge pe TREI tipuri, nu unul:** pe prototip, ramura de contract e + `Inlist(tnTip, 2, 26, 6)` (`ofacturare.prg:762`) fata de `Inlist(tnTip, 2, 26, 6, 52)` pe standard + (`:283`) — **verificat direct de sesiunea principala** —, iar ramura de retur omite `24`. Pe aceste + tipuri, prin prototip, `lcSqlCursor` ar ramane **nedefinit**: eroare, nu doar comportament diferit. +- **Punctul lasat deschis de S4 punctul 2 se inchide aici:** tipurile **`26` si `52` n-au niciun + bookkeeping** `crsarticole` — nici Rol A, nici Rol B. Excluderea e totala, pe `Do Case` exhaustiv fara + ramura implicita, deci e „dovedit absent", nu „neconfirmat". +- **`30` nu e un formular separat**, cum spunea planul: e **acelasi `frm_facturare_articole`**, trecut + prin acelasi `Init`, dar niciodata aratat (`ofacturare.prg:444-453` — calculeaza totalurile, apasa + programatic `but_termin1.Click()`, apoi `Release()`, fara `Show()`). Deci **e afectat de golurile din + `Do Case`** ca oricare alt tip; doar ca defectele nu se vad pe ecran. `27` chiar ramane pe calea lui. +- **Alegerea de proiectare centrala, recomandata: tabel de configurare per tip**, un rand per `tip`, cu + exact proprietatile pe care le seteaza azi `Do Case`-ul (titlu, cap cantitate, mesaj stoc, discount + vizibil, are serie, tip doc, butoane, grup-sursa pentru meniul S4b). Motivul nu e estetic: **un + `Do Case` fara `Otherwise` nu semnaleaza niciodata un tip lipsa** — exact mecanismul care a lasat + patru tipuri pierdute ani la rand. Cu tabel, un tip necunoscut devine **eroare la deschidere**, iar + completitudinea se verifica **mecanic**, cu un `SELECT` fata de `tipuri_documente_facturare.md`, fara + sa porneasca formularul. Variantele „completeaza `Do Case`-ul" si „metoda per grup" au fost respinse + motivat: amandoua raman implicite, deci nu adauga detectie. +- **DECIS — decizia 48 (Marius, runda 13): tabelul e un CURSOR GENERAT IN COD la pornire**, nu `DBF` + static (recomandarea raportului) si nu tabela pe Oracle. Ambele au fost puse pe masa cu argumentele + lor si respinse: `DBF`-ul adauga un fisier de intretinut, de livrat la fiecare update si de tinut + sincron intre produsele ROA; tabela Oracle ar cere script de migrare si o citire in plus la + deschiderea formularului. **Ce NU se pierde prin alegerea asta:** detectia ramane intacta — tip + necunoscut = **eroare la deschidere**, exact castigul pentru care exista propunerea —, iar proba + mecanica de completitudine ramane posibila, doar ca ruleaza **din aplicatie**, nu cu un `SELECT` + din afara. **Ce se accepta:** orice tip nou de document cere **recompilare si versiune noua de exe**, + ca azi. Forma concreta: un `.prg` cu `CREATE CURSOR` + `INSERT`-uri, un rand per tip, incarcat o + singura data si citit de `Init` prin `SEEK` pe `tip`. + +*Gata cand:* fiecare tip din `COMUN\docs\tipuri_documente_facturare.md` fie e acoperit, fie e +declarat explicit ramas pe calea veche — **verificat prin proba mecanica de la sectiunea 8 a raportului**, +nu prin citire. Include cele patru tipuri lipsa azi (`45,48,49,52`) si alinierea rutarii de cursor intre +standard si prototip. Pe `48`/`49`, in plus: restrictia `IN_STOC = 0` mostenita ramane activa (decizia +60) — nu se poate adauga un articol gestionabil. +*Depinde de:* S4. + +#### S5b — Proforma si copierea pe formularul unificat +Deciziile 10 si 11. Amandoua vin **aproape gratuit**, pentru ca folosesc deja acest drum: proforma e +o valoare in combo-ul de tip document, copierea e `factureaza(tip, toFactura)` cu antetul +precompletat. De facut, concret: +- combo-ul `Ct_clb_fdoc` ramane in antetul unificat, cu realocarea de serie si numar la comutare; +- garzile `eProforma = 0` de pe notele contabile si atasamente raman intacte, iar raportul propriu de + proforma continua sa fie ales; +- copierea deschide formularul in starea „document nou”, cu antetul editabil (comportamentul de azi); +- **degradarea de tip din `do_copiaza` ramane pentru copiere si nu se aplica la regenerare** (D) — + sunt doua moduri distincte ale aceluiasi formular, nu acelasi comportament. +*Gata cand:* o proforma emisa din formularul unificat nu produce nota contabila si se listeaza pe +raportul ei; o copie produce un document nou cu numar nou si acelasi continut. +**VERIFICAT (runda 9) — `docs\cercetare\s5b_proforma_descarcare_gestiune.md`. Verificarea 4 e inchisa.** +**NU, gestiunea nu se descarca pe proforma** — pentru niciun articol, indiferent daca e gestionabil in +nomenclator. Si nu e un efect colateral al ascunderii stocului, e **prin design**: VFP marcheaza toate +liniile proformei negestionabile inainte de compunerea documentului, ceea ce le trimite cu sentinela +`id_gestiune = -1000`, iar pe Oracle `contabilizeaza_articol` sare apelul catre `descarca_gestiune` +exact pe acest sentinel. Intentia e explicita in changelog (12.03.2021 / 2.7.x): *„Articolele din +proforma sunt marcate 'negestionabile', astfel incat nu mai este necesara existenta articolelor in stoc +pentru a genera proforma."* — deci cerinta a fost „proforma trebuie sa mearga si fara stoc", nu „nu arata +plafonul". **Se corecteaza presupunerea din plan** ca zeroizarea `gestionabil` ar fi doar cosmetica. +Consecinta pentru S5b: sentinela `-1000` e contractul care trebuie pastrat — vezi sectiunile 6 si 7 ale +raportului pentru constrangerile de proiectare si ce lipseste la copiere. + +**PROIECTAT (runda 12) — `docs\cercetare\s5b_proiectare_proforma_copiere.md`. „Aproape gratuit" nu mai +tine: proiectarea a scos la iveala un risc de date care nu era semnalat nicaieri.** + +- **Mecanismul real care tine proforma curata nu e sentinela, ci routing-ul.** In `do_scrie_factura` + (`ofacturare.vc2:14282-14300`), `eProforma = 1` alege `scrie_proforma`, care **nu cheama niciodata** + `contabilizeaza_articol` — comentariul din cod o spune direct: *„salveaza doar in vanzari, nu si in + contabilitate"* (verificat de sesiunea principala). Sentinela `-1000` e al doilea strat, nu primul. +- **RISCUL CENTRAL — „drumul invers", nesemnalat pana acum si cu consecinta pe date.** Operatorul + adauga linii cu documentul pe **Proforma** (deci marcate `gestionabil = 0` / `id_gestiune = -1000`), + apoi comuta combo-ul inapoi pe **Factura** inainte de „Termina". Atunci routing-ul alege + `scrie_factura2`, care **chiar** cheama `contabilizeaza_articol` — dar acesta sare `descarca_gestiune` + exact pe sentinela `-1000` (`PACK:7472-7476`). Rezultatul: **o factura reala iese cu stocul + nedescarcat, silentios** — `-1000` e o valoare valida, nu ridica nicio exceptie. **Azi nu exista + nicio plasa pentru acest caz**, si nici nu putea exista: combo-ul traieste in dialogul separat + `frm_date_factura`, inchis **inainte** sa existe vreo linie. Riscul se naste **din unificare**. +- **De aici si de ce marcarea negestionabila nu mai poate fi un singur `UPDATE` de masa:** in + formularul unificat trebuie extrasa intr-o metoda refolosita din **trei** puncte — la incarcare, la + adaugarea unei linii, si la comutarea tipului cu linii deja prezente. +- **`id_c` la copiere: SIGUR, cu dovada.** `do_copiaza` degradeaza tipul spre grupul-tinta + **`{1,5,7,10,22,23}`**, care nu intra niciodata in ramurile Rol A din `do_scrie_factura` / `do_sterge`. + Coliziunea tehnica exista in date, dar **n-are efect observabil**. (Intrebarea venea din corectia lui + S4e — vezi acolo.) + > **CORECTIE (runda 13, verificata direct pe cod).** Grupul-tinta scris pana acum in plan + > (`{1,5,10,22}`) era **gresit**, si la fel era si `{1,5,7,10}` din raportul S5c. Setul real e cel de + > mai sus, citit din primul `CASE` al lui `frm_facturi.do_copiaza` + > (`COMUN\clase\ofacturare_comun.vc2:3693-3694`), care lasa neatinse exact `T1,T5,T7,T10,T22,T23`. + > Concluzia „copierea e sigura" **nu se schimba** — `7` si `23` nu au bookkeeping Rol A —, dar cifrele + > se corecteaza peste tot unde apar. Aceeasi corectie se aplica lui + > `s5b_proiectare_proforma_copiere.md` §2.1 / §7. +- **DEFECT PREEXISTENT, gasit in trecere la verificarea de mai sus — de raportat, NU de reparat acum.** + In `frm_facturi.do_copiaza`, ramura de avize scrie `lnTip = T22` + (`COMUN\clase\ofacturare_comun.vc2:3703`) in loc de `loFactura.tip = T22` — **singura ramura din tot + `Do Case`-ul care nu atribuie in obiect**; toate celelalte cinci scriu `loFactura.tip`. Consecinta: + la copierea unui aviz (`21,24,26,30,-7,-9,-10,-13,28,29,42`) **degradarea nu se produce**, iar + `copiere_factura` cheama `factureaza(toFactura.Tip, ...)` + (`COMUN\programe\oproceduri_facturare.prg:150-152`) cu tipul **original** — deci copia unui „aviz pe + baza de comanda" reintra pe ruta de comanda, nu pe cea de lista de preturi. **Verificat ca `factureaza` + nu citeste un `lnTip` privat** (`ofacturare.prg:101` declara `lnTipTemp`, nu `lnTip`), deci + atribuirea chiar se pierde; in plus `lnTip` e nedeclarat in metoda, deci poate suprascrie un `lnTip` + al apelantului. **Fisierul e in perimetrul interzis (#6) — se raporteaza, nu se atinge.** + +**VERIFICAT (runda 12, la cererea lui Marius) — `docs\cercetare\verif_proforma_alegere_stoc.md`. +Pe proforma NU se alege stoc azi, si asta e deliberat.** + +- **Un singur mecanism activ, si e in VFP, la incarcare:** `ofacturare.prg:333-336` face + `UPDATE (lcCursor) SET gestionabil = 0` cand `eProforma = 1`, **inainte** sa se deschida gridul. + Comentariul din cod spune intentia direct: *„Daca este o proforma, consider toate articolele + negestionabile, **pentru a nu mai alege din stoc**"* (12.03.2021). Deci `Do Case`-ul de la + `ofacturare.vc2:13803` vede mereu `gestionabil = 0` → **`do_alege_stoc` nu ruleaza niciodata**. + Valabil pe **toate** cursoarele de creare directa a unei proforme; **niciunul** n-are parametru + `V_PROFORMA` (verificat pe ~9 proceduri `cursor_` din pachet). +- **Mecanismul Oracle exista, dar e mort in fluxul curent:** `cursor_retur_document` (`PACK:3993-4000`) + are `CASE V_PROFORMA = 1 THEN 0`, insa se cheama doar la **copiere**, unde `V_PROFORMA` trimis e + `eProforma` al documentului **nou** — mereu `0`. **Nu e o contradictie intre rapoarte**: sunt doua + mecanisme reale, doar unul activ. +- **`pret_achizitie` depinde de sursa:** pe calea principala (lista de preturi) ramane `0` — + `cursor_preturi` nici nu-l selecteaza. Pe surse care carata un document existent (avize, copiere) + vine real din `VANZARI_DETALII.PRET_ACHIZITIE`. +- **Daca liniile ar pastra gestiunea reala pana la salvare, nu s-ar strica nimic pe contabilizare sau + stoc:** `adauga_articol_factura` se cheama oricum si pentru proforma, dar `scrie_proforma` **nu** + cheama `contabilizeaza_articol`, deci `descarca_gestiune` tot n-ar rula; iar **`do_alege_stoc` nu + rezerva stoc** (doar `SELECT` + scadere locala in memorie, zero scriere Oracle). Singurul loc unde + s-ar vedea o diferenta e la **relistare** (`crsDetaliiListare` / `fact_vfacturi2` citesc + `id_gestiune` fara filtru pe `eproforma`) — **neconfirmat** daca vreun raport chiar il tipareste. + +**Consecinta care schimba forma deciziei: cele doua sensuri nu sunt simetrice.** +- **FACTURA → PROFORMA cu linii deja adaugate:** liniile au trecut deja prin `do_alege_stoc`, deci au + gestiune reala si pret de achizitie corect. **E sigur, si sentinela se poate aplica abia la salvare** + — exact varianta ceruta de Marius. **Fara atentionare.** +- **PROFORMA → FACTURA cu linii deja adaugate:** liniile au fost adaugate **fara** alegere de stoc + (`gestionabil` fortat `0`), deci **nu au gestiune**. Aici e riscul „drumului invers". +- **A forta alegerea stocului si pe proforma NU e o optiune** — ar regresa cerinta din 12.03.2021 + (*„nu mai este necesara existenta articolelor in stoc pentru a genera proforma"*): un articol fara + stoc n-ar mai putea intra pe proforma, pentru ca `do_alege_stoc` ar cere un lot inexistent. + +### Decizia 43 (Marius, runda 12) — proforma NU alege stoc, si factura se face DIN proforma + +**Pe proforma nu se alege stoc, si asta e cerinta, nu efect colateral.** Motivul, in cuvintele lui +Marius: *„sa dau o proforma chiar si in absenta stocului, pentru ca ma intereseaza doar pretul de +vanzare, nu si cel de achizitie din stoc"*. Deci: +- **Varianta (b) — realegerea gestiunii linie cu linie la comutare — e RESPINSA.** Nu se mai + reargumenteaza. +- **Comutarea PROFORMA → FACTURA cu linii prezente se blocheaza**, cu mesaj explicit. Comportamentul de + azi (`gestionabil = 0` fortat la incarcare, `ofacturare.prg:333-336`) se **pastreaza**, nu se rafineaza. +- **Sensul FACTURA → PROFORMA ramane liber**, fara atentionare, cu sentinela aplicata **la salvare** — + liniile au deja gestiune reala, deci nu se pierde nimic. + +**Cerinta noua: „ulterior o sa vreau si o factura din proforma".** Nu prin comutarea tipului pe acelasi +document, ci ca **document nou**, generat din proforma. **Mecanismul pare sa existe deja pe calea de +copiere** — `cursor_retur_document` reface gestionabilitatea reala cand `V_COPIERE = 1` +(`GESTIONABIL = B.IN_STOC`, `PACK:3993-4000`), deci liniile venite dintr-o proforma **redevin +gestionabile**, trec prin `do_alege_stoc` la adaugare, primesc `id_gestiune` real si descarca gestiune +normal (`docs\cercetare\s5b_proiectare_proforma_copiere.md` §2.6). **De verificat inainte de a te baza +pe asta**, pentru ca §2.6 descria copierea in general, nu cazul „sursa e o proforma": +1. poate fi azi o **proforma** aleasa ca sursa de copiere, sau e exclusa undeva? +2. `do_copiaza` degradeaza tipul spre `{1,5,10,22}` — ce tip rezulta dintr-o proforma si e cel dorit? +3. ~~se pastreaza legatura proforma → factura?~~ **RASPUNS (Marius, runda 12): DA — „ar fi bine sa aiba + urma sursei, la fel ca factura din aviz".** Deci trasabilitatea **e ceruta**, si tiparul de urmat e + cel deja existent pentru aviz → factura: `scrie_corespondente_vanzari(1)`, chemata din + `finalizeaza_factura`, care scrie in **`VANZARI_CORESP`** perechea + `(ID_VANZARE_FACT = factura, ID_VANZARE_AVIZ = documentul sursa, TIP = 1)`. Numele coloanei e generic, + deja reutilizat pentru mai multe feluri de perechi (`TIP = 1/2` aviz-factura, `TIP = 3` retur), deci + **structura nu se schimba — se adauga o valoare noua de `TIP`** pentru proforma → factura. + **De proiectat, nu de presupus:** ce valoare de `TIP` se aloca; de unde stie fluxul de copiere ca + sursa a fost o proforma (azi `do_copiaza` degradeaza tipul, deci informatia s-ar putea pierde + **inainte** de scriere — vezi punctul 2); si daca `marcheaza_facturat` / `FACTURAT` trebuie sau nu + atinse (pe aviz sunt, pe proforma probabil **nu**, pentru ca proforma nu e un document de livrare — + **de confirmat, e exact genul de detaliu care se copiaza gresit din tiparul avizului**). +~~**Aceasta e prima sarcina de proiectare a rundei 13.**~~ **LIVRATA — vezi S5c mai jos.** + +*Depinde de:* S5. + +#### S5c — Factura din proforma (decizia 43, cerinta noua) +**PROIECTAT (runda 13) — `docs\cercetare\s5c_factura_din_proforma.md`. Toate cele trei intrebari si +ambele capcane au raspuns cu dovada. Vestea buna: mecanismul de baza chiar exista, si nu se atinge.** + +- **(a) O proforma poate fi azi aleasa ca sursa de copiere, fara nicio excludere.** `IsCopy` returneaza + **necondiționat `.T.`** (`COMUN\clase\ofacturare_comun.vc2:4969`, cu comentariul explicit *„POT SA + COPIEZ ORICE, TRATEZ TIPURILE IN DO_COPIAZA"*; filtrul vechi pe tip e comentat dedesubt, la `:4971`) — + **verificat direct**. Nici vizibilitatea butonului, nici filtrul de grid „Facturi&Avize / Proforme" nu + se uita la `eproforma`. Deci nu e nimic de deblocat. +- **(b) Tipul rezultat e corect, fara schimbare.** Copierea produce un document real + (`nIdTipDoc = 5`, `eproforma = 0`) — factura fiscala, exact ce trebuie. Degradarea lasa neatins + grupul-tinta **`{1,5,7,10,22,23}`** (vezi corectia de cifre de mai sus). +- **(c) `TIP = 4` e liber in `VANZARI_CORESP`** si se aloca pentru „factura din proforma". Confirmat + **cod + date** pe schema vie: tabela are un **singur scriitor in toata baza** + (`pack_facturare.scrie_corespondente_vanzari`), iar azi se folosesc doar `1/2/3`. **Structura nu se + schimba.** +- **Capcana (i) — confirmata, si e miezul poveștii.** `id_vanzare` al proformei **supravietuieste** + copierii (prin `poDate.listaid`), dar faptul ca **sursa era o proforma se pierde**: + `completeaza_setari_document` copiaza `.listaid`, dar **nu** si `eproforma` + (`COMUN\programe\ofacturare_comun.prg:387` — **verificat direct: `eproforma` nu apare nicaieri in tot + fisierul**). De aceea legatura **nu se poate agata de `CASE`-ul din `finalizeaza_factura`**, care e + cheiat pe `ntip` — `ntip` nu poarta distinctia. Solutia: un semnal nou, client-side + (`poDate.lProformaSursa`), capturat exact acolo unde informatia mai exista, plus un **apel Oracle + explicit separat** care reutilizeaza `scrie_corespondente_vanzari(4)` **neschimbata**. Zero cod PL/SQL + nou pe calea recomandata. +- **Capcana (ii) — INFIRMATA presupunerea comoda, cu patru argumente: `marcheaza_facturat` NU se cheama + pe proforma.** Proforma n-are `VANZARI_CANTITATI`, n-are ramura de reversare la stergere in + `sterge_factura`, nimic nu filtreaza dupa `FACTURAT` pe proforme, si oricum calea aleasa e in afara + `CASE`-ului care il cupleaza azi. Era exact detaliul care se copia gresit din tiparul avizului. + +**Ce NU se schimba:** `IsCopy`, vizibilitatea butonului de copiere, filtrul de grid, degradarea de tip +din `do_copiaza`, `cursor_retur_document` (`GESTIONABIL = B.IN_STOC`), si `scrie_corespondente_vanzari` +insasi. + +**INCHISE, nu de reintrebat (marcaj pus in runda 17).** Cele cinci puncte de mai jos au primit raspuns: punctul 1 (`TIP = 4`) prin **decizia 51**, restul in bloc prin **decizia 56** („da la toate"). Lista ramane ca **inventar al recomandarilor acceptate**, ca sa se stie ce s-a decis si de ce — nu ca intrebari: +1. **`TIP = 4`** — liber azi, dar alocarea e **ireversibila in date** odata intrata in productie. De + confirmat explicit, nu tacit. +2. **Apel pe starea de sesiune a pachetului (`clistaid` / `nid_vanzare`) vs. procedura noua cu + parametri expliciti.** *Recomandarea raportului:* varianta simpla (zero cod Oracle nou), **cu + conditia** verificata la implementare ca niciun apel Oracle intercalat nu reseteaza starea intre + scrierea facturii si scrierea corespondentei. +3. **Aceeasi proforma poate fi copiata de N ori**, fiecare copie cu randul ei `TIP = 4`. E comportamentul + implicit al oricarei copieri de azi. *Recomandare:* daca deranjeaza, avertisment — **nu** blocare. +4. **Garda simetrica la stergere** (*„nu poti sterge o proforma care are deja factura generata din ea"*), + pe tiparul `TIP IN (1,2,3)` din `sterge_factura` — de decis daca se doreste. +5. **Afisarea „provine din proforma X"** pe factura noua — vine gratis din legatura scrisa, la nivel de + **document** (ca la retur), nu de linie. De decis daca intra acum sau mai tarziu. + +*Gata cand:* dintr-o proforma emisa se genereaza o factura reala, cu numar nou, care descarca gestiunea +normal, are rand `TIP = 4` in `VANZARI_CORESP` catre proforma sursa, iar proforma **nu** e marcata +`FACTURAT`. +*Depinde de:* S5b. + +### Decizia 49 (Marius, runda 13) — cele doua formulare merg IN PARALEL, si ALEGE UTILIZATORUL + +**Cerinta, in cuvintele lui Marius:** *„utilizatorul sa poata accesa alternativ, daca doreste"*. Deci +**nu** o setare care ruteaza tacit pe un flux sau altul, si **nu** un pilot pe cativa oameni: ambele +formulare raman accesibile, iar **alegerea o face omul, in momentul in care factureaza**. Rostul e sa +existe mereu **un flux despre care se stie ca functioneaza**, cat timp cel nou se stabilizeaza. + +> **Corectie a rundei 13:** prima formulare a acestei decizii descria un pilot per utilizator, prin +> optiunea de firma. **Gresit** — Marius a corectat: optiunea **nu alege**, ci doar **face alegerea +> disponibila**. Nu se reargumenteaza in varianta veche. + +**Mecanismul exista deja in produs si face exact asta**, nu se inventeaza: +- `factureaza` (`COMUN\programe\ofacturare.prg:87-93`) verifica `gnFacturareNou` si, cand e pornita, + **intreaba la fiecare facturare**: `AMESSAGEBOX('Facturare noua (DA) sau standard (NU)?', 4+32, ...)`. + Optiunea deschide alegerea; raspunsul il da utilizatorul, de fiecare data. +- `gnFacturareNou` e o **optiune de firma**: `optiuni_firma` (`COMUN\programe\oinit_optiuni.prg:225-275`) + cheama `SCRIE_OPTIUNI(gcUserName)`, parcurge `v_optiuni` si **declara dinamic** globalele publice dupa + tip (`Public gn&lcvarname` pentru `NUMERIC`), cu filtru optional pe program + (`Isnull(programe) Or gcNumeProgram $ programe`). **Deci se activeaza si se dezactiveaza din date, + fara livrare de exe.** +- **Fluxul vechi ramane intreg:** `factureaza` + `frm_facturare_articole`. S2 sterge doar + **`factureaza2`**, un fork mort din 2017 (executia interogarii dezactivata, `lnSucces = 1` hardcodat, + zero utilizatori reali) — **nu** calea veche. **S2 nu are voie sa desfiinteze alegerea.** +- **Pentru etapa II plasa e alta, si exista deja:** editarea prin regenerare e o **actiune noua**, deci + „fluxul vechi" pentru ea e **fluxul lui #6**, pe care decizia 38 il pastreaza oricum. + +**Cum se prezinta alegerea — de ales la implementare, ambele satisfac cerinta:** +- **(A) intrebarea de azi**, modal la fiecare facturare. Exista deja, zero cod. Neajuns: intreaba si + cand utilizatorul stie de o luna ce vrea. +- **(B) doua intrari distincte** in meniu / doua butoane („Facturare" si „Facturare (nou)"). Aceeasi + libertate, fara modal la fiecare document. **Recomandat.** Se poate porni cu (A), care e gata, si trece + la (B). + +**Limita, si trebuie spusa explicit ca sa nu creeze o falsa siguranta: alegerea acopera VFP-ul, nu +Oracle.** Modificarile din pachete — parametrul nou al lui `contabilizeaza_articol` (decizia 34), +variabila noua din `SET_IDFACT`, si `INSERT` → `MERGE` in `DOCUMENTE` (S9) — sunt **cod comun al +intregii suite** si se aplica **tuturor deodata**, indiferent pe ce formular alege omul sa lucreze. +Proiectarea le face **inerte prin constructie** (parametru `NULL` → executie identica cu azi; +`WHEN MATCHED` care nu se declanseaza pe drumul normal), dar asta e o garantie de regresie zero, +**nu** o cale de intoarcere. Consecinta practica: pe formular si procedura intoarcerea e imediata; pe +baza de date, siguranta vine din **testarea unui ciclu normal de scriere in fiecare produs** (deja +ceruta in S9). + +### Decizia 50 (Marius, runda 13) — se editeaza documente curente, nu documente dintr-un lant + +**Formularea lui Marius:** *„in principiu nu se poate edita un document pentru care s-a facut retur; +de principiu se pot modifica documente curente, nu cele care fac parte dintr-un lant"*. + +**Deci garda de azi ramane, si devine regula declarata, nu limitare tolerata.** `sterge_factura` arunca +`ORA-20000` cand documentul are deja facturi / avize de retur emise peste el; S7 **semnaleaza conditia +inainte de intrarea in formular**, cu mesaj clar, nu ca eroare Oracle la final. Utilizatorul care chiar +vrea sa editeze sterge intai documentele-copil — comportament existent, nu ceva de construit. + +**PRECIZAT de Marius (runda 13), si inchide punctul: „lantul" se citeste IN AMONTE, nu in ambele +sensuri.** In cuvintele lui: *„daca este generata factura din aviz, avizul nu se mai poate modifica, +factura da, pentru ca este documentul curent; ma refeream la documentele din lant anterioare"*. + +Deci regula, in forma finala: +- **Documentul care are urmasi se blocheaza** — avizul din care s-a facut factura, factura peste care + s-a emis retur. Are deja o reprezentare in cod: garda din `sterge_factura`. +- **Documentul de la capatul lantului ramane editabil** — el e „documentul curent". **Factura din aviz + (`ntip = 4`) ESTE editabila**, chiar daca are parinte. +- Prin urmare **perimetrul etapei II nu se restrange**, iar intrebarea deschisa din S10 despre `ntip = 4` + (aceeasi re-derivare tacuta ca pe contract, dar cu alta sursa de comparat) **ramane in picioare** — nu + dispare, cum s-ar fi intamplat la citirea stricta. + +**VERIFICAT in runda 14 — si premisa era gresita.** Raport: `docs\cercetare\garda_aviz_facturat.md`. +Se credea ca garda de azi blocheaza documentul doar cand are **retururi** peste el. **Nu e asa:** a doua +garda din `sterge_factura` testeaza `TIP IN (1, 2)`, iar **`TIP = 1` este corespondenta aviz → factura +normala, nu retur** (`EXPORT:5464-5476`, semantica citita ramura cu ramura din `CASE`-ul lui +`finalizeaza_factura`, `EXPORT:14818-14839`). Formularea gresita venea din citirea comentariului +`-- verific daca exista facturi sau avize de retur`, in care „de retur" se distribuia si peste „facturi"; +codul zice altceva. **Deci regula ceruta de decizia 50 e deja implementata pentru avize** — nu e nevoie de +nicio garda noua ca *regula*. + +**Dar sunt trei garzi, nu doua, si a treia schimba enuntul deciziei 50:** + +| garda | `EXPORT` | ce blocheaza | +|---|---|---| +| 1 | 5452-5462 | **factura** care are facturi de retur peste ea (`TIP = 3`) | +| 2 | 5466-5476 | **aviz** care are factura (`TIP = 1`) sau aviz de retur (`TIP = 2`) peste el | +| 3 | 5480-5494 | **factura din aviz** ale carei avize-sursa au primit intre timp aviz de retur | + +Prin urmare **„factura din aviz ESTE editabila" nu e neconditionat**: e editabila doar cat timp niciunul +dintre avizele ei nu a primit aviz de retur. Nu e „capat de lant" prin definitie. + +**Ce lipseste nu e regula, e MOMENTUL.** Garda traieste in interiorul lui `sterge_factura`, deci se +manifesta ca `ORA-20000` **in mijlocul** pasului de stergere din regenerare — dupa ce utilizatorul a +completat formularul si dupa deschiderea tranzactiei. S7 are nevoie de un **pre-flight read-only** pe +**exact aceeasi conditie**, fara reimplementarea regulii (vezi S7). Interogarea se face pe +`VANZARI_CORESP`, **nu** pe `VANZARI.FACTURAT`: `FACTURAT` e un marcaj derivat, scris si resetat, dar +**necitit de nicio garda** — semnalul autoritar e `VANZARI_CORESP`. + +**GOLUL REAL NU E PE AVIZ, E PE PROFORMA — si a devenit relevant chiar in runda 14, odata cu decizia 51.** +`pack_facturare.sterge_proforma` (`EXPORT:5610-5635`) **nu are nicio garda**: corpul ei e doua +`UPDATE ... SET STERS = 1`. E o procedura **complet separata**, care nu cheama `sterge_factura`, deci +garda existenta **nu se extinde automat** la `TIP = 4`. O proforma din care s-a emis factura se poate +sterge azi fara niciun avertisment. Calea VFP iese devreme din `do_sterge` +(`ofacturare_comun.vc2:4707-4719`), inainte de restul verificarilor. **Asta e continutul concret al +punctului deschis 4 din S5c** („garda simetrica la stergere") — nu mai e o intrebare de principiu, e o +garda de scris intr-o procedura care azi n-are niciuna. + +**Comanda si contractul nu trec prin `VANZARI_CORESP`** si n-au garda de tip „are urmasi" pe factura: +comanda se leaga prin `VANZARI.ID_COMANDA` + `inchide_comanda`, iar garda ei e **in VFP si pe comanda**, +nu pe factura (`COMUN\clase\ocomenzi.vc2:1806-1807` la modificare, `:2065-2066` la stergere); contractul +se leaga prin `VANZARI.ID_CTR` + `CTR_RATE_FACTURI`. + +### Deciziile lui Marius, runda 13 (10.08.2026) — luate, nu de reluat + +Cele cinci puncte neblocante ramase din runda 12. **Patru din cinci au mers pe recomandare; al +cincilea (48) a respins-o motivat.** Fiecare e scrisa si la locul ei, in povestea careia ii apartine. + +44. **`poArticol` devine parametru explicit** al dialogurilor de linie (`do_verifica_articol`, + `do_alege_stoc` / `frm_articol_gest_factura`), in loc de variabila `Private` populata de apelant. + **O singura solutie pentru golul din S4e si cel din S4f (R7)** — e aceeasi problema structurala. + Schimbare de contract, **aprobata ca atare**; cere inventarul si actualizarea tuturor apelantilor + existenti inainte de prima editare. (S4e, S4f) +45. **Asimetria din `do_modifica`** se lasa sa se **corecteze de la sine** prin recalculul la cerere, + dar corectia se **declara explicit in changelog**. (S4, punctul 2) +46. **Corectia pe tipurile `23,41` asteapta punctul 2** (recalculul pe server, care acopera Rolul B). + Punctul 1 **nu se redeschide** acum. (S4, punctul 2) +47. **`zi_curs`: simetrie.** Campul se ascunde la loc la stergerea ultimului articol in valuta; + „clipitul" e acceptat ca pret al unei reguli unice. (S4d) +48. **Tabelul de configurare per tip = cursor generat in cod la pornire**, nu `DBF` static, nu tabela + Oracle. Detectia tipului necunoscut (eroare la deschidere) se pastreaza; se accepta recompilarea + la orice tip nou. (S5) + +### Deciziile lui Marius, runda 14 (11.08.2026) — luate, nu de reluat + +51. **`TIP = 4` in `VANZARI_CORESP` = „factura scrisa dintr-o proforma".** Confirmat explicit, dupa ce + i s-a explicat ce inseamna `1/2/3` (`TIP` = **natura legaturii parinte-copil**, nu tipul + documentului: `1` = aviz → factura, `2` = aviz → aviz de retur, `3` = factura → factura de retur). + `4` intra in aceeasi familie cu `1`. Valoarea e libera pe ambele fronturi (niciun apel cu `4` in + pachet, zero randuri in date), tabela are un singur scriitor in toata suita, structura nu se + schimba. **Alocarea e ireversibila odata cu primele date de productie** — acceptat ca atare. (S5c) +52. **`do_modifica` ramane activ pentru multi-selectie.** Formularul unificat preia cazul cu un singur + document; calea veche ramane pentru modificarea in bloc. **Nicio capacitate nu se pierde la + retragere** — dar consecinta e ca S2 nu poate desfiinta nici aceasta ruta, nu doar alegerea de la + decizia 49. (S8b, punctul 10) +53. **Atasamentul PDF se sterge la reemitere**, nu se remigreaza si nu se marcheaza „versiune + inlocuita". Documentul reemis porneste curat; PDF-ul se regenereaza la prima listare. **Consecinta + semnalata explicit lui Marius si acceptata de el:** urma a ceea ce s-a trimis efectiv clientului + dispare din sistem. Pasul `UPDATE ATASAMENTE_VANZARI SET id_vanzare = :nou`, adaugat in S9 la runda + 13, **se inlocuieste cu stergere**. (S11, punctul 15) +54. **La reemitere se scriu valorile din formular, nu se reciteste sursa.** Formularea lui Marius: + *„nu inteleg de ce se schimba valorile fata de ce se incarca din factura sursa care se modifica? + asta este comportamentul pe care il doresc — modific sursa"*. **Asta rastoarna S10:** intrebarea nu + mai e „ce garda punem peste re-derivare", ci **„re-derivarea chiar se produce pe calea de + reemitere?"** — si daca da, se **elimina**, nu se avertizeaza. Avertizarea + confirmarea propuse de + raportul S10 sunt **respinse**: nu se cere utilizatorului sa confirme o schimbare pe care n-a + cerut-o. Vezi S10, rescris. (S10, punctele 11 si 12) +56. **„DA LA TOATE" — Marius a acceptat in bloc toate recomandarile deschise, runda 14.** Formularea + lui: *„da la toate, mai putin"* cele doua puncte pe care le-a intrebat separat (48/49 si voiajele). + **Nu se mai reintreaba niciunul dintre punctele de mai jos** — sunt luate, si fiecare e scris si la + locul lui, in povestea careia ii apartine: + - **S8** — canalul de citire a liniilor: **(B), procedura noua `cursor_editare_document`** (nu se + extinde `cursor_retur_document`, ca sa nu se schimbe comportamentul copierii; nu + `FACT_VFACTURI_DETALII`, care pierde `ID_POL`, `PRETD` si tratamentul valutar — argument intarit + de faptul ca **`VVANZARI_ARTICOLE` nu expune `ID_POL` / `ID_CTR`**, vezi S10). + - **S8** — `GESTIONABIL` la editare: **din document**, nu din nomenclatorul curent. + - **S8** — `text_aditional`: **se normalizeaza la incarcare** (`Chr(170)` → `CR+LF`) si se + re-normalizeaza la comparatie. Formele diferite salvate de cele doua rute de scriere se + **semnaleaza separat** ca defect preexistent. + - **S8** — `zi_curs` pe calea de editare: **ascuns** (precedent: tipurile 8/9). Abatere mica de la + decizia 5, asumata. + - **S8** — `poDate.lEditare`: **proprietate pe `oDateFactura`**, nu parametru (patru locuri au + nevoie de semnal; `lCopiere` e deja acolo cu acelasi rol). + - **S8** — `id_ruta`: **proprietate noua pe `oDateFactura`** (fara ea S8c nu poate implementa unul + din cei 14 parametri). + - **S8** — defectul de prefixare `text_aditional` la `Init` pentru contracte: **se ocoleste in #13** + prin `lEditare` si **se semnaleaza separat**, nu se repara pe calea de emitere. + - **S5c** — apelul Oracle pentru legatura proforma → factura: **varianta simpla**, pe starea de + sesiune a pachetului, zero cod PL/SQL nou — **cu conditia verificata la implementare** ca niciun + apel intercalat nu reseteaza starea intre scrierea facturii si scrierea corespondentei. + - **S5c** — aceeasi proforma facturata de N ori: **avertisment, nu blocare**. + - **S5c** — garda simetrica la stergerea proformei: **se scrie**. Nu e reutilizare — + `sterge_proforma` n-are azi nicio garda (vezi decizia 50, sectiunea rescrisa). + - **S5c** — afisarea „provine din proforma X": **da**, la nivel de document. + - **S4g** — `CU_TVA = 1` hardcodat: **se verifica pe date inainte de implementare**, nu se + presupune inofensiv. Efectul prin `nproc_tva_max` e masurat, pe linie scutita + discount global. + - **S4g** — trasabilitatea `CONT_VENIT` pe `VANZARI_DETALII`: **da**, se pastreaza coloana. + - **Decizia 54, cele trei consecinte: acceptate toate trei.** `IN_STOC` vine din formular (si **S8 + trebuie sa-l incarce** — vezi S10, consecinta 1); **se pierde validarea** liniei fata de comanda + pe ramura comenzi, asumat; **`PROC_TVAV` ramane derivat** — reemiterea dupa o modificare legala + de cota va da cota noua, **acceptat deocamdata**; transformarea lui in parametru ramane o + decizie separata, daca se cere vreodata reproducere exacta si peste asta. + - **Defectul `lnTip` din `do_copiaza`** (runda 13): **se repara la #6**, fisierul fiind al lui. + - **Punctele pur interne** (numarul codului de eroare `FACT-0xx`, numele cheii de optiune + `FACT_SCD_ARTFPRET`, forma semnalului de regenerare) — **lasate la latitudinea implementarii**, + cu recomandarile deja scrise in povestile lor. + + **Raman deschise doar doua**, si amandoua din motive proprii, nu din lipsa de raspuns: + **(a) tipurile 48/49** (custodie) — Marius a cerut sa stie implicatiile, i s-au explicat, decizia + n-a fost inca data. *Inchis intre timp — decizia 60, runda 16: da, sunt editabile prin #13, + cu executia conditionata de verificarea custodiei in curs.* + **(b) cota si explicatia de TVA a discountului de document** — + **cercetare TERMINATA** (sectiunea K-bis): cota era regula implicita „cota maxima de pe factura", + explicatia e azi o constanta hardcodata (`ReasonCode="95"`, text „Discount"). **Partea de + repartizare s-a decis — decizia 59, runda 16: proportional pe cote.** Ramane deschis doar campul + text optional de motiv — **inchis si el: decizia 61** (da, se adauga), cu asezarea fixata de + **decizia 66** (in banda de totaluri, langa discount). +55. **Metoda de executie e obligatorie, si e scrisa in plan.** La implementare planul **se sparge pe + stories**, fiecare story fiind o livrare de sine statatoare; **fiecare pas se testeaza**, nu doar + capetele de etapa (S6 / S12); si **fiecare story trece prin code review dupa implementare si dupa + teste, inainte de commit**. Sectiunea **„Metoda de executie"**, imediat inainte de Etapa I. S13 + nu mai e momentul review-ului, ci al inchiderii. + +### Deciziile lui Marius, runda 15 (11.08.2026) — luate, nu de reluat + +57. **Asezarea zonei de jos: VARIANTA D.** Aprobata explicit („sunt de acord cu varianta D"), dupa doua + corectii cerute de el pe drum: (a) *„imi place linia de totaluri de la varianta C, dar incasarea si + alte date le vreau tot in acelasi formular, colapsate, mai jos de totaluri"*; (b) *„sectiunile + incasare si alte date colapsate trebuie sa fie pe acelasi rand — este destul loc pentru amandoua, + detaliile pot sa fie grupate pe orizontala, nu pe verticala"*. A / B / C **cad**. + + **Ce inseamna D, concret, pentru S1 si S3:** + 1. **Banda de totaluri** (preluata din C): pe toata latimea, lipita de grid, cu baza, discountul pe + articole, discountul de document **cu procentul editabil pe loc** si TVA desfacute pe un rand; + totalul mare, singur, la dreapta. + 2. **Incasare si Alte date sunt sectiuni colapsabile in formular, nu dialoguri modale** — **una + langa alta pe acelasi rand**, sub totaluri, fiecare pe jumatate de latime. Randul inchis arata + **rezumatul continutului** („NUMERAR · 5 570,55 lei"), nu doar un titlu. Independente: se poate + tine deschisa doar una. Campurile dinauntru se aseaza **pe orizontala**, doua randuri fiecare. + 3. **Nu exista bara de comenzi jos.** `but_renunt` / `but_termin` raman unde sunt azi — in banda de + titlu, sus in dreapta (`COMUN\clase\cmd_butoane.vc2:288` si `:386`, butoane-imagine cu + `Top = 1`, `Anchor = 8/9`, tooltip „Renuntare (ESC)" / „Terminare (CTRL+F)"). + 4. **Antetul strans la doua randuri**, fara titluri de grup, si **panoul „Discount pe document" + dispare** ca panou separat. + + **Patru consecinte de dus in executie, nu de redescoperit:** + - **Dispare „renunt doar la incasare".** Fara `Accept` propriu pe dialog, ce se completeaza in + sectiune se scrie la `Termina`, impreuna cu documentul. **I-a fost spus explicit** inainte de + aprobare si a acceptat. + - **Validarea incasarii nu mai are moment propriu** — se muta in `Termina`, langa restul + verificarilor. **De scris explicit in S9.** + - **Starea deschis/inchis a sectiunilor** — de decis daca se retine intre documente sau porneste + mereu inchisa. Nu blocheaza nimic; recomandare: porneste inchisa, se retine per utilizator. + - **Caption pe cele doua butoane.** Sus in dreapta, langa `✕`-ul ferestrei, un `✓` verde fara text + e ambiguu. Punctul era deja notat in v8 pentru `But_renunt1` / `But_reset1`; D il face mai + apasat, pentru ca acum ele sunt **singura** cale de iesire. + + **INCHIS.** Cercetarea despre cota si explicatia de TVA a discountului de document (punctul + deschis (b) de la decizia 56) **s-a terminat** — vezi sectiunea K-bis — si repartizarea s-a decis + (**decizia 59, runda 16**: proportional pe cote, fara sa se ceara cota de la utilizator), deci a + treia sectiune „grea" (cota + explicatie proprii) nu mai e in discutie. Ramane varianta minimala: + **decizia 61 (runda 16) a materializat-o** — Marius vrea campul text optional de motiv. Asezarea + lui a oscilat o data: decizia 64 il facea a treia sectiune jos, **decizia 66 (runda 17) o + rastoarna si il muta in banda de totaluri, langa discount**. **Punctul 2 de mai sus ramane deci + exact cum a fost aprobat: doua sectiuni jos, fiecare pe jumatate de latime.** D ramane intr-un etaj. + +58. **Mockup-ul asezarii ramane doar online.** Formularea lui: *„nu vreau artifactul html, doar online, + ca sa nu mai intretii 2 variante"*. `docs\mockup_13_variante_asezare_jos.html` **a fost scos din + `docs\`**; sursa de adevar e + **https://claude.ai/code/artifact/dfc70d38-856f-48fd-b6af-3da78e9da4f7**. + Ca sa-l modifici: `WebFetch` pe URL → scrie HTML-ul intr-un fisier de lucru **in scratchpad, nu in + `docs\`** → `Artifact` cu **`url` = link-ul de mai sus** (fara `url` se creeaza link nou). + **`docs\mockup_13_formular_unificat.html` (v8) nu intra sub regula asta** — cerinta a fost data + numai pentru mockup-ul asezarii. + +### Decizia 59 (Marius, runda 16) — luata, nu de reluat + +59. **Discountul de DOCUMENT se repartizeaza PROPORTIONAL PE COTE.** Formularea lui: *„discount pe + document repartizat proportional pe cote"*. Se adopta recomandarea cercetarii din **K-bis**: + regula de azi („toata valoarea discountului primeste cota MAXIMA de pe factura", prin + `Calculate Max(proc_tvav)`) **se inlocuieste** cu repartizarea proportionala cu baza fiecarei + cote de pe factura. **Nu se cere utilizatorului cota** — repartizarea e automata. + + **Ce atrage dupa sine, tot din K-bis:** + 1. **Doua locuri de schimbat, nu unul, si in ACEEASI livrare:** `prelucreaza_facturacrs` (VFP, + `COMUN\programe\ofacturare_comun.prg:1886-1897`) trebuie sa insereze cate un rand-sentinela + **per cota** in loc de unul singur; si `recalculeaza_totaluri_vanzari` (PL/SQL, + `ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:16081-16092`) trebuie sa inlocuiasca `MAX(...)` + cu suma repartizarii. Daca se schimba doar unul, `VANZARI.TOTAL_TVA` si TVA-ul din eFactura + **diverg** pe facturile cu cote mixte. **Nu e optional si nu se poate esalona.** + 2. **Bucatile de discount trebuie sa primeasca `id_jtva_coloana` al grupului pe care il reduc**, + nu doar cota. Cheia de grupare din `xmlefactura.prg:246` are cinci campuri si `expltva` se + completeaza tot prin `id_jtva_coloana`. O implementare care seteaza doar `proc_tva` **lasa + grupul orfan exact unde e azi** — repara jumatate din defect si o lasa pe cealalta. + 3. **eFactura nu cere nicio modificare** — `xmlefactura.prg:758-792` grupeaza deja + `GROUP BY proc_tva` si emite cate un `AllowanceCharge` per cota. + 4. **Diferenta de rotunjire cade pe cota cu baza cea mai mare**, ca suma bucatilor sa fie exact + `VANZARI.DISCOUNT`. E recomandarea cercetarii, luata ca implicita — Marius a fost instiintat + ca se merge asa fara sa mai fie intrebat, deci se schimba doar daca obiecteaza. (Raportul + spune pe alocuri „ultima cota preia diferenta"; **regula care se implementeaza e cea de aici**, + ca sa nu ramana doua formulari in circulatie.) + 5. **Doua consecinte vizibile, acceptate implicit prin decizie:** factura tiparita va arata **N + randuri** „Discount X % Factura" in loc de unul, pe facturile cu cote mixte; si **nota + contabila** primeste TVA-ul discountului spart pe cote. + 6. **Rezolva si grupul orfan** de pe facturile scutite / taxare inversa / intracomunitare + (K-bis), fara garda separata, si desfiinteaza ambiguitatea lui `agettipcota(1)`. + + **Raman DESCHISE, nu de presupus rezolvate:** + - **campul text optional de motiv** (`AllowanceChargeReason` in locul constantei „Discount"; + `ReasonCode` ramane `95`) — Marius **nu s-a pronuntat**; de el atarna si intrebarea de asezare + din S1 / decizia 57 (a treia sectiune pe rand sau rand propriu); + - **retroactivitatea la relistare / retrimitere**: o factura veche relistata sau retrimisa in + eFactura ar genera alt XML decat cel trimis initial. **Nedecis.** + +### Deciziile 60-65 (Marius, runda 16) — luate, nu de reluat + +60. **Facturile de marfa in CUSTODIE (tipurile 48 si 49) SUNT editabile prin #13.** Formularea lui: + *„vreau sa fie posibila editarea si a facturilor in custodie"*. Intra deci in perimetrul + **etapei II**, contrar recomandarii „nu acum" din raportul S8 (intrebarea 7, §8.2). + + **Inchide** punctul **(a)** de la **decizia 56** (tipurile 48/49, singurul ramas deschis din + blocul „da la toate") si **necunoscuta N5** din `docs\cercetare\s8_incarcare_document.md` §8.1, + plus restul intrebarii 7 din §8.2. + + **Ramificatie noua pentru S8:** pe tipurile 48/49, controlul `ct_clb_altele` e scos + **neconditionat** la copiere (`ofacturare.vc2:9705-9712`), spre deosebire de tipurile 1/5/10 + unde copierea il **pastreaza** si il reeticheteaza (`s8_incarcare_document.md` §3.2, ramura 3). + Calea de **editare** trebuie sa-l pastreze si pe custodie — ramura 48/49 a copierii nu poate fi + refolosita ca atare. + + **VERIFICAT — blocantul CADE, dar premisa initiala era gresita.** Raportul: + `docs\cercetare\custodie_48_49_stergere_reemitere.md`. `scrie_fact_aviz_custodie` **nu are + legatura cu tipurile 48/49** — are un singur apel in tot pachetul (`PACK:7521`), pe ramura + `pack_facturare.ntip <> 4` (`:7472`), deci serveste `ntip = 4` (factura din avize), nu custodia; + numele procedurii a indus in eroare, si premisa gresita a circulat pana in aceasta decizie. + Emiterea unui document 48/49 **nu atinge deloc stocul**: sursa lor unica de articole e + `cursor_articole_k` (`PACK:3595-3701`), restransa explicit la `WHERE C.IN_STOC = 0` (`:3695`), + iar `descarca_gestiune` se cheama doar cand `in_stoc = 1` (garda dubla, la apelant `:7472-7475` + si in corpul procedurii `:7789-7797`). `sterge_factura` (`:5432-5607`) nu are ramura dedicata + pentru 48/49, si nici cele trei garzi de refuz al stergerii (`:5452-5494`) nu le prind — dar + **n-are ce reversa**, fiindca nimic legat de stoc n-a fost scris la emitere. **Regenerarea e + sigura pe 48/49**, nu pentru ca reversarea ar functiona, ci pentru ca nu exista nimic de + reversat. + + **Rezerva, de scris, nu de ascuns:** siguranta atarna de un invariant azi impus doar de sursa de + articole — „documentele 48/49 contin numai articole cu `IN_STOC = 0`". Invariantul **nu s-a + verificat exhaustiv**, doar constatat pe sursa curenta. #13 schimba modul de adaugare a + articolelor (**S4** — cautare pe server, in linie; **S4g** — adaugare de articole la modificarea + oricarui document): daca formularul unificat ajunge sa permita adaugarea unui articol + **gestionabil** pe un document 48/49, invariantul se rupe si concluzia de siguranta pica. + **Cerinta pentru S4/S4g/S5: pe tipurile 48/49 se pastreaza restrictia la articole `IN_STOC = 0`** + — vezi notele de executie la acele povesti; devine **criteriu de test**, nu presupunere. + + Pentru **emitere**, tipurile 48/49 erau oricum deja acoperite de etapa I (S5 completeaza cele + patru randuri lipsa din `Do Case`: 45, 48, 49, 52) — decizia 60 priveste doar **editarea**. + +61. **Discountul de document primeste un CAMP TEXT OPTIONAL de motiv.** Formularea lui: *„la + discount, da, un camp text optional"*. + + **Inchide** ultimul punct ramas deschis din **K-bis** si din **decizia 56 (b)** — campul de + motiv nu mai e „Marius nu s-a pronuntat". + + **Ce inseamna concret:** + - inlocuieste constanta hardcodata `"Discount"` ca `cbc:AllowanceChargeReason` + (`COMUN\programe\xmlefactura.prg:774-776`); **`ReasonCode` ramane `95`**; + - cere **stocare noua** — azi nu exista nicio coloana pe `VANZARI` pentru motivul discountului + de document; + - cere **un control nou in formular** — singura bucata din reparatia discountului (K-bis / + decizia 59) care chiar cere UI nou, restul fiind repartizare automata fara interactiune. + + **Redeschisese, in varianta minimala, intrebarea de asezare de la decizia 57. INCHISA DEFINITIV + PRIN DECIZIA 66 (runda 17): campul sta in banda de totaluri, langa discount.** Decizia 64, care il + facea a treia sectiune jos, **e rasturnata** — randul de jos ramane cu doua sectiuni, ca la decizia + 57. Varianta D ramane intr-un etaj. Paragrafele din **S1** si din **decizia 57** sunt actualizate + in acest sens. + +62. **Restrictia de modificare e „documentul sa NU fi fost trimis in eFactura" — si de aici + retroactivitatea NU mai e o intrebare.** Formularea lui: *„singura restrictie pentru modificare + este sa nu fie trimisa in eFactura, deci nu are legatura retroactivitatea"*. + + Garda insasi nu e noua — **S7** o are deja, reutilizata din #6 + (`COMUN\programe\ofacturare_editare.prg:19-28`, functia `EsteInEFactura`; apelata din + `COMUN\clase\ofacturare_comun.vc2:3764`). Ce era intrebare deschisa era + doar partea lasata de **decizia 59**: daca o factura veche, retrimisa, ar trebui tratata altfel + fata de una noua. Raspunsul lui Marius **inchide** acea intrebare: nu se leaga de nicio data si + de niciun flag suplimentar — verificarea `EsteInEFactura` ramane singurul criteriu de + modificare. + + **Ramane un rest neacoperit, semnalat lui Marius, nedecis inca:** o factura **veche, deja + trimisa**, daca e doar **relistata** (tiparita din nou, nu editata), trece iar prin + `prelucreaza_facturacrs` si — dupa decizia 59 — ar produce **N randuri de discount in loc de + unul** pe facturile cu cote mixte, deci hartia ar diferi de originalul trimis in eFactura. + Relistarea nu e modificare, deci garda `EsteInEFactura` nu o acopera. + +63. **CERINTA NOUA DE AUDIT pe documente.** Formularea lui: *„este doar util de stiut data crearii, + data modificarii daca este cazul, utilizatorul crearii si al modificarii daca este cazul, + respectiv data stergere si utilizator stergere daca este cazul, pentru audit"*. Sase informatii: + **data + utilizator** pentru **creare**, **modificare** si **stergere**. + + Scrisa initial ca cerinta, cu cercetarea in curs. **Cercetarea TERMINATA**, raport: + `docs\cercetare\audit_vanzari_creare_modificare_stergere.md`. + + **Verdict, in doua randuri:** patru din cele sase informatii cerute **exista deja** pe `VANZARI` + si se scriu consecvent (`ID_UTIL`/`DATAORA` la creare, `ID_UTILS`/`DATAORAS` la stergere); **lipseste + complet perechea de modificare** (nicio coloana, verificat cu filtru `%MODIF%` pe + `all_tab_columns`), iar editarea prin stergere+reemitere (S9) ar **suprascrie tacit** perechea de + creare cu utilizatorul si data regenerarii, daca nu se transporta explicit — acelasi risc deja + rezolvat pentru `ID_FACT` (sectiunea „E. `ID_FACT`", linia 762). + + **Poveste noua, proiectata: S14** — verdict complet, recomandare si punctele ramase de decis cu + Marius acolo. + +64. ~~**Asezarea campului de motiv: RANDUL SE IMPARTE IN TREI.**~~ **RASTURNATA DE DECIZIA 66 + (runda 17) — NU MAI E IN VIGOARE.** Formularea de atunci: *„campul de motiv imparte randul in 3"*; + randul de jos ar fi avut trei sectiuni (incasare, alte date, motivul discountului), ~440 px + fiecare la 1366 px. + + **Se pastreaza ca istorie, nu ca regula**, si merita pastrata: varianta a fost **construita in + mockup (v9) si respinsa dupa ce Marius a vazut-o** — *„motiv discount vreau sa fie langa discount, + nu a treia coloana"*. E argumentul cel mai bun din tot planul pentru **de ce se face mockup + inainte de cod**: decizia luata pe descriere s-a intors la prima privire pe forma desenata. + Ce e in vigoare: **decizia 66**, mai jos. + +65. **Auditul (decizia 63) se afiseaza in gridul din `frm_facturi`, nu in formularul facturii.** + Formularea lui: *„auditul presupun coloanele dataora si utilizator adaugat/modificat/sters, le + vreau pe grid-ul din formularul frm_facturi, nu in formularul facturii propriu-zise - este + posibil sa fie deja coloane"*. + + Confirma continutul cerintei de audit (decizia 63): trei perechi **data + utilizator**, pentru + **adaugat**, **modificat** si **sters**. Locul de afisare e **gridul din `frm_facturi`**, nu + formularul unificat — deci **S14 nu cere controale noi in formularul unificat**, cere **coloane + in gridul listei de facturi**. E o cerinta mai usoara decat presupunea S14, si e de spus explicit. + + **Sarcina de verificat la implementarea lui S14, adaugata ca prim pas al povestii:** Marius + banuieste ca unele coloane exista deja in acel grid. **Nu s-a verificat.** S14 incepe deci cu + **inventarul gridului din `frm_facturi`** — ce coloane de audit sunt deja acolo, ce surse au — + si abia apoi adauga ce lipseste. Nu se presupune nici ca exista, nici ca nu. + +### Decizia 66 (Marius, runda 17) — luata, nu de reluat. RASTOARNA DECIZIA 64. + +66. **Motivul discountului sta LANGA DISCOUNT, in banda de totaluri — nu ca a treia sectiune jos.** + Formularea lui: *„motiv discount vreau sa fie langa discount, nu a treia coloana"*. + + **Anuleaza decizia 64** (randul de jos se imparte in trei). Randul de jos revine la **doua + sectiuni colapsabile** — incasare si alte date — adica exact la ce aprobase decizia 57, punctul 2, + inainte ca decizia 61 sa redeschida discutia. **Decizia 57 ramane intreaga; nu se mai atinge.** + + **Ce inseamna concret, pentru S1 si S3:** + - campul de motiv e un **control in banda de totaluri**, imediat dupa suma discountului de + document, inainte de TVA; ia latimea ramasa pana la totalul mare, care sta la dreapta; + - **cele doua sectiuni de jos redevin pe jumatate de latime** (~660 px la 1366 px), nu ~440 — + argumentul „strans, asumat ca atare" din decizia 64 **cade odata cu ea**; + - **regula de activare, adaugata de proiectare, nu ceruta explicit:** campul e activ **numai cand + discountul de document nu e zero**. Un motiv fara discount n-are ce explica, si ar ajunge in + `AllowanceChargeReason` pe un `AllowanceCharge` inexistent. Daca Marius vrea altfel, e o linie + de schimbat. + + **Consecinta de asezare, de stiut la implementare:** banda de totaluri devine plina — baza, + discount articole, discount document (procent + suma + bifa „evidentiat"), motiv, TVA, total. La + latimi mici **se rupe pe doua randuri** (`flex-wrap`), si asta e acceptat: alternativa ar fi + scoaterea bifei „evidentiat" din banda, care n-a fost ceruta. + + Restul deciziei 61 (stocare noua pe `VANZARI`, `AllowanceChargeReason`, `ReasonCode` ramane `95`) + **e neatins** — se schimba doar locul controlului in formular. + +### Decizia 67 (Marius, 20.08.2026) — intrare separata „Facturare (nou)", nu comutator. Inchide (A)/(B) din decizia 49 pe (B). + +67. **Nu comutator global cu dialog — buton nou de meniu, ca ambele fluxuri sa fie accesibile in + acelasi timp.** Formularea lui: *„nu vreau sa modific o optiune pentru facturarea pe model nou, + ci vreau sa pui in meniu o varianta de facturare nou, ca sa pot sa accesez in acelasi timp ambele + facturari, si cea existenta si cea noua"*. + + **Inchide definitiv alegerea (A)/(B) din decizia 49 (plan, liniile 3040-3082) pe varianta (B).** + Implementarea S2 din runda anterioara (comutator `gnFacturareNou` + `AMESSAGEBOX` DA/NU la + inceputul lui `factureaza`, care selecta intre proceduri) **se inlocuieste**, nu se pastreaza ca + alternativa. + + **Mecanismul, minim, pe doua fisiere:** + - `COMUN\programe\ofacturare.prg`, in `factureaza`, la punctul de alegere a lui `lcObject`: + `llFacturareNoua = (Type('plFacturareNoua') = 'L' And m.plFacturareNoua)` — variabila + `plFacturareNoua` e `Private`, declarata de apelant; nedeclarata => `.F.` => calea veche, + identica azi, in toata suita (celelalte produse nu o declara). Fara dialog, fara comutator + global. `gnFacturareNou` dispare complet din fisier. + - `Clase\ofundal_facturare.vc2`, `Page2` (fundalul „Facturare"): buton nou `Cw10`, „Facturare + (nou)", cu propriul `do_actiune` care declara `Private plFacturareNoua = .T.` si ofera acelasi + meniu de surse (lista de preturi, contracte, comenzi, avize) ca butoanele existente, apeland + aceiasi wrapperi din `oproceduri_facturare.prg`. Popup-urile (`politica.mpr`/`contracte.mpr`) + executa `factureaza(N)` in interiorul `ACTIVATE POPUP`, deci vad variabila privata a + apelantului pe tot lantul de apel, fara sa fie redeclarata pe drum. + + **Criteriul „gata" pentru S2 se rescrie in consecinta** (vezi story S2 mai jos): zero aparitii + `gnFacturareNou`, exact o aparitie `plFacturareNoua` in `ofacturare.prg`; butonul `Cw10` deschide + `frm_facturare_articole2` din formularul unificat vechi al meniului `Facturare`, fara sa + afecteze butoanele Cw1-Cw9 existente. + + **COMPLETARE (Marius, 20.08.2026, aceeasi zi) — acoperire completa, nu doar 4 intrari.** + Intrarea „model nou" trebuie sa acopere **toate** intrarile de facturare existente, pe **ambele** + pagini ale fundalului, nu doar cele patru de pe `Page2` alese initial. Implementare generica, + fara duplicare de cod: + - `Page2.Cw10.do_actiune` ofera **toate cele noua** optiuni ale paginii „Facturare" (captions + identice, extrase textual din `Label_item1.Caption`/`LABEL_ITEM1.Caption` al fiecarui `Cw1..Cw9`, + in aceeasi ordine), si apeleaza `This.Parent.Cw.do_actiune()` pentru optiunea aleasa — nu mai + cheama wrapperii direct, ca sa nu duplice logica fiecarui buton (unele cheama wrapperi din + `oproceduri_facturare.prg`, altele — pe `Page3` — cheama `.mpr` direct; `This.Parent.Cw` merge + identic in ambele cazuri). + - Buton nou `Page3.Cw5`, „Avize (nou)", acelasi tipar, peste cele patru intrari ale paginii „Avize" + (`Cw1`=catre clienti, `Cw2`=catre clienti in custodie, `Cw3`=catre clienti debitori, + `Cw4`=transfer intre subunitati). + - `plFacturareNoua` (Private, setata `.T.` in `do_actiune`-ul butonului nou) ramane vizibila prin + `This.Parent.Cw.do_actiune()` -> wrapper/`.mpr` -> `ACTIVATE POPUP` -> `factureaza(N)`, la fel + ca in varianta cu patru intrari - niciun apel din lant nu o redeclara. + - **Verificat in `factureaza`**: exista un singur punct unde se atribuie `lcObject`, intr-un + `If tnTip = 27 ... Else ... Endif` cu exact doua ramuri. Tipul 27 (aviz pe baza de lucrare) e + **singura exceptie** - toate celelalte tipuri (toate avizele, toate vanzarile, contractele, + returul, credit note) cad in `Else`, deci vad `plFacturareNoua`. Daca un utilizator ajunge la + tipul 27 prin butonul nou „Avize (nou)" (posibil via `aviz_subunitati.mpr`), flagul nu are efect + pentru acel sub-flux - comportament asteptat, nu bug, semnalat aici explicit. + + **CORECTIE dupa review (aceeasi zi) — 5 din cele 9 optiuni initiale ale `Page2.Cw10` NU treceau + niciodata prin `factureaza`.** Review-ul (`docs\review_s2.md`, sectiunea 3) a gasit ca `Cw5..Cw9` + (materii prime, produse, marfa la pret de achizitie/vanzare/achizitie-vanzare) ruteaza prin + `vanzare_*` -> `vanzare1-5.mpr` -> `initializeaza_vanzare_din_stoc()` + (`COMUN\programe\ofacturare_stoc.prg:35-103`), un flux complet separat de „facturare" (deschide + formularul prin `lans()`, nu apeleaza niciodata `factureaza()` - `grep -c "factureaza("` pe fisier + = 0). Analiza de mai sus despre `lcObject`/`Else` e corecta, dar incompleta: demonstreaza doar ca + *daca* un tip ajunge la `factureaza`, vede `plFacturareNoua` - nu ca toate cele noua optiuni ale + lui `Cw10` ajung acolo. **`Page2.Cw10.do_actiune` s-a corectat la primele patru optiuni (`Cw1..Cw4` + - lista de preturi, contract, comanda, aviz), singurele care trec prin `factureaza`.** `Page3.Cw5` + ramane neschimbat - toate cele patru optiuni ale lui verificate ca trec prin `factureaza`. Vanzarile + din stoc (`Cw5..Cw9` de pe `Page2`) raman in afara perimetrului povestii #13 - `factureaza` nu are + nicio legatura cu ele. + + **`frm_facturare_articole2` ramane, ca si pana acum, un prototip neterminat** (S1/S3) — decizia + 67 schimba doar *cum se ajunge la el* (intrare de meniu explicita, nu comutator ascuns), nu + starea lui functionala. + + +### Decizia 68 (Marius, 21.08.2026) — S3 se sparge in S3-1..S3-4; logica comuna a antetului trece in `.prg`, designul ramane in `.vcx` + +Marius a aprobat, pe nota de executie `docs\nota_executie_s3.md`: **spargerea lui S3** in S3-1 (pas 0 ++ `Init` fuzionat + C3 + `do_cauta_fdoc`), S3-2 (cele 28 `do_cauta_*` + `do_verifica`), S3-3 +(`inainte_de_do_termin` fuzionat + `do_schimba_tipdoc` + evenimentele de antet + testul UI), S3-4 +(integrarea buclei din `factureaza`, `ofacturare.prg` — cod comun suitei; raspunde la Q1), in +ordinea asta; si recomandarile Q2 (combo `cboFdoc` dezactivat „AVIZ" pe aviz, `do_cauta_fdoc` +portata, nelegata) si Q3 (doar `RemoveObject`, reasezarea la S3b). + +**Q4, precizat de Marius, textual:** *„toate metodele care se preteaza se pot muta in prg, sub forma +de metode de clase. preteaza inseamna ca sunt lucruri comune, folosite de mai multe clase, sau +utilitare [...]. imi este mult mai usor sa editez prg decat vcx. daca sunt lucruri care afecteaza +design, ar trebui sa fie in vcx."* Regula de aplicat in S3-1..S3-4 (si in restul lui #13): + +- **in `.prg`, ca metode ale unei clase** (`Define Class ... As Custom`, sablon `anaf_efactura.prg`; + un singur cod, nu doua — in spiritul deciziei 35): cautarile `do_cauta_*` care scriu pe `poDate` + prin `caut_*()`, validarile din `inainte_de_do_termin` (lanturile `Do Case` / `amessagebox`), + regulile de `do_schimba_tipdoc` care nu ating controale, si orice utilitar folosit din mai multe + clase (`frm_date_factura`, `frm_date_aviz`, `frm_facturare_articole2`). Metoda din `.vcx` devine + un apel de o linie catre obiectul din `.prg` (`This.oAntet.(This)` sau echivalent), ca sa + ramana `cprocedura` al controalelor valid. +- **in `.vcx`**: tot ce tine de design si de suprafata — `Init`-ul (asezare, `RemoveObject`, focus, + etichete, serie/numar pe controale), `SetFocus`, `Enabled`/`Visible`, `ADD OBJECT`, evenimentele + controalelor. +- Linia de demarcatie nu se trage mecanic: o metoda care amesteca o regula cu un `SetFocus` se + desparte — regula in `.prg`, efectul pe control in `.vcx`. Portarea ramane verificabila fata de + sursa (diff-ul citeaza de unde a venit fiecare corp). +- Fisierul `.prg` nou (sau clasa noua in `COMUN\programe\ofacturare_comun.prg`, unde traieste deja + `oDateFactura`) se inregistreaza in `SET PROCEDURE ... ADDITIVE` din `Programe\roafacturare.prg` + daca e fisier nou — alegerea (fisier nou vs. clasa in `ofacturare_comun.prg`) se face in S3-2, o + singura data, si se noteaza in nota de executie. + +Consecinta pe S3-1, deja pornit: `Init` ramane in `.vcx` (e design); `do_cauta_fdoc` se porteaza in +`.vcx` acum si **se muta** in clasa `.prg` la S3-2, odata cu celelalte `do_cauta_*` — ca sa nu se +faca de doua ori alegerea locului. +### Decizia 69 (Marius, 21.08.2026) — S3-4: forma functiei, declansarea incarcarii, antetul dupa incarcare, numarul ars + +Raspunsuri la Q5-Q7 din `docs\nota_executie_s3_4.md` plus perimetrul semnalat de +`docs\verif_nract_do_cauta.md`. + +- **Q5 — functie globala**, langa clasa, in `COMUN\programe\ofacturare_antet.prg`. Constructia + cursorului de articole se apeleaza si din `factureaza` (unde nu exista formular), si din + `do_incarca_articole` (unde exista); o metoda de clasa ar cere fie doua cai de apel, fie + instantierea artificiala a lui `oAntetFacturare` doar pentru acest apel. +- **Q6 — buton explicit** `but_incarca_articole` pe formularul unificat. Incarcarea automata la + completarea ultimului camp obligatoriu ar re-executa interogari Oracle la fiecare corectie de camp. +- **Q7 — limitare cunoscuta, se lasa pentru S3b.** S3-4 livreaza exact criteriul „gata" al lui S3: + antetul se completeaza o data, articolele se incarca o data, documentul se termina. Ce se intampla + cu `crsfactura` daca antetul se schimba dupa incarcare nu se rezolva aici si nu se blocheaza + antetul; se documenteaza. +- **Q8 — numarul ars se repara pe AMBELE cai, in S3-4.** In `do_schimba_tipdoc` alocarea + (`clb_serie_act1._cbbase1.LostFocus()`) ruleaza inaintea garzii „s-a schimbat tipul?" + (`ofacturare_antet.prg:906-908`; echivalent inline pe calea veche, `ofacturare.vc2:9396+`), deci se + consuma un numar real la simpla intrare-iesire din combo, fara nicio schimbare. Garda se muta + inainte de alocare in ambele implementari. + + **Abatere asumata de la politica S3** („calea veche nu se atinge — nici nu se repara, nici nu se + reproduce"): orchestratorul a recomandat amanarea ca bug separat dupa #13, fiindca reparatia atinge + cod din productia tuturor utilizatorilor in mijlocul unificarii. Marius a decis reparatia pe ambele + cai. Consecinte de verificat la implementare: `test_s3_neregresie_vechi` compara amprente de dupa + `Init` (controale, etichete, `nid_tip`, `Height`) si nu exercita `do_schimba_tipdoc`, deci ar trebui + sa ramana identic — **de dovedit, nu de presupus**; iar paritatea pe `nract` din + `test_s3_do_cauta`/`test_s3_tipdoc` se pastreaza doar daca ambele cai se schimba impreuna. + +### Decizia 70 (Marius, 21.08.2026, seara) — amendament la Q8: pe modelul vechi se lasa cum era + +Reparatia Q8 ramane **doar pe calea unificata** (`plFacturareNoua`); `frm_date_factura` revine +neatins. Politica S3 „calea veche nu se atinge" se restabileste. + +Motivul: review-ul independent (`docs\review_q8_numar_ars.md`, Q8-R5) a aratat ca pe calea veche +reparatia nu inseamna doar „nu se mai arde un numar", ci **muta momentul alocarii** — documentul nou +primeste numar abia la iesirea din controlul de serie, iar utilizatorul care sare peste el cu mouse-ul +vede „Numar document" gol si primeste la finalizare „Nu ati completat numarul documentului!" +(`ofacturare.vc2:9492-9495`). Schimbare de flux zilnic pentru toti utilizatorii suitei, in mijlocul +unificarii. + +Ce ramane ca **datorie deschisa pe calea veche** (constient, nu prin omisiune): numarul ars la simpla +intrare-iesire din combo si eroarea 1925 pe configuratiile cu `rezultat_serii = 0`. + +Consecinta prevazuta de decizia 69 s-a materializat: cele doua suite de paritate au incetat sa mai fie +valabile ca atare (`test_s3_tipdoc` 11/5 FAIL, `test_s3_do_cauta` cu 23 de comparatii excluse tacit). +Au fost adaptate sa **afirme divergenta explicit**, nu s-o excluda — `docs\raport_adaptare_paritate_q8.md`. + + +### Decizia 71 (Marius, 22.08.2026) — `incarca_cursor_articole` nu mai primeste `poDate` ca parametru + +Functia extrasa la S3-4a isi pierde parametrul `poDate`; foloseste doar `Private`-ul apelantului. +Semnatura devine `Lparameters tnTip, tlCopiere, poGeneratorNumere`. + +Motivul, ridicat de review-ul S3-4a (`docs\raport_s3_4a_implementare.md:142-177`): parametrul creeaza +**doua legaturi** pentru acelasi nume. Codul VFP din corp foloseste parametrul, dar parametrii SQL +`?poDate.*` se rezolva in `oExecute` la `poDate`-ul `Private` al apelantului. Azi coincid; un apel +viitor cu alt obiect (de exemplu `This.oDate` din formularul unificat, exact ce urmeaza la S3-4b) ar +construi cursorul pe alt antet decat cel trimis la Oracle — **tacut**, fara eroare si fara mesaj. +Testul actual nu ar prinde-o: foloseste acelasi obiect pe ambele cai, cu executor mock-uit. + +Garda runtime propusa in review nu functioneaza in VFP (`Evaluate('poDate')` in interiorul functiei +intoarce parametrul local, nu privatul apelantului), deci singura varianta care inchide problema prin +constructie e eliminarea parametrului. Pretul: un parametru mai putin explicit in semnatura. + +### Decizia 72 (Marius, 22.08.2026) — reasezarea formularului unificat. RASTOARNA DECIZIA 26. + +Mockup aprobat, online: https://claude.ai/code/artifact/de29f441-7f23-4c73-801f-e75d801ed3e9 +(inventarul complet control-cu-control e sectiunea 5 din mockup; nu se duplica aici). Castigul masurat +pe captura 1920x944: antet 177 -> ~139 px, banda de totaluri 154 -> ~36 px, grid 580 -> ~674 px, +adica ~4-5 randuri de articole in plus. + +**1. Formularul se deschide maximizat** (`WindowState`). + +**2. Antetul pe doua randuri grupate, etichete deasupra casetelor** (varianta D din mockup): +- rand 1, documentul: tip document, serie, numar, data, scadenta, inregistrare, data curs; +- rand 2, partenerul: client, cod fiscal **cu butonul ANAF in caseta**, sold lei, bifa + **TVA la incasare** — bifa tine de client, nu de document. + +**3. Ies din antet si coboara in sectiunea pliata `Alte date`:** analiticele (venit/cheltuiala, sectie, +lucrare, responsabil), **gestiunea sursa** — etichetata „filtru de stoc", fiindca `poDate.id_gestiune_init` +pleaca pe server ca `V_ID_GESTIUNE_INIT` si restrange ce gestiuni intra in calculul stocului +(`ofacturare.prg:471`, `:479`), nu e o valoare implicita a liniilor — si **explicatia pentru nota +contabila** (`Ed_tx_simplu1` = `poDate.explicatia4`), etichetata „nu apare pe factura": ajunge in `ACT` +prin `initializeaza_date_factura` (`ofacturare_stoc.prg:335`), dar nu apare nici pe `.frx`, nici in +`xmlefactura.prg`. + +**4. Banda de totaluri pe un singur rand**, lipita de grid: Baza · Disc. art. · Disc. factura (procent + +suma + bifa scurta „evid." cu tooltip) · Motiv · TVA · **TOTAL** la dreapta. Inlocuieste cele doua +panouri de azi. + +**5. Gridul se intinde pe toata latimea** — azi se opreste la ~1360 px din 1920. + +**6. Bara de butoane deasupra gridului**, forma minima: Linie noua / Sterge linia / Incarca articole. +**`But_verifica1` NU intra aici**: e verificarea ANAF (`do_verifica` -> `VerificaCodFiscal(..., +"ANAF_SERVICIU_WEB")`, `ofacturare_antet.prg:740-754`), ramane langa codul fiscal. + +**7. `Alte date` si `Incasare` raman JOS, sub totaluri, una langa alta.** Marius: *„nu este esentiala +pentru completarea facturii... nu vreau sa ocupe loc principal"*. **Decizia 57 punctul 2 ramane +intacta**; propunerea de a le muta deasupra gridului a fost retrasa si nu se reia. + +**8. Analiticele raman EDITABILE, definitiv — decizia 26 se rastoarna.** Marius: *„de ce sa fie blocate? +la introducere sa pot sa aleg sectia, lucrarea etc. la fel si la editarea ulterioara a facturii, care +face stergere + inregistrare noua. editarea din #6 este separata de ce faci acum"*. Venit/cheltuiala, +sectie, lucrare si responsabil se aleg si la introducere, si la reemitere; **nu se scrie `ReadOnly` pe +ele nicaieri**. Consecinta de consemnat, nu de reluat: la reemitere nota se reconstruieste uniform pe +document, deci o diferentiere facuta in #6 **la nivel de linie de nota** se pierde — e inerent +regenerarii (documentul vechi se marcheaza `sters=1`, se scrie unul nou), nu efectul acestei decizii. + +**Punctele deschise la momentul aprobarii — INCHISE de decizia 73** (sursa deasupra gridului langa buton, +buton cu eticheta dupa sursa, `explicatia`/`explicatia5` amanate, grupul contabil fara optiune de +ascundere). Ramane deschis un singur punct: +- **Adresa de livrare** — cercetare completa in `docs\cercetare\adrese_facturare_livrare.md`. Golul real: + pe o factura din comanda adresa de livrare **pleaca in eFactura** (`xmlefactura.prg:696-736`, cu + validari blocante) venind tacit din `comenzi.id_livrare` prin `poDate.oClient.adresa_livrare` + (`ofacturare.prg:685`, `694-695`), dar utilizatorul **nu o vede si nu o poate schimba** pe formularul + de facturare. `frm_alte_date` are un singur control de adresa, `clb_adresa_facturare` + (`ferestre_cere_date.vc2:2422-2440`) — control de livrare nu exista. Daca se vrea vizibila/editabila: + camp nou pe `poDate` + control in `Alte date` + propagare in `poClient`. + +**De stiut la implementare:** nici `Incasare`, nici `Alte date` nu exista azi in `frm_facturare_articole2` — +amandoua sunt inca dialogul modal `frm_alte_date` (`COMUN\clase\ferestre_cere_date.vc2:2219`), deschis cu +`Createobject("frm_alte_date")` (`ofacturare.vc2:19193`). Continutul lui se imparte astfel: **Incasare** — +`Clb_incasat`, `opt_incasat`, `Clb_serie_chit`, `Clb_nrchit`, `Cb_casa`, `chkPOS`, `chkDetaliat`, +`cmdModificaBon`, `But_modifica1`; **Alte date** — `Ct_clb_delegat`, `Ct_clb_masina`, `Ct_clb_agent`, +`Clb_dataora_exp`, `clb_adresa_facturare`, `cboTipFactura`. + + +### Decizia 73 (Marius, 22.08.2026) — inchide cele patru puncte lasate deschise de decizia 72 + +**1. Sursa (`Ct_clb_altele`) sta deasupra gridului, langa butonul de import** — nu in antet si nu in +`Alte date`. Marius: *„cred este nevoie de sursa sa fie vizibila la factura din comanda/contract/aviz/retur +etc. probabil si cu butonul importa din comanda/contract/aviz etc"*. Sursa si butonul care importa din ea +stau impreuna; dupa incarcare butonul dispare (`do_incarca_articole` face +`This.but_incarca_articole.Visible = .F.`, `ofacturare.vc2:18071`), iar sursa ramane vizibila ca referinta. + +Bara devine: `[Nr. comanda: 1234]` `[Incarca din comanda]` | `[Linie noua]` `[Sterge linia]`. + +**Verificat pe cod, nu e cerinta noua:** `Ct_clb_altele` isi schimba deja eticheta dupa tipul documentului — +„Nr. comanda", „Nr. contract", „Nr. aviz / avize", „Nr. facturi", „Gestiune destinatie", „Retur la", +„Locatie" — in `Do Case`-ul din `Init` (`COMUN\clase\ofacturare.vc2:19375-19520`), si e scos cu +`RemoveObject` exact acolo unde nu exista sursa (factura din lista de preturi, necopiata). Deci +vizibilitatea ceruta exista deja; decizia priveste **doar pozitia**. + +**2. Butonul de import primeste eticheta dupa sursa:** „Incarca din comanda" / „din contract" / „din aviz" / +„din facturi", in loc de „Incarca articole" (`ofacturare.vc2:15982`). Caption-ul se seteaza din **acelasi** +`Do Case` care seteaza eticheta lui `Ct_clb_altele` — un singur loc, cateva linii. Cand sursa lipseste si +controlul e scos, ramane „Incarca articole". + +**3. `explicatia` si `explicatia5` NU intra in aceasta reasezare.** S3b livreaza doar `explicatia4`, ca azi. +Aducerea lor pe formular cere doua campuri noi pe `poDate` (`ofacturare_comun.prg:200`) plus lantul de +scriere pana in `ACT` — story propriu, in afara #13 asa cum e definit acum. + +**4. Grupul contabil nu primeste optiune de ascundere.** Plierea sectiunii `Alte date` acopera deja cazul: +cine nu foloseste analiticele nu deschide sectiunea. Zero cod in plus, niciun camp de configurare nou. + + +### Decizia 74 (Marius, 22.08.2026) — trei corectii de asezare, dupa ce cercetarea a infirmat premisele mockup-ului + +Cercetarea completa, cu citate: `docs\cercetare\s3b_geometrie_grid_totaluri.md`. + +**1. Gridul de cursuri valutare coboara in sectiunea pliata `Alte date`.** + +Mockup-ul spunea „ascunse pe factura in lei". **Fals.** `grd_cursuri` / `lb_cursuri` apar si pe +facturile in lei: conditia de afisare nu e moneda documentului, ci existenta cursurilor zilei. +Pe ramura de lei, `ofacturare.prg:599` apeleaza `citeste_cursuri_zi(poDate.zi_curs)`, care citeste +tot `vcurs`-ul valabil in ziua respectiva (`oproceduri_curs.prg:113-122`), iar `zi_curs` e setat +neconditionat la data documentului (`ofacturare_comun.prg:247`, `:496`). Formularul nu verifica +nicaieri `in_valuta` pentru vizibilitate — doar `Used('crscursuri') And Reccount('crscursuri') > 0` +(`ofacturare.vc2:18072`, `:19269`). + +Blocul ocupa deci permanent ~100 px stanga-jos, exact unde vine banda de totaluri. Marius: intra +in `Alte date`, langa analitice — nu ocupa loc cand sectiunea e pliata, ramane accesibil pe orice +document, iar banda de totaluri ia toata latimea. **Nu se schimba conditia de populare** a +cursorului si nu se ascunde nimic pe criteriu de moneda; se muta doar locul. + +**2. „Motiv" (motivul discountului) se creeaza acum, gol.** + +Nu exista azi: zero potriviri in `frm_facturare_articole2` si niciun camp pe `poDate`. E controlul +nou cerut de reparatia discountului (deciziile 59 / 64 / 66), pe care decizia 66 l-a asezat in banda +de totaluri, langa discount. + +Marius alege sa fie creat acum, ca sa nu se rearanjeze banda a doua oara. Ca sa nu fie un camp +vizibil care minte, controlul se leaga de o proprietate noua pe `poDate` (`ofacturare_comun.prg`), +deci valoarea traieste cat sesiunea de editare a documentului. **Nu primeste coloana in baza si nu +se scrie nicaieri** — persistenta vine odata cu reparatia discountului. Limitare declarata, de +consemnat ca atare. + +**3. Latimea ramasa se imparte proportional pe toate coloanele gridului.** + +Azi `grd_factura` se intinde prin `Anchor = 15`, dar coloanele raman la latimile de design, de unde +spatiul gol de la ~1360 px la 1920 px. **Nu exista niciun mecanism de autofit in suita** — cautare +exhaustiva pe `ColumnWidth` / `AdjustColumns` / `autofit` in `COMUN\clase\*.vc2` si +`COMUN\programe\*.prg`; `_grdrow` (`_grd_base.vc2:445`) si `_grdbase` (`_grd_base.vc2:7`) n-au asa +ceva. Deci e cod nou. + +**Preferintele salvate per utilizator nu intervin aici**, contrar capcanei generale din +`COMUN\docs\capcana_grid_preferinte_utilizator.md`: instanta `Gridextra3` din acest formular are +`allowgridpreferences = .F.` explicit (`ofacturare.vc2:17172`), iar `restoregridpreferences` si +`savegridpreferences` sunt amandoua gardate de acea proprietate (`gridextras.vc2:1051-1078` si +`:1130`). Latimile redistribuite la runtime nu vor fi suprascrise. + +Codul nou merge intr-o clasa dintr-un `.prg`, nu ca metoda in `.vcx` (`reguli_lucru.md`, punctul 3); +formularul doar instantiaza si apeleaza. + + +#### S6 — Test pe fluxul real, formularul unificat +Headless (`COMUN\docs\depanare_testare_vfp.md`) + UI (`COMUN\docs\testare-ui-vfp.md`), cate un caz +pe fiecare sursa: lista de preturi, comanda, contract, aviz, retur, transfer, valuta. Comparatie +directa cu documentul emis pe calea veche: `vanzari`, `vanzari_detalii`, `act`, `rul`, totalurile +denormalizate, listarea. +*Depinde de:* S5. + +### Etapa II — editarea prin regenerare + +#### S7 — Actiunea si garzile +Actiune noua pe `frm_facturi`, sub tokenul de drepturi existent. Garzi refolosite din +`COMUN\programe\ofacturare_editare.prg` (`EsteInEFactura:12`, plus luna inchisa, luna curenta, +`ReferinteDocumenteNota`) — **nu se rescriu**, sunt deja extrase de #6. Plus garzile proprii +regenerarii: refuz daca documentul are **urmasi**. + +**PRECIZAT in runda 14, pe cod** (`docs\cercetare\garda_aviz_facturat.md`): regula exista deja integral +in Oracle, in **trei** garzi consecutive din `sterge_factura` — factura cu retururi (`TIP = 3`, +`EXPORT:5452-5462`), **aviz cu factura sau aviz de retur** (`TIP IN (1,2)`, `EXPORT:5466-5476`), si +factura din aviz ale carei avize-sursa au primit intre timp aviz de retur (`EXPORT:5480-5494`). +**S7 nu reimplementeaza regula** — o citeste **mai devreme**, read-only, inainte de intrarea in +formular: + +```sql +select count(*) from vanzari_coresp + where sters = 0 and id_vanzare_aviz = :id_vanzare and tip in (1, 2, 3) +``` + +plus subinterogarea din garda 3 pentru cazul „factura din aviz". Se interogheaza `VANZARI_CORESP`, +**nu `VANZARI.FACTURAT`** — `FACTURAT` e derivat si necitit de nicio garda. Garda din `sterge_factura` +**ramane pe loc ca plasa de siguranta**: nu se muta, nu se slabeste. + +**Gol de acoperit, iesit tot in runda 14:** pentru **proforma** (`TIP = 4`, decizia 51) garda **nu +exista deloc** — `sterge_proforma` (`EXPORT:5610-5635`) e o procedura separata fara nicio verificare, +iar calea VFP iese din `do_sterge` inainte de restul (`ofacturare_comun.vc2:4707-4719`). Garda simetrica +ceruta de S5c e **cod nou**, nu reutilizare. +*Gata cand:* actiunea refuza corect si cu mesaj clar documentele trimise in eFactura, sterse, din +luna inchisa, referentiate, sau care au urmasi (retur, factura din aviz, factura din proforma) — +**inainte** de deschiderea formularului, nu ca `ORA-20000` la final. +*Depinde de:* S6, si de inchiderea perimetrului cu #6 (fisier partajat). + +#### S8 — Incarcarea documentului in formular + +> **PROIECTAT INTEGRAL in runda 14 — `docs\cercetare\s8_incarcare_document.md` (52 KB, 8 sectiuni).** +> **Schita de mai jos e corecta ca directie, dar gresita in piese, si nu marunt:** +> 1. **`completeaza_setari_document(toFactura, .T.)` copiaza 16 proprietati din ~120** +> (`COMUN\programe\ofacturare_comun.prg:361-411`) si **niciuna de identitate**. Lipsesc serie, +> numar, data, scadenta, `text_aditional`, `id_facturare`, `id_ruta`, `tip_saft`, `efactura`, +> `listare_detaliata`, `dataora_exp`, discountul de document, `discount_evidentiat`, `eproforma`, +> valuta, cursul. +> 2. **`cursor_retur_document(V_COPIERE = 1)` nu umple documentul, ci selectorul-sursa.** Rezultatul +> intra in `crsarticole` (gridul din stanga); `crsfactura` se creeaza **gol si ramane gol** — +> comentariul din cod e explicit (`COMUN\programe\ofacturare.prg:338`, `:455-457`). Transferul se +> face doar prin `do_adauga_tot` → `do_adauga_articol`, care pentru articolele gestionabile trece +> **prin dialogul de alegere din stoc**. +> 3. **`cursor_retur_document` nu intoarce `ID_VANZARE_DET` si nici `TAXCODE`** +> (`PACK_FACTURARE:3949-4054`). Fara `ID_VANZARE_DET`, cheia de linie presupusa de S8b **nu exista**, +> iar ruta ieftina `modifica_explicatie_articol` **devine neapelabila** — primul ei parametru *este* +> `V_ID_VANZARE_DET` (`PACK_FACTURARE:14511-14519`). +> +> **Recomandarea centrala:** S8 se construieste pe precedentul care face deja exact asta — +> `relisteaza_ofacturare_stoc` (`COMUN\programe\ofacturare_stoc.prg:456-742`), care reconstituie +> `poDate` + cursorul de linii dintr-un document salvat, prin `FACT_VFACTURI` + `FACT_VFACTURI_DETALII` +> (ambele au `ID_VANZARE_DET` si `TAXCODE`). Canalul final e intrebarea 1 din §8.2 al raportului +> (*recomandat:* procedura noua `cursor_editare_document`, ca sa nu se schimbe comportamentul copierii). +> +> **CORECTIE la raportul S8, facuta de sesiunea principala (runda 14):** raportul lasa deschis (N6, +> intrebarea 7) daca „`frm_facturare_articole2` (varianta paralela)" intra in perimetru, si recomanda +> „nu acum". **Premisa e gresita: `frm_facturare_articole2` NU e o varianta paralela de exclus — e +> PROTOTIPUL pe care se construieste formularul unificat**, conform S1 („se porneste de la prototip", +> `ofacturare.vc2:15741-19355`). Deci S8 il tinteste pe el, nu pe `frm_facturare_articole`. Faptul ca +> are `do_adauga_tot` / `do_adauga_articol` proprii, cu logica divergenta (fara testul `llGestionabil`, +> `ofacturare.vc2:17476`, `:17124`), **nu e un motiv de excludere, e exact driftul din 2017 pe care S2 +> il inchide** — cele doua se unifica, nu se aleg. Partea de tipuri 48/49 din intrebarea 7 **s-a +> inchis prin decizia 60**: sunt editabile. Intrebarea 7 e deci inchisa integral. + +> **CERINTA DE EXECUTIE ADAUGATA IN RUNDA 17 — `IN_STOC` se incarca din document, nu din nomenclator.** +> Vine din consecinta 1 a deciziei 54 (vezi S10, „Trei consecinte"), si se prinde **aici**, nu la S12. +> Cu flag-ul de regenerare pornit, `IN_STOC` nu mai e re-derivat de `adauga_articol_factura`, ci **vine +> din formular** — deci valoarea pe care o incarca S8 devine valoarea care decide **descarcarea de +> gestiune la reemitere**. Azi loader-ul lui #6 o citeste din nomenclatorul curent +> (`ofacturare_editare.prg:302-303`), deci un articol devenit intre timp gestionabil (sau invers) ar +> face reemiterea sa atinga **alt stoc decat documentul initial**, tacut. +> +> Trei consecinte concrete pentru canalul de citire (intrebarea 1 din §8.2, *recomandat* (B), +> `cursor_editare_document`): +> - canalul trebuie sa intoarca `IN_STOC` **asa cum a fost la emitere**, nu `GESTIONABIL = B.IN_STOC` +> din nomenclatorul de azi, cum face `cursor_retur_document` (`PACK:3993-4000`). E un **al treilea +> argument** pentru procedura noua, langa `ID_VANZARE_DET` / `TAXCODE` si langa `ID_POL` / `ID_CTR` +> lipsa din `VVANZARI_ARTICOLE`; +> - **valoarea istorica nu e stocata nicaieri**: `IN_STOC` nu e coloana pe `VANZARI_DETALII` (verificat +> pe DB, vezi S10), traieste doar in temp. Deci primul pas al lui S8 pe aceasta cerinta e sa +> stabileasca **de unde se reconstituie** — fie din urma lasata in rulaje / gestiune pentru documentul +> respectiv, fie se accepta nomenclatorul curent ca aproximatie **declarata explicit**, fie se adauga +> coloana (migrare DB, deci **DB inainte de EXE**, ca la S10). **Nu se presupune niciuna dintre +> variante**; e o **preconditie de proiectare a lui S8**, nu un detaliu de implementare; +> - pe tipurile **48/49** cerinta se intalneste cu decizia 60: acolo invariantul e `IN_STOC = 0` prin +> constructie, deci valoarea incarcata trebuie sa fie `0` indiferent ce zice nomenclatorul azi. +> +> *Criteriu de test (intra in „gata cand" al lui S8):* un document emis cu un articol caruia i s-a +> schimbat intre timp `IN_STOC` in nomenclator, deschis in formular, poarta **valoarea de la emitere**; +> iar reemiterea lui lasa **stocul agregat neschimbat** (masurat inainte / dupa, nu prin inspectia +> codului). + +> **Cerinta noua din S8b e mai blanda decat se credea:** lookup-ul „ultimul delegat / ultima masina" +> **are deja o garda** — `Empty(id_delegat) And Empty(id_masina)` (`ferestre_cere_date.vc2:3111`). +> Riscul ramane real, dar **numai** pe documentele fara delegat si fara masina. Raportul da inventarul +> complet al celorlalte initializari „pentru document nou" care trebuie sarite (§2.2). + +`completeaza_setari_document` pentru antet + `cursor_retur_document(V_COPIERE = 1)` pentru linii, +**fara** degradarea de tip din `do_copiaza`. Se incarca in plus, fata de copiere: serie / numar / +data / scadenta reale, discountul de document (`VANZARI.DISCOUNT`), `discount_evidentiat`, textul +aditional, datele din `frm_alte_date` (delegat, auto, agent, adresa de facturare), explicatia si +`taxcode` pe fiecare linie, si legatura cu sursa (`id_comanda`, `id_ctr`, lista de avize din +`vanzari_coresp`) — ca reemiterea sa reconsume aceeasi sursa. +**Campul de sursa exista deja** — nu e de adaugat, ci de pastrat vizibil: e `Ct_clb_altele`, cu +eticheta schimbata pe tip de `do_schimba_explicatia` („Nr. contract” / „Nr. comanda” / „Nr. factura” / +„Nr. facturi” / „Locatie”, `ofacturare.vc2:9633-9643`), eliminat azi din formular cand +`gnScadereStoc = 0` si tipul e 1/5/10 fara copiere. La modificare e blocat: schimbarea sursei ar +insemna alt document, nu o corectie. +Formularul arata **identic** cu cel de introducere: fara banda de avertizare, fara coloane cu +valorile initiale, fara panou de diferente (decizia 5). Se schimba titlul ferestrei si **butonul +principal** (decizia 9, vezi S8c). +*Gata cand:* formularul deschis pe un document existent arata exact documentul, pe fiecare tip de +sursa, si nu se distinge vizual de formularul de introducere; **si** liniile incarcate poarta `IN_STOC` +de la emitere, nu din nomenclatorul de azi (cerinta rundei 17, mai sus). +*Depinde de:* S7. + +#### S8b — Rutarea scrierii dupa ce s-a schimbat +Comasarea celor trei actiuni de modificare (G-bis): la confirmare se compara starea din formular cu +cea incarcata si se alege ruta — `modifica_date_factura` pentru antet, `modifica_explicatie_articol` +pentru explicatia liniei, regenerare pentru orice atinge sumele, nimic daca nu s-a schimbat nimic. +Cele trei actiuni vechi de pe `frm_facturi` (`do_modifica`, `do_modifica_explicatie`) se retrag +abia dupa ce formularul unificat le acopera, nu inainte. +**PROIECTAT (runda 13) — `docs\cercetare\s8b_rutarea_scrierii.md`. Reteta G-bis e corecta ca directie, +dar avea un gol nedocumentat, si el schimba forma solutiei.** + +- **Cele patru rute NU sunt teste independente, ci un lant cu prioritate — sumele primele.** + Motivul e concret: **`modifica_date_factura` nu e singura ruta care scrie cei 14 parametri de antet.** + **Sapte din 14** (`id_delegat`, `id_masina`, `id_facturare`, `listare_detaliata`, `dataora_exp`, + `id_agent`, `text_aditional`) sunt scrisi **si** de calea normala de emitere, prin `scrie_factura2`, + direct din `poDate` — **verificat direct de sesiunea principala** pe apelul de la + `COMUN\clase\ofacturare.vc2:14345-14359`. Nu exista doi proprietari ai acestor campuri: e **acelasi + obiect `poDate`** citit de ambele cai. +- **Deci, cand regenerarea porneste, `modifica_date_factura` NU se mai cheama.** Inainte de regenerare + ar scrie pe randul care urmeaza sa fie sters — pierdut. Dupa, ar fi a doua scriere pe aceleasi sapte + coloane, pe alt `ID_VANZARE`. Regenerarea **este** deja calea de scriere a antetului cand sumele se + schimba, nu o cale care trebuie compusa cu alta. +- **Ordinea rutarii:** (1) s-au schimbat liniile / discountul de document? → **regenerare, gata**; + (2) altfel, antet? → `modifica_date_factura`; (3) altfel, doar explicatie de linie? → + `modifica_explicatie_articol`; (4) altfel → **nimic**. +- **Explicatia de linie e transportata gratuit de regenerare**, prin parametrii nativi ai lui + `adauga_articol_factura` (`V_EXPLICATIE`, `V_TAXCODE`) — **cu conditia** ca regenerarea sa citeasca + din cursorul curent al formularului, nu dintr-un snapshot separat. +- **Cel mai probabil fals pozitiv al criteriului „deschid si inchid → nicio scriere":** lookup-ul + „ultimul delegat / masina al clientului" din `frm_alte_date.Init` + (`COMUN\clase\ferestre_cere_date.vc2:3119-3136`). E cod gandit pentru **document nou**. Pe calea de + editare ar suprascrie delegatul **incarcat corect din document** cu ultimul folosit de client — deci + ori documentul apare „modificat" fara ca nimeni sa fi atins nimic, ori, daca ruleaza inainte de + snapshot, **salveaza tacit delegatul gresit**. **S8 trebuie sa-l sara explicit pe calea de editare.** +- **Comparatia se face pe valori rotunjite la precizia de scriere** (`gnPc` / `gnPPretV` / `gnPCant`), + nu pe valoarea binara — altfel rotunjirea singura produce diferente. + +**INCHIS, nu de reintrebat (marcaj pus in runda 17).** Punctul de mai jos are raspuns: **decizia 52** — `do_modifica` ramane activ pentru multi-selectie, iar formularul unificat preia doar cazul cu un singur document. Se pastreaza enuntul, nu intrebarea: +1. **Editarea multipla** — `do_modifica` de azi lucreaza pe multi-selectie, capacitate fara echivalent + in formularul unificat. Se accepta pierderea ei la retragere, sau `do_modifica` ramane activ separat + exact pentru cazul asta? + +**Doua goluri de inchis inainte de implementare (semnalate, nu presupuse):** +- **Ce canal scrie `serie_act` / `numar_act` / `data_act` / `data_scad` / `id_ruta` / `tip_saft` / + `efactura`** pe documentul reemis — **nu apar in `scrie_factura2`**, deci vin pe alt drum. Raspunsul + decide daca mai e nevoie de o scriere separata dupa regenerare. **Se inchide in S9.** +- ~~**`cursor_retur_document` re-deriva pretul la incarcare?**~~ **INCHIS (runda 13): NU** — pretul vine + din `VANZARI_DETALII.PRET` stocat, fara `JOIN` catre contract sau politici (`PACK_FACTURARE:3960-4062`, + verificat direct). **Mecanismul de detectie e valid.** Detaliul despre rotunjire si `DIFERENTA` — in S10. + +*Gata cand:* fiecare din cele patru situatii din tabelul G-bis produce exact scrierile din tabel si +nimic in plus; in special, deschiderea si inchiderea fara modificari nu scrie nimic. +*Depinde de:* S8. + +#### S8c — Butonul comutator de antet si salvarea separata a antetului +Decizia 9, proiectata in I. Antetul unui document existent porneste **blocat**; un singur +`but_modifica` il deschide si isi schimba imaginea in discheta de salvare; a doua apasare cheama +`modifica_date_factura` si **nu atinge articolele** — exact ce face azi `do_modifica`, dar din +formularul unificat, dupa care antetul se blocheaza la loc si butonul revine la creion. **Fara nicio +bifa**: butonul deschide tot antetul, inclusiv serie / numar / data / scadenta; cele patru bife de +azi nu se transpun. `Termina` ramane salvarea intregului document. +Se generalizeaza reteta de blocare din `clb_tx_data.dezactiveaza()` / `reactiveaza()` +(`lb_tx.vc2:521-538`) la containerele de antet care azi nu au niciuna (I), si se aplica **uniform** +tuturor campurilor de antet — asta e ce face posibila eliminarea bifelor. +Comutarea imaginii butonului se copiaza din `frm_rulaje.se_modifica_assign` (`rulaje.vc2:4716-4773`): +o singura instanta `but_modifica`, cu `.cpicturedown` / `.cpictureup` / `.Picture` rescrise impreuna +si `.Refresh()` — vezi decizia 9 pentru capcana hover-ului. +**Ce deschide efectiv butonul (decizia 25, verificat pe cod in G-bis).** „Tot antetul” nu inseamna +ca totul se salveaza pe aceeasi ruta: +- **cei 14 parametri** — pe loc, prin `modifica_date_factura`. Asta livreaza S8c; +- **grupul B** (tip document, valuta, zi curs, client, sursa, gestiune sursa, politica de preturi) si + **grupul C** (incasare) — **nu au ruta de scriere pe loc**, deci raman **blocate in etapa I**, cu + explicatie la hover; se deschid in etapa II, cand modificarea lor marcheaza documentul pentru + regenerare; +- **grupul A** (analiticele) — **editabile** (decizia 72, care rastoarna decizia 26), si la introducere, + si la reemitere; nota se reconstruieste uniform pe document. + +Deci in etapa I butonul deschide **exact cei 14**, si asta nu e o abatere de la decizia 9, ci +consecinta ei corecta pe starea de azi a codului: restul campurilor n-ar avea unde sa se scrie. +*Gata cand:* se poate corecta delegatul unui document emis, salva antetul si inchide formularul, +**fara nicio scriere in `VANZARI_DETALII` si fara recalcul de sume in note sau rulaje**; se poate +corecta si data scadentei prin acelasi buton, fara alt control intermediar; iar un document deschis +si inchis fara a apasa butonul nu produce nicio scriere. +*Atentie la formularea criteriului:* „nicio scriere in note si rulaje” ar fi un criteriu imposibil — +schimbarea seriei / numarului / datei propaga coloanele de identitate in `JV2007` si `RUL` prin +procedura insasi (I-bis). Ce se verifica e ca **nu se ating sumele**, nu ca nu se atinge tabelul. +*Depinde de:* S8. + +#### S9 — Stergerea si reemiterea intr-o singura tranzactie, cu `ID_FACT` pastrat +Partea grea. Doua lucruri simultan: + +**Constrangere de la decizia 35, inainte de orice:** reemiterea **scrie prin `pack_facturare`**, pe acelasi +drum ca emiterea (`scrie_factura2` → `contabilizeaza_articol`, cu parametrul de cont de la decizia 34). +`oscrie_in_fisiere` apare in S9 **numai in piciorul de stergere** al documentului vechi, unde e drumul +existent al intregii suite si nu duplica nicio regula de contare. **Nu se scrie si nu se corecteaza niciun +rand din `ACT_TEMP` direct din VFP.** Consecinta de secventiere: **S9 nu poate porni inaintea parametrului +deciziei 34** — nu mai exista varianta „editarea nu cere nimic in pachet". + +**VERIFICAT ca decizia 35 e realizabila** (`docs\cercetare\parametru_cont_contabilizeaza_articol.md`, +sectiunea 1): pe partea Oracle, **a doua emitere a aceluiasi document merge pe cod identic**. +`scrie_factura2` cheama `initializeaza_date_factura` (`PACK_FACTURARE:1808-1846`) la fiecare apel, iar +aceasta face `DELETE FROM VANZARI_DETALII_TEMP` (`:1835`) si `nid_act := 0` (`:1836`) — contorul folosit +de `scrie_nota` / `scrie_discount` / `scrie_tva` la fiecare `INSERT INTO ACT_TEMP` **porneste curat si la +reemitere**. Nimic din `contabilizeaza_articol`, ramura noua inclusa, nu citeste vreo stare care sa +presupuna „documentul e nou": variabilele de sesiune sunt **citite**, nu comparate cu o stare anterioara. +**Singurul obstacol real al regenerarii e in alt pachet** — `PACK_CONTAFIN`, prin `SET_IDFACT` si +`PK_DOCUMENTE`, exact ce e deja analizat mai jos in acest S9. Adica: „cod separat" inseamna **alta +procedura, in alt pachet**, nu o a doua ruta de contare — deci **decizia 35 nu e contrazisa**. + +- **Tranzactia.** Apelul de stergere (`oscrie_in_fisiere` + `finalizeaza_stergere_nota` + + `sterge_factura`) se muta in interiorul tranzactiei deschise de `do_scrie_articole`, inaintea + primului `adauga_articol_factura`. Fara `commit` intermediar (`VANZARI_DETALII_TEMP` e GTT + `ON COMMIT DELETE ROWS`). La orice eroare, rollback total. +- **Tripleta de mai sus e si ce face ciclul NEUTRU fata de stoc — nu se simplifica.** `sterge_factura` + **nu** reverseaza rulajele. Revenirea stocului vine din `PACK_CONTAFIN.STERGE_DIN_RUL` + (`UPDATE RUL SET STERS = 1, ID_UTILS = ..., DATAORAS = ...`, + `ff_2026_07_29_03_COMUN_PACK_CONTAFIN.sql:1867-1882`, apelata la `:8473`), la care se ajunge prin + `finalizeaza_scriere_act_rul(tnScrieSterge = 2)` ← `oscrie_in_fisiere(2, ...)`. Adica **stocul e + derivat** din miscari nesterse, iar marcarea lor `STERS = 1` inchide subiectul — dar **numai daca + piciorul de stergere chiar trece prin `oscrie_in_fisiere`**. O implementare care „simplifica" pasul + la un apel direct de `sterge_factura` ar lasa rulajele in picioare si reemiterea ar descarca + gestiunea **a doua oara**, tacut. Verificat pe sursa pachetului. + `docs\cercetare\stoc_la_stergere_si_reemitere.md`. +- **`ID_FACT`.** Se citeste inainte de stergere (vezi capcana `STERS = 0` din E) si se impune + documentului nou, printr-un comutator folosit **numai** de regenerare. `SET_IDFACT` nu se schimba + neconditionat — e cod comun intregii suite. +- **`ID_UTIL` / `DATAORA` (audit de creare, S14).** Acelasi tipar ca la `ID_FACT`: se citesc din + documentul vechi **inainte** de stergere si se impun explicit documentului nou — altfel `INSERT + INTO VANZARI` de la reemitere le rescrie cu utilizatorul si momentul curente, si „data/utilizatorul + crearii" se pierde tacut. Detaliu si recomandare de coloane: S14. + +**VERIFICAT — se poate, dar cere TREI schimbari simultane, nu una.** Raport: +`docs\cercetare\idfact_refolosire_si_documente.md`. Verdict: **DA, cu conditii.** + +1. **Varianta comentata din `SET_IDFACT` NU rezolva S9** — nu se reactiveaza. + (`PACK_CONTAFIN.pck:3016-3035`) cauta documentul pe `NRACT + SERIE_ACT + DATAACT + ID_CTR` cu + `STERS = 0`; dupa soft-delete `STERS` e deja 1, deci **nu-l gaseste** si cade tot pe secventa. + In plus e vulnerabila la coliziuni cu un document viitor neinrudit care are aceleasi patru campuri. + `ID_FACT`-ul se **citeste explicit inainte de stergere** (VFP il are in cursor) si se **transmite**. +2. **`SET_IDFACT` are nevoie de o cale de intrare noua, inerta implicit.** Azi **nu exista niciun + canal**: `pack_contafin.nIdFact` e scrisa neconditionat de fiecare apel, si nu exista alt global sau + parametru de sesiune. Solutia cu suprafata minima: **variabila noua de pachet** („ID_FACT fortat", + implicit `NULL`), citita **doar in corpul activ** al lui `SET_IDFACT`, consumata si resetata la prima + folosire. **Nu se atinge semnatura `SET_IDFACT(V_GCS)`** — altfel s-ar schimba apelul pentru toti. +3. **Blocajul real nu e in `SET_IDFACT`, ci in `DOCUMENTE`** — si asta e descoperirea care schimba + estimarea. Scrierea de azi (`PACK_CONTAFIN.pck:796-817`) e un **`INSERT` simplu** pe `ID_DOC`, care e + **PRIMARY KEY** (`PK_DOCUMENTE`, `fn_script.sql:5192-5198`). Stergerea (`STERGE_DIN_ACT:1835-1855`) e + **soft-delete pur** — randul vechi ramane fizic in tabel cu `STERS = 1`. Deci reemiterea cu acelasi + `ID_FACT`, cu codul de azi, **arunca `ORA-00001`**. Dovada, nu presupunere. + `MERGE`-ul deja comentat (`:818-847`) **nu ajuta**: are doar `WHEN NOT MATCHED`, deci ar ignora tacit + randul existent si documentul reemis ar ramane cu `STERS = 1`. E nevoie de **upsert real**, cu + `WHEN MATCHED THEN UPDATE SET STERS = 0, ...` pe restul coloanelor de identitate. + +**Succesiunea corecta**, in aceeasi tranzactie: citeste `ID_FACT` **si lista sursa** -> sterge documentul +vechi (soft-delete, ca azi) -> seteaza variabila fortata -> scrie documentul nou pe drumul obisnuit -> +**remigreaza atasamentele** -> commit doar daca toate au reusit; orice eroare -> rollback total, +documentul vechi intact. + +**DOI PASI ADAUGATI DE S11 (runda 13) — niciunul nu „vine gratis" din drumul normal:** +1. **Citirea listei sursa INAINTE de stergere, si refurnizarea ei la reemitere.** `VANZARI_CORESP` si + `marcheaza_facturat` **se rescriu singure** prin `CASE`-ul pe `ntip` din `finalizeaza_factura` — dar + **numai daca** `ntip` si lista sursa (`clistaid` / `clistaid_avize`) sunt refurnizate. Ele traiesc in + randurile `VANZARI_CORESP` ale documentului **vechi**, care dupa stergere nu mai sunt disponibile. + **Fara acest pas nu apare nicio eroare — corespondenta si `FACTURAT` pur si simplu nu se scriu.** + Scriere lipsa silentioasa, exact tipul de defect care trece de o verificare vizuala. +2. ~~**`UPDATE ATASAMENTE_VANZARI SET id_vanzare = :nou`**~~ — **INLOCUIT de decizia 53 (runda 14): + atasamentele NU se remigreaza, se sterg la reemitere.** Documentul reemis porneste curat; PDF-ul se + regenereaza la prima listare. Pasul ramane **cod nou**, dar e o **stergere**, nu un `UPDATE` + (`STERS = 1`, ca sa ramana consistent cu view-ul `VATASAMENTE_VANZARI`, care filtreaza `b.sters = 0` + — vezi S11). Consecinta acceptata explicit de Marius: **urma PDF-ului efectiv trimis clientului + dispare din sistem.** + +**Suprafata de risc pentru suita**, masurata: **~25-30 cai de apel** — cate o copie a lui +`COMUN\programe\oscrie_in_fisiere.prg` in fiecare produs ROA, plus trei variante care cheama +`SCRIE_IN_ACT` direct. Toate converg in acelasi punct, `SET_IDFACT(V_GCS)`. **Garantia e structurala, +nu prin inspectie:** cu variabila implicit `NULL` si citita strict local, toate cele ~25-30 de cai raman +identice cu azi — nu trebuie verificat fiecare apelant. +**Dar `INSERT` -> `MERGE` in `DOCUMENTE` e cod comun apelat de toata suita.** Practic ramura noua e +inerta (secventa e monoton crescatoare, `WHEN MATCHED` nu se declanseaza niciodata pe drumul normal), +**dar se testeaza un ciclu normal de scriere in fiecare produs care scrie facturi sau note**, nu doar in +ROAFACTURARE. + +*Gata cand:* o editare care mareste cantitatea peste stocul curent trece (stocul documentului +initial e eliberat inainte); documentul reemis are **acelasi `ID_FACT`, serie, numar si data** ca +inainte; **randul din `DOCUMENTE` e acelasi, revenit la `STERS = 0`**, nu unul nou; o eroare la scriere +lasa documentul initial intact; si un ciclu normal de scriere ramane neschimbat in celelalte produse. +*Depinde de:* S8b. + +#### S10 — Pretul care nu trebuie re-derivat +Inventarul ramurilor din `adauga_articol_factura` (H) si, unde e nevoie, un mod in care preturile +vin din formular. + +**VERIFICAT (runda 9) — `docs\cercetare\s10_pret_rederivat.md`. Verificarea 3 e inchisa.** +**Exista exact o ramura care suprascrie pretul: contractul.** Cu `OPT_FACTURARE = 3` (sau `NULL`, care +se implicit-eaza tot la 3), daca `CTR_ARTICOLE.PRET_UNITAR` e diferit de 0, acea valoare **inlocuieste** +pretul trimis de VFP — `DECODE(A.PRET_UNITAR, 0, V_PRET_TEMP, A.PRET_UNITAR)` — necondiționat de faptul +ca linia a fost sau nu atinsa pe ecran. Procedura **nu are nicio notiune de „reemitere"**: la fiecare +apel cu acelasi `V_ID_CTR` se reface derivarea din contract. Pe celelalte ramuri (implicita, restaurant) +pretul trimis de VFP e respectat ca atare. **Nu exista niciun parametru sau flag care sa opreasca +re-derivarea** — deci S10 nu poate „cere pachetului sa nu re-derive", trebuie sa lucreze in jurul +ramurii de contract. Riscul se materializeaza doar cand pretul din contract s-a schimbat intre emitere +si reemitere; pana atunci rezultatul e identic si nimeni nu observa. Detaliile pe discount, TVA si curs +sunt in sectiunile 6 si 7 ale raportului. +**PROIECTAT (runda 13) — `docs\cercetare\s10_pret_contract_reemitere.md`. Faptul portant al rundei 9 +se reconfirma la sursa, dar problema e mai larga decat „pretul".** + +- **Nu e doar pretul: acelasi `SELECT` de pe ramura de contract alimenteaza CINCI valori.** + **Verificat direct de sesiunea principala** (`PACK_FACTURARE:5149-5166`): `DECODE(A.PRET_UNITAR, 0, + V_PRET_TEMP, A.PRET_UNITAR)` **impreuna cu** `B.PROC_TVAV`, `B.ID_VALUTA`, `A.PRET_CU_TVA` si + `C.IN_STOC` intra in aceeasi interogare. Raportul semnalase trei (pret, TVA, valuta); sunt **cinci** — + se adauga flagul „preturi cu TVA" si **gestionabilitatea**, care vine din **nomenclatorul curent**, + nu din document. **Orice garda trebuie sa acopere tot setul**, nu doar pretul. +- **Nu exista alt loc care suprascrie pretul mai departe in lant** — `scrie_in_vanzari` copiaza `PRET` + neschimbat din `VANZARI_DETALII_TEMP` in `VANZARI_DETALII`. +- **Divergenta e reala, nu ipotetica:** pe baza de dezvoltare, **3 din 11** linii deja facturate pe + contract au azi alt pret in contract decat cel facturat. Esantionul e mic si **nu se extrapoleaza** la + productie — dar demonstreaza ca situatia se intampla. +- **Discountul ramane passthrough** — acolo nu e nicio problema. +- **Recomandarea raportului: varianta (c) — se avertizeaza si se cere confirmare, cu diferenta + afisata.** Satisface literal criteriul „decis explicit, nu accidental", se implementeaza **integral in + VFP**, fara sa se atinga `pack_facturare`. Variantele: (a) accepta tacit — respinsa, e exact ce cere + criteriul sa nu se intample; (b) blocheaza — mai sigura, dar frustranta daca divergentele sunt dese; + (d) ocoleste ramura trimitand `V_ID_CTR = NULL` — **respinsa motivat**, pierde **permanent** legatura + `VANZARI_DETALII.ID_CTR`. +- **„Reemitere identica" nu e garantata uniform:** pe ramurile `ELSE` / restaurant, da; pe **comenzi** + esueaza **tare** (eroare, deci vizibila); pe **contract** si pe **aviz (`ntip = 4`)** esueaza **tacit**. + Riscul pe aviz **nu fusese semnalat pana acum** ca decizie explicita. + +**GOLUL LUI S8b E INCHIS — `cursor_retur_document` NU re-deriva pretul.** Verificat direct de sesiunea +principala (`PACK_FACTURARE:3960-4062`): pretul vine din **`VANZARI_DETALII.PRET`** stocat, printr-un +`FROM VANZARI_DETALII A1` fara niciun `JOIN` catre `CTR_ARTICOLE` sau catre politici; `PROC_TVAV` la fel, +din document. **Deci mecanismul de detectare a schimbarii din S8b e valid** — un document deschis nu +apare „modificat" din cauza citirii. **Confirmat independent si pe partea VFP** (raportul S10): in +`ofacturare.prg:266-283`, ramura `Case m.llCopiere` are **prioritate indiferent de tip** — pentru un +document de contract (`2,6,26,52`) **nu se ajunge deloc** la `cursor_contract` la incarcare. Citirea si +scrierea sunt guvernate de **cursoare complet diferite, fara cod comun**: riscul de suprascriere tacita +apare **strict la re-scriere**, niciodata la deschidere. O garda pusa la scriere nu afecteaza citirea si +nu e afectata de ea. +> **Dar citirea nu e o copie curata, si asta conteaza pentru S12.** Pretul returnat e +> `ROUND(A.PRET, nzecimale_pretv) + A.DIFERENTA` (pe valuta: `ROUND(CURS * ROUND(PRET, ...) / +> MULTIPLICATOR, ...) + DIFERENTA`). Adica **rotunjit la precizia de sesiune si cu `DIFERENTA` pliata +> inauntru**. La reemitere se trimite inapoi valoarea pliata, deci **impartirea `PRET` / `DIFERENTA` se +> reface**, nu se conserva. De verificat in S12 ca o reemitere identica reproduce **suma**, chiar daca +> nu reproduce neaparat aceeasi impartire — si ca `DIFERENTA` nu se acumuleaza la reemiteri repetate. + +### RASTURNAT de decizia 54 (Marius, runda 14) — nu garda, ci eliminarea re-derivarii + +Cele trei intrebari de mai sus (blocare vs. avertizare; ramura de aviz; masurarea frecventei) **sunt +inchise, si nu prin alegerea uneia dintre variante — prin respingerea premisei lor.** Formularea lui +Marius: *„nu inteleg de ce se schimba valorile fata de ce se incarca din factura sursa care se modifica? +asta este comportamentul pe care il doresc — modific sursa"*. + +**Deci:** la reemitere se scriu **valorile din formular** (incarcate din documentul editat), nu se +reciteste sursa. Nu se cere utilizatorului sa confirme o schimbare pe care n-a cerut-o. Variantele +(b) si (c) **cad amandoua**; ramane obiectivul (a-invers): re-derivarea, unde exista pe calea de +reemitere, se **elimina**. + +**Si aici e problema reala, spusa pe fata:** runda 9 a stabilit deja ca **„nu exista niciun parametru +sau flag care sa opreasca re-derivarea"**, iar singura varianta care ocolea ramura — (d), `V_ID_CTR = +NULL` — a fost **respinsa motivat**, pentru ca pierde permanent legatura `VANZARI_DETALII.ID_CTR`. +Prin urmare decizia 54 **cere, cel mai probabil, o modificare in `pack_facturare`** (un semnal „nu +re-deriva, foloseste ce ti-am trimis" pe `adauga_articol_factura`), nu o solutie curat VFP. Asta atinge +un pachet din `COMUN`, partajat de toata suita — **iar modificarile de pachet se aplica tuturor +deodata, fara cale de intoarcere prin alegerea de la decizia 49.** Trebuie proiectata ca inerta pentru +apelantii de azi (implicit = comportamentul actual), exact ca la S4g. + +**VERIFICAT in runda 14 — `docs\cercetare\s10_rederivare_pe_calea_reemiterii.md`.** Confirmat pe fisier +**si** pe DB (`all_source`, corp VALID, `last_ddl_time = 2026-08-09 20:03:50`). + +**Se pierd intr-un SINGUR loc:** `adauga_articol_factura`, in `CASE`-ul de la `PF:5052-5220`, adica +**inainte** de `INSERT INTO VANZARI_DETALII_TEMP` (`PF:5222`). Tot ce urmeaza — copierea temp → +`VANZARI_DETALII` (`PF:13705-13757`) — e **1:1**, deci „copiaza `PRET` neschimbat" e adevarat dar **nu +e o protectie**: dauna e amonte de temp. + +**RAMURILE CARE RE-DERIVA SUNT TREI, NU UNA. Raportul rundei 9 gresea.** + +| valoare | **contract** (2/6/26/52) | **aviz** (`ntip = 4`) | **comenzi** (3/21/28/42/47) | restaurant (45) | `ELSE` | +|---|---|---|---|---|---| +| `PRET` | re-derivat din `CTR_ARTICOLE.PRET_UNITAR` daca `<> 0`; temp daca `= 0`; **NULL daca e NULL** (`PF:5149`) | **re-derivat neconditionat** din avizul sursa (`PF:5082`) | temp de facto — `WHERE A.PRET = V_PRET_TEMP` (`PF:5077`) | temp | temp | +| `PROC_TVAV` | re-derivat din `CRM_POLITICI_PRET_ART` | re-derivat din aviz | re-derivat din `COMENZI_ELEMENTE.PTVA` | derivat din `JTVA_COLOANE` | derivat din `JTVA_COLOANE` pe `ID_JTVA_COLOANA` **din formular** | +| `ID_VALUTA` | re-derivat | re-derivat | re-derivat | temp | temp | +| `PRET_CU_TVA` | re-derivat | re-derivat | re-derivat | temp | temp | +| `IN_STOC` | re-derivat din `NOM_ARTICOLE` | re-derivat | re-derivat | temp | temp | + +- **Ramura AVIZ e cea mai agresiva din tot `CASE`-ul** (`PF:5080-5103`): `PRET` se inlocuieste + **neconditionat**, nu exista nici `DECODE`, nici `AND A.PRET = V_PRET_TEMP`, **si nu exista bloc + `EXCEPTION`**. Un pret modificat pe ecran e inlocuit tacit. In plus **nu filtreaza `A.STERS = 0`**, + desi coloana exista — linii sterse ale avizului sursa pot fi citite. Si depinde de `clistaid`, care + la reemitere **trebuie repopulata** (leaga direct de pasul 1 adaugat in S9). +- **Ramura comenzi esueaza tare, nu tacit** — fara `EXCEPTION`, o linie care nu se mai potriveste da + **ORA-01403**, vizibila. Mai putin periculoasa, dar tot re-derivare. +- **Ramura contract are o portita** — `EXCEPTION WHEN NO_DATA_FOUND` (`PF:5167-5185`) pune **toate cele + cinci** pe valorile din formular. **Comportamentul cerut de decizia 54 exista deja in cod**, dar + numai pe calea de exceptie. Aviz si comenzi **nu au** asa ceva. +- **Capcana NULL, dedusa din semantica `DECODE`, nerulata:** daca `CTR_ARTICOLE.PRET_UNITAR` e `NULL` + (nu `0`), `DECODE` nu potriveste literalul `0` → rezultatul e **NULL**, deci pretul din formular se + pierde complet si linia intra cu `PRET` gol. In dev cazul nu apare (27 randuri, 0 NULL) — **ceea ce + nu spune nimic despre productie**. +- **`PROC_TVAV` nu vine din formular pe NICIO ramura** — nu e parametru al procedurii. Pe calea „buna" + se deriva din `JTVA_COLOANE` pe `ID_JTVA_COLOANA`-ul trimis de formular, deci reproduce documentul + **doar cat timp cota nu s-a schimbat**. La o modificare legala de cota, reemiterea schimba TVA-ul + **chiar si pe `ELSE`**. +- **`IN_STOC` nu ajunge in `VANZARI_DETALII`** — coloana nu exista (verificat pe DB). Traieste doar in + temp, unde decide **descarcarea de gestiune**. +- **Dupa insertul in temp nimic nu mai suprascrie cele cinci valori.** `adauga_diferente_pret` atinge + numai `DIFERENTA`, `scrie_factura_avize` numai `CANTITATE`, `scrie_seturi` numai `ID_VANZARE_SET`. + +### Cum se implementeaza decizia 54 + +**Curat VFP nu se poate — confirmat cu dovada pozitiva, nu prin absenta.** Semnatura n-are comutator +(`PF:4989-5015`, 27 de parametri, toti date de linie); globalele pachetului n-au (`PF:126-212`); poarta +`CASE`-ului se decide pe `ntip` (care ajunge in `VANZARI.TIP`, deci nu se poate falsifica) si pe +`CONTRACTE.OPT_FACTURARE` (date de contract, nu parametru). Singurele parghii VFP care ar devia pe +`NO_DATA_FOUND` sunt `V_ID_CTR = NULL` — **deja respinsa** — si `V_ID_POL = NULL`, care are **exact +acelasi defect pe alta coloana** (`PF:5258` → `temp.ID_POL` → `PF:13737` → `VANZARI_DETALII.ID_POL`) si +in plus ar rupe ramura aviz, care potriveste pe `A.ID_POL`. **Notata aici tocmai ca sa nu fie +redescoperita ca „solutie" intr-o runda urmatoare.** + +**Deci decizia 54 cere obligatoriu modificare in `pack_facturare`** — dar mica, si care nu adauga +comportament nou: + +- **Semnalul:** variabila noua de pachet („regenerare in curs"), implicit `0` = comportamentul de azi, + scrisa de VFP **dupa** `initializeaza_date_factura` (`ofacturare.vc2:13981`) si inainte de bucla de + `adauga_articol_factura`. *Recomandata* fata de un parametru `DEFAULT 0`, din trei motive: se + potriveste cu stilul pachetului (deja o punga de stare de sesiune); se scrie o data pe document, nu + o data pe linie, in toate produsele; si **punctul de resetare exista deja** — + `initializeaza_date_factura` (`PF:1808-1917`) reinitializeaza ~40 de globale si goleste temp + (`PF:1835`), deci flag-ul nu poate scapa in documentul urmator. +- **Ce face:** un `WHEN` nou, **primul** in `CASE`-ul de la `PF:5052`, cu **acelasi corp ca `ELSE`-ul de + azi** (`PF:5189-5203`). Nimic inventat — se forteaza ramura care exista deja. **Acopera dintr-o data + toate trei ramurile problematice**, fiind in acelasi `CASE`. `INSERT`-ul de la `PF:5222` ramane + neatins. +- **Inert prin constructie** pentru toti apelantii de azi, exact ca la S4g. **Consecinta de livrare: + DB inainte de EXE.** + +### Trei consecinte de acceptat explicit, nu de descoperit la S12 + +1. **`IN_STOC` ar veni din formular.** Nu murdareste documentul (coloana nu exista), dar schimba + **comportamentul de stoc** al reemiterii. Ca reemiterea sa fie identica, **S8 trebuie sa incarce + valoarea cu care s-a scris documentul initial** — si **azi nu o incarca**: loader-ul lui #6 o citeste + din nomenclatorul curent (`ofacturare_editare.prg:302-303`). **Flag-ul singur nu rezolva asta.** + **PRELUAT (runda 17) ca cerinta de executie in S8**, cu cele trei consecinte pentru canalul de citire + si criteriul de test — vezi caseta „CERINTA DE EXECUTIE ADAUGATA IN RUNDA 17" din S8. Aici nu mai e + nimic de decis. +2. **Se pierde o validare pe ramura comenzi.** Azi, `A.PRET = V_PRET_TEMP` + lipsa lui `EXCEPTION` fac + ca o linie care nu se mai potriveste cu comanda sa **opreasca** scrierea. Cu flag-ul pornit, + reemiterea unei facturi din comanda nu mai verifica linia fata de comanda. Decizia 54 pare sa vrea + exact asta, dar e schimbare de comportament — **se declara, nu se descopera**. +3. **`PROC_TVAV` ramane derivat, nu preluat.** Reproducerea exacta cere ca S8 sa pastreze + `ID_JTVA_COLOANA` **si** ca `JTVA_COLOANE` sa nu se fi schimbat. Daca se cere reproducere exacta si + peste o modificare legala de cota, `PROC_TVAV` trebuie sa devina **parametru** — schimbare mai mare + decat flag-ul, **de decis separat**. + +### Obstacol pentru S9, gasit in treacat + +**`VVANZARI_ARTICOLE` nu expune `ID_POL` si nu expune `ID_CTR`** (lista de coloane interogata pe DB) — +exact cei doi pe care `adauga_articol_factura` ii cere (`V_ID_POL`, `V_ID_CTR`) si pe care +`VANZARI_DETALII` ii pastreaza. **Reemiterea nu poate folosi acest view ca atare**: ori se completeaza +view-ul, ori loader-ul citeste direct din `VANZARI_DETALII`. Se leaga de intrebarea 1 din S8 (canalul de +citire a liniilor) — **un argument in plus pentru varianta (B), procedura noua.** + +*Gata cand:* pentru fiecare tip, documentul reemis are **exact valorile din formular** — pret, TVA, +valuta, flag „preturi cu TVA" — iar o reemitere fara modificari reproduce documentul identic, inclusiv +dupa ce pretul din contract s-a schimbat intre timp. +*Depinde de:* S9. + +#### S11 — Legaturile care raman pe `ID_VANZARE` +`ID_FACT`, seria, numarul si data se pastreaza (E, F), deci legaturile prin `ID_FACT` sunt in +regula. Ramane discontinuitatea pe **`ID_VANZARE`**, care se schimba: `ATASAMENTE_VANZARI`, +`marcheaza_facturat`, `vanzari_coresp`, si eventualele referinte de incasari / plati. +De verificat si daca `DOCUMENTE` primeste un al doilea rand pe acelasi `ID_DOC` sau il refoloseste. + +**PROIECTAT (runda 13) — `docs\cercetare\s11_legaturi_id_vanzare.md`. Inventarul s-a facut prin +FK-urile reale din Oracle, nu prin grep — si asta a scos doi consumatori pe care nicio cautare de text +nu-i gasise.** + +- **Premisa centrala, corectata: mecanismul lui #6 NU se mosteneste de S9.** La #6, documentul isi + **pastreaza randul** din `VANZARI` — `actualizeaza_vanzari` face `UPDATE ... SET COD = nou, + STERS = 0`, deci **`ID_VANZARE` nu se schimba**, doar `COD`, si atasamentele se realiniaza in acelasi + gest. La S9, reemiterea merge pe drumul normal de emitere (`INSERT ... RETURNING ID_VANZARE`), deci + **rand nou, cheie noua, si `actualizeaza_vanzari` nu se declanseaza deloc.** Tot ce e protejat azi + tacit de #6 e **expus** la regenerare. Asta nu era in plan. +- **Sapte tabele au FK declarat pe `VANZARI.ID_VANZARE`. Doua nu erau in nicio lista:** + **`REST_NOTE_PLATA`** (modul restaurant) si **`IPS_VOYAGES_VANZARI`** (modul specific unui client). + Clasificate **(A) suspecte** — pachetele lor PL/SQL n-au fost citite integral. + **INCHIS de Marius, runda 14 — riscul dispare, si nu prin presupunere.** Intrebat direct, a raspuns: + *„daca este vorba despre facturarea voiajelor din ROAACNPRO, acolo nu merge modificarea de genul + acesta, ci doar editarea de la #6"*. **Documentele acestor module sunt IN AFARA domeniului + regenerabil al #13** — pentru ele ramane calea #6 (modificare pe loc). Pachetele lor **nu mai trebuie + citite**. Doua consecinte de dus mai departe: **(a) garda lui S7 trebuie sa le refuze EXPLICIT**, nu + doar sa nu le trateze — altfel „in afara domeniului" e o intentie, nu o garantie; **(b)** conditia se + declara in S13, la actualizarea `tipuri_documente_facturare.md`. +- **`ATASAMENTE_VANZARI` — corectie de premisa, in favoarea noastra.** **Nu** sunt documente incarcate + de utilizator: sunt **PDF-uri auto-generate la listare** (`poDate.scrieAtasamente()` dupa + `export2pdf`; cursorul nu vine din niciun dialog de fisier — cautat explicit, zero potriviri). Deci + **nu e pierdere ireparabila**, cum presupusese briefingul. Se rup totusi real: view-ul + `VATASAMENTE_VANZARI` filtreaza pe `b.sters = 0`, deci dupa regenerare atasamentele vechi **dispar + tacit** din toate interogarile (inclusiv din ROAGEST / ROAIMOB), desi BLOB-ul ramane fizic. + ~~**(A) — remigrare printr-un simplu `UPDATE ... SET id_vanzare = :nou`**~~ — **INCHIS altfel, + decizia 53 (runda 14): se STERG la reemitere.** Marius a ales a treia varianta, nici remigrare, nici + marcaj „versiune inlocuita". Documentul reemis porneste curat. **Consecinta i-a fost spusa explicit + si a acceptat-o:** motivatia care sustinea remigrarea — *pastrarea urmei PDF-ului trimis clientului* — + **cade odata cu ea**; dupa editare nu mai exista in sistem dovada a ce a primit clientul. Punctul 15 + se inchide aici. +- **Incasarile si platile nu sunt afectate — si dovada e structurala, nu statistica:** niciun tabel de + incasari/plati n-are FK pe `ID_VANZARE`, si nici referinta text. Se leaga prin **`ID_FACT`**, care se + pastreaza. **(C)**, cu dovada dubla. +- **eFactura, `DOCUMENTE`, listarile, `VANZARI_CANTITATI` — (C)**, toate cheiate pe `ID_FACT` sau + rescrise automat. + +**INCHISE, nu de reintrebat (marcaj pus in runda 17).** Punctul 1 e transat de cercetarea garzilor din runda 14 (garda exista si e corecta; S7 o muta in pre-flight read-only) — ramane **limitare declarata**. Punctul 2 e rasturnat de **decizia 53**: atasamentul vechi **nu se remigreaza, se sterge**, deci premisa „factura editata ajunge cu PDF-ul vechi" nu se mai produce. Punctul 3 e inchis in runda 14, cu cerinta ca **S7 sa refuze explicit** cele doua tipuri. Se pastreaza ca inventar: +1. **Editarea unui document care are deja retur emis pe el e azi BLOCATA, nu doar riscanta** — + `sterge_factura` arunca `ORA-20000`. Garda e corecta (protejeaza lantul), iar **S7 planifica deja + sa semnaleze conditia inainte de intrarea in formular**, nu ca eroare Oracle la final. Ce ramane de + decis: se accepta ca **limitare declarata** (utilizatorul sterge intai documentele-copil), sau se + vrea alt comportament? +2. **Atasamentul vechi arata continutul dinainte de editare.** Remigrat pe documentul nou, factura + editata ajunge sa aiba atasat PDF-ul **vechi**. De decis daca asta e exact ce se vrea (audit trail), + sau daca vechiul ar trebui marcat cumva ca „versiune inlocuita". +3. **`REST_NOTE_PLATA` / `IPS_VOYAGES_VANZARI`** — de confirmat ca tipurile lor sunt in afara + domeniului regenerabil. + +*Gata cand:* documentul reemis se regaseste corect in borderoul eFactura, in listari, in atasamente +si in rapoartele care il cauta dupa numar. +*Depinde de:* S9. + +#### S12 — Test pe fluxul real, regenerarea +Cate un caz pe fiecare sursa, rulat de doua ori (o data fara modificari — reemitere identica, o data +cu modificari de cantitate, pret si linii adaugate / sterse). Se verifica: documentul initial +`sters = 1` cu nota si rulajele lui, documentul nou corect, sursa eliberata **si** reconsumata +(comanda redevine facturabila si redevine facturata), stocul corect dupa ciclu, totalurile +denormalizate din `VANZARI`, si ca o reemitere identica produce **exact** aceleasi sume. + +**Probe adaugate de runda 13:** +- **Reemitere repetata de trei ori pe acelasi document** — `DIFERENTA` **nu se acumuleaza**. Motivul: + `cursor_retur_document` intoarce pretul deja **rotunjit si cu `DIFERENTA` pliata inauntru** (S10), iar + reemiterea trimite inapoi valoarea pliata, deci impartirea `PRET` / `DIFERENTA` se **reface** la + fiecare ciclu. Se verifica **suma**, nu impartirea — dar se verifica si ca impartirea nu deriveaza. +- **„Reemitere identica" nu e garantata uniform** (S10): pe `ELSE` / restaurant da; pe **comenzi** + esueaza **tare** (eroare vizibila); pe **contract** si pe **aviz (`ntip = 4`)** esueaza **tacit**. + Fiecare din cele patru situatii isi are cazul lui de test, cu rezultatul asteptat **declarat dinainte** + — inclusiv cele care trebuie sa dea eroare. +- **Deschid si inchid fara sa modific nimic → nicio scriere** (S8b), rulat pe un document al unui client + **cu activitate recenta** — cazul in care lookup-ul „ultimul delegat / masina" ar polua antetul. + +*Depinde de:* S10, S11. + +#### S14 — Audit: creare, modificare si stergere pe document + +**Cerinta (decizia 63):** pentru fiecare document, sase informatii de audit — data + utilizator +pentru creare, pentru modificare (daca e cazul) si pentru stergere (daca e cazul). + +**Cercetarea s-a terminat** — `docs\cercetare\audit_vanzari_creare_modificare_stergere.md`. +Verificat pe `all_tab_columns` (`ROA_CENTRAL`, schema live) si pe cod. + +**Ce exista deja, pe `VANZARI`:** +- `ID_UTIL` + `DATAORA` — **creare**, ambele `NOT NULL`, scrise consecvent la fiecare `INSERT` + (`PACK_FACTURARE.scrie_in_vanzari`, `EXPORT:13598-13640`, si a doua ruta din + `finalizeaza_avize_lucrare`, `EXPORT:14930`). Niciun `UPDATE VANZARI SET ID_UTIL/DATAORA` in tot + pachetul (cautare directa, zero rezultate). +- `ID_UTILS` + `DATAORAS` — **stergere**, nullable, scrise consecvent de `sterge_factura` + (`EXPORT:5496-5499`) si `sterge_proforma`. +- (neceruta, dar arata conventia) `ID_UTILFACT` + `DATA_FACTURAT` — facturare din aviz. +- Pe `VANZARI_DETALII`: acelasi tipar de creare/stergere, plus `ID_UTIL_VALID` + `DATAORA_VALID` + (validare). + +**Conventia casei:** o pereche utilizator + data per eveniment. Patru din cele sase informatii +cerute exista deja si se scriu consecvent. + +**Ce lipseste, integral:** nicio coloana de **modificare**, nici pe `VANZARI`, nici pe +`VANZARI_DETALII`. Interogat explicit `all_tab_columns` cu filtru pe `%MODIF%` — zero rezultate. +`DATA_ACT` nu e echivalentul: e data contabila, propagata in `ACT`/`DOCUMENTE`/`JV2007`/`RUL` +(verificat in `modifica_date_factura`, `EXPORT:14439-14500`), nu un marcaj de audit. + +**Capcana, si e miezul povestii:** editarea din #13 se face prin stergere + reemitere (S9), iar +documentul nou se scrie pe **acelasi `INSERT`** ca o creare normala (`scrie_in_vanzari`). Fara un +pas explicit, `ID_UTIL` / `DATAORA` ale documentului reemis ar deveni tacut utilizatorul si data +**modificarii**, iar informatia despre creare s-ar pierde — exact ce cere decizia 63 sa nu se +intample. **Precedentul de rezolvare exista deja in plan, pentru `ID_FACT`** (sectiunea „E. +`ID_FACT`", linia 762, si S9: se citeste din documentul vechi inainte de stergere si se impune +celui nou). **Niciun pas echivalent nu e proiectat azi pentru `ID_UTIL` / `DATAORA`** — cautare +directa in tot planul, zero mentiuni. + +**Atenuare, nu solutie:** la regenerare randul vechi ramane cu `STERS = 1` si cu `ID_UTILS` / +`DATAORAS` completate — iar acea stergere **este** momentul modificarii. Cum `ID_FACT` se pastreaza +peste regenerare, lantul vechi → nou e parcurgibil, deci o parte din istoric e reconstruibila din +randurile existente, fara coloane noi. O pereche explicita pe documentul curent ramane insa +preferabila: se citeste direct, fara parcurgerea lantului. + +**Complicatie reala, verificata pe cod livrat de #6:** editarea de linie +(`COMUN\programe\ofacturare_editare.prg`) scrie `id_utils` / `dataoras` pe `VANZARI_DETALII` si pe +randuri care **nu sunt sterse** — `ofacturare_editare.prg:492-494` (corect, cu `sters = 1`), +`:501-505` (odata cu `sters = 0`, pe rand viu) si `:522-525` (pe un rand proaspat inserat). Acolo +perechea de stergere e folosita ca marcaj „cine a umblat ultima data", nu strict ca „sters de/la". +**Consecinta:** un raport de audit nu poate citi `ID_UTILS` / `DATAORAS` ca „sters de/la" fara sa +puna si `STERS` in conditie. `ofacturare_editare.prg` e perimetrul lui #6, in lucru — semnalat aici, +neatins. + +**Recomandare (de confirmat, nu decisa):** +1. **O singura pereche noua pe `VANZARI`**, urmand conventia casei — nume propus + `ID_UTILM` / `DATAORAM` (utilizator si data ultimei modificari). Numele e de confirmat. +2. **La regenerare (S9): `ID_UTIL` / `DATAORA` se transporta din documentul vechi in cel nou**, + exact ca `ID_FACT` — vezi nota adaugata la S9. Perechea noua de modificare primeste utilizatorul + curent si `sysdate`. Fara acest pas, creare si modificare se suprapun. +3. **Perechea de stergere nu are nevoie de nimic** — exista si e scrisa consecvent. +4. **Cere migrare de DB** (`ALTER TABLE` pe `VANZARI`) — **DB inainte de EXE**, ca la S10. Scriptul + intra in `D:\ROA\DATABASE\SCRIPTURI_CLAR\`, `versiune_db.txt` se bumpeaza. + +**De decis cu Marius — AMBELE INCHISE prin decizia 65 (runda 16):** +- **(a)** Auditul e cerut numai pe antet (`VANZARI`) sau si pe linii (`VANZARI_DETALII`)? **Raspuns + prin deductie, nu spus explicit de Marius:** locul de afisare ales de el e gridul din `frm_facturi`, + care listeaza **documente** — deci auditul e pe **antet**. +- **(b)** Se afiseaza undeva in interfata (formular / lista), sau ramane doar pentru interogare? + **Da, se afiseaza** — in **gridul din `frm_facturi`**, nu in formularul facturii propriu-zise. + Prim pas de implementare: inventarul acelui grid, posibil sa existe deja coloane de audit. + +*Gata cand:* documentul reemis pastreaza `ID_UTIL` / `DATAORA` ale creatiei originale; perechea noua +de modificare e completata la fiecare regenerare, cu utilizatorul si momentul curente; perechea de +stergere ramane neschimbata fata de azi; migrarea DB e aplicata inainte de EXE. +*Depinde de:* S9. + +#### S13 — Diff, review, changelog, documentatie + +**Atentie: S13 NU e momentul in care se face review-ul.** Conform metodei de executie, **fiecare story +isi are propriile teste, propriul diff si propriul code review, inainte de commit-ul ei** — S13 nu +strange la final ce n-a fost revizuit pe parcurs. Ce ramane aici e **inchiderea**, adica exact ce nu se +poate face per story: + +- **Review de ansamblu**, pe suma povestilor: coerenta intre ele, cai ramase orfane, cod mort din + fluxul vechi care trebuia retras si n-a fost, si verificarea ca alegerea de la decizia 49 chiar + functioneaza in ambele sensuri. +- **Changelog** `:nou:`, cu mentiunile declarate explicit pe parcurs: asimetria `do_modifica` + corectata prin recalculul la cerere (decizia 45), si faptul ca `do_modifica` ramane activ pentru + multi-selectie (decizia 52). +- **Documentatie**: `COMUN\docs\tipuri_documente_facturare.md` — ce tipuri sunt regenerabile si ce + tipuri sunt **declarate explicit ca neacoperite** (48/49 si `frm_facturare_articole2`, daca se merge + pe recomandarea din S8), plus confirmarea din S11 pentru `REST_NOTE_PLATA` si `IPS_VOYAGES_VANZARI`. +- **Rularea suitei complete** — toate testele scrise per story, la un loc, nu doar cele ale ultimei + povesti. +- **Commit-urile finale**, in **ambele** repo-uri (ROAFACTURARE si COMUN), dupa aprobare. + +--- + +## Riscuri + +- ~~**Cel mai mare: perimetrul comun cu #6.**~~ **Inchis prin decizia 30** — implementarea lui #13 + incepe dupa terminarea lui #6, deci nu exista doi scriitori simultan pe `ofacturare_comun.vc2` / + `ofacturare_editare.prg`. Riscul revine doar daca ordinea se schimba. +- ~~**Riscul „cod nou in `pack_facturare`" e evitat prin constructie (decizia 27-bis).**~~ **Caduc dupa + deciziile 34 si 35, si riscul revine ca risc principal.** Politica tehnica per `SCC` si scrierea in + `CRM_POLITICI_PRET_ART` nu se mai fac — deci cade si riscul ca o politica tehnica sa apara in + `caut_politici_curente_util()`. In locul lui: **`pack_facturare` se modifica** (parametru de cont + + ramura fara politica in `contabilizeaza_articol`), iar pachetul e **comun intregii suite** — ROACONT, + ROAGEST, ROACONTRACTE, ROAAUTO, ROAACNPRO. Mitigarea e structurala, nu prin inspectie: parametru + **`DEFAULT NULL` la finalul listei** si ramura inerta cand lipseste, astfel incat apelantii existenti + sa fie identici cu azi. + **MASURAT — `docs\cercetare\suprafata_regresie_contabilizeaza_articol.md`. Riscul e mai mic decat parea.** + `contabilizeaza_articol` are **trei apelanti, toti interni pachetului**, toti pozitionali cu acelasi + singur argument — **zero apelanti externi, nici PL/SQL, nici VFP, in niciunul din cele sapte produse**. + `adauga_articol_factura` e apelata **pozitional peste tot**, niciodata cu notatie pe nume (`=>`), iar + cele sapte copii `COMUN\clase\ofacturare.vc2` sunt **acelasi sit de apel duplicat**, nu sapte + implementari care ar putea diverge: fisierele difera intre ele (patru variante distincte pe MD5), dar + **textul apelului e identic cuvant cu cuvant**, doar offsetul de linie difera. Exista in plus doua + implementari proprii reale — ROAGEST (`Programe\ofactureaza.prg:264`) si ramura `_deviz` din ROAAUTO / + ROAACNPRO. + **Precedentul e activ, nu teoretic:** ROAGEST apeleaza azi `adauga_articol_factura` cu **24 din 25 de + parametri**, omitand `V_TAXCODE` si `V_LOT` tocmai pentru ca au `DEFAULT NULL`. Tiparul propus e deja + in uz in productie. + **Ce ramane:** toate cele **sapte** produse emit efectiv prin acest pachet, deci aria de regresie e + toata suita chiar daca niciun apelant nu se modifica. +- ~~**Anomalie preexistenta: `scrie_factura2` apelata cu 16 din 17 parametri.**~~ **INCHISA — fals + pozitiv, nu intra ca risc.** `oExecuta` (`COMUN\programe\oproceduri_comune.prg:121-159`) deleaga la + `oExecute` (`:173-504`), care face `SQLExec(lnHandle, lcSql, lcCursor)` (`:330`) — **textul SQL nu e + rescris**, deci al 17-lea parametru chiar n-are placeholder. Explicatia e tiparul cunoscut al + driverului ODBC Oracle: cand ultimul parametru declarat e un `REF CURSOR OUT`, driverul il ia din + catalog, nu din textul apelului, si intoarce randurile ca *result set* — captat exact de al treilea + argument al lui `SQLExec`. *Nuanta de pastrat:* mecanismul din interiorul driverului **nu poate fi + confirmat din codebase**, e verificabil doar prin comportament; dovada e indirecta, dar consistenta — + acelasi tipar in toate cele patru situri, neschimbat de la `v 2.0.13` la `v 2.0.93`, iar ecranul de + verificare depinde de acel cursor populat la fiecare emitere, deci un apel esuat s-ar fi vazut demult. +- **Decizia 35 leaga etapa II de aceeasi modificare de pachet.** Editarea prin regenerare nu mai are cale + proprie (canalul `oscrie_in_fisiere` e respins ca al doilea cod de contare), deci **un blocaj pe + parametrul deciziei 34 blocheaza si S9-S12**, nu doar etapa I. In schimb dispare riscul de divergenta + intre regula de contare de la emitere si cea de la editare. +- **`ofacturare.vc2` e in COMUN si are 843 KB / 25 de clase**, folosite de toata suita. Formularul + vechi trebuie sa ramana functional pana cand cel nou acopera toate tipurile — deci comutator, nu + inlocuire. +- **`ID_FACT` pastrat cere atingerea unui punct comun intregii suite.** `SET_IDFACT` e in + `PACK_CONTAFIN` si e chemat de fiecare scriere de note din toate produsele ROA. Comutatorul din S9 + trebuie sa fie inert pentru toate celelalte cai; altfel regresia nu e in ROAFACTURARE, ci peste tot. +- **Formularul identic la introducere si la modificare taie o plasa de siguranta.** Decizia 9 o repune + **doar pe antet** — butonul protejeaza campurile de antet, nu si liniile. Pe articole nu exista niciun + semnal ca modificarea va sterge si va rescrie documentul; confirmarea de la `Termina` ramane + singurul moment in care se poate spune ce urmeaza sa se intample. De formulat cu grija. +- **Ascunderea datei de curs poate lasa un document fara curs corectabil.** `poDate.zi_curs` se + trimite neconditionat catre cursoarele de articole; daca lipseste cursul pentru acea zi, Oracle da + eroarea 20005 si se deschide formularul de curs — dar utilizatorul nu mai are unde sa corecteze data + daca i-am ascuns campul. Regula din M trebuie sa aduca inapoi campul in exact acest caz. +- **Sectiunea pliata ascunde campuri care schimba documentul.** Analiticele si datele de incasare + intra sub un panou inchis implicit. Daca un camp obligatoriu pe un anumit tip ajunge acolo, + utilizatorul primeste eroarea fara sa vada campul. Panoul trebuie sa se deschida singur cand + contine un camp necompletat si obligatoriu, si sa arate in antet ca are ceva completat. +- **Mutarea `frm_alte_date` muta si alocarea de numere.** Bonul fiscal, POS-ul si chitanta primesc + numar din masina de stari a incasarii. Daca ea ajunge sa ruleze la deschiderea formularului in loc + de la alegerea tipului de incasare, se aloca numere pentru documente care nu se mai emit. +- **`S3c` si `S4c` ating cod comun.** Parametrizarea sursei atinge `factureaza` (toata suita), iar + discountul editabil atinge listarile si eventual eFactura. Fiecare merge separat, cu regresie + proprie. +- **Reemiterea nu e idempotenta prin constructie.** Daca la reemitere pretul se re-deriva (H) sau + cursul valutar al zilei difera, documentul "nemodificat" iese cu alte sume decat originalul. + Testul din S12 (reemitere identica) exista tocmai ca sa prinda asta. +- **`verifica_total_document`** (`PACK_FACTURARE:16009+`) insereaza automat o linie de corectie cand + totalul difera de suma notelor. La regenerari repetate trebuie confirmat ca nu se acumuleaza — + aceeasi intrebare ca S7 din #6. +- **Cautarea in linie schimba un obicei.** Utilizatorii care lucreaza azi cu gridul de articole + vizibil si filtrare locala pierd vederea de ansamblu. Merita verificat pe un client inainte de a + scoate definitiv gridul vechi. + +## Dependente + +- **#7 (pret cu TVA pe linie)** — flagul `pret_cu_tva` pe linie apare in gridul unificat; se + pastreaza comportamentul stabilit acolo. +- **#12 (nomenclator ca lista de preturi)** — S4 construieste cautarea in linie peste politici de + preturi; daca #12 muta pretul in nomenclator, S4 se rescrie. +- **#16** — bug-ul de focus / renumerotare la revenirea din cursul valutar. Confirmat ca apare **dupa** + ce antetul s-a inchis si s-a eliberat (`ofacturare.prg:248`), la recuperarea erorii Oracle 20005. + In formularul unificat nu mai exista un antet inchis la care sa te intorci, deci **mecanismul lui + dispare** — de confirmat in S4d, nu de presupus. +- **#6** — **dependenta de calendar, nu de perimetru** (decizia 30): implementarea lui #13 incepe + dupa ce #6 se termina. Proiectarea si cercetarea merg mai departe in paralel. + +## Preconditii de mediu + +- Orice DDL / modificare `PACK_FACTURARE`: sursa de referinta e `MARIUSM_AUTO` pe `ROA_CENTRAL`, + nu productia (`COMUN\docs\scripturi-migrare-db.md`). Scripturi nivel Oracle 10.2, CRLF, + idempotente, `versiune_db.txt` actualizat. +- Sursa completa `PACK_FACTURARE` nu e in working copy; copia pe disc e + `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\03\ff_2026_03_23_04_COMUN_PACK_FACTURARE.sql` (16 948 linii), + cu alta numerotare decat exportul din baza. +- Editarile pe `.vc2` / `.sc2` trec prin `git_sync.ps1` + `txt2vcx.ps1`, cu atentie la + `COMUN\docs\conventie_encoding_cp1252.md` (diacriticele din `.vc2` sunt cp1250 — vezi si memoria + proiectului). diff --git a/docs/plan_index.md b/docs/plan_index.md index e023bc7..a67e621 100644 --- a/docs/plan_index.md +++ b/docs/plan_index.md @@ -9,7 +9,7 @@ si verificari proprii, ca sa nu se amestece. | — | #8 — denormalizare VANZARI | **mediu** (scop extins 06.08) | `PACK_FACTURARE` + ambele view-uri (COMUN) + reparare date | **TERMINAT si COMIS** 07.08.2026 (r17990-r17993). Planul a fost sters la curatenie; istoricul e in `progres.md` | | — | #7 — pret cu TVA pe linie | mic-mediu | doar VFP, gridul de articole din `frm_facturare_articole` | **TERMINAT** 08.08.2026 (changelog 2.11.14). Editarea flagului pe o factura **deja salvata** a fost mutata in #6. Planul a fost sters la curatenie; istoricul e in `progres.md` | | — | #6 — editare factura emisa | mediu | `omodificari.vc2` (COMUN) + `PACK_FACTURARE` | **INCHIS** 20.08.2026 (r18026, changelog 2.11.15). Planul a fost sters la curatenie; istoricul — inclusiv cele 2 esecuri ramase pe „factura din aviz" din S8 — e in `progres.md` | -| 1 | [#13 — formular unificat + editare prin regenerare](plan_13_unificare_formular_facturare.md) | **mare** | `ofacturare.vc2` + `ofacturare.prg` + `PACK_FACTURARE` (COMUN) | **PROIECTAT INTEGRAL, cod neatins** (17 runde de proiectare, 11.08.2026). Etapele I si II sunt proiectate story cu story (S1-S14); raman testele (S6, S12) si inchiderea (S13). **65 de decizii luate**, niciuna deschisa. Perimetrul cu #6 e transat: **#13 incepe dupa terminarea lui #6** (decizia 30). Mockup: [`mockup_13_formular_unificat.html`](mockup_13_formular_unificat.html). Stare curenta: `handoff_13_formular_unificat.md` | +| 1 | [#13 — formular unificat + editare prin regenerare](plan_13_unificare_formular_facturare.md) | **mare** | `ofacturare.vc2` + `ofacturare.prg` + `PACK_FACTURARE` (COMUN) | **INCHIS ca plan (08.09.2026)**, pe branch `plan13-s2` (ROAFACTURARE + COMUN), nemerge-uit si netestat manual; ce ramane sunt datorii. Stare curenta: `handoff_reluare_13_b13.md`, `handoff_plan13_executie_continua.md` | | — | [#12 — nomenclator ca sursa de pret](plan_12_nomenclator_ca_lista_preturi.md) | mediu | depinde de varianta | valabil, **amanat** | | — | [#11 — integrare politici de preturi](plan_11_integrare_politici_preturi.md) | mare | COMUN + ROAPRETURI | valabil, **amanat** | | — | [#10 — integrare contracte](plan_10_integrare_contracte.md) | mare | COMUN + ROACONTRACTE | valabil, **amanat** | diff --git a/docs/progres.md b/docs/progres.md index e320c33..100ead6 100644 --- a/docs/progres.md +++ b/docs/progres.md @@ -1,10 +1,37 @@ -# Progres implementare — ROAFACTURARE, punctele 6, 7, 8 (10, 11, 12 amanate) +# Progres implementare — ROAFACTURARE, punctele 6, 7, 8, 13 (10, 11, 12 amanate) **Fisierul curent de stare.** Orice sesiune il citeste primul si il actualizeaza inainte sa se incheie. Planurile (`plan_0*.md`) spun *ce* e de facut; acesta spune *unde s-a ajuns*. Istoricul sesiunilor nu se pastreaza aici — doar starea la zi, deciziile, faptele stabilite si datoriile. -Ultima actualizare: **19.08.2026**. +Ultima actualizare: **21.08.2026**. + +> **RUNDA 21.08.2026 — #13 S2 implementata si comisa pe branch.** +> Cerere Marius, aprobata azi: **fara teste manuale, commit pe branch, continuare in alta sesiune.** +> +> **Ce s-a schimbat**: `COMUN\programe\ofacturare.prg` — procedura duplicata `factureaza2` (fork mort +> din 2017, zero utilizatori reali) a fost stearsa; alegerea formularului se face acum in +> `factureaza`, dupa flagul `Private plFacturareNoua`. `Clase\ofundal_facturare.vc2` — butoane noi +> `Page2.Cw10` „Facturare (nou)" (4 optiuni: lista de preturi, contract, comanda, aviz) si +> `Page3.Cw5` „Avize (nou)" (4 optiuni), ambele seteaza `plFacturareNoua` si deschid +> `frm_facturare_articole2`. +> +> **Decizia 67** (Marius, 20.08.2026): intrare separata in meniu, nu comutator/dialog; ambele cai +> facturare vechi si noua raman accesibile simultan. Perimetru: vanzarile din stoc (`Cw5..Cw9` -> +> `initializeaza_vanzare_din_stoc`) **nu** trec prin `factureaza` — raman in afara #13. +> +> **NETESTAT MANUAL** — decizia explicita a lui Marius, 21.08.2026. Ramane datorie de verificat cel +> tarziu la S6 (test pe flux real, planificat dupa S5). +> +> **Commis pe branch `plan13-s2`** in ambele repo-uri (ROAFACTURARE + COMUN), **fara SVN** — SVN +> ramane neatins pana la aprobarea finala si testele manuale. Detalii si hash-uri: +> `docs\handoff_13_s2.md`. +> +> **Urmatoarea story: S3** — portarea logicii de antet in formularul unificat. +> +> **Capcana platita**: FoxBin2Prg ordoneaza obiectele si metodele **ASCII-lexicografic**, nu numeric — +> `Cw10` cade intre `Cw1` si `Cw2` in textul `.vc2`, nu dupa `Cw9`. De verificat cu `vfp_symbols.ps1` +> inainte de orice cautare pe pozitie in fisier. > **RUNDA 19.08.2026 — butoanele de linie trec pe butoanele laterale ale grid-ului de articole.** > Cerere Marius, executata si testata; **fara commit**. @@ -2326,26 +2353,26 @@ Ramase: partea VFP a punctului 6 si intrarea de changelog pentru runda 6 (nu exi livrare nu sunt surse de stare pentru ce e in Oracle. Starea se citeste din `VERSIUNE` + `ALL_SOURCE`, prin diff, inainte de a re-aplica ceva. - Ramane valabila interdictia: **nu se re-aplica `ff_2026_08_20_01`** — ar sterge punctul 6. - -## Runda 6 punctul 6 + runda 7 (sincronizare) - 20.08.2026, seara - -Starea completa, cu inventar pe `fisier:linie`, ce s-a stabilit si ce e in lucru: -**`docs\handoff_r6_r7_sincronizare.md`**. Pe scurt: - -- **Punctul 6 (explicatie TVA in `frm_facturi`): TERMINAT**, write-back verificat prin - reconversie (0 linii diferenta, md5 identic), probat pe ecran de Marius de doua ori. - Filtrul final al listei: `id_jtva_coloana > 0 AND afisat > 0 AND jv = 1 AND cota_tva = `. Pe date (`vjtva_coloane`, `MARIUSM_AUTO`): `afisat = 0` sunt liniile de TVA, - `afisat = 1` bazele, `afisat = 2` neimpozabilele; `jv = 1` tine afara explicatiile de - achizitie, care altfel treceau de garda `FACT-029` fiindca au aceeasi cota. -- **Runda 7, in lucru**: sincronizarea articole-rulaje porneste doar din buton (se scoate - declansarea de la salvare, `omodificari.vc2:14484-14494`), si `AplicaModificareTrul` - (`ofacturare_editare.prg:924`) scrie si `pretv`/`tvav`, nu doar `pretvtva`. - **Perimetru fixat de Marius: doar preturi, nu si `valoarev`/`valtvav`/`valoarevcTVA`** - - sincronizarea e intre cantitate si preturi. Ramane de raspuns, ca fapt, cine recalculeaza - acele valori dupa sincronizare. -- **Nimic nu e comis** - decizia lui Marius e un singur commit, dupa ce sunt gata toate trei. -- `roafacturare.PJT`/`.PJX`/`.exe` apar modificate: zgomot din sesiunea lui de VFP, se lasa asa. + +## Runda 6 punctul 6 + runda 7 (sincronizare) - 20.08.2026, seara + +Starea completa, cu inventar pe `fisier:linie`, ce s-a stabilit si ce e in lucru: +**`docs\handoff_r6_r7_sincronizare.md`**. Pe scurt: + +- **Punctul 6 (explicatie TVA in `frm_facturi`): TERMINAT**, write-back verificat prin + reconversie (0 linii diferenta, md5 identic), probat pe ecran de Marius de doua ori. + Filtrul final al listei: `id_jtva_coloana > 0 AND afisat > 0 AND jv = 1 AND cota_tva = `. Pe date (`vjtva_coloane`, `MARIUSM_AUTO`): `afisat = 0` sunt liniile de TVA, + `afisat = 1` bazele, `afisat = 2` neimpozabilele; `jv = 1` tine afara explicatiile de + achizitie, care altfel treceau de garda `FACT-029` fiindca au aceeasi cota. +- **Runda 7, in lucru**: sincronizarea articole-rulaje porneste doar din buton (se scoate + declansarea de la salvare, `omodificari.vc2:14484-14494`), si `AplicaModificareTrul` + (`ofacturare_editare.prg:924`) scrie si `pretv`/`tvav`, nu doar `pretvtva`. + **Perimetru fixat de Marius: doar preturi, nu si `valoarev`/`valtvav`/`valoarevcTVA`** - + sincronizarea e intre cantitate si preturi. Ramane de raspuns, ca fapt, cine recalculeaza + acele valori dupa sincronizare. +- **Nimic nu e comis** - decizia lui Marius e un singur commit, dupa ce sunt gata toate trei. +- `roafacturare.PJT`/`.PJX`/`.exe` apar modificate: zgomot din sesiunea lui de VFP, se lasa asa. ### Runda 7 - terminata, 20.08.2026, 22:40 diff --git a/docs/sabloane/README_sablon_vcx.md b/docs/sabloane/README_sablon_vcx.md new file mode 100644 index 0000000..c80c845 --- /dev/null +++ b/docs/sabloane/README_sablon_vcx.md @@ -0,0 +1,150 @@ +# Sablon `.vc2` gol — pornire pentru o biblioteca de clase noua + +`sablon_vcx_gol.vc2` e punctul de plecare cand trebuie creata o biblioteca `.vcx` noua in +proiect. Exista pentru ca `txt2vcx.ps1` **refuza permanent** sa creeze un binar `.vcx` +inexistent (`txt2vcx.ps1:203-206`, garda nu verifica provenienta binarului, doar existenta lui) — +deci fluxul text->binar nu poate produce singur un `.vcx` nou de la zero. Solutia e sa pornesti +de la un binar-purtator **copiat**, peste care aplici acest text. + +Continut: antetul FoxBin2Prg (6 linii, obligatoriu identic) + **o singura clasa-exemplu +minimala** (`AS custom`, fara obiecte-copil). Am ales varianta cu o clasa, nu biblioteca goala +de-a dreptul, pentru ca n-am gasit nicaieri in proiect un `.vcx` cu 0 clase de folosit ca +referinta si n-am vrut sa presupun formatul text al unei biblioteci goale fara sa-l vad — o clasa +`custom` fara copii insa exista deja ca tipar verificat (`COMUN\clase\_baza.vc2:114-121`, clasa +`_custom`) si a fost **confirmata printr-un test complet text->binar->text in izolare** (vezi mai +jos), deci e alegerea provata, nu doar plauzibila. + +## Procedura, pas cu pas + +### 1. Alege binarul-purtator + +Trebuie sa fie o pereche `.vcx` + `.vct` **existenta** (ambele — `.vct` e memo-ul cu proprietatile +lungi/codul; fara el binarul nu se deschide). Nu presupune care e cea mai mica — verifica: + +``` +ls -la Clase/*.vcx Clase/*.vct +``` + +Alege perechea cu cel mai mic `.vcx`. Nu conteaza ce clase contine acum — le stergi la pasul 3. + +### 2. Copiaza si redenumeste + +``` +cp Clase/.vcx Clase/.vcx +cp Clase/.vct Clase/.vct +``` + +Ambele comenzi, nu doar `.vcx` — un `.vcx` fara `.vct` pereche e un binar corupt pentru VFP. + +### 3. Aplica sablonul si inlocuieste + +``` +cp docs/sabloane/sablon_vcx_gol.vc2 Clase/.vc2 +``` + +In fisierul copiat, inlocuieste **toate** aparitiile: + +- `SourceFile="__NUME_BIBLIOTECA__.vcx"` -> `SourceFile=".vcx"` (trebuie sa fie + exact numele fisierului `.vcx` de la pasul 2, cu tot cu extensie). +- `DEFINE CLASS __NumeClasaExemplu__ AS custom` -> numele si clasa-parinte reala dorita. Poti + schimba si baseclass-ul (`AS custom` -> ex. `AS _frmbase OF "_frm_base.vcx"` pentru un formular) + si adauga `ADD OBJECT` / metode dupa nevoie — respecta constrangerile de mai jos. +- `Name = "__NumeClasaExemplu__"` (in blocul `*`) -> **acelasi** nume ca la + `DEFINE CLASS`, cele doua trebuie sa coincida. + +Spre deosebire de fluxul folosit la crearea lui `Clase\ofacturare_util.vcx` +(`docs\nota_s8b_vcx_util.md`), care a luat antetul dintr-o conversie reala ca sa nu-l inventeze, +**antetul acestui sablon e deja verificat printr-un test text->binar->text complet** (mai jos) — +nu mai trebuie reconfirmat printr-o conversie separata inainte de folosire, doar `SourceFile` +trebuie sa corespunda numelui ales la pasul 2. + +### 4. Write-back + +``` +powershell -ExecutionPolicy Bypass -File D:\ROA\UTIL\foxbin2prg\txt2vcx.ps1 ` + -TextFile 'D:\ROA\ROAFACTURARE\Clase\.vc2' ` + -ProjectRoot D:\ROA\ROAFACTURARE -CacheRoot D:\ROA\ROAFACTURARE +``` + +Fara `-Force`, fara `-NoVerify` (fidelity check-ul trebuie sa treaca real). Adauga +`-AllowComun` **doar** daca `.vcx` ajunge sub `COMUN\`. + +### 5. Dovada de sincronizare — pe continut, nu pe mtime + +`txt2vcx.ps1` rescrie mtime-ul textului la refresh de cache, deci mtime-ul egal nu dovedeste +nimic. Reconverteste binarul proaspat scris intr-un cache **izolat** (diferit de `ProjectRoot`, +altfel `vcx2txt.ps1` isi poate sterge singur binarul sursa — vezi capcana documentata in +`docs\nota_s8b_vcx_util.md`, sectiunea "Conversie text") si compara pe octeti: + +``` +powershell -ExecutionPolicy Bypass -File D:\ROA\UTIL\foxbin2prg\vcx2txt.ps1 ` + -Source 'D:\ROA\ROAFACTURARE\Clase\.vcx' ` + -ProjectRoot D:\ROA\ROAFACTURARE -CacheRoot '' + +diff Clase/.vc2 /Clase/.vc2 +``` + +Zero diferente = dovada reala. Orice diferenta inseamna ca binarul din proiect nu reflecta +textul din arbore. + +### 6. Verificari de integritate + +``` +grep -c 'ADD OBJECT' Clase/.vc2 +grep -c 'OBJECTDATA:' Clase/.vc2 +``` + +Trebuie sa fie **egale**. Pentru CRLF si diacritice, vezi `COMUN\docs\flux-editare-vfp-text.md` +si memoria de proiect despre editarea `.vc2` in cp1250 (`Edit`/`sed -i` corup octetii non-ASCII — +scrie orice modificare text cu un script Python pe disc, `latin-1`/`cp1250` passthrough, CRLF +explicit, niciodata `sed -i` sau `python -c`). + +### 7. `.pjx` ramane manual + +`.pjx` nu e editabil din text (cf. `CLAUDE.md` proiect). `Clase\.vcx` trebuie adaugat +manual in `roafacturare.pjx` (Project Manager > Classes > Add) — dar **doar** cand ai nevoie de +Build/Rebuild pentru EXE. Nu e necesar pentru o proba cu `vfp9.exe` + `DO roafacturare.prg`, unde +`SET CLASSLIB` (pasul 8) rezolva clasa prin `SET PATH`. + +### 8. Inregistrare in `roafacturare.prg` + +Biblioteca trebuie inregistrata ca sa fie vazuta de aplicatie. Exemplu deja existent in fisier, +langa care se adauga o linie similara pentru biblioteca noua (`Programe\roafacturare.prg:137-139`): + +``` +SET CLASSLIB TO ocriterii.vcx ADDITIVE +SET CLASSLIB TO ofacturare_util.vcx ADDITIVE +Set Classlib To ctl32_statusbar.vcx Additive +``` + +`Clase\` e deja pe `SET PATH` (`Programe\roafacturare.prg:89`), deci fara cale completa. Aceasta +editare **nu e facuta automat de acest sablon** — se face separat, cu aprobare, cand biblioteca +noua chiar trebuie incarcata de aplicatie. + +## Constrangeri FoxBin2Prg de retinut (au picat fidelity check-ul in trecut) + +- `ADD OBJECT` la nivelul unei clase sunt ordonate **ASCII/colationat case-insensitive** pe + `ObjPath` (ex. `Cw10` intre `Cw1` si `Cw2`), inclusiv obiectele de top-nivel. +- Proprietatile in interiorul unui bloc `ADD OBJECT` / `*` sunt in **ordine + alfabetica**. +- **Ultima proprietate dintr-un bloc nu are `, ;`** la final (vezi ultima linie din blocul + `ADD OBJECT` din sablon — n-are virgula). +- Numarul de `ADD OBJECT` == numarul de `*< OBJECTDATA:` (0 = 0 in sablonul de baza). +- Liniile goale din corpul unui `PROCEDURE`/inainte de un `PROCEDURE` la nivel de clasa se + indenteaza cu un TAB; liniile goale **dintre blocuri `ADD OBJECT` consecutive** raman + neindentate (verificat direct pe text regenerat, `docs\nota_s8b_vcx_util.md` punctul 3). +- Encoding **cp1250** pentru diacritice, CRLF (`\r\n`) pe fiecare linie, inclusiv ultima + (`ENDDEFINE\r\n`, fara linie goala dupa la finalul fisierului). + +## Ce s-a probat si cum + +Test complet text -> binar -> text, **izolat in scratchpad**, fara sa atinga arborele +`D:\ROA\ROAFACTURARE`: copiat un binar-purtator existent intr-un proiect-fals in scratchpad, +aplicat sablonul (cu `SourceFile`/nume de clasa concrete), rulat `txt2vcx.ps1` cu +`-ProjectRoot`/`-CacheRoot` catre acel proiect-fals — **fidelity check trecut din prima +incercare** (spre deosebire de `frm_cursuri_valutare`, care a avut nevoie de 3 corectii). Apoi +reconvertit binarul proaspat cu `vcx2txt.ps1` intr-un cache izolat separat si comparat cu +`diff` — **zero diferente**. Detalii complete si comenzile exacte: `docs\nota_s8b_sablon_vc2.md`. + +**Neprobat**: adaugarea in `.pjx` si deschiderea reala in designer/rulare (pasii 7-8 raman +descrisi, nu executati — n-ar fi trebuit sa atinga arborele real fara aprobare). diff --git a/docs/sabloane/sablon_vcx_gol.vc2 b/docs/sabloane/sablon_vcx_gol.vc2 new file mode 100644 index 0000000..83b81c0 --- /dev/null +++ b/docs/sabloane/sablon_vcx_gol.vc2 @@ -0,0 +1,14 @@ +*-------------------------------------------------------------------------------------------------------------------------------------------------------- +* (EN) AUTOGENERATED - ATTENTION!! - NOT INTENDED FOR EXECUTION!! USE ONLY FOR MERGING CHANGES AND STORING WITH SCM TOOLS!! +*-------------------------------------------------------------------------------------------------------------------------------------------------------- +*< FOXBIN2PRG: Version="1.21" SourceFile="__NUME_BIBLIOTECA__.vcx" CPID="1252" /> (Solo para binarios VFP 9 / Only for VFP 9 binaries) +* +* +DEFINE CLASS __NumeClasaExemplu__ AS custom + *< CLASSDATA: Baseclass="custom" Timestamp="" Scale="Pixels" Uniqueid="" /> + + * + Name = "__NumeClasaExemplu__" + * + +ENDDEFINE diff --git a/versiune_db.txt b/versiune_db.txt index 36bff2c..4bc7c05 100644 --- a/versiune_db.txt +++ b/versiune_db.txt @@ -1 +1 @@ -2026_08_09_02 \ No newline at end of file +2026_09_09_07 \ No newline at end of file