#13: formular de facturare unificat - etapa curenta
Squash al branch-ului de lucru plan13-s2. Cod: clasa noua ofacturare_util (geometrie si utilitare de formular), inrolata in roafacturare.prg; meniul "Facturare (nou)" plat, cu reparatia literalului peste limita VFP de 255 de caractere care impiedica compilarea metodei. Livrare: changelog 2.11.19, marcajul de versiune DB pentru scripturile finale din SCRIPTURI_CLAR\2026\09, inventarul livrarii in docs/livrare_13.md si registrul de erori deschise. Documentatia de lucru a etapei (cercetare, rapoarte, handoff-uri, snapshot-uri de scripturi si backup-uri de rollback) a fost scoasa din arbore. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PkyjGyrV2S7932om4kfSiK
This commit is contained in:
@@ -1,468 +0,0 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user