Folderul docs\ era pana acum in afara oricarui control de versiuni - nici git, nici SVN - desi contine planurile pe puncte, proiectarile si rapoartele de cercetare pe care se sprijina modificarile din cod. O stergere acolo era definitiva. Fisierele intermediare (handoff-uri intre sesiuni, diff-uri deja aplicate) au fost sterse inainte, nu versionate: ce era durabil in ele a intrat in antetele fisierelor de test la care se refereau. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SN8snvkk94KuhWwoXUUey3
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 mostenestefrm_articol_factura(vfp_symbols -Class:frm_articol_gest_factura -> frm_articol_factura -> _frmbase -> _form) si al careiInit(:3986-3989) cheamaDoDefault(tnCantitate,tlAscunde)— deci ruleaza acelasiInitde 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.