ocautare.prg - miezul functionalitatii: - anaf_verif_cautare: verdictul ANAF pe randul curent din formularul de cautare (label sub grid), la data documentului; discordantele si codurile fiscale invalide cu rosu; F4 sau click deschide detaliile. - ANAF_StarePartener, cu cache pe sesiune, excludere parteneri externi si persoane fizice; ANAF_ValidareCod pentru CIF/CNP. - ANAF_NivelVerificare: cascada RC_ANAF_VERIF_SELECTIE (kill-switch settings.ini > optiuni utilizator > optiuni firma > implicit 1), cu cache invalidat la schimbarea firmei sau utilizatorului; ANAF_ComutaVerificare pentru punctul de meniu. - Detalii: alegerea variantei corecte de partener din perechea RO / fara RO, cu discriminator "are documente in perioada"; dupa Da sau Nu se inchide cautarea si se revine in formular cu partenerul ales (AplicaInlocuirePartenerANAF). - ANAF_CaData: data documentului poate veni si ca DateTime (tact.dataact). - Banda proprie sub grid pentru label: formularul creste cu 54, gridul isi pierde ancorarea de jos si primeste inaltimea din AjusteazaBanda, legata de Resize si Activate - formularul isi reaseaza gridul dupa Show, deci Activate e momentul util. validare.prg: ANAF_VerificaCuiSingle, wrapper single-CUI peste serviciul ANAF, cu data verificarii si o singura eroare logata pe sesiune. cauta_alfa.prg: hook generic - ataseaza poVerifAlegere pe formularul de cautare inainte de Show si il detaseaza dupa. Apelanti opt-in (lVerificaANAF): baza.vc2 (lookup-ul generic de partener din formularele actbaza/actbaza2007 lansate din meniuri), onote_contabile.vc2 (partener debit/credit), omodificari.vc2 (do_cauta din cele trei clase de modificare, cu data notei), ofacturare.vc2 (client, furnizor). Casa/banca ramane in afara: acolo partenerul se alege pentru imperecherea facturilor. cauta_alfa_forms.vc2: F4 deschide detaliile, iar terminarea cautarii trece prin VerificaAlegere. Teste: suita headless pentru fluxul de verificare (utile/Teste/partener_anaf/), cu mock pentru apelul ANAF si pentru dialoguri. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019H3r66sVojGhgaKq5niu1u
178 lines
8.2 KiB
PowerShell
178 lines
8.2 KiB
PowerShell
# shot_dialog_anaf.ps1 - lanseaza shot_dialog_anaf.prg (dialog ANAF REAL, neinlocuit) si
|
|
# captureaza fereastra nativa MESSAGEBOX() (clasa #32770) aparuta in acelasi proces vfp9,
|
|
# apoi o inchide cu WM_CLOSE (fara SendKeys, fara sa fure focus - fereastra e mutata off-screen
|
|
# inainte de captura, ca in vfp_ui_harness.ps1).
|
|
param(
|
|
[string]$Prg = (Join-Path $PSScriptRoot 'shot_dialog_anaf.prg'),
|
|
[string]$OutPng = (Join-Path $PSScriptRoot 'screenshots\dialog_anaf_real.png'),
|
|
[int]$ReadyTimeoutSec = 150,
|
|
[int]$DialogTimeoutSec = 20,
|
|
[string]$Vfp = 'C:\Program Files (x86)\Microsoft Visual FoxPro 9\vfp9.exe'
|
|
)
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
$TestDir = [System.IO.Path]::GetDirectoryName($Prg)
|
|
$TestFxp = [System.IO.Path]::ChangeExtension($Prg, 'fxp')
|
|
$TestLog = Join-Path $TestDir 'shot_dialog_anaf_log.txt'
|
|
$SyncDir = Join-Path $TestDir 'uisync_dialog'
|
|
$Precompile = Join-Path (Split-Path $TestDir -Parent) '_precompile.ps1'
|
|
|
|
Add-Type -AssemblyName System.Drawing
|
|
|
|
Add-Type @"
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
public class Win32Dlg {
|
|
public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct RECT { public int Left, Top, Right, Bottom; }
|
|
[DllImport("user32.dll")] public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
|
|
[DllImport("user32.dll")] public static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
|
|
[DllImport("user32.dll")] public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
|
|
[DllImport("user32.dll")] public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
|
|
[DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr hWnd);
|
|
[DllImport("user32.dll")] public static extern bool IsWindow(IntPtr hWnd);
|
|
[DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
|
|
[DllImport("user32.dll")] public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
|
|
[DllImport("user32.dll")] public static extern bool PrintWindow(IntPtr hwnd, IntPtr hdc, uint nFlags);
|
|
[DllImport("user32.dll")] public static extern IntPtr PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
|
|
public static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
|
|
public const uint SWP_NOSIZE = 0x0001;
|
|
public const uint SWP_NOACTIVATE = 0x0010;
|
|
public const uint WM_CLOSE = 0x0010;
|
|
|
|
public static List<IntPtr> FindByPidAndClass(int pid, string className) {
|
|
var result = new List<IntPtr>();
|
|
EnumWindows((hWnd, lParam) => {
|
|
int wpid;
|
|
GetWindowThreadProcessId(hWnd, out wpid);
|
|
if (wpid == pid && IsWindowVisible(hWnd)) {
|
|
var sb = new StringBuilder(256);
|
|
GetClassName(hWnd, sb, sb.Capacity);
|
|
if (sb.ToString() == className) { result.Add(hWnd); }
|
|
}
|
|
return true;
|
|
}, IntPtr.Zero);
|
|
return result;
|
|
}
|
|
public static string GetTitle(IntPtr hWnd) {
|
|
var sb = new StringBuilder(512);
|
|
GetWindowText(hWnd, sb, sb.Capacity);
|
|
return sb.ToString();
|
|
}
|
|
}
|
|
"@
|
|
|
|
function Push-Offscreen([IntPtr]$Hwnd) {
|
|
if ($Hwnd -eq [IntPtr]::Zero -or -not [Win32Dlg]::IsWindow($Hwnd)) { return }
|
|
[Win32Dlg]::SetWindowPos($Hwnd, [Win32Dlg]::HWND_BOTTOM, -4000, 0, 0, 0, ([Win32Dlg]::SWP_NOSIZE -bor [Win32Dlg]::SWP_NOACTIVATE)) | Out-Null
|
|
}
|
|
|
|
function Wait-MainWindowHandle([System.Diagnostics.Process]$Proc, [int]$TimeoutSec) {
|
|
$deadline = (Get-Date).AddSeconds($TimeoutSec)
|
|
while ((Get-Date) -lt $deadline) {
|
|
try {
|
|
$Proc.Refresh()
|
|
if ($Proc.HasExited) { return [IntPtr]::Zero }
|
|
if ($Proc.MainWindowHandle -ne [IntPtr]::Zero) { return $Proc.MainWindowHandle }
|
|
} catch {}
|
|
Start-Sleep -Milliseconds 200
|
|
}
|
|
return [IntPtr]::Zero
|
|
}
|
|
|
|
function Wait-File([string]$Path, [int]$TimeoutSec, [IntPtr]$Hwnd = [IntPtr]::Zero) {
|
|
$deadline = (Get-Date).AddSeconds($TimeoutSec)
|
|
while (-not (Test-Path $Path)) {
|
|
if ((Get-Date) -gt $deadline) { return $false }
|
|
if ($Hwnd -ne [IntPtr]::Zero) { Push-Offscreen $Hwnd }
|
|
Start-Sleep -Milliseconds 300
|
|
}
|
|
return $true
|
|
}
|
|
|
|
function Take-Screenshot([IntPtr]$Hwnd, [string]$Path) {
|
|
$rect = New-Object Win32Dlg+RECT
|
|
[Win32Dlg]::GetWindowRect($Hwnd, [ref]$rect) | Out-Null
|
|
$w = $rect.Right - $rect.Left
|
|
$h = $rect.Bottom - $rect.Top
|
|
if ($w -le 0 -or $h -le 0) { $w = 600; $h = 300 }
|
|
$bmp = New-Object System.Drawing.Bitmap $w, $h
|
|
$g = [System.Drawing.Graphics]::FromImage($bmp)
|
|
$hdc = $g.GetHdc()
|
|
$ok = [Win32Dlg]::PrintWindow($Hwnd, $hdc, 2)
|
|
$g.ReleaseHdc($hdc)
|
|
$g.Dispose()
|
|
if (-not $ok) {
|
|
$g2 = [System.Drawing.Graphics]::FromImage($bmp)
|
|
$hdc2 = $g2.GetHdc()
|
|
[Win32Dlg]::PrintWindow($Hwnd, $hdc2, 0) | Out-Null
|
|
$g2.ReleaseHdc($hdc2)
|
|
$g2.Dispose()
|
|
}
|
|
$bmp.Save($Path, [System.Drawing.Imaging.ImageFormat]::Png)
|
|
$bmp.Dispose()
|
|
}
|
|
|
|
New-Item -ItemType Directory -Force -Path (Split-Path $OutPng -Parent) | Out-Null
|
|
if (Test-Path $SyncDir) { Get-ChildItem $SyncDir -File | Remove-Item -Force -ErrorAction SilentlyContinue } else { New-Item -ItemType Directory -Path $SyncDir | Out-Null }
|
|
|
|
Write-Host "Precompilez .fxp (izolat): $Precompile -Prg $Prg"
|
|
Get-Process vfp9 -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
|
|
Start-Sleep -Milliseconds 500
|
|
Start-Process powershell -WindowStyle Hidden -ArgumentList @('-ExecutionPolicy','Bypass','-File',"`"$Precompile`"",'-Prg',"`"$Prg`"") -Wait | Out-Null
|
|
Start-Sleep -Seconds 2
|
|
Get-Process vfp9 -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
|
|
Start-Sleep -Seconds 2
|
|
if (-not (Test-Path $TestFxp)) { Write-Warning "Nu s-a generat $TestFxp - compilare esuata."; exit 1 }
|
|
|
|
$ready0 = Join-Path $SyncDir 'ready_0.txt'
|
|
if (Test-Path $ready0) { Remove-Item $ready0 -Force }
|
|
if (Test-Path $TestLog) { Remove-Item $TestLog -Force }
|
|
$t0 = Get-Date
|
|
Write-Host "Lansez VFP: $Vfp -A $TestFxp"
|
|
$proc = Start-Process -FilePath $Vfp -ArgumentList @('-A', "`"$TestFxp`"") -PassThru
|
|
$hwnd = Wait-MainWindowHandle $proc 15
|
|
Push-Offscreen $hwnd
|
|
|
|
if (-not (Wait-File $ready0 $ReadyTimeoutSec $hwnd)) {
|
|
Write-Warning "Nu am primit ready_0 in ${ReadyTimeoutSec}s. Ultima linie log: '$(Get-Content $TestLog -Tail 1 -ErrorAction SilentlyContinue)'"
|
|
if (-not $proc.HasExited) { try { $proc.Kill() } catch {} }
|
|
Get-Process vfp9 -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
|
|
exit 1
|
|
}
|
|
Write-Host "ready_0 primit - astept fereastra MESSAGEBOX() nativa (clasa #32770, acelasi PID)"
|
|
|
|
$dlgHwnd = [IntPtr]::Zero
|
|
$deadline = (Get-Date).AddSeconds($DialogTimeoutSec)
|
|
while ((Get-Date) -lt $deadline -and $dlgHwnd -eq [IntPtr]::Zero) {
|
|
$cands = [Win32Dlg]::FindByPidAndClass($proc.Id, '#32770')
|
|
foreach ($h in $cands) { $dlgHwnd = $h; break }
|
|
if ($dlgHwnd -eq [IntPtr]::Zero) { Start-Sleep -Milliseconds 300 }
|
|
}
|
|
|
|
if ($dlgHwnd -eq [IntPtr]::Zero) {
|
|
Write-Warning "Fereastra dialogului nu a aparut in ${DialogTimeoutSec}s."
|
|
if (-not $proc.HasExited) { try { $proc.Kill() } catch {} }
|
|
Get-Process vfp9 -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
|
|
exit 1
|
|
}
|
|
Write-Host ("dialog gasit: hwnd={0} titlu=[{1}]" -f $dlgHwnd, [Win32Dlg]::GetTitle($dlgHwnd))
|
|
Push-Offscreen $dlgHwnd
|
|
Start-Sleep -Milliseconds 600
|
|
Push-Offscreen $dlgHwnd
|
|
Take-Screenshot $dlgHwnd $OutPng
|
|
Write-Host "screenshot: $OutPng"
|
|
|
|
# inchide dialogul (echivalent Escape/Cancel) - nu SendKeys, ca sa nu ceara focus
|
|
[Win32Dlg]::PostMessage($dlgHwnd, [Win32Dlg]::WM_CLOSE, [IntPtr]::Zero, [IntPtr]::Zero) | Out-Null
|
|
|
|
$done = Join-Path $SyncDir 'done.txt'
|
|
if (Wait-File $done 30 $hwnd) { Write-Host "done: $((Get-Content $done -Raw).Trim())" } else { Write-Warning "Nu am primit done.txt dupa inchiderea dialogului." }
|
|
Start-Sleep -Milliseconds 500
|
|
if (-not $proc.HasExited) { try { $proc.Kill() } catch {} }
|
|
Get-Process vfp9 -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
|
|
Write-Host "GATA. Screenshot in $OutPng"
|