Compare commits
30 Commits
Author | SHA1 | Date | |
---|---|---|---|
e8bd81ccc7 | |||
f26d55af68 | |||
e46ddb9cdc | |||
3cf3ca71d6 | |||
1d4ab7b264 | |||
841f8bfb84 | |||
a075189bdd | |||
e52475a4bf | |||
8ce4911317 | |||
c190ce1474 | |||
f19de3ae6e | |||
e56e209506 | |||
28fb4f6fa2 | |||
a832879b73 | |||
a2f49e1b8b | |||
99c3474fb6 | |||
9924795888 | |||
733ab0d208 | |||
6e2ba56a7c | |||
2507a2695f | |||
ddd83bf63d | |||
685793f0fb | |||
f6712704ee | |||
de500854c4 | |||
d992b6a206 | |||
d2b96736bb | |||
50b9f4e207 | |||
45fc0893b1 | |||
c4dd56075d | |||
f1084c716a |
@ -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;
|
||||||
|
@ -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>
|
||||||
|
@ -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)
|
||||||
|
@ -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>
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
@ -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" />
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
""");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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) { }
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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>
|
@ -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) {
|
@ -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) {
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
Margin="50,80,0,0"/>
|
Margin="50,80,0,0"/>
|
||||||
<CheckBox x:Name="AllowAttrIntoLowerBinsInput" Content="Erlauben Lieferungen auch auf (konfigurierte) "schlechtere" Flächenbindungen aufzuteilen" IsChecked="True"
|
<CheckBox x:Name="AllowAttrIntoLowerBinsInput" Content="Erlauben Lieferungen auch auf (konfigurierte) "schlechtere" 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 "besseren" Flächenbindungen vermeiden" IsEnabled="False"
|
<CheckBox x:Name="AvoidUnderDeliveriesInput" Content="Unterlieferungen durch Abzug bei "besseren" 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>
|
||||||
|
@ -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) {
|
||||||
|
@ -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\"
|
||||||
|
BIN
Installer/Files/WinziPrint.exe
Normal file
BIN
Installer/Files/WinziPrint.exe
Normal file
Binary file not shown.
@ -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>
|
||||||
|
Reference in New Issue
Block a user