diff --git a/Elwig/Documents/DeliveryConfirmation.cshtml.cs b/Elwig/Documents/DeliveryConfirmation.cshtml.cs index 16fede2..c4aece8 100644 --- a/Elwig/Documents/DeliveryConfirmation.cshtml.cs +++ b/Elwig/Documents/DeliveryConfirmation.cshtml.cs @@ -10,15 +10,21 @@ namespace Elwig.Documents { public int Year; public IEnumerable Deliveries; - public DeliveryConfirmation(AppDbContext ctx, int year, Member m) : base($"Anlieferungsbestätigung {year} – {m.Name}", m) { + public DeliveryConfirmation(AppDbContext ctx, int year, Member m) : + base($"Anlieferungsbestätigung {year} – {((IAddress?)m.BillingAddress ?? m).Name}", m) { Year = year; - DocumentId = $"Anl.-Best. {Year}/{m.MgNr}"; + ShowDateAndLocation = true; + UseBillingAddress = true; + // FIXME footer in merged documents + //DocumentId = $"Anl.-Best. {Year}/{m.MgNr}"; Deliveries = ctx.DeliveryParts.FromSqlRaw($""" SELECT p.* FROM v_delivery v JOIN delivery_part p ON (p.year, p.did, p.dpnr) = (v.year, v.did, v.dpnr) WHERE (v.year, v.mgnr) = ({Year}, {m.MgNr}) - ORDER BY v.sortid, v.abgewertet ASC, LENGTH(v.attributes) DESC, COALESCE(v.attributes, '~'), v.kmw DESC, v.lsnr, v.dpnr + ORDER BY v.sortid, v.abgewertet ASC, + COALESCE(LENGTH(v.attributes), 0) ASC, attribute_prio DESC, COALESCE(v.attributes, '~'), + v.kmw DESC, v.lsnr, v.dpnr """) .ToList(); } diff --git a/Elwig/Documents/style-deliveryconfirmation.css b/Elwig/Documents/style-deliveryconfirmation.css index a164788..ff7fb13 100644 --- a/Elwig/Documents/style-deliveryconfirmation.css +++ b/Elwig/Documents/style-deliveryconfirmation.css @@ -46,6 +46,10 @@ table.delivery-confirmation tr.new td { border-top: 0.5pt solid black; } +table.delivery-confirmation tr:not(.first) { + break-before: avoid; +} + table.delivery-confirmation tr:not(.first) td { padding-top: 0; } diff --git a/Elwig/Helpers/AppDbUpdater.cs b/Elwig/Helpers/AppDbUpdater.cs index 3e3b3c9..ca5179d 100644 --- a/Elwig/Helpers/AppDbUpdater.cs +++ b/Elwig/Helpers/AppDbUpdater.cs @@ -157,21 +157,25 @@ namespace Elwig.Helpers { ExecuteNonQuery(cnx, "DROP VIEW v_delivery"); ExecuteNonQuery(cnx, """ CREATE VIEW v_delivery AS - SELECT p.year, p.did, p.dpnr, - d.date, d.time, d.zwstid, d.lnr, d.lsnr, - m.mgnr, m.family_name, m.given_name, - p.sortid, p.weight, p.kmw, ROUND(p.kmw * (4.54 + 0.022 * p.kmw), 0) AS oe, p.qualid, p.hkid, p.kgnr, p.rdnr, - p.qualid IN (SELECT l.qualid FROM wine_quality_level l WHERE NOT l.predicate AND (p.kmw >= l.min_kmw OR l.min_kmw IS NULL) ORDER BY l.min_kmw DESC LIMIT 1,100) AS abgewertet, - p.qualid NOT IN ('WEI', 'RSW', 'LDW') AS min_quw, - GROUP_CONCAT(DISTINCT a.attrid) as attributes, GROUP_CONCAT(DISTINCT o.modid) as modifiers, - d.comment, p.comment as part_comment - FROM delivery_part p - JOIN delivery d ON (d.year, d.did) = (p.year, p.did) - JOIN member m ON m.mgnr = d.mgnr - LEFT JOIN delivery_part_attribute a ON (a.year, a.did, a.dpnr) = (p.year, p.did, p.dpnr) - LEFT JOIN delivery_part_modifier o ON (o.year, o.did, o.dpnr) = (p.year, p.did, p.dpnr) - GROUP BY p.year, p.did, p.dpnr - ORDER BY p.year, p.did, p.dpnr; + SELECT s.*, GROUP_CONCAT(o.modid) AS modifiers + FROM (SELECT p.year, p.did, p.dpnr, + d.date, d.time, d.zwstid, d.lnr, d.lsnr, + m.mgnr, m.family_name, m.given_name, + p.sortid, p.weight, p.kmw, ROUND(p.kmw * (4.54 + 0.022 * p.kmw), 0) AS oe, p.qualid, p.hkid, p.kgnr, p.rdnr, + p.qualid IN (SELECT l.qualid FROM wine_quality_level l WHERE NOT l.predicate AND (p.kmw >= l.min_kmw OR l.min_kmw IS NULL) ORDER BY l.min_kmw DESC LIMIT 1,100) AS abgewertet, + p.qualid NOT IN ('WEI', 'RSW', 'LDW') AS min_quw, + GROUP_CONCAT(a.attrid) AS attributes, + COALESCE(SUM(a.fill_lower_bins), 0) AS attribute_prio, + d.comment, p.comment AS part_comment + FROM delivery_part p + JOIN delivery d ON (d.year, d.did) = (p.year, p.did) + JOIN member m ON m.mgnr = d.mgnr + LEFT JOIN delivery_part_attribute pa ON (pa.year, pa.did, pa.dpnr) = (p.year, p.did, p.dpnr) + LEFT JOIN wine_attribute a ON a.attrid = pa.attrid + GROUP BY p.year, p.did, p.dpnr) s + LEFT JOIN delivery_part_modifier o ON (o.year, o.did, o.dpnr) = (s.year, s.did, s.dpnr) + GROUP BY s.year, s.did, s.dpnr + ORDER BY s.year, s.did, s.dpnr; """); ExecuteNonQuery(cnx, "DROP VIEW v_bucket"); @@ -185,7 +189,9 @@ namespace Elwig.Helpers { ORDER BY year, mgnr, LENGTH(bin) DESC, bin; """); - ExecuteNonQuery(cnx, "ALTER TABLE wine_attribute ADD COLUMN fill_lower_bins INTEGER NOT NULL CHECK (fill_lower_bins IN (TRUE, FALSE)) DEFAULT FALSE"); + ExecuteNonQuery(cnx, "ALTER TABLE wine_attribute ADD COLUMN fill_lower_bins INTEGER NOT NULL CHECK (fill_lower_bins IN (0, 1, 2)) DEFAULT 0"); + + ExecuteNonQuery(cnx, "UPDATE delivery_part_bin SET bin_2 = bin_2 + bin_3, bin_3 = NULL WHERE bin_4 IS NULL"); } } } diff --git a/Elwig/Helpers/Billing/Billing.cs b/Elwig/Helpers/Billing/Billing.cs index 3f2021c..d7c7dcb 100644 --- a/Elwig/Helpers/Billing/Billing.cs +++ b/Elwig/Helpers/Billing/Billing.cs @@ -43,7 +43,8 @@ namespace Elwig.Helpers.Billing { } public async Task CalculateBins() { - var forcedAttr = Context.WineAttributes.Where(a => !a.FillLowerBins).Select(a => a.AttrId).ToArray(); + 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); var inserts = new List<(int, int, int, int, int, int, int)>(); @@ -54,7 +55,9 @@ namespace Elwig.Helpers.Billing { SELECT mgnr, did, dpnr, sortid, weight, kmw, qualid, attributes, modifiers FROM v_delivery WHERE year = {Year} - ORDER BY mgnr, sortid, abgewertet ASC, LENGTH(attributes) DESC, COALESCE(attributes, '~'), kmw DESC, lsnr, dpnr + ORDER BY mgnr, sortid, abgewertet ASC, + COALESCE(LENGTH(attributes), 0) ASC, attribute_prio DESC, COALESCE(attributes, '~'), + kmw DESC, lsnr, dpnr """; var reader = await cmd.ExecuteReaderAsync(); while (await reader.ReadAsync()) { @@ -69,11 +72,18 @@ namespace Elwig.Helpers.Billing { int lastMgNr = 0; Dictionary? rights = null; + Dictionary? obligations = null; + Dictionary used = new(); foreach (var (mgnr, did, dpnr, sortid, weight, kmw, qualid, attributes, modifiers) in deliveries) { if (lastMgNr != mgnr) { - rights = memberOblRig.GetValueOrDefault(mgnr, (new(), new())).Item2; + var or = memberOblRig.GetValueOrDefault(mgnr, (new(), new())); + rights = or.Item2; + obligations = or.Item1; + used = new(); } - if (rights == null || rights.Count == 0 || qualid == "WEI" || qualid == "RSW" || qualid == "LDW") { + if (obligations == null || rights == null || obligations.Count == 0 || rights.Count == 0 || + qualid == "WEI" || qualid == "RSW" || qualid == "LDW") + { // Mitglied hat keine Flächenbindungen, oder // Nicht mindestens Qualitätswein (QUW) -> ungebunden inserts.Add((did, dpnr, 0, 0, 0, 0, weight)); @@ -85,21 +95,24 @@ namespace Elwig.Helpers.Billing { int w = weight; int[] b = new int[4]; - foreach (var p in Utils.Permutate(attributes, attributes.Intersect(forcedAttr))) { + foreach (var p in Utils.Permutate(attributes, attributes.Intersect(attrForced))) { var c = p.Count(); var key = sortid + string.Join("", p); - if (rights.ContainsKey(key)) { + if (rights.ContainsKey(key) && obligations.ContainsKey(key)) { int i = 0; if (c == 1) { i = (p.ElementAt(0) == attributes[0]) ? 1 : 2; } else if (c == 0) { i = b.Length - 1; } - var v = Math.Max(0, Math.Min(rights[key], w)); + var vr = Math.Max(0, Math.Min(rights[key] - used.GetValueOrDefault(key, 0), w)); + var vo = Math.Max(0, Math.Min(obligations[key] - used.GetValueOrDefault(key, 0), w)); + var v = (c == 0 || p.Select(a => attrVals[a]).Min() == 2) ? vr : vo; b[i] += v; - rights[key] -= v; + used[key] = used.GetValueOrDefault(key, 0) + v; w -= v; } + if (w == 0) break; } inserts.Add((did, dpnr, b[0], b[1], b[2], b[3], weight - b[0] - b[1] - b[2] - b[3])); lastMgNr = mgnr; diff --git a/Elwig/Models/WineAttr.cs b/Elwig/Models/WineAttr.cs index 6c8ff68..8013707 100644 --- a/Elwig/Models/WineAttr.cs +++ b/Elwig/Models/WineAttr.cs @@ -14,7 +14,7 @@ namespace Elwig.Models { public int? MaxKgPerHa { get; set; } [Column("fill_lower_bins")] - public bool FillLowerBins { get; set; } + public int FillLowerBins { get; set; } [Column("active")] public bool IsActive { get; set; }