Compare commits

..

8 Commits

Author SHA1 Message Date
de23611b4d Import parteneri: cautare pe codul salvat si preluarea partenerului cel mai vechi
Importul cauta partenerul dupa codul brut din XML, dar il salva pe cel intors de
GetCodFiscalValid, care la persoana fizica poate lua CNP-ul din campul de serie CI /
pasaport. Cand cele doua difera, partenerul nu era gasit niciodata si pack_def.adauga_partener
respingea inserarea ("Mai exista un partener cu acest CNP"), oprind tot importul.

- cautarea se face acum pe codul cu care partenerul chiar se salveaza, cu revenire pe codul
  brut si pe cheia externa a importului;
- GetIdPartener() intoarce partenerul cel mai vechi cand mai multi au acelasi cod fiscal;
- GetIdPartenerROA() preia partenerul existent din ROA dupa refuzul procedurii, ca importul
  sa continue; se opreste doar daca nici acolo nu exista;
- lcCod nu mai e suprascris cu codul fiscal, deci in coresp_parteneri se scrie iar cheia
  externa, nu codul fiscal.

Verificat pe baza reala cu Teste\test_parteneri_roa.prg (doar SELECT, in afara suitei
implicite); suita implicita ramane PASS.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PTZNiJg2pZk1rky1rpw94u
2026-08-18 13:34:53 +03:00
36b116c00d gitignore 2026-08-02 11:11:03 +03:00
8c4a14deb1 Sterg import.prg, fisier gol nefolosit 2026-08-02 11:06:13 +03:00
8857e8fedf CLAUDE.md: ghid de proiect pentru agenti, cu trimitere la regulile comune ROA 2026-08-02 10:55:06 +03:00
ce9ec394df Changelog 2.0.6 concis si actualizare fisier de versiune 2026-08-02 10:36:36 +03:00
5249670062 Changelog 2.0.6: curatare CNP-uri invalide si clasificare parteneri la import 2026-08-02 10:31:57 +03:00
da19090ed9 Import parteneri: se curata doar CNP-urile invalide, codul persoanelor juridice ramane neatins 2026-08-02 10:29:34 +03:00
349130c369 Import parteneri: coduri fiscale/CNP invalide se salveaza gol, partenerii straini nu se valideaza romaneste 2026-08-02 10:02:31 +03:00
17 changed files with 1242 additions and 69 deletions

2
.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/
@@ -102,3 +103,4 @@ _textcache/
docs/diff_*.patch
docs/local/
.gstack/
flora2roa.zip

12
ALTE/check_cf_litere.ps1 Normal file
View File

@@ -0,0 +1,12 @@
# Ce coduri fiscale NENUMERICE apar in XML-urile Fidelio (posibili parteneri straini)
param([string]$Dir = "D:\PROIECTE\FLORA\FLORA2ROA\EXEMPLE\hotel")
$map = @{}
Get-ChildItem -Path $Dir -Filter 'bts_detalii_facturi_rep_*.xml' | ForEach-Object {
$t = Get-Content $_.FullName -Raw
foreach ($m in [regex]::Matches($t, '(?i)<CODFISCAL_CNP>([^<]*)</CODFISCAL_CNP>')) {
$v = $m.Groups[1].Value.Trim()
if ($v -ne '' -and $v -match '[A-Za-z]') { $map[$v] = 1 }
}
}
"coduri cu litere = $($map.Count)"
$map.Keys | Sort-Object

50
ALTE/check_cnp.ps1 Normal file
View File

@@ -0,0 +1,50 @@
# Simulare a regulii GetCodFiscalValid pe TOATE codurile din XML-urile Fidelio.
# Raporteaza cate coduri se pastreaza, cate se sterg si cate sunt straine (nevalidate).
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]))
}
$map = @{}
Get-ChildItem -Path $Dir -Filter 'bts_detalii_facturi_rep_*.xml' | ForEach-Object {
$t = Get-Content $_.FullName -Raw
foreach ($m in [regex]::Matches($t, '(?i)<CODFISCAL_CNP>([^<]*)</CODFISCAL_CNP>')) {
$v = $m.Groups[1].Value.Trim().ToUpper()
if ($v -ne '') { $map[$v] = 1 }
}
}
$pastratCNP = 0; $pastratCUI = 0; $strain = 0; $sters = 0
$listaStersi = @()
foreach ($cod in $map.Keys) {
$prefix = ($cod -replace '[^A-Z]', '')
$roPrefix = @('RO', 'CUI', 'CF', 'CIF') -contains $prefix
$romanesc = ($prefix -eq '' -or $roPrefix)
if (-not $romanesc) { $strain++; continue }
$cifre = ($cod -replace '[^\d]', '')
if ($cifre.Length -eq 13 -and -not $roPrefix) {
if (Test-CNP $cifre) { $pastratCNP++ } else { $sters++; $listaStersi += $cod }
} elseif ($cifre.Length -ge 2 -and $cifre.Length -le 10) {
if (Test-CUI $cifre) { $pastratCUI++ } else { $sters++; $listaStersi += $cod }
} else { $sters++; $listaStersi += $cod }
}
"total coduri distincte : $($map.Count)"
"CNP valide pastrate : $pastratCNP"
"CUI valide pastrate : $pastratCUI"
"straine (nevalidate) : $strain"
"STERSE (invalide) : $sters"
'--- primele 30 sterse ---'
$listaStersi | Select-Object -First 30

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"

113
CLAUDE.md Normal file
View File

@@ -0,0 +1,113 @@
# CLAUDE.md
Ghid pentru agenti (Claude Code / Jcode) care lucreaza in acest repo.
## Ce este
FLORA2ROA e o aplicatie Visual FoxPro 9 care importa facturile si vanzarile din **Fidelio**
(hotel, XML) si **BIZ** (restaurant, XML) si genereaza note contabile in **ROA** (Oracle).
Un singur formular, `frm_import.scx`, face tot fluxul: citeste XML-urile din interval,
completeaza nomenclatorul de parteneri, construieste notele si le scrie in ROA.
Radacina: `D:\PROIECTE\FLORA\FLORA2ROA`. Nu are folder `COMUN\` (doar `comun.h` local), deci
scripturile partajate ROA au echivalente locale in `Teste\`.
## Reguli de lucru: OBLIGATORIU inainte de orice modificare
**Citeste `D:\ROA\ROACONT\COMUN\docs\reguli_lucru.md`.** Nu e context optional, e contractul de
livrare. Ce se incalca cel mai usor daca nu-l citesti:
- **Nu se comite fara aprobare.** Livrezi diff ca FISIER (`docs\diff_runda<N>_<subiect>.patch`),
iar write-back-ul in binar si commit-ul se fac dupa ce utilizatorul aproba. Patch-urile de
review nu se comit niciodata (sunt in `.gitignore`).
- **Comentarii minime, fara jurnal.** O linie, descriind comportamentul CURENT. Interzis inline:
istoricul modificarii ("inlocuieste...", "nu mai depinde de..."), nume de agent, referinte la
runde/decizii. Istoricul sta doar in antetul fisierului, cumulativ, semnat de om.
- **Modificari minime**: doar ce s-a cerut, fara refactorizari din oficiu.
- Punctul 7 din acel fisier e un index de conventii per zona atinsa (encoding cp1252, UX
formulare, `GO` pe `Recno()`, `goExecutor` + `ALTER TABLE`, testare UI, export Oracle).
## Documentatia proprie a proiectului
- `docs\depanare_testare_flora2roa.md` - harnessul de testare headless, ce contine `Teste\`,
comenzile uzuale si capcanele confirmate aici. **De citit inainte de a rula sau scrie teste.**
- `docs\flux_git_flora2roa.md` - fluxul git + SVN, ce urmareste fiecare, `roa_sync.bat`.
## Versionare
**SVN** e sursa de adevar, inclusiv pentru binarele VFP. **git** urmareste versiunile TEXT
(`.sc2`, `.pj2`) generate de FoxBin2Prg in arbore, plus `.prg`, `Teste\`, `docs\`. Binarele sunt
git-ignored, ca diff-urile sa fie lizibile. La inceput de sesiune: `roa_sync.bat`.
`svn commit` (care atinge binarele) se face **doar cu acordul explicit al utilizatorului**.
## Editarea formularului
`frm_import.scx` se editeaza pe text, byte-safe in Windows-1252:
```powershell
powershell -ExecutionPolicy Bypass -File Teste\text_din_bin.ps1 # binar -> frm_import.sc2
# editezi frm_import.sc2 cu [IO.File]::ReadAllText/WriteAllText + GetEncoding(1252)
powershell -ExecutionPolicy Bypass -File Teste\bin_din_text.ps1 -TextFile frm_import.sc2
powershell -ExecutionPolicy Bypass -File Teste\recompileaza_binare.ps1
```
Ultimul pas nu e optional: write-back-ul reface `.scx/.sct` fara cod obiect, deci formularul
ruleaza fara `#INCLUDE "comun.h"` si constantele (`CT_SUCCES`) crapa in RUNTIME, desi
verificarea de sintaxa trece.
## Testare
Nu exista IDE in flux. Totul headless, din PowerShell:
```powershell
powershell -ExecutionPolicy Bypass -File Teste\ruleaza_teste.ps1 # suita + sinteza
powershell -ExecutionPolicy Bypass -File Teste\verifica_sintaxa.ps1 # dupa orice editare .prg
```
Un `.fxp` vechi ascunde modificarile din `.prg`: daca un test da acelasi rezultat dupa o
corectie evidenta, sterge `.fxp`-ul si reia.
## Cod
- Punct de intrare `main.prg`; `myutils.prg` = biblioteca de functii utilitare; `capp/app/myapp.prg`
= obiectele globale (`goApp`, `goConn`, `goExecutor`, `goLog`).
- Constantele din `comun.h` (`CT_SUCCES = 1`, `CT_INSUCCES`) sunt conventia de retur.
- Cod si mesaje in **romana, fara diacritice** (fisierele sunt cp1252).
- Functiile utilitare partajate se copiaza din `D:\ROA\ROACONT\COMUN\programe\oproceduri_comune.prg`,
nu se rescriu.
## Reguli de domeniu (contabil) de stiut
- In ROA, `RO12345678` (platitor de TVA) si `12345678` sunt **doi parteneri diferiti**: prefixul
`RO` se pastreaza exact cum vine din import si nu se adauga niciodata automat.
- La D394/D406 o **persoana fizica** poate fi raportata fara CNP, deci un CNP invalid se goleste.
O **persoana juridica** trebuie sa aiba cod fiscal: codul ei nu se sterge niciodata, ca eroarea
sa fie semnalata la validarea declaratiei si corectata la sursa, in Fidelio.
- Algoritmul romanesc de validare (CNP/CUI) nu se aplica partenerilor straini; codurile lor pot fi
si numai din cifre, de orice lungime.
## Changelog
`changelog_flora2roa.txt`, blocuri de comentariu HTML, cel mai nou primul:
```
<!--
DD/MM/YYYY
FLORA2ROA - X.Y.Z
:modificare:
Ce vede utilizatorul.
-->
```
Taguri: `:nou:`, `:modificare:`, `:eroare:`. Text **pentru utilizator**, scurt, netehnic, o fraza
per idee. Erorile introduse si reparate in aceeasi versiune nelivrata nu se trec: `:eroare:` e
doar pentru ce a fost in productie. La bump de versiune se actualizeaza si `flora2roa.txt`
(fisierul pe care `update.bat` il compara cu cel de pe server).
## Stil de raspuns
Scurt si concret. Ce e gata (fisier:linie), ce urmeaza, ce trebuie decis. Fara naratiunea
drumului pana la rezultat, fara reluari, fara justificari necerute. Detaliile lungi (dovezi,
iesiri de test) se scriu in fisier si se trimite link.

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,6 +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_real.prg'; Steps = @('analiza') }
)
if ($Doar) { $suita = $suita | Where-Object { $_.Prg -like "*$Doar*" } }

211
Teste/test_cod_fiscal.prg Normal file
View File

@@ -0,0 +1,211 @@
* 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"
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_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 lnTip, lcRez
*==============================================================
* 1. PERSOANE FIZICE - singurele la care se sterge codul
*==============================================================
lnTip = 0
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')
TestAssertEqual('1800101410013', GetCodFiscalValid(' 1800101410013. ', @lnTip, 'POPESCU ION', ''), ;
'PF: CNP valid curatat de spatii si punct')
* CNP-uri invalide reale din Fidelio: codul se goleste
lnTip = 0
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('', 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, '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 romanesc GRESIT la o persoana juridica: NU se sterge, ca sa dea eroare la declaratie
lnTip = 0
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')
* partener strain cu cod alfanumeric
lnTip = 0
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('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'
HarnessLog('REZULTAT: ' + TestRezultat())
DO HarnessDone WITH TestRezultat()
QUIT

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

@@ -0,0 +1,87 @@
* test_parteneri_roa.prg - cautarea partenerului la import, pe datele reale din ROA.
*
* Reguli verificate:
* - cautarea se face pe codul cu care partenerul se salveaza (GetCodFiscalValid), nu pe cel brut
* - cand mai multi parteneri au acelasi cod fiscal, se ia cel mai vechi (id_part minim)
*
* Testul e READ ONLY (doar SELECT) si NU face parte din suita implicita: cere Oracle.
* vfp9.exe -A -T "D:\PROIECTE\FLORA\FLORA2ROA\Teste\test_parteneri_roa.prg"
#INCLUDE "comun.h"
SET SAFETY OFF
SET TALK OFF
PUBLIC gcUILog, gcSyncDir, gnTestFail, gnMockRaspuns, gcMockUltimMesaj, gnMockUltimTip
gnTestFail = 0
gnMockRaspuns = 6
gcMockUltimMesaj = ''
gnMockUltimTip = 0
LOCAL lcTestDir, lcSqlPart, lnSucces, lnTip, lcCodSalvat, lnIdVechi, lnIdGasit
lcTestDir = ADDBS(JUSTPATH(SYS(16, 0)))
gcUILog = lcTestDir + 'test_parteneri_roa_log.txt'
gcSyncDir = lcTestDir + 'uisync\'
IF !DIRECTORY(gcSyncDir)
MD (gcSyncDir)
ENDIF
STRTOFILE('START ' + TTOC(DATETIME()) + CHR(13) + CHR(10), gcUILog)
SQLSETPROP(0, 'DispLogin', 3)
DO (lcTestDir + 'test_init_env_auto.prg') WITH .T.
SET PROCEDURE TO (lcTestDir + 'ui_harness.prg') ADDITIVE
SET PROCEDURE TO (lcTestDir + 'mock_amessagebox.prg') ADDITIVE
DO HarnessInit
IF TYPE('gnHandle') <> 'N' OR gnHandle <= 0
HarnessLog('Fara conexiune la ROA (' + ALLTRIM(goApp.oSettings.host) + '), testul iese controlat.')
DO HarnessDone WITH 'SKIP'
QUIT
ENDIF
DO FORM (gcAppPath + 'frm_import') NAME goImportFrm NOSHOW
goImportFrm.WindowType = 0
* un CNP care in ROA e pe mai multi parteneri: se ia cel mai vechi
LOCAL lcCnpDublat
lcCnpDublat = '1951113297313'
lnSucces = goExecutor.oExecute(GetHash("cSql=>select min(id_part) as id_part, count(*) as nr from nom_parteneri " + ;
"where replace(upper(cod_fiscal), ' ', '') = '" + m.lcCnpDublat + "' and sters = 0??cCursor=>cAsteptat"))
TestAssertEqual(CT_SUCCES, m.lnSucces, 'interogare de control executata')
lnIdVechi = NVL(cAsteptat.id_part, 0)
HarnessLog('CNP ' + m.lcCnpDublat + ': ' + TRANSFORM(cAsteptat.nr) + ' parteneri, cel mai vechi ' + TRANSFORM(m.lnIdVechi))
TestAssert(m.lnIdVechi > 0, 'CNP-ul de test exista in ROA')
* cursorul de cautare, ca in CompleteazaParteneriROA
CREATE CURSOR cParteneri (id_part N(10), cod_fiscal C(30) null, denumire C(100) null)
lcSqlPart = [select id_part, cod_fiscal, denumire from nom_parteneri where sters = 0 and inactiv = 0]
lnSucces = goExecutor.oExecute(GetHash("cSql=>" + m.lcSqlPart + '??cCursor=>cParteneriTemp'))
TestAssertEqual(CT_SUCCES, m.lnSucces, 'parteneri ROA cititi')
SELECT cParteneri
APPEND FROM DBF('cParteneriTemp')
INDEX on denumire TAG denumire
INDEX on PADR(STRTRAN(cod_fiscal, ' ', ''),30, ' ') TAG cod_fiscal
USE IN (SELECT('cParteneriTemp'))
* randul din Fidelio cu campurile inversate: CNP in 'serie CI / pasaport'
lnTip = 0
lcCodSalvat = GetCodFiscalValid('PX920016', @lnTip, 'STRATON ADRIAN FLORIN', m.lcCnpDublat, '')
TestAssertEqual(m.lcCnpDublat, m.lcCodSalvat, 'CNP-ul din serie CI este codul care se salveaza')
lnIdGasit = goImportFrm.GetIdPartener(PADR('PX920016', 30, ' '), PADR('STRATON ADRIAN FLORIN', 100, ' '))
TestAssertEqual(0, m.lnIdGasit, 'codul brut din import nu gaseste partenerul')
lnIdGasit = goImportFrm.GetIdPartener(PADR(m.lcCodSalvat, 30, ' '), PADR('STRATON ADRIAN FLORIN', 100, ' '))
TestAssertEqual(m.lnIdVechi, m.lnIdGasit, 'codul salvat gaseste partenerul cel mai vechi')
lnIdGasit = goImportFrm.GetIdPartenerROA(m.lcCodSalvat)
TestAssertEqual(m.lnIdVechi, m.lnIdGasit, 'cautarea in ROA da tot partenerul cel mai vechi')
TestAssertEqual(0, goImportFrm.GetIdPartenerROA(''), 'cod fiscal gol: nu se cauta in ROA')
goImportFrm.Release()
goConn.Disconnect()
HarnessLog('REZULTAT: ' + TestRezultat())
DO HarnessDone WITH TestRezultat()
QUIT

View File

@@ -1,3 +1,24 @@
<!--
18/08/2026
FLORA2ROA - 2.0.7
:eroare:
Importul nu se mai opreste cand partenerul exista deja in ROA cu acelasi cod fiscal sau CNP:
este folosit partenerul cel mai vechi cu acel cod.
Partenerii al caror CNP a fost scris in campul de serie CI sunt acum regasiti corect, fara sa mai fie adaugati inca o data.
-->
<!--
02/08/2026
FLORA2ROA - 2.0.6
:modificare:
Import parteneri. Persoanele fizice cu CNP invalid se salveaza fara CNP, ca sa nu dea eroare la D394/D406.
Codul persoanelor juridice nu se mai sterge, ca sa fie semnalat la validarea declaratiei si corectat in Fidelio.
Daca CNP-ul a fost scris din greseala in campul de serie CI, este preluat de acolo.
In jurnalul de import se scriu CNP-urile curatate si codurile de firma care nu trec validarea.
-->
<!--
02/07/2026
FLORA2ROA - 2.0.5

View File

@@ -31,9 +31,17 @@ 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).
- `test_parteneri_roa.prg` - cautarea partenerului la import, pe datele reale din ROA:
cautarea se face pe codul cu care partenerul se salveaza, iar cand mai multi parteneri au
acelasi cod fiscal se ia cel mai vechi. Doar SELECT-uri, NU e in suita implicita (cere Oracle).
## Comenzi uzuale
@@ -66,8 +74,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

@@ -1 +1 @@
2.0.3
2.0.7

View File

@@ -47,6 +47,8 @@ DEFINE CLASS form1 AS form
*m: disconnectmentor
*m: disconnectroa
*m: exportdate
*m: getidpartener
*m: getidpartenerroa
*m: scrienote
*m: sql_temp_insert
*m: sterge_import
@@ -73,6 +75,8 @@ DEFINE CLASS form1 AS form
<memberdata name="nhandlementor" display="nHandleMentor"/>
<memberdata name="disconnectmentor" display="DisconnectMentor"/>
<memberdata name="completeazaparteneriroa" display="CompleteazaParteneriROA"/>
<memberdata name="getidpartener" display="GetIdPartener"/>
<memberdata name="getidpartenerroa" display="GetIdPartenerROA"/>
</VFPData>
*</PropValue>
@@ -272,13 +276,13 @@ 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
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
Local lcPrincipala, lcScara, lcSqlJudete, lcSqlLocalitati, lcSqlPart, lcStrada, lcTelefon1
Local lcTelefon2, lcWeb, lnIdJudet, lnIdJudetBucuresti, lnIdLocalitateBucuresti, lnIdTaraRO, lnPos
Local lnRecc
Local lnRecc, lcCodBrut
*:Global pcDenumire, pnIdAdresa, pnNrAdrese
*:Global pcCodFiscal, pnIdPart
@@ -353,12 +357,8 @@ DEFINE CLASS form1 AS form
lnRecc = RECCOUNT()
Scan
pnIdPart = 0
pcCodFiscal = PADR(STRTRAN(cod_fiscal, ' ', ''),30, ' ')
pcDenumire = PADR(Alltrim(Upper(denumire)), 100, ' ')
lcAdresa = Strtran(Alltrim(Upper(Nvl(adresa, ''))), Chr(13), ' ')
IF LEN(ALLTRIM(m.pcCodFiscal)) <= 3
pcCodFiscal = PADL(ALLTRIM(cod), 10, '0')
ENDIF
lcCod = cod
IF MOD(RECNO(), 250) = 0
@@ -375,64 +375,62 @@ DEFINE CLASS form1 AS form
SELECT cParteneri
DO CASE
CASE !Empty(m.pcCodFiscal)
IF SEEK(m.pcCodFiscal, 'cParteneri', 'cod_fiscal')
pnIdPart = cParteneri.id_part
ENDIF
OTHERWISE
IF SEEK(m.pcDenumire, 'cParteneri', 'denumire')
pnIdPart = cParteneri.id_part
ENDIF
ENDCASE
Select cClientiFurnizori
lcDenumire = Nvl(Strtran(Alltrim(Upper(denumire)), ['], ['']), "")
lcNume = Nvl(Strtran(Alltrim(Upper(denumire)), ['], ['']), "")
lcPrenume = ''
lcCod_fiscal = Upper(Alltrim(cod_fiscal))
lcReg_comert = Nvl(Alltrim(Upper(reg_com)), "")
***************************
lcCodBrut = Alltrim(Upper(Nvl(m.lcCod_fiscal,"")))
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.lcCodBrut)
lnTip_persoana = 0
lcCodTara = Alltrim(Upper(Nvl(cod_tara, "")))
lcCod_fiscal = GetCodFiscalValid(m.lcCodBrut, @lnTip_persoana, m.lcDenumire, m.lcReg_comert, m.lcCodTara)
lcTip_persoana = Iif(m.lnTip_persoana = 1, "1", "2")
Do Case
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.lcCodBrut + '] ' + 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"
lnPos = At(' ', m.lcNume)
IF m.lnPos > 0
lcPrenume = Alltrim(Substr(m.lcNume, m.lnPos))
lcNume = Alltrim(Left(m.lcNume, m.lnPos))
ENDIF
ENDIF
***************************
* Cautarea se face pe codul cu care partenerul se salveaza in ROA, nu pe cel din import:
* GetCodFiscalValid poate lua CNP-ul din campul de serie CI / pasaport.
pcCodFiscal = PADR(STRTRAN(m.lcCod_fiscal, ' ', ''), 30, ' ')
IF LEN(ALLTRIM(m.pcCodFiscal)) <= 3
pcCodFiscal = PADR(STRTRAN(m.lcCodBrut, ' ', ''), 30, ' ')
ENDIF
IF LEN(ALLTRIM(m.pcCodFiscal)) <= 3
pcCodFiscal = PADL(ALLTRIM(cod), 10, '0')
ENDIF
pnIdPart = Thisform.GetIdPartener(m.pcCodFiscal, m.pcDenumire)
If !Empty(Nvl(m.pnIdPart, 0))
Replace id_part With m.pnIdPart In cClientiFurnizori
*!* lcMesaj = 'Client existent ' + Alltrim(cParteneri.denumire) + ' CUI: ' + Alltrim(cParteneri.cod_fiscal) + ' ID: ' + Alltrim(Transform(cParteneri.id_part))
*!* Thisform.trace(m.lcMesaj)
Else
* Adaugare clienti
Select cClientiFurnizori
lcDenumire = Nvl(Strtran(Alltrim(Upper(denumire)), ['], ['']), "")
lcNume = Nvl(Strtran(Alltrim(Upper(denumire)), ['], ['']), "")
lcPrenume = ''
lcCod_fiscal = Upper(Alltrim(cod_fiscal))
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
If !Empty(m.lcCod)
If Left(lcCod,2)="RO"
lcCod=Substr(m.lcCod,3)
lcTip_persoana="1"
ENDIF
IF ISALPHA(m.lcCod)
lcTip_persoana="1"
ENDIF
If Len(m.lcCod)=13 And Isdigit(m.lcCod)
If !cValidCNP(m.lcCod)
* daca CNP invalid, sterg CNP, sa nu dea eroare la D406
lcCod_fiscal = ''
Endif
ELSE
* PJ din ROMANIA au coduri fiscale intre 2-10 cifre
* Exista si PJ din alte tari care pot avea coduri fiscale cu alt numar de cifre, dar sunt exceptii
If Len(m.lcCod)>=2 And Len(m.lcCod)<=10 And Isdigit(m.lcCod)
lcTip_persoana="1"
Endif
Endif
ENDIF
IF m.lcTip_Persoana = "2"
lnPos = At(' ', m.lcNume)
IF m.lnPos > 0
lcPrenume = Alltrim(Substr(m.lcNume, m.lnPos))
lcNume = Alltrim(Left(m.lcNume, m.lnPos))
ENDIF
ENDIF
***************************
lcId_loc_inreg = 'NULL'
lcId_categ_ent = 'NULL'
@@ -454,15 +452,25 @@ DEFINE CLASS form1 AS form
lnSucces = goExecutor.oExecute(GetHash("cSql=>" + m.lcSqlInsert))
If !Empty(Nvl(m.pnIdPart, 0))
Replace id_part With m.pnIdPart In cClientiFurnizori
Thisform.trace('Client nou ' + Alltrim(cClientiFurnizori.denumire) + ' CUI: ' + Alltrim(cClientiFurnizori.cod_fiscal) + ' ID: ' + Alltrim(Transform(cClientiFurnizori.id_part)))
INSERT INTO cParteneri (id_part, denumire, cod_fiscal) VALUES (m.pnIdPart, cClientiFurnizori.denumire, cClientiFurnizori.cod_fiscal)
Thisform.trace('Client nou ' + Alltrim(m.lcDenumire) + ' CUI: ' + Alltrim(m.lcCod_fiscal) + ' ID: ' + Alltrim(Transform(m.pnIdPart)))
INSERT INTO cParteneri (id_part, denumire, cod_fiscal) VALUES (m.pnIdPart, m.pcDenumire, m.pcCodFiscal)
Else
lcMesaj = 'Eroare la adaugarea in clienti ROA ' + Alltrim(cParteneri.denumire) + ' CUI: ' + Alltrim(cParteneri.cod_fiscal) + Chr(13) + Chr(10) + goExecutor.oPrelucrareEroare()
Thisform.trace(m.lcMesaj)
aMessagebox(m.lcMesaj)
SET STEP ON
Exit
* Codul fiscal exista deja in ROA, uneori pe mai multi parteneri: se preia cel mai vechi si
* importul continua. Doar daca nici asa nu se gaseste partenerul, importul se opreste.
lcMesaj = goExecutor.oPrelucrareEroare()
pnIdPart = Thisform.GetIdPartenerROA(m.lcCod_fiscal)
If !Empty(Nvl(m.pnIdPart, 0))
Replace id_part With m.pnIdPart In cClientiFurnizori
INSERT INTO cParteneri (id_part, denumire, cod_fiscal) VALUES (m.pnIdPart, m.pcDenumire, m.pcCodFiscal)
Thisform.trace('Partener existent in ROA ' + Alltrim(m.lcDenumire) + ' CUI: ' + Alltrim(m.lcCod_fiscal) + ;
' ID: ' + Alltrim(Transform(m.pnIdPart)) + ' (' + Alltrim(m.lcMesaj) + ')')
Else
lcMesaj = 'Eroare la adaugarea in clienti ROA ' + Alltrim(m.lcDenumire) + ' CUI: ' + ;
Alltrim(m.lcCod_fiscal) + Chr(13) + Chr(10) + m.lcMesaj
Thisform.trace(m.lcMesaj)
aMessagebox(m.lcMesaj)
Exit
Endif
Endif && !Empty(Nvl(m.pnIdPart,0))
Endif && !Empty(Nvl(m.pnIdPart,0))
@@ -1748,6 +1756,62 @@ DEFINE CLASS form1 AS form
ENDPROC
PROCEDURE getidpartener
* Cauta partenerul in cursorul cParteneri, intai dupa cod fiscal, apoi dupa denumire.
* Cand mai multi parteneri au acelasi cod fiscal, il intoarce pe cel mai vechi (id_part minim).
Lparameters tcCodFiscal, tcDenumire
Local lcCheie, lnArie, lnIdPart
lnIdPart = 0
lnArie = Select()
If !Empty(m.tcCodFiscal)
lcCheie = Padr(Strtran(Upper(Alltrim(m.tcCodFiscal)), ' ', ''), 30, ' ')
Select cParteneri
Set Order To Tag cod_fiscal
If Seek(m.lcCheie, 'cParteneri', 'cod_fiscal')
Scan Rest While Padr(Strtran(cod_fiscal, ' ', ''), 30, ' ') == m.lcCheie
If m.lnIdPart = 0 Or cParteneri.id_part < m.lnIdPart
lnIdPart = cParteneri.id_part
Endif
Endscan
Endif
Endif
If Empty(m.tcCodFiscal) And !Empty(m.tcDenumire)
Select cParteneri
Set Order To Tag denumire
If Seek(Padr(Alltrim(Upper(m.tcDenumire)), 100, ' '), 'cParteneri', 'denumire')
lnIdPart = cParteneri.id_part
Endif
Endif
Select (m.lnArie)
Return m.lnIdPart
ENDPROC
PROCEDURE getidpartenerroa
* Intoarce cel mai vechi partener nesters din ROA cu acest cod fiscal (id_part minim), 0 daca nu exista.
Lparameters tcCodFiscal
Local lcCod, lcSql, lnArie, lnIdPart, lnSucces
lnIdPart = 0
lcCod = Strtran(Upper(Alltrim(Nvl(m.tcCodFiscal, ''))), ' ', '')
If Empty(m.lcCod)
Return 0
Endif
lnArie = Select()
lcSql = [select min(id_part) as id_part from nom_parteneri where replace(upper(cod_fiscal), ' ', '') = '] + ;
m.lcCod + [' and sters = 0]
lnSucces = goExecutor.oExecute(GetHash("cSql=>" + m.lcSql + '??cCursor=>cPartExistent'))
If m.lnSucces >= 0 And Used('cPartExistent')
lnIdPart = Nvl(cPartExistent.id_part, 0)
Use In (Select('cPartExistent'))
Endif
Select (m.lnArie)
Return m.lnIdPart
ENDPROC
PROCEDURE Init
Local ldData1, ldData2, ldLastDay, lnHeight, lnWidth

View File

View File

@@ -1664,6 +1664,25 @@ FUNCTION GetDataCNP
RETURN CTOD(SUBSTR(plcnp, 6, 2) + "." + SUBSTR(plcnp, 4, 2) + "." + mlan)
ENDFUNC
*
*
* Copiata din D:\ROA\ROACONT\COMUN\programe\oproceduri_comune.prg (GetNrFromString),
* ca sa nu duplic logica: taie de la inceputul sirului literele si separatorii,
* pana la prima cifra. Ex. 'RO 15613488' -> '15613488', 'CUI 14770212' -> '14770212'.
FUNCTION GetNrFromString
LPARAMETERS plstr
LOCAL mlenstr
mlenstr = LEN(ALLTRIM(plstr))
DO WHILE ISALPHA(plstr) .OR. LEFT(plstr, 1) == " ";
.OR. LEFT(plstr, 1) == "&" .OR. LEFT(plstr, 1) == "/";
.OR. LEFT(plstr, 1) == "-" .OR. LEFT(plstr, 1) == "_";
.OR. LEFT(plstr, 1) == "." .OR. LEFT(plstr, 1) == ":"
plstr = RTRIM(SUBSTR(plstr, 2, mlenstr))
ENDDO
RETURN plstr
ENDFUNC
*
FUNCTION VerifCF
LPARAMETERS plcfisc
LOCAL mlsuma, mlrest
@@ -1705,3 +1724,249 @@ FUNCTION GetCodFiscalFRO
RETURN m.lcCodFiscalFRO
ENDFUNC
*
* Clasifica partenerul importat (Fidelio hotel, BIZ restaurant) in persoana fizica / juridica
* si curata DOAR CNP-urile invalide.
*
* 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.
*
* 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 (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, tcDenumire, tcRegCom, tcCodTara, tcTipCunoscut
LOCAL lcCod, lcBrut, lcCifre, lcCifreReg, lcPrefix, lcPrefixBrut, lcTara, lcTip
LOCAL llLitere, llPrefixFiscal, lnParam
lnParam = PCOUNT()
lcCod = GetCodFiscalCurat(m.tcCod)
lcCifre = CHRTRAN(m.lcCod, CHRTRAN(m.lcCod, '0123456789', ''), '')
lcPrefix = ALLTRIM(CHRTRAN(m.lcCod, CHRTRAN(m.lcCod, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', ''), ''))
llLitere = !EMPTY(m.lcPrefix)
* 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')
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 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
* 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
*