469 lines
29 KiB
Markdown
469 lines
29 KiB
Markdown
# S10 (runda 3) — Proiectare: pretul re-derivat din contract la reemiterea #13
|
|
|
|
Agent de proiectare (research), read-only. Nicio editare de cod, nicio scriere Oracle in afara de
|
|
`SELECT`. Sursa SQL: `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql`
|
|
(17217 linii — **de data asta liniile citate coincid exact cu cele din raportul vechi
|
|
`s10_pret_rederivat.md`, fara offset**). Date verificate pe `MARIUSM_AUTO@ROA_CENTRAL`, doar
|
|
`SELECT`, 11.08.2026 — baza de dezvoltare, esantion mic (vezi sectiunea 2), nu productie.
|
|
|
|
## 0. Rezumat
|
|
|
|
**Intrebare adaugata de team-lead, raspuns scurt: NU.** `cursor_retur_document` (procedura folosita
|
|
la INCARCAREA liniilor unui document existent, apelata din VFP cu `V_COPIERE=1`) **citeste pretul
|
|
ca atare din `VANZARI_DETALII.PRET`, nu il re-deriva** — nu exista niciun `JOIN` catre
|
|
`CTR_ARTICOLE`/`CRM_POLITICI_PRET_ART` in toata procedura (`:3949-4062`). Presupunerea din S8b se
|
|
confirma pe cod. Detaliu complet, cu dovezi, in sectiunea 1bis (nou adaugata, tratata ca prioritara
|
|
fata de restul raportului, conform cererii).
|
|
|
|
**Faptul portant (scrierea, nu citirea) se reconfirma integral** (`:5146-5185`) si se extinde cu
|
|
doua descoperiri noi:
|
|
|
|
1. **TVA-ul si identitatea valutei sunt re-derivate din exact acelasi `JOIN`/`SELECT` ca pretul**,
|
|
pe aceeasi ramura de contract — nu doar pretul e in pericol la reemitere, ci pretul + cota de
|
|
TVA + valuta articolului, impreuna, tot-sau-nimic (sectiunea 3).
|
|
2. **Nu exista nicio a doua re-derivare mai departe in lant** — `PRET` trece neschimbat de la
|
|
`VANZARI_DETALII_TEMP` in `VANZARI_DETALII` (`scrie_in_vanzari`, `:13705-13757`, coloana `PRET`
|
|
copiata direct, fara `DECODE`/recalcul) si `contabilizeaza_articol` nu scrie niciodata pe
|
|
`VANZARI_DETALII.PRET` (scrie doar `ACT_TEMP`, nota contabila). Singurul loc de re-derivare e
|
|
`adauga_articol_factura:5146-5185` (sectiunea 1) — **la scriere, niciodata la citire** (sectiunea
|
|
1bis).
|
|
|
|
Pe date reale (esantion mic, baza de dezvoltare): **3 din 11 linii deja facturate pe contract au
|
|
azi un pret de contract diferit de pretul efectiv facturat** — divergenta nu e ipotetica, e deja
|
|
prezenta (sectiunea 2).
|
|
|
|
**Recomandare** (detaliata in sectiunea 4): varianta **(c) avertizare + confirmare explicita**,
|
|
implementabila integral in VFP cu `goExecutor` (fara sa ating `pack_facturare`, conform deciziei
|
|
27-bis), cu optiunea de a trece la **(b) blocare stricta** daca Marius prefera zero schimbari
|
|
tacite vreodata. Varianta **(d) ocolire a ramurii** e posibila mecanic dar produce o regresie reala
|
|
(pierderea legaturii `VANZARI_DETALII.ID_CTR`) — **nu o recomand**.
|
|
|
|
---
|
|
|
|
## 1. Reverificarea faptului portant
|
|
|
|
Cod citit direct din sursa (`:5039-5220`), nu preluat din raportul vechi.
|
|
|
|
```sql
|
|
-- :5039-5050 — V_OPT_FACTURARE se calculeaza intern, din V_ID_CTR primit de la VFP
|
|
IF pack_facturare.ntip IN (2, 6, 26, 52) THEN
|
|
BEGIN
|
|
SELECT NVL(OPT_FACTURARE, 3) INTO V_OPT_FACTURARE FROM CONTRACTE WHERE ID_CTR = V_ID_CTR;
|
|
EXCEPTION
|
|
WHEN NO_DATA_FOUND THEN
|
|
V_OPT_FACTURARE := 4;
|
|
END;
|
|
END IF;
|
|
```
|
|
|
|
```sql
|
|
-- :5146-5185 — ramura de contract, in interiorul CASE-ului principal (:5052-5220)
|
|
WHEN V_OPT_FACTURARE = 3 THEN
|
|
BEGIN
|
|
SELECT DECODE(A.PRET_UNITAR, 0, V_PRET_TEMP, A.PRET_UNITAR),
|
|
B.PROC_TVAV,
|
|
B.ID_VALUTA,
|
|
A.PRET_CU_TVA,
|
|
C.IN_STOC
|
|
INTO V_PRET, V_PROC_TVAV, V_ID_VALUTA, V_PRETURI_CU_TVA, V_IN_STOC
|
|
FROM CTR_ARTICOLE A
|
|
LEFT JOIN CRM_POLITICI_PRET_ART B ON A.ID_POL_ART = B.ID_POL_ART
|
|
LEFT JOIN NOM_ARTICOLE C ON B.ID_ARTICOL = C.ID_ARTICOL
|
|
WHERE B.ID_ARTICOL = V_ID_ARTICOL AND A.ID_CTR = V_ID_CTR AND B.ID_POL = V_ID_POL;
|
|
EXCEPTION
|
|
WHEN NO_DATA_FOUND THEN
|
|
... V_PRET := V_PRET_TEMP; V_ID_VALUTA := V_ID_VALUTA_TEMP; V_PRETURI_CU_TVA := V_PRETURI_CU_TVA_TEMP; V_IN_STOC := V_IN_STOC_TEMP;
|
|
END;
|
|
```
|
|
|
|
**Confirmat, cuvant cu cuvant fata de runda 9**: cu `OPT_FACTURARE = 3` (sau `NULL`, implicit 3),
|
|
daca `CTR_ARTICOLE.PRET_UNITAR <> 0`, acea valoare **inlocuieste** `V_PRET_TEMP` (pretul trimis de
|
|
VFP), necondiționat de faptul ca operatorul a atins sau nu linia. Procedura **nu are nicio notiune
|
|
de reemitere** — parametrii sunt toti `IN` (`:4989-5059`, verificat), niciun canal care sa spuna
|
|
"nu recalcula". `NO_DATA_FOUND` (politica/articol lipsa) e singurul caz care respecta pretul
|
|
trimis.
|
|
|
|
### Nu exista alt loc care suprascrie pretul
|
|
|
|
- **`scrie_in_vanzari`** (`:13488+`), care muta liniile din `VANZARI_DETALII_TEMP` in
|
|
`VANZARI_DETALII` (tabela finala) la finalizarea documentului: `PRET` e in lista de coloane
|
|
copiate **neschimbat** (`:13705-13757`, `SELECT ... PRET ... FROM VANZARI_DETALII_TEMP` →
|
|
`INSERT INTO VANZARI_DETALII (..., PRET, ...)`), fara `DECODE`/recalcul. Cautare exhaustiva pe
|
|
fisier: **zero** `INSERT INTO VANZARI_DETALII` (tabela finala, nu `_TEMP`) in afara de acest
|
|
punct.
|
|
- **`contabilizeaza_articol`** (`:7173-7547`, reconfirmat structural fata de runda 9) citeste
|
|
`detalii_articol.pret` (randul deja scris in `VANZARI_DETALII_TEMP`) doar ca sa calculeze suma
|
|
notei contabile — nu scrie niciodata inapoi pe `VANZARI_DETALII.PRET`.
|
|
- **`modificare_politica_stoc`** (`:2122-2135`) face un `UPDATE CRM_POLITICI_PRET_ART SET PRET=0,
|
|
...` — e singurul alt `SET PRET` gasit in tot fisierul, dar reseteaza **politica** la schimbarea
|
|
monedei stocului, complet in afara lantului de facturare a unei linii; nu atinge vreo linie de
|
|
document.
|
|
|
|
**Concluzie sectiune**: raspunsul din runda 9 se confirma **integral**, fara nicio corectie, si se
|
|
inchide explicit intrebarea "mai exista si alte locuri" — nu mai exista.
|
|
|
|
---
|
|
|
|
## 1bis. Citirea la incarcare: `cursor_retur_document` re-deriva pretul?
|
|
|
|
**Adaugat la cererea team-lead-ului — critic pentru S8b, tratat ca prioritar.**
|
|
|
|
### Verdict: NU. Pretul se citeste ca atare din `VANZARI_DETALII.PRET`, fara nicio re-derivare.
|
|
|
|
Corp complet, `PACK_FACTURARE:3949-4062`:
|
|
|
|
```sql
|
|
PROCEDURE cursor_retur_document(V_IN_VALUTA IN NUMBER,
|
|
V_LISTAID IN VARCHAR2,
|
|
V_COPIERE IN NUMBER,
|
|
V_PROFORMA IN NUMBER,
|
|
V_ID_UTIL IN NUMBER,
|
|
V_CURSOR OUT cursor_facturare) IS
|
|
...
|
|
BEGIN
|
|
pack_facturare.initializeaza_facturare(V_ID_UTIL);
|
|
|
|
OPEN V_CURSOR FOR
|
|
WITH CRS AS (SELECT X as ID_VANZARE FROM table(charn2collection(V_LISTAID, V_SEPARATOR)))
|
|
SELECT ROWNUM AS ID_C, A.ID_ARTICOL, A.LOT, A.SERIE, A.ID_POL, A.ID_VALUTA,
|
|
... DISCOUNT_UNITAR (derivat din A.DISCOUNT_UNITAR) ...,
|
|
B.CODMAT, B.CODBARE, B.DENUMIRE, NVL(B.UM,'') AS UM,
|
|
... GESTIONABIL ..., A.CANTITATE, A.PROC_TVAV, A.ID_JTVA_COLOANA,
|
|
A.PRET_CU_TVA AS PRETURI_CU_TVA, A.CURS, A.MULTIPLICATOR,
|
|
(CASE WHEN NVL(C.MONEDA_NATIONALA,1)<>1
|
|
THEN ROUND(A.CURS * ROUND(A.PRET, pack_sesiune.nzecimale_pretvval) / NVL(A.MULTIPLICATOR,1), pack_sesiune.nzecimale_pretv)
|
|
ELSE ROUND(A.PRET, pack_sesiune.nzecimale_pretv)
|
|
END) + A.DIFERENTA AS PRET,
|
|
...
|
|
FROM (SELECT A1.ID_ARTICOL, A1.LOT, A1.SERIE, A1.ID_POL, A1.DISCOUNT_UNITAR, A1.DIFERENTA,
|
|
A1.CANTITATE, A1.PROC_TVAV, A1.ID_JTVA_COLOANA,
|
|
NVL2(A1.ID_GESTIUNE,1,0) AS GESTIONABIL, A1.PRET_CU_TVA, A1.PRET, A1.ID_VALUTA,
|
|
NVL(A2.CURS,0) AS CURS, NVL(A2.MULTIPLICATOR,1) AS MULTIPLICATOR,
|
|
A1.EXPLICATIE, A1.ID_GESTIUNE, A1.PRET_ACHIZITIE, A1.PRETD, A1.ID_JTVA_COLOANA_EX
|
|
FROM VANZARI_DETALII A1
|
|
LEFT JOIN VANZARI_CURSURI A2 ON A1.ID_VANZARE = A2.ID_VANZARE AND A1.ID_VALUTA = A2.ID_VALUTA
|
|
WHERE A1.STERS = 0 AND A1.ID_VANZARE IN (SELECT ID_VANZARE FROM CRS)) A
|
|
LEFT JOIN NOM_ARTICOLE B ON A.ID_ARTICOL = B.ID_ARTICOL
|
|
LEFT JOIN NOM_VALUTE C ON A.ID_VALUTA = C.ID_VALUTA
|
|
ORDER BY B.DENUMIRE;
|
|
END cursor_retur_document;
|
|
```
|
|
|
|
**Analiza surselor, coloana cu coloana:**
|
|
|
|
- **`A.PRET`** (`:4009-4015`, alias final `PRET`) vine din subquery-ul `A` care e
|
|
**`VANZARI_DETALII A1`** direct (`A1.PRET`, `:4041`) — **nicio referinta la
|
|
`CTR_ARTICOLE`/`CRM_POLITICI_PRET_ART`/`CRM_POLITICI_PRETURI` in toata procedura** (cautare
|
|
exhaustiva pe corpul de la `:3949-4062`: zero hit-uri pentru oricare din cele trei tabele).
|
|
Singurele `JOIN`-uri sunt:
|
|
- **`VANZARI_CURSURI A2`** (`:4051-4053`), pe `ID_VANZARE`+`ID_VALUTA` — aduce `CURS`/
|
|
`MULTIPLICATOR` **stocate pe documentul insusi** (cursul valutar de la momentul cand a fost
|
|
scris, nu un curs "de azi" recalculat — tabela `VANZARI_CURSURI`, nu `CURS`).
|
|
- **`NOM_ARTICOLE B`** (`:4056-4057`) — doar campuri descriptive (`CODMAT`, `CODBARE`,
|
|
`DENUMIRE`, `UM`), acelasi tipar confirmat deja in `rec_pret_lazy.md` A2 pentru alte cursoare
|
|
din pachet — niciodata sursa de pret.
|
|
- **`NOM_VALUTE C`** (`:4058-4059`) — doar `MONEDA_NATIONALA`/`NUME_VAL`, folosit in `CASE` ca sa
|
|
decida **cum se formateaza** afisarea (lei vs. valuta), nu ca sa aduca un pret alternativ.
|
|
- Expresia finala pe `PRET` e o **transformare de afisare** a lui `A.PRET` (rotunjire +
|
|
conversie in valuta folosind `A.CURS`/`A.MULTIPLICATOR` **stocate pe document**) plus
|
|
`A.DIFERENTA` (coloana proprie pe `VANZARI_DETALII`, un ajustaj deja persistat pe linie, nu o
|
|
recalculare live) — nu o re-derivare dintr-o sursa externa.
|
|
- **Acelasi tipar pe `DISCOUNT_UNITAR`, `PROC_TVAV`, `ID_VALUTA`, `PRET_CU_TVA`** — toate citite
|
|
direct din `A1.*` (`VANZARI_DETALII`), fara vreun `JOIN` catre politica/contract.
|
|
|
|
**Confirmare suplimentara pe partea VFP — `cursor_retur_document` cu `V_COPIERE=1` e chiar calea
|
|
folosita la reincarcarea unui document existent, cu prioritate fata de ramura de contract**:
|
|
|
|
```
|
|
COMUN\programe\ofacturare.prg:266-283
|
|
Do Case
|
|
Case m.llCopiere
|
|
lcSqlCursor = [{call pack_facturare.cursor_retur_document(?poDate.in_valuta,?poDate.listaid,1,?poDate.eProforma,?gnIdUtil)}]
|
|
Case Inlist(tnTip, 48, 49)
|
|
...
|
|
Case tnTip = 45
|
|
...
|
|
Case Inlist(tnTip, 1, 22, 5, 29, 7, 10, 23) && lista de preturi
|
|
...
|
|
Case Inlist(tnTip, 2, 26, 6, 52) && contract
|
|
lcSqlCursor = [{call ]+gcS+[.pack_facturare.cursor_contract(...)}]
|
|
```
|
|
|
|
`Do Case` in VFP executa **doar prima ramura adevarata**. Cand `llCopiere` e activ (incarcarea unui
|
|
document existent — exact scenariul "editare prin regenerare" descris de team-lead), procedura
|
|
apelata e **intotdeauna** `cursor_retur_document`, **indiferent de `tnTip`** — ramura de contract
|
|
(`cursor_contract`, `:283+`) nu se mai ajunge deloc, chiar daca documentul e de tip 2/6/26/52.
|
|
`cursor_retur` (`:3934-3947`) e doar un wrapper subtire peste `cursor_retur_document` cu
|
|
`V_COPIERE=0`/`V_PROFORMA=0`, pentru returul propriu-zis — nu schimba concluzia.
|
|
|
|
### Consecinta pentru S8b
|
|
|
|
**Presupunerea "re-derivarea e o proprietate a scrierii, nu a citirii" se confirma pe cod, fara
|
|
rezerve.** Deschiderea formularului de editare (etapa II, incarcarea liniilor existente) **nu**
|
|
declanseaza nicio re-derivare de pret/TVA/valuta — documentul apare "neschimbat" la simpla
|
|
deschidere, exact cum a presupus S8b. Mecanismul de detectare a schimbarii (compara starea
|
|
incarcata cu starea la confirmare) **poate functiona ca premisa** — riscul de suprascriere tacita
|
|
descris in restul acestui raport (sectiunile 1-6) apare **doar la re-scriere** (regenerare efectiva
|
|
prin `adauga_articol_factura`), nu la incarcare. Cele doua momente (citire la deschidere, scriere
|
|
la regenerare) sunt guvernate de **cursoare Oracle complet diferite** (`cursor_retur_document` vs.
|
|
`adauga_articol_factura`), fara cod comun — o modificare facuta doar pe unul din ele (de exemplu o
|
|
garda adaugata la scriere, variantele (b)/(c) din sectiunea 4) **nu afecteaza** comportamentul
|
|
celuilalt.
|
|
|
|
---
|
|
|
|
## 2. Tipurile afectate si frecventa in date
|
|
|
|
**Tipurile de document**: `pack_facturare.ntip IN (2, 6, 26, 52)` (`:5039`), confirmat identic cu
|
|
tabelul din `rec_editare_factura.md:176` (contract = tip 2/6/26/52, ramura de scriere
|
|
`scrie_factura2`, la fel ca lista de preturi — nu are RPC separat).
|
|
|
|
**Frecventa in date** (schema `MARIUSM_AUTO`, doar `SELECT`, 11.08.2026):
|
|
|
|
```sql
|
|
-- VANZARI_DETALII cu ID_CTR populat, active: 74
|
|
-- din care, articolul are un rand corespunzator in CTR_ARTICOLE (id_ctr+id_articol): 11
|
|
-- din acelea, CTR_ARTICOLE.PRET_UNITAR <> 0 SI diferit de VANZARI_DETALII.PRET: 3
|
|
-- din acelea, CTR_ARTICOLE.PRET_UNITAR <> 0 SI egal cu VANZARI_DETALII.PRET: 8
|
|
-- din acelea, CTR_ARTICOLE.PRET_UNITAR = 0 (nu suprascrie): 0
|
|
```
|
|
|
|
**Interpretare, cu grija la marimea esantionului**: baza e de dezvoltare, cu doar 27 randuri totale
|
|
in `CTR_ARTICOLE` — nu extrapolez procentul (27%) la volumul de productie. Dar **3 cazuri reale,
|
|
nu zero**, e dovada suficienta ca fenomenul **chiar se intampla**, nu doar teoretic: daca oricare
|
|
din cele 3 facturi ar fi reemisa azi prin regenerare (#13 etapa II), suma ei s-ar schimba tacit,
|
|
fara nicio actiune a operatorului legata de pret. Simetric, cele 8 cazuri "egal" arata ca in
|
|
majoritatea situatiilor curente reemiterea ar fi azi inofensiva — ceea ce face problema mai
|
|
insidioasa, nu mai putin reala: cand se produce, nimeni nu se astepta, pentru ca de obicei nu se
|
|
intampla nimic vizibil.
|
|
|
|
**Nu exista coloana de audit pe `CTR_ARTICOLE`** (confirmat din `user_tab_columns`: `ID_CTR_ART,
|
|
ID_CTR, ID_POL_ART, PRET_UNITAR, CANT, COEF_DISCOUNT, VAL_DISCOUNT, ID_VALUTA, EXPLICATIE, UM,
|
|
ID_ARTICOL, PRET_CU_TVA, ID_LOCATIA, PROC_TVAV, VALOARE` — nicio `DATA_MODIF`/`ID_UTIL_MODIF`) —
|
|
nu exista cale de a masura *cat de des* se schimba `PRET_UNITAR` in timp, doar *cate cazuri
|
|
divergente exista azi*. Raspunsul la "cat de des" ramane nedeterminabil din date; ce se poate
|
|
spune e doar ca divergenta **exista deja**, azi, pe un esantion mic.
|
|
|
|
---
|
|
|
|
## 3. Descoperire noua: TVA si valuta sunt re-derivate din **aceeasi** interogare ca pretul
|
|
|
|
Din citatul de la sectiunea 1: `SELECT DECODE(A.PRET_UNITAR, ...), B.PROC_TVAV, B.ID_VALUTA, ...`
|
|
— **un singur `SELECT`**, trei valori. Asta inseamna ca varianta de raspuns nu poate trata pretul
|
|
izolat: daca politica de pret a contractului (`CRM_POLITICI_PRET_ART`, aceeasi `B`) si-a schimbat
|
|
si cota de TVA sau valuta intre timp, reemiterea le suprascrie **pe amandoua**, prin acelasi
|
|
mecanism, in aceeasi conditie (`NO_DATA_FOUND` → respecta ce a trimis VFP; altfel → suprascrie).
|
|
|
|
Comparativ cu discountul si cursul (reconfirmare fata de runda 9, sectiunile 6-7 ale raportului
|
|
vechi, verificate din nou pe sursa curenta):
|
|
|
|
| Camp | Tratament pe ramura de contract | Risc la reemitere |
|
|
|---|---|---|
|
|
| **Pret** (`V_PRET`) | `DECODE(A.PRET_UNITAR, 0, V_PRET_TEMP, A.PRET_UNITAR)` — suprascris daca `PRET_UNITAR<>0` | **Da — faptul portant** |
|
|
| **TVA** (`V_PROC_TVAV`) | `B.PROC_TVAV`, din acelasi `SELECT`, fara fallback conditionat separat | **Da — aceeasi conditie ca pretul** |
|
|
| **Valuta articol** (`V_ID_VALUTA`) | `B.ID_VALUTA`, idem | **Da — aceeasi conditie** |
|
|
| **Discount** (`V_DISCOUNT_UNITAR`) | Parametru trimis de VFP, scris direct in `INSERT` (`:5266`), nicio ramura din `CASE` il citeste | **Nu — mereu respectat, indiferent de ramura** |
|
|
| **Curs** (`V_CURS`) | `DECODE(V_CURS, 0, 1, V_CURS)` la `INSERT` (`:5270`) — inlocuieste doar 0 cu 1 | **Nu direct — dar vezi mai jos** |
|
|
|
|
**Consecinta combinata pret+valuta+curs**: daca `V_ID_VALUTA` re-derivat difera de valuta pentru
|
|
care VFP a calculat `V_CURS` (de ex. politica a trecut de la EUR la USD intre emitere si
|
|
reemitere), documentul reemis scrie **noua valuta cu vechiul curs trimis de VFP** — nicio
|
|
validare incrucisata intre cele doua (reconfirmat, `:5270` nu verifica `V_ID_VALUTA`). Aceeasi
|
|
observatie ca in runda 9, dar acum cu dovada ca sursa comuna (pret+TVA+valuta) inseamna ca **orice
|
|
gardă construita pentru pret trebuie sa verifice si TVA si valuta in acelasi pas**, nu doar pretul
|
|
— altfel o factura reemisa poate trece garda de pret nemodificata si totusi sa iasa cu alta cota
|
|
de TVA sau alta valuta.
|
|
|
|
**Discountul nu ridica aceeasi problema** — e mereu respectat ca atare, indiferent de ramura,
|
|
deci nu are nevoie de nicio garda la reemitere.
|
|
|
|
---
|
|
|
|
## 4. Variantele de raspuns
|
|
|
|
Toate patru presupun ca infrastructura de "sterge + reemite" din #13 etapa II trimite, pentru
|
|
liniile de pe un document-contract, acelasi `V_ID_CTR` care era pe linia originala (asta e
|
|
premisa explicita a sarcinii: "acelasi drum ca la emitere" — daca `V_ID_CTR` nu s-ar retrimite,
|
|
linia nici n-ar mai fi "de pe contract").
|
|
|
|
### (a) Se accepta re-derivarea — nicio garda
|
|
|
|
**Ce se face**: nimic — regenerarea apeleaza `adauga_articol_factura` exact ca la emitere, cu
|
|
acelasi `V_ID_CTR`. Comportamentul existent (sectiunea 1) se aplica neschimbat.
|
|
|
|
**Ce se strica**: criteriul din plan ("reemitere identica produce exact aceleasi sume" — vezi
|
|
sectiunea 6) **esueaza silentios** pe orice linie de contract al carei pret/TVA/valuta a fost
|
|
modificat intre timp. Dovedit sa se intample azi, pe date reale (sectiunea 2, 3 din 11). Utilizatorul
|
|
care corecteaza o cantitate pe o factura veche poate vedea, fara sa i se spuna, totalul facturii
|
|
schimbat pentru un articol la care nici nu s-a uitat.
|
|
|
|
**Cod atins**: zero. **Regresie pe emiterea normala**: zero (comportamentul de azi ramane
|
|
identic).
|
|
|
|
### (b) Se blocheaza reemiterea cand pretul curent difera
|
|
|
|
**Ce se face**: inainte de a porni stergerea+regenerarea, VFP ruleaza un `SELECT` (prin
|
|
`goExecutor`, exact tiparul deja folosit in `ofacturare.prg` pentru alte verificari punctuale —
|
|
`goExecutor.oSelecteaza2Value(...)`, `:2628`, `:2661`) care compara, pentru fiecare linie a
|
|
documentului cu `ID_CTR` populat, pretul/TVA/valuta stocate pe linie
|
|
(`VANZARI_DETALII.PRET`/`PROC_TVAV`/`ID_VALUTA`) fata de ce ar recalcula azi ramura de contract
|
|
(`CTR_ARTICOLE.PRET_UNITAR`/`CRM_POLITICI_PRET_ART.PROC_TVAV`/`.ID_VALUTA`, acelasi `JOIN` ca in
|
|
sectiunea 1, dar rulat direct din VFP, fara sa ating `pack_facturare`). Daca gaseste macar o
|
|
divergenta, **blocheaza regenerarea** cu mesaj ("Pretul de pe contract s-a schimbat pentru
|
|
articolul X: <vechi> -> <nou>. Actualizati contractul sau anulati regenerarea.") si nu porneste
|
|
deloc stergerea.
|
|
|
|
**Ce se strica**: orice regenerare pe un document cu **cel putin o linie** de contract cu pret
|
|
schimbat e blocata **integral**, chiar daca utilizatorul voia sa corecteze cu totul altceva
|
|
(ruta, o cantitate pe alta linie). Nu exista cale de a "accepta noul pret si continua" fara o a
|
|
doua interactiune — varianta (c) rezolva exact asta.
|
|
|
|
**Cod atins**: doar VFP (o interogare noua, read-only, plus un dialog de blocare), inaintea
|
|
apelului la infrastructura de stergere+regenerare din #13. **Zero atingere `pack_facturare`**
|
|
(decizia 27-bis respectata). **Regresie pe emiterea normala**: zero — garda ruleaza doar pe
|
|
calea de regenerare, nu la emiterea unui document nou.
|
|
|
|
### (c) Se avertizeaza si se cere confirmare, cu diferenta afisata
|
|
|
|
**Ce se face**: aceeasi interogare de detectie ca la (b), dar in loc sa blocheze, afiseaza un
|
|
dialog cu lista liniilor divergente si delta (pret vechi vs. nou, si TVA/valuta daca difera —
|
|
vezi sectiunea 3, toate trei trebuie aratate, nu doar pretul) si cere confirmare explicita
|
|
("Continuati cu noile preturi de pe contract?" Da/Nu). La "Nu", regenerarea se opreste ca la (b).
|
|
La "Da", regenerarea continua normal si ramura de contract face exact ce face azi (suprascrie).
|
|
|
|
**Ce se strica**: nu opreste suprascrierea insasi — doar o face **vizibila si asumata** inainte
|
|
sa se produca, exact criteriul cerut de plan ("un rezultat explicit decis, nu accidental"). Nu
|
|
ofera o cale de a **pastra** vechiul pret in timp ce se accepta restul modificarii — pentru asta
|
|
ar trebui combinata cu (d), cu costul ei (vezi mai jos). Risc de "warning fatigue": daca
|
|
divergenta e frecventa in productie (nu se poate masura azi, sectiunea 2), utilizatorii pot invata
|
|
sa dea click pe "Da" fara sa citeasca.
|
|
|
|
**Cod atins**: identic cu (b) plus un dialog cu doua butoane in loc de unul singur. **Regresie pe
|
|
emiterea normala**: zero.
|
|
|
|
### (d) Se ocoleste ramura — reemiterea trimite pretul documentului pe o cale care nu trece prin `DECODE`
|
|
|
|
**Posibil mecanic, cu un cost real.** Ramura de contract se selecteaza din doua conditii, ambele
|
|
in afara controlului direct al apelantului per-linie:
|
|
|
|
1. `pack_facturare.ntip IN (2,6,26,52)` — variabila **de sesiune** (`:1882`,
|
|
`pack_facturare.ntip := V_TIP`), setata o singura data la initializarea documentului (tipul
|
|
documentului insusi), nu per linie. A o schimba ar insemna sa reclasifici tot documentul
|
|
(afecteaza si alte ramuri `CASE` care depind de `ntip` — `:1905`, `:5053`, `:6056-6124`,
|
|
`:14820` — cu efecte necunoscute si necontrolate) — **contrazice premisa "acelasi drum ca la
|
|
emitere"** data explicit in sarcina. Nu recomand aceasta sub-varianta.
|
|
2. `V_ID_CTR` — parametru **per linie**, trimis explicit de VFP la fiecare apel
|
|
`adauga_articol_factura`. Daca VFP trimite `V_ID_CTR = NULL` pentru o linie la reemitere,
|
|
blocul de la `:5039-5050` gaseste `NO_DATA_FOUND` (cautarea `WHERE ID_CTR = NULL` nu potriveste
|
|
nimic) → `V_OPT_FACTURARE := 4` → ramura `WHEN V_OPT_FACTURARE = 3` nu se mai potriveste →
|
|
cade pe `ELSE` (`:5187-5203`) → `V_PRET := V_PRET_TEMP` (pretul documentului, respectat ca
|
|
atare, la fel TVA prin `JTVA_COLOANE` dupa `V_ID_JTVA_COLOANA`).
|
|
|
|
**Costul**: `V_ID_CTR` e si coloana scrisa in `VANZARI_DETALII_TEMP`/`VANZARI_DETALII.ID_CTR`
|
|
(`:5280`, copiata neschimbat mai departe la `:13730`/`:13755`). Trimitand `NULL` la reemitere,
|
|
linia **pierde definitiv legatura cu contractul** in tabela finala — orice raport care
|
|
grupeaza/filtreaza pe `VANZARI_DETALII.ID_CTR` (regasire vanzari pe contract, situatii
|
|
contractuale) nu va mai gasi acea linie dupa reemitere, desi articolul a fost, de fapt, livrat
|
|
pe acel contract. E o regresie permanenta si greu de observat (nu da eroare, doar dispare
|
|
tacit dintr-un raport), pe un camp folosit azi activ (`rec_pret_lazy.md`/`idpol_comanda_contract.md`
|
|
confirma ca legatura cu contractul e cheie de raportare in tot lantul `PACK_FACTURARE`).
|
|
In plus, o data pierduta legatura, **o a doua reemitere ulterioara nu ar mai putea re-deriva
|
|
pretul din contract nici daca s-ar dori** — comutarea e ireversibila per linie, nu un flag
|
|
comutabil.
|
|
|
|
**Nu recomand varianta (d)** — costul (pierderea trasabilitatii contract-linie, permanenta) e mai
|
|
mare decat problema pe care o rezolva, si nici macar nu rezolva complet criteriul "reemitere
|
|
identica" (rezolva doar pretul/TVA/valuta acelei linii, cu pretul unei regresii pe alt camp).
|
|
|
|
---
|
|
|
|
## 5. Discount, TVA, curs valutar — verificare fata de sectiunile 6-7 din raportul vechi
|
|
|
|
Deja tratat integral in sectiunea 3 de mai sus (descoperirea principala a acestei runde). Rezumat
|
|
scurt pentru trasabilitate fata de cererea explicita:
|
|
|
|
- **Discountul** (sectiunea 6 raport vechi): reconfirmat — niciodata re-derivat, indiferent de
|
|
ramura. **Nu ridica problema de reemitere.**
|
|
- **TVA-ul** (sectiunea 6 raport vechi): reconfirmat ca "mereu recalculat, din surse diferite pe
|
|
ramura" — dar cu precizarea noua ca pe ramura de contract, sursa **coincide exact** cu sursa
|
|
pretului (acelasi `SELECT`, aceeasi conditie `NO_DATA_FOUND`). **Ridica aceeasi problema ca
|
|
pretul, prin acelasi mecanism** — orice varianta (b)/(c) trebuie sa acopere si TVA, nu doar pret.
|
|
- **Cursul valutar** (sectiunea 7 raport vechi): reconfirmat — `V_CURS` mereu respectat ca atare
|
|
(`DECODE(V_CURS,0,1,V_CURS)`), nu se re-deriva direct. **Dar** identitatea valutei
|
|
(`V_ID_VALUTA`) se re-deriva pe aceeasi ramura de contract, din acelasi `SELECT` — daca politica
|
|
a schimbat valuta intre timp, reemiterea scrie noua valuta cu vechiul curs trimis de VFP, fara
|
|
nicio validare incrucisata (reconfirmat, `:5270` nu verifica `V_ID_VALUTA`). **Ridica aceeasi
|
|
problema, prin acelasi mecanism, si trebuie acoperita de aceeasi garda.**
|
|
|
|
---
|
|
|
|
## 6. Cazul "reemitere identica"
|
|
|
|
Criteriul din plan (S12) cere ca o reemitere fara nicio modificare intentionata sa produca
|
|
**exact** aceleasi sume. Verificat pe fiecare ramura a `CASE`-ului de la `:5052-5220` (nu doar
|
|
ramura de contract):
|
|
|
|
| Ramura (`ntip`) | Garantat identic la reemitere? | De ce |
|
|
|---|---|---|
|
|
| `ELSE` (lista de preturi, fara contract) | **Da, cu o rezerva minora** | `V_PRET := V_PRET_TEMP` — passthrough direct (`:5200`). TVA vine din `JTVA_COLOANE` dupa `V_ID_JTVA_COLOANA` (`:5188-5198`) — identic **doar daca** acea coloana de TVA nu a fost ea insasi editata intre timp (posibil, dar alt tip de risc, nu cel cerut de aceasta sarcina). |
|
|
| `45` (restaurant) | **Da, cu aceeasi rezerva** | `V_PRET := V_PRET_TEMP` setat **inainte** de orice `SELECT` (`:5109`) — respectat necondiționat. TVA vine tot din `JTVA_COLOANE` (`:5114-5139`), aceeasi rezerva ca mai sus. |
|
|
| `3,21,28,42,47` (comenzi) | **Nu garantat, dar esueaza tare, nu tacit** | `SELECT ... WHERE A.PRET = V_PRET_TEMP` (`:5077`) — daca pretul din comanda-sursa nu mai coincide exact cu ce trimite VFP, **`NO_DATA_FOUND` netratat** → eroare Oracle bruta, regenerarea pica integral, nu produce o suma gresita silentios. Diferit structural de ramura de contract. |
|
|
| `4` (aviz) | **Nu garantat, silentios** | `SELECT DISTINCT A.PRET ... FROM VANZARI_DETALII` (`:5082-5103`), **fara filtru pe pret** — re-citeste pretul curent al liniei din avizul-sursa necondiționat. Daca avizul insusi a fost modificat intre emiterea initiala si reemitere, sau daca exista mai multe randuri candidate (`DISTINCT` pe o potrivire ambigua), reemiterea poate lua alt pret, tacit — **acelasi tipar de risc ca la contract**, dar pe alt document-sursa. |
|
|
| `V_OPT_FACTURARE=3` (contract) | **Nu garantat, silentios** | Sectiunea 1 — faptul portant. |
|
|
|
|
**Observatie in afara perimetrului cerut, dar direct relevanta**: daca #13 etapa II ajunge sa
|
|
regenereze si facturi emise initial din **aviz** (`ntip=4`), nu doar din contract, **acelasi tip
|
|
de risc silentios exista si acolo**, prin alt mecanism (re-citire necondiționata din
|
|
`VANZARI_DETALII` a avizului-sursa, fara filtru pe pret). Raportul vechi (`s10_pret_rederivat.md`,
|
|
sectiunea 4) semnalase deja asta ca "neverificat, in afara bugetului" — de data asta o marchez
|
|
explicit ca **decizie deschisa**: daca reemiterea din #13 se aplica si documentelor provenite din
|
|
aviz, aceeasi discutie (a/b/c/d) trebuie purtata si pentru acea ramura, cu propriul cost (sursa
|
|
de comparat e alt document, nu `CTR_ARTICOLE`). Nu am extins cercetarea de date pe aceasta ramura
|
|
(in afara cererii explicite a sarcinii, care a cerut "cazul confirmat", adica cel de contract).
|
|
|
|
**Concluzie sectiune**: criteriul "reemitere identica" e garantat azi doar pe ramurile
|
|
`ELSE`/restaurant (fara contract, fara comanda, fara aviz). Pe comenzi, o divergenta produce
|
|
eroare vizibila (rau, dar nu tacit). Pe contract **si** pe aviz, o divergenta produce o suma
|
|
gresita fara nicio semnalare — acestea sunt cele doua ramuri care au nevoie de o decizie explicita
|
|
de tipul (a)-(d) inainte ca #13 etapa II sa poata pretinde ca satisface criteriul S12.
|
|
|
|
---
|
|
|
|
## 7. Ce ramane de decis de Marius
|
|
|
|
1. **(b) blocare stricta vs. (c) avertizare+confirmare** — (c) satisface literal criteriul "decis
|
|
explicit, nu accidental" cerut de plan si nu intrerupe fluxul quasi-niciodata (doar cand chiar
|
|
exista divergenta); (b) e mai sigur (niciodata nu se schimba nimic fara interventie manuala pe
|
|
contract) dar mai frustrant daca divergentele sunt frecvente in productie — lucru nemasurabil
|
|
azi (sectiunea 2). **Recomand (c)** ca implicit, cu mesajul aratand explicit delta de
|
|
pret/TVA/valuta (sectiunea 3) — nu doar pretul.
|
|
2. **Garda trebuie sa acopere pret + TVA + valuta impreuna**, nu doar pretul — confirmat la
|
|
sectiunea 3 ca vin din acelasi `SELECT`. O implementare care verifica doar pretul ar lasa
|
|
trecerea tacuta a unei schimbari de TVA sau valuta.
|
|
3. **Varianta (d) (ocolire) — nu o recomand**, din cauza pierderii permanente a legaturii
|
|
`VANZARI_DETALII.ID_CTR`. Daca Marius considera totusi ca merita costul (de ex. daca
|
|
trasabilitatea contract-linie pe documentele reemise nu conteaza in practica), e o decizie de
|
|
produs explicita, nu tehnica — semnalez aici, nu decid.
|
|
4. **Ramura de aviz (`ntip=4`) are acelasi tip de risc silentios** (sectiunea 6) — de decis daca
|
|
intra in perimetrul #13 etapa II acum sau se trateaza separat, cu propriul studiu de fezabilitate
|
|
pentru garda (sursa de comparat difera fata de contract).
|
|
5. **Frecventa reala in productie a divergentei `CTR_ARTICOLE.PRET_UNITAR`** ramane nemasurabila
|
|
din baza de dezvoltare (27 randuri total) — daca decizia (b) vs (c) depinde de cat de des s-ar
|
|
declansa garda, ar trebui repetata interogarea din sectiunea 2 pe o baza cu volum de productie
|
|
inainte de implementare.
|
|
|
|
---
|
|
|
|
## STARE / CE RAMANE
|
|
|
|
**Livrabil complet** — toate cele 6 puncte cerute in brief sunt acoperite (sectiunile 1-6), plus
|
|
recomandare argumentata (sectiunea 4, cu tabel comparativ) si lista de decizii deschise
|
|
(sectiunea 7).
|
|
|
|
Nimic ramas in lucru; nicio verificare intrerupta. Singurele limitari explicite:
|
|
- esantionul de date (27 randuri `CTR_ARTICOLE`, baza de dezvoltare) nu se extrapoleaza la
|
|
productie (sectiunea 2);
|
|
- ramura de aviz (`ntip=4`) e semnalata ca avand acelasi tip de risc, dar nu a primit acelasi nivel
|
|
de cercetare (nu era in perimetrul cerut) — vezi sectiunea 6, ultimul paragraf inainte de
|
|
concluzie.
|