diff --git a/Elwig/Documents/DeliveryConfirmation.cshtml b/Elwig/Documents/DeliveryConfirmation.cshtml
index b2e2f63..e6bd18d 100644
--- a/Elwig/Documents/DeliveryConfirmation.cshtml
+++ b/Elwig/Documents/DeliveryConfirmation.cshtml
@@ -93,7 +93,60 @@
-
+
@if (Model.Text != null) {
diff --git a/Elwig/Documents/DeliveryConfirmation.cshtml.cs b/Elwig/Documents/DeliveryConfirmation.cshtml.cs
index 19f9aaf..7e4fb3d 100644
--- a/Elwig/Documents/DeliveryConfirmation.cshtml.cs
+++ b/Elwig/Documents/DeliveryConfirmation.cshtml.cs
@@ -10,6 +10,7 @@ namespace Elwig.Documents {
public int Year;
public IEnumerable
Deliveries;
public string? Text = App.Client.TextDeliveryConfirmation;
+ public Dictionary MemberBins;
public DeliveryConfirmation(AppDbContext ctx, int year, Member m) :
base($"Anlieferungsbestätigung {year} – {((IAddress?)m.BillingAddress ?? m).Name}", m) {
@@ -29,6 +30,7 @@ namespace Elwig.Documents {
v.kmw DESC, v.lsnr, v.dpnr
""")
.ToList();
+ MemberBins = ctx.GetMemberBins(Year, m.MgNr).GetAwaiter().GetResult();
}
}
}
diff --git a/Elwig/Documents/DeliveryNote.cshtml b/Elwig/Documents/DeliveryNote.cshtml
index bd73689..f44af88 100644
--- a/Elwig/Documents/DeliveryNote.cshtml
+++ b/Elwig/Documents/DeliveryNote.cshtml
@@ -76,61 +76,59 @@
}
@if (Model.DisplayStats > 0) {
-
-
-
-
-
-
-
-
-
-
-
-
-
- Lese @Model.Delivery.Year per @($"{Model.Date:dd.MM.yyyy}") [kg] |
- Lieferpflicht |
- Lieferrecht |
- Unterliefert |
- Noch zu liefern |
- Überliefert |
- Geliefert |
-
-
-
- @{
- string FormatRow(int obligation, int right, int sum) {
- return $"{obligation:N0} | " +
- $"{right:N0} | " +
- $"{(sum < obligation ? $"{obligation - sum:N0}" : "-")} | " +
- $"{(sum >= obligation && sum <= right ? $"{right - sum:N0}" : "-")} | " +
- $"{(sum > right ? $"{sum - right:N0}" : "-")} | " +
- $"{sum:N0} | ";
- }
- var sortids = Model.Delivery.Parts.Select(p => p.SortId).ToList();
- var bins = Model.MemberBins.GroupBy(b => b.Item1[..2]).ToDictionary(g => g.Key, g => g.Count());
+
+
+
+
+
+
+
+
+
+
+
+
+ Lese @Model.Delivery.Year per @($"{Model.Date:dd.MM.yyyy}") [kg] |
+ Lieferpflicht |
+ Lieferrecht |
+ Unterliefert |
+ Noch zu liefern |
+ Überliefert |
+ Geliefert |
+
+
+
+ @{
+ string FormatRow(int obligation, int right, int sum) {
+ return $"{obligation:N0} | " +
+ $"{right:N0} | " +
+ $"{(sum < obligation ? $"{obligation - sum:N0}" : "-")} | " +
+ $"{(sum >= obligation && sum <= right ? $"{right - sum:N0}" : "-")} | " +
+ $"{(sum > right ? $"{sum - right:N0}" : "-")} | " +
+ $"{sum:N0} | ";
}
-
- Gesamtlieferung lt. gez. GA |
- @Raw(FormatRow(Model.Member.DeliveryObligation, Model.Member.DeliveryRight, Model.Member.Deliveries.Where(d => d.Year == Model.Delivery.Year).Sum(d => d.Weight)))
+ var sortids = Model.Delivery.Parts.Select(p => p.SortId).ToList();
+ var bins = Model.MemberBins.GroupBy(b => b.Key[..2]).ToDictionary(g => g.Key, g => g.Count());
+ }
+
+ Gesamtlieferung lt. gez. GA |
+ @Raw(FormatRow(Model.Member.DeliveryObligation, Model.Member.DeliveryRight, Model.Member.Deliveries.Where(d => d.Year == Model.Delivery.Year).Sum(d => d.Weight)))
+
+ @if (Model.DisplayStats > 1) {
+
+ Flächenbindungen: |
- @if (Model.DisplayStats > 1) {
-
- Flächenbindungen: |
-
- @foreach (var (id, name, right, obligation, sum) in Model.MemberBins.OrderBy(b => b.Item1)) {
- if (right > 0 || obligation > 0 || (sum > 0 && bins[id[..2]] > 1 && !id.EndsWith('_'))) {
-
- @name |
- @Raw(FormatRow(obligation, right, sum))
-
- }
+ @foreach (var (id, (name, right, obligation, sum, _)) in Model.MemberBins.OrderBy(b => b.Key)) {
+ if (right > 0 || obligation > 0 || (sum > 0 && bins[id[..2]] > 1 && !id.EndsWith('_'))) {
+
+ @name |
+ @Raw(FormatRow(obligation, right, sum))
+
}
}
-
-
-
+ }
+
+
}
@for (int i = 0; i < 2; i++) {
diff --git a/Elwig/Documents/DeliveryNote.cshtml.cs b/Elwig/Documents/DeliveryNote.cshtml.cs
index cd9df60..43c67d0 100644
--- a/Elwig/Documents/DeliveryNote.cshtml.cs
+++ b/Elwig/Documents/DeliveryNote.cshtml.cs
@@ -7,7 +7,7 @@ namespace Elwig.Documents {
public Delivery Delivery;
public string? Text;
- public IEnumerable<(string, string, int, int, int)> MemberBins;
+ public Dictionary
MemberBins;
// 0 - none
// 1 - GA only
@@ -27,7 +27,7 @@ namespace Elwig.Documents {
$"";
Text = App.Client.TextDeliveryNote;
DocumentId = d.LsNr;
- MemberBins = ctx.GetMemberBins(d.Member, d.Year).GetAwaiter().GetResult();
+ MemberBins = ctx.GetMemberBins(d.Year, d.Member.MgNr).GetAwaiter().GetResult();
}
}
}
diff --git a/Elwig/Documents/style-deliveryconfirmation.css b/Elwig/Documents/style-deliveryconfirmation.css
index ff7fb13..fbb26f3 100644
--- a/Elwig/Documents/style-deliveryconfirmation.css
+++ b/Elwig/Documents/style-deliveryconfirmation.css
@@ -1,6 +1,7 @@
table.delivery-confirmation {
font-size: 10pt;
+ margin-bottom: 5mm;
}
table.delivery-confirmation thead {
@@ -68,3 +69,38 @@ table.delivery-confirmation tr.sum {
table.delivery-confirmation tr.sum td {
padding-top: 1mm;
}
+
+table.delivery-confirmation-stats {
+ font-size: 10pt;
+ break-inside: avoid;
+}
+
+table.delivery-confirmation-stats th,
+table.delivery-confirmation-stats td {
+ padding: 0.125mm 0;
+}
+
+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;
+}
diff --git a/Elwig/Documents/style-deliverynote.css b/Elwig/Documents/style-deliverynote.css
index 44543ff..190ded3 100644
--- a/Elwig/Documents/style-deliverynote.css
+++ b/Elwig/Documents/style-deliverynote.css
@@ -52,45 +52,45 @@ table.delivery tr.sum td {
padding-top: 1mm;
}
-table.delivery-stats {
+table.delivery-note-stats {
font-size: 8pt;
break-inside: avoid;
break-after: avoid;
}
-table.delivery-stats th,
-table.delivery-stats td {
+table.delivery-note-stats th,
+table.delivery-note-stats td {
padding: 0.125mm 0;
}
-table.delivery-stats:not(.expanded) tr.optional {
+table.delivery-note-stats:not(.expanded) tr.optional {
display: none;
}
-table.delivery-stats tr.subheading th {
+table.delivery-note-stats tr.subheading th {
text-align: left;
}
-table.delivery-stats.expanded tr.subheading:not(:has(~ tr)),
-table.delivery-stats tr.subheading:not(:has(~ tr:not(.optional))) {
+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-stats thead th {
+table.delivery-note-stats thead th {
font-weight: normal;
font-style: italic;
text-align: right;
}
-table.delivery-stats thead th:first-child {
+table.delivery-note-stats thead th:first-child {
text-align: left;
}
-table.delivery-stats td {
+table.delivery-note-stats td {
text-align: right;
}
-table.delivery-stats tbody th {
+table.delivery-note-stats tbody th {
font-weight: normal;
font-style: italic;
text-align: left;
diff --git a/Elwig/Helpers/AppDbContext.cs b/Elwig/Helpers/AppDbContext.cs
index de2ccd4..c2b0e96 100644
--- a/Elwig/Helpers/AppDbContext.cs
+++ b/Elwig/Helpers/AppDbContext.cs
@@ -53,6 +53,10 @@ namespace Elwig.Helpers {
public static string ConnectionString => $"Data Source=\"{App.Config.DatabaseFile}\"; Foreign Keys=True; Mode=ReadWrite; Cache=Default";
+ private readonly Dictionary>> _memberRightsAndObligations = new();
+ private readonly Dictionary>> _memberDeliveryBins = new();
+ private readonly Dictionary>> _memberPaymentBins = new();
+
public AppDbContext() {
if (App.Config.DatabaseLog != null) {
try {
@@ -206,26 +210,111 @@ namespace Elwig.Helpers {
}
}
- public async Task> GetMemberBins(Member m, int year) {
- using var cnx = await ConnectAsync();
- var (rights, obligations) = await Billing.Billing.GetMemberRightsObligations(cnx, year, m.MgNr);
- var bins = await Billing.Billing.GetMemberBinWeights(m.MgNr, year, cnx);
-
- var list = new List<(string, string, int, int, int)>();
- foreach (var id in rights.Keys.Union(obligations.Keys).Union(bins.Keys)) {
- var s = await WineVarieties.FindAsync(id[..2]);
- var attrIds = id[2..];
- var a = await WineAttributes.Where(a => attrIds.Contains(a.AttrId)).ToListAsync();
- var name = (s?.Name ?? "") + (a.Count > 0 ? $" ({string.Join(" / ", a.Select(a => a.Name))})" : "");
- list.Add((
- id, name,
- rights.TryGetValue(id, out var v1) ? v1 : 0,
- obligations.TryGetValue(id, out var v2) ? v2 : 0,
- bins.TryGetValue(id, out var v3) ? v3 : 0
- ));
+ private async Task FetchMemberRightsAndObligations(int year, SqliteConnection? cnx = null) {
+ var ownCnx = cnx == null;
+ cnx ??= await ConnectAsync();
+ var bins = new Dictionary>();
+ using (var cmd = cnx.CreateCommand()) {
+ cmd.CommandText = $"""
+ SELECT mgnr, t.vtrgid,
+ ROUND(SUM(COALESCE(area * min_kg_per_ha, 0)) / 10000.0) AS min_kg,
+ ROUND(SUM(COALESCE(area * max_kg_per_ha, 0)) / 10000.0) AS max_kg
+ FROM area_commitment c
+ JOIN area_commitment_type t ON t.vtrgid = c.vtrgid
+ WHERE (year_from IS NULL OR year_from <= {year}) AND
+ (year_to IS NULL OR year_to >= {year})
+ GROUP BY mgnr, t.vtrgid
+ ORDER BY LENGTH(t.vtrgid) DESC, t.vtrgid
+ """;
+ using var reader = await cmd.ExecuteReaderAsync();
+ while (await reader.ReadAsync()) {
+ var mgnr = reader.GetInt32(0);
+ var vtrgid = reader.GetString(1);
+ if (!bins.ContainsKey(mgnr)) bins[mgnr] = new();
+ bins[mgnr][vtrgid] = (reader.GetInt32(3), reader.GetInt32(2));
+ }
}
+ if (ownCnx) await cnx.DisposeAsync();
+ _memberRightsAndObligations[year] = bins;
+ }
- return list;
+ private async Task FetchMemberDeliveryBins(int year, SqliteConnection? cnx = null) {
+ var ownCnx = cnx == null;
+ cnx ??= await ConnectAsync();
+ var bins = new Dictionary>();
+ using (var cmd = cnx.CreateCommand()) {
+ cmd.CommandText = $"SELECT mgnr, bin, weight FROM v_delivery_bin WHERE year = {year}";
+ using var reader = await cmd.ExecuteReaderAsync();
+ while (await reader.ReadAsync()) {
+ var mgnr = reader.GetInt32(0);
+ var bin = reader.GetString(1);
+ if (!bins.ContainsKey(mgnr)) bins[mgnr] = new();
+ bins[mgnr][bin] = reader.GetInt32(2);
+ }
+ }
+ if (ownCnx) await cnx.DisposeAsync();
+ _memberDeliveryBins[year] = bins;
+ }
+
+ private async Task FetchMemberPaymentBins(int year, SqliteConnection? cnx = null) {
+ var ownCnx = cnx == null;
+ cnx ??= await ConnectAsync();
+ var bins = new Dictionary>();
+ using (var cmd = cnx.CreateCommand()) {
+ cmd.CommandText = $"SELECT mgnr, bin, weight FROM v_payment_bin WHERE year = {year}";
+ using var reader = await cmd.ExecuteReaderAsync();
+ while (await reader.ReadAsync()) {
+ var mgnr = reader.GetInt32(0);
+ var bin = reader.GetString(1);
+ if (!bins.ContainsKey(mgnr)) bins[mgnr] = new();
+ bins[mgnr][bin] = reader.GetInt32(2);
+ }
+ }
+ if (ownCnx) await cnx.DisposeAsync();
+ _memberPaymentBins[year] = bins;
+ }
+
+ public async Task> 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> GetMemberDeliveryBins(int year, int mgnr, SqliteConnection? cnx = null) {
+ if (!_memberDeliveryBins.ContainsKey(year))
+ await FetchMemberDeliveryBins(year, cnx);
+ return _memberDeliveryBins[year].GetValueOrDefault(mgnr, new());
+ }
+
+ public async Task> GetMemberPaymentBins(int year, int mgnr, SqliteConnection? cnx = null) {
+ if (!_memberPaymentBins.ContainsKey(year))
+ await FetchMemberPaymentBins(year, cnx);
+ return _memberPaymentBins[year].GetValueOrDefault(mgnr, new());
+ }
+
+ public async Task> GetMemberBins(int year, int mgnr, SqliteConnection? cnx = null) {
+ var ownCnx = cnx == null;
+ cnx ??= await ConnectAsync();
+ var rightsAndObligations = await GetMemberRightsAndObligations(year, mgnr, cnx);
+ var deliveryBins = await GetMemberDeliveryBins(year, mgnr, cnx);
+ var paymentBins = await GetMemberPaymentBins(year, mgnr, cnx);
+ if (ownCnx) await cnx.DisposeAsync();
+
+ var bins = new Dictionary();
+ foreach (var id in rightsAndObligations.Keys.Union(deliveryBins.Keys).Union(paymentBins.Keys)) {
+ var variety = await WineVarieties.FindAsync(id[..2]);
+ var attrIds = id[2..];
+ var attrs = await WineAttributes.Where(a => attrIds.Contains(a.AttrId)).ToListAsync();
+ var name = (variety?.Name ?? "") + (attrs.Count > 0 ? $" ({string.Join(" / ", attrs.Select(a => a.Name))})" : "");
+ bins[id] = (
+ name,
+ rightsAndObligations.GetValueOrDefault(id).Item1,
+ rightsAndObligations.GetValueOrDefault(id).Item2,
+ deliveryBins.GetValueOrDefault(id),
+ paymentBins.GetValueOrDefault(id)
+ );
+ }
+ return bins;
}
}
}
diff --git a/Elwig/Helpers/Billing/Billing.cs b/Elwig/Helpers/Billing/Billing.cs
index e006d81..171cb76 100644
--- a/Elwig/Helpers/Billing/Billing.cs
+++ b/Elwig/Helpers/Billing/Billing.cs
@@ -42,7 +42,7 @@ namespace Elwig.Helpers.Billing {
var attrVals = Context.WineAttributes.ToDictionary(a => a.AttrId, a => a.FillLowerBins);
var attrForced = attrVals.Where(a => a.Value == 0).Select(a => a.Key).ToArray();
using var cnx = await AppDbContext.ConnectAsync();
- var memberOblRig = await GetMemberRightsObligations(cnx, Year);
+ await Context.GetMemberRightsAndObligations(Year, 0, cnx);
var inserts = new List<(int, int, int, string, int)>();
var deliveries = new List<(int, int, int, string, int, double, string, string[], string[], bool?)>();
@@ -68,19 +68,15 @@ namespace Elwig.Helpers.Billing {
}
int lastMgNr = 0;
- Dictionary? rights = null;
- Dictionary? obligations = null;
+ Dictionary? rightsAndObligations = null;
Dictionary used = new();
foreach (var (mgnr, did, dpnr, sortid, weight, kmw, qualid, attributes, modifiers, gebunden) in deliveries) {
if (lastMgNr != mgnr) {
- var or = memberOblRig.GetValueOrDefault(mgnr, (new(), new()));
- rights = or.Item2;
- obligations = or.Item1;
+ rightsAndObligations = await Context.GetMemberRightsAndObligations(Year, mgnr);
used = new();
}
if ((honorGebunden && gebunden == false) ||
- obligations == null || rights == null ||
- obligations.Count == 0 || rights.Count == 0 ||
+ rightsAndObligations == null || rightsAndObligations.Count == 0 ||
qualid == "WEI" || qualid == "RSW" || qualid == "LDW")
{
// Explizit als ungebunden markiert,
@@ -98,7 +94,7 @@ namespace Elwig.Helpers.Billing {
foreach (var p in Utils.Permutate(attributes, attributes.Intersect(attrForced))) {
var c = p.Count();
var key = sortid + string.Join("", p);
- if (rights.ContainsKey(key) && obligations.ContainsKey(key)) {
+ if (rightsAndObligations.ContainsKey(key)) {
int i = 4;
if (c == 1) {
i = (p.ElementAt(0) == attributes[0]) ? 2 : 3;
@@ -106,8 +102,8 @@ namespace Elwig.Helpers.Billing {
i = 1;
}
var u = used.GetValueOrDefault(key, 0);
- var vr = Math.Max(0, Math.Min(rights[key] - u, w));
- var vo = Math.Max(0, Math.Min(obligations[key] - u, w));
+ var vr = Math.Max(0, Math.Min(rightsAndObligations[key].Item1 - u, w));
+ var vo = Math.Max(0, Math.Min(rightsAndObligations[key].Item2 - u, w));
var v = (c == 0 || p.Select(a => attrVals[a]).Min() == 2) ? vr : vo;
used[key] = u + v;
inserts.Add((did, dpnr, i, key[2..], v));
@@ -131,58 +127,5 @@ namespace Elwig.Helpers.Billing {
// TODO add second round to avoid under deliveries
}
-
- public static async Task, Dictionary)>> GetMemberRightsObligations(SqliteConnection cnx, int year, int? mgnr = null) {
- var members = new Dictionary, Dictionary)>();
-
- using var cmd = cnx.CreateCommand();
- cmd.CommandText = $"""
- SELECT mgnr, t.vtrgid,
- SUM(COALESCE(area * min_kg_per_ha, 0)) / 10000 AS min_kg,
- SUM(COALESCE(area * max_kg_per_ha, 0)) / 10000 AS max_kg
- FROM area_commitment c
- JOIN area_commitment_type t ON t.vtrgid = c.vtrgid
- WHERE ({(mgnr == null ? "NULL" : mgnr)} IS NULL OR mgnr = {(mgnr == null ? "NULL" : mgnr)}) AND
- (year_from IS NULL OR year_from <= {year}) AND
- (year_to IS NULL OR year_to >= {year})
- GROUP BY mgnr, t.vtrgid
- ORDER BY LENGTH(t.vtrgid) DESC, t.vtrgid
- """;
-
- var reader = await cmd.ExecuteReaderAsync();
- while (await reader.ReadAsync()) {
- var m = reader.GetInt32(0);
- var vtrgid = reader.GetString(1);
- if (!members.ContainsKey(m)) members[m] = (new(), new());
- members[m].Item1[vtrgid] = reader.GetInt32(2);
- members[m].Item2[vtrgid] = reader.GetInt32(3);
- }
-
- return members;
- }
-
- public static async Task<(Dictionary, Dictionary)> GetMemberRightsObligations(SqliteConnection cnx, int year, int mgnr) {
- var members = await GetMemberRightsObligations(cnx, year, (int?)mgnr);
- return members.GetValueOrDefault(mgnr, (new(), new()));
- }
-
- public static async Task> GetMemberBinWeights(int mgnr, int year, SqliteConnection cnx) {
- var bins = new Dictionary();
-
- using var cmd = cnx.CreateCommand();
- cmd.CommandText = $"""
- SELECT bin, weight
- FROM v_delivery_bin
- WHERE (year, mgnr) = ({year}, {mgnr})
- """;
-
- var reader = await cmd.ExecuteReaderAsync();
- while (await reader.ReadAsync()) {
- var bin = reader.GetString(0);
- bins[bin] = reader.GetInt32(1);
- }
-
- return bins;
- }
}
}