Files
comun/docs/fluxuri_calcul_facturare.md
Marius Mutu 69c5290a8d Calcul tranzit: coeficient distanta rotunjit la 3 zecimale + protectie impartire la 0
Pentru distante scurte (ex. 0.2 km) coeficientul de distanta rotunjit la 2
zecimale iesea 0, deci valoarea facturata era 0. Rotunjire la 3 zecimale in
do_executa si in afisarea "mod de calcul". Protectie la impartirea cu 0 la
valoarea finala cand valoarea convoiului calculat este 0.

Adauga docs/fluxuri_calcul_facturare.md (lant facturare end-to-end pe tipuri)
si referinta in CLAUDE.md. Changelog 2.0.5.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 13:45:20 +03:00

360 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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ă.