#13: formular de facturare unificat - etapa curenta

Squash al branch-ului de lucru plan13-s2.

Cod: clasa noua ofacturare_util (geometrie si utilitare de formular), inrolata in
roafacturare.prg; meniul "Facturare (nou)" plat, cu reparatia literalului peste
limita VFP de 255 de caractere care impiedica compilarea metodei.

Livrare: changelog 2.11.19, marcajul de versiune DB pentru scripturile finale din
SCRIPTURI_CLAR\2026\09, inventarul livrarii in docs/livrare_13.md si registrul de
erori deschise. Documentatia de lucru a etapei (cercetare, rapoarte, handoff-uri,
snapshot-uri de scripturi si backup-uri de rollback) a fost scoasa din arbore.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PkyjGyrV2S7932om4kfSiK
This commit is contained in:
2026-09-09 21:30:51 +03:00
parent 87063b06a8
commit 104c24ec20
80 changed files with 5585 additions and 27852 deletions

View File

@@ -1,800 +0,0 @@
# CORESP_CONT_VENCHELT ca sursa de cont de venit pentru articole fara politica de pret (decizia 27)
Cercetare pe cod, read-only, fara modificari. Continua `cont_venit_articol_fara_politica.md` si
`cont_venit_corespondente.md` (care stabilisera ca SCC vine azi din `NOTE_CONTABILE` prin lantul
politicii de pret) si `nota_contabila_fara_politica.md` (care stabilise ca `contabilizeaza_articol`
ridica FACT-024 si opreste tranzactia cand nu exista politica).
## 9. Intrebarea care putea rasturna tot: "in ROAACNPRO si pe factura din comanda/contract se adauga articole fara politica de preturi — de ce nu si aici?"
**Raspuns scurt, verificat pe cod: impresia e explicabila, dar niciunul din cele doua exemple nu e de
fapt un articol fara politica trecut cu bine prin `contabilizeaza_articol`. FACT-024 ramane blocantul
real — nu exista azi, in productie, niciun flux in care un articol ajunge la `contabilizeaza_articol`
fara sa fie membru al unei politici si sa treaca fara eroare.**
### 9a) Ce verifica de fapt FACT-024 — apartenenta articolului la politica liniei, nu "id_pol e gol"
Blocul (`D:\ROA\ROAFACTURARE\docs\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:7278-7302`, citat integral
la sectiunea 6) face:
```sql
SELECT COMPUS, ID_POL_ART
INTO V_COMPUS, V_ID_POL_ART
FROM VCRM_POLITICI_PRET_ART
WHERE ID_ARTICOL = detalii_articol.id_articol
AND ID_POL = detalii_articol.id_pol;
```
Mesajul confirma exact asta: `'Articolul ' || ... || ' nu este definit in politica de preturi ' || ...`
— verifica **apartenenta articolului la politica `id_pol` care a ajuns pe linie**, nu doar "exista vreun
`id_pol`". Insa `id_pol` **nu e derivat de Oracle** din client/contract/tip document — e parametru de
intrare explicit (`V_ID_POL IN NUMBER`, `adauga_articol_factura`, `ff_...:4993`, citat integral la
sectiunea 8a), trimis de VFP pe fiecare linie. Deci sunt **doua cauze distincte care duc la aceeasi
eroare**:
1. `id_pol` e NULL/gol pe linie (articolul a fost ales dintr-o cautare care nu are deloc coloana
`id_pol` — cazul `caut_articol`, sectiunea 9d) — `NO_DATA_FOUND` trivial (`X = NULL` nu se potriveste
niciodata).
2. `id_pol` e o valoare reala, dar articolul **nu e** in lista acelei politici — la fel `NO_DATA_FOUND`,
cu acelasi mesaj.
Ambele cauze converg spre FACT-024; codul nu le distinge in eroare. Formularea din intrebarea lui
Marius ("nu se cauta politica articolului, ci apartenenta articolului la politica documentului") e
**partial corecta**: verificarea e de apartenenta, dar `id_pol` nu vine din "antetul documentului" ca
o singura valoare fixa — vine per-linie, de la formularul de adaugare a articolului (sectiunea 9d).
### 9b) ROAACNPRO — importul Roris/Contracte NU trece deloc prin `contabilizeaza_articol`
Verificat direct in `D:\ROA\ROAACNPRO\Programe\proceduri_acnpro.prg` (read-only). Cautare
`pack_facturare\.|pack_acn\.|pack_contafin\.` in tot fisierul — apelurile de la salvarea facturii
(`factura_salvare_db`, `:3331-3467`) sunt:
```
proceduri_acnpro.prg:3332 lcSql = [begin pack_facturare.initializeaza_scriere_actrul(NULL,1); end;]
proceduri_acnpro.prg:3343 lcSql = [begin pack_facturare.initializeaza_date_factura(...
proceduri_acnpro.prg:3382 lcSql = [begin pack_facturare.adauga_articol_factura_deviz(...
proceduri_acnpro.prg:3412 lcSql = [begin pack_facturare.scrie_in_vanzari(0,...
proceduri_acnpro.prg:3430 lcSql = [begin pack_acn.salveaza_regdoc(?.id, ?.id_vanzare, ?.tip, ...
```
**Exact tiparul deja documentat pentru ROAAUTO "Alte servicii" in `nota_contabila_fara_politica.md`**:
`adauga_articol_factura_deviz` (insereaza doar in `VANZARI_DETALII_TEMP`, fara `ID_POL` — semnatura
n-are acest parametru) urmat de `scrie_in_vanzari` (copiaza in `VANZARI_DETALII`, **fara sa scrie vreo
nota contabila de venit**) — **`contabilizeaza_articol` nu apare deloc** in acest fisier (cautare
directa `contabilizeaza_articol` in `proceduri_acnpro.prg`: zero rezultate). Nota contabila a
documentului ACN o scrie **`pack_acn.salveaza_regdoc`**, o procedura complet separata, specifica ACN,
apelata cu parametri proprii (`?.id_articol, ?.id_locatia, ?.id_valuta, ?.id_client, ?.id_contract,
?.pret, ?.curs, ?.cantitate, ?.valval, ?.tvaval, ?.totval, ...`) — **fara `id_pol`** in lista de
parametri. Confirmat si de raportul deja existent `COMUN\docs\cercetare\import_roris_roaacnpro.md`
(sectiunea 3): "**nicio procedura Oracle stocata (`pack_*`) nu e apelata in etapa de import** ...
acestea sunt apelate abia la salvarea finala", si articolele/preturile vin din dialoage de calcul tarife
proprii ACN (`frm_calcul_tranzit`, `vctr_articole2`), nu din `CRM_POLITICI_PRET_ART`.
**Concluzie 9b**: ROAACNPRO nu e un exemplu de "articol fara politica trecut cu bine prin
`contabilizeaza_articol`" — e un produs care **nu foloseste deloc** acest mecanism de contabilizare
pentru facturile lui. Impresia lui Marius e intemeiata pe experienta ("acolo merge fara sa aleg vreo
politica"), dar explicatia e alta decat "FACT-024 se poate evita" — e ca **acel flux nu ruleaza deloc
codul care ar putea da FACT-024**.
### 9c) Factura din comanda/contract in ROAFACTURARE — articolul "liber" e de fapt din lista de preturi
Deja cercetat, cu dovezi, in `docs\cercetare\retur_si_lista_preturi.md` sectiunea B (citit integral aici,
nu doar rezumat). Punctul central (`retur_si_lista_preturi.md` sectiunea B7):
- **Contract** (tip 2,6,26,52): `crsarticole` (grila principala de articole, folosita si pentru
adaugare "libera") e populat de `pack_facturare.cursor_contract(...)`, care da **lista de preturi
completa**, nerestrictionata la continutul contractului — "azi se poate deja adauga liber din lista de
preturi pe un document contract".
- **Comanda** (tip 3): `crsarticole` e populat de `cursor_comanda`, **doar** articolele comenzii — nu
exista adaugare libera in fluxul normal (doar prin copiere de document, care re-adauga tot din
`cursor_preturi`).
Verificat aici, suplimentar, ca `cursor_preturi` (folosit si de `cursor_contract`, structura identica)
**selecteaza explicit `A.ID_POL`** ca coloana de output:
```sql
-- ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:2162-2167 (cursor_preturi, ramura V_TIP = 45, tipic pentru toate ramurile CASE)
OPEN V_CURSOR FOR
SELECT rownum as id_c,
B.ID_ARTICOL,
NULL AS LOT,
NULL as SERIE,
A.ID_POL,
...
```
si ca cursorul insusi porneste, la fiecare apel, prin `pack_facturare.completare_politica_stoc()`
(`ff_...:2151`), care garanteaza deja randuri in `CRM_POLITICI_PRET_ART` pentru toate articolele
`IN_STOC=1` (procedura citata integral la sectiunea 8c2). Deci **fiecare articol afisat in grila
"lista de preturi"/"contract" vine deja cu un `id_pol` valid, dintr-o politica reala**, exact pentru ca
e extras printr-un join pe `CRM_POLITICI_PRETURI`/`CRM_POLITICI_PRET_ART`. "Adaugare libera pe contract"
nu inseamna "articol fara politica" — inseamna "orice articol din lista de preturi, nu doar cele din
contract", dar tot cu politica ceruta, doar aleasa implicit prin cursor, nu prin dialogul manual
`do_cauta_politica`.
**Concluzie 9c**: nu e un contraexemplu la FACT-024 — e acelasi mecanism (articol + politica), livrat
printr-o alta grila de selectie (`crsarticole` populat de `cursor_contract` in loc de `caut_articol`),
care intampla sa nu ceara utilizatorului sa caute manual politica pentru ca cursorul o aduce deja
atasata pe fiecare rand.
### 9d) Ce diferentiaza de fapt "adaugare din nomenclator" (cazul #13) de "adaugare din lista de preturi/contract" (cazul care merge azi)
Diferenta e cursorul sursa al gridului de articole, nu tipul de document:
- **`caut_articol`** (`COMUN\programe\ocautare.prg:1636-1735`, folosit pentru cautare libera in
nomenclator) — SELECT-ul confirmat la `ocautare.prg:1671-1689` (`vnom_articole_crm`/`vnom_articole`):
**nu are coloana `id_pol`** in nicio ramura (`tlCRM` sau nu). Un articol ales prin acest dialog ajunge
in `poArticol` (scatter din `crsarticole`, `ofacturare.vc2:12850-12851`) **fara `id_pol`** — exact
cazul care da FACT-024 azi, si exact cazul cerut de povestea #13 ("adaugat direct din nomenclator").
- **`cursor_preturi`/`cursor_contract`** (Oracle, sectiunea 9c) — id_pol e parte din rezultat, pentru ca
interogarea porneste de la politica de pret, nu de la nomenclator.
Deci povestea #13 cere exact ce nu exista azi: un articol ales **prin cautarea de nomenclator**
(fara trecere prin vreo politica), caruia i se cere totusi sa treaca prin `contabilizeaza_articol` fara
eroare. Niciunul din exemplele lui Marius (ROAACNPRO, contract) nu demonstreaza ca asta functioneaza deja
undeva — primul nu foloseste deloc functia, al doilea nu e de fapt "fara politica".
### Verdict 9
**FACT-024 ramane blocantul real**, confirmand (nu contrazicand) analiza din sectiunile 6-8. Nu exista
azi, pe niciun flux de productie cercetat, un caz in care un articol trece prin
`pack_facturare.contabilizeaza_articol` fara sa fie membru al unei politici de pret si nu cade cu
eroare. Impresia lui Marius e intemeiata pe doua experiente reale, dar explicatia lor e alta:
- ROAACNPRO: alt produs, alta procedura de contabilizare (`pack_acn.salveaza_regdoc`), niciodata
`contabilizeaza_articol`;
- Contract in ROAFACTURARE: articolul PARE liber, dar e adus printr-un cursor care il livreaza deja cu
o politica reala atasata (`cursor_contract`/`cursor_preturi` + `completare_politica_stoc`).
Asta nu inseamna ca planul A (sectiunea 8, VFP alege singur un `id_pol`) sau planul B (sectiunea 6,
fallback in pachet) sunt de abandonat — arata insa ca **niciuna din cele doua nu se poate simplifica
la "nu faceti nimic, functioneaza deja"**: mecanismul de protectie e real si consecvent, nu un artefact
uitat.
## Constrangere noua (Marius, aparuta in timpul cercetarii): pack_facturare e plan B
Marius prefera sa **nu se scrie cod nou in `pack_facturare`** — e pachetul comun folosit de toata
suita, si orice ramura noua acolo e risc peste tot, nu doar in ROAFACTURARE. Sectiunea 6 (punctul de
injectie in pachet) ramane in raport ca informatie, dar devine **plan B**. Intrebarea reala, tratata in
sectiunea 8 de mai jos: **se poate obtine acelasi rezultat din VFP, alegand singur un `id_pol` potrivit,
fara sa se modifice `pack_facturare`?** Raspuns scurt: **da, ingredientele exista deja**, dar solutia nu
e "zero cod" — e cod VFP nou (nu Oracle), care se sprijina pe 3 mecanisme deja functionale in productie.
Detalii in sectiunea 8.
## Concluzie: fezabil cu conditii, si conditiile sunt mai mari decat pare din formularea deciziei 27
0. **Verificare prioritara (sectiunea 9, ceruta de Marius in timpul cercetarii): FACT-024 ramane
blocantul real.** Nu exista azi niciun flux de productie in care un articol trece prin
`contabilizeaza_articol` fara sa fie membru al unei politici de pret si nu cade cu eroare.
ROAACNPRO nu e un contraexemplu — nu foloseste deloc `contabilizeaza_articol` (alta procedura de
contabilizare, `pack_acn.salveaza_regdoc`). Contractul in ROAFACTURARE nu e un contraexemplu —
articolele "libere" vin din `cursor_contract`/`cursor_preturi`, care le ataseaza deja o politica
reala. Detalii complete in sectiunea 9.
1. **Tabelul exista, e populat cu date reale si e folosit activ in productie** — dar niciodata pentru
`CONT_VENIT`. Toti consumatorii gasiti (pack_vin, pack_devize, gestiune_pack_gest_import,
gestiune_rapoarte) citesc `CONT_CHELT` (sau `CONT_APROVIZIONARE`), niciunul `CONT_VENIT`. Coloana
`CONT_VENIT` e completata cu date reale (o migrare din 2023 acopera 20 de conturi de gestiune), dar
n-are niciun consumator — nici Oracle, nici VFP. Cititul ei pentru facturare ar fi un consumator nou,
nu o reteta deja rulata in productie.
2. **Cheia de join e stabila si testata**: mereu `CONT` (contul de gestiune al liniei), cu filtru
`STERS = 0`, prin `LEFT JOIN`. Pattern-ul se poate copia identic pentru `CONT_VENIT`.
3. **Punctul care schimba verdictul de la "fezabil simplu" la "fezabil cu conditii mari"**: in
`contabilizeaza_articol`, ramura "articol simplu" nu executa **nimic** (nici `scrie_nota`, nici
`descarca_gestiune`) daca `cursor_articol` (care citeste din `CRM_POLITICI_PRET_ART`) nu gaseste
niciun rand — si nu gaseste niciun rand exact in cazurile in care azi se ridica FACT-024. Un simplu
"prinde exceptia si continua" ar produce o linie facturata **fara nota contabila si fara descarcare
de gestiune, fara nicio eroare** — mai rau decat blocajul actual. Fallback-ul cere o **ramura noua,
paralela cursorului**, nu doar inlocuirea unei valori.
4. **Chiar cu ramura noua, `CU_TVA` si `IN_VALUTA` (cota TVA inclusa in pret / linie in valuta) nu au
nicio sursa alternativa** in afara `NOTE_CONTABILE`. Corespondentele si `NOM_ARTICOLE.CONT` dau doar
un cont, nu aceste doua semnale de interpretare a pretului — ele trebuie fie hardcodate (risc de
calcul gresit al bazei/TVA), fie cerute ca intrare noua de la utilizator/articol.
5. **704 ca literal de fallback are un precedent real in suita**, dar in alt subsistem: proprietatea
`cconte = 704` ("cont implicit articol client") din `frm_configurare_efactura`
(`COMUN\clase\anaf_efactura.vc2:8376`), folosita la import eFactura, nu in `pack_facturare`. Nu e o
reteta gata scrisa pentru facturare, dar arata ca alegerea lui Marius nu e arbitrara in suita.
## 1. `CORESP_CONT_VENCHELT` exista? Structura, DDL, consumatori
**Exista.** Nu are `CREATE TABLE` in arhiva `D:\ROA\DATABASE\SCRIPTURI_CLAR` (arhiva incepe 2009-2010,
tabelul e mai vechi — acelasi motiv pentru care `NOTE_CONTABILE`/`CRM_NOTE_VANZARI` nu au `CREATE TABLE`
in arhiva, documentat deja in `cont_venit_corespondente.md`).
**Coloane confirmate din `ALTER TABLE` + `CREATE OR REPLACE VIEW` (cronologic):**
- `CONT`, `CONT_CHELT`, `CONT_VENIT`, `STERS` — preexistente arhivei (folosite direct in view-ul din
2010 fara `ALTER TABLE` premergator vizibil).
- `CONT_APROVIZIONARE varchar2(4)` — adaugata 19.02.2010
(`D:\ROA\DATABASE\SCRIPTURI_CLAR\2010\02\ff_2010_02_19_03_GESTIUNI.sql:9`:
`alter table CORESP_CONT_VENCHELT add CONT_APROVIZIONARE varchar2(4);`).
- `CONT_DIFERENTE varchar2(4)` — adaugata 05.02.2015
(`D:\ROA\DATABASE\SCRIPTURI_CLAR\2015\02\ff_2015_02_05_01_GESTIUNE.sql:6-7`:
`alter table coresp_cont_venchelt add cont_diferente varchar2(4);` +
`comment on column CORESP_CONT_VENCHELT.cont_diferente is 'cont diferente de pret pe NIR fata de
pretul din factura de achizitie (ex: 6588 = 401 sau 308 = 401)';`).
- Nu am gasit `ID_CCV`/`DATAORAS` in DDL-ul cercetat (cautare directa, zero rezultate in
`SCRIPTURI_CLAR`) — interogarea din decizia 27 (`select id_ccv, cont, cont_chelt, cont_venit, sters,
dataoras, cont_aprovizionare, cont_diferente from coresp_cont_venchelt`) le presupune, dar tabelul
fiind pre-arhiva, DDL-ul complet nu e in cod static. **Ramane de verificat pe baza vie** (vezi sectiunea
finala) — probabil `ID_CCV` e cheia surogat (PK) si `DATAORAS` un timestamp tehnic, tipar comun in
suita ROA pentru tabelele de configurare, dar neconfirmat din DDL.
- Toate coloanele `CONT*` sunt `varchar2(4)` (confirmat din `ALTER TABLE` explicit pentru
`CONT_APROVIZIONARE`/`CONT_DIFERENTE`; consistent cu tratarea lor ca cod de cont in tot codul citit).
**View-ul consumat de VFP**, `VCORESP_CONT_VENCHELT`, filtreaza deja `STERS = 0` (deci VFP nu mai trebuie
sa filtreze separat):
```sql
-- D:\ROA\DATABASE\SCRIPTURI_CLAR\2015\02\ff_2015_02_05_01_GESTIUNE.sql:10-13
create or replace view vcoresp_cont_venchelt as
select cont, cont_chelt, cont_venit, cont_aprovizionare, cont_diferente
from coresp_cont_venchelt
where sters = 0;
```
**Populare cu date reale — `CONT_VENIT` e completat, nu doar declarat.** Migrare dedicata 23.02.2023
(`D:\ROA\DATABASE\SCRIPTURI_CLAR\2023\02\ff_2023_02_23_01_COMUN.sql:1-21`, titlul chiar din fisier:
`-- Completare cont venit pe corespondenta 3xx - 6xx/7xx`):
```sql
update CORESP_CONT_VENCHELT set cont_venit = '707' where cont = '301';
update CORESP_CONT_VENCHELT set cont_venit = '707' where cont = '302';
update CORESP_CONT_VENCHELT set cont_venit = '707' where cont = '3021';
... (3022,3023,3024,3025,3026,3028, 303 -> tot '707')
update CORESP_CONT_VENCHELT set cont_venit = '711' where cont = '331';
update CORESP_CONT_VENCHELT set cont_venit = '711' where cont = '332';
update CORESP_CONT_VENCHELT set cont_venit = '702' where cont = '341';
update CORESP_CONT_VENCHELT set cont_venit = '7015' where cont = '345';
update CORESP_CONT_VENCHELT set cont_venit = '703' where cont = '346';
update CORESP_CONT_VENCHELT set cont_venit = '7015' where cont = '348';
update CORESP_CONT_VENCHELT set cont_venit = '7018' where cont = '361';
update CORESP_CONT_VENCHELT set cont_venit = '707' where cont = '371';
update CORESP_CONT_VENCHELT set cont_venit = '707' where cont = '381';
```
Deci pentru conturile de gestiune uzuale (marfuri 371/381, materii prime 301-303/3021-3028, produse
finite 345/348, semifabricate 341, animale 346, ambalaje 381) exista deja o valoare `CONT_VENIT`
configurata in Oracle, de 3 ani. **Datele exista** — intrebarea e doar cine le citeste.
**Ecran de configurare in VFP: NU exista.** Cautare exhaustiva `CORESP_CONT_VENCHELT` (case-insensitive)
in `D:\ROA\ROAFACTURARE\COMUN` -> exact 3 fisiere, niciunul un formular de editare:
- `COMUN\clase\ointroduceri.vc2:2` — un comentariu de istoric (19.02.2010, mentioneaza
`crsconfigcvc (coresp_cont_venchelt)`), nu cod.
- `COMUN\programe\ointroduceri.prg:733` (procedura `update_corespondente_cvc`, citata integral mai jos)
— un `SELECT` care **reincarca** un cursor local, nu scrie in tabel.
- `COMUN\programe\updateserver.prg:733` — acelasi `SELECT`, alt loc de apel.
```
-- COMUN\programe\ointroduceri.prg:727-743
Procedure update_corespondente_cvc
If Used('crsconfigcvc')
Use In crsconfigcvc
Endif
*!* 19.02.2010
lcSql = [select cont,cont_chelt,cont_venit, cont_aprovizionare, cont_diferente from vcoresp_cont_venchelt order by cont]
*!* 19.02.2010 ^
lnSucces = goExecutor.oExecute(lcSql,[crsconfigcvc])
If lnSucces < 0
amessagebox(goExecutor.cEroare,16,"Eroare")
Endif
goExecutor.oReset()
Return lnSucces
Endproc
```
Deci tabelul e intretinut exclusiv prin **scripturi SQL manuale** (cele din `SCRIPTURI_CLAR`, rulate de un
DBA/dezvoltator la nevoie), nu printr-un formular VFP — la fel ca alte tabele tehnice de configurare mai
vechi din suita. Nu exista niciun `INSERT INTO CORESP_CONT_VENCHELT` in arhiva (randurile de baza,
`CONT`/`CONT_CHELT`/`CONT_VENIT`, predateaza arhiva); doar `UPDATE`/`ALTER TABLE` pentru coloanele
adaugate ulterior (`CONT_APROVIZIONARE` in 2010, `CONT_DIFERENTE` in 2015, valorile `CONT_VENIT` in
2023).
## 2. Cheia de cautare
**`CONT` = contul de gestiune al liniei, mereu.** Confirmat prin 4 exemple reale de `JOIN`, in module
diferite:
- **pack_vin** (`D:\ROA\DATABASE\SCRIPTURI_CLAR\2023\07\ff_2023_07_18_02_VIN_PACK_VIN.sql:1579-1589`,
procedura `inregistreaza_materiale`):
```sql
FROM (SELECT SUM(...) as suma, A.CONT as SCC, A.ACONT as ASCC, A.ID_GESTIUNE
FROM VIN_RULAJE_TEMP A WHERE A.TIP = V_TIP
GROUP BY A.ID_GESTIUNE, A.CONT, A.ACONT) A
LEFT JOIN CORESP_CONT_VENCHELT B
ON A.SCC = B.CONT
AND B.STERS = 0;
```
aici `A.SCC` e de fapt contul de gestiune (aliasul `SCC` vine din contextul rulajului de stoc, nu din
`NOTE_CONTABILE`) — deci join-ul e tot `cont_gestiune = B.CONT`.
- **pack_devize** (14+ aparitii identice intre 2013-2014, ex.
`D:\ROA\DATABASE\SCRIPTURI_CLAR\2014\01\ff_2014_01_22_01_DEVIZE_PACK_DEVIZE.sql:3214-3217`):
```sql
from rul_temp a
left join coresp_cont_venchelt b
on a.cont = b.cont
and b.sters = 0
```
`rul_temp.cont` e contul de gestiune al articolului din rulajul de stoc.
- **gestiune_pack_gest_import** (5+ aparitii 2013-2017, ex.
`D:\ROA\DATABASE\SCRIPTURI_CLAR\2017\05\ff_2017_05_31_01_GESTIUNE_PACK_GEST_IMPORT.sql:512`):
`left join coresp_cont_venchelt b` — pe `a.cont = b.cont` (acelasi tipar).
- **Raport recent, 2026** (`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\07\ff_2026_07_23_01_GESTIUNE_RAPOARTE.sql:25-29`,
view `VRUL_ACT_CHELTUIELI`):
```sql
iesiri AS (
SELECT R.*, CC.CONT_CHELT AS CONT_CHELT_CORESP
FROM VRUL_TOT R
LEFT JOIN CORESP_CONT_VENCHELT CC ON CC.CONT = R.CONT AND CC.STERS = 0
WHERE R.STERS = 0 AND R.CANTE <> 0
)
```
cel mai recent consumator gasit (23.07.2026), deci tabelul e inca "viu" in productie — dar
`CONT_CHELT_CORESP` calculat aici nici nu apare in `SELECT`-ul final al view-ului (e calculat si
abandonat), semn ca tiparul de "join pe CONT, ia campul de care ai nevoie" e suficient de standard
incat sa fie reutilizat fara alta discutie de design.
- **In VFP**: `Select crsconfigcvc; Locate For Cont = Upper(Alltrim(...))` — vezi sectiunea 3, mereu
aceeasi cheie `CONT`.
**Nu am gasit nicio discriminare suplimentara pe gestiune/tip document.** Toate `JOIN`-urile/`LOCATE`
sunt strict pe `CONT` (+ `STERS = 0`), fara `ID_GESTIUNE`, fara `TIP_DOC`. `ID_CCV` nu apare folosit ca
FK in niciun cod cercetat — pare o simpla cheie surogata a tabelului, neexpusa in `VCORESP_CONT_VENCHELT`
(view-ul nu o include).
**Ambiguitate / mai multe randuri active pentru acelasi `CONT`**: nu exista `UNIQUE`/`PRIMARY KEY`
vizibil in cod (DDL-ul e pre-arhiva), dar **toate cele ~20 de utilizari reale presupun un singur rand
activ per `CONT`** — niciun cod cercetat foloseste `DISTINCT`, `ROW_NUMBER()`, `MAX(...)` sau alt
mecanism de dezambiguizare pe rezultatul join-ului. Daca ar exista doua randuri active cu acelasi `CONT`,
`LEFT JOIN`-ul ar multiplica randurile din interogarea principala (risc de dublare de suma in
`pack_vin`/`pack_devize`) — nu exista protectie in cod contra acestui caz. **Presupunerea de unicitate e
implicita in tot codul care exista deja**, nu doar in propunerea lui Marius.
## 3. Cine il foloseste azi — CONT_CHELT are 4 module consumatoare, CONT_VENIT are zero
| Consumator | Fisier | Coloana citita |
|---|---|---|
| `pack_vin.inregistreaza_materiale` | `ff_2023_07_18_02_VIN_PACK_VIN.sql:1567` (si variante 2010/2012/2014) | `CONT_CHELT` |
| `pack_devize` (procedura de calcul cost material, 14+ export-uri 2013-2014) | `ff_2014_01_22_01_DEVIZE_PACK_DEVIZE.sql:3222` (si altele) | `CONT_CHELT` (in `GROUP BY`, folosit ca `SCD`) |
| `pack_gest_import` (5+ export-uri 2013-2017) | `ff_2017_05_31_01_GESTIUNE_PACK_GEST_IMPORT.sql:512` | `CONT_CHELT` (deductie din context, tipar identic) |
| `VRUL_ACT_CHELTUIELI` (raport, 2026) | `ff_2026_07_23_01_GESTIUNE_RAPOARTE.sql:26` | `CONT_CHELT` (calculat, needatat in output final) |
| VFP `ointroduceri.vc2` (bon consum, finalizare aprovizionare, transfer) | `:5476-5490` (`oinventar.vc2`), `:14663-14681`, `:15327-15332` | `CONT_CHELT`, `CONT_APROVIZIONARE` |
| VFP `ointroduceri.vc2` (diferente NIR vs factura) | `:15395-15403` (bloc dezactivat `IF .F.`) | `CONT_DIFERENTE` (cod comentat/mort, nu ruleaza azi) |
**Niciun consumator pentru `CONT_VENIT`** — cautare directa `CONT_VENIT` (case-insensitive) in tot
`D:\ROA\DATABASE\SCRIPTURI_CLAR` (arhiva completa 2009-2026): doar 3 fisiere, toate deja citate (2 view-uri
care includ coloana in `SELECT` fara sa o consume, 1 script de populare cu date). In VFP, cautare
`cont_venit` (case-insensitive) in `COMUN` si `ROAGEST`: un singur hit, `SELECT ... cont_venit ...` in
`update_corespondente_cvc`/`updateserver.prg` (fetch in cursor), **zero utilizari ale campului dupa
fetch** (nicio referinta `.cont_venit`/`crsconfigcvc.cont_venit` gasita).
**Concluzie**: propunerea lui Marius ar fi **primul consumator real al `CONT_VENIT`** in toata suita,
desi coloana e populata din 2023. Reteta "citeste corespondenta pe cont de gestiune" e deja rulata in
productie de 10+ ani, dar mereu pentru latura `CONT_CHELT` (cheltuiala la consum de stoc), niciodata
pentru `CONT_VENIT` (venit la vanzare). Riscul tehnic al join-ului (cheie, filtru `STERS`, tipul
`LEFT JOIN`) e deja validat de utilizarea existenta; riscul de **date** (daca valorile `CONT_VENIT`
completate in 2023 sunt corecte/complete pentru toate conturile de gestiune folosite azi in facturare,
nu doar cele 20 acoperite explicit) e nou si neverificat pe baza vie.
## 4. Semantica coloanelor
Dedusa din utilizari reale, nu din nume:
- **`CONT`**: contul de gestiune/stoc al liniei (clasa 3xx: 301-303, 331/332, 341, 345/346/348, 361,
371, 381) — cheia de cautare in toate cazurile vazute (sectiunea 2).
- **`CONT_CHELT`**: contul de cheltuiala (6xx) care se debiteaza cand marfa/materialul iese din gestiune
(consum, bon de consum, productie) — confirmat de toti cei 4+ consumatori din sectiunea 3, unde e
folosit mereu ca `SCD` (debit) pe o nota de tip "iesire din gestiune".
- **`CONT_VENIT`**: prin simetrie de nume si migrarea din 2023 ("cont venit pe corespondenta 3xx-7xx"),
e menit sa fie contul de venit (7xx) corespunzator vanzarii aceluiasi cont de gestiune — dar **nu are
niciun consumator care sa confirme comportamentul in executie** (nicio ramura de cod care sa arate ce
se intampla la `NULL`, la vanzare partiala, la retur). Semantica e dedusa din nume + date, nu din
utilizare, contrar regulii "nu presupune semantica dupa nume" — de aceea marchez ca **ipoteza, nu
fapt verificat pe comportament**.
- **`CONT_APROVIZIONARE`**: contul folosit la operatia "finalizare aprovizionare" (`ID_SET` 264/265),
cand se transfera valoarea din contul de tranzit (401 sau alt cont de gestiune) intr-un cont de
gestiune specific de tranzit intern (32x) — confirmat de comentariul din DDL 2010 si de utilizarea in
`ointroduceri.vc2:15323-15332` (`Case Alltrim(Upper(oSet.explicatia)) = 'FINALIZARE APROVIZIONARE' ...
lcContC = Alltrim(cont_aprovizionare)`).
- **`CONT_DIFERENTE`**: contul folosit pentru diferentele de pret intre NIR si factura de achizitie —
confirmat de comentariul DDL 2015 ("cont diferente de pret pe NIR fata de pretul din factura de
achizitie, ex: 6588 = 401 sau 308 = 401") si de codul `ointroduceri.vc2:15394-15403`, dar acel bloc e
**dezactivat** (`IF .F. THEN ... ENDIF`), deci in executia curenta ramane mereu fallback-ul hardcodat
`'6588'` — dovada ca chiar si un camp cu semantica clara si comentariu explicit poate ajunge neutilizat
in productie, ceea ce intareste incertitudinea despre `CONT_VENIT`.
- **`STERS`**: flag boolean soft-delete, filtrat explicit `= 0` in **toate** utilizarile gasite (view-ul
`VCORESP_CONT_VENCHELT` il filtreaza deja la nivel de view; join-urile Oracle directe pe tabel il
filtreaza explicit `AND B.STERS = 0`/`AND CC.STERS = 0`). Niciun cod cercetat citeste randuri cu
`STERS = 1`.
- **`DATAORAS`**: nu apare in niciun cod cercetat (Oracle sau VFP) — nu am gasit nicio referinta directa
la aceasta coloana in afara interogarii propuse in decizia 27. Tipic in suita ROA e un timestamp
tehnic de audit (data ultimei modificari a randului), dar **neconfirmat aici** — ramane de verificat pe
schema vie.
- **Ambiguitate pe `CONT`**: vezi sectiunea 2 — niciun cod nu trateaza cazul "mai multe randuri active
pentru acelasi CONT"; presupunerea de unicitate e implicita, nu impusa de o constrangere vizibila.
## 5. `NOM_ARTICOLE.CONT` pentru articole negestionabile si fallback pe 704
- **`NOM_ARTICOLE.CONT` nu e restrans la clasa 3** — reconfirmat (vezi deja `cont_venit_corespondente.md`
sectiunea 2): validarea `verific_cont` (`COMUN\programe\oproceduri_comune.prg:2389-2415`) verifica doar
ca contul exista in planul de conturi al anului, fara filtru de clasa; niciun `CHECK CONSTRAINT` in
DDL. Deci campul poate contine legitim un cont 6xx/7xx pentru un articol negestionabil, exact cum
presupune decizia 27.
- **Niciun fallback pe `704` existent in `pack_facturare`** — cautare `'704'` (literal SQL) in
`D:\ROA\ROAFACTURARE\docs\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` (17000+ linii, exportul curent al
pachetului): **zero rezultate**. Cautare in tot `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026` (toate scripturile
din 2026): **zero rezultate**. Fallback-ul pe 704 propus de Marius ar fi cod nou, nu o reteta deja
scrisa in `pack_facturare`.
- **Exista totusi un precedent independent pentru 704 ca "cont implicit de venit"**, in alt subsistem:
```
COMUN\clase\anaf_efactura.vc2:8370-8376 (clasa "frm_configurare_efactura")
*p: cconte && cont implicit articol client
*p: ccontp && cont implicit articol furnizor
...
cconte = 704
ccontp = 628
```
E o proprietate cu valoare implicita `704` ("cont implicit articol client", deci un cont de venit
implicit pentru liniile generate la import eFactura) intr-un ecran de configurare a modulului ANAF
eFactura. **Nu am gasit cod care sa citeasca explicit `.cconte`** (cautare `\.cconte\b` in tot `COMUN`:
zero rezultate) — proprietatea e definita cu valoare implicita, dar consumatorul ei concret n-a fost
gasit in timpul alocat (posibil legat generic la un camp de configurare cu acelasi nume, tipar comun in
suita, dar neconfirmat aici). **Ce arata cu certitudine**: alegerea `704` ca implicit pentru "cont
articol client" nu e o inventie a acestei cereri — a mai fost aleasa o data, independent, de altcineva
din echipa (sau de Marius insusi, in alt moment), pentru un scop asemanator. Nu e insa o reteta de cod
reutilizabila direct in `pack_facturare` — e in alt pachet/clasa, alt flux (import, nu emitere).
## 6. Punctul de injectie in `contabilizeaza_articol`
Sursa: `D:\ROA\ROAFACTURARE\docs\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql` (exportul curent al
pachetului, 17548+ linii). Numerotarea de mai jos e din **acest fisier**, verificata prin citire directa
(difera cu cateva linii fata de exportul mai vechi citat in rapoartele anterioare, care avea FACT-024 la
7301 in loc de 7311 — normal, fisierul a mai fost actualizat intre timp).
**Structura exacta a blocului care ridica FACT-024** (`:7275-7302`, inceputul functiei):
```
7275 BEGIN
7276
7277 -- 05.07.2011
7278 BEGIN
7279 SELECT COMPUS, ID_POL_ART
7280 INTO V_COMPUS, V_ID_POL_ART
7281 FROM VCRM_POLITICI_PRET_ART
7282 WHERE ID_ARTICOL = detalii_articol.id_articol
7283 AND ID_POL = detalii_articol.id_pol;
7284 EXCEPTION
7285 WHEN NO_DATA_FOUND THEN
7286 SELECT DENUMIRE
7287 INTO lcArticol
7288 FROM NOM_ARTICOLE
7289 where id_articol = detalii_articol.id_articol;
7290
7291 SELECT NUME_LISTA_PRETURI
7292 INTO lcPolitica
7293 FROM CRM_POLITICI_PRETURI
7294 where id_pol = detalii_articol.id_pol;
7295
7296 RAISE_APPLICATION_ERROR(-20000,
7297 'Articolul ' || detalii_articol.id_articol || '|' ||
7298 lcArticol ||
7299 ' nu este definit in politica de preturi ' ||
7300 detalii_articol.id_pol || '|' || lcPolitica ||
7301 '! (FACT-024)');
7302 END;
```
**Acesta e locul unde s-ar decide "articolul nu are politica" — dar NU e suficient sa se schimbe doar
acest bloc.** Motivul, cu citate exacte:
1. Dupa acest bloc, codul verifica `IF V_COMPUS = 1 THEN` (articol compus, `:7305`, ridica alta eroare,
FACT-016) `ELSE` (`:7391`) — ramura "articol simplu" care e cea relevanta pentru cazul cerut.
2. Ramura "articol simplu" **deschide un cursor nou**, `cursor_articol` (definit la `:7218-7271`),
filtrat exact pe aceeasi cheie care a dat `NO_DATA_FOUND` mai sus:
```
7261 FROM CRM_POLITICI_PRET_ART A
...
7268 WHERE
7269 -- A.STERS = 0 AND
7270 A.ID_POL = detalii_articol.id_pol
7271 AND A.ID_ARTICOL = detalii_articol.id_articol;
```
3. Tot corpul de lucru real (calculul `V_SCD`/`V_ASCD`/`V_SCC`/`V_ASCC`/`V_EXPLICATIE`, apelul
`pack_facturare.scrie_nota(...)` la `:7443-7467`, apelul `pack_facturare.descarca_gestiune(...)` la
`:7476-7498`, scrierea discountului la `:7501-7518`) e **in interiorul**
`WHILE cursor_articol%FOUND LOOP` (`:7396-7541`). **Daca articolul nu are politica de pret, acest
cursor nu returneaza niciun rand** (e exact aceeasi conditie care a dat `NO_DATA_FOUND` la pasul 1,
pe acelasi `(id_pol, id_articol)`), deci bucla ruleaza zero iteratii.
**Concluzia tehnica**: daca s-ar modifica *doar* blocul `EXCEPTION WHEN NO_DATA_FOUND` (pasul 1) ca sa nu
mai ridice `RAISE_APPLICATION_ERROR` si sa lase `V_COMPUS := 0` implicit, executia ar trece la ramura
"articol simplu", ar deschide `cursor_articol`, acesta ar gasi tot zero randuri (aceeasi cauza), bucla nu
ar rula deloc, si functia ar reveni `RETURN V_INCASAT_CALCUL` cu valoarea initiala `0`
(declarata la `:7178`), **fara sa scrie nicio nota contabila si fara sa descarce gestiunea** — silentios,
fara nicio eroare. Asta e o regresie mai grava decat FACT-024: azi tranzactia se opreste vizibil; cu un
fallback naiv, factura s-ar emite fara inregistrare contabila si fara descarcare de stoc, nedetectabil
fara audit manual.
**Ce ar cere de fapt fallback-ul, pentru a pastra comportamentul actual cand politica exista**:
- Blocul de la pasul 1 ar trebui sa **distinga** explicit cazul "politica lipseste" (continua cu
fallback) de cazul "articol compus fara politica" (probabil tot eroare, nu e in scopul cerut) —
posibil punand un flag local (`V_ARE_POLITICA := FALSE`) in loc de `RAISE_APPLICATION_ERROR`.
- Ar trebui adaugata o **ramura noua, in afara/inainte de bucla `cursor_articol`**, activa doar cand
`V_ARE_POLITICA = FALSE`, care sa:
- calculeze `V_SCD`/`V_ASCD` **reutilizand `CASE`-ul existent pe tip document** (`:7398-7423` —
acesta nu depinde de `cursor_articol`, poate fi extras/reutilizat identic);
- calculeze `V_SCC` din sursa noua (corespondente pentru articol gestionabil / `NOM_ARTICOLE.CONT` sau
`704` pentru negestionabil) — **doar acest pas e cel descris de decizia 27**;
- decida un `V_ASCC` (azi vine din `NVL(crs_rand_articol.ascc, GetAnaliticByGrupUtilizatori(...))` —
fara rand din cursor, ar trebui sa cada direct pe `GetAnaliticByGrupUtilizatori(...)`, posibil fara
modificare, pentru ca aceasta functie nu depinde de cursor);
- decida `V_EXPLICATIE`, `ID_VENCHELT`, `ID_SECTIE`, `CU_TVA`, `IN_VALUTA` — vezi sectiunea 7, aici e
gaura reala, nu doar cod de rescris.
- apeleze explicit `pack_facturare.scrie_nota(...)` si, conditionat, `descarca_gestiune(...)`, cu
parametrii calculati mai sus, **duplicand** (nu reutilizand) logica din interiorul buclei existente.
Asta confirma exact avertismentul din decizia 27 insasi ("modificare in COMUN, cu impact peste toata
suita") — nu e un `IF` adaugat pe o linie, e o ramura noua de ~40-60 linii care trebuie sa reproduca o
parte din logica ramurii existente, cu surse diferite pentru fiecare camp.
## 7. Ce se pierde fata de lantul complet `CRM_POLITICI_PRET_ART -> ... -> NOTE_CONTABILE`
Aceasta e partea centrala a raspunsului: **fallback-ul pe cont de venit (corespondente + nomenclator +
704) acopera doar `SCC`, nu si restul campurilor pe care lantul de politica de pret le aduce azi din
`NOTE_CONTABILE` fara efort.** Campurile aduse azi de `D.EXPLICATIE, D.SCD, D.ASCD, D.SCC, D.ASCC,
D.CU_TVA, D.IN_VALUTA` (`cursor_articol`, `:7248-7253, 7259-7260`) si ce se intampla cu fiecare fara
politica:
- **`SCD`/`ASCD` (contul debitor si analiticul lui)**: **nu se pierde** — `V_SCD` se calculeaza deja
independent de `NOTE_CONTABILE`, dintr-un `CASE` pe tipul de document (`:7398-7423`, ex. `461` pt aviz
catre debitori, `418` pt aviz simplu, `crs_rand_articol.scd` doar pt factura normala — dar acesta din
urma **tot vine din `NOTE_CONTABILE.SCD` prin cursor**, deci pt factura normala SCD s-ar pierde la fel
ca SCC). **Corectie necesara fata de formularea initiala a intrebarii**: nu doar SCC vine din
`NOTE_CONTABILE` pt factura normala — si SCD (contul de creante, tipic 411) vine de acolo. Decizia 27
vorbeste doar de "cont de venit", nu de contul debitor — inseamna ca **si sursa lui `V_SCD` pentru
factura normala ar trebui rezolvata** (probabil un cont fix, gen 411/`4111`, dar nu e in scopul
deciziei 27 asa cum e formulata azi).
- **`CU_TVA`**: **se pierde, fara alta sursa in cod.** Controleaza daca pretul de pe linie e interpretat
cu sau fara TVA la scrierea sumei (`pack_facturare.scrie_nota(...)`, parametru `crs_rand_articol.cu_tva`
la `:7463`). Nu exista nicio coloana echivalenta in `CORESP_CONT_VENCHELT` sau `NOM_ARTICOLE`. Fara ea,
fallback-ul trebuie sa aleaga o valoare hardcodata sau sa o deriveze din alt camp deja disponibil pe
`detalii_articol` (ex. `pret_cu_tva`, vazut ca parametru separat la `:7447` — posibil suficient, dar
necesita verificare separata, nu facuta aici).
- **`IN_VALUTA`**: **se pierde, fara alta sursa.** Controleaza daca linia e tratata ca fiind in valuta la
nivel de nota contabila (`V_IN_VALUTA := crs_rand_articol.in_valuta;` la `:7470`, folosit apoi la
discount `:7507`). La fel ca `CU_TVA`, nicio coloana echivalenta in corespondente/nomenclator.
- **`EXPLICATIE`**: se pierde ca sablon configurat, dar are deja fallback partial in cod — `V_EXPLICATIE`
e calculat cu un `CASE` pe tipul de operatie (`:7430-7439`, ex. concateneaza `pack_facturare.cdescriere`
pt facturi tip 7) folosind `crs_rand_articol.explicatie` ca baza; fara cursor, baza ar fi goala/NULL,
dar structura `CASE` insasi nu depinde de cursor — impact moderat, nu blocant.
- **`ID_VENCHELT`**: are deja un fallback la nivel de sesiune —
`NVL(pack_facturare.nid_venchelt, NVL(A.ID_VENCHELT, C.ID_VENCHELT))` (`:7244-7245`) — daca
`pack_facturare.nid_venchelt` e setat global (parametru de sesiune), fallback-ul functioneaza deja fara
politica; daca nu e setat, ramane `NULL` (fara eroare in cod, dar posibil relevant pentru rapoarte care
folosesc aceasta dimensiune, neverificat).
- **`ID_SECTIE`**: acelasi tipar — `NVL(pack_facturare.nid_sectie_stoc, C.ID_SECTIE)` (`:7246`), cu
fallback pe variabila de sesiune.
- **`ID_SET`** (identificatorul setului de nota folosit la scriere): **nu se pierde** — parametrul trimis
efectiv la `scrie_nota` e `pack_facturare.nid_set` (variabila de sesiune, `:7462`), nu
`crs_rand_articol.id_set` din cursor (acel camp e citit la `:7247` dar nu e folosit la apelul de
scriere in ramura articol simplu — verificat direct in cod). Deci acest camp special nu depinde de
politica de pret.
**Rezumat sectiunea 7**: din cele 7 campuri aduse de lantul politicii de pret, 2 (`ID_VENCHELT`,
`ID_SECTIE`) au deja fallback pe variabile de sesiune si ar functiona fara politica; `EXPLICATIE` are
impact moderat (structura de calcul ramane, doar baza se pierde); `ID_SET` nu depinde deloc de politica
in ramura articol simplu; dar **`CU_TVA` si `IN_VALUTA` nu au nicio sursa alternativa** — sunt gaura reala
pe care simpla adaugare a unui cont de venit din corespondente/nomenclator nu o umple. **Confirmarea
finala a intrebarii din cerere**: da, fallback-ul pe cont de venit e insuficient pentru o nota contabila
completa — nu pentru ca "SCC" ar fi gresit, ci pentru ca doua campuri de control (TVA, valuta) raman fara
sursa.
## 8. Varianta VFP, fara sa se atinga `pack_facturare` (plan A, cerut de Marius)
Ideea de verificat: daca `contabilizeaza_articol` deriva `SCC` din `id_pol`, VFP ar putea sa aleaga
singur, inainte de a trimite articolul, un `id_pol` a carui nota are deja `SCC` = contul de venit
calculat dupa regula lui Marius — pachetul ar rula neschimbat. Raspunsul, punct cu punct:
### a) `id_pol` vine din VFP? Da — e trimis explicit, pe fiecare linie
Semnatura efectiva a procedurii apelate pe fluxul principal (verificat direct in export, nu presupus):
```
D:\ROA\ROAFACTURARE\docs\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:4989-5015
PROCEDURE adauga_articol_factura(V_ID_TEMP IN NUMBER,
V_ID_ARTICOL IN NUMBER,
V_SERIE IN VARCHAR2,
V_EXPLICATIE IN VARCHAR2,
V_ID_POL IN NUMBER,
V_ID_GESTIUNE IN NUMBER,
...
V_CONT IN VARCHAR2,
...
```
`V_ID_POL` e parametru de intrare explicit — Oracle nu il deriva din client/contract/tip document, il
primeste ca atare de la VFP. **Niciun alt parametru din aceasta lista nu ofera un canal pentru
SCC/id_set** (raspuns si la punctul d — vezi mai jos).
**De unde il ia azi VFP**: din controlul de cautare obligatoriu al formularului `frm_articol_factura`
(clasa `ofacturare.vc2`):
```
COMUN\clase\ofacturare.vc2:6822-6825
ADD OBJECT 'Ct_clb_politici_preturi' AS ct_clb_cautare WITH ;
cconditie = ISNULL(poDate.id_pol) or EMPTY(poDate.id_pol), ;
cprocedura = thisform.do_cauta_politica, ;
cvar_afisata = poDate.nume_politica, ...
```
```
COMUN\clase\ofacturare.vc2:7167-7182
PROCEDURE do_cauta_politica
Local loCauta
loCauta = caut_politici_curente_util()
If !Isnull(loCauta) And !Empty(Nvl(loCauta.id_pol,0))
poDate.id_pol = loCauta.id_pol
poDate.nume_politica = loCauta.nume
...
```
`cconditie = ISNULL(poDate.id_pol) or EMPTY(poDate.id_pol)` e exact starea "articol fara politica" din
cererea #13 — controlul UI cere explicit utilizatorului sa caute o politica cand campul e gol. VFP
controleaza integral valoarea: azi vine din alegerea utilizatorului, dar nimic tehnic nu impiedica sa fie
setata programatic, fara interactiune, la un `id_pol` calculat.
### b) Drumul invers e interogabil? Da — e simetricul exact al cursorului deja documentat la sectiunea 6
Cursorul din `contabilizeaza_articol` (`ff_...:7261-7267`) merge
`CRM_POLITICI_PRET_ART -> CRM_POLITICI_PRETURI -> CRM_NOTE_VANZARI -> NOTE_CONTABILE` prin
`A.ID_POL = B.ID_POL`, `B.ID_NOTA = C.ID_NOTA`, `C.ID_SET = D.ID_SET`. Interogarea inversa, plecand de la
un `SCC` dorit catre `id_pol`-urile candidate, foloseste aceleasi coloane de legatura, doar fara filtrul
pe articol:
```sql
SELECT DISTINCT PP.ID_POL
FROM CRM_POLITICI_PRETURI PP
JOIN CRM_NOTE_VANZARI NV ON NV.ID_NOTA = PP.ID_NOTA
JOIN NOTE_CONTABILE NC ON NC.ID_SET = NV.ID_SET
WHERE NC.SCC = :cont_venit_calculat; -- '707'/'711'/'702'/'703'/'7015'/'7018' (gestionabil) sau valoarea din NOM_ARTICOLE.CONT/'704' (negestionabil)
```
E o interogare noua (nu am gasit-o deja scrisa nicaieri in cod), dar foloseste exclusiv coloane si
relatii deja confirmate ca existente si corecte (sectiunea 1 din `cont_venit_corespondente.md` + citirea
directa a `cursor_articol` la sectiunea 6 a acestui raport). **Neverificat pe date reale**: cate randuri
returneaza pentru fiecare `SCC` candidat — zero (nicio politica configurata cu acel SCC azi), unul
(cazul ideal) sau mai multe (ambiguitate: care `id_pol` se alege?). Interogarea de rulat pe baza vie e
chiar cea de mai sus, cu `:cont_venit_calculat` inlocuit pe rand cu `707`, `711`, `702`, `703`, `7015`,
`7018`, `704`.
### c) Se poate insera din VFP un rand in `CRM_POLITICI_PRET_ART`? Da — cod existent, functional, deja rulat in productie
Doua mecanisme distincte, ambele reale:
**c1. RPC direct, apelabil din VFP azi** — `pack_preturi.adauga_politica_pret_art`, apelat din
`ofacturare.vc2` in procedura de modificare a listei de preturi la NIR (`ID_SET = 231`):
```
COMUN\clase\ofacturare.vc2:15551-15587
*!* verific daca exista articolul in politica de preturi
lcSql = [Select id_pol_art, id_venchelt, proc_tvav from crm_politici_pret_art where id_pol = ] + Alltrim(Str(tnIdPol)) + [ and id_articol = ] + Alltrim(Str(tnIdArticol))
lnSucces = goExecutor.oExecute(m.lcSql, "cPoliticiPretArt")
...
If Nvl(loPoliticaPretArt.id_pol_art,0) = 0
lcSql = [begin pack_preturi.adauga_politica_pret_art(] + ;
ALLTRIM(Str(tnIdPol)) + [,] + ; && id_pol
Alltrim(Str(tnIdArticol)) + [,] + ; && id_articol
Alltrim(Str(tnPretLista,20,4)) + [,] + ; && pret
Alltrim(Str(tnPretftvaLista,20,4)) + [,] + ; && pretftva
Alltrim(Str(tnPretvtvaLista ,20,4)) + [,] + ; && pretctva
[NULL, ] + ; && id_valuta
Alltrim(Str(tnProcTvav,20,4)) + [,] + ; && proc_tvav
[NULL, ] + ; && procent
[NULL, ] + ; && id_venchelt
Alltrim(Str(gnIdUtil)) + ; && id_util
[); end;]
```
Verifica intai daca exista deja un rand (`id_pol`, `id_articol`), si daca nu, insereaza unul nou prin RPC
existent — **exact tiparul necesar pentru punctul (c)**: "adauga articolul X in politica de pret Y",
apelabil din VFP fara nicio modificare de pachet (`pack_preturi` nu e `pack_facturare`).
**c2. Auto-populare in masa, deja existenta in `pack_facturare` insusi, dar fara parametru nou** — o
procedura care nu ar necesita nicio modificare de cod pentru ca deja exista si e apelabila ca atare:
```
D:\ROA\ROAFACTURARE\docs\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:2093-2120
PROCEDURE completare_politica_stoc IS
BEGIN
IF pack_facturare.nid_politica_stoc IS NOT NULL THEN
MERGE INTO CRM_POLITICI_PRET_ART A
USING (SELECT ID_ARTICOL FROM NOM_ARTICOLE
WHERE STERS = 0 AND INACTIV = 0 AND IN_STOC = 1) B
ON (A.ID_POL = pack_facturare.nid_politica_stoc AND A.ID_ARTICOL = B.ID_ARTICOL)
WHEN NOT MATCHED THEN
INSERT (ID_POL, ID_ARTICOL, ID_VALUTA)
VALUES (pack_facturare.nid_politica_stoc, B.ID_ARTICOL, pack_facturare.nid_moneda_nationala);
...
END IF;
END completare_politica_stoc;
```
Aceasta e o functie **existenta, apelabila fara nicio modificare**, care garanteaza deja un rand in
`CRM_POLITICI_PRET_ART` pentru *orice* articol cu `IN_STOC = 1` sub o singura politica "de stoc"
(`pack_facturare.nid_politica_stoc`). Acest id_pol e la randul lui o **optiune de configurare controlata
din VFP**:
```
COMUN\clase\ofacturare.vc2:21733-21734 (Init-ul ecranului de optiuni facturare)
actualizeaza_politica_pret(23,@gnId_pol_pret_tr,@lcPolPretTr)
actualizeaza_politica_pret(1,@gnId_pol_pret_stoc,@lcPolPretStoc)
...
COMUN\clase\ofacturare.vc2:21753
This.nidpolpretstoc = Nvl(gnId_pol_pret_stoc,0)
```
`gnId_pol_pret_stoc` e o optiune globala (`ID_POL_PRET_STOC`, salvata/citita prin
`actualizeaza_politica_pret`), care alimenteaza `pack_facturare.nid_politica_stoc` la initializarea
sesiunii Oracle. **Avertisment**: aceasta politica pare deja folosita azi pentru un scop specific
(vanzare/evaluare la pret de stoc — `nin_valuta`, `nTipVanzareRetail` sunt tratate diferentiat pentru ea
la `ff_...:7223-7260`), cu o singura nota/SCC configurata pentru toate articolele `IN_STOC=1`. Nu e
direct reutilizabila pentru reteta pe 6 conturi diferite (707/711/702/703/7015/7018) ceruta de decizia
27 — dar **arata modelul exact** ("o politica tehnica, configurata o singura data, populata automat"),
model care se poate replica de cate ori e nevoie (o politica tehnica per `SCC` distinct), fara sa se
toate cod in `pack_facturare`.
### d) Alte cai de bypass la nivel de parametru — cautate explicit, nu gasite
Semnatura completa a `adauga_articol_factura` (sectiunea a) si a variantelor `_deviz`/`_stoc` (deja
citate in `cont_venit_articol_fara_politica.md`) **nu au niciun parametru pentru cont de venit sau
`id_set` direct** — singurul levier disponibil la nivelul acestui apel e `V_ID_POL`. Nu exista un
parametru `V_SCC`/`V_ID_SET`/`V_COD_VENIT` pe nicio varianta citita. Concluzie: **`id_pol` e singura
cale de influenta din VFP asupra contului de venit**, exact premisa din care a pornit intrebarea.
### e) Verdict onest
**Cale VFP posibila, dar nu "zero cod" si nu fara pasi de configurare o singura data**:
1. VFP calculeaza `SCC`-ul dorit exact ca in decizia 27 (interogare simpla, deja tipul de cod scris in
`update_corespondente_cvc` — sectiunea 1 a acestui raport): `CORESP_CONT_VENCHELT.CONT_VENIT` pe
contul de gestiune pentru articol gestionabil, `NOM_ARTICOLE.CONT` (daca e 6xx/7xx) sau `'704'` pentru
negestionabil.
2. VFP cauta (interogarea de la punctul b) o `id_pol` existenta a carei nota are deja acel `SCC`. **Daca
gaseste una** (plauzibil pentru conturile comune 707/711/702/703, care probabil se folosesc deja in
politici de pret reale ale firmei) — trece la pasul 3 direct, fara nicio configurare noua.
3. VFP se asigura (RPC `pack_preturi.adauga_politica_pret_art`, punctul c1, deja existent) ca exista un
rand `CRM_POLITICI_PRET_ART` pentru (acel `id_pol`, articolul curent) — insereaza unul cu pretul
tastat manual de utilizator (`tnPretLista` = pretul liniei) daca nu exista.
4. VFP trimite acel `id_pol` in `adauga_articol_factura`, exact ca pe fluxul actual (cu politica).
`contabilizeaza_articol` **ruleaza neschimbat** — si, bonus fata de varianta plan B (sectiunea 7),
**campurile `CU_TVA`/`IN_VALUTA`/`EXPLICATIE`/`ASCD`/`ASCC` vin corect din nota reala configurata**,
nu raman goale ca in fallback-ul partial din pachet.
**Ce nu e gratuit**:
- **Pasul 2 poate rata** (nicio politica existenta cu acel `SCC`) — pentru un `SCC` fara precedent (de
ex. `704`, pentru care nu am gasit nicio nota configurata azi, sectiunea 5), ar trebui creat manual,
o singura data, un lant nou `CRM_POLITICI_PRETURI` + `CRM_NOTE_VANZARI` + `NOTE_CONTABILE` (contabilul
configureaza nota din `frm_config_note_contabile[2007]`, deja documentat in
`cont_venit_corespondente.md` sectiunea 1) — asta e configurare (ecran existent), nu cod nou, dar e un
pas manual, nu automat.
- **Efect lateral real**: o "politica tehnica" (creata special pentru acest mecanism) ar aparea in
ecranul de cautare politici al utilizatorului (`caut_politici_curente_util()`, aceeasi functie folosita
la cautarea normala) — trebuie fie exclusa explicit din acel cursor de cautare (filtru pe un flag/prefix
dedicat), fie acceptata ca vizibila, cu riscul ca un utilizator sa o aleaga din greseala pentru o
factura normala. Nu am verificat daca `caut_politici_curente_util()` are deja un filtru care ar exclude
automat politici fara preturi reale/marcate altfel.
- **Ambiguitatea de la pasul 2** (mai multe `id_pol` cu acelasi `SCC`) ar cere o regula de departajare
(cea mai recenta? prima gasita? o marcare explicita "politica implicita pentru SCC X"?) — neverificat
pe date reale, vezi sectiunea "Ramas de verificat".
- Aceasta varianta **schimba `id_pol`-ul scris pe linie** (azi `NULL`/gol pentru articol fara politica,
ar deveni id-ul politicii tehnice) — orice raport/ecran care azi presupune "`id_pol` gol = articol fara
politica" (de ex. eventuale filtre in rapoarte de vanzari) ar vedea un `id_pol` populat; nu am gasit
cod care sa faca aceasta presupunere explicit, dar nici n-am cautat-o exhaustiv (in afara bugetului).
**Concluzie e)**: nu e nevoie de "niciuna dintre cai" pentru un "nu" — exista o cale VFP verificabila si
construita din piese deja functionale in productie (RPC de inserare in politica de pret, plus o
interogare de cautare inversa noua dar directa). E **fezabila**, probabil **mai completa** decat plan B
(rezolva si `CU_TVA`/`IN_VALUTA`, nu doar `SCC`), dar cere: (i) o interogare noua de cautare `id_pol` pe
`SCC`, (ii) reutilizarea unui RPC existent pentru inserarea articolului in politica, (iii) potential
configurare manuala o singura data (nota noua) pentru `SCC`-urile fara precedent, (iv) o decizie explicita
despre cum se exclude/trateaza politica tehnica in ecranele de cautare vizibile utilizatorului.
## Ramas de verificat pe baza de date vie
- **Structura completa DDL a `CORESP_CONT_VENCHELT`** (`DESC coresp_cont_venchelt` sau echivalent):
confirmarea coloanelor `ID_CCV` (probabil PK surogat) si `DATAORAS` (probabil timestamp tehnic),
tipurile exacte, nullability, si daca exista o constrangere `UNIQUE`/index pe `CONT` (relevant pentru
ambiguitatea discutata in sectiunea 2 si 4) — DDL-ul nu e in arhiva `SCRIPTURI_CLAR` (tabel pre-2009).
Interogare de verificat: `SELECT column_name, data_type, nullable FROM user_tab_columns WHERE
table_name = 'CORESP_CONT_VENCHELT' ORDER BY column_id;` si
`SELECT index_name, uniqueness FROM user_indexes WHERE table_name = 'CORESP_CONT_VENCHELT';`.
- **Continutul complet al `CONT_VENIT`** — migrarea din 2023 acopera 20 de conturi de gestiune; ramane de
verificat daca acopera **toate** conturile de gestiune folosite azi in `NOM_ARTICOLE.CONT`/`STOC.CONT`
in productie (altfel fallback-ul ar produce `CONT_VENIT = NULL` pentru conturi de gestiune
neacoperite). Interogare: `SELECT DISTINCT cont FROM (SELECT cont FROM nom_articole UNION SELECT cont
FROM stoc) WHERE cont NOT IN (SELECT cont FROM coresp_cont_venchelt WHERE sters = 0);`.
- **Daca exista mai mult de un rand activ (`STERS = 0`) pentru acelasi `CONT`** — cod critic pentru
join-uri fara dezambiguizare. Interogare: `SELECT cont, COUNT(*) FROM coresp_cont_venchelt WHERE
sters = 0 GROUP BY cont HAVING COUNT(*) > 1;`.
- **Valorile reale din `NOM_ARTICOLE.CONT` pentru articole negestionabile** — cate au deja un cont
6xx/7xx explicit vs. cate au un cont 3xx "gresit" (articol marcat negestionabil dar cu cont de gestiune
ramas din import) vs. cate au `NULL`/gol, ceea ce ar decide cat de des s-ar folosi de fapt fallback-ul
pe `704`. Interogare: `SELECT SUBSTR(cont,1,1) AS clasa, COUNT(*) FROM nom_articole WHERE gestionabil =
0 GROUP BY SUBSTR(cont,1,1);` (numele exact al coloanei `gestionabil` de verificat pe schema).
Verificat si numele exact al coloanei `NOM_ARTICOLE.CONT` — vezi si `cont_venit_articol_fara_politica.md`.
- **Cine citeste efectiv proprietatea `cconte` din `frm_configurare_efactura`** — nu am gasit consumatorul
in codul static cercetat (`\.cconte\b`, zero rezultate in `COMUN`); posibil legata generic la o
configuratie pe nume de camp, tipar comun in suita, dar neconfirmat.
- **Comportamentul `scrie_nota`/`ACT_TEMP` la `SCD`/`SCC` NULL sau la `CU_TVA`/`IN_VALUTA` NULL** — codul
PL/SQL nu are validare (confirmat in `nota_contabila_fara_politica.md`, sectiunea 2), dar constrangerile
reale de tabel (`ACT`/`ACT_TEMP`, `NOT NULL`?) nu au fost verificate — ar decide daca o implementare pe
jumatate facuta a fallback-ului ar da eroare Oracle explicita (`ORA-01400`) sau ar trece silentios.