RefactoringDI
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
@using TrafagSalesExporter.Data
|
@using TrafagSalesExporter.Data
|
||||||
@using TrafagSalesExporter.Services
|
@using TrafagSalesExporter.Services
|
||||||
@inject IDbContextFactory<AppDbContext> DbFactory
|
@inject IDbContextFactory<AppDbContext> DbFactory
|
||||||
@inject SharePointUploadService SpService
|
@inject ISharePointUploadService SpService
|
||||||
@inject TimerBackgroundService TimerService
|
@inject TimerBackgroundService TimerService
|
||||||
@inject ISnackbar Snackbar
|
@inject ISnackbar Snackbar
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
@using TrafagSalesExporter.Data
|
@using TrafagSalesExporter.Data
|
||||||
@using TrafagSalesExporter.Services
|
@using TrafagSalesExporter.Services
|
||||||
@inject IDbContextFactory<AppDbContext> DbFactory
|
@inject IDbContextFactory<AppDbContext> DbFactory
|
||||||
@inject HanaQueryService HanaService
|
@inject IHanaQueryService HanaService
|
||||||
@inject ISnackbar Snackbar
|
@inject ISnackbar Snackbar
|
||||||
@inject IDialogService DialogService
|
@inject IDialogService DialogService
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using System.Data;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using TrafagSalesExporter.Models;
|
using TrafagSalesExporter.Models;
|
||||||
|
|
||||||
@@ -14,102 +13,4 @@ public class AppDbContext : DbContext
|
|||||||
public DbSet<ExportSettings> ExportSettings => Set<ExportSettings>();
|
public DbSet<ExportSettings> ExportSettings => Set<ExportSettings>();
|
||||||
public DbSet<ExportLog> ExportLogs => Set<ExportLog>();
|
public DbSet<ExportLog> ExportLogs => Set<ExportLog>();
|
||||||
public DbSet<FieldTransformationRule> FieldTransformationRules => Set<FieldTransformationRule>();
|
public DbSet<FieldTransformationRule> FieldTransformationRules => Set<FieldTransformationRule>();
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Fügt Spalten zu existierenden Tabellen hinzu, die bei neueren Versionen
|
|
||||||
/// hinzugekommen sind. EnsureCreated aktualisiert das Schema nicht automatisch.
|
|
||||||
/// </summary>
|
|
||||||
public static void EnsureSchema(AppDbContext db)
|
|
||||||
{
|
|
||||||
AddColumnIfMissing(db, "HanaServers", "DatabaseName", "TEXT NOT NULL DEFAULT ''");
|
|
||||||
AddColumnIfMissing(db, "HanaServers", "UseSsl", "INTEGER NOT NULL DEFAULT 0");
|
|
||||||
AddColumnIfMissing(db, "HanaServers", "ValidateCertificate", "INTEGER NOT NULL DEFAULT 0");
|
|
||||||
AddColumnIfMissing(db, "HanaServers", "AdditionalParams", "TEXT NOT NULL DEFAULT ''");
|
|
||||||
AddColumnIfMissing(db, "Sites", "SourceSystem", "TEXT NOT NULL DEFAULT 'SAP'");
|
|
||||||
EnsureTransformationTable(db);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void AddColumnIfMissing(AppDbContext db, string table, string column, string type)
|
|
||||||
{
|
|
||||||
var conn = db.Database.GetDbConnection();
|
|
||||||
if (conn.State != ConnectionState.Open) conn.Open();
|
|
||||||
|
|
||||||
bool exists = false;
|
|
||||||
using (var cmd = conn.CreateCommand())
|
|
||||||
{
|
|
||||||
cmd.CommandText = $"PRAGMA table_info({table})";
|
|
||||||
using var reader = cmd.ExecuteReader();
|
|
||||||
while (reader.Read())
|
|
||||||
{
|
|
||||||
if (string.Equals(reader["name"]?.ToString(), column, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
exists = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!exists)
|
|
||||||
{
|
|
||||||
using var alter = conn.CreateCommand();
|
|
||||||
alter.CommandText = $"ALTER TABLE {table} ADD COLUMN {column} {type}";
|
|
||||||
alter.ExecuteNonQuery();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void EnsureTransformationTable(AppDbContext db)
|
|
||||||
{
|
|
||||||
var conn = db.Database.GetDbConnection();
|
|
||||||
if (conn.State != ConnectionState.Open) conn.Open();
|
|
||||||
|
|
||||||
using var cmd = conn.CreateCommand();
|
|
||||||
cmd.CommandText = @"
|
|
||||||
CREATE TABLE IF NOT EXISTS FieldTransformationRules (
|
|
||||||
Id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
|
||||||
SourceSystem TEXT NOT NULL DEFAULT 'SAP',
|
|
||||||
SourceField TEXT NOT NULL,
|
|
||||||
TargetField TEXT NOT NULL,
|
|
||||||
TransformationType TEXT NOT NULL,
|
|
||||||
Argument TEXT NOT NULL DEFAULT '',
|
|
||||||
SortOrder INTEGER NOT NULL DEFAULT 0,
|
|
||||||
IsActive INTEGER NOT NULL DEFAULT 1
|
|
||||||
);";
|
|
||||||
cmd.ExecuteNonQuery();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SeedIfEmpty(AppDbContext db)
|
|
||||||
{
|
|
||||||
if (db.HanaServers.Any()) return;
|
|
||||||
|
|
||||||
var serverInternal = new HanaServer { Name = "Internal", Host = "travtrp0", Port = 30015, Username = "", Password = "" };
|
|
||||||
var serverIndia = new HanaServer { Name = "India", Host = "20.197.20.60", Port = 30015, Username = "", Password = "" };
|
|
||||||
db.HanaServers.AddRange(serverInternal, serverIndia);
|
|
||||||
db.SaveChanges();
|
|
||||||
|
|
||||||
db.Sites.AddRange(
|
|
||||||
new Site { HanaServerId = serverInternal.Id, Schema = "fr01_p", TSC = "TRFR", Land = "Frankreich", IsActive = true },
|
|
||||||
new Site { HanaServerId = serverInternal.Id, Schema = "it01_p", TSC = "TRIT", Land = "Italien", IsActive = true },
|
|
||||||
new Site { HanaServerId = serverInternal.Id, Schema = "us01_p", TSC = "TRUS", Land = "USA", IsActive = true },
|
|
||||||
new Site { HanaServerId = serverIndia.Id, Schema = "TRAFAG_LIVE", TSC = "TRIN", Land = "Indien", IsActive = true }
|
|
||||||
);
|
|
||||||
|
|
||||||
db.SharePointConfigs.Add(new SharePointConfig
|
|
||||||
{
|
|
||||||
SiteUrl = "https://trafagag.sharepoint.com/sites/WorldwideBIPlatform",
|
|
||||||
ExportFolder = "/Shared Documents/Exports/",
|
|
||||||
TenantId = "",
|
|
||||||
ClientId = "",
|
|
||||||
ClientSecret = ""
|
|
||||||
});
|
|
||||||
|
|
||||||
db.ExportSettings.Add(new ExportSettings
|
|
||||||
{
|
|
||||||
DateFilter = "2025-01-01",
|
|
||||||
TimerHour = 3,
|
|
||||||
TimerMinute = 0,
|
|
||||||
TimerEnabled = true
|
|
||||||
});
|
|
||||||
|
|
||||||
db.SaveChanges();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,10 +13,21 @@ builder.Services.AddMudServices();
|
|||||||
builder.Services.AddDbContextFactory<AppDbContext>(options =>
|
builder.Services.AddDbContextFactory<AppDbContext>(options =>
|
||||||
options.UseSqlite("Data Source=trafag_exporter.db"));
|
options.UseSqlite("Data Source=trafag_exporter.db"));
|
||||||
|
|
||||||
builder.Services.AddSingleton<HanaQueryService>();
|
builder.Services.AddSingleton<IHanaQueryService, HanaQueryService>();
|
||||||
builder.Services.AddSingleton<ExcelExportService>();
|
builder.Services.AddSingleton<IExcelExportService, ExcelExportService>();
|
||||||
builder.Services.AddSingleton<SharePointUploadService>();
|
builder.Services.AddSingleton<ISharePointUploadService, SharePointUploadService>();
|
||||||
builder.Services.AddSingleton<RecordTransformationService>();
|
builder.Services.AddSingleton<ITransformationStrategy, CopyTransformationStrategy>();
|
||||||
|
builder.Services.AddSingleton<ITransformationStrategy, UppercaseTransformationStrategy>();
|
||||||
|
builder.Services.AddSingleton<ITransformationStrategy, LowercaseTransformationStrategy>();
|
||||||
|
builder.Services.AddSingleton<ITransformationStrategy, PrefixTransformationStrategy>();
|
||||||
|
builder.Services.AddSingleton<ITransformationStrategy, SuffixTransformationStrategy>();
|
||||||
|
builder.Services.AddSingleton<ITransformationStrategy, ReplaceTransformationStrategy>();
|
||||||
|
builder.Services.AddSingleton<ITransformationStrategy, ConstantTransformationStrategy>();
|
||||||
|
builder.Services.AddSingleton<IRecordTransformationService, RecordTransformationService>();
|
||||||
|
builder.Services.AddSingleton<ISiteExportService, SiteExportService>();
|
||||||
|
builder.Services.AddSingleton<IConsolidatedExportService, ConsolidatedExportService>();
|
||||||
|
builder.Services.AddSingleton<IExportLogService, ExportLogService>();
|
||||||
|
builder.Services.AddSingleton<IDatabaseInitializationService, DatabaseInitializationService>();
|
||||||
builder.Services.AddSingleton<ExportOrchestrationService>();
|
builder.Services.AddSingleton<ExportOrchestrationService>();
|
||||||
builder.Services.AddSingleton<TimerBackgroundService>();
|
builder.Services.AddSingleton<TimerBackgroundService>();
|
||||||
builder.Services.AddHostedService(sp => sp.GetRequiredService<TimerBackgroundService>());
|
builder.Services.AddHostedService(sp => sp.GetRequiredService<TimerBackgroundService>());
|
||||||
@@ -25,11 +36,8 @@ var app = builder.Build();
|
|||||||
|
|
||||||
using (var scope = app.Services.CreateScope())
|
using (var scope = app.Services.CreateScope())
|
||||||
{
|
{
|
||||||
var dbFactory = scope.ServiceProvider.GetRequiredService<IDbContextFactory<AppDbContext>>();
|
var databaseInitialization = scope.ServiceProvider.GetRequiredService<IDatabaseInitializationService>();
|
||||||
using var db = await dbFactory.CreateDbContextAsync();
|
await databaseInitialization.InitializeAsync();
|
||||||
await db.Database.EnsureCreatedAsync();
|
|
||||||
AppDbContext.EnsureSchema(db);
|
|
||||||
AppDbContext.SeedIfEmpty(db);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!app.Environment.IsDevelopment())
|
if (!app.Environment.IsDevelopment())
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using TrafagSalesExporter.Data;
|
||||||
|
using TrafagSalesExporter.Models;
|
||||||
|
|
||||||
|
namespace TrafagSalesExporter.Services;
|
||||||
|
|
||||||
|
public class ConsolidatedExportService : IConsolidatedExportService
|
||||||
|
{
|
||||||
|
private readonly IDbContextFactory<AppDbContext> _dbFactory;
|
||||||
|
private readonly IExcelExportService _excelService;
|
||||||
|
private readonly ISharePointUploadService _sharePointService;
|
||||||
|
|
||||||
|
public ConsolidatedExportService(
|
||||||
|
IDbContextFactory<AppDbContext> dbFactory,
|
||||||
|
IExcelExportService excelService,
|
||||||
|
ISharePointUploadService sharePointService)
|
||||||
|
{
|
||||||
|
_dbFactory = dbFactory;
|
||||||
|
_excelService = excelService;
|
||||||
|
_sharePointService = sharePointService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string?> ExportAsync(List<SalesRecord> records)
|
||||||
|
{
|
||||||
|
if (records.Count == 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
using var db = await _dbFactory.CreateDbContextAsync();
|
||||||
|
var spConfig = await db.SharePointConfigs.FirstOrDefaultAsync();
|
||||||
|
var outputDir = Path.Combine(AppContext.BaseDirectory, "output");
|
||||||
|
var consolidatedPath = _excelService.CreateConsolidatedExcelFile(
|
||||||
|
outputDir,
|
||||||
|
DateTime.UtcNow.Date,
|
||||||
|
records
|
||||||
|
.OrderBy(r => r.Land)
|
||||||
|
.ThenBy(r => r.Tsc)
|
||||||
|
.ThenByDescending(r => r.InvoiceDate ?? DateTime.MinValue)
|
||||||
|
.ThenBy(r => r.InvoiceNumber)
|
||||||
|
.ThenBy(r => r.PositionOnInvoice)
|
||||||
|
.ToList());
|
||||||
|
|
||||||
|
if (spConfig is not null &&
|
||||||
|
!string.IsNullOrWhiteSpace(spConfig.TenantId) &&
|
||||||
|
!string.IsNullOrWhiteSpace(spConfig.ClientId) &&
|
||||||
|
!string.IsNullOrWhiteSpace(spConfig.ClientSecret))
|
||||||
|
{
|
||||||
|
await _sharePointService.UploadAsync(
|
||||||
|
spConfig.TenantId, spConfig.ClientId, spConfig.ClientSecret,
|
||||||
|
spConfig.SiteUrl, spConfig.ExportFolder, "Alle", consolidatedPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return consolidatedPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
using System.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using TrafagSalesExporter.Data;
|
||||||
|
using TrafagSalesExporter.Models;
|
||||||
|
|
||||||
|
namespace TrafagSalesExporter.Services;
|
||||||
|
|
||||||
|
public class DatabaseInitializationService : IDatabaseInitializationService
|
||||||
|
{
|
||||||
|
private readonly IDbContextFactory<AppDbContext> _dbFactory;
|
||||||
|
|
||||||
|
public DatabaseInitializationService(IDbContextFactory<AppDbContext> dbFactory)
|
||||||
|
{
|
||||||
|
_dbFactory = dbFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InitializeAsync()
|
||||||
|
{
|
||||||
|
using var db = await _dbFactory.CreateDbContextAsync();
|
||||||
|
await db.Database.EnsureCreatedAsync();
|
||||||
|
EnsureSchema(db);
|
||||||
|
SeedIfEmpty(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EnsureSchema(AppDbContext db)
|
||||||
|
{
|
||||||
|
AddColumnIfMissing(db, "HanaServers", "DatabaseName", "TEXT NOT NULL DEFAULT ''");
|
||||||
|
AddColumnIfMissing(db, "HanaServers", "UseSsl", "INTEGER NOT NULL DEFAULT 0");
|
||||||
|
AddColumnIfMissing(db, "HanaServers", "ValidateCertificate", "INTEGER NOT NULL DEFAULT 0");
|
||||||
|
AddColumnIfMissing(db, "HanaServers", "AdditionalParams", "TEXT NOT NULL DEFAULT ''");
|
||||||
|
AddColumnIfMissing(db, "Sites", "SourceSystem", "TEXT NOT NULL DEFAULT 'SAP'");
|
||||||
|
EnsureTransformationTable(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AddColumnIfMissing(AppDbContext db, string table, string column, string type)
|
||||||
|
{
|
||||||
|
var conn = db.Database.GetDbConnection();
|
||||||
|
if (conn.State != ConnectionState.Open)
|
||||||
|
conn.Open();
|
||||||
|
|
||||||
|
var exists = false;
|
||||||
|
using (var cmd = conn.CreateCommand())
|
||||||
|
{
|
||||||
|
cmd.CommandText = $"PRAGMA table_info({table})";
|
||||||
|
using var reader = cmd.ExecuteReader();
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
if (string.Equals(reader["name"]?.ToString(), column, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
exists = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!exists)
|
||||||
|
{
|
||||||
|
using var alter = conn.CreateCommand();
|
||||||
|
alter.CommandText = $"ALTER TABLE {table} ADD COLUMN {column} {type}";
|
||||||
|
alter.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EnsureTransformationTable(AppDbContext db)
|
||||||
|
{
|
||||||
|
var conn = db.Database.GetDbConnection();
|
||||||
|
if (conn.State != ConnectionState.Open)
|
||||||
|
conn.Open();
|
||||||
|
|
||||||
|
using var cmd = conn.CreateCommand();
|
||||||
|
cmd.CommandText = @"
|
||||||
|
CREATE TABLE IF NOT EXISTS FieldTransformationRules (
|
||||||
|
Id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
SourceSystem TEXT NOT NULL DEFAULT 'SAP',
|
||||||
|
SourceField TEXT NOT NULL,
|
||||||
|
TargetField TEXT NOT NULL,
|
||||||
|
TransformationType TEXT NOT NULL,
|
||||||
|
Argument TEXT NOT NULL DEFAULT '',
|
||||||
|
SortOrder INTEGER NOT NULL DEFAULT 0,
|
||||||
|
IsActive INTEGER NOT NULL DEFAULT 1
|
||||||
|
);";
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SeedIfEmpty(AppDbContext db)
|
||||||
|
{
|
||||||
|
if (db.HanaServers.Any())
|
||||||
|
return;
|
||||||
|
|
||||||
|
var serverInternal = new HanaServer { Name = "Internal", Host = "travtrp0", Port = 30015, Username = "", Password = "" };
|
||||||
|
var serverIndia = new HanaServer { Name = "India", Host = "20.197.20.60", Port = 30015, Username = "", Password = "" };
|
||||||
|
db.HanaServers.AddRange(serverInternal, serverIndia);
|
||||||
|
db.SaveChanges();
|
||||||
|
|
||||||
|
db.Sites.AddRange(
|
||||||
|
new Site { HanaServerId = serverInternal.Id, Schema = "fr01_p", TSC = "TRFR", Land = "Frankreich", IsActive = true },
|
||||||
|
new Site { HanaServerId = serverInternal.Id, Schema = "it01_p", TSC = "TRIT", Land = "Italien", IsActive = true },
|
||||||
|
new Site { HanaServerId = serverInternal.Id, Schema = "us01_p", TSC = "TRUS", Land = "USA", IsActive = true },
|
||||||
|
new Site { HanaServerId = serverIndia.Id, Schema = "TRAFAG_LIVE", TSC = "TRIN", Land = "Indien", IsActive = true }
|
||||||
|
);
|
||||||
|
|
||||||
|
db.SharePointConfigs.Add(new SharePointConfig
|
||||||
|
{
|
||||||
|
SiteUrl = "https://trafagag.sharepoint.com/sites/WorldwideBIPlatform",
|
||||||
|
ExportFolder = "/Shared Documents/Exports/",
|
||||||
|
TenantId = "",
|
||||||
|
ClientId = "",
|
||||||
|
ClientSecret = ""
|
||||||
|
});
|
||||||
|
|
||||||
|
db.ExportSettings.Add(new ExportSettings
|
||||||
|
{
|
||||||
|
DateFilter = "2025-01-01",
|
||||||
|
TimerHour = 3,
|
||||||
|
TimerMinute = 0,
|
||||||
|
TimerEnabled = true
|
||||||
|
});
|
||||||
|
|
||||||
|
db.SaveChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ using TrafagSalesExporter.Models;
|
|||||||
|
|
||||||
namespace TrafagSalesExporter.Services;
|
namespace TrafagSalesExporter.Services;
|
||||||
|
|
||||||
public class ExcelExportService
|
public class ExcelExportService : IExcelExportService
|
||||||
{
|
{
|
||||||
public string CreateExcelFile(string outputDirectory, string tsc, DateTime fileDate, List<SalesRecord> records)
|
public string CreateExcelFile(string outputDirectory, string tsc, DateTime fileDate, List<SalesRecord> records)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using TrafagSalesExporter.Data;
|
||||||
|
using TrafagSalesExporter.Models;
|
||||||
|
|
||||||
|
namespace TrafagSalesExporter.Services;
|
||||||
|
|
||||||
|
public class ExportLogService : IExportLogService
|
||||||
|
{
|
||||||
|
private readonly IDbContextFactory<AppDbContext> _dbFactory;
|
||||||
|
|
||||||
|
public ExportLogService(IDbContextFactory<AppDbContext> dbFactory)
|
||||||
|
{
|
||||||
|
_dbFactory = dbFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task WriteAsync(ExportLog log)
|
||||||
|
{
|
||||||
|
using var db = await _dbFactory.CreateDbContextAsync();
|
||||||
|
db.ExportLogs.Add(log);
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System.Diagnostics;
|
|
||||||
using TrafagSalesExporter.Data;
|
using TrafagSalesExporter.Data;
|
||||||
using TrafagSalesExporter.Models;
|
using TrafagSalesExporter.Models;
|
||||||
|
|
||||||
@@ -8,11 +7,9 @@ namespace TrafagSalesExporter.Services;
|
|||||||
public class ExportOrchestrationService
|
public class ExportOrchestrationService
|
||||||
{
|
{
|
||||||
private readonly IDbContextFactory<AppDbContext> _dbFactory;
|
private readonly IDbContextFactory<AppDbContext> _dbFactory;
|
||||||
private readonly HanaQueryService _hanaService;
|
private readonly ISiteExportService _siteExportService;
|
||||||
private readonly ExcelExportService _excelService;
|
private readonly IConsolidatedExportService _consolidatedExportService;
|
||||||
private readonly SharePointUploadService _sharePointService;
|
private readonly IExportLogService _exportLogService;
|
||||||
private readonly RecordTransformationService _transformationService;
|
|
||||||
private readonly ILogger<ExportOrchestrationService> _logger;
|
|
||||||
|
|
||||||
public event Action? OnExportStatusChanged;
|
public event Action? OnExportStatusChanged;
|
||||||
|
|
||||||
@@ -21,18 +18,14 @@ public class ExportOrchestrationService
|
|||||||
|
|
||||||
public ExportOrchestrationService(
|
public ExportOrchestrationService(
|
||||||
IDbContextFactory<AppDbContext> dbFactory,
|
IDbContextFactory<AppDbContext> dbFactory,
|
||||||
HanaQueryService hanaService,
|
ISiteExportService siteExportService,
|
||||||
ExcelExportService excelService,
|
IConsolidatedExportService consolidatedExportService,
|
||||||
SharePointUploadService sharePointService,
|
IExportLogService exportLogService)
|
||||||
RecordTransformationService transformationService,
|
|
||||||
ILogger<ExportOrchestrationService> logger)
|
|
||||||
{
|
{
|
||||||
_dbFactory = dbFactory;
|
_dbFactory = dbFactory;
|
||||||
_hanaService = hanaService;
|
_siteExportService = siteExportService;
|
||||||
_excelService = excelService;
|
_consolidatedExportService = consolidatedExportService;
|
||||||
_sharePointService = sharePointService;
|
_exportLogService = exportLogService;
|
||||||
_transformationService = transformationService;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsExporting(int siteId)
|
public bool IsExporting(int siteId)
|
||||||
@@ -64,31 +57,7 @@ public class ExportOrchestrationService
|
|||||||
consolidatedRecords.AddRange(result.Records);
|
consolidatedRecords.AddRange(result.Records);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (consolidatedRecords.Count > 0)
|
await _consolidatedExportService.ExportAsync(consolidatedRecords);
|
||||||
{
|
|
||||||
var spConfig = await db.SharePointConfigs.FirstOrDefaultAsync();
|
|
||||||
var outputDir = Path.Combine(AppContext.BaseDirectory, "output");
|
|
||||||
var consolidatedPath = _excelService.CreateConsolidatedExcelFile(
|
|
||||||
outputDir,
|
|
||||||
DateTime.UtcNow.Date,
|
|
||||||
consolidatedRecords
|
|
||||||
.OrderBy(r => r.Land)
|
|
||||||
.ThenBy(r => r.Tsc)
|
|
||||||
.ThenByDescending(r => r.InvoiceDate ?? DateTime.MinValue)
|
|
||||||
.ThenBy(r => r.InvoiceNumber)
|
|
||||||
.ThenBy(r => r.PositionOnInvoice)
|
|
||||||
.ToList());
|
|
||||||
|
|
||||||
if (spConfig is not null &&
|
|
||||||
!string.IsNullOrWhiteSpace(spConfig.TenantId) &&
|
|
||||||
!string.IsNullOrWhiteSpace(spConfig.ClientId) &&
|
|
||||||
!string.IsNullOrWhiteSpace(spConfig.ClientSecret))
|
|
||||||
{
|
|
||||||
await _sharePointService.UploadAsync(
|
|
||||||
spConfig.TenantId, spConfig.ClientId, spConfig.ClientSecret,
|
|
||||||
spConfig.SiteUrl, spConfig.ExportFolder, "Alle", consolidatedPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ExportSiteByIdAsync(int siteId)
|
public async Task ExportSiteByIdAsync(int siteId)
|
||||||
@@ -102,6 +71,7 @@ public class ExportOrchestrationService
|
|||||||
private async Task<SiteExportResult?> ExportSiteAsync(Site site)
|
private async Task<SiteExportResult?> ExportSiteAsync(Site site)
|
||||||
{
|
{
|
||||||
if (site.HanaServer is null) return null;
|
if (site.HanaServer is null) return null;
|
||||||
|
SiteExportResult? result = null;
|
||||||
|
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
@@ -110,75 +80,17 @@ public class ExportOrchestrationService
|
|||||||
}
|
}
|
||||||
NotifyChanged();
|
NotifyChanged();
|
||||||
|
|
||||||
var sw = Stopwatch.StartNew();
|
|
||||||
var log = new ExportLog
|
|
||||||
{
|
|
||||||
Timestamp = DateTime.Now,
|
|
||||||
SiteId = site.Id,
|
|
||||||
Land = site.Land,
|
|
||||||
TSC = site.TSC
|
|
||||||
};
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using var db = await _dbFactory.CreateDbContextAsync();
|
result = await _siteExportService.ExportAsync(site, status => UpdateStatus(site.Id, status));
|
||||||
var settings = await db.ExportSettings.FirstOrDefaultAsync() ?? new ExportSettings();
|
return result;
|
||||||
var spConfig = await db.SharePointConfigs.FirstOrDefaultAsync();
|
|
||||||
|
|
||||||
UpdateStatus(site.Id, "HANA Abfrage...");
|
|
||||||
var records = await Task.Run(() => _hanaService.GetSalesRecords(
|
|
||||||
site.HanaServer, site.Schema, site.TSC, site.Land, settings.DateFilter));
|
|
||||||
|
|
||||||
UpdateStatus(site.Id, "Transformationen anwenden...");
|
|
||||||
var rules = await db.FieldTransformationRules
|
|
||||||
.Where(r => r.IsActive && r.SourceSystem == (string.IsNullOrWhiteSpace(site.SourceSystem) ? "SAP" : site.SourceSystem))
|
|
||||||
.OrderBy(r => r.SortOrder)
|
|
||||||
.ToListAsync();
|
|
||||||
_transformationService.Apply(records, rules);
|
|
||||||
|
|
||||||
UpdateStatus(site.Id, "Excel erstellen...");
|
|
||||||
var outputDir = Path.Combine(AppContext.BaseDirectory, "output");
|
|
||||||
var filePath = _excelService.CreateExcelFile(outputDir, site.TSC, DateTime.UtcNow.Date, records);
|
|
||||||
var fileName = Path.GetFileName(filePath);
|
|
||||||
|
|
||||||
if (spConfig is not null &&
|
|
||||||
!string.IsNullOrWhiteSpace(spConfig.TenantId) &&
|
|
||||||
!string.IsNullOrWhiteSpace(spConfig.ClientId) &&
|
|
||||||
!string.IsNullOrWhiteSpace(spConfig.ClientSecret))
|
|
||||||
{
|
|
||||||
UpdateStatus(site.Id, "SharePoint Upload...");
|
|
||||||
await _sharePointService.UploadAsync(
|
|
||||||
spConfig.TenantId, spConfig.ClientId, spConfig.ClientSecret,
|
|
||||||
spConfig.SiteUrl, spConfig.ExportFolder, site.Land, filePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
sw.Stop();
|
|
||||||
log.Status = "OK";
|
|
||||||
log.RowCount = records.Count;
|
|
||||||
log.FileName = fileName;
|
|
||||||
log.DurationSeconds = sw.Elapsed.TotalSeconds;
|
|
||||||
|
|
||||||
_logger.LogInformation("Export OK: {Land} ({TSC}) - {Rows} Zeilen in {Duration:F1}s",
|
|
||||||
site.Land, site.TSC, records.Count, sw.Elapsed.TotalSeconds);
|
|
||||||
|
|
||||||
return new SiteExportResult(records, filePath);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
sw.Stop();
|
|
||||||
log.Status = "Error";
|
|
||||||
log.ErrorMessage = ex.Message;
|
|
||||||
log.FileName = string.Empty;
|
|
||||||
log.DurationSeconds = sw.Elapsed.TotalSeconds;
|
|
||||||
|
|
||||||
_logger.LogError(ex, "Export Fehler: {Land} ({TSC})", site.Land, site.TSC);
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
using var db = await _dbFactory.CreateDbContextAsync();
|
if (result is not null)
|
||||||
db.ExportLogs.Add(log);
|
{
|
||||||
await db.SaveChangesAsync();
|
await _exportLogService.WriteAsync(result.Log);
|
||||||
|
}
|
||||||
|
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
@@ -201,6 +113,4 @@ public class ExportOrchestrationService
|
|||||||
{
|
{
|
||||||
OnExportStatusChanged?.Invoke();
|
OnExportStatusChanged?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed record SiteExportResult(List<SalesRecord> Records, string FilePath);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using TrafagSalesExporter.Models;
|
|||||||
|
|
||||||
namespace TrafagSalesExporter.Services;
|
namespace TrafagSalesExporter.Services;
|
||||||
|
|
||||||
public class HanaQueryService
|
public class HanaQueryService : IHanaQueryService
|
||||||
{
|
{
|
||||||
public List<SalesRecord> GetSalesRecords(HanaServer server,
|
public List<SalesRecord> GetSalesRecords(HanaServer server,
|
||||||
string schema, string tsc, string land, string dateFilter)
|
string schema, string tsc, string land, string dateFilter)
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
using TrafagSalesExporter.Models;
|
||||||
|
|
||||||
|
namespace TrafagSalesExporter.Services;
|
||||||
|
|
||||||
|
public interface IConsolidatedExportService
|
||||||
|
{
|
||||||
|
Task<string?> ExportAsync(List<SalesRecord> records);
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace TrafagSalesExporter.Services;
|
||||||
|
|
||||||
|
public interface IDatabaseInitializationService
|
||||||
|
{
|
||||||
|
Task InitializeAsync();
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using TrafagSalesExporter.Models;
|
||||||
|
|
||||||
|
namespace TrafagSalesExporter.Services;
|
||||||
|
|
||||||
|
public interface IExcelExportService
|
||||||
|
{
|
||||||
|
string CreateExcelFile(string outputDirectory, string tsc, DateTime fileDate, List<SalesRecord> records);
|
||||||
|
string CreateConsolidatedExcelFile(string outputDirectory, DateTime fileDate, List<SalesRecord> records);
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
using TrafagSalesExporter.Models;
|
||||||
|
|
||||||
|
namespace TrafagSalesExporter.Services;
|
||||||
|
|
||||||
|
public interface IExportLogService
|
||||||
|
{
|
||||||
|
Task WriteAsync(ExportLog log);
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
using TrafagSalesExporter.Models;
|
||||||
|
|
||||||
|
namespace TrafagSalesExporter.Services;
|
||||||
|
|
||||||
|
public interface IHanaQueryService
|
||||||
|
{
|
||||||
|
List<SalesRecord> GetSalesRecords(HanaServer server, string schema, string tsc, string land, string dateFilter);
|
||||||
|
ConnectionTestResult TestConnectionDetailed(HanaServer server);
|
||||||
|
void TestConnection(HanaServer server);
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
using TrafagSalesExporter.Models;
|
||||||
|
|
||||||
|
namespace TrafagSalesExporter.Services;
|
||||||
|
|
||||||
|
public interface IRecordTransformationService
|
||||||
|
{
|
||||||
|
void Apply(List<SalesRecord> records, IEnumerable<FieldTransformationRule> rules);
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace TrafagSalesExporter.Services;
|
||||||
|
|
||||||
|
public interface ISharePointUploadService
|
||||||
|
{
|
||||||
|
Task UploadAsync(string tenantId, string clientId, string clientSecret, string siteUrl, string exportFolder, string land, string localFilePath);
|
||||||
|
Task TestConnectionAsync(string tenantId, string clientId, string clientSecret, string siteUrl);
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
using TrafagSalesExporter.Models;
|
||||||
|
|
||||||
|
namespace TrafagSalesExporter.Services;
|
||||||
|
|
||||||
|
public interface ISiteExportService
|
||||||
|
{
|
||||||
|
Task<SiteExportResult> ExportAsync(Site site, Action<string>? updateStatus = null);
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace TrafagSalesExporter.Services;
|
||||||
|
|
||||||
|
public interface ITransformationStrategy
|
||||||
|
{
|
||||||
|
string TransformationType { get; }
|
||||||
|
object? Transform(object? sourceValue, string? argument);
|
||||||
|
}
|
||||||
@@ -3,12 +3,19 @@ using TrafagSalesExporter.Models;
|
|||||||
|
|
||||||
namespace TrafagSalesExporter.Services;
|
namespace TrafagSalesExporter.Services;
|
||||||
|
|
||||||
public class RecordTransformationService
|
public class RecordTransformationService : IRecordTransformationService
|
||||||
{
|
{
|
||||||
private static readonly Dictionary<string, PropertyInfo> PropertyMap = typeof(SalesRecord)
|
private static readonly Dictionary<string, PropertyInfo> PropertyMap = typeof(SalesRecord)
|
||||||
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
||||||
.ToDictionary(p => p.Name, p => p, StringComparer.OrdinalIgnoreCase);
|
.ToDictionary(p => p.Name, p => p, StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
private readonly IReadOnlyDictionary<string, ITransformationStrategy> _strategies;
|
||||||
|
|
||||||
|
public RecordTransformationService(IEnumerable<ITransformationStrategy> strategies)
|
||||||
|
{
|
||||||
|
_strategies = strategies.ToDictionary(s => s.TransformationType, StringComparer.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
public void Apply(List<SalesRecord> records, IEnumerable<FieldTransformationRule> rules)
|
public void Apply(List<SalesRecord> records, IEnumerable<FieldTransformationRule> rules)
|
||||||
{
|
{
|
||||||
var orderedRules = rules.Where(r => r.IsActive).OrderBy(r => r.SortOrder).ToList();
|
var orderedRules = rules.Where(r => r.IsActive).OrderBy(r => r.SortOrder).ToList();
|
||||||
@@ -23,37 +30,19 @@ public class RecordTransformationService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ApplyRule(SalesRecord record, FieldTransformationRule rule)
|
private void ApplyRule(SalesRecord record, FieldTransformationRule rule)
|
||||||
{
|
{
|
||||||
if (!PropertyMap.TryGetValue(rule.SourceField, out var sourceProp)) return;
|
if (!PropertyMap.TryGetValue(rule.SourceField, out var sourceProp)) return;
|
||||||
if (!PropertyMap.TryGetValue(rule.TargetField, out var targetProp)) return;
|
if (!PropertyMap.TryGetValue(rule.TargetField, out var targetProp)) return;
|
||||||
|
|
||||||
var sourceValue = sourceProp.GetValue(record);
|
var sourceValue = sourceProp.GetValue(record);
|
||||||
object? result = rule.TransformationType switch
|
object? result = _strategies.TryGetValue(rule.TransformationType, out var strategy)
|
||||||
{
|
? strategy.Transform(sourceValue, rule.Argument)
|
||||||
"Copy" => sourceValue,
|
: sourceValue;
|
||||||
"Uppercase" => sourceValue?.ToString()?.ToUpperInvariant(),
|
|
||||||
"Lowercase" => sourceValue?.ToString()?.ToLowerInvariant(),
|
|
||||||
"Prefix" => $"{rule.Argument}{sourceValue}",
|
|
||||||
"Suffix" => $"{sourceValue}{rule.Argument}",
|
|
||||||
"Replace" => ApplyReplace(sourceValue?.ToString(), rule.Argument),
|
|
||||||
"Constant" => rule.Argument,
|
|
||||||
_ => sourceValue
|
|
||||||
};
|
|
||||||
|
|
||||||
SetPropertyValue(record, targetProp, result);
|
SetPropertyValue(record, targetProp, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string ApplyReplace(string? input, string? argument)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(input)) return string.Empty;
|
|
||||||
if (string.IsNullOrWhiteSpace(argument)) return input;
|
|
||||||
|
|
||||||
var parts = argument.Split("=>", 2, StringSplitOptions.TrimEntries);
|
|
||||||
if (parts.Length != 2) return input;
|
|
||||||
return input.Replace(parts[0], parts[1], StringComparison.OrdinalIgnoreCase);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SetPropertyValue(SalesRecord record, PropertyInfo property, object? value)
|
private static void SetPropertyValue(SalesRecord record, PropertyInfo property, object? value)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using Microsoft.Graph;
|
|||||||
|
|
||||||
namespace TrafagSalesExporter.Services;
|
namespace TrafagSalesExporter.Services;
|
||||||
|
|
||||||
public class SharePointUploadService
|
public class SharePointUploadService : ISharePointUploadService
|
||||||
{
|
{
|
||||||
public async Task UploadAsync(string tenantId, string clientId, string clientSecret,
|
public async Task UploadAsync(string tenantId, string clientId, string clientSecret,
|
||||||
string siteUrl, string exportFolder, string land, string localFilePath)
|
string siteUrl, string exportFolder, string land, string localFilePath)
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
using TrafagSalesExporter.Models;
|
||||||
|
|
||||||
|
namespace TrafagSalesExporter.Services;
|
||||||
|
|
||||||
|
public sealed class SiteExportResult
|
||||||
|
{
|
||||||
|
public required List<SalesRecord> Records { get; init; }
|
||||||
|
public required ExportLog Log { get; init; }
|
||||||
|
public string? FilePath { get; init; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using TrafagSalesExporter.Data;
|
||||||
|
using TrafagSalesExporter.Models;
|
||||||
|
|
||||||
|
namespace TrafagSalesExporter.Services;
|
||||||
|
|
||||||
|
public class SiteExportService : ISiteExportService
|
||||||
|
{
|
||||||
|
private readonly IDbContextFactory<AppDbContext> _dbFactory;
|
||||||
|
private readonly IHanaQueryService _hanaService;
|
||||||
|
private readonly IExcelExportService _excelService;
|
||||||
|
private readonly ISharePointUploadService _sharePointService;
|
||||||
|
private readonly IRecordTransformationService _transformationService;
|
||||||
|
private readonly ILogger<SiteExportService> _logger;
|
||||||
|
|
||||||
|
public SiteExportService(
|
||||||
|
IDbContextFactory<AppDbContext> dbFactory,
|
||||||
|
IHanaQueryService hanaService,
|
||||||
|
IExcelExportService excelService,
|
||||||
|
ISharePointUploadService sharePointService,
|
||||||
|
IRecordTransformationService transformationService,
|
||||||
|
ILogger<SiteExportService> logger)
|
||||||
|
{
|
||||||
|
_dbFactory = dbFactory;
|
||||||
|
_hanaService = hanaService;
|
||||||
|
_excelService = excelService;
|
||||||
|
_sharePointService = sharePointService;
|
||||||
|
_transformationService = transformationService;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<SiteExportResult> ExportAsync(Site site, Action<string>? updateStatus = null)
|
||||||
|
{
|
||||||
|
if (site.HanaServer is null)
|
||||||
|
throw new InvalidOperationException($"Standort '{site.Land}' hat keinen HANA-Server.");
|
||||||
|
|
||||||
|
var sw = Stopwatch.StartNew();
|
||||||
|
var log = new ExportLog
|
||||||
|
{
|
||||||
|
Timestamp = DateTime.Now,
|
||||||
|
SiteId = site.Id,
|
||||||
|
Land = site.Land,
|
||||||
|
TSC = site.TSC
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var db = await _dbFactory.CreateDbContextAsync();
|
||||||
|
var settings = await db.ExportSettings.FirstOrDefaultAsync() ?? new ExportSettings();
|
||||||
|
var spConfig = await db.SharePointConfigs.FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
updateStatus?.Invoke("HANA Abfrage...");
|
||||||
|
var records = await Task.Run(() => _hanaService.GetSalesRecords(
|
||||||
|
site.HanaServer, site.Schema, site.TSC, site.Land, settings.DateFilter));
|
||||||
|
|
||||||
|
updateStatus?.Invoke("Transformationen anwenden...");
|
||||||
|
var rules = await db.FieldTransformationRules
|
||||||
|
.Where(r => r.IsActive && r.SourceSystem == (string.IsNullOrWhiteSpace(site.SourceSystem) ? "SAP" : site.SourceSystem))
|
||||||
|
.OrderBy(r => r.SortOrder)
|
||||||
|
.ToListAsync();
|
||||||
|
_transformationService.Apply(records, rules);
|
||||||
|
|
||||||
|
updateStatus?.Invoke("Excel erstellen...");
|
||||||
|
var outputDir = Path.Combine(AppContext.BaseDirectory, "output");
|
||||||
|
var filePath = _excelService.CreateExcelFile(outputDir, site.TSC, DateTime.UtcNow.Date, records);
|
||||||
|
var fileName = Path.GetFileName(filePath);
|
||||||
|
|
||||||
|
if (spConfig is not null &&
|
||||||
|
!string.IsNullOrWhiteSpace(spConfig.TenantId) &&
|
||||||
|
!string.IsNullOrWhiteSpace(spConfig.ClientId) &&
|
||||||
|
!string.IsNullOrWhiteSpace(spConfig.ClientSecret))
|
||||||
|
{
|
||||||
|
updateStatus?.Invoke("SharePoint Upload...");
|
||||||
|
await _sharePointService.UploadAsync(
|
||||||
|
spConfig.TenantId, spConfig.ClientId, spConfig.ClientSecret,
|
||||||
|
spConfig.SiteUrl, spConfig.ExportFolder, site.Land, filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
sw.Stop();
|
||||||
|
log.Status = "OK";
|
||||||
|
log.RowCount = records.Count;
|
||||||
|
log.FileName = fileName;
|
||||||
|
log.DurationSeconds = sw.Elapsed.TotalSeconds;
|
||||||
|
|
||||||
|
_logger.LogInformation("Export OK: {Land} ({TSC}) - {Rows} Zeilen in {Duration:F1}s",
|
||||||
|
site.Land, site.TSC, records.Count, sw.Elapsed.TotalSeconds);
|
||||||
|
|
||||||
|
return new SiteExportResult
|
||||||
|
{
|
||||||
|
Records = records,
|
||||||
|
Log = log,
|
||||||
|
FilePath = filePath
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
sw.Stop();
|
||||||
|
log.Status = "Error";
|
||||||
|
log.ErrorMessage = ex.Message;
|
||||||
|
log.FileName = string.Empty;
|
||||||
|
log.DurationSeconds = sw.Elapsed.TotalSeconds;
|
||||||
|
|
||||||
|
_logger.LogError(ex, "Export Fehler: {Land} ({TSC})", site.Land, site.TSC);
|
||||||
|
|
||||||
|
return new SiteExportResult
|
||||||
|
{
|
||||||
|
Records = [],
|
||||||
|
Log = log,
|
||||||
|
FilePath = null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
namespace TrafagSalesExporter.Services;
|
||||||
|
|
||||||
|
public sealed class CopyTransformationStrategy : ITransformationStrategy
|
||||||
|
{
|
||||||
|
public string TransformationType => "Copy";
|
||||||
|
public object? Transform(object? sourceValue, string? argument) => sourceValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class UppercaseTransformationStrategy : ITransformationStrategy
|
||||||
|
{
|
||||||
|
public string TransformationType => "Uppercase";
|
||||||
|
public object? Transform(object? sourceValue, string? argument) => sourceValue?.ToString()?.ToUpperInvariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class LowercaseTransformationStrategy : ITransformationStrategy
|
||||||
|
{
|
||||||
|
public string TransformationType => "Lowercase";
|
||||||
|
public object? Transform(object? sourceValue, string? argument) => sourceValue?.ToString()?.ToLowerInvariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class PrefixTransformationStrategy : ITransformationStrategy
|
||||||
|
{
|
||||||
|
public string TransformationType => "Prefix";
|
||||||
|
public object? Transform(object? sourceValue, string? argument) => $"{argument}{sourceValue}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class SuffixTransformationStrategy : ITransformationStrategy
|
||||||
|
{
|
||||||
|
public string TransformationType => "Suffix";
|
||||||
|
public object? Transform(object? sourceValue, string? argument) => $"{sourceValue}{argument}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class ReplaceTransformationStrategy : ITransformationStrategy
|
||||||
|
{
|
||||||
|
public string TransformationType => "Replace";
|
||||||
|
|
||||||
|
public object? Transform(object? sourceValue, string? argument)
|
||||||
|
{
|
||||||
|
var input = sourceValue?.ToString();
|
||||||
|
if (string.IsNullOrEmpty(input))
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(argument))
|
||||||
|
return input;
|
||||||
|
|
||||||
|
var parts = argument.Split("=>", 2, StringSplitOptions.TrimEntries);
|
||||||
|
if (parts.Length != 2)
|
||||||
|
return input;
|
||||||
|
|
||||||
|
return input.Replace(parts[0], parts[1], StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class ConstantTransformationStrategy : ITransformationStrategy
|
||||||
|
{
|
||||||
|
public string TransformationType => "Constant";
|
||||||
|
public object? Transform(object? sourceValue, string? argument) => argument;
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user