Files
roafacturare/docs/cercetare/s8_incarcare_document.md
2026-09-09 22:19:22 +03:00

52 KiB

S8 — Proiectare: incarcarea documentului existent in formularul unificat

Livrabilul povestii S8 din docs\plan_13_unificare_formular_facturare.md:2944-2962 (etapa II, deciziile 5, 8, 9, 19, 35, 50). Cercetare READ-ONLY — zero editari de cod, zero write-back, zero git_sync.ps1 / txt2vcx.ps1, zero commit. Pe Oracle numai SELECT pe dictionar (all_tab_columns pentru VANZARI, VANZARI_DETALII, FACT_VFACTURI, FACT_VFACTURI_DETALII). COMUN\clase\ofacturare_comun.vc2 si COMUN\programe\ofacturare_editare.prg (perimetrul #6) doar citite, neatinse.

Conventie de marcare, folosita peste tot in raport:

  • [V] = verificat direct pe cod / pe dictionarul Oracle, cu fisier:linie sau nume de coloana;
  • [D] = dedus din cod verificat, dar concluzia insasi n-a fost executata / observata;
  • [N] = nestabilit, cu motivul scris.

0. Verdict (rezumat)

Schita din plan — „completeaza_setari_document pentru antet + cursor_retur_document(V_COPIERE = 1) pentru linii, fara degradarea de tip din do_copiaza" — e corecta ca directie, dar niciuna dintre cele doua piese nu incarca documentul, si asta nu e o nuanta de implementare:

  1. completeaza_setari_document(toFactura, .T.) copiaza 16 proprietati din ~120 [V] (COMUN\programe\ofacturare_comun.prg:361-411). Nu copiaza niciunul dintre cei 14 parametri de identitate/antet ai lui modifica_date_factura, in afara de id_delegat / id_masina / id_agent. Serie, numar, data, scadenta, text_aditional, id_facturare, id_ruta, tip_saft, efactura, listare_detaliata, dataora_exp, discountul de document, discount_evidentiat, eproforma, valuta, cursul — toate lipsesc. Sectiunea 1.2 le da pe toate, cu sursa.
  2. cursor_retur_document(V_COPIERE = 1) nu umple gridul documentului, ci gridul-sursa. Pe calea de copiere, rezultatul lui intra in crsarticole (selectorul din stanga), iar crsfactura (documentul) se creeaza gol si ramane gol — comentariul e explicit: „Nu adaug automat articolele din factura originala, ca sa dau posibilitate de modificare" (COMUN\programe\ofacturare.prg:455-457, :338 creeaza_facturacrs([crsfactura])) [V]. Transferul in document se face numai prin do_adauga_tot → do_adauga_articol, care pentru articolele gestionabile trece prin dialogul de alegere din stoc (do_alege_stoc). Sectiunea 4.3.
  3. cursor_retur_document nu intoarce ID_VANZARE_DET si nu intoarce TAXCODE [V] (lista completa de coloane: PACK_FACTURARE:3949-4054). Fara ID_VANZARE_DET:
    • cheia de linie presupusa de S8b (§1.2 al raportului S8b) nu exista pe calea propusa;
    • ruta ieftina modifica_explicatie_articol devine neapelabila — primul ei parametru este V_ID_VANZARE_DET (PACK_FACTURARE:14511-14519) [V]. Fara TAXCODE, cerinta explicita a planului („explicatia si taxcode pe fiecare linie") nu e acoperita.

Recomandarea centrala a acestei proiectari: S8 nu se construieste pe perechea din schita, ci pe precedentul care exista deja si face exact acest lucru — relisteaza_ofacturare_stoc (COMUN\programe\ofacturare_stoc.prg:456-742) [V], care reconstituie poDate + cursorul de linii dintr-un document salvat, prin FACT_VFACTURI (antet) + FACT_VFACTURI_DETALII (linii), pentru relistare. FACT_VFACTURI_DETALII are ID_VANZARE_DET si TAXCODE [V]. Detaliile, compromisurile si ce ramane totusi de completat — sectiunea 1.3 si 4.


1. Inventarul complet a ce se incarca, camp cu camp

1.1 Cele doua canale de citire, comparate pe coloane [V]

Sursa: PACK_FACTURARE:3949-4054 (cursor_retur_document) si all_tab_columns pentru FACT_VFACTURI_DETALII / VANZARI_DETALII.

Ce trebuie pe linie cursor_retur_document(V_COPIERE=1) FACT_VFACTURI_DETALII Exista pe VANZARI_DETALII?
ID_VANZARE_DET (cheia liniei) NU DA DA
TAXCODE NU DA DA
EXPLICATIE DA DA DA
ID_POL (politica de pret pe linie) DA NU (doar NUME_LISTA_PRETURI) DA
PRETD / ID_VALUTAD DA (PRETD) NU DA
GESTIONABIL (flag calculat) DA (calculat, vezi 1.4) NU — (derivat din ID_GESTIUNE)
DIFERENTA pliata in PRET (vezi 1.4) NU DA
ID_GESTIUNE, LOT, SERIE, CANTITATE, PRET, PRET_CU_TVA, PROC_TVAV, ID_JTVA_COLOANA, ID_JTVA_COLOANA_EX, PRET_ACHIZITIE, DISCOUNT_UNITAR, ID_VALUTA, CURS, MULTIPLICATOR DA DA DA
ID_CTR / NUMAR_CONTRACT NU DA DA (ID_CTR)

Concluzia operationala: niciunul dintre cele doua canale nu e suficient singur [V]. Cele doua coloane care lipsesc din cursor_retur_document (ID_VANZARE_DET, TAXCODE) exista amandoua pe VANZARI_DETALII, deci sunt disponibile fara nicio schimbare de structura — lipseste doar din lista SELECT.

Trei variante, cu recomandare:

Varianta Ce cere Risc
(A) adaugarea celor doua coloane in SELECT-ul lui cursor_retur_document doua randuri in PL/SQL, plus A1.ID_VANZARE_DET, A1.TAXCODE in subselectul intern Procedura e folosita si de copiere si (prin cursor_retur) de retur. Coloanele in plus ajung in crsarticole → Scatter Name poArticol → si taxcode s-ar gathera in crsfactura (campul exista deja acolo, creeaza_facturacrs, ofacturare_comun.prg:1785) [V]. Adica copierea ar incepe sa transporte taxcode — probabil o corectie, dar e o schimbare de comportament pe o cale existenta, nu una inerta.
(B) procedura noua, cursor_editare_document, clona cu cele doua coloane in plus — RECOMANDAT o procedura noua in pack_facturare, zero atingere pe cele existente Duplicare de SQL (~100 randuri). Costul e intretinerea, nu regresia. Garantie de regresie zero prin constructie, in acelasi spirit ca solutia SET_IDFACT din S9.
(C) citire din FACT_VFACTURI_DETALII (calea relisteaza_ofacturare_stoc) zero cod Oracle nou Pierde ID_POL, PRETD/ID_VALUTAD, GESTIONABIL si tratamentul DIFERENTA/valuta din cursor_retur_document — adica exact partea grea. Ar cere refacerea ei in VFP.

Recomandare: (B), cu SELECT-ul copiat identic din cursor_retur_document plus A1.ID_VANZARE_DET si A1.TAXCODE, si cu ramura GESTIONABIL decisa explicit (1.4).

1.2 Antetul — ce se incarca, si de unde [V]

poDate e oDateFactura (COMUN\programe\ofacturare_comun.prg:99-320). Coloana „sursa" e coloana din FACT_VFACTURI (view-ul din spatele crsFacturi, folosit deja de do_modifica ofacturare_comun.vc2:4560-4568 si de relisteaza_ofacturare_stoc ofacturare_stoc.prg:504-509), sau VANZARI direct.

Legenda coloanei „azi": CSD = acoperit de completeaza_setari_document(…, .T.); GOL = nimeni nu-l incarca azi pe calea de copiere, deci e gol de umplut in S8.

(a) Identitatea documentului — antetul vizibil

poDate. Sursa Azi Nota
nIdTipDoc (FACTURA/PROFORMA/BON) derivat din tip + eproforma GOL — linia e comentata explicit in CSD (ofacturare_comun.prg:366) vezi 3.2
tip FACT_VFACTURI.TIP GOL (si azi degradat de do_copiaza) S8 il pastreaza, per plan
eProforma FACT_VFACTURI.EPROFORMA GOL — eproforma nu apare deloc in ofacturare_comun.prg (rezultatul 1 al rundei 13)
serie_act FACT_VFACTURI.SERIE_ACT GOL (CSD il pune doar in .descriere, ca text) vezi 4.2 — conflict cu poGeneratorNumere
nract FACT_VFACTURI.NUMAR_ACT GOL (idem) idem
dataact FACT_VFACTURI.DATA_ACT GOL (Init pune azi)
datascad FACT_VFACTURI.DATA_SCAD GOL (Init calculeaza din gnZileScadentaFact)
zi_curs NU EXISTA COLOANA — vezi 1.5 GOL, si nerecuperabil raspuns la intrebarea deschisa din S8b §5.2
in_valuta / id_valuta / nume_valuta FACT_VFACTURI.IN_VALUTA / ID_VALUTA / NUME_VAL GOL (Init deriva in_valuta din tip)
Curs / multiplicator FACT_VFACTURI.CURS / MULTIPLICATOR GOL pe linii, din VANZARI_CURSURI

(b) Partener si sursa

poDate. Sursa Azi
id_client / nume_client ID_PART / CLIENT CSD
cod_fiscal FACT_VFACTURI.COD_FISCAL GOL
sold_lei / sold_valuta recalculate la deschidere (derivate, read-only) GOL — se recalculeaza, nu se restaureaza
listaid (id comanda / contract / lista avize) vezi 1.6 CSD, dar cu valoare GRESITA pentru editare — CSD scrie .listaid = toDateAnterior.id_vanzare (ofacturare_comun.prg:383) [V]
descriere (nr. comanda/contract/avize) FACT_VFACTURI.ALTELE / COMANDA / CONTRACT / AVIZE CSD, dar cu serie+numarul documentului insusi, nu al sursei (:384)
id_gestiune_init FACT_VFACTURI.ID_GESTIUNE GOL
id_pol / nume_politica NU EXISTA pe VANZARI — doar VANZARI_DETALII.ID_POL GOL — vezi 1.5
id_ctr / contract VANZARI.ID_CTR / VANZARI.CONTRACT GOL

(c) Pliat — analitice (grupul A, read-only in #13, decizia 26)

poDate. Sursa Azi
id_sectie / sectie FACT_VFACTURI.ID_SECTIE / SECTIE CSD
id_lucrare / nrord ID_LUCRARE / LUCRARE CSD
id_responsabil / responsabil NU EXISTA pe VANZARI GOL, si nerecuperabil din VANZARI — liniile sunt comentate in CSD (:369-370) [V]
id_venchelt / venchelt NU EXISTA pe VANZARI GOL — idem, comentate (:373-374) [V]

Faptul ca ID_RESPONSABIL si ID_VENCHELT nu sunt coloane pe VANZARI (verificat pe all_tab_columns) e dovada structurala ca grupul A traieste la nivel de linie de nota, nu de antet — exact ce spunea rute_scriere_antet.md, dar acum cu argument de schema, nu de cautare. [V]

(d) Pliat — alte date (frm_alte_date) si cei 14 parametri

poDate. Sursa Azi
id_delegat / nume_delegat / BIdelegat / CNPdelegat ID_DELEGAT / DELEGAT / BIDELEGAT / CNPDELEGAT CSD
id_masina / nrinmat ID_MASINA / NRINMAT CSD
id_agent / nume_agent ID_AGENT / NUME_AGENT CSD
id_facturare / adresa_facturare ID_FACTURARE / ADRESA_FACTURARE GOL
dataora_exp DATAORA_EXP GOL
text_aditional TEXT_ADITIONAL GOL — plus capcana de la 1.7
nListareDetaliata LISTARE_DETALIATA GOL
tip_saft TIP_SAFT GOL (Init pune 380)
eFactura EFACTURA GOL (Init pune 0)
id_ruta ID_RUTA GOL, si nu exista proprietate pe oDateFactura — vezi 1.5
afisare_scadenta AFISARE_SCADENTA GOL (Init pune 1; ROACONTRACTE il recalculeaza)
tva_incasare TVA_INCASARE GOL (Init ia goCalendar.tva_incasare)
institutie_publica FACT_VFACTURI.INSTITUTIE_PUBLICA GOL
nTipFactura TIP_FACTURA GOL
nIdBeneficiar ID_BENEFICIAR GOL
id_ordl ID_ORDL GOL
coeficient_k VANZARI.COEFICIENT_K GOL
grupul C (ntip_incasare, serie_chit, nr_incasare, incasat, id_casa) TIP_INCASAT, SERIE_INCASAT, NR_INCASAT, SUMA_INCASAT GOL — blocat in etapa I, dar vezi 2.3

(e) Discountul de document — nu e in poDate

[V] Discountul de document nu are proprietate pe oDateFactura: traieste in proprietatile formularului frm_facturare_articole.ndiscfactron / .ndiscfactval (ofacturare.vc2:5286-5287 declaratie *p:, :5317-5318 initializare la 0, :11325/:11337 ControlSource pe cele doua textbox-uri, :14272-14274 citirea la scriere).

Ce Sursa Cum se incarca
discount de document, lei VANZARI.DISCOUNT (in lei cand IN_VALUTA=0) Thisform.ndiscfactron
discount de document, valuta VANZARI.DISCOUNT (in valuta cand IN_VALUTA=1) Thisform.ndiscfactval, plus ndiscfactron = Round(ndiscfactval * Curs / multiplicator, …)
discount_evidentiat VANZARI.DISCOUNT_EVIDENTIAT poDate.discount_evidentiat (ControlSource la ofacturare.vc2:11305 si :15968)

Precedentul exact exista si trateaza deja bifurcatia lei/valuta: ofacturare_stoc.prg:553-561 [V] — lnDiscountVal = discount cand in_valuta = 1, altfel lnDiscount = discount. S8 copiaza acest tipar.

Atentie [V]: oDateFactura.Init pune .discount_evidentiat = IIF(TYPE('gnDiscountEvidentiat')='N', gnDiscountEvidentiat, 1) (ofacturare_comun.prg:238) — optiunea de firma, nu valoarea documentului. Daca S8 nu suprascrie, un document emis pe alta setare apare cu discountul evidentiat altfel decat a fost emis, si — pentru ca discount_evidentiat atinge sumele (e parametru al lui scrie_factura2, ofacturare.vc2:14358) — ar declansa regenerare la simpla deschidere. Fals pozitiv de acelasi tip cu lookup-ul delegatului, dar cu efect mai scump.

1.3 Liniile — inventar

Coloanele intoarse de cursor_retur_document(V_COPIERE=1), in ordinea din SELECT (PACK_FACTURARE:3963-4022) [V]: ID_C (=ROWNUM), ID_ARTICOL, LOT, SERIE, ID_POL, ID_VALUTA, DISCOUNT_UNITAR, DISCOUNT_UNITAR_VAL, CODMAT, CODBARE, DENUMIRE, UM, GESTIONABIL, CANTITATE, PROC_TVAV, ID_JTVA_COLOANA, PRETURI_CU_TVA, CURS, MULTIPLICATOR, PRET, PRET_VAL, TIP_VALUTA, NUME_VAL, EXPLICATIE, ID_GESTIUNE, PRET_ACHIZITIE, PRETD, ID_JTVA_COLOANA_EX.

Lipsesc, si sunt necesare pentru S8/S8b: ID_VANZARE_DET, TAXCODE, ID_CTR, CODMATC, COD_UM_ISO, CONT. Ultimele trei sunt in crsfactura si vin azi pe alte cai (do_adauga_articol cere codmatf cu un SELECT separat, ofacturare.vc2:12932-12934 [V]).

1.4 Doua capcane in cursor_retur_document, ambele verificate direct

(i) GESTIONABIL cu V_COPIERE = 1 vine din nomenclatorul CURENT, nu din document [V] (PACK_FACTURARE:3990-3997):

(case when V_PROFORMA = 1 then 0
      when V_COPIERE  = 1 then B.IN_STOC          -- NOM_ARTICOLE, starea de AZI
      else A.GESTIONABIL                          -- NVL2(A1.ID_GESTIUNE,1,0), starea DOCUMENTULUI
 end) AS GESTIONABIL

Pentru copiere e corect (documentul nou se emite cu regulile de azi). Pentru editare e o divergenta tacuta: un articol care a fost facturat gestionabil si intre timp a fost trecut pe IN_STOC = 0 (sau invers) se incarca cu alt regim decat cel cu care e scris in VANZARI_DETALII. Efectul practic: articolul trece pe alta ramura in do_adauga_articol (dialog de stoc vs. fara), deci se schimba si ce se descarca la reemitere. Recomandare: pe calea de editare se foloseste A.GESTIONABIL (ramura else), adica adevarul documentului — argument suplimentar pentru varianta (B) din 1.1. Aceasta e a cincea valoare din rezultatul 6 al handoff-ului (IN_STOC din nomenclator), regasita pe calea de CITIRE, nu doar pe cea de scriere.

(ii) PRET vine rotunjit si cu DIFERENTA pliata inauntru [V] (PACK_FACTURARE:4008-4014): ROUND(...) + A.DIFERENTA. Confirma rezultatul 6 din handoff. Consecinta directa pentru S8b: comparatia de pret la confirmare trebuie facuta pe aceasta forma (rotunjita + DIFERENTA), nu pe VANZARI_DETALII.PRET brut — altfel orice document cu DIFERENTA <> 0 apare modificat la deschidere.

1.5 Trei campuri de antet care nu au unde sa fie salvate — deci nu se pot restaura [V]

Verificat pe all_tab_columns pentru VANZARI:

Camp Constatare Ce inseamna pentru S8
zi_curs Nu exista coloana ZI_CURS pe VANZARI. Se stocheaza rezultatul (CURS, MULTIPLICATOR, si VANZARI_CURSURI pe linie), nu ziua de la care s-a luat cursul. Inchide intrebarea deschisa din S8b §5.2. Nu e „S8 trebuie sa suprascrie implicitul cu valoarea salvata" — nu exista valoare salvata. Se incarca Curs/multiplicator din document, iar zi_curs se exclude din comparatie si (recomandat) se ascunde pe calea de editare, ca sa nu sugereze o valoare pe care documentul n-o are. Ascunderea are precedent in acelasi Do Case (ofacturare.vc2:9720-9725, tipurile 8 si 9).
id_pol (politica de preturi, antet) Nu exista pe VANZARI; exista pe linie (VANZARI_DETALII.ID_POL). Antetul nu are ce sa afiseze ca „politica documentului". Recomandat: pe editare se afiseaza politica doar daca e unica pe toate liniile, altfel gol — si e oricum blocat (grupul B, 3.1).
id_ruta Exista pe VANZARI (ID_RUTA) si e unul din cei 14, dar oDateFactura nu are proprietate id_ruta (verificat pe lista completa de proprietati, ofacturare_comun.prg:99-218). Azi valoarea circula prin poRec (Scatter din crsfacturi), nu prin poDate (ofacturare_comun.vc2:4601). Proprietate noua pe oDateFactura, altfel but_modifica (S8c) n-are de unde sa citeasca ruta. Gol de umplut, semnalat aici pentru ca S8c il presupune existent.

1.6 Legatura cu sursa — id_comanda, id_ctr, lista de avize

Ce exista, verificat:

Sursa Unde e salvata Citibila?
comanda VANZARI.ID_COMANDA (+ VANZARI.COMANDA, text) [V] DA, direct din FACT_VFACTURI.ID_COMANDA
contract VANZARI.ID_CTR (+ VANZARI.CONTRACT, text) [V] DA, direct din FACT_VFACTURI.ID_CTR
avize VANZARI_CORESP(ID_VANZARE_FACT, ID_VANZARE_AVIZ, TIP) + VANZARI.AVIZE (text redundant) [V] (PACK_FACTURARE:15494-15516) DA ca date, NU ca procedura — vezi mai jos

[V] Nu exista in pack_facturare nicio procedura care sa CITEASCA lista sursa a unui document. Cele 9 aparitii ale lui VANZARI_CORESP in export sunt: 6 in garzile lui sterge_factura (:5454-5530), 1 UPDATE ... STERS = 1 tot acolo (:5582), 1 subselect in scrie_cantitati_vanzari_avize (:6319), 1 INSERT in scrie_corespondente_vanzari (:15494). Citirea e cod nou — un SELECT simplu, dar de scris:

SELECT ID_VANZARE_AVIZ FROM VANZARI_CORESP
 WHERE ID_VANZARE_FACT = :id AND STERS = 0 AND TIP = 1

Semantica lui TIP nu e stabilita in acest raport [N] — scrie_corespondente_vanzari(V_TIP) comuta doar sursa listei (clistaid_avize pentru TIP = 1, clistaid altfel, :15485-15493), iar valorile efective (1/2/3) vin din CASE-ul lui finalizeaza_factura, necitit aici. Handoff-ul rundei 13 le da ca 1/2/3 folosite, 4 liber. De confirmat ramura cu ramura la implementare, nu de preluat din raport — e exact tiparul care a produs cifra gresita a rundei 13.

Nota de proiectare: poDate.listaid e suprasolicitat. Pe calea de copiere/editare el trebuie sa fie id_vanzare-ul documentului (ca cursor_retur_document sa-i citeasca liniile), dar pentru reemitere pack_facturare.clistaid / clistaid_avize trebuie sa contina lista sursei originale. Sunt doua valori diferite in acelasi camp, la momente diferite. Precedentul din relisteaza_ofacturare_stoc arata ca problema e veche si nerezolvata: acolo poDate.listaid = Iif(poDate.tip = 3, Alltrim(Str(id_comanda)), [0]) [V] (ofacturare_stoc.prg:531) — cu ramura de contract comentata la :529, deci nici acel precedent nu restaureaza sursa pentru contracte sau avize. S8 are nevoie de un camp separat (propunere: poDate.cListaSursa + poDate.cListaSursaAvize), nu de o a doua semnificatie a lui listaid. Vezi si sectiunea 6.

1.7 text_aditional — trei transformari intre ce se vede si ce se salveaza [V]

  1. Truncat la 100 de caractere la scriere: LEFT(Nvl(poDate.text_aditional,[]),100) (ofacturare.vc2:14356, identic pe toate ramurile scrie_*).
  2. CR+LF inlocuit cu Chr(170) imediat inainte: poDate.text_aditional = Strtran(poDate.text_aditional, Chr(13)+Chr(10), Chr(170), 1, 100, 1) (:14281) — si atribuirea e pe poDate insusi, deci obiectul ramane modificat dupa scriere.
  3. Pe calea modifica_date_factura nu exista nici truncare, nici substitutie (ofacturare_comun.vc2:4608 trimite ?poRec.text_aditional ca parametru legat) [V] — deci cele doua rute salveaza forme diferite ale aceluiasi camp.

Consecinte pentru S8: la incarcare, Chr(170) trebuie convertit inapoi in CR+LF (altfel textul apare pe un rand cu caractere ciudate), iar comparatia din S8b trebuie facuta pe forma normalizata (vezi 5.2). Fara asta, orice document emis cu text pe mai multe randuri apare „modificat" la deschidere. Nesemnalat pana acum in niciun raport.


2. Initializarile „pentru document nou" care trebuie sarite la editare

Cerinta obligatorie din S8b, rezultatul 5, plus inventarul cerut de briefing.

2.1 Lookup-ul „ultimul delegat / ultima masina" — garda exista deja [V]

frm_alte_date.Init, COMUN\clase\ferestre_cere_date.vc2:3105-3208. Codul real (:3110-3136) — si aici raportul S8b si planul descriu situatia incomplet:

If poDate.eProforma = 0
    If Empty(Nvl(poDate.id_delegat,0)) And Empty(Nvl(poDate.id_masina,0))     && :3111
        poDate.id_delegat = 0
        poDate.id_masina  = 0
        ... cauta_date_ultima_factura[_tip](...)                              && :3118-3124

Lookup-ul e deja conditionat de „ambele goale". Deci:

  • pentru un document care are delegat sau masina salvate, e suficient ca S8 sa populeze poDate.id_delegat / id_masina INAINTE de construirea lui frm_alte_date — lookup-ul nu mai ruleaza, fara niciun cod nou. completeaza_setari_document le pune deja (1.2.d), deci pe calea de editare conditia e indeplinita din ordinea de apel, nu dintr-un semnal;
  • riscul real ramane, dar e mai ingust decat il descrie S8b: el se manifesta exact pe documentele care n-au nici delegat, nici masina salvate — un caz frecvent (multe facturi se emit fara delegat). Pentru acelea, lookup-ul umple poDate cu ultimul delegat al clientului, si atunci: ori documentul apare modificat, ori modifica_date_factura scrie un delegat pe care documentul nu l-a avut niciodata. Sub modifica_date_factura campul se scrie neconditionat (ID_DELEGAT = V_ID_DELEGAT, PACK_FACTURARE:14462) [V], deci scrierea gresita e certa, nu probabila.

Mecanismul propus (minim, si in acord cu tiparul existent): parametru nou pe frm_alte_date.Init sau — mai ieftin si mai greu de ratat — o proprietate pe poDate, poDate.lEditare, citita in conditia de la :3111:

If !poDate.lEditare And Empty(Nvl(poDate.id_delegat,0)) And Empty(Nvl(poDate.id_masina,0))

Argumentul pentru proprietate pe poDate si nu parametru: frm_alte_date nu e singurul formular care are initializari de acest tip (2.2), poDate e deja obiectul citit de toate, si e acelasi tipar cu lCopiere, care exista deja pe oDateFactura (ofacturare_comun.prg:212) [V].

„Ce se intampla cand documentul chiar n-are delegat salvat": cu garda de mai sus, poDate.id_delegat ramane .NULL. (valoarea implicita din declaratia de clasa), campul se afiseaza gol, iar la salvare modifica_date_factura primeste NULL si scrie NULL — identic cu starea din baza. Adica exact comportamentul dorit: documentul ramane cum a fost. Fara garda, valoarea devine 0 (atribuirea de la :3112-3113) si apoi rezultatul lookup-ului.

Diferenta fata de S8b, spusa explicit: S8b cerea „sarirea explicita a lookup-ului"; codul real arata ca sarirea e deja implicita pentru documentele cu delegat, si e necesara explicit doar pentru cele fara. Concluzia lui S8b (S8 trebuie sa faca ceva) ramane valida; motivul si perimetrul se schimba, iar asta reduce riscul de la „primul document editat al unui client cu activitate recenta" la „primul document fara delegat al unui client cu activitate recenta".

2.2 Alte initializari de acelasi tip — inventar cu fisier:linie [V]

Cautate in oDateFactura.Init, frm_alte_date.Init, frm_date_factura.Init, frm_date_aviz.Init.

# Loc Ce face Efect pe calea de editare Gravitate
1 ofacturare_comun.prg:232-236 .Data, .dataireg, .dataact = azi (sau ultima zi a lunii de lucru); .datascad din gnZileScadentaFact; .zi_curs = azi Documentul apare cu data de azi si scadenta recalculata Mare — atinge 2 din cei 4 campuri de identitate
2 ofacturare_comun.prg:238 .discount_evidentiat = gnDiscountEvidentiat (optiune de firma) Vezi 1.2.e — poate declansa regenerare la simpla deschidere Mare
3 ofacturare_comun.prg:233 .tva_incasare = goCalendar.tva_incasare (starea de azi a firmei) Un document emis in alt regim se incarca cu regimul curent Medie
4 ofacturare_comun.prg:237 .in_valuta = 1 pentru tnIdSet / tnTip din lista Derivat din tip, nu din document — coincide de obicei, dar nu prin constructie Mica
5 ofacturare_comun.prg:241-243 .initializeaza_politica_pret() pentru tipurile 23, 30, 41 — apel actualizeaza_politica_pret Politica curenta, nu cea a documentului Medie (tipuri de transfer)
6 ofacturare_comun.prg:240 .initializeaza_setari_document() → actualizeaza_document(tip) → id_fdoc / fdoc Reconfigureaza tipul de document din setarile curente Medie
7 ofacturare_comun.prg:245-283 ramura ROACONTRACTE (goContract): suprascrie id_client, nume_client, cod_fiscal, listaid, descriere, id_sectie, id_responsabil, id_valuta, si .datascad = .dataact + lnScadentaIncasare, si .afisare_scadenta Daca goContract exista in sesiune, suprascrie antetul documentului editat cu datele contractului Mare, conditionata de context
8 ofacturare_comun.prg:288-316 ramura ROACOMENZI (goComanda, tnTip = 3): idem, id_client, listaid, descriere, id_sectie, sectie Idem Mare, conditionata
9 ferestre_cere_date.vc2:3177-3179 If This.opt_incasat.Value = 2 → poDate.incasat = poDate.totalctva Suprascrie suma incasata cu totalul documentului Medie (grup C, blocat in etapa I)
10 ferestre_cere_date.vc2:3166-3171 poDate.id_casa = gnid_part_casa (casa implicita a firmei) Suprascrie casa documentului Medie (grup C)
11 ferestre_cere_date.vc2:3183-3185 IF poDate.tip = 2 AND poDate.afisare_scadenta = 0 AND AT([Scadenta la ], poDate.text_aditional) = 0 → prefixeaza text_aditional cu „Scadenta la N zile." A doua instanta a exact aceluiasi defect ca lookup-ul delegatului, si pe un camp din cei 14: modifica text_aditional la Init, fara actiune a utilizatorului Mare
12 ferestre_cere_date.vc2:3150 If poDate.incasat <> 0 → Thisform.opt_incasat.Value = 2 — atribuire programatica pe opt_incasat Declanseaza ProgrammaticChange → actualizeaza_tipincasare() → alocare/dezalocare de numere de chitanta (riscul deja documentat in s3b_alte_date_analitice.md §4.3) Medie
13 ofacturare.prg:207-209 poGeneratorNumere.ResetNumere() + creeaza_cursor_serii(nIdTipDoc), la fiecare trecere prin factureaza Aloca un numar nou pe calea de emitere Mare — vezi 4.2
14 ofacturare.prg:184-193 Do Case pe tnTip care forteaza poDate.nIdTipDoc (5 = FACTURA / 6 = AVIZ) inainte de completeaza_setari_document Pe editare, nIdTipDoc trebuie sa vina din document (proforma = 23, bon = 3), nu din tip Medie

Nr. 11 merita subliniat: garda lui (AT([Scadenta la ], text_aditional) = 0) il face inert pe un document de contract emis cu acelasi numar de zile de scadenta. Devine activ exact cand scadenta s-a schimbat de la emitere — adica precis cazul in care textul e cel vechi si trebuie pastrat. Metoda Destroy/Unload face si operatia inversa (:3037-3038: Strtran(...,[])), ceea ce inseamna ca pe calea normala prefixul e adaugat si scos in aceeasi sesiune; pe o cale de editare care nu trece prin acelasi Unload, prefixul ar ramane. [D] — n-am urmarit toate caile de iesire ale formularului.

2.3 Regula generala propusa

Orice camp al lui poDate populat prin apel Oracle sau dintr-o variabila globala/go* in Init/Load e o sugestie pentru document nou, nu o valoare de document. Pe calea de editare, ordinea trebuie sa garanteze ca aceste sugestii nu ajung sa se execute (garda), nu doar ca sunt suprascrise dupa (ceea ce ar merge pentru valori, dar nu pentru efectele secundare — nr. 12 si nr. 13 aloca numere, nu doar seteaza campuri).


3. Ce se blocheaza la editare, si de ce

3.1 Blocate — schimbarea lor ar insemna alt document

Camp / control De ce Ruta care ar lipsi oricum
Tipul documentului (Ct_clb_fdoc, poDate.tip / nIdTipDoc) Tipul decide ce sursa se consuma, ce nota se scrie si ce garzi se aplica. do_copiaza il degradeaza tocmai pentru ca un document copiat nu mai poate reconsuma sursa (ofacturare_comun.vc2:3694-3711) [V]; la editare degradarea e interzisa prin plan, deci tipul e fix. grupul B — nicio ruta de scriere pe loc
Sursa (Ct_clb_altele) Vezi 3.2 grupul B
Clientul (Ct_clb_nume_client) Schimbarea partenerului rescrie IREG_PARTENERI, ACT, soldurile — alt document grupul B
Valuta, zi_curs, gestiunea sursa, politica de preturi grupul B, decizia 25; in plus zi_curs si id_pol n-au valoare salvata (1.5) grupul B
Grupul C (incasare) decizia 25; in plus opt_incasat are efect secundar de alocare (2.2 nr. 12) grupul C
Grupul A (venit/cheltuiala, sectie, responsabil, lucrare) decizia 26 — read-only permanent in #13; in plus doua din patru nu exista pe VANZARI (1.2.c) editarea e a lui #6

3.2 Ct_clb_altele — verificarea ceruta explicit, cu doua corectii la plan [V]

Planul (:2952-2955) spune: „eticheta schimbata pe tip de do_schimba_explicatia („Nr. contract" / „Nr. comanda" / „Nr. factura" / „Nr. facturi" / „Locatie", ofacturare.vc2:9633-9643), eliminat azi din formular cand gnScadereStoc = 0 si tipul e 1/5/10 fara copiere."

Citit ramura cu ramura din frm_date_factura.Init (ofacturare.vc2:9563-9796), Do Case-ul de la :9615-9722:

Corectia 1 — lista de etichete e incompleta. Intervalul 9633-9643 din plan e corect ca interval, dar contine sase apeluri, nu cinci: Nr. contract (:9633, tip 2/6/52), Nr. comanda (:9635, tip 3), Nr. aviz / avize (:9637, tip 4), Nr. factura (:9639, tip 7), Nr. facturi (:9641, tip 8/9), Locatie (:9643, tip 45). Planul omite exact tipul 4 — cel pentru care Ct_clb_altele tine lista de avize, adica singura sursa care trebuie refurnizata lui S9 (sectiunea 6). Omisiunea nu e cosmetica.

Corectia 2 — conditia de eliminare e mai larga decat cea din plan. ct_clb_altele e scos (RemoveObject) in trei situatii, nu una:

Ramura Conditie Ce face
:9616-9628 gnScadereStoc = 0 And Inlist(poDate.tip, 1, 5, 10, **48, 49**) daca llCopiere → eticheta Nr. factura (:9623); altfel RemoveObject('ct_clb_altele') (:9627)
:9651-9660 gnScadereStoc = 1 And Inlist(poDate.tip, 1, 5, 10) idem (:9653 / :9658)
:9705-9712 (in Otherwise) Inlist(poDate.tip, 48, 49) RemoveObject('ct_clb_altele') neconditionat

Deci: tipurile sunt 1, 5, 10, 48, 49 (nu doar 1/5/10), si eliminarea are loc si cand gnScadereStoc = 1, nu doar cand e 0. Formularea din plan („gnScadereStoc = 0 si tipul e 1/5/10") descrie una din trei ramuri.

Consecinta pentru S8, si e in favoarea noastra: pe ramurile 1 si 2, llCopiere = .T. pastreaza controlul si ii pune eticheta Nr. factura. Calea de editare, care va avea acelasi semnal ridicat (lCopiere sau lEditare), mosteneste automat pastrarea controlului — nu e nimic de adaugat, doar de blocat. Pe ramura 3 (tipurile 48/49) controlul e scos neconditionat; [N] n-am stabilit daca tipurile 48/49 intra in perimetrul etapei II.

3.3 Ce ramane editabil

Cei 14 parametri, prin but_modifica (S8c), plus liniile (cantitati, preturi, discounturi, explicatii, adaugari/stergeri) si discountul de document — care declanseaza regenerarea. Nimic din sectiunea 3.1.


4. Ordinea reala de incarcare, si de ce conteaza

4.1 Secventa propusa

 0. S7 a validat deja documentul (garzi) si a stabilit id_vanzare.
 1. Citeste ANTETUL:  SELECT ... FROM FACT_VFACTURI WHERE id_vanzare = :id
 2. Citeste LISTA SURSA (avize) inainte de orice altceva: SELECT ... FROM VANZARI_CORESP  (§6)
 3. poDate = CreateObject("oDateFactura", 0, 0)      <-- tnIdSet = 0 => Init NU ruleaza (§4.2)
 4. poDate.lEditare = .T.                            <-- semnalul, inainte de orice formular
 5. Populeaza poDate din antet: cei 14 + tip + eproforma + valuta/curs +
    discount_evidentiat + client + gestiune + sursa (cListaSursa / cListaSursaAvize)
 6. Populeaza thisform.ndiscfactron / ndiscfactval din VANZARI.DISCOUNT
 7. Citeste LINIILE (canalul din §1.1(B)) -> crsarticole
 8. GARDA DE RECALCUL: thisform.lIncarcare = .T.
 9. Umple crsfactura din crsarticole, fara dialoguri (§4.3)
10. Adauga lista de preturi peste crsarticole (pentru S4g: adaugarea de articole noi)
11. thisform.lIncarcare = .F.
12. Un singur do_calculeaza_totaluri()
13. SNAPSHOT pentru S8b (§5)
14. Blocheaza antetul (S8c) si campurile din §3.1; arata formularul

4.2 De ce pasul 3 arata asa — tnIdSet = 0 [V]

Tot corpul lui oDateFactura.Init e inchis in If !Empty(m.tnIdSet) (ofacturare_comun.prg:225). Cu tnIdSet = 0, niciuna dintre cele opt initializari de la 2.2 (nr. 1-8) nu se executa, iar constructorul devine inert. Precedentul exista si e chiar cel de reincarcare a unui document: relisteaza_ofacturare_stoc face poDate = Createobject("oDateFactura", 0, 0) (ofacturare_stoc.prg:497) [V] exact din acest motiv.

Asta rezolva printr-o singura decizie de constructie opt din cele paisprezece initializari periculoase, fara nicio modificare in oDateFactura — si e mult mai robust decat „suprascriem dupa", pentru ca nu depinde de completitudinea listei de suprascrieri.

Ce ramane de tratat separat, pentru ca nu trece prin Init:

  • nr. 9-12 (frm_alte_date.Init) → garda poDate.lEditare de la 2.1;
  • nr. 13, poGeneratorNumere — e apelat din factureaza (ofacturare.prg:207-209), nu din Init. Pe calea de editare nu trebuie sa se aloce numar: seria si numarul vin din document (decizia F / plan §F). oGeneratorNumere are deja dezaloca_numar si verifica_numar (ofacturare.prg:227, :250), dar calea corecta e sa nu se aloce deloc. [N] — COMUN\programe\oserii_numere.prg n-a fost citit in aceasta sesiune; planul citeaza :227-233 pentru afirmatia „la modificare nu se aloca numar nou". De verificat la implementare ca ocolirea alocarii nu lasa poDate.rezultat_serii intr-o stare pe care frm_date_factura.Init o citeste (ofacturare.vc2:9736: If Inlist(poDate.rezultat_serii, 1, 2, 3) → clb_serie_act.do_initializeaza(...)).

4.3 Pasul 9 e piesa care lipseste azi — si e cea mai mare

[V] Pe calea de copiere, crsfactura ramane gol; documentul se compune manual sau prin do_adauga_tot. do_adauga_tot (ofacturare.vc2:13169-13198) face SCAN peste crsarticole si cheama do_adauga_articol(.T.), care (:12871-12894):

  • pe ramura poArticol.gestionabil = 0 Or gnScadereStoc = 0 Or poDate.tip = 45 — creeaza frm_articol_factura fara sa-l arate (cu tlImplicit = .T.) si calculeaza totalurile. Acceptabil.
  • pe ramura Otherwise — cheama do_alege_stoc(...), adica dialogul de alegere din stocul de azi. Pe calea de editare asta e gresit de doua ori: (a) ar putea cere interventia utilizatorului pentru fiecare linie gestionabila la simpla deschidere a documentului; (b) ar alege loturi/serii din stocul curent, desi documentul are deja LOT, SERIE si ID_GESTIUNE proprii, intoarse de cursor.

In plus, do_adauga_tot are un Do While cu aMessageBox("Nu ati selectat toata cantitatea …") (:13188) — un modal per linie neacoperita. Inacceptabil la incarcare.

Si do_adauga_articol scrie Replace id_temp With Recno() (:12951 si :13000) [V] — deci chiar daca sursa ar avea ID_VANZARE_DET, nu ar ajunge in crsfactura pe aceasta cale.

Concluzie: S8 nu poate refolosi do_adauga_tot. Ii trebuie o populare directa crsarticole → crsfactura (un INSERT INTO crsfactura ... SELECT ... plus recalculul valorilor pe linie prin calculeaza_totaluri), care:

  1. nu deschide niciun dialog;
  2. pastreaza lot, serie, id_gestiune, id_pol, pretd din document;
  3. duce id_vanzare_det intr-o coloana proprie noua pe crsfactura (nu in id_temp, care e suprasolicitat: Recno() pe o cale, id_vanzare_det pe alta — prelucreaza_facturacrs face id_vanzare_det As id_temp, ofacturare_comun.prg:1811 [V]);
  4. duce taxcode. Precedentul de structura exista: blocul tnTip = 30 din factureaza (ofacturare.prg:340-378) face exact o populare directa crsarticole → crsfactura prin INSERT INTO crsfactura (...) Values (...), fara niciun dialog [V]. Se cloneaza forma lui.

4.4 Garda de „nu recalcula acum" — unde, si de ce

crsfactura e RecordSource-ul gridului, iar coloanele au Valid/InteractiveChange care recalculeaza. La populare programatica, Valid nu se declanseaza in VFP (se declanseaza doar la iesirea din control, pe interactiune) — deci riscul principal nu e in grid, ci in:

  • ControlSource legate direct de poDate (ex. poDate.discount_evidentiat, ofacturare.vc2:11305) — atribuirea programatica declanseaza ProgrammaticChange, nu Valid [D] (comportament VFP standard; nu l-am observat rulat aici);
  • opt_incasat.Value = — 2.2 nr. 12, efectul secundar documentat;
  • clb_discount.procent.Value = (ofacturare.vc2:13475) — daca S8 populeaza procentul de discount in loc de suma, InteractiveChange ar recalcula discountul pe baza curenta, care in timpul popularii e incompleta.

Recomandare concreta: un singur flag pe formular, Thisform.lIncarcare, testat la intrarea in do_calculeaza_totaluri si in actualizeaza_discount (ofacturare.vc2:13408-13415), plus regula ca discountul de document se incarca ca SUMA (ndiscfactron/ndiscfactval, valorile pe care le citeste si scrierea, :14272-14274), nu ca procent — procentul se recalculeaza din suma la pasul 12 (:13475: procent.Value = Round(ndiscfactron * 100 / nbazafdiscount, 2)), nu invers.

4.5 Asezarea — decizia 5

Nimic din cele de mai sus nu schimba aspectul: nu se adauga banda de avertizare, coloane cu valori initiale sau panou de diferente. Se schimba titlul ferestrei si butonul principal (decizia 9 / S8c). Diferentele vizibile fata de introducere sunt doar cele care rezulta din blocarea campurilor (3.1) si din ascunderea lui zi_curs (1.5) — ultima e o abatere minora de la „identic", propusa motivat, de confirmat de Marius (intrebarea 4, sectiunea 8).


5. Interfata cu S8b — ce se retine ca „stare initiala"

5.1 Momentul

Snapshot-ul se ia la pasul 13: dupa incarcarea completa si dupa singurul recalcul, dar inainte de afisarea formularului. Nu mai devreme (valorile derivate n-ar fi calculate) si nu mai tarziu (orice Init de subformular ar putea polua — 2.2).

5.2 Ce se retine, si in ce forma

Grup Forma Normalizare obligatorie inainte de comparatie
Cei 14 parametri copie a valorilor din poDate (+ id_ruta, 1.5) intr-un obiect poDateInitial .NULL. vs 0 vs '' — functie unica aplicata simetric; text_aditional: LEFT(...,100) + Chr(170)→CR+LF pe ambele parti (1.7)
Discountul de document ndiscfactron si ndiscfactval, ca sume Round(..., gnPc) / Round(..., gnPVal)
discount_evidentiat scalar —
Liniile copie a lui crsfactura, cheia = id_vanzare_det (coloana noua, 4.3) Round la gnPc / gnPPretV / gnPCant; pretul comparat in forma rotunjita + DIFERENTA (1.4.ii)
Explicatie / taxcode pe linie in aceeasi copie Alltrim, Nvl(...,'')

Trei precizari care corecteaza sau completeaza S8b:

  1. Cheia id_vanzare_det nu vine gratuit — S8b o presupunea incarcata („coloana deja incarcata la S8"). Nu e (1.1). Devine livrabil al lui S8, nu premisa.
  2. Liniile fara id_vanzare_det (adaugate in sesiune) se marcheaza cu 0/.NULL. si inseamna direct „regenerare", ca in S8b §1.3.
  3. zi_curs se scoate din comparatie (1.5) — altfel orice document ar aparea modificat.

5.3 Cele 7 campuri comune scrie_factura2 / modifica_date_factura

Nimic de adaugat la S8b §3.1 — lantul cu prioritate ramane. S8 doar garanteaza premisa lui: poDate contine, la momentul confirmarii, valorile documentului plus editarile utilizatorului si nimic altceva — ceea ce e exact ce asigura sectiunile 2 si 4.


6. Interfata cu S9 — ce trebuie sa incarce S8 ca S9 sa poata refurniza lista sursa

Pasul 1 nou al lui S9 (handoff, rezultatul 7) cere citirea listei sursa inainte de stergere. S8 e locul unde se citeste, pentru ca dupa stergere VANZARI_CORESP are STERS = 1 (:5582 [V]) si VANZARI.ID_COMANDA / ID_CTR raman pe randul soft-sters.

Ce incarca S8, si de unde:

Ce Camp propus pe poDate Sursa Consumator la reemitere
lista de avize cListaSursaAvize SELECT ID_VANZARE_AVIZ FROM VANZARI_CORESP WHERE ID_VANZARE_FACT = :id AND STERS = 0 AND TIP = 1 pack_facturare.clistaid_avize → scrie_corespondente_vanzari(1) si marcheaza_facturat
comanda cListaSursa FACT_VFACTURI.ID_COMANDA pack_facturare.clistaid
contract cListaSursa (+ id_ctr) FACT_VFACTURI.ID_CTR idem, plus CTR_RATE_FACTURI
tipul original poDate.tip, nedegradat FACT_VFACTURI.TIP CASE-ul pe ntip din finalizeaza_factura

Doua avertismente:

  1. poDate.listaid NU poate purta aceasta informatie — la incarcare el trebuie sa fie id_vanzare-ul documentului insusi, ca sa se citeasca liniile (1.6). Doua campuri distincte, nu unul reinterpretat.
  2. Semantica valorilor lui TIP din VANZARI_CORESP nu e stabilita aici [N] (1.6) — se citeste din CASE-ul real al lui finalizeaza_factura la implementare.

Nu tine de S8, dar se semnaleaza pentru ca S8 decide continutul lui crsarticole: do_scrie_factura face Select crsarticole + Calculate Sum(cantitate) To lnCantitateRamasa pe ramurile poDate.Tip = 4 (ofacturare.vc2:14305-14307) si Inlist(poDate.Tip,3,21,25,28,42,47) (:14336-14338) [V], si pe baza sumei intreaba „Doriti sa se inregistreze si avizul de retur?" / „Doriti sa se inchida comanda?" si seteaza pnParametruAditional. Pe calea de editare crsarticole contine liniile documentului plus lista de preturi adaugata peste ele (ofacturare.prg:465-473 [V]), deci suma e lipsita de sens si intrebarea ar aparea gresit, cu efect real pe pnParametruAditional. De rezolvat in S9 — fie prin cursor separat pentru lista de preturi, fie prin calcularea sumei doar peste randurile provenite din document.


7. Criteriul „gata cand", in forma testabila

Comun tuturor tipurilor. Pe un document existent, dupa deschidere si inainte de orice interactiune:

# Ce se verifica Cum
C1 serie, numar, data, scadenta afisate = cele din VANZARI comparatie ecran ↔ SELECT serie_act, numar_act, data_act, data_scad FROM vanzari WHERE id_vanzare = :id
C2 niciun numar nou alocat inainte/dupa: ultimul numar din generatorul de serii pentru nIdTipDoc e neschimbat
C3 numarul de linii din grid = SELECT count(*) FROM vanzari_detalii WHERE id_vanzare = :id AND sters = 0
C4 pe fiecare linie: cantitate, pret, discount, gestiune, lot, serie, cota TVA, explicatie, taxcode = valorile din VANZARI_DETALII (pret in forma rotunjita + DIFERENTA, 1.4.ii)
C5 totalurile afisate = VANZARI.TOTAL_FARA_TVA / TOTAL_TVA / TOTAL_CU_TVA
C6 discountul de document si discount_evidentiat = VANZARI.DISCOUNT / DISCOUNT_EVIDENTIAT
C7 delegat, masina, agent, adresa de facturare, dataora_exp, text_aditional, listare_detaliata, tip_saft, efactura, id_ruta = VANZARI inclusiv cazul „documentul n-are delegat" → campul ramane gol (2.1)
C8 nicio scriere in baza — criteriul de baza al lui S8b SELECT dataoras, id_utils FROM vanzari WHERE id_vanzare = :id neschimbat dupa deschidere+inchidere; idem pe VANZARI_DETALII
C9 niciun dialog modal la deschidere (nici alegere de stoc, nici „nu ati selectat toata cantitatea", nici „doriti sa inchideti comanda") observatie
C10 aspectul e cel de introducere (decizia 5): fara banda, fara coloane de valori initiale, fara panou de diferente; difera doar titlul si butonul principal observatie

Pe fiecare tip de sursa, in plus:

Tip Ce se verifica specific
comanda (3, 21, 25, 28, 42, 47) Ct_clb_altele afiseaza numarul comenzii, eticheta „Nr. comanda", blocat; poDate.cListaSursa = VANZARI.ID_COMANDA
contract (2, 6, 26, 52) eticheta „Nr. contract", blocat; id_ctr incarcat; goContract din sesiune NU suprascrie antetul (2.2 nr. 7); daca S10 se implementeaza, niciun avertisment de re-derivare la simpla deschidere (cursor_retur_document citeste pretul stocat, rezultatul 6 al handoff-ului)
aviz (tip 4 = factura din avize) eticheta „Nr. aviz / avize" (3.2), blocat; poDate.cListaSursaAvize = randurile VANZARI_CORESP ale documentului; nicio intrebare „doriti sa se inregistreze si avizul de retur?" la deschidere (§6)
factura simpla (1, 5, 10) Ct_clb_altele pastrat si vizibil cu eticheta „Nr. factura" (3.2, ramurile 1 si 2 cu llCopiere), spre deosebire de emiterea normala unde e scos
retur (8, 9, 24) zi_curs e oricum scos azi pentru 8/9 (ofacturare.vc2:9720-9725) — comportament neschimbat; liniile se incarca cu cantitatile documentului, nu cu cele disponibile la retur
ROAAUTO poDate.id_ordl incarcat din VANZARI.ID_ORDL; [N] nu s-a verificat in aceasta sesiune ce alte campuri specifice ROAAUTO exista pe antet — roaauto_facturi.md nu a fost citit
proforma (eproforma = 1) poDate.eProforma incarcat (1.2.a) → frm_alte_date.Init sare din start pe ramura de proforma (:3110), iar gestionabil vine 0 din cursor (1.4)

8. Riscuri, goluri ramase, si intrebarile pentru Marius

8.1 Ce n-am putut stabili, si de ce

# Ce De ce
N1 Semantica exacta a valorilor VANZARI_CORESP.TIP (1/2/3, 4 liber) Cere citirea CASE-ului pe ntip din finalizeaza_factura, in afara perimetrului parcurs. Nu se preia din raportul precedent — e exact tiparul cifrei gresite din runda 13.
N2 Daca ocolirea alocarii de numar lasa poDate.rezultat_serii intr-o stare pe care frm_date_factura.Init o citeste gresit (ofacturare.vc2:9736) COMUN\programe\oserii_numere.prg necitit in aceasta sesiune
N3 Daca prefixul „Scadenta la N zile." adaugat la Init (2.2 nr. 11) e scos pe toate caile de iesire Am vazut operatia inversa la ferestre_cere_date.vc2:3037-3038, dar n-am urmarit toate caile de Unload/Destroy
N4 Campurile de antet specifice ROAAUTO roaauto_facturi.md necitit
N5 Daca tipurile 48/49 (custodie) intra in perimetrul etapei II Conteaza pentru 3.2, ramura 3
N6 Daca frm_facturare_articole2 (varianta paralela) trebuie tratata identic Are do_adauga_tot / do_adauga_articol proprii (ofacturare.vc2:17476, :17124), cu logica usor diferita (fara testul llGestionabil) — de decis daca S8 tinteste ambele forme sau doar frm_facturare_articole

8.2 Intrebari pentru Marius, cu recomandare

  1. Canalul de citire a liniilor: (A) extindem cursor_retur_document, (B) procedura noua cursor_editare_document, sau (C) FACT_VFACTURI_DETALII? (1.1) Recomandare: (B). Argumentul nu e estetica, ci ca (A) schimba comportamentul copierii (taxcode ar incepe sa se propage), iar (C) pierde ID_POL, PRETD si tratamentul valutar — partea grea. (B) da si libertatea de a alege corect GESTIONABIL (1.4.i).

  2. GESTIONABIL la editare: din nomenclatorul de azi (IN_STOC) sau din document (ID_GESTIUNE)? (1.4.i) Recomandare: din document. Un document editat trebuie sa arate cum a fost emis; regimul de stoc al articolului s-a putut schimba intre timp fara nicio legatura cu documentul.

  3. text_aditional: se normalizeaza la incarcare (Chr(170) → CR+LF) sau se afiseaza ca atare? (1.7) Recomandare: se normalizeaza la incarcare si se re-normalizeaza la comparatie, altfel orice document cu text pe mai multe randuri apare modificat la deschidere. Nota: cele doua rute de scriere salveaza forme diferite ale campului (truncat/substituit vs. brut) — merita semnalat separat, e un defect preexistent, nu al lui #13.

  4. zi_curs pe calea de editare: ascuns, sau afisat gol? (1.5) Recomandare: ascuns, cu precedent in acelasi Do Case (tipurile 8/9). E o abatere mica de la decizia 5, si o semnalez ca atare — un camp afisat gol pe un document care are curs ar fi mai derutant decat absenta lui.

  5. poDate.lEditare ca proprietate pe oDateFactura, sau parametru pe frm_alte_date.Init? (2.1) Recomandare: proprietate, pentru ca sunt cel putin patru locuri care au nevoie de semnal (2.2 nr. 9-13), nu unul, si pentru ca lCopiere e deja acolo, cu exact acelasi rol.

  6. id_ruta — proprietate noua pe oDateFactura? (1.5) Recomandare: da. Fara ea S8c nu poate implementa unul din cei 14 parametri, iar azi valoarea circula doar prin poRec, care nu exista in formularul unificat.

  7. Tipurile 48/49 si frm_facturare_articole2 (N5, N6) — intra in perimetrul etapei II? Recomandare: nu acum; se declara explicit ca neacoperite, ca sa nu se descopere la testare.

  8. Defectul de la 2.2 nr. 11 (prefixarea text_aditional la Init pentru contracte) — se repara in #13, se semnaleaza separat, sau se lasa? Recomandare: se ocoleste in #13 (prin lEditare) si se semnaleaza separat ca defect preexistent — repararea lui pe calea de emitere nu tine de aceasta poveste.

8.3 Ce a fost corectat fata de materialele existente

Afirmatie anterioara Stare
S8b: „lookup-ul delegatului trebuie sarit explicit, altfel pica primul criteriu" Nuantat — garda Empty(id_delegat) And Empty(id_masina) exista deja (ferestre_cere_date.vc2:3111); riscul e real doar pe documentele fara delegat si fara masina
S8b §1.2 / §7.4: „cheia de linie id_vanzare_det, coloana deja incarcata la S8" Infirmat — cursor_retur_document nu o intoarce; devine livrabil al lui S8
S8b §5.2: „S8 trebuie sa suprascrie implicitul zi_curs cu data reala salvata" Infirmat structural — nu exista coloana ZI_CURS pe VANZARI
Plan S8: etichetele lui Ct_clb_altele (cinci) Incomplet — sunt sase; lipseste „Nr. aviz / avize" (tip 4), exact cazul relevant pentru S9
Plan S8: „eliminat cand gnScadereStoc = 0 si tipul e 1/5/10 fara copiere" Incomplet — trei ramuri, tipurile 1,5,10,48,49, si pentru gnScadereStoc = 1, nu doar 0
Plan S8: „cursor_retur_document(V_COPIERE=1) pentru linii" Insuficient — umple selectorul, nu documentul; lipsesc ID_VANZARE_DET si TAXCODE
Plan S8: „completeaza_setari_document pentru antet" Insuficient — 16 proprietati din ~120, niciuna de identitate

Cercetare incheiata pe toate cele 8 puncte cerute in briefing. Afirmatiile portante au fisier:linie sau nume de coloana din dictionar. Nu s-a modificat niciun fisier de cod; pe Oracle numai SELECT pe all_tab_columns.