sammelxport
This commit is contained in:
@@ -310,41 +310,19 @@
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RefreshLiveDataAsync()
|
||||
private Task RefreshLiveDataAsync()
|
||||
{
|
||||
var runningSiteIds = _dashboardRows
|
||||
.Where(r => Orchestrator.IsExporting(r.SiteId))
|
||||
.Select(r => r.SiteId)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
if (runningSiteIds.Count == 0)
|
||||
{
|
||||
_anyRunning = false;
|
||||
return;
|
||||
}
|
||||
|
||||
using var db = await DbFactory.CreateDbContextAsync();
|
||||
var appLogs = await db.AppEventLogs
|
||||
.Where(l => l.SiteId != null && runningSiteIds.Contains(l.SiteId.Value))
|
||||
.OrderByDescending(l => l.Timestamp)
|
||||
.Take(200)
|
||||
.ToListAsync();
|
||||
|
||||
var latestAppLogsBySite = appLogs
|
||||
.GroupBy(l => l.SiteId!.Value)
|
||||
.ToDictionary(g => g.Key, g => g.OrderByDescending(x => x.Timestamp).First());
|
||||
|
||||
foreach (var row in _dashboardRows)
|
||||
{
|
||||
if (!latestAppLogsBySite.TryGetValue(row.SiteId, out var appLog))
|
||||
if (!Orchestrator.IsExporting(row.SiteId))
|
||||
continue;
|
||||
|
||||
row.LiveMessage = $"{appLog.Category}: {appLog.Message}";
|
||||
row.LiveDetails = appLog.Details ?? string.Empty;
|
||||
row.LiveMessage = Orchestrator.GetExportStatus(row.SiteId);
|
||||
row.LiveDetails = string.Empty;
|
||||
}
|
||||
|
||||
_anyRunning = _dashboardRows.Any(r => Orchestrator.IsExporting(r.SiteId));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private class DashboardRow
|
||||
|
||||
@@ -0,0 +1,347 @@
|
||||
# TrafagSalesExporter Handoff
|
||||
|
||||
Stand: 2026-04-15
|
||||
|
||||
## Zielbild
|
||||
|
||||
Die App wurde von einem reinen BI1/HANA-Exporter zu einer kombinierten Plattform erweitert:
|
||||
|
||||
- `BI1` und `SAGE` bleiben auf direktem HANA-Zugriff
|
||||
- `SAP` läuft separat über SAP Gateway / OData
|
||||
- SAP-Quellen können gelesen, gejoint und auf das zentrale `SalesRecord`-Schema gemappt werden
|
||||
- Standort-Exporte werden lokal als Excel geschrieben
|
||||
- Zusätzlich werden Datensätze in eine zentrale SQLite-Tabelle geschrieben
|
||||
- Ein konsolidierter Export liest aus dieser zentralen Tabelle
|
||||
|
||||
## Wichtigste umgesetzte Funktionen
|
||||
|
||||
### 1. Zentrale Credentials pro Quellsystem
|
||||
|
||||
Es gibt zentrale Zugangsdaten in `ExportSettings` für:
|
||||
|
||||
- `SAP`
|
||||
- `BI1`
|
||||
- `SAGE`
|
||||
|
||||
Zusätzlich gibt es pro Standort optionale Overrides:
|
||||
|
||||
- `UsernameOverride`
|
||||
- `PasswordOverride`
|
||||
|
||||
Auflösungsreihenfolge:
|
||||
|
||||
1. Standort-Override
|
||||
2. zentrale Credentials des Quellsystems
|
||||
3. bei HANA zusätzlich Fallback auf alten `HanaServer.Username/Password`
|
||||
|
||||
## 2. SAP von BI1/HANA getrennt
|
||||
|
||||
`SAP` nutzt nicht mehr den HANA-Pfad, sondern eine eigene Gateway/OData-Strecke.
|
||||
|
||||
Pro SAP-Standort gibt es:
|
||||
|
||||
- `SapServiceUrl`
|
||||
- `SapEntitySet`
|
||||
- `SapEntitySetsCache`
|
||||
- `SapEntitySetsRefreshedAtUtc`
|
||||
|
||||
Refresh der SAP-Quellen erfolgt nur auf Knopfdruck.
|
||||
|
||||
Beispiel Service URL:
|
||||
|
||||
```text
|
||||
http://travt762.sap.trafag.com:8000/sap/opu/odata/sap/ZPOWERBI_EINKAUF_SRV/
|
||||
```
|
||||
|
||||
Wichtig:
|
||||
|
||||
- Service URL immer nur bis zum Service
|
||||
- Entity Set separat auswählen
|
||||
|
||||
## 3. SAP-Quellen, Joins und Feldmappings
|
||||
|
||||
Für SAP gibt es mehrere neue Modelle:
|
||||
|
||||
- `SapSourceDefinition`
|
||||
- `SapJoinDefinition`
|
||||
- `SapFieldMapping`
|
||||
|
||||
Unterstützt wird:
|
||||
|
||||
- mehrere SAP-Quellen pro Standort
|
||||
- Alias pro Quelle
|
||||
- Primärquelle
|
||||
- Join-Definitionen
|
||||
- Mapping von `Alias.Feldname` auf zentrales Schema
|
||||
|
||||
UI-Erweiterungen:
|
||||
|
||||
- `Quellen refreshen`
|
||||
- `Felder aus Quellen laden`
|
||||
- Join-Key-Auswahl aus Metadaten
|
||||
- `Auto-Match` für gleiche Feldnamen zwischen Primärquelle und anderen Quellen
|
||||
|
||||
## 4. Zentrale Datenspeicherung
|
||||
|
||||
Neue Tabelle:
|
||||
|
||||
- `CentralSalesRecords`
|
||||
|
||||
Verwendung:
|
||||
|
||||
- pro Standort werden alte zentrale Sätze dieses Standorts ersetzt
|
||||
- konsolidierte Excel liest aus `CentralSalesRecords`
|
||||
|
||||
Wichtig:
|
||||
|
||||
- zentrale Excel wird nicht appendet
|
||||
- sie wird aus dem aktuellen Zustand der zentralen Tabelle neu erstellt
|
||||
|
||||
## 5. Exportpfade
|
||||
|
||||
Neue Konfigurationsmöglichkeiten:
|
||||
|
||||
Zentral in `Settings`:
|
||||
|
||||
- `LocalSiteExportFolder`
|
||||
- `LocalConsolidatedExportFolder`
|
||||
|
||||
Pro Standort:
|
||||
|
||||
- `LocalExportFolderOverride`
|
||||
|
||||
Fallback wenn leer:
|
||||
|
||||
```text
|
||||
./output
|
||||
```
|
||||
|
||||
relativ zum App-Verzeichnis.
|
||||
|
||||
## 6. SharePoint
|
||||
|
||||
SharePoint-Upload ist optional.
|
||||
|
||||
Wenn keine vollständige SharePoint-Konfiguration vorhanden ist:
|
||||
|
||||
- Excel wird trotzdem lokal erzeugt
|
||||
- kein Upload nach SharePoint
|
||||
|
||||
Benötigte SharePoint-Werte:
|
||||
|
||||
- `Tenant ID`
|
||||
- `Client ID`
|
||||
- `Client Secret`
|
||||
|
||||
Das sind Entra App Registration Werte, nicht normale Benutzer-Credentials.
|
||||
|
||||
## 7. Config Import/Export
|
||||
|
||||
Es gibt JSON-Import/Export der Konfiguration mit Checkbox:
|
||||
|
||||
- mit Secrets
|
||||
- ohne Secrets
|
||||
|
||||
Enthalten sind u. a.:
|
||||
|
||||
- SharePoint Config
|
||||
- ExportSettings
|
||||
- HanaServers
|
||||
- Sites
|
||||
- Transformation Rules
|
||||
- SAP-Quellen
|
||||
- SAP-Joins
|
||||
- SAP-Mappings
|
||||
|
||||
## 8. Logging und Live-Status
|
||||
|
||||
Neue technische Logs über `AppEventLogs`.
|
||||
|
||||
Sichtbar:
|
||||
|
||||
- auf `/logs`
|
||||
- im Dashboard als `Live-Status`
|
||||
|
||||
Geloggt werden u. a.:
|
||||
|
||||
- HANA-Query Start
|
||||
- SAP Refresh
|
||||
- SAP Reads
|
||||
- Transformationen
|
||||
- Excel-Erstellung
|
||||
- zentrale Tabellenspeicherung
|
||||
- Export erfolgreich / fehlgeschlagen
|
||||
|
||||
## 9. Excel öffnen
|
||||
|
||||
Im Dashboard gibt es neben `Export` den Button:
|
||||
|
||||
- `Excel öffnen`
|
||||
|
||||
Dieser nutzt `ExportLogs.FilePath`.
|
||||
|
||||
Voraussetzungen:
|
||||
|
||||
- letzter Export erfolgreich
|
||||
- `FilePath` gespeichert
|
||||
- Datei existiert lokal
|
||||
|
||||
## 10. Management Cockpit
|
||||
|
||||
Es gibt einen neuen Menüpunkt:
|
||||
|
||||
- `Management Cockpit`
|
||||
|
||||
Funktion:
|
||||
|
||||
- Auswahl vorhandener Excel-Dateien
|
||||
- Analyse einer exportierten Standort-Datei
|
||||
- Kennzahlen für Geschäftsinhaber / Management
|
||||
|
||||
Aktuell enthalten:
|
||||
|
||||
- Umsatz
|
||||
- geschätzte Kosten
|
||||
- geschätzte Marge
|
||||
- Rechnungsanzahl
|
||||
- Kundenanzahl
|
||||
- Top Kunden
|
||||
- Top Produktgruppen
|
||||
- Top Sales Owner
|
||||
- Datenqualitätshinweise
|
||||
- automatische Management-Aussagen
|
||||
|
||||
## Wichtige Dateien
|
||||
|
||||
### Modelle
|
||||
|
||||
- `Models/Site.cs`
|
||||
- `Models/ExportSettings.cs`
|
||||
- `Models/ExportLog.cs`
|
||||
- `Models/CentralSalesRecord.cs`
|
||||
- `Models/SapSourceDefinition.cs`
|
||||
- `Models/SapJoinDefinition.cs`
|
||||
- `Models/SapFieldMapping.cs`
|
||||
- `Models/ManagementCockpitModels.cs`
|
||||
- `Models/ConfigTransferPackage.cs`
|
||||
|
||||
### Services
|
||||
|
||||
- `Services/SiteExportService.cs`
|
||||
- `Services/ConsolidatedExportService.cs`
|
||||
- `Services/CentralSalesRecordService.cs`
|
||||
- `Services/SapGatewayService.cs`
|
||||
- `Services/SapCompositionService.cs`
|
||||
- `Services/ConfigTransferService.cs`
|
||||
- `Services/AppEventLogService.cs`
|
||||
- `Services/ManagementCockpitService.cs`
|
||||
- `Services/DatabaseInitializationService.cs`
|
||||
- `Services/ExportOrchestrationService.cs`
|
||||
|
||||
### UI
|
||||
|
||||
- `Components/Pages/Standorte.razor`
|
||||
- `Components/Pages/Settings.razor`
|
||||
- `Components/Pages/Dashboard.razor`
|
||||
- `Components/Pages/Logs.razor`
|
||||
- `Components/Pages/ManagementCockpit.razor`
|
||||
- `Components/Layout/NavMenu.razor`
|
||||
|
||||
## Datenbank / Migrationen
|
||||
|
||||
Viele Änderungen laufen über `DatabaseInitializationService`.
|
||||
|
||||
Wichtige neue oder erweiterte Tabellen/Felder:
|
||||
|
||||
- `Sites`
|
||||
- `UsernameOverride`
|
||||
- `PasswordOverride`
|
||||
- `SapServiceUrl`
|
||||
- `SapEntitySet`
|
||||
- `SapEntitySetsCache`
|
||||
- `SapEntitySetsRefreshedAtUtc`
|
||||
- `LocalExportFolderOverride`
|
||||
- `ExportSettings`
|
||||
- zentrale SAP/BI1/SAGE Credentials
|
||||
- `LocalSiteExportFolder`
|
||||
- `LocalConsolidatedExportFolder`
|
||||
- `DebugLoggingEnabled`
|
||||
- `ExportLogs`
|
||||
- `FilePath`
|
||||
- neue Tabellen:
|
||||
- `AppEventLogs`
|
||||
- `CentralSalesRecords`
|
||||
- SAP-Konfigtabellen
|
||||
|
||||
## Aktuell offenes Hauptproblem
|
||||
|
||||
### Zentrale Speicherung hängt noch
|
||||
|
||||
Die große Problemstelle war die zentrale SQLite-Speicherung.
|
||||
|
||||
Bereits probiert:
|
||||
|
||||
- EF `RemoveRange + SaveChanges`
|
||||
- EF Batch-Speichern
|
||||
- Dashboard-Polling reduziert
|
||||
- SQLite WAL + busy timeout
|
||||
- direkte SQLite-Inserts in einer großen Transaktion
|
||||
- jetzt: kleine abgeschlossene Transaktionen pro Batch
|
||||
|
||||
Aktueller Stand:
|
||||
|
||||
- zentrale Excel ist jetzt sehr schnell
|
||||
- das Hängen wurde stark eingegrenzt
|
||||
- zuletzt wurde der Schreibpfad so umgebaut, dass:
|
||||
- Löschen in eigener kurzer Transaktion läuft
|
||||
- Inserts batchweise mit Commit pro Batch laufen
|
||||
|
||||
Datei:
|
||||
|
||||
- `Services/CentralSalesRecordService.cs`
|
||||
|
||||
Die nächste Session sollte genau dort weiter debuggen, falls es noch hängt.
|
||||
|
||||
Wichtig:
|
||||
|
||||
- Das Problem ist nicht SAP
|
||||
- nicht SharePoint
|
||||
- nicht mehr der große EF-Insert
|
||||
- sondern sehr wahrscheinlich SQLite-Commit/Lock-Verhalten rund um die zentrale Tabelle
|
||||
|
||||
## Letzte bekannte Beobachtung
|
||||
|
||||
Der User meldete zuletzt:
|
||||
|
||||
- vorher Hänger bei `Zentrale Tabelle: Abschluss speichern...`
|
||||
- danach wurde auf Commit pro Batch umgestellt
|
||||
- neue Session soll testen, ob es jetzt bei
|
||||
- `Batch x/y speichern...`
|
||||
- `Batch x/y abschliessen...`
|
||||
- oder gar nicht mehr hängt
|
||||
|
||||
## Build-Status
|
||||
|
||||
Letzter Build:
|
||||
|
||||
```text
|
||||
dotnet build TrafagSalesExporter.sln
|
||||
```
|
||||
|
||||
Ergebnis:
|
||||
|
||||
- erfolgreich
|
||||
- bekannte Warnungen bleiben:
|
||||
- SAP HANA Architekturwarnung `MSB3270`
|
||||
- MudBlazor Analyzer `Dense`
|
||||
|
||||
## Hinweise für nächste Session
|
||||
|
||||
1. Zuerst aktuellen Export testen
|
||||
2. Genaue letzte Live-Status-Meldung notieren
|
||||
3. `Services/CentralSalesRecordService.cs` prüfen
|
||||
4. Falls nötig:
|
||||
- SQLite pragmas weiter anpassen
|
||||
- zentrale Tabelle temporär ganz abschaltbar machen
|
||||
- oder Schreiben über separate DB / Queue entkoppeln
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
# Next Steps
|
||||
|
||||
Stand: 2026-04-15
|
||||
|
||||
## 1. Erstes Ziel
|
||||
|
||||
Prüfen, ob die aktuelle Version beim Standort-Export noch in der zentralen SQLite-Speicherung hängen bleibt.
|
||||
|
||||
Wichtig:
|
||||
|
||||
- App neu starten
|
||||
- denselben Standort erneut exportieren
|
||||
- letzte sichtbare `Live-Status`-Meldung exakt notieren
|
||||
|
||||
Interessant sind vor allem diese Fälle:
|
||||
|
||||
- `Zentrale Tabelle: Batch x/y speichern...`
|
||||
- `Zentrale Tabelle: Batch x/y abschliessen...`
|
||||
- `Zentrale Tabelle aktualisiert`
|
||||
- `Export erfolgreich`
|
||||
|
||||
## 2. Hauptverdächtiger
|
||||
|
||||
Datei:
|
||||
|
||||
- `Services/CentralSalesRecordService.cs`
|
||||
|
||||
Aktueller Stand:
|
||||
|
||||
- alte Sätze werden in eigener Transaktion gelöscht
|
||||
- Inserts laufen in Batches von 25
|
||||
- jeder Batch wird separat committed
|
||||
|
||||
Wenn es noch hängt, dort zuerst ansetzen.
|
||||
|
||||
## 3. Falls es weiter hängt
|
||||
|
||||
In dieser Reihenfolge prüfen:
|
||||
|
||||
1. Batchgröße weiter reduzieren
|
||||
- z. B. `10` statt `25`
|
||||
2. Direkt vor und direkt nach `transaction.CommitAsync()` zusätzlich technische Logs setzen
|
||||
3. Prüfen, ob parallel noch andere SQLite-Zugriffe laufen
|
||||
4. Optional zentrale Speicherung vorübergehend per Setting deaktivierbar machen
|
||||
5. Falls nötig zentrale Speicherung in separate DB-Datei auslagern
|
||||
|
||||
## 4. Dashboard / UI prüfen
|
||||
|
||||
Zu testen:
|
||||
|
||||
- `Excel öffnen` wird nach neuem erfolgreichen Export aktiv
|
||||
- `Export erfolgreich` zeigt `Pfad=...`
|
||||
- Dashboard-Live-Status setzt sich nach Abschluss sauber zurück
|
||||
|
||||
Dateien:
|
||||
|
||||
- `Components/Pages/Dashboard.razor`
|
||||
- `Services/SiteExportService.cs`
|
||||
- `Models/ExportLog.cs`
|
||||
|
||||
## 5. SAP-Funktionalität kurz gegenprüfen
|
||||
|
||||
Zu testen:
|
||||
|
||||
- `Quellen refreshen`
|
||||
- `Felder aus Quellen laden`
|
||||
- `Auto-Match`
|
||||
- SAP-Export eines Standorts
|
||||
|
||||
Dateien:
|
||||
|
||||
- `Components/Pages/Standorte.razor`
|
||||
- `Services/SapGatewayService.cs`
|
||||
- `Services/SapCompositionService.cs`
|
||||
|
||||
## 6. Management Cockpit prüfen
|
||||
|
||||
Zu testen:
|
||||
|
||||
- vorhandene Excel-Datei auswählbar
|
||||
- Analyse läuft
|
||||
- Kennzahlen plausibel
|
||||
|
||||
Dateien:
|
||||
|
||||
- `Components/Pages/ManagementCockpit.razor`
|
||||
- `Services/ManagementCockpitService.cs`
|
||||
|
||||
## 7. Wenn Stabilität vor Funktion geht
|
||||
|
||||
Sinnvolle pragmatische Zwischenlösung:
|
||||
|
||||
- zentrale SQLite-Speicherung per Setting abschaltbar machen
|
||||
- Export lokal und zentral Excel weiter erlauben
|
||||
- zentrale DB erst wieder aktivieren, wenn der Commit-Pfad stabil ist
|
||||
|
||||
## 8. Referenzdatei
|
||||
|
||||
Für den vollständigen Kontext zuerst lesen:
|
||||
|
||||
- `HANDOFF_2026-04-15.md`
|
||||
|
||||
@@ -11,7 +11,7 @@ builder.Services.AddRazorComponents()
|
||||
builder.Services.AddMudServices();
|
||||
|
||||
builder.Services.AddDbContextFactory<AppDbContext>(options =>
|
||||
options.UseSqlite("Data Source=trafag_exporter.db;Default Timeout=10"));
|
||||
options.UseSqlite("Data Source=trafag_exporter.db;Default Timeout=60"));
|
||||
|
||||
builder.Services.AddSingleton<IHanaQueryService, HanaQueryService>();
|
||||
builder.Services.AddSingleton<IExcelExportService, ExcelExportService>();
|
||||
|
||||
@@ -39,6 +39,7 @@ public class CentralSalesRecordService : ICentralSalesRecordService
|
||||
|
||||
updateStatus?.Invoke("Zentrale Tabelle: neue Saetze vorbereiten...");
|
||||
await InsertRecordsInCommittedBatchesAsync(connection, site, recordList, updateStatus);
|
||||
updateStatus?.Invoke("Zentrale Tabelle aktualisiert");
|
||||
|
||||
await _appEventLogService.WriteAsync(
|
||||
"Export",
|
||||
|
||||
@@ -45,6 +45,7 @@ public class DatabaseInitializationService : IDatabaseInitializationService
|
||||
private static void EnsureSchema(AppDbContext db)
|
||||
{
|
||||
EnsureSitesTableSupportsOptionalHanaServer(db);
|
||||
RepairBrokenSiteForeignKeys(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");
|
||||
@@ -175,6 +176,221 @@ FROM Sites_old;";
|
||||
enableFk.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
private static void RepairBrokenSiteForeignKeys(AppDbContext db)
|
||||
{
|
||||
var conn = db.Database.GetDbConnection();
|
||||
if (conn.State != ConnectionState.Open)
|
||||
conn.Open();
|
||||
|
||||
var tablesToRepair = new[]
|
||||
{
|
||||
("ExportLogs", GetExportLogsCreateSql()),
|
||||
("AppEventLogs", GetAppEventLogsCreateSql()),
|
||||
("CentralSalesRecords", GetCentralSalesRecordsCreateSql()),
|
||||
("SapSourceDefinitions", GetSapSourceDefinitionsCreateSql()),
|
||||
("SapJoinDefinitions", GetSapJoinDefinitionsCreateSql()),
|
||||
("SapFieldMappings", GetSapFieldMappingsCreateSql())
|
||||
};
|
||||
|
||||
foreach (var (tableName, createSql) in tablesToRepair)
|
||||
{
|
||||
if (TableReferencesSitesOld(conn, tableName))
|
||||
RebuildTable(conn, tableName, createSql);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TableReferencesSitesOld(System.Data.Common.DbConnection connection, string tableName)
|
||||
{
|
||||
using var command = connection.CreateCommand();
|
||||
command.CommandText = "SELECT sql FROM sqlite_master WHERE type = 'table' AND name = $tableName;";
|
||||
|
||||
var parameter = command.CreateParameter();
|
||||
parameter.ParameterName = "$tableName";
|
||||
parameter.Value = tableName;
|
||||
command.Parameters.Add(parameter);
|
||||
|
||||
var sql = command.ExecuteScalar()?.ToString() ?? string.Empty;
|
||||
return sql.Contains("Sites_old", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static void RebuildTable(System.Data.Common.DbConnection connection, string tableName, string createSql)
|
||||
{
|
||||
using var disableFk = connection.CreateCommand();
|
||||
disableFk.CommandText = "PRAGMA foreign_keys = OFF;";
|
||||
disableFk.ExecuteNonQuery();
|
||||
|
||||
using var transaction = connection.BeginTransaction();
|
||||
|
||||
var tempTableName = $"{tableName}_repair_old";
|
||||
|
||||
using (var rename = connection.CreateCommand())
|
||||
{
|
||||
rename.Transaction = transaction;
|
||||
rename.CommandText = $"ALTER TABLE {tableName} RENAME TO {tempTableName};";
|
||||
rename.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
using (var create = connection.CreateCommand())
|
||||
{
|
||||
create.Transaction = transaction;
|
||||
create.CommandText = createSql;
|
||||
create.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
var columns = GetSharedColumns(connection, transaction, tableName, tempTableName);
|
||||
if (columns.Count > 0)
|
||||
{
|
||||
var columnList = string.Join(", ", columns);
|
||||
|
||||
using var copy = connection.CreateCommand();
|
||||
copy.Transaction = transaction;
|
||||
copy.CommandText = $"INSERT INTO {tableName} ({columnList}) SELECT {columnList} FROM {tempTableName};";
|
||||
copy.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
using (var drop = connection.CreateCommand())
|
||||
{
|
||||
drop.Transaction = transaction;
|
||||
drop.CommandText = $"DROP TABLE {tempTableName};";
|
||||
drop.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
transaction.Commit();
|
||||
|
||||
using var enableFk = connection.CreateCommand();
|
||||
enableFk.CommandText = "PRAGMA foreign_keys = ON;";
|
||||
enableFk.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
private static List<string> GetSharedColumns(System.Data.Common.DbConnection connection, System.Data.Common.DbTransaction transaction, string newTableName, string oldTableName)
|
||||
{
|
||||
var newColumns = GetTableColumns(connection, transaction, newTableName);
|
||||
var oldColumns = GetTableColumns(connection, transaction, oldTableName);
|
||||
|
||||
return newColumns.Where(oldColumns.Contains).ToList();
|
||||
}
|
||||
|
||||
private static HashSet<string> GetTableColumns(System.Data.Common.DbConnection connection, System.Data.Common.DbTransaction transaction, string tableName)
|
||||
{
|
||||
var columns = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
using var command = connection.CreateCommand();
|
||||
command.Transaction = transaction;
|
||||
command.CommandText = $"PRAGMA table_info({tableName})";
|
||||
|
||||
using var reader = command.ExecuteReader();
|
||||
while (reader.Read())
|
||||
{
|
||||
var name = reader["name"]?.ToString();
|
||||
if (!string.IsNullOrWhiteSpace(name))
|
||||
columns.Add(name);
|
||||
}
|
||||
|
||||
return columns;
|
||||
}
|
||||
|
||||
private static string GetExportLogsCreateSql() => @"
|
||||
CREATE TABLE ExportLogs (
|
||||
Id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
Timestamp TEXT NOT NULL,
|
||||
SiteId INTEGER NOT NULL,
|
||||
Land TEXT NOT NULL,
|
||||
TSC TEXT NOT NULL,
|
||||
Status TEXT NOT NULL,
|
||||
RowCount INTEGER NOT NULL,
|
||||
ErrorMessage TEXT NULL,
|
||||
FileName TEXT NOT NULL DEFAULT '',
|
||||
FilePath TEXT NOT NULL DEFAULT '',
|
||||
DurationSeconds REAL NOT NULL,
|
||||
FOREIGN KEY (SiteId) REFERENCES Sites (Id)
|
||||
);";
|
||||
|
||||
private static string GetAppEventLogsCreateSql() => @"
|
||||
CREATE TABLE AppEventLogs (
|
||||
Id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
Timestamp TEXT NOT NULL,
|
||||
Level TEXT NOT NULL,
|
||||
Category TEXT NOT NULL,
|
||||
SiteId INTEGER NULL,
|
||||
Land TEXT NOT NULL,
|
||||
Message TEXT NOT NULL,
|
||||
Details TEXT NOT NULL,
|
||||
FOREIGN KEY (SiteId) REFERENCES Sites (Id)
|
||||
);";
|
||||
|
||||
private static string GetCentralSalesRecordsCreateSql() => @"
|
||||
CREATE TABLE CentralSalesRecords (
|
||||
Id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
StoredAtUtc TEXT NOT NULL,
|
||||
SiteId INTEGER NOT NULL,
|
||||
SourceSystem TEXT NOT NULL,
|
||||
ExtractionDate TEXT NOT NULL,
|
||||
Tsc TEXT NOT NULL,
|
||||
InvoiceNumber TEXT NOT NULL,
|
||||
PositionOnInvoice INTEGER NOT NULL,
|
||||
Material TEXT NOT NULL,
|
||||
Name TEXT NOT NULL,
|
||||
ProductGroup TEXT NOT NULL,
|
||||
Quantity TEXT NOT NULL,
|
||||
SupplierNumber TEXT NOT NULL,
|
||||
SupplierName TEXT NOT NULL,
|
||||
SupplierCountry TEXT NOT NULL,
|
||||
CustomerNumber TEXT NOT NULL,
|
||||
CustomerName TEXT NOT NULL,
|
||||
CustomerCountry TEXT NOT NULL,
|
||||
CustomerIndustry TEXT NOT NULL,
|
||||
StandardCost TEXT NOT NULL,
|
||||
StandardCostCurrency TEXT NOT NULL,
|
||||
PurchaseOrderNumber TEXT NOT NULL,
|
||||
SalesPriceValue TEXT NOT NULL,
|
||||
SalesCurrency TEXT NOT NULL,
|
||||
Incoterms2020 TEXT NOT NULL,
|
||||
SalesResponsibleEmployee TEXT NOT NULL,
|
||||
InvoiceDate TEXT NULL,
|
||||
OrderDate TEXT NULL,
|
||||
Land TEXT NOT NULL,
|
||||
DocumentType TEXT NOT NULL,
|
||||
FOREIGN KEY (SiteId) REFERENCES Sites (Id)
|
||||
);";
|
||||
|
||||
private static string GetSapSourceDefinitionsCreateSql() => @"
|
||||
CREATE TABLE SapSourceDefinitions (
|
||||
Id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
SiteId INTEGER NOT NULL,
|
||||
Alias TEXT NOT NULL,
|
||||
EntitySet TEXT NOT NULL,
|
||||
IsPrimary INTEGER NOT NULL DEFAULT 0,
|
||||
IsActive INTEGER NOT NULL DEFAULT 1,
|
||||
SortOrder INTEGER NOT NULL DEFAULT 0,
|
||||
FOREIGN KEY (SiteId) REFERENCES Sites (Id)
|
||||
);";
|
||||
|
||||
private static string GetSapJoinDefinitionsCreateSql() => @"
|
||||
CREATE TABLE SapJoinDefinitions (
|
||||
Id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
SiteId INTEGER NOT NULL,
|
||||
LeftAlias TEXT NOT NULL,
|
||||
RightAlias TEXT NOT NULL,
|
||||
LeftKeys TEXT NOT NULL,
|
||||
RightKeys TEXT NOT NULL,
|
||||
JoinType TEXT NOT NULL DEFAULT 'Left',
|
||||
IsActive INTEGER NOT NULL DEFAULT 1,
|
||||
SortOrder INTEGER NOT NULL DEFAULT 0,
|
||||
FOREIGN KEY (SiteId) REFERENCES Sites (Id)
|
||||
);";
|
||||
|
||||
private static string GetSapFieldMappingsCreateSql() => @"
|
||||
CREATE TABLE SapFieldMappings (
|
||||
Id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
SiteId INTEGER NOT NULL,
|
||||
TargetField TEXT NOT NULL,
|
||||
SourceExpression TEXT NOT NULL,
|
||||
IsRequired INTEGER NOT NULL DEFAULT 0,
|
||||
IsActive INTEGER NOT NULL DEFAULT 1,
|
||||
SortOrder INTEGER NOT NULL DEFAULT 0,
|
||||
FOREIGN KEY (SiteId) REFERENCES Sites (Id)
|
||||
);";
|
||||
|
||||
private static void AddColumnIfMissing(AppDbContext db, string table, string column, string type)
|
||||
{
|
||||
var conn = db.Database.GetDbConnection();
|
||||
|
||||
Reference in New Issue
Block a user