#6 editare factura emisa: S4b - comparatia rulaje/articole si aplicarea ei

Rulajele contabile si articolele facturii sunt doua reprezentari independente ale
aceluiasi document, editabile separat. Helperele noi construiesc enumerarea
diferentelor dintre ele si o pot aplica, la cererea utilizatorului.

- ConstruiestePropunereSincronizare: potrivire pe id_articol, singura cheie
  comuna - nu exista corespondent la nivel de linie intre rulaj si articol.
  Agregarea RUL foloseste pretul mediu ponderat cand un articol are mai multe
  randuri. Articolele nestocate, documentele in valuta si articolele cu mai
  multe randuri RUL active raman N-A, cu motiv afisat: propunerea ar fi o
  presupunere, nu un calcul.
- AplicaSincronizareArticole: scrie doar in tvd/trul, in memorie. Nu adauga
  randuri de rulaj (conturile nu se deduc din articol) si nu sterge linii in
  nicio directie - divergenta se semnaleaza, stergerea ramane manuala.
  Scrierea reala in Oracle ramane calea existenta, la salvare.
- test_s4b_sincronizare: headless, fara Oracle, pe cursoare construite in test.

Butonul, dialogul si verificarea la salvare vin separat.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SN8snvkk94KuhWwoXUUey3
This commit is contained in:
2026-08-11 00:35:09 +03:00
parent a96b6a6eef
commit d3268a69bc
2 changed files with 816 additions and 2 deletions

View File

@@ -1,6 +1,6 @@
*** ofacturare_editare
*!* 10.08.2026
*!* 11.08.2026
*!* marius.mutu
*!* helpere comune pentru editarea unei facturi emise: garda eFactura, cursoarele notei si ale rulajelor,
*!* randul de vanzare corespunzator notei (inclusiv valuta documentului: in_valuta/id_valuta/nume_val,
@@ -9,7 +9,9 @@
*!* pentru corectia verdictului RUL pe liniile nestocate si pentru liniile din seturi), pregatirea
*!* cursorului de articole inainte de Createobject(frm_modific2024), construirea obiectului poArticol
*!* pentru adaugarea unei linii noi prin dialogul frm_articol_factura (populat cu valuta documentului
*!* cand acesta e in valuta) si scrierea in VANZARI_DETALII a liniilor editate/adaugate/sterse
*!* cand acesta e in valuta), scrierea in VANZARI_DETALII a liniilor editate/adaugate/sterse, si
*!* 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)
*!* parametru: id_fact
@@ -554,3 +556,367 @@ FUNCTION ScrieArticoleFacturaEditate
RETURN m.llSucces
ENDFUNC && ScrieArticoleFacturaEditate
*!* parametru: tcDirectie - 'RUL_SURSA' (implicit, rulajul actualizeaza articolele) sau 'ARTICOLE_SURSA'
*!* (articolele actualizeaza rulajul); orice alta valoare cade pe 'RUL_SURSA'
*!* compara rulajele (trul) cu articolele facturii (tvd), agregate pe id_articol (singura cheie comuna,
*!* fara FK la nivel de linie), si produce cursorul propunere_sincronizare (READWRITE, lasat deschis pe
*!* seama apelantului): id_articol, denumire, codmat, cantitate_veche/noua, pret_vechi/nou, actiune
*!* (Modificare/Adaugare/Semnalare/N-A), motiv. "vechi" = cursorul-tinta curent, "nou" = cursorul-sursa
*!* calculat (agregare formula E.3: SUM(cant*pretvtva) + SUM(cante*pretvtva) pe randurile id_tip_rulaj<>3,
*!* pret mediu ponderat); articolele identice intre sursa si tinta nu apar in cursor. N-A pe: articol
*!* nestocat (fara randuri RUL posibile), articol cu mai multe randuri RUL active (niciodata alegere
*!* automata a randului) sau document in valuta (conversia RUL<->valuta nu e acoperita de aceasta functie)
*!* retur logic: .F. daca tvd/trul nu sunt deschise (cursorul iese oricum creat, gol)
FUNCTION ConstruiestePropunereSincronizare
LPARAMETERS tcDirectie
LOCAL lcDirectie, lnAreaOrigine, lnCantLinie, lnValLinie, llDocInValuta
LOCAL lnCantRul, lnValRul, lnNrandRul, lnPretRul, lcDenumireRul, lcCodmatRul
LOCAL lnCantTvd, lnValTvd, lnNrandTvd, lnPretTvd, lcDenumireTvd, lcCodmatTvd, llInStoc
LOCAL llGasitSursa, llGasitTinta, lnCantSursa, lnPretSursa, lnCantTinta, lnPretTinta
LOCAL lnCantVechi, lnCantNou, lnPretVechi, lnPretNou, lcActiune, lcMotiv, lcDenumire, lcCodmat
lcDirectie = IIF(Upper(Alltrim(Nvl(m.tcDirectie,''))) == 'ARTICOLE_SURSA', 'ARTICOLE_SURSA', 'RUL_SURSA')
lnAreaOrigine = Select()
IF Used('propunere_sincronizare')
Use In propunere_sincronizare
ENDIF
CREATE CURSOR propunere_sincronizare (id_articol I, denumire C(100), codmat C(30), ;
cantitate_veche N(12,3), cantitate_noua N(12,3), pret_vechi N(14,4), pret_nou N(14,4), ;
actiune C(12), motiv C(80))
IF !Used('tvd') OR !Used('trul')
IF m.lnAreaOrigine > 0
SELECT (m.lnAreaOrigine)
ENDIF
RETURN .F.
ENDIF
llDocInValuta = (Used('tvanz') AND Reccount('tvanz') = 1 AND Nvl(tvanz.in_valuta,0) <> 0)
*!* agregare RUL pe articol (formula E.3) - cant/val insumate, nrand = randuri active distincte
IF Used('agg_rul')
Use In agg_rul
ENDIF
CREATE CURSOR agg_rul (id_articol I, denumire C(100), codmat C(30), cant N(12,3), val N(16,4), nrand I)
SELECT trul
SCAN FOR Nvl(sters,0) <> 1
lnCantLinie = Nvl(cant,0) + IIF(Nvl(id_tip_rulaj,0) <> 3, Nvl(cante,0), 0)
lnValLinie = Nvl(cant,0) * Nvl(pretvtva,0) + IIF(Nvl(id_tip_rulaj,0) <> 3, Nvl(cante,0) * Nvl(pretvtva,0), 0)
lcDenumireRul = Nvl(trul.denumire,'')
lcCodmatRul = Nvl(trul.codmat,'')
SELECT agg_rul
LOCATE FOR id_articol = trul.id_articol
IF Found()
REPLACE cant WITH cant + m.lnCantLinie, val WITH val + m.lnValLinie, nrand WITH nrand + 1 IN agg_rul
ELSE
INSERT INTO agg_rul (id_articol, denumire, codmat, cant, val, nrand) ;
VALUES (trul.id_articol, m.lcDenumireRul, m.lcCodmatRul, m.lnCantLinie, m.lnValLinie, 1)
ENDIF
SELECT trul
ENDSCAN
*!* agregare articole factura pe articol - val se ia din tvd.valoare (deja calculata, cu TVA, la incarcare/editare)
IF Used('agg_tvd')
Use In agg_tvd
ENDIF
CREATE CURSOR agg_tvd (id_articol I, denumire C(100), codmat C(30), cant N(12,3), val N(16,4), nrand I, in_stoc I)
SELECT tvd
SCAN FOR Nvl(sters,0) <> 1
lcDenumireTvd = Nvl(tvd.denumire,'')
lcCodmatTvd = Nvl(tvd.codmat,'')
SELECT agg_tvd
LOCATE FOR id_articol = tvd.id_articol
IF Found()
REPLACE cant WITH cant + Nvl(tvd.cantitate,0), val WITH val + Nvl(tvd.valoare,0), nrand WITH nrand + 1 IN agg_tvd
ELSE
INSERT INTO agg_tvd (id_articol, denumire, codmat, cant, val, nrand, in_stoc) ;
VALUES (tvd.id_articol, m.lcDenumireTvd, m.lcCodmatTvd, Nvl(tvd.cantitate,0), Nvl(tvd.valoare,0), 1, Nvl(tvd.in_stoc,1))
ENDIF
SELECT tvd
ENDSCAN
IF Used('crs_articole_unite')
Use In crs_articole_unite
ENDIF
SELECT id_articol FROM agg_rul ;
UNION ;
SELECT id_articol FROM agg_tvd ;
INTO CURSOR crs_articole_unite READWRITE
SELECT crs_articole_unite
SCAN
lcDenumire = ''
lcCodmat = ''
SELECT agg_rul
LOCATE FOR id_articol = crs_articole_unite.id_articol
llGasitSursa = .F. && folosit mai jos doar dupa mapare pe directie
IF Found()
lnCantRul = cant
lnValRul = val
lnNrandRul = nrand
lcDenumire = denumire
lcCodmat = codmat
ELSE
lnCantRul = 0
lnValRul = 0
lnNrandRul = 0
ENDIF
lnPretRul = IIF(m.lnCantRul <> 0, m.lnValRul / m.lnCantRul, 0)
SELECT agg_tvd
LOCATE FOR id_articol = crs_articole_unite.id_articol
IF Found()
lnCantTvd = cant
lnValTvd = val
lnNrandTvd = nrand
llInStoc = (Nvl(in_stoc,1) <> 0)
IF !Empty(Nvl(denumire,''))
lcDenumire = denumire
lcCodmat = codmat
ENDIF
ELSE
lnCantTvd = 0
lnValTvd = 0
lnNrandTvd = 0
llInStoc = .T.
ENDIF
lnPretTvd = IIF(m.lnCantTvd <> 0, m.lnValTvd / m.lnCantTvd, 0)
IF m.lcDirectie == 'RUL_SURSA'
llGasitSursa = (m.lnNrandRul > 0)
llGasitTinta = (m.lnNrandTvd > 0)
lnCantSursa = m.lnCantRul
lnPretSursa = m.lnPretRul
lnCantTinta = m.lnCantTvd
lnPretTinta = m.lnPretTvd
ELSE
llGasitSursa = (m.lnNrandTvd > 0)
llGasitTinta = (m.lnNrandRul > 0)
lnCantSursa = m.lnCantTvd
lnPretSursa = m.lnPretTvd
lnCantTinta = m.lnCantRul
lnPretTinta = m.lnPretRul
ENDIF
lnCantVechi = m.lnCantTinta
lnPretVechi = m.lnPretTinta
lnCantNou = m.lnCantSursa
lnPretNou = m.lnPretSursa
lcActiune = ''
lcMotiv = ''
DO CASE
CASE m.llDocInValuta
lcActiune = 'N-A'
lcMotiv = 'Documentul e in valuta - sincronizarea automata a preturilor nu e suportata, verificati manual'
CASE m.lnNrandRul > 1
lcActiune = 'N-A'
lcMotiv = 'Mai multe randuri RUL active pe acelasi articol, verificati manual'
CASE m.lnNrandTvd > 0 AND !m.llInStoc
lcActiune = 'N-A'
lcMotiv = 'Articol nestocat, fara corespondent in rulaje'
CASE m.llGasitSursa AND !m.llGasitTinta
lcActiune = 'Adaugare'
lcMotiv = IIF(m.lcDirectie == 'ARTICOLE_SURSA', ;
'Articol prezent doar in articolele facturii - adaugarea unui rand nou in rulaje nu se aplica automat', '')
CASE !m.llGasitSursa AND m.llGasitTinta
lcActiune = 'Semnalare'
lcMotiv = 'Articol fara corespondent in sursa - stergerea nu se face automat'
OTHERWISE
IF Abs(m.lnCantVechi - m.lnCantNou) < 0.0005 AND Round(m.lnPretVechi, m.gnPC) == Round(m.lnPretNou, m.gnPC)
lcActiune = ''
ELSE
lcActiune = 'Modificare'
ENDIF
ENDCASE
IF !Empty(m.lcActiune)
INSERT INTO propunere_sincronizare (id_articol, denumire, codmat, cantitate_veche, cantitate_noua, ;
pret_vechi, pret_nou, actiune, motiv) VALUES (crs_articole_unite.id_articol, m.lcDenumire, m.lcCodmat, ;
m.lnCantVechi, m.lnCantNou, Round(m.lnPretVechi,4), Round(m.lnPretNou,4), m.lcActiune, m.lcMotiv)
ENDIF
SELECT crs_articole_unite
ENDSCAN
IF Used('agg_rul')
Use In agg_rul
ENDIF
IF Used('agg_tvd')
Use In agg_tvd
ENDIF
IF Used('crs_articole_unite')
Use In crs_articole_unite
ENDIF
IF m.lnAreaOrigine > 0
SELECT (m.lnAreaOrigine)
ENDIF
RETURN .T.
ENDFUNC && ConstruiestePropunereSincronizare
*!* parametri: tcDirectie (ca la ConstruiestePropunereSincronizare), toForm (obiectul frm_modific2024,
*!* opus/reutilizat pentru calculeaza_valori_articol/AdaugaLinieTvdDinArticol - fara el, liniile
*!* Modificare/Adaugare pe tvd nu se aplica, doar cele pe trul, care nu au nevoie de metode de formular)
*!* reconstruieste propunerea (ConstruiestePropunereSincronizare) si aplica tot ce nu e N-A/Semnalare,
*!* DOAR in cursoarele din memorie (tvd/trul) - niciun INSERT/UPDATE Oracle; RUL_SURSA scrie in tvd
*!* (cantitate/pret, pastrand pret_cu_tva existent pe rand), ARTICOLE_SURSA scrie doar cant/pretvtva pe
*!* randul RUL deja existent (niciodata rand RUL nou - lipsesc contul/gestiunea sigure din articol)
*!* retur numeric: cate linii au fost efectiv aplicate (ca apelantul sa poata reface bara de totaluri)
FUNCTION AplicaSincronizareArticole
LPARAMETERS tcDirectie, toForm
LOCAL lcDirectie, lnAplicate, lnAreaOrigine, lnIdArticol, lcActiune, lnCantitateNoua, lnPretNou
lcDirectie = IIF(Upper(Alltrim(Nvl(m.tcDirectie,''))) == 'ARTICOLE_SURSA', 'ARTICOLE_SURSA', 'RUL_SURSA')
lnAplicate = 0
lnAreaOrigine = Select()
IF !ConstruiestePropunereSincronizare(m.lcDirectie)
IF m.lnAreaOrigine > 0
SELECT (m.lnAreaOrigine)
ENDIF
RETURN 0
ENDIF
SELECT propunere_sincronizare
SCAN FOR Inlist(Alltrim(actiune), 'Modificare', 'Adaugare')
lnIdArticol = id_articol
lcActiune = Alltrim(actiune)
lnCantitateNoua = cantitate_noua
lnPretNou = pret_nou
DO CASE
CASE m.lcDirectie == 'RUL_SURSA' AND m.lcActiune == 'Modificare'
IF AplicaModificareTvd(m.lnIdArticol, m.lnCantitateNoua, m.lnPretNou, m.toForm)
lnAplicate = m.lnAplicate + 1
ENDIF
CASE m.lcDirectie == 'RUL_SURSA' AND m.lcActiune == 'Adaugare'
IF AplicaAdaugareTvd(m.lnIdArticol, m.lnCantitateNoua, m.lnPretNou, m.toForm)
lnAplicate = m.lnAplicate + 1
ENDIF
CASE m.lcDirectie == 'ARTICOLE_SURSA' AND m.lcActiune == 'Modificare'
IF AplicaModificareTrul(m.lnIdArticol, m.lnCantitateNoua, m.lnPretNou)
lnAplicate = m.lnAplicate + 1
ENDIF
*!* ARTICOLE_SURSA + Adaugare: fara scriere, un rand RUL nou nu se poate crea sigur din articol
ENDCASE
SELECT propunere_sincronizare
ENDSCAN
IF m.lnAreaOrigine > 0
SELECT (m.lnAreaOrigine)
ENDIF
RETURN m.lnAplicate
ENDFUNC && AplicaSincronizareArticole
*!* parametri: id_articol, cantitate/pret noi (pret_nou e intotdeauna cu TVA - formula E.3 din RUL),
*!* toForm (opus, cu metoda calculeaza_valori_articol - fara el se scrie doar REPLACE-ul, fara recalcul
*!* de valoare/bara totaluri, pe seama apelantului)
*!* scrie in tvd (primul rand activ gasit pe articol) cantitatea si pretul propuse, pastrand pret_cu_tva
*!* existent pe rand (converteste pret_nou la conventia fara TVA a randului daca era asa dinainte);
*!* discount_unitar se reseteaza la 0 - pretul propus e deja pretul mediu ponderat efectiv din rulaje
FUNCTION AplicaModificareTvd
LPARAMETERS tnIdArticol, tnCantitateNoua, tnPretNouCuTva, toForm
LOCAL lnAreaOrigine, llAplicat, lnPretDeScris, lnProcTvav
lnAreaOrigine = Select()
llAplicat = .F.
IF Used('tvd')
SELECT tvd
LOCATE FOR id_articol = m.tnIdArticol AND Nvl(sters,0) <> 1
IF Found()
lnProcTvav = Nvl(proc_tvav,1)
IF m.lnProcTvav = 0
lnProcTvav = 1
ENDIF
lnPretDeScris = IIF(Nvl(pret_cu_tva,0) = 0, m.tnPretNouCuTva / m.lnProcTvav, m.tnPretNouCuTva)
REPLACE cantitate WITH m.tnCantitateNoua, pret WITH m.lnPretDeScris, discount_unitar WITH 0, ;
lmodificat WITH .T. IN tvd
IF Vartype(m.toForm) == 'O' AND Pemstatus(m.toForm, 'calculeaza_valori_articol', 5)
m.toForm.calculeaza_valori_articol()
ENDIF
llAplicat = .T.
ENDIF
ENDIF
IF m.lnAreaOrigine > 0
SELECT (m.lnAreaOrigine)
ENDIF
RETURN m.llAplicat
ENDFUNC && AplicaModificareTvd
*!* parametri: id_articol (existent doar in trul), cantitate/pret noi (pret_nou cu TVA), toForm (obligatoriu,
*!* cu metoda AdaugaLinieTvdDinArticol - fara el functia nu are cum sa adauge linia, intoarce .F.)
*!* construieste obiectul poArticol (tiparul din CreeazaPoArticolNouTvd, cont/id_gestiune/um preluate din
*!* randul RUL al articolului) si il preda lui toForm.AdaugaLinieTvdDinArticol - linia noua intra in tvd
*!* cu pret_cu_tva=1 (pretul propus e deja cu TVA) si discount_unitar=0; document RON (tip_valuta=0),
*!* documentele in valuta sunt deja excluse la nivelul propunerii
FUNCTION AplicaAdaugareTvd
LPARAMETERS tnIdArticol, tnCantitateNoua, tnPretNouCuTva, toForm
LOCAL lnAreaOrigine, llAplicat, loArticol
lnAreaOrigine = Select()
llAplicat = .F.
IF Vartype(m.toForm) == 'O' AND Pemstatus(m.toForm, 'AdaugaLinieTvdDinArticol', 5) AND Used('trul')
SELECT trul
LOCATE FOR id_articol = m.tnIdArticol AND Nvl(sters,0) <> 1
IF Found()
loArticol = CreeazaPoArticolNouTvd(trul.id_articol, Nvl(trul.codmat,''), Nvl(trul.denumire,''), ;
Nvl(trul.um,''), Nvl(trul.cont,''), .F., 1, 1, '', 0)
loArticol.id_gestiune = Nvl(trul.id_gestiune, -1000)
loArticol.cantitate = m.tnCantitateNoua
loArticol.preturi_cu_tva = 1
loArticol.pretctva = m.tnPretNouCuTva
loArticol.proc_tvav = Nvl(trul.proc_tvav, 1)
loArticol.discount_unitar_ctva = 0
m.toForm.AdaugaLinieTvdDinArticol(loArticol)
llAplicat = .T.
ENDIF
ENDIF
IF m.lnAreaOrigine > 0
SELECT (m.lnAreaOrigine)
ENDIF
RETURN m.llAplicat
ENDFUNC && AplicaAdaugareTvd
*!* parametri: id_articol, cantitate/pret noi (pret_nou cu TVA)
*!* scrie in trul (singurul rand activ gasit pe articol - garantat unic, altfel propunerea a marcat N-A)
*!* doar cant/cante (dupa care din ele era deja folosit pe rand) si pretvtva; nu atinge id_tip_rulaj,
*!* conturi (scd/scc) sau alte campuri derivate (valoare/tva/etc.) - raman de recalculat de apelant daca e nevoie
FUNCTION AplicaModificareTrul
LPARAMETERS tnIdArticol, tnCantitateNoua, tnPretNouCuTva
LOCAL lnAreaOrigine, llAplicat, lcCampCant
lnAreaOrigine = Select()
llAplicat = .F.
IF Used('trul')
SELECT trul
LOCATE FOR id_articol = m.tnIdArticol AND Nvl(sters,0) <> 1
IF Found()
lcCampCant = IIF(Nvl(cant,0) <> 0, 'cant', 'cante')
REPLACE (m.lcCampCant) WITH m.tnCantitateNoua, pretvtva WITH m.tnPretNouCuTva IN trul
llAplicat = .T.
ENDIF
ENDIF
IF m.lnAreaOrigine > 0
SELECT (m.lnAreaOrigine)
ENDIF
RETURN m.llAplicat
ENDFUNC && AplicaModificareTrul

View File

@@ -0,0 +1,448 @@
* test_s4b_sincronizare.prg
* Acopera S4b etapa 1 (ConstruiestePropunereSincronizare/AplicaSincronizareArticole, ofacturare_editare.prg):
* compararea si aplicarea sincronizarii cantitate/pret intre rulaje (trul) si articolele facturii (tvd),
* agregate pe id_articol. Complet headless, FARA Oracle - niciuna din cele doua functii testate atinge
* goExecutor; tvd/trul sunt construite direct in test (CreeazaCursorArticoleGol pentru tvd, structura
* minimala relevanta pentru trul), nu vin din documente reale.
*
* Cazuri acoperite, pe fiecare directie (RUL_SURSA/ARTICOLE_SURSA) unde se aplica: identic (nu apare in
* cursor), modificare, adaugare, semnalare, articol nestocat (N-A), mai multe randuri RUL active pe
* acelasi articol (N-A, chiar daca agregatul ar fi identic), randuri STERS excluse din agregare, document
* in valuta (N-A pe tot documentul), aplicarea efectiva (REPLACE pe tvd/trul, pastrarea conventiei
* pret_cu_tva la Modificare, alegerea cant/cante dupa care era populat, Adaugare in ARTICOLE_SURSA
* niciodata scrisa), toForm absent (Adaugare pe tvd se sare, fara eroare).
*
* dummyform (DEFINE CLASS la finalul fisierului) e un test-double care oglindeste EXACT
* calculeaza_valori_articol/AdaugaLinieTvdDinArticol/ActualizeazaBaraTotaluri din omodificari.vc2 -
* tipar identic cu dummyexecutor din test_s5_validari_articole.prg (acelasi folder): scop de verificare
* ca AplicaSincronizareArticole apeleaza metodele corecte, cu argumentele corecte, fara sa deschida
* formularul real sau sa atinga vreun .vc2.
*
* Nu scrie in Oracle. Nu da COMMIT. QUIT la final.
SET SAFETY OFF
SET TALK OFF
LOCAL lcLog
lcLog = 'D:\ROA\ROAFACTURARE\COMUN\utile\Teste\editare_factura\test_s4b_sincronizare_log.txt'
STRTOFILE('start ' + TTOC(DATETIME()) + CHR(13) + CHR(10), lcLog)
PUBLIC gcErrLog, gnPass, gnFail
gcErrLog = lcLog
gnPass = 0
gnFail = 0
ON ERROR DO test_error_handler WITH ERROR(), MESSAGE(), PROGRAM(), LINENO()
IF Type('gnPC') <> 'N'
PUBLIC gnPC
gnPC = 2
ENDIF
SET PROCEDURE TO D:\ROA\ROAFACTURARE\COMUN\programe\ofacturare_editare.prg ADDITIVE
*============================================================================================
* SECTIUNEA A - ConstruiestePropunereSincronizare, caz cu caz
*============================================================================================
*!* --- A1: identic - nu apare in cursor ---
CreeazaTvdTest()
CreeazaTrulTest()
AdaugaTvd(100, 5, 10, 1, 1, 'ARTICOL 100', 'COD100', 50)
AdaugaTrul(100, 5, 0, 10, 0, 1, '371', 1)
ConstruiestePropunereSincronizare('RUL_SURSA')
SELECT propunere_sincronizare
LOCATE FOR id_articol = 100
DO asserteaza WITH 'A1 identic RUL_SURSA: articolul nu apare in propunere', !Found(), TRANSFORM(Reccount())
ConstruiestePropunereSincronizare('ARTICOLE_SURSA')
SELECT propunere_sincronizare
LOCATE FOR id_articol = 100
DO asserteaza WITH 'A1 identic ARTICOLE_SURSA: articolul nu apare in propunere', !Found(), TRANSFORM(Reccount())
*!* --- A2: modificare, ambele directii pe acelasi cursor ---
CreeazaTvdTest()
CreeazaTrulTest()
AdaugaTvd(200, 3, 10, 1, 1, 'ARTICOL 200', 'COD200', 30)
AdaugaTrul(200, 5, 0, 12, 0, 1, '371', 1)
ConstruiestePropunereSincronizare('RUL_SURSA')
SELECT propunere_sincronizare
LOCATE FOR id_articol = 200
DO asserteaza WITH 'A2 modificare RUL_SURSA: gasit', Found(), ''
IF Found()
DO asserteaza WITH 'A2 modificare RUL_SURSA: actiune', Alltrim(actiune) == 'Modificare', actiune
DO asserteaza WITH 'A2 modificare RUL_SURSA: cant veche=tvd(3)/noua=rul(5)', cantitate_veche == 3 AND cantitate_noua == 5, ;
TRANSFORM(cantitate_veche) + '/' + TRANSFORM(cantitate_noua)
DO asserteaza WITH 'A2 modificare RUL_SURSA: pret vechi=10/nou=12', pret_vechi == 10 AND pret_nou == 12, ;
TRANSFORM(pret_vechi) + '/' + TRANSFORM(pret_nou)
ENDIF
ConstruiestePropunereSincronizare('ARTICOLE_SURSA')
SELECT propunere_sincronizare
LOCATE FOR id_articol = 200
DO asserteaza WITH 'A2 modificare ARTICOLE_SURSA: actiune si valori inversate (veche=rul(5)/noua=tvd(3))', ;
Found() AND Alltrim(actiune) == 'Modificare' AND cantitate_veche == 5 AND cantitate_noua == 3 AND pret_vechi == 12 AND pret_nou == 10, ;
'gasit=' + TRANSFORM(Found())
*!* --- A3: doar in trul -> Adaugare (RUL_SURSA) / Semnalare (ARTICOLE_SURSA) ---
CreeazaTvdTest()
CreeazaTrulTest()
AdaugaTrul(300, 2, 0, 25, 0, 50, '371', 1.19)
ConstruiestePropunereSincronizare('RUL_SURSA')
SELECT propunere_sincronizare
LOCATE FOR id_articol = 300
DO asserteaza WITH 'A3 doar in trul, RUL_SURSA: Adaugare, cant 0->2, pret 0->25', ;
Found() AND Alltrim(actiune) == 'Adaugare' AND cantitate_veche == 0 AND cantitate_noua == 2 AND pret_vechi == 0 AND pret_nou == 25, ''
ConstruiestePropunereSincronizare('ARTICOLE_SURSA')
SELECT propunere_sincronizare
LOCATE FOR id_articol = 300
DO asserteaza WITH 'A3 doar in trul, ARTICOLE_SURSA: Semnalare, cant veche=2/noua=0', ;
Found() AND Alltrim(actiune) == 'Semnalare' AND cantitate_veche == 2 AND cantitate_noua == 0, ''
*!* --- A4: doar in tvd (stocat) -> Semnalare (RUL_SURSA) / Adaugare (ARTICOLE_SURSA) ---
CreeazaTvdTest()
CreeazaTrulTest()
AdaugaTvd(400, 4, 8, 1, 1, 'ARTICOL 400', 'COD400', 32)
ConstruiestePropunereSincronizare('RUL_SURSA')
SELECT propunere_sincronizare
LOCATE FOR id_articol = 400
DO asserteaza WITH 'A4 doar in tvd, RUL_SURSA: Semnalare, cant veche=4/noua=0', ;
Found() AND Alltrim(actiune) == 'Semnalare' AND cantitate_veche == 4 AND cantitate_noua == 0, ''
ConstruiestePropunereSincronizare('ARTICOLE_SURSA')
SELECT propunere_sincronizare
LOCATE FOR id_articol = 400
DO asserteaza WITH 'A4 doar in tvd, ARTICOLE_SURSA: Adaugare, cant veche=0/noua=4, pret nou=8, motiv nemarcat', ;
Found() AND Alltrim(actiune) == 'Adaugare' AND cantitate_veche == 0 AND cantitate_noua == 4 AND pret_nou == 8 AND 'rulaje' $ Lower(motiv), motiv
*!* --- A5: articol nestocat -> N-A, ambele directii ---
CreeazaTvdTest()
CreeazaTrulTest()
AdaugaTvd(500, 1, 99, 1, 0, 'ARTICOL 500 SERVICIU', 'COD500', 99)
ConstruiestePropunereSincronizare('RUL_SURSA')
SELECT propunere_sincronizare
LOCATE FOR id_articol = 500
DO asserteaza WITH 'A5 nestocat RUL_SURSA: N-A', Found() AND Alltrim(actiune) == 'N-A' AND 'nestocat' $ Lower(motiv), ''
ConstruiestePropunereSincronizare('ARTICOLE_SURSA')
SELECT propunere_sincronizare
LOCATE FOR id_articol = 500
DO asserteaza WITH 'A5 nestocat ARTICOLE_SURSA: N-A', Found() AND Alltrim(actiune) == 'N-A' AND 'nestocat' $ Lower(motiv), ''
*!* --- A6: mai multe randuri RUL active (pereche diferenta de pret) -> N-A, chiar daca agregatul ar fi identic cu tvd ---
CreeazaTvdTest()
CreeazaTrulTest()
AdaugaTvd(600, 2, 110, 1, 1, 'ARTICOL 600', 'COD600', 220)
AdaugaTrul(600, 0, 2, 100, 3, 1, '371', 1)
AdaugaTrul(600, 2, 0, 110, 3, 1, '371', 1)
ConstruiestePropunereSincronizare('RUL_SURSA')
SELECT propunere_sincronizare
LOCATE FOR id_articol = 600
DO asserteaza WITH 'A6 mai multe randuri RUL: N-A, nu identic, chiar daca agregatul (2*110=220) egaleaza tvd', ;
Found() AND Alltrim(actiune) == 'N-A' AND 'mai multe randuri' $ Lower(motiv), ''
ConstruiestePropunereSincronizare('ARTICOLE_SURSA')
SELECT propunere_sincronizare
LOCATE FOR id_articol = 600
DO asserteaza WITH 'A6 mai multe randuri RUL: N-A si in ARTICOLE_SURSA', ;
Found() AND Alltrim(actiune) == 'N-A' AND 'mai multe randuri' $ Lower(motiv), ''
*!* --- A7: randurile STERS nu intra in agregare (nici pe trul, nici pe tvd) ---
CreeazaTvdTest()
CreeazaTrulTest()
AdaugaTvd(700, 3, 15, 1, 1, 'ARTICOL 700', 'COD700', 45)
AdaugaTvd(700, 999, 999, 1, 1, 'ARTICOL 700 STEARSA', 'COD700', 999999)
SELECT tvd
GO Bottom
REPLACE sters WITH 1 IN tvd
AdaugaTrul(700, 3, 0, 15, 0, 1, '371', 1)
AdaugaTrul(700, 999, 0, 999, 0, 1, '371', 1)
SELECT trul
GO Bottom
REPLACE sters WITH 1 IN trul
ConstruiestePropunereSincronizare('RUL_SURSA')
SELECT propunere_sincronizare
LOCATE FOR id_articol = 700
DO asserteaza WITH 'A7 randuri sters excluse din agregare: articolul 700 e identic (nu apare in propunere)', !Found(), TRANSFORM(Reccount())
*!* --- A8: document in valuta -> N-A pe tot, chiar pe un caz care ar fi fost Modificare ---
CreeazaTvdTest()
CreeazaTrulTest()
AdaugaTvd(200, 3, 10, 1, 1, 'ARTICOL 200', 'COD200', 30)
AdaugaTrul(200, 5, 0, 12, 0, 1, '371', 1)
IF Used('tvanz')
Use In tvanz
ENDIF
CreeazaCursorTvanzGol()
INSERT INTO tvanz (id_vanzare, discount, total_fara_tva, total_tva, total_cu_tva, curs, multiplicator, in_valuta, id_valuta, nume_val, id_fact) ;
VALUES (1, 0, 0, 0, 0, 4.97, 1, 1, 999, 'EUR', 1)
ConstruiestePropunereSincronizare('RUL_SURSA')
SELECT propunere_sincronizare
LOCATE FOR id_articol = 200
DO asserteaza WITH 'A8 document in valuta: N-A, motiv mentioneaza valuta', ;
Found() AND Alltrim(actiune) == 'N-A' AND 'valuta' $ Lower(motiv), ''
IF Used('tvanz')
Use In tvanz
ENDIF
*!* --- A9: retur .F. cand tvd/trul nu sunt deschise, cursorul iese oricum creat ---
IF Used('tvd')
Use In tvd
ENDIF
IF Used('trul')
Use In trul
ENDIF
LOCAL llRetA9
llRetA9 = ConstruiestePropunereSincronizare('RUL_SURSA')
DO asserteaza WITH 'A9 fara tvd/trul deschise: retur .F., cursor gol dar deschis', ;
llRetA9 == .F. AND Used('propunere_sincronizare') AND Reccount('propunere_sincronizare') == 0, TRANSFORM(llRetA9)
*============================================================================================
* SECTIUNEA B - AplicaSincronizareArticole, directia RUL_SURSA (scrie in tvd)
*============================================================================================
LOCAL loForm
loForm = Createobject('dummyform')
CreeazaTvdTest()
CreeazaTrulTest()
*!* 800: modificare - pastreaza pret_cu_tva=1 existent, discount resetat
AdaugaTvd(800, 2, 10, 1, 1, 'ARTICOL 800', 'COD800', 20)
AdaugaTrul(800, 4, 0, 16, 0, 1, '371', 1.19)
*!* 900: adaugare - doar in trul
AdaugaTrul(900, 1, 0, 50, 0, 2, '371', 1.19)
*!* 1000: semnalare - doar in tvd, stocat -> nu se aplica nimic
AdaugaTvd(1000, 1, 1, 1, 1, 'ARTICOL 1000', 'COD1000', 1)
*!* 1100: N-A - nestocat -> nu se aplica nimic
AdaugaTvd(1100, 1, 1, 1, 0, 'ARTICOL 1100', 'COD1100', 1)
LOCAL lnReccountTvdInainte
lnReccountTvdInainte = Reccount('tvd')
LOCAL lnAplicateB1
lnAplicateB1 = AplicaSincronizareArticole('RUL_SURSA', loForm)
DO asserteaza WITH 'B1 RUL_SURSA: 2 linii aplicate (modificare 800 + adaugare 900), semnalare/N-A sarite', lnAplicateB1 == 2, TRANSFORM(lnAplicateB1)
DO asserteaza WITH 'B1: tvd a crescut cu exact o linie (doar Adaugarea 900)', Reccount('tvd') == m.lnReccountTvdInainte + 1, TRANSFORM(Reccount('tvd'))
SELECT tvd
LOCATE FOR id_articol = 800
DO asserteaza WITH 'B1 modificare 800: cantitate=4, pret=16 (pret_cu_tva era 1, scris direct), discount=0', ;
Found() AND cantitate == 4 AND pret == 16 AND discount_unitar == 0, TRANSFORM(cantitate) + '/' + TRANSFORM(pret)
DO asserteaza WITH 'B1 modificare 800: valoare recalculata prin dummyform (4*16=64)', valoare == 64, TRANSFORM(valoare)
LOCATE FOR id_articol = 900
DO asserteaza WITH 'B1 adaugare 900: linie noua in tvd, cantitate=1, pret=50, pret_cu_tva=1, cont/gestiune din trul', ;
Found() AND cantitate == 1 AND pret == 50 AND pret_cu_tva == 1 AND Alltrim(cont) == '371' AND id_gestiune == 2, ''
DO asserteaza WITH 'B1: dummyform.AdaugaLinieTvdDinArticol apelat o singura data', loForm.nAdaugaCalls == 1, TRANSFORM(loForm.nAdaugaCalls)
DO asserteaza WITH 'B1: dummyform.calculeaza_valori_articol apelat (direct pe 800 + indirect din AdaugaLinieTvdDinArticol pe 900)', loForm.nCalcCalls == 2, TRANSFORM(loForm.nCalcCalls)
SELECT tvd
LOCATE FOR id_articol = 1000
DO asserteaza WITH 'B1: articolul 1000 (semnalare) neatins', Found() AND cantitate == 1 AND pret == 1, ''
LOCATE FOR id_articol = 1100
DO asserteaza WITH 'B1: articolul 1100 (nestocat, N-A) neatins', Found() AND cantitate == 1 AND pret == 1, ''
*!* --- B2: fara toForm - Modificare pe tvd tot se aplica (REPLACE simplu), Adaugare se sare fara eroare ---
CreeazaTvdTest()
CreeazaTrulTest()
AdaugaTvd(800, 2, 10, 1, 1, 'ARTICOL 800', 'COD800', 20)
AdaugaTrul(800, 4, 0, 16, 0, 1, '371', 1.19)
AdaugaTrul(900, 1, 0, 50, 0, 2, '371', 1.19)
LOCAL lnReccountTvdB2, lnAplicateB2
lnReccountTvdB2 = Reccount('tvd')
lnAplicateB2 = AplicaSincronizareArticole('RUL_SURSA')
DO asserteaza WITH 'B2 fara toForm: doar 1 linie aplicata (modificarea 800; adaugarea 900 nu are cum, fara toForm)', lnAplicateB2 == 1, TRANSFORM(lnAplicateB2)
DO asserteaza WITH 'B2 fara toForm: tvd nu a crescut (adaugarea 900 s-a sarit)', Reccount('tvd') == m.lnReccountTvdB2, TRANSFORM(Reccount('tvd'))
SELECT tvd
LOCATE FOR id_articol = 800
DO asserteaza WITH 'B2 fara toForm: REPLACE-ul de baza tot s-a facut pe 800 (cantitate=4, pret=16)', Found() AND cantitate == 4 AND pret == 16, ''
*!* --- B3: pret_cu_tva=0 pe randul tinta - pretul propus (mereu cu TVA) se converteste inainte de scriere ---
CreeazaTvdTest()
CreeazaTrulTest()
*!* proc_tvav=1.19, pret fara tva=10 -> valoare = 10*1.19 = 11.9 la incarcare; RUL propune pret cu tva 23.8 -> se scrie pret fara tva 20 (23.8/1.19)
AdaugaTvd(1200, 1, 10, 0, 1, 'ARTICOL 1200', 'COD1200', 11.9)
SELECT tvd
REPLACE proc_tvav WITH 1.19 IN tvd
AdaugaTrul(1200, 2, 0, 23.8, 0, 1, '371', 1.19)
AplicaSincronizareArticole('RUL_SURSA', loForm)
SELECT tvd
LOCATE FOR id_articol = 1200
DO asserteaza WITH 'B3 pret_cu_tva=0 pastrat: pret scris = pret_nou_cu_tva / proc_tvav = 23.8/1.19 = 20', ;
Found() AND pret_cu_tva == 0 AND Round(pret,4) == 20.0000, TRANSFORM(pret)
*============================================================================================
* SECTIUNEA C - AplicaSincronizareArticole, directia ARTICOLE_SURSA (scrie in trul)
*============================================================================================
CreeazaTvdTest()
CreeazaTrulTest()
*!* 1300: modificare, campul cu cantitate era 'cant' -> ramane 'cant' dupa aplicare
AdaugaTvd(1300, 5, 12, 1, 1, 'ARTICOL 1300', 'COD1300', 60)
AdaugaTrul(1300, 3, 0, 9, 0, 1, '371', 1)
*!* 1400: modificare, campul cu cantitate era 'cante' -> ramane 'cante' dupa aplicare
AdaugaTvd(1400, 5, 12, 1, 1, 'ARTICOL 1400', 'COD1400', 60)
AdaugaTrul(1400, 0, 3, 9, 0, 1, '371', 1)
*!* 1500: adaugare in ARTICOLE_SURSA (doar in tvd) - nu se aplica niciodata, trul nu primeste rand nou
AdaugaTvd(1500, 1, 1, 1, 1, 'ARTICOL 1500', 'COD1500', 1)
LOCAL lnReccountTrulInainte, lnAplicateC1
lnReccountTrulInainte = Reccount('trul')
lnAplicateC1 = AplicaSincronizareArticole('ARTICOLE_SURSA')
DO asserteaza WITH 'C1 ARTICOLE_SURSA: 2 linii aplicate (modificarile 1300 si 1400; adaugarea 1500 sarita)', lnAplicateC1 == 2, TRANSFORM(lnAplicateC1)
DO asserteaza WITH 'C1: trul nu a crescut (adaugarea 1500 nu creeaza rand RUL nou)', Reccount('trul') == m.lnReccountTrulInainte, TRANSFORM(Reccount('trul'))
SELECT trul
LOCATE FOR id_articol = 1300
DO asserteaza WITH 'C1 1300: cant (campul folosit initial) actualizat la 5, pretvtva la 12, id_tip_rulaj neatins', ;
Found() AND cant == 5 AND cante == 0 AND pretvtva == 12 AND id_tip_rulaj == 0, TRANSFORM(cant) + '/' + TRANSFORM(cante)
LOCATE FOR id_articol = 1400
DO asserteaza WITH 'C1 1400: cante (campul folosit initial) actualizat la 5, cant ramane 0', ;
Found() AND cante == 5 AND cant == 0 AND pretvtva == 12, TRANSFORM(cant) + '/' + TRANSFORM(cante)
STRTOFILE('REZULTAT: ' + TRANSFORM(gnPass) + ' PASS / ' + TRANSFORM(gnFail) + ' FAIL' + CHR(13) + CHR(10), lcLog, 1)
IF Used('tvd')
Use In tvd
ENDIF
IF Used('trul')
Use In trul
ENDIF
IF Used('tvanz')
Use In tvanz
ENDIF
IF Used('propunere_sincronizare')
Use In propunere_sincronizare
ENDIF
QUIT
*!* --- helpere de constructie a cursoarelor de test ---
PROCEDURE CreeazaTvdTest
CreeazaCursorArticoleGol('tvd')
ENDPROC
PROCEDURE CreeazaTrulTest
IF Used('trul')
Use In trul
ENDIF
CREATE CURSOR trul (id_articol I, denumire C(100), codmat C(30), um C(10), cant N(12,3), cante N(12,3), ;
pretvtva N(14,4), id_tip_rulaj I, sters I, id_gestiune I, cont C(20), proc_tvav N(6,4))
ENDPROC
*!* adauga o linie in tvd, cu valoarea data explicit (nu recalculata aici - fixture, nu logica de productie)
PROCEDURE AdaugaTvd
LPARAMETERS tnIdArticol, tnCantitate, tnPret, tnPretCuTva, tnInStoc, tcDenumire, tcCodmat, tnValoare
SELECT tvd
APPEND BLANK
REPLACE id_vanzare WITH 1, id_vanzare_det WITH Recno('tvd'), id_articol WITH m.tnIdArticol, ;
cantitate WITH m.tnCantitate, pret WITH m.tnPret, pret_cu_tva WITH m.tnPretCuTva, proc_tvav WITH 1, ;
discount_unitar WITH 0, sters WITH 0, denumire WITH m.tcDenumire, codmat WITH m.tcCodmat, ;
in_stoc WITH m.tnInStoc, valoare WITH m.tnValoare, lmodificat WITH .F. IN tvd
ENDPROC
*!* adauga o linie in trul: id_articol, cant, cante, pretvtva, id_tip_rulaj, id_gestiune, cont, proc_tvav
*!* (denumire/codmat generate automat, neverificate individual in teste)
PROCEDURE AdaugaTrul
LPARAMETERS tnIdArticol, tnCant, tnCante, tnPretvtva, tnIdTipRulaj, tnIdGestiune, tcCont, tnProcTvav
SELECT trul
APPEND BLANK
REPLACE id_articol WITH m.tnIdArticol, cant WITH m.tnCant, cante WITH m.tnCante, pretvtva WITH m.tnPretvtva, ;
id_tip_rulaj WITH m.tnIdTipRulaj, sters WITH 0, um WITH 'BUC', id_gestiune WITH m.tnIdGestiune, ;
cont WITH m.tcCont, proc_tvav WITH m.tnProcTvav, denumire WITH 'ARTICOL ' + Transform(m.tnIdArticol), ;
codmat WITH 'COD' + Transform(m.tnIdArticol) IN trul
ENDPROC
PROCEDURE asserteaza
LPARAMETERS tcNume, tlConditie, tcValoare
IF m.tlConditie
gnPass = gnPass + 1
STRTOFILE('PASS ' + m.tcNume + CHR(13) + CHR(10), gcErrLog, 1)
ELSE
gnFail = gnFail + 1
STRTOFILE('FAIL ' + m.tcNume + ' (valoare: ' + m.tcValoare + ')' + CHR(13) + CHR(10), gcErrLog, 1)
ENDIF
ENDPROC
PROCEDURE test_error_handler
LPARAMETERS tnError, tcMessage, tcProgram, tnLineNo
STRTOFILE('EROARE ' + TRANSFORM(tnError) + ' [' + tcProgram + ':' + TRANSFORM(tnLineNo) + '] ' + tcMessage + CHR(13) + CHR(10), gcErrLog, 1)
ENDPROC
*!* test-double pentru frm_modific2024 - oglindeste EXACT calculeaza_valori_articol/AdaugaLinieTvdDinArticol/
*!* ActualizeazaBaraTotaluri din omodificari.vc2 (13130+/13447+), ca sa verifice ca AplicaSincronizareArticole
*!* apeleaza metodele corecte cu argumentele corecte, fara sa deschida formularul real. Document RON in toate
*!* testele (tip_valuta=0, curs=1, multiplicator=1) - documentele in valuta sunt N-A la nivelul propunerii
DEFINE CLASS dummyform AS Custom
nCalcCalls = 0
nAdaugaCalls = 0
nBaraCalls = 0
PROCEDURE calculeaza_valori_articol
LOCAL lnCantitate, lnPretNet, lnProcTvav, lnValoare
This.nCalcCalls = This.nCalcCalls + 1
SELECT tvd
lnCantitate = Nvl(cantitate,0)
lnPretNet = Nvl(pret,0) - Nvl(discount_unitar,0)
lnProcTvav = Nvl(proc_tvav,1)
lnValoare = Round(m.lnCantitate * Iif(Nvl(pret_cu_tva,0) = 0, m.lnPretNet * m.lnProcTvav, m.lnPretNet), gnPC)
REPLACE lmodificat WITH .T., valoare WITH m.lnValoare IN tvd
This.ActualizeazaBaraTotaluri()
ENDPROC
PROCEDURE AdaugaLinieTvdDinArticol
LPARAMETERS toArticol
LOCAL lnPretRon, lnDiscountRon, lnPret, lnDiscount
This.nAdaugaCalls = This.nAdaugaCalls + 1
lnPretRon = Iif(toArticol.preturi_cu_tva = 0, toArticol.pretftva, toArticol.pretctva)
lnDiscountRon = Iif(toArticol.preturi_cu_tva = 0, toArticol.discount_unitar, toArticol.discount_unitar_ctva)
lnPret = m.lnPretRon * 1 / 1
lnDiscount = m.lnDiscountRon * 1 / 1
SELECT tvd
APPEND BLANK
REPLACE id_vanzare WITH 1, id_vanzare_det WITH 0, id_articol WITH toArticol.id_articol, ;
cantitate WITH toArticol.cantitate, pret WITH m.lnPret, pret_cu_tva WITH toArticol.preturi_cu_tva, ;
proc_tvav WITH toArticol.proc_tvav, discount_unitar WITH m.lnDiscount, ;
id_gestiune WITH toArticol.id_gestiune, cont WITH toArticol.Cont, sters WITH 0, ;
denumire WITH toArticol.denumire, codmat WITH toArticol.codmat, ;
pret_achizitie WITH Nvl(toArticol.pret_achizitie,0), lmodificat WITH .T. IN tvd
This.calculeaza_valori_articol()
ENDPROC
PROCEDURE ActualizeazaBaraTotaluri
This.nBaraCalls = This.nBaraCalls + 1
ENDPROC
ENDDEFINE