Files
roafacturare/docs/cercetare/rec_s7_rotunjire.md
Marius Mutu d9f5ca4226 docs: planurile, proiectarile si rapoartele de lucru intra in versionare
Folderul docs\ era pana acum in afara oricarui control de versiuni - nici git,
nici SVN - desi contine planurile pe puncte, proiectarile si rapoartele de
cercetare pe care se sprijina modificarile din cod. O stergere acolo era
definitiva.

Fisierele intermediare (handoff-uri intre sesiuni, diff-uri deja aplicate) au
fost sterse inainte, nu versionate: ce era durabil in ele a intrat in antetele
fisierelor de test la care se refereau.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SN8snvkk94KuhWwoXUUey3
2026-08-11 22:17:17 +03:00

205 lines
11 KiB
Markdown

# S7 — rotunjirea la reeditare. Rezultat
Cerinta din plan (`docs\plan_06_editare_factura.md:275-280`): `verifica_total_document` insereaza o
linie de corectie in `ACT_TEMP` cand totalul documentului difera de suma din note; *gata cand* trei
editari consecutive nu lasa trei linii de corectie.
## Verdict
**Nu se acumuleaza.** Mecanismul e marginit prin constructie: nota activa a documentului nu poate
contine niciodata mai mult decat o singura generatie de linii de corectie (cel mult 2, cate una
pentru fiecare din cele doua verificari ftva/tva), indiferent de cate ori a fost editat documentul
inainte. Nu e nevoie de reparatie — nu exista defect.
## Dovada pe cod
Sursa: `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` (fisierul
real aplicat in baza, nu un export `all_source` — capcana deja platita in S5, vezi
`docs\handoff_s5.md`).
**1. `ACT_TEMP` e tabela de lucru (scratch), golita automat la fiecare COMMIT/ROLLBACK.**
Verificat direct din dictionar, nu presupus:
```sql
select table_name, temporary, duration from all_tables where table_name in ('ACT_TEMP','ACT');
--> ACT N (tabela persistenta)
--> ACT_TEMP Y SYS$TRANSACTION (global temporary table, DURATION = SYS$TRANSACTION)
```
Un GTT cu `DURATION = SYS$TRANSACTION` se goleste singur la finalul TRANZACTIEI curente. Decizia 38
(`docs\handoff_s5.md`, deja testata cu scriere reala in S5) stabileste ca **fiecare editare ruleaza
intr-o singura tranzactie manuala, incheiata cu COMMIT** — deci `ACT_TEMP` porneste mereu **goala**
la inceputul fiecarei editari; nu poate purta randuri dintr-o editare anterioara.
**2. `verifica_total_document` se apeleaza o singura data pe generatie, imediat dupa reconstructia
notei.** `cumuleaza_note_act` (`:14070-14107`):
```
14078 UPDATE ACT_TEMP SET STERS = 1, TVA_INCASARE = ...
...
14092 pack_facturare.cumuleaza_note_act_temp();
14094 if pack_facturare.ntip <> pack_facturare.nTipNotaPlata then
14095 pack_facturare.verifica_total_document();
14096 end if;
```
`cumuleaza_note_act_temp` (`:14109-14319`) marcheaza tot ce e in `ACT_TEMP` (deci doar randurile
scrise in TRANZACTIA curenta, `ACT_TEMP` fiind goala la start) `STERS=1`, reconstruieste randurile
prin `GROUP BY` peste ele (`:14215-14312`) si sterge randurile pre-agregare (`:14318 DELETE FROM
ACT_TEMP WHERE STERS = 1`). Rezultatul, inainte de `verifica_total_document`, e un set de randuri
**agregate, cate unul pe combinatie `(scd,scc,...)`**, construit exclusiv din datele editarii
curente.
**3. Corectia insereaza cel mult un rand per verificare, pe baza totalurilor abia calculate.**
`verifica_total_document` (`:16276-16690`): calculeaza `V_TOTFTVA_VER`/`V_TOTTVA_VER` prin `SUM`
peste `ACT_TEMP` (fara filtrare pe `cod`, dar `ACT_TEMP` contine oricum o singura generatie — vezi
punctul 1), le compara cu `pack_facturare.ntotftva`/`ntottva` (setate mai devreme in acelasi flux)
si, la neconcordanta, insereaza UN rand nou, copiat dupa randul-ancora (`scd`,`ascd`,`scc`,`ascc`,
`explicatia`, etc.) cu `suma` = diferenta si `id_act = max(id_act)+1`:
```
16349 IF NVL(pack_facturare.ntotftva, 0) <> 0 and NVL(pack_facturare.ntotftva, 0) <> V_TOTFTVA_VER THEN
16352 pack_facturare.ndifftva := pack_facturare.ntotftva - V_TOTFTVA_VER;
16353 insert into act_temp (...) select b.id_act, ..., pack_facturare.ndifftva as suma, ...
16456 from act_temp a
16457 left join (select max(id_act) + 1 as id_act, 0 as sters from act_temp) b on a.sters = b.sters
16460 where a.id_act = V_ID_TOTFTVA;
16461 END IF;
16462 IF NVL(pack_facturare.ntottva, 0) <> 0 and NVL(pack_facturare.ntottva, 0) <> V_TOTTVA_VER THEN
-- acelasi tipar, insereaza a doua linie (verificarea de TVA)
16575 END IF;
16577 IF pack_facturare.ntip in (48, 49) AND (...) THEN
-- a treia linie, DOAR pentru tip 48/49 (nota de credit/debit) - nu e cazul facturii (tip=1)
16688 END IF;
```
Fiecare din cele doua/trei `IF`-uri e evaluat o singura data pe apel, deci **maximum 2 linii de
corectie pe generatie** (3 doar pentru `ntip in (48,49)`) — niciodata proportional cu numarul de
editari anterioare.
**4. Blocul vechi e retras integral la fiecare editare**, nu doar linia de corectie. `OSCRIE_IN_FISIERE(2)`
(sterge nota veche) + `actualizeaza_vanzari` marcheaza tot `cod`-ul vechi `STERS=1` — confirmat
empiric mai jos, pe 8 generatii reale. Deci nota **activa** contine mereu rezultatul unei singure
generatii, cu cel mult 2 linii de corectie proprii — niciodata suma corectiilor din generatii
anterioare.
## Dovada pe date, in tranzactie — trei editari consecutive
Suita noua: `COMUN\utile\Teste\editare_factura\test_s7_rotunjire.prg`
(log: `COMUN\utile\Teste\editare_factura\test_s7_rotunjire_log.txt`).
Acelasi lant real ca in S5 (`OSCRIE_IN_FISIERE(2)` → `OSCRIE_IN_FISIERE(0)` →
`finalizeaza_modificare_nota` → `ScrieArticoleFacturaEditate`), pe `id_vanzare = 1049`, trei treceri
consecutive, **fiecare in tranzactia ei proprie, cu COMMIT** — fara nicio modificare de continut
(reeditare pura, ca sa izoleze exact mecanismul `verifica_total_document`, nu efectele altor
modificari). Dupa fiecare COMMIT s-a citit `VANZARI.cod` (nou) si s-a numarat in `ACT`:
- randurile active (`STERS=0`) pe `cod`-ul curent;
- liniile de corectie, cu semnatura exacta a INSERT-ului de mai sus: aceeasi cheie
`(scd,scc,nract,dataact,explicatia)`, cel putin 2 randuri, sume **diferite** si cu diferenta
**mica** (`< 5`) — nu orice pereche `(scd,scc)` duplicata, care poate fi legitim generata de doua
linii de detaliu diferite ale documentului (vezi capcana de mai jos).
**Rezultat: 6 PASS / 0 FAIL.**
| Trecere | `cod` inainte -> dupa | randuri active `ACT` | linii de corectie |
|---|---|---|---|
| 1 | 1140903 -> 1140904 | 10 | **0** |
| 2 | 1140904 -> 1140905 | 10 | **0** |
| 3 | 1140905 -> 1140906 | 10 | **0** |
Totalurile documentului au ramas neschimbate (`747.96 / 157.06 / 905.02`), confirmand ca cele trei
treceri au fost reeditari pure, fara alta variabila.
**Limitare, consemnata explicit**: pe compozitia acestui document, `verifica_total_document` nu
declanseaza NICIODATA neconcordanta (`ntotftva = V_TOTFTVA_VER` exact, la toate cele 8 generatii
verificate — vezi mai jos). Cifra `0/0/0` arata ca randurile nu cresc, dar **„zero cazuri in date nu
e dovada"** — nu demonstreaza prin ea insasi ca, DACA s-ar declansa, corectiile nu s-ar acumula.
Pentru asta raspunsul vine din cod (sectiunea de mai sus) si din exemplul real de mai jos.
### Capcana prinsa in propria masuratoare — semnatura gresita la prima incercare
Prima versiune a interogarii de numarare (fara filtrul de diferenta mica) a gasit „3 linii de
corectie" la fiecare trecere — fals pozitiv. Documentul are legitim doua linii de detaliu active
(`det=1581`, `det=1583`) care genereaza fiecare propriile perechi `(371,378)`/`(378,371)`/`(4111,4428)`
in notă — duplicat structural normal, cu diferente mari intre sume (ex. `126.04`, `57.48`), nu
diferente de rotunjire. Corectat cu filtrul `abs(max(suma)-min(suma)) < 5 AND max(suma) <> min(suma)`
(interogare separata, `sqlplus`, verificat pe `cod` 1140900-1140903 dupa corectie: 0 randuri la toate
patru) inainte de rularea finala (mai sus). `NumaraCorectii` din `test_s7_rotunjire.prg` are acum
filtrul corect.
## Confirmare independenta: mecanismul chiar functioneaza in productie
Cautare in `ACT` (an=2026), dupa semnatura exacta a corectiei — cauta orice caz real, nu doar pe
documentul de test:
```sql
select cod, scd, scc, nract, dataact, count(*) nr, min(suma) smin, max(suma) smax
from act where an=2026 and sters=0 and id_fact is not null
group by cod, scd, scc, nract, dataact, explicatia
having count(*) = 2 and min(suma) <> max(suma) and abs(max(suma)-min(suma)) < 5;
```
Gasit: `cod=1140709` (`id_fact=8008816`, an=2026 luna=3) — `id_act=122905` (`scd=4428, scc=401,
suma=150`) si `id_act=122906` (acelasi `scd/scc/nract/dataact`, `suma=150.02`, diferenta `0.02`).
Corespunde exact tiparului INSERT-ului: `id_act` cel mai mare = `max(id_act)+1`, restul coloanelor
copiate de pe randul-ancora, `suma` = diferenta de rotunjire. **Acest document nu a fost editat
niciodata dupa** (o singura valoare `cod`), deci nu arata direct comportamentul la editari repetate —
dar confirma pe date reale ca mecanismul chiar insereaza, si insereaza **exact un rand** cand se
declanseaza, in acord cu structura codului (un `INSERT` per `IF`).
## Istoricul complet al documentului de test — randurile nu cresc
`id_vanzare=1049` / `id_fact=8009659` a fost editat de 8 ori pana acum (S5 + acest test), fiecare
generatie verificata in `ACT`:
| `cod` | randuri active la generare | linii de corectie |
|---|---|---|
| 1140887 | 10 | 0 |
| 1140896 | 10 | 0 |
| 1140897 | 10 | 0 |
| 1140898 | 10 | 0 |
| 1140900 | 10 | 0 |
| 1140904 | 10 | 0 |
| 1140905 | 10 | 0 |
| 1140906 (curent) | 10 | 0 |
Numarul de randuri e identic la fiecare generatie — regenerare completa, nu acumulare incrementala,
consistent cu punctul 4 din dovada pe cod (blocul vechi se retrage integral).
## Ce NU acopera cercetarea
- **Niciun caz gasit in productie unde corectia se declanseaza PE UN DOCUMENT editat de mai multe
ori** — cautarea (an=2026, semnatura stricta) a gasit un singur exemplu real, pe un document editat
o singura data. N-a fost fortata o neconcordanta artificiala pe `id_vanzare=1049` (ar fi cerut
modificarea logicii de calcul a notei, `scrie_nota`, cateva mii de linii, in afara bugetului
acestei cercetari) si nici o cautare exhaustiva pe alti ani/luni.
- **Ramura `ntip in (48,49)`** (a treia linie de corectie, `:16577-16688`) — nu se aplica facturii de
test (tip=1), neexercitata.
- `PACK_CONTAFIN` (flux-ul care muta randurile din `ACT_TEMP` in `ACT` persistenta) a fost citit doar
punctual (`INITIALIZEAZA_SCRIERE_ACT_RUL`), via `ALL_SOURCE` (read-only, `sqlplus`) — nu integral;
concluzia despre golirea lui `ACT_TEMP` se bazeaza pe metadata `ALL_TABLES.DURATION` (autoritara),
nu pe citirea completa a fluxului de flush.
## Date de test consumate
Continuare pe **`id_vanzare = 1049`** (deja in lucru din S5, `docs\handoff_punct6_dupa_s5.md`):
`cod` realocat suplimentar **1140900 -> 1140901 -> 1140902 -> 1140903** (prima rulare, cu interogarea
de numarare gresita — pastrata doar ca generatie reala, nu s-a revenit peste ea) **-> 1140904 ->
1140905 -> 1140906** (rularea finala, cea raportata mai sus). **Cod curent: `1140906`.** Niciun
`det` schimbat sau sters suplimentar fata de S5 (toate cele trei treceri au fost reeditari pure);
totalurile documentului raman `747.96 / 157.06 / 905.02`, identice cu starea de la finalul S5.
`id_vanzare = 1050` si `1037` — neatinse.
## Fisiere atinse
Un singur fisier nou, in `COMUN` (necomis): `COMUN\utile\Teste\editare_factura\test_s7_rotunjire.prg`
+ logul `test_s7_rotunjire_log.txt`. Niciun fisier de productie (`.vc2`/`.sc2`/`.prg` de aplicatie),
niciun script de migrare. Zero commit git/SVN.
## Stare finala — verificata, nu presupusa
- **Tranzactii Oracle deschise**: 0 (`v$transaction` join `v$session` pe `MARIUSM_AUTO`).
- **Procese `vfp9.exe` ramase**: 0 (`tasklist`).
- **Documentul de test**: `id_vanzare=1049`, `cod=1140906`, `sters=0`, totaluri neschimbate.