Compare commits

...

28 Commits

Author SHA1 Message Date
b58dee6d3f Bump version to 1.0.3.4
All checks were successful
Deploy / Build and Deploy (push) Successful in 2m28s
2026-02-19 19:33:22 +01:00
0e2b004b0d Elwig: Update dependencies
All checks were successful
Test / Run tests (push) Successful in 2m46s
2026-02-19 19:26:39 +01:00
19c3322ef2 Tests: Update dependencies 2026-02-19 19:26:29 +01:00
6f081811c4 MailWindow: Allow users to double click on avaiable/selected documents
All checks were successful
Test / Run tests (push) Successful in 2m58s
2026-02-19 18:10:26 +01:00
432c511b85 Billing: Create credit notes only for members who receive a payment 2026-02-19 18:10:22 +01:00
7e22759c33 PaymentVariantsWindow: Never lock seasons
All checks were successful
Test / Run tests (push) Successful in 2m27s
2026-02-19 16:12:54 +01:00
a47904cf0b SyncService: Catch exceptions in ChangesAvailable()
All checks were successful
Test / Run tests (push) Successful in 3m16s
2026-02-19 15:59:23 +01:00
6818491ae3 Export: Fix importing delivery parts with no kgnr 2026-02-19 15:58:51 +01:00
23db4de1ee Database: Fix xtime/mtime for delivery parts
All checks were successful
Test / Run tests (push) Successful in 3m17s
2026-02-19 15:31:36 +01:00
9e5f709d42 DeliveryConfirmation: Show 'Davon abgewertet' below total sum 2026-02-19 15:26:09 +01:00
4cd7ef85a1 Printing: Replace PdfiumViewer with native pdfium.dll binary
All checks were successful
Test / Run tests (push) Successful in 3m47s
2026-02-18 23:02:18 +01:00
2c0b000073 MainWindow: Fix Tooltip for 'Sorten-/Qual.aufschlüssel.' Button
All checks were successful
Test / Run tests (push) Successful in 2m31s
2026-02-18 23:01:35 +01:00
f1737af2a2 Bump version to 1.0.3.3
All checks were successful
Test / Run tests (push) Successful in 2m26s
Deploy / Build and Deploy (push) Successful in 2m21s
2026-02-05 23:52:33 +01:00
8f423e3e92 Pdfium: Use updated fork of PdfiumViewer
All checks were successful
Test / Run tests (push) Successful in 2m46s
2026-02-05 23:21:28 +01:00
c8911c0acb Pdfium: Explicitly add WindowsForms after dotnet 10 upgrade
All checks were successful
Test / Run tests (push) Successful in 3m5s
2026-02-05 22:50:06 +01:00
baf598b76c Bump version to 1.0.3.2
All checks were successful
Test / Run tests (push) Successful in 3m1s
Deploy / Build and Deploy (push) Successful in 2m3s
2026-02-04 16:34:52 +01:00
7adc9f89e4 Printing/Html: Try to hotfix null pointer error
Some checks failed
Test / Run tests (push) Has been cancelled
2026-02-04 16:33:21 +01:00
dde808df6a Bump version to 1.0.3.1
All checks were successful
Test / Run tests (push) Successful in 2m28s
Deploy / Build and Deploy (push) Successful in 2m16s
2026-01-19 10:58:10 +01:00
e63de0d867 Setup: Try to hotfix VC redist version incompatability
Some checks failed
Deploy / Build and Deploy (push) Failing after 44s
2026-01-19 10:52:53 +01:00
e9e90b1e98 Bump version to 1.0.3.0
All checks were successful
Test / Run tests (push) Successful in 3m4s
Deploy / Build and Deploy (push) Successful in 2m8s
2026-01-16 14:43:52 +01:00
640dbf705e Printing/Pdf: Fix detection of WinziPrint.exe to isolate tests properly
All checks were successful
Test / Run tests (push) Successful in 2m11s
2026-01-16 01:26:35 +01:00
42121fe7da Elwig: Update dependencies
Some checks failed
Test / Run tests (push) Has been cancelled
2026-01-16 00:35:50 +01:00
d45c3f867f Tests: Update dependencies 2026-01-16 00:35:38 +01:00
a90be2644d [#50] MailWindow: Add button to cancel document generation
Some checks failed
Test / Run tests (push) Has been cancelled
2026-01-16 00:22:11 +01:00
01739ba42e ClientParameters: Fix storing of MailSendPostal and MailSendEmail 2026-01-16 00:20:15 +01:00
7bea4d9ee0 BillingVariant: Fix NULL/0 error introduced in c7a2f2241d
All checks were successful
Test / Run tests (push) Successful in 2m12s
2026-01-15 17:56:12 +01:00
b6497fd422 PaymentVariantsWindow: Select last variant by default and ask user before deleting
All checks were successful
Test / Run tests (push) Successful in 2m33s
2026-01-15 14:03:00 +01:00
f141485537 Documents: Only show header and footer on BusinessDocuments 2026-01-15 14:01:21 +01:00
34 changed files with 500 additions and 147 deletions

View File

@@ -2,6 +2,110 @@
Changelog Changelog
========= =========
[v1.0.3.4][v1.0.3.4] (2026-02-19) {#v1.0.3.4}
---------------------------------------------
### Neue Funktionen {#v1.0.3.4-features}
* Bei Anlieferungsbestätigungen (`DeliveryConfirmation`) wird nun auch die Summe aller abgewerteten Lieferungen angeführt. (9e5f709d42)
* Bei der Auszahlung werden Traubengutschriften (`CreditNote`) nur noch für Mitglieder erstellt, die einen positiven Betrag ausgezahlt bekommen. (432c511b85)
### Behobene Fehler {#v1.0.3.4-bugfixes}
* Im Haupt-Fenster (`MainWindow`) wurde der Tooltip für den Knopf _Sorten-/Qual.aufschlüssel_ korrigiert. (2c0b000073)
* Falsch gesetzte `xtime` und `mtime` bei Lieferungen korrigiert. (23db4de1ee)
* Beim Import von Lieferungen ohne `kgnr` tritt nun kein Fehler auf. (6818491ae3)
* Beim Abfragen von neuen Daten zum Synchronisieren werden alle Fehler abgefangen. (a47904cf0b)
### Sonstiges {#v1.0.3.4-misc}
* Pdfium wird nun direkt importiert (anstatt über PdfiumViewer). (4cd7ef85a1)
* Auszahlungsvarianten von vergangenen Saisons sind nun nicht mehr für eine Bearbeitung gesperrt. (7e22759c33)
* Im Rundschreiben-Fenster (`MailWindow`) ist es nun möglich Dokumente durch Doppelklick an-/abzuwählen. (6f081811c4)
* Abhängigkeiten aktualisiert. (19c3322ef2, 0e2b004b0d)
[v1.0.3.4]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.3.4
[v1.0.3.3][v1.0.3.3] (2026-02-05) {#v1.0.3.3}
---------------------------------------------
### Behobene Fehler {#v1.0.3.3-bugfixes}
* Hotfix: Probleme beim Drucken seit der Installation von [v1.0.3.0](#v1.0.3.0). (c8911c0acb, 8f423e3e92)
[v1.0.3.3]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.3.3
[v1.0.3.2][v1.0.3.2] (2026-02-04) {#v1.0.3.2}
---------------------------------------------
### Behobene Fehler {#v1.0.3.2-bugfixes}
* Hotfix: Probleme bei der Installation von [v1.0.3.0](#v1.0.3.0). (7adc9f89e4)
[v1.0.3.2]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.3.2
[v1.0.3.1][v1.0.3.1] (2026-01-19) {#v1.0.3.1}
---------------------------------------------
### Behobene Fehler {#v1.0.3.1-bugfixes}
* Hotfix: Probleme bei der Installation von [v1.0.3.0](#v1.0.3.0). (e63de0d867)
[v1.0.3.1]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.3.1
[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} [v1.0.2.0][v1.0.2.0] (2025-11-10) {#v1.0.2.0}
--------------------------------------------- ---------------------------------------------
@@ -226,7 +330,7 @@ Changelog
* Bei Traubengutschriften (`CreditNote`) wurde der Rebelzuschlag immer angeführt, auch wenn dieser in der zugrundeliegenden Berechnung nicht berücksichtigt wurde. (336aef5c70) * 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]) * 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]) * 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) * Im Lieferungen-Fenster (`DeliveryAdminWindow`) und im Mitglieder-Fenster (`MemberAdminWindow`) wird der Tooltip für Gewicht/Gradation mit korrektem Layout angezeigt. (e9f389b885)
* Bei Traubengutschriften (`CreditNote`) werden längere Freitexte vollständig angezeigt statt abgeschnitten. ([#62][i62]) * Bei Traubengutschriften (`CreditNote`) werden längere Freitexte vollständig angezeigt statt abgeschnitten. ([#62][i62])
### Sonstiges {#v1.0.0.0-misc} ### Sonstiges {#v1.0.0.0-misc}

View File

@@ -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>" +

View File

@@ -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;
} }

View File

@@ -100,6 +100,12 @@
<td></td> <td></td>
<td></td> <td></td>
</tr> </tr>
<tr>
<td colspan="8">Davon abgewertet:</td>
<td colspan="2" class="number">@($"{Model.Data.Rows.Where(p => p.IsDepreciated).Sum(p => p.Weight):N0}")</td>
<td></td>
<td></td>
</tr>
</tbody> </tbody>
</table> </table>
@Raw(BusinessDocument.PrintSortenaufteilung(Model.MemberStats)) @Raw(BusinessDocument.PrintSortenaufteilung(Model.MemberStats))

View File

@@ -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;
} }

View File

@@ -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;
} }

View File

@@ -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 {
@@ -39,15 +40,8 @@ namespace Elwig.Documents {
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("Bio-KSt.", c.OrganicAuthority).NextLine()
.Item("UID", c.UstIdNr).Item("BIC", c.Bic).Item("IBAN", c.Iban)
.ToString();
Date = DateOnly.FromDateTime(Utils.Today); Date = DateOnly.FromDateTime(Utils.Today);
} }
@@ -105,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);
@@ -115,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, IsDoublePaged, 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, IsDoublePaged); 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);
} }

View File

@@ -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;
} }

View File

@@ -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;
} }

View File

@@ -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;
} }

View File

@@ -3,11 +3,13 @@
<PropertyGroup> <PropertyGroup>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net10.0-windows</TargetFramework> <TargetFramework>net10.0-windows</TargetFramework>
<PlatformTarget>x64</PlatformTarget>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms>
<PreserveCompilationContext>true</PreserveCompilationContext> <PreserveCompilationContext>true</PreserveCompilationContext>
<ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon> <ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon>
<Version>1.0.2.0</Version> <Version>1.0.3.4</Version>
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages> <SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<ApplicationManifest>app.manifest</ApplicationManifest> <ApplicationManifest>app.manifest</ApplicationManifest>
@@ -21,24 +23,22 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="bblanchon.PDFium.Win32" Version="147.0.7690" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" /> <PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="LinqKit" Version="1.3.9" /> <PackageReference Include="LinqKit" Version="1.3.11" />
<PackageReference Include="MailKit" Version="4.14.1" /> <PackageReference Include="MailKit" Version="4.15.0" />
<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="10.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="10.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.3" />
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="10.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="10.0.3" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.3595.46" /> <PackageReference Include="Microsoft.Web.WebView2" Version="1.0.3800.47" />
<PackageReference Include="NJsonSchema" Version="11.5.2" /> <PackageReference Include="NJsonSchema" Version="11.5.2" />
<PackageReference Include="PdfiumViewer" Version="2.13.0" />
<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.1.57" /> <PackageReference Include="ScottPlot.WPF" Version="5.1.57" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="3.0.2" /> <PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="3.0.2" />
<PackageReference Include="System.IO.Hashing" Version="10.0.0" /> <PackageReference Include="System.IO.Hashing" Version="10.0.3" />
<PackageReference Include="System.IO.Ports" Version="10.0.0" /> <PackageReference Include="System.IO.Ports" Version="10.0.3" />
<PackageReference Include="System.Management" Version="10.0.0" /> <PackageReference Include="System.Management" Version="10.0.3" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="10.0.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -9,7 +9,7 @@ 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 = 36; public static readonly int RequiredSchemaVersion = 37;
private static int VersionOffset = 0; private static int VersionOffset = 0;

View File

@@ -54,14 +54,14 @@ namespace Elwig.Helpers.Billing {
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,
IIF(lc.amount >= 0, ROUND(lp.amount / POW(10, s.precision - 2)), 0) AS prev_net_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,
IIF(lc.amount >= 0, lc.modifiers, 0) 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
@@ -80,7 +80,7 @@ namespace Elwig.Helpers.Billing {
LEFT JOIN v_penalty_area_commitments u ON (u.year, u.mgnr) = (s.year, m.mgnr) LEFT JOIN v_penalty_area_commitments u ON (u.year, u.mgnr) = (s.year, m.mgnr)
LEFT JOIN v_auto_business_shares a ON (a.year, a.mgnr) = (s.year, m.mgnr) LEFT JOIN v_auto_business_shares a ON (a.year, a.mgnr) = (s.year, m.mgnr)
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} AND p.amount > COALESCE(lp.amount, 0);
"""); """);
await cnx.ExecuteBatch($""" 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});

View File

@@ -206,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;

View File

@@ -783,15 +783,15 @@ namespace Elwig.Helpers.Export {
Comment = json["comment"]?.AsValue().GetValue<string>(), Comment = json["comment"]?.AsValue().GetValue<string>(),
ImportedAt = DateTime.Now, ImportedAt = DateTime.Now,
}, json["parts"]!.AsArray().Select(p => p!.AsObject()).Select(p => { }, json["parts"]!.AsArray().Select(p => p!.AsObject()).Select(p => {
var kgnr = p["kgnr"]!.AsValue().GetValue<int>(); var kgnr = p["kgnr"]?.AsValue().GetValue<int>();
var ried = p["ried"]?.AsValue().GetValue<string>(); var ried = p["ried"]?.AsValue().GetValue<string>();
WbRd? rd = null; WbRd? rd = null;
if (ried != null) { if (ried != null && kgnr != null) {
var rde = riede.GetValueOrDefault(kgnr, []); var rde = riede.GetValueOrDefault(kgnr.Value, []);
rd = rde.FirstOrDefault(r => r.Name == ried); rd = rde.FirstOrDefault(r => r.Name == ried);
if (rd == null) { if (rd == null) {
rd = new WbRd { rd = new WbRd {
KgNr = kgnr, KgNr = kgnr.Value,
RdNr = (rde.Count == 0 ? 1 : rde.Max(r => r.RdNr)) + 1, RdNr = (rde.Count == 0 ? 1 : rde.Max(r => r.RdNr)) + 1,
Name = ried, Name = ried,
}; };

View File

@@ -14,14 +14,23 @@ namespace Elwig.Helpers.Printing {
.UseMemoryCachingProvider() .UseMemoryCachingProvider()
.Build(); .Build();
await Task.Delay(500);
await e.CompileTemplateAsync("Document"); await e.CompileTemplateAsync("Document");
await e.CompileTemplateAsync("BusinessDocument"); await e.CompileTemplateAsync("BusinessDocument");
await e.CompileTemplateAsync("BusinessLetter"); await e.CompileTemplateAsync("BusinessLetter");
await e.CompileTemplateAsync("DeliveryNote");
await e.CompileTemplateAsync("CreditNote"); await e.CompileTemplateAsync("CreditNote");
await e.CompileTemplateAsync("DeliveryJournal"); await e.CompileTemplateAsync("DeliveryAncmtList");
await e.CompileTemplateAsync("Letterhead");
await e.CompileTemplateAsync("DeliveryConfirmation"); await e.CompileTemplateAsync("DeliveryConfirmation");
await e.CompileTemplateAsync("DeliveryDepreciationList");
await e.CompileTemplateAsync("DeliveryJournal");
await e.CompileTemplateAsync("DeliveryNote");
await e.CompileTemplateAsync("Letterhead");
await e.CompileTemplateAsync("MemberDataSheet");
await e.CompileTemplateAsync("MemberList");
await e.CompileTemplateAsync("PaymentVariantSummary");
await e.CompileTemplateAsync("WineQualityStatistics");
Engine = e; Engine = e;
evtHandler?.Invoke(); evtHandler?.Invoke();

View File

@@ -1,28 +1,32 @@
using System.Threading.Tasks;
using Elwig.Windows; using Elwig.Windows;
using System.Diagnostics;
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.InstallPath } 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;
public static async Task Init(Action? evtHandler = null) { public static async Task Init(Action? evtHandler = null) {
PdfiumNative.FPDF_InitLibrary();
// NOTE: If the WinziPrint daemon is already running this will succeed, but the process will fail. // NOTE: If the WinziPrint daemon is already running this will succeed, but the process will fail.
// Should be no problem, as long as the daemon is not closed // Should be no problem, as long as the daemon is not closed
var p = new Process() { StartInfo = new() { var p = new Process() { StartInfo = new() {
@@ -43,27 +47,49 @@ namespace Elwig.Helpers.Printing {
public static Task Cleanup() { public static Task Cleanup() {
WinziPrintProc?.Kill(true); WinziPrintProc?.Kill(true);
WinziPrintProc?.Close(); WinziPrintProc?.Close();
PdfiumNative.FPDF_DestroyLibrary();
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]);
@@ -98,9 +124,9 @@ namespace Elwig.Helpers.Printing {
public static Task Print(string path, PrinterSettings settings) { public static Task Print(string path, PrinterSettings settings) {
try { try {
using var doc = PdfDocument.Load(path); using var printDoc = new PdfPrintDocument(path) {
using var printDoc = doc.CreatePrintDocument(PdfPrintMode.CutMargin); PrinterSettings = settings,
printDoc.PrinterSettings = settings; };
printDoc.Print(); printDoc.Print();
} catch (Exception e) { } catch (Exception e) {
MessageBox.Show("Beim Drucken ist ein Fehler aufgetreten:\n\n" + e.Message, "Fehler beim Drucken", MessageBoxButton.OK, MessageBoxImage.Error); MessageBox.Show("Beim Drucken ist ein Fehler aufgetreten:\n\n" + e.Message, "Fehler beim Drucken", MessageBoxButton.OK, MessageBoxImage.Error);

View File

@@ -0,0 +1,102 @@
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Printing;
using System.Runtime.InteropServices;
namespace Elwig.Helpers.Printing {
public class PdfPrintDocument : PrintDocument {
private readonly IntPtr _handle;
private readonly int _pageCount;
private readonly double _dpi;
private int _currentPage;
public PdfPrintDocument(string path, string? password = null, double dpi = 300.0) :
base() {
_handle = PdfiumNative.FPDF_LoadDocument(path, password);
_pageCount = PdfiumNative.FPDF_GetPageCount(_handle);
_dpi = dpi;
}
protected override void Dispose(bool disposing) {
PdfiumNative.FPDF_CloseDocument(_handle);
base.Dispose(disposing);
}
protected override void OnBeginPrint(PrintEventArgs evt) {
_currentPage = (PrinterSettings.FromPage != 0) ? (PrinterSettings.FromPage - 1) : 0;
base.OnBeginPrint(evt);
}
protected override void OnPrintPage(PrintPageEventArgs evt) {
if (_currentPage < _pageCount) {
IntPtr page = PdfiumNative.FPDF_LoadPage(_handle, _currentPage);
double width = PdfiumNative.FPDF_GetPageWidth(page);
double height = PdfiumNative.FPDF_GetPageHeight(page);
int pixelWidth = (int)(width / 72.0 * _dpi);
int pixelHeight = (int)(height / 72.0 * _dpi);
IntPtr bitmap = PdfiumNative.FPDFBitmap_Create(pixelWidth, pixelHeight, 1);
PdfiumNative.FPDF_RenderPageBitmap(bitmap, page, 0, 0, pixelWidth, pixelHeight, 0, 0);
IntPtr buffer = PdfiumNative.FPDFBitmap_GetBuffer(bitmap);
int stride = PdfiumNative.FPDFBitmap_GetStride(bitmap);
using (var bmp = new Bitmap(pixelWidth, pixelHeight, stride, PixelFormat.Format32bppArgb, buffer)) {
evt.Graphics?.DrawImage(bmp, evt.PageBounds);
}
_currentPage++;
PdfiumNative.FPDFBitmap_Destroy(bitmap);
PdfiumNative.FPDF_ClosePage(page);
}
evt.HasMorePages = _currentPage < ((PrinterSettings.ToPage == 0) ? _pageCount : Math.Min(PrinterSettings.ToPage, _pageCount));
base.OnPrintPage(evt);
}
}
internal partial class PdfiumNative {
[LibraryImport("pdfium.dll")]
public static partial void FPDF_InitLibrary();
[LibraryImport("pdfium.dll")]
public static partial void FPDF_DestroyLibrary();
[LibraryImport("pdfium.dll", StringMarshalling = StringMarshalling.Utf8)]
public static partial IntPtr FPDF_LoadDocument(string filePath, string? password);
[LibraryImport("pdfium.dll")]
public static partial void FPDF_CloseDocument(IntPtr document);
[LibraryImport("pdfium.dll")]
public static partial int FPDF_GetPageCount(IntPtr document);
[LibraryImport("pdfium.dll")]
public static partial IntPtr FPDF_LoadPage(IntPtr document, int pageIndex);
[LibraryImport("pdfium.dll")]
public static partial void FPDF_ClosePage(IntPtr page);
[LibraryImport("pdfium.dll")]
public static partial double FPDF_GetPageWidth(IntPtr page);
[LibraryImport("pdfium.dll")]
public static partial double FPDF_GetPageHeight(IntPtr page);
[LibraryImport("pdfium.dll")]
public static partial IntPtr FPDFBitmap_Create(int width, int height, int alpha);
[LibraryImport("pdfium.dll")]
public static partial void FPDFBitmap_Destroy(IntPtr bitmap);
[LibraryImport("pdfium.dll")]
public static partial IntPtr FPDFBitmap_GetBuffer(IntPtr bitmap);
[LibraryImport("pdfium.dll")]
public static partial int FPDFBitmap_GetStride(IntPtr bitmap);
[LibraryImport("pdfium.dll")]
public static partial void FPDF_RenderPageBitmap(IntPtr bitmap, IntPtr page, int start_x, int start_y, int size_x, int size_y, int rotate, int flags);
}
}

View File

@@ -75,6 +75,7 @@ namespace Elwig.Models.Dtos {
public string? Attribute; public string? Attribute;
public string? Cultivation; public string? Cultivation;
public string QualityLevel; public string QualityLevel;
public bool IsDepreciated;
public (double Oe, double Kmw) Gradation; public (double Oe, double Kmw) Gradation;
public string[] Modifiers; public string[] Modifiers;
public int Weight; public int Weight;
@@ -89,17 +90,15 @@ namespace Elwig.Models.Dtos {
Attribute = p.Attribute?.Name; Attribute = p.Attribute?.Name;
Cultivation = p.Cultivation?.Name; Cultivation = p.Cultivation?.Name;
QualityLevel = p.Quality.Name; QualityLevel = p.Quality.Name;
IsDepreciated = p.QualId == "WEI";
Gradation = (p.Oe, p.Kmw); Gradation = (p.Oe, p.Kmw);
Modifiers = p.Modifiers Modifiers = [.. p.Modifiers.Select(m => m.Name)];
.Select(m => m.Name)
.ToArray();
Weight = p.Weight; Weight = p.Weight;
IsNetWeight = p.IsNetWeight; IsNetWeight = p.IsNetWeight;
Buckets = p.Buckets Buckets = [.. p.Buckets
.Where(b => b.Value > 0) .Where(b => b.Value > 0)
.OrderByDescending(b => b.BktNr) .OrderByDescending(b => b.BktNr)
.Select(b => (b.Discr == "_" ? "ungeb." : $"geb. {p.SortId}{b.Discr}", b.Value)) .Select(b => (b.Discr == "_" ? "ungeb." : $"geb. {p.SortId}{b.Discr}", b.Value))];
.ToArray();
} }
} }
} }

View File

@@ -1,6 +1,8 @@
-- schema version 34 to 35 -- schema version 34 to 35
UPDATE client_parameter SET value = '0' WHERE param = 'ENABLE_TIME_TRIGGERS';
ALTER TABLE delivery_part ADD COLUMN unloading TEXT DEFAULT NULL; ALTER TABLE delivery_part ADD COLUMN unloading TEXT DEFAULT NULL;
UPDATE delivery_part SET unloading = 'pumped' WHERE lesewagen; 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)); 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; ALTER TABLE delivery_part DROP COLUMN lesewagen;
UPDATE client_parameter SET value = '1' WHERE param = 'ENABLE_TIME_TRIGGERS';

View File

@@ -0,0 +1,12 @@
-- schema version 36 to 37
UPDATE client_parameter SET value = '0' WHERE param = 'ENABLE_TIME_TRIGGERS';
-- fix old deliveries
UPDATE delivery SET xtime = NULL, mtime = ctime WHERE year <= 2022 AND mtime >= 1768521600 AND mtime < 1772323200;
UPDATE delivery_part SET xtime = NULL, mtime = ctime WHERE year <= 2022 AND mtime >= 1768521600 AND mtime < 1772323200;
-- clear xtime at one laptop to force updates from this one
UPDATE delivery SET xtime = NULL WHERE year >= 2023 AND xtime >= 1771200000 AND xtime < 1771286400;
UPDATE delivery_part SET xtime = NULL WHERE year >= 2023 AND xtime >= 1771200000 AND xtime < 1771286400;
UPDATE client_parameter SET value = '1' WHERE param = 'ENABLE_TIME_TRIGGERS';

View File

@@ -271,7 +271,11 @@ namespace Elwig.Services {
} }
public static async Task<bool> ChangesAvailable(AppDbContext ctx, string url, string username, string password) { 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); try {
return await ctx.Members.AnyAsync(ChangedMembers) || await ctx.Deliveries.AnyAsync(ChangedDeliveries) || (Utils.HasInternetConnectivity() && (await GetFilesToImport(url, username, password)).Count > 0);
} catch {
return false;
}
} }
} }
} }

View File

@@ -14,7 +14,7 @@ namespace Elwig.ViewModels {
private IEnumerable<PaymentVar> _paymentVariants = []; private IEnumerable<PaymentVar> _paymentVariants = [];
public BillingData? BillingData; public BillingData? BillingData;
public bool SeasonLocked; public bool SeasonLocked = false; // never locked
public bool WeightModifierChanged; public bool WeightModifierChanged;
[ObservableProperty] [ObservableProperty]

View File

@@ -22,7 +22,7 @@
Programmiersprache: C#<LineBreak/> Programmiersprache: C#<LineBreak/>
Framework: Windows Presentation Framework (WPF)<LineBreak/> Framework: Windows Presentation Framework (WPF)<LineBreak/>
Datenbank: <Hyperlink NavigateUri="https://sqlite.org/" RequestNavigate="Hyperlink_RequestNavigate">SQLite</Hyperlink><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/> 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/bblanchon/pdfium-binaries" RequestNavigate="Hyperlink_RequestNavigate">Pdfium</Hyperlink><LineBreak/>
Paketierung: <Hyperlink NavigateUri="https://www.firegiant.com/wixtoolset/" RequestNavigate="Hyperlink_RequestNavigate">WiX Toolset</Hyperlink> Paketierung: <Hyperlink NavigateUri="https://www.firegiant.com/wixtoolset/" RequestNavigate="Hyperlink_RequestNavigate">WiX Toolset</Hyperlink>
</TextBlock> </TextBlock>

View File

@@ -69,7 +69,7 @@
Grid.Column="0" Margin="10,8,10,10"/> Grid.Column="0" Margin="10,8,10,10"/>
<ListBox x:Name="AvaiableDocumentsList" <ListBox x:Name="AvaiableDocumentsList"
Grid.Column="0" Margin="10,30,10,10" Grid.Column="0" Margin="10,30,10,10"
SelectionChanged="AvaiableDocumentsList_SelectionChanged"/> SelectionChanged="AvaiableDocumentsList_SelectionChanged" MouseDoubleClick="AvaiableDocumentsList_MouseDoubleClick"/>
<Button x:Name="DocumentAddButton" Content="&#xF0AF;" FontFamily="Segoe MDL2 Assets" FontSize="14" <Button x:Name="DocumentAddButton" Content="&#xF0AF;" FontFamily="Segoe MDL2 Assets" FontSize="14"
Grid.Column="1" Margin="0,0,0,30" VerticalAlignment="Center" Height="25" IsEnabled="False" Grid.Column="1" Margin="0,0,0,30" VerticalAlignment="Center" Height="25" IsEnabled="False"
@@ -82,7 +82,7 @@
Grid.Column="2" Margin="10,8,10,10"/> Grid.Column="2" Margin="10,8,10,10"/>
<ListBox x:Name="SelectedDocumentsList" DisplayMemberPath="Name" <ListBox x:Name="SelectedDocumentsList" DisplayMemberPath="Name"
Grid.Column="2" Margin="10,30,10,37" Grid.Column="2" Margin="10,30,10,37"
SelectionChanged="SelectedDocumentsList_SelectionChanged"> SelectionChanged="SelectedDocumentsList_SelectionChanged" MouseDoubleClick="SelectedDocumentsList_MouseDoubleClick">
<ListBox.InputBindings> <ListBox.InputBindings>
<KeyBinding Key="Delete" Command="{Binding Path=DeleteCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MailWindow}}}"/> <KeyBinding Key="Delete" Command="{Binding Path=DeleteCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MailWindow}}}"/>
</ListBox.InputBindings> </ListBox.InputBindings>
@@ -138,58 +138,61 @@
<RadioButton GroupName="Recipients" x:Name="RecipientsNonDeliveryMembersInput" Content="Nicht-Lieferanten der Saison" <RadioButton GroupName="Recipients" x:Name="RecipientsNonDeliveryMembersInput" Content="Nicht-Lieferanten der Saison"
Margin="10,90,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,90,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="RecipientsInput_Changed" Unchecked="RecipientsInput_Changed"/> Checked="RecipientsInput_Changed" Unchecked="RecipientsInput_Changed"/>
<RadioButton GroupName="Recipients" x:Name="RecipientsCustomInput" Content="Benutzerdefiniert" <RadioButton GroupName="Recipients" x:Name="RecipientsCreditMembersInput" Content="Empfänger von Gutschriften"
Margin="10,110,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,110,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="RecipientsInput_Changed" Unchecked="RecipientsInput_Changed"/> Checked="RecipientsInput_Changed" Unchecked="RecipientsInput_Changed"/>
<RadioButton GroupName="Recipients" x:Name="RecipientsCustomInput" Content="Benutzerdefiniert"
Margin="10,130,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="RecipientsInput_Changed" Unchecked="RecipientsInput_Changed"/>
<Label Content="Zwst.:" x:Name="MemberBranchLabel" Margin="10,140,0,10"/> <Label Content="Zwst.:" x:Name="MemberBranchLabel" Margin="10,160,0,10"/>
<ctrl:CheckComboBox x:Name="MemberBranchInput" AllItemsSelectedContent="Alle Stammzweigstellen" Delimiter=", " DisplayMemberPath="Name" <ctrl:CheckComboBox x:Name="MemberBranchInput" AllItemsSelectedContent="Alle Stammzweigstellen" Delimiter=", " DisplayMemberPath="Name"
Margin="50,140,10,10" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="25" Margin="50,160,10,10" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="25"
SelectionChanged="MemberInput_SelectionChanged"/> SelectionChanged="MemberInput_SelectionChanged"/>
<Label Content="Gem.:" x:Name="MemberKgLabel" Margin="10,170,0,10"/> <Label Content="Gem.:" x:Name="MemberKgLabel" Margin="10,190,0,10"/>
<ctrl:CheckComboBox x:Name="MemberKgInput" AllItemsSelectedContent="Alle Stammgemeinden" Delimiter=", " DisplayMemberPath="Name" <ctrl:CheckComboBox x:Name="MemberKgInput" AllItemsSelectedContent="Alle Stammgemeinden" Delimiter=", " DisplayMemberPath="Name"
IsSelectAllActive="True" SelectAllContent="Alle Stammgemeinden" IsSelectAllActive="True" SelectAllContent="Alle Stammgemeinden"
Margin="50,170,10,10" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="25" Margin="50,190,10,10" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="25"
SelectionChanged="MemberInput_SelectionChanged"/> SelectionChanged="MemberInput_SelectionChanged"/>
<Label Content="Bio-Betrieb:" x:Name="MemberOrganicLabel" Margin="10,200,0,10"/> <Label Content="Bio-Betrieb:" x:Name="MemberOrganicLabel" Margin="10,220,0,10"/>
<RadioButton x:Name="MemberOrganicYesInput" Content="Ja" GroupName="MemberOrganic" <RadioButton x:Name="MemberOrganicYesInput" Content="Ja" GroupName="MemberOrganic"
Margin="80,205,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="80,225,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="MemberInput_Checked"/> Checked="MemberInput_Checked"/>
<RadioButton x:Name="MemberOrganicNoInput" Content="Nein" GroupName="MemberOrganic" <RadioButton x:Name="MemberOrganicNoInput" Content="Nein" GroupName="MemberOrganic"
Margin="125,205,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="125,225,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="MemberInput_Checked"/> Checked="MemberInput_Checked"/>
<RadioButton x:Name="MemberOrganicIndifferentInput" Content="Egal" GroupName="MemberOrganic" <RadioButton x:Name="MemberOrganicIndifferentInput" Content="Egal" GroupName="MemberOrganic"
Margin="180,205,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="180,225,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="MemberInput_Checked"/> Checked="MemberInput_Checked"/>
<Label Content="Funktionär:" x:Name="MemberFunktionärLabel" Margin="10,230,0,10"/> <Label Content="Funktionär:" x:Name="MemberFunktionärLabel" Margin="10,250,0,10"/>
<RadioButton x:Name="MemberFunktionärYesInput" Content="Ja" GroupName="MemberFunktionär" <RadioButton x:Name="MemberFunktionärYesInput" Content="Ja" GroupName="MemberFunktionär"
Margin="80,235,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="80,255,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="MemberInput_Checked"/> Checked="MemberInput_Checked"/>
<RadioButton x:Name="MemberFunktionärNoInput" Content="Nein" GroupName="MemberFunktionär" <RadioButton x:Name="MemberFunktionärNoInput" Content="Nein" GroupName="MemberFunktionär"
Margin="125,235,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="125,255,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="MemberInput_Checked"/> Checked="MemberInput_Checked"/>
<RadioButton x:Name="MemberFunktionärIndifferentInput" Content="Egal" GroupName="MemberFunktionär" <RadioButton x:Name="MemberFunktionärIndifferentInput" Content="Egal" GroupName="MemberFunktionär"
Margin="180,235,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="180,255,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="MemberInput_Checked"/> Checked="MemberInput_Checked"/>
<Label Content="Vtrg.:" x:Name="MemberAreaComLabel" Margin="10,260,0,10"/> <Label Content="Vtrg.:" x:Name="MemberAreaComLabel" Margin="10,280,0,10"/>
<ctrl:CheckComboBox x:Name="MemberAreaComInput" AllItemsSelectedContent="Alle Vertragsarten" Delimiter=", " DisplayMemberPath="VtrgId" <ctrl:CheckComboBox x:Name="MemberAreaComInput" AllItemsSelectedContent="Alle Vertragsarten" Delimiter=", " DisplayMemberPath="VtrgId"
IsSelectAllActive="True" SelectAllContent="Alle Vertragsarten" IsSelectAllActive="True" SelectAllContent="Alle Vertragsarten"
Margin="50,260,10,10" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="25" Margin="50,280,10,10" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="25"
SelectionChanged="MemberInput_SelectionChanged"/> SelectionChanged="MemberInput_SelectionChanged"/>
<Label Content="Tag:" x:Name="MemberDeliveryAncmtLabel" Margin="10,260,0,10"/> <Label Content="Tag:" x:Name="MemberDeliveryAncmtLabel" Margin="10,280,0,10"/>
<ctrl:CheckComboBox x:Name="MemberDeliveryAncmtInput" AllItemsSelectedContent="Alle Lesepläne" Delimiter=", " DisplayMemberPath="Identifier" <ctrl:CheckComboBox x:Name="MemberDeliveryAncmtInput" AllItemsSelectedContent="Alle Lesepläne" Delimiter=", " DisplayMemberPath="Identifier"
IsSelectAllActive="True" SelectAllContent="Alle Lesepläne" IsSelectAllActive="True" SelectAllContent="Alle Lesepläne"
Margin="50,260,10,10" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="25" Margin="50,280,10,10" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="25"
SelectionChanged="MemberInput_SelectionChanged"/> SelectionChanged="MemberInput_SelectionChanged"/>
<ctrl:CheckComboBox x:Name="MemberCustomInput" AllItemsSelectedContent="Alle Mitglieder" Delimiter=", " DisplayMemberPath="AdministrativeName" <ctrl:CheckComboBox x:Name="MemberCustomInput" AllItemsSelectedContent="Alle Mitglieder" Delimiter=", " DisplayMemberPath="AdministrativeName"
IsSelectAllActive="True" SelectAllContent="Alle Mitglieder" IsSelectAllActive="True" SelectAllContent="Alle Mitglieder"
Margin="10,140,10,10" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="25" Margin="10,160,10,10" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="25"
SelectionChanged="MemberInput_SelectionChanged"/> SelectionChanged="MemberInput_SelectionChanged"/>
</Grid> </Grid>
</GroupBox> </GroupBox>
@@ -302,6 +305,9 @@
<Button x:Name="GenerateButton" Content="Generieren" <Button x:Name="GenerateButton" Content="Generieren"
Grid.Row="0" Grid.Column="0" FontSize="14" Grid.Row="0" Grid.Column="0" FontSize="14"
Click="GenerateButton_Click"/> Click="GenerateButton_Click"/>
<Button x:Name="AbortButton" Content="Abbrechen" Visibility="Hidden" IsEnabled="False"
Grid.Row="0" Grid.Column="0" FontSize="14"
Click="AbortButton_Click"/>
<ProgressBar x:Name="ProgressBar" <ProgressBar x:Name="ProgressBar"
Grid.Row="2" Grid.Column="0" SnapsToDevicePixels="True"/> Grid.Row="2" Grid.Column="0" SnapsToDevicePixels="True"/>

View File

@@ -13,6 +13,7 @@ using System.Collections.ObjectModel;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
@@ -61,6 +62,8 @@ namespace Elwig.Windows {
protected Dictionary<Member, List<Document>>? PrintMemberDocuments; protected Dictionary<Member, List<Document>>? PrintMemberDocuments;
protected Dictionary<Member, List<Document>>? EmailDocuments; protected Dictionary<Member, List<Document>>? EmailDocuments;
private CancellationTokenSource? CancelGeneration;
public static readonly DependencyProperty PostalAllCountProperty = DependencyProperty.Register(nameof(PostalAllCount), typeof(int), typeof(MailWindow)); public static readonly DependencyProperty PostalAllCountProperty = DependencyProperty.Register(nameof(PostalAllCount), typeof(int), typeof(MailWindow));
public int PostalAllCount { public int PostalAllCount {
get => (int)GetValue(PostalAllCountProperty); get => (int)GetValue(PostalAllCountProperty);
@@ -330,7 +333,35 @@ namespace Elwig.Windows {
} }
} }
private async void DocumentAddButton_Click(object sender, RoutedEventArgs evt) { private void AvaiableDocumentsList_MouseDoubleClick(object sender, MouseEventArgs evt) {
if (evt.LeftButton != MouseButtonState.Pressed) return;
var src = evt.OriginalSource;
if (src is Border b) {
src = (b.Child as ContentPresenter)?.Content.ToString();
} else if (src is TextBlock t) {
src = t.Text;
} else {
return;
}
AvaiableDocumentsList.SelectedIndex = AvaiableDocumentsList.ItemsSource.Cast<object?>().ToList().IndexOf(src);
DocumentAddButton_Click(sender, null);
}
private void SelectedDocumentsList_MouseDoubleClick(object sender, MouseEventArgs evt) {
if (evt.LeftButton != MouseButtonState.Pressed) return;
var src = evt.OriginalSource;
if (src is Border b) {
src = (b.Child as ContentPresenter)?.Content.ToString();
} else if (src is TextBlock t) {
src = t.Text;
} else {
return;
}
SelectedDocumentsList.SelectedItem = SelectedDocs.FirstOrDefault(d => d.Name == (string?)src);
DocumentRemoveButton_Click(sender, null);
}
private async void DocumentAddButton_Click(object sender, RoutedEventArgs? evt) {
var idx = AvaiableDocumentsList.SelectedIndex; var idx = AvaiableDocumentsList.SelectedIndex;
using var ctx = new AppDbContext(); using var ctx = new AppDbContext();
if (AvaiableDocumentsList.SelectedItem is not string s) if (AvaiableDocumentsList.SelectedItem is not string s)
@@ -344,13 +375,13 @@ namespace Elwig.Windows {
var name = s.Split(" ")[^1]; var name = s.Split(" ")[^1];
var pv = await ctx.PaymentVariants.SingleAsync(v => v.Year == Year && v.Name == name)!; var pv = await ctx.PaymentVariants.SingleAsync(v => v.Year == Year && v.Name == name)!;
SelectedDocs.Add(new(DocType.CreditNote, s, (pv.Year, pv.AvNr))); SelectedDocs.Add(new(DocType.CreditNote, s, (pv.Year, pv.AvNr)));
RecipientsDeliveryMembersInput.IsChecked = true; RecipientsCreditMembersInput.IsChecked = true;
} }
SelectedDocumentsList.SelectedIndex = SelectedDocs.Count - 1; SelectedDocumentsList.SelectedIndex = SelectedDocs.Count - 1;
await UpdateRecipients(ctx); await UpdateRecipients(ctx);
} }
private async void DocumentRemoveButton_Click(object sender, RoutedEventArgs evt) { private async void DocumentRemoveButton_Click(object sender, RoutedEventArgs? evt) {
DeleteCommand.Execute(null); DeleteCommand.Execute(null);
using var ctx = new AppDbContext(); using var ctx = new AppDbContext();
await UpdateRecipients(ctx); await UpdateRecipients(ctx);
@@ -451,8 +482,11 @@ namespace Elwig.Windows {
query = query.Where(m => m.Deliveries.Any(d => d.Year == Year)); query = query.Where(m => m.Deliveries.Any(d => d.Year == Year));
} else if (RecipientsNonDeliveryMembersInput.IsChecked == true) { } else if (RecipientsNonDeliveryMembersInput.IsChecked == true) {
query = query.Where(m => m.IsActive && !m.Deliveries.Any(d => d.Year == Year)); query = query.Where(m => m.IsActive && !m.Deliveries.Any(d => d.Year == Year));
} else if (RecipientsActiveMembersInput.IsChecked == true && SelectedDocs.Any(d => d.Type == DocType.DeliveryConfirmation || d.Type == DocType.CreditNote)) { } else if (RecipientsActiveMembersInput.IsChecked == true && SelectedDocs.Any(d => d.Type == DocType.DeliveryConfirmation || d.Type == DocType.CreditNote)) {
query = query.Where(m => m.IsActive || m.Deliveries.Any(d => d.Year == Year)); query = query.Where(m => m.IsActive || m.Deliveries.Any(d => d.Year == Year));
} else if (RecipientsCreditMembersInput.IsChecked == true) {
var avnrs = SelectedDocs.Where(d => d.Type == DocType.CreditNote).Select(d => (((int,int)?)d.Details)?.Item2).ToList();
query = query.Where(m => m.Credits.Any(c => c.Year == Year && avnrs.Contains(c.AvNr)));
} else { } else {
query = query.Where(m => m.IsActive); query = query.Where(m => m.IsActive);
} }
@@ -594,20 +628,32 @@ namespace Elwig.Windows {
} }
private void Window_Closed(object sender, EventArgs evt) { private void Window_Closed(object sender, EventArgs evt) {
CancelGeneration?.Dispose();
DisposeDocs(); DisposeDocs();
} }
private async void AbortButton_Click(object sender, RoutedEventArgs evt) {
AbortButton.IsEnabled = false;
CancelGeneration?.Cancel();
}
private async void GenerateButton_Click(object sender, RoutedEventArgs evt) { private async void GenerateButton_Click(object sender, RoutedEventArgs evt) {
LockInputs(); LockInputs();
PreviewButton.IsEnabled = false; PreviewButton.IsEnabled = false;
PrintButton.IsEnabled = false; PrintButton.IsEnabled = false;
EmailButton.IsEnabled = false; EmailButton.IsEnabled = false;
Mouse.OverrideCursor = Cursors.Wait; Mouse.OverrideCursor = Cursors.Wait;
AbortButton.IsEnabled = true;
AbortButton.Visibility = Visibility.Visible;
GenerateButton.IsEnabled = false; GenerateButton.IsEnabled = false;
GenerateButton.Visibility = Visibility.Hidden;
DisposeDocs(); DisposeDocs();
await UpdateClientParameters(); await UpdateClientParameters();
CancelGeneration?.Dispose();
CancelGeneration = new();
using var ctx = new AppDbContext(); using var ctx = new AppDbContext();
var doublePaged = DoublePagedInput.IsChecked == true; var doublePaged = DoublePagedInput.IsChecked == true;
@@ -662,6 +708,9 @@ namespace Elwig.Windows {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
UnlockInputs(); UnlockInputs();
GenerateButton.IsEnabled = true; GenerateButton.IsEnabled = true;
GenerateButton.Visibility = Visibility.Visible;
AbortButton.IsEnabled = false;
AbortButton.Visibility = Visibility.Hidden;
Mouse.OverrideCursor = null; Mouse.OverrideCursor = null;
return; return;
} }
@@ -679,6 +728,9 @@ namespace Elwig.Windows {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
UnlockInputs(); UnlockInputs();
GenerateButton.IsEnabled = true; GenerateButton.IsEnabled = true;
GenerateButton.Visibility = Visibility.Visible;
AbortButton.IsEnabled = false;
AbortButton.Visibility = Visibility.Hidden;
Mouse.OverrideCursor = null; Mouse.OverrideCursor = null;
return; return;
} }
@@ -735,11 +787,14 @@ namespace Elwig.Windows {
var hasPreviewDocs = memberDocs.Any(m => m.Docs.Any(d => d.Doc.IsPreview)); var hasPreviewDocs = memberDocs.Any(m => m.Docs.Any(d => d.Doc.IsPreview));
if (hasPreviewDocs) { if (hasPreviewDocs) {
var res = MessageBox.Show("Einige der ausgewählten Dokumente sind nur als vorläufig zu betrachten und können daher nicht verschickt/ausgedruckt werden!\n\nDies könnte an einer noch nicht festgesetzen Auszahlungsvariante liegen.\n\nSoll mit dem Generieren fortgefahren werden?", var res = MessageBox.Show("Einige der ausgewählten Dokumente sind nur als vorläufig zu betrachten und können daher nicht verschickt/ausgedruckt werden!\n\nDies könnte an einer noch nicht festgesetzen Auszahlungsvariante liegen oder daran, dass nicht alle Adressaten/Empfänger eine Traubengutschrift erhalten\n(\"Empfänger von Gutschriften\").\n\nSoll mit dem Generieren fortgefahren werden?",
"Vorläufige Dokumente", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel); "Vorläufige Dokumente", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (res != MessageBoxResult.OK) { if (res != MessageBoxResult.OK) {
UnlockInputs(); UnlockInputs();
GenerateButton.IsEnabled = true; GenerateButton.IsEnabled = true;
GenerateButton.Visibility = Visibility.Visible;
AbortButton.IsEnabled = false;
AbortButton.Visibility = Visibility.Hidden;
Mouse.OverrideCursor = null; Mouse.OverrideCursor = null;
return; return;
} }
@@ -771,7 +826,7 @@ namespace Elwig.Windows {
try { try {
foreach (var item1 in email.Select((e, i) => new { Index = i, e.Key, e.Value })) { foreach (var item1 in email.Select((e, i) => new { Index = i, e.Key, e.Value })) {
foreach (var item2 in item1.Value.Select((d, i) => new { Index = i, Doc = d })) { foreach (var item2 in item1.Value.Select((d, i) => new { Index = i, Doc = d })) {
await item2.Doc.Generate(new Progress<double>(v => { await item2.Doc.Generate(CancelGeneration.Token, new Progress<double>(v => {
ProgressBar.Value = v * (item2.Index + 1) / item1.Value.Count / totalNum + 100.0 * item1.Index / totalNum; ProgressBar.Value = v * (item2.Index + 1) / item1.Value.Count / totalNum + 100.0 * item1.Index / totalNum;
})); }));
} }
@@ -780,6 +835,9 @@ namespace Elwig.Windows {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
UnlockInputs(); UnlockInputs();
GenerateButton.IsEnabled = true; GenerateButton.IsEnabled = true;
GenerateButton.Visibility = Visibility.Visible;
AbortButton.IsEnabled = false;
AbortButton.Visibility = Visibility.Hidden;
Mouse.OverrideCursor = null; Mouse.OverrideCursor = null;
return; return;
} }
@@ -814,7 +872,7 @@ namespace Elwig.Windows {
try { try {
var print = Document.Merge(printDocs); var print = Document.Merge(printDocs);
print.IsDoublePaged = doublePaged; print.IsDoublePaged = doublePaged;
await print.Generate(new Progress<double>(v => { await print.Generate(CancelGeneration.Token, new Progress<double>(v => {
ProgressBar.Value = 100.0 * emailNum / totalNum + v * printNum / totalNum; ProgressBar.Value = 100.0 * emailNum / totalNum + v * printNum / totalNum;
})); }));
PrintDocument = print; PrintDocument = print;
@@ -823,6 +881,9 @@ namespace Elwig.Windows {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
UnlockInputs(); UnlockInputs();
GenerateButton.IsEnabled = true; GenerateButton.IsEnabled = true;
GenerateButton.Visibility = Visibility.Visible;
AbortButton.IsEnabled = false;
AbortButton.Visibility = Visibility.Hidden;
Mouse.OverrideCursor = null; Mouse.OverrideCursor = null;
return; return;
} }
@@ -834,6 +895,9 @@ namespace Elwig.Windows {
UnlockInputs(); UnlockInputs();
GenerateButton.IsEnabled = true; GenerateButton.IsEnabled = true;
GenerateButton.Visibility = Visibility.Visible;
AbortButton.IsEnabled = false;
AbortButton.Visibility = Visibility.Hidden;
Mouse.OverrideCursor = null; Mouse.OverrideCursor = null;
PreviewButton.IsEnabled = true; PreviewButton.IsEnabled = true;
PrintButton.IsEnabled = PrintDocument != null && !hasPreviewDocs; PrintButton.IsEnabled = PrintDocument != null && !hasPreviewDocs;
@@ -981,7 +1045,7 @@ namespace Elwig.Windows {
var pv = ctx.PaymentVariants.Single(v => v.Year == Year && v.Name == name)!; var pv = ctx.PaymentVariants.Single(v => v.Year == Year && v.Name == name)!;
SelectedDocs.Add(new(DocType.CreditNote, s, (pv.Year, pv.AvNr))); SelectedDocs.Add(new(DocType.CreditNote, s, (pv.Year, pv.AvNr)));
SelectedDocumentsList.SelectedIndex = SelectedDocs.Count - 1; SelectedDocumentsList.SelectedIndex = SelectedDocs.Count - 1;
RecipientsDeliveryMembersInput.IsChecked = true; RecipientsCreditMembersInput.IsChecked = true;
} }
private void DocumentInput_TextChanged(object sender, TextChangedEventArgs evt) { private void DocumentInput_TextChanged(object sender, TextChangedEventArgs evt) {

View File

@@ -248,7 +248,7 @@
<Button x:Name="BreakdownButton" <Button x:Name="BreakdownButton"
Click="BreakdownButton_Click" Click="BreakdownButton_Click"
Margin="195,90,0,10" Width="190" Padding="3,5,5,5" Margin="195,90,0,10" Width="190" Padding="3,5,5,5"
ToolTip="Aufschlüsselung der Menge nach Zweigstelle, Mitglied, Sorte, Attribut/Bewirt., Qualitätsstufe, gebunden/ungebunden"> ToolTip="Aufschlüsselung der Menge nach Zweigstelle, Sorte, Attribut/Bewirt., Qualitätsstufe, gebunden/ungebunden">
<Grid> <Grid>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE9F9;" <TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE9F9;"
TextAlignment="Left" HorizontalAlignment="Left" Padding="6.5,0.5,0,0"/> TextAlignment="Left" HorizontalAlignment="Left" Padding="6.5,0.5,0,0"/>

View File

@@ -16,16 +16,14 @@ namespace Elwig.Windows {
public partial class PaymentAdjustmentWindow : ContextWindow { public partial class PaymentAdjustmentWindow : ContextWindow {
public readonly int Year; public readonly int Year;
public readonly bool SeasonLocked; public readonly bool SeasonLocked = false; // never locked
public Dictionary<int, PaymentCustom>? CustomPayments; public Dictionary<int, PaymentCustom>? CustomPayments;
public PaymentAdjustmentWindow(int year) { public PaymentAdjustmentWindow(int year) {
InitializeComponent(); InitializeComponent();
Year = year; Year = year;
using (var ctx = new AppDbContext()) { // using (var ctx = new AppDbContext()) { SeasonLocked = ctx.Seasons.Find(Year + 1) != null; }
SeasonLocked = ctx.Seasons.Find(Year + 1) != null;
}
Title = $"Auszahlung anpassen - Lese {Year} - Elwig"; Title = $"Auszahlung anpassen - Lese {Year} - Elwig";
AutoAdjustBsButton.IsEnabled = !SeasonLocked; AutoAdjustBsButton.IsEnabled = !SeasonLocked;
UnAdjustBsButton.IsEnabled = !SeasonLocked; UnAdjustBsButton.IsEnabled = !SeasonLocked;

View File

@@ -1,12 +1,9 @@
using Elwig.Helpers; using Elwig.Helpers;
using Elwig.Helpers.Billing; using Elwig.Helpers.Billing;
using Elwig.Helpers.Export;
using Elwig.Models.Dtos;
using Elwig.Models.Entities; using Elwig.Models.Entities;
using Elwig.Services; using Elwig.Services;
using Elwig.ViewModels; using Elwig.ViewModels;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Win32;
using System; using System;
using System.Linq; using System.Linq;
using System.Text.Json; using System.Text.Json;
@@ -35,9 +32,7 @@ namespace Elwig.Windows {
CommandBindings.Add(new CommandBinding(CtrlÜ, Menu_EbicsSave_Click)); CommandBindings.Add(new CommandBinding(CtrlÜ, Menu_EbicsSave_Click));
CommandBindings.Add(new CommandBinding(CtrlShiftP, Menu_SummaryPrint_Click)); CommandBindings.Add(new CommandBinding(CtrlShiftP, Menu_SummaryPrint_Click));
Year = year; Year = year;
using (var ctx = new AppDbContext()) { // using (var ctx = new AppDbContext()) { ViewModel.SeasonLocked = ctx.Seasons.Find(Year + 1) != null; }
ViewModel.SeasonLocked = ctx.Seasons.Find(Year + 1) != null;
}
Title = $"Auszahlungsvarianten - Lese {Year} - Elwig"; Title = $"Auszahlungsvarianten - Lese {Year} - Elwig";
if (!App.Config.Debug) { if (!App.Config.Debug) {
DataInput.Visibility = Visibility.Hidden; DataInput.Visibility = Visibility.Hidden;
@@ -55,6 +50,9 @@ namespace Elwig.Windows {
.OrderBy(v => v.AvNr) .OrderBy(v => v.AvNr)
.Include(v => v.Season.Currency) .Include(v => v.Season.Currency)
.ToListAsync()); .ToListAsync());
if (PaymentVariantList.SelectedItem == null && PaymentVariantList.Items.Count > 0) {
PaymentVariantList.SelectedIndex = PaymentVariantList.Items.Count - 1;
}
Update(); Update();
} }
@@ -119,6 +117,11 @@ namespace Elwig.Windows {
private async void DeleteButton_Click(object sender, RoutedEventArgs evt) { private async void DeleteButton_Click(object sender, RoutedEventArgs evt) {
if (PaymentVariantList.SelectedItem is not PaymentVar v || !v.TestVariant) if (PaymentVariantList.SelectedItem is not PaymentVar v || !v.TestVariant)
return; return;
var res = MessageBox.Show(
$"Soll die Auszahlungsvariante \"{v.Name}\" wirklich unwiderruflich gelöscht werden?",
"Auszahlungsvariante löschen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (res != MessageBoxResult.OK)
return;
Mouse.OverrideCursor = Cursors.Wait; Mouse.OverrideCursor = Cursors.Wait;
try { try {
await PaymentVariantService.DeletePaymentVariant(v.Year, v.AvNr); await PaymentVariantService.DeletePaymentVariant(v.Year, v.AvNr);

Binary file not shown.

View File

@@ -13,7 +13,7 @@ About
**Product:** Elwig **Product:** Elwig
**Description:** Electronic Management for Vintners' Cooperatives **Description:** Electronic Management for Vintners' Cooperatives
**Type:** ERP system **Type:** ERP system
**Version:** 1.0.2.0 ([Changelog](./CHANGELOG.md)) **Version:** 1.0.3.4 ([Changelog](./CHANGELOG.md))
**License:** [GNU General Public License 3.0 (GPLv3)](./LICENSE) **License:** [GNU General Public License 3.0 (GPLv3)](./LICENSE)
**Website:** https://elwig.at/ **Website:** https://elwig.at/
**Source code:** https://git.necronda.net/winzer/elwig **Source code:** https://git.necronda.net/winzer/elwig
@@ -33,7 +33,7 @@ Packaging: [WiX Toolset](https://www.firegiant.com/wixtoolset/)
**Produkt:** Elwig **Produkt:** Elwig
**Beschreibung:** Elektronische Winzergenossenschaftsverwaltung **Beschreibung:** Elektronische Winzergenossenschaftsverwaltung
**Typ:** Warenwirtschaftssystem (ERP-System) **Typ:** Warenwirtschaftssystem (ERP-System)
**Version:** 1.0.2.0 ([Änderungsprotokoll](./CHANGELOG.md)) **Version:** 1.0.3.4 ([Änderungsprotokoll](./CHANGELOG.md))
**Lizenz:** [GNU General Public License 3.0 (GPLv3)](./LICENSE) **Lizenz:** [GNU General Public License 3.0 (GPLv3)](./LICENSE)
**Website:** https://elwig.at/ **Website:** https://elwig.at/
**Quellcode:** https://git.necronda.net/winzer/elwig **Quellcode:** https://git.necronda.net/winzer/elwig
@@ -43,5 +43,5 @@ Packaging: [WiX Toolset](https://www.firegiant.com/wixtoolset/)
Programmiersprache: C# Programmiersprache: C#
Framework: Windows Presentation Framework (WPF) Framework: Windows Presentation Framework (WPF)
Datenbank: [SQLite](https://sqlite.org/) Datenbank: [SQLite](https://sqlite.org/)
PDF-Erstellung: [WeasyPrint](https://weasyprint.org/), [RazorLight](https://github.com/toddams/RazorLight), [PdfiumViewer](https://github.com/pvginkel/PdfiumViewer) PDF-Erstellung: [WeasyPrint](https://weasyprint.org/), [RazorLight](https://github.com/toddams/RazorLight), [Pdfium](https://github.com/bblanchon/pdfium-binaries)
Paketierung: [WiX Toolset](https://www.firegiant.com/wixtoolset/) Paketierung: [WiX Toolset](https://www.firegiant.com/wixtoolset/)

View File

@@ -8,18 +8,18 @@
SuppressOptionsUI="yes" ShowVersion="yes"/> SuppressOptionsUI="yes" ShowVersion="yes"/>
</BootstrapperApplication> </BootstrapperApplication>
<util:RegistrySearch Id="VCredistx86" Variable="VCredistx86" Result="exists" <util:RegistrySearch Id="VCredistx64" Variable="VCredistx64" Result="exists"
Root="HKLM" Key="SOFTWARE\Classes\Installer\Dependencies\VC,redist.x86,x86,14.36,bundle"/> Root="HKLM" Key="SOFTWARE\Classes\Installer\Dependencies\VC,redist.x64,amd64,14.50,bundle"/>
<util:RegistrySearch Id="Webview2Machine" Variable="Webview2Machine" Result="exists" <util:RegistrySearch Id="Webview2Machine" Variable="Webview2Machine" Result="exists"
Root="HKLM" Key="SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}"/> Root="HKLM" Key="SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}"/>
<util:RegistrySearch Id="Webview2User" Variable="Webview2User" Result="exists" <util:RegistrySearch Id="Webview2User" Variable="Webview2User" Result="exists"
Root="HKCU" Key="Software\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}"/> Root="HKCU" Key="Software\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}"/>
<Chain> <Chain>
<ExePackage Id="VCredistx86Installer" DisplayName="VC Redist x86 installer" Name="VC_redist.x86.exe" <ExePackage Id="VCredistx64Installer" DisplayName="VC Redist x64 installer" Name="VC_redist.x64.exe"
SourceFile="$(ProjectDir)\Files\VC_redist.x86.exe" SourceFile="$(ProjectDir)\Files\VC_redist.x64.exe"
Cache="remove" Compressed="yes" PerMachine="yes" Permanent="yes" Vital="yes" Cache="remove" Compressed="yes" PerMachine="yes" Permanent="yes" Vital="yes"
InstallArguments="/install /passive /norestart" DetectCondition="VCredistx86"/> InstallArguments="/install /passive /norestart" DetectCondition="VCredistx64"/>
<ExePackage Id="MicrosoftEdgeWebview2" DisplayName="Microsoft Edge Webview2 Runtime" Name="MicrosoftEdgeWebview2Setup.exe" <ExePackage Id="MicrosoftEdgeWebview2" DisplayName="Microsoft Edge Webview2 Runtime" Name="MicrosoftEdgeWebview2Setup.exe"
SourceFile="$(ProjectDir)\Files\MicrosoftEdgeWebview2Setup.exe" SourceFile="$(ProjectDir)\Files\MicrosoftEdgeWebview2Setup.exe"
Cache="remove" Compressed="yes" PerMachine="yes" Permanent ="yes" Vital="no" Cache="remove" Compressed="yes" PerMachine="yes" Permanent ="yes" Vital="no"

View File

@@ -6,7 +6,7 @@
</PropertyGroup> </PropertyGroup>
<Target Name="CustomBeforeBuild" BeforeTargets="BeforeBuild"> <Target Name="CustomBeforeBuild" BeforeTargets="BeforeBuild">
<Exec Command="curl --fail -s -L &quot;https://go.microsoft.com/fwlink/p/?LinkId=2124703&quot; -z &quot;$(ProjectDir)\Files\MicrosoftEdgeWebview2Setup.exe&quot; -o &quot;$(ProjectDir)\Files\MicrosoftEdgeWebview2Setup.exe&quot;" /> <Exec Command="curl --fail -s -L &quot;https://go.microsoft.com/fwlink/p/?LinkId=2124703&quot; -z &quot;$(ProjectDir)\Files\MicrosoftEdgeWebview2Setup.exe&quot; -o &quot;$(ProjectDir)\Files\MicrosoftEdgeWebview2Setup.exe&quot;" />
<Exec Command="curl --fail -s -L &quot;https://aka.ms/vs/17/release/vc_redist.x86.exe&quot; -z &quot;$(ProjectDir)\Files\VC_redist.x86.exe&quot; -o &quot;$(ProjectDir)\Files\VC_redist.x86.exe&quot;" /> <Exec Command="curl --fail -s -L &quot;https://aka.ms/vc14/vc_redist.x64.exe&quot; -z &quot;$(ProjectDir)\Files\VC_redist.x64.exe&quot; -o &quot;$(ProjectDir)\Files\VC_redist.x64.exe&quot;" />
<PropertyGroup> <PropertyGroup>
<DefineConstants>ElwigProjectDir=..\Elwig</DefineConstants> <DefineConstants>ElwigProjectDir=..\Elwig</DefineConstants>
</PropertyGroup> </PropertyGroup>

View File

@@ -22,13 +22,13 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageReference Include="Appium.WebDriver" Version="4.4.5" /> <PackageReference Include="Appium.WebDriver" Version="4.4.5" />
<PackageReference Include="NReco.PdfRenderer" Version="1.6.0" /> <PackageReference Include="NReco.PdfRenderer" Version="1.6.0" />
<PackageReference Include="NUnit" Version="4.4.0" /> <PackageReference Include="NUnit" Version="4.5.0" />
<PackageReference Include="NUnit3TestAdapter" Version="5.2.0" /> <PackageReference Include="NUnit3TestAdapter" Version="6.1.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.11.2"> <PackageReference Include="NUnit.Analyzers" Version="4.11.2">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.4"> <PackageReference Include="coverlet.collector" Version="8.0.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>