Compare commits

...

30 Commits

Author SHA1 Message Date
e8bd81ccc7 Installer: Fix typo 2023-10-20 00:13:28 +02:00
f26d55af68 Bump version to 0.4.3 2023-10-20 00:10:22 +02:00
e46ddb9cdc DeliveryConfirmationsDialog: move to DeliveryConfirmationsWindow 2023-10-19 23:50:21 +02:00
3cf3ca71d6 Update fetch-resources.bat 2023-10-19 23:41:14 +02:00
1d4ab7b264 Update fetch-resources.bat 2023-10-19 23:32:47 +02:00
841f8bfb84 Documents/Pdf: fix progression 2023-10-19 23:25:55 +02:00
a075189bdd Added WinziPrint.exe to Wix Installer 2023-10-19 23:22:18 +02:00
e52475a4bf Documents: fix WinziPrint args 2023-10-19 22:45:31 +02:00
8ce4911317 DeliveryConfirmationsDialog: small fixes 2023-10-19 22:35:31 +02:00
c190ce1474 Documents/Pdf: Add progress tracker 2023-10-19 22:16:33 +02:00
f19de3ae6e Billing: fix calc issue 2023-10-19 18:54:47 +02:00
e56e209506 DeliveryConfirmation: Remove footer todo 2023-10-19 17:07:18 +02:00
28fb4f6fa2 DeliveryConfirmation: Cache deliveries of all members 2023-10-19 17:04:35 +02:00
a832879b73 Documents: use WinziPrint instead of WeasyPrint 2023-10-19 16:38:03 +02:00
a2f49e1b8b Bump version to 0.4.2 2023-10-19 01:10:13 +02:00
99c3474fb6 App: Fix wolkersdorf im weinviertel branch name issue 2023-10-19 01:09:26 +02:00
9924795888 DeliveryAdminWindow: make only non-predicate quality levels searchable 2023-10-19 01:09:03 +02:00
733ab0d208 Add wine variety and attribute to AreaComAdminWindow datagrid 2023-10-19 00:18:28 +02:00
6e2ba56a7c SeasonFinishWindow: Implement Über-/Unterlieferungen 2023-10-19 00:09:38 +02:00
2507a2695f DeliveryConfirmation: Change stat table headings 2023-10-18 18:44:12 +02:00
ddd83bf63d Bump version to 0.4.1 2023-10-18 01:31:22 +02:00
685793f0fb Billing: Implement feature 'avoid under deliveries' 2023-10-18 01:30:34 +02:00
f6712704ee DeliveryConfirmationsDialog: Fix button width 2023-10-18 01:22:17 +02:00
de500854c4 AppDbContext: Add v_area_commitment_bin 2023-10-17 23:48:16 +02:00
d992b6a206 Document: Add member name to pdf viewer window title 2023-10-17 23:10:19 +02:00
d2b96736bb DeliveryConfirmation: remove name from title 2023-10-17 23:02:28 +02:00
50b9f4e207 AppDbContext: add name for _ attribute 2023-10-17 22:58:22 +02:00
45fc0893b1 DeliveryConfirmation: fix tables 2023-10-17 22:56:21 +02:00
c4dd56075d DeliveryConfirmationsDialog: Use Print-Tag 2023-10-17 20:53:40 +02:00
f1084c716a DeliveryConfirmationsDialog: Add AllMemberInput checkbox 2023-10-17 20:50:16 +02:00
22 changed files with 408 additions and 156 deletions

View File

@ -127,7 +127,7 @@ namespace Elwig {
ZwstId = entry.Item1; ZwstId = entry.Item1;
BranchName = entry.Item2; BranchName = entry.Item2;
BranchPlz = entry.Item3; BranchPlz = entry.Item3;
BranchLocation = entry.Item4; BranchLocation = entry.Item4?.Split(" im ")[0]; // FIXME
BranchAddress = entry.Item5; BranchAddress = entry.Item5;
BranchPhoneNr = entry.Item6; BranchPhoneNr = entry.Item6;
BranchFaxNr = entry.Item7; BranchFaxNr = entry.Item7;
@ -138,7 +138,7 @@ namespace Elwig {
ZwstId = entry.Item1; ZwstId = entry.Item1;
BranchName = entry.Item2; BranchName = entry.Item2;
BranchPlz = entry.Item3; BranchPlz = entry.Item3;
BranchLocation = entry.Item4; BranchLocation = entry.Item4?.Split(" im ")[0]; // FIXME
BranchAddress = entry.Item5; BranchAddress = entry.Item5;
BranchPhoneNr = entry.Item6; BranchPhoneNr = entry.Item6;
BranchFaxNr = entry.Item7; BranchFaxNr = entry.Item7;

View File

@ -109,41 +109,52 @@
<th><b>Lese @Model.Year</b> per @($"{Model.Date:dd.MM.yyyy}") [kg]</th> <th><b>Lese @Model.Year</b> per @($"{Model.Date:dd.MM.yyyy}") [kg]</th>
<th>Lieferpflicht</th> <th>Lieferpflicht</th>
<th>Lieferrecht</th> <th>Lieferrecht</th>
<th>Unterliefert<br/>(bzgl. Zuget.)</th> <th>Unterliefert</th>
<th>Noch zu liefern<br/>(bzgl. Gelft.)</th> <th>Noch lieferbar</th>
<th>Überliefert<br/>(bzgl. Gelft.)</th> <th>Überliefert</th>
<th>Zugeteilt</th> <th>Zugeteilt</th>
<th>Geliefert</th> <th>Geliefert</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@{ @{
string FormatRow(int obligation, int right, int sum, int? payment = null) { string FormatRow(int mode, int obligation, int right, int sum, int? payment = null) {
var isGa = payment == null; var isGa = mode == 0;
payment ??= sum; payment ??= sum;
return $"<td>{obligation:N0}</td>" + return $"<td>{(mode == 1 ? "" : obligation == 0 ? "-" : $"{obligation:N0}")}</td>" +
$"<td>{right:N0}</td>" + $"<td>{(mode == 1 ? "" : right == 0 ? "-" : $"{right:N0}")}</td>" +
$"<td>{(payment < obligation ? $"<b>{obligation - payment:N0}\x3c/b>" : "-")}</td>" + $"<td>{(mode == 1 ? "" : payment < obligation ? $"<b>{obligation - payment:N0}\x3c/b>" : "-")}</td>" +
$"<td>{(sum >= obligation && sum <= right ? $"{right - sum:N0}" : "-")}</td>" + $"<td>{(mode == 1 ? "" : payment >= obligation && sum <= right ? $"{right - sum:N0}" : "-")}</td>" +
$"<td>{(obligation == 0 && right == 0 ? "-" : (sum > right ? ((isGa ? "<b>" : "") + $"{sum - right:N0}" + (isGa ? "</b>" : "")) : "-"))}</td>" + $"<td>{(mode == 1 ? "" : obligation == 0 && right == 0 ? "-" : (sum > right ? ((isGa ? "<b>" : "") + $"{sum - right:N0}" + (isGa ? "</b>" : "")) : "-"))}</td>" +
$"<td>{(obligation == 0 && right == 0 ? "-" : $"{payment:N0}")}</td>" + $"<td>{(mode != 2 ? "" : obligation == 0 && right == 0 ? "-" : $"{payment:N0}")}</td>" +
$"<td>{sum:N0}</td>"; $"<td>{sum:N0}</td>";
} }
var mBins = Model.MemberBins.Where(b => b.Value.Item2 > 0 || b.Value.Item3 > 0 || b.Value.Item4 > 0).ToList();
var fbVars = mBins.Where(b => b.Value.Item2 > 0 || b.Value.Item3 > 0).Select(b => b.Key.Replace("_", "")).Order().ToArray();
var fbs = mBins.Where(b => fbVars.Contains(b.Key)).OrderBy(b => b.Value.Item1);
var rem = mBins.Where(b => !fbVars.Contains(b.Key)).OrderBy(b => b.Value.Item1);
} }
<tr> <tr>
<th>Gesamtlieferung lt. gez. GA</th> <th>Gesamtlieferung lt. gez. GA</th>
@Raw(FormatRow(Model.Member.DeliveryObligation, Model.Member.DeliveryRight, Model.Member.Deliveries.Where(d => d.Year == Model.Year).Sum(d => d.Weight))) @Raw(FormatRow(0, Model.Member.DeliveryObligation, Model.Member.DeliveryRight, Model.Member.Deliveries.Where(d => d.Year == Model.Year).Sum(d => d.Weight)))
</tr> </tr>
<tr class="subheading"> @if (rem.Any()) {
<th>Flächenbindungen:</th> <tr class="subheading"><th colspan="8">Sortenaufteilung:</th></tr>
</tr> }
@foreach (var (id, (name, right, obligation, sum, payment)) in Model.MemberBins.OrderBy(b => b.Key)) { @foreach (var (id, (name, right, obligation, sum, payment)) in rem) {
if (right > 0 || obligation > 0 || sum > 0) { <tr>
<tr> <th>@name</th>
<th>@name</th> @Raw(FormatRow(1, obligation, right, sum, payment))
@Raw(FormatRow(obligation, right, sum, payment)) </tr>
</tr> }
} @if (fbs.Any()){
<tr class="subheading"><th colspan="8">Flächenbindungen:</th></tr>
}
@foreach (var (id, (name, right, obligation, sum, payment)) in fbs) {
<tr>
<th>@name</th>
@Raw(FormatRow(2, obligation, right, sum, payment))
</tr>
} }
</tbody> </tbody>
</table> </table>

View File

@ -12,15 +12,14 @@ namespace Elwig.Documents {
public string? Text = App.Client.TextDeliveryConfirmation; public string? Text = App.Client.TextDeliveryConfirmation;
public Dictionary<string, (string, int, int, int, int)> MemberBins; public Dictionary<string, (string, int, int, int, int)> MemberBins;
public DeliveryConfirmation(AppDbContext ctx, int year, Member m) : public DeliveryConfirmation(AppDbContext ctx, int year, Member m, IEnumerable<DeliveryPart>? deliveries = null) :
base($"Anlieferungsbestätigung {year} {((IAddress?)m.BillingAddress ?? m).Name}", m) { base($"Anlieferungsbestätigung {year}", m) {
Year = year; Year = year;
ShowDateAndLocation = true; ShowDateAndLocation = true;
UseBillingAddress = true; UseBillingAddress = true;
IncludeSender = true; IncludeSender = true;
// FIXME footer in merged documents DocumentId = $"Anl.-Best. {Year}/{m.MgNr}";
//DocumentId = $"Anl.-Best. {Year}/{m.MgNr}"; Deliveries = deliveries ?? ctx.DeliveryParts.FromSqlRaw($"""
Deliveries = ctx.DeliveryParts.FromSqlRaw($"""
SELECT p.* SELECT p.*
FROM v_delivery v FROM v_delivery v
JOIN delivery_part p ON (p.year, p.did, p.dpnr) = (v.year, v.did, v.dpnr) JOIN delivery_part p ON (p.year, p.did, p.dpnr) = (v.year, v.did, v.dpnr)

View File

@ -92,7 +92,7 @@
<th>Lieferpflicht</th> <th>Lieferpflicht</th>
<th>Lieferrecht</th> <th>Lieferrecht</th>
<th>Unterliefert</th> <th>Unterliefert</th>
<th>Noch zu liefern</th> <th>Noch lieferbar</th>
<th>Überliefert</th> <th>Überliefert</th>
<th>Geliefert</th> <th>Geliefert</th>
</tr> </tr>

View File

@ -3,14 +3,12 @@ using System.Threading.Tasks;
using System.IO; using System.IO;
using Elwig.Helpers; using Elwig.Helpers;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Linq; using System.Linq;
namespace Elwig.Documents { namespace Elwig.Documents {
public abstract partial class Document : IDisposable { public abstract partial class Document : IDisposable {
private TempFile? _pdfFile = null; private TempFile? _pdfFile = null;
private string? _renderedHtml = null;
public bool ShowFoldMarks = App.Config.Debug; public bool ShowFoldMarks = App.Config.Debug;
@ -39,18 +37,6 @@ namespace Elwig.Documents {
Date = DateTime.Today; Date = DateTime.Today;
} }
[GeneratedRegex(@"</body>.*?</footer>\s*</div>", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Compiled)]
private static partial Regex GeneratedDocumentHeaderRegex();
private static readonly Regex DocumentHeaderRegex = GeneratedDocumentHeaderRegex();
[GeneratedRegex(@"<style>.*?/style>", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Compiled)]
private static partial Regex GeneratedHtmlStyleRegex();
private static readonly Regex HtmlStyleRegex = GeneratedHtmlStyleRegex();
[GeneratedRegex(@"<link[^>]*>", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Compiled)]
private static partial Regex GeneratedHtmlLinkRegex();
private static readonly Regex HtmlLinkRegex = GeneratedHtmlLinkRegex();
~Document() { ~Document() {
Dispose(); Dispose();
} }
@ -61,34 +47,11 @@ namespace Elwig.Documents {
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
public static async Task<Document> Merge(IEnumerable<Document> docs) { public static Document Merge(IEnumerable<Document> docs) {
string html = ""; return new MergedDocument(docs);
var styles = new List<string>();
foreach (var d in docs) {
var h = await d.Render();
var s = HtmlStyleRegex.Matches(h).Select(m => m.Value).ToList();
var l = HtmlLinkRegex.Matches(h).Select(m => m.Value).ToList();
if (s.All(styles.Contains)) {
h = HtmlStyleRegex.Replace(h, "");
} else {
styles.AddRange(s);
}
if (l.All(styles.Contains)) {
h = HtmlLinkRegex.Replace(h, "");
} else {
styles.AddRange(l);
}
html += h;
}
html = DocumentHeaderRegex.Replace(html, "<div class='document-break'/>");
return new InternalDocument("Mehrere Dokumente") {
_renderedHtml = html,
};
} }
private async Task<string> Render() { private async Task<string> Render() {
if (_renderedHtml != null)
return _renderedHtml;
string name; string name;
if (this is BusinessLetter) { if (this is BusinessLetter) {
name = "BusinessLetter"; name = "BusinessLetter";
@ -109,17 +72,39 @@ namespace Elwig.Documents {
} }
private async Task<string> Render(string name) { private async Task<string> Render(string name) {
_renderedHtml = await Html.CompileRenderAsync(name, this); return await Html.CompileRenderAsync(name, this); ;
return _renderedHtml;
} }
public async Task Generate() { public async Task Generate(IProgress<double>? progress = null) {
var pdf = new TempFile("pdf"); progress?.Report(0.0);
using (var tmpHtml = new TempFile("html")) { if (this is MergedDocument m) {
await File.WriteAllTextAsync(tmpHtml.FilePath, await Render(), Utils.UTF8); var pdf = new TempFile("pdf");
await Pdf.Convert(tmpHtml.FilePath, pdf.FilePath); var tmpHtmls = new List<TempFile>();
var n = m.Documents.Count();
int i = 0;
foreach (var doc in m.Documents) {
var tmpHtml = new TempFile("html");
await File.WriteAllTextAsync(tmpHtml.FilePath, await doc.Render(), Utils.UTF8);
tmpHtmls.Add(tmpHtml);
i++;
progress?.Report(50.0 * i / n);
}
progress?.Report(50.0);
await Pdf.Convert(tmpHtmls.Select(f => f.FileName), pdf.FileName, new Progress<double>(v => progress?.Report(50.0 + v / 2)));
foreach (var tmp in tmpHtmls) {
tmp.Dispose();
}
_pdfFile = pdf;
} else {
var pdf = new TempFile("pdf");
using (var tmpHtml = new TempFile("html")) {
await File.WriteAllTextAsync(tmpHtml.FilePath, await Render(), Utils.UTF8);
progress?.Report(50.0);
await Pdf.Convert(tmpHtml.FilePath, pdf.FilePath);
}
_pdfFile = pdf;
} }
_pdfFile = pdf; progress?.Report(100.0);
} }
public void SaveTo(string pdfPath) { public void SaveTo(string pdfPath) {
@ -134,11 +119,14 @@ namespace Elwig.Documents {
public void Show() { public void Show() {
if (_pdfFile == null) throw new InvalidOperationException("Pdf file has not been generated yet"); if (_pdfFile == null) throw new InvalidOperationException("Pdf file has not been generated yet");
Pdf.Show(_pdfFile.NewReference(), Title); Pdf.Show(_pdfFile.NewReference(), Title + (this is BusinessDocument b ? $" - {b.Member.Name}" : ""));
} }
private class InternalDocument : Document { private class MergedDocument : Document {
public InternalDocument(string title) : base(title) { } public IEnumerable<Document> Documents;
public MergedDocument(IEnumerable<Document> docs) : base("Mehrere Dokumente") {
Documents = docs;
}
} }
} }
} }

View File

@ -2,44 +2,57 @@ using System.Threading.Tasks;
using Elwig.Helpers; using Elwig.Helpers;
using Elwig.Windows; using Elwig.Windows;
using System.Diagnostics; using System.Diagnostics;
using Balbarak.WeasyPrint;
using System; using System;
using System.IO; using System.IO;
using System.Collections.Generic;
using System.Windows;
using System.Text.RegularExpressions;
using System.Linq;
namespace Elwig.Documents { namespace Elwig.Documents {
public static class Pdf { public static class Pdf {
private static readonly string PdfToPrinter = App.ExePath + "PDFtoPrinter.exe"; private static readonly string PdfToPrinter = App.ExePath + "PDFtoPrinter.exe";
private static readonly FilesManager WeasyPrintManager = new(); private static readonly string WinziPrint = App.ExePath + "WinziPrint.exe";
private static string? WeasyPrintPython = null; private static Process? WinziPrintProc;
private static string? WeasyPrintDir => WeasyPrintManager.FolderPath; public static bool IsReady => WinziPrintProc != null;
public static bool IsReady => WeasyPrintPython != null && WeasyPrintDir != null;
public static async Task Init(Action evtHandler) { public static async Task Init(Action evtHandler) {
if (!WeasyPrintManager.IsFilesExsited()) { var p = new Process() { StartInfo = new() {
await WeasyPrintManager.InitFilesAsync(); FileName = WinziPrint,
} Arguments = $"-p -e utf-8 -d \"{App.TempPath}\" -",
WeasyPrintPython = Path.Combine(WeasyPrintManager.FolderPath, "python.exe"); CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true
} };
p.Start();
WinziPrintProc = p;
evtHandler(); evtHandler();
} }
public static async Task Convert(string htmlPath, string pdfPath) { public static async Task<IEnumerable<int>> Convert(string htmlPath, string pdfPath, IProgress<double>? progress = null) {
var p = new Process() { StartInfo = new() { return await Convert(new string[] { htmlPath }, pdfPath, progress);
FileName = WeasyPrintPython, }
CreateNoWindow = true,
WorkingDirectory = WeasyPrintDir, public static async Task<IEnumerable<int>> Convert(IEnumerable<string> htmlPath, string pdfPath, IProgress<double>? progress = null) {
RedirectStandardError = true, if (WinziPrintProc == null) throw new InvalidOperationException("The WinziPrint process has not been initialized yet");
} }; progress?.Report(0.0);
p.StartInfo.EnvironmentVariables["PATH"] = "Scripts;gtk3;" + Environment.GetEnvironmentVariable("PATH"); await WinziPrintProc.StandardInput.WriteLineAsync($"{string.Join(';', htmlPath)};{pdfPath}");
p.StartInfo.ArgumentList.Add("scripts/weasyprint.exe"); while (true) {
p.StartInfo.ArgumentList.Add("-e"); var line = await WinziPrintProc.StandardOutput.ReadLineAsync() ?? throw new IOException("Invalid response from WinziPrint");
p.StartInfo.ArgumentList.Add("utf8"); if (line.StartsWith("error:")) {
p.StartInfo.ArgumentList.Add(htmlPath); MessageBox.Show(line[6..].Trim(), "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
p.StartInfo.ArgumentList.Add(pdfPath); return Array.Empty<int>();
p.Start(); } else if (line.StartsWith("progress:")) {
await p.WaitForExitAsync(); var parts = line[9..].Trim().Split('/').Select(int.Parse).ToArray();
var stderr = await p.StandardError.ReadToEndAsync(); progress?.Report(100.0 * parts[0] / parts[1]);
if (p.ExitCode != 0) throw new Exception(stderr); } else if (line.StartsWith("success:")) {
var m = Regex.Match(line, @"\(([0-9, ]+)\)");
return m.Groups[1].Value.Split(", ").Select(int.Parse);
}
}
} }
public static void Show(TempFile file, string title) { public static void Show(TempFile file, string title) {

View File

@ -106,3 +106,8 @@ table.delivery-confirmation-stats tbody th {
font-style: italic; font-style: italic;
text-align: left; text-align: left;
} }
table.delivery-confirmation-stats tr.subheading th {
font-weight: bold;
border-top: 0.5pt solid black;
}

View File

@ -7,7 +7,7 @@
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
<PreserveCompilationContext>true</PreserveCompilationContext> <PreserveCompilationContext>true</PreserveCompilationContext>
<ApplicationIcon>elwig.ico</ApplicationIcon> <ApplicationIcon>elwig.ico</ApplicationIcon>
<Version>0.4.0</Version> <Version>0.4.3</Version>
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages> <SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
</PropertyGroup> </PropertyGroup>
@ -16,7 +16,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Balbarak.WeasyPrint" Version="2.0.2" />
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.5.1" /> <PackageReference Include="Extended.Wpf.Toolkit" Version="4.5.1" />
<PackageReference Include="ini-parser" Version="2.5.2" /> <PackageReference Include="ini-parser" Version="2.5.2" />
<PackageReference Include="LinqKit" Version="1.2.4" /> <PackageReference Include="LinqKit" Version="1.2.4" />

View File

@ -215,17 +215,7 @@ namespace Elwig.Helpers {
cnx ??= await ConnectAsync(); cnx ??= await ConnectAsync();
var bins = new Dictionary<int, Dictionary<string, (int, int)>>(); var bins = new Dictionary<int, Dictionary<string, (int, int)>>();
using (var cmd = cnx.CreateCommand()) { using (var cmd = cnx.CreateCommand()) {
cmd.CommandText = $""" cmd.CommandText = $"SELECT mgnr, bin, min_kg, max_kg FROM v_area_commitment_bin WHERE year = {year}";
SELECT mgnr, t.vtrgid,
ROUND(SUM(COALESCE(area * min_kg_per_ha, 0)) / 10000.0) AS min_kg,
ROUND(SUM(COALESCE(area * max_kg_per_ha, 0)) / 10000.0) AS max_kg
FROM area_commitment c
JOIN area_commitment_type t ON t.vtrgid = c.vtrgid
WHERE (year_from IS NULL OR year_from <= {year}) AND
(year_to IS NULL OR year_to >= {year})
GROUP BY mgnr, t.vtrgid
ORDER BY LENGTH(t.vtrgid) DESC, t.vtrgid
""";
using var reader = await cmd.ExecuteReaderAsync(); using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync()) { while (await reader.ReadAsync()) {
var mgnr = reader.GetInt32(0); var mgnr = reader.GetInt32(0);
@ -305,7 +295,7 @@ namespace Elwig.Helpers {
var variety = await WineVarieties.FindAsync(id[..2]); var variety = await WineVarieties.FindAsync(id[..2]);
var attrIds = id[2..]; var attrIds = id[2..];
var attrs = await WineAttributes.Where(a => attrIds.Contains(a.AttrId)).ToListAsync(); var attrs = await WineAttributes.Where(a => attrIds.Contains(a.AttrId)).ToListAsync();
var name = (variety?.Name ?? "") + (attrs.Count > 0 ? $" ({string.Join(" / ", attrs.Select(a => a.Name))})" : ""); var name = (variety?.Name ?? "") + (attrIds == "_" ? " (kein Qual.Wein)" : attrs.Count > 0 ? $" ({string.Join(" / ", attrs.Select(a => a.Name))})" : "");
bins[id] = ( bins[id] = (
name, name,
rightsAndObligations.GetValueOrDefault(id).Item1, rightsAndObligations.GetValueOrDefault(id).Item1,

View File

@ -4,11 +4,11 @@ using System;
namespace Elwig.Helpers { namespace Elwig.Helpers {
public static class AppDbUpdater { public static class AppDbUpdater {
public static readonly int RequiredSchemaVersion = 3; public static readonly int RequiredSchemaVersion = 4;
private static int _versionOffset = 0; private static int _versionOffset = 0;
private static readonly Action<SqliteConnection>[] _updaters = new[] { private static readonly Action<SqliteConnection>[] _updaters = new[] {
UpdateDbSchema_1_To_2, UpdateDbSchema_2_To_3 UpdateDbSchema_1_To_2, UpdateDbSchema_2_To_3, UpdateDbSchema_3_To_4
}; };
private static void ExecuteNonQuery(SqliteConnection cnx, string sql) { private static void ExecuteNonQuery(SqliteConnection cnx, string sql) {
@ -184,5 +184,34 @@ namespace Elwig.Helpers {
ExecuteNonQuery(cnx, "ALTER TABLE wine_attribute ADD COLUMN fill_lower_bins INTEGER NOT NULL CHECK (fill_lower_bins IN (0, 1, 2)) DEFAULT 0"); ExecuteNonQuery(cnx, "ALTER TABLE wine_attribute ADD COLUMN fill_lower_bins INTEGER NOT NULL CHECK (fill_lower_bins IN (0, 1, 2)) DEFAULT 0");
} }
private static void UpdateDbSchema_3_To_4(SqliteConnection cnx) {
ExecuteNonQuery(cnx, "DROP VIEW v_payment_bin");
ExecuteNonQuery(cnx, """
CREATE VIEW v_payment_bin AS
SELECT d.year, d.mgnr,
sortid || discr AS bin,
SUM(value) AS weight
FROM v_delivery d
JOIN delivery_part_bin b ON (b.year, b.did, b.dpnr) = (d.year, d.did, d.dpnr)
GROUP BY d.year, d.mgnr, bin
HAVING SUM(value) > 0
ORDER BY d.year, d.mgnr, LENGTH(bin) DESC, bin;
""");
ExecuteNonQuery(cnx, """
CREATE VIEW v_area_commitment_bin AS
SELECT s.year, c.mgnr,
c.vtrgid AS bin,
CAST(ROUND(SUM(COALESCE(area * min_kg_per_ha, 0)) / 10000.0, 0) AS INTEGER) AS min_kg,
CAST(ROUND(SUM(COALESCE(area * max_kg_per_ha, 0)) / 10000.0, 0) AS INTEGER) AS max_kg
FROM area_commitment c, season s
JOIN area_commitment_type t ON t.vtrgid = c.vtrgid
WHERE (year_from IS NULL OR year_from <= s.year) AND
(year_to IS NULL OR year_to >= s.year)
GROUP BY s.year, c.mgnr, c.vtrgid
ORDER BY s.year, c.mgnr, LENGTH(c.vtrgid) DESC, c.vtrgid;
""");
}
} }
} }

View File

@ -55,7 +55,7 @@ namespace Elwig.Helpers.Billing {
COALESCE(LENGTH(attributes), 0) ASC, attribute_prio DESC, COALESCE(attributes, '~'), COALESCE(LENGTH(attributes), 0) ASC, attribute_prio DESC, COALESCE(attributes, '~'),
kmw DESC, lsnr, dpnr kmw DESC, lsnr, dpnr
"""; """;
var reader = await cmd.ExecuteReaderAsync(); using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync()) { while (await reader.ReadAsync()) {
deliveries.Add(( deliveries.Add((
reader.GetInt32(0), reader.GetInt32(1), reader.GetInt32(2), reader.GetString(3), reader.GetInt32(4), reader.GetInt32(0), reader.GetInt32(1), reader.GetInt32(2), reader.GetString(3), reader.GetInt32(4),
@ -125,7 +125,92 @@ namespace Elwig.Helpers.Billing {
await cmd.ExecuteNonQueryAsync(); await cmd.ExecuteNonQueryAsync();
} }
// TODO add second round to avoid under deliveries if (!avoidUnderDeliveries)
return;
var underDeliveries = new Dictionary<(int, string), int>();
using (var cmd = cnx.CreateCommand()) {
cmd.CommandText = $"""
SELECT c.mgnr, c.bin, COALESCE(p.weight, 0) - c.min_kg AS diff
FROM v_area_commitment_bin c
LEFT JOIN v_payment_bin p ON (p.year, p.mgnr, p.bin) = (c.year, c.mgnr, c.bin)
WHERE c.year = {Year} AND LENGTH(c.bin) = 2 AND diff < 0
""";
using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync()) {
underDeliveries[(reader.GetInt32(0), reader.GetString(1))] = reader.GetInt32(2);
}
}
var fittingBins = new Dictionary<(int, string), int>();
using (var cmd = cnx.CreateCommand()) {
cmd.CommandText = $"""
SELECT c.mgnr, c.bin, COALESCE(p.weight, 0) - c.min_kg AS diff
FROM v_area_commitment_bin c
LEFT JOIN v_payment_bin p ON (p.year, p.mgnr, p.bin) = (c.year, c.mgnr, c.bin)
WHERE c.year = {Year} AND LENGTH(c.bin) = 3 AND diff > 0
""";
using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync()) {
fittingBins[(reader.GetInt32(0), reader.GetString(1))] = reader.GetInt32(2);
}
}
foreach (var item in fittingBins) {
var mgnr = item.Key.Item1;
var id = item.Key.Item2[..2];
var attr = item.Key.Item2[2..];
var available = item.Value;
var needed = -underDeliveries.GetValueOrDefault((mgnr, id), 0);
if (needed <= 0) continue;
var fittingDeliveries = new List<(int, int, int, int)>();
using (var cmd = cnx.CreateCommand()) {
cmd.CommandText = $"""
SELECT d.did, d.dpnr, d.weight, b.value
FROM v_delivery d
JOIN delivery_part_bin b ON (b.year, b.did, b.dpnr) = (d.year, d.did, d.dpnr) AND b.discr = '{attr}'
WHERE d.year = {Year} AND mgnr = {mgnr} AND sortid = '{id}' AND attributes = '{attr}'
ORDER BY kmw ASC, lsnr DESC, d.dpnr DESC
""";
using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync()) {
fittingDeliveries.Add((
reader.GetInt32(0), reader.GetInt32(1), reader.GetInt32(2), reader.GetInt32(3)
));
}
}
var negChanges = new List<(int, int, int, int)>();
var posChanges = new List<(int, int, int, int)>();
foreach (var (did, dpnr, _, w) in fittingDeliveries) {
int v = Math.Min(needed, w);
needed -= v;
posChanges.Add((did, dpnr, 1, v));
negChanges.Add((did, dpnr, 2, w - v));
if (needed == 0) break;
}
using (var cmd = cnx.CreateCommand()) {
cmd.CommandText = $"""
INSERT INTO delivery_part_bin (year, did, dpnr, binnr, discr, value)
VALUES {string.Join(",\n ", posChanges.Select(i => $"({Year}, {i.Item1}, {i.Item2}, {i.Item3}, '', {i.Item4})"))}
ON CONFLICT DO UPDATE
SET value = value + excluded.value
""";
await cmd.ExecuteNonQueryAsync();
}
using (var cmd = cnx.CreateCommand()) {
cmd.CommandText = $"""
INSERT INTO delivery_part_bin (year, did, dpnr, binnr, discr, value)
VALUES {string.Join(",\n ", negChanges.Select(i => $"({Year}, {i.Item1}, {i.Item2}, {i.Item3}, '', {i.Item4})"))}
ON CONFLICT DO UPDATE
SET value = excluded.value
""";
await cmd.ExecuteNonQueryAsync();
}
}
} }
} }
} }

View File

@ -5,6 +5,7 @@ namespace Elwig.Helpers {
public sealed class TempFile : IDisposable { public sealed class TempFile : IDisposable {
private int Usages = 0; private int Usages = 0;
public string FilePath { get; private set; } public string FilePath { get; private set; }
public string FileName => Path.GetFileName(FilePath);
public TempFile() : this(null) { } public TempFile() : this(null) { }

View File

@ -6,7 +6,7 @@
xmlns:local="clr-namespace:Elwig.Windows" xmlns:local="clr-namespace:Elwig.Windows"
mc:Ignorable="d" mc:Ignorable="d"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
Title="Flächenbindungen - Elwig" Height="480" Width="850" Title="Flächenbindungen - Elwig" Height="480" Width="1100"
Loaded="Window_Loaded"> Loaded="Window_Loaded">
<Window.Resources> <Window.Resources>
<Style TargetType="Label"> <Style TargetType="Label">
@ -52,7 +52,7 @@
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
<ColumnDefinition Width="330"/> <ColumnDefinition Width="340"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Menu Grid.ColumnSpan="2" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White"> <Menu Grid.ColumnSpan="2" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White">
@ -74,13 +74,15 @@
<DataGridTextColumn Header="Katastralgemeinde" Binding="{Binding Kg.AtKg.Name}" Width="6*"/> <DataGridTextColumn Header="Katastralgemeinde" Binding="{Binding Kg.AtKg.Name}" Width="6*"/>
<DataGridTextColumn Header="Ried" Binding="{Binding Rd.Name}" Width="4*"/> <DataGridTextColumn Header="Ried" Binding="{Binding Rd.Name}" Width="4*"/>
<DataGridTextColumn Header="Parzelle" Binding="{Binding GstNr}" Width="4*"/> <DataGridTextColumn Header="Parzelle" Binding="{Binding GstNr}" Width="4*"/>
<DataGridTextColumn Header="Fläche" Binding="{Binding Area, StringFormat='{}{0:N0} m²'}" Width="4*"> <DataGridTextColumn Header="Fläche" Binding="{Binding Area, StringFormat='{}{0:N0} m²'}" Width="3*">
<DataGridTextColumn.CellStyle> <DataGridTextColumn.CellStyle>
<Style> <Style>
<Setter Property="TextBlock.TextAlignment" Value="Right"/> <Setter Property="TextBlock.TextAlignment" Value="Right"/>
</Style> </Style>
</DataGridTextColumn.CellStyle> </DataGridTextColumn.CellStyle>
</DataGridTextColumn> </DataGridTextColumn>
<DataGridTextColumn Header="Sorte" Binding="{Binding AreaComType.WineVar.Name}" Width="4*"/>
<DataGridTextColumn Header="Attribut" Binding="{Binding AreaComType.WineAttr1.Name}" Width="3*"/>
</DataGrid.Columns> </DataGrid.Columns>
</DataGrid> </DataGrid>

View File

@ -318,7 +318,7 @@ namespace Elwig.Windows {
var filter = TextFilter.ToList(); var filter = TextFilter.ToList();
if (filter.Count > 0) { if (filter.Count > 0) {
var var = await Context.WineVarieties.ToDictionaryAsync(v => v.SortId, v => v); var var = await Context.WineVarieties.ToDictionaryAsync(v => v.SortId, v => v);
var qual = await Context.WineQualityLevels.ToDictionaryAsync(q => q.QualId, q => q); var qual = await Context.WineQualityLevels.Where(q => !q.IsPredicate).ToDictionaryAsync(q => q.QualId, q => q);
var mgnr = await Context.Members.ToDictionaryAsync(m => m.MgNr.ToString(), m => m); var mgnr = await Context.Members.ToDictionaryAsync(m => m.MgNr.ToString(), m => m);
var zwst = await Context.Branches.ToDictionaryAsync(b => b.Name.ToLower().Split(" ")[0], b => b); var zwst = await Context.Branches.ToDictionaryAsync(b => b.Name.ToLower().Split(" ")[0], b => b);
var attr = await Context.WineAttributes.ToDictionaryAsync(a => a.Name.ToLower().Split(" ")[0], a => a); var attr = await Context.WineAttributes.ToDictionaryAsync(a => a.Name.ToLower().Split(" ")[0], a => a);

View File

@ -1,14 +1,14 @@
<local:ContextWindow x:Class="Elwig.Dialogs.DeliveryConfirmationsDialog" <local:ContextWindow x:Class="Elwig.Dialogs.DeliveryConfirmationsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Elwig.Windows" xmlns:local="clr-namespace:Elwig.Windows"
mc:Ignorable="d" mc:Ignorable="d"
ResizeMode="NoResize" Loaded="Window_Loaded"
Title="Anlieferungsbestätingungen - Elwig" Height="400" Width="600"> Title="Anlieferungsbestätingungen - Elwig" Height="500" Width="800" MinHeight="400" MinWidth="600">
<Grid> <Grid>
<GroupBox Header="Sortieren nach" Margin="10,10,10,10" Width="150" Height="80" VerticalAlignment="Top" HorizontalAlignment="Left"> <GroupBox Header="Sortieren nach" Margin="10,10,10,10" Width="180" Height="80" VerticalAlignment="Top" HorizontalAlignment="Left">
<StackPanel Margin="5,5,0,5"> <StackPanel Margin="5,5,0,5">
<RadioButton GroupName="Order" x:Name="OrderMgNrInput" Content="Mitgliedsnummer" IsChecked="True"/> <RadioButton GroupName="Order" x:Name="OrderMgNrInput" Content="Mitgliedsnummer" IsChecked="True"/>
<RadioButton GroupName="Order" x:Name="OrderNameInput" Content="Name"/> <RadioButton GroupName="Order" x:Name="OrderNameInput" Content="Name"/>
@ -16,16 +16,22 @@
</StackPanel> </StackPanel>
</GroupBox> </GroupBox>
<TextBox x:Name="TextElement" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible" AcceptsReturn="True" <CheckBox x:Name="AllMembersInput" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="10,100,10,10">
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="170,10,10,10" Height="Auto"/> <TextBlock>Auch Mitglieder ohne<LineBreak/>Lieferungen miteinbeziehen</TextBlock>
</CheckBox>
<Button x:Name="TestButton" Content="Stichprobe" FontSize="14" Width="150" Margin="10,10,10,74" Height="27" <TextBox x:Name="TextElement" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible" AcceptsReturn="True"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="200,10,10,10" Height="Auto"/>
<ProgressBar x:Name="ProgressBar" Margin="10,10,10,106" Height="27" Width="180"
VerticalAlignment="Bottom" HorizontalAlignment="Left"/>
<Button x:Name="TestButton" Content="Stichprobe" FontSize="14" Width="180" Margin="10,10,10,74" Height="27" Tag="Print" IsEnabled="False"
Click="TestButton_Click" Click="TestButton_Click"
VerticalAlignment="Bottom" HorizontalAlignment="Left"/> VerticalAlignment="Bottom" HorizontalAlignment="Left"/>
<Button x:Name="ShowButton" Content="Vorschau" FontSize="14" Width="150" Margin="10,10,10,42" Height="27" <Button x:Name="ShowButton" Content="Vorschau" FontSize="14" Width="180" Margin="10,10,10,42" Height="27" Tag="Print" IsEnabled="False"
Click="ShowButton_Click" Click="ShowButton_Click"
VerticalAlignment="Bottom" HorizontalAlignment="Left"/> VerticalAlignment="Bottom" HorizontalAlignment="Left"/>
<Button x:Name="PrintButton" Content="Drucken" FontSize="14" Width="150" Margin="10,10,10,10" Height="27" <Button x:Name="PrintButton" Content="Drucken" FontSize="14" Width="180" Margin="10,10,10,10" Height="27" Tag="Print" IsEnabled="False"
Click="PrintButton_Click" Click="PrintButton_Click"
VerticalAlignment="Bottom" HorizontalAlignment="Left"/> VerticalAlignment="Bottom" HorizontalAlignment="Left"/>
</Grid> </Grid>

View File

@ -10,20 +10,28 @@ using System.Windows;
using System.Windows.Input; using System.Windows.Input;
namespace Elwig.Dialogs { namespace Elwig.Dialogs {
public partial class DeliveryConfirmationsDialog : ContextWindow { public partial class DeliveryConfirmationsWindow : ContextWindow {
public readonly int Year; public readonly int Year;
public DeliveryConfirmationsDialog(int year) { public DeliveryConfirmationsWindow(int year) {
InitializeComponent(); InitializeComponent();
Year = year; Year = year;
Title = $"Anlieferungsbestätigungen - Lese {Year} - Elwig"; Title = $"Anlieferungsbestätigungen - Lese {Year} - Elwig";
TextElement.Text = App.Client.TextDeliveryConfirmation; TextElement.Text = App.Client.TextDeliveryConfirmation;
if (!App.Config.Debug) { if (!App.Config.Debug) {
TestButton.Visibility = Visibility.Hidden; TestButton.Visibility = Visibility.Hidden;
var m = ProgressBar.Margin;
ProgressBar.Margin = new(m.Left, m.Top, m.Right, m.Bottom - 32);
} }
} }
private void Window_Loaded(object sender, RoutedEventArgs evt) {
TestButton.IsEnabled = App.IsPrintingReady;
ShowButton.IsEnabled = App.IsPrintingReady;
PrintButton.IsEnabled = App.IsPrintingReady;
}
protected override async Task OnRenewContext() { } protected override async Task OnRenewContext() { }
private async Task UpdateTextParameter() { private async Task UpdateTextParameter() {
@ -40,13 +48,19 @@ namespace Elwig.Dialogs {
Mouse.OverrideCursor = Cursors.AppStarting; Mouse.OverrideCursor = Cursors.AppStarting;
await UpdateTextParameter(); await UpdateTextParameter();
var members = Context.Members.FromSqlRaw($""" IQueryable<Member> members;
SELECT m.* if (AllMembersInput.IsChecked == true) {
FROM member m members = Context.Members.Where(m => m.IsActive);
INNER JOIN delivery d ON d.mgnr = m.mgnr } else {
WHERE d.year = {Year} members = Context.Members.FromSqlRaw($"""
GROUP BY m.mgnr SELECT m.*
"""); FROM member m
INNER JOIN delivery d ON d.mgnr = m.mgnr
WHERE d.year = {Year}
GROUP BY m.mgnr
""");
}
if (OrderMgNrInput.IsChecked == true) { if (OrderMgNrInput.IsChecked == true) {
members = members members = members
.OrderBy(m => m.MgNr); .OrderBy(m => m.MgNr);
@ -70,8 +84,21 @@ namespace Elwig.Dialogs {
list = list.Where((_, n) => n % 10 == r); list = list.Where((_, n) => n % 10 == r);
} }
using var doc = await Document.Merge(list.Select(m => new DeliveryConfirmation(Context, Year, m))); ; var deliveries = await Context.DeliveryParts.FromSqlRaw($"""
await doc.Generate(); SELECT p.*
FROM v_delivery v
JOIN delivery_part p ON (p.year, p.did, p.dpnr) = (v.year, v.did, v.dpnr)
WHERE v.year = {Year}
ORDER BY v.sortid, v.abgewertet ASC,
COALESCE(LENGTH(v.attributes), 0) ASC, attribute_prio DESC, COALESCE(v.attributes, '~'),
v.kmw DESC, v.lsnr, v.dpnr
""")
.ToListAsync();
using var doc = Document.Merge(list.Select(m => new DeliveryConfirmation(Context, Year, m, deliveries.Where(d => d.Delivery.MgNr == m.MgNr).ToList()))); ;
await doc.Generate(new Progress<double>(v => {
ProgressBar.Value = v;
}));
Mouse.OverrideCursor = null; Mouse.OverrideCursor = null;
if (mode < 2) { if (mode < 2) {

View File

@ -317,7 +317,7 @@ namespace Elwig.Windows {
.ThenBy(m => m.MgNr); .ThenBy(m => m.MgNr);
break; break;
} }
using var doc = await Document.Merge((await members.ToListAsync()).Select(m => new Letterhead(m))); using var doc = Document.Merge((await members.ToListAsync()).Select(m => new Letterhead(m)));
await doc.Generate(); await doc.Generate();
Mouse.OverrideCursor = null; Mouse.OverrideCursor = null;
if (App.Config.Debug) { if (App.Config.Debug) {

View File

@ -29,7 +29,7 @@
Margin="50,80,0,0"/> Margin="50,80,0,0"/>
<CheckBox x:Name="AllowAttrIntoLowerBinsInput" Content="Erlauben Lieferungen auch auf (konfigurierte) &quot;schlechtere&quot; Flächenbindungen aufzuteilen" IsChecked="True" <CheckBox x:Name="AllowAttrIntoLowerBinsInput" Content="Erlauben Lieferungen auch auf (konfigurierte) &quot;schlechtere&quot; Flächenbindungen aufzuteilen" IsChecked="True"
VerticalAlignment="Top" HorizontalAlignment="Left" Margin="255,68,0,0"/> VerticalAlignment="Top" HorizontalAlignment="Left" Margin="255,68,0,0"/>
<CheckBox x:Name="AvoidUnderDeliveriesInput" Content="Unterlieferungen durch Abzug bei &quot;besseren&quot; Flächenbindungen vermeiden" IsEnabled="False" <CheckBox x:Name="AvoidUnderDeliveriesInput" Content="Unterlieferungen durch Abzug bei &quot;besseren&quot; Flächenbindungen vermeiden" IsChecked="True"
VerticalAlignment="Top" HorizontalAlignment="Left" Margin="255,88,0,0"/> VerticalAlignment="Top" HorizontalAlignment="Left" Margin="255,88,0,0"/>
<CheckBox x:Name="HonorGebundenInput" Margin="255,108,0,0" VerticalAlignment="Top"> <CheckBox x:Name="HonorGebundenInput" Margin="255,108,0,0" VerticalAlignment="Top">
<TextBlock>Bei Lieferungen das Feld <Italic>Gebunden</Italic> berücksichtigen</TextBlock> <TextBlock>Bei Lieferungen das Feld <Italic>Gebunden</Italic> berücksichtigen</TextBlock>

View File

@ -1,6 +1,10 @@
using Elwig.Dialogs; using Elwig.Dialogs;
using Elwig.Helpers; using Elwig.Helpers;
using Elwig.Helpers.Billing; using Elwig.Helpers.Billing;
using Microsoft.Win32;
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Input; using System.Windows.Input;
@ -47,11 +51,100 @@ namespace Elwig.Windows {
private void DeliveryConfirmationButton_Click(object sender, RoutedEventArgs evt) { private void DeliveryConfirmationButton_Click(object sender, RoutedEventArgs evt) {
if (SeasonInput.Value is not int year) if (SeasonInput.Value is not int year)
return; return;
var d = new DeliveryConfirmationsDialog(year); var w = new DeliveryConfirmationsWindow(year);
d.Show(); w.Show();
} }
private void OverUnderDeliveryButton_Click(object sender, RoutedEventArgs evt) { private async void OverUnderDeliveryButton_Click(object sender, RoutedEventArgs evt) {
if (SeasonInput.Value is not int year)
return;
var d = new SaveFileDialog() {
FileName = $"Über-Unterlieferungen-{year}.csv",
DefaultExt = "csv",
Filter = "CSV-Datei (*.csv)|*.csv",
Title = $"Über-/Unterlieferungen {year} speichern unter - Elwig"
};
if (d.ShowDialog() == false)
return;
Mouse.OverrideCursor = Cursors.AppStarting;
try {
using var file = new StreamWriter(d.FileName, false, Encoding.Latin1);
using var cnx = await AppDbContext.ConnectAsync();
await file.WriteLineAsync($"Auswertungen {year};;;;;;;;;;;");
await file.WriteLineAsync($";;;;;;;;;;;");
await file.WriteLineAsync($"Über-/Unterlieferungen lt. gez. GA;;;;;;;;;;;");
await file.WriteLineAsync($"MgNr;Name;Vorname;Adresse;PLZ;Ort;GA;Lieferpflicht;Lieferrecht;Geliefert;Über-/Unterliefert;Prozent");
using (var cmd = cnx.CreateCommand()) {
cmd.CommandText = $"""
SELECT m.mgnr, m.family_name, m.given_name, p.plz, o.name, m.address, m.business_shares,
m.business_shares * (SELECT value FROM client_parameter WHERE param = 'DELIVERY_OBLIGATION') AS min_kg,
m.business_shares * (SELECT value FROM client_parameter WHERE param = 'DELIVERY_RIGHT') AS max_kg,
COALESCE(SUM(d.weight), 0) AS sum
FROM member m
LEFT JOIN AT_plz_dest p ON p.id = m.postal_dest
LEFT JOIN AT_ort o ON o.okz = p.okz
LEFT JOIN v_delivery d ON d.mgnr = m.mgnr AND d.year = {year}
WHERE m.active = 1
GROUP BY d.year, m.mgnr
ORDER BY sum = 0 DESC, 100.0 * sum / max_kg, m.mgnr;
""";
using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync()) {
var mgnr = reader.GetInt32(0);
var familyName = reader.GetString(1);
var givenName = reader.GetString(2);
var plz = reader.GetInt32(3);
var ort = reader.GetString(4);
var addr = reader.GetString(5);
var ga = reader.GetInt32(6);
var minKg = reader.GetInt32(7);
var maxKg = reader.GetInt32(8);
var sum = reader.GetInt32(9);
var s1 = sum < minKg ? $"{sum - minKg}" : sum > maxKg ? $"{sum - maxKg}" : "";
var s2 = sum < minKg ? $"{sum * 100.0 / minKg - 100.0:0.0}" : sum > maxKg ? $"{sum * 100.0 / maxKg - 100:0.0}" : "";
await file.WriteLineAsync($"{mgnr};{familyName};{givenName};{addr};{plz};{ort};{ga};{minKg};{maxKg};{sum};{s1};{s2}");
}
}
await file.WriteLineAsync($";;;;;;;;;;;");
await file.WriteLineAsync($"Unterlieferungen lt. Flächenbindungen;;;;;;;;;;;");
await file.WriteLineAsync($"MgNr;Name;Vorname;Adresse;PLZ;Ort;Vertrag;Lieferpflicht;Lieferrecht;Geliefert;Unterliefert;Prozent");
using (var cmd = cnx.CreateCommand()) {
cmd.CommandText = $"""
SELECT m.mgnr, m.family_name, m.given_name, p.plz, o.name, m.address,
c.bin, c.min_kg, c.max_kg, b.weight
FROM member m
LEFT JOIN AT_plz_dest p ON p.id = m.postal_dest
LEFT JOIN AT_ort o ON o.okz = p.okz
JOIN v_area_commitment_bin c ON c.mgnr = m.mgnr AND c.year = {year}
LEFT JOIN v_payment_bin b ON (b.mgnr, b.bin) = (m.mgnr, c.bin) AND b.year = {year}
WHERE m.active = 1 AND b.weight < c.min_kg
ORDER BY m.mgnr, c.bin
""";
using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync()) {
var mgnr = reader.GetInt32(0);
var familyName = reader.GetString(1);
var givenName = reader.GetString(2);
var plz = reader.GetInt32(3);
var ort = reader.GetString(4);
var addr = reader.GetString(5);
var id = reader.GetString(6);
var minKg = reader.GetInt32(7);
var maxKg = reader.GetInt32(8);
var sum = reader.GetInt32(9);
await file.WriteLineAsync($"{mgnr};{familyName};{givenName};{addr};{plz};{ort};{id};{minKg};{maxKg};{sum};{sum - minKg};{sum * 100.0 / minKg - 100.0:0.0}");
}
}
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
} }
private void PaymentButton_Click(object sender, RoutedEventArgs evt) { private void PaymentButton_Click(object sender, RoutedEventArgs evt) {

View File

@ -3,3 +3,4 @@
mkdir "C:\ProgramData\Elwig\resources" mkdir "C:\ProgramData\Elwig\resources"
copy /b /y Documents\*.css "C:\ProgramData\Elwig\resources" copy /b /y Documents\*.css "C:\ProgramData\Elwig\resources"
copy /b /y Documents\*.cshtml "C:\ProgramData\Elwig\resources" copy /b /y Documents\*.cshtml "C:\ProgramData\Elwig\resources"
::copy /b /y ..\Installer\Files\*.exe "C:\Program Files\Elwig\"

Binary file not shown.

View File

@ -7,9 +7,12 @@
<Component Directory="ConfigFolder" Permanent="true" NeverOverwrite="true"> <Component Directory="ConfigFolder" Permanent="true" NeverOverwrite="true">
<File Source="$(ProjectDir)\Files\config.ini" Id="config.ini"/> <File Source="$(ProjectDir)\Files\config.ini" Id="config.ini"/>
</Component> </Component>
<Component Directory="InstallFolder"> <Component Directory="InstallFolder">
<File Source="$(TargetDir)\PDFtoPrinter.exe" Id="PDFtoPrinter.exe"/> <File Source="$(ProjectDir)\Files\WinziPrint.exe" Id="WinziPrint.exe"/>
</Component> </Component>
<Component Directory="InstallFolder">
<File Source="$(TargetDir)\PDFtoPrinter.exe" Id="PDFtoPrinter.exe"/>
</Component>
</ComponentGroup> </ComponentGroup>
</Fragment> </Fragment>
</Wix> </Wix>