Add-Type -AssemblyName PresentationFramework, PresentationCore, System.Windows.Forms
Add-Type -AssemblyName ReachFramework
# ================= CONFIG =================
$baseDir = 'C:\Users\walid\Desktop\RECO3'
$archiveDir = Join-Path $baseDir "Archive"
$backupDir = Join-Path $baseDir "Backup"
$logFile = Join-Path $baseDir "audit_agence.log"
$fileToday = Join-Path $baseDir "recette_$((Get-Date).ToString('yyyyMMdd')).csv"
foreach ($p in @($archiveDir,$backupDir)) {
if (-not (Test-Path $p)) { New-Item $p -ItemType Directory | Out-Null }
}
function Log-Audit($msg) {
"$(Get-Date -Format 'dd/MM/yyyy HH:mm:ss') | $msg" | Add-Content $logFile
}
function Parse-Double($txt) {
try { return [double]::Parse($txt.Replace(",", "."), [System.Globalization.CultureInfo]::InvariantCulture) }
catch { return $null }
}
# ================= LOAD XAML =================
try {
[xml]$xaml = Get-Content (Join-Path $baseDir "INTERFACE.XAML") -Raw
$Window = [Windows.Markup.XamlReader]::Load((New-Object System.Xml.XmlNodeReader $xaml))
} catch {
[System.Windows.MessageBox]::Show("Impossible de charger INTERFACE.XAML")
exit
}
# ================= BIND CONTROLS =================
$names = @(
"AssureBox","PoliceBox","QuittanceBox","PrimeBox","DateEffetBox","DateEcheanceBox",
"PaiementBox","TypeBox","RecetteGrid","AddBtn","EditBtn","DeleteBtn","CloseBtn",
"SearchBtn","ReportBtn","TxtTotalTheorique","TxtCaisseManuelle","TxtDesequilibre"
)
foreach ($n in $names) { Set-Variable $n ($Window.FindName($n)) }
# ================= DATA =================
$script:masterData = New-Object System.Collections.ObjectModel.ObservableCollection[PSObject]
$RecetteGrid.ItemsSource = $script:masterData
# ================= COMPTA =================
function Update-Compta([System.Collections.IEnumerable]$data = $null) {
if ($data -eq $null) { $data = $script:masterData }
$total = 0
if ($data -and $data.Count -gt 0) {
$total = ($data | ForEach-Object {
$p = 0
if ([double]::TryParse($_.Prime, [ref]$p)) { $p } else { 0 }
} | Measure-Object -Sum).Sum
}
$reel = 0
if ($TxtCaisseManuelle -and -not [string]::IsNullOrWhiteSpace($TxtCaisseManuelle.Text)) {
$parsed = Parse-Double $TxtCaisseManuelle.Text
if ($parsed -ne $null) { $reel = $parsed }
}
$diff = $reel - $total
if ($TxtTotalTheorique) { $TxtTotalTheorique.Text = "{0:N0}" -f $total }
if ($TxtDesequilibre) {
$TxtDesequilibre.Text = "{0:N0}" -f $diff
if ([math]::Abs($diff) -lt 0.01) {
$TxtDesequilibre.Foreground = "#22C55E"
} elseif ($diff -lt 0) {
$TxtDesequilibre.Foreground = "#EF4444"
} else {
$TxtDesequilibre.Foreground = "#F59E0B"
}
}
}
# ================= CSV =================
function Sync-Csv {
try {
$script:masterData | Export-Csv $fileToday -Delimiter ";" -NoTypeInformation -Encoding UTF8
if ($script:masterData.Count % 10 -eq 0) {
Copy-Item $fileToday (Join-Path $backupDir "auto_$((Get-Date).ToString('yyyyMMddHHmmss')).csv") -Force
}
} catch {
[System.Windows.MessageBox]::Show("Erreur d'écriture CSV")
}
Update-Compta
}
# ================= VALIDATION =================
function Test-Validation {
foreach ($b in @($AssureBox,$PoliceBox,$QuittanceBox,$PrimeBox)) {
if ([string]::IsNullOrWhiteSpace($b.Text)) { $b.BorderBrush = "Red"; return $false }
$b.BorderBrush = "#CBD5E0"
}
if ($script:masterData | Where-Object {
$_.Police -eq $PoliceBox.Text -and $_.Quittance -eq $QuittanceBox.Text
}) {
[System.Windows.MessageBox]::Show("Doublon Police / Quittance")
return $false
}
if (-not (Parse-Double $PrimeBox.Text)) {
[System.Windows.MessageBox]::Show("Prime invalide")
return $false
}
return $true
}
# ================= RECHERCHE =================
$SearchBtn.Add_Click({
try {
$files = Get-ChildItem $archiveDir -Filter "recette_*.csv" -ErrorAction SilentlyContinue
if (-not $files) { return }
$all = foreach ($f in $files) {
Import-Csv $f.FullName -Delimiter ";" -Encoding UTF8 |
ForEach-Object { $_ | Add-Member -NotePropertyName SourceFile -NotePropertyValue $f.BaseName -Force -PassThru }
}
# Filtrage ultra safe SANS date
$filtered = $all | Where-Object {
$txtOK = $true
$typeOK = $true
$payOK = $true
if (-not [string]::IsNullOrWhiteSpace($AssureBox.Text)) {
$txt = $AssureBox.Text.ToUpper()
$txtOK = ($_.Assure.ToUpper() -like "*$txt*" -or $_.Police.ToUpper() -like "*$txt*" -or $_.Quittance.ToUpper() -like "*$txt*")
}
if ($TypeBox.Text -and $TypeBox.SelectedIndex -ge 0) {
$typeOK = $_.Type.ToUpper() -like "*$($TypeBox.Text.ToUpper())*"
}
if ($PaiementBox.Text -and $PaiementBox.SelectedIndex -ge 0) {
$payOK = $_.ModalitePaiement.ToUpper() -like "*$($PaiementBox.Text.ToUpper())*"
}
$txtOK -and $typeOK -and $payOK
}
# ItemsSource ObservableCollection
$RecetteGrid.ItemsSource = New-Object System.Collections.ObjectModel.ObservableCollection[PSObject] (@($filtered))
# Statistiques
Update-Compta $filtered
} catch {
[System.Windows.MessageBox]::Show("Erreur lors du filtrage : $($_.Exception.Message)")
}
})
# ================= GRID SELECTION =================
$RecetteGrid.Add_SelectionChanged({
$has = $RecetteGrid.SelectedItem -ne $null
$EditBtn.IsEnabled = $has
$DeleteBtn.IsEnabled = $has
if ($has) {
$s = $RecetteGrid.SelectedItem
$AssureBox.Text = $s.Assure
$PoliceBox.Text = $s.Police
$QuittanceBox.Text = $s.Quittance
$PrimeBox.Text = $s.Prime
$PaiementBox.Text = $s.ModalitePaiement
$TypeBox.Text = $s.Type
}
})
# ================= ADD =================
$AddBtn.Add_Click({
if (-not (Test-Validation)) { return }
$entry = [PSCustomObject]@{
Assure = $AssureBox.Text.ToUpper()
Police = $PoliceBox.Text
Quittance = $QuittanceBox.Text
Prime = Parse-Double $PrimeBox.Text
DateEffet = $DateEffetBox.SelectedDate
DateEcheance = $DateEcheanceBox.SelectedDate
ModalitePaiement = $PaiementBox.Text
Type = $TypeBox.Text
}
$script:masterData.Add($entry)
Sync-Csv
Log-Audit "AJOUT $($entry.Police)"
$AssureBox.Text = $PoliceBox.Text = $QuittanceBox.Text = $PrimeBox.Text = ""
})
# ================= EDIT =================
$EditBtn.Add_Click({
if ($RecetteGrid.SelectedIndex -lt 0) { return }
if (-not (Test-Validation)) { return }
$i = $RecetteGrid.SelectedIndex
$item = $script:masterData[$i]
$item.Assure = $AssureBox.Text.ToUpper()
$item.Police = $PoliceBox.Text
$item.Quittance = $QuittanceBox.Text
$item.Prime = Parse-Double $PrimeBox.Text
$item.ModalitePaiement = $PaiementBox.Text
$item.Type = $TypeBox.Text
Sync-Csv
$RecetteGrid.Items.Refresh()
Log-Audit "MODIF $($item.Police)"
})
# ================= DELETE =================
$DeleteBtn.Add_Click({
if ($RecetteGrid.SelectedItem) {
$r = [System.Windows.MessageBox]::Show("Supprimer ?", "Confirmation","YesNo","Warning")
if ($r -eq "Yes") {
Log-Audit "SUPPR $($RecetteGrid.SelectedItem.Police)"
$script:masterData.RemoveAt($RecetteGrid.SelectedIndex)
Sync-Csv
}
}
})
# ================= CLOSE =================
$CloseBtn.Add_Click({
if ($script:masterData.Count -gt 0) {
$name = "recette_$((Get-Date).ToString('yyyyMMdd_HHmmss')).csv"
Move-Item $fileToday (Join-Path $archiveDir $name) -Force
Log-Audit "CLOTURE"
}
$Window.Close()
})
$TxtCaisseManuelle.Add_TextChanged({ Update-Compta })
# ================= LOAD DATA =================
if (Test-Path $fileToday) {
Import-Csv $fileToday -Delimiter ";" | ForEach-Object { $script:masterData.Add($_) }
}
Update-Compta
$Window.ShowDialog() | Out-Null