diff --git a/TrafagSalesExporter/Components/Pages/Settings.razor b/TrafagSalesExporter/Components/Pages/Settings.razor
index 4c5c4cb..8db9332 100644
--- a/TrafagSalesExporter/Components/Pages/Settings.razor
+++ b/TrafagSalesExporter/Components/Pages/Settings.razor
@@ -313,8 +313,8 @@
- @T("Schreibt beim Laenderexport je Standort eine Sales_*.csv mit den transformierten Daten.",
- "Writes one Sales_*.csv per site during country export with the transformed data.")
+ @T("Schreibt beim Laenderexport je Standort eine Sales_ProcessedMergeInput_*.csv mit den transformierten Daten.",
+ "Writes one Sales_ProcessedMergeInput_*.csv per site during country export with the transformed data.")
@@ -351,6 +351,7 @@
@T("Beispiel:", "Example:") Sales_TRFR_@(DateTime.Now.ToString("yyyy-MM-dd")).xlsx
+ / Sales_ProcessedMergeInput_TRFR_@(DateTime.Now.ToString("yyyy-MM-dd")).csv
diff --git a/TrafagSalesExporter/Services/CentralSalesDataProvider.cs b/TrafagSalesExporter/Services/CentralSalesDataProvider.cs
index dcfb23a..7ebb099 100644
--- a/TrafagSalesExporter/Services/CentralSalesDataProvider.cs
+++ b/TrafagSalesExporter/Services/CentralSalesDataProvider.cs
@@ -38,7 +38,7 @@ public sealed class CentralSalesDataProvider : ICentralSalesDataProvider
{
var directory = _auditCsvService.ResolveAuditCsvDirectory(settings);
throw new InvalidOperationException(
- $"Audit-CSV ist als zentrale Quelle aktiv, aber im Ordner '{directory}' wurden keine Sales_*.csv-Dateien gefunden.");
+ $"Audit-CSV ist als zentrale Quelle aktiv, aber im Ordner '{directory}' wurden keine Sales_ProcessedMergeInput_*.csv-Dateien gefunden.");
}
return records
diff --git a/TrafagSalesExporter/Services/ExportAuditCsvService.cs b/TrafagSalesExporter/Services/ExportAuditCsvService.cs
index 05c5137..49a94fd 100644
--- a/TrafagSalesExporter/Services/ExportAuditCsvService.cs
+++ b/TrafagSalesExporter/Services/ExportAuditCsvService.cs
@@ -21,6 +21,8 @@ public interface IExportAuditCsvService
public sealed class ExportAuditCsvService : IExportAuditCsvService
{
private const char Delimiter = ';';
+ private const string ProcessedMergeInputFilePrefix = "Sales_ProcessedMergeInput_";
+ private const string LegacyFilePrefix = "Sales_";
private static readonly string[] Headers =
[
@@ -84,7 +86,7 @@ public sealed class ExportAuditCsvService : IExportAuditCsvService
Directory.CreateDirectory(directory);
var tsc = string.IsNullOrWhiteSpace(site.TSC) ? "UNKNOWN" : site.TSC.Trim();
- var fileName = $"Sales_{SanitizeFileNamePart(tsc)}_{DateTime.UtcNow:yyyy-MM-dd}.csv";
+ var fileName = $"{ProcessedMergeInputFilePrefix}{SanitizeFileNamePart(tsc)}_{DateTime.UtcNow:yyyy-MM-dd}.csv";
var path = Path.Combine(directory, fileName);
await using var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read);
@@ -105,12 +107,16 @@ public sealed class ExportAuditCsvService : IExportAuditCsvService
if (!Directory.Exists(directory))
return [];
- var latestFiles = Directory.EnumerateFiles(directory, "Sales_*.csv", SearchOption.TopDirectoryOnly)
- .GroupBy(ResolveTscFromFileName, StringComparer.OrdinalIgnoreCase)
+ var latestFiles = EnumerateAuditCsvFiles(directory)
+ .Select(path => new { Path = path, Tsc = ResolveTscFromFileName(path) })
+ .Where(file => !string.IsNullOrWhiteSpace(file.Tsc))
+ .GroupBy(file => file.Tsc, StringComparer.OrdinalIgnoreCase)
.Select(group => group
- .OrderByDescending(File.GetLastWriteTimeUtc)
- .ThenByDescending(Path.GetFileName, StringComparer.OrdinalIgnoreCase)
- .First())
+ .OrderByDescending(file => File.GetLastWriteTimeUtc(file.Path))
+ .ThenByDescending(file => IsProcessedMergeInputFile(file.Path))
+ .ThenByDescending(file => Path.GetFileName(file.Path), StringComparer.OrdinalIgnoreCase)
+ .First()
+ .Path)
.OrderBy(path => path, StringComparer.OrdinalIgnoreCase)
.ToList();
@@ -257,14 +263,29 @@ public sealed class ExportAuditCsvService : IExportAuditCsvService
private static string ResolveTscFromFileName(string path)
{
var name = Path.GetFileNameWithoutExtension(path);
- if (!name.StartsWith("Sales_", StringComparison.OrdinalIgnoreCase))
- return name;
+ if (name.StartsWith(ProcessedMergeInputFilePrefix, StringComparison.OrdinalIgnoreCase))
+ return ResolveTscFromSuffix(name[ProcessedMergeInputFilePrefix.Length..]);
- var withoutPrefix = name["Sales_".Length..];
- var lastUnderscore = withoutPrefix.LastIndexOf('_');
- return lastUnderscore <= 0 ? withoutPrefix : withoutPrefix[..lastUnderscore];
+ if (name.StartsWith(LegacyFilePrefix, StringComparison.OrdinalIgnoreCase))
+ return ResolveTscFromSuffix(name[LegacyFilePrefix.Length..]);
+
+ return string.Empty;
}
+ private static string ResolveTscFromSuffix(string suffix)
+ {
+ var lastUnderscore = suffix.LastIndexOf('_');
+ return lastUnderscore <= 0 ? suffix : suffix[..lastUnderscore];
+ }
+
+ private static IEnumerable EnumerateAuditCsvFiles(string directory)
+ => Directory.EnumerateFiles(directory, $"{ProcessedMergeInputFilePrefix}*.csv", SearchOption.TopDirectoryOnly)
+ .Concat(Directory.EnumerateFiles(directory, $"{LegacyFilePrefix}*.csv", SearchOption.TopDirectoryOnly)
+ .Where(path => !IsProcessedMergeInputFile(path)));
+
+ private static bool IsProcessedMergeInputFile(string path)
+ => Path.GetFileName(path).StartsWith(ProcessedMergeInputFilePrefix, StringComparison.OrdinalIgnoreCase);
+
private static string SanitizeFileNamePart(string value)
{
var invalid = Path.GetInvalidFileNameChars();
diff --git a/TrafagSalesExporter/Services/UiTextService.cs b/TrafagSalesExporter/Services/UiTextService.cs
index 9654b5f..bcabb6d 100644
--- a/TrafagSalesExporter/Services/UiTextService.cs
+++ b/TrafagSalesExporter/Services/UiTextService.cs
@@ -254,7 +254,8 @@ public sealed class UiTextService : IUiTextService
["Transformer Ansicht"] = "Vista de transformaciones",
["Transformationscode"] = "Código de transformación",
["Keine Beschreibung."] = "Sin descripción.",
- ["Optionales Argument."] = "Argumento opcional."
+ ["Optionales Argument."] = "Argumento opcional.",
+ ["Schreibt beim Laenderexport je Standort eine Sales_ProcessedMergeInput_*.csv mit den transformierten Daten."] = "Escribe una Sales_ProcessedMergeInput_*.csv por sitio durante la exportacion de paises con los datos transformados."
},
["it"] = new Dictionary(StringComparer.OrdinalIgnoreCase)
{
@@ -487,7 +488,8 @@ public sealed class UiTextService : IUiTextService
["Transformer Ansicht"] = "Vista trasformazioni",
["Transformationscode"] = "Codice trasformazione",
["Keine Beschreibung."] = "Nessuna descrizione.",
- ["Optionales Argument."] = "Argomento opzionale."
+ ["Optionales Argument."] = "Argomento opzionale.",
+ ["Schreibt beim Laenderexport je Standort eine Sales_ProcessedMergeInput_*.csv mit den transformierten Daten."] = "Scrive una Sales_ProcessedMergeInput_*.csv per sito durante l'esportazione dei Paesi con i dati trasformati."
},
["hi"] = new Dictionary(StringComparer.OrdinalIgnoreCase)
{
@@ -720,7 +722,8 @@ public sealed class UiTextService : IUiTextService
["Transformer Ansicht"] = "रूपांतरण दृश्य",
["Transformationscode"] = "रूपांतरण कोड",
["Keine Beschreibung."] = "कोई विवरण नहीं.",
- ["Optionales Argument."] = "वैकल्पिक तर्क."
+ ["Optionales Argument."] = "वैकल्पिक तर्क.",
+ ["Schreibt beim Laenderexport je Standort eine Sales_ProcessedMergeInput_*.csv mit den transformierten Daten."] = "Writes one Sales_ProcessedMergeInput_*.csv per site during country export with the transformed data."
}
};
diff --git a/TrafagSalesExporter/TrafagSalesExporter.Tests/ExportAuditCsvServiceTests.cs b/TrafagSalesExporter/TrafagSalesExporter.Tests/ExportAuditCsvServiceTests.cs
index 862d15c..7ed6c80 100644
--- a/TrafagSalesExporter/TrafagSalesExporter.Tests/ExportAuditCsvServiceTests.cs
+++ b/TrafagSalesExporter/TrafagSalesExporter.Tests/ExportAuditCsvServiceTests.cs
@@ -67,6 +67,7 @@ public sealed class ExportAuditCsvServiceTests : IDisposable
Assert.True(File.Exists(path));
Assert.Equal(_tempDirectory, Path.GetDirectoryName(path));
+ Assert.StartsWith("Sales_ProcessedMergeInput_TRCH_", Path.GetFileName(path), StringComparison.OrdinalIgnoreCase);
var records = await service.ReadLatestSiteAuditCsvRecordsAsync(settings);
var roundtrip = Assert.Single(records);
Assert.Equal("SAP", roundtrip.SourceSystem);
@@ -80,6 +81,62 @@ public sealed class ExportAuditCsvServiceTests : IDisposable
Assert.Equal(new DateTime(2026, 6, 11), roundtrip.InvoiceDate);
}
+ [Fact]
+ public async Task ReadLatestSiteAuditCsvRecordsAsync_Reads_New_Name_Before_Legacy_Name()
+ {
+ var service = new ExportAuditCsvService();
+ var settings = new ExportSettings
+ {
+ AuditCsvEnabled = true,
+ LocalSiteExportFolder = _tempDirectory
+ };
+ var site = new Site { TSC = "TRSE", Land = "Spanien" };
+
+ var legacyPath = await service.WriteSiteAuditCsvAsync(
+ site,
+ settings,
+ "MANUAL_EXCEL",
+ _tempDirectory,
+ [
+ new SalesRecord
+ {
+ SourceSystem = "MANUAL_EXCEL",
+ ExtractionDate = new DateTime(2026, 6, 10),
+ Tsc = "TRSE",
+ Land = "Spanien",
+ InvoiceNumber = "NEW",
+ SalesPriceValue = 20m
+ }
+ ]);
+ var oldPath = Path.Combine(_tempDirectory, "Sales_TRSE_2026-06-10.csv");
+ File.Move(legacyPath!, oldPath);
+ File.SetLastWriteTimeUtc(oldPath, new DateTime(2026, 6, 10, 8, 0, 0, DateTimeKind.Utc));
+
+ var newPath = await service.WriteSiteAuditCsvAsync(
+ site,
+ settings,
+ "MANUAL_EXCEL",
+ _tempDirectory,
+ [
+ new SalesRecord
+ {
+ SourceSystem = "MANUAL_EXCEL",
+ ExtractionDate = new DateTime(2026, 6, 11),
+ Tsc = "TRSE",
+ Land = "Spanien",
+ InvoiceNumber = "PROCESSED",
+ SalesPriceValue = 30m
+ }
+ ]);
+ File.SetLastWriteTimeUtc(newPath!, new DateTime(2026, 6, 11, 8, 0, 0, DateTimeKind.Utc));
+
+ var records = await service.ReadLatestSiteAuditCsvRecordsAsync(settings);
+
+ var record = Assert.Single(records);
+ Assert.Equal("PROCESSED", record.InvoiceNumber);
+ Assert.Equal(30m, record.SalesPriceValue);
+ }
+
[Fact]
public async Task CentralSalesDataProvider_Uses_AuditCsv_When_Configured()
{
diff --git a/TrafagSalesExporter/TrafagSalesExporter.Tests/SiteExportServiceTests.cs b/TrafagSalesExporter/TrafagSalesExporter.Tests/SiteExportServiceTests.cs
index 112c569..fb3e176 100644
--- a/TrafagSalesExporter/TrafagSalesExporter.Tests/SiteExportServiceTests.cs
+++ b/TrafagSalesExporter/TrafagSalesExporter.Tests/SiteExportServiceTests.cs
@@ -101,7 +101,7 @@ public sealed class SiteExportServiceTests : IDisposable
Assert.NotNull(result.FilePath);
Assert.True(File.Exists(result.FilePath));
- var auditCsv = Directory.GetFiles(_tempDirectory, "Sales_TRSE_*.csv").Single();
+ var auditCsv = Directory.GetFiles(_tempDirectory, "Sales_ProcessedMergeInput_TRSE_*.csv").Single();
Assert.True(File.Exists(auditCsv));
Assert.Equal(2, sharePoint.Uploads.Count);