sync SVN r18016: editare articole in factura de vanzare, precizie pret achizitie
ROAFACTURARE 2.11.15 (r18015): programe/ofacturare_editare.prg, programe/ofacturare_stoc.prg. docs: conventia de encoding .sc2/.vc2 corectata la cp1250; cercetarile trans-proiect mutate din ROAFACTURARE in docs/cercetare (r18014); actualizari reguli_lucru, oracle_export, flux-editare-vfp-text, scripturi-migrare-db, flux-modificare-stergere-nota-jurnal, todos. .gitignore: watchdog_out si PNG-urile din rularile headless (r18008). Text FoxBin2Prg regenerat: clase/comun.vc2, ferestre/frm_import_note_facturi_clienti.sc2, ferestre/frm_initializare_facturi_balanta.sc2. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AV7dQbTLAqvrtQbH7zxXxw
This commit is contained in:
35
docs/cercetare/ff_view_articole_vanzare.sql
Normal file
35
docs/cercetare/ff_view_articole_vanzare.sql
Normal file
@@ -0,0 +1,35 @@
|
||||
-- 08.08.2026 Marius Mutu
|
||||
-- VVANZARI_ARTICOLE: liniile unei vanzari (VANZARI_DETALII) cu denumirea articolului, a gestiunii
|
||||
-- si a valutei, pentru gridul de articole din formularul de modificare a notei. Valori brute, fara
|
||||
-- conversie valutara si fara calcule - editarea scrie inapoi exact ce a citit. Filtrul STERS ramane
|
||||
-- pe seama apelantului, ca la VACT_TOT / VRUL_TOT / VRUL_OBINV_TOT.
|
||||
|
||||
CREATE OR REPLACE VIEW VVANZARI_ARTICOLE AS
|
||||
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;
|
||||
|
||||
exec pack_migrare.UpdateVersiune('ff_2026_08_08_01_COMUN_VVANZARI_ARTICOLE');
|
||||
commit;
|
||||
127
docs/cercetare/handoff_watchdog_vfp.md
Normal file
127
docs/cercetare/handoff_watchdog_vfp.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# Handoff — watchdog VFP + bisectie blocaj "View Parameter" gnAn
|
||||
|
||||
Predare la peste 310k context (Regula zero). **Doar stare, fara analize noi.** Raport analitic
|
||||
complet (dovezi, log-uri, discutie): `docs\cercetare\rec_watchdog_vfp.md`.
|
||||
|
||||
## 1. Inventar livrabile, cu fisier:linie
|
||||
|
||||
**Utilitar**: `D:\ROA\ROAFACTURARE\COMUN\utile\Teste\watchdog_vfp.ps1`. Linia de comanda completa:
|
||||
|
||||
```
|
||||
powershell -ExecutionPolicy Bypass -File "D:\ROA\ROAFACTURARE\COMUN\utile\Teste\watchdog_vfp.ps1" -Script "<cale.prg>" -TimeoutSec 90 -AutoDismiss -MaxDialogs 10
|
||||
```
|
||||
|
||||
Lanseaza `vfp9.exe -A -T "<script>"`, detecteaza orice fereastra noua diferita de
|
||||
`Process.MainWindowHandle`, o captureaza (PNG + `WM_GETTEXT` pe titlu si controale copil) in
|
||||
`<folderul scriptului>\watchdog_out\`, si cu `-AutoDismiss` incearca s-o inchida STRICT prin mesaje
|
||||
Windows tintite pe handle (`BM_CLICK` pe buton Cancel/Anulare -> `WM_COMMAND IDCANCEL` ->
|
||||
`WM_KEYDOWN`/`WM_KEYUP` ca mesaj -> `WM_CLOSE`) - **FARA input real** (vezi sectiunea 3). Omoara
|
||||
procesul mereu la iesire (`finally`).
|
||||
|
||||
**Fisiere de test noi**:
|
||||
- `COMUN\utile\Teste\watchdog_selftest.prg` - caz banal de validare (MESSAGEBOX), folosit doar ca
|
||||
sa confirme ca watchdog-ul detecteaza/captureaza/dismite corect, inainte de cazul real.
|
||||
- `COMUN\utile\Teste\editare_factura\test_repro_izolat_gnan.prg` - experimentul B (izolat): DOAR
|
||||
`test_init_env_auto` + `update_jtva_coloane("", "crsJtvaTemp", 6)`, fara nimic din S4. NU
|
||||
reproduce blocajul - dovada ca `update_jtva_coloane`/`updateserver.prg` sunt nevinovate.
|
||||
|
||||
**Fisier de test modificat**: `COMUN\utile\Teste\editare_factura\test_page3_articole.prg`:
|
||||
- linia 176: `update_jtva_coloane("", "crsJtvaTemp", 6)` (parametrul schimbat din `0` in `6` -
|
||||
necesar pentru indexul `id_jtva` cerut de `Column63.ControlSource` din `omodificari.vc2:9199`;
|
||||
independent de defectul principal, fix cunoscut deja din `date_test_nnir.md`);
|
||||
- `PROCEDURE bisect_log_gnan` (noua, la coada fisierului, ~linia 236) + apeluri `DO bisect_log_gnan
|
||||
WITH <tag>, lcLog` inserate: in programul principal intre fiecare apel de nivel superior, si ca
|
||||
PRIMA linie in interiorul fiecarei proceduri (`verifica_vanzare_nota`, `verifica_coliziune_cod`,
|
||||
`verifica_pagecount_form`) - infrastructura de bisectie, ramasa in cod ca dovada;
|
||||
- **liniile 37, 39, 50 - remediul APLICAT DE TEAM-LEAD** (nu de mine, sub pragul lui de editare
|
||||
directa): `gnAn`/`gnLuna` parantezate - `(gnAn)`, `(gnLuna)` - in apelurile `DO verifica_vanzare_nota
|
||||
WITH ...` (x2) si `DO verifica_pagecount_form WITH ...` (primul), cu comentariu explicativ deasupra
|
||||
primei aparitii (liniile 34-36).
|
||||
|
||||
**Log-uri si capturi**: `test_page3_articole_log.txt` (log-ul aplicatiei, contine liniile `[BISECT]`),
|
||||
`watchdog_out\*.png` + `watchdog_out\*_watchdog.log` (capturi + jurnal watchdog, per fisier de test),
|
||||
toate in `COMUN\utile\Teste\editare_factura\`.
|
||||
|
||||
## 2. Ce e terminat, ce nu
|
||||
|
||||
**Terminat**:
|
||||
- Watchdog-ul: scris, testat pe caz banal, testat pe cazul real, corectat (heuristica de fereastra
|
||||
principala, bug de indexare PowerShell pe array cu 1 element, input real scos complet).
|
||||
- Bisectia: COMPLETA, cauza CONFIRMATA cu date brute (nu deductie) - vezi sectiunea 4.
|
||||
- Remediul: APLICAT de team-lead (liniile 37/39/50).
|
||||
|
||||
**Neterminat**: remediul NU a fost re-testat dupa aplicare. Ultima rulare a suitei (a mea) a fost
|
||||
INAINTE de remediul team-lead-ului. Team-lead ruleaza el insusi suita, separat - **eu nu mai rulez
|
||||
nimic** (interdictie explicita primita).
|
||||
|
||||
## 3. Stare fisiere - write-back
|
||||
|
||||
**Toate fisierele atinse in aceasta sesiune sunt `.prg`/`.ps1` (text simplu)** - NU exista
|
||||
`.vc2`/`.sc2`/`.vcx`/`.vct` atinse, deci **NU exista niciun write-back nefacut**.
|
||||
|
||||
Confirmare explicita: `COMUN\clase\omodificari.vc2/.vcx/.vct` si `COMUN\programe\updateserver.prg`
|
||||
sunt **NEATINSE** in aceasta sesiune (nici de mine, nici - din cate stiu - de altcineva). Fara
|
||||
commit git/svn facut sau initiat.
|
||||
|
||||
## 4. Ce s-a stabilit deja - NU relua
|
||||
|
||||
- **Cauza CONFIRMATA, nu ipoteza**: `test_page3_articole.prg` (inainte de remediu) trecea
|
||||
`gnAn`/`gnLuna` NEPARANTEZAT in `DO...WITH` (liniile 34, 36, 47 - numerotare dinainte de remediu).
|
||||
`DO proc WITH var` paseaza variabile de memorie BY REFERENCE implicit in VFP; `LPARAMETERS`
|
||||
primitor devine alias direct pe storage-ul original, iar numele original (`gnAn`) devine
|
||||
inaccesibil (`TYPE()='U'`) STRICT pe durata acelui apel, revenind valid imediat dupa `RETURN`.
|
||||
Confirmat simetric pe 3 apeluri afectate vs 2 neafectate (vezi tabelul din `rec_watchdog_vfp.md`
|
||||
sectiunea 5).
|
||||
- **`updateserver.prg`/`update_jtva_coloane` sunt nevinovate** - experimentul B (izolat, fara nimic
|
||||
din S4) NU reproduce; functia merge perfect cand `gnAn` e vizibil normal.
|
||||
- **`gnAn` NU e eliberata niciodata** - ramane valida (`TYPE()='N'`, 2026) la FIECARE checkpoint din
|
||||
scope-ul PRINCIPAL, fara exceptie. Nu exista `CLEAR ALL`/`CLEAR MEMORY`/`RELEASE ALL EXTENDED` pe
|
||||
lantul executat.
|
||||
- **Nu e coliziune de nume in corpul procedurii**: `verifica_pagecount_form` NU are `gnAn` in
|
||||
`LOCAL` (linia ei 148/159), si NU exista `PRIVATE gnAn`/`LOCAL gnAn` nicaieri in `COMUN\programe`.
|
||||
Umbrirea vine din SINTAXA APELULUI (`DO...WITH` neparantezat), nu din declaratiile callee-ului.
|
||||
- **Handler-ul de eroare** (`test_error_handler`/`ON ERROR`) doar logheaza (`STRTOFILE`) si continua
|
||||
- nu ascunde nimic, util pentru diagnostic.
|
||||
- **Incidentul de focus**: ESC-ul trimis de o versiune veche a watchdog-ului (cu `SetForegroundWindow`
|
||||
+ `keybd_event`, SCOASA complet acum) a aterizat de fapt in PROPRIUL nostru proces de test, NU in
|
||||
sesiunea utilizatorului - dar mecanismul tot nu are tinta si regula "fara input real" ramane
|
||||
obligatorie indiferent (documentat in `rec_watchdog_vfp.md` sectiunea 1, corectat acolo dupa o
|
||||
formulare initiala gresita).
|
||||
|
||||
## 5. Ce e interzis
|
||||
|
||||
- Input real de tastatura/mouse in watchdog (`keybd_event`, `SetForegroundWindow`, `SendInput`,
|
||||
`mouse_event`) - masina e PARTAJATA. Deja scos din cod, verificat cu grep (zero hit-uri de cod,
|
||||
doar comentarii care explica interdictia).
|
||||
- Atingerea `COMUN\clase\omodificari.vc2/.vcx/.vct` si `COMUN\programe\updateserver.prg`.
|
||||
- Commit git/svn.
|
||||
- **Eu nu mai rulez suita** - team-lead o ruleaza separat dupa remediul lui.
|
||||
|
||||
## 6. Capcane de mediu platite in aceasta sesiune
|
||||
|
||||
- **Heuristica "prima fereastra vazuta = principala" e o cursa pierduta**: un dialog poate aparea in
|
||||
aceeasi fractiune de secunda cu fereastra principala. Solutia corecta: `Process.MainWindowHandle`
|
||||
(.NET), NU ordinea de aparitie si NU numele clasei (VFP foloseste clase `vfp9...` si pentru shell,
|
||||
si pentru dialogurile proprii - `vfp99400000` vs `vfp994000002`).
|
||||
- **Dialogurile proprii VFP (ex. "View Parameter") sunt owner-drawn** - `EnumChildWindows` intoarce
|
||||
ZERO controale copil (butoanele sunt desenate, nu HWND-uri reale). `BM_CLICK` e imposibil pentru
|
||||
ele; chiar si `WM_CLOSE` poate "reusi" aparent (`IsWindow` -> fals) FARA sa deblocheze de fapt
|
||||
motorul VFP din spate (SQLEXEC ramas agatat, fara linie noua in log, pana la timeout) - un fals
|
||||
"succes" de retinut daca cineva reia mecanismul de dismiss.
|
||||
- **Bug PowerShell subtil**: un `List[string]` cu UN SINGUR element e "descompus" de PowerShell la
|
||||
scalar string simplu la `return` din functie (fara `,` unar) - indexarea ulterioara `$x[0]` citeste
|
||||
atunci primul CARACTER, nu primul element. Prins la dump-ul de text al dialogului "View Parameter"
|
||||
(fara controale copil = un singur element in listă). Fix: `return , $lista.ToArray()`.
|
||||
- **`.fxp` vechi blocat**: sterge intotdeauna `.fxp`-ul inainte de fiecare rulare (watchdog-ul o face
|
||||
singur) - altfel VFP ruleaza tacut codul vechi compilat.
|
||||
- **Bisectia**: `TRANSFORM(gnAn)` pe o variabila `TYPE()='U'` ARUNCA eroare catchabila ("Variable
|
||||
'GNAN' is not found") - helper-ul `bisect_log_gnan` verifica `TYPE()<>'U'` INAINTE de a apela
|
||||
`TRANSFORM()`, ca sa nu produca o eroare noua care ar fi intrerupt bisectia la primul checkpoint
|
||||
"gol".
|
||||
|
||||
## Confirmare finala
|
||||
|
||||
**PID 16548 nu mai exista** (verificat cu `Get-Process -Id 16548` - inexistent la momentul acestui
|
||||
handoff) si **niciun `vfp9.exe` nu ruleaza** (verificat cu `Get-Process vfp9` - lista goala). Nimic
|
||||
in stare periculoasa: fara editare pe jumatate, fara cursor/tranzactie Oracle deschisa (doar
|
||||
citiri), fara fisier binar atins. Sesiunea se opreste aici.
|
||||
210
docs/cercetare/import_roris_roaacnpro.md
Normal file
210
docs/cercetare/import_roris_roaacnpro.md
Normal file
@@ -0,0 +1,210 @@
|
||||
# Anatomia butonului "Import Roris & Contracte" (ROAACNPRO)
|
||||
|
||||
Cercetare read-only in `D:\ROA\ROAACNPRO`, ca model pentru un buton generic
|
||||
"importa articole din sursa" intr-un formular de facturare unificat in ROAFACTURARE.
|
||||
Nicio editare de fisiere, niciun `git_sync.ps1`/`txt2vcx.ps1`, niciun commit — doar investigatie.
|
||||
|
||||
## 1. Localizare
|
||||
|
||||
**Concluzie**: Butonul e `cmdImport` (clasa `cmd_executa`) pe formularul `frm_factura`, definit in
|
||||
`Clase\oacnpro.vc2`. Nu are `Click` propriu — mosteneste `Click` din clasa de baza a butoanelor,
|
||||
care ruleaza metoda numita in proprietatea `caction`; `cmdImport` nu suprascrie `caction`, deci
|
||||
ramane valoarea implicita `do_executa` mostenita de la clasa `cmd_executa`
|
||||
(`COMUN\clase\cmd_butoane.vc2:474`). Codul efectiv e in `PROCEDURE do_executa` a formularului
|
||||
`frm_factura`.
|
||||
|
||||
**Dovezi**:
|
||||
- `Clase\oacnpro.vc2:6894-6905` — definirea controlului:
|
||||
```
|
||||
ADD OBJECT 'cmdImport' AS cmd_executa WITH ... Caption = "\<Import Roris & Contracte", ... Left = 597, Top = 65, Width = 169
|
||||
```
|
||||
- `COMUN\clase\cmd_butoane.vc2:474` — `caction = do_executa` (mostenit de `cmd_executa`).
|
||||
- `COMUN\clase\_cmd_base.vc2:120-159` — `PROCEDURE Click` a clasei de baza: construieste
|
||||
`lcCommand = [this.Parent.] + lcAction + ...` si il executa cu macro (`&lcCommand`).
|
||||
- `Clase\oacnpro.vc2:7814-7836` — `PROCEDURE do_executa` a lui `frm_factura` (clasa incepe la
|
||||
`oacnpro.vc2:6398 DEFINE CLASS frm_factura`, se termina la `8383`).
|
||||
|
||||
## 2. Preconditii
|
||||
|
||||
**Concluzie**: Butonul e activ doar daca (a) factura nu e inca salvata (`llFacturaEditabila`) si
|
||||
(b) tipul facturii (`poDate.cTip`, ales din `cboTip`) e unul din
|
||||
`TRANZIT, CHEIAJ, CHIRII, APA, PENALITATI`. In plus, la Click, `do_executa` verifica separat ca a
|
||||
fost ales clientul.
|
||||
|
||||
**Dovezi**:
|
||||
- `Clase\oacnpro.vc2:8117-8145`:
|
||||
```
|
||||
llFacturaEditabila = !This.lFacturaSalvata
|
||||
llRorisSauContracte = INLIST(poDate.cTip, 'TRANZIT', 'CHEIAJ', 'CHIRII', 'APA', 'PENALITATI') && butonul import RORIS&Contracte este activ doar pentru RORIS sau contracte
|
||||
...
|
||||
.cmdImport.Enabled = m.llFacturaEditabila and m.llRorisSauContracte
|
||||
```
|
||||
- `Clase\oacnpro.vc2:7820-7823`:
|
||||
```
|
||||
IF EMPTY(NVL(this.nIdClient,0))
|
||||
AMESSAGEBOX('Alegeti clientul!',0+48,_screen.Caption)
|
||||
RETURN
|
||||
ENDIF
|
||||
```
|
||||
- Fara client ales, butonul e vizibil/activ dar Click-ul se opreste cu acest mesaj.
|
||||
|
||||
## 3. Ce face efectiv
|
||||
|
||||
**Concluzie**: `do_executa` -> `factura_import()` (`Programe\proceduri_acnpro.prg:3151`) ramifica
|
||||
dupa `poDate.cTip`:
|
||||
- TRANZIT/CHEIAJ: `vizualizare_tranzit()` -> deschide dialogul `frm_tranzit` (selectie convoaie din
|
||||
vederea Oracle `ips_vvoyages`) -> la confirmare, `calcul_tranzit()`/`calcul_cheiaj()` interogheaza
|
||||
direct (SELECT-uri simple, nu pachete PL/SQL) tabelele/vederile `ips_vvoyage_members_calcul`,
|
||||
`ips_vvoyage_locks`, apoi deschide un al doilea dialog de editare tarife (`frm_calcul_tranzit`).
|
||||
- CHIRII/APA: `vizualizare_contract()` -> `calcul_contract()` -> SELECT din vederea
|
||||
`vctr_articole2` (articole de contract) -> dialog `frm_calcul_contract`.
|
||||
- PENALITATI: `vizualizare_penalitati()` -> `frm_penalitati` cu cursorul `crsCalculPenalitati`
|
||||
calculat in VFP din vederile `vanzari`/`ireg_parteneri`/`penalitati`.
|
||||
|
||||
Dupa confirmarea dialogului (validat prin variabila globala `gnButon = 1`, standard framework
|
||||
pentru formulare modale), se apeleaza `make_factura_tranzit` / `make_factura_cheiaj` /
|
||||
`make_factura_contracte` / `make_factura_penalitati`, care insereaza direct in `crsFactura`.
|
||||
|
||||
**Important**: nicio procedura Oracle stocata (`pack_*`) nu e apelata in etapa de import — acestea
|
||||
(`pack_facturare.*`, `pack_acn.salveaza_regdoc`, `pack_contafin.*`) sunt apelate abia la
|
||||
**salvarea** finala a facturii (`factura_salvare_db`, `Programe\proceduri_acnpro.prg:3290+`), nu
|
||||
la import.
|
||||
|
||||
**Dovezi**:
|
||||
- `Programe\proceduri_acnpro.prg:3151-3212` (`Procedure factura_import`), `740-771`
|
||||
(`calcul_tranzit`, sursa `ips_vvoyage_members_calcul`), `1127-1161` (`calcul_contract`, sursa
|
||||
`vctr_articole2`).
|
||||
- `Programe\proceduri_acnpro.prg:939-943`:
|
||||
`loFrmTranzit = Crea("frm_calcul_tranzit", ...); loFrmTranzit.Show(1); llSucces = (m.gnButon = 1)`.
|
||||
- `Programe\proceduri_acnpro.prg:3331-3467` — apelurile `pack_facturare.*`/`pack_acn.salveaza_regdoc`
|
||||
sunt in `factura_salvare_db`, nu in `factura_import`.
|
||||
|
||||
## 4. Cum populeaza factura
|
||||
|
||||
**Concluzie**: Scrie **direct** in cursorul de articole al facturii, `crsFactura`, prin
|
||||
`INSERT INTO crsFactura(...)` in fiecare `make_factura_*` — nu trece prin vreo metoda generica
|
||||
"adauga articol" a formularului (exista o metoda separata `do_adauga_articol` pentru adaugare
|
||||
manuala de linie, dar importul n-o foloseste). Liniile se **adauga** peste cele existente (nu
|
||||
sterge/inlocuieste `crsFactura` inainte). Protectia la dublu-import e partiala:
|
||||
- dupa un import reusit, `This.cmdImport.Enabled = .F.` dezactiveaza butonul (nu se mai poate
|
||||
importa a doua oara in aceeasi sesiune de editare a facturii);
|
||||
- pentru TRANZIT/CHEIAJ exista un avertisment (nu blocaj) daca pe convoaiele alese exista deja
|
||||
facturi (`ips_vvoyages_vanzari.numar_act IS NOT NULL`) — utilizatorul poate continua oricum.
|
||||
|
||||
**Dovezi**:
|
||||
- `Clase\oacnpro.vc2:7827-7834`:
|
||||
```
|
||||
llSucces = factura_import(m.tlImportCalculat)
|
||||
IF m.llSucces
|
||||
This.cmdImport.Enabled = .F. && nu mai import altceva
|
||||
This.chkIntern.Enabled = .F.
|
||||
This.ActiveazaSalvare()
|
||||
...
|
||||
```
|
||||
- `Programe\proceduri_acnpro.prg:3601` (TRANZIT) / `3677-3678` (CHEIAJ) / `3764-3765`
|
||||
(CHIRII/APA): `Insert Into crsFactura(nrcrt, id_articol, denumire, ...)`.
|
||||
- `Clase\oacnpro.vc2:14915-14930` — avertisment dublu-import in `frm_tranzit.do_executa`:
|
||||
```
|
||||
SELECT numar_act, data_act, client FROM ips_vvoyages_vanzari WHERE vye_id in (...) AND numar_act is NOT null ...
|
||||
IF RECCOUNT('cFacturiTranzitTemp') > 0
|
||||
lcMesaj = 'Atentie! Exista facturi pe tranzitele alese!' + CHR(13)+CHR(10) + ...
|
||||
AMESSAGEBOX(m.lcMesaj,0+48,_screen.Caption)
|
||||
ENDIF
|
||||
```
|
||||
(mesaj informativ, apoi continua oricum cu `calcul_tranzit`/`calcul_cheiaj`).
|
||||
|
||||
## 5. Feedback catre utilizator
|
||||
|
||||
**Concluzie**: Mesaje punctuale prin `AMESSAGEBOX`, nu un sistem unitar de raportare:
|
||||
- Client lipsa: `'Alegeti clientul!'` (`oacnpro.vc2:7821`).
|
||||
- Convoi/liniile de import lipsa dupa dialog: `'Alegeti un convoi!'` in `make_factura_tranzit`
|
||||
daca `poDate.uuid` (id-urile alese) e gol (`proceduri_acnpro.prg:3530`); analog pentru
|
||||
CHIRII/APA prin `factura_salvare` (`'Alegeti convoiul'`/`'Adaugati prestatii'`,
|
||||
`proceduri_acnpro.prg:3221`, dar aceasta e verificare la *salvare*, nu la import).
|
||||
- Facturi deja existente pe convoaiele alese: avertisment neblocant (vezi punctul 4).
|
||||
- Zero rezultate in dialogul de selectie (`frm_tranzit`/`frm_calcul_contract`): niciun mesaj
|
||||
explicit gasit — grila ramane goala, iar la `Calculare`/`Terminat` fara nimic bifat `lcVyeIds`
|
||||
ramane vid si `IF !EMPTY(m.lcVyeIds)` (`oacnpro.vc2:14912`) sare peste tot calculul, fara mesaj
|
||||
catre utilizator (comportament implicit: butonul pare sa nu faca nimic).
|
||||
- Eroare Oracle: nu exista tratare `TRY/CATCH` explicita in `factura_import`/`calcul_tranzit`/
|
||||
`make_factura_*`; erorile SQL se propaga prin returul `.F.` al lui
|
||||
`goExecutor.oExecuta`/`oSelecteaza2Value`, iar afisarea erorii tine de comportamentul standard
|
||||
al `goExecutor` (framework comun, neexaminat in detaliu aici).
|
||||
|
||||
**Dovezi**: citatele de mai sus; pentru "zero rezultate fara mesaj" — `Clase\oacnpro.vc2:14898-14943`.
|
||||
|
||||
## 6. Selectivitate
|
||||
|
||||
**Concluzie**: Da, exista dialoage intermediare cu selectie, diferite pe tip:
|
||||
- **TRANZIT/CHEIAJ**: `frm_tranzit` (`oacnpro.vc2:14492-15003`) — grid `grdTranzit` cu coloane
|
||||
`Declaratia`, `Data decl.`, `Convoi`, `Origin`, `Destinatie` si o coloana checkbox `cAles`
|
||||
(`ControlSource='crsConvoaie.ales'`), plus criterii de cautare (`Cb_tx_cautare1`,
|
||||
`But_start_criterii1`/`But_reset_criterii1`) si buton `cmdCalculeaza` ("Calculare Tarife").
|
||||
Utilizatorul bifeaza unul sau mai multe convoaie; daca nu bifeaza niciunul, se ia randul curent
|
||||
(`lcVyeId` din `crsConvoaie` la pozitia curenta). Dupa `Calculare Tarife`, se deschide **al
|
||||
doilea nivel** — `frm_calcul_tranzit`/`frm_calcul_cheiaj` — pentru editarea/confirmarea
|
||||
tarifelor calculate, inainte de a reveni in factura.
|
||||
- **CHIRII/APA**: `calcul_contract()` deschide `frm_calcul_contract` cu cursorul
|
||||
`crsArticoleContract` (articol, perioada, um, pret, cantitate, valoare, valuta, locatie) din
|
||||
vederea `vctr_articole2` filtrata pe `id_ctr` — practic toate articolele contractului curent,
|
||||
editabile in acel dialog.
|
||||
- **PENALITATI**: `frm_penalitati` cu grid `grdPenalitati` (`crsCalculPenalitati`), coloane
|
||||
vizibile precum `cNrDoc`, `cSuma` ("Sold restant" in modul evaluare), `cDataDoc`.
|
||||
|
||||
Deci nu e un "importa tot" fara interventie — e mereu mediat de un dialog de vizualizare/calcul/
|
||||
confirmare (`gnButon = 1` = utilizatorul a confirmat).
|
||||
|
||||
**Dovezi**: `Clase\oacnpro.vc2:14500-14515` (obiecte grid + `cAles`), `14801`
|
||||
(`ControlSource='crsConvoaie.ales'`), `14594-14601` (`cmdCalculeaza`);
|
||||
`Programe\proceduri_acnpro.prg:1137-1153` (`crsArticoleContract` din `vctr_articole2`);
|
||||
`Programe\proceduri_acnpro.prg:545-555` (`frm_penalitati`, `grdPenalitati.cSuma`, `.cNrDoc`,
|
||||
`.cDataDoc`).
|
||||
|
||||
## 7. Butoanele vecine
|
||||
|
||||
**Concluzie**: Pe `frm_factura`, `cmdImport` e plasat in zona antetului facturii
|
||||
(`Left=597, Top=65`), langa `cboTip` (`Left=507, Top=67` — combo cu tipul facturii) si
|
||||
`chkIntern` (`Left=412, Top=70`) — nu langa bara principala de butoane. Bara principala de sus
|
||||
(`Top=0`) contine, in ordinea `Left`: `but_factura_noua` ("Nou", `Left=688`), `But_salveaza1`
|
||||
("Salveaza", `Left=719`), `But_renunt1` ("Renunta", `Left=749`). Separat, langa grila
|
||||
`grdFactura`, exista butoane de linie (`Top=120`): `But_nou1` ("Adauga linie", `Left=707`) si
|
||||
`But_sterge1` ("Sterge linie", `Left=737`). Mai sunt `but_cautare_client`/`but_cautare_beneficiar`
|
||||
(lupa cautare partener, langa campurile de client/beneficiar).
|
||||
|
||||
**Dovezi**: `Clase\oacnpro.vc2:6779-6818` (definitiile `but_factura_noua`, `But_nou1`,
|
||||
`But_renunt1`, `But_salveaza1`, `But_sterge1` cu `Left`/`Top`), `6836-6851` (`cboTip`),
|
||||
`6853-6863` (`chkIntern`), `6894-6905` (`cmdImport`).
|
||||
|
||||
## Ce e transferabil in ROAFACTURARE
|
||||
|
||||
Tiparul general merita copiat ca **model de arhitectura pentru un buton generic "importa
|
||||
articole din sursa"**:
|
||||
1. Buton activ conditionat de (a) factura editabila (nesalvata) si (b) tip de document care are o
|
||||
sursa de import — exact ca `llFacturaEditabila and llRorisSauContracte`.
|
||||
2. O metoda de intrare unica (`do_executa`/`factura_import`) care ramifica dupa tipul documentului
|
||||
catre functii specializate de "vizualizare/selectie" — fiecare deschide un dialog modal de
|
||||
selectie (grid cu coloana checkbox `ales`, criterii de cautare), returneaza succes/esec prin
|
||||
variabila globala tip `gnButon = 1`.
|
||||
3. Populare prin `INSERT INTO <cursor_articole>` direct, aditiv (nu sterge liniile existente), cu
|
||||
dezactivarea butonului dupa import reusit ca protectie minimala la dublu-import — plus
|
||||
(optional) un avertisment neblocant daca sursa a mai fost deja folosita in alta factura.
|
||||
4. Separarea neta intre etapa de **calcul/import** (SELECT-uri simple pe vederi Oracle, fara
|
||||
proceduri stocate) si etapa de **salvare** (unde intra pachetele PL/SQL) — util ca principiu:
|
||||
importul nu trebuie sa scrie definitiv in baza, doar sa populeze cursorul local.
|
||||
|
||||
Ce e strict specific ACN/RORIS si **nu** se transfera: sursele de date (`ips_vvoyages`,
|
||||
`ips_voyage_members_vanzari`, `ips_vberthing_details_vanzari`, `vctr_articole2`), logica de calcul
|
||||
tarife/ecluzari/porturi, formularele `frm_tranzit`/`frm_calcul_tranzit`/`frm_calcul_contract`/
|
||||
`frm_penalitati` ca atare, si valorile `poDate.cTip` (`TRANZIT/CHEIAJ/CHIRII/APA/PENALITATI`).
|
||||
|
||||
## Necunoscute ramase
|
||||
|
||||
- Comportamentul exact al `goExecutor.oExecuta`/`oSelecteaza2Value` la eroare Oracle (mesaj
|
||||
afisat, retry, log) — n-a fost verificat in profunzime (functie framework comun).
|
||||
- Continutul complet al dialogului `frm_calcul_tranzit`/`frm_calcul_cheiaj` (al doilea nivel,
|
||||
editare tarife) — am confirmat doar ca exista si ca `gnButon=1` marcheaza confirmarea, dar nu am
|
||||
detaliat coloanele/campurile lui.
|
||||
- Detaliile `frm_calcul_contract` (butoane, posibilitate de deselectare articole individuale) nu
|
||||
au fost citite integral — doar sursa cursorului.
|
||||
- Nu am verificat daca exista vreo validare suplimentara de "acelasi convoi importat de doua ori
|
||||
in aceeasi factura" dincolo de avertismentul cross-factura mentionat la punctul 4.
|
||||
220
docs/cercetare/rec_consumatori_vanzari.md
Normal file
220
docs/cercetare/rec_consumatori_vanzari.md
Normal file
@@ -0,0 +1,220 @@
|
||||
# Inventar consumatori `VANZARI.AVIZE`/`CURS`/`ID_VALUTA`/`MULTIPLICATOR` in suita ROA
|
||||
|
||||
Cercetare STRICT de citire (fara modificari de cod, fara DDL, fara conexiuni DB, fara rulare de
|
||||
conversii noi `vcx2txt.ps1`/`git_sync.ps1`) — masoara riscul de comportament schimbat dupa
|
||||
corectiile S4 (AVIZE, WHERE lipsa) si S8/curs (garda `nin_valuta`) aplicate in `PACK_FACTURARE`
|
||||
(vezi `rec_s4_aplicare.md`). Executata cu 3 subagenti in paralel: (a) ROAFACTURARE+COMUN, (b) cele
|
||||
8 produse cu cache text deja generat, (c) restul suitei (~56 directoare).
|
||||
|
||||
## RISC REAL
|
||||
|
||||
**1. Referinta la aviz dispare de pe factura retiparita (tip=4).**
|
||||
`COMUN\programe\oproceduri_facturare.prg` (`listeaza_formular`, ~:1125/:1189/:1265) si dublura ei
|
||||
`COMUN\clase\ofacturare_comun.vc2` (`frm_facturi.do_listeaza_formular`, ~:4103/:4204) — cod
|
||||
partajat, prezent in tot ecosistemul ROA*. Construiesc `crsFacturaListare` din `FACT_VFACTURI2`
|
||||
(coloana `altele` = alias pentru `vanzari.avize` cand `tip in (4,24,8,9)`), apoi
|
||||
`poDate.descriere = Alltrim(altele)` specific pentru `tip=4` ("factura din aviz"). `poDate.descriere`
|
||||
ajunge pe formularul tiparit ca referinta "nr. aviz" (layout exact in `.frx`, neconvertit — vezi
|
||||
gap mai jos). **Ce se schimba**: la retiparirea/relistarea unei facturi `tip=4` mai vechi, unde
|
||||
`vanzari.avize` era populat gresit inainte (smearat de UPDATE-ul fara WHERE) si acum e gol pentru
|
||||
majoritatea facturilor, referinta la aviz dispare vizibil de pe document. E in calea de reprint,
|
||||
nu de emitere — deci afecteaza utilizatorul la orice reeditare/retiparire ulterioara corectiei.
|
||||
|
||||
**2. Nota "Curs: X RON/valuta" pe factura electronica/PDF, generata fara verificare `in_valuta`.**
|
||||
`xmlefactura.prg` (COMUN partajat, cod identic — verificat prin hash — in ROAEFACTURA, ROASITFIN,
|
||||
ROACONIMPORT, ROAPRINT, ROAPRODUCTIE, ROACONT/OUTPUT, si prezent si in ROAFACTURARE/COMUN):
|
||||
```
|
||||
IF !EMPTY(NVL(loDate.curs,0)) AND ATC('curs', m.lcTextAditional) = 0
|
||||
lcTextAditional = ... + "Curs: " + ALLTRIM(STR(loDate.curs,10,4)) + " RON/" + ... loDate.cValuta ...
|
||||
ENDIF
|
||||
```
|
||||
Conditia verifica doar `curs` nenul, nu si `loDate.in_valuta` (spre deosebire de blocul de cateva
|
||||
linii mai jos, `DocumentCurrencyCode`, care testeaza corect `in_valuta=1`). Inainte, facturile in
|
||||
LEI aveau deja `curs` populat gresit (bug), deci nota aparea eronat, dar cu o valoare "plauzibila".
|
||||
**Ce se schimba**: dupa corectie, `curs=1` pe facturile in lei — `!EMPTY(1)` tot trece, deci nota
|
||||
tot apare, dar acum arata constant `"Curs: 1.0000 RON/"` cu sufix de valuta gol (`id_valuta=0`).
|
||||
Vizibil pe fiecare factura/aviz in lei exportata ca e-factura sau tiparita, in toate produsele de
|
||||
mai sus.
|
||||
|
||||
## RISC POSIBIL
|
||||
|
||||
3. **`ofacturare_comun.vc2:4156-4157`** (si dublura `oproceduri_facturare.prg:1225-1226`): dupa
|
||||
blocul `If poDate.in_valuta = 1 ... Else ... Endif`, `poDate.Curs`/`poDate.multiplicator` sunt
|
||||
suprascrise necondiționat din valorile citite din view. Calculul de discount e corect protejat
|
||||
de `in_valuta=1`, dar nu s-a putut confirma daca FRX-ul (neconvertit) afiseaza aceste campuri
|
||||
necondiționat de `in_valuta`. De verificat in layout-ul tiparit.
|
||||
4. **`vizualizare_facturi2` (`oproceduri_facturare.prg:431-479`)**: grid-ul de cautare/listare
|
||||
facturi expune coloanele `altele`, `curs`, `multiplicator`, `valuta`, `id_valuta`, `nume_val`
|
||||
direct din `FACT_VFACTURI2`. Nu s-a putut confirma legarea la `ControlSource` in `.scx`
|
||||
(neconvertit) — daca vreuna e legata vizibil intr-un grid, utilizatorii ar vedea coloane
|
||||
goale/"1" unde inainte vedeau valori (gresite).
|
||||
5. **ROAACNPRO** `Programe\proceduri_acnpro.prg:1180-1229`, `calcul_penalitati` (calea activa):
|
||||
`Select v.Curs, v.id_Valuta ... left join vnom_valute vl on v.id_valuta = vl.id_valuta From
|
||||
vanzari v`, afisat probabil intr-un grid de review inainte de generarea facturilor de
|
||||
penalizare. Calculul numeric al penalitatii NU foloseste `curs` (confirmat), deci fara risc de
|
||||
calcul; ramane risc de **afisare**: `id_valuta=0` ar putea sa nu se potriveasca in
|
||||
`left join vnom_valute` (tabela de valute proprie ACNPRO, distincta de `NOM_VALUTE`), lasand
|
||||
coloana `valuta` goala. Nu s-a putut confirma daca `vnom_valute` are un rand pentru
|
||||
`id_valuta=0` (spre deosebire de `NOM_VALUTE`, unde existenta randului `id_valuta=0` a fost deja
|
||||
confirmata in `rec_s4_aplicare.md`, punctul 3 din sectiunea S8/curs — deci pentru view-urile
|
||||
`FACT_VFACTURI*` din COMUN acest risc e deja exclus, ramane specific tabelei proprii ACNPRO).
|
||||
6. **ROAAUTO** `Programe\oproceduri_devize.prg:1486-1532`, `relisteaza_factura_deviz`: citeste
|
||||
`curs`/`multiplicator`/`altele` din `fact_vfacturi` la reafisarea unei facturi, dar cursorul se
|
||||
inchide imediat fara ca valorile sa fie propagate mai departe — risc redus, de reverificat doar
|
||||
daca un apelant viitor se bazeaza pe acelasi cursor.
|
||||
7. **`CONTAFIN2ORA\VFP2ORA\Programe\acn.prg:~1247-1319`** (unealta de migrare, nu produs curent de
|
||||
vanzare): scrie direct `curs`/`id_valuta`/`multiplicator` in `INSERT INTO VANZARI` +
|
||||
`MERGE INTO VANZARI_CURSURI`, cu propria regula de zero-ing independenta de `PACK_FACTURARE` —
|
||||
probabil neafectata functional, dar merita o verificare separata daca unealta mai e folosita
|
||||
activ.
|
||||
|
||||
## FARA RISC (rezumat, nu enumerate)
|
||||
|
||||
- **ROAFACTURARE+COMUN**: restul celor ~930 potriviri brute pentru `AVIZE`/`CURS`/`ID_VALUTA`/
|
||||
`MULTIPLICATOR`/`ALTELE` — module NIR/import, balante/parteneri/compensari, salarii, curs
|
||||
valutar BNR, sau citiri deja corect protejate de `in_valuta`/`tip_valuta`
|
||||
(`ofacturare.prg::listeaza_ofacturare` la emitere, `ofacturare_stoc.prg`, `anaf_efactura.prg` cu
|
||||
`decode(in_valuta,1,curs,1)`, `makexmlfacturaelectronica.prg` cu garda `mmoneda<>"RON"`). Niciun
|
||||
hit `.avize` (acces direct de camp) in afara celor doua raportate mai sus.
|
||||
- **ROACONT**: `Programe\saft_d406.prg` (declaratia fiscala D406, `oSalesInvoices`) — foloseste
|
||||
`DECODE(v.in_valuta, 0, 0, ...)` explicit, output SAF-T neschimbat de corectie. Restul hit-urilor
|
||||
pe tabele proprii (`ireg_parteneri`, `act`, `rul`) sau text necorelat.
|
||||
- **ROAACNPRO** (restul), **ROACONTRACTE**, **ROAGEST**, **ROAIMOB**, **ROAREGISTRATURA**,
|
||||
**ROASTART**: zero hit relevant legat de `VANZARI` in afara COMUN (verificat, nu doar negasit).
|
||||
- **~40 de produse din restul suitei** (ROAEFACTURA si variante, ROASITFIN, ROAPRINT,
|
||||
ROACONIMPORT, ROAPRODUCTIE, ROAHOTEL si variante, ROASAL, ROAMANAGER, ROADECL, ROAPRETURI,
|
||||
ROABAZA, ROAAPROV, ROADEVIZE, etc.): hit-uri pe "AVIZE" = conceptul de business "aviz de
|
||||
expeditie" (tip document), nu coloana; hit-uri "CURS"/"ID_VALUTA" pe alte tabele proprii sau in
|
||||
`oDateFactura` (populate de apelant, protejate de `in_valuta`). `COMUNROA` (depozitul central)
|
||||
contine doar scripturi de deployment, fara logica de facturare.
|
||||
|
||||
## Rezumat acoperire
|
||||
|
||||
- **ROAFACTURARE + COMUN local**: acoperire completa `.prg` + `.vc2`/`.sc2` via `vfp_symbols.ps1
|
||||
-CodeOnly` (index deja construit). **Gap**: 207 `.frx` si 30 `.mnx` fara `.fr2`/`.mn2` in cache —
|
||||
layout-ul efectiv tiparit (unde `poDate.descriere`/`Curs`/`cValuta` chiar apar pe hartie) nu e
|
||||
verificabil fara conversie (interzisa in acest task). Riscurile REAL #1/#2 si POSIBIL #3/#4
|
||||
depind partial de acest layout.
|
||||
- **8 produse cu cache text existent** (ROAACNPRO, ROAAUTO, ROACONT, ROACONTRACTE, ROAGEST,
|
||||
ROAIMOB, ROAREGISTRATURA, ROASTART): acoperire completa `.prg` + `.vc2`/`.sc2` via
|
||||
`vfp_symbols.ps1 -CodeOnly`. **Gap**: proprietati/metadata (`ControlSource` de grid legat direct
|
||||
de o coloana, fara linie de cod explicita) nu apar in `-CodeOnly` — dar un asemenea caz s-ar
|
||||
clasifica oricum FARA RISC (simpla afisare).
|
||||
- **Restul suitei** (~56 directoare, inclusiv `COMUNROA`): acoperire **doar** pe fisierele `.prg`
|
||||
(text simplu, nu necesita conversie); zero cache text pentru `.vcx`/`.scx` — **niciun cod din
|
||||
clase/formulare compilate al acestor produse nu a fost verificat**, conform interdictiei de a
|
||||
genera cache nou. ~35 produse identificate ca in afara domeniului (unelte/infrastructura: SSH,
|
||||
server, telefonie, criptare, declaratii D1xx/D3xx/D406 etc.) fara nicio potrivire pe termenii
|
||||
cautati.
|
||||
- Nicio conexiune la baza de date folosita; cifrele despre distributia datelor (ex. randul
|
||||
`id_valuta=0` din `NOM_VALUTE`) sunt preluate din `rec_s4_aplicare.md`, deja documentate.
|
||||
|
||||
## Concluzie
|
||||
|
||||
Doua locuri cu **risc real** confirmat, ambele in codul COMUN partajat (deci efect cross-project):
|
||||
disparitia referintei la aviz de pe facturile `tip=4` retiparite, si textul "Curs: 1.0000 RON/"
|
||||
afisat gresit pe nota facturii electronice/PDF pentru facturile in lei. Restul e risc posibil,
|
||||
dependent de layout-uri `.frx`/`.scx` neconvertite (gap de acoperire cunoscut, nu absenta de risc).
|
||||
Nu s-a gasit niciun consumator cu risc real de **calcul** (sume/discounturi) — toate caile de
|
||||
calcul verificate sunt deja protejate corect de `in_valuta`.
|
||||
|
||||
---
|
||||
|
||||
## Corectie aplicata — riscul REAL #2 (nota "Curs:" din `xmlefactura.prg`)
|
||||
|
||||
Modificare de cod, ceruta explicit de team-lead dupa livrarea inventarului de mai sus. Domeniu:
|
||||
**doar** `D:\ROA\ROAFACTURARE\COMUN\programe\xmlefactura.prg`. Nicio alta copie din suita nu a
|
||||
fost atinsa.
|
||||
|
||||
### Ce s-a schimbat
|
||||
|
||||
Linia 374 (acum singura diferenta fata de fisierul dinainte de editare):
|
||||
```diff
|
||||
- IF !EMPTY(NVL(loDate.curs,0)) AND ATC('curs', m.lcTextAditional) = 0
|
||||
+ IF loDate.in_valuta = 1 AND !EMPTY(NVL(loDate.curs,0)) AND ATC('curs', m.lcTextAditional) = 0
|
||||
lcTextAditional = m.lcTextAditional + Iif(!Empty(m.lcTextAditional), ' # ', '') + "Curs: " + ...
|
||||
ENDIF
|
||||
```
|
||||
Garda adaugata (`loDate.in_valuta = 1 AND`) e preluata **identic** din modelul deja corect din
|
||||
acelasi fisier, la 9 linii mai jos (acum `:385`, era `:384`): `If loDate.in_valuta = 1` /
|
||||
`oinvoice.lastchild.Text = m.mmoneda`, folosit pentru `DocumentCurrencyCode`. Nicio forma noua
|
||||
inventata. Fara comentarii adaugate (regula 2, rezolvari de erori). Diff complet:
|
||||
`D:\ROA\ROAFACTURARE\docs\diff_xmlefactura_garda_valuta.patch`.
|
||||
|
||||
### Verificare pe cazuri (dupa modificare)
|
||||
|
||||
1. **Factura in lei (`in_valuta=0`), `curs=1`** (comportamentul nou, generalizat, al facturilor in
|
||||
lei dupa corectia `PACK_FACTURARE`): inainte, `!EMPTY(NVL(1,0))=.T.` -> nota aparea cu
|
||||
`"Curs: 1.0000 RON/"` (valuta goala). Acum, `loDate.in_valuta = 1` e `.F.` -> intreg `AND`-ul e
|
||||
fals -> nota NU se mai adauga. **Singurul caz care isi schimba comportamentul** (cel vizat).
|
||||
2. **Factura in valuta reala (`in_valuta=1`)**, curs populat normal: `loDate.in_valuta = 1` e
|
||||
`.T.`, restul conditiei neschimbat -> nota apare identic ca inainte. Neschimbat.
|
||||
3. **Factura veche cu `curs` NULL** (orice `in_valuta`): `NVL(NULL,0)=0` -> `!EMPTY(0)=.F.` ->
|
||||
conditia era deja falsa inainte de garda si ramane falsa (garda adaugata doar restrange in plus
|
||||
cazul `in_valuta=0`, nu schimba nimic pe ramura `curs` NULL). Neschimbat.
|
||||
|
||||
Confirmat: doar cazul 1 (facturi in lei cu `curs` efectiv nenul) isi schimba comportamentul, exact
|
||||
riscul identificat.
|
||||
|
||||
### Verificare encoding (byte-level)
|
||||
|
||||
Fisierul contine octeti cp1252 `>=0x80` (`0xEE` = "î", la liniile 1203 si 1330 — "puteti încarca
|
||||
prin SPV"), deci risc de corupere la scriere cu tool care nu pastreaza octetii nativ. Editare
|
||||
facuta cu `perl` in mod raw (`s/.../.../ `), nu cu Edit/Write:
|
||||
- Backup pre-editare: `xmlefactura.prg.pre_runda.bak` (md5 `e800c71bd08e6c472b6fe912437f37f8`,
|
||||
62093 octeti).
|
||||
- Dupa editare: md5 `c9c9f4774346b2e62bfe9c12ea02dfb3`, 62118 octeti (+25 = exact lungimea
|
||||
textului `loDate.in_valuta = 1 AND ` inserat).
|
||||
- Comparatie linie-cu-linie (raw bytes) intre backup si fisierul editat: **1364 linii in ambele,
|
||||
o singura linie diferita (374)** — restul fisierului byte-identic.
|
||||
- Octetii `0xEE` de la liniile 1203/1330 confirmati neschimbati dupa editare.
|
||||
- Scanare pentru markeri de corupere (`EF BF BD` = U+FFFD reincodat): zero potriviri.
|
||||
|
||||
Encoding intact, nicio corupere.
|
||||
|
||||
### Copii identice/asemanatoare in suita — corectie fata de raportul initial
|
||||
|
||||
Raportul initial (sectiunea RISC REAL #2) afirma ca fisierul e "identic prin hash" in ROAEFACTURA,
|
||||
ROASITFIN, ROACONIMPORT, ROAPRINT, ROAPRODUCTIE si ROACONT/OUTPUT — verificare hash directa acum
|
||||
(md5 pe tot arborele `D:\ROA`, exclus `DATABASE`) arata ca afirmatia era **partial gresita**: doar
|
||||
`ROACONIMPORT` si `ROAPRINT` sunt byte-identice intre ele; restul au fiecare continut propriu,
|
||||
divergent de `ROAFACTURARE`. Ce e adevarat si ramane valabil: **toate contin acelasi tipar de cod
|
||||
vulnerabil** (linia cu conditia fara garda pe `in_valuta`), verificat cu grep, o singura aparitie
|
||||
in fiecare fisier.
|
||||
|
||||
**Grup A — fisier byte-identic cu `ROAFACTURARE/COMUN` INAINTE de editarea de azi**
|
||||
(md5 `e800c71bd08e6c472b6fe912437f37f8`, deci acelasi patch de o linie de la `:374` se aplica
|
||||
identic, byte cu byte, in toate):
|
||||
`ROAACNPRO`, `ROAAUTO`, `ROACONT`, `ROACONTRACTE`, `ROADEF`, `ROAGEST`, `ROAIMOB`,
|
||||
`ROAREGISTRATURA`, `ROARES`, `ROASTART` — cale `D:\ROA\<PRODUS>\COMUN\programe\xmlefactura.prg`.
|
||||
(In plus, acelasi hash apare si in foldere de backup istoric fara relevanta:
|
||||
`_backup_comun_conflicts\*`, `_backup_roaimob_comun_conflict\*` — nu sunt copii vii.)
|
||||
|
||||
**Grup B — fisier divergent (alt continut/alte linii in rest), dar cu ACELASI tipar vulnerabil**
|
||||
(conditie identica textual, gasita o singura data per fisier, doar la alt numar de linie):
|
||||
| Produs | Cale | md5 | Linia conditiei |
|
||||
|---|---|---|---|
|
||||
| OUTPUT/ROACONT | `D:\ROA\OUTPUT\ROACONT\COMUN\programe\xmlefactura.prg` | `504dc24e771f6d66e4f028d1347671a8` | 350 |
|
||||
| ROACONIMPORT | `D:\ROA\ROACONIMPORT\COMUN\programe\xmlefactura.prg` | `fa1e389839109ae46da1ef815e102259` | 356 |
|
||||
| ROAEFACTURA | `D:\ROA\ROAEFACTURA\COMUN\programe\xmlefactura.prg` | `200442ad86e180c2484c96367ac9514a` | 356 |
|
||||
| ROAPRINT | `D:\ROA\ROAPRINT\COMUN\programe\xmlefactura.prg` | `fa1e389839109ae46da1ef815e102259` | 356 |
|
||||
| ROAPRODUCTIE | `D:\ROA\ROAPRODUCTIE\COMUN\programe\xmlefactura.prg` | `b71c1d9284d8ee78d02429b04c968a35` | 326 |
|
||||
| ROASITFIN | `D:\ROA\ROASITFIN\COMUN\programe\xmlefactura.prg` | `47bb2e49770713b396e855e8af0ae2ea` | 356 |
|
||||
|
||||
**Grup C — NU are acest bloc de cod deloc** (verificat, nu doar negasit — nu construiesc nota de
|
||||
curs valutar in e-factura): `ROADECL`, `ROAMANAGER`, `ROAPRETURI`, `ROASAL`. Fara risc, fara
|
||||
propagare necesara.
|
||||
|
||||
**Fisierul modificat azi**: doar `D:\ROA\ROAFACTURARE\COMUN\programe\xmlefactura.prg` (md5 nou
|
||||
`c9c9f4774346b2e62bfe9c12ea02dfb3`). Toate celelalte 16 cai listate mai sus (Grup A + Grup B)
|
||||
raman NEATINSE, cu bug-ul inca prezent — propagarea e decizie de proces a lui Marius, nu s-a facut
|
||||
aici.
|
||||
|
||||
### Livrabile
|
||||
|
||||
- Modificare: `D:\ROA\ROAFACTURARE\COMUN\programe\xmlefactura.prg` (linia 374, +garda `in_valuta`).
|
||||
- Patch de review: `D:\ROA\ROAFACTURARE\docs\diff_xmlefactura_garda_valuta.patch`.
|
||||
- Backup pre-editare (ramas pe disc, netracked): `xmlefactura.prg.pre_runda.bak` in acelasi folder.
|
||||
- Aceasta sectiune.
|
||||
|
||||
Fara commit git/svn — asteapta aprobarea patch-ului.
|
||||
358
docs/cercetare/rec_integrari.md
Normal file
358
docs/cercetare/rec_integrari.md
Normal file
@@ -0,0 +1,358 @@
|
||||
# Cercetare: integrare CONTRACTE, politici de preturi, nomenclator ca lista de preturi
|
||||
|
||||
Metoda: `vfp_symbols.ps1` (cache text ROAFACTURARE deja la zi) + Grep pe `.prg`/`.vc2`/`.mn2`.
|
||||
Fapte cu `fisier:linie`; ipoteze marcate `IPOTEZA:`.
|
||||
|
||||
## SUBIECT A - Integrare pagina CONTRACTE (todo #10)
|
||||
|
||||
### 1. Unde exista azi contractele
|
||||
|
||||
Produs separat, working copy completa: `D:\ROA\ROACONTRACTE` (`.git` + `.svn`, `roaContracte.pjx`,
|
||||
`roacontracte.exe`). Structura: `Clase\` (`ofundal.vcx`, `onom_clienti.vcx`, `oOptiuni.vcx`,
|
||||
`roaclienti.vcx`, `ferestre_contracte.vcx` - probabil formularele CRUD de contracte),
|
||||
`Ferestre\`, `Programe\`, `Rapoarte\`, `Meniuri\`, `COMUN\` (propria copie a librariei partajate),
|
||||
`Teste\`, `docs\`.
|
||||
|
||||
**Important**: ROAFACTURARE NU e izolat de contracte azi - are deja o integrare de facturare
|
||||
partiala "pe baza de contract" (vezi punctul 4), care citeste direct din schema Oracle a
|
||||
ROACONTRACTE prin view-uri (`vcontracte`, `fact_vcontracte`, `tipuri_contracte`), fara pagina de
|
||||
editare. `ferestre_contracte.vcx` din ROACONTRACTE contine probabil formularele CRUD care ar
|
||||
trebui aduse in ROAFACTURARE (nu au fost deschise/citite - binar, necesita conversie separata daca
|
||||
se trece la implementare).
|
||||
|
||||
Exista deja urme ale unei integrari partiale de UI in ROAFACTURARE:
|
||||
- `Meniuri\contracte.mnx`/`.mn2` (`D:\ROA\ROAFACTURARE\Meniuri\contracte.mn2:1`) - NU e un meniu de
|
||||
administrare contracte, ci un shortcut-popup cu 3 optiuni de facturare ("Factura fiscala lei /
|
||||
Invoice / Factura fiscala valuta") - cf. continut citit integral.
|
||||
- `Grafice\icon_contracte1.png`, `icon_contracte2.png`, `Grafice\Originale\contracte.png` -
|
||||
iconite deja pregatite in ROAFACTURARE.
|
||||
|
||||
### 2. Cum e integrata azi COMENZI in ROAFACTURARE (sablonul de urmat)
|
||||
|
||||
COMENZI **nu** e produs separat legat prin exe, ci cod montat direct in ROAFACTURARE din
|
||||
`COMUN\` (librarie partajata `gitea.romfast.ro:romfast/comun.git`, cf. CLAUDE.md). Exista si un
|
||||
produs stand-alone `D:\ROA\ROACOMENZI` (cu `.pjx` propriu), dar in ROAFACTURARE comenzile sunt
|
||||
o **pagina/panou montat direct in formularul principal (fundal)**, nu un exe separat lansat.
|
||||
|
||||
Reteta pas cu pas (comenzi ca model pentru contracte):
|
||||
|
||||
1. **Clasa container** `ct_comenzi` din `COMUN\clase\ocomenzi.vcx` (`.vc2` cache:
|
||||
`COMUN\clase\ocomenzi.vc2`) - contine formulare/containere CRUD comenzi
|
||||
(`frm_optiuni_comenzi`, cursoare `vcomenzi_elemente` etc.).
|
||||
2. **Montare in formularul principal**: containerul e plasat ca obiect copil in
|
||||
`Clase\ofundal_facturare.vc2` (form fundal), cu comentariul `< END OBJECT:
|
||||
ClassLib="..\comun\clase\ocomenzi.vcx" BaseClass="container" />` in jurul liniei
|
||||
`Clase\ofundal_facturare.vc2:831`; obiectul se numeste `lb_comenzi`
|
||||
(`Clase\ofundal_facturare.vc2:824`).
|
||||
3. **Butoane de actiune** ("Cw" = clase de tip buton-cu-drept, vezi punctul 3) legate la proceduri
|
||||
business, ex. `Page2.Cw3.do_actiune` -> `DO facturare_comenzi IN oproceduri_facturare.prg`
|
||||
(`Clase\ofundal_facturare.vc2:899-902`).
|
||||
4. **Inregistrare in `Programe\roafacturare.prg`** (entry point):
|
||||
- `SET CLASSLIB TO ocomenzi ADDITIVE` sub comentariul `*** COMENZI`
|
||||
(`Programe\roafacturare.prg:180-181`);
|
||||
- `SET PROCEDURE TO orap_comenzi.prg / onom_comenzi.prg / update_comenzi.prg ADDITIVE`
|
||||
(`Programe\roafacturare.prg:239-242`), tot sub `*** COMENZI`;
|
||||
- variabile module: `PRIVATE pocomenzi,pocomenzielemente,polucrari,pocomenzi2,polucrarielemente`
|
||||
(`Programe\roafacturare.prg:246-247`).
|
||||
- Toate cele 3 `.prg` (`orap_comenzi.prg`, `onom_comenzi.prg`, `update_comenzi.prg`) si clasa
|
||||
`ocomenzi.vcx`/`.vct` locuiesc fizic in `COMUN\programe\` / `COMUN\clase\`, dar sunt
|
||||
inregistrate ca membri ai proiectului `roafacturare.pjx` (confirmat prin Grep pe `.pjx`:
|
||||
`COMUN\clase\ocomenzi.vcx`, `COMUN\programe\orap_comenzi.prg` etc. apar in el).
|
||||
5. **Business logic de facturare din comenzi**: `Procedure facturare_comenzi` in
|
||||
`COMUN\programe\oproceduri_facturare.prg:139-141` - un simplu `factureaza(3)` (tip document 3
|
||||
= "din comanda", motorul central `factureaza()` face restul).
|
||||
6. **Meniu**: nu exista `Meniuri\comenzi.mnx` separat in ROAFACTURARE - comenzile nu au intrare de
|
||||
meniu proprie, ci doar butonul `Cw3` de pe pagina fundal (Page2 = "Facturare"). Contractele au
|
||||
deja `Meniuri\contracte.mnx` dar cu alt continut (shortcut factura), deci pentru pagina noua de
|
||||
contracte ar trebui fie extins acest fisier, fie creat altul.
|
||||
|
||||
Concluzie sablon: pentru CONTRACTE ar insemna (a) o clasa container tip `ct_contracte` (posibil
|
||||
adaptata din `ferestre_contracte.vcx` al ROACONTRACTE, mutata/duplicata in `COMUN\clase\`), (b)
|
||||
montarea ei ca obiect in `ofundal_facturare.vc2` langa `lb_comenzi`, (c) inregistrare `SET
|
||||
CLASSLIB`/`SET PROCEDURE` in `roafacturare.prg` sub un bloc nou `*** CONTRACTE`, (d) adaugare in
|
||||
`roafacturare.pjx`.
|
||||
|
||||
### 3. Mecanismul de DREPTURI pe obiecte
|
||||
|
||||
Sursa: `COMUN\programe\acces_meniu.prg` (fisier citit integral).
|
||||
|
||||
- **Sursa de date**: view Oracle `contafin_oracle.vdef_util_obiecte`, interogat cu
|
||||
`select cheie,id_firma from contafin_oracle.vdef_util_obiecte where id_util=?gnIdUtil and
|
||||
id_program=?gnIdProgram and id_firma=?gnIdFirma` (`acces_meniu.prg:29-31`), rezultat in cursorul
|
||||
`crsdrepturi` (o singura coloana cheie relevanta: `cheie`, string).
|
||||
- **Codificarea cheii**: concatenare de "caractere de nivel" - `Chr(lnKey)` pentru fiecare nivel de
|
||||
pageframe/pagina (`dezactiveaza_obiecte_pageframe`, `acces_meniu.prg:103-165`, recursiv pe
|
||||
subpageframe-uri), plus un cod de 2 cifre pentru fiecare buton `Cw*`:
|
||||
`lcCheie = lcKey + Padl(Alltrim(Str(.Objects(l).nid_cw)), 2, '0')` (`acces_meniu.prg:138`).
|
||||
Fiecare obiect `Cw*` are proprietatea `nid_cw` (numarul lui in cadrul paginii) si la runtime i se
|
||||
seteaza `ccheie` (`acces_meniu.prg:140`) si `coptiuni_active` (lista de operatii CRUD permise,
|
||||
citita din caracterele urmatoare cheii - `acces_meniu.prg:141-149`). Butonul apeleaza
|
||||
`.Objects(l).activeaza()` / `.dezactiveaza()` in functie de gasire (`acces_meniu.prg:150-152`).
|
||||
- **Pentru imagini/iconite** (nivel diferit, folosit pe alte forme): proprietate `ccod` pe obiect
|
||||
(`acces_meniu.prg:48`), aceeasi logica de cautare in `crsdrepturi`.
|
||||
- **Cod de meniu (pad-uri)**: `GetAccesByCod(tcCod, tcAccesDefault)` (`acces_meniu.prg:236-276`)
|
||||
cauta o cheie explicita (cod optiune meniu, ex. "ZA01") in `crsdrepturi` si intoarce lista de
|
||||
operatii permise (ex. "1;2;3;4").
|
||||
- **Punct de intrare**: `verifica_drepturi(tcObiectFundal, tcPageFrame)`
|
||||
(`acces_meniu.prg:9-15`) apelat din formularul fundal (`Ferestre\fundal.sc2:699`:
|
||||
`verifica_drepturi('gofundal','_pgfrmbase1')`), care incarca `crsdrepturi` o singura data per
|
||||
firma (cache in memorie, `citeste_drepturi`, `acces_meniu.prg:17-37`) si dezactiveaza in cascada
|
||||
paginile/butoanele/meniurile fara drept.
|
||||
- **Administrare drepturi** (unde se declara catalogul de obiecte si se atribuie pe grupuri):
|
||||
`COMUN\clase\drept_grupuri.vc2` - `frm_grupuri`, apel catre pachetul Oracle
|
||||
`PACK_DREPTURI.grupdreptmodproc` (`COMUN\clase\drept_grupuri.vc2:39`) si `citeste_drepturi
|
||||
(loRec.id_grup)` (`COMUN\clase\drept_grupuri.vc2:205`).
|
||||
|
||||
IPOTEZA: catalogul efectiv de "obiecte disponibile pentru ROAFACTURARE" (denumirile/codurile
|
||||
`nid_cw`/`ccod`/coduri de meniu, ex. cele pentru COMENZI) e definit **partial in designerul VFP**
|
||||
(proprietatea `nid_cw` seteaza pe fiecare buton la design-time in `.scx`/`.vcx`) si **partial
|
||||
server-side** in schema Oracle `contafin_oracle` (tabelul din spatele view-ului
|
||||
`vdef_util_obiecte`, populat probabil printr-un script de instalare/migrare, nu vazut in sursa
|
||||
VFP). Pentru "comasarea" drepturilor ROACONTRACTE + ROAFACTURARE mentionata in cerere, ar trebui
|
||||
inspectat acest tabel server-side (in afara sursei VFP disponibile aici) plus alocarea de noi
|
||||
`nid_cw` pentru butoanele noi de contracte, fara sa coincida cu cele deja folosite de COMENZI/
|
||||
lista de preturi/avize pe aceeasi pagina.
|
||||
|
||||
Exemplu concret COMENZI: butonul `Page2.Cw3` (facturare din comenzi) foloseste automat cheia
|
||||
`<cheie_pagina>+'03'` (Cw3 => `nid_cw=3`); pentru un buton nou de contracte pe aceeasi pagina ar
|
||||
trebui un `nid_cw` neutilizat (ex. 11+, dat fiind ca Page2 are deja Cw1..Cw9 conform
|
||||
`Clase\ofundal_facturare.vc2:882-926`).
|
||||
|
||||
### 4. Facturarea pe baza de comanda / pe baza de contract
|
||||
|
||||
**Comanda -> factura**: `Procedure facturare_comenzi` (`COMUN\programe\oproceduri_facturare.prg:
|
||||
139-141`) => `factureaza(3)`. Cautarea comenzii disponibile pentru facturare:
|
||||
`Function caut_comanda_gestiune` (`COMUN\programe\oproceduri_facturare.prg:1961-1983`), citeste
|
||||
din view-ul `vcomenzi` (`... FROM ] + gcS + [.vcomenzi`), filtru
|
||||
`facturat = 0 and interna = 3 ... ` (linia 1977).
|
||||
|
||||
**"Pe baza de contract" EXISTA DEJA**, mai complet decat comenzile pe alocuri:
|
||||
- `Procedure facturare_contracte(tcTip)` (`COMUN\programe\oproceduri_facturare.prg:119-136`) -
|
||||
primeste tipul de document ("FACTURA LEI"/"INVOICE"/"FACTURA VALUTA") si apeleaza
|
||||
`factureaza(2)`/`factureaza(6)`/`factureaza(52)`.
|
||||
- Buton pe pagina fundal: `Page2.Cw2.do_actiune` (`Clase\ofundal_facturare.vc2:886-897`) - meniu
|
||||
`xmenu` cu cele 3 optiuni, cheama `facturare_contracte`.
|
||||
- **Cautare contract**: `Function caut_contract_facturare(tnIdPart, tcSirTipFacturare)`
|
||||
(`COMUN\programe\oproceduri_facturare.prg:1986-2021`) - citeste din view-ul `fact_vcontracte`
|
||||
(`select id_ctr, contract, numar, data, denumire, scadenta_incasare, opt_facturare,
|
||||
text_standard, afisare_scadenta FROM fact_vcontracte`, linia 2001), filtrat pe
|
||||
`opt_facturare in (...)` si `id_part`.
|
||||
- **Alegerea contractului la factura**: `frm_date_factura.do_cauta_contract`
|
||||
(`COMUN\clase\ofacturare.vc2:9067-9115`) si `frm_date_aviz.do_cauta_contract`
|
||||
(`COMUN\clase\ofacturare.vc2:7049-7051`) apeleaza `caut_contract_facturare`.
|
||||
- **Editorul de articole pe factura** are un tab/grid dedicat contractelor:
|
||||
`frm_facturare_articole` cu controale `grd_contracte`, `cb_contracte` (combobox cu ratele /
|
||||
contractele), populate din cursorul `crscontracte` (`COMUN\clase\ofacturare.vc2:15069-15107` si
|
||||
in jur). Optiunea `opt_facturare` din `fact_vcontracte`/`crsfactura` marcheaza randurile "din
|
||||
contract" (`COMUN\programe\oproceduri_facturare.prg:176-177`, `Inlist(opt_facturare,1,2)` in alt
|
||||
context legat de seturi).
|
||||
- **Aviz pe baza de contract**: exista si un tip de aviz "26 - catre clienti din contract"
|
||||
(`COMUN\programe\oproceduri_facturare.prg:207`, enumerat si in `caut_avize`,
|
||||
`COMUN\programe\oproceduri_facturare.prg:2045`), apelat din `emitere_aviz_clienti(tnTip=3)`.
|
||||
|
||||
Concluzie: **motorul de facturare din contract e deja complet functional** in ROAFACTURARE (citire
|
||||
din schema ROACONTRACTE prin view-uri Oracle `vcontracte`/`fact_vcontracte`/`tipuri_contracte`).
|
||||
Ce lipseste conform cererii e (a) o **pagina de editare CRUD a contractelor** in ROAFACTURARE
|
||||
(azi doar in exe-ul separat ROACONTRACTE) si (b) **rapoarte de contracte** in ROAFACTURARE, plus
|
||||
(c) unificarea drepturilor. Nu a fost gasit niciun raport de contracte in
|
||||
`ROAFACTURARE\Rapoarte\` (glob `*contract*` nu a dat `.frx` in Rapoarte, doar meniu/iconite).
|
||||
|
||||
---
|
||||
|
||||
## SUBIECT B - Politici de preturi (todo #11)
|
||||
|
||||
### 5. Unde sunt azi definite/editate
|
||||
|
||||
Produs separat, mic, dedicat: `D:\ROA\ROAPRETURI` (`.pjx` propriu, `roapreturi.exe`). Structura:
|
||||
`Programe\onom_preturi.prg`, `Programe\update_preturi.prg`, `Programe\update_nomenclator.prg`,
|
||||
`Clase\opreturi.vcx`, `Clase\onom_preturi.vcx`, `Clase\ofundal_preturi.vcx`,
|
||||
`Clase\ofundal_roapreturi.vcx`, `Ferestre\fundal.scx`. (Continutul acestor clase nu a fost convertit
|
||||
in text - ROAPRETURI nu are un cache text propriu generat in aceasta sesiune; doar structura de
|
||||
fisiere a fost inspectata.)
|
||||
|
||||
Interfata de editare pare sa fie un produs desktop de sine statator, distinct de ROAFACTURARE si de
|
||||
ROACONT, focalizat strict pe politici/liste de preturi si pe actualizarea nomenclatorului
|
||||
(`update_nomenclator.prg` sugereaza ca ROAPRETURI scrie si in nomenclatorul comun de articole).
|
||||
|
||||
### 6. Tabele/view-uri implicate (identificate din ROAFACTURARE)
|
||||
|
||||
Din codul ROAFACTURARE care CITESTE politici de preturi (nu editeaza), gasite:
|
||||
- `vcrm_politici_preturi` - view folosit in cautare dupa drepturi utilizator
|
||||
(`COMUN\clase\baza.vc2:10087`, `:10157`, `:10502-10512`). Interogare efectiva:
|
||||
`select nume_lista_preturi, id_pol from crm_vpolpretcurutil` (`COMUN\clase\baza.vc2:10504`) -
|
||||
deci exista si view-ul `crm_vpolpretcurutil` ("politica de pret curenta pentru utilizator"),
|
||||
cheie `id_pol`.
|
||||
- `vvanzari_detalii` - contine coloana `nume_lista_preturi` folosita in rapoarte de marfa
|
||||
(`COMUN\clase\configurare.vc2:3915-3972`, `frm_raport_marfa`).
|
||||
- Meniu dedicat facturarii pe lista de preturi: `Meniuri\politica.mnx`/`.mn2`/`.MPR` in
|
||||
ROAFACTURARE; procedura `facturare_lista_de_preturi` = `Do politica.mpr`
|
||||
(`COMUN\programe\oproceduri_facturare.prg:113-116`), buton `Page2.Cw1.do_actiune`
|
||||
(`Clase\ofundal_facturare.vc2:882-884`).
|
||||
- Prefixul `crm_`/`CRM` in numele tabelelor/view-urilor (`vcrm_politici_preturi`,
|
||||
`crm_vpolpretcurutil`) sugereaza schema/modul Oracle numit "CRM", separat de schema principala
|
||||
de facturare (`gcS`). IPOTEZA: politicile de pret sunt un modul Oracle transversal (folosit si
|
||||
de ROAGEST, ROAPRETURI, ROACONTRACTE), nu proprietatea exclusiva a unui singur produs VFP.
|
||||
- Comenzile (ROACOMENZI/`ocomenzi.vcx`) au propriul mecanism de asociere pret-din-comanda:
|
||||
globale `gnIdPoliticaPret`, `gnId_lista_preturi_PV` (`Programe\roafacturare.prg:467,469`),
|
||||
folosite si in `frm_optiuni_comenzi` (`COMUN\clase\ocomenzi.vc2:6488-6686`, variabila
|
||||
`gnID_LISTA_PRETURI_PV` = politica de pret "de productie" folosita la generarea automata a
|
||||
comenzilor). Cursorul de articole al comenzii are coloanele `id_pol`, `nume_lista_preturi`,
|
||||
`pret`, `pret_cu_tva`, `ptva` direct in el (`COMUN\clase\ocomenzi.vc2:1227-1229`, cursor creat
|
||||
din view-ul `vcomenzi_elemente`).
|
||||
|
||||
Nu a fost gasita nicio schema DBF/DDL explicita pentru "politici de preturi" / "liste de preturi"
|
||||
in sursa VFP (tabelele reale sunt Oracle, definite server-side; VFP le vede doar prin view-uri
|
||||
enumerate mai sus). N-a fost identificat un tabel separat de "note contabile asociate politicii de
|
||||
pret" in codul cercetat - contul contabil de vanzare pare sa vina din nomenclatorul de articole
|
||||
(`nom_articole.cont`, vezi punctul 10), nu dintr-o tabela separata legata de politica.
|
||||
|
||||
### 7. Ce foloseste ROAFACTURARE azi din aceste date
|
||||
|
||||
- **Facturare pe lista de preturi** (`Do politica.mpr`) - flux complet de vanzare pe baza unei
|
||||
politici de pret selectate (analog cu vanzarea din stoc/comenzi/contract), tip document
|
||||
distinct in motorul central `factureaza()`.
|
||||
- **Cautare/afisare politica dupa drepturi utilizator** (`COMUN\clase\baza.vc2:10502-10512`) -
|
||||
ROAFACTURARE citeste `crm_vpolpretcurutil` pentru a limita politicile vizibile la cele pe care
|
||||
utilizatorul are drept (alt strat de drepturi, distinct de `acces_meniu.prg` - specific pe
|
||||
politici de pret, posibil gestionat tot server-side prin pachetul `PACK_DREPTURI`).
|
||||
- **Rapoarte de vanzari pe lista de preturi** (`frm_raport_marfa`,
|
||||
`COMUN\clase\configurare.vc2:3915-3972`) - grupare/însumare pe `nume_lista_preturi`.
|
||||
- Nu editeaza politici/liste - doar le CITESTE si le foloseste ca sursa de pret la facturare/
|
||||
raportare. Editarea (adaugare politica, adaugare articole in politica, preturi) ramane in
|
||||
ROAPRETURI.
|
||||
|
||||
Concluzie pentru migrare: ce ar trebui **mutat efectiv in ROAFACTURARE** (conform cererii - "se
|
||||
folosesc numai in programul ROAFACTURARE") e interfata de editare (`opreturi.vcx`/
|
||||
`onom_preturi.vcx` din ROAPRETURI), nu structura de date (Oracle, deja partajata/citita corect).
|
||||
Rapoartele si drepturile pe liste de preturi trebuie de asemenea aduse ca pagina/panou in
|
||||
ROAFACTURARE, dupa acelasi sablon COMENZI descris la punctul 2.
|
||||
|
||||
---
|
||||
|
||||
## SUBIECT C - Nomenclatorul de articole ca lista de preturi virtuala (todo #12)
|
||||
|
||||
### 8. Structura tabelei de nomenclator
|
||||
|
||||
Tabela Oracle **`catalog_articole`**, expusa prin view-ul **`vnom_articole`** (si `vnom_articole2`
|
||||
pentru un al doilea tip - vezi `nom_articole2_nou`, `COMUN\programe\onomenclatoare.prg:1376-1396`,
|
||||
tabela `catalog_articole2`).
|
||||
|
||||
Coloane identificate din interogari/scatter (nu e o lista exhaustiva - vin din SELECT-uri
|
||||
punctuale, nu din DDL):
|
||||
`id_articol, denumire, codmat, codmatf (cod furnizor), codbare, um, grupa, subgrupa, id_grupa,
|
||||
id_subgrupa, dnf, cont (cont contabil, 3-4 caractere), acont, inactiv, sters, in_stoc, in_crm,
|
||||
tip (ex. 1 = manopera pt. ROAACNPRO), id_part/partener (pt. articole legate de furnizor/client)`.
|
||||
Surse: `COMUN\clase\ocriterii.vc2:1531` (`select denumire, codmat, um, grupa, subgrupa, id_grupa,
|
||||
id_subgrupa, dnf, cont, acont, inactiv, id_articol from vnom_articole`),
|
||||
`COMUN\programe\onomenclatoare.prg:1345-1353` (`in_crm`, `in_stoc`, `tip`),
|
||||
`COMUN\clase\ointroduceri.vc2:9631` (`in_stoc, cont`).
|
||||
|
||||
**Nicio coloana de pret** nu a fost gasita direct pe `nom_articole`/`catalog_articole` (grep
|
||||
`pret_v|pretv|pret_lista` in `onomenclatoare.prg` = fara rezultate; scatter-ul din
|
||||
`nom_articole_nou` nu populeaza niciun camp de pret). Confirma punctul 11 mai jos.
|
||||
|
||||
**Formular de editare**: `frm_catalog_articole` (grid/cautare) si `frm_catalog_articole_nou`
|
||||
(fisa), ambele in `COMUN\clase\onom_articole.vc2` (`:531-599`, `:1655-1733`), salvare prin
|
||||
`cus_odata_catalog_articole.salvare` si `Adauga_Modifica_Inregistrare('catalog_articole', ...)`
|
||||
(`COMUN\programe\onomenclatoare.prg:1367,1443`). Deschidere din meniu:
|
||||
`Procedure viz_catalog_articole` (`COMUN\programe\oproceduri_articole.prg:62-122`).
|
||||
|
||||
**Important pentru subiectul B/C**: `nom_articole_nou` (`COMUN\programe\onomenclatoare.prg:
|
||||
1343-1353`) marcheaza acelasi articol cu `in_crm = 1` cand programul curent e ROAPRETURI sau
|
||||
ROACONTRACTE, respectiv `in_stoc = 1` in rest (inclusiv ROAFACTURARE) - **nomenclatorul de
|
||||
articole e deja UNIC/PARTAJAT** intre ROAFACTURARE, ROAPRETURI, ROACONTRACTE si ROAACNPRO (aceeasi
|
||||
tabela `catalog_articole`), flagurile `in_stoc`/`in_crm`/`tip` fiind doar clasificari de
|
||||
utilizare, nu tabele separate. Asta simplifica mult todo #12: nomenclatorul nu trebuie replicat,
|
||||
doar completat cu campuri de pret/tva/cont-vanzare si folosit direct ca sursa de pret la
|
||||
facturare.
|
||||
|
||||
### 9. Mecanismul actual de "lista de articole din stoc ca lista de preturi virtuala"
|
||||
|
||||
Confirmat: e mecanismul de **"vanzare din stoc/gestiune"**, un tip de facturare paralel cu
|
||||
"lista de preturi"/"contract"/"comanda", identificat prin variabila globala `gnTipGest`:
|
||||
- `Procedure vanzare_materii_prime` -> `gnTipGest = 2`, `Do vanzare1.mpr`
|
||||
- `Procedure vanzare_produse` -> `gnTipGest = 4`, `Do vanzare2.mpr`
|
||||
- `Procedure vanzare_marfa_pret_achi` -> `gnTipGest = 5`, `Do vanzare3.mpr` (marfa la pret de
|
||||
achizitie)
|
||||
- `Procedure vanzare_marfa_pret_vanz` -> `gnTipGest = 6`, `Do vanzare4.mpr` (marfa la pret de
|
||||
**vanzare**)
|
||||
- `Procedure vanzare_marfa_pret_achi_vanz` -> `gnTipGest = 7`, `Do vanzare5.mpr`
|
||||
(toate in `COMUN\programe\oproceduri_facturare.prg:1505-1534`; butoane `Page2.Cw5..Cw9` in
|
||||
`Clase\ofundal_facturare.vc2:908-926`).
|
||||
|
||||
Gestiunile disponibile per tip se filtreaza prin `Procedure selecteaza_gestiuni`
|
||||
(`COMUN\programe\oproceduri_facturare.prg:1536-1565`), pe view-ul `vnom_GESTIUNI` filtrat
|
||||
`nr_pag = ?gnTipGest`, plus un al doilea nivel de drept pe gestiuni (view-urile
|
||||
`vgest_coresp_grupe_gestiuni` / `vgest_coresp_util_grupe`, linia 1552-1555) - deci "lista de
|
||||
preturi virtuala" = stocul unei gestiuni, cu control de acces pe gestiune (nu pe politica de
|
||||
pret).
|
||||
|
||||
Motorul de scriere: `Function oscrie_vanzare_din_stoc` in `COMUN\programe\ofacturare_stoc.prg:
|
||||
104-...`, apelat din `initializeaza_vanzare_din_stoc` (`ofacturare_stoc.prg:31-99`). Comentariu
|
||||
explicit in cod: *"in vanzari_detalii scriu pretul cu tva daca am marfa la pret de vanzare, daca
|
||||
nu scriu pretul de vanzare fara tva"* (`ofacturare_stoc.prg:107`), cu
|
||||
`lnPretCuTva = Iif(INLIST(gnTipGest,6,7), 1, 0)` (linia 111) - deci flagul "pret cu TVA" e
|
||||
determinat de TIPUL de vanzare din stoc ales (6/7 = la pret de vanzare), nu citit dintr-o coloana
|
||||
a nomenclatorului.
|
||||
|
||||
Cursorul-cheie `crsvanztemp` (`ofacturare_stoc.prg:137-139`) are coloanele:
|
||||
`id_articol, Pret, proc_tvav, id_jtva_coloana, cantitate, discount_unitar, id_gestiune, Cont
|
||||
(c4), pret_cu_tva, serie, id_valuta, codmat, Curs, multiplicator, pret_achizitie, pretd,
|
||||
id_valuta_d, id_rul_aux, taxcode, lot`.
|
||||
|
||||
### 10. Lantul de cod: pret, valuta, %TVA, pret_cu_tva, cont vanzare - la facturare "din stoc"
|
||||
|
||||
Din structura `crsvanztemp` (punctul 9) rezulta explicit lantul folosit azi cand se factureaza
|
||||
"virtual" din stoc (echivalentul cerut pentru nomenclator-ca-lista-de-preturi):
|
||||
- **Pret**: coloana `Pret` in `crsvanztemp`, luata din inregistrarea de gestiune/stoc (miscarea de
|
||||
intrare), nu din nomenclator - `pret_achizitie` separat pentru pretul de achizitie.
|
||||
- **Valuta**: `id_valuta`, `Curs`, plus varianta in alta valuta `pretd`/`id_valuta_d` (pret dublu,
|
||||
pentru afisare in a doua valuta).
|
||||
- **%TVA**: `proc_tvav` + `id_jtva_coloana` (coloana de defalcare TVA in jurnal) + `taxcode` (cod
|
||||
fiscal pt. integrari, ex. eFactura).
|
||||
- **Flag pret_cu_tva**: `pret_cu_tva` in cursor, calculat din `gnTipGest` (`ofacturare_stoc.prg:
|
||||
111`), NU citit dintr-o coloana persistenta a articolului.
|
||||
- **Cont vanzare (echivalent 4111=7xx)**: coloana `Cont c(4)` in `crsvanztemp` - cont contabil pe
|
||||
4 caractere (ex. "707x"/"701x"), scris explicit in cursor la nivel de linie de vanzare. Sursa lui
|
||||
cea mai probabila (nu confirmata cu linie exacta de SELECT in aceasta cercetare, cursorul e
|
||||
populat mai jos in fisier, dincolo de zona citita) e coloana `nom_articole.cont` (confirmata ca
|
||||
existenta la punctul 8) sau contul gestiunii (`nom_gestiuni`) - **IPOTEZA**: trebuie verificat
|
||||
punctual restul lui `oscrie_vanzare_din_stoc` (fisierul continua dupa linia 140, necitit
|
||||
integral in aceasta trecere) pentru sursa exacta linie-cu-linie a lui `Cont`.
|
||||
|
||||
Pentru comparatie, la facturarea pe **lista de preturi/politica** (nu pe stoc), pretul/valuta/TVA
|
||||
vin din politica de pret (view `crm_vpolpretcurutil`/`vcrm_politici_preturi`, punctul 6), deci
|
||||
lantul e diferit dupa tipul de facturare ales (`gnTipGest` vs. `id_pol`).
|
||||
|
||||
### 11. Coloane de pret existente/partial folosite in nomenclator
|
||||
|
||||
**Nu exista azi nicio coloana de pret pe `nom_articole`/`catalog_articole`** (cautare explicita
|
||||
fara rezultate). Exista insa deja doua campuri reutilizabile direct pentru scenariul din cerere:
|
||||
- `cont` (cont contabil de vanzare/achizitie, deja pe articol - vezi punctul 8) - poate fi folosit
|
||||
ca "nota contabila" fara tabel separat.
|
||||
- Flagurile `in_stoc` / `in_crm` - clasifica deja fiecare articol dupa modul de utilizare (stoc
|
||||
vs. politica de pret / CRM), un precedent direct pentru un viitor flag suplimentar de tipul
|
||||
"articol cu pret propriu in nomenclator" daca se implementeaza todo #12.
|
||||
|
||||
Nu au fost gasite coloane de tipul `pret`, `pret_vanzare`, `valuta_pret`, `ptva` direct pe
|
||||
nomenclator - toate preturile de vanzare vin azi fie din politici de pret (Oracle, schema CRM),
|
||||
fie din miscarile de gestiune/stoc (nu din articolul insusi). Implementarea todo #12
|
||||
("nomenclatorul direct ca lista de preturi, fara politica + nota contabila asociate") ar necesita
|
||||
adaugarea a cel putin: pret (+valuta), procent TVA, flag pret_cu_tva pe `catalog_articole`/
|
||||
`nom_articole` - camp nou, nu o coloana ascunsa deja existenta.
|
||||
|
||||
---
|
||||
|
||||
## Rezumat surse cheie (fisier:linie)
|
||||
|
||||
- Sablon COMENZI: `Programe\roafacturare.prg:180-181,239-247`; `Clase\ofundal_facturare.vc2:
|
||||
760-926`; `COMUN\clase\ocomenzi.vc2`.
|
||||
- Drepturi: `COMUN\programe\acces_meniu.prg` (tot fisierul, 306 linii).
|
||||
- Facturare contract: `COMUN\programe\oproceduri_facturare.prg:119-136,1986-2021`;
|
||||
`COMUN\clase\ofacturare.vc2:9067-9115,15069-15107`.
|
||||
- Politici de pret: `COMUN\clase\baza.vc2:10087,10157,10453-10512`;
|
||||
`COMUN\programe\oproceduri_facturare.prg:113-116`.
|
||||
- Vanzare din stoc (lista virtuala): `COMUN\programe\oproceduri_facturare.prg:1505-1534`;
|
||||
`COMUN\programe\ofacturare_stoc.prg:31-140`.
|
||||
- Nomenclator articole: `COMUN\programe\onomenclatoare.prg:1302-1373`;
|
||||
`COMUN\clase\onom_articole.vc2:531-599,1655-1733`.
|
||||
243
docs/cercetare/rec_pret_lazy.md
Normal file
243
docs/cercetare/rec_pret_lazy.md
Normal file
@@ -0,0 +1,243 @@
|
||||
# Cercetare: lant pret (#12) + editare politici pret (#11/#10) + lazy loading
|
||||
|
||||
## REZUMAT (max 30 linii, focus A2 + C2)
|
||||
|
||||
**A2 — NU exista niciun fallback la nomenclator azi.** Confirmat din chiar view-ul care alimenteaza
|
||||
majoritatea ramurilor lui `cursor_preturi` (`fact_vpreturi_utilizator`, definit in
|
||||
`D:\ROA\DATABASE\SCRIPTURI\2009\4\ff_2009_04_21_03_FACTURARE.sql:11-50`): clauza
|
||||
`and d.id_pol is not null` (linia 49) e o conditie **obligatorie** pe `crm_politici_pret_art d` —
|
||||
un articol nu apare deloc in lista de vanzare daca nu are un rand in `CRM_POLITICI_PRET_ART` pentru
|
||||
politica activa a utilizatorului. `NOM_ARTICOLE` e joinat doar pentru `um/denumire/codmat/codbare/
|
||||
in_stoc` (descriptive), niciodata pentru `pret/valuta/proc_tvav`. In `PACK_FACTURARE.cursor_preturi`
|
||||
insusi (`ff_2026_03_23_04_COMUN_PACK_FACTURARE.sql:2121-2627`) acelasi tipar se repeta in toate
|
||||
ramurile (2, 45, 1/2, 5/6/10/52, 7, else/aviz): driver-ul e mereu `CRM_POLITICI_PRET_ART`/politica,
|
||||
niciun `NVL`/`COALESCE` catre `catalog_articole`/`nom_articole` pentru pret. Nu exista deloc referinte
|
||||
la `catalog_articole` in tot pachetul `PACK_FACTURARE` (grep pe fisierul de 16948 linii: 0 hit-uri).
|
||||
**Concluzie: fallback-ul de #12 chiar trebuie construit de la zero.** Locul ieftin de inserat: in
|
||||
`fact_vpreturi_utilizator` si/sau direct in `cursor_preturi`, un `UNION ALL`/`LEFT JOIN` suplimentar
|
||||
care aduce articolele din `catalog_articole` **fara** rand in `CRM_POLITICI_PRET_ART` pentru
|
||||
politica curenta, cu pret/valuta/tva **NULL sau valori implicite din optiuni firma** — deoarece
|
||||
`catalog_articole` nu are nicio coloana de pret/valuta/tva (confirmat de echipa, si verificat: n-am
|
||||
gasit `PRET`/`ID_VALUTA`/`PROC_TVAV` pe `NOM_ARTICOLE`/`CATALOG_ARTICOLE` in DDL). Fallback-ul e deci
|
||||
"articolul apare, dar utilizatorul trebuie sa completeze pretul manual" — nu un pret implicit real.
|
||||
|
||||
**C2 — Exista deja un sablon de lazy loading, complet si reutilizabil**, in
|
||||
`COMUN\clase\_ct_base.vc2:278-281` (`do_activeaza_container` → `This.actualizeaza_cursoare()`), legat
|
||||
la `PageX.Activate` (`Clase\ofundal_facturare.vc2:1006-1008` pentru comenzi). Implementarea reala e
|
||||
in `COMUN\clase\ocomenzi.vc2:1088-1104`: cursorul se creeaza o singura data (guard
|
||||
`!Used('crscomenzi') Or gcS!=This.cSchema Or ...schimbare sectie`), cu un `WHERE` imposibil
|
||||
(`id_comanda = -9999999`) — deci structura se creeaza dar nu se aduc date. Datele reale vin abia la
|
||||
`do_cauta()` (`ocomenzi.vc2:1484-1510`), care trimite un `WHERE` filtrat prin `gencursor`/
|
||||
`ca_baza1.afisare()` — deci cautarea e deja server-side (Oracle), nu filtrare locala. **Acesta e
|
||||
sablonul de refolosit pentru #11/#10, exact cum indica deja `plan_10_integrare_contracte.md` S3.**
|
||||
Atentie: baza `_ct_base.actualizeaza_cursoare` (linia 231-232) e un stub gol — clasa container noua
|
||||
(`ct_preturi`/`ct_contracte`) trebuie sa suprascrie explicit `actualizeaza_cursoare`, dupa modelul
|
||||
`ocomenzi`, nu doar sa mosteneasca `_ct_base`. `ct_contracte` din ROACONTRACTE **nu** face asta azi
|
||||
(vezi C4) — deci nu e lazy, desi mosteneste acelasi `_ct_base`.
|
||||
|
||||
---
|
||||
|
||||
## A. Lantul de determinare a pretului
|
||||
|
||||
### A1. `cursor_preturi` — surse pe ramura (spec `:335-343`, body `:2121-2627`,
|
||||
`ff_2026_03_23_04_COMUN_PACK_FACTURARE.sql`)
|
||||
|
||||
Toate ramurile pornesc din `pack_facturare.initializeaza_facturare` + `completare_politica_stoc` +
|
||||
`verifica_cursuri_valute` (:2132-2136), apoi `CASE V_TIP`:
|
||||
|
||||
- **V_TIP=45 (restaurant)**, `:2143-2247`: subselect A = politica activa a utilizatorului
|
||||
(`utilizatori_rol_intern` → `politici_grupuri` → `crm_politici_preturi` → `crm_note_vanzari` →
|
||||
`note_contabile`, filtrat pe `datai/datas` fata de luna curenta si `id_sucursala`), apoi
|
||||
`LEFT JOIN CRM_POLITICI_PRET_ART B ON A.ID_POL=B.ID_POL`, `LEFT JOIN NOM_ARTICOLE C`. **Pret**:
|
||||
`B.PRET`/`B.DISCOUNT_UNITAR` convertite prin curs (`F.CURS`) daca valuta politicii ≠ valuta
|
||||
nationala. **Valuta**: `B.ID_VALUTA` → `NOM_VALUTE G`. **TVA%**: `B.PROC_TVAV` (direct din
|
||||
`CRM_POLITICI_PRET_ART`, nicio referinta la `ID_JTVA_COLOANA` in acest cursor). **Flag
|
||||
pret_cu_tva**: `A.PRETURI_CU_TVA` (de pe `crm_politici_preturi`, nu per-articol). **Cont**:
|
||||
`'371' AS CONT` — **hardcodat**, nu vine din nicio tabela.
|
||||
- **V_TIP IN (1,2) (factura lei)**, `:2248-2372`: acelasi tipar (A/B/C), plus `LEFT JOIN` pe `STOC`
|
||||
pentru cantitate gestionabila (E) si pe `CURS`/`NOM_VALUTE` (F/G). Nicio coloana `CONT` in select.
|
||||
- **V_TIP IN (5,6,10,52) (valuta)** si **V_TIP=7 (credit note)**, `:2374-2524`: sursa e view-ul
|
||||
`FACT_VPRETURI_UTILIZATOR A` (nu mai construieste politica inline), plus `STOC` (C) si `CURS`/
|
||||
`NOM_VALUTE` (D/E). Filtru `A.ID_VALUTA=V_ID_VALUTA AND A.IN_VALUTA=1` (sau `A.ID_POL=
|
||||
pack_facturare.nid_politica_stoc` la tip 5/6/10/52). Nicio coloana `CONT`.
|
||||
- **ELSE (aviz)**, `:2526-2624`: tot din `FACT_VPRETURI_UTILIZATOR A`, fara filtru pe valuta. Nicio
|
||||
coloana `CONT`.
|
||||
|
||||
**Definitia `FACT_VPRETURI_UTILIZATOR`** (cea mai recenta gasita, `D:\ROA\DATABASE\SCRIPTURI\2009\4\
|
||||
ff_2009_04_21_03_FACTURARE.sql:11-50`; nu a mai fost modificata in `SCRIPTURI_CLAR`, deci pare
|
||||
stabila de atunci):
|
||||
```
|
||||
utilizatori_rol_intern a
|
||||
left join politici_grupuri b on a.id_grup=b.id_grup
|
||||
left join crm_politici_preturi c on b.id_politica=c.id_pol
|
||||
left join crm_politici_pret_art d on b.id_politica=d.id_pol
|
||||
left join nom_articole e on d.id_articol=e.id_articol -- doar um/denumire/codmat/codbare/in_stoc
|
||||
left join crm_note_vanzari f on c.id_nota=f.id_nota
|
||||
left join note_contabile g on f.id_set=g.id_set
|
||||
where ... and d.id_pol is not null -- <- gate-ul, vezi A2
|
||||
```
|
||||
Coloane expuse: `id_pol, preturi_cu_tva, nume_lista_preturi, id_articol, pret, discount_unitar,
|
||||
proc_tvav, um, denumire, codmat, codbare, gestionabil, id_valuta, in_valuta, nota_discount`. **Nicio
|
||||
coloana `cont`.**
|
||||
|
||||
**`ID_JTVA_COLOANA`**: nu apare deloc in `cursor_preturi`. Grep pe tot pachetul arata ca se
|
||||
foloseste doar mai tarziu, la scriere/postare (`scrie_in_vanzari`, `descarca_gestiune`,
|
||||
`contabilizeaza_tva`, in jur de liniile 3700-4130, 4660-5400, 12250-13700 din pachet) — deci cota de
|
||||
TVA "oficiala" (coloana `JTVA_COLOANE`) se rezolva separat de `PROC_TVAV`-ul afisat la selectarea
|
||||
pretului, probabil prin cautare dupa procent in `citeste_id_jtva_coloana`-tip logica (nu am urmarit
|
||||
in detaliu — in afara bugetului A, marcat ca zona neexplorata).
|
||||
|
||||
**Cont de vanzare, de fapt**: parametrul `V_CONT` din `PACK_FACTURARE.adauga_articol_factura`
|
||||
(`:4972-4998`) e trimis de VFP la adaugarea liniei (valoare `'XXXX'` = "fara override" -> `V_CONT2`
|
||||
ramane NULL). Nu am gasit el sa fie citit din `CRM_POLITICI_PRET_ART`/`NOM_ARTICOLE` in acest flux;
|
||||
in schimb exista deja `initializeaza_date_gestiune(V_ID_GESTIUNE, V_ID_TIPGEST, V_CONT, V_ACONT)`
|
||||
(spec `:311-314`) care leaga contul de **gestiune**, nu de articol/politica. Separat, pachetul
|
||||
`PACK_PRETURI` (editorul din ROAPRETURI) scrie `NOM_ARTICOLE.CONT` direct la
|
||||
`adauga_articol`/`modifica_articol` (`D:\ROA\DATABASE\SCRIPTURI\2008\7\ff_2008_07_31_01_PRETURI.sql
|
||||
:107-130, 300-330, 386-424`) — deci **contul de vanzare al articolului exista deja pe
|
||||
`NOM_ARTICOLE.CONT`** (= `catalog_articole.cont` din nomenclatura curenta), independent de orice
|
||||
lista de preturi. Asta inseamna ca, pentru #12, contul NU are nevoie de fallback nou — poate fi citit
|
||||
direct din nomenclator, exact ce cere planul (ramane de confirmat unde/cum VFP populeaza azi `V_CONT`
|
||||
la trimiterea catre `adauga_articol_factura`, cautare separata, in afara bugetului acestei runde).
|
||||
|
||||
### A2. Fallback existent — **NU exista**. Detaliat mai sus (rezumat) si in A1
|
||||
(`d.id_pol is not null`, zero hit-uri `catalog_articole` in `PACK_FACTURARE`). Singurul loc unde
|
||||
`NOM_ARTICOLE`/nomenclatorul intra in calculul de pret e ca sursa a campurilor descriptive
|
||||
(um/denumire/codmat/codbare/in_stoc), niciodata pentru pret/valuta/tva/cont.
|
||||
|
||||
### A3. `citeste_setari_pol_pret` (`:2025-2065`)
|
||||
Nu seteaza `pret_cu_tva`/TVA. Citeste doar **care politica e implicita** pentru un `V_ID_UTIL`, in
|
||||
functie de `V_TIP`: `OPTIUNI.VARNAME` = `'ID_POL_PRET_TR'` (tip 23/30/41), `'ID_POL_PRET_STOC'`
|
||||
(tip 1), `'IDPOLPRETFACTK'` (tip 48/49), fiecare cu `LEFT JOIN CRM_VPOLPRETCURUTIL B ON ... AND
|
||||
B.ID_UTIL=V_ID_UTIL`. Deci `pret_cu_tva`/TVA vin exclusiv din randurile `crm_politici_preturi`/
|
||||
`crm_politici_pret_art` selectate ulterior de `cursor_preturi`, nu din aceasta procedura.
|
||||
|
||||
### A4. Tabelele politicii de pret (coloane confirmate din uz + din
|
||||
`ff_2008_07_31_01_PRETURI.sql:10-95`, ADD/FK, si `create or replace view vcrm_politici_preturi`):
|
||||
|
||||
- **`CRM_POLITICI_PRETURI`**: `ID_POL, NUME_LISTA_PRETURI, DATAI, DATAS, ID_VALUTA, ID_UTIL,
|
||||
DATAORA, NOTA_ONLINE, ID_NOTA, PRETURI_CU_TVA, ID_SUCURSALA, STERS`. FK: `ID_VALUTA->NOM_VALUTE`,
|
||||
`ID_NOTA->CRM_NOTE_VANZARI`, `ID_SUCURSALA->CONTAFIN_ORACLE.NOM_FIRME`.
|
||||
- **`CRM_POLITICI_PRET_ART`**: `ID_POL, ID_ARTICOL, ID_VALUTA, PRET, PRETFTVA, PRETCTVA, PROC_TVAV,
|
||||
DISCOUNT_UNITAR, STERS` (din `modificare_politica_stoc`, `:2105-2119`, si din selectii). **Nicio
|
||||
coloana CONT.**
|
||||
- Nu am gasit `CREATE TABLE` originar pentru aceste doua tabele in `SCRIPTURI_CLAR` (probabil
|
||||
create inainte de 2006, in afara arhivei "clare"); coloanele de mai sus sunt derivate din uz activ
|
||||
in cod, nu dintr-un DDL complet — de tratat ca aproape sigure, nu 100% exhaustive (pot exista
|
||||
coloane suplimentare needitate de codul cercetat).
|
||||
- View-uri asociate: `VCRM_POLITICI_PRETURI` (`ff_2008_07_31_01_PRETURI.sql:42-65`),
|
||||
`VPOLITICI_GRUPURI` (`:67-95`), `CRM_VPOLPRETCURUTIL` (folosit pretutindeni, definitia lui nu a
|
||||
fost cautata — in afara bugetului).
|
||||
|
||||
---
|
||||
|
||||
## B. Cine mai foloseste listele de preturi
|
||||
|
||||
- **ROAFACTURARE**: doar citeste. `crm_vpolpretcurutil` la `COMUN\clase\baza.vc2:10087,10157,
|
||||
10502-10512` (filtrare politici dupa dreptul utilizatorului); `facturare_lista_de_preturi` =
|
||||
`Do politica.mpr` (`COMUN\programe\oproceduri_facturare.prg:113-116`); rapoarte grupate pe
|
||||
`nume_lista_preturi` din `vvanzari_detalii` (`COMUN\clase\configurare.vc2:3915-3972`).
|
||||
(Sursa: `docs\plan_11_integrare_politici_preturi.md:15-22`, deja in `D:\ROA\ROAFACTURARE\docs\`.)
|
||||
- **ROAPRETURI** (`D:\ROA\ROAPRETURI`, exe separat `roapreturi.exe`): **editeaza** politicile —
|
||||
`Clase\opreturi.vcx`, `Clase\onom_preturi.vcx`, `Clase\ofundal_preturi.vcx`,
|
||||
`Programe\update_preturi.prg`, `Programe\update_nomenclator.prg`. Pachet Oracle `PACK_PRETURI`
|
||||
(`adauga_articol`/`modifica_articol`/`verifica_articol` — scriu si pe `NOM_ARTICOLE`, nu doar pe
|
||||
politica). **Nu are cache text (.vc2/.sc2) generat** — nu s-a putut inspecta clasa in detaliu (vezi
|
||||
C4); confirmat prin `Glob D:\ROA\ROAPRETURI\**\*.vc2` = 0 fisiere, doar `.vcx` binare.
|
||||
- **ROACONTRACTE** (`D:\ROA\ROACONTRACTE\Clase\ferestre_contracte.vc2`): citeste direct
|
||||
`vcrm_politici_preturi` / `vcrm_politici_pret_art` prin SQL Pass Through (`goExecutor.oExecute`),
|
||||
la cautarea/atasarea unui articol pe linie de contract
|
||||
(`Programe\oproceduri_roacontracte.prg:213-239`: `select id_pol,... from vcrm_politici_preturi`,
|
||||
apoi `select id_articol, id_pol_art, ... from ]+gcS+[.vcrm_politici_pret_art where id_pol=...`).
|
||||
Nu editeaza politica insasi, doar o consuma pentru a atasa articole+pret pe contract.
|
||||
- **ROAGEST**: 0 hit-uri directe pe `CRM_POLITICI_PRET*`/`cursor_preturi`/`PACK_PRETURI` in cod VFP
|
||||
(`.vc2`/`.prg`) — foloseste probabil acelasi `PACK_FACTURARE` server-side prin
|
||||
`Programe\ofactureaza.prg` (fisier gasit la cautarea `id_pol`, dar doar in comentarii vechi de
|
||||
schema cursor, nu apel activ identificat in bugetul alocat).
|
||||
- **ROAACNPRO**: la fel, 0 hit-uri directe; `id_pol` apare doar intr-un `Text To lcSchema`/
|
||||
`lcSelect` ascuns (`Omitted long matching line` — continut necunoscut, de investigat separat daca
|
||||
devine relevant).
|
||||
- **ROAIMOB**: 0 hit-uri (`.vc2`).
|
||||
- **COMUNROA**: 0 hit-uri (`.vc2`) — surprinzator pentru un modul "CRM transversal"; posibil ca
|
||||
accesul se face exclusiv server-side (proceduri Oracle), nu prin VFP direct in produsele
|
||||
verificate, sau ROAGEST/ROAACNPRO folosesc cod VFP montat din `COMUN` (acelasi `ocomenzi.vcx` etc.)
|
||||
fara sa atinga politica de pret local.
|
||||
- **Nomenclatorul comun** (`update_nomenclator.prg` in ROAPRETURI) scrie in tabela de articole
|
||||
folosita de toate produsele — punct de atentie deja notat in `plan_11...md:82-84` (S2, perimetru).
|
||||
|
||||
**Concluzie B**: politica de pret (structura de date) e deja un modul transversal minim
|
||||
(ROAFACTURARE citeste, ROAPRETURI editeaza, ROACONTRACTE citeste), consistent cu ce spune deja
|
||||
`plan_11...md`. Nu s-a gasit inca un al patrulea consumator activ in cod VFP (ROAGEST/ROAACNPRO
|
||||
probabil trec prin acelasi `PACK_FACTURARE` server-side, fara sa atinga tabelele direct din VFP).
|
||||
|
||||
---
|
||||
|
||||
## C. Incarcare lazy si cautare
|
||||
|
||||
### C1. Cum incarca azi paginile mari
|
||||
|
||||
- **`ct_comenzi`** (`COMUN\clase\ocomenzi.vc2`): montat ca `Page5.Ct_comenzi1`
|
||||
(`Clase\ofundal_facturare.vc2:604-606`). `PROCEDURE Page5.Activate` (`:1006-1008`) apeleaza
|
||||
`this.ct_comenzi1.do_activeaza_container()`. Aceasta e mostenita din `_ct_base.vc2:278-281`
|
||||
(`do_activeaza_container` → `bind keypress` + `This.actualizeaza_cursoare()`).
|
||||
`ocomenzi.actualizeaza_cursoare` (`:1088-1104`) **nu incarca date** — creeaza doar structura
|
||||
cursorului prin `creeaza_cursoare()` (`:1191-1245`), cu `lcFiltru = [id_comanda = -9999999]`
|
||||
(`:1216`) — deci 0 randuri reale la deschidere/activare de pagina.
|
||||
- **`onom_articole.vc2`**: editorul de articol individual (`PROCEDURE Init`, `:1704-1772`) e per-
|
||||
inregistrare (`toRec`), nu o grila; incarca doar cursoarele mici de grupe/subgrupe pentru combo-uri
|
||||
(`crs_grupe_art`, `crs_subgrupe_art`, `:1717-1729`). Nu s-a gasit/verificat in bugetul alocat cum
|
||||
se incarca lista/grila principala de articole a nomenclatorului (formularul-parinte, nu editorul) —
|
||||
**neacoperit**, marcat ca zona pentru o runda ulterioara daca devine relevanta pentru #12/#11.
|
||||
|
||||
### C2. Sablon de lazy loading existent — **DA**, detaliat in rezumat.
|
||||
Cheia: `_ct_base.do_activeaza_container` (hook legat de `Page.Activate`) → `actualizeaza_cursoare()`
|
||||
**suprascrisa** in clasa concreta cu un guard `!Used(cursor) Or <context s-a schimbat>` →
|
||||
`creeaza_cursoare()` cu `WHERE` fals → date reale doar la `do_cauta()`. `_ct_base` insusi ofera doar
|
||||
stub-uri goale (`:231-232` pentru `actualizeaza_cursoare`, la fel `do_adauga/do_cauta/...` la
|
||||
`:283-322`) — **fiecare container concret trebuie sa implementeze explicit lazy-load-ul**, nu vine
|
||||
gratis din mostenire.
|
||||
|
||||
### C3. Cautare server-side vs. filtrare locala
|
||||
- **Server-side (tiparul de urmat)**: `ocomenzi.do_cauta` (`:1484-1510`) construieste `lcFiltru`
|
||||
(`id_sectie = ...` + `filtru_pretty`) si il trimite prin `actualizeaza_grid1(lcFiltru)` →
|
||||
`pocomenzi.ca_baza1.cfiltru=lcFiltru` → `pocomenzi.ca_baza1.afisare()` (`:1109-1123`) — clasa
|
||||
`ca_baza1` (generata de `gencursor`) trimite `WHERE`-ul catre Oracle prin `goExecutor`, nu
|
||||
filtreaza un cursor deja incarcat integral.
|
||||
- **Local (contra-exemplu)**: `actualizeaza_grid1` in continuare (`:1119-1124`) face si operatii pur
|
||||
locale pe cursor deja adus (`SELECT * FROM crscomenzi WITH (Buffering=.T.) WHERE selectat=1 INTO
|
||||
CURSOR crstempcomenzi`) — dar asta e pentru pastrarea selectiei intre reincarcari, nu pentru
|
||||
cautare/filtrare initiala.
|
||||
|
||||
### C4. ROAPRETURI / ROACONTRACTE — incarca tot la deschidere?
|
||||
|
||||
- **ROAPRETURI**: **nu se poate stabili** — lipseste cache-ul text (`.vc2`/`.sc2`), doar `.vcx`
|
||||
binare (`Clase\opreturi.vcx`, `Clase\ofundal_preturi.vcx`); conform regulilor primite, nu am
|
||||
convertit nimic. Precondiitia e deja documentata ca blocanta in `plan_11...md:66-70` (S1: inrolare
|
||||
in fluxul text, procedura `COMUN\docs\inrolare-proiect-git-text.md`).
|
||||
- **ROACONTRACTE**: `ferestre_contracte.vc2` are cache text si e vizibil. Descoperire importanta:
|
||||
clasa container `ct_contracte` (`:9`, `DEFINE CLASS ct_contracte AS _ctfrmbase OF
|
||||
"..\comun\clase\_ct_base.vcx"`) **mosteneste acelasi `_ct_base`** ca `ct_comenzi`, si are propriul
|
||||
`filtru_pretty`/`but_start_criterii1` (vazut in `But_reset_criterii1.Click`, `:1551-1567`) — deci
|
||||
infrastructura de cautare exista. **Dar nu suprascrie `actualizeaza_cursoare`/`creeaza_cursoare`**
|
||||
(0 hit-uri in fisier) — inseamna ca foloseste stub-ul gol din `_ct_base` pentru acel hook, iar
|
||||
cursorul principal (`cContracte`, vazut deja populat la `PROCEDURE Init` a ferestrei,
|
||||
`:1518-1527`, `Select cContracte / If Reccount()>0`) se incarca **altundeva, probabil eager, la
|
||||
deschiderea formularului** — nu s-a gasit punctul exact de populare in bugetul alocat (cautare
|
||||
separata necesara: `cContracte` INTO CURSOR / gencursor pentru contracte). **Concluzie: ROACONTRACTE
|
||||
NU e azi lazy**, desi are "schela" pentru a deveni (acelasi `_ct_base`). Pentru #10/#11, sablonul
|
||||
de copiat e strict cel din `ocomenzi.vc2`, nu cel din `ferestre_contracte.vc2`.
|
||||
|
||||
---
|
||||
|
||||
## Neacoperit / de reluat intr-o runda viitoare
|
||||
- `CRM_VPOLPRETCURUTIL` — definitia view-ului (coloane exacte, drepturi).
|
||||
- `ID_JTVA_COLOANA` — cum se leaga procentul afisat in `cursor_preturi` (`PROC_TVAV`) de coloana TVA
|
||||
oficiala folosita la postare.
|
||||
- Unde/cum populeaza azi VFP parametrul `V_CONT` la `adauga_articol_factura` (ipoteza: din gestiune,
|
||||
nu din articol) — relevant pentru a confirma daca planul #12 poate refolosi direct
|
||||
`NOM_ARTICOLE.CONT` fara alta lucrare.
|
||||
- Incarcarea listei/grilei principale de articole din `onom_articole.vc2` (formularul-parinte, nu
|
||||
editorul per-inregistrare).
|
||||
- Unde exact se populeaza `cContracte` in `ferestre_contracte.vc2` (cautare punctuala, nu facuta).
|
||||
- ROAGEST/ROAACNPRO: confirmarea ca folosesc `PACK_FACTURARE` exclusiv server-side pentru preturi
|
||||
(ipoteza, nu verificata prin citirea completa a `ofactureaza.prg`/`proceduri_acnpro.prg`).
|
||||
110
docs/cercetare/rec_s10_s12.md
Normal file
110
docs/cercetare/rec_s10_s12.md
Normal file
@@ -0,0 +1,110 @@
|
||||
# S10 + S12 — versiune_db.txt, curatare VERSIUNE, propunere changelog 2.11.13
|
||||
|
||||
## S10 — `versiune_db.txt`
|
||||
|
||||
Regula exacta, confirmata in `COMUN\docs\scripturi-migrare-db.md` ("Numerotare si versiune_db.txt"):
|
||||
in `versiune_db.txt` se trece **doar versiunea ultimului script `ff_`** (nu `co_`/`sys_`/`rf_`/`ris_`),
|
||||
pentru ca programele se conecteaza pe schema firmei, nu pe `CONTAFIN_ORACLE`.
|
||||
|
||||
Verificat pe disc, `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\`: cinci scripturi `ff_2026_08_06_*`
|
||||
aplicate azi pe `MARIUSM_AUTO` — `_02` (`PACK_FACTURARE`, S4), `_03` (`PACK_FACTURARE`, S7), `_04`
|
||||
(`VANZARI_COMANDA_CONTRACT`, S6), `_05` (`FACT_VFACTURI`, S8), `_06` (`VANZARI_BACKFILL`, S5).
|
||||
Confirmat si prin interogare pe `VERSIUNE` (`order by data_script desc, seq_script desc`): ultimul
|
||||
`ff_` aplicat e `ff_2026_08_06_06_COMUN_VANZARI_BACKFILL.sql`.
|
||||
|
||||
**Scris in `versiune_db.txt`: `2026_08_06_06`** (fara newline la final, pastrand conventia
|
||||
fisierului existent — verificat byte-level, 13 octeti, identic ca lungime cu vechea valoare
|
||||
`2026_08_02_01`).
|
||||
|
||||
## S10 — starea tabelei `VERSIUNE`
|
||||
|
||||
Interogata direct pe `MARIUSM_AUTO` (`docs\cercetare\s10_curata_versiune.sql`, pasul 1). Numar de
|
||||
inregistrari per script, azi:
|
||||
|
||||
| Script | Inregistrari |
|
||||
|---|---|
|
||||
| `ff_2026_08_06_02_COMUN_PACK_FACTURARE.sql` (S4) | 2 |
|
||||
| `ff_2026_08_06_03_COMUN_PACK_FACTURARE.sql` (S7) | 1 |
|
||||
| `ff_2026_08_06_04_COMUN_VANZARI_COMANDA_CONTRACT.sql` (S6) | 2 |
|
||||
| `ff_2026_08_06_05_COMUN_FACT_VFACTURI.sql` (S8) | 4 |
|
||||
| `ff_2026_08_06_06_COMUN_VANZARI_BACKFILL.sql` (S5) | 5 |
|
||||
|
||||
14 randuri in total pentru cele 5 scripturi de azi, 9 in plus fata de cate 1 per script. Confirma
|
||||
tiparul semnalat: `_02`, `_05`, `_06` (si, in plus fata de ce era asteptat, `_04`) au fost aplicate
|
||||
de mai multe ori pe masura extinderii lor in cursul zilei; `_03` are deja un singur rand, nimic de
|
||||
curatat acolo.
|
||||
|
||||
**Propunere, nerulata**: `docs\cercetare\s10_curata_versiune.sql`. Pastreaza per script randul cu
|
||||
`ID_VERSIUNE` maxim (ultima aplicare = starea finala reala a scriptului), sterge restul — scoped
|
||||
strict pe cele 5 nume de script din lista de mai sus, cu `select` de verificare inainte si dupa,
|
||||
`delete` la mijloc, `commit` comentat (de dat manual). Fara impact functional indiferent daca se
|
||||
ruleaza sau nu: nici `versiune_db.txt`, nici aplicarea DDL nu depind de numarul de randuri din
|
||||
`VERSIUNE`. **Nu s-a rulat** — stergerea de istoric ramane decizie de om.
|
||||
|
||||
## S12 — propunere changelog
|
||||
|
||||
Livrat: `docs\propunere_changelog_2.11.13.txt` (neaplicat in `changelog_roafacturare.txt`).
|
||||
|
||||
```
|
||||
<!--
|
||||
06/08/2026
|
||||
ROAFACTURARE - 2.11.13
|
||||
|
||||
:eroare:
|
||||
Referinta catre aviz de pe facturi era gresita - toate facturile aratau acelasi aviz, indiferent de cel real. Acum se afiseaza avizul corect acolo unde exista, iar unde nu exista referinta, campul ramane necompletat.
|
||||
|
||||
Unele facturi mai vechi ramasesera fara total salvat la emitere si aparea fara valoare la listare sau retiparire. Totalurile lipsa au fost completate.
|
||||
|
||||
Cursul valutar afisat pe facturile emise in lei era uneori inregistrat gresit. Facturile in lei nu mai afiseaza curs valutar strain.
|
||||
|
||||
La facturile de retur-transfer numele clientului afisat in lista de facturi era uneori gresit. Acum se afiseaza clientul corect.
|
||||
|
||||
:modificare:
|
||||
S-a uniformizat textul explicativ (comanda/contract) afisat pe facturi.
|
||||
-->
|
||||
```
|
||||
|
||||
### Motivare inclusiune/excludere
|
||||
|
||||
- **Aviz, totaluri, curs valutar** (`:eroare:`) — toate trei sunt defecte confirmate ca fiind
|
||||
efectiv pe date de productie (`VENDING`), vizibile pe facturi reale (lista principala si
|
||||
retiparire), acum corectate. Se incadreaza clar la `:eroare:`.
|
||||
- **`CLIENT` pe retur-transfer** (`:eroare:`) — `fact_vfacturi`, folosit de gridul principal de
|
||||
listare, calcula gresit numele clientului pe cele 23 de facturi `tip=41`/`tip=-6`; view-ul
|
||||
corect (`fact_vfacturi2`) confirma sursa deliberata din cod. E o valoare gresita afisata in
|
||||
productie, nu doar o diferenta cosmetica intre doua liste — de aceea l-am pus la `:eroare:` si
|
||||
nu la `:modificare:`, desi in mesajul initial parea doar "etichete neuniforme".
|
||||
- **`EXPLICATIE`** (`:modificare:`) — diferenta e strict de formatare a textului
|
||||
("COMERCIAL - FACTURARE" vs "COMERCIAL-FACTURARE" etc.), nu o valoare gresita; l-am tinut separat
|
||||
si mai jos in prioritate, la `:modificare:`.
|
||||
- **Denormalizarea comanda/contract (S6) — EXCLUSA din changelog.** Harnessul de regresie da
|
||||
aceleasi cifre inainte si dupa aplicarea scriptului; nu exista nimic vizibil pentru utilizator.
|
||||
O intrare de changelog ar fi inselatoare (ar sugera o schimbare functionala inexistenta).
|
||||
- **Incasarea lipsa pe facturile din devize auto — EXCLUSA din `changelog_roafacturare.txt`.**
|
||||
Corectia e integral in cod VFP din **ROAAUTO** (`Programe\oproceduri_devize.prg`,
|
||||
`factureaza_deviz`) plus un parametru nou cu `DEFAULT` in `PACK_FACTURARE.scrie_incasari`
|
||||
(pachet Oracle comun, dar ramura noua e apelata **doar** din ROAAUTO). Recompilarea
|
||||
`roafacturare.exe` singura, fara recompilarea ROAAUTO, nu produce niciun efect vizibil pentru
|
||||
utilizatorul ROAFACTURARE — deci intrarea nu apartine acestui changelog, chiar daca facturile
|
||||
respective ar putea fi vazute si din listele ROAFACTURARE dupa ce ROAAUTO e recompilat si el.
|
||||
**Propunere de text pentru `changelog_roaauto.txt` (needitat, doar propus aici)**:
|
||||
|
||||
```
|
||||
<!--
|
||||
06/08/2026
|
||||
ROAAUTO - 2.5.5
|
||||
|
||||
:eroare:
|
||||
La facturile emise din deviz auto nu se inregistra incasarea (chitanta/bon), desi factura era platita. S-a corectat.
|
||||
-->
|
||||
```
|
||||
|
||||
## Fisiere livrate
|
||||
|
||||
- `versiune_db.txt` — actualizat la `2026_08_06_06` (editat direct, marcaj de proiect).
|
||||
- `docs\cercetare\s10_curata_versiune.sql` — propunere de curatare, **nerulata**.
|
||||
- `docs\propunere_changelog_2.11.13.txt` — propunere, **neaplicata** in `changelog_roafacturare.txt`.
|
||||
- Acest raport.
|
||||
|
||||
**Fara commit** (git/SVN). Nu s-au atins scripturile de migrare, nu s-a rulat DDL, nu s-a scris in
|
||||
`VERSIUNE`.
|
||||
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.
|
||||
296
docs/cercetare/rec_s5_script_oracle.md
Normal file
296
docs/cercetare/rec_s5_script_oracle.md
Normal file
@@ -0,0 +1,296 @@
|
||||
# Verificare script Oracle S5 — PACK_FACTURARE.recalculeaza_totaluri_vanzari
|
||||
|
||||
Data: 09.08.2026. Livrabil: `docs/ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` (823433 octeti,
|
||||
17231 randuri; 17010->17231 fata de `.pck`-ul sursa, +221 randuri de continut adaugat).
|
||||
|
||||
Construit programatic dintr-un script PowerShell (nu retastat): citeste `PACK_FACTURARE.pck`
|
||||
(export proaspat, scratchpad, 810960 octeti, 17011 randuri) ca octeti ASCII, insereaza declaratia
|
||||
noua in SPEC dupa `actualizeaza_vanzari` si corpul nou in BODY dupa `actualizeaza_vanzari`, adauga
|
||||
`CREATE OR REPLACE` pe cele doua linii de start (absente in exportul brut din `all_source`),
|
||||
antetul si coada, si scrie rezultatul CRLF. Fiecare punct de insertie e verificat printr-un
|
||||
assert pe textul exact al ancorei (throw daca nu se potriveste) — scriptul s-a oprit si a fost
|
||||
corectat de doua ori in timpul lucrului (vezi „Erori prinse" mai jos), rularea finala a trecut
|
||||
toate ancorele.
|
||||
|
||||
## Ce s-a facut
|
||||
|
||||
1. **PACKAGE SPEC** (`.pck:1187-1189`): dupa declaratia `actualizeaza_vanzari`, s-a inserat
|
||||
`PROCEDURE recalculeaza_totaluri_vanzari(V_ID_VANZARE IN NUMBER, V_DISCOUNT IN NUMBER DEFAULT
|
||||
NULL);` — text identic cu cel din brief.
|
||||
2. **PACKAGE BODY** (`.pck:16015-16025`, dupa `END actualizeaza_vanzari;`): s-a inserat procedura
|
||||
noua, corpul fiind blocul de agregare din `scrie_in_vanzari` (`.pck:13762-13954`) cu substitutiile
|
||||
cerute, plus SELECT-ul de discount/in_valuta/discount_evidentiat inainte, plus `discount` in
|
||||
UPDATE, fara handler de exceptie — toate exact ca in sectiunea 7 a specificatiei.
|
||||
3. Antet de 6 randuri (4 randuri text + 1 rand `--` gol + titlu), fara referinte la planuri/rapoarte.
|
||||
4. Coada: `exec pack_migrare.UpdateVersiune('ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql'); commit;`
|
||||
|
||||
## Verificari cerute, cu cifre
|
||||
|
||||
**CRLF** — octeti LF fara CR inainte in fisierul final: **0**.
|
||||
|
||||
**Nume sub 30 caractere** — `'recalculeaza_totaluri_vanzari'.Length` = **29**. Confirmat.
|
||||
|
||||
**Diff-ul blocului de agregare** — original (`.pck:13762-13954`, 193 randuri, citat integral in
|
||||
`rec_s5_proiectare_oracle.md` sectiunea 2) vs. blocul nou din procedura (extras din fisierul
|
||||
livrat). Generat cu `diff -u -b` (ignora *doar* diferentele de cantitate de spatiu — necesar
|
||||
pentru ca tot blocul a fost mutat cu un nivel de indentare mai putin, vezi nota de mai jos; `-b`
|
||||
nu ascunde nicio diferenta de continut). Diff-ul integral:
|
||||
|
||||
```diff
|
||||
--- orig_block.txt (PACK_FACTURARE.pck:13762-13954)
|
||||
+++ new_block.txt (recalculeaza_totaluri_vanzari, corpul agregarii)
|
||||
@@ -1,5 +1,4 @@
|
||||
- -- Completare totaluri in vanzari din vanzari_detalii pentru a usura selectiile din fact_vfacturi
|
||||
- begin
|
||||
+-- Completare totaluri in vanzari din vanzari_detalii pentru a usura selectiile din fact_vfacturi
|
||||
select DISC_TVA_VAL AS DISCOUNT_TVA,
|
||||
VALOARE_ACHIZITIE,
|
||||
a.suma_fara_tva_ron - a.disc_fara_tva_ron as TOTAL_FARA_TVA,
|
||||
@@ -12,11 +11,7 @@
|
||||
a.disc_tva_val as TOTVAL,
|
||||
id_valuta,
|
||||
curs,
|
||||
- multiplicator,
|
||||
- pack_facturare.cserie_act_incasare as SERIE_INCASAT,
|
||||
- pack_facturare.nnumar_act_incasare as NR_INCASAT,
|
||||
- pack_facturare.nsuma_incasare AS SUMA_INCASAT,
|
||||
- pack_facturare.ntip_doc_incasare as TIP_INCASAT
|
||||
+ multiplicator
|
||||
INTO lnDiscountTVA,
|
||||
lnValoareAchizitie,
|
||||
lnTotalFaraTVA,
|
||||
@@ -27,29 +22,25 @@
|
||||
lnTotVal,
|
||||
lnIdValuta,
|
||||
lnCurs,
|
||||
- lnMultiplicator,
|
||||
- lnSerieIncasat,
|
||||
- lnNrIncasat,
|
||||
- lnSumaIncasat,
|
||||
- lnTipIncasat
|
||||
- FROM (select MAX(decode(pack_facturare.nin_valuta,
|
||||
+ lnMultiplicator
|
||||
+ FROM (select MAX(decode(lnInValuta,
|
||||
1,
|
||||
- ROUND(a1.curs * NVL(V_DISCOUNT_FACTURA, 0) /
|
||||
+ ROUND(a1.curs * NVL(lnDiscountFactura, 0) /
|
||||
a1.multiplicator,
|
||||
lnPreciziePretV),
|
||||
- NVL(V_DISCOUNT_FACTURA, 0))) as DISC_FARA_TVA_RON,
|
||||
- NVL(V_DISCOUNT_FACTURA, 0) as DISC_FARA_TVA_VAL,
|
||||
- pack_facturare.nin_valuta AS IN_VALUTA,
|
||||
- MAX(ROUND(decode(pack_facturare.nin_valuta,
|
||||
+ NVL(lnDiscountFactura, 0))) as DISC_FARA_TVA_RON,
|
||||
+ NVL(lnDiscountFactura, 0) as DISC_FARA_TVA_VAL,
|
||||
+ lnInValuta AS IN_VALUTA,
|
||||
+ MAX(ROUND(decode(lnInValuta,
|
||||
1,
|
||||
ROUND(a1.curs *
|
||||
- NVL(V_DISCOUNT_FACTURA, 0) /
|
||||
+ NVL(lnDiscountFactura, 0) /
|
||||
a1.multiplicator,
|
||||
lnPreciziePretV),
|
||||
- NVL(V_DISCOUNT_FACTURA, 0)) *
|
||||
+ NVL(lnDiscountFactura, 0)) *
|
||||
(a1.proc_tvav - 1),
|
||||
lnPreciziePretV)) as DISC_TVA_RON,
|
||||
- MAX(ROUND(NVL(V_DISCOUNT_FACTURA, 0) *
|
||||
+ MAX(ROUND(NVL(lnDiscountFactura, 0) *
|
||||
(a1.proc_tvav - 1),
|
||||
lnPreciziePretV)) as DISC_TVA_VAL,
|
||||
sum(pack_facturare.calculeaza_total_fara_tva_fact(a1.pret_ron,
|
||||
@@ -57,7 +48,7 @@
|
||||
1,
|
||||
NVL(a1.discount_unitar_ron,
|
||||
0),
|
||||
- pack_facturare.ndiscount_evidentiat,
|
||||
+ lnDiscountEvidentiat,
|
||||
a1.cantitate,
|
||||
a1.pret_cu_tva,
|
||||
a1.proc_tvav)) AS SUMA_FARA_TVA_RON,
|
||||
@@ -66,7 +57,7 @@
|
||||
1,
|
||||
NVL(a1.discount_unitar_ron,
|
||||
0),
|
||||
- pack_facturare.ndiscount_evidentiat,
|
||||
+ lnDiscountEvidentiat,
|
||||
a1.cantitate,
|
||||
a1.pret_cu_tva,
|
||||
a1.proc_tvav)) as SUMA_TVA_RON,
|
||||
@@ -75,7 +66,7 @@
|
||||
1,
|
||||
NVL(a1.discount_unitar_val,
|
||||
0),
|
||||
- pack_facturare.ndiscount_evidentiat,
|
||||
+ lnDiscountEvidentiat,
|
||||
a1.cantitate,
|
||||
a1.pret_cu_tva,
|
||||
a1.proc_tvav)) AS SUMA_FARA_TVA_VAL,
|
||||
@@ -84,18 +75,18 @@
|
||||
1,
|
||||
NVL(a1.discount_unitar_val,
|
||||
0),
|
||||
- pack_facturare.ndiscount_evidentiat,
|
||||
+ lnDiscountEvidentiat,
|
||||
a1.cantitate,
|
||||
a1.pret_cu_tva,
|
||||
a1.proc_tvav)) as SUMA_TVA_VAL,
|
||||
sum(round(a1.cantitate * a1.pret_achizitie,
|
||||
lnPrecizieCalcul)) AS VALOARE_ACHIZITIE,
|
||||
- max(decode(pack_facturare.nin_valuta, 1, a1.id_valuta, 0)) as id_valuta,
|
||||
- max(decode(pack_facturare.nin_valuta, 1, a1.curs, 1)) as curs,
|
||||
- max(decode(pack_facturare.nin_valuta, 1, a1.multiplicator, 1)) as multiplicator
|
||||
+ max(decode(lnInValuta, 1, a1.id_valuta, 0)) as id_valuta,
|
||||
+ max(decode(lnInValuta, 1, a1.curs, 1)) as curs,
|
||||
+ max(decode(lnInValuta, 1, a1.multiplicator, 1)) as multiplicator
|
||||
from (select vd.id_vanzare_set,
|
||||
(case
|
||||
- when (pack_facturare.nin_valuta = 1 or
|
||||
+ when (lnInValuta = 1 or
|
||||
vd.id_valuta <>
|
||||
pack_def.GetIdMonedaNationala()) then
|
||||
ROUND(vc.curs * vd.pret / vc.multiplicator,
|
||||
@@ -108,7 +99,7 @@
|
||||
vd.cantitate,
|
||||
vd.diferenta,
|
||||
(case
|
||||
- when (pack_facturare.nin_valuta = 1 or
|
||||
+ when (lnInValuta = 1 or
|
||||
vd.id_valuta <>
|
||||
pack_def.GetIdMonedaNationala()) then
|
||||
ROUND(vc.curs * vd.discount_unitar /
|
||||
@@ -132,8 +123,10 @@
|
||||
a.id_valuta,
|
||||
a.pret_cu_tva,
|
||||
a.pret_achizitie
|
||||
- from VANZARI_DETALII_TEMP a
|
||||
+ from VANZARI_DETALII a
|
||||
where nvl(a.id_vanzare_set, 0) = 0
|
||||
+ and a.id_vanzare = V_ID_VANZARE
|
||||
+ and a.sters = 0
|
||||
union all
|
||||
select b.id_vanzare_set,
|
||||
b.pret,
|
||||
@@ -141,7 +134,7 @@
|
||||
b.cantitate,
|
||||
0 as diferenta,
|
||||
b.discount_unitar,
|
||||
- decode(pack_facturare.nin_valuta,
|
||||
+ decode(lnInValuta,
|
||||
0,
|
||||
pack_def.GetIdMonedaNationala(),
|
||||
c.id_valuta) as id_valuta,
|
||||
@@ -151,17 +144,19 @@
|
||||
0,
|
||||
c.pret_achizitie * c.cantitate /
|
||||
b.cantitate)) as pret_achizitie
|
||||
- from vanzari_detalii_temp c
|
||||
+ from vanzari_detalii c
|
||||
left join vanzari_seturi b
|
||||
on b.id_vanzare_set = c.id_vanzare_set
|
||||
where nvl(c.id_vanzare_set, 0) <> 0
|
||||
- and nvl(pack_facturare.nin_valuta, -1) > -1
|
||||
+ and c.id_vanzare = V_ID_VANZARE
|
||||
+ and c.sters = 0
|
||||
+ and nvl(lnInValuta, -1) > -1
|
||||
group by b.id_vanzare_set,
|
||||
b.pret,
|
||||
b.cantitate,
|
||||
b.discount_unitar,
|
||||
b.pret_cu_tva,
|
||||
- decode(pack_facturare.nin_valuta,
|
||||
+ decode(lnInValuta,
|
||||
0,
|
||||
pack_def.GetIdMonedaNationala(),
|
||||
c.id_valuta)) vd
|
||||
@@ -170,7 +165,8 @@
|
||||
and vd.id_valuta = vc.id_valuta) a1) a;
|
||||
|
||||
update vanzari
|
||||
- set discount_tva = lnDiscountTVA,
|
||||
+ set discount = lnDiscountFactura,
|
||||
+ discount_tva = lnDiscountTVA,
|
||||
valoare_achizitie = lnValoareAchizitie,
|
||||
total_fara_tva = lnTotalFaraTVA,
|
||||
total_tva = lnTotalTVA,
|
||||
@@ -180,14 +176,5 @@
|
||||
totval = lnTotVal,
|
||||
id_valuta = lnIdValuta,
|
||||
curs = lnCurs,
|
||||
- multiplicator = lnMultiplicator,
|
||||
- serie_incasat = lnSerieIncasat,
|
||||
- nr_incasat = lnNrIncasat,
|
||||
- suma_incasat = lnSumaIncasat,
|
||||
- tip_incasat = lnTipIncasat
|
||||
+ multiplicator = lnMultiplicator
|
||||
where id_vanzare = V_ID_VANZARE;
|
||||
-
|
||||
- exception
|
||||
- when NO_DATA_FOUND then
|
||||
- null;
|
||||
- end;
|
||||
```
|
||||
|
||||
Diff-ul contine **exact**: cele 6 substitutii din tabelul din brief (`nin_valuta`->`lnInValuta` x11,
|
||||
`ndiscount_evidentiat`->`lnDiscountEvidentiat` x4, `NVL(V_DISCOUNT_FACTURA, 0)`->
|
||||
`NVL(lnDiscountFactura, 0)` x6, cele doua perechi FROM/WHERE), eliminarea celor 4 coloane de
|
||||
incasare din SELECT/INTO/UPDATE (cu fixarea virgulei ramase), adaugarea `discount =
|
||||
lnDiscountFactura,` in UPDATE si eliminarea wrapper-ului `begin ... exception ... end;` (cerut
|
||||
explicit: „Fara handler WHEN NO_DATA_FOUND THEN NULL"). **Nicio alta diferenta de continut.**
|
||||
|
||||
Nota pe metoda: `-b` a fost necesar (nu `diff` simplu) pentru ca tot blocul, o data scos din
|
||||
`begin...end;`-ul intern, a coborat cu un nivel de indentare (2 spatii) — o consecinta mecanica,
|
||||
uniforma, a aplatizarii cerute de sectiunea 7, nu o modificare de continut. Fara `-b`, diff-ul ar
|
||||
fi aratat *toate* liniile ca schimbate, desi doar spatiul de inceput difera pe liniile
|
||||
neatinse de tabelul de substitutii (verificat separat: liniile fara nicio substitutie, ex.
|
||||
`select DISC_TVA_VAL AS DISCOUNT_TVA,`, `VALOARE_ACHIZITIE,`, nu apar deloc in diff-ul de mai sus).
|
||||
|
||||
**`;` in comentarii `--` in interiorul unei instructiuni** — 11 aparitii ale tiparului `--.*;` in
|
||||
tot fisierul, **toate preexistente** in codul neatins (`scrie_incasari`, `contabilizeaza_articol`
|
||||
etc., linii 5908-14852 din script), **niciuna** introdusa de mine (verificat separat: 0 in antetul
|
||||
nou, 0 in declaratia SPEC noua, 0 in corpul noii proceduri). Riscul descris in
|
||||
`scripturi-migrare-db.md` (SP2-0734) se aplica instructiunilor SQL terminate cu `;`
|
||||
(`CREATE VIEW` etc.); intregul script de fata e un singur bloc `CREATE OR REPLACE PACKAGE`/
|
||||
`PACKAGE BODY` terminat cu `/`, deci riscul nu se aplica structural — dar cifra e cea ceruta.
|
||||
|
||||
**Constructii peste Oracle 10.2** — scanat corpul noii proceduri pentru
|
||||
`LISTAGG|CONTINUE|REGEXP_COUNT|FETCH FIRST|PIVOT|NEXTVAL`: **0 aparitii** pentru fiecare.
|
||||
Identificatori: cel mai lung e `recalculeaza_totaluri_vanzari` (29) si `lnDiscountEvidentiat` (20)
|
||||
— niciunul peste 30.
|
||||
|
||||
**Numarul de linii** — fisier final: **17231** (17230 dupa convenția `wc -l`, care nu numara
|
||||
ultimul rand fiindca fisierul, la fel ca sursa, nu are newline final); `.pck` original:
|
||||
**17011** (17010 `wc -l`). Delta: +221 randuri adaugate (antet 7 + insert SPEC 3 + rand gol inainte
|
||||
de `CREATE OR REPLACE PACKAGE BODY` 1 + procedura noua in BODY 205 + coada 4 + `CREATE OR REPLACE`
|
||||
adaugat pe 2 linii existente, fara linii noi acolo).
|
||||
|
||||
**Coloanele de incasare** — `serie_incasat`, `nr_incasat`, `suma_incasat`, `tip_incasat`: **0**
|
||||
aparitii in SELECT/INTO/UPDATE-ul noii proceduri (verificat separat, izolat pe textul extras al
|
||||
procedurii). Apar in continuare de 5 ori fiecare **in restul fisierului** — in `scrie_in_vanzari`,
|
||||
neatinsa, unde e corect sa ramana (comportamentul de emitere nu se schimba).
|
||||
|
||||
## Erori prinse si corectate in timpul lucrului (nu au ajuns in fisierul livrat)
|
||||
|
||||
1. Prima rulare a omis complet linia `discount = lnDiscountFactura,` din `UPDATE` (am copiat doar
|
||||
eliminarea coloanelor de incasare, am uitat adaugarea cerincetei separat in brief). Prins de
|
||||
verificarea numerica (`lnDiscountFactura` aparea de 8 ori in loc de 9) inainte de a scrie
|
||||
raportul; corectat si re-rulat.
|
||||
2. Prima rulare a scris `PACKAGE "PACK_FACTURARE" is` si `PACKAGE BODY PACK_FACTURARE is` fara
|
||||
prefixul `CREATE OR REPLACE` pe **linia de SPEC** (l-am adaugat doar pe linia de BODY). Ar fi
|
||||
dat eroare de sintaxa la aplicare — `PACKAGE ... is` singur nu e o instructiune DDL valida.
|
||||
Prins prin citirea directa a antetului fisierului scris; corectat si re-rulat.
|
||||
|
||||
## Ce NU am facut / neverificat
|
||||
|
||||
- Nu am rulat nimic in Oracle — niciun `sqlplus`, niciun DDL, niciun test de compilare a
|
||||
pachetului. Corectitudinea sintactica dincolo de verificarile de mai sus (paranteze, virgule,
|
||||
cuvinte cheie) nu e garantata decat prin inspectie si prin construirea mecanica din blocul
|
||||
original deja compilat.
|
||||
- Coada scriptului foloseste `UpdateVersiune('ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql')` **cu**
|
||||
extensia `.sql`, asa cum cere explicit brief-ul si `scripturi-migrare-db.md` („script_final =
|
||||
numele fisierului, cu tot cu .sql"). Modelul citat (`ff_2026_08_06_10_...sql:17019`) foloseste
|
||||
de fapt numele **fara** `.sql` — o inconsistenta intre precedent si regula scrisa. Am urmat
|
||||
regula scrisa si instructiunea explicita, nu precedentul; semnalez discrepanta, nu am
|
||||
„reparat-o" in modelul vechi (nu era in scop).
|
||||
- Nu am atins `versiune_db.txt`, nu am scris in `D:\ROA\DATABASE\SCRIPTURI_CLAR`, nu am dat
|
||||
commit — conform interdictiilor din brief.
|
||||
341
docs/cercetare/rec_tva_vanzari.md
Normal file
341
docs/cercetare/rec_tva_vanzari.md
Normal file
@@ -0,0 +1,341 @@
|
||||
# Cercetare: TVA calculat vs salvat (#7) + denormalizare VANZARI (#8)
|
||||
|
||||
Sursele principale sunt fisierele "curate" din arhiva de migrare Oracle
|
||||
`D:\ROA\DATABASE\SCRIPTURI_CLAR\...` (nu in working copy VFP git — DDL/PL-SQL nu e versionat in
|
||||
`ROAFACTURARE`), plus `D:\ROA\ROAFACTURARE\COMUN\clase\ofacturare.vc2` (working copy VFP, text
|
||||
`.vc2` deja la zi).
|
||||
|
||||
Cea mai recenta definitie **completa** a pachetului `PACK_FACTURARE` (COMUN, folosit de toate
|
||||
produsele ROA care factureaza) e in
|
||||
`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\03\ff_2026_03_23_04_COMUN_PACK_FACTURARE.sql` (16948 linii).
|
||||
Toate liniile citate mai jos fara alta mentiune sunt din acest fisier.
|
||||
|
||||
---
|
||||
|
||||
## SUBIECT A — TVA calculat, nu salvat (#7)
|
||||
|
||||
### 1. Structura VANZARI / VANZARI_DETALII (coloane pret/TVA)
|
||||
|
||||
Nu exista `CREATE TABLE VANZARI_DETALII` in arhiva cautata (tabela e mai veche decat
|
||||
`SCRIPTURI_CLAR`, care incepe efectiv din 2009; originea e probabil in
|
||||
`D:\ROA\DATABASE\SCRIPTURI\2006-2008`, nu am parcurs exhaustiv acel arhiv de migrari vechi).
|
||||
Coloanele efective au fost confirmate prin utilizarea lor in cod (view-uri + SQL dinamic VFP):
|
||||
|
||||
**VANZARI_DETALII** (per linie articol):
|
||||
- `PRET` — pret unitar (fara/cu TVA, in functie de flag)
|
||||
- `DIFERENTA` — ajustare pret (folosita in `calculeaza_total_*_fact`)
|
||||
- `DISCOUNT_UNITAR`
|
||||
- `CANTITATE`
|
||||
- `PRET_CU_TVA` — flag NUMBER(1): 1 = pretul de mai sus e cu TVA inclus, 0 = fara TVA
|
||||
(`ff_2026_07_27_01_FACTURARE.sql:25,43` — `b.pret_cu_tva`)
|
||||
- `PROC_TVAV` — procent TVA ca multiplicator (ex. 1.19), nu procent brut
|
||||
(`ff_2026_07_27_01_FACTURARE.sql:26` — `b.proc_tvav`)
|
||||
- `PRET_ACHIZITIE` (`ff_2026_07_27_01_FACTURARE.sql:105,130`)
|
||||
- `ID_ARTICOL`, `ID_VALUTA`, `ID_POL` (politica de pret), `SERIE`, `STERS`, `ID_VANZARE`,
|
||||
`ID_VANZARE_DET` (`ff_2026_07_27_01_FACTURARE.sql:11-76`)
|
||||
- `TAXCODE` — adaugata `2022\03\ff_2022_03_24_01_COMUN_SAFT.sql:6` (cod taxa SAFT)
|
||||
- `ID_JTVA_COLOANA_EX` — adaugata `2019\08\ff_2019_08_20_04_COMUN.sql:7`
|
||||
- `LOT` — adaugata `2024\06\ff_2024_06_04_01_COMUN_FACTURARE.sql:3`
|
||||
|
||||
**NU exista** o coloana de TVA calculata/salvata per linie (nici `valoare_tva`, nici
|
||||
`pret_fara_tva` rezultat) — doar inputurile (`pret`, `pret_cu_tva` flag, `proc_tvav`), confirmand
|
||||
exact ce zice todo #7: TVA e derivat, nu stocat.
|
||||
|
||||
**VANZARI** (antet factura) — coloane denormalizate relevante TVA, toate adaugate prin
|
||||
`2017\03\ff_2017_03_28_01_FACTURARE.sql:9-38`:
|
||||
- `DISCOUNT_TVA` — "TVA DISCOUNT (DISCOUNT * MAX(PROC_TVA) ARTICOLE)"
|
||||
- `TOTAL_FARA_TVA`, `TOTAL_TVA`, `TOTAL_CU_TVA` — totaluri pe document (denormalizate)
|
||||
- `VALOARE_ACHIZITIE`
|
||||
- vezi si Subiect B pt. `SERIE_INCASAT`/`NR_INCASAT`/`SUMA_INCASAT`/`TIP_INCASAT`/`AVIZE`
|
||||
|
||||
### 2. Unde se calculeaza TVA-ul pe linie — toate locurile
|
||||
|
||||
Calculul e centralizat in doua straturi Oracle, apelate de fiecare punct de consum (VFP nu face
|
||||
calcul TVA local — doar construieste SQL dinamic care cheama functiile Oracle):
|
||||
|
||||
- **`PACK_SESIUNE.calculeaza_pret_fara_tva/tva/cu_tva`** — motorul de rotunjire per-unitate de
|
||||
pret (nu e in `PACK_FACTURARE`; pachetul `PACK_SESIUNE` nu a fost gasit definit separat in
|
||||
arhiva cautata — cautare punctuala pe cateva luni din 2025/2026, fara succes; probabil e intr-un
|
||||
script `COMUN_PACK_SESIUNE` mai vechi, netestat exhaustiv).
|
||||
- **`PACK_FACTURARE.calculeaza_pret`** (linia 14557) — wrapper subtire peste cele 3 functii
|
||||
`PACK_SESIUNE` de mai sus.
|
||||
- **`PACK_FACTURARE.calculeaza_sume`** (linia 14631, spec 980) — calculeaza suma_fara_tva /
|
||||
suma_tva / suma_cu_tva pe linie (cantitate * pret), delegand catre:
|
||||
- **`calculeaza_total_fara_tva`** (linia 15745/15769) — delega la `pack_sesiune.calculeaza_total_fara_tva`
|
||||
- **`calculeaza_total_tva`** (linia 15850/15874) — delega la `pack_sesiune.calculeaza_total_tva`
|
||||
- **`calculeaza_total_cu_tva`** (linia 15638/15662) — delega la `pack_sesiune.calculeaza_total_cu_tva`
|
||||
- variantele `_fact` (`calculeaza_total_fara_tva_fact` 15794, `calculeaza_total_tva_fact` 15899,
|
||||
`calculeaza_total_cu_tva_fact` 15687) au **formula proprie inline** (nu delega la
|
||||
`pack_sesiune`), documentata explicit in cod ca fiind "folosita in view-ul
|
||||
fact_vrap_centralizator_fact" (comentarii 15686, 15744, 15793, 15848, 15897).
|
||||
|
||||
Formula `_fact` (exemplu `calculeaza_total_tva_fact`, 15899-15949) e explicit ROUND-based, cu
|
||||
ramuri separate pentru `discount_evidentiat` — aici e punctul central de rotunjire per linie.
|
||||
|
||||
- **Puncte de CONSUM ale acestor functii** (unde efectiv se calculeaza TVA afisat/raportat):
|
||||
- Views `fact_vrap_fact_articole` si `fact_vrap_articole_vandute`
|
||||
(`ff_2026_07_27_01_FACTURARE.sql:18-44, 131-196` — apeleaza
|
||||
`calculeaza_total_fara_tva`/`calculeaza_total_tva` direct in `SELECT`)
|
||||
- Raport VFP dinamic: `COMUN\programe\oproceduri_rapoarte_fact.prg:581-590` — SQL construit in
|
||||
VFP apeleaza direct `pack_sesiune.calculeaza_pret_cu_tva(...)` si
|
||||
`pack_sesiune.calculeaza_total_cu_tva(...)` pentru listare/relistare rapoarte articole vandute.
|
||||
- `PACK_FACTURARE.scrie_in_vanzari` (13468) si `finalizeaza_avize_lucrare` (14807) — calculeaza
|
||||
si scriu `VANZARI.TOTAL_FARA_TVA`/`TOTAL_CU_TVA` folosind `calculeaza_total_fara_tva_fact` in
|
||||
subquery-uri (13716-13786, 14965-15008).
|
||||
- `verifica_total_document` (16009) — vezi punctul 5, verifica totalul calculat vs. cel din
|
||||
notele contabile si genereaza o corectie.
|
||||
|
||||
Nu am gasit un apel VFP direct catre `calculeaza_pret(` sau `calculeaza_sume(` in working copy
|
||||
`ROAFACTURARE`/`COMUN` (grep pe nume exact, fara rezultate) — deci formularul de introducere
|
||||
factura in VFP nu recalculeaza local pretul/TVA-ul linie cu linie prin apel explicit de
|
||||
procedura; cel mai probabil articolele + preturile lor (deja cu `pret`, `pret_cu_tva`,
|
||||
`proc_tvav`) vin dintr-un cursor Oracle (`PACK_FACTURARE.cursor_preturi`, spec 335, body 2121)
|
||||
populat cand se adauga articolul, iar TVA vizibila in grid e calculata la afisare din acele
|
||||
coloane brute — nu am gasit expresia VFP exacta (`cursor_preturi` e ~500 linii, nu am parcurs
|
||||
linie cu linie in bugetul acestei cercetari).
|
||||
|
||||
### 3. Ce inseamna "preturi_cu_tva" / `pret_cu_tva`
|
||||
|
||||
- Pe linie: `VANZARI_DETALII.PRET_CU_TVA` — NUMBER(1), 0/1, transmis ca parametru `V_PRET_CU_TVA`
|
||||
/ `V_PRET_ARE_TVA` la toate functiile de calcul de mai sus (controleaza care ramura de formula
|
||||
se aplica — pornind de la pret cu TVA sau fara TVA).
|
||||
- In grid-ul VFP exista o coloana **checkbox** `cPret_cu_tva` in
|
||||
`D:\ROA\ROAFACTURARE\Clase\ofundal_facturare.vc2:696-699` (`_grdrow2.cPret_cu_tva...`) — probabil
|
||||
afiseaza/permite editarea flagului per linie in formular.
|
||||
- **Nu am gasit** coloana `PRET_CU_TVA` (sau similara) direct pe `CRM_POLITICI_PRETURI` in
|
||||
scripturile de migrare cautate (am cautat `ALTER TABLE CRM_POLITICI_PRETURI ADD`, fara hit
|
||||
pe acest nume) — deci originea exacta a valorii implicite per politica de pret ("politica are
|
||||
bifat 'preturi fara TVA'", cum descrie todo #7) nu e confirmata cu `fisier:linie`; e probabil
|
||||
citita/propagata in `PACK_FACTURARE.cursor_preturi` (2121) sau
|
||||
`citeste_setari_pol_pret` (spec 324, body 2025) cand se adauga articolul pe factura — nu am
|
||||
parcurs acele corpuri in detaliu (buget de cercetare).
|
||||
|
||||
### 4. Locuri de atins daca s-ar salva `pret_cu_tva`/`valoare_tva` per linie real (nu calculat)
|
||||
|
||||
Enumerare pe baza punctelor de consum gasite mai sus:
|
||||
|
||||
1. **DDL**: `ALTER TABLE VANZARI_DETALII ADD (valoare_tva, pret_fara_tva_calculat, ...)` — coloane
|
||||
noi + migrare date istorice.
|
||||
2. **`PACK_FACTURARE`** (Oracle, `ff_2026_03_23_04_COMUN_PACK_FACTURARE.sql`):
|
||||
- `adauga_articol_factura` (spec 529, body 4972) — punctul unde se insereaza linia in
|
||||
`VANZARI_DETALII_TEMP`; ar trebui sa scrie valoarea (eventual editata de user) in loc s-o
|
||||
lase doar pe `pret`/`pret_cu_tva`/`proc_tvav`.
|
||||
- `calculeaza_sume` (14631) — daca valoarea e editabila, aceasta procedura ar trebui sa
|
||||
citeasca valoarea salvata in loc s-o recalculeze, sau sa fie ocolita.
|
||||
- `scrie_in_vanzari` (13468) si `finalizeaza_avize_lucrare` (14807) — subquery-urile care insumeaza
|
||||
`calculeaza_total_fara_tva_fact`/`calculeaza_total_tva_fact` pe toate liniile
|
||||
(13716-13786, 14965-15008) ar trebui sa insumeze coloana noua in loc s-o recalculeze.
|
||||
- `verifica_total_document` (16009) — logica de reconciliere total-vs-note-contabile ar trebui
|
||||
ajustata (vezi punct 5) daca totalul nu se mai deriva strict din formula.
|
||||
3. **Views Oracle**: `fact_vrap_fact_articole`, `fact_vrap_articole_vandute`
|
||||
(`ff_2026_07_27_01_FACTURARE.sql:10-257`), `fact_vfacturi`/`fact_vfacturi2` (vezi Subiect B #11)
|
||||
— toate ar trebui sa citeasca noua coloana in loc de `calculeaza_total_*`.
|
||||
4. **VFP**: `COMUN\programe\oproceduri_rapoarte_fact.prg:581-590` — SQL-ul de raport care apeleaza
|
||||
direct `pack_sesiune.calculeaza_pret_cu_tva`/`calculeaza_total_cu_tva`.
|
||||
5. **VFP grid formular**: `D:\ROA\ROAFACTURARE\Clase\ofundal_facturare.vc2` (coloana
|
||||
`cPret_cu_tva` + coloanele de pret/valoare din grid — nu identificate exhaustiv, cautare
|
||||
punctuala doar pe flag).
|
||||
6. **Rapoarte `.frx`** care afiseaza TVA pe linie de factura (ex. `usr_factura*.fr2` — 25 de
|
||||
fisiere gasite cu referinta la `PACK_FACTURARE`, listate la cautarea initiala; nu au fost
|
||||
deschise individual).
|
||||
|
||||
Estimare: minim **6 zone distincte** (DDL, pachet Oracle ~4 proceduri, 3-4 views, 1 program raport
|
||||
VFP, grid formular, rapoarte `.frx`) — consistent cu observatia ca e o schimbare "de volum", nu
|
||||
locala.
|
||||
|
||||
### 5. Mecanism existent de ajustare/rotunjire
|
||||
|
||||
**Da, exista, dar la nivel contabil, nu la nivel de linie facturata / vizibil userului.**
|
||||
|
||||
`PACK_FACTURARE.verifica_total_document` (linia 16009-16188+) compara totalul FTVA/TVA asteptat
|
||||
(`pack_facturare.ntotftva`, populat din VFP la `pnTotalFtva`/`Thisform.nbazaron`) cu suma reala din
|
||||
notele contabile generate (`ACT_TEMP`, grupate pe conturi `4111`/`4427`/`418`/`4428`). Daca difera
|
||||
(16082-16083: `NVL(pack_facturare.ntotftva,0) <> V_TOTFTVA_VER`), calculeaza diferenta
|
||||
`pack_facturare.ndifftva := pack_facturare.ntotftva - V_TOTFTVA_VER` (16085) si **insereaza automat
|
||||
o linie de corectie in `ACT_TEMP`** (16086-16188, INSERT cu `suma => pack_facturare.ndifftva`) —
|
||||
adica ajustarea de rotunjire se absoarbe silentios in nota contabila, nu apare ca linie editabila
|
||||
pe factura si nu se reflecta in `VANZARI_DETALII`. Aceasta e probabil exact mecanismul care face ca
|
||||
utilizatorul sa nu poata corecta manual TVA-ul afisat: rotunjirea se "repara" doar in contabilitate,
|
||||
dupa fapt.
|
||||
|
||||
---
|
||||
|
||||
## SUBIECT B — Denormalizare VANZARI, bug facturi din avize + numerar (#8)
|
||||
|
||||
### 6. Locatia `PACK_FACTURARE`
|
||||
|
||||
Nu exista fisier `.pck`/`.sql` cu acest pachet in working copy `ROAFACTURARE` (nici in `COMUN/`
|
||||
local) — **e in afara working copy-ului VFP**, in arhiva DDL/PL-SQL Oracle
|
||||
`D:\ROA\DATABASE\SCRIPTURI_CLAR\`. Cea mai recenta versiune completa (16948 linii):
|
||||
|
||||
`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\03\ff_2026_03_23_04_COMUN_PACK_FACTURARE.sql`
|
||||
|
||||
(exista si un diff mai nou, doar pe view-uri, `2026\07\ff_2026_07_27_01_FACTURARE.sql` — vezi #11).
|
||||
Istoricul complet de modificari e in `SCRIPTURI_CLAR\<an>\<luna>\ff_..._COMUN_PACK_FACTURARE.sql`
|
||||
respectiv `..._FACTURARE.sql` (zeci de fisiere, 2006-2026).
|
||||
|
||||
### 7. Procedura care scrie in VANZARI valorile denormalizate
|
||||
|
||||
**`PACK_FACTURARE.scrie_in_vanzari`** (spec linia 894, body **linia 13468-13908**) — apelata din
|
||||
`finalizeaza_factura` (linia **14740**). Face `UPDATE VANZARI SET ...` cu (13886-13898):
|
||||
|
||||
```
|
||||
total_fara_tva = lnTotalFaraTVA,
|
||||
...
|
||||
total_cu_tva = lnTotalCuTVA,
|
||||
...
|
||||
serie_incasat = lnSerieIncasat,
|
||||
nr_incasat = lnNrIncasat,
|
||||
suma_incasat = lnSumaIncasat,
|
||||
tip_incasat = lnTipIncasat
|
||||
```
|
||||
|
||||
unde `lnSerieIncasat`/`lnNrIncasat`/`lnSumaIncasat`/`lnTipIncasat` sunt populate (13727-13730) din
|
||||
variabilele **de pachet** `pack_facturare.cserie_act_incasare`, `.nnumar_act_incasare`,
|
||||
`.nsuma_incasare`, `.ntip_doc_incasare` (declarate 179-182).
|
||||
|
||||
O a doua ramura echivalenta exista in **`finalizeaza_avize_lucrare`** (spec 1011, body
|
||||
**14807-15176**), care face acelasi UPDATE (15124-15130) — pentru ramura "avize pe lucrare".
|
||||
|
||||
Variabilele de pachet `cserie_act_incasare`/`nnumar_act_incasare`/`ntip_doc_incasare`/
|
||||
`nsuma_incasare` sunt setate **doar** de **`PACK_FACTURARE.scrie_incasari`** (spec 864, body
|
||||
**13070-13139**), apelata condiționat (`IF V_LISTA_INCASARE IS NOT NULL`) din:
|
||||
- `scrie_factura2` — linia **6178** (ramura factura normala / comanda / contract)
|
||||
- `scrie_factura_avize` — linia **7008** (ramura **factura din aviz**)
|
||||
|
||||
Sunt resetate la `NULL` in `initializeaza_date_factura` (comentariu 1384-1386, cod la
|
||||
**1876-1880**) — fix explicit din 03.07.2020: *"ramaneau completate pe facturile urmatoare de la o
|
||||
factura anterioara"*.
|
||||
|
||||
`VANZARI.AVIZE` (seria/numarul avizelor facturate) e scris de
|
||||
**`scrie_corespondente_vanzari`** (spec 1057, body **15421-15457**, comentariu la linia **15445**:
|
||||
*"Completez VANZARI.AVIZE redundant, pentru a nu face mai rapid fact_vfacturi"*) — nu am confirmat
|
||||
in acest buget de cercetare din ce apeleaza `finalizeaza_factura` daca `scrie_corespondente_vanzari`
|
||||
ruleaza necondiționat pe toate tipurile sau doar pe unele (`V_TIP` e parametru) — merita verificat
|
||||
punctual daca se investigheaza bug-ul mai departe.
|
||||
|
||||
### 8. Coloanele denormalizate din VANZARI si ramura care le populeaza
|
||||
|
||||
Toate adaugate prin `2017\03\ff_2017_03_28_01_FACTURARE.sql:9-38` (comentariu fisier, linia 1-4:
|
||||
*"adaugare coloane in vanzari pentru denormalizarea join-urilor din fact_vfacturi"*):
|
||||
|
||||
| Coloana | Comentariu DB (linia 44-54 din script) | Populata de |
|
||||
|---|---|---|
|
||||
| `DISCOUNT_TVA` | TVA DISCOUNT (DISCOUNT * MAX(PROC_TVA) ARTICOLE) | `scrie_in_vanzari` |
|
||||
| `VALOARE_ACHIZITIE` | Valoare la pret achizitie articole | `scrie_in_vanzari` |
|
||||
| `TOTAL_FARA_TVA` | Total fara TVA lei | `scrie_in_vanzari` (13886), `finalizeaza_avize_lucrare` (15124) |
|
||||
| `TOTAL_TVA` | Total TVA lei | idem |
|
||||
| `TOTAL_CU_TVA` | Total cu TVA lei | `scrie_in_vanzari` (13888), `finalizeaza_avize_lucrare` (15126) |
|
||||
| `SERIE_INCASAT` | Seria chitanta/bon fiscal | `scrie_in_vanzari` (13895), din `pack_facturare.cserie_act_incasare` (setat de `scrie_incasari`) |
|
||||
| `NR_INCASAT` | Nr chitanta/bon fiscal | idem (13896) |
|
||||
| `SUMA_INCASAT` | Suma chitanta/bon fiscal | idem (13897) |
|
||||
| `TIP_INCASAT` | 11=CHITANTA, 2=BON FISCAL | idem (13898); vezi si `nTipIncasareCardBancar`=3, `nTipIncasareTichete`=5 |
|
||||
| `AVIZE` | Serie/numar avize facturate (max 1000 caractere, comentariu la linia 1490) | `scrie_corespondente_vanzari` (15421) |
|
||||
|
||||
### 9. Tipurile de factura/aviz si ramificarea codului
|
||||
|
||||
Din `VANZARI.TIP` (CASE explicit in view `fact_vfacturi2`,
|
||||
`ff_2017_03_28_01_FACTURARE.sql:89-171`) — cateva valori cheie:
|
||||
`1`=POLITICA PRETURI, `2`=CONTRACT, `3`=COMANDA, **`4`=FACT. DIN AVIZ**, `21`=AVIZ PE BAZA DE
|
||||
COMANDA, `24`=AVIZ DE RETUR, `43`=BON FISCAL MAGAZIN, etc. (lista completa la liniile citate).
|
||||
|
||||
Ramificare in VFP, `D:\ROA\ROAFACTURARE\COMUN\clase\ofacturare.vc2`, metoda
|
||||
**`do_scrie_factura`** — exista **doua copii aproape identice** ale acestei logici, in doua clase
|
||||
diferite (confirmat via `vfp_symbols.ps1 -Where`):
|
||||
- `frm_facturare_articole.do_scrie_factura` — **liniile 13981-14339**
|
||||
- `frm_facturare_articole2.do_scrie_factura` — **liniile 18004-18331**
|
||||
|
||||
Structura `DO CASE` (identica in ambele, ex. clasa 1 la liniile 14067-14174):
|
||||
- `poDate.eProforma = 1` → `{call pack_facturare.scrie_proforma(...)}` (14071)
|
||||
- `poDate.Tip = 4` → **`{call pack_facturare.scrie_factura_avize(...)}`** (14103) — "facturare din
|
||||
aviz" (comentariu 14086-14087)
|
||||
- `poDate.Tip IN (3,21,25,28,42,47)` → `{call pack_facturare.scrie_factura2(...)}` (14130) —
|
||||
"facturare pe baza de comanda / avize pe baza de comanda"
|
||||
- `Otherwise` → `{call pack_facturare.scrie_factura2(...)}` (14158) — politica de preturi/contract
|
||||
|
||||
Inainte de asta, lista de incasare `lcListaIncasare` se construieste (14012-14038) din
|
||||
`poDate.ntip_incasare` (11=chitanta, 2=bon fiscal, 3=POS/card), comun tuturor ramurilor.
|
||||
|
||||
### 10. Ramura "factura din aviz" + "achitata numerar" — ce ar trebui sa scrie si ipoteza de cauza
|
||||
|
||||
**Ce se intampla, pas cu pas** (`Tip = 4`, `ntip_incasare` in (11,2,3)):
|
||||
|
||||
1. VFP (`ofacturare.vc2:14012-14038`) construieste `lcListaIncasare` de forma
|
||||
`'11|<suma>|<id_casa>;'` (chitanta) sau `'2|...'`/`'3|...'` (bon fiscal/card) — **daca**
|
||||
`ntip_incasare` nu e in (11,2,3), `lcListaIncasare = [NULL]` (literal SQL NULL).
|
||||
2. VFP construieste `lcSql = {call pack_facturare.scrie_factura_avize(pnDiscount, serie_chit,
|
||||
nr_incasare, lcListaIncasare, id_delegat, id_masina, id_facturare, listare_detaliata,
|
||||
dataora_exp, id_agent, text_aditional, discount_evidentiat, param_aditional, ?@nid_vanzare)}`
|
||||
(14103-14115) — semnatura corespunde exact cu procedura Oracle activa
|
||||
`scrie_factura_avize` (linia **6675-7041**, care NU are `V_TOTFTVA`/`V_TOTTVA` ca prime
|
||||
argumente, spre deosebire de `scrie_factura2` — asta e corect, nu e discrepanta de parametri).
|
||||
3. In Oracle, `scrie_factura_avize` (7007-7012): `IF V_LISTA_INCASARE IS NOT NULL THEN
|
||||
pack_facturare.scrie_incasari(...)` — deci **doar daca** `lcListaIncasare` nu e literalul
|
||||
`NULL` se seteaza variabilele de pachet `cserie_act_incasare`/etc.
|
||||
4. `scrie_factura_avize` seteaza `pack_facturare.clistaid_avize := ...V_LISTAID` (7020) — lista de
|
||||
ID-uri de avize facturate, folosita ulterior de `scrie_corespondente_vanzari` pentru
|
||||
`VANZARI.AVIZE`.
|
||||
5. `scrie_factura_avize` cheama `finalizeaza_factura` (7026-7034), care cheama `scrie_in_vanzari`
|
||||
(14740) → scrie `serie_incasat`/`nr_incasat`/`suma_incasat`/`tip_incasat` din variabilele de
|
||||
pachet setate la pasul 3.
|
||||
|
||||
**La nivel de cod citit, lantul pare corect cablat** — parametrii trec prin, semnaturile se
|
||||
potrivesc, iar cele doua clase VFP (`frm_facturare_articole` si `frm_facturare_articole2`) au
|
||||
logica identica pentru aceasta ramura.
|
||||
|
||||
**IPOTEZA (cauza posibila a bug-ului, nedemonstrata prin citire de cod — necesita investigatie
|
||||
suplimentara/testare)**:
|
||||
- Daca ordinea reala de operatii difera de `ordine_operatii_facturare.txt` (ex. daca intre pasul 3
|
||||
si pasul 5 se mai apeleaza `initializeaza_date_factura` pentru alt document/lucru din aceeasi
|
||||
sesiune, inainte ca `finalizeaza_factura` sa apuce sa citeasca variabilele de pachet), fix-ul din
|
||||
03.07.2020 (linia 1876-1880, reset la NULL) ar putea sterge datele de incasare **inainte** ca
|
||||
`scrie_in_vanzari` sa le foloseasca — dar nu am gasit in codul citit un asemenea apel intercalat.
|
||||
- Alternativ: daca pe ramura aviz `scrie_corespondente_vanzari` (care scrie `VANZARI.AVIZE`) nu e
|
||||
apelata necondiționat din `finalizeaza_factura` pentru `TIP=4` (nu am verificat corpul complet al
|
||||
`finalizeaza_factura`, liniile 14723-14807, dincolo de apelul la `scrie_in_vanzari`) — merita
|
||||
verificat explicit daca acel apel exista si e neconditionat de tip.
|
||||
- Nu am verificat daca exista vreo diferenta intre `scrie_factura_avize` si
|
||||
`scrie_factura_avize_retur` (spec 667, alta ramura pentru tip retur din aviz) in privinta
|
||||
apelului `scrie_incasari`/`scrie_in_vanzari` — posibil ca bug-ul sa fie specific ramurii de retur,
|
||||
nu celei simple.
|
||||
|
||||
Recomandare pentru pasul urmator (daca se investigheaza mai departe): instrumentare/breakpoint pe
|
||||
`scrie_in_vanzari` (13468) intr-un mediu de test, pentru o factura Tip=4 platita numerar, verificand
|
||||
valorile efective ale `pack_facturare.cserie_act_incasare`/`nnumar_act_incasare`/`nsuma_incasare`/
|
||||
`ntip_doc_incasare` chiar inainte de UPDATE (13886-13898).
|
||||
|
||||
### 11. Cele doua view-uri de facturi
|
||||
|
||||
- **`fact_vfacturi`** — view-ul "original" (pe model relational, cu join-uri catre `VANZARI_DETALII`
|
||||
/ `ACT` / etc., fara denormalizare). Redefinit de multe ori de-a lungul timpului; cea mai recenta
|
||||
gasita: `D:\ROA\DATABASE\SCRIPTURI_CLAR\2023\11\ff_2023_11_17_01_COMUN.sql` (si
|
||||
`2023\02\ff_2023_02_08_01_COMUN.sql`) — nu am deschis continutul exact in acest buget.
|
||||
- **`fact_vfacturi2`** — view-ul cu totaluri **denormalizate din VANZARI** (`total_fara_tva`,
|
||||
`total_tva`, `total_cu_tva`, `serie_incasat`, `nr_incasat`, `suma_incasat`, `tip_incasat`,
|
||||
`avize` citite direct din coloanele `VANZARI`, nu recalculate din `VANZARI_DETALII`/`ACT`).
|
||||
Definit initial in **`D:\ROA\DATABASE\SCRIPTURI_CLAR\2017\03\ff_2017_03_28_01_FACTURARE.sql:59-236`**,
|
||||
cu comentariul explicit (linia 3): *"View fact_vfacturi2 - temporar, urmand a inlocui view-ul
|
||||
fact_vfacturi, dupa completarea coloanelor din vanzari pentru datele existente deja"*.
|
||||
|
||||
Separat, exista si o a doua pereche de view-uri, mai recenta si mai granulara (pe linie de
|
||||
articol, nu pe factura): **`fact_vrap_fact_articole`** / **`fact_vrap_articole_vandute`**, definite
|
||||
in `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\07\ff_2026_07_27_01_FACTURARE.sql:10-257` — acestea NU
|
||||
citesc din coloane denormalizate, ci calculeaza TVA live prin `pack_facturare.calculeaza_total_*`
|
||||
(vezi Subiect A #2). Nu sunt aceleasi cu perechea mentionata in cerinta (care pare sa se refere la
|
||||
`fact_vfacturi`/`fact_vfacturi2`), dar sunt relevante daca se cauta "view-ul cu totaluri din
|
||||
vanzari" generic.
|
||||
|
||||
---
|
||||
|
||||
## Note metodologice
|
||||
|
||||
- `PACK_SESIUNE` (motorul de rotunjire per-pret, apelat de `PACK_FACTURARE`) nu a fost gasit
|
||||
definit ca fisier separat in cautarile punctuale facute (cateva luni din 2025-2026); daca se
|
||||
continua investigatia pe rotunjire, merita o cautare dedicata `create or replace package
|
||||
pack_sesiune` in `D:\ROA\DATABASE\SCRIPTURI_CLAR`.
|
||||
- Arhiva `D:\ROA\DATABASE\SCRIPTURI_CLAR` e foarte mare (mii de fisiere, 2009-2026); cautarile de
|
||||
mai sus au fost tintite (nume de coloane/proceduri cunoscute), nu exhaustive — un `grep` complet
|
||||
pe intreg arhivul repetat timeout la 20s in unele incercari initiale.
|
||||
- Nu am gasit apeluri catre `PACK_FACTURARE` din `D:\ROA\ROAFACTURARE` propriu-zis (in afara
|
||||
`COMUN\clase\ofacturare.vc2`) — pachetul e consumat exclusiv prin acea clasa si prin
|
||||
`COMUN\programe\oproceduri_rapoarte_fact.prg`.
|
||||
164
docs/cercetare/rec_view_articole_vanzare.md
Normal file
164
docs/cercetare/rec_view_articole_vanzare.md
Normal file
@@ -0,0 +1,164 @@
|
||||
# Proiectare view Oracle pentru liniile de articole ale unei vanzari (VVANZARI_ARTICOLE)
|
||||
|
||||
Cercetare + proiectare, read-only pe baza. Decizia lui Marius (08.08.2026): view dedicat, varianta B
|
||||
din `rec_review_ancorare_s4.md` (respinsa atunci doar pentru ca insemna migrare de schema - acum
|
||||
aprobata). Inlocuieste lista de 20 coloane + join pe 4 tabele din `IncarcaArticoleFactura`
|
||||
(`COMUN\programe\ofacturare_editare.prg:201-208`).
|
||||
|
||||
## A. Precoditia VERSIUNE
|
||||
|
||||
`MARIUSM_AUTO` e la zi **pentru schema firmei (`ff_`)** pe orice ar putea atinge `VANZARI_DETALII`/
|
||||
facturare: toate scripturile `ff_` din 2015 incoace apar in `VERSIUNE`, inclusiv cele mai recente
|
||||
patru (`ff_2026_08_06_09/10/11/12`, comanda/contract, `PACK_FACTURARE`, `FACT_VFACTURI`,
|
||||
`VANZARI_BACKFILL`). Diferenta gasita fata de `SCRIPTURI_CLAR` (452 de fisiere, din care 14 `ff_`)
|
||||
e integral din 2009-2014 (dinainte de generalizarea `UpdateVersiune`) plus scripturi de diagnostic
|
||||
fara prefix de tracking (`DIAG_SPATIU_*`, ad-hoc-uri fara nume standard) - niciunul din ele nu
|
||||
atinge `VANZARI`/`VANZARI_DETALII`/facturare. Verdict: **precoditia trece**, sursa `MARIUSM_AUTO` e
|
||||
de incredere pentru acest DDL.
|
||||
|
||||
## B. Ce exista deja - niciun candidat direct refolosibil, dar un precedent util
|
||||
|
||||
Cautat in `all_views` (MARIUSM_AUTO) orice view pe `VANZARI_DETALII`/liniile unei vanzari:
|
||||
`VVANZARI_DETALII`, `VVANZARI_DETALII_TOT`, `FACT_VFACTURI_DETALII` sunt cele relevante.
|
||||
|
||||
- **`FACT_VFACTURI_DETALII`** (exportat integral) e cel mai apropiat candidat structural - selecteaza
|
||||
direct din `vanzari_detalii`, cu join-uri catre `nom_articole`/`nom_gestiuni`/`nom_valute` ca in
|
||||
proiectarea de mai jos. **Nu e reutilizabil ca atare**: `pret` si `discount_unitar` sunt
|
||||
RECALCULATE la cursul valutar curent (`ROUND(NVL(g.curs,1)*NVL(a.pret,0)/NVL(g.multiplicator,1),
|
||||
pack_sesiune.getoptiunefirma('PPRETV'))`, apel de functie PL/SQL per coloana per rand), construit
|
||||
pentru **afisare/tiparire** facturi, nu pentru editare. Pagina de editare (#6) trebuie sa lucreze
|
||||
pe valorile RAW stocate, ca la salvare (S5) sa scrie inapoi exact ce a citit - un `pret` deja
|
||||
convertit ar strica exact acest contract. Confirma insa tiparul de join corect (articol/gestiune/
|
||||
valuta) si ca genul asta de view exista deja in familia `FACT_*` pentru alt scop.
|
||||
- **`VVANZARI_DETALII`/`VVANZARI_DETALII_TOT`**: alt scop (afisare generica vanzari + utilizatori +
|
||||
politici de pret), nu au `pret_cu_tva`, `cont`, `id_valuta`, `id_gestiune`, `taxcode`, `lot` -
|
||||
nepotrivite.
|
||||
|
||||
Niciunul nu inlocuieste nevoia unui view nou - se continua cu proiectarea.
|
||||
|
||||
## C. Numele: `VVANZARI_ARTICOLE`
|
||||
|
||||
Verificat liber in `all_objects` (MARIUSM_AUTO). 17 caractere, sub limita de 30 (Oracle 10.2).
|
||||
|
||||
**Criteriul lui Marius (08.08.2026)**: numele trebuie sa pastreze prefixul familiei de vanzari,
|
||||
`vvanzari_*`, pentru ca el se uita la view-uri **alfabetic** si vrea sa vada dintr-o privire ca tine
|
||||
de vanzari. La o listare alfabetica: `VVANZARI_ARTICOLE`, `VVANZARI_DETALII`,
|
||||
`VVANZARI_DETALII_TOT`, `VVANZARI_TOT`.
|
||||
|
||||
Numele analog conventiei surorilor de pe acelasi formular (`vact_tot`, `vrul_tot`,
|
||||
`vrul_obinv_tot`) ar fi fost `vvanzari_detalii_tot`, dar e **deja ocupat** (alt view, B mai sus).
|
||||
|
||||
Numele de lucru din prima varianta a fost `VVD_TOT` (`v` + abreviere + `_tot`, aliniat cu aliasul
|
||||
VFP `tvd`), respins de Marius pe criteriul de mai sus: nu se citea ca facand parte din familie si
|
||||
cadea in alta parte a listei alfabetice.
|
||||
|
||||
## D. Coloanele - RAW, fara filtru STERS in view
|
||||
|
||||
Cele 20 de coloane consumate azi + **`id_vanzare` adaugat**: lista din
|
||||
`ofacturare_editare.prg:201-203` filtreaza pe `vd.id_vanzare` dar nu-l intoarce niciodata ca si
|
||||
coloana - omisiune reala, semnalata in misiune, acum corectata in view.
|
||||
|
||||
- **Fara conversie valutara, fara valori calculate** (spre deosebire de `FACT_VFACTURI_DETALII`) -
|
||||
editarea scrie inapoi exact ce citeste.
|
||||
- **`STERS` ramane in view ca si coloana, dar FARA filtru `WHERE sters=0` in definitie** - acelasi
|
||||
tipar ca `VACT_TOT`/`VRUL_TOT`/`VRUL_OBINV_TOT` (niciunul nu filtreaza `sters` in view, apelantul
|
||||
o face explicit). Apelantul de azi (`IncarcaArticoleFactura`) deja are `WHERE ... vd.sters = 0` -
|
||||
ramane neschimbat, doar `FROM vanzari_detalii vd ... WHERE vd.id_vanzare=... AND vd.sters=0` devine
|
||||
`FROM vvanzari_articole vd WHERE vd.id_vanzare=... AND vd.sters=0`.
|
||||
|
||||
## E. Ce s-a respins: valorile calculate `CALCULEAZA_TOTAL_FARA_TVA_FACT`/`_TVA_FACT`
|
||||
|
||||
Ambele sunt functii **in `PACK_FACTURARE`**, semnatura `(V_PRET, V_DIFERENTA, V_CURS,
|
||||
V_DISCOUNT_UNITAR, V_DISCOUNT_EVIDENTIAT, V_CANTITATE, V_PRET_CU_TVA, V_PROC_TVAV) RETURN NUMBER` -
|
||||
strict `NUMBER`, deci **apelabile din SQL** (confirmat prin `all_arguments`, fara tip PL/SQL-only).
|
||||
Exista precedent local pentru functii de pachet apelate per-rand intr-un view din aceeasi familie
|
||||
(`VRUL_TOT` cheama `PACK_SESIUNE.SUMA_RON` pe aproape fiecare coloana numerica), deci performanta nu
|
||||
e un argument respingator prin ea insasi pe un view filtrat pe `id_vanzare` (cateva linii/factura).
|
||||
|
||||
**Respins totusi, pentru acum**:
|
||||
1. `V_DISCOUNT_EVIDENTIAT` **nu e coloana pe `VANZARI_DETALII`** - e pe antet (`VANZARI.
|
||||
DISCOUNT_EVIDENTIAT`, confirmat in `all_tab_columns`). A include totalul calculat per linie ar
|
||||
cere fie un join suplimentar la `VANZARI` (umfla view-ul cu o dependenta noua doar pentru un
|
||||
parametru), fie parametrul trimis din VFP - oricum, in afara scopului Rundei 1 (doar afisare).
|
||||
2. Runda 1 (deja implementata si testata, `docs\progres.md`) **nu consuma** aceste valori - grid-ul
|
||||
e readonly, fara subtotal calculat.
|
||||
3. Nevoia reala apare la S4b/Runda 4 (`plan_06_s4_proiectare.md`, C.1-C.3) - dar acolo e vorba de
|
||||
**totalul pe DOCUMENT** (suma peste toate liniile), comparat cu `ACT`/`RUL`, nu de o coloana pe
|
||||
fiecare rand din grid. O interogare de agregare separata (sau o functie Oracle dedicata) e o
|
||||
potrivire mai buna pentru "totalul de control" decat 2 coloane calculate in view-ul de grid.
|
||||
4. **Costul amanarii e mic**: extinderea unui VIEW (`CREATE OR REPLACE`) nu e o migrare de schema in
|
||||
sensul de risc/coordonare al unei modificari de tabela - se poate adauga oricand printr-un script
|
||||
nou, fara sa afecteze datele sau consumatorii existenti ai coloanelor deja definite. Deci nu e
|
||||
nevoie sa se "prevada" acum ca sa se evite un al doilea script peste o saptamana.
|
||||
|
||||
**Recomandare pentru Runda 4**: cand S4b ajunge la implementare, se adauga fie doua coloane noi in
|
||||
`VVANZARI_ARTICOLE` (dupa un join la `VANZARI` pentru `DISCOUNT_EVIDENTIAT`), fie - preferabil - o interogare
|
||||
de agregare separata in Oracle care calculeaza direct SUMA pe document, fara sa treaca prin grid.
|
||||
Decizia ramane a lui Marius/sesiunii care implementeaza S4b, pe baza planului deja scris in
|
||||
`plan_06_s4_proiectare.md` C.1.
|
||||
|
||||
## F. Validare pe cele doua cazuri de regresie
|
||||
|
||||
Rulat direct ca `SELECT` (fara `CREATE VIEW`), corpul propus vs. interogarea de azi din
|
||||
`IncarcaArticoleFactura`, pe `MARIUSM_AUTO`:
|
||||
|
||||
- **`id_vanzare = 1050`** (`cod=1140888`, `tip=1`): **4 randuri**, valori identice rand-cu-rand intre
|
||||
interogarea veche si corpul noului view (`id_vanzare_det` 1584-1587). Coincide cu baza de regresie
|
||||
din `docs\progres.md`.
|
||||
- **`id_vanzare = 1047`** (`cod=1140885`, `tip=-12`): **2 randuri** (1578, 1579), identice intre cele
|
||||
doua interogari.
|
||||
|
||||
Ambele cazuri: zero divergenta, inclusiv pe coloanele cu `NULL` (ex. `id_gestiune`/`nume_gestiune`
|
||||
pe liniile nestocate din `id_vanzare=1047`).
|
||||
|
||||
## G. Numele coloanelor la iesire
|
||||
|
||||
Fara alias-uri noi - toate coloanele pastreaza numele lor de baza din tabelele sursa (Oracle
|
||||
foloseste automat numele coloanei cand nu exista `AS`):
|
||||
|
||||
```
|
||||
ID_VANZARE, ID_VANZARE_DET, ID_ARTICOL, CANTITATE, PRET, PRET_CU_TVA, PROC_TVAV, DISCOUNT_UNITAR,
|
||||
ID_GESTIUNE, CONT, ID_VALUTA, ID_JTVA_COLOANA, SERIE, EXPLICATIE, TAXCODE, LOT, STERS,
|
||||
DENUMIRE, CODMAT, NUME_GESTIUNE, NUME_VAL
|
||||
```
|
||||
|
||||
**Zero schimbari fata de azi** pentru `ControlSource`-urile existente ale `grdArticoleFactura`
|
||||
(`tvd.denumire`, `tvd.codmat`, etc.) - toate numele coincid cu ce se foloseste azi. Singura
|
||||
schimbare vizibila e coloana noua `tvd.id_vanzare`, disponibila dar neconsumata inca de grid.
|
||||
|
||||
## H. Numerotare script
|
||||
|
||||
`SCRIPTURI_CLAR\2026\08\` exista deja, ultimul numar folosit azi (08.08.2026, la momentul
|
||||
verificarii) e **niciunul** - nu exista fisiere `_2026_08_08_` in director (ultimele sunt din
|
||||
06.08.2026, `NN` pana la 12). **`NN=01` e liber pentru 08.08.2026**, comun tuturor prefixelor -
|
||||
de reverificat la momentul aplicarii, pentru ca sesiunea are mai multi agenti activi in paralel azi
|
||||
care ar putea consuma acelasi numar inaintea acestui script.
|
||||
|
||||
Nume final propus: **`ff_2026_08_08_01_COMUN_VVANZARI_ARTICOLE.sql`** - scriptul livrat foloseste deja acest
|
||||
nume in `exec pack_migrare.UpdateVersiune(...)`; daca sesiunea principala aloca alt `NN`, fisierul
|
||||
**si** acel apel trebuie schimbate impreuna (altfel `VERSIUNE` inregistreaza un nume care nu exista
|
||||
pe disc).
|
||||
|
||||
## I. Format script
|
||||
|
||||
`CREATE OR REPLACE VIEW` (fara `FORCE`) - verificat pe modelul cel mai recent din `SCRIPTURI_CLAR`
|
||||
(`ff_2026_08_06_11_COMUN_FACT_VFACTURI.sql`, care creeaza doua view-uri fara `FORCE`); `FORCE` nu e
|
||||
necesar oricum, toate tabelele sursa (`VANZARI_DETALII`, `NOM_ARTICOLE`, `NOM_GESTIUNI`,
|
||||
`NOM_VALUTE`) exista deja. CRLF verificat byte-safe dupa scriere (44 CRLF, 0 LF singur). Fara `;` in
|
||||
comentarii inaintea instructiunii. Se incheie cu `UpdateVersiune` + `commit`.
|
||||
|
||||
## Livrabil
|
||||
|
||||
`D:\ROA\ROAFACTURARE\docs\cercetare\ff_view_articole_vanzare.sql` - scriptul complet, **neaplicat**.
|
||||
|
||||
## Ce trebuie schimbat in VFP ca urmare (doar cand se aplica scriptul)
|
||||
|
||||
1. `COMUN\programe\ofacturare_editare.prg:201-208` (`IncarcaArticoleFactura`): inlocuieste
|
||||
`FROM vanzari_detalii vd LEFT JOIN nom_articole na ... LEFT JOIN nom_gestiuni ng ... LEFT JOIN
|
||||
nom_valute nv ...` cu `FROM vvanzari_articole vd`, pastrand neschimbat `WHERE vd.id_vanzare = ... AND
|
||||
vd.sters = 0` si toata lista de coloane din `SELECT` (numele raman identice).
|
||||
2. Fallback-ul `CREATE CURSOR tvd (...)` (`:214-216`) si duplicatul din `omodificari.vc2` (~14076,
|
||||
semnalate deja ca duplicare in `rec_review_ancorare_s4.md`) **nu se ating** de aceasta schimbare -
|
||||
raman neschimbate structural, doar sursa `SELECT`-ului se simplifica.
|
||||
3. **Opional**, daca se doreste sa se profite de coloana noua `id_vanzare`: nu e necesar niciun
|
||||
consum imediat in VFP - grid-ul nu are nevoie de ea (id-ul e deja cunoscut din parametrul functiei).
|
||||
207
docs/cercetare/rec_watchdog_vfp.md
Normal file
207
docs/cercetare/rec_watchdog_vfp.md
Normal file
@@ -0,0 +1,207 @@
|
||||
# Watchdog VFP + diagnostic blocaj "View Parameter" (test_page3_articole.prg)
|
||||
|
||||
## 1. Utilitarul: `COMUN\utile\Teste\watchdog_vfp.ps1`
|
||||
|
||||
Lanseaza `vfp9.exe -A -T "<script>"`, polleaza la ~700ms ferestrele TOP-LEVEL ale procesului
|
||||
(`EnumWindows`+`GetWindowThreadProcessId`, filtrate pe PID). Fereastra principala VFP se identifica
|
||||
prin `Process.MainWindowHandle` (.NET) - **nu** dupa numele clasei: VFP inregistreaza clase diferite
|
||||
prefixate `vfp9...` atat pentru shell-ul principal (`vfp99400000`) cat si pentru dialogurile lui
|
||||
proprii (`vfp994000002` pentru "View Parameter") - o clasificare pe clasa a lasat "View Parameter"
|
||||
nedetectat la prima incercare (corectat).
|
||||
|
||||
Pentru fiecare fereastra noua (diferita de `MainWindowHandle`): captura PNG (`PrintWindow`, fallback
|
||||
`CopyFromScreen` daca iese neagra) + dump text (titlu, clasa, si textul/clasa fiecarui control copil
|
||||
via `WM_GETTEXT`, cross-proces). Cu `-AutoDismiss`: cauta un buton copil "Cancel"/"Anulare" si
|
||||
trimite `BM_CLICK`; altfel incearca, in ordine, `WM_COMMAND IDCANCEL` -> ESCAPE ca MESAJ
|
||||
(`WM_KEYDOWN`/`WM_KEYUP`, tintit pe handle) -> `WM_CLOSE`.
|
||||
|
||||
**REGULA OBLIGATORIE, incalcata initial si corectata**: watchdog-ul NU are voie sa foloseasca INPUT
|
||||
REAL de tastatura/mouse (`keybd_event`, `SetForegroundWindow`, `SendInput`, `mouse_event`) - masina e
|
||||
PARTAJATA cu utilizatorul. Prima versiune folosea `SetForegroundWindow`+`keybd_event(ESCAPE)` ca
|
||||
fallback pentru dialogurile proprii VFP owner-drawn (fara controale copil reale) - **acest input NU
|
||||
are tinta, ajunge in orice fereastra are focus pe masina in acel moment, indiferent al cui proces
|
||||
e**. **Fapt clarificat de team-lead, dupa investigare**: in acest caz concret, ESC-ul a aterizat de
|
||||
fapt in PROPRIUL NOSTRU proces de test (eroarea "Variable 'GNAN' is not found" vazuta imediat inainte
|
||||
de "Execution was canceled by the user" e chiar semnatura testului nostru) - **nu in sesiunea lui
|
||||
Marius**, deci de fapt nu s-a intrerupt munca nimanui de data asta. Asta NU schimba regula: mecanismul
|
||||
tot nu are tinta si putea la fel de usor sa ajunga in aplicatia de productie (`roafacturare.exe`/
|
||||
`roacont.exe`) daca utilizatorul avea focus acolo in acea clipa - o formulare anterioara aici spunea
|
||||
gresit ca ar fi afectat sesiunea utilizatorului, corectata acum. **Scos complet** din cod (nu mai
|
||||
exista nicio linie `keybd_event`/`SetForegroundWindow` in `watchdog_vfp.ps1`). Consecinta: pentru
|
||||
dialogurile owner-drawn care nu raspund la mesaje tintite, `-AutoDismiss` **esueaza cinstit** (dialogul
|
||||
ramane deschis pana la `-TimeoutSec`, apoi procesul e omorat) - captura (PNG+dump text) ramane de incredere,
|
||||
dismiss-ul nu e garantat pentru acest tip de dialog. Procesul e omorat mereu la iesire (`finally`),
|
||||
nu ramane niciodata viu.
|
||||
|
||||
Validat intai pe caz banal: `COMUN\utile\Teste\watchdog_selftest.prg` (`MESSAGEBOX` cu OK/Cancel) -
|
||||
detectie, captura, dump text si auto-dismiss confirmate corecte inainte de a-l rula pe cazul real.
|
||||
|
||||
Utilizare: `powershell -ExecutionPolicy Bypass -File watchdog_vfp.ps1 -Script "<test.prg>" -AutoDismiss [-TimeoutSec 90] [-MaxDialogs 10] [-OutDir <cale>]`.
|
||||
|
||||
## 2. Dialogurile capturate pe `test_page3_articole.prg`
|
||||
|
||||
**Dialog 0** - nativ VFP, clasa `vfp994000002`, titlu **"View Parameter"**, text **"Enter the value
|
||||
for gnAn:"** (fara controale copil reale - owner-drawn). Screenshot:
|
||||
`COMUN\utile\Teste\editare_factura\watchdog_out\test_page3_articole_dialog0.png`.
|
||||
|
||||
**Dialog 1** (apare doar cand dialog 0 e inchis prin ESCAPE real, care functioneaza) - "Open" Win32
|
||||
standard, filtru "Table/DBF (*.dbf)", folder implicit `ROACONT` (working directory-ul mediului de
|
||||
test, mostenit din `test_init_env_auto.prg`). Screenshot:
|
||||
`COMUN\utile\Teste\editare_factura\watchdog_out\test_page3_articole_dialog1.png`.
|
||||
|
||||
## 3. PROGRAM()/LINENO() din log (dupa Cancel pe dialog 0)
|
||||
|
||||
Din `test_page3_articole_log.txt`, PROGRAM()=`VERIFICA_PAGECOUNT_FORM` (procedura de test) pe
|
||||
liniile din jurul apelului `loForm = Createobject([frm_modific2024], lnIdSet)`:
|
||||
|
||||
```
|
||||
EROARE 1 [VERIFICA_PAGECOUNT_FORM:165] File 'crsjtvatemp.dbf' does not exist.
|
||||
EROARE 1924 [VERIFICA_PAGECOUNT_FORM:166..173] LOFORM is not an object. (cascada, zgomot)
|
||||
```
|
||||
|
||||
## 4. Experimente de izolare (cerute de team-lead) - **INFIRMA ipoteza initiala**
|
||||
|
||||
Ipoteza initiala din aceasta sectiune ("`SQLEXEC` din `update_jtva_coloane` nu rezolva `?gnAn`") era
|
||||
o **deductie**, nu o masuratoare - team-lead a cerut-o verificata direct, corect. Trei experimente
|
||||
ieftine, in ordine:
|
||||
|
||||
**Experiment A - "chiar exista in acel moment?"** `TYPE('gnAn')`/`TRANSFORM(gnAn)` puse imediat
|
||||
**INAINTE** de apelul `update_jtva_coloane` (linia 161 curenta, nu inainte de `Createobject` cum
|
||||
fusese verificat prima data):
|
||||
|
||||
```
|
||||
EROARE 12 [VERIFICA_PAGECOUNT_FORM:161] Variable 'GNAN' is not found.
|
||||
```
|
||||
|
||||
- Eroare aparuta DOAR la primul apel al `verifica_pagecount_form` (`cod=1140888`), inainte sa se
|
||||
ajunga la `update_jtva_coloane`. **Rezultat: `gnAn` e deja invizibila INAINTE ca `update_jtva_coloane`
|
||||
sa fie apelata** - markerele `?gnAn`/`?gnLuna` din `updateserver.prg:597` nu pot fi (macar nu
|
||||
singure) cauza, contrazice ipoteza initiala.
|
||||
|
||||
**Experiment B - "se reproduce izolat, fara nimic din S4?"** Script nou,
|
||||
`COMUN\utile\Teste\editare_factura\test_repro_izolat_gnan.prg`: DOAR `test_init_env_auto` +
|
||||
`update_jtva_coloane("", "crsJtvaTemp", 6)`, fara `IncarcaCursoareModificareNota`, fara
|
||||
`frm_modific2024`, fara nimic din PAGE3. Rezultat:
|
||||
|
||||
```
|
||||
TYPE(gnAn)=N TRANSFORM(gnAn)=2026 TYPE(gnLuna)=N TRANSFORM(gnLuna)=8
|
||||
dupa update_jtva_coloane: Used(crsJtvaTemp)=.T. Reccount=120
|
||||
done
|
||||
```
|
||||
|
||||
**NU reproduce.** Zero dialog, exit curat, cursorul se creeaza corect cu 120 randuri.
|
||||
`update_jtva_coloane` singura, chemata imediat dupa initul mediului, functioneaza perfect -
|
||||
**nu e o capcana preexistenta a harness-ului si nici o vina proprie a functiei in izolare**.
|
||||
Rerulat identic dupa scoaterea input-ului real din watchdog (vezi sectiunea 1) - acelasi rezultat,
|
||||
deci reconfirmat, nu era un artefact al mecanismului de dismiss.
|
||||
|
||||
**Experiment C - oprit inainte de a-l rula.** Premisa lui ("copiaza corpul lui `update_jtva_coloane`
|
||||
cu concatenare in loc de `?param`, ca sa confirmi mecanismul") presupune ca vina e in legarea
|
||||
`SQLEXEC` a functiei - exact ce B tocmai a infirmat. Nu are sens sa continue in forma ceruta initial
|
||||
fara o noua directie.
|
||||
|
||||
## 5. Cauza CONFIRMATA prin bisectie (nu doar deductie)
|
||||
|
||||
Bisectie ceruta de team-lead: `TYPE('gnAn')`/`TRANSFORM(gnAn)` logat in **doua straturi** - (a) in
|
||||
programul principal, intre fiecare apel de nivel superior, si (b) ca **prima linie** in interiorul
|
||||
fiecarei proceduri (`verifica_vanzare_nota`, `verifica_coliziune_cod`, `verifica_pagecount_form`).
|
||||
Helper `bisect_log_gnan` (nou, la coada `test_page3_articole.prg`) - TYPE() e sigur necoditionat,
|
||||
TRANSFORM() doar daca TYPE()<>'U', ca sa nu produca o eroare noua care ar intrerupe bisectia.
|
||||
|
||||
Rezultat brut (`test_page3_articole_log.txt`):
|
||||
|
||||
```
|
||||
[BISECT] main: dupa test_init_env_auto :: TYPE(gnAn)=N gnAn=2026
|
||||
[BISECT] verifica_vanzare_nota ENTRY cod=1140888 :: TYPE(gnAn)=U gnAn=(U)
|
||||
[BISECT] main: dupa verifica_vanzare_nota #1 (cod=1140888) :: TYPE(gnAn)=N gnAn=2026
|
||||
[BISECT] verifica_vanzare_nota ENTRY cod=1140885 :: TYPE(gnAn)=U gnAn=(U)
|
||||
[BISECT] main: dupa verifica_vanzare_nota #2 (cod=1140885) :: TYPE(gnAn)=N gnAn=2026
|
||||
[BISECT] verifica_vanzare_nota ENTRY cod=1125486 :: TYPE(gnAn)=N gnAn=2026 <- diferit!
|
||||
[BISECT] main: dupa verifica_vanzare_nota #3 (cod=1125486) :: TYPE(gnAn)=N gnAn=2026
|
||||
[BISECT] verifica_coliziune_cod ENTRY :: TYPE(gnAn)=N gnAn=2026
|
||||
[BISECT] main: dupa verifica_coliziune_cod :: TYPE(gnAn)=N gnAn=2026
|
||||
[BISECT] main: inainte de verifica_pagecount_form :: TYPE(gnAn)=N gnAn=2026
|
||||
[BISECT] verifica_pagecount_form ENTRY cod=1140888 :: TYPE(gnAn)=U gnAn=(U)
|
||||
[BISECT] verifica_pagecount_form inainte de update_jtva_coloane :: TYPE(gnAn)=U gnAn=(U)
|
||||
```
|
||||
|
||||
**Tiparul e limpede si consecvent**: `gnAn` e INTOTDEAUNA valid (`N`, `2026`) in scope-ul PRINCIPAL,
|
||||
la fiecare checkpoint, fara exceptie - deci **NU e "eliberata"** (nu e `CLEAR ALL`/`CLEAR MEMORY`/
|
||||
`RELEASE ALL EXTENDED` pe undeva). E **`'U'` STRICT la intrarea in proceduri apelate cu `DO ... WITH`
|
||||
in care `gnAn`/`gnLuna` sunt trecute NEPARANTEZATE ca argumente**, si redevine valid imediat ce
|
||||
procedura respectiva se termina si controlul revine in principal. Corelatia e exacta cu sintaxa
|
||||
apelului, nu cu ce face procedura pe dinauntru:
|
||||
|
||||
- `verifica_vanzare_nota` apelul #1/#2 (`gnAn` -> `U`): call-site-urile trec `gnAn, gnLuna` DIRECT -
|
||||
`test_page3_articole.prg:34` (`DO verifica_vanzare_nota WITH 1140888, gnAn, gnLuna, ...`) si
|
||||
`test_page3_articole.prg:36` (`... WITH 1140885, gnAn, gnLuna, ...`).
|
||||
- `verifica_vanzare_nota` apelul #3 (`gnAn` ramane `N`): `test_page3_articole.prg:38` trece
|
||||
`2008, 2` LITERAL, nu `gnAn`/`gnLuna`.
|
||||
- `verifica_coliziune_cod` (`gnAn` ramane `N`): `test_page3_articole.prg:42` nu trece deloc
|
||||
`gnAn`/`gnLuna` (doar `lcLog`).
|
||||
- `verifica_pagecount_form` primul apel (`gnAn` -> `U`, **exact scenariul blocat**):
|
||||
**`test_page3_articole.prg:47`** - `DO verifica_pagecount_form WITH 1140888, gnAn, gnLuna, 'A (factura reala)', lcLog, 3, .T.`.
|
||||
|
||||
**Mecanismul**: `DO <procedura> WITH <arg1>, <arg2>, ...` (stilul vechi, folosit peste tot in acest
|
||||
script) trece variabilele de memorie **BY REFERENCE** implicit (`SET UDFPARMS` e `REFERENCE` in mod
|
||||
implicit VFP) - `LPARAMETERS tnAn, tnLuna` din procedura primitoare devin ALIAS-uri directe pe
|
||||
storage-ul lui `gnAn`/`gnLuna`, iar numele ORIGINAL devine inaccesibil (`TYPE()='U'`) **pe toata
|
||||
durata apelului**, exact cat tine executia procedurii - confirmat empiric de simetria perfecta
|
||||
"intra U, revine N" la fiecare din cele 3 perechi de apeluri afectate.
|
||||
|
||||
**Clasificare in termenii cerutii de team-lead**: e **"umbrire de scope"** (categoria 2), NU
|
||||
"eliberare" (categoria 1) - dar mecanismul exact nu e o coliziune de nume `PRIVATE`/`LOCAL` in corpul
|
||||
procedurii (team-lead avea deja dreptate: `verifica_pagecount_form` nu are `gnAn` in `LOCAL`, si nu
|
||||
exista `PRIVATE gnAn`/`LOCAL gnAn` nicaieri in `COMUN\programe`) - **umbrirea vine din SINTAXA
|
||||
APELULUI** (`DO...WITH` fara paranteze in jurul lui `gnAn`/`gnLuna`), nu din declaratiile procedurii
|
||||
apelate.
|
||||
|
||||
**Statement-ul vinovat exact, cu fisier:linie**: `COMUN\utile\Teste\editare_factura\test_page3_articole.prg:47`.
|
||||
|
||||
**IMPLICATIE IMPORTANTA**: acesta e un tipar din **SCRIPTUL DE TEST**, nu din fluxul real al
|
||||
aplicatiei - `do_editare_factura` (codul de productie) nu trece prin `verifica_pagecount_form`
|
||||
(helper propriu testului). Team-lead a confirmat verdictul: **e strict un defect de harness** -
|
||||
`updateserver.prg`, `omodificari.*` si codul S4 sunt toate nevinovate.
|
||||
|
||||
**Remediu APLICAT de team-lead** (3 linii, sub pragul lui de editare directa): argumentele
|
||||
`gnAn`/`gnLuna` sunt acum parantezate - `(gnAn)`, `(gnLuna)` - la liniile 37, 39 si 50 din
|
||||
`test_page3_articole.prg` (parantezele forteaza trecere PRIN VALOARE in loc de PRIN REFERINTA),
|
||||
cu un comentariu explicativ deasupra primei aparitii. Nerulat inca de mine (interzis explicit -
|
||||
suita o ruleaza team-lead-ul dupa eliberarea `.fxp`-ului).
|
||||
|
||||
**Remediul din rundele anterioare ale acestui raport (concatenare in loc de `?gnAn`/`?gnLuna` in
|
||||
`updateserver.prg:597`) ramane infirmat** - nu era cauza. `updateserver.prg` nu a fost si nu e atins.
|
||||
|
||||
## 6. Fisiere atinse
|
||||
|
||||
- **Nou**: `COMUN\utile\Teste\watchdog_vfp.ps1`, `COMUN\utile\Teste\watchdog_selftest.prg`,
|
||||
`COMUN\utile\Teste\editare_factura\test_repro_izolat_gnan.prg` (experimentul B, izolat).
|
||||
- **Modificat**: `COMUN\utile\Teste\editare_factura\test_page3_articole.prg`
|
||||
- linia 176 (`update_jtva_coloane(..., 6)`, ramane - fix necesar pt. indexul `id_jtva`, independent
|
||||
de defectul de mai jos);
|
||||
- `PROCEDURE bisect_log_gnan` (noua, la coada fisierului) + apeluri `DO bisect_log_gnan WITH ...`
|
||||
inserate in principal (intre apelurile de nivel superior) si ca prima linie in fiecare procedura
|
||||
- ramase in cod, sunt dovada bisectiei;
|
||||
- **liniile 37, 39, 50 - remediul APLICAT de team-lead**: `gnAn`/`gnLuna` parantezate (`(gnAn)`,
|
||||
`(gnLuna)`), forteaza trecere prin valoare in loc de prin referinta in `DO...WITH`.
|
||||
- **Neatins**: `COMUN\clase\omodificari.vc2/.vcx/.vct`, `COMUN\programe\updateserver.prg` (ambele
|
||||
infirmate ca posibila cauza, vezi sectiunea 5).
|
||||
- Artefacte de rulare (`watchdog_out\*.png/.log`, `*_log.txt`) raman pe disc ca dovada; se pot sterge
|
||||
cu `curatenie.ps1` la finalul lucrarii.
|
||||
|
||||
## 7. Stare la data acestui raport
|
||||
|
||||
**Cauza confirmata prin bisectie (sectiunea 5) si remediul APLICAT de team-lead** (paranteze la
|
||||
liniile 37/39/50). **Nerulat inca** de nimeni dupa aplicarea remediului - team-lead ruleaza suita
|
||||
separat, dupa eliberarea `.fxp`-ului (nu s-a mai relansat testul in aceasta sesiune, per interdictia
|
||||
primita). Ramane deschis, pentru cine continua:
|
||||
|
||||
1. Confirma cu o rulare ca remediul chiar elimina dialogul si `verifica_pagecount_form` trece PASS
|
||||
pe `PageCount=3`/`lAreArticoleVanzari=.T.` pentru cod=1140888.
|
||||
2. Optional: verifica daca fluxul REAL de productie (`do_editare_factura` in `ofacturare_comun.vc2`)
|
||||
are undeva acelasi tipar `DO...WITH <variabila PUBLIC>` NEPARANTEZAT inainte de un `?param` in
|
||||
SQLEXEC - team-lead a verdictuit "defect de harness", dar asta ramane neverificat exhaustiv pe
|
||||
codul de productie.
|
||||
3. Watchdog-ul (`watchdog_vfp.ps1`) ramane instrumentul de verificat orice ipoteza noua fara sa se
|
||||
agate procesul si FARA input real (regula obligatorie, sectiunea 1) - reutilizabil pentru orice
|
||||
alt blocaj similar in suita.
|
||||
55
docs/cercetare/s10_curata_versiune.sql
Normal file
55
docs/cercetare/s10_curata_versiune.sql
Normal file
@@ -0,0 +1,55 @@
|
||||
-- S10 - curatare istoric TABELA VERSIUNE, MARIUSM_AUTO/ROA_CENTRAL
|
||||
-- Scop: cele 5 scripturi ff_2026_08_06_* au fiecare mai multe inregistrari in VERSIUNE,
|
||||
-- din aplicari succesive pe masura ce au fost extinse in cursul zilei de 06.08.2026.
|
||||
-- Fara impact functional (versiune_db.txt si aplicarea DDL nu depind de numarul de randuri),
|
||||
-- doar istoric zgomotos. Pastreaza UN singur rand per script (cel cu ID_VERSIUNE maxim, adica
|
||||
-- ultima aplicare - starea finala reala a scriptului), sterge restul.
|
||||
--
|
||||
-- NU S-A RULAT. Propunere pentru aprobare - stergerea de istoric e decizie de om, iar
|
||||
-- MARIUSM_AUTO e schema de dezvoltare partajata.
|
||||
|
||||
-- 1) Verificare inainte de stergere: cate randuri per script, azi
|
||||
select script_final, count(*) as nr_inregistrari
|
||||
from versiune
|
||||
where script_final in (
|
||||
'ff_2026_08_06_02_COMUN_PACK_FACTURARE.sql',
|
||||
'ff_2026_08_06_03_COMUN_PACK_FACTURARE.sql',
|
||||
'ff_2026_08_06_04_COMUN_VANZARI_COMANDA_CONTRACT.sql',
|
||||
'ff_2026_08_06_05_COMUN_FACT_VFACTURI.sql',
|
||||
'ff_2026_08_06_06_COMUN_VANZARI_BACKFILL.sql'
|
||||
)
|
||||
group by script_final
|
||||
order by script_final;
|
||||
|
||||
-- Stare masurata 06.08.2026: _02=2, _03=1 (nimic de sters), _04=2, _05=4, _06=5 (14 randuri total,
|
||||
-- 9 de sters, ramanand 5 - unul per script).
|
||||
|
||||
-- 2) Stergere: pastreaza doar randul cu ID_VERSIUNE maxim per script (ultima aplicare)
|
||||
delete from versiune v
|
||||
where v.script_final in (
|
||||
'ff_2026_08_06_02_COMUN_PACK_FACTURARE.sql',
|
||||
'ff_2026_08_06_03_COMUN_PACK_FACTURARE.sql',
|
||||
'ff_2026_08_06_04_COMUN_VANZARI_COMANDA_CONTRACT.sql',
|
||||
'ff_2026_08_06_05_COMUN_FACT_VFACTURI.sql',
|
||||
'ff_2026_08_06_06_COMUN_VANZARI_BACKFILL.sql'
|
||||
)
|
||||
and v.id_versiune < (
|
||||
select max(v2.id_versiune)
|
||||
from versiune v2
|
||||
where v2.script_final = v.script_final
|
||||
);
|
||||
|
||||
-- 3) Verificare dupa stergere: fiecare script din lista trebuie sa aiba exact 1 rand
|
||||
select script_final, count(*) as nr_inregistrari
|
||||
from versiune
|
||||
where script_final in (
|
||||
'ff_2026_08_06_02_COMUN_PACK_FACTURARE.sql',
|
||||
'ff_2026_08_06_03_COMUN_PACK_FACTURARE.sql',
|
||||
'ff_2026_08_06_04_COMUN_VANZARI_COMANDA_CONTRACT.sql',
|
||||
'ff_2026_08_06_05_COMUN_FACT_VFACTURI.sql',
|
||||
'ff_2026_08_06_06_COMUN_VANZARI_BACKFILL.sql'
|
||||
)
|
||||
group by script_final
|
||||
order by script_final;
|
||||
|
||||
-- commit; -- de dat manual, dupa verificarea pasului 3
|
||||
296
docs/cercetare/valuta_si_curs.md
Normal file
296
docs/cercetare/valuta_si_curs.md
Normal file
@@ -0,0 +1,296 @@
|
||||
# Cercetare: `poDate.in_valuta`, `Clb_zi_curs` si cele doua concepte de valuta
|
||||
|
||||
Investigatie read-only (fara editari, fara `git_sync.ps1`/`txt2vcx.ps1`, fara commit). Sursa: cache
|
||||
text `.vc2`/`.prg` din `COMUN\clase\ofacturare.vc2`, `COMUN\programe\ofacturare.prg`,
|
||||
`COMUN\programe\ofacturare_comun.prg`, `COMUN\programe\ofacturare_stoc.prg`,
|
||||
`COMUN\programe\oproceduri_curs.prg`, plus pachetul Oracle
|
||||
`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\03\ff_2026_03_23_04_COMUN_PACK_FACTURARE.sql` (copie pe disc,
|
||||
alta numerotare decat baza). Reutilizeaza si confirma cercetari anterioare din
|
||||
`docs\cercetare\rec_cale_vanzari_detalii.md` si `docs\cercetare\rec_consumatori_vanzari.md`.
|
||||
|
||||
---
|
||||
|
||||
## 1. `poDate.in_valuta` — unde se seteaza si din ce
|
||||
|
||||
**Concluzie**: `in_valuta` e determinat **o singura data**, la `Init`, exclusiv din **tipul
|
||||
documentului** (`tnTip`) plus o lista fixa de `id_set` — niciodata din alegerea manuala a unei
|
||||
valute in antet si niciodata resetat ulterior. E o proprietate a *tipului de factura* (Invoice /
|
||||
credit note / retur valuta / factura fiscala valuta pe contract), nu a faptului ca articolele au
|
||||
preturi in valuta.
|
||||
|
||||
**Dovezi**:
|
||||
- Valoare implicita `0`: `COMUN\programe\ofacturare_comun.prg:165` (`in_valuta = 0`, in blocul de
|
||||
proprietati al obiectului `poDate`).
|
||||
- Singurul loc care il seteaza pe `1`, in `Procedure Init`:
|
||||
```
|
||||
ofacturare_comun.prg:248-250
|
||||
If Inlist(tnIdSet,223,225,226,227,271) OR INLIST(m.tnTip, 5,6,7,9,10,52)
|
||||
.in_valuta = 1
|
||||
Endif
|
||||
```
|
||||
- Legenda tipurilor (comentariu din `frm_date_factura.Init`), `ofacturare.vc2:9571-9582`:
|
||||
`1`=lista preturi, `2`=contract, `3`=comanda, `4`=aviz, `5`=Invoice lista preturi, `6`=Invoice
|
||||
contract, `7`=credit note, `8`=retur lei, `9`=retur valuta, `48`=avize valoric, `52`=Factura
|
||||
fiscala valuta contract. Tipul `10` nu are comentariu explicit in acest bloc (adaugat separat,
|
||||
v2.0.56, ca variatie de tip 1/5).
|
||||
- Nicio alta cale de scriere pe `in_valuta` in `.vc2`/`.prg` din COMUN — verificat cu grep
|
||||
(`\.in_valuta\s*=\s*[01]`) pe intreg `ofacturare.vc2` si `ofacturare.prg`: restul potrivirilor
|
||||
sunt toate **citiri** (`If poDate.in_valuta = 1 ...`), nu atribuiri. `ofacturare_stoc.prg:549`
|
||||
(`poDate.in_valuta = in_valuta`) e o cale alternativa (facturare "din stoc") care primeste
|
||||
parametrul `in_valuta` deja calculat in amonte, cu aceeasi semantica.
|
||||
- Consecinta directa, confirmata de cod: alegerea manuala a unei valute pentru document (control
|
||||
`ct_clb_valuta`) nu poate schimba `in_valuta` — controlul insusi e eliminat din formular cand
|
||||
`in_valuta = 0` (`ofacturare.vc2:9725-9728`), deci userul nu are cum sa-l "aleaga" pe o factura
|
||||
care nu e deja de tip valuta.
|
||||
|
||||
---
|
||||
|
||||
## 2. Campul „Data curs valutar" (`Clb_zi_curs`) — unde e definit si cand e eliminat azi
|
||||
|
||||
**Concluzie**: campul e eliminat **doar** pentru facturi de retur (`tip` 8 sau 9), niciodata pe
|
||||
baza de `in_valuta`. **Da, azi campul apare si pe facturile in lei** (`in_valuta=0`) de orice alt
|
||||
tip — comportament confirmat explicit prin simetrie de cod: imediat dupa blocul de retur, exista un
|
||||
bloc separat care elimina `ct_clb_valuta` cand `in_valuta=0`, dar echivalentul lipseste pentru
|
||||
`clb_zi_curs`.
|
||||
|
||||
**Dovezi** (identificarea claselor facuta cu `vfp_symbols.ps1 -Where`):
|
||||
|
||||
| Clasa | Interval | `Clb_zi_curs` definit | Eliminat azi? |
|
||||
|---|---|---|---|
|
||||
| `frm_date_aviz` | `ofacturare.vc2:6566-7618` | `:6727-6740` (caption "Data cursului valutar") | Niciodata — zero `RemoveObject`/conditionare pe `in_valuta` in toata clasa (verificat exhaustiv) |
|
||||
| `frm_date_aviz_lucrare` | `:7620-8201` | `:7787-7801` | Niciodata; in plus, validare **necondiționata**: `:8076` `Case Empty(Nvl(poDate.zi_curs,{}))` fara nicio garda pe `in_valuta` (spre deosebire de `frm_date_factura`, vezi mai jos) |
|
||||
| `frm_date_factura` | `:8482-9869` | `:8701-8714` (caption "Data curs valutar") | **Doar** pentru `tip` 8/9, in `Init`: |
|
||||
|
||||
```
|
||||
ofacturare.vc2:9717-9722 (frm_date_factura.Init)
|
||||
*!* modificare v 2.0.56
|
||||
If Inlist(poDate.tip, 8, 9)
|
||||
lnHeight = lnHeight - .clb_zi_curs.Height
|
||||
laPozitii(.clb_zi_curs.TabIndex, 2) = 1
|
||||
.RemoveObject('clb_zi_curs')
|
||||
Endif
|
||||
*!* modificare v 2.0.56 ^
|
||||
|
||||
ofacturare.vc2:9725-9728 (imediat dupa, in ACEEASI metoda)
|
||||
If poDate.in_valuta = 0
|
||||
lnHeight = lnHeight - .ct_clb_valuta.Height
|
||||
laPozitii(.ct_clb_valuta.TabIndex, 2) = 1
|
||||
.RemoveObject('ct_clb_valuta')
|
||||
```
|
||||
|
||||
Cele doua blocuri sunt adiacente si scrise dupa acelasi tipar (inaltime, `laPozitii`,
|
||||
`RemoveObject`) — dovada ca autorul original a tratat `ct_clb_valuta` (selectorul de valuta) ca
|
||||
dependent de `in_valuta`, dar **nu** a facut acelasi lucru pentru `clb_zi_curs`. Validarea de
|
||||
completare e de asemenea asimetrica: `ofacturare.vc2:9484`
|
||||
(`Case poDate.in_valuta = 1 And Empty(Nvl(poDate.zi_curs,{})) And ...`) cere data curs **doar** cand
|
||||
`in_valuta=1`, in timp ce `frm_date_aviz_lucrare` (`:8076`) o cere mereu — inconsistenta intre cele
|
||||
doua forme, de retinut daca regula noua trebuie aplicata uniform.
|
||||
|
||||
Nota: `frm_facturare_articole2` (clasa separata, `:15741-19355`) are propriul label "Curs valutar"
|
||||
(control diferit, `:16285-16299`), tratat la punctul 3/5 — nu e acelasi camp cu `Clb_zi_curs` din
|
||||
antet, dar citeste aceeasi `poDate.zi_curs`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Cele doua concepte, in cod — confirmate distinct
|
||||
|
||||
**Concluzie**: da, distinctia exista clar in cod, pe doua axe independente:
|
||||
- **(a) valuta de document**: `poDate.in_valuta` / `poDate.Curs` / `poDate.multiplicator` /
|
||||
`poDate.id_valuta` — un singur curs, al documentului intreg, folosit cand tot documentul e emis
|
||||
in valuta.
|
||||
- **(b) articol cu pret in valuta pe document in lei**: `poArticol.tip_valuta = 1` (proprietate a
|
||||
politicii de pret a articolului, independenta de `poDate.in_valuta`), cu preturile brute in
|
||||
valuta pastrate in `poArticol.pretftva_val`/`pretctva_val`/`pretd` + `id_valuta_d`, convertite in
|
||||
lei **client-side, in VFP**, folosind cursul zilei `poDate.zi_curs`.
|
||||
|
||||
**Dovezi — conversia in lei pentru cazul (b)**:
|
||||
```
|
||||
ofacturare.vc2:1989 poArticol.pretftva = Round(poArticol.pretftva_val * poArticol.Curs / poArticol.multiplicator, gnPPretV)
|
||||
ofacturare.vc2:2953 poArticol.pretftva = Round(poArticol.pretftva_val * poArticol.Curs / poArticol.multiplicator, gnPPretV)
|
||||
ofacturare.vc2:2017 poArticol.pretctva = Round(poArticol.pretctva_val * poArticol.Curs / poArticol.multiplicator, gnPPretV)
|
||||
```
|
||||
`poArticol.Curs`/`multiplicator` (nu e alt curs decat cel al zilei documentului) provin din cursorul
|
||||
`crscursuri`, incarcat explicit **cand documentul e in lei** — exact pentru articolele cu preturi in
|
||||
valuta:
|
||||
```
|
||||
ofacturare.prg:407-418 (identic si la :941-947)
|
||||
If poDate.in_valuta = 1
|
||||
Select Distinct Curs, nume_val, id_valuta, multiplicator From (lcCursor) ... Where tip_valuta = 1 ... Into Cursor crscursuri
|
||||
Select crscursuri
|
||||
poDate.Curs = Curs
|
||||
poDate.multiplicator = multiplicator
|
||||
Else
|
||||
citeste_cursuri_zi(poDate.zi_curs)
|
||||
If Reccount('crscursuri') = 0
|
||||
Use In crscursuri
|
||||
Endif
|
||||
Endif
|
||||
```
|
||||
`citeste_cursuri_zi` (`COMUN\programe\oproceduri_curs.prg:113-124`) construieste `crscursuri` cu
|
||||
**toate** cursurile valabile la `tdDataCurs` (`= poDate.zi_curs`):
|
||||
```
|
||||
oproceduri_curs.prg:117-118
|
||||
lcSql = [select nume_val,curs,id_valuta,multiplicator from ] + gcS + [.vcurs where data <= ... and data2 >= ...]
|
||||
```
|
||||
Cursorul e apoi afisat direct pe grila de selectie a articolelor, cu eticheta construita din
|
||||
`poDate.zi_curs`, **indiferent de `in_valuta`**:
|
||||
```
|
||||
ofacturare.vc2:15097-15098 si 19004-19005 (frm_facturare_articole2)
|
||||
If !Empty(Nvl(poDate.zi_curs, {}))
|
||||
Thisform.lb_cursuri.Caption = "Curs valutar " + Iif(!Inlist(poDate.tip, 8, 9), "(" + Dtoc(poDate.zi_curs) + ")", "")
|
||||
```
|
||||
Grila/eticheta apar doar daca `crscursuri` are randuri (`ofacturare.vc2:15092`/`19000`), ceea ce se
|
||||
intampla si pe un document in lei, daca exista macar un articol cu politica in valuta.
|
||||
|
||||
**`VANZARI` vs `VANZARI_DETALII` — ce se stocheaza**:
|
||||
- `VANZARI` (document): coloane proprii `CURS`/`ID_VALUTA`/`MULTIPLICATOR` — cursul/valuta **doar
|
||||
ale documentului**, populate din `poDate.Curs`/`id_valuta`/`multiplicator` (relevante cand
|
||||
`in_valuta=1`).
|
||||
- `VANZARI_DETALII` (linie): **nu are coloana `CURS`** — confirmat din `all_tab_columns`
|
||||
(`docs\cercetare\rec_cale_vanzari_detalii.md:171-175`) si din SQL-ul live al pachetului, care
|
||||
reface cursul liniei prin JOIN, nu dintr-o coloana proprie:
|
||||
```
|
||||
PACK_FACTURARE.sql:3773-3778 (identic la :4034-4036, :6393-6396, :6571-6573)
|
||||
LEFT JOIN VANZARI_CURSURI B1 ON A1.ID_VANZARE = B1.ID_VANZARE AND A1.ID_VALUTA = B1.ID_VALUTA
|
||||
```
|
||||
Linia stocheaza insa `PRET` (pretul efectiv, in lei, folosit pe factura) **si** `PRETD` +
|
||||
`ID_VALUTAD` (pretul brut in valuta si valuta originii, pastrate ca referinta/audit) — vezi
|
||||
INSERT-ul din `adauga_articol_factura`
|
||||
(`docs\cercetare\rec_cale_vanzari_detalii.md:59-65`, coloanele `PRET`, `PRETD`, `ID_VALUTAD`,
|
||||
`ID_VALUTA` alaturi). Deci **da**, se pastreaza si urma valutei originale pe linie, dar valoarea
|
||||
de facturare efectiva e mereu in lei pe `VANZARI_DETALII.PRET`.
|
||||
|
||||
**La ce se foloseste `VANZARI_CURSURI`**: e un instantaneu al cursurilor pentru **orice valuta
|
||||
straina aparuta printre liniile documentului** (nu doar valuta proprie a documentului), scris o
|
||||
singura data la emitere, indiferent de `in_valuta`:
|
||||
```
|
||||
PACK_FACTURARE.sql:14491-14501
|
||||
PROCEDURE scrie_cursuri(V_ID_VANZARE IN NUMBER) IS
|
||||
BEGIN
|
||||
INSERT INTO VANZARI_CURSURI (ID_VANZARE, ID_VALUTA, CURS, MULTIPLICATOR)
|
||||
SELECT DISTINCT V_ID_VANZARE, ID_VALUTA, CURS, MULTIPLICATOR
|
||||
FROM VANZARI_DETALII_TEMP
|
||||
WHERE ID_VALUTA <> pack_facturare.nid_moneda_nationala;
|
||||
END scrie_cursuri;
|
||||
```
|
||||
Rulat necondiționat la fiecare emitere (apelat din `scrie_in_vanzari`, vezi
|
||||
`docs\cercetare\rec_cale_vanzari_detalii.md:98-102`) — deci **si pe o factura in lei** cu articole
|
||||
in valuta se scrie cate un rand `VANZARI_CURSURI` pentru fiecare valuta straina aparuta pe linii,
|
||||
cu cursul citit la `poDate.zi_curs`. Tabela e folosita ulterior la reafisare/reprint/retur, ca sursa
|
||||
a cursului per-linie (JOIN-urile de mai sus).
|
||||
|
||||
---
|
||||
|
||||
## 4. Ce se strica daca ascundem campul cand "nu are sens"
|
||||
|
||||
**Concluzie**: nu exista risc de "factura fara curs deloc" — `poDate.zi_curs` primeste mereu o
|
||||
valoare implicita la `Init` (data documentului) si nu poate ramane `NULL`. Riscul real e altul:
|
||||
**userul pierde controlul asupra datei de curs pentru cazul (b)** — o factura in lei cu articole in
|
||||
valuta ar folosi mereu cursul zilei curente/implicite, fara sa poata fi corectat manual, exact in
|
||||
situatia in care lipseste cursul pentru acea data (eroarea Oracle 20005, punctul 5) sau cand userul
|
||||
vrea sa aliniaze cursul cu data unui document sursa (aviz, contract).
|
||||
|
||||
**Consumatori confirmati ai `poDate.zi_curs`** (grep exhaustiv `poDate\.zi_curs`, fisiere
|
||||
`ofacturare.vc2`, `ofacturare.prg`, `ofacturare_stoc.prg` — singurele 3 din COMUN):
|
||||
|
||||
| Loc | Ce primeste | Conditionat de `in_valuta`? |
|
||||
|---|---|---|
|
||||
| `ofacturare.vc2:6105-6107`, `:13994-13996`, `:18030-18032` | parametru `to_date(...)` catre `pack_facturare.initializeaza_date_factura(...)` la fiecare finalizare de antet | **Nu** — trimis pentru orice tip |
|
||||
| `ofacturare.prg:272,276,281,297,303` | primul parametru al `pack_facturare.cursor_articole_k` / `cursor_preturi` / `cursor_gestiune` / `cursor_lucrare` — SQL-ul care aduce lista de articole/preturi afisata userului | **Nu** — inclusiv pentru `tip=1` (lista de preturi, lei) |
|
||||
| `ofacturare_stoc.prg:79,335` | acelasi rol, pe calea alternativa "facturare din stoc" | Nu |
|
||||
| `ofacturare.vc2:15097-15098`, `:19004-19005` | eticheta grilei "Curs valutar (data)" din `frm_facturare_articole2` | Nu — apare oricand `crscursuri` are randuri |
|
||||
| `ofacturare.vc2:9484` | validare "Nu ati completat ziua cursului valutar" | **Da**, doar `in_valuta=1` (`frm_date_factura`) |
|
||||
| `ofacturare.vc2:8076` | aceeasi validare | **Nu**, necondiționata (`frm_date_aviz_lucrare`) |
|
||||
|
||||
**Riscul concret**: daca regula noua ascunde/dezactiveaza campul strict cand `poDate.in_valuta=0`,
|
||||
pe `frm_date_factura` (tip 1, lista de preturi) userul nu va mai putea edita `zi_curs` inainte de a
|
||||
deschide grila de articole — dar `ofacturare.prg:279-282` tot va trimite acel `poDate.zi_curs`
|
||||
(neschimbat, ramas pe data initializata la `Init`) catre `cursor_preturi`, iar daca acolo Oracle
|
||||
ridica eroarea 20005 (curs lipsa pentru acea data/valuta), fluxul de recuperare
|
||||
(`vizualizeaza_curs`, punctul 5) va porni oricum, dar userul nu va (mai) avea camp pe antet ca sa
|
||||
retina/corecteze data dupa ce inchide formularul de curs. Pe `frm_date_aviz_lucrare`, ascunderea ar
|
||||
intra chiar in conflict cu validarea necondiționata de la `:8076`, care ar bloca finalizarea
|
||||
antetului cerand un camp care nu mai exista pe ecran — de reparat impreuna, nu doar ascuns.
|
||||
|
||||
---
|
||||
|
||||
## 5. Formularul de curs valutar din antet — localizare si traseu
|
||||
|
||||
**Concluzie**: nu exista niciun buton/dblclick pe `Clb_zi_curs` care sa deschida formularul de curs
|
||||
(verificat exhaustiv — zero hit pentru `Clb_zi_curs.DblClick/RightClick/Click` sau `but_curs` in
|
||||
`ofacturare.vc2`). Formularul se deschide **automat**, ca recuperare de eroare, dupa ce antetul
|
||||
(`frm_date_factura`/`frm_date_aviz`) s-a inchis deja si codul incearca sa incarce lista de articole.
|
||||
|
||||
**Traseu**:
|
||||
1. `ofacturare.prg:228-235` — `ofrmceredate = Createobject(lcObiect, toFactura)` (`lcObiect` =
|
||||
`frm_date_factura` sau `frm_date_aviz`), `.Show()` modal; la inchidere, `Release ofrmceredate`
|
||||
(`:248`).
|
||||
2. `ofacturare.prg:266-308` — pe baza tipului, se construieste apelul catre
|
||||
`pack_facturare.cursor_preturi(?poDate.zi_curs, ...)` (sau `cursor_articole_k`/`cursor_gestiune`/
|
||||
`cursor_lucrare`) si se executa (`:311` `goExecutor.oExecute(lcSqlCursor, lcCursor)`).
|
||||
3. Daca esueaza cu eroarea Oracle **20005** (curs lipsa pentru data/valuta ceruta):
|
||||
```
|
||||
ofacturare.prg:313-317 (identic la :828-832)
|
||||
If lnSucces < 0
|
||||
AMESSAGEBOX(goExecutor.oPrelucrareEroare(), 16, "Eroare")
|
||||
If goExecutor.nEroare = 20005
|
||||
vizualizeaza_curs(poDate.zi_curs)
|
||||
ENDIF
|
||||
```
|
||||
4. `vizualizeaza_curs(tdDataCurs)` — `COMUN\programe\oproceduri_curs.prg:8-42` — construieste
|
||||
cursorul `crscurs` si deschide `loFrmCurs = Createobject("frm_curs", tdDataCurs)` /
|
||||
`.Show(1)` (modal).
|
||||
5. Clasa `frm_curs` e definita in **`COMUN\clase\onom_curs.vc2:537`** (`DEFINE CLASS frm_curs AS
|
||||
_frmbase ...`), cu formulare-satelit pentru adaugare curs: `frm_curs_nou`
|
||||
(`onom_curs.vc2:1066`) si `frm_curs_nou_multiplu` (`onom_curs.vc2:1330`, apelat de la
|
||||
`onom_curs.vc2:941`).
|
||||
6. A doua cale catre acelasi formular: `citeste_cursuri_stoc` (`oproceduri_curs.prg:45-110`, folosit
|
||||
pe fluxul de facturare din stoc) cheama `vizualizeaza_curs()` **fara parametru de data**
|
||||
(`oproceduri_curs.prg:94`) cand gaseste valute fara curs pentru ziua ceruta, in bucla `Do While
|
||||
llVerificare` (retry pana userul completeaza sau renunta).
|
||||
|
||||
Aceasta e exact situatia din `COMUN\docs\todos.txt:45` (punctul 16): "la finalizare se verifica
|
||||
cursul valutar necesar pentru politicile de preturi care se factureaza [...] la revenire din
|
||||
formularul de curs valutar, focusul revine [...] pe TIP DOCUMENT, si la iesire din serie se
|
||||
regenereaza numar act" — confirmat ca fenomenul are loc **dupa** ce antetul (`frm_date_factura`) e
|
||||
deja inchis si eliberat (`Release ofrmceredate`, pasul 1), deci orice regenerare de numar/focus
|
||||
vizibila dupa inchiderea `frm_curs` tine de ecranul/starea care ramane activa in spate (nu s-a
|
||||
localizat mai departe — cere depanare separata, in afara scopului "doar localizare" cerut aici).
|
||||
|
||||
---
|
||||
|
||||
## Cand are sens campul „Data curs valutar" — propunere de regula
|
||||
|
||||
Campul are sens de aratat/editabil pe antet exact cand exista **vreo** sansa ca cursul zilei sa fie
|
||||
folosit la facturare — nu doar cand documentul insusi e in valuta:
|
||||
|
||||
> Arata (si activeaza) `Clb_zi_curs` cand `poDate.tip` NU e retur (`!Inlist(poDate.tip,8,9)`) **SI**
|
||||
> (`poDate.in_valuta = 1` **SAU** exista macar o politica de pret in valuta accesibila tipului
|
||||
> curent de document — adica exact conditia care astazi populeaza `crscursuri` cu randuri, verificata
|
||||
> deja empiric la `ofacturare.vc2:15092`/`:19000`: `Used('crscursuri') And Reccount('crscursuri') > 0`
|
||||
> dupa incarcarea listei de articole). Pe retur (8/9) ramane ascuns ca azi.
|
||||
|
||||
Pe `frm_date_aviz`/`frm_date_aviz_lucrare`, unde `crscursuri` nu e inca disponibil la momentul
|
||||
antetului (se incarca dupa, la fel ca la factura), regula echivalenta practicabila **pe antet** (nu
|
||||
dupa) ar fi: arata campul daca tipul documentului admite politici de pret in valuta pentru
|
||||
sucursala/gestiunea curenta (verificare care azi nu exista pe antet, doar mai tarziu pe grila de
|
||||
articole) — necesita fie mutarea verificarii mai devreme, fie acceptarea ca antetul nu poate sti cu
|
||||
certitudine inainte de a incarca articolele, caz in care campul ramane vizibil implicit si doar
|
||||
eticheta/relevanta lui se clarifica ulterior (ca azi, in `frm_facturare_articole2`).
|
||||
|
||||
---
|
||||
|
||||
## Necunoscute ramase
|
||||
|
||||
- Nu s-a confirmat daca exista si alte politici (`CRM_POLITICI_PRET*`) sau reguli de sucursala care
|
||||
determina *inainte* de deschiderea grilei de articole daca vor exista linii `tip_valuta=1` — utile
|
||||
pentru a decide vizibilitatea campului chiar pe antet, nu doar reactiv dupa incarcarea articolelor.
|
||||
- Nu s-a urmarit complet "focusul sare pe tip document / regenerare numar act" (punctul 5/todos #16)
|
||||
dupa inchiderea `frm_curs` — s-a localizat doar traseul de deschidere, nu si ecranul/handler-ul
|
||||
care ramane activ in spate si cauzeaza simptomul; cere sesiune separata de depanare (posibil in
|
||||
`poGeneratorNumere`/`clb_serie_act`, cf. `ofacturare.vc2:9735-9737`, needatate aici).
|
||||
- Layout-urile `.frx` (neconvertite) care afiseaza `poDate.Curs`/`cValuta` pe hartie nu au fost
|
||||
verificate — gap deja semnalat in `rec_consumatori_vanzari.md` (risc posibil #3), ramane valabil.
|
||||
- Nu s-a confirmat empiric (interogare Oracle) daca exista azi in productie facturi `tip=1` (lei) cu
|
||||
linii `ID_VALUTA <> moneda_nationala` in `VANZARI_DETALII` — dovada ar intari direct concluzia
|
||||
punctului 3, dar cercetarea a fost strict pe cod (fara conexiuni DB, conform mandatului).
|
||||
@@ -1,40 +1,66 @@
|
||||
# Capcană: corupere diacritice la editarea `.sc2`/`.vc2` (encoding cp1252)
|
||||
# Capcană: corupere diacritice la editarea `.sc2`/`.vc2`
|
||||
|
||||
Partajat între proiectele ROA* — capcana ține de formatul `.sc2`/`.vc2` produs de FoxBin2Prg,
|
||||
nu de un proiect anume.
|
||||
|
||||
## De ce se întâmplă
|
||||
|
||||
Fișierele `.sc2`/`.vc2` sunt **cp1252** (Windows-1252), nu UTF-8 (vezi antetul: `CPID="1252"`).
|
||||
Fișierele `.sc2`/`.vc2` sunt **cp1250** (Windows-1250, Europa Centrală), nu UTF-8 — deși
|
||||
antetul FoxBin2Prg declară `CPID="1252"`. Eticheta din antet nu corespunde conținutului:
|
||||
controalele care poartă text cu diacritice au `FontCharSet = 238` (Europa de Est), adică VFP
|
||||
le randează tot ca cp1250. Diferența nu e cosmetică — un `ă` real scris prin cp1252 nu are
|
||||
echivalent în acel codepage și s-ar pierde; scris prin cp1250 (encoding-ul real al fișierului)
|
||||
e pur și simplu octetul `0xE3`, corect și stabil.
|
||||
|
||||
Dacă un editor/tool citește fișierul ca UTF-8, fiecare byte >= 0x80 (diacritic) devine invalid
|
||||
ca secvență UTF-8 și e înlocuit cu U+FFFD. Dacă tool-ul apoi **salvează tot ca UTF-8**, acel
|
||||
U+FFFD e scris ca 3 octeți (`0xEF 0xBF 0xBD`) — care, citiți înapoi ca cp1252 (cum face
|
||||
U+FFFD e scris ca 3 octeți (`0xEF 0xBF 0xBD`) — care, citiți înapoi ca cp1250 (cum face
|
||||
FoxBin2Prg la write-back), apar ca `�`. Caracterul original e **ireversibil pierdut**
|
||||
(recuperabil DOAR din altă sursă: git history, backup).
|
||||
|
||||
Codebase-ul ROA "reciclează" caractere cp1252 fără echivalent direct pentru Ș/ă/ț:
|
||||
Octeții diacriticelor din codebase-ul ROA, cu adevărul (cp1250) și aparența dacă îi decodezi
|
||||
greșit ca cp1252:
|
||||
|
||||
| Literă românească | Caracter folosit (cp1252) | Byte |
|
||||
| Literă românească | Byte | Aparență dacă decodezi ca cp1252 |
|
||||
|---|---|---|
|
||||
| ă | ã (a-tilde) | `0xE3` |
|
||||
| Ă | Ã | `0xC3` |
|
||||
| ț | þ (thorn minuscul) | `0xFE` |
|
||||
| Ș (majusculă) | ª (ordinal masculin) | `0xAA` |
|
||||
| ș (minusculă) | º (ordinal feminin) | `0xBA` |
|
||||
| î, â | î, â (native cp1252) | `0xEE`, `0xE2` |
|
||||
| ă | `0xE3` | ã (a-tilde) |
|
||||
| Ă | `0xC3` | Ã |
|
||||
| î | `0xEE` | î (identic — zonă comună cp1250/cp1252) |
|
||||
| Î | `0xCE` | Î (identic — zonă comună cp1250/cp1252) |
|
||||
| â | `0xE2` | â (identic — zonă comună cp1250/cp1252) |
|
||||
| ț | `0xFE` | þ (thorn minuscul) |
|
||||
| Ș (majusculă) | `0xAA` | ª (ordinal masculin) |
|
||||
| ș (minusculă) | `0xBA` | º (ordinal feminin) |
|
||||
|
||||
Par "gunoi"/typo într-un editor UTF-8 (`ã`, `þ`, `ª`, `º`) — **sunt corecte, nu le "corecta"
|
||||
fără să verifici encoding-ul.**
|
||||
Decodate cp1250 (encoding-ul real), aceste litere sunt diacritice românești normale — exact
|
||||
ce apare în formular când fișierul e sănătos. Par "gunoi"/typo (`ã`, `þ`, `ª`, `º`) **doar
|
||||
dacă le decodezi cu codepage-ul greșit** — nu le "corecta" fără să verifici encoding-ul real.
|
||||
|
||||
## Cum se verifică dacă un fișier `.sc2`/`.vc2` e corupt
|
||||
|
||||
Verificarea se face **pe octeți**, nu pe text decodat: un codepage single-byte (1250 sau 1252)
|
||||
mapează fiecare octet la un caracter, deci nu produce niciodată U+FFFD — o căutare de
|
||||
`[char]0xFFFD` în textul decodat nu găsește nimic nici pe un fișier stricat. Ce se caută e
|
||||
secvența de trei octeți pe care a lăsat-o tool-ul care a stricat fișierul:
|
||||
|
||||
```powershell
|
||||
$bytes = [System.IO.File]::ReadAllBytes("<path>.vc2")
|
||||
$text = [System.Text.Encoding]::GetEncoding(1252).GetString($bytes)
|
||||
$text -split "`r`n" | Select-String ([char]0xFFFD) # cauta caracterul de replacement
|
||||
$b = [System.IO.File]::ReadAllBytes("<path>.vc2")
|
||||
$n = 0
|
||||
for ($i = 0; $i -lt $b.Length - 2; $i++) {
|
||||
if ($b[$i] -eq 0xEF -and $b[$i+1] -eq 0xBF -and $b[$i+2] -eq 0xBD) { $n++; $i += 2 }
|
||||
}
|
||||
"EF BF BD (U+FFFD) x $n" # orice valoare > 0 = fisier stricat
|
||||
```
|
||||
|
||||
Dacă apare `�` (sau `$text` conține U+FFFD) — fișierul are corupere de encoding.
|
||||
Echivalentul din Bash, util și pentru censul complet înainte/după orice editare (vezi
|
||||
`reguli_lucru.md`, punctul 7):
|
||||
|
||||
```bash
|
||||
od -An -tx1 <path>.vc2 | tr ' ' '\n' | grep -v '^$' | awk '$1>"7f"' | sort | uniq -c
|
||||
```
|
||||
|
||||
Censul **crește** la stricare — un octet cp1250 devine trei octeți de U+FFFD — deci nu urmări
|
||||
doar scăderile. Decodat cp1250, `EF BF BD` apare ca `ďż˝` în caption.
|
||||
|
||||
## Cum se repară
|
||||
|
||||
@@ -46,22 +72,26 @@ Dacă apare `�` (sau `$text` conține U+FFFD) — fișierul are corupere de
|
||||
git show HEAD:<cale>.vct > /tmp/head.vct # (sau .VCT, dupa caz)
|
||||
powershell -File D:\ROA\UTIL\foxbin2prg\vcx2txt.ps1 -Source /tmp/head.vcx -CacheRoot /tmp/head_out
|
||||
```
|
||||
apoi decodează ambele fișiere (HEAD curat + curent corupt) ca cp1252 și compară linie cu
|
||||
linie (PowerShell, nu `diff`/Bash direct — nu decodează cp1252 corect la afișare, deși
|
||||
Dacă și HEAD e deja corupt (corupera a fost comisă), caută ultima versiune curată cu
|
||||
`git cat-file blob <commit>:<cale>` (din Bash, niciodată cu `>` din PowerShell 5.1 — adaugă
|
||||
BOM și re-encodează) sau `svn cat <fișier>` — ambele surse independente de encoding-ul
|
||||
curent al fișierului.
|
||||
Apoi decodează ambele fișiere (versiunea curată + curent corupt) ca cp1250 și compară linie
|
||||
cu linie (PowerShell, nu `diff`/Bash direct — nu decodează cp1250 corect la afișare, deși
|
||||
detectează totuși diferența de octeți).
|
||||
3. Reconstruiește fișierul text pornind de la versiunea HEAD (curată), reaplicând DOAR
|
||||
modificările intenționate din sesiunea curentă (nu invers — nu cârpi fișierul corupt
|
||||
caracter cu caracter).
|
||||
4. Scrie rezultatul înapoi ca bytes cp1252
|
||||
(`[System.Text.Encoding]::GetEncoding(1252).GetBytes(...)`), NU ca string UTF-8/`Out-File`
|
||||
3. Reconstruiește fișierul text pornind de la versiunea curată, reaplicând DOAR modificările
|
||||
intenționate din sesiunea curentă (nu invers — nu cârpi fișierul corupt caracter cu
|
||||
caracter).
|
||||
4. Scrie rezultatul înapoi ca bytes cp1250
|
||||
(`[System.Text.Encoding]::GetEncoding(1250).GetBytes(...)`), NU ca string UTF-8/`Out-File`
|
||||
implicit.
|
||||
5. Rulează write-back (`txt2vcx.ps1 -Force`) și verifică fidelity check = zero diferențe.
|
||||
|
||||
## Cum se evită pe viitor
|
||||
|
||||
Când editezi `.sc2`/`.vc2` cu tool-uri text (Read/Edit/Write), verifică întâi dacă fișierul
|
||||
conține diacritice (`ã`, `þ`, `ª`, `º`, `î`, `â` etc.) — dacă da, **orice rescrie completă
|
||||
trebuie făcută la nivel de octeți cu encoding cp1252 explicit**, nu presupunând UTF-8.
|
||||
conține diacritice (`ă`, `ț`, `Ș`, `ș`, `î`, `â` etc.) — dacă da, **orice rescrie completă
|
||||
trebuie făcută la nivel de octeți cu encoding cp1250 explicit**, nu presupunând UTF-8.
|
||||
Editările punctuale (Edit tool, string replace) sunt sigure DOAR dacă tool-ul păstrează restul
|
||||
fișierului byte-identic (nu rescrie tot fișierul reîncodat).
|
||||
|
||||
|
||||
@@ -32,7 +32,9 @@ fals `roundtrip text1 != text2`. Ruleaza atunci cu lista rebazata:
|
||||
`vcx2txt.ps1 -Project <pjx> -ProjectRoot <root> -CacheRoot <cache> -Types vcx,scx`
|
||||
1. Baseline: `Copy-Item <f>.vc2 <f>.vc2.pre_runda<N>.bak` (in cache; la runde succesive
|
||||
necomise, diff-ul fata de HEAD ar amesteca rundele).
|
||||
2. Editare byte-safe: PowerShell `[IO.File]::ReadAllText/WriteAllText(..., GetEncoding(1252))`;
|
||||
2. Editare byte-safe: PowerShell `[IO.File]::ReadAllText/WriteAllText(..., GetEncoding(1252))` -
|
||||
doar pentru round-trip pe octeti, NU pentru retastat literal diacritice (antetul zice
|
||||
`CPID="1252"`, dar octetii diacriticelor sunt de fapt cp1250: `conventie_encoding_cp1252.md`).
|
||||
NU tool-ul Edit/Write (UTF-8 strica diacriticele). Continut nou ASCII, TAB-uri ca in jur,
|
||||
fara reflow (format position-sensitive, proprietati alfabetizate). Valabil si pentru `.prg`.
|
||||
3. Patch review: `git diff --no-index <bak> <editat> > docs/diff_runda<N>_<subiect>.patch`
|
||||
|
||||
@@ -44,6 +44,113 @@ ruleaza intr-o tranzactie manuala unica (commit sau rollback pe amandoua).
|
||||
`do_inchide_tranzactie` in `do_sterge`); fiecare `OSCRIE_IN_FISIERE` isi gestioneaza singur
|
||||
commit-ul (vezi `tlModificare`/`llManualTransactions` in `oscrie_in_fisiere.prg:111-165`).
|
||||
|
||||
## Ramura noua: editare factura de vanzare (VANZARI/VANZARI_DETALII)
|
||||
|
||||
Cand documentul modificat prin `frm_modific2024` are un rand corespunzator in `VANZARI`, clasa
|
||||
adauga o pagina noua in pageframe-ul de rulaje, `PAGE3 "Articole factura"`
|
||||
(`COMUN\clase\omodificari.vc2:8707`), cu un grid legat pe cursorul `tvd` (structura view-ului
|
||||
`VVANZARI_ARTICOLE`). Pe orice alt document (nota fara factura) formularul ramane neschimbat -
|
||||
pageframe-ul are `PageCount = 2`, ca azi.
|
||||
|
||||
Ramura e activa doar cand `COMUN\programe\ofacturare_editare.prg` e incarcat in `SET PROCEDURE`
|
||||
(verificat prin `"OFACTURARE_EDITARE" $ Upper(Set("Procedure"))`) - inregistrat la pornire de toate
|
||||
trei aplicatiile care folosesc `frm_modific2024`: `ROAFACTURARE\Programe\roafacturare.prg:214`,
|
||||
`ROACONT\Programe\roacont.prg:212`, `ROAGEST\Programe\roagest.prg:260`.
|
||||
|
||||
### Doua puncte de intrare
|
||||
|
||||
- `frm_facturi.do_editare_factura` (`COMUN\clase\ofacturare_comun.vc2:3715-3872`), actiune noua pe
|
||||
formularul de facturi din ROAFACTURARE. Refuza editarea daca documentul e sters, e proforma, nu e
|
||||
din luna curenta (`:3742-3755`), are referinte de incasari/plati (`ReferinteDocumenteNota`,
|
||||
`:3758`) sau a fost trimis in eFactura (`EsteInEFactura`, in `ofacturare_editare.prg`, `:3764`).
|
||||
Inainte de `Createobject([frm_modific2024])` incarca `crsArticoleFactura` cu
|
||||
`IncarcaArticoleFactura` (`:3793`), ca gridul din PAGE3 sa se lege o singura data pe cursorul deja
|
||||
plin.
|
||||
- `afisjurcom.do_modifica` (`COMUN\clase\comun.vc2:2222-2567`) - acelasi punct folosit pentru orice
|
||||
nota din Registrul Jurnal, neschimbat pentru documentele care nu sunt facturi de vanzare. Inainte
|
||||
de `Createobject`, `PregatesteArticoleFacturaEditare('tact')` (`:2436`, in
|
||||
`ofacturare_editare.prg`) cauta randul din `VANZARI` corespunzator notei (pe combinatiile
|
||||
`cod/nract/serie_act/dataact` din `tact`) si, daca il gaseste, pre-incarca articolele. Pe acest
|
||||
punct de intrare nu se verifica garda eFactura si nici cea de referinte de incasari/plati - doar
|
||||
restrictia de luna curenta, deja existenta pentru orice nota (`:2265-2268`). Diferenta e
|
||||
intentionata: garda de referinte protejeaza incasarile legate prin `id_fact` la operatia care
|
||||
sterge efectiv factura; pe editarea de sume din ROAFACTURARE se aplica pentru ca stergerea si
|
||||
rescrierea notei sunt parte din acelasi pas, dar pe editarea generica de note din Registrul
|
||||
Jurnal (care poate sa nu priveasca deloc o factura) nu se aplica.
|
||||
|
||||
Daca apelantul nu a putut pregati articolele dinainte, `frm_modific2024.Show` reface legatura prin
|
||||
`IncarcaVanzareDinNota` ca fallback si decide acolo daca arata `PAGE3`
|
||||
(`omodificari.vc2:14769-14794`).
|
||||
|
||||
### Scrierea, in aceeasi tranzactie manuala
|
||||
|
||||
La `buton = 1` (Terminat), pasul nou se adauga la coada secventei existente de
|
||||
stergere+scriere+realiniere, in aceeasi tranzactie manuala unica, identic pe ambele puncte de
|
||||
intrare (`ofacturare_comun.vc2:3828-3830`, `comun.vc2:2491-2493`):
|
||||
|
||||
```
|
||||
do_deschide_tranzactie()
|
||||
oscrie_in_fisiere(2,...) -- sterge nota veche (ACT/RUL)
|
||||
oscrie_in_fisiere(0,...) -- scrie nota noua, cod nou
|
||||
pack_contafin.finalizeaza_modificare_nota(...) -- realiniaza VANZARI.COD (actualizeaza_vanzari)
|
||||
ScrieArticoleFacturaEditate(tvanz.id_vanzare) -- NOU
|
||||
do_inchide_tranzactie(...)
|
||||
```
|
||||
|
||||
Agatarea ruleaza doar cand `tvanz` e deschis cu exact un rand (documentul are factura asociata).
|
||||
`VANZARI.ID_FACT` nu e atins de niciun pas al secventei - ramane cel alocat la emiterea initiala a
|
||||
facturii, la fel ca la orice alta nota (vezi "Implicatii practice"); doar `VANZARI.COD` se
|
||||
realiniaza pe codul nou al notei. Tot ce se leaga prin `id_fact` (`ANAF_EFACTURA`, `DOCUMENTE`,
|
||||
`ACT.id_factd`/`id_factc`) sau prin `ID_VANZARE` supravietuieste neschimbat unei reeditari; doar ce
|
||||
ar tine minte `cod`-ul vechi il pierde.
|
||||
|
||||
`pack_facturare.actualizeaza_vanzari`, apelata din `finalizeaza_modificare_nota`, face
|
||||
`UPDATE VANZARI_DETALII SET STERS = 0` pe tot documentul, fara nicio garda pe linie - reseteaza si
|
||||
liniile sterse la o editare anterioara. De aceea `ScrieArticoleFacturaEditate` ruleaza DUPA
|
||||
`finalizeaza_modificare_nota`, nu inauntrul ei: scrierea liniilor trebuie sa vina dupa acest reset,
|
||||
ca sa-l corecteze, nu inaintea lui.
|
||||
|
||||
`ScrieArticoleFacturaEditate` (`COMUN\programe\ofacturare_editare.prg:468-555`):
|
||||
1. marcheaza `sters = 1` pe tot documentul din `VANZARI_DETALII` (`:489-491`);
|
||||
2. invie si actualizeaza liniile pastrate din `tvd` (cantitate, pret, pret_cu_tva - `pret_achizitie`
|
||||
se omite, ramane valoarea persistata) (`:497-506`);
|
||||
3. insereaza liniile noi (`id_vanzare_det = 0`), fara `id_vanzare_det` explicit - il genereaza
|
||||
trigger-ul `TRG_VANZARI_DET_BEFOINS` (`:518-535`);
|
||||
4. apeleaza `pack_facturare.recalculeaza_totaluri_vanzari(id_vanzare, discount)` (`:544-548`), cu
|
||||
discountul luat din `tvanz.discount`; `NULL` pastreaza valoarea curenta din `VANZARI` in loc s-o
|
||||
zeroeze.
|
||||
|
||||
O linie stearsa de utilizator la o editare ramane stearsa si dupa reeditari ulterioare - fiecare
|
||||
salvare reface pasii 1-2 pe starea curenta din `tvd`, deci resetul din `actualizeaza_vanzari` e
|
||||
mereu corectat inainte sa ajunga vizibil.
|
||||
|
||||
`pack_facturare.recalculeaza_totaluri_vanzari` (script de migrare
|
||||
`ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql`) recalculeaza din `VANZARI_DETALII` - liniile proprii
|
||||
si, separat, liniile din seturi agregate din `VANZARI_SETURI` - coloanele `discount`,
|
||||
`discount_tva`, `valoare_achizitie`, `total_fara_tva`, `total_tva`, `total_cu_tva`, `valval`,
|
||||
`tvaval`, `totval`, `id_valuta`, `curs`, `multiplicator`, cu acelasi calcul per-linie ca la emitere
|
||||
(`calculeaza_total_fara_tva_fact`/`calculeaza_total_tva_fact`). Coloanele de incasare
|
||||
(`serie_incasat`/`nr_incasat`/`suma_incasat`/`tip_incasat`) nu sunt atinse.
|
||||
|
||||
View-ul `VVANZARI_ARTICOLE` (script de migrare `ff_2026_08_09_02_COMUN_VVANZARI_ARTICOLE.sql`)
|
||||
expune direct `VANZARI_DETALII`, cu `id_vanzare_set` si `pret_achizitie` printre coloanele proprii
|
||||
ale tabelei, plus denumire/codmat/nume_gestiune/nume_val din join-uri - sursa cursorului `tvd`.
|
||||
|
||||
### Editarea in grid
|
||||
|
||||
Gridul `pgfArticole.PAGE3.grdArticoleFactura` (`omodificari.vc2:12320-...`) e legat pe `tvd`. Linia
|
||||
provenita dintr-un set (`id_vanzare_set <> 0`) e needitabila (cantitate, pret, `pret_cu_tva`) si
|
||||
apare intr-o culoare distincta in grid (`DynamicForeColor`); coloana `pret_achizitie` e editabila
|
||||
doar pe liniile noi (`id_vanzare_set = 0 AND id_vanzare_det = 0`), doar-afisare pe restul
|
||||
(`:16526-16552`). Stergerea unei linii e logica - `cmdStergeArticol.Click` comuta `sters` pe randul
|
||||
curent din `tvd`, ramane vizibila grizata pana la salvare (`:16508-16517`). Adaugarea reutilizeaza
|
||||
dialogul `frm_articol_factura` de la compunerea facturii (`:16497-16505`).
|
||||
|
||||
Inainte de salvare (`inainte_de_do_termin`, `:14329-14386`, activa doar cand
|
||||
`ofacturare_editare.prg` e incarcat si `tvd` exista), pentru fiecare linie activa din `tvd`: opreste
|
||||
salvarea daca are cantitate <= 0, pret nul sau nu are articol asociat; cere confirmare daca o linie
|
||||
noua are pretul de achizitie 0, respectiv daca toate liniile facturii au fost sterse.
|
||||
|
||||
## Implicatii practice
|
||||
|
||||
- Codul (`cod`) unui document din Registrul Jurnal NU e stabil dupa modificare/anulare - se schimba
|
||||
@@ -51,3 +158,8 @@ ruleaza intr-o tranzactie manuala unica (commit sau rollback pe amandoua).
|
||||
- Regasirea documentului editat se face dupa `id_fact` (sau alt marcaj stabil), niciodata dupa `cod`.
|
||||
- Randurile `sters = 1` ramase in `ACT`/`RUL`/`RUL_OBINV` dupa o modificare sunt normale (istoric),
|
||||
nu date corupte sau duplicate de curatat.
|
||||
- Editarea unei facturi de vanzare (document cu rand in `VANZARI`) reface si liniile si totalurile
|
||||
denormalizate din `VANZARI`/`VANZARI_DETALII`, in aceeasi tranzactie cu nota - vezi ramura de mai
|
||||
sus.
|
||||
- Documentele care nu sunt facturi de vanzare nu sunt atinse de aceasta ramura - pagina de articole
|
||||
nu apare si scrierea in `VANZARI_DETALII` nu se declanseaza (garda: `Reccount('tvanz') = 1`).
|
||||
|
||||
@@ -27,9 +27,22 @@ Nu trimite SQL prin pipe din PowerShell (`'select...' | sqlplus`) — BOM-ul UTF
|
||||
|
||||
## Comanda de export (re-generare .pck)
|
||||
|
||||
> **`linesize` prea mic corupe tăcut sursa.** SQL\*Plus face **wrap** la coloana `linesize`, deci o
|
||||
> linie de cod mai lungă e ruptă în două — inclusiv **prin mijlocul unui identificator**. Fișierul
|
||||
> pare întreg, `diff` față de el nu arată nimic, dar recompilarea lui dă erori la linii care n-au
|
||||
> nicio legătură cu ce ai modificat (`ORA-00920`, `PLS-*`). `PACK_FACTURARE` are linii de peste
|
||||
> **1000** de caractere, iar un export la `linesize 400` i-a rupt 8. Folosește `linesize 32767` și
|
||||
> verifică după export că nicio linie nu atinge exact valoarea `linesize`.
|
||||
>
|
||||
> **Exportul e bun pentru CITIT, nu ca bază pentru un script de migrare.** Pentru un script de
|
||||
> pachet, pleacă de la ultimul script aplicat din `SCRIPTURI_CLAR` (fișier real, care n-a trecut
|
||||
> niciodată printr-un spool) și confirmă că e la zi comparându-l cu exportul **ignorând spațiul
|
||||
> alb** — așa wrap-ul nu mai contează:
|
||||
> `tr -d ' \t\r\n' < fisier | md5sum` pe ambele.
|
||||
|
||||
```sql
|
||||
-- export_pack.sql
|
||||
set pagesize 0 linesize 400 trimspool on feedback off heading off termout off
|
||||
set pagesize 0 linesize 32767 long 2000000 longchunksize 32767 trimspool on feedback off heading off termout off
|
||||
spool D:\ROA\ROAGEST\COMUN\docs\PACK_CONTAFIN.pck
|
||||
select text from all_source where owner='MARIUSM_AUTO' and name='PACK_CONTAFIN' and type='PACKAGE' order by line;
|
||||
select '/' from dual;
|
||||
|
||||
@@ -43,9 +43,16 @@
|
||||
propune COMPLETAREA unei functii/tabele/view comune, nu o varianta paralela. 80/20: solutia cea
|
||||
mai simpla care rezolva cazul real. Completeaza `inventar-comun.md` la orice descoperire/creare
|
||||
de element comun nedocumentat.
|
||||
**Cod nou: clase in `.prg`, nu metode in `.vcx`/`.scx`.** Logica se incapsuleaza intr-o clasa
|
||||
(`Define Class ... As Custom`) dintr-un `.prg`; formularul sau clasa vizuala doar instantiaza
|
||||
obiectul (`Createobject`) si il apeleaza, metoda din binar ramanand un apel de o linie. Sablon:
|
||||
`COMUN\programe\anaf_efactura.prg` (`AnafeFacturaServer`, `ANAFeFactura`), apelat din
|
||||
`clase\anaf_efactura.vcx`. Motivul e de intretinere: un `.prg` se editeaza direct si diff-ul lui
|
||||
se citeste in git, pe cand codul pus ca metoda in binar trece prin FoxBin2Prg si write-back si
|
||||
se urmareste greu.
|
||||
4. Testare headless (fara IDE): `vfp9.exe -A -T "<script.prg>" <param>` din PowerShell.
|
||||
Mediu, sabloane, capcane, depanare: `depanare_testare_vfp.md`.
|
||||
5. Editare .vcx/.scx pe text (cache, cp1252 byte-safe, fidelity-check): `flux-editare-vfp-text.md`.
|
||||
5. Editare .vcx/.scx pe text (cache, cp1250 byte-safe, fidelity-check): `flux-editare-vfp-text.md`.
|
||||
Cautare in binare: `cautare_vcx_vct.md`. Versiunile `.??2` sunt instantanee, pot fi mai vechi
|
||||
decat binarul — verifica mtime inainte de concluzii; continutul real e in `.PJX`.
|
||||
6. Delegare: implementarile, testele, verificarile si cercetarile se dau unor subagenti Sonnet in
|
||||
@@ -68,17 +75,30 @@
|
||||
- **spune-i ca poate infirma briefingul.** Altfel implementeaza corectia ceruta chiar cand a
|
||||
descoperit ca nu e nevoie de ea.
|
||||
7. Conventii obligatorii, de citit cand atingi zona respectiva:
|
||||
- editezi fisiere cu octeti cp1252 - `.vc2`/`.sc2`, dar si `.prg` (ex. `programe\ocautare.prg`,
|
||||
unde `s`-urile cu virgula din `masina`/`si contul`/`transa` sunt octetul `0xBA`) ->
|
||||
`conventie_encoding_cp1252.md`. Orice write cu un tool ce nu scrie cp1252 nativ (Edit/Write)
|
||||
corupe caracterele >= 0x80 din TOT fisierul (`0xBA` -> `EF BF BD`), chiar la o singura linie
|
||||
schimbata, si o repeta la FIECARE scriere — editeaza tot ce ai de editat, verifica byte-level
|
||||
si repara o singura data DUPA ultima scriere. Verificare: `perl -ne 'print "$.\n" if
|
||||
- editezi fisiere `.vc2`/`.sc2`, dar si `.prg` (ex. `programe\ocautare.prg`, unde `s`-urile cu
|
||||
virgula din `masina`/`si contul`/`transa` sunt octetul `0xBA`) -> `conventie_encoding_cp1252.md`.
|
||||
Antetul FoxBin2Prg declara `CPID="1252"`, dar octetii diacriticelor sunt de fapt **cp1250**
|
||||
(`0xE3`=a cu caciula, `0xFE`=t cu virgula, `0xAA`=S majuscul cu virgula, `0xBA`=s minuscul cu
|
||||
virgula) - nu trata eticheta din antet ca adevarul despre encoding. Orice write cu un tool ce
|
||||
nu pastreaza octetii nativ (Edit/Write) corupe caracterele >= 0x80 din TOT fisierul (`0xBA` ->
|
||||
`EF BF BD`), chiar la o singura linie schimbata, si o repeta la FIECARE scriere - **nu
|
||||
re-encoda niciodata fisierul: lucreaza pe octeti (`byte[]`) sau scrie strict ASCII in
|
||||
continutul nou**; editeaza tot ce ai de editat, verifica byte-level si repara o singura data
|
||||
DUPA ultima scriere.
|
||||
Cens obligatoriu **inainte SI dupa** fiecare editare, care trebuie sa ramana identic:
|
||||
`od -An -tx1 <fisier> | tr ' ' '\n' | grep -v '^$' | awk '$1>"7f"' | sort | uniq -c`. Atentie
|
||||
la sens: la stricare censul **CRESTE**, nu scade - un octet cp1250 devine 3 octeti `EF BF BD`
|
||||
(U+FFFD) la o re-encodare UTF-8 gresita; cauta explicit secventa `EF BF BD`, nu doar tiparul
|
||||
generic de mojibake `C3`/`C4`/`C5`/`C8` + octet de continuare (acela da fals-pozitiv pe text
|
||||
UTF-8 legitim sau pe comentarii intr-o alta limba, deja intalnite in cod). Verificare veche
|
||||
(orice octet >=0x80, utila cand cauti pozitia exacta): `perl -ne 'print "$.\n" if
|
||||
/[\x80-\xFF]/' <fisier>`; reparare (pt. `0xBA`): `perl -e
|
||||
'binmode(STDIN);binmode(STDOUT);local $/;$_=<STDIN>;s/\xEF\xBF\xBD/\xBA/g;print' < f > f.tmp`.
|
||||
`EF BF BD` nu spune ce caracter s-a pierdut - toate diacriticele devin la fel; ia octetul corect
|
||||
per pozitie din `svn cat <fisier>` (necorupt) inainte sa inlocuiesti - o inlocuire oarba cu
|
||||
`0xBA` strica `a`/`t` cu caciula (`0xE3`, `0xFE`) din alte fisiere;
|
||||
per pozitie din `svn cat <fisier>` (necorupt) sau din git history (`git cat-file blob
|
||||
<commit>:<cale>`, **din Bash, niciodata cu `>` din PowerShell 5.1** - adauga BOM si
|
||||
re-encodeaza) inainte sa inlocuiesti - o inlocuire oarba cu `0xBA` strica `a`/`t` cu caciula
|
||||
(`0xE3`, `0xFE`) din alte fisiere;
|
||||
- adaugi/rearanjezi controale pe formulare sau coloane in grid -> `conventie_ux_formulare.md`;
|
||||
- grid needitabil pe formular cu 2+ grid-uri -> `capcana_grid_controlsource.md`;
|
||||
- adaugi o coloana intr-un grid deja livrat (ordinea din clasa e neutralizata de
|
||||
|
||||
@@ -22,7 +22,11 @@ Reguli confirmate de Marius (24.07.2026):
|
||||
arata spre comentariu. Pus la sfarsit de fraza, `.` in loc de `;`. Comentariile de **dinaintea**
|
||||
instructiunii nu sunt afectate.
|
||||
- **Fiecare script se incheie cu `exec pack_migrare.UpdateVersiune('<nume_script>');` urmat de
|
||||
`commit;`.** DDL-ul comite implicit, dar `UpdateVersiune` e DML: fara `commit` explicit, un script
|
||||
`commit;`.** Numele se da **fara extensia `.sql`** — o adauga `UpdateVersiune`; trecuta si in
|
||||
argument, ajunge in `VERSIUNE` ca `..._.sql.sql` si scriptul apare ca neaplicat la comparatia de
|
||||
mai jos. Toate scriptele din `SCRIPTURI_CLAR` o omit, iar in `VERSIUNE` nu exista niciun rand cu
|
||||
dubla extensie.
|
||||
DDL-ul comite implicit, dar `UpdateVersiune` e DML: fara `commit` explicit, un script
|
||||
se poate aplica fara ca versiunea sa se inregistreze, iar `VERSIUNE` ajunge sa minta despre ce s-a
|
||||
aplicat.
|
||||
- **Rularea manuala se face conectat pe schema tinta** (`CONTAFIN_ORACLE` pentru `co_`, schema firmei
|
||||
|
||||
@@ -36,7 +36,7 @@ Ideea este sa se poata face configurarea mult mai repede, eficient, dintr-un sin
|
||||
|
||||
13. ROAFACTURARE - FORMULARUL DE FACTURARE SA INCLUDA SI FORMULARUL DATE_FACTURA/DATE_AVIZ ETC. SI SA NU MAI INCARCE DE PE SERVER TOATE ARTICOLELE DIN TOATE POLITICELE DE PRETURI - INTRUCAT ESTE POSIBIL SA FIE SI MII DE ARTICOLE SI DUREAZA MULT SA LE ADUCA DE PE SERVER. AM INCEPUT DEJA MAI DEMULT UN FORMULAR UNIFICAT frm_facturare_articole2, DAR ERA MULT DE INTEGRAT DIN FORMULARUL VECHI.
|
||||
FRONTEND SIMILAR PE CARE IL DORESC ESTE IN ROAACPRO > FACTURA, SAU IN IMPORTUL DE EFACTURA, IN CARE AM INTEGRAT DATELE FACTURI, DOAR CA FACTURAREA DIN ROAFACTURARE ESTE MAI COMPLEXA - ESTE FOLOSITA SI PENTRU POLITICI DE PRETURI SI PENTRU CONTRACT/COMANDA/AVIZ/RETUR ETC.
|
||||
INTERESUL MEU ESTE SA SIMPLIFIC INTERFATA, SA FIE MAI EFICIENTA, SA NU FIE 2 FORMULARE PENTRU FACTURA, SA FAC MAI RAPID.
|
||||
INTERESUL MEU ESTE SA SIMPLIFIC INTERFATA, SA FIE MAI EFICIENTA, SA NU FIE 2 FORMULARE PENTRU FACTURA, SA FAC MAI RAPID. in plus vreau si editare factura/aviz in toate variantele (politici preturi, comanda, contract, aviz etc) prin regenerare, adica sa arate formularul completat ca si cum ar fi inainte de salvarea initiala, ca sa pot sa fac editari ca si cum as fi la introducerea initiala - bineinteles cu stergerea facturii initiale (sters = 1) si salvarea celei noi, pentru a se vedea ce s-a modificat
|
||||
|
||||
14. ROACONT - efactura se salveaza xml detaliat si xml zip efactura si ocupa mult spatiu. vreau sa stiu cand nu mai este nevoie de xml detaliat si daca se poate curata tabelul ca sa nu mai ocupe spatiu, cel putin pentru facturi mai vechi. este important zip pentru ca este factura originala anaf.
|
||||
|
||||
@@ -48,4 +48,8 @@ INTERESUL MEU ESTE SA SIMPLIFIC INTERFATA, SA FIE MAI EFICIENTA, SA NU FIE 2 FOR
|
||||
|
||||
18. ROAFACTURARE - EMITERE FACTURA DIN PROFORMA
|
||||
|
||||
19. ROAFACTURARE - EDITARE PROFORMA
|
||||
19. ROAFACTURARE - EDITARE PROFORMA
|
||||
|
||||
20. ROAFACTURARE - FACTURA DIN COMANDA, COMANDA SE CONSIDERA FACTURATA DACA EXISTA FACTURI CU ACELEASI CANTITATI. AS VREA SA PUN MARKER DE FACTURAT PE COMANDA, CA SA NU MAI CAUT FACTURI - ESTE COSTISITOR. DOAR CA TREBUIE SA FII ATENT CA O COMANDA SE POATE FACTURA TOTAL/PARTIAL, SAU DIN MAI MULTE FACTURI, SAU SE POATE INCHIDE FORTAT. ACUM INCHIDEREA FORTATA ADAUGA CANTITATI NEGATIVE PE COMANDA, CA SA FACA CANTITATILE NEFACTURATE ZERO.
|
||||
|
||||
21. ROACONT - in formularul de verificare coduri fiscale, ar trebui sa fie si perioadele de tva, tva incasare, inactivitate, la fel ca in istoric, pentru ca utilizatorul sa nu mai caute altundeva. de asemenea, in messagebox pentru verificarea automata pe ANAF individuala ar trebui sa fie toate aceste informatii. sa se poata edita partenerul din formularul de verificare coduri fiscale prin dublu-click pe numele/codul fiscal al partenerului, similar cu formularul saft.
|
||||
Reference in New Issue
Block a user