From da19090ed9f431fc4b6a81648a4cecec69426d38 Mon Sep 17 00:00:00 2001 From: Marius Mutu Date: Sun, 2 Aug 2026 10:29:34 +0300 Subject: [PATCH] Import parteneri: se curata doar CNP-urile invalide, codul persoanelor juridice ramane neatins --- .gitignore | 1 + ALTE/check_semnale_pj.ps1 | 91 +++++++++ ALTE/check_swap_tara.ps1 | 49 +++++ Teste/recompileaza_binare.ps1 | 59 ++++++ Teste/ruleaza_teste.ps1 | 3 +- Teste/test_cod_fiscal.prg | 217 +++++++++++++++------ Teste/test_cod_fiscal_real.prg | 133 +++++++++++++ docs/depanare_testare_flora2roa.md | 11 ++ frm_import.sc2 | 32 ++-- myutils.prg | 297 ++++++++++++++++++++++------- 10 files changed, 754 insertions(+), 139 deletions(-) create mode 100644 ALTE/check_semnale_pj.ps1 create mode 100644 ALTE/check_swap_tara.ps1 create mode 100644 Teste/recompileaza_binare.ps1 create mode 100644 Teste/test_cod_fiscal_real.prg diff --git a/.gitignore b/.gitignore index 7657b18..ea78113 100644 --- a/.gitignore +++ b/.gitignore @@ -92,6 +92,7 @@ Teste/uisync/ Teste/screenshots/ Teste/_erori_test.txt Teste/*_log.txt +Teste/raport_*.csv # --- Cache text extern (inlocuit de fluxul in-arbore .??2) --- _textcache/ diff --git a/ALTE/check_semnale_pj.ps1 b/ALTE/check_semnale_pj.ps1 new file mode 100644 index 0000000..a649bd7 --- /dev/null +++ b/ALTE/check_semnale_pj.ps1 @@ -0,0 +1,91 @@ +# Cat de bine putem distinge persoana fizica (PF) de persoana juridica (PJ) in Fidelio, +# FARA sa ne bazam pe validitatea codului fiscal. +param([string]$Dir = "D:\PROIECTE\FLORA\FLORA2ROA\EXEMPLE\hotel") + +function Test-CNP([string]$c) { + if ($c -notmatch '^\d{13}$') { return $false } + $w = @(2,7,9,1,4,6,3,5,8,2,7,9); $s = 0 + for ($i = 0; $i -lt 12; $i++) { $s += [int]::Parse($c[$i]) * $w[$i] } + $r = $s % 11; if ($r -eq 10) { $r = 1 } + return ($r -eq [int]::Parse($c[12])) +} +function Test-CUI([string]$c) { + if ($c -notmatch '^\d{2,10}$') { return $false } + $c = $c.PadLeft(10, '0'); $k = '7532175321'; $s = 0 + for ($i = 0; $i -lt 9; $i++) { $s += [int]::Parse($c[$i]) * [int]::Parse($k[$i]) } + $r = ($s * 10) % 11; if ($r -eq 10) { $r = 0 } + return ($r -eq [int]::Parse($c[9])) +} + +# indicii de denumire pentru persoana juridica +$cuvintePJ = @('SRL','S.R.L','SA','S.A','SC','S.C','PFA','P.F.A','SNC','SCS','SRL-D','II','I.I', + 'ASOCIATIA','ASOCIATIE','FUNDATIA','SOCIETATE','COMPANY','LTD','LIMITED','GMBH','GMBH.', + 'BV','NV','INC','CORP','GROUP','HOLDING','UNIVERSITE','UNIVERSITATEA','UNIVERSITY', + 'SPITAL','PRIMARIA','MINISTERUL','AGENTIA','INSTITUTUL','SCOALA','LICEUL','COLEGIUL', + 'CLUB','ACS','A.C.S','CAR','CFR','TRANS','TOURS','TRAVEL','IMOB','CONSTRUCT','CONSULT', + 'SOLUTIONS','TECHTRONIC','WORKSHOP','STUDIO','CONCEPT','SERV','COMERCIAL','PROFESSIONAL') + +function Get-TipDupaNume([string]$nume) { + $cuv = $nume -split '[^A-Z0-9\.]+' | Where-Object { $_ -ne '' } + foreach ($c in $cuv) { if ($cuvintePJ -contains $c) { return 'PJ' } } + return '' +} +function Get-TipDupaReg([string]$reg) { + if ($reg -eq '') { return '' } + if ($reg -match '^[JFC]\s?\d{1,2}\s?/\s?\d+\s?/\s?\d{4}') { return 'PJ' } # J40/1234/2018 + if ($reg -match '^[JL]\d{6,}') { return 'PJ' } # L2019000159348 + if ($reg -match '^\d{1,2}/\d{4}$') { return 'PJ' } # 54/1997 + if ($reg -match '^[A-Z]{2}\s?\d{6}$') { return 'PF' } # serie CI: ZV 093375 + return '' +} + +$rows = @{} +Get-ChildItem -Path $Dir -Filter 'bts_detalii_facturi_rep_*.xml' | ForEach-Object { + $t = Get-Content $_.FullName -Raw + foreach ($m in [regex]::Matches($t, '(?is)([^<]*).*?([^<]*)\s*([^<]*)')) { + $nume = $m.Groups[1].Value.Trim().ToUpper() + $cod = $m.Groups[2].Value.Trim().ToUpper() + $reg = $m.Groups[3].Value.Trim().ToUpper() + if ($cod -ne '') { $rows["$nume|$cod|$reg"] = [pscustomobject]@{ Nume=$nume; Cod=$cod; Reg=$reg } } + } +} + +$rez = foreach ($r in $rows.Values) { + $d = $r.Cod -replace '[^\d]', '' + $roPrefix = ($r.Cod -replace '[^A-Z]', '') -in @('RO','CUI','CF','CIF') + $tipNume = Get-TipDupaNume $r.Nume + $tipReg = Get-TipDupaReg $r.Reg + $cnpOk = Test-CNP $d + $cuiOk = (Test-CUI $d) -and -not $cnpOk + + # decizie: semnale tari intai (nume/reg comert/prefix RO), apoi forma codului + $tip = '' + if ($roPrefix) { $tip = 'PJ' } + elseif ($tipNume -eq 'PJ') { $tip = 'PJ' } + elseif ($tipReg -eq 'PJ') { $tip = 'PJ' } + elseif ($cnpOk) { $tip = 'PF' } + elseif ($tipReg -eq 'PF') { $tip = 'PF' } + elseif ($cuiOk) { $tip = 'PJ' } + else { $tip = 'PF?' } # necunoscut, presupunem PF + + [pscustomobject]@{ Nume=$r.Nume; Cod=$r.Cod; Reg=$r.Reg; Tip=$tip; CnpOk=$cnpOk; CuiOk=$cuiOk } +} + +"total = $($rez.Count)" +$rez | Group-Object Tip | Sort-Object Count -Descending | Format-Table Name, Count | Out-String -Width 100 + +'' +'=== PJ cu cod care NU trece validarea de CUI (raman cu cod, ca sa atentioneze declaratia) ===' +$pjInvalid = $rez | Where-Object { $_.Tip -eq 'PJ' -and -not $_.CuiOk } +"numar = $($pjInvalid.Count)" +$pjInvalid | Select-Object -First 25 | Format-Table -AutoSize Nume, Cod, Reg | Out-String -Width 200 + +'' +'=== PF cu CNP invalid (se vor salva CU CNP GOL) ===' +$pfInvalid = $rez | Where-Object { $_.Tip -like 'PF*' -and -not $_.CnpOk } +"numar = $($pfInvalid.Count)" +$pfInvalid | Select-Object -First 25 | Format-Table -AutoSize Nume, Cod, Reg | Out-String -Width 200 + +'' +'=== risc: clasificate PJ dar codul e CNP valid (nu trebuie sa existe) ===' +($rez | Where-Object { $_.Tip -eq 'PJ' -and $_.CnpOk }) | Format-Table -AutoSize Nume, Cod, Reg | Out-String -Width 200 diff --git a/ALTE/check_swap_tara.ps1 b/ALTE/check_swap_tara.ps1 new file mode 100644 index 0000000..6f2920e --- /dev/null +++ b/ALTE/check_swap_tara.ps1 @@ -0,0 +1,49 @@ +# Cazuri in care CODFISCAL_CNP e invalid dar REGCOMERT_CI_PASS contine un CNP valid (campuri inversate). +param([string]$Dir = "D:\PROIECTE\FLORA\FLORA2ROA\EXEMPLE\hotel") + +function Test-CNP([string]$c) { + if ($c -notmatch '^\d{13}$') { return $false } + $w = @(2,7,9,1,4,6,3,5,8,2,7,9); $s = 0 + for ($i = 0; $i -lt 12; $i++) { $s += [int]::Parse($c[$i]) * $w[$i] } + $r = $s % 11; if ($r -eq 10) { $r = 1 } + return ($r -eq [int]::Parse($c[12])) +} + +$rows = @{} +Get-ChildItem -Path $Dir -Filter 'bts_detalii_facturi_rep_*.xml' | ForEach-Object { + $t = Get-Content $_.FullName -Raw + foreach ($m in [regex]::Matches($t, '(?is)([^<]*).*?([^<]*)\s*([^<]*)')) { + $nume = $m.Groups[1].Value.Trim().ToUpper() + $cod = $m.Groups[2].Value.Trim().ToUpper() + $reg = $m.Groups[3].Value.Trim().ToUpper() + if ($cod -ne '') { $rows["$nume|$cod|$reg"] = [pscustomobject]@{ Nume=$nume; Cod=$cod; Reg=$reg } } + } +} + +$swap = $rows.Values | Where-Object { + $d = $_.Cod -replace '[^\d]', '' + $r = $_.Reg -replace '[^\d]', '' + -not (Test-CNP $d) -and (Test-CNP $r) +} +"cazuri cu campuri inversate (CNP valid in REGCOMERT) = $($swap.Count)" +$swap | Format-Table -AutoSize Nume, Cod, Reg | Out-String -Width 200 + +# exista informatie de tara la cumparator? +$f = Get-ChildItem -Path $Dir -Filter 'bts_detalii_facturi_rep_*.xml' | Select-Object -First 1 +$t = Get-Content $f.FullName -Raw +$m = [regex]::Match($t, '(?is).*?') +'' +'=== campurile de tara existente in XML (primul rand) ===' +[regex]::Matches($m.Value, '(?i)<(\w*TARA\w*)>([^<]*)') | ForEach-Object { " $($_.Groups[1].Value) = [$($_.Groups[2].Value)]" } + +'' +'=== cate randuri au COMPANIE_TARA completat ===' +$cuTara = 0; $total = 0 +Get-ChildItem -Path $Dir -Filter 'bts_detalii_facturi_rep_*.xml' | ForEach-Object { + $txt = Get-Content $_.FullName -Raw + foreach ($mm in [regex]::Matches($txt, '(?i)([^<]*)')) { + $total++ + if ($mm.Groups[1].Value.Trim() -ne '') { $cuTara++ } + } +} +"COMPANIE_TARA completat pe $cuTara din $total randuri" diff --git a/Teste/recompileaza_binare.ps1 b/Teste/recompileaza_binare.ps1 new file mode 100644 index 0000000..d851a49 --- /dev/null +++ b/Teste/recompileaza_binare.ps1 @@ -0,0 +1,59 @@ +<# +.SYNOPSIS + Recompileaza formularele/clasele VFP dupa un write-back din text (bin_din_text.ps1). + +.DESCRIPTION + FoxBin2Prg regenereaza .scx/.sct DIN TEXT, dar fara codul obiect compilat. Un formular + regenerat si NErecompilat ruleaza fara `#INCLUDE`-uri: constantele din comun.h (CT_SUCCES, + CT_INSUCCES...) devin "Variable not found" abia in RUNTIME, nu la verificarea de sintaxa. + + De aceea, dupa FIECARE bin_din_text.ps1 se ruleaza si scriptul asta. + +.EXAMPLE + powershell -ExecutionPolicy Bypass -File Teste\bin_din_text.ps1 -TextFile frm_import.sc2 + powershell -ExecutionPolicy Bypass -File Teste\recompileaza_binare.ps1 +#> +[CmdletBinding()] +param( + [string[]]$Formular = @('frm_import.scx'), + [string]$ProjectRoot +) +$ErrorActionPreference = 'Stop' + +if (-not $ProjectRoot) { $ProjectRoot = Split-Path $PSScriptRoot -Parent } + +$vfp = 'C:\Program Files (x86)\Microsoft Visual FoxPro 9\vfp9.exe' +if (-not (Test-Path $vfp)) { throw "Nu gasesc $vfp" } + +$prg = Join-Path $env:TEMP ("recompile_" + [guid]::NewGuid().ToString('N') + '.prg') +$linii = @( + "SET DEFAULT TO `"$ProjectRoot`"", + "SET PATH TO `"$ProjectRoot`"" +) +foreach ($f in $Formular) { + $cale = Join-Path $ProjectRoot $f + if (-not (Test-Path $cale)) { throw "Nu exista $cale" } + $linii += "COMPILE FORM `"$f`"" +} +$linii += 'QUIT' +[IO.File]::WriteAllLines($prg, $linii, [Text.Encoding]::GetEncoding(1252)) + +try { + & $vfp -A -T $prg | Out-Null +} finally { + Remove-Item $prg -ErrorAction SilentlyContinue +} + +# verificare: .sct trebuie sa contina cod obiect, nu doar textul metodelor +$rezultate = foreach ($f in $Formular) { + $sct = Join-Path $ProjectRoot ([IO.Path]::ChangeExtension($f, '.sct')) + [pscustomobject]@{ + Formular = $f + SCT_KB = if (Test-Path $sct) { [math]::Round((Get-Item $sct).Length / 1KB) } else { 0 } + Stare = if ((Test-Path $sct) -and (Get-Item $sct).Length -gt 0) { 'OK' } else { 'LIPSA' } + } +} +$rezultate | Format-Table -AutoSize + +if ($rezultate | Where-Object { $_.Stare -ne 'OK' }) { exit 1 } +Write-Host 'Recompilare terminata.' -ForegroundColor Green diff --git a/Teste/ruleaza_teste.ps1 b/Teste/ruleaza_teste.ps1 index 020b411..23fdb2f 100644 --- a/Teste/ruleaza_teste.ps1 +++ b/Teste/ruleaza_teste.ps1 @@ -34,7 +34,8 @@ $Root = Split-Path $Teste -Parent $suita = @( @{ Prg = 'test_frm_import_ui.prg'; Steps = @('afisat', 'trace', 'date') } @{ Prg = 'test_export_date.prg'; Steps = @('afisat', 'importat') } - @{ Prg = 'test_cod_fiscal.prg'; Steps = @('validari') } + @{ Prg = 'test_cod_fiscal.prg'; Steps = @('validari') } + @{ Prg = 'test_cod_fiscal_real.prg'; Steps = @('analiza') } ) if ($Doar) { $suita = $suita | Where-Object { $_.Prg -like "*$Doar*" } } diff --git a/Teste/test_cod_fiscal.prg b/Teste/test_cod_fiscal.prg index 8459184..16756a8 100644 --- a/Teste/test_cod_fiscal.prg +++ b/Teste/test_cod_fiscal.prg @@ -1,6 +1,11 @@ -* test_cod_fiscal.prg - test unitar pentru GetCodFiscalValid (validare CNP / CUI la import parteneri). -* Verifica regula ceruta: cod fiscal / CNP invalid => se salveaza GOL in ROA, ca sa nu dea -* erori la declaratii (D394 / D406). +* test_cod_fiscal.prg - teste pentru clasificarea partenerilor importati si curatarea CNP-urilor. +* +* Regula verificata: +* - PERSOANA FIZICA cu CNP invalid => cod fiscal GOL (permis la D394/D406) +* - PERSOANA FIZICA cu CNP valid => CNP pastrat, curatat de spatii/puncte +* - PERSOANA JURIDICA => codul NU se sterge NICIODATA, chiar daca nu trece +* validarea romaneasca (poate fi partener strain), +* ca sa fim atentionati la validarea declaratiei * * Rulare directa: * vfp9.exe -A -T "D:\PROIECTE\FLORA\FLORA2ROA\Teste\test_cod_fiscal.prg" @@ -31,70 +36,172 @@ DO HarnessInit LOCAL lnTip, lcRez -*--- CNP valid: se pastreaza, tip persoana fizica --- +*============================================================== +* 1. PERSOANE FIZICE - singurele la care se sterge codul +*============================================================== lnTip = 0 -lcRez = GetCodFiscalValid('1800101410013', @lnTip) -TestAssertEqual('1800101410013', m.lcRez, 'CNP valid pastrat') -TestAssertEqual(2, m.lnTip, 'CNP valid => persoana fizica') +lcRez = GetCodFiscalValid('1800101410013', @lnTip, 'POPESCU ION', 'ZV 093375') +TestAssertEqual('1800101410013', m.lcRez, 'PF: CNP valid pastrat') +TestAssertEqual(2, m.lnTip, 'PF: CNP valid => persoana fizica') -*--- CNP valid murdar (spatii, punct): se curata si ramane --- -TestAssertEqual('1800101410013', GetCodFiscalValid(' 1800101410013. '), 'CNP valid curatat de spatii/punct') +TestAssertEqual('1800101410013', GetCodFiscalValid(' 1800101410013. ', @lnTip, 'POPESCU ION', ''), ; + 'PF: CNP valid curatat de spatii si punct') -*--- CNP cu cifra de control gresita => sir gol --- -TestAssertEqual('', GetCodFiscalValid('1800101410018'), 'CNP cu cifra de control alterata se sterge') -TestAssertEqual('', GetCodFiscalValid('2650407170374'), 'CNP invalid real din Fidelio (1)') -TestAssertEqual('', GetCodFiscalValid('1550622400577'), 'CNP invalid real din Fidelio (2)') - -*--- lungimi gresite (12 / 14 cifre) => sir gol --- -TestAssertEqual('', GetCodFiscalValid('171030126201'), '12 cifre => invalid') -TestAssertEqual('', GetCodFiscalValid('29050709211182'), '14 cifre => invalid') -TestAssertEqual('', GetCodFiscalValid('60109121600600'), '14 cifre => invalid (2)') -* '2920121295914.' are 13 cifre dupa curatarea punctului si CNP-ul e valid => se pastreaza -TestAssertEqual('2920121295914', GetCodFiscalValid('2920121295914.'), 'CNP valid cu punct la final se curata') - -*--- CUI persoana juridica --- +* CNP-uri invalide reale din Fidelio: codul se goleste lnTip = 0 -lcRez = GetCodFiscalValid('RO21675175', @lnTip) -TestAssertEqual('RO21675175', m.lcRez, 'CUI cu prefix RO pastrat') -TestAssertEqual(1, m.lnTip, 'CUI => persoana juridica') +lcRez = GetCodFiscalValid('2650407170374', @lnTip, 'TRUTA CLAUDIA MIHAELA', 'RK 800386') +TestAssertEqual('', m.lcRez, 'PF: CNP cu cifra de control gresita se sterge') +TestAssertEqual(2, m.lnTip, 'PF: ramane persoana fizica') -TestAssertEqual('RO23565004', GetCodFiscalValid('RO 23565004'), 'CUI cu spatiu normalizat') +TestAssertEqual('', GetCodFiscalValid('171030126201', @lnTip, 'TARTA GAVRIL SILVIU', ''), ; + 'PF: CNP de 12 cifre se sterge') +TestAssertEqual('', GetCodFiscalValid('29050709211182', @lnTip, 'HOGESCU CRISTINA', ''), ; + 'PF: CNP de 14 cifre se sterge') +TestAssertEqual('', GetCodFiscalValid('60109121600600', @lnTip, 'IOANCA DAMARIS', ''), ; + 'PF: CNP de 14 cifre se sterge (2)') + +* CNP valid scris cu punct la final +TestAssertEqual('2920121295914', GetCodFiscalValid('2920121295914.', @lnTip, 'ANDREI MARIA', ''), ; + 'PF: CNP valid cu punct la final se curata') + +* persoana fizica fara niciun semnal, cod invalid => tot persoana fizica, cod gol +lnTip = 0 +lcRez = GetCodFiscalValid('287101935573', @lnTip, 'OPREA ANDREEA', '') +TestAssertEqual('', m.lcRez, 'PF: fara semnale, cod invalid => gol') +TestAssertEqual(2, m.lnTip, 'PF: fara semnale => persoana fizica') + +*============================================================== +* 2. PERSOANE JURIDICE - codul NU se sterge niciodata +*============================================================== +lnTip = 0 +lcRez = GetCodFiscalValid('RO21675175', @lnTip, 'JUST MULTISERV SRL', 'J03/245/2007') +TestAssertEqual('RO21675175', m.lcRez, 'PJ: CUI cu prefix RO pastrat') +TestAssertEqual(1, m.lnTip, 'PJ: prefix RO => persoana juridica') + +TestAssertEqual('RO23565004', GetCodFiscalValid('RO 23565004', @lnTip, 'FIRMA SRL', ''), ; + 'PJ: spatiul din cod se elimina') lnTip = 0 -lcRez = GetCodFiscalValid('24536740', @lnTip) -TestAssertEqual('24536740', m.lcRez, 'CUI fara RO pastrat') -TestAssertEqual(1, m.lnTip, 'CUI fara RO => persoana juridica') +lcRez = GetCodFiscalValid('24536740', @lnTip, 'KALVA SOLUTIONS SRL', 'J29/2677/2008') +TestAssertEqual('24536740', m.lcRez, 'PJ: CUI fara RO pastrat') +TestAssertEqual(1, m.lnTip, 'PJ: denumire cu SRL => persoana juridica') -*--- CUI cu cifra de control gresita => sir gol --- -TestAssertEqual('', GetCodFiscalValid('24536741'), 'CUI cu cifra de control gresita se sterge') - -*--- valori goale / fara cifre utile --- -TestAssertEqual('', GetCodFiscalValid(''), 'sir gol') -TestAssertEqual('', GetCodFiscalValid(' '), 'spatii') - -*--- parteneri STRAINI: codul nu respecta algoritmul romanesc, deci NU se sterge --- +* CUI romanesc GRESIT la o persoana juridica: NU se sterge, ca sa dea eroare la declaratie lnTip = 0 -lcRez = GetCodFiscalValid('BE0325777171', @lnTip) -TestAssertEqual('BE0325777171', m.lcRez, 'TVA belgian pastrat ca atare') -TestAssertEqual(1, m.lnTip, 'cod strain => persoana juridica') +lcRez = GetCodFiscalValid('24536741', @lnTip, 'KALVA SOLUTIONS SRL', 'J29/2677/2008') +TestAssertEqual('24536741', m.lcRez, 'PJ: CUI invalid NU se sterge') +TestAssertEqual(1, m.lnTip, 'PJ: CUI invalid ramane persoana juridica') -TestAssertEqual('DICA9712015/99KS', GetCodFiscalValid('DICA9712015/99KS'), 'cod strain cu slash pastrat') -TestAssertEqual('NN9J17714', GetCodFiscalValid('NN9J17714'), 'cod strain alfanumeric pastrat') -TestAssertEqual('XD 061964', GetCodFiscalValid('XD 061964'), 'cod strain cu spatiu pastrat') -TestAssertEqual('ABC', GetCodFiscalValid('ABC'), 'cod strain fara cifre pastrat') - -*--- prefixele romanesti se trateaza tot cu regulile RO --- -TestAssertEqual('RO14770212', GetCodFiscalValid('CUI 14770212'), 'prefix CUI => CUI romanesc valid') -TestAssertEqual('RO12870', GetCodFiscalValid('RO 12870'), 'CUI scurt valid, spatiu eliminat') -TestAssertEqual('', GetCodFiscalValid('RO 12871'), 'CUI romanesc invalid se sterge') - -*--- codul de tara declarat opreste validarea romaneasca --- +* partener strain cu cod alfanumeric lnTip = 0 -lcRez = GetCodFiscalValid('123456789012345', @lnTip, 'BE') -TestAssertEqual('123456789012345', m.lcRez, 'cod numeric cu tara straina nu se valideaza romaneste') -TestAssertEqual(1, m.lnTip, 'tara straina => persoana juridica') +lcRez = GetCodFiscalValid('BE0325777171', @lnTip, 'UNIVERSITE DE LIEGE', '') +TestAssertEqual('BE0325777171', m.lcRez, 'PJ straina: TVA belgian pastrat') +TestAssertEqual(1, m.lnTip, 'PJ straina: cod TVA intracomunitar => persoana juridica') -TestAssertEqual('', GetCodFiscalValid('1800101410018', 0, 'RO'), 'tara RO => se valideaza romaneste') +TestAssertEqual('DICA9712015/99KS', GetCodFiscalValid('DICA9712015/99KS', @lnTip, 'FIRMA STRAINA SRL', ''), ; + 'PJ straina: cod cu slash pastrat cand denumirea are forma juridica') + +* cod cu litere care NU e cod de TVA si fara alt semnal => pasaport de persoana fizica +lnTip = 0 +lcRez = GetCodFiscalValid('NN9J17714', @lnTip, 'TORRES MELISSA', '') +TestAssertEqual('', m.lcRez, 'PF straina: pasaportul nu se salveaza pe cod fiscal') +TestAssertEqual(2, m.lnTip, 'PF straina: pasaport fara alt semnal => persoana fizica') + +* prefixul romanesc se normalizeaza la RO +lnTip = 0 +lcRez = GetCodFiscalValid('CUI 14770212', @lnTip, 'ASOCIATIA JUDETEANA DE HANDBAL', '') +TestAssertEqual('14770212', m.lcRez, 'PJ: eticheta CUI se scoate, fara sa se adauge RO') +TestAssertEqual(1, m.lnTip, 'PJ: eticheta CUI => persoana juridica') + +* 'RO' inseamna platitor de TVA: in ROA e ALT partener decat neplatitorul cu acelasi numar, +* deci prefixul se pastreaza exact cum vine si NU se adauga niciodata de noi +TestAssertEqual('RO24536740', GetCodFiscalValid('RO24536740', @lnTip, 'KALVA SOLUTIONS SRL', ''), ; + 'PJ platitoare de TVA: prefixul RO se pastreaza') +TestAssertEqual('24536740', GetCodFiscalValid('24536740', @lnTip, 'KALVA SOLUTIONS SRL', ''), ; + 'PJ neplatitoare: NU se adauga RO') + +* partener strain cu cod NUMAI DIN CIFRE, recunoscut dupa tara +lnTip = 0 +lcRez = GetCodFiscalValid('123456789012345', @lnTip, 'HOTEL PARTNER', '', 'BE') +TestAssertEqual('123456789012345', m.lcRez, 'PJ straina: cod numeric cu tara straina se pastreaza') +TestAssertEqual(1, m.lnTip, 'PJ straina: tara straina => persoana juridica') + +* persoana juridica recunoscuta dupa numarul de registrul comertului +lnTip = 0 +lcRez = GetCodFiscalValid('99999999', @lnTip, 'DENUMIRE FARA FORMA JURIDICA', 'J40/5790/2018') +TestAssertEqual('99999999', m.lcRez, 'PJ: cod invalid pastrat cand exista nr. reg. comertului') +TestAssertEqual(1, m.lnTip, 'PJ: nr. registrul comertului => persoana juridica') + +lnTip = 0 +lcRez = GetCodFiscalValid('88888888', @lnTip, 'TICMAR EMMA CONCEPT', 'J2018000211289') +TestAssertEqual(1, m.lnTip, 'PJ: nr. reg. comertului fara slash => persoana juridica') + +lnTip = 0 +lcRez = GetCodFiscalValid('9924188', @lnTip, 'CAR CFR SECTIA L2 SI CT1 BRAILA', '54/1997') +TestAssertEqual(1, m.lnTip, 'PJ: numar/an => persoana juridica') + +* tipul venit direct din import bate toate celelalte semnale +lnTip = 0 +lcRez = GetCodFiscalValid('1800101410013', @lnTip, 'POPESCU ION', '', '', '1') +TestAssertEqual('1800101410013', m.lcRez, 'tip impus PJ: codul se pastreaza') +TestAssertEqual(1, m.lnTip, 'tip impus din import => persoana juridica') + +*============================================================== +* 3. Recuperare din campuri inversate in Fidelio +*============================================================== +* operatorul a scris pasaportul pe cod fiscal si CNP-ul pe serie CI +lnTip = 0 +lcRez = GetCodFiscalValid('XD 061964', @lnTip, 'POPITIU IOANA', '2640626364228') +TestAssertEqual('2640626364228', m.lcRez, 'recuperare: CNP valid luat din campul de serie CI') +TestAssertEqual(2, m.lnTip, 'recuperare: ramane persoana fizica') + +* daca in campul de serie CI nu exista CNP valid, nu inventam nimic +TestAssertEqual('', GetCodFiscalValid('XD 061964', @lnTip, 'POPITIU IOANA', 'ZV 093375'), ; + 'recuperare: fara CNP valid in serie CI ramane gol') + +*============================================================== +* 4. Cazuri limita +*============================================================== +lnTip = 0 +TestAssertEqual('', GetCodFiscalValid('', @lnTip, 'POPESCU ION', ''), 'cod gol ramane gol') +TestAssertEqual(2, m.lnTip, 'cod gol + nume de persoana => persoana fizica') + +lnTip = 0 +TestAssertEqual('', GetCodFiscalValid(' ', @lnTip, 'POPESCU ION', ''), 'spatii => gol') + +lnTip = 0 +lcRez = GetCodFiscalValid('', @lnTip, 'ALFA CONSTRUCT SRL', '') +TestAssertEqual('', m.lcRez, 'PJ fara cod ramane fara cod') +TestAssertEqual(1, m.lnTip, 'PJ fara cod este tot persoana juridica') + +*--- functiile ajutatoare --- +TestAssert(EstePersoanaJuridica('KALVA SOLUTIONS SRL'), 'EstePersoanaJuridica: SRL') +TestAssert(EstePersoanaJuridica('ASOCIATIA SPORTIVA X'), 'EstePersoanaJuridica: ASOCIATIA') +TestAssert(EstePersoanaJuridica('UNIVERSITE DE LIEGE'), 'EstePersoanaJuridica: UNIVERSITE') +TestAssert(!EstePersoanaJuridica('SAVU MARIA'), 'EstePersoanaJuridica: SAVU nu e SA') +TestAssert(!EstePersoanaJuridica('MIHAII ION'), 'EstePersoanaJuridica: MIHAII nu e II') +TestAssert(!EstePersoanaJuridica('POPESCU ION'), 'EstePersoanaJuridica: nume simplu') + +TestAssert(EsteNrRegComert('J40/5790/2018'), 'EsteNrRegComert: J40/5790/2018') +TestAssert(EsteNrRegComert('J2018000211289'), 'EsteNrRegComert: J2018000211289') +TestAssert(EsteNrRegComert('54/1997'), 'EsteNrRegComert: 54/1997') +TestAssert(!EsteNrRegComert('ZV 093375'), 'EsteNrRegComert: serie CI nu e reg. comert') +TestAssert(!EsteNrRegComert(''), 'EsteNrRegComert: gol') + +TestAssert(EsteSerieCI('ZV 093375'), 'EsteSerieCI: cu spatiu') +TestAssert(EsteSerieCI('IF834409'), 'EsteSerieCI: fara spatiu') +TestAssert(!EsteSerieCI('J40/5790/2018'), 'EsteSerieCI: nr. reg. comert nu e serie CI') +TestAssert(!EsteSerieCI('2990118070015'), 'EsteSerieCI: CNP nu e serie CI') + +TestAssert(EsteCodTVAStrain('BE0325777171'), 'EsteCodTVAStrain: BE') +TestAssert(EsteCodTVAStrain('DE811234567'), 'EsteCodTVAStrain: DE') +TestAssert(!EsteCodTVAStrain('NN9J17714'), 'EsteCodTVAStrain: pasaport nu e cod TVA') +TestAssert(!EsteCodTVAStrain('XD061964'), 'EsteCodTVAStrain: serie pasaport nu e cod TVA') + +TestAssertEqual('RO23565004', GetCodFiscalCurat('RO 23565004'), 'GetCodFiscalCurat: spatii, RO pastrat') +TestAssertEqual('14770212', GetCodFiscalCurat('CUI 14770212'), 'GetCodFiscalCurat: eticheta CUI se scoate') +TestAssertEqual('14770212', GetCodFiscalCurat('CF 14770212'), 'GetCodFiscalCurat: eticheta CF se scoate') +TestAssertEqual('2920121295914', GetCodFiscalCurat('2920121295914.'), 'GetCodFiscalCurat: punct') DO HarnessStep WITH 0, 'validari cod fiscal' diff --git a/Teste/test_cod_fiscal_real.prg b/Teste/test_cod_fiscal_real.prg new file mode 100644 index 0000000..4af0dad --- /dev/null +++ b/Teste/test_cod_fiscal_real.prg @@ -0,0 +1,133 @@ +* test_cod_fiscal_real.prg - ruleaza GetCodFiscalValid pe TOATE codurile din XML-urile Fidelio +* si scrie un raport, ca sa vedem efectul real inainte de import. +* +* Verifica invariantii care conteaza pentru declaratii: +* - nicio persoana JURIDICA nu ramane fara cod din cauza noastra (codul nu se sterge) +* - se sterge cod DOAR la persoane fizice si DOAR cand CNP-ul chiar este invalid +* - niciun CNP valid nu se pierde +* +* Rulare: vfp9.exe -A -T "D:\PROIECTE\FLORA\FLORA2ROA\Teste\test_cod_fiscal_real.prg" + +SET SAFETY OFF +SET TALK OFF + +PUBLIC gcUILog, gcSyncDir, gnTestFail, gnMockRaspuns, gcMockUltimMesaj, gnMockUltimTip +gnTestFail = 0 +gnMockRaspuns = 6 +gcMockUltimMesaj = '' +gnMockUltimTip = 0 + +LOCAL lcTestDir +lcTestDir = ADDBS(JUSTPATH(SYS(16, 0))) +gcUILog = lcTestDir + 'test_cod_fiscal_real_log.txt' +gcSyncDir = lcTestDir + 'uisync\' +IF !DIRECTORY(gcSyncDir) + MD (gcSyncDir) +ENDIF +STRTOFILE('START ' + TTOC(DATETIME()) + CHR(13) + CHR(10), gcUILog) + +DO (lcTestDir + 'test_init_env_auto.prg') WITH .F. +SET PROCEDURE TO (lcTestDir + 'ui_harness.prg') ADDITIVE +SET PROCEDURE TO (lcTestDir + 'mock_amessagebox.prg') ADDITIVE + +DO HarnessInit + +LOCAL lcCale, lcFisier, lnFisiere, lcRaport +lcCale = ADDBS(ALLTRIM(goApp.oSettings.cale_import_fidelio)) +HarnessLog('Citesc XML-urile din: ' + m.lcCale) + +CREATE CURSOR cTot (denumire C(200), cod C(50), reg_com C(50)) + +lnFisiere = 0 +lnSarite = 0 +lcFisier = SYS(2000, m.lcCale + 'bts_detalii_facturi_rep_*.xml') +DO WHILE !EMPTY(m.lcFisier) + lnRanduri = 0 + * fisierele goale sau fara coloanele asteptate se sar controlat + TRY + lnRanduri = XMLTOCURSOR(m.lcCale + m.lcFisier, 'cUnul', 512) + CATCH + lnRanduri = 0 + ENDTRY + + IF m.lnRanduri > 0 AND USED('cUnul') AND ; + TYPE('cUnul.codfiscal_cnp') = 'C' AND TYPE('cUnul.cumparator') = 'C' + SELECT DISTINCT CAST(ALLTRIM(cumparator) AS C(200)) AS denumire, ; + CAST(ALLTRIM(codfiscal_cnp) AS C(50)) AS cod, ; + CAST(ALLTRIM(regcomert_ci_pass) AS C(50)) AS reg_com ; + FROM cUnul WHERE !EMPTY(NVL(codfiscal_cnp, '')) INTO CURSOR cTemp + SELECT cTot + APPEND FROM DBF('cTemp') + USE IN (SELECT('cTemp')) + lnFisiere = m.lnFisiere + 1 + ELSE + lnSarite = m.lnSarite + 1 + ENDIF + + USE IN (SELECT('cUnul')) + lcFisier = SYS(2000, m.lcCale + 'bts_detalii_facturi_rep_*.xml', 1) +ENDDO + +HarnessLog('Fisiere citite: ' + TRANSFORM(m.lnFisiere) + ', sarite (goale/fara coloane): ' + TRANSFORM(m.lnSarite)) +TestAssert(m.lnFisiere > 0, 'exista fisiere XML de analizat') + +SELECT DISTINCT denumire, cod, reg_com FROM cTot INTO CURSOR cUnic +HarnessLog('Combinatii distincte nume+cod+regcom: ' + TRANSFORM(RECCOUNT('cUnic'))) + +LOCAL lnPF, lnPJ, lnSters, lnPJNevalidat, lnCnpPierdut, lnPJFaraCod +LOCAL lnTip, lcRez, lcCifre, lcCod +STORE 0 TO lnPF, lnPJ, lnSters, lnPJNevalidat, lnCnpPierdut, lnPJFaraCod +lcRaport = 'denumire;cod_import;reg_com;tip;cod_salvat' + CHR(13) + CHR(10) + +SELECT cUnic +SCAN + lcCod = ALLTRIM(cUnic.cod) + lnTip = 0 + lcRez = GetCodFiscalValid(m.lcCod, @lnTip, cUnic.denumire, cUnic.reg_com) + + IF m.lnTip = 1 + lnPJ = m.lnPJ + 1 + * invariant 1: la persoana juridica NU stergem niciodata codul + IF EMPTY(m.lcRez) AND !EMPTY(m.lcCod) + lnPJFaraCod = m.lnPJFaraCod + 1 + HarnessLog('EROARE invariant: PJ ramasa fara cod: ' + ALLTRIM(cUnic.denumire) + ' [' + m.lcCod + ']') + ENDIF + IF !VerifCF(m.lcRez) + lnPJNevalidat = m.lnPJNevalidat + 1 + ENDIF + ELSE + lnPF = m.lnPF + 1 + IF EMPTY(m.lcRez) + lnSters = m.lnSters + 1 + * invariant 2: nu stergem CNP-uri VALIDE + lcCifre = CHRTRAN(m.lcCod, CHRTRAN(m.lcCod, '0123456789', ''), '') + IF LEN(m.lcCifre) = 13 AND cValidCNP(m.lcCifre) + lnCnpPierdut = m.lnCnpPierdut + 1 + HarnessLog('EROARE invariant: CNP valid pierdut: ' + ALLTRIM(cUnic.denumire) + ' [' + m.lcCod + ']') + ENDIF + ENDIF + ENDIF + + lcRaport = m.lcRaport + ALLTRIM(cUnic.denumire) + ';' + m.lcCod + ';' + ALLTRIM(cUnic.reg_com) + ';' + ; + IIF(m.lnTip = 1, 'PJ', 'PF') + ';' + m.lcRez + CHR(13) + CHR(10) +ENDSCAN + +STRTOFILE(m.lcRaport, m.lcTestDir + 'raport_cod_fiscal_real.csv', .F.) + +HarnessLog('persoane fizice : ' + TRANSFORM(m.lnPF)) +HarnessLog('persoane juridice : ' + TRANSFORM(m.lnPJ)) +HarnessLog('PF cu CNP sters : ' + TRANSFORM(m.lnSters)) +HarnessLog('PJ cu cod nevalidat RO : ' + TRANSFORM(m.lnPJNevalidat) + ' (raman cu cod, atentioneaza declaratia)') + +TestAssertEqual(0, m.lnPJFaraCod, 'nicio persoana juridica nu ramane fara cod') +TestAssertEqual(0, m.lnCnpPierdut, 'niciun CNP valid nu se pierde') +TestAssert(m.lnPF + m.lnPJ > 1000, 's-au analizat toti partenerii din exemple') +TestAssert(m.lnSters > 0, 'exista CNP-uri invalide care se curata') +TestAssert(m.lnPJ > 0, 'exista si persoane juridice detectate') + +DO HarnessStep WITH 0, 'analiza pe date reale' + +HarnessLog('REZULTAT: ' + TestRezultat()) +DO HarnessDone WITH TestRezultat() + +QUIT diff --git a/docs/depanare_testare_flora2roa.md b/docs/depanare_testare_flora2roa.md index 529f004..ec970a4 100644 --- a/docs/depanare_testare_flora2roa.md +++ b/docs/depanare_testare_flora2roa.md @@ -31,6 +31,11 @@ Specific FLORA2ROA: `settings.ini`, `Trace` scrie in `edtInfo`. - `test_export_date.prg` - functional: `ExportDate()` citeste XML-urile din interval si populeaza cursoarele (fara Oracle). +- `test_cod_fiscal.prg` - unitar: clasificarea partenerilor (persoana fizica / juridica) si + curatarea CNP-urilor invalide (`GetCodFiscalValid` si functiile ajutatoare din `myutils.prg`). +- `test_cod_fiscal_real.prg` - ruleaza aceeasi logica pe TOATE codurile din `EXEMPLE\hotel` + si verifica invariantii: nicio persoana juridica nu ramane fara cod, niciun CNP valid nu se + pierde. Scrie `Teste\raport_cod_fiscal_real.csv` cu decizia luata pentru fiecare partener. - `test_conectare_roa.prg` - verifica drumul CU conectare la Oracle: fie se conecteaza si executa o interogare de proba, fie raporteaza esecul si iese controlat. NU e in suita implicita (depinde de reteaua/serverul Oracle). @@ -66,8 +71,14 @@ powershell -ExecutionPolicy Bypass -File Teste\text_din_bin.ps1 # binar # [IO.File]::WriteAllText($p, $t, [Text.Encoding]::GetEncoding(1252)) powershell -ExecutionPolicy Bypass -File Teste\bin_din_text.ps1 -TextFile _textcache\frm_import.sc2 -DryRun powershell -ExecutionPolicy Bypass -File Teste\bin_din_text.ps1 -TextFile _textcache\frm_import.sc2 +powershell -ExecutionPolicy Bypass -File Teste\recompileaza_binare.ps1 # OBLIGATORIU dupa write-back ``` +> **Write-back-ul NU compileaza.** FoxBin2Prg reface `.scx`/`.sct` din text, dar fara codul +> obiect, deci formularul ruleaza fara `#INCLUDE "comun.h"`: constantele (`CT_SUCCES`, +> `CT_INSUCCES`) devin *"Variable not found"* abia in RUNTIME. Verificarea de sintaxa trece, +> testul pica. `Teste\recompileaza_binare.ps1` face `COMPILE FORM` si repara asta. + Write-back-ul face fidelity check (bin -> text, comparat pe octeti) inainte de a atinge binarul si refuza daca `frm_import.scx` e blocat de IDE sau de `flora2roa.exe`. diff --git a/frm_import.sc2 b/frm_import.sc2 index a8d3998..043ffd9 100644 --- a/frm_import.sc2 +++ b/frm_import.sc2 @@ -272,7 +272,7 @@ DEFINE CLASS form1 AS form * Completez id_part Local lcBanca, lcCod_fiscal, lcCont_Banca, lcCorespDel, lcDenumire, lcIdString, lcId_categ_ent Local lcId_loc_inreg, lcId_util, lcMesaj, lcMotiv_inactiv, lcNume, lcPrefix, lcPrenume, lcReg_comert - Local lcSql, lcSqlInsert, lcSufix, lcTip_persoana, lcinactiv, lnSucces, lcCodValid, lnTip_persoana, lcCodTara + Local lcSql, lcSqlInsert, lcSufix, lcTip_persoana, lcinactiv, lnSucces, lnTip_persoana, lcCodTara Local lcAdresa, lcAdreseParteneri, lcApart, lcBloc, lcCaleImport, lcCod, lcCodpostal, lcDA_apare Local lcDenumire_adresa, lcEmail, lcEtaj, lcFax, lcFile, lcIdPart, lcId_Judet, lcId_loc, lcId_tara Local lcItem1, lcItem2, lcItem3, lcItem4, lcJudet, lcJudetBucuresti, lcLocalitate, lcNumar @@ -400,24 +400,26 @@ DEFINE CLASS form1 AS form lcReg_comert = Nvl(Alltrim(Upper(reg_com)), "") *************************** lcCod=Alltrim(Upper(Nvl(m.lcCod_fiscal,""))) - lcTip_persoana="2" && 1 = juridica, 2 = fizica - * Prefer persoanele fizice pentru ca sunt mai multe fata de persoanele juridice. - * Sunt multe persoane fizice cu CNP-uri gresite (lungime gresita sau cifra de control gresita). - * GetCodFiscalValid curata si valideaza codul; daca este invalid intoarce sir gol, - * deci partenerul se salveaza in ROA FARA cod fiscal si nu mai da erori la D394/D406. + lcTip_persoana = "2" && 1 = juridica, 2 = fizica + * GetCodFiscalValid decide tipul de persoana din denumire, nr. registrul comertului si + * forma codului, apoi curata DOAR CNP-urile invalide ale persoanelor fizice. + * Codul unei persoane juridice nu se sterge niciodata: daca e gresit, vrem sa fim + * atentionati la validarea declaratiei si sa il corectam in Fidelio, nu sa il ascundem. If !Empty(m.lcCod) lnTip_persoana = 0 lcCodTara = Alltrim(Upper(Nvl(cod_tara, ""))) - lcCodValid = GetCodFiscalValid(m.lcCod, @lnTip_persoana, m.lcCodTara) + lcCod_fiscal = GetCodFiscalValid(m.lcCod, @lnTip_persoana, m.lcDenumire, m.lcReg_comert, m.lcCodTara) + lcTip_persoana = Iif(m.lnTip_persoana = 1, "1", "2") + Do Case - Case !Empty(m.lcCodValid) - lcCod_fiscal = m.lcCodValid - lcTip_persoana = Iif(m.lnTip_persoana = 1, "1", "2") - Otherwise - * numeric dar invalid (12/14/15 cifre, cifra de control gresita): sterg codul - Thisform.trace('Cod fiscal invalid, se salveaza gol: [' + m.lcCod + '] ' + Alltrim(m.lcDenumire)) - lcCod_fiscal = '' - lcTip_persoana = "2" + Case m.lcTip_persoana = "2" And Empty(m.lcCod_fiscal) + * persoana fizica cu CNP invalid: se salveaza fara CNP (permis la D394/D406) + Thisform.trace('CNP invalid, partenerul se salveaza fara CNP: [' + m.lcCod + '] ' + Alltrim(m.lcDenumire)) + Case m.lcTip_persoana = "1" And !VerifCF(m.lcCod_fiscal) + * persoana juridica cu cod fiscal care nu trece validarea romaneasca: poate fi + * partener strain (cod valid la el in tara) sau o eroare de operare. Codul ramane, + * dar il semnalam in log ca sa poata fi verificat. + Thisform.trace('ATENTIE: persoana juridica cu cod fiscal nevalidat: [' + m.lcCod_fiscal + '] ' + Alltrim(m.lcDenumire)) Endcase ENDIF IF m.lcTip_Persoana = "2" diff --git a/myutils.prg b/myutils.prg index 8d9936d..14be332 100644 --- a/myutils.prg +++ b/myutils.prg @@ -1724,88 +1724,249 @@ FUNCTION GetCodFiscalFRO RETURN m.lcCodFiscalFRO ENDFUNC * -* Normalizeaza codul fiscal / CNP primit din import (Fidelio hotel, BIZ restaurant). -* Intoarce codul curatat daca este valid, altfel SIR GOL, ca sa nu dea erori la D394/D406. +* Clasifica partenerul importat (Fidelio hotel, BIZ restaurant) in persoana fizica / juridica +* si curata DOAR CNP-urile invalide. * -* ATENTIE: algoritmul de validare (CNP si CUI) este DOAR pentru Romania. Partenerii straini -* au coduri care nu respecta regulile romanesti (BE0325777171, DICA9712015/99KS, NN9J17714), -* deci NU se valideaza si NU se sterg: se pastreaza asa cum vin, ca persoana juridica. +* De ce asa: +* - la declaratii (D394/D406) o PERSOANA FIZICA poate fi raportata FARA CNP, deci un CNP +* invalid se poate goli fara sa pierdem nimic si fara sa dea eroare de validare; +* - o PERSOANA JURIDICA trebuie sa aiba cod fiscal, deci codul ei NU se sterge niciodata: +* daca e gresit, ramane asa ca sa fie semnalat la validarea declaratiei si sa fie corectat +* la sursa (in Fidelio), nu ascuns aici. * -* Cod considerat ROMANESC: -* - numai cifre (eventual cu spatii, puncte, liniute), SAU -* - prefix 'RO' / 'CUI' / 'CF' urmat de cifre -* Pentru codurile romanesti: -* - 13 cifre => CNP, validat cu cifra de control (cValidCNP); invalid => '' -* - 2-10 cifre => CUI persoana juridica, validat cu VerifCF; invalid => '' -* - alta lungime (12, 14, 15 cifre etc.) => invalid => '' -* Pentru orice alt cod cu litere (partener strain, pasaport) => se intoarce codul curatat de -* spatii duble, fara validare. +* De ce NU ne putem baza pe validarea romaneasca ca sa decidem tipul: +* codurile straine pot fi si numai cifre (fara prefix de tara), deci un cod numeric care nu +* trece algoritmul romanesc NU inseamna automat "gresit". Tipul se decide din alte semnale, +* in ordinea increderii: +* 1. tcTipCunoscut - tip venit din import, daca sistemul sursa il stie (cel mai sigur) +* 2. prefix de tara pe cod (RO/BE/DE...) sau litere in cod => persoana juridica +* 3. denumire cu forma juridica (SRL, SA, PFA, ASOCIATIA...) => persoana juridica +* 4. numar de registrul comertului in tcRegCom (J40/1234/2018, L2019...) => juridica +* serie+numar de CI (2 litere + 6 cifre) => fizica +* 5. CNP valid pe 13 cifre => fizica; CUI romanesc valid => juridica +* 6. altfel => fizica (majoritatea covarsitoare a clientilor de hotel) * * Parametri: -* tcCod - codul brut din import -* tnTipPersoana - optional, prin referinta: 1 = juridica, 2 = fizica, 0 = necunoscut -* tcCodTara - optional, codul de tara al partenerului ('RO', 'BE', ...). Daca este -* completat si nu este 'RO', codul NU se valideaza cu regulile romanesti. +* tcCod - codul brut din import (CODFISCAL_CNP) +* tnTipPersoana - IESIRE prin referinta: 1 = juridica, 2 = fizica +* tcDenumire - optional, denumirea partenerului (pentru forma juridica) +* tcRegCom - optional, REGCOMERT_CI_PASS (nr. registrul comertului sau serie CI) +* tcCodTara - optional, codul/denumirea tarii daca importul o stie +* tcTipCunoscut - optional, tipul deja stiut din import: '1'/'PJ' sau '2'/'PF' +* +* Intoarce: codul de pastrat in ROA. Sir GOL doar cand este persoana fizica cu CNP invalid. FUNCTION GetCodFiscalValid - LPARAMETERS tcCod, tnTipPersoana, tcCodTara + LPARAMETERS tcCod, tnTipPersoana, tcDenumire, tcRegCom, tcCodTara, tcTipCunoscut - LOCAL lcCod, lcCifre, lcPrefix, lcTara, llRO, llRomanesc, lnParam + LOCAL lcCod, lcBrut, lcCifre, lcCifreReg, lcPrefix, lcPrefixBrut, lcTara, lcTip + LOCAL llLitere, llPrefixFiscal, lnParam lnParam = PCOUNT() - IF m.lnParam > 1 - tnTipPersoana = 0 - ENDIF - lcCod = ALLTRIM(UPPER(TRANSFORM(NVL(m.tcCod, '')))) - lcCod = ALLTRIM(CHRTRAN(m.lcCod, CHR(9) + CHR(13) + CHR(10), ' ')) - IF EMPTY(m.lcCod) - RETURN '' - ENDIF - - lcTara = IIF(m.lnParam > 2 AND VARTYPE(m.tcCodTara) = 'C', ALLTRIM(UPPER(m.tcCodTara)), '') - - * partea numerica: GetNrFromString taie prefixul de litere/separatori ('RO 15613488' -> '15613488') - lcCifre = ALLTRIM(GetNrFromString(m.lcCod)) - lcCifre = CHRTRAN(m.lcCifre, CHRTRAN(m.lcCifre, '0123456789', ''), '') - - * prefixul de litere ramas in fata cifrelor (RO, CUI, CF, BE, DICA...) + lcCod = GetCodFiscalCurat(m.tcCod) + lcCifre = CHRTRAN(m.lcCod, CHRTRAN(m.lcCod, '0123456789', ''), '') lcPrefix = ALLTRIM(CHRTRAN(m.lcCod, CHRTRAN(m.lcCod, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', ''), '')) - llRO = INLIST(m.lcPrefix, 'RO', 'CUI', 'CF', 'CIF') + llLitere = !EMPTY(m.lcPrefix) - * romanesc = fara litere deloc, sau cu prefix romanesc, si fara tara straina declarata - llRomanesc = (EMPTY(m.lcPrefix) OR m.llRO) AND ; - (EMPTY(m.lcTara) OR m.lcTara == 'RO' OR m.lcTara == 'ROMANIA') + * Eticheta din codul BRUT ('CUI 14770212') e semnal de persoana juridica, chiar daca + * GetCodFiscalCurat o scoate din codul salvat. 'RO' se pastreaza in cod pentru ca inseamna + * platitor de TVA, iar in ROA acela e alt partener decat neplatitorul cu acelasi numar. + lcBrut = ALLTRIM(UPPER(TRANSFORM(NVL(m.tcCod, '')))) + lcPrefixBrut = ALLTRIM(CHRTRAN(m.lcBrut, CHRTRAN(m.lcBrut, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', ''), '')) + llPrefixFiscal = INLIST(m.lcPrefixBrut, 'RO', 'CUI', 'CF', 'CIF') - IF !m.llRomanesc - * partener strain: nu pot valida cu algoritmul romanesc, pastrez codul asa cum vine - IF m.lnParam > 1 - tnTipPersoana = 1 - ENDIF - RETURN ALLTRIM(STRTRAN(m.lcCod, ' ', ' ')) - ENDIF - - IF EMPTY(m.lcCifre) - RETURN '' - ENDIF + lcTara = IIF(m.lnParam > 4 AND VARTYPE(m.tcCodTara) = 'C', ALLTRIM(UPPER(m.tcCodTara)), '') + lcTip = IIF(m.lnParam > 5 AND VARTYPE(m.tcTipCunoscut) = 'C', ALLTRIM(UPPER(m.tcTipCunoscut)), '') DO CASE - CASE LEN(m.lcCifre) = 13 AND !m.llRO - IF cValidCNP(m.lcCifre) - IF m.lnParam > 1 - tnTipPersoana = 2 - ENDIF - RETURN m.lcCifre - ENDIF - RETURN '' - CASE BETWEEN(LEN(m.lcCifre), 2, 10) - IF VerifCF(m.lcCifre) - IF m.lnParam > 1 - tnTipPersoana = 1 - ENDIF - RETURN IIF(m.llRO, 'RO' + m.lcCifre, m.lcCifre) - ENDIF - RETURN '' + CASE INLIST(m.lcTip, '1', 'PJ', 'J') + tnTipPersoana = 1 + CASE INLIST(m.lcTip, '2', 'PF', 'F') + tnTipPersoana = 2 + CASE m.llPrefixFiscal + * eticheta romaneasca de cod fiscal (RO/CUI/CF/CIF) => persoana juridica + tnTipPersoana = 1 + CASE !EMPTY(m.lcTara) AND !INLIST(m.lcTara, 'RO', 'ROU', 'ROMANIA') + * tara straina declarata: nu putem valida romaneste, tratam ca juridica + tnTipPersoana = 1 + CASE EstePersoanaJuridica(m.tcDenumire) + tnTipPersoana = 1 + CASE EsteNrRegComert(m.tcRegCom) + tnTipPersoana = 1 + CASE LEN(m.lcCifre) = 13 AND cValidCNP(m.lcCifre) + tnTipPersoana = 2 + CASE EsteSerieCI(m.tcRegCom) + tnTipPersoana = 2 + CASE m.llLitere + * cod cu litere fara alt semnal: TVA intracomunitar (BE0325777171) daca incepe cu + * un cod de tara UE, altfel e cel mai probabil pasaport de persoana fizica + tnTipPersoana = IIF(EsteCodTVAStrain(m.lcCod), 1, 2) + CASE BETWEEN(LEN(m.lcCifre), 2, 10) AND VerifCF(m.lcCifre) + tnTipPersoana = 1 OTHERWISE - RETURN '' + * clientii de hotel sunt in marea lor majoritate persoane fizice + tnTipPersoana = 2 ENDCASE + + IF m.tnTipPersoana = 1 + * persoana juridica: codul NU se sterge niciodata, chiar daca nu trece validarea. + * Vrem sa fim atentionati la declaratie, nu sa ascundem problema. + RETURN m.lcCod + ENDIF + + * persoana fizica: pastram CNP-ul doar daca este valid, altfel gol (permis la declaratii). + * Pasapoartele si seriile de CI nu sunt CNP-uri, deci nu se salveaza pe cod fiscal. + IF LEN(m.lcCifre) = 13 AND cValidCNP(m.lcCifre) + RETURN m.lcCifre + ENDIF + + * recuperare: in Fidelio se intampla ca operatorul sa inverseze campurile si sa scrie CNP-ul + * in 'serie CI / pasaport'. Daca acolo gasim un CNP valid, il folosim pe acela. + lcCifreReg = GetCodFiscalCurat(m.tcRegCom) + lcCifreReg = CHRTRAN(m.lcCifreReg, CHRTRAN(m.lcCifreReg, '0123456789', ''), '') + IF LEN(m.lcCifreReg) = 13 AND cValidCNP(m.lcCifreReg) + RETURN m.lcCifreReg + ENDIF + + RETURN '' +ENDFUNC +* +* .T. daca sirul arata a cod de TVA intracomunitar: 2 litere = cod de tara UE, urmate de cifre. +* Ex: 'BE0325777171', 'DE811234567', 'HU12345678'. NU include 'RO' (tratat separat). +FUNCTION EsteCodTVAStrain + LPARAMETERS tcCod + + LOCAL lcCod, lcTari, lcTara, lcRest + lcCod = ALLTRIM(UPPER(TRANSFORM(NVL(m.tcCod, '')))) + lcCod = CHRTRAN(m.lcCod, ' .-', '') + IF LEN(m.lcCod) < 6 + RETURN .F. + ENDIF + + * coduri de tara UE + cateva uzuale in turism + lcTari = ' AT BE BG CY CZ DE DK EE EL ES FI FR GR HR HU IE IT LT LU LV MT NL PL PT SE SI SK ' + ; + ' GB CH NO MD UA RS TR US ' + + lcTara = LEFT(m.lcCod, 2) + IF !(' ' + m.lcTara + ' ' $ m.lcTari) + RETURN .F. + ENDIF + + * dupa codul de tara trebuie sa urmeze numai cifre + lcRest = SUBSTR(m.lcCod, 3) + RETURN LEN(CHRTRAN(m.lcRest, CHRTRAN(m.lcRest, '0123456789', ''), '')) = LEN(m.lcRest) +ENDFUNC +* +* Curata un cod fiscal / CNP de spatii, puncte si separatori redundanti, pastrand literele +* semnificative. +* 'RO 23565004' -> 'RO23565004' +* 'CUI 14770212' -> '14770212' +* '2920121295914.' -> '2920121295914' +* +* ATENTIE: prefixul 'RO' NU se adauga niciodata de la noi. In ROA, 'RO12345678' (platitor de +* TVA) si '12345678' (neplatitor) sunt DOI parteneri diferiti, deci prefixul se pastreaza +* exact cum a venit din import. Etichetele 'CUI'/'CF'/'CIF' sunt doar text descriptiv, nu +* atribut fiscal, deci se sterg fara sa fie inlocuite cu 'RO'. +FUNCTION GetCodFiscalCurat + LPARAMETERS tcCod + + LOCAL lcCod, lcCifre, lcPrefix + lcCod = ALLTRIM(UPPER(TRANSFORM(NVL(m.tcCod, '')))) + lcCod = CHRTRAN(m.lcCod, CHR(9) + CHR(13) + CHR(10), ' ') + * scot spatiile si punctele; liniuta si slash-ul pot face parte din coduri straine + lcCod = ALLTRIM(CHRTRAN(m.lcCod, ' .', '')) + + * 'CUI 14770212' -> '14770212': eticheta nu spune nimic despre calitatea de platitor de TVA + lcPrefix = ALLTRIM(CHRTRAN(m.lcCod, CHRTRAN(m.lcCod, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', ''), '')) + IF INLIST(m.lcPrefix, 'CUI', 'CF', 'CIF') + lcCifre = CHRTRAN(m.lcCod, CHRTRAN(m.lcCod, '0123456789', ''), '') + IF !EMPTY(m.lcCifre) + lcCod = m.lcCifre + ENDIF + ENDIF + + RETURN m.lcCod +ENDFUNC +* +* .T. daca denumirea contine o forma juridica (SRL, SA, PFA, ASOCIATIA, LTD, GMBH...). +* Compara pe cuvinte intregi, ca sa nu ia 'SA' din 'SAVU' sau 'II' din 'MIHAII'. +FUNCTION EstePersoanaJuridica + LPARAMETERS tcDenumire + + LOCAL lcDen, lcForme, lcCuv, lnI, lnCuvinte + lcDen = ALLTRIM(UPPER(TRANSFORM(NVL(m.tcDenumire, '')))) + IF EMPTY(m.lcDen) + RETURN .F. + ENDIF + + * separatorii devin spatii, ca sa pot compara cuvant cu cuvant + lcDen = CHRTRAN(m.lcDen, '.,-/()"' + CHR(9), ' ') + + lcForme = ' SRL SA SC SNC SCS SCA PFA II IF SRLD RA RL ' + ; + ' ASOCIATIA ASOCIATIE FUNDATIA FUNDATIE SOCIETATEA COOPERATIVA CABINET ' + ; + ' PRIMARIA MINISTERUL AGENTIA INSTITUTUL SPITALUL SCOALA LICEUL COLEGIUL ' + ; + ' UNIVERSITATEA UNIVERSITE UNIVERSITY UNIVERSITAT INSPECTORATUL DIRECTIA ' + ; + ' LTD LIMITED GMBH AG BV NV INC LLC PLC CORP CORPORATION COMPANY HOLDING KFT SPZOO OOD ' + + lnCuvinte = GETWORDCOUNT(m.lcDen, ' ') + FOR lnI = 1 TO m.lnCuvinte + lcCuv = ALLTRIM(GETWORDNUM(m.lcDen, m.lnI, ' ')) + IF !EMPTY(m.lcCuv) AND (' ' + m.lcCuv + ' ') $ m.lcForme + RETURN .T. + ENDIF + ENDFOR + + RETURN .F. +ENDFUNC +* +* .T. daca sirul arata a numar de inregistrare la Registrul Comertului. +* Forme acceptate: 'J40/1234/2018', 'F13/45/2005', 'C23/9/2019', 'L2019000159348', '54/1997' +FUNCTION EsteNrRegComert + LPARAMETERS tcRegCom + + LOCAL lcReg, lcRest + lcReg = ALLTRIM(UPPER(TRANSFORM(NVL(m.tcRegCom, '')))) + lcReg = CHRTRAN(m.lcReg, ' ', '') + IF EMPTY(m.lcReg) + RETURN .F. + ENDIF + + * J40/1234/2018 - litera + cifre + doua slash-uri + IF INLIST(LEFT(m.lcReg, 1), 'J', 'F', 'C') AND OCCURS('/', m.lcReg) = 2 AND ; + ISDIGIT(SUBSTR(m.lcReg, 2, 1)) + RETURN .T. + ENDIF + + * L2019000159348 / J2018000211289 - litera urmata de minim 10 cifre + IF INLIST(LEFT(m.lcReg, 1), 'J', 'L') AND LEN(m.lcReg) >= 11 + lcRest = SUBSTR(m.lcReg, 2) + IF ISDIGIT(m.lcRest) AND LEN(CHRTRAN(m.lcRest, CHRTRAN(m.lcRest, '0123456789', ''), '')) = LEN(m.lcRest) + RETURN .T. + ENDIF + ENDIF + + * 54/1997 - numar/an, tipic institutiilor publice + IF OCCURS('/', m.lcReg) = 1 AND LEN(m.lcReg) <= 8 AND ; + ISDIGIT(m.lcReg) AND LEN(GETWORDNUM(m.lcReg, 2, '/')) = 4 + RETURN .T. + ENDIF + + RETURN .F. +ENDFUNC +* +* .T. daca sirul arata a serie + numar de carte de identitate: 2 litere urmate de 6 cifre. +* Ex: 'ZV 093375', 'IF834409', 'RK 340434' +FUNCTION EsteSerieCI + LPARAMETERS tcRegCom + + LOCAL lcReg + lcReg = ALLTRIM(UPPER(TRANSFORM(NVL(m.tcRegCom, '')))) + lcReg = CHRTRAN(m.lcReg, ' -.', '') + IF LEN(m.lcReg) <> 8 + RETURN .F. + ENDIF + + RETURN ISALPHA(LEFT(m.lcReg, 1)) AND ISALPHA(SUBSTR(m.lcReg, 2, 1)) AND ; + ISDIGIT(SUBSTR(m.lcReg, 3, 1)) AND ; + LEN(CHRTRAN(SUBSTR(m.lcReg, 3), CHRTRAN(SUBSTR(m.lcReg, 3), '0123456789', ''), '')) = 6 ENDFUNC *