From 4666262c92e6b321c82750511b074fc4877825ca Mon Sep 17 00:00:00 2001 From: Marius Mutu Date: Mon, 3 Aug 2026 23:14:47 +0300 Subject: [PATCH 1/9] publicare scripturi de baza de date: script + documentatie COMUN/utile/publicare_scripturi.ps1 face cei doi pasi ai publicarii (import in UPD_DATABASE + arhiva lunara database[n]_*.zip in _UPDATE), cu configurarea citita din settings.ini-ul lui tasks.exe si verificare octet cu octet a continutului. --- docs/publicare-scripturi-db.md | 83 ++++ docs/scripturi-migrare-db.md | 3 +- docs/scripturi-uzuale.md | 1 + utile/publicare_scripturi.ps1 | 689 +++++++++++++++++++++++++++++++++ 4 files changed, 775 insertions(+), 1 deletion(-) create mode 100644 docs/publicare-scripturi-db.md create mode 100644 utile/publicare_scripturi.ps1 diff --git a/docs/publicare-scripturi-db.md b/docs/publicare-scripturi-db.md new file mode 100644 index 0000000..30bee3d --- /dev/null +++ b/docs/publicare-scripturi-db.md @@ -0,0 +1,83 @@ +# Publicarea scripturilor de baza de date catre clienti + +Scriptul: `COMUN\utile\publicare_scripturi.ps1`. Inlocuieste pasii manuali din `tasks.exe` +(`D:\PROIECTE\fox\tasks`, formularul din `clase\generare_script.vcx`), care raman varianta de rezerva. +Cum se scrie un script de migrare: `scripturi-migrare-db.md`. + +```powershell +powershell -File COMUN\utile\publicare_scripturi.ps1 -DryRun # ce ar face pe luna curenta +powershell -File COMUN\utile\publicare_scripturi.ps1 # luna curenta, tot fluxul +powershell -File COMUN\utile\publicare_scripturi.ps1 -Luna 2026-08 # alta luna +powershell -File COMUN\utile\publicare_scripturi.ps1 -Fisier a.sql,b.sql # doar scripturile date +``` + +Alte optiuni: `-DoarImport` (fara arhiva), `-DoarArhiva` (doar regenerare arhiva), `-Toate` +(republica si scripturile identice), `-FaraVerificareZip`, `-Settings`, `-ZipPath`, `-DirScripturi`, +`-Alias`, `-User`, `-Password`, `-SqlPlus`. + +## Configurarea + +Se citeste din `settings.ini`-ul lui `tasks.exe` (`D:\PROIECTE\fox\tasks\settings.ini`, schimbabil +cu `-Settings`), aceleasi chei pe care le foloseste formularul; orice parametru dat explicit bate +ini-ul. + +| din ini | ce devine | +|---|---| +| `[connection] host_database` | nume de **DSN ODBC** — aliasul TNS e cheia `ServerName` a DSN-ului din registri (`CENTRAL` → `ROA_CENTRAL`) | +| `[connection] username_database` / `password_database` | utilizatorul Oracle | +| `[folder] script_folder` | directorul scripturilor, cu `\SCRIPTURI\` → `\SCRIPTURI_CLAR\` (+ `\\\`) | +| `[folder] roa_output` | + `_UPDATE\` = directorul arhivelor | +| `[folder] sqlplus_exe` | clientul Oracle (si `TNS_ADMIN` = directorul lui) | +| `[script]` | prefixele valide (`CO_`, `FF_`, `RF_`, `SYS_`, `JCS_`) — prefix necunoscut = avertisment | + +`sqlplus_exe` arata spre `instantclient_19_18`; `instantclient_11_2_0_2` (valoarea de dinainte) are +un `tnsnames.ora` vechi, cu `ROA_CENTRAL` pe `10.0.20.122` in loc de `.121`, deci conexiunea expira. +`settings.ini` e local (ignorat de SVN). + +## Cei doi pasi + +**1. Import** — fiecare `.sql` intra ca un rand in `CONTAFIN_ORACLE.UPD_DATABASE` de pe `ROA_CENTRAL`, +cu `SCRIPT_ORDER = 0` (randul contine scriptul intreg, nu instructiuni parsate), `SCRIPT_NAME` cu +MAJUSCULE si `.SQL`, `SCRIPT_TYPE` = ce urmeaza dupa `NN_`, `SCRIPT_CONTENT` = fisierul in clar. +`ID` vine din trigger, `DATAORA` din `SYSDATE` implicit, `SCRIPT_APPVER`/`SCRIPT_EXTRA_FILE` raman +goale. Daca `SCRIPT_NAME` exista deja, se face **update pe acelasi `id`** — asa se republica un +script corectat, pentru ca clientul face `MERGE ... on (a.id = b.id)`. + +**2. Arhiva lunara** — in `Y:\ROAUPDATE\_UPDATE\` se scriu `database_YYYYMMDD_YYYYMMDD.zip` **si** +`databasen_...zip` (identice azi: `database_` era varianta criptata cu `wrap.exe`, nu se mai +foloseste). Fiecare contine XML-ul cu acelasi nume ca zip-ul (format `VFPData`/`crsxml`, +Windows-1252, CRLF, tab-uri, `script_content` gol) plus cate un `.sql` per script. +Indexii `roa_database.xml` / `roa_databasen.xml` listeaza arhivele existente si se schimba doar +cand apare o luna noua. + +Continutul din arhiva se citeste **inapoi din baza**, nu de pe disc — deci arhiva contine exact ce +va primi clientul. + +## Verificari facute automat + +- fisier cu sfarsituri de linie LF: **refuzat** (parsarea pe client se face pe `CHR(13)+CHR(10)`); +- dupa import, continutul e citit inapoi din CLOB si comparat octet cu octet cu fisierul; la + diferenta se opreste inainte de arhiva; +- scripturile deja publicate cu acelasi continut se sar (`-Toate` forteaza republicarea); +- arhiva generata se citeste inapoi cu cititorul clientului (`pack_utils.decodebase64` + + `zip_util_pkg.get_file`) si se compara lungime + MD5 per fisier — o arhiva facuta din PowerShell + este citita corect de `zip_util_pkg`; +- fisierele inlocuite din `_UPDATE` se copiaza intai in `_UPDATE\_backup\\`. + +`Y:\ROAUPDATE\_UPDATE\` e **live**: clientii descarca de acolo imediat ce fisierul e pus. + +## Ce face clientul + +`PACK_UPDATE.UpdateScripts` (`COMUN\docs\PACK_UPDATE.pck`) descarca indexul, apoi arhivele care +acopera perioada neaplicata, extrage XML-ul cu `zip_util_pkg.get_file`, face `MERGE` in +`UPD_DATABASE` pe `id`, apoi pentru fiecare rand cu `script_extra_file` pune continutul fisierului +in `script_content` — pentru `.sql` text clar, fara base64 (base64 e doar pentru instructiunile +parsate, alta ramura). Actualizare blocata: `depanare-pack-update.md`. + +## Detalii de implementare + +Continutul circula **hexazecimal** in ambele sensuri (`utl_i18n.raw_to_char` / `string_to_raw` cu +`WE8MSWIN1252`), pe bucati de 900 de octeti, in blocuri PL/SQL de cate 15 bucati. Motivul: baza e +`AL32UTF8`, fisierele sunt CP1252, iar comanda trimisa lui `sqlplus` ramane ASCII pur — nu depinde +de `NLS_LANG` si nu poate strica octetii >= 0x80. Consecinta utila: comparatia dintre fisier si +CLOB e exacta, nu pe lungime. diff --git a/docs/scripturi-migrare-db.md b/docs/scripturi-migrare-db.md index d015b12..ee070df 100644 --- a/docs/scripturi-migrare-db.md +++ b/docs/scripturi-migrare-db.md @@ -3,7 +3,8 @@ Scripturile de migrare a schemei (si modelele pentru scripturi noi) sunt in `DATABASE\SCRIPTURI_CLAR` de sub radacina suitei ROA (`gcDirMare`/`dirgen` — ex. `D:\ROA\DATABASE\SCRIPTURI_CLAR`). Sursa SVN: `http://svnroa:3001/svn/ROA/DATABASE/Branches/RB-1.00`. -Pentru un script nou, urmeaza formatul/conventiile celor recente de acolo. +Pentru un script nou, urmeaza formatul/conventiile celor recente de acolo. Publicarea catre clienti +(import in `UPD_DATABASE` + arhiva lunara): `publicare-scripturi-db.md`. Reguli confirmate de Marius (24.07.2026): diff --git a/docs/scripturi-uzuale.md b/docs/scripturi-uzuale.md index 4474e06..d079951 100644 --- a/docs/scripturi-uzuale.md +++ b/docs/scripturi-uzuale.md @@ -12,6 +12,7 @@ a doua oara — daca a doua oara e nevoie de acelasi lucru, scrie scriptul. | editare `.vcx`/`.scx` | `D:\ROA\UTIL\foxbin2prg\` : `git_sync.ps1`, `vfp_symbols.ps1`, `txt2vcx.ps1` | reimprospatare text in-arbore, index de simboluri, write-back cu fidelity-check | | actualizare de baza de date blocata | `COMUN\utile\diag_actualizare.ps1 -Alias ` | `UPD_ISTORIC` → `UPD_LOG` → `script_master.log` → versiuni per schema → job → tablespace → verdict. Vezi `depanare-pack-update.md` | | spatiu plin la un client | `COMUN\utile\diag_spatiu.ps1 -Alias [-Disc] [-SysPassword]` | tablespace, audit, ADR, FRA, disc server, top segmente. Vezi `depanare-spatiu-oracle.md` | +| publicare scripturi de baza de date | `COMUN\utile\publicare_scripturi.ps1 [-Luna YYYY-MM] [-Fisier ...] [-DryRun]` | import in `UPD_DATABASE` → arhiva lunara `database[n]_*.zip` + indexi in `_UPDATE`, cu verificare octet cu octet. Vezi `publicare-scripturi-db.md` | | teste UI VFP | harness din `COMUN\utile\Teste` | vezi `testare-ui-vfp.md` | Documentatia **nu** se genereaza automat si nu exista niciun hook care s-o scrie — `livrare.ps1` diff --git a/utile/publicare_scripturi.ps1 b/utile/publicare_scripturi.ps1 new file mode 100644 index 0000000..11c0ac5 --- /dev/null +++ b/utile/publicare_scripturi.ps1 @@ -0,0 +1,689 @@ +# publicare_scripturi.ps1 - publica scripturile de migrare .sql catre clientii ROA. +# Doi pasi, exact ca formularul din tasks.exe (clase\generare_script.vcx): +# 1. import - scrie fiecare .sql in CONTAFIN_ORACLE.UPD_DATABASE de pe ROA_CENTRAL +# (script_order = 0, un rand = un script intreg); daca script_name exista deja, +# face update pe id, ca sa se republice continutul corectat; +# 2. arhiva - regenereaza database_.zip / databasen_.zip si indexii +# roa_database.xml / roa_databasen.xml in Y:\ROAUPDATE\_UPDATE. +# Continutul din arhiva se citeste INAPOI din baza, nu de pe disc, si se compara octet cu octet cu +# fisierul sursa - deci arhiva contine exact ce vor primi clientii. Optional (implicit pornit), +# arhiva generata se citeste inapoi prin zip_util_pkg, cititorul Oracle al clientului. +# +# Configurarea se citeste din settings.ini-ul lui tasks.exe (aceleasi chei pe care le foloseste +# formularul): [connection] host_database/username_database/password_database, [folder] +# script_folder (\SCRIPTURI\ -> \SCRIPTURI_CLAR\), roa_output (+ _UPDATE) si sqlplus_exe, [script] +# prefixele valide. Aliasul TNS se ia din DSN-ul ODBC numit in host_database (cheia ServerName din +# registri). Orice parametru dat explicit are prioritate fata de ini. +# +# Utilizare: +# powershell -File COMUN\utile\publicare_scripturi.ps1 -DryRun +# powershell -File COMUN\utile\publicare_scripturi.ps1 -Fisier a.sql,b.sql +# powershell -File COMUN\utile\publicare_scripturi.ps1 -Luna 2026-08 +# powershell -File COMUN\utile\publicare_scripturi.ps1 -Luna 2026-08 -DoarArhiva +param( + [string[]]$Fisier, + [string]$Luna, + [string]$Settings = 'D:\PROIECTE\fox\tasks\settings.ini', + [string]$DirScripturi, + [string]$ZipPath, + [string]$BackupPath, + [switch]$DoarImport, + [switch]$DoarArhiva, + [switch]$Toate, + [switch]$FaraVerificareZip, + [switch]$DryRun, + [switch]$PastreazaTemp, + [string]$Alias, + [string]$User, + [string]$Password, + [string]$SqlPlus +) + +$ErrorActionPreference = 'Stop' +Add-Type -AssemblyName System.IO.Compression.FileSystem + +# octeti per bucata trimisa/citita ca hexazecimal (2 caractere per octet, sub limita de linie sqlplus) +$BUCATA = 900 +# bucati per bloc PL/SQL (sursa unui bloc trebuie sa ramana sub ~64k) +$BUCATI_PER_BLOC = 15 + +function Scrie-Titlu { + param([string]$Text) + Write-Output '' + Write-Output ('===== ' + $Text + ' =====') +} + +function Opreste { + param([string]$Mesaj) + Write-Output '' + Write-Output ('EROARE: ' + $Mesaj) + Curata-Temp + exit 1 +} + +function Curata-Temp { + if ($PastreazaTemp) { + if (Test-Path $TempDir) { Write-Output ('Temp pastrat: ' + $TempDir) } + return + } + if (Test-Path $TempDir) { Remove-Item -LiteralPath $TempDir -Recurse -Force -ErrorAction SilentlyContinue } +} + +function Invoke-SqlPlus { + # ruleaza un script sqlplus; intoarce textul si codul de iesire (whenever sqlerror => cod <> 0) + param([string]$Sql, [string]$Nume = 'q') + $f = Join-Path $TempDir ($Nume + '_' + [guid]::NewGuid().ToString('N') + '.sql') + $antet = "whenever sqlerror exit failure rollback`r`nwhenever oserror exit failure`r`nset define off`r`nset feedback off`r`n" + [System.IO.File]::WriteAllText($f, ($antet + $Sql), [System.Text.Encoding]::ASCII) + try { + $out = & $SqlPlus -L -S $ConnStr ('@' + $f) 2>&1 + $cod = $LASTEXITCODE + } catch { + $out = 'EROARE lansare sqlplus: ' + $_.Exception.Message + $cod = -1 + } + if (-not $PastreazaTemp) { Remove-Item -LiteralPath $f -Force -ErrorAction SilentlyContinue } + $text = ($out | ForEach-Object { [string]$_ }) -join "`n" + [PSCustomObject]@{ Text = $text; Cod = $cod } +} + +function Test-Rezultat { + param($R, [string]$Context) + if ($R.Cod -ne 0 -or $R.Text -match 'ORA-\d{5}|SP2-\d{4}|PLS-\d{5}') { + Write-Output $R.Text + Opreste ($Context + ' - sqlplus a raportat eroare (cod ' + $R.Cod + ').') + } +} + +function ConvertTo-Hex { + param([byte[]]$Octeti) + if ($Octeti.Length -eq 0) { return '' } + [System.BitConverter]::ToString($Octeti).Replace('-', '') +} + +function ConvertFrom-Hex { + param([string]$Hex) + $n = [int]($Hex.Length / 2) + $b = New-Object byte[] $n + for ($i = 0; $i -lt $n; $i++) { $b[$i] = [Convert]::ToByte($Hex.Substring($i * 2, 2), 16) } + ,$b +} + +function Citeste-Ini { + param([string]$Cale) + $ini = @{} + $sectiune = '' + foreach ($linie in [System.IO.File]::ReadAllLines($Cale, [System.Text.Encoding]::GetEncoding(1252))) { + $linie = $linie.Trim() + if (-not $linie -or $linie.StartsWith(';') -or $linie.StartsWith('#')) { continue } + if ($linie -match '^\[(.+)\]$') { + $sectiune = $Matches[1].ToLower() + if (-not $ini.ContainsKey($sectiune)) { $ini[$sectiune] = @{} } + continue + } + $p = $linie.IndexOf('=') + if ($p -lt 1 -or -not $sectiune) { continue } + $ini[$sectiune][$linie.Substring(0, $p).Trim().ToLower()] = $linie.Substring($p + 1).Trim() + } + $ini +} + +function Ia-Cheie { + param($Ini, [string]$Sectiune, [string]$Cheie) + $s = $Sectiune.ToLower() + $c = $Cheie.ToLower() + if ($Ini.ContainsKey($s) -and $Ini[$s].ContainsKey($c)) { return $Ini[$s][$c] } + '' +} + +function Rezolva-Alias { + # numele din host_database e un DSN ODBC; aliasul TNS e in cheia ServerName a DSN-ului + param([string]$Dsn) + foreach ($radacina in @('HKCU:\SOFTWARE\ODBC\ODBC.INI', 'HKLM:\SOFTWARE\ODBC\ODBC.INI', 'HKLM:\SOFTWARE\WOW6432Node\ODBC\ODBC.INI')) { + $cale = Join-Path $radacina $Dsn + if (Test-Path $cale) { + $v = (Get-ItemProperty -Path $cale -Name 'ServerName' -ErrorAction SilentlyContinue).ServerName + if ($v) { return $v } + } + } + # nu e DSN ODBC - il tratam ca alias TNS direct + $Dsn +} + +function Compara-Octeti { + param([byte[]]$A, [byte[]]$B) + if ($null -eq $A -or $null -eq $B) { return $false } + if ($A.Length -ne $B.Length) { return $false } + for ($i = 0; $i -lt $A.Length; $i++) { if ($A[$i] -ne $B[$i]) { return $false } } + $true +} + +function Split-Hex { + # taie un sir hexazecimal in bucati de $BUCATA octeti + param([string]$Hex) + $l = New-Object System.Collections.ArrayList + $pas = $BUCATA * 2 + for ($i = 0; $i -lt $Hex.Length; $i += $pas) { + [void]$l.Add($Hex.Substring($i, [Math]::Min($pas, $Hex.Length - $i))) + } + ,$l +} + +function Parseaza-NumeScript { + # _YYYY_MM_DD_NN_.sql -> obiect cu elementele componente (ca parsescriptname din VFP) + param([string]$Cale) + $stem = [System.IO.Path]::GetFileNameWithoutExtension($Cale) + if ($stem -notmatch '^([A-Za-z]+)_(\d{4})_(\d{2})_(\d{2})_(\d+)(?:_(.+))?$') { return $null } + $an = [int]$Matches[2]; $luna = [int]$Matches[3]; $zi = [int]$Matches[4] + try { $data = Get-Date -Year $an -Month $luna -Day $zi -Hour 0 -Minute 0 -Second 0 } catch { return $null } + $tip = '' + if ($Matches[6]) { $tip = $Matches[6].ToUpper() } + [PSCustomObject]@{ + Cale = $Cale + NumeScript = ($stem.ToUpper() + '.SQL') + Stem = $stem.ToUpper() + Prefix = $Matches[1].ToUpper() + Data = $data + DataYmd = $data.ToString('yyyyMMdd') + Secventa = [int]$Matches[5] + Tip = $tip + } +} + +function Citeste-MetadateLuna { + # metadatele scripturilor intregi (script_order = 0) din luna, in ordinea din arhiva + param([datetime]$Inceput, [datetime]$Sfarsit) + $sql = @" +set pagesize 0 linesize 600 trimspool on heading off +select 'M|' || id || '|' || script_name || '|' || script_type || '|' || to_char(script_date,'yyyymmdd') || + '|' || to_char(script_seq) || '|' || to_char(script_order) || '|' || nvl(script_appver,'') || + '|' || nvl(length(script_content),0) + from upd_database + where script_order = 0 + and script_date between to_date('$($Inceput.ToString('yyyyMMdd'))','yyyymmdd') and to_date('$($Sfarsit.ToString('yyyyMMdd'))','yyyymmdd') + order by script_name, id; +exit +"@ + $r = Invoke-SqlPlus -Sql $sql -Nume 'meta' + Test-Rezultat -R $r -Context 'citire metadate din UPD_DATABASE' + $l = New-Object System.Collections.ArrayList + foreach ($linie in ($r.Text -split "`r?`n")) { + $linie = $linie.Trim() + if (-not $linie.StartsWith('M|')) { continue } + $c = $linie.Substring(2) -split '\|' + if ($c.Count -lt 8) { continue } + [void]$l.Add([PSCustomObject]@{ + Id = [int]$c[0] + NumeScript = $c[1].Trim() + Stem = [System.IO.Path]::GetFileNameWithoutExtension($c[1].Trim()) + Tip = $c[2].Trim() + DataYmd = $c[3].Trim() + Secventa = [int]$c[4] + Ordine = [int]$c[5] + AppVer = $c[6].Trim() + Lungime = [int]$c[7] + }) + } + ,$l +} + +function Citeste-Continut { + # citeste inapoi CLOB-urile ca octeti CP1252 (hexazecimal), pentru toate id-urile date + param($Metadate) + $rezultat = @{} + if ($Metadate.Count -eq 0) { return $rezultat } + $maxBucati = 1 + foreach ($m in $Metadate) { + $rezultat[$m.Id] = New-Object System.Text.StringBuilder + $b = [Math]::Ceiling($m.Lungime / $BUCATA) + if ($b -gt $maxBucati) { $maxBucati = [int]$b } + } + $toateId = @($Metadate | ForEach-Object { $_.Id }) + $grupuri = New-Object System.Collections.ArrayList + for ($i = 0; $i -lt $toateId.Count; $i += 500) { + [void]$grupuri.Add('c.id in (' + (($toateId[$i..([Math]::Min($i + 499, $toateId.Count - 1))]) -join ',') + ')') + } + $ids = $grupuri -join ' or ' + $sql = @" +set pagesize 0 linesize 32767 long 2000000000 longchunksize 32767 trimspool on heading off arraysize 100 +select '#' || c.id || '#' || lpad(to_char(l.n),7,'0') || '#' || + rawtohex(utl_i18n.string_to_raw(dbms_lob.substr(c.script_content, $BUCATA, (l.n-1)*$BUCATA+1), 'WE8MSWIN1252')) + from upd_database c, + (select level n from dual connect by level <= $maxBucati) l + where ($ids) + and (l.n-1)*$BUCATA < nvl(length(c.script_content),0) + order by c.id, l.n; +exit +"@ + $r = Invoke-SqlPlus -Sql $sql -Nume 'citire' + Test-Rezultat -R $r -Context 'citire continut scripturi din UPD_DATABASE' + foreach ($linie in ($r.Text -split "`r?`n")) { + $linie = $linie.Trim() + if (-not $linie.StartsWith('#')) { continue } + $c = $linie.Substring(1) -split '#', 3 + if ($c.Count -lt 3) { continue } + $id = [int]$c[0] + if ($rezultat.ContainsKey($id)) { [void]$rezultat[$id].Append($c[2]) } + } + $final = @{} + foreach ($k in $rezultat.Keys) { $final[$k] = ConvertFrom-Hex $rezultat[$k].ToString() } + $final +} + +function Importa-Script { + # insert sau update pe script_name existent; continutul se trimite hexazecimal, bucata cu bucata + param($Info, [byte[]]$Octeti) + $hex = ConvertTo-Hex $Octeti + $bucati = Split-Hex $hex + $sb = New-Object System.Text.StringBuilder + [void]$sb.AppendLine('set serveroutput on size 1000000') + [void]$sb.AppendLine('variable b_id number') + [void]$sb.AppendLine('declare') + [void]$sb.AppendLine(' l_id number;') + [void]$sb.AppendLine('begin') + [void]$sb.AppendLine(" select max(id) into l_id from upd_database where script_name = '$($Info.NumeScript)';") + [void]$sb.AppendLine(' if l_id is null then') + [void]$sb.AppendLine(' insert into upd_database') + [void]$sb.AppendLine(' (script_name, script_type, script_date, script_seq, script_content, script_order, script_appver)') + [void]$sb.AppendLine(" values ('$($Info.NumeScript)', '$($Info.Tip)', to_date('$($Info.DataYmd)','yyyymmdd'), $($Info.Secventa), empty_clob(), 0, null)") + [void]$sb.AppendLine(' returning id into l_id;') + [void]$sb.AppendLine(' else') + [void]$sb.AppendLine(' update upd_database') + [void]$sb.AppendLine(" set script_type = '$($Info.Tip)',") + [void]$sb.AppendLine(" script_date = to_date('$($Info.DataYmd)','yyyymmdd'),") + [void]$sb.AppendLine(" script_seq = $($Info.Secventa),") + [void]$sb.AppendLine(' script_content = empty_clob(),') + [void]$sb.AppendLine(' script_order = 0,') + [void]$sb.AppendLine(' script_appver = null') + [void]$sb.AppendLine(' where id = l_id;') + [void]$sb.AppendLine(' end if;') + [void]$sb.AppendLine(' :b_id := l_id;') + [void]$sb.AppendLine(' commit;') + [void]$sb.AppendLine(" dbms_output.put_line('IDSCRIPT=' || l_id);") + [void]$sb.AppendLine('end;') + [void]$sb.AppendLine('/') + + for ($i = 0; $i -lt $bucati.Count; $i += $BUCATI_PER_BLOC) { + [void]$sb.AppendLine('declare s varchar2(32767); l clob;') + [void]$sb.AppendLine('begin') + [void]$sb.AppendLine(' select script_content into l from upd_database where id = :b_id for update;') + for ($j = $i; $j -lt [Math]::Min($i + $BUCATI_PER_BLOC, $bucati.Count); $j++) { + [void]$sb.AppendLine(" s := utl_i18n.raw_to_char(hextoraw('$($bucati[$j])'),'WE8MSWIN1252'); dbms_lob.writeappend(l, length(s), s);") + } + [void]$sb.AppendLine(' commit;') + [void]$sb.AppendLine('end;') + [void]$sb.AppendLine('/') + } + [void]$sb.AppendLine('exit') + + $r = Invoke-SqlPlus -Sql $sb.ToString() -Nume ('imp_' + $Info.Stem) + Test-Rezultat -R $r -Context ('import ' + $Info.NumeScript) + if ($r.Text -notmatch 'IDSCRIPT=(\d+)') { Opreste ('import ' + $Info.NumeScript + ' - nu am primit id-ul randului.') } + [int]$Matches[1] +} + +function Scrie-XmlArhiva { + # acelasi format ca CURSORTOXML din genereazadatabasexml2: Windows-1252, CRLF, tab-uri, + # script_appver doar cand e completat, script_content gol (continutul e in fisierul separat) + param($Metadate, [string]$Cale) + $sb = New-Object System.Text.StringBuilder + [void]$sb.Append("`r`n") + [void]$sb.Append("`r`n") + foreach ($m in $Metadate) { + [void]$sb.Append("`t`r`n") + [void]$sb.Append("`t`t$($m.Id)`r`n") + [void]$sb.Append("`t`t$([System.Security.SecurityElement]::Escape($m.NumeScript))`r`n") + [void]$sb.Append("`t`t$([System.Security.SecurityElement]::Escape($m.Tip))`r`n") + [void]$sb.Append("`t`t$($m.DataYmd)`r`n") + [void]$sb.Append("`t`t$($m.Secventa)`r`n") + [void]$sb.Append("`t`t$($m.Ordine)`r`n") + if ($m.AppVer) { + [void]$sb.Append("`t`t$([System.Security.SecurityElement]::Escape($m.AppVer))`r`n") + } + [void]$sb.Append("`t`t$($m.Stem).sql`r`n") + [void]$sb.Append("`t`t`r`n") + [void]$sb.Append("`t`r`n") + } + [void]$sb.Append("`r`n") + [System.IO.File]::WriteAllText($Cale, $sb.ToString(), [System.Text.Encoding]::GetEncoding(1252)) +} + +function Scrie-Arhiva { + # xml-ul primul, apoi scripturile in ordinea din xml (ca ZipOpen/ZipFile din VFP) + param([string]$CaleZip, [string]$CaleXml, $Fisiere) + if (Test-Path $CaleZip) { Remove-Item -LiteralPath $CaleZip -Force } + $z = [System.IO.Compression.ZipFile]::Open($CaleZip, 'Create') + try { + [void][System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($z, $CaleXml, [System.IO.Path]::GetFileName($CaleXml), 'Optimal') + foreach ($f in $Fisiere) { + [void][System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($z, $f, [System.IO.Path]::GetFileName($f), 'Optimal') + } + } finally { + $z.Dispose() + } +} + +function Scrie-IndexArhive { + # roa_database.xml / roa_databasen.xml - lista arhivelor existente, sortata dupa nume de fisier + param([string]$Prefix, [string]$Cale) + $arhive = @(Get-ChildItem -LiteralPath $ZipPath -Filter ($Prefix + '_*.zip') -File | + Where-Object { $_.Name -like ($Prefix + '_*.zip') } | Sort-Object Name) + $sb = New-Object System.Text.StringBuilder + [void]$sb.Append("`r`n") + [void]$sb.Append("`r`n") + foreach ($a in $arhive) { + $parti = [System.IO.Path]::GetFileNameWithoutExtension($a.Name) -split '_' + if ($parti.Count -lt 3) { continue } + [void]$sb.Append("`t`r`n") + [void]$sb.Append("`t`t|BASEPATH|/$($a.Name)`r`n") + [void]$sb.Append("`t`t$($parti[1])`r`n") + [void]$sb.Append("`t`t$($parti[2])`r`n") + [void]$sb.Append("`t`r`n") + } + [void]$sb.Append("`r`n") + [System.IO.File]::WriteAllText($Cale, $sb.ToString(), [System.Text.Encoding]::GetEncoding(1252)) +} + +function Verifica-ArhivaPrinOracle { + # citeste arhiva inapoi cu cititorul clientului (pack_utils.decodebase64 + zip_util_pkg.get_file) + # si compara lungimea + MD5 al fiecarui fisier extras cu cel de pe disc + param([string]$CaleZip, $FisiereAsteptate) + $octeti = [System.IO.File]::ReadAllBytes($CaleZip) + $b64 = [Convert]::ToBase64String($octeti) + $sb = New-Object System.Text.StringBuilder + [void]$sb.AppendLine('set serveroutput on size unlimited') + [void]$sb.AppendLine('set pagesize 0 linesize 400 trimspool on heading off') + [void]$sb.AppendLine('variable b_z clob') + [void]$sb.AppendLine('begin dbms_lob.createtemporary(:b_z, true); end;') + [void]$sb.AppendLine('/') + # bucati multiplu de 4, ca sirul base64 sa ramana valid oriunde s-ar taia + $pas = 1920 + for ($i = 0; $i -lt $b64.Length; $i += $pas * $BUCATI_PER_BLOC) { + [void]$sb.AppendLine('declare s varchar2(32767);') + [void]$sb.AppendLine('begin') + for ($j = $i; $j -lt [Math]::Min($i + $pas * $BUCATI_PER_BLOC, $b64.Length); $j += $pas) { + $bucata = $b64.Substring($j, [Math]::Min($pas, $b64.Length - $j)) + [void]$sb.AppendLine(" s := '$bucata'; dbms_lob.writeappend(:b_z, length(s), s);") + } + [void]$sb.AppendLine('end;') + [void]$sb.AppendLine('/') + } + [void]$sb.AppendLine('declare') + [void]$sb.AppendLine(' l_zip blob; l_f blob;') + [void]$sb.AppendLine('begin') + [void]$sb.AppendLine(' l_zip := pack_utils.decodebase64(:b_z);') + [void]$sb.AppendLine(" dbms_output.put_line('ZIP|' || dbms_lob.getlength(l_zip));") + foreach ($f in $FisiereAsteptate) { + [void]$sb.AppendLine(" l_f := zip_util_pkg.get_file(l_zip, '$f');") + [void]$sb.AppendLine(" if l_f is null then dbms_output.put_line('LIPSA|$f');") + [void]$sb.AppendLine(" else dbms_output.put_line('FIS|$f|' || dbms_lob.getlength(l_f) || '|' || rawtohex(dbms_crypto.hash(l_f, 2))); end if;") + } + [void]$sb.AppendLine('end;') + [void]$sb.AppendLine('/') + [void]$sb.AppendLine('exit') + + $r = Invoke-SqlPlus -Sql $sb.ToString() -Nume 'verifzip' + Test-Rezultat -R $r -Context ('verificare arhiva ' + [System.IO.Path]::GetFileName($CaleZip)) + $citite = @{} + foreach ($linie in ($r.Text -split "`r?`n")) { + $linie = $linie.Trim() + if ($linie -match '^FIS\|([^|]+)\|(\d+)\|([0-9A-F]+)$') { + $citite[$Matches[1]] = [PSCustomObject]@{ Lungime = [int]$Matches[2]; Md5 = $Matches[3] } + } elseif ($linie -match '^LIPSA\|(.+)$') { + Opreste ('verificare arhiva - zip_util_pkg nu gaseste ' + $Matches[1] + ' in ' + $CaleZip) + } + } + $md5 = [System.Security.Cryptography.MD5]::Create() + $erori = 0 + foreach ($f in $FisiereAsteptate) { + if (-not $citite.ContainsKey($f)) { Write-Output (' LIPSA ' + $f); $erori++; continue } + $local = Join-Path $TempDir $f + $b = [System.IO.File]::ReadAllBytes($local) + $h = (ConvertTo-Hex $md5.ComputeHash($b)) + if ($citite[$f].Lungime -eq $b.Length -and $citite[$f].Md5 -eq $h) { + Write-Output (' ok ' + $f + ' (' + $b.Length + ' octeti)') + } else { + Write-Output (' DIFERIT ' + $f + ' - disc ' + $b.Length + '/' + $h + ', extras ' + $citite[$f].Lungime + '/' + $citite[$f].Md5) + $erori++ + } + } + $md5.Dispose() + if ($erori -gt 0) { Opreste ('verificare arhiva - ' + $erori + ' fisiere nu se citesc corect prin zip_util_pkg.') } +} + +# ---------- pregatire ---------- +$TempDir = Join-Path $env:TEMP ('publicare_scripturi_' + [guid]::NewGuid().ToString('N')) +New-Item -ItemType Directory -Force -Path $TempDir | Out-Null + +if (-not (Test-Path -LiteralPath $Settings)) { Opreste ('nu gasesc settings.ini la ' + $Settings + ' - da caile cu -DirScripturi/-ZipPath/-Alias/-User/-Password sau alt -Settings.') } +$ini = Citeste-Ini $Settings + +# parametrii dati explicit bat ini-ul +if (-not $PSBoundParameters.ContainsKey('User')) { $User = Ia-Cheie $ini 'connection' 'username_database' } +if (-not $PSBoundParameters.ContainsKey('Password')) { $Password = Ia-Cheie $ini 'connection' 'password_database' } +if (-not $PSBoundParameters.ContainsKey('Alias')) { + $dsn = Ia-Cheie $ini 'connection' 'host_database' + if ($dsn) { $Alias = Rezolva-Alias $dsn } +} +if (-not $PSBoundParameters.ContainsKey('DirScripturi')) { + $f = Ia-Cheie $ini 'folder' 'script_folder' + if ($f) { $DirScripturi = ($f.TrimEnd('\') -replace '\\SCRIPTURI$', '\SCRIPTURI_CLAR') } +} +if (-not $PSBoundParameters.ContainsKey('ZipPath')) { + $f = Ia-Cheie $ini 'folder' 'roa_output' + if ($f) { $ZipPath = (Join-Path $f '_UPDATE') } +} +if (-not $PSBoundParameters.ContainsKey('SqlPlus')) { $SqlPlus = Ia-Cheie $ini 'folder' 'sqlplus_exe' } +foreach ($p in @(@('User', $User), @('Password', $Password), @('Alias', $Alias), @('DirScripturi', $DirScripturi), @('ZipPath', $ZipPath), @('SqlPlus', $SqlPlus))) { + if (-not $p[1]) { Opreste ('lipseste ' + $p[0] + ' - nu e in ' + $Settings + ' si nu a fost dat ca parametru.') } +} +# prefixele valide (o schema tinta per prefix), pentru validarea numelor de scripturi +$prefixe = @() +if ($ini.ContainsKey('script')) { $prefixe = @($ini['script'].Values | ForEach-Object { $_.TrimEnd('_').ToUpper() }) } + +if (-not (Test-Path $SqlPlus)) { Opreste ('nu gasesc sqlplus la ' + $SqlPlus) } +$env:TNS_ADMIN = Split-Path $SqlPlus -Parent +$ConnStr = $User + '/' + $Password + '@' + $Alias + +if ($DoarImport -and $DoarArhiva) { Opreste '-DoarImport si -DoarArhiva se exclud reciproc.' } + +# ---------- 1. ce se publica ---------- +Scrie-Titlu '1. SCRIPTURI DE PUBLICAT' +$candidati = New-Object System.Collections.ArrayList +if ($Fisier) { + foreach ($f in $Fisier) { + $cale = $f + if (-not [System.IO.Path]::IsPathRooted($cale)) { + $cale = (Resolve-Path -LiteralPath $f -ErrorAction SilentlyContinue) + if (-not $cale) { Opreste ('nu gasesc fisierul ' + $f) } + $cale = $cale.Path + } + if (-not (Test-Path -LiteralPath $cale)) { Opreste ('nu gasesc fisierul ' + $cale) } + $info = Parseaza-NumeScript $cale + if (-not $info) { Opreste ('nume de script neconform (_YYYY_MM_DD_NN_.sql): ' + $cale) } + [void]$candidati.Add($info) + } + $luni = @($candidati | ForEach-Object { $_.Data.ToString('yyyy-MM') } | Sort-Object -Unique) + if ($luni.Count -gt 1) { Opreste ('scripturile date sunt din luni diferite (' + ($luni -join ', ') + ') - ruleaza cate o luna.') } + if (-not $Luna) { $Luna = $luni[0] } +} else { + if (-not $Luna) { $Luna = (Get-Date).ToString('yyyy-MM') } +} + +if ($Luna -notmatch '^(\d{4})-(\d{2})$') { Opreste ('-Luna trebuie in formatul YYYY-MM (primit: ' + $Luna + ')') } +$anLuna = [int]$Matches[1] +$lunaLuna = [int]$Matches[2] +$dataInceput = Get-Date -Year $anLuna -Month $lunaLuna -Day 1 -Hour 0 -Minute 0 -Second 0 +$dataSfarsit = $dataInceput.AddMonths(1).AddDays(-1) +$etichetaLuna = $dataInceput.ToString('yyyyMMdd') + '_' + $dataSfarsit.ToString('yyyyMMdd') + +$dirLuna = Join-Path (Join-Path $DirScripturi $dataInceput.ToString('yyyy')) $dataInceput.ToString('MM') +if (-not $Fisier -and -not $DoarArhiva) { + if (-not (Test-Path -LiteralPath $dirLuna)) { Opreste ('nu gasesc directorul lunii: ' + $dirLuna) } + foreach ($f in (Get-ChildItem -LiteralPath $dirLuna -Filter '*.sql' -File | Sort-Object Name)) { + $info = Parseaza-NumeScript $f.FullName + if (-not $info) { Write-Output (' [sarit] nume neconform: ' + $f.Name); continue } + [void]$candidati.Add($info) + } +} + +Write-Output ('Luna: ' + $Luna + ' arhiva: ' + $etichetaLuna) +Write-Output ('Configurare: ' + $Settings) +Write-Output ('Baza: ' + $User + '@' + $Alias + ' (' + $SqlPlus + ')') +Write-Output ('Scripturi: ' + $dirLuna) +Write-Output ('Arhive: ' + $ZipPath) + +foreach ($c in $candidati) { + if ($prefixe.Count -gt 0 -and $prefixe -notcontains $c.Prefix) { + Write-Output (' [atentie] prefix necunoscut "' + $c.Prefix + '" in ' + [System.IO.Path]::GetFileName($c.Cale) + ' (in ini: ' + ($prefixe -join ', ') + ')') + } + $octeti = [System.IO.File]::ReadAllBytes($c.Cale) + if ($octeti.Length -eq 0) { Opreste ('fisier gol: ' + $c.Cale) } + $text = [System.Text.Encoding]::GetEncoding(1252).GetString($octeti) + if ($text -match "(? id ' + $id) + } + $metadate = Citeste-MetadateLuna -Inceput $dataInceput -Sfarsit $dataSfarsit + $dupaNume = @{} + foreach ($m in $metadate) { $dupaNume[$m.NumeScript] = $m } + $verif = Citeste-Continut -Metadate @($deImportat | ForEach-Object { $dupaNume[$_.NumeScript] }) + $erori = 0 + foreach ($c in $deImportat) { + $m = $dupaNume[$c.NumeScript] + $inBaza = $verif[$m.Id] + if (Compara-Octeti -A $inBaza -B $c.Octeti) { + Write-Output (' verificat ' + $c.NumeScript + ' (' + $c.Octeti.Length + ' octeti, identic cu fisierul)') + } else { + Write-Output (' DIFERIT ' + $c.NumeScript + ' - continutul din baza nu coincide cu fisierul!') + $erori++ + } + } + if ($erori -gt 0) { Opreste ($erori + ' scripturi nu s-au scris corect in baza - nu generez arhiva.') } + } +} + +if ($DoarImport) { + Scrie-Titlu 'GATA (doar import)' + Write-Output 'Arhiva NU a fost regenerata - clientii primesc scripturile abia dupa pasul de arhiva.' + Curata-Temp + exit 0 +} + +# ---------- 4. arhiva lunara ---------- +Scrie-Titlu '4. ARHIVA LUNARA' +if (-not (Test-Path -LiteralPath $ZipPath)) { Opreste ('nu gasesc directorul arhivelor: ' + $ZipPath) } +if ($DryRun) { + Write-Output ('DryRun - as regenera database_' + $etichetaLuna + '.zip si databasen_' + $etichetaLuna + '.zip din ' + $metadate.Count + ' scripturi.') + Curata-Temp + exit 0 +} + +$metadate = Citeste-MetadateLuna -Inceput $dataInceput -Sfarsit $dataSfarsit +if ($metadate.Count -eq 0) { Opreste ('nu exista scripturi in UPD_DATABASE pentru luna ' + $Luna + ' - nu am ce arhiva.') } + +$continuturi = Citeste-Continut -Metadate $metadate +$fisiereSql = New-Object System.Collections.ArrayList +foreach ($m in $metadate) { + $b = $continuturi[$m.Id] + if (-not $b -or $b.Length -ne $m.Lungime) { + Opreste ('citire incompleta pentru ' + $m.NumeScript + ' (asteptat ' + $m.Lungime + ' octeti, citit ' + $(if ($b) { $b.Length } else { 0 }) + ').') + } + $numeFis = $m.Stem + '.sql' + [System.IO.File]::WriteAllBytes((Join-Path $TempDir $numeFis), $b) + [void]$fisiereSql.Add($numeFis) +} +Write-Output ('Extrase din baza: ' + $fisiereSql.Count + ' scripturi.') + +$numeFisiere = @($fisiereSql) +$rezultat = @() +foreach ($prefix in @('database', 'databasen')) { + $numeXml = $prefix + '_' + $etichetaLuna + '.xml' + $caleXml = Join-Path $TempDir $numeXml + Scrie-XmlArhiva -Metadate $metadate -Cale $caleXml + $caleZip = Join-Path $TempDir ($prefix + '_' + $etichetaLuna + '.zip') + Scrie-Arhiva -CaleZip $caleZip -CaleXml $caleXml -Fisiere @($numeFisiere | ForEach-Object { Join-Path $TempDir $_ }) + Write-Output ('Generat ' + [System.IO.Path]::GetFileName($caleZip) + ' (' + (Get-Item -LiteralPath $caleZip).Length + ' octeti)') + $rezultat += [PSCustomObject]@{ Zip = $caleZip; Xml = $numeXml } +} + +# ---------- 5. verificare cu cititorul clientului ---------- +if (-not $FaraVerificareZip) { + Scrie-Titlu '5. VERIFICARE ARHIVA PRIN ZIP_UTIL_PKG' + Verifica-ArhivaPrinOracle -CaleZip $rezultat[0].Zip -FisiereAsteptate (@($rezultat[0].Xml) + $numeFisiere) +} else { + Scrie-Titlu '5. VERIFICARE ARHIVA PRIN ZIP_UTIL_PKG' + Write-Output 'Sarit (-FaraVerificareZip).' +} + +# ---------- 6. publicare in _UPDATE ---------- +Scrie-Titlu '6. PUBLICARE IN _UPDATE' +if (-not $BackupPath) { $BackupPath = Join-Path $ZipPath ('_backup\' + (Get-Date).ToString('yyyyMMdd_HHmmss')) } +New-Item -ItemType Directory -Force -Path $BackupPath | Out-Null +foreach ($n in @(($rezultat | ForEach-Object { [System.IO.Path]::GetFileName($_.Zip) }) + 'roa_database.xml' + 'roa_databasen.xml')) { + $tinta = Join-Path $ZipPath $n + if (Test-Path -LiteralPath $tinta) { + Copy-Item -LiteralPath $tinta -Destination (Join-Path $BackupPath $n) -Force + Write-Output (' salvat ' + $n + ' -> ' + $BackupPath) + } +} +foreach ($r in $rezultat) { + Copy-Item -LiteralPath $r.Zip -Destination (Join-Path $ZipPath ([System.IO.Path]::GetFileName($r.Zip))) -Force + Write-Output (' publicat ' + [System.IO.Path]::GetFileName($r.Zip)) +} +Scrie-IndexArhive -Prefix 'database' -Cale (Join-Path $ZipPath 'roa_database.xml') +Scrie-IndexArhive -Prefix 'databasen' -Cale (Join-Path $ZipPath 'roa_databasen.xml') +Write-Output ' publicat roa_database.xml, roa_databasen.xml' + +Scrie-Titlu 'GATA' +Write-Output ('Luna ' + $Luna + ': ' + $metadate.Count + ' scripturi in arhiva, ' + $deImportat.Count + ' importate acum.') +Write-Output ('Backup-ul fisierelor inlocuite: ' + $BackupPath) +Curata-Temp +exit 0 From 26d2c205c91d7e5eeeaa9231e0f6111fb1636482 Mon Sep 17 00:00:00 2001 From: Marius Mutu Date: Mon, 3 Aug 2026 23:44:27 +0300 Subject: [PATCH 2/9] NNIR editabil in Registrul Jurnal (frm_modific2024) + teste --- clase/omodificari.vc2 | 180 +++- docs/capcana_grid_preferinte_utilizator.md | 17 + docs/flux-editare-vfp-text.md | 4 + docs/flux-modificare-stergere-nota-jurnal.md | 53 ++ docs/reguli_lucru.md | 2 + .../nnir_registru_jurnal/date_test_nnir.md | 121 +++ .../test_nnir_sincronizare.prg | 837 ++++++++++++++++++ .../nnir_registru_jurnal/verif_nnir_cod.sql | 11 + utile/Teste/test_init_env_auto_roagest.prg | 302 +++++++ 9 files changed, 1488 insertions(+), 39 deletions(-) create mode 100644 docs/capcana_grid_preferinte_utilizator.md create mode 100644 docs/flux-modificare-stergere-nota-jurnal.md create mode 100644 utile/Teste/nnir_registru_jurnal/date_test_nnir.md create mode 100644 utile/Teste/nnir_registru_jurnal/test_nnir_sincronizare.prg create mode 100644 utile/Teste/nnir_registru_jurnal/verif_nnir_cod.sql create mode 100644 utile/Teste/test_init_env_auto_roagest.prg diff --git a/clase/omodificari.vc2 b/clase/omodificari.vc2 index 2b930e6..1f09ead 100644 --- a/clase/omodificari.vc2 +++ b/clase/omodificari.vc2 @@ -6720,6 +6720,8 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" *< OBJECTDATA: ObjPath="Grid1.Column43.Header1" UniqueID="" Timestamp="" /> *< OBJECTDATA: ObjPath="Grid1.Column43.Text1" UniqueID="" Timestamp="" /> *< OBJECTDATA: ObjPath="Grid1.Column43._combobox1" UniqueID="" Timestamp="" /> + *< OBJECTDATA: ObjPath="Grid1.Column44.Header1" UniqueID="" Timestamp="" /> + *< OBJECTDATA: ObjPath="Grid1.Column44.Text1" UniqueID="" Timestamp="" /> *< OBJECTDATA: ObjPath="Command1" UniqueID="" Timestamp="" /> *< OBJECTDATA: ObjPath="Gridextra1" UniqueID="" Timestamp="" /> *< OBJECTDATA: ObjPath="Command2" UniqueID="" Timestamp="" /> @@ -6762,6 +6764,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" *m: grid_focus_prima_coloana *m: resize_grid1 *m: salveazaxml + *m: sincronizeaza_nnir *m: updateexplicatiesaft *m: updateexplicatiesaftrul *m: valideaza_data @@ -6778,6 +6781,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" *p: lverificare *p: lvizualizare *p: nid_set + *p: nnnirvechi *p: noldsuma && Valoarea la intrarea in campul "Suma" *p: nprimadata *p: nsuma && Total suma inregistrari @@ -6809,6 +6813,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" lvizualizare = .F. Name = "frm_modific2024" nid_set = 0 + nnnirvechi = 0 noldsuma = 0 nprimadata = 0 nsuma = 0 @@ -7143,7 +7148,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" ADD OBJECT 'Grid1' AS grid WITH ; Anchor = 10, ; BackColor = 244,250,255, ; - ColumnCount = 43, ; + ColumnCount = 44, ; DeleteMark = .F., ; FontCharSet = 238, ; FontSize = 10, ; @@ -7185,7 +7190,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column2.Width = 71, ; Column3.Alignment = 0, ; Column3.BackColor = 244,250,255, ; - Column3.ColumnOrder = 15, ; + Column3.ColumnOrder = 16, ; Column3.ControlSource = "fdoc", ; Column3.FontCharSet = 238, ; Column3.FontName = "Arial", ; @@ -7215,7 +7220,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column5.Width = 53, ; Column6.Alignment = 0, ; Column6.BackColor = 244,250,255, ; - Column6.ColumnOrder = 7, ; + Column6.ColumnOrder = 8, ; Column6.ControlSource = "partd", ; Column6.FontCharSet = 238, ; Column6.FontName = "Arial", ; @@ -7225,7 +7230,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column6.Width = 114, ; Column7.Alignment = 1, ; Column7.BackColor = 244,250,255, ; - Column7.ColumnOrder = 10, ; + Column7.ColumnOrder = 11, ; Column7.ControlSource = "scd", ; Column7.FontCharSet = 238, ; Column7.FontName = "Arial", ; @@ -7235,7 +7240,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column7.Width = 57, ; Column8.Alignment = 1, ; Column8.BackColor = 244,250,255, ; - Column8.ColumnOrder = 12, ; + Column8.ColumnOrder = 13, ; Column8.ControlSource = "scc", ; Column8.FontCharSet = 238, ; Column8.FontName = "Arial", ; @@ -7245,7 +7250,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column8.Width = 62, ; Column9.Alignment = 1, ; Column9.BackColor = 244,250,255, ; - Column9.ColumnOrder = 14, ; + Column9.ColumnOrder = 15, ; Column9.ControlSource = "suma", ; Column9.DynamicForeColor = "IIF(SUMA>=0, RGB(0,0,0), RGB(255,0,0))", ; Column9.FontCharSet = 238, ; @@ -7258,7 +7263,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column9.Width = 93, ; Column10.Alignment = 0, ; Column10.BackColor = 244,250,255, ; - Column10.ColumnOrder = 8, ; + Column10.ColumnOrder = 9, ; Column10.ControlSource = "partc", ; Column10.FontCharSet = 238, ; Column10.FontName = "Arial", ; @@ -7268,7 +7273,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column10.Width = 100, ; Column11.Alignment = 0, ; Column11.BackColor = 244,250,255, ; - Column11.ColumnOrder = 9, ; + Column11.ColumnOrder = 10, ; Column11.ControlSource = "explicatia", ; Column11.FontCharSet = 238, ; Column11.FontName = "Arial", ; @@ -7277,7 +7282,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column11.ReadOnly = .F., ; Column11.Width = 77, ; Column12.BackColor = 244,250,255, ; - Column12.ColumnOrder = 37, ; + Column12.ColumnOrder = 38, ; Column12.ControlSource = "cod", ; Column12.FontCharSet = 238, ; Column12.FontName = "Arial", ; @@ -7286,7 +7291,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column12.ReadOnly = .T., ; Column12.Width = 52, ; Column13.BackColor = 244,250,255, ; - Column13.ColumnOrder = 16, ; + Column13.ColumnOrder = 17, ; Column13.ControlSource = "pereched", ; Column13.FontCharSet = 238, ; Column13.FontName = "Arial", ; @@ -7295,7 +7300,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column13.ReadOnly = .F., ; Column13.Width = 106, ; Column14.BackColor = 244,250,255, ; - Column14.ColumnOrder = 31, ; + Column14.ColumnOrder = 32, ; Column14.ControlSource = "explicatia4", ; Column14.FontCharSet = 238, ; Column14.FontName = "Arial", ; @@ -7304,7 +7309,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column14.ReadOnly = .F., ; Column14.Width = 120, ; Column15.BackColor = 244,250,255, ; - Column15.ColumnOrder = 24, ; + Column15.ColumnOrder = 25, ; Column15.ControlSource = "nrord", ; Column15.FontCharSet = 238, ; Column15.FontName = "Arial", ; @@ -7312,7 +7317,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column15.Name = "Column15", ; Column15.ReadOnly = .F., ; Column16.BackColor = 244,250,255, ; - Column16.ColumnOrder = 26, ; + Column16.ColumnOrder = 27, ; Column16.ControlSource = "nresp", ; Column16.FontCharSet = 238, ; Column16.FontName = "Arial", ; @@ -7322,7 +7327,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column16.Name = "Column16", ; Column16.ReadOnly = .F., ; Column17.BackColor = 244,250,255, ; - Column17.ColumnOrder = 27, ; + Column17.ColumnOrder = 28, ; Column17.ControlSource = "dst_chlt", ; Column17.FontCharSet = 238, ; Column17.FontName = "Arial", ; @@ -7332,7 +7337,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column17.Name = "Column17", ; Column17.ReadOnly = .F., ; Column18.BackColor = 244,250,255, ; - Column18.ColumnOrder = 32, ; + Column18.ColumnOrder = 33, ; Column18.ControlSource = "explicatia5", ; Column18.FontCharSet = 238, ; Column18.FontName = "Arial", ; @@ -7341,7 +7346,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column18.ReadOnly = .F., ; Column18.Width = 119, ; Column19.BackColor = 244,250,255, ; - Column19.ColumnOrder = 23, ; + Column19.ColumnOrder = 24, ; Column19.ControlSource = "datascad", ; Column19.FontCharSet = 238, ; Column19.FontName = "Arial", ; @@ -7351,7 +7356,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column19.Visible = .T., ; Column19.Width = 87, ; Column20.BackColor = 244,250,255, ; - Column20.ColumnOrder = 28, ; + Column20.ColumnOrder = 29, ; Column20.ControlSource = "suma_val", ; Column20.FontCharSet = 238, ; Column20.FontName = "Arial", ; @@ -7361,7 +7366,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column20.ReadOnly = .F., ; Column20.Width = 89, ; Column21.BackColor = 244,250,255, ; - Column21.ColumnOrder = 30, ; + Column21.ColumnOrder = 31, ; Column21.ControlSource = "curs", ; Column21.FontCharSet = 238, ; Column21.FontName = "Arial", ; @@ -7371,7 +7376,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column21.ReadOnly = .F., ; Column21.Width = 72, ; Column22.BackColor = 244,250,255, ; - Column22.ColumnOrder = 33, ; + Column22.ColumnOrder = 34, ; Column22.ControlSource = "gestin", ; Column22.FontCharSet = 238, ; Column22.FontName = "Arial", ; @@ -7380,7 +7385,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column22.ReadOnly = .F., ; Column22.Width = 116, ; Column23.BackColor = 244,250,255, ; - Column23.ColumnOrder = 11, ; + Column23.ColumnOrder = 12, ; Column23.ControlSource = "ascd", ; Column23.FontCharSet = 238, ; Column23.FontName = "Arial", ; @@ -7389,7 +7394,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column23.ReadOnly = .F., ; Column23.Width = 46, ; Column24.BackColor = 244,250,255, ; - Column24.ColumnOrder = 13, ; + Column24.ColumnOrder = 14, ; Column24.ControlSource = "ascc", ; Column24.FontCharSet = 238, ; Column24.FontName = "Arial", ; @@ -7398,7 +7403,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column24.ReadOnly = .F., ; Column24.Width = 48, ; Column25.BackColor = 244,250,255, ; - Column25.ColumnOrder = 38, ; + Column25.ColumnOrder = 39, ; Column25.ControlSource = "id_set", ; Column25.FontCharSet = 238, ; Column25.FontName = "Arial", ; @@ -7407,7 +7412,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column25.ReadOnly = .T., ; Column25.Width = 71, ; Column26.BackColor = 244,250,255, ; - Column26.ColumnOrder = 19, ; + Column26.ColumnOrder = 20, ; Column26.ControlSource = "proc_tva", ; Column26.FontCharSet = 238, ; Column26.FontName = "Arial", ; @@ -7416,7 +7421,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column26.ReadOnly = .T., ; Column26.Width = 54, ; Column27.BackColor = 244,250,255, ; - Column27.ColumnOrder = 17, ; + Column27.ColumnOrder = 18, ; Column27.ControlSource = "perechec", ; Column27.FontCharSet = 238, ; Column27.FontName = "Arial", ; @@ -7425,7 +7430,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column27.ReadOnly = .F., ; Column27.Width = 104, ; Column28.BackColor = 244,250,255, ; - Column28.ColumnOrder = 35, ; + Column28.ColumnOrder = 36, ; Column28.ControlSource = "sectie", ; Column28.FontCharSet = 238, ; Column28.FontName = "Arial", ; @@ -7434,7 +7439,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column28.ReadOnly = .F., ; Column28.Width = 88, ; Column29.BackColor = 244,250,255, ; - Column29.ColumnOrder = 29, ; + Column29.ColumnOrder = 30, ; Column29.ControlSource = "nume_val", ; Column29.FontCharSet = 238, ; Column29.FontName = "Arial", ; @@ -7443,7 +7448,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column29.ReadOnly = .F., ; Column29.Width = 79, ; Column30.BackColor = 244,250,255, ; - Column30.ColumnOrder = 39, ; + Column30.ColumnOrder = 40, ; Column30.ControlSource = "id_partd", ; Column30.FontCharSet = 238, ; Column30.FontName = "Arial", ; @@ -7452,7 +7457,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column30.ReadOnly = .T., ; Column30.Width = 55, ; Column31.BackColor = 244,250,255, ; - Column31.ColumnOrder = 40, ; + Column31.ColumnOrder = 41, ; Column31.ControlSource = "id_partc", ; Column31.FontCharSet = 238, ; Column31.FontName = "Arial", ; @@ -7461,7 +7466,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column31.ReadOnly = .T., ; Column31.Width = 55, ; Column32.BackColor = 244,250,255, ; - Column32.ColumnOrder = 34, ; + Column32.ColumnOrder = 35, ; Column32.ControlSource = "gestout", ; Column32.FontCharSet = 238, ; Column32.FontName = "Arial", ; @@ -7470,7 +7475,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column32.ReadOnly = .F., ; Column32.Width = 92, ; Column33.BackColor = 244,250,255, ; - Column33.ColumnOrder = 36, ; + Column33.ColumnOrder = 37, ; Column33.ControlSource = "sucursala", ; Column33.FontCharSet = 238, ; Column33.FontName = "Arial", ; @@ -7479,7 +7484,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column33.ReadOnly = .F., ; Column33.Width = 121, ; Column34.BackColor = 244,250,255, ; - Column34.ColumnOrder = 25, ; + Column34.ColumnOrder = 26, ; Column34.ControlSource = "contract", ; Column34.FontCharSet = 238, ; Column34.FontName = "Arial", ; @@ -7488,7 +7493,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column34.ReadOnly = .T., ; Column34.Width = 65, ; Column35.BackColor = 244,250,255, ; - Column35.ColumnOrder = 20, ; + Column35.ColumnOrder = 21, ; Column35.ControlSource = "explicatie_tva", ; Column35.FontCharSet = 238, ; Column35.FontName = "Arial", ; @@ -7497,7 +7502,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column35.ReadOnly = .F., ; Column35.Width = 136, ; Column36.BackColor = 244,250,255, ; - Column36.ColumnOrder = 18, ; + Column36.ColumnOrder = 19, ; Column36.ControlSource = "cu_tva", ; Column36.FontCharSet = 238, ; Column36.FontName = "Arial", ; @@ -7517,7 +7522,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column37.ReadOnly = .F., ; Column37.Width = 58, ; Column38.BackColor = 244,250,255, ; - Column38.ColumnOrder = 41, ; + Column38.ColumnOrder = 42, ; Column38.ControlSource = "id_fact", ; Column38.FontCharSet = 238, ; Column38.FontName = "Arial", ; @@ -7526,7 +7531,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column38.ReadOnly = .T., ; Column38.Width = 94, ; Column39.BackColor = 244,250,255, ; - Column39.ColumnOrder = 42, ; + Column39.ColumnOrder = 43, ; Column39.ControlSource = "id_factd", ; Column39.FontCharSet = 238, ; Column39.FontName = "Arial", ; @@ -7535,7 +7540,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column39.ReadOnly = .T., ; Column39.Width = 75, ; Column40.BackColor = 244,250,255, ; - Column40.ColumnOrder = 43, ; + Column40.ColumnOrder = 44, ; Column40.ControlSource = "id_factc", ; Column40.FontCharSet = 238, ; Column40.FontName = "Arial", ; @@ -7555,7 +7560,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column41.Width = 22, ; Column42.BackColor = 244,250,255, ; Column42.Bound = .F., ; - Column42.ColumnOrder = 21, ; + Column42.ColumnOrder = 22, ; Column42.ControlSource = "Iif(Seek(tAct.taxcode,'saft_taxtable','taxcode'),saft_taxtable.taxname,'')", ; Column42.CurrentControl = "_combobox1", ; Column42.FontCharSet = 238, ; @@ -7566,7 +7571,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column42.Width = 184, ; Column43.BackColor = 244,250,255, ; Column43.Bound = .F., ; - Column43.ColumnOrder = 22, ; + Column43.ColumnOrder = 23, ; Column43.ControlSource = "Iif(Seek(tAct.paymentcode,'saft_mecanisme_plati','mcode'),saft_mecanisme_plati.paymentname,'')", ; Column43.CurrentControl = "_combobox1", ; Column43.FontCharSet = 238, ; @@ -7574,7 +7579,17 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column43.Name = "Column43", ; Column43.ReadOnly = .F., ; Column43.Sparse = .F., ; - Column43.Width = 146 + Column43.Width = 146, ; + Column44.Alignment = 1, ; + Column44.BackColor = 244,250,255, ; + Column44.ColumnOrder = 7, ; + Column44.ControlSource = "nnir", ; + Column44.FontCharSet = 238, ; + Column44.FontName = "Arial", ; + Column44.FontSize = 10, ; + Column44.Name = "Column44", ; + Column44.ReadOnly = .F., ; + Column44.Width = 60 *< END OBJECT: BaseClass="grid" /> ADD OBJECT 'Grid1.cAles._CHECKBOX1' AS _checkbox WITH ; @@ -8442,6 +8457,29 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" SelectedBackColor = 49,106,197 *< END OBJECT: BaseClass="textbox" /> + ADD OBJECT 'Grid1.Column44.Header1' AS header WITH ; + Alignment = 2, ; + Caption = "Nr. NIR", ; + FontCharSet = 238, ; + FontName = "Arial", ; + FontSize = 10, ; + Name = "Header1" + *< END OBJECT: BaseClass="header" /> + + ADD OBJECT 'Grid1.Column44.Text1' AS textbox WITH ; + Alignment = 1, ; + BackColor = 244,250,255, ; + BorderStyle = 0, ; + FontCharSet = 238, ; + FontName = "Arial", ; + FontSize = 10, ; + ForeColor = 0,0,0, ; + Margin = 0, ; + Name = "Text1", ; + ReadOnly = .F., ; + SelectedBackColor = 49,106,197 + *< END OBJECT: BaseClass="textbox" /> + ADD OBJECT 'Grid1.Column5.Header5' AS header WITH ; Alignment = 2, ; Caption = "Nr.Doc.", ; @@ -13613,6 +13651,9 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" This.grid1.Anchor = 11 This.but_renunt1.Anchor = 8 + If This.nid_set = 90101 + This.grid1.Column44.ReadOnly = .T. + Endif ENDPROC PROCEDURE Load @@ -13683,6 +13724,19 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" this.gridextra1.setup() + LOCAL lcPrefFile, lcPrefFileContents, lcKey, lcPrefs + LOCAL ARRAY laNnirPrefs(1) + lcPrefFile = This.gridextra1.GetUserApplicationDataPath() + This.gridextra1.GridPreferenceFile + lcPrefFileContents = "" + IF FILE(m.lcPrefFile) + lcPrefFileContents = FILETOSTR(m.lcPrefFile) + ENDIF + lcKey = SYS(1272, This.grid1) + "(" + lcPrefs = STREXTRACT(m.lcPrefFileContents, m.lcKey, ")", 1, 1) + *!* pozitioneaza coloana la prima deschidere, cat timp preferintele salvate nu o acopera + IF EMPTY(m.lcPrefs) OR ALINES(laNnirPrefs, m.lcPrefs, 7, ",") < This.grid1.ColumnCount * 2 + This.grid1.Column44.ColumnOrder = 7 + ENDIF this.grid1.SetAll("dynamicbackcolor", "IIF(ALES, RGB(255,255,225), RGB(255,255,255))", "Column") This.CalculeazaTotal() *!* 24.05.2011 ^ @@ -13717,6 +13771,37 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" ENDPROC + PROCEDURE sincronizeaza_nnir + *!* propaga NNIR-ul modificat pe toate liniile notei si pe rulajele asociate + LPARAMETERS tnNnirVechi, tnNnirNou + LOCAL lcSelect, lnRecno + IF NVL(m.tnNnirVechi, 0) = NVL(m.tnNnirNou, 0) + RETURN + ENDIF + lcSelect = SELECT() + lnRecno = RECNO('tact') + UPDATE tact SET nnir = m.tnNnirNou WHERE NVL(nnir, 0) = m.tnNnirVechi + IF USED('trul') + UPDATE trul SET nnir = m.tnNnirNou WHERE NVL(nnir, 0) = m.tnNnirVechi + IF _TALLY > 0 + Thisform.pgfArticole.page1.grdRulaje.Refresh() + ENDIF + ENDIF + IF USED('trul_obinv') + UPDATE trul_obinv SET nnir = m.tnNnirNou WHERE NVL(nnir, 0) = m.tnNnirVechi + IF _TALLY > 0 + Thisform.pgfArticole.page2.grdRulajeObinv.Refresh() + ENDIF + ENDIF + SELECT tact + IF BETWEEN(m.lnRecno, 1, RECCOUNT('tact')) + GO m.lnRecno + ELSE + GO TOP + ENDIF + SELECT (m.lcSelect) + ENDPROC + PROCEDURE updateexplicatiesaft * Daca nu este activata SAFT 406 nu fac nimic IF !m.gl406 @@ -14201,6 +14286,10 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" .Column35.ReadOnly = .T. && cod .Column25.ReadOnly = .T. && id_set ENDWITH + *!* pastreaza blocarea NNIR pentru notele de inventar (id_set = 90101) + IF Thisform.nid_set = 90101 + This.Column44.ReadOnly = .T. + ENDIF *!* 10.08.2011 ^ *!* pot selecta inregistrari din alte sucursale pentru operatiile de copiere, adaugare care This.cAles.ReadOnly = .F. @@ -14674,6 +14763,19 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" thisform.cPaymentCode = ALLTRIM(tAct.paymentcode) ENDPROC + PROCEDURE Grid1.Column44.Text1.LostFocus + Local lnNnirNou, lnNnirVechi + lnNnirVechi = thisform.nnnirvechi + lnNnirNou = NVL(tAct.nnir, 0) + Thisform.sincronizeaza_nnir(m.lnNnirVechi, m.lnNnirNou) + Thisform.nnnirvechi = m.lnNnirNou + Thisform.Grid1.Refresh() + ENDPROC + + PROCEDURE Grid1.Column44.Text1.When + Thisform.nnnirvechi = NVL(tAct.nnir, 0) + ENDPROC + PROCEDURE Grid1.Column6.Text1.InteractiveChange ADDPROPERTY(this, 'lInteractiveChange') LOCAL lnIdPart diff --git a/docs/capcana_grid_preferinte_utilizator.md b/docs/capcana_grid_preferinte_utilizator.md new file mode 100644 index 0000000..6777fcc --- /dev/null +++ b/docs/capcana_grid_preferinte_utilizator.md @@ -0,0 +1,17 @@ +# Capcana: coloana noua intr-un grid existent ajunge la coada (preferinte GridExtras) + +`GridExtras` (`COMUN\utile\GridExtras\gridextras.vc2:1051`, `restoregridpreferences` / +`savegridpreferences`) salveaza **per utilizator** latimea si ordinea coloanelor fiecarui grid, +in `%APPDATA%\...\gridprefs.tmp`, sub cheia `SYS(1272, grid)` (ex. `frm_modific2024.grid1`). +Salvarea se face live, la `columnmoved`/`columnresize`, nu la iesirea din formular. + +Consecinta: `ColumnOrder` setat in clasa e neutralizat la runtime pentru orice utilizator care a +deschis deja formularul inainte de livrare - `gridextra1.setup()` restaureaza ordinea veche, iar +coloana nou adaugata (care nu exista in preferinte) ajunge ultima. + +Solutie: repozitionare la runtime, in `Show()`, **dupa** `gridextra1.setup()`, aplicata o singura +data - cat timp preferintele salvate nu acopera toate coloanele (implementat pe +`frm_modific2024`, `COMUN\clase\omodificari.vc2`, coloana `nnir`). + +La testare: pozitia coloanei nu e o asertiune stabila intre masini. Se asertesteaza tare +`ControlSource` si `ReadOnly`; `ColumnOrder` ramane informativ. diff --git a/docs/flux-editare-vfp-text.md b/docs/flux-editare-vfp-text.md index c8ba0cb..bb4ce27 100644 --- a/docs/flux-editare-vfp-text.md +++ b/docs/flux-editare-vfp-text.md @@ -76,6 +76,10 @@ fals `roundtrip text1 != text2`. Ruleaza atunci cu lista rebazata: - Metoda noua de clasa cere `*m: nume` in `*`; fara ea, prima salvare din IDE o arunca tacut, iar refresh-ul urmator absoarbe pierderea in cache (si in .bak-uri). Override-urile de metode de baza (Init, Show, hook-uri) nu au nevoie de `*m:`. +- FoxBin2Prg NU pastreaza pozitia in text a unei metode noi: o regenereaza la pozitia + alfabetica din `*`. O metoda scrisa in alta parte a fisierului + face fidelity-check-ul sa pice. Sursa de adevar pentru relocare e textul regenerat din + `\verify\` - muta metoda unde apare acolo si reia write-back-ul. - Salvarea din VFP IDE peste un binar scris de txt2vcx poate PIERDE si definitii `*m:` deja existente, desi corpurile PROCEDURE raman (patit pe ROAGEST: `import_adauga_factura.recalc_tva`, `import_nota.do_adauga/do_copie/do_reface`); simptom: crash la Createobject cu diff --git a/docs/flux-modificare-stergere-nota-jurnal.md b/docs/flux-modificare-stergere-nota-jurnal.md new file mode 100644 index 0000000..269791d --- /dev/null +++ b/docs/flux-modificare-stergere-nota-jurnal.md @@ -0,0 +1,53 @@ +# Flux: modificare/stergere nota in Registrul Jurnal + +Comportament CORECT (nu bug): la **modificarea** unei note din Registrul Jurnal, documentul +original nu se suprascrie. Se marcheaza `sters = 1` pe randurile vechi (`ACT`/`RUL`/`RUL_OBINV`), +apoi se scrie un document NOU cu alt `cod`, pastrand acelasi `id_fact`/`id_factd`. Ambele operatii +ruleaza intr-o tranzactie manuala unica (commit sau rollback pe amandoua). + +## Unde + +- `afisjurcom.do_modifica` - `COMUN\clase\comun.vc2:2222-2562`. Deschide tranzactia manuala + (`Thisform.do_deschide_tranzactie()`, linia 2448), apoi `oscrie_in_fisiere(2,...)` = STERGE + (linia 2451), apoi `oscrie_in_fisiere(0,...)` = SCRIE (linia 2479), apoi + `pack_contafin.finalizeaza_modificare_nota(...)` (2484-2486), apoi + `Thisform.do_inchide_tranzactie(...)` (linia 2535) - commit daca tot ce a rulat inainte a + reusit, rollback altfel. +- `do_deschide_tranzactie`/`do_inchide_tranzactie` (`_frmbase`, `COMUN\clase\_frm_base.vc2:252,279`; + varianta identica ca procedura globala in `COMUN\programe\oproceduri_rulaje.prg:287,303`): + `SQLSetProp(gnHandle,'Transactions',2)` la deschidere, `SQLCOMMIT`/`SQLROLLBACK` + + `SQLSetProp(...,'Transactions',1)` la inchidere. +- `oscrie_in_fisiere.prg` (`COMUN\programe\oscrie_in_fisiere.prg`): `tnScrie_Sterge` 0=scriere, + 2=stergere. Apeleaza server-side `pack_contafin.init_scriere_act_rul_local` + + `final_scriere_act_rul_local` -> `finalizeaza_scriere_act_rul`, care ruleaza `SCRIE_IN_ACT` + (scriere) sau `STERGE_DIN_ACT` (stergere) din `PACK_CONTAFIN.pck`. +- `PACK_CONTAFIN.STERGE_DIN_ACT` (`COMUN\docs\PACK_CONTAFIN.pck:1815-1859`): + `UPDATE ACT SET STERS = 1 ... WHERE COD = tnCod` - marcheaza vechiul document, pe `cod`-ul vechi. +- `PACK_CONTAFIN.SCRIE_IN_ACT` (`PACK_CONTAFIN.pck:630-901`): `LN_COD := pack_contafin.GET_COD()` + (linia 648) aloca un `cod` nou; `UPDATE ACT_TEMP SET COD = LN_COD, ...` (linia 897) il scrie pe + noul document - `ID_FACT` nu e atins, ramane cel din documentul original. +- `PACK_CONTAFIN.finalizeaza_modificare_nota` (`PACK_CONTAFIN.pck:8601-8651`): aloca din nou + `lnCodNou := pack_contafin.get_cod()` si actualizeaza pe codul nou `vanzari` + (`pack_facturare.actualizeaza_vanzari`) si `atasamente_vanzari`. `nom_lucrari` se actualizeaza + DOAR pentru `id_set IN (31003,31004,31005,31011)` (repune `id_fact`), nu general. `gest_inventar` + NU se leaga de `cod`-ul nou - se identifica dupa `dataora_validat + an + luna` (cazul + `id_set = 90101`, note de inventariere). + +## Atentie: nu e valabil la fel pentru stergerea simpla + +`afisjurcom.do_sterge` (`comun.vc2:2565-2724`) ofera doua optiuni (`xmenu`, linia 2625): +- **Stergere document** (`lnOptiune = 1`): DOAR `OSCRIE_IN_FISIERE(2,...)` + `finalizeaza_stergere_nota` + - marcheaza `sters = 1`, NU se creeaza document nou. +- **Anulare document** (`lnOptiune = 2`): STERGE (linia 2698) + SCRIE (linia 2709, cu sumele puse pe + 0) - creeaza si document nou, la fel ca la modificare - dar aici cele doua apeluri NU sunt + invelite intr-o tranzactie manuala comuna (nu apare `do_deschide_tranzactie`/ + `do_inchide_tranzactie` in `do_sterge`); fiecare `OSCRIE_IN_FISIERE` isi gestioneaza singur + commit-ul (vezi `tlModificare`/`llManualTransactions` in `oscrie_in_fisiere.prg:111-165`). + +## Implicatii practice + +- Codul (`cod`) unui document din Registrul Jurnal NU e stabil dupa modificare/anulare - se schimba + la fiecare editare. Cod hardcodat sau retinut dintr-o executare anterioara devine invalid. +- Regasirea documentului editat se face dupa `id_fact` (sau alt marcaj stabil), niciodata dupa `cod`. +- Randurile `sters = 1` ramase in `ACT`/`RUL`/`RUL_OBINV` dupa o modificare sunt normale (istoric), + nu date corupte sau duplicate de curatat. diff --git a/docs/reguli_lucru.md b/docs/reguli_lucru.md index 92acf82..317c148 100644 --- a/docs/reguli_lucru.md +++ b/docs/reguli_lucru.md @@ -60,6 +60,8 @@ `0xBA` strica `a`/`t` cu caciula (`0xE3`, `0xFE`) din alte fisiere; - adaugi/rearanjezi controale pe formulare sau coloane in grid -> `conventie_ux_formulare.md`; - grid needitabil pe formular cu 2+ grid-uri -> `capcana_grid_controlsource.md`; + - adaugi o coloana intr-un grid deja livrat (ordinea din clasa e neutralizata de + preferintele salvate per utilizator) -> `capcana_grid_preferinte_utilizator.md`; - `GO` pe un `Recno()` capturat/primit ca parametru -> `conventie_go_recno.md`; - `ALTER TABLE` pe cursorul intors de `goExecutor.oExecute()` -> `conventie_goexecutor_alter_table.md`; - testare UI / prin MCP -> `testare-ui-vfp.md`, `testare-vfp-mcp.md`; diff --git a/utile/Teste/nnir_registru_jurnal/date_test_nnir.md b/utile/Teste/nnir_registru_jurnal/date_test_nnir.md new file mode 100644 index 0000000..bac7cdc --- /dev/null +++ b/utile/Teste/nnir_registru_jurnal/date_test_nnir.md @@ -0,0 +1,121 @@ +# Date de test - NNIR editabil in Registru Jurnal + +Baza: `MARIUSM_AUTO@ROA_CENTRAL` (test, nu productie). Verificare: `verif_nnir_cod.sql` in acest +folder (`sqlplus ... @verif_nnir_cod.sql `). + +| Caz | COD | AN | LUNA | ID_SET | NNIR curent | ACT randuri | RUL randuri | RUL_OBINV randuri | +|---|---|---|---|---|---|---|---|---| +| NIR/achizitie (principal) | 1140841 | 2026 | 7 | 209 | 3 | 7 (doar 1 cu nnir=3, restul 0) | 1 (nnir=3) | 0 | +| Obiecte de inventar | 1140835 | 2026 | 6 | 211 | 2026060001 | 1 | 0 | 2 (nnir=2026060001) | +| Fara rulaje | 1140848 | 2026 | 7 | 209 | 124 | 2 (ambele nnir=124) | 0 | 0 | +| Nota de inventar (blocare) | 1140716 | 2026 | 3 | 90101 | 2026030004 | 1 | 1 (nnir=2026030004) | 0 | + +Note: +- ACT e pe linii (nu pe antet) - la COD 1140841 doar linia efectiv legata de rulaj are NNIR-ul + real, restul liniilor documentului au NNIR=0. Sincronizarea (`WHERE Nvl(nnir,0) = vechi`) trebuie + sa lase liniile cu 0 neatinse. +- Nu exista nicio nota cu `id_set=90101` sau cu rulaje in `RUL_OBINV` in luna curenta de lucru + (AN=2026, LUNA=7) - alese din perioade anterioare. Editarea reala prin `afisjurcom.do_modifica` + e limitata la luna curenta (`comun.vc2:2265-2268`); testul instantiaza `frm_modific2024` direct + (ca `test_repro_cnrcrt_footer.prg`), cu cursoare construite pe AN/LUNA propriu notei, ocolind + acel filtru - la fel ca precedentul deja acceptat in `achizitie_import`. + +## Stare implementare (livrata, 03.08.2026) + +`COMUN\clase\omodificari.vc2` (frm_modific2024): +- `Grid1.ColumnCount = 44`, `Column44.ControlSource = "nnir"` (la runtime apare calificat automat + de VFP cu aliasul cursorului: `tact.nnir`), `ReadOnly = .F.` - confirmat prin instantiere reala + (nu doar citire de sursa). +- `PROCEDURE sincronizeaza_nnir(tnNnirVechi, tnNnirNou)` (`omodificari.vc2:13762`) - update pe + `tact`/`trul`/`trul_obinv` unde `Nvl(nnir,0) = vechi`, cu revenire pe recno. Propaga si valoarea + 0 (stergerea NNIR) - nu exista garda `EMPTY` pe valoarea noua. +- Legare pe coloana: `Grid1.Column44.Text1.When` retine valoarea veche in `thisform.nnnirvechi`; + **`Grid1.Column44.Text1.LostFocus`** apeleaza `sincronizeaza_nnir` si `Thisform.Grid1.Refresh()` + - `Valid` a fost eliminat (la textbox valoarea nu ajunge in ControlSource decat DUPA ce Valid + returneaza .T., deci nu propaga). Testul declanseaza `LostFocus`. +- Blocarea pentru `id_set = 90101` (nota de inventar): in `Init` (`omodificari.vc2:13654`) SI + reimpusa in `Grid1.AfterRowColChange` (14289-14292), dupa `SetAll("ReadOnly", ...)` - fara a doua, + `SetAll` anuleaza blocarea la prima navigare in grid. +- `ColumnOrder = 7` in clasa, plus repozitionare la runtime in `Show()`, dupa `gridextra1.setup()` + (13729-13738) - preferintele de grid salvate per utilizator impingeau coloana noua la coada + (vezi `COMUN\docs\capcana_grid_preferinte_utilizator.md`). Pozitia NU e o asertiune stabila intre + masini; asertat tare in test: `ControlSource = "nnir"`, `ReadOnly` (.F. normal / .T. pe + id_set 90101), propagarea valorii. + +## Cerinte de mediu descoperite la construirea harness-ului (deja tratate in scripturile livrate) + +- `COMUN\utile\Teste\test_init_env_auto.prg` (generic) e de fapt specific ROACONT (`gcAppPath`, + classlib-uri si proceduri hardcodate `D:\ROA\ROACONT\`) - inutilizabil pentru ROAGEST. S-a scris + `COMUN\utile\Teste\test_init_env_auto_roagest.prg`, cu SET PATH/CLASSLIB/PROCEDURE reconstruite + din `Programe\roagest.prg`. +- `gcUserName` trebuie setat explicit inainte de `optiuni_firma` (`oinit_optiuni.prg`) - altfel + `SCRIE_OPTIUNI(gcUserName)` da "Operator/operand type mismatch" (PUBLIC nedeclarat ramane + logic .F.). **`gcUserName` e schema/userul de conectare Oracle** (`Programe\roagest.prg:560,575`) + - `SCRIE_OPTIUNI` il foloseste direct in `from .optiuni`, deci trebuie + `gcUserName = m.gcS`, NU un nume de persoana (ex. 'MARIUS M' da ORA-00907, identificator invalid + - incercare gresita initiala). Numele de persoana e `gcUserNameApp` (variabila separata). +- `frm_modific2024` face `Seek(..,'crsJtvaTemp','id_jtva')` in ControlSource-ul coloanei de + explicatie TVA a rulajelor (Column63) - evaluat la CONSTRUCTIA formularului. Fara cursorul + deschis, `CREATEOBJECT('frm_modific2024',...)` pica cu "File 'crsjtvatemp.dbf' does not exist". + Fix: `update_jtva_coloane('', 'crsJtvaTemp', 6)` (in `COMUN\programe\updateserver.prg:553`) + inainte de instantiere - construieste direct cursorul cu indexul cerut, fara pasii intermediari + folositi de exemplul ROACONT (`update_jtva_coloana`/`crsExplicatiiTVATemp`, care oricum nu + exista in ROAGEST). +- Mock-ul `amessagebox` trebuie incarcat **INAINTE** de `test_init_env_auto_roagest.prg` (nu doar + dupa, cum sugereaza exemplele din `achizitie_import`) - `optiuni_firma` poate afisa un dialog + real daca `SCRIE_OPTIUNI` raporteaza eroare, inainte sa apuce sa se incarce mock-ul "la coada". +- La citirea unei coloane de grid prin ControlSource dintr-un cursor legat ca RecordSource, VFP + calificat automat campul simplu cu aliasul (`nnir` -> `tact.nnir`) - comparatiile exacte + `== 'NNIR'` trebuie sa accepte si forma calificata. +- Coloanele de grid nu sunt garantat numite `Column` - unele au `Name` custom (ex. `cAles` pe + pozitia 41 in acest grid); accesul dinamic la coloana trebuie facut prin colectia + `Grid1.Columns(i)`, nu prin constructia textuala a numelui `"column" + TRANSFORM(i)`. +- `frm_modific2024.Grid1.AfterRowColChange` are propria logica de blocare pe sucursala (SetAll + ReadOnly pe toate coloanele daca `Nvl(gnIdSucursala,0) <> Nvl(tAct.id_sucursala,0)`) - independenta + de blocarea 90101, se declanseaza automat la pozitionare in grid. COD 1140835 (cazul obiecte de + inventar) are `id_sucursala` diferit de sesiunea de test (`gnIdSucursala = NULL`), deci Column44 + iese `ReadOnly=.T.` in acest scenariu - nu e o eroare, e comportament existent, neplanificat de + NNIR; sincronizarea tot functioneaza (apelul e direct, nu prin UI). +- **Termin reatribuie COD nou**: `afisjurcom.do_modifica` (comun.vc2:2444-2487) cheama + `oscrie_in_fisiere` de DOUA ori - intai `(2,.T.,llRul)` (STERGE, marcheaza `sters=1` pe COD-ul + ORIGINAL, din `actactan` neatins de editare), apoi `(0,.T.,llRul)` (SCRIE varianta editata sub + COD nou, generat de `pack_contafin`, sub AN/LUNA de sesiune curenta - nu pastreaza COD-ul/AN/LUNA + notei editate). Verificarea si curatenia testului se fac dupa NNIR (marcaj distinct), nu dupa + COD, si readuc nota originala la `sters=0` dupa test (pasul STERGE o dezactiveaza altfel permanent). +- Salvarea reala cere tranzactie manuala explicita: `SQLSetProp(gnHandle,'Transactions',2)` inainte + de blocul STERGE+SCRIE+finalizeaza, apoi `SQLCOMMIT(gnHandle)`/`SQLROLLBACK` + + `SQLSetProp(...,1)` dupa - echivalentul direct al `thisform.do_deschide_tranzactie()`/ + `do_inchide_tranzactie()` din `afisjurcom` (`_frm_base.vc2:252-305`, mostenite, apelate pe + formularul de jurnal - nu pe `frm_modific2024`, deci invizibile la o privire doar pe clasa notei). + Fara acest wrapping, `oscrie_in_fisiere`/`finalizeaza_modificare_nota` raporteaza succes dar nu + se vede nimic persistat, nici in aceeasi sesiune. + +## Capcana: dialog modal "Enter the value for " in loc de eroare catchabila + +O variabila memorie lipsa/umbrita folosita ca `?param` intr-un SQL pass-through (SQLEXEC/ +`goExecutor.oExecuta`) NU da o eroare VFP catchabila (ON ERROR) - deschide dialogul nativ +"View Parameter" ("Enter the value for "), MODAL, care blocheaza tot procesul headless la +nesfarsit si nu lasa nimic in log (spre deosebire de un `REPLACE`/expresie cu aceeasi variabila +lipsa, care DA eroare catchabila - vezi cazul `gnIdUtil` scapat o data ca `PRIVATE` neasignat). +Simptom: proces `vfp9.exe` care nu mai raspunde, fara `.ERR`, fara linie noua in log. + +Remediu: **garda explicita inainte de orice rulare care ajunge la cod ce foloseste `?param`** - +verifica `TYPE() == 'U'` pe toate variabilele relevante (nu doar cele proprii testului, ci +si cele din codul apelat: `oscrie_in_fisiere`, `pack_contafin`/`pack_sesiune`, `oinit_optiuni.prg`) +si opreste testul cu FAIL clar in log daca lipseste ceva, inainte sa ajunga la SQLEXEC. Implementat +in `test_nnir_sincronizare.prg` (`NnirGardaVariabile`, apelata imediat dupa init mediu). `TYPE()` +returneaza `'X'` (nu `'U'`) pentru NULL - o valoare NULL e un bind valid, doar variabila absenta +e problema. + +## Rezultat test complet (03.08.2026, pe binarul livrat) + +**33 PASS, 0 FAIL** pe cele 6 scenarii: NIR, obiecte de inventar si nota fara rulaje (toate cu +salvare reala prin Termin + verificare in Oracle), blocare `id_set=90101`, `NNIR -> 0`, si blocare +`id_set=90101` pe sucursala curenta. Baza de test verificata dupa rulare: cele 4 note originale +(1140841, 1140835, 1140848, 1140716) raman intacte (NNIR si `sters` la valorile initiale); notele +noi create de Termin (COD reatribuit) au fost marcate `sters=1` de curatenia din test. + +Scenariul 6 (`INVENTAR2`) exista pentru ca scenariul 4 ruleaza pe o nota din ALTA sucursala, unde +`AfterRowColChange` blocheaza oricum tot gridul - trecea din motivul gresit. Scenariul 6 aliniaza +`gnIdSucursala` la nota (`llReadOnly` iese `.F.`) si asertesteaza si ca restul coloanelor raman +editabile, altfel blocarea NNIR nu dovedeste nimic. diff --git a/utile/Teste/nnir_registru_jurnal/test_nnir_sincronizare.prg b/utile/Teste/nnir_registru_jurnal/test_nnir_sincronizare.prg new file mode 100644 index 0000000..f78d57b --- /dev/null +++ b/utile/Teste/nnir_registru_jurnal/test_nnir_sincronizare.prg @@ -0,0 +1,837 @@ +* test_nnir_sincronizare.prg +* Test e2e (fluxul real): NNIR editabil in Registru Jurnal, cu sincronizare in tact/trul/trul_obinv +* si in ACT/RUL/RUL_OBINV (Oracle). Vezi cerinta initiala (todos.txt pct. 9) si +* date_test_nnir.md (datele de test alese, an/luna/cod, stare implementare la data scrierii). +* +* Instantiaza frm_modific2024 direct (ca test_repro_cnrcrt_footer.prg din achizitie_import), +* cu cursoarele tact/trul/trul_obinv construite exact ca afisjurcom.do_modifica (comun.vc2:2352- +* 2434), dar pe AN/LUNA proprii notei - do_modifica limiteaza editarea la luna curenta de lucru +* (comun.vc2:2265-2268), iar notele de test nu sunt toate in luna curenta. +* +* Editarea NNIR se simuleaza ca un utilizator: pozitionare pe rand, declansarea evenimentelor +* reale ale coloanei (When la intrare, LostFocus la iesire - textboxul nu comite valoarea in +* ControlSource inainte de Valid, de-aia implementarea sincronizeaza pe LostFocus), NU apel +* direct al metodei de sincronizare. Salvarea (Termin) replica blocul din +* afisjurcom.do_modifica (comun.vc2:2456-2487): oscrie_in_fisiere + pack_contafin. +* finalizeaza_modificare_nota. Termin reatribuie COD nou (pack_contafin), sub AN/LUNA de sesiune +* curenta - nu pastreaza COD-ul original; verificarea si curatenia de dupa salvare se fac dupa +* valoarea NNIR marcaj (nu poate coincide cu date reale), marcand sters=1 (conventia aplicatiei) +* orice a ajuns sub acea valoare - nota originala (COD/NNIR vechi) ramane intacta. +* +* Rulare headless (nu tine VFP IDE deschis; nu rula cu roagest.exe pornit): +* & "C:\Program Files (x86)\Microsoft Visual FoxPro 9\vfp9.exe" -A -T ; +* "D:\ROA\ROAGEST\COMUN\utile\Teste\nnir_registru_jurnal\test_nnir_sincronizare.prg" +* Timeout recomandat: minim 220s. Log: %TEMP%\test_nnir_sincronizare_log.txt (PASS/FAIL pe linie). + +LPARAMETERS tcMode +tcMode = IIF(EMPTY(tcMode), 'ALL', UPPER(ALLTRIM(tcMode))) + +SET SAFETY OFF +SET TALK OFF + +PUBLIC gcLogFile, gnFail, gnPass +gcLogFile = ADDBS(SYS(2023)) + 'test_nnir_sincronizare_log.txt' +gnFail = 0 +gnPass = 0 +STRTOFILE('START ' + tcMode + ' ' + TTOC(DATETIME()) + CHR(13)+CHR(10), gcLogFile) + +ON ERROR DO NnirErr WITH ERROR(), MESSAGE(), PROGRAM(), LINENO() +ON SHUTDOWN QUIT + +* mock amessagebox incarcat INAINTE de mediul aplicatiei: optiuni_firma (in mediul de test) +* poate afisa un dialog real daca SCRIE_OPTIUNI raporteaza eroare +SET PROCEDURE TO D:\ROA\ROAGEST\COMUN\utile\Teste\mock_amessagebox.prg ADDITIVE +DO D:\ROA\ROAGEST\COMUN\utile\Teste\test_init_env_auto_roagest.prg WITH 'CENTRAL', 'MARIUSM_AUTO', 'ROMFASTSOFT' +IF gnHandle <= 0 + DO NnirLog WITH 'EROARE: conectare Oracle esuata' + DO NnirLog WITH 'REZULTAT SUITA: NEDETERMINAT' + QUIT +ENDIF +DO NnirLog WITH 'mediu OK, gnAn=' + TRANSFORM(gnAn) + ' gnLuna=' + TRANSFORM(gnLuna) + +* garda anti-blocaj: o variabila lipsa folosita ca ?param intr-un SQL pass-through NU da eroare +* catchabila - VFP deschide dialogul modal nativ "Enter the value for ", care blocheaza +* headless-ul la nesfarsit si nu lasa nimic in log (vezi date_test_nnir.md). Verificare +* explicita INAINTE de orice apel care le foloseste, opreste testul cu FAIL clar daca lipseste ceva. +IF !NnirGardaVariabile() + DO NnirLog WITH 'REZULTAT SUITA: NEDETERMINAT (garda variabile a picat)' + QUIT +ENDIF + +* mock amessagebox - se incarca ULTIMUL (dupa fisierele aplicatiei), vezi antetul mock-ului +SET PROCEDURE TO D:\ROA\ROAGEST\COMUN\utile\Teste\mock_amessagebox.prg ADDITIVE + +* Grid1.Column63 (explicatie TVA pe rulaje) face Seek pe alias 'crsJtvaTemp' la CONSTRUCTIA +* formularului (inainte de Init) - fara cursorul deschis, CREATEOBJECT('frm_modific2024',...) +* pica cu "File 'crsjtvatemp.dbf' does not exist" +update_jtva_coloane('', 'crsJtvaTemp', 6) + +LOCAL Omodif, lcCallWhen, lcCallLostFocus, lcCall + +*===================================================================================== +* Scenariul 1: NIR/achizitie, cu rulaje in RUL, cu salvare (Termin) + verificare Oracle +*===================================================================================== +IF INLIST(tcMode, 'ALL', 'NIR') + LOCAL lnCod, lnAn, lnLuna, lnIdSet, lnNnirVechi, lnNnirNou + LOCAL lnCntTactVechi, lnCntTrulVechi, lnCntTactNou, lnCntTrulNou + LOCAL i, llGasit, lnColOrder, llReadOnly, lnColCheck, lcColName + + lnCod = 1140841 + lnAn = 2026 + lnLuna = 7 + lnIdSet = 209 + lnNnirVechi = 3 + lnNnirNou = lnNnirVechi + 90000000 + + DO NnirConstruiesteCursoare WITH lnCod, lnAn, lnLuna + + SELECT tact + lnCntTactVechi = 0 + COUNT FOR NVL(nnir,0) = lnNnirVechi TO lnCntTactVechi + SELECT trul + lnCntTrulVechi = 0 + COUNT FOR NVL(nnir,0) = lnNnirVechi TO lnCntTrulVechi + + Omodif = CREATEOBJECT('frm_modific2024', lnIdSet) + IF VARTYPE(Omodif) <> 'O' + DO NnirAssert WITH 'instantiere', 'NIR', .F., 'frm_modific2024 nu s-a instantiat' + ELSE + SELECT tact + Omodif.grid1.SetFocus + Omodif.gridextra1.setup() + Omodif.CalculeazaTotal() + + llGasit = .F. + lnColOrder = 0 + llReadOnly = .T. + lnColCheck = 0 + FOR i = 1 TO Omodif.grid1.ColumnCount + lcColName = 'Omodif.grid1.Columns(' + TRANSFORM(i) + ')' + IF NnirEsteColoanaNnir(EVALUATE(lcColName + '.ControlSource')) + llGasit = .T. + lnColOrder = EVALUATE(lcColName + '.ColumnOrder') + llReadOnly = EVALUATE(lcColName + '.ReadOnly') + lnColCheck = i + ENDIF + ENDFOR + DO NnirAssert WITH 'coloana_grid', 'NIR', llGasit, 'ControlSource=nnir gasit=' + ; + TRANSFORM(llGasit) + ' ColumnOrder=' + TRANSFORM(lnColOrder) + ' ReadOnly=' + TRANSFORM(llReadOnly) + + IF llGasit + DO NnirAssert WITH 'coloana_editabila', 'NIR', !llReadOnly, 'nota normala: ReadOnly asteptat .F.' + + SELECT tact + LOCATE FOR NVL(nnir,0) = lnNnirVechi + IF FOUND() + lcCallWhen = 'Omodif.grid1.Columns(' + TRANSFORM(lnColCheck) + ').Text1.When' + &lcCallWhen + REPLACE nnir WITH lnNnirNou + lcCallLostFocus = 'Omodif.grid1.Columns(' + TRANSFORM(lnColCheck) + ').Text1.LostFocus' + &lcCallLostFocus + + SELECT tact + lnCntTactNou = 0 + COUNT FOR NVL(nnir,0) = lnNnirNou TO lnCntTactNou + DO NnirAssert WITH 'sync_tact', 'NIR', lnCntTactNou = lnCntTactVechi, ; + TRANSFORM(lnCntTactNou) + ' randuri tact cu nnir nou (asteptat ' + TRANSFORM(lnCntTactVechi) + ')' + + * trul = cursorul legat de pgfArticole.page1.grdRulaje (RecordSource) - verificare + * explicita ca propagarea se vede in cursorul de rulaje, nu doar in tact + SELECT trul + lnCntTrulNou = 0 + COUNT FOR NVL(nnir,0) = lnNnirNou TO lnCntTrulNou + DO NnirAssert WITH 'sync_trul', 'NIR', lnCntTrulNou = lnCntTrulVechi, ; + TRANSFORM(lnCntTrulNou) + ' randuri trul (grid rulaje) cu nnir nou (asteptat ' + TRANSFORM(lnCntTrulVechi) + ')' + ELSE + DO NnirAssert WITH 'locate_rand_vechi', 'NIR', .F., 'nu s-a gasit randul cu nnir vechi in tact' + ENDIF + ENDIF + + DO NnirVerificaOracleNeschimbat WITH lnCod, lnNnirVechi, 'NIR' + + Omodif.Release() + Omodif = NULL + DO NnirSalveazaSiVerifica WITH lnCod, lnAn, lnLuna, lnIdSet, lnNnirVechi, lnNnirNou, 'NIR' + ENDIF + DO NnirCurataCursoare +ENDIF + +*===================================================================================== +* Scenariul 2: obiecte de inventar, cu rulaje in RUL_OBINV, cu salvare + verificare Oracle +*===================================================================================== +IF INLIST(tcMode, 'ALL', 'OBINV') + LOCAL lnCod2, lnAn2, lnLuna2, lnIdSet2, lnNnirVechi2, lnNnirNou2 + LOCAL lnCntTactVechi2, lnCntRobinvVechi2, lnCntTactNou2, lnCntRobinvNou2 + LOCAL i2, llGasit2, lnColOrder2, llReadOnly2, lnColCheck2, lcColName2 + + lnCod2 = 1140835 + lnAn2 = 2026 + lnLuna2 = 6 + lnIdSet2 = 211 + lnNnirVechi2 = 2026060001 + lnNnirNou2 = lnNnirVechi2 + 90000000 + + DO NnirConstruiesteCursoare WITH lnCod2, lnAn2, lnLuna2 + + SELECT tact + lnCntTactVechi2 = 0 + COUNT FOR NVL(nnir,0) = lnNnirVechi2 TO lnCntTactVechi2 + SELECT trul_obinv + lnCntRobinvVechi2 = 0 + COUNT FOR NVL(nnir,0) = lnNnirVechi2 TO lnCntRobinvVechi2 + + Omodif = CREATEOBJECT('frm_modific2024', lnIdSet2) + IF VARTYPE(Omodif) <> 'O' + DO NnirAssert WITH 'instantiere', 'OBINV', .F., 'frm_modific2024 nu s-a instantiat' + ELSE + SELECT tact + Omodif.grid1.SetFocus + Omodif.gridextra1.setup() + Omodif.CalculeazaTotal() + + llGasit2 = .F. + lnColOrder2 = 0 + llReadOnly2 = .T. + lnColCheck2 = 0 + FOR i2 = 1 TO Omodif.grid1.ColumnCount + lcColName2 = 'Omodif.grid1.Columns(' + TRANSFORM(i2) + ')' + IF NnirEsteColoanaNnir(EVALUATE(lcColName2 + '.ControlSource')) + llGasit2 = .T. + lnColOrder2 = EVALUATE(lcColName2 + '.ColumnOrder') + llReadOnly2 = EVALUATE(lcColName2 + '.ReadOnly') + lnColCheck2 = i2 + ENDIF + ENDFOR + DO NnirAssert WITH 'coloana_grid', 'OBINV', llGasit2, 'ControlSource=nnir gasit=' + ; + TRANSFORM(llGasit2) + ' ColumnOrder=' + TRANSFORM(lnColOrder2) + ' ReadOnly=' + TRANSFORM(llReadOnly2) + + IF llGasit2 + SELECT tact + LOCATE FOR NVL(nnir,0) = lnNnirVechi2 + IF FOUND() + lcCallWhen = 'Omodif.grid1.Columns(' + TRANSFORM(lnColCheck2) + ').Text1.When' + &lcCallWhen + REPLACE nnir WITH lnNnirNou2 + lcCallLostFocus = 'Omodif.grid1.Columns(' + TRANSFORM(lnColCheck2) + ').Text1.LostFocus' + &lcCallLostFocus + + SELECT tact + lnCntTactNou2 = 0 + COUNT FOR NVL(nnir,0) = lnNnirNou2 TO lnCntTactNou2 + DO NnirAssert WITH 'sync_tact', 'OBINV', lnCntTactNou2 = lnCntTactVechi2, ; + TRANSFORM(lnCntTactNou2) + ' randuri tact cu nnir nou (asteptat ' + TRANSFORM(lnCntTactVechi2) + ')' + + SELECT trul_obinv + lnCntRobinvNou2 = 0 + COUNT FOR NVL(nnir,0) = lnNnirNou2 TO lnCntRobinvNou2 + DO NnirAssert WITH 'sync_trul_obinv', 'OBINV', lnCntRobinvNou2 = lnCntRobinvVechi2, ; + TRANSFORM(lnCntRobinvNou2) + ' randuri trul_obinv cu nnir nou (asteptat ' + TRANSFORM(lnCntRobinvVechi2) + ')' + ELSE + DO NnirAssert WITH 'locate_rand_vechi', 'OBINV', .F., 'nu s-a gasit randul cu nnir vechi in tact' + ENDIF + ENDIF + + Omodif.Release() + Omodif = NULL + DO NnirSalveazaSiVerificaObinv WITH lnCod2, lnAn2, lnLuna2, lnIdSet2, lnNnirVechi2, lnNnirNou2 + ENDIF + DO NnirCurataCursoare +ENDIF + +*===================================================================================== +* Scenariul 3: nota fara rulaje - regresie (nu crapa), cu salvare + verificare Oracle +*===================================================================================== +IF INLIST(tcMode, 'ALL', 'FARARUL') + LOCAL lnCod3, lnAn3, lnLuna3, lnIdSet3, lnNnirVechi3, lnNnirNou3 + LOCAL lnCntTactVechi3, lnCntTactNou3 + LOCAL i3, llGasit3, lnColCheck3, lcColName3 + + lnCod3 = 1140848 + lnAn3 = 2026 + lnLuna3 = 7 + lnIdSet3 = 209 + lnNnirVechi3 = 124 + lnNnirNou3 = lnNnirVechi3 + 90000000 + + DO NnirConstruiesteCursoare WITH lnCod3, lnAn3, lnLuna3 + + SELECT tact + lnCntTactVechi3 = 0 + COUNT FOR NVL(nnir,0) = lnNnirVechi3 TO lnCntTactVechi3 + + Omodif = CREATEOBJECT('frm_modific2024', lnIdSet3) + IF VARTYPE(Omodif) <> 'O' + DO NnirAssert WITH 'instantiere', 'FARARUL', .F., 'frm_modific2024 nu s-a instantiat' + ELSE + SELECT tact + Omodif.grid1.SetFocus + Omodif.gridextra1.setup() + Omodif.CalculeazaTotal() + + llGasit3 = .F. + lnColCheck3 = 0 + FOR i3 = 1 TO Omodif.grid1.ColumnCount + lcColName3 = 'Omodif.grid1.Columns(' + TRANSFORM(i3) + ')' + IF NnirEsteColoanaNnir(EVALUATE(lcColName3 + '.ControlSource')) + llGasit3 = .T. + lnColCheck3 = i3 + ENDIF + ENDFOR + DO NnirAssert WITH 'coloana_grid', 'FARARUL', llGasit3, 'ControlSource=nnir gasit=' + TRANSFORM(llGasit3) + + IF llGasit3 + SELECT tact + LOCATE FOR NVL(nnir,0) = lnNnirVechi3 + IF FOUND() + lcCallWhen = 'Omodif.grid1.Columns(' + TRANSFORM(lnColCheck3) + ').Text1.When' + &lcCallWhen + REPLACE nnir WITH lnNnirNou3 + lcCallLostFocus = 'Omodif.grid1.Columns(' + TRANSFORM(lnColCheck3) + ').Text1.LostFocus' + &lcCallLostFocus + + SELECT tact + lnCntTactNou3 = 0 + COUNT FOR NVL(nnir,0) = lnNnirNou3 TO lnCntTactNou3 + DO NnirAssert WITH 'sync_tact', 'FARARUL', lnCntTactNou3 = lnCntTactVechi3, ; + TRANSFORM(lnCntTactNou3) + ' randuri tact cu nnir nou (asteptat ' + TRANSFORM(lnCntTactVechi3) + ')' + DO NnirAssert WITH 'nu_crapa', 'FARARUL', .T., 'editarea pe nota fara rulaje nu a generat eroare' + ELSE + DO NnirAssert WITH 'locate_rand_vechi', 'FARARUL', .F., 'nu s-a gasit randul cu nnir vechi in tact' + ENDIF + ENDIF + + Omodif.Release() + Omodif = NULL + DO NnirSalveazaSiVerifica WITH lnCod3, lnAn3, lnLuna3, lnIdSet3, lnNnirVechi3, lnNnirNou3, 'FARARUL' + ENDIF + DO NnirCurataCursoare +ENDIF + +*===================================================================================== +* Scenariul 4: nota de inventar (id_set=90101) - regresie blocare editare NNIR +*===================================================================================== +IF INLIST(tcMode, 'ALL', 'INVENTAR') + LOCAL lnCod4, lnAn4, lnLuna4, lnIdSet4 + LOCAL i4, llGasit4, llReadOnly4, lcColName4 + + lnCod4 = 1140716 + lnAn4 = 2026 + lnLuna4 = 3 + lnIdSet4 = 90101 + + DO NnirConstruiesteCursoare WITH lnCod4, lnAn4, lnLuna4 + + Omodif = CREATEOBJECT('frm_modific2024', lnIdSet4) + IF VARTYPE(Omodif) <> 'O' + DO NnirAssert WITH 'instantiere', 'INVENTAR', .F., 'frm_modific2024 nu s-a instantiat' + ELSE + SELECT tact + Omodif.grid1.SetFocus + Omodif.gridextra1.setup() + Omodif.CalculeazaTotal() + + llGasit4 = .F. + llReadOnly4 = .F. + FOR i4 = 1 TO Omodif.grid1.ColumnCount + lcColName4 = 'Omodif.grid1.Columns(' + TRANSFORM(i4) + ')' + IF NnirEsteColoanaNnir(EVALUATE(lcColName4 + '.ControlSource')) + llGasit4 = .T. + llReadOnly4 = EVALUATE(lcColName4 + '.ReadOnly') + ENDIF + ENDFOR + DO NnirAssert WITH 'coloana_grid', 'INVENTAR', llGasit4, 'ControlSource=nnir gasit=' + TRANSFORM(llGasit4) + IF llGasit4 + * nu se incearca editarea - doar se verifica blocarea, ca sa nu se altereze o nota + * reala de inventar cat timp blocarea nu e implementata + DO NnirAssert WITH 'coloana_blocata', 'INVENTAR', llReadOnly4, ; + 'id_set=90101: ReadOnly asteptat .T., gasit ' + TRANSFORM(llReadOnly4) + ENDIF + + Omodif.Release() + Omodif = NULL + ENDIF + DO NnirCurataCursoare +ENDIF + +*===================================================================================== +* Scenariul 5: stergere NNIR (-> 0) pe randul unei note cu rulaje - propagare la 0. +* Comportament nou (runda 4): garda EMPTY(NVL(tnNnirNou,0)) eliminata din sincronizeaza_nnir, +* ramane doar egalitatea - stergerea celulei (0 scris pe rand inainte de LostFocus) trebuie sa +* se propage la toate liniile cu valoarea veche, nu doar schimbarile intre doua valori nenule. +* Doar la nivel de cursoare (tact/trul) - nu se salveaza in Oracle. +*===================================================================================== +IF INLIST(tcMode, 'ALL', 'ZERO') + LOCAL lnCodZ, lnAnZ, lnLunaZ, lnIdSetZ, lnNnirVechiZ + LOCAL lnCntTactVechiZ, lnCntTrulVechiZ, lnCntTactRamasVechiZ, lnCntTrulRamasVechiZ + LOCAL lnCntTactZeroZ, lnCntTrulZeroZ + LOCAL iz, llGasitZ, lnColCheckZ, lcColNameZ + + lnCodZ = 1140841 + lnAnZ = 2026 + lnLunaZ = 7 + lnIdSetZ = 209 + lnNnirVechiZ = 3 + + DO NnirConstruiesteCursoare WITH lnCodZ, lnAnZ, lnLunaZ + + SELECT tact + lnCntTactVechiZ = 0 + COUNT FOR NVL(nnir,0) = lnNnirVechiZ TO lnCntTactVechiZ + SELECT trul + lnCntTrulVechiZ = 0 + COUNT FOR NVL(nnir,0) = lnNnirVechiZ TO lnCntTrulVechiZ + + Omodif = CREATEOBJECT('frm_modific2024', lnIdSetZ) + IF VARTYPE(Omodif) <> 'O' + DO NnirAssert WITH 'instantiere', 'ZERO', .F., 'frm_modific2024 nu s-a instantiat' + ELSE + SELECT tact + Omodif.grid1.SetFocus + Omodif.gridextra1.setup() + Omodif.CalculeazaTotal() + + llGasitZ = .F. + lnColCheckZ = 0 + FOR iz = 1 TO Omodif.grid1.ColumnCount + lcColNameZ = 'Omodif.grid1.Columns(' + TRANSFORM(iz) + ')' + IF NnirEsteColoanaNnir(EVALUATE(lcColNameZ + '.ControlSource')) + llGasitZ = .T. + lnColCheckZ = iz + ENDIF + ENDFOR + DO NnirAssert WITH 'coloana_grid', 'ZERO', llGasitZ, 'ControlSource=nnir gasit=' + TRANSFORM(llGasitZ) + + IF llGasitZ + SELECT tact + LOCATE FOR NVL(nnir,0) = lnNnirVechiZ + IF FOUND() + lcCallWhen = 'Omodif.grid1.Columns(' + TRANSFORM(lnColCheckZ) + ').Text1.When' + &lcCallWhen + * simuleaza stergerea celulei: utilizatorul goleste campul, valoarea ajunge 0 + REPLACE nnir WITH 0 + lcCallLostFocus = 'Omodif.grid1.Columns(' + TRANSFORM(lnColCheckZ) + ').Text1.LostFocus' + &lcCallLostFocus + + SELECT tact + lnCntTactRamasVechiZ = 0 + COUNT FOR NVL(nnir,0) = lnNnirVechiZ TO lnCntTactRamasVechiZ + lnCntTactZeroZ = 0 + COUNT FOR NVL(nnir,0) = 0 TO lnCntTactZeroZ + DO NnirAssert WITH 'sync_tact_zero', 'ZERO', lnCntTactRamasVechiZ = 0 AND lnCntTactZeroZ >= lnCntTactVechiZ, ; + TRANSFORM(lnCntTactRamasVechiZ) + ' randuri tact au ramas pe valoarea veche (asteptat 0), ' + ; + TRANSFORM(lnCntTactZeroZ) + ' au ajuns la 0 (asteptat cel putin ' + TRANSFORM(lnCntTactVechiZ) + ')' + + * verificare explicita in cursorul legat de grid-ul de rulaje (pgfArticole.page1.grdRulaje) + SELECT trul + lnCntTrulRamasVechiZ = 0 + COUNT FOR NVL(nnir,0) = lnNnirVechiZ TO lnCntTrulRamasVechiZ + lnCntTrulZeroZ = 0 + COUNT FOR NVL(nnir,0) = 0 TO lnCntTrulZeroZ + DO NnirAssert WITH 'sync_trul_zero', 'ZERO', lnCntTrulRamasVechiZ = 0 AND lnCntTrulZeroZ >= lnCntTrulVechiZ, ; + TRANSFORM(lnCntTrulRamasVechiZ) + ' randuri trul au ramas pe valoarea veche (asteptat 0), ' + ; + TRANSFORM(lnCntTrulZeroZ) + ' au ajuns la 0 (asteptat cel putin ' + TRANSFORM(lnCntTrulVechiZ) + ')' + ELSE + DO NnirAssert WITH 'locate_rand_vechi', 'ZERO', .F., 'nu s-a gasit randul cu nnir vechi in tact' + ENDIF + ENDIF + + Omodif.Release() + Omodif = NULL + ENDIF + DO NnirCurataCursoare +ENDIF + +*===================================================================================== +* Scenariul 6: nota de inventar (id_set=90101) pe sucursala CURENTA - blocarea NNIR trebuie sa +* vina din regula 90101, nu din blocarea pe sucursala din AfterRowColChange. Scenariul 4 ruleaza +* pe o nota din alta sucursala, unde SetAll("ReadOnly",.T.) blocheaza oricum tot gridul, deci ar +* trece si fara implementare. Aici gnIdSucursala se aliniaza la id_sucursala notei (llReadOnly +* iese .F.), se declanseaza AfterRowColChange (care face SetAll dupa Init) si se verifica ca +* Column44 ramane blocata cat timp restul coloanelor sunt editabile. +*===================================================================================== +IF INLIST(tcMode, 'ALL', 'INVENTAR2') + LOCAL lnCod6, lnAn6, lnLuna6, lnIdSet6 + LOCAL i6, llGasit6, llReadOnly6, lnEditabile6, lnSucVechi6, llMamaVechi6 + LOCAL lnIdSucNota6, lcColName6 + + lnCod6 = 1140716 + lnAn6 = 2026 + lnLuna6 = 3 + lnIdSet6 = 90101 + + DO NnirConstruiesteCursoare WITH lnCod6, lnAn6, lnLuna6 + + SELECT tact + GO TOP + lnIdSucNota6 = NVL(tact.id_sucursala, 0) + lnSucVechi6 = gnIdSucursala + llMamaVechi6 = glEMama + gnIdSucursala = lnIdSucNota6 + glEMama = .F. + + Omodif = CREATEOBJECT('frm_modific2024', lnIdSet6) + IF VARTYPE(Omodif) <> 'O' + DO NnirAssert WITH 'instantiere', 'INVENTAR2', .F., 'frm_modific2024 nu s-a instantiat' + ELSE + SELECT tact + GO TOP + Omodif.grid1.SetFocus + Omodif.gridextra1.setup() + Omodif.CalculeazaTotal() + + * AfterRowColChange face SetAll("ReadOnly", llReadOnly) peste blocarea pusa in Init + Omodif.grid1.AfterRowColChange(1) + + llGasit6 = .F. + llReadOnly6 = .F. + lnEditabile6 = 0 + FOR i6 = 1 TO Omodif.grid1.ColumnCount + lcColName6 = 'Omodif.grid1.Columns(' + TRANSFORM(i6) + ')' + IF NnirEsteColoanaNnir(EVALUATE(lcColName6 + '.ControlSource')) + llGasit6 = .T. + llReadOnly6 = EVALUATE(lcColName6 + '.ReadOnly') + ELSE + IF !EVALUATE(lcColName6 + '.ReadOnly') + lnEditabile6 = lnEditabile6 + 1 + ENDIF + ENDIF + ENDFOR + + DO NnirAssert WITH 'coloana_grid', 'INVENTAR2', llGasit6, 'ControlSource=nnir gasit=' + TRANSFORM(llGasit6) + * control: daca gridul e blocat integral, testul de blocare NNIR nu dovedeste nimic + DO NnirAssert WITH 'grid_editabil', 'INVENTAR2', lnEditabile6 > 0, ; + 'coloane editabile (fara nnir): ' + TRANSFORM(lnEditabile6) + ' (asteptat > 0, ' + ; + 'gnIdSucursala=' + TRANSFORM(lnIdSucNota6) + ')' + IF llGasit6 + DO NnirAssert WITH 'coloana_blocata', 'INVENTAR2', llReadOnly6, ; + 'id_set=90101 pe sucursala curenta: ReadOnly asteptat .T., gasit ' + TRANSFORM(llReadOnly6) + ENDIF + + Omodif.Release() + Omodif = NULL + ENDIF + + gnIdSucursala = lnSucVechi6 + glEMama = llMamaVechi6 + DO NnirCurataCursoare +ENDIF + +DO NnirLog WITH 'REZULTAT SUITA: ' + TRANSFORM(gnPass) + ' PASS, ' + TRANSFORM(gnFail) + ' FAIL' +QUIT + +*===================================================================================== +* proceduri ajutatoare (fara CREATEOBJECT - vezi depanare_testare_vfp.md / capcana l: +* CREATEOBJECT apelat dintr-o procedura se comporta gresit in acest runtime headless) +*===================================================================================== + +PROCEDURE NnirLog + LPARAMETERS tcMsg + STRTOFILE(tcMsg + CHR(13)+CHR(10), gcLogFile, 1) +ENDPROC + +PROCEDURE NnirAssert + LPARAMETERS tcPas, tcCaz, llOk, tcDetaliu + LOCAL lcRez + lcRez = IIF(llOk, 'PASS', 'FAIL') + IF llOk + gnPass = gnPass + 1 + ELSE + gnFail = gnFail + 1 + ENDIF + DO NnirLog WITH lcRez + ' [' + tcCaz + '/' + tcPas + '] ' + tcDetaliu +ENDPROC + +PROCEDURE NnirErr + LPARAMETERS tnErr, tcMsg, tcProg, tnLine + DO NnirLog WITH 'ERR ' + TRANSFORM(tnErr) + ' [' + tcProg + ':' + TRANSFORM(tnLine) + '] ' + tcMsg +ENDPROC + +* verifica ca toate variabilele folosite ca ?param in SQL pass-through pe calea de test (env-init, +* optiuni_firma, oscrie_in_fisiere, pack_contafin/pack_sesiune) sunt definite - o variabila +* PUBLIC lipsa/umbrita da direct dialogul modal "Enter the value for ", nu o eroare catchabila +FUNCTION NnirGardaVariabile + LOCAL laVar(9), i, llOk + laVar(1) = 'gnIdUtil' + laVar(2) = 'gnAn' + laVar(3) = 'gnLuna' + laVar(4) = 'gnIdFirma' + laVar(5) = 'gnIdSucursala' + laVar(6) = 'gcS' + laVar(7) = 'gnIdProgram' + laVar(8) = 'gnId_Prg_Owner' + laVar(9) = 'gnHandle' + llOk = .T. + FOR i = 1 TO ALEN(laVar) + IF TYPE(laVar(i)) == 'U' + DO NnirLog WITH 'FAIL [garda_variabile] ' + laVar(i) + ' nu exista (TYPE U) - ar fi dat dialog modal "Enter the value"' + llOk = .F. + ENDIF + ENDFOR + RETURN llOk +ENDFUNC + +* VFP calificat automat ControlSource-ul unui camp simplu cu aliasul cursorului (RecordSource) - +* "nnir" definit in sursa apare la runtime ca "tact.nnir" - se accepta ambele forme +FUNCTION NnirEsteColoanaNnir + LPARAMETERS tcControlSource + LOCAL lcCs + lcCs = UPPER(ALLTRIM(TRANSFORM(tcControlSource))) + RETURN lcCs == 'NNIR' OR RIGHT(lcCs, 5) == '.NNIR' +ENDFUNC + +* construieste tact/trul/trul_obinv exact ca afisjurcom.do_modifica (comun.vc2:2352-2434), +* dar pe AN/LUNA proprii notei (nu gnAn/gnLuna) - vezi antetul fisierului +PROCEDURE NnirConstruiesteCursoare + LPARAMETERS tnCod, tnAn, tnLuna + LOCAL lcSql, lnSucces + + DO NnirCurataCursoare + + lcSql = [select * from vact_tot where sters = 0 and an = ] + TRANSFORM(tnAn) + ; + [ and luna = ] + TRANSFORM(tnLuna) + [ and cod = ] + TRANSFORM(tnCod) + [ order by id_act] + lnSucces = goExecutor.oExecute(lcSql, 'v_act') + goExecutor.oReset() + IF lnSucces < 0 + DO NnirLog WITH 'EROARE construire tact: ' + goExecutor.cEroare + RETURN .F. + ENDIF + SELECT * FROM v_act INTO CURSOR actactan + SELECT .F. AS ales, T.*, CAST(0 AS N(2)) AS tipnota FROM actactan T INTO CURSOR tact READWRITE + USE IN v_act + + lcSql = [select * from vrul_tot where sters = 0 and an = ] + TRANSFORM(tnAn) + ; + [ and luna = ] + TRANSFORM(tnLuna) + [ and cod = ] + TRANSFORM(tnCod) + [ order by id_rul] + goExecutor.oExecute(lcSql, 'v_rul') + goExecutor.oReset() + SELECT *, valoare + valtva AS valoarectva, valoarev + valtvav AS valoarevctva ; + FROM v_rul INTO CURSOR RUL_TEMP READWRITE + SELECT * FROM RUL_TEMP INTO CURSOR trul READWRITE + USE IN v_rul + + lcSql = [select * from vrul_obinv_tot where sters = 0 and an = ] + TRANSFORM(tnAn) + ; + [ and luna = ] + TRANSFORM(tnLuna) + [ and cod = ] + TRANSFORM(tnCod) + [ order by id_rul_obinv] + goExecutor.oExecute(lcSql, 'v_rul_obinv') + goExecutor.oReset() + SELECT *, valoare + valtva AS valoarectva, valoarev + valtvav AS valoarevctva ; + FROM v_rul_obinv INTO CURSOR RUL_TEMP_OBINV READWRITE + SELECT * FROM RUL_TEMP_OBINV INTO CURSOR trul_obinv READWRITE + USE IN v_rul_obinv + + SELECT a.*, IIF(NVL(a.id_jtva_coloana,0)=0,0,1) AS cu_tva FROM tact a INTO CURSOR tact READWRITE + SELECT tact + GO TOP + RETURN .T. +ENDPROC + +PROCEDURE NnirCurataCursoare + IF USED('tact') + USE IN tact + ENDIF + IF USED('trul') + USE IN trul + ENDIF + IF USED('trul_obinv') + USE IN trul_obinv + ENDIF + IF USED('actactan') + USE IN actactan + ENDIF + IF USED('rul_temp') + USE IN rul_temp + ENDIF + IF USED('rul_temp_obinv') + USE IN rul_temp_obinv + ENDIF +ENDPROC + +FUNCTION NnirOraCount + LPARAMETERS tcSql + LOCAL lnVal, llOk + lnVal = -1 + llOk = goExecutor.oExecuta(tcSql, 'cNnirOraCnt') + IF llOk AND USED('cNnirOraCnt') AND RECCOUNT('cNnirOraCnt') > 0 + lnVal = cNnirOraCnt.cnt + ENDIF + IF USED('cNnirOraCnt') + USE IN cNnirOraCnt + ENDIF + RETURN lnVal +ENDFUNC + +* verifica ca in Oracle nu s-a scris nimic dupa editarea DOAR in cursoare (echivalent Renunt) +PROCEDURE NnirVerificaOracleNeschimbat + LPARAMETERS tnCod, tnNnirVechi, tcCaz + LOCAL lnCnt, lcSql + lcSql = [select count(*) cnt from act where cod = ] + TRANSFORM(tnCod) + ; + [ and nnir = ] + TRANSFORM(tnNnirVechi) + lnCnt = NnirOraCount(lcSql) + DO NnirAssert WITH 'renunt_neschimbat', tcCaz, lnCnt > 0, ; + 'ACT.NNIR in Oracle inainte de Termin ramane ' + TRANSFORM(tnNnirVechi) + ' (' + TRANSFORM(lnCnt) + ' randuri)' +ENDPROC + +* replica blocul de salvare din afisjurcom.do_modifica (comun.vc2:2456-2487): scrie tact/trul/ +* trul_obinv in ACT/RUL/RUL_OBINV prin oscrie_in_fisiere + pack_contafin.finalizeaza_modificare_ +* nota, verifica in Oracle, apoi reface valoarea originala (revert - baza de test ramane +* neschimbata pentru rulari repetate) +PROCEDURE NnirSalveazaSiVerifica + LPARAMETERS tnCod, tnAn, tnLuna, tnIdSet, tnNnirVechi, tnNnirNou, tcCaz + PRIVATE pnAn, pnLuna, pdDataOra + LOCAL lnIdFact, lnIdfactd, llRul, lnSuccesSalvare, lcSqlFin, lnCnt, lcSql + + pnAn = tnAn + pnLuna = tnLuna + pdDataOra = NULL + + SELECT tact + LOCATE FOR NVL(nnir,0) = tnNnirNou + lnIdFact = NVL(id_fact, 0) + lnIdfactd = NVL(id_factd, 0) + + llRul = (RECCOUNT('trul') > 0) OR (RECCOUNT('trul_obinv') > 0) + + * thisform.do_deschide_tranzactie()/do_inchide_tranzactie() din afisjurcom (_frm_base.vc2:252- + * 305, mostenite, apelate pe formularul de jurnal, nu pe frm_modific2024) - echivalent direct, + * fara instanta de forma: manual pe durata scrierii, apoi commit/rollback + revenire pe auto + SQLSetProp(gnHandle, 'Transactions', 2) + + * replica exact secventa din afisjurcom.do_modifica: intai STERGE nota veche (COD-ul + * original, din actactan neatins de editare), abia apoi SCRIE varianta editata + SELECT actactan + lnSuccesSalvare = oscrie_in_fisiere(2, .T., llRul) + DO NnirAssert WITH 'oscrie_in_fisiere_sterge', tcCaz, lnSuccesSalvare > 0, 'a returnat ' + TRANSFORM(lnSuccesSalvare) + + IF lnSuccesSalvare > 0 + SELECT * FROM tact INTO CURSOR actactan READWRITE + REPLACE ALL id_util WITH gnIdUtil, sters WITH 0 IN actactan + + IF llRul + SELECT * FROM trul INTO CURSOR rul_temp READWRITE + REPLACE ALL id_util WITH gnIdUtil, sters WITH 0 IN rul_temp + SELECT * FROM trul_obinv INTO CURSOR rul_temp_obinv READWRITE + REPLACE ALL id_util WITH gnIdUtil, sters WITH 0 IN rul_temp_obinv + ENDIF + + lnSuccesSalvare = oscrie_in_fisiere(0, .T., llRul) + DO NnirAssert WITH 'oscrie_in_fisiere', tcCaz, lnSuccesSalvare > 0, 'a returnat ' + TRANSFORM(lnSuccesSalvare) + ENDIF + + IF lnSuccesSalvare > 0 + lcSqlFin = [begin pack_contafin.finalizeaza_modificare_nota(?pnLuna,?pnAn,?pdDataOra,] + ; + TRANSFORM(tnIdSet) + [,] + TRANSFORM(tnCod) + [,] + TRANSFORM(lnIdFact) + [,] + ; + TRANSFORM(lnIdfactd) + [,?gnIdUtil); end;] + lnSuccesSalvare = IIF(goExecutor.oExecuta(lcSqlFin), 1, -1) + DO NnirAssert WITH 'finalizeaza_modificare_nota', tcCaz, lnSuccesSalvare > 0, 'a returnat ' + TRANSFORM(lnSuccesSalvare) + ENDIF + + IF lnSuccesSalvare > 0 + SQLCOMMIT(gnHandle) + ELSE + SQLROLLBACK(gnHandle) + ENDIF + SQLSetProp(gnHandle, 'Transactions', 1) + + * Termin reatribuie COD nou (pack_contafin), sub AN/LUNA de sesiune curente - nu pastreaza + * COD-ul original notei editate. Verificarea si curatenia se fac dupa valoarea NNIR marcaj + * (distincta, nu poate coincide cu date reale), nu dupa COD-ul original. + lcSql = [select count(*) cnt from act where sters = 0 and nnir = ] + TRANSFORM(tnNnirNou) + lnCnt = NnirOraCount(lcSql) + DO NnirAssert WITH 'oracle_act_final', tcCaz, lnCnt > 0, TRANSFORM(lnCnt) + ' randuri ACT cu nnir nou in Oracle' + + IF RECCOUNT('trul') > 0 + lcSql = [select count(*) cnt from rul where sters = 0 and nnir = ] + TRANSFORM(tnNnirNou) + lnCnt = NnirOraCount(lcSql) + DO NnirAssert WITH 'oracle_rul_final', tcCaz, lnCnt > 0, TRANSFORM(lnCnt) + ' randuri RUL cu nnir nou in Oracle' + ENDIF + + * curatenie: pasul STERGE (oscrie_in_fisiere param 2) marcheaza sters=1 pe COD-ul ORIGINAL - + * readuc nota originala activa, apoi marchez sters=1 (conventia aplicatiei) pe orice a ajuns + * sub NNIR-ul marcaj (COD nou, atribuit de pack_contafin la scriere) + goExecutor.oExecuta([update act set sters = 0 where cod = ] + TRANSFORM(tnCod)) + goExecutor.oExecuta([update rul set sters = 0 where cod = ] + TRANSFORM(tnCod)) + goExecutor.oExecuta([update rul_obinv set sters = 0 where cod = ] + TRANSFORM(tnCod)) + goExecutor.oExecuta([update act set sters = 1 where nnir = ] + TRANSFORM(tnNnirNou) + [ and cod <> ] + TRANSFORM(tnCod)) + goExecutor.oExecuta([update rul set sters = 1 where nnir = ] + TRANSFORM(tnNnirNou) + [ and cod <> ] + TRANSFORM(tnCod)) + goExecutor.oExecuta([update rul_obinv set sters = 1 where nnir = ] + TRANSFORM(tnNnirNou) + [ and cod <> ] + TRANSFORM(tnCod)) + goExecutor.oExecuta('commit') +ENDPROC + +* varianta pt. scenariul RUL_OBINV (verifica RUL_OBINV in loc de RUL) +PROCEDURE NnirSalveazaSiVerificaObinv + LPARAMETERS tnCod, tnAn, tnLuna, tnIdSet, tnNnirVechi, tnNnirNou + PRIVATE pnAn, pnLuna, pdDataOra + LOCAL lnIdFact, lnIdfactd, llRul, lnSuccesSalvare, lcSqlFin, lnCnt, lcSql + + pnAn = tnAn + pnLuna = tnLuna + pdDataOra = NULL + + SELECT tact + LOCATE FOR NVL(nnir,0) = tnNnirNou + lnIdFact = NVL(id_fact, 0) + lnIdfactd = NVL(id_factd, 0) + + llRul = (RECCOUNT('trul') > 0) OR (RECCOUNT('trul_obinv') > 0) + + * thisform.do_deschide_tranzactie()/do_inchide_tranzactie() din afisjurcom (_frm_base.vc2:252- + * 305, mostenite, apelate pe formularul de jurnal, nu pe frm_modific2024) - echivalent direct, + * fara instanta de forma: manual pe durata scrierii, apoi commit/rollback + revenire pe auto + SQLSetProp(gnHandle, 'Transactions', 2) + + * replica exact secventa din afisjurcom.do_modifica: intai STERGE nota veche (COD-ul + * original, din actactan neatins de editare), abia apoi SCRIE varianta editata + SELECT actactan + lnSuccesSalvare = oscrie_in_fisiere(2, .T., llRul) + DO NnirAssert WITH 'oscrie_in_fisiere_sterge', 'OBINV', lnSuccesSalvare > 0, 'a returnat ' + TRANSFORM(lnSuccesSalvare) + + IF lnSuccesSalvare > 0 + SELECT * FROM tact INTO CURSOR actactan READWRITE + REPLACE ALL id_util WITH gnIdUtil, sters WITH 0 IN actactan + + IF llRul + SELECT * FROM trul INTO CURSOR rul_temp READWRITE + REPLACE ALL id_util WITH gnIdUtil, sters WITH 0 IN rul_temp + SELECT * FROM trul_obinv INTO CURSOR rul_temp_obinv READWRITE + REPLACE ALL id_util WITH gnIdUtil, sters WITH 0 IN rul_temp_obinv + ENDIF + + lnSuccesSalvare = oscrie_in_fisiere(0, .T., llRul) + DO NnirAssert WITH 'oscrie_in_fisiere', 'OBINV', lnSuccesSalvare > 0, 'a returnat ' + TRANSFORM(lnSuccesSalvare) + ENDIF + + IF lnSuccesSalvare > 0 + lcSqlFin = [begin pack_contafin.finalizeaza_modificare_nota(?pnLuna,?pnAn,?pdDataOra,] + ; + TRANSFORM(tnIdSet) + [,] + TRANSFORM(tnCod) + [,] + TRANSFORM(lnIdFact) + [,] + ; + TRANSFORM(lnIdfactd) + [,?gnIdUtil); end;] + lnSuccesSalvare = IIF(goExecutor.oExecuta(lcSqlFin), 1, -1) + DO NnirAssert WITH 'finalizeaza_modificare_nota', 'OBINV', lnSuccesSalvare > 0, 'a returnat ' + TRANSFORM(lnSuccesSalvare) + ENDIF + + IF lnSuccesSalvare > 0 + SQLCOMMIT(gnHandle) + ELSE + SQLROLLBACK(gnHandle) + ENDIF + SQLSetProp(gnHandle, 'Transactions', 1) + + * Termin reatribuie COD nou (pack_contafin), sub AN/LUNA de sesiune curente - nu pastreaza + * COD-ul original notei editate. Verificarea si curatenia se fac dupa valoarea NNIR marcaj + * (distincta, nu poate coincide cu date reale), nu dupa COD-ul original. + lcSql = [select count(*) cnt from act where sters = 0 and nnir = ] + TRANSFORM(tnNnirNou) + lnCnt = NnirOraCount(lcSql) + DO NnirAssert WITH 'oracle_act_final', 'OBINV', lnCnt > 0, TRANSFORM(lnCnt) + ' randuri ACT cu nnir nou in Oracle' + + lcSql = [select count(*) cnt from rul_obinv where sters = 0 and nnir = ] + TRANSFORM(tnNnirNou) + lnCnt = NnirOraCount(lcSql) + DO NnirAssert WITH 'oracle_rul_obinv_final', 'OBINV', lnCnt > 0, TRANSFORM(lnCnt) + ' randuri RUL_OBINV cu nnir nou in Oracle' + + * curatenie: pasul STERGE (oscrie_in_fisiere param 2) marcheaza sters=1 pe COD-ul ORIGINAL - + * readuc nota originala activa, apoi marchez sters=1 (conventia aplicatiei) pe orice a ajuns + * sub NNIR-ul marcaj (COD nou, atribuit de pack_contafin la scriere) + goExecutor.oExecuta([update act set sters = 0 where cod = ] + TRANSFORM(tnCod)) + goExecutor.oExecuta([update rul set sters = 0 where cod = ] + TRANSFORM(tnCod)) + goExecutor.oExecuta([update rul_obinv set sters = 0 where cod = ] + TRANSFORM(tnCod)) + goExecutor.oExecuta([update act set sters = 1 where nnir = ] + TRANSFORM(tnNnirNou) + [ and cod <> ] + TRANSFORM(tnCod)) + goExecutor.oExecuta([update rul set sters = 1 where nnir = ] + TRANSFORM(tnNnirNou) + [ and cod <> ] + TRANSFORM(tnCod)) + goExecutor.oExecuta([update rul_obinv set sters = 1 where nnir = ] + TRANSFORM(tnNnirNou) + [ and cod <> ] + TRANSFORM(tnCod)) + goExecutor.oExecuta('commit') +ENDPROC diff --git a/utile/Teste/nnir_registru_jurnal/verif_nnir_cod.sql b/utile/Teste/nnir_registru_jurnal/verif_nnir_cod.sql new file mode 100644 index 0000000..e9d06e2 --- /dev/null +++ b/utile/Teste/nnir_registru_jurnal/verif_nnir_cod.sql @@ -0,0 +1,11 @@ +-- verif_nnir_cod.sql - NNIR distinct pe ACT/RUL/RUL_OBINV pentru un COD dat +-- Rulare: sqlplus -L -S MARIUSM_AUTO/@ROA_CENTRAL @verif_nnir_cod.sql +set pagesize 200 linesize 200 trimspool on feedback off +define v_cod = &1 +prompt ==ACT== +select cod, an, luna, id_set, nnir from act where cod = &v_cod; +prompt ==RUL_NNIR_DISTINCT== +select an, luna, nnir, count(*) cnt from rul where cod = &v_cod group by an, luna, nnir; +prompt ==RUL_OBINV_NNIR_DISTINCT== +select an, luna, nnir, count(*) cnt from rul_obinv where cod = &v_cod group by an, luna, nnir; +exit; diff --git a/utile/Teste/test_init_env_auto_roagest.prg b/utile/Teste/test_init_env_auto_roagest.prg new file mode 100644 index 0000000..9d4e150 --- /dev/null +++ b/utile/Teste/test_init_env_auto_roagest.prg @@ -0,0 +1,302 @@ +* test_init_env_auto_roagest.prg +* Varianta ROAGEST a test_init_env_auto.prg (acel fisier e specializat pe ROACONT - +* gcAppPath/clase/proceduri hardcodate D:\ROA\ROACONT\ - nu poate initializa mediul ROAGEST). +* Initializare completa de mediu FARA interactiune, pentru vfp9.exe -A -T. +* +* Utilizare: +* DO test_init_env_auto_roagest WITH 'CENTRAL', 'MARIUSM_AUTO', 'parola' +* IF gnHandle <= 0 ... eroare conectare +* +* Dupa apel sunt disponibile: goConn/goExecutor/goApp/goLog/goApi, goFirma/goCalendar, +* gnAn/gnLuna/gcS/gnIdFirma etc., SET PATH/CLASSLIB/PROCEDURE ca in roagest.prg, +* pachetele Oracle de sesiune setate (pack_sesiune/pack_contafin). +* ATENTIE: mentine lista de SET CLASSLIB/PROCEDURE sincronizata cu Programe\roagest.prg. + +LPARAMETERS tcHost, tcSchema, tcPassword, tnAn, tnLuna + +tcHost = IIF(EMPTY(tcHost), 'CENTRAL', tcHost) +tcSchema = IIF(EMPTY(tcSchema), 'MARIUSM_AUTO', tcSchema) +tcPassword = IIF(EMPTY(tcPassword), 'ROMFASTSOFT', tcPassword) + +SET DELETED ON +SET ANSI ON +SET EXACT ON +SET SAFETY OFF +CLOSE DATABASES +SET CENTURY ON +SET DATE DMY +SET NULLDISPLAY TO '' +SET DECIMALS TO 4 +SET POINT TO '.' + +PUBLIC CT_SUCCES, CT_INSUCCES, crlf, gnAn, gnLuna, gnIdUtil, glEMama, gnIdFirma, gcCondSucursala, ; + gnIdSucursala, gcS, gcFirma, gcUserName, gcUserNameApp, gcNumeProgram, gcAppName, glLunaInchisa, ; + buton, gnButon, pnButon, gl406, gcAcces, gnIdProgram, gnId_Prg_Owner +CT_SUCCES = 1 +CT_INSUCCES = -1 +crlf = CHR(13) + CHR(10) +gnAn = IIF(EMPTY(tnAn), YEAR(DATE()), tnAn) +gnLuna = IIF(EMPTY(tnLuna), MONTH(DATE()), tnLuna) +gnIdUtil = -3 +gnIdProgram = 0 +gnId_Prg_Owner = 0 +gnIdFirma = 110 +gcCondSucursala = [] +gnIdSucursala = m.gnIdFirma +gcS = UPPER(ALLTRIM(tcSchema)) +gcFirma = m.gcS +gcUserName = m.gcS +gcUserNameApp = [MARIUS M] +gcNumeProgram = [ROAGEST] +gcAppName = [ROAGEST] +glLunaInchisa = .F. +glEMama = .F. +gl406 = .F. +* PUBLIC, nu Declare: nrcrt() (oproceduri_comune.prg) citeste NROR din ControlSource-uri +* de grid mult dupa ce acest program s-a terminat (Declare ar muri la RETURN) +PUBLIC nror(65000) +buton = 1 +gnButon = 1 +pnButon = 1 + +LOCAL lcPath +PUBLIC gcAppPath, DIRGEN, gcDirMare, gcBasePath, gcTempPath, gcGeneralIniFile, gcSettingsFile, ; + gcSecurityPath, gcSecurityFile +gcAppPath = [D:\ROA\ROAGEST\] +Set Default To (gcAppPath) +lcPath = gcAppPath + 'Date;' + ; + gcAppPath + 'Include;' + ; + gcAppPath + 'FERESTRE;' + ; + gcAppPath + 'GRAFICE;' + ; + gcAppPath + 'CLASE;' + ; + gcAppPath + 'MENIURI;' + ; + gcAppPath + 'PROGRAME;' + ; + gcAppPath + 'RAPOARTE;' + ; + gcAppPath + 'COMUN;' + ; + gcAppPath + 'COMUN\CLASE;' + ; + gcAppPath + 'COMUN\FERESTRE;' + ; + gcAppPath + 'COMUN\PROGRAME;' + ; + gcAppPath + 'COMUN\GRAFICE;' + ; + gcAppPath + 'COMUN\RAPOARTE;' + ; + gcAppPath + 'COMUN\MENIURI;' + ; + gcAppPath + 'COMUN\UTILE\GRIDEXTRAS;' + ; + gcAppPath + 'COMUN\UTILE\CTL32;' + ; + gcAppPath + 'COMUN\UTILE\HPDF;' + ; + gcAppPath + 'COMUN\UTILE\HPDF\REPORTOUTPUT;' + ; + gcAppPath + 'COMUN\UTILE\WEB;' + ; + gcAppPath + 'COMUN\UTILE\EXCEL;' + ; + gcAppPath + 'COMUN\UTILE\NFJSON;' + ; + Addbs(Substr(gcAppPath,1,Rat([\],gcAppPath,2)))+[COMUNROA\] +SET PATH TO &lcPath ADDITIVE + +Local lcMainClassLib +lcMainClassLib = gcAppPath + "comun\clase\appwiz" +SET CLASSLIB TO (lcMainClassLib) ADDITIVE +SET CLASSLIB TO GESTIUNI ADDITIVE +SET CLASSLIB TO CAUT ADDITIVE +SET CLASSLIB TO BAZA ADDITIVE +SET CLASSLIB TO comun ADDITIVE +SET CLASSLIB TO accessibility.vcx ADDITIVE +SET CLASSLIB TO messagebox ADDITIVE +SET CLASSLIB TO registry ADDITIVE +SET CLASSLIB TO cauta_alfa_forms.vcx ADDITIVE +SET CLASSLIB TO DECABAZA ADDITIVE +SET CLASSLIB TO onomenclatoare ADDITIVE +SET CLASSLIB TO stocuri.vcx ADDITIVE +SET CLASSLIB TO rulaje.vcx ADDITIVE +SET CLASSLIB TO ointroduceri ADDITIVE +SET CLASSLIB TO ointroduceri_web ADDITIVE +SET CLASSLIB TO ointroduceri_depozit ADDITIVE +SET CLASSLIB TO overificari ADDITIVE +SET CLASSLIB TO ferestre_oracle ADDITIVE +SET CLASSLIB TO ferestre_cere_date.vcx ADDITIVE +SET CLASSLIB TO configurare.vcx ADDITIVE +SET CLASSLIB TO serii_numere.vcx ADDITIVE +SET CLASSLIB TO omodificari.vcx ADDITIVE +SET CLASSLIB TO ocompensari.vcx ADDITIVE +SET CLASSLIB TO caut_ora ADDITIVE +SET CLASSLIB TO onote_contabile ADDITIVE +SET CLASSLIB TO otoolbar ADDITIVE +SET CLASSLIB TO bon_fisc ADDITIVE +SET CLASSLIB TO onom_articole ADDITIVE +SET CLASSLIB TO onom_retete ADDITIVE +Set Classlib To oinventar ADDITIVE +SET CLASSLIB TO orapoarte ADDITIVE +SET CLASSLIB TO orapoarte_gestiuni ADDITIVE +SET CLASSLIB TO orapoarte_parametri ADDITIVE +SET CLASSLIB TO ONOM_CURS ADDITIVE +SET CLASSLIB TO ctl32_statusbar_fals.vcx additive +SET CLASSLIB TO ctl32_common.vcx additive +SET CLASSLIB TO ctl32_structs.vcx additive +SET CLASSLIB TO ctl32_progressbar.vcx additive +SET CLASSLIB TO ctl32_balloontip.vcx additive +SET CLASSLIB TO ocriterii.vcx ADDITIVE +SET CLASSLIB TO oavize.vcx ADDITIVE +SET CLASSLIB TO onomenclatoare2 ADDITIVE +Set Classlib To oschimbare_pret.vcx additive +SET CLASSLIB TO wwdialogs.vcx additive +SET CLASSLIB TO oimportxml.vcx additive +SET CLASSLIB TO excelxml.vcx ADDITIVE +SET CLASSLIB TO orapoarte_obinv.vcx ADDITIVE +SET CLASSLIB TO locale ADDITIVE + +SET PROCEDURE TO PROCEDURI ADDITIVE +SET PROCEDURE TO acces_meniu ADDITIVE +SET PROCEDURE TO cauta_alfa ADDITIVE +SET PROCEDURE TO pmenu ADDITIVE +SET PROCEDURE TO proceduri_comune ADDITIVE +SET PROCEDURE TO quitapp ADDITIVE +SET PROCEDURE TO init_program ADDITIVE +SET PROCEDURE TO oproceduri_listari.prg ADDITIVE +SET PROCEDURE TO oproceduri_configurare.prg ADDITIVE +SET PROCEDURE TO oproceduri_articole.prg ADDITIVE +SET PROCEDURE TO oproceduri_retete.prg ADDITIVE +SET PROCEDURE TO orapoarte.prg ADDITIVE +SET PROCEDURE TO mesaje ADDITIVE +SET PROCEDURE TO oserii_numere.prg ADDITIVE +SET PROCEDURE TO oexport.prg ADDITIVE +SET PROCEDURE TO wwConfig.prg ADDITIVE +SET PROCEDURE TO proceduri_rapoarte.prg additive +SET PROCEDURE TO OPROCEDURI_CURS.PRG ADDITIVE +Set procedure to orapoarte_dinamice.prg additive +SET PROCEDURE TO validare.prg additive +SET PROCEDURE TO GENCURSOR.PRG ADDITIVE +SET PROCEDURE TO OPROCEDURI_COMUNE.PRG ADDITIVE +SET PROCEDURE TO OINIT_OPTIUNI.PRG ADDITIVE +SET PROCEDURE TO updateserver.prg ADDITIVE +SET PROCEDURE TO oinainte_de.prg ADDITIVE +SET PROCEDURE TO ooperatii_comune.prg ADDITIVE +SET PROCEDURE TO Ocompensari.PRG ADDITIVE +SET PROCEDURE TO OPROCEDURI_aMS.PRG ADDITIVE +SET PROCEDURE TO oproceduri_rulaje additive +SET PROCEDURE TO oproceduri_stocuri additive +SET PROCEDURE TO OINTRODUCERI ADDITIVE +SET PROCEDURE TO oHeader.prg ADDITIVE +SET PROCEDURE TO proceduri_excel.PRG ADDITIVE +SET PROCEDURE TO onomenclatoare.PRG ADDITIVE +SET PROCEDURE TO osecurity ADDITIVE +SET PROCEDURE TO ocautare ADDITIVE +SET PROCEDURE TO orefaceri ADDITIVE +SET PROCEDURE TO ofacturare_comun.PRG ADDITIVE +SET PROCEDURE TO ofacturare_stoc.PRG ADDITIVE +SET PROCEDURE TO oproceduri_casademarcat ADDITIVE +SET PROCEDURE TO oproceduri_casa_marcat_e500 ADDITIVE +SET PROCEDURE TO oproceduri_casa_marcat_mp500 ADDITIVE +SET PROCEDURE TO oproceduri_util.prg ADDITIVE +SET PROCEDURE TO wwxmlhttp.prg ADDITIVE +SET PROCEDURE TO ini.prg ADDITIVE +SET PROCEDURE TO odocumente.prg ADDITIVE +SET PROCEDURE TO regex.prg ADDITIVE +SET PROCEDURE TO wwutils.prg ADDITIVE +SET PROCEDURE TO inchidere_k ADDITIVE +Set procedure to oschimbare_pret.prg additive +SET PROCEDURE TO iniacces.prg ADDITIVE +SET PROCEDURE TO oupdate.prg additive +SET PROCEDURE TO procese.prg additive +SET PROCEDURE TO version.prg additive +SET PROCEDURE TO xmlaccess.prg additive +SET PROCEDURE TO xmlparser.prg additive +SET PROCEDURE TO filebringer.prg additive +SET PROCEDURE TO wwcodeupdate.prg additive +SET PROCEDURE TO wwhttp.prg ADDITIVE +Set Procedure To wwApi.prg Additive +SET PROCEDURE TO xdate.prg Additive +SET PROCEDURE TO ofacturare.prg ADDITIVE +SET PROCEDURE TO oproceduri_obinv.prg ADDITIVE +SET PROCEDURE TO oproceduri_facturare.prg ADDITIVE +SET PROCEDURE TO email.prg ADDITIVE + +Declare Integer GetPrivateProfileString In WIN32API ; + string, String, String, String @, Integer, String +Declare Integer WritePrivateProfileString In Win32Api ; + string, String, String, String +Declare Integer CopyFile In WIN32API ; + STRING lpExistingFileName, STRING lpNewFileName, INTEGER bFailIfExists +Declare Integer PathFileExists In shlwapi STRING pszPath + +If Type('goApi') = 'U' + Public goApi + goApi = Newobject("wwAPI", "wwAPI.prg") +ENDIF + +PUBLIC gnHandle, goExecutor, goConn, goFirma, goCalendar, poLog, goLog, goApp +DIRGEN = [D:\ROA\] +gcDirMare = DIRGEN +gcBasePath = dirgen +gcSecurityPath = DIRGEN + 'Security\' +gcSecurityFile = gcSecurityPath + 'ROA_SECURITY.TXT' +gcGeneralIniFile = DIRGEN + "settings.ini" +gcSettingsFile = m.gcGeneralIniFile +gcTempPath = Addbs(shortpath(goApi.GetTempPath())) + gcS + [\] +If !Directory(gcTempPath) + Md (gcTempPath) +Endif +gcAcces = [1;2;3;4;5;6;7;8;] + +poLog = Newobject("Log_Mesaje","Log_Mesaje.prg") +goLog = poLog +goApp = Createobject("wzApplication") +gnHandle = 0 +goExecutor = createobject("oExecutor") +goConn = createobject("oConn") +goConn.Connect(m.tcHost, m.tcSchema, m.tcPassword) +IF m.gnHandle <= 0 + RETURN .F. +ENDIF + +LOCAL llSucces, lcSql +goFirma = null +llSucces = goExecutor.oExecuta("select * from syn_v_nom_firme where schema = ?gcS", "cFirma") +IF m.llSucces + SELECT cFirma + gnIdFirma = id_firma + gnIdSucursala = null + gcFirma = ALLTRIM(firma) + SCATTER NAME goFirma + IF TYPE('goFirma.codfiscalfro') = 'U' + ADDPROPERTY(goFirma, 'codfiscalfro', ALLTRIM(STRTRAN(ALLTRIM(NVL(goFirma.cod_fiscal, '')), 'RO', ''))) + ENDIF +ELSE + goFirma = CREATEOBJECT('custom') + goFirma.addproperty('cod_fiscal', '') + goFirma.addproperty('schema', m.gcS) + goFirma.addproperty('firma', m.gcS) + goFirma.addproperty('codfiscalfro', '') +ENDIF +USE IN (SELECT('cFirma')) + +goCalendar = null +llSucces = goExecutor.oExecuta('SELECT anul, luna, tva_incasare FROM calendar WHERE an*12+luna = (select MAX(an*12+luna) FROM calendar)', 'cCalendar') +IF m.llSucces + SELECT cCalendar + GO TOP + IF EMPTY(m.tnAn) + gnAn = anul + gnLuna = luna + ENDIF + SCATTER NAME goCalendar +ENDIF +USE IN (SELECT('cCalendar')) + +CREATE CURSOR dual (dummy c(10)) +INSERT INTO dual (dummy) VALUES ("") + +actualizeaza_optiuni() +Do optiuni_firma IN oinit_optiuni.prg + +lcSql = [begin pack_sesiune.set_Id_Util(?gnIdUtil); end;] +llSucces = goExecutor.oExecuta(lcSql) +lcSql = [begin pack_sesiune.setluna(?gnLuna); pack_sesiune.setan(?gnAn); end;] +llSucces = goExecutor.oExecuta(lcSql) +lcSql = [begin pack_sesiune.setlunabal(?gnLuna); pack_sesiune.setanbal(?gnAn); end;] +llSucces = goExecutor.oExecuta(lcSql) +lcSql = [begin pack_contafin.SET_DATA_RON(?gcS); end;] +llSucces = goExecutor.oExecuta(lcSql) +lcSql = [begin pack_sesiune.set_data_ron(?gcS); end;] +llSucces = goExecutor.oExecuta(lcSql) +lcSql = [begin pack_contafin.set_id_sucursala(?gnIdSucursala); end;] +llSucces = goExecutor.oExecuta(lcSql) +lcSql = [begin pack_contafin.set_id_firma(?gnIdFirma); end;] +llSucces = goExecutor.oExecuta(lcSql) + +RETURN .T. From bd09073229b3daca47b4433c22cd910660ca5f50 Mon Sep 17 00:00:00 2001 From: Marius Mutu Date: Mon, 3 Aug 2026 23:45:58 +0300 Subject: [PATCH 3/9] teste NNIR: avertisment .FXP stale (VFP ruleaza tacut versiunea veche a scriptului) Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01EBFMe75MwdnDQk3qhp1YB2 --- utile/Teste/nnir_registru_jurnal/date_test_nnir.md | 7 +++++++ .../Teste/nnir_registru_jurnal/test_nnir_sincronizare.prg | 2 ++ 2 files changed, 9 insertions(+) diff --git a/utile/Teste/nnir_registru_jurnal/date_test_nnir.md b/utile/Teste/nnir_registru_jurnal/date_test_nnir.md index bac7cdc..e8662db 100644 --- a/utile/Teste/nnir_registru_jurnal/date_test_nnir.md +++ b/utile/Teste/nnir_registru_jurnal/date_test_nnir.md @@ -90,6 +90,13 @@ Note: Fara acest wrapping, `oscrie_in_fisiere`/`finalizeaza_modificare_nota` raporteaza succes dar nu se vede nimic persistat, nici in aceeasi sesiune. +## Capcana: .FXP stale ascunde scenariile noi + +VFP nu recompileaza .prg-ul daca exista un .FXP mai vechi - ruleaza tacut versiunea veche a +scriptului. Simptom: suita raporteaza PASS-uri corecte, dar scenariile adaugate dupa ultima +compilare nu apar deloc in log (verifica numarul de assert-uri, nu doar `0 FAIL`). Sterge +.FXP-ul inainte de fiecare rulare. + ## Capcana: dialog modal "Enter the value for " in loc de eroare catchabila O variabila memorie lipsa/umbrita folosita ca `?param` intr-un SQL pass-through (SQLEXEC/ diff --git a/utile/Teste/nnir_registru_jurnal/test_nnir_sincronizare.prg b/utile/Teste/nnir_registru_jurnal/test_nnir_sincronizare.prg index f78d57b..245f918 100644 --- a/utile/Teste/nnir_registru_jurnal/test_nnir_sincronizare.prg +++ b/utile/Teste/nnir_registru_jurnal/test_nnir_sincronizare.prg @@ -22,6 +22,8 @@ * & "C:\Program Files (x86)\Microsoft Visual FoxPro 9\vfp9.exe" -A -T ; * "D:\ROA\ROAGEST\COMUN\utile\Teste\nnir_registru_jurnal\test_nnir_sincronizare.prg" * Timeout recomandat: minim 220s. Log: %TEMP%\test_nnir_sincronizare_log.txt (PASS/FAIL pe linie). +* STERGE .FXP-ul inainte de rulare: VFP nu recompileaza automat si ruleaza tacut versiunea +* veche a scriptului - scenariile adaugate intre timp lipsesc cu totul din log. LPARAMETERS tcMode tcMode = IIF(EMPTY(tcMode), 'ALL', UPPER(ALLTRIM(tcMode))) From 4d1179f8e5bcc199a8f845e1963cc6f0b2a9fe99 Mon Sep 17 00:00:00 2001 From: Marius Mutu Date: Wed, 5 Aug 2026 21:55:13 +0300 Subject: [PATCH 4/9] diag_spatiu.ps1: aceleasi praguri ca jobul de diagnostic + plafonul editiei XE Pragurile ies in capul scriptului si sunt cele din PACK_DIAG_SPATIU: tablespace-ul intra in atentie sub 2048 MB ramasi sau sub 15% din maximul lui (inainte: sub 1 MB, adica dupa ce ORA-01653 e la usa), iar FRA la 75% in loc de 80%. Sectiune noua 2c: plafonul de date al editiei XE (4 GB pe 10.2, 11 GB pe 11.2, 12 GB de la 18c) - limita e independenta de maxbytes-ul tablespace-urilor, baza poate refuza extinderea cu ORA-12952 desi tablespace-ul mai are loc. Pe alte editii sectiunea se sare. Verificat pe XE 21c: sectiunea 2c da aceleasi cifre ca pachetul. --- utile/diag_spatiu.ps1 | 73 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 62 insertions(+), 11 deletions(-) diff --git a/utile/diag_spatiu.ps1 b/utile/diag_spatiu.ps1 index ea4a5ea..1214391 100644 --- a/utile/diag_spatiu.ps1 +++ b/utile/diag_spatiu.ps1 @@ -1,6 +1,7 @@ # diag_spatiu.ps1 - diagnostic de spatiu pe serverul Oracle al unui client ROA. # Raspunde "cat spatiu mai am si ce ocupa unde": tablespace/datafile (cat mai poate creste), -# audit Oracle (tabele + fisiere .aud), alert log/trace/ADR, archivelog/FRA, spatiu pe discurile +# plafonul de date al editiei XE, audit Oracle (tabele + fisiere .aud), alert log/trace/ADR, +# archivelog/FRA, spatiu pe discurile # fizice ale serverului, si alti mancatori de spatiu (recyclebin, UNDO, istoric statistici/ # scheduler, TEMP, top segmente generic - fara nume de tabele ROA hardcodate). # STRICT READ-ONLY pe baza: doar select/agregate si UTL_FILE in mod 'R'. Actiunile de curatare @@ -29,6 +30,12 @@ param( $ErrorActionPreference = 'Stop' +# praguri de alerta, aceleasi ca in PACK_DIAG_SPATIU (jobul zilnic de la clienti): tablespace-ul +# intra in atentie sub pragul absolut SAU sub procentul din maximul lui, ca sa existe timp de reactie +$PragTsMb = 2048 +$PragTsPct = 15 +$PragFraPct = 75 + if (-not (Test-Path $SqlPlus)) { Write-Output ('EROARE: nu gasesc sqlplus la ' + $SqlPlus) exit 1 @@ -173,16 +180,17 @@ select a.tablespace_name, round(a.max_bytes/1048576) mb_max, round(nvl(tf.free_bytes,0)/1048576) mb_liber, round((nvl(tf.free_bytes,0) + a.creste_bytes)/1048576) mb_poate_creste, - case when a.max_bytes > 0 and (nvl(tf.free_bytes,0) + a.creste_bytes) < 1048576 - then 'ATENTIE: MAXBYTES ATINS' end stare + case when a.max_bytes > 0 and (nvl(tf.free_bytes,0) + a.creste_bytes) < + greatest($PragTsMb * 1048576, a.max_bytes * $PragTsPct / 100) + then 'ATENTIE: APROAPE FARA LOC' end stare from agg a, ts_free tf where a.tablespace_name = tf.tablespace_name(+) order by a.tablespace_name; exit "@ -Show-Section -Title '2. SPATIU TABLESPACE (detaliu pe datafile + cat mai poate creste)' -Text ('mb_poate_creste = liber curent + (maxbytes - alocat) - spatiul real disponibil inainte de ORA-01653, nu doar liberul de acum.' + "`n`n" + $r2.Text) +Show-Section -Title '2. SPATIU TABLESPACE (detaliu pe datafile + cat mai poate creste)' -Text ('mb_poate_creste = liber curent + (maxbytes - alocat) - spatiul real disponibil inainte de ORA-01653, nu doar liberul de acum.' + "`n" + ('Prag de atentie: sub ' + $PragTsMb + ' MB ramasi sau sub ' + $PragTsPct + '% din maximul tablespace-ului.') + "`n`n" + $r2.Text) -# tablespace-urile cu maxbytes efectiv atins - top segmente acolo +# tablespace-urile care au trecut pragul de atentie - top segmente acolo $r2m = Invoke-Sql -Sql @" set pagesize 0 linesize 100 trimspool on feedback off heading off with ts_free as ( @@ -196,7 +204,8 @@ select 'TS_ATENTIE|' || a.tablespace_name from agg a, ts_free tf where a.tablespace_name = tf.tablespace_name(+) and a.max_bytes > 0 - and (nvl(tf.free_bytes,0) + a.creste_bytes) < 1048576; + and (nvl(tf.free_bytes,0) + a.creste_bytes) < + greatest($PragTsMb * 1048576, a.max_bytes * $PragTsPct / 100); exit "@ $tsProblema = @($r2m.Text -split "`r?`n" | ForEach-Object { $_.Trim() } | Where-Object { $_.StartsWith('TS_ATENTIE|') } | ForEach-Object { $_.Substring(11) } | Where-Object { $_ -match '^[A-Z0-9_]+$' }) @@ -263,6 +272,45 @@ exit Write-Output $rPdb.Text } +# ---------- 2c. Plafonul de date al editiei XE ---------- +# limita editiei, independenta de maxbytes-ul tablespace-urilor: extinderea poate fi refuzata cu +# ORA-12952 desi tablespace-ul mai are loc +Write-Output '' +Write-Output '===== 2c. PLAFON DE DATE XE =====' +$xeCritic = $false +if ($r1.Text -notmatch 'Express Edition') { + Write-Output 'Sarit - editia nu e Express Edition, nu exista plafon de date pe editie.' +} else { + $xePlafonGb = if ($verMajor -le 10) { 4 } elseif ($verMajor -eq 11) { 11 } else { 12 } + $rXe = Invoke-Sql -Sql @" +set pagesize 0 linesize 200 trimspool on feedback off heading off +select 'XEFOLOSIT|' || round(nvl(sum(f.bytes),0)/1048576, 2) + from dba_data_files f + where f.tablespace_name not in ('SYSTEM','SYSAUX') + and f.tablespace_name not in (select t.tablespace_name from dba_tablespaces t + where t.contents in ('UNDO','TEMPORARY')); +exit +"@ + $xeFolositMb = $null + foreach ($line in ($rXe.Text -split "`r?`n")) { + if ($line.Trim() -match '^XEFOLOSIT\|([\d.]+)$') { $xeFolositMb = [double]$Matches[1] } + } + if ($null -eq $xeFolositMb) { + Write-Output 'Nu am putut masura datele de utilizator (dba_data_files) - vezi erorile de mai sus.' + Write-Output $rXe.Text + } else { + $xePlafonMb = $xePlafonGb * 1024 + $xeLiberMb = $xePlafonMb - $xeFolositMb + $xePragMb = [math]::Max($PragTsMb, $xePlafonMb * $PragTsPct / 100) + $xeCritic = ($xeLiberMb -lt $xePragMb) + Write-Output ('Editie Express, plafon de date ' + $xePlafonGb + ' GB (masurat pe dba_data_files, fara SYSTEM/SYSAUX/UNDO/TEMP).') + Write-Output (' folosit = ' + [math]::Round($xeFolositMb/1024, 2) + ' GB liber pana la plafon = ' + [math]::Round($xeLiberMb, 0) + ' MB') + if ($xeCritic) { + Write-Output ('[ATENTIE] sub pragul de ' + [math]::Round($xePragMb, 0) + ' MB. Cand plafonul e atins, baza refuza extinderea cu ORA-12952 si nu se mai poate face nimic din baza - elibereaza date sau treci pe alta editie.') + } + } +} + # ---------- 3. Audit Oracle ---------- $r3 = Invoke-Sql -Sql @" set pagesize 0 linesize 250 trimspool on feedback off heading off @@ -369,12 +417,12 @@ exit $fraCritic = $false foreach ($line in ($r5c.Text -split "`r?`n")) { if ($line.Trim() -match '^FRAPCT\|([\d.]+)$') { - if ([double]$Matches[1] -ge 80) { $fraCritic = $true } + if ([double]$Matches[1] -ge $PragFraPct) { $fraCritic = $true } } } if ($fraCritic) { Write-Output '' - Write-Output '[ATENTIE] FRA peste 80% pe cel putin un tip de fisier. Daca log_mode=ARCHIVELOG si FRA se umple complet, baza se blocheaza pe scriere (ORA-19809/ORA-00257).' + Write-Output ('[ATENTIE] FRA peste ' + $PragFraPct + '% din spatiul alocat. Daca log_mode=ARCHIVELOG si FRA se umple complet, baza se blocheaza pe scriere (ORA-19809/ORA-00257).') Write-Output 'Ce se poate face fara backup: marire db_recovery_file_dest_size (daca discul are loc) - imediat, fara pierdere.' Write-Output 'Ce NU se face fara backup real: sterge archivelog-uri neaplicate-la-backup ar rupe recuperarea (RMAN DELETE ARCHIVELOG ... BACKED UP 1 TIMES TO DISK, doar daca exista backup real configurat).' } @@ -602,12 +650,15 @@ Show-Section -Title ('7e. TOP ' + $Top + ' SEGMENTE DIN TOATA BAZA (generic, far Write-Output '' Write-Output '===== 8. VERDICT =====' if ($tsProblema.Count -gt 0) { - Write-Output ('Tablespace-uri la limita (maxbytes atins): ' + ($tsProblema -join ', ') + '. Vezi sectiunea 2 pentru top segmente acolo - candidatii de curatare/crestere maxbytes sunt acolo.') + Write-Output ('Tablespace-uri aproape fara loc: ' + ($tsProblema -join ', ') + '. Vezi sectiunea 2 pentru top segmente acolo - candidatii de curatare/crestere maxbytes sunt acolo.') } else { - Write-Output 'Niciun tablespace la limita de maxbytes gasit (sectiunea 2).' + Write-Output ('Niciun tablespace sub pragul de atentie (' + $PragTsMb + ' MB sau ' + $PragTsPct + '% din maxim, sectiunea 2).') +} +if ($xeCritic) { + Write-Output ('Plafonul de date al editiei XE e aproape atins (sectiunea 2c) - la ORA-12952 nu se mai poate face nimic din baza. Prioritate maxima: eliberare de date sau trecere pe alta editie.') } if ($fraCritic) { - Write-Output 'FRA peste 80% pe cel putin un tip de fisier (sectiunea 5b) - risc de blocaj pe scriere daca log_mode=ARCHIVELOG. Prioritate: verifica arhivarea/backup-ul inainte sa se umple complet.' + Write-Output ('FRA peste ' + $PragFraPct + '% din spatiul alocat (sectiunea 5b) - risc de blocaj pe scriere daca log_mode=ARCHIVELOG. Prioritate: verifica arhivarea/backup-ul inainte sa se umple complet.') } Write-Output 'Ordine tipica de curatare (cea mai sigura primul): fisiere .aud vechi din audit_file_dest (sters direct pe disc, fara privilegii DB) -> adrci purge pe alert/trace/incident vechi -> istoric statistici/scheduler (DBMS_STATS.PURGE_STATS / DBMS_SCHEDULER.PURGE_LOG) -> recyclebin -> resize UNDO dupa ce scade activitatea -> curatare SYS.aud$/fga_log$ sau alte tabele interne (cere SYSDBA) -> ca ultima solutie, recreare PDB (export/import).' if (-not $ConnStrSys) { From 54d97c0349dab1dd7db0e0040858b0ffbdedaa5c Mon Sep 17 00:00:00 2001 From: Marius Mutu Date: Wed, 5 Aug 2026 22:21:32 +0300 Subject: [PATCH 5/9] context_watch.ps1: nu mai raporteaza contextul altei sesiuni La primul mesaj dintr-o sesiune noua transcriptul .jsonl inca nu e creat, asa ca Test-Path pica si scriptul cadea pe fallback-ul "cel mai recent .jsonl din directorul proiectului" - adica transcriptul sesiunii precedente, anuntand un context de sute de k intr-o sesiune goala. Cand calea vine ca parametru sau pe stdin si nu exista, iese tacut; fallback-ul pe director ramane doar pentru invocarea manuala, fara cale. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01UKTBDwMB1dAoN4DPBa1b2m --- utile/context_watch.ps1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/utile/context_watch.ps1 b/utile/context_watch.ps1 index cadd815..78e1745 100644 --- a/utile/context_watch.ps1 +++ b/utile/context_watch.ps1 @@ -19,7 +19,9 @@ if (-not $TranscriptPath) { try { $TranscriptPath = (ConvertFrom-Json $stdin).transcript_path } catch { } } } -if (-not $TranscriptPath -or -not (Test-Path $TranscriptPath)) { +# la primul mesaj al unei sesiuni noi transcriptul inca nu e creat: fara fallback, ca sa nu raporteze alta sesiune +if ($TranscriptPath -and -not (Test-Path $TranscriptPath)) { exit 0 } +if (-not $TranscriptPath) { $dir = Join-Path $env:USERPROFILE ".claude\projects\$((Get-Location).Path -replace '[:\\/]','-')" if (Test-Path $dir) { $f = Get-ChildItem "$dir\*.jsonl" | Sort-Object LastWriteTime -Descending | Select-Object -First 1 From b35e62c880b69cf7fbc6bec46e8dca4a1e46eb19 Mon Sep 17 00:00:00 2001 From: Marius Mutu Date: Wed, 5 Aug 2026 23:36:20 +0300 Subject: [PATCH 6/9] achizitie import: linia de diferenta 6xx fara cod de taxa SAFT --- clase/ointroduceri.vc2 | 5 +-- .../test_runda7_diferente.prg | 36 ++++++++++++++++--- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/clase/ointroduceri.vc2 b/clase/ointroduceri.vc2 index 8618286..208b1c2 100644 --- a/clase/ointroduceri.vc2 +++ b/clase/ointroduceri.vc2 @@ -10199,7 +10199,8 @@ DEFINE CLASS import_nota AS frm_termin_renunt OF "_frm_child.vcx" *!* Randul D, unic pe nota (doc_key gol, nr_doc 99999999): diferenta = baza documentelor *!* care participa la pret - Suma(pret*cant) cu pretul incarcat. Cont dominant = contul de *!* clasa 3 cu baza ABS maxima; fara clasa 3 -> clasa 2, cu contul fix 6588. Diferenta se - *!* scrie doar in lei. Antetul se mosteneste de la documentul principal. + *!* scrie doar in lei, fara TVA si fara cod de taxa SAFT. Antetul se mosteneste de la + *!* documentul principal. Local lcSel, lnMinNrDoc, lnTotalLei, lnTotalVal, lnBazaParticipa Local lcContDominant, lnBazaDominantaAbs, lcScd1, lnDiferentaLei, lnDiferentaVal, lcExplicatie Local llGasitClasa3, loAntet @@ -10310,7 +10311,7 @@ DEFINE CLASS import_nota AS frm_termin_renunt OF "_frm_child.vcx" Replace partc With '', id_partc With 0, partd With '', id_partd With 0 Endif Replace scd With lcScd1, scc With lcContDominant, suma With lnDiferentaLei, suma_val With 0, ; - explicatia With lcExplicatie, id_jtva_coloana With Null, ptva With 0, cu_tva With 0, ; + explicatia With lcExplicatie, id_jtva_coloana With Null, taxcode With 0, ptva With 0, cu_tva With 0, ; in_valuta With 0, participa_valuta With .F., nume_val With '', id_valuta With 0, curs With 0 Select (lcSel) diff --git a/utile/Teste/achizitie_import/test_runda7_diferente.prg b/utile/Teste/achizitie_import/test_runda7_diferente.prg index 2dac160..b42cc49 100644 --- a/utile/Teste/achizitie_import/test_runda7_diferente.prg +++ b/utile/Teste/achizitie_import/test_runda7_diferente.prg @@ -245,11 +245,11 @@ TRY DO CreeazaIntrodc WITH 'introdc' DO CreeazaIntrodc WITH 'introdc_sablon' INSERT INTO introdc_sablon (scd, scc, cu_tva, ordine, id_set, participa_valuta, tip_rand, ; - id_jtva_coloana, ptva, explicatie_tva) ; - VALUES ('371', '401', 1, 1, 1, .T., 'B', 208, 21, 'ACH. INT. 21%') + id_jtva_coloana, ptva, explicatie_tva, taxcode) ; + VALUES ('371', '401', 1, 1, 1, .T., 'B', 208, 21, 'ACH. INT. 21%', 301204) INSERT INTO introdc_sablon (scd, scc, cu_tva, ordine, id_set, participa_valuta, tip_rand, ; - id_jtva_coloana, ptva, explicatie_tva) ; - VALUES ('4426', '401', 1, 2, 1, .T., 'T', 209, 21, 'TVA ACH. INT. 21%') + id_jtva_coloana, ptva, explicatie_tva, taxcode) ; + VALUES ('4426', '401', 1, 2, 1, .T., 'T', 209, 21, 'TVA ACH. INT. 21%', 301204) GO TOP IN introdc_sablon DO CreeazaRulTemp @@ -735,6 +735,34 @@ PROCEDURE VerificaRandD lcMsg = 'FAIL ' + tcLabel + '-randD: nu exista rand tip_rand=D in introdc dupa sincronizeaza() (diferenta asteptata=' + TRANSFORM(lnSumaAsteptata) + ')' ENDIF DO R7Log WITH lcMsg + DO VerificaTaxCodeRandD WITH tcLabel + SELECT (lnSel) +ENDPROC + +PROCEDURE VerificaTaxCodeRandD + *-- randul D (diferenta 6xx) nu are cod de taxa SAFT; randurile de baza si TVA il pastreaza. + LPARAMETERS tcLabel + LOCAL lnSel, lcMsg, lnTaxD, lnFaraTax + lnSel = SELECT() + SELECT introdc + LOCATE FOR tip_rand == 'D' + IF !FOUND() + DO R7Log WITH 'FAIL ' + tcLabel + '-randD-taxcode: nu exista rand tip_rand=D' + SELECT (lnSel) + RETURN + ENDIF + lnTaxD = NVL(taxcode, 0) + lnFaraTax = 0 + SCAN FOR INLIST(tip_rand, 'B', 'S', 'T', 'G') AND EMPTY(NVL(taxcode, 0)) + lnFaraTax = lnFaraTax + 1 + ENDSCAN + IF EMPTY(lnTaxD) AND lnFaraTax = 0 + lcMsg = 'PASS ' + tcLabel + '-randD-taxcode: randul D are taxcode gol, iar randurile B/S/T/G il pastreaza' + ELSE + lcMsg = 'FAIL ' + tcLabel + '-randD-taxcode: taxcode pe randul D=' + TRANSFORM(lnTaxD) + ; + ' (astept 0), randuri B/S/T/G fara taxcode=' + TRANSFORM(lnFaraTax) + ' (astept 0)' + ENDIF + DO R7Log WITH lcMsg SELECT (lnSel) ENDPROC From 89695721f436694e426a4bd33631bb6f0622c1c5 Mon Sep 17 00:00:00 2001 From: Marius Mutu Date: Thu, 6 Aug 2026 10:40:06 +0300 Subject: [PATCH 7/9] diag spatiu: emailul de alerta contine si logul complet al rularii Nota in depanare-spatiu-oracle.md pentru co_2026_08_06_01_DIAG_SPATIU_PACK: pragul absolut nu se mai aplica tablespace-urilor cu maximul sub el, iar emailul include tot DIAG_SPATIU_LOG-ul rularii, plafonat la 30000 de caractere. --- docs/depanare-spatiu-oracle.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/depanare-spatiu-oracle.md b/docs/depanare-spatiu-oracle.md index bf433d7..265d741 100644 --- a/docs/depanare-spatiu-oracle.md +++ b/docs/depanare-spatiu-oracle.md @@ -22,6 +22,16 @@ cu `diag_spatiu.ps1`; **email doar la prag depasit**, tacere completa altfel. Su Sectiunile informative (recyclebin, UNDO, TEMP, istoric statistici/scheduler, top segmente, audit SYS, disc) se scriu doar cand exista deja o alerta — dau context, nu alerteaza singure. +`co_2026_08_06_01_DIAG_SPATIU_PACK` (06.08.2026) corecteaza doua lucruri vazute pe primele rulari: + +- **pragul absolut (2048 MB) nu se mai aplica tablespace-urilor al caror maxim e sub el** — orice + `UNDOTBS`/`SYSTEM` plafonat la 500–2048 MB alerta zilnic, prin definitie; acolo ramane doar garda + relativa de 15%. +- **emailul contine tot logul rularii** (alertele intai, apoi toate randurile din `DIAG_SPATIU_LOG`, + cu `!` pe cele peste prag, sortate pe sectiune si valoare descrescator), nu doar un pointer catre + tabela. Corpul e plafonat la 30000 de caractere (`EmailDiagSpatiu` lucreaza pe `VARCHAR2(32000)`), + cu marcaj `... restul in DIAG_SPATIU_LOG` la depasire. + ## Mecanisme reutilizabile (din investigatia pentru job) - **`CONTAFIN_ORACLE.SERVER_INFO`** tine parolele `PASSWORD_SYS`/`PASSWORD_CONTAFIN_ORACLE` From 8fa842888a112fd5b37a4807bb48a4ef6e27a0c3 Mon Sep 17 00:00:00 2001 From: Marius Mutu Date: Thu, 6 Aug 2026 12:44:15 +0300 Subject: [PATCH 8/9] sync SVN r17985: sursa de referinta DDL (MARIUSM_AUTO) + aliniere la origin Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_011Euno5AmwoFEvBTdWH8C8S --- clase/ointroduceri.vc2 | 5 +- clase/omodificari.vc2 | 180 +++-- clase/onom_curs.vc2 | 2 +- docs/capcana_grid_preferinte_utilizator.md | 17 + docs/depanare-spatiu-oracle.md | 10 + docs/flux-editare-vfp-text.md | 4 + docs/flux-modificare-stergere-nota-jurnal.md | 53 ++ docs/oracle_export.md | 4 + docs/publicare-scripturi-db.md | 83 +++ docs/reguli_lucru.md | 2 + docs/scripturi-migrare-db.md | 31 +- docs/scripturi-uzuale.md | 1 + utile/context_watch.ps1 | 4 +- utile/diag_spatiu.ps1 | 73 +- utile/publicare_scripturi.ps1 | 689 +++++++++++++++++++ 15 files changed, 1103 insertions(+), 55 deletions(-) create mode 100644 docs/capcana_grid_preferinte_utilizator.md create mode 100644 docs/flux-modificare-stergere-nota-jurnal.md create mode 100644 docs/publicare-scripturi-db.md create mode 100644 utile/publicare_scripturi.ps1 diff --git a/clase/ointroduceri.vc2 b/clase/ointroduceri.vc2 index 8618286..208b1c2 100644 --- a/clase/ointroduceri.vc2 +++ b/clase/ointroduceri.vc2 @@ -10199,7 +10199,8 @@ DEFINE CLASS import_nota AS frm_termin_renunt OF "_frm_child.vcx" *!* Randul D, unic pe nota (doc_key gol, nr_doc 99999999): diferenta = baza documentelor *!* care participa la pret - Suma(pret*cant) cu pretul incarcat. Cont dominant = contul de *!* clasa 3 cu baza ABS maxima; fara clasa 3 -> clasa 2, cu contul fix 6588. Diferenta se - *!* scrie doar in lei. Antetul se mosteneste de la documentul principal. + *!* scrie doar in lei, fara TVA si fara cod de taxa SAFT. Antetul se mosteneste de la + *!* documentul principal. Local lcSel, lnMinNrDoc, lnTotalLei, lnTotalVal, lnBazaParticipa Local lcContDominant, lnBazaDominantaAbs, lcScd1, lnDiferentaLei, lnDiferentaVal, lcExplicatie Local llGasitClasa3, loAntet @@ -10310,7 +10311,7 @@ DEFINE CLASS import_nota AS frm_termin_renunt OF "_frm_child.vcx" Replace partc With '', id_partc With 0, partd With '', id_partd With 0 Endif Replace scd With lcScd1, scc With lcContDominant, suma With lnDiferentaLei, suma_val With 0, ; - explicatia With lcExplicatie, id_jtva_coloana With Null, ptva With 0, cu_tva With 0, ; + explicatia With lcExplicatie, id_jtva_coloana With Null, taxcode With 0, ptva With 0, cu_tva With 0, ; in_valuta With 0, participa_valuta With .F., nume_val With '', id_valuta With 0, curs With 0 Select (lcSel) diff --git a/clase/omodificari.vc2 b/clase/omodificari.vc2 index 2b930e6..1f09ead 100644 --- a/clase/omodificari.vc2 +++ b/clase/omodificari.vc2 @@ -6720,6 +6720,8 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" *< OBJECTDATA: ObjPath="Grid1.Column43.Header1" UniqueID="" Timestamp="" /> *< OBJECTDATA: ObjPath="Grid1.Column43.Text1" UniqueID="" Timestamp="" /> *< OBJECTDATA: ObjPath="Grid1.Column43._combobox1" UniqueID="" Timestamp="" /> + *< OBJECTDATA: ObjPath="Grid1.Column44.Header1" UniqueID="" Timestamp="" /> + *< OBJECTDATA: ObjPath="Grid1.Column44.Text1" UniqueID="" Timestamp="" /> *< OBJECTDATA: ObjPath="Command1" UniqueID="" Timestamp="" /> *< OBJECTDATA: ObjPath="Gridextra1" UniqueID="" Timestamp="" /> *< OBJECTDATA: ObjPath="Command2" UniqueID="" Timestamp="" /> @@ -6762,6 +6764,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" *m: grid_focus_prima_coloana *m: resize_grid1 *m: salveazaxml + *m: sincronizeaza_nnir *m: updateexplicatiesaft *m: updateexplicatiesaftrul *m: valideaza_data @@ -6778,6 +6781,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" *p: lverificare *p: lvizualizare *p: nid_set + *p: nnnirvechi *p: noldsuma && Valoarea la intrarea in campul "Suma" *p: nprimadata *p: nsuma && Total suma inregistrari @@ -6809,6 +6813,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" lvizualizare = .F. Name = "frm_modific2024" nid_set = 0 + nnnirvechi = 0 noldsuma = 0 nprimadata = 0 nsuma = 0 @@ -7143,7 +7148,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" ADD OBJECT 'Grid1' AS grid WITH ; Anchor = 10, ; BackColor = 244,250,255, ; - ColumnCount = 43, ; + ColumnCount = 44, ; DeleteMark = .F., ; FontCharSet = 238, ; FontSize = 10, ; @@ -7185,7 +7190,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column2.Width = 71, ; Column3.Alignment = 0, ; Column3.BackColor = 244,250,255, ; - Column3.ColumnOrder = 15, ; + Column3.ColumnOrder = 16, ; Column3.ControlSource = "fdoc", ; Column3.FontCharSet = 238, ; Column3.FontName = "Arial", ; @@ -7215,7 +7220,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column5.Width = 53, ; Column6.Alignment = 0, ; Column6.BackColor = 244,250,255, ; - Column6.ColumnOrder = 7, ; + Column6.ColumnOrder = 8, ; Column6.ControlSource = "partd", ; Column6.FontCharSet = 238, ; Column6.FontName = "Arial", ; @@ -7225,7 +7230,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column6.Width = 114, ; Column7.Alignment = 1, ; Column7.BackColor = 244,250,255, ; - Column7.ColumnOrder = 10, ; + Column7.ColumnOrder = 11, ; Column7.ControlSource = "scd", ; Column7.FontCharSet = 238, ; Column7.FontName = "Arial", ; @@ -7235,7 +7240,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column7.Width = 57, ; Column8.Alignment = 1, ; Column8.BackColor = 244,250,255, ; - Column8.ColumnOrder = 12, ; + Column8.ColumnOrder = 13, ; Column8.ControlSource = "scc", ; Column8.FontCharSet = 238, ; Column8.FontName = "Arial", ; @@ -7245,7 +7250,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column8.Width = 62, ; Column9.Alignment = 1, ; Column9.BackColor = 244,250,255, ; - Column9.ColumnOrder = 14, ; + Column9.ColumnOrder = 15, ; Column9.ControlSource = "suma", ; Column9.DynamicForeColor = "IIF(SUMA>=0, RGB(0,0,0), RGB(255,0,0))", ; Column9.FontCharSet = 238, ; @@ -7258,7 +7263,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column9.Width = 93, ; Column10.Alignment = 0, ; Column10.BackColor = 244,250,255, ; - Column10.ColumnOrder = 8, ; + Column10.ColumnOrder = 9, ; Column10.ControlSource = "partc", ; Column10.FontCharSet = 238, ; Column10.FontName = "Arial", ; @@ -7268,7 +7273,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column10.Width = 100, ; Column11.Alignment = 0, ; Column11.BackColor = 244,250,255, ; - Column11.ColumnOrder = 9, ; + Column11.ColumnOrder = 10, ; Column11.ControlSource = "explicatia", ; Column11.FontCharSet = 238, ; Column11.FontName = "Arial", ; @@ -7277,7 +7282,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column11.ReadOnly = .F., ; Column11.Width = 77, ; Column12.BackColor = 244,250,255, ; - Column12.ColumnOrder = 37, ; + Column12.ColumnOrder = 38, ; Column12.ControlSource = "cod", ; Column12.FontCharSet = 238, ; Column12.FontName = "Arial", ; @@ -7286,7 +7291,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column12.ReadOnly = .T., ; Column12.Width = 52, ; Column13.BackColor = 244,250,255, ; - Column13.ColumnOrder = 16, ; + Column13.ColumnOrder = 17, ; Column13.ControlSource = "pereched", ; Column13.FontCharSet = 238, ; Column13.FontName = "Arial", ; @@ -7295,7 +7300,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column13.ReadOnly = .F., ; Column13.Width = 106, ; Column14.BackColor = 244,250,255, ; - Column14.ColumnOrder = 31, ; + Column14.ColumnOrder = 32, ; Column14.ControlSource = "explicatia4", ; Column14.FontCharSet = 238, ; Column14.FontName = "Arial", ; @@ -7304,7 +7309,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column14.ReadOnly = .F., ; Column14.Width = 120, ; Column15.BackColor = 244,250,255, ; - Column15.ColumnOrder = 24, ; + Column15.ColumnOrder = 25, ; Column15.ControlSource = "nrord", ; Column15.FontCharSet = 238, ; Column15.FontName = "Arial", ; @@ -7312,7 +7317,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column15.Name = "Column15", ; Column15.ReadOnly = .F., ; Column16.BackColor = 244,250,255, ; - Column16.ColumnOrder = 26, ; + Column16.ColumnOrder = 27, ; Column16.ControlSource = "nresp", ; Column16.FontCharSet = 238, ; Column16.FontName = "Arial", ; @@ -7322,7 +7327,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column16.Name = "Column16", ; Column16.ReadOnly = .F., ; Column17.BackColor = 244,250,255, ; - Column17.ColumnOrder = 27, ; + Column17.ColumnOrder = 28, ; Column17.ControlSource = "dst_chlt", ; Column17.FontCharSet = 238, ; Column17.FontName = "Arial", ; @@ -7332,7 +7337,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column17.Name = "Column17", ; Column17.ReadOnly = .F., ; Column18.BackColor = 244,250,255, ; - Column18.ColumnOrder = 32, ; + Column18.ColumnOrder = 33, ; Column18.ControlSource = "explicatia5", ; Column18.FontCharSet = 238, ; Column18.FontName = "Arial", ; @@ -7341,7 +7346,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column18.ReadOnly = .F., ; Column18.Width = 119, ; Column19.BackColor = 244,250,255, ; - Column19.ColumnOrder = 23, ; + Column19.ColumnOrder = 24, ; Column19.ControlSource = "datascad", ; Column19.FontCharSet = 238, ; Column19.FontName = "Arial", ; @@ -7351,7 +7356,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column19.Visible = .T., ; Column19.Width = 87, ; Column20.BackColor = 244,250,255, ; - Column20.ColumnOrder = 28, ; + Column20.ColumnOrder = 29, ; Column20.ControlSource = "suma_val", ; Column20.FontCharSet = 238, ; Column20.FontName = "Arial", ; @@ -7361,7 +7366,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column20.ReadOnly = .F., ; Column20.Width = 89, ; Column21.BackColor = 244,250,255, ; - Column21.ColumnOrder = 30, ; + Column21.ColumnOrder = 31, ; Column21.ControlSource = "curs", ; Column21.FontCharSet = 238, ; Column21.FontName = "Arial", ; @@ -7371,7 +7376,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column21.ReadOnly = .F., ; Column21.Width = 72, ; Column22.BackColor = 244,250,255, ; - Column22.ColumnOrder = 33, ; + Column22.ColumnOrder = 34, ; Column22.ControlSource = "gestin", ; Column22.FontCharSet = 238, ; Column22.FontName = "Arial", ; @@ -7380,7 +7385,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column22.ReadOnly = .F., ; Column22.Width = 116, ; Column23.BackColor = 244,250,255, ; - Column23.ColumnOrder = 11, ; + Column23.ColumnOrder = 12, ; Column23.ControlSource = "ascd", ; Column23.FontCharSet = 238, ; Column23.FontName = "Arial", ; @@ -7389,7 +7394,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column23.ReadOnly = .F., ; Column23.Width = 46, ; Column24.BackColor = 244,250,255, ; - Column24.ColumnOrder = 13, ; + Column24.ColumnOrder = 14, ; Column24.ControlSource = "ascc", ; Column24.FontCharSet = 238, ; Column24.FontName = "Arial", ; @@ -7398,7 +7403,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column24.ReadOnly = .F., ; Column24.Width = 48, ; Column25.BackColor = 244,250,255, ; - Column25.ColumnOrder = 38, ; + Column25.ColumnOrder = 39, ; Column25.ControlSource = "id_set", ; Column25.FontCharSet = 238, ; Column25.FontName = "Arial", ; @@ -7407,7 +7412,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column25.ReadOnly = .T., ; Column25.Width = 71, ; Column26.BackColor = 244,250,255, ; - Column26.ColumnOrder = 19, ; + Column26.ColumnOrder = 20, ; Column26.ControlSource = "proc_tva", ; Column26.FontCharSet = 238, ; Column26.FontName = "Arial", ; @@ -7416,7 +7421,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column26.ReadOnly = .T., ; Column26.Width = 54, ; Column27.BackColor = 244,250,255, ; - Column27.ColumnOrder = 17, ; + Column27.ColumnOrder = 18, ; Column27.ControlSource = "perechec", ; Column27.FontCharSet = 238, ; Column27.FontName = "Arial", ; @@ -7425,7 +7430,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column27.ReadOnly = .F., ; Column27.Width = 104, ; Column28.BackColor = 244,250,255, ; - Column28.ColumnOrder = 35, ; + Column28.ColumnOrder = 36, ; Column28.ControlSource = "sectie", ; Column28.FontCharSet = 238, ; Column28.FontName = "Arial", ; @@ -7434,7 +7439,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column28.ReadOnly = .F., ; Column28.Width = 88, ; Column29.BackColor = 244,250,255, ; - Column29.ColumnOrder = 29, ; + Column29.ColumnOrder = 30, ; Column29.ControlSource = "nume_val", ; Column29.FontCharSet = 238, ; Column29.FontName = "Arial", ; @@ -7443,7 +7448,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column29.ReadOnly = .F., ; Column29.Width = 79, ; Column30.BackColor = 244,250,255, ; - Column30.ColumnOrder = 39, ; + Column30.ColumnOrder = 40, ; Column30.ControlSource = "id_partd", ; Column30.FontCharSet = 238, ; Column30.FontName = "Arial", ; @@ -7452,7 +7457,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column30.ReadOnly = .T., ; Column30.Width = 55, ; Column31.BackColor = 244,250,255, ; - Column31.ColumnOrder = 40, ; + Column31.ColumnOrder = 41, ; Column31.ControlSource = "id_partc", ; Column31.FontCharSet = 238, ; Column31.FontName = "Arial", ; @@ -7461,7 +7466,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column31.ReadOnly = .T., ; Column31.Width = 55, ; Column32.BackColor = 244,250,255, ; - Column32.ColumnOrder = 34, ; + Column32.ColumnOrder = 35, ; Column32.ControlSource = "gestout", ; Column32.FontCharSet = 238, ; Column32.FontName = "Arial", ; @@ -7470,7 +7475,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column32.ReadOnly = .F., ; Column32.Width = 92, ; Column33.BackColor = 244,250,255, ; - Column33.ColumnOrder = 36, ; + Column33.ColumnOrder = 37, ; Column33.ControlSource = "sucursala", ; Column33.FontCharSet = 238, ; Column33.FontName = "Arial", ; @@ -7479,7 +7484,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column33.ReadOnly = .F., ; Column33.Width = 121, ; Column34.BackColor = 244,250,255, ; - Column34.ColumnOrder = 25, ; + Column34.ColumnOrder = 26, ; Column34.ControlSource = "contract", ; Column34.FontCharSet = 238, ; Column34.FontName = "Arial", ; @@ -7488,7 +7493,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column34.ReadOnly = .T., ; Column34.Width = 65, ; Column35.BackColor = 244,250,255, ; - Column35.ColumnOrder = 20, ; + Column35.ColumnOrder = 21, ; Column35.ControlSource = "explicatie_tva", ; Column35.FontCharSet = 238, ; Column35.FontName = "Arial", ; @@ -7497,7 +7502,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column35.ReadOnly = .F., ; Column35.Width = 136, ; Column36.BackColor = 244,250,255, ; - Column36.ColumnOrder = 18, ; + Column36.ColumnOrder = 19, ; Column36.ControlSource = "cu_tva", ; Column36.FontCharSet = 238, ; Column36.FontName = "Arial", ; @@ -7517,7 +7522,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column37.ReadOnly = .F., ; Column37.Width = 58, ; Column38.BackColor = 244,250,255, ; - Column38.ColumnOrder = 41, ; + Column38.ColumnOrder = 42, ; Column38.ControlSource = "id_fact", ; Column38.FontCharSet = 238, ; Column38.FontName = "Arial", ; @@ -7526,7 +7531,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column38.ReadOnly = .T., ; Column38.Width = 94, ; Column39.BackColor = 244,250,255, ; - Column39.ColumnOrder = 42, ; + Column39.ColumnOrder = 43, ; Column39.ControlSource = "id_factd", ; Column39.FontCharSet = 238, ; Column39.FontName = "Arial", ; @@ -7535,7 +7540,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column39.ReadOnly = .T., ; Column39.Width = 75, ; Column40.BackColor = 244,250,255, ; - Column40.ColumnOrder = 43, ; + Column40.ColumnOrder = 44, ; Column40.ControlSource = "id_factc", ; Column40.FontCharSet = 238, ; Column40.FontName = "Arial", ; @@ -7555,7 +7560,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column41.Width = 22, ; Column42.BackColor = 244,250,255, ; Column42.Bound = .F., ; - Column42.ColumnOrder = 21, ; + Column42.ColumnOrder = 22, ; Column42.ControlSource = "Iif(Seek(tAct.taxcode,'saft_taxtable','taxcode'),saft_taxtable.taxname,'')", ; Column42.CurrentControl = "_combobox1", ; Column42.FontCharSet = 238, ; @@ -7566,7 +7571,7 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column42.Width = 184, ; Column43.BackColor = 244,250,255, ; Column43.Bound = .F., ; - Column43.ColumnOrder = 22, ; + Column43.ColumnOrder = 23, ; Column43.ControlSource = "Iif(Seek(tAct.paymentcode,'saft_mecanisme_plati','mcode'),saft_mecanisme_plati.paymentname,'')", ; Column43.CurrentControl = "_combobox1", ; Column43.FontCharSet = 238, ; @@ -7574,7 +7579,17 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" Column43.Name = "Column43", ; Column43.ReadOnly = .F., ; Column43.Sparse = .F., ; - Column43.Width = 146 + Column43.Width = 146, ; + Column44.Alignment = 1, ; + Column44.BackColor = 244,250,255, ; + Column44.ColumnOrder = 7, ; + Column44.ControlSource = "nnir", ; + Column44.FontCharSet = 238, ; + Column44.FontName = "Arial", ; + Column44.FontSize = 10, ; + Column44.Name = "Column44", ; + Column44.ReadOnly = .F., ; + Column44.Width = 60 *< END OBJECT: BaseClass="grid" /> ADD OBJECT 'Grid1.cAles._CHECKBOX1' AS _checkbox WITH ; @@ -8442,6 +8457,29 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" SelectedBackColor = 49,106,197 *< END OBJECT: BaseClass="textbox" /> + ADD OBJECT 'Grid1.Column44.Header1' AS header WITH ; + Alignment = 2, ; + Caption = "Nr. NIR", ; + FontCharSet = 238, ; + FontName = "Arial", ; + FontSize = 10, ; + Name = "Header1" + *< END OBJECT: BaseClass="header" /> + + ADD OBJECT 'Grid1.Column44.Text1' AS textbox WITH ; + Alignment = 1, ; + BackColor = 244,250,255, ; + BorderStyle = 0, ; + FontCharSet = 238, ; + FontName = "Arial", ; + FontSize = 10, ; + ForeColor = 0,0,0, ; + Margin = 0, ; + Name = "Text1", ; + ReadOnly = .F., ; + SelectedBackColor = 49,106,197 + *< END OBJECT: BaseClass="textbox" /> + ADD OBJECT 'Grid1.Column5.Header5' AS header WITH ; Alignment = 2, ; Caption = "Nr.Doc.", ; @@ -13613,6 +13651,9 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" This.grid1.Anchor = 11 This.but_renunt1.Anchor = 8 + If This.nid_set = 90101 + This.grid1.Column44.ReadOnly = .T. + Endif ENDPROC PROCEDURE Load @@ -13683,6 +13724,19 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" this.gridextra1.setup() + LOCAL lcPrefFile, lcPrefFileContents, lcKey, lcPrefs + LOCAL ARRAY laNnirPrefs(1) + lcPrefFile = This.gridextra1.GetUserApplicationDataPath() + This.gridextra1.GridPreferenceFile + lcPrefFileContents = "" + IF FILE(m.lcPrefFile) + lcPrefFileContents = FILETOSTR(m.lcPrefFile) + ENDIF + lcKey = SYS(1272, This.grid1) + "(" + lcPrefs = STREXTRACT(m.lcPrefFileContents, m.lcKey, ")", 1, 1) + *!* pozitioneaza coloana la prima deschidere, cat timp preferintele salvate nu o acopera + IF EMPTY(m.lcPrefs) OR ALINES(laNnirPrefs, m.lcPrefs, 7, ",") < This.grid1.ColumnCount * 2 + This.grid1.Column44.ColumnOrder = 7 + ENDIF this.grid1.SetAll("dynamicbackcolor", "IIF(ALES, RGB(255,255,225), RGB(255,255,255))", "Column") This.CalculeazaTotal() *!* 24.05.2011 ^ @@ -13717,6 +13771,37 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" ENDPROC + PROCEDURE sincronizeaza_nnir + *!* propaga NNIR-ul modificat pe toate liniile notei si pe rulajele asociate + LPARAMETERS tnNnirVechi, tnNnirNou + LOCAL lcSelect, lnRecno + IF NVL(m.tnNnirVechi, 0) = NVL(m.tnNnirNou, 0) + RETURN + ENDIF + lcSelect = SELECT() + lnRecno = RECNO('tact') + UPDATE tact SET nnir = m.tnNnirNou WHERE NVL(nnir, 0) = m.tnNnirVechi + IF USED('trul') + UPDATE trul SET nnir = m.tnNnirNou WHERE NVL(nnir, 0) = m.tnNnirVechi + IF _TALLY > 0 + Thisform.pgfArticole.page1.grdRulaje.Refresh() + ENDIF + ENDIF + IF USED('trul_obinv') + UPDATE trul_obinv SET nnir = m.tnNnirNou WHERE NVL(nnir, 0) = m.tnNnirVechi + IF _TALLY > 0 + Thisform.pgfArticole.page2.grdRulajeObinv.Refresh() + ENDIF + ENDIF + SELECT tact + IF BETWEEN(m.lnRecno, 1, RECCOUNT('tact')) + GO m.lnRecno + ELSE + GO TOP + ENDIF + SELECT (m.lcSelect) + ENDPROC + PROCEDURE updateexplicatiesaft * Daca nu este activata SAFT 406 nu fac nimic IF !m.gl406 @@ -14201,6 +14286,10 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" .Column35.ReadOnly = .T. && cod .Column25.ReadOnly = .T. && id_set ENDWITH + *!* pastreaza blocarea NNIR pentru notele de inventar (id_set = 90101) + IF Thisform.nid_set = 90101 + This.Column44.ReadOnly = .T. + ENDIF *!* 10.08.2011 ^ *!* pot selecta inregistrari din alte sucursale pentru operatiile de copiere, adaugare care This.cAles.ReadOnly = .F. @@ -14674,6 +14763,19 @@ DEFINE CLASS frm_modific2024 AS _frmbase OF "_frm_base.vcx" thisform.cPaymentCode = ALLTRIM(tAct.paymentcode) ENDPROC + PROCEDURE Grid1.Column44.Text1.LostFocus + Local lnNnirNou, lnNnirVechi + lnNnirVechi = thisform.nnnirvechi + lnNnirNou = NVL(tAct.nnir, 0) + Thisform.sincronizeaza_nnir(m.lnNnirVechi, m.lnNnirNou) + Thisform.nnnirvechi = m.lnNnirNou + Thisform.Grid1.Refresh() + ENDPROC + + PROCEDURE Grid1.Column44.Text1.When + Thisform.nnnirvechi = NVL(tAct.nnir, 0) + ENDPROC + PROCEDURE Grid1.Column6.Text1.InteractiveChange ADDPROPERTY(this, 'lInteractiveChange') LOCAL lnIdPart diff --git a/clase/onom_curs.vc2 b/clase/onom_curs.vc2 index 88d7f88..4042942 100644 --- a/clase/onom_curs.vc2 +++ b/clase/onom_curs.vc2 @@ -25,7 +25,7 @@ DEFINE CLASS actualizare_curs_bnr AS _custom OF "_baza.vcx" * ccursor = ccrsbnr - clink = https://www.bnr.ro/nbrfxrates.xml + clink = https://curs.bnr.ro/nbrfxrates.xml ctempfile = C:\temp\temp.xml ddata = {} Height = 17 diff --git a/docs/capcana_grid_preferinte_utilizator.md b/docs/capcana_grid_preferinte_utilizator.md new file mode 100644 index 0000000..6777fcc --- /dev/null +++ b/docs/capcana_grid_preferinte_utilizator.md @@ -0,0 +1,17 @@ +# Capcana: coloana noua intr-un grid existent ajunge la coada (preferinte GridExtras) + +`GridExtras` (`COMUN\utile\GridExtras\gridextras.vc2:1051`, `restoregridpreferences` / +`savegridpreferences`) salveaza **per utilizator** latimea si ordinea coloanelor fiecarui grid, +in `%APPDATA%\...\gridprefs.tmp`, sub cheia `SYS(1272, grid)` (ex. `frm_modific2024.grid1`). +Salvarea se face live, la `columnmoved`/`columnresize`, nu la iesirea din formular. + +Consecinta: `ColumnOrder` setat in clasa e neutralizat la runtime pentru orice utilizator care a +deschis deja formularul inainte de livrare - `gridextra1.setup()` restaureaza ordinea veche, iar +coloana nou adaugata (care nu exista in preferinte) ajunge ultima. + +Solutie: repozitionare la runtime, in `Show()`, **dupa** `gridextra1.setup()`, aplicata o singura +data - cat timp preferintele salvate nu acopera toate coloanele (implementat pe +`frm_modific2024`, `COMUN\clase\omodificari.vc2`, coloana `nnir`). + +La testare: pozitia coloanei nu e o asertiune stabila intre masini. Se asertesteaza tare +`ControlSource` si `ReadOnly`; `ColumnOrder` ramane informativ. diff --git a/docs/depanare-spatiu-oracle.md b/docs/depanare-spatiu-oracle.md index bf433d7..265d741 100644 --- a/docs/depanare-spatiu-oracle.md +++ b/docs/depanare-spatiu-oracle.md @@ -22,6 +22,16 @@ cu `diag_spatiu.ps1`; **email doar la prag depasit**, tacere completa altfel. Su Sectiunile informative (recyclebin, UNDO, TEMP, istoric statistici/scheduler, top segmente, audit SYS, disc) se scriu doar cand exista deja o alerta — dau context, nu alerteaza singure. +`co_2026_08_06_01_DIAG_SPATIU_PACK` (06.08.2026) corecteaza doua lucruri vazute pe primele rulari: + +- **pragul absolut (2048 MB) nu se mai aplica tablespace-urilor al caror maxim e sub el** — orice + `UNDOTBS`/`SYSTEM` plafonat la 500–2048 MB alerta zilnic, prin definitie; acolo ramane doar garda + relativa de 15%. +- **emailul contine tot logul rularii** (alertele intai, apoi toate randurile din `DIAG_SPATIU_LOG`, + cu `!` pe cele peste prag, sortate pe sectiune si valoare descrescator), nu doar un pointer catre + tabela. Corpul e plafonat la 30000 de caractere (`EmailDiagSpatiu` lucreaza pe `VARCHAR2(32000)`), + cu marcaj `... restul in DIAG_SPATIU_LOG` la depasire. + ## Mecanisme reutilizabile (din investigatia pentru job) - **`CONTAFIN_ORACLE.SERVER_INFO`** tine parolele `PASSWORD_SYS`/`PASSWORD_CONTAFIN_ORACLE` diff --git a/docs/flux-editare-vfp-text.md b/docs/flux-editare-vfp-text.md index c8ba0cb..bb4ce27 100644 --- a/docs/flux-editare-vfp-text.md +++ b/docs/flux-editare-vfp-text.md @@ -76,6 +76,10 @@ fals `roundtrip text1 != text2`. Ruleaza atunci cu lista rebazata: - Metoda noua de clasa cere `*m: nume` in `*`; fara ea, prima salvare din IDE o arunca tacut, iar refresh-ul urmator absoarbe pierderea in cache (si in .bak-uri). Override-urile de metode de baza (Init, Show, hook-uri) nu au nevoie de `*m:`. +- FoxBin2Prg NU pastreaza pozitia in text a unei metode noi: o regenereaza la pozitia + alfabetica din `*`. O metoda scrisa in alta parte a fisierului + face fidelity-check-ul sa pice. Sursa de adevar pentru relocare e textul regenerat din + `\verify\` - muta metoda unde apare acolo si reia write-back-ul. - Salvarea din VFP IDE peste un binar scris de txt2vcx poate PIERDE si definitii `*m:` deja existente, desi corpurile PROCEDURE raman (patit pe ROAGEST: `import_adauga_factura.recalc_tva`, `import_nota.do_adauga/do_copie/do_reface`); simptom: crash la Createobject cu diff --git a/docs/flux-modificare-stergere-nota-jurnal.md b/docs/flux-modificare-stergere-nota-jurnal.md new file mode 100644 index 0000000..269791d --- /dev/null +++ b/docs/flux-modificare-stergere-nota-jurnal.md @@ -0,0 +1,53 @@ +# Flux: modificare/stergere nota in Registrul Jurnal + +Comportament CORECT (nu bug): la **modificarea** unei note din Registrul Jurnal, documentul +original nu se suprascrie. Se marcheaza `sters = 1` pe randurile vechi (`ACT`/`RUL`/`RUL_OBINV`), +apoi se scrie un document NOU cu alt `cod`, pastrand acelasi `id_fact`/`id_factd`. Ambele operatii +ruleaza intr-o tranzactie manuala unica (commit sau rollback pe amandoua). + +## Unde + +- `afisjurcom.do_modifica` - `COMUN\clase\comun.vc2:2222-2562`. Deschide tranzactia manuala + (`Thisform.do_deschide_tranzactie()`, linia 2448), apoi `oscrie_in_fisiere(2,...)` = STERGE + (linia 2451), apoi `oscrie_in_fisiere(0,...)` = SCRIE (linia 2479), apoi + `pack_contafin.finalizeaza_modificare_nota(...)` (2484-2486), apoi + `Thisform.do_inchide_tranzactie(...)` (linia 2535) - commit daca tot ce a rulat inainte a + reusit, rollback altfel. +- `do_deschide_tranzactie`/`do_inchide_tranzactie` (`_frmbase`, `COMUN\clase\_frm_base.vc2:252,279`; + varianta identica ca procedura globala in `COMUN\programe\oproceduri_rulaje.prg:287,303`): + `SQLSetProp(gnHandle,'Transactions',2)` la deschidere, `SQLCOMMIT`/`SQLROLLBACK` + + `SQLSetProp(...,'Transactions',1)` la inchidere. +- `oscrie_in_fisiere.prg` (`COMUN\programe\oscrie_in_fisiere.prg`): `tnScrie_Sterge` 0=scriere, + 2=stergere. Apeleaza server-side `pack_contafin.init_scriere_act_rul_local` + + `final_scriere_act_rul_local` -> `finalizeaza_scriere_act_rul`, care ruleaza `SCRIE_IN_ACT` + (scriere) sau `STERGE_DIN_ACT` (stergere) din `PACK_CONTAFIN.pck`. +- `PACK_CONTAFIN.STERGE_DIN_ACT` (`COMUN\docs\PACK_CONTAFIN.pck:1815-1859`): + `UPDATE ACT SET STERS = 1 ... WHERE COD = tnCod` - marcheaza vechiul document, pe `cod`-ul vechi. +- `PACK_CONTAFIN.SCRIE_IN_ACT` (`PACK_CONTAFIN.pck:630-901`): `LN_COD := pack_contafin.GET_COD()` + (linia 648) aloca un `cod` nou; `UPDATE ACT_TEMP SET COD = LN_COD, ...` (linia 897) il scrie pe + noul document - `ID_FACT` nu e atins, ramane cel din documentul original. +- `PACK_CONTAFIN.finalizeaza_modificare_nota` (`PACK_CONTAFIN.pck:8601-8651`): aloca din nou + `lnCodNou := pack_contafin.get_cod()` si actualizeaza pe codul nou `vanzari` + (`pack_facturare.actualizeaza_vanzari`) si `atasamente_vanzari`. `nom_lucrari` se actualizeaza + DOAR pentru `id_set IN (31003,31004,31005,31011)` (repune `id_fact`), nu general. `gest_inventar` + NU se leaga de `cod`-ul nou - se identifica dupa `dataora_validat + an + luna` (cazul + `id_set = 90101`, note de inventariere). + +## Atentie: nu e valabil la fel pentru stergerea simpla + +`afisjurcom.do_sterge` (`comun.vc2:2565-2724`) ofera doua optiuni (`xmenu`, linia 2625): +- **Stergere document** (`lnOptiune = 1`): DOAR `OSCRIE_IN_FISIERE(2,...)` + `finalizeaza_stergere_nota` + - marcheaza `sters = 1`, NU se creeaza document nou. +- **Anulare document** (`lnOptiune = 2`): STERGE (linia 2698) + SCRIE (linia 2709, cu sumele puse pe + 0) - creeaza si document nou, la fel ca la modificare - dar aici cele doua apeluri NU sunt + invelite intr-o tranzactie manuala comuna (nu apare `do_deschide_tranzactie`/ + `do_inchide_tranzactie` in `do_sterge`); fiecare `OSCRIE_IN_FISIERE` isi gestioneaza singur + commit-ul (vezi `tlModificare`/`llManualTransactions` in `oscrie_in_fisiere.prg:111-165`). + +## Implicatii practice + +- Codul (`cod`) unui document din Registrul Jurnal NU e stabil dupa modificare/anulare - se schimba + la fiecare editare. Cod hardcodat sau retinut dintr-o executare anterioara devine invalid. +- Regasirea documentului editat se face dupa `id_fact` (sau alt marcaj stabil), niciodata dupa `cod`. +- Randurile `sters = 1` ramase in `ACT`/`RUL`/`RUL_OBINV` dupa o modificare sunt normale (istoric), + nu date corupte sau duplicate de curatat. diff --git a/docs/oracle_export.md b/docs/oracle_export.md index df995cd..e80a00c 100644 --- a/docs/oracle_export.md +++ b/docs/oracle_export.md @@ -15,6 +15,10 @@ PL/SQL Developer. `PACK_CONTAFIN` există în schemele `MARIUSM_AUTO` (dev) și `ACN`, plus sinonim `PUBLIC`. Exportul de referință se face din `MARIUSM_AUTO`. +Înainte de a lua o sursă de acolo ca referință pentru o modificare, verifică că `MARIUSM_AUTO` are +toate scripturile aplicate — procedura e în `scripturi-migrare-db.md`, secțiunea +"Sursa de referință pentru DDL". + ## Gotcha sqlplus pe Windows/PowerShell Nu trimite SQL prin pipe din PowerShell (`'select...' | sqlplus`) — BOM-ul UTF-16 sparge parserul diff --git a/docs/publicare-scripturi-db.md b/docs/publicare-scripturi-db.md new file mode 100644 index 0000000..30bee3d --- /dev/null +++ b/docs/publicare-scripturi-db.md @@ -0,0 +1,83 @@ +# Publicarea scripturilor de baza de date catre clienti + +Scriptul: `COMUN\utile\publicare_scripturi.ps1`. Inlocuieste pasii manuali din `tasks.exe` +(`D:\PROIECTE\fox\tasks`, formularul din `clase\generare_script.vcx`), care raman varianta de rezerva. +Cum se scrie un script de migrare: `scripturi-migrare-db.md`. + +```powershell +powershell -File COMUN\utile\publicare_scripturi.ps1 -DryRun # ce ar face pe luna curenta +powershell -File COMUN\utile\publicare_scripturi.ps1 # luna curenta, tot fluxul +powershell -File COMUN\utile\publicare_scripturi.ps1 -Luna 2026-08 # alta luna +powershell -File COMUN\utile\publicare_scripturi.ps1 -Fisier a.sql,b.sql # doar scripturile date +``` + +Alte optiuni: `-DoarImport` (fara arhiva), `-DoarArhiva` (doar regenerare arhiva), `-Toate` +(republica si scripturile identice), `-FaraVerificareZip`, `-Settings`, `-ZipPath`, `-DirScripturi`, +`-Alias`, `-User`, `-Password`, `-SqlPlus`. + +## Configurarea + +Se citeste din `settings.ini`-ul lui `tasks.exe` (`D:\PROIECTE\fox\tasks\settings.ini`, schimbabil +cu `-Settings`), aceleasi chei pe care le foloseste formularul; orice parametru dat explicit bate +ini-ul. + +| din ini | ce devine | +|---|---| +| `[connection] host_database` | nume de **DSN ODBC** — aliasul TNS e cheia `ServerName` a DSN-ului din registri (`CENTRAL` → `ROA_CENTRAL`) | +| `[connection] username_database` / `password_database` | utilizatorul Oracle | +| `[folder] script_folder` | directorul scripturilor, cu `\SCRIPTURI\` → `\SCRIPTURI_CLAR\` (+ `\\\`) | +| `[folder] roa_output` | + `_UPDATE\` = directorul arhivelor | +| `[folder] sqlplus_exe` | clientul Oracle (si `TNS_ADMIN` = directorul lui) | +| `[script]` | prefixele valide (`CO_`, `FF_`, `RF_`, `SYS_`, `JCS_`) — prefix necunoscut = avertisment | + +`sqlplus_exe` arata spre `instantclient_19_18`; `instantclient_11_2_0_2` (valoarea de dinainte) are +un `tnsnames.ora` vechi, cu `ROA_CENTRAL` pe `10.0.20.122` in loc de `.121`, deci conexiunea expira. +`settings.ini` e local (ignorat de SVN). + +## Cei doi pasi + +**1. Import** — fiecare `.sql` intra ca un rand in `CONTAFIN_ORACLE.UPD_DATABASE` de pe `ROA_CENTRAL`, +cu `SCRIPT_ORDER = 0` (randul contine scriptul intreg, nu instructiuni parsate), `SCRIPT_NAME` cu +MAJUSCULE si `.SQL`, `SCRIPT_TYPE` = ce urmeaza dupa `NN_`, `SCRIPT_CONTENT` = fisierul in clar. +`ID` vine din trigger, `DATAORA` din `SYSDATE` implicit, `SCRIPT_APPVER`/`SCRIPT_EXTRA_FILE` raman +goale. Daca `SCRIPT_NAME` exista deja, se face **update pe acelasi `id`** — asa se republica un +script corectat, pentru ca clientul face `MERGE ... on (a.id = b.id)`. + +**2. Arhiva lunara** — in `Y:\ROAUPDATE\_UPDATE\` se scriu `database_YYYYMMDD_YYYYMMDD.zip` **si** +`databasen_...zip` (identice azi: `database_` era varianta criptata cu `wrap.exe`, nu se mai +foloseste). Fiecare contine XML-ul cu acelasi nume ca zip-ul (format `VFPData`/`crsxml`, +Windows-1252, CRLF, tab-uri, `script_content` gol) plus cate un `.sql` per script. +Indexii `roa_database.xml` / `roa_databasen.xml` listeaza arhivele existente si se schimba doar +cand apare o luna noua. + +Continutul din arhiva se citeste **inapoi din baza**, nu de pe disc — deci arhiva contine exact ce +va primi clientul. + +## Verificari facute automat + +- fisier cu sfarsituri de linie LF: **refuzat** (parsarea pe client se face pe `CHR(13)+CHR(10)`); +- dupa import, continutul e citit inapoi din CLOB si comparat octet cu octet cu fisierul; la + diferenta se opreste inainte de arhiva; +- scripturile deja publicate cu acelasi continut se sar (`-Toate` forteaza republicarea); +- arhiva generata se citeste inapoi cu cititorul clientului (`pack_utils.decodebase64` + + `zip_util_pkg.get_file`) si se compara lungime + MD5 per fisier — o arhiva facuta din PowerShell + este citita corect de `zip_util_pkg`; +- fisierele inlocuite din `_UPDATE` se copiaza intai in `_UPDATE\_backup\\`. + +`Y:\ROAUPDATE\_UPDATE\` e **live**: clientii descarca de acolo imediat ce fisierul e pus. + +## Ce face clientul + +`PACK_UPDATE.UpdateScripts` (`COMUN\docs\PACK_UPDATE.pck`) descarca indexul, apoi arhivele care +acopera perioada neaplicata, extrage XML-ul cu `zip_util_pkg.get_file`, face `MERGE` in +`UPD_DATABASE` pe `id`, apoi pentru fiecare rand cu `script_extra_file` pune continutul fisierului +in `script_content` — pentru `.sql` text clar, fara base64 (base64 e doar pentru instructiunile +parsate, alta ramura). Actualizare blocata: `depanare-pack-update.md`. + +## Detalii de implementare + +Continutul circula **hexazecimal** in ambele sensuri (`utl_i18n.raw_to_char` / `string_to_raw` cu +`WE8MSWIN1252`), pe bucati de 900 de octeti, in blocuri PL/SQL de cate 15 bucati. Motivul: baza e +`AL32UTF8`, fisierele sunt CP1252, iar comanda trimisa lui `sqlplus` ramane ASCII pur — nu depinde +de `NLS_LANG` si nu poate strica octetii >= 0x80. Consecinta utila: comparatia dintre fisier si +CLOB e exacta, nu pe lungime. diff --git a/docs/reguli_lucru.md b/docs/reguli_lucru.md index 92acf82..317c148 100644 --- a/docs/reguli_lucru.md +++ b/docs/reguli_lucru.md @@ -60,6 +60,8 @@ `0xBA` strica `a`/`t` cu caciula (`0xE3`, `0xFE`) din alte fisiere; - adaugi/rearanjezi controale pe formulare sau coloane in grid -> `conventie_ux_formulare.md`; - grid needitabil pe formular cu 2+ grid-uri -> `capcana_grid_controlsource.md`; + - adaugi o coloana intr-un grid deja livrat (ordinea din clasa e neutralizata de + preferintele salvate per utilizator) -> `capcana_grid_preferinte_utilizator.md`; - `GO` pe un `Recno()` capturat/primit ca parametru -> `conventie_go_recno.md`; - `ALTER TABLE` pe cursorul intors de `goExecutor.oExecute()` -> `conventie_goexecutor_alter_table.md`; - testare UI / prin MCP -> `testare-ui-vfp.md`, `testare-vfp-mcp.md`; diff --git a/docs/scripturi-migrare-db.md b/docs/scripturi-migrare-db.md index d015b12..459a9e6 100644 --- a/docs/scripturi-migrare-db.md +++ b/docs/scripturi-migrare-db.md @@ -3,7 +3,8 @@ Scripturile de migrare a schemei (si modelele pentru scripturi noi) sunt in `DATABASE\SCRIPTURI_CLAR` de sub radacina suitei ROA (`gcDirMare`/`dirgen` — ex. `D:\ROA\DATABASE\SCRIPTURI_CLAR`). Sursa SVN: `http://svnroa:3001/svn/ROA/DATABASE/Branches/RB-1.00`. -Pentru un script nou, urmeaza formatul/conventiile celor recente de acolo. +Pentru un script nou, urmeaza formatul/conventiile celor recente de acolo. Publicarea catre clienti +(import in `UPD_DATABASE` + arhiva lunara): `publicare-scripturi-db.md`. Reguli confirmate de Marius (24.07.2026): @@ -16,6 +17,34 @@ Reguli confirmate de Marius (24.07.2026): - Aplicarea prin ODBC/`goExecutor`: sintaxa SQL*Plus `exec pachet.procedura(...)` nu functioneaza — foloseste `begin pachet.procedura(...); end;`. +## Sursa de referinta pentru DDL: MARIUSM_AUTO, nu productia + +Regula lui Marius (06.08.2026): **orice modificare de tabela, view, procedura, functie sau pachet se +scrie plecand de la sursa din schema de dezvoltare `MARIUSM_AUTO` (`ROA_CENTRAL`), niciodata de la +sursa citita dintr-o schema de client** (`VENDING`, `ACN`, ...). Schemele de client raman in urma: +pot avea scripturi neaplicate sau variante livrate separat (ex. pachetele `_10G_ROMCONSTRUCT`), iar o +modificare scrisa peste o sursa veche sterge corectii deja livrate. + +Schemele de client raman utile doar pentru **masuratori pe date reale**, in citire. + +### Verifica intai ca MARIUSM_AUTO e la zi + +"Ultima versiune din dev" e ultima versiune doar daca toate scripturile din `SCRIPTURI_CLAR` chiar au +fost aplicate acolo. `pack_migrare.UpdateVersiune` inregistreaza fiecare script aplicat in tabela +`VERSIUNE` (`script_final` = numele fisierului, cu tot cu `.sql`), deci diferenta se vede direct: + +```sql +select script_final, data_final from versiune order by data_script desc, seq_script desc; +``` + +```powershell +Get-ChildItem D:\ROA\DATABASE\SCRIPTURI_CLAR -Recurse -Filter *.sql | + Select-Object -ExpandProperty Name | Sort-Object +``` + +Daca lipsesc scripturi din `VERSIUNE`, **nu porni modificarea** pe sursa de acolo — semnaleaza-i lui +Marius ce nu e aplicat. Exportul sursei de referinta: `oracle_export.md`. + ## Continutul unui script - **Minimul necesar si intotdeauna SCOPED**: `update`/`insert` doar pe randurile cazului tratat diff --git a/docs/scripturi-uzuale.md b/docs/scripturi-uzuale.md index 4474e06..d079951 100644 --- a/docs/scripturi-uzuale.md +++ b/docs/scripturi-uzuale.md @@ -12,6 +12,7 @@ a doua oara — daca a doua oara e nevoie de acelasi lucru, scrie scriptul. | editare `.vcx`/`.scx` | `D:\ROA\UTIL\foxbin2prg\` : `git_sync.ps1`, `vfp_symbols.ps1`, `txt2vcx.ps1` | reimprospatare text in-arbore, index de simboluri, write-back cu fidelity-check | | actualizare de baza de date blocata | `COMUN\utile\diag_actualizare.ps1 -Alias ` | `UPD_ISTORIC` → `UPD_LOG` → `script_master.log` → versiuni per schema → job → tablespace → verdict. Vezi `depanare-pack-update.md` | | spatiu plin la un client | `COMUN\utile\diag_spatiu.ps1 -Alias [-Disc] [-SysPassword]` | tablespace, audit, ADR, FRA, disc server, top segmente. Vezi `depanare-spatiu-oracle.md` | +| publicare scripturi de baza de date | `COMUN\utile\publicare_scripturi.ps1 [-Luna YYYY-MM] [-Fisier ...] [-DryRun]` | import in `UPD_DATABASE` → arhiva lunara `database[n]_*.zip` + indexi in `_UPDATE`, cu verificare octet cu octet. Vezi `publicare-scripturi-db.md` | | teste UI VFP | harness din `COMUN\utile\Teste` | vezi `testare-ui-vfp.md` | Documentatia **nu** se genereaza automat si nu exista niciun hook care s-o scrie — `livrare.ps1` diff --git a/utile/context_watch.ps1 b/utile/context_watch.ps1 index cadd815..78e1745 100644 --- a/utile/context_watch.ps1 +++ b/utile/context_watch.ps1 @@ -19,7 +19,9 @@ if (-not $TranscriptPath) { try { $TranscriptPath = (ConvertFrom-Json $stdin).transcript_path } catch { } } } -if (-not $TranscriptPath -or -not (Test-Path $TranscriptPath)) { +# la primul mesaj al unei sesiuni noi transcriptul inca nu e creat: fara fallback, ca sa nu raporteze alta sesiune +if ($TranscriptPath -and -not (Test-Path $TranscriptPath)) { exit 0 } +if (-not $TranscriptPath) { $dir = Join-Path $env:USERPROFILE ".claude\projects\$((Get-Location).Path -replace '[:\\/]','-')" if (Test-Path $dir) { $f = Get-ChildItem "$dir\*.jsonl" | Sort-Object LastWriteTime -Descending | Select-Object -First 1 diff --git a/utile/diag_spatiu.ps1 b/utile/diag_spatiu.ps1 index ea4a5ea..1214391 100644 --- a/utile/diag_spatiu.ps1 +++ b/utile/diag_spatiu.ps1 @@ -1,6 +1,7 @@ # diag_spatiu.ps1 - diagnostic de spatiu pe serverul Oracle al unui client ROA. # Raspunde "cat spatiu mai am si ce ocupa unde": tablespace/datafile (cat mai poate creste), -# audit Oracle (tabele + fisiere .aud), alert log/trace/ADR, archivelog/FRA, spatiu pe discurile +# plafonul de date al editiei XE, audit Oracle (tabele + fisiere .aud), alert log/trace/ADR, +# archivelog/FRA, spatiu pe discurile # fizice ale serverului, si alti mancatori de spatiu (recyclebin, UNDO, istoric statistici/ # scheduler, TEMP, top segmente generic - fara nume de tabele ROA hardcodate). # STRICT READ-ONLY pe baza: doar select/agregate si UTL_FILE in mod 'R'. Actiunile de curatare @@ -29,6 +30,12 @@ param( $ErrorActionPreference = 'Stop' +# praguri de alerta, aceleasi ca in PACK_DIAG_SPATIU (jobul zilnic de la clienti): tablespace-ul +# intra in atentie sub pragul absolut SAU sub procentul din maximul lui, ca sa existe timp de reactie +$PragTsMb = 2048 +$PragTsPct = 15 +$PragFraPct = 75 + if (-not (Test-Path $SqlPlus)) { Write-Output ('EROARE: nu gasesc sqlplus la ' + $SqlPlus) exit 1 @@ -173,16 +180,17 @@ select a.tablespace_name, round(a.max_bytes/1048576) mb_max, round(nvl(tf.free_bytes,0)/1048576) mb_liber, round((nvl(tf.free_bytes,0) + a.creste_bytes)/1048576) mb_poate_creste, - case when a.max_bytes > 0 and (nvl(tf.free_bytes,0) + a.creste_bytes) < 1048576 - then 'ATENTIE: MAXBYTES ATINS' end stare + case when a.max_bytes > 0 and (nvl(tf.free_bytes,0) + a.creste_bytes) < + greatest($PragTsMb * 1048576, a.max_bytes * $PragTsPct / 100) + then 'ATENTIE: APROAPE FARA LOC' end stare from agg a, ts_free tf where a.tablespace_name = tf.tablespace_name(+) order by a.tablespace_name; exit "@ -Show-Section -Title '2. SPATIU TABLESPACE (detaliu pe datafile + cat mai poate creste)' -Text ('mb_poate_creste = liber curent + (maxbytes - alocat) - spatiul real disponibil inainte de ORA-01653, nu doar liberul de acum.' + "`n`n" + $r2.Text) +Show-Section -Title '2. SPATIU TABLESPACE (detaliu pe datafile + cat mai poate creste)' -Text ('mb_poate_creste = liber curent + (maxbytes - alocat) - spatiul real disponibil inainte de ORA-01653, nu doar liberul de acum.' + "`n" + ('Prag de atentie: sub ' + $PragTsMb + ' MB ramasi sau sub ' + $PragTsPct + '% din maximul tablespace-ului.') + "`n`n" + $r2.Text) -# tablespace-urile cu maxbytes efectiv atins - top segmente acolo +# tablespace-urile care au trecut pragul de atentie - top segmente acolo $r2m = Invoke-Sql -Sql @" set pagesize 0 linesize 100 trimspool on feedback off heading off with ts_free as ( @@ -196,7 +204,8 @@ select 'TS_ATENTIE|' || a.tablespace_name from agg a, ts_free tf where a.tablespace_name = tf.tablespace_name(+) and a.max_bytes > 0 - and (nvl(tf.free_bytes,0) + a.creste_bytes) < 1048576; + and (nvl(tf.free_bytes,0) + a.creste_bytes) < + greatest($PragTsMb * 1048576, a.max_bytes * $PragTsPct / 100); exit "@ $tsProblema = @($r2m.Text -split "`r?`n" | ForEach-Object { $_.Trim() } | Where-Object { $_.StartsWith('TS_ATENTIE|') } | ForEach-Object { $_.Substring(11) } | Where-Object { $_ -match '^[A-Z0-9_]+$' }) @@ -263,6 +272,45 @@ exit Write-Output $rPdb.Text } +# ---------- 2c. Plafonul de date al editiei XE ---------- +# limita editiei, independenta de maxbytes-ul tablespace-urilor: extinderea poate fi refuzata cu +# ORA-12952 desi tablespace-ul mai are loc +Write-Output '' +Write-Output '===== 2c. PLAFON DE DATE XE =====' +$xeCritic = $false +if ($r1.Text -notmatch 'Express Edition') { + Write-Output 'Sarit - editia nu e Express Edition, nu exista plafon de date pe editie.' +} else { + $xePlafonGb = if ($verMajor -le 10) { 4 } elseif ($verMajor -eq 11) { 11 } else { 12 } + $rXe = Invoke-Sql -Sql @" +set pagesize 0 linesize 200 trimspool on feedback off heading off +select 'XEFOLOSIT|' || round(nvl(sum(f.bytes),0)/1048576, 2) + from dba_data_files f + where f.tablespace_name not in ('SYSTEM','SYSAUX') + and f.tablespace_name not in (select t.tablespace_name from dba_tablespaces t + where t.contents in ('UNDO','TEMPORARY')); +exit +"@ + $xeFolositMb = $null + foreach ($line in ($rXe.Text -split "`r?`n")) { + if ($line.Trim() -match '^XEFOLOSIT\|([\d.]+)$') { $xeFolositMb = [double]$Matches[1] } + } + if ($null -eq $xeFolositMb) { + Write-Output 'Nu am putut masura datele de utilizator (dba_data_files) - vezi erorile de mai sus.' + Write-Output $rXe.Text + } else { + $xePlafonMb = $xePlafonGb * 1024 + $xeLiberMb = $xePlafonMb - $xeFolositMb + $xePragMb = [math]::Max($PragTsMb, $xePlafonMb * $PragTsPct / 100) + $xeCritic = ($xeLiberMb -lt $xePragMb) + Write-Output ('Editie Express, plafon de date ' + $xePlafonGb + ' GB (masurat pe dba_data_files, fara SYSTEM/SYSAUX/UNDO/TEMP).') + Write-Output (' folosit = ' + [math]::Round($xeFolositMb/1024, 2) + ' GB liber pana la plafon = ' + [math]::Round($xeLiberMb, 0) + ' MB') + if ($xeCritic) { + Write-Output ('[ATENTIE] sub pragul de ' + [math]::Round($xePragMb, 0) + ' MB. Cand plafonul e atins, baza refuza extinderea cu ORA-12952 si nu se mai poate face nimic din baza - elibereaza date sau treci pe alta editie.') + } + } +} + # ---------- 3. Audit Oracle ---------- $r3 = Invoke-Sql -Sql @" set pagesize 0 linesize 250 trimspool on feedback off heading off @@ -369,12 +417,12 @@ exit $fraCritic = $false foreach ($line in ($r5c.Text -split "`r?`n")) { if ($line.Trim() -match '^FRAPCT\|([\d.]+)$') { - if ([double]$Matches[1] -ge 80) { $fraCritic = $true } + if ([double]$Matches[1] -ge $PragFraPct) { $fraCritic = $true } } } if ($fraCritic) { Write-Output '' - Write-Output '[ATENTIE] FRA peste 80% pe cel putin un tip de fisier. Daca log_mode=ARCHIVELOG si FRA se umple complet, baza se blocheaza pe scriere (ORA-19809/ORA-00257).' + Write-Output ('[ATENTIE] FRA peste ' + $PragFraPct + '% din spatiul alocat. Daca log_mode=ARCHIVELOG si FRA se umple complet, baza se blocheaza pe scriere (ORA-19809/ORA-00257).') Write-Output 'Ce se poate face fara backup: marire db_recovery_file_dest_size (daca discul are loc) - imediat, fara pierdere.' Write-Output 'Ce NU se face fara backup real: sterge archivelog-uri neaplicate-la-backup ar rupe recuperarea (RMAN DELETE ARCHIVELOG ... BACKED UP 1 TIMES TO DISK, doar daca exista backup real configurat).' } @@ -602,12 +650,15 @@ Show-Section -Title ('7e. TOP ' + $Top + ' SEGMENTE DIN TOATA BAZA (generic, far Write-Output '' Write-Output '===== 8. VERDICT =====' if ($tsProblema.Count -gt 0) { - Write-Output ('Tablespace-uri la limita (maxbytes atins): ' + ($tsProblema -join ', ') + '. Vezi sectiunea 2 pentru top segmente acolo - candidatii de curatare/crestere maxbytes sunt acolo.') + Write-Output ('Tablespace-uri aproape fara loc: ' + ($tsProblema -join ', ') + '. Vezi sectiunea 2 pentru top segmente acolo - candidatii de curatare/crestere maxbytes sunt acolo.') } else { - Write-Output 'Niciun tablespace la limita de maxbytes gasit (sectiunea 2).' + Write-Output ('Niciun tablespace sub pragul de atentie (' + $PragTsMb + ' MB sau ' + $PragTsPct + '% din maxim, sectiunea 2).') +} +if ($xeCritic) { + Write-Output ('Plafonul de date al editiei XE e aproape atins (sectiunea 2c) - la ORA-12952 nu se mai poate face nimic din baza. Prioritate maxima: eliberare de date sau trecere pe alta editie.') } if ($fraCritic) { - Write-Output 'FRA peste 80% pe cel putin un tip de fisier (sectiunea 5b) - risc de blocaj pe scriere daca log_mode=ARCHIVELOG. Prioritate: verifica arhivarea/backup-ul inainte sa se umple complet.' + Write-Output ('FRA peste ' + $PragFraPct + '% din spatiul alocat (sectiunea 5b) - risc de blocaj pe scriere daca log_mode=ARCHIVELOG. Prioritate: verifica arhivarea/backup-ul inainte sa se umple complet.') } Write-Output 'Ordine tipica de curatare (cea mai sigura primul): fisiere .aud vechi din audit_file_dest (sters direct pe disc, fara privilegii DB) -> adrci purge pe alert/trace/incident vechi -> istoric statistici/scheduler (DBMS_STATS.PURGE_STATS / DBMS_SCHEDULER.PURGE_LOG) -> recyclebin -> resize UNDO dupa ce scade activitatea -> curatare SYS.aud$/fga_log$ sau alte tabele interne (cere SYSDBA) -> ca ultima solutie, recreare PDB (export/import).' if (-not $ConnStrSys) { diff --git a/utile/publicare_scripturi.ps1 b/utile/publicare_scripturi.ps1 new file mode 100644 index 0000000..11c0ac5 --- /dev/null +++ b/utile/publicare_scripturi.ps1 @@ -0,0 +1,689 @@ +# publicare_scripturi.ps1 - publica scripturile de migrare .sql catre clientii ROA. +# Doi pasi, exact ca formularul din tasks.exe (clase\generare_script.vcx): +# 1. import - scrie fiecare .sql in CONTAFIN_ORACLE.UPD_DATABASE de pe ROA_CENTRAL +# (script_order = 0, un rand = un script intreg); daca script_name exista deja, +# face update pe id, ca sa se republice continutul corectat; +# 2. arhiva - regenereaza database_.zip / databasen_.zip si indexii +# roa_database.xml / roa_databasen.xml in Y:\ROAUPDATE\_UPDATE. +# Continutul din arhiva se citeste INAPOI din baza, nu de pe disc, si se compara octet cu octet cu +# fisierul sursa - deci arhiva contine exact ce vor primi clientii. Optional (implicit pornit), +# arhiva generata se citeste inapoi prin zip_util_pkg, cititorul Oracle al clientului. +# +# Configurarea se citeste din settings.ini-ul lui tasks.exe (aceleasi chei pe care le foloseste +# formularul): [connection] host_database/username_database/password_database, [folder] +# script_folder (\SCRIPTURI\ -> \SCRIPTURI_CLAR\), roa_output (+ _UPDATE) si sqlplus_exe, [script] +# prefixele valide. Aliasul TNS se ia din DSN-ul ODBC numit in host_database (cheia ServerName din +# registri). Orice parametru dat explicit are prioritate fata de ini. +# +# Utilizare: +# powershell -File COMUN\utile\publicare_scripturi.ps1 -DryRun +# powershell -File COMUN\utile\publicare_scripturi.ps1 -Fisier a.sql,b.sql +# powershell -File COMUN\utile\publicare_scripturi.ps1 -Luna 2026-08 +# powershell -File COMUN\utile\publicare_scripturi.ps1 -Luna 2026-08 -DoarArhiva +param( + [string[]]$Fisier, + [string]$Luna, + [string]$Settings = 'D:\PROIECTE\fox\tasks\settings.ini', + [string]$DirScripturi, + [string]$ZipPath, + [string]$BackupPath, + [switch]$DoarImport, + [switch]$DoarArhiva, + [switch]$Toate, + [switch]$FaraVerificareZip, + [switch]$DryRun, + [switch]$PastreazaTemp, + [string]$Alias, + [string]$User, + [string]$Password, + [string]$SqlPlus +) + +$ErrorActionPreference = 'Stop' +Add-Type -AssemblyName System.IO.Compression.FileSystem + +# octeti per bucata trimisa/citita ca hexazecimal (2 caractere per octet, sub limita de linie sqlplus) +$BUCATA = 900 +# bucati per bloc PL/SQL (sursa unui bloc trebuie sa ramana sub ~64k) +$BUCATI_PER_BLOC = 15 + +function Scrie-Titlu { + param([string]$Text) + Write-Output '' + Write-Output ('===== ' + $Text + ' =====') +} + +function Opreste { + param([string]$Mesaj) + Write-Output '' + Write-Output ('EROARE: ' + $Mesaj) + Curata-Temp + exit 1 +} + +function Curata-Temp { + if ($PastreazaTemp) { + if (Test-Path $TempDir) { Write-Output ('Temp pastrat: ' + $TempDir) } + return + } + if (Test-Path $TempDir) { Remove-Item -LiteralPath $TempDir -Recurse -Force -ErrorAction SilentlyContinue } +} + +function Invoke-SqlPlus { + # ruleaza un script sqlplus; intoarce textul si codul de iesire (whenever sqlerror => cod <> 0) + param([string]$Sql, [string]$Nume = 'q') + $f = Join-Path $TempDir ($Nume + '_' + [guid]::NewGuid().ToString('N') + '.sql') + $antet = "whenever sqlerror exit failure rollback`r`nwhenever oserror exit failure`r`nset define off`r`nset feedback off`r`n" + [System.IO.File]::WriteAllText($f, ($antet + $Sql), [System.Text.Encoding]::ASCII) + try { + $out = & $SqlPlus -L -S $ConnStr ('@' + $f) 2>&1 + $cod = $LASTEXITCODE + } catch { + $out = 'EROARE lansare sqlplus: ' + $_.Exception.Message + $cod = -1 + } + if (-not $PastreazaTemp) { Remove-Item -LiteralPath $f -Force -ErrorAction SilentlyContinue } + $text = ($out | ForEach-Object { [string]$_ }) -join "`n" + [PSCustomObject]@{ Text = $text; Cod = $cod } +} + +function Test-Rezultat { + param($R, [string]$Context) + if ($R.Cod -ne 0 -or $R.Text -match 'ORA-\d{5}|SP2-\d{4}|PLS-\d{5}') { + Write-Output $R.Text + Opreste ($Context + ' - sqlplus a raportat eroare (cod ' + $R.Cod + ').') + } +} + +function ConvertTo-Hex { + param([byte[]]$Octeti) + if ($Octeti.Length -eq 0) { return '' } + [System.BitConverter]::ToString($Octeti).Replace('-', '') +} + +function ConvertFrom-Hex { + param([string]$Hex) + $n = [int]($Hex.Length / 2) + $b = New-Object byte[] $n + for ($i = 0; $i -lt $n; $i++) { $b[$i] = [Convert]::ToByte($Hex.Substring($i * 2, 2), 16) } + ,$b +} + +function Citeste-Ini { + param([string]$Cale) + $ini = @{} + $sectiune = '' + foreach ($linie in [System.IO.File]::ReadAllLines($Cale, [System.Text.Encoding]::GetEncoding(1252))) { + $linie = $linie.Trim() + if (-not $linie -or $linie.StartsWith(';') -or $linie.StartsWith('#')) { continue } + if ($linie -match '^\[(.+)\]$') { + $sectiune = $Matches[1].ToLower() + if (-not $ini.ContainsKey($sectiune)) { $ini[$sectiune] = @{} } + continue + } + $p = $linie.IndexOf('=') + if ($p -lt 1 -or -not $sectiune) { continue } + $ini[$sectiune][$linie.Substring(0, $p).Trim().ToLower()] = $linie.Substring($p + 1).Trim() + } + $ini +} + +function Ia-Cheie { + param($Ini, [string]$Sectiune, [string]$Cheie) + $s = $Sectiune.ToLower() + $c = $Cheie.ToLower() + if ($Ini.ContainsKey($s) -and $Ini[$s].ContainsKey($c)) { return $Ini[$s][$c] } + '' +} + +function Rezolva-Alias { + # numele din host_database e un DSN ODBC; aliasul TNS e in cheia ServerName a DSN-ului + param([string]$Dsn) + foreach ($radacina in @('HKCU:\SOFTWARE\ODBC\ODBC.INI', 'HKLM:\SOFTWARE\ODBC\ODBC.INI', 'HKLM:\SOFTWARE\WOW6432Node\ODBC\ODBC.INI')) { + $cale = Join-Path $radacina $Dsn + if (Test-Path $cale) { + $v = (Get-ItemProperty -Path $cale -Name 'ServerName' -ErrorAction SilentlyContinue).ServerName + if ($v) { return $v } + } + } + # nu e DSN ODBC - il tratam ca alias TNS direct + $Dsn +} + +function Compara-Octeti { + param([byte[]]$A, [byte[]]$B) + if ($null -eq $A -or $null -eq $B) { return $false } + if ($A.Length -ne $B.Length) { return $false } + for ($i = 0; $i -lt $A.Length; $i++) { if ($A[$i] -ne $B[$i]) { return $false } } + $true +} + +function Split-Hex { + # taie un sir hexazecimal in bucati de $BUCATA octeti + param([string]$Hex) + $l = New-Object System.Collections.ArrayList + $pas = $BUCATA * 2 + for ($i = 0; $i -lt $Hex.Length; $i += $pas) { + [void]$l.Add($Hex.Substring($i, [Math]::Min($pas, $Hex.Length - $i))) + } + ,$l +} + +function Parseaza-NumeScript { + # _YYYY_MM_DD_NN_.sql -> obiect cu elementele componente (ca parsescriptname din VFP) + param([string]$Cale) + $stem = [System.IO.Path]::GetFileNameWithoutExtension($Cale) + if ($stem -notmatch '^([A-Za-z]+)_(\d{4})_(\d{2})_(\d{2})_(\d+)(?:_(.+))?$') { return $null } + $an = [int]$Matches[2]; $luna = [int]$Matches[3]; $zi = [int]$Matches[4] + try { $data = Get-Date -Year $an -Month $luna -Day $zi -Hour 0 -Minute 0 -Second 0 } catch { return $null } + $tip = '' + if ($Matches[6]) { $tip = $Matches[6].ToUpper() } + [PSCustomObject]@{ + Cale = $Cale + NumeScript = ($stem.ToUpper() + '.SQL') + Stem = $stem.ToUpper() + Prefix = $Matches[1].ToUpper() + Data = $data + DataYmd = $data.ToString('yyyyMMdd') + Secventa = [int]$Matches[5] + Tip = $tip + } +} + +function Citeste-MetadateLuna { + # metadatele scripturilor intregi (script_order = 0) din luna, in ordinea din arhiva + param([datetime]$Inceput, [datetime]$Sfarsit) + $sql = @" +set pagesize 0 linesize 600 trimspool on heading off +select 'M|' || id || '|' || script_name || '|' || script_type || '|' || to_char(script_date,'yyyymmdd') || + '|' || to_char(script_seq) || '|' || to_char(script_order) || '|' || nvl(script_appver,'') || + '|' || nvl(length(script_content),0) + from upd_database + where script_order = 0 + and script_date between to_date('$($Inceput.ToString('yyyyMMdd'))','yyyymmdd') and to_date('$($Sfarsit.ToString('yyyyMMdd'))','yyyymmdd') + order by script_name, id; +exit +"@ + $r = Invoke-SqlPlus -Sql $sql -Nume 'meta' + Test-Rezultat -R $r -Context 'citire metadate din UPD_DATABASE' + $l = New-Object System.Collections.ArrayList + foreach ($linie in ($r.Text -split "`r?`n")) { + $linie = $linie.Trim() + if (-not $linie.StartsWith('M|')) { continue } + $c = $linie.Substring(2) -split '\|' + if ($c.Count -lt 8) { continue } + [void]$l.Add([PSCustomObject]@{ + Id = [int]$c[0] + NumeScript = $c[1].Trim() + Stem = [System.IO.Path]::GetFileNameWithoutExtension($c[1].Trim()) + Tip = $c[2].Trim() + DataYmd = $c[3].Trim() + Secventa = [int]$c[4] + Ordine = [int]$c[5] + AppVer = $c[6].Trim() + Lungime = [int]$c[7] + }) + } + ,$l +} + +function Citeste-Continut { + # citeste inapoi CLOB-urile ca octeti CP1252 (hexazecimal), pentru toate id-urile date + param($Metadate) + $rezultat = @{} + if ($Metadate.Count -eq 0) { return $rezultat } + $maxBucati = 1 + foreach ($m in $Metadate) { + $rezultat[$m.Id] = New-Object System.Text.StringBuilder + $b = [Math]::Ceiling($m.Lungime / $BUCATA) + if ($b -gt $maxBucati) { $maxBucati = [int]$b } + } + $toateId = @($Metadate | ForEach-Object { $_.Id }) + $grupuri = New-Object System.Collections.ArrayList + for ($i = 0; $i -lt $toateId.Count; $i += 500) { + [void]$grupuri.Add('c.id in (' + (($toateId[$i..([Math]::Min($i + 499, $toateId.Count - 1))]) -join ',') + ')') + } + $ids = $grupuri -join ' or ' + $sql = @" +set pagesize 0 linesize 32767 long 2000000000 longchunksize 32767 trimspool on heading off arraysize 100 +select '#' || c.id || '#' || lpad(to_char(l.n),7,'0') || '#' || + rawtohex(utl_i18n.string_to_raw(dbms_lob.substr(c.script_content, $BUCATA, (l.n-1)*$BUCATA+1), 'WE8MSWIN1252')) + from upd_database c, + (select level n from dual connect by level <= $maxBucati) l + where ($ids) + and (l.n-1)*$BUCATA < nvl(length(c.script_content),0) + order by c.id, l.n; +exit +"@ + $r = Invoke-SqlPlus -Sql $sql -Nume 'citire' + Test-Rezultat -R $r -Context 'citire continut scripturi din UPD_DATABASE' + foreach ($linie in ($r.Text -split "`r?`n")) { + $linie = $linie.Trim() + if (-not $linie.StartsWith('#')) { continue } + $c = $linie.Substring(1) -split '#', 3 + if ($c.Count -lt 3) { continue } + $id = [int]$c[0] + if ($rezultat.ContainsKey($id)) { [void]$rezultat[$id].Append($c[2]) } + } + $final = @{} + foreach ($k in $rezultat.Keys) { $final[$k] = ConvertFrom-Hex $rezultat[$k].ToString() } + $final +} + +function Importa-Script { + # insert sau update pe script_name existent; continutul se trimite hexazecimal, bucata cu bucata + param($Info, [byte[]]$Octeti) + $hex = ConvertTo-Hex $Octeti + $bucati = Split-Hex $hex + $sb = New-Object System.Text.StringBuilder + [void]$sb.AppendLine('set serveroutput on size 1000000') + [void]$sb.AppendLine('variable b_id number') + [void]$sb.AppendLine('declare') + [void]$sb.AppendLine(' l_id number;') + [void]$sb.AppendLine('begin') + [void]$sb.AppendLine(" select max(id) into l_id from upd_database where script_name = '$($Info.NumeScript)';") + [void]$sb.AppendLine(' if l_id is null then') + [void]$sb.AppendLine(' insert into upd_database') + [void]$sb.AppendLine(' (script_name, script_type, script_date, script_seq, script_content, script_order, script_appver)') + [void]$sb.AppendLine(" values ('$($Info.NumeScript)', '$($Info.Tip)', to_date('$($Info.DataYmd)','yyyymmdd'), $($Info.Secventa), empty_clob(), 0, null)") + [void]$sb.AppendLine(' returning id into l_id;') + [void]$sb.AppendLine(' else') + [void]$sb.AppendLine(' update upd_database') + [void]$sb.AppendLine(" set script_type = '$($Info.Tip)',") + [void]$sb.AppendLine(" script_date = to_date('$($Info.DataYmd)','yyyymmdd'),") + [void]$sb.AppendLine(" script_seq = $($Info.Secventa),") + [void]$sb.AppendLine(' script_content = empty_clob(),') + [void]$sb.AppendLine(' script_order = 0,') + [void]$sb.AppendLine(' script_appver = null') + [void]$sb.AppendLine(' where id = l_id;') + [void]$sb.AppendLine(' end if;') + [void]$sb.AppendLine(' :b_id := l_id;') + [void]$sb.AppendLine(' commit;') + [void]$sb.AppendLine(" dbms_output.put_line('IDSCRIPT=' || l_id);") + [void]$sb.AppendLine('end;') + [void]$sb.AppendLine('/') + + for ($i = 0; $i -lt $bucati.Count; $i += $BUCATI_PER_BLOC) { + [void]$sb.AppendLine('declare s varchar2(32767); l clob;') + [void]$sb.AppendLine('begin') + [void]$sb.AppendLine(' select script_content into l from upd_database where id = :b_id for update;') + for ($j = $i; $j -lt [Math]::Min($i + $BUCATI_PER_BLOC, $bucati.Count); $j++) { + [void]$sb.AppendLine(" s := utl_i18n.raw_to_char(hextoraw('$($bucati[$j])'),'WE8MSWIN1252'); dbms_lob.writeappend(l, length(s), s);") + } + [void]$sb.AppendLine(' commit;') + [void]$sb.AppendLine('end;') + [void]$sb.AppendLine('/') + } + [void]$sb.AppendLine('exit') + + $r = Invoke-SqlPlus -Sql $sb.ToString() -Nume ('imp_' + $Info.Stem) + Test-Rezultat -R $r -Context ('import ' + $Info.NumeScript) + if ($r.Text -notmatch 'IDSCRIPT=(\d+)') { Opreste ('import ' + $Info.NumeScript + ' - nu am primit id-ul randului.') } + [int]$Matches[1] +} + +function Scrie-XmlArhiva { + # acelasi format ca CURSORTOXML din genereazadatabasexml2: Windows-1252, CRLF, tab-uri, + # script_appver doar cand e completat, script_content gol (continutul e in fisierul separat) + param($Metadate, [string]$Cale) + $sb = New-Object System.Text.StringBuilder + [void]$sb.Append("`r`n") + [void]$sb.Append("`r`n") + foreach ($m in $Metadate) { + [void]$sb.Append("`t`r`n") + [void]$sb.Append("`t`t$($m.Id)`r`n") + [void]$sb.Append("`t`t$([System.Security.SecurityElement]::Escape($m.NumeScript))`r`n") + [void]$sb.Append("`t`t$([System.Security.SecurityElement]::Escape($m.Tip))`r`n") + [void]$sb.Append("`t`t$($m.DataYmd)`r`n") + [void]$sb.Append("`t`t$($m.Secventa)`r`n") + [void]$sb.Append("`t`t$($m.Ordine)`r`n") + if ($m.AppVer) { + [void]$sb.Append("`t`t$([System.Security.SecurityElement]::Escape($m.AppVer))`r`n") + } + [void]$sb.Append("`t`t$($m.Stem).sql`r`n") + [void]$sb.Append("`t`t`r`n") + [void]$sb.Append("`t`r`n") + } + [void]$sb.Append("`r`n") + [System.IO.File]::WriteAllText($Cale, $sb.ToString(), [System.Text.Encoding]::GetEncoding(1252)) +} + +function Scrie-Arhiva { + # xml-ul primul, apoi scripturile in ordinea din xml (ca ZipOpen/ZipFile din VFP) + param([string]$CaleZip, [string]$CaleXml, $Fisiere) + if (Test-Path $CaleZip) { Remove-Item -LiteralPath $CaleZip -Force } + $z = [System.IO.Compression.ZipFile]::Open($CaleZip, 'Create') + try { + [void][System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($z, $CaleXml, [System.IO.Path]::GetFileName($CaleXml), 'Optimal') + foreach ($f in $Fisiere) { + [void][System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($z, $f, [System.IO.Path]::GetFileName($f), 'Optimal') + } + } finally { + $z.Dispose() + } +} + +function Scrie-IndexArhive { + # roa_database.xml / roa_databasen.xml - lista arhivelor existente, sortata dupa nume de fisier + param([string]$Prefix, [string]$Cale) + $arhive = @(Get-ChildItem -LiteralPath $ZipPath -Filter ($Prefix + '_*.zip') -File | + Where-Object { $_.Name -like ($Prefix + '_*.zip') } | Sort-Object Name) + $sb = New-Object System.Text.StringBuilder + [void]$sb.Append("`r`n") + [void]$sb.Append("`r`n") + foreach ($a in $arhive) { + $parti = [System.IO.Path]::GetFileNameWithoutExtension($a.Name) -split '_' + if ($parti.Count -lt 3) { continue } + [void]$sb.Append("`t`r`n") + [void]$sb.Append("`t`t|BASEPATH|/$($a.Name)`r`n") + [void]$sb.Append("`t`t$($parti[1])`r`n") + [void]$sb.Append("`t`t$($parti[2])`r`n") + [void]$sb.Append("`t`r`n") + } + [void]$sb.Append("`r`n") + [System.IO.File]::WriteAllText($Cale, $sb.ToString(), [System.Text.Encoding]::GetEncoding(1252)) +} + +function Verifica-ArhivaPrinOracle { + # citeste arhiva inapoi cu cititorul clientului (pack_utils.decodebase64 + zip_util_pkg.get_file) + # si compara lungimea + MD5 al fiecarui fisier extras cu cel de pe disc + param([string]$CaleZip, $FisiereAsteptate) + $octeti = [System.IO.File]::ReadAllBytes($CaleZip) + $b64 = [Convert]::ToBase64String($octeti) + $sb = New-Object System.Text.StringBuilder + [void]$sb.AppendLine('set serveroutput on size unlimited') + [void]$sb.AppendLine('set pagesize 0 linesize 400 trimspool on heading off') + [void]$sb.AppendLine('variable b_z clob') + [void]$sb.AppendLine('begin dbms_lob.createtemporary(:b_z, true); end;') + [void]$sb.AppendLine('/') + # bucati multiplu de 4, ca sirul base64 sa ramana valid oriunde s-ar taia + $pas = 1920 + for ($i = 0; $i -lt $b64.Length; $i += $pas * $BUCATI_PER_BLOC) { + [void]$sb.AppendLine('declare s varchar2(32767);') + [void]$sb.AppendLine('begin') + for ($j = $i; $j -lt [Math]::Min($i + $pas * $BUCATI_PER_BLOC, $b64.Length); $j += $pas) { + $bucata = $b64.Substring($j, [Math]::Min($pas, $b64.Length - $j)) + [void]$sb.AppendLine(" s := '$bucata'; dbms_lob.writeappend(:b_z, length(s), s);") + } + [void]$sb.AppendLine('end;') + [void]$sb.AppendLine('/') + } + [void]$sb.AppendLine('declare') + [void]$sb.AppendLine(' l_zip blob; l_f blob;') + [void]$sb.AppendLine('begin') + [void]$sb.AppendLine(' l_zip := pack_utils.decodebase64(:b_z);') + [void]$sb.AppendLine(" dbms_output.put_line('ZIP|' || dbms_lob.getlength(l_zip));") + foreach ($f in $FisiereAsteptate) { + [void]$sb.AppendLine(" l_f := zip_util_pkg.get_file(l_zip, '$f');") + [void]$sb.AppendLine(" if l_f is null then dbms_output.put_line('LIPSA|$f');") + [void]$sb.AppendLine(" else dbms_output.put_line('FIS|$f|' || dbms_lob.getlength(l_f) || '|' || rawtohex(dbms_crypto.hash(l_f, 2))); end if;") + } + [void]$sb.AppendLine('end;') + [void]$sb.AppendLine('/') + [void]$sb.AppendLine('exit') + + $r = Invoke-SqlPlus -Sql $sb.ToString() -Nume 'verifzip' + Test-Rezultat -R $r -Context ('verificare arhiva ' + [System.IO.Path]::GetFileName($CaleZip)) + $citite = @{} + foreach ($linie in ($r.Text -split "`r?`n")) { + $linie = $linie.Trim() + if ($linie -match '^FIS\|([^|]+)\|(\d+)\|([0-9A-F]+)$') { + $citite[$Matches[1]] = [PSCustomObject]@{ Lungime = [int]$Matches[2]; Md5 = $Matches[3] } + } elseif ($linie -match '^LIPSA\|(.+)$') { + Opreste ('verificare arhiva - zip_util_pkg nu gaseste ' + $Matches[1] + ' in ' + $CaleZip) + } + } + $md5 = [System.Security.Cryptography.MD5]::Create() + $erori = 0 + foreach ($f in $FisiereAsteptate) { + if (-not $citite.ContainsKey($f)) { Write-Output (' LIPSA ' + $f); $erori++; continue } + $local = Join-Path $TempDir $f + $b = [System.IO.File]::ReadAllBytes($local) + $h = (ConvertTo-Hex $md5.ComputeHash($b)) + if ($citite[$f].Lungime -eq $b.Length -and $citite[$f].Md5 -eq $h) { + Write-Output (' ok ' + $f + ' (' + $b.Length + ' octeti)') + } else { + Write-Output (' DIFERIT ' + $f + ' - disc ' + $b.Length + '/' + $h + ', extras ' + $citite[$f].Lungime + '/' + $citite[$f].Md5) + $erori++ + } + } + $md5.Dispose() + if ($erori -gt 0) { Opreste ('verificare arhiva - ' + $erori + ' fisiere nu se citesc corect prin zip_util_pkg.') } +} + +# ---------- pregatire ---------- +$TempDir = Join-Path $env:TEMP ('publicare_scripturi_' + [guid]::NewGuid().ToString('N')) +New-Item -ItemType Directory -Force -Path $TempDir | Out-Null + +if (-not (Test-Path -LiteralPath $Settings)) { Opreste ('nu gasesc settings.ini la ' + $Settings + ' - da caile cu -DirScripturi/-ZipPath/-Alias/-User/-Password sau alt -Settings.') } +$ini = Citeste-Ini $Settings + +# parametrii dati explicit bat ini-ul +if (-not $PSBoundParameters.ContainsKey('User')) { $User = Ia-Cheie $ini 'connection' 'username_database' } +if (-not $PSBoundParameters.ContainsKey('Password')) { $Password = Ia-Cheie $ini 'connection' 'password_database' } +if (-not $PSBoundParameters.ContainsKey('Alias')) { + $dsn = Ia-Cheie $ini 'connection' 'host_database' + if ($dsn) { $Alias = Rezolva-Alias $dsn } +} +if (-not $PSBoundParameters.ContainsKey('DirScripturi')) { + $f = Ia-Cheie $ini 'folder' 'script_folder' + if ($f) { $DirScripturi = ($f.TrimEnd('\') -replace '\\SCRIPTURI$', '\SCRIPTURI_CLAR') } +} +if (-not $PSBoundParameters.ContainsKey('ZipPath')) { + $f = Ia-Cheie $ini 'folder' 'roa_output' + if ($f) { $ZipPath = (Join-Path $f '_UPDATE') } +} +if (-not $PSBoundParameters.ContainsKey('SqlPlus')) { $SqlPlus = Ia-Cheie $ini 'folder' 'sqlplus_exe' } +foreach ($p in @(@('User', $User), @('Password', $Password), @('Alias', $Alias), @('DirScripturi', $DirScripturi), @('ZipPath', $ZipPath), @('SqlPlus', $SqlPlus))) { + if (-not $p[1]) { Opreste ('lipseste ' + $p[0] + ' - nu e in ' + $Settings + ' si nu a fost dat ca parametru.') } +} +# prefixele valide (o schema tinta per prefix), pentru validarea numelor de scripturi +$prefixe = @() +if ($ini.ContainsKey('script')) { $prefixe = @($ini['script'].Values | ForEach-Object { $_.TrimEnd('_').ToUpper() }) } + +if (-not (Test-Path $SqlPlus)) { Opreste ('nu gasesc sqlplus la ' + $SqlPlus) } +$env:TNS_ADMIN = Split-Path $SqlPlus -Parent +$ConnStr = $User + '/' + $Password + '@' + $Alias + +if ($DoarImport -and $DoarArhiva) { Opreste '-DoarImport si -DoarArhiva se exclud reciproc.' } + +# ---------- 1. ce se publica ---------- +Scrie-Titlu '1. SCRIPTURI DE PUBLICAT' +$candidati = New-Object System.Collections.ArrayList +if ($Fisier) { + foreach ($f in $Fisier) { + $cale = $f + if (-not [System.IO.Path]::IsPathRooted($cale)) { + $cale = (Resolve-Path -LiteralPath $f -ErrorAction SilentlyContinue) + if (-not $cale) { Opreste ('nu gasesc fisierul ' + $f) } + $cale = $cale.Path + } + if (-not (Test-Path -LiteralPath $cale)) { Opreste ('nu gasesc fisierul ' + $cale) } + $info = Parseaza-NumeScript $cale + if (-not $info) { Opreste ('nume de script neconform (_YYYY_MM_DD_NN_.sql): ' + $cale) } + [void]$candidati.Add($info) + } + $luni = @($candidati | ForEach-Object { $_.Data.ToString('yyyy-MM') } | Sort-Object -Unique) + if ($luni.Count -gt 1) { Opreste ('scripturile date sunt din luni diferite (' + ($luni -join ', ') + ') - ruleaza cate o luna.') } + if (-not $Luna) { $Luna = $luni[0] } +} else { + if (-not $Luna) { $Luna = (Get-Date).ToString('yyyy-MM') } +} + +if ($Luna -notmatch '^(\d{4})-(\d{2})$') { Opreste ('-Luna trebuie in formatul YYYY-MM (primit: ' + $Luna + ')') } +$anLuna = [int]$Matches[1] +$lunaLuna = [int]$Matches[2] +$dataInceput = Get-Date -Year $anLuna -Month $lunaLuna -Day 1 -Hour 0 -Minute 0 -Second 0 +$dataSfarsit = $dataInceput.AddMonths(1).AddDays(-1) +$etichetaLuna = $dataInceput.ToString('yyyyMMdd') + '_' + $dataSfarsit.ToString('yyyyMMdd') + +$dirLuna = Join-Path (Join-Path $DirScripturi $dataInceput.ToString('yyyy')) $dataInceput.ToString('MM') +if (-not $Fisier -and -not $DoarArhiva) { + if (-not (Test-Path -LiteralPath $dirLuna)) { Opreste ('nu gasesc directorul lunii: ' + $dirLuna) } + foreach ($f in (Get-ChildItem -LiteralPath $dirLuna -Filter '*.sql' -File | Sort-Object Name)) { + $info = Parseaza-NumeScript $f.FullName + if (-not $info) { Write-Output (' [sarit] nume neconform: ' + $f.Name); continue } + [void]$candidati.Add($info) + } +} + +Write-Output ('Luna: ' + $Luna + ' arhiva: ' + $etichetaLuna) +Write-Output ('Configurare: ' + $Settings) +Write-Output ('Baza: ' + $User + '@' + $Alias + ' (' + $SqlPlus + ')') +Write-Output ('Scripturi: ' + $dirLuna) +Write-Output ('Arhive: ' + $ZipPath) + +foreach ($c in $candidati) { + if ($prefixe.Count -gt 0 -and $prefixe -notcontains $c.Prefix) { + Write-Output (' [atentie] prefix necunoscut "' + $c.Prefix + '" in ' + [System.IO.Path]::GetFileName($c.Cale) + ' (in ini: ' + ($prefixe -join ', ') + ')') + } + $octeti = [System.IO.File]::ReadAllBytes($c.Cale) + if ($octeti.Length -eq 0) { Opreste ('fisier gol: ' + $c.Cale) } + $text = [System.Text.Encoding]::GetEncoding(1252).GetString($octeti) + if ($text -match "(? id ' + $id) + } + $metadate = Citeste-MetadateLuna -Inceput $dataInceput -Sfarsit $dataSfarsit + $dupaNume = @{} + foreach ($m in $metadate) { $dupaNume[$m.NumeScript] = $m } + $verif = Citeste-Continut -Metadate @($deImportat | ForEach-Object { $dupaNume[$_.NumeScript] }) + $erori = 0 + foreach ($c in $deImportat) { + $m = $dupaNume[$c.NumeScript] + $inBaza = $verif[$m.Id] + if (Compara-Octeti -A $inBaza -B $c.Octeti) { + Write-Output (' verificat ' + $c.NumeScript + ' (' + $c.Octeti.Length + ' octeti, identic cu fisierul)') + } else { + Write-Output (' DIFERIT ' + $c.NumeScript + ' - continutul din baza nu coincide cu fisierul!') + $erori++ + } + } + if ($erori -gt 0) { Opreste ($erori + ' scripturi nu s-au scris corect in baza - nu generez arhiva.') } + } +} + +if ($DoarImport) { + Scrie-Titlu 'GATA (doar import)' + Write-Output 'Arhiva NU a fost regenerata - clientii primesc scripturile abia dupa pasul de arhiva.' + Curata-Temp + exit 0 +} + +# ---------- 4. arhiva lunara ---------- +Scrie-Titlu '4. ARHIVA LUNARA' +if (-not (Test-Path -LiteralPath $ZipPath)) { Opreste ('nu gasesc directorul arhivelor: ' + $ZipPath) } +if ($DryRun) { + Write-Output ('DryRun - as regenera database_' + $etichetaLuna + '.zip si databasen_' + $etichetaLuna + '.zip din ' + $metadate.Count + ' scripturi.') + Curata-Temp + exit 0 +} + +$metadate = Citeste-MetadateLuna -Inceput $dataInceput -Sfarsit $dataSfarsit +if ($metadate.Count -eq 0) { Opreste ('nu exista scripturi in UPD_DATABASE pentru luna ' + $Luna + ' - nu am ce arhiva.') } + +$continuturi = Citeste-Continut -Metadate $metadate +$fisiereSql = New-Object System.Collections.ArrayList +foreach ($m in $metadate) { + $b = $continuturi[$m.Id] + if (-not $b -or $b.Length -ne $m.Lungime) { + Opreste ('citire incompleta pentru ' + $m.NumeScript + ' (asteptat ' + $m.Lungime + ' octeti, citit ' + $(if ($b) { $b.Length } else { 0 }) + ').') + } + $numeFis = $m.Stem + '.sql' + [System.IO.File]::WriteAllBytes((Join-Path $TempDir $numeFis), $b) + [void]$fisiereSql.Add($numeFis) +} +Write-Output ('Extrase din baza: ' + $fisiereSql.Count + ' scripturi.') + +$numeFisiere = @($fisiereSql) +$rezultat = @() +foreach ($prefix in @('database', 'databasen')) { + $numeXml = $prefix + '_' + $etichetaLuna + '.xml' + $caleXml = Join-Path $TempDir $numeXml + Scrie-XmlArhiva -Metadate $metadate -Cale $caleXml + $caleZip = Join-Path $TempDir ($prefix + '_' + $etichetaLuna + '.zip') + Scrie-Arhiva -CaleZip $caleZip -CaleXml $caleXml -Fisiere @($numeFisiere | ForEach-Object { Join-Path $TempDir $_ }) + Write-Output ('Generat ' + [System.IO.Path]::GetFileName($caleZip) + ' (' + (Get-Item -LiteralPath $caleZip).Length + ' octeti)') + $rezultat += [PSCustomObject]@{ Zip = $caleZip; Xml = $numeXml } +} + +# ---------- 5. verificare cu cititorul clientului ---------- +if (-not $FaraVerificareZip) { + Scrie-Titlu '5. VERIFICARE ARHIVA PRIN ZIP_UTIL_PKG' + Verifica-ArhivaPrinOracle -CaleZip $rezultat[0].Zip -FisiereAsteptate (@($rezultat[0].Xml) + $numeFisiere) +} else { + Scrie-Titlu '5. VERIFICARE ARHIVA PRIN ZIP_UTIL_PKG' + Write-Output 'Sarit (-FaraVerificareZip).' +} + +# ---------- 6. publicare in _UPDATE ---------- +Scrie-Titlu '6. PUBLICARE IN _UPDATE' +if (-not $BackupPath) { $BackupPath = Join-Path $ZipPath ('_backup\' + (Get-Date).ToString('yyyyMMdd_HHmmss')) } +New-Item -ItemType Directory -Force -Path $BackupPath | Out-Null +foreach ($n in @(($rezultat | ForEach-Object { [System.IO.Path]::GetFileName($_.Zip) }) + 'roa_database.xml' + 'roa_databasen.xml')) { + $tinta = Join-Path $ZipPath $n + if (Test-Path -LiteralPath $tinta) { + Copy-Item -LiteralPath $tinta -Destination (Join-Path $BackupPath $n) -Force + Write-Output (' salvat ' + $n + ' -> ' + $BackupPath) + } +} +foreach ($r in $rezultat) { + Copy-Item -LiteralPath $r.Zip -Destination (Join-Path $ZipPath ([System.IO.Path]::GetFileName($r.Zip))) -Force + Write-Output (' publicat ' + [System.IO.Path]::GetFileName($r.Zip)) +} +Scrie-IndexArhive -Prefix 'database' -Cale (Join-Path $ZipPath 'roa_database.xml') +Scrie-IndexArhive -Prefix 'databasen' -Cale (Join-Path $ZipPath 'roa_databasen.xml') +Write-Output ' publicat roa_database.xml, roa_databasen.xml' + +Scrie-Titlu 'GATA' +Write-Output ('Luna ' + $Luna + ': ' + $metadate.Count + ' scripturi in arhiva, ' + $deImportat.Count + ' importate acum.') +Write-Output ('Backup-ul fisierelor inlocuite: ' + $BackupPath) +Curata-Temp +exit 0 From c6216e006e615fbd31b9d8de86cade08382afde8 Mon Sep 17 00:00:00 2001 From: Marius Mutu Date: Thu, 6 Aug 2026 23:29:16 +0300 Subject: [PATCH 9/9] acces email Thunderbird + tabela de provenienta la top segmente in diagnosticul de spatiu --- docs/depanare-spatiu-oracle.md | 19 ++++++ docs/email-thunderbird.md | 41 +++++++++++++ docs/scripturi-uzuale.md | 1 + utile/citeste_email.ps1 | 103 +++++++++++++++++++++++++++++++++ 4 files changed, 164 insertions(+) create mode 100644 docs/email-thunderbird.md create mode 100644 utile/citeste_email.ps1 diff --git a/docs/depanare-spatiu-oracle.md b/docs/depanare-spatiu-oracle.md index 265d741..e36b97c 100644 --- a/docs/depanare-spatiu-oracle.md +++ b/docs/depanare-spatiu-oracle.md @@ -22,6 +22,16 @@ cu `diag_spatiu.ps1`; **email doar la prag depasit**, tacere completa altfel. Su Sectiunile informative (recyclebin, UNDO, TEMP, istoric statistici/scheduler, top segmente, audit SYS, disc) se scriu doar cand exista deja o alerta — dau context, nu alerteaza singure. +`co_2026_08_06_08_DIAG_SPATIU_PACK` + `sys_2026_08_06_07_DIAG_SPATIU_GRANT3` (06.08.2026) scriu la +top segmente si **tabela/coloana din care provine segmentul** (`DetaliuSegment`: `DBA_LOBS`, +`DBA_LOB_PARTITIONS`, `DBA_INDEXES`; segmentele temporare se marcheaza ca atare) — fara asta un +`SYS_LOB0000630560C00017$$` de 4 GB in email nu spune nimic, iar serverul clientului nu e accesibil +direct. Atentie: pentru `LOB PARTITION`, `dba_segments.segment_name` e `lob_name`, nu +`lob_partition_name`. + +Emailurile ajung pe `marius.mutu@romfast.ro`, cate un expeditor per client +(`roaupdate_@romfast.ro`); citirea lor: `email-thunderbird.md`. + `co_2026_08_06_01_DIAG_SPATIU_PACK` (06.08.2026) corecteaza doua lucruri vazute pe primele rulari: - **pragul absolut (2048 MB) nu se mai aplica tablespace-urilor al caror maxim e sub el** — orice @@ -80,6 +90,15 @@ SYS, disc) se scriu doar cand exista deja o alerta — dau context, nu alerteaza ## Capcane +- **Segmentele `TEMPORARY` dintr-un tablespace permanent nu dispar intotdeauna la restart** (ACN, + 06.08.2026: 1.3 GB ramasi si dupa repornirea serviciului). Daca `v$sort_usage` e gol, sunt orfane + si se curata fortat, ca `SYS`: `alter session set events 'immediate trace name DROP_SEGMENTS level + '` (`ts#` din `v$tablespace`) — la ACN au disparut instantaneu, cu tot spatiul recuperat. + Daca nici asa nu pleaca, apartin unui DDL neterminat — cauta `SYS_JOURNAL%` (index online) sau + obiecte `INVALID/UNUSABLE` in schema respectiva. +- **`shrink space` pe un LOB cere ASSM**; pe un tablespace MSSM da `ORA-10635` (cazul `ROA` la ACN). + Echivalentul care merge: `alter table t move lob (col) store as (tablespace )` — reconstruieste + segmentul, tine tabela blocata cat dureaza si cere spatiu liber cat segmentul mutat. - **Privilegiile `ANY` nu se aplica pe obiectele din schema `SYS`** cat timp `O7_DICTIONARY_ACCESSIBILITY=FALSE` — orice curatare acolo cere `SYSDBA`. - Parola `SYS` e de obicei cea generica (vezi `COMUN\docs\local\oracle.md`), dar **nu la toti diff --git a/docs/email-thunderbird.md b/docs/email-thunderbird.md new file mode 100644 index 0000000..893fcf1 --- /dev/null +++ b/docs/email-thunderbird.md @@ -0,0 +1,41 @@ +# Citirea emailurilor (Thunderbird Portable) + +Conectorul Gmail din Claude vede **doar** `mmarius28@gmail.com`. Conturile de lucru +(`marius.mutu@romfast.ro` etc.) nu sunt acolo — se citesc local, din mbox-urile Thunderbird: + +``` +D:\UTIL\portable\PortableApps\ThunderbirdPortable\Data\profile\ImapMail\\INBOX +``` + +Conturi active (dupa `LastWriteTime`): `mail.romfast.ro` (~36 MB, contul principal) si +`mail.romfast-1.ro` (~108 MB). Restul (`seanet.ro`, `romfast-4.ro`, `imap.googlemail.com`, …) +sunt vechi de ani. + +```powershell +powershell -File COMUN\utile\citeste_email.ps1 -Cauta 'Diagnostic spatiu' -Ultimele 10 +powershell -File COMUN\utile\citeste_email.ps1 -Nr [-Linii 200] +``` + +Prima forma listeaza `Cont / Linie / Data / De_la / Subiect` (filtru `-Cauta` pe subiect **sau** +expeditor, `-Cont mail.romfast.ro` restrange la un cont); a doua tipareste mesajul care incepe la +linia respectiva, pana la urmatorul separator mbox. + +## Capcane + +- **Nu da `Grep`/ripgrep pe folder**: `INBOX.sbd\Sent` are ~2.5 GB si cautarea expira. +- **Numerotarea liniilor difera intre unelte** (`Select-String` vs `Get-Content` vs `StreamReader`) + — mbox-ul are linii cu `\r` singur. Foloseste numarul dat de `citeste_email.ps1`, pentru ca + `-Nr` citeste cu acelasi cititor; daca linia ceruta nu e inceput de mesaj, scriptul trece la + contul urmator (`-Nr` fara `-Cont` cauta in toate). +- Thunderbird tine fisierul deschis → citire doar cu `FileShare::ReadWrite` (scriptul o face). +- Mesajele sunt in ordine cronologica, **cel mai nou la sfarsitul fisierului**; separatorul e + `From - `. Corpurile HTML/base64 se tipar brut — pentru ele ia doar `Content-Type` si + primele linii, nu tot mesajul. + +## Emailuri automate ROA + +| Expeditor | Ce e | +|---|---| +| `roaupdate_acn@`, `roaupdate-sigma@`, `roaupdate-clever@`, `roaupdate-automotive@` … | jobul `DIAGSPATIU_ZILNIC`, subiect `Diagnostic spatiu Oracle `, cate un expeditor per client. Vezi `depanare-spatiu-oracle.md` | + +Emailul vine **doar la prag depasit**; tacere = totul in regula. diff --git a/docs/scripturi-uzuale.md b/docs/scripturi-uzuale.md index d079951..a21f5f7 100644 --- a/docs/scripturi-uzuale.md +++ b/docs/scripturi-uzuale.md @@ -14,6 +14,7 @@ a doua oara — daca a doua oara e nevoie de acelasi lucru, scrie scriptul. | spatiu plin la un client | `COMUN\utile\diag_spatiu.ps1 -Alias [-Disc] [-SysPassword]` | tablespace, audit, ADR, FRA, disc server, top segmente. Vezi `depanare-spatiu-oracle.md` | | publicare scripturi de baza de date | `COMUN\utile\publicare_scripturi.ps1 [-Luna YYYY-MM] [-Fisier ...] [-DryRun]` | import in `UPD_DATABASE` → arhiva lunara `database[n]_*.zip` + indexi in `_UPDATE`, cu verificare octet cu octet. Vezi `publicare-scripturi-db.md` | | teste UI VFP | harness din `COMUN\utile\Teste` | vezi `testare-ui-vfp.md` | +| citirea emailurilor de pe conturile romfast | `COMUN\utile\citeste_email.ps1 [-Cauta ...] [-Nr ]` | listeaza/extrage mesaje din mbox-urile Thunderbird Portable (conectorul Gmail nu vede conturile romfast). Vezi `email-thunderbird.md` | Documentatia **nu** se genereaza automat si nu exista niciun hook care s-o scrie — `livrare.ps1` doar semnaleaza ce lipseste (changelog neatins, binar VFP fara textul lui sincronizat). diff --git a/utile/citeste_email.ps1 b/utile/citeste_email.ps1 new file mode 100644 index 0000000..73cbf1b --- /dev/null +++ b/utile/citeste_email.ps1 @@ -0,0 +1,103 @@ +# Citeste emailurile din mbox-urile Thunderbird Portable (fara conectorul Gmail). +# citeste_email.ps1 [-Cauta ] [-Cont ] [-Ultimele ] -> lista mesaje +# citeste_email.ps1 -Nr [-Linii ] -> corpul unui mesaj +param( + [string]$Cauta = '', + [string]$Cont = '', + [int]$Ultimele = 20, + [int]$Nr = 0, + [int]$Linii = 200, + [string]$Radacina = 'D:\UTIL\portable\PortableApps\ThunderbirdPortable\Data\profile\ImapMail' +) + +$ErrorActionPreference = 'Stop' + +function Get-Mboxuri { + param([string]$Cont) + $c = if ($Cont) { $Cont } else { '*' } + Get-ChildItem $Radacina -Directory -Filter $c | + ForEach-Object { Join-Path $_.FullName 'INBOX' } | + Where-Object { Test-Path $_ } | + Where-Object { (Get-Item $_).LastWriteTime -gt (Get-Date).AddYears(-2) } +} + +function Deschide-Fisier { + param([string]$Cale) + # Thunderbird tine fisierul deschis -> FileShare ReadWrite obligatoriu + $fs = New-Object IO.FileStream($Cale, [IO.FileMode]::Open, [IO.FileAccess]::Read, [IO.FileShare]::ReadWrite) + New-Object IO.StreamReader($fs, [Text.Encoding]::GetEncoding(28591)) +} + +function Decodeaza-Subiect { + param([string]$S) + $rx = [regex]'=\?([^?]+)\?([BbQq])\?([^?]*)\?=' + $rx.Replace($S, { + param($m) + $enc = [Text.Encoding]::GetEncoding($m.Groups[1].Value) + $txt = $m.Groups[3].Value + if ($m.Groups[2].Value -match '[Bb]') { + $enc.GetString([Convert]::FromBase64String($txt)) + } else { + $b = New-Object Collections.Generic.List[byte] + for ($i = 0; $i -lt $txt.Length; $i++) { + if ($txt[$i] -eq '=') { $b.Add([Convert]::ToByte($txt.Substring($i + 1, 2), 16)); $i += 2 } + elseif ($txt[$i] -eq '_') { $b.Add(32) } + else { $b.Add([byte][char]$txt[$i]) } + } + $enc.GetString($b.ToArray()) + } + }) +} + +# ---- corpul unui mesaj: de la linia data pana la urmatorul separator mbox ---- +if ($Nr -gt 0) { + foreach ($f in Get-Mboxuri -Cont $Cont) { + $r = Deschide-Fisier $f + try { + $i = 0; $scris = 0; $pornit = $false + while ($null -ne ($l = $r.ReadLine())) { + $i++ + if ($i -lt $Nr) { continue } + # linia ceruta trebuie sa fie inceput de mesaj, altfel e alt cont + if ($i -eq $Nr -and $l -notmatch '^From - \w\w\w \w\w\w ') { break } + if ($pornit -and $l -match '^From - \w\w\w \w\w\w ') { break } + $pornit = $true + $l + if (++$scris -ge $Linii) { "... (taiat la $Linii linii, mareste cu -Linii)"; break } + } + if ($pornit) { return } + } finally { $r.Dispose() } + } + Write-Warning "Linia $Nr nu exista in mbox-urile cautate." + return +} + +# ---- lista de mesaje ---- +$mesaje = @() +foreach ($f in Get-Mboxuri -Cont $Cont) { + $cont = Split-Path (Split-Path $f -Parent) -Leaf + $r = Deschide-Fisier $f + try { + $i = 0; $inAntet = $false; $m = $null + while ($null -ne ($l = $r.ReadLine())) { + $i++ + if ($l -match '^From - \w\w\w \w\w\w ') { + if ($m) { $mesaje += $m } + $m = [pscustomobject]@{ Cont = $cont; Linie = $i; Data = ''; De_la = ''; Subiect = '' } + $inAntet = $true + continue + } + if (-not $inAntet -or -not $m) { continue } + if ($l -eq '') { $inAntet = $false; continue } + if ($l -like 'Date: *' -and -not $m.Data) { $m.Data = $l.Substring(6).Trim() } + elseif ($l -like 'From: *' -and -not $m.De_la) { $m.De_la = $l.Substring(6).Trim() } + elseif ($l -like 'Subject: *' -and -not $m.Subiect) { $m.Subiect = Decodeaza-Subiect $l.Substring(9).Trim() } + } + if ($m) { $mesaje += $m } + } finally { $r.Dispose() } +} + +if ($Cauta) { + $mesaje = $mesaje | Where-Object { $_.Subiect -like "*$Cauta*" -or $_.De_la -like "*$Cauta*" } +} +$mesaje | Select-Object -Last $Ultimele | Format-Table -AutoSize -Wrap