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
44 KiB
Proiectare S4/S4b — pagina de articole factura in frm_modific2024
Cercetare + proiectare pentru plan_06_editare_factura.md, story S4 (pagina noua de articole) si
S4b (helpere/totaluri/verificari). NU e implementare — propunere supusa aprobarii lui Marius.
Stare de plecare (verificata, nu presupusa): S1-S3 sunt deja implementate si testate
(docs\progres.md, sectiunea "#6, runda 1"). Actiunea frm_facturi.do_editare_factura
(COMUN\clase\ofacturare_comun.vc2:3727-3869) si afisjurcom.do_modifica
(COMUN\clase\comun.vc2:2222-2563) sunt cele doua puncte de intrare reale, ambele deschid deja
frm_modific2024 pe cursoarele tact/trul/trul_obinv. Tot ce descrie documentul de fata se
adauga peste acest cod existent, fara sa-l modifice decat unde e explicit spus (D, E).
Deciziile lui Marius pe intrebarile din F (08.08.2026) — au prioritate fata de recomandarile din text
- F.1 — respinsa recomandarea "strict facturi". Pagina se aplica pe orice rand din
VANZARI, deci si pe avize. Tot ce spune A.3 despre restrangerea la lista deTIPde facturi se reciteste in cheia asta. - F.2 — premisa din C.1 e gresita. Contul nu e
4111peste tot: avizele folosesc418. In plus, nota contine randuri de discount si poate contine note adaugate manual de utilizator. Deci filtrul fixSCD='4111'nu e o regula, ci o potrivire pe un singur document. Regula corecta se stabileste indocs\cercetare\rec_suma_act.md(cercetare in curs), si C.1 se rescrie dupa ea. Pana atunci, nu implementa indicatorul pe formula din C.1. - F.3 — raspuns de la Marius: in
RULpot fi si linii cu diferente de pret, cand pretul de vanzare din factura difera de cel din stoc — doar pentru marfa tinuta la pret de vanzare. Deci suma bruta dinRULnu e comparabila prin constructie cu totalul documentului (pecod=1140888: 10 randuriRULpentru 4 linii, 4476.28 fata de 1924.59). O bara de totaluri care le compara direct ar semnala "desincronizat" permanent pe orice document cu marfa la pret de vanzare. Subsetul comparabil se stabileste indocs\cercetare\rec_suma_act.md. - F.4 — varianta A (bara de totaluri sub grid, permanent vizibila).
- F.5 — alegere globala a directiei de sincronizare, pe document, nu per linie.
- Separat, din decizia 18: liniile din seturi se trateaza ca orice alta linie.
- Transfer si custodie (23, 25, 30, 41, 27, 42, 47): pagina apare, dar fara bara de
totaluri — nu bara goala cu mesaj, ci fara ea. Pe aceste tipuri nu exista suma comparabila
(transferurile merg pe cont de stoc, custodia nu genereaza randuri
ACTper articol). - Tipul 51 (ROAACNPRO) foloseste
4111— deci divergenta gasita in cercetare are alta cauza decat contul; prima suspiciune e filtrarea pecodfaraan+luna. - Comparatia stricta e imposibila prin constructie:
ACTnu marcheaza originea randului, deci un rand adaugat manual nu se distinge de unul generat. Bara de totaluri arata cifrele si diferenta, fara verdict automat de eroare. - Garda pe
id_setse scoate de tot (decizia 24) — premisa ei a picat. - Documente mixte (decizia 25): suma
RULse corecteaza cu valoarea liniilor nestocate dinVANZARI_DETALII, marcata in bara ca ajustata. RULare formula comparabila (nu mai e doar informativ):SUM(CANT*PRETVTVA) + SUM(CANTE*PRETVTVA WHERE ID_TIP_RULAJ <> 3). C.1 se rescrie dupadocs\cercetare\rec_suma_act.md, care are si propunerile concrete de corectie la final.
Ipoteza lui Marius de verificat, cu efect asupra codului deja scris: id_set + 5 la discount ar fi
un marcaj tranzitoriu consumat inainte de ACT, nu o valoare persistata — caz in care garda pe
id_set din do_editare_factura (runda 3) e construita pe o premisa falsa.
A. Ce se poate face concret pe frm_modific2024
A.1 Structura curenta a pgfArticole si a celor doua pagini de rulaje
Clasa frm_modific2024 incepe la COMUN\clase\omodificari.vc2:6375. Pageframe-ul:
- Nu are
PageCountpropriu setat inADD OBJECT 'pgfArticole'...(:8627-8641) — mostenestePageCount = 2din clasa de baza_pageframe(COMUN\clase\_baza.vc2:496). Cele doua pagini sunt configurate doar prin proprietatilePAGE1.Caption/ForeColor/NamesiPAGE2.Caption/ForeColor/Namein acelasi blocADD OBJECT. PAGE1contine_grdfooter1(:8644-8655, footer cu sume pe coloane,csumcolumns=...) sigrdRulaje(:8657-...,ColumnCount=63,RecordSource="trul",ReadOnly=.F.).PAGE2are aceeasi structura petrul_obinv/grdRulajeObinv(confirmat prin indexul de metode:pgfArticole.PAGE2.grdRulajeObinv.*,omodificari.vc2:15159-15424), nu am recitit bloculADD OBJECTcomplet (nu era necesar — tiparul e identic cu PAGE1, doar alt cursor).Init(:13551-13660): primesteLparameters tnIdSet, tlNotaNoua, toBackupXML, toSet, tlVizualizare; seteazaThis.nid_set,lEditare/lVizualizare/lModificare/lVerificare. Zona:13610-13643ascundebut_nou1/but_sterge1si facegrid1readonly pe seturi speciale (id_set 25000-25099 sau o lista fixa) — logica veche, fara legatura cu facturile de vanzare (acele id_set-uri sunt pentru note "cu scadere", nu pentru pagina noua).Activate(:12238-12243): la prima activare cheamaresize_grid1().resize_grid1()(:13680-13688) siafiseaza_rulaje()(:12245-12281) folosesc dejathis.pgfArticole.Visibleca switch — dar e un toggle manual, comandat de utilizator (butonulbut_afiseaza_rulaje, colapseaza/expandeaza TOT pageframe-ul ca sa faca loc pentrugrid1), nu o decizie "documentul e de tip X". Nu e mecanismul de folosit pentru afisarea conditionata a PAGE3.Show(:13719-13775) e unde se leaga efectiv footerele de sume la grid-uri:this.pgfArticole.page1._grdfooter1.attachtogrid(...)+.calcTotal(), identic pentru PAGE2 (:13748-13752) si unde se ascunde automat tot pageframe-ul cand nu exista deloc rulaje (:13764-13770,If Reccount('trul')=0 And Reccount('trul_obinv')=0 Then This.afiseaza_rulaje()).
A.2 Mecanica adaugarii PAGE3
PageCount nu e o proprietate care se citeste o singura data — poate fi modificata la runtime, si
exact acest tipar exista deja in codebase, la un alt pageframe: comun.vc2:10521-10531
(frm_...Init) face THISFORM.pgcod.PageCount = n+1 + .Pages(n+1).Caption = 'Gestiuni' cand
conditia e adevarata, altfel PageCount = n (pagina ramane ascunsa, pentru ca nu exista in
colectia activa de pagini). Acesta e mecanismul recomandat pentru PAGE3, nu .Visible pe
pagina (VFP pageframe Page are Visible, dar codebase-ul nu-l foloseste nicaieri pentru
show/hide conditionat — a fost cautat explicit, 0 rezultate Page.*Visible in COMUN\clase).
Propunere concreta:
- In definitia clasei,
ADD OBJECT 'pgfArticole'capataPageCount = 3explicit (nu mai ramane pe default-ul 2) +PAGE3.Caption = "Articole factura",PAGE3.Name = "PAGE3", plus grid-ul noupgfArticole.PAGE3.grdArticoleFactura(dupa modelulgrdRulaje) si un_grdfooterpropriu (pgfArticole.PAGE3._grdfooter1), la fel ca PAGE1/PAGE2. - La runtime, in
Show(langa blocul:13764-13770, dupa acelasi principiu): daca documentul curent nu e o factura de vanzare (vezi A.3),This.pgfArticole.PageCount = 2— PAGE3 dispare din colectia activa de pagini, comportament identic cu azi pentru orice alt tip de nota. Cand este factura,PageCountramane3(valoarea din clasa) si se leaga footerul nou:this.pgfArticole.page3._grdfooter1.attachtogrid(...)+.calcTotal(), exact ca la PAGE1/PAGE2. - Consecinta directa: pentru orice document care nu e factura (99.9% din utilizarile din
ROAGEST/ROACONT),
PageCountscade inapoi la 2 la fiecare deschidere — clasa arata byte-cu-byte ca azi, nicio schimbare vizuala sau de comportament. Asta e conditia ceruta explicit in plan (S4: "pagina se afiseaza doar cand documentul curent are rand in vanzari... altfel formularul arata exact ca azi") si raspunde direct la riscul cel mai mare listat in plan.
A.3 Cum decide formularul ca documentul curent are randuri in VANZARI
Comparatia celor trei variante, asa cum a cerut misiunea. Concluzia (varianta 3) nu se schimba
fata de versiunea anterioara a acestei sectiuni — se schimba doar filtrul aplicat dupa ea, cf.
[DECIS — decizia 19 din progres.md] mai jos:
- Test pe
id_set— respins. Intervalele facturilor si ale avizelor dinCOMUN\docs\tipuri_documente_facturare.mdse suprapun aproape complet (facturi: 25000-25009, 25042-25048, 25051, 50100 + variantele "cu scadere" +10; avize: 25020-25029, 25040-25041, 25046 + variantele +10 — ambele in acelasi interval brut 25000-25099). Un test de formaBetween(tnIdSet,25000,25099)(deja folosit inInit,:13610-13612, dar pentru alt scop) ar prinde si avizele, nu doar facturile. Documentul insusi semnaleaza o coliziune nerezolvata pe25051(punctul 3 din capcanele acelui fisier) — nu e o baza solida pentru o decizie care controleaza afisarea/ascunderea unei pagini cu bani. - Flag pasat de apelant — respins ca mecanism principal.
afisjurcom.do_modifica(ROACONT/ROAGEST, registrul jurnal) e cod generic pentru orice tip de nota; azi nu stie si nu are motiv sa stie ca documentul curent are randuri inVANZARI(asta a fost motivul pentru care garda eFactura si cea de referinte au trebuit extrase inCOMUN\programe\la S1, nu lasate infrm_facturi). A cere unui al doilea apelant sa afle si sa transmita acest flag ar duplica exact interogarea pe care oricum trebuie sa o facem undeva, cu riscul ca un al treilea apelant viitor sa uite s-o transmita corect. SELECTinvanzaridupacod— recomandat.tact(cursorul pe care se leaga deja formularul, incarcat deIncarcaCursoareModificareNota,COMUN\programe\ofacturare_editare.prg:27-140) contine coloanacoddinvact_tot(filtrulWHERE ... cod = tnCodde la:53confirma coloana).tact.codse poate citi oricare ar fi workarea curenta (referinta calificata pe alias), deci nu conteaza care apelant a deschis formularul — informatia necesara e deja in cursorul pe care oricum formularul il primeste prin contract. Un singurSELECT tip, id_vanzare FROM vanzari WHERE cod = <tact.cod>(rulat o singura data, la deschidere) da simultan raspunsul la "are rand invanzari?" si, daca da,tip-ul exact — necesar in Runda 4 pentru tratamentul special al transferului/custodiei (decizia 22, mai jos).
Recomandare: detectia se face in interiorul frm_modific2024 (nu in apelanti), intr-o
metoda noua apelata din Init sau din Show (inaintea blocului de la A.2), care citeste
tact.cod, interogheaza vanzari o singura data, si populeaza trei proprietati noi pe
formular: This.lAreArticoleVanzari, This.nIdVanzare si This.nTipVanzare (tip-ul documentului
din vanzari). Asta inseamna ca PAGE3 apare automat din ambele puncte de intrare fara nicio
modificare in do_editare_factura sau afisjurcom.do_modifica — exact arhitectura ceruta de
plan ("extinderea clasei comune, nu cod apelant nou").
Numele lAreArticoleVanzari (nu lEsteFacturaVanzare, cum se numea in versiunea anterioara a
acestei sectiuni) e ales deliberat: sub decizia 19 de mai jos flagul devine adevarat si pe avize,
transferuri si custodie, nu doar pe facturi — numele vechi ar fi mintit. nTipVanzare se retine
separat, tot din acelasi SELECT, pentru ca Runda 4 (decizia 22: transfer/custodie afiseaza
pagina, dar fara bara de totaluri) are nevoie sa stie tipul exact, nu doar "are/nu are randuri".
[DECIS — decizia 19 din progres.md] Filtrul de mai sus nu se restrange la lista de TIP
de factura — pagina apare pe orice rand din VANZARI, deci si pe avize, transfer intre
subunitati, transfer pe lucrare si custodie. Varianta "strict facturi" (recomandarea acestei
sectiuni intr-o versiune anterioara, cand decizia inca nu fusese luata) a fost respinsa explicit
de Marius. Consecinte pentru restul lucrarii:
- Garda eFactura/S1 ramane specifica facturilor si nu se extinde — pe avize/transfer/custodie pur si simplu nu se aplica azi, pentru ca acele tipuri de document nu trec pe acolo.
- Scrierea (E, S5) si bara de totaluri (C.1, C.3) raman gatate separat, dupa
nTipVanzare: transfer/custodie (decizia 22) primesc pagina, dar fara bara. frm_modific2024deschis dinafisjurcom.do_modifica(registrul jurnal ROACONT/ROAGEST) capata aceeasi pagina pe orice document cu rand inVANZARI, nu doar pe facturi — de retinut la testarea riscului D, care azi verifica doar cazul "document care nu e deloc inVANZARI".
A.4 Lantul inainte_de_do_termin — unde intra validarile noi
omodificari.vc2:13357-13549. Ce face azi, pe scurt:
:13360-13367reseteaza filtrul petact, completeazaid_setgol petact/trul/trul_obinv.:13369-13386verificare generica de completare cont/analitic/partener (verificare_note_contabile('tact',...),oOperatii_comune), sarita pentru seturile speciale (id_set99998/90024).:13389-13402(doargnAn >= 2013): avertisment pe combinatia de conturi4426-4428/4428-4427fara sa fi folosit optiunea dedicata, apoiThis.VerificaAvertizareExigibilizareTVA()(:13909-14083, verificare separata, neatinsa de aceasta propunere).RETURN m.llRet— daca oricare pas a esuat, formularul nu inchide (butonul Termina ramane blocat pana la corectare).
Punctul de agatare pentru validarile noi ale lui S4b: chiar inainte de RETURN m.llRet
(:13404), un bloc nou gatat de This.lAreArticoleVanzari (A.3) — de exemplu, garda "nu lasa
utilizatorul sa iasa cu buton=1 daca a marcat sincronizarea ca necesara dar n-a confirmat-o
explicit" (detaliu in C). Nu inlocuieste nimic din ce exista azi — se adauga dupa validarile
generice, cu acelasi tipar (If m.llRet Then ... Endif).
B. Cursorul de articole
B.1 Sursa si momentul incarcarii
Confirmat in docs\cercetare\rec_cale_vanzari_detalii.md: scrierea la editare merge direct in
VANZARI_DETALII (fara VANZARI_DETALII_TEMP, care e GTT populata doar la emitere). Simetric,
citirea pentru formular trebuie sa vina direct din VANZARI_DETALII, nu din vreo tabela temp.
Propunere: o functie noua in COMUN\programe\ofacturare_editare.prg (alaturi de
IncarcaCursoareModificareNota, acelasi stil de cod — goExecutor.oExecute, gestiune de eroare
simetrica), de exemplu:
FUNCTION IncarcaArticoleFactura
LPARAMETERS tnIdVanzare
* incarca header-ul (1 rand, cursor tvanz) si liniile active (cursor tvd) pentru factura data
* tvd/tvanz raman deschise READWRITE - apelantul (frm_modific2024) le foloseste si le inchide
ENDFUNC
Apelata din interiorul frm_modific2024 (Init/Show, dupa ce A.3 a stabilit
This.lAreArticoleVanzari = .T. si This.nIdVanzare), nu din apelanti — acelasi motiv ca la A.3:
zero cod nou in do_editare_factura/afisjurcom.do_modifica pentru partea de citire.
tvanz (1 rand, header): id_vanzare, cod, discount (singurul camp editabil din antet, decizia
17 din progres.md) + campurile needitabile utile ca referinta vizuala (total_fara_tva, total_tva, total_cu_tva — valorile vechi, denormalizate, afisate readonly langa totalurile
live din S4b, nu suprascrise decat de S5 la salvare).
tvd (liniile), coloane din VANZARI_DETALII (lista completa in
rec_cale_vanzari_detalii.md, sectiunea 2.2) — subsetul relevant editarii:
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.
Filtrul de incarcare: WHERE id_vanzare = :tnIdVanzare AND sters = 0 (liniile deja sterse nu se
mai arata — simetric cu tact, care si el filtreaza sters=0 la incarcare).
B.2 Marcaje de stare, fara concept nou fata de restul clasei
Clasa foloseste deja doua idiomuri simple pentru starea liniilor din tact, ambele reutilizabile
ca atare pentru tvd, fara sa inventam un al treilea:
- Sters = flag pe randul existent, nu stergere fizica din cursor — exact cum
tact/trulreprezinta dejaSTERSca o coloana obisnuita (rescrisa la scriere, nuDELETEd din cursor).tvd.STERS(coloana deja prezenta inVANZARI_DETALII) se flipuieste in cursor la actiunea "sterge linie"; la scriere (D), un rand cuSTERS=1care aveaSTERS=0la incarcare devine unUPDATE ... SET STERS=1. - Adaugat =
id_vanzare_det = 0— acelasi sentinel folosit deja indo_adauga(omodificari.vc2:12685,loadd.id_act = 0pentru randurile noi dintact, inainte deAppend Blank/Gather). La scriere, orice rand dintvdcuid_vanzare_det = 0e unINSERT(PK-ul real vine automat dinSEQ_VANZARI_DETALII, confirmat inrec_cale_vanzari_detalii.mdsectiunea 1.3/3.2 — nu trebuie generat in VFP). - Modificat — singurul marcaj cu adevarat nou necesar, pentru ca "a fost atins" nu se poate
deduce din
id_vanzare_det/STERS. Propunere: o coloana logica_modificat(prefix_, convenabil pentru un camp de lucru care nu exista in tabela reala — verifica totusi ca VFP nu interpreteaza gresit numele; alternativlModificat), setata.T.din handler-eleValid/InteractiveChangeale coloanelor editabile din grid — acelasi tipar folosit deja de clasa petrul(pgfArticole.PAGE1.grdRulaje.cCant.Text1.Valid,omodificari.vc2:14906-14911, si restul handler-elorValid/When/InteractiveChangedin acelasi grid). La scriere, un rand cuid_vanzare_det > 0si_modificat = .T.e unUPDATE; fara flag, randul nu se atinge (evitaUPDATE-uri inutile pe linii doar rasfoite).
Acest model evita complet o alternativa mai grea (snapshot + diff intre cursorul original si cel
curent) care ar fi introdus un concept nou, fara sa aduca vreun beneficiu fata de flag-urile deja
folosite in clasa pentru tact.
C. Helperele si verificarile
C.1 Ce inseamna "suma comparabila" — regula pe tip de document, verificata pe date reale
Corectie fata de versiunea anterioara a acestei sectiuni (premisa ei — filtru fix SCD='4111' — a
fost respinsa explicit de Marius, F.2 mai jos). Cercetarea completa, cu toate interogarile pe cele
trei scheme si sursele exacte pe cod, e in docs\cercetare\rec_suma_act.md; ce urmeaza e concluzia
ei. Doua completari ulterioare, verificate separat pe MARIUSM_AUTO (08.08.2026), sunt in
docs\cercetare\rec_cele_41_facturi.md: derivarea an/luna si cauza reala a divergentei pe
cod=1138989. Nu se reimplementeaza nicio formula fiscala in VFP — recomandarea ramane cea de
dinainte: "suma din VANZARI_DETALII" se ia direct din calculeaza_total_fara_tva_fact/
calculeaza_total_tva_fact (Oracle, aceleasi functii care vor rula la salvarea din S5), niciodata
recalculata in VFP.
Nu exista un filtru fix de cont pentru "suma din ACT". Contul de debit al liniei depinde de
tipul documentului (PACK_FACTURARE.pck, contabilizeaza_articol decide contul la
:7390-7415):
Grup de TIP |
Cont debit (linie) | Comparabil cu TOTAL_CU_TVA? |
|---|---|---|
| Facturi (1,2,3,5,7,8,9,10,43,44,45,46,48,49,51,52 fara rata) | 4111 (empiric stabil pe 3 scheme) |
DA |
| Factura din aviz (tip 4) | 4111, discount direct pe el cu semn negativ |
DA |
Vanzare pe rate/contract (2,6,52 cu id_rata<>0) |
din NOTE_CONTABILE, legat de CONTRACTE |
DA |
| Avize catre clienti debitori (28, 29) | 461 (hardcodat) |
DA |
| Restul avizelor (21,22,24,26) | 418 (hardcodat) |
DA |
| Transfer intre subunitati (23,25,30,41) | cont de STOC, nu de client | NU |
| Transfer pe lucrare (27) | cont de STOC | NU |
| Custodie (42,47) | — (descarca_gestiune nu scrie rand ACT per articol) |
NU |
| ROAACNPRO (51) | 4111, confirmat stabil de Marius |
cont OK, dar comparatie nesigura — vezi mai jos |
Filtrul pe ACT cere cod + an + luna, niciodata cod singur. Dovada: cod=1140632 are
un rand de achizitie straina (OCR furnizor) in an=2026,luna=1 si nota de vanzare reala in
an=2026,luna=2, cu acelasi cod reutilizat intre module. IncarcaCursoareModificareNota( tnCod, tnAn, tnLuna, ...) (COMUN\programe\ofacturare_editare.prg:27-140) filtreaza deja corect —
orice interogare noua din S4b trebuie sa foloseasca acelasi tipar de filtru complet, nu doar cod.
an/luna nu se deriva din VANZARI.DATA_ACT — trebuie luate din contextul notei deja
incarcate, niciodata recalculate din antet. Pe 703 documente VANZARI (MARIUSM_AUTO,
docs\cercetare\rec_cele_41_facturi.md): 542 au nota in luna din DATA_ACT, 78 (11%) au nota
intr-o alta luna, 83 n-au deloc randuri ACT pe cod. Exemplu: cod=1138989 are
VANZARI.DATA_ACT = 01-JAN-19, dar cele 12 randuri ACT sunt in an=2019, luna=3
(dataact=31-MAR-19) — un filtru cod + an(data_act) + luna(data_act) ar intoarce zero randuri.
Pe datele de test cazurile sunt concentrate pe tip=51 (ROAACNPRO, DATA_ACT sablon 01-JAN-19),
deci nu e dovedit tipar general de productie — dar consecinta de implementare e reala: an/luna
se iau din cursorul tact/actactan deja incarcat de IncarcaCursoareModificareNota (apelata cu
an/luna explicite), niciodata recalculate din VANZARI.DATA_ACT. Pentru cele 78 de documente cu
luna divergenta, do_editare_factura raspunde azi "Nu exista nota contabila pentru aceasta
factura" si refuza editarea — comportament sigur, dar de consemnat ca limitare cunoscuta.
Discountul de document intra NET (debit minus credit), nu ca SUM(SCD=cont) simplu.
scrie_discount (PACK_FACTURARE.pck:12859-13057) scrie discountul pe sensul OPUS liniei de
vanzare: pe facturi normale (tip<=20 sau in (44,45,46,43,48,49,51,52)) discountul e
SCD='667'/SCC='4111', adica pe credit fata de contul de client — un SUM(SUMA) WHERE SCD='4111' simplu il ignora complet. Suma corecta e soldul net, aceeasi formula pe care
aplicatia insasi o foloseste la auto-verificarea de la emitere (verifica_total_document,
PACK_FACTURARE.pck:16073-16145):
SUM(CASE WHEN SCD = :cont THEN SUMA
WHEN SCC = :cont AND SCD NOT IN ('5311','5314','5121','5125','5126') THEN -SUMA
ELSE 0 END)
(pe factura din aviz, tip=4, discountul e deja direct pe 4111 cu semn negativ, deci formula neta
da acelasi rezultat ca un SUM simplu pe acel tip — nu strica nimic sa se aplice uniform pe toate
tipurile comparabile din tabel.)
RUL are acum o formula comparabila (nu mai ramane doar informativ):
SUM(CANT*PRETVTVA) + SUM(CANTE*PRETVTVA WHERE ID_TIP_RULAJ <> 3)
Randurile "in plus" observate initial (10 randuri RUL pentru 4 linii, suma bruta 4476.28 in loc
de 1924.59 pe cod=1140888) vin din perechile de diferenta de pret, generate de
PACK_FACTURARE.descarca_gestiune pentru marfa/produse tinute la pret de vanzare
(V_CONT IN ('371','357') AND V_TIP_GESTIUNE=6, :9361-9540; produse/ambalaje similar,
:9641-9818) cand pretul de vanzare inregistrat in stoc difera de cel facturat efectiv
(V_PRETV_ORIG <> V_PRETV). Fiecare diferenta scrie o pereche marcata ID_TIP_RULAJ=3 (:7745):
un rand cu CANTE>0 la pretul VECHI din stoc (de exclus din suma) si unul cu CANT>0 la pretul
REAL facturat (de inclus). Formula de mai sus, aplicata pe cod=1140888, da exact 1924.59 =
TOTAL_CU_TVA.
Documente mixte (decizia 25): liniile nestocate nu au deloc rand RUL.
descarca_gestiune sare complet peste articolele cu NOM_ARTICOLE.IN_STOC=0 (:7783-7789) — nu
scrie nimic in RUL pentru ele (confirmat pe productie, cod=1397106: linia de "SERVICII
TRANSPORT" lipseste integral din RUL). Pe orice document cu linii stocate SI nestocate, suma
RUL de mai sus subestimeaza sistematic documentul cu exact valoarea liniilor nestocate.
Corectie: suma RUL se aduna cu suma liniilor IN_STOC=0 din VANZARI_DETALII, iar bara de
totaluri marcheaza explicit ca cifra e ajustata (nu doar RUL brut).
Tipuri fara suma comparabila — nu se afiseaza bara, nu se da verdict (decizia 22): transfer
intre subunitati (23, 25, 30, 41) si transfer pe lucrare (27) merg pe cont de stoc, nu de
client; custodia (42, 47) nu scrie niciun rand ACT per articol. Pe niciunul din aceste tipuri
nu exista ce compara — tratament explicit ("pagina apare, bara nu"), nu o eroare de raportat. Pe
tip=50 (marcat "in lucru" in pachet), acelasi tratament, prin analogie.
ROAACNPRO (tip 51): cauza divergentei pe cod=1138989 gasita — nota e DUBLATA, nu e cazul
deciziei 9. Cele 12 randuri ACT (toate in aceeasi an/luna, deci nu e capcana de filtrare de
mai sus; toate pe 4111, zero 411) sunt doua blocuri de cate 6, iar al doilea e exact 2x
primul, rand cu rand (docs\cercetare\rec_cele_41_facturi.md): blocul A insumeaza exact
TOTAL_CU_TVA = 13895.45 (0.01 lei rotunjire), blocul B e dublul lui, iar suma totala peste toate
cele 12 randuri e 41686.35 = 3x totalul — exact raportul semnalat initial. Regula pentru suma
din ACT se inchide si pe acest document: nu e o exceptie a regulii, e o nota postata de doua
ori — o anomalie reala de date pe care un indicator corect trebuie s-o semnaleze, nu s-o ascunda.
Nu e printre cele 41 de facturi de la decizia 9 (#8/S9) — acelea sunt toate STERS=1, iar
cod=1138989 are STERS=0 si o linie activa; presupunerea anterioara ca ar fi "aceeasi familie"
nu se sustine. Ramane deschis, separat: cele 12 randuri ACT explica antetul VANZARI, dar linia
unica din VANZARI_DETALII (2468.21) nu reconciliaza cu niciuna din cele doua cifre — asta chiar
ramane neexplicat, si e un argument in plus pentru caracterul informativ (nu verdict automat) al
comparatiei ACT vs VANZARI_DETALII de mai jos.
Comparatia ramane informativa, niciodata verdict automat de eroare. ACT nu are nicio
coloana care sa marcheze originea randului (omodificari.vc2:12653-12701, do_adauga) — un rand
adaugat manual de utilizator (posibil, decizia F.1: pagina apare pe orice document din VANZARI)
e indistinctibil, dupa salvare, de unul generat automat la emitere. Indicatorul din S4b ramane deci
informativ pe subset bine definit, niciodata sursa unica de adevar pentru un verdict de eroare.
Gradul de incredere: regula de mai sus (cont pe tip + filtru cod+an+luna + sold net) e
verificata pe 360 de documente, 12 tipuri de document, 3 scheme (MARIUSM_AUTO date de test,
ROMFAST@ROA_ROMFAST client real, VENDING productie) — ~97.5% potrivire exacta
(docs\cercetare\rec_suma_act.md, sectiunea "Concluzie C"). cod=1138989 (ROAACNPRO) nu mai e un
caz neexplicat — cauza e gasita (nota dublata, de mai sus) — dar un indicator automat tot ar
semnala divergenta pe el, corect, pentru ca e o anomalie reala de date. Restul cazurilor raman
deschise, nu ascunse: transfer/custodie (necomparabile prin design, de mai sus), 5 documente
ROMFAST fara nicio nota ACT scrisa, si un document VENDING in valuta (cod=1165566, tip=9)
cu o diferenta de 5454.12 lei neexplorata — niciunul din aceste cazuri nu infirma regula, dar
niciunul nu trebuie prezentat ca "inchis" fara nuanta de mai sus.
C.2 Ce inseamna "live" vs "la ultima salvare"
Cele trei surse nu pot fi comparate coerent "in timp real" cat timp utilizatorul tasteaza intr-o
celula din grid, pentru ca ACT/RUL/VANZARI_DETALII reala raman la valoarea de dinaintea
editarii curente pana la Salveaza. Propunere clara, ca sa nu induca fals sentiment de precizie:
- Subtotal pe linie, in grid (cantitate x pret, cu/fara TVA dupa flag) — calcul simplu, live,
in VFP, la fiecare
InteractiveChange/Valid(acelasi tipar cacalculeaza_valori_rul,omodificari.vc2:12519-12588, aplicat petrul). Nu implica formula fiscala complexa (fara discount de document, fara rotunjiri de agregare), deci riscul de divergenta e neglijabil si local, vizibil imediat de utilizator. - Cele trei totaluri de control (RUL / VANZARI_DETALII / ACT) raman "la ultima stare
persistata" — se recalculeaza (interogare Oracle) la deschiderea paginii si dupa fiecare
Salveazareusit, nu la fiecare tasta. Rolul lor real (asa cum reiese din problema descrisa de Marius) e sa arate ca un document editat anterior a ramas nesincronizat, nu sa dea un preview in timp real al editarii curente — editarea curenta oricum nu poate fi "corecta" pana nu trece prin acelasi calcul Oracle care va rula la salvare.
C.3 Indicatorul de stare a sincronizarii
Un label vizibil pe PAGE3 (langa footerul de totaluri), 3 stari: verde/OK (toate sumele
aplicabile coincid, in limita rotunjirii de 2 zecimale — pe tipurile fara suma comparabila,
transfer/custodie, bara nici nu apare, cf. C.1), galben/atentie (VANZARI_DETALII si ACT
coincid, dar cifra din RUL e ajustata pentru linii nestocate sau documentul e de un tip cu
comparatie nesigura, ex. ROAACNPRO — cf. C.1), rosu/desincronizat (VANZARI_DETALII si ACT
NU coincid — semnalul real ca ceva nu s-a propagat corect, singurul caz in care indicatorul trebuie
sa opreasca vizual atentia utilizatorului). Recalculat la deschidere si dupa fiecare salvare
reusita (C.2).
C.4 Actiunea explicita de sincronizare
Cerinta lui Marius: directia (rulaj->articol sau articol->rulaj) o alege utilizatorul, niciodata implicit. Propunere de flux (schita UI in F, intrebarea de UX):
- Buton "Verifica sincronizarea" (sau automat la deschidere, doar afisare) — ruleaza C.1/C.3,
populeaza un cursor de diferente
tvd_diff(id_articol, cantitate_rul, cantitate_vd, pret_rul, pret_vd, ...) doar pentru randurile unde difera. - Daca exista diferente, buton "Propune sincronizare" deschide un dialog/grid cu liniile afectate si valorile vechi/noi, pe ambele directii posibile (utilizatorul alege per-sesiune care parte e sursa — RUL sau VANZARI_DETALII —, nu per-linie individual, ca sa evite o combinatie inconsistenta).
- Confirmarea aplica modificarile doar in cursorul in memorie (
tvdsautrul, dupa directie) — nu scrie nimic in Oracle pana laTermina/Salveaza. Consistent cu principiul "nimic nu se aplica silentios si nimic nu se declanseaza automat lado_termin" din plan. - Refuzul propunerii nu modifica nimic — utilizatorul poate corecta manual, linie cu linie, in oricare din cele doua griduri.
C.5 Linii adaugate/sterse fara corespondent in RUL
O linie noua in tvd (id_vanzare_det=0) nu are, prin definitie, niciun rand RUL corespunzator —
nu exista "vechi" de comparat. Propunere: astfel de linii sunt automat excluse din comparatia
C.1/C.3 (nu pot fi "desincronizate", pentru ca n-au fost niciodata sincronizate) si marcate separat
in UI ("linie noua, fara rulaj — se creeaza la salvare" / "linie stearsa"). Simetric pentru liniile
sterse (STERS=1 in tvd): nu mai intra in suma "curenta" din C.2, dar raman vizibile (tacuate/
strikethrough) pana la salvare, ca utilizatorul sa vada ce a marcat pentru stergere inainte sa
confirme.
C.6 Ce NU se poate verifica automat
- Corelatia RUL <-> VANZARI_DETALII linie-cu-linie (nu agregat) — ramane nesigura: formula din
C.1 e verificata ca sumă pe tot documentul, nu mapeaza un rand
RULanume pe o linie anume dinVANZARI_DETALII. Suma agregata (C.1) are acum formula verificata; ce nu se poate face e legatura 1-la-1 intre randuri. - Corectitudinea contabila a notei dupa editare (echilibrul debit=credit, alegerea corecta a
conturilor) — ramane acoperita de validarile generice deja existente in
inainte_de_do_termin(A.4), care nu se ating. - Impactul asupra eFactura/SAFT dupa editare — in afara scopului #6 (garda S1 blocheaza deja editarea facturilor trimise in eFactura).
D. Riscul asupra registrului jurnal
omodificari.vc2 e in COMUN si serveste si afisjurcom/registrul jurnal ROACONT/ROAGEST. Ce
poate regresa, concret, si cum se limiteaza:
PageCountschimbat global pe clasa — daca noulPageCount=3din definitia clasei nu e readus la 2 la runtime pentru non-facturi (A.2), PAGE3 ar aparea (goala sau cu date gresite) pe orice nota din ROACONT/ROAGEST. Mitigare: testul din A.2 ruleaza necontional inShow, inaintea oricarei afisari, si defaultul din clasa e explicit "1 pas de siguranta" (daca testul crapa/nu ruleaza,PageCountramane 3 din clasa — deci testul TREBUIE sa aiba unCatch/elsecare forteazaPageCount=2, nu invers). De verificat explicit la implementare: comportamentul pe eroare al noii interogari Oracle (A.3) trebuie sa fie "ascunde PAGE3", nu "las-o vizibila".- Interogarea noua din A.3 (
SELECT ... FROM vanzari WHERE cod=...) ruleaza la FIECARE deschidere a formularului, inclusiv pentru note care n-au nicio legatura cu facturarea — cost suplimentar mic (un SELECT indexat pecod), dar trebuie sa fie garantat sa nu blocheze deschiderea pe eroare de retea/Oracle. Mitigare: acelasi tipar defensiv caIncarcaCursoareModificareNota(ofacturare_editare.prg:58-61) — pe eroare, se comporta ca "nu e factura" (ascunde PAGE3), nu propaga eroarea in sus si nu blocheaza formularul. - Cursoarele noi (
tvd/tvanz) nu trebuie sa interfereze cutact/trul/trul_obinv— nume de alias distincte, verificate ca nu exista deja in cod (tvd/tvanzcautate, 0 rezultate azi). Grid-ul nou trebuie sa aibaControlSourcecalificat complet pe fiecare coloana (COMUN\docs\capcana_grid_controlsource.md) — capcana confirmata activa exact in acest scenariu (3+ griduri pe acelasi formular:grid1/grdRulaje/grdRulajeObinv/noul grid). - Scrierea (nu doar afisarea): partea cea mai sensibila. Scrierea noua in
VANZARI_DETALII(E, S5) trebuie sa fie strict gatata deThis.lAreArticoleVanzari, apelata din apelanti (do_editare_factura/afisjurcom.do_modifica) DOAR dupa ce pasul existentfinalizeaza_modificare_notaa reusit deja — deci pe orice nota fara rand inVANZARI, pasul nou nu se executa niciodata (flag-ul e.F.), cod identic cu azi. gridextra1.setup()(:13729) — salveaza/restaureaza preferinte per-grid, cheieSYS(1272, grid). Grid-ul nou (pgfArticole.PAGE3.grdArticoleFactura) e complet nou, deci nu are preferinte salvate de niciun utilizator — capcana dincapcana_grid_preferinte_utilizator.md(coloana noua intr-un grid EXISTENT ajunge la coada) nu se aplica la infiintare, doar daca se adauga o coloana ulterior. Neverificat: dacagridextra1inregistreaza automat orice grid nou de pe formular sau necesita inregistrare explicita — de confirmat direct ingridextras.vc2la implementare, nu presupus aici.
Cum se testeaza D: un caz de test obligatoriu (deja in S8 din plan) e "document care nu e
factura, deschis din registrul jurnal — pagina de articole nu apare si comportamentul e identic cu
azi". Recomand completarea lui cu: (a) o rulare explicita cu Oracle temporar indisponibil pe
interogarea din A.3 (verifica gracious degradation, punctul 2 de mai sus), (b) o comparatie
byte-cu-byte a XML-ului salvat de salveazaxml (:13690-13717) inainte/dupa modificare, pe un
document non-factura, ca sa confirme ca noul cod n-a atins deloc acel drum.
E. Impartirea in runde de implementare
Ordonat pe risc, cu rezultat testabil la finalul fiecarei runde. S1-S3 (deja facute) raman runda 0.
Runda 1 — PAGE3 doar afisare, fara scriere (depinde doar de VFP + Oracle read-only, nu de S5)
- A.2 (PageCount/Caption/grid nou) + A.3 (detectie factura, in
frm_modific2024) + B (incarcaretvanz/tvd, read-only). - Grid needitabil (
ReadOnly=.T.pe toate coloanele), fara buton de salvare separat pe pagina. - Gata cand: PAGE3 apare pe orice document cu rand in
VANZARI(facturi, avize, transfer, custodie — decizia 19), din ambele puncte de intrare, arata liniile corecte; pe orice document fara rand inVANZARIdispare complet (testul de risc D). - Nu depinde de S5. Se poate livra si testa independent.
Runda 2 — editare in memorie, fara scriere in Oracle
- Grid editabil (B.2, marcaje
_modificat/sters/id_vanzare_det=0), dialogul per-linie (reutilizareafrm_articol_factura, vezi nota tehnica de mai jos), adaugare/stergere linie in cursor, discount de antet editabil intvanz. - Subtotalul live pe linie (C.2, calcul simplu VFP).
- Gata cand: utilizatorul poate adauga/sterge/modifica linii in grid, vede subtotaluri live;
Renuntalasa totul neschimbat;Terminanu scrie inca nimic inVANZARI_DETALII(doar nota contabila, ca azi). - Nu depinde de S5 — poate rula complet pe VFP, testabil headless fara risc de scriere Oracle.
Runda 3 — S5 (Oracle) + scrierea reala
- Procedura
recalculeaza_totaluri_vanzari(Oracle,rec_s5_oracle_vanzari.mdsectiunea B) + procedurile de UPDATE/INSERT/soft-DELETE peVANZARI_DETALII(rec_cale_vanzari_detalii.mdsectiunea 4, Varianta B). - Scrierea efectiva din VFP: apel nou, simetric in ambii apelanti, gatat de
Omodif.lAreArticoleVanzari, dupafinalizeaza_modificare_notareusit (D.4). - Gata cand: dupa
Termina,VANZARI_DETALII/VANZARIreflecta editarea; S7 (rotunjire la reeditare) verificat pe acest flux. - Depinde de Runda 2 (cursorul editat trebuie sa existe) si de S5/S6 din planul general.
Runda 4 — S4b complet (helpere/verificari)
- C.1-C.6: totalurile de control (interogare directa a functiilor Oracle existente, nu formula VFP), indicatorul de stare, actiunea explicita de sincronizare.
- Gata cand: criteriile din plan_06 S4b (indicator vizibil, propunere enumerata, refuzul nu modifica nimic).
- Depinde de Runda 3 — comparatia cu ACT/VANZARI_DETALII n-are sens pana nu exista scriere reala de comparat.
Nota tehnica pentru Runda 2: dialogul per-linie recomandat e reutilizarea
frm_articol_factura (COMUN\clase\ofacturare.vc2:2315-..., deschis azi din
frm_facturare_articole.do_modifica, ofacturare.vc2:13746-13844, model documentat si in
plan_06_editare_factura.md "Ce preda #7 catre S6"). Dialogul citeste PRIVATE poArticol (setat
de apelant inainte de Createobject) si Lparameters tnCantitate, tlAscunde — fara plafon
(decizia 15), tnCantitate se transmite cu o valoare santinela mare (ex. 999999999), nu se
recalculeaza din stoc. poArticol asteptat de dialog are un set bogat de proprietati calculate
(valftva, vval*, etc. — vezi lista completa la ofacturare.vc2:13835-13840), diferite de
coloanele brute din VANZARI_DETALII; adaptorul nou trebuie sa populeze campurile de baza
(pret_achizitie, cantitate, id_articol, cont, id_gestiune, proc_tvav, pretftva/pretctva dupa flag, discount_unitar, id_valuta, ...) din tvd, apoi sa cheme aceeasi functie VFP
calculeaza_totaluri(poArticol) (apelata deja la :13875 pentru randuri noi) ca sa deriveze restul
— nu se reinventeaza formula, se refoloseste exact ca la compunere. Ramura do_alege_stoc
(redeschiderea dialogului de gestiuni, folosita azi doar la compunere) nu se foloseste in #6 —
decizia "fara verificare de stoc" (15) inseamna ca toate liniile, gestionabile sau nu, trec prin
acelasi dialog simplu.
F. Intrebari pentru Marius
-
[DECIS — decizia 19 din progres.md, vezi A.3] Nu strict facturi: pagina apare pe orice rand din
VANZARI, deci si pe avize, transfer si custodie. Varianta "strict facturi" recomandata initial in A.3 a fost respinsa explicit de Marius. -
[REZOLVAT — vezi C.1] Contul nu e fix
4111: regula e pe tip de document (facturi4111, avize418, avize catre clienti debitori461), verificata pe 360 de documente / 12 tipuri / 3 scheme (docs\cercetare\rec_suma_act.md). Raman deschise, fara sa infirme regula: 5 documenteROMFASTfara nicio notaACTsi un documentVENDINGin valuta (cod=1165566) cu diferenta neexplorata. -
[REZOLVAT — vezi C.1] Regula gasita: perechile
cant/cantemarcateID_TIP_RULAJ=3sunt randuri de diferenta de pret (PACK_FACTURARE.descarca_gestiune), de exclus randul cu pretul vechi din stoc. FormulaSUM(CANT*PRETVTVA) + SUM(CANTE*PRETVTVA WHERE ID_TIP_RULAJ<>3)reproduce exact totalul pe documente cu toate liniile stocate; pe documente mixte se corecteaza cu liniile nestocate (C.1). -
UX-ul concret al zonei de totaluri si al indicatorului — trei variante, cu recomandare:
Varianta A (recomandata) — bara de totaluri sub grid-ul PAGE3, indicator ca punct colorat + text:
+-----------------------------------------------------------------------+ | [Articole factura] Discount document: [____] % | | +---------------------------------------------------------------+ | | | Articol | Cant | Pret | Cu TVA | ... | | | | ... | | | +---------------------------------------------------------------+ | | Total ACT (contabil): 1924.59 lei | | Total VANZARI_DETALII: 1924.59 lei | | Total RUL (formula C.1): 1924.59 lei [*] Sincronizat | | [Verifica sincronizare] [Propune] | +-----------------------------------------------------------------------+Exemplu
cod=1140888(C.1): dupa formula corecta, cele trei sume coincid — nu mai e un caz care sa ilustreze o divergenta. Pentru un document mixt (linii stocate + nestocate, decizia 25), randul RUL se marcheaza explicit ca ajustat, de exemplu:| Total RUL (ajustat, +linii nestocate): 1170.00 lei |Simplu, aliniat cu
_grdfooter1deja existent pe PAGE1/PAGE2 (acelasi loc, acelasi stil vizual).Varianta B — indicator langa caption-ul paginii (
PAGE3.Caption = "Articole factura ⚠"sau cu iconita), totalurile doar la cerere (buton "Arata totaluri de control" care deschide un dialog separat). Mai compact, dar ascunde informatia pana la un click — risc sa nu fie observat.Varianta C — culoare de fond pe intreaga pagina (rosu deschis) cand desincronizat, fara text explicit pana la deschiderea dialogului de sincronizare. Cel mai putin verbose, dar ambiguu (utilizatorul nu stie CE e desincronizat fara sa deschida dialogul).
Recomand A — respecta convenția UX (
conventie_ux_formulare.md: informatia de control trebuie vizibila, nu ascunsa dupa un click) si reutilizeaza tiparul deja vizual familiar din PAGE1/PAGE2 (footer de sume sub grid). -
Dialogul de propunere de sincronizare (C.4) — schita:
+---------------------------------------------------------+ | Propunere sincronizare (sursa: Articole factura -> Rulaj)| | +-------------------------------------------------------+| | | Articol | Rulaj (vechi) | Articol (nou) | || | | Piesa X | cant=2 pret=100 | cant=3 pret=110 | || | | Piesa Y | -- (fara rulaj) | cant=1 pret=50 | || | +-------------------------------------------------------+| | [Alege directia: Articol->Rulaj | Rulaj->Articol]| | [Aplica in memorie] [Renunta] | +---------------------------------------------------------+De confirmat daca alegerea directiei e un singur radio-button global (recomandat, C.4 punctul 2) sau daca Marius vrea control per-linie (mai flexibil, mult mai complex de implementat si de explicat utilizatorului — nerecomandat pentru complexitatea/beneficiul).
Ce nu am putut verifica
- Daca
gridextra1.setup()inregistreaza automat grid-uri noi de pe formular (D.5). - Comportamentul
_grdfooter/attachtogridpe un grid gol (0 randuri) la prima deschidere a PAGE3 — nu a fost testat, doar citit codul PAGE1/PAGE2 ca precedent.
(Cele trei puncte legate de formula ACT/RUL care erau listate aici — divergenta 903.53 vs
1924.59, regula perechilor cant/cante, stabilitatea contului 4111 — s-au rezolvat prin
cercetarea din docs\cercetare\rec_suma_act.md si sunt acum in C.1 / F.2 / F.3.)