Folderul docs\ era pana acum in afara oricarui control de versiuni - nici git, nici SVN - desi contine planurile pe puncte, proiectarile si rapoartele de cercetare pe care se sprijina modificarile din cod. O stergere acolo era definitiva. Fisierele intermediare (handoff-uri intre sesiuni, diff-uri deja aplicate) au fost sterse inainte, nu versionate: ce era durabil in ele a intrat in antetele fisierelor de test la care se refereau. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SN8snvkk94KuhWwoXUUey3
616 lines
38 KiB
Markdown
616 lines
38 KiB
Markdown
# Cercetare — proiectare S4 punctul 2: decuplarea registrului de cantitate ramasa de `crsarticole`
|
|
|
|
Investigatie READ-ONLY pentru **punctul 2** al povestii S4 din
|
|
`docs\plan_13_unificare_formular_facturare.md:2099-2113`. Continua raportul punctului 1
|
|
(`docs\cercetare\s4_cautare_articole_server.md`) si corectiile din `docs\cercetare\s4_puncte_deschise.md`.
|
|
Fara editari de cod, fara `git_sync.ps1`/`txt2vcx.ps1`, fara commit, fara scriere pe Oracle (numai
|
|
`SELECT`). Nu ating `COMUN\clase\ofacturare_comun.vc2` si `COMUN\programe\ofacturare_editare.prg`
|
|
(perimetrul altei sarcini). **Decizia 39 (S4 ramane integrala, se desface registrul) nu e
|
|
reargumentata** — proiectez CUM, nu DACA.
|
|
|
|
Sursa VFP: `COMUN\clase\ofacturare.vc2` (`frm_facturare_articole`; `frm_facturare_articole2`/prototip
|
|
citit doar pentru confirmare, are aceeasi structura). Sursa Oracle:
|
|
`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` (corpul
|
|
pachetului, `versiune_db.txt` = `2026_08_09_02`).
|
|
|
|
## Verdict (rezumat)
|
|
|
|
**Descoperirea centrala: `crsarticole.cantitate` are azi DOUA roluri distincte, nu unul, si numai
|
|
unul din ele e "registrul de cantitate ramasa" pe care planul il numeste riscant.**
|
|
|
|
- **Rol A — cantitate ramasa de facturat dintr-un document sursa** (doar comanda: tip
|
|
`3,21,25,28,42,47`; si avize: tip `4`). Cursoarele Oracle (`cursor_comanda`, `cursor_avize`) o
|
|
**calculeaza deja live** la deschidere ca `cantitate_document - deja_facturat`. `do_scrie_factura`
|
|
o re-suma din `crsarticole` (`Calculate Sum`) **doar ca sa decida ce sa trimita mai departe**
|
|
(`pnParametruAditional`) — dar Oracle **recalculeaza acelasi lucru independent, din tabele reale**,
|
|
in `inchide_comanda()` si `marcheaza_facturat()`, chiar in procedura care scrie factura. Cursorul
|
|
VFP e o **copie redundanta a unui calcul pe care Oracle il repeta oricum la scriere** — exact
|
|
problema "sursa unica" semnalata in misiune, si exact motivul pentru care decuplarea e posibila
|
|
fara sa piarda paritatea: mutam citirea "cat a mai ramas" din cursorul local intr-un apel Oracle
|
|
facut la momentul potrivit, nu intr-o a doua copie tinuta manual.
|
|
- **Rol B — plafon de cantitate in sesiune** (lista de preturi gestionabila `1,22,29,2`-jumatate;
|
|
transfer `23,41`; retur `8,9,24`). Decrementat/incrementat in `do_adauga_articol`/`do_modifica`/
|
|
`do_sterge` ca sa nu lase operatorul sa adauge mai mult decat vede pe ecran, in aceeasi sesiune.
|
|
**Nu alimenteaza nicio decizie Oracle** — nu apare in niciun `Calculate Sum` in afara celor doua
|
|
locuri de la Rolul A, si Oracle nu-l citeste niciodata direct. E un plafon UI, nu un registru de
|
|
business.
|
|
- **Corectie fata de raportul punctului 1**: pasul 5 de acolo (`s4_cautare_articole_server.md:417-424`)
|
|
propune oprirea incarcarii in masa pe tipurile `23,41` (printre altele), presupunand ca ele n-au
|
|
bookkeeping de pastrat — dar **au Rol B** (confirmat pe cod, sectiunea 1 de mai jos). Punctul 1 nu
|
|
poate fi aplicat pe `23,41` fara ca punctul 2 sa acopere intai Rolul B pe aceste doua tipuri.
|
|
- **Recomandare de proiectare** (detaliata la sectiunea 4): pentru Rolul A, inlocuieste
|
|
`Select crsarticole / Calculate Sum(cantitate)` cu un apel Oracle nou, la acelasi moment din
|
|
`do_scrie_factura` (dupa `do_scrie_articole()`, cand `VANZARI_DETALII_TEMP` e deja populat), care
|
|
reface exact interogarea pe care `inchide_comanda`/`marcheaza_facturat` o repeta oricum. Pentru
|
|
Rolul B, plafonul devine un apel Oracle la cerere (aceeasi interogare care alimenteaza azi
|
|
`cursor_preturi`/`cursor_comanda`/`cursor_gestiune`, filtrata pe articol), nu o valoare tinuta in
|
|
memorie — nu mai e nevoie de sincronizare manuala pentru ca nu mai exista o a doua copie.
|
|
|
|
---
|
|
|
|
## 1. Inventar exact al scrierilor/citirilor de bookkeeping
|
|
|
|
Verificat direct pe `COMUN\clase\ofacturare.vc2` (nu pe `.bak`), linii confirmate cu `vfp_symbols.ps1`.
|
|
|
|
### 1.1 `do_adauga_articol` — scrierea la adaugarea unei linii (`:12813-13086`)
|
|
|
|
Dupa ce linia e scrisa in `crsfactura`, la `:13034-13051`:
|
|
|
|
```
|
|
Select (lcCursor)
|
|
lnRecno = Recno()
|
|
Do Case
|
|
Case gnScadereStoc = 1 And poDate.tip = 41 && aviz retur transfer catre subunitati lista pret
|
|
Replace cantitate With cantitate + poArticol.cantitate For id_c = poArticol.id_c
|
|
Go lnRecno
|
|
Case (gnScadereStoc = 1 And poDate.tip = 23) Or Inlist(poDate.tip, 3, 4, 21, 25, 28, 42, 47)
|
|
Replace cantitate With cantitate - poArticol.cantitate For id_c = poArticol.id_c
|
|
Go lnRecno
|
|
Case Inlist(poDate.tip, 8, 9, 24) && factura retur lei si valuta, aviz retur
|
|
Replace cantitate With cantitate + poArticol.cantitate For id_c = poArticol.id_c
|
|
Go lnRecno
|
|
Case poArticol.gestionabil = 1 And gnScadereStoc = 1 And ;
|
|
(Inlist(poDate.tip, 1, 22, 29) Or (poDate.tip = 2 And poArticol.opt_facturare = 0))
|
|
Replace cantitate With IIF(cantitate - poArticol.cantitate > 0, cantitate - poArticol.cantitate, 0) For id_articol = poArticol.id_articol And gestionabil = 1
|
|
Go lnRecno
|
|
Endcase
|
|
```
|
|
|
|
`lcCursor` = `crsarticole1` daca `tlContract`, altfel `crsarticole` (`:12839-12845`).
|
|
|
|
### 1.2 `do_sterge` — refacerea la stergerea unei linii (`:14608-14677`)
|
|
|
|
```
|
|
Select (lcCursor)
|
|
lnRecNo = Recno()
|
|
Do Case
|
|
Case gnScadereStoc = 1 And poDate.tip = 41
|
|
Select (lcCursor)
|
|
Replace cantitate With cantitate - poArticol.cantitate For id_c = poArticol.id_c
|
|
Case (gnScadereStoc = 1 And poDate.tip = 23) Or Inlist(poDate.tip,3,4,21,25,28,42,47)
|
|
Select (lcCursor)
|
|
Replace cantitate With cantitate + poArticol.cantitate For id_c = poArticol.id_c
|
|
Case Inlist(poDate.tip,8,9,24)
|
|
Replace cantitate With cantitate - poArticol.cantitate For id_c = poArticol.id_c
|
|
Case poArticol.id_gestiune <> - 1000 And gnScadereStoc = 1 And ;
|
|
(Inlist(poDate.tip,1,22,29) Or (poDate.tip = 2 And poArticol.opt_facturare = 0))
|
|
Select (lcCursor)
|
|
Replace cantitate With cantitate + poArticol.cantitate For id_articol = poArticol.id_articol And gestionabil = 1
|
|
Endcase
|
|
```
|
|
|
|
`lcCursor` = `crsarticole1` daca `poArticol.opt_facturare <> 0`, altfel `crsarticole` (`:14615-14619`).
|
|
**Exact inversul semnului fata de `do_adauga_articol`, pe aceleasi patru grupuri de tipuri** — simetrie
|
|
confirmata, nu presupusa.
|
|
|
|
### 1.3 `do_modifica` — ajustarea la schimbarea cantitatii pe o linie deja adaugata (`:13746-13914`)
|
|
|
|
Doua interactiuni distincte cu registrul, in aceeasi metoda:
|
|
|
|
**(a) Calculul plafonului inainte de a arata dialogul** (`:13775-13798`), comentat explicit in cod
|
|
ca "plafonul de cantitate = stocul disponibil reconstituit din cursorul sursa; fara randul in cursor
|
|
ramane cantitatea liniei" (`:13775`):
|
|
```
|
|
lnCantitateMax = poArticol.cantitate
|
|
lcCursorStoc = Iif(poArticol.opt_facturare = 0, [crsarticole], [crsarticole1])
|
|
...
|
|
Locate For id_c = poArticol.id_c
|
|
If Found()
|
|
Do Case
|
|
Case (gnScadereStoc = 1 And poDate.tip = 23) Or Inlist(poDate.tip, 4, 21, 28, 42, 47)
|
|
lnCantitateMax = cantitate + poArticol.cantitate
|
|
Case Inlist(poDate.tip, 8, 9, 24)
|
|
lnCantitateMax = cantitate - poArticol.cantitate
|
|
Endcase
|
|
Endif
|
|
```
|
|
Aduna inapoi ce a consumat deja linia curenta, ca sa arate operatorului plafonul real (cat mai era
|
|
disponibil **inainte** de aceasta linie), trimis ca parametru la `do_alege_stoc`/`frm_articol_factura`.
|
|
|
|
**(b) Ajustarea delta dupa ce operatorul schimba cantitatea** (`:13887-13904`):
|
|
```
|
|
lnDeltaCantitate = poArticol.cantitate - lnCantitateVeche
|
|
If lnDeltaCantitate <> 0 And Used(lcCursorStoc)
|
|
Do Case
|
|
Case (gnScadereStoc = 1 And poDate.tip = 23) Or Inlist(poDate.tip, 4, 21, 28, 42, 47)
|
|
Replace cantitate With cantitate - lnDeltaCantitate For id_c = poArticol.id_c
|
|
Case Inlist(poDate.tip, 8, 9, 24)
|
|
Replace cantitate With cantitate + lnDeltaCantitate For id_c = poArticol.id_c
|
|
Endcase
|
|
Endif
|
|
```
|
|
|
|
**Gasit, nu presupus: `do_modifica` NU ajusteaza `crsarticole` pentru tipurile `1,22,29` si
|
|
`2`-lista-jumatate** (grupul "gestionabil, plafon local" din `do_adauga_articol`/`do_sterge`) — doar
|
|
pentru grupul comanda/aviz (`4,21,23,28,42,47`) si retur (`8,9,24`). E o asimetrie **preexistenta**
|
|
in codul de azi (nu introdusa de S4): editarea cantitatii pe o linie de lista-de-preturi deja adaugata
|
|
nu recalibreaza plafonul local. Nu e de reparat aici — doar de semnalat, ca varianta noua sa nu
|
|
incerce sa reproduca o simetrie care nu exista azi.
|
|
|
|
### 1.4 `do_adauga_tot` — enumerare, nu bookkeeping propriu (`:13169-13198`)
|
|
|
|
```
|
|
Select crsarticole
|
|
Scan
|
|
...
|
|
Thisform.do_adauga_articol(.T.)
|
|
...
|
|
Endscan
|
|
```
|
|
|
|
**Nu scrie direct in `crsarticole`** — cheama `do_adauga_articol` pe fiecare rand, care face
|
|
bookkeeping-ul de la 1.1. Rolul lui `crsarticole` aici e al treilea, distinct de Rolul A/B: **sursa
|
|
de enumerare** ("ce randuri exista de adaugat"), necesara oricum pentru populare (S4 punctul 1), nu
|
|
doar pentru bookkeeping.
|
|
|
|
### 1.5 `do_scrie_factura` — citirea care alimenteaza decizia de inchidere (`:14301-14338`)
|
|
|
|
Doua ramuri, singurele doua locuri din intregul fisier unde `Calculate Sum(cantitate)` ruleaza peste
|
|
`crsarticole` (confirmat cu grep pe tot fisierul, nicio a treia aparitie):
|
|
|
|
```
|
|
Case poDate.Tip = 4
|
|
Select crsarticole
|
|
Calculate Sum(cantitate) To lnCantitateRamasa
|
|
If lnCantitateRamasa > 0
|
|
pnFacturaRetur = 7 - amessagebox("Doriti sa se inregistreze si avizul de retur?",4+32,"Confirmare")
|
|
pnParametruAditional = 1
|
|
Else
|
|
pnFacturaRetur = 0
|
|
pnParametruAditional = 0
|
|
Endif
|
|
...
|
|
Case Inlist(poDate.Tip,3,21,25,28,42,47)
|
|
Select crsarticole
|
|
Calculate Sum(cantitate) To lnCantitateRamasa
|
|
If lnCantitateRamasa <> 0
|
|
pnParametruAditional = 7 - amessagebox("Doriti sa se inchida comanda?",4+32,"Confirmare")
|
|
Endif
|
|
...
|
|
```
|
|
|
|
`pnParametruAditional` default e `0` (`:14222`, setat inainte de `Do Case`). Pentru **orice alt tip**
|
|
(inclusiv `41`, `23`, `2/6/26/52`, `45/48/49`), ramane `0` fara sa fie calculat din `crsarticole` deloc
|
|
(exceptie: tip `48` il suprascrie cu `poDate.coeficient_k`, fara legatura cu inchiderea — `:14367-14369`).
|
|
|
|
**Concluzie:** doar `4` si `Inlist(3,21,25,28,42,47)` sunt Rolul A. Restul tipurilor nu ating deloc
|
|
aceasta parte a lui `do_scrie_factura` — inclusiv `41`/`23`, care au totusi bookkeeping Rol B activ la
|
|
1.1-1.3.
|
|
|
|
---
|
|
|
|
## 2. Semantica registrului azi
|
|
|
|
### 2.1 Ce inseamna `cantitate` la incarcare, per grup
|
|
|
|
Confirmat pe corpul cursoarelor Oracle (`ff_...PACK_FACTURARE.sql`):
|
|
|
|
- **`cursor_comanda`** (`:2952-3171` per raportul punctului 1; verificat aici direct la sectiunea
|
|
"aviz"/comanda a interogarii, `:3060-3081`): coloana `cantitate` e `A.CANTITATE - NVL(D.CANTITATE, 0)`
|
|
unde `D` e `SUM(cantitate)` deja facturat din `VANZARI_DETALII` pe acelasi `id_comanda`
|
|
(`:3060-3068`), filtrat `WHERE ... SIGN(A.CANTITATE) * (A.CANTITATE - NVL(D.CANTITATE, 0)) > 0`
|
|
(`:3079`). **E deja "ramas de facturat", calculat live la deschidere** — nu cantitatea comandata
|
|
bruta.
|
|
- **`cursor_avize`** (`:3703-3874`): coloana `A.CANTITATE - NVL(D.CANTITATE, 0) AS CANTITATE`
|
|
(analog la `:3118`), plus agregarea finala (`:3820-3872`) care scade `VANZARI_CANTITATI` deja
|
|
consumat din `VANZARI_DETALII` (`WHERE A.CANTITATE <> NVL(B.CANTITATE, 0)`, `:3871`). Acelasi
|
|
tipar: "ramas", nu cantitatea avizata bruta.
|
|
- **Restul grupurilor (Rol B: `1,22,29,2`-lista, `23,41` transfer, `8,9,24` retur)**: `cantitate`
|
|
vine din `cursor_preturi`/`cursor_gestiune`, care e **stoc disponibil** (`LEFT JOIN` pe `STOC`,
|
|
raportul punctului 1 sectiunea 1.1/1.5), nu "ramas de facturat dintr-un document" — pentru ca aceste
|
|
tipuri nu au un document sursa cu cantitati de urmarit (lista de preturi n-are document sursa;
|
|
transferul/returul opereaza pe stoc, nu pe o comanda).
|
|
|
|
### 2.2 Cine o scade si cand
|
|
|
|
Vezi sectiunea 1: la fiecare `do_adauga_articol` (scade sau creste, dupa tip), la fiecare `do_sterge`
|
|
(inversul), si la `do_modifica` doar pentru grupul Rol A + retur (2.1(b) de mai sus).
|
|
|
|
### 2.3 Ce se intampla la adaugare / stergere / modificare cantitate — rezumat comportamental
|
|
|
|
| Actiune | Rol A (comanda 3/21/25/28/42/47, aviz 4) | Rol B (lista gestionabila 1/22/29/2, transfer 23/41, retur 8/9/24) |
|
|
|---|---|---|
|
|
| Adauga linie | `crsarticole.cantitate` scade (creste la retur/41) cu cantitatea adaugata | idem, plafon local |
|
|
| Sterge linie | reface exact simetric | reface exact simetric |
|
|
| Modifica cantitate pe linie existenta | ajusteaza cu delta (23/4/21/28/42/47 si 8/9/24 explicit) | **nu ajusteaza** pe 1/22/29/2-lista (asimetrie preexistenta, 1.3) |
|
|
| La scriere (`do_scrie_factura`) | **suma peste `crsarticole` decide `pnParametruAditional`** (inchidere) | **nu se suma niciodata** — nu alimenteaza nicio decizie Oracle |
|
|
|
|
---
|
|
|
|
## 3. Regula de inchidere automata — exact, cu dovada Oracle
|
|
|
|
### 3.1 Ce trimite VFP si ce face Oracle cu el
|
|
|
|
`pnParametruAditional` merge la Oracle prin `scrie_factura_avize` (tip `4`) sau `scrie_factura2`
|
|
(toate celelalte, inclusiv comanda). Ambele cheama in interior `finalizeaza_factura(...)`
|
|
(`ff_...PACK_FACTURARE.sql:14770-14852`), care are un `CASE` unic pe `pack_facturare.ntip` (setat
|
|
intern din tipul documentului, nu parametru separat):
|
|
|
|
```sql
|
|
CASE
|
|
WHEN V_PARAMETRU_ADITIONAL = 1 AND pack_facturare.ntip IN (3, 21, 25, 28, 42, 47) THEN
|
|
-- comanda: V_PARAMETRU_ADITIONAL este V_INCHIDERE_COMANDA
|
|
pack_facturare.inchide_comanda();
|
|
WHEN pack_facturare.ntip = 4 THEN
|
|
-- aviz: V_PARAMETRU_ADITIONAL este V_VERIFICARE_FACTURAT
|
|
pack_facturare.scrie_cantitati_vanzari_avize;
|
|
pack_facturare.scrie_corespondente_vanzari(1);
|
|
pack_facturare.marcheaza_facturat(V_PARAMETRU_ADITIONAL);
|
|
WHEN pack_facturare.ntip = 24 THEN
|
|
pack_facturare.scrie_corespondente_vanzari(2);
|
|
pack_facturare.marcheaza_facturat(V_PARAMETRU_ADITIONAL);
|
|
WHEN pack_facturare.ntip in (2, 6, 52) THEN
|
|
pack_facturare.scrie_rate_factura(V_DATAORA);
|
|
WHEN pack_facturare.ntip in (8, 9) THEN
|
|
pack_facturare.scrie_corespondente_vanzari(3);
|
|
ELSE
|
|
dbms_output.put_line('---');
|
|
END CASE;
|
|
```
|
|
(`:14818-14839`)
|
|
|
|
**`41` si `23` nu apar deloc in acest `CASE`** — cad pe `ELSE`, fara nicio actiune de inchidere.
|
|
Confirma pe cod ce arata si sectiunea 1.5: transferul intre subunitati nu are un "document sursa" de
|
|
inchis, doar un plafon de stoc (Rol B). `2/6/26/52` (contract) intra pe ramura `scrie_rate_factura`,
|
|
care scrie scadentarul de rate — **nu e o "inchidere" in sensul comanda/aviz**, e alta operatie.
|
|
|
|
### 3.2 Comanda — `inchide_comanda()` (`:5769-5820`)
|
|
|
|
**Ruleaza doar cand `V_PARAMETRU_ADITIONAL = 1`** — adica doar daca `crsarticole` a aratat cantitate
|
|
ramasa nenula **si** operatorul a raspuns "Da" la "Doriti sa se inchida comanda?" (`ofacturare.vc2:14336-14337`).
|
|
Cand ramane 0 (fara diferenta), `inchide_comanda()` **nu se cheama deloc** — pentru ca o comanda complet
|
|
livrata se raporteaza deja "inchisa" prin simplul fapt ca interogarea de mai jos nu mai returneaza
|
|
randuri, fara nicio actiune suplimentara.
|
|
|
|
Ce face efectiv (`:5771-5818`): **insereaza un rand compensator** in `COMENZI_ELEMENTE`, cu
|
|
`CANTITATE = ramas` (calculat live, independent de VFP, din `NVL(C.CANTITATE,0) + NVL(B.CANTITATE,0) -
|
|
A.CANTITATE` unde `B` = deja facturat in aceasta tranzactie (`VANZARI_DETALII_TEMP`) si `C` = deja
|
|
facturat istoric (`VANZARI`/`VANZARI_DETALII`)) — asta face ca o interogare ulterioara de tip
|
|
`cursor_comanda` (aceeasi forma de `WHERE`, `SIGN(...) * (A.CANTITATE - NVL(D.CANTITATE,0)) > 0`) sa
|
|
nu mai gaseasca randuri ramase, adica sa "inchida" comanda **prin recalcul, nu printr-un flag setat**.
|
|
**Nu exista o coloana `INCHISA`/status persistat pe comanda** (cautare `INCHISA` in tot pachetul —
|
|
zero potriviri) — starea "deschisa/inchisa" e intotdeauna derivata din suma `COMENZI_ELEMENTE` vs
|
|
`VANZARI_DETALII`, nu citita dintr-un camp.
|
|
|
|
**Important pentru paritate:** cantitatea trimisa de VFP (`pnParametruAditional`, un simplu `1`/`0`)
|
|
**nu participa la calculul cantitatii inserate** — Oracle o recalculeaza singur din tabele reale.
|
|
Rolul VFP e strict binar: "a fost cerut sa se forteze inchiderea?".
|
|
|
|
### 3.3 Avize — `marcheaza_facturat(V_VERIFICARE)` (`:15381-15418`)
|
|
|
|
```sql
|
|
IF V_VERIFICARE = 0 THEN
|
|
UPDATE VANZARI SET FACTURAT = 1, ID_UTILFACT = ...
|
|
WHERE ID_VANZARE IN (SELECT id_vanzare_aviz ... WHERE id_vanzare_Fact = pack_facturare.nid_vanzare AND sters=0 AND tip<>3)
|
|
AND FACTURAT = 0;
|
|
ELSE
|
|
UPDATE VANZARI SET FACTURAT = 1, ...
|
|
WHERE ID_VANZARE IN (
|
|
SELECT id_vanzare FROM (
|
|
SELECT a.id_vanzare, SUM(a.cantitate - NVL(b.cantitate,0)) AS ramas
|
|
FROM vanzari_detalii a LEFT JOIN (...VANZARI_CANTITATI...) b ON ...
|
|
WHERE a.id_vanzare IN (...avizele referite de aceasta factura...)
|
|
GROUP BY a.id_vanzare
|
|
) WHERE ramas = 0
|
|
);
|
|
END IF;
|
|
```
|
|
|
|
**Aici exista un flag real persistat: `VANZARI.FACTURAT`.** `V_VERIFICARE` (= `pnParametruAditional`
|
|
trimis de VFP) alege intre doua moduri:
|
|
- `V_VERIFICARE = 0` (VFP a vazut `lnCantitateRamasa = 0`, adica a crezut ca tot ce era in avizele
|
|
referite s-a facturat): **marcheaza TOATE avizele referite ca facturate, fara re-verificare** —
|
|
increde in calculul VFP.
|
|
- `V_VERIFICARE = 1` (VFP a vazut ceva ramas): **recalculeaza per-aviz, din tabele reale**
|
|
(`vanzari_detalii`/`vanzari_cantitati`), si marcheaza **doar** avizele individuale al caror
|
|
`ramas = 0` — mai stric, pentru ca VFP nu mai e sigur.
|
|
|
|
**Observatie relevanta pentru punctul 2 al plafonului de risc**: cand un singur `crsarticole` agrega
|
|
mai multe avize (facturare din mai multe avize deodata), suma globala poate fi 0 chiar daca un aviz
|
|
individual din grup mai are ramas si altul are exces care-l compenseaza — caz in care `V_VERIFICARE=0`
|
|
ar marca **toate** ca facturate, inclusiv cel cu ramas real. **Comportament preexistent**, nu introdus
|
|
de decuplare — dar varianta noua trebuie sa-l reproduca identic (aceeasi conditie de trigger: suma
|
|
globala peste toate liniile din tranzactia curenta, nu per-document), nu sa-l "repare" din greseala.
|
|
|
|
### 3.4 Cine altcineva verifica remainder — `scrie_cantitati_vanzari_avize` (`:15420-15479`)
|
|
|
|
Ruleaza **necondiționat** pe ramura `tip=4`, indiferent de `V_VERIFICARE` — scrie in `VANZARI_CANTITATI`
|
|
cate din cantitatile avizate au fost efectiv consumate de aceasta factura (mecanismul care alimenteaza
|
|
`ramas` la sectiunea 3.3). Nu depinde de `crsarticole` — foloseste `VANZARI_DETALII_TEMP` (deja
|
|
populat de `do_scrie_articole()`, apelat inaintea acestui `Do Case` din VFP, `:14264`) si tabele reale.
|
|
**Confirma independenta totala de `crsarticole` a partii Oracle** — singurul lucru pe care Oracle il
|
|
primeste de la VFP e semnalul binar `pnParametruAditional`.
|
|
|
|
---
|
|
|
|
## 4. Proiectare — variante comparate
|
|
|
|
### Varianta (a) — cursor propriu, minimal, decuplat de populare
|
|
|
|
Un cursor separat (`crsregistru`), incarcat cu `id_c`/`id_articol` + cantitate ramasa, populat din
|
|
aceeasi interogare care alimenteaza azi `crsarticole`, dar **independent** de campurile de populare
|
|
(pret, TVA, valuta...) pe care S4 punctul 1 le muta pe cautare filtrata.
|
|
|
|
**Ce se atinge:** aceleasi patru metode (`do_adauga_articol`, `do_sterge`, `do_modifica`,
|
|
`do_scrie_factura`) — schimba doar numele cursorului tinta, logica ramane identica.
|
|
**Ce se rupe:** nimic structural — e schimbarea minima de sintaxa.
|
|
**Cost:** mic, dar **nu rezolva problema de fond**: tot exista o a doua copie a "cat a mai ramas",
|
|
tinuta manual in memorie VFP, care trebuie sa ramana in sincron cu ce calculeaza Oracle la scriere
|
|
(sectiunea 3). E acelasi risc de azi, doar cu un cursor mai ingust. **Nu e "sursa unica"** — e aceeasi
|
|
sursa dubla, doar mai mica.
|
|
|
|
### Varianta (b) — recalculul pe server la scriere, in loc de suma locala (RECOMANDATA pentru Rolul A)
|
|
|
|
Inlocuieste cele doua `Select crsarticole / Calculate Sum(cantitate) To lnCantitateRamasa` din
|
|
`do_scrie_factura` (`:14303-14304`, `:14334-14335`) cu un apel Oracle nou, facut **dupa**
|
|
`Thisform.do_scrie_articole()` (`:14264`, care populeaza deja `VANZARI_DETALII_TEMP` cu exact ce e pe
|
|
cale sa fie scris) si **inainte** de `Do Case` care alege `scrie_factura_avize`/`scrie_factura2`.
|
|
|
|
Doua proceduri Oracle noi, minimale, care **reutilizeaza exact interogarea** pe care
|
|
`inchide_comanda`/`marcheaza_facturat` o ruleaza oricum peste doua randuri mai jos in acelasi flux —
|
|
nu o interogare noua, o extragere a celei existente intr-o forma care returneaza un numar in loc sa
|
|
scrie:
|
|
|
|
```sql
|
|
-- comanda: acelasi WHERE ca inchide_comanda (:5771-5818), dar SUM in loc de INSERT
|
|
FUNCTION cantitate_ramasa_comanda(V_ID_COMANDA IN NUMBER) RETURN NUMBER IS
|
|
V_RAMAS NUMBER;
|
|
BEGIN
|
|
SELECT NVL(SUM(SIGN(A.CANTITATE) *
|
|
GREATEST(SIGN(A.CANTITATE) * (A.CANTITATE - NVL(B.CANTITATE,0) - NVL(C.CANTITATE,0)), 0)), 0)
|
|
INTO V_RAMAS
|
|
FROM COMENZI_ELEMENTE A
|
|
LEFT JOIN (SELECT ID_ARTICOL, ID_POL, ID_VALUTA, PRET, SUM(CANTITATE) AS CANTITATE
|
|
FROM VANZARI_DETALII_TEMP GROUP BY ID_ARTICOL, ID_POL, ID_VALUTA, PRET) B
|
|
ON A.ID_ARTICOL = B.ID_ARTICOL AND A.ID_POL = B.ID_POL AND A.ID_VALUTA = B.ID_VALUTA AND A.PRET = B.PRET
|
|
LEFT JOIN (... acelasi C ca in inchide_comanda, VANZARI/VANZARI_DETALII istoric ...) C ON ...
|
|
WHERE A.STERS = 0 AND A.ID_COMANDA = V_ID_COMANDA;
|
|
RETURN V_RAMAS;
|
|
END;
|
|
|
|
-- avize: acelasi calcul ca in marcheaza_facturat(V_VERIFICARE=1), dar returnat, nu aplicat
|
|
FUNCTION exista_ramas_avize(V_LISTAID IN VARCHAR2) RETURN NUMBER IS ...
|
|
```
|
|
|
|
**Ce se atinge:** `do_scrie_factura` (inlocuieste 4 linii VFP cu un apel Oracle + citire scalar), plus
|
|
doua functii Oracle noi in `PACK_FACTURARE`, care **extrag** logica deja scrisa in `inchide_comanda`/
|
|
`marcheaza_facturat`, nu o duplica din nimic. `do_adauga_tot`, `do_sterge`, `do_modifica` **raman
|
|
neschimbate pentru Rolul A** — nu mai au ce sa faca, pentru ca nimic nu mai citeste suma lor.
|
|
**Ce se rupe:** niciun comportament — recalculul Oracle e **mai precis** decat suma locala (vede
|
|
concurenta: doi operatori facturand din aceeasi comanda simultan; `crsarticole` local nu vede
|
|
modificari facute de alt utilizator dupa deschiderea formularului — bug latent de azi, pe care
|
|
varianta (b) il elimina ca efect secundar, nu ca scop).
|
|
**Cost:** doua functii Oracle noi (SQL, nu logica noua — extrase din proceduri existente), un apel
|
|
suplimentar `goExecutor` in `do_scrie_factura`.
|
|
**De ce e "sursa unica":** Oracle calculeaza "cat a ramas" o singura data, in doua locuri (recalcul
|
|
inainte de scriere + recalcul in `inchide_comanda`/`marcheaza_facturat` la scriere), din **aceeasi
|
|
interogare**, niciodata dintr-o copie VFP. Nu mai exista sincronizare de mentinut, pentru ca nu mai
|
|
exista a doua stare.
|
|
|
|
### Varianta (c) — plafon Rol B recalculat la cerere, nu tinut in memorie (RECOMANDATA pentru Rolul B)
|
|
|
|
Pentru grupul Rol B (`1,22,29,2`-lista, `23,41`, `8,9,24`), plafonul din `do_modifica`
|
|
(`lnCantitateMax`, sectiunea 1.3(a)) si decrementul din `do_adauga_articol`/`do_sterge` (sectiunea
|
|
1.1/1.2) devin un apel Oracle facut **la fiecare adaugare/editare**, in loc de o valoare tinuta in
|
|
`crsarticole` — interogare filtrata pe `id_articol`, aceeasi forma ca `cursor_preturi`/
|
|
`cursor_gestiune` filtrate (deja proiectate la S4 punctul 1, sectiunea 4), minus stocul deja rezervat
|
|
in sesiunea curenta (calculabil din `crsfactura` insusi, sumand liniile deja adaugate cu acelasi
|
|
`id_articol` — cursor local, dar de linii **efectiv adaugate**, nu de "cat mai era", deci nu mai poate
|
|
diverge de continutul real al facturii in curs).
|
|
|
|
**Ce se atinge:** `do_adauga_articol` (:13034-13051 dispare, inlocuit cu un apel la cerere din
|
|
`do_alege_stoc`, care oricum face verificare de stoc pe server — de confirmat la implementare daca
|
|
verificarea existenta acopera deja acest plafon sau trebuie extinsa), `do_sterge` (:14640-14669
|
|
Rol B dispare, ramane doar Rolul A), `do_modifica` (:13775-13798 plafonul se cere pe server, nu se
|
|
reconstituie din cursor).
|
|
**Ce se rupe:** **de verificat cu Marius** — plafonul Rol B azi e un avertisment soft (`do_alege_stoc`
|
|
primeste `lnCantitateMax` ca parametru, dar verificarea reala de stoc la comitere ramane oricum in
|
|
Oracle, la `adauga_articol_factura`/scriere; codul citit aici nu arata un blocaj hard bazat pe
|
|
`crsarticole` — de confirmat explicit, nu presupus, ca UX-ul de azi (mesaj/limitare la alegerea
|
|
cantitatii) nu depinde de faptul ca plafonul persista *in memorie* intre doua adaugari ale *aceluiasi*
|
|
articol in aceeasi sesiune, ci doar de valoarea citita la momentul respectiv).
|
|
**Cost:** mediu — mai multe interogari Oracle mici (una per adaugare/editare de linie gestionabila),
|
|
in loc de aritmetica locala. Justificat de acelasi motiv ca varianta (b): elimina a doua copie.
|
|
|
|
### Comparatie si recomandare finala
|
|
|
|
| | (a) cursor propriu minimal | (b) recalcul server (Rol A) | (c) plafon la cerere (Rol B) |
|
|
|---|---|---|---|
|
|
| E "sursa unica"? | Nu — copie mai mica, tot manuala | **Da** | **Da** |
|
|
| Risc de divergenta fata de Oracle | Identic cu azi | Eliminat (Oracle citeste Oracle) | Eliminat |
|
|
| Cost implementare | Mic | Mic-mediu (2 functii Oracle) | Mediu (mai multe roundtrip-uri) |
|
|
| Rezolva corectia sectiunii 0 (23/41 blocheaza punctul 1) | Nu direct | N/A (23/41 nu sunt Rol A) | **Da** |
|
|
|
|
**Recomandare: (b) pentru Rolul A, (c) pentru Rolul B.** Impreuna elimina complet nevoia de a incarca
|
|
`crsarticole` in masa pentru bookkeeping — incarcarea ramasa (daca ramane) e strict pentru
|
|
**enumerare** la "adauga tot" (sectiunea 1.4), care e un scop diferit si mai ingust (nu mai trebuie sa
|
|
tina sincron o cantitate, doar sa listeze randuri candidate o singura data, la deschidere).
|
|
|
|
---
|
|
|
|
## 5. Cazul limita: adauga si sterge inainte de salvare
|
|
|
|
**Azi:** `do_adauga_articol` decrementeaza `crsarticole`/`crsarticole1` (Rol A si/sau B, dupa tip);
|
|
`do_sterge` reface exact simetric, inainte de orice salvare (sectiunile 1.1-1.2 sunt perechi
|
|
simetrice pe fiecare grup de tipuri). Suma finala din `crsarticole` la momentul `do_scrie_factura`
|
|
reflecta corect doar liniile **ramase** in `crsfactura`, indiferent cate au fost adaugate si sterse
|
|
intre timp — pentru ca fiecare stergere anuleaza exact adaugarea corespunzatoare.
|
|
|
|
**In varianta recomandata (b+c):** cazul limita devine **trivial**, nu doar acoperit — pentru ca nu
|
|
mai exista o stare intermediara de sincronizat. Rolul A: `do_scrie_factura` calculeaza remainder-ul
|
|
o singura data, la scriere, din `VANZARI_DETALII_TEMP` care contine **exact** liniile ramase in
|
|
`crsfactura` la acel moment (populat de `do_scrie_articole()` chiar inainte, `Scan` peste `crsfactura`
|
|
curent — sectiunea 4 varianta (b)) — liniile adaugate-si-sterse nu ajung niciodata in
|
|
`VANZARI_DETALII_TEMP`, deci nu influenteaza calculul, fara nicio actiune suplimentara. Rolul B:
|
|
plafonul la fiecare adaugare se cere din nou pe server, minus ce e deja in `crsfactura` in acel
|
|
moment — o stergere anterioara pur si simplu nu mai apare in acea suma, fara "refacere" explicita.
|
|
**Cazul limita nu mai e un caz special de tratat — e comportamentul implicit al oricarei citiri facute
|
|
din starea curenta, in loc de dintr-un contor tinut manual.**
|
|
|
|
---
|
|
|
|
## 6. Per tip de document
|
|
|
|
| Tip(uri) | Rol azi | Ce se schimba in varianta recomandata |
|
|
|---|---|---|
|
|
| **Comanda** `3,21,25,28,42,47` | Rol A (inchidere automata) | `do_scrie_factura` cheama functia Oracle noua (4b) in loc de `Calculate Sum`; `do_adauga_tot`/`do_sterge` raman (enumerare + Rol B nu se aplica pe comanda insasi, doar pe articolele ei individuale daca sunt gestionabile — **de verificat**, comanda nu apare in niciuna din listele Rol B de la sectiunea 1, deci pare curatata deja) |
|
|
| **Avize** `4` | Rol A (marcheaza_facturat) | idem, functia Oracle pentru avize (4b) |
|
|
| **Contract** `2,6,26,52` | Jumatate `crsarticole` (delegat la `cursor_preturi`, per raportul punctului 1) e Rol B doar pentru `poDate.tip=2 And opt_facturare=0`; restul (`crsarticole1`, rate+articole `OPT_FACTURARE=3`) nu are bookkeeping in sectiunea 1 (needitat de cautare, ramane cum e azi, per raportul punctului 1 sectiunea 7) | Rolul B pe jumatatea `opt_facturare=0` trece pe varianta (c); `26,52` nu apar in niciuna din listele Rol B/A gasite in cod — **de verificat separat, posibil fara bookkeeping deloc pe aceste doua**, nesemnalat ca atare in codul citit aici |
|
|
| **Transfer** `41` (standard); `23` (doar prototip, standard il trateaza ca lista de preturi) | Rol B pur (niciun Rol A — confirmat sectiunea 3.1, `41`/`23` cad pe `ELSE` in `finalizeaza_factura`) | Trece integral pe varianta (c); **aceasta e corectia care debloca pasul 5 al punctului 1** — fara ea, oprirea incarcarii in masa pe `23,41` (propusa acolo) ar sparge plafonul de stoc existent |
|
|
| **Lista de preturi** `1,5,7,10,22,29` | Rol B doar pentru articole gestionabile (`1,22,29`; `5,7,10` nu apar in Do Case-urile Rol B — **fara bookkeeping**, confirmat pe cod) | `1,22,29` trec pe varianta (c); `5,7,10` nu au nimic de decuplat, deja curatate |
|
|
| **Retur** `8,9,24` | Rol B (inversul directiei fata de restul) | Varianta (c), simetric |
|
|
| **Restaurant `45`, K `48,49`** | Nu ating Rol A/B (in afara `Do Case`-urilor gasite; `45` explicit exclus la `poArticol.gestionabil=0 Or gnScadereStoc=0 Or poDate.tip=45` in ambele metode, deci ocoleste bookkeeping-ul indiferent de gestionabilitate) | Fara schimbare — deja fara bookkeeping de decuplat |
|
|
|
|
**Tipuri semnalate ca neclare, de stabilit separat, nu presupuse aici:** `26` (aviz din contract) si
|
|
`52` (contract in valuta/alt subtip) nu apar explicit in niciuna din listele Rol A/B din sectiunea 1 —
|
|
codul citit in aceasta sesiune nu confirma nici prezenta, nici absenta bookkeeping-ului pe ele
|
|
specific; tratamentul lor pare sa urmeze grupul `2,6` din `Inlist`-urile care le includ, dar niciun
|
|
`Do Case` din sectiunea 1 nu le mentioneaza individual in afara de includerea in `Inlist(poDate.tip,
|
|
2, 6, 52)` la `do_scrie_articole` (rate) — nu la bookkeeping-ul de `crsarticole`.
|
|
|
|
---
|
|
|
|
## 7. Pasi de implementare, ordonati
|
|
|
|
1. **Pas 1 — Oracle: extrage functiile de recalcul remainder** (`cantitate_ramasa_comanda`,
|
|
`exista_ramas_avize`) din logica deja scrisa in `inchide_comanda`/`marcheaza_facturat`, fara sa
|
|
modifice acele proceduri. *Gata cand:* apelate manual cu parametrii unei comenzi/unui grup de avize
|
|
cunoscute, valoarea returnata coincide cu suma pe care `Calculate Sum(cantitate)` din VFP o
|
|
calculeaza azi peste `crsarticole`, pe acelasi document, in aceeasi stare (comparatie directa,
|
|
inainte de orice alta schimbare).
|
|
2. **Pas 2 — VFP: `do_scrie_factura` cheama functiile noi in loc de `Calculate Sum`** (sectiunea 4b),
|
|
pastrand identic restul logicii (`pnFacturaRetur`, mesajele de confirmare, `pnParametruAditional`).
|
|
*Gata cand:* pe un document de comanda si unul de aviz, cu acelasi scenariu (facturare partiala),
|
|
dialogul de confirmare apare in acelasi moment si cu acelasi rezultat ca inainte de Pas 2 — regresie
|
|
manuala, comparatie inainte/dupa pe acelasi document.
|
|
3. **Pas 3 — Oracle: functie de plafon Rol B filtrata pe articol**, reutilizand `WHERE`-ul din
|
|
`cursor_preturi`/`cursor_gestiune` (deja proiectat la S4 punctul 1, sectiunea 4), minus ce e deja in
|
|
`crsfactura` pentru acelasi articol. *Gata cand:* pe un articol gestionabil cunoscut, valoarea
|
|
returnata coincide cu `crsarticole.cantitate` de azi, la aceeasi stare a sesiunii (inainte de orice
|
|
adaugare).
|
|
4. **Pas 4 — VFP: `do_adauga_articol`/`do_sterge`/`do_modifica` folosesc plafonul cerut pe server**
|
|
pentru grupul Rol B, in loc de `Replace cantitate` pe `crsarticole` (sectiunea 4c). *Gata cand:*
|
|
cazul limita de la sectiunea 5 (adauga-apoi-sterge inainte de salvare) produce acelasi plafon
|
|
disponibil ca azi, verificat manual pe un articol cu stoc limitat.
|
|
5. **Pas 5 — activarea pasului 5 al punctului 1 pe `23`/`41`** (oprirea incarcarii in masa), acum ca
|
|
Pasul 4 a mutat Rolul B in afara lui `crsarticole`. *Gata cand:* deschiderea formularului pe tip
|
|
`41` (si `23` pe prototip) nu mai executa `cursor_gestiune`/`cursor_preturi` la deschidere, dar
|
|
adaugarea unui articol tot respecta plafonul de stoc (verificat manual).
|
|
6. **Pas 6 — curatare: `crsarticole` ramane incarcat doar unde e nevoie de enumerare** (comanda, aviz,
|
|
contract-rate — pentru "adauga tot" existent), fara bookkeeping legat de el. *Gata cand:* grep pe
|
|
`ofacturare.vc2` pentru `Replace cantitate.*crsarticole` (in afara populare) nu mai gaseste
|
|
potriviri in `do_adauga_articol`/`do_sterge`/`do_modifica`.
|
|
7. **Pas 7 — verificare paritate finala** (sectiunea 8), pe toate tipurile cu document sursa.
|
|
|
|
*Depinde de:* S4 punctul 1 (Pasii 1-4 din PROIECTAREA acelui punct trebuie sa existe, dar Pasii 1-4
|
|
**de aici** pot fi facuti independent, inaintea sau in paralel cu populare — nu depind de cautarea
|
|
filtrata, doar de `crsarticole`/`crsfactura` existente azi). Pasul 5 de aici depinde explicit de Pasul
|
|
4 de aici (nu poate porni inaintea lui).
|
|
|
|
---
|
|
|
|
## 8. Cum se verifica paritatea inchiderii automate
|
|
|
|
**Comanda** (nu exista flag `INCHISA` persistat — cautat explicit in tot pachetul, zero potriviri;
|
|
starea se deriva din `COMENZI_ELEMENTE` vs `VANZARI_DETALII`):
|
|
|
|
```sql
|
|
-- inainte de facturare partiala + inchidere fortata (flux vechi vs nou, aceeasi comanda X)
|
|
{call pack_facturare.cursor_comanda(V_DATA_CURS, V_TIP, 'X', V_ID_UTIL, :cursor)}
|
|
-- numara randurile ramase (trebuie sa fie 0 dupa inchidere fortata, identic pe ambele fluxuri)
|
|
SELECT COUNT(*) FROM (...continutul cursorului de mai sus...);
|
|
|
|
-- verificare directa a compensatiei inserate de inchide_comanda
|
|
SELECT ID_ARTICOL, ID_POL, CANTITATE FROM COMENZI_ELEMENTE
|
|
WHERE ID_COMANDA = :X ORDER BY ID_COMANDA_ELEMENT DESC; -- randul nou trebuie sa fie identic pe ambele fluxuri (aceeasi cantitate compensatorie)
|
|
```
|
|
|
|
**Avize** (flag real: `VANZARI.FACTURAT`):
|
|
|
|
```sql
|
|
SELECT ID_VANZARE, FACTURAT, ID_UTILFACT FROM VANZARI
|
|
WHERE ID_VANZARE IN (:lista_avize_test)
|
|
ORDER BY ID_VANZARE;
|
|
-- rulat dupa fiecare flux (vechi, apoi nou, pe date de test resetate identic), FACTURAT trebuie sa coincida rand cu rand
|
|
```
|
|
|
|
**Protocol de comparatie**, aplicabil pe fiecare tip cu document sursa (comanda: alege una din
|
|
`3,21,25,28,42,47`; avize: `4`):
|
|
1. reseteaza datele de test la aceeasi stare (aceeasi comanda/aviz, aceleasi cantitati ramase);
|
|
2. factureaza partial pe fluxul **vechi** (cod actual), noteaza rezultatul interogarilor de mai sus;
|
|
3. reseteaza din nou la aceeasi stare initiala;
|
|
4. factureaza identic (aceleasi linii, aceleasi cantitati) pe fluxul **nou** (dupa Pasii 1-2 din
|
|
sectiunea 7), noteaza acelasi rezultat;
|
|
5. compara — trebuie sa fie identic, inclusiv pe cazul limita de la sectiunea 5 (adauga-si-sterge
|
|
inainte de salvare) si pe cazul avizelor multiple agregate intr-o singura factura (sectiunea 3.3,
|
|
riscul de marcare in bloc).
|
|
|
|
**Zero cazuri testate azi nu e dovada** (memorie de proiect) — protocolul de mai sus cere minim un
|
|
caz per tip din lista, plus cazul limita, nu doar "a mers o data".
|
|
|
|
---
|
|
|
|
## 9. Riscuri si ce ramane de decis de Marius
|
|
|
|
- **Corectia sectiunii 0/6**: pasul 5 al proiectarii punctului 1 (`s4_cautare_articole_server.md:417-424`)
|
|
presupune ca `23,41` n-au bookkeeping de pastrat — gasit aici ca au Rol B activ. **De confirmat cu
|
|
Marius daca punctul 1 se re-deschide pentru aceasta corectie sau ramane cum e, cu mentiunea ca
|
|
aplicarea pe `23,41` asteapta finalizarea punctului 2** (asa cum recomand la Pasul 5, sectiunea 7).
|
|
- **Asimetria `do_modifica` fata de `do_adauga_articol`/`do_sterge`** (sectiunea 1.3, grupul
|
|
`1,22,29,2`-lista nu se ajusteaza la editarea cantitatii unei linii existente) — **preexistenta**,
|
|
nu introdusa de decuplare. De decis daca varianta noua (Rol B pe server, sectiunea 4c) trebuie sa
|
|
reproduca exact aceasta asimetrie (plafonul nu se recalculeaza corect la editare pe aceste tipuri,
|
|
ca azi) sau sa o corecteze ca efect secundar al recalcularii la cerere (care, prin natura ei, ar
|
|
elimina automat asimetria — orice cerere de plafon citeste starea curenta, indiferent daca a fost
|
|
o adaugare sau o editare). **Recomandare: lasat sa se corecteze de la sine** (comportament mai
|
|
corect, cost zero suplimentar, dar semnaleaza explicit ca S4 schimba acest comportament punctual,
|
|
nu doar "decupleaza" — de mentionat in changelog daca se alege aceasta cale).
|
|
- **Riscul de concurenta multi-utilizator, semnalat ca beneficiu la sectiunea 4b, dar nediscutat cu
|
|
Marius**: varianta recomandata face `crsarticole` local sa nu mai poata diverge de Oracle *la
|
|
scriere*, dar tot poate divarge *in timpul editarii* (doi operatori pe aceeasi comanda, unul
|
|
vede plafonul invechit pana la urmatoarea lui adaugare/editare). Nu e un risc nou introdus — e
|
|
identic cu azi pe cursorul static incarcat o data la deschidere — dar varianta (c) il reduce (cere
|
|
plafonul proaspat la fiecare adaugare, nu o singura data la deschidere), fara sa-l elimine complet
|
|
(ramane fereastra intre "am cerut plafonul" si "am scris linia"). De mentionat ca imbunatatire, nu
|
|
garantie.
|
|
- **Cont Rol B pe contract `26,52`**: nu s-a gasit dovada nici de prezenta, nici de absenta bookkeeping
|
|
pe aceste doua tipuri in codul citit (sectiunea 6) — de verificat separat inainte de a le include in
|
|
Pasul 3/4 al implementarii, nu de presupus ca urmeaza grupul `2,6`.
|
|
- **Functiile Oracle noi (Pas 1, sectiunea 7) sunt o extragere, nu o duplicare** — dar tot inseamna cod
|
|
PL/SQL nou in `PACK_FACTURARE`, care trebuie revizuit separat de cineva familiar cu pachetul
|
|
(schema exacta a `JOIN`-urilor din `inchide_comanda`, reprodusa aici din citire, nu din executie
|
|
reala pe Oracle — verificarea Pasului 1 din sectiunea 7 e obligatorie inainte de a continua).
|
|
|
|
## Handoff
|
|
|
|
Cercetare incheiata in aceasta sesiune. Toate cele 9 puncte cerute sunt acoperite, cu citate
|
|
`fisier:linie` verificate direct pe fisierele reale (`ofacturare.vc2`, nu `.bak`; corpul
|
|
`PACK_FACTURARE.sql`, nu spec-ul comentat). Descoperirea centrala (Rol A vs Rol B, si corectia asupra
|
|
pasului 5 al punctului 1 pentru `23`/`41`) nu era vizibila din raportul punctului 1 — acela trata
|
|
`crsarticole` ca un singur registru omogen; aici s-a aratat ca sunt doua mecanisme cu scopuri
|
|
diferite, unul (Rol A) recalculat oricum de Oracle la scriere si deci usor de mutat pe server fara
|
|
pierdere de paritate, celalalt (Rol B) un plafon UI care nu alimenteaza nicio decizie de business.
|
|
|
|
Niciun fisier de cod atins, niciun `git_sync.ps1`/`txt2vcx.ps1` rulat, nicio scriere pe Oracle (doar
|
|
`Read`/`Select-String`/`grep` pe fisiere de pe disc).
|