Apply confirmed Italy finance method
This commit is contained in:
@@ -2,6 +2,25 @@
|
|||||||
|
|
||||||
Stand: 2026-05-19
|
Stand: 2026-05-19
|
||||||
|
|
||||||
|
## Nachtrag 2026-05-20 IT Finance-Methode
|
||||||
|
|
||||||
|
Erledigt:
|
||||||
|
|
||||||
|
- IT-Methode gemaess Finance-Leiter umgesetzt.
|
||||||
|
- `CustomerName` enthaelt `Trafag Italia` wird fuer IT ausgeschlossen.
|
||||||
|
- Doppelte IT-Zeilen mit leerem `Supplier country` werden nur einmal gezaehlt.
|
||||||
|
- Regel greift im Finance-Vergleich/Testprogramm und in den Finance-Spalten der zentralen Excel.
|
||||||
|
|
||||||
|
Bewusster Entscheid:
|
||||||
|
|
||||||
|
- Die alte 2025-Kombination ist naeher am Soll, aber fachlich nicht zukunftssicher.
|
||||||
|
- Fuer 2026+ gilt die neue Methode, auch wenn sie 2025 in der aktuellen DB weiter vom Sollwert abweicht.
|
||||||
|
|
||||||
|
Naechster Check:
|
||||||
|
|
||||||
|
- Nach neuem IT-Export pruefen, ob die vollstaendige `Trafag Italia`-Summe aus den neuen Rohdaten sichtbar wird.
|
||||||
|
- Zentrale Excel fuer `Finance | Country Key = IT`, `Finance | Include = TRUE` filtern und gegen Finance-Vergleich kontrollieren.
|
||||||
|
|
||||||
## Nachtrag 2026-05-19 IIS Deployment / 500 Fehler
|
## Nachtrag 2026-05-19 IIS Deployment / 500 Fehler
|
||||||
|
|
||||||
Vollstaendige Doku:
|
Vollstaendige Doku:
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ public class ExcelExportService : IExcelExportService
|
|||||||
}
|
}
|
||||||
|
|
||||||
var row = 2;
|
var row = 2;
|
||||||
|
var italyBlankSupplierCountryRows = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
foreach (var record in records)
|
foreach (var record in records)
|
||||||
{
|
{
|
||||||
ws.Cell(row, 1).Value = record.ExtractionDate.ToString("dd.MM.yyyy HH:mm:ss");
|
ws.Cell(row, 1).Value = record.ExtractionDate.ToString("dd.MM.yyyy HH:mm:ss");
|
||||||
@@ -129,13 +130,17 @@ public class ExcelExportService : IExcelExportService
|
|||||||
ws.Cell(row, 34).Value = record.Land;
|
ws.Cell(row, 34).Value = record.Land;
|
||||||
ws.Cell(row, 35).Value = record.DocumentType;
|
ws.Cell(row, 35).Value = record.DocumentType;
|
||||||
var financeDate = ResolveFinanceDate(record);
|
var financeDate = ResolveFinanceDate(record);
|
||||||
|
var financeCountryKey = ResolveFinanceCountryKey(record.Land, record.Tsc);
|
||||||
|
var financeInclude = ResolveFinanceInclude(record, financeCountryKey, italyBlankSupplierCountryRows);
|
||||||
ws.Cell(row, 36).Value = financeDate.Year;
|
ws.Cell(row, 36).Value = financeDate.Year;
|
||||||
ws.Cell(row, 37).Value = ResolveFinanceCountryKey(record.Land, record.Tsc);
|
ws.Cell(row, 37).Value = financeCountryKey;
|
||||||
ws.Cell(row, 38).Value = financeDate.ToString("dd.MM.yyyy");
|
ws.Cell(row, 38).Value = financeDate.ToString("dd.MM.yyyy");
|
||||||
ws.Cell(row, 39).Value = record.SalesPriceValue;
|
ws.Cell(row, 39).Value = financeInclude ? record.SalesPriceValue : 0m;
|
||||||
ws.Cell(row, 40).Value = ResolveFinanceCurrency(record);
|
ws.Cell(row, 40).Value = ResolveFinanceCurrency(record);
|
||||||
ws.Cell(row, 41).Value = record.SalesPriceValue != 0m ? "TRUE" : "FALSE";
|
ws.Cell(row, 41).Value = financeInclude && record.SalesPriceValue != 0m ? "TRUE" : "FALSE";
|
||||||
ws.Cell(row, 42).Value = "Sales Price/Value";
|
ws.Cell(row, 42).Value = financeInclude
|
||||||
|
? "Sales Price/Value"
|
||||||
|
: ResolveFinanceExclusionReason(record, financeCountryKey);
|
||||||
row++;
|
row++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,6 +168,7 @@ public class ExcelExportService : IExcelExportService
|
|||||||
("Waehrung", "Finance | Currency zeigt die fuer den Finance-Abgleich fuehrende Hauswaehrung."),
|
("Waehrung", "Finance | Currency zeigt die fuer den Finance-Abgleich fuehrende Hauswaehrung."),
|
||||||
("Datum", "Finance | Date verwendet PostingDate, danach InvoiceDate, danach ExtractionDate."),
|
("Datum", "Finance | Date verwendet PostingDate, danach InvoiceDate, danach ExtractionDate."),
|
||||||
("Wertquelle", "Finance | Source Value Field zeigt, aus welchem Rohfeld der Finance-Wert kommt."),
|
("Wertquelle", "Finance | Source Value Field zeigt, aus welchem Rohfeld der Finance-Wert kommt."),
|
||||||
|
("IT-Sonderregel", "Fuer IT wird Trafag Italia im Finance-Wert ausgeschlossen; doppelte IT-Zeilen ohne Supplier country werden nur einmal gezaehlt."),
|
||||||
("Nicht verwenden", "Nicht Land, TSC, Document Total LC oder andere Betragsspalten fuer den CFO-Abgleich erraten."),
|
("Nicht verwenden", "Nicht Land, TSC, Document Total LC oder andere Betragsspalten fuer den CFO-Abgleich erraten."),
|
||||||
("Hinweis", "Offene fachliche Differenzen bleiben sichtbar; diese Excel-Sicht soll die gleiche Ist-Summe wie das Testprogramm reproduzieren.")
|
("Hinweis", "Offene fachliche Differenzen bleiben sichtbar; diese Excel-Sicht soll die gleiche Ist-Summe wie das Testprogramm reproduzieren.")
|
||||||
};
|
};
|
||||||
@@ -235,6 +241,62 @@ public class ExcelExportService : IExcelExportService
|
|||||||
return normalizedTsc.Replace("TR", string.Empty);
|
return normalizedTsc.Replace("TR", string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool ResolveFinanceInclude(SalesRecord record, string financeCountryKey, HashSet<string> italyBlankSupplierCountryRows)
|
||||||
|
{
|
||||||
|
if (!financeCountryKey.Equals("IT", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (IsExcludedItalyCustomer(record))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(record.SupplierCountry))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return italyBlankSupplierCountryRows.Add(BuildItalyBlankSupplierCountryDeduplicationKey(record));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ResolveFinanceExclusionReason(SalesRecord record, string financeCountryKey)
|
||||||
|
{
|
||||||
|
if (financeCountryKey.Equals("IT", StringComparison.OrdinalIgnoreCase) && IsExcludedItalyCustomer(record))
|
||||||
|
return "Excluded IT customer: Trafag Italia";
|
||||||
|
|
||||||
|
if (financeCountryKey.Equals("IT", StringComparison.OrdinalIgnoreCase) && string.IsNullOrWhiteSpace(record.SupplierCountry))
|
||||||
|
return "Excluded IT duplicate without Supplier country";
|
||||||
|
|
||||||
|
return "Excluded";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsExcludedItalyCustomer(SalesRecord record)
|
||||||
|
=> NormalizeFinanceText(record.CustomerName).Contains("TRAFAG ITALIA", StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
private static string BuildItalyBlankSupplierCountryDeduplicationKey(SalesRecord record)
|
||||||
|
=> string.Join("|",
|
||||||
|
record.Tsc,
|
||||||
|
record.DocumentType,
|
||||||
|
record.DocumentEntry,
|
||||||
|
record.InvoiceNumber,
|
||||||
|
record.PositionOnInvoice,
|
||||||
|
record.Material,
|
||||||
|
record.Name,
|
||||||
|
record.Quantity,
|
||||||
|
record.CustomerNumber,
|
||||||
|
record.CustomerName,
|
||||||
|
record.SalesPriceValue,
|
||||||
|
record.DocumentTotalForeignCurrency,
|
||||||
|
record.DocumentTotalLocalCurrency,
|
||||||
|
record.VatSumForeignCurrency,
|
||||||
|
record.VatSumLocalCurrency,
|
||||||
|
record.PostingDate?.ToString("O") ?? string.Empty,
|
||||||
|
record.InvoiceDate?.ToString("O") ?? string.Empty);
|
||||||
|
|
||||||
|
private static string NormalizeFinanceText(string value)
|
||||||
|
=> (value ?? string.Empty)
|
||||||
|
.Replace("\u00e4", "ae", StringComparison.OrdinalIgnoreCase)
|
||||||
|
.Replace("\u00f6", "oe", StringComparison.OrdinalIgnoreCase)
|
||||||
|
.Replace("\u00fc", "ue", StringComparison.OrdinalIgnoreCase)
|
||||||
|
.Trim()
|
||||||
|
.ToUpperInvariant();
|
||||||
|
|
||||||
private static void WriteGenericWorkbook(string fullPath, string worksheetName, IReadOnlyList<IReadOnlyDictionary<string, object?>> rows)
|
private static void WriteGenericWorkbook(string fullPath, string worksheetName, IReadOnlyList<IReadOnlyDictionary<string, object?>> rows)
|
||||||
{
|
{
|
||||||
using var workbook = new XLWorkbook();
|
using var workbook = new XLWorkbook();
|
||||||
|
|||||||
@@ -40,12 +40,17 @@ public sealed class FinanceReconciliationService : IFinanceReconciliationService
|
|||||||
r.Tsc,
|
r.Tsc,
|
||||||
r.DocumentEntry,
|
r.DocumentEntry,
|
||||||
r.InvoiceNumber,
|
r.InvoiceNumber,
|
||||||
|
r.PositionOnInvoice,
|
||||||
|
r.Material,
|
||||||
|
r.Name,
|
||||||
|
r.Quantity,
|
||||||
r.DocumentType,
|
r.DocumentType,
|
||||||
r.PostingDate,
|
r.PostingDate,
|
||||||
r.InvoiceDate,
|
r.InvoiceDate,
|
||||||
r.ExtractionDate,
|
r.ExtractionDate,
|
||||||
r.CustomerNumber,
|
r.CustomerNumber,
|
||||||
r.CustomerName,
|
r.CustomerName,
|
||||||
|
r.SupplierCountry,
|
||||||
r.SalesCurrency,
|
r.SalesCurrency,
|
||||||
r.DocumentCurrency,
|
r.DocumentCurrency,
|
||||||
r.CompanyCurrency,
|
r.CompanyCurrency,
|
||||||
@@ -150,7 +155,7 @@ public sealed class FinanceReconciliationService : IFinanceReconciliationService
|
|||||||
IReadOnlyDictionary<string, decimal> budgetRatesToChf,
|
IReadOnlyDictionary<string, decimal> budgetRatesToChf,
|
||||||
IReadOnlyList<FinanceIntercompanyRule> intercompanyRules)
|
IReadOnlyList<FinanceIntercompanyRule> intercompanyRules)
|
||||||
{
|
{
|
||||||
var rowList = rows.ToList();
|
var rowList = ApplyCountryFinanceRules(referenceKey, rows).ToList();
|
||||||
var houseCurrency = ResolveHouseCurrency(referenceKey, rowList);
|
var houseCurrency = ResolveHouseCurrency(referenceKey, rowList);
|
||||||
var documentRows = rowList
|
var documentRows = rowList
|
||||||
.GroupBy(row => BuildDocumentKey(row.Tsc, row.DocumentType, row.DocumentEntry, row.InvoiceNumber), StringComparer.OrdinalIgnoreCase)
|
.GroupBy(row => BuildDocumentKey(row.Tsc, row.DocumentType, row.DocumentEntry, row.InvoiceNumber), StringComparer.OrdinalIgnoreCase)
|
||||||
@@ -239,6 +244,50 @@ public sealed class FinanceReconciliationService : IFinanceReconciliationService
|
|||||||
return repeatedGroups / (decimal)multiLineGroups.Count >= 0.8m;
|
return repeatedGroups / (decimal)multiLineGroups.Count >= 0.8m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<NetSalesActualSourceRow> ApplyCountryFinanceRules(
|
||||||
|
string referenceKey,
|
||||||
|
IEnumerable<NetSalesActualSourceRow> rows)
|
||||||
|
{
|
||||||
|
if (!referenceKey.Equals("IT", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return rows;
|
||||||
|
|
||||||
|
var seenBlankSupplierCountryRows = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
return rows.Where(row =>
|
||||||
|
{
|
||||||
|
if (IsExcludedItalyCustomer(row))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(row.SupplierCountry))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return seenBlankSupplierCountryRows.Add(BuildItalyBlankSupplierCountryDeduplicationKey(row));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsExcludedItalyCustomer(NetSalesActualSourceRow row)
|
||||||
|
=> ResolveReferenceKey(row.Land, row.Tsc).Equals("IT", StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
NormalizeRuleText(row.CustomerName).Contains("TRAFAG ITALIA", StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
private static string BuildItalyBlankSupplierCountryDeduplicationKey(NetSalesActualSourceRow row)
|
||||||
|
=> string.Join("|",
|
||||||
|
row.Tsc,
|
||||||
|
row.DocumentType,
|
||||||
|
row.DocumentEntry,
|
||||||
|
row.InvoiceNumber,
|
||||||
|
row.PositionOnInvoice,
|
||||||
|
row.Material,
|
||||||
|
row.Name,
|
||||||
|
row.Quantity,
|
||||||
|
row.CustomerNumber,
|
||||||
|
row.CustomerName,
|
||||||
|
row.SalesPriceValue,
|
||||||
|
row.DocumentTotalForeignCurrency,
|
||||||
|
row.DocumentTotalLocalCurrency,
|
||||||
|
row.VatSumForeignCurrency,
|
||||||
|
row.VatSumLocalCurrency,
|
||||||
|
row.PostingDate?.ToString("O") ?? string.Empty,
|
||||||
|
row.InvoiceDate?.ToString("O") ?? string.Empty);
|
||||||
|
|
||||||
private static decimal ConvertHouseCurrencyNetToBudgetChf(
|
private static decimal ConvertHouseCurrencyNetToBudgetChf(
|
||||||
string houseCurrency,
|
string houseCurrency,
|
||||||
NetSalesActualSourceRow row,
|
NetSalesActualSourceRow row,
|
||||||
@@ -398,12 +447,17 @@ internal sealed record NetSalesActualSourceRow(
|
|||||||
string Tsc,
|
string Tsc,
|
||||||
int DocumentEntry,
|
int DocumentEntry,
|
||||||
string InvoiceNumber,
|
string InvoiceNumber,
|
||||||
|
int PositionOnInvoice,
|
||||||
|
string Material,
|
||||||
|
string Name,
|
||||||
|
decimal Quantity,
|
||||||
string DocumentType,
|
string DocumentType,
|
||||||
DateTime? PostingDate,
|
DateTime? PostingDate,
|
||||||
DateTime? InvoiceDate,
|
DateTime? InvoiceDate,
|
||||||
DateTime ExtractionDate,
|
DateTime ExtractionDate,
|
||||||
string CustomerNumber,
|
string CustomerNumber,
|
||||||
string CustomerName,
|
string CustomerName,
|
||||||
|
string SupplierCountry,
|
||||||
string SalesCurrency,
|
string SalesCurrency,
|
||||||
string DocumentCurrency,
|
string DocumentCurrency,
|
||||||
string CompanyCurrency,
|
string CompanyCurrency,
|
||||||
|
|||||||
@@ -79,6 +79,53 @@ public class FinanceReconciliationServiceTests : IDisposable
|
|||||||
Assert.Contains(row.Candidates, c => c.Key == "SalesPriceValue" && c.Value == 90m && c.IsPreferred);
|
Assert.Contains(row.Candidates, c => c.Key == "SalesPriceValue" && c.Value == 90m && c.IsPreferred);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task BuildNetSalesReferenceRowsAsync_Excludes_Trafag_Italia_For_Italy()
|
||||||
|
{
|
||||||
|
await using (var db = await _dbFactory.CreateDbContextAsync())
|
||||||
|
{
|
||||||
|
db.Sites.Add(BuildSite());
|
||||||
|
db.FinanceReferences.Add(new FinanceReference { Key = "IT", Label = "Trafag IT", Year = 2025, CheckValue = 100m, IsActive = true });
|
||||||
|
db.CentralSalesRecords.AddRange(
|
||||||
|
BuildCentralRecord("TRIT", "Italien", 30, 1, 100m, new DateTime(2025, 4, 1), new DateTime(2025, 4, 1), salesPriceValue: 100m, customerName: "External Customer S.R.L.", supplierCountry: "IT"),
|
||||||
|
BuildCentralRecord("TRIT", "Italien", 31, 1, 400m, new DateTime(2025, 4, 2), new DateTime(2025, 4, 2), salesPriceValue: 400m, customerName: "TRAFAG ITALIA S.R.L.", supplierCountry: "IT"));
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
var service = new FinanceReconciliationService(_dbFactory);
|
||||||
|
|
||||||
|
var rows = await service.BuildNetSalesReferenceRowsAsync(2025);
|
||||||
|
|
||||||
|
var row = Assert.Single(rows);
|
||||||
|
Assert.Equal(100m, row.ActualValue);
|
||||||
|
Assert.Equal(1, row.RowCount);
|
||||||
|
Assert.Equal("OK", row.Status);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task BuildNetSalesReferenceRowsAsync_Deduplicates_Italy_Rows_With_Blank_Supplier_Country()
|
||||||
|
{
|
||||||
|
await using (var db = await _dbFactory.CreateDbContextAsync())
|
||||||
|
{
|
||||||
|
db.Sites.Add(BuildSite());
|
||||||
|
db.FinanceReferences.Add(new FinanceReference { Key = "IT", Label = "Trafag IT", Year = 2025, CheckValue = 150m, IsActive = true });
|
||||||
|
db.CentralSalesRecords.AddRange(
|
||||||
|
BuildCentralRecord("TRIT", "Italien", 40, 1, 100m, new DateTime(2025, 5, 1), new DateTime(2025, 5, 1), salesPriceValue: 100m, customerName: "External Customer S.R.L.", supplierCountry: ""),
|
||||||
|
BuildCentralRecord("TRIT", "Italien", 40, 1, 100m, new DateTime(2025, 5, 1), new DateTime(2025, 5, 1), salesPriceValue: 100m, customerName: "External Customer S.R.L.", supplierCountry: ""),
|
||||||
|
BuildCentralRecord("TRIT", "Italien", 41, 1, 50m, new DateTime(2025, 5, 2), new DateTime(2025, 5, 2), salesPriceValue: 50m, customerName: "External Customer S.R.L.", supplierCountry: "IT"));
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
var service = new FinanceReconciliationService(_dbFactory);
|
||||||
|
|
||||||
|
var rows = await service.BuildNetSalesReferenceRowsAsync(2025);
|
||||||
|
|
||||||
|
var row = Assert.Single(rows);
|
||||||
|
Assert.Equal(150m, row.ActualValue);
|
||||||
|
Assert.Equal(2, row.RowCount);
|
||||||
|
Assert.Equal("OK", row.Status);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task BuildNetSalesReferenceRowsAsync_Reports_India_As_Inr_House_Currency()
|
public async Task BuildNetSalesReferenceRowsAsync_Reports_India_As_Inr_House_Currency()
|
||||||
{
|
{
|
||||||
@@ -113,7 +160,9 @@ public class FinanceReconciliationServiceTests : IDisposable
|
|||||||
DateTime invoiceDate,
|
DateTime invoiceDate,
|
||||||
decimal vatLocal = 0m,
|
decimal vatLocal = 0m,
|
||||||
decimal? salesPriceValue = null,
|
decimal? salesPriceValue = null,
|
||||||
string salesCurrency = "EUR")
|
string salesCurrency = "EUR",
|
||||||
|
string customerName = "",
|
||||||
|
string supplierCountry = "IT")
|
||||||
=> new()
|
=> new()
|
||||||
{
|
{
|
||||||
StoredAtUtc = DateTime.UtcNow,
|
StoredAtUtc = DateTime.UtcNow,
|
||||||
@@ -124,6 +173,11 @@ public class FinanceReconciliationServiceTests : IDisposable
|
|||||||
DocumentEntry = documentEntry,
|
DocumentEntry = documentEntry,
|
||||||
InvoiceNumber = documentEntry.ToString(),
|
InvoiceNumber = documentEntry.ToString(),
|
||||||
PositionOnInvoice = position,
|
PositionOnInvoice = position,
|
||||||
|
Material = "MAT",
|
||||||
|
Name = "Item",
|
||||||
|
Quantity = 1m,
|
||||||
|
CustomerName = customerName,
|
||||||
|
SupplierCountry = supplierCountry,
|
||||||
SalesPriceValue = salesPriceValue ?? documentTotalLocal - vatLocal,
|
SalesPriceValue = salesPriceValue ?? documentTotalLocal - vatLocal,
|
||||||
SalesCurrency = salesCurrency,
|
SalesCurrency = salesCurrency,
|
||||||
DocumentCurrency = salesCurrency,
|
DocumentCurrency = salesCurrency,
|
||||||
|
|||||||
@@ -357,10 +357,19 @@ AND h."CardCode" NOT IN (
|
|||||||
Formel im Vergleich:
|
Formel im Vergleich:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Ist IT = Sum(SalesPriceValue) nach obigem B1-Filter
|
Ist IT = Sum(SalesPriceValue) nach obigem B1-Filter und IT-Finance-Abgrenzung
|
||||||
Soll IT = 7'669'840 EUR
|
Soll IT = 7'669'840 EUR
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Zusaetzliche IT-Finance-Abgrenzung, Stand 2026-05-20:
|
||||||
|
|
||||||
|
```text
|
||||||
|
1. CustomerName enthaelt "Trafag Italia" => aus IT-Finance-Ist ausschliessen.
|
||||||
|
2. IT-Zeilen mit leerem Supplier country => identische Zeile nur einmal zaehlen.
|
||||||
|
```
|
||||||
|
|
||||||
|
Diese Methode ist gemaess Finance-Leiter fachlich korrekt. Die alte Kundenausschluss-Kombination traf 2025 zufaellig naeher, ist aber nicht die zukunftssichere Methode.
|
||||||
|
|
||||||
Bekannter Stand:
|
Bekannter Stand:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
|
|||||||
@@ -185,6 +185,12 @@ Die sechs provisorisch ausgeschlossenen Kunden:
|
|||||||
|
|
||||||
Wichtig: Dieser IT-Filter ist ein Arbeits-/Prueffilter, noch nicht fachlich final bestaetigt.
|
Wichtig: Dieser IT-Filter ist ein Arbeits-/Prueffilter, noch nicht fachlich final bestaetigt.
|
||||||
|
|
||||||
|
Nachtrag 2026-05-20:
|
||||||
|
|
||||||
|
- Finance-Leiter bestaetigt als fachliche Methode: `Trafag Italia` aus dem externen IT-Finance-Ist ausschliessen.
|
||||||
|
- Identische IT-Zeilen mit leerem `Supplier country` nur einmal zaehlen.
|
||||||
|
- Die alte Kundenausschluss-Kombination bleibt als 2025-Analyse dokumentiert, ist aber nicht die fuehrende Methode fuer Folgejahre.
|
||||||
|
|
||||||
Detaildokument:
|
Detaildokument:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
|
|||||||
@@ -204,6 +204,37 @@ Noch zu klaeren:
|
|||||||
- Ist der echte Filter eine Kundengruppe, Branche, Sales-Channel, Projekt-/OEM-Abgrenzung oder ein anderes B1-Feld?
|
- Ist der echte Filter eine Kundengruppe, Branche, Sales-Channel, Projekt-/OEM-Abgrenzung oder ein anderes B1-Feld?
|
||||||
- Soll der Filter in der App spaeter als pflegbare Finance-Regel statt als harter Code umgesetzt werden?
|
- Soll der Filter in der App spaeter als pflegbare Finance-Regel statt als harter Code umgesetzt werden?
|
||||||
|
|
||||||
|
## Nachtrag 2026-05-20 fachliche IT-Methode
|
||||||
|
|
||||||
|
Finance-Leiter bestaetigt:
|
||||||
|
|
||||||
|
- `CustomerName` mit `Trafag Italia` gehoert nicht in den externen IT-Finance-Istwert.
|
||||||
|
- Doppelte Einzelpositionen mit leerem `Supplier country` sollen nur einmal gezaehlt werden.
|
||||||
|
|
||||||
|
Bewertung:
|
||||||
|
|
||||||
|
- Die fruehere Kundenausschluss-Kombination passt fuer 2025 rechnerisch naeher.
|
||||||
|
- Sie ist aber keine belastbare Methode fuer Folgejahre.
|
||||||
|
- Deshalb wird die fachlich bestaetigte Methode umgesetzt, auch wenn die aktuelle 2025-DB danach weiter vom Sollwert abweicht.
|
||||||
|
|
||||||
|
Test gegen aktuelle DB:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Bisherige IT-Summe: 7'669'641.47
|
||||||
|
Trafag Italia Abzug in DB: 6'495.71
|
||||||
|
Dubletten-Abzug SupplierCountry leer: 0.00
|
||||||
|
Neue fachliche Methode: 7'663'145.76
|
||||||
|
Soll IT: 7'669'840.00
|
||||||
|
Neue Differenz: -6'694.24
|
||||||
|
```
|
||||||
|
|
||||||
|
Technische Stellen:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Services/FinanceReconciliationService.cs
|
||||||
|
Services/ExcelExportService.cs
|
||||||
|
```
|
||||||
|
|
||||||
Naechster Test:
|
Naechster Test:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
|
|||||||
@@ -1,5 +1,37 @@
|
|||||||
# Last Change 2026-05-04
|
# Last Change 2026-05-04
|
||||||
|
|
||||||
|
## IT Finance-Methode fachlich bestaetigt 2026-05-20
|
||||||
|
|
||||||
|
Entscheid:
|
||||||
|
|
||||||
|
- Fuer Italien gilt die vom Finance-Leiter bestaetigte Methode.
|
||||||
|
- `CustomerName` enthaelt `Trafag Italia` wird aus dem IT-Finance-Ist ausgeschlossen.
|
||||||
|
- Doppelte IT-Zeilen mit leerem `Supplier country` werden nur einmal gezaehlt.
|
||||||
|
- Diese Regel gilt nur fuer IT.
|
||||||
|
|
||||||
|
Wichtig:
|
||||||
|
|
||||||
|
- Die bisherige Kundenausschluss-Kombination passte 2025 numerisch naeher an den Sollwert, ist aber nicht die belastbare Methode fuer Folgejahre.
|
||||||
|
- Der 2025-Zufallstreffer wird deshalb nicht als fachliche Regel weiterverwendet.
|
||||||
|
|
||||||
|
Gegen aktuelle DB getestet:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Soll IT: 7'669'840.00
|
||||||
|
Bisherige IT-Summe: 7'669'641.47
|
||||||
|
Bisherige Differenz: -198.53
|
||||||
|
Trafag Italia Abzug in DB: 6'495.71
|
||||||
|
Dubletten-Abzug SupplierCountry leer: 0.00
|
||||||
|
Neue fachliche Methode: 7'663'145.76
|
||||||
|
Neue Differenz: -6'694.24
|
||||||
|
```
|
||||||
|
|
||||||
|
Umsetzung:
|
||||||
|
|
||||||
|
- `Services/FinanceReconciliationService.cs`
|
||||||
|
- `Services/ExcelExportService.cs`
|
||||||
|
- Tests in `TrafagSalesExporter.Tests/FinanceReconciliationServiceTests.cs`
|
||||||
|
|
||||||
## IIS Deployment Handoff 2026-05-19
|
## IIS Deployment Handoff 2026-05-19
|
||||||
|
|
||||||
Aktueller Deployment-/IIS-Stand wurde hier dokumentiert:
|
Aktueller Deployment-/IIS-Stand wurde hier dokumentiert:
|
||||||
|
|||||||
Reference in New Issue
Block a user