Files
comun/utile/Teste/facturare_unificat/verifica_grila.ps1
Marius Mutu 8e022d332e #13: formular de facturare unificat - etapa curenta
Squash al branch-ului de lucru plan13-s2.

Clase: ofacturare (nucleul formularului unificat), ofacturare_comun,
ferestre_cere_date, ocomenzi, caut_ora (lista de preturi in combo-urile de
cautare), omodificari.

Programe: ofacturare impartit - ofacturare_antet, ofacturare_rutare_scriere si
ogrid_latimi sunt fisiere noi; oproceduri_facturare primeste discountul pe linie
si cota standard de TVA cand articolul nu are cota pe politica.

Documentatie: capcana SQLExec no_data_found, conventia de encoding, depanarea
testelor VFP si regulile de lucru - conflictele cu modificarile venite din alte
proiecte sunt rezolvate pastrand ambele parti.

Loguri de rulare a testelor scoase din versionare; testele raman.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PkyjGyrV2S7932om4kfSiK
2026-09-09 21:38:25 +03:00

257 lines
10 KiB
PowerShell

# verifica_grila.ps1 - citeste hartile de geometrie recursiva geom_grila_<stare>.tsv (scrise de
# DumpGrilaRecursiv/ColectGrila din test_s3b_dupa_ui.prg) si raporteaza incalcarile din
# docs\nota_s3b_reasezare_grila.md sectiunea 6 + addendumul A1.5. Nu ruleaza VFP, doar text.
#
# Rulare: powershell -ExecutionPolicy Bypass -File verifica_grila.ps1 [-Dir <folder cu tsv>] [-OutFile <raport>]
# implicit -Dir = .\out , -OutFile = .\out\raport_grila.txt
param(
[string]$Dir = (Join-Path $PSScriptRoot 'out'),
[string]$OutFile = (Join-Path (Join-Path $PSScriptRoot 'out') 'raport_grila.txt')
)
$ErrorActionPreference = 'Stop'
# controale admise in banda de titlu (Top 0..29), pe Name complet
$TitleBandAllow = @('_shape1', '_shape2', 'Lb_titlu_alb_b121', 'BUT_TERMIN1', 'But_renunt1')
function Read-GeomTsv {
param([string]$Path)
$lines = Get-Content -Path $Path -Encoding Default
if ($lines.Count -lt 2) { return @() }
$header = $lines[0] -split "`t"
$rows = @()
for ($i = 1; $i -lt $lines.Count; $i++) {
if ([string]::IsNullOrEmpty($lines[$i])) { continue }
$f = $lines[$i] -split "`t"
# linia poate avea Caption gol la coada -> -split scurteaza array-ul; completeaza
while ($f.Count -lt $header.Count) { $f += '' }
$rows += [pscustomobject]@{
Name = $f[0]
Class = $f[1]
Left = [double]$f[2]
Top = [double]$f[3]
Width = [double]$f[4]
Height = [double]$f[5]
Visible = ($f[6] -eq '.T.')
AbsLeft = [double]$f[7]
AbsTop = [double]$f[8]
Caption = $f[9]
}
}
return $rows
}
function Is-ShapeOrGrid {
param($row)
if ($row.Class -match '^_shape') { return $true }
if ($row.Class -eq '_grdrow') { return $true }
if ($row.Name -match '(^|\.)[Gg]rd_') { return $true }
return $false
}
function Is-AncestorOf {
param([string]$a, [string]$b)
return $b.StartsWith($a + '.')
}
# --- verificarile pe o singura stare -------------------------------------------------
function Check-TitleBand {
param($rows)
$hits = @()
foreach ($r in $rows) {
if (-not $r.Visible) { continue }
if ($TitleBandAllow -icontains $r.Name) { continue }
if ($r.AbsTop -lt 29 -and ($r.AbsTop + $r.Height) -gt 0) {
$hits += $r
}
}
return $hits
}
function Check-Overlaps {
param($rows)
$cand = @($rows | Where-Object { $_.Visible -and -not (Is-ShapeOrGrid $_) })
$hits = @()
for ($i = 0; $i -lt $cand.Count; $i++) {
for ($j = $i + 1; $j -lt $cand.Count; $j++) {
$a = $cand[$i]; $b = $cand[$j]
if ((Is-AncestorOf $a.Name $b.Name) -or (Is-AncestorOf $b.Name $a.Name)) { continue }
$aR = $a.AbsLeft + $a.Width; $aB = $a.AbsTop + $a.Height
$bR = $b.AbsLeft + $b.Width; $bB = $b.AbsTop + $b.Height
$ovX = [Math]::Min($aR, $bR) - [Math]::Max($a.AbsLeft, $b.AbsLeft)
$ovY = [Math]::Min($aB, $bB) - [Math]::Max($a.AbsTop, $b.AbsTop)
if ($ovX -gt 2 -and $ovY -gt 2) {
$hits += [pscustomobject]@{
A = $a; B = $b; OvX = $ovX; OvY = $ovY
RectLeft = [Math]::Max($a.AbsLeft, $b.AbsLeft)
RectTop = [Math]::Max($a.AbsTop, $b.AbsTop)
}
}
}
}
return $hits
}
# Zonele nu sunt etichetate in harta - se reconstituie geometric: randurile (AbsTop grupat +/-3px)
# se ordoneaza pe verticala, iar un gol de peste 40px intre bottom-ul unui rand si top-ul
# urmatorului marcheaza granita intre doua zone (antet / grid / acordeoane / totaluri).
$ZoneGapPx = 40
$RowTolerancePx = 3
function Group-Rows {
param($rows)
$cand = @($rows | Where-Object { $_.Visible -and -not (Is-ShapeOrGrid $_) } | Sort-Object AbsTop)
$rowGroups = @()
foreach ($r in $cand) {
$g = $rowGroups | Where-Object { [Math]::Abs($_.Top - $r.AbsTop) -le $RowTolerancePx } | Select-Object -First 1
if ($null -eq $g) {
$rowGroups += [pscustomobject]@{ Top = $r.AbsTop; Items = @($r); Bottom = ($r.AbsTop + $r.Height) }
} else {
$g.Items += $r
if (($r.AbsTop + $r.Height) -gt $g.Bottom) { $g.Bottom = $r.AbsTop + $r.Height }
}
}
return @($rowGroups | Sort-Object Top)
}
function Group-Zones {
param($rowGroups)
$zones = @()
$cur = @()
$prevBottom = $null
foreach ($rg in $rowGroups) {
if ($null -ne $prevBottom -and ($rg.Top - $prevBottom) -gt $ZoneGapPx) {
$zones += , $cur
$cur = @()
}
$cur += $rg
if ($null -eq $prevBottom -or $rg.Bottom -gt $prevBottom) { $prevBottom = $rg.Bottom }
}
if ($cur.Count -gt 0) { $zones += , $cur }
return $zones
}
function Check-Alignment {
param($rows)
$rowGroups = Group-Rows $rows
$zones = Group-Zones $rowGroups
$report = @()
$zoneIdx = 0
foreach ($zone in $zones) {
$zoneIdx++
if ($zone.Count -lt 2) { continue }
$leftCounts = @{}
foreach ($rg in $zone) {
foreach ($it in $rg.Items) {
$key = [string][int]$it.AbsLeft
if ($leftCounts.ContainsKey($key)) { $leftCounts[$key] += 1 } else { $leftCounts[$key] = 1 }
}
}
$distinct = $leftCounts.Keys | Sort-Object { [int]$_ }
$suspects = $leftCounts.GetEnumerator() | Where-Object { $_.Value -eq 1 } | ForEach-Object { $_.Key }
$report += [pscustomobject]@{
ZoneIdx = $zoneIdx
TopRange = "$([int]$zone[0].Top)..$([int]($zone[$zone.Count-1].Top))"
NumRows = $zone.Count
Distinct = ($distinct -join ', ')
Suspects = @($suspects | Sort-Object { [int]$_ })
}
}
return $report
}
function Get-MaxRight {
param($rows)
$cand = @($rows | Where-Object { $_.Visible })
if ($cand.Count -eq 0) { return 0 }
return ([Math]::Round(($cand | ForEach-Object { $_.AbsLeft + $_.Width } | Measure-Object -Maximum).Maximum))
}
# --- rulare pe toate starile -----------------------------------------------------------
if (-not (Test-Path $Dir)) {
"Directorul $Dir nu exista." | Set-Content -Path $OutFile -Encoding UTF8
Write-Host "Director inexistent: $Dir"
exit 1
}
$files = Get-ChildItem -Path $Dir -Filter 'geom_grila_*.tsv' | Sort-Object Name
if ($files.Count -eq 0) {
"Niciun fisier geom_grila_*.tsv gasit in $Dir." | Set-Content -Path $OutFile -Encoding UTF8
Write-Host "Niciun fisier geom_grila_*.tsv in $Dir"
exit 1
}
$allTitleHits = @{}
$allOverlapHits = @{}
$allAlignHits = @{}
$allMaxRight = @{}
$allVisCount = @{}
$detailLines = @()
foreach ($f in $files) {
$stare = $f.BaseName -replace '^geom_grila_', ''
$rows = Read-GeomTsv -Path $f.FullName
$visCount = @($rows | Where-Object { $_.Visible }).Count
$titleHits = Check-TitleBand $rows
$overlapHits = Check-Overlaps $rows
$alignReport = Check-Alignment $rows
$alignSuspectZones = @($alignReport | Where-Object { $_.Suspects.Count -gt 0 })
$maxRight = Get-MaxRight $rows
$allTitleHits[$stare] = $titleHits.Count
$allOverlapHits[$stare] = $overlapHits.Count
$allAlignHits[$stare] = ($alignSuspectZones | ForEach-Object { $_.Suspects.Count } | Measure-Object -Sum).Sum
if ($null -eq $allAlignHits[$stare]) { $allAlignHits[$stare] = 0 }
$allMaxRight[$stare] = $maxRight
$allVisCount[$stare] = $visCount
$detailLines += ""
$detailLines += "=== STARE: $stare ($($rows.Count) randuri in harta, $visCount vizibile) ==="
$detailLines += "--- Banda de titlu (0..29): $($titleHits.Count) incalcari ---"
foreach ($h in $titleHits) {
$detailLines += (" {0} ({1}) AbsLeft={2} AbsTop={3} W={4} H={5}" -f $h.Name, $h.Class, [int]$h.AbsLeft, [int]$h.AbsTop, [int]$h.Width, [int]$h.Height)
}
$detailLines += "--- Suprapuneri eticheta/caseta: $($overlapHits.Count) ---"
foreach ($h in $overlapHits) {
$detailLines += (" {0} ({1}) x {2} ({3}) suprapunere {4}x{5}px la ({6},{7}) captionA='{8}' captionB='{9}'" -f `
$h.A.Name, $h.A.Class, $h.B.Name, $h.B.Class, [int]$h.OvX, [int]$h.OvY, [int]$h.RectLeft, [int]$h.RectTop, $h.A.Caption, $h.B.Caption)
}
$detailLines += "--- Aliniere pe coloane (zone reconstituite din goluri >$($ZoneGapPx)px, randuri grupate +/-$($RowTolerancePx)px) ---"
foreach ($z in $alignReport) {
$flag = if ($z.Suspects.Count -gt 0) { " <-- POSIBILA DEZALINIERE: AbsLeft=$($z.Suspects -join ', ')" } else { "" }
$detailLines += (" zona {0} (Top {1}, {2} randuri): AbsLeft distincte = {3}{4}" -f $z.ZoneIdx, $z.TopRange, $z.NumRows, $z.Distinct, $flag)
}
$detailLines += "--- Latime folosita: max(AbsLeft+Width) peste vizibile = $maxRight px ---"
$detailLines += "--- Numar total controale vizibile: $visCount ---"
}
$sumTitle = ($allTitleHits.Values | Measure-Object -Sum).Sum
$sumOverlap = ($allOverlapHits.Values | Measure-Object -Sum).Sum
$sumAlign = ($allAlignHits.Values | Measure-Object -Sum).Sum
$widthSummary = ($allMaxRight.GetEnumerator() | ForEach-Object { "$($_.Key)=$($_.Value)" }) -join '; '
$widthWarn = @($allMaxRight.GetEnumerator() | Where-Object { $_.Value -lt 1400 } | ForEach-Object { $_.Key })
$widthFlag = if ($widthWarn.Count -gt 0) { " -- SUB 1400px (criteriul 9): $($widthWarn -join ', ')" } else { " -- OK (>=1400px peste tot)" }
$countSummary = ($allVisCount.GetEnumerator() | ForEach-Object { "$($_.Key)=$($_.Value)" }) -join '; '
$summary = @(
"REZUMAT (5 linii, citeste doar astea pentru verdict rapid):"
"1. Incalcari banda de titlu (0..29px): $sumTitle"
"2. Suprapuneri eticheta/caseta: $sumOverlap"
"3. Posibile dezalinieri de coloana: $sumAlign"
"4. Latime folosita per stare: $widthSummary$widthFlag"
"5. Controale vizibile per stare: $countSummary"
)
$out = @()
$out += $summary
$out += ""
$out += "=" * 90
$out += $detailLines
$out -join "`r`n" | Set-Content -Path $OutFile -Encoding UTF8
Write-Host "Raport scris in $OutFile"
Write-Host ($summary -join "`n")