docs: cercetarile trans-proiect din ROAFACTURARE trec in COMUN
Paisprezece rapoarte care nu erau ale ROAFACTURARE traiau in docs\cercetare\ al acelui proiect: valuta si curs in ofacturare, TVA calculat vs salvat plus denormalizarea VANZARI, inventarul consumatorilor VANZARI din toata suita ROA, integrarile de contracte/politici/nomenclator (#10, #11, #12), watchdog-ul VFP de testare, proiectarea Oracle a scrierii din S5 si view-ul VVANZARI_ARTICOLE. Motivul mutarii, nu doar al pastrarii: planurile #10, #11 si #12 sunt amanate, iar indexul lor spune explicit ca se reiau din aceste rapoarte - deci sunt punct de plecare, nu istoric. Iar tinta lor e COMUN plus ROAPRETURI/ROACONTRACTE. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SN8snvkk94KuhWwoXUUey3
This commit is contained in:
780
docs/cercetare/rec_s5_proiectare_oracle.md
Normal file
780
docs/cercetare/rec_s5_proiectare_oracle.md
Normal file
@@ -0,0 +1,780 @@
|
||||
# Cercetare S5 — proiectarea partii Oracle (plan #6, editare factura emisa)
|
||||
|
||||
Data: 09.08.2026. Sursa: export PROASPAT din `MARIUSM_AUTO@ROA_CENTRAL` (`all_source`), facut in
|
||||
aceasta sesiune, in scratchpad (nu in proiect):
|
||||
|
||||
- `PACK_FACTURARE.pck` — 16160 linii
|
||||
- `PACK_CONTAFIN.pck` — 8550 linii
|
||||
|
||||
**Toate numerele de linie de mai jos sunt din ACEST export.** Raportul precedent
|
||||
(`rec_s5_oracle_vanzari.md`, 08.08.2026) declara `PACK_FACTURARE.pck = 17010` linii; offset-urile
|
||||
procedurilor coincid totusi exact (`scrie_in_vanzari` la `:13491`, `actualizeaza_vanzari` la
|
||||
`:16015`), deci diferenta e de formatare a spool-ului, nu de continut. Concluziile lui A.1-A.2, C si
|
||||
D raman valabile pe sursa de azi si nu se repeta aici.
|
||||
|
||||
**Doua corectii de fond fata de raportul precedent** (detaliate la 3 si 4):
|
||||
|
||||
1. agregarea NU citeste `VANZARI_SETURI_TEMP`, ci tabela **persistenta `VANZARI_SETURI`**
|
||||
(`:13916`) — deci liniile de set se pot reconstitui integral la editare;
|
||||
2. recomandarea „procedura noua apelata din interiorul `finalizeaza_modificare_nota`" e
|
||||
**incompatibila cu ordinea corecta de scriere** si trebuie abandonata.
|
||||
|
||||
---
|
||||
|
||||
## 1. Baza de plecare e la zi? DA (pentru `ff_`)
|
||||
|
||||
Comparatie `VERSIUNE` (4151 randuri) vs. `D:\ROA\DATABASE\SCRIPTURI_CLAR` (3030 fisiere `.sql`):
|
||||
|
||||
- **`ff_` pe disc dar neaplicate in `MARIUSM_AUTO`: 0.** Schema e la zi pe tot ce o priveste.
|
||||
- Ultimul `ff_` inregistrat: `ff_2026_08_08_01_COMUN_VVANZARI_ARTICOLE.sql`, identic cu
|
||||
`versiune_db.txt` din radacina proiectului (`2026_08_08_01`).
|
||||
- Cele 26 de scripturi din 2026 care lipsesc din `VERSIUNE` sunt **exclusiv `co_` si `sys_`** — se
|
||||
aplica pe `CONTAFIN_ORACLE`, respectiv `SYS`, nu pe schema firmei; `VERSIUNE` din `MARIUSM_AUTO`
|
||||
contine doar 3 randuri `co_`, deci absenta lor e normala, nu o restanta.
|
||||
|
||||
**Verdict: se poate construi propunerea peste sursa exportata azi.**
|
||||
|
||||
### Doua anomalii de semnalat (nu blocheaza S5)
|
||||
|
||||
- **`ff_2026_08_08_01_COMUN_VVD_TOT.sql` e inregistrat in `VERSIUNE` dar NU exista nicaieri pe
|
||||
disc** (cautat recursiv in tot `SCRIPTURI_CLAR`). Un obiect aplicat in dev fara script salvat nu
|
||||
ajunge niciodata la clienti. De verificat cu Marius daca e un script de lucru abandonat sau daca
|
||||
lipseste din SVN.
|
||||
- Acelasi `NN` (`2026_08_08_01`) e folosit de doua scripturi `ff_` (`VVANZARI_ARTICOLE` si
|
||||
`VVD_TOT`), contra regulii „NN e secventa unica pe zi".
|
||||
|
||||
---
|
||||
|
||||
## 2. Blocul de agregare din `scrie_in_vanzari` — integral
|
||||
|
||||
Procedura: `PACK_FACTURARE.pck:13491-13956`. Blocul de agregare + `UPDATE VANZARI` e
|
||||
`:13762-13954`, citat integral:
|
||||
|
||||
```
|
||||
13762 -- Completare totaluri in vanzari din vanzari_detalii pentru a usura selectiile din fact_vfacturi
|
||||
13763 begin
|
||||
13764 select DISC_TVA_VAL AS DISCOUNT_TVA,
|
||||
13765 VALOARE_ACHIZITIE,
|
||||
13766 a.suma_fara_tva_ron - a.disc_fara_tva_ron as TOTAL_FARA_TVA,
|
||||
13767 a.suma_tva_ron - a.disc_tva_ron as TOTAL_TVA,
|
||||
13768 a.suma_fara_tva_ron - a.disc_fara_tva_ron + a.suma_tva_ron -
|
||||
13769 a.disc_tva_ron as TOTAL_CU_TVA,
|
||||
13770 a.suma_fara_tva_val - a.disc_fara_tva_val as VALVAL,
|
||||
13771 a.suma_tva_val - a.disc_tva_val as TVAVAL,
|
||||
13772 a.suma_fara_tva_val - a.disc_fara_tva_val + a.suma_tva_val -
|
||||
13773 a.disc_tva_val as TOTVAL,
|
||||
13774 id_valuta,
|
||||
13775 curs,
|
||||
13776 multiplicator,
|
||||
13777 pack_facturare.cserie_act_incasare as SERIE_INCASAT,
|
||||
13778 pack_facturare.nnumar_act_incasare as NR_INCASAT,
|
||||
13779 pack_facturare.nsuma_incasare AS SUMA_INCASAT,
|
||||
13780 pack_facturare.ntip_doc_incasare as TIP_INCASAT
|
||||
13781 INTO lnDiscountTVA,
|
||||
13782 lnValoareAchizitie,
|
||||
13783 lnTotalFaraTVA,
|
||||
13784 lnTotalTVA,
|
||||
13785 lnTotalCuTVA,
|
||||
13786 lnValVal,
|
||||
13787 lnTVAVal,
|
||||
13788 lnTotVal,
|
||||
13789 lnIdValuta,
|
||||
13790 lnCurs,
|
||||
13791 lnMultiplicator,
|
||||
13792 lnSerieIncasat,
|
||||
13793 lnNrIncasat,
|
||||
13794 lnSumaIncasat,
|
||||
13795 lnTipIncasat
|
||||
13796 FROM (select MAX(decode(pack_facturare.nin_valuta,
|
||||
13797 1,
|
||||
13798 ROUND(a1.curs * NVL(V_DISCOUNT_FACTURA, 0) /
|
||||
13799 a1.multiplicator,
|
||||
13800 lnPreciziePretV),
|
||||
13801 NVL(V_DISCOUNT_FACTURA, 0))) as DISC_FARA_TVA_RON,
|
||||
13802 NVL(V_DISCOUNT_FACTURA, 0) as DISC_FARA_TVA_VAL,
|
||||
13803 pack_facturare.nin_valuta AS IN_VALUTA,
|
||||
13804 MAX(ROUND(decode(pack_facturare.nin_valuta,
|
||||
13805 1,
|
||||
13806 ROUND(a1.curs *
|
||||
13807 NVL(V_DISCOUNT_FACTURA, 0) /
|
||||
13808 a1.multiplicator,
|
||||
13809 lnPreciziePretV),
|
||||
13810 NVL(V_DISCOUNT_FACTURA, 0)) *
|
||||
13811 (a1.proc_tvav - 1),
|
||||
13812 lnPreciziePretV)) as DISC_TVA_RON,
|
||||
13813 MAX(ROUND(NVL(V_DISCOUNT_FACTURA, 0) *
|
||||
13814 (a1.proc_tvav - 1),
|
||||
13815 lnPreciziePretV)) as DISC_TVA_VAL,
|
||||
13816 sum(pack_facturare.calculeaza_total_fara_tva_fact(a1.pret_ron,
|
||||
13817 0,
|
||||
13818 1,
|
||||
13819 NVL(a1.discount_unitar_ron,
|
||||
13820 0),
|
||||
13821 pack_facturare.ndiscount_evidentiat,
|
||||
13822 a1.cantitate,
|
||||
13823 a1.pret_cu_tva,
|
||||
13824 a1.proc_tvav)) AS SUMA_FARA_TVA_RON,
|
||||
13825 sum(pack_facturare.calculeaza_total_tva_fact(a1.pret_ron,
|
||||
13826 0,
|
||||
13827 1,
|
||||
13828 NVL(a1.discount_unitar_ron,
|
||||
13829 0),
|
||||
13830 pack_facturare.ndiscount_evidentiat,
|
||||
13831 a1.cantitate,
|
||||
13832 a1.pret_cu_tva,
|
||||
13833 a1.proc_tvav)) as SUMA_TVA_RON,
|
||||
13834 sum(pack_facturare.calculeaza_total_fara_tva_fact(a1.pret_val,
|
||||
13835 0,
|
||||
13836 1,
|
||||
13837 NVL(a1.discount_unitar_val,
|
||||
13838 0),
|
||||
13839 pack_facturare.ndiscount_evidentiat,
|
||||
13840 a1.cantitate,
|
||||
13841 a1.pret_cu_tva,
|
||||
13842 a1.proc_tvav)) AS SUMA_FARA_TVA_VAL,
|
||||
13843 sum(pack_facturare.calculeaza_total_tva_fact(a1.pret_val,
|
||||
13844 0,
|
||||
13845 1,
|
||||
13846 NVL(a1.discount_unitar_val,
|
||||
13847 0),
|
||||
13848 pack_facturare.ndiscount_evidentiat,
|
||||
13849 a1.cantitate,
|
||||
13850 a1.pret_cu_tva,
|
||||
13851 a1.proc_tvav)) as SUMA_TVA_VAL,
|
||||
13852 sum(round(a1.cantitate * a1.pret_achizitie,
|
||||
13853 lnPrecizieCalcul)) AS VALOARE_ACHIZITIE,
|
||||
13854 max(decode(pack_facturare.nin_valuta, 1, a1.id_valuta, 0)) as id_valuta,
|
||||
13855 max(decode(pack_facturare.nin_valuta, 1, a1.curs, 1)) as curs,
|
||||
13856 max(decode(pack_facturare.nin_valuta, 1, a1.multiplicator, 1)) as multiplicator
|
||||
13857 from (select vd.id_vanzare_set,
|
||||
13858 (case
|
||||
13859 when (pack_facturare.nin_valuta = 1 or
|
||||
13860 vd.id_valuta <>
|
||||
13861 pack_def.GetIdMonedaNationala()) then
|
||||
13862 ROUND(vc.curs * vd.pret / vc.multiplicator,
|
||||
13863 lnPreciziePretV)
|
||||
13864 else
|
||||
13865 vd.pret
|
||||
13866 end) as pret_ron,
|
||||
13867 vd.pret as pret_val,
|
||||
13868 vd.proc_tvav,
|
||||
13869 vd.cantitate,
|
||||
13870 vd.diferenta,
|
||||
13871 (case
|
||||
13872 when (pack_facturare.nin_valuta = 1 or
|
||||
13873 vd.id_valuta <>
|
||||
13874 pack_def.GetIdMonedaNationala()) then
|
||||
13875 ROUND(vc.curs * vd.discount_unitar /
|
||||
13876 vc.multiplicator,
|
||||
13877 lnPreciziePretV)
|
||||
13878 else
|
||||
13879 vd.discount_unitar
|
||||
13880 end) as discount_unitar_ron,
|
||||
13881 vd.discount_unitar as discount_unitar_val,
|
||||
13882 vd.id_valuta,
|
||||
13883 vd.pret_cu_tva,
|
||||
13884 vd.pret_achizitie,
|
||||
13885 vc.curs,
|
||||
13886 vc.multiplicator
|
||||
13887 from (select a.id_vanzare_set,
|
||||
13888 a.pret,
|
||||
13889 a.proc_tvav,
|
||||
13890 a.cantitate,
|
||||
13891 a.diferenta,
|
||||
13892 a.discount_unitar,
|
||||
13893 a.id_valuta,
|
||||
13894 a.pret_cu_tva,
|
||||
13895 a.pret_achizitie
|
||||
13896 from VANZARI_DETALII_TEMP a
|
||||
13897 where nvl(a.id_vanzare_set, 0) = 0
|
||||
13898 union all
|
||||
13899 select b.id_vanzare_set,
|
||||
13900 b.pret,
|
||||
13901 max(c.proc_tvav) as proc_tvav,
|
||||
13902 b.cantitate,
|
||||
13903 0 as diferenta,
|
||||
13904 b.discount_unitar,
|
||||
13905 decode(pack_facturare.nin_valuta,
|
||||
13906 0,
|
||||
13907 pack_def.GetIdMonedaNationala(),
|
||||
13908 c.id_valuta) as id_valuta,
|
||||
13909 b.pret_cu_tva,
|
||||
13910 sum(decode(b.cantitate,
|
||||
13911 0,
|
||||
13912 0,
|
||||
13913 c.pret_achizitie * c.cantitate /
|
||||
13914 b.cantitate)) as pret_achizitie
|
||||
13915 from vanzari_detalii_temp c
|
||||
13916 left join vanzari_seturi b
|
||||
13917 on b.id_vanzare_set = c.id_vanzare_set
|
||||
13918 where nvl(c.id_vanzare_set, 0) <> 0
|
||||
13919 and nvl(pack_facturare.nin_valuta, -1) > -1
|
||||
13920 group by b.id_vanzare_set,
|
||||
13921 b.pret,
|
||||
13922 b.cantitate,
|
||||
13923 b.discount_unitar,
|
||||
13924 b.pret_cu_tva,
|
||||
13925 decode(pack_facturare.nin_valuta,
|
||||
13926 0,
|
||||
13927 pack_def.GetIdMonedaNationala(),
|
||||
13928 c.id_valuta)) vd
|
||||
13929 left join vanzari_cursuri vc
|
||||
13930 on vc.id_vanzare = V_ID_VANZARE
|
||||
13931 and vd.id_valuta = vc.id_valuta) a1) a;
|
||||
13932
|
||||
13933 update vanzari
|
||||
13934 set discount_tva = lnDiscountTVA,
|
||||
13935 valoare_achizitie = lnValoareAchizitie,
|
||||
13936 total_fara_tva = lnTotalFaraTVA,
|
||||
13937 total_tva = lnTotalTVA,
|
||||
13938 total_cu_tva = lnTotalCuTVA,
|
||||
13939 valval = lnValVal,
|
||||
13940 tvaval = lnTVAVal,
|
||||
13941 totval = lnTotVal,
|
||||
13942 id_valuta = lnIdValuta,
|
||||
13943 curs = lnCurs,
|
||||
13944 multiplicator = lnMultiplicator,
|
||||
13945 serie_incasat = lnSerieIncasat,
|
||||
13946 nr_incasat = lnNrIncasat,
|
||||
13947 suma_incasat = lnSumaIncasat,
|
||||
13948 tip_incasat = lnTipIncasat
|
||||
13949 where id_vanzare = V_ID_VANZARE;
|
||||
13950
|
||||
13951 exception
|
||||
13952 when NO_DATA_FOUND then
|
||||
13953 null;
|
||||
13954 end;
|
||||
```
|
||||
|
||||
Variabilele locale relevante, declarate la `:13501-13523`:
|
||||
|
||||
```
|
||||
13522 lnPrecizieCalcul NUMBER(2) := pack_sesiune.getOptiuneFirma('PC');
|
||||
13523 lnPreciziePretV NUMBER(2) := pack_sesiune.getOptiuneFirma('PPRETV');
|
||||
```
|
||||
|
||||
### Inventarul dependentelor blocului
|
||||
|
||||
| Dependenta | Linii | Sursa la emitere | Echivalent PERSISTENT la editare | Verdict |
|
||||
|---|---|---|---|---|
|
||||
| `V_DISCOUNT_FACTURA` | 13798, 13801, 13802, 13807, 13810, 13813 | parametru al procedurii | **`VANZARI.DISCOUNT`** — scrisa la INSERT din exact acelasi parametru (`:13621` / `:13682`) | reconstituibil |
|
||||
| `pack_facturare.nin_valuta` | 13796, 13803, 13804, 13854-13856, 13859, 13872, 13905, 13919, 13925 | stare de sesiune pe pachet | **`VANZARI.IN_VALUTA`** (`NUMBER(1)`, NOT NULL) — scrisa la INSERT din aceeasi variabila (`:13625` / `:13686`) | reconstituibil |
|
||||
| `pack_facturare.ndiscount_evidentiat` | 13821, 13830, 13839, 13848 | stare de sesiune pe pachet | **`VANZARI.DISCOUNT_EVIDENTIAT`** — scrisa la INSERT din aceeasi variabila (`:13622` / `:13683`) | reconstituibil |
|
||||
| `cserie_act_incasare`, `nnumar_act_incasare`, `nsuma_incasare`, `ntip_doc_incasare` | 13777-13780 | stare de sesiune, setata numai in fluxul de emitere (`:13102-13144`; resetate la `:1882-1886`) | **NICIUNUL** | vezi 6 |
|
||||
| `pack_def.GetIdMonedaNationala()` | 13861, 13874, 13907, 13927 | functie pura: `SELECT MIN(ID_VALUTA) FROM NOM_VALUTE WHERE STERS=0 AND MONEDA_NATIONALA=1` (PACK_DEF body `:214-230`) | idem — fara stare | fara probleme |
|
||||
| `pack_sesiune.getOptiuneFirma('PC'/'PPRETV')` | 13522-13523, 13853 | `SELECT varvalue FROM optiuni WHERE ...` (PACK_SESIUNE body `:119-141`) — **lookup pe tabela, fara stare de pachet** | idem | fara probleme |
|
||||
| `VANZARI_DETALII_TEMP` (liniile directe) | 13896-13897 | GTT `ON COMMIT DELETE ROWS` | **`VANZARI_DETALII WHERE ID_VANZARE = :id AND STERS = 0 AND NVL(ID_VANZARE_SET,0) = 0`** | vezi 3 |
|
||||
| `VANZARI_DETALII_TEMP` (liniile de set, alias `c`) | 13915, 13918 | GTT | **`VANZARI_DETALII ... AND NVL(ID_VANZARE_SET,0) <> 0`** | vezi 3 |
|
||||
| `vanzari_seturi` (alias `b`) | 13916 | **tabela PERSISTENTA, deja** | ea insasi | vezi 3 |
|
||||
| `vanzari_cursuri vc` | 13929-13930 | tabela persistenta, populata cu `id_vanzare`-ul curent la `:13704` (`scrie_cursuri`) | ea insasi, deja legata pe `ID_VANZARE` | fara probleme |
|
||||
|
||||
Verificat pe `all_tab_columns`: **toate cele 9 coloane citite din `VANZARI_DETALII_TEMP` de
|
||||
agregare** (`id_vanzare_set, pret, proc_tvav, cantitate, diferenta, discount_unitar, id_valuta,
|
||||
pret_cu_tva, pret_achizitie`) exista, cu acelasi nume, in `VANZARI_DETALII`. Singurele coloane pe
|
||||
care TEMP le are in plus si care nu exista in tabela reala sunt `CURS, MULTIPLICATOR, EXPLICATIA,
|
||||
ID_COMANDA, NUMAR_ACT, ID_TEMP, ID_UTIL, IN_STOC, PRETV_ORIG, ID_GESTIUNE_DEST, ID_LUCRARE_REZ,
|
||||
ID_PART_REZ` — **niciuna nu e folosita de blocul de agregare** (`curs`/`multiplicator` vin din
|
||||
`vanzari_cursuri vc`, nu din linie).
|
||||
|
||||
**Concluzie: blocul se reproduce 1:1 pe surse persistente, schimband doar clauzele `FROM` si
|
||||
inlocuind cele 3 valori de stare cu cele 3 coloane din `VANZARI`.** Nu e nevoie de nicio
|
||||
reformulare a formulei — inclusiv `calculeaza_total_fara_tva_fact` / `calculeaza_total_tva_fact`
|
||||
(`PACK_FACTURARE.pck:15858-16013`, semnaturi in spec la `:1167-1185`) raman apelate identic.
|
||||
|
||||
Doua observatii de detaliu:
|
||||
- `a1.diferenta` e selectata (`:13870`, `:13891`) dar **nu e folosita nicaieri** in agregarea din
|
||||
`scrie_in_vanzari` (parametrul `V_DIFERENTA` e `0` hard-codat la `:13817`, `:13826`, `:13835`,
|
||||
`:13844`). Se poate pastra pentru fidelitate sau elimina; recomand pastrarea, ca diff-ul fata de
|
||||
original sa ramana citibil.
|
||||
- Handler-ul `WHEN NO_DATA_FOUND THEN NULL` (`:13951-13953`) e defensiv si practic inaccesibil:
|
||||
subinterogarea are agregate fara `GROUP BY`, deci intoarce mereu exact un rand. **Dar** pe zero
|
||||
linii agregatele intorc `NULL`, nu `0` — la emitere cazul nu poate aparea, la editare da (vezi
|
||||
riscul R4).
|
||||
|
||||
---
|
||||
|
||||
## 3. Liniile din seturi — EXISTA echivalent persistent
|
||||
|
||||
**Corectia raportului precedent.** Agregarea nu citeste `VANZARI_SETURI_TEMP`, ci
|
||||
**`VANZARI_SETURI`** (`PACK_FACTURARE.pck:13916`), care e o tabela **normala, persistenta**
|
||||
(verificat pe `all_tables`: `VANZARI_SETURI temporary=N`; doar `VANZARI_SETURI_TEMP` si
|
||||
`VANZARI_DETALII_TEMP` sunt `temporary=Y duration=SYS$TRANSACTION`).
|
||||
|
||||
Trecerea TEMP -> persistent o face `pack_facturare.scrie_seturi` (`:13993-14028`), apelata la
|
||||
`:13706`, **inainte** de agregare: insereaza fiecare rand din `VANZARI_SETURI_TEMP` in
|
||||
`VANZARI_SETURI` (`ID_VANZARE_SET` din `SEQ_VANZARI_SETURI`, prin trigger
|
||||
`TRG_VANZARI_SET_BEFOINS`) si reface pointerul in `VANZARI_DETALII_TEMP.ID_VANZARE_SET`. De aceea
|
||||
agregarea de la `:13915-13928` face join intre TEMP (componentele) si tabela persistenta (capul de
|
||||
set).
|
||||
|
||||
Structura `VANZARI_SETURI` (`all_tab_columns`), identica cu a TEMP-ului:
|
||||
|
||||
```
|
||||
ID_VANZARE_SET NUMBER(10) NOT NULL -- PK, din SEQ_VANZARI_SETURI
|
||||
DENUMIRE VARCHAR2(100)
|
||||
EXPLICATIE VARCHAR2(100)
|
||||
CANTITATE NUMBER(10,4)
|
||||
UM VARCHAR2(10)
|
||||
SERIE VARCHAR2(100)
|
||||
PRET NUMBER(20,4)
|
||||
DISCOUNT_UNITAR NUMBER(20,4)
|
||||
PRET_CU_TVA NUMBER(1) NOT NULL
|
||||
```
|
||||
|
||||
Nu are `ID_VANZARE` si nu are `STERS`: legatura cu documentul e **exclusiv** prin
|
||||
`VANZARI_DETALII.ID_VANZARE_SET`. Deci filtrarea pe document se face tot din `VANZARI_DETALII`.
|
||||
|
||||
**Precedent care confirma reconstituirea**: `pack_facturare.citeste_vanzari_seturi`
|
||||
(`:16619-16698`) reface deja exact acest lucru pe surse persistente — `VANZARI` (`cod`, `sters=0`)
|
||||
+ `VANZARI_DETALII` (`sters=0`, `id_vanzare_set is not null`) + `VANZARI_CURSURI` +
|
||||
`VANZARI_SETURI`, cu acelasi `GROUP BY b.id_vanzare_set`. Nu inventam un tipar nou.
|
||||
|
||||
**Raspuns la intrebarea din brief: DA, recalculul poate acoperi si liniile de set, fara pierdere de
|
||||
informatie.** Transformarea necesara in ramura de set:
|
||||
|
||||
```
|
||||
from vanzari_detalii c -- in loc de vanzari_detalii_temp c
|
||||
left join vanzari_seturi b on b.id_vanzare_set = c.id_vanzare_set
|
||||
where nvl(c.id_vanzare_set, 0) <> 0
|
||||
and c.id_vanzare = V_ID_VANZARE
|
||||
and c.sters = 0
|
||||
```
|
||||
|
||||
Date (test, `MARIUSM_AUTO`): 4 randuri in `VANZARI_SETURI`, 4 documente cu linii de set. **Nu e o
|
||||
dovada** — validarea ramane pe cod, nu pe volum.
|
||||
|
||||
### Ce NU se poate face din interfata (limitare de semnalat pentru S4)
|
||||
|
||||
View-ul `VVANZARI_ARTICOLE` (sursa lui `IncarcaArticoleFactura`,
|
||||
`COMUN\programe\ofacturare_editare.prg:288-330`) **nu expune `ID_VANZARE_SET` si nici
|
||||
`PRET_ACHIZITIE`**:
|
||||
|
||||
```
|
||||
select vd.id_vanzare, vd.id_vanzare_det, vd.id_articol, vd.cantitate, vd.pret, vd.pret_cu_tva,
|
||||
vd.proc_tvav, vd.discount_unitar, vd.id_gestiune, vd.cont, vd.id_valuta,
|
||||
vd.id_jtva_coloana, vd.serie, vd.explicatie, vd.taxcode, vd.lot, vd.sters,
|
||||
na.denumire, na.codmat, ng.nume_gestiune, nv.nume_val
|
||||
from vanzari_detalii vd
|
||||
left join nom_articole na on na.id_articol = vd.id_articol
|
||||
left join nom_gestiuni ng on ng.id_gestiune = vd.id_gestiune
|
||||
left join nom_valute nv on nv.id_valuta = vd.id_valuta
|
||||
```
|
||||
|
||||
Consecinte concrete:
|
||||
|
||||
1. componentele de set apar in grid ca linii obisnuite, dar formularul **nu poate sti** ca sunt
|
||||
componente si nu poate edita capul de set (`VANZARI_SETURI.PRET`/`CANTITATE`), care e ceea ce
|
||||
intra efectiv in totaluri. Un utilizator care schimba pretul unei componente **nu schimba
|
||||
totalul documentului** — recalculul ia pretul din `VANZARI_SETURI`. Divergenta tacuta.
|
||||
2. `PRET_ACHIZITIE` lipseste din cursor, deci VFP nu-l poate rescrie la un `UPDATE` de linie: la
|
||||
scriere trebuie **omis din `SET`**, ca sa ramana valoarea existenta (altfel `VALOARE_ACHIZITIE`
|
||||
se pierde). Pentru liniile **noi** insa nu exista sursa — vor intra cu `PRET_ACHIZITIE` NULL si
|
||||
vor contribui cu NULL la `SUM(round(cantitate * pret_achizitie))`, deci **`VALOARE_ACHIZITIE`
|
||||
devine NULL pe tot documentul** daca fie si o singura linie noua are NULL. Vezi riscul R3.
|
||||
|
||||
---
|
||||
|
||||
## 4. Capcana de ordonare — punctul central
|
||||
|
||||
### Ce ruleaza azi, in ce ordine
|
||||
|
||||
Punctul de intrare nou (deja scris in ramura curenta):
|
||||
`COMUN\clase\ofacturare_comun.vc2`, `do_editare_factura`, blocul de salvare:
|
||||
|
||||
```
|
||||
3798 If Thisform.do_deschide_tranzactie() && SQLSetprop(gnHandle,"Transactions",2)
|
||||
3800 lnSucces = OSCRIE_IN_FISIERE(2,.T.,.T.) && marcheaza STERS=1 nota veche
|
||||
...
|
||||
3820 lnSucces = OSCRIE_IN_FISIERE(0,.T.,.T.) && scrie nota noua (COD nou din SCRIE_IN_ACT)
|
||||
3823 If lnSucces > 0
|
||||
3824 lcSql = [begin pack_contafin.finalizeaza_modificare_nota(...); end]
|
||||
3826 lnSucces = Iif(goExecutor.oExecuta(lcSql),1,-1)
|
||||
3827 Endif
|
||||
3828 If Thisform.do_inchide_tranzactie(Iif(lnSucces<0,2,1)) && SQLCOMMIT / SQLROLLBACK
|
||||
```
|
||||
|
||||
`do_deschide_tranzactie` / `do_inchide_tranzactie`: `COMUN\clase\_frm_base.vc2:251-269` si
|
||||
`:278-302` — `SQLSetprop(gnHandle,"Transactions",2)` / `Sqlcommit(gnHandle)`.
|
||||
|
||||
`finalizeaza_modificare_nota` (`PACK_CONTAFIN.pck:8601-8651`) apeleaza `actualizeaza_vanzari` la
|
||||
`:8616`, gardat de `SELECT COUNT(*) FROM vanzari WHERE cod = tnCod > 0` (`:8613-8615`).
|
||||
|
||||
`actualizeaza_vanzari` (`PACK_FACTURARE.pck:16015-16025`):
|
||||
|
||||
```
|
||||
16018 -- de modificat in caz ca il las sa stearga manual inregistrari din VANZARI_DETALII
|
||||
16019 -- acum se marcheaza cu STERS = 1 doar cand se sterge toata factura
|
||||
16020 UPDATE VANZARI_DETALII
|
||||
16021 SET STERS = 0
|
||||
16022 WHERE ID_VANZARE IN
|
||||
16023 (SELECT ID_VANZARE FROM VANZARI WHERE COD = V_COD_VECHI);
|
||||
16024 UPDATE VANZARI SET COD = V_COD_NOU, STERS = 0 WHERE COD = V_COD_VECHI;
|
||||
```
|
||||
|
||||
### De ce exista `STERS = 0` acolo (nu e cod mort)
|
||||
|
||||
E perechea lui `sterge_din_vanzari` -> `sterge_factura`, care marcheaza documentul sters:
|
||||
|
||||
```
|
||||
5549 UPDATE VANZARI_DETALII
|
||||
5550 SET STERS = V_STERS, ID_UTILS = V_ID_UTIL, DATAORAS = V_DATAORA
|
||||
5551 WHERE ID_VANZARE = V_ID_VANZARE
|
||||
5552 AND STERS = V_NESTERS
|
||||
```
|
||||
|
||||
Reeditarea unei note sterse trebuie sa readuca la viata si randul din `VANZARI`, si liniile lui.
|
||||
**Scoaterea reset-ului ar rupe fluxul „stergi nota, apoi o reeditezi" pe toata suita ROA.**
|
||||
|
||||
### Consecinta pentru S5 — mai grava decat „stergerile se pierd"
|
||||
|
||||
Reset-ul e **in bloc, fara nicio garda**: nici `AND STERS = 1` (irelevant), nici o discriminare
|
||||
intre „stersa ca linie" si „stersa odata cu documentul". `sterge_factura` marcheaza doar liniile
|
||||
active (`AND STERS = V_NESTERS`, `:5552`), deci o linie stearsa la o editare anterioara ramane
|
||||
`STERS=1` — si e **inviata** de `actualizeaza_vanzari` la urmatoarea salvare a notei.
|
||||
|
||||
Deci daca liniile se scriu inainte de `finalizeaza_modificare_nota`:
|
||||
|
||||
- stergerile din editarea CURENTA se pierd tacut;
|
||||
- **si**, independent de ce face utilizatorul acum, orice stergere facuta la o editare
|
||||
ANTERIOARA e anulata la fiecare salvare ulterioara — inclusiv la o salvare care nu atinge deloc
|
||||
articolele. Stergerea de linie **nu s-ar fixa niciodata**.
|
||||
|
||||
`UPDATE`-urile de pret/cantitate si `INSERT`-urile de linii noi ar supravietui — deci esecul e
|
||||
partial si asimetric, exact tipul care trece de un test superficial.
|
||||
|
||||
### Variantele de ordonare
|
||||
|
||||
**(a) VFP scrie detaliile inainte, `actualizeaza_vanzari` neatinsa.**
|
||||
Respinsa. Motivul complet e cel de mai sus: nu doar ca stergerile din sesiunea curenta se pierd,
|
||||
dar mecanismul de stergere de linie devine structural imposibil, fiindca fiecare salvare a notei
|
||||
reseteaza tot documentul la `STERS = 0`. In plus, dupa `actualizeaza_vanzari` `VANZARI.COD` s-a
|
||||
schimbat, deci orice scriere ulterioara ancorata pe `cod` ar rata randul (`ID_VANZARE` ramane —
|
||||
vezi A.1 din raportul precedent, reconfirmat la `:16024`).
|
||||
|
||||
**(a') VFP scrie inainte + `actualizeaza_vanzari` primeste o garda.**
|
||||
Respinsa. Ar cere un discriminator „stearsa ca linie" vs „stearsa cu documentul", care nu exista in
|
||||
schema (`VANZARI_DETALII` nu are alt marcaj decat `STERS`/`ID_UTILS`/`DATAORAS`); orice euristica
|
||||
pe `DATAORAS` e fragila. Si e Varianta A din raportul precedent — modificare in-place a unei
|
||||
proceduri apelate de **orice** editare de nota cu `cod` in `vanzari`, din toata suita ROA.
|
||||
|
||||
**(b) VFP scrie detaliile DUPA ce `finalizeaza_modificare_nota` s-a intors, apoi apeleaza
|
||||
`pack_facturare.recalculeaza_totaluri_vanzari(id_vanzare, discount)`, in aceeasi tranzactie.**
|
||||
**RECOMANDATA.** Verificat, nu presupus:
|
||||
|
||||
- **tranzactia e inca deschisa** cand se intoarce `finalizeaza_modificare_nota`: aceasta ruleaza la
|
||||
`ofacturare_comun.vc2:3826`, iar `do_inchide_tranzactie` abia la `:3828`; tranzactia e manuala pe
|
||||
`gnHandle` (`_frm_base.vc2:255`), aceeasi conexiune ODBC pe care ruleaza `goExecutor`. Deci
|
||||
scrierile de dupa intra in acelasi `COMMIT`/`ROLLBACK`, fara fereastra de inconsistenta.
|
||||
- `actualizeaza_vanzari` ramane **neatinsa** — zero regresie pe ROAGEST/ROACONT.
|
||||
- `PACK_CONTAFIN` ramane **neatins** — nu se recompileaza pachetul central de scriere a
|
||||
documentelor.
|
||||
- reset-ul `STERS = 0` ruleaza primul, iar VFP re-aplica dupa el starea autoritativa a liniilor.
|
||||
- `VANZARI.COD` e deja rescris cand VFP scrie — de aceea toate scrierile se ancoreaza pe
|
||||
`ID_VANZARE` (`lnIdVanzare`, citit din `crsfacturi` la `ofacturare_comun.vc2:3735`, stabil).
|
||||
|
||||
**Atentie — (b) naiv are un defect.** `IncarcaArticoleFactura` incarca **doar `sters = 0`**
|
||||
(`ofacturare_editare.prg:302`), deci liniile sterse la o editare anterioara nu sunt in
|
||||
`crsArticoleFactura`: reset-ul le-a inviat, iar VFP nu le atinge, deci raman active. Corectia e in
|
||||
**forma scrierii**, nu in Oracle — VFP scrie o stare completa, nu un delta:
|
||||
|
||||
```
|
||||
1. UPDATE VANZARI_DETALII SET STERS = 1, ID_UTILS = ?, DATAORAS = SYSDATE
|
||||
WHERE ID_VANZARE = <id> AND STERS = 0 -- marcheaza tot
|
||||
2. pentru fiecare linie pastrata din cursor, cu id_vanzare_det > 0:
|
||||
UPDATE VANZARI_DETALII SET STERS = 0, CANTITATE = ?, PRET = ?, PRET_CU_TVA = ?, ...
|
||||
WHERE ID_VANZARE_DET = <det> -- invie doar ce ramane
|
||||
3. pentru fiecare linie noua: INSERT INTO VANZARI_DETALII (...) -- ID_VANZARE_DET din trigger
|
||||
4. pack_facturare.recalculeaza_totaluri_vanzari(<id>, <discount>)
|
||||
```
|
||||
|
||||
Idiomul „marcheaza tot, invie ce ramane" e idempotent, nu are limita de 1000 de elemente intr-un
|
||||
`IN`, nu cere lista separata de linii sterse, si pastreaza sterse liniile scoase la editari
|
||||
anterioare. La pasul 2, `PRET_ACHIZITIE` se **omite** din `SET` (nu e in cursor — vezi 3).
|
||||
|
||||
**(c) recalcul apelat din interiorul `finalizeaza_modificare_nota`** (recomandarea raportului
|
||||
precedent, sectiunea B). **De abandonat.** Este incompatibila cu (b): daca recalculul ruleaza
|
||||
inauntrul lui `finalizeaza_modificare_nota`, el vede liniile **vechi** (VFP nu le-a scris inca,
|
||||
pentru ca nu poate scrie inainte de reset), deci ar produce exact totalurile de dinainte de editare
|
||||
— tacut corecte ca formula, tacut gresite ca valoare. Nu e o varianta mai riscanta, e o varianta
|
||||
gresita.
|
||||
|
||||
**(c') procedura Oracle care primeste si liniile** (prin `VANZARI_DETALII_TEMP` sau o colectie) si
|
||||
face si scrierea, si recalculul. Respinsa: reintroduce calea TEMP pe care planul a exclus-o explicit
|
||||
(`plan_06_editare_factura.md:106-114`), cere o forma de parametru incomoda prin ODBC, si dubleaza
|
||||
scrierea per-linie pe care planul a decis-o deja in VFP, dupa modelul `modifica_explicatie_articol`
|
||||
(`PACK_FACTURARE.pck:14514-14522`).
|
||||
|
||||
### Recomandare
|
||||
|
||||
**Varianta (b), cu scrierea in forma „marcheaza tot, invie ce ramane".** Argumentul decisiv nu e
|
||||
comoditatea, ci ca e **singura ordine in care reset-ul `STERS = 0` din `actualizeaza_vanzari` ramane
|
||||
inofensiv fara sa modificam procedura** — iar procedura aceea e folosita azi de toata suita, pentru
|
||||
un scop legitim (reeditarea unei note sterse) pe care nu-l putem sacrifica.
|
||||
|
||||
---
|
||||
|
||||
## 5. Discountul de document (`VANZARI.DISCOUNT`)
|
||||
|
||||
**Cine il scrie azi: nimeni, dupa emitere.** Verificat pe toata schema, nu doar pe `PACK_FACTURARE`:
|
||||
|
||||
- `all_source` (`PACKAGE BODY`/`PROCEDURE`/`FUNCTION`/`TRIGGER`, `MARIUSM_AUTO`), cautand
|
||||
`discount\s*=` exclusiv coloanele `discount_unitar|discount_tva|discount_evidentiat`: **niciun
|
||||
`UPDATE ... SET DISCOUNT = ...` pe `VANZARI`.** Rezultatele sunt toate pe alte tabele
|
||||
(`PACK_COMENZI.PROC_DISCOUNT`, `PACK_CRM.val_discount`, `PACK_OFERTARE.valdiscount`, ...).
|
||||
- Toate cele 8 instructiuni `UPDATE VANZARI` din `PACK_FACTURARE` (`:5485, :5493, :5516, :5615,
|
||||
:14817, :15387, :15397, :15509`) plus `modifica_date_factura` (`:14463-14512`, singura procedura
|
||||
de „modifica antetul facturii" existenta) ating `STERS`/`FACTURAT`/`ID_FACT`/`AVIZE`/
|
||||
`SERIE_ACT`/`NUMAR_ACT`/`DATA_ACT`/`DATA_SCAD` — **niciuna `DISCOUNT`**.
|
||||
- In VFP: cautare pe `COMUN\` dupa `set discount =` / `vanzari set ... discount` — **niciun
|
||||
rezultat**.
|
||||
- Trigger-ele pe `VANZARI` nu-l ating: `TRG_VANZARI_BEFOUPD` face doar audit pe
|
||||
`NR_ACT`/`SERIE_ACT`/`DATA_ACT`/`DATA_SCAD` (`pack_audit.verifica_val`).
|
||||
|
||||
Deci `VANZARI.DISCOUNT` e scris **o singura data in viata documentului**, la `INSERT`-ul din
|
||||
`scrie_in_vanzari` (`:13621` in lista de coloane, `:13682` in `VALUES`, din `V_DISCOUNT_FACTURA`).
|
||||
Nu exista nici procedura de modificare, nici cale VFP.
|
||||
|
||||
Pe partea VFP valoarea e deja disponibila in memorie: cursorul `tvanz` are coloana `discount`
|
||||
(`ofacturare_editare.prg:152`, `CreeazaCursorTvanzGol`), populata din `VANZARI` de
|
||||
`IncarcaVanzareNota` (`:176`).
|
||||
|
||||
### Recomandare: **parametru al procedurii noi**, nu `UPDATE` separat din VFP
|
||||
|
||||
```
|
||||
recalculeaza_totaluri_vanzari(V_ID_VANZARE IN NUMBER, V_DISCOUNT IN NUMBER DEFAULT NULL)
|
||||
```
|
||||
|
||||
cu semantica `V_DISCOUNT IS NULL` = „pastreaza valoarea curenta". Motive:
|
||||
|
||||
1. **discountul si totalurile nu pot diverge.** Cu doua instructiuni separate din VFP, un esec pe
|
||||
a doua (sau o omisiune la un viitor call-site) lasa `DISCOUNT` nou si totaluri calculate pe cel
|
||||
vechi. Cu un parametru, e imposibil sa recalculezi cu un discount invechit.
|
||||
2. procedura ramane utilizabila ca **recalcul pur** (fara al doilea argument) de oriunde altundeva
|
||||
— de exemplu dintr-un script de backfill.
|
||||
3. e o instructiune ODBC in minus pe drumul critic.
|
||||
|
||||
Implementare: `V_DISCOUNT` se aplica in acelasi `UPDATE vanzari` final,
|
||||
`discount = NVL(V_DISCOUNT, discount)`, iar valoarea folosita in agregare se citeste **inainte**,
|
||||
ca `NVL(V_DISCOUNT, (select discount from vanzari where id_vanzare = V_ID_VANZARE))` — altfel
|
||||
agregarea ar lucra pe discountul vechi.
|
||||
|
||||
---
|
||||
|
||||
## 6. Coloanele care NU se recalculeaza — reconfirmat pe sursa proaspata
|
||||
|
||||
`SERIE_INCASAT` / `NR_INCASAT` / `SUMA_INCASAT` / `TIP_INCASAT` (`:13777-13780`, scrise la
|
||||
`:13945-13948`) vin din `pack_facturare.cserie_act_incasare` / `nnumar_act_incasare` /
|
||||
`nsuma_incasare` / `ntip_doc_incasare`.
|
||||
|
||||
Cautare exhaustiva a atribuirilor catre aceste 4 variabile in `PACK_FACTURARE.pck` — **7 rezultate,
|
||||
toate in fluxul de emitere**:
|
||||
|
||||
```
|
||||
1882-1886 cserie_act_incasare := NULL; nnumar_act_incasare := NULL;
|
||||
ntip_doc_incasare := NULL; nsuma_incasare := NULL; (initializeaza_date_factura)
|
||||
13102-13105 cserie_act_incasare := V_SERIE_ACT_INCASARE; nnumar_act_incasare := V_NUMAR_ACT_INCASARE;
|
||||
ntip_doc_incasare := nTipIncasareChitanta; nsuma_incasare := 0;
|
||||
13136 nsuma_incasare := nsuma_incasare + ...
|
||||
13140,13144 ntip_doc_incasare := V_TIP / nTipIncasareBonFiscal;
|
||||
```
|
||||
|
||||
Sursele lor (`V_SERIE_ACT_INCASARE`, `V_NUMAR_ACT_INCASARE`) sunt parametri ai emiterii unei
|
||||
facturi-cu-incasare combinata. **Nu exista nicio tabela din care sa fie reconstituite la o editare
|
||||
ulterioara** — singura urma persistenta sunt chiar cele 4 coloane din `VANZARI`, care ar fi
|
||||
suprascrise cu `NULL` de o copiere naiva a `UPDATE`-ului.
|
||||
|
||||
**Concluzie reconfirmata: procedura noua scrie 11 coloane** (`discount_tva`, `valoare_achizitie`,
|
||||
`total_fara_tva`, `total_tva`, `total_cu_tva`, `valval`, `tvaval`, `totval`, `id_valuta`, `curs`,
|
||||
`multiplicator`), **plus optional `discount`** (vezi 5). Cele 4 de incasare raman la valoarea de la
|
||||
emitere.
|
||||
|
||||
---
|
||||
|
||||
## 7. Semnatura propusa si scripturile de migrare
|
||||
|
||||
### Declaratia din PACKAGE SPEC
|
||||
|
||||
Se adauga imediat dupa `actualizeaza_vanzari` (`PACK_FACTURARE.pck:1187-1188`), langa procedurile
|
||||
surori:
|
||||
|
||||
```sql
|
||||
PROCEDURE recalculeaza_totaluri_vanzari(V_ID_VANZARE IN NUMBER,
|
||||
V_DISCOUNT IN NUMBER DEFAULT NULL);
|
||||
```
|
||||
|
||||
`recalculeaza_totaluri_vanzari` = **29 de caractere**, sub limita de 30 a lui Oracle 10.2/11
|
||||
(peste 30 ar da `ORA-00972`). Nu mai lungi numele.
|
||||
|
||||
### Corpul (schita, compatibila 10.2)
|
||||
|
||||
Constructii folosite: `SELECT INTO`, `UPDATE`, `DECODE`, `CASE`, `NVL`, `ROUND`, `MAX`, `SUM`,
|
||||
`LEFT JOIN`, `UNION ALL`, `GROUP BY`, subinterogari inline. **Nimic din tabelul de incompatibilitati
|
||||
din `scripturi-migrare-db.md`** — fara `LISTAGG`, `CONTINUE`, `REGEXP_COUNT`, `FETCH FIRST`,
|
||||
`PIVOT`, `secventa.NEXTVAL` in atribuire.
|
||||
|
||||
```sql
|
||||
PROCEDURE recalculeaza_totaluri_vanzari(V_ID_VANZARE IN NUMBER,
|
||||
V_DISCOUNT IN NUMBER DEFAULT NULL) IS
|
||||
lnDiscountFactura VANZARI.DISCOUNT%TYPE;
|
||||
lnInValuta VANZARI.IN_VALUTA%TYPE;
|
||||
lnDiscountEvidentiat VANZARI.DISCOUNT_EVIDENTIAT%TYPE;
|
||||
lnDiscountTVA VANZARI.DISCOUNT_TVA%TYPE;
|
||||
lnValoareAchizitie VANZARI.VALOARE_ACHIZITIE%TYPE;
|
||||
lnTotalFaraTVA VANZARI.TOTAL_FARA_TVA%TYPE;
|
||||
lnTotalTVA VANZARI.TOTAL_TVA%TYPE;
|
||||
lnTotalCuTVA VANZARI.TOTAL_CU_TVA%TYPE;
|
||||
lnValVal VANZARI.VALVAL%TYPE;
|
||||
lnTVAVal VANZARI.TVAVAL%TYPE;
|
||||
lnTotVal VANZARI.TOTVAL%TYPE;
|
||||
lnIdValuta VANZARI.ID_VALUTA%TYPE;
|
||||
lnCurs VANZARI.CURS%TYPE;
|
||||
lnMultiplicator VANZARI.MULTIPLICATOR%TYPE;
|
||||
lnPrecizieCalcul NUMBER(2) := pack_sesiune.getOptiuneFirma('PC');
|
||||
lnPreciziePretV NUMBER(2) := pack_sesiune.getOptiuneFirma('PPRETV');
|
||||
BEGIN
|
||||
SELECT NVL(V_DISCOUNT, discount), in_valuta, NVL(discount_evidentiat, 0)
|
||||
INTO lnDiscountFactura, lnInValuta, lnDiscountEvidentiat
|
||||
FROM vanzari
|
||||
WHERE id_vanzare = V_ID_VANZARE;
|
||||
|
||||
-- acelasi bloc de agregare ca in scrie_in_vanzari (:13764-13931), cu trei substitutii:
|
||||
-- VANZARI_DETALII_TEMP a -> VANZARI_DETALII a WHERE a.id_vanzare = V_ID_VANZARE AND a.sters = 0
|
||||
-- vanzari_detalii_temp c -> vanzari_detalii c WHERE c.id_vanzare = V_ID_VANZARE AND c.sters = 0
|
||||
-- pack_facturare.nin_valuta -> lnInValuta
|
||||
-- pack_facturare.ndiscount_evidentiat -> lnDiscountEvidentiat
|
||||
-- V_DISCOUNT_FACTURA -> lnDiscountFactura
|
||||
-- fara coloanele de incasare (serie/nr/suma/tip)
|
||||
SELECT ... INTO lnDiscountTVA, lnValoareAchizitie, lnTotalFaraTVA, lnTotalTVA,
|
||||
lnTotalCuTVA, lnValVal, lnTVAVal, lnTotVal,
|
||||
lnIdValuta, lnCurs, lnMultiplicator
|
||||
FROM ( ... );
|
||||
|
||||
UPDATE vanzari
|
||||
SET discount = lnDiscountFactura,
|
||||
discount_tva = lnDiscountTVA,
|
||||
valoare_achizitie = lnValoareAchizitie,
|
||||
total_fara_tva = lnTotalFaraTVA,
|
||||
total_tva = lnTotalTVA,
|
||||
total_cu_tva = lnTotalCuTVA,
|
||||
valval = lnValVal,
|
||||
tvaval = lnTVAVal,
|
||||
totval = lnTotVal,
|
||||
id_valuta = lnIdValuta,
|
||||
curs = lnCurs,
|
||||
multiplicator = lnMultiplicator
|
||||
WHERE id_vanzare = V_ID_VANZARE;
|
||||
END recalculeaza_totaluri_vanzari;
|
||||
```
|
||||
|
||||
Idempotenta: `SELECT` + `UPDATE ... WHERE id_vanzare = :id`, fara `INSERT` — rularea de doua ori pe
|
||||
acelasi id da acelasi rezultat.
|
||||
|
||||
**Fara handler `WHEN NO_DATA_FOUND THEN NULL`** pe modelul originalului: la editare, un `id_vanzare`
|
||||
inexistent e o eroare reala care trebuie sa opreasca tranzactia, nu sa fie inghitita. (Primul
|
||||
`SELECT INTO`, pe cheia primara, e singurul care poate ridica `NO_DATA_FOUND`.)
|
||||
|
||||
### Scripturile de migrare
|
||||
|
||||
Azi, 09.08.2026, in `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08` nu exista niciun script cu data de azi
|
||||
(ultimele sunt `..._2026_08_08_01` si `..._2026_08_08_02`), deci **`NN` porneste de la `01`**.
|
||||
`NN` e secventa unica pe zi, comuna tuturor prefixelor.
|
||||
|
||||
**Un singur script**, fiindca S5 atinge un singur pachet si nimic altceva:
|
||||
|
||||
```
|
||||
ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql
|
||||
```
|
||||
|
||||
- prefix `ff_` — pachetul sta pe schema fiecarei firme;
|
||||
- **un pachet sta singur in scriptul lui**: fara DDL de tabele si fara DML alaturi (nu e nevoie de
|
||||
niciunul — nu se adauga coloane si nu se curata date);
|
||||
- contine SPEC + BODY (SPEC-ul se schimba: declaratia noua), CRLF obligatoriu, antet de 4-5 randuri,
|
||||
fara `select` de raportare, si se incheie cu
|
||||
`exec pack_migrare.UpdateVersiune('ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql');` + `commit;`;
|
||||
- `versiune_db.txt` din radacina proiectului se muta pe `2026_08_09_01` (fara newline final).
|
||||
|
||||
**Nu e nevoie de un script separat pentru `VVANZARI_ARTICOLE`** pentru S5 asa cum e proiectat aici
|
||||
— dar vezi intrebarea 3 de mai jos, care ar cere unul (`NN = 02`, si atunci **inainte** de cel al
|
||||
pachetului daca pachetul l-ar folosi; aici nu-l foloseste, deci ordinea e libera).
|
||||
|
||||
---
|
||||
|
||||
## 8. Riscuri si intrebari deschise
|
||||
|
||||
**R1 — Componentele de set sunt editabile in grid dar nu influenteaza totalurile. (mediu)**
|
||||
`VVANZARI_ARTICOLE` nu expune `ID_VANZARE_SET`, deci S4 nu poate distinge componentele de liniile
|
||||
normale; recalculul ia insa pretul/cantitatea din `VANZARI_SETURI`, nu din componente. Utilizatorul
|
||||
modifica o componenta, apasa salvare, totalul nu se schimba. Tacut.
|
||||
*Recomandarea mea:* pentru #6, **adauga `ID_VANZARE_SET` in `VVANZARI_ARTICOLE`** (script separat,
|
||||
`ff_2026_08_09_02_COMUN_VVANZARI_ARTICOLE.sql`) si fa liniile cu `id_vanzare_set` nenul
|
||||
**needitabile in grid**, cu o eticheta („linie din set"). Editarea seturilor e o functionalitate
|
||||
proprie, nu o extindere gratuita a lui S4. Alternativa minimala, daca Marius nu vrea scriptul de
|
||||
view: blocheaza intreaga factura care contine seturi — dar asta contrazice decizia din
|
||||
`plan_06_editare_factura.md:183-186` („fara blocarea facturilor care le contin").
|
||||
|
||||
**R2 — Stergerea unei componente de set. (mic, dar urat)**
|
||||
Cu idiomul „marcheaza tot, invie ce ramane", stergerea in grid a unei componente lasa capul de set
|
||||
in `VANZARI_SETURI` si celelalte componente pe loc: totalul ramane neschimbat (vine din cap), dar
|
||||
`VALOARE_ACHIZITIE` scade (se pierde `pret_achizitie`-ul componentei). Divergenta partiala.
|
||||
*Recomandare:* acoperit de R1 — daca liniile de set sunt needitabile, cazul dispare.
|
||||
|
||||
**R3 — `PRET_ACHIZITIE` NULL pe liniile noi anuleaza `VALOARE_ACHIZITIE` pe tot documentul. (mediu)**
|
||||
`SUM(round(cantitate * pret_achizitie))` cu un singur operand NULL nu da NULL pe total (SUM ignora
|
||||
NULL-urile), dar **linia noua nu contribuie deloc** — deci `VALOARE_ACHIZITIE` (baza de calcul a
|
||||
marjei) subestimeaza sistematic dupa fiecare adaugare de linie. Coloana nu e in cursorul din grid si
|
||||
nu are sursa la editare (la emitere vine din stoc/politica de pret).
|
||||
*Recomandare:* la `INSERT`-ul liniei noi, VFP scrie `PRET_ACHIZITIE` cu pretul de achizitie curent
|
||||
al articolului (acelasi lookup pe care il face `adauga_articol_factura_stoc`), sau, daca nu se poate
|
||||
determina, cu `0` explicit si o avertizare in verificarile din S4b. **Nu lasa NULL tacut.**
|
||||
|
||||
**R4 — Factura fara nicio linie da totaluri NULL, nu 0. (mic)**
|
||||
Agregatele pe zero randuri intorc NULL; la emitere cazul nu poate aparea, la editare da.
|
||||
*Recomandare:* nu modifica formula (ar diverge de original). Interzice in S4b salvarea unei facturi
|
||||
cu zero linii active — e oricum un document invalid.
|
||||
|
||||
**R5 — Linie cu valuta fara rand in `VANZARI_CURSURI`. (mic)**
|
||||
`left join vanzari_cursuri vc on vc.id_vanzare = V_ID_VANZARE and vd.id_valuta = vc.id_valuta`
|
||||
(`:13929-13930`): daca o linie ajunge cu o valuta pentru care documentul nu are curs, `vc.curs` e
|
||||
NULL si `pret_ron` iese NULL. `CreeazaPoArticolNouTvd` primeste explicit valuta documentului
|
||||
(`tnIdValutaDoc`, `ofacturare_editare.prg:356`), deci in fluxul proiectat nu ar trebui sa apara.
|
||||
**NEVERIFICAT** ca S4 chiar transmite acel parametru pe toate caile de adaugare.
|
||||
*Recomandare:* verificare in S4b, nu garda in Oracle.
|
||||
|
||||
**R6 — `pack_sesiune.getOptiuneFirma` intoarce `''` la orice eroare. (mic)**
|
||||
`PACK_SESIUNE` body `:128-131`: `WHEN OTHERS THEN lcValue := ''`. Atribuit intr-un `NUMBER(2)`, `''`
|
||||
devine NULL, iar `ROUND(x, NULL)` da NULL. Comportament identic cu cel de la emitere — deci nu e o
|
||||
regresie introdusa de S5, dar merita stiut daca apar totaluri NULL inexplicabile.
|
||||
*Recomandare:* nimic de facut in S5; consemnat pentru depanare.
|
||||
|
||||
**R7 — `ff_2026_08_08_01_COMUN_VVD_TOT.sql` aplicat in dev fara script pe disc. (de clarificat)**
|
||||
Obiectul nu exista in `all_objects` sub niciun nume `VVD%`, deci probabil scriptul a creat altceva
|
||||
sau a fost anulat ulterior. Fisierul lipseste din `SCRIPTURI_CLAR` -> nu ajunge la clienti.
|
||||
*Recomandare:* intrebare pentru Marius, nu blocheaza S5.
|
||||
|
||||
### Intrebari pentru Marius
|
||||
|
||||
1. **Liniile de set (R1)** — le facem needitabile in grid, cu `ID_VANZARE_SET` adaugat in
|
||||
`VVANZARI_ARTICOLE` printr-un al doilea script? *Recomandarea mea: da.* Fara asta, editarea unei
|
||||
facturi cu seturi arata ca merge si nu merge.
|
||||
2. **Discountul de document** — parametru al procedurii (`V_DISCOUNT ... DEFAULT NULL`), sau
|
||||
`UPDATE VANZARI SET DISCOUNT` separat din VFP? *Recomandarea mea: parametru*, ca discountul si
|
||||
totalurile sa nu poata diverge (vezi 5).
|
||||
3. **`PRET_ACHIZITIE` pe linia noua (R3)** — se citeste din nomenclator/stoc la adaugare, sau se
|
||||
scrie `0` cu avertizare? *Recomandarea mea: citit din nomenclator*, cu `0` doar ca ultima
|
||||
rezerva, niciodata NULL.
|
||||
4. **`VALVAL`/`TVAVAL`/`TOTVAL`** — raman in recalcul? *Recomandarea mea: da* (cost zero, fac parte
|
||||
din acelasi `SELECT`; excluderea lor ar lasa facturile in valuta inconsistente). Confirmare
|
||||
ceruta si in raportul precedent, inca neconfirmata.
|
||||
5. **`ff_2026_08_08_01_COMUN_VVD_TOT.sql`** — script de lucru abandonat, sau lipseste din SVN?
|
||||
|
||||
---
|
||||
|
||||
## Ce a ramas neverificat
|
||||
|
||||
- **R5**: nu am verificat pe codul S4 in lucru ca `CreeazaPoArticolNouTvd` primeste efectiv valuta
|
||||
documentului pe toate caile de adaugare de linie.
|
||||
- Nu am rulat nimic in Oracle in afara de `SELECT`-uri de dictionar si de export — **niciun DDL,
|
||||
niciun DML, niciun test de executie a procedurii propuse.** Corpul propus la 7 e schita, nu cod
|
||||
compilat.
|
||||
- Numarul de documente cu seturi in `MARIUSM_AUTO` (4) e din date de test si **nu constituie
|
||||
dovada** pentru niciuna dintre afirmatiile de mai sus; toate concluziile sunt din cod.
|
||||
Reference in New Issue
Block a user