16 KiB
Cercetare: legatura "linie de retur -> linie/factura originala" se persista undeva?
Verdict (10 randuri)
NU se persista la nivel de linie. PARTIAL la nivel de document, si numai cand a fost aleasa o
singura factura sursa. Cursorul Oracle care populeaza grila de retur pentru N.1 (documente tip
8/9/24, pack_facturare.cursor_retur_document, ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:3949-4062)
nu selecteaza deloc A1.ID_VANZARE sau A1.ID_VANZARE_DET din VANZARI_DETALII — legatura cu
factura/linia sursa se pierde chiar in query-ul care aduce datele in VFP, inainte sa existe vreo
sansa sa fie afisata. INSERT-ul final in VANZARI_DETALII (scrie_in_vanzari, documentat deja in
rec_cale_vanzari_detalii.md:105-113) nu are nicio coloana de tip sursa. Exista in schimb un link
la nivel de document intreg: VANZARI_CORESP (TIP=3, ID_VANZARE_FACT=documentul de retur,
ID_VANZARE_AVIZ=fiecare factura sursa aleasa) — dar cand utilizatorul alege mai multe facturi
sursa deodata (selectie multipla, suportata explicit de dialog), acest link iti da multimea de
facturi posibile, nu factura exacta a liniei. Pentru N.2 (But_retur, per articol) nu exista niciun
document nou scris separat — returul e o linie in factura normala curenta, iar "sursa" e verificata
doar la nivel de stoc (RUL/STOC filtrat pe COD legat de VANZARI.ID_VANZARE), nu persista ca
atribut al liniei noi. Concluzie pentru S4f: se livreaza fara coloana de provenienta pe linie;
cel mult se poate afisa, cand e un singur document sursa, "factura de retur X provine din factura Y"
la nivel de document (din VANZARI_CORESP), nu per linie.
1. Ce face Oracle cu poDate.listaid
Doua cai, in functie de mecanism:
N.1 (document, tip 8/9/24): poDate.listaid = CSV de id_vanzare (facturile alese la pasul de
cautare multipla, ofacturare.vc2:9200: poDate.listaid = cursor2lista("crsFacturiTemp", "id_vanzare", ",")).
Ajunge la Oracle ca parametru V_LISTAID in pack_facturare.cursor_retur(V_IN_VALUTA, V_LISTAID, V_ID_UTIL, V_CURSOR) (ff_...sql:3934-3947), care e doar un wrapper peste
cursor_retur_document(V_IN_VALUTA, V_LISTAID, V_COPIERE=0, V_PROFORMA, V_ID_UTIL, V_CURSOR)
(:3949-4062). Acolo V_LISTAID devine CTE-ul CRS (:3962-3964):
WITH CRS AS (SELECT X as ID_VANZARE FROM table(charn2collection(V_LISTAID, V_SEPARATOR)))
folosit doar ca filtru: WHERE A1.STERS = 0 AND A1.ID_VANZARE IN (SELECT ID_VANZARE FROM CRS)
(:4054-4055). Filtru, nu persistare — dupa acest WHERE, ID_VANZARE nu mai apare deloc in
lista de coloane a SELECT-ului extern (:3965-4028: ID_C (=ROWNUM, sintetic), ID_ARTICOL,
LOT, SERIE, ID_POL, preturi, GESTIONABIL, CANTITATE, ID_GESTIUNE, PRET_ACHIZITIE... dar
nu ID_VANZARE si nu ID_VANZARE_DET). Cursorul primit de VFP in crsarticole nu are, deci, nicio
coloana care sa spuna din ce factura/linie vine randul.
Separat, la scrierea efectiva a documentului de retur, scrie_corespondente_vanzari(3)
(:14834-14836, apelata din finalizeaza_factura) foloseste tot pack_facturare.clistaid
(= acelasi poDate.listaid, tinut in variabila de sesiune a pachetului) ca sa scrie in
VANZARI_CORESP (:15481-15516):
INSERT INTO VANZARI_CORESP (ID_VANZARE_FACT, ID_VANZARE_AVIZ, TIP)
SELECT pack_facturare.nid_vanzare, ID_VANZARE, 3
FROM VANZARI WHERE ID_VANZARE IN (SELECT X FROM table(charn2collection(V_LISTAID,',')));
Asta scrie cate un rand per factura sursa aleasa (nu per linie), cu noul document de retur ca
ID_VANZARE_FACT si fiecare sursa ca ID_VANZARE_AVIZ (numele coloanei e generic, reutilizat si
pentru perechi aviz-factura, TIP=1/2, vezi sterge_factura:5452-5457,5582-5585).
N.2 (But_retur, per articol): poDate.listaid = "ID_ARTICOL:ID_VANZARE" (una sau mai multe
perechi CSV, ofacturare.vc2:12888: thisform.cListaIdArticoleRetur = ... + STR(poArticol.id_articol) + ':' + poDate.listaid,
trimis la Oracle la :13978). In pack_facturare (verificat in ramura WHEN V_CANTE < 0 and pack_facturare.clistaid is not null and instr(pack_facturare.clistaid, ':') > 0,
ff_...sql:8142-8212) e folosit ca filtru pentru calculul cantitatii disponibile de retur din
rulaj (RUL), NU ca sa scrie o legatura:
AND A.COD IN (SELECT COD FROM VANZARI WHERE ID_VANZARE IN
(SELECT id_vanzare FROM (SELECT CAST(GETWORDNUM(id_articol_id_vanzare,1,':') AS NUMBER(20,0)) id_articol,
CAST(GETWORDNUM(id_articol_id_vanzare,2,':') AS NUMBER(20,0)) id_vanzare
FROM (SELECT x AS id_articol_id_vanzare FROM table(cast(CHARC2COLLECTION(pack_facturare.clistaid,',') AS char_tab))))
WHERE id_articol = V_ID_ARTICOL))
Foloseste listaid doar ca sa restranga RUL la miscarile de stoc (A.COD) legate de factura
aleasa, ca sa calculeze cantitatea inca disponibila in gestiune din acea vanzare pentru articolul
respectiv (tab_stoc, tip=1 in acel SELECT). Nu se scrie nicio linie noua de legatura in vreun
tabel — e folosit exclusiv ca filtru intr-un calcul de disponibil.
2. Coloana de provenienta pe VANZARI_DETALII
Nu exista. DDL-ul complet nu e disponibil in acest repo (nu exista CREATE TABLE VANZARI_DETALII in
docs/; confirmat deja de cercetari anterioare — docs/cercetare/discount_verificare2.md:104-111,
COMUN\docs\cercetare\rec_tva_vanzari.md:19), dar structura efectiva a fost verificata pe schema live in
docs/cercetare/rec_cale_vanzari_detalii.md:157-169 (interogare all_tab_columns, 08.08.2026):
35 de coloane, PK ID_VANZARE_DET (generat prin trigger TRG_VANZARI_DET_BEFOINS din
SEQ_VANZARI_DETALII), FK logic ID_VANZARE. Lista coloanelor relevante citata acolo: ID_ARTICOL,
PRET, CANTITATE, PRET_CU_TVA, DISCOUNT_UNITAR, PROC_TVAV, ID_GESTIUNE, CONT,
ID_VALUTA, ID_JTVA_COLOANA, SERIE, EXPLICATIE, TAXCODE, LOT, STERS, VALIDAT,
DATAORA_VALID, ID_UTIL_VALID, ID_UTILS, DATAORAS, DIFERENTA, CUSTODIE, DESCARCAT,
PRETD, ID_VALUTAD, PRETV_ORIG, ID_CTR, ID_RATA. Nicio coloana de tipul
ID_VANZARE_SURSA, ID_DETALIU_SURSA, ID_FACT_SURSA, ID_VANZARE_RETUR, ID_ORIGINAL sau orice
self-referinta catre o alta linie VANZARI_DETALII. Cautat explicit acele siruri in tot exportul
ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql (~28000 linii) si in COMUN\docs\ — zero potriviri
(vezi comanda de mai jos, sectiunea "Ramas de verificat").
Confirmarea independenta finala si cea mai directa: INSERT-ul care trece liniile din
VANZARI_DETALII_TEMP in VANZARI_DETALII la finalizarea oricarui document (inclusiv retur, pentru
ca scrie_in_vanzari e comun tuturor tipurilor) are lista de coloane explicita
(rec_cale_vanzari_detalii.md:105-113):
INSERT /*+ APPEND */ INTO VANZARI_DETALII
(ID_VANZARE, ID_ARTICOL, SERIE, ID_POL, CANTITATE, PRET_ACHIZITIE, PRETD, ID_VALUTAD,
PRET, PROC_TVAV, DISCOUNT_UNITAR, ID_GESTIUNE, ID_VALUTA, CONT, PRET_CU_TVA, ID_JTVA_COLOANA)
SELECT pack_facturare.nid_vanzare, ID_ARTICOL, ... FROM VANZARI_DETALII_TEMP WHERE ...
Nicio coloana sursa in lista. Cum VANZARI_DETALII_TEMP insasi e populata pentru retur din
cursor_retur_document (care, cf. punctul 1, nu aduce ID_VANZARE/ID_VANZARE_DET sursa), legatura
e pierduta cu doi pasi inainte de a ajunge la acest INSERT — nu doar "nu se scrie", ci "nu mai exista
in date la momentul scrierii".
3. Tabel separat de legatura
Da, exista, dar la nivel de document, nu de linie: VANZARI_CORESP(ID_VANZARE_FACT, ID_VANZARE_AVIZ, TIP, STERS). Folosit pentru mai multe perechi de corespondenta, disambiguizate prin
TIP:
TIP=1: factura scrisa dintr-un aviz (scrie_corespondente_vanzari(1), la facturare din aviz,:14826);TIP=2: aviz de retur (scrie_corespondente_vanzari(2),:14830, lantip=24);TIP=3: factura de retur (scrie_corespondente_vanzari(3),:14834-14836, lantip in (8,9)) — exact cazul cerut de S4f.
Verificarile de stergere din sterge_factura (:5450-5494) confirma semantica: interogheaza
VANZARI_CORESP WHERE ID_VANZARE_AVIZ = V_ID_VANZARE AND TIP = 3 pentru "exista facturi de retur pe
aceasta factura?" — deci pentru o factura normala, ID_VANZARE_AVIZ (nume generic, refolosit) e ea
insasi, iar ID_VANZARE_FACT gasit prin acea interogare e factura(le) de retur emise pe baza ei.
Invers, pentru un document de retur dat, SELECT ID_VANZARE_AVIZ FROM VANZARI_CORESP WHERE ID_VANZARE_FACT = :id_retur AND TIP = 3 da toate facturile sursa alese la emiterea acelui retur
(poate fi mai multe, cf. selectie multipla din caut_facturi_multiple_client,
docs/cercetare/factura_retur_document.md:67-85).
Limita exacta: cand pe un document de retur exista o singura factura sursa in VANZARI_CORESP,
"factura originala" e determinata fara ambiguitate pentru toate liniile documentului de retur
(pentru ca nu exista alt candidat). Cand exista mai multe (utilizatorul a bifat 2+ facturi la
cautare), VANZARI_CORESP da multimea, dar nu se poate spune care linie de retur vine din care
factura din multime — informatia care ar face diferenta (ID_VANZARE per linie in cursorul de
populare) a fost deja aruncata la pasul 1/2. Nu exista niciun tabel VANZARI_RETUR, RETUR_DETALII
sau LEGATURI_DOCUMENTE; cautate explicit in ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql si in
COMUN\docs\ — zero potriviri.
DOCUMENTE.AVIZE (coloana denormalizata pe VANZARI, nu tabel separat) e completata redundant tot
din VANZARI_CORESP (scrie_corespondente_vanzari:15505-15514, UPDATE VANZARI SET AVIZE = ...) —
tine text afisabil (serie+numar avize), nu un id structurat, si nu e populata pentru TIP=3 (doar
pentru avize, vezi apelul unic la acel UPDATE in corpul procedurii, comun tuturor V_TIP-urilor
dar cu sens practic doar pentru avize-spre-factura).
4. Cum calculeaza serverul maximul returnabil
Raspunsul e diferit pe cele doua mecanisme, si niciunul nu se sprijina pe o legatura persistata linie-la-linie:
N.1 (document): NU exista un calcul de "cat s-a mai returnat deja". Coloana pe care utilizatorul
o vede ca "Cant. max. de returnat" (ofacturare.vc2:15238,15243 — doar schimbare de caption pe capul
de coloana, nu logica noua) e pur si simplu CANTITATE a liniei originale, asa cum vine din
cursor_retur_document (A1.CANTITATE, :4036, filtrat doar pe A1.STERS = 0, fara nicio
agregare cu alte retururi anterioare pe aceeasi linie). Validarea din
do_verifica_articol (:14743-14754) nu face niciun calcul suplimentar de disponibil — blocheaza
doar cazul tnCantitate >= 0 cand esti in mod retur (semnul gresit), nu o depasire de maxim istoric.
Consecinta directa: daca acelasi utilizator emite doua facturi de retur separate pe aceeasi
factura sursa, a doua interogare cursor_retur_document va aduce din nou linia originala cu
CANTITATE intreaga, neredusa de primul retur — nimic in cod nu scade sau marcheaza cat s-a
returnat deja la acest nivel. Acesta e cel mai clar indiciu ca legatura linie-la-linie nu e tinuta
nicaieri pentru N.1: daca ar fi fost tinuta, ar fi trebuit folosita exact aici, ca sa capeze
cantitatea, si nu e.
N.2 (But_retur): exista un calcul real de disponibil, dar e un calcul de stoc/rulaj, nu de
"cat s-a returnat pe acea linie". Ramura din pack_facturare citata la punctul 1
(ff_...sql:8142-8212) calculeaza cantitatea inca prezenta in RUL (miscari de stoc) care a venit
din vanzarea aleasa (RUL.COD legat prin VANZARI.COD, filtrat pe id_articol:id_vanzare din
listaid), plus STOC/RUL_TEMP pentru cazul general. E un calcul valid de "poti scoate din
gestiune atat cat inca exista acolo cu provenienta asta", sprijinit pe legatura persistata
RUL.COD -> VANZARI.COD (asta e adevarata "legatura care exista" pentru N.2) — dar e o legatura la
nivelul miscarii de stoc, nu un contor "cantitate returnata" pe VANZARI_DETALII, si nu se
translateaza in nicio coloana de provenienta pe linia noua scrisa.
5. Ce se poate afisa efectiv
- Numarul/seria facturii originale la nivel de document de retur, cand exista o singura factura
sursa: SE POATE, din
VANZARI_CORESP(TIP=3) joinVANZARIpeID_VANZARE_AVIZ. Interogare de rulat pe baza vie (nu verificata aici, doar formulata):Daca randul e unic, se poate afisa "provine din factura X" pe capul documentului de retur (nu pe linie — toate liniile ar arata aceeasi sursa, pentru ca e singura posibila).SELECT v.serie_act, v.numar_act FROM VANZARI_CORESP c JOIN VANZARI v ON v.ID_VANZARE = c.ID_VANZARE_AVIZ WHERE c.ID_VANZARE_FACT = :id_document_retur AND c.TIP = 3 AND c.STERS = 0; - Cand documentul de retur are 2+ facturi sursa (selectie multipla permisa de dialog): se poate
afisa lista de facturi sursa posibile (tot din
VANZARI_CORESP), dar NU care linie vine din care factura din lista — informatia nu exista. - Numarul facturii originale pe fiecare linie individuala de retur: NU SE POATE, in niciun caz — nici cand exista un singur document sursa (pentru ca "linie cu linie" nu inseamna nimic diferit de "documentul cu documentul" atunci, dar afirmatia stricta "aceasta linie de retur vine din linia Y a facturii" nu poate fi demonstrata din date, doar presupusa cand exista un singur candidat), si sigur nu cand exista mai multe facturi sursa sau cand linia e libera (permisa explicit de decizia 22).
- Pentru N.2 (retur in factura normala, tip 1/5/7/10): nu exista deloc "document de retur" separat de
arata provenienta — linia de retur e o linie normala (cu cantitate negativa) in factura curenta;
singura urma a sursei e
RUL.COD/VANZARI.CODfolosita tranzitoriu la calculul de disponibil, nu persistata pe linia noua dinVANZARI_DETALII.
Verdict pentru plan (S4f)
NU se persista legatura linie-la-linie. Pentru documentul de retur (N.1), se poate afisa
"factura sursa" la nivel de document, din VANZARI_CORESP (TIP=3), doar cand exista exact o
singura factura sursa aleasa — cazul cu selectie multipla da doar multimea, fara atribuire per
linie. Pe linia individuala de retur nu se poate afisa nimic verificabil din date, in niciun caz.
Recomandare pentru S4f: se livreaza fara coloana de provenienta pe linie (cf. deciziei deja scrise in
plan, "nu se inventeaza"); daca se vrea un gest minim, singura afisare sustenabila cu date e un text
de tip "Retur pentru factura: X" pe capul documentului (deja exista, cf.
factura_retur_document.md:85: poDate.text_aditional = ... + poDate.descriere, unde
poDate.descriere e lista serie+numar a facturilor alese) — nu pe linie, si nu nou, e deja acolo.
Ramas de verificat pe baza vie
- Nu s-a interogat live daca
VANZARI_CORESP.TIP=3e scris consecvent pentru fiecare factura de retur emisa istoric (posibil sa existe date vechi scrise inainte ca aceasta ramura sa existe, sau prin alt cod neexaminat aici) — de rulat:Daca da >0, exista facturi de retur fara nicio legatura de document persistata, nici macar cea partiala descrisa mai sus.SELECT COUNT(*) FROM VANZARI v WHERE v.TIP IN (8,9) AND v.STERS = 0 AND NOT EXISTS (SELECT 1 FROM VANZARI_CORESP c WHERE c.ID_VANZARE_FACT = v.ID_VANZARE AND c.TIP = 3); - Nu s-a verificat live cate din facturile de retur existente au >1 factura sursa in
VANZARI_CORESP(adica cate din documentele reale ar cadea in cazul "multime, nu atribuire") — utila pentru a decide cat de des s-ar aplica de fapt afisarea propusa la punctul 5. - Nu s-a gasit (si nici nu era in scop) un mecanism separat pentru avize de retur custodie (
tip=50, marcat "in lucru" intipuri_documente_facturare.md) — daca S4f ajunge sa acopere si acel tip, de recercetat separat. - Subagentul de research auxiliar lansat pentru confirmarea independenta a definitiei
caut_facturi_multiple_client/caut_facturi_multiple_client_articolnu a returnat un raport utilizabil (rezultat gol la finalizare); definitiile au fost gasite si citite direct in aceasta sesiune, inCOMUN\programe\oproceduri_facturare.prg:2091-2160, deci nu blocheaza verdictul, dar nu a adaugat nimic peste ce e deja in acest raport.