Add provisional Germany Alphaplan import

This commit is contained in:
2026-05-20 09:26:28 +02:00
parent 1b898a1efe
commit 930f124aae
7 changed files with 291 additions and 16 deletions
@@ -2,6 +2,49 @@
Stand: 2026-05-20 Stand: 2026-05-20
## Nachtrag 2026-05-20 DE Alphaplan-Excel provisorisch eingebaut
Erledigt:
- Deutschland wird als manueller Excel-Standort vorbereitet:
- `TSC = TRDE`
- `Land = Deutschland`
- `SourceSystem = MANUAL_EXCEL`
- neuer Standort ist standardmaessig inaktiv, damit Export-All nicht ohne Datei scheitert
- Alphaplan-Mapping wird automatisch geseedet:
- `NettoPreisGesamtX` -> `SalesPriceValue`
- `Belegnummer` -> `InvoiceNumber`
- `Position` -> `PositionOnInvoice`
- `ArtikelNummer` -> `Material`
- `ArtikelBezeichnung` -> `Name`
- `Warengruppen-Bezeichnung` -> `ProductGroup`
- `Anz. VE` -> `Quantity`
- `Name/Land Lieferant`, `Name/Land Kunde`, `Branche`, `Versandbedingung`
- `Belegdatum-Rechnung` -> `PostingDate` und `InvoiceDate`
- `DocumentType = Alphaplan Excel`
- Datei erhalten:
```text
docs/2025_DataExport_DE.xlsx
```
Bedienung:
1. App starten.
2. `Standorte` oeffnen.
3. Deutschland / `TRDE` oeffnen.
4. Alphaplan-Excel hochladen oder Pfad setzen.
5. Standort aktivieren.
6. Standortexport fuer DE ausfuehren.
7. Danach zentrale Excel erzeugen; DE ist dann in `CentralSalesRecords` und im Endexcel enthalten.
Offen fachlich:
- Komplette Summe `NettoPreisGesamtX`: `4'154'690.05 EUR`.
- Nur `Land Kunde = Deutschland`: `3'455'276.64 EUR`.
- Sollwert DE: `3'635'923.00 EUR`.
- Finance/Munir muss bestaetigen, welche Kundenlaender oder Filter zum offiziellen DE-Ist gehoeren.
## Nachtrag 2026-05-20 IIS 500 aktueller Stand ## Nachtrag 2026-05-20 IIS 500 aktueller Stand
Vollstaendige Doku: Vollstaendige Doku:
@@ -13,6 +13,7 @@ public class DatabaseSeedService : IDatabaseSeedService
EnsureSourceSystemDefinitions(db); EnsureSourceSystemDefinitions(db);
EnsureCentralHanaServerRecords(db); EnsureCentralHanaServerRecords(db);
EnsureSpainManualExcelSite(db); EnsureSpainManualExcelSite(db);
EnsureGermanyManualExcelSite(db);
EnsureUkManualExcelFolder(db); EnsureUkManualExcelFolder(db);
EnsureSapODataDachSite(db); EnsureSapODataDachSite(db);
EnsureFinanceReferenceDefaults(db); EnsureFinanceReferenceDefaults(db);
@@ -288,6 +289,61 @@ public class DatabaseSeedService : IDatabaseSeedService
db.SaveChanges(); db.SaveChanges();
} }
private static void EnsureGermanyManualExcelSite(AppDbContext db)
{
if (db.Sites.Count() <= 1)
return;
var existing = db.Sites
.OrderBy(x => x.Id)
.FirstOrDefault(x =>
x.TSC == "TRDE" ||
x.Land == "Deutschland" ||
x.Land == "Germany");
if (existing is null)
{
existing = new Site
{
Schema = string.Empty,
TSC = "TRDE",
Land = "Deutschland",
SourceSystem = "MANUAL_EXCEL",
IsActive = false
};
db.Sites.Add(existing);
db.SaveChanges();
}
else
{
var changed = false;
if (string.IsNullOrWhiteSpace(existing.TSC))
{
existing.TSC = "TRDE";
changed = true;
}
if (string.IsNullOrWhiteSpace(existing.Land))
{
existing.Land = "Deutschland";
changed = true;
}
if (string.IsNullOrWhiteSpace(existing.SourceSystem))
{
existing.SourceSystem = "MANUAL_EXCEL";
changed = true;
}
if (changed)
db.SaveChanges();
}
if (CanSeedSiteDependentTable(db, "ManualExcelColumnMappings"))
EnsureGermanyManualExcelMapping(db, existing.Id);
}
private static void EnsureUkManualExcelFolder(AppDbContext db) private static void EnsureUkManualExcelFolder(AppDbContext db)
{ {
var existing = db.Sites var existing = db.Sites
@@ -411,6 +467,91 @@ public class DatabaseSeedService : IDatabaseSeedService
db.SaveChanges(); db.SaveChanges();
} }
private static void EnsureGermanyManualExcelMapping(AppDbContext db, int siteId)
{
var mappings = new (string Target, string Source, bool Required)[]
{
(nameof(SalesRecord.ExtractionDate), "Export-Datum", false),
(nameof(SalesRecord.Tsc), "=TRDE", false),
(nameof(SalesRecord.Land), "=Deutschland", false),
(nameof(SalesRecord.InvoiceNumber), "Belegnummer", true),
(nameof(SalesRecord.PositionOnInvoice), "Position", false),
(nameof(SalesRecord.Material), "ArtikelNummer", false),
(nameof(SalesRecord.Name), "ArtikelBezeichnung", false),
(nameof(SalesRecord.ProductGroup), "Warengruppen-Bezeichnung", false),
(nameof(SalesRecord.Quantity), "Anz. VE", false),
(nameof(SalesRecord.SupplierNumber), "Lieferanten Nummer", false),
(nameof(SalesRecord.SupplierName), "Name Lieferant", false),
(nameof(SalesRecord.SupplierCountry), "Land Lieferant", false),
(nameof(SalesRecord.CustomerNumber), "AdressNummer-Kunde", false),
(nameof(SalesRecord.CustomerName), "Name Kunde", false),
(nameof(SalesRecord.CustomerCountry), "Land Kunde", false),
(nameof(SalesRecord.CustomerIndustry), "Branche", false),
(nameof(SalesRecord.StandardCost), "EinstandsPreis", false),
(nameof(SalesRecord.StandardCostCurrency), "W\u00e4hrung", false),
(nameof(SalesRecord.SalesPriceValue), "NettoPreisGesamtX", true),
(nameof(SalesRecord.SalesCurrency), "W\u00e4hrung", false),
(nameof(SalesRecord.DocumentCurrency), "W\u00e4hrung", false),
(nameof(SalesRecord.CompanyCurrency), "W\u00e4hrung", false),
(nameof(SalesRecord.Incoterms2020), "Versandbedingung", false),
(nameof(SalesRecord.SalesResponsibleEmployee), "AdressNummer_V", false),
(nameof(SalesRecord.PostingDate), "Belegdatum-Rechnung", false),
(nameof(SalesRecord.InvoiceDate), "Belegdatum-Rechnung", false),
(nameof(SalesRecord.OrderDate), "BelegDatum Auftrag", false),
(nameof(SalesRecord.DocumentType), "=Alphaplan Excel", false)
};
var changed = false;
for (var i = 0; i < mappings.Length; i++)
{
var mapping = db.ManualExcelColumnMappings
.OrderBy(x => x.Id)
.FirstOrDefault(x => x.SiteId == siteId && x.TargetField == mappings[i].Target);
if (mapping is null)
{
db.ManualExcelColumnMappings.Add(new ManualExcelColumnMapping
{
SiteId = siteId,
TargetField = mappings[i].Target,
SourceHeader = mappings[i].Source,
IsRequired = mappings[i].Required,
IsActive = true,
SortOrder = i
});
changed = true;
continue;
}
if (mapping.SourceHeader != mappings[i].Source)
{
mapping.SourceHeader = mappings[i].Source;
changed = true;
}
if (mapping.IsRequired != mappings[i].Required)
{
mapping.IsRequired = mappings[i].Required;
changed = true;
}
if (!mapping.IsActive)
{
mapping.IsActive = true;
changed = true;
}
if (mapping.SortOrder != i)
{
mapping.SortOrder = i;
changed = true;
}
}
if (changed)
db.SaveChanges();
}
private static void EnsureSapODataDachSite(AppDbContext db) private static void EnsureSapODataDachSite(AppDbContext db)
{ {
if (db.Sites.Count() <= 1) if (db.Sites.Count() <= 1)
@@ -89,6 +89,18 @@ public class DatabaseInitializationServiceTests : IDisposable
Assert.Contains(db.HanaServers, x => x.SourceSystem == "BI1"); Assert.Contains(db.HanaServers, x => x.SourceSystem == "BI1");
Assert.Contains(db.HanaServers, x => x.SourceSystem == "SAGE"); Assert.Contains(db.HanaServers, x => x.SourceSystem == "SAGE");
var germany = Assert.Single(db.Sites, x => x.TSC == "TRDE" && x.Land == "Deutschland");
Assert.Equal("MANUAL_EXCEL", germany.SourceSystem);
Assert.False(germany.IsActive);
Assert.Contains(db.ManualExcelColumnMappings, x =>
x.SiteId == germany.Id &&
x.TargetField == nameof(SalesRecord.SalesPriceValue) &&
x.SourceHeader == "NettoPreisGesamtX" &&
x.IsRequired);
Assert.Contains(db.ManualExcelColumnMappings, x =>
x.SiteId == germany.Id &&
x.TargetField == nameof(SalesRecord.DocumentType) &&
x.SourceHeader == "=Alphaplan Excel");
Assert.Equal(2, db.FieldTransformationRules.Count(x => x.SourceSystem == "MANUAL_EXCEL")); Assert.Equal(2, db.FieldTransformationRules.Count(x => x.SourceSystem == "MANUAL_EXCEL"));
} }
Binary file not shown.
@@ -144,6 +144,16 @@ Ob FKDAT fuer diese Quelle fachlich Buchungsdatum oder Fakturadatum ist, muss be
## DE ## DE
Aktuelle Quelle:
```text
SourceSystem = MANUAL_EXCEL
Fachlich = Alphaplan Excel
TSC = TRDE
Land = Deutschland
Aktueller Datei-/Teststand = docs/2025_DataExport_DE.xlsx
```
Aktueller Referenzwert: Aktueller Referenzwert:
```text ```text
@@ -151,30 +161,56 @@ FinanceReference.DE.LocalCurrencyValue = 3'635'923
Hauswaehrung = EUR Hauswaehrung = EUR
``` ```
Formel im Vergleich: Provisorisches Import-Mapping:
```text ```text
Ist = gemeinsame Auswahl aus CentralSalesRecords fuer Key DE SalesPriceValue = NettoPreisGesamtX
InvoiceNumber = Belegnummer
PositionOnInvoice = Position
Material = ArtikelNummer
Name = ArtikelBezeichnung
ProductGroup = Warengruppen-Bezeichnung
Quantity = Anz. VE
SupplierNumber = Lieferanten Nummer
SupplierName = Name Lieferant
SupplierCountry = Land Lieferant
CustomerNumber = AdressNummer-Kunde
CustomerName = Name Kunde
CustomerCountry = Land Kunde
CustomerIndustry = Branche
StandardCost = EinstandsPreis
SalesCurrency / DocumentCurrency / CompanyCurrency = Waehrung
Incoterms2020 = Versandbedingung
SalesResponsibleEmployee = AdressNummer_V
PostingDate / InvoiceDate = Belegdatum-Rechnung
OrderDate = BelegDatum Auftrag
DocumentType = Alphaplan Excel
``` ```
Das heisst konkret: Technischer Ablauf:
```text ```text
bevorzugt SalesPriceValue, wenn Belegkopfwerte wiederholt aussehen DE wird als manueller Excel-Standort vorbereitet.
sonst Nettofakturawert Hauswaehrung pro Position Nach Upload/Pfad setzen und Aktivieren wird die Alphaplan-Datei beim Standortexport gelesen.
sonst Nettofakturawert Hauswaehrung pro Beleg dedupliziert Die gelesenen Zeilen werden in CentralSalesRecords gespeichert.
sonst SalesPriceValue Die zentrale Excel enthaelt danach DE-Zeilen mit Finance | Country Key = DE.
```
Erster Befund aus `docs/2025_DataExport_DE.xlsx`:
```text
Zeilen: 6'198 Datenzeilen
Summe NettoPreisGesamtX komplett: 4'154'690.05 EUR
Nur Land Kunde = Deutschland: 3'455'276.64 EUR
Deutschland + China: 3'647'592.44 EUR
Sollwert DE: 3'635'923.00 EUR
``` ```
Offen: Offen:
```text - Finance muss bestaetigen, welche Kundenlaender fuer DE zum offiziellen Ist gehoeren.
Fuer DE ist in diesem Dokument keine bestaetigte landesspezifische Importformel dokumentiert. - Manager-Input nennt Warengruppen-Codes und Versandbedingungs-Codes, im Excel sind aktuell primär Bezeichnungen/Texte sichtbar.
Wenn DE aus Sage kommt, muss in einer Sitzung geklaert werden: - Falls nach Codes gefiltert werden soll, braucht der Export eigene Code-Spalten oder eine eindeutige Mapping-Tabelle Text -> Code.
- welche Datei/CSV geladen wird
- welches Betragsfeld Rhino verwendet
- wie Gutschriften, Zuschlaege, Freight/Charges und Steuer getrennt werden
```
## ES ## ES
@@ -30,7 +30,7 @@ Die Logik darf nicht auf einzelne Testzahlen optimiert werden. Sie muss je Jahr
| IT | Hauswaehrung verwenden. Intercompany separat ausweisen und weiter fachlich abgrenzen. | | IT | Hauswaehrung verwenden. Intercompany separat ausweisen und weiter fachlich abgrenzen. |
| UK | Sage/Manual-Excel. Hauswaehrung `GBP` verwenden. Netto ohne VAT; Credit Notes muessen negativ in die Summe laufen. | | UK | Sage/Manual-Excel. Hauswaehrung `GBP` verwenden. Netto ohne VAT; Credit Notes muessen negativ in die Summe laufen. |
| CH / AT | SAP-ZSCHWEIZ liefert Schweiz und Oesterreich aus gleichem System; Trennung ueber Buchungskreis bzw. Reporting-Land. | | CH / AT | SAP-ZSCHWEIZ liefert Schweiz und Oesterreich aus gleichem System; Trennung ueber Buchungskreis bzw. Reporting-Land. |
| DE | Alphaplan-Excel; finaler Jahresfile erforderlich. Sample darf nicht als Jahres-Ist verwendet werden. | | DE | Alphaplan-Excel; finaler Jahresfile `docs/2025_DataExport_DE.xlsx` liegt vor. Provisorisches Mapping nutzt `NettoPreisGesamtX` als SalesPriceValue. Fachlich offen bleibt, welche Kundenlaender fuer den Sollwert eingeschlossen werden. |
| ES | Sage-CSV. `ImporteNeto` als Nettozeile ohne VAT verwenden; Credit Notes/REC negativ; Datumsbasis ist `FechaFactura`, solange Finance nichts anderes vorgibt. | | ES | Sage-CSV. `ImporteNeto` als Nettozeile ohne VAT verwenden; Credit Notes/REC negativ; Datumsbasis ist `FechaFactura`, solange Finance nichts anderes vorgibt. |
## Intercompany / 2nd Party ## Intercompany / 2nd Party
@@ -69,7 +69,7 @@ Ergebnis im Reporting:
- IN: Anzeige muss fachlich `INR` zeigen, auch wenn Quellzeilen verschiedene Belegwaehrungen enthalten. - IN: Anzeige muss fachlich `INR` zeigen, auch wenn Quellzeilen verschiedene Belegwaehrungen enthalten.
- IT: IC-Kundenliste final bestaetigen. - IT: IC-Kundenliste final bestaetigen.
- CH / AT: echtes SAP-Buchungsdatum pruefen, falls `ZSCHWEIZ` aktuell nur Fakturadatum liefert. - CH / AT: echtes SAP-Buchungsdatum pruefen, falls `ZSCHWEIZ` aktuell nur Fakturadatum liefert.
- DE: finalen Jahresfile laden. - DE: finaler Alphaplan-Jahresfile liegt vor und ist technisch mappbar. Rohsumme `NettoPreisGesamtX` komplett ist `4'154'690.05 EUR`; nur `Land Kunde = Deutschland` ist `3'455'276.64 EUR`; Sollwert ist `3'635'923.00 EUR`. Offene Fachfrage: welche Kundenlaender/Abgrenzungen gehoeren offiziell zu DE?
- ES: Aktuell `3'082'320.18 EUR` gegen Soll `3'102'333.61`; Differenz `-20'013.43 EUR`. CSV nutzt `ImporteNeto`; Credit Notes/REC sind negativ. Offen bleiben Perioden-/Serienabgrenzung und ob Rhino eine andere Sage-Auswertung nutzt. - ES: Aktuell `3'082'320.18 EUR` gegen Soll `3'102'333.61`; Differenz `-20'013.43 EUR`. CSV nutzt `ImporteNeto`; Credit Notes/REC sind negativ. Offen bleiben Perioden-/Serienabgrenzung und ob Rhino eine andere Sage-Auswertung nutzt.
## Pruefstand 2026-05-11 ## Pruefstand 2026-05-11
+43
View File
@@ -1703,6 +1703,49 @@ Ergebnis:
- Build erfolgreich. - Build erfolgreich.
- 3 bestehende MudBlazor-Analyzer-Warnungen in `Logs.razor`, `Transformations.razor` und `Standorte.razor`. - 3 bestehende MudBlazor-Analyzer-Warnungen in `Logs.razor`, `Transformations.razor` und `Standorte.razor`.
## DE Alphaplan-Excel provisorisch vorbereitet 2026-05-20
Geaendert:
- Deutschland wird beim Start als manueller Excel-Standort vorbereitet:
- `TSC = TRDE`
- `Land = Deutschland`
- `SourceSystem = MANUAL_EXCEL`
- `IsActive = false`, damit der Gesamtexport ohne gesetzte Datei nicht scheitert.
- Alphaplan-Mapping wird fuer Deutschland geseedet:
- `NettoPreisGesamtX` -> `SalesPriceValue`
- `Belegnummer`, `Position`, `ArtikelNummer`, `ArtikelBezeichnung`
- `Warengruppen-Bezeichnung`, `Anz. VE`
- Lieferant/Kunde/Land/Branche
- `Waehrung`, `Versandbedingung`, `AdressNummer_V`
- `Belegdatum-Rechnung` fuer Posting-/Invoice-Date
- `DocumentType = Alphaplan Excel`
- Testdatei erhalten und eingeordnet:
- `docs/2025_DataExport_DE.xlsx`
Erster Befund:
- `NettoPreisGesamtX` komplett: `4'154'690.05 EUR`
- `Land Kunde = Deutschland`: `3'455'276.64 EUR`
- `Land Kunde = Deutschland + China`: `3'647'592.44 EUR`
- Sollwert DE: `3'635'923.00 EUR`
Offen:
- Finance/Munir muss bestaetigen, welche Kundenlaender und Filter in Alphaplan fuer den offiziellen DE-Istwert gelten.
- Manager-Input nennt Warengruppen- und Versandbedingungs-Codes; im Excel sind aktuell vor allem Bezeichnungen/Texte sichtbar.
Verifikation:
```text
dotnet test .\TrafagSalesExporter.Tests\TrafagSalesExporter.Tests.csproj --no-restore -p:UseAppHost=false -p:OutDir=.\obj\verify_de_alphaplan\ --verbosity minimal
```
Ergebnis:
- 75/75 Tests erfolgreich.
- Bestehende Warnungen: NU1900 wegen lokaler Paket-Sicherheitsdatenabfrage, sowie bekannte MudBlazor-Analyzer-Warnungen zu `Dense`.
## Management Cockpit zentrale Filterkopplung 2026-05-15 ## Management Cockpit zentrale Filterkopplung 2026-05-15
Geaendert: Geaendert: