#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
This commit is contained in:
256
utile/Teste/facturare_unificat/verifica_grila.ps1
Normal file
256
utile/Teste/facturare_unificat/verifica_grila.ps1
Normal file
@@ -0,0 +1,256 @@
|
||||
# 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")
|
||||
Reference in New Issue
Block a user