Import parteneri: se curata doar CNP-urile invalide, codul persoanelor juridice ramane neatins

This commit is contained in:
2026-08-02 10:29:34 +03:00
parent 349130c369
commit da19090ed9
10 changed files with 754 additions and 139 deletions

1
.gitignore vendored
View File

@@ -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/

91
ALTE/check_semnale_pj.ps1 Normal file
View File

@@ -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)<CUMPARATOR>([^<]*)</CUMPARATOR>.*?<CODFISCAL_CNP>([^<]*)</CODFISCAL_CNP>\s*<REGCOMERT_CI_PASS>([^<]*)</REGCOMERT_CI_PASS>')) {
$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

49
ALTE/check_swap_tara.ps1 Normal file
View File

@@ -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)<CUMPARATOR>([^<]*)</CUMPARATOR>.*?<CODFISCAL_CNP>([^<]*)</CODFISCAL_CNP>\s*<REGCOMERT_CI_PASS>([^<]*)</REGCOMERT_CI_PASS>')) {
$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)<ROW>.*?</ROW>')
''
'=== campurile de tara existente in XML (primul rand) ==='
[regex]::Matches($m.Value, '(?i)<(\w*TARA\w*)>([^<]*)</\1>') | 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)<COMPANIE_TARA>([^<]*)</COMPANIE_TARA>')) {
$total++
if ($mm.Groups[1].Value.Trim() -ne '') { $cuTara++ }
}
}
"COMPANIE_TARA completat pe $cuTara din $total randuri"

View File

@@ -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

View File

@@ -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*" } }

View File

@@ -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'

View File

@@ -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

View File

@@ -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`.

View File

@@ -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"

View File

@@ -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
*