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

11 KiB

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.