Compare commits

...

22 Commits

Author SHA1 Message Date
lorenz.stechauner b9a2893d80 [#17] CreditNote: Overhaul CreditNote 2023-12-22 15:34:03 +01:00
lorenz.stechauner 82f93746ab MemberDataSheet: Fix typo 2023-12-22 10:11:35 +01:00
lorenz.stechauner b79fcfb1ed MemberDataSheet: Make more compact 2023-12-22 00:56:57 +01:00
lorenz.stechauner ba241c98a9 MemberDataSheet: Make Bankverbindung section smaller 2023-12-22 00:15:42 +01:00
lorenz.stechauner f7cdd7a4c1 MemberDataSheet: Minor improvements 2023-12-21 23:24:14 +01:00
lorenz.stechauner 1bdb7183ed [#5][#17][#23] MemberDataSheet: Overhaul entire MemberDataSheet 2023-12-21 22:24:04 +01:00
lorenz.stechauner 4d5ad13e0c Utils: Add PhoneNrTypes 2023-12-21 22:21:38 +01:00
lorenz.stechauner 69e6b6a713 Member: Use current calendar year to retrieve ActiveAreaCommitments 2023-12-21 22:20:24 +01:00
lorenz.stechauner 781077e5e3 [#25] Documents: Fix page breaking 2023-12-20 01:39:26 +01:00
lorenz.stechauner 16d429e9e4 DeliveryConfirmation: Keep last two tables together on page 2023-12-19 22:04:34 +01:00
lorenz.stechauner 18600a44da Document: Add mailto: and http: hrefs to footer 2023-12-19 20:48:01 +01:00
lorenz.stechauner 9c46974bd7 [#23] DeliveryConfirmation: Add Sortenaufteilung table 2023-12-19 20:48:01 +01:00
lorenz.stechauner 480f99234c AppDbContext: Remove support for multiple attributes in buckets 2023-12-19 19:31:35 +01:00
lorenz.stechauner 8811ca25ce [#17][#23] BusinessDocument: Remove enum RowMode 2023-12-19 18:11:02 +01:00
lorenz.stechauner 161bf31a62 [#17][#23] Documents: Unify table stylings 2023-12-19 18:10:31 +01:00
lorenz.stechauner ae00fd2c31 [#22] DeliveryConfirmation: Fix wrong border 2023-12-18 21:01:15 +01:00
lorenz.stechauner 2c48c89cfa Installer/config.ini: Comment out branch 2023-12-18 12:20:05 +01:00
lorenz.stechauner de5e62de50 MemberAdminWindow: Rename ...Memberdata... to ...MemberDataSheet... 2023-12-02 17:30:04 +01:00
lorenz.stechauner 0eed426559 [#21] AdministrationWindow: Temporarily fix UnitTextBox/TextBox casting error 2023-12-02 14:00:22 +01:00
lorenz.stechauner 03a9a3793a Config: Use Microsoft's INI implementation instead of a 3rd party's 2023-12-02 13:04:50 +01:00
lorenz.stechauner 7528764ff3 Update project to .NET 8 2023-12-02 12:28:16 +01:00
lorenz.stechauner 3576a066fe AppDbContext: Use records instead of unnamed tuples for buckets 2023-11-30 17:29:01 +01:00
36 changed files with 890 additions and 948 deletions
+146
View File
@@ -1,5 +1,7 @@
using Elwig.Helpers; using Elwig.Helpers;
using Elwig.Models.Entities; using Elwig.Models.Entities;
using System.Collections.Generic;
using System.Linq;
namespace Elwig.Documents { namespace Elwig.Documents {
public abstract class BusinessDocument : Document { public abstract class BusinessDocument : Document {
@@ -32,5 +34,149 @@ namespace Elwig.Documents {
return (addr is BillingAddr ? $"{addr.Name}\n" : "") + $"{Member.AdministrativeName}\n{addr.Address}\n{plz?.Plz} {plz?.Ort.Name.Split(",")[0]}\n{addr.PostalDest.Country.Name}"; return (addr is BillingAddr ? $"{addr.Name}\n" : "") + $"{Member.AdministrativeName}\n{addr.Address}\n{plz?.Plz} {plz?.Ort.Name.Split(",")[0]}\n{addr.PostalDest.Country.Name}";
} }
} }
private static string GetColGroup(IEnumerable<double> cols) {
return "<colgroup>\n" + string.Join("\n", cols.Select(g => $"<col style=\"width: {g.ToString().Replace(',', '.')}mm;\"/>")) + "\n</colgroup>\n";
}
public static string PrintSortenaufteilung(Dictionary<string, MemberBucket> buckets) {
List<string> attributes = ["_", ""];
List<string> names = ["kein Qual.Wein", "ohne Attribut"];
List<(string, string)> bucketAttrs = [
.. buckets
.Where(b => b.Key.Length > 2 && b.Key[2] != '_' && b.Value.DeliveryStrict > 0)
.Select(b => (b.Key[2..], b.Value.Name.Split("(")[1][..^1]))
.Distinct()
.OrderBy(v => v.Item1)
];
names.AddRange(bucketAttrs.Select(b => b.Item2));
names.Add("Gesamt");
attributes.AddRange(bucketAttrs.Select(b => b.Item1));
List<double> cols = [40];
cols.AddRange(names.Select(_ => 125.0 / names.Count));
string tbl = GetColGroup(cols);
tbl += "<thead><tr>" +
$"<th><b>Sortenaufteilung</b> [kg]</th>" +
string.Join("", names.Select(c => $"<th>{c}</th>")) +
"</tr></thead>";
tbl += string.Join("\n", buckets
.GroupBy(b => (b.Key[..2], b.Value.Name.Split("(")[0].Trim()))
.Where(g => g.Sum(a => a.Value.DeliveryStrict) > 0)
.OrderBy(g => g.Key.Item1)
.Select(g => {
var dict = g.ToDictionary(a => a.Key[2..], a => a.Value);
var vals = attributes.Select(a => dict.TryGetValue(a, out MemberBucket value) ? value.DeliveryStrict : 0).ToList();
return $"<tr><th>{g.Key.Item2}</th>" + string.Join("", vals.Select(v => "<td class=\"number\">" + (v == 0 ? "-" : $"{v:N0}") + "</td>")) +
$"<td class=\"number\">{dict.Values.Select(v => v.DeliveryStrict).Sum():N0}</td></tr>";
})
);
var totalDict = buckets.GroupBy(b => b.Key[2..]).ToDictionary(g => g.Key, g => g.Sum(a => a.Value.DeliveryStrict));
var totals = attributes.Select(a => totalDict.TryGetValue(a, out int value) ? value : 0);
tbl += "<tr class=\"sum\"><td></td>" + string.Join("", totals.Select(v => $"<td class=\"number\">{v:N0}</td>")) +
$"<td class=\"number\">{totalDict.Values.Sum():N0}</td></tr>";
return "<table class=\"sortenaufteilung small number cohere\">" + tbl + "</table>";
}
private static string FormatRow(
int obligation, int right, int delivery, int? payment = null, int? area = null,
bool isGa = false, bool showPayment = false, bool showArea = false
) {
payment ??= delivery;
var baseline = showPayment ? payment : delivery;
if (showArea) {
return $"<td>{(area == null ? "" : $"{area:N0}")}</td>" +
$"<td>{obligation:N0}</td>" +
$"<td>{right:N0}</td>";
}
return $"<td>{(obligation == 0 ? "-" : $"{obligation:N0}")}</td>" +
$"<td>{(right == 0 ? "-" : $"{right:N0}")}</td>" +
$"<td>{(baseline < obligation ? $"<b>{obligation - baseline:N0}</b>" : "-")}</td>" +
$"<td>{(baseline >= obligation && delivery <= right ? $"{right - delivery:N0}" : "-")}</td>" +
$"<td>{(obligation == 0 && right == 0 ? "-" : (delivery > right ? ((isGa ? "<b>" : "") + $"{delivery - right:N0}" + (isGa ? "</b>" : "")) : "-"))}</td>" +
(showPayment ? $"<td>{(isGa ? "" : obligation == 0 && right == 0 ? "-" : $"{payment:N0}")}</td>" : "") +
$"<td>{delivery:N0}</td>";
}
private static string FormatRow(MemberBucket bucket, bool isGa = false, bool showPayment = false, bool showArea = false) {
return FormatRow(bucket.Obligation, bucket.Right, bucket.Delivery, bucket.Payment, bucket.Area, isGa, showPayment, showArea);
}
public string PrintBucketTable(
Season season, Dictionary<string, MemberBucket> buckets,
bool includeDelivery = true, bool includePayment = false,
bool isTiny = false, IEnumerable<string>? filter = null
) {
includePayment = includePayment && includeDelivery;
string tbl = GetColGroup(!includeDelivery ? [105, 20, 20, 20] : includePayment ? [45, 17, 17, 17, 19, 16, 17, 17] : [45, 20, 20, 20, 20, 20, 20]);
tbl += $"""
<thead>
<tr>
<th{(!includeDelivery ? " rowspan=\"2\"" : "")}>
<b>{(includeDelivery ? "Lese " + season.Year : "Zusammengefasste Flächenbindungen")}</b>
per {Date:dd.MM.yyyy} {(includeDelivery ? "[kg]" : "")}
</th>
{(!includeDelivery ? "<th>Fläche</th>" : "")}
<th>Lieferpflicht</th>
<th>Lieferrecht</th>
{(includeDelivery ? "<th>Unterliefert</th>" : "")}
{(includeDelivery ? "<th>Noch lieferbar</th>" : "")}
{(includeDelivery ? "<th>Geliefert</th>" : "")}
{(includePayment ? "<th>Zugeteilt</th>" : "")}
{(includeDelivery ? "<th>Geliefert</th>" : "")}
</tr>
{(!includeDelivery ? "<tr><th class=\"unit\">[m²]</th><th class=\"unit\">[kg]</th><th class=\"unit\">[kg]</th></tr>" : "")}
</thead>
""";
var mBuckets = buckets
.Where(b => ((!includeDelivery && b.Value.Area > 0) ||
(includeDelivery && (b.Value.Right > 0 || b.Value.Obligation > 0 || b.Value.Delivery > 0))) &&
(filter == null || filter.Contains(b.Key[..2])))
.ToList();
var fbVars = mBuckets
.Where(b => b.Value.Right > 0 || b.Value.Obligation > 0)
.Select(b => b.Key.Replace("_", ""))
.Order()
.ToArray();
var fbs = mBuckets
.Where(b => fbVars.Contains(b.Key) && b.Key.Length == 2)
.OrderBy(b => b.Value.Name);
var vtr = mBuckets
.Where(b => fbVars.Contains(b.Key) && b.Key.Length > 2)
.OrderBy(b => b.Value.Name);
var rem = mBuckets
.Where(b => !fbVars.Contains(b.Key))
.OrderBy(b => b.Value.Name);
tbl += "\n<tbody>\n";
tbl += $"<tr><th>Gesamtlieferung lt. gez. GA</th>{FormatRow(
Member.BusinessShares * season.MinKgPerBusinessShare,
Member.BusinessShares * season.MaxKgPerBusinessShare,
Member.Deliveries.Where(d => d.Year == season.Year).Sum(d => d.Weight),
isGa: true, showPayment: includePayment, showArea: !includeDelivery
)}</tr>";
if (fbs.Any()) {
tbl += $"<tr class=\"subheading{(filter == null ? " border" : "")}\"><th colspan=\"{(includePayment ? 8 : 7)}\">" +
$"Flächenbindungen{(vtr.Any() ? " (inkl. Verträge)" : "")}:</th></tr>";
foreach (var (id, b) in fbs) {
tbl += $"<tr><th>{b.Name}</th>{FormatRow(b, showPayment: includePayment, showArea: !includeDelivery)}</tr>";
}
}
if (vtr.Any()) {
tbl += $"<tr class=\"subheading{(filter == null ? " border" : "")}\"><th colspan=\"{(includePayment ? 8 : 7)}\">" +
"Verträge:</th></tr>";
foreach (var (id, b) in vtr) {
tbl += $"<tr><th>{b.Name}</th>{FormatRow(b, showPayment: includePayment, showArea: !includeDelivery)}</tr>";
}
}
tbl += "\n</tbody>\n";
return $"<table class=\"buckets {(isTiny ? "tiny" : "small")} number cohere\">\n" + tbl + "\n</table>";
}
} }
} }
+6 -3
View File
@@ -1,5 +1,5 @@
.address-wrapper, aside, main { .address-wrapper, aside {
overflow: hidden; overflow: hidden;
} }
@@ -64,7 +64,7 @@ aside table thead:not(:first-child) tr {
border-top: 0.5pt solid #808080; border-top: 0.5pt solid #808080;
} }
aside table thead th { aside table thead tr {
background-color: #E0E0E0; background-color: #E0E0E0;
font-size: 10pt; font-size: 10pt;
} }
@@ -95,7 +95,10 @@ main > *:first-child {
margin-top: 0; margin-top: 0;
} }
main h1, .main-wrapper p { main h1,
main h2,
main h3,
.main-wrapper p {
font-size: 12pt; font-size: 12pt;
margin: 1em 0; margin: 1em 0;
text-align: justify; text-align: justify;
+2
View File
@@ -5,6 +5,7 @@ using Elwig.Models.Entities;
namespace Elwig.Documents { namespace Elwig.Documents {
public class CreditNote : BusinessDocument { public class CreditNote : BusinessDocument {
public PaymentMember? Payment;
public Credit? Credit; public Credit? Credit;
public CreditNoteData Data; public CreditNoteData Data;
public string? Text; public string? Text;
@@ -16,6 +17,7 @@ namespace Elwig.Documents {
UseBillingAddress = true; UseBillingAddress = true;
ShowDateAndLocation = true; ShowDateAndLocation = true;
Data = data; Data = data;
Payment = p;
Credit = p.Credit; Credit = p.Credit;
Aside = Aside.Replace("</table>", "") + Aside = Aside.Replace("</table>", "") +
$"<thead><tr><th colspan='2'>Gutschrift</th></tr></thead><tbody>" + $"<thead><tr><th colspan='2'>Gutschrift</th></tr></thead><tbody>" +
+51 -45
View File
@@ -1,3 +1,4 @@
@using Elwig.Helpers
@using RazorLight @using RazorLight
@inherits TemplatePage<Elwig.Documents.CreditNote> @inherits TemplatePage<Elwig.Documents.CreditNote>
@model Elwig.Documents.CreditNote @model Elwig.Documents.CreditNote
@@ -8,84 +9,89 @@
<table class="credit"> <table class="credit">
<colgroup> <colgroup>
<col style="width: 25mm;"/> <col style="width: 25mm;"/>
<col style="width: 5mm;"/> <col style="width: 5mm;"/>
<col style="width: 20mm;"/>
<col style="width: 20mm;"/> <col style="width: 20mm;"/>
<col style="width: 18mm;"/>
<col style="width: 10mm;"/> <col style="width: 10mm;"/>
<col style="width: 10mm;"/> <col style="width: 10mm;"/>
<col style="width: 15mm;"/> <col style="width: 15mm;"/>
<col style="width: 12mm;"/> <col style="width: 12mm;"/>
<col style="width: 15mm;"/> <col style="width: 15mm;"/>
<col style="width: 10mm;"/> <col style="width: 17mm;"/>
<col style="width: 10mm;"/> <col style="width: 16mm;"/>
<col style="width: 15mm;"/>
</colgroup> </colgroup>
<thead> <thead>
<tr> <tr>
<th rowspan="3" style="text-align: left;">Lieferschein-Nr.</th> <th rowspan="2" style="text-align: left;">Lieferschein-Nr.</th>
<th rowspan="3">Pos.</th> <th rowspan="2" class="narrow">Pos.</th>
<th rowspan="3" style="text-align: left;">Sorte</th> <th rowspan="2" style="text-align: left;">Sorte</th>
<th rowspan="3" style="text-align: left;">Attribut</th> <th rowspan="2" style="text-align: left;">Attribut</th>
<th rowspan="2" colspan="2">Gradation</th> <th colspan="2">Gradation</th>
<th rowspan="2" colspan="2">Flächenbindung</th> <th colspan="2">Flächenbindung</th>
<th rowspan="2">Preis</th> <th>Preis</th>
<th colspan="2">Zu-/Abschläge</th> <th class="narrow">Zu-/Abschläge</th>
<th rowspan="2">Betrag</th> <th>Betrag</th>
</tr> </tr>
<tr> <tr>
<th>Rel.</th> <th class="unit">[°Oe]</th>
<th>Abs.</th> <th class="unit narrow">[°KMW]</th>
</tr> <th class="unit" colspan="2">[kg]</th>
<tr> <th class="unit">[@Model.CurrencySymbol/kg]</th>
<th>[°Oe]</th> <th class="unit">[@Model.CurrencySymbol]</th>
<th>[°KMW]</th> <th class="unit">[@Model.CurrencySymbol]</th>
<th colspan="2">[kg]</th>
<th>[@Model.CurrencySymbol/kg]</th>
<th>[%]</th>
<th>[@Model.CurrencySymbol/kg]</th>
<th>[@Model.CurrencySymbol]</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@foreach (var p in Model.Data.Rows) { @foreach (var p in Model.Data.Rows) {
var rows = Math.Max(p.Buckets.Length, p.Modifiers.Length + 1); var rows = Math.Max(p.Buckets.Length, p.Modifiers.Length + 1);
var first = true; var first = true;
//var pmt = p.Payment;
var abs = 0; // pmt?.ModAbs == null || pmt?.ModAbs == 0 ? "-" : pmt?.ModAbs.ToString("0." + string.Concat(Enumerable.Repeat('0', Model.Precision)));
var rel = 0; // pmt?.ModRel == null || pmt?.ModRel == 0 ? "-" : $"{pmt?.ModRel * 100:0.00##}";
@for (int i = 0; i < rows; i++) { @for (int i = 0; i < rows; i++) {
<tr class="@(first ? "first" : "") @(rows > i + 1 ? "trailing" : "")"> <tr class="@(first ? "first" : "") @(rows > i + 1 ? "last" : "")">
@if (first) { @if (first) {
<td rowspan="@rows" class="lsnr">@p.LsNr</td> <td rowspan="@rows">@p.LsNr</td>
<td rowspan="@rows" class="dpnr">@p.DPNr</td> <td rowspan="@rows">@p.DPNr</td>
<td class="variant small">@p.Variant</td> <td class="small">@p.Variant</td>
<td class="attribute small">@p.Attribute</td> <td class="small">@p.Attribute</td>
<td rowspan="@rows" class="oe">@($"{p.Gradation.Oe:N0}")</td> <td rowspan="@rows" class="center">@($"{p.Gradation.Oe:N0}")</td>
<td rowspan="@rows" class="kmw">@($"{p.Gradation.Kmw:N1}")</td> <td rowspan="@rows" class="center">@($"{p.Gradation.Kmw:N1}")</td>
} }
@if (i > 0 && i <= p.Modifiers.Length) { @if (i > 0 && i <= p.Modifiers.Length) {
<td colspan="2" class="mod">@(p.Modifiers[i - 1])</td> <td colspan="2" class="small mod">@p.Modifiers[i - 1]</td>
} else if (i > 0) { } else if (i > 0) {
<td colspan="2"></td> <td colspan="2"></td>
} }
@if (i < p.Buckets.Length) { @if (i < p.Buckets.Length) {
var bucket = p.Buckets[i]; var bucket = p.Buckets[i];
<td class="geb small">@bucket.Name:</td> <td class="small">@bucket.Name:</td>
<td class="weight">@($"{bucket.Value:N0}")</td> <td class="number">@($"{bucket.Value:N0}")</td>
<td class="price">@($"{bucket.Price:N4}")</td> <td class="number">@($"{bucket.Price:N4}")</td>
} else { } else {
<td colspan="3"></td> <td colspan="3"></td>
} }
@if (first) { @if (i == p.Buckets.Length - 1) {
<td rowspan="@rows" class="rel">@rel</td> var totalMod = p.TotalModifiers ?? 0;
<td rowspan="@rows" class="abs">@abs</td> <td class="number@(totalMod == 0 ? " center" : "")">@(totalMod == 0 ? "-" : Utils.GetSign(totalMod) + $"{Math.Abs(totalMod):N2}")</td>
<!-- FIXME rel/abs mods --> <td class="number">@($"{p.Amount:N2}")</td>
<td rowspan="@rows" class="amount">@($"{p.Buckets.Sum(b => b.Amount):N2}")</td> } else {
first = false; <td colspan="2"></td>
} }
</tr> </tr>
first = false;
} }
} }
@if (Model.Payment == null) {
<tr class="sum">
<td colspan="7"></td>
<td colspan="2">Gesamt:</td>
<td colspan="2" class="number">@($"{Model.Data.Rows.Sum(p => p.Amount):N2}")</td>
</tr>
} else {
<tr class="sum">
<td colspan="7"></td>
<td colspan="2">Gesamt:</td>
<td colspan="2" class="number">@($"{Model.Payment.NetAmount:N2}")</td>
</tr>
}
</tbody> </tbody>
</table> </table>
</main> </main>
+15 -58
View File
@@ -1,50 +1,7 @@
table.credit {
font-size: 10pt;
}
table.credit th, table.credit .mod {
table.credit td { padding-left: 5mm;
padding: 0 0.25mm;
}
table.credit thead {
font-size: 8pt
}
table.credit thead th {
font-weight: normal;
font-style: italic;
vertical-align: bottom;
}
table.credit td {
vertical-align: top;
}
table.credit .oe,
table.credit .kmw {
text-align: center;
}
table.credit .dpnr {
text-align: center;
}
table.credit .amount,
table.credit .weight,
table.credit .price {
text-align: right;
}
table.credit .rel,
table.credit .abs {
text-align: center;
}
table.credit .amount.sum {
vertical-align: bottom;
padding-bottom: 1mm;
} }
table.credit tbody tr:not(.first):not(.last) { table.credit tbody tr:not(.first):not(.last) {
@@ -52,18 +9,18 @@ table.credit tbody tr:not(.first):not(.last) {
break-after: avoid; break-after: avoid;
} }
table.credit tbody tr.first td { table.credit tr:not(.first) td {
padding-top: 0;
}
table.credit tr.last td {
padding-bottom: 0;
}
table.credit tr.sum {
font-size: 12pt;
}
table.credit tr.sum td {
padding-top: 1mm; padding-top: 1mm;
} }
table.credit tbody tr.last td {
padding-bottom: 1mm;
}
table.credit tbody tr.new {
border-top: 0.5pt solid black;
}
table.credit .small {
font-size: 8pt;
}
+1 -1
View File
@@ -10,7 +10,7 @@ namespace Elwig.Documents {
public Season Season; public Season Season;
public DeliveryConfirmationData Data; public DeliveryConfirmationData Data;
public string? Text = App.Client.TextDeliveryConfirmation; public string? Text = App.Client.TextDeliveryConfirmation;
public Dictionary<string, (string, int, int, int, int)> MemberBuckets; public Dictionary<string, MemberBucket> MemberBuckets;
public DeliveryConfirmation(AppDbContext ctx, int year, Member m, DeliveryConfirmationData data) : public DeliveryConfirmation(AppDbContext ctx, int year, Member m, DeliveryConfirmationData data) :
base($"Anlieferungsbestätigung {year}", m) { base($"Anlieferungsbestätigung {year}", m) {
+20 -97
View File
@@ -1,3 +1,4 @@
@using Elwig.Documents
@using RazorLight @using RazorLight
@inherits TemplatePage<Elwig.Documents.DeliveryConfirmation> @inherits TemplatePage<Elwig.Documents.DeliveryConfirmation>
@model Elwig.Documents.DeliveryConfirmation @model Elwig.Documents.DeliveryConfirmation
@@ -8,7 +9,7 @@
<table class="delivery-confirmation"> <table class="delivery-confirmation">
<colgroup> <colgroup>
<col style="width: 25mm;"/> <col style="width: 25mm;"/>
<col style="width: 5mm;"/> <col style="width: 5mm;"/>
<col style="width: 20mm;"/> <col style="width: 20mm;"/>
<col style="width: 21mm;"/> <col style="width: 21mm;"/>
<col style="width: 19mm;"/> <col style="width: 19mm;"/>
@@ -22,7 +23,7 @@
<thead> <thead>
<tr> <tr>
<th rowspan="2" style="text-align: left;">Lieferschein-Nr.</th> <th rowspan="2" style="text-align: left;">Lieferschein-Nr.</th>
<th rowspan="2">Pos.</th> <th rowspan="2" class="narrow">Pos.</th>
<th rowspan="2" style="text-align: left;">Sorte</th> <th rowspan="2" style="text-align: left;">Sorte</th>
<th rowspan="2" style="text-align: left;">Attribut</th> <th rowspan="2" style="text-align: left;">Attribut</th>
<th rowspan="2" style="text-align: left;">Qualitätsstufe</th> <th rowspan="2" style="text-align: left;">Qualitätsstufe</th>
@@ -32,11 +33,11 @@
<th>Davon<br/>abzuwerten</th> <th>Davon<br/>abzuwerten</th>
</tr> </tr>
<tr> <tr>
<th>[°Oe]</th> <th class="unit">[°Oe]</th>
<th>[°KMW]</th> <th class="unit narrow">[°KMW]</th>
<th colspan="2">[kg]</th> <th class="unit" colspan="2">[kg]</th>
<th>[kg]</th> <th class="unit">[kg]</th>
<th>[kg]</th> <th class="unit">[kg]</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -47,128 +48,50 @@
var rows = Math.Max(p.Buckets.Length, p.Modifiers.Length + 1); var rows = Math.Max(p.Buckets.Length, p.Modifiers.Length + 1);
var first = true; var first = true;
@for (int i = 0; i < rows; i++) { @for (int i = 0; i < rows; i++) {
<tr class="@(first ? "first" : "") @(p.Variant != lastVariant && lastVariant != "" ? "new": "") @(rows > i + 1 ? "trailing" : "")"> <tr class="@(first ? "first" : "") @(p.Variant != lastVariant && lastVariant != "" ? "new": "") @(rows > i + 1 ? "last" : "")">
@if (first) { @if (first) {
<td rowspan="@rows">@p.LsNr</td> <td rowspan="@rows">@p.LsNr</td>
<td rowspan="@rows">@p.DPNr</td> <td rowspan="@rows">@p.DPNr</td>
<td class="small">@p.Variant</td> <td class="small">@p.Variant</td>
<td class="small">@p.Attribute</td> <td class="small">@p.Attribute</td>
<td class="small">@p.QualityLevel</td> <td class="small">@p.QualityLevel</td>
<td rowspan="@rows" class="grad">@($"{p.Gradation.Oe:N0}")</td> <td rowspan="@rows" class="center">@($"{p.Gradation.Oe:N0}")</td>
<td rowspan="@rows" class="grad">@($"{p.Gradation.Kmw:N1}")</td> <td rowspan="@rows" class="center">@($"{p.Gradation.Kmw:N1}")</td>
} }
@if (i > 0 && i <= p.Modifiers.Length) { @if (i > 0 && i <= p.Modifiers.Length) {
<td colspan="3" class="mod">@(p.Modifiers[i - 1])</td> <td colspan="3" class="small mod">@(p.Modifiers[i - 1])</td>
} else if (i > 0) { } else if (i > 0) {
<td colspan="3"></td> <td colspan="3"></td>
} }
@if (i < p.Buckets.Length) { @if (i < p.Buckets.Length) {
var bucket = p.Buckets[i]; var bucket = p.Buckets[i];
<td class="geb">@bucket.Name:</td> <td class="small">@bucket.Name:</td>
<td class="weight">@($"{bucket.Value:N0}")</td> <td class="number">@($"{bucket.Value:N0}")</td>
} else { } else {
<td colspan="2"></td> <td colspan="2"></td>
} }
@if (i == p.Buckets.Length - 1) { @if (i == p.Buckets.Length - 1) {
<td class="weight">@($"{p.Weight:N0}")</td> <td class="number">@($"{p.Weight:N0}")</td>
} else { } else {
<td></td> <td></td>
} }
@if (first) { @if (first) {
<td rowspan="@rows" class="weight"></td> <td rowspan="@rows" class="number"></td>
first = false; first = false;
} }
</tr> </tr>
lastVariant = p.Variant;
} }
lastVariant = p.Variant;
} }
<tr class="sum"> <tr class="sum">
<td colspan="8">Gesamt:</td> <td colspan="8">Gesamt:</td>
<td colspan="2" class="weight">@($"{Model.Data.Rows.Sum(p => p.Weight):N0}")</td> <td colspan="2" class="number">@($"{Model.Data.Rows.Sum(p => p.Weight):N0}")</td>
<td></td> <td></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<table class="delivery-confirmation-stats"> @Raw(BusinessDocument.PrintSortenaufteilung(Model.MemberBuckets))
<colgroup> @Raw(Model.PrintBucketTable(Model.Season, Model.MemberBuckets, includePayment: true))
<col style="width: 45mm;"/>
<col style="width: 17mm;"/>
<col style="width: 17mm;"/>
<col style="width: 17mm;"/>
<col style="width: 19mm;"/>
<col style="width: 16mm;"/>
<col style="width: 17mm;"/>
<col style="width: 17mm;"/>
</colgroup>
<thead>
<tr>
<th><b>Lese @Model.Season.Year</b> per @($"{Model.Date:dd.MM.yyyy}") [kg]</th>
<th>Lieferpflicht</th>
<th>Lieferrecht</th>
<th>Unterliefert</th>
<th>Noch lieferbar</th>
<th>Überliefert</th>
<th>Zugeteilt</th>
<th>Geliefert</th>
</tr>
</thead>
<tbody>
@{
string FormatRow(int mode, int obligation, int right, int sum, int? payment = null) {
var isGa = mode == 0;
payment ??= sum;
return $"<td>{(mode == 1 ? "" : obligation == 0 ? "-" : $"{obligation:N0}")}</td>" +
$"<td>{(mode == 1 ? "" : right == 0 ? "-" : $"{right:N0}")}</td>" +
$"<td>{(mode == 1 ? "" : payment < obligation ? $"<b>{obligation - payment:N0}\x3c/b>" : "-")}</td>" +
$"<td>{(mode == 1 ? "" : payment >= obligation && sum <= right ? $"{right - sum:N0}" : "-")}</td>" +
$"<td>{(mode == 1 ? "" : obligation == 0 && right == 0 ? "-" : (sum > right ? ((isGa ? "<b>" : "") + $"{sum - right:N0}" + (isGa ? "</b>" : "")) : "-"))}</td>" +
$"<td>{(mode != 2 ? "" : obligation == 0 && right == 0 ? "-" : $"{payment:N0}")}</td>" +
$"<td>{sum:N0}</td>";
}
var mBuckets = Model.MemberBuckets.Where(b => b.Value.Item2 > 0 || b.Value.Item3 > 0 || b.Value.Item4 > 0).ToList();
var fbVars = mBuckets.Where(b => b.Value.Item2 > 0 || b.Value.Item3 > 0).Select(b => b.Key.Replace("_", "")).Order().ToArray();
var fbs = mBuckets.Where(b => fbVars.Contains(b.Key) && b.Key.Length == 2).OrderBy(b => b.Value.Item1);
var vtr = mBuckets.Where(b => fbVars.Contains(b.Key) && b.Key.Length > 2).OrderBy(b => b.Value.Item1);
var rem = mBuckets.Where(b => !fbVars.Contains(b.Key)).OrderBy(b => b.Value.Item1);
}
<tr>
<th>Gesamtlieferung lt. gez. GA</th>
@Raw(FormatRow(
0,
Model.Member.BusinessShares * Model.Season.MinKgPerBusinessShare,
Model.Member.BusinessShares * Model.Season.MaxKgPerBusinessShare,
Model.Member.Deliveries.Where(d => d.Year == Model.Season.Year).Sum(d => d.Weight)
))
</tr>
@if (rem.Any()) {
<tr class="subheading"><th colspan="8">Sortenaufteilung:</th></tr>
}
@foreach (var (id, (name, right, obligation, sum, payment)) in rem) {
<tr>
<th>@name</th>
@Raw(FormatRow(1, obligation, right, sum, payment))
</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>
}
@if (vtr.Any()) {
<tr class="subheading"><th colspan="8">Verträge:</th></tr>
}
@foreach (var (id, (name, right, obligation, sum, payment)) in vtr) {
<tr>
<th>@name</th>
@Raw(FormatRow(2, obligation, right, sum, payment))
</tr>
}
</tbody>
</table>
<div class="text" style="margin-top: 2em;"> <div class="text" style="margin-top: 2em;">
@if (Model.Text != null) { @if (Model.Text != null) {
<p class="comment" style="white-space: pre-wrap; break-inside: avoid;">@Model.Text</p> <p class="comment" style="white-space: pre-wrap; break-inside: avoid;">@Model.Text</p>
+5 -86
View File
@@ -1,52 +1,8 @@
table.delivery-confirmation {
font-size: 10pt;
margin-bottom: 5mm;
}
table.delivery-confirmation thead {
font-size: 8pt;
}
table.delivery-confirmation thead th {
font-weight: normal;
font-style: italic;
}
table.delivery-confirmation td {
overflow: hidden;
white-space: nowrap;
}
table.delivery-confirmation td[rowspan] {
vertical-align: top;
}
table.delivery-confirmation .weight {
text-align: right;
}
table.delivery-confirmation .grad {
text-align: center;
}
table.delivery-confirmation .geb {
font-size: 8pt;
}
table.delivery-confirmation .mod { table.delivery-confirmation .mod {
font-size: 8pt;
padding-left: 5mm; padding-left: 5mm;
} }
table.delivery-confirmation .small {
font-size: 8pt;
}
table.delivery-confirmation tr.new td {
border-top: 0.5pt solid black;
}
table.delivery-confirmation tr:not(.first) { table.delivery-confirmation tr:not(.first) {
break-before: avoid; break-before: avoid;
} }
@@ -55,14 +11,11 @@ table.delivery-confirmation tr:not(.first) td {
padding-top: 0; padding-top: 0;
} }
table.delivery-confirmation tr.trailing td { table.delivery-confirmation tr.last td {
padding-bottom: 0; padding-bottom: 0;
} }
table.delivery-confirmation tr.sum { table.delivery-confirmation tr.sum {
border-top: 0.5pt solid black;
break-before: avoid;
font-weight: bold;
font-size: 12pt; font-size: 12pt;
} }
@@ -70,44 +23,10 @@ table.delivery-confirmation tr.sum td {
padding-top: 1mm; padding-top: 1mm;
} }
table.delivery-confirmation-stats { table.sortenaufteilung {
font-size: 10pt; break-after: avoid;
break-inside: avoid;
} }
table.delivery-confirmation-stats th, table.buckets {
table.delivery-confirmation-stats td { break-before: avoid;
padding: 0.125mm 0;
overflow: hidden;
white-space: nowrap;
}
table.delivery-confirmation-stats tr.subheading th {
text-align: left;
}
table.delivery-confirmation-stats thead th {
font-weight: normal;
font-style: italic;
text-align: right;
font-size: 8pt;
}
table.delivery-confirmation-stats thead th:first-child {
text-align: left;
}
table.delivery-confirmation-stats td {
text-align: right;
}
table.delivery-confirmation-stats tbody th {
font-weight: normal;
font-style: italic;
text-align: left;
}
table.delivery-confirmation-stats tr.subheading th {
font-weight: bold;
border-top: 0.5pt solid black;
} }
+17 -17
View File
@@ -10,9 +10,9 @@
<colgroup> <colgroup>
<col style="width: 25mm;"/> <col style="width: 25mm;"/>
<col style="width: 5mm;"/> <col style="width: 5mm;"/>
<col style="width: 17mm;"/> <col style="width: 15mm;"/>
<col style="width: 10mm;"/>
<col style="width: 8mm;"/> <col style="width: 8mm;"/>
<col style="width: 12mm;"/>
<col style="width: 38mm;"/> <col style="width: 38mm;"/>
<col style="width: 28mm;"/> <col style="width: 28mm;"/>
<col style="width: 10mm;"/> <col style="width: 10mm;"/>
@@ -22,7 +22,7 @@
<thead> <thead>
<tr> <tr>
<th rowspan="2" style="text-align: left;">Lieferschein-Nr.</th> <th rowspan="2" style="text-align: left;">Lieferschein-Nr.</th>
<th rowspan="2">Pos.</th> <th rowspan="2" class="narrow">Pos.</th>
<th rowspan="2">Datum</th> <th rowspan="2">Datum</th>
<th rowspan="2">Zeit</th> <th rowspan="2">Zeit</th>
<th colspan="2" rowspan="2" style="text-align: left;">Mitglied</th> <th colspan="2" rowspan="2" style="text-align: left;">Mitglied</th>
@@ -31,9 +31,9 @@
<th>Gewicht</th> <th>Gewicht</th>
</tr> </tr>
<tr> <tr>
<th>[°Oe]</th> <th class="unit">[°Oe]</th>
<th>[°KMW]</th> <th class="unit narrow">[°KMW]</th>
<th>[kg]</th> <th class="unit">[kg]</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -41,14 +41,14 @@
<tr> <tr>
<td>@p.Delivery.LsNr</td> <td>@p.Delivery.LsNr</td>
<td>@p.DPNr</td> <td>@p.DPNr</td>
<td>@($"{p.Delivery.Date:dd.MM.yyyy}")</td> <td class="small">@($"{p.Delivery.Date:dd.MM.yyyy}")</td>
<td>@($"{p.Delivery.Time:HH:mm}")</td> <td class="small">@($"{p.Delivery.Time:HH:mm}")</td>
<td class="mgnr">@p.Delivery.Member.MgNr</td> <td class="number">@p.Delivery.Member.MgNr</td>
<td>@p.Delivery.Member.AdministrativeName</td> <td class="small">@p.Delivery.Member.AdministrativeName</td>
<td>@p.Variant.Name</td> <td class="small">@p.Variant.Name</td>
<td class="grad">@($"{p.Oe:N0}")</td> <td class="center">@($"{p.Oe:N0}")</td>
<td class="grad">@($"{p.Kmw:N1}")</td> <td class="center">@($"{p.Kmw:N1}")</td>
<td class="weight">@($"{p.Weight:N0}")</td> <td class="number">@($"{p.Weight:N0}")</td>
</tr> </tr>
} }
<tr class="sum"> <tr class="sum">
@@ -58,9 +58,9 @@
} }
<td colspan="2">Gesamt:</td> <td colspan="2">Gesamt:</td>
<td colspan="5">(Teil-)Lieferungen: @($"{Model.Deliveries.DistinctBy(p => p.Delivery).Count():N0}") (@($"{Model.Deliveries.Count():N0}"))</td> <td colspan="5">(Teil-)Lieferungen: @($"{Model.Deliveries.DistinctBy(p => p.Delivery).Count():N0}") (@($"{Model.Deliveries.Count():N0}"))</td>
<td class="grad">@($"{oe:N0}")</td> <td class="center">@($"{oe:N0}")</td>
<td class="grad">@($"{kmw:N1}")</td> <td class="center">@($"{kmw:N1}")</td>
<td class="weight">@($"{Model.Deliveries.Sum(p => p.Weight):N0}")</td> <td class="number">@($"{Model.Deliveries.Sum(p => p.Weight):N0}")</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
-35
View File
@@ -10,38 +10,3 @@ h2 {
font-size: 14pt; font-size: 14pt;
margin-top: 2mm; margin-top: 2mm;
} }
table.journal {
font-size: 10pt;
}
table.journal thead {
font-size: 8pt;
}
table.journal th {
font-weight: normal;
font-style: italic;
}
table.journal td {
overflow: hidden;
white-space: nowrap;
}
table.journal .mgnr,
table.journal .weight {
text-align: right;
}
table.journal .grad {
text-align: center;
}
table.journal tr.sum {
font-weight: bold;
}
table.journal tr.sum td {
border-top: 0.5pt solid black;
}
+1 -1
View File
@@ -7,7 +7,7 @@ namespace Elwig.Documents {
public Delivery Delivery; public Delivery Delivery;
public string? Text; public string? Text;
public Dictionary<string, (string, int, int, int, int)> MemberBuckets; public Dictionary<string, MemberBucket> MemberBuckets;
// 0 - none // 0 - none
// 1 - GA only // 1 - GA only
+18 -69
View File
@@ -5,7 +5,7 @@
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\DeliveryNote.css" /> <link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\DeliveryNote.css" />
<main> <main>
<h1>@Model.Title</h1> <h1>@Model.Title</h1>
<table class="delivery"> <table class="delivery large">
<colgroup> <colgroup>
<col style="width: 10.00mm;"/> <col style="width: 10.00mm;"/>
<col style="width: 21.25mm;"/> <col style="width: 21.25mm;"/>
@@ -19,7 +19,7 @@
</colgroup> </colgroup>
<thead> <thead>
<tr> <tr>
<th class="main" rowspan="2" style="text-align: center;">Pos.</th> <th class="main center narrow" rowspan="2">Pos.</th>
<th class="main" rowspan="2" colspan="2">Sorte</th> <th class="main" rowspan="2" colspan="2">Sorte</th>
<th class="main" rowspan="2" colspan="2">Attribut</th> <th class="main" rowspan="2" colspan="2">Attribut</th>
<th class="main" rowspan="2">Qualitätsstufe</th> <th class="main" rowspan="2">Qualitätsstufe</th>
@@ -27,21 +27,21 @@
<th>Gewicht</th> <th>Gewicht</th>
</tr> </tr>
<tr> <tr>
<th style="font-size: 8pt;">[°Oe]</th> <th class="unit">[°Oe]</th>
<th style="font-size: 8pt;">[°KMW]</th> <th class="unit narrow">[°KMW]</th>
<th style="font-size: 8pt;">[kg]</th> <th class="unit">[kg]</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@foreach (var part in Model.Delivery.Parts.OrderBy(p => p.DPNr)) { @foreach (var part in Model.Delivery.Parts.OrderBy(p => p.DPNr)) {
<tr class="main"> <tr class="main">
<td style="text-align: center;">@part.DPNr</td> <td class="center">@part.DPNr</td>
<td colspan="2">@part.Variant.Name</td> <td colspan="2">@part.Variant.Name</td>
<td colspan="2">@part.Attribute?.Name</td> <td colspan="2">@part.Attribute?.Name</td>
<td>@part.Quality.Name</td> <td>@part.Quality.Name</td>
<td class="narrow" style="text-align: center;">@($"{part.Oe:N0}")</td> <td class="center">@($"{part.Oe:N0}")</td>
<td class="narrow" style="text-align: center;">@($"{part.Kmw:N1}")</td> <td class="center">@($"{part.Kmw:N1}")</td>
<td class="narrow" style="text-align: right;">@($"{part.Weight:N0}")</td> <td class="number">@($"{part.Weight:N0}")</td>
</tr> </tr>
<tr><td></td><td colspan="5" style="white-space: pre;"><i>Herkunft:</i> @part.OriginString</td></tr> <tr><td></td><td colspan="5" style="white-space: pre;"><i>Herkunft:</i> @part.OriginString</td></tr>
@if (part.Modifiers.Count() > 0) { @if (part.Modifiers.Count() > 0) {
@@ -65,9 +65,9 @@
@if (Model.Delivery.Parts.Count() > 1) { @if (Model.Delivery.Parts.Count() > 1) {
<tr class="main sum"> <tr class="main sum">
<td colspan="6">Gesamt:</td> <td colspan="6">Gesamt:</td>
<td style="text-align: center;">@($"{Model.Delivery.Oe:N0}")</td> <td class="center">@($"{Model.Delivery.Oe:N0}")</td>
<td style="text-align: center;">@($"{Model.Delivery.Kmw:N1}")</td> <td class="center">@($"{Model.Delivery.Kmw:N1}")</td>
<td style="text-align: right;">@($"{Model.Delivery.Weight:N0}")</td> <td class="number">@($"{Model.Delivery.Weight:N0}")</td>
</tr> </tr>
} }
</tbody> </tbody>
@@ -76,63 +76,12 @@
<p class="comment">Amerkung zur Lieferung: @Model.Delivery.Comment</p> <p class="comment">Amerkung zur Lieferung: @Model.Delivery.Comment</p>
} }
@if (Model.DisplayStats > 0) { @if (Model.DisplayStats > 0) {
<table class="delivery-note-stats @(Model.DisplayStats > 2 ? "expanded" : "")"> @Raw(Model.PrintBucketTable(
<colgroup> Model.Delivery.Season, Model.MemberBuckets, isTiny: true,
<col style="width: 45mm;"/> filter: Model.DisplayStats > 2 ? null :
<col style="width: 20mm;"/> Model.DisplayStats == 1 ? new List<string>() :
<col style="width: 20mm;"/> Model.Delivery.Parts.Select(p => p.SortId).Distinct().ToList()
<col style="width: 20mm;"/> ))
<col style="width: 20mm;"/>
<col style="width: 20mm;"/>
<col style="width: 20mm;"/>
</colgroup>
<thead>
<tr>
<th><b>Lese @Model.Delivery.Year</b> per @($"{Model.Date:dd.MM.yyyy}") [kg]</th>
<th>Lieferpflicht</th>
<th>Lieferrecht</th>
<th>Unterliefert</th>
<th>Noch lieferbar</th>
<th>Überliefert</th>
<th>Geliefert</th>
</tr>
</thead>
<tbody>
@{
string FormatRow(int obligation, int right, int sum) {
return $"<td>{obligation:N0}</td>" +
$"<td>{right:N0}</td>" +
$"<td>{(sum < obligation ? $"{obligation - sum:N0}" : "-")}</td>" +
$"<td>{(sum >= obligation && sum <= right ? $"{right - sum:N0}" : "-")}</td>" +
$"<td>{(sum > right ? $"{sum - right:N0}" : "-")}</td>" +
$"<td>{sum:N0}</td>";
}
var sortids = Model.Delivery.Parts.Select(p => p.SortId).ToList();
var buckets = Model.MemberBuckets.GroupBy(b => b.Key[..2]).ToDictionary(g => g.Key, g => g.Count());
}
<tr>
<th>Gesamtlieferung lt. gez. GA</th>
@Raw(FormatRow(
Model.Member.BusinessShares * Model.Delivery.Season.MinKgPerBusinessShare,
Model.Member.BusinessShares * Model.Delivery.Season.MaxKgPerBusinessShare,
Model.Member.Deliveries.Where(d => d.Year == Model.Delivery.Year).Sum(d => d.Weight)
))
</tr>
@if (Model.DisplayStats > 1) {
<tr class="subheading">
<th>Flächenbindungen:</th>
</tr>
@foreach (var (id, (name, right, obligation, sum, _)) in Model.MemberBuckets.OrderBy(b => b.Key)) {
if (right > 0 || obligation > 0 || (sum > 0 && buckets[id[..2]] > 1 && !id.EndsWith('_'))) {
<tr class="@(sortids.Contains(id[..2]) ? "" : "optional")">
<th>@name</th>
@Raw(FormatRow(obligation, right, sum))
</tr>
}
}
}
</tbody>
</table>
} }
</main> </main>
@for (int i = 0; i < 2; i++) { @for (int i = 0; i < 2; i++) {
-51
View File
@@ -11,12 +11,6 @@ table.delivery tr:not(.main) {
break-before: avoid; break-before: avoid;
} }
table.delivery th {
font-weight: normal;
font-style: italic;
font-size: 10pt;
}
table.delivery th.main { table.delivery th.main {
text-align: left; text-align: left;
} }
@@ -43,55 +37,10 @@ table.delivery tr.tight:has(+ tr:not(.tight)) td {
padding-bottom: 0.5mm !important; padding-bottom: 0.5mm !important;
} }
table.delivery tr.sum {
border-top: 0.5pt solid black;
break-before: avoid;
}
table.delivery tr.sum td { table.delivery tr.sum td {
padding-top: 1mm; padding-top: 1mm;
} }
table.delivery-note-stats { table.delivery-note-stats {
font-size: 8pt;
break-inside: avoid;
break-after: avoid; break-after: avoid;
} }
table.delivery-note-stats th,
table.delivery-note-stats td {
padding: 0.125mm 0;
}
table.delivery-note-stats:not(.expanded) tr.optional {
display: none;
}
table.delivery-note-stats tr.subheading th {
text-align: left;
}
table.delivery-note-stats.expanded tr.subheading:not(:has(~ tr)),
table.delivery-note-stats tr.subheading:not(:has(~ tr:not(.optional))) {
display: none;
}
table.delivery-note-stats thead th {
font-weight: normal;
font-style: italic;
text-align: right;
}
table.delivery-note-stats thead th:first-child {
text-align: left;
}
table.delivery-note-stats td {
text-align: right;
}
table.delivery-note-stats tbody th {
font-weight: normal;
font-style: italic;
text-align: left;
}
+5
View File
@@ -68,4 +68,9 @@ hr.page-break {
.page::after { .page::after {
content: "Seite " counter(page) " von " counter(pages) !important; content: "Seite " counter(page) " von " counter(pages) !important;
} }
a {
text-decoration: inherit;
color: inherit;
}
} }
+147
View File
@@ -0,0 +1,147 @@
main table {
border-collapse: collapse;
margin-bottom: 10mm;
font-size: 10pt;
}
main table.large {font-size: 12pt;}
main table.tiny {
font-size: 8pt;
margin-bottom: 5mm;
}
main table.border {
border: 0.5pt solid black;
}
main table tr {
break-inside: avoid;
}
main table th,
main table td {
overflow: hidden;
white-space: nowrap;
vertical-align: middle;
padding: 0.5mm 1mm;
font-weight: normal;
}
main table.small th,
main table.small td,
main table.tiny th,
main table.tiny td {
padding: 0.125mm 0.125mm;
}
main table td[rowspan] {
vertical-align: top;
}
main table thead {
font-size: 8pt;
}
main table.large thead {
font-size: 10pt;
}
main table th {
font-style: italic;
}
main table tbody {
}
main table .small {
font-size: 8pt;
}
main table .large {
font-size: 12pt;
}
main table.number td,
main table.number th {
padding-left: 0;
padding-right: 0;
}
main table.number thead th,
main table.number td,
main table tbody td.number {
text-align: right;
}
main table.center tbody td,
main table tbody td.center {
text-align: center;
}
main table tbody th {
text-align: left;
}
main table.cohere {
break-inside: avoid;
}
main table tr.subheading th,
main table tr.subheading td {
font-weight: bold;
}
main table tr.subheading th {
text-align: left;
font-size: 10pt;
}
main table.small tr.subheading th,
main table.small tr.subheading td,
main table.tiny tr.subheading th,
main table.tiny tr.subheading td {
font-size: 8pt;
}
main table tr.sectionheading {
background-color: #E0E0E0;
}
main table tr.sectionheading th {
padding-top: 0.5mm;
padding-bottom: 0.5mm;
font-weight: bold;
text-align: center;
font-size: 10pt;
border-top: 0.5pt solid black;
}
main table.number thead tr:first-child th:first-child {
text-align: left;
}
main table tr.sum td {
font-weight: bold;
}
main table tr.sum,
main table tr.new,
main table tr.border {
border-top: 0.5pt solid black;
}
main table tr.sum {
break-before: avoid;
}
main table th.unit {
font-size: 8pt;
}
main table.number th.unit {
padding-right: 2mm;
}
main table th.narrow {
padding-left: 0;
padding-right: 0;
}
main table .lborder {border-left: 0.5pt solid black;}
main table .rborder {border-right: 0.5pt solid black;}
+3 -1
View File
@@ -33,7 +33,9 @@ namespace Elwig.Documents {
Footer = Utils.GenerateFooter("<br/>", " \u00b7 ") Footer = Utils.GenerateFooter("<br/>", " \u00b7 ")
.Item(c.NameFull).NextLine() .Item(c.NameFull).NextLine()
.Item(c.Address).Item($"{c.Plz} {c.Ort}").Item("Österreich").Item("Tel.", c.PhoneNr).Item("Fax", c.FaxNr).NextLine() .Item(c.Address).Item($"{c.Plz} {c.Ort}").Item("Österreich").Item("Tel.", c.PhoneNr).Item("Fax", c.FaxNr).NextLine()
.Item(c.EmailAddress).Item(c.Website).Item("Betriebs-Nr.", c.LfbisNr).Item("UID", c.UstIdNr).NextLine() .Item(c.EmailAddress != null ? $"<a href=\"mailto:{c.Name} {c.NameSuffix} <{c.EmailAddress}>\">{c.EmailAddress}</a>" : null)
.Item(c.Website != null ? $"<a href=\"http://{c.Website}/\">{c.Website}</a>" : null)
.Item("Betriebs-Nr.", c.LfbisNr).Item("UID", c.UstIdNr).NextLine()
.Item("BIC", c.Bic).Item("IBAN", c.Iban) .Item("BIC", c.Bic).Item("IBAN", c.Iban)
.ToString(); .ToString();
Date = DateTime.Today; Date = DateTime.Today;
+1
View File
@@ -9,6 +9,7 @@
<meta charset="UTF-8"/> <meta charset="UTF-8"/>
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\Document.css"/> <link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\Document.css"/>
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\Document.Page.css"/> <link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\Document.Page.css"/>
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\Document.Table.css"/>
@if (Model.DoubleSided) { @if (Model.DoubleSided) {
<style> <style>
@@page :left { @@page :left {
+1 -3
View File
@@ -8,14 +8,12 @@ namespace Elwig.Documents {
public Season Season; public Season Season;
public int Year = Utils.CurrentYear; public int Year = Utils.CurrentYear;
public Dictionary<string, (string, int, int, int, int)> MemberBuckets; public Dictionary<string, MemberBucket> MemberBuckets;
public Dictionary<string, int> BucketAreas;
public MemberDataSheet(Member m, AppDbContext ctx) : base($"Stammdatenblatt {m.AdministrativeName}", m) { public MemberDataSheet(Member m, AppDbContext ctx) : base($"Stammdatenblatt {m.AdministrativeName}", m) {
DocumentId = $"Stammdatenblatt {m.MgNr}"; DocumentId = $"Stammdatenblatt {m.MgNr}";
Season = ctx.Seasons.Find(Year) ?? throw new ArgumentException("invalid season"); Season = ctx.Seasons.Find(Year) ?? throw new ArgumentException("invalid season");
MemberBuckets = ctx.GetMemberBuckets(Year, m.MgNr).GetAwaiter().GetResult(); MemberBuckets = ctx.GetMemberBuckets(Year, m.MgNr).GetAwaiter().GetResult();
BucketAreas = ctx.GetMemberBucketAreas(Year, m.MgNr).GetAwaiter().GetResult();
} }
} }
} }
+136 -247
View File
@@ -1,4 +1,5 @@
@using RazorLight @using RazorLight
@using Elwig.Helpers
@inherits TemplatePage<Elwig.Documents.MemberDataSheet> @inherits TemplatePage<Elwig.Documents.MemberDataSheet>
@model Elwig.Documents.MemberDataSheet @model Elwig.Documents.MemberDataSheet
@{ @{
@@ -7,166 +8,146 @@
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\MemberDataSheet.css" /> <link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\MemberDataSheet.css" />
<main> <main>
<h1>@Model.Title</h1> <h1>@Model.Title</h1>
<table class="member"> <table class="member border">
<thead> <colgroup>
<tr><th colspan="4">Persönliche Daten</th></tr> <col style="width: 30.0mm;"/>
</thead> <col style="width: 51.5mm;"/>
<col style="width: 20.0mm;"/>
<col style="width: 12.0mm;"/>
<col style="width: 18.0mm;"/>
<col style="width: 31.5mm;"/>
</colgroup>
<tbody> <tbody>
<tr class="sectionheading"><th colspan="6">Persönliche Daten</th></tr>
<tr> <tr>
<th>Mitglieds-Nr.</th> <th class="small">Titel (vorangestellt)</th>
<th class="small">Vorname</th>
<th colspan="3" class="small">Nachname</th>
<th class="small">Titel (nachgestellt)</th>
</tr>
<tr>
<td class="large">@Model.Member.Prefix</td>
<td class="large">@Model.Member.GivenName @Model.Member.MiddleName</td>
<td class="large" colspan="3">@Model.Member.FamilyName</td>
<td class="large">@Model.Member.Suffix</td>
</tr>
<tr>
<th>Mitglieds-Nr.:</th>
<td>@Model.Member.MgNr</td> <td>@Model.Member.MgNr</td>
<th>Vorgänger MgNr.</th> <th colspan="2">Geburtsjahr/-tag:</th>
<td>@Model.Member.PredecessorMgNr</td> <td colspan="2">@(string.Join('.', Model.Member.Birthday?.Split('-')?.Reverse() ?? Array.Empty<string>()))</td>
</tr> </tr>
<tr> <tr>
<th>Titel (vorangestellt)</th> <th>Adresse:</th>
<td>@Model.Member.Prefix</td> <td colspan="5">@Model.Member.Address</td>
<th>Titel (nachgestellt)</th>
<td>@Model.Member.Suffix</td>
</tr> </tr>
<tr> <tr>
<th>Vorname</th> <th>PLZ/Ort:</th>
<td>@Model.Member.GivenName</td> <td colspan="5">
<th>Nachname</th> @Model.Member.PostalDest.AtPlz?.Plz
<td>@Model.Member.FamilyName</td> @Model.Member.PostalDest.AtPlz?.Dest
(@Model.Member.PostalDest.AtPlz?.Ort.Name)
</td>
</tr>
<tr class="sectionheading"><th colspan="6">Rechnungsadresse (optional)</th></tr>
<tr>
<th>Name:</th>
<td colspan="5">@Model.Member.BillingAddress?.Name</td>
</tr> </tr>
<tr> <tr>
<th>Weitere Vornamen</th> <th>Adresse:</th>
<td colspan="3">@Model.Member.MiddleName</td> <td colspan="5">@Model.Member.BillingAddress?.Address</td>
</tr> </tr>
<tr> <tr>
<th>Geburtsjahr/-tag</th> <th>PLZ/Ort:</th>
<td colspan="3">@(string.Join('.', Model.Member.Birthday?.Split('-')?.Reverse()??Array.Empty<string>()))</td> <td colspan="5">
</tr> @if (Model.Member.BillingAddress != null) {
</tbody> @Model.Member.BillingAddress.PostalDest.AtPlz?.Plz
@Model.Member.BillingAddress.PostalDest.AtPlz?.Dest
<thead> @("(")@Model.Member.BillingAddress.PostalDest.AtPlz?.Ort.Name@(")")
<tr><th colspan="4">Anschrift</th></tr>
</thead>
<tbody>
<tr>
<th>Adresse</th>
<td colspan="3">@Model.Member.Address</td>
</tr>
<tr>
<th>PLZ/Ort</th>
<td colspan="3">@Model.Member.PostalDest.AtPlz?.Plz @Model.Member.PostalDest.AtPlz?.Dest (@Model.Member.PostalDest.AtPlz?.Ort.Name)</td>
</tr>
</tbody>
<thead>
<tr><th colspan="4">Rechnungsadresse</th></tr>
</thead>
<tbody>
<tr>
<th>Name</th>
<td colspan="3">@Model.Member.BillingAddress?.Name</td>
</tr>
<tr>
<th>Adresse</th>
<td colspan="3">@Model.Member.BillingAddress?.Address</td>
</tr>
<tr>
<th>PLZ/Ort</th>
<td colspan="3">@Model.Member.BillingAddress?.PostalDest.AtPlz?.Plz @Model.Member.BillingAddress?.PostalDest.AtPlz?.Dest @((Model.Member.BillingAddress != null && Model.Member.BillingAddress.PostalDest.AtPlz != null) ? "(" + Model.Member.BillingAddress?.PostalDest.AtPlz?.Ort.Name + ")" : "")</td>
</tr>
</tbody>
<thead>
<tr><th colspan="4">Kontaktdaten</th></tr>
</thead>
<tbody>
@foreach (var e in Model.Member.EmailAddresses) {
var emailNumber = Model.Member.EmailAddresses.Count > 1 ? 1 : 0;
<tr>
<th>E-Mail-Adresse @(emailNumber != 0 ? $"({emailNumber})" : "")</th>
<td colspan="3">@e.Address</td>
</tr>
emailNumber++;
}
@foreach (var k in Model.Member.TelephoneNumbers) {
<tr>
@if (k.Type.Equals("landline")) {
<th>Tel.-Nr. (Festnetz)</th>
} else if (k.Type.Equals("mobile")) {
<th>Tel.-Nr. (mobil)</th>
} else if (k.Type.Equals("fax")) {
<th>Fax-Nr.</th>
} }
<td colspan="3">@k.Number</td> </td>
</tr>
<tr class="sectionheading">
<th colspan="3">Kontaktdaten</th>
<th colspan="3" class="lborder">Bankverbindung</th>
</tr>
@{
List<string?[]> subTbl1 = new();
subTbl1.AddRange(Model.Member.EmailAddresses.Select(a => new[] { "E-Mail-Adresse", a.Address }));
subTbl1.AddRange(Model.Member.TelephoneNumbers.Select(n => new[] { Utils.PhoneNrTypeToString(n.Type), n.Number, n.Comment }));
subTbl1.Add(new[] { "Tel.-Nr./E-Mail-Adr.", null });
List<string?[]> subTbl2 = new();
subTbl2.Add(new[] { "IBAN", Model.Member.Iban != null ? Elwig.Helpers.Utils.FormatIban(Model.Member.Iban) : null });
subTbl2.Add(new[] { "BIC", Model.Member.Bic });
}
@for (int i = 0; i < Math.Max(subTbl1.Count, subTbl2.Count); i++) {
<tr>
<th>@(i < subTbl1.Count ? subTbl1[i][0] + ":" : "")</th>
@if (i < subTbl1.Count && subTbl1[i].Length >= 3 && subTbl1[i][2] != null) {
<td>@subTbl1[i][1]</td>
<td>(@subTbl1[i][2])</td>
} else {
<td colspan="2">@(i < subTbl1.Count ? subTbl1[i][1] : "")</td>
}
<th class="lborder">@(i < subTbl2.Count ? subTbl2[i][0] + ":" : "")</th>
<td colspan="2">@(i < subTbl2.Count ? subTbl2[i][1] : "")</td>
</tr> </tr>
} }
</tbody> <tr class="sectionheading"><th colspan="6">Betrieb</th></tr>
<thead>
<tr><th colspan="4">Bankverbindung</th></tr>
</thead>
<tbody>
<tr> <tr>
<th>IBAN</th> <th>Betriebs-Nr.:</th>
<td colspan="3">@(Model.Member.Iban != null ? Elwig.Helpers.Utils.FormatIban(Model.Member.Iban) : "")</td>
</tr>
<tr>
<th>BIC</th>
<td colspan="3">@Model.Member.Bic</td>
</tr>
</tbody>
<thead>
<tr><th colspan="4">Betrieb</th></tr>
</thead>
<tbody>
<tr>
<th>UID</th>
<td>@Model.Member.UstIdNr</td>
<th>Betriebs-Nr.</th>
<td>@Model.Member.LfbisNr</td> <td>@Model.Member.LfbisNr</td>
<th colspan="2">UID:</th>
<td colspan="2">@Model.Member.UstIdNr</td>
</tr> </tr>
<tr> <tr>
<th>Buchführend</th> <th>Stammgemeinde:</th>
<td>@(Model.Member.IsBuchführend ? "Ja" : "Nein")</td>
<th>Bio</th>
<td>@(Model.Member.IsOrganic ? "Ja" : "Nein")</td>
</tr>
</tbody>
<thead>
<tr><th colspan="4">Genossenschaft</th></tr>
</thead>
<tbody>
<tr>
<th>Eintritt</th>
<td>@Model.Member.EntryDate</td>
<th>Austritt</th>
<td>@Model.Member.ExitDate</td>
</tr>
<tr>
<th>Aktiv</th>
<td>@(Model.Member.IsActive ? "Ja" : "Nein")</td>
<th>Geschäftsanteile</th>
<td>@Model.Member.BusinessShares</td>
</tr>
<tr>
<th>Volllierferant</th>
<td>@(Model.Member.IsVollLieferant ? "Ja" : "Nein")</td>
<th>Funktionär</th>
<td>@(Model.Member.IsFunktionär ? "Ja" : "Nein")</td>
</tr>
<tr>
<th>Stamm-Zweigstelle</th>
<td>@Model.Member.Branch?.Name</td>
<th>Stammgemeinde</th>
<td>@Model.Member.DefaultKg?.Name</td> <td>@Model.Member.DefaultKg?.Name</td>
<th colspan="2">Buchführend:</th>
<td colspan="2">@(Model.Member.IsBuchführend ? "Ja" : "Nein")</td>
</tr> </tr>
<tr> <tr>
<th>Kontakt via Post</th> <th colspan="2" class="small">(Katastralgemeinde mit dem größten Anteil an Weinbauflächen)</th>
<td>@(Model.Member.ContactViaPost ? "Ja" : "Nein")</td> <th colspan="2">Bio:</th>
<th>Kontakt via E-Mail</th> <td colspan="2">@(Model.Member.IsOrganic ? "Ja" : "Nein")</td>
<td>@(Model.Member.ContactViaEmail ? "Ja" : "Nein")</td> </tr>
<tr class="sectionheading"><th colspan="6">Genossenschaft</th></tr>
<tr>
<th>Status:</th>
<td>
@(Model.Member.IsActive ? "Aktiv" : "Nicht aktiv")
<span class="small">
(@(Model.Member.ExitDate != null ?
$"{Model.Member.EntryDate:dd.MM.yyyy}{Model.Member.ExitDate:dd.MM.yyyy}" :
$"seit {Model.Member.EntryDate:dd.MM.yyyy}"))
</span>
</td>
<th colspan="2">Geschäftsanteile:</th>
<td colspan="2">@Model.Member.BusinessShares</td>
</tr>
<tr>
<th>Stamm-Zweigstelle:</th>
<td>@Model.Member.Branch?.Name</td>
<th colspan="2">Volllierferant:</th>
<td colspan="2">@(Model.Member.IsVollLieferant ? "Ja" : "Nein")</td>
</tr>
<tr>
<th>Zusendungen via...</th>
<td>
<i>Post:</i> @(Model.Member.ContactViaPost ? "Ja" : "Nein")
<i>E-Mail:</i> @(Model.Member.ContactViaEmail ? "Ja" : "Nein")
</td>
<th colspan="2">Funktionär:</th>
<td colspan="2">@(Model.Member.IsFunktionär ? "Ja" : "Nein")</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@Raw(Model.PrintBucketTable(Model.Season, Model.MemberBuckets, includeDelivery: false))
@{ @{
var areaComs = Model.Member.ActiveAreaCommitments.GroupBy(a => a.AreaComType).Select(group => new { var areaComs = Model.Member.ActiveAreaCommitments.GroupBy(a => a.AreaComType).Select(group => new {
AreaComType = group.Key, AreaComType = group.Key,
@@ -177,15 +158,15 @@
} }
@if (areaComs.Count != 0) { @if (areaComs.Count != 0) {
<h1>Flächenbindungen</h1> <h2>Flächenbindungen per @($"{Model.Date:dd.MM.yyyy}")</h2>
<table class="area-commitements"> <table class="area-commitements">
<colgroup> <colgroup>
<col style="width: 40mm;" /> <col style="width: 40mm;"/>
<col style="width: 30mm;" /> <col style="width: 30mm;"/>
<col style="width: 35mm;" /> <col style="width: 35mm;"/>
<col style="width: 15mm;" /> <col style="width: 15mm;"/>
<col style="width: 25mm;" /> <col style="width: 25mm;"/>
<col style="width: 20mm;" /> <col style="width: 20mm;"/>
</colgroup> </colgroup>
<thead> <thead>
<tr> <tr>
@@ -201,124 +182,32 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@foreach (var contractType in areaComs) { @foreach (var contractType in areaComs) {
<tr class="contract-type @(contractType.AreaComType.DisplayName != lastContract && lastContract != "" ? "new" : "")"> <tr class="subheading @(contractType.AreaComType.DisplayName != lastContract && lastContract != "" ? "new" : "")">
<th colspan="3" style="text-align: left"> <th colspan="3">
@($"{contractType.AreaComType.WineVar.Name} {(contractType.AreaComType.WineAttr != null ? "(" + contractType.AreaComType.WineAttr + ")" : "")}") @($"{contractType.AreaComType.WineVar.Name} {(contractType.AreaComType.WineAttr != null ? "(" + contractType.AreaComType.WineAttr + ")" : "")}")
</th> </th>
<th style="text-align: right">@($"{contractType.Size:N0}")</th> <td class="number">@($"{contractType.Size:N0}")</td>
<th colspan="2"></th> <td colspan="2"></td>
</tr> </tr>
@foreach (var areaCom in contractType.AreaComs) { @foreach (var areaCom in contractType.AreaComs) {
<tr class="area-commitment"> <tr class="area-commitment">
<td>@areaCom.Kg.AtKg.Name <span style="font-size: 8pt;">(@areaCom.Kg.AtKg.KgNr)</span></td> <td>@areaCom.Kg.AtKg.Name <span style="font-size: 8pt;">(@($"{areaCom.Kg.AtKg.KgNr:00000}"))</span></td>
<td>@areaCom.Rd?.Name</td> <td>@areaCom.Rd?.Name</td>
<td>@areaCom.GstNr.Replace(",", ", ")</td> <td class="text">@areaCom.GstNr.Replace(",", ", ").Replace("-", "")</td>
<td style="text-align: right;">@($"{areaCom.Area:N0}")</td> <td class="number">@($"{areaCom.Area:N0}")</td>
<td style="text-align: center;">@areaCom.WineCult.Name</td> <td class="center">@areaCom.WineCult.Name</td>
<td style="text-align: center;">Ab @areaCom.YearFrom</td> <td class="center">@(areaCom.YearTo == null ? $"ab {areaCom.YearFrom}" : $"{areaCom.YearFrom}{areaCom.YearTo}")</td>
</tr> </tr>
lastContract = contractType.AreaComType.DisplayName; lastContract = contractType.AreaComType.DisplayName;
} }
} }
<tr class="sum">
<tr class="new"> <td colspan="3">Gesamt:</td>
<th colspan="3" style="text-align: left">Gesamt</th> <td class="number">@($"{Model.Member.ActiveAreaCommitments.Select(a => a.Area).Sum():N0}")</td>
<th style="text-align: right">@($"{Model.Member.ActiveAreaCommitments.Select(a => a.Area).Sum():N0}")</th> <td colspan="2"></td>
<th colspan="2"></th>
</tr>
</tbody>
</table>
<table class="delivery-confirmation-stats">
<colgroup>
<col style="width: 102mm;" />
<col style="width: 21mm;" />
<col style="width: 21mm;" />
<col style="width: 21mm;" />
</colgroup>
<thead>
<tr>
<th rowspan="2"></th>
<th style="text-align: center;">Fläche</th>
<th style="text-align: center;">Lieferpflicht</th>
<th style="text-align: center;">Lieferrecht</th>
</tr>
<tr>
<th style="text-align: center;">[m²]</th>
<th style="text-align: center;">[kg]</th>
<th style="text-align: center;">[kg]</th>
</tr>
</thead>
<tbody>
@{
string FormatRow(int mode, int area, int obligation, int right) {
return $"<td>{(mode == 0 || mode == 1 ? "" : area == 0 ? "-" : $"{area:N0}")}</td>" +
$"<td>{(mode == 1 ? "" : obligation == 0 ? "-" : $"{obligation:N0}")}</td>" +
$"<td>{(mode == 1 ? "" : right == 0 ? "-" : $"{right:N0}")}</td>";
}
var mBuckets = Model.MemberBuckets.Where(b => b.Value.Item2 > 0 || b.Value.Item3 > 0 || b.Value.Item4 > 0).ToList();
var fbVars = mBuckets.Where(b => b.Value.Item2 > 0 || b.Value.Item3 > 0).Select(b => b.Key.Replace("_", "")).Order().ToArray();
var fbs = mBuckets.Where(b => fbVars.Contains(b.Key) && b.Key.Length == 2).OrderBy(b => b.Value.Item1);
var vtr = mBuckets.Where(b => fbVars.Contains(b.Key) && b.Key.Length > 2).OrderBy(b => b.Value.Item1);
}
<tr>
<th>Laut gezeichneten GA</th>
@Raw(FormatRow(
0,
0,
Model.Member.BusinessShares * Model.Season.MinKgPerBusinessShare,
Model.Member.BusinessShares * Model.Season.MaxKgPerBusinessShare
))
</tr>
@if (fbs.Any()) {
<tr class="subheading"><th colspan="8">Flächenbindungen:</th></tr>
}
@foreach (var (id, (name, right, obligation, _, _)) in fbs) {
<tr>
<th>@name</th>
@Raw(FormatRow(2, Model.BucketAreas[id], obligation, right))
</tr>
}
@if (vtr.Any()) {
<tr class="subheading"><th colspan="8">Verträge:</th></tr>
}
@foreach (var (id, (name, right, obligation, _, _)) in vtr) {
<tr>
<th>@name</th>
@Raw(FormatRow(2, Model.BucketAreas[id], obligation, right))
</tr>
}
</tbody>
</table>
} else {
<table class="delivery-confirmation-stats" style="margin-top: 5mm;">
<colgroup>
<col style="width: 123mm;" />
<col style="width: 21mm;" />
<col style="width: 21mm;" />
</colgroup>
<thead>
<tr>
<th rowspan="2"></th>
<th style="text-align: center;">Lieferpflicht</th>
<th style="text-align: center;">Lieferrecht</th>
</tr>
<tr>
<th style="text-align: center;">[kg]</th>
<th style="text-align: center;">[kg]</th>
</tr>
</thead>
<tbody>
<tr>
<th>Laut gezeichneten GA</th>
<td>@($"{Model.Member.BusinessShares * Model.Season.MinKgPerBusinessShare:N0}")</td>
<td>@($"{Model.Member.BusinessShares * Model.Season.MaxKgPerBusinessShare:N0}")</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
} }
</main> </main>
+11 -69
View File
@@ -1,82 +1,24 @@
table.member {
border: 0.5pt solid black; h2 {
margin-bottom: 0.5em !important;
} }
table.member, table.area-commitements { table.member {
font-size: 10pt; margin-bottom: 5mm;
} }
table.member tbody th { table.area-commitements {
font-weight: normal; margin-top: 0;
font-style: italic;
text-align: left;
}
table.member thead,
table.member tbody {
border: 0.5pt solid black;
} }
table.area-commitements td { table.area-commitements td {
overflow: hidden;
}
table.area-commitements thead tr th {
font-weight: normal;
font-style: italic;
}
table.area-commitements tbody tr.contract-type {
margin-bottom: 10px;
}
table.area-commitements tbody td {
vertical-align: top; vertical-align: top;
} }
table.area-commitements tr.new th { table.area-commitements td.text {
border-top: 0.5pt solid black; white-space: normal;
} }
table.delivery-confirmation-stats { table.area-commitements tr.sum {
font-size: 10pt; font-size: 12pt;
break-inside: avoid;
} }
table.delivery-confirmation-stats th,
table.delivery-confirmation-stats td {
padding: 0.125mm 0;
overflow: hidden;
white-space: nowrap;
}
table.delivery-confirmation-stats tr.subheading th {
text-align: left;
}
table.delivery-confirmation-stats thead th {
font-weight: normal;
font-style: italic;
text-align: right;
font-size: 10pt;
}
table.delivery-confirmation-stats thead th:first-child {
text-align: left;
}
table.delivery-confirmation-stats td {
text-align: right;
}
table.delivery-confirmation-stats tbody th {
font-weight: normal;
font-style: italic;
text-align: left;
}
table.delivery-confirmation-stats tr.subheading th {
font-weight: bold;
border-top: 0.5pt solid black;
}
+9 -9
View File
@@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net7.0-windows</TargetFramework> <TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
<PreserveCompilationContext>true</PreserveCompilationContext> <PreserveCompilationContext>true</PreserveCompilationContext>
@@ -22,16 +22,16 @@
<ItemGroup> <ItemGroup>
<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="LinqKit" Version="1.2.5" />
<PackageReference Include="LinqKit" Version="1.2.4" /> <PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.25" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.24" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="8.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="7.0.13" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.13" /> <PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="8.0.0" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2088.41" /> <PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2151.40" />
<PackageReference Include="RazorLight" Version="2.3.1" /> <PackageReference Include="RazorLight" Version="2.3.1" />
<PackageReference Include="ScottPlot.WPF" Version="4.1.68" /> <PackageReference Include="ScottPlot.WPF" Version="4.1.68" />
<PackageReference Include="System.IO.Ports" Version="7.0.0" /> <PackageReference Include="System.IO.Ports" Version="8.0.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="7.0.0" /> <PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>
+50 -48
View File
@@ -12,6 +12,10 @@ using System.Collections.Generic;
using Elwig.Models.Dtos; using Elwig.Models.Dtos;
namespace Elwig.Helpers { namespace Elwig.Helpers {
public record struct AreaComBucket(int Area, int Obligation, int Right);
public record struct MemberBucket(string Name, int Area, int Obligation, int Right, int Delivery, int DeliveryStrict, int Payment);
public class AppDbContext : DbContext { public class AppDbContext : DbContext {
public DbSet<Country> Countries { get; private set; } public DbSet<Country> Countries { get; private set; }
@@ -60,10 +64,10 @@ namespace Elwig.Helpers {
public static string ConnectionString => $"Data Source=\"{App.Config.DatabaseFile}\"; Foreign Keys=True; Mode=ReadWrite; Cache=Default"; public static string ConnectionString => $"Data Source=\"{App.Config.DatabaseFile}\"; Foreign Keys=True; Mode=ReadWrite; Cache=Default";
private readonly Dictionary<int, Dictionary<int, Dictionary<string, (int, int)>>> _memberRightsAndObligations = new(); private readonly Dictionary<int, Dictionary<int, Dictionary<string, AreaComBucket>>> _memberAreaCommitmentBuckets = new();
private readonly Dictionary<int, Dictionary<int, Dictionary<string, int>>> _memberDeliveryBuckets = new(); private readonly Dictionary<int, Dictionary<int, Dictionary<string, int>>> _memberDeliveryBuckets = new();
private readonly Dictionary<int, Dictionary<int, Dictionary<string, int>>> _memberDeliveryBucketsStrict = new();
private readonly Dictionary<int, Dictionary<int, Dictionary<string, int>>> _memberPaymentBuckets = new(); private readonly Dictionary<int, Dictionary<int, Dictionary<string, int>>> _memberPaymentBuckets = new();
private readonly Dictionary<int, Dictionary<int, Dictionary<string, int>>> _memberBucketAreas = new();
public AppDbContext() { public AppDbContext() {
if (App.Config.DatabaseLog != null) { if (App.Config.DatabaseLog != null) {
@@ -203,22 +207,22 @@ namespace Elwig.Helpers {
} }
} }
private async Task FetchMemberRightsAndObligations(int year, SqliteConnection? cnx = null) { private async Task FetchMemberAreaCommitmentBuckets(int year, SqliteConnection? cnx = null) {
var ownCnx = cnx == null; var ownCnx = cnx == null;
cnx ??= await ConnectAsync(); cnx ??= await ConnectAsync();
var buckets = new Dictionary<int, Dictionary<string, (int, int)>>(); var buckets = new Dictionary<int, Dictionary<string, AreaComBucket>>();
using (var cmd = cnx.CreateCommand()) { using (var cmd = cnx.CreateCommand()) {
cmd.CommandText = $"SELECT mgnr, bucket, min_kg, max_kg FROM v_area_commitment_bucket WHERE year = {year}"; cmd.CommandText = $"SELECT mgnr, bucket, area, min_kg, max_kg FROM v_area_commitment_bucket WHERE year = {year}";
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);
var vtrgid = reader.GetString(1); var vtrgid = reader.GetString(1);
if (!buckets.ContainsKey(mgnr)) buckets[mgnr] = new(); if (!buckets.ContainsKey(mgnr)) buckets[mgnr] = new();
buckets[mgnr][vtrgid] = (reader.GetInt32(3), reader.GetInt32(2)); buckets[mgnr][vtrgid] = new(reader.GetInt32(2), reader.GetInt32(3), reader.GetInt32(4));
} }
} }
if (ownCnx) await cnx.DisposeAsync(); if (ownCnx) await cnx.DisposeAsync();
_memberRightsAndObligations[year] = buckets; _memberAreaCommitmentBuckets[year] = buckets;
} }
private async Task FetchMemberDeliveryBuckets(int year, SqliteConnection? cnx = null) { private async Task FetchMemberDeliveryBuckets(int year, SqliteConnection? cnx = null) {
@@ -239,6 +243,24 @@ namespace Elwig.Helpers {
_memberDeliveryBuckets[year] = buckets; _memberDeliveryBuckets[year] = buckets;
} }
private async Task FetchMemberDeliveryBucketsStrict(int year, SqliteConnection? cnx = null) {
var ownCnx = cnx == null;
cnx ??= await ConnectAsync();
var buckets = new Dictionary<int, Dictionary<string, int>>();
using (var cmd = cnx.CreateCommand()) {
cmd.CommandText = $"SELECT mgnr, bucket, weight FROM v_delivery_bucket_strict WHERE year = {year}";
using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync()) {
var mgnr = reader.GetInt32(0);
var bucket = reader.GetString(1);
if (!buckets.ContainsKey(mgnr)) buckets[mgnr] = new();
buckets[mgnr][bucket] = reader.GetInt32(2);
}
}
if (ownCnx) await cnx.DisposeAsync();
_memberDeliveryBucketsStrict[year] = buckets;
}
private async Task FetchMemberPaymentBuckets(int year, SqliteConnection? cnx = null) { private async Task FetchMemberPaymentBuckets(int year, SqliteConnection? cnx = null) {
var ownCnx = cnx == null; var ownCnx = cnx == null;
cnx ??= await ConnectAsync(); cnx ??= await ConnectAsync();
@@ -257,32 +279,10 @@ namespace Elwig.Helpers {
_memberPaymentBuckets[year] = buckets; _memberPaymentBuckets[year] = buckets;
} }
private async Task FetchMemberBucketAreas(int year, SqliteConnection? cnx = null) { public async Task<Dictionary<string, AreaComBucket>> GetMemberAreaCommitmentBuckets(int year, int mgnr, SqliteConnection? cnx = null) {
var ownCnx = cnx == null; if (!_memberAreaCommitmentBuckets.ContainsKey(year))
cnx ??= await ConnectAsync(); await FetchMemberAreaCommitmentBuckets(year, cnx);
var buckets = new Dictionary<int, Dictionary<string, int>>(); return _memberAreaCommitmentBuckets[year].GetValueOrDefault(mgnr, new());
using (var cmd = cnx.CreateCommand()) {
cmd.CommandText = $"SELECT mgnr, bucket, area FROM v_area_commitment_bucket_strict WHERE year = {year}";
using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync()) {
var mgnr = reader.GetInt32(0);
var bucket = reader.GetString(1);
var v = reader.GetInt32(2);
if (!buckets.ContainsKey(mgnr)) buckets[mgnr] = new();
buckets[mgnr][bucket] = v;
if (bucket.Length > 2) {
buckets[mgnr][bucket[..2]] = buckets[mgnr].GetValueOrDefault(bucket[..2], 0) + v;
}
}
}
if (ownCnx) await cnx.DisposeAsync();
_memberBucketAreas[year] = buckets;
}
public async Task<Dictionary<string, (int, int)>> GetMemberRightsAndObligations(int year, int mgnr, SqliteConnection? cnx = null) {
if (!_memberRightsAndObligations.ContainsKey(year))
await FetchMemberRightsAndObligations(year, cnx);
return _memberRightsAndObligations[year].GetValueOrDefault(mgnr, new());
} }
public async Task<Dictionary<string, int>> GetMemberDeliveryBuckets(int year, int mgnr, SqliteConnection? cnx = null) { public async Task<Dictionary<string, int>> GetMemberDeliveryBuckets(int year, int mgnr, SqliteConnection? cnx = null) {
@@ -291,37 +291,39 @@ namespace Elwig.Helpers {
return _memberDeliveryBuckets[year].GetValueOrDefault(mgnr, new()); return _memberDeliveryBuckets[year].GetValueOrDefault(mgnr, new());
} }
public async Task<Dictionary<string, int>> GetMemberDeliveryBucketsStrict(int year, int mgnr, SqliteConnection? cnx = null) {
if (!_memberDeliveryBucketsStrict.ContainsKey(year))
await FetchMemberDeliveryBucketsStrict(year, cnx);
return _memberDeliveryBucketsStrict[year].GetValueOrDefault(mgnr, new());
}
public async Task<Dictionary<string, int>> GetMemberPaymentBuckets(int year, int mgnr, SqliteConnection? cnx = null) { public async Task<Dictionary<string, int>> GetMemberPaymentBuckets(int year, int mgnr, SqliteConnection? cnx = null) {
if (!_memberPaymentBuckets.ContainsKey(year)) if (!_memberPaymentBuckets.ContainsKey(year))
await FetchMemberPaymentBuckets(year, cnx); await FetchMemberPaymentBuckets(year, cnx);
return _memberPaymentBuckets[year].GetValueOrDefault(mgnr, new()); return _memberPaymentBuckets[year].GetValueOrDefault(mgnr, new());
} }
public async Task<Dictionary<string, int>> GetMemberBucketAreas(int year, int mgnr, SqliteConnection? cnx = null) { public async Task<Dictionary<string, MemberBucket>> GetMemberBuckets(int year, int mgnr, SqliteConnection? cnx = null) {
if (!_memberBucketAreas.ContainsKey(year))
await FetchMemberBucketAreas(year, cnx);
return _memberBucketAreas[year].GetValueOrDefault(mgnr, new());
}
public async Task<Dictionary<string, (string, int, int, int, int)>> GetMemberBuckets(int year, int mgnr, SqliteConnection? cnx = null) {
var ownCnx = cnx == null; var ownCnx = cnx == null;
cnx ??= await ConnectAsync(); cnx ??= await ConnectAsync();
var rightsAndObligations = await GetMemberRightsAndObligations(year, mgnr, cnx); var rightsAndObligations = await GetMemberAreaCommitmentBuckets(year, mgnr, cnx);
var deliveryBuckets = await GetMemberDeliveryBuckets(year, mgnr, cnx); var deliveryBuckets = await GetMemberDeliveryBuckets(year, mgnr, cnx);
var deliveryBucketsStrict = await GetMemberDeliveryBucketsStrict(year, mgnr, cnx);
var paymentBuckets = await GetMemberPaymentBuckets(year, mgnr, cnx); var paymentBuckets = await GetMemberPaymentBuckets(year, mgnr, cnx);
if (ownCnx) await cnx.DisposeAsync(); if (ownCnx) await cnx.DisposeAsync();
var buckets = new Dictionary<string, (string, int, int, int, int)>(); var buckets = new Dictionary<string, MemberBucket>();
foreach (var id in rightsAndObligations.Keys.Union(deliveryBuckets.Keys).Union(paymentBuckets.Keys)) { foreach (var id in rightsAndObligations.Keys.Union(deliveryBuckets.Keys).Union(paymentBuckets.Keys)) {
var variety = await WineVarieties.FindAsync(id[..2]); var variety = await WineVarieties.FindAsync(id[..2]);
var attrIds = id[2..]; var attribute = await WineAttributes.FindAsync(id[2..]);
var attrs = await WineAttributes.Where(a => attrIds.Contains(a.AttrId)).ToListAsync(); var name = (variety?.Name ?? "") + (id[2..] == "_" ? " (kein Qual.Wein)" : attribute != null ? $" ({attribute})" : "");
var name = (variety?.Name ?? "") + (attrIds == "_" ? " (kein Qual.Wein)" : attrs.Count > 0 ? $" ({string.Join(" / ", attrs.Select(a => a.Name))})" : ""); buckets[id] = new(
buckets[id] = (
name, name,
rightsAndObligations.GetValueOrDefault(id).Item1, rightsAndObligations.GetValueOrDefault(id).Area,
rightsAndObligations.GetValueOrDefault(id).Item2, rightsAndObligations.GetValueOrDefault(id).Obligation,
rightsAndObligations.GetValueOrDefault(id).Right,
deliveryBuckets.GetValueOrDefault(id), deliveryBuckets.GetValueOrDefault(id),
deliveryBucketsStrict.GetValueOrDefault(id),
paymentBuckets.GetValueOrDefault(id) paymentBuckets.GetValueOrDefault(id)
); );
} }
+90 -4
View File
@@ -1,16 +1,16 @@
using Microsoft.Data.Sqlite; using Microsoft.Data.Sqlite;
using System; using System;
using System.Windows;
namespace Elwig.Helpers { namespace Elwig.Helpers {
public static class AppDbUpdater { public static class AppDbUpdater {
public static readonly int RequiredSchemaVersion = 9; public static readonly int RequiredSchemaVersion = 11;
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_3_To_4, UpdateDbSchema_4_To_5, UpdateDbSchema_1_To_2, UpdateDbSchema_2_To_3, UpdateDbSchema_3_To_4, UpdateDbSchema_4_To_5,
UpdateDbSchema_5_To_6, UpdateDBSchema_6_To_7, UpdateDbSchema_7_To_8, UpdateDbSchema_8_To_9, UpdateDbSchema_5_To_6, UpdateDBSchema_6_To_7, UpdateDbSchema_7_To_8, UpdateDbSchema_8_To_9,
UpdateDbSchema_9_To_10, UpdateDbSchema_10_To_11
}; };
private static void ExecuteNonQuery(SqliteConnection cnx, string sql) { private static void ExecuteNonQuery(SqliteConnection cnx, string sql) {
@@ -46,8 +46,7 @@ namespace Elwig.Helpers {
if (App.VersionMajor > major || if (App.VersionMajor > major ||
(App.VersionMajor == major && App.VersionMinor > minor) || (App.VersionMajor == major && App.VersionMinor > minor) ||
(App.VersionMajor == major && App.VersionMinor == minor && App.VersionPatch > patch)) (App.VersionMajor == major && App.VersionMinor == minor && App.VersionPatch > patch)) {
{
long vers = (App.VersionMajor << 24) | (App.VersionMinor << 16) | App.VersionPatch; long vers = (App.VersionMajor << 24) | (App.VersionMinor << 16) | App.VersionPatch;
ExecuteNonQuery(cnx, $"PRAGMA user_version = {vers}"); ExecuteNonQuery(cnx, $"PRAGMA user_version = {vers}");
} }
@@ -714,5 +713,92 @@ namespace Elwig.Helpers {
END; END;
"""); """);
} }
private static void UpdateDbSchema_9_To_10(SqliteConnection cnx) {
ExecuteNonQuery(cnx, "UPDATE wine_quality_level SET min_kmw = 10.6 WHERE qualid = 'RSW'");
ExecuteNonQuery(cnx, "DROP VIEW v_area_commitment_bucket");
ExecuteNonQuery(cnx, """
CREATE VIEW v_area_commitment_bucket AS
SELECT year, mgnr, bucket, area, min_kg, max_kg
FROM v_area_commitment_bucket_strict
WHERE attrid IS NOT NULL
UNION ALL
SELECT b.year, b.mgnr, b.sortid,
SUM(b.area) AS area,
SUM(b.min_kg) AS min_kg,
SUM(b.upper_max_kg) AS max_kg
FROM v_area_commitment_bucket_strict b
LEFT JOIN wine_attribute a ON a.attrid = b.attrid
WHERE a.strict IS NULL OR a.strict = FALSE
GROUP BY b.year, b.mgnr, b.sortid
ORDER BY year, mgnr, bucket;
""");
}
private static void UpdateDbSchema_10_To_11(SqliteConnection cnx) {
// Drop columns, if they exist...
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN price_1");
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN price_2");
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN price_3");
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN price_4");
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN price_5");
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN price_6");
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN price_7");
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN price_8");
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN price_9");
ExecuteNonQuery(cnx, "DROP TRIGGER t_payment_delivery_part_i");
ExecuteNonQuery(cnx, """
CREATE TRIGGER t_payment_delivery_part_i
AFTER INSERT ON payment_delivery_part FOR EACH ROW
BEGIN
INSERT INTO payment_member (year, avnr, mgnr, net_amount)
SELECT year, NEW.avnr, mgnr, NEW.amount FROM delivery WHERE (year, did) = (NEW.year, NEW.did)
ON CONFLICT DO UPDATE SET net_amount = net_amount + excluded.net_amount;
END;
""");
ExecuteNonQuery(cnx, "DROP TRIGGER t_payment_delivery_part_u");
ExecuteNonQuery(cnx, """
CREATE TRIGGER t_payment_delivery_part_u
AFTER UPDATE ON payment_delivery_part FOR EACH ROW
BEGIN
UPDATE payment_member
SET net_amount = net_amount - OLD.amount
WHERE (year, avnr, mgnr) IN (SELECT year, OLD.avnr, mgnr FROM delivery WHERE (year, did) = (OLD.year, OLD.did));
INSERT INTO payment_member (year, avnr, mgnr, net_amount)
SELECT year, NEW.avnr, mgnr, NEW.amount FROM delivery WHERE (year, did) = (NEW.year, NEW.did)
ON CONFLICT DO UPDATE SET net_amount = net_amount + excluded.net_amount;
END;
""");
ExecuteNonQuery(cnx, "DROP TRIGGER t_payment_delivery_part_d");
ExecuteNonQuery(cnx, """
CREATE TRIGGER t_payment_delivery_part_d
AFTER DELETE ON payment_delivery_part FOR EACH ROW
BEGIN
UPDATE payment_member
SET net_amount = net_amount - OLD.amount
WHERE (year, avnr, mgnr) IN (SELECT year, OLD.avnr, mgnr FROM delivery WHERE (year, did) = (OLD.year, OLD.did));
END;
""");
ExecuteNonQuery(cnx, "DROP TRIGGER t_payment_delivery_part_bucket_u");
ExecuteNonQuery(cnx, """
CREATE TRIGGER t_payment_delivery_part_bucket_u
AFTER UPDATE ON payment_delivery_part_bucket FOR EACH ROW
BEGIN
UPDATE payment_delivery_part
SET net_amount = net_amount - OLD.amount
WHERE (year, did, dpnr, avnr) = (OLD.year, OLD.did, OLD.dpnr, OLD.avnr);
UPDATE payment_delivery_part
SET net_amount = net_amount + NEW.amount
WHERE (year, did, dpnr, avnr) = (NEW.year, NEW.did, NEW.dpnr, NEW.avnr);
END;
""");
ExecuteNonQuery(cnx, "ALTER TABLE payment_member DROP COLUMN amount");
ExecuteNonQuery(cnx, "ALTER TABLE payment_member ADD COLUMN amount INTEGER NOT NULL GENERATED ALWAYS AS (ROUND(net_amount * (1 + mod_rel) + mod_abs)) VIRTUAL");
}
} }
} }
+5 -5
View File
@@ -41,7 +41,7 @@ namespace Elwig.Helpers.Billing {
var attrVals = Context.WineAttributes.ToDictionary(a => a.AttrId, a => (a.IsStrict, a.FillLower)); var attrVals = Context.WineAttributes.ToDictionary(a => a.AttrId, a => (a.IsStrict, a.FillLower));
var attrForced = attrVals.Where(a => a.Value.IsStrict && a.Value.FillLower == 0).Select(a => a.Key).ToArray(); var attrForced = attrVals.Where(a => a.Value.IsStrict && a.Value.FillLower == 0).Select(a => a.Key).ToArray();
using var cnx = await AppDbContext.ConnectAsync(); using var cnx = await AppDbContext.ConnectAsync();
await Context.GetMemberRightsAndObligations(Year, 0, cnx); await Context.GetMemberAreaCommitmentBuckets(Year, 0, cnx);
var inserts = new List<(int, int, int, string, int)>(); var inserts = new List<(int, int, int, string, int)>();
var deliveries = new List<(int, int, int, string, int, double, string, string?, string[], bool?)>(); var deliveries = new List<(int, int, int, string, int, double, string, string?, string[], bool?)>();
@@ -66,11 +66,11 @@ namespace Elwig.Helpers.Billing {
} }
int lastMgNr = 0; int lastMgNr = 0;
Dictionary<string, (int, int)>? rightsAndObligations = null; Dictionary<string, AreaComBucket>? rightsAndObligations = null;
Dictionary<string, int> used = new(); Dictionary<string, int> used = new();
foreach (var (mgnr, did, dpnr, sortid, weight, kmw, qualid, attrid, modifiers, gebunden) in deliveries) { foreach (var (mgnr, did, dpnr, sortid, weight, kmw, qualid, attrid, modifiers, gebunden) in deliveries) {
if (lastMgNr != mgnr) { if (lastMgNr != mgnr) {
rightsAndObligations = await Context.GetMemberRightsAndObligations(Year, mgnr); rightsAndObligations = await Context.GetMemberAreaCommitmentBuckets(Year, mgnr);
used = new(); used = new();
} }
if ((honorGebunden && gebunden == false) || if ((honorGebunden && gebunden == false) ||
@@ -94,8 +94,8 @@ namespace Elwig.Helpers.Billing {
if (rightsAndObligations.ContainsKey(key)) { if (rightsAndObligations.ContainsKey(key)) {
int i = (c == 0) ? 1 : 2; int i = (c == 0) ? 1 : 2;
var u = used.GetValueOrDefault(key, 0); var u = used.GetValueOrDefault(key, 0);
var vr = Math.Max(0, Math.Min(rightsAndObligations[key].Item1 - u, w)); var vr = Math.Max(0, Math.Min(rightsAndObligations[key].Right - u, w));
var vo = Math.Max(0, Math.Min(rightsAndObligations[key].Item2 - u, w)); var vo = Math.Max(0, Math.Min(rightsAndObligations[key].Obligation - u, w));
var v = (attributes.Length == c || attributes.Select(a => !attrVals[a].IsStrict ? 2 : attrVals[a].FillLower).Min() == 2) ? vr : vo; var v = (attributes.Length == c || attributes.Select(a => !attrVals[a].IsStrict ? 2 : attrVals[a].FillLower).Min() == 2) ? vr : vo;
used[key] = u + v; used[key] = u + v;
if (key.Length > 2 && !isStrict) used[key[..2]] = used.GetValueOrDefault(key[..2], 0) + v; if (key.Length > 2 && !isStrict) used[key[..2]] = used.GetValueOrDefault(key[..2], 0) + v;
+22 -2
View File
@@ -11,6 +11,12 @@ namespace Elwig.Helpers.Billing {
AvNr = avnr; AvNr = avnr;
} }
public async Task Calculate() {
await DeleteInDb();
await CalculatePrices();
await CalculateModifiers();
}
protected async Task DeleteInDb() { protected async Task DeleteInDb() {
using var cnx = await AppDbContext.ConnectAsync(); using var cnx = await AppDbContext.ConnectAsync();
using (var cmd = cnx.CreateCommand()) { using (var cmd = cnx.CreateCommand()) {
@@ -27,8 +33,7 @@ namespace Elwig.Helpers.Billing {
} }
} }
public async Task CalculatePrices() { protected async Task CalculatePrices() {
await DeleteInDb();
using var cnx = await AppDbContext.ConnectAsync(); using var cnx = await AppDbContext.ConnectAsync();
var parts = new List<(int Year, int DId, int DPNr, int BktNr, string SortId, string Discr, int Value, bool MinQuw, double Oe, double Kmw)>(); var parts = new List<(int Year, int DId, int DPNr, int BktNr, string SortId, string Discr, int Value, bool MinQuw, double Oe, double Kmw)>();
@@ -65,5 +70,20 @@ namespace Elwig.Helpers.Billing {
await cmd.ExecuteNonQueryAsync(); await cmd.ExecuteNonQueryAsync();
} }
} }
protected async Task CalculateModifiers() {
using var cnx = await AppDbContext.ConnectAsync();
using var cmd = cnx.CreateCommand();
cmd.CommandText = $"""
INSERT INTO payment_delivery_part (year, did, dpnr, avnr, net_amount, mod_abs, mod_rel)
SELECT d.year, d.did, d.dpnr, {AvNr}, 0, COALESCE(m.abs, 0), COALESCE(m.rel, 0)
FROM delivery_part d
LEFT JOIN delivery_part_modifier p ON (p.year, p.did, p.dpnr) = (d.year, d.did, d.dpnr)
LEFT JOIN modifier m ON m.modid = p.modid
WHERE d.year = {Year}
ON CONFLICT DO UPDATE SET mod_abs = mod_abs + excluded.mod_abs, mod_rel = mod_rel + excluded.mod_rel;
""";
await cmd.ExecuteNonQueryAsync();
}
} }
} }
+17 -50
View File
@@ -1,8 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using IniParser; using Microsoft.Extensions.Configuration;
using IniParser.Model;
namespace Elwig.Helpers { namespace Elwig.Helpers {
public class Config { public class Config {
@@ -13,7 +12,8 @@ namespace Elwig.Helpers {
public string? DatabaseLog = null; public string? DatabaseLog = null;
public string? Branch = null; public string? Branch = null;
public IList<string?[]> Scales; public IList<string?[]> Scales;
private readonly List<string?[]> ScaleList = new(); private readonly List<string?[]> ScaleList = [];
private static readonly string[] trueValues = ["1", "true", "yes", "on"];
public Config(string filename) { public Config(string filename) {
FileName = filename; FileName = filename;
@@ -22,57 +22,24 @@ namespace Elwig.Helpers {
} }
public void Read() { public void Read() {
var parser = new FileIniDataParser(); var config = new ConfigurationBuilder().AddIniFile(FileName).Build();
IniData? ini = null;
try {
ini = parser.ReadFile(FileName, Utils.UTF8);
} catch {}
if (ini == null || !ini.TryGetKey("database.file", out string db)) { DatabaseFile = Utils.GetAbsolutePath(config["database:file"] ?? "database.sqlite3", App.DataPath);
DatabaseFile = App.DataPath + "database.sqlite3"; var log = config["database:log"];
} else if (db.Length > 1 && (db[1] == ':' || db[0] == '/' || db[0] == '\\')) { DatabaseLog = log != null ? Utils.GetAbsolutePath(log, App.DataPath) : null;
DatabaseFile = db; Branch = config["general:branch"];
} else { Debug = trueValues.Contains(config["general:debug"]?.ToLower());
DatabaseFile = App.DataPath + db;
}
if (ini == null || !ini.TryGetKey("database.log", out string log)) {
DatabaseLog = null;
} else if (log.Length > 1 && (log[1] == ':' || log[0] == '/' || log[0] == '\\')) {
DatabaseLog = log;
} else {
DatabaseLog = App.DataPath + log;
}
if (ini == null || !ini.TryGetKey("general.branch", out string branch)) {
Branch = null;
} else {
Branch = branch;
}
if (ini == null || !ini.TryGetKey("general.debug", out string debug)) {
Debug = false;
} else {
debug = debug.ToLower();
Debug = debug == "1" || debug == "true" || debug == "yes" || debug == "on";
}
var scales = config.AsEnumerable().Where(i => i.Key.StartsWith("scale.")).GroupBy(i => i.Key.Split(':')[0][6..]).Select(i => i.Key);
ScaleList.Clear(); ScaleList.Clear();
Scales = ScaleList; Scales = ScaleList;
if (ini != null) { foreach (var s in scales) {
foreach (var s in ini.Sections.Where(s => s.SectionName.StartsWith("scale."))) { string? scaleLog = config[$"scale.{s}:log"];
string? scaleLog = null; if (scaleLog != null) scaleLog = Utils.GetAbsolutePath(scaleLog, App.DataPath);
if (s.Keys["log"] != null) { ScaleList.Add([
scaleLog = s.Keys["log"]; s, config[$"scale.{s}:type"], config[$"scale.{s}:model"], config[$"scale.{s}:connection"],
if (scaleLog.Length <= 1 || (scaleLog[1] != ':' && scaleLog[0] != '/' && scaleLog[0] != '\\')) { config[$"scale.{s}:empty"], config[$"scale.{s}:filling"], config[$"scale.{s}:limit"], scaleLog
scaleLog = App.DataPath + scaleLog; ]);
}
}
ScaleList.Add(new string?[] {
s.SectionName[6..], s.Keys["type"], s.Keys["model"], s.Keys["connection"],
s.Keys["empty"], s.Keys["filling"], s.Keys["limit"], scaleLog
});
}
} }
} }
+15
View File
@@ -11,6 +11,7 @@ using Elwig.Dialogs;
using System.Text; using System.Text;
using System.Numerics; using System.Numerics;
using Elwig.Models.Entities; using Elwig.Models.Entities;
using System.IO;
namespace Elwig.Helpers { namespace Elwig.Helpers {
public static partial class Utils { public static partial class Utils {
@@ -47,6 +48,16 @@ namespace Elwig.Helpers {
[GeneratedRegex(@"^(.*?) +([0-9].*)$", RegexOptions.Compiled)] [GeneratedRegex(@"^(.*?) +([0-9].*)$", RegexOptions.Compiled)]
private static partial Regex GeneratedAddressRegex(); private static partial Regex GeneratedAddressRegex();
public static readonly KeyValuePair<string, string>[] PhoneNrTypes = [
new("landline", "Tel.-Nr. (Festnetz)"),
new("mobile", "Tel.-Nr. (mobil)"),
new("fax", "Fax-Nr."),
];
public static string PhoneNrTypeToString(string type) {
return PhoneNrTypes.Where(t => t.Key == type).Select(t => t.Value).FirstOrDefault(type);
}
private static readonly ushort[] Crc16ModbusTable = { private static readonly ushort[] Crc16ModbusTable = {
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
@@ -345,5 +356,9 @@ namespace Elwig.Helpers {
} }
return output.OrderByDescending(l => l.Count()); return output.OrderByDescending(l => l.Count());
} }
public static string GetAbsolutePath(string path, string basePath) {
return (path.Length > 1 && (path[1] == ':' || path[0] == '/' || path[0] == '\\')) ? Path.Combine(basePath, path) : path;
}
} }
} }
+36 -14
View File
@@ -1,4 +1,5 @@
using Elwig.Helpers; using Elwig.Helpers;
using Elwig.Helpers.Billing;
using Elwig.Models.Entities; using Elwig.Models.Entities;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System; using System;
@@ -27,11 +28,11 @@ namespace Elwig.Models.Dtos {
MgNr = mgnr; MgNr = mgnr;
} }
public static async Task<IDictionary<int, CreditNoteData>> ForPaymentVariant(DbSet<CreditNoteRowSingle> table, int year, int avnr) { public static async Task<IDictionary<int, CreditNoteData>> ForPaymentVariant(DbSet<CreditNoteRowSingle> table, DbSet<Season> seasons, int year, int avnr) {
return (await FromDbSet(table, year, avnr)) return (await FromDbSet(table, year, avnr))
.GroupBy( .GroupBy(
r => new { r.Year, r.AvNr, r.MgNr, r.TgNr, r.DId, r.DPNr }, r => new { r.Year, r.AvNr, r.MgNr, r.TgNr, r.DId, r.DPNr },
(k, g) => new CreditNoteRow(g)) (k, g) => new CreditNoteRow(g, seasons))
.GroupBy( .GroupBy(
r => new { r.Year, r.AvNr, r.MgNr, r.TgNr }, r => new { r.Year, r.AvNr, r.MgNr, r.TgNr },
(k, g) => new CreditNoteData(g, k.Year, k.TgNr, mgnr: k.MgNr)) (k, g) => new CreditNoteData(g, k.Year, k.TgNr, mgnr: k.MgNr))
@@ -43,17 +44,20 @@ namespace Elwig.Models.Dtos {
var v = avnr?.ToString() ?? "NULL"; var v = avnr?.ToString() ?? "NULL";
var m = mgnr?.ToString() ?? "NULL"; var m = mgnr?.ToString() ?? "NULL";
return await table.FromSqlRaw($""" return await table.FromSqlRaw($"""
SELECT d.year, c.tgnr, p.avnr, d.mgnr, d.did, d.lsnr, d.dpnr, b.bktnr, d.sortid, b.discr, b.value, p.price, p.amount, SELECT d.year, c.tgnr, v.avnr, d.mgnr, d.did, d.lsnr, d.dpnr, d.weight, d.modifiers,
v.name AS variant, a.name AS attribute, q.name AS quality_level, d.oe, d.kmw b.bktnr, d.sortid, b.discr, b.value, pb.price, pb.amount, p.net_amount, p.amount AS total_amount,
s.name AS variant, a.name AS attribute, q.name AS quality_level, d.oe, d.kmw
FROM v_delivery d FROM v_delivery d
JOIN wine_variety v ON d.sortid = v.sortid JOIN wine_variety s ON s.sortid = d.sortid
LEFT JOIN wine_attribute a ON a.attrid = d.attrid LEFT JOIN wine_attribute a ON a.attrid = d.attrid
JOIN wine_quality_level q ON q.qualid = d.qualid JOIN wine_quality_level q ON q.qualid = d.qualid
LEFT JOIN delivery_part_bucket b ON (b.year, b.did, b.dpnr) = (d.year, d.did, d.dpnr) LEFT JOIN delivery_part_bucket b ON (b.year, b.did, b.dpnr) = (d.year, d.did, d.dpnr)
LEFT JOIN payment_delivery_part_bucket p ON (p.year, p.did, p.dpnr, p.bktnr) = (b.year, b.did, b.dpnr, b.bktnr) LEFT JOIN payment_variant v ON v.year = d.year
LEFT JOIN credit c ON (c.year, c.avnr, c.mgnr) = (d.year, p.avnr, d.mgnr) LEFT JOIN payment_delivery_part p ON (p.year, p.did, p.dpnr, p.avnr) = (d.year, d.did, d.dpnr, v.avnr)
WHERE b.value > 0 AND (d.year = {y} OR {y} IS NULL) AND (p.avnr = {v} OR {v} IS NULL OR p.avnr IS NULL) AND (d.mgnr = {m} OR {m} IS NULL) LEFT JOIN payment_delivery_part_bucket pb ON (pb.year, pb.did, pb.dpnr, pb.bktnr) = (b.year, b.did, b.dpnr, b.bktnr)
ORDER BY d.year, p.avnr, d.mgnr, d.lsnr, d.dpnr LEFT JOIN credit c ON (c.year, c.avnr, c.mgnr) = (d.year, v.avnr, d.mgnr)
WHERE b.value > 0 AND (d.year = {y} OR {y} IS NULL) AND (v.avnr = {v} OR {v} IS NULL) AND (d.mgnr = {m} OR {m} IS NULL)
ORDER BY d.year, v.avnr, d.mgnr, d.lsnr, d.dpnr
""").ToListAsync(); """).ToListAsync();
} }
} }
@@ -73,28 +77,38 @@ namespace Elwig.Models.Dtos {
public string QualityLevel; public string QualityLevel;
public (double Oe, double Kmw) Gradation; public (double Oe, double Kmw) Gradation;
public (string Name, int Value, decimal? Price, decimal? Amount)[] Buckets; public (string Name, int Value, decimal? Price, decimal? Amount)[] Buckets;
public decimal? TotalModifiers;
public decimal? Amount;
public CreditNoteRow(IEnumerable<CreditNoteRowSingle> rows) { public CreditNoteRow(IEnumerable<CreditNoteRowSingle> rows, DbSet<Season> seasons) {
var f = rows.First(); var f = rows.First();
Year = f.Year; Year = f.Year;
TgNr = f.TgNr; TgNr = f.TgNr;
MgNr = f.MgNr; MgNr = f.MgNr;
var season = seasons.Find(Year);
LsNr = f.LsNr; LsNr = f.LsNr;
DPNr = f.DPNr; DPNr = f.DPNr;
Variant = f.Variant; Variant = f.Variant;
Attribute = f.Attribute; Attribute = f.Attribute;
Modifiers = Array.Empty<string>(); // TODO var modifiers = (IEnumerable<Modifier>)(f.Modifiers ?? "").Split(',')
.Select(m => season?.Modifiers.FirstOrDefault(s => s.ModId == m))
.Where(m => m != null)
.OrderBy(m => m.Ordering)
.ToList();
Modifiers = modifiers.Select(m => m.Name).ToArray();
QualityLevel = f.QualityLevel; QualityLevel = f.QualityLevel;
Gradation = (f.Oe, f.Kmw); Gradation = (f.Oe, f.Kmw);
Buckets = rows Buckets = rows
.Where(b => b.Value > 0) .Where(b => b.Value > 0)
.OrderByDescending(b => b.BktNr) .OrderByDescending(b => b.BktNr)
// FIXME precision
.Select(b => (b.Discr == "_" ? "ungeb." : $"geb. {f.SortId}{b.Discr}", b.Value, .Select(b => (b.Discr == "_" ? "ungeb." : $"geb. {f.SortId}{b.Discr}", b.Value,
b.Price != null ? (decimal?)Utils.DecFromDb((long)b.Price, 4) : null, b.Price != null ? season?.DecFromDb((long)b.Price) : null,
b.Amount != null ? (decimal?)Utils.DecFromDb((long)b.Amount, 4) : null)) b.Amount != null ? season?.DecFromDb((long)b.Amount) : null))
.ToArray(); .ToArray();
Amount = f.TotalAmount != null ? season?.DecFromDb((long)f.TotalAmount) : null;
var netAmount = f.NetAmount != null ? season?.DecFromDb((long)f.NetAmount) : null;
TotalModifiers = Amount - netAmount;
} }
} }
@@ -114,6 +128,10 @@ namespace Elwig.Models.Dtos {
public string LsNr { get; set; } public string LsNr { get; set; }
[Column("dpnr")] [Column("dpnr")]
public int DPNr { get; set; } public int DPNr { get; set; }
[Column("weight")]
public int Weight { get; set; }
[Column("modifiers")]
public string? Modifiers { get; set; }
[Column("bktnr")] [Column("bktnr")]
public int BktNr { get; set; } public int BktNr { get; set; }
[Column("sortid")] [Column("sortid")]
@@ -126,6 +144,10 @@ namespace Elwig.Models.Dtos {
public long? Price { get; set; } public long? Price { get; set; }
[Column("amount")] [Column("amount")]
public long? Amount { get; set; } public long? Amount { get; set; }
[Column("net_amount")]
public long? NetAmount { get; set; }
[Column("total_amount")]
public long? TotalAmount { get; set; }
[Column("variant")] [Column("variant")]
public string Variant { get; set; } public string Variant { get; set; }
[Column("attribute")] [Column("attribute")]
+1 -1
View File
@@ -163,7 +163,7 @@ namespace Elwig.Models.Entities {
[NotMapped] [NotMapped]
public IEnumerable<AreaCom> ActiveAreaCommitments => AreaCommitments public IEnumerable<AreaCom> ActiveAreaCommitments => AreaCommitments
.Where(c => c.YearFrom <= Utils.CurrentNextSeason && (c.YearTo ?? int.MaxValue) >= Utils.CurrentNextSeason); .Where(c => c.YearFrom <= Utils.CurrentYear && (c.YearTo ?? int.MaxValue) >= Utils.CurrentYear);
[InverseProperty("Member")] [InverseProperty("Member")]
public virtual BillingAddr? BillingAddress { get; private set; } public virtual BillingAddr? BillingAddress { get; private set; }
+25 -1
View File
@@ -13,9 +13,33 @@ namespace Elwig.Models.Entities {
[Column("mgnr")] [Column("mgnr")]
public int MgNr { get; set; } public int MgNr { get; set; }
[Column("net_amount")]
public long NetAmountValue { get; set; }
[NotMapped]
public decimal NetAmount {
get => Variant.Season.DecFromDb(NetAmountValue);
set => NetAmountValue = Variant.Season.DecToDb(value);
}
[Column("mod_abs")]
public long ModAbsValue { get; set; }
[NotMapped]
public decimal ModAbs {
get => Variant.Season.DecFromDb(ModAbsValue);
set => ModAbsValue = Variant.Season.DecToDb(value);
}
[Column("mod_rel")]
public double ModRelValue { get; set; }
[NotMapped]
public decimal ModRel {
get => (decimal)ModRelValue;
set => ModRelValue = (double)value;
}
[Column("amount")] [Column("amount")]
public long AmountValue { get; set; } public long AmountValue { get; set; }
[NotMapped] [NotMapped]
public decimal Amount { public decimal Amount {
get => Variant.Season.DecFromDb(AmountValue); get => Variant.Season.DecFromDb(AmountValue);
+7 -4
View File
@@ -427,8 +427,9 @@ namespace Elwig.Windows {
} }
protected void TextBox_TextChanged(object sender, RoutedEventArgs? evt) { protected void TextBox_TextChanged(object sender, RoutedEventArgs? evt) {
var input = (TextBox)sender; var input = (Control)sender;
if (SenderIsRequired(input) && input.Text.Length == 0) { var tb = input as TextBox ?? (input as UnitTextBox)?.TextBox;
if (SenderIsRequired(input) && tb?.Text.Length == 0) {
ValidateInput(input, false); ValidateInput(input, false);
ControlUtils.SetInputInvalid(input); ControlUtils.SetInputInvalid(input);
} else { } else {
@@ -472,11 +473,13 @@ namespace Elwig.Windows {
} }
protected void IntegerInput_TextChanged(object sender, TextChangedEventArgs evt) { protected void IntegerInput_TextChanged(object sender, TextChangedEventArgs evt) {
InputTextChanged((TextBox)sender, Validator.CheckInteger); // FIXME
InputTextChanged((sender as UnitTextBox)?.TextBox ?? (TextBox)sender, Validator.CheckInteger);
} }
protected void DecimalInput_TextChanged(object sender, TextChangedEventArgs evt) { protected void DecimalInput_TextChanged(object sender, TextChangedEventArgs evt) {
InputTextChanged((TextBox)sender, Validator.CheckDecimal); // FIXME
InputTextChanged((sender as UnitTextBox)?.TextBox ?? (TextBox)sender, Validator.CheckDecimal);
} }
protected void PartialDateInput_TextChanged(object sender, TextChangedEventArgs evt) { protected void PartialDateInput_TextChanged(object sender, TextChangedEventArgs evt) {
+6 -3
View File
@@ -1,4 +1,5 @@
<local:AdministrationWindow x:Class="Elwig.Windows.MemberAdminWindow" <local:AdministrationWindow
x:Class="Elwig.Windows.MemberAdminWindow"
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"
@@ -53,8 +54,10 @@
<MenuItem Header="Drucken"> <MenuItem Header="Drucken">
<MenuItem x:Name="Menu_Print_Letterhead" Header="Briefkopf drucken" <MenuItem x:Name="Menu_Print_Letterhead" Header="Briefkopf drucken"
Click="Menu_Print_Letterhead_Click"/> Click="Menu_Print_Letterhead_Click"/>
<MenuItem x:Name="Menu_Show_Memberdata" Header="Stammdatenblatt anzeigen" Click="Menu_Show_Memberdata_Click"/> <MenuItem x:Name="Menu_Show_MemberDataSheet" Header="Stammdatenblatt anzeigen" IsEnabled="False" Tag="Print"
<MenuItem x:Name="Menu_Print_Memberdata" Header="Stammdatenblatt drucken" Click="Menu_Print_Memberdata_Click"/> Click="Menu_Show_MemberDataSheet_Click"/>
<MenuItem x:Name="Menu_Print_MemberDataSheet" Header="Stammdatenblatt drucken" IsEnabled="False" Tag="Print"
Click="Menu_Print_MemberDataSheet_Click"/>
<MenuItem Header="Briefköpfe drucken"> <MenuItem Header="Briefköpfe drucken">
<MenuItem x:Name="Menu_Print_Letterheads_MgNr" Header="nach MgNr. sortiert" IsEnabled="False" Tag="Print" <MenuItem x:Name="Menu_Print_Letterheads_MgNr" Header="nach MgNr. sortiert" IsEnabled="False" Tag="Print"
Click="Menu_Print_Letterheads_MgNr_Click"/> Click="Menu_Print_Letterheads_MgNr_Click"/>
+17 -20
View File
@@ -15,29 +15,25 @@ using Elwig.Documents;
namespace Elwig.Windows { namespace Elwig.Windows {
public partial class MemberAdminWindow : AdministrationWindow { public partial class MemberAdminWindow : AdministrationWindow {
private List<string> TextFilter = new(); private List<string> TextFilter = [];
private readonly RoutedCommand CtrlF = new(); private readonly RoutedCommand CtrlF = new();
private readonly (ComboBox, TextBox, TextBox)[] PhoneNrInputs; private readonly (ComboBox, TextBox, TextBox)[] PhoneNrInputs;
private static ObservableCollection<KeyValuePair<string, string>> PhoneNrTypes { get; set; } = new() { private static ObservableCollection<KeyValuePair<string, string>> PhoneNrTypes { get; set; } = new(Utils.PhoneNrTypes);
new("landline", "Tel.-Nr. (Festnetz)"),
new("mobile", "Tel.-Nr. (mobil)"),
new("fax", "Fax-Nr."),
};
public MemberAdminWindow() { public MemberAdminWindow() {
InitializeComponent(); InitializeComponent();
CtrlF.InputGestures.Add(new KeyGesture(Key.F, ModifierKeys.Control)); CtrlF.InputGestures.Add(new KeyGesture(Key.F, ModifierKeys.Control));
CommandBindings.Add(new CommandBinding(CtrlF, FocusSearchInput)); CommandBindings.Add(new CommandBinding(CtrlF, FocusSearchInput));
ExemptInputs = new Control[] { ExemptInputs = [
SearchInput, ActiveMemberInput, MemberList, SearchInput, ActiveMemberInput, MemberList,
}; ];
RequiredInputs = new Control[] { RequiredInputs = [
MgNrInput, GivenNameInput, FamilyNameInput, MgNrInput, GivenNameInput, FamilyNameInput,
AddressInput, PlzInput, OrtInput, BillingOrtInput, AddressInput, PlzInput, OrtInput, BillingOrtInput,
BusinessSharesInput, BranchInput, DefaultKgInput BusinessSharesInput, BranchInput, DefaultKgInput
}; ];
PhoneNrInputs = new (ComboBox, TextBox, TextBox)[] { PhoneNrInputs = [
(PhoneNr1TypeInput, PhoneNr1Input, PhoneNr1CommentInput), (PhoneNr1TypeInput, PhoneNr1Input, PhoneNr1CommentInput),
(PhoneNr2TypeInput, PhoneNr2Input, PhoneNr2CommentInput), (PhoneNr2TypeInput, PhoneNr2Input, PhoneNr2CommentInput),
(PhoneNr3TypeInput, PhoneNr3Input, PhoneNr3CommentInput), (PhoneNr3TypeInput, PhoneNr3Input, PhoneNr3CommentInput),
@@ -47,7 +43,7 @@ namespace Elwig.Windows {
(PhoneNr7TypeInput, PhoneNr7Input, PhoneNr7CommentInput), (PhoneNr7TypeInput, PhoneNr7Input, PhoneNr7CommentInput),
(PhoneNr8TypeInput, PhoneNr8Input, PhoneNr8CommentInput), (PhoneNr8TypeInput, PhoneNr8Input, PhoneNr8CommentInput),
(PhoneNr9TypeInput, PhoneNr9Input, PhoneNr9CommentInput), (PhoneNr9TypeInput, PhoneNr9Input, PhoneNr9CommentInput),
}; ];
foreach (var input in PhoneNrInputs) input.Item1.ItemsSource = PhoneNrTypes; foreach (var input in PhoneNrInputs) input.Item1.ItemsSource = PhoneNrTypes;
InitializeDelayTimer(SearchInput, SearchInput_TextChanged); InitializeDelayTimer(SearchInput, SearchInput_TextChanged);
@@ -57,8 +53,9 @@ namespace Elwig.Windows {
private void Window_Loaded(object sender, RoutedEventArgs evt) { private void Window_Loaded(object sender, RoutedEventArgs evt) {
Menu_Print_Letterheads_MgNr.IsEnabled = App.IsPrintingReady; Menu_Print_Letterheads_MgNr.IsEnabled = App.IsPrintingReady;
Menu_Print_Letterheads_Name.IsEnabled = App.IsPrintingReady; Menu_Print_Letterheads_Name.IsEnabled = App.IsPrintingReady;
Menu_Show_Memberdata.IsEnabled = App.IsPrintingReady; Menu_Print_Letterheads_Plz.IsEnabled = App.IsPrintingReady;
Menu_Print_Memberdata.IsEnabled = App.IsPrintingReady; Menu_Show_MemberDataSheet.IsEnabled = App.IsPrintingReady;
Menu_Print_MemberDataSheet.IsEnabled = App.IsPrintingReady;
ActiveMemberInput.IsChecked = true; ActiveMemberInput.IsChecked = true;
UpdatePhoneNrInputVisibility(); UpdatePhoneNrInputVisibility();
@@ -339,7 +336,7 @@ namespace Elwig.Windows {
await PrintLetterheads(2); await PrintLetterheads(2);
} }
private async void Menu_Print_Memberdata_Click(object sender, RoutedEventArgs evt) { private async void Menu_Print_MemberDataSheet_Click(object sender, RoutedEventArgs evt) {
if (MemberList.SelectedItem is not Member m) if (MemberList.SelectedItem is not Member m)
return; return;
Mouse.OverrideCursor = Cursors.AppStarting; Mouse.OverrideCursor = Cursors.AppStarting;
@@ -353,7 +350,7 @@ namespace Elwig.Windows {
} }
} }
private async void Menu_Show_Memberdata_Click(object sender, RoutedEventArgs evt) { private async void Menu_Show_MemberDataSheet_Click(object sender, RoutedEventArgs evt) {
if (MemberList.SelectedItem is not Member m) if (MemberList.SelectedItem is not Member m)
return; return;
Mouse.OverrideCursor = Cursors.AppStarting; Mouse.OverrideCursor = Cursors.AppStarting;
@@ -649,8 +646,8 @@ namespace Elwig.Windows {
Menu_Member_SendEmail.IsEnabled = m.EmailAddresses.Count > 0; Menu_Member_SendEmail.IsEnabled = m.EmailAddresses.Count > 0;
Menu_Print_Letterhead.IsEnabled = true; Menu_Print_Letterhead.IsEnabled = true;
Menu_Show_Memberdata.IsEnabled = true; Menu_Show_MemberDataSheet.IsEnabled = true;
Menu_Print_Memberdata.IsEnabled = true; Menu_Print_MemberDataSheet.IsEnabled = true;
FinishInputFilling(); FinishInputFilling();
} }
@@ -658,8 +655,8 @@ namespace Elwig.Windows {
new protected void ClearInputs(bool validate = false) { new protected void ClearInputs(bool validate = false) {
Menu_Member_SendEmail.IsEnabled = false; Menu_Member_SendEmail.IsEnabled = false;
Menu_Print_Letterhead.IsEnabled = false; Menu_Print_Letterhead.IsEnabled = false;
Menu_Show_Memberdata.IsEnabled = false; Menu_Show_MemberDataSheet.IsEnabled = false;
Menu_Print_Memberdata.IsEnabled = false; Menu_Print_MemberDataSheet.IsEnabled = false;
StatusDeliveriesLastSeason.Text = $"Lieferungen ({Utils.CurrentLastSeason - 1}): -"; StatusDeliveriesLastSeason.Text = $"Lieferungen ({Utils.CurrentLastSeason - 1}): -";
StatusDeliveriesThisSeason.Text = $"Lieferungen ({Utils.CurrentLastSeason}): -"; StatusDeliveriesThisSeason.Text = $"Lieferungen ({Utils.CurrentLastSeason}): -";
StatusAreaCommitment.Text = "Gebundene Fläche: -"; StatusAreaCommitment.Text = "Gebundene Fläche: -";
+2 -2
View File
@@ -75,7 +75,7 @@ namespace Elwig.Windows {
CalculateButton.IsEnabled = false; CalculateButton.IsEnabled = false;
Mouse.OverrideCursor = Cursors.AppStarting; Mouse.OverrideCursor = Cursors.AppStarting;
var b = new BillingVariant(v.Year, v.AvNr); var b = new BillingVariant(v.Year, v.AvNr);
await b.CalculatePrices(); await b.Calculate();
Mouse.OverrideCursor = null; Mouse.OverrideCursor = null;
CalculateButton.IsEnabled = true; CalculateButton.IsEnabled = true;
} }
@@ -110,7 +110,7 @@ namespace Elwig.Windows {
members = members.OrderBy(m => m.MgNr); members = members.OrderBy(m => m.MgNr);
IEnumerable<Member> list = await members.ToListAsync(); IEnumerable<Member> list = await members.ToListAsync();
var data = await CreditNoteData.ForPaymentVariant(Context.CreditNoteRows, v.Year, v.AvNr); var data = await CreditNoteData.ForPaymentVariant(Context.CreditNoteRows, Context.Seasons, v.Year, v.AvNr);
var payments = await Context.MemberPayments.Where(p => p.Year == v.Year && p.AvNr == v.AvNr).ToDictionaryAsync(c => c.MgNr); var payments = await Context.MemberPayments.Where(p => p.Year == v.Year && p.AvNr == v.AvNr).ToDictionaryAsync(c => c.MgNr);
using var doc = Document.Merge(list.Select(m => using var doc = Document.Merge(list.Select(m =>
new CreditNote(Context, payments[m.MgNr], data[m.MgNr]) new CreditNote(Context, payments[m.MgNr], data[m.MgNr])
+1 -1
View File
@@ -1,7 +1,7 @@
[general] [general]
; Only needed, if more than one branch is stored in database ; Only needed, if more than one branch is stored in database
branch = Zweigstelle ;branch = Zweigstelle
;debug = true ;debug = true
[database] [database]
+1 -1
View File
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0-windows</TargetFramework> <TargetFramework>net8.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>