Files
ROMFASTSQL/proxmox/lxc108-oracle/scripts/build-client-kit.ps1
Marius 5a746990ce feat(oracle): instalare ROA in doua faze, licente, IIS si export zilnic
Adauga FAZA1.cmd/FAZA2.cmd, care despart migrarea in partea care se poate
face in timpul programului (contafin, obiecte SYS, sinonime, licente, IIS)
si partea de seara (schemele de firma). Scripturi noi: 09 pentru licentele
din SYS.AUTH_SERII/AUTH_DETALII, 10 pentru publicarea D:\ROAUPDATE in IIS,
11 pentru exportul zilnic cu backupora.exe, drop-contafin pentru reimport.

backupora.exe si sabloanele sale intra in repo ca sa fie kitul autonom.
Pasul 07 primeste verificarile corespunzatoare pentru licente, IIS si task.

Nerulat inca pe o baza reala - vezi docs/handoff_instalare-doua-faze.md.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SzF1hf4aFS1tmJWpPMiwGp
2026-08-25 20:57:23 +03:00

519 lines
22 KiB
PowerShell

#Requires -Version 5.1
<#
.SYNOPSIS
Construieste kitul de instalare ROA pentru un client: DMP-urile sablon
exportate din LXC 108 + scripturile de instalare/migrare, intr-un singur
director gata de copiat pe serverul clientului.
.DESCRIPTION
Ce face, in ordine:
1. Ruleaza exportul pe LXC 108 (`export-roa2.sh`, acelasi script folosit
de `export-roa2.ps1`). Sursa e PDB-ul ROA2, nu ROA - ROA e instanta
de productie ROA_CENTRAL si nu se scrie in ea.
2. Aduce arhivele .tar.gz pe statia locala. Sunt trei niveluri:
container Docker -> LXC 108 -> host pvemini -> statia ta. `docker cp`
te scoate doar din container, de aceea e nevoie si de `pct pull`.
3. Despacheteaza si redenumeste DMP-urile la numele pe care le asteapta
scripturile: `contafin_oracle.dmp` si `FIRMANOUA.dmp`.
4. Citeste logul expdp si raporteaza cate firme are NOM_FIRME in dump.
Asa vezi daca ai construit un sablon curat sau ai prins din greseala
datele unui client.
5. Copiaza peste ele continutul din `roa-windows-setup/`.
Rezultatul e un director care se dezarhiveaza la client si se ruleaza cu
`INSTALEAZA.cmd curat` sau `INSTALEAZA.cmd migrare`.
.PARAMETER OracleVersion
Sursa exportului: '21' (PDB ROA2, VERSION=19) sau '18' (container
oracle18-xe, VERSION=11.2, pentru baze mai vechi). Implicit '21'.
.PARAMETER OutputDir
Directorul parinte in care se creeaza kitul. Implicit E:\backups\roa-kit.
.PARAMETER KitName
Numele directorului de kit. Implicit roa-kit-<data>.
.PARAMETER SkipExport
Nu ruleaza export nou; foloseste cele mai recente arhive deja prezente pe
LXC 108. Util cand ai exportat deja si vrei doar sa reconstruiesti kitul.
.PARAMETER Zip
Creeaza si o arhiva .zip a kitului, langa director.
.PARAMETER PushTo
Optional, destinatie scp pentru kit dupa constructie, in forma
`utilizator@gazda:/cale`. Se trimite directorul intreg, recursiv.
.EXAMPLE
.\build-client-kit.ps1
Export nou din ROA2 si kit in E:\backups\roa-kit\roa-kit-<data>.
.EXAMPLE
.\build-client-kit.ps1 -Zip -KitName "roa-kit-clientX"
Kit cu nume fix, plus arhiva .zip de trimis clientului.
.EXAMPLE
.\build-client-kit.ps1 -SkipExport -PushTo "romfast@10.0.20.130:C:/roa-kit"
Refoloseste ultimele arhive si trimite kitul direct pe serverul tinta.
.NOTES
Necesita ssh/scp (OpenSSH din Windows 10+) si tar (bsdtar, tot din Windows).
Acces cu cheie la host-ul Proxmox - vezi docs/acces-ssh-chei-angajati.md.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[ValidateSet('21', '18')]
[string]$OracleVersion = '21',
[Parameter(Mandatory = $false)]
[string]$OutputDir = 'E:\backups\roa-kit',
[Parameter(Mandatory = $false)]
[string]$KitName,
[Parameter(Mandatory = $false)]
[switch]$SkipExport,
[Parameter(Mandatory = $false)]
[switch]$Zip,
[Parameter(Mandatory = $false)]
[string]$PushTo,
[Parameter(Mandatory = $false)]
[string]$ProxmoxHost = 'root@10.0.20.201',
[Parameter(Mandatory = $false)]
[int]$Ctid = 108
)
$ErrorActionPreference = 'Stop'
# Schemele exportate si numele sub care ajung in kit. FIRMANOUA.dmp trebuie sa
# aiba exact numele asta: SYS.NEWSCHEMA il compune ca <schema-sursa> || '.dmp'.
$SchemaMap = [ordered]@{
'CONTAFIN_ORACLE' = 'contafin_oracle.dmp'
'FIRMANOUA' = 'FIRMANOUA.dmp'
}
if ($OracleVersion -eq '18') {
$Container = 'oracle18-xe'
$RemoteDmpDir = '/opt/oracle18/oradata/dmpdir'
$SourceLabel = 'Oracle 18c (XEPDB1), VERSION=11.2'
}
else {
$Container = 'oracle-xe'
$RemoteDmpDir = '/opt/oracle/oradata/dmpdir'
$SourceLabel = 'Oracle 21c (PDB ROA2), VERSION=19'
}
function Write-Step { param([string]$m) Write-Host "`n=== $m ===" -ForegroundColor Cyan }
function Write-Ok { param([string]$m) Write-Host " [OK] $m" -ForegroundColor Green }
function Write-Info { param([string]$m) Write-Host " $m" }
function Write-Warn { param([string]$m) Write-Host " [ATENTIE] $m" -ForegroundColor Yellow }
function Invoke-Remote {
<# Ruleaza o comanda pe host-ul Proxmox si intoarce iesirea.
Opreste scriptul daca ssh iese cu cod nenul. #>
param(
[Parameter(Mandatory = $true)][string]$Command,
[switch]$AllowFailure
)
# Fara "2>&1": in Windows PowerShell 5.1 redirectarea stderr-ului unei
# comenzi native impacheteaza fiecare linie intr-un ErrorRecord, iar
# mesajul de eroare devine "System.Management.Automation.RemoteException".
# Lasam stderr sa curga direct in consola si ne bazam pe codul de iesire.
$out = & ssh -o BatchMode=yes $ProxmoxHost $Command
if ($LASTEXITCODE -ne 0 -and -not $AllowFailure) {
throw "Comanda pe $ProxmoxHost a esuat (cod $LASTEXITCODE): $Command"
}
return $out
}
# =============================================================================
# 0. Validari locale
# =============================================================================
Write-Step "Verificari initiale"
$SetupDir = Join-Path (Split-Path $PSScriptRoot -Parent) 'roa-windows-setup'
if (-not (Test-Path $SetupDir -PathType Container)) {
throw "Nu gasesc directorul cu scripturile de instalare: $SetupDir"
}
$InstallCmd = Join-Path $SetupDir 'INSTALEAZA.cmd'
if (-not (Test-Path $InstallCmd)) {
throw "Nu gasesc $InstallCmd - kitul ar fi inutilizabil fara el."
}
Write-Ok "Scripturi de instalare: $SetupDir"
foreach ($tool in @('ssh', 'scp', 'tar')) {
if (-not (Get-Command $tool -ErrorAction SilentlyContinue)) {
throw "Lipseste '$tool' din PATH. Pe Windows 10+ vine cu OpenSSH / bsdtar."
}
}
Write-Ok "ssh, scp, tar disponibile"
if (-not $KitName) {
$KitName = "roa-kit-$(Get-Date -Format 'yyyyMMdd_HHmmss')"
}
$KitDir = Join-Path $OutputDir $KitName
if (Test-Path $KitDir) {
throw "Directorul $KitDir exista deja. Sterge-l sau foloseste alt -KitName."
}
$Staging = Join-Path ([System.IO.Path]::GetTempPath()) "roa-kit-staging-$([System.Guid]::NewGuid().ToString('N').Substring(0,8))"
New-Item -ItemType Directory -Path $Staging -Force | Out-Null
try {
# =========================================================================
# 1. Starea LXC 108 si a containerului Oracle
# =========================================================================
Write-Step "Starea LXC $Ctid si a containerului $Container"
$lxcStatus = Invoke-Remote "pct status $Ctid"
if ($lxcStatus -notmatch 'running') {
throw "LXC $Ctid nu ruleaza ($lxcStatus). Porneste-l cu: ssh $ProxmoxHost `"pct start $Ctid`""
}
Write-Ok "LXC $Ctid ruleaza"
$running = Invoke-Remote "pct exec $Ctid -- docker ps --format '{{.Names}}'"
if ($running -notcontains $Container) {
throw "Containerul $Container nu ruleaza in LXC $Ctid. Porneste-l cu: ssh $ProxmoxHost `"pct exec $Ctid -- docker start $Container`" si asteapta ~60s."
}
Write-Ok "Containerul $Container ruleaza"
Write-Info "Sursa exportului: $SourceLabel"
# =========================================================================
# 2. Exportul
# =========================================================================
if ($SkipExport) {
Write-Step "Export sarit (-SkipExport) - folosesc arhivele existente"
}
else {
Write-Step "Export din $SourceLabel"
Write-Info "Dureaza cateva minute. Ruleaza export-roa2.sh pe LXC $Ctid..."
$exportOut = Invoke-Remote "pct exec $Ctid -- bash /opt/oracle/oradata/export-roa2.sh $OracleVersion"
$exportOut | Where-Object { $_ -match 'EROARE|ORA-|successfully completed|elapsed' } |
ForEach-Object { Write-Info $_ }
Write-Ok "Export terminat"
}
# =========================================================================
# 3. Aducerea arhivelor: container -> LXC -> host -> statie
# =========================================================================
Write-Step "Aduc arhivele pe statia locala"
$archives = @{}
foreach ($schema in $SchemaMap.Keys) {
$newest = (Invoke-Remote "pct exec $Ctid -- bash -c 'ls -t $RemoteDmpDir/${schema}*.tar.gz 2>/dev/null | head -1'" | Select-Object -First 1)
if ([string]::IsNullOrWhiteSpace($newest)) {
throw "Nu am gasit nicio arhiva ${schema}*.tar.gz in $RemoteDmpDir. Ruleaza fara -SkipExport."
}
$newest = $newest.Trim()
$leaf = Split-Path $newest -Leaf
# pct pull scoate fisierul din LXC pe host-ul Proxmox; scp il aduce mai
# departe. Fara pct pull, scp ar citi de pe host, unde nu exista nimic.
Invoke-Remote "pct pull $Ctid $newest /tmp/$leaf" | Out-Null
& scp -o BatchMode=yes "${ProxmoxHost}:/tmp/$leaf" (Join-Path $Staging $leaf) | Out-Null
if ($LASTEXITCODE -ne 0) { throw "scp a esuat pentru $leaf" }
Invoke-Remote "rm -f /tmp/$leaf" -AllowFailure | Out-Null
$localArchive = Join-Path $Staging $leaf
$sizeMb = [math]::Round((Get-Item $localArchive).Length / 1MB, 1)
Write-Ok "$leaf ($sizeMb MB)"
$archives[$schema] = $localArchive
}
# =========================================================================
# 4. Despachetare, redenumire, verificarea continutului
# =========================================================================
Write-Step "Despachetez si verific"
$KitDmpDir = Join-Path $KitDir 'DMPDIR'
New-Item -ItemType Directory -Path $KitDmpDir -Force | Out-Null
$nomFirmeRows = $null
foreach ($schema in $SchemaMap.Keys) {
$extractDir = Join-Path $Staging "x_$schema"
New-Item -ItemType Directory -Path $extractDir -Force | Out-Null
& tar -xzf $archives[$schema] -C $extractDir | Out-Null
if ($LASTEXITCODE -ne 0) { throw "tar a esuat la despachetarea $($archives[$schema])" }
$dmp = Get-ChildItem -Path $extractDir -Filter '*.dmp' -File | Select-Object -First 1
if (-not $dmp) { throw "Arhiva pentru $schema nu contine niciun fisier .dmp" }
$target = Join-Path $KitDmpDir $SchemaMap[$schema]
Copy-Item -Path $dmp.FullName -Destination $target -Force
$sizeMb = [math]::Round((Get-Item $target).Length / 1MB, 1)
Write-Ok "$($SchemaMap[$schema]) ($sizeMb MB)"
# Logul expdp spune exact cate randuri are fiecare tabela. Pentru
# CONTAFIN_ORACLE, NOM_FIRME e indicatorul care separa un sablon curat
# de datele unui client.
$log = Get-ChildItem -Path $extractDir -Filter '*.log' -File | Select-Object -First 1
if ($log -and $schema -eq 'CONTAFIN_ORACLE') {
$m = Select-String -Path $log.FullName -Pattern '"NOM_FIRME"\s+\S+\s+\S+\s+(\d+)\s+rows' |
Select-Object -First 1
if ($m) { $nomFirmeRows = [int]$m.Matches[0].Groups[1].Value }
}
}
if ($null -eq $nomFirmeRows) {
Write-Warn "Nu am putut citi numarul de firme din logul expdp. Verifica manual continutul lui NOM_FIRME."
}
elseif ($nomFirmeRows -le 5) {
Write-Ok "NOM_FIRME: $nomFirmeRows randuri - sablon curat, potrivit pentru instalare pe curat"
}
else {
Write-Warn "NOM_FIRME are $nomFirmeRows randuri - NU e un sablon curat!"
Write-Warn "Dump-ul contine firmele unei baze reale. Nu-l trimite altui client."
}
# =========================================================================
# 5. Scripturile de instalare
# =========================================================================
Write-Step "Copiez scripturile de instalare"
# Kitul are structura lui roa-windows-setup direct in radacina, plus DMPDIR:
# Run.cmd si INSTALEAZA.cmd se bazeaza pe %~dp0scripts, deci trebuie sa fie
# la acelasi nivel cu scripts\.
$excludeNames = @('logs', 'test')
# Gunoiul din working tree nu are ce cauta la client. *.dmp e exclus ca
# masura de siguranta: daca cineva a lasat dump-ul unui client in
# roa-windows-setup, nu vrem sa plece mai departe.
$excludePatterns = @('*.stackdump', '*.zip', '*.dmp', '*.log', 'config.ps1')
Get-ChildItem -Path $SetupDir -Force | Where-Object {
$item = $_
($excludeNames -notcontains $item.Name) -and
(-not ($excludePatterns | Where-Object { $item.Name -like $_ }))
} | ForEach-Object {
Copy-Item -Path $_.FullName -Destination $KitDir -Recurse -Force
}
# Acelasi filtru, aplicat si in subdirectoare (config.ps1 poate contine
# parolele altui client, logurile pot contine nume de firme).
# DMPDIR e scutit: acolo tocmai am pus sabloanele, iar '*.dmp' e in lista de
# excluderi. Fara filtrul asta, pasul de curatare isi sterge propriul rezultat.
foreach ($pat in $excludePatterns) {
Get-ChildItem -Path $KitDir -Filter $pat -Recurse -File -Force -ErrorAction SilentlyContinue |
Where-Object { $_.DirectoryName -ne $KitDmpDir } |
Remove-Item -Force -ErrorAction SilentlyContinue
}
New-Item -ItemType Directory -Path (Join-Path $KitDir 'logs') -Force | Out-Null
# Plasa de siguranta: in kit au voie exact cele doua sabloane, nimic altceva.
$strayDmp = Get-ChildItem -Path $KitDir -Filter '*.dmp' -Recurse -File |
Where-Object { $_.DirectoryName -ne $KitDmpDir }
if ($strayDmp) {
throw "Fisiere .dmp neasteptate in kit, in afara DMPDIR: $($strayDmp.FullName -join ', ')"
}
# ...si invers: sabloanele TREBUIE sa fie acolo. Verificarea asta prinde
# orice pas ulterior care le-ar sterge din greseala.
foreach ($expected in $SchemaMap.Values) {
$p = Join-Path $KitDmpDir $expected
if (-not (Test-Path $p)) {
throw "Lipseste $expected din kit - constructia e incompleta."
}
if ((Get-Item $p).Length -lt 1MB) {
throw "$expected are sub 1 MB - fisier trunchiat sau gresit."
}
}
$copied = (Get-ChildItem -Path $KitDir -Recurse -File).Count
Write-Ok "$copied fisiere in kit"
# =========================================================================
# 6. Instructiunile
# =========================================================================
$sursa = if ($SkipExport) { "$SourceLabel (arhive existente, neexportate acum)" } else { $SourceLabel }
$firme = if ($null -ne $nomFirmeRows) { "$nomFirmeRows" } else { "necunoscut" }
$citeste = @"
KIT DE INSTALARE ROA ORACLE
===========================
Construit: $(Get-Date -Format 'yyyy-MM-dd HH:mm')
Sursa DMP-urilor: $sursa
Firme in NOM_FIRME din contafin_oracle.dmp: $firme
CE E IN KIT
-----------
DMPDIR\contafin_oracle.dmp schema comuna (drepturi, actualizari, NOM_FIRME)
DMPDIR\FIRMANOUA.dmp sablon de firma goala
scripts\, sql\, config\ scripturile de instalare
backup\ backupora.exe + sablonul de setari.ini
FAZA1.cmd / FAZA2.cmd instalare in doua faze (recomandat la migrari)
INSTALEAZA.cmd instalare intr-o singura rulare
README.md documentatia completa
CUM SE INSTALEAZA
-----------------
Inainte de orice: instaleaza Oracle 21c XE pe server (docs\00-INSTALL-ORACLE-XE.md),
cu parola SYS/SYSTEM 'romfastsoft'. Apoi, din radacina kitului:
A. MIGRARE IN DOUA FAZE (recomandat: downtime de minute, nu de ore)
Ideea: pe CONTAFIN_ORACLE nu se lucreaza activ, deci poate fi exportat
de pe serverul vechi inca din timpul programului. Doar schemele de firma
se schimba sub picioare. Deci tot ce nu depinde de firme se face ziua,
iar seara raman doar firmele.
FAZA 1 - in timpul programului, utilizatorii lucreaza mai departe
pe serverul vechi:
1. Exporta DOAR contafin_oracle de pe serverul vechi si pune-l in
DMPDIR\ peste sablon.
2. FAZA1.cmd <IdClient> /vechi:<gazda-veche> /parolavechi:<parola-SYS>
Se instaleaza baza, obiectele SYS, contafin-ul clientului,
sinonimele, LICENTELE de pe serverul vechi, directoarele ROAUPDATE,
publicarea in IIS si exportul zilnic. La final, verificarea va
spune "N company schemas are missing" - e NORMAL, firmele vin abia
in faza 2.
FAZA 2 - dupa ce utilizatorii au terminat:
1. Exporta schemele de firma de pe serverul vechi.
2. Pune DMP-urile in C:\DMPDIR (sau da /dmp:<cale>).
3. FAZA2.cmd <IdClient>
Daca intre faze s-a adaugat o firma noua sau s-au modificat
nomenclatoare pe serverul vechi, adauga /reimportcontafin -
altfel NOM_FIRME ramane in urma.
B. CLIENT NOU, fara firme anterioare (o singura rulare)
INSTALEAZA.cmd curat <IdClient> [ServerSMTP]
Prima firma se creeaza DUPA aceea, DIN APLICATIA ROA - nu din scripturi.
Aplicatia scrie firma in CONTAFIN_ORACLE.NOM_FIRME si apeleaza
SYS.NEWSCHEMA, care ii importa structura din FIRMANOUA.dmp.
Sau, echivalent si cu IIS + export zilnic incluse:
FAZA1.cmd <IdClient> /faralicente
C. MIGRARE INTR-O SINGURA RULARE (cand downtime-ul nu conteaza)
1. Inlocuieste DMPDIR\contafin_oracle.dmp cu cel exportat de la client
(cel din kit are NOM_FIRME goala).
2. Pune in DMPDIR si cate un .dmp per firma a clientului.
3. LASA FIRMANOUA.dmp pe loc - nu vine de la client, dar e obligatoriu.
4. INSTALEAZA.cmd migrare <IdClient> [ServerSMTP]
Atentie: INSTALEAZA.cmd NU copiaza licentele, NU publica in IIS si NU
configureaza exportul zilnic. Ruleaza dupa el, manual:
Run.cmd 09-copy-licenses.ps1 -OldHost <gazda> -OldSysPassword <parola> -Replace
Run.cmd 10-setup-roaupdate-iis.ps1
Run.cmd 11-setup-backup-export.ps1
Instalarea e gata cand ultimul pas (07-verify-installation) iese cu codul 0.
DE STIUT
--------
- FIRMANOUA.dmp ramane in C:\DMPDIR si dupa instalare. E folosit la fiecare
firma noua, oricand pe viitor. Daca il stergi, adaugarea de firme va esua
cu o eroare in DBMS_DATAPUMP.ADD_FILE.
- Pasul 03 (import contafin) iese cu codul 5 si asta e NORMAL: e codul Data
Pump pentru "completed with errors", din ORA-31684 (userul exista deja,
creat de pasul 01) si ORA-39082 (warning-uri de compilare, rezolvate la
pasul 04). INSTALEAZA.cmd il trateaza corect.
- Conectarea se face la serviciul XEPDB1, niciodata la XE (CDB root).
- Daca reiei instalarea de la zero: scripts\99-uninstall-roa.ps1 trebuie sa
iasa cu codul 0. Orice alt cod inseamna ca au ramas obiecte in baza (uzual
o sesiune activa tinea userul ocupat) - nu porni instalarea peste ele.
- Logurile fiecarui pas sunt in logs\.
- LICENTELE nu vin din DMP-uri. Stau in SYS.AUTH_SERII pe serverul vechi si
se copiaza separat, cu 09-copy-licenses.ps1. Daca sari peste pasul asta,
aplicatia porneste dar refuza fiecare modul. FAZA1.cmd il ruleaza singur
daca ii dai /vechi si /parolavechi.
- Fara backup\backupora.exe in kit, pasul 11 (exportul zilnic) se opreste
cu eroare. Restul instalarii nu depinde de el.
- setari.ini pentru backupora.exe se genereaza din backup\setari.ini.template
prin inlocuirea marcajelor {{...}}. Daca formatul difera de ce asteapta
executabilul, corecteaza SABLONUL, nu scriptul.
"@
Set-Content -Path (Join-Path $KitDir 'CITESTE-MA.txt') -Value $citeste -Encoding UTF8
# =========================================================================
# 7. Arhivare si livrare
# =========================================================================
$zipPath = $null
if ($Zip) {
Write-Step "Creez arhiva .zip"
$zipPath = Join-Path $OutputDir "$KitName.zip"
if (Test-Path $zipPath) { Remove-Item $zipPath -Force }
Compress-Archive -Path (Join-Path $KitDir '*') -DestinationPath $zipPath -CompressionLevel Optimal
$zipMb = [math]::Round((Get-Item $zipPath).Length / 1MB, 1)
Write-Ok "$zipPath ($zipMb MB)"
}
if ($PushTo) {
Write-Step "Trimit kitul catre $PushTo"
& scp -o BatchMode=yes -r $KitDir $PushTo | Out-Null
if ($LASTEXITCODE -ne 0) { throw "scp catre $PushTo a esuat (cod $LASTEXITCODE)" }
Write-Ok "Kit trimis"
}
# =========================================================================
# 8. Sumar
# =========================================================================
$totalMb = [math]::Round(((Get-ChildItem -Path $KitDir -Recurse -File | Measure-Object Length -Sum).Sum) / 1MB, 1)
Write-Host ""
Write-Host "============================================================" -ForegroundColor Green
Write-Host " KIT GATA" -ForegroundColor Green
Write-Host "============================================================" -ForegroundColor Green
Write-Host ""
Write-Host " Director: $KitDir"
if ($zipPath) { Write-Host " Arhiva: $zipPath" }
Write-Host " Marime: $totalMb MB"
Write-Host " NOM_FIRME: $firme randuri in contafin_oracle.dmp"
Write-Host ""
Write-Host " La client, din radacina kitului:"
Write-Host " FAZA1.cmd <IdClient> /vechi:<gazda> /parolavechi:<parola>" -ForegroundColor Cyan
Write-Host " FAZA2.cmd <IdClient> (dupa program)" -ForegroundColor Cyan
Write-Host " INSTALEAZA.cmd curat <IdClient> (client nou, o rulare)" -ForegroundColor Cyan
Write-Host ""
if (-not (Test-Path (Join-Path $KitDir 'backup\backupora.exe'))) {
Write-Warn "backup\backupora.exe lipseste din kit - pasul 11 (export zilnic) va esua la client."
Write-Host ""
}
Write-Host " Detalii complete: $KitDir\CITESTE-MA.txt"
Write-Host ""
}
catch {
Write-Host ""
Write-Host "[EROARE] $_" -ForegroundColor Red
if (Test-Path $KitDir) {
Write-Host "[INFO] Kit incomplet la $KitDir - sterge-l inainte de a relua." -ForegroundColor Yellow
}
exit 1
}
finally {
if (Test-Path $Staging) {
Remove-Item -Path $Staging -Recurse -Force -ErrorAction SilentlyContinue
}
}