diff --git a/TrafagSalesExporter/NEXT_STEPS_2026-04-15.md b/TrafagSalesExporter/NEXT_STEPS_2026-04-15.md index 1118d80..ef5fe60 100644 --- a/TrafagSalesExporter/NEXT_STEPS_2026-04-15.md +++ b/TrafagSalesExporter/NEXT_STEPS_2026-04-15.md @@ -2,6 +2,49 @@ 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 Vollstaendige Doku: diff --git a/TrafagSalesExporter/Services/DatabaseSeedService.cs b/TrafagSalesExporter/Services/DatabaseSeedService.cs index 5135c9d..0fccab9 100644 --- a/TrafagSalesExporter/Services/DatabaseSeedService.cs +++ b/TrafagSalesExporter/Services/DatabaseSeedService.cs @@ -13,6 +13,7 @@ public class DatabaseSeedService : IDatabaseSeedService EnsureSourceSystemDefinitions(db); EnsureCentralHanaServerRecords(db); EnsureSpainManualExcelSite(db); + EnsureGermanyManualExcelSite(db); EnsureUkManualExcelFolder(db); EnsureSapODataDachSite(db); EnsureFinanceReferenceDefaults(db); @@ -288,6 +289,61 @@ public class DatabaseSeedService : IDatabaseSeedService 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) { var existing = db.Sites @@ -411,6 +467,91 @@ public class DatabaseSeedService : IDatabaseSeedService 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) { if (db.Sites.Count() <= 1) diff --git a/TrafagSalesExporter/TrafagSalesExporter.Tests/DatabaseInitializationServiceTests.cs b/TrafagSalesExporter/TrafagSalesExporter.Tests/DatabaseInitializationServiceTests.cs index 38f03a9..8554f44 100644 --- a/TrafagSalesExporter/TrafagSalesExporter.Tests/DatabaseInitializationServiceTests.cs +++ b/TrafagSalesExporter/TrafagSalesExporter.Tests/DatabaseInitializationServiceTests.cs @@ -89,6 +89,18 @@ public class DatabaseInitializationServiceTests : IDisposable Assert.Contains(db.HanaServers, x => x.SourceSystem == "BI1"); 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")); } diff --git a/TrafagSalesExporter/docs/2025_DataExport_DE.xlsx b/TrafagSalesExporter/docs/2025_DataExport_DE.xlsx new file mode 100644 index 0000000..cef7cda Binary files /dev/null and b/TrafagSalesExporter/docs/2025_DataExport_DE.xlsx differ diff --git a/TrafagSalesExporter/docs/FINANCE_BERECHNUNGSFORMELN_LAENDER_2026-05-19.md b/TrafagSalesExporter/docs/FINANCE_BERECHNUNGSFORMELN_LAENDER_2026-05-19.md index fffe629..4f4d833 100644 --- a/TrafagSalesExporter/docs/FINANCE_BERECHNUNGSFORMELN_LAENDER_2026-05-19.md +++ b/TrafagSalesExporter/docs/FINANCE_BERECHNUNGSFORMELN_LAENDER_2026-05-19.md @@ -144,6 +144,16 @@ Ob FKDAT fuer diese Quelle fachlich Buchungsdatum oder Fakturadatum ist, muss be ## DE +Aktuelle Quelle: + +```text +SourceSystem = MANUAL_EXCEL +Fachlich = Alphaplan Excel +TSC = TRDE +Land = Deutschland +Aktueller Datei-/Teststand = docs/2025_DataExport_DE.xlsx +``` + Aktueller Referenzwert: ```text @@ -151,30 +161,56 @@ FinanceReference.DE.LocalCurrencyValue = 3'635'923 Hauswaehrung = EUR ``` -Formel im Vergleich: +Provisorisches Import-Mapping: ```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 -bevorzugt SalesPriceValue, wenn Belegkopfwerte wiederholt aussehen -sonst Nettofakturawert Hauswaehrung pro Position -sonst Nettofakturawert Hauswaehrung pro Beleg dedupliziert -sonst SalesPriceValue +DE wird als manueller Excel-Standort vorbereitet. +Nach Upload/Pfad setzen und Aktivieren wird die Alphaplan-Datei beim Standortexport gelesen. +Die gelesenen Zeilen werden in CentralSalesRecords gespeichert. +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: -```text -Fuer DE ist in diesem Dokument keine bestaetigte landesspezifische Importformel dokumentiert. -Wenn DE aus Sage kommt, muss in einer Sitzung geklaert werden: -- welche Datei/CSV geladen wird -- welches Betragsfeld Rhino verwendet -- wie Gutschriften, Zuschlaege, Freight/Charges und Steuer getrennt werden -``` +- Finance muss bestaetigen, welche Kundenlaender fuer DE zum offiziellen Ist gehoeren. +- Manager-Input nennt Warengruppen-Codes und Versandbedingungs-Codes, im Excel sind aktuell primär Bezeichnungen/Texte sichtbar. +- Falls nach Codes gefiltert werden soll, braucht der Export eigene Code-Spalten oder eine eindeutige Mapping-Tabelle Text -> Code. ## ES diff --git a/TrafagSalesExporter/docs/FINANCE_ENTSCHEIDE.md b/TrafagSalesExporter/docs/FINANCE_ENTSCHEIDE.md index 96de67b..365b0db 100644 --- a/TrafagSalesExporter/docs/FINANCE_ENTSCHEIDE.md +++ b/TrafagSalesExporter/docs/FINANCE_ENTSCHEIDE.md @@ -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. | | 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. | -| 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. | ## Intercompany / 2nd Party @@ -69,7 +69,7 @@ Ergebnis im Reporting: - IN: Anzeige muss fachlich `INR` zeigen, auch wenn Quellzeilen verschiedene Belegwaehrungen enthalten. - IT: IC-Kundenliste final bestaetigen. - 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. ## Pruefstand 2026-05-11 diff --git a/TrafagSalesExporter/lastchange.md b/TrafagSalesExporter/lastchange.md index 4495fc9..848cba3 100644 --- a/TrafagSalesExporter/lastchange.md +++ b/TrafagSalesExporter/lastchange.md @@ -1703,6 +1703,49 @@ Ergebnis: - Build erfolgreich. - 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 Geaendert: