# 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: ```sql 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`.*