Compare commits
150 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e9e90b1e98 | |||
| 640dbf705e | |||
| 42121fe7da | |||
| d45c3f867f | |||
| a90be2644d | |||
| 01739ba42e | |||
| 7bea4d9ee0 | |||
| b6497fd422 | |||
| f141485537 | |||
| b31603554a | |||
| 90def81cc5 | |||
| 87903227b5 | |||
| 6d6776f0f9 | |||
| bf3cc2ea1e | |||
| c1697dc4f3 | |||
| 3b335a568e | |||
| beacdab54f | |||
| da05a49e10 | |||
| 5cec5b3556 | |||
| 9af498287d | |||
| 452f246f24 | |||
| e97c29db43 | |||
| 3419113dec | |||
| bf6297f63b | |||
| f228ba3019 | |||
| 495aa8a691 | |||
| 811916a8b9 | |||
| 3b6333a6a2 | |||
| 9b37330362 | |||
| 889a17b21c | |||
| ac6d559e5d | |||
| 6d80cca241 | |||
| 9dc225d3e4 | |||
| 0d513f7bff | |||
| b10c744bf9 | |||
| e6367da286 | |||
| 01f4480a08 | |||
| e9722c790c | |||
| af98c32026 | |||
| 7300b30cf5 | |||
| 428cd6ddc2 | |||
| 2de8af878b | |||
| 34d95eab9d | |||
| 548aeb2ce9 | |||
| 7edd888aa2 | |||
| a0d4f19f30 | |||
| 67ba342c28 | |||
| 1b69fcb16a | |||
| c8a95422af | |||
| 9d02f18bac | |||
| f9ee2cb120 | |||
| b27b89f599 | |||
| bfbd0a6a22 | |||
| e2de7a1f1c | |||
| 3f769eb7d7 | |||
| 8bc053053c | |||
| a0dcaf7b4f | |||
| 844fc5217a | |||
| 542afa5892 | |||
| 9e02b15ff1 | |||
| 463769b549 | |||
| 2c383d0c55 | |||
| 5ba74c8368 | |||
| 3c9b3c2db1 | |||
| 98f8907817 | |||
| 44dcc5e19f | |||
| d7012ebfa1 | |||
| a9b5317e79 | |||
| f02598760f | |||
| 4b8cd2a0d7 | |||
| 104798d4f2 | |||
| 4653a4f129 | |||
| 07f9a0f522 | |||
| 7e21d9e2e4 | |||
| 4e2eca295d | |||
| 1f165055c1 | |||
| 7b953fa73e | |||
| d3157e4d48 | |||
| 73fe4531cc | |||
| b6c03892b1 | |||
| 91950ad9fd | |||
| 77c3f388e7 | |||
| 6f3e1a8905 | |||
| 466c8a322c | |||
| ab7c7404e2 | |||
| ea3ea5ca83 | |||
| 93e920f61a | |||
| 9b48242f0e | |||
| 7153bfab6f | |||
| 4b27ebf81b | |||
| c26e4a2dbd | |||
| fad1e28c06 | |||
| c61c2e3fcd | |||
| 267aa3d47c | |||
| 7e9a27c75d | |||
| 53d82604a1 | |||
| bb65a1f20e | |||
| c6748055fd | |||
| e6746f76b1 | |||
| e9f389b885 | |||
| 919ab3356d | |||
| 39060f6a1e | |||
| 070dda1bde | |||
| b6bfc60ab8 | |||
| 8aaa22fcb4 | |||
| 64832e9a53 | |||
| 7c9844e277 | |||
| 19d2208fc3 | |||
| 38d0ff969d | |||
| fb2b998d1b | |||
| 10d5f49f63 | |||
| 41811925be | |||
| 97300b6e30 | |||
| c6c8fd9b68 | |||
| 913129f155 | |||
| 953532cae4 | |||
| 267797b55d | |||
| 4234c7f994 | |||
| 3493ff6df1 | |||
| 623f55f5b0 | |||
| b580e1bf79 | |||
| b08ca63bed | |||
| 6a5f4cefa9 | |||
| 0f3ce39f35 | |||
| 336aef5c70 | |||
| 0dff3986b7 | |||
| 318fb5dc7b | |||
| 41c5288fc5 | |||
| e50e7337e6 | |||
| ffe85d471c | |||
| d1c07ee92a | |||
| 4af2fa256e | |||
| bf0db37872 | |||
| 3161351a30 | |||
| aa98909c0a | |||
| 775bb08e95 | |||
| fe0a7dab2a | |||
| 138dae715e | |||
| 28af7f8dd3 | |||
| 7cc2f75e7c | |||
| c7a2f2241d | |||
| bd4ebb8c35 | |||
| 6d88c5645c | |||
| 5d017cc8ea | |||
| 0b8a1b321f | |||
| 95927c3f1a | |||
| f7297d313a | |||
| 80fec4473a | |||
| 95ccb2627c | |||
| 20e3e2a76b |
@@ -1,7 +1,7 @@
|
|||||||
name: Deploy
|
name: Deploy
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags: ["v[0-9]+.[0-9]+.[0-9]+"]
|
tags: ["v[0-9]+.[0-9]+.[0-9]+.[0-9]+"]
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy:
|
||||||
name: Build and Deploy
|
name: Build and Deploy
|
||||||
|
|||||||
381
CHANGELOG.md
381
CHANGELOG.md
@@ -2,16 +2,369 @@
|
|||||||
Changelog
|
Changelog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
[v1.0.3.0][v1.0.3.0] (2026-01-16) {#v1.0.3.0}
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
### Neue Funktionen {#v1.0.3.0-features}
|
||||||
|
|
||||||
|
* Bei Zu-/Abschlägen ist es nun möglich den konkreten Wert dieser auf Lieferscheinen nicht anzuzeigen (Stammdaten -> Saisons). (495aa8a691)
|
||||||
|
* Bei Teillieferungen kann nun die Art der Anlieferung angegeben werden: Planenwagen/Kipper, Lesewagen, Kiste(n). (bf6297f63b, 3419113dec, 6d6776f0f9)
|
||||||
|
* Im Lieferungen-Fenster (`DeliveryAdminWindow`) können nun die Gesamtmengen der gefilterten Lieferungen nach Mitglied und Sorte als Excel-Liste exportiert werden ("Liefermengen"). ([#73][i73], e97c29db43)
|
||||||
|
* Im Datenbankabfragen-Fenster (`QueryWindow`) gibt es die Möglichkeit die Ergebnisse als CSV-Datei zu exportieren. (9af498287d, 5cec5b3556)
|
||||||
|
* PDF-Dokumente wurden überarbeitet:
|
||||||
|
* Traubengutschriften können nur noch ausgedruckt bzw. verschickt werden, wenn die zugehörige Auszahlungsvariante festgesetzt ist. (b31603554a)
|
||||||
|
* Auf Dokumenten für interne Verwendung (alle außer an Mitglieder adressierte) wurden Kopf- und Fußzeile auf ein Minimum beschränkt. (f141485537)
|
||||||
|
* Das Synchronisieren zwischen Zweigstellen ist für den Benutzer nun wesentlich vereinfacht. Geänderte Daten von Mitgliedern, Flächenbindungen oder Lieferungen werden beim Synchronisieren automatsich hochgeladen bzw. heruntergeladen. ([#66][i66], 3b335a568e)
|
||||||
|
* Im Rundschreiben-Fenster (`MailWindow`) wurde ein Knopf zum Abbrechen des Generierens hinzugefügt. ([#50][i50])
|
||||||
|
|
||||||
|
### Behobene Fehler {#v1.0.3.0-bugfixes}
|
||||||
|
|
||||||
|
* Einzelne (nicht über das Rundschreiben-Fenster) verschickte E-Mails werde korrekt im Ausgangsprotokoll angeführt. (ac6d559e5d)
|
||||||
|
* Bei automatischen Datenbank-Updates sind auftretende Fehler ignoriert worden. (f228ba3019)
|
||||||
|
* Beim Aufteilen von Lieferungen sind Zu-/Abschläge nicht auf neu erstellen Teillieferungen übernommen worden. (da05a49e10)
|
||||||
|
* Das Verbinden und Trennen von Waagen mittels Serial-/COM-Port ist nun bei laufendem Programm möglich. ([#71][i71])
|
||||||
|
* In den Auszahlungsvarianten-Daten (`PaymentVariantSummary`) ist die statistische Summe der gebundenen und ungebundenen Menge ohne die Spalte _attributlos gebunden_ berechnet worden. (90def81cc5)
|
||||||
|
* Seit [v0.13.7](#v0.13.7) (2025-01-21) wurde auch bei der ersten Auszahlung der Saison ein "Bisher berücksichtigt: € 0,00" auf den Traubengutschriften angeführt. (7bea4d9ee0)
|
||||||
|
* Das Speichern der Parameter `MAIL_SEND_POSTAL` und `MAIL_SEND_EMAIL` war fehlerhaft. (01739ba42e)
|
||||||
|
* Bessere Isolation der automatisierten Dokumenten-Tests. (640dbf705e)
|
||||||
|
|
||||||
|
### Sonstiges {#v1.0.3.0-misc}
|
||||||
|
|
||||||
|
* Im Lieferungen-Fenster (`DeliveryAdminWindow`) wird bei Zu-/Abschlägen nur "Alle" angezeigt, wenn mehr als 1 ausgewählt sind. (811916a8b9)
|
||||||
|
* Im Auszahlungsvarianten-Fenster (`PaymentVariantsWindow`) wird die letzte Variante standardmäßig ausgewählt und beim Löschen wird um Bestätigung gebeten. (b6497fd422)
|
||||||
|
* Abhängigkeiten aktualisiert. (889a17b21c, 9b37330362, 3b6333a6a2, d45c3f867f, 42121fe7da)
|
||||||
|
|
||||||
|
[v1.0.3.0]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.3.0
|
||||||
|
[i50]: https://git.necronda.net/winzer/elwig/issues/50
|
||||||
|
[i66]: https://git.necronda.net/winzer/elwig/issues/60
|
||||||
|
[i71]: https://git.necronda.net/winzer/elwig/issues/71
|
||||||
|
[i73]: https://git.necronda.net/winzer/elwig/issues/73
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[v1.0.2.0][v1.0.2.0] (2025-11-10) {#v1.0.2.0}
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
### Neue Funktionen {#v1.0.2.0-features}
|
||||||
|
|
||||||
|
* Im Mitglieder-Fenster (`MemberAdminWindow`) können Kontaktdaten der Mitglieder als .vcf-Datei exportiert werden. (01f4480a08, 9dc225d3e4)
|
||||||
|
|
||||||
|
### Sonstiges {#v1.0.2.0-misc}
|
||||||
|
|
||||||
|
* Wenn ein Serial-/COM-Port-USB-Adapter an- oder abgesteckt wird, wird das nun automatisch erkannt. (e6367da286)
|
||||||
|
* Abhängigkeiten aktualisiert. (b10c744bf9, 0d513f7bff)
|
||||||
|
|
||||||
|
[v1.0.2.0]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.2.0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[v1.0.1.5][v1.0.1.5] (2025-10-29) {#v1.0.1.5}
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
### Behobene Fehler {#v1.0.1.5-bugfixes}
|
||||||
|
|
||||||
|
* Im Rundschreiben-Fenster (`MailWindow`) kam es zu einem Absturz, wenn man das Fenster über den "Anlieferungsbestätigung"-Knopf im Leseabschluss-Abschnitt geöffnet hat. (af98c32026)
|
||||||
|
|
||||||
|
[v1.0.1.5]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.1.5
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[v1.0.1.4][v1.0.1.4] (2025-10-28) {#v1.0.1.4}
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
### Behobene Fehler {#v1.0.1.4-bugfixes}
|
||||||
|
|
||||||
|
* Im Rundschreiben-Fenster (`MailWindow`) kam es zu einem Absturz, wenn man die Zustelloptionen "Post zusenden an Mitglieder, die keine E-Mail erhalten würden" und "E-Mail zusenden an niemanden" kombiniert hat. (2de8af878b)
|
||||||
|
|
||||||
|
### Sonstiges {#v1.0.1.4-misc}
|
||||||
|
|
||||||
|
* Im Auszahlungsvariante-Fenster (`ChartWindow`) gibt es keine Fehlermeldung mehr wenn nicht für alle Sorten ein Preis definiert ist, nur noch eine Warnung. (428cd6ddc2)
|
||||||
|
|
||||||
|
[v1.0.1.4]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.1.4
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[v1.0.1.3][v1.0.1.3] (2025-10-13) {#v1.0.1.3}
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
### Neue Funktionen {#v1.0.1.3-features}
|
||||||
|
|
||||||
|
* In der Liste des Lieferungen-Fenster (`DeliveryAdminWindow`) werden
|
||||||
|
* statt ausschließlich der Sorte auch Attribut und Bewirtschaftungsart angezeigt. (a0d4f19f30)
|
||||||
|
* Kommentare der Lieferungen (und Teillieferungen) angezeigt. (548aeb2ce9)
|
||||||
|
|
||||||
|
### Sonstiges {#v1.0.1.3-misc}
|
||||||
|
|
||||||
|
* Verzögerung der Überprüfung auf automatische Updates auf einige Sekunden verlängert. (67ba342c28)
|
||||||
|
* Verbesserung der Ladezeiten im Anmeldungen-Fenster (`DeliveryAncmtAdminWindow`). (7edd888aa2)
|
||||||
|
|
||||||
|
[v1.0.1.3]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.1.3
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[v1.0.1.2][v1.0.1.2] (2025-09-25) {#v1.0.1.2}
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
### Behobene Fehler {#v1.0.1.2-bugfixes}
|
||||||
|
|
||||||
|
* Beim automatischen Importieren/Synchronisieren wird bei einem Fehlerfall der Benutzer verständigt, aber der Vorgang nicht abgebrochen. (9d02f18bac)
|
||||||
|
|
||||||
|
### Sonstiges {#v1.0.1.2-misc}
|
||||||
|
|
||||||
|
* Beim Sichern der Datenbank werden Meta-Informationen in der ZIP-Datei gespeichert. (c8a95422af)
|
||||||
|
|
||||||
|
[v1.0.1.2]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.1.2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[v1.0.1.1][v1.0.1.1] (2025-09-21) {#v1.0.1.1}
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
### Sonstiges {#v1.0.1.1-misc}
|
||||||
|
|
||||||
|
* Eingabe von Sorten und Qualitätsstufen im Übernahme-Fenster (`DeliveryAdminWindows`) verbessert. (e2de7a1f1c, b27b89f599)
|
||||||
|
|
||||||
|
[v1.0.1.1]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.1.1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[v1.0.1.0][v1.0.1.0] (2025-09-18) {#v1.0.1.0}
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
### Neue Funktionen {#v1.0.1.0-features}
|
||||||
|
|
||||||
|
* Neue Weinsorten gemäß Kürzelliste der Bundeskellereiinspektion hinzugefügt. (a0dcaf7b4f)
|
||||||
|
|
||||||
|
### Sonstiges {#v1.0.1.0-misc}
|
||||||
|
|
||||||
|
* HTTP-Anfragen haben jetzt das Feld `User-Agent` gesetzt. (844fc5217a)
|
||||||
|
* Auto-Update-Funktion wird auch beim Erlangen von Netzwerkverbindung ausgeführt. (8bc053053c)
|
||||||
|
|
||||||
|
[v1.0.1.0]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.1.0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[v1.0.0.6][v1.0.0.6] (2025-09-17) {#v1.0.0.6}
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
### Behobene Fehler {#v1.0.0.6-bugfixes}
|
||||||
|
|
||||||
|
* Die automatische Erkennung des Wiege-Modus hat im WKW nicht funktioniert. (463769b549)
|
||||||
|
|
||||||
|
### Sonstiges {#v1.0.0.6-misc}
|
||||||
|
|
||||||
|
* Tippfehler im Über-Fenster (`AboutWindow`) behoben. (2c383d0c55)
|
||||||
|
* `SaveFileDialog` mit Multi-File-Extensions verbessert. (9e02b15ff1)
|
||||||
|
|
||||||
|
[v1.0.0.6]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.0.6
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[v1.0.0.5][v1.0.0.5] (2025-09-15) {#v1.0.0.5}
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
### Neue Funktionen {#v1.0.0.5-features}
|
||||||
|
|
||||||
|
* Die Datenbank kann im Haupt-Fenster (`MainWindow`) gesichert und wiederhergestellt werden. (f02598760f)
|
||||||
|
|
||||||
|
### Sonstiges {#v1.0.0.5-misc}
|
||||||
|
|
||||||
|
* In der WGM ist eine Auswahl eines Zu/-Abschlags im Übernahme-Fenster (`DeliveryAdminWindow`) nun erforderlich. (a9b5317e79)
|
||||||
|
* In der Konfigurationsdatei kann im `[general]` Block `weighing = gross`, `weighing = net`, oder `weighing = box` angegeben werden. (d7012ebfa1)
|
||||||
|
* Über-Fenser (`AboutWindow`) hinzugefügt. (3c9b3c2db1)
|
||||||
|
* Abhängigkeiten aktualisiert. (44dcc5e19f, 98f8907817)
|
||||||
|
|
||||||
|
[v1.0.0.5]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.0.5
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[v1.0.0.4][v1.0.0.4] (2025-09-01) {#v1.0.0.4}
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
### Behobene Fehler {#v1.0.0.4-bugfixes}
|
||||||
|
|
||||||
|
* Absturz beim Verschicken von einzelnen E-Mails außerhalb des Rundschreiben-Fensters (`MailWindow`) behoben. (07f9a0f522)
|
||||||
|
|
||||||
|
### Sonstiges {#v1.0.0.4-misc}
|
||||||
|
|
||||||
|
* Ladezeit des Fehler-Protokoll-Fensters (`LogWindow`) verbessert. (4653a4f129)
|
||||||
|
* Im Übernahme-Fenster (`DeliveryAdminWindow`) ist es nun möglich die Qualitätsstufe zu verändern. (104798d4f2)
|
||||||
|
|
||||||
|
[v1.0.0.4]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.0.4
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[v1.0.0.3][v1.0.0.3] (2025-08-11) {#v1.0.0.3}
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
### Neue Funktionen {#v1.0.0.3-features}
|
||||||
|
|
||||||
|
* Im Haupt-Fenster (`MainWindow`) ist es nun möglich die gesamte Datenbank zu hochzuladen bzw. herunterzuladen. ([#69][i69])
|
||||||
|
|
||||||
|
### Sonstiges {#v1.0.0.3-misc}
|
||||||
|
|
||||||
|
* Die Intigrität von Elwig-Export-Dateien (`.elwig.zip`) und anderen `.zip` Dateien wir nun überprüft. (d3157e4d48)
|
||||||
|
* Die Herkunftshierarchie wird nun auch automatisch synchronisiert (benötigt für Stamm-KG bei Mitglied, Ried/KG bei Flächenbindung, und Herkunft bei Lieferung). ([#70][i70], b6c03892b1)
|
||||||
|
* Abhängigkeiten aktualisiert. (1f165055c1, 4e2eca295d)
|
||||||
|
|
||||||
|
[v1.0.0.3]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.0.3
|
||||||
|
[i69]: https://git.necronda.net/winzer/elwig/issues/69
|
||||||
|
[i70]: https://git.necronda.net/winzer/elwig/issues/70
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[v1.0.0.2][v1.0.0.2] (2025-08-05) {#v1.0.0.2}
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
### Sonstiges {#v1.0.0.2-misc}
|
||||||
|
|
||||||
|
* Explizit native SQLite-Bibliothek hinzugefügt. (77c3f388e7)
|
||||||
|
|
||||||
|
[v1.0.0.2]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.0.2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[v1.0.0.1][v1.0.0.1] (2025-08-05) {#v1.0.0.1}
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
### Sonstiges {#v1.0.0.1-misc}
|
||||||
|
|
||||||
|
* Abhängigkeiten aktualisiert. (466c8a322c)
|
||||||
|
* Angepasste Fehlermeldung, wenn Importieren fehlschlägt. (ab7c7404e2)
|
||||||
|
|
||||||
|
[v1.0.0.1]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.0.1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[v1.0.0.0][v1.0.0.0] (2025-07-30) {#v1.0.0.0}
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
### Neue Funktionen {#v1.0.0.0-features}
|
||||||
|
|
||||||
|
* Es wird nun gespeichert, wann Mitglieder/Flächenbindungen/Lieferungen importiert bzw. exporiert wurden. (4234c7f994)
|
||||||
|
* Im Auszahlungsvarianten-Fenster (`PaymentVariantsWindow`) wird der Auszahlungsbetrag für nicht-festgesetzte Varianten nun überschlagsmäßig angezeigt. (913129f155)
|
||||||
|
* Wenn eine Variante im Auszahlungsvariante-Fenster (`ChartWindow`) gespeichert wird, wird sie nun automatisch auch berechnet. (c6c8fd9b68)
|
||||||
|
* Überall wo Weinsorten vorkommen werden diese jetzt rot oder grün eingefärbt angezeigt. ([#61][i61])
|
||||||
|
* Das Flächenbindungs-Fenster (`AreaComAdminWindow`) wurde überarbeitet. (53d82604a1, 267aa3d47c, [#64][i64])
|
||||||
|
* Auf allen Dokumenten wird der Bio-Kontrollstellen-Code in der Fußzeile angeführt (sofern dieser in den Stammdaten gesetzt wurde). (fad1e28c06)
|
||||||
|
|
||||||
|
### Behobene Fehler {#v1.0.0.0-bugfixes}
|
||||||
|
|
||||||
|
* Bei Traubengutschriften (`CreditNote`) wurde der Rebelzuschlag immer angeführt, auch wenn dieser in der zugrundeliegenden Berechnung nicht berücksichtigt wurde. (336aef5c70)
|
||||||
|
* In den Variantendaten einer Auszahlungsvariante (`PaymentVariantSummary`) wurde neben den Spalten _gebunden_ und _ungebunden_ noch _attributlos gebunden_ hinzugefügt. Ohne diese neue Spalte wären die Werte der anderen beiden falsch. ([#58][i58])
|
||||||
|
* Das erste Laden des Ausgangs-Protokoll-Fensters (`MailLogWindow`) hat nicht funktioniert. ([#65][i65])
|
||||||
|
* Im Lieferungen-Fenster (`DeliveryAdminWindow`) und im Mitglieder-Fenster (`MemberAdminWindow`) wird der Tool-Tip für Gewicht/Gradation mit korrektem Layout angezeigt. (e9f389b885)
|
||||||
|
* Bei Traubengutschriften (`CreditNote`) werden längere Freitexte vollständig angezeigt statt abgeschnitten. ([#62][i62])
|
||||||
|
|
||||||
|
### Sonstiges {#v1.0.0.0-misc}
|
||||||
|
|
||||||
|
* Im Auszahlungsvarianten-Fenster (`PaymentVariantsWindow`) wird angeführt, dass "Zu- und Abschläge bei Lieferungen" auch den Rebelzuschlag umfassen. (0dff3986b7)
|
||||||
|
* Im gesamten Program wird an passenden Stellen statt "Gewicht" nun "Menge" verwendet. (0f3ce39f35)
|
||||||
|
* Verbesserungen der Code-Qualität und der Leistung. ([#59][i59], b580e1bf79, 623f55f5b0, 3493ff6df1, 267797b55d, 41811925be, [#57][i57], 38d0ff969d, e6746f76b1, 7e9a27c75d)
|
||||||
|
* Im Mitglieder-Fenster (`MemberAdminWindow`) werden die Flächenbindungen jetzt für die momentane Saison angezeigt statt für das aktuelle Jahr. (953532cae4)
|
||||||
|
* Abhängigkeiten aktualisiert. (4b27ebf81b, 7153bfab6f, 9b48242f0e)
|
||||||
|
* GPLv3 Lizenz hinzugefügt. (93e920f61a)
|
||||||
|
|
||||||
|
[v1.0.0.0]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.0.0
|
||||||
|
[i57]: https://git.necronda.net/winzer/elwig/issues/57
|
||||||
|
[i58]: https://git.necronda.net/winzer/elwig/issues/58
|
||||||
|
[i59]: https://git.necronda.net/winzer/elwig/issues/59
|
||||||
|
[i61]: https://git.necronda.net/winzer/elwig/issues/61
|
||||||
|
[i62]: https://git.necronda.net/winzer/elwig/issues/62
|
||||||
|
[i64]: https://git.necronda.net/winzer/elwig/issues/64
|
||||||
|
[i65]: https://git.necronda.net/winzer/elwig/issues/65
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[v0.13.9][v0.13.9] (2025-05-05) {#v0.13.9}
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
|
### Sonstiges {#v0.13.9-misc}
|
||||||
|
|
||||||
|
* Abhängigkeiten aktualisiert. (bf0db37872, 4af2fa256e, d1c07ee92a, 41c5288fc5)
|
||||||
|
* Automatisches Aktualisieren der Synchroisations-URL (`https://elwig.at/clients/` -> `https://sync.elwig.at/`). (e50e7337e6)
|
||||||
|
|
||||||
|
[v0.13.9]: https://git.necronda.net/winzer/elwig/releases/tag/v0.13.9
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[v0.13.8][v0.13.8] (2025-02-21) {#v0.13.8}
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
|
### Behobene Fehler {#v0.13.8-bugfixes}
|
||||||
|
|
||||||
|
* Details im Lieferungen-Fenster (`DeliveryAdminWindow`) für die WG Weinland richtiggestellt (Lesweagen, Verhalten bei _Gerebelt gewogen_). (138dae715e, aa98909c0a)
|
||||||
|
|
||||||
|
### Sonstiges {#v0.13.8-misc}
|
||||||
|
|
||||||
|
* Abhängigkeiten aktualisiert. (fe0a7dab2a, 775bb08e95)
|
||||||
|
|
||||||
|
[v0.13.8]: https://git.necronda.net/winzer/elwig/releases/tag/v0.13.8
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[v0.13.7][v0.13.7] (2025-01-21) {#v0.13.7}
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
|
### Behobene Fehler {#v0.13.7-bugfixes}
|
||||||
|
|
||||||
|
* In seltenen Fällen konnten im Auszahlungsvariante-Fenster (`ChartWindow`) manche (Sorten-/Attribut-/Bewirtschaftungsart-)Zuordnungen zu Kurven nicht richtig gespeichert werden. (0b8a1b321f)
|
||||||
|
* Beim Öffnen des Ausgangs-Protokoll-Fensters (`MailLogWindow`) kam es zu einem Absturz. (5d017cc8ea)
|
||||||
|
* Im Auszahlungsvarianten-Fenster (`PaymentVariantsWindow`) war es nicht möglich die Überweisungsdaten zu exportieren, sofern mindestens eine Traubengutschrift einen negativen Betrag aufwies.
|
||||||
|
Jetzt wird der Benutzer nur gewarnt und es ist möglich alle anderen Gutschriften zu exportieren. (6d88c5645c, c7a2f2241d)
|
||||||
|
|
||||||
|
### Sonstiges {#v0.13.7-misc}
|
||||||
|
|
||||||
|
* Im Auszahlungsvarianten-Fenster (`PaymentVariantsWindow`) ist es nun möglich das Datum einer Auszahlungsvariante zu ändern. (bd4ebb8c35)
|
||||||
|
|
||||||
|
[v0.13.7]: https://git.necronda.net/winzer/elwig/releases/tag/v0.13.7
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[v0.13.6][v0.13.6] (2025-01-14) {#v0.13.6}
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
|
### Behobene Fehler {#v0.13.6-bugfixes}
|
||||||
|
|
||||||
|
* In seltenen Fällen konnten im Auszahlungsvariante-Fenster (`ChartWindow`) manche (Sorten-/Attribut-/Bewirtschaftungsart-)Zuordnungen zu Kurven nicht richtig gespeichert werden.
|
||||||
|
Berechnungen basierend auf diesen (evtl. falschen) Zuordnungen wurden immer richtig ausgeführt, eine nachträgliche Überprüfung ist daher möglich. (20e3e2a76b)
|
||||||
|
|
||||||
|
### Sonstiges {#v0.13.6-misc}
|
||||||
|
|
||||||
|
* Abhängigkeiten aktualisiert. (95ccb2627c, 80fec4473a)
|
||||||
|
|
||||||
|
[v0.13.6]: https://git.necronda.net/winzer/elwig/releases/tag/v0.13.6
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[v0.13.5][v0.13.5] (2025-01-02) {#v0.13.5}
|
[v0.13.5][v0.13.5] (2025-01-02) {#v0.13.5}
|
||||||
------------------------------------------
|
------------------------------------------
|
||||||
|
|
||||||
### Neue Funktionen {#0.13.5-features}
|
### Neue Funktionen {#v0.13.5-features}
|
||||||
|
|
||||||
* Im Mitglieder-Fenster (`MemberAdminWindow`) Filter `aktiv` und `!aktiv` hinzugefügt. (5e53d864b1)
|
* Im Mitglieder-Fenster (`MemberAdminWindow`) Filter `aktiv` und `!aktiv` hinzugefügt. (5e53d864b1)
|
||||||
* Im Lieferungen Fenster (`DeliveryAdminWindow`) Menüpunkt _Statistik_ mitsamt _Qualitätsstatistik_ und _Lieferstatistik pro Ort_ hinzugefügt. (c24b1ca2b9)
|
* Im Lieferungen Fenster (`DeliveryAdminWindow`) Menüpunkt _Statistik_ mitsamt _Qualitätsstatistik_ und _Lieferstatistik pro Ort_ hinzugefügt. (c24b1ca2b9)
|
||||||
|
|
||||||
### Sonstiges {#0.13.5-misc}
|
### Sonstiges {#v0.13.5-misc}
|
||||||
|
|
||||||
* Abhängigkeiten aktualisiert. (c9e483ba9d, 633b560a67)
|
* Abhängigkeiten aktualisiert. (c9e483ba9d, 633b560a67)
|
||||||
|
|
||||||
@@ -23,12 +376,12 @@ Changelog
|
|||||||
[v0.13.4][v0.13.4] (2024-11-25) {#v0.13.4}
|
[v0.13.4][v0.13.4] (2024-11-25) {#v0.13.4}
|
||||||
------------------------------------------
|
------------------------------------------
|
||||||
|
|
||||||
### Behobene Fehler {#0.13.4-bugfixes}
|
### Behobene Fehler {#v0.13.4-bugfixes}
|
||||||
|
|
||||||
* Bei _Unter-/Überlieferungen lt. gez. GA_ waren seit [v0.10.6](#v0.10.6) (2023-08-30) Nicht-Lieferanten nicht aufgeführt. (6ba2aa7143)
|
* Bei _Unter-/Überlieferungen lt. gez. GA_ waren seit [v0.10.6](#v0.10.6) (2023-08-30) Nicht-Lieferanten nicht aufgeführt. (6ba2aa7143)
|
||||||
* Bei _Unterlieferungen laut Flächenbindungen_ wurden nur Mitglieder berücksichtigt, die zum Zeitpunkt des Exports aktiv waren. (3b97c2243a, 338f9fe092)
|
* Bei _Unterlieferungen laut Flächenbindungen_ wurden nur Mitglieder berücksichtigt, die zum Zeitpunkt des Exports aktiv waren. (3b97c2243a, 338f9fe092)
|
||||||
|
|
||||||
### Sonstiges {#0.13.4-misc}
|
### Sonstiges {#v0.13.4-misc}
|
||||||
|
|
||||||
* Abhängigkeiten aktualisiert. (c3a2f983d5, 70e01849be, a99a23fd08, 4a10e94d71, 6c7f10cb26)
|
* Abhängigkeiten aktualisiert. (c3a2f983d5, 70e01849be, a99a23fd08, 4a10e94d71, 6c7f10cb26)
|
||||||
|
|
||||||
@@ -40,17 +393,17 @@ Changelog
|
|||||||
[v0.13.3][v0.13.3] (2024-11-13) {#v0.13.3}
|
[v0.13.3][v0.13.3] (2024-11-13) {#v0.13.3}
|
||||||
------------------------------------------
|
------------------------------------------
|
||||||
|
|
||||||
### Neue Funktionen {#0.13.3-features}
|
### Neue Funktionen {#v0.13.3-features}
|
||||||
|
|
||||||
* Im Haupt-Fenster (`MainWindow`) unter _Leseabschluss_ eine Statistik-Tabelle (Gebunden/Ungeb., Mitglieder/Gewicht/Fläche) hinzugefügt. (54deccf021)
|
* Im Haupt-Fenster (`MainWindow`) unter _Leseabschluss_ eine Statistik-Tabelle (Gebunden/Ungeb., Mitglieder/Gewicht/Fläche) hinzugefügt. (54deccf021)
|
||||||
* Im Haupt-Fenster (`MainWindow`) unter _Leseabschluss_ zwei Exportmöglichkeiten (_Flächenbindungen_, _Liefermenge/Ertrag_) hinzugefügt. (c5453c2fe6)
|
* Im Haupt-Fenster (`MainWindow`) unter _Leseabschluss_ zwei Exportmöglichkeiten (_Flächenbindungen_, _Liefermenge/Ertrag_) hinzugefügt. (c5453c2fe6)
|
||||||
* Ausgangs-Protokoll-Fenster (`MailLogWindow`) zum Ansehen aller ausgehenden E-Mails oder ausgedruckten Rundschreiben hinzugefügt (_Rundschreiben_ -> _Hilfe_). (2ee0d56dcc)
|
* Ausgangs-Protokoll-Fenster (`MailLogWindow`) zum Ansehen aller ausgehenden E-Mails oder ausgedruckten Rundschreiben hinzugefügt (_Rundschreiben_ -> _Hilfe_). (2ee0d56dcc)
|
||||||
|
|
||||||
### Behobene Fehler {#0.13.3-bugfixes}
|
### Behobene Fehler {#v0.13.3-bugfixes}
|
||||||
|
|
||||||
* Im Rundschreiben-Fenster (`MailWindow`) kleinere Fehler behoben und alle Einstellungen werden nun gespeichert. (0a9731af09)
|
* Im Rundschreiben-Fenster (`MailWindow`) kleinere Fehler behoben und alle Einstellungen werden nun gespeichert. (0a9731af09)
|
||||||
|
|
||||||
### Sonstiges {#0.13.3-misc}
|
### Sonstiges {#v0.13.3-misc}
|
||||||
|
|
||||||
* In allen Fenstern an passenden Stellen Symbole/Icons hinzugefügt. (f4fa549130)
|
* In allen Fenstern an passenden Stellen Symbole/Icons hinzugefügt. (f4fa549130)
|
||||||
* Im Mitglieder-Fenster (`MemberAdminWindow`) Filter `Flächenbindung` (Mitglieder mit irgendeiner aktiven Flächenbindung) hinzugefügt. (a1d84dd988)
|
* Im Mitglieder-Fenster (`MemberAdminWindow`) Filter `Flächenbindung` (Mitglieder mit irgendeiner aktiven Flächenbindung) hinzugefügt. (a1d84dd988)
|
||||||
@@ -64,17 +417,17 @@ Changelog
|
|||||||
[v0.13.2][v0.13.2] (2024-10-13) {#v0.13.2}
|
[v0.13.2][v0.13.2] (2024-10-13) {#v0.13.2}
|
||||||
------------------------------------------
|
------------------------------------------
|
||||||
|
|
||||||
### Neue Funktionen {#0.13.2-features}
|
### Neue Funktionen {#v0.13.2-features}
|
||||||
|
|
||||||
* Im Lieferungen-Fenster den Menüpunkt _Abwertungsliste_ (`DeliveryDepreciationList`) hinzugefügt. (3cbffdbf27)
|
* Im Lieferungen-Fenster den Menüpunkt _Abwertungsliste_ (`DeliveryDepreciationList`) hinzugefügt. (3cbffdbf27)
|
||||||
|
|
||||||
### Behobene Fehler {#0.13.2-bugfixes}
|
### Behobene Fehler {#v0.13.2-bugfixes}
|
||||||
|
|
||||||
* Fehler im Waagenprotokoll `Avery-Async` (L320) behoben. (8680e51052)
|
* Fehler im Waagenprotokoll `Avery-Async` (L320) behoben. (8680e51052)
|
||||||
* Hausnummern in der BKI Traubentransportscheinliste werden in Excel richtig angezeigt. (86f7f693a0)
|
* Hausnummern in der BKI Traubentransportscheinliste werden in Excel richtig angezeigt. (86f7f693a0)
|
||||||
* Beim Ändern der Identifikatoren von Attributen/Bewirtschaftungsarten wurden diese in Auszahlungsvarianten nicht aktualisiert. (d1f67dc57d)
|
* Beim Ändern der Identifikatoren von Attributen/Bewirtschaftungsarten wurden diese in Auszahlungsvarianten nicht aktualisiert. (d1f67dc57d)
|
||||||
|
|
||||||
### Sonstiges {#0.13.2-misc}
|
### Sonstiges {#v0.13.2-misc}
|
||||||
|
|
||||||
* Weitere automatisierte Tests hinzugefügt. ([#11][i11])
|
* Weitere automatisierte Tests hinzugefügt. ([#11][i11])
|
||||||
* Namenszusätze bei Gemeinden (z.B. an, bei, im, am) genauer angegeben. (65498dd18f)
|
* Namenszusätze bei Gemeinden (z.B. an, bei, im, am) genauer angegeben. (65498dd18f)
|
||||||
@@ -90,15 +443,15 @@ Changelog
|
|||||||
[v0.13.1][v0.13.1] (2024-09-29) {#v0.13.1}
|
[v0.13.1][v0.13.1] (2024-09-29) {#v0.13.1}
|
||||||
------------------------------------------
|
------------------------------------------
|
||||||
|
|
||||||
### Neue Funktionen {#0.13.1-features}
|
### Neue Funktionen {#v0.13.1-features}
|
||||||
|
|
||||||
* Das Extrahieren/Abwerten/Aufteilen von (Teil-)Lieferungen wurde grundlegend überarbeitet und funktioniert ab jetzt in einem einzigen, übersichtlicheren Dialog. (c62947dacd, c185437b9a)
|
* Das Extrahieren/Abwerten/Aufteilen von (Teil-)Lieferungen wurde grundlegend überarbeitet und funktioniert ab jetzt in einem einzigen, übersichtlicheren Dialog. (c62947dacd, c185437b9a)
|
||||||
|
|
||||||
### Behobene Fehler {#0.13.1-bugfixes}
|
### Behobene Fehler {#v0.13.1-bugfixes}
|
||||||
|
|
||||||
* Im Mitglieder-Fenster (`MemberAdminWinodw`) wurden bei `Anlieferungsbestätigung -> speichern (PDF)` und `Traubengutschrift -> speichern (PDF)` E-Mails verschickt, anstatt ein PDF gespeichert. (6ba1973087, a2315e84bd)
|
* Im Mitglieder-Fenster (`MemberAdminWinodw`) wurden bei `Anlieferungsbestätigung -> speichern (PDF)` und `Traubengutschrift -> speichern (PDF)` E-Mails verschickt, anstatt ein PDF gespeichert. (6ba1973087, a2315e84bd)
|
||||||
|
|
||||||
### Sonstiges {#0.13.1-misc}
|
### Sonstiges {#v0.13.1-misc}
|
||||||
|
|
||||||
* Abhängigkeiten aktualisiert. (b6ae1f5675)
|
* Abhängigkeiten aktualisiert. (b6ae1f5675)
|
||||||
|
|
||||||
@@ -113,7 +466,7 @@ Changelog
|
|||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> Ab dieser Version verhält sich die Berechnung der Unterlieferungen bei Flächenbindungen anders.
|
> Ab dieser Version verhält sich die Berechnung der Unterlieferungen bei Flächenbindungen anders.
|
||||||
|
|
||||||
### Behobene Fehler {#0.13.0-bugfixes}
|
### Behobene Fehler {#v0.13.0-bugfixes}
|
||||||
|
|
||||||
* Im Lieferungen-Fenster (`DeliveryAdminWindow`) war das Extrahieren in eine neue Lieferung seit ca. 6 Monaten (98688168b8) nicht mehr möglich. (8a61747538)
|
* Im Lieferungen-Fenster (`DeliveryAdminWindow`) war das Extrahieren in eine neue Lieferung seit ca. 6 Monaten (98688168b8) nicht mehr möglich. (8a61747538)
|
||||||
|
|
||||||
|
|||||||
@@ -30,23 +30,37 @@
|
|||||||
|
|
||||||
<DataTemplate x:Key="WineVarietyTemplateCollapsed">
|
<DataTemplate x:Key="WineVarietyTemplateCollapsed">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBlock Text="{Binding Name}"/>
|
<TextBlock Text="{Binding Name}" Foreground="{Binding Color}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
<DataTemplate x:Key="WineVarietyTemplateExpanded">
|
<DataTemplate x:Key="WineVarietyTemplateExpanded">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBlock Text="{Binding SortId}" MinWidth="36" Margin="0,0,10,0"/>
|
<TextBlock Text="{Binding SortIdFormat}" Foreground="{Binding Color}" MinWidth="36" Margin="0,0,10,0"/>
|
||||||
<TextBlock Text="{Binding Name}"/>
|
<TextBlock Text="{Binding Name}" Foreground="{Binding Color}"/>
|
||||||
<TextBlock Text="{Binding CommentFormat}" FontSize="10" VerticalAlignment="Bottom" Margin="0,0,0,2"/>
|
<TextBlock Text="{Binding CommentFormat}" FontSize="10" VerticalAlignment="Bottom" Margin="0,0,0,2"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
|
<DataTemplate x:Key="AreaCommitmentTypeTemplate">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock Text="{Binding WineVar.Name}" Foreground="{Binding WineVar.Color}"/>
|
||||||
|
<TextBlock Text="{Binding WineAttr.Name}" Margin="5,0,0,0"/>
|
||||||
|
<TextBlock Text="{Binding Discriminator}" Margin="5,0,0,0"/>
|
||||||
|
</StackPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
<DataTemplate x:Key="ModifierTemplate">
|
<DataTemplate x:Key="ModifierTemplate">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBlock Text="{Binding Name}" MinWidth="250" Margin="0,0,10,0"/>
|
<TextBlock Text="{Binding Name}" MinWidth="250" Margin="0,0,10,0"/>
|
||||||
<TextBlock Text="{Binding ValueStr}"/>
|
<TextBlock Text="{Binding ValueStr}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
<DataTemplate x:Key="PublicModifierTemplate">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock Text="{Binding Name}" MinWidth="250" Margin="0,0,10,0"/>
|
||||||
|
<TextBlock Text="{Binding PublicValueStr}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
<DataTemplate x:Key="WineAttributeTemplate">
|
<DataTemplate x:Key="WineAttributeTemplate">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
|
|||||||
@@ -1,20 +1,22 @@
|
|||||||
using System;
|
|
||||||
using System.Data;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Windows;
|
|
||||||
using System.IO;
|
|
||||||
using Elwig.Helpers;
|
|
||||||
using Elwig.Helpers.Weighing;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Windows.Threading;
|
|
||||||
using System.Reflection;
|
|
||||||
using Elwig.Helpers.Printing;
|
|
||||||
using Elwig.Windows;
|
|
||||||
using Elwig.Dialogs;
|
using Elwig.Dialogs;
|
||||||
using System.Threading.Tasks;
|
using Elwig.Helpers;
|
||||||
using Elwig.Helpers.Billing;
|
using Elwig.Helpers.Billing;
|
||||||
|
using Elwig.Helpers.Export;
|
||||||
|
using Elwig.Helpers.Printing;
|
||||||
|
using Elwig.Helpers.Weighing;
|
||||||
using Elwig.Models.Entities;
|
using Elwig.Models.Entities;
|
||||||
|
using Elwig.Windows;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.NetworkInformation;
|
||||||
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Threading;
|
||||||
|
|
||||||
namespace Elwig {
|
namespace Elwig {
|
||||||
public partial class App : Application {
|
public partial class App : Application {
|
||||||
@@ -24,11 +26,15 @@ namespace Elwig {
|
|||||||
public static bool ForceShutdown { get; private set; } = false;
|
public static bool ForceShutdown { get; private set; } = false;
|
||||||
|
|
||||||
private readonly DispatcherTimer _autoUpdateTimer = new() { Interval = TimeSpan.FromHours(1) };
|
private readonly DispatcherTimer _autoUpdateTimer = new() { Interval = TimeSpan.FromHours(1) };
|
||||||
|
public readonly SerialPortWatcher SerialPortWatcher = new();
|
||||||
|
|
||||||
public static readonly string DataPath = @"C:\ProgramData\Elwig\";
|
public static readonly string DataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Elwig");
|
||||||
public static readonly string MailsPath = Path.Combine(DataPath, "mails");
|
public static readonly string MailsPath = Path.Combine(DataPath, "mails");
|
||||||
public static readonly string ConfigPath = Path.Combine(DataPath, "config.ini");
|
public static readonly string ConfigPath = Path.Combine(DataPath, "config.ini");
|
||||||
public static readonly string ExePath = @"C:\Program Files\Elwig\";
|
public static readonly string InstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Elwig");
|
||||||
|
public static readonly string DocumentsPath = (Assembly.GetEntryAssembly()?.Location.Contains(@"\bin\") ?? false) ?
|
||||||
|
Path.Combine(Assembly.GetEntryAssembly()!.Location.Split(@"\bin\")[0], "../Elwig/Documents") :
|
||||||
|
Path.Combine(InstallPath, "resources/Documents");
|
||||||
public static readonly string TempPath = Path.Combine(Path.GetTempPath(), "Elwig");
|
public static readonly string TempPath = Path.Combine(Path.GetTempPath(), "Elwig");
|
||||||
|
|
||||||
public static Config Config { get; private set; } = new(ConfigPath);
|
public static Config Config { get; private set; } = new(ConfigPath);
|
||||||
@@ -43,9 +49,10 @@ namespace Elwig {
|
|||||||
public static string? BranchPhoneNr { get; private set; }
|
public static string? BranchPhoneNr { get; private set; }
|
||||||
public static string? BranchFaxNr { get; private set; }
|
public static string? BranchFaxNr { get; private set; }
|
||||||
public static string? BranchMobileNr { get; private set; }
|
public static string? BranchMobileNr { get; private set; }
|
||||||
public static IList<IScale> Scales { get; private set; }
|
|
||||||
public static IList<ICommandScale> CommandScales => Scales.Where(s => s is ICommandScale).Cast<ICommandScale>().ToList();
|
public static IList<IScale> Scales { get; private set; } = [];
|
||||||
public static IList<IEventScale> EventScales => Scales.Where(s => s is IEventScale).Cast<IEventScale>().ToList();
|
public static IList<ICommandScale> CommandScales => [.. Scales.Where(s => s is ICommandScale).Cast<ICommandScale>()];
|
||||||
|
public static IList<IEventScale> EventScales => [.. Scales.Where(s => s is IEventScale).Cast<IEventScale>()];
|
||||||
public static ClientParameters Client { get; set; }
|
public static ClientParameters Client { get; set; }
|
||||||
|
|
||||||
public static Dispatcher MainDispatcher { get; private set; }
|
public static Dispatcher MainDispatcher { get; private set; }
|
||||||
@@ -82,7 +89,7 @@ namespace Elwig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override async void OnStartup(StartupEventArgs evt) {
|
protected override async void OnStartup(StartupEventArgs evt) {
|
||||||
Version = new Version(typeof(App).GetTypeInfo().Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion.Split('+')[0] ?? "0.0.0");
|
Version = new Version(typeof(App).GetTypeInfo().Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion.Split('+')[0] ?? "0.0.0.0");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await AppDbUpdater.CheckDb();
|
await AppDbUpdater.CheckDb();
|
||||||
@@ -123,14 +130,18 @@ namespace Elwig {
|
|||||||
if (Config.UpdateAuto && Config.UpdateUrl != null) {
|
if (Config.UpdateAuto && Config.UpdateUrl != null) {
|
||||||
if (Utils.HasInternetConnectivity()) {
|
if (Utils.HasInternetConnectivity()) {
|
||||||
Utils.RunBackground("Auto Updater", async () => {
|
Utils.RunBackground("Auto Updater", async () => {
|
||||||
await Task.Delay(500);
|
await Task.Delay(1000);
|
||||||
await CheckForUpdates();
|
await CheckForUpdates();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
NetworkChange.NetworkAvailabilityChanged += OnNetworkAvailabilityChanged;
|
||||||
_autoUpdateTimer.Tick += new EventHandler(OnAutoUpdateTimer);
|
_autoUpdateTimer.Tick += new EventHandler(OnAutoUpdateTimer);
|
||||||
_autoUpdateTimer.Start();
|
_autoUpdateTimer.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SerialPortWatcher.SerialPortConnected += OnSerialPortConnected;
|
||||||
|
SerialPortWatcher.SerialPortDisconnected += OnSerialPortDisconnected;
|
||||||
|
|
||||||
var list = new List<IScale>();
|
var list = new List<IScale>();
|
||||||
foreach (var s in Config.Scales) {
|
foreach (var s in Config.Scales) {
|
||||||
try {
|
try {
|
||||||
@@ -138,7 +149,7 @@ namespace Elwig {
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
list.Add(new InvalidScale(s.Id));
|
list.Add(new InvalidScale(s.Id));
|
||||||
if (s.Required)
|
if (s.Required)
|
||||||
MessageBox.Show($"Unable to create scale {s.Id}:\n\n{e.Message}", "Scale Error",
|
MessageBox.Show($"Verbindung zu Waage {s.Id} konnte nicht hergestellt werden:\n\n{e.Message}", "Waagenfehler",
|
||||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -146,7 +157,7 @@ namespace Elwig {
|
|||||||
|
|
||||||
if (Config.Branch != null) {
|
if (Config.Branch != null) {
|
||||||
if (!branches.ContainsKey(Config.Branch.ToLower())) {
|
if (!branches.ContainsKey(Config.Branch.ToLower())) {
|
||||||
MessageBox.Show("Invalid branch name in config!", "Invalid Branch Config", MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBox.Show("Ungültige Zweigstelle in Konfigurationsdatei!", "Ungültige Zweigstelle", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
Shutdown();
|
Shutdown();
|
||||||
} else {
|
} else {
|
||||||
SetBranch(branches[Config.Branch.ToLower()]);
|
SetBranch(branches[Config.Branch.ToLower()]);
|
||||||
@@ -154,10 +165,20 @@ namespace Elwig {
|
|||||||
} else if (branches.Count == 1) {
|
} else if (branches.Count == 1) {
|
||||||
SetBranch(branches.First().Value);
|
SetBranch(branches.First().Value);
|
||||||
} else {
|
} else {
|
||||||
MessageBox.Show("Unable to determine local branch!", "Invalid Branch Config", MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBox.Show("Erkennen der lokalen Zweigstelle nicht möglich!", "Ungültige Zweigstelle", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
Shutdown();
|
Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Config.WeighingMode == null) {
|
||||||
|
if (Client.IsMatzen || Client.IsWolkersdorf) {
|
||||||
|
Config.WeighingMode = WeighingMode.Net;
|
||||||
|
} else if (Client.IsHaugsdorf || Client.IsSitzendorf) {
|
||||||
|
Config.WeighingMode = WeighingMode.Box;
|
||||||
|
} else if (Client.IsBaden || Client.IsGrInzersdorf) {
|
||||||
|
Config.WeighingMode = WeighingMode.Gross;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
base.OnStartup(evt);
|
base.OnStartup(evt);
|
||||||
|
|
||||||
var window = new MainWindow();
|
var window = new MainWindow();
|
||||||
@@ -165,6 +186,7 @@ namespace Elwig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async void Application_Exit(object sender, ExitEventArgs evt) {
|
private async void Application_Exit(object sender, ExitEventArgs evt) {
|
||||||
|
SerialPortWatcher.Dispose();
|
||||||
foreach (var s in EventScales) {
|
foreach (var s in EventScales) {
|
||||||
s.Dispose();
|
s.Dispose();
|
||||||
}
|
}
|
||||||
@@ -197,10 +219,12 @@ namespace Elwig {
|
|||||||
var ch = CurrentLastWrite;
|
var ch = CurrentLastWrite;
|
||||||
if (ch > CurrentApp.LastChanged)
|
if (ch > CurrentApp.LastChanged)
|
||||||
CurrentApp.LastChanged = ch;
|
CurrentApp.LastChanged = ch;
|
||||||
foreach (Window w in CurrentApp.Windows) {
|
MainDispatcher.Invoke(() => {
|
||||||
if (w is not ContextWindow c) continue;
|
foreach (Window w in CurrentApp.Windows) {
|
||||||
MainDispatcher.BeginInvoke(c.HintContextChange);
|
if (w is not ContextWindow c) continue;
|
||||||
}
|
MainDispatcher.BeginInvoke(c.HintContextChange);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnAutoUpdateTimer(object? sender, EventArgs? evt) {
|
private void OnAutoUpdateTimer(object? sender, EventArgs? evt) {
|
||||||
@@ -212,6 +236,63 @@ namespace Elwig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnNetworkAvailabilityChanged(object? sender, NetworkAvailabilityEventArgs evt) {
|
||||||
|
if (!evt.IsAvailable) return;
|
||||||
|
if (Utils.HasInternetConnectivity()) {
|
||||||
|
Utils.RunBackground("Auto Updater", async () => {
|
||||||
|
await Task.Delay(2000);
|
||||||
|
await CheckForUpdates();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSerialPortConnected(object? sender, string name) {
|
||||||
|
for (var i = 0; i < Config.Scales.Count; i++) {
|
||||||
|
var s = Config.Scales[i];
|
||||||
|
if (s.Connection?.StartsWith($"serial://{name}:") ?? false) {
|
||||||
|
if (Scales[i] is InvalidScale) {
|
||||||
|
try {
|
||||||
|
Scales[i] = Scale.FromConfig(s);
|
||||||
|
MessageBox.Show($"Verbindung zu Waage {s.Id} wieder hergestellt!", $"Waage {s.Id}", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Scales[i] = new InvalidScale(s.Id);
|
||||||
|
MessageBox.Show($"Verbindung zu Waage {s.Id} konnte nicht hergestellt werden:\n\n{e.Message}", "Waagenfehler",
|
||||||
|
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
} else if (Scales[i] is IEventScale) {
|
||||||
|
MessageBox.Show($"Verbindung zu Waage {s.Id} wieder hergestellt!", $"Waage {s.Id}", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UpdateScales();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSerialPortDisconnected(object? sender, string name) {
|
||||||
|
for (var i = 0; i < Config.Scales.Count; i++) {
|
||||||
|
var s = Config.Scales[i];
|
||||||
|
if ((s.Connection?.StartsWith($"serial://{name}:") ?? false) && Scales[i] is not InvalidScale) {
|
||||||
|
MessageBox.Show($"Verbindung zu Waage {s.Id} unterbrochen!", $"Waagen {s.Id}", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||||
|
if (Scales[i] is ICommandScale) {
|
||||||
|
try {
|
||||||
|
Scales[i].Dispose();
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
Scales[i] = new InvalidScale(s.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UpdateScales();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void UpdateScales() {
|
||||||
|
foreach (Window w in CurrentApp.Windows) {
|
||||||
|
if (w is DeliveryAdminWindow t && t.ViewModel.IsReceipt) {
|
||||||
|
t.UpdateScales();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static async Task CheckForUpdates(bool showAlert = false) {
|
public static async Task CheckForUpdates(bool showAlert = false) {
|
||||||
if (Config.UpdateUrl == null) return;
|
if (Config.UpdateUrl == null) return;
|
||||||
var latest = await Utils.GetLatestInstallerUrl(Config.UpdateUrl);
|
var latest = await Utils.GetLatestInstallerUrl(Config.UpdateUrl);
|
||||||
@@ -234,6 +315,19 @@ namespace Elwig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task ReplaceDatabase(string filename) {
|
||||||
|
try {
|
||||||
|
await Task.Run(async () => {
|
||||||
|
await Database.Import(filename);
|
||||||
|
});
|
||||||
|
MessageBox.Show("Das Ersetzen war erfolgreich!\n\nBitte starten Sie Elwig neu!", "Datenbank ersetzen", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
|
ForceShutdown = true;
|
||||||
|
Current.Shutdown();
|
||||||
|
} catch (Exception exc) {
|
||||||
|
MessageBox.Show("Fehler beim Ersetzen:\n\n" + exc.Message, "Datenbank ersetzen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static T FocusWindow<T>(Func<T> constructor, Predicate<T>? selector = null) where T : Window {
|
private static T FocusWindow<T>(Func<T> constructor, Predicate<T>? selector = null) where T : Window {
|
||||||
foreach (Window w in CurrentApp.Windows) {
|
foreach (Window w in CurrentApp.Windows) {
|
||||||
if (w is T t && (selector == null || selector(t))) {
|
if (w is T t && (selector == null || selector(t))) {
|
||||||
|
|||||||
@@ -124,19 +124,24 @@ namespace Elwig.Controls {
|
|||||||
SelectItemsReverse();
|
SelectItemsReverse();
|
||||||
var dmp = !string.IsNullOrEmpty(ListDisplayMemberPath) ? ListDisplayMemberPath : !string.IsNullOrEmpty(DisplayMemberPath) ? DisplayMemberPath : null;
|
var dmp = !string.IsNullOrEmpty(ListDisplayMemberPath) ? ListDisplayMemberPath : !string.IsNullOrEmpty(DisplayMemberPath) ? DisplayMemberPath : null;
|
||||||
if (SelectedItems.Count == ItemsSource.Cast<object>().Count() && AllItemsSelectedContent != null) {
|
if (SelectedItems.Count == ItemsSource.Cast<object>().Count() && AllItemsSelectedContent != null) {
|
||||||
_textBox.Text = AllItemsSelectedContent;
|
|
||||||
AllItemsSelected = true;
|
AllItemsSelected = true;
|
||||||
} else if (SelectedItems.Count == 0) {
|
} else if (SelectedItems.Count == 0) {
|
||||||
_textBox.Text = "";
|
|
||||||
AllItemsSelected = false;
|
AllItemsSelected = false;
|
||||||
|
} else {
|
||||||
|
AllItemsSelected = null;
|
||||||
|
}
|
||||||
|
if (SelectedItems.Count > 1 && SelectedItems.Count == ItemsSource.Cast<object>().Count() && AllItemsSelectedContent != null) {
|
||||||
|
_textBox.Text = AllItemsSelectedContent;
|
||||||
|
} else if (SelectedItems.Count == 0) {
|
||||||
|
_textBox.Text = "";
|
||||||
} else {
|
} else {
|
||||||
_textBox.Text = string.Join(Delimiter,
|
_textBox.Text = string.Join(Delimiter,
|
||||||
dmp == null ? SelectedItems.Cast<object>() :
|
dmp == null ? SelectedItems.Cast<object>() :
|
||||||
SelectedItems.Cast<object>()
|
SelectedItems.Cast<object>()
|
||||||
.Select(i => i.GetType().GetProperty(dmp)?.GetValue(i))
|
.Select(i => i.GetType().GetProperty(dmp)?.GetValue(i))
|
||||||
);
|
);
|
||||||
AllItemsSelected = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RaiseEvent(new SelectionChangedEventArgs(SelectionChangedEvent, evt.RemovedItems, evt.AddedItems));
|
RaiseEvent(new SelectionChangedEventArgs(SelectionChangedEvent, evt.RemovedItems, evt.AddedItems));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
71
Elwig/Dialogs/AreaComModifyDialog.xaml
Normal file
71
Elwig/Dialogs/AreaComModifyDialog.xaml
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<Window x:Class="Elwig.Dialogs.AreaComModifyDialog"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:local="clr-namespace:Elwig.Dialogs"
|
||||||
|
xmlns:ctrl="clr-namespace:Elwig.Controls"
|
||||||
|
ResizeMode="NoResize" ShowInTaskbar="False" Topmost="True"
|
||||||
|
WindowStartupLocation="CenterOwner"
|
||||||
|
DataContext="{Binding RelativeSource={RelativeSource Self}}"
|
||||||
|
Title="Flächenbindungen bearbeiten" Height="220" Width="450">
|
||||||
|
<Window.Resources>
|
||||||
|
<Style TargetType="Label">
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Left"/>
|
||||||
|
<Setter Property="VerticalAlignment" Value="Top"/>
|
||||||
|
<Setter Property="Padding" Value="2,4,2,4"/>
|
||||||
|
<Setter Property="Height" Value="25"/>
|
||||||
|
</Style>
|
||||||
|
<Style TargetType="TextBox">
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||||
|
<Setter Property="VerticalAlignment" Value="Top"/>
|
||||||
|
<Setter Property="FontSize" Value="14"/>
|
||||||
|
<Setter Property="Padding" Value="2"/>
|
||||||
|
<Setter Property="Height" Value="25"/>
|
||||||
|
<Setter Property="TextWrapping" Value="NoWrap"/>
|
||||||
|
</Style>
|
||||||
|
<Style TargetType="Button">
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Right"/>
|
||||||
|
<Setter Property="VerticalAlignment" Value="Bottom"/>
|
||||||
|
<Setter Property="Width" Value="100"/>
|
||||||
|
<Setter Property="Height" Value="25"/>
|
||||||
|
</Style>
|
||||||
|
</Window.Resources>
|
||||||
|
<Grid>
|
||||||
|
<TextBlock x:Name="QuestionBlock1" TextAlignment="Center" Margin="0,10,0,0"
|
||||||
|
HorizontalAlignment="Center" VerticalAlignment="Top">
|
||||||
|
Soll die Flächenbindung (<Run Text="{Binding Area}"/> m², <Run Text="{Binding OrigYearFrom}"/>–<Run Text="{Binding OrigYearTo}"/>) rückwirkend bearbeitet werden,<LineBreak/>
|
||||||
|
oder erst ab der angegebenen Saison?
|
||||||
|
</TextBlock>
|
||||||
|
<TextBlock x:Name="QuestionBlock2" TextAlignment="Center" Margin="0,10,0,0" Visibility="Hidden"
|
||||||
|
HorizontalAlignment="Center" VerticalAlignment="Top">
|
||||||
|
Soll die Flächenbindung (<Run Text="{Binding Area}"/> m², <Run Text="{Binding OrigYearFrom}"/>–<Run Text="{Binding OrigYearTo}"/>) rückwirkend gelöscht werden,<LineBreak/>
|
||||||
|
oder erst ab der angegebenen Saison?
|
||||||
|
</TextBlock>
|
||||||
|
|
||||||
|
<Label x:Name="SeasonLabel" Content="Saison:" Margin="0,50,100,0"
|
||||||
|
HorizontalAlignment="Center" VerticalAlignment="Top"/>
|
||||||
|
<ctrl:IntegerUpDown x:Name="SeasonInput" Width="56" Height="25" Margin="0,50,0,0" FontSize="14"
|
||||||
|
Minimum="1900" Maximum="9999"
|
||||||
|
HorizontalAlignment="Center" VerticalAlignment="Top"
|
||||||
|
TextChanged="SeasonInput_TextChanged"/>
|
||||||
|
<CheckBox x:Name="RetroactiveInput" Content="Rückwirkend bearbeiten" Margin="0,80,0,0"
|
||||||
|
HorizontalAlignment="Center" VerticalAlignment="Top"
|
||||||
|
Checked="RetroactiveInput_Changed" Unchecked="RetroactiveInput_Changed"
|
||||||
|
IsChecked="{Binding ModifyRetroactively}"/>
|
||||||
|
|
||||||
|
<TextBlock x:Name="DescBlock1" Margin="0,105,0,0" TextAlignment="Center"
|
||||||
|
HorizontalAlignment="Center" VerticalAlignment="Top">
|
||||||
|
Die Flächenbindung bleibt bis inkl. Saison <Bold><Run x:Name="CancelSeason1"/></Bold> unverändert,<LineBreak/>
|
||||||
|
und wird ab inkl. Saison <Bold><Run x:Name="NewSeason1"/></Bold> in geänderter Form übernommen.
|
||||||
|
</TextBlock>
|
||||||
|
<TextBlock x:Name="DescBlock2" Margin="0,105,0,0" TextAlignment="Center" Visibility="Hidden"
|
||||||
|
HorizontalAlignment="Center" VerticalAlignment="Top">
|
||||||
|
Die Flächenbindung bleibt bis inklusive Saison <Bold><Run x:Name="CancelSeason2"/></Bold> gültig.
|
||||||
|
</TextBlock>
|
||||||
|
|
||||||
|
<Button x:Name="ConfirmButton" Content="Bestätigen" Margin="10,10,115,10" Grid.Column="1"
|
||||||
|
Click="ConfirmButton_Click"/>
|
||||||
|
<Button x:Name="CancelButton" Content="Abbrechen" Margin="10,10,10,10" Grid.Column="1" IsCancel="True"/>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
62
Elwig/Dialogs/AreaComModifyDialog.xaml.cs
Normal file
62
Elwig/Dialogs/AreaComModifyDialog.xaml.cs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
using Elwig.Helpers;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
|
||||||
|
namespace Elwig.Dialogs {
|
||||||
|
public partial class AreaComModifyDialog : Window {
|
||||||
|
|
||||||
|
public int? YearTo { get; set; }
|
||||||
|
public bool ModifyRetroactively { get; set; }
|
||||||
|
|
||||||
|
public string OrigYearFrom { get; set; }
|
||||||
|
public string OrigYearTo { get; set; }
|
||||||
|
public string Area { get; set; }
|
||||||
|
|
||||||
|
public AreaComModifyDialog(int? yearFrom, int? yearTo, int area, bool delete) {
|
||||||
|
Area = $"{area:N0}";
|
||||||
|
OrigYearFrom = $"{yearFrom}";
|
||||||
|
OrigYearTo = $"{yearTo}";
|
||||||
|
InitializeComponent();
|
||||||
|
Title = delete ? "Flächenbindung löschen" : "Flächenbindung bearbeiten";
|
||||||
|
RetroactiveInput.Content = delete ? "Rückwirkend löschen" : "Rückwirkend bearbeiten";
|
||||||
|
if (delete) {
|
||||||
|
QuestionBlock1.Visibility = Visibility.Hidden;
|
||||||
|
QuestionBlock2.Visibility = Visibility.Visible;
|
||||||
|
DescBlock1.Visibility = Visibility.Hidden;
|
||||||
|
DescBlock2.Visibility = Visibility.Visible;
|
||||||
|
}
|
||||||
|
if ((yearTo.HasValue && yearTo < Utils.CurrentNextSeason) || (yearFrom.HasValue && yearFrom >= Utils.CurrentNextSeason)) {
|
||||||
|
RetroactiveInput.IsChecked = true;
|
||||||
|
} else {
|
||||||
|
SeasonInput.Text = $"{Utils.CurrentNextSeason}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SeasonInput_TextChanged(object sender, TextChangedEventArgs evt) {
|
||||||
|
YearTo = SeasonInput.Value! - 1;
|
||||||
|
CancelSeason1.Text = $"{YearTo}";
|
||||||
|
CancelSeason2.Text = $"{YearTo}";
|
||||||
|
NewSeason1.Text = $"{YearTo + 1}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RetroactiveInput_Changed(object sender, RoutedEventArgs evt) {
|
||||||
|
if (ModifyRetroactively) {
|
||||||
|
SeasonInput.IsEnabled = false;
|
||||||
|
SeasonInput.Text = "";
|
||||||
|
YearTo = null;
|
||||||
|
DescBlock1.Visibility = Visibility.Hidden;
|
||||||
|
DescBlock2.Visibility = Visibility.Hidden;
|
||||||
|
} else {
|
||||||
|
SeasonInput.IsEnabled = true;
|
||||||
|
SeasonInput.Text = $"{Utils.CurrentNextSeason}";
|
||||||
|
DescBlock1.Visibility = QuestionBlock1.Visibility;
|
||||||
|
DescBlock2.Visibility = QuestionBlock2.Visibility;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ConfirmButton_Click(object sender, RoutedEventArgs evt) {
|
||||||
|
DialogResult = true;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<Window x:Class="Elwig.Dialogs.AreaComDialog"
|
<Window x:Class="Elwig.Dialogs.AreaComTransferDialog"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
<CheckBox x:Name="CopyYearToInput" Content="Beginn der Laufzeit von Vorgänger übernehmen" Margin="0,80,0,0"
|
<CheckBox x:Name="CopyYearToInput" Content="Beginn der Laufzeit von Vorgänger übernehmen" Margin="0,80,0,0"
|
||||||
HorizontalAlignment="Center" VerticalAlignment="Top"
|
HorizontalAlignment="Center" VerticalAlignment="Top"
|
||||||
Checked="CopyYearToInput_Changed" Unchecked="CopyYearToInput_Changed"
|
Checked="CopyYearToInput_Changed" Unchecked="CopyYearToInput_Changed"
|
||||||
IsChecked="{Binding MaintainYearTo}"/>
|
IsChecked="{Binding MaintainYearFrom}"/>
|
||||||
|
|
||||||
<TextBlock x:Name="DescBlock1" Margin="0,105,0,0" TextAlignment="Center"
|
<TextBlock x:Name="DescBlock1" Margin="0,105,0,0" TextAlignment="Center"
|
||||||
HorizontalAlignment="Center" VerticalAlignment="Top">
|
HorizontalAlignment="Center" VerticalAlignment="Top">
|
||||||
@@ -3,16 +3,16 @@ using System.Windows;
|
|||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
|
|
||||||
namespace Elwig.Dialogs {
|
namespace Elwig.Dialogs {
|
||||||
public partial class AreaComDialog : Window {
|
public partial class AreaComTransferDialog : Window {
|
||||||
|
|
||||||
public int CancelSeason { get; set; }
|
public int CancelSeason { get; set; }
|
||||||
public int SuccessorSeason => CancelSeason + 1;
|
public int SuccessorSeason => CancelSeason + 1;
|
||||||
public bool MaintainYearTo { get; set; }
|
public bool MaintainYearFrom { get; set; }
|
||||||
|
|
||||||
public string AreaComNum { get; set; }
|
public string AreaComNum { get; set; }
|
||||||
public string Area { get; set; }
|
public string Area { get; set; }
|
||||||
|
|
||||||
public AreaComDialog(string name, int areaComNum, int area) {
|
public AreaComTransferDialog(string name, int areaComNum, int area) {
|
||||||
CancelSeason = Utils.FollowingSeason - 1;
|
CancelSeason = Utils.FollowingSeason - 1;
|
||||||
AreaComNum = $"{areaComNum:N0}";
|
AreaComNum = $"{areaComNum:N0}";
|
||||||
Area = $"{area:N0}";
|
Area = $"{area:N0}";
|
||||||
@@ -29,7 +29,7 @@ namespace Elwig.Dialogs {
|
|||||||
SeasonLabel.Margin = new(0, 40, 100, 0);
|
SeasonLabel.Margin = new(0, 40, 100, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AreaComDialog(string name, string successorName, int areaComNum, int area) {
|
public AreaComTransferDialog(string name, string successorName, int areaComNum, int area) {
|
||||||
CancelSeason = Utils.FollowingSeason - 1;
|
CancelSeason = Utils.FollowingSeason - 1;
|
||||||
AreaComNum = $"{areaComNum:N0}";
|
AreaComNum = $"{areaComNum:N0}";
|
||||||
Area = $"{area:N0}";
|
Area = $"{area:N0}";
|
||||||
@@ -43,13 +43,13 @@ namespace Elwig.Dialogs {
|
|||||||
CancelSeason = (int)SeasonInput.Value!;
|
CancelSeason = (int)SeasonInput.Value!;
|
||||||
CancelSeason1.Text = $"{CancelSeason}";
|
CancelSeason1.Text = $"{CancelSeason}";
|
||||||
CancelSeason2.Text = $"{CancelSeason}";
|
CancelSeason2.Text = $"{CancelSeason}";
|
||||||
TransferSeason.Text = MaintainYearTo ? "" : $"{SuccessorSeason}";
|
TransferSeason.Text = MaintainYearFrom ? "" : $"{SuccessorSeason}";
|
||||||
DescBlock1Season.Text = MaintainYearTo ? "dem originalen Beginn der FB" : "inkl. Saison ";
|
DescBlock1Season.Text = MaintainYearFrom ? "dem originalen Beginn der FB" : "inkl. Saison ";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CopyYearToInput_Changed(object sender, RoutedEventArgs evt) {
|
private void CopyYearToInput_Changed(object sender, RoutedEventArgs evt) {
|
||||||
TransferSeason.Text = MaintainYearTo ? "" : $"{SuccessorSeason}";
|
TransferSeason.Text = MaintainYearFrom ? "" : $"{SuccessorSeason}";
|
||||||
DescBlock1Season.Text = MaintainYearTo ? "dem originalen Beginn der FB" : "inkl. Saison ";
|
DescBlock1Season.Text = MaintainYearFrom ? "dem originalen Beginn der FB" : "inkl. Saison ";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ConfirmButton_Click(object sender, RoutedEventArgs evt) {
|
private void ConfirmButton_Click(object sender, RoutedEventArgs evt) {
|
||||||
@@ -19,6 +19,16 @@ namespace Elwig.Documents {
|
|||||||
Member = m;
|
Member = m;
|
||||||
Location = App.BranchLocation;
|
Location = App.BranchLocation;
|
||||||
IncludeSender = includeSender;
|
IncludeSender = includeSender;
|
||||||
|
var c = App.Client;
|
||||||
|
Header = $"<div class='name'>{c.Name}</div><div class='suffix'>{c.NameSuffix}</div><div class='type'>{c.NameTypeFull}</div>";
|
||||||
|
Footer = Utils.GenerateFooter("<br/>", " \u00b7 ")
|
||||||
|
.Item(c.NameFull).NextLine()
|
||||||
|
.Item(c.Address).Item($"{c.Plz} {c.Ort}").Item("Österreich").Item("Tel.", c.PhoneNr).Item("Fax", c.FaxNr).NextLine()
|
||||||
|
.Item(c.EmailAddress != null ? $"<a href=\"mailto:{c.Name} {c.NameSuffix} <{c.EmailAddress}>\">{c.EmailAddress}</a>" : null)
|
||||||
|
.Item(c.Website != null ? $"<a href=\"http://{c.Website}/\">{c.Website}</a>" : null)
|
||||||
|
.Item("Betriebs-Nr.", c.LfbisNr).Item("Bio-KSt.", c.OrganicAuthority).NextLine()
|
||||||
|
.Item("UID", c.UstIdNr).Item("BIC", c.Bic).Item("IBAN", c.Iban)
|
||||||
|
.ToString();
|
||||||
var uid = (m.UstIdNr ?? "-") + (m.IsBuchführend ? "" : " <i>(pauschaliert)</i>");
|
var uid = (m.UstIdNr ?? "-") + (m.IsBuchführend ? "" : " <i>(pauschaliert)</i>");
|
||||||
Aside = $"<table><colgroup><col span='1' style='width: 22.5mm;'/><col span='1' style='width: 42.5mm;'/></colgroup>" +
|
Aside = $"<table><colgroup><col span='1' style='width: 22.5mm;'/><col span='1' style='width: 42.5mm;'/></colgroup>" +
|
||||||
$"<thead><tr><th colspan='2'>Mitglied</th></tr></thead><tbody>" +
|
$"<thead><tr><th colspan='2'>Mitglied</th></tr></thead><tbody>" +
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
@inherits TemplatePage<Elwig.Documents.BusinessDocument>
|
@inherits TemplatePage<Elwig.Documents.BusinessDocument>
|
||||||
@model Elwig.Documents.BusinessDocument
|
@model Elwig.Documents.BusinessDocument
|
||||||
@{ Layout = "Document"; }
|
@{ Layout = "Document"; }
|
||||||
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\BusinessDocument.css"/>
|
<link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\BusinessDocument.css"/>
|
||||||
<div class="info-wrapper">
|
<div class="info-wrapper">
|
||||||
<div class="address-wrapper">
|
<div class="address-wrapper">
|
||||||
<div class="sender">
|
<div class="sender">
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ namespace Elwig.Documents {
|
|||||||
Data = data;
|
Data = data;
|
||||||
Payment = p;
|
Payment = p;
|
||||||
Credit = p.Credit;
|
Credit = p.Credit;
|
||||||
|
IsPreview = Payment == null || Credit == null;
|
||||||
var season = p.Variant.Season;
|
var season = p.Variant.Season;
|
||||||
if (considerCustomModifiers) {
|
if (considerCustomModifiers) {
|
||||||
CustomPayment = ctx.CustomPayments.Find(p.Year, p.MgNr);
|
CustomPayment = ctx.CustomPayments.Find(p.Year, p.MgNr);
|
||||||
@@ -48,7 +49,7 @@ namespace Elwig.Documents {
|
|||||||
if (CustomPayment?.ModComment != null) {
|
if (CustomPayment?.ModComment != null) {
|
||||||
MemberModifier = CustomPayment.ModComment;
|
MemberModifier = CustomPayment.ModComment;
|
||||||
} else if (mod != null) {
|
} else if (mod != null) {
|
||||||
MemberModifier = $"{mod.Name} ({mod.ValueStr})";
|
MemberModifier = $"{mod.Name} ({mod.PublicValueStr})";
|
||||||
} else {
|
} else {
|
||||||
MemberModifier = "Sonstige Zu-/Abschläge";
|
MemberModifier = "Sonstige Zu-/Abschläge";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
@inherits TemplatePage<Elwig.Documents.CreditNote>
|
@inherits TemplatePage<Elwig.Documents.CreditNote>
|
||||||
@model Elwig.Documents.CreditNote
|
@model Elwig.Documents.CreditNote
|
||||||
@{ Layout = "BusinessDocument"; }
|
@{ Layout = "BusinessDocument"; }
|
||||||
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\CreditNote.css"/>
|
<link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\CreditNote.css" />
|
||||||
<main>
|
<main>
|
||||||
<h1>@Model.Title</h1>
|
<h1>@Model.Title</h1>
|
||||||
<table class="credit">
|
<table class="credit">
|
||||||
@@ -136,7 +136,10 @@
|
|||||||
}
|
}
|
||||||
</tbody>
|
</tbody>
|
||||||
<tbody style="break-inside: avoid;">
|
<tbody style="break-inside: avoid;">
|
||||||
@{ decimal penalty = 0; }
|
@{
|
||||||
|
decimal penalty = 0;
|
||||||
|
string? comment = null;
|
||||||
|
}
|
||||||
|
|
||||||
@if (Model.MemberUnderDeliveries != null && Model.MemberUnderDeliveries.Count() > 0) {
|
@if (Model.MemberUnderDeliveries != null && Model.MemberUnderDeliveries.Count() > 0) {
|
||||||
<tr class="small">
|
<tr class="small">
|
||||||
@@ -158,7 +161,13 @@
|
|||||||
penalty += Model.MemberAutoBusinessSharesAmount;
|
penalty += Model.MemberAutoBusinessSharesAmount;
|
||||||
}
|
}
|
||||||
@if (Model.CustomPayment?.Amount != null) {
|
@if (Model.CustomPayment?.Amount != null) {
|
||||||
@Raw(FormatRow(Model.CustomPayment.Comment ?? ((Model.CustomPayment.Amount.Value) < 0 ? "Weitere Abzüge" : "Weitere Zuschläge"), Model.CustomPayment.Amount.Value, add: true));
|
comment = Model.CustomPayment.Comment;
|
||||||
|
string text = (Model.CustomPayment.Amount.Value < 0 ? "Weitere Abzüge" : "Weitere Zuschläge") + (comment != null ? "*" : "");
|
||||||
|
if (comment != null && comment!.Length <= 30) {
|
||||||
|
text = comment;
|
||||||
|
comment = null;
|
||||||
|
}
|
||||||
|
@Raw(FormatRow(text, Model.CustomPayment.Amount.Value, add: true));
|
||||||
penalty += Model.CustomPayment.Amount.Value;
|
penalty += Model.CustomPayment.Amount.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,6 +185,9 @@
|
|||||||
}
|
}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
@if (comment != null) {
|
||||||
|
<p>* @comment</p>
|
||||||
|
}
|
||||||
<p>Überweisung erfolgt auf Konto @(Elwig.Helpers.Utils.FormatIban(Model.Member.Iban ?? "-")).</p>
|
<p>Überweisung erfolgt auf Konto @(Elwig.Helpers.Utils.FormatIban(Model.Member.Iban ?? "-")).</p>
|
||||||
<div style="margin-top: 1em;">
|
<div style="margin-top: 1em;">
|
||||||
@if (Model.Text != null) {
|
@if (Model.Text != null) {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
@inherits TemplatePage<Elwig.Documents.DeliveryAncmtList>
|
@inherits TemplatePage<Elwig.Documents.DeliveryAncmtList>
|
||||||
@model Elwig.Documents.DeliveryAncmtList
|
@model Elwig.Documents.DeliveryAncmtList
|
||||||
@{ Layout = "Document"; }
|
@{ Layout = "Document"; }
|
||||||
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\DeliveryAncmtList.css" />
|
<link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\DeliveryAncmtList.css" />
|
||||||
<main>
|
<main>
|
||||||
<h1>Anmeldeliste</h1>
|
<h1>Anmeldeliste</h1>
|
||||||
<h2>@Model.Filter</h2>
|
<h2>@Model.Filter</h2>
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
<th rowspan="2" style="text-align: left;">Ort</th>
|
<th rowspan="2" style="text-align: left;">Ort</th>
|
||||||
<th rowspan="2" style="text-align: left;">Sorte</th>
|
<th rowspan="2" style="text-align: left;">Sorte</th>
|
||||||
<th rowspan="2">Anmldg.</th>
|
<th rowspan="2">Anmldg.</th>
|
||||||
<th>Gewicht</th>
|
<th>Menge</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="unit">[kg]</th>
|
<th class="unit">[kg]</th>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
h1 {
|
h1 {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 24pt;
|
font-size: 24pt;
|
||||||
margin-top: 10mm;
|
margin-top: 0;
|
||||||
margin-bottom: 2mm;
|
margin-bottom: 2mm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
@inherits TemplatePage<Elwig.Documents.DeliveryConfirmation>
|
@inherits TemplatePage<Elwig.Documents.DeliveryConfirmation>
|
||||||
@model Elwig.Documents.DeliveryConfirmation
|
@model Elwig.Documents.DeliveryConfirmation
|
||||||
@{ Layout = "BusinessDocument"; }
|
@{ Layout = "BusinessDocument"; }
|
||||||
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\DeliveryConfirmation.css"/>
|
<link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\DeliveryConfirmation.css" />
|
||||||
<main>
|
<main>
|
||||||
<h1>@Model.Title</h1>
|
<h1>@Model.Title</h1>
|
||||||
<table class="delivery-confirmation">
|
<table class="delivery-confirmation">
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
<th rowspan="2" style="text-align: left;">Qualitätsstufe</th>
|
<th rowspan="2" style="text-align: left;">Qualitätsstufe</th>
|
||||||
<th colspan="2">Gradation</th>
|
<th colspan="2">Gradation</th>
|
||||||
<th colspan="2">Flächenbindung</th>
|
<th colspan="2">Flächenbindung</th>
|
||||||
<th>Gewicht</th>
|
<th>Menge</th>
|
||||||
<th rowspan="3" style="padding: 0;">
|
<th rowspan="3" style="padding: 0;">
|
||||||
<svg width="10" height="40" xmlns="http://www.w3.org/2000/svg">
|
<svg width="10" height="40" xmlns="http://www.w3.org/2000/svg">
|
||||||
<text x="-40" y="4" transform="rotate(270)" font-size="8pt" font-style="italic" font-family="Times New Roman"
|
<text x="-40" y="4" transform="rotate(270)" font-size="8pt" font-style="italic" font-family="Times New Roman"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
@inherits TemplatePage<Elwig.Documents.DeliveryDepreciationList>
|
@inherits TemplatePage<Elwig.Documents.DeliveryDepreciationList>
|
||||||
@model Elwig.Documents.DeliveryDepreciationList
|
@model Elwig.Documents.DeliveryDepreciationList
|
||||||
@{ Layout = "Document"; }
|
@{ Layout = "Document"; }
|
||||||
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\DeliveryDepreciationList.css" />
|
<link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\DeliveryDepreciationList.css" />
|
||||||
<main>
|
<main>
|
||||||
<h1>Abwertungsliste</h1>
|
<h1>Abwertungsliste</h1>
|
||||||
<h2>@Model.Filter</h2>
|
<h2>@Model.Filter</h2>
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
<th rowspan="2" style="text-align: left;">Sorte</th>
|
<th rowspan="2" style="text-align: left;">Sorte</th>
|
||||||
<th rowspan="2" style="text-align: left;">Attr./Bewirt.</th>
|
<th rowspan="2" style="text-align: left;">Attr./Bewirt.</th>
|
||||||
<th colspan="2">Gradation</th>
|
<th colspan="2">Gradation</th>
|
||||||
<th>Gewicht</th>
|
<th>Menge</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="unit">[°Oe]</th>
|
<th class="unit">[°Oe]</th>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
h1 {
|
h1 {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 24pt;
|
font-size: 24pt;
|
||||||
margin-top: 10mm;
|
margin-top: 0;
|
||||||
margin-bottom: 2mm;
|
margin-bottom: 2mm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
@inherits TemplatePage<Elwig.Documents.DeliveryJournal>
|
@inherits TemplatePage<Elwig.Documents.DeliveryJournal>
|
||||||
@model Elwig.Documents.DeliveryJournal
|
@model Elwig.Documents.DeliveryJournal
|
||||||
@{ Layout = "Document"; }
|
@{ Layout = "Document"; }
|
||||||
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\DeliveryJournal.css"/>
|
<link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\DeliveryJournal.css"/>
|
||||||
<main>
|
<main>
|
||||||
<h1>Lieferjournal</h1>
|
<h1>Lieferjournal</h1>
|
||||||
<h2>@Model.Filter</h2>
|
<h2>@Model.Filter</h2>
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
<th rowspan="2" style="text-align: left;">Mitglied</th>
|
<th rowspan="2" style="text-align: left;">Mitglied</th>
|
||||||
<th rowspan="2" style="text-align: left;">Sorte</th>
|
<th rowspan="2" style="text-align: left;">Sorte</th>
|
||||||
<th colspan="2">Gradation</th>
|
<th colspan="2">Gradation</th>
|
||||||
<th>Gewicht</th>
|
<th>Menge</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="unit">[°Oe]</th>
|
<th class="unit">[°Oe]</th>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
h1 {
|
h1 {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 24pt;
|
font-size: 24pt;
|
||||||
margin-top: 10mm;
|
margin-top: 0;
|
||||||
margin-bottom: 2mm;
|
margin-bottom: 2mm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
@inherits TemplatePage<Elwig.Documents.DeliveryNote>
|
@inherits TemplatePage<Elwig.Documents.DeliveryNote>
|
||||||
@model Elwig.Documents.DeliveryNote
|
@model Elwig.Documents.DeliveryNote
|
||||||
@{ Layout = "BusinessDocument"; }
|
@{ Layout = "BusinessDocument"; }
|
||||||
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\DeliveryNote.css" />
|
<link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\DeliveryNote.css" />
|
||||||
<main>
|
<main>
|
||||||
<h1>@Model.Title</h1>
|
<h1>@Model.Title</h1>
|
||||||
<table class="delivery large">
|
<table class="delivery large">
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
<th class="main" rowspan="2" colspan="2">Attribut</th>
|
<th class="main" rowspan="2" colspan="2">Attribut</th>
|
||||||
<th class="main" rowspan="2">Qualitätsstufe</th>
|
<th class="main" rowspan="2">Qualitätsstufe</th>
|
||||||
<th colspan="2">Gradation</th>
|
<th colspan="2">Gradation</th>
|
||||||
<th>Gewicht</th>
|
<th>Menge</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="unit">[°Oe]</th>
|
<th class="unit">[°Oe]</th>
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
@if (part.Modifiers.Count() > 0) {
|
@if (part.Modifiers.Count() > 0) {
|
||||||
var first = true;
|
var first = true;
|
||||||
foreach (var mod in part.Modifiers) {
|
foreach (var mod in part.Modifiers) {
|
||||||
<tr class="tight @(first ? "first" : "")"><td></td><td>@Raw(first ? "<i>Zu-/Abschläge:</i>" : "")</td><td colspan="3"><b>@mod.Name</b></td><td style="white-space: pre;">@mod.ValueStr</td></tr>
|
<tr class="tight @(first ? "first" : "")"><td></td><td>@Raw(first ? "<i>Zu-/Abschläge:</i>" : "")</td><td colspan="3"><b>@mod.Name</b></td><td style="white-space: pre;">@mod.PublicValueStr</td></tr>
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Elwig.Helpers.Printing;
|
using Elwig.Helpers.Printing;
|
||||||
using MimeKit;
|
using MimeKit;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Elwig.Documents {
|
namespace Elwig.Documents {
|
||||||
public abstract partial class Document : IDisposable {
|
public abstract partial class Document : IDisposable {
|
||||||
@@ -18,12 +19,13 @@ namespace Elwig.Documents {
|
|||||||
protected string? _pdfPath;
|
protected string? _pdfPath;
|
||||||
protected string? PdfPath => _pdfPath ?? _pdfFile?.FilePath;
|
protected string? PdfPath => _pdfPath ?? _pdfFile?.FilePath;
|
||||||
public int? TotalPages { get; private set; }
|
public int? TotalPages { get; private set; }
|
||||||
public int? Pages => TotalPages / (DoublePaged ? 2 : 1);
|
public int? Pages => TotalPages / (IsDoublePaged ? 2 : 1);
|
||||||
|
|
||||||
public bool ShowFoldMarks = App.Config.Debug;
|
public bool ShowFoldMarks = App.Config.Debug;
|
||||||
public bool DoublePaged = false;
|
public bool IsDoublePaged = false;
|
||||||
|
public bool IsPreview = false;
|
||||||
|
|
||||||
public string DataPath;
|
public string DocumentsPath;
|
||||||
public int CurrentNextSeason;
|
public int CurrentNextSeason;
|
||||||
public string? DocumentId;
|
public string? DocumentId;
|
||||||
public string Title;
|
public string Title;
|
||||||
@@ -34,19 +36,12 @@ namespace Elwig.Documents {
|
|||||||
|
|
||||||
public Document(string title) {
|
public Document(string title) {
|
||||||
var c = App.Client;
|
var c = App.Client;
|
||||||
DataPath = App.DataPath;
|
DocumentsPath = App.DocumentsPath;
|
||||||
CurrentNextSeason = Utils.CurrentNextSeason;
|
CurrentNextSeason = Utils.CurrentNextSeason;
|
||||||
Title = title;
|
Title = title;
|
||||||
Author = c.NameFull;
|
Author = c.NameFull;
|
||||||
Header = $"<div class='name'>{c.Name}</div><div class='suffix'>{c.NameSuffix}</div><div class='type'>{c.NameTypeFull}</div>";
|
Header = "";
|
||||||
Footer = Utils.GenerateFooter("<br/>", " \u00b7 ")
|
Footer = Utils.GenerateFooter("<br/>", " \u00b7 ").Item(c.NameFull).ToString();
|
||||||
.Item(c.NameFull).NextLine()
|
|
||||||
.Item(c.Address).Item($"{c.Plz} {c.Ort}").Item("Österreich").Item("Tel.", c.PhoneNr).Item("Fax", c.FaxNr).NextLine()
|
|
||||||
.Item(c.EmailAddress != null ? $"<a href=\"mailto:{c.Name} {c.NameSuffix} <{c.EmailAddress}>\">{c.EmailAddress}</a>" : null)
|
|
||||||
.Item(c.Website != null ? $"<a href=\"http://{c.Website}/\">{c.Website}</a>" : null)
|
|
||||||
.Item("Betriebs-Nr.", c.LfbisNr).Item("UID", c.UstIdNr).NextLine()
|
|
||||||
.Item("BIC", c.Bic).Item("IBAN", c.Iban)
|
|
||||||
.ToString();
|
|
||||||
Date = DateOnly.FromDateTime(Utils.Today);
|
Date = DateOnly.FromDateTime(Utils.Today);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,7 +99,7 @@ namespace Elwig.Documents {
|
|||||||
return await Html.CompileRenderAsync(name, this); ;
|
return await Html.CompileRenderAsync(name, this); ;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Generate(IProgress<double>? progress = null) {
|
public async Task Generate(CancellationToken? cancelToken = null, IProgress<double>? progress = null) {
|
||||||
if (_pdfFile != null)
|
if (_pdfFile != null)
|
||||||
return;
|
return;
|
||||||
progress?.Report(0.0);
|
progress?.Report(0.0);
|
||||||
@@ -114,36 +109,50 @@ namespace Elwig.Documents {
|
|||||||
var pdf = new TempFile("pdf");
|
var pdf = new TempFile("pdf");
|
||||||
var tmpHtmls = new List<TempFile>();
|
var tmpHtmls = new List<TempFile>();
|
||||||
var tmpFiles = new List<string>();
|
var tmpFiles = new List<string>();
|
||||||
var n = m.Documents.Count();
|
try {
|
||||||
int i = 0;
|
var n = m.Documents.Count();
|
||||||
foreach (var doc in m.Documents) {
|
int i = 0;
|
||||||
if (doc is PdfDocument) {
|
foreach (var doc in m.Documents) {
|
||||||
tmpFiles.Add(doc.PdfPath!);
|
if (doc is PdfDocument) {
|
||||||
continue;
|
tmpFiles.Add(doc.PdfPath!);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (cancelToken?.IsCancellationRequested ?? false)
|
||||||
|
throw new OperationCanceledException("Dokumentenerzeugung abgebrochen!");
|
||||||
|
var tmpHtml = new TempFile("html");
|
||||||
|
await File.WriteAllTextAsync(tmpHtml.FilePath, await doc.Render(), Utils.UTF8);
|
||||||
|
tmpHtmls.Add(tmpHtml);
|
||||||
|
tmpFiles.Add((doc is Letterhead ? "#" : "") + tmpHtml.FileName);
|
||||||
|
i++;
|
||||||
|
progress?.Report(GenerationProportion * 100 * i / n);
|
||||||
|
}
|
||||||
|
progress?.Report(GenerationProportion * 100);
|
||||||
|
var pages = await Pdf.Convert(tmpFiles, pdf.FileName, IsDoublePaged, cancelToken, new Progress<double>(v => progress?.Report(GenerationProportion * 100 + v * (1 - GenerationProportion))));
|
||||||
|
TotalPages = pages.Pages;
|
||||||
|
_pdfFile = pdf;
|
||||||
|
} catch {
|
||||||
|
pdf.Dispose();
|
||||||
|
throw;
|
||||||
|
} finally {
|
||||||
|
foreach (var tmp in tmpHtmls) {
|
||||||
|
tmp.Dispose();
|
||||||
}
|
}
|
||||||
var tmpHtml = new TempFile("html");
|
|
||||||
await File.WriteAllTextAsync(tmpHtml.FilePath, await doc.Render(), Utils.UTF8);
|
|
||||||
tmpHtmls.Add(tmpHtml);
|
|
||||||
tmpFiles.Add((doc is Letterhead ? "#" : "") + tmpHtml.FileName);
|
|
||||||
i++;
|
|
||||||
progress?.Report(GenerationProportion * 100 * i / n);
|
|
||||||
}
|
}
|
||||||
progress?.Report(GenerationProportion * 100);
|
|
||||||
var pages = await Pdf.Convert(tmpFiles, pdf.FileName, DoublePaged, new Progress<double>(v => progress?.Report(GenerationProportion * 100 + v * (1 - GenerationProportion))));
|
|
||||||
TotalPages = pages.Pages;
|
|
||||||
foreach (var tmp in tmpHtmls) {
|
|
||||||
tmp.Dispose();
|
|
||||||
}
|
|
||||||
_pdfFile = pdf;
|
|
||||||
} else {
|
} else {
|
||||||
|
if (cancelToken?.IsCancellationRequested ?? false)
|
||||||
|
throw new OperationCanceledException("Dokumentenerzeugung abgebrochen!");
|
||||||
var pdf = new TempFile("pdf");
|
var pdf = new TempFile("pdf");
|
||||||
using (var tmpHtml = new TempFile("html")) {
|
try {
|
||||||
|
using var tmpHtml = new TempFile("html");
|
||||||
await File.WriteAllTextAsync(tmpHtml.FilePath, await Render(), Utils.UTF8);
|
await File.WriteAllTextAsync(tmpHtml.FilePath, await Render(), Utils.UTF8);
|
||||||
progress?.Report(50.0);
|
progress?.Report(50.0);
|
||||||
var pages = await Pdf.Convert(tmpHtml.FilePath, pdf.FilePath, DoublePaged);
|
var pages = await Pdf.Convert(tmpHtml.FilePath, pdf.FilePath, IsDoublePaged, cancelToken);
|
||||||
TotalPages = pages.Pages;
|
TotalPages = pages.Pages;
|
||||||
|
_pdfFile = pdf;
|
||||||
|
} catch {
|
||||||
|
pdf.Dispose();
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
_pdfFile = pdf;
|
|
||||||
}
|
}
|
||||||
progress?.Report(100.0);
|
progress?.Report(100.0);
|
||||||
}
|
}
|
||||||
@@ -155,7 +164,7 @@ namespace Elwig.Documents {
|
|||||||
|
|
||||||
public async Task Print(int copies = 1) {
|
public async Task Print(int copies = 1) {
|
||||||
if (PdfPath == null) throw new InvalidOperationException("Pdf file has not been generated yet");
|
if (PdfPath == null) throw new InvalidOperationException("Pdf file has not been generated yet");
|
||||||
await Pdf.Print(PdfPath, copies, DoublePaged);
|
await Pdf.Print(PdfPath, copies, IsDoublePaged);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Show() {
|
public void Show() {
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
<title>@Model.Title</title>
|
<title>@Model.Title</title>
|
||||||
<meta name="author" value="@Model.Author"/>
|
<meta name="author" value="@Model.Author"/>
|
||||||
<meta charset="UTF-8"/>
|
<meta charset="UTF-8"/>
|
||||||
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\Document.css"/>
|
<link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\Document.css" />
|
||||||
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\Document.Page.css"/>
|
<link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\Document.Page.css" />
|
||||||
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\Document.Table.css"/>
|
<link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\Document.Table.css" />
|
||||||
@if (Model.DoublePaged) {
|
@if (Model.IsDoublePaged) {
|
||||||
<style>
|
<style>
|
||||||
@@page :left {
|
@@page :left {
|
||||||
margin: 25mm 25mm 35mm 20mm;
|
margin: 25mm 25mm 35mm 20mm;
|
||||||
@@ -34,14 +34,14 @@
|
|||||||
<div class="pre-footer">
|
<div class="pre-footer">
|
||||||
<span class="date">@($"{Model.Date:dddd, d. MMMM yyyy}")</span>
|
<span class="date">@($"{Model.Date:dddd, d. MMMM yyyy}")</span>
|
||||||
<span class="doc-id">@Model.DocumentId</span>
|
<span class="doc-id">@Model.DocumentId</span>
|
||||||
<span class="page"></span>
|
<span><span class="page"></span>@Raw(Model.IsPreview ? " <b>(vorläufig)</b>" : "")</span>
|
||||||
</div>
|
</div>
|
||||||
<footer>@Raw(Model.Footer)</footer>
|
<footer>@Raw(Model.Footer)</footer>
|
||||||
</div>
|
</div>
|
||||||
@if (Model.DoublePaged) {
|
@if (Model.IsDoublePaged) {
|
||||||
<div class="footer-wrapper left">
|
<div class="footer-wrapper left">
|
||||||
<div class="pre-footer">
|
<div class="pre-footer">
|
||||||
<span class="page"></span>
|
<span>@Raw(Model.IsPreview ? "<b>(vorläufig)</b> " : "")<span class="page"></span></span>
|
||||||
<span class="doc-id">@Model.DocumentId</span>
|
<span class="doc-id">@Model.DocumentId</span>
|
||||||
<span class="date">@($"{Model.Date:dddd, d. MMMM yyyy}")</span>
|
<span class="date">@($"{Model.Date:dddd, d. MMMM yyyy}")</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
@inherits TemplatePage<Elwig.Documents.MemberDataSheet>
|
@inherits TemplatePage<Elwig.Documents.MemberDataSheet>
|
||||||
@model Elwig.Documents.MemberDataSheet
|
@model Elwig.Documents.MemberDataSheet
|
||||||
@{ Layout = "BusinessDocument"; }
|
@{ Layout = "BusinessDocument"; }
|
||||||
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\MemberDataSheet.css" />
|
<link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\MemberDataSheet.css" />
|
||||||
<main>
|
<main>
|
||||||
<h1>@Model.Title</h1>
|
<h1>@Model.Title</h1>
|
||||||
<table class="member border">
|
<table class="member border">
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
@inherits TemplatePage<Elwig.Documents.MemberList>
|
@inherits TemplatePage<Elwig.Documents.MemberList>
|
||||||
@model Elwig.Documents.MemberList
|
@model Elwig.Documents.MemberList
|
||||||
@{ Layout = "Document"; }
|
@{ Layout = "Document"; }
|
||||||
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\MemberList.css"/>
|
<link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\MemberList.css" />
|
||||||
<main>
|
<main>
|
||||||
<h1>Mitgliederliste</h1>
|
<h1>Mitgliederliste</h1>
|
||||||
<h2>@Model.Filter</h2>
|
<h2>@Model.Filter</h2>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
h1 {
|
h1 {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 24pt;
|
font-size: 24pt;
|
||||||
margin-top: 10mm;
|
margin-top: 0;
|
||||||
margin-bottom: 2mm;
|
margin-bottom: 2mm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ namespace Elwig.Documents {
|
|||||||
Data = data;
|
Data = data;
|
||||||
CurrencySymbol = v.Season.Currency.Symbol ?? v.Season.Currency.Code;
|
CurrencySymbol = v.Season.Currency.Symbol ?? v.Season.Currency.Code;
|
||||||
MemberNum = v.Credits.Count;
|
MemberNum = v.Credits.Count;
|
||||||
|
IsPreview = MemberNum == 0;
|
||||||
DeliveryNum = v.DeliveryPartPayments.DistinctBy(p => p.DeliveryPart.Delivery).Count();
|
DeliveryNum = v.DeliveryPartPayments.DistinctBy(p => p.DeliveryPart.Delivery).Count();
|
||||||
DeliveryPartNum = v.DeliveryPartPayments.Count;
|
DeliveryPartNum = v.DeliveryPartPayments.Count;
|
||||||
ModifierStat = AppDbContext.GetModifierStats(v.Year, v.AvNr).GetAwaiter().GetResult();
|
ModifierStat = AppDbContext.GetModifierStats(v.Year, v.AvNr).GetAwaiter().GetResult();
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
@inherits TemplatePage<Elwig.Documents.PaymentVariantSummary>
|
@inherits TemplatePage<Elwig.Documents.PaymentVariantSummary>
|
||||||
@model Elwig.Documents.PaymentVariantSummary
|
@model Elwig.Documents.PaymentVariantSummary
|
||||||
@{ Layout = "Document"; }
|
@{ Layout = "Document"; }
|
||||||
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\PaymentVariantSummary.css" />
|
<link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\PaymentVariantSummary.css" />
|
||||||
<main>
|
<main>
|
||||||
<h1>Auszahlungsvariante Lese @Model.Variant.Year</h1>
|
<h1>Auszahlungsvariante Lese @Model.Variant.Year</h1>
|
||||||
<h2>@Model.Variant.Name</h2>
|
<h2>@Model.Variant.Name</h2>
|
||||||
@@ -111,8 +111,8 @@
|
|||||||
<td class="number"><span class="fleft">@Model.CurrencySymbol</span>@($"{Math.Abs(payed):N2}")</td>
|
<td class="number"><span class="fleft">@Model.CurrencySymbol</span>@($"{Math.Abs(payed):N2}")</td>
|
||||||
@{
|
@{
|
||||||
var weiRows = Model.Data.Rows.Where(r => r.QualityLevel == "Wein");
|
var weiRows = Model.Data.Rows.Where(r => r.QualityLevel == "Wein");
|
||||||
var minWei = weiRows.Min(r => r.Ungeb.Price);
|
var minWei = weiRows.Min(r => r.Ungeb.MinPrice);
|
||||||
var maxWei = weiRows.Max(r => r.Ungeb.Price);
|
var maxWei = weiRows.Max(r => r.Ungeb.MaxPrice);
|
||||||
}
|
}
|
||||||
<th class="lborder tborder">Preis (abgewertet):</th>
|
<th class="lborder tborder">Preis (abgewertet):</th>
|
||||||
<td colspan="2" class="center tborder">@(minWei != maxWei ? $"{minWei:N4}–{maxWei:N4}" : $"{minWei:N4}") @Model.CurrencySymbol/kg</td>
|
<td colspan="2" class="center tborder">@(minWei != maxWei ? $"{minWei:N4}–{maxWei:N4}" : $"{minWei:N4}") @Model.CurrencySymbol/kg</td>
|
||||||
@@ -123,8 +123,8 @@
|
|||||||
<td class="number tborder"><span class="fleft">@Model.CurrencySymbol</span>@($"{netSum:N2}")</td>
|
<td class="number tborder"><span class="fleft">@Model.CurrencySymbol</span>@($"{netSum:N2}")</td>
|
||||||
@{
|
@{
|
||||||
var quwRows = Model.Data.Rows.Where(r => r.QualityLevel != "Wein");
|
var quwRows = Model.Data.Rows.Where(r => r.QualityLevel != "Wein");
|
||||||
var minPrice = quwRows.Min(r => r.Ungeb.Price);
|
var minPrice = quwRows.Min(r => r.Ungeb.MinPrice);
|
||||||
var maxPrice = quwRows.Max(r => r.Ungeb.Price);
|
var maxPrice = quwRows.Max(r => r.Ungeb.MaxPrice);
|
||||||
}
|
}
|
||||||
<th class="lborder">Preis (ungeb., nicht abgew.):</th>
|
<th class="lborder">Preis (ungeb., nicht abgew.):</th>
|
||||||
<td colspan="2" class="center">@(minPrice != maxPrice ? $"{minPrice:N4}–{maxPrice:N4}" : $"{minPrice:N4}") @Model.CurrencySymbol/kg</td>
|
<td colspan="2" class="center">@(minPrice != maxPrice ? $"{minPrice:N4}–{maxPrice:N4}" : $"{minPrice:N4}") @Model.CurrencySymbol/kg</td>
|
||||||
@@ -135,8 +135,8 @@
|
|||||||
<td class="number"><span class="fleft">@Model.CurrencySymbol</span>@($"{Math.Abs(vat):N2}")</td>
|
<td class="number"><span class="fleft">@Model.CurrencySymbol</span>@($"{Math.Abs(vat):N2}")</td>
|
||||||
@{
|
@{
|
||||||
var gebRows = Model.Data.Rows
|
var gebRows = Model.Data.Rows
|
||||||
.Where(r => r.Geb.Price != null && r.Ungeb.Price != null)
|
.Where(r => r.Geb.MaxPrice != null && r.Ungeb.MinPrice != null)
|
||||||
.Select(r => r.Geb.Price - r.Ungeb.Price);
|
.Select(r => r.Geb.MaxPrice - r.Ungeb.MinPrice);
|
||||||
var minGeb = gebRows.Min();
|
var minGeb = gebRows.Min();
|
||||||
var maxGeb = gebRows.Max();
|
var maxGeb = gebRows.Max();
|
||||||
}
|
}
|
||||||
@@ -164,14 +164,14 @@
|
|||||||
<td class="number">@Utils.GetSign(considered)</td>
|
<td class="number">@Utils.GetSign(considered)</td>
|
||||||
<td class="number"><span class="fleft">@Model.CurrencySymbol</span>@($"{Math.Abs(considered):N2}")</td>
|
<td class="number"><span class="fleft">@Model.CurrencySymbol</span>@($"{Math.Abs(considered):N2}")</td>
|
||||||
<th class="lborder">Menge (gebunden):</th>
|
<th class="lborder">Menge (gebunden):</th>
|
||||||
<td colspan="2" class="number">@($"{Model.Data.Rows.Sum(r => r.Geb.Weight):N0}") kg</td>
|
<td colspan="2" class="number">@($"{Model.Data.Rows.Sum(r => r.Geb.Weight + r.LowGeb.Weight):N0}") kg</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th colspan="2">Auszahlungsbetrag:</th>
|
<th colspan="2">Auszahlungsbetrag:</th>
|
||||||
<td class="number tborder"></td>
|
<td class="number tborder"></td>
|
||||||
<td class="number tborder"><span class="fleft">@Model.CurrencySymbol</span>@($"{totalSum:N2}")</td>
|
<td class="number tborder"><span class="fleft">@Model.CurrencySymbol</span>@($"{totalSum:N2}")</td>
|
||||||
<th class="lborder">Gesamtmenge:</th>
|
<th class="lborder">Gesamtmenge:</th>
|
||||||
<td colspan="2" class="number tborder">@($"{Model.Data.Rows.Sum(r => r.Ungeb.Weight + r.Geb.Weight):N0}") kg</td>
|
<td colspan="2" class="number tborder">@($"{Model.Data.Rows.Sum(r => r.Ungeb.Weight + r.LowGeb.Weight + r.Geb.Weight):N0}") kg</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@@ -219,19 +219,22 @@
|
|||||||
</table>
|
</table>
|
||||||
<table class="payment-variant-data">
|
<table class="payment-variant-data">
|
||||||
<colgroup>
|
<colgroup>
|
||||||
<col style="width: 30mm;"/>
|
|
||||||
<col style="width: 20mm;"/>
|
|
||||||
<col style="width: 25mm;"/>
|
|
||||||
<col style="width: 20mm;"/>
|
|
||||||
<col style="width: 25mm;"/>
|
|
||||||
<col style="width: 20mm;"/>
|
|
||||||
<col style="width: 25mm;"/>
|
<col style="width: 25mm;"/>
|
||||||
|
<col style="width: 19mm;"/>
|
||||||
|
<col style="width: 18mm;"/>
|
||||||
|
<col style="width: 15mm;"/>
|
||||||
|
<col style="width: 18mm;"/>
|
||||||
|
<col style="width: 15mm;"/>
|
||||||
|
<col style="width: 18mm;"/>
|
||||||
|
<col style="width: 15mm;"/>
|
||||||
|
<col style="width: 22mm;"/>
|
||||||
</colgroup>
|
</colgroup>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th rowspan="2" style="text-align: left;">Qualitätsstufe</th>
|
<th rowspan="2" style="text-align: left;">Qualitätsstufe</th>
|
||||||
<th>Gradation</th>
|
<th>Gradation</th>
|
||||||
<th colspan="2">ungebunden</th>
|
<th colspan="2">ungebunden</th>
|
||||||
|
<th colspan="2">attributlos gebunden</th>
|
||||||
<th colspan="2">gebunden</th>
|
<th colspan="2">gebunden</th>
|
||||||
<th>Gesamt</th>
|
<th>Gesamt</th>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -241,6 +244,8 @@
|
|||||||
<th>[@(Model.CurrencySymbol)/kg]</th>
|
<th>[@(Model.CurrencySymbol)/kg]</th>
|
||||||
<th>[kg]</th>
|
<th>[kg]</th>
|
||||||
<th>[@(Model.CurrencySymbol)/kg]</th>
|
<th>[@(Model.CurrencySymbol)/kg]</th>
|
||||||
|
<th>[kg]</th>
|
||||||
|
<th>[@(Model.CurrencySymbol)/kg]</th>
|
||||||
<th>[@(Model.CurrencySymbol)]</th>
|
<th>[@(Model.CurrencySymbol)]</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -258,6 +263,8 @@
|
|||||||
<th colspan="2">@hdr</th>
|
<th colspan="2">@hdr</th>
|
||||||
<td class="number">@($"{rows.Sum(r => r.Ungeb.Weight):N0}")</td>
|
<td class="number">@($"{rows.Sum(r => r.Ungeb.Weight):N0}")</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
|
<td class="number">@($"{rows.Sum(r => r.LowGeb.Weight):N0}")</td>
|
||||||
|
<td></td>
|
||||||
<td class="number">@($"{rows.Sum(r => r.Geb.Weight):N0}")</td>
|
<td class="number">@($"{rows.Sum(r => r.Geb.Weight):N0}")</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td class="number">@($"{rows.Sum(r => r.Amount):N2}")</td>
|
<td class="number">@($"{rows.Sum(r => r.Amount):N2}")</td>
|
||||||
@@ -267,9 +274,11 @@
|
|||||||
<td>@(row.QualityLevel)</td>
|
<td>@(row.QualityLevel)</td>
|
||||||
<td class="center">@($"{row.Oe:N0}")</td>
|
<td class="center">@($"{row.Oe:N0}")</td>
|
||||||
<td class="number">@(row.Ungeb.Weight != 0 ? $"{row.Ungeb.Weight:N0}" : "-")</td>
|
<td class="number">@(row.Ungeb.Weight != 0 ? $"{row.Ungeb.Weight:N0}" : "-")</td>
|
||||||
<td class="number">@(row.Ungeb.Price != null ? $"{row.Ungeb.Price:N4}" : "-")</td>
|
<td class="number">@(row.Ungeb.MaxPrice != null ? $"{row.Ungeb.MaxPrice:N4}" : "-")</td>
|
||||||
|
<td class="number">@(row.LowGeb.Weight != 0 ? $"{row.LowGeb.Weight:N0}" : "-")</td>
|
||||||
|
<td class="number">@(row.LowGeb.MaxPrice != null ? $"{row.LowGeb.MaxPrice:N4}" : "-")</td>
|
||||||
<td class="number">@(row.Geb.Weight != 0 ? $"{row.Geb.Weight:N0}" : "-")</td>
|
<td class="number">@(row.Geb.Weight != 0 ? $"{row.Geb.Weight:N0}" : "-")</td>
|
||||||
<td class="number">@(row.Geb.Price != null ? $"{row.Geb.Price:N4}" : "-")</td>
|
<td class="number">@(row.Geb.MaxPrice != null ? $"{row.Geb.MaxPrice:N4}" : "-")</td>
|
||||||
<td class="number">@($"{row.Amount:N2}")</td>
|
<td class="number">@($"{row.Amount:N2}")</td>
|
||||||
</tr>
|
</tr>
|
||||||
lastHdr = hdr;
|
lastHdr = hdr;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
h1 {
|
h1 {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 24pt;
|
font-size: 24pt;
|
||||||
margin-top: 10mm;
|
margin-top: 0;
|
||||||
margin-bottom: 2mm;
|
margin-bottom: 2mm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
@inherits TemplatePage<Elwig.Documents.WineQualityStatistics>
|
@inherits TemplatePage<Elwig.Documents.WineQualityStatistics>
|
||||||
@model Elwig.Documents.WineQualityStatistics
|
@model Elwig.Documents.WineQualityStatistics
|
||||||
@{ Layout = "Document"; }
|
@{ Layout = "Document"; }
|
||||||
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\WineQualityStatistics.css"/>
|
<link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\WineQualityStatistics.css" />
|
||||||
<main>
|
<main>
|
||||||
<h1>Qualitätsstatistik</h1>
|
<h1>Qualitätsstatistik</h1>
|
||||||
<h2>@Model.Filter</h2>
|
<h2>@Model.Filter</h2>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
h1 {
|
h1 {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 24pt;
|
font-size: 24pt;
|
||||||
margin-top: 10mm;
|
margin-top: 0;
|
||||||
margin-bottom: 2mm;
|
margin-bottom: 2mm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFramework>net8.0-windows</TargetFramework>
|
<TargetFramework>net10.0-windows</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<UseWPF>true</UseWPF>
|
<UseWPF>true</UseWPF>
|
||||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||||
<ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon>
|
<ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon>
|
||||||
<Version>0.13.5</Version>
|
<Version>1.0.3.0</Version>
|
||||||
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
|
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
@@ -20,26 +20,25 @@
|
|||||||
<EmbeddedResource Include="Resources\Sql\*" />
|
<EmbeddedResource Include="Resources\Sql\*" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
|
||||||
<Exec Command="call fetch-resources.bat" />
|
|
||||||
</Target>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||||
<PackageReference Include="LinqKit" Version="1.3.7" />
|
<PackageReference Include="LinqKit" Version="1.3.9" />
|
||||||
<PackageReference Include="MailKit" Version="4.9.0" />
|
<PackageReference Include="MailKit" Version="4.14.1" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.36" />
|
<PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.36" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="9.0.0" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="10.0.2" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.0" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.2" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="9.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="10.0.2" />
|
||||||
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2903.40" />
|
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.3650.58" />
|
||||||
<PackageReference Include="NJsonSchema" Version="11.1.0" />
|
<PackageReference Include="NJsonSchema" Version="11.5.2" />
|
||||||
<PackageReference Include="PdfiumViewer" Version="2.13.0" />
|
<PackageReference Include="PdfiumViewer" Version="2.13.0" />
|
||||||
<PackageReference Include="PdfiumViewer.Native.x86_64.no_v8-no_xfa" Version="2018.4.8.256" />
|
<PackageReference Include="PdfiumViewer.Native.x86_64.no_v8-no_xfa" Version="2018.4.8.256" />
|
||||||
<PackageReference Include="RazorLight" Version="2.3.1" />
|
<PackageReference Include="RazorLight" Version="2.3.1" />
|
||||||
<PackageReference Include="ScottPlot.WPF" Version="5.0.47" />
|
<PackageReference Include="ScottPlot.WPF" Version="5.1.57" />
|
||||||
<PackageReference Include="System.IO.Ports" Version="9.0.0" />
|
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="3.0.2" />
|
||||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.0" />
|
<PackageReference Include="System.IO.Hashing" Version="10.0.2" />
|
||||||
|
<PackageReference Include="System.IO.Ports" Version="10.0.2" />
|
||||||
|
<PackageReference Include="System.Management" Version="10.0.2" />
|
||||||
|
<PackageReference Include="System.Text.Encoding.CodePages" Version="10.0.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ using Microsoft.Data.Sqlite;
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Elwig.Models.Dtos;
|
using Elwig.Models.Dtos;
|
||||||
using System.Reflection;
|
|
||||||
using System.Data;
|
using System.Data;
|
||||||
|
|
||||||
namespace Elwig.Helpers {
|
namespace Elwig.Helpers {
|
||||||
@@ -67,7 +66,7 @@ namespace Elwig.Helpers {
|
|||||||
|
|
||||||
public DbSet<OverUnderDeliveryRow> OverUnderDeliveryRows { get; private set; }
|
public DbSet<OverUnderDeliveryRow> OverUnderDeliveryRows { get; private set; }
|
||||||
public DbSet<AreaComUnderDeliveryRowSingle> AreaComUnderDeliveryRows { get; private set; }
|
public DbSet<AreaComUnderDeliveryRowSingle> AreaComUnderDeliveryRows { get; private set; }
|
||||||
public DbSet<MemberDeliveryPerVariantRowSingle> MemberDeliveryPerVariantRows { get; private set; }
|
public DbSet<MemberDeliveryPerVarietyRowSingle> MemberDeliveryPerVariantRows { get; private set; }
|
||||||
public DbSet<MemberAreaComsRowSingle> MemberAreaComsRows { get; private set; }
|
public DbSet<MemberAreaComsRowSingle> MemberAreaComsRows { get; private set; }
|
||||||
public DbSet<CreditNoteDeliveryRowSingle> CreditNoteDeliveryRows { get; private set; }
|
public DbSet<CreditNoteDeliveryRowSingle> CreditNoteDeliveryRows { get; private set; }
|
||||||
public DbSet<CreditNoteRowSingle> CreditNoteRows { get; private set; }
|
public DbSet<CreditNoteRowSingle> CreditNoteRows { get; private set; }
|
||||||
@@ -117,39 +116,6 @@ namespace Elwig.Helpers {
|
|||||||
return cnx;
|
return cnx;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task ExecuteBatch(SqliteConnection cnx, string sql) {
|
|
||||||
using var cmd = cnx.CreateCommand();
|
|
||||||
cmd.CommandText = sql;
|
|
||||||
await (await cmd.ExecuteReaderAsync()).CloseAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task ExecuteEmbeddedScript(SqliteConnection cnx, Assembly asm, string name) {
|
|
||||||
using var stream = asm.GetManifestResourceStream(name) ?? throw new FileNotFoundException("Unable to load embedded resource");
|
|
||||||
using var reader = new StreamReader(stream);
|
|
||||||
await ExecuteBatch(cnx, await reader.ReadToEndAsync());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<object?> ExecuteScalar(SqliteConnection cnx, string sql) {
|
|
||||||
using var cmd = cnx.CreateCommand();
|
|
||||||
cmd.CommandText = sql;
|
|
||||||
return await cmd.ExecuteScalarAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<(string Table, long RowId, string Parent, long FkId)[]> ForeignKeyCheck(SqliteConnection cnx) {
|
|
||||||
using var cmd = cnx.CreateCommand();
|
|
||||||
cmd.CommandText = "PRAGMA foreign_key_check";
|
|
||||||
using var reader = await cmd.ExecuteReaderAsync();
|
|
||||||
var list = new List<(string, long, string, long)>();
|
|
||||||
while (await reader.ReadAsync()) {
|
|
||||||
var table = reader.GetString(0);
|
|
||||||
var rowid = reader.GetInt64(1);
|
|
||||||
var parent = reader.GetString(2);
|
|
||||||
var fkid = reader.GetInt64(3);
|
|
||||||
list.Add((table, rowid, parent, fkid));
|
|
||||||
}
|
|
||||||
return [.. list];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
|
||||||
optionsBuilder.UseSqlite(ConnectionString);
|
optionsBuilder.UseSqlite(ConnectionString);
|
||||||
optionsBuilder.UseLazyLoadingProxies();
|
optionsBuilder.UseLazyLoadingProxies();
|
||||||
@@ -248,13 +214,6 @@ namespace Elwig.Helpers {
|
|||||||
return c + 1;
|
return c + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<WineQualLevel> GetWineQualityLevel(double kmw) {
|
|
||||||
return await WineQualityLevels
|
|
||||||
.Where(q => !q.IsPredicate && (q.MinKmw == null || q.MinKmw <= kmw))
|
|
||||||
.OrderBy(q => q.MinKmw)
|
|
||||||
.LastAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateDeliveryPartModifiers(DeliveryPart part, IEnumerable<Modifier> oldModifiers, IEnumerable<Modifier> newModifiers) {
|
public void UpdateDeliveryPartModifiers(DeliveryPart part, IEnumerable<Modifier> oldModifiers, IEnumerable<Modifier> newModifiers) {
|
||||||
foreach (var m in Modifiers.Where(m => m.Year == part.Year)) {
|
foreach (var m in Modifiers.Where(m => m.Year == part.Year)) {
|
||||||
var mod = new DeliveryPartModifier {
|
var mod = new DeliveryPartModifier {
|
||||||
|
|||||||
@@ -9,17 +9,17 @@ namespace Elwig.Helpers {
|
|||||||
public static class AppDbUpdater {
|
public static class AppDbUpdater {
|
||||||
|
|
||||||
// Don't forget to update value in Tests/fetch-resources.bat!
|
// Don't forget to update value in Tests/fetch-resources.bat!
|
||||||
public static readonly int RequiredSchemaVersion = 31;
|
public static readonly int RequiredSchemaVersion = 36;
|
||||||
|
|
||||||
private static int VersionOffset = 0;
|
private static int VersionOffset = 0;
|
||||||
|
|
||||||
public static async Task<Version> CheckDb() {
|
public static async Task<Version> CheckDb() {
|
||||||
using var cnx = AppDbContext.Connect();
|
using var cnx = AppDbContext.Connect();
|
||||||
|
|
||||||
var applId = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA application_id") ?? 0;
|
var applId = (long?)await cnx.ExecuteScalar("PRAGMA application_id") ?? 0;
|
||||||
if (applId != 0x454C5747) throw new Exception($"Invalid application_id in database (0x{applId:X08})");
|
if (applId != 0x454C5747) throw new Exception($"Invalid application_id in database (0x{applId:X08})");
|
||||||
|
|
||||||
var schemaVers = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA schema_version") ?? 0;
|
var schemaVers = (long?)await cnx.ExecuteScalar("PRAGMA schema_version") ?? 0;
|
||||||
VersionOffset = (int)(schemaVers % 100);
|
VersionOffset = (int)(schemaVers % 100);
|
||||||
if (VersionOffset != 0) {
|
if (VersionOffset != 0) {
|
||||||
// schema was modified manually/externally
|
// schema was modified manually/externally
|
||||||
@@ -27,12 +27,12 @@ namespace Elwig.Helpers {
|
|||||||
}
|
}
|
||||||
await UpdateDbSchema(cnx, (int)(schemaVers / 100), RequiredSchemaVersion);
|
await UpdateDbSchema(cnx, (int)(schemaVers / 100), RequiredSchemaVersion);
|
||||||
|
|
||||||
var userVers = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA user_version") ?? 0;
|
var userVers = (long?)await cnx.ExecuteScalar("PRAGMA user_version") ?? 0;
|
||||||
var v = new Version((int)(userVers >> 24), (int)((userVers >> 16) & 0xFF), (int)(userVers & 0xFFFF));
|
var v = new Version((int)(userVers >> 24), (int)((userVers >> 16) & 0xFF), (int)((userVers >> 8) & 0xFF), (int)(userVers & 0xFF));
|
||||||
|
|
||||||
if (App.Version > v) {
|
if (App.Version > v) {
|
||||||
long vers = (App.Version.Major << 24) | (App.Version.Minor << 16) | App.Version.Build;
|
long vers = (App.Version.Major << 24) | (App.Version.Minor << 16) | (App.Version.Build << 8) | App.Version.Revision;
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"PRAGMA user_version = {vers}");
|
await cnx.ExecuteBatch($"PRAGMA user_version = {vers}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
@@ -67,20 +67,20 @@ namespace Elwig.Helpers {
|
|||||||
if (toExecute.Count == 0)
|
if (toExecute.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await AppDbContext.ExecuteBatch(cnx, """
|
await cnx.ExecuteBatch("""
|
||||||
PRAGMA locking_mode = EXCLUSIVE;
|
PRAGMA locking_mode = EXCLUSIVE;
|
||||||
BEGIN EXCLUSIVE;
|
BEGIN EXCLUSIVE;
|
||||||
""");
|
""");
|
||||||
foreach (var script in toExecute) {
|
foreach (var script in toExecute) {
|
||||||
await AppDbContext.ExecuteEmbeddedScript(cnx, asm, script);
|
await cnx.ExecuteEmbeddedScript(asm, script);
|
||||||
}
|
}
|
||||||
var violations = await AppDbContext.ForeignKeyCheck(cnx);
|
var violations = await cnx.ForeignKeyCheck();
|
||||||
if (violations.Length > 0) {
|
if (violations.Length > 0) {
|
||||||
throw new Exception($"Foreign key violations ({violations.Length}):\n" + string.Join("\n", violations
|
throw new Exception($"Foreign key violations ({violations.Length}):\n" + string.Join("\n", violations
|
||||||
.Select(v => $"{v.Table} - {v.RowId} - {v.Parent} - {v.FkId}")));
|
.Select(v => $"{v.Table} - {v.RowId} - {v.Parent} - {v.FkId}")));
|
||||||
}
|
}
|
||||||
|
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"""
|
await cnx.ExecuteBatch($"""
|
||||||
COMMIT;
|
COMMIT;
|
||||||
VACUUM;
|
VACUUM;
|
||||||
PRAGMA schema_version = {toVersion * 100 + VersionOffset};
|
PRAGMA schema_version = {toVersion * 100 + VersionOffset};
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ namespace Elwig.Helpers.Billing {
|
|||||||
|
|
||||||
public async Task FinishSeason() {
|
public async Task FinishSeason() {
|
||||||
using var cnx = await AppDbContext.ConnectAsync();
|
using var cnx = await AppDbContext.ConnectAsync();
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"""
|
await cnx.ExecuteBatch($"""
|
||||||
UPDATE season
|
UPDATE season
|
||||||
SET (start_date, end_date) = (SELECT MIN(date), MAX(date) FROM delivery WHERE year = {Year})
|
SET (start_date, end_date) = (SELECT MIN(date), MAX(date) FROM delivery WHERE year = {Year})
|
||||||
WHERE year = {Year};
|
WHERE year = {Year};
|
||||||
@@ -37,7 +37,7 @@ namespace Elwig.Helpers.Billing {
|
|||||||
public async Task AutoAdjustBusinessShares(DateOnly date, int allowanceKg = 0, double allowanceBs = 0, int allowanceKgPerBs = 0, double allowanceRel = 0, int addMinBs = 1) {
|
public async Task AutoAdjustBusinessShares(DateOnly date, int allowanceKg = 0, double allowanceBs = 0, int allowanceKgPerBs = 0, double allowanceRel = 0, int addMinBs = 1) {
|
||||||
if (addMinBs < 1) addMinBs = 1;
|
if (addMinBs < 1) addMinBs = 1;
|
||||||
using var cnx = await AppDbContext.ConnectAsync();
|
using var cnx = await AppDbContext.ConnectAsync();
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"""
|
await cnx.ExecuteBatch($"""
|
||||||
UPDATE member
|
UPDATE member
|
||||||
SET business_shares = member.business_shares - h.business_shares
|
SET business_shares = member.business_shares - h.business_shares
|
||||||
FROM member_history h
|
FROM member_history h
|
||||||
@@ -66,7 +66,7 @@ namespace Elwig.Helpers.Billing {
|
|||||||
|
|
||||||
public async Task UnAdjustBusinessShares() {
|
public async Task UnAdjustBusinessShares() {
|
||||||
using var cnx = await AppDbContext.ConnectAsync();
|
using var cnx = await AppDbContext.ConnectAsync();
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"""
|
await cnx.ExecuteBatch($"""
|
||||||
UPDATE member
|
UPDATE member
|
||||||
SET business_shares = member.business_shares - h.business_shares
|
SET business_shares = member.business_shares - h.business_shares
|
||||||
FROM member_history h
|
FROM member_history h
|
||||||
@@ -157,9 +157,9 @@ namespace Elwig.Helpers.Billing {
|
|||||||
lastMgNr = mgnr;
|
lastMgNr = mgnr;
|
||||||
}
|
}
|
||||||
|
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"UPDATE delivery_part_bucket SET value = 0 WHERE year = {Year}");
|
await cnx.ExecuteBatch($"UPDATE delivery_part_bucket SET value = 0 WHERE year = {Year}");
|
||||||
if (inserts.Count > 0) {
|
if (inserts.Count > 0) {
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"""
|
await cnx.ExecuteBatch($"""
|
||||||
INSERT INTO delivery_part_bucket (year, did, dpnr, bktnr, discr, value)
|
INSERT INTO delivery_part_bucket (year, did, dpnr, bktnr, discr, value)
|
||||||
VALUES {string.Join(",\n ", inserts.Select(i => $"({Year}, {i.Item1}, {i.Item2}, {i.Item3}, '{i.Item4}', {i.Item5})"))}
|
VALUES {string.Join(",\n ", inserts.Select(i => $"({Year}, {i.Item1}, {i.Item2}, {i.Item3}, '{i.Item4}', {i.Item5})"))}
|
||||||
ON CONFLICT DO UPDATE
|
ON CONFLICT DO UPDATE
|
||||||
@@ -237,7 +237,7 @@ namespace Elwig.Helpers.Billing {
|
|||||||
if (needed == 0) break;
|
if (needed == 0) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"""
|
await cnx.ExecuteBatch($"""
|
||||||
INSERT INTO delivery_part_bucket (year, did, dpnr, bktnr, discr, value)
|
INSERT INTO delivery_part_bucket (year, did, dpnr, bktnr, discr, value)
|
||||||
VALUES {string.Join(",\n ", posChanges.Select(i => $"({Year}, {i.Item1}, {i.Item2}, {i.Item3}, '', {i.Item4})"))}
|
VALUES {string.Join(",\n ", posChanges.Select(i => $"({Year}, {i.Item1}, {i.Item2}, {i.Item3}, '', {i.Item4})"))}
|
||||||
ON CONFLICT DO UPDATE
|
ON CONFLICT DO UPDATE
|
||||||
|
|||||||
@@ -299,22 +299,30 @@ namespace Elwig.Helpers.Billing {
|
|||||||
return (rev1, rev2);
|
return (rev1, rev2);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static void CollapsePaymentData(JsonObject data, IEnumerable<RawVaribute> vaributes, bool useDefault = true) {
|
protected static void CollapsePaymentData(JsonObject data, JsonObject originalData, IEnumerable<RawVaribute> vaributes, bool useDefault = true) {
|
||||||
var (rev1, rev2) = GetReverseKeys(data);
|
var (rev1, rev2) = GetReverseKeys(data);
|
||||||
if (!data.ContainsKey("default")) {
|
if (!data.ContainsKey("default")) {
|
||||||
foreach (var (v, ks) in rev1) {
|
foreach (var (v, ks) in rev1) {
|
||||||
if ((ks.Count >= vaributes.Count() * 0.5 && useDefault) || ks.Count == vaributes.Count()) {
|
if ((ks.Count > vaributes.Count() * 0.5 && useDefault) || ks.Count == vaributes.Count()) {
|
||||||
foreach (var k in ks) data.Remove(k);
|
foreach (var k in ks) {
|
||||||
|
if (!(originalData[$"{k[..2]}/"]?.AsValue().TryGetValue<string>(out var o) ?? false) || o == v)
|
||||||
|
if (!(originalData[k.Split('-')[0]]?.AsValue().TryGetValue<string>(out var o2) ?? false) || o2 == v)
|
||||||
|
data.Remove(k);
|
||||||
|
}
|
||||||
data["default"] = v;
|
data["default"] = v;
|
||||||
CollapsePaymentData(data, vaributes, useDefault);
|
CollapsePaymentData(data, originalData, vaributes, useDefault);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach (var (v, ks) in rev2) {
|
foreach (var (v, ks) in rev2) {
|
||||||
if ((ks.Count >= vaributes.Count() * 0.5 && useDefault) || ks.Count == vaributes.Count()) {
|
if ((ks.Count > vaributes.Count() * 0.5 && useDefault) || ks.Count == vaributes.Count()) {
|
||||||
foreach (var k in ks) data.Remove(k);
|
foreach (var k in ks) {
|
||||||
|
if (!(originalData[$"{k[..2]}/"]?.AsValue().TryGetValue<decimal>(out var o) ?? false) || o == v)
|
||||||
|
if (!(originalData[k.Split('-')[0]]?.AsValue().TryGetValue<decimal>(out var o2) ?? false) || o2 == v)
|
||||||
|
data.Remove(k);
|
||||||
|
}
|
||||||
data["default"] = v;
|
data["default"] = v;
|
||||||
CollapsePaymentData(data, vaributes, useDefault);
|
CollapsePaymentData(data, originalData, vaributes, useDefault);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -330,16 +338,26 @@ namespace Elwig.Helpers.Billing {
|
|||||||
var len = vaributes.Count(e => $"{e.AttrId}{(e.CultId != null && e.CultId != "" ? "-" : "")}{e.CultId}" == idx);
|
var len = vaributes.Count(e => $"{e.AttrId}{(e.CultId != null && e.CultId != "" ? "-" : "")}{e.CultId}" == idx);
|
||||||
foreach (var (v, ks) in rev1) {
|
foreach (var (v, ks) in rev1) {
|
||||||
var myKs = ks.Where(k => k.EndsWith($"/{idx}")).ToList();
|
var myKs = ks.Where(k => k.EndsWith($"/{idx}")).ToList();
|
||||||
if (myKs.Count > 1 && ((myKs.Count >= len * 0.5 && useDefault) || myKs.Count == len)) {
|
if (myKs.Count > 1 && ((myKs.Count > len * 0.5 && useDefault) || myKs.Count == len)) {
|
||||||
foreach (var k in myKs) data.Remove(k);
|
foreach (var k in myKs) data.Remove(k);
|
||||||
data[(idx.StartsWith('-') && !useDefault ? "" : "/") + idx] = v;
|
var discr = (idx.StartsWith('-') && !useDefault ? "" : "/") + idx;
|
||||||
|
data[discr] = v;
|
||||||
|
foreach (var (k, o) in originalData) {
|
||||||
|
if (o!.AsValue().TryGetValue<string>(out var o2) && o2 != v && k.Contains(discr))
|
||||||
|
data[k] = o2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach (var (v, ks) in rev2) {
|
foreach (var (v, ks) in rev2) {
|
||||||
var myKs = ks.Where(k => k.EndsWith($"/{idx}")).ToList();
|
var myKs = ks.Where(k => k.EndsWith($"/{idx}")).ToList();
|
||||||
if (myKs.Count > 1 && ((myKs.Count >= len * 0.5 && useDefault) || myKs.Count == len)) {
|
if (myKs.Count > 1 && ((myKs.Count > len * 0.5 && useDefault) || myKs.Count == len)) {
|
||||||
foreach (var k in myKs) data.Remove(k);
|
foreach (var k in myKs) data.Remove(k);
|
||||||
data[(idx.StartsWith('-') && !useDefault ? "" : "/") + idx] = v;
|
var discr = (idx.StartsWith('-') && !useDefault ? "" : "/") + idx;
|
||||||
|
data[discr] = v;
|
||||||
|
foreach (var (k, o) in originalData) {
|
||||||
|
if (o!.AsValue().TryGetValue<decimal>(out var o2) && o2 != v && k.Contains(discr))
|
||||||
|
data[k] = o2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -355,13 +373,22 @@ namespace Elwig.Helpers.Billing {
|
|||||||
} else if (k.Contains("/-")) {
|
} else if (k.Contains("/-")) {
|
||||||
data.Remove(k, out var val);
|
data.Remove(k, out var val);
|
||||||
data.Add(k.Replace("/-", "-"), val);
|
data.Add(k.Replace("/-", "-"), val);
|
||||||
|
if (k[0] == '/' || k.Contains('-')) {
|
||||||
|
foreach (var (k2, o) in originalData) {
|
||||||
|
if (!data.ContainsKey(k2) && k2.Contains('-') && k2.Contains("-" + k.Split('-')[1]) && !k2.Contains("/-")
|
||||||
|
&& (!k2.Contains('/') || k2.Length <= 4 || !data.ContainsKey(k2[2..])))
|
||||||
|
{
|
||||||
|
data[k2] = o?.DeepClone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(rev1, rev2) = GetReverseKeys(data, false);
|
(rev1, rev2) = GetReverseKeys(data, false);
|
||||||
var keyVaributes = data
|
var keyVaributes = data
|
||||||
.Select(e => e.Key.Split('-')[0])
|
.Select(e => e.Key)
|
||||||
.Where(e => e.Length > 0 && e != "default")
|
.Where(e => e.Length > 0 && !e.Contains('-') && e != "default")
|
||||||
.Distinct()
|
.Distinct()
|
||||||
.ToList();
|
.ToList();
|
||||||
foreach (var idx in keyVaributes) {
|
foreach (var idx in keyVaributes) {
|
||||||
@@ -419,8 +446,8 @@ namespace Elwig.Helpers.Billing {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CollapsePaymentData(payment, vaributes ?? payment.Select(e => new RawVaribute(e.Key)).ToList(), useDefaultPayment);
|
CollapsePaymentData(payment, payment.DeepClone().AsObject(), vaributes ?? payment.Select(e => new RawVaribute(e.Key)).ToList(), useDefaultPayment);
|
||||||
CollapsePaymentData(qualityWei, vaributes ?? qualityWei.Select(e => new RawVaribute(e.Key)).ToList(), useDefaultQuality);
|
CollapsePaymentData(qualityWei, qualityWei.DeepClone().AsObject(), vaributes ?? qualityWei.Select(e => new RawVaribute(e.Key)).ToList(), useDefaultQuality);
|
||||||
|
|
||||||
var data = new JsonObject {
|
var data = new JsonObject {
|
||||||
["mode"] = "elwig",
|
["mode"] = "elwig",
|
||||||
|
|||||||
@@ -20,13 +20,19 @@ namespace Elwig.Helpers.Billing {
|
|||||||
Data = PaymentBillingData.FromJson(PaymentVariant.Data, Utils.GetVaributes(ctx, Year, onlyDelivered: false));
|
Data = PaymentBillingData.FromJson(PaymentVariant.Data, Utils.GetVaributes(ctx, Year, onlyDelivered: false));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Calculate(bool? honorGebunden = null, bool? allowAttrsIntoLower = null, bool? avoidUnderDeliveries = null) {
|
public async Task Calculate(bool strictPrices = true, bool? honorGebunden = null, bool? allowAttrsIntoLower = null, bool? avoidUnderDeliveries = null) {
|
||||||
using var cnx = await AppDbContext.ConnectAsync();
|
using var cnx = await AppDbContext.ConnectAsync();
|
||||||
using var tx = await cnx.BeginTransactionAsync();
|
using var tx = await cnx.BeginTransactionAsync();
|
||||||
await CalculateBuckets(honorGebunden, allowAttrsIntoLower, avoidUnderDeliveries, cnx);
|
await CalculateBuckets(honorGebunden, allowAttrsIntoLower, avoidUnderDeliveries, cnx);
|
||||||
await DeleteInDb(cnx);
|
await DeleteInDb(cnx);
|
||||||
await SetCalcTime(cnx);
|
await SetCalcTime(cnx);
|
||||||
await CalculatePrices(cnx);
|
KeyNotFoundException? exception = null;
|
||||||
|
try {
|
||||||
|
await CalculatePrices(cnx, strictPrices);
|
||||||
|
} catch (KeyNotFoundException e) {
|
||||||
|
if (strictPrices) throw;
|
||||||
|
exception = e;
|
||||||
|
}
|
||||||
if (Data.ConsiderDelieryModifiers) {
|
if (Data.ConsiderDelieryModifiers) {
|
||||||
await CalculateDeliveryModifiers(cnx);
|
await CalculateDeliveryModifiers(cnx);
|
||||||
}
|
}
|
||||||
@@ -34,26 +40,28 @@ namespace Elwig.Helpers.Billing {
|
|||||||
await CalculateMemberModifiers(cnx);
|
await CalculateMemberModifiers(cnx);
|
||||||
}
|
}
|
||||||
await tx.CommitAsync();
|
await tx.CommitAsync();
|
||||||
|
if (exception != null)
|
||||||
|
throw exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Commit() {
|
public async Task Commit() {
|
||||||
await Revert();
|
await Revert();
|
||||||
using var cnx = await AppDbContext.ConnectAsync();
|
using var cnx = await AppDbContext.ConnectAsync();
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"""
|
await cnx.ExecuteBatch($"""
|
||||||
INSERT INTO credit (year, tgnr, mgnr, avnr, net_amount, prev_net_amount, vat, modifiers, prev_modifiers)
|
INSERT INTO credit (year, tgnr, mgnr, avnr, net_amount, prev_net_amount, vat, modifiers, prev_modifiers)
|
||||||
SELECT s.year,
|
SELECT s.year,
|
||||||
COALESCE(t.tgnr, 0) + ROW_NUMBER() OVER(ORDER BY m.mgnr) AS tgnr,
|
COALESCE(t.tgnr, 0) + ROW_NUMBER() OVER(ORDER BY m.mgnr) AS tgnr,
|
||||||
m.mgnr,
|
m.mgnr,
|
||||||
v.avnr,
|
v.avnr,
|
||||||
ROUND(p.amount / POW(10, s.precision - 2)) AS net_amount,
|
ROUND(p.amount / POW(10, s.precision - 2)) AS net_amount,
|
||||||
ROUND(lp.amount / POW(10, s.precision - 2)) AS prev_amount,
|
IIF(lc.amount < 0, 0, ROUND(lp.amount / POW(10, s.precision - 2))) AS prev_net_amount,
|
||||||
IIF(m.buchführend, s.vat_normal, s.vat_flatrate) AS vat,
|
IIF(m.buchführend, s.vat_normal, s.vat_flatrate) AS vat,
|
||||||
ROUND(IIF({Data.ConsiderTotalPenalty}, COALESCE(b.total_penalty, 0), 0) / POW(10, s.precision - 2)) +
|
ROUND(IIF({Data.ConsiderTotalPenalty}, COALESCE(b.total_penalty, 0), 0) / POW(10, s.precision - 2)) +
|
||||||
ROUND(IIF({Data.ConsiderContractPenalties}, COALESCE(u.total_penalty, 0), 0) / POW(10, 4 - 2)) +
|
ROUND(IIF({Data.ConsiderContractPenalties}, COALESCE(u.total_penalty, 0), 0) / POW(10, 4 - 2)) +
|
||||||
ROUND(IIF({Data.ConsiderAutoBusinessShares}, -COALESCE(a.total_amount, 0), 0) / POW(10, s.precision - 2)) +
|
ROUND(IIF({Data.ConsiderAutoBusinessShares}, -COALESCE(a.total_amount, 0), 0) / POW(10, s.precision - 2)) +
|
||||||
IIF({Data.ConsiderCustomModifiers}, COALESCE(x.amount, 0), 0)
|
IIF({Data.ConsiderCustomModifiers}, COALESCE(x.amount, 0), 0)
|
||||||
AS modifiers,
|
AS modifiers,
|
||||||
lc.modifiers AS prev_modifiers
|
IIF(lc.amount < 0, 0, lc.modifiers) AS prev_modifiers
|
||||||
FROM season s
|
FROM season s
|
||||||
JOIN payment_variant v ON v.year = s.year
|
JOIN payment_variant v ON v.year = s.year
|
||||||
LEFT JOIN payment_variant l ON l.year = s.year
|
LEFT JOIN payment_variant l ON l.year = s.year
|
||||||
@@ -74,27 +82,27 @@ namespace Elwig.Helpers.Billing {
|
|||||||
LEFT JOIN payment_custom x ON (x.year, x.mgnr) = (s.year, m.mgnr)
|
LEFT JOIN payment_custom x ON (x.year, x.mgnr) = (s.year, m.mgnr)
|
||||||
WHERE s.year = {Year} AND v.avnr = {AvNr};
|
WHERE s.year = {Year} AND v.avnr = {AvNr};
|
||||||
""");
|
""");
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"""
|
await cnx.ExecuteBatch($"""
|
||||||
UPDATE payment_variant SET test_variant = FALSE WHERE (year, avnr) = ({Year}, {AvNr});
|
UPDATE payment_variant SET test_variant = FALSE WHERE (year, avnr) = ({Year}, {AvNr});
|
||||||
""");
|
""");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Revert() {
|
public async Task Revert() {
|
||||||
using var cnx = await AppDbContext.ConnectAsync();
|
using var cnx = await AppDbContext.ConnectAsync();
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"""
|
await cnx.ExecuteBatch($"""
|
||||||
DELETE FROM credit WHERE (year, avnr) = ({Year}, {AvNr});
|
DELETE FROM credit WHERE (year, avnr) = ({Year}, {AvNr});
|
||||||
UPDATE payment_variant SET test_variant = TRUE WHERE (year, avnr) = ({Year}, {AvNr});
|
UPDATE payment_variant SET test_variant = TRUE WHERE (year, avnr) = ({Year}, {AvNr});
|
||||||
""");
|
""");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task SetCalcTime(SqliteConnection cnx) {
|
protected async Task SetCalcTime(SqliteConnection cnx) {
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"""
|
await cnx.ExecuteBatch($"""
|
||||||
UPDATE payment_variant SET calc_time = UNIXEPOCH() WHERE (year, avnr) = ({Year}, {AvNr})
|
UPDATE payment_variant SET calc_time = UNIXEPOCH() WHERE (year, avnr) = ({Year}, {AvNr})
|
||||||
""");
|
""");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task DeleteInDb(SqliteConnection cnx) {
|
protected async Task DeleteInDb(SqliteConnection cnx) {
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"""
|
await cnx.ExecuteBatch($"""
|
||||||
DELETE FROM payment_delivery_part_bucket WHERE (year, avnr) = ({Year}, {AvNr});
|
DELETE FROM payment_delivery_part_bucket WHERE (year, avnr) = ({Year}, {AvNr});
|
||||||
DELETE FROM payment_delivery_part WHERE (year, avnr) = ({Year}, {AvNr});
|
DELETE FROM payment_delivery_part WHERE (year, avnr) = ({Year}, {AvNr});
|
||||||
DELETE FROM payment_member WHERE (year, avnr) = ({Year}, {AvNr});
|
DELETE FROM payment_member WHERE (year, avnr) = ({Year}, {AvNr});
|
||||||
@@ -108,7 +116,7 @@ namespace Elwig.Helpers.Billing {
|
|||||||
var multiplier = 0.50;
|
var multiplier = 0.50;
|
||||||
var includePredecessor = true;
|
var includePredecessor = true;
|
||||||
var modName = "Treue%";
|
var modName = "Treue%";
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"""
|
await cnx.ExecuteBatch($"""
|
||||||
INSERT INTO payment_member (year, avnr, mgnr, net_amount, mod_abs, mod_rel)
|
INSERT INTO payment_member (year, avnr, mgnr, net_amount, mod_abs, mod_rel)
|
||||||
SELECT c.year, {AvNr}, s.mgnr, 0,
|
SELECT c.year, {AvNr}, s.mgnr, 0,
|
||||||
ROUND(s.sum * COALESCE(m.abs, 0)),
|
ROUND(s.sum * COALESCE(m.abs, 0)),
|
||||||
@@ -130,7 +138,7 @@ namespace Elwig.Helpers.Billing {
|
|||||||
mod_rel = mod_rel + excluded.mod_rel
|
mod_rel = mod_rel + excluded.mod_rel
|
||||||
""");
|
""");
|
||||||
}
|
}
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"""
|
await cnx.ExecuteBatch($"""
|
||||||
INSERT INTO payment_member (year, avnr, mgnr, net_amount, mod_abs, mod_rel)
|
INSERT INTO payment_member (year, avnr, mgnr, net_amount, mod_abs, mod_rel)
|
||||||
SELECT x.year, {AvNr}, x.mgnr, 0, COALESCE(x.mod_abs * POW(10, s.precision - 2), 0), COALESCE(x.mod_rel, 0)
|
SELECT x.year, {AvNr}, x.mgnr, 0, COALESCE(x.mod_abs * POW(10, s.precision - 2), 0), COALESCE(x.mod_rel, 0)
|
||||||
FROM payment_custom x
|
FROM payment_custom x
|
||||||
@@ -142,7 +150,8 @@ namespace Elwig.Helpers.Billing {
|
|||||||
""");
|
""");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task CalculatePrices(SqliteConnection cnx) {
|
protected async Task CalculatePrices(SqliteConnection cnx, bool strict = true) {
|
||||||
|
var invalid = new HashSet<string>();
|
||||||
var parts = new List<(int Year, int DId, int DPNr, int BktNr, string SortId, string? AttrId, string? CultId, string Discr, int Value, double Oe, double Kmw, string QualId, bool AttrAreaCom)>();
|
var parts = new List<(int Year, int DId, int DPNr, int BktNr, string SortId, string? AttrId, string? CultId, string Discr, int Value, double Oe, double Kmw, string QualId, bool AttrAreaCom)>();
|
||||||
using (var cmd = cnx.CreateCommand()) {
|
using (var cmd = cnx.CreateCommand()) {
|
||||||
cmd.CommandText = $"""
|
cmd.CommandText = $"""
|
||||||
@@ -172,21 +181,31 @@ namespace Elwig.Helpers.Billing {
|
|||||||
var payAttrId = (part.Discr is "" or "_") ? null : part.Discr;
|
var payAttrId = (part.Discr is "" or "_") ? null : part.Discr;
|
||||||
var attrId = part.AttrAreaCom ? payAttrId : part.AttrId;
|
var attrId = part.AttrAreaCom ? payAttrId : part.AttrId;
|
||||||
var geb = !ungeb && (payAttrId == attrId || !part.AttrAreaCom);
|
var geb = !ungeb && (payAttrId == attrId || !part.AttrAreaCom);
|
||||||
var price = Data.CalculatePrice(part.SortId, attrId, part.CultId, part.QualId, geb, part.Oe, part.Kmw);
|
decimal price = 0;
|
||||||
|
try {
|
||||||
|
price = Data.CalculatePrice(part.SortId, attrId, part.CultId, part.QualId, geb, part.Oe, part.Kmw);
|
||||||
|
} catch (KeyNotFoundException e) {
|
||||||
|
invalid.Add(e.Message.Split('\'')[1]);
|
||||||
|
}
|
||||||
var priceL = PaymentVariant.Season.DecToDb(price);
|
var priceL = PaymentVariant.Season.DecToDb(price);
|
||||||
inserts.Add((part.Year, part.DId, part.DPNr, part.BktNr, priceL, priceL * part.Value));
|
inserts.Add((part.Year, part.DId, part.DPNr, part.BktNr, priceL, priceL * part.Value));
|
||||||
}
|
}
|
||||||
|
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"""
|
var msg = invalid.Count == 0 ? null : "Für folgende Sorten wurde noch keine Preiskurve festgelegt: " + string.Join(", ", invalid);
|
||||||
|
if (msg != null && strict)
|
||||||
|
throw new KeyNotFoundException(msg);
|
||||||
|
await cnx.ExecuteBatch($"""
|
||||||
INSERT INTO payment_delivery_part_bucket (year, did, dpnr, bktnr, avnr, price, amount)
|
INSERT INTO payment_delivery_part_bucket (year, did, dpnr, bktnr, avnr, price, amount)
|
||||||
VALUES {string.Join(",\n ", inserts.Select(i => $"({i.Year}, {i.DId}, {i.DPNr}, {i.BktNr}, {AvNr}, {i.Price}, {i.Amount})"))};
|
VALUES {string.Join(",\n ", inserts.Select(i => $"({i.Year}, {i.DId}, {i.DPNr}, {i.BktNr}, {AvNr}, {i.Price}, {i.Amount})"))};
|
||||||
""");
|
""");
|
||||||
|
if (msg != null)
|
||||||
|
throw new KeyNotFoundException(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task CalculateDeliveryModifiers(SqliteConnection cnx) {
|
protected async Task CalculateDeliveryModifiers(SqliteConnection cnx) {
|
||||||
var netMod = Data.NetWeightModifier.ToString().Replace(',', '.');
|
var netMod = Data.NetWeightModifier.ToString().Replace(',', '.');
|
||||||
var grossMod = Data.GrossWeightModifier.ToString().Replace(',', '.');
|
var grossMod = Data.GrossWeightModifier.ToString().Replace(',', '.');
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"""
|
await cnx.ExecuteBatch($"""
|
||||||
INSERT INTO payment_delivery_part (year, did, dpnr, avnr, net_amount, mod_abs, mod_rel)
|
INSERT INTO payment_delivery_part (year, did, dpnr, avnr, net_amount, mod_abs, mod_rel)
|
||||||
SELECT d.year, d.did, d.dpnr, {AvNr}, 0, 0, IIF(d.net_weight, {netMod}, {grossMod})
|
SELECT d.year, d.did, d.dpnr, {AvNr}, 0, 0, IIF(d.net_weight, {netMod}, {grossMod})
|
||||||
FROM delivery_part d
|
FROM delivery_part d
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Windows.Media;
|
||||||
|
|
||||||
namespace Elwig.Helpers.Billing {
|
namespace Elwig.Helpers.Billing {
|
||||||
public class GraphEntry {
|
public class GraphEntry {
|
||||||
@@ -40,6 +41,7 @@ namespace Elwig.Helpers.Billing {
|
|||||||
public string VaributeStringSimple => (Abgewertet ? "Abgew.: " : "") + (Vaributes.Count != 0 ? (Vaributes.Count >= 25 ? "Restliche Sorten" : string.Join(", ", Vaributes.Select(c => c.Listing))) : "-");
|
public string VaributeStringSimple => (Abgewertet ? "Abgew.: " : "") + (Vaributes.Count != 0 ? (Vaributes.Count >= 25 ? "Restliche Sorten" : string.Join(", ", Vaributes.Select(c => c.Listing))) : "-");
|
||||||
public string VaributeString => Vaributes.Count != 0 ? string.Join("\n", Vaributes.Select(c => c.FullName)) : "-";
|
public string VaributeString => Vaributes.Count != 0 ? string.Join("\n", Vaributes.Select(c => c.FullName)) : "-";
|
||||||
public string VaributeStringChange => (Abgewertet ? "A." : "") + string.Join(",", Vaributes.Select(c => c.Listing));
|
public string VaributeStringChange => (Abgewertet ? "A." : "") + string.Join(",", Vaributes.Select(c => c.Listing));
|
||||||
|
public Brush? Color => Vaributes.Select(v => v.Variety?.Color).Distinct().SingleOrDefault();
|
||||||
private readonly int Precision;
|
private readonly int Precision;
|
||||||
|
|
||||||
public GraphEntry(int id, int precision, BillingData.CurveMode mode) {
|
public GraphEntry(int id, int precision, BillingData.CurveMode mode) {
|
||||||
|
|||||||
@@ -19,10 +19,6 @@ namespace Elwig.Helpers {
|
|||||||
public bool IsSitzendorf => IsWinzerkeller && App.ZwstId == "S";
|
public bool IsSitzendorf => IsWinzerkeller && App.ZwstId == "S";
|
||||||
public bool IsGrInzersdorf => IsWeinland;
|
public bool IsGrInzersdorf => IsWeinland;
|
||||||
|
|
||||||
public bool HasNetWeighing(string? zwstId) => IsMatzen || (IsWinzerkeller && zwstId == "W");
|
|
||||||
public bool HasNetWeighing(Branch? b) => HasNetWeighing(b?.ZwstId);
|
|
||||||
public bool HasNetWeighing() => HasNetWeighing(App.ZwstId);
|
|
||||||
|
|
||||||
public string NameToken;
|
public string NameToken;
|
||||||
public string NameShort;
|
public string NameShort;
|
||||||
public string Name;
|
public string Name;
|
||||||
@@ -51,6 +47,7 @@ namespace Elwig.Helpers {
|
|||||||
public string? Bic;
|
public string? Bic;
|
||||||
public string? UstIdNr;
|
public string? UstIdNr;
|
||||||
public string? LfbisNr;
|
public string? LfbisNr;
|
||||||
|
public string? OrganicAuthority;
|
||||||
|
|
||||||
public string? PhoneNr;
|
public string? PhoneNr;
|
||||||
public string? FaxNr;
|
public string? FaxNr;
|
||||||
@@ -109,6 +106,7 @@ namespace Elwig.Helpers {
|
|||||||
UstIdNr = parameters.GetValueOrDefault("CLIENT_USTIDNR");
|
UstIdNr = parameters.GetValueOrDefault("CLIENT_USTIDNR");
|
||||||
Bic = parameters.GetValueOrDefault("CLIENT_BIC");
|
Bic = parameters.GetValueOrDefault("CLIENT_BIC");
|
||||||
Iban = parameters.GetValueOrDefault("CLIENT_IBAN");
|
Iban = parameters.GetValueOrDefault("CLIENT_IBAN");
|
||||||
|
OrganicAuthority = parameters.GetValueOrDefault("CLIENT_ORGANIC_AUTHORITY");
|
||||||
|
|
||||||
switch (parameters.GetValueOrDefault("MODE_DELIVERYNOTE_STATS", "SHORT")?.ToUpper()) {
|
switch (parameters.GetValueOrDefault("MODE_DELIVERYNOTE_STATS", "SHORT")?.ToUpper()) {
|
||||||
case "NONE": ModeDeliveryNoteStats = 0; break;
|
case "NONE": ModeDeliveryNoteStats = 0; break;
|
||||||
@@ -208,15 +206,15 @@ namespace Elwig.Helpers {
|
|||||||
case 1: orderingMemberList = "NAME"; break;
|
case 1: orderingMemberList = "NAME"; break;
|
||||||
case 2: orderingMemberList = "KG"; break;
|
case 2: orderingMemberList = "KG"; break;
|
||||||
}
|
}
|
||||||
string mailSendPostal = "MGNR";
|
string mailSendPostal = "WISH";
|
||||||
switch (MailOrdering) {
|
switch (MailSendPostal) {
|
||||||
case 0: mailSendPostal = "NONE"; break;
|
case 0: mailSendPostal = "NONE"; break;
|
||||||
case 1: mailSendPostal = "NO_EMAIL"; break;
|
case 1: mailSendPostal = "NO_EMAIL"; break;
|
||||||
case 2: mailSendPostal = "WISH"; break;
|
case 2: mailSendPostal = "WISH"; break;
|
||||||
case 3: mailSendPostal = "ALL"; break;
|
case 3: mailSendPostal = "ALL"; break;
|
||||||
}
|
}
|
||||||
string mailSendEmail = "MGNR";
|
string mailSendEmail = "WISH";
|
||||||
switch (MailOrdering) {
|
switch (MailSendEmail) {
|
||||||
case 0: mailSendEmail = "NONE"; break;
|
case 0: mailSendEmail = "NONE"; break;
|
||||||
case 1: mailSendEmail = "WISH"; break;
|
case 1: mailSendEmail = "WISH"; break;
|
||||||
case 2: mailSendEmail = "ALL"; break;
|
case 2: mailSendEmail = "ALL"; break;
|
||||||
@@ -253,6 +251,7 @@ namespace Elwig.Helpers {
|
|||||||
("CLIENT_USTIDNR", UstIdNr),
|
("CLIENT_USTIDNR", UstIdNr),
|
||||||
("CLIENT_BIC", Bic),
|
("CLIENT_BIC", Bic),
|
||||||
("CLIENT_IBAN", Iban),
|
("CLIENT_IBAN", Iban),
|
||||||
|
("CLIENT_ORGANIC_AUTHORITY", OrganicAuthority),
|
||||||
("MODE_DELIVERYNOTE_STATS", deliveryNoteStats),
|
("MODE_DELIVERYNOTE_STATS", deliveryNoteStats),
|
||||||
("MODE_WINEQUALITYSTATISTICS", modeWineQualityStatistics),
|
("MODE_WINEQUALITYSTATISTICS", modeWineQualityStatistics),
|
||||||
("ORDERING_MEMBERLIST", orderingMemberList),
|
("ORDERING_MEMBERLIST", orderingMemberList),
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -5,6 +6,8 @@ using Microsoft.Extensions.Configuration;
|
|||||||
|
|
||||||
namespace Elwig.Helpers {
|
namespace Elwig.Helpers {
|
||||||
|
|
||||||
|
public enum WeighingMode { Gross, Net, Box }
|
||||||
|
|
||||||
public record struct ScaleConfig {
|
public record struct ScaleConfig {
|
||||||
public string Id;
|
public string Id;
|
||||||
public string? Type;
|
public string? Type;
|
||||||
@@ -41,6 +44,7 @@ namespace Elwig.Helpers {
|
|||||||
public string DatabaseFile = App.DataPath + "database.sqlite3";
|
public string DatabaseFile = App.DataPath + "database.sqlite3";
|
||||||
public string? DatabaseLog = null;
|
public string? DatabaseLog = null;
|
||||||
public string? Branch = null;
|
public string? Branch = null;
|
||||||
|
public WeighingMode? WeighingMode;
|
||||||
public string? UpdateUrl = null;
|
public string? UpdateUrl = null;
|
||||||
public bool UpdateAuto = false;
|
public bool UpdateAuto = false;
|
||||||
public string? SyncUrl = null;
|
public string? SyncUrl = null;
|
||||||
@@ -74,6 +78,8 @@ namespace Elwig.Helpers {
|
|||||||
DatabaseLog = log != null ? Path.Combine(Path.GetDirectoryName(FileName) ?? App.DataPath, log) : null;
|
DatabaseLog = log != null ? Path.Combine(Path.GetDirectoryName(FileName) ?? App.DataPath, log) : null;
|
||||||
Branch = config["general:branch"];
|
Branch = config["general:branch"];
|
||||||
Debug = TrueValues.Contains(config["general:debug"]?.ToLower());
|
Debug = TrueValues.Contains(config["general:debug"]?.ToLower());
|
||||||
|
var weighing = config["general:weighing"];
|
||||||
|
WeighingMode = weighing != null && Enum.TryParse<WeighingMode>(weighing, true, out var w) ? w : null;
|
||||||
UpdateUrl = config["update:url"];
|
UpdateUrl = config["update:url"];
|
||||||
UpdateAuto = TrueValues.Contains(config["update:auto"]?.ToLower());
|
UpdateAuto = TrueValues.Contains(config["update:auto"]?.ToLower());
|
||||||
SyncUrl = config["sync:url"];
|
SyncUrl = config["sync:url"];
|
||||||
|
|||||||
@@ -15,7 +15,9 @@ namespace Elwig.Helpers.Export {
|
|||||||
protected readonly char Separator;
|
protected readonly char Separator;
|
||||||
protected string? Header;
|
protected string? Header;
|
||||||
|
|
||||||
public Csv(string filename, char separator = ';') : this(filename, separator, Utils.UTF8) { }
|
public Csv(string filename, char separator = ';') :
|
||||||
|
this(filename, separator, Utils.UTF8) {
|
||||||
|
}
|
||||||
|
|
||||||
public Csv(string filename, char separator, Encoding encoding) {
|
public Csv(string filename, char separator, Encoding encoding) {
|
||||||
_writer = new StreamWriter(filename, false, encoding);
|
_writer = new StreamWriter(filename, false, encoding);
|
||||||
@@ -58,4 +60,22 @@ namespace Elwig.Helpers.Export {
|
|||||||
|
|
||||||
public abstract string FormatRow(T row);
|
public abstract string FormatRow(T row);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class CsvSimple : Csv<IEnumerable<object?>> {
|
||||||
|
|
||||||
|
public CsvSimple(string filename, char separator, Encoding encoding) :
|
||||||
|
base(filename, separator, encoding) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public CsvSimple(string filename, char separator = ';') :
|
||||||
|
base(filename, separator) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string FormatRow(IEnumerable<object?> row) {
|
||||||
|
return string.Join(Separator, row.Select(i => {
|
||||||
|
var str = $"{i}";
|
||||||
|
return str.Contains(Separator) || str.Contains('\n') ? $"\"{str.Replace("\"", "\"\"")}\"" : str;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
243
Elwig/Helpers/Export/Database.cs
Normal file
243
Elwig/Helpers/Export/Database.cs
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Text.Json.Nodes;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Elwig.Helpers.Export {
|
||||||
|
public static class Database {
|
||||||
|
|
||||||
|
private static async Task<(long? ApplicationId, string? UserVersion, long? SchemaVersion, long FileSize)> GetMeta() {
|
||||||
|
long size = new FileInfo(App.Config.DatabaseFile).Length;
|
||||||
|
using var cnx = await AppDbContext.ConnectAsync();
|
||||||
|
var applId = (long?)await cnx.ExecuteScalar("PRAGMA application_id");
|
||||||
|
var userVers = (long?)await cnx.ExecuteScalar("PRAGMA user_version");
|
||||||
|
var schemaVers = (long?)await cnx.ExecuteScalar("PRAGMA schema_version");
|
||||||
|
return (applId, userVers != null ? $"{userVers >> 24}.{(userVers >> 16) & 0xFF}.{(userVers >> 8) & 0xFF}.{userVers & 0xFF}" : null, schemaVers, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task ExportSqlite(string filename, bool zipFile) {
|
||||||
|
if (zipFile) {
|
||||||
|
File.Delete(filename);
|
||||||
|
using var zip = ZipFile.Open(filename, ZipArchiveMode.Create);
|
||||||
|
|
||||||
|
var version = zip.CreateEntry("version", CompressionLevel.NoCompression);
|
||||||
|
using (var writer = new StreamWriter(version.Open(), Utils.UTF8)) {
|
||||||
|
await writer.WriteAsync("elwig:1");
|
||||||
|
}
|
||||||
|
|
||||||
|
var (applId, userVers, schemaVers, size) = await GetMeta();
|
||||||
|
var meta = zip.CreateEntry("meta.json", CompressionLevel.NoCompression);
|
||||||
|
using (var writer = new StreamWriter(meta.Open(), Utils.UTF8)) {
|
||||||
|
var obj = new JsonObject {
|
||||||
|
["timestamp"] = $"{DateTime.UtcNow:yyyy-MM-ddTHH:mm:ssZ}",
|
||||||
|
["zwstid"] = App.ZwstId,
|
||||||
|
["device"] = Environment.MachineName,
|
||||||
|
["database"] = new JsonObject {
|
||||||
|
["application_id"] = applId,
|
||||||
|
["user_version"] = userVers,
|
||||||
|
["schema_version"] = schemaVers,
|
||||||
|
["file_size"] = size,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
await writer.WriteAsync(obj.ToJsonString(Utils.JsonOpts));
|
||||||
|
}
|
||||||
|
|
||||||
|
var db = zip.CreateEntryFromFile(App.Config.DatabaseFile, "database.sqlite3", CompressionLevel.SmallestSize);
|
||||||
|
} else {
|
||||||
|
File.Copy(App.Config.DatabaseFile, filename, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task ExportSql(string filename, bool zipFile) {
|
||||||
|
if (zipFile) {
|
||||||
|
File.Delete(filename);
|
||||||
|
using var zip = ZipFile.Open(filename, ZipArchiveMode.Create);
|
||||||
|
|
||||||
|
var version = zip.CreateEntry("version", CompressionLevel.NoCompression);
|
||||||
|
using (var writer = new StreamWriter(version.Open(), Utils.UTF8)) {
|
||||||
|
await writer.WriteAsync("elwig:1");
|
||||||
|
}
|
||||||
|
|
||||||
|
var (applId, userVers, schemaVers, size) = await GetMeta();
|
||||||
|
var meta = zip.CreateEntry("meta.json", CompressionLevel.NoCompression);
|
||||||
|
using (var writer = new StreamWriter(meta.Open(), Utils.UTF8)) {
|
||||||
|
var obj = new JsonObject {
|
||||||
|
["timestamp"] = $"{DateTime.UtcNow:yyyy-MM-ddTHH:mm:ssZ}",
|
||||||
|
["zwstid"] = App.ZwstId,
|
||||||
|
["device"] = Environment.MachineName,
|
||||||
|
["database"] = new JsonObject {
|
||||||
|
["application_id"] = applId,
|
||||||
|
["user_version"] = userVers,
|
||||||
|
["schema_version"] = schemaVers,
|
||||||
|
["file_size"] = size,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
await writer.WriteAsync(obj.ToJsonString(Utils.JsonOpts));
|
||||||
|
}
|
||||||
|
|
||||||
|
var sql = zip.CreateEntry("database.sql", CompressionLevel.SmallestSize);
|
||||||
|
using (var writer = new StreamWriter(sql.Open(), Utils.UTF8)) {
|
||||||
|
await ExportSql(writer);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
using var stream = File.OpenWrite(filename);
|
||||||
|
using var writer = new StreamWriter(stream, Utils.UTF8);
|
||||||
|
await ExportSql(writer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task ExportSql(StreamWriter writer) {
|
||||||
|
using var cnx = await AppDbContext.ConnectAsync();
|
||||||
|
|
||||||
|
var tables = new List<(string Name, string Sql)>();
|
||||||
|
using (var cmd = cnx.CreateCommand()) {
|
||||||
|
cmd.CommandText = "SELECT name, sql FROM sqlite_schema WHERE type = 'table'";
|
||||||
|
using var reader = await cmd.ExecuteReaderAsync();
|
||||||
|
while (await reader.ReadAsync()) {
|
||||||
|
tables.Add((reader.GetString(0), reader.GetString(1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var applId = (long?)await cnx.ExecuteScalar("PRAGMA application_id") ?? 0;
|
||||||
|
var userVers = (long?)await cnx.ExecuteScalar("PRAGMA user_version") ?? 0;
|
||||||
|
var schemaVers = (long?)await cnx.ExecuteScalar("PRAGMA schema_version") ?? 0;
|
||||||
|
|
||||||
|
await writer.WriteLineAsync($"-- Elwig database dump, {DateTime.Now:yyyy-MM-dd, HH:mm:ss}");
|
||||||
|
await writer.WriteLineAsync($"-- {Environment.MachineName}, Zwst. {App.BranchName}, {App.Client.Name}");
|
||||||
|
await writer.WriteLineAsync("BEGIN TRANSACTION;");
|
||||||
|
await writer.WriteLineAsync("PRAGMA foreign_keys=OFF;");
|
||||||
|
await writer.WriteLineAsync($"PRAGMA application_id=0x{applId:X8};");
|
||||||
|
await writer.WriteLineAsync($"PRAGMA user_version=0x{userVers:X8};");
|
||||||
|
|
||||||
|
foreach (var t in tables) {
|
||||||
|
await writer.WriteAsync(t.Sql);
|
||||||
|
await writer.WriteLineAsync(";");
|
||||||
|
|
||||||
|
var columnNames = new List<string>();
|
||||||
|
using (var cmd = cnx.CreateCommand()) {
|
||||||
|
cmd.CommandText = $"PRAGMA table_info({t.Name})";
|
||||||
|
using var reader = await cmd.ExecuteReaderAsync();
|
||||||
|
while (await reader.ReadAsync()) {
|
||||||
|
columnNames.Add(reader.GetString(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var cmd = cnx.CreateCommand()) {
|
||||||
|
cmd.CommandText = $"SELECT {string.Join(',', columnNames)} FROM {t.Name}";
|
||||||
|
using var reader = await cmd.ExecuteReaderAsync();
|
||||||
|
var columns = await reader.GetColumnSchemaAsync();
|
||||||
|
var values = new object[reader.FieldCount];
|
||||||
|
while (await reader.ReadAsync()) {
|
||||||
|
await writer.WriteAsync($"INSERT INTO {t.Name} VALUES (");
|
||||||
|
|
||||||
|
reader.GetValues(values);
|
||||||
|
for (int i = 0; i < columns.Count; i++) {
|
||||||
|
var c = columns[i];
|
||||||
|
var v = values[i];
|
||||||
|
if (i > 0) await writer.WriteAsync(",");
|
||||||
|
if (v == null || v is DBNull) {
|
||||||
|
await writer.WriteAsync("NULL");
|
||||||
|
} else if (c.DataTypeName == "TEXT") {
|
||||||
|
await writer.WriteAsync($"'{v.ToString()?.Replace("'", "''")}'");
|
||||||
|
} else {
|
||||||
|
await writer.WriteAsync(v.ToString()?.Replace(',', '.'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await writer.WriteLineAsync(");");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var cmd = cnx.CreateCommand()) {
|
||||||
|
cmd.CommandText = "SELECT sql FROM sqlite_schema WHERE type != 'table' AND sql IS NOT NULL";
|
||||||
|
using var reader = await cmd.ExecuteReaderAsync();
|
||||||
|
while (await reader.ReadAsync()) {
|
||||||
|
await writer.WriteAsync(reader.GetString(0));
|
||||||
|
await writer.WriteLineAsync(";");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await writer.WriteLineAsync($"PRAGMA schema_version={schemaVers};");
|
||||||
|
await writer.WriteLineAsync("PRAGMA foreign_keys=ON;");
|
||||||
|
await writer.WriteLineAsync("COMMIT;");
|
||||||
|
await writer.WriteLineAsync("VACUUM;");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task Import(string filename) {
|
||||||
|
if (filename.EndsWith(".sql")) {
|
||||||
|
await ImportSql(filename, false);
|
||||||
|
} else if (filename.EndsWith(".sql.zip")) {
|
||||||
|
await ImportSql(filename, true);
|
||||||
|
} else if (filename.EndsWith(".sqlite3")) {
|
||||||
|
await ImportSqlite(filename, false);
|
||||||
|
} else if (filename.EndsWith(".sqlite3.zip")) {
|
||||||
|
await ImportSqlite(filename, true);
|
||||||
|
} else {
|
||||||
|
throw new ArgumentException($"Unknown file extension for importing: '{filename}'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task ImportSql(string filename, bool zipFile = false) {
|
||||||
|
if (zipFile) {
|
||||||
|
using var zip = ZipFile.Open(filename, ZipArchiveMode.Read);
|
||||||
|
await zip.CheckIntegrity();
|
||||||
|
foreach (var entry in zip.Entries) {
|
||||||
|
if (entry.Name.EndsWith(".sql")) {
|
||||||
|
using var stream = entry.Open();
|
||||||
|
using var reader = new StreamReader(stream, Utils.UTF8);
|
||||||
|
await ImportSql(reader);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new FileFormatException("ZIP archive has to contain at least one .sql file");
|
||||||
|
} else {
|
||||||
|
using var stream = File.Open(filename, FileMode.Open);
|
||||||
|
using var reader = new StreamReader(stream, Utils.UTF8);
|
||||||
|
await ImportSql(reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task ImportSqlite(string filename, bool zipFile = false) {
|
||||||
|
if (zipFile) {
|
||||||
|
var newName = Path.ChangeExtension(App.Config.DatabaseFile, ".new.sqlite3");
|
||||||
|
try {
|
||||||
|
using var zip = ZipFile.Open(filename, ZipArchiveMode.Read);
|
||||||
|
await zip.CheckIntegrity();
|
||||||
|
foreach (var entry in zip.Entries) {
|
||||||
|
if (entry.Name.EndsWith(".sqlite3")) {
|
||||||
|
entry.ExtractToFile(newName);
|
||||||
|
await ImportSqlite(newName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new FileFormatException("ZIP archive has to contain at least one .sqlite3 file");
|
||||||
|
} finally {
|
||||||
|
if (File.Exists(newName)) File.Delete(newName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var oldName = Path.ChangeExtension(App.Config.DatabaseFile, ".old.sqlite3");
|
||||||
|
File.Move(App.Config.DatabaseFile, oldName, true);
|
||||||
|
File.Move(filename, App.Config.DatabaseFile, false);
|
||||||
|
|
||||||
|
using var cnx = await AppDbContext.ConnectAsync();
|
||||||
|
await cnx.ExecuteBatch("VACUUM");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task ImportSql(StreamReader reader) {
|
||||||
|
var newName = Path.ChangeExtension(App.Config.DatabaseFile, ".new.sqlite3");
|
||||||
|
File.Delete(newName);
|
||||||
|
try {
|
||||||
|
using (var cnx = await AppDbContext.ConnectAsync($"Data Source=\"{newName}\"; Mode=ReadWriteCreate; Foreign Keys=False; Cache=Default; Pooling=False")) {
|
||||||
|
await cnx.ExecuteBatch(await reader.ReadToEndAsync());
|
||||||
|
}
|
||||||
|
await ImportSqlite(newName);
|
||||||
|
} finally {
|
||||||
|
if (File.Exists(newName)) File.Delete(newName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
using System.IO.Compression;
|
|
||||||
using System.IO;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Elwig.Models.Entities;
|
using Elwig.Models.Entities;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System;
|
|
||||||
using System.Text.Json.Nodes;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Windows;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System.Text.Json;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json.Nodes;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
namespace Elwig.Helpers.Export {
|
namespace Elwig.Helpers.Export {
|
||||||
public static class ElwigData {
|
public static class ElwigData {
|
||||||
@@ -17,8 +17,6 @@ namespace Elwig.Helpers.Export {
|
|||||||
|
|
||||||
public static readonly string ImportedTxt = Path.Combine(App.DataPath, "imported.txt");
|
public static readonly string ImportedTxt = Path.Combine(App.DataPath, "imported.txt");
|
||||||
|
|
||||||
private static readonly JsonSerializerOptions JsonOpts = new() { Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping };
|
|
||||||
|
|
||||||
public static async Task<string[]> GetImportedFiles() {
|
public static async Task<string[]> GetImportedFiles() {
|
||||||
try {
|
try {
|
||||||
return await File.ReadAllLinesAsync(ImportedTxt, Utils.UTF8);
|
return await File.ReadAllLinesAsync(ImportedTxt, Utils.UTF8);
|
||||||
@@ -40,6 +38,7 @@ namespace Elwig.Helpers.Export {
|
|||||||
Dictionary<string, int> currentLsNrs;
|
Dictionary<string, int> currentLsNrs;
|
||||||
Dictionary<int, List<WbRd>> currentWbRde;
|
Dictionary<int, List<WbRd>> currentWbRde;
|
||||||
Dictionary<int, AT_Kg> kgs;
|
Dictionary<int, AT_Kg> kgs;
|
||||||
|
List<WbGl> currentWbGls;
|
||||||
|
|
||||||
using (var ctx = new AppDbContext()) {
|
using (var ctx = new AppDbContext()) {
|
||||||
branches = await ctx.Branches.ToDictionaryAsync(b => b.ZwstId);
|
branches = await ctx.Branches.ToDictionaryAsync(b => b.ZwstId);
|
||||||
@@ -51,6 +50,7 @@ namespace Elwig.Helpers.Export {
|
|||||||
currentWbRde = await ctx.WbRde
|
currentWbRde = await ctx.WbRde
|
||||||
.GroupBy(r => r.KgNr)
|
.GroupBy(r => r.KgNr)
|
||||||
.ToDictionaryAsync(g => g.Key, g => g.ToList());
|
.ToDictionaryAsync(g => g.Key, g => g.ToList());
|
||||||
|
currentWbGls = await ctx.WbGls.ToListAsync();
|
||||||
kgs = await ctx.Katastralgemeinden.Include(k => k.WbKg).ToDictionaryAsync(k => k.KgNr);
|
kgs = await ctx.Katastralgemeinden.Include(k => k.WbKg).ToDictionaryAsync(k => k.KgNr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,9 +61,12 @@ namespace Elwig.Helpers.Export {
|
|||||||
List<MemberEmailAddr> EmailAddresses,
|
List<MemberEmailAddr> EmailAddresses,
|
||||||
List<AreaCom> AreaCommitments,
|
List<AreaCom> AreaCommitments,
|
||||||
List<WbRd> Riede,
|
List<WbRd> Riede,
|
||||||
|
List<WbKg> WbKgs,
|
||||||
|
List<WbGl> WbGls,
|
||||||
List<Delivery> Deliveries,
|
List<Delivery> Deliveries,
|
||||||
List<DeliveryPart> DeliveryParts,
|
List<DeliveryPart> DeliveryParts,
|
||||||
List<DeliveryPartModifier> Modifiers)>();
|
List<DeliveryPartModifier> Modifiers,
|
||||||
|
Dictionary<string, List<(int Id1, int Id2, DateTime CreatedAt, DateTime ModifiedAt)>> Timestamps)>();
|
||||||
|
|
||||||
var metaData = new List<(string FileName, string ZwstId, string Device,
|
var metaData = new List<(string FileName, string ZwstId, string Device,
|
||||||
int? MemberNum, string? MemberFilters,
|
int? MemberNum, string? MemberFilters,
|
||||||
@@ -71,71 +74,117 @@ namespace Elwig.Helpers.Export {
|
|||||||
int? DeliveryNum, string? DeliveryFilters)>();
|
int? DeliveryNum, string? DeliveryFilters)>();
|
||||||
|
|
||||||
foreach (var filename in filenames) {
|
foreach (var filename in filenames) {
|
||||||
// TODO read encrypted files
|
try {
|
||||||
using var zip = ZipFile.Open(filename, ZipArchiveMode.Read);
|
data.Add(new([], [], [], [], [], [], [], new([], [], [], [], new() {
|
||||||
|
["member"] = [],
|
||||||
|
["area_commitment"] = [],
|
||||||
|
["delivery"] = [],
|
||||||
|
})));
|
||||||
|
var r = data[^1];
|
||||||
|
|
||||||
var version = zip.GetEntry("version");
|
// TODO read encrypted files
|
||||||
using (var reader = new StreamReader(version!.Open(), Utils.UTF8)) {
|
using var zip = ZipFile.Open(filename, ZipArchiveMode.Read);
|
||||||
if (await reader.ReadToEndAsync() != "elwig:1")
|
await zip.CheckIntegrity();
|
||||||
throw new FileFormatException($"Ungültige Export-Datei ({filename})");
|
|
||||||
}
|
|
||||||
|
|
||||||
var metaJson = zip.GetEntry("meta.json");
|
var version = zip.GetEntry("version");
|
||||||
var meta = await JsonNode.ParseAsync(metaJson!.Open());
|
using (var reader = new StreamReader(version!.Open(), Utils.UTF8)) {
|
||||||
var memberCount = meta!["members"]?["count"]?.AsValue().GetValue<int>();
|
if (await reader.ReadToEndAsync() != "elwig:1")
|
||||||
var memberFilters = meta!["members"]?["filters"]?.AsArray().Select(f => f!.AsValue().GetValue<string>()).ToArray();
|
throw new FileFormatException($"Ungültige Elwig-Export-Datei ({filename})");
|
||||||
var areaComCount = meta!["area_commitments"]?["count"]?.AsValue().GetValue<int>();
|
|
||||||
var areaComFilters = meta!["area_commitments"]?["filters"]?.AsArray().Select(f => f!.AsValue().GetValue<string>()).ToArray();
|
|
||||||
var deliveryCount = meta!["deliveries"]?["count"]?.AsValue().GetValue<int>();
|
|
||||||
var deliveryFilters = meta!["deliveries"]?["filters"]?.AsArray().Select(f => f!.AsValue().GetValue<string>()).ToArray();
|
|
||||||
metaData.Add((Path.GetFileName(filename),
|
|
||||||
meta["zwstid"]!.AsValue().GetValue<string>(), meta["device"]!.AsValue().GetValue<string>(),
|
|
||||||
memberCount, memberFilters != null ? string.Join(" / ", memberFilters) : null,
|
|
||||||
areaComCount, areaComFilters != null ? string.Join(" / ", areaComFilters) : null,
|
|
||||||
deliveryCount, deliveryFilters != null ? string.Join(" / ", deliveryFilters) : null));
|
|
||||||
|
|
||||||
data.Add(new([], [], [], [], [], [], [], new([], [])));
|
|
||||||
var r = data[^1];
|
|
||||||
|
|
||||||
var membersJson = zip.GetEntry("members.json");
|
|
||||||
if (membersJson != null) {
|
|
||||||
using var reader = new StreamReader(membersJson.Open(), Utils.UTF8);
|
|
||||||
string? line;
|
|
||||||
while ((line = await reader.ReadLineAsync()) != null) {
|
|
||||||
var obj = JsonNode.Parse(line)!.AsObject();
|
|
||||||
var (m, b, telNrs, emailAddrs) = obj.ToMember(kgs);
|
|
||||||
r.Members.Add(m);
|
|
||||||
if (b != null) r.BillingAddresses.Add(b);
|
|
||||||
r.TelephoneNumbers.AddRange(telNrs);
|
|
||||||
r.EmailAddresses.AddRange(emailAddrs);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
var areaComsJson = zip.GetEntry("area_commitments.json");
|
var metaJson = zip.GetEntry("meta.json");
|
||||||
if (areaComsJson != null) {
|
var meta = await JsonNode.ParseAsync(metaJson!.Open());
|
||||||
using var reader = new StreamReader(areaComsJson.Open(), Utils.UTF8);
|
var memberCount = meta!["members"]?["count"]?.AsValue().GetValue<int>();
|
||||||
string? line;
|
var memberFilters = meta!["members"]?["filters"]?.AsArray().Select(f => f!.AsValue().GetValue<string>()).ToArray();
|
||||||
while ((line = await reader.ReadLineAsync()) != null) {
|
var areaComCount = meta!["area_commitments"]?["count"]?.AsValue().GetValue<int>();
|
||||||
var obj = JsonNode.Parse(line)!.AsObject();
|
var areaComFilters = meta!["area_commitments"]?["filters"]?.AsArray().Select(f => f!.AsValue().GetValue<string>()).ToArray();
|
||||||
var (areaCom, wbrd) = obj.ToAreaCom(kgs, currentWbRde);
|
var deliveryCount = meta!["deliveries"]?["count"]?.AsValue().GetValue<int>();
|
||||||
r.AreaCommitments.Add(areaCom);
|
var deliveryFilters = meta!["deliveries"]?["filters"]?.AsArray().Select(f => f!.AsValue().GetValue<string>()).ToArray();
|
||||||
if (wbrd != null) {
|
metaData.Add((Path.GetFileName(filename),
|
||||||
currentWbRde[wbrd.KgNr].Add(wbrd);
|
meta["zwstid"]!.AsValue().GetValue<string>(), meta["device"]!.AsValue().GetValue<string>(),
|
||||||
r.Riede.Add(wbrd);
|
memberCount, memberFilters != null ? string.Join(" / ", memberFilters) : null,
|
||||||
|
areaComCount, areaComFilters != null ? string.Join(" / ", areaComFilters) : null,
|
||||||
|
deliveryCount, deliveryFilters != null ? string.Join(" / ", deliveryFilters) : null));
|
||||||
|
|
||||||
|
var wbKgsJson = zip.GetEntry("wb_kgs.json");
|
||||||
|
if (wbKgsJson != null) {
|
||||||
|
using var reader = new StreamReader(wbKgsJson.Open(), Utils.UTF8);
|
||||||
|
string? line;
|
||||||
|
while ((line = await reader.ReadLineAsync()) != null) {
|
||||||
|
var obj = JsonNode.Parse(line)!.AsObject();
|
||||||
|
var (k, g) = obj.ToWbKg(currentWbGls);
|
||||||
|
r.WbKgs.Add(k);
|
||||||
|
if (g != null) {
|
||||||
|
currentWbGls[g.GlNr] = g;
|
||||||
|
r.WbGls.Add(g);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
var deliveriesJson = zip.GetEntry("deliveries.json");
|
var membersJson = zip.GetEntry("members.json");
|
||||||
if (deliveriesJson != null) {
|
if (membersJson != null) {
|
||||||
using var reader = new StreamReader(deliveriesJson.Open(), Utils.UTF8);
|
using var reader = new StreamReader(membersJson.Open(), Utils.UTF8);
|
||||||
string? line;
|
string? line;
|
||||||
while ((line = await reader.ReadLineAsync()) != null) {
|
while ((line = await reader.ReadLineAsync()) != null) {
|
||||||
var obj = JsonNode.Parse(line)!.AsObject();
|
var obj = JsonNode.Parse(line)!.AsObject();
|
||||||
var (d, parts, mods) = obj.ToDelivery(currentLsNrs, currentDids);
|
var (m, b, telNrs, emailAddrs, timestamps) = obj.ToMember(kgs);
|
||||||
r.Deliveries.Add(d);
|
r.Members.Add(m);
|
||||||
r.DeliveryParts.AddRange(parts);
|
if (b != null) r.BillingAddresses.Add(b);
|
||||||
r.Modifiers.AddRange(mods);
|
r.TelephoneNumbers.AddRange(telNrs);
|
||||||
|
r.EmailAddresses.AddRange(emailAddrs);
|
||||||
|
if (timestamps.HasValue)
|
||||||
|
r.Timestamps["member"].Add((m.MgNr, 0, timestamps.Value.CreatedAt, timestamps.Value.ModifiedAt));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var areaComsJson = zip.GetEntry("area_commitments.json");
|
||||||
|
if (areaComsJson != null) {
|
||||||
|
using var reader = new StreamReader(areaComsJson.Open(), Utils.UTF8);
|
||||||
|
string? line;
|
||||||
|
while ((line = await reader.ReadLineAsync()) != null) {
|
||||||
|
var obj = JsonNode.Parse(line)!.AsObject();
|
||||||
|
var (areaCom, wbrd, timestamps) = obj.ToAreaCom(currentWbRde);
|
||||||
|
r.AreaCommitments.Add(areaCom);
|
||||||
|
if (wbrd != null) {
|
||||||
|
r.Riede.Add(wbrd);
|
||||||
|
}
|
||||||
|
if (timestamps.HasValue)
|
||||||
|
r.Timestamps["area_commitment"].Add((areaCom.FbNr, 0, timestamps.Value.CreatedAt, timestamps.Value.ModifiedAt));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var deliveriesJson = zip.GetEntry("deliveries.json");
|
||||||
|
if (deliveriesJson != null) {
|
||||||
|
using var reader = new StreamReader(deliveriesJson.Open(), Utils.UTF8);
|
||||||
|
string? line;
|
||||||
|
while ((line = await reader.ReadLineAsync()) != null) {
|
||||||
|
var obj = JsonNode.Parse(line)!.AsObject();
|
||||||
|
var (d, parts, mods, rde, timestamps) = obj.ToDelivery(currentLsNrs, currentDids, kgs, currentWbRde);
|
||||||
|
r.Deliveries.Add(d);
|
||||||
|
r.DeliveryParts.AddRange(parts);
|
||||||
|
r.Modifiers.AddRange(mods);
|
||||||
|
r.Riede.AddRange(rde);
|
||||||
|
if (timestamps.HasValue)
|
||||||
|
r.Timestamps["delivery"].Add((d.Year, d.DId, timestamps.Value.CreatedAt, timestamps.Value.ModifiedAt));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception exc) when (
|
||||||
|
exc is InvalidDataException ||
|
||||||
|
exc is FileFormatException ||
|
||||||
|
exc is FileNotFoundException ||
|
||||||
|
exc is IOException) {
|
||||||
|
data.RemoveAt(data.Count - 1);
|
||||||
|
var str = $"Die Elwig-Export-Datei '{Path.GetFileName(filename)}' konnte nicht verarbeitet werden und wird übersprungen.\n\n" + exc.Message;
|
||||||
|
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
|
||||||
|
MessageBox.Show(str, "Fehler beim Importieren", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
await AddImportedFiles(Path.GetFileName(filename));
|
||||||
|
} catch (Exception exc) {
|
||||||
|
data.RemoveAt(data.Count - 1);
|
||||||
|
var str = $"Die Elwig-Export-Datei '{Path.GetFileName(filename)}' konnte nicht verarbeitet werden. Soll sie in Zukunft übersprungen werden?\n\n" + exc.Message;
|
||||||
|
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
|
||||||
|
var r = MessageBox.Show(str, "Fehler beim Importieren", MessageBoxButton.YesNo, MessageBoxImage.Error, MessageBoxResult.No);
|
||||||
|
if (r == MessageBoxResult.Yes) {
|
||||||
|
await AddImportedFiles(Path.GetFileName(filename));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -144,12 +193,18 @@ namespace Elwig.Helpers.Export {
|
|||||||
var importedAreaComs = new List<(string FileName, string ZwstId, string Device, int Imported, int NotImported, string Filters)>();
|
var importedAreaComs = new List<(string FileName, string ZwstId, string Device, int Imported, int NotImported, string Filters)>();
|
||||||
var importedDeliveries = new List<(string FileName, string ZwstId, string Device, int New, int Overwritten, int NotImported, string Filters)>();
|
var importedDeliveries = new List<(string FileName, string ZwstId, string Device, int New, int Overwritten, int NotImported, string Filters)>();
|
||||||
|
|
||||||
foreach (var ((members, billingAddresses, telephoneNumbers, emailAddresses, areaCommitments, riede, deliveries, deliveryParts, modifiers), meta) in data.Zip(metaData)) {
|
foreach (var ((members, billingAddresses, telephoneNumbers, emailAddresses, areaCommitments, riede, wbKgs, wbGls, deliveries, deliveryParts, modifiers, timestamps), meta) in data.Zip(metaData)) {
|
||||||
var branch = branches[meta.ZwstId];
|
var branch = branches[meta.ZwstId];
|
||||||
var device = meta.Device;
|
var device = meta.Device;
|
||||||
|
|
||||||
using var ctx = new AppDbContext();
|
using var ctx = new AppDbContext();
|
||||||
|
|
||||||
|
var kgnrs = wbKgs.Select(k => k.KgNr).ToList();
|
||||||
|
var duplicateKgNrs = await ctx.WbKgs
|
||||||
|
.Where(k => kgnrs.Contains(k.KgNr))
|
||||||
|
.Select(k => k.KgNr)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
var mgnrs = members.Select(m => m.MgNr).ToList();
|
var mgnrs = members.Select(m => m.MgNr).ToList();
|
||||||
var duplicateMgNrs = await ctx.Members
|
var duplicateMgNrs = await ctx.Members
|
||||||
.Where(m => mgnrs.Contains(m.MgNr))
|
.Where(m => mgnrs.Contains(m.MgNr))
|
||||||
@@ -203,6 +258,12 @@ namespace Elwig.Helpers.Export {
|
|||||||
importDuplicateDeliveries = ImportQuestion(branch.Name, device, "Lieferungen", true, duplicateLsNrs.Count);
|
importDuplicateDeliveries = ImportQuestion(branch.Name, device, "Lieferungen", true, duplicateLsNrs.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (importDuplicateMembers || importNewMembers || importDuplicateDeliveries || importNewDeliveries) {
|
||||||
|
ctx.AddRange(wbGls);
|
||||||
|
ctx.UpdateRange(wbKgs.Where(k => duplicateKgNrs.Contains(k.KgNr)));
|
||||||
|
ctx.AddRange(wbKgs.Where(k => !duplicateKgNrs.Contains(k.KgNr)));
|
||||||
|
}
|
||||||
|
|
||||||
if (importDuplicateMembers) {
|
if (importDuplicateMembers) {
|
||||||
ctx.RemoveRange(ctx.BillingAddresses.Where(a => duplicateMgNrs.Contains(a.MgNr)));
|
ctx.RemoveRange(ctx.BillingAddresses.Where(a => duplicateMgNrs.Contains(a.MgNr)));
|
||||||
ctx.RemoveRange(ctx.MemberTelephoneNrs.Where(n => duplicateMgNrs.Contains(n.MgNr)));
|
ctx.RemoveRange(ctx.MemberTelephoneNrs.Where(n => duplicateMgNrs.Contains(n.MgNr)));
|
||||||
@@ -272,6 +333,26 @@ namespace Elwig.Helpers.Export {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await ctx.SaveChangesAsync();
|
await ctx.SaveChangesAsync();
|
||||||
|
|
||||||
|
var primaryKeys = new Dictionary<string, string>() {
|
||||||
|
["member"] = "mgnr, 0",
|
||||||
|
["area_commitment"] = "fbnr, 0",
|
||||||
|
["delivery"] = "year, did",
|
||||||
|
};
|
||||||
|
var updateStmts = timestamps
|
||||||
|
.SelectMany(e => e.Value.Select(m => $"UPDATE {e.Key} " +
|
||||||
|
$"SET ctime = {((DateTimeOffset)m.CreatedAt.ToUniversalTime()).ToUnixTimeSeconds()}, " +
|
||||||
|
$"mtime = {((DateTimeOffset)m.ModifiedAt.ToUniversalTime()).ToUnixTimeSeconds()} " +
|
||||||
|
$"WHERE ({primaryKeys[e.Key]}) = ({m.Id1}, {m.Id2});"));
|
||||||
|
using var cnx = AppDbContext.Connect();
|
||||||
|
await cnx.ExecuteBatch($"""
|
||||||
|
BEGIN;
|
||||||
|
UPDATE client_parameter SET value = '0' WHERE param = 'ENABLE_TIME_TRIGGERS';
|
||||||
|
{string.Join("\n", updateStmts)}
|
||||||
|
UPDATE client_parameter SET value = '1' WHERE param = 'ENABLE_TIME_TRIGGERS';
|
||||||
|
COMMIT;
|
||||||
|
""");
|
||||||
|
|
||||||
await AddImportedFiles(Path.GetFileName(meta.FileName));
|
await AddImportedFiles(Path.GetFileName(meta.FileName));
|
||||||
}
|
}
|
||||||
App.HintContextChange();
|
App.HintContextChange();
|
||||||
@@ -302,9 +383,9 @@ namespace Elwig.Helpers.Export {
|
|||||||
"Importieren erfolgreich",
|
"Importieren erfolgreich",
|
||||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
} catch (Exception exc) {
|
} catch (Exception exc) {
|
||||||
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
|
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\nEvtl. muss die Datenbank manuell auf dieses Gerät kopieren werden.\n\n" + exc.Message;
|
||||||
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
|
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
|
||||||
MessageBox.Show(str, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBox.Show(str, "Fehler beim Importieren", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
}
|
}
|
||||||
GC.Collect();
|
GC.Collect();
|
||||||
GC.WaitForPendingFinalizers();
|
GC.WaitForPendingFinalizers();
|
||||||
@@ -319,26 +400,30 @@ namespace Elwig.Helpers.Export {
|
|||||||
) == MessageBoxResult.Yes;
|
) == MessageBoxResult.Yes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task Export(string filename, IEnumerable<Member> members, IEnumerable<string> filters) {
|
public static Task Export(string filename, IEnumerable<Member> members, IEnumerable<WbKg> wbKgs, IEnumerable<string> filters) {
|
||||||
return new ElwigExport {
|
return new ElwigExport {
|
||||||
Members = (members, filters)
|
Members = (members, filters),
|
||||||
|
WbKgs = (wbKgs, ["von exportierten Mitgliedern"]),
|
||||||
}.Export(filename);
|
}.Export(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task Export(string filename, IEnumerable<Member> members, IEnumerable<AreaCom> areaComs, IEnumerable<string> filters) {
|
public static Task Export(string filename, IEnumerable<Member> members, IEnumerable<AreaCom> areaComs, IEnumerable<WbKg> wbKgs, IEnumerable<string> filters) {
|
||||||
return new ElwigExport {
|
return new ElwigExport {
|
||||||
Members = (members, filters),
|
Members = (members, filters),
|
||||||
AreaComs = (areaComs, ["von exportierten Mitgliedern"]),
|
AreaComs = (areaComs, ["von exportierten Mitgliedern"]),
|
||||||
|
WbKgs = (wbKgs, ["von exportierten Mitgliedern und Flächenbindungen"]),
|
||||||
}.Export(filename);
|
}.Export(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task Export(string filename, IEnumerable<Delivery> deliveries, IEnumerable<string> filters) {
|
public static Task Export(string filename, IEnumerable<Delivery> deliveries, IEnumerable<WbKg> wbKgs, IEnumerable<string> filters) {
|
||||||
return new ElwigExport {
|
return new ElwigExport {
|
||||||
Deliveries = (deliveries, filters)
|
Deliveries = (deliveries, filters),
|
||||||
|
WbKgs = (wbKgs, ["von exportierten Lieferungen"]),
|
||||||
}.Export(filename);
|
}.Export(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ElwigExport {
|
public class ElwigExport {
|
||||||
|
public (IEnumerable<WbKg> WbKgs, IEnumerable<string> Filters)? WbKgs { get; set; }
|
||||||
public (IEnumerable<Member> Members, IEnumerable<string> Filters)? Members { get; set; }
|
public (IEnumerable<Member> Members, IEnumerable<string> Filters)? Members { get; set; }
|
||||||
public (IEnumerable<AreaCom> AreaComs, IEnumerable<string> Filters)? AreaComs { get; set; }
|
public (IEnumerable<AreaCom> AreaComs, IEnumerable<string> Filters)? AreaComs { get; set; }
|
||||||
public (IEnumerable<Delivery> Deliveries, IEnumerable<string> Filters)? Deliveries { get; set; }
|
public (IEnumerable<Delivery> Deliveries, IEnumerable<string> Filters)? Deliveries { get; set; }
|
||||||
@@ -359,6 +444,12 @@ namespace Elwig.Helpers.Export {
|
|||||||
["zwstid"] = App.ZwstId,
|
["zwstid"] = App.ZwstId,
|
||||||
["device"] = Environment.MachineName,
|
["device"] = Environment.MachineName,
|
||||||
};
|
};
|
||||||
|
if (WbKgs != null) {
|
||||||
|
obj["wb_kgs"] = new JsonObject {
|
||||||
|
["count"] = WbKgs.Value.WbKgs.Count(),
|
||||||
|
["filters"] = new JsonArray(WbKgs.Value.Filters.Select(f => (JsonNode)f).ToArray()),
|
||||||
|
};
|
||||||
|
}
|
||||||
if (Members != null)
|
if (Members != null)
|
||||||
obj["members"] = new JsonObject {
|
obj["members"] = new JsonObject {
|
||||||
["count"] = Members.Value.Members.Count(),
|
["count"] = Members.Value.Members.Count(),
|
||||||
@@ -375,34 +466,69 @@ namespace Elwig.Helpers.Export {
|
|||||||
["parts"] = Deliveries.Value.Deliveries.Sum(d => d.Parts.Count),
|
["parts"] = Deliveries.Value.Deliveries.Sum(d => d.Parts.Count),
|
||||||
["filters"] = new JsonArray(Deliveries.Value.Filters.Select(f => (JsonNode)f).ToArray()),
|
["filters"] = new JsonArray(Deliveries.Value.Filters.Select(f => (JsonNode)f).ToArray()),
|
||||||
};
|
};
|
||||||
await writer.WriteAsync(obj.ToJsonString(JsonOpts));
|
await writer.WriteAsync(obj.ToJsonString(Utils.JsonOpts));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO encrypt files
|
// TODO encrypt files
|
||||||
|
if (WbKgs != null) {
|
||||||
|
var json = zip.CreateEntry("wb_kgs.json", CompressionLevel.SmallestSize);
|
||||||
|
using var writer = new StreamWriter(json.Open(), Utils.UTF8);
|
||||||
|
foreach (var k in WbKgs.Value.WbKgs) {
|
||||||
|
await writer.WriteLineAsync(k.ToJson().ToJsonString(Utils.JsonOpts));
|
||||||
|
}
|
||||||
|
}
|
||||||
if (Members != null) {
|
if (Members != null) {
|
||||||
var json = zip.CreateEntry("members.json", CompressionLevel.SmallestSize);
|
var json = zip.CreateEntry("members.json", CompressionLevel.SmallestSize);
|
||||||
using var writer = new StreamWriter(json.Open(), Utils.UTF8);
|
using var writer = new StreamWriter(json.Open(), Utils.UTF8);
|
||||||
foreach (var m in Members.Value.Members) {
|
foreach (var m in Members.Value.Members) {
|
||||||
await writer.WriteLineAsync(m.ToJson().ToJsonString(JsonOpts));
|
await writer.WriteLineAsync(m.ToJson().ToJsonString(Utils.JsonOpts));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (AreaComs != null) {
|
if (AreaComs != null) {
|
||||||
var json = zip.CreateEntry("area_commitments.json", CompressionLevel.SmallestSize);
|
var json = zip.CreateEntry("area_commitments.json", CompressionLevel.SmallestSize);
|
||||||
using var writer = new StreamWriter(json.Open(), Utils.UTF8);
|
using var writer = new StreamWriter(json.Open(), Utils.UTF8);
|
||||||
foreach (var c in AreaComs.Value.AreaComs) {
|
foreach (var c in AreaComs.Value.AreaComs) {
|
||||||
await writer.WriteLineAsync(c.ToJson().ToJsonString(JsonOpts));
|
await writer.WriteLineAsync(c.ToJson().ToJsonString(Utils.JsonOpts));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Deliveries != null) {
|
if (Deliveries != null) {
|
||||||
var json = zip.CreateEntry("deliveries.json", CompressionLevel.SmallestSize);
|
var json = zip.CreateEntry("deliveries.json", CompressionLevel.SmallestSize);
|
||||||
using var writer = new StreamWriter(json.Open(), Utils.UTF8);
|
using var writer = new StreamWriter(json.Open(), Utils.UTF8);
|
||||||
foreach (var d in Deliveries.Value.Deliveries) {
|
foreach (var d in Deliveries.Value.Deliveries) {
|
||||||
await writer.WriteLineAsync(d.ToJson().ToJsonString(JsonOpts));
|
await writer.WriteLineAsync(d.ToJson().ToJsonString(Utils.JsonOpts));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static JsonObject ToJson(this WbKg k) {
|
||||||
|
return new JsonObject {
|
||||||
|
["kgnr"] = k.KgNr,
|
||||||
|
["großlage"] = k.Gl?.Name,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static (WbKg, WbGl?) ToWbKg(this JsonNode json, List<WbGl> gls) {
|
||||||
|
var grosslage = json["großlage"]?.AsValue().GetValue<string>();
|
||||||
|
WbGl? gl = null;
|
||||||
|
bool newGl = false;
|
||||||
|
if (grosslage != null) {
|
||||||
|
gl = gls.FirstOrDefault(g => g.Name == grosslage);
|
||||||
|
if (gl == null) {
|
||||||
|
newGl = true;
|
||||||
|
gl = new WbGl {
|
||||||
|
GlNr = (gls.Count == 0 ? 1 : gls.Max(g => g.GlNr)) + 1,
|
||||||
|
Name = grosslage,
|
||||||
|
};
|
||||||
|
gls[gl.GlNr] = gl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (new WbKg {
|
||||||
|
KgNr = json["kgnr"]!.AsValue().GetValue<int>(),
|
||||||
|
GlNr = gl?.GlNr,
|
||||||
|
}, newGl ? gl : null);
|
||||||
|
}
|
||||||
|
|
||||||
public static JsonObject ToJson(this Member m) {
|
public static JsonObject ToJson(this Member m) {
|
||||||
return new JsonObject {
|
return new JsonObject {
|
||||||
["mgnr"] = m.MgNr,
|
["mgnr"] = m.MgNr,
|
||||||
@@ -460,15 +586,19 @@ namespace Elwig.Helpers.Export {
|
|||||||
return obj;
|
return obj;
|
||||||
}).ToArray()),
|
}).ToArray()),
|
||||||
["comment"] = m.Comment,
|
["comment"] = m.Comment,
|
||||||
|
["created_at"] = $"{m.CreatedAt:yyyy-MM-ddTHH:mm:ssK}",
|
||||||
|
["modified_at"] = $"{m.ModifiedAt:yyyy-MM-ddTHH:mm:ssK}",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static (Member, BillingAddr?, List<MemberTelNr>, List<MemberEmailAddr>) ToMember(this JsonNode json, Dictionary<int, AT_Kg> kgs) {
|
public static (Member, BillingAddr?, List<MemberTelNr>, List<MemberEmailAddr>, (DateTime CreatedAt, DateTime ModifiedAt)?) ToMember(this JsonNode json, Dictionary<int, AT_Kg> kgs) {
|
||||||
var mgnr = json["mgnr"]!.AsValue().GetValue<int>();
|
var mgnr = json["mgnr"]!.AsValue().GetValue<int>();
|
||||||
var kgnr = json["default_kgnr"]?.AsValue().GetValue<int>();
|
var kgnr = json["default_kgnr"]?.AsValue().GetValue<int>();
|
||||||
if (kgnr != null && !kgs.Values.Any(k => k.WbKg?.KgNr == kgnr)) {
|
if (kgnr != null && !kgs.Values.Any(k => k.WbKg?.KgNr == kgnr)) {
|
||||||
throw new ArgumentException($"Für KG {(kgs.TryGetValue(kgnr.Value, out var k) ? k.Name : "?")} ({kgnr:00000}) ist noch keine Großlage festgelegt!\n(Stammdaten → Herkunftshierarchie)");
|
throw new ArgumentException($"Für KG {(kgs.TryGetValue(kgnr.Value, out var k) ? k.Name : "?")} ({kgnr:00000}) ist noch keine Großlage festgelegt!\n(Stammdaten → Herkunftshierarchie)");
|
||||||
}
|
}
|
||||||
|
var createdAt = json["created_at"]?.AsValue().GetValue<string>();
|
||||||
|
var modifiedAt = json["modified_at"]?.AsValue().GetValue<string>();
|
||||||
return (new Member {
|
return (new Member {
|
||||||
MgNr = mgnr,
|
MgNr = mgnr,
|
||||||
PredecessorMgNr = json["predecessor_mgnr"]?.AsValue().GetValue<int>(),
|
PredecessorMgNr = json["predecessor_mgnr"]?.AsValue().GetValue<int>(),
|
||||||
@@ -502,6 +632,7 @@ namespace Elwig.Helpers.Export {
|
|||||||
ContactViaPost = json["contact_postal"]?.AsValue().GetValue<bool>() ?? false,
|
ContactViaPost = json["contact_postal"]?.AsValue().GetValue<bool>() ?? false,
|
||||||
ContactViaEmail = json["contact_email"]?.AsValue().GetValue<bool>() ?? false,
|
ContactViaEmail = json["contact_email"]?.AsValue().GetValue<bool>() ?? false,
|
||||||
Comment = json["comment"]?.AsValue().GetValue<string>(),
|
Comment = json["comment"]?.AsValue().GetValue<string>(),
|
||||||
|
ImportedAt = DateTime.Now,
|
||||||
}, json["billing_address"] is JsonObject a ? new BillingAddr {
|
}, json["billing_address"] is JsonObject a ? new BillingAddr {
|
||||||
MgNr = mgnr,
|
MgNr = mgnr,
|
||||||
FullName = a["name"]!.AsValue().GetValue<string>(),
|
FullName = a["name"]!.AsValue().GetValue<string>(),
|
||||||
@@ -519,7 +650,10 @@ namespace Elwig.Helpers.Export {
|
|||||||
Nr = i + 1,
|
Nr = i + 1,
|
||||||
Address = a["address"]!.AsValue().GetValue<string>(),
|
Address = a["address"]!.AsValue().GetValue<string>(),
|
||||||
Comment = a["comment"]?.AsValue().GetValue<string>(),
|
Comment = a["comment"]?.AsValue().GetValue<string>(),
|
||||||
}).ToList());
|
}).ToList(),
|
||||||
|
createdAt == null || modifiedAt == null ? null :
|
||||||
|
(DateTime.ParseExact(createdAt, "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None),
|
||||||
|
DateTime.ParseExact(modifiedAt, "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JsonObject ToJson(this AreaCom c) {
|
public static JsonObject ToJson(this AreaCom c) {
|
||||||
@@ -535,27 +669,32 @@ namespace Elwig.Helpers.Export {
|
|||||||
["year_from"] = c.YearFrom,
|
["year_from"] = c.YearFrom,
|
||||||
["year_to"] = c.YearTo,
|
["year_to"] = c.YearTo,
|
||||||
["comment"] = c.Comment,
|
["comment"] = c.Comment,
|
||||||
|
["created_at"] = $"{c.CreatedAt:yyyy-MM-ddTHH:mm:ssK}",
|
||||||
|
["modified_at"] = $"{c.ModifiedAt:yyyy-MM-ddTHH:mm:ssK}",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static (AreaCom, WbRd?) ToAreaCom(this JsonNode json, Dictionary<int, AT_Kg> kgs, Dictionary<int, List<WbRd>> riede) {
|
public static (AreaCom, WbRd?, (DateTime CreatedAt, DateTime ModifiedAt)?) ToAreaCom(this JsonNode json, Dictionary<int, List<WbRd>> riede) {
|
||||||
var kgnr = json["kgnr"]!.AsValue().GetValue<int>();
|
var kgnr = json["kgnr"]!.AsValue().GetValue<int>();
|
||||||
var ried = json["ried"]?.AsValue().GetValue<string>();
|
var ried = json["ried"]?.AsValue().GetValue<string>();
|
||||||
WbRd? rd = null;
|
WbRd? rd = null;
|
||||||
bool newRd = false;
|
bool newRd = false;
|
||||||
if (ried != null) {
|
if (ried != null) {
|
||||||
var rde = riede[kgnr] ?? throw new ArgumentException($"Für KG {(kgs.TryGetValue(kgnr, out var k) ? k.Name : "?")} ({kgnr:00000}) ist noch keine Großlage festgelegt!\n(Stammdaten → Herkunftshierarchie)");
|
var rde = riede.GetValueOrDefault(kgnr, []);
|
||||||
rd = rde.FirstOrDefault(r => r.Name == ried);
|
rd = rde.FirstOrDefault(r => r.Name == ried);
|
||||||
if (rd == null) {
|
if (rd == null) {
|
||||||
newRd = true;
|
newRd = true;
|
||||||
rd = new WbRd {
|
rd = new WbRd {
|
||||||
KgNr = kgnr,
|
KgNr = kgnr,
|
||||||
RdNr = (rde.Count == 0 ? 0 : rde.Max(r => r.RdNr)) + 1,
|
RdNr = (rde.Count == 0 ? 1 : rde.Max(r => r.RdNr)) + 1,
|
||||||
Name = ried,
|
Name = ried,
|
||||||
};
|
};
|
||||||
rde.Add(rd);
|
rde.Add(rd);
|
||||||
|
riede[rd.KgNr] = rde;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var createdAt = json["created_at"]?.AsValue().GetValue<string>();
|
||||||
|
var modifiedAt = json["modified_at"]?.AsValue().GetValue<string>();
|
||||||
return (new AreaCom {
|
return (new AreaCom {
|
||||||
FbNr = json["fbnr"]!.AsValue().GetValue<int>(),
|
FbNr = json["fbnr"]!.AsValue().GetValue<int>(),
|
||||||
MgNr = json["mgnr"]!.AsValue().GetValue<int>(),
|
MgNr = json["mgnr"]!.AsValue().GetValue<int>(),
|
||||||
@@ -564,11 +703,15 @@ namespace Elwig.Helpers.Export {
|
|||||||
Area = json["area"]!.AsValue().GetValue<int>(),
|
Area = json["area"]!.AsValue().GetValue<int>(),
|
||||||
KgNr = kgnr,
|
KgNr = kgnr,
|
||||||
GstNr = json["gstnr"]?.AsValue().GetValue<string>() ?? "-",
|
GstNr = json["gstnr"]?.AsValue().GetValue<string>() ?? "-",
|
||||||
RdNr = rd?.RdNr,
|
RdNr = rd?.RdNr ?? json["rdnr"]?.AsValue().GetValue<int>(),
|
||||||
YearFrom = json["year_from"]?.AsValue().GetValue<int>(),
|
YearFrom = json["year_from"]?.AsValue().GetValue<int>(),
|
||||||
YearTo = json["year_to"]?.AsValue().GetValue<int>(),
|
YearTo = json["year_to"]?.AsValue().GetValue<int>(),
|
||||||
Comment = json["comment"]?.AsValue().GetValue<string>(),
|
Comment = json["comment"]?.AsValue().GetValue<string>(),
|
||||||
}, newRd ? rd : null);
|
ImportedAt = DateTime.Now,
|
||||||
|
}, newRd ? rd : null,
|
||||||
|
createdAt == null || modifiedAt == null ? null :
|
||||||
|
(DateTime.ParseExact(createdAt, "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None),
|
||||||
|
DateTime.ParseExact(modifiedAt, "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JsonObject ToJson(this Delivery d) {
|
public static JsonObject ToJson(this Delivery d) {
|
||||||
@@ -591,16 +734,18 @@ namespace Elwig.Helpers.Export {
|
|||||||
["qualid"] = p.QualId,
|
["qualid"] = p.QualId,
|
||||||
["hkid"] = p.HkId,
|
["hkid"] = p.HkId,
|
||||||
["kgnr"] = p.KgNr,
|
["kgnr"] = p.KgNr,
|
||||||
["rdnr"] = p.RdNr,
|
["ried"] = p.Rd?.Name,
|
||||||
["net_weight"] = p.IsNetWeight,
|
["net_weight"] = p.IsNetWeight,
|
||||||
["manual_weighing"] = p.IsManualWeighing,
|
["manual_weighing"] = p.IsManualWeighing,
|
||||||
["modids"] = new JsonArray(p.Modifiers.Select(m => (JsonNode)m.ModId).ToArray()),
|
["modids"] = new JsonArray(p.Modifiers.Select(m => (JsonNode)m.ModId).ToArray()),
|
||||||
["comment"] = p.Comment,
|
["comment"] = p.Comment,
|
||||||
|
["created_at"] = $"{p.CreatedAt:yyyy-MM-ddTHH:mm:ssK}",
|
||||||
|
["modified_at"] = $"{p.ModifiedAt:yyyy-MM-ddTHH:mm:ssK}",
|
||||||
};
|
};
|
||||||
if (p.IsSplCheck) obj["spl_check"] = p.IsSplCheck;
|
if (p.IsSplCheck) obj["spl_check"] = p.IsSplCheck;
|
||||||
if (p.IsHandPicked != null) obj["hand_picked"] = p.IsHandPicked;
|
if (p.IsHandPicked != null) obj["hand_picked"] = p.IsHandPicked;
|
||||||
if (p.IsLesewagen != null) obj["lesewagen"] = p.IsLesewagen;
|
|
||||||
if (p.IsGebunden != null) obj["gebunden"] = p.IsGebunden;
|
if (p.IsGebunden != null) obj["gebunden"] = p.IsGebunden;
|
||||||
|
if (p.Unloading != null) obj["unloading"] = p.Unloading;
|
||||||
if (p.Temperature != null) obj["temperature"] = p.Temperature;
|
if (p.Temperature != null) obj["temperature"] = p.Temperature;
|
||||||
if (p.Acid != null) obj["acid"] = p.Acid;
|
if (p.Acid != null) obj["acid"] = p.Acid;
|
||||||
if (p.ScaleId != null) obj["scale_id"] = p.ScaleId;
|
if (p.ScaleId != null) obj["scale_id"] = p.ScaleId;
|
||||||
@@ -609,10 +754,12 @@ namespace Elwig.Helpers.Export {
|
|||||||
return obj;
|
return obj;
|
||||||
}).ToArray()),
|
}).ToArray()),
|
||||||
["comment"] = d.Comment,
|
["comment"] = d.Comment,
|
||||||
|
["created_at"] = $"{d.CreatedAt:yyyy-MM-ddTHH:mm:ssK}",
|
||||||
|
["modified_at"] = $"{d.ModifiedAt:yyyy-MM-ddTHH:mm:ssK}",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static (Delivery, List<DeliveryPart>, List<DeliveryPartModifier>) ToDelivery(this JsonNode json, Dictionary<string, int> currentLsNrs, Dictionary<int, int> currentDids) {
|
public static (Delivery, List<DeliveryPart>, List<DeliveryPartModifier>, List<WbRd>, (DateTime CreatedAt, DateTime ModifiedAt)?) ToDelivery(this JsonNode json, Dictionary<string, int> currentLsNrs, Dictionary<int, int> currentDids, Dictionary<int, AT_Kg> kgs, Dictionary<int, List<WbRd>> riede) {
|
||||||
var year = json["year"]!.AsValue().GetValue<int>();
|
var year = json["year"]!.AsValue().GetValue<int>();
|
||||||
var lsnr = json["lsnr"]!.AsValue().GetValue<string>();
|
var lsnr = json["lsnr"]!.AsValue().GetValue<string>();
|
||||||
var did = currentLsNrs.GetValueOrDefault(lsnr, -1);
|
var did = currentLsNrs.GetValueOrDefault(lsnr, -1);
|
||||||
@@ -621,6 +768,9 @@ namespace Elwig.Helpers.Export {
|
|||||||
did = ++currentDids[year];
|
did = ++currentDids[year];
|
||||||
}
|
}
|
||||||
currentLsNrs[lsnr] = did;
|
currentLsNrs[lsnr] = did;
|
||||||
|
var createdAt = json["created_at"]?.AsValue().GetValue<string>();
|
||||||
|
var modifiedAt = json["modified_at"]?.AsValue().GetValue<string>();
|
||||||
|
var wbRde = new List<WbRd>();
|
||||||
return (new Delivery {
|
return (new Delivery {
|
||||||
Year = year,
|
Year = year,
|
||||||
DId = did,
|
DId = did,
|
||||||
@@ -631,37 +781,61 @@ namespace Elwig.Helpers.Export {
|
|||||||
LsNr = lsnr,
|
LsNr = lsnr,
|
||||||
MgNr = json["mgnr"]!.AsValue().GetValue<int>(),
|
MgNr = json["mgnr"]!.AsValue().GetValue<int>(),
|
||||||
Comment = json["comment"]?.AsValue().GetValue<string>(),
|
Comment = json["comment"]?.AsValue().GetValue<string>(),
|
||||||
}, json["parts"]!.AsArray().Select(p => p!.AsObject()).Select(p => new DeliveryPart {
|
ImportedAt = DateTime.Now,
|
||||||
Year = year,
|
}, json["parts"]!.AsArray().Select(p => p!.AsObject()).Select(p => {
|
||||||
DId = did,
|
var kgnr = p["kgnr"]!.AsValue().GetValue<int>();
|
||||||
DPNr = p["dpnr"]!.AsValue().GetValue<int>(),
|
var ried = p["ried"]?.AsValue().GetValue<string>();
|
||||||
SortId = p["sortid"]!.AsValue().GetValue<string>(),
|
WbRd? rd = null;
|
||||||
AttrId = p["attrid"]?.AsValue().GetValue<string>(),
|
if (ried != null) {
|
||||||
CultId = p["cultid"]?.AsValue().GetValue<string>(),
|
var rde = riede.GetValueOrDefault(kgnr, []);
|
||||||
Weight = p["weight"]!.AsValue().GetValue<int>(),
|
rd = rde.FirstOrDefault(r => r.Name == ried);
|
||||||
Kmw = p["kmw"]!.AsValue().GetValue<double>(),
|
if (rd == null) {
|
||||||
QualId = p["qualid"]!.AsValue().GetValue<string>(),
|
rd = new WbRd {
|
||||||
HkId = p["hkid"]!.AsValue().GetValue<string>(),
|
KgNr = kgnr,
|
||||||
KgNr = p["kgnr"]?.AsValue().GetValue<int>(),
|
RdNr = (rde.Count == 0 ? 1 : rde.Max(r => r.RdNr)) + 1,
|
||||||
RdNr = p["rdnr"]?.AsValue().GetValue<int>(),
|
Name = ried,
|
||||||
IsNetWeight = p["net_weight"]!.AsValue().GetValue<bool>(),
|
};
|
||||||
IsManualWeighing = p["manual_weighing"]!.AsValue().GetValue<bool>(),
|
rde.Add(rd);
|
||||||
Comment = p["comment"]?.AsValue().GetValue<string>(),
|
riede[rd.KgNr] = rde;
|
||||||
IsSplCheck = p["spl_check"]?.AsValue().GetValue<bool>() ?? false,
|
wbRde.Add(rd);
|
||||||
IsHandPicked = p["hand_picked"]?.AsValue().GetValue<bool>(),
|
}
|
||||||
IsLesewagen = p["lesewagen"]?.AsValue().GetValue<bool>(),
|
}
|
||||||
IsGebunden = p["gebunden"]?.AsValue().GetValue<bool>(),
|
return new DeliveryPart {
|
||||||
Temperature = p["temperature"]?.AsValue().GetValue<double>(),
|
Year = year,
|
||||||
Acid = p["acid"]?.AsValue().GetValue<double>(),
|
DId = did,
|
||||||
ScaleId = p["scale_id"]?.AsValue().GetValue<string>(),
|
DPNr = p["dpnr"]!.AsValue().GetValue<int>(),
|
||||||
WeighingData = p["weighing_data"]?.AsObject().ToJsonString(JsonOpts),
|
SortId = p["sortid"]!.AsValue().GetValue<string>(),
|
||||||
WeighingReason = p["weighing_reason"]?.AsValue().GetValue<string>(),
|
AttrId = p["attrid"]?.AsValue().GetValue<string>(),
|
||||||
|
CultId = p["cultid"]?.AsValue().GetValue<string>(),
|
||||||
|
Weight = p["weight"]!.AsValue().GetValue<int>(),
|
||||||
|
Kmw = p["kmw"]!.AsValue().GetValue<double>(),
|
||||||
|
QualId = p["qualid"]!.AsValue().GetValue<string>(),
|
||||||
|
HkId = p["hkid"]!.AsValue().GetValue<string>(),
|
||||||
|
KgNr = p["kgnr"]?.AsValue().GetValue<int>(),
|
||||||
|
RdNr = rd?.RdNr ?? p["rdnr"]?.AsValue().GetValue<int>(),
|
||||||
|
IsNetWeight = p["net_weight"]!.AsValue().GetValue<bool>(),
|
||||||
|
IsManualWeighing = p["manual_weighing"]!.AsValue().GetValue<bool>(),
|
||||||
|
Comment = p["comment"]?.AsValue().GetValue<string>(),
|
||||||
|
IsSplCheck = p["spl_check"]?.AsValue().GetValue<bool>() ?? false,
|
||||||
|
IsHandPicked = p["hand_picked"]?.AsValue().GetValue<bool>(),
|
||||||
|
IsGebunden = p["gebunden"]?.AsValue().GetValue<bool>(),
|
||||||
|
Unloading = p["unloading"]?.AsValue().GetValue<string>() ?? ((p["lesewagen"]?.AsValue().GetValue<bool>() ?? false) ? DeliveryPart.Pumped : null),
|
||||||
|
Temperature = p["temperature"]?.AsValue().GetValue<double>(),
|
||||||
|
Acid = p["acid"]?.AsValue().GetValue<double>(),
|
||||||
|
ScaleId = p["scale_id"]?.AsValue().GetValue<string>(),
|
||||||
|
WeighingData = p["weighing_data"]?.AsObject().ToJsonString(Utils.JsonOpts),
|
||||||
|
WeighingReason = p["weighing_reason"]?.AsValue().GetValue<string>(),
|
||||||
|
};
|
||||||
}).ToList(), json["parts"]!.AsArray().SelectMany(p => p!["modids"]!.AsArray().Select(m => new DeliveryPartModifier {
|
}).ToList(), json["parts"]!.AsArray().SelectMany(p => p!["modids"]!.AsArray().Select(m => new DeliveryPartModifier {
|
||||||
Year = year,
|
Year = year,
|
||||||
DId = did,
|
DId = did,
|
||||||
DPNr = p["dpnr"]!.AsValue().GetValue<int>(),
|
DPNr = p["dpnr"]!.AsValue().GetValue<int>(),
|
||||||
ModId = m!.AsValue().GetValue<string>(),
|
ModId = m!.AsValue().GetValue<string>(),
|
||||||
})).ToList());
|
})).ToList(),
|
||||||
|
wbRde,
|
||||||
|
createdAt == null || modifiedAt == null ? null :
|
||||||
|
(DateTime.ParseExact(createdAt, "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None),
|
||||||
|
DateTime.ParseExact(modifiedAt, "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -317,7 +317,7 @@ namespace Elwig.Helpers.Export {
|
|||||||
c = $"<{ct} office:value-type=\"string\" calcext:value-type=\"string\"{add}><text:p>{SecurityElement.Escape(data.ToString())}</text:p></{ct}>";
|
c = $"<{ct} office:value-type=\"string\" calcext:value-type=\"string\"{add}><text:p>{SecurityElement.Escape(data.ToString())}</text:p></{ct}>";
|
||||||
}
|
}
|
||||||
|
|
||||||
return $" {c}\r\n" + (colSpan > 1 ? $" <table:covered-table-cell table:number-rows-repeated=\"{colSpan - 1}\"/>\r\n" : "");
|
return $" {c}\r\n" + (colSpan > 1 ? $" <table:covered-table-cell table:number-columns-repeated=\"{colSpan - 1}\"/>\r\n" : "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
69
Elwig/Helpers/Export/VCard.cs
Normal file
69
Elwig/Helpers/Export/VCard.cs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
using Elwig.Models.Entities;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Elwig.Helpers.Export {
|
||||||
|
public class VCard : IExporter<Member> {
|
||||||
|
|
||||||
|
public static string FileExtension => "vcf";
|
||||||
|
|
||||||
|
private readonly StreamWriter _writer;
|
||||||
|
|
||||||
|
public VCard(string filename) : this(filename, Utils.UTF8) { }
|
||||||
|
|
||||||
|
public VCard(string filename, Encoding encoding) {
|
||||||
|
_writer = new StreamWriter(filename, false, encoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() {
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
_writer.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueTask DisposeAsync() {
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
return _writer.DisposeAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ExportAsync(IEnumerable<Member> data, IProgress<double>? progress = null) {
|
||||||
|
progress?.Report(0.0);
|
||||||
|
int count = data.Count() + 1, i = 0;
|
||||||
|
|
||||||
|
foreach (var row in data) {
|
||||||
|
var billingAddr = row.BillingAddress != null ? $"ADR;TYPE=work;LANGUAGE=de;LABEL=\"{Escape(row.BillingAddress.FullName)}\\n{Escape(row.BillingAddress.Address)}\\n{row.BillingAddress.PostalDest.AtPlz?.Plz} {Escape(row.BillingAddress.PostalDest.AtPlz?.Ort.Name)}\\nÖsterreich\":;;{Escape(row.BillingAddress.Address)};{Escape(row.BillingAddress.PostalDest.AtPlz?.Ort.Name)};;{row.BillingAddress.PostalDest.AtPlz?.Plz};Österreich\r\n" : null;
|
||||||
|
var tel = string.Join("", row.TelephoneNumbers
|
||||||
|
.Where(n => n.Type != "fax")
|
||||||
|
.Select(n => $"TEL;TYPE={(n.Type == "mobile" ? "cell" : "voice")}:{Escape(n.Number)}\r\n"));
|
||||||
|
var email = string.Join("", row.EmailAddresses.Select(a => $"EMAIL:{Escape(a.Address)}\r\n"));
|
||||||
|
await _writer.WriteLineAsync($"""
|
||||||
|
BEGIN:VCARD
|
||||||
|
VERSION:4.0
|
||||||
|
UID:mg{row.MgNr}@{App.Client.NameToken.ToLower()}.elwig.at
|
||||||
|
NOTE:MgNr. {row.MgNr}
|
||||||
|
FN:{Escape(row.AdministrativeName)}
|
||||||
|
N:{Escape(row.Name)};{Escape(row.GivenName)};{Escape(row.MiddleName)};{Escape(row.Prefix)};{Escape(row.Suffix)}
|
||||||
|
KIND:{(row.IsJuridicalPerson ? "org" : "individual")}
|
||||||
|
ADR{(billingAddr == null ? "" : ";TYPE=home")};LANGUAGE=de;LABEL="{Escape(row.Address)}\n{row.PostalDest.AtPlz?.Plz} {Escape(row.PostalDest.AtPlz?.Ort.Name)}\nÖsterreich":;;{Escape(row.Address)};{Escape(row.PostalDest.AtPlz?.Ort.Name)};;{row.PostalDest.AtPlz?.Plz};Österreich
|
||||||
|
{billingAddr}{tel}{email}REV:{row.ModifiedAt.ToUniversalTime():yyyyMMdd\THHmmss\Z}
|
||||||
|
END:VCARD
|
||||||
|
""");
|
||||||
|
progress?.Report(100.0 * ++i / count);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _writer.FlushAsync();
|
||||||
|
progress?.Report(100.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Export(IEnumerable<Member> data, IProgress<double>? progress = null) {
|
||||||
|
ExportAsync(data, progress).GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string? Escape(string? text) {
|
||||||
|
return text?.Replace("\\", "\\\\").Replace(",", "\\,").Replace(";", "\\;").Replace("\n", "\\n").Replace("\r", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
namespace Elwig.Helpers {
|
namespace Elwig.Helpers {
|
||||||
public enum ExportMode {
|
public enum ExportMode {
|
||||||
Show, SaveList, SavePdf, Print, Email, Export, Upload
|
Show, SaveList, SavePdf, Print, Email, Vcf, Export, Upload
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
|
using Microsoft.Data.Sqlite;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.IO.Hashing;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Reflection;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Elwig.Helpers {
|
namespace Elwig.Helpers {
|
||||||
static partial class Extensions {
|
public static partial class Extensions {
|
||||||
|
|
||||||
[LibraryImport("msvcrt.dll")]
|
[LibraryImport("msvcrt.dll")]
|
||||||
[UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
|
[UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
|
||||||
@@ -95,5 +100,50 @@ namespace Elwig.Helpers {
|
|||||||
await download.CopyToAsync(destination, 81920, relativeProgress, cancellationToken);
|
await download.CopyToAsync(destination, 81920, relativeProgress, cancellationToken);
|
||||||
progress.Report(100.0);
|
progress.Report(100.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task CheckIntegrity(this ZipArchive zip) {
|
||||||
|
var crc = new Crc32();
|
||||||
|
foreach (var entry in zip.Entries) {
|
||||||
|
crc.Reset();
|
||||||
|
using var stream = entry.Open();
|
||||||
|
await crc.AppendAsync(stream);
|
||||||
|
if (crc.GetCurrentHashAsUInt32() != entry.Crc32)
|
||||||
|
throw new InvalidDataException($"CRC-32 mismatch in '{entry.FullName}'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task ExecuteBatch(this SqliteConnection cnx, string sql) {
|
||||||
|
using var cmd = cnx.CreateCommand();
|
||||||
|
cmd.CommandText = sql;
|
||||||
|
using var reader = await cmd.ExecuteReaderAsync();
|
||||||
|
while (await reader.NextResultAsync()) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task ExecuteEmbeddedScript(this SqliteConnection cnx, Assembly asm, string name) {
|
||||||
|
using var stream = asm.GetManifestResourceStream(name) ?? throw new FileNotFoundException("Unable to load embedded resource");
|
||||||
|
using var reader = new StreamReader(stream);
|
||||||
|
await ExecuteBatch(cnx, await reader.ReadToEndAsync());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<object?> ExecuteScalar(this SqliteConnection cnx, string sql) {
|
||||||
|
using var cmd = cnx.CreateCommand();
|
||||||
|
cmd.CommandText = sql;
|
||||||
|
return await cmd.ExecuteScalarAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<(string Table, long RowId, string Parent, long FkId)[]> ForeignKeyCheck(this SqliteConnection cnx) {
|
||||||
|
using var cmd = cnx.CreateCommand();
|
||||||
|
cmd.CommandText = "PRAGMA foreign_key_check";
|
||||||
|
using var reader = await cmd.ExecuteReaderAsync();
|
||||||
|
var list = new List<(string, long, string, long)>();
|
||||||
|
while (await reader.ReadAsync()) {
|
||||||
|
var table = reader.GetString(0);
|
||||||
|
var rowid = reader.GetInt64(1);
|
||||||
|
var parent = reader.GetString(2);
|
||||||
|
var fkid = reader.GetInt64(3);
|
||||||
|
list.Add((table, rowid, parent, fkid));
|
||||||
|
}
|
||||||
|
return [.. list];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace Elwig.Helpers.Printing {
|
|||||||
|
|
||||||
public static async Task Init(Action? evtHandler = null) {
|
public static async Task Init(Action? evtHandler = null) {
|
||||||
var e = new RazorLightEngineBuilder()
|
var e = new RazorLightEngineBuilder()
|
||||||
.UseFileSystemProject(App.DataPath + "resources")
|
.UseFileSystemProject(App.DocumentsPath)
|
||||||
.UseMemoryCachingProvider()
|
.UseMemoryCachingProvider()
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,28 @@
|
|||||||
using System.Threading.Tasks;
|
|
||||||
using Elwig.Windows;
|
using Elwig.Windows;
|
||||||
using System.Diagnostics;
|
using PdfiumViewer;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Windows;
|
using System.Diagnostics;
|
||||||
using System.Text.RegularExpressions;
|
using System.Drawing.Printing;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using PdfiumViewer;
|
using System.Reflection;
|
||||||
using System.Drawing.Printing;
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
namespace Elwig.Helpers.Printing {
|
namespace Elwig.Helpers.Printing {
|
||||||
public static class Pdf {
|
public static class Pdf {
|
||||||
|
|
||||||
private static readonly string WinziPrint = new string[] { App.ExePath }
|
private static readonly string WinziPrint = (Assembly.GetEntryAssembly()?.Location.Contains(@"\bin\") ?? false) ?
|
||||||
.Union(Environment.GetEnvironmentVariable("PATH")?.Split(';') ?? [])
|
Path.Combine(Assembly.GetEntryAssembly()!.Location.Split(@"\bin\")[0], "../Installer/Files/WinziPrint.exe") :
|
||||||
.Select(x => Path.Combine(x, "WinziPrint.exe"))
|
new string[] { App.InstallPath }
|
||||||
.Where(File.Exists)
|
.Union(Environment.GetEnvironmentVariable("PATH")?.Split(';') ?? [])
|
||||||
.FirstOrDefault() ?? throw new FileNotFoundException("WiniPrint executable not found");
|
.Select(x => Path.Combine(x, "WinziPrint.exe"))
|
||||||
|
.Where(File.Exists)
|
||||||
|
.FirstOrDefault() ?? throw new FileNotFoundException("WiniPrint executable not found");
|
||||||
private static Process? WinziPrintProc;
|
private static Process? WinziPrintProc;
|
||||||
public static bool IsReady => WinziPrintProc != null;
|
public static bool IsReady => WinziPrintProc != null;
|
||||||
|
|
||||||
@@ -46,24 +50,45 @@ namespace Elwig.Helpers.Printing {
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<(int Pages, IEnumerable<int> PerDoc)> Convert(string htmlPath, string pdfPath, bool doublePaged = false, IProgress<double>? progress = null) {
|
public static async Task<(int Pages, IEnumerable<int> PerDoc)> Convert(string htmlPath, string pdfPath, bool doublePaged = false, CancellationToken? cancelToken = null, IProgress<double>? progress = null) {
|
||||||
return await Convert([htmlPath], pdfPath, doublePaged, progress);
|
return await Convert([htmlPath], pdfPath, doublePaged, cancelToken, progress);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<(int Pages, IEnumerable<int> PerDoc)> Convert(IEnumerable<string> htmlPath, string pdfPath, bool doublePaged = false, IProgress<double>? progress = null) {
|
public static async Task<(int Pages, IEnumerable<int> PerDoc)> Convert(IEnumerable<string> htmlPath, string pdfPath, bool doublePaged = false, CancellationToken? cancelToken = null, IProgress<double>? progress = null) {
|
||||||
if (WinziPrintProc == null) throw new InvalidOperationException("The WinziPrint process has not been initialized yet");
|
if (WinziPrintProc == null) throw new InvalidOperationException("The WinziPrint process has not been initialized yet");
|
||||||
progress?.Report(0.0);
|
progress?.Report(0.0);
|
||||||
using var client = new TcpClient("127.0.0.1", 30983);
|
using var client = new TcpClient("127.0.0.1", 30983);
|
||||||
using var stream = client.GetStream();
|
using var stream = client.GetStream();
|
||||||
|
string cnxId;
|
||||||
|
using var reader = new StreamReader(stream);
|
||||||
|
var first = await reader.ReadLineAsync() ?? throw new IOException("Invalid response from WinziPrint");
|
||||||
|
if (first.StartsWith("id:")) {
|
||||||
|
cnxId = first[3..].Trim();
|
||||||
|
} else {
|
||||||
|
throw new IOException("Invalid response from WinziPrint");
|
||||||
|
}
|
||||||
await stream.WriteAsync(Utils.UTF8.GetBytes(
|
await stream.WriteAsync(Utils.UTF8.GetBytes(
|
||||||
"-e utf-8;-p;" + (doublePaged ? "-2;" : "") +
|
"-e utf-8;-p;" + (doublePaged ? "-2;" : "") +
|
||||||
$"{string.Join(';', htmlPath)};{pdfPath}" +
|
$"{string.Join(';', htmlPath)};{pdfPath}" +
|
||||||
"\r\n"));
|
"\r\n"));
|
||||||
using var reader = new StreamReader(stream);
|
bool cancelled = false;
|
||||||
while (true) {
|
while (true) {
|
||||||
|
if (!cancelled && (cancelToken?.IsCancellationRequested ?? false)) {
|
||||||
|
try {
|
||||||
|
using var cancelClient = new TcpClient("127.0.0.1", 30983);
|
||||||
|
using var cancelStream = cancelClient.GetStream();
|
||||||
|
using var cancelReader = new StreamReader(cancelStream);
|
||||||
|
await cancelReader.ReadLineAsync();
|
||||||
|
await cancelStream.WriteAsync(Utils.UTF8.GetBytes($"cancel;{cnxId}\r\n"));
|
||||||
|
} catch { }
|
||||||
|
cancelled = true;
|
||||||
|
}
|
||||||
var line = await reader.ReadLineAsync() ?? throw new IOException("Invalid response from WinziPrint");
|
var line = await reader.ReadLineAsync() ?? throw new IOException("Invalid response from WinziPrint");
|
||||||
if (line.StartsWith("error:")) {
|
if (line.StartsWith("error:")) {
|
||||||
throw new IOException($"WinziPrint: {line[6..].Trim()}");
|
var msg = line[6..].Trim();
|
||||||
|
if (msg == "aborted")
|
||||||
|
throw new OperationCanceledException("Dokumentenerzeugung abgebrochen!");
|
||||||
|
throw new IOException($"WinziPrint: {msg}");
|
||||||
} else if (line.StartsWith("progress:")) {
|
} else if (line.StartsWith("progress:")) {
|
||||||
var parts = line[9..].Trim().Split('/').Select(int.Parse).ToArray();
|
var parts = line[9..].Trim().Split('/').Select(int.Parse).ToArray();
|
||||||
progress?.Report(100.0 * parts[0] / parts[1]);
|
progress?.Report(100.0 * parts[0] / parts[1]);
|
||||||
|
|||||||
60
Elwig/Helpers/SerialPortWatcher.cs
Normal file
60
Elwig/Helpers/SerialPortWatcher.cs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO.Ports;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Management;
|
||||||
|
|
||||||
|
namespace Elwig.Helpers {
|
||||||
|
public sealed class SerialPortWatcher : IDisposable {
|
||||||
|
|
||||||
|
private readonly ManagementEventWatcher _deviceArrivalWatcher;
|
||||||
|
private readonly ManagementEventWatcher _deviceRemovalWatcher;
|
||||||
|
|
||||||
|
private string[] _knownPorts;
|
||||||
|
|
||||||
|
public event EventHandler<string>? SerialPortConnected;
|
||||||
|
public event EventHandler<string>? SerialPortDisconnected;
|
||||||
|
|
||||||
|
public SerialPortWatcher() {
|
||||||
|
_knownPorts = SerialPort.GetPortNames();
|
||||||
|
_deviceArrivalWatcher = new ManagementEventWatcher("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 2");
|
||||||
|
_deviceArrivalWatcher.EventArrived += OnDeviceArrived;
|
||||||
|
_deviceRemovalWatcher = new ManagementEventWatcher("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 3");
|
||||||
|
_deviceRemovalWatcher.EventArrived += OnDeviceRemoved;
|
||||||
|
_deviceArrivalWatcher.Start();
|
||||||
|
_deviceRemovalWatcher.Start();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDeviceArrived(object sender, EventArrivedEventArgs evt) {
|
||||||
|
App.MainDispatcher.Invoke(() => {
|
||||||
|
// "synchronized"
|
||||||
|
string[] currentPorts = SerialPort.GetPortNames();
|
||||||
|
var newPorts = currentPorts.Except(_knownPorts).ToArray();
|
||||||
|
foreach (var port in newPorts)
|
||||||
|
SerialPortConnected?.Invoke(this, port);
|
||||||
|
_knownPorts = currentPorts;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDeviceRemoved(object sender, EventArrivedEventArgs evt) {
|
||||||
|
App.MainDispatcher.Invoke(() => {
|
||||||
|
// "synchronized"
|
||||||
|
string[] currentPorts = SerialPort.GetPortNames();
|
||||||
|
var removedPorts = _knownPorts.Except(currentPorts).ToArray();
|
||||||
|
foreach (var port in removedPorts)
|
||||||
|
SerialPortDisconnected?.Invoke(this, port);
|
||||||
|
_knownPorts = currentPorts;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() {
|
||||||
|
try {
|
||||||
|
_deviceArrivalWatcher?.Stop();
|
||||||
|
_deviceRemovalWatcher?.Stop();
|
||||||
|
} finally {
|
||||||
|
_deviceArrivalWatcher?.Dispose();
|
||||||
|
_deviceRemovalWatcher?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,41 +1,43 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.IO.Ports;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using Elwig.Dialogs;
|
using Elwig.Dialogs;
|
||||||
using System.Text;
|
using Elwig.Documents;
|
||||||
using System.Numerics;
|
|
||||||
using Elwig.Models.Entities;
|
|
||||||
using Elwig.Helpers.Billing;
|
using Elwig.Helpers.Billing;
|
||||||
using System.Runtime.InteropServices;
|
using Elwig.Models;
|
||||||
using System.Net.Http;
|
using Elwig.Models.Entities;
|
||||||
using System.Text.Json.Nodes;
|
using LinqKit;
|
||||||
using System.IO;
|
|
||||||
using MailKit.Net.Smtp;
|
using MailKit.Net.Smtp;
|
||||||
using MailKit.Security;
|
using MailKit.Security;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System.Reflection;
|
|
||||||
using System.Collections;
|
|
||||||
using Elwig.Documents;
|
|
||||||
using MimeKit;
|
|
||||||
using System.Windows.Input;
|
|
||||||
using LinqKit;
|
|
||||||
using System.Linq.Expressions;
|
|
||||||
using Elwig.Models;
|
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
|
using MimeKit;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Ports;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Nodes;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
using System.Windows.Markup;
|
using System.Windows.Markup;
|
||||||
|
|
||||||
namespace Elwig.Helpers {
|
namespace Elwig.Helpers {
|
||||||
public static partial class Utils {
|
public static partial class Utils {
|
||||||
|
|
||||||
public static readonly Encoding UTF8 = new UTF8Encoding(false, true);
|
public static readonly Encoding UTF8 = new UTF8Encoding(false, true);
|
||||||
|
public static readonly Encoding UTF8BOM = new UTF8Encoding(true, true);
|
||||||
|
public static readonly JsonSerializerOptions JsonOpts = new() { Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping };
|
||||||
|
|
||||||
public static int CurrentYear => DateTime.Now.Year;
|
public static int CurrentYear => DateTime.Now.Year;
|
||||||
public static int CurrentNextSeason => DateTime.Now.Year - (DateTime.Now.Month <= 3 ? 1 : 0);
|
public static int CurrentNextSeason => DateTime.Now.Year - (DateTime.Now.Month <= 3 ? 1 : 0);
|
||||||
@@ -430,12 +432,13 @@ namespace Elwig.Helpers {
|
|||||||
var client = new HttpClient() {
|
var client = new HttpClient() {
|
||||||
Timeout = TimeSpan.FromSeconds(5),
|
Timeout = TimeSpan.FromSeconds(5),
|
||||||
};
|
};
|
||||||
|
client.DefaultRequestHeaders.UserAgent.Clear();
|
||||||
|
client.DefaultRequestHeaders.UserAgent.ParseAdd($"Elwig/{App.Version} ({App.Client.NameToken}, {App.BranchName}, {Environment.MachineName}, {Environment.OSVersion})");
|
||||||
client.DefaultRequestHeaders.Accept.Clear();
|
client.DefaultRequestHeaders.Accept.Clear();
|
||||||
if (accept != null)
|
if (accept != null)
|
||||||
client.DefaultRequestHeaders.Accept.Add(new(accept));
|
client.DefaultRequestHeaders.Accept.Add(new(accept));
|
||||||
if (username != null || password != null)
|
if (username != null || password != null)
|
||||||
client.DefaultRequestHeaders.Authorization = new("Basic", Convert.ToBase64String(
|
client.DefaultRequestHeaders.Authorization = new("Basic", Convert.ToBase64String(Utils.UTF8.GetBytes($"{username}:{password}")));
|
||||||
Utils.UTF8.GetBytes($"{username}:{password}")));
|
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -454,15 +457,19 @@ namespace Elwig.Helpers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static async Task UploadExportData(string zip, string url, string username, string password) {
|
public static async Task UploadExportData(string zip, string url, string username, string password) {
|
||||||
|
if (url.StartsWith("https://elwig.at/clients/"))
|
||||||
|
url = "https://sync.elwig.at/" + url[25..];
|
||||||
if (!url.EndsWith('/')) url += "/";
|
if (!url.EndsWith('/')) url += "/";
|
||||||
using var client = GetHttpClient(username, password, accept: "application/json");
|
using var client = GetHttpClient(username, password, accept: "application/json");
|
||||||
var content = new StreamContent(new FileStream(zip, FileMode.Open, FileAccess.Read));
|
using var content = new StreamContent(new FileStream(zip, FileMode.Open, FileAccess.Read));
|
||||||
content.Headers.ContentType = new("application/zip");
|
content.Headers.ContentType = new("application/zip");
|
||||||
using var res = await client.PutAsync(url + Path.GetFileName(zip), content);
|
using var res = await client.PutAsync(url + Path.GetFileName(zip), content);
|
||||||
res.EnsureSuccessStatusCode();
|
res.EnsureSuccessStatusCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<JsonArray> GetExportMetaData(string url, string username, string password) {
|
public static async Task<JsonArray> GetExportMetaData(string url, string username, string password) {
|
||||||
|
if (url.StartsWith("https://elwig.at/clients/"))
|
||||||
|
url = "https://sync.elwig.at/" + url[25..];
|
||||||
using var client = GetHttpClient(username, password, accept: "application/json");
|
using var client = GetHttpClient(username, password, accept: "application/json");
|
||||||
using var res = await client.GetAsync(url);
|
using var res = await client.GetAsync(url);
|
||||||
res.EnsureSuccessStatusCode();
|
res.EnsureSuccessStatusCode();
|
||||||
@@ -495,48 +502,67 @@ namespace Elwig.Helpers {
|
|||||||
public static async Task<bool> SendEmail(Member member, string subject, string text, IEnumerable<Document> docs) {
|
public static async Task<bool> SendEmail(Member member, string subject, string text, IEnumerable<Document> docs) {
|
||||||
if (App.Config.Smtp == null)
|
if (App.Config.Smtp == null)
|
||||||
return false;
|
return false;
|
||||||
|
return await Task.Run(async () => {
|
||||||
|
await AddSentMailBody(subject, text, 1);
|
||||||
|
SmtpClient? client = null;
|
||||||
|
try {
|
||||||
|
client = await GetSmtpClient();
|
||||||
|
|
||||||
SmtpClient? client = null;
|
using var msg = new MimeMessage();
|
||||||
try {
|
msg.From.Add(new MailboxAddress(App.Client.NameFull, App.Config.Smtp.Value.From));
|
||||||
Mouse.OverrideCursor = Cursors.AppStarting;
|
msg.To.AddRange(member.EmailAddresses.OrderBy(a => a.Nr).Select(a => new MailboxAddress(member.AdministrativeName, a.Address)));
|
||||||
client = await GetSmtpClient();
|
msg.Subject = subject;
|
||||||
|
var body = new Multipart("mixed") {
|
||||||
using var msg = new MimeMessage();
|
new TextPart("plain") { Text = text }
|
||||||
msg.From.Add(new MailboxAddress(App.Client.NameFull, App.Config.Smtp.Value.From));
|
};
|
||||||
msg.To.AddRange(member.EmailAddresses.OrderBy(a => a.Nr).Select(a => new MailboxAddress(member.AdministrativeName, a.Address)));
|
foreach (var doc in docs) {
|
||||||
msg.Subject = subject;
|
var name = NormalizeFileName(doc.Title);
|
||||||
var body = new Multipart("mixed") {
|
body.Add(doc.AsEmailAttachment($"{name}.pdf"));
|
||||||
new TextPart("plain") { Text = text }
|
}
|
||||||
};
|
msg.Body = body;
|
||||||
foreach (var doc in docs) {
|
await client!.SendAsync(msg);
|
||||||
var name = NormalizeFileName(doc.Title);
|
await AddSentMails([(
|
||||||
body.Add(doc.AsEmailAttachment($"{name}.pdf"));
|
"email", member.MgNr, member.AdministrativeName,
|
||||||
|
member.EmailAddresses.OrderBy(a => a.Nr).Select(a => a.Address).ToArray(),
|
||||||
|
subject, docs.Select(d => d.Title).ToArray()
|
||||||
|
)]);
|
||||||
|
} catch (Exception exc) {
|
||||||
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
if (client != null)
|
||||||
|
await client.DisconnectAsync(true);
|
||||||
|
client?.Dispose();
|
||||||
}
|
}
|
||||||
msg.Body = body;
|
return true;
|
||||||
await client!.SendAsync(msg);
|
});
|
||||||
} catch (Exception exc) {
|
|
||||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
return false;
|
|
||||||
} finally {
|
|
||||||
if (client != null)
|
|
||||||
await client.DisconnectAsync(true);
|
|
||||||
client?.Dispose();
|
|
||||||
Mouse.OverrideCursor = null;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task ExportDocument(Document doc, ExportMode mode, string? filename = null, (Member, string, string)? emailData = null) {
|
public static async Task ExportDocument(Document doc, ExportMode mode, string? filename = null, (Member Member, string Subject, string Text)? emailData = null) {
|
||||||
if (mode == ExportMode.Print && !App.Config.Debug) {
|
if (mode == ExportMode.Print && !App.Config.Debug) {
|
||||||
|
if (doc.IsPreview) {
|
||||||
|
MessageBox.Show("Dieses Dokument ist als vorläufig markiert und kann daher nicht ausgedruckt werden!",
|
||||||
|
"Vorläufiges Dokument", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
await doc.Generate();
|
await doc.Generate();
|
||||||
await doc.Print();
|
await doc.Print();
|
||||||
} else if (mode == ExportMode.Email && emailData is (Member, string, string) e) {
|
} else if (mode == ExportMode.Email && emailData is (Member, string, string) e) {
|
||||||
|
if (doc.IsPreview) {
|
||||||
|
MessageBox.Show("Dieses Dokument ist als vorläufig markiert und kann daher nicht verschickt werden!",
|
||||||
|
"Vorläufiges Dokument", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
await doc.Generate();
|
await doc.Generate();
|
||||||
var success = await SendEmail(e.Item1, e.Item2, e.Item3, [doc]);
|
var success = await SendEmail(e.Member, e.Subject, e.Text, [doc]);
|
||||||
if (success)
|
if (success)
|
||||||
MessageBox.Show("Die E-Mail wurde erfolgreich verschickt!", "E-Mail verschickt",
|
MessageBox.Show("Die E-Mail wurde erfolgreich verschickt!\n\nEs kann einige Minuten dauern, bis die E-Mail im Posteingang des Empfängers aufscheint.", "E-Mail verschickt",
|
||||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
} else if (mode == ExportMode.SavePdf) {
|
} else if (mode == ExportMode.SavePdf) {
|
||||||
|
if (doc.IsPreview) {
|
||||||
|
MessageBox.Show("Dieses Dokument ist als vorläufig markiert und sollte daher nicht langfristig gespeichert werden!",
|
||||||
|
"Vorläufiges Dokument", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||||
|
}
|
||||||
var d = new SaveFileDialog() {
|
var d = new SaveFileDialog() {
|
||||||
FileName = $"{NormalizeFileName(filename ?? doc.Title)}.pdf",
|
FileName = $"{NormalizeFileName(filename ?? doc.Title)}.pdf",
|
||||||
DefaultExt = "pdf",
|
DefaultExt = "pdf",
|
||||||
@@ -559,9 +585,7 @@ namespace Elwig.Helpers {
|
|||||||
Log = "Application",
|
Log = "Application",
|
||||||
Source = ".NET Runtime",
|
Source = ".NET Runtime",
|
||||||
};
|
};
|
||||||
return log.Entries.Cast<EventLogEntry>()
|
return [.. log.Entries.OfType<EventLogEntry>().Where(e => e.InstanceId == 1026).Where(e => e.Message.StartsWith("Application: Elwig.exe"))];
|
||||||
.Where(e => e.Message.StartsWith("Application: Elwig.exe"))
|
|
||||||
.ToList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int GetEntityIdetifierForPk(params object?[] primaryKey) {
|
public static int GetEntityIdetifierForPk(params object?[] primaryKey) {
|
||||||
@@ -651,9 +675,9 @@ namespace Elwig.Helpers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<string?> FindSentMailBody(DateTime target) {
|
public static async Task<string?> FindSentMailBody(DateTime target) {
|
||||||
var dt = $"{target:yyyy-MM-dd_HH-mm-ss}_";
|
var dt = $"{target:yyyy-MM-dd_HH-mm-ss}";
|
||||||
var filename = Directory.GetFiles(App.MailsPath, "????-??-??_??-??-??_*.txt")
|
var filename = Directory.GetFiles(App.MailsPath, "????-??-??_??-??-??_*.txt")
|
||||||
.Where(n => Path.GetFileName(n).CompareTo(dt) <= 0)
|
.Where(n => Path.GetFileName(n)[..19].CompareTo(dt) <= 0)
|
||||||
.Order()
|
.Order()
|
||||||
.LastOrDefault();
|
.LastOrDefault();
|
||||||
if (filename == null)
|
if (filename == null)
|
||||||
|
|||||||
@@ -628,5 +628,55 @@ namespace Elwig.Helpers {
|
|||||||
|
|
||||||
return new(true, null);
|
return new(true, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ValidationResult CheckOrganicAuthorityCode(TextBox input, bool required) {
|
||||||
|
string text = "";
|
||||||
|
int pos = input.CaretIndex;
|
||||||
|
for (int i = 0, v = 0; i < input.Text.Length; i++) {
|
||||||
|
char ch = char.ToUpper(input.Text[i]);
|
||||||
|
if (v < 2 && char.IsAsciiLetter(ch)) {
|
||||||
|
v++;
|
||||||
|
text += ch;
|
||||||
|
} else if ((v == 2 || v == 6) && ch == '-') {
|
||||||
|
v++;
|
||||||
|
text += ch;
|
||||||
|
} else if (v >= 2 && char.IsLetterOrDigit(ch)) {
|
||||||
|
if (text.StartsWith("AT")) {
|
||||||
|
if (v == 3 && ch == 'B' || v == 4 && ch == 'I' || v == 5 && ch == 'O') {
|
||||||
|
v++;
|
||||||
|
text += ch;
|
||||||
|
} else if (v > 6 && char.IsAsciiDigit(ch)) {
|
||||||
|
v++;
|
||||||
|
text += ch;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
v++;
|
||||||
|
text += ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == input.CaretIndex - 1) {
|
||||||
|
pos = text.Length;
|
||||||
|
} else if (text.StartsWith("AT") && v >= 10) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input.Text = text;
|
||||||
|
input.CaretIndex = pos;
|
||||||
|
|
||||||
|
if (text.Length == 0)
|
||||||
|
return required ? new(false, "Bio-Kontrollstellen-Code ist nicht optional") : new(true, null);
|
||||||
|
|
||||||
|
if (text.StartsWith("AT")) {
|
||||||
|
if (text.Length != 10) {
|
||||||
|
return new(false, "Bio-Kontrollstellen-Code ist ungültig");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return new(false, "Not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new(true, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,15 +15,17 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
public bool IsReady { get; private set; }
|
public bool IsReady { get; private set; }
|
||||||
public bool HasFillingClearance { get; private set; }
|
public bool HasFillingClearance { get; private set; }
|
||||||
|
|
||||||
public event IEventScale.EventHandler<WeighingEventArgs> WeighingEvent;
|
public event IEventScale.EventHandler<WeighingEventArgs>? WeighingEvent;
|
||||||
|
|
||||||
private bool IsRunning = true;
|
private bool IsRunning = true;
|
||||||
private readonly Thread BackgroundThread;
|
private readonly Thread BackgroundThread;
|
||||||
|
private readonly string Connection;
|
||||||
|
|
||||||
public AveryEventScale(string id, string model, string cnx, string? empty = null, string? filling = null, int? limit = null, string? log = null) :
|
public AveryEventScale(string id, string model, string cnx, string? log = null, bool required = true) :
|
||||||
base(cnx, empty, filling, limit, log) {
|
base(cnx, null, null, null, log, true, !required) {
|
||||||
ScaleId = id;
|
ScaleId = id;
|
||||||
Model = model;
|
Model = model;
|
||||||
|
Connection = cnx;
|
||||||
IsReady = true;
|
IsReady = true;
|
||||||
HasFillingClearance = false;
|
HasFillingClearance = false;
|
||||||
Stream.WriteTimeout = -1;
|
Stream.WriteTimeout = -1;
|
||||||
@@ -50,19 +52,49 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
var data = await Receive();
|
var data = await Receive();
|
||||||
if (data != null)
|
if (data != null)
|
||||||
RaiseWeighingEvent(new WeighingEventArgs(data.Value));
|
RaiseWeighingEvent(new WeighingEventArgs(data.Value));
|
||||||
|
} catch (ThreadInterruptedException) {
|
||||||
|
// ignore
|
||||||
|
} catch (IOException) {
|
||||||
|
await Task.Delay(500);
|
||||||
|
await Reconnect();
|
||||||
|
} catch (TimeoutException) {
|
||||||
|
await Task.Delay(500);
|
||||||
|
await Reconnect();
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
MessageBox.Show($"Beim Wiegen ist ein Fehler Aufgetreten:\n\n{ex.Message}", "Waagenfehler",
|
MessageBox.Show($"Beim Wiegen ist ein Fehler Aufgetreten:\n\n{ex.Message} ({ex.GetType().Name})", "Waagenfehler",
|
||||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected async Task Reconnect() {
|
||||||
|
try { Reader.Close(); } catch { }
|
||||||
|
try { Stream.Close(); } catch { }
|
||||||
|
try { Serial?.Close(); } catch { }
|
||||||
|
while (IsRunning) {
|
||||||
|
try {
|
||||||
|
if (Connection.StartsWith("serial:")) {
|
||||||
|
Serial = Utils.OpenSerialConnection(Connection);
|
||||||
|
Stream = Serial.BaseStream;
|
||||||
|
} else if (Connection.StartsWith("tcp:")) {
|
||||||
|
Tcp = Utils.OpenTcpConnection(Connection);
|
||||||
|
Stream = Tcp.GetStream();
|
||||||
|
}
|
||||||
|
Reader = new(Stream, Encoding.ASCII, false, 512);
|
||||||
|
break;
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
await Task.Delay(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected async Task<WeighingResult?> Receive() {
|
protected async Task<WeighingResult?> Receive() {
|
||||||
var line = "";
|
var line = "";
|
||||||
while (line.Length < 33) {
|
while (line.Length < 33) {
|
||||||
var ch = Reader.Read();
|
var ch = Reader.Read();
|
||||||
if (ch == -1) {
|
if (ch == -1) {
|
||||||
return null;
|
throw new IOException("Connection closed");
|
||||||
} else if (line.Length > 0 || ch == ' ') {
|
} else if (line.Length > 0 || ch == ' ') {
|
||||||
line += char.ToString((char)ch);
|
line += char.ToString((char)ch);
|
||||||
}
|
}
|
||||||
@@ -71,7 +103,7 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
if (line == null || line == "") {
|
if (line == null || line == "") {
|
||||||
return null;
|
return null;
|
||||||
} else if (line.Length != 33 || line[0] != ' ' || line[9] != ' ' || line[15] != ' ' || line[20] != ' ' || line[32] != ' ') {
|
} else if (line.Length != 33 || line[0] != ' ' || line[9] != ' ' || line[15] != ' ' || line[20] != ' ' || line[32] != ' ') {
|
||||||
throw new IOException($"Invalid event from scale: '{line}'");
|
throw new FormatException($"Invalid event from scale: '{line}'");
|
||||||
}
|
}
|
||||||
|
|
||||||
var date = line[ 1.. 9];
|
var date = line[ 1.. 9];
|
||||||
@@ -81,7 +113,7 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
var unit = line[30..32];
|
var unit = line[30..32];
|
||||||
|
|
||||||
if (unit != "kg") {
|
if (unit != "kg") {
|
||||||
throw new IOException($"Unsupported unit in weighing event: '{unit}'");
|
throw new WeighingException($"Unsupported unit in weighing event: '{unit}'");
|
||||||
}
|
}
|
||||||
|
|
||||||
identNr = identNr.Length > 0 && identNr != "0" ? identNr : null;
|
identNr = identNr.Length > 0 && identNr != "0" ? identNr : null;
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
var line = await Reader.ReadUntilAsync('\x03');
|
var line = await Reader.ReadUntilAsync('\x03');
|
||||||
if (LogPath != null) await File.AppendAllTextAsync(LogPath, $"{line}\r\n");
|
if (LogPath != null) await File.AppendAllTextAsync(LogPath, $"{line}\r\n");
|
||||||
if (line == null || line.Length < 4 || !line.StartsWith('\x02')) {
|
if (line == null || line.Length < 4 || !line.StartsWith('\x02')) {
|
||||||
throw new IOException("Invalid response from scale");
|
throw new FormatException("Invalid response from scale");
|
||||||
}
|
}
|
||||||
|
|
||||||
var status = line[1..3];
|
var status = line[1..3];
|
||||||
@@ -45,9 +45,9 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
switch (status[1]) {
|
switch (status[1]) {
|
||||||
case 'M': msg = "Waage in Bewegung"; break;
|
case 'M': msg = "Waage in Bewegung"; break;
|
||||||
}
|
}
|
||||||
throw new IOException($"Waagenfehler {status}: {msg}");
|
throw new WeighingException($"Waagenfehler {status}: {msg}");
|
||||||
} else if (status[0] != ' ') {
|
} else if (status[0] != ' ') {
|
||||||
throw new IOException($"Invalid response from scale (error code {status})");
|
throw new WeighingException($"Invalid response from scale (error code {status})");
|
||||||
}
|
}
|
||||||
|
|
||||||
return line[1..^1];
|
return line[1..^1];
|
||||||
@@ -57,7 +57,7 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
await SendCommand(incIdentNr ? '\x05' : '?');
|
await SendCommand(incIdentNr ? '\x05' : '?');
|
||||||
string record = await ReceiveResponse();
|
string record = await ReceiveResponse();
|
||||||
if (record.Length != 45)
|
if (record.Length != 45)
|
||||||
throw new IOException("Invalid response from scale: Received record has invalid size");
|
throw new FormatException("Invalid response from scale: Received record has invalid size");
|
||||||
var line = record[2..];
|
var line = record[2..];
|
||||||
|
|
||||||
var brutto = line[ 0.. 7].Trim();
|
var brutto = line[ 0.. 7].Trim();
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
using System.IO.Ports;
|
|
||||||
using System.IO;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Ports;
|
||||||
|
using System.Net.Sockets;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
namespace Elwig.Helpers.Weighing {
|
namespace Elwig.Helpers.Weighing {
|
||||||
public abstract class Scale : IDisposable {
|
public abstract class Scale : IDisposable {
|
||||||
@@ -27,7 +28,7 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
if (config.Type == "SysTec-IT") {
|
if (config.Type == "SysTec-IT") {
|
||||||
return new SysTecITScale(config.Id, config.Model!, config.Connection!, config.Empty, config.Filling, limit, config.Log);
|
return new SysTecITScale(config.Id, config.Model!, config.Connection!, config.Empty, config.Filling, limit, config.Log);
|
||||||
} else if (config.Type == "Avery-Async") {
|
} else if (config.Type == "Avery-Async") {
|
||||||
return new AveryEventScale(config.Id, config.Model!, config.Connection!, config.Empty, config.Filling, limit, config.Log);
|
return new AveryEventScale(config.Id, config.Model!, config.Connection!, config.Log, config.Required);
|
||||||
} else if (config.Type == "Gassner") {
|
} else if (config.Type == "Gassner") {
|
||||||
return new GassnerScale(config.Id, config.Model!, config.Connection!, config.Empty, config.Filling, limit, config.Log);
|
return new GassnerScale(config.Id, config.Model!, config.Connection!, config.Empty, config.Filling, limit, config.Log);
|
||||||
} else {
|
} else {
|
||||||
@@ -35,10 +36,17 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Scale(string cnx, string? empty, string? filling, int? limit, string? log) {
|
protected Scale(string cnx, string? empty, string? filling, int? limit, string? log, bool softFail = false, bool failSilent = false) {
|
||||||
if (cnx.StartsWith("serial:")) {
|
if (cnx.StartsWith("serial:")) {
|
||||||
Serial = Utils.OpenSerialConnection(cnx);
|
try {
|
||||||
Stream = Serial.BaseStream;
|
Serial = Utils.OpenSerialConnection(cnx);
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (!softFail) throw;
|
||||||
|
if (!failSilent)
|
||||||
|
MessageBox.Show($"Verbindung zu Waage konnte nicht hergestellt werden:\n\n{e.Message}", "Waagenfehler",
|
||||||
|
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||||
|
}
|
||||||
|
Stream = Serial?.BaseStream ?? Stream.Null;
|
||||||
} else if (cnx.StartsWith("tcp:")) {
|
} else if (cnx.StartsWith("tcp:")) {
|
||||||
Tcp = Utils.OpenTcpConnection(cnx);
|
Tcp = Utils.OpenTcpConnection(cnx);
|
||||||
Stream = Tcp.GetStream();
|
Stream = Tcp.GetStream();
|
||||||
|
|||||||
@@ -34,14 +34,14 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
var line = await Reader.ReadUntilAsync("\r\n");
|
var line = await Reader.ReadUntilAsync("\r\n");
|
||||||
if (LogPath != null) await File.AppendAllTextAsync(LogPath, line);
|
if (LogPath != null) await File.AppendAllTextAsync(LogPath, line);
|
||||||
if (line == null || line.Length < 4 || !line.StartsWith('<') || !line.EndsWith(">\r\n")) {
|
if (line == null || line.Length < 4 || !line.StartsWith('<') || !line.EndsWith(">\r\n")) {
|
||||||
throw new IOException("Invalid response from scale");
|
throw new FormatException("Invalid response from scale");
|
||||||
}
|
}
|
||||||
|
|
||||||
var error = line[1..3];
|
var error = line[1..3];
|
||||||
string msg = $"Unbekannter Fehler (Fehler code {error})";
|
string msg = $"Unbekannter Fehler (Fehler code {error})";
|
||||||
if (error[0] == '0') {
|
if (error[0] == '0') {
|
||||||
if (error[1] != '0') {
|
if (error[1] != '0') {
|
||||||
throw new IOException($"Invalid response from scale (error code {error})");
|
throw new WeighingException($"Invalid response from scale (error code {error})");
|
||||||
}
|
}
|
||||||
} else if (error[0] == '1') {
|
} else if (error[0] == '1') {
|
||||||
switch (error[1]) {
|
switch (error[1]) {
|
||||||
@@ -52,21 +52,21 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
case '6': msg = "Drucker nicht bereit"; break;
|
case '6': msg = "Drucker nicht bereit"; break;
|
||||||
case '7': msg = "Druckmuster enthält ungültiges Kommando"; break;
|
case '7': msg = "Druckmuster enthält ungültiges Kommando"; break;
|
||||||
}
|
}
|
||||||
throw new IOException($"Waagenfehler {error}: {msg}");
|
throw new WeighingException($"Waagenfehler {error}: {msg}");
|
||||||
} else if (error[0] == '2') {
|
} else if (error[0] == '2') {
|
||||||
switch (error[1]) {
|
switch (error[1]) {
|
||||||
case '0': msg = "Brutto negativ"; break;
|
case '0': msg = "Brutto negativ"; break;
|
||||||
}
|
}
|
||||||
throw new IOException($"Fehler {error}: {msg}");
|
throw new WeighingException($"Fehler {error}: {msg}");
|
||||||
} else if (error[0] == '3') {
|
} else if (error[0] == '3') {
|
||||||
switch (error[1]) {
|
switch (error[1]) {
|
||||||
case '1': msg = "Übertragunsfehler"; break;
|
case '1': msg = "Übertragunsfehler"; break;
|
||||||
case '2': msg = "Ungültiger Befehl"; break;
|
case '2': msg = "Ungültiger Befehl"; break;
|
||||||
case '3': msg = "Ungültiger Parameter"; break;
|
case '3': msg = "Ungültiger Parameter"; break;
|
||||||
}
|
}
|
||||||
throw new IOException($"Kommunikationsfehler {error}: {msg}");
|
throw new WeighingException($"Kommunikationsfehler {error}: {msg}");
|
||||||
} else {
|
} else {
|
||||||
throw new IOException($"Invalid response from scale (error code {error})");
|
throw new WeighingException($"Invalid response from scale (error code {error})");
|
||||||
}
|
}
|
||||||
|
|
||||||
return line[1..^3];
|
return line[1..^3];
|
||||||
@@ -76,7 +76,7 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
await SendCommand(incIdentNr ? $"RN{InternalScaleNr}" : $"RM{InternalScaleNr}");
|
await SendCommand(incIdentNr ? $"RN{InternalScaleNr}" : $"RM{InternalScaleNr}");
|
||||||
string record = await ReceiveResponse();
|
string record = await ReceiveResponse();
|
||||||
if (record.Length != 62)
|
if (record.Length != 62)
|
||||||
throw new IOException("Invalid response from scale: Received record has invalid size");
|
throw new FormatException("Invalid response from scale: Received record has invalid size");
|
||||||
var line = record[2..];
|
var line = record[2..];
|
||||||
|
|
||||||
var status = line[ 0.. 2];
|
var status = line[ 0.. 2];
|
||||||
@@ -94,9 +94,9 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
var crc16 = line[52..60].Trim();
|
var crc16 = line[52..60].Trim();
|
||||||
|
|
||||||
if (Utils.CalcCrc16Modbus(record[..54]) != ushort.Parse(crc16)) {
|
if (Utils.CalcCrc16Modbus(record[..54]) != ushort.Parse(crc16)) {
|
||||||
throw new IOException($"Invalid response from scale: Invalid CRC16 checksum ({crc16} != {Utils.CalcCrc16Modbus(record[..54])})");
|
throw new WeighingException($"Invalid response from scale: Invalid CRC16 checksum ({crc16} != {Utils.CalcCrc16Modbus(record[..54])})");
|
||||||
} else if (unit != "kg") {
|
} else if (unit != "kg") {
|
||||||
throw new IOException($"Unsupported unit in weighing response: '{unit}'");
|
throw new WeighingException($"Unsupported unit in weighing response: '{unit}'");
|
||||||
}
|
}
|
||||||
|
|
||||||
identNr = identNr.Length > 0 && identNr != "0" ? identNr : null;
|
identNr = identNr.Length > 0 && identNr != "0" ? identNr : null;
|
||||||
|
|||||||
6
Elwig/Helpers/Weighing/WeighingException.cs
Normal file
6
Elwig/Helpers/Weighing/WeighingException.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Elwig.Helpers.Weighing {
|
||||||
|
public class WeighingException(string? message = null) : Exception(message) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,14 +27,14 @@ namespace Elwig.Models.Dtos {
|
|||||||
MgNr = mgnr;
|
MgNr = mgnr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<IDictionary<int, CreditNoteDeliveryData>> ForPaymentVariant(DbSet<CreditNoteDeliveryRowSingle> table, DbSet<Season> seasons, int year, int avnr) {
|
public static async Task<IDictionary<int, CreditNoteDeliveryData>> ForPaymentVariant(DbSet<CreditNoteDeliveryRowSingle> table, DbSet<PaymentVar> paymentVariants, int year, int avnr) {
|
||||||
var variant = (await seasons.FindAsync(year))?.PaymentVariants.Where(v => v.AvNr == avnr).SingleOrDefault();
|
var variant = await paymentVariants.FindAsync(year, avnr);
|
||||||
BillingData? varData = null;
|
BillingData? varData = null;
|
||||||
try { varData = variant != null ? BillingData.FromJson(variant.Data) : null; } catch { }
|
try { varData = variant != null ? BillingData.FromJson(variant.Data) : null; } catch { }
|
||||||
return (await FromDbSet(table, year, avnr))
|
return (await FromDbSet(table, year, avnr))
|
||||||
.GroupBy(
|
.GroupBy(
|
||||||
r => new { r.Year, r.AvNr, r.MgNr, r.TgNr, r.DId, r.DPNr },
|
r => new { r.Year, r.AvNr, r.MgNr, r.TgNr, r.DId, r.DPNr },
|
||||||
(k, g) => new CreditNoteDeliveryRow(g, seasons, varData?.NetWeightModifier ?? 0.0, varData?.GrossWeightModifier ?? 0.0))
|
(k, g) => new CreditNoteDeliveryRow(g, variant, varData))
|
||||||
.GroupBy(
|
.GroupBy(
|
||||||
r => new { r.Year, r.AvNr, r.MgNr, r.TgNr },
|
r => new { r.Year, r.AvNr, r.MgNr, r.TgNr },
|
||||||
(k, g) => new CreditNoteDeliveryData(g, k.Year, k.TgNr, mgnr: k.MgNr))
|
(k, g) => new CreditNoteDeliveryData(g, k.Year, k.TgNr, mgnr: k.MgNr))
|
||||||
@@ -86,13 +86,13 @@ namespace Elwig.Models.Dtos {
|
|||||||
public decimal? Amount;
|
public decimal? Amount;
|
||||||
public double WeighingModifier;
|
public double WeighingModifier;
|
||||||
|
|
||||||
public CreditNoteDeliveryRow(IEnumerable<CreditNoteDeliveryRowSingle> rows, DbSet<Season> seasons, double netWeightModifier, double grossWeightModifier) {
|
public CreditNoteDeliveryRow(IEnumerable<CreditNoteDeliveryRowSingle> rows, PaymentVar paymentVariant, BillingData? varData) {
|
||||||
var f = rows.First();
|
var f = rows.First();
|
||||||
Year = f.Year;
|
Year = f.Year;
|
||||||
TgNr = f.TgNr;
|
TgNr = f.TgNr;
|
||||||
AvNr = f.AvNr;
|
AvNr = f.AvNr;
|
||||||
MgNr = f.MgNr;
|
MgNr = f.MgNr;
|
||||||
var season = seasons.Find(Year);
|
var season = paymentVariant.Season;
|
||||||
|
|
||||||
LsNr = f.LsNr;
|
LsNr = f.LsNr;
|
||||||
DPNr = f.DPNr;
|
DPNr = f.DPNr;
|
||||||
@@ -115,7 +115,7 @@ namespace Elwig.Models.Dtos {
|
|||||||
b.Price != null ? season?.DecFromDb((long)b.Price) : null,
|
b.Price != null ? season?.DecFromDb((long)b.Price) : null,
|
||||||
b.Amount != null ? season?.DecFromDb((long)b.Amount) : null))
|
b.Amount != null ? season?.DecFromDb((long)b.Amount) : null))
|
||||||
.ToArray();
|
.ToArray();
|
||||||
WeighingModifier = f.NetWeight ? netWeightModifier : grossWeightModifier;
|
WeighingModifier = (varData == null || !varData.ConsiderDelieryModifiers) ? 0 : f.NetWeight ? varData.NetWeightModifier : varData.GrossWeightModifier;
|
||||||
Amount = f.TotalAmount != null ? season?.DecFromDb((long)f.TotalAmount) : null;
|
Amount = f.TotalAmount != null ? season?.DecFromDb((long)f.TotalAmount) : null;
|
||||||
var netAmount = f.NetAmount != null ? season?.DecFromDb((long)f.NetAmount) : null;
|
var netAmount = f.NetAmount != null ? season?.DecFromDb((long)f.NetAmount) : null;
|
||||||
var amt = netAmount * (decimal)(1.0 + WeighingModifier);
|
var amt = netAmount * (decimal)(1.0 + WeighingModifier);
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ namespace Elwig.Models.Dtos {
|
|||||||
("Name2", "Vorname", null, 40),
|
("Name2", "Vorname", null, 40),
|
||||||
("DefaultKg", "Ort", null, 40),
|
("DefaultKg", "Ort", null, 40),
|
||||||
("SortId", "Sorte", null, 10),
|
("SortId", "Sorte", null, 10),
|
||||||
("Weight", "Gewicht", "kg", 20),
|
("Weight", "Menge", "kg", 20),
|
||||||
("CreatedTimestamp", "Angemeldet", null, 35),
|
("CreatedAt", "Angemeldet", null, 35),
|
||||||
("ModifiedTimestamp", "Geändert", null, 35),
|
("ModifiedAt", "Geändert", null, 35),
|
||||||
("Status", "Status", null, 20),
|
("Status", "Status", null, 20),
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -47,8 +47,8 @@ namespace Elwig.Models.Dtos {
|
|||||||
public string? DefaultKg;
|
public string? DefaultKg;
|
||||||
public string SortId;
|
public string SortId;
|
||||||
public string Variety;
|
public string Variety;
|
||||||
public DateTime CreatedTimestamp;
|
public DateTime CreatedAt;
|
||||||
public DateTime? ModifiedTimestamp;
|
public DateTime? ModifiedAt;
|
||||||
public int Weight;
|
public int Weight;
|
||||||
public string? Status;
|
public string? Status;
|
||||||
|
|
||||||
@@ -65,10 +65,10 @@ namespace Elwig.Models.Dtos {
|
|||||||
DefaultKg = m.DefaultKg?.Name;
|
DefaultKg = m.DefaultKg?.Name;
|
||||||
SortId = a.SortId;
|
SortId = a.SortId;
|
||||||
Variety = a.Variety.Name;
|
Variety = a.Variety.Name;
|
||||||
CreatedTimestamp = a.CreatedTimestamp;
|
CreatedAt = a.CreatedAt;
|
||||||
ModifiedTimestamp = a.ModifiedTimestamp == a.CreatedTimestamp ? null : a.ModifiedTimestamp;
|
ModifiedAt = a.ModifiedAt == a.CreatedAt ? null : a.ModifiedAt;
|
||||||
Weight = a.Weight;
|
Weight = a.Weight;
|
||||||
Status = s.AncmtTo == null ? null : a.CreatedTimestamp >= s.AncmtTo ? "verspät." : "ok";
|
Status = s.AncmtTo == null ? null : a.CreatedAt >= s.AncmtTo ? "verspät." : "ok";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ namespace Elwig.Models.Dtos {
|
|||||||
("QualityLevel", "Qualitätsstufe", null, 25),
|
("QualityLevel", "Qualitätsstufe", null, 25),
|
||||||
("Gradation", "Gradation", "°Oe|°KMW", 32),
|
("Gradation", "Gradation", "°Oe|°KMW", 32),
|
||||||
("Buckets", "Flächenbindung", "|kg", 36),
|
("Buckets", "Flächenbindung", "|kg", 36),
|
||||||
("Weight", "Gewicht", "kg", 16),
|
("Weight", "Menge", "kg", 16),
|
||||||
];
|
];
|
||||||
|
|
||||||
private readonly int MgNr;
|
private readonly int MgNr;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace Elwig.Models.Dtos {
|
|||||||
("CultId", "Bewirt.", null, 15),
|
("CultId", "Bewirt.", null, 15),
|
||||||
("QualId", "Qualität", null, 15),
|
("QualId", "Qualität", null, 15),
|
||||||
("Gradation", "Gradation", "°Oe|°KMW", 40),
|
("Gradation", "Gradation", "°Oe|°KMW", 40),
|
||||||
("Weight", "Gewicht", "kg", 20),
|
("Weight", "Menge", "kg", 20),
|
||||||
("IsNetWeight", "Gerebelt", null, 20),
|
("IsNetWeight", "Gerebelt", null, 20),
|
||||||
("HkId", "Herkunft", null, 20),
|
("HkId", "Herkunft", null, 20),
|
||||||
("Modifiers", "Zu-/Abschläge", null, 40),
|
("Modifiers", "Zu-/Abschläge", null, 40),
|
||||||
|
|||||||
59
Elwig/Models/Dtos/MemberDeliveryData.cs
Normal file
59
Elwig/Models/Dtos/MemberDeliveryData.cs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
using Elwig.Models.Entities;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Elwig.Models.Dtos {
|
||||||
|
public class MemberDeliveryData : DataTable<MemberDeliveryRow> {
|
||||||
|
|
||||||
|
private static readonly (string, string, string?, int?)[] FieldNames = [
|
||||||
|
("MgNr", "MgNr.", null, 12),
|
||||||
|
("Name1", "Name", null, 40),
|
||||||
|
("Name2", "Vorname", null, 40),
|
||||||
|
("Address", "Adresse", null, 60),
|
||||||
|
("Plz", "PLZ", null, 10),
|
||||||
|
("Locality", "Ort", null, 60),
|
||||||
|
("Weight", "Geliefert", "kg", 22),
|
||||||
|
];
|
||||||
|
|
||||||
|
public MemberDeliveryData(IEnumerable<MemberDeliveryRow> rows, List<string> filterNames) :
|
||||||
|
base("Liefermengen Gesamt", "Liefermengen pro Mitglied", string.Join(" / ", filterNames), rows, FieldNames) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<MemberDeliveryData> FromQuery(IQueryable<DeliveryPart> query, List<string> filterNames) {
|
||||||
|
return new((await query
|
||||||
|
.GroupBy(p => new {
|
||||||
|
p.Delivery.MgNr,
|
||||||
|
p.Delivery.Member.Name,
|
||||||
|
p.Delivery.Member.GivenName,
|
||||||
|
p.Delivery.Member.Address,
|
||||||
|
p.Delivery.Member.PostalDest.AtPlz!.Plz,
|
||||||
|
Ort = p.Delivery.Member.PostalDest.AtPlz!.Ort.Name,
|
||||||
|
})
|
||||||
|
.Select(g => new {
|
||||||
|
g.Key,
|
||||||
|
Weight = g.Sum(p => p.Weight),
|
||||||
|
}).ToListAsync())
|
||||||
|
.Select(g => new MemberDeliveryRow {
|
||||||
|
MgNr = g.Key.MgNr,
|
||||||
|
Name1 = g.Key.Name,
|
||||||
|
Name2 = g.Key.GivenName,
|
||||||
|
Address = g.Key.Address,
|
||||||
|
Plz = g.Key.Plz,
|
||||||
|
Locality = g.Key.Ort,
|
||||||
|
Weight = g.Weight,
|
||||||
|
}), filterNames);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MemberDeliveryRow {
|
||||||
|
public required int MgNr;
|
||||||
|
public required string Name1;
|
||||||
|
public required string? Name2;
|
||||||
|
public required string Address;
|
||||||
|
public required int Plz;
|
||||||
|
public required string Locality;
|
||||||
|
public required int Weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
|
using Elwig.Models.Entities;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Elwig.Models.Dtos {
|
namespace Elwig.Models.Dtos {
|
||||||
public class MemberDeliveryPerVarietyData : DataTable<MemberDeliveryPerVariantRow> {
|
public class MemberDeliveryPerVarietyData : DataTable<MemberDeliveryPerVarietyRow> {
|
||||||
|
|
||||||
private static readonly (string, string, string?, int)[] FieldNames = [
|
private static readonly (string, string, string?, int?)[] FieldNames = [
|
||||||
("MgNr", "MgNr.", null, 12),
|
("MgNr", "MgNr.", null, 12),
|
||||||
("Name1", "Name", null, 40),
|
("Name1", "Name", null, 40),
|
||||||
("Name2", "Vorname", null, 40),
|
("Name2", "Vorname", null, 40),
|
||||||
@@ -16,104 +16,63 @@ namespace Elwig.Models.Dtos {
|
|||||||
("Locality", "Ort", null, 60),
|
("Locality", "Ort", null, 60),
|
||||||
("SortIds", "Sorte", null, 12),
|
("SortIds", "Sorte", null, 12),
|
||||||
("AttrIds", "Attribut", null, 16),
|
("AttrIds", "Attribut", null, 16),
|
||||||
|
("CultIds", "Bewirt.", null, 16),
|
||||||
("Weights", "Geliefert", "kg", 22),
|
("Weights", "Geliefert", "kg", 22),
|
||||||
("Areas", "Fläche", "m²", 22),
|
|
||||||
("Yields", "Ertrag", "kg/ha", 22),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
public MemberDeliveryPerVarietyData(IEnumerable<MemberDeliveryPerVariantRow> rows, int year) :
|
public MemberDeliveryPerVarietyData(IEnumerable<MemberDeliveryPerVarietyRow> rows, List<string> filterNames) :
|
||||||
base($"Liefermengen", $"Liefermengen pro Mitglied, Sorte und Attribut {year}", rows, FieldNames) {
|
base("Liefermengen pro Sorte", "Liefermengen pro Mitglied, Sorte, Attribut und Bewirtschaftungsart", string.Join(" / ", filterNames), rows, FieldNames) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<MemberDeliveryPerVarietyData> ForSeason(DbSet<MemberDeliveryPerVariantRowSingle> table, int year) {
|
public static async Task<MemberDeliveryPerVarietyData> FromQuery(IQueryable<DeliveryPart> query, List<string> filterNames) {
|
||||||
return new MemberDeliveryPerVarietyData(
|
return new((await query
|
||||||
(await FromDbSet(table, year)).GroupBy(
|
.GroupBy(p => new {
|
||||||
r => r.MgNr,
|
p.Delivery.MgNr,
|
||||||
(k, g) => new MemberDeliveryPerVariantRow(g)
|
p.Delivery.Member.Name,
|
||||||
), year);
|
p.Delivery.Member.GivenName,
|
||||||
}
|
p.Delivery.Member.Address,
|
||||||
|
p.Delivery.Member.PostalDest.AtPlz!.Plz,
|
||||||
private static async Task<IEnumerable<MemberDeliveryPerVariantRowSingle>> FromDbSet(DbSet<MemberDeliveryPerVariantRowSingle> table, int year) {
|
Ort = p.Delivery.Member.PostalDest.AtPlz!.Ort.Name,
|
||||||
return await table.FromSql($"""
|
p.SortId,
|
||||||
SELECT m.mgnr, m.name AS name_1,
|
p.AttrId,
|
||||||
COALESCE(m.prefix || ' ', '') || m.given_name ||
|
p.CultId,
|
||||||
COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2,
|
})
|
||||||
p.plz, o.name AS ort, m.address,
|
.Select(g => new {
|
||||||
v.bucket, v.weight, v.area
|
g.Key,
|
||||||
FROM (
|
Weight = g.Sum(p => p.Weight),
|
||||||
SELECT c.year AS year,
|
})
|
||||||
c.mgnr AS mgnr,
|
.ToListAsync()).GroupBy(g => new {
|
||||||
c.bucket AS bucket,
|
g.Key.MgNr,
|
||||||
COALESCE(d.weight, 0) AS weight,
|
g.Key.Name,
|
||||||
COALESCE(c.area, 0) AS area
|
g.Key.GivenName,
|
||||||
FROM v_area_commitment_bucket_strict c
|
g.Key.Address,
|
||||||
LEFT JOIN v_delivery_bucket_strict d ON (d.year, d.mgnr, d.bucket) = (c.year, c.mgnr, c.bucket)
|
g.Key.Plz,
|
||||||
WHERE c.year = {year}
|
g.Key.Ort,
|
||||||
UNION
|
}).Select(g => new MemberDeliveryPerVarietyRow {
|
||||||
SELECT d.year,
|
MgNr = g.Key.MgNr,
|
||||||
d.mgnr,
|
Name1 = g.Key.Name,
|
||||||
d.bucket,
|
Name2 = g.Key.GivenName,
|
||||||
COALESCE(d.weight, 0),
|
Address = g.Key.Address,
|
||||||
COALESCE(c.area, 0)
|
Plz = g.Key.Plz,
|
||||||
FROM v_delivery_bucket_strict d
|
Locality = g.Key.Ort,
|
||||||
LEFT JOIN v_area_commitment_bucket_strict c ON (c.year, c.mgnr, c.bucket) = (d.year, d.mgnr, d.bucket)
|
SortIds = [.. g.Select(d => d.Key.SortId)],
|
||||||
WHERE d.year = {year}
|
AttrIds = [.. g.Select(d => d.Key.AttrId)],
|
||||||
) v
|
CultIds = [.. g.Select(d => d.Key.CultId)],
|
||||||
LEFT JOIN member m ON m.mgnr = v.mgnr
|
Weights = [.. g.Select(d => d.Weight)],
|
||||||
LEFT JOIN AT_plz_dest p ON p.id = m.postal_dest
|
}), filterNames);
|
||||||
LEFT JOIN AT_ort o ON o.okz = p.okz
|
|
||||||
ORDER BY m.mgnr, v.bucket
|
|
||||||
""").ToListAsync();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MemberDeliveryPerVariantRow {
|
public class MemberDeliveryPerVarietyRow {
|
||||||
public int MgNr;
|
public required int MgNr;
|
||||||
public string Name1;
|
public required string Name1;
|
||||||
public string? Name2;
|
public required string? Name2;
|
||||||
public string Address;
|
public required string Address;
|
||||||
public int Plz;
|
public required int Plz;
|
||||||
public string Locality;
|
public required string Locality;
|
||||||
public string[] SortIds;
|
public required string[] SortIds;
|
||||||
public string[] AttrIds;
|
public required string?[] AttrIds;
|
||||||
public int[] Areas;
|
public required string?[] CultIds;
|
||||||
public int[] Weights;
|
public required int[] Weights;
|
||||||
public int?[] Yields => Weights.Zip(Areas).Select(i => i.Second > 0 ? (int?)i.First * 10_000 / i.Second : null).ToArray();
|
|
||||||
|
|
||||||
public MemberDeliveryPerVariantRow(IEnumerable<MemberDeliveryPerVariantRowSingle> rows) {
|
|
||||||
var f = rows.First();
|
|
||||||
MgNr = f.MgNr;
|
|
||||||
Name1 = f.Name1;
|
|
||||||
Name2 = f.Name2;
|
|
||||||
Address = f.Address;
|
|
||||||
Plz = f.Plz;
|
|
||||||
Locality = f.Locality.Split(",")[0];
|
|
||||||
SortIds = rows.Select(r => r.VtrgId[..2]).ToArray();
|
|
||||||
AttrIds = rows.Select(r => r.VtrgId[2..]).ToArray();
|
|
||||||
Areas = rows.Select(r => r.Area).ToArray();
|
|
||||||
Weights = rows.Select(r => r.Weight).ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Keyless]
|
|
||||||
public class MemberDeliveryPerVariantRowSingle {
|
|
||||||
[Column("mgnr")]
|
|
||||||
public int MgNr { get; set; }
|
|
||||||
[Column("name_1")]
|
|
||||||
public required string Name1 { get; set; }
|
|
||||||
[Column("name_2")]
|
|
||||||
public string? Name2 { get; set; }
|
|
||||||
[Column("address")]
|
|
||||||
public required string Address { get; set; }
|
|
||||||
[Column("plz")]
|
|
||||||
public int Plz { get; set; }
|
|
||||||
[Column("ort")]
|
|
||||||
public required string Locality { get; set; }
|
|
||||||
[Column("bucket")]
|
|
||||||
public required string VtrgId { get; set; }
|
|
||||||
[Column("area")]
|
|
||||||
public int Area { get; set; }
|
|
||||||
[Column("weight")]
|
|
||||||
public int Weight { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
119
Elwig/Models/Dtos/MemberDeliveryYieldsPerVarietyData.cs
Normal file
119
Elwig/Models/Dtos/MemberDeliveryYieldsPerVarietyData.cs
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Elwig.Models.Dtos {
|
||||||
|
public class MemberDeliveryYieldsPerVarietyData : DataTable<MemberDeliveryYieldsPerVarietyRow> {
|
||||||
|
|
||||||
|
private static readonly (string, string, string?, int)[] FieldNames = [
|
||||||
|
("MgNr", "MgNr.", null, 12),
|
||||||
|
("Name1", "Name", null, 40),
|
||||||
|
("Name2", "Vorname", null, 40),
|
||||||
|
("Address", "Adresse", null, 60),
|
||||||
|
("Plz", "PLZ", null, 10),
|
||||||
|
("Locality", "Ort", null, 60),
|
||||||
|
("SortIds", "Sorte", null, 12),
|
||||||
|
("AttrIds", "Attribut", null, 16),
|
||||||
|
("Weights", "Geliefert", "kg", 22),
|
||||||
|
("Areas", "Fläche", "m²", 22),
|
||||||
|
("Yields", "Ertrag", "kg/ha", 22),
|
||||||
|
];
|
||||||
|
|
||||||
|
public MemberDeliveryYieldsPerVarietyData(IEnumerable<MemberDeliveryYieldsPerVarietyRow> rows, int year) :
|
||||||
|
base($"Liefermengen", $"Liefermengen pro Mitglied, Sorte und Attribut {year}", rows, FieldNames) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<MemberDeliveryYieldsPerVarietyData> ForSeason(DbSet<MemberDeliveryPerVarietyRowSingle> table, int year) {
|
||||||
|
return new MemberDeliveryYieldsPerVarietyData(
|
||||||
|
(await FromDbSet(table, year)).GroupBy(
|
||||||
|
r => r.MgNr,
|
||||||
|
(k, g) => new MemberDeliveryYieldsPerVarietyRow(g)
|
||||||
|
), year);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<IEnumerable<MemberDeliveryPerVarietyRowSingle>> FromDbSet(DbSet<MemberDeliveryPerVarietyRowSingle> table, int year) {
|
||||||
|
return await table.FromSql($"""
|
||||||
|
SELECT m.mgnr, m.name AS name_1,
|
||||||
|
COALESCE(m.prefix || ' ', '') || m.given_name ||
|
||||||
|
COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2,
|
||||||
|
p.plz, o.name AS ort, m.address,
|
||||||
|
v.bucket, v.weight, v.area
|
||||||
|
FROM (
|
||||||
|
SELECT c.year AS year,
|
||||||
|
c.mgnr AS mgnr,
|
||||||
|
c.bucket AS bucket,
|
||||||
|
COALESCE(d.weight, 0) AS weight,
|
||||||
|
COALESCE(c.area, 0) AS area
|
||||||
|
FROM v_area_commitment_bucket_strict c
|
||||||
|
LEFT JOIN v_delivery_bucket_strict d ON (d.year, d.mgnr, d.bucket) = (c.year, c.mgnr, c.bucket)
|
||||||
|
WHERE c.year = {year}
|
||||||
|
UNION
|
||||||
|
SELECT d.year,
|
||||||
|
d.mgnr,
|
||||||
|
d.bucket,
|
||||||
|
COALESCE(d.weight, 0),
|
||||||
|
COALESCE(c.area, 0)
|
||||||
|
FROM v_delivery_bucket_strict d
|
||||||
|
LEFT JOIN v_area_commitment_bucket_strict c ON (c.year, c.mgnr, c.bucket) = (d.year, d.mgnr, d.bucket)
|
||||||
|
WHERE d.year = {year}
|
||||||
|
) v
|
||||||
|
LEFT JOIN member m ON m.mgnr = v.mgnr
|
||||||
|
LEFT JOIN AT_plz_dest p ON p.id = m.postal_dest
|
||||||
|
LEFT JOIN AT_ort o ON o.okz = p.okz
|
||||||
|
ORDER BY m.mgnr, v.bucket
|
||||||
|
""").ToListAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MemberDeliveryYieldsPerVarietyRow {
|
||||||
|
public int MgNr;
|
||||||
|
public string Name1;
|
||||||
|
public string? Name2;
|
||||||
|
public string Address;
|
||||||
|
public int Plz;
|
||||||
|
public string Locality;
|
||||||
|
public string[] SortIds;
|
||||||
|
public string[] AttrIds;
|
||||||
|
public int[] Areas;
|
||||||
|
public int[] Weights;
|
||||||
|
public int?[] Yields => Weights.Zip(Areas).Select(i => i.Second > 0 ? (int?)i.First * 10_000 / i.Second : null).ToArray();
|
||||||
|
|
||||||
|
public MemberDeliveryYieldsPerVarietyRow(IEnumerable<MemberDeliveryPerVarietyRowSingle> rows) {
|
||||||
|
var f = rows.First();
|
||||||
|
MgNr = f.MgNr;
|
||||||
|
Name1 = f.Name1;
|
||||||
|
Name2 = f.Name2;
|
||||||
|
Address = f.Address;
|
||||||
|
Plz = f.Plz;
|
||||||
|
Locality = f.Locality.Split(",")[0];
|
||||||
|
SortIds = rows.Select(r => r.VtrgId[..2]).ToArray();
|
||||||
|
AttrIds = rows.Select(r => r.VtrgId[2..]).ToArray();
|
||||||
|
Areas = rows.Select(r => r.Area).ToArray();
|
||||||
|
Weights = rows.Select(r => r.Weight).ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Keyless]
|
||||||
|
public class MemberDeliveryPerVarietyRowSingle {
|
||||||
|
[Column("mgnr")]
|
||||||
|
public int MgNr { get; set; }
|
||||||
|
[Column("name_1")]
|
||||||
|
public required string Name1 { get; set; }
|
||||||
|
[Column("name_2")]
|
||||||
|
public string? Name2 { get; set; }
|
||||||
|
[Column("address")]
|
||||||
|
public required string Address { get; set; }
|
||||||
|
[Column("plz")]
|
||||||
|
public int Plz { get; set; }
|
||||||
|
[Column("ort")]
|
||||||
|
public required string Locality { get; set; }
|
||||||
|
[Column("bucket")]
|
||||||
|
public required string VtrgId { get; set; }
|
||||||
|
[Column("area")]
|
||||||
|
public int Area { get; set; }
|
||||||
|
[Column("weight")]
|
||||||
|
public int Weight { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,8 +18,9 @@ namespace Elwig.Models.Dtos {
|
|||||||
("Cultivation", "Bewirt.", null, 20),
|
("Cultivation", "Bewirt.", null, 20),
|
||||||
("QualityLevel", "Qualitätsstufe", null, 30),
|
("QualityLevel", "Qualitätsstufe", null, 30),
|
||||||
("Oe", "Gradation", "°Oe", 20),
|
("Oe", "Gradation", "°Oe", 20),
|
||||||
("Ungeb", "ungebunden", "kg|€/kg", 40),
|
("Ungeb", "ungebunden", "kg|€/kg|€/kg", 60),
|
||||||
("Geb", "gebunden", "kg|€/kg", 40),
|
("LowGeb", "attributlos gebunden", "kg|€/kg|€/kg", 60),
|
||||||
|
("Geb", "gebunden", "kg|€/kg|€/kg", 60),
|
||||||
("Amount", "Gesamt", "€", 25),
|
("Amount", "Gesamt", "€", 25),
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -30,8 +31,9 @@ namespace Elwig.Models.Dtos {
|
|||||||
string? Cultivation,
|
string? Cultivation,
|
||||||
string QualityLevel,
|
string QualityLevel,
|
||||||
double Oe,
|
double Oe,
|
||||||
(int Weight, decimal? Price) Ungeb,
|
(int Weight, decimal? MinPrice, decimal? MaxPrice) Ungeb,
|
||||||
(int Weight, decimal? Price) Geb,
|
(int Weight, decimal? MinPrice, decimal? MaxPrice) LowGeb,
|
||||||
|
(int Weight, decimal? MinPrice, decimal? MaxPrice) Geb,
|
||||||
decimal Amount
|
decimal Amount
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -42,8 +44,9 @@ namespace Elwig.Models.Dtos {
|
|||||||
public static async Task<PaymentVariantSummaryData> ForPaymentVariant(PaymentVar v, DbSet<PaymentVariantSummaryRow> table) {
|
public static async Task<PaymentVariantSummaryData> ForPaymentVariant(PaymentVar v, DbSet<PaymentVariantSummaryRow> table) {
|
||||||
return new(v, (await FromDbSet(table, v.Year, v.AvNr))
|
return new(v, (await FromDbSet(table, v.Year, v.AvNr))
|
||||||
.Select(r => new PaymentRow(r.Type, r.Variety, r.Attribute, r.Cultivation, r.QualityLevel, r.Oe,
|
.Select(r => new PaymentRow(r.Type, r.Variety, r.Attribute, r.Cultivation, r.QualityLevel, r.Oe,
|
||||||
(r.WeightUngeb, r.PriceUngeb != null ? Utils.DecFromDb(r.PriceUngeb.Value, v.Season.Precision) : null),
|
(r.WeightUngeb, r.MinPriceUngeb != null ? Utils.DecFromDb(r.MinPriceUngeb.Value, v.Season.Precision) : null, r.MaxPriceUngeb != null ? Utils.DecFromDb(r.MaxPriceUngeb.Value, v.Season.Precision) : null),
|
||||||
(r.WeightGeb, r.PriceGeb != null ? Utils.DecFromDb(r.PriceGeb.Value, v.Season.Precision) : null),
|
(r.WeightLowGeb, r.MinPriceLowGeb != null ? Utils.DecFromDb(r.MinPriceLowGeb.Value, v.Season.Precision) : null, r.MaxPriceLowGeb != null ? Utils.DecFromDb(r.MaxPriceLowGeb.Value, v.Season.Precision) : null),
|
||||||
|
(r.WeightGeb, r.MinPriceGeb != null ? Utils.DecFromDb(r.MinPriceGeb.Value, v.Season.Precision) : null, r.MaxPriceGeb != null ? Utils.DecFromDb(r.MaxPriceGeb.Value, v.Season.Precision) : null),
|
||||||
Utils.DecFromDb(r.Amount, v.Season.Precision)))
|
Utils.DecFromDb(r.Amount, v.Season.Precision)))
|
||||||
.ToArray());
|
.ToArray());
|
||||||
}
|
}
|
||||||
@@ -57,19 +60,23 @@ namespace Elwig.Models.Dtos {
|
|||||||
q.name AS quality_level,
|
q.name AS quality_level,
|
||||||
ROUND(kmw * (4.54 + 0.022 * kmw)) AS oe,
|
ROUND(kmw * (4.54 + 0.022 * kmw)) AS oe,
|
||||||
SUM(IIF(w.discr = '_', w.value, 0)) AS weight_ungeb,
|
SUM(IIF(w.discr = '_', w.value, 0)) AS weight_ungeb,
|
||||||
MAX(IIF(w.discr = '_', b.price, NULL)) AS price_ungeb,
|
MIN(IIF(w.discr = '_', b.price, NULL)) AS min_price_ungeb,
|
||||||
SUM(IIF(w.discr != '_', w.value, 0)) AS weight_geb,
|
MAX(IIF(w.discr = '_', b.price, NULL)) AS max_price_ungeb,
|
||||||
MAX(IIF(w.discr != '_', b.price, NULL)) AS price_geb,
|
SUM(IIF(w.discr NOT IN (COALESCE(p.attrid, ''), '_'), w.value, 0)) AS weight_lowgeb,
|
||||||
|
MIN(IIF(w.discr NOT IN (COALESCE(p.attrid, ''), '_'), b.price, NULL)) AS min_price_lowgeb,
|
||||||
|
MAX(IIF(w.discr NOT IN (COALESCE(p.attrid, ''), '_'), b.price, NULL)) AS max_price_lowgeb,
|
||||||
|
SUM(IIF(w.discr = COALESCE(p.attrid, ''), w.value, 0)) AS weight_geb,
|
||||||
|
MIN(IIF(w.discr = COALESCE(p.attrid, ''), b.price, NULL)) AS min_price_geb,
|
||||||
|
MAX(IIF(w.discr = COALESCE(p.attrid, ''), b.price, NULL)) AS max_price_geb,
|
||||||
SUM(b.amount) AS amount
|
SUM(b.amount) AS amount
|
||||||
FROM payment_delivery_part_bucket b
|
FROM payment_delivery_part_bucket b
|
||||||
LEFT JOIN delivery_part_bucket w ON (w.year, w.did, w.dpnr, w.bktnr) = (b.year, b.did, b.dpnr, b.bktnr)
|
LEFT JOIN delivery_part_bucket w ON (w.year, w.did, w.dpnr, w.bktnr) = (b.year, b.did, b.dpnr, b.bktnr)
|
||||||
LEFT JOIN delivery_part p ON (p.year, p.did, p.dpnr) = (b.year, b.did, b.dpnr)
|
LEFT JOIN delivery_part p ON (p.year, p.did, p.dpnr) = (b.year, b.did, b.dpnr)
|
||||||
LEFT JOIN delivery d ON (d.year, d.did) = (p.year, p.did)
|
|
||||||
LEFT JOIN wine_variety v ON v.sortid = p.sortid
|
LEFT JOIN wine_variety v ON v.sortid = p.sortid
|
||||||
LEFT JOIN wine_attribute a ON a.attrid = p.attrid
|
LEFT JOIN wine_attribute a ON a.attrid = p.attrid
|
||||||
LEFT JOIN wine_cultivation c ON c.cultid = p.cultid
|
LEFT JOIN wine_cultivation c ON c.cultid = p.cultid
|
||||||
LEFT JOIN wine_quality_level q ON q.qualid = p.qualid
|
LEFT JOIN wine_quality_level q ON q.qualid = p.qualid
|
||||||
WHERE d.year = {year} AND b.avnr = {avnr}
|
WHERE p.year = {year} AND b.avnr = {avnr}
|
||||||
GROUP BY variety, attribute, cultivation, q.min_kmw, oe
|
GROUP BY variety, attribute, cultivation, q.min_kmw, oe
|
||||||
ORDER BY variety, attribute, cultivation, q.min_kmw, oe
|
ORDER BY variety, attribute, cultivation, q.min_kmw, oe
|
||||||
""").ToListAsync();
|
""").ToListAsync();
|
||||||
@@ -92,12 +99,22 @@ namespace Elwig.Models.Dtos {
|
|||||||
public double Oe { get; set; }
|
public double Oe { get; set; }
|
||||||
[Column("weight_ungeb")]
|
[Column("weight_ungeb")]
|
||||||
public int WeightUngeb { get; set; }
|
public int WeightUngeb { get; set; }
|
||||||
[Column("price_ungeb")]
|
[Column("min_price_ungeb")]
|
||||||
public long? PriceUngeb { get; set; }
|
public long? MinPriceUngeb { get; set; }
|
||||||
|
[Column("max_price_ungeb")]
|
||||||
|
public long? MaxPriceUngeb { get; set; }
|
||||||
|
[Column("weight_lowgeb")]
|
||||||
|
public int WeightLowGeb { get; set; }
|
||||||
|
[Column("min_price_lowgeb")]
|
||||||
|
public long? MinPriceLowGeb { get; set; }
|
||||||
|
[Column("max_price_lowgeb")]
|
||||||
|
public long? MaxPriceLowGeb { get; set; }
|
||||||
[Column("weight_geb")]
|
[Column("weight_geb")]
|
||||||
public int WeightGeb { get; set; }
|
public int WeightGeb { get; set; }
|
||||||
[Column("price_geb")]
|
[Column("min_price_geb")]
|
||||||
public long? PriceGeb { get; set; }
|
public long? MinPriceGeb { get; set; }
|
||||||
|
[Column("max_price_geb")]
|
||||||
|
public long? MaxPriceGeb { get; set; }
|
||||||
[Column("amount")]
|
[Column("amount")]
|
||||||
public long Amount { get; set; }
|
public long Amount { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace Elwig.Models.Dtos {
|
|||||||
|
|
||||||
public static IEnumerable<Transaction> FromPaymentVariant(PaymentVar variant) {
|
public static IEnumerable<Transaction> FromPaymentVariant(PaymentVar variant) {
|
||||||
return variant.Credits
|
return variant.Credits
|
||||||
.Where(c => c.Member.Iban != null)
|
.Where(c => c.Member.Iban != null && c.Amount > 0)
|
||||||
.OrderBy(c => c.TgNr)
|
.OrderBy(c => c.TgNr)
|
||||||
.Select(c => new Transaction(c))
|
.Select(c => new Transaction(c))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ namespace Elwig.Models.Dtos {
|
|||||||
("CultId", "Bewirt.", null, 15),
|
("CultId", "Bewirt.", null, 15),
|
||||||
("QualId", "Qual.", null, 15),
|
("QualId", "Qual.", null, 15),
|
||||||
("Geb", "gebunden", null, 20),
|
("Geb", "gebunden", null, 20),
|
||||||
("Weight", "Gewicht", "kg", 20),
|
("Weight", "Menge", "kg", 20),
|
||||||
];
|
];
|
||||||
|
|
||||||
public WeightBreakdownData(IEnumerable<WeightBreakdownRow> rows, int year, string name) :
|
public WeightBreakdownData(IEnumerable<WeightBreakdownRow> rows, int year, string name) :
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ namespace Elwig.Models.Dtos {
|
|||||||
("Members", "Mitgl.", "#", 15),
|
("Members", "Mitgl.", "#", 15),
|
||||||
("Deliveries", "Lfrg.", "#", 15),
|
("Deliveries", "Lfrg.", "#", 15),
|
||||||
("Parts", "Teill.", "#", 15),
|
("Parts", "Teill.", "#", 15),
|
||||||
("Weight", "Gewicht", "kg", 20),
|
("Weight", "Menge", "kg", 20),
|
||||||
("Gradation", "Gradation", "°Oe|°KMW", 30),
|
("Gradation", "Gradation", "°Oe|°KMW", 30),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -41,14 +41,36 @@ namespace Elwig.Models.Entities {
|
|||||||
public string? Comment { get; set; }
|
public string? Comment { get; set; }
|
||||||
|
|
||||||
[Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
[Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
||||||
public long CTime { get; private set; }
|
public long CTime { get; set; }
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public DateTime CreatedTimestamp => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
|
public DateTime CreatedAt {
|
||||||
|
get => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
|
||||||
|
set => CTime = ((DateTimeOffset)value.ToUniversalTime()).ToUnixTimeSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
[Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
[Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
||||||
public long MTime { get; private set; }
|
public long MTime { get; set; }
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public DateTime ModifiedTimestamp => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime;
|
public DateTime ModifiedAt {
|
||||||
|
get => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime;
|
||||||
|
set => MTime = ((DateTimeOffset)value.ToUniversalTime()).ToUnixTimeSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Column("xtime")]
|
||||||
|
public long? XTime { get; set; }
|
||||||
|
[NotMapped]
|
||||||
|
public DateTime? ExportedAt {
|
||||||
|
get => XTime == null ? null : DateTimeOffset.FromUnixTimeSeconds(XTime.Value).LocalDateTime;
|
||||||
|
set => XTime = value == null ? null : ((DateTimeOffset)value.Value.ToUniversalTime()).ToUnixTimeSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Column("itime")]
|
||||||
|
public long? ITime { get; set; }
|
||||||
|
[NotMapped]
|
||||||
|
public DateTime? ImportedAt {
|
||||||
|
get => ITime == null ? null : DateTimeOffset.FromUnixTimeSeconds(ITime.Value).LocalDateTime;
|
||||||
|
set => ITime = value == null ? null : ((DateTimeOffset)value.Value.ToUniversalTime()).ToUnixTimeSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
[ForeignKey("MgNr")]
|
[ForeignKey("MgNr")]
|
||||||
public virtual Member Member { get; private set; } = null!;
|
public virtual Member Member { get; private set; } = null!;
|
||||||
|
|||||||
@@ -84,14 +84,36 @@ namespace Elwig.Models.Entities {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
[Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
||||||
public long CTime { get; private set; }
|
public long CTime { get; set; }
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public DateTime CreatedTimestamp => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
|
public DateTime CreatedAt {
|
||||||
|
get => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
|
||||||
|
set => CTime = ((DateTimeOffset)value.ToUniversalTime()).ToUnixTimeSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
[Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
[Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
||||||
public long MTime { get; private set; }
|
public long MTime { get; set; }
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public DateTime ModifiedTimestamp => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime;
|
public DateTime ModifiedAt {
|
||||||
|
get => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime;
|
||||||
|
set => MTime = ((DateTimeOffset)value.ToUniversalTime()).ToUnixTimeSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Column("xtime")]
|
||||||
|
public long? XTime { get; set; }
|
||||||
|
[NotMapped]
|
||||||
|
public DateTime? ExportedAt {
|
||||||
|
get => XTime == null ? null : DateTimeOffset.FromUnixTimeSeconds(XTime.Value).LocalDateTime;
|
||||||
|
set => XTime = value == null ? null : ((DateTimeOffset)value.Value.ToUniversalTime()).ToUnixTimeSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Column("itime")]
|
||||||
|
public long? ITime { get; set; }
|
||||||
|
[NotMapped]
|
||||||
|
public DateTime? ImportedAt {
|
||||||
|
get => ITime == null ? null : DateTimeOffset.FromUnixTimeSeconds(ITime.Value).LocalDateTime;
|
||||||
|
set => ITime = value == null ? null : ((DateTimeOffset)value.Value.ToUniversalTime()).ToUnixTimeSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
[ForeignKey("Year, AvNr, MgNr")]
|
[ForeignKey("Year, AvNr, MgNr")]
|
||||||
public virtual PaymentMember Payment { get; private set; } = null!;
|
public virtual PaymentMember Payment { get; private set; } = null!;
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
using Elwig.Helpers;
|
using Elwig.Helpers;
|
||||||
|
using Elwig.Helpers.Billing;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Windows.Media;
|
||||||
using IndexAttribute = Microsoft.EntityFrameworkCore.IndexAttribute;
|
using IndexAttribute = Microsoft.EntityFrameworkCore.IndexAttribute;
|
||||||
|
|
||||||
namespace Elwig.Models.Entities {
|
namespace Elwig.Models.Entities {
|
||||||
@@ -61,15 +63,42 @@ namespace Elwig.Models.Entities {
|
|||||||
[Column("comment")]
|
[Column("comment")]
|
||||||
public string? Comment { get; set; }
|
public string? Comment { get; set; }
|
||||||
|
|
||||||
[Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
|
||||||
public long CTime { get; private set; }
|
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public DateTime CreatedTimestamp => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
|
public string[] Comments => [.. Parts.Select(p => p.Comment).Prepend(Comment).Where(c => c != null).Cast<string>()];
|
||||||
|
[NotMapped]
|
||||||
|
public string CommentsString => string.Join(" / ", Comments);
|
||||||
|
|
||||||
|
[Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
||||||
|
public long CTime { get; set; }
|
||||||
|
[NotMapped]
|
||||||
|
public DateTime CreatedAt {
|
||||||
|
get => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
|
||||||
|
set => CTime = ((DateTimeOffset)value.ToUniversalTime()).ToUnixTimeSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
[Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
[Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
||||||
public long MTime { get; private set; }
|
public long MTime { get; set; }
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public DateTime ModifiedTimestamp => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime;
|
public DateTime ModifiedAt {
|
||||||
|
get => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime;
|
||||||
|
set => MTime = ((DateTimeOffset)value.ToUniversalTime()).ToUnixTimeSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Column("xtime")]
|
||||||
|
public long? XTime { get; set; }
|
||||||
|
[NotMapped]
|
||||||
|
public DateTime? ExportedAt {
|
||||||
|
get => XTime == null ? null : DateTimeOffset.FromUnixTimeSeconds(XTime.Value).LocalDateTime;
|
||||||
|
set => XTime = value == null ? null : ((DateTimeOffset)value.Value.ToUniversalTime()).ToUnixTimeSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Column("itime")]
|
||||||
|
public long? ITime { get; set; }
|
||||||
|
[NotMapped]
|
||||||
|
public DateTime? ImportedAt {
|
||||||
|
get => ITime == null ? null : DateTimeOffset.FromUnixTimeSeconds(ITime.Value).LocalDateTime;
|
||||||
|
set => ITime = value == null ? null : ((DateTimeOffset)value.Value.ToUniversalTime()).ToUnixTimeSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
[ForeignKey("Year")]
|
[ForeignKey("Year")]
|
||||||
public virtual Season Season { get; private set; } = null!;
|
public virtual Season Season { get; private set; } = null!;
|
||||||
@@ -85,16 +114,19 @@ namespace Elwig.Models.Entities {
|
|||||||
public int Weight => Parts.Select(p => p.Weight).Sum();
|
public int Weight => Parts.Select(p => p.Weight).Sum();
|
||||||
public int FilteredWeight => FilteredParts.Select(p => p.Weight).Sum();
|
public int FilteredWeight => FilteredParts.Select(p => p.Weight).Sum();
|
||||||
|
|
||||||
public IEnumerable<string> SortIds => Parts
|
public IEnumerable<RawVaribute> Vaributes => Parts
|
||||||
.GroupBy(p => p.SortId)
|
.GroupBy(p => (p.SortId, p.AttrId, p.CultId))
|
||||||
.OrderByDescending(g => g.Select(p => p.Weight).Sum())
|
.OrderByDescending(g => g.Select(p => p.Weight).Sum())
|
||||||
.Select(g => g.Key);
|
.Select(g => new RawVaribute(g.Key.SortId, g.Key.AttrId, g.Key.CultId));
|
||||||
public IEnumerable<string> FilteredSortIds => FilteredParts
|
public IEnumerable<RawVaribute> FilteredVaributes => FilteredParts
|
||||||
.GroupBy(p => p.SortId)
|
.GroupBy(p => (p.SortId, p.AttrId, p.CultId))
|
||||||
.OrderByDescending(g => g.Select(p => p.Weight).Sum())
|
.OrderByDescending(g => g.Select(p => p.Weight).Sum())
|
||||||
.Select(g => g.Key);
|
.Select(g => new RawVaribute(g.Key.SortId, g.Key.AttrId, g.Key.CultId));
|
||||||
public string SortIdString => string.Join(", ", SortIds);
|
public string VaributeString => string.Join(", ", Vaributes);
|
||||||
public string FilteredSortIdString => string.Join(", ", FilteredSortIds);
|
public string FilteredVaributeString => string.Join(", ", FilteredVaributes);
|
||||||
|
|
||||||
|
public Brush? Color => Parts.Select(p => p.Variety.Color).Distinct().SingleOrDefault();
|
||||||
|
public Brush? FilteredColor => FilteredParts.Select(p => p.Variety.Color).Distinct().SingleOrDefault();
|
||||||
|
|
||||||
public IEnumerable<string> Modifiers => Parts
|
public IEnumerable<string> Modifiers => Parts
|
||||||
.SelectMany(p => p.Modifiers)
|
.SelectMany(p => p.Modifiers)
|
||||||
|
|||||||
@@ -27,14 +27,36 @@ namespace Elwig.Models.Entities {
|
|||||||
public required string Type { get; set; }
|
public required string Type { get; set; }
|
||||||
|
|
||||||
[Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
[Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
||||||
public long CTime { get; private set; }
|
public long CTime { get; set; }
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public DateTime CreatedTimestamp => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
|
public DateTime CreatedAt {
|
||||||
|
get => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
|
||||||
|
set => CTime = ((DateTimeOffset)value.ToUniversalTime()).ToUnixTimeSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
[Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
[Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
||||||
public long MTime { get; private set; }
|
public long MTime { get; set; }
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public DateTime ModifiedTimestamp => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime;
|
public DateTime ModifiedAt {
|
||||||
|
get => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime;
|
||||||
|
set => MTime = ((DateTimeOffset)value.ToUniversalTime()).ToUnixTimeSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Column("xtime")]
|
||||||
|
public long? XTime { get; set; }
|
||||||
|
[NotMapped]
|
||||||
|
public DateTime? ExportedAt {
|
||||||
|
get => XTime == null ? null : DateTimeOffset.FromUnixTimeSeconds(XTime.Value).LocalDateTime;
|
||||||
|
set => XTime = value == null ? null : ((DateTimeOffset)value.Value.ToUniversalTime()).ToUnixTimeSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Column("itime")]
|
||||||
|
public long? ITime { get; set; }
|
||||||
|
[NotMapped]
|
||||||
|
public DateTime? ImportedAt {
|
||||||
|
get => ITime == null ? null : DateTimeOffset.FromUnixTimeSeconds(ITime.Value).LocalDateTime;
|
||||||
|
set => ITime = value == null ? null : ((DateTimeOffset)value.Value.ToUniversalTime()).ToUnixTimeSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
[ForeignKey("Year, DsNr")]
|
[ForeignKey("Year, DsNr")]
|
||||||
public virtual DeliverySchedule Schedule { get; private set; } = null!;
|
public virtual DeliverySchedule Schedule { get; private set; } = null!;
|
||||||
|
|||||||
@@ -9,6 +9,11 @@ using System.Text.Json.Nodes;
|
|||||||
namespace Elwig.Models.Entities {
|
namespace Elwig.Models.Entities {
|
||||||
[Table("delivery_part"), PrimaryKey("Year", "DId", "DPNr")]
|
[Table("delivery_part"), PrimaryKey("Year", "DId", "DPNr")]
|
||||||
public class DeliveryPart : IDelivery {
|
public class DeliveryPart : IDelivery {
|
||||||
|
|
||||||
|
public const string Dumper = "dumper";
|
||||||
|
public const string Pumped = "pumped";
|
||||||
|
public const string Box = "box";
|
||||||
|
|
||||||
[Column("year")]
|
[Column("year")]
|
||||||
public int Year { get; set; }
|
public int Year { get; set; }
|
||||||
|
|
||||||
@@ -86,12 +91,27 @@ namespace Elwig.Models.Entities {
|
|||||||
[Column("hand_picked")]
|
[Column("hand_picked")]
|
||||||
public bool? IsHandPicked { get; set; }
|
public bool? IsHandPicked { get; set; }
|
||||||
|
|
||||||
[Column("lesewagen")]
|
|
||||||
public bool? IsLesewagen { get; set; }
|
|
||||||
|
|
||||||
[Column("gebunden")]
|
[Column("gebunden")]
|
||||||
public bool? IsGebunden { get; set; }
|
public bool? IsGebunden { get; set; }
|
||||||
|
|
||||||
|
[Column("unloading")]
|
||||||
|
public string? Unloading { get; set; }
|
||||||
|
[NotMapped]
|
||||||
|
public bool IsDumper {
|
||||||
|
get => Unloading == Dumper;
|
||||||
|
set => Unloading = value ? Dumper : Unloading;
|
||||||
|
}
|
||||||
|
[NotMapped]
|
||||||
|
public bool IsPumped {
|
||||||
|
get => Unloading == Pumped;
|
||||||
|
set => Unloading = value ? Pumped : Unloading;
|
||||||
|
}
|
||||||
|
[NotMapped]
|
||||||
|
public bool IsBox {
|
||||||
|
get => Unloading == Box;
|
||||||
|
set => Unloading = value ? Box : Unloading;
|
||||||
|
}
|
||||||
|
|
||||||
[Column("temperature")]
|
[Column("temperature")]
|
||||||
public double? Temperature { get; set; }
|
public double? Temperature { get; set; }
|
||||||
|
|
||||||
@@ -129,14 +149,36 @@ namespace Elwig.Models.Entities {
|
|||||||
public string? Comment { get; set; }
|
public string? Comment { get; set; }
|
||||||
|
|
||||||
[Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
[Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
||||||
public long CTime { get; private set; }
|
public long CTime { get; set; }
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public DateTime CreatedTimestamp => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
|
public DateTime CreatedAt {
|
||||||
|
get => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
|
||||||
|
set => CTime = ((DateTimeOffset)value.ToUniversalTime()).ToUnixTimeSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
[Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
[Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
||||||
public long MTime { get; private set; }
|
public long MTime { get; set; }
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public DateTime ModifiedTimestamp => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime;
|
public DateTime ModifiedAt {
|
||||||
|
get => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime;
|
||||||
|
set => MTime = ((DateTimeOffset)value.ToUniversalTime()).ToUnixTimeSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Column("xtime")]
|
||||||
|
public long? XTime { get; set; }
|
||||||
|
[NotMapped]
|
||||||
|
public DateTime? ExportedAt {
|
||||||
|
get => XTime == null ? null : DateTimeOffset.FromUnixTimeSeconds(XTime.Value).LocalDateTime;
|
||||||
|
set => XTime = value == null ? null : ((DateTimeOffset)value.Value.ToUniversalTime()).ToUnixTimeSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Column("itime")]
|
||||||
|
public long? ITime { get; set; }
|
||||||
|
[NotMapped]
|
||||||
|
public DateTime? ImportedAt {
|
||||||
|
get => ITime == null ? null : DateTimeOffset.FromUnixTimeSeconds(ITime.Value).LocalDateTime;
|
||||||
|
set => ITime = value == null ? null : ((DateTimeOffset)value.Value.ToUniversalTime()).ToUnixTimeSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
[InverseProperty(nameof(DeliveryPartModifier.Part))]
|
[InverseProperty(nameof(DeliveryPartModifier.Part))]
|
||||||
public virtual ICollection<DeliveryPartModifier> PartModifiers { get; private set; } = null!;
|
public virtual ICollection<DeliveryPartModifier> PartModifiers { get; private set; } = null!;
|
||||||
|
|||||||
@@ -42,7 +42,9 @@ namespace Elwig.Models.Entities {
|
|||||||
[Column("max_weight")]
|
[Column("max_weight")]
|
||||||
public int? MaxWeight { get; set; }
|
public int? MaxWeight { get; set; }
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public int AnnouncedWeight => Announcements.Sum(a => a.Weight);
|
public int AnnouncedWeight => AnnouncedWeightOverride ?? Announcements.Sum(a => a.Weight);
|
||||||
|
[NotMapped]
|
||||||
|
public int? AnnouncedWeightOverride { get; set; }
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public double? Percent => (double)AnnouncedWeight / MaxWeight * 100;
|
public double? Percent => (double)AnnouncedWeight / MaxWeight * 100;
|
||||||
|
|
||||||
|
|||||||
@@ -145,14 +145,36 @@ namespace Elwig.Models.Entities {
|
|||||||
public AT_Kg? DefaultKg => DefaultWbKg?.AtKg;
|
public AT_Kg? DefaultKg => DefaultWbKg?.AtKg;
|
||||||
|
|
||||||
[Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
[Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
||||||
public long CTime { get; private set; }
|
public long CTime { get; set; }
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public DateTime CreatedTimestamp => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
|
public DateTime CreatedAt {
|
||||||
|
get => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
|
||||||
|
set => CTime = ((DateTimeOffset)value.ToUniversalTime()).ToUnixTimeSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
[Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
[Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
||||||
public long MTime { get; private set; }
|
public long MTime { get; set; }
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public DateTime ModifiedTimestamp => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime;
|
public DateTime ModifiedAt {
|
||||||
|
get => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime;
|
||||||
|
set => MTime = ((DateTimeOffset)value.ToUniversalTime()).ToUnixTimeSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Column("xtime")]
|
||||||
|
public long? XTime { get; set; }
|
||||||
|
[NotMapped]
|
||||||
|
public DateTime? ExportedAt {
|
||||||
|
get => XTime == null ? null : DateTimeOffset.FromUnixTimeSeconds(XTime.Value).LocalDateTime;
|
||||||
|
set => XTime = value == null ? null : ((DateTimeOffset)value.Value.ToUniversalTime()).ToUnixTimeSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Column("itime")]
|
||||||
|
public long? ITime { get; set; }
|
||||||
|
[NotMapped]
|
||||||
|
public DateTime? ImportedAt {
|
||||||
|
get => ITime == null ? null : DateTimeOffset.FromUnixTimeSeconds(ITime.Value).LocalDateTime;
|
||||||
|
set => ITime = value == null ? null : ((DateTimeOffset)value.Value.ToUniversalTime()).ToUnixTimeSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
[ForeignKey("ZwstId")]
|
[ForeignKey("ZwstId")]
|
||||||
public virtual Branch? Branch { get; private set; }
|
public virtual Branch? Branch { get; private set; }
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ namespace Elwig.Models.Entities {
|
|||||||
[Column("active")]
|
[Column("active")]
|
||||||
public bool IsActive { get; set; }
|
public bool IsActive { get; set; }
|
||||||
|
|
||||||
|
[Column("redacted")]
|
||||||
|
public bool IsRedacted { get; set; }
|
||||||
|
|
||||||
[Column("ordering")]
|
[Column("ordering")]
|
||||||
public int Ordering { get; set; }
|
public int Ordering { get; set; }
|
||||||
|
|
||||||
@@ -46,6 +49,8 @@ namespace Elwig.Models.Entities {
|
|||||||
(Rel != null) ? $"{Utils.GetSign(Rel.Value)}{(Math.Abs(Rel.Value) < 0.1m ? "\u2007" : "")}{Math.Abs(Rel.Value):0.00##\u00a0%}" :
|
(Rel != null) ? $"{Utils.GetSign(Rel.Value)}{(Math.Abs(Rel.Value) < 0.1m ? "\u2007" : "")}{Math.Abs(Rel.Value):0.00##\u00a0%}" :
|
||||||
"";
|
"";
|
||||||
|
|
||||||
|
public string? PublicValueStr => IsRedacted ? null : ValueStr;
|
||||||
|
|
||||||
public override string ToString() {
|
public override string ToString() {
|
||||||
return Name;
|
return Name;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,14 +46,36 @@ namespace Elwig.Models.Entities {
|
|||||||
public required string Data { get; set; }
|
public required string Data { get; set; }
|
||||||
|
|
||||||
[Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
[Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
||||||
public long CTime { get; private set; }
|
public long CTime { get; set; }
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public DateTime CreatedTimestamp => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
|
public DateTime CreatedAt {
|
||||||
|
get => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
|
||||||
|
set => CTime = ((DateTimeOffset)value.ToUniversalTime()).ToUnixTimeSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
[Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
[Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
||||||
public long MTime { get; private set; }
|
public long MTime { get; set; }
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public DateTime ModifiedTimestamp => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime;
|
public DateTime ModifiedAt {
|
||||||
|
get => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime;
|
||||||
|
set => MTime = ((DateTimeOffset)value.ToUniversalTime()).ToUnixTimeSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Column("xtime")]
|
||||||
|
public long? XTime { get; set; }
|
||||||
|
[NotMapped]
|
||||||
|
public DateTime? ExportedAt {
|
||||||
|
get => XTime == null ? null : DateTimeOffset.FromUnixTimeSeconds(XTime.Value).LocalDateTime;
|
||||||
|
set => XTime = value == null ? null : ((DateTimeOffset)value.Value.ToUniversalTime()).ToUnixTimeSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Column("itime")]
|
||||||
|
public long? ITime { get; set; }
|
||||||
|
[NotMapped]
|
||||||
|
public DateTime? ImportedAt {
|
||||||
|
get => ITime == null ? null : DateTimeOffset.FromUnixTimeSeconds(ITime.Value).LocalDateTime;
|
||||||
|
set => ITime = value == null ? null : ((DateTimeOffset)value.Value.ToUniversalTime()).ToUnixTimeSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
[ForeignKey("Year")]
|
[ForeignKey("Year")]
|
||||||
public virtual Season Season { get; private set; } = null!;
|
public virtual Season Season { get; private set; } = null!;
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ namespace Elwig.Models.Entities {
|
|||||||
[Table("wb_gl"), PrimaryKey("GlNr")]
|
[Table("wb_gl"), PrimaryKey("GlNr")]
|
||||||
public class WbGl {
|
public class WbGl {
|
||||||
[Column("glnr")]
|
[Column("glnr")]
|
||||||
public int GlNr { get; private set; }
|
public int GlNr { get; set; }
|
||||||
|
|
||||||
[Column("name")]
|
[Column("name")]
|
||||||
public string Name { get; private set; } = null!;
|
public string Name { get; set; } = null!;
|
||||||
|
|
||||||
[InverseProperty(nameof(WbKg.Gl))]
|
[InverseProperty(nameof(WbKg.Gl))]
|
||||||
public virtual ICollection<WbKg> Kgs { get; private set; } = null!;
|
public virtual ICollection<WbKg> Kgs { get; private set; } = null!;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace Elwig.Models.Entities {
|
|||||||
public virtual AT_Kg AtKg { get; private set; } = null!;
|
public virtual AT_Kg AtKg { get; private set; } = null!;
|
||||||
|
|
||||||
[ForeignKey("GlNr")]
|
[ForeignKey("GlNr")]
|
||||||
public virtual WbGl Gl { get; private set; } = null!;
|
public virtual WbGl? Gl { get; private set; } = null!;
|
||||||
|
|
||||||
[InverseProperty(nameof(WbRd.Kg))]
|
[InverseProperty(nameof(WbRd.Kg))]
|
||||||
public virtual ICollection<WbRd> Rds { get; private set; } = null!;
|
public virtual ICollection<WbRd> Rds { get; private set; } = null!;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using System.Windows.Media;
|
||||||
|
|
||||||
namespace Elwig.Models.Entities {
|
namespace Elwig.Models.Entities {
|
||||||
[Table("wine_variety"), PrimaryKey("SortId")]
|
[Table("wine_variety"), PrimaryKey("SortId")]
|
||||||
@@ -10,22 +11,32 @@ namespace Elwig.Models.Entities {
|
|||||||
[Column("type")]
|
[Column("type")]
|
||||||
public string Type { get; private set; } = null!;
|
public string Type { get; private set; } = null!;
|
||||||
|
|
||||||
|
[Column("max_qualid")]
|
||||||
|
public string MaxQualId { get; private set; } = null!;
|
||||||
|
|
||||||
|
[ForeignKey("MaxQualId")]
|
||||||
|
public virtual WineQualLevel MaxQualityLevel { get; private set; } = null!;
|
||||||
|
|
||||||
[Column("name")]
|
[Column("name")]
|
||||||
public string Name { get; private set; } = null!;
|
public string Name { get; private set; } = null!;
|
||||||
|
|
||||||
[Column("comment")]
|
[Column("comment")]
|
||||||
public string? Comment { get; private set; }
|
public string? Comment { get; private set; }
|
||||||
|
|
||||||
|
public string SortIdFormat => IsQuw ? SortId : $"({SortId})";
|
||||||
public string CommentFormat => (Comment != null) ? $" ({Comment})" : "";
|
public string CommentFormat => (Comment != null) ? $" ({Comment})" : "";
|
||||||
|
|
||||||
public bool IsRed => Type == "R";
|
public bool IsRed => Type == "R";
|
||||||
public bool IsWhite => Type == "W";
|
public bool IsWhite => Type == "W";
|
||||||
|
public bool IsQuw => MaxQualId == "QUW";
|
||||||
|
public Brush? Color => IsWhite ? Brushes.DarkGreen : IsRed ? Brushes.DarkRed : null;
|
||||||
|
|
||||||
public WineVar() { }
|
public WineVar() { }
|
||||||
|
|
||||||
public WineVar(string sortId, string name) {
|
public WineVar(string sortId, string name) {
|
||||||
SortId = sortId;
|
SortId = sortId;
|
||||||
Name = name;
|
Name = name;
|
||||||
|
MaxQualId = "QUW";
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString() {
|
public override string ToString() {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
|||||||
<PublishDir>bin\Publish</PublishDir>
|
<PublishDir>bin\Publish</PublishDir>
|
||||||
<PublishProtocol>FileSystem</PublishProtocol>
|
<PublishProtocol>FileSystem</PublishProtocol>
|
||||||
<_TargetId>Folder</_TargetId>
|
<_TargetId>Folder</_TargetId>
|
||||||
<TargetFramework>net8.0-windows</TargetFramework>
|
<TargetFramework>net10.0-windows</TargetFramework>
|
||||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||||
<SelfContained>true</SelfContained>
|
<SelfContained>true</SelfContained>
|
||||||
<PublishSingleFile>false</PublishSingleFile>
|
<PublishSingleFile>false</PublishSingleFile>
|
||||||
|
|||||||
33
Elwig/Resources/Sql/31-32.sql
Normal file
33
Elwig/Resources/Sql/31-32.sql
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
-- schema version 31 to 32
|
||||||
|
|
||||||
|
INSERT INTO client_parameter (param, value) VALUES ('ENABLE_TIME_TRIGGERS', '1');
|
||||||
|
|
||||||
|
ALTER TABLE member ADD COLUMN xtime INTEGER DEFAULT NULL;
|
||||||
|
ALTER TABLE member ADD COLUMN itime INTEGER DEFAULT NULL;
|
||||||
|
ALTER TABLE area_commitment ADD COLUMN xtime INTEGER DEFAULT NULL;
|
||||||
|
ALTER TABLE area_commitment ADD COLUMN itime INTEGER DEFAULT NULL;
|
||||||
|
ALTER TABLE delivery_announcement ADD COLUMN xtime INTEGER DEFAULT NULL;
|
||||||
|
ALTER TABLE delivery_announcement ADD COLUMN itime INTEGER DEFAULT NULL;
|
||||||
|
ALTER TABLE delivery ADD COLUMN xtime INTEGER DEFAULT NULL;
|
||||||
|
ALTER TABLE delivery ADD COLUMN itime INTEGER DEFAULT NULL;
|
||||||
|
ALTER TABLE delivery_part ADD COLUMN xtime INTEGER DEFAULT NULL;
|
||||||
|
ALTER TABLE delivery_part ADD COLUMN itime INTEGER DEFAULT NULL;
|
||||||
|
ALTER TABLE payment_variant ADD COLUMN xtime INTEGER DEFAULT NULL;
|
||||||
|
ALTER TABLE payment_variant ADD COLUMN itime INTEGER DEFAULT NULL;
|
||||||
|
ALTER TABLE credit ADD COLUMN xtime INTEGER DEFAULT NULL;
|
||||||
|
ALTER TABLE credit ADD COLUMN itime INTEGER DEFAULT NULL;
|
||||||
|
|
||||||
|
PRAGMA writable_schema = ON;
|
||||||
|
|
||||||
|
UPDATE sqlite_schema SET sql = REPLACE(REPLACE(sql,
|
||||||
|
' WHEN',
|
||||||
|
' WHEN (SELECT value FROM client_parameter WHERE param = ''ENABLE_TIME_TRIGGERS'') = 1 AND'),
|
||||||
|
'FOR EACH ROW' || char(10) ||
|
||||||
|
'BEGIN',
|
||||||
|
'FOR EACH ROW' || char(10) ||
|
||||||
|
' WHEN (SELECT value FROM client_parameter WHERE param = ''ENABLE_TIME_TRIGGERS'') = 1' || char(10) ||
|
||||||
|
'BEGIN')
|
||||||
|
WHERE type = 'trigger' AND name LIKE '%time%';
|
||||||
|
|
||||||
|
PRAGMA writable_schema = OFF;
|
||||||
|
PRAGMA schema_version = 3101;
|
||||||
17
Elwig/Resources/Sql/32-33.sql
Normal file
17
Elwig/Resources/Sql/32-33.sql
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
-- schema version 32 to 33
|
||||||
|
|
||||||
|
ALTER TABLE wine_variety ADD COLUMN max_qualid TEXT NOT NULL DEFAULT 'QUW';
|
||||||
|
UPDATE wine_quality_level SET qualid = 'ALW' WHERE qualid = 'AUL';
|
||||||
|
UPDATE wine_variety SET comment = 'Muscato' WHERE sortid = 'MO';
|
||||||
|
|
||||||
|
INSERT INTO wine_variety (sortid, type, max_qualid, name, comment) VALUES
|
||||||
|
('DR', 'W', 'QUW', 'Donauriesling', NULL),
|
||||||
|
('DV', 'W', 'QUW', 'Donauveltliner', NULL),
|
||||||
|
('BN', 'W', 'RSW', 'Bronner', NULL),
|
||||||
|
('CB', 'W', 'RSW', 'Cabernet Blanc', NULL),
|
||||||
|
('CJ', 'R', 'RSW', 'Cabernet Jura', NULL),
|
||||||
|
('JO', 'W', 'RSW', 'Johanniter', NULL),
|
||||||
|
('OR', 'W', 'RSW', 'Orangetraube', NULL),
|
||||||
|
('PI', 'R', 'RSW', 'Pinot Nova', NULL),
|
||||||
|
('RE', 'R', 'RSW', 'Regent', NULL),
|
||||||
|
('SI', 'W', 'RSW', 'Solaris', NULL);
|
||||||
3
Elwig/Resources/Sql/33-34.sql
Normal file
3
Elwig/Resources/Sql/33-34.sql
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
-- schema version 33 to 34
|
||||||
|
|
||||||
|
ALTER TABLE modifier ADD COLUMN redacted INTEGER NOT NULL CHECK (redacted IN (TRUE, FALSE)) DEFAULT FALSE;
|
||||||
6
Elwig/Resources/Sql/34-35.sql
Normal file
6
Elwig/Resources/Sql/34-35.sql
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
-- schema version 34 to 35
|
||||||
|
|
||||||
|
ALTER TABLE delivery_part ADD COLUMN unloading TEXT DEFAULT NULL;
|
||||||
|
UPDATE delivery_part SET unloading = 'pumped' WHERE lesewagen;
|
||||||
|
UPDATE delivery_part SET unloading = 'box' WHERE (SELECT zwstid IN ('H','S') FROM delivery d WHERE (d.year, d.did) = (delivery_part.year, delivery_part.did));
|
||||||
|
ALTER TABLE delivery_part DROP COLUMN lesewagen;
|
||||||
19
Elwig/Resources/Sql/35-36.sql
Normal file
19
Elwig/Resources/Sql/35-36.sql
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
-- schema version 35 to 36
|
||||||
|
|
||||||
|
CREATE VIEW v_member AS
|
||||||
|
SELECT m.mgnr, m.name,
|
||||||
|
(COALESCE(m.prefix || ' ', '') || m.given_name || COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '')) AS other_names,
|
||||||
|
m.address, p.plz, o.name AS locality,
|
||||||
|
a.name AS billing_name, a.address AS billing_address, p2.plz AS billing_plz, o2.name AS billing_locality,
|
||||||
|
k.name AS default_kg,
|
||||||
|
GROUP_CONCAT(e.address, ', ') AS email_addresses
|
||||||
|
FROM member m
|
||||||
|
LEFT JOIN AT_plz_dest p ON p.id = m.postal_dest
|
||||||
|
LEFT JOIN AT_ort o ON o.okz = p.okz
|
||||||
|
LEFT JOIN member_billing_address a ON a.mgnr = m.mgnr
|
||||||
|
LEFT JOIN AT_plz_dest p2 ON p2.id = a.postal_dest
|
||||||
|
LEFT JOIN AT_ort o2 ON o2.okz = p2.okz
|
||||||
|
LEFT JOIN AT_kg k ON k.kgnr = m.default_kgnr
|
||||||
|
LEFT JOIN member_email_address e ON e.mgnr = m.mgnr
|
||||||
|
GROUP BY m.mgnr
|
||||||
|
ORDER BY m.mgnr;
|
||||||
@@ -106,12 +106,13 @@ namespace Elwig.Services {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<int> UpdateAreaCommitment(this AreaComAdminViewModel vm, int? oldFbNr) {
|
public static async Task<int> UpdateAreaCommitment(this AreaComAdminViewModel vm, int? oldFbNr) {
|
||||||
int newFbNr = (int)vm.FbNr!;
|
int newFbNr = vm.FbNr!.Value;
|
||||||
|
|
||||||
using (var ctx = new AppDbContext()) {
|
return await Task.Run(async () => {
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
var a = new AreaCom {
|
var a = new AreaCom {
|
||||||
FbNr = oldFbNr ?? newFbNr,
|
FbNr = oldFbNr ?? newFbNr,
|
||||||
MgNr = (int)vm.MgNr!,
|
MgNr = vm.MgNr!.Value,
|
||||||
YearFrom = vm.YearFrom,
|
YearFrom = vm.YearFrom,
|
||||||
YearTo = vm.YearTo,
|
YearTo = vm.YearTo,
|
||||||
VtrgId = vm.AreaComType!.VtrgId,
|
VtrgId = vm.AreaComType!.VtrgId,
|
||||||
@@ -119,8 +120,8 @@ namespace Elwig.Services {
|
|||||||
Comment = string.IsNullOrEmpty(vm.Comment) ? null : vm.Comment,
|
Comment = string.IsNullOrEmpty(vm.Comment) ? null : vm.Comment,
|
||||||
KgNr = vm.Kg!.KgNr,
|
KgNr = vm.Kg!.KgNr,
|
||||||
RdNr = vm.Rd?.RdNr,
|
RdNr = vm.Rd?.RdNr,
|
||||||
GstNr = vm.GstNr!.Trim(),
|
GstNr = vm.GstNr?.Trim() ?? "-",
|
||||||
Area = (int)vm.Area!,
|
Area = vm.Area!.Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (vm.Rd?.RdNr == 0) {
|
if (vm.Rd?.RdNr == 0) {
|
||||||
@@ -140,11 +141,9 @@ namespace Elwig.Services {
|
|||||||
if (newFbNr != a.FbNr) {
|
if (newFbNr != a.FbNr) {
|
||||||
await ctx.Database.ExecuteSqlAsync($"UPDATE area_commitment SET fbnr = {newFbNr} WHERE fbnr = {oldFbNr}");
|
await ctx.Database.ExecuteSqlAsync($"UPDATE area_commitment SET fbnr = {newFbNr} WHERE fbnr = {oldFbNr}");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
App.HintContextChange();
|
return newFbNr;
|
||||||
|
});
|
||||||
return newFbNr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AddToolTipCell(Grid grid, string text, int row, int col, int colSpan = 1, bool bold = false, bool alignRight = false, bool alignCenter = false) {
|
private static void AddToolTipCell(Grid grid, string text, int row, int col, int colSpan = 1, bool bold = false, bool alignRight = false, bool alignCenter = false) {
|
||||||
@@ -253,5 +252,14 @@ namespace Elwig.Services {
|
|||||||
}
|
}
|
||||||
return grid;
|
return grid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task DeleteAreaCom(int fbnr) {
|
||||||
|
await Task.Run(async () => {
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
|
var l = (await ctx.AreaCommitments.FindAsync(fbnr))!;
|
||||||
|
ctx.Remove(l);
|
||||||
|
await ctx.SaveChangesAsync();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ namespace Elwig.Services {
|
|||||||
vm.DeliverySchedule = (DeliverySchedule?)ControlUtils.GetItemFromSourceWithPk(vm.DeliveryScheduleSource, a.Year, a.DsNr);
|
vm.DeliverySchedule = (DeliverySchedule?)ControlUtils.GetItemFromSourceWithPk(vm.DeliveryScheduleSource, a.Year, a.DsNr);
|
||||||
vm.SortId = a.SortId;
|
vm.SortId = a.SortId;
|
||||||
vm.Weight = a.Weight;
|
vm.Weight = a.Weight;
|
||||||
vm.StatusAncmtCreated = $"{a.CreatedTimestamp:dd.MM.yyyy, HH:mm} ({a.Type})";
|
vm.StatusAncmtCreated = $"{a.CreatedAt:dd.MM.yyyy, HH:mm} ({a.Type})";
|
||||||
vm.StatusAncmtModified = a.ModifiedTimestamp != a.CreatedTimestamp ? $"{a.ModifiedTimestamp:dd.MM.yyyy, HH:mm}" : "-";
|
vm.StatusAncmtModified = a.ModifiedAt != a.CreatedAt ? $"{a.ModifiedAt:dd.MM.yyyy, HH:mm}" : "-";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<(List<string>, IQueryable<DeliveryAncmt>, List<string>)> GetFilters(this DeliveryAncmtAdminViewModel vm, AppDbContext ctx) {
|
public static async Task<(List<string>, IQueryable<DeliveryAncmt>, List<string>)> GetFilters(this DeliveryAncmtAdminViewModel vm, AppDbContext ctx) {
|
||||||
@@ -211,7 +211,8 @@ namespace Elwig.Services {
|
|||||||
int newMgNr = vm.MgNr!.Value;
|
int newMgNr = vm.MgNr!.Value;
|
||||||
string newSortId = vm.SortId!;
|
string newSortId = vm.SortId!;
|
||||||
|
|
||||||
using (var ctx = new AppDbContext()) {
|
return await Task.Run(async () => {
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
var a = new DeliveryAncmt {
|
var a = new DeliveryAncmt {
|
||||||
Year = oldYear ?? year,
|
Year = oldYear ?? year,
|
||||||
DsNr = oldDsNr ?? dsnr,
|
DsNr = oldDsNr ?? dsnr,
|
||||||
@@ -232,11 +233,9 @@ namespace Elwig.Services {
|
|||||||
if (oldDsNr != null && (oldYear != year || oldDsNr != dsnr || oldMgNr != newMgNr || oldSortId != newSortId)) {
|
if (oldDsNr != null && (oldYear != year || oldDsNr != dsnr || oldMgNr != newMgNr || oldSortId != newSortId)) {
|
||||||
await ctx.Database.ExecuteSqlAsync($"UPDATE delivery_announcement SET year = {year}, dsnr = {dsnr}, mgnr = {newMgNr}, sortid = {newSortId} WHERE (year, dsnr, mgnr, sortid) = ({a.Year}, {a.DsNr}, {a.MgNr}, {a.SortId})");
|
await ctx.Database.ExecuteSqlAsync($"UPDATE delivery_announcement SET year = {year}, dsnr = {dsnr}, mgnr = {newMgNr}, sortid = {newSortId} WHERE (year, dsnr, mgnr, sortid) = ({a.Year}, {a.DsNr}, {a.MgNr}, {a.SortId})");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
App.HintContextChange();
|
return (year, dsnr, newMgNr, newSortId);
|
||||||
|
});
|
||||||
return (year, dsnr, newMgNr, newSortId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task GenerateDeliveryAncmtList(this DeliveryAncmtAdminViewModel vm, ExportSubject subject, ExportMode mode) {
|
public static async Task GenerateDeliveryAncmtList(this DeliveryAncmtAdminViewModel vm, ExportSubject subject, ExportMode mode) {
|
||||||
@@ -269,25 +268,29 @@ namespace Elwig.Services {
|
|||||||
Title = $"{DeliveryAncmtList.Name} speichern unter - Elwig"
|
Title = $"{DeliveryAncmtList.Name} speichern unter - Elwig"
|
||||||
};
|
};
|
||||||
if (d.ShowDialog() == true) {
|
if (d.ShowDialog() == true) {
|
||||||
Mouse.OverrideCursor = Cursors.AppStarting;
|
Mouse.OverrideCursor = Cursors.Wait;
|
||||||
try {
|
await Task.Run(async () => {
|
||||||
var data = await DeliveryAncmtListData.FromQuery(query, filterNames);
|
try {
|
||||||
using var ods = new OdsFile(d.FileName);
|
var data = await DeliveryAncmtListData.FromQuery(query, filterNames);
|
||||||
await ods.AddTable(data);
|
using var ods = new OdsFile(d.FileName);
|
||||||
} catch (Exception exc) {
|
await ods.AddTable(data);
|
||||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
} catch (Exception exc) {
|
||||||
}
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
});
|
||||||
Mouse.OverrideCursor = null;
|
Mouse.OverrideCursor = null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Mouse.OverrideCursor = Cursors.AppStarting;
|
Mouse.OverrideCursor = Cursors.Wait;
|
||||||
try {
|
await Task.Run(async () => {
|
||||||
var data = await DeliveryAncmtListData.FromQuery(query, filterNames);
|
try {
|
||||||
using var doc = new DeliveryAncmtList(string.Join(" / ", filterNames), data);
|
var data = await DeliveryAncmtListData.FromQuery(query, filterNames);
|
||||||
await Utils.ExportDocument(doc, mode);
|
using var doc = new DeliveryAncmtList(string.Join(" / ", filterNames), data);
|
||||||
} catch (Exception exc) {
|
await Utils.ExportDocument(doc, mode);
|
||||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
} catch (Exception exc) {
|
||||||
}
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
});
|
||||||
Mouse.OverrideCursor = null;
|
Mouse.OverrideCursor = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -326,7 +329,7 @@ namespace Elwig.Services {
|
|||||||
|
|
||||||
var weight = await deliveryAncmts.SumAsync(p => p.Weight);
|
var weight = await deliveryAncmts.SumAsync(p => p.Weight);
|
||||||
text = $"{weight:N0} kg";
|
text = $"{weight:N0} kg";
|
||||||
AddToolTipRow(grid, 0, "Gewicht", null, weight, null, weight);
|
AddToolTipRow(grid, 0, "Menge", null, weight, null, weight);
|
||||||
|
|
||||||
if (await deliveryAncmts.AnyAsync()) {
|
if (await deliveryAncmts.AnyAsync()) {
|
||||||
var attrGroups = await deliveryAncmts
|
var attrGroups = await deliveryAncmts
|
||||||
|
|||||||
@@ -152,7 +152,8 @@ namespace Elwig.Services {
|
|||||||
public static async Task UpdateDeliverySchedule(this DeliveryScheduleAdminViewModel vm, int? oldYear, int? oldDsNr) {
|
public static async Task UpdateDeliverySchedule(this DeliveryScheduleAdminViewModel vm, int? oldYear, int? oldDsNr) {
|
||||||
int year = vm.Date!.Value.Year;
|
int year = vm.Date!.Value.Year;
|
||||||
|
|
||||||
using (var ctx = new AppDbContext()) {
|
await Task.Run(async () => {
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
var s = new DeliverySchedule {
|
var s = new DeliverySchedule {
|
||||||
Year = oldYear ?? year,
|
Year = oldYear ?? year,
|
||||||
DsNr = oldDsNr ?? await ctx.NextDsNr(year),
|
DsNr = oldDsNr ?? await ctx.NextDsNr(year),
|
||||||
@@ -181,9 +182,7 @@ namespace Elwig.Services {
|
|||||||
.ToList(), vm.MainVarieties.Select(v => (v, 1)).Union(vm.OtherVarieties.Select(v => (v, 2))).ToList());
|
.ToList(), vm.MainVarieties.Select(v => (v, 1)).Union(vm.OtherVarieties.Select(v => (v, 2))).ToList());
|
||||||
|
|
||||||
await ctx.SaveChangesAsync();
|
await ctx.SaveChangesAsync();
|
||||||
}
|
});
|
||||||
|
|
||||||
App.HintContextChange();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,10 +15,8 @@ using LinqKit;
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System.IO;
|
|
||||||
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Net.Http;
|
|
||||||
|
|
||||||
namespace Elwig.Services {
|
namespace Elwig.Services {
|
||||||
public static class DeliveryService {
|
public static class DeliveryService {
|
||||||
@@ -80,9 +78,9 @@ namespace Elwig.Services {
|
|||||||
vm.PartComment = p.Comment ?? "";
|
vm.PartComment = p.Comment ?? "";
|
||||||
vm.TemperatureString = (p.Temperature != null) ? $"{p.Temperature:N1}" : "";
|
vm.TemperatureString = (p.Temperature != null) ? $"{p.Temperature:N1}" : "";
|
||||||
vm.AcidString = (p.Acid != null) ? $"{p.Acid:N1}" : "";
|
vm.AcidString = (p.Acid != null) ? $"{p.Acid:N1}" : "";
|
||||||
vm.IsLesewagen = p.IsLesewagen ?? false;
|
|
||||||
vm.IsHandPicked = p.IsHandPicked;
|
vm.IsHandPicked = p.IsHandPicked;
|
||||||
vm.IsGebunden = p.IsGebunden;
|
vm.IsGebunden = p.IsGebunden;
|
||||||
|
vm.Unloading = p.Unloading;
|
||||||
|
|
||||||
vm.ScaleId = p.ScaleId;
|
vm.ScaleId = p.ScaleId;
|
||||||
vm.WeighingData = p.WeighingData;
|
vm.WeighingData = p.WeighingData;
|
||||||
@@ -188,14 +186,54 @@ namespace Elwig.Services {
|
|||||||
prd = prd.And(p => p.IsNetWeight == true);
|
prd = prd.And(p => p.IsNetWeight == true);
|
||||||
filter.RemoveAt(i--);
|
filter.RemoveAt(i--);
|
||||||
filterNames.Add("gerebelt gewogen");
|
filterNames.Add("gerebelt gewogen");
|
||||||
|
} else if (e.Length >= 5 && e.Length <= 11 && "planenwagen".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
||||||
|
prd = prd.And(p => p.Unloading == DeliveryPart.Dumper);
|
||||||
|
filter.RemoveAt(i--);
|
||||||
|
filterNames.Add("Planenw./Kipper");
|
||||||
|
} else if (e.Length >= 6 && e.Length <= 12 && "!planenwagen".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
||||||
|
prd = prd.And(p => p.Unloading != DeliveryPart.Dumper);
|
||||||
|
filter.RemoveAt(i--);
|
||||||
|
filterNames.Add("kein Planenw./Kipper");
|
||||||
|
} else if (e.Length >= 4 && e.Length <= 6 && "kipper".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
||||||
|
prd = prd.And(p => p.Unloading == DeliveryPart.Dumper);
|
||||||
|
filter.RemoveAt(i--);
|
||||||
|
filterNames.Add("Planenw./Kipper");
|
||||||
|
} else if (e.Length >= 5 && e.Length <= 7 && "!kipper".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
||||||
|
prd = prd.And(p => p.Unloading != DeliveryPart.Dumper);
|
||||||
|
filter.RemoveAt(i--);
|
||||||
|
filterNames.Add("kein Planenw./Kipper");
|
||||||
} else if (e.Length >= 5 && e.Length <= 9 && "lesewagen".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
} else if (e.Length >= 5 && e.Length <= 9 && "lesewagen".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
||||||
prd = prd.And(p => p.IsLesewagen == true);
|
prd = prd.And(p => p.Unloading == DeliveryPart.Pumped);
|
||||||
filter.RemoveAt(i--);
|
filter.RemoveAt(i--);
|
||||||
filterNames.Add("Lesewagen");
|
filterNames.Add("Lesewagen");
|
||||||
} else if (e.Length >= 6 && e.Length <= 10 && "!lesewagen".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
} else if (e.Length >= 6 && e.Length <= 10 && "!lesewagen".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
||||||
prd = prd.And(p => p.IsLesewagen == false);
|
prd = prd.And(p => p.Unloading != DeliveryPart.Pumped);
|
||||||
filter.RemoveAt(i--);
|
filter.RemoveAt(i--);
|
||||||
filterNames.Add("kein Lesewagen");
|
filterNames.Add("kein Lesewagen");
|
||||||
|
} else if (e.Length >= 5 && e.Length <= 6 && "kisten".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
||||||
|
prd = prd.And(p => p.Unloading == DeliveryPart.Box);
|
||||||
|
filter.RemoveAt(i--);
|
||||||
|
filterNames.Add("Kisten");
|
||||||
|
} else if (e.Length >= 6 && e.Length <= 7 && "!kisten".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
||||||
|
prd = prd.And(p => p.Unloading != DeliveryPart.Box);
|
||||||
|
filter.RemoveAt(i--);
|
||||||
|
filterNames.Add("keine Kisten");
|
||||||
|
} else if ("upload".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
||||||
|
prd = prd.And(p => (p.Delivery.XTime == null || p.Delivery.MTime > p.Delivery.XTime) && (p.Delivery.ITime == null || p.Delivery.MTime > p.Delivery.ITime));
|
||||||
|
filter.RemoveAt(i--);
|
||||||
|
filterNames.Add("geändert seit letztem Export");
|
||||||
|
} else if ("!upload".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
||||||
|
prd = prd.And(p => !((p.Delivery.XTime == null || p.Delivery.MTime > p.Delivery.XTime) && (p.Delivery.ITime == null || p.Delivery.MTime > p.Delivery.ITime)));
|
||||||
|
filter.RemoveAt(i--);
|
||||||
|
filterNames.Add("unverändert seit letztem Export");
|
||||||
|
} else if (">import".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
||||||
|
prd = prd.And(p => p.ITime != null && p.Delivery.MTime > p.Delivery.ITime);
|
||||||
|
filter.RemoveAt(i--);
|
||||||
|
filterNames.Add("geändert seit letztem Import");
|
||||||
|
} else if ("<import".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
||||||
|
prd = prd.And(p => p.Delivery.MTime <= p.Delivery.ITime);
|
||||||
|
filter.RemoveAt(i--);
|
||||||
|
filterNames.Add("unverändert seit letztem Import");
|
||||||
} else if (e.Length == 2 && var.ContainsKey(e.ToUpper())) {
|
} else if (e.Length == 2 && var.ContainsKey(e.ToUpper())) {
|
||||||
filterVar.Add(e.ToUpper());
|
filterVar.Add(e.ToUpper());
|
||||||
filter.RemoveAt(i--);
|
filter.RemoveAt(i--);
|
||||||
@@ -430,9 +468,10 @@ namespace Elwig.Services {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<DeliveryPart> UpdateDeliveryPart(this DeliveryAdminViewModel vm, int? oldYear, int? oldDid, int? oldDpnr, bool dateHasChanged, bool timeHasChanged, bool timeIsDefault) {
|
public static async Task<DeliveryPart> UpdateDeliveryPart(this DeliveryAdminViewModel vm, int? oldYear, int? oldDid, int? oldDpnr, bool dateHasChanged, bool timeHasChanged, bool timeIsDefault) {
|
||||||
DeliveryPart p;
|
return await Task.Run(async () => {
|
||||||
|
DeliveryPart p;
|
||||||
|
|
||||||
using (var ctx = new AppDbContext()) {
|
using var ctx = new AppDbContext();
|
||||||
int year = oldYear ?? Utils.CurrentYear;
|
int year = oldYear ?? Utils.CurrentYear;
|
||||||
int did = oldDid ?? await ctx.NextDId(year);
|
int did = oldDid ?? await ctx.NextDId(year);
|
||||||
int dpnr = oldDpnr ?? await ctx.NextDPNr(year, did);
|
int dpnr = oldDpnr ?? await ctx.NextDPNr(year, did);
|
||||||
@@ -482,8 +521,8 @@ namespace Elwig.Services {
|
|||||||
|
|
||||||
IsNetWeight = vm.IsNetWeight,
|
IsNetWeight = vm.IsNetWeight,
|
||||||
IsHandPicked = vm.IsHandPicked,
|
IsHandPicked = vm.IsHandPicked,
|
||||||
IsLesewagen = vm.IsLesewagen,
|
|
||||||
IsGebunden = vm.IsGebunden,
|
IsGebunden = vm.IsGebunden,
|
||||||
|
Unloading = vm.Unloading,
|
||||||
Temperature = vm.Temperature,
|
Temperature = vm.Temperature,
|
||||||
Acid = vm.Acid,
|
Acid = vm.Acid,
|
||||||
Comment = string.IsNullOrEmpty(vm.PartComment) ? null : vm.PartComment,
|
Comment = string.IsNullOrEmpty(vm.PartComment) ? null : vm.PartComment,
|
||||||
@@ -524,17 +563,16 @@ namespace Elwig.Services {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await ctx.SaveChangesAsync();
|
await ctx.SaveChangesAsync();
|
||||||
}
|
|
||||||
|
|
||||||
App.HintContextChange();
|
return p;
|
||||||
|
});
|
||||||
return p;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<Delivery> SplitDeliveryToMember(int year, int did, int[] weights, int mgnr) {
|
public static async Task<Delivery> SplitDeliveryToMember(int year, int did, int[] weights, int mgnr) {
|
||||||
Delivery n;
|
return await Task.Run(async () => {
|
||||||
|
Delivery n;
|
||||||
|
|
||||||
using (var ctx = new AppDbContext()) {
|
using var ctx = new AppDbContext();
|
||||||
bool anyLeft = false;
|
bool anyLeft = false;
|
||||||
var d = (await ctx.Deliveries.FindAsync(year, did))!;
|
var d = (await ctx.Deliveries.FindAsync(year, did))!;
|
||||||
var lnr = await ctx.NextLNr(d.Date, d.ZwstId);
|
var lnr = await ctx.NextLNr(d.Date, d.ZwstId);
|
||||||
@@ -571,23 +609,28 @@ namespace Elwig.Services {
|
|||||||
s.DPNr = dpnr++;
|
s.DPNr = dpnr++;
|
||||||
s.Weight = w;
|
s.Weight = w;
|
||||||
ctx.Add(s);
|
ctx.Add(s);
|
||||||
|
|
||||||
|
ctx.AddRange(p.PartModifiers.Select(m => new DeliveryPartModifier() {
|
||||||
|
Year = s.Year,
|
||||||
|
DId = s.DId,
|
||||||
|
DPNr = s.DPNr,
|
||||||
|
ModId = m.ModId,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await ctx.SaveChangesAsync();
|
await ctx.SaveChangesAsync();
|
||||||
if (!anyLeft)
|
if (!anyLeft)
|
||||||
await ctx.Database.ExecuteSqlAsync($"DELETE FROM delivery WHERE (year, did) = ({d.Year}, {d.DId})");
|
await ctx.Database.ExecuteSqlAsync($"DELETE FROM delivery WHERE (year, did) = ({d.Year}, {d.DId})");
|
||||||
}
|
|
||||||
|
|
||||||
App.HintContextChange();
|
return n;
|
||||||
|
});
|
||||||
return n;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<Delivery> SplitDeliveryToLsNr(int year, int did, int[] weights, string lsnr) {
|
public static async Task<Delivery> SplitDeliveryToLsNr(int year, int did, int[] weights, string lsnr) {
|
||||||
Delivery n;
|
return await Task.Run(async () => {
|
||||||
|
Delivery n;
|
||||||
using (var ctx = new AppDbContext()) {
|
using var ctx = new AppDbContext();
|
||||||
var anyLeft = false;
|
var anyLeft = false;
|
||||||
n = (await ctx.Deliveries.FirstAsync(d => d.LsNr == lsnr))!;
|
n = (await ctx.Deliveries.FirstAsync(d => d.LsNr == lsnr))!;
|
||||||
var d = (await ctx.Deliveries.FindAsync(year, did))!;
|
var d = (await ctx.Deliveries.FindAsync(year, did))!;
|
||||||
@@ -610,21 +653,27 @@ namespace Elwig.Services {
|
|||||||
s.DPNr = dpnr++;
|
s.DPNr = dpnr++;
|
||||||
s.Weight = w;
|
s.Weight = w;
|
||||||
ctx.Add(s);
|
ctx.Add(s);
|
||||||
|
|
||||||
|
ctx.AddRange(p.PartModifiers.Select(m => new DeliveryPartModifier() {
|
||||||
|
Year = s.Year,
|
||||||
|
DId = s.DId,
|
||||||
|
DPNr = s.DPNr,
|
||||||
|
ModId = m.ModId,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await ctx.SaveChangesAsync();
|
await ctx.SaveChangesAsync();
|
||||||
if (!anyLeft && n.LsNr != d.LsNr)
|
if (!anyLeft && n.LsNr != d.LsNr)
|
||||||
await ctx.Database.ExecuteSqlAsync($"DELETE FROM delivery WHERE (year, did) = ({d.Year}, {d.DId})");
|
await ctx.Database.ExecuteSqlAsync($"DELETE FROM delivery WHERE (year, did) = ({d.Year}, {d.DId})");
|
||||||
}
|
|
||||||
|
|
||||||
App.HintContextChange();
|
return n;
|
||||||
|
});
|
||||||
return n;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task DepreciateDelivery(int year, int did, int[] weights) {
|
public static async Task DepreciateDelivery(int year, int did, int[] weights) {
|
||||||
using (var ctx = new AppDbContext()) {
|
await Task.Run(async () => {
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
var d = (await ctx.Deliveries.FindAsync(year, did))!;
|
var d = (await ctx.Deliveries.FindAsync(year, did))!;
|
||||||
var dpnr = await ctx.NextDPNr(year, did);
|
var dpnr = await ctx.NextDPNr(year, did);
|
||||||
foreach (var (p, w) in d.Parts.ToList().Zip(weights)) {
|
foreach (var (p, w) in d.Parts.ToList().Zip(weights)) {
|
||||||
@@ -645,24 +694,31 @@ namespace Elwig.Services {
|
|||||||
n.QualId = "WEI";
|
n.QualId = "WEI";
|
||||||
n.HkId = "OEST";
|
n.HkId = "OEST";
|
||||||
ctx.Add(n);
|
ctx.Add(n);
|
||||||
|
|
||||||
|
ctx.AddRange(p.PartModifiers.Select(m => new DeliveryPartModifier() {
|
||||||
|
Year = n.Year,
|
||||||
|
DId = n.DId,
|
||||||
|
DPNr = n.DPNr,
|
||||||
|
ModId = m.ModId,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await ctx.SaveChangesAsync();
|
await ctx.SaveChangesAsync();
|
||||||
}
|
});
|
||||||
|
|
||||||
App.HintContextChange();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task GenerateDeliveryNote(int year, int did, ExportMode mode) {
|
public static async Task GenerateDeliveryNote(int year, int did, ExportMode mode) {
|
||||||
Mouse.OverrideCursor = Cursors.AppStarting;
|
Mouse.OverrideCursor = Cursors.Wait;
|
||||||
try {
|
await Task.Run(async () => {
|
||||||
using var ctx = new AppDbContext();
|
try {
|
||||||
var d = (await ctx.Deliveries.FindAsync(year, did))!;
|
using var ctx = new AppDbContext();
|
||||||
using var doc = new DeliveryNote(d, ctx);
|
var d = (await ctx.Deliveries.FindAsync(year, did))!;
|
||||||
await Utils.ExportDocument(doc, mode, d.LsNr, (d.Member, $"{DeliveryNote.Name} Nr. {d.LsNr}", $"Im Anhang finden Sie den {DeliveryNote.Name} Nr. {d.LsNr}"));
|
using var doc = new DeliveryNote(d, ctx);
|
||||||
} catch (Exception exc) {
|
await Utils.ExportDocument(doc, mode, d.LsNr, (d.Member, $"{DeliveryNote.Name} Nr. {d.LsNr}", $"Im Anhang finden Sie den {DeliveryNote.Name} Nr. {d.LsNr}"));
|
||||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
} catch (Exception exc) {
|
||||||
}
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
});
|
||||||
Mouse.OverrideCursor = null;
|
Mouse.OverrideCursor = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -707,14 +763,16 @@ namespace Elwig.Services {
|
|||||||
Title = $"{DeliveryJournal.Name} speichern unter - Elwig"
|
Title = $"{DeliveryJournal.Name} speichern unter - Elwig"
|
||||||
};
|
};
|
||||||
if (d.ShowDialog() == true) {
|
if (d.ShowDialog() == true) {
|
||||||
Mouse.OverrideCursor = Cursors.AppStarting;
|
Mouse.OverrideCursor = Cursors.Wait;
|
||||||
try {
|
await Task.Run(async () => {
|
||||||
var data = await DeliveryJournalData.FromQuery(query, filterNames);
|
try {
|
||||||
using var ods = new OdsFile(d.FileName);
|
var data = await DeliveryJournalData.FromQuery(query, filterNames);
|
||||||
await ods.AddTable(data);
|
using var ods = new OdsFile(d.FileName);
|
||||||
} catch (Exception exc) {
|
await ods.AddTable(data);
|
||||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
} catch (Exception exc) {
|
||||||
}
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
});
|
||||||
Mouse.OverrideCursor = null;
|
Mouse.OverrideCursor = null;
|
||||||
}
|
}
|
||||||
} else if (mode == ExportMode.Export) {
|
} else if (mode == ExportMode.Export) {
|
||||||
@@ -722,61 +780,53 @@ namespace Elwig.Services {
|
|||||||
FileName = subject == ExportSubject.Selected ? $"Lieferung_{vm.SelectedDelivery?.LsNr}.elwig.zip" : $"Lieferungen_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip",
|
FileName = subject == ExportSubject.Selected ? $"Lieferung_{vm.SelectedDelivery?.LsNr}.elwig.zip" : $"Lieferungen_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip",
|
||||||
DefaultExt = "elwig.zip",
|
DefaultExt = "elwig.zip",
|
||||||
Filter = "Elwig-Export-Datei (*.elwig.zip)|*.elwig.zip",
|
Filter = "Elwig-Export-Datei (*.elwig.zip)|*.elwig.zip",
|
||||||
Title = $"{DeliveryJournal.Name} speichern unter - Elwig"
|
Title = $"{DeliveryJournal.Name} speichern unter - Elwig",
|
||||||
|
AddExtension = false,
|
||||||
};
|
};
|
||||||
if (d.ShowDialog() == true) {
|
if (d.ShowDialog() == true) {
|
||||||
Mouse.OverrideCursor = Cursors.AppStarting;
|
if (!d.FileName.EndsWith(".elwig.zip")) d.FileName += ".elwig.zip";
|
||||||
try {
|
Mouse.OverrideCursor = Cursors.Wait;
|
||||||
await ElwigData.Export(d.FileName, await query
|
await Task.Run(async () => {
|
||||||
.Select(p => p.Delivery)
|
try {
|
||||||
.Distinct()
|
var list = await query
|
||||||
.Include(d => d.Parts)
|
.Select(p => p.Delivery)
|
||||||
.ThenInclude(p => p.PartModifiers)
|
.Distinct()
|
||||||
.AsSplitQuery()
|
.Include(d => d.Parts).ThenInclude(p => p.PartModifiers)
|
||||||
.ToListAsync(), filterNames);
|
.Include(d => d.Parts).ThenInclude(p => p.Rd)
|
||||||
} catch (Exception exc) {
|
.Include(d => d.Parts).ThenInclude(p => p.Kg!.Gl)
|
||||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
.AsSplitQuery()
|
||||||
}
|
.ToListAsync();
|
||||||
|
var wbKgs = list
|
||||||
|
.SelectMany(d => d.Parts)
|
||||||
|
.Where(p => p.Kg != null)
|
||||||
|
.Select(p => p.Kg!)
|
||||||
|
.DistinctBy(k => k.KgNr)
|
||||||
|
.OrderBy(k => k.KgNr)
|
||||||
|
.ToList();
|
||||||
|
await ElwigData.Export(d.FileName, list, wbKgs, filterNames);
|
||||||
|
} catch (Exception exc) {
|
||||||
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
});
|
||||||
Mouse.OverrideCursor = null;
|
Mouse.OverrideCursor = null;
|
||||||
}
|
}
|
||||||
} else if (mode == ExportMode.Upload && App.Config.SyncUrl != null) {
|
} else if (mode == ExportMode.Upload && App.Config.SyncUrl != null) {
|
||||||
Mouse.OverrideCursor = Cursors.AppStarting;
|
Mouse.OverrideCursor = Cursors.Wait;
|
||||||
try {
|
await Task.Run(async () => {
|
||||||
var filename = $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip";
|
await SyncService.Upload(App.Config.SyncUrl, App.Config.SyncUrl, App.Config.SyncPassword, query, filterNames);
|
||||||
var path = Path.Combine(App.TempPath, filename);
|
});
|
||||||
var list = await query
|
|
||||||
.Select(p => p.Delivery)
|
|
||||||
.Distinct()
|
|
||||||
.Include(d => d.Parts)
|
|
||||||
.ThenInclude(p => p.PartModifiers)
|
|
||||||
.AsSplitQuery()
|
|
||||||
.ToListAsync();
|
|
||||||
if (list.Count == 0) {
|
|
||||||
MessageBox.Show("Es wurden keine Lieferungen zum Hochladen ausgewählt!", "Lieferungen hochladen",
|
|
||||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
} else {
|
|
||||||
await ElwigData.Export(path, list, filterNames);
|
|
||||||
await Utils.UploadExportData(path, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
|
|
||||||
MessageBox.Show($"Hochladen von {list.Count:N0} Lieferungen erfolgreich!", "Lieferungen hochgeladen",
|
|
||||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
|
||||||
}
|
|
||||||
} catch (HttpRequestException exc) {
|
|
||||||
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
} catch (TaskCanceledException exc) {
|
|
||||||
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
} catch (Exception exc) {
|
|
||||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
}
|
|
||||||
Mouse.OverrideCursor = null;
|
Mouse.OverrideCursor = null;
|
||||||
} else {
|
} else {
|
||||||
Mouse.OverrideCursor = Cursors.AppStarting;
|
Mouse.OverrideCursor = Cursors.Wait;
|
||||||
try {
|
await Task.Run(async () => {
|
||||||
var data = await DeliveryJournalData.FromQuery(query, filterNames);
|
try {
|
||||||
using var doc = new DeliveryJournal(string.Join(" / ", filterNames), data);
|
var data = await DeliveryJournalData.FromQuery(query, filterNames);
|
||||||
await Utils.ExportDocument(doc, mode);
|
using var doc = new DeliveryJournal(string.Join(" / ", filterNames), data);
|
||||||
} catch (Exception exc) {
|
await Utils.ExportDocument(doc, mode);
|
||||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
} catch (Exception exc) {
|
||||||
}
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
});
|
||||||
Mouse.OverrideCursor = null;
|
Mouse.OverrideCursor = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -798,14 +848,16 @@ namespace Elwig.Services {
|
|||||||
throw new ArgumentException("Invalid value for ExportSubject");
|
throw new ArgumentException("Invalid value for ExportSubject");
|
||||||
}
|
}
|
||||||
|
|
||||||
Mouse.OverrideCursor = Cursors.AppStarting;
|
Mouse.OverrideCursor = Cursors.Wait;
|
||||||
try {
|
await Task.Run(async () => {
|
||||||
var data = await WineQualityStatisticsData.FromQuery(query, App.Client.OrderingMemberList);
|
try {
|
||||||
using var doc = new WineQualityStatistics(string.Join(" / ", filterNames), data);
|
var data = await WineQualityStatisticsData.FromQuery(query, App.Client.OrderingMemberList);
|
||||||
await Utils.ExportDocument(doc, mode);
|
using var doc = new WineQualityStatistics(string.Join(" / ", filterNames), data);
|
||||||
} catch (Exception exc) {
|
await Utils.ExportDocument(doc, mode);
|
||||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
} catch (Exception exc) {
|
||||||
}
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
});
|
||||||
Mouse.OverrideCursor = null;
|
Mouse.OverrideCursor = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -829,14 +881,16 @@ namespace Elwig.Services {
|
|||||||
Title = $"Lieferstatistik pro Ort speichern unter - Elwig"
|
Title = $"Lieferstatistik pro Ort speichern unter - Elwig"
|
||||||
};
|
};
|
||||||
if (d.ShowDialog() == true) {
|
if (d.ShowDialog() == true) {
|
||||||
Mouse.OverrideCursor = Cursors.AppStarting;
|
Mouse.OverrideCursor = Cursors.Wait;
|
||||||
try {
|
await Task.Run(async () => {
|
||||||
using var ods = new OdsFile(d.FileName);
|
try {
|
||||||
var tbl = await WineLocalityStatisticsData.FromQuery(query, filterNames);
|
using var ods = new OdsFile(d.FileName);
|
||||||
await ods.AddTable(tbl);
|
var tbl = await WineLocalityStatisticsData.FromQuery(query, filterNames);
|
||||||
} catch (Exception exc) {
|
await ods.AddTable(tbl);
|
||||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
} catch (Exception exc) {
|
||||||
}
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
});
|
||||||
Mouse.OverrideCursor = null;
|
Mouse.OverrideCursor = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -875,33 +929,83 @@ namespace Elwig.Services {
|
|||||||
Title = $"{DeliveryDepreciationList.Name} speichern unter - Elwig"
|
Title = $"{DeliveryDepreciationList.Name} speichern unter - Elwig"
|
||||||
};
|
};
|
||||||
if (d.ShowDialog() == true) {
|
if (d.ShowDialog() == true) {
|
||||||
Mouse.OverrideCursor = Cursors.AppStarting;
|
Mouse.OverrideCursor = Cursors.Wait;
|
||||||
try {
|
await Task.Run(async () => {
|
||||||
using var ods = new OdsFile(d.FileName);
|
try {
|
||||||
var tblTotal = await DeliveryJournalData.FromQuery(query, filterNames);
|
using var ods = new OdsFile(d.FileName);
|
||||||
tblTotal.FullName = DeliveryDepreciationList.Name;
|
var tblTotal = await DeliveryJournalData.FromQuery(query, filterNames);
|
||||||
tblTotal.Name = "Gesamt";
|
tblTotal.FullName = DeliveryDepreciationList.Name;
|
||||||
await ods.AddTable(tblTotal);
|
tblTotal.Name = "Gesamt";
|
||||||
foreach (var branch in await ctx.Branches.OrderBy(b => b.Name).ToListAsync()) {
|
await ods.AddTable(tblTotal);
|
||||||
var tbl = await DeliveryJournalData.FromQuery(query.Where(p => p.Delivery.ZwstId == branch.ZwstId), filterNames);
|
foreach (var branch in await ctx.Branches.OrderBy(b => b.Name).ToListAsync()) {
|
||||||
tbl.FullName = DeliveryDepreciationList.Name;
|
var tbl = await DeliveryJournalData.FromQuery(query.Where(p => p.Delivery.ZwstId == branch.ZwstId), filterNames);
|
||||||
tbl.Name = branch.Name;
|
tbl.FullName = DeliveryDepreciationList.Name;
|
||||||
await ods.AddTable(tbl);
|
tbl.Name = branch.Name;
|
||||||
|
await ods.AddTable(tbl);
|
||||||
|
}
|
||||||
|
} catch (Exception exc) {
|
||||||
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
}
|
}
|
||||||
} catch (Exception exc) {
|
});
|
||||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
}
|
|
||||||
Mouse.OverrideCursor = null;
|
Mouse.OverrideCursor = null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Mouse.OverrideCursor = Cursors.AppStarting;
|
Mouse.OverrideCursor = Cursors.Wait;
|
||||||
try {
|
await Task.Run(async () => {
|
||||||
var data = await DeliveryJournalData.FromQuery(query, filterNames);
|
try {
|
||||||
using var doc = new DeliveryDepreciationList(string.Join(" / ", filterNames), data);
|
var data = await DeliveryJournalData.FromQuery(query, filterNames);
|
||||||
await Utils.ExportDocument(doc, mode);
|
using var doc = new DeliveryDepreciationList(string.Join(" / ", filterNames), data);
|
||||||
} catch (Exception exc) {
|
await Utils.ExportDocument(doc, mode);
|
||||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
} catch (Exception exc) {
|
||||||
}
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Mouse.OverrideCursor = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task GenerateDeliveryDataList(this DeliveryAdminViewModel vm, ExportSubject subject, ExportMode mode) {
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
|
IQueryable<DeliveryPart> query;
|
||||||
|
List<string> filterNames = [];
|
||||||
|
if (subject == ExportSubject.FromFilters) {
|
||||||
|
var (f, _, q, _, _) = await vm.GetFilters(ctx);
|
||||||
|
query = q;
|
||||||
|
filterNames.AddRange(f);
|
||||||
|
} else if (subject == ExportSubject.FromSeason) {
|
||||||
|
var year = vm.FilterSeason ?? Utils.CurrentLastSeason;
|
||||||
|
query = ctx.DeliveryParts
|
||||||
|
.Where(p => p.Year == year);
|
||||||
|
filterNames.Add($"{year}");
|
||||||
|
} else {
|
||||||
|
throw new ArgumentException("Invalid value for ExportSubject");
|
||||||
|
}
|
||||||
|
|
||||||
|
query = query
|
||||||
|
.OrderBy(p => p.Delivery.MgNr)
|
||||||
|
.ThenBy(p => p.SortId)
|
||||||
|
.ThenBy(p => p.AttrId)
|
||||||
|
.ThenBy(p => p.CultId);
|
||||||
|
|
||||||
|
var d = new SaveFileDialog() {
|
||||||
|
FileName = $"Liefermengen.ods",
|
||||||
|
DefaultExt = "ods",
|
||||||
|
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
|
||||||
|
Title = $"Liefermengen speichern unter - Elwig"
|
||||||
|
};
|
||||||
|
if (d.ShowDialog() == true) {
|
||||||
|
Mouse.OverrideCursor = Cursors.Wait;
|
||||||
|
await Task.Run(async () => {
|
||||||
|
try {
|
||||||
|
using var ods = new OdsFile(d.FileName);
|
||||||
|
var tblTotal = await MemberDeliveryData.FromQuery(query, filterNames);
|
||||||
|
var tbl = await MemberDeliveryPerVarietyData.FromQuery(query, filterNames);
|
||||||
|
await ods.AddTable(tblTotal);
|
||||||
|
await ods.AddTable(tbl);
|
||||||
|
} catch (Exception exc) {
|
||||||
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
});
|
||||||
Mouse.OverrideCursor = null;
|
Mouse.OverrideCursor = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -946,7 +1050,7 @@ namespace Elwig.Services {
|
|||||||
|
|
||||||
var weight = await deliveryParts.SumAsync(p => p.Weight);
|
var weight = await deliveryParts.SumAsync(p => p.Weight);
|
||||||
wText = $"{weight:N0} kg";
|
wText = $"{weight:N0} kg";
|
||||||
wGrid.Add(("Gewicht", null, weight, null, weight));
|
wGrid.Add(("Menge", null, weight, null, weight));
|
||||||
|
|
||||||
if (await deliveryParts.AnyAsync()) {
|
if (await deliveryParts.AnyAsync()) {
|
||||||
var kmwMin = await deliveryParts.MinAsync(p => p.Kmw);
|
var kmwMin = await deliveryParts.MinAsync(p => p.Kmw);
|
||||||
@@ -1047,7 +1151,7 @@ namespace Elwig.Services {
|
|||||||
wGrid.ColumnDefinitions.Add(new() { Width = new(50) });
|
wGrid.ColumnDefinitions.Add(new() { Width = new(50) });
|
||||||
int rowNum = 0;
|
int rowNum = 0;
|
||||||
foreach (var row in weightData) {
|
foreach (var row in weightData) {
|
||||||
if (rowNum == 1 || (rowNum != 0 && row.Item1 != null)) rowNum++;
|
if (rowNum != 0 && row.Item2 == null) rowNum++;
|
||||||
AddWeightToolTipRow(wGrid, rowNum++, row.Item1, row.Item2, row.Item3, row.Item4, row.Item5);
|
AddWeightToolTipRow(wGrid, rowNum++, row.Item1, row.Item2, row.Item3, row.Item4, row.Item5);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1062,7 +1166,7 @@ namespace Elwig.Services {
|
|||||||
AddToolTipCell(gGrid, "Max.", 0, 4, 1, false, false, true);
|
AddToolTipCell(gGrid, "Max.", 0, 4, 1, false, false, true);
|
||||||
rowNum = 1;
|
rowNum = 1;
|
||||||
foreach (var row in gradationData) {
|
foreach (var row in gradationData) {
|
||||||
if (rowNum == 2 || (rowNum != 1 && row.Item1 != null)) rowNum++;
|
if (rowNum != 1 && row.Item2 == null) rowNum++;
|
||||||
AddGradationToolTipRow(gGrid, rowNum++, row.Item1, row.Item2, row.Item3, row.Item4, row.Item5);
|
AddGradationToolTipRow(gGrid, rowNum++, row.Item1, row.Item2, row.Item3, row.Item4, row.Item5);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1070,11 +1174,11 @@ namespace Elwig.Services {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static async Task DeleteDelivery(string lsnr) {
|
public static async Task DeleteDelivery(string lsnr) {
|
||||||
using (var ctx = new AppDbContext()) {
|
await Task.Run(async () => {
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
await ctx.Deliveries.Where(d => d.LsNr == lsnr).ExecuteDeleteAsync();
|
await ctx.Deliveries.Where(d => d.LsNr == lsnr).ExecuteDeleteAsync();
|
||||||
await ctx.SaveChangesAsync();
|
await ctx.SaveChangesAsync();
|
||||||
}
|
});
|
||||||
App.HintContextChange();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,18 @@
|
|||||||
|
using Elwig.Documents;
|
||||||
using Elwig.Helpers;
|
using Elwig.Helpers;
|
||||||
|
using Elwig.Helpers.Billing;
|
||||||
|
using Elwig.Helpers.Export;
|
||||||
|
using Elwig.Models.Dtos;
|
||||||
using Elwig.Models.Entities;
|
using Elwig.Models.Entities;
|
||||||
|
using Elwig.ViewModels;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Win32;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Elwig.Documents;
|
|
||||||
using System.Windows.Input;
|
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using Elwig.Helpers.Billing;
|
using System.Windows.Input;
|
||||||
using Elwig.Models.Dtos;
|
|
||||||
using Elwig.Helpers.Export;
|
|
||||||
using Microsoft.Win32;
|
|
||||||
using Elwig.ViewModels;
|
|
||||||
using System.IO;
|
|
||||||
using System.Net.Http;
|
|
||||||
|
|
||||||
namespace Elwig.Services {
|
namespace Elwig.Services {
|
||||||
public static class MemberService {
|
public static class MemberService {
|
||||||
@@ -49,6 +47,11 @@ namespace Elwig.Services {
|
|||||||
vm.StatusAreaCommitmentInfo = $"{Utils.CurrentLastSeason}";
|
vm.StatusAreaCommitmentInfo = $"{Utils.CurrentLastSeason}";
|
||||||
vm.StatusAreaCommitmentToolTip = null;
|
vm.StatusAreaCommitmentToolTip = null;
|
||||||
vm.Age = "-";
|
vm.Age = "-";
|
||||||
|
vm.CreatedAt = "-";
|
||||||
|
vm.ModifiedAt = "-";
|
||||||
|
vm.ModifiedAtShort = "-";
|
||||||
|
vm.ExportedAt = "-";
|
||||||
|
vm.ImportedAt = "-";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void FillInputs(this MemberAdminViewModel vm, Member m) {
|
public static void FillInputs(this MemberAdminViewModel vm, Member m) {
|
||||||
@@ -137,6 +140,12 @@ namespace Elwig.Services {
|
|||||||
vm.ContactViaPost = m.ContactViaPost;
|
vm.ContactViaPost = m.ContactViaPost;
|
||||||
vm.ContactViaEmail = m.ContactViaEmail;
|
vm.ContactViaEmail = m.ContactViaEmail;
|
||||||
|
|
||||||
|
vm.CreatedAt = $"{m.CreatedAt:dd.MM.yyyy, HH:mm:ss}";
|
||||||
|
vm.ModifiedAt = $"{m.ModifiedAt:dd.MM.yyyy, HH:mm:ss}";
|
||||||
|
vm.ModifiedAtShort = $"{m.ModifiedAt:dd.MM.yyyy, HH:mm}";
|
||||||
|
vm.ExportedAt = m.ExportedAt == null ? "-" : $"{m.ExportedAt:dd.MM.yyyy, HH:mm:ss}";
|
||||||
|
vm.ImportedAt = m.ImportedAt == null ? "-" : $"{m.ImportedAt:dd.MM.yyyy, HH:mm:ss}";
|
||||||
|
|
||||||
vm.StatusDeliveriesLastSeasonInfo = $"{Utils.CurrentLastSeason - 1}";
|
vm.StatusDeliveriesLastSeasonInfo = $"{Utils.CurrentLastSeason - 1}";
|
||||||
vm.StatusDeliveriesLastSeason = "...";
|
vm.StatusDeliveriesLastSeason = "...";
|
||||||
vm.StatusDeliveriesLastSeasonToolTip = null;
|
vm.StatusDeliveriesLastSeasonToolTip = null;
|
||||||
@@ -304,6 +313,22 @@ namespace Elwig.Services {
|
|||||||
memberQuery = memberQuery.Where(m => !m.ContactViaPost);
|
memberQuery = memberQuery.Where(m => !m.ContactViaPost);
|
||||||
filter.RemoveAt(i--);
|
filter.RemoveAt(i--);
|
||||||
filterNames.Add("nicht Kontaktart Post");
|
filterNames.Add("nicht Kontaktart Post");
|
||||||
|
} else if ("upload".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
||||||
|
memberQuery = memberQuery.Where(p => (p.XTime == null || p.MTime > p.XTime) && (p.ITime == null || p.MTime > p.ITime));
|
||||||
|
filter.RemoveAt(i--);
|
||||||
|
filterNames.Add("geändert seit letztem Export");
|
||||||
|
} else if ("!upload".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
||||||
|
memberQuery = memberQuery.Where(p => !((p.XTime == null || p.MTime > p.XTime) && (p.ITime == null || p.MTime > p.ITime)));
|
||||||
|
filter.RemoveAt(i--);
|
||||||
|
filterNames.Add("unverändert seit letztem Export");
|
||||||
|
} else if (">import".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
||||||
|
memberQuery = memberQuery.Where(p => p.MTime > p.ITime);
|
||||||
|
filter.RemoveAt(i--);
|
||||||
|
filterNames.Add("geändert seit letztem Import");
|
||||||
|
} else if ("<import".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
||||||
|
memberQuery = memberQuery.Where(p => p.MTime <= p.ITime);
|
||||||
|
filter.RemoveAt(i--);
|
||||||
|
filterNames.Add("unverändert seit letztem Import");
|
||||||
} else if (e.All(char.IsAsciiDigit) && mgnr.ContainsKey(e)) {
|
} else if (e.All(char.IsAsciiDigit) && mgnr.ContainsKey(e)) {
|
||||||
filterMgNr.Add(int.Parse(e));
|
filterMgNr.Add(int.Parse(e));
|
||||||
filter.RemoveAt(i--);
|
filter.RemoveAt(i--);
|
||||||
@@ -357,8 +382,8 @@ namespace Elwig.Services {
|
|||||||
if (filterMgNr.Count > 0) memberQuery = memberQuery.Where(m => filterMgNr.Contains(m.MgNr));
|
if (filterMgNr.Count > 0) memberQuery = memberQuery.Where(m => filterMgNr.Contains(m.MgNr));
|
||||||
if (filterKgNr.Count > 0) memberQuery = memberQuery.Where(m => m.DefaultKgNr != null && filterKgNr.Contains((int)m.DefaultKgNr));
|
if (filterKgNr.Count > 0) memberQuery = memberQuery.Where(m => m.DefaultKgNr != null && filterKgNr.Contains((int)m.DefaultKgNr));
|
||||||
if (filterZwst.Count > 0) memberQuery = memberQuery.Where(m => m.ZwstId != null && filterZwst.Contains(m.ZwstId));
|
if (filterZwst.Count > 0) memberQuery = memberQuery.Where(m => m.ZwstId != null && filterZwst.Contains(m.ZwstId));
|
||||||
if (filterAreaCom.Count > 0) memberQuery = memberQuery.Where(m => m.AreaCommitments.AsQueryable().Where(Utils.ActiveAreaCommitments()).Any(c => filterAreaCom.Contains(c.VtrgId)));
|
if (filterAreaCom.Count > 0) memberQuery = memberQuery.Where(m => m.AreaCommitments.AsQueryable().Where(Utils.ActiveAreaCommitments(Utils.CurrentLastSeason)).Any(c => filterAreaCom.Contains(c.VtrgId)));
|
||||||
if (filterNotAreaCom.Count > 0) memberQuery = memberQuery.Where(m => !m.AreaCommitments.AsQueryable().Where(Utils.ActiveAreaCommitments()).All(c => filterNotAreaCom.Contains(c.VtrgId)));
|
if (filterNotAreaCom.Count > 0) memberQuery = memberQuery.Where(m => !m.AreaCommitments.AsQueryable().Where(Utils.ActiveAreaCommitments(Utils.CurrentLastSeason)).All(c => filterNotAreaCom.Contains(c.VtrgId)));
|
||||||
if (filterLfbisNr.Count > 0) memberQuery = memberQuery.Where(m => m.LfbisNr != null && filterLfbisNr.Contains(m.LfbisNr));
|
if (filterLfbisNr.Count > 0) memberQuery = memberQuery.Where(m => m.LfbisNr != null && filterLfbisNr.Contains(m.LfbisNr));
|
||||||
if (filterUstIdNr.Count > 0) memberQuery = memberQuery.Where(m => m.UstIdNr != null && filterUstIdNr.Contains(m.UstIdNr));
|
if (filterUstIdNr.Count > 0) memberQuery = memberQuery.Where(m => m.UstIdNr != null && filterUstIdNr.Contains(m.UstIdNr));
|
||||||
}
|
}
|
||||||
@@ -367,51 +392,57 @@ namespace Elwig.Services {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static async Task GenerateMemberDataSheet(Member m, ExportMode mode) {
|
public static async Task GenerateMemberDataSheet(Member m, ExportMode mode) {
|
||||||
Mouse.OverrideCursor = Cursors.AppStarting;
|
Mouse.OverrideCursor = Cursors.Wait;
|
||||||
try {
|
await Task.Run(async () => {
|
||||||
using var ctx = new AppDbContext();
|
try {
|
||||||
using var doc = new MemberDataSheet(m, ctx);
|
using var ctx = new AppDbContext();
|
||||||
await Utils.ExportDocument(doc, mode, emailData: (m, MemberDataSheet.Name, "Im Anhang finden Sie das aktuelle Stammdatenblatt"));
|
using var doc = new MemberDataSheet(m, ctx);
|
||||||
} catch (Exception exc) {
|
await Utils.ExportDocument(doc, mode, emailData: (m, MemberDataSheet.Name, "Im Anhang finden Sie das aktuelle Stammdatenblatt"));
|
||||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
} catch (Exception exc) {
|
||||||
}
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
});
|
||||||
Mouse.OverrideCursor = null;
|
Mouse.OverrideCursor = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task GenerateDeliveryConfirmation(Member m, int year, ExportMode mode) {
|
public static async Task GenerateDeliveryConfirmation(Member m, int year, ExportMode mode) {
|
||||||
Mouse.OverrideCursor = Cursors.AppStarting;
|
Mouse.OverrideCursor = Cursors.Wait;
|
||||||
try {
|
await Task.Run(async () => {
|
||||||
var b = new Billing(year);
|
try {
|
||||||
await b.FinishSeason();
|
var b = new Billing(year);
|
||||||
await b.CalculateBuckets();
|
await b.FinishSeason();
|
||||||
App.HintContextChange();
|
await b.CalculateBuckets();
|
||||||
|
App.HintContextChange();
|
||||||
|
|
||||||
using var ctx = new AppDbContext();
|
using var ctx = new AppDbContext();
|
||||||
var data = await DeliveryConfirmationDeliveryData.ForMember(ctx.DeliveryParts, year, m);
|
var data = await DeliveryConfirmationDeliveryData.ForMember(ctx.DeliveryParts, year, m);
|
||||||
using var doc = new DeliveryConfirmation(ctx, year, m, data);
|
using var doc = new DeliveryConfirmation(ctx, year, m, data);
|
||||||
await Utils.ExportDocument(doc, mode, emailData: (m, $"{DeliveryConfirmation.Name} {year}", $"Im Anhang finden Sie die Anlieferungsbestätigung {year}"));
|
await Utils.ExportDocument(doc, mode, emailData: (m, $"{DeliveryConfirmation.Name} {year}", $"Im Anhang finden Sie die Anlieferungsbestätigung {year}"));
|
||||||
} catch (Exception exc) {
|
} catch (Exception exc) {
|
||||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
Mouse.OverrideCursor = null;
|
Mouse.OverrideCursor = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task GenerateCreditNote(Member m, int year, int avnr, ExportMode mode) {
|
public static async Task GenerateCreditNote(Member m, int year, int avnr, ExportMode mode) {
|
||||||
Mouse.OverrideCursor = Cursors.AppStarting;
|
Mouse.OverrideCursor = Cursors.Wait;
|
||||||
try {
|
await Task.Run(async () => {
|
||||||
using var ctx = new AppDbContext();
|
try {
|
||||||
var v = (await ctx.PaymentVariants.FindAsync(year, avnr))!;
|
using var ctx = new AppDbContext();
|
||||||
var data = await CreditNoteDeliveryData.ForPaymentVariant(ctx.CreditNoteDeliveryRows, ctx.Seasons, year, avnr);
|
var v = (await ctx.PaymentVariants.FindAsync(year, avnr))!;
|
||||||
var p = (await ctx.MemberPayments.FindAsync(year, avnr, m.MgNr))!;
|
var data = await CreditNoteDeliveryData.ForPaymentVariant(ctx.CreditNoteDeliveryRows, ctx.PaymentVariants, year, avnr);
|
||||||
var b = BillingData.FromJson((await ctx.PaymentVariants.FindAsync(year, avnr))!.Data);
|
var p = (await ctx.MemberPayments.FindAsync(year, avnr, m.MgNr))!;
|
||||||
|
var b = BillingData.FromJson((await ctx.PaymentVariants.FindAsync(year, avnr))!.Data);
|
||||||
|
|
||||||
using var doc = new CreditNote(ctx, p, data[m.MgNr],
|
using var doc = new CreditNote(ctx, p, data[m.MgNr],
|
||||||
b.ConsiderContractPenalties, b.ConsiderTotalPenalty, b.ConsiderAutoBusinessShares, b.ConsiderCustomModifiers,
|
b.ConsiderContractPenalties, b.ConsiderTotalPenalty, b.ConsiderAutoBusinessShares, b.ConsiderCustomModifiers,
|
||||||
await ctx.GetMemberUnderDelivery(year, m.MgNr));
|
await ctx.GetMemberUnderDelivery(year, m.MgNr));
|
||||||
await Utils.ExportDocument(doc, mode, emailData: (m, $"{CreditNote.Name} {v.Name}", $"Im Anhang finden Sie die Traubengutschrift {v.Name}"));
|
await Utils.ExportDocument(doc, mode, emailData: (m, $"{CreditNote.Name} {v.Name}", $"Im Anhang finden Sie die Traubengutschrift {v.Name}"));
|
||||||
} catch (Exception exc) {
|
} catch (Exception exc) {
|
||||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
Mouse.OverrideCursor = null;
|
Mouse.OverrideCursor = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -464,130 +495,146 @@ namespace Elwig.Services {
|
|||||||
Title = $"{MemberList.Name} speichern unter - Elwig"
|
Title = $"{MemberList.Name} speichern unter - Elwig"
|
||||||
};
|
};
|
||||||
if (d.ShowDialog() == true) {
|
if (d.ShowDialog() == true) {
|
||||||
Mouse.OverrideCursor = Cursors.AppStarting;
|
Mouse.OverrideCursor = Cursors.Wait;
|
||||||
try {
|
await Task.Run(async () => {
|
||||||
var data = await MemberListData.FromQuery(query, filterNames, filterNames.Where(f => f.StartsWith("Flächenbindung")).Select(f => f.Split(' ')[^1]));
|
try {
|
||||||
using var ods = new OdsFile(d.FileName);
|
var data = await MemberListData.FromQuery(query, filterNames, filterNames.Where(f => f.StartsWith("Flächenbindung")).Select(f => f.Split(' ')[^1]));
|
||||||
await ods.AddTable(data);
|
using var ods = new OdsFile(d.FileName);
|
||||||
} catch (Exception exc) {
|
await ods.AddTable(data);
|
||||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
} catch (Exception exc) {
|
||||||
}
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Mouse.OverrideCursor = null;
|
||||||
|
}
|
||||||
|
} else if (mode == ExportMode.Vcf) {
|
||||||
|
var d = new SaveFileDialog() {
|
||||||
|
FileName = "Mitglieder.vcf",
|
||||||
|
DefaultExt = "vcf",
|
||||||
|
Filter = "vCard-Datei (*.vcf)|*.vcf",
|
||||||
|
Title = "Kontakte speichern unter - Elwig"
|
||||||
|
};
|
||||||
|
if (d.ShowDialog() == true) {
|
||||||
|
Mouse.OverrideCursor = Cursors.Wait;
|
||||||
|
await Task.Run(async () => {
|
||||||
|
try {
|
||||||
|
var members = await query
|
||||||
|
.OrderBy(m => m.MgNr)
|
||||||
|
.Include(m => m.BillingAddress)
|
||||||
|
.Include(m => m.TelephoneNumbers)
|
||||||
|
.Include(m => m.EmailAddresses)
|
||||||
|
.AsSplitQuery()
|
||||||
|
.ToListAsync();
|
||||||
|
using var exporter = new VCard(d.FileName);
|
||||||
|
await exporter.ExportAsync(members);
|
||||||
|
} catch (Exception exc) {
|
||||||
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
});
|
||||||
Mouse.OverrideCursor = null;
|
Mouse.OverrideCursor = null;
|
||||||
}
|
}
|
||||||
} else if (mode == ExportMode.Export) {
|
} else if (mode == ExportMode.Export) {
|
||||||
var d = new SaveFileDialog() {
|
var d = new SaveFileDialog() {
|
||||||
FileName = subject == ExportSubject.Selected ? $"Mitglied_{vm.SelectedMember?.MgNr}.elwig.zip" : $"Mitglieder_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip",
|
FileName = subject == ExportSubject.Selected ? $"Mitglied_{vm.SelectedMember?.MgNr}.elwig.zip" : $"Mitglieder_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip",
|
||||||
DefaultExt = ".elwig.zip",
|
DefaultExt = "elwig.zip",
|
||||||
Filter = "Elwig-Export-Datei (*.elwig.zip)|*.elwig.zip",
|
Filter = "Elwig-Export-Datei (*.elwig.zip)|*.elwig.zip",
|
||||||
Title = $"{MemberList.Name} speichern unter - Elwig"
|
Title = $"{MemberList.Name} speichern unter - Elwig",
|
||||||
|
AddExtension = false,
|
||||||
};
|
};
|
||||||
if (d.ShowDialog() == true) {
|
if (d.ShowDialog() == true) {
|
||||||
Mouse.OverrideCursor = Cursors.AppStarting;
|
if (!d.FileName.EndsWith(".elwig.zip")) d.FileName += ".elwig.zip";
|
||||||
try {
|
Mouse.OverrideCursor = Cursors.Wait;
|
||||||
var members = await query
|
await Task.Run(async () => {
|
||||||
.OrderBy(m => m.MgNr)
|
try {
|
||||||
.Include(m => m.BillingAddress)
|
var members = await query
|
||||||
.Include(m => m.TelephoneNumbers)
|
.OrderBy(m => m.MgNr)
|
||||||
.Include(m => m.EmailAddresses)
|
.Include(m => m.BillingAddress)
|
||||||
.AsSplitQuery()
|
.Include(m => m.TelephoneNumbers)
|
||||||
.ToListAsync();
|
.Include(m => m.EmailAddresses)
|
||||||
var areaComs = await query
|
.Include(m => m.DefaultWbKg!.Gl)
|
||||||
.SelectMany(m => m.AreaCommitments)
|
.AsSplitQuery()
|
||||||
.Include(c => c.Rd)
|
.ToListAsync();
|
||||||
.ToListAsync();
|
var areaComs = await query
|
||||||
await ElwigData.Export(d.FileName, members, areaComs, filterNames);
|
.SelectMany(m => m.AreaCommitments)
|
||||||
} catch (Exception exc) {
|
.Include(c => c.Rd)
|
||||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
.Include(c => c.Kg.Gl)
|
||||||
}
|
.ToListAsync();
|
||||||
|
var wbKgs = members
|
||||||
|
.Where(m => m.DefaultWbKg != null)
|
||||||
|
.Select(m => m.DefaultWbKg!)
|
||||||
|
.Union(areaComs.Select(c => c.Kg))
|
||||||
|
.Distinct()
|
||||||
|
.OrderBy(k => k.KgNr)
|
||||||
|
.ToList();
|
||||||
|
await ElwigData.Export(d.FileName, members, areaComs, wbKgs, filterNames);
|
||||||
|
} catch (Exception exc) {
|
||||||
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
});
|
||||||
Mouse.OverrideCursor = null;
|
Mouse.OverrideCursor = null;
|
||||||
}
|
}
|
||||||
} else if (mode == ExportMode.Upload && App.Config.SyncUrl != null) {
|
} else if (mode == ExportMode.Upload && App.Config.SyncUrl != null) {
|
||||||
Mouse.OverrideCursor = Cursors.AppStarting;
|
Mouse.OverrideCursor = Cursors.Wait;
|
||||||
try {
|
await Task.Run(async () => {
|
||||||
var filename = $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip";
|
await SyncService.Upload(App.Config.SyncUrl, App.Config.SyncUrl, App.Config.SyncPassword, query, filterNames);
|
||||||
var path = Path.Combine(App.TempPath, filename);
|
});
|
||||||
var members = await query
|
|
||||||
.OrderBy(m => m.MgNr)
|
|
||||||
.Include(m => m.BillingAddress)
|
|
||||||
.Include(m => m.TelephoneNumbers)
|
|
||||||
.Include(m => m.EmailAddresses)
|
|
||||||
.AsSplitQuery()
|
|
||||||
.ToListAsync();
|
|
||||||
var areaComs = await query
|
|
||||||
.SelectMany(m => m.AreaCommitments)
|
|
||||||
.Include(c => c.Rd)
|
|
||||||
.ToListAsync();
|
|
||||||
if (members.Count == 0) {
|
|
||||||
MessageBox.Show("Es wurden keine Mitglieder zum Hochladen ausgewählt!", "Mitglieder hochladen",
|
|
||||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
} else {
|
|
||||||
await ElwigData.Export(path, members, areaComs, filterNames);
|
|
||||||
await Utils.UploadExportData(path, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
|
|
||||||
MessageBox.Show($"Hochladen von {members.Count:N0} Mitgliedern erfolgreich!", "Mitglieder hochgeladen",
|
|
||||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
|
||||||
}
|
|
||||||
} catch (HttpRequestException exc) {
|
|
||||||
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Mitglieder hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
} catch (TaskCanceledException exc) {
|
|
||||||
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Mitglieder hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
} catch (Exception exc) {
|
|
||||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
}
|
|
||||||
Mouse.OverrideCursor = null;
|
Mouse.OverrideCursor = null;
|
||||||
} else {
|
} else {
|
||||||
Mouse.OverrideCursor = Cursors.AppStarting;
|
Mouse.OverrideCursor = Cursors.Wait;
|
||||||
try {
|
await Task.Run(async () => {
|
||||||
var data = await MemberListData.FromQuery(query, filterNames, filterNames.Where(f => f.StartsWith("Flächenbindung")).Select(f => f.Split(' ')[^1]));
|
try {
|
||||||
using var doc = new MemberList(string.Join(" / ", filterNames), data);
|
var data = await MemberListData.FromQuery(query, filterNames, filterNames.Where(f => f.StartsWith("Flächenbindung")).Select(f => f.Split(' ')[^1]));
|
||||||
await Utils.ExportDocument(doc, mode);
|
using var doc = new MemberList(string.Join(" / ", filterNames), data);
|
||||||
} catch (Exception exc) {
|
await Utils.ExportDocument(doc, mode);
|
||||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
} catch (Exception exc) {
|
||||||
}
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
});
|
||||||
Mouse.OverrideCursor = null;
|
Mouse.OverrideCursor = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<int> UpdateMember(this MemberAdminViewModel vm, int? oldMgNr) {
|
public static async Task<int> UpdateMember(this MemberAdminViewModel vm, int? oldMgNr) {
|
||||||
var newMgNr = (int)vm.MgNr!;
|
var newMgNr = (int)vm.MgNr!;
|
||||||
|
var m = new Member {
|
||||||
|
MgNr = oldMgNr ?? newMgNr,
|
||||||
|
PredecessorMgNr = vm.PredecessorMgNr,
|
||||||
|
IsJuridicalPerson = vm.IsJuridicalPerson,
|
||||||
|
Prefix = vm.IsJuridicalPerson || string.IsNullOrWhiteSpace(vm.Prefix) ? null : vm.Prefix,
|
||||||
|
GivenName = vm.IsJuridicalPerson || string.IsNullOrWhiteSpace(vm.GivenName) ? null : vm.GivenName,
|
||||||
|
Name = vm.Name!,
|
||||||
|
Suffix = vm.IsJuridicalPerson || string.IsNullOrWhiteSpace(vm.Suffix) ? null : vm.Suffix,
|
||||||
|
ForTheAttentionOf = !vm.IsJuridicalPerson || string.IsNullOrWhiteSpace(vm.ForTheAttentionOf) ? null : vm.ForTheAttentionOf,
|
||||||
|
Birthday = string.IsNullOrEmpty(vm.Birthday) ? null : string.Join("-", vm.Birthday!.Split(".").Reverse()),
|
||||||
|
IsDeceased = vm.IsDeceased,
|
||||||
|
CountryNum = 40, // Austria AT AUT
|
||||||
|
PostalDestId = vm.Ort!.Id,
|
||||||
|
Address = vm.Address!,
|
||||||
|
|
||||||
using (var ctx = new AppDbContext()) {
|
Iban = string.IsNullOrEmpty(vm.Iban) ? null : vm.Iban?.Replace(" ", ""),
|
||||||
var m = new Member {
|
Bic = string.IsNullOrEmpty(vm.Bic) ? null : vm.Bic,
|
||||||
MgNr = oldMgNr ?? newMgNr,
|
|
||||||
PredecessorMgNr = vm.PredecessorMgNr,
|
|
||||||
IsJuridicalPerson = vm.IsJuridicalPerson,
|
|
||||||
Prefix = vm.IsJuridicalPerson || string.IsNullOrWhiteSpace(vm.Prefix) ? null : vm.Prefix,
|
|
||||||
GivenName = vm.IsJuridicalPerson || string.IsNullOrWhiteSpace(vm.GivenName) ? null : vm.GivenName,
|
|
||||||
Name = vm.Name!,
|
|
||||||
Suffix = vm.IsJuridicalPerson || string.IsNullOrWhiteSpace(vm.Suffix) ? null : vm.Suffix,
|
|
||||||
ForTheAttentionOf = !vm.IsJuridicalPerson || string.IsNullOrWhiteSpace(vm.ForTheAttentionOf) ? null : vm.ForTheAttentionOf,
|
|
||||||
Birthday = string.IsNullOrEmpty(vm.Birthday) ? null : string.Join("-", vm.Birthday!.Split(".").Reverse()),
|
|
||||||
IsDeceased = vm.IsDeceased,
|
|
||||||
CountryNum = 40, // Austria AT AUT
|
|
||||||
PostalDestId = vm.Ort!.Id,
|
|
||||||
Address = vm.Address!,
|
|
||||||
|
|
||||||
Iban = string.IsNullOrEmpty(vm.Iban) ? null : vm.Iban?.Replace(" ", ""),
|
UstIdNr = string.IsNullOrEmpty(vm.UstIdNr) ? null : vm.UstIdNr,
|
||||||
Bic = string.IsNullOrEmpty(vm.Bic) ? null : vm.Bic,
|
LfbisNr = string.IsNullOrEmpty(vm.LfbisNr) ? null : vm.LfbisNr,
|
||||||
|
IsBuchführend = vm.IsBuchführend,
|
||||||
|
IsOrganic = vm.IsOrganic,
|
||||||
|
|
||||||
UstIdNr = string.IsNullOrEmpty(vm.UstIdNr) ? null : vm.UstIdNr,
|
EntryDateString = string.IsNullOrEmpty(vm.EntryDate) ? null : string.Join("-", vm.EntryDate.Split(".").Reverse()),
|
||||||
LfbisNr = string.IsNullOrEmpty(vm.LfbisNr) ? null : vm.LfbisNr,
|
ExitDateString = string.IsNullOrEmpty(vm.ExitDate) ? null : string.Join("-", vm.ExitDate.Split(".").Reverse()),
|
||||||
IsBuchführend = vm.IsBuchführend,
|
BusinessShares = (int)vm.BusinessShares!,
|
||||||
IsOrganic = vm.IsOrganic,
|
AccountingNr = string.IsNullOrEmpty(vm.AccountingNr) ? null : vm.AccountingNr,
|
||||||
|
IsActive = vm.IsActive,
|
||||||
EntryDateString = string.IsNullOrEmpty(vm.EntryDate) ? null : string.Join("-", vm.EntryDate.Split(".").Reverse()),
|
IsVollLieferant = vm.IsVollLieferant,
|
||||||
ExitDateString = string.IsNullOrEmpty(vm.ExitDate) ? null : string.Join("-", vm.ExitDate.Split(".").Reverse()),
|
IsFunktionär = vm.IsFunktionär,
|
||||||
BusinessShares = (int)vm.BusinessShares!,
|
ZwstId = vm.Branch?.ZwstId,
|
||||||
AccountingNr = string.IsNullOrEmpty(vm.AccountingNr) ? null : vm.AccountingNr,
|
DefaultKgNr = vm.DefaultKg?.KgNr,
|
||||||
IsActive = vm.IsActive,
|
Comment = string.IsNullOrEmpty(vm.Comment) ? null : vm.Comment,
|
||||||
IsVollLieferant = vm.IsVollLieferant,
|
ContactViaPost = vm.ContactViaPost,
|
||||||
IsFunktionär = vm.IsFunktionär,
|
ContactViaEmail = vm.ContactViaEmail,
|
||||||
ZwstId = vm.Branch?.ZwstId,
|
};
|
||||||
DefaultKgNr = vm.DefaultKg?.KgNr,
|
|
||||||
Comment = string.IsNullOrEmpty(vm.Comment) ? null : vm.Comment,
|
|
||||||
ContactViaPost = vm.ContactViaPost,
|
|
||||||
ContactViaEmail = vm.ContactViaEmail,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
return await Task.Run(async () => {
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
if (oldMgNr != null) {
|
if (oldMgNr != null) {
|
||||||
ctx.Update(m);
|
ctx.Update(m);
|
||||||
} else {
|
} else {
|
||||||
@@ -644,7 +691,7 @@ namespace Elwig.Services {
|
|||||||
KgNr = c.KgNr,
|
KgNr = c.KgNr,
|
||||||
GstNr = c.GstNr,
|
GstNr = c.GstNr,
|
||||||
RdNr = c.RdNr,
|
RdNr = c.RdNr,
|
||||||
YearFrom = vm.MaintainAreaComYearTo ? c.YearFrom : year,
|
YearFrom = vm.MaintainAreaComYearFrom ? c.YearFrom : year,
|
||||||
YearTo = c.YearTo,
|
YearTo = c.YearTo,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -670,15 +717,14 @@ namespace Elwig.Services {
|
|||||||
if (newMgNr != m.MgNr) {
|
if (newMgNr != m.MgNr) {
|
||||||
await ctx.Database.ExecuteSqlAsync($"UPDATE member SET mgnr = {newMgNr} WHERE mgnr = {oldMgNr}");
|
await ctx.Database.ExecuteSqlAsync($"UPDATE member SET mgnr = {newMgNr} WHERE mgnr = {oldMgNr}");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
App.HintContextChange();
|
return newMgNr;
|
||||||
|
});
|
||||||
return newMgNr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task DeleteMember(int mgnr, bool deletePaymentData, bool deleteDeliveries, bool deleteAreaComs) {
|
public static async Task DeleteMember(int mgnr, bool deletePaymentData, bool deleteDeliveries, bool deleteAreaComs) {
|
||||||
using (var ctx = new AppDbContext()) {
|
await Task.Run(async () => {
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
var l = (await ctx.Members.FindAsync(mgnr))!;
|
var l = (await ctx.Members.FindAsync(mgnr))!;
|
||||||
if (deletePaymentData) {
|
if (deletePaymentData) {
|
||||||
ctx.RemoveRange(l.Credits);
|
ctx.RemoveRange(l.Credits);
|
||||||
@@ -691,8 +737,7 @@ namespace Elwig.Services {
|
|||||||
}
|
}
|
||||||
ctx.Remove(l);
|
ctx.Remove(l);
|
||||||
await ctx.SaveChangesAsync();
|
await ctx.SaveChangesAsync();
|
||||||
}
|
});
|
||||||
App.HintContextChange();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
387
Elwig/Services/PaymentVariantService.cs
Normal file
387
Elwig/Services/PaymentVariantService.cs
Normal file
@@ -0,0 +1,387 @@
|
|||||||
|
using Elwig.Documents;
|
||||||
|
using Elwig.Helpers;
|
||||||
|
using Elwig.Helpers.Billing;
|
||||||
|
using Elwig.Helpers.Export;
|
||||||
|
using Elwig.Models.Dtos;
|
||||||
|
using Elwig.Models.Entities;
|
||||||
|
using Elwig.ViewModels;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Win32;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace Elwig.Services {
|
||||||
|
public static class PaymentVariantService {
|
||||||
|
|
||||||
|
private static readonly JsonSerializerOptions JsonOpt = new() { WriteIndented = true };
|
||||||
|
|
||||||
|
public static void ClearInputs(this PaymentVariantsViewModel vm) {
|
||||||
|
vm.IsPaymentVariantSelected = false;
|
||||||
|
vm.EditText = "Bearbeiten";
|
||||||
|
vm.SaveIsEnabled = false;
|
||||||
|
vm.DeleteIsEnabled = false;
|
||||||
|
vm.CalculateIsEnabled = false;
|
||||||
|
vm.CommitIsEnabled = false;
|
||||||
|
vm.CommitVisibility = Visibility.Visible;
|
||||||
|
vm.RevertIsEnabled = false;
|
||||||
|
vm.RevertVisibility = Visibility.Hidden;
|
||||||
|
vm.Arrow = "\xF0AF";
|
||||||
|
vm.ExportIsEnabled = false;
|
||||||
|
|
||||||
|
vm.IsReadOnly = true;
|
||||||
|
vm.DataIsReadOnly = true;
|
||||||
|
vm.BillingData = null;
|
||||||
|
vm.Name = "";
|
||||||
|
vm.Comment = "";
|
||||||
|
vm.Date = null;
|
||||||
|
vm.TransferDate = null;
|
||||||
|
vm.WeightModifier = null;
|
||||||
|
vm.ConsiderModifiers = false;
|
||||||
|
vm.ConsiderPenalties = false;
|
||||||
|
vm.ConsiderPenalty = false;
|
||||||
|
vm.ConsiderAuto = false;
|
||||||
|
vm.ConsiderCustom = false;
|
||||||
|
vm.IsEnabled = false;
|
||||||
|
vm.Data = "";
|
||||||
|
|
||||||
|
vm.StatusModifierSum = "-";
|
||||||
|
vm.StatusTotalSum = "-";
|
||||||
|
vm.StatusVatSum = "-";
|
||||||
|
vm.StatusDeductionSum = "-";
|
||||||
|
vm.StatusPaymentSum = "-";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void FillInputs(this PaymentVariantsViewModel vm, PaymentVar v) {
|
||||||
|
var locked = !v.TestVariant;
|
||||||
|
vm.IsPaymentVariantSelected = true;
|
||||||
|
vm.SaveIsEnabled = !locked;
|
||||||
|
vm.DeleteIsEnabled = !locked;
|
||||||
|
vm.CalculateIsEnabled = !locked;
|
||||||
|
vm.CommitIsEnabled = !locked && !vm.SeasonLocked;
|
||||||
|
vm.CommitVisibility = !locked ? Visibility.Visible : Visibility.Hidden;
|
||||||
|
vm.RevertIsEnabled = locked && !vm.SeasonLocked;
|
||||||
|
vm.RevertVisibility = locked ? Visibility.Visible : Visibility.Hidden;
|
||||||
|
vm.Arrow = locked ? "\xF0B0" : "\xF0AF";
|
||||||
|
vm.EditText = locked ? "Ansehen" : "Bearbeiten";
|
||||||
|
vm.ExportIsEnabled = locked;
|
||||||
|
|
||||||
|
vm.IsReadOnly = false;
|
||||||
|
vm.DataIsReadOnly = locked;
|
||||||
|
vm.Name = v.Name;
|
||||||
|
vm.Comment = v.Comment ?? "";
|
||||||
|
vm.Date = v.Date;
|
||||||
|
vm.TransferDate = v.TransferDate;
|
||||||
|
try {
|
||||||
|
vm.BillingData = BillingData.FromJson(v.Data);
|
||||||
|
vm.ConsiderModifiers = vm.BillingData.ConsiderDelieryModifiers;
|
||||||
|
vm.ConsiderPenalties = vm.BillingData.ConsiderContractPenalties;
|
||||||
|
vm.ConsiderPenalty = vm.BillingData.ConsiderTotalPenalty;
|
||||||
|
vm.ConsiderAuto = vm.BillingData.ConsiderAutoBusinessShares;
|
||||||
|
vm.ConsiderCustom = vm.BillingData.ConsiderCustomModifiers;
|
||||||
|
if (vm.BillingData.NetWeightModifier != 0) {
|
||||||
|
vm.WeightModifier = Math.Round(vm.BillingData.NetWeightModifier * 100.0, 8);
|
||||||
|
} else if (vm.BillingData.GrossWeightModifier != 0) {
|
||||||
|
vm.WeightModifier = Math.Round(vm.BillingData.GrossWeightModifier * 100.0, 8);
|
||||||
|
} else {
|
||||||
|
vm.WeightModifier = null;
|
||||||
|
}
|
||||||
|
vm.Data = JsonSerializer.Serialize(vm.BillingData.Data, JsonOpt);
|
||||||
|
} catch {
|
||||||
|
vm.BillingData = null;
|
||||||
|
vm.ConsiderModifiers = false;
|
||||||
|
vm.ConsiderPenalties = false;
|
||||||
|
vm.ConsiderPenalty = false;
|
||||||
|
vm.ConsiderAuto = false;
|
||||||
|
vm.ConsiderCustom = false;
|
||||||
|
vm.WeightModifier = null;
|
||||||
|
vm.Data = v.Data;
|
||||||
|
}
|
||||||
|
vm.IsEnabled = !locked;
|
||||||
|
|
||||||
|
vm.StatusModifierSum = "...";
|
||||||
|
vm.StatusTotalSum = "...";
|
||||||
|
vm.StatusVatSum = "...";
|
||||||
|
vm.StatusDeductionSum = "...";
|
||||||
|
vm.StatusPaymentSum = "...";
|
||||||
|
Utils.RunBackground("Variantendaten laden", async () => {
|
||||||
|
await vm.UpdateSums(v);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task UpdateSums(this PaymentVariantsViewModel vm, PaymentVar v) {
|
||||||
|
if (App.MainDispatcher == null)
|
||||||
|
return;
|
||||||
|
var modText = "-";
|
||||||
|
var totalText = "-";
|
||||||
|
var vatText = "-";
|
||||||
|
var deductionText = "-";
|
||||||
|
var paymentText = "-";
|
||||||
|
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
|
var sym = v.Season.Currency.Symbol ?? v.Season.Currency.Code;
|
||||||
|
|
||||||
|
var modSum = await ctx.PaymentDeliveryParts
|
||||||
|
.Where(p => p.Year == v.Year && p.AvNr == v.AvNr)
|
||||||
|
.SumAsync(p => p.AmountValue - p.NetAmountValue);
|
||||||
|
modText = $"{v.Season.DecFromDb(modSum):N2} {sym}";
|
||||||
|
|
||||||
|
var totalSum = await ctx.MemberPayments
|
||||||
|
.Where(p => p.Year == v.Year && p.AvNr == v.AvNr)
|
||||||
|
.SumAsync(p => p.AmountValue);
|
||||||
|
totalText = $"{v.Season.DecFromDb(totalSum):N2} {sym}";
|
||||||
|
|
||||||
|
await App.MainDispatcher.BeginInvoke(() => {
|
||||||
|
if (vm.SelectedPaymentVariant != v)
|
||||||
|
return;
|
||||||
|
vm.StatusModifierSum = modText;
|
||||||
|
vm.StatusTotalSum = totalText;
|
||||||
|
});
|
||||||
|
|
||||||
|
var credits = ctx.Credits.Where(c => c.Year == v.Year && c.AvNr == v.AvNr);
|
||||||
|
if (!credits.Any()) {
|
||||||
|
long lastTotalSum = 0;
|
||||||
|
decimal vatSum = 0;
|
||||||
|
var currentPayments = await ctx.MemberPayments
|
||||||
|
.Where(p => p.Year == v.Year && p.AvNr == v.AvNr)
|
||||||
|
.ToDictionaryAsync(p => p.MgNr);
|
||||||
|
var lastV = await ctx.PaymentVariants
|
||||||
|
.Where(l => l.Year == v.Year && !l.TestVariant)
|
||||||
|
.OrderByDescending(l => l.TransferDateString ?? l.DateString)
|
||||||
|
.ThenByDescending(l => l.AvNr)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
if (lastV != null) {
|
||||||
|
var lastPayments = await ctx.MemberPayments
|
||||||
|
.Where(p => p.Year == v.Year && p.AvNr == lastV.AvNr)
|
||||||
|
.ToDictionaryAsync(p => p.MgNr);
|
||||||
|
lastTotalSum = lastPayments.Sum(e => e.Value.AmountValue);
|
||||||
|
foreach (int mgnr in currentPayments.Keys) {
|
||||||
|
var c = currentPayments[mgnr];
|
||||||
|
var l = lastPayments[mgnr];
|
||||||
|
vatSum += (c.Amount - l.Amount) * (decimal)(c.Member.IsBuchführend ? v.Season.VatNormal : v.Season.VatFlatrate);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vatSum = currentPayments.Sum(e => e.Value.Amount * (decimal)(e.Value.Member.IsBuchführend ? v.Season.VatNormal : v.Season.VatFlatrate));
|
||||||
|
}
|
||||||
|
vatText = $"~{vatSum:N2} {sym}";
|
||||||
|
deductionText = $"- {sym}";
|
||||||
|
paymentText = $"~{v.Season.DecFromDb(totalSum - lastTotalSum) + vatSum:N2} {sym}";
|
||||||
|
} else {
|
||||||
|
// all values in the credit table are stored with precision 2!
|
||||||
|
totalText = $"{Utils.DecFromDb(await credits.SumAsync(c => c.NetAmountValue), 2):N2} {sym}";
|
||||||
|
vatText = $"{Utils.DecFromDb(await credits.SumAsync(c => c.VatAmountValue), 2):N2} {sym}";
|
||||||
|
deductionText = $"{-Utils.DecFromDb(await credits.SumAsync(c => c.ModifiersValue ?? 0), 2):N2} {sym}";
|
||||||
|
paymentText = $"{Utils.DecFromDb(await credits.SumAsync(c => c.AmountValue), 2):N2} {sym}";
|
||||||
|
}
|
||||||
|
|
||||||
|
await App.MainDispatcher.BeginInvoke(() => {
|
||||||
|
if (vm.SelectedPaymentVariant != v)
|
||||||
|
return;
|
||||||
|
vm.StatusModifierSum = modText;
|
||||||
|
vm.StatusTotalSum = totalText;
|
||||||
|
vm.StatusVatSum = vatText;
|
||||||
|
vm.StatusDeductionSum = deductionText;
|
||||||
|
vm.StatusPaymentSum = paymentText;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task GenerateSummary(PaymentVar v, ExportMode mode) {
|
||||||
|
if (mode == ExportMode.SaveList) {
|
||||||
|
var d = new SaveFileDialog() {
|
||||||
|
FileName = $"Variantendaten-{v.Name.Trim().Replace(' ', '-')}.ods",
|
||||||
|
DefaultExt = "ods",
|
||||||
|
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
|
||||||
|
Title = $"Variantendaten {v.Name} speichern unter - Elwig"
|
||||||
|
};
|
||||||
|
if (d.ShowDialog() == false)
|
||||||
|
return;
|
||||||
|
Mouse.OverrideCursor = Cursors.Wait;
|
||||||
|
await Task.Run(async () => {
|
||||||
|
try {
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
|
var data = await PaymentVariantSummaryData.ForPaymentVariant(v, ctx.PaymentVariantSummaryRows);
|
||||||
|
using var ods = new OdsFile(d.FileName);
|
||||||
|
await ods.AddTable(data);
|
||||||
|
} catch (Exception exc) {
|
||||||
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Mouse.OverrideCursor = null;
|
||||||
|
} else {
|
||||||
|
Mouse.OverrideCursor = Cursors.Wait;
|
||||||
|
await Task.Run(async () => {
|
||||||
|
try {
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
|
var data = await PaymentVariantSummaryData.ForPaymentVariant(v, ctx.PaymentVariantSummaryRows);
|
||||||
|
using var doc = new PaymentVariantSummary((await ctx.PaymentVariants.FindAsync(v.Year, v.AvNr))!, data);
|
||||||
|
await Utils.ExportDocument(doc, mode);
|
||||||
|
} catch (Exception exc) {
|
||||||
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Mouse.OverrideCursor = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task GenerateEbics(int year, int avnr) {
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
|
var v = (await ctx.PaymentVariants.FindAsync(year, avnr))!;
|
||||||
|
|
||||||
|
var withoutIban = v.Credits.Count(c => c.Member.Iban == null);
|
||||||
|
if (withoutIban > 0) {
|
||||||
|
var r = MessageBox.Show($"Achtung: Für {withoutIban:N0} Mitglieder ist kein IBAN hinterlegt.\n\nDiese werden NICHT exportiert.",
|
||||||
|
"Mitglieder ohne IBAN", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
|
||||||
|
if (r != MessageBoxResult.OK) return;
|
||||||
|
}
|
||||||
|
var withNegAmount = v.Credits.Count(c => c.Amount <= 0);
|
||||||
|
if (withNegAmount > 0) {
|
||||||
|
var r = MessageBox.Show($"Achtung: Es gibt {withNegAmount:N0} Traubengutschriften mit negativem Betrag.\n\nDiese werden NICHT exportiert.",
|
||||||
|
"Traubengutschriften mit negativem Betrag", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.OK);
|
||||||
|
if (r != MessageBoxResult.OK) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var d = new SaveFileDialog() {
|
||||||
|
FileName = $"{App.Client.NameToken}-Überweisungsdaten-{v.Year}-{v.Name.Trim().Replace(' ', '-')}.{Ebics.FileExtension}",
|
||||||
|
DefaultExt = Ebics.FileExtension,
|
||||||
|
Filter = "EBICS-Datei (*.xml)|*.xml",
|
||||||
|
Title = $"Überweisungsdaten speichern unter - Elwig",
|
||||||
|
};
|
||||||
|
if (d.ShowDialog() == true) {
|
||||||
|
Mouse.OverrideCursor = Cursors.Wait;
|
||||||
|
await Task.Run(async () => {
|
||||||
|
try {
|
||||||
|
using var e = new Ebics(v, d.FileName, App.Client.ExportEbicsVersion, (Ebics.AddressMode)App.Client.ExportEbicsAddress);
|
||||||
|
await e.ExportAsync(Transaction.FromPaymentVariant(v));
|
||||||
|
} catch (Exception exc) {
|
||||||
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Mouse.OverrideCursor = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task GenerateAccountingList(int year, int avnr, string name) {
|
||||||
|
var d = new SaveFileDialog() {
|
||||||
|
FileName = $"{App.Client.NameToken}-Buchungsliste-{year}-{name.Trim().Replace(' ', '-')}.ods",
|
||||||
|
DefaultExt = "ods",
|
||||||
|
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
|
||||||
|
Title = $"Buchungsliste speichern unter - Elwig"
|
||||||
|
};
|
||||||
|
if (d.ShowDialog() == true) {
|
||||||
|
Mouse.OverrideCursor = Cursors.Wait;
|
||||||
|
await Task.Run(async () => {
|
||||||
|
try {
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
|
var tbl = await CreditNoteData.ForPaymentVariant(ctx, year, avnr);
|
||||||
|
using var ods = new OdsFile(d.FileName);
|
||||||
|
await ods.AddTable(tbl);
|
||||||
|
} catch (Exception exc) {
|
||||||
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Mouse.OverrideCursor = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<PaymentVar> CreatePaymentVariant(int year) {
|
||||||
|
return await Task.Run(async () => {
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
|
var v = new PaymentVar {
|
||||||
|
Year = year,
|
||||||
|
AvNr = await ctx.NextAvNr(year),
|
||||||
|
Name = "Neue Auszahlungsvariante",
|
||||||
|
TestVariant = true,
|
||||||
|
DateString = $"{DateTime.Today:yyyy-MM-dd}",
|
||||||
|
Data = "{\"mode\": \"elwig\", \"version\": 1, \"payment\": {}, \"curves\": []}",
|
||||||
|
};
|
||||||
|
ctx.Add(v);
|
||||||
|
await ctx.SaveChangesAsync();
|
||||||
|
return v;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<PaymentVar> Duplicate(this PaymentVar orig) {
|
||||||
|
return await Task.Run(async () => {
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
|
var n = new PaymentVar {
|
||||||
|
Year = orig.Year,
|
||||||
|
AvNr = await ctx.NextAvNr(orig.Year),
|
||||||
|
Name = $"{orig.Name} (Kopie)",
|
||||||
|
TestVariant = true,
|
||||||
|
DateString = $"{DateTime.Today:yyyy-MM-dd}",
|
||||||
|
Data = orig.Data,
|
||||||
|
};
|
||||||
|
ctx.Add(n);
|
||||||
|
await ctx.SaveChangesAsync();
|
||||||
|
return n;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<(int, int)> UpdatePaymentVariant(this PaymentVariantsViewModel vm, int? oldYear, int? oldAvNr) {
|
||||||
|
var year = oldYear ?? Utils.CurrentYear;
|
||||||
|
int avnr = 0;
|
||||||
|
await Task.Run(async () => {
|
||||||
|
var d = App.Config.Debug ? BillingData.FromJson(vm.Data ?? "{}") : vm.BillingData!;
|
||||||
|
d.ConsiderDelieryModifiers = vm.ConsiderModifiers;
|
||||||
|
d.ConsiderContractPenalties = vm.ConsiderPenalties;
|
||||||
|
d.ConsiderTotalPenalty = vm.ConsiderPenalty;
|
||||||
|
d.ConsiderAutoBusinessShares = vm.ConsiderAuto;
|
||||||
|
d.ConsiderCustomModifiers = vm.ConsiderCustom;
|
||||||
|
var modVal = vm.WeightModifier ?? 0;
|
||||||
|
d.NetWeightModifier = modVal > 0 ? modVal / 100.0 : 0;
|
||||||
|
d.GrossWeightModifier = modVal < 0 ? modVal / 100.0 : 0;
|
||||||
|
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
|
var v = new PaymentVar {
|
||||||
|
Year = year,
|
||||||
|
AvNr = oldAvNr ?? await ctx.NextAvNr(year),
|
||||||
|
Name = vm.Name!,
|
||||||
|
DateString = $"{vm.Date!.Value:yyyy-MM-dd}",
|
||||||
|
TransferDate = vm.TransferDate,
|
||||||
|
TestVariant = vm.SelectedPaymentVariant?.TestVariant ?? true,
|
||||||
|
CalcTimeUnix = vm.SelectedPaymentVariant?.CalcTimeUnix,
|
||||||
|
Comment = string.IsNullOrEmpty(vm.Comment) ? null : vm.Comment,
|
||||||
|
Data = JsonSerializer.Serialize(d.Data),
|
||||||
|
};
|
||||||
|
avnr = v.AvNr;
|
||||||
|
ctx.Update(v);
|
||||||
|
await ctx.SaveChangesAsync();
|
||||||
|
});
|
||||||
|
vm.WeightModifierChanged = false;
|
||||||
|
return (year, avnr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task DeletePaymentVariant(int year, int avnr) {
|
||||||
|
await Task.Run(async () => {
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
|
var v = (await ctx.PaymentVariants.FindAsync(year, avnr))!;
|
||||||
|
ctx.Remove(v);
|
||||||
|
await ctx.SaveChangesAsync();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task Calculate(int year, int avnr) {
|
||||||
|
await Task.Run(async () => {
|
||||||
|
var b = new BillingVariant(year, avnr);
|
||||||
|
await b.Calculate();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task Commit(int year, int avnr) {
|
||||||
|
await Task.Run(async () => {
|
||||||
|
var b = new BillingVariant(year, avnr);
|
||||||
|
await b.Commit();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task Revert(int year, int avnr) {
|
||||||
|
await Task.Run(async () => {
|
||||||
|
var b = new BillingVariant(year, avnr);
|
||||||
|
await b.Revert();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
277
Elwig/Services/SyncService.cs
Normal file
277
Elwig/Services/SyncService.cs
Normal file
@@ -0,0 +1,277 @@
|
|||||||
|
using Elwig.Helpers;
|
||||||
|
using Elwig.Helpers.Export;
|
||||||
|
using Elwig.Models.Entities;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace Elwig.Services {
|
||||||
|
public static class SyncService {
|
||||||
|
|
||||||
|
public static readonly Expression<Func<Member, bool>> ChangedMembers = (m) => ((m.XTime == null && m.MTime > 1751328000) || m.MTime > m.XTime) && (m.ITime == null || m.MTime > m.ITime);
|
||||||
|
public static readonly Expression<Func<Delivery, bool>> ChangedDeliveries = (d) => ((d.XTime == null && d.MTime > 1751328000) || d.MTime > d.XTime) && (d.ITime == null || d.MTime > d.ITime);
|
||||||
|
|
||||||
|
public static async Task Upload(string url, string username, string password, IQueryable<Member> query, IEnumerable<string> filterNames) {
|
||||||
|
try {
|
||||||
|
var filename = $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip";
|
||||||
|
var path = Path.Combine(App.TempPath, filename);
|
||||||
|
var members = await query
|
||||||
|
.OrderBy(m => m.MgNr)
|
||||||
|
.Include(m => m.BillingAddress)
|
||||||
|
.Include(m => m.TelephoneNumbers)
|
||||||
|
.Include(m => m.EmailAddresses)
|
||||||
|
.Include(m => m.DefaultWbKg!.Gl)
|
||||||
|
.AsSplitQuery()
|
||||||
|
.ToListAsync();
|
||||||
|
var areaComs = await query
|
||||||
|
.SelectMany(m => m.AreaCommitments)
|
||||||
|
.OrderBy(c => c.MgNr).ThenBy(c => c.FbNr)
|
||||||
|
.Include(c => c.Rd)
|
||||||
|
.Include(c => c.Kg.Gl)
|
||||||
|
.ToListAsync();
|
||||||
|
var wbKgs = members
|
||||||
|
.Where(m => m.DefaultWbKg != null)
|
||||||
|
.Select(m => m.DefaultWbKg!)
|
||||||
|
.Union(areaComs.Select(c => c.Kg))
|
||||||
|
.Distinct()
|
||||||
|
.OrderBy(k => k.KgNr)
|
||||||
|
.ToList();
|
||||||
|
if (members.Count == 0) {
|
||||||
|
MessageBox.Show("Es wurden keine Mitglieder zum Hochladen ausgewählt!", "Mitglieder hochladen",
|
||||||
|
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
} else {
|
||||||
|
var exportedAt = DateTime.Now;
|
||||||
|
await ElwigData.Export(path, members, areaComs, wbKgs, filterNames);
|
||||||
|
await Utils.UploadExportData(path, url, username, password);
|
||||||
|
await UpdateExportedAt(members, [], exportedAt);
|
||||||
|
MessageBox.Show($"Hochladen von {members.Count:N0} Mitgliedern erfolgreich!", "Mitglieder hochgeladen",
|
||||||
|
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
|
}
|
||||||
|
} catch (HttpRequestException exc) {
|
||||||
|
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Mitglieder hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
} catch (TaskCanceledException exc) {
|
||||||
|
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Mitglieder hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
} catch (Exception exc) {
|
||||||
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task Upload(string url, string username, string password, IQueryable<DeliveryPart> query, IEnumerable<string> filterNames) {
|
||||||
|
try {
|
||||||
|
var filename = $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip";
|
||||||
|
var path = Path.Combine(App.TempPath, filename);
|
||||||
|
var list = await query
|
||||||
|
.Select(p => p.Delivery)
|
||||||
|
.Distinct()
|
||||||
|
.Include(d => d.Parts).ThenInclude(p => p.PartModifiers).ThenInclude(m => m.Modifier)
|
||||||
|
.Include(d => d.Parts).ThenInclude(p => p.Rd)
|
||||||
|
.Include(d => d.Parts).ThenInclude(p => p.Kg!.Gl)
|
||||||
|
.AsSplitQuery()
|
||||||
|
.ToListAsync();
|
||||||
|
var wbKgs = list
|
||||||
|
.SelectMany(d => d.Parts)
|
||||||
|
.Where(p => p.Kg != null)
|
||||||
|
.Select(p => p.Kg!)
|
||||||
|
.DistinctBy(k => k.KgNr)
|
||||||
|
.OrderBy(k => k.KgNr)
|
||||||
|
.ToList();
|
||||||
|
if (list.Count == 0) {
|
||||||
|
MessageBox.Show("Es wurden keine Lieferungen zum Hochladen ausgewählt!", "Lieferungen hochladen",
|
||||||
|
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
} else {
|
||||||
|
var exportedAt = DateTime.Now;
|
||||||
|
await ElwigData.Export(path, list, wbKgs, filterNames);
|
||||||
|
await Utils.UploadExportData(path, url, username, password);
|
||||||
|
await UpdateExportedAt([], list, exportedAt);
|
||||||
|
MessageBox.Show($"Hochladen von {list.Count:N0} Lieferungen erfolgreich!", "Lieferungen hochgeladen",
|
||||||
|
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
|
}
|
||||||
|
} catch (HttpRequestException exc) {
|
||||||
|
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
} catch (TaskCanceledException exc) {
|
||||||
|
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
} catch (Exception exc) {
|
||||||
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task UploadModified(string url, string username, string password) {
|
||||||
|
try {
|
||||||
|
var path = Path.Combine(App.TempPath, $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip");
|
||||||
|
List<Member> members;
|
||||||
|
List<AreaCom> areaComs;
|
||||||
|
List<Delivery> deliveries;
|
||||||
|
using (var ctx = new AppDbContext()) {
|
||||||
|
members = await ctx.Members
|
||||||
|
.Where(ChangedMembers)
|
||||||
|
.Include(m => m.BillingAddress)
|
||||||
|
.Include(m => m.TelephoneNumbers)
|
||||||
|
.Include(m => m.EmailAddresses)
|
||||||
|
.Include(m => m.DefaultWbKg!.Gl)
|
||||||
|
.OrderBy(m => m.MgNr)
|
||||||
|
.AsSplitQuery()
|
||||||
|
.ToListAsync();
|
||||||
|
areaComs = await ctx.Members
|
||||||
|
.Where(ChangedMembers)
|
||||||
|
.SelectMany(m => m.AreaCommitments)
|
||||||
|
.Include(c => c.Rd)
|
||||||
|
.Include(c => c.Kg.Gl)
|
||||||
|
.OrderBy(c => c.MgNr).ThenBy(c => c.FbNr)
|
||||||
|
.ToListAsync();
|
||||||
|
deliveries = await ctx.Deliveries
|
||||||
|
.Where(ChangedDeliveries)
|
||||||
|
.Include(d => d.Parts).ThenInclude(p => p.PartModifiers).ThenInclude(m => m.Modifier)
|
||||||
|
.Include(d => d.Parts).ThenInclude(p => p.Rd)
|
||||||
|
.Include(d => d.Parts).ThenInclude(p => p.Kg).ThenInclude(k => k!.Gl)
|
||||||
|
.OrderBy(d => d.DateString).ThenBy(d => d.TimeString).ThenBy(d => d.LsNr)
|
||||||
|
.AsSplitQuery()
|
||||||
|
.ToListAsync();
|
||||||
|
}
|
||||||
|
var wbKgs = members
|
||||||
|
.Where(m => m.DefaultWbKg != null)
|
||||||
|
.Select(m => m.DefaultWbKg!)
|
||||||
|
.Union(areaComs.Select(c => c.Kg))
|
||||||
|
.Union(deliveries.SelectMany(d => d.Parts)
|
||||||
|
.Where(p => p.Kg != null)
|
||||||
|
.Select(p => p.Kg!))
|
||||||
|
.DistinctBy(k => k.KgNr)
|
||||||
|
.OrderBy(k => k.KgNr)
|
||||||
|
.ToList();
|
||||||
|
if (members.Count == 0 && deliveries.Count == 0) {
|
||||||
|
MessageBox.Show("Es gibt keine geänderten Mitglieder oder Lieferungen, die hochgeladen werden könnten!", "Mitglieder und Lieferungen hochladen",
|
||||||
|
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
|
} else {
|
||||||
|
var exportedAt = DateTime.Now;
|
||||||
|
await (new ElwigData.ElwigExport {
|
||||||
|
Members = (members, ["geändert seit letztem Export"]),
|
||||||
|
AreaComs = (areaComs, ["von exportierten Mitgliedern"]),
|
||||||
|
Deliveries = (deliveries, ["geändert seit letzem Export"]),
|
||||||
|
WbKgs = (wbKgs, ["von exportierten Mitgliedern, Flächenbindungen und Lieferungen"]),
|
||||||
|
}).Export(path);
|
||||||
|
await Utils.UploadExportData(path, url, username, password);
|
||||||
|
await UpdateExportedAt(members, deliveries, exportedAt);
|
||||||
|
MessageBox.Show($"Hochladen von {members.Count:N0} Mitgliedern und {deliveries.Count:N0} Lieferungen erfolgreich!", "Mitglieder und Lieferungen hochladen",
|
||||||
|
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
|
}
|
||||||
|
} catch (HttpRequestException exc) {
|
||||||
|
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Mitglieder und Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
} catch (TaskCanceledException exc) {
|
||||||
|
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Mitglieder und Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
} catch (Exception exc) {
|
||||||
|
MessageBox.Show(exc.Message, "Mitglieder und Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task UploadBranchDeliveries(string url, string username, string password, int? year = null) {
|
||||||
|
try {
|
||||||
|
year ??= Utils.CurrentLastSeason;
|
||||||
|
var path = Path.Combine(App.TempPath, $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip");
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
|
var deliveries = await ctx.Deliveries
|
||||||
|
.Where(d => d.Year == year && d.ZwstId == App.ZwstId)
|
||||||
|
.Include(d => d.Parts).ThenInclude(p => p.PartModifiers).ThenInclude(m => m.Modifier)
|
||||||
|
.Include(d => d.Parts).ThenInclude(p => p.Rd)
|
||||||
|
.Include(d => d.Parts).ThenInclude(p => p.Kg).ThenInclude(k => k!.Gl)
|
||||||
|
.OrderBy(d => d.DateString).ThenBy(d => d.TimeString).ThenBy(d => d.LsNr)
|
||||||
|
.AsSplitQuery()
|
||||||
|
.ToListAsync();
|
||||||
|
var wbKgs = deliveries
|
||||||
|
.SelectMany(d => d.Parts)
|
||||||
|
.Where(p => p.Kg != null)
|
||||||
|
.Select(p => p.Kg!)
|
||||||
|
.DistinctBy(k => k.KgNr)
|
||||||
|
.ToList();
|
||||||
|
if (deliveries.Count == 0) {
|
||||||
|
MessageBox.Show("Es gibt keine Lieferungen, die hochgeladen werden können!", "Lieferungen hochladen",
|
||||||
|
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
} else {
|
||||||
|
var exportedAt = DateTime.Now;
|
||||||
|
await ElwigData.Export(path, deliveries, wbKgs, [$"{year}", $"Zweigstelle {App.BranchName}"]);
|
||||||
|
await Utils.UploadExportData(path, url, username, password);
|
||||||
|
await UpdateExportedAt([], deliveries, exportedAt);
|
||||||
|
MessageBox.Show($"Hochladen von {deliveries.Count:N0} Lieferungen erfolgreich!", "Lieferungen hochladen",
|
||||||
|
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
|
}
|
||||||
|
} catch (HttpRequestException exc) {
|
||||||
|
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
} catch (TaskCanceledException exc) {
|
||||||
|
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
} catch (Exception exc) {
|
||||||
|
MessageBox.Show(exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<List<(string Name, string Url)>> GetFilesToImport(string url, string username, string password) {
|
||||||
|
var data = await Utils.GetExportMetaData(url, username, password);
|
||||||
|
var files = data
|
||||||
|
.Select(f => new {
|
||||||
|
Name = f!["name"]!.AsValue().GetValue<string>(),
|
||||||
|
Timestamp = f!["timestamp"] != null && DateTime.TryParseExact(f!["timestamp"]!.AsValue().GetValue<string>(), "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None, out var dt) ? dt : (DateTime?)null,
|
||||||
|
ZwstId = f!["meta"]?["zwstid"]?.AsValue().GetValue<string>() ?? f!["zwstid"]?.AsValue().GetValue<string>(),
|
||||||
|
Device = f!["meta"]?["device"]?.AsValue().GetValue<string>(),
|
||||||
|
Url = f!["url"]!.AsValue().GetValue<string>(),
|
||||||
|
Size = f!["size"]!.AsValue().GetValue<long>(),
|
||||||
|
})
|
||||||
|
.Where(f => f.Timestamp >= new DateTime(Utils.CurrentLastSeason, 7, 1))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var imported = await ElwigData.GetImportedFiles();
|
||||||
|
return [.. files
|
||||||
|
.Where(f => f.Device != Environment.MachineName && !imported.Contains(f.Name))
|
||||||
|
.Select(f => (f.Name, f.Url))
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task Download(string url, string username, string password) {
|
||||||
|
try {
|
||||||
|
var import = await GetFilesToImport(url, username, password);
|
||||||
|
var paths = new List<string>();
|
||||||
|
using (var client = Utils.GetHttpClient(username, password)) {
|
||||||
|
foreach (var f in import) {
|
||||||
|
var filename = Path.Combine(App.TempPath, f.Name);
|
||||||
|
using var stream = new FileStream(filename, FileMode.Create);
|
||||||
|
await client.DownloadAsync(f.Url, stream);
|
||||||
|
paths.Add(filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await ElwigData.Import(paths, ElwigData.ImportMode.FromBranches);
|
||||||
|
} catch (HttpRequestException exc) {
|
||||||
|
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Daten herunterladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
} catch (TaskCanceledException exc) {
|
||||||
|
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Daten herunterladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
} catch (Exception exc) {
|
||||||
|
MessageBox.Show(exc.Message, "Daten herunterladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task UpdateExportedAt(IEnumerable<Member> member, IEnumerable<Delivery> deliveries, DateTime dateTime) {
|
||||||
|
var timestamp = ((DateTimeOffset)dateTime.ToUniversalTime()).ToUnixTimeSeconds();
|
||||||
|
var mgnrs = string.Join(",", member.Select(m => $"{m.MgNr}").Append("0"));
|
||||||
|
var dids = string.Join(",", deliveries.Select(d => $"({d.Year},{d.DId})").Append("(0,0)"));
|
||||||
|
using (var cnx = await AppDbContext.ConnectAsync()) {
|
||||||
|
await cnx.ExecuteBatch($"""
|
||||||
|
BEGIN;
|
||||||
|
UPDATE client_parameter SET value = '0' WHERE param = 'ENABLE_TIME_TRIGGERS';
|
||||||
|
UPDATE member SET xtime = {timestamp} WHERE mgnr IN ({mgnrs});
|
||||||
|
UPDATE area_commitment SET xtime = {timestamp} WHERE mgnr IN ({mgnrs});
|
||||||
|
UPDATE delivery SET xtime = {timestamp} WHERE (year, did) IN ({dids});
|
||||||
|
UPDATE delivery_part SET xtime = {timestamp} WHERE (year, did) IN ({dids});
|
||||||
|
UPDATE client_parameter SET value = '1' WHERE param = 'ENABLE_TIME_TRIGGERS';
|
||||||
|
COMMIT;
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
App.HintContextChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<bool> ChangesAvailable(AppDbContext ctx, string url, string username, string password) {
|
||||||
|
return await ctx.Members.AnyAsync(ChangedMembers) || await ctx.Deliveries.AnyAsync(ChangedDeliveries) || (Utils.HasInternetConnectivity() && (await GetFilesToImport(url, username, password)).Count > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,7 +15,7 @@ namespace Elwig.ViewModels {
|
|||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string? _searchQuery = "";
|
private string? _searchQuery = "";
|
||||||
public List<string> TextFilter {
|
public List<string> TextFilter {
|
||||||
get => [.. SearchQuery?.ToLower().Split(' ').ToList().FindAll(e => e.Length > 0)];
|
get => [.. SearchQuery?.ToLower().Split(' ').ToList().FindAll(e => e.Length > 0) ?? []];
|
||||||
set => SearchQuery = string.Join(' ', value.Where(e => e.Length > 0));
|
set => SearchQuery = string.Join(' ', value.Where(e => e.Length > 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ namespace Elwig.ViewModels {
|
|||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string? _searchQuery = "";
|
private string? _searchQuery = "";
|
||||||
public List<string> TextFilter => [.. SearchQuery?.ToLower().Split(' ').ToList().FindAll(e => e.Length > 0)];
|
public List<string> TextFilter => [.. SearchQuery?.ToLower().Split(' ').ToList().FindAll(e => e.Length > 0) ?? []];
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string? _lastScaleError;
|
private string? _lastScaleError;
|
||||||
@@ -169,10 +169,28 @@ namespace Elwig.ViewModels {
|
|||||||
set => AcidString = $"{value:0.0}";
|
set => AcidString = $"{value:0.0}";
|
||||||
}
|
}
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private bool _isLesewagen;
|
|
||||||
[ObservableProperty]
|
|
||||||
private bool? _isHandPicked;
|
private bool? _isHandPicked;
|
||||||
|
|
||||||
|
public string? Unloading {
|
||||||
|
get => IsUnloadingDumper ? DeliveryPart.Dumper : IsUnloadingPumped ? DeliveryPart.Pumped : IsUnloadingBox ? DeliveryPart.Box : null;
|
||||||
|
set {
|
||||||
|
switch (value) {
|
||||||
|
case DeliveryPart.Dumper: IsUnloadingDumper = true; break;
|
||||||
|
case DeliveryPart.Pumped: IsUnloadingPumped = true; break;
|
||||||
|
case DeliveryPart.Box: IsUnloadingBox = true; break;
|
||||||
|
default: IsUnloadingOther = true; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _isUnloadingDumper;
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _isUnloadingPumped;
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _isUnloadingBox;
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _isUnloadingOther;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string _statusMembers = "-";
|
private string _statusMembers = "-";
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace Elwig.ViewModels {
|
|||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string? _searchQuery = "";
|
private string? _searchQuery = "";
|
||||||
public List<string> TextFilter => [.. SearchQuery?.ToLower().Split(' ').ToList().FindAll(e => e.Length > 0)];
|
public List<string> TextFilter => [.. SearchQuery?.ToLower().Split(' ').ToList().FindAll(e => e.Length > 0) ?? []];
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private bool _filterOnlyUpcoming;
|
private bool _filterOnlyUpcoming;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ namespace Elwig.ViewModels {
|
|||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string? _searchQuery = "";
|
private string? _searchQuery = "";
|
||||||
public List<string> TextFilter => [.. SearchQuery?.ToLower().Split(' ').ToList().FindAll(e => e.Length > 0)];
|
public List<string> TextFilter => [.. SearchQuery?.ToLower().Split(' ').ToList().FindAll(e => e.Length > 0) ?? []];
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private bool _filterOnlyUpcoming;
|
private bool _filterOnlyUpcoming;
|
||||||
|
|||||||
@@ -11,14 +11,14 @@ namespace Elwig.ViewModels {
|
|||||||
public partial class MemberAdminViewModel : ObservableObject {
|
public partial class MemberAdminViewModel : ObservableObject {
|
||||||
|
|
||||||
public int? TransferPredecessorAreaComs = null;
|
public int? TransferPredecessorAreaComs = null;
|
||||||
public bool MaintainAreaComYearTo = false;
|
public bool MaintainAreaComYearFrom = false;
|
||||||
public int? CancelAreaComs = null;
|
public int? CancelAreaComs = null;
|
||||||
|
|
||||||
public ObservableCollection<KeyValuePair<string, string>> PhoneNrTypes { get; set; } = new(Utils.PhoneNrTypes);
|
public ObservableCollection<KeyValuePair<string, string>> PhoneNrTypes { get; set; } = new(Utils.PhoneNrTypes);
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string? _searchQuery = "";
|
private string? _searchQuery = "";
|
||||||
public List<string> TextFilter => [.. SearchQuery?.ToLower().Split(' ').ToList().FindAll(e => e.Length > 0)];
|
public List<string> TextFilter => [.. SearchQuery?.ToLower().Split(' ').ToList().FindAll(e => e.Length > 0) ?? []];
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private bool _showOnlyActiveMembers;
|
private bool _showOnlyActiveMembers;
|
||||||
@@ -153,6 +153,17 @@ namespace Elwig.ViewModels {
|
|||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private bool _contactViaEmail;
|
private bool _contactViaEmail;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _createdAt = "-";
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _modifiedAt = "-";
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _modifiedAtShort = "-";
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _exportedAt = "-";
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _importedAt = "-";
|
||||||
|
|
||||||
public ObservableCollection<string?> EmailAddresses { get; private set; } = [null, null, null, null, null, null, null, null, null];
|
public ObservableCollection<string?> EmailAddresses { get; private set; } = [null, null, null, null, null, null, null, null, null];
|
||||||
|
|
||||||
public partial class PhoneNr(int? type = null, string? number = null, string? comment = null) : ObservableObject {
|
public partial class PhoneNr(int? type = null, string? number = null, string? comment = null) : ObservableObject {
|
||||||
|
|||||||
95
Elwig/ViewModels/PaymentVariantsViewModel.cs
Normal file
95
Elwig/ViewModels/PaymentVariantsViewModel.cs
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using Elwig.Helpers.Billing;
|
||||||
|
using Elwig.Models.Entities;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace Elwig.ViewModels {
|
||||||
|
public partial class PaymentVariantsViewModel : ObservableObject {
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private PaymentVar? _selectedPaymentVariant;
|
||||||
|
[ObservableProperty]
|
||||||
|
private IEnumerable<PaymentVar> _paymentVariants = [];
|
||||||
|
|
||||||
|
public BillingData? BillingData;
|
||||||
|
public bool SeasonLocked;
|
||||||
|
public bool WeightModifierChanged;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _name = "";
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _comment = "";
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _dateString = "";
|
||||||
|
public DateOnly? Date {
|
||||||
|
get => DateOnly.TryParseExact(DateString, "dd.MM.yyyy", out var d) ? d : null;
|
||||||
|
set => DateString = $"{value:dd.MM.yyyy}";
|
||||||
|
}
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _transferDateString = "";
|
||||||
|
public DateOnly? TransferDate {
|
||||||
|
get => DateOnly.TryParseExact(TransferDateString, "dd.MM.yyyy", out var d) ? d : null;
|
||||||
|
set => TransferDateString = $"{value:dd.MM.yyyy}";
|
||||||
|
}
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _weightModifierString = "";
|
||||||
|
public double? WeightModifier {
|
||||||
|
get => double.TryParse(WeightModifierString, out var d) ? d : null;
|
||||||
|
set => WeightModifierString = $"{value}";
|
||||||
|
}
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _data = "";
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _considerModifiers;
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _considerPenalties;
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _considerPenalty;
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _considerAuto;
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _considerCustom;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _isPaymentVariantSelected;
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _isReadOnly = true;
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _dataIsReadOnly = true;
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _isEnabled = false;
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _saveIsEnabled = false;
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _deleteIsEnabled = false;
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _calculateIsEnabled = false;
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _exportIsEnabled = false;
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _commitIsEnabled = false;
|
||||||
|
[ObservableProperty]
|
||||||
|
private Visibility _commitVisibility = Visibility.Visible;
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _revertIsEnabled = false;
|
||||||
|
[ObservableProperty]
|
||||||
|
private Visibility _revertVisibility = Visibility.Hidden;
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _arrow = "\xF0AF";
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _editText = "Bearbeiten";
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _statusModifierSum = "-";
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _statusTotalSum = "-";
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _statusVatSum = "-";
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _statusDeductionSum = "-";
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _statusPaymentSum = "-";
|
||||||
|
}
|
||||||
|
}
|
||||||
32
Elwig/Windows/AboutWindow.xaml
Normal file
32
Elwig/Windows/AboutWindow.xaml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<Window x:Class="Elwig.Windows.AboutWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:local="clr-namespace:Elwig.Windows"
|
||||||
|
Title="Über - Elwig" Height="340" Width="460" ResizeMode="NoResize">
|
||||||
|
<Grid>
|
||||||
|
<TextBlock Margin="20,10" FontSize="12">
|
||||||
|
<Bold>Produkt:</Bold> Elwig<LineBreak/>
|
||||||
|
<Bold>Beschreibung:</Bold> Elektronische Winzergenossenschaftsverwaltung<LineBreak/>
|
||||||
|
<Bold>Typ:</Bold> Warenwirtschaftssystem (ERP-System)<LineBreak/>
|
||||||
|
<Bold>Version:</Bold> <Run x:Name="Version">0.0.0.0</Run> (<Hyperlink NavigateUri="https://elwig.at/changelog" RequestNavigate="Hyperlink_RequestNavigate">Änderungsprotokoll</Hyperlink>)<LineBreak/>
|
||||||
|
<Bold>Lizenz:</Bold> <Hyperlink NavigateUri="https://www.gnu.org/licenses/gpl-3.0.html" RequestNavigate="Hyperlink_RequestNavigate">GNU General Public License 3.0 (GPLv3)</Hyperlink><LineBreak/>
|
||||||
|
<Bold>Website:</Bold> <Hyperlink NavigateUri="https://elwig.at/" RequestNavigate="Hyperlink_RequestNavigate">https://elwig.at/</Hyperlink><LineBreak/>
|
||||||
|
<Bold>Entwickler:</Bold> Lorenz Stechauner, Thomas Hilscher<LineBreak/>
|
||||||
|
<Bold>Kontakt:</Bold> <Hyperlink NavigateUri="mailto:lorenz.stechauner@necronda.net" RequestNavigate="Hyperlink_RequestNavigate">lorenz.stechauner@necronda.net</Hyperlink>, <Hyperlink NavigateUri="mailto:thomas.hilscher@gmail.com" RequestNavigate="Hyperlink_RequestNavigate">thomas.hilscher@gmail.com</Hyperlink><LineBreak/>
|
||||||
|
<Bold>Quellcode:</Bold> <Hyperlink NavigateUri="https://git.necronda.net/winzer/elwig" RequestNavigate="Hyperlink_RequestNavigate">https://git.necronda.net/winzer/elwig</Hyperlink><LineBreak/>
|
||||||
|
<Bold>Entwicklungszeitraum:</Bold> 2022–2025<LineBreak/>
|
||||||
|
<LineBreak/>
|
||||||
|
<Bold>Verwendete Technologien:</Bold><LineBreak/>
|
||||||
|
Programmiersprache: C#<LineBreak/>
|
||||||
|
Framework: Windows Presentation Framework (WPF)<LineBreak/>
|
||||||
|
Datenbank: <Hyperlink NavigateUri="https://sqlite.org/" RequestNavigate="Hyperlink_RequestNavigate">SQLite</Hyperlink><LineBreak/>
|
||||||
|
PDF-Erstellung: <Hyperlink NavigateUri="https://weasyprint.org/" RequestNavigate="Hyperlink_RequestNavigate">WeasyPrint</Hyperlink>, <Hyperlink NavigateUri="https://github.com/toddams/RazorLight" RequestNavigate="Hyperlink_RequestNavigate">RazorLight</Hyperlink>, <Hyperlink NavigateUri="https://github.com/pvginkel/PdfiumViewer" RequestNavigate="Hyperlink_RequestNavigate">PdfiumViewer</Hyperlink><LineBreak/>
|
||||||
|
Paketierung: <Hyperlink NavigateUri="https://www.firegiant.com/wixtoolset/" RequestNavigate="Hyperlink_RequestNavigate">WiX Toolset</Hyperlink>
|
||||||
|
</TextBlock>
|
||||||
|
|
||||||
|
<Image Source="\Resources\Images\Elwig.png" RenderOptions.BitmapScalingMode="HighQuality" Height="64"
|
||||||
|
HorizontalAlignment="Right" Margin="10" VerticalAlignment="Top"/>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user