Files
roafacturare/docs/cercetare/s4_puncte_deschise.md
2026-09-09 22:19:22 +03:00

15 KiB

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.