diff --git a/programe/ofacturare_editare.prg b/programe/ofacturare_editare.prg index d6d3c51..b05b62c 100644 --- a/programe/ofacturare_editare.prg +++ b/programe/ofacturare_editare.prg @@ -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 diff --git a/utile/Teste/editare_factura/test_s4b_sincronizare.prg b/utile/Teste/editare_factura/test_s4b_sincronizare.prg new file mode 100644 index 0000000..822bd56 --- /dev/null +++ b/utile/Teste/editare_factura/test_s4b_sincronizare.prg @@ -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