# Fluxuri de calcul și facturare pe tipuri de prestații Acest document descrie **lanțul complet de facturare end-to-end** pentru fiecare tip de prestație: **Import → Calcul → Salvare → Listare**. Scopul este să nu mai fie nevoie de fiecare dată să se caute prin cod *cum* se ajunge de la datele sursă la valoarea facturată. Documente complementare (a NU se duplica): - `docs/facturare.md` — fluxul general de salvare a facturii (form `frm_factura` → `factura_salvare_db` → `PACK_FACTURARE.pck`), structura `crsFactura`, formulele de TVA. - `docs/cautare_vcx_vct.md` — cum se caută cod în binarele `.vcx`/`.scx`. Constante tipuri prestații: `Include/roaacnpro.h` (string `TIP_*`, numeric `NTIP_*`). Codul de calcul al claselor de formular stă în binarul `Clase/oacnpro.vcx`; referințele de mai jos folosesc cache-ul TEXT `_textcache/clase/oacnpro.vc2` (vezi `docs/cautare_vcx_vct.md`). > Notație: `fișier:linie`. Pentru clase, `oacnpro.vc2:NNNN` = `_textcache/clase/oacnpro.vc2`. --- ## 0. Convenții de rotunjire Rotunjirile folosesc constante globale de precizie, promovate din `RoaApp` (framework COMUN): | Constantă | Rol | Valoare uzuală | |-----------|-----|----------------| | `gnPC` | precizie valori în monedă națională (lei) | 2 | | `gnPVal` | precizie valori în valută | 2 | | `gnZ` | precizie penalități (testat în cod: 2 — `proceduri_acnpro.prg:1542`) | 2 | | `gnPCant` | precizie cantitate | (config) | | `gnPPret` / `gnPPretV` | precizie preț unitar (lei / valută) | (config) | | `gnPcurs` | precizie curs valutar | (config) | Aceste constante sunt setate la inițializarea aplicației din configurația firmei (COMUN); nu sunt hard-codate în acest repo. Acolo unde formula folosește un număr fix de zecimale (ex. `Round(..., 2)`, `Round(..., 3)`, `Round(..., 4)`) acela este intenționat și independent de configurație. --- ## 1. IMPORT DATE (sursă RORIS) ### 1.1. Sinonime Oracle către schema `roris` Datele sursă sunt tabele din sistemul extern **RORIS**, expuse aplicației prin **sinonime** create în `database.txt`. Toate `ips_*` sunt sinonime spre `roris.ips_*`: - `ips_voyages`, `ips_voyage_members`, `ips_voyage_locks` — voiaje, membrii convoiului, ecluzări (`database.txt:2-3,11`). - `ips_berthings` / `ips_vberthings` — staționări la dană (cheiaj) (`database.txt:15,17`). - `ips_cargoes`, `ips_goods`, `ips_goods_groups` — mărfuri și grupe de marfă (`database.txt:7-9`). - `ips_routes` / `ips_vroutes`, `ips_route_points` — rute și puncte de rută (distanțe) (`database.txt:5,16,24`). - `ips_vessels`, `ips_vessel_types`, `ips_vas_echivalent`, `ips_grup_tip_nave` — nave, tipuri, echivalențe UM, grupe de tip navă (`database.txt:4,10,13,14`). - `ips_vas_um` — unități de măsură ale tarifelor (`database.txt:12`). - Vederile `ips_v*` (ex. `ips_vberthings`, `ips_vvoyages`, `ips_vroute_points`) sunt versiuni pre-join ale acelorași date. Tabelele proprii ale aplicației (NU sinonime): `ips_voyages_vanzari`, `ips_voyage_members_vanzari`, `ips_regdoc`, `vanzari`, `factura`, `penalitati`, `contracte`, `ips_vtarife_tranzit`, tarifele de cheiaj/contract. ### 1.2. Importul propriu-zis Importul rulează ca **executabil separat** `importroris.exe` (proiect `importroris.pjx`), NU din aplicația principală: - Punct de intrare: `Programe/importroris_main.prg` — rulează silentios (`importroris s nnn`, unde `nnn` = nr. zile) sau cu formular (`Do Form importroris`) (`importroris_main.prg:32-37`). - Importul efectiv este delegat unui **pachet Oracle**: `pack_acn_import.import_roris_job(?ptLastGenerationTime)` (`importroris_proceduri.prg:33`). Toată extragerea/normalizarea din `roris.*` în tabelele locale `ips_*` se face în pachetul PL/SQL, nu în VFP. ### 1.3. Câmpuri sursă relevante pentru calcul La calcul, datele importate sunt citite din vederi de calcul: - **Tranzit**: vederea `ips_vvoyage_members_calcul` (citită în `calcul_tranzit`, `proceduri_acnpro.prg:835`). Câmpuri-cheie: `distanta`, `distanta_cdmn` (km pe canal), `tcap` (capacitate/tone), `trn` (NRT), `lbd`, `hp`, `cuplat`, `periculos`, `procdiv`, `gds_id`/`gdsg_id` (marfă/grupă marfă), `vtp_id`/`vgrp_id` (tip/grupă navă), `rte_id` (rută), `cargo` (cantitate marfă). Schema cursorului `crsVoyages` în `proceduri_acnpro.prg:838-844`. - **Cheiaj**: vederea `ips_vberthings` (citită în `calcul_cheiaj`, `proceduri_acnpro.prg:1023`). Câmpuri: `datai`/`datap` (intrare/plecare → durată), `cap`, `trn`, `lbd`, `hp`, `lung`, `tipn_id`, `dana_id`/`rpt_id` (dana). Schema `crsBerthings` în `proceduri_acnpro.prg:1028`. - **Ecluzări** (tranzit): `ips_vvoyage_locks` (defalcate pe membru și cameră de ecluzare), citite la distribuția pe ecluze în `completeaza_regdoc_tranzit` (`proceduri_acnpro.prg:2325`). --- ## 2. CALCUL TARIFE ȘI VALORI Calculul se face în clasele de formular din `Clase/oacnpro.vcx`. Lanțul pentru tranzit/cheiaj este: `frm_tranzit.do_executa` → `calcul_tranzit`/`calcul_cheiaj` (umplu cursoarele din RORIS) → formularul de calcul (`frm_calcul_tranzit` / `frm_calcul_cheiaj`) → metoda de calcul tarife. ### 2.1. TRANZIT - **Pregătire date**: `calcul_tranzit(tcVyeIds)` — `proceduri_acnpro.prg:740`. Creează `crsVoyage` (convoaie) și `crsVoyages` (membrii, cu indicatorii din `ips_vvoyage_members_calcul`). - **Calcul valoare**: clasa `frm_calcul_tranzit`, metoda **`do_executa`** — `oacnpro.vc2:3651-3918`. **De unde vin tariful și cantitatea:** - Tarifele: `IPS_VTARIFE_TRANZIT` (join cu `IPS_VVAS_UM`), selectate de DOUĂ ori: - tarife pe **contract** (`ctr_id = lnClientCtrId`) — `oacnpro.vc2:3688-3694` (cursor `tTarife`); - tarife **generale/fără contract** (`ctr_id = get_ctr_id_fara_contract()`) — `oacnpro.vc2:3698-3703` (cursor `tTarife2`). - Selecția tarifului se face pe tip UM (`TC`/`TRN`/`LBD`/`HP`), încadrarea pe capacitate (`TCAP1/TCAP2` la capacitatea totală a convoiului, `TCCAP1/TCCAP2` la capacitatea cumulată a clientului), rută (`RTE_ID`) și grupă/tip marfă (`GDSG_ID`/`GDS_ID`) — `oacnpro.vc2:3750-3812`. Ordinea de căutare: contract+marfă → general+marfă → contract+grupă → general+grupă → "orice marfă" (`GDSG_ID=0`). - Cantitatea (`cant`) depinde de UM: `TC`→`tcap`, `TRN`→`trn`, `LBD`→`lbd`, `HP`→`hp` (`oacnpro.vc2:3768-3795`). **Coeficienți** (`oacnpro.vc2:3717-3727, 3861-3865`): - `corect` (coeficient corector): pornește de la 100, +`gnACNPFmarfa` (fără marfă), +`gnIpsPCuplat` (necuplat), +`gnIpsPPericulos` (marfă periculoasă); apoi normalizat: `corect = Round(lnCoeficient/100, 3)` (`oacnpro.vc2:3861`). - `procdiv` — coeficient suplimentar adus din sursă (`Nvl(procdiv,0)`). - `procdist` (coeficient distanță): **km din 64.4** (lungimea canalului), plafonat la 1: ``` Replace procdist With MIN(Round(Abs(distanta) / 64.4, 2), 1.00) && oacnpro.vc2:3864 ``` **Formula valorii pe navă** (`oacnpro.vc2:3867`): ``` valval = Round(tarif * cant * procdist * (corect + procdiv), 2) ``` **Valoare minimă / valoare finală manuală** (`oacnpro.vc2:3877-3910`): - Suma pe convoi: `lnValoareConvoi = SUM(valval) FOR ales=1`. - Dacă `lnValoareConvoi <= valoare_minima_convoi` (`gnIpsValoareMinConvoi`) și > 0 și nu s-a scris manual, valoarea finală devine valoarea minimă (`oacnpro.vc2:3883-3885`). - Dacă utilizatorul a scris o **valoare finală** diferită, valorile se distribuie proporțional: ``` Replace All VALVAL With Round(lnValoareFinala * VALVAL / lnValoareConvoi, gnPC) For ales = 1 && oacnpro.vc2:3891 ``` Diferența de rotunjire dintre suma recalculată și valoarea finală se pune pe primul rând (`oacnpro.vc2:3903-3907`). La stornare (val. finală < 0) se recalculează cantitatea (`oacnpro.vc2:3895`); tariful NU se recalculează (apare tariful de încadrare pe recapitulație). **Rotunjiri tranzit:** `procdist`→2 zecimale, `corect`→3 zecimale, `valval`→2 zecimale fix; distribuția valorii finale→`gnPC`. ### 2.2. CHEIAJ - **Pregătire date**: `calcul_cheiaj(tcVyeIds)` — `proceduri_acnpro.prg:963`. Creează `crsVoyage`, `crsBerthings` (staționări din `ips_vberthings`) și `crsBerthingDetails` (prestații de facturat). - **Calcul valoare**: clasa `frm_calcul_cheiaj`, metoda **`do_calcul_tarife`** — `oacnpro.vc2:1191-1375`. **Durata** (oră → UM tarif): `lnDurata1 = Round((datap - datai)/3600, 3)` ore (`oacnpro.vc2:1228`), apoi împărțită la `corect` pentru transformare în UM tarifului (ore sau zile) și, dacă tariful are flag `plus`, rotunjită în sus cu `Ceiling()` (`oacnpro.vc2:1289-1292`). **Tariful**: `crsTarifeDiverseContract` construit de `make_tarife_cheiaj_contract` cu tarifele de contract și cele generale (`oacnpro.vc2:1215`). Încadrarea pe interval de durată: `lim1*corect < durata AND lim2*corect >= durata`, după `ctr_id`, `id_articol`, `intern` (`oacnpro.vc2:1247-1248`). **Cantitatea** după UM tarif (`oacnpro.vc2:1298-1313`): `ZI`→1 (ex. cheiaj iernat), `TC`→`cap`, `TRN`→`trn`, `ML`→`lung`, `CP`/`HP`→`hp`, `LBP`/`LBD`→`lbd`. Dacă UM conține `ZI`, valoarea se înmulțește și cu numărul de zile (`llZile`). **Formula valorii** (`oacnpro.vc2:1320-1345`): - Caz simplu (fără bază): `valval = tarif * cant * IIF(llZile, durata, 1)` (`oacnpro.vc2:1342`). - Caz cu **tarif pe tranșe** (`arebaza=1`): se cumulează tarifele tranșelor inferioare (`baza=1`) plus tranșa curentă pentru durata rămasă (`oacnpro.vc2:1321-1343`). - `valval = Round(valval, gnPVal)` (`oacnpro.vc2:1345`). **Valoare finală manuală** (`oacnpro.vc2:1356-1367`): distribuție proporțională `Round(lnValoareFinala * valval / lnValoareConvoi, gnPVal)`, recalcul cantitate `Round(valval/tarif, 2)`, diferența pe primul rând. ### 2.3. CHIRII (și contracte similare) - **Calcul valoare**: clasa `frm_calcul_contract` — `oacnpro.vc2:1615`. Lucrează cu `crsArticoleContract` (linii introduse pe baza articolelor de contract), NU pe date RORIS. - Metode: `calculeazapreturi` (`oacnpro.vc2:2240`) → `calculeazapreturirand` (`oacnpro.vc2:2248`) → `calculeazatotaluri` (`oacnpro.vc2:2267`). **Formula** (`calculeazapreturirand`, `oacnpro.vc2:2256-2262`): - la modificarea cantității/prețului: `valval = Round(pret * cantitate, gnPC)`; - la modificarea valorii (recalc preț unitar): `pret = Round(valval/cantitate, 4)`. Tariful (`pret`) provine din articolul de contract (introdus de utilizator / tarif contract); nu există coeficient de distanță sau corecție. ### 2.4. PENALITĂȚI - **Calcul valoare**: clasa `frm_penalitati` — `oacnpro.vc2:8366`. Metoda `do_executa` (`oacnpro.vc2:9671`) apelează `calcul_penalitati2` (`proceduri_acnpro.prg:1286`); varianta cu vânzări reale este `calcul_penalitati` (`proceduri_acnpro.prg:1166`). - Recalcul pe o linie editată: `calcul_penalitati_linie` (`proceduri_acnpro.prg:1455`). **De unde vin datele**: facturi/încasări din `vanzari`, `vact`, `ireg_parteneri`; cotele de penalitate din `contracte` (`coef_penalitati`→`ppenzi`, `coef_penalitati2`→`ppenzi2`, `zile_penalitati`→`penzile`, `zile_gratie_penalitati`→`zilegratie`) (`proceduri_acnpro.prg:1183,1225`). Cursorul de lucru: `crsCalculPenalitati`. **Zile de întârziere** (`proceduri_acnpro.prg:1257-1265`): - `databaza` = `Max(datascad, sfârșit lună precedentă)` sau `datascad` (după modul de calcul); - `dataref` = `Min(dataref, pdDataReferinta)`; - `nr_zile = IIF(dataref - databaza <= zilegratie, 0, dataref - databaza)` (zile de grație). **Formula penalitate, o singură cotă** (`proceduri_acnpro.prg:1266`): ``` penalitati = Round(Round(ppenzi/100*suma, gnZ) * nr_zile, gnZ) ``` **Formula penalitate, două cote** (`CalculeazaPenalitati`, `proceduri_acnpro.prg:1510-1539`): - se împart zilele restante între cota 1 (până la `penzile2-penzile`) și cota 2 (peste); vezi `lnZileProcent1`/`lnZileProcent2` (`proceduri_acnpro.prg:1531-1534`). - ``` penalitati = Round(Round(ppenzi /100*suma, gnZ)*lnZileProcent1, gnZ) + Round(Round(ppenzi2/100*suma, gnZ)*lnZileProcent2, gnZ) && proceduri_acnpro.prg:1536 ``` - Aplicată în `calcul_penalitati` doar pentru contracte cu `ppenzi2 <> 0` (`proceduri_acnpro.prg:1271-1277`). **Rotunjiri**: toate la `gnZ` (dublă rotunjire: întâi penalitatea/zi, apoi totalul). Funcția de test cu valori așteptate: `TestCalculeazaPenalitati` (`proceduri_acnpro.prg:1541`). ### 2.5. APA / PILOTAJ / ALTE / DIVERSE Aceste tipuri **nu au un motor de calcul propriu** bazat pe RORIS. Sunt facturate prin lanțul generic de contract/articol: - Calculul valorii este `pret * cantitate` (vezi 2.3 CHIRII, `frm_calcul_contract` / `calculeazapreturirand`, `oacnpro.vc2:2256-2262`), respectiv introducere directă pe linia de factură în `frm_factura` (`crsFactura`, vezi `docs/facturare.md`). - Tariful provine din articol/contract (`ips_vprestatii_locatii`, `contracte`), nu există coeficienți de distanță/corecție. - Constantele de tip: `NTIP_APA 2`, `NTIP_PILOTAJ 4`, `NTIP_ALTE 5`, `NTIP_DIVERSE 9` (`Include/roaacnpro.h`). - Salvarea acestor linii se face în `ips_regdoc` prin `salvare_contract` (`proceduri_acnpro.prg:1918`). --- ## 3. SALVARE FACTURĂ Fluxul general (cap factură, articole, vânzări, contabilitate) este în `docs/facturare.md`; sursa de adevăr pentru valori/TVA este **`PACK_FACTURARE.pck`**. Aici se documentează doar specificul fiecărui tip. ### 3.1. Lanțul comun - `factura_acn(tcTip, loDate)` — `proceduri_acnpro.prg:3064` — orchestrarea facturii. - `factura_salvare_db(tcTip)` — `proceduri_acnpro.prg:3290` — persistarea: - `pack_facturare.initializeaza_date_factura(...)` (cap factură), `adauga_articol_factura_deviz` (articole), `scrie_in_vanzari` (`proceduri_acnpro.prg:3343,3382,3412`); - reg. doc.: `pack_acn.salveaza_regdoc(...)` pentru fiecare rând din `cRegDoc` (`proceduri_acnpro.prg:3430`); - pentru TRANZIT/CHEIAJ: actualizează `ips_voyages_vanzari.vz_id` cu id-ul vânzării (`proceduri_acnpro.prg:3448-3451`); - pentru PENALITĂȚI: inserează în tabela `penalitati` (`proceduri_acnpro.prg:3475-3488`). ### 3.2. TRANZIT — specific - `salvare_tranzit` — `proceduri_acnpro.prg:1779`: - inserează capul de convoi facturat în **`IPS_VOYAGES_VANZARI`** (`...:1788-1791`); - inserează fiecare navă în **`IPS_VOYAGE_MEMBERS_VANZARI`** cu toate valorile calculate (`procdist`, `corect`, `tarif`, `cant`, `valval`, `distanta`, `distanta_cdmn`, etc.) (`...:1794-1849`). Tranzacție manuală cu COMMIT/ROLLBACK. - `completeaza_regdoc_tranzit(tcVVIds)` — `proceduri_acnpro.prg:2259` — **distribuția pe canale și ecluze**: - procentul CDMN al fiecărei nave: `Round(DISTANTA_CDMN / DISTANTA, 4)` (`...:2332`); - valori CDMN = `valval * procentCDMN`, PAMN = restul (`...:2355-2364`); - valori pe **senale navigabile** = valoarea CDMN/PAMN × procentul de senal (`100 - procente_ecluze`)/100 (`...:2369-2385`); - procentele pe ecluze (`AGIGEA`/`CERNAVODA`/`OVIDIU`/`NAVODARI`) din `ips_vvoyage_locks` și constantele `gnIPSTranzitProcAG/CV/OV/NV` (`...:2289-2324`). ### 3.3. CHEIAJ — specific Salvarea se face tot prin `IPS_VOYAGES_VANZARI` / `IPS_VOYAGE_MEMBERS_VANZARI` (același mecanism ca tranzit, cu `poVoyage.tip = NTIP_CHEIAJ`) și prin lanțul comun din 3.1 (`vz_id` actualizat la `factura_salvare_db`, `proceduri_acnpro.prg:3448`). `make_factura_cheiaj` (`proceduri_acnpro.prg:3615`) construiește linia de articol în `crsFactura`. ### 3.4. CHIRII / APA / PILOTAJ / ALTE / DIVERSE — specific - `salvare_contract` — `proceduri_acnpro.prg:1918` — inserează în **`ips_regdoc`** (tip, articol, locație, valută, client, contract, preț, cantitate, valval, TVA, perioadă) (`...:1924-1953`). ### 3.5. PENALITĂȚI — specific - `salvare_penalitati` — `proceduri_acnpro.prg:2008` — marchează înregistrările alese/șterse; scrierea reală în tabela `penalitati` se face în `factura_salvare_db` (`proceduri_acnpro.prg:3475-3488`). - `make_factura_penalitati` — `proceduri_acnpro.prg:3775` — construiește linia de factură. --- ## 4. LISTARE FACTURĂ + RECAPITULAȚIE Rapoartele citesc, în general, **valorile deja calculate** din tabelele de vânzări; recapitulațiile recalculează doar agregări/distribuiri pentru afișare, nu tarife. ### 4.1. Construirea liniilor de factură (`crsFactura`) - `make_factura_tranzit` — `proceduri_acnpro.prg:3517`: citește valorile însumate din **`ips_voyage_members_vanzari`** (`SUM(valval)` pe valută, `proceduri_acnpro.prg:3561-3567`), numele convoiului din `ips_vvoyages` și completează `crsFactura` (un singur rând, cantitate 1, preț = valval în monedă națională). NU recalculează tarife. - `make_factura_cheiaj` — `proceduri_acnpro.prg:3615`: analog, pentru cheiaj. - `make_factura_penalitati` — `proceduri_acnpro.prg:3775`. ### 4.2. Recapitulații (rapoarte `Rapoarte/*.frx`) Procedurile de generare a cursoarelor de raport: `Programe/proceduri_acnpro_rapoarte.prg` și proceduri din `proceduri_acnpro.prg`: - **Recapitulație tranzit**: `listeaza_recapitulatie_tranzit` — `proceduri_acnpro.prg:5504`. Citește **`ips_vvoyage_members_vanzari`** `WHERE vz_id = ?pnIdVanzare` (`...:5515-5584`) — toate valorile (`tarif`, `cant`, `procdist`, `corect`, `valval`, `valctva`...) sunt deja calculate și salvate; raportul le afișează. Capacitatea totală a convoiului se recalculează cu fereastră `LAST_VALUE(...) OVER(PARTITION BY vms_id)` (`...:5599-5613`). Modul de calcul afișat (textul formulei) se generează cu `mod_calcul_tranzit` (`proceduri_acnpro.prg:5728`): `TARIF * CANT * %DIST * (%COR + %SUPL) = TOTAL` (`...:5736,5746`). - **Recapitulație cheiaj**: `listeaza_recapitulatie_cheiaj` — `proceduri_acnpro.prg:5368`. - **Recapitulație penalități**: `listeaza_recapitulatie_penalitati` — `proceduri_acnpro.prg:5777`. Din factură citește din vederea `vpenalitati` `WHERE id_fact_pen = ?pnIdFact` (`...:5809-5836`); din formularul de calcul (evaluare) folosește cursorul `crsRecapitulatie` deja calculat. Valorile penalităților NU se recalculează la listare. - Rapoarte `.frx` asociate: `Rapoarte/factura*.frx`, `recapitulatie_tranzit`, `recapitulatie_cheiaj`, `recapitulatie_penalitati`, `registru tranzit`. **Regula generală**: la listare se citesc valori **deja calculate** (din `ips_voyage_members_vanzari`, `vpenalitati`, `ips_regdoc`/`vanzari`). Singurele recalculări la listare sunt: textul „mod de calcul" (`mod_calcul_tranzit`) și agregările/încadrările de capacitate pe recapitulația de tranzit. --- ## 5. Capcane cunoscute 1. **Coeficientul de distanță rotunjit la 2 zecimale poate deveni 0.** `procdist = MIN(Round(Abs(distanta)/64.4, 2), 1.00)` (`oacnpro.vc2:3864`). Pentru distanțe sub ~0.32 km (0.005 × 64.4) `Round(.../64.4, 2)` dă 0 → `valval = tarif*cant*0*(...) = 0`. Câmpul `PROCDIST` este `N(6,3)` în schema cursorului (`proceduri_acnpro.prg:843`) și în `ips_voyage_members_vanzari`, deci ar putea stoca 3 zecimale, dar **calculul rotunjește la 2**, nu la 3 — distanțele foarte mici se facturează cu valoare 0. 2. **Împărțire la 0 la valoarea finală.** La distribuirea valorii finale introduse manual: `Round(lnValoareFinala * VALVAL / lnValoareConvoi, gnPC)` (`oacnpro.vc2:3891`, tranzit; analog `oacnpro.vc2:1360`, cheiaj). Dacă valoarea convoiului calculat (`lnValoareConvoi`) este 0 (de ex. toate `valval=0` din cauza capcanei 1, sau tarife 0), împărțirea dă eroare/`NaN`. Codul protejează parțial (condiția `lnValoareConvoi > 0` la valoarea minimă, `oacnpro.vc2:3883`), dar ramura cu valoare finală scrisă manual (`oacnpro.vc2:3887`) nu verifică `lnValoareConvoi <> 0` înainte de împărțire. 3. **Tariful NU se recalculează la valoarea finală (tranzit).** Intenționat: pe recapitulație apare tariful de încadrare, nu cel rezultat din valoarea finală (`oacnpro.vc2:3896-3899`). Diferența de rotunjire ajunge pe primul rând (`oacnpro.vc2:3906`), deci `valval` al primului rând poate diferi de `tarif*cant*procdist*(corect+procdiv)`. 4. **Penalități — dublă rotunjire.** Formula rotunjește întâi penalitatea/zi și apoi totalul (`Round(Round(ppenzi/100*suma, gnZ)*nr_zile, gnZ)`, `proceduri_acnpro.prg:1266`). Pentru cele două cote, fiecare termen este rotunjit separat și apoi însumat (`proceduri_acnpro.prg:1536`), deci totalul poate să nu fie egal cu `Round(total_brut, gnZ)`. 5. **`calcul_penalitati2` folosește `ireg_parteneri` în loc de `vanzari`** (temporar, până la importul vânzărilor) — comentariu în cod (`proceduri_acnpro.prg:1284-1286`, `oacnpro.vc2:9678`). Rezultatele pot diferi de `calcul_penalitati` până la stabilizare. 6. **Distribuția CDMN depinde de `DISTANTA <> 0`.** `Round(DISTANTA_CDMN/DISTANTA, 4)` (`proceduri_acnpro.prg:2332`) — dacă `DISTANTA` este 0, distribuția pe canale/ecluze eșuează.