Files
roafacturare/docs/qa_factura_harta.md
2026-09-17 22:44:24 +03:00

162 lines
11 KiB
Markdown

# Harta cod: adaugare/editare FACTURA si AVIZ (read-only)
Sursa: text FoxBin2Prg deja in-tree (`.vc2`/`.prg`), citit cu `Grep`/`Read` si indexat cu
`vfp_symbols.ps1` (`-CacheRoot/-ProjectRoot D:\ROA\ROAFACTURARE`,
`-IndexFile D:\ROA\_vfp_textcache\roafacturare\_symbols.tsv`). Nicio modificare de fisier.
## 1. Formulare si clase implicate
Nu exista formular `.scx` pentru factura/aviz in acest proiect (`.scx`-urile din arbore sunt
doar utilitare: `frm_borderou_facturi.scx`, `frm_import_efactura.scx` etc.). Ecranul de
adaugare/editare e o **clasa `.vcx` instantiata prin `Createobject()`**, nu un `DO FORM`:
- **`frm_facturare_articole2`** — `COMUN\clase\ofacturare.vcx` (text `COMUN\clase\ofacturare.vc2:15936-22614`).
Formularul unificat, curent, folosit in productie pentru factura SI aviz. Instantiat in
`COMUN\programe\ofacturare.prg:248` (`ofrmdetaliifactura = Createobject('frm_facturare_articole2')`)
si in `COMUN\clase\ofacturare_comun.vc2:4028`, doar cand `llFacturareNoua` e adevarat
(`COMUN\programe\ofacturare.prg:246-248`).
- **`frm_facturare_articole`** — acelasi fisier, `ofacturare.vc2:11123-15936`. Varianta mai veche;
in cod de productie nu mai e instantiata (doar in probele din
`COMUN\utile\Teste\facturare_unificat\*.prg` si `COMUN\utile\Teste\editare_factura\creeaza_documente_s8.prg:571`,
`emite_document_stoc_s93.prg:264`) — ramane in clasa dar calea curenta e `frm_facturare_articole2`.
- **Flux vechi, ne-unificat** (`llFacturareNoua = .F.`, `ofacturare.prg:255-266`) — alege un
formular de antet separat pe tip de document, fara grila unificata de articole:
- `frm_date_aviz_lucrare` — `ofacturare.vc2:7775` — pentru `tnTip` 27 sau 30.
- `frm_date_factura` — `ofacturare.vc2:8637` — pentru `tnTip < 21` sau in `(45,48,49,51,52)`.
- `frm_date_aviz` — `ofacturare.vc2:6721` — altfel (aviz).
- **`oAntetFacturare`** (`Define Class ... As Custom`) — `COMUN\programe\ofacturare_antet.prg:14`.
Logica de antet a lui `frm_facturare_articole2`: cautari (`do_cauta_*`), validare
(`valideaza_antet`), schimbarea tipului de document (`alege_tipdoc`, `schimba_tipdoc`).
Header-ul fisierului (`ofacturare_antet.prg:1-7`) spune explicit ca a fost **portata din
`frm_date_factura`/`frm_date_aviz`** cand s-a facut unificarea. Instantiata in
`frm_facturare_articole2.Init`: `This.oAntet = Createobject('oAntetFacturare')` (`ofacturare.vc2:21365`).
- **`oDateFactura`** (`poDate`) — `COMUN\programe\ofacturare_comun.prg:131`. Obiectul de stare al
documentului curent (creat in `ofacturare.prg:196`); tine `nIdTipDoc`, `nIdTipDocFactura`(=5),
`nIdTipDocAvizExpeditie`(=6), `id_gestiune_init`, `listaid`, `id_pol`, `zi_curs` etc.
- **`ofacturare_editare.prg`** (`COMUN\programe\ofacturare_editare.prg`, 1444 linii) — flux **separat**,
pentru editarea liniilor unei facturi/aviz deja emise (nu formularul de creare):
`IncarcaAntetFacturaEditare` (63), `IncarcaLiniiFacturaEditare` (209),
`PregatesteArticoleFacturaEditare` (605), `ScrieArticoleFacturaEditate` (741),
clasa `ArticoleNotaEditor As Custom` (1226). Inregistrat via
`Set Procedure To ofacturare_editare.prg Additive` (`Programe\roafacturare.prg:219`).
- **`combosql_cautare`** — clasa de baza pentru comboboxurile de cautare din grid (vezi pct. 2),
`ofacturare.vc2:407-531`, mostenind `combosql As combobox` din `COMUN\clase\_cb_base.vc2:519`.
## 2. Zona de introducere articole (cautare cod material / denumire)
In grila `grd_factura` a lui `frm_facturare_articole2`, coloanele au cate un combobox de tip
`combosql_cautare`:
- `grd_factura.cCodMat.cboCodmat` — `ADD OBJECT` la `ofacturare.vc2:17524-17537`,
`ncharcountbegin = 2`, `pcursorname = crsCodmat`, `pfieldactiv = codmat`.
- `grd_factura.cDenumire.cCboDenumire` — `ADD OBJECT` la `ofacturare.vc2:17560-17574`,
`ncharcountbegin = 2`, `pcursorname = crsDenumire`, `pfieldactiv = denumire`.
**Pragul de caractere**: logica e in `combosql_cautare.refreshdata` (`ofacturare.vc2:463-496`):
```
lnTip = Iif(Type('tnTip') = 'N', m.tnTip, 0)
If m.lnTip <> 0 And Len(Alltrim(This.cSearchString)) <= This.nCharCountBegin
Return .T. && nu cauta, iese
Endif
```
Cu `ncharcountbegin = 2` pe ambele combo-uri, filtrarea porneste abia cand
`Len(cSearchString) > 2`, adica de la **al treilea caracter tastat**.
**Interactivechange/Keypress**: nu sunt suprascrise in `combosql_cautare` — vin din parintele
`combosql` (`COMUN\clase\_cb_base.vc2:519`):
- `InteractiveChange` (linia 606-608): doar reseteaza `cSearchString`.
- `KeyPress` (linia 610-776): construieste `csearchstring` caracter cu caracter (sageti,
Del/Backspace tratate separat), apoi cheama `This.RefreshData(1)` = cautare "incepe cu"
(linia 720) sau, la Ctrl+Enter, `RefreshData(2)` = cautare "contine" (liniile 636-649).
**Filtrul/SQL aplicat**: `refreshdata` seteaza `cfiltrucod`/`cfiltruden` in functie de tip
(`ofacturare.vc2:476-489`):
```
Case lnTip = 1 (incepe cu): cfiltrucod/cfiltruden = cSearchString + '%'
Case lnTip = 2 (contine): cfiltrucod/cfiltruden = '%' + cSearchString + '%'
```
apoi `cursor_preturi_call()` (`ofacturare.vc2:446-461`) construieste apelul catre pachetul
Oracle `pack_facturare.cursor_preturi(?poDate.zi_curs,?poDate.tip,?poDate.id_valuta,
?poDate.id_gestiune_init sau ?poDate.listaid,?gnLuna,?gnAn,?gnIdUtil,?gnIdSucursala,
?pcFiltruCod,?pcFiltruDen)` (sau `pack_facturare.cursor_gestiune(...)` cand `poDate.tip = 41`),
executat prin `goExecutor.oExecuta` in `selectdata` (linia 514-529). Corpul PL/SQL al
pachetului nu e in acest repo VFP (schema Oracle) — nu l-am putut citi read-only de aici.
Proprietatile `csourcesql`/`csourcewhere` de pe `ADD OBJECT` (ex. `select codmat, denumire, ...
from vnom_articole ... where inactiv = 0`) sunt doar sablonul design-time al cursorului gol
(`creeaza_cursor_gol`, linia 426), nu interogarea reala rulata la tastare.
**Sincronizare cod<->denumire** dupa alegere: `grd_factura.cCodMat.cboCodmat.LostFocus`
(`ofacturare.vc2:22088-22109`) si perechea ei `cDenumire.cCboDenumire.LostFocus`
(22115-22136) cheama `thisform.do_adauga_articol_cautat(loArticol, loArticol.cantitate)`
si fortez re-creerea cursorului celeilalte combo (seteaza `cSearchString` cu valoarea gasita
si reseteaza `RowSource`), ca sa afiseze articolul ales in ambele coloane.
## 3. Lista de preturi / politici de pret
- `poDate.id_gestiune_init` e parametrul implicit trimis la `pack_facturare.cursor_preturi`;
cand `poDate.tip = 45` se trimite `poDate.listaid` in loc (`ofacturare.vc2:453-457`).
- De ce poate aparea un articol de mai multe ori: interogarea serverului (Oracle,
`pack_facturare.cursor_preturi`) uneste nomenclatorul de articole cu politicile de pret —
confirmarea client-side ca politicile sunt cheia vine din `modifica_lista_preturi`
(`ofacturare.vc2:21735-21837`), care interogheaza direct tabelele de politici:
`Select id_pol_art, id_venchelt, proc_tvav from crm_politici_pret_art where id_pol = ...
and id_articol = ...` (linia 21755) si scrie prin `pack_preturi.adauga_politica_pret_art`/
`pack_preturi.modifica_pret_pol_pret_art` (21779, 21792). Corpul exact al join-ului din
`cursor_preturi` e in PL/SQL, nevizibil din acest working copy VFP — afirmatia despre
"un rand per politica aplicabila" e o inferenta din tabelele folosite, nu o citire directa
a interogarii.
## 4. Zona de jos: controale de discount/totaluri
Toate sunt copii directe ale formularului `frm_facturare_articole2` (fara container
intermediar), clasa `_textbox` din `_baza.vcx`, asezate sub grila (Top ~742-792):
| Obiect | Top | Left | Width | Anchor | Note |
|---|---|---|---|---|---|
| `tx_total_baza_nat` (18123) | 745 | 17 | (implicit) | 4 | ReadOnly, `ControlSource=thisform.nbazaron` |
| `tx_total_baza_val` (18137) | 789 | 17 | (implicit) | 4 | ReadOnly, `thisform.nbazaval` |
| `tx_disc_factura_nat` (18082) | 745 | 351 | (implicit) | 4 | ReadOnly, `thisform.ndiscfactron` |
| `tx_disc_factura_procent` (18096) | 767 | 542 | 54 | 4 | editabil, `Value=0` |
| `tx_disc_factura_val` (18109) | 789 | 351 | (implicit) | 4 | ReadOnly, `thisform.ndiscfactval` |
| `tx_total_tva_nat` (18181) | 742 | 689 | (implicit) | 12 | ReadOnly, `thisform.ntvaron` |
| `tx_total_tva_val` (18195) | 792 | 689 | (implicit) | 12 | ReadOnly, `thisform.ntvaval` |
| `tx_total_factura_nat` (18151) | 742 | 833 | (implicit) | 12 | ReadOnly, FontBold, `thisform.ntotalron` |
| `tx_total_factura_val` (18166) | 792 | 835 | (implicit) | 12 | ReadOnly, FontBold, `thisform.ntotalval` |
(linii = `COMUN\clase\ofacturare.vc2`). `Anchor=4` = ancorat de Bottom (coboara odata cu
formularul); `Anchor=12` (4+8) = Bottom+Right, pe coloana totalurilor din dreapta. Niciun
`Height`/`Width` explicit pe majoritatea — mostenesc default-ul clasei `_textbox`; doar
`tx_disc_factura_procent` are `Width=54` explicit. Nu am gasit cod care repozitioneaza
aceste controale la runtime in `frm_facturare_articole2` — pozitionarea e strict design-time
+ `Anchor` (fara logica de `Resize`/`Move` pe ele; `Resize` al formularului, linia
21847-21854, nu le atinge explicit).
## 5. Diferente factura vs aviz pe `frm_facturare_articole2`
- **Discriminator unic**: `poDate.nIdTipDoc`, comparat cu `poDate.nIdTipDocFactura` (=5) /
`poDate.nIdTipDocAvizExpeditie` (=6). Helper: `oAntetFacturare.EsteAviz()`
(`ofacturare_antet.prg:16-18`): `Return poDate.nIdTipDoc = poDate.nIdTipDocAvizExpeditie`.
- **Setare initiala**, dupa `tnTip` primit la deschidere (`COMUN\programe\ofacturare.prg:196-206`):
```
Case tnTip = 27 or 30 -> nIdTipDoc = 6 (AVIZ)
Case tnTip < 21 sau tnTip in (45,48,49,51,52) -> nIdTipDoc = 5 (FACTURA)
Otherwise -> nIdTipDoc = 6 (AVIZ)
```
- **Schimbare interactiva**: combo `clb_fdoc.cboFdoc` -> `do_schimba_tipdoc`
(`ofacturare.vc2:20046-20071`) -> `oAntet.alege_tipdoc(valoare_combo)` mapeaza textul
("FACTURA"/"AVIZ"/"PROFORMA"/"BON FISCAL") pe id-ul de tip, apoi `oAntet.schimba_tipdoc(id)`
(`ofacturare_antet.prg:889-923`) actualizeaza `poDate.nIdTipDoc` si seriile. Exista o garda:
daca documentul e proforma si au fost deja adaugate articole, schimbarea tipului e blocata
cu `amessagebox` (`ofacturare.vc2:20050-20055`).
- **Ramuri `EsteAviz()` in `oAntetFacturare`** (aceleasi metode de cautare/validare servesc
ambele tipuri, dar cu cai diferite): `do_cauta_altele` (20-45), `do_cauta_client` (97-100),
`do_cauta_comanda` (240-243), `do_cauta_contract` (296-299), `do_cauta_gestiune_init`
(518-521), `do_cauta_lucrare` (553-556), `do_cauta_sectie` (633-636), `do_cauta_venchelt`
(708-711), `valideaza_antet` (762-766) — toate in `COMUN\programe\ofacturare_antet.prg`.
- **Fluxul vechi (ne-unificat)** folosea deja formulare separate per tip pentru antet
(`frm_date_factura` / `frm_date_aviz` / `frm_date_aviz_lucrare`, vezi pct. 1) — unificarea
descrisa in header-ul `ofacturare_antet.prg` a mutat logica lor comuna intr-o singura clasa,
ramura aleasa la runtime pe `poDate.nIdTipDoc`.