#6 editare factura emisa: articolele nu se mai editeaza pe facturi trimise in eFactura

Cand documentul e deja trimis in eFactura, pagina de articole ramane vizibila,
dar needitabila: cantitate, pret, pret achizitie, flagul pret cu TVA, discountul
de antet, plus adaugarea si stergerea de linii. Alegerea e "vizibil, dar blocat",
nu "pagina ascunsa" - contabilul trebuie sa vada ce contine factura trimisa.

- omodificari.vc2: flag lArticoleReadOnly calculat in frm_modific2024.Show, impins
  peste .When-urile de celula, peste Enabled si peste garda din Click-ul butoanelor,
  si peste txtDiscountArt.ReadOnly. Eticheta lblArticoleReadOnly explica motivul.
- ofacturare_editare.prg: id_fact adus pe tvanz, in IncarcaVanzareNota si in
  CreeazaCursorTvanzGol. EsteInEFactura interogheaza anaf_efactura dupa id_fact,
  nu dupa id_vanzare - fara asta garda nu s-ar fi declansat niciodata pe date reale.
- teste: test_efactura_readonly (headless, garda in ambele sensuri),
  test_ui_efactura_readonly (formular vizibil - coloanele gridului nu se
  materializeaza sub -A -T), test_s7_rotunjire, si test_s8_matrice_surse pentru
  matricea S8 pe tipuri de sursa.

S8 rulat pe cate un document din fiecare tip de sursa (lista de preturi, contract,
aviz, factura din aviz), fiecare editat din ambele puncte de intrare: notele vechi
raman STERS=1, id_fact nu se schimba, totalurile si liniile raman coerente.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SN8snvkk94KuhWwoXUUey3
This commit is contained in:
2026-08-10 23:27:26 +03:00
parent 1c42ae0af4
commit 2dfbef40dc
6 changed files with 1555 additions and 10 deletions

View File

@@ -0,0 +1,349 @@
* test_s7_rotunjire.prg
* S7 - rotunjirea la reeditare: verifica_total_document (PACK_FACTURARE.pck) insereaza o linie de
* corectie in ACT_TEMP cand totalul documentului difera de suma din note. Criteriul din plan:
* trei editari consecutive nu trebuie sa lase trei linii de corectie in nota activa.
*
* Reproduce TREI reeditari consecutive, fara nicio modificare de continut (acelasi tipar ca
* trecerea 2 din test_s5_scriere_reala.prg, repetat de trei ori), pe acelasi document
* id_vanzare = 1049. Fiecare trecere ruleaza lantul real (OSCRIE_IN_FISIERE(2)/(0),
* finalizeaza_modificare_nota, ScrieArticoleFacturaEditate), in tranzactie proprie, cu COMMIT.
*
* Dupa fiecare COMMIT, numara in ACT (nu ACT_TEMP - GTT SYS$TRANSACTION, golita la fiecare COMMIT):
* - randurile active (STERS=0) pe COD-ul curent al documentului;
* - liniile de corectie: perechi (scd,scc,nract,dataact,explicatia) cu 2+ randuri si suma diferita
* - semnatura exacta a INSERT-ului din verifica_total_document (PACK_FACTURARE.pck:16353-16460,
* 16467-16574), confirmata pe un caz real din productie (id_fact=8008816, cod=1140709).
*
* Harness, nu UI: acelasi tipar ca test_s5_scriere_reala.prg (buton=1 fortat, do_deschide/inchide
* tranzactie reproduse inline). CONSUMA DATE DE TEST: VANZARI.COD se realoca la fiecare trecere.
* Nu modifica cod de aplicatie, nu atinge .PJX/.exe. QUIT la final.
SET SAFETY OFF
SET TALK OFF
LOCAL lcLog
lcLog = 'D:\ROA\ROAFACTURARE\COMUN\utile\Teste\editare_factura\test_s7_rotunjire_log.txt'
STRTOFILE('start ' + TTOC(DATETIME()) + CHR(13) + CHR(10), lcLog)
PUBLIC gcErrLog, gcTestLog, gnPass, gnFail
gcErrLog = lcLog
gcTestLog = lcLog
gnPass = 0
gnFail = 0
ON ERROR DO test_error_handler WITH ERROR(), MESSAGE(), PROGRAM(), LINENO()
SET PROCEDURE TO D:\ROA\ROAFACTURARE\COMUN\utile\Teste\mock_amessagebox.prg ADDITIVE
DO ('D:\ROA\ROAFACTURARE\COMUN\utile\Teste\test_init_env_auto.prg') WITH 'CENTRAL', 'MARIUSM_AUTO', 'ROMFASTSOFT'
IF gnHandle <= 0
STRTOFILE('EROARE: conectare esuata' + CHR(13) + CHR(10), lcLog, 1)
QUIT
ENDIF
SET PROCEDURE TO D:\ROA\ROAFACTURARE\COMUN\programe\ofacturare_editare.prg ADDITIVE
SET PROCEDURE TO D:\ROA\ROAFACTURARE\COMUN\programe\oscrie_in_fisiere.prg ADDITIVE
SET PROCEDURE TO D:\ROA\ROAFACTURARE\COMUN\programe\odocumente.prg ADDITIVE
IF TYPE('gnPC') <> 'N'
PUBLIC gnPC
gnPC = 2
ENDIF
PUBLIC gcMockUltimMesaj, gnMockUltimTip
gcMockUltimMesaj = ''
gnMockUltimTip = 0
PUBLIC gnIdVanzareTest
gnIdVanzareTest = 1049
STRTOFILE('gnAn=' + TRANSFORM(gnAn) + ' gnLuna=' + TRANSFORM(gnLuna) + ' gnIdUtil=' + TRANSFORM(gnIdUtil) + CHR(13) + CHR(10), lcLog, 1)
DO ruleaza_trecere_s7 WITH gnIdVanzareTest, 1, lcLog
DO ruleaza_trecere_s7 WITH gnIdVanzareTest, 2, lcLog
DO ruleaza_trecere_s7 WITH gnIdVanzareTest, 3, lcLog
STRTOFILE(CHR(13) + CHR(10) + 'REZULTAT: ' + TRANSFORM(gnPass) + ' PASS / ' + TRANSFORM(gnFail) + ' FAIL' + CHR(13) + CHR(10), lcLog, 1)
STRTOFILE('done ' + TTOC(DATETIME()) + CHR(13) + CHR(10), lcLog, 1)
QUIT
PROCEDURE asserteaza
LPARAMETERS tcNume, tlConditie, tcValoare
IF m.tlConditie
gnPass = gnPass + 1
STRTOFILE('PASS ' + m.tcNume + ' [' + m.tcValoare + ']' + CHR(13) + CHR(10), gcTestLog, 1)
ELSE
gnFail = gnFail + 1
STRTOFILE('FAIL ' + m.tcNume + ' [' + m.tcValoare + ']' + CHR(13) + CHR(10), gcTestLog, 1)
ENDIF
ENDPROC
PROCEDURE ExecSel
LPARAMETERS tcSql, tcCursor
LOCAL lnQ
IF USED(tcCursor)
USE IN (tcCursor)
ENDIF
lnQ = goExecutor.oExecute(tcSql, tcCursor)
RETURN lnQ
ENDPROC
*-- numara, pentru cod-ul dat, randurile active si liniile de corectie (semnatura verifica_total_document)
PROCEDURE NumaraCorectii
LPARAMETERS tnCod, tnAn, tnLuna, tcLog
LOCAL lcSql, lnQ, lnRanduriActive, lnLiniiCorectie
lcSql = [select count(*) as nr from act where cod = ] + TRANSFORM(tnCod) + ;
[ and an = ] + TRANSFORM(tnAn) + [ and luna = ] + TRANSFORM(tnLuna) + [ and sters = 0]
lnQ = ExecSel(lcSql, 'v_nract')
lnRanduriActive = IIF(lnQ > 0, v_nract.nr, -1)
IF USED('v_nract')
USE IN v_nract
ENDIF
*-- semnatura verifica_total_document (PACK_FACTURARE.pck:16353-16460, 16467-16574): un rand nou
*-- cu ACELASI (scd,scc,nract,dataact,explicatia) ca randul original, dar SUMA = diferenta de
*-- rotunjire, deci mica (delta < 5) si diferita de suma originala - nu orice pereche duplicata
*-- de (scd,scc): documentul poate avea legitim doua linii de detaliu cu acelasi cont contabil
*-- (delta mare sau delta = 0 intre ele, verificat pe 1140900-1140903 - fals pozitiv prins la runda 1)
lcSql = [select count(*) as nr from ( ] + ;
[ select scd, scc, nract, dataact, explicatia, count(*) c, min(suma) smin, max(suma) smax ] + ;
[ from act where cod = ] + TRANSFORM(tnCod) + [ and an = ] + TRANSFORM(tnAn) + ;
[ and luna = ] + TRANSFORM(tnLuna) + [ and sters = 0 ] + ;
[ group by scd, scc, nract, dataact, explicatia ] + ;
[ having count(*) > 1 and max(suma) <> min(suma) and abs(max(suma) - min(suma)) < 5) ]
lnQ = ExecSel(lcSql, 'v_ncor')
lnLiniiCorectie = IIF(lnQ > 0, v_ncor.nr, -1)
IF USED('v_ncor')
USE IN v_ncor
ENDIF
STRTOFILE(' cod=' + TRANSFORM(tnCod) + ' randuri active in ACT=' + TRANSFORM(lnRanduriActive) + ;
' linii de corectie (semnatura verifica_total_document)=' + TRANSFORM(lnLiniiCorectie) + CHR(13) + CHR(10), tcLog, 1)
RETURN lnLiniiCorectie
ENDPROC
PROCEDURE ruleaza_trecere_s7
LPARAMETERS tnIdVanzare, tnNrTrecere, tcLog
PRIVATE pnAn, pnLuna, lnCod, lnIdFact
LOCAL lnSters, llEProforma, lnIdSet, lnIdFactD, lnSucces, llGasitRand
LOCAL lcSql, lnQ, lnCodInainte, lnCodDupa, lnLiniiCorectie
STRTOFILE(CHR(13) + CHR(10) + '========== TRECEREA ' + TRANSFORM(tnNrTrecere) + ' - reeditare fara nicio modificare (id_vanzare=' + ;
TRANSFORM(tnIdVanzare) + ') ==========' + CHR(13) + CHR(10), tcLog, 1)
lcSql = [select cod, sters, id_fact, eproforma, ] + ;
[extract(year from data_act) as anact, extract(month from data_act) as lunaact ] + ;
[from vanzari where id_vanzare = ] + TRANSFORM(tnIdVanzare)
lnQ = ExecSel(lcSql, 'v_vz_inainte')
IF lnQ <= 0 OR RECCOUNT('v_vz_inainte') <> 1
DO asserteaza WITH 'trecerea ' + TRANSFORM(tnNrTrecere) + ': VANZARI citit', .F., 'lnQ=' + TRANSFORM(lnQ)
RETURN
ENDIF
SELECT v_vz_inainte
lnCod = cod
lnSters = sters
lnIdFact = id_fact
llEProforma = (NVL(eproforma, 0) = 1)
pnAn = anact
pnLuna = lunaact
lnCodInainte = cod
USE IN v_vz_inainte
STRTOFILE(' VANZARI inainte: cod=' + TRANSFORM(lnCod) + ' sters=' + TRANSFORM(lnSters) + ' id_fact=' + TRANSFORM(lnIdFact) + CHR(13) + CHR(10), tcLog, 1)
IF lnSters = 1 OR llEProforma
DO asserteaza WITH 'trecerea ' + TRANSFORM(tnNrTrecere) + ': document editabil (nesters, neproforma)', .F., 'sters=' + TRANSFORM(lnSters)
RETURN
ENDIF
IF (pnAn*12) + pnLuna <> (gnAn*12) + gnLuna
DO asserteaza WITH 'trecerea ' + TRANSFORM(tnNrTrecere) + ': document din luna curenta', .F., TRANSFORM(pnAn) + '/' + TRANSFORM(pnLuna)
RETURN
ENDIF
IF ReferinteDocumenteNota(pnAn, pnLuna, lnCod) OR EsteInEFactura(lnIdFact)
DO asserteaza WITH 'trecerea ' + TRANSFORM(tnNrTrecere) + ': document fara referinte / netrimis in eFactura', .F., 'blocat de garda'
RETURN
ENDIF
IF !IncarcaCursoareModificareNota(lnCod, pnAn, pnLuna, .F.)
DO asserteaza WITH 'trecerea ' + TRANSFORM(tnNrTrecere) + ': IncarcaCursoareModificareNota', .F., 'a esuat'
RETURN
ENDIF
IF RECCOUNT('actactan') = 0
DO asserteaza WITH 'trecerea ' + TRANSFORM(tnNrTrecere) + ': exista nota contabila', .F., 'Reccount(actactan)=0'
RETURN
ENDIF
SELECT actactan
llGasitRand = .F.
IF lnIdFact > 0
LOCATE FOR NVL(id_fact, 0) = lnIdFact
llGasitRand = FOUND()
ENDIF
IF !llGasitRand
GO TOP
lnIdFact = id_fact
ENDIF
lnIdSet = id_set
lnIdFactD = NVL(id_factd, 0)
SELECT a.*, IIF(NVL(id_jtva_coloana, 0) = 0, 0, 1) AS cu_Tva FROM tact a INTO CURSOR tact READWRITE
IF !IncarcaVanzareDinNota('tact')
DO asserteaza WITH 'trecerea ' + TRANSFORM(tnNrTrecere) + ': IncarcaVanzareDinNota', .F., 'nu a gasit randul din VANZARI'
RETURN
ENDIF
IncarcaArticoleFactura(tnIdVanzare, 'tvd')
*-- nicio modificare pe tvd - reeditare pura
buton = 1
lnSucces = 0
TRY
IF MyDeschideTranzactie()
SELECT actactan
lnSucces = OSCRIE_IN_FISIERE(2, .T., .T.)
STRTOFILE(' OSCRIE_IN_FISIERE(2,...) => ' + TRANSFORM(lnSucces) + CHR(13) + CHR(10), tcLog, 1)
IF lnSucces > 0
IF USED('actactan')
USE IN actactan
ENDIF
SELECT tact
REPLACE id_jtva_coloana WITH NULL, proc_tva WITH 0 FOR cu_Tva = 0
SELECT * FROM tact INTO CURSOR actactan READWRITE
REPLACE ALL id_util WITH gnIdUtil, sters WITH 0
IF USED('rul_temp')
USE IN rul_temp
ENDIF
SELECT * FROM trul INTO CURSOR RUL_TEMP READWRITE
REPLACE ALL id_util WITH gnIdUtil, sters WITH 0
IF USED('rul_temp_obinv')
USE IN rul_temp_obinv
ENDIF
SELECT * FROM trul_obinv INTO CURSOR RUL_TEMP_OBINV READWRITE
REPLACE ALL id_util WITH gnIdUtil, sters WITH 0
lnSucces = OSCRIE_IN_FISIERE(0, .T., .T.)
STRTOFILE(' OSCRIE_IN_FISIERE(0,...) => ' + TRANSFORM(lnSucces) + CHR(13) + CHR(10), tcLog, 1)
ENDIF
IF lnSucces > 0
lcSql = [begin pack_contafin.finalizeaza_modificare_nota(?pnLuna,?pnAn,Null,] + ALLTRIM(STR(lnIdSet)) + [,] + ;
ALLTRIM(STR(lnCod)) + [,] + ALLTRIM(STR(lnIdFact)) + [,] + ALLTRIM(STR(lnIdFactD)) + [,?gnIdUtil); end;]
lnSucces = IIF(goExecutor.oExecuta(lcSql), 1, -1)
STRTOFILE(' finalizeaza_modificare_nota => ' + TRANSFORM(lnSucces) + CHR(13) + CHR(10), tcLog, 1)
ENDIF
IF lnSucces > 0 AND "OFACTURARE_EDITARE" $ UPPER(SET("Procedure")) AND USED('tvanz') AND RECCOUNT('tvanz') = 1
lnSucces = IIF(ScrieArticoleFacturaEditate(tvanz.id_vanzare), 1, -1)
STRTOFILE(' ScrieArticoleFacturaEditate => ' + TRANSFORM(lnSucces) + CHR(13) + CHR(10), tcLog, 1)
ELSE
STRTOFILE(' ATENTIE: blocul ScrieArticoleFacturaEditate NU s-a executat' + CHR(13) + CHR(10), tcLog, 1)
ENDIF
IF MyInchideTranzactie(IIF(lnSucces < 0, 2, 1))
STRTOFILE(' tranzactie inchisa cu ' + IIF(lnSucces < 0, 'ROLLBACK', 'COMMIT') + CHR(13) + CHR(10), tcLog, 1)
ELSE
STRTOFILE(' FAIL la inchiderea tranzactiei' + CHR(13) + CHR(10), tcLog, 1)
ENDIF
ELSE
STRTOFILE(' FAIL: deschiderea tranzactiei a esuat' + CHR(13) + CHR(10), tcLog, 1)
ENDIF
CATCH TO loExc
STRTOFILE(' EXCEPTIE in lantul de scriere: ' + TRANSFORM(loExc.ErrorNo) + ' [' + loExc.Message + '] ' + ;
loExc.Procedure + ':' + TRANSFORM(loExc.LineNo) + ' -> ROLLBACK' + CHR(13) + CHR(10), tcLog, 1)
lnSucces = -99
MyInchideTranzactie(2)
ENDTRY
DO asserteaza WITH 'trecerea ' + TRANSFORM(tnNrTrecere) + ': lantul de scriere a mers pana la capat (lnSucces>0, COMMIT)', ;
lnSucces > 0, 'lnSucces=' + TRANSFORM(lnSucces)
IF USED('actactan')
USE IN actactan
ENDIF
IF USED('tact')
USE IN tact
ENDIF
IF USED('rul_temp')
USE IN rul_temp
ENDIF
IF USED('trul')
USE IN trul
ENDIF
IF USED('rul_temp_obinv')
USE IN rul_temp_obinv
ENDIF
IF USED('trul_obinv')
USE IN trul_obinv
ENDIF
IF USED('tvd')
USE IN tvd
ENDIF
IF USED('tvanz')
USE IN tvanz
ENDIF
IF lnSucces <= 0
STRTOFILE(' scrierea nu s-a produs - fara numarare de corectii' + CHR(13) + CHR(10), tcLog, 1)
RETURN
ENDIF
lcSql = [select cod from vanzari where id_vanzare = ] + TRANSFORM(tnIdVanzare)
lnQ = ExecSel(lcSql, 'v_vz_dupa')
lnCodDupa = IIF(lnQ > 0, v_vz_dupa.cod, -1)
IF USED('v_vz_dupa')
USE IN v_vz_dupa
ENDIF
STRTOFILE(' VANZARI.cod: ' + TRANSFORM(lnCodInainte) + ' -> ' + TRANSFORM(lnCodDupa) + CHR(13) + CHR(10), tcLog, 1)
lnLiniiCorectie = NumaraCorectii(lnCodDupa, pnAn, pnLuna, tcLog)
*-- criteriul din plan: trei editari consecutive nu lasa trei linii de corectie in nota ACTIVA.
*-- verifica_total_document insereaza cel mult 2 linii pe generatie (una pentru ftva, una pentru
*-- tva; a treia doar pentru ntip in (48,49), nu e cazul aici) - nota activa nu poate contine mai
*-- mult decat atat, indiferent de numarul de editari anterioare (blocul vechi e integral STERS=1).
DO asserteaza WITH 'trecerea ' + TRANSFORM(tnNrTrecere) + ': linii de corectie in nota activa nu creste cu numarul de treceri (<= 2)', ;
lnLiniiCorectie >= 0 AND lnLiniiCorectie <= 2, ;
'linii de corectie=' + TRANSFORM(lnLiniiCorectie) + ' dupa ' + TRANSFORM(tnNrTrecere) + ' treceri'
ENDPROC
PROCEDURE MyDeschideTranzactie
LOCAL llReturn, lnSuccesLocal
IF TYPE('goExecutor') = 'O'
goExecutor.oExecuta([select * from dual])
ENDIF
lnSuccesLocal = SQLSetprop(gnHandle, "Transactions", 2)
llReturn = IIF(lnSuccesLocal < 0, .F., .T.)
RETURN llReturn
ENDPROC
PROCEDURE MyInchideTranzactie
LPARAMETERS tnTip
LOCAL llReturn, lnSuccesLocal
IF tnTip = 1
lnSuccesLocal = SQLCOMMIT(gnHandle)
ELSE
lnSuccesLocal = SQLROLLBACK(gnHandle)
ENDIF
IF lnSuccesLocal < 0
llReturn = .F.
ELSE
lnSuccesLocal = SQLSetprop(gnHandle, "Transactions", 1)
llReturn = IIF(lnSuccesLocal < 0, .F., .T.)
ENDIF
RETURN llReturn
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