Simplify finance dashboard overview

This commit is contained in:
2026-06-04 10:42:11 +02:00
parent 37a175551b
commit 40805e0222
6 changed files with 815 additions and 1 deletions
@@ -48,7 +48,123 @@
@if (_financeResult is not null)
{
<MudTabs Elevation="1" Rounded="false" PanelClass="pt-4" @bind-ActivePanelIndex="_activeFinanceTabIndex">
<MudTabs Elevation="1" Rounded="false" PanelClass="pt-4" @bind-ActivePanelIndex="_activeOverviewTabIndex">
<MudTabPanel Text="@T("Schnelluebersicht", "Quick overview")" Icon="@Icons.Material.Filled.Speed">
<MudGrid Class="mb-4">
<MudItem xs="12" sm="6" md="3">
<MudPaper Class="pa-4" Elevation="1">
<MudText Typo="Typo.caption">@T("Net Sales Actual", "Net sales actual")</MudText>
<MudText Typo="Typo.h5">@FormatValue(_financeResult.NetSalesActual, _financeResult.DisplayCurrency)</MudText>
<MudText Typo="Typo.body2">@($"{_financeResult.Filter.Year}")</MudText>
</MudPaper>
</MudItem>
<MudItem xs="12" sm="6" md="3">
<MudPaper Class="pa-4" Elevation="1">
<MudText Typo="Typo.caption">@T("Laender OK", "Countries OK")</MudText>
<MudText Typo="Typo.h5">@_financeResult.CountryRows.Count(row => row.Status == "OK").ToString("N0")</MudText>
<MudText Typo="Typo.body2">@T("Soll/Ist ohne Abweichung", "Actual/reference without deviation")</MudText>
</MudPaper>
</MudItem>
<MudItem xs="12" sm="6" md="3">
<MudPaper Class="pa-4" Elevation="1">
<MudText Typo="Typo.caption">@T("Zu pruefen", "To check")</MudText>
<MudText Typo="Typo.h5">@_financeResult.CountryRows.Count(row => row.Status == "Pruefen").ToString("N0")</MudText>
<MudText Typo="Typo.body2">@T("Abweichung oder offene Regel", "Deviation or open rule")</MudText>
</MudPaper>
</MudItem>
<MudItem xs="12" sm="6" md="3">
<MudPaper Class="pa-4" Elevation="1">
<MudText Typo="Typo.caption">@T("Datenstandorte", "Data sites")</MudText>
<MudText Typo="Typo.h5">@_financeResult.DataStatusRows.Count(row => row.IsActive).ToString("N0")</MudText>
<MudText Typo="Typo.body2">@T("aktive Quellen", "active sources")</MudText>
</MudPaper>
</MudItem>
</MudGrid>
<MudTabs Elevation="0" Rounded="false" PanelClass="pt-4">
<MudTabPanel Text="@T("Freigabe", "Approval")" Icon="@Icons.Material.Filled.FactCheck">
<MudPaper Class="pa-4" Elevation="1">
<MudText Typo="Typo.h6" Class="mb-2">@T("Finance-Freigabe je Land", "Finance approval by country")</MudText>
<MudTable Items="_financeResult.CountryRows" Dense Hover Striped>
<HeaderContent>
<MudTh>@T("Status", "Status")</MudTh>
<MudTh>@T("Land", "Country")</MudTh>
<MudTh>@T("Ist", "Actual")</MudTh>
<MudTh>@T("Soll", "Reference")</MudTh>
<MudTh>@T("Differenz", "Difference")</MudTh>
<MudTh>@T("Datenstand", "Data status")</MudTh>
<MudTh>@T("Hinweis", "Note")</MudTh>
</HeaderContent>
<RowTemplate>
<MudTd><MudChip T="string" Size="Size.Small" Color="@StatusColor(context.Status)" Variant="Variant.Filled">@context.Status</MudChip></MudTd>
<MudTd>@FormatCountryWithFlag(context.CountryKey)</MudTd>
<MudTd>@FormatValue(context.NetSalesActual, context.Currency)</MudTd>
<MudTd>@FormatNullableValue(context.ReferenceValue, context.Currency)</MudTd>
<MudTd>@FormatNullableValue(context.Difference, context.Currency)</MudTd>
<MudTd>@BuildDataStatusText(context)</MudTd>
<MudTd>@BuildQuickFinanceNote(context)</MudTd>
</RowTemplate>
</MudTable>
</MudPaper>
</MudTabPanel>
<MudTabPanel Text="@T("Datenstand", "Data status")" Icon="@Icons.Material.Filled.Storage">
<MudPaper Class="pa-4" Elevation="1">
<MudText Typo="Typo.h6" Class="mb-2">@T("Letzter Datenstand je Standort", "Latest data status by site")</MudText>
<MudTable Items="_financeResult.DataStatusRows" Dense Hover Striped>
<HeaderContent>
<MudTh>@T("Aktiv", "Active")</MudTh>
<MudTh>@T("Land", "Country")</MudTh>
<MudTh>TSC</MudTh>
<MudTh>@T("Quelle", "Source")</MudTh>
<MudTh>@T("Zentrale Zeilen", "Central rows")</MudTh>
<MudTh>@T("Letzter Export", "Latest export")</MudTh>
<MudTh>@T("Status", "Status")</MudTh>
<MudTh>@T("Manual Import", "Manual import")</MudTh>
</HeaderContent>
<RowTemplate>
<MudTd>
<MudIcon Icon="@(context.IsActive ? Icons.Material.Filled.CheckCircle : Icons.Material.Filled.Cancel)"
Color="@(context.IsActive ? Color.Success : Color.Default)" Size="Size.Small" />
</MudTd>
<MudTd>@context.Land</MudTd>
<MudTd>@context.Tsc</MudTd>
<MudTd>@context.SourceSystem</MudTd>
<MudTd>@context.RowCount.ToString("N0")</MudTd>
<MudTd>@FormatDateTime(context.LatestExportAt)</MudTd>
<MudTd>@(string.IsNullOrWhiteSpace(context.LatestExportStatus) ? "-" : context.LatestExportStatus)</MudTd>
<MudTd>@FormatManualImportStatus(context)</MudTd>
</RowTemplate>
</MudTable>
</MudPaper>
</MudTabPanel>
<MudTabPanel Text="@T("Sparten", "Divisions")" Icon="@Icons.Material.Filled.AccountTree">
<MudPaper Class="pa-4" Elevation="1">
<MudText Typo="Typo.h6" Class="mb-2">@T("Sparten-Abdeckung nach Land", "Division coverage by country")</MudText>
<MudTable Items="_financeResult.ProductFinanceCountryRows" Dense Hover Striped>
<HeaderContent>
<MudTh>@T("Land", "Country")</MudTh>
<MudTh>TSC</MudTh>
<MudTh>@T("Gesamtumsatz", "Total sales")</MudTh>
<MudTh>@T("Zugeordnet", "Assigned")</MudTh>
<MudTh>@T("Nicht im Stamm", "Not in master")</MudTh>
<MudTh>@T("Abdeckung", "Coverage")</MudTh>
</HeaderContent>
<RowTemplate>
<MudTd>@FormatCountryWithFlag(context.CountryKey)</MudTd>
<MudTd>@context.Tsc</MudTd>
<MudTd>@FormatValue(context.TotalValue, context.Currency)</MudTd>
<MudTd>@FormatValue(context.AssignedValue, context.Currency)</MudTd>
<MudTd>@FormatValue(context.MissingReferenceValue, context.Currency)</MudTd>
<MudTd>@FormatPercent(context.AssignedValuePercent)</MudTd>
</RowTemplate>
</MudTable>
</MudPaper>
</MudTabPanel>
</MudTabs>
</MudTabPanel>
<MudTabPanel Text="@T("Experten", "Experts")" Icon="@Icons.Material.Filled.Tune">
<MudTabs Elevation="0" Rounded="false" PanelClass="pt-4" @bind-ActivePanelIndex="_activeFinanceTabIndex">
<MudTabPanel Text="@T("Finance Summary", "Finance summary")" Icon="@Icons.Material.Filled.Dashboard">
<MudGrid Class="mb-4">
<MudItem xs="12" sm="6" md="3">
@@ -867,6 +983,8 @@
</MudTabPanel>
</MudTabs>
</MudTabPanel>
</MudTabs>
}
@code {
@@ -917,6 +1035,7 @@
private bool _analyzing;
private bool _analyzingCentral;
private bool _analyzingFinance;
private int _activeOverviewTabIndex;
private int _activeFinanceTabIndex;
private int _activeDivisionTabIndex;
private string _productFinanceGroupLevel = ProductFinanceGroupLevels.Hierarchy;
@@ -928,6 +1047,8 @@
protected override void OnParametersSet()
{
_activeOverviewTabIndex = string.IsNullOrWhiteSpace(Section) ? 0 : 1;
if (string.Equals(Section, "division", StringComparison.OrdinalIgnoreCase))
{
_activeFinanceTabIndex = ManagementFinanceTabIndexes.Division;
@@ -1193,6 +1314,41 @@
return "kein Pfad";
}
private string BuildDataStatusText(ManagementFinanceCountryStatusRow countryRow)
{
if (_financeResult is null)
return "-";
var tscs = countryRow.Tscs
.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
.ToHashSet(StringComparer.OrdinalIgnoreCase);
var matchingRows = _financeResult.DataStatusRows
.Where(row => row.Land.Equals(countryRow.CountryKey, StringComparison.OrdinalIgnoreCase) ||
tscs.Contains(row.Tsc))
.OrderByDescending(row => row.LatestExportAt ?? row.LatestStoredAtUtc ?? DateTime.MinValue)
.ToList();
var latest = matchingRows.FirstOrDefault();
if (latest is null)
return "-";
var date = latest.LatestExportAt ?? latest.LatestStoredAtUtc;
var status = string.IsNullOrWhiteSpace(latest.LatestExportStatus) ? latest.SourceSystem : latest.LatestExportStatus;
return $"{status} / {FormatDateTime(date)}";
}
private string BuildQuickFinanceNote(ManagementFinanceCountryStatusRow row)
{
if (!row.ReferenceValue.HasValue)
return T("Kein Sollwert gepflegt.", "No reference value maintained.");
if (row.Status == "OK")
return T("Freigabefaehig.", "Ready for approval.");
if (row.Difference.HasValue)
return T("Abweichung pruefen.", "Check deviation.");
return T("Pruefen.", "Check.");
}
private static Color StatusColor(string status) => status switch
{
"OK" => Color.Success,
Binary file not shown.
@@ -46,3 +46,73 @@ Default source:
If the SQL instance or database name differs:
.\Export-SageSpainSalesCsv.ps1 -ServerInstance "localhost" -Database "Sage" -ExportMode Full -Year 2025
Automatic upload to SharePoint with rclone
==========================================
Target SharePoint folder:
https://trafagag.sharepoint.com/sites/WorldwideBIPlatform/Shared%20Documents/Import/Finance/Spanien
Decoded folder path:
Shared Documents/Import/Finance/Spanien
Recommended rclone setup on the Spain Sage server:
1. Install rclone.
2. Run:
rclone config
3. Create a new remote for the SharePoint document library.
Recommended remote name:
trafag-bi
The remote should point to the document library root "Shared Documents" of:
https://trafagag.sharepoint.com/sites/WorldwideBIPlatform
Then this target path is used by the wrapper script:
trafag-bi:Import/Finance/Spanien
Test rclone:
rclone lsd trafag-bi:
rclone lsd trafag-bi:"Import/Finance"
rclone lsd trafag-bi:"Import/Finance/Spanien"
Run daily range export and upload, default window yesterday until today:
.\Run-SpainExportAndUpload.ps1
Explicit range:
.\Run-SpainExportAndUpload.ps1 -ExportMode Range -DateFilter LineRegistrationDate -FromDate "2026-06-02" -ToDate "2026-06-03"
Full export and upload:
.\Run-SpainExportAndUpload.ps1 -ExportMode Full -Year 2025
If the rclone remote has another name:
.\Run-SpainExportAndUpload.ps1 -RcloneRemote "YOUR_REMOTE_NAME"
If rclone.exe is not in PATH:
.\Run-SpainExportAndUpload.ps1 -RcloneExe "C:\Tools\rclone\rclone.exe"
Suggested Windows Task Scheduler command:
powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\Trafag\SageSpain\Run-SpainExportAndUpload.ps1
Important:
- The export script only reads SQL Server data.
- rclone only uploads the generated CSV and matching summary file.
- For daily deltas use ExportMode Range with DateFilter LineRegistrationDate.
- ToDate is exclusive.
@@ -0,0 +1,72 @@
param(
[string]$ServerInstance = "localhost",
[string]$Database = "Sage",
[ValidateSet("Full", "Range")]
[string]$ExportMode = "Range",
[ValidateSet("InvoiceDate", "LineRegistrationDate")]
[string]$DateFilter = "LineRegistrationDate",
[int]$Year = 2025,
[datetime]$FromDate = (Get-Date).Date.AddDays(-1),
[datetime]$ToDate = (Get-Date).Date,
[string]$BaseDirectory = "C:\Trafag\SageSpain",
[string]$RcloneExe = "rclone",
[string]$RcloneRemote = "trafag-bi",
[string]$RcloneTarget = "Import/Finance/Spanien"
)
$ErrorActionPreference = "Stop"
$scriptDirectory = Split-Path -Parent $MyInvocation.MyCommand.Path
$exportScript = Join-Path $scriptDirectory "Export-SageSpainSalesCsv.ps1"
if (-not (Test-Path -LiteralPath $exportScript)) {
throw "Export script not found: $exportScript"
}
$outputDirectory = Join-Path $BaseDirectory "out"
$logDirectory = Join-Path $BaseDirectory "logs"
New-Item -ItemType Directory -Force -Path $outputDirectory, $logDirectory | Out-Null
$exportArgs = @(
"-ServerInstance", $ServerInstance,
"-Database", $Database,
"-ExportMode", $ExportMode,
"-DateFilter", $DateFilter,
"-Year", $Year,
"-OutputDirectory", $outputDirectory
)
if ($ExportMode -eq "Range") {
$exportArgs += @(
"-FromDate", $FromDate.ToString("yyyy-MM-dd"),
"-ToDate", $ToDate.ToString("yyyy-MM-dd")
)
}
& $exportScript @exportArgs
if ($LASTEXITCODE -ne 0) {
throw "Spain Sage export failed with exit code $LASTEXITCODE"
}
$latestRun = Get-ChildItem -LiteralPath $outputDirectory -Directory |
Sort-Object LastWriteTime -Descending |
Select-Object -First 1
if ($null -eq $latestRun) {
throw "No export run directory found in $outputDirectory"
}
$rcloneLog = Join-Path $logDirectory ("rclone-spain-" + (Get-Date -Format "yyyyMMdd") + ".log")
$target = "${RcloneRemote}:$RcloneTarget"
& $RcloneExe copy $latestRun.FullName $target `
--include "*.csv" `
--include "*_summary.txt" `
--log-file $rcloneLog `
--log-level INFO
if ($LASTEXITCODE -ne 0) {
throw "rclone upload failed with exit code $LASTEXITCODE"
}
Write-Host "Spain export and upload finished."
Write-Host "Local export: $($latestRun.FullName)"
Write-Host "SharePoint target: $target"
Write-Host "rclone log: $rcloneLog"
@@ -0,0 +1,258 @@
# Sage Spanien Rclone Upload Anleitung
Stand: 2026-06-03
Ziel: Der Sage-Server in Spanien erzeugt die Sales-CSV lokal und lädt die Datei danach automatisch in den SharePoint-Ordner fuer den Dashboard-Import.
## Zielordner
SharePoint URL:
```text
https://trafagag.sharepoint.com/sites/WorldwideBIPlatform/Shared%20Documents/Import/Finance/Spanien
```
Technischer Ordner:
```text
Shared Documents/Import/Finance/Spanien
```
Empfohlener rclone-Zielpfad:
```text
trafag-bi:Import/Finance/Spanien
```
Dabei zeigt `trafag-bi` auf die Dokumentbibliothek `Shared Documents` der SharePoint-Site:
```text
https://trafagag.sharepoint.com/sites/WorldwideBIPlatform
```
## Benötigte Dateien Auf Dem Spanien-Server
Empfohlener Ordner:
```text
C:\Trafag\SageSpain
```
Dateien:
```text
Export-SageSpainSalesCsv.ps1
Run-SpainExportAndUpload.ps1
```
Die Dateien liegen im Paket:
```text
SageSpainFinalExportPackage.zip
```
## rclone Installieren
Falls `winget` vorhanden ist:
```powershell
winget install Rclone.Rclone
```
Alternativ rclone ZIP manuell installieren, z.B. nach:
```text
C:\Tools\rclone\rclone.exe
```
Danach testen:
```powershell
rclone version
```
Falls `rclone` nicht im PATH ist, später den vollständigen Pfad verwenden:
```powershell
C:\Tools\rclone\rclone.exe version
```
## rclone Remote Einrichten
Auf dem Spanien-Server:
```powershell
rclone config
```
Empfohlene Eingaben:
```text
n
name> trafag-bi
Storage> onedrive
```
Danach Microsoft Login durchführen.
Wichtig:
- Site: `WorldwideBIPlatform`
- Dokumentbibliothek: `Shared Documents`
- Der rclone-Remote `trafag-bi` soll auf die Dokumentbibliothek `Shared Documents` zeigen.
## rclone Testen
```powershell
rclone lsd trafag-bi:
rclone lsd trafag-bi:"Import"
rclone lsd trafag-bi:"Import/Finance"
rclone lsd trafag-bi:"Import/Finance/Spanien"
```
Wenn der letzte Befehl den Ordner ohne Fehler zeigt, ist der Zielpfad korrekt.
## Manueller Export Ohne Upload
Full Export 2025:
```powershell
Set-ExecutionPolicy -Scope Process Bypass
cd C:\Trafag\SageSpain
.\Export-SageSpainSalesCsv.ps1 -ExportMode Full -Year 2025 -OutputDirectory C:\Trafag\SageSpain\out
```
Delta/Range Export:
```powershell
Set-ExecutionPolicy -Scope Process Bypass
cd C:\Trafag\SageSpain
.\Export-SageSpainSalesCsv.ps1 -ExportMode Range -DateFilter LineRegistrationDate -FromDate "2026-06-02" -ToDate "2026-06-03" -OutputDirectory C:\Trafag\SageSpain\out
```
Hinweis:
- `ToDate` ist exklusiv.
- Der Zeitraum `"2026-06-02"` bis `"2026-06-03"` exportiert den 2. Juni.
- Für tägliche Deltas ist `LineRegistrationDate` sinnvoll, weil neue oder geänderte Zeilen nach Registrierungsdatum kommen.
## Export Und Upload Zusammen Starten
Standard: täglicher Delta-Lauf, gestern bis heute:
```powershell
Set-ExecutionPolicy -Scope Process Bypass
cd C:\Trafag\SageSpain
.\Run-SpainExportAndUpload.ps1
```
Expliziter Zeitraum:
```powershell
.\Run-SpainExportAndUpload.ps1 -ExportMode Range -DateFilter LineRegistrationDate -FromDate "2026-06-02" -ToDate "2026-06-03"
```
Full Export mit Upload:
```powershell
.\Run-SpainExportAndUpload.ps1 -ExportMode Full -Year 2025
```
Wenn rclone nicht im PATH ist:
```powershell
.\Run-SpainExportAndUpload.ps1 -RcloneExe "C:\Tools\rclone\rclone.exe"
```
Wenn der rclone-Remote anders heisst:
```powershell
.\Run-SpainExportAndUpload.ps1 -RcloneRemote "MEIN_REMOTE_NAME"
```
## Was Wird Hochgeladen?
Das Wrapper-Script lädt aus dem neuesten Exportordner:
```text
*.csv
*_summary.txt
```
Ziel:
```text
trafag-bi:Import/Finance/Spanien
```
Das Script ändert keine Daten in Sage und keine Daten in SQL Server.
## Windows Task Scheduler
Empfohlener täglicher Lauf, z.B. 02:00 Uhr:
```powershell
$action = New-ScheduledTaskAction `
-Execute "powershell.exe" `
-Argument "-NoProfile -ExecutionPolicy Bypass -File C:\Trafag\SageSpain\Run-SpainExportAndUpload.ps1"
$trigger = New-ScheduledTaskTrigger -Daily -At 02:00
Register-ScheduledTask `
-TaskName "Trafag Spain Sage Export Upload" `
-Action $action `
-Trigger $trigger `
-Description "Exports Sage Spain sales CSV and uploads it to SharePoint via rclone"
```
Wenn rclone nicht im PATH ist:
```powershell
$action = New-ScheduledTaskAction `
-Execute "powershell.exe" `
-Argument "-NoProfile -ExecutionPolicy Bypass -File C:\Trafag\SageSpain\Run-SpainExportAndUpload.ps1 -RcloneExe C:\Tools\rclone\rclone.exe"
```
## Kontrolle Nach Dem Lauf
Lokal:
```powershell
Get-ChildItem C:\Trafag\SageSpain\out -Directory | Sort-Object LastWriteTime -Descending | Select-Object -First 1
Get-ChildItem C:\Trafag\SageSpain\logs
```
SharePoint:
```powershell
rclone ls trafag-bi:"Import/Finance/Spanien"
```
Im Browser prüfen:
```text
https://trafagag.sharepoint.com/sites/WorldwideBIPlatform/Shared%20Documents/Import/Finance/Spanien
```
## Fehlerbilder
`rclone: command not found`
- rclone ist nicht im PATH.
- Lösung: `-RcloneExe "C:\Tools\rclone\rclone.exe"` verwenden.
`directory not found`
- Remote zeigt nicht auf `Shared Documents` oder Zielordner ist anders.
- Mit `rclone lsd trafag-bi:` und `rclone lsd trafag-bi:"Import/Finance"` prüfen.
`Access denied`
- Microsoft Login oder SharePoint-Berechtigung fehlt.
- Der Windows-User des geplanten Tasks muss Zugriff auf rclone-Konfiguration und SharePoint haben.
Leere Delta-Datei:
- Zeitraum prüfen.
- `ToDate` ist exklusiv.
- Bei täglichem Lauf für gestern bis heute ist das korrekt.
@@ -0,0 +1,258 @@
# Sage Spain Rclone Upload Guide
Status: 2026-06-03
Purpose: The Sage server in Spain creates the sales CSV locally and then automatically uploads the file to the SharePoint folder used by the dashboard import.
## Target Folder
SharePoint URL:
```text
https://trafagag.sharepoint.com/sites/WorldwideBIPlatform/Shared%20Documents/Import/Finance/Spanien
```
Technical folder:
```text
Shared Documents/Import/Finance/Spanien
```
Recommended rclone target path:
```text
trafag-bi:Import/Finance/Spanien
```
The rclone remote `trafag-bi` should point to the `Shared Documents` document library of this SharePoint site:
```text
https://trafagag.sharepoint.com/sites/WorldwideBIPlatform
```
## Required Files On The Spain Server
Recommended folder:
```text
C:\Trafag\SageSpain
```
Required files:
```text
Export-SageSpainSalesCsv.ps1
Run-SpainExportAndUpload.ps1
```
The files are included in:
```text
SageSpainFinalExportPackage.zip
```
## Install rclone
If `winget` is available:
```powershell
winget install Rclone.Rclone
```
Alternatively, install the rclone ZIP manually, for example to:
```text
C:\Tools\rclone\rclone.exe
```
Test the installation:
```powershell
rclone version
```
If `rclone` is not in the PATH, use the full path later:
```powershell
C:\Tools\rclone\rclone.exe version
```
## Configure The rclone Remote
On the Spain server:
```powershell
rclone config
```
Recommended input:
```text
n
name> trafag-bi
Storage> onedrive
```
Then complete the Microsoft login.
Important:
- Site: `WorldwideBIPlatform`
- Document library: `Shared Documents`
- The rclone remote `trafag-bi` should point to the document library `Shared Documents`.
## Test rclone
```powershell
rclone lsd trafag-bi:
rclone lsd trafag-bi:"Import"
rclone lsd trafag-bi:"Import/Finance"
rclone lsd trafag-bi:"Import/Finance/Spanien"
```
If the last command lists the folder without an error, the target path is correct.
## Manual Export Without Upload
Full export for 2025:
```powershell
Set-ExecutionPolicy -Scope Process Bypass
cd C:\Trafag\SageSpain
.\Export-SageSpainSalesCsv.ps1 -ExportMode Full -Year 2025 -OutputDirectory C:\Trafag\SageSpain\out
```
Delta/range export:
```powershell
Set-ExecutionPolicy -Scope Process Bypass
cd C:\Trafag\SageSpain
.\Export-SageSpainSalesCsv.ps1 -ExportMode Range -DateFilter LineRegistrationDate -FromDate "2026-06-02" -ToDate "2026-06-03" -OutputDirectory C:\Trafag\SageSpain\out
```
Notes:
- `ToDate` is exclusive.
- The range `"2026-06-02"` to `"2026-06-03"` exports June 2.
- For daily delta exports, `LineRegistrationDate` is recommended because it captures newly registered or changed lines.
## Run Export And Upload Together
Default: daily delta run, yesterday until today:
```powershell
Set-ExecutionPolicy -Scope Process Bypass
cd C:\Trafag\SageSpain
.\Run-SpainExportAndUpload.ps1
```
Explicit date range:
```powershell
.\Run-SpainExportAndUpload.ps1 -ExportMode Range -DateFilter LineRegistrationDate -FromDate "2026-06-02" -ToDate "2026-06-03"
```
Full export with upload:
```powershell
.\Run-SpainExportAndUpload.ps1 -ExportMode Full -Year 2025
```
If rclone is not in the PATH:
```powershell
.\Run-SpainExportAndUpload.ps1 -RcloneExe "C:\Tools\rclone\rclone.exe"
```
If the rclone remote has another name:
```powershell
.\Run-SpainExportAndUpload.ps1 -RcloneRemote "YOUR_REMOTE_NAME"
```
## What Gets Uploaded?
The wrapper script uploads these files from the newest export folder:
```text
*.csv
*_summary.txt
```
Target:
```text
trafag-bi:Import/Finance/Spanien
```
The script does not change any data in Sage or SQL Server.
## Windows Task Scheduler
Recommended daily run, for example at 02:00:
```powershell
$action = New-ScheduledTaskAction `
-Execute "powershell.exe" `
-Argument "-NoProfile -ExecutionPolicy Bypass -File C:\Trafag\SageSpain\Run-SpainExportAndUpload.ps1"
$trigger = New-ScheduledTaskTrigger -Daily -At 02:00
Register-ScheduledTask `
-TaskName "Trafag Spain Sage Export Upload" `
-Action $action `
-Trigger $trigger `
-Description "Exports Sage Spain sales CSV and uploads it to SharePoint via rclone"
```
If rclone is not in the PATH:
```powershell
$action = New-ScheduledTaskAction `
-Execute "powershell.exe" `
-Argument "-NoProfile -ExecutionPolicy Bypass -File C:\Trafag\SageSpain\Run-SpainExportAndUpload.ps1 -RcloneExe C:\Tools\rclone\rclone.exe"
```
## Check After The Run
Local output:
```powershell
Get-ChildItem C:\Trafag\SageSpain\out -Directory | Sort-Object LastWriteTime -Descending | Select-Object -First 1
Get-ChildItem C:\Trafag\SageSpain\logs
```
SharePoint via rclone:
```powershell
rclone ls trafag-bi:"Import/Finance/Spanien"
```
Browser check:
```text
https://trafagag.sharepoint.com/sites/WorldwideBIPlatform/Shared%20Documents/Import/Finance/Spanien
```
## Common Issues
`rclone: command not found`
- rclone is not in the PATH.
- Use `-RcloneExe "C:\Tools\rclone\rclone.exe"`.
`directory not found`
- The remote may not point to `Shared Documents`, or the target folder may be different.
- Check with `rclone lsd trafag-bi:` and `rclone lsd trafag-bi:"Import/Finance"`.
`Access denied`
- Microsoft login or SharePoint permissions are missing.
- The Windows user running the scheduled task must have access to the rclone configuration and to SharePoint.
Empty delta file:
- Check the date range.
- `ToDate` is exclusive.
- For a daily run, yesterday until today is correct.