# Cercetare: rulaje (RUL) pe factura emisă din aviz (tip = 4) — `test_s8_matrice_surse.prg:308` ## Verdict **Asserția e greșită pentru tip = 4.** Tabela `RUL` reține exclusiv mișcări pe conturi de stoc (`371` în datele verificate); o factură emisă din aviz nu atinge niciodată contul de stoc — ea doar transformă creanța provizorie (`418`) în creanță fermă (`4111`) și TVA neexigibilă în TVA colectată (`4428`→`4428`) — pentru că marfa a ieșit deja din gestiune la avizul-sursă (tip 22), care e cel ce scrie rândurile de `RUL`. `Reccount(trul) = 0` pe un document de tip 4 e starea corectă, nu un semn că editarea a distrus rulaje. --- ## Ce am verificat la sursă (cod + date Oracle) ### 1. De unde se umple `trul` `IncarcaCursoareModificareNota` (`COMUN\programe\ofacturare_editare.prg:35-148`) încarcă `trul` direct din view-ul `vrul_tot`, filtrat pe `an`/`luna`/`cod`, fără nicio sinteză sau completare: ``` COMUN\programe\ofacturare_editare.prg:76 lcSql = [select * from vrul_tot where ] + lcConditieSters + [ an = ] + Transform(m.tnAn) + [ and luna = ] + Transform(m.tnLuna) + [ and cod = ] + Alltrim(Str(m.tnCod)) + [ order by id_rul] ``` `Reccount(trul)` e deci exact numărul de rânduri deja existente în `RUL` (server) pentru acel `cod` — nu ceva calculat sau garantat nenul de vreo regulă de business în client. ### 2. Cine scrie rulajele — și diferența pe tip de document Scrierea efectivă e server-side, în Oracle, `PACK_CONTAFIN.SCRIE_IN_RUL` (`COMUN\docs\PACK_CONTAFIN.pck:1896-2019`): face `INSERT INTO RUL (...) SELECT ... FROM RUL_TEMP` — deci **scrie exact ce a fost pus în `RUL_TEMP` de partea VFP**, fără vreo ramură condiționată de tipul documentului. Tot ce contează e dacă `RUL_TEMP` (populat din `trul`, deci din `vrul_tot` deja existent) are rânduri. Pe partea VFP, calea de editare a facturii (cea exercitată de test, prin `frm_facturi.do_editare_factura`, `COMUN\clase\ofacturare_comun.vc2:3799-3821`) copiază necondiționat `trul` înapoi în `RUL_TEMP` și apelează `OSCRIE_IN_FISIERE(0,.T.,.T.)` — al treilea parametru (echivalentul lui `llRul` din editorul generic) e **hardcodat `.T.`**, nu depinde de starea inițială: ``` COMUN\clase\ofacturare_comun.vc2:3811-3821 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 ... lnSucces = OSCRIE_IN_FISIERE(0,.T.,.T.) ``` Deci: dacă `trul` a fost gol la încărcare (pasul 1), rămâne gol și la scriere — codul **conservă** starea, nu o corectează și nu o strică. (Contrast: editorul generic de jurnal contabil, `afisjurcom.do_modifica`, `COMUN\clase\comun.vc2:2370-2482`, are un flag explicit `llRul` setat doar dacă `Reccount(lcCursor)>0` la încărcarea inițială din `vrul_tot` — dar calea de factură nu folosește acest flag deloc, e mereu `.T.`, ceea ce confirmă din nou că absența rândurilor din `RUL` nu e tratată ca eroare de nicio ramură a codului.) Originea rândurilor din `RUL` e deci strict la **crearea/finalizarea notei**, nu la editare — `PACK_CONTAFIN.finalizeaza_modificare_nota` (apelat și la creare, și la editare) doar re-scrie ce primește; nu am găsit nicio ramură condiționată de `tip`/`ntip` care să decidă "acest tip de document trebuie să aibă rulaje" — decizia e implicită, dată de **ce conturi apar în notă**, nu de tipul documentului. ### 3. Rolul lui `IN_STOC` în `ActualizeazaVerdictActRul` `COMUN\clase\omodificari.vc2:13045-13146`. Verdictul ACT/RUL are trei stări, toate **informative, niciodată eroare**: ``` COMUN\clase\omodificari.vc2:13129-13139 DO CASE CASE !m.llVerdictSigur lcVerdict = 'Verdict ACT/RUL: nu se aplica (informativ, date de import)' CASE Abs(This.nTotalActRon - This.nTotalRulRon) <= 0.02 lcVerdict = 'Verdict ACT/RUL: sincronizat (informativ)' OTHERWISE lcVerdict = 'Verdict ACT/RUL: divergent (informativ, nu e eroare)' ENDCASE ``` `in_stoc` corectează doar **suma** comparată (exclude valoarea liniilor nestocate din articolele facturii, convertită în RON, din totalul RUL — `tvd` unde `Nvl(in_stoc,1) = 0`, linia 13111), nu decide dacă documentul "trebuie" să aibă rulaje. Nu există în această metodă (verificat integral, liniile 13045-13146, nu doar grep) nicio ramură specifică pentru `RUL = 0` pe tip 4 — cazul cade pur și simplu în "divergent (informativ, nu e eroare)" dacă `nTotalActRon <> 0` și `nTotalRulRon = 0`, ceea ce confirmă că design-ul acceptă explicit acest caz ca non-eroare. ### 4. Verificare pe date, read-only, Oracle `MARIUSM_AUTO`/`ROA_CENTRAL` Conectat cu succes (`sqlplus.exe`, `SET TRANSACTION READ ONLY` ... `ROLLBACK`, fișier `.sql` ASCII, fără pipe). Cod-urile curente (verificate din `VANZARI`, nu presupuse): | id_vanzare | tip | cod (curent) | sters | RUL (cnt activ) | ACT (cnt activ) | |---|---|---|---|---|---| | 1048 | 1 | 1140918 | 0 | 1 | 5 | | 1052 | 22 (aviz) | 1140921 | 0 | 4 | 12 | | 1054 | 4 (factură din aviz) | **1140923** | 0 | **0** | 2 | | 1055 | 2 | 1140920 | 0 | 1 | 7 | `id_vanzare = 1054` are `cod` curent `1140923` (confirmă realocarea din test: `1140910 -> 1140922 -> 1140923`, citită direct din `VANZARI`, nu presupusă). Detaliu decisiv — conturile efective din `ACT`/`RUL` pentru perechea aviz→factură (avizul 1052 e cel mai probabil sursă a facturii 1054, pe baza secvenței de cod-uri și a fluxului tip 22→tip 4): ``` ACT pe avizul 1052 (tip 22): scd/scc includ 607/371, 371/378, 378/371, 4428/371, 371/4428, 418/704, 418/4428 RUL pe avizul 1052 (tip 22): 4 rânduri, toate CONT = 371 (cont de stoc) ACT pe factura 1054 (tip 4): scd/scc = 4111/418 (305 lei) și 4428/4428 (52.93 lei) -- NICIUN cont 371 RUL pe factura 1054 (tip 4): 0 rânduri ``` Avizul e cel care mișcă efectiv contul de stoc (`371`) și de aceea are rânduri în `RUL` (care urmărește exclusiv cantități pe conturi de stoc — vezi și `cant`/`cante` din `trul`, folosite ca atare în `ActualizeazaVerdictActRul:13102`). Factura emisă din acel aviz nu mai atinge `371` deloc — transformă doar creanța provizorie (`418`) în creanță fermă (`4111`) și TVA neexigibilă (`4428`) în TVA colectată (`4428`). N-are, structural, ce rând de stoc să scrie. Zero tranzacții deschise la final (`ROLLBACK` executat, `SET TRANSACTION READ ONLY` respectat pe tot scriptul). ## Ce am dedus (nu verificat direct, dar consistent cu toate dovezile de mai sus) - Regula generală: `RUL` se scrie doar pentru documentele/notele care conțin mișcare pe cont de stoc (aviz, NIR, bonuri de consum etc.); facturile "de închidere" (emise din aviz, sau orice document care doar transformă o creanță provizorie într-una fermă) nu vor avea niciodată rânduri în `RUL`, indiferent câte documente de acest fel verifici — nu e o particularitate a lui `id_vanzare = 1054`, ci a tipului de operațiune contabilă. - Aserția de la `test_s8_matrice_surse.prg:308` (`Reccount(trul) > 0` obligatoriu după editare) e probabil corectă ca test general "rulajele nu trebuie distruse de editare" pentru documentele care AU avut rulaje înainte de editare, dar e o presupunere greșită aplicată universal — pentru tip 4 (și, prin extensie, orice tip de document care nu atinge cont de stoc) condiția trebuie relaxată la "dacă documentul avea rulaje înainte, tot le are și după" sau pur și simplu exclusă pentru tip = 4. ## Fișiere atinse Niciunul (misiune read-only). Fișiere citite: `COMUN\programe\ofacturare_editare.prg`, `COMUN\clase\omodificari.vc2`, `COMUN\clase\comun.vc2`, `COMUN\clase\ofacturare_comun.vc2`, `COMUN\docs\PACK_CONTAFIN.pck`. Interogări Oracle read-only pe `MARIUSM_AUTO`@`ROA_CENTRAL`, cu `ROLLBACK` la final.