Compare commits
11 Commits
a6e04ab1d2
...
65544f6f29
| Author | SHA1 | Date | |
|---|---|---|---|
| 65544f6f29 | |||
| b58dee6d3f | |||
| 0e2b004b0d | |||
| 19c3322ef2 | |||
| 6f081811c4 | |||
| 432c511b85 | |||
| 7e22759c33 | |||
| a47904cf0b | |||
| 6818491ae3 | |||
| 23db4de1ee | |||
| 9e5f709d42 |
29
CHANGELOG.md
29
CHANGELOG.md
@@ -2,6 +2,33 @@
|
||||
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}
|
||||
---------------------------------------------
|
||||
|
||||
@@ -303,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)
|
||||
* In den Variantendaten einer Auszahlungsvariante (`PaymentVariantSummary`) wurde neben den Spalten _gebunden_ und _ungebunden_ noch _attributlos gebunden_ hinzugefügt. Ohne diese neue Spalte wären die Werte der anderen beiden falsch. ([#58][i58])
|
||||
* Das erste Laden des Ausgangs-Protokoll-Fensters (`MailLogWindow`) hat nicht funktioniert. ([#65][i65])
|
||||
* Im Lieferungen-Fenster (`DeliveryAdminWindow`) und im Mitglieder-Fenster (`MemberAdminWindow`) wird der Tool-Tip für Gewicht/Gradation mit korrektem Layout angezeigt. (e9f389b885)
|
||||
* 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])
|
||||
|
||||
### Sonstiges {#v1.0.0.0-misc}
|
||||
|
||||
@@ -21,14 +21,6 @@ namespace Elwig.Documents {
|
||||
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>" +
|
||||
|
||||
@@ -100,6 +100,12 @@
|
||||
<td></td>
|
||||
<td></td>
|
||||
</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>
|
||||
</table>
|
||||
@Raw(BusinessDocument.PrintSortenaufteilung(Model.MemberStats))
|
||||
|
||||
@@ -1,19 +1,4 @@
|
||||
|
||||
.m1, .m2, .m3 {
|
||||
height: 0;
|
||||
width: 10mm;
|
||||
position: fixed;
|
||||
left: -25mm;
|
||||
border-top: var(--border-thickness) solid black;
|
||||
}
|
||||
.m1.r, .m2.r, .m3.r {
|
||||
left: initial;
|
||||
right: -20mm;
|
||||
}
|
||||
.m1 {top: 80mm;}
|
||||
.m2 {top: 123.5mm;}
|
||||
.m3 {top: 185mm;}
|
||||
|
||||
.page-break {
|
||||
break-before: page;
|
||||
}
|
||||
@@ -23,15 +8,15 @@ hr.page-break {
|
||||
|
||||
@page {
|
||||
size: A4;
|
||||
margin: 25mm 20mm 35mm 25mm;
|
||||
margin: 25mm 20mm 25mm 25mm;
|
||||
}
|
||||
|
||||
@bottom-center {
|
||||
content: element(page-footer);
|
||||
}
|
||||
@page :first {
|
||||
margin: 25mm 20mm 35mm 25mm;
|
||||
}
|
||||
|
||||
@media screen {
|
||||
body, header, .footer-wrapper {
|
||||
body, header {
|
||||
width: 210mm;
|
||||
}
|
||||
|
||||
@@ -39,10 +24,6 @@ hr.page-break {
|
||||
border: 1px solid lightgray;
|
||||
}
|
||||
|
||||
.m1, .m2, .m3 {
|
||||
display: none;
|
||||
}
|
||||
|
||||
header {
|
||||
top: 0;
|
||||
}
|
||||
@@ -54,23 +35,9 @@ hr.page-break {
|
||||
.main-wrapper {
|
||||
margin: 0 20mm 40mm 25mm;
|
||||
}
|
||||
|
||||
.footer-wrapper {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: white;
|
||||
}
|
||||
}
|
||||
|
||||
@media print {
|
||||
.page::after {
|
||||
content: "Seite " counter(page) " von " counter(pages) !important;
|
||||
}
|
||||
|
||||
a {
|
||||
a {
|
||||
text-decoration: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
|
||||
main table {
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 10mm;
|
||||
margin-bottom: 0mm;
|
||||
font-size: 10pt;
|
||||
}
|
||||
main table.large {font-size: 12pt;}
|
||||
main table.tiny {
|
||||
font-size: 8pt;
|
||||
margin-bottom: 5mm;
|
||||
margin-bottom: 0mm;
|
||||
}
|
||||
|
||||
main table.border {
|
||||
@@ -30,7 +30,7 @@ main table.small th,
|
||||
main table.small td,
|
||||
main table.tiny th,
|
||||
main table.tiny td {
|
||||
padding: 0.125mm 0.125mm;
|
||||
padding: 0mm 0.125mm;
|
||||
}
|
||||
|
||||
main table td[rowspan] {
|
||||
|
||||
@@ -31,7 +31,6 @@ namespace Elwig.Documents {
|
||||
public string Title;
|
||||
public string Author;
|
||||
public string Header;
|
||||
public string Footer;
|
||||
public DateOnly Date;
|
||||
|
||||
public Document(string title) {
|
||||
@@ -41,7 +40,6 @@ namespace Elwig.Documents {
|
||||
Title = title;
|
||||
Author = c.NameFull;
|
||||
Header = "";
|
||||
Footer = Utils.GenerateFooter("<br/>", " \u00b7 ").Item(c.NameFull).ToString();
|
||||
Date = DateOnly.FromDateTime(Utils.Today);
|
||||
}
|
||||
|
||||
@@ -122,12 +120,12 @@ namespace Elwig.Documents {
|
||||
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);
|
||||
tmpFiles.Add((doc is Letterhead ? "#" : "") + tmpHtml.FilePath);
|
||||
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))));
|
||||
var pages = Pdf.Convert(tmpFiles, pdf.FilePath, IsDoublePaged, m.Documents, cancelToken, new Progress<double>(v => progress?.Report(GenerationProportion * 100 + v * (1 - GenerationProportion))));
|
||||
TotalPages = pages.Pages;
|
||||
_pdfFile = pdf;
|
||||
} catch {
|
||||
@@ -146,7 +144,7 @@ namespace Elwig.Documents {
|
||||
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, IsDoublePaged, cancelToken);
|
||||
var pages = Pdf.Convert(tmpHtml.FilePath, pdf.FilePath, this);
|
||||
TotalPages = pages.Pages;
|
||||
_pdfFile = pdf;
|
||||
} catch {
|
||||
|
||||
@@ -13,41 +13,12 @@
|
||||
@if (Model.IsDoublePaged) {
|
||||
<style>
|
||||
@@page :left {
|
||||
margin: 25mm 25mm 35mm 20mm;
|
||||
@@bottom-center {
|
||||
content: element(page-footer-left);
|
||||
}
|
||||
margin: 25mm 25mm 25mm 20mm;
|
||||
}
|
||||
</style>
|
||||
}
|
||||
</head>
|
||||
<body>
|
||||
@if (Model.ShowFoldMarks) {
|
||||
<div class="m1"></div>
|
||||
<div class="m2"></div>
|
||||
<div class="m3"></div>
|
||||
<div class="m1 r"></div>
|
||||
<div class="m2 r"></div>
|
||||
<div class="m3 r"></div>
|
||||
}
|
||||
<div class="footer-wrapper">
|
||||
<div class="pre-footer">
|
||||
<span class="date">@($"{Model.Date:dddd, d. MMMM yyyy}")</span>
|
||||
<span class="doc-id">@Model.DocumentId</span>
|
||||
<span><span class="page"></span>@Raw(Model.IsPreview ? " <b>(vorläufig)</b>" : "")</span>
|
||||
</div>
|
||||
<footer>@Raw(Model.Footer)</footer>
|
||||
</div>
|
||||
@if (Model.IsDoublePaged) {
|
||||
<div class="footer-wrapper left">
|
||||
<div class="pre-footer">
|
||||
<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>
|
||||
<footer>@Raw(Model.Footer)</footer>
|
||||
</div>
|
||||
}
|
||||
<header>@Raw(Model.Header)</header>
|
||||
<div class="spacing"></div>
|
||||
<div class="main-wrapper">
|
||||
|
||||
@@ -55,53 +55,6 @@ header .type {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.footer-wrapper {
|
||||
position: running(page-footer);
|
||||
width: 165mm;
|
||||
/* for some reason the position without the following statement changes on the second page */
|
||||
border: var(--border-thickness) solid #00000000;
|
||||
}
|
||||
|
||||
.footer-wrapper.left {
|
||||
position: running(page-footer-left);
|
||||
}
|
||||
|
||||
.pre-footer {
|
||||
margin: 1em 0;
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
.pre-footer > * {
|
||||
display: inline-block;
|
||||
width: 33%;
|
||||
}
|
||||
|
||||
.pre-footer > *:first-child {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.pre-footer > *:nth-child(2) {
|
||||
text-align: center;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.pre-footer > *:last-child {
|
||||
text-align: right;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.pre-footer .page::after {
|
||||
content: "Seite 1 von 1";
|
||||
}
|
||||
|
||||
footer {
|
||||
font-size: 10pt;
|
||||
border-top: var(--border-thickness) solid black;
|
||||
height: 25mm;
|
||||
padding-top: 1mm;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
@@ -166,7 +166,6 @@
|
||||
}
|
||||
|
||||
@if (areaComs.Count != 0) {
|
||||
<br class="area-commitements"/>
|
||||
<h2>Flächenbindungen per @($"{Model.Date:dd.MM.yyyy}")</h2>
|
||||
<table class="area-commitements">
|
||||
<colgroup>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
h2 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5em !important;
|
||||
}
|
||||
|
||||
@@ -22,9 +23,3 @@ table.area-commitements td.text {
|
||||
table.area-commitements tr.sum {
|
||||
font-size: 12pt;
|
||||
}
|
||||
|
||||
@page :not(:first) {
|
||||
br.area-commitements {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||
<ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon>
|
||||
<Version>1.0.3.3</Version>
|
||||
<Version>1.0.3.4</Version>
|
||||
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
@@ -25,20 +25,23 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="bblanchon.PDFium.Win32" Version="147.0.7690" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||
<PackageReference Include="LinqKit" Version="1.3.9" />
|
||||
<PackageReference Include="MailKit" Version="4.14.1" />
|
||||
<PackageReference Include="itext" Version="9.5.0" />
|
||||
<PackageReference Include="itext.bouncy-castle-adapter" Version="9.5.0" />
|
||||
<PackageReference Include="itext.pdfhtml" Version="6.3.1" />
|
||||
<PackageReference Include="LinqKit" Version="1.3.11" />
|
||||
<PackageReference Include="MailKit" Version="4.15.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.36" />
|
||||
<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="Microsoft.EntityFrameworkCore.Proxies" Version="10.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="10.0.3" />
|
||||
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.3800.47" />
|
||||
<PackageReference Include="NJsonSchema" Version="11.5.2" />
|
||||
<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.2" />
|
||||
<PackageReference Include="System.IO.Ports" Version="10.0.2" />
|
||||
<PackageReference Include="System.Management" Version="10.0.2" />
|
||||
<PackageReference Include="System.IO.Hashing" Version="10.0.3" />
|
||||
<PackageReference Include="System.IO.Ports" Version="10.0.3" />
|
||||
<PackageReference Include="System.Management" Version="10.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Elwig.Helpers {
|
||||
public static class AppDbUpdater {
|
||||
|
||||
// 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;
|
||||
|
||||
|
||||
@@ -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_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)
|
||||
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($"""
|
||||
UPDATE payment_variant SET test_variant = FALSE WHERE (year, avnr) = ({Year}, {AvNr});
|
||||
|
||||
@@ -783,15 +783,15 @@ namespace Elwig.Helpers.Export {
|
||||
Comment = json["comment"]?.AsValue().GetValue<string>(),
|
||||
ImportedAt = DateTime.Now,
|
||||
}, 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>();
|
||||
WbRd? rd = null;
|
||||
if (ried != null) {
|
||||
var rde = riede.GetValueOrDefault(kgnr, []);
|
||||
if (ried != null && kgnr != null) {
|
||||
var rde = riede.GetValueOrDefault(kgnr.Value, []);
|
||||
rd = rde.FirstOrDefault(r => r.Name == ried);
|
||||
if (rd == null) {
|
||||
rd = new WbRd {
|
||||
KgNr = kgnr,
|
||||
KgNr = kgnr.Value,
|
||||
RdNr = (rde.Count == 0 ? 1 : rde.Max(r => r.RdNr)) + 1,
|
||||
Name = ried,
|
||||
};
|
||||
|
||||
176
Elwig/Helpers/Printing/FooterEventHandler.cs
Normal file
176
Elwig/Helpers/Printing/FooterEventHandler.cs
Normal file
@@ -0,0 +1,176 @@
|
||||
using Elwig.Documents;
|
||||
using iText.IO.Font.Constants;
|
||||
using iText.Kernel.Font;
|
||||
using iText.Kernel.Geom;
|
||||
using iText.Kernel.Pdf;
|
||||
using iText.Kernel.Pdf.Action;
|
||||
using iText.Kernel.Pdf.Canvas;
|
||||
using iText.Kernel.Pdf.Event;
|
||||
using iText.Kernel.Pdf.Xobject;
|
||||
using iText.Layout;
|
||||
using iText.Layout.Borders;
|
||||
using iText.Layout.Element;
|
||||
using iText.Layout.Properties;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Elwig.Helpers.Printing {
|
||||
public class FooterEventHandler : AbstractPdfDocumentEventHandler {
|
||||
|
||||
private const float _fontSize = 10;
|
||||
private const float _ptInMm = 2.8346456693f;
|
||||
private const float _placeholderWidth = 50 * _ptInMm;
|
||||
|
||||
private readonly string _date;
|
||||
private readonly string? _center;
|
||||
private readonly bool _doublePaged;
|
||||
private readonly bool _isPreview;
|
||||
private readonly bool _isBusiness;
|
||||
private readonly bool _showFoldMarks;
|
||||
private readonly PdfFont _font;
|
||||
private readonly PdfFont _fontBold;
|
||||
private readonly PdfFont _fontItalic;
|
||||
|
||||
private readonly List<PdfFormXObject> _pageNumPlaceholders;
|
||||
|
||||
public int NumberOfPages { get; private set; }
|
||||
|
||||
public FooterEventHandler(Documents.Document? doc = null) {
|
||||
_date = $"{doc?.Date ?? DateOnly.FromDateTime(Utils.Today):dddd, d. MMMM yyyy}";
|
||||
_center = doc?.DocumentId;
|
||||
_doublePaged = doc?.IsDoublePaged ?? false;
|
||||
_isPreview = doc?.IsPreview ?? false;
|
||||
_isBusiness = doc is BusinessDocument;
|
||||
_showFoldMarks = doc?.ShowFoldMarks ?? false;
|
||||
_font = PdfFontFactory.CreateFont(StandardFonts.TIMES_ROMAN);
|
||||
_fontBold = PdfFontFactory.CreateFont(StandardFonts.TIMES_BOLD);
|
||||
_fontItalic = PdfFontFactory.CreateFont(StandardFonts.TIMES_ITALIC);
|
||||
_pageNumPlaceholders = [];
|
||||
}
|
||||
|
||||
protected override void OnAcceptedEvent(AbstractPdfDocumentEvent evt) {
|
||||
if (evt.GetType() == PdfDocumentEvent.END_PAGE) {
|
||||
OnPageEnd((PdfDocumentEvent)evt);
|
||||
} else if (evt.GetType() == PdfDocumentEvent.START_DOCUMENT_CLOSING) {
|
||||
OnDocumentClose((PdfDocumentEvent)evt);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPageEnd(PdfDocumentEvent evt) {
|
||||
var pdf = evt.GetDocument();
|
||||
var page = evt.GetPage();
|
||||
var pageNum = pdf.GetPageNumber(page);
|
||||
|
||||
var pageSize = page.GetPageSize();
|
||||
float leftX1 = pageSize.GetLeft() + 25 * _ptInMm;
|
||||
float leftX2 = pageSize.GetLeft() + 20 * _ptInMm;
|
||||
float rightX1 = pageSize.GetRight() - 20 * _ptInMm;
|
||||
float rightX2 = pageSize.GetRight() - 25 * _ptInMm;
|
||||
float footerY = pageSize.GetBottom() + 25 * _ptInMm;
|
||||
float y = footerY + _fontSize;
|
||||
float footerWidth = 165 * _ptInMm;
|
||||
float footerHeight = 25 * _ptInMm;
|
||||
|
||||
var pdfCanvas = new PdfCanvas(page.NewContentStreamAfter(), page.GetResources(), pdf);
|
||||
using var canvas = new Canvas(pdfCanvas, pageSize);
|
||||
|
||||
var placeholder = new PdfFormXObject(new Rectangle(0, 0, _placeholderWidth, _fontSize));
|
||||
_pageNumPlaceholders.Add(placeholder);
|
||||
|
||||
var c = App.Client;
|
||||
var dateP = new Paragraph(_date).SetFont(_font).SetFontSize(_fontSize);
|
||||
var centerP = new Paragraph(_center ?? "").SetFont(_fontItalic).SetFontSize(_fontSize);
|
||||
var pageNumP = new Paragraph().Add(new Image(placeholder)).SetFont(_font).SetFontSize(_fontSize);
|
||||
|
||||
if (pageNum == 1) {
|
||||
// First page
|
||||
canvas.ShowTextAligned(dateP, leftX1, y, TextAlignment.LEFT);
|
||||
canvas.ShowTextAligned(centerP, (leftX1 + rightX1) / 2, y, TextAlignment.CENTER);
|
||||
canvas.ShowTextAligned(pageNumP, rightX1, y, TextAlignment.RIGHT);
|
||||
|
||||
var footerP = new Paragraph().SetFont(_font).SetFontSize(_fontSize)
|
||||
.SetTextAlignment(TextAlignment.CENTER).SetVerticalAlignment(VerticalAlignment.TOP)
|
||||
.SetMargin(0).SetMultipliedLeading(1)
|
||||
.SetWidth(footerWidth).SetHeight(footerHeight).SetPaddingTop(1 * _ptInMm).SetBorderTop(new SolidBorder(0.5f))
|
||||
.Add(c.NameFull);
|
||||
if (_isBusiness) {
|
||||
footerP.Add("\n");
|
||||
footerP.AddAll(Utils.GenerateFooter("\n", " \u00b7 ")
|
||||
.Item(c.Address).Item($"{c.Plz} {c.Ort}").Item("Österreich").Item("Tel.", c.PhoneNr).Item("Fax", c.FaxNr).NextLine()
|
||||
.Item(c.EmailAddress != null ? new Link(c.EmailAddress, PdfAction.CreateURI($"mailto:{Uri.EscapeDataString(c.Name)}%20<{c.EmailAddress}>")) : null)
|
||||
.Item(c.Website != null ? new Link(c.Website, PdfAction.CreateURI($"http://{c.Website}/")) : null)
|
||||
.Item("Betriebs-Nr.", c.LfbisNr).Item("Bio-KSt.", c.OrganicAuthority).NextLine()
|
||||
.Item("UID", c.UstIdNr).Item("BIC", c.Bic).Item("IBAN", c.Iban)
|
||||
.ToLeafElements());
|
||||
}
|
||||
// FIXME links are drawn on next page - move footer into "normal" document creation
|
||||
canvas.ShowTextAligned(footerP, (leftX1 + rightX1) / 2, footerY, TextAlignment.CENTER, VerticalAlignment.TOP);
|
||||
} else if (_doublePaged && (pageNum % 2 == 0)) {
|
||||
// Swap side
|
||||
canvas.ShowTextAligned(pageNumP, leftX2, footerY, TextAlignment.LEFT, VerticalAlignment.TOP);
|
||||
canvas.ShowTextAligned(centerP, (leftX2 + rightX2) / 2, footerY, TextAlignment.CENTER, VerticalAlignment.TOP);
|
||||
canvas.ShowTextAligned(dateP, rightX2, footerY, TextAlignment.RIGHT, VerticalAlignment.TOP);
|
||||
} else {
|
||||
canvas.ShowTextAligned(dateP, leftX1, footerY, TextAlignment.LEFT, VerticalAlignment.TOP);
|
||||
canvas.ShowTextAligned(centerP, (leftX1 + rightX1) / 2, footerY, TextAlignment.CENTER, VerticalAlignment.TOP);
|
||||
canvas.ShowTextAligned(pageNumP, rightX1, footerY, TextAlignment.RIGHT, VerticalAlignment.TOP);
|
||||
}
|
||||
|
||||
if (_showFoldMarks) {
|
||||
var m1 = pageSize.GetTop() - 105 * _ptInMm;
|
||||
var m2 = pageSize.GetTop() - 148.5 * _ptInMm;
|
||||
var m3 = pageSize.GetTop() - 210 * _ptInMm;
|
||||
pdfCanvas.SetLineWidth(0.5f);
|
||||
|
||||
pdfCanvas.MoveTo(0, m1);
|
||||
pdfCanvas.LineTo(10 * _ptInMm, m1);
|
||||
pdfCanvas.MoveTo(pageSize.GetRight(), m1);
|
||||
pdfCanvas.LineTo(pageSize.GetRight() - 10 * _ptInMm, m1);
|
||||
|
||||
pdfCanvas.MoveTo(0, m2);
|
||||
pdfCanvas.LineTo(7 * _ptInMm, m2);
|
||||
pdfCanvas.MoveTo(pageSize.GetRight(), m2);
|
||||
pdfCanvas.LineTo(pageSize.GetRight() - 7 * _ptInMm, m2);
|
||||
|
||||
pdfCanvas.MoveTo(0, m3);
|
||||
pdfCanvas.LineTo(10 * _ptInMm, m3);
|
||||
pdfCanvas.MoveTo(pageSize.GetRight(), m3);
|
||||
pdfCanvas.LineTo(pageSize.GetRight() - 10 * _ptInMm, m3);
|
||||
|
||||
pdfCanvas.ClosePathStroke();
|
||||
}
|
||||
|
||||
if (NumberOfPages > 0) {
|
||||
// FillPlaceholders() was already called
|
||||
FillPlaceholder(pdf, pageNum);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDocumentClose(PdfDocumentEvent evt) {
|
||||
FillPlaceholders(evt.GetDocument());
|
||||
}
|
||||
|
||||
private void FillPlaceholders(PdfDocument pdf) {
|
||||
NumberOfPages = pdf.GetNumberOfPages();
|
||||
for (int i = 0; i < _pageNumPlaceholders.Count; i++)
|
||||
FillPlaceholder(pdf, i + 1);
|
||||
}
|
||||
|
||||
private void FillPlaceholder(PdfDocument pdf, int pageNum) {
|
||||
var placeholder = _pageNumPlaceholders[pageNum - 1];
|
||||
using var canvas = new Canvas(placeholder, pdf);
|
||||
if (_doublePaged && (pageNum % 2 == 0)) {
|
||||
// swap
|
||||
var p = new Paragraph().SetFont(_font).SetFontSize(_fontSize);
|
||||
if (_isPreview) p.Add(new Text("(vorläufig) ").SetFont(_fontBold));
|
||||
p.Add($"Seite {pageNum:N0} von {NumberOfPages:N0} ");
|
||||
canvas.ShowTextAligned(p, 0, 0, TextAlignment.LEFT);
|
||||
} else {
|
||||
var p = new Paragraph().SetFont(_font).SetFontSize(_fontSize)
|
||||
.Add($"Seite {pageNum:N0} von {NumberOfPages:N0}");
|
||||
if (_isPreview) p.Add(new Text(" (vorläufig)").SetFont(_fontBold));
|
||||
canvas.ShowTextAligned(p, _placeholderWidth, 0, TextAlignment.RIGHT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,103 +1,148 @@
|
||||
using Elwig.Windows;
|
||||
using iText.Html2pdf;
|
||||
using iText.Kernel.Pdf;
|
||||
using iText.Kernel.Pdf.Event;
|
||||
using iText.Kernel.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing.Printing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Sockets;
|
||||
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 = (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)
|
||||
.FirstOrDefault() ?? throw new FileNotFoundException("WiniPrint executable not found");
|
||||
private static Process? WinziPrintProc;
|
||||
public static bool IsReady => WinziPrintProc != null;
|
||||
|
||||
public static async Task Init(Action? evtHandler = null) {
|
||||
public static Task Init(Action? evtHandler = null) {
|
||||
PdfiumNative.FPDF_InitLibrary();
|
||||
// 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
|
||||
var p = new Process() { StartInfo = new() {
|
||||
FileName = WinziPrint,
|
||||
CreateNoWindow = true,
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true,
|
||||
} };
|
||||
p.StartInfo.ArgumentList.Add("-D");
|
||||
p.StartInfo.ArgumentList.Add("-d");
|
||||
p.StartInfo.ArgumentList.Add(App.TempPath);
|
||||
p.Start();
|
||||
await p.StandardOutput.ReadLineAsync();
|
||||
WinziPrintProc = p;
|
||||
evtHandler?.Invoke();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public static Task Cleanup() {
|
||||
WinziPrintProc?.Kill(true);
|
||||
WinziPrintProc?.Close();
|
||||
PdfiumNative.FPDF_DestroyLibrary();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
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 (int Pages, IEnumerable<int> PerDoc) Convert(string htmlPath, string pdfPath, Documents.Document? doc = null) {
|
||||
int nPages;
|
||||
using var html = File.Open(htmlPath, FileMode.Open);
|
||||
using var writer = new PdfWriter(pdfPath);
|
||||
using var pdf = new PdfDocument(writer);
|
||||
var footerHandler = new FooterEventHandler(doc);
|
||||
pdf.AddEventHandler(PdfDocumentEvent.END_PAGE, footerHandler);
|
||||
pdf.AddEventHandler(PdfDocumentEvent.START_DOCUMENT_CLOSING, footerHandler);
|
||||
// TODO embed fonts?
|
||||
//pdf.AddFont(PdfFontFactory.CreateFont(StandardFonts.TIMES_ROMAN));
|
||||
HtmlConverter.ConvertToPdf(html, pdf);
|
||||
nPages = footerHandler.NumberOfPages;
|
||||
return (nPages, [nPages]);
|
||||
}
|
||||
|
||||
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"));
|
||||
bool cancelled = false;
|
||||
while (true) {
|
||||
if (!cancelled && (cancelToken?.IsCancellationRequested ?? false)) {
|
||||
public static (int Pages, IEnumerable<int> PerDoc) Convert(IEnumerable<string> inputFiles, string pdfPath, bool doublePaged = false, IEnumerable<Documents.Document>? docs = null, CancellationToken? cancelToken = null, IProgress<double>? progress = null) {
|
||||
var tmpFileNames = new List<string>();
|
||||
var pageNums = new List<int>();
|
||||
var tmpPageNums = new List<int>();
|
||||
|
||||
var htmlFiles = inputFiles
|
||||
.Where(f => !f.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase))
|
||||
.Select(f => f.TrimStart('!', '#'))
|
||||
.ToList();
|
||||
|
||||
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;
|
||||
for (int i = 0; i < htmlFiles.Count; i++) {
|
||||
string tmpFile = $"{pdfPath}.{i:0000}.part";
|
||||
tmpFileNames.Add(tmpFile);
|
||||
var (pages, _) = Convert(htmlFiles[i], tmpFile, docs?.ElementAt(i));
|
||||
tmpPageNums.Add(pages);
|
||||
}
|
||||
var line = await reader.ReadLineAsync() ?? throw new IOException("Invalid response from WinziPrint");
|
||||
if (line.StartsWith("error:")) {
|
||||
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]);
|
||||
} else if (line.StartsWith("success:")) {
|
||||
var m = Regex.Match(line, @"([0-9]+) pages \(([0-9, ]+)\)");
|
||||
return (int.Parse(m.Groups[1].Value), m.Groups[2].Value.Split(", ").Select(int.Parse).ToList());
|
||||
|
||||
using var writer = new PdfWriter(pdfPath);
|
||||
using var mergedPdf = new PdfDocument(writer);
|
||||
var merger = new PdfMerger(mergedPdf);
|
||||
|
||||
PdfPage? letterheadPage = null;
|
||||
int letterheadInsertIndex = 0;
|
||||
int letterheadDocIndex = 0;
|
||||
|
||||
for (int i = 0; i < inputFiles.Count(); i++) {
|
||||
var fileName = inputFiles.ElementAt(i);
|
||||
int p0 = mergedPdf.GetNumberOfPages();
|
||||
|
||||
if (letterheadPage != null && fileName.StartsWith('#')) {
|
||||
if (mergedPdf.GetNumberOfPages() <= letterheadInsertIndex) {
|
||||
mergedPdf.AddPage(letterheadPage.CopyTo(mergedPdf));
|
||||
mergedPdf.AddNewPage();
|
||||
} else {
|
||||
mergedPdf.AddPage(letterheadInsertIndex + 1, letterheadPage.CopyTo(mergedPdf));
|
||||
mergedPdf.AddNewPage(letterheadInsertIndex + 2);
|
||||
}
|
||||
|
||||
pageNums[letterheadDocIndex] = 1;
|
||||
letterheadPage = null;
|
||||
}
|
||||
|
||||
if (fileName.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase)) {
|
||||
var cleanName = fileName.TrimStart('!', '#');
|
||||
|
||||
if (doublePaged && fileName.StartsWith('#')) {
|
||||
using var reader = new PdfReader(cleanName);
|
||||
using var src = new PdfDocument(reader);
|
||||
letterheadPage = src.GetPage(1).CopyTo(mergedPdf);
|
||||
letterheadInsertIndex = p0;
|
||||
letterheadDocIndex = i;
|
||||
} else {
|
||||
using var reader = new PdfReader(cleanName);
|
||||
using var src = new PdfDocument(reader);
|
||||
merger.Merge(src, 1, src.GetNumberOfPages());
|
||||
}
|
||||
} else {
|
||||
string tmpFile = tmpFileNames[i];
|
||||
if (doublePaged && fileName.StartsWith('#')) {
|
||||
using var reader = new PdfReader(tmpFile);
|
||||
using var src = new PdfDocument(reader);
|
||||
letterheadPage = src.GetPage(1).CopyTo(mergedPdf);
|
||||
letterheadInsertIndex = p0;
|
||||
letterheadDocIndex = i;
|
||||
} else {
|
||||
using var reader = new PdfReader(tmpFile);
|
||||
using var src = new PdfDocument(reader);
|
||||
merger.Merge(src, 1, tmpPageNums[i]);
|
||||
}
|
||||
}
|
||||
|
||||
int p1 = mergedPdf.GetNumberOfPages();
|
||||
pageNums.Add(p1 - p0);
|
||||
|
||||
if (doublePaged && fileName[0] != '!' && fileName[0] != '#' && mergedPdf.GetNumberOfPages() % 2 != 0) {
|
||||
if (letterheadPage != null) {
|
||||
mergedPdf.AddPage(letterheadPage.CopyTo(mergedPdf));
|
||||
letterheadPage = null;
|
||||
} else {
|
||||
mergedPdf.AddNewPage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (letterheadPage != null) {
|
||||
if (mergedPdf.GetNumberOfPages() <= letterheadInsertIndex) {
|
||||
mergedPdf.AddPage(letterheadPage.CopyTo(mergedPdf));
|
||||
mergedPdf.AddNewPage();
|
||||
} else {
|
||||
mergedPdf.AddPage(letterheadInsertIndex + 1, letterheadPage.CopyTo(mergedPdf));
|
||||
mergedPdf.AddNewPage(letterheadInsertIndex + 2);
|
||||
}
|
||||
|
||||
pageNums[letterheadDocIndex] = 1;
|
||||
}
|
||||
} finally {
|
||||
foreach (var tmp in tmpFileNames) {
|
||||
if (File.Exists(tmp)) File.Delete(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
return (pageNums.Sum(), pageNums);
|
||||
}
|
||||
|
||||
public static void Show(TempFile file, string title) {
|
||||
|
||||
@@ -3,6 +3,7 @@ using Elwig.Documents;
|
||||
using Elwig.Helpers.Billing;
|
||||
using Elwig.Models;
|
||||
using Elwig.Models.Entities;
|
||||
using iText.Layout.Element;
|
||||
using LinqKit;
|
||||
using MailKit.Net.Smtp;
|
||||
using MailKit.Security;
|
||||
@@ -304,23 +305,18 @@ namespace Elwig.Helpers {
|
||||
}
|
||||
|
||||
public class Footer {
|
||||
private string Text = "";
|
||||
private readonly List<List<object>> Items = [[]];
|
||||
private readonly string LineBreak;
|
||||
private readonly string Seperator;
|
||||
private bool FirstLine = true;
|
||||
private bool FirstItemInLine = true;
|
||||
private readonly string Separator;
|
||||
|
||||
public Footer(string lineBreak, string seperator) {
|
||||
public Footer(string lineBreak, string separator) {
|
||||
LineBreak = lineBreak;
|
||||
Seperator = seperator;
|
||||
Separator = separator;
|
||||
}
|
||||
|
||||
public Footer Item(string? text) {
|
||||
public Footer Item(object? text) {
|
||||
if (text == null) return this;
|
||||
Text += FirstItemInLine ? (FirstLine ? "" : LineBreak) : Seperator;
|
||||
Text += text;
|
||||
FirstLine = false;
|
||||
FirstItemInLine = false;
|
||||
Items[^1].Add(text);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -329,12 +325,28 @@ namespace Elwig.Helpers {
|
||||
}
|
||||
|
||||
public Footer NextLine() {
|
||||
FirstItemInLine = true;
|
||||
Items.Add([]);
|
||||
return this;
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return Text;
|
||||
return string.Join(LineBreak, Items.Select(l => string.Join(Separator, l.ToString())));
|
||||
}
|
||||
|
||||
public IList<ILeafElement> ToLeafElements() {
|
||||
var l = new List<ILeafElement>();
|
||||
var first1 = true;
|
||||
foreach (var line in Items) {
|
||||
if (!first1) l.Add(new Text(LineBreak));
|
||||
var first2 = true;
|
||||
foreach (var item in line) {
|
||||
if (!first2) l.Add(new Text(Separator));
|
||||
l.Add(item as ILeafElement ?? new Text(item.ToString()));
|
||||
first2 = false;
|
||||
}
|
||||
first1 = false;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ namespace Elwig.Models.Dtos {
|
||||
public string? Attribute;
|
||||
public string? Cultivation;
|
||||
public string QualityLevel;
|
||||
public bool IsDepreciated;
|
||||
public (double Oe, double Kmw) Gradation;
|
||||
public string[] Modifiers;
|
||||
public int Weight;
|
||||
@@ -89,17 +90,15 @@ namespace Elwig.Models.Dtos {
|
||||
Attribute = p.Attribute?.Name;
|
||||
Cultivation = p.Cultivation?.Name;
|
||||
QualityLevel = p.Quality.Name;
|
||||
IsDepreciated = p.QualId == "WEI";
|
||||
Gradation = (p.Oe, p.Kmw);
|
||||
Modifiers = p.Modifiers
|
||||
.Select(m => m.Name)
|
||||
.ToArray();
|
||||
Modifiers = [.. p.Modifiers.Select(m => m.Name)];
|
||||
Weight = p.Weight;
|
||||
IsNetWeight = p.IsNetWeight;
|
||||
Buckets = p.Buckets
|
||||
Buckets = [.. p.Buckets
|
||||
.Where(b => b.Value > 0)
|
||||
.OrderByDescending(b => b.BktNr)
|
||||
.Select(b => (b.Discr == "_" ? "ungeb." : $"geb. {p.SortId}{b.Discr}", b.Value))
|
||||
.ToArray();
|
||||
.Select(b => (b.Discr == "_" ? "ungeb." : $"geb. {p.SortId}{b.Discr}", b.Value))];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
-- 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;
|
||||
UPDATE delivery_part SET unloading = 'pumped' WHERE lesewagen;
|
||||
UPDATE delivery_part SET unloading = 'box' WHERE (SELECT zwstid IN ('H','S') FROM delivery d WHERE (d.year, d.did) = (delivery_part.year, delivery_part.did));
|
||||
ALTER TABLE delivery_part DROP COLUMN lesewagen;
|
||||
UPDATE client_parameter SET value = '1' WHERE param = 'ENABLE_TIME_TRIGGERS';
|
||||
|
||||
12
Elwig/Resources/Sql/36-37.sql
Normal file
12
Elwig/Resources/Sql/36-37.sql
Normal 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';
|
||||
@@ -271,7 +271,11 @@ namespace Elwig.Services {
|
||||
}
|
||||
|
||||
public static async Task<bool> ChangesAvailable(AppDbContext ctx, string url, string username, string password) {
|
||||
try {
|
||||
return await ctx.Members.AnyAsync(ChangedMembers) || await ctx.Deliveries.AnyAsync(ChangedDeliveries) || (Utils.HasInternetConnectivity() && (await GetFilesToImport(url, username, password)).Count > 0);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Elwig.ViewModels {
|
||||
private IEnumerable<PaymentVar> _paymentVariants = [];
|
||||
|
||||
public BillingData? BillingData;
|
||||
public bool SeasonLocked;
|
||||
public bool SeasonLocked = false; // never locked
|
||||
public bool WeightModifierChanged;
|
||||
|
||||
[ObservableProperty]
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
Programmiersprache: C#<LineBreak/>
|
||||
Framework: Windows Presentation Framework (WPF)<LineBreak/>
|
||||
Datenbank: <Hyperlink NavigateUri="https://sqlite.org/" RequestNavigate="Hyperlink_RequestNavigate">SQLite</Hyperlink><LineBreak/>
|
||||
PDF-Erstellung: <Hyperlink NavigateUri="https://weasyprint.org/" RequestNavigate="Hyperlink_RequestNavigate">WeasyPrint</Hyperlink>, <Hyperlink NavigateUri="https://github.com/toddams/RazorLight" RequestNavigate="Hyperlink_RequestNavigate">RazorLight</Hyperlink>, <Hyperlink NavigateUri="https://github.com/bblanchon/pdfium-binaries" RequestNavigate="Hyperlink_RequestNavigate">Pdfium</Hyperlink><LineBreak/>
|
||||
PDF-Erstellung: <Hyperlink NavigateUri="https://itextpdf.com/" RequestNavigate="Hyperlink_RequestNavigate">iText</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>
|
||||
</TextBlock>
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
Grid.Column="0" Margin="10,8,10,10"/>
|
||||
<ListBox x:Name="AvaiableDocumentsList"
|
||||
Grid.Column="0" Margin="10,30,10,10"
|
||||
SelectionChanged="AvaiableDocumentsList_SelectionChanged"/>
|
||||
SelectionChanged="AvaiableDocumentsList_SelectionChanged" MouseDoubleClick="AvaiableDocumentsList_MouseDoubleClick"/>
|
||||
|
||||
<Button x:Name="DocumentAddButton" Content="" FontFamily="Segoe MDL2 Assets" FontSize="14"
|
||||
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"/>
|
||||
<ListBox x:Name="SelectedDocumentsList" DisplayMemberPath="Name"
|
||||
Grid.Column="2" Margin="10,30,10,37"
|
||||
SelectionChanged="SelectedDocumentsList_SelectionChanged">
|
||||
SelectionChanged="SelectedDocumentsList_SelectionChanged" MouseDoubleClick="SelectedDocumentsList_MouseDoubleClick">
|
||||
<ListBox.InputBindings>
|
||||
<KeyBinding Key="Delete" Command="{Binding Path=DeleteCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MailWindow}}}"/>
|
||||
</ListBox.InputBindings>
|
||||
@@ -138,58 +138,61 @@
|
||||
<RadioButton GroupName="Recipients" x:Name="RecipientsNonDeliveryMembersInput" Content="Nicht-Lieferanten der Saison"
|
||||
Margin="10,90,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
|
||||
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"
|
||||
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"
|
||||
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"/>
|
||||
|
||||
<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"
|
||||
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"/>
|
||||
|
||||
<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"
|
||||
Margin="80,205,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
|
||||
Margin="80,225,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
|
||||
Checked="MemberInput_Checked"/>
|
||||
<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"/>
|
||||
<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"/>
|
||||
|
||||
<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"
|
||||
Margin="80,235,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
|
||||
Margin="80,255,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
|
||||
Checked="MemberInput_Checked"/>
|
||||
<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"/>
|
||||
<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"/>
|
||||
|
||||
<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"
|
||||
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"/>
|
||||
|
||||
<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"
|
||||
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"/>
|
||||
|
||||
<ctrl:CheckComboBox x:Name="MemberCustomInput" AllItemsSelectedContent="Alle Mitglieder" Delimiter=", " DisplayMemberPath="AdministrativeName"
|
||||
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"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
@@ -333,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;
|
||||
using var ctx = new AppDbContext();
|
||||
if (AvaiableDocumentsList.SelectedItem is not string s)
|
||||
@@ -347,13 +375,13 @@ namespace Elwig.Windows {
|
||||
var name = s.Split(" – ")[^1];
|
||||
var pv = await ctx.PaymentVariants.SingleAsync(v => v.Year == Year && v.Name == name)!;
|
||||
SelectedDocs.Add(new(DocType.CreditNote, s, (pv.Year, pv.AvNr)));
|
||||
RecipientsDeliveryMembersInput.IsChecked = true;
|
||||
RecipientsCreditMembersInput.IsChecked = true;
|
||||
}
|
||||
SelectedDocumentsList.SelectedIndex = SelectedDocs.Count - 1;
|
||||
await UpdateRecipients(ctx);
|
||||
}
|
||||
|
||||
private async void DocumentRemoveButton_Click(object sender, RoutedEventArgs evt) {
|
||||
private async void DocumentRemoveButton_Click(object sender, RoutedEventArgs? evt) {
|
||||
DeleteCommand.Execute(null);
|
||||
using var ctx = new AppDbContext();
|
||||
await UpdateRecipients(ctx);
|
||||
@@ -456,6 +484,9 @@ namespace Elwig.Windows {
|
||||
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)) {
|
||||
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 {
|
||||
query = query.Where(m => m.IsActive);
|
||||
}
|
||||
@@ -756,7 +787,7 @@ namespace Elwig.Windows {
|
||||
|
||||
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?",
|
||||
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);
|
||||
if (res != MessageBoxResult.OK) {
|
||||
UnlockInputs();
|
||||
@@ -1014,7 +1045,7 @@ namespace Elwig.Windows {
|
||||
var pv = ctx.PaymentVariants.Single(v => v.Year == Year && v.Name == name)!;
|
||||
SelectedDocs.Add(new(DocType.CreditNote, s, (pv.Year, pv.AvNr)));
|
||||
SelectedDocumentsList.SelectedIndex = SelectedDocs.Count - 1;
|
||||
RecipientsDeliveryMembersInput.IsChecked = true;
|
||||
RecipientsCreditMembersInput.IsChecked = true;
|
||||
}
|
||||
|
||||
private void DocumentInput_TextChanged(object sender, TextChangedEventArgs evt) {
|
||||
|
||||
@@ -16,16 +16,14 @@ namespace Elwig.Windows {
|
||||
public partial class PaymentAdjustmentWindow : ContextWindow {
|
||||
|
||||
public readonly int Year;
|
||||
public readonly bool SeasonLocked;
|
||||
public readonly bool SeasonLocked = false; // never locked
|
||||
|
||||
public Dictionary<int, PaymentCustom>? CustomPayments;
|
||||
|
||||
public PaymentAdjustmentWindow(int year) {
|
||||
InitializeComponent();
|
||||
Year = year;
|
||||
using (var ctx = new AppDbContext()) {
|
||||
SeasonLocked = ctx.Seasons.Find(Year + 1) != null;
|
||||
}
|
||||
// using (var ctx = new AppDbContext()) { SeasonLocked = ctx.Seasons.Find(Year + 1) != null; }
|
||||
Title = $"Auszahlung anpassen - Lese {Year} - Elwig";
|
||||
AutoAdjustBsButton.IsEnabled = !SeasonLocked;
|
||||
UnAdjustBsButton.IsEnabled = !SeasonLocked;
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
using Elwig.Helpers;
|
||||
using Elwig.Helpers.Billing;
|
||||
using Elwig.Helpers.Export;
|
||||
using Elwig.Models.Dtos;
|
||||
using Elwig.Models.Entities;
|
||||
using Elwig.Services;
|
||||
using Elwig.ViewModels;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Win32;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
@@ -35,9 +32,7 @@ namespace Elwig.Windows {
|
||||
CommandBindings.Add(new CommandBinding(CtrlÜ, Menu_EbicsSave_Click));
|
||||
CommandBindings.Add(new CommandBinding(CtrlShiftP, Menu_SummaryPrint_Click));
|
||||
Year = year;
|
||||
using (var ctx = new AppDbContext()) {
|
||||
ViewModel.SeasonLocked = ctx.Seasons.Find(Year + 1) != null;
|
||||
}
|
||||
// using (var ctx = new AppDbContext()) { ViewModel.SeasonLocked = ctx.Seasons.Find(Year + 1) != null; }
|
||||
Title = $"Auszahlungsvarianten - Lese {Year} - Elwig";
|
||||
if (!App.Config.Debug) {
|
||||
DataInput.Visibility = Visibility.Hidden;
|
||||
|
||||
Binary file not shown.
@@ -35,6 +35,5 @@
|
||||
</Target>
|
||||
<ItemGroup>
|
||||
<None Include="Files\config.ini" />
|
||||
<None Include="Files\WinziPrint.exe" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
<Component Directory="ConfigFolder" Permanent="true" NeverOverwrite="true">
|
||||
<File Source="$(ProjectDir)\Files\config.ini" Id="config.ini"/>
|
||||
</Component>
|
||||
<Component Directory="InstallFolder">
|
||||
<File Source="$(ProjectDir)\Files\WinziPrint.exe" Id="WinziPrint.exe"/>
|
||||
</Component>
|
||||
</ComponentGroup>
|
||||
</Fragment>
|
||||
</Wix>
|
||||
|
||||
@@ -13,7 +13,7 @@ About
|
||||
**Product:** Elwig
|
||||
**Description:** Electronic Management for Vintners' Cooperatives
|
||||
**Type:** ERP system
|
||||
**Version:** 1.0.3.3 ([Changelog](./CHANGELOG.md))
|
||||
**Version:** 1.0.3.4 ([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.3.3 ([Änderungsprotokoll](./CHANGELOG.md))
|
||||
**Version:** 1.0.3.4 ([Änderungsprotokoll](./CHANGELOG.md))
|
||||
**Lizenz:** [GNU General Public License 3.0 (GPLv3)](./LICENSE)
|
||||
**Website:** https://elwig.at/
|
||||
**Quellcode:** https://git.necronda.net/winzer/elwig
|
||||
@@ -43,5 +43,5 @@ Packaging: [WiX Toolset](https://www.firegiant.com/wixtoolset/)
|
||||
Programmiersprache: C#
|
||||
Framework: Windows Presentation Framework (WPF)
|
||||
Datenbank: [SQLite](https://sqlite.org/)
|
||||
PDF-Erstellung: [WeasyPrint](https://weasyprint.org/), [RazorLight](https://github.com/toddams/RazorLight), [Pdfium](https://github.com/bblanchon/pdfium-binaries)
|
||||
PDF-Erstellung: [iText](https://itextpdf.com/), [RazorLight](https://github.com/toddams/RazorLight), [Pdfium](https://github.com/bblanchon/pdfium-binaries)
|
||||
Paketierung: [WiX Toolset](https://www.firegiant.com/wixtoolset/)
|
||||
|
||||
@@ -22,13 +22,13 @@
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
|
||||
<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="NUnit" Version="4.5.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>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.4">
|
||||
<PackageReference Include="coverlet.collector" Version="8.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
Reference in New Issue
Block a user