Document workflow deltas and clean up docs
This commit is contained in:
@@ -0,0 +1,313 @@
|
||||
# Finance Kurs-Workflow
|
||||
|
||||
Stand: 2026-06-09
|
||||
|
||||
Zweck: Diese Doku beschreibt isoliert den Weg eines Umrechnungskurses vom einzelnen Land bis zur Analyse eines zentralen Dashboard-Wertes. Sie ersetzt nicht die allgemeine Finance-Datenflussdoku, sondern schneidet nur das Thema Kurs/Waehrung heraus.
|
||||
|
||||
Visualisierung: `docs/FINANCE_KURS_WORKFLOW_2026-06-09.svg`
|
||||
|
||||

|
||||
|
||||
## Kurzfazit
|
||||
|
||||
- Der Standortimport rechnet Werte normalerweise nicht ueber die App-Kurstabelle um.
|
||||
- `CentralSalesRecords` speichert die Werte und Waehrungen so, wie sie nach Import und optionalen Transformationen vorliegen.
|
||||
- `DocumentRate` ist ein Quellfeld aus SAP/B1/OData, kein automatisch angewendeter App-Kurs.
|
||||
- Die fuehrende `Finance Summary` und das zentrale Excel nutzen Hauswaehrung je Land. Die App-Kurstabelle wird dort nicht still angewendet.
|
||||
- Eine echte App-Umrechnung passiert nur in Analyse-/Anzeige-Sichten mit Zielwaehrung, in einer expliziten `ConvertCurrency`-Transformation oder im separaten Budget-CHF-Kandidaten.
|
||||
- Wenn in einer Zielwaehrung kein Kurs gefunden wird, fliesst diese Zeile mit `0` in die Anzeige-Summe ein und wird als fehlender Kurs gezaehlt.
|
||||
|
||||
## Begriffe
|
||||
|
||||
| Begriff | Bedeutung |
|
||||
| --- | --- |
|
||||
| Hauswaehrung | Fuehrende Finance-Waehrung des Landes, z. B. `EUR`, `CHF`, `GBP`, `USD`, `INR`. |
|
||||
| `SalesCurrency` | Waehrung von `SalesPriceValue`; fuer Dashboard-Umrechnung die wichtigste Quellwaehrung. |
|
||||
| `CompanyCurrency` | lokale Firmen-/Hauswaehrung aus dem Quellsystem, soweit vorhanden. |
|
||||
| `DocumentCurrency` | Belegwaehrung, z. B. Fremdwaehrung eines einzelnen Kundenbelegs. |
|
||||
| `DocumentRate` | Belegkurs aus dem ERP-Quellsystem. Wird gespeichert, aber nicht automatisch fuer Dashboard-Umrechnung verwendet. |
|
||||
| App-Kurstabelle | Tabelle `CurrencyExchangeRates` mit `FromCurrency`, `ToCurrency`, `Rate`, `ValidFrom`, `ValidTo`, `Notes`, `IsActive`. |
|
||||
| Anzeige-Waehrung | Zielwaehrung in Analyse-Sichten, aktuell `NATIVE`, `CHF`, `EUR`, `USD`. |
|
||||
| Budgetkurs | Kurs mit `Notes = Budget <Jahr>`, z. B. `Budget 2025`, fuer separaten CHF-Kontrollkandidaten. |
|
||||
|
||||
## Gesamtfluss
|
||||
|
||||
```text
|
||||
Land / Quellsystem
|
||||
|
|
||||
+-- Werte und Waehrungen lesen
|
||||
| SalesPriceValue, SalesCurrency, CompanyCurrency,
|
||||
| DocumentCurrency, DocumentTotal FC/LC, DocumentRate
|
||||
|
|
||||
+-- optionale FieldTransformationRules
|
||||
| normal: keine Kursumrechnung
|
||||
| Ausnahme: ConvertCurrency schreibt dauerhaft ein Zielfeld
|
||||
|
|
||||
+-- Standort-Excel schreiben
|
||||
|
|
||||
+-- CentralSalesRecords fuer Standort ersetzen
|
||||
|
|
||||
+-- zentrale Excel / Finance Summary
|
||||
| Hauswaehrung, keine stille App-Kursumrechnung
|
||||
|
|
||||
+-- Management Analyse / Rohdaten-Diagnose mit Zielwaehrung
|
||||
Kurstabelle suchen -> Betrag * Kurs -> zentrale Anzeige-Summe
|
||||
```
|
||||
|
||||
## Schritt 1: Land liefert Betrag und Waehrung
|
||||
|
||||
Jedes Land liefert zuerst einen Nettowert und die dazugehoerige Waehrungsinformation. Der Kurs selbst wird dabei nur als Quellinformation uebernommen, nicht automatisch angewendet.
|
||||
|
||||
| Land / TSC | Quelle | Fuehrender Importwert | Waehrungsfelder | Kursfeld aus Quelle |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| CH / AT `ZSCHWEIZ` | SAP OData | `Z.NetwrHc` -> `SalesPriceValue` | `Z.Hwaer` -> `SalesCurrency` / `CompanyCurrency`, `Z.Waerk` -> `DocumentCurrency` | `Z.Kurrf` -> `DocumentRate` |
|
||||
| FR `TRFR` | SAP B1/HANA | `INV1.LineTotal`, Credit Notes negativ | `OADM.MainCurncy`, `OINV.DocCur` | `OINV.DocRate` / `ORIN.DocRate` |
|
||||
| IT `TRIT` | SAP B1/HANA | `INV1.LineTotal`, Credit Notes negativ, mit IT-Filter | `OADM.MainCurncy`, `DocCur` | `DocRate` |
|
||||
| US `TRUS` | SAP B1/HANA | `INV1.LineTotal`, Credit Notes negativ | `OADM.MainCurncy`, `DocCur` | `DocRate` |
|
||||
| IN `TRIN` | HANA/Sage-Quelle | Hauswaehrungswert in INR | Finance-Hauswaehrung `INR` | ggf. Quellkurs, nicht fuehrend fuer Soll/Ist |
|
||||
| UK `TRUK` | Sage/Manual Excel | `[Sales Price/Value] * [Quantity]` | `GBP` | kein App-Kurs beim Import |
|
||||
| ES `TRES` | Sage CSV | `ImporteNeto`, REC/Credit negativ | `EUR` | kein App-Kurs beim Import |
|
||||
| DE `TRDE` | Alphaplan Excel | `NettoPreisGesamtX` | `Waehrung` / aktuell fachlich `EUR` | kein App-Kurs beim Import |
|
||||
|
||||
Wichtig: Ein vorhandener ERP-Belegkurs erklaert die Beziehung zwischen Belegwaehrung und lokaler Hauswaehrung im Quellsystem. Die App nutzt fuer die zentrale Anzeigeumrechnung trotzdem die eigene Kurstabelle, sobald eine Zielwaehrung gewaehlt wird.
|
||||
|
||||
## Schritt 2: Kurstabelle pflegen
|
||||
|
||||
Die App-Kurse liegen in `CurrencyExchangeRates`.
|
||||
|
||||
Pflegeorte:
|
||||
|
||||
- `Settings > Wechselkurse`: Kurse manuell erfassen, aktivieren/deaktivieren und speichern.
|
||||
- `Refresh Kurse`: importiert ECB-Tageskurse als `EUR -> <Waehrung>` mit Notiz `ECB daily reference rate`.
|
||||
- Seed beim App-Start: Budgetkurse fuer `Budget 2025` und `Budget 2026`, jeweils zur Umrechnung in `CHF`.
|
||||
- Konfigurationsexport/-import: Kurse sind Teil des Config-Transferpakets.
|
||||
|
||||
Technische Regeln beim Speichern:
|
||||
|
||||
- Waehrungscodes werden getrimmt und in Grossbuchstaben gespeichert.
|
||||
- Leere Waehrungen und Kurse `<= 0` werden verworfen.
|
||||
- `ValidFrom` und `ValidTo` werden auf Datum ohne Uhrzeit normalisiert.
|
||||
|
||||
## Schritt 3: Kurs aufloesen
|
||||
|
||||
Die zentrale Kursaufloesung laeuft ueber `CurrencyExchangeRateService.ResolveRate(from, to, date)`.
|
||||
|
||||
Reihenfolge:
|
||||
|
||||
1. Waehrungscodes normalisieren, z. B. `$` -> `USD`, `SFR` -> `CHF`, `RS` -> `INR`.
|
||||
2. Gleiche Waehrung ergibt Kurs `1`.
|
||||
3. Wirksames Datum bestimmen. Ohne Datum wird das heutige UTC-Datum verwendet.
|
||||
4. Direkten aktiven Kurs suchen: `FromCurrency = Quelle`, `ToCurrency = Ziel`, Datum innerhalb `ValidFrom`/`ValidTo`.
|
||||
5. Wenn kein direkter Kurs existiert: inversen Kurs suchen und `1 / Rate` rechnen.
|
||||
6. Wenn weiterhin kein Kurs existiert: Kreuzkurs ueber `EUR` suchen.
|
||||
7. Wenn nichts passt: Ergebnis `null`.
|
||||
|
||||
Bei mehreren gueltigen Kursen gewinnt der mit dem neuesten `ValidFrom`.
|
||||
|
||||
## Schritt 4: Standortexport und zentrale Tabelle
|
||||
|
||||
Beim normalen Standortexport gilt:
|
||||
|
||||
```text
|
||||
Daten holen
|
||||
-> Transformationen anwenden
|
||||
-> Standort-Excel schreiben
|
||||
-> CentralSalesRecords fuer diesen Standort ersetzen
|
||||
-> optional SharePoint Upload
|
||||
```
|
||||
|
||||
Ohne aktive `ConvertCurrency`-Transformation passiert keine App-Kursumrechnung. `CentralSalesRecords` erhaelt:
|
||||
|
||||
- den Importwert `SalesPriceValue`,
|
||||
- die zugehoerige `SalesCurrency`,
|
||||
- Belegfelder wie `DocumentCurrency`, `DocumentTotalForeignCurrency`, `DocumentTotalLocalCurrency`, `DocumentRate`,
|
||||
- die Datumsfelder `PostingDate`, `InvoiceDate`, `ExtractionDate`.
|
||||
|
||||
Damit bleibt nachvollziehbar, ob ein Wert bereits vom Landessystem als Hauswaehrungswert geliefert wurde oder ob er spaeter nur in der Anzeige umgerechnet wurde.
|
||||
|
||||
## Schritt 5: Fuehrende Finance Summary
|
||||
|
||||
Die fuehrende Finance Summary im Dashboard und das zentrale Excel-Blatt `Finance Summary` rechnen nicht automatisch in eine globale Zielwaehrung um.
|
||||
|
||||
Logik:
|
||||
|
||||
```text
|
||||
CentralSalesRecords
|
||||
-> FinanceRuleEngine
|
||||
-> Finance | Net Sales Actual
|
||||
-> Gruppierung nach Jahr, Land, Finance-Waehrung
|
||||
```
|
||||
|
||||
Die Finance-Waehrung ist je Land fest bzw. aus dem Hauswaehrungskontext bestimmt:
|
||||
|
||||
| Land | Finance-Waehrung |
|
||||
| --- | --- |
|
||||
| CH | CHF |
|
||||
| AT | EUR |
|
||||
| DE | EUR |
|
||||
| ES | EUR |
|
||||
| FR | EUR |
|
||||
| IN | INR |
|
||||
| IT | EUR |
|
||||
| UK | GBP |
|
||||
| US | USD |
|
||||
|
||||
Der Waehrungsfilter in `Finance Summary` ist deshalb ein Filter auf diese vorhandene Finance-Waehrung. Er ist keine Umrechnung.
|
||||
|
||||
Konsequenz:
|
||||
|
||||
- `Finance Summary` mit Filter `GBP` zeigt UK-Werte in GBP.
|
||||
- `Finance Summary` mit mehreren Laendern kann `Mixed` anzeigen.
|
||||
- Es wird dabei kein EUR/GBP/CHF-Kurs aus `CurrencyExchangeRates` angewendet.
|
||||
|
||||
## Schritt 6: Zentrale Analyse mit Anzeige-Waehrung
|
||||
|
||||
Die Kursanwendung fuer einen zentralen Dashboard-Wert passiert in der Management-Analyse der zentralen Rohdaten, wenn eine Anzeige-Waehrung gewaehlt ist.
|
||||
|
||||
Eingaben:
|
||||
|
||||
- Datenbasis: `CentralSalesRecords`.
|
||||
- Summenfeld: z. B. `SalesPriceValue`, `StandardCost`, `StandardCostTotal`, `Quantity`.
|
||||
- Anzeige-Waehrung: `NATIVE`, `CHF`, `EUR` oder `USD`.
|
||||
- Zeitraum/Filter: Jahr, Monat, Land, TSC.
|
||||
|
||||
Waehrungsquelle je Summenfeld:
|
||||
|
||||
| Summenfeld | Waehrung fuer Kurs |
|
||||
| --- | --- |
|
||||
| `SalesPriceValue` | `SalesCurrency` |
|
||||
| `StandardCost` | `StandardCostCurrency` |
|
||||
| `StandardCostTotal` | `StandardCostCurrency` |
|
||||
| `Quantity` | keine Waehrung; Anzeige-Waehrung wird ignoriert |
|
||||
|
||||
Datumswahl fuer zentrale Analyse:
|
||||
|
||||
| Setting `ExchangeRateDateField` | Kursdatum |
|
||||
| --- | --- |
|
||||
| `PostingDate` | `PostingDate`, sonst `InvoiceDate`, sonst `ExtractionDate` |
|
||||
| `InvoiceDate` | `InvoiceDate`, sonst `PostingDate`, sonst `ExtractionDate` |
|
||||
| `ExtractionDate` | `ExtractionDate` |
|
||||
|
||||
Formel bei Zielwaehrung:
|
||||
|
||||
```text
|
||||
Anzeige-Wert je Zeile = Quellwert * ResolveRate(Quellwaehrung, Zielwaehrung, Kursdatum)
|
||||
Zentraler Dashboard-Wert = Summe aller Anzeige-Werte im Filter
|
||||
```
|
||||
|
||||
Sonderfaelle:
|
||||
|
||||
- Zielwaehrung `NATIVE`: keine Umrechnung; Werte bleiben in ihrer Quellwaehrung.
|
||||
- Quellwaehrung gleich Zielwaehrung: Faktor `1`.
|
||||
- Kurs fehlt: Anzeige-Wert dieser Zeile wird `0`, `MissingExchangeRateCount` steigt.
|
||||
- Mehrere native Waehrungen im Ergebnis: Anzeige `Mixed`.
|
||||
|
||||
Diese Umrechnung ist eine Laufzeit-Anzeige. Sie aendert `CentralSalesRecords`, Standort-Excel und zentrales Excel nicht.
|
||||
|
||||
## Schritt 7: Explizite Transformation `ConvertCurrency`
|
||||
|
||||
Nur wenn eine aktive `FieldTransformationRule` mit `TransformationType = ConvertCurrency` existiert, wird beim Standortexport dauerhaft ein Feld umgerechnet.
|
||||
|
||||
Beispielargument:
|
||||
|
||||
```text
|
||||
amountField=SalesPriceValue;
|
||||
currencyField=SalesCurrency;
|
||||
targetCurrency=EUR;
|
||||
dateField=InvoiceDate;
|
||||
targetCurrencyField=SalesCurrency;
|
||||
round=2
|
||||
```
|
||||
|
||||
Wirkung:
|
||||
|
||||
- Quelle und Ziel werden ueber die App-Kurstabelle aufgeloest.
|
||||
- `TargetField` erhaelt den umgerechneten Betrag.
|
||||
- Optional wird ein Ziel-Waehrungsfeld gesetzt.
|
||||
- Diese Veraenderung liegt danach in Standort-Excel und `CentralSalesRecords`.
|
||||
|
||||
Fallback-Datum in `ConvertCurrency`:
|
||||
|
||||
```text
|
||||
konfiguriertes dateField
|
||||
sonst InvoiceDate
|
||||
sonst OrderDate
|
||||
sonst ExtractionDate
|
||||
```
|
||||
|
||||
Damit ist `ConvertCurrency` ein anderer Pfad als die Anzeige-Waehrung in der zentralen Analyse.
|
||||
|
||||
## Schritt 8: Budget-CHF-Kandidat
|
||||
|
||||
Im Soll/Ist-Vergleich gibt es einen separaten Kandidaten:
|
||||
|
||||
```text
|
||||
Nettofakturawert Hauswaehrung -> CHF Budget <Jahr>
|
||||
```
|
||||
|
||||
Dieser Kandidat nutzt nur Kurse mit:
|
||||
|
||||
```text
|
||||
Notes = Budget 2025
|
||||
Notes = Budget 2026
|
||||
```
|
||||
|
||||
Er ist eine Kontroll-/Reporting-Sicht. Er ersetzt nicht den Standardabgleich in Hauswaehrung.
|
||||
|
||||
## Analysepfad fuer eine konkrete Kursfrage
|
||||
|
||||
Wenn ein zentraler Wert im Dashboard wegen Kursen geprueft werden soll:
|
||||
|
||||
1. Land/TSC und Quellwert pruefen: `SalesPriceValue`, `SalesCurrency`, `CompanyCurrency`, `DocumentCurrency`, `DocumentRate`.
|
||||
2. Klaeren, welche Dashboard-Sicht gemeint ist:
|
||||
- `Finance Summary`: keine App-Kursumrechnung, nur Waehrungsfilter.
|
||||
- zentrale Rohdaten-/Management-Analyse mit Anzeige-Waehrung: App-Kursumrechnung.
|
||||
- `ConvertCurrency`: dauerhafte Transformation beim Export.
|
||||
3. In den Settings das Kursdatum pruefen: `PostingDate`, `InvoiceDate` oder `ExtractionDate`.
|
||||
4. In `CurrencyExchangeRates` den gueltigen Kurs zum Datum suchen.
|
||||
5. Direkte, inverse und EUR-Kreuzkurslogik beachten.
|
||||
6. Im Dashboard `Nicht umgerechnet` / `MissingExchangeRateCount` kontrollieren.
|
||||
7. Falls `Mixed` angezeigt wird, wurde nicht in eine einheitliche Zielwaehrung gerechnet.
|
||||
|
||||
Hilfsabfragen fuer SQLite:
|
||||
|
||||
```sql
|
||||
SELECT Land, Tsc, SalesCurrency, CompanyCurrency, DocumentCurrency,
|
||||
COUNT(*) AS Rows,
|
||||
SUM(SalesPriceValue) AS SalesValue
|
||||
FROM CentralSalesRecords
|
||||
GROUP BY Land, Tsc, SalesCurrency, CompanyCurrency, DocumentCurrency
|
||||
ORDER BY Land, Tsc;
|
||||
```
|
||||
|
||||
```sql
|
||||
SELECT FromCurrency, ToCurrency, Rate, ValidFrom, ValidTo, Notes, IsActive
|
||||
FROM CurrencyExchangeRates
|
||||
WHERE IsActive = 1
|
||||
ORDER BY FromCurrency, ToCurrency, ValidFrom DESC;
|
||||
```
|
||||
|
||||
## Code-Stellen
|
||||
|
||||
| Thema | Code |
|
||||
| --- | --- |
|
||||
| Kursmodell | `Models/CurrencyExchangeRate.cs` |
|
||||
| Kursaufloesung | `Services/CurrencyExchangeRateService.cs` |
|
||||
| ECB-Import | `Services/ExchangeRateImportService.cs` |
|
||||
| Settings/Kurspflege | `Services/SettingsPageService.cs`, `Components/Pages/Settings.razor` |
|
||||
| Standortexport-Reihenfolge | `Services/SiteExportService.cs` |
|
||||
| zentrale Speicherung | `Services/CentralSalesRecordService.cs` |
|
||||
| zentrale Analyse mit Zielwaehrung | `Services/ManagementCockpitService.cs` |
|
||||
| Finance Summary ohne stille Umrechnung | `Services/ManagementCockpitService.cs`, `Services/ExcelExportService.cs` |
|
||||
| Budget-CHF-Kandidat | `Services/FinanceReconciliationService.cs` |
|
||||
| ConvertCurrency | `Services/TransformationStrategies.cs` |
|
||||
|
||||
## Nicht verwechseln
|
||||
|
||||
| Nicht verwechseln | Klarstellung |
|
||||
| --- | --- |
|
||||
| `DocumentRate` vs. App-Kurstabelle | `DocumentRate` kommt aus dem Landessystem; App-Umrechnung nutzt `CurrencyExchangeRates`. |
|
||||
| Finance-Waehrungsfilter vs. Anzeige-Waehrung | Finance-Waehrungsfilter filtert vorhandene Hauswaehrungen; Anzeige-Waehrung rechnet Werte um. |
|
||||
| Zentrale Excel vs. Dashboard-Livewert | Die App-Anzeige liest `CentralSalesRecords`; SharePoint-Excel ist Ergebnis/Ablage. |
|
||||
| Budget-CHF vs. Tageskurs | Budgetkurse sind eigene Kontrollkurse, nicht automatisch der Standard-Ist. |
|
||||
| Native/Mixed vs. konvertiert | `NATIVE` summiert je Quellwaehrung; `Mixed` heisst mehrere Waehrungen im Ergebnis. |
|
||||
Reference in New Issue
Block a user