12 KiB
Cercetare: factura de retur ca document de sine statator (tip 8/9) + aviz retur (24)
Corectie fata de retur_si_lista_preturi.md: acea cercetare a documentat corect But_retur
(retur de articole in interiorul unei facturi normale, tip 1/5/7/10 — selectie per articol).
Aici e documentat mecanismul separat: factura de retur ca document propriu (tip 8 = retur lei,
tip 9 = retur valuta), unde utilizatorul alege facturile sursa la nivel de document, iar linia
de articole se populeaza integral din acele facturi.
1. Punctul de intrare si cursorul Oracle
Tile-ul de pe ecranul principal de facturare, Page2.Cw1 (COMUN\clase\ofundal_facturare.vc2:882-884):
PROCEDURE Page2.Cw1.do_actiune
DO facturare_lista_de_preturi IN oproceduri_facturare.prg
ENDPROC
facturare_lista_de_preturi (COMUN\programe\oproceduri_facturare.prg:114-116) = Do politica.mpr,
care ruleaza meniul shortcut generat din Meniuri\politica.mn2:14-15,45-46:
DEFINE BAR 2 OF Shortcut PROMPT "\<Retur factura in lei"
ON SELECTION BAR 2 OF Shortcut factureaza(8)
...
DEFINE BAR 9 OF Shortcut PROMPT "Re\<tur factura in valuta"
ON SELECTION BAR 9 OF Shortcut factureaza(9)
Deci factureaza(8) / factureaza(9) sunt apelate direct, fara toFactura (nu e copiere).
Comentariul din factureaza (COMUN\programe\ofacturare.prg:134-135) confirma explicit numerotarea:
** (25007,8) - retur factura in lei ( 25017 )
** (25008,9) - retur factura in valuta ( 25018 )
In Do Case pe tnTip din factureaza (ofacturare.prg:306-307):
Case Inlist(tnTip, 8, 9, 24) && 8,9 = facturi de retur, 24 = aviz retur
lcSqlCursor = [{call ] + gcS + [.pack_facturare.cursor_retur(?poDate.in_valuta,?poDate.listaid,?gnIdUtil)}]
executat prin goExecutor.oExecute(lcSqlCursor, [crsarticole]) (ofacturare.prg:310-311) — acelasi
cursor crsarticole folosit si de cursor_preturi/cursor_comanda/cursor_contract pentru
celelalte tipuri. Nota: Do Case are inaintea acestei ramuri o ramura separata Case m.llCopiere
(ofacturare.prg:268) care foloseste pack_facturare.cursor_retur_document(...) — dar llCopiere
e .T. doar cand factureaza() primeste un al doilea parametru toFactura (obiect), adica la
copiere de document, nu la intrarea normala prin meniu pentru tip 8/9 (llCopiere = (Type('toFactura')='O'),
ofacturare.prg:111). Vezi si punctul 6.
nIdTipDoc = 5 (FACTURA, ofacturare.prg:193, tnTip<21), formularul de date antet este
frm_date_factura (ofacturare.prg:222-223, acelasi caz tnTip<21).
2. Alegerea facturilor sursa
Formularul frm_date_factura (COMUN\clase\ofacturare.vc2:8482) are metoda dedicata
do_cauta_facturi (ofacturare.vc2:9173-9212):
Case Empty(Nvl(poDate.id_client,0))
amessagebox("Nu ati ales clientul!",...)
Case Empty(Nvl(poDate.id_valuta,0)) And poDate.tip = 9
amessagebox("Nu ati ales valuta!",...)
OTHERWISE
lcXMLFacturi = caut_facturi_multiple_client(poDate.id_client,poDate.in_valuta,poDate.id_valuta,.T.)
If !Empty(lcXMLFacturi) and gnButon = 1
Xmltocursor(lcXMLFacturi, "crsFacturiTemp")
...
poDate.listaid = cursor2lista("crsFacturiTemp", "id_vanzare", ",")
poDate.descriere = cursor2lista("crsFacturiTemp", "numar_act", ",")
...
poDate.text_aditional = Iif(poDate.tip=8,[RETUR FACTURA ],[REFUND INVOICE FOR ]) + poDate.descriere
Dialogul e caut_facturi_multiple_client (COMUN\programe\oproceduri_facturare.prg:2091-2121),
un browse generic cauta_alfa cu titlu "Alegeti facturile (mouse-click pe numar sau apasati SPACE)"
(:2104, selectie multipla — lnTipReturn = Iif(tlFacturiMultiple,1,0), apelat cu tlFacturiMultiple=.T.).
Criteriile SQL (:2108-2111):
lcSelect = [select serie_act,numar_act,data_act,dataora,id_vanzare from ] + gcS + [.fact_vfacturi ]
lcFiltruOriginal = [sters=0 and tip in (1,2,3,4,5,6,10,-1,-2,-3,-4,-11) ] + gcCondSucursala +
lcFiltruPart + lcFiltruValuta
adica client (id_part, obligatoriu ales inainte) si valuta (in_valuta/id_valuta, doar
daca tnInValuta e setat) — nu exista filtru SQL pe perioada sau serie/numar in interogare (coloanele
Serie act, Numar act, Data, Data inreg. sunt doar afisate/sortabile in browse-ul generic, filtrarea
pe ele e comportament generic al cauta_alfa, neverificat mecanismul intern). Sursa exclude explicit
tipurile de retur (tip in (1,2,3,4,5,6,10,-1,-2,-3,-4,-11)) — nu se poate face retur dintr-un retur.
Selectie multipla: se aduna prin cursor2lista("crsFacturiTemp","id_vanzare",",") intr-un
singur string CSV in poDate.listaid (id-urile facturilor alese), respectiv
cursor2lista(...,"numar_act",",") in poDate.descriere (afisat apoi pe antetul liniilor si in
titlul formularului de articole, ofacturare.vc2:15022-15023: [ * Retur pentru facturile : ] + poDate.descriere).
Validare obligatorie inainte de a continua (frm_date_factura.inainte_de_do_termin, ofacturare.vc2:9523-9526):
Case Empty(Nvl(poDate.descriere,[])) And Inlist(poDate.tip,8,9)
amessagebox("Nu ati ales factura/facturile pentru care se face returul!",48,"Atentie")
3. Popularea liniilor: gestiune si pret
pack_facturare.cursor_retur e un wrapper subtire peste implementarea reala
(ff_2026_08_06_10_COMUN_PACK_FACTURARE.sql:3943-3956):
PROCEDURE cursor_retur(V_IN_VALUTA, V_LISTAID, V_ID_UTIL, V_CURSOR) IS
V_COPIERE NUMBER := 0;
V_PROFORMA NUMBER := 0;
BEGIN
pack_facturare.cursor_retur_document(V_IN_VALUTA, V_LISTAID, V_COPIERE, V_PROFORMA, V_ID_UTIL, V_CURSOR);
END;
cursor_retur_document (:3958-4071) selecteaza direct din VANZARI_DETALII (liniile facturilor
originale), filtrat pe A1.ID_VANZARE IN (SELECT ID_VANZARE FROM CRS) unde CRS e lista de id-uri
din V_LISTAID (= poDate.listaid, adica exact facturile alese la pasul 2, :4059-4064):
FROM VANZARI_DETALII A1 LEFT JOIN VANZARI_CURSURI A2 ON A1.ID_VANZARE = A2.ID_VANZARE ...
WHERE A1.STERS = 0 AND A1.ID_VANZARE IN (SELECT ID_VANZARE FROM CRS)
Coloane relevante, confirmate ca provin direct din linia facturii originale:
- gestiune:
nvl(A.ID_GESTIUNE, 0) as ID_GESTIUNE(:4034), dinA1.ID_GESTIUNE(VANZARI_DETALII.ID_GESTIUNEal liniei originale,:4055) — confirma afirmatia lui Marius: gestiunea vine din factura sursa. - pret de achizitie:
A.PRET_ACHIZITIE(:4035), dinA1.PRET_ACHIZITIE(:4056) — preluat neschimbat din linia originala, fara recalcul de curs. - pret: coloana
PRET(:4016-4024) e pretul de vanzare al liniei originale, recalculat pe cursul valutar daca moneda nu e nationala (ROUND(A.CURS * ROUND(A.PRET,...) / A.MULTIPLICATOR, ...), altfelROUND(A.PRET,...); plusPRET_VAL(:4025-4030) — valoarea in valuta straina, cand e cazul. GESTIONABIL(:4002-4009) pentru cazulV_COPIERE=0(retur, ramura efectiv folosita decursor_retur):A.GESTIONABIL=NVL2(A1.ID_GESTIUNE,1,0)(subselect intern,:4048) — gestionabil doar daca linia originala avea gestiune.
Concluzie Q3: ambele preturi trec prin, atat cel de vanzare (PRET/PRET_VAL, ajustat pe curs)
cat si cel de achizitie (PRET_ACHIZITIE, neschimbat) — plus gestiunea originala (ID_GESTIUNE).
Numele coloanelor Oracle -> cate un camp cu acelasi nume in cursorul VFP crsarticole (maparea VFP
exacta camp-cu-camp nu a fost trasata pana in crsfactura; nu era necesara pentru raspuns).
4. Ce se poate face manual (frm_facturare_articole, COMUN\clase\ofacturare.vc2:10968)
- Stergere linie:
do_sterge(:14608-14693) nu e restrictionat pe tip; pentru retur (Case Inlist(poDate.tip,8,9,24),:14658-14659) cantitatea stearsa se reintoarce in cursorul sursa (Replace cantitate With cantitate - poArticol.cantitate For id_c = poArticol.id_c) — deci da, se pot sterge linii aduse, iar cantitatea redevine disponibila pentru re-adaugare. - Retur partial (modificare cantitate): coloana de cantitate din grila sursa isi schimba titlul
in "Cant. max. de returnat" pentru tip 8/9 (
:15236-15239); validarea indo_verifica_articol(:14743-14754,llRetur = Inlist(poDate.tip,8,9,24)) respinge doar cazul in care cantitatea ceruta ar depasi maximul returnabil — utilizatorul poate introduce orice cantitate <= maxim, deci da, retur partial e posibil.do_modifica(:13746-13914) trateaza explicitInlist(poDate.tip,8,9,24)la:13788,13895fara blocaj suplimentar. - Adaugare linie care NU e in facturile sursa:
do_adauga_articol(:12813-13086) preia articolul mereu din cursorul sursa al gridului (lcCursor = [crsarticole], apoiSelect (lcCursor) / Scatter Name poArticol,:12843-12851) — pentru tip 8/9 acestcrsarticolee chiar rezultatulcursor_returde la punctul 3, deci contine doar liniile facturilor alese. Nu exista pe acest formular o cale de a alege un articol din lista de preturi completa candpoDate.tipe 8/9 (spre deosebire de contract/comanda undecrsarticoleramane lista de preturi libera) — nu se poate adauga o linie in afara facturilor sursa, prin design-ul continutului cursorului, nu printr-o validare explicita de interzicere.
5. Aviz de retur (tip 24)
Acelasi mecanism de baza: Inlist(tnTip,8,9,24) la ofacturare.prg:306-307 (acelasi
pack_facturare.cursor_retur) si aceleasi ramuri Inlist(poDate.tip,8,9,24) in
frm_facturare_articole (do_adauga_articol:12863, do_alege_stoc:13230, do_modifica:13788,13895,
do_verifica_articol:14743, do_sterge:14658). Diferenta: la antet, nIdTipDoc = 6 (AVIZ, ramura
Otherwise la ofacturare.prg:194-195, pentru ca 24 nu e <21 si nu e in Inlist(45,48,49,51,52)),
iar formularul de date e frm_date_aviz (ofacturare.prg:225, ramura Otherwise), nu
frm_date_factura. Punctul de intrare pentru tip 24: emitere_aviz_clienti cu tnTip=7
(COMUN\programe\oproceduri_facturare.prg:221-222, factureaza(24) && Retur aviz), apelat din
tile-ul Page3.Cw1 -> aviz_clienti.mpr (neverificat detaliat, nu s-a insistat conform cerintei).
6. Relatia cu But_retur
Mecanisme complet separate, confirmate pe cod:
- Vizibilitate:
but_reture vizibil doar pentru tip 1/5/7/10 (ofacturare.vc2:15127,This.but_retur.Visible = .T.in ramuraCase Inlist(poDate.tip,1,5,7,10)) — pe un document tip 8/9 acest buton nu exista deloc in UI (ramura lui laInite alta,:15236). - Formular de alegere a facturii sursa:
But_returfolosestecaut_facturi_multiple_client_articol(oproceduri_facturare.prg:2124-2159, filtrat suplimentar peb.id_articol = ?pnIdArticol— cautare per-articol, cu titlu simplu "Alegeti factura"), tip 8/9 document folosestecaut_facturi_multiple_client(:2091-2121, fara filtru pe articol, multi-select, titlu "Alegeti facturile ..."). Doua functii diferite, in acelasi fisier, cu semnaturi aproape identice dar interogari SQL diferite. - Procedura Oracle de populare a liniilor: documentul tip 8/9 foloseste
pack_facturare.cursor_returla nivel de document intreg (populeazacrsarticolecu toate liniile facturilor alese, punctul 3 de mai sus).But_returnu apeleazacursor_retur/cursor_retur_documentdeloc — folosestepack_facturare.cursor_gestiuni_articol_retur(ofacturare.vc2:13230, per articol individual, indo_alege_stoc) pentru a alege gestiunea/seria/lotul de returnat pentru articolul deja selectat din lista de preturi a facturii normale curente. - Punct comun: niciunul direct. Singurul element comun e conventia de semn a cantitatii
(
Iif(Inlist(poDate.tip,8,9,24),(-1),1), folosita si indo_alege_stocalBut_retur,:13279-13309, si pe formularulfrm_facturare_articole2,:17574-17585) si textul UI ("cant. max. de returnat"). Concluzie: sunt doua fluxuri de cod independente care ating aceeasi clasa de formular (frm_facturare_articole) dar prin metode si proceduri Oracle diferite.