Convert TrafagSalesExporter from console app to Blazor Server app with MudBlazor UI

- Replaced console app with .NET 8 Blazor Server architecture
- Added EF Core SQLite database (trafag_exporter.db) with auto-seed data
- Models: HanaServer, Site, SharePointConfig, ExportSettings, ExportLog, SalesRecord
- Services: HanaQueryService (with configurable dateFilter), ExcelExportService,
  SharePointUploadService, ExportOrchestrationService (with live status events),
  TimerBackgroundService (scheduled daily export)
- MudBlazor UI pages: Dashboard (export status + manual trigger), Standorte
  (HANA server + site CRUD), Settings (SharePoint + timer config), Logs (filtered view)
- SAP HANA queries unchanged (INV + CRN with exact SAP B1 table joins)
- SharePoint upload via Microsoft Graph with app registration auth

https://claude.ai/code/session_012heAXNMbbyxqYf2S2HrKLj
This commit is contained in:
Claude
2026-04-09 14:00:44 +00:00
parent 2f56082adc
commit 8524631508
23 changed files with 1327 additions and 154 deletions
+33 -89
View File
@@ -1,100 +1,44 @@
using Microsoft.Extensions.Configuration;
using Microsoft.EntityFrameworkCore;
using MudBlazor.Services;
using TrafagSalesExporter.Data;
using TrafagSalesExporter.Services;
namespace TrafagSalesExporter;
var builder = WebApplication.CreateBuilder(args);
internal static class Program
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
builder.Services.AddMudServices();
builder.Services.AddDbContextFactory<AppDbContext>(options =>
options.UseSqlite("Data Source=trafag_exporter.db"));
builder.Services.AddSingleton<HanaQueryService>();
builder.Services.AddSingleton<ExcelExportService>();
builder.Services.AddSingleton<SharePointUploadService>();
builder.Services.AddSingleton<ExportOrchestrationService>();
builder.Services.AddSingleton<TimerBackgroundService>();
builder.Services.AddHostedService(sp => sp.GetRequiredService<TimerBackgroundService>());
var app = builder.Build();
using (var scope = app.Services.CreateScope())
{
private static async Task Main()
{
var config = new ConfigurationBuilder()
.SetBasePath(AppContext.BaseDirectory)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: false)
.Build();
var appConfig = config.Get<AppConfig>() ?? throw new InvalidOperationException("Konfiguration konnte nicht geladen werden.");
var hanaService = new HanaQueryService();
var excelService = new ExcelExportService();
var sharePointService = new SharePointUploadService(
appConfig.SharePoint.TenantId,
appConfig.SharePoint.ClientId,
appConfig.SharePoint.ClientSecret,
appConfig.SharePoint.SiteUrl,
appConfig.SharePoint.ExportFolder);
var outputDir = Path.Combine(AppContext.BaseDirectory, "output");
foreach (var site in appConfig.Sites)
{
try
{
Log($"Starte Standort: {site.Land} ({site.Schema})");
if (!appConfig.HanaServers.TryGetValue(site.Server, out var serverConfig))
{
throw new InvalidOperationException($"HANA Server-Konfiguration '{site.Server}' nicht gefunden.");
}
var records = hanaService.GetSalesRecords(
serverConfig.Host,
serverConfig.Port,
serverConfig.Username,
serverConfig.Password,
site.Schema,
site.TSC,
site.Land);
var filePath = excelService.CreateExcelFile(outputDir, site.TSC, DateTime.UtcNow.Date, records);
Log($"Excel erzeugt: {filePath}");
await sharePointService.UploadAsync(site.Land, filePath);
Log($"Upload abgeschlossen: {site.Land}");
}
catch (Exception ex)
{
Log($"Fehler bei Standort {site.Land}: {ex.Message}");
}
}
Log("Export beendet.");
}
private static void Log(string message)
{
Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {message}");
}
var dbFactory = scope.ServiceProvider.GetRequiredService<IDbContextFactory<AppDbContext>>();
using var db = await dbFactory.CreateDbContextAsync();
await db.Database.EnsureCreatedAsync();
AppDbContext.SeedIfEmpty(db);
}
public class AppConfig
if (!app.Environment.IsDevelopment())
{
public Dictionary<string, HanaServerConfig> HanaServers { get; set; } = new();
public List<SiteConfig> Sites { get; set; } = new();
public SharePointConfig SharePoint { get; set; } = new();
public string DateFilter { get; set; } = "2025-01-01";
app.UseHsts();
}
public class HanaServerConfig
{
public string Host { get; set; } = string.Empty;
public int Port { get; set; }
public string Username { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty;
}
app.UseStaticFiles();
app.UseAntiforgery();
public class SiteConfig
{
public string Schema { get; set; } = string.Empty;
public string Server { get; set; } = string.Empty;
public string TSC { get; set; } = string.Empty;
public string Land { get; set; } = string.Empty;
}
app.MapRazorComponents<TrafagSalesExporter.Components.App>()
.AddInteractiveServerRenderMode();
public class SharePointConfig
{
public string SiteUrl { get; set; } = string.Empty;
public string ExportFolder { get; set; } = string.Empty;
public string TenantId { get; set; } = string.Empty;
public string ClientId { get; set; } = string.Empty;
public string ClientSecret { get; set; } = string.Empty;
}
app.Run();