#13: formular de facturare unificat - etapa curenta

Squash al branch-ului de lucru plan13-s2.

Clase: ofacturare (nucleul formularului unificat), ofacturare_comun,
ferestre_cere_date, ocomenzi, caut_ora (lista de preturi in combo-urile de
cautare), omodificari.

Programe: ofacturare impartit - ofacturare_antet, ofacturare_rutare_scriere si
ogrid_latimi sunt fisiere noi; oproceduri_facturare primeste discountul pe linie
si cota standard de TVA cand articolul nu are cota pe politica.

Documentatie: capcana SQLExec no_data_found, conventia de encoding, depanarea
testelor VFP si regulile de lucru - conflictele cu modificarile venite din alte
proiecte sunt rezolvate pastrand ambele parti.

Loguri de rulare a testelor scoase din versionare; testele raman.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PkyjGyrV2S7932om4kfSiK
This commit is contained in:
2026-09-09 21:38:25 +03:00
parent d1d0d7c37f
commit 8e022d332e
259 changed files with 57305 additions and 1576 deletions

View File

@@ -1,8 +1,16 @@
*** ofacturare_editare
*!* 11.08.2026
*!* 06.09.2026
*!* marius.mutu
*!* helpere comune pentru editarea unei facturi emise: garda eFactura, cursoarele notei si ale rulajelor,
*!* helpere comune pentru editarea unei facturi emise: garda eFactura, garda pe urmasi in lantul
*!* aviz -> factura -> retur (VANZARI_CORESP), incarcarea antetului unui document existent (FACT_VFACTURI)
*!* intr-un oDateFactura nou (tnIdSet=0, lEditare=.T.), cu discountul de document ca suma si lista de
*!* avize sursa (VANZARI_CORESP, TIP=1), liniile unui document existent (VVANZARI_ARTICOLE) direct in
*!* crsfactura, cu gestionabil din nomenclatorul curent, contul liniei din document, pretul de
*!* vanzare al lotului reluat din rulajul original (RUL) numai pe gestiunile la pret de vanzare
*!* (nr_pag 6/7, ca in STOC) si legatura de rezervare (id_part_rez/id_lucrare_rez) tot din RUL,
*!* si zi_curs/dataact/datascad normalizate la Date (Ttod) din data_act/data_scad, care vin din
*!* Oracle ca DateTime, cursoarele notei si ale rulajelor,
*!* randul de vanzare corespunzator notei (inclusiv valuta documentului: in_valuta/id_valuta/nume_val,
*!* si id_fact - pentru garda eFactura pe pagina de articole),
*!* liniile de articole pentru pagina de articole factura (inclusiv in_stoc si id_vanzare_set/pret_achizitie,
@@ -13,7 +21,10 @@
*!* compararea/aplicarea sincronizarii cantitate/pret intre rulaje (trul) si articolele facturii (tvd),
*!* agregate pe id_articol, in ambele sensuri (documentele in valuta raman doar semnalate, neaplicate);
*!* pe linia de articol se poate alege si explicatia TVA (filtrata pe cota liniei), care recoreleaza
*!* codul SAF-T, iar liniile deja salvate se actualizeaza cu toate campurile pe care grila le schimba
*!* codul SAF-T, iar liniile deja salvate se actualizeaza cu toate campurile pe care grila le schimba,
*!* si oglindirea in IncarcaAntetFacturaEditare a copierii incasat->incasatCARD facuta la emitere
*!* directa pentru documentele cu tip_incasare=3 (POS/Card), ca reemiterea sa nu mai gaseasca
*!* lista de incasare goala in do_scrie_factura
*!* parametru: id_fact
@@ -30,6 +41,257 @@ FUNCTION EsteInEFactura
ENDFUNC && EsteInEFactura
*!* parametru: id_vanzare; verifica daca documentul are urmasi (aviz/factura/retur) prin VANZARI_CORESP
FUNCTION DocumentAreUrmasi
LPARAMETERS tnIdVanzare
LOCAL lcSql, lnNr, llSucces
lnNr = 0
lcSql = [select count(*) as lnnr from vanzari_coresp where sters = 0 and (] + ;
[(tip in (1,2,3) and id_vanzare_aviz = ] + Alltrim(Str(Nvl(m.tnIdVanzare,0))) + [) or ] + ;
[(tip = 2 and id_vanzare_aviz in (select id_vanzare_aviz from vanzari_coresp ] + ;
[where sters = 0 and tip = 1 and id_vanzare_fact = ] + Alltrim(Str(Nvl(m.tnIdVanzare,0))) + [)))]
llSucces = goExecutor.oSelecteaza2Value(m.lcSql, @lnNr)
RETURN (Nvl(m.lnNr,0) > 0)
ENDFUNC && DocumentAreUrmasi
*!* parametru: id_vanzare al unui document existent; iesire prin referinta - tnDiscountRon/tnDiscountValuta,
*!* discountul de document ca suma, in lei si in valuta; construieste oDateFactura cu tnIdSet=0, lEditare=.T.
*!* intoarce oDateFactura populat, sau .NULL. daca documentul nu exista/e sters sau la eroare Oracle
FUNCTION IncarcaAntetFacturaEditare
LPARAMETERS tnIdVanzare, tnDiscountRon, tnDiscountValuta
LOCAL lcSql, lnSucces, loDate, lcListaSursa, lnDiscount, lnDiscountVal
tnDiscountRon = 0
tnDiscountValuta = 0
IF Used('v_antet_editare')
Use In v_antet_editare
ENDIF
lcSql = [select id_vanzare, tip, eproforma, serie_act, numar_act, data_act, data_scad, discount, ] + ;
[discount_evidentiat, delegat, id_delegat, bidelegat, cnpdelegat, nrinmat, id_masina, nume_agent, ] + ;
[id_agent, adresa_facturare, id_facturare, dataora_exp, text_aditional, listare_detaliata, tip_saft, ] + ;
[efactura, id_ruta, in_valuta, id_valuta, nume_val, curs, multiplicator, id_part, client, cod_fiscal, ] + ;
[incasat, nr_incasare, serie_chit, tip_incasare, ] + ;
[id_gestiune, id_comanda, id_ctr from fact_vfacturi where id_vanzare = ] + Alltrim(Str(Nvl(m.tnIdVanzare,0))) + [ and sters = 0]
lnSucces = goExecutor.oExecute(lcSql,[v_antet_editare])
goExecutor.oReset()
IF lnSucces < 0
AMESSAGEBOX(goExecutor.cEroare,0+16,"Eroare")
RETURN .NULL.
ENDIF
IF Reccount('v_antet_editare') <> 1
Use In v_antet_editare
RETURN .NULL.
ENDIF
SELECT v_antet_editare
loDate = Createobject('oDateFactura',0,0)
loDate.lEditare = .T.
loDate.zi_curs = Ttod(data_act) && Init cheama citeste_cursuri_zi(poDate.zi_curs), altfel ramane {} si Oracle da ORA-01840
loDate.tip = tip
loDate.nIdTipDoc = Iif(Nvl(eproforma,0) = 1, loDate.nIdTipDocProforma, loDate.nIdTipDocFactura)
loDate.serie_act = Nvl(serie_act,[])
loDate.nract = Nvl(numar_act,0)
loDate.dataact = Ttod(data_act)
loDate.datascad = Ttod(data_scad)
loDate.discount_evidentiat = Nvl(discount_evidentiat,0)
loDate.in_valuta = Nvl(in_valuta,0)
IF loDate.in_valuta = 1
loDate.id_valuta = id_valuta
loDate.nume_valuta = Nvl(nume_val,[])
loDate.Curs = Nvl(curs,1)
loDate.multiplicator = Nvl(multiplicator,1)
lnDiscountVal = Nvl(discount,0)
lnDiscount = Round(lnDiscountVal * loDate.Curs / loDate.multiplicator, gnPPretV)
ELSE
lnDiscount = Nvl(discount,0)
lnDiscountVal = 0
ENDIF
tnDiscountRon = lnDiscount
tnDiscountValuta = lnDiscountVal
loDate.id_client = id_part
loDate.nume_client = Nvl(client,[])
loDate.cod_fiscal = Alltrim(Nvl(cod_fiscal,[]))
loDate.id_gestiune_init = id_gestiune
loDate.incasat = Nvl(incasat,0)
loDate.nr_incasare = Nvl(nr_incasare,0)
loDate.serie_chit = Alltrim(Nvl(serie_chit,[]))
loDate.ntip_incasare = Nvl(tip_incasare,0)
IF loDate.ntip_incasare = 3
loDate.incasatCARD = loDate.incasat
loDate.incasat = 0
ENDIF
loDate.id_delegat = id_delegat
loDate.nume_delegat = Nvl(delegat,[])
loDate.BIdelegat = Nvl(bidelegat,[])
loDate.CNPdelegat = Nvl(cnpdelegat,[])
loDate.id_masina = id_masina
loDate.nrinmat = Nvl(nrinmat,[])
loDate.id_agent = id_agent
loDate.nume_agent = Nvl(nume_agent,[])
loDate.id_facturare = id_facturare
loDate.adresa_facturare = Nvl(adresa_facturare,[])
loDate.dataora_exp = dataora_exp
*!* Chr(170) e substitutul CR+LF scris la salvare (ofacturare.vc2:14281) - se inverseaza la incarcare
loDate.text_aditional = Strtran(Nvl(text_aditional,[]), Chr(170), Chr(13)+Chr(10))
loDate.nListareDetaliata = Nvl(listare_detaliata,0)
loDate.tip_saft = Nvl(tip_saft,380)
loDate.eFactura = Nvl(efactura,0)
loDate.id_ruta = id_ruta
*!* sursa comanda/contract vine direct din antet - doar avizele trec prin VANZARI_CORESP
DO CASE
CASE !Empty(Nvl(id_comanda,0))
lcListaSursa = Alltrim(Str(id_comanda))
CASE !Empty(Nvl(id_ctr,0))
lcListaSursa = Alltrim(Str(id_ctr))
OTHERWISE
lcListaSursa = []
ENDCASE
loDate.cListaSursa = lcListaSursa
Use In v_antet_editare
loDate.cListaSursaAvize = IncarcaListaAvizeSursa(m.tnIdVanzare)
RETURN loDate
ENDFUNC && IncarcaAntetFacturaEditare
*!* parametru: id_vanzare al facturii; intoarce lista (separata prin virgula) a id_vanzare ale avizelor
*!* sursa (VANZARI_CORESP, TIP=1); lista goala daca documentul nu are avize sursa sau la eroare Oracle
FUNCTION IncarcaListaAvizeSursa
LPARAMETERS tnIdVanzare
LOCAL lcSql, lnSucces, lcLista
lcLista = []
IF Used('v_avize_sursa')
Use In v_avize_sursa
ENDIF
lcSql = [select id_vanzare_aviz from vanzari_coresp where sters = 0 and tip = 1 and id_vanzare_fact = ] + ;
Alltrim(Str(Nvl(m.tnIdVanzare,0)))
lnSucces = goExecutor.oExecute(lcSql,[v_avize_sursa])
goExecutor.oReset()
IF lnSucces < 0
AMESSAGEBOX(goExecutor.cEroare,0+16,"Eroare")
RETURN lcLista
ENDIF
IF lnSucces > 0
SELECT v_avize_sursa
SCAN
lcLista = lcLista + Iif(Empty(m.lcLista),[],[,]) + Alltrim(Str(id_vanzare_aviz))
ENDSCAN
ENDIF
Use In v_avize_sursa
RETURN lcLista
ENDFUNC && IncarcaListaAvizeSursa
*!* parametri: oDateFactura deja incarcat (antet), id_vanzare; incarca liniile nesterse din
*!* VVANZARI_ARTICOLE in crsfactura (deschis READWRITE, gol la eroare Oracle); intoarce mereu .T.
FUNCTION IncarcaLiniiFacturaEditare
LPARAMETERS toDate, tnIdVanzare
LOCAL llInValuta, lnCursDoc, lnMultDoc, lnIdTemp, lnGestionabil, lcSql
llInValuta = (toDate.in_valuta = 1)
lnCursDoc = Nvl(toDate.Curs,1)
lnMultDoc = Nvl(toDate.multiplicator,1)
IncarcaArticoleFactura(m.tnIdVanzare, 'crsarticole_doc')
*!* pret/discount_unitar din view sunt in moneda documentului; prelucreaza_facturacrs le vrea in lei
IF Used('crsarticole_editare')
Use In crsarticole_editare
ENDIF
SELECT id_vanzare_det, id_articol, explicatie, Nvl(denumire,[]) As denumire, lot, serie, codmat, id_jtva_coloana, taxcode, ;
pret_achizitie, cantitate, proc_tvav, pret_cu_tva, id_vanzare_set, id_gestiune, nume_gestiune, Cont, id_ctr, id_valuta, nume_val, in_stoc, ;
id_pol, pretd, id_valutad, ;
Nvl(curs, m.lnCursDoc) As curs, Nvl(multiplicator, m.lnMultDoc) As multiplicator, ;
Iif(m.llInValuta, Round(pret * Nvl(curs, m.lnCursDoc) / Nvl(multiplicator, m.lnMultDoc), gnPPretV), pret) As pret, ;
Iif(m.llInValuta, pret, 0) As pret_val, ;
Iif(m.llInValuta, Round(discount_unitar * Nvl(curs, m.lnCursDoc) / Nvl(multiplicator, m.lnMultDoc), gnPPretV), discount_unitar) As discount_unitar, ;
Iif(m.llInValuta, discount_unitar, 0) As discount_unitar_val, ;
Iif(m.llInValuta,1,0) As tip_valuta, ;
Cast(.Null. As N(10)) As id_jtva_coloana_ex, Space(50) As codmatf, Space(50) As codbare, Space(10) As um, Space(50) As numar_contract ;
FROM crsarticole_doc INTO CURSOR crsarticole_editare READWRITE
creeaza_facturacrs('crsfactura')
prelucreaza_facturacrs('crsarticole_editare','crsfactura',0,0,0)
*!* gestionabil vine din nomenclatorul curent, nu din id_gestiune; id_gestiune, nume_gestiune, Cont, id_pol/
*!* pretd/id_valuta_d si vvaldiminuatctva (total in valuta cu tva, cf. oproceduri_facturare.prg:2282)
*!* nu sunt in lista de insert a lui prelucreaza_facturacrs
SELECT crsarticole_editare
SCAN
lnIdTemp = id_vanzare_det
lnGestionabil = Iif(Nvl(in_stoc,0) <> 0, 1, 0)
SELECT crsfactura
REPLACE gestionabil WITH m.lnGestionabil, ;
id_gestiune WITH Nvl(crsarticole_editare.id_gestiune, -1000), ;
nume_gestiune WITH Nvl(crsarticole_editare.nume_gestiune, []), ;
Cont WITH Nvl(crsarticole_editare.Cont, []), ;
id_pol WITH crsarticole_editare.id_pol, ;
pretd WITH crsarticole_editare.pretd, ;
id_valuta_d WITH crsarticole_editare.id_valutad, ;
vvaldiminuatctva WITH vvaldiminuatftva + vvaldiminuattva ;
FOR id_temp = m.lnIdTemp
SELECT crsarticole_editare
ENDSCAN
*!* pretv_orig si legatura de rezervare nu sunt stocate pe document; se reiau din rulajul original
*!* pretv are sens doar pe gestiunile la pret de vanzare (nr_pag 6/7); in rest STOC.PRETV e 0
lcSql = [select r.id_articol, r.id_gestiune, Nvl(r.cont,' ') as cont, r.pret, ] + ;
[Nvl(r.serie,' ') as serie, r.cante as cantitate, ] + ;
[case when (select max(g.nr_pag) from nom_gestiuni g ] + ;
[where g.id_gestiune = r.id_gestiune) in (6,7) ] + ;
[then r.pretv else 0 end as pretv, r.id_tip_rulaj, ] + ;
[r.id_part_rez, r.id_lucrare_rez ] + ;
[from rul r, vanzari v ] + ;
[where v.id_vanzare = ] + Alltrim(Str(m.tnIdVanzare)) + ;
[ and r.cod = v.cod and r.sters = 0 and r.cante <> 0 ] + ;
[order by r.id_tip_rulaj desc, r.id_rul]
IF goExecutor.oExecuta(m.lcSql, 'crs_lot_orig')
SELECT crsfactura
SCAN FOR gestionabil = 1
SELECT crs_lot_orig
LOCATE FOR id_articol = crsfactura.id_articol AND id_gestiune = crsfactura.id_gestiune ;
AND Alltrim(cont) == Alltrim(crsfactura.Cont) AND pret = crsfactura.pret_achizitie ;
AND Alltrim(serie) == Alltrim(Nvl(crsfactura.serie,[])) AND cantitate = crsfactura.cantitate
IF Found()
REPLACE pretv_orig WITH crs_lot_orig.pretv, ;
id_part_rez WITH crs_lot_orig.id_part_rez, ;
id_lucrare_rez WITH crs_lot_orig.id_lucrare_rez IN crsfactura
ENDIF
SELECT crsfactura
ENDSCAN
Use In crs_lot_orig
ENDIF
Use In crsarticole_doc
Use In crsarticole_editare
RETURN .T.
ENDFUNC && IncarcaLiniiFacturaEditare
*!* parametri: cod, an, luna, arata si randurile sterse (implicit .F.)
*!* incarca cursoarele notei (tact) si ale rulajelor (trul, trul_obinv), cu recalculul de
*!* valoare/valtva/valoarev/valtvav pe randurile goale; raman deschise READWRITE pe seama apelantului
@@ -283,6 +545,7 @@ FUNCTION CreeazaCursorArticoleGol
CREATE CURSOR (m.lcAlias) (id_vanzare I NULL, id_vanzare_det I NULL, id_articol N(20) NULL, cantitate N(12,3) NULL, pret N(14,4) NULL, pret_cu_tva N(14,4) NULL, proc_tvav N(6,4) NULL, ;
discount_unitar N(14,4) NULL, id_gestiune I NULL, cont C(20) NULL, id_valuta I NULL, id_jtva_coloana I NULL, serie C(20) NULL, explicatie C(60) NULL, ;
taxcode I NULL, lot C(20) NULL, sters I NULL, id_vanzare_set I NULL, pret_achizitie N(14,4) NULL, denumire C(100) NULL, codmat C(30) NULL, nume_gestiune C(60) NULL, nume_val C(20) NULL, ;
id_pol I NULL, id_ctr I NULL, diferenta N(14,4) NULL, pretd N(14,4) NULL, id_valutad I NULL, curs N(10,4) NULL, multiplicator N(10,4) NULL, ;
lmodificat L, valoare N(14,2), in_stoc I NULL)
ENDFUNC && CreeazaCursorArticoleGol