232 lines
15 KiB
Markdown
232 lines
15 KiB
Markdown
# Cercetare Oracle S5/S6/S7 (plan #6 - editare factura emisa)
|
|
|
|
Sursa: export proaspat din `MARIUSM_AUTO@ROA_CENTRAL` (`all_source`), facut in aceasta sesiune
|
|
(08.08.2026), NU copia veche de pe disc din martie. Inainte de export s-a verificat tabela
|
|
`VERSIUNE`: ultimul script aplicat e `ff_2026_08_06_12_COMUN_VANZARI_BACKFILL.sql`, identic cu
|
|
`versiune_db.txt` din radacina proiectului (`2026_08_06_12`) - MARIUSM_AUTO e la zi.
|
|
|
|
Numerele de linie de mai jos sunt din exportul acestei sesiuni (`PACK_FACTURARE.pck` = 17010
|
|
linii, `PACK_CONTAFIN.pck` = 9041 linii, `PACK_DOCUMENTE.pck` = 96 linii), NU din
|
|
`ff_2026_03_23_04_COMUN_PACK_FACTURARE.sql` de pe disc, care e vechi si nu contine modificarile de
|
|
la #8. Fisierele exportate au ramas in scratchpad-ul sesiunii, nu sunt commise.
|
|
|
|
## A. Starea reala de azi
|
|
|
|
### A.1 `actualizeaza_vanzari` (PACK_FACTURARE.pck:16015-16025)
|
|
|
|
Confirmat: face EXCLUSIV realinierea `cod` + `STERS=0`, nimic altceva.
|
|
|
|
```
|
|
UPDATE VANZARI_DETALII SET STERS = 0
|
|
WHERE ID_VANZARE IN (SELECT ID_VANZARE FROM VANZARI WHERE COD = V_COD_VECHI);
|
|
UPDATE VANZARI SET COD = V_COD_NOU, STERS = 0 WHERE COD = V_COD_VECHI;
|
|
```
|
|
|
|
Important, nu era explicit in plan: al doilea `UPDATE` schimba `COD` pe ACELASI rand din `VANZARI`
|
|
(filtrat dupa vechiul cod) - `ID_VANZARE` (cheia primara) NU se schimba niciodata la editare. Asta
|
|
conteaza direct pentru sectiunea C. Comentariul inline de la `:16018` ("de modificat in caz ca il
|
|
las sa stearga manual inregistrari din VANZARI_DETALII") confirma ca extinderea era anticipata.
|
|
|
|
### A.2 `PACK_CONTAFIN.finalizeaza_modificare_nota` / `finalizeaza_stergere_nota`
|
|
|
|
`finalizeaza_modificare_nota` (PACK_CONTAFIN.pck:8601-8651): cheama
|
|
`pack_facturare.actualizeaza_vanzari(tnCod, lnCodNou)` DOAR daca
|
|
`SELECT COUNT(*) FROM vanzari WHERE cod = tnCod` > 0 (`:8613-8617`). Daca `cod`-ul vechi nu exista
|
|
in `vanzari` (nota nu vine dintr-o factura), pasul e sarit tacut, fara eroare - comportament corect
|
|
pentru orice alt tip de nota (ROAGEST/ROACONT).
|
|
|
|
`finalizeaza_stergere_nota` (`:8653-8709`) e simetric: cheama `sterge_din_vanzari` doar daca
|
|
`cod`-ul exista in vanzari. Spre deosebire de `actualizeaza_vanzari`,
|
|
`sterge_din_vanzari` (PACK_FACTURARE.pck:16027-16042) cauta `ID_VANZARE` dupa `cod` si, daca nu-l
|
|
gaseste, ridica `RAISE_APPLICATION_ERROR(-20000, ... FACT-017 ...)` - dar acest caz nu poate aparea
|
|
in fluxul normal, fiindca apelul e deja gardat de count-ul de mai sus.
|
|
|
|
### A.3-A.4 `scrie_in_vanzari` (PACK_FACTURARE.pck:13491-13956) - formula de calcul
|
|
|
|
Extrasa integral (bloc `SELECT INTO` la `:13763-13931`, `UPDATE VANZARI` la `:13933-13949`).
|
|
Coloane denormalizate scrise: `discount_tva, valoare_achizitie, total_fara_tva, total_tva,
|
|
total_cu_tva, valval, tvaval, totval, id_valuta, curs, multiplicator, serie_incasat, nr_incasat,
|
|
suma_incasat, tip_incasat` (14 coloane).
|
|
|
|
Sursa datelor: **`VANZARI_DETALII_TEMP`** (tabela globala temporara de sesiune, NU
|
|
`VANZARI_DETALII`) + `VANZARI_SETURI_TEMP` (pentru linii-set) + `VANZARI_CURSURI` (deja scrisa cu
|
|
`id_vanzare`-ul curent, la `:13704`, prin `scrie_cursuri`). `V_DISCOUNT_FACTURA` e parametru
|
|
explicit al procedurii (discountul de pe document), nu o coloana citita din `VANZARI`.
|
|
|
|
Formula per-linie e deja extrasa in doua FUNCTII REUTILIZABILE, pure, cu parametri expliciti -
|
|
NU inline: `calculeaza_total_fara_tva_fact` / `calculeaza_total_tva_fact`
|
|
(PACK_FACTURARE.pck:15858-16013). Ambele ramifica explicit pe `V_PRET_CU_TVA` (`:15871`, `:15976`),
|
|
apeland `calculeaza_total_cu_tva_fact` cand flagul e 1. Deci **partea de calcul per linie e deja
|
|
partajabila** - nu trebuie reinventata.
|
|
|
|
Ce NU e extras e blocul de AGREGARE (suma pe toate liniile documentului, tratarea liniilor din
|
|
seturi, alegerea `curs`/`multiplicator`/`id_valuta` din `vanzari_cursuri`), care e inline in
|
|
`scrie_in_vanzari` si depinde de:
|
|
- `VANZARI_DETALII_TEMP` ca sursa (nu `VANZARI_DETALII`);
|
|
- stare de sesiune pe pachet: `pack_facturare.nin_valuta`, `ndiscount_evidentiat`,
|
|
`cserie_act_incasare` / `nnumar_act_incasare` / `nsuma_incasare` / `ntip_doc_incasare` (acestea
|
|
din urma populate DOAR la emiterea unei facturi-cu-incasare combinata - nu au sens la o editare
|
|
ulterioara, vezi C);
|
|
- `pack_def.GetIdMonedaNationala()`.
|
|
|
|
**Cine mai apeleaza `scrie_in_vanzari`**: doar 2 locuri, ambele INSERT-then-populate cu
|
|
`RETURNING ID_VANZARE`: `scrie_proforma` (`:5643`) si `finalizeaza_factura` (`:14790`). Extragerea
|
|
blocului de agregare intr-o functie interna parametrizata pe sursa (TEMP la emitere,
|
|
`VANZARI_DETALII` real la editare) e SIGURA fata de ambii apelanti, daca varianta "sursa=TEMP"
|
|
pastreaza exact comportamentul actual.
|
|
|
|
**Risc concret de reutilizare naiva pentru S5**: coloanele `serie_incasat/nr_incasat/
|
|
suma_incasat/tip_incasat` NU trebuie recalculate la editare - vin din stare de sesiune specifica
|
|
emiterii unei facturi-cu-incasare, fara nicio sursa persistenta din care sa fie reconstruite la o
|
|
editare ulterioara. O procedura noua care ar copia tot `UPDATE`-ul din `scrie_in_vanzari` le-ar
|
|
suprascrie cu `NULL` la fiecare editare, rupand legatura cu incasarea. Procedura propusa pentru S5
|
|
trebuie sa scrie DOAR cele 11 coloane de totaluri/curs, nu si aceste 4.
|
|
|
|
### A.5 Flagul `VANZARI_DETALII.PRET_CU_TVA`
|
|
|
|
Deja parte a calculului in Oracle, nu doar in VFP: valoarea intra in agregare din
|
|
`a1.pret_cu_tva <- vd.pret_cu_tva <- VANZARI_DETALII_TEMP.PRET_CU_TVA` pentru liniile directe
|
|
(`:13883`) sau din `VANZARI_SETURI_TEMP.pret_cu_tva` pentru liniile din seturi (`:13909`), apoi
|
|
`calculeaza_total_fara_tva_fact`/`calculeaza_total_tva_fact` ramifica pe el (`:15871`, `:15976`).
|
|
Orice extragere pentru S5 trebuie sa citeasca acelasi `VANZARI_DETALII.PRET_CU_TVA` per linie (deja
|
|
persistat, cf. #7) - nu exista alta sursa de adevar pentru el.
|
|
|
|
## B. Propunerea pentru S5
|
|
|
|
### Varianta A vs B
|
|
|
|
`actualizeaza_vanzari` e apelata NECONDITIONAT de `finalizeaza_modificare_nota` pentru ORICE
|
|
editare de nota al carei `cod` exista in `vanzari` - nu doar facturi din #6, ci orice tip de
|
|
document din VANZARI editat azi prin `frm_modific2024`/`afisjurcom.do_modifica`, folosit deja de
|
|
ROAGEST/ROACONT in registrul jurnal. Daca **Varianta A** (extinderea in-place a lui
|
|
`actualizeaza_vanzari` cu recalcul de totaluri) e aleasa, recalculul ar rula automat la ORICE
|
|
editare de nota cu `cod` in vanzari, inclusiv editari care azi nu ating deloc sumele (ex. doar
|
|
`explicatie`/`cont`). Risc de regresie: **mediu-mare**, extins la toata suita ROA, nu doar la #6.
|
|
|
|
**Varianta B** (procedura sora noua, ex. `recalculeaza_totaluri_vanzari`, apelata explicit din
|
|
`finalizeaza_modificare_nota` doar cand editarea a atins efectiv `VANZARI_DETALII`): risc **mic** -
|
|
`actualizeaza_vanzari` ramane neschimbata (zero impact pe restul aplicatiilor ROA care o folosesc
|
|
azi), noua procedura e un pas suplimentar, opt-in.
|
|
|
|
**Recomandare: Varianta B**, motivata exclusiv de riscul de regresie asupra editarilor de note care
|
|
NU sunt facturi si trec prin acelasi `finalizeaza_modificare_nota`.
|
|
|
|
### Schita procedurii propuse
|
|
|
|
```
|
|
PROCEDURE recalculeaza_totaluri_vanzari(V_ID_VANZARE IN NUMBER) IS
|
|
-- reia blocul de agregare din scrie_in_vanzari (:13763-13931), cu sursa
|
|
-- VANZARI_DETALII WHERE ID_VANZARE = V_ID_VANZARE AND STERS = 0
|
|
-- in loc de VANZARI_DETALII_TEMP; V_DISCOUNT_FACTURA citit din VANZARI.DISCOUNT
|
|
-- (coloana existenta pe randul curent, populata la INSERT din acelasi parametru, :13682)
|
|
BEGIN
|
|
SELECT discount, ... INTO lnDiscountFactura, ... FROM vanzari WHERE id_vanzare = V_ID_VANZARE;
|
|
-- acelasi SELECT agregat ca in scrie_in_vanzari, sursa VANZARI_DETALII in loc de _TEMP
|
|
UPDATE vanzari
|
|
SET discount_tva = ..., valoare_achizitie = ..., total_fara_tva = ...,
|
|
total_tva = ..., total_cu_tva = ..., valval = ..., tvaval = ..., totval = ...,
|
|
id_valuta = ..., curs = ..., multiplicator = ...
|
|
-- FARA serie_incasat/nr_incasat/suma_incasat/tip_incasat, vezi A.4
|
|
WHERE id_vanzare = V_ID_VANZARE;
|
|
END;
|
|
```
|
|
|
|
Apelata din `finalizeaza_modificare_nota` (PACK_CONTAFIN.pck:8601), dupa `actualizeaza_vanzari`.
|
|
|
|
### Mecanismul de scriere VFP in `VANZARI_DETALII` la emitere
|
|
|
|
Cautare in cache-ul text `COMUN` pentru `VANZARI_DETALII_TEMP` / `DETALII_TEMP` (case-insensitive):
|
|
**niciun rezultat**, inclusiv in `oscrie_in_fisiere.prg`. Tabela temp e populata probabil printr-un
|
|
helper generic de upload cursor->tabela (nume de tabela asamblat dinamic sau printr-un mecanism
|
|
care nu apare ca literal in sursa text). Nu am putut confirma static calea exacta - de cercetat
|
|
separat, pe partea VFP, inainte de proiectarea S4 (ce cursor/tabela temp foloseste editarea:
|
|
reutilizeaza `VANZARI_DETALII_TEMP` sau una noua).
|
|
|
|
### Idempotenta si tranzactionalitate
|
|
|
|
Fluxul de editare a notei ruleaza deja intr-o singura tranzactie manuala:
|
|
`Thisform.do_deschide_tranzactie()` (comun.vc2:2448) ... `oscrie_in_fisiere` + apelul catre
|
|
`finalizeaza_modificare_nota` (`:2484-2486`) ... `Thisform.do_inchide_tranzactie(...)` (`:2535`,
|
|
`COMMIT`/`ROLLBACK` in functie de succes). Procedura noua, apelata DIN INTERIORUL
|
|
`finalizeaza_modificare_nota`, mosteneste automat aceeasi tranzactie: la esec pe jumatate,
|
|
`ROLLBACK`-ul anuleaza tot (nota + `vanzari` + noile totaluri) - nu exista fereastra de
|
|
inconsistenta. `recalculeaza_totaluri_vanzari` propusa e ea insasi idempotenta ca `UPDATE ... WHERE
|
|
id_vanzare = :id` (poate rula de mai multe ori pe acelasi id fara efect cumulativ, spre deosebire
|
|
de un `INSERT`).
|
|
|
|
## C. S6 - legaturile care depind de `cod`
|
|
|
|
Verificat pe cod (nu date, dat fiind ca `MARIUSM_AUTO` are date de test):
|
|
|
|
| Legatura | Cheie reala | Ramane valida? |
|
|
|---|---|---|
|
|
| `vanzari_coresp` | `ID_VANZARE_FACT`/`ID_VANZARE_AVIZ` (coloane confirmate din `all_tab_columns`) | DA - `actualizeaza_vanzari` nu schimba `ID_VANZARE` (vezi A.1), doar `COD` pe acelasi rand |
|
|
| `facturat` (aviz/comanda, `marcheaza_facturat`) | `ID_VANZARE`, exclusiv (PACK_FACTURARE.pck:15384-15421, foloseste doar `pack_facturare.nid_vanzare`, niciodata `cod`) | DA |
|
|
| chitanta/incasare (`SERIE_INCASAT`/`NR_INCASAT`/`SUMA_INCASAT`/`TIP_INCASAT`) | denormalizate direct pe randul `VANZARI` (nu FK), scrise o singura data la `scrie_in_vanzari` (`:13777-13780`) | DA - `actualizeaza_vanzari` nu le atinge; raman la valoarea de la emitere, ceea ce e comportamentul corect (nu se recalculeaza la editare de sume, vezi A.4/B) |
|
|
| `ReferinteDocumenteNota`/`ReferinteDocument` (PACK_DOCUMENTE.pck:25-93) | `ACT.id_factd`/`id_factc` = `id_fact`-ul documentului, filtrat `cod <> tnCod` | DA - e o VERIFICARE PRE-EDITARE (gate, apelata inainte sa se schimbe ceva), nu o legatura persistenta; nu depinde de `cod`-ul rezultat dupa editare |
|
|
| `anaf_efactura` | `ID_FACT` (coloana confirmata) | DA - `id_fact` nu se schimba la editare (by design, deja stabilit in plan) |
|
|
| `documente` | `DOCUMENTE.ID_DOC = ACT.ID_FACT` (confirmat din `MERGE INTO DOCUMENTE ... ON A.ID_DOC = B.ID_DOC` unde `B.ID_DOC` vine din `ACT_TEMP.ID_FACT`, PACK_CONTAFIN.pck:858-880) | DA - si se REACTUALIZEAZA automat (SERIE_ACT/NRACT/DATAACT) la fiecare scriere de nota, inclusiv editare, prin acelasi `MERGE` care ruleaza in `cumuleaza_note_act` |
|
|
|
|
**Concluzie generala**: toate legaturile intre note contabile trec prin `ID_FACT`
|
|
(`ACT.id_factd/id_factc`, `DOCUMENTE.id_doc`, `ANAF_EFACTURA.id_fact`), niciodata prin `cod`. Cum
|
|
#6 pastreaza `id_fact` neschimbat by design, toate raman valide fara nicio interventie
|
|
suplimentara. Singura legatura care trece prin altceva (`ID_VANZARE`, PK-ul randului `VANZARI`) e
|
|
`vanzari_coresp`/`marcheaza_facturat`, si acesta ramane la fel neschimbat (doar `COD` se rescrie pe
|
|
acelasi rand, vezi A.1). **S6 se poate inchide ca "confirmat pe cod, fara lucru suplimentar
|
|
necesar"**, nu doar ca "de verificat".
|
|
|
|
## D. S7 - rotunjirea la reeditare
|
|
|
|
`verifica_total_document` (PACK_FACTURARE.pck:16073-...) insereaza o linie de corectie in
|
|
`ACT_TEMP` cand suma recalculata din `ACT_TEMP` insusi (`V_TOTFTVA_VER`/`V_TOTTVA_VER`) difera de
|
|
`pack_facturare.ntotftva`/`ntottva` (valori calculate in VFP, trecute prin stare de sesiune inainte
|
|
de scriere). `ACT_TEMP` e complet repopulat la fiecare scriere - procedura nu "tine minte" nimic
|
|
intre apeluri, in Oracle.
|
|
|
|
**Risc identificat pe partea VFP**: la editare, `afisjurcom.do_modifica` (comun.vc2:2352-2366)
|
|
incarca in cursorul `tact` TOATE randurile curente ale notei
|
|
(`SELECT * FROM vact_tot WHERE cod = ... [AND STERS = 0] ORDER BY id_act`) - asta include si linia
|
|
de corectie inserata la salvarea anterioara (e un rand `ACT` normal, fara marcaj special care sa o
|
|
distinga). La resalvare, acest rand curge inapoi prin `oscrie_in_fisiere` in `ACT_TEMP` impreuna cu
|
|
liniile editate de utilizator, iar `verifica_total_document` ruleaza din nou pe noul `ACT_TEMP`
|
|
(care deja contine vechea corectie).
|
|
|
|
**Nu se poate decide static** daca rezultatul e o a doua corectie suprapusa peste prima, sau daca
|
|
mecanismul e self-consistent (adica `V_TOTFTVA_VER` deja include vechea corectie in suma agregata,
|
|
iar `pack_facturare.ntotftva` recalculat de VFP coincide, deci nu se mai adauga nimic) - depinde de
|
|
cum recalculeaza VFP `ntotftva`/`ntottva` la editare, cod care nu a fost verificat in aceasta trecere
|
|
(afara de scope-ul Oracle-only al acestei cercetari).
|
|
|
|
De notat: acest mecanism NU e nou pentru #6 - e folosit azi neschimbat de orice editare de nota
|
|
prin `do_modifica` (ROAGEST/ROACONT registru jurnal), pe orice tip de document, de ani de zile.
|
|
Daca ar acumula corectii sistematic la editari repetate, ar fi deja o problema cunoscuta pe editari
|
|
non-factura. Asta scade riscul, dar nu-l elimina pentru cazul specific facturii, unde S4/S5
|
|
introduc o cale noua de a schimba cantitati/preturi care nu exista azi in `do_modifica` generic (pe
|
|
notele generice azi de regula nu se schimba baza de calcul TVA in acelasi fel).
|
|
|
|
**Raspuns**: nu se poate decide din cod - testul deja propus in plan (S7: trei editari consecutive,
|
|
verifica nr. de linii de corectie in `ACT`) e calea corecta si suficienta; nu exista scurtatura
|
|
statica.
|
|
|
|
## E. Intrebari deschise
|
|
|
|
1. **Semnatura procedurii noi**: apel neconditionat din `finalizeaza_modificare_nota` oricand
|
|
`cod`-ul exista in `vanzari` (simplu, cost mic - un `SELECT`+`UPDATE` in plus si pe editari care
|
|
nu ating articolele), sau flag explicit `tnAtinsArticole` trecut din VFP (evita lucru inutil, dar
|
|
risc de flag uitat/gresit)? **Recomandare: apel neconditionat** - cost neglijabil, elimina o
|
|
clasa de bug.
|
|
2. **Discountul de document la editare**: `V_DISCOUNT_FACTURA` la emitere e parametru explicit din
|
|
VFP; planul S4/S4b nu mentioneaza editarea discountului de pe document, doar cantitate/pret/
|
|
`pret_cu_tva` pe linie. La S5, discountul se citeste din `VANZARI.DISCOUNT` (neschimbat) - de
|
|
confirmat cu Marius ca asta e comportamentul dorit (discountul de document ramane needitabil in
|
|
#6).
|
|
3. **`VALVAL`/`TVAVAL`/`TOTVAL`** (totaluri in valuta): planul S5 mentioneaza explicit doar
|
|
`total_fara_tva`/`total_tva`/`total_cu_tva`/`valoare_achizitie`/`discount_tva`. Fac parte din
|
|
acelasi bloc de agregare din `scrie_in_vanzari` - **recomandare: le includem in recalcul**, cost
|
|
suplimentar zero, evita inconsistenta pe documentele in valuta straina.
|
|
4. **Mecanismul de populare al `VANZARI_DETALII_TEMP`** la emitere n-a putut fi gasit static in
|
|
cache-ul text `COMUN` (cautare literal, fara rezultate) - necesita cercetare VFP separata inainte
|
|
de a proiecta calea exacta de scriere pentru editarea din S4 (aceeasi tabela temp, sau una noua).
|
|
5. **S7**: raspunsul cere test dinamic (3 editari consecutive), nu poate fi confirmat static - de
|
|
pastrat explicit in scope-ul S8, nu doar "de verificat" generic in text.
|