# Import extrase de banca si imperecherea partener/factura - cum functioneaza azi Cercetare de cod, fara nicio modificare. Toate afirmatiile au citat fisier:linie din `D:\roa\ROACONT`. Sursa e VFP compilat in `.prg`/`.vcx`; codul citat mai jos e din `.prg` (text direct, nu are nevoie de `.vc2`). ## A. Harta fluxului 1. **Pornire**: `Programe\ocont2003.prg:845` (`lans_import_extras`) deschide setul de note `90023` = "IMPORT EXTRAS MT940" (`ocont2003.prg:858`, `ocont2003.prg:964-965`). 2. `note_fara_predefinire` (`ocont2003.prg:948`) pregateste cursorul `tAct` si apeleaza `import_extras('tAct', ...)` la `ocont2003.prg:1226`. 3. `import_extras` (`ocont2003.prg:866`) instantiaza clasa fabrica `ImportNote` din `Programe\oproceduri_import.prg:57` si cheama `.Import()`. In functie de fisierul ales, `ImportNote` (`oproceduri_import.prg:120-227`) alege subclasa potrivita: `ExtrasBanca_MT940` (Raiffeisen etc.), `ExtrasBanca_CREDITEUROPE_XML`, `ExtrasBanca_UNICREDIT_CSV`, sau `ExtrasBanca_General` (`oproceduri_import.prg:1415`) - clasa "detectez singur formatul", folosita si pentru fisierele **Banca Transilvania**. 4. `ExtrasBanca_General::Parse` (`oproceduri_import.prg:1417`) citeste prima linie nevida a fisierului si decide dupa continutul ei ce parser cheama. Pentru CSV sunt ~25 formate bancare/procesatori de plati recunoscute dupa antet (`oproceduri_import.prg:1452-1531`: Stripe, Booking, PayU, Intesa, FanCourier, Cargus, DPD, Garanti, OTP, Raiffeisen, Unicredit, ING, ING Business, **BT** (linia 1502-1507), PayPal, BRD, BCR, First Bank, CEC, MobilPay, Un-Doi, Paypoint, Idea, CreditEurope, Alpha). 5. Pentru **Banca Transilvania** liniile 1502-1507 detecteaza doua variante de export (lista de tranzactii lunara vs. extras de cont) si cheama `This.csvBT(fisier, 1)` respectiv `This.csvBT(fisier, 2)`. Parserul activ e `csvBT` la `oproceduri_import.prg:2923-3025` (functia veche `csvBT_original`, linia 3027, nu mai e apelata de nicaieri). 6. Rezultatul parsarii ajunge in cursorul local `c_iex`, apoi `C_IMPORT_TEMP` (`oproceduri_import.prg:1544-1550`), care alimenteaza motorul de imperechere `ExtrasBanca::CreeazaNote` (`oproceduri_import.prg:312-926`) - aici se cauta partenerul si factura pentru fiecare linie. 7. Rezultatul (`cActTemp` -> `tAct`) e afisat operatorului intr-un formular de corectie manuala, `frm_modific2024` (instantiat la `ocont2003.prg:1229`), unde utilizatorul poate modifica orice camp (partener, cont, factura) inainte de salvare. 8. La apasarea butonului de salvare (`buton = 1`, `ocont2003.prg:1245-1257`), cursorul corectat e scris in tabela Oracle **`ACTACTAN`** (jurnalul contabil) prin `oscrie_in_fisiere` (`ocont2003.prg:1256`). Coloanele relevante confirmate din `INSERT INTO actactan(...)` real (`Programe\oproceduri_incasari.prg:270-279`, `Programe\oproceduri_inchidere.prg:856-879`) si din vederea `vact_tot`/`xnote` (`COMUN\programe\ocont2003... ` / `Programe\ocont2003.prg:1101-1183`): `nr_nota, an, luna, dataireg, nract, dataact, explicatia, scd, ascd, scc, ascc, suma, suma_val, id_valuta, curs, id_set, id_fact, id_partd, id_partc, id_factd, id_factc, pereched, perechec, id_fdoc`. Liniile provenite din import extras au `id_set = 90023` (`ocont2003.prg:858`), ceea ce le face identificabile ulterior in Oracle. 9. Nomenclatorul de parteneri e tabela `NOM_PARTENERI` (cautat prin vederea `vnom_parteneri`, coloane `cod_fiscal`, `denumire`, `cont_banca`, `sters`, `inactiv`, `COMUN\programe\oproceduri_comune.prg:6599-6667`). Facturile/documentele de imperecheat sunt in tabela `IREG_PARTENERI` (fisa partener pe cont/an/luna, coloane `id_fact`, `cont`, `acont`, `nract`, `id_part`, `an`, `luna`, `precdeb`, `debit`, `preccred`, `credit`, `id_lucrare`, cautate in `GetDocumentByContPartenerAct`, `COMUN\programe\oproceduri_comune.prg:6984-7057`). Pe scurt: **(a) manual** = operatorul introduce direct in `frm_modific2024` fara pasii 3-6. **(b) import fisier** = pasii 1-9 de mai sus; pasul 7 (formularul) e exact punctul unde "operatorul intervine" mentionat de tine. ## B. Cum se identifica azi PARTENERUL (client/furnizor) Motorul e in `ExtrasBanca::CreeazaNote`, blocul de cautare parteneri (`oproceduri_import.prg:433-547`), rulat o singura data per (cod_fiscal, denumire, iban) distinct din import (`oproceduri_import.prg:435`), apoi aplicat pe toate liniile (`oproceduri_import.prg:542-544`). Ordinea de cautare, in cascada: 1. **Dupa cod fiscal** (`oproceduri_import.prg:453-488`) - doar daca linia are cod fiscal completat. `GetPartenerByCodFiscal` (`COMUN\programe\oproceduri_comune.prg:6599-6621`) face potrivire **exacta** dupa cod fiscal normalizat (fara spatii, fara prefix "RO"), `MAX(id_part)` daca sunt mai multi identici. Daca acelasi CUI normalizat exista pe mai multi parteneri (grup de duplicate, `GetParteneriByCuiNormalizat`, `oproceduri_import.prg:4515-4526`), se alege partenerul din grup cu cele mai multe inregistrari (si cel mai mare sold) in `ireg_parteneri` pentru anul/luna curenta (`oproceduri_import.prg:459-483`, comentariu explicit la linia 52: *"la CUI-uri duplicate aleg partenerul cu facturi in perioada curenta, nu ultimul creat"*). 2. **Dupa denumire** (`oproceduri_import.prg:490-513`), doar daca pasul 1 nu a gasit nimic. `GetPartenerByDenumire` (`COMUN\programe\oproceduri_comune.prg:6623-6645`) face potrivire **exacta** `TRIM(UPPER(denumire)) = ?pcDenumire` - nu foloseste `LIKE`, nu elimina diacritice, nu normalizeaza forma juridica ("SRL" vs "S.R.L." vs fara sufix). Daca nu gaseste exact, incearca variante de ordine a cuvintelor pentru nume de persoane (`GetNamePermutations`, `COMUN\programe\oproceduri_comune.prg:6789 si urm.`, apelat la `oproceduri_import.prg:500-511`) - util doar pentru "Popescu Ion" vs "Ion Popescu", nu pentru firme. 3. **Dupa IBAN** (`oproceduri_import.prg:516-523`), doar daca pasii 1-2 au esuat. `GetPartenerByContBanca` (`COMUN\programe\oproceduri_comune.prg:6647-6667`) - potrivire **exacta** pe `cont_banca`. 4. **Creare automata de partener nou** (`oproceduri_import.prg:527-540`) - doar daca are cod fiscal si optiunea `This.lCreeazaParteneri` e activa; fara cod fiscal, linia ramane fara partener (`id_part = 0`), vizibila operatorului in grid la pasul de corectie. **Cazul concret Banca Transilvania**: parserul `csvBT` **nu extrage niciodata codul fiscal** al platitorului - `mcf = ""` e fixat necondiotionat la `oproceduri_import.prg:3015`. Asta inseamna ca pentru singurul format pe care il ai (BT), pasul 1 de mai sus (cod fiscal, cel mai sigur) **nu se executa niciodata** - se sare direct la potrivirea exacta dupa denumire (pasul 2), care e cea mai fragila. Denumirea partenerului extrasa din linia BT vine din pozitia 3 a campului "Descriere", despartit dupa `;` (`mtert = ALLTRIM(syGETWORDNUM(mexplicatie, 3, ';'))`, `oproceduri_import.prg:3013`), iar IBAN-ul partenerului din pozitia 4 (`oproceduri_import.prg:3014`). ## C. Cum se identifica azi FACTURA Tot in `CreeazaNote`, blocul "asociez document" (`oproceduri_import.prg:701-817`), care ruleaza **doar daca** optiunea `This.lFactura` e activa (cautare dupa numar de document, `oproceduri_import.prg:710-764`) sau `This.lComanda` (cautare dupa numar de comanda, `oproceduri_import.prg:769-817`, logica identica dar cauta si in `nrord`/`explicatia` prin `LIKE`). Comentariul autorului chiar deasupra blocului descrie exact ce ar trebui sa faca si ce NU face azi (`oproceduri_import.prg:701-706`): > *"Repet pana la epuizarea sumei de plata/incasare... Daca nu mai am documente, dar suma de > plata nu este epuizata, imperechez facturile in ordine cronologica..."* - acest TODO nu e > implementat. Ce se intampla efectiv, pas cu pas (`oproceduri_import.prg:719-764`): 1. Se ia lista de numere gasite in text (`documente`, extrase de `GetRegExpAllNumbers`, `oproceduri_import.prg:4480-4510` - ia **toate** numerele intregi din descriere, dupa ce elimina datele de forma zz.ll.aa(aa) si anul curent). Pentru BT, aceasta lista vine din pozitia 2 a campului "Descriere" (`lcDescriereFacturi`, `oproceduri_import.prg:3017-3019`). 2. Pentru **primul** numar din lista, `GetDocumentByContPartenerAct` (`COMUN\programe\oproceduri_comune.prg:6984-7057`) cauta in `ireg_parteneri` documentul cu acel `nract`, pe acelasi partener si (daca exista) acelasi cont; daca gaseste **mai mult de un rezultat**, renunta si doar scrie in log ("Sunt prea multe rezultate care se potrivesc", linia 7038) - **nu alege niciunul**. 3. Daca a gasit exact un document (`oproceduri_import.prg:726`), suma din extras e acceptata **doar daca nu depaseste soldul ramas al facturii** (`lnSuma <= solddeb`, liniile 728-734). Dupa acest test, bucla **iese necondiotionat** (`EXIT`, linia 736) - indiferent daca s-a facut sau nu vreo asociere. **Consecinte directe, cu explicatie**: - **Suma exacta pe o singura factura** -> functioneaza (cazul comun, o plata = o factura). - **O plata acopera mai multe facturi** (suma > soldul primei facturi gasite) -> testul de la linia 728/731 esueaza, dar bucla oricum iese la `EXIT` (linia 736) fara sa incerce urmatorul numar de document din lista si fara sa distribuie suma pe facturi. Rezulta o linie **fara factura pereche**, desi numarul facturii a fost corect citit din text. - **O plata partiala pe o factura** (suma < sold) -> se asociaza, dar factura ramane cu sold neachitat corect in continuare (nu e un bug, e comportamentul dorit). - **Avansuri** (plata fara numar de factura in text) -> `documente` ramane gol, se forteaza totusi o cautare cu `'0'` (`oproceduri_import.prg:715-717`), care de regula nu gaseste nimic -> linia ramane fara factura, de corectat manual. - **Compensari** -> nu sunt tratate aici; sunt un modul separat (`COMUN\programe\ocompensari.prg`), nu fac parte din fluxul de import extras. - **Comisioane bancare** -> sunt recunoscute prin cuvinte cheie in descriere ("comision", "comision-atm", "pachet izi", etc., `oproceduri_import.prg:2998-3012` pentru BT) si duse direct pe cont 627, fara sa treaca prin cautarea de partener/factura - acest caz e deja tratat rezonabil. ## D. Ce e slab si ce s-ar schimba Defecte reale si confirmate in cod. Ce s-a masurat efectiv pe date de productie e in `BACKTEST_BANCA.md` - acolo e si cifra care arata ca schimbarea de la punctul 1 de mai jos nu e cea mai buna: `CONT_BANCA` e completat la doar 4,5% dintre parteneri activi, deci cheia de IBAN nu are azi pe ce lucra la scara mare. 1. **Pentru BT (singurul format folosit), cautarea partenerului nu foloseste niciodata codul fiscal** - `oproceduri_import.prg:3015` (`mcf = ""` fix) + cascada din `oproceduri_import.prg:453-523`. Se ajunge direct la potrivire exacta de denumire (`COMUN\programe\oproceduri_comune.prg:6623-6645`, fara `LIKE`, fara normalizare diacritice/ forma juridica). Extrasul BT contine IBAN-ul platitorului la pozitia 4 din descriere (`oproceduri_import.prg:3014`) - IBAN e mai stabil ca si cheie decat denumirea si e deja extras, dar in cod e incercat abia **al treilea**, dupa cod fiscal (mereu gol pentru BT) si dupa denumire. Nomenclatorul insa nu are `CONT_BANCA` completat decat la 4,5% dintre parteneri (masurat, vezi `BACKTEST_BANCA.md`), deci o simpla schimbare de ordine nu recupereaza mult pe cont propriu - cheia invatata din istoric, masurata in acelasi document, e cea care aduce castig real. 2. **O plata care acopera mai multe facturi, sau al carei prim numar de factura gasit nu se potriveste cu soldul, ramane azi complet nepereche** - `oproceduri_import.prg:710-764`, `EXIT` necondiotionat la linia 736 dupa primul document gasit, indiferent de rezultatul testului de suma; TODO-ul autorului chiar deasupra (`oproceduri_import.prg:701-706`) confirma ca aceasta e o lipsa cunoscuta, nu o alegere deliberata. Aceasta e cea mai probabila cauza pentru "plata acopera mai multe facturi" mentionata in cerinta ta. 3. **Cand acelasi numar de document se potriveste pe mai multe randuri in `ireg_parteneri`, sistemul renunta silentios** - `COMUN\programe\oproceduri_comune.prg:7035-7039` (`Reccount('cRezultatTemp') > 1` -> doar log, fara alegere). 4. **Extragerea numarului de factura ia orice numar din text, nu doar numere plauzibile de factura** - `GetRegExpAllNumbers` (`oproceduri_import.prg:4480-4510`) elimina doar datele calendaristice, nu si alte numere (referinte, telefoane). Combinat cu punctul 2 (se incearca doar primul numar gasit), un numar irelevant aflat inaintea numarului real de factura in text ar putea fi incercat primul si esua fara a mai incerca urmatorul. 5. **Pozitiile fixe din descrierea BT (cuvintele 3 si 4 despartite de `;`) nu sunt validate** - `oproceduri_import.prg:3013-3014` - daca banca schimba formatul sau tranzactia are alt tip de descriere decat "Incasare/Plata OP", `mtert`/`miban` pot prelua bucati de text gresite, fara nicio verificare (ex: ca IBAN-ul extras chiar arata a IBAN). ## E. Formatul fisierului Banca Transilvania (din cod) Nu exista niciun fisier exemplu de la Banca Transilvania in `Alte\` (doar `Alte\Extras_de_cont_11634808MT 940.txt` si `.xls`, care nu par BT dupa continut/format). Descrierea de mai jos vine strict din parserul `csvBT` (`oproceduri_import.prg:2923-3025`). **Detectie format** (`oproceduri_import.prg:1502-1507`), pe baza primei linii nevide a fisierului CSV: - incepe cu `"tranzactii"` sau `"transactions"` -> `csvBT(fisier, 1)` = "lista de tranzactii" (export lunar din BT24/NeoBT), antet comentat in cod: `Data tranzactie, Data valuta, Descriere, Referinta tranzactiei, Debit, Credit, Sold contabil` (7 coloane). - incepe cu `[lista de tranzactii]` sau `"lista de tranzactii"` -> `csvBT(fisier, 2)` = "extras de cont", antet: `Data tranzactie, Data valuta, Referinta, Tip tranzactie, Descriere, Debit, Credit` (7 coloane, ordinea Referinta/Tip tranzactie difera de varianta 1). **Cont propriu (IBAN al extrasului)**: cautat linie cu linie dupa eticheta `"numar cont:"`, primele 24 caractere dupa virgula (`oproceduri_import.prg:2936-2940`); daca nu se gaseste, `This.cIBAN = 'BT'` (`oproceduri_import.prg:2942-2944`). **Curatare text inainte de parsare CSV** (`oproceduri_import.prg:2954-2969`): - elimina virgulele din interiorul ghilimelelor si ghilimelele insesi; - elimina `LF + "~~"` (artefact de export BT24 cand descrierea trece pe linia urmatoare); - reuneste liniile rupte, pastrand un rand nou doar daca urmeaza dupa el o data (mai multe formate acceptate: `AAAA-LL-ZZ`, `ZZ/LL/AAAA`, `ZZ.LL.AAAA`, `ZZ-LL-AAAA`, `AAAA/LL/ZZ`); - detecteaza varianta BT24 (are coloana "sold contabil" -> `llBT24`, linia 2966): la BT24 sumele de pe Debit sunt negative si se inverseaza semnul (`oproceduri_import.prg:2980-2982`). **Camp "Descriere"** - text liber, dar cu conventie interna a bancii separata prin `;`. Exemplele reale din codul sursa (comentarii, `oproceduri_import.prg:2989,2992` - denumiri de firma anonimizate mai jos): ``` "30-05-2024","30-05-2024","Incasare OP - canal electronic;f 2024103;XXX SRL;ROXXBTRLXXXXXXXXXXXXXXXX;BTRLRO22;REF: 014ZEXA24151017X","014ZEXA24151017X","","1,184.24 ","2,184.24 " 2024-06-05,2024-06-05,"Incasare OP - canal electronic;F 2024131;XXX SA;ROXXBTRLXXXXXXXXXXXXXXXX;BTRLRO22;REF: 414ZEXA2415702DU",414ZEXA2415702DU,,"797.30","12,013.14" ``` Campurile din "Descriere" (despartite prin `;`), asa cum le foloseste codul: 1. tip tranzactie (text liber, ex. "Incasare OP - canal electronica"); 2. referinta/numar document - din acest camp `GetRegExpAllNumbers` extrage numerele candidate de factura (`oproceduri_import.prg:3017-3019`); 3. denumirea partenerului (`mtert`, `oproceduri_import.prg:3013`); 4. IBAN-ul partenerului (`miban`, `oproceduri_import.prg:3014`); 5. codul SWIFT al bancii partenerului (necitit de cod); 6. referinta platii (necitita de cod, ramane doar in text). **Clasificare tip tranzactie** dupa cuvinte cheie in "Descriere" (`oproceduri_import.prg:2998-3012`): `"comision"` (la inceput), `"comision-atm"`, `"comision procesare ridicare numerar"`, `"pachet izi"` -> cont 627 (cheltuiala bancara); `"retragere de numerar"` -> transfer numerar (cont 581/5311); `"transfer intern"` -> transfer intre conturi proprii (cont 581); orice altceva -> tratat ca tert (client/furnizor), cont 4111/401. ## Ce nu am lamurit - Nu am gasit un fisier CSV real exportat de Banca Transilvania in acest working copy, deci descrierea de la punctul E e dedusa din cod si din exemplele scrise ca si comentarii de dezvoltator - nu am validat-o pe un export proaspat (BT poate sa fi schimbat coloanele intre timp; codul insusi are comentarii separate pentru "BT24" si "NeoBT" ca doua variante usor diferite). - Nu exista (cel putin nu l-am gasit) un tabel de audit/log care sa retina *propunerea automata a algoritmului* separat de *starea finala dupa corectia manuala din `frm_modific2024`* - masuratorile de productie (`BACKTEST_BANCA.md`) vad doar rezultatul final, nu cat de mult "salveaza" operatorul azi prin corectii manuale linie cu linie. Pentru a masura exact asta ar trebui adaugata o logare la pasul de import (inainte de afisarea formularului). - Nu am verificat daca IBAN-ul partenerului extras de `csvBT` (`miban`, `oproceduri_import.prg:3014`) ajunge sa fie salvat ca atare in `ACTACTAN` sau se pierde la constructia liniei finale (`oproceduri_import.prg:898-909`) - `iban` apare in `C_IMPORT_TEMP`/`cActTemp`, dar nu l-am gasit explicit in lista de coloane a `INSERT INTO actactan` verificata la punctul A.8; ar necesita o verificare suplimentara pe schema reala a tabelei `ACTACTAN` din Oracle (`DESC actactan`), pe care nu am rulat-o (doar SELECT-uri, nu aveam nici conexiune).