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
257 lines
10 KiB
PowerShell
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")
|