Initial: flux text FoxBin2Prg (git urmareste .??2 in-arbore, binarele VFP git-ignored)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-16 11:43:11 +03:00
commit 430d2c6000
72 changed files with 44452 additions and 0 deletions

122
docs/cautare_vcx_vct.md Normal file
View File

@@ -0,0 +1,122 @@
# Căutare în codul din librăriile/formele VFP (`.vcx`/`.vct`, `.scx`/`.sct`, ...)
Cum se caută cod-sursă (proceduri, metode, parametri) care **nu** stă în fișiere `.prg`, ci
în interiorul fișierelor binare VFP: librării de clase (`.vcx`+`.vct`), forme (`.scx`+`.sct`),
rapoarte (`.frx`+`.frt`), meniuri (`.mnx`+`.mnt`), baze de date (`.dbc`+`.dct`).
## De ce e nevoie de asta
Un `.vcx` este de fapt o **tabelă DBF**: codul metodelor stă în câmpuri memo în `.vct`. Un
`grep` direct pe `.vct` *găsește* textul, dar îl vede ca fișier binar (octeți `\0` între linii),
fără numere de linie utile și greu de citit. Soluția: convertim binarul în reprezentarea lui
**TEXT** (`.vc2`, `.sc2`, …) — un PRG-style lizibil, cu metode sortate alfabetic — și căutăm
în text cu `grep`/`Grep`.
> **Regula de aur:** textul generat e **doar pentru citire/căutare**. Modificările de cod se fac
> tot în **IDE-ul VFP** (vezi `CLAUDE.md`). Nu edita `.vcx/.vct` pe text și nu pune textul înapoi
> în binar pentru aceste scopuri.
## Unelte disponibile (verificate)
| Unealtă | Cale | Rol | Recomandare |
|---|---|---|---|
| **FoxBin2Prg** | `D:\ROA\UTIL\foxbin2prg\FoxBin2Prg.EXE` | Binar VFP ⇄ text bidirecțional (`.vc2/.sc2/.fr2/.mn2/.dc2/.lb2`) | **De folosit.** Complet, fidel, include corpul metodelor, parametri, comentarii |
| vcx2prg | `D:\ROA\UTIL\vcx2prg\x2prg.prg` | Doar `.vcx``.prg` *compilabil* | Alternativă; mai vechi, doar clase, necesită IDE VFP deschis pentru a rula forma |
| prgtovcx | `D:\ROA\UTIL\prgtovcx\prgtovcx.PRG` | Invers: `.prg``.vcx` | **Nu** pentru căutare (e direcția opusă) |
FoxBin2Prg e prima alegere: rulează din linia de comandă fără IDE deschis (EXE compilat), acoperă
toate tipurile de container, și produce text mult mai fidel decât vcx2prg.
> Dacă `FoxBin2Prg.EXE` lipsește, se compilează o singură dată: deschide
> `D:\ROA\UTIL\foxbin2prg\foxbin2prg.pj2` în VFP 9 și *Build > Build Executable*. Lângă EXE trebuie
> să existe `filename_caps.exe` și fișierele `props*.txt` (sunt deja în folder).
## Mod recomandat: scriptul `vcx2txt.ps1`
`D:\ROA\UTIL\foxbin2prg\vcx2txt.ps1` automatizează tot: copiază binarul + memo-ul într-un
**cache** care oglindește structura proiectului (ca să nu murdărească arborele SVN), rulează
FoxBin2Prg, păstrează doar fișierul text. E **incremental** (sare peste ce e deja la zi).
**Implicit e „project mode"**: citește `roaacnpro.PJX` și convertește **doar** fișierele
`.vcx`/`.scx` pe care le folosește efectiv proiectul (~78), **nu** tot `COMUN\` (care are zeci de
librării nefolosite în ROAACNPRO — pierdere de timp). Lista de fișiere vine direct din proiect, deci
e exactă.
```powershell
# DOAR fișierele proiectului (recomandat, rapid) -> cache text
powershell -ExecutionPolicy Bypass -File D:\ROA\UTIL\foxbin2prg\vcx2txt.ps1
# include și rapoartele (.frx) și meniurile (.mnx)
powershell -ExecutionPolicy Bypass -File D:\ROA\UTIL\foxbin2prg\vcx2txt.ps1 -Types vcx,scx,frx,mnx
# conversie punctuală a unui fișier sau folder (chiar dacă NU e în proiect)
powershell -ExecutionPolicy Bypass -File D:\ROA\UTIL\foxbin2prg\vcx2txt.ps1 -Source D:\ROA\ROAACNPRO\COMUN\clase\caut.vcx
# rebuild curat (șterge cache-ul întâi) / forțează reconversia
powershell -ExecutionPolicy Bypass -File D:\ROA\UTIL\foxbin2prg\vcx2txt.ps1 -Clean
powershell -ExecutionPolicy Bypass -File D:\ROA\UTIL\foxbin2prg\vcx2txt.ps1 -Force
```
Parametri utili: `-Project <alt.pjx>` (alt proiect, ex. `importroris.pjx`), `-Types`,
`-Source`, `-Clean`, `-Force`.
Rezultatul ajunge în `D:\ROA\UTIL\foxbin2prg\_textcache\` cu aceeași structură de directoare ca
proiectul (ex.: `_textcache\Clase\oacnpro.vc2`, `_textcache\comun\clase\_frm_base.vc2`). Apoi:
```bash
# cu unealta Grep (preferat) sau grep:
grep -rn "PROCEDURE do_calcul_tarife" "D:\ROA\UTIL\foxbin2prg\_textcache"
grep -rln "factura_salvare" "D:\ROA\UTIL\foxbin2prg\_textcache" # ce clase ating subiectul
```
Flux tipic pentru o problemă: rulează scriptul fără parametri o dată (populează cache-ul), apoi
caută cu `Grep` în `_textcache`. La sesiuni următoare rescrie doar ce s-a schimbat (incremental).
Dacă ai nevoie de o librărie din `COMUN\` care nu e în proiect, convertește-o punctual cu `-Source`.
## Alternativă manuală (fără script)
FoxBin2Prg scrie textul **lângă sursă**. Ca să nu murdărești `Clase/`, copiază perechea
binar+memo într-un folder temporar și convertește acolo:
```powershell
# .vcx -> .vc2 (cType gol = extensia decide direcția BIN2PRG)
Copy-Item D:\ROA\ROAACNPRO\Clase\oacnpro.vcx, D:\ROA\ROAACNPRO\Clase\oacnpro.vct D:\tmp\
& 'D:\ROA\UTIL\foxbin2prg\FoxBin2Prg.EXE' 'D:\tmp\oacnpro.vcx' '' '' '' '1' '0' '1'
# => D:\tmp\oacnpro.vc2
```
Pozițiile parametrilor folosite: `fișier, cType, cTextName, lGenText, cDontShowErrors=1,
cDebug=0, cDontShowProgress=1`. **Lasă `cType` gol** — extensia (`.vcx`) determină direcția.
(Nu da explicit `"BIN2PRG"` pentru un singur fișier: în acel mod EXE-ul așteaptă un *director*.)
Detalii complete de parametri: `D:\ROA\UTIL\foxbin2prg\docs\FoxBin2Prg_Run.md`.
## Soluție rapidă „murdară" (un singur fișier, fără conversie)
Pentru o verificare rapidă „există metoda X undeva în librărie?", `grep` merge direct pe `.vct`
(o vede ca binar, dar găsește potrivirile):
```bash
grep -c "PROCEDURE" "D:\ROA\ROAACNPRO\Clase\oacnpro.VCT"
grep -a -o "PROCEDURE [a-zA-Z0-9_]*" "D:\ROA\ROAACNPRO\Clase\oacnpro.VCT"
```
Bun pentru „da/nu" și nume de metode; pentru citit corpul/contextul, folosește conversia în text.
## Corespondență extensii binar → text
| Container | Binar (+memo) | Text |
|---|---|---|
| Librărie clase | `.vcx` + `.vct` | `.vc2` |
| Formă | `.scx` + `.sct` | `.sc2` |
| Raport | `.frx` + `.frt` | `.fr2` |
| Etichetă | `.lbx` + `.lbt` | `.lb2` |
| Meniu | `.mnx` + `.mnt` | `.mn2` |
| Bază de date | `.dbc` + `.dct` | `.dc2` |
## Note
- Codul aplicației stă mai ales în `Clase\*.vcx`; cea mai mare parte din `.vcx`-uri sunt în
`COMUN\` (framework partajat — vezi `CLAUDE.md`).
- `_textcache\` e regenerabil și e în afara arborelui SVN — poate fi șters oricând.
- Conversia **nu** necesită IDE-ul VFP deschis (folosește EXE-ul compilat).

79
docs/facturare.md Normal file
View File

@@ -0,0 +1,79 @@
# Fluxul facturilor (ROAACNPRO)
Document de referință minimal: ce fișiere trebuie verificate/modificate când se schimbă
ceva la facturare. Backend = Oracle (prin `goExecutor`/`goConn`), front-end = VFP9.
## Componentele cheie
| Rol | Fișier | Observații |
|-----|--------|-----------|
| Formularul de facturare | clasa `frm_factura` din `Clase/oacnpro.vcx` (cod în `.vct`) | **se editează în IDE VFP**, nu manual |
| Procedurile de facturare VFP | `Programe/proceduri_acnpro.prg` | `factura_salvare_db`, `make_cfactura`, `factura_acn` |
| Rapoarte facturare | `Programe/proceduri_acnpro_rapoarte.prg` + `Rapoarte/*.frx` | relistare/printare factură |
| Calcul + salvare valori în Oracle | `PACK_FACTURARE.pck` (pachet PL/SQL) | **sursa de adevăr** pentru valori |
| Framework facturare partajat | `COMUN/programe/ofacturare_comun.prg`, `oproceduri_facturare.prg` | partajat între aplicațiile ROA; a se evita modificarea |
## Lanțul de calcul (form → pachet → rapoarte)
1. **Introducere / import** (`frm_factura` + `factura_acn`): articolele intră în cursorul
`crsFactura` (creat în `make_cfactura`, `proceduri_acnpro.prg`). Import din
contract/tranzit/cheiaj prin `Insert Into crsFactura(...)` în `factura_acn`.
2. **Preview pe ecran** (`frm_factura.CalculeazaPreturiRand` + `CalculeazaTotaluri`):
calculează valorile fiecărui rând **doar pentru afișare**.
3. **Salvare** (`factura_salvare_db`): copiază rândurile din `crsFactura` în cursorul
`crsvanztemp`, apoi le trimite în Oracle prin `PACK_FACTURARE` (un apel per rând).
4. **Calcul autoritar** (`PACK_FACTURARE`): recalculează și stochează
`total_fara_tva / total_tva / total_cu_tva` în `vanzari` (antet) și `vanzari_detalii` (rânduri).
5. **Relistare / lista de facturi**: `proceduri_acnpro_rapoarte.prg` și interogările din
`proceduri_acnpro.prg` (~liniile 1180, 1222) **citesc** valorile deja calculate din Oracle
(`v.total_cu_tva` etc.) — **nu recalculează**.
> **Regula de sincronizare:** valorile reale sunt cele calculate de `PACK_FACTURARE` la salvare.
> Preview-ul din `frm_factura` trebuie să folosească **aceleași formule** ca pachetul, iar
> `factura_salvare_db` trebuie să trimită corect toți parametrii relevanți. Dacă cele două
> diverg, ecranul arată altceva decât factura salvată/relistată.
## Structura `crsFactura` (rândul de factură în memorie)
Definit în `make_cfactura` (`proceduri_acnpro.prg`). Câmpuri relevante:
- `pretval` = preț unitar în valută (prețul introdus de operator; baza calculului)
- `Curs` = curs valutar; `Pret` = preț unitar în lei = `pretval * Curs`
- `cantitate`
- `proc_tva` = multiplicatorul de TVA (ex. **1.19**, nu 19). La salvare e trimis ca `proc_tvav`.
- `pret_cu_tva` N(1) = flag: 1 = `pretval` este **cu TVA inclus**, 0 = fără TVA
- Lei: `valftva` (fără TVA), `valtva` (TVA), `valctva` (cu TVA)
- Valută: `valval` (fără TVA), `tvaval` (TVA), `totval` (cu TVA)
## Formule TVA
Cu `proc_tva` = multiplicator (1 + cotă/100):
- **Preț fără TVA** (`pret_cu_tva = 0`): `valftva = Pret*cant`; `valtva = valftva*(proc_tva-1)`;
`valctva = valftva + valtva`.
- **Preț cu TVA inclus** (`pret_cu_tva = 1`, calcul invers, identic cu `PACK_FACTURARE`,
`V_PRET_CU_TVA = 1`, PACK_FACTURARE.pck ~1570015922):
`valctva = Pret*cant`; `valtva = valctva*(proc_tva-1)/proc_tva`; `valftva = valctva - valtva`.
Rotunjiri: lei la `gnPC`, valută la `gnPval` (opțiuni firmă `PC` / `PPRETV` în pachet).
## Unde modific când...
- **Calculul valorilor pe rând (preview)** → `frm_factura.CalculeazaPreturiRand` (IDE VFP).
- **Ce se trimite la salvare în Oracle** → `factura_salvare_db` (insert `crsvanztemp` + apel pachet).
- **Calculul/stocarea finală a valorilor** → `PACK_FACTURARE.pck` (a se ține sincron cu preview-ul).
- **Coloane noi pe rândul de factură** → `make_cfactura` (cursor) + grid `grdFactura` (IDE VFP)
+ import în `factura_acn` + insert în `factura_salvare_db`.
- **Aspectul facturii tipărite / relistare** → `proceduri_acnpro_rapoarte.prg` + `Rapoarte/*.frx`.
- **Lista de facturi** → interogările din `proceduri_acnpro.prg` (citesc din `vanzari`).
## Exemplu real: suport „preț cu TVA inclus" (v2.0.4)
Lanțul complet care a trebuit atins pentru o singură funcționalitate:
1. `make_cfactura` — coloană nouă `pret_cu_tva N(1)` în `crsFactura`.
2. `frm_factura.CalculeazaPreturiRand` — branch pe `pret_cu_tva` (calcul invers).
3. `grdFactura` — coloană checkbox editabilă legată de `crsFactura.pret_cu_tva`.
4. `factura_salvare_db` — trimite `Nvl(pret_cu_tva,0)` (nu mai e hardcodat 0) către pachet.
Pachetul și rapoartele **aveau deja** suport pentru `pret_cu_tva`, deci nu au fost modificate —
exemplu tipic de „verifică întâi ce face deja pachetul/raportul".

View File

@@ -0,0 +1,359 @@
# 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ă.