Compare commits

..

19 Commits

Author SHA1 Message Date
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
b31603554a Document: Add IsPreview to indicate that a document may only be viewed by internal staff
All checks were successful
Test / Run tests (push) Successful in 2m51s
2026-01-15 13:26:44 +01:00
90def81cc5 PaymentVariantSummary: Fix statistical sum for geb weight
All checks were successful
Test / Run tests (push) Successful in 2m52s
2026-01-14 09:15:27 +01:00
87903227b5 [#66] Services: Add filters for import/export/upload 2026-01-03 20:49:00 +01:00
6d6776f0f9 DeliveryAdminWindow: Complete tooltip in search bar and warning when no season is found 2026-01-03 20:48:39 +01:00
bf3cc2ea1e [#71] Weighing: Fix reconnection behaviour when COM port is connected/disconnected 2026-01-03 20:48:39 +01:00
c1697dc4f3 [#66] MainWindow: Merge both sync buttons into a single one 2026-01-03 20:47:51 +01:00
3b335a568e AbbDbContext: Move SqliteConnection extensions to Extensions class 2026-01-03 20:47:45 +01:00
beacdab54f [#66] Services: Add SyncService 2026-01-03 20:47:14 +01:00
30 changed files with 298 additions and 96 deletions

View File

@@ -2,6 +2,59 @@
Changelog
=========
[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}
---------------------------------------------

View File

@@ -19,6 +19,16 @@ namespace Elwig.Documents {
Member = m;
Location = App.BranchLocation;
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>");
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>" +

View File

@@ -39,6 +39,7 @@ namespace Elwig.Documents {
Data = data;
Payment = p;
Credit = p.Credit;
IsPreview = Payment == null || Credit == null;
var season = p.Variant.Season;
if (considerCustomModifiers) {
CustomPayment = ctx.CustomPayments.Find(p.Year, p.MgNr);

View File

@@ -2,7 +2,7 @@
h1 {
text-align: center;
font-size: 24pt;
margin-top: 10mm;
margin-top: 0;
margin-bottom: 2mm;
}

View File

@@ -2,7 +2,7 @@
h1 {
text-align: center;
font-size: 24pt;
margin-top: 10mm;
margin-top: 0;
margin-bottom: 2mm;
}

View File

@@ -2,7 +2,7 @@
h1 {
text-align: center;
font-size: 24pt;
margin-top: 10mm;
margin-top: 0;
margin-bottom: 2mm;
}

View File

@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Linq;
using Elwig.Helpers.Printing;
using MimeKit;
using System.Threading;
namespace Elwig.Documents {
public abstract partial class Document : IDisposable {
@@ -18,10 +19,11 @@ namespace Elwig.Documents {
protected string? _pdfPath;
protected string? PdfPath => _pdfPath ?? _pdfFile?.FilePath;
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 DoublePaged = false;
public bool IsDoublePaged = false;
public bool IsPreview = false;
public string DocumentsPath;
public int CurrentNextSeason;
@@ -38,15 +40,8 @@ namespace Elwig.Documents {
CurrentNextSeason = Utils.CurrentNextSeason;
Title = title;
Author = c.NameFull;
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();
Header = "";
Footer = Utils.GenerateFooter("<br/>", " \u00b7 ").Item(c.NameFull).ToString();
Date = DateOnly.FromDateTime(Utils.Today);
}
@@ -104,7 +99,7 @@ namespace Elwig.Documents {
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)
return;
progress?.Report(0.0);
@@ -114,6 +109,7 @@ namespace Elwig.Documents {
var pdf = new TempFile("pdf");
var tmpHtmls = new List<TempFile>();
var tmpFiles = new List<string>();
try {
var n = m.Documents.Count();
int i = 0;
foreach (var doc in m.Documents) {
@@ -121,6 +117,8 @@ namespace Elwig.Documents {
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);
@@ -129,21 +127,32 @@ namespace Elwig.Documents {
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))));
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();
}
_pdfFile = pdf;
}
} else {
if (cancelToken?.IsCancellationRequested ?? false)
throw new OperationCanceledException("Dokumentenerzeugung abgebrochen!");
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);
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;
}
_pdfFile = pdf;
} catch {
pdf.Dispose();
throw;
}
}
progress?.Report(100.0);
}
@@ -155,7 +164,7 @@ namespace Elwig.Documents {
public async Task Print(int copies = 1) {
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() {

View File

@@ -10,7 +10,7 @@
<link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\Document.css" />
<link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\Document.Page.css" />
<link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\Document.Table.css" />
@if (Model.DoublePaged) {
@if (Model.IsDoublePaged) {
<style>
@@page :left {
margin: 25mm 25mm 35mm 20mm;
@@ -34,14 +34,14 @@
<div class="pre-footer">
<span class="date">@($"{Model.Date:dddd, d. MMMM yyyy}")</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>
<footer>@Raw(Model.Footer)</footer>
</div>
@if (Model.DoublePaged) {
@if (Model.IsDoublePaged) {
<div class="footer-wrapper left">
<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="date">@($"{Model.Date:dddd, d. MMMM yyyy}")</span>
</div>

View File

@@ -2,7 +2,7 @@
h1 {
text-align: center;
font-size: 24pt;
margin-top: 10mm;
margin-top: 0;
margin-bottom: 2mm;
}

View File

@@ -27,6 +27,7 @@ namespace Elwig.Documents {
Data = data;
CurrencySymbol = v.Season.Currency.Symbol ?? v.Season.Currency.Code;
MemberNum = v.Credits.Count;
IsPreview = MemberNum == 0;
DeliveryNum = v.DeliveryPartPayments.DistinctBy(p => p.DeliveryPart.Delivery).Count();
DeliveryPartNum = v.DeliveryPartPayments.Count;
ModifierStat = AppDbContext.GetModifierStats(v.Year, v.AvNr).GetAwaiter().GetResult();

View File

@@ -164,14 +164,14 @@
<td class="number">@Utils.GetSign(considered)</td>
<td class="number"><span class="fleft">@Model.CurrencySymbol</span>@($"{Math.Abs(considered):N2}")</td>
<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>
<th colspan="2">Auszahlungsbetrag:</th>
<td class="number tborder"></td>
<td class="number tborder"><span class="fleft">@Model.CurrencySymbol</span>@($"{totalSum:N2}")</td>
<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>
</tbody>
</table>

View File

@@ -2,7 +2,7 @@
h1 {
text-align: center;
font-size: 24pt;
margin-top: 10mm;
margin-top: 0;
margin-bottom: 2mm;
}

View File

@@ -2,7 +2,7 @@
h1 {
text-align: center;
font-size: 24pt;
margin-top: 10mm;
margin-top: 0;
margin-bottom: 2mm;
}

View File

@@ -7,7 +7,7 @@
<UseWPF>true</UseWPF>
<PreserveCompilationContext>true</PreserveCompilationContext>
<ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon>
<Version>1.0.2.0</Version>
<Version>1.0.3.1</Version>
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<ApplicationManifest>app.manifest</ApplicationManifest>
@@ -25,20 +25,20 @@
<PackageReference Include="LinqKit" Version="1.3.9" />
<PackageReference Include="MailKit" Version="4.14.1" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.36" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="10.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="10.0.0" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.3595.46" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="10.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="10.0.2" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.3650.58" />
<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="ScottPlot.WPF" Version="5.1.57" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="3.0.2" />
<PackageReference Include="System.IO.Hashing" Version="10.0.0" />
<PackageReference Include="System.IO.Ports" Version="10.0.0" />
<PackageReference Include="System.Management" Version="10.0.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="10.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>
</Project>

View File

@@ -54,14 +54,14 @@ namespace Elwig.Helpers.Billing {
m.mgnr,
v.avnr,
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,
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.ConsiderAutoBusinessShares}, -COALESCE(a.total_amount, 0), 0) / POW(10, s.precision - 2)) +
IIF({Data.ConsiderCustomModifiers}, COALESCE(x.amount, 0), 0)
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
JOIN payment_variant v ON v.year = s.year
LEFT JOIN payment_variant l ON l.year = s.year

View File

@@ -206,15 +206,15 @@ namespace Elwig.Helpers {
case 1: orderingMemberList = "NAME"; break;
case 2: orderingMemberList = "KG"; break;
}
string mailSendPostal = "MGNR";
switch (MailOrdering) {
string mailSendPostal = "WISH";
switch (MailSendPostal) {
case 0: mailSendPostal = "NONE"; break;
case 1: mailSendPostal = "NO_EMAIL"; break;
case 2: mailSendPostal = "WISH"; break;
case 3: mailSendPostal = "ALL"; break;
}
string mailSendEmail = "MGNR";
switch (MailOrdering) {
string mailSendEmail = "WISH";
switch (MailSendEmail) {
case 0: mailSendEmail = "NONE"; break;
case 1: mailSendEmail = "WISH"; break;
case 2: mailSendEmail = "ALL"; break;

View File

@@ -1,20 +1,24 @@
using System.Threading.Tasks;
using Elwig.Windows;
using System.Diagnostics;
using PdfiumViewer;
using System;
using System.IO;
using System.Collections.Generic;
using System.Windows;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.Drawing.Printing;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using PdfiumViewer;
using System.Drawing.Printing;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
namespace Elwig.Helpers.Printing {
public static class Pdf {
private static readonly string WinziPrint = new string[] { App.InstallPath }
private static readonly string WinziPrint = (Assembly.GetEntryAssembly()?.Location.Contains(@"\bin\") ?? false) ?
Path.Combine(Assembly.GetEntryAssembly()!.Location.Split(@"\bin\")[0], "../Installer/Files/WinziPrint.exe") :
new string[] { App.InstallPath }
.Union(Environment.GetEnvironmentVariable("PATH")?.Split(';') ?? [])
.Select(x => Path.Combine(x, "WinziPrint.exe"))
.Where(File.Exists)
@@ -46,24 +50,45 @@ namespace Elwig.Helpers.Printing {
return Task.CompletedTask;
}
public static async Task<(int Pages, IEnumerable<int> PerDoc)> Convert(string htmlPath, string pdfPath, bool doublePaged = false, IProgress<double>? progress = null) {
return await Convert([htmlPath], pdfPath, doublePaged, progress);
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, 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");
progress?.Report(0.0);
using var client = new TcpClient("127.0.0.1", 30983);
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(
"-e utf-8;-p;" + (doublePaged ? "-2;" : "") +
$"{string.Join(';', htmlPath)};{pdfPath}" +
"\r\n"));
using var reader = new StreamReader(stream);
bool cancelled = false;
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");
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:")) {
var parts = line[9..].Trim().Split('/').Select(int.Parse).ToArray();
progress?.Report(100.0 * parts[0] / parts[1]);

View File

@@ -540,15 +540,29 @@ namespace Elwig.Helpers {
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 (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.Print();
} 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();
var success = await SendEmail(e.Member, e.Subject, e.Text, [doc]);
if (success)
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);
} 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() {
FileName = $"{NormalizeFileName(filename ?? doc.Title)}.pdf",
DefaultExt = "pdf",

View File

@@ -218,6 +218,22 @@ namespace Elwig.Services {
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())) {
filterVar.Add(e.ToUpper());
filter.RemoveAt(i--);

View File

@@ -313,6 +313,22 @@ namespace Elwig.Services {
memberQuery = memberQuery.Where(m => !m.ContactViaPost);
filter.RemoveAt(i--);
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)) {
filterMgNr.Add(int.Parse(e));
filter.RemoveAt(i--);

View File

@@ -226,6 +226,7 @@
<Bold>Uhrzeit</Bold>: z.B. 06:00-08:00, 18:00-, ...<LineBreak/>
<Bold>Handwiegung</Bold>: handw[iegung], !Handw[iegung] (alle ohne Handwiegung)<LineBreak/>
<Bold>Handlese</Bold>: Handl[ese], !handl[ese] (alle ohne Handlese)<LineBreak/>
<Bold>Anlieferung</Bold>: Plane[nwagen]/Kipp[er], !plane[nwagen]/!kipp[er], Lesew[agen], !lesew[agen], kiste[n], !kiste[n]<LineBreak/>
<Bold>Gebunden</Bold>: geb[unden], ungeb[unden], !geb[unden], !ungeb[unden]<LineBreak/>
<Bold>Gerebelt</Bold>: gerebelt, !Gerebelt (nicht gerebelt gewogen)<LineBreak/>
<Bold>Freitext</Bold>: z.B. Lieferscheinnummern, Anmerkung, "quw" (sucht nach dem Text "quw")

View File

@@ -135,7 +135,7 @@ namespace Elwig.Windows {
NewDeliveryButton_Click(null, null);
using var ctx = new AppDbContext();
if (ctx.Seasons.Find(Utils.CurrentYear) == null) {
MessageBox.Show("Die Saison für das aktuelle Jahr wurde noch nicht erstellt. Neue Lieferungen können nicht abgespeichert werden.",
MessageBox.Show("Die Saison für das aktuelle Jahr wurde noch nicht erstellt. Neue Lieferungen können nicht abgespeichert werden.\n\n(Stammdaten -> Saisons -> Neu anlegen...)",
"Saison noch nicht erstellt", MessageBoxButton.OK, MessageBoxImage.Warning);
}
}

View File

@@ -302,6 +302,9 @@
<Button x:Name="GenerateButton" Content="Generieren"
Grid.Row="0" Grid.Column="0" FontSize="14"
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"
Grid.Row="2" Grid.Column="0" SnapsToDevicePixels="True"/>

View File

@@ -13,6 +13,7 @@ using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
@@ -61,6 +62,8 @@ namespace Elwig.Windows {
protected Dictionary<Member, List<Document>>? PrintMemberDocuments;
protected Dictionary<Member, List<Document>>? EmailDocuments;
private CancellationTokenSource? CancelGeneration;
public static readonly DependencyProperty PostalAllCountProperty = DependencyProperty.Register(nameof(PostalAllCount), typeof(int), typeof(MailWindow));
public int PostalAllCount {
get => (int)GetValue(PostalAllCountProperty);
@@ -594,20 +597,32 @@ namespace Elwig.Windows {
}
private void Window_Closed(object sender, EventArgs evt) {
CancelGeneration?.Dispose();
DisposeDocs();
}
private async void AbortButton_Click(object sender, RoutedEventArgs evt) {
AbortButton.IsEnabled = false;
CancelGeneration?.Cancel();
}
private async void GenerateButton_Click(object sender, RoutedEventArgs evt) {
LockInputs();
PreviewButton.IsEnabled = false;
PrintButton.IsEnabled = false;
EmailButton.IsEnabled = false;
Mouse.OverrideCursor = Cursors.Wait;
AbortButton.IsEnabled = true;
AbortButton.Visibility = Visibility.Visible;
GenerateButton.IsEnabled = false;
GenerateButton.Visibility = Visibility.Hidden;
DisposeDocs();
await UpdateClientParameters();
CancelGeneration?.Dispose();
CancelGeneration = new();
using var ctx = new AppDbContext();
var doublePaged = DoublePagedInput.IsChecked == true;
@@ -662,6 +677,9 @@ namespace Elwig.Windows {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
UnlockInputs();
GenerateButton.IsEnabled = true;
GenerateButton.Visibility = Visibility.Visible;
AbortButton.IsEnabled = false;
AbortButton.Visibility = Visibility.Hidden;
Mouse.OverrideCursor = null;
return;
}
@@ -679,6 +697,9 @@ namespace Elwig.Windows {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
UnlockInputs();
GenerateButton.IsEnabled = true;
GenerateButton.Visibility = Visibility.Visible;
AbortButton.IsEnabled = false;
AbortButton.Visibility = Visibility.Hidden;
Mouse.OverrideCursor = null;
return;
}
@@ -733,6 +754,21 @@ namespace Elwig.Windows {
}).ToList()
}).ToList();
var hasPreviewDocs = memberDocs.Any(m => m.Docs.Any(d => d.Doc.IsPreview));
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?",
"Vorläufige Dokumente", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (res != MessageBoxResult.OK) {
UnlockInputs();
GenerateButton.IsEnabled = true;
GenerateButton.Visibility = Visibility.Visible;
AbortButton.IsEnabled = false;
AbortButton.Visibility = Visibility.Hidden;
Mouse.OverrideCursor = null;
return;
}
}
var printMode = PostalAllInput.IsChecked == true ? 3 :
PostalWishInput.IsChecked == true ? 2 :
PostalNoEmailInput.IsChecked == true ? 1 : 0;
@@ -747,7 +783,7 @@ namespace Elwig.Windows {
.ToDictionary(d => d.Member, m => {
var docs = m.Docs.Select(d => d.Doc).ToList();
foreach (var doc in docs) {
doc!.DoublePaged = false;
doc!.IsDoublePaged = false;
if (doc is BusinessDocument b) {
b.IncludeSender = false;
b.Location = location;
@@ -759,7 +795,7 @@ namespace Elwig.Windows {
try {
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 })) {
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;
}));
}
@@ -768,6 +804,9 @@ namespace Elwig.Windows {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
UnlockInputs();
GenerateButton.IsEnabled = true;
GenerateButton.Visibility = Visibility.Visible;
AbortButton.IsEnabled = false;
AbortButton.Visibility = Visibility.Hidden;
Mouse.OverrideCursor = null;
return;
}
@@ -788,7 +827,7 @@ namespace Elwig.Windows {
docs.Insert(0, new Letterhead(m.Member));
}
docs.ForEach(doc => {
doc.DoublePaged = doublePaged;
doc.IsDoublePaged = doublePaged;
if (doc is BusinessDocument b)
b.Location = location;
});
@@ -801,8 +840,8 @@ namespace Elwig.Windows {
if (printDocs.Count > 0) {
try {
var print = Document.Merge(printDocs);
print.DoublePaged = doublePaged;
await print.Generate(new Progress<double>(v => {
print.IsDoublePaged = doublePaged;
await print.Generate(CancelGeneration.Token, new Progress<double>(v => {
ProgressBar.Value = 100.0 * emailNum / totalNum + v * printNum / totalNum;
}));
PrintDocument = print;
@@ -811,6 +850,9 @@ namespace Elwig.Windows {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
UnlockInputs();
GenerateButton.IsEnabled = true;
GenerateButton.Visibility = Visibility.Visible;
AbortButton.IsEnabled = false;
AbortButton.Visibility = Visibility.Hidden;
Mouse.OverrideCursor = null;
return;
}
@@ -822,10 +864,13 @@ namespace Elwig.Windows {
UnlockInputs();
GenerateButton.IsEnabled = true;
GenerateButton.Visibility = Visibility.Visible;
AbortButton.IsEnabled = false;
AbortButton.Visibility = Visibility.Hidden;
Mouse.OverrideCursor = null;
PreviewButton.IsEnabled = true;
PrintButton.IsEnabled = PrintDocument != null;
EmailButton.IsEnabled = EmailDocuments != null && App.Config.Smtp != null;
PrintButton.IsEnabled = PrintDocument != null && !hasPreviewDocs;
EmailButton.IsEnabled = EmailDocuments != null && !hasPreviewDocs && App.Config.Smtp != null;
}
private async void PreviewButton_Click(object sender, RoutedEventArgs evt) {

View File

@@ -55,6 +55,9 @@ namespace Elwig.Windows {
.OrderBy(v => v.AvNr)
.Include(v => v.Season.Currency)
.ToListAsync());
if (PaymentVariantList.SelectedItem == null && PaymentVariantList.Items.Count > 0) {
PaymentVariantList.SelectedIndex = PaymentVariantList.Items.Count - 1;
}
Update();
}
@@ -119,6 +122,11 @@ namespace Elwig.Windows {
private async void DeleteButton_Click(object sender, RoutedEventArgs evt) {
if (PaymentVariantList.SelectedItem is not PaymentVar v || !v.TestVariant)
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;
try {
await PaymentVariantService.DeletePaymentVariant(v.Year, v.AvNr);

Binary file not shown.

View File

@@ -13,7 +13,7 @@ About
**Product:** Elwig
**Description:** Electronic Management for Vintners' Cooperatives
**Type:** ERP system
**Version:** 1.0.2.0 ([Changelog](./CHANGELOG.md))
**Version:** 1.0.3.1 ([Changelog](./CHANGELOG.md))
**License:** [GNU General Public License 3.0 (GPLv3)](./LICENSE)
**Website:** https://elwig.at/
**Source code:** https://git.necronda.net/winzer/elwig
@@ -33,7 +33,7 @@ Packaging: [WiX Toolset](https://www.firegiant.com/wixtoolset/)
**Produkt:** Elwig
**Beschreibung:** Elektronische Winzergenossenschaftsverwaltung
**Typ:** Warenwirtschaftssystem (ERP-System)
**Version:** 1.0.2.0 ([Änderungsprotokoll](./CHANGELOG.md))
**Version:** 1.0.3.1 ([Änderungsprotokoll](./CHANGELOG.md))
**Lizenz:** [GNU General Public License 3.0 (GPLv3)](./LICENSE)
**Website:** https://elwig.at/
**Quellcode:** https://git.necronda.net/winzer/elwig

View File

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

View File

@@ -6,7 +6,7 @@
</PropertyGroup>
<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://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>
<DefineConstants>ElwigProjectDir=..\Elwig</DefineConstants>
</PropertyGroup>

View File

@@ -23,7 +23,7 @@
<PackageReference Include="Appium.WebDriver" Version="4.4.5" />
<PackageReference Include="NReco.PdfRenderer" Version="1.6.0" />
<PackageReference Include="NUnit" Version="4.4.0" />
<PackageReference Include="NUnit3TestAdapter" Version="5.2.0" />
<PackageReference Include="NUnit3TestAdapter" Version="6.1.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.11.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>