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
312 lines
30 KiB
Markdown
312 lines
30 KiB
Markdown
# S8b — Proiectare: rutarea scrierii dupa ce utilizatorul a schimbat ceva
|
|
|
|
Livrabilul povestii **S8b** din `docs\plan_13_unificare_formular_facturare.md:2890-2898` (G-bis,
|
|
deciziile 6/9/25/26). Cercetare READ-ONLY — zero editari de cod, zero `git_sync.ps1`/`txt2vcx.ps1`,
|
|
zero commit, zero scriere Oracle (doar SELECT, nefolosit efectiv — tot ce trebuia era in codul VFP/
|
|
PL-SQL deja exportat sau in rapoartele de referinta citate in briefing).
|
|
`COMUN\clase\ofacturare_comun.vc2` si `COMUN\programe\ofacturare_editare.prg` (perimetrul #6) doar
|
|
citite, neatinse.
|
|
|
|
## Verdict (rezumat, 8 randuri)
|
|
|
|
Reteta din G-bis e corecta ca directie, dar are un gol nedocumentat pana acum: **`modifica_date_factura`
|
|
nu e singura ruta care scrie cei 14 parametri de antet** — 7 din 14 (`id_delegat`, `id_masina`,
|
|
`id_facturare`, `listare_detaliata`, `dataora_exp`, `id_agent`, `text_aditional`) sunt scrisi **si**
|
|
de calea normala de emitere (`scrie_factura2`, apelata direct de regenerare, decizia 35), pentru ca
|
|
ambele cai citesc din **acelasi obiect `poDate`**, nu din doua reprezentari separate (sectiunea 3,
|
|
dovada pe cod). Consecinta directa pentru punctul cel mai periculos al sarcinii (schimbare simultana
|
|
antet+sume): **cand regenerarea porneste, NU se mai cheama `modifica_date_factura` in plus** — ar fi
|
|
fie redundant (pe cele 7 campuri comune), fie periculos (ar scrie pe un `ID_VANZARE` care tocmai a
|
|
fost soft-sters sau inca nu exista). Regenerarea *este* deja calea de scriere a antetului cand sumele
|
|
se schimba, nu o cale separata care trebuie compusa cu ea. Explicatia de linie e acoperita direct de
|
|
`adauga_articol_factura` (parametru `V_EXPLICATIE`, plus `V_TAXCODE`), deci regenerarea o transporta
|
|
fara cod suplimentar — dar **doar daca cursorul de linii incarcat la S8 e re-citit din formular, nu
|
|
din snapshot-ul initial**. Cel mai probabil loc de fals-pozitiv pentru „deschid si inchid fara sa
|
|
modific nimic” e lookup-ul „ultimul delegat/masina al clientului” din `frm_alte_date.Init`
|
|
(sectiunea 5) — cod construit pentru emiterea unui document nou, periculos daca ruleaza neschimbat pe
|
|
calea de editare.
|
|
|
|
---
|
|
|
|
## 1. Mecanismul de detectare a schimbarii
|
|
|
|
### 1.1 Optiuni si ce se strica la fiecare
|
|
|
|
| Optiune | Ce se strica |
|
|
|---|---|
|
|
| **Hash/checksum pe randuri** | Ascunde exact tipul de fals-pozitiv cel mai periculos aici: doua reprezentari numeric-egale dar text-diferite (`Str(12.5,18,2)` vs `Str(12.50,18,2)`, `.NULL.` vs `0` vs `""`) produc hash-uri diferite desi valoarea „reala" e identica. Orice normalizare facuta *inainte* de hash trebuie sa fie deja perfecta — hash-ul nu adauga nimic, doar ascunde bug-urile de normalizare in loc sa le arate. |
|
|
| **Flag-uri `lModificat` in evenimentele de editare** | E robust doar daca *fiecare* eveniment care poate schimba o valoare seteaza flag-ul — un `ControlSource` legat direct (binding automat VFP, cazul majoritatii campurilor de antet aici, vezi `modifica_date_factura_parametri.md` §4) nu trece printr-un eveniment scriptat, deci flag-ul ramane `.F.` desi valoarea s-a schimbat. Whitelist fragil: la fiecare control nou adaugat, cineva trebuie sa-si aminteasca sa cablasje flag-ul. Cel mai riscant pentru campurile din `frm_alte_date` (S3b), unde `ControlSource=poDate.xxx` e tiparul dominant. |
|
|
| **Snapshot la incarcare + comparatie camp cu camp la confirmare** (recomandat) | Cere disciplina la normalizare (precizie, `.NULL.`), dar e singura optiune care **nu poate rata o schimbare structurala** — compara starea finala, nu istoricul de evenimente, deci un binding automat care a schimbat o valoare fara eveniment scriptat tot apare in diff. E si optiunea deja folosita implicit in codebase pentru un caz inrudit: `chkSerieAct.Enabled = !EMPTY(NVL(poRec.numar_act,0))` (`ofacturare_comun.vc2:5736`) testeaza starea curenta, nu un istoric de evenimente. |
|
|
|
|
**Recomandare: snapshot + comparatie la confirmare**, din motivul de mai sus (robustete la binding
|
|
automat) — argumentat, nu doar preferat.
|
|
|
|
### 1.2 Ce se snapshoteaza, concret
|
|
|
|
- **Antet**: o copie profunda a lui `poDate` (sau a subsetului de proprietati relevante) luata
|
|
imediat dupa S8 (incarcare), **inainte** de orice lookup auto-completat (vezi sectiunea 5 — ordinea
|
|
conteaza: daca lookup-ul „ultimul delegat" ruleaza inainte de snapshot, snapshot-ul insusi e deja
|
|
poluat, si orice comparatie ulterioara devine inutila).
|
|
- **Linii**: o copie a cursorului `crsfactura` (nume alias de confirmat la implementare — S4d il
|
|
citeaza ca atare) imediat dupa populare la S8, cu cheia de linie **`id_vanzare_det`** (coloana deja
|
|
incarcata la S8 din `VANZARI_DETALII`, cf. plan S8: „explicatia si taxcode pe fiecare linie" — nu
|
|
exista alt candidat de cheie stabila in materialele citite; **de confirmat exact numele coloanei in
|
|
cursor la implementare**, nu presupus mai departe aici).
|
|
- **Discountul de document** (`VANZARI.DISCOUNT`, `discount_evidentiat`) — campuri de antet cu efect
|
|
de suma, tratate separat de cele 14 (vezi matricea, sectiunea 2).
|
|
|
|
### 1.3 Capcanele reale de VFP, cu tratament explicit
|
|
|
|
| Capcana | Tratament recomandat |
|
|
|---|---|
|
|
| `.NULL.` vs `0` vs `""` | Comparatie prin functie de normalizare unica (`NVL`-style) aplicata **simetric** pe ambele parti (snapshot si curent) inainte de `=`, nu doar pe una — o comparatie `Nvl(nou,0) == vechi` fara acelasi tratament pe `vechi` e asimetrica si poate rata cazul `vechi=.NULL., nou=0` ca "neschimbat" cand de fapt utilizatorul a introdus explicit un zero peste un camp gol (relevant pt. campuri ca `id_ruta`/`id_agent`, unde `.NULL.` si `0` pot avea semnatura diferita in Oracle — `modifica_date_factura` scrie orice i se da, inclusiv `NULL` neconditionat, deci diferenta chiar conteaza acolo). |
|
|
| Precizie numerica (`gnPc`, `gnPPretV`, `gnPCant`) | Comparatia nu se face pe valoarea `Double` bruta, ci pe reprezentarea **rotunjita la aceeasi precizie folosita la scriere** — acelasi `Round(x, gnPc)`/`Round(x, gnPPretV)`/`Round(x, gnPCant)` aplicat pe ambele parti inainte de `=`. Motiv concret: `adauga_articol_factura` primeste pretul ca `Str(..., 18, gnPc)` (text), deci orice zgomot de reprezentare in binar (ex. `12.4999999999` vs `12.5`) care nu apare si in textul trimis Oracle-ului nu trebuie sa declanseze regenerare — regenerarea trebuie sa porneasca de la o diferenta **care ar produce efectiv un text diferit trimis la Oracle**, nu de la zgomot de virgula mobila intern VFP. |
|
|
| Randuri sterse/adaugate, nu doar modificate | Comparatie pe **multimea cheilor** `id_vanzare_det`, nu pe pozitie: chei prezente in snapshot dar absente in curent = sters; chei prezente in curent dar absente in snapshot (sau `id_vanzare_det` gol/`0`, sentinela de linie noua) = adaugat; chei prezente in ambele = potential modificat, comparat camp cu camp. **Orice** rand adaugat sau sters, indiferent de continutul lui, marcheaza direct pentru regenerare (tabelul din G-bis: „linii adaugate/sterse” → regenerare) — nu are sens sa se compare campuri pe un rand care oricum nu exista pe ambele parti. |
|
|
| Ordinea randurilor | **Nu conteaza pentru detectie** — comparatia e pe chei (set), nu pe pozitie in grid. Ordinea ar conta doar daca reordonarea insasi ar fi o schimbare semnificativa pentru Oracle (nu e cazul — `adauga_articol_factura` nu are parametru de ordine vizibil in semnatura citata in `s10_pret_rederivat.md` §1). **Rezerva**: daca formularul unificat permite reordonare manuala a liniilor si aceasta conteaza pentru vreun raport (necercetat aici), ar trebui un camp explicit de ordine comparat separat — nu presupus din pozitia in cursor. |
|
|
|
|
---
|
|
|
|
## 2. Matricea „ce s-a schimbat → ce ruta", exhaustiva
|
|
|
|
Sursa de adevar: G-bis (`plan:812-889`) + `modifica_date_factura_parametri.md` + `rute_scriere_antet.md`.
|
|
|
|
| Camp / grup | Ruta | De ce nu una mai ieftina |
|
|
|---|---|---|
|
|
| **Cei 14 parametri** (serie, numar, data, scadenta, ruta, delegat, masina, agent, `dataora_exp`, adresa facturare, text aditional, `listare_detaliata`, `tip_saft`, `efactura`) | `modifica_date_factura`, **daca nimic altceva nu s-a schimbat in aceeasi sesiune** | E singura cale directa pe `VANZARI`+`DOCUMENTE`+`ACT`+`IREG_PARTENERI`+`JV2007`+`RUL` care nu atinge sumele — mai ieftina decat regenerarea (fara stergere+reemitere, fara stoc, fara nota noua). Regenerarea ar face acelasi lucru, dar cu cost mult mai mare (tranzactie stergere+reemitere, stoc, ID_VANZARE nou) pentru un camp care nu atinge nicio suma — nu se justifica. |
|
|
| **Explicatia si `taxcode` pe o linie**, **fara nicio alta schimbare pe linii** | `modifica_explicatie_articol` | Update pe doua coloane, fara sume — regenerarea ar fi disproportionata (stergere+reemitere completa pentru un text). |
|
|
| **Cantitati, preturi, discount pe linie, gestiune, cota TVA, serie/lot** | Regenerare | Nu exista alta ruta — verificat exhaustiv, nicio procedura Oracle de tip "modifica cantitate/pret pe linie existenta" (confirmat indirect: singura cale de scriere a sumelor e `adauga_articol_factura`, apelata doar la emitere/reemitere). |
|
|
| **Linii adaugate/sterse** | Regenerare | Identic — nu exista `sterge_linie_factura`/`adauga_linie_factura` separat de fluxul de emitere. |
|
|
| **Discountul de document** (`VANZARI.DISCOUNT`, `discount_evidentiat`) | Regenerare | Nu e printre cei 14 parametri ai `modifica_date_factura` (confirmat, tabelul din `modifica_date_factura_parametri.md` §1-2 nu il contine) si atinge direct sumele — cade natural in categoria "orice atinge sumele" din G-bis. |
|
|
| **Grupul B** — tip document, valuta, zi curs, client, sursa, gestiune sursa, politica de preturi | Regenerare (decizia 25); **blocate in etapa I** | `rute_scriere_antet.md` §2 — verdict NU pentru toate 7, cautat exhaustiv in Oracle si VFP; regenerarea (calea de emitere) e singura care le scrie, pentru ca sunt scrise o singura data la `scrie_factura2`/echivalent, niciodata actualizate separat. |
|
|
| **Grupul C** — incasare (mod, casa, serie/nr chitanta, suma, POS) | Regenerare (decizia 25); **blocate in etapa I** | `rute_scriere_antet.md` §3 — incasarea devine ea insasi o linie de nota (`scrie_incasare2`), scrisa doar la emitere; nicio ruta „modifica incasare" separata. |
|
|
| **Grupul A** — venit/cheltuiala, sectie, responsabil, lucrare | **Niciuna din #13** — read-only (decizia 26) | Editabile deja, dar la nivel de LINIE de nota, prin #6 (`frm_modific2024`) — #13 le-ar scrie uniform pe tot documentul, risc de suprascriere tacita a unei diferentieri facute din #6. Zero suprapunere intre fire, per decizia lui Marius din 09.08.2026. |
|
|
| **Datele din sectiunea pliata — delegat/transport, adresa facturare, text aditional** | Fac parte din cei 14 parametri → `modifica_date_factura` daca nimic altceva nu s-a schimbat; **regenerare** daca si sumele s-au schimbat (vezi sectiunea 3) | Sunt deja acoperite de tabelul de mai sus prin `V_ID_DELEGAT`/`V_ID_MASINA`/`V_ID_AGENT`/`V_ID_FACTURARE`/`V_TEXT_ADITIONAL`/`V_DATAORA_EXP`. |
|
|
| **Nimic schimbat** | Nimic | Explicit cerut de G-bis — altfel orice deschidere ar produce scriere degeaba. |
|
|
|
|
---
|
|
|
|
## 3. Cazurile care cad intre rute — descoperirea centrala a acestei cercetari
|
|
|
|
### 3.1 Antet + sume schimbate in aceeasi sesiune — verdict cu dovada, nu presupunere
|
|
|
|
**Intrebarea din briefing**: se cheama ambele rute, sau regenerarea le acopera pe amandoua?
|
|
|
|
**Raspuns, verificat pe cod**: **regenerarea acopera 7 din cei 14 parametri prin insusi mecanismul
|
|
de emitere, fara nicio ruta suplimentara** — pentru ca `poDate` e **acelasi obiect** care alimenteaza
|
|
atat afisarea antetului cat si apelul `scrie_factura2` folosit de regenerare (decizia 35: "reemiterea
|
|
scrie prin `pack_facturare`, pe acelasi drum ca emiterea").
|
|
|
|
Dovada directa, apelul `scrie_factura2` (`COMUN\clase\ofacturare.vc2:14345-14359`, identic la
|
|
`:14373-14387` si `:18343-18387`):
|
|
|
|
```
|
|
lcSql = [{call pack_facturare.scrie_factura2(] + ;
|
|
... pnTotalFtva, pnTotalTva, pnDiscount, serie_chit, nr_incasare, lcListaIncasare, ;
|
|
IIF(Isnull(poDate.id_delegat),[NULL],Alltrim(Str(poDate.id_delegat))) + [,] + ;
|
|
IIF(Isnull(poDate.id_masina),[NULL],Alltrim(Str(poDate.id_masina))) + [,] + ;
|
|
IIF(Isnull(poDate.id_facturare),[NULL],Alltrim(Str(poDate.id_facturare))) + [,] + ;
|
|
IIF(Isnull(poDate.nListareDetaliata),[0],Alltrim(Str(poDate.nListareDetaliata))) + [,] + ;
|
|
[to_date('] + Ttoc(poDate.dataora_exp,1) + [','YYYYMMDDHH24MISS'),] + ;
|
|
IIF(Isnull(poDate.id_agent),[NULL],Alltrim(Str(poDate.id_agent))) + [,] + ;
|
|
['] + OracleSpecialCharacters(...(poDate.text_aditional...)) + [',] + ;
|
|
Alltrim(Str(poDate.discount_evidentiat)) + [,] + ;
|
|
ALLTRIM(Str(pnParametruAditional)) + [,?@poDate.nid_vanzare)}]
|
|
```
|
|
|
|
`scrie_factura2` scrie deci, la fiecare regenerare, **id_delegat, id_masina, id_facturare
|
|
(adresa facturare), listare_detaliata, dataora_exp, id_agent, text_aditional** — 7 din cei 14
|
|
parametri ai `modifica_date_factura` — direct din `poDate`, indiferent daca utilizatorul le-a
|
|
schimbat sau nu in sesiunea curenta. **Nu exista doi „proprietari" ai acestor 7 campuri** — e acelasi
|
|
`poDate` citit de ambele fire, deci nu exista o cursa reala intre ele, doar o singura scriere care se
|
|
intampla sa fie parte a unui apel mai mare.
|
|
|
|
**Verdict, cu ordinea explicita**:
|
|
|
|
1. **Cand regenerarea porneste, NU se mai cheama `modifica_date_factura`.** Ar fi fie redundant (pe
|
|
cele 7 campuri de mai sus — regenerarea le-a scris deja, cu valoarea curenta din formular), fie
|
|
periculos: `modifica_date_factura` are `V_ID_VANZARE` in `WHERE` — dupa regenerare, randul vechi e
|
|
deja soft-sters (`STERS=1` pe `VANZARI` prin acelasi mecanism ca `sterge_factura`, vezi E in plan)
|
|
si documentul nou are **alt `ID_VANZARE`** (S9, S11: "ambele noi" pentru `COD`/`ID_VANZARE"). Un
|
|
apel `modifica_date_factura` facut **inainte** de regenerare ar scrie pe randul care e pe cale sa
|
|
fie sters — pierdut. Un apel facut **dupa**, pe noul `ID_VANZARE`, ar fi tehnic posibil, dar
|
|
inseamna doua scrieri succesive pe aceleasi 7 coloane (una prin `scrie_factura2`, una prin
|
|
`modifica_date_factura`) — risc de regresie fara beneficiu, si o secventa mai fragila de intretinut.
|
|
2. **Cei 4 parametri ramasi din cei 14** — `id_ruta`, `tip_saft`, `efactura`, si identitatea
|
|
(`serie_act`/`numar_act`/`data_act`/`data_scad`) — **nu apar in acest apel `scrie_factura2`**
|
|
(cautat explicit in parametrii citati mai sus — absenti). Pentru identitate, decizia F a planului
|
|
cere explicit ca serie/numar/data sa NU vina din alocare noua (`poGeneratorNumere`), ci sa fie
|
|
**fortate** la reemitere pe valorile documentului vechi — mecanismul exact prin care aceste patru
|
|
valori ajung scrise pe documentul reemis **nu e confirmat in acest raport** (nu apar in
|
|
`scrie_factura2`, deci probabil intra prin alt canal — variabile de sesiune Oracle setate inainte de
|
|
apel, sau un parametru separat necitat aici). **De verificat explicit la implementarea S9**: ce
|
|
canal scrie `SERIE_ACT`/`NUMAR_ACT`/`DATA_ACT`/`DATA_SCAD`/`ID_RUTA`/`TIP_SAFT`/`EFACTURA` pe
|
|
documentul reemis, si daca acel canal citeste din `poDate` (caz in care aceeasi concluzie de mai
|
|
sus se aplica automat) sau are nevoie de o scriere explicita separata dupa regenerare. **Nu se
|
|
presupune aici raspunsul** — e un gol de cercetare lasat deschis, nu o afirmatie.
|
|
3. **Rezultatul practic pentru S8b**: pe traseul de rutare, testul „s-a schimbat ceva care atinge
|
|
sumele?" trebuie evaluat **inaintea** testului „s-a schimbat antetul?" — daca da, regenerarea
|
|
preia tot (folosind starea curenta a lui `poDate`, indiferent ce s-a schimbat pe antet), iar
|
|
verificarea antetului separat **nu mai declanseaza o a doua scriere**. Ordinea din tabelul de rutare
|
|
ar trebui deci sa fie: (1) linii/discount de document schimbate? → regenerare, gata; (2) altfel,
|
|
antet schimbat (14 parametri)? → `modifica_date_factura`; (3) altfel, doar explicatie de linie? →
|
|
`modifica_explicatie_articol`; (4) altfel → nimic. Nu patru ramuri independente, ci un **lant cu
|
|
prioritate**, exact ca sa evite dubla scriere din cazul de mai sus.
|
|
|
|
### 3.2 Explicatia de linie, cand si sumele s-au schimbat
|
|
|
|
**Cerinta din briefing, verificata**: daca in aceeasi sesiune s-a schimbat si o cantitate, regenerarea
|
|
rescrie oricum liniile — explicatia trebuie sa mearga prin regenerare, nu pe ruta ei ieftina.
|
|
|
|
**Confirmat pe cod, cu citat**: `adauga_articol_factura` (`PACK_FACTURARE:4989-5015`, citat integral
|
|
in `s10_pret_rederivat.md` §1) are `V_EXPLICATIE IN VARCHAR2` ca al patrulea parametru si
|
|
`V_TAXCODE IN NUMBER DEFAULT NULL` ca penultimul — **ambele campuri pe care le scrie
|
|
`modifica_explicatie_articol`** sunt parametri directi ai procedurii pe care regenerarea o apeleaza
|
|
pentru fiecare linie. Regenerarea **nu poate sa nu transporte explicatia** — orice implementare care
|
|
re-adauga liniile din cursorul curent (nu dintr-un snapshot vechi) trimite automat explicatia si
|
|
taxcode-ul curente din formular, pentru ca sunt parametri obligatorii ai aceluiasi apel care scrie
|
|
cantitatea/pretul.
|
|
|
|
**Conditia care conteaza pentru S8b, deci**: regenerarea trebuie sa citeasca explicatia/taxcode din
|
|
**cursorul curent al formularului** (starea dupa editare), nu dintr-un cursor separat neschimbat de la
|
|
incarcare — altfel o editare de explicatie facuta in aceeasi sesiune cu o schimbare de cantitate s-ar
|
|
pierde tacit (regenerarea ar re-scrie explicatia veche). Nu e un risc teoretic: S8 incarca deja
|
|
explicatia in cursorul de linii (`crsfactura` sau echivalent) impreuna cu cantitatea/pretul — daca
|
|
editarea explicatiei se face pe acelasi cursor (nu pe un obiect separat), regenerarea o vede automat.
|
|
**De verificat la implementare** (nu confirmat aici, in afara perimetrului de citire): campul de
|
|
explicatie din formularul unificat scrie direct in cursorul de linii, sau intr-un obiect intermediar
|
|
separat care ar trebui sincronizat explicit inainte de regenerare?
|
|
|
|
**Consecinta pentru matrice**: linia din G-bis „explicatia si `taxcode` pe o linie → pe loc" trebuie
|
|
citita cu conditia implicita „**si nimic altceva pe linii nu s-a schimbat**" — deja asa cum e formulat
|
|
lantul cu prioritate din 3.1 punctul 3 (verificarea de sume vine prima).
|
|
|
|
---
|
|
|
|
## 4. Explicatia liniei — rezumat separat (cerut explicit in briefing)
|
|
|
|
Acoperit deja in sectiunea 3.2. Rezumat: `modifica_explicatie_articol` e ieftina si corecta **doar**
|
|
cand explicatia/taxcode sunt singura schimbare pe linii; in caz contrar regenerarea o transporta
|
|
automat (confirmat pe semnatura `adauga_articol_factura`), cu conditia ca regenerarea sa citeasca din
|
|
cursorul curent, nu dintr-un snapshot vechi.
|
|
|
|
---
|
|
|
|
## 5. Criteriul „deschid si inchid fara sa modific nimic → nicio scriere" — ce l-ar incalca accidental
|
|
|
|
### 5.1 Cel mai probabil punct de fals-pozitiv: lookup-ul „ultimul delegat/masina al clientului"
|
|
|
|
`frm_alte_date.Init` (`COMUN\clase\ferestre_cere_date.vc2:3119-3136`, citat in `s3b_alte_date_analitice.md`
|
|
§1.1/§4.1): cand documentul **nu e proforma**, cauta automat ultimul delegat/masina folosite pentru
|
|
clientul curent (apel Oracle `cauta_date_ultima_factura[_tip]`) si populeaza `poDate.id_delegat`/
|
|
`poDate.id_masina` cu rezultatul. Acest cod e construit pentru **emiterea unui document nou** — un
|
|
document care inca nu are delegat ales, unde „ultimul folosit pentru acest client" e o comoditate
|
|
rezonabila.
|
|
|
|
**Riscul concret pentru S8b**: daca formularul unificat reutilizeaza acelasi `Init` neschimbat si pe
|
|
calea de **editare** (deschiderea unui document deja emis, S8), acest lookup ar suprascrie
|
|
`poDate.id_delegat`/`poDate.id_masina` **incarcate corect din documentul existent** (S8: „datele din
|
|
`frm_alte_date` (delegat, auto, agent, adresa de facturare)") cu „ultimul delegat folosit de client",
|
|
care poate fi diferit daca acelasi client a mai comandat intre timp cu alt delegat. Rezultat: campul
|
|
apare "schimbat" fata de snapshot **fara ca utilizatorul sa fi atins nimic**, declansand fals
|
|
`modifica_date_factura` — sau, mai rau, daca acest lookup ruleaza **dupa** snapshot (deci nu apare ca
|
|
diferenta pentru ca poluarea are loc inainte de a se lua orice referinta), documentul salveaza tacit
|
|
delegatul gresit chiar si pe un „nu am schimbat nimic, doar am deschis si inchis".
|
|
|
|
**Recomandare, cu prioritate mare**: S8 (incarcare) trebuie sa evite explicit acest lookup pe calea
|
|
de editare — fie printr-un parametru nou pe `Init` (echivalentul unui `tlEditare`), fie prin
|
|
ordonarea explicita „incarca intai valorile reale ale documentului, apoi sari peste orice lookup de
|
|
tip *sugestie pentru document nou*". **Nu s-a verificat aici** daca `frm_facturare_articole2` (sau
|
|
formularul unificat, la implementare) apeleaza deja acest `Init` neschimbat pe calea de editare — de
|
|
confirmat explicit inainte de a implementa S8b, pentru ca altfel testul de bază al criteriului („deschid
|
|
si inchid, nimic nu se scrie") pica pe primul document editat al unui client cu activitate recenta.
|
|
|
|
### 5.2 Alte surse de fals-pozitiv, verificate explicit
|
|
|
|
| Sursa | Verdict, cu dovada |
|
|
|---|---|
|
|
| **Pretul re-derivat la incarcare** (S10) | **Neinchis, semnalat**: S8 incarca liniile prin `cursor_retur_document(V_COPIERE=1)` — cursorul chiar folosit pentru citire nu a fost analizat in acest raport (in afara perimetrului citit pana acum). `s10_pret_rederivat.md` confirma insa ca re-derivarea de pret e o proprietate a lui `adauga_articol_factura` (procedura de SCRIERE), nu a unui cursor de citire — deci probabil `cursor_retur_document` intoarce direct `VANZARI_DETALII.PRET` stocat, fara sa treaca prin logica de re-derivare. **Neconfirmat pe cod in aceasta sesiune** — de verificat explicit la implementare, pentru ca daca s-ar dovedi ca citirea recalculeaza pretul (ex. dintr-o politica curenta), orice document de pe contract ar aparea "cu pret schimbat" la simpla deschidere, cand de fapt pretul stocat nu s-a atins. |
|
|
| **`zi_curs` reactiv** (S4d) | **Confirmat fara risc pe valoare**: `poDate.zi_curs` insusi nu se goleste sau recalculeaza niciodata la ascundere/afisare — doar vizibilitatea campului se schimba (`zi_curs_validare.md`, citat integral in `s4d_zi_curs_reactiv.md` §3). Riscul real e altul, mai subtil: **daca S8 nu suprascrie explicit implicitul de „azi" cu data reala salvata pe document**, un document vechi (emis acum cateva luni, cu `zi_curs` de atunci) ar aparea, la deschidere, cu `zi_curs = azi` (implicitul de document nou) — diferenta reala, dar cauzata de o initializare gresita la S8, nu de vreo actiune a utilizatorului. **Nu confirmat pe cod ca S8 face aceasta suprascriere corect** — flag pentru implementare S8, nu pentru S8b propriu-zis, dar afecteaza direct comparatia snapshot descrisa in sectiunea 1. |
|
|
| **Rotunjiri la afisare vs. la scriere** | Acoperit deja in sectiunea 1.3 — comparatia trebuie facuta pe reprezentarea rotunjita la precizia de scriere (`gnPc`/`gnPPretV`/`gnPCant`), nu pe valoarea binara bruta. |
|
|
| **`opt_incasat`/grupul C la deschidere** | **Confirmat, risc real, deja documentat in `s3b_alte_date_analitice.md` §4.3**: `opt_incasat.Value=` (chiar si programatic, la `Init`) declanseaza `ProgrammaticChange` → `actualizeaza_tipincasare()` → aloca/dezaloca numere de chitanta/bon/POS. Daca sectiunea pliata re-populeaza `opt_incasat.Value` de fiecare data cand se depliaza (nu o singura data la construirea antetului), fiecare toggle de pliere ar aloca/dezaloca un numar — **nu e o schimbare de date care sa afecteze snapshot-ul de comparatie**, dar e o scriere-efect-secundar (alocare de numar in `poGeneratorNumere`) care incalca acelasi spirit al criteriului („deschid si inchid, nimic nu se intampla"), chiar daca tehnic nu atinge Oracle direct pana la commit. Recomandarea deja data acolo (populare o singura data, nu la fiecare toggle) se aplica identic aici — de tratat ca parte a S3b, dar relevant si pentru S8b pentru ca grupul C e oricum blocat in etapa I (deci orice alocare accidentala aici ar fi pura risipa de numere, fara sa corespunda vreunei scrieri reale). |
|
|
| **Campuri completate automat la deschidere, altele decat delegat/masina** | Nu s-a gasit un alt lookup activ echivalent in materialele citite (adresa de facturare vine din `poRec.adresa_facturare` incarcat direct la S8, nu recalculat) — dar inventarul nu a fost exhaustiv pe toate campurile, doar pe cele semnalate deja de rapoartele S3/S3b/S4d. **Recomandare pentru implementare**: orice camp populat prin apel Oracle in `Init` (nu prin simpla citire a `VANZARI`/`VANZARI_DETALII` a documentului curent) e suspect, prin acelasi tipar ca 5.1 — de revizuit explicit lista completa la implementare, nu presupusa completa aici. |
|
|
|
|
---
|
|
|
|
## 6. Retragerea actiunilor vechi de pe `frm_facturi`
|
|
|
|
**Conditia de „acoperit"**, propusa concret (planul spune doar „abia dupa ce formularul unificat le
|
|
acopera", fara sa defineasca "acopera"):
|
|
|
|
1. **Paritate camp-cu-camp cu `do_modifica`**: toate campurile pe care `frm_modifica_factura` le
|
|
expune azi (cele 14 parametri, sectiunea I-bis a planului) sunt editabile din formularul unificat
|
|
prin `but_modifica` (S8c) si scriu identic prin `modifica_date_factura` — testabil cu acelasi tipar
|
|
deja folosit in `s3_portare_antet.md` §7 si `s3b_alte_date_analitice.md` §10.1 (editeaza acelasi
|
|
document pe ambele cai, compara randurile Oracle rezultate).
|
|
2. **Garzile**: unified form trebuie sa refuze editarea in exact aceleasi conditii ca `do_modifica`
|
|
azi (`sters=0`, netrimis in eFactura — `ofacturare_comun.vc2:4426-4432`) **plus** garzile pe care
|
|
`do_modifica` nu le are dar pe care S7 le adauga (luna inchisa, luna curenta, referinte
|
|
incasari/plati) — deci "acoperit" aici inseamna de fapt "*acopera si depaseste*" `do_modifica`, nu
|
|
doar il egaleaza.
|
|
3. **Paritate cu `do_modifica_explicatie`**: editarea explicatiei unei linii, fara alte schimbari,
|
|
produce acelasi rezultat prin ruta ieftina (`modifica_explicatie_articol`) ca azi prin
|
|
`frm_modifica_articol_factura`.
|
|
4. **Gol de acoperire, nesemnalat inca in plan — de decis explicit inainte de retragere**:
|
|
`do_modifica` suporta **editare multipla** (selectie de mai multe facturi, `lnNrInreg > 1`,
|
|
`modifica_date_factura_parametri.md` §3: ramura `Otherwise` face `Scatter ... Blank` si aplica
|
|
acelasi antet pe toate randurile selectate din `SCAN`). **Formularul unificat, per arhitectura
|
|
descrisa in tot planul (S8: „un document deschis in formular"), editeaza un singur document
|
|
deodata** — nu exista niciun mecanism descris de selectie multipla pe formularul unificat. Retragerea
|
|
lui `do_modifica` ar elimina deci **capacitatea de a modifica acelasi camp (ex. ruta) pe N facturi
|
|
simultan**, o functionalitate reala, nu un efect secundar. **Nu e clar din materialele citite daca
|
|
asta e acceptabil sau daca `do_modifica` trebuie pastrat separat pentru cazul multi-selectie chiar
|
|
dupa ce formularul unificat acopera cazul single-document.** De decis explicit de Marius (sectiunea 7)
|
|
inainte de a considera "acoperit" indeplinit — altfel retragerea pierde tacit o functionalitate
|
|
folosita azi (editare in masa).
|
|
|
|
---
|
|
|
|
## 7. Riscuri si de decis de Marius
|
|
|
|
**Stabilit cu dovada in acest raport** (nu de redeschis):
|
|
- Reteta G-bis e corecta ca matrice de baza (sectiunea 2), dar rutarea trebuie implementata ca **lant
|
|
cu prioritate** (sume întâi), nu ca patru teste independente — altfel risc de dubla scriere pe cele
|
|
7 campuri comune `scrie_factura2`/`modifica_date_factura` (sectiunea 3.1).
|
|
- Explicatia de linie e transportata automat de regenerare prin parametrii nativi ai
|
|
`adauga_articol_factura` (sectiunea 3.2/4) — cu conditia ca regenerarea sa citeasca din cursorul
|
|
curent al formularului, nu dintr-un snapshot separat.
|
|
- Lookup-ul „ultimul delegat/masina" din `frm_alte_date.Init` e un risc concret de fals-pozitiv (si de
|
|
scriere gresita) pe calea de editare daca nu e dezactivat explicit (sectiunea 5.1) — cel mai probabil
|
|
candidat pentru a sparge criteriul de baza al S8b.
|
|
- `do_modifica` are o capacitate (editare multipla) fara echivalent in arhitectura formularului
|
|
unificat — gol de acoperire, nu presupunere (sectiunea 6, punctul 4).
|
|
|
|
**Ramase de decis de Marius**:
|
|
1. **Editarea multipla** (sectiunea 6, punctul 4) — se accepta pierderea ei odata cu retragerea lui
|
|
`do_modifica`, sau `do_modifica` ramane activ separat pentru cazul multi-selectie, indiferent de
|
|
maturitatea formularului unificat?
|
|
2. **Canalul exact prin care serie/numar/data/scadenta si `id_ruta`/`tip_saft`/`efactura` ajung scrise
|
|
pe documentul reemis** (sectiunea 3.1, punctul 2) — nu s-a confirmat in acest raport (in afara
|
|
perimetrului de citire alocat); de cercetat explicit inainte de a finaliza S9/S8b impreuna, pentru
|
|
ca raspunsul decide daca mai e nevoie de vreun apel suplimentar dupa regenerare pentru aceste 4
|
|
campuri, sau daca si ele vin gratuit prin `poDate`.
|
|
3. **Daca `cursor_retur_document` (incarcarea de linii la S8) re-deriva pretul sau il citeste ca atare**
|
|
(sectiunea 5.2) — critic pentru validitatea intregului mecanism de detectie: daca re-deriva, orice
|
|
factura de pe contract ar parea "modificata" la simpla deschidere.
|
|
4. **Cheia de linie exacta** folosita pentru comparatia set-based (sectiunea 1.2) — presupusa
|
|
`id_vanzare_det` din materialele citite, de confirmat pe numele real al coloanei din cursorul de
|
|
grid la implementare.
|
|
|
|
## Ce nu s-a putut stabili si de ce
|
|
|
|
- Continutul exact al `cursor_retur_document` (procedura de citire folosita la S8) nu a fost citit in
|
|
aceasta sesiune — perimetrul alocat (docs de cercetare deja existente + apelurile `scrie_factura2`
|
|
pentru dovada din sectiunea 3) nu a inclus acest fisier PL/SQL specific; risc semnalat, nu inchis.
|
|
- Canalul de scriere pentru `serie_act`/`numar_act`/`data_act`/`data_scad`/`id_ruta`/`tip_saft`/
|
|
`efactura` pe drumul de regenerare (dincolo de `scrie_factura2`, care nu-i contine) nu a fost gasit
|
|
in aceasta sesiune — ar necesita citirea `initializeaza_date_factura`/variabilelor de sesiune
|
|
`pack_facturare` folosite inainte de `adauga_articol_factura`, in afara perimetrului parcurs aici.
|
|
|
|
Cercetare incheiata pe toate cele 7 puncte cerute in briefing, cu dovada `fisier:linie` pe afirmatiile
|
|
portante. Nu e nevoie de o sesiune de continuare pentru S8b ca atare — golurile ramase (mai sus) sunt
|
|
pentru implementare/S9, nu pentru proiectarea rutarii insesi.
|