Folderul docs\ era pana acum in afara oricarui control de versiuni - nici git, nici SVN - desi contine planurile pe puncte, proiectarile si rapoartele de cercetare pe care se sprijina modificarile din cod. O stergere acolo era definitiva. Fisierele intermediare (handoff-uri intre sesiuni, diff-uri deja aplicate) au fost sterse inainte, nu versionate: ce era durabil in ele a intrat in antetele fisierelor de test la care se refereau. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SN8snvkk94KuhWwoXUUey3
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:liniesau 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:
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 luimodifica_date_factura, in afara deid_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.cursor_retur_document(V_COPIERE = 1)nu umple gridul documentului, ci gridul-sursa. Pe calea de copiere, rezultatul lui intra incrsarticole(selectorul din stanga), iarcrsfactura(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,:338creeaza_facturacrs([crsfactura])) [V]. Transferul in document se face numai prindo_adauga_tot→do_adauga_articol, care pentru articolele gestionabile trece prin dialogul de alegere din stoc (do_alege_stoc). Sectiunea 4.3.cursor_retur_documentnu intoarceID_VANZARE_DETsi nu intoarceTAXCODE[V] (lista completa de coloane:PACK_FACTURARE:3949-4054). FaraID_VANZARE_DET:- cheia de linie presupusa de S8b (§1.2 al raportului S8b) nu exista pe calea propusa;
- ruta ieftina
modifica_explicatie_articoldevine neapelabila — primul ei parametru esteV_ID_VANZARE_DET(PACK_FACTURARE:14511-14519) [V]. FaraTAXCODE, cerinta explicita a planului („explicatia sitaxcodepe 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_RESPONSABILsiID_VENCHELTnu sunt coloane peVANZARI(verificat peall_tab_columns) e dovada structurala ca grupul A traieste la nivel de linie de nota, nu de antet — exact ce spunearute_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]
- Truncat la 100 de caractere la scriere:
LEFT(Nvl(poDate.text_aditional,[]),100)(ofacturare.vc2:14356, identic pe toate ramurilescrie_*). CR+LFinlocuit cuChr(170)imediat inainte:poDate.text_aditional = Strtran(poDate.text_aditional, Chr(13)+Chr(10), Chr(170), 1, 100, 1)(:14281) — si atribuirea e pepoDateinsusi, deci obiectul ramane modificat dupa scriere.- Pe calea
modifica_date_facturanu exista nici truncare, nici substitutie (ofacturare_comun.vc2:4608trimite?poRec.text_aditionalca 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_masinaINAINTE de construirea luifrm_alte_date— lookup-ul nu mai ruleaza, fara niciun cod nou.completeaza_setari_documentle 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
poDatecu ultimul delegat al clientului, si atunci: ori documentul apare modificat, orimodifica_date_facturascrie un delegat pe care documentul nu l-a avut niciodata. Submodifica_date_facturacampul 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
poDatepopulat prin apel Oracle sau dintr-o variabila globala/go*inInit/Loade 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) → gardapoDate.lEditarede la 2.1; - nr. 13,
poGeneratorNumere— e apelat dinfactureaza(ofacturare.prg:207-209), nu dinInit. Pe calea de editare nu trebuie sa se aloce numar: seria si numarul vin din document (decizia F / plan §F).oGeneratorNumereare dejadezaloca_numarsiverifica_numar(ofacturare.prg:227,:250), dar calea corecta e sa nu se aloce deloc. [N] —COMUN\programe\oserii_numere.prgn-a fost citit in aceasta sesiune; planul citeaza:227-233pentru afirmatia „la modificare nu se aloca numar nou". De verificat la implementare ca ocolirea alocarii nu lasapoDate.rezultat_seriiintr-o stare pe carefrm_date_factura.Inito 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— creeazafrm_articol_facturafara sa-l arate (cutlImplicit = .T.) si calculeaza totalurile. Acceptabil. - pe ramura
Otherwise— cheamado_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 dejaLOT,SERIEsiID_GESTIUNEproprii, 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:
- nu deschide niciun dialog;
- pastreaza
lot,serie,id_gestiune,id_pol,pretddin document; - duce
id_vanzare_detintr-o coloana proprie noua pecrsfactura(nu inid_temp, care e suprasolicitat:Recno()pe o cale,id_vanzare_detpe alta —prelucreaza_facturacrsfaceid_vanzare_det As id_temp,ofacturare_comun.prg:1811[V]); - duce
taxcode. Precedentul de structura exista: blocultnTip = 30dinfactureaza(ofacturare.prg:340-378) face exact o populare directacrsarticole → crsfacturaprinINSERT 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:
ControlSourcelegate direct depoDate(ex.poDate.discount_evidentiat,ofacturare.vc2:11305) — atribuirea programatica declanseazaProgrammaticChange, nuValid[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,InteractiveChangear 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:
- Cheia
id_vanzare_detnu vine gratuit — S8b o presupunea incarcata („coloana deja incarcata la S8"). Nu e (1.1). Devine livrabil al lui S8, nu premisa. - Liniile fara
id_vanzare_det(adaugate in sesiune) se marcheaza cu0/.NULL.si inseamna direct „regenerare", ca in S8b §1.3. zi_cursse 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:
poDate.listaidNU poate purta aceasta informatie — la incarcare el trebuie sa fieid_vanzare-ul documentului insusi, ca sa se citeasca liniile (1.6). Doua campuri distincte, nu unul reinterpretat.- Semantica valorilor lui
TIPdinVANZARI_CORESPnu e stabilita aici [N] (1.6) — se citeste dinCASE-ul real al luifinalizeaza_facturala 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
-
Canalul de citire a liniilor: (A) extindem
cursor_retur_document, (B) procedura nouacursor_editare_document, sau (C)FACT_VFACTURI_DETALII? (1.1) Recomandare: (B). Argumentul nu e estetica, ci ca (A) schimba comportamentul copierii (taxcodear incepe sa se propage), iar (C) pierdeID_POL,PRETDsi tratamentul valutar — partea grea. (B) da si libertatea de a alege corectGESTIONABIL(1.4.i). -
GESTIONABILla 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. -
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. -
zi_curspe calea de editare: ascuns, sau afisat gol? (1.5) Recomandare: ascuns, cu precedent in acelasiDo 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. -
poDate.lEditareca proprietate peoDateFactura, sau parametru pefrm_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 calCopieree deja acolo, cu exact acelasi rol. -
id_ruta— proprietate noua peoDateFactura? (1.5) Recomandare: da. Fara ea S8c nu poate implementa unul din cei 14 parametri, iar azi valoarea circula doar prinpoRec, care nu exista in formularul unificat. -
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. -
Defectul de la 2.2 nr. 11 (prefixarea
text_aditionallaInitpentru contracte) — se repara in #13, se semnaleaza separat, sau se lasa? Recomandare: se ocoleste in #13 (prinlEditare) 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.