From 4d950b2597221af5f843d7eef3450f5cd8a87d24 Mon Sep 17 00:00:00 2001 From: Lorenz Stechauner Date: Wed, 11 Oct 2023 23:46:38 +0200 Subject: [PATCH] Billing: Add feature to calculate member/delivery bins --- Elwig/Documents/CreditNote.cshtml | 32 +++---- Elwig/Documents/CreditNote.cshtml.cs | 4 +- Elwig/Documents/DeliveryNote.cshtml | 6 +- Elwig/Documents/DeliveryNote.cshtml.cs | 4 +- Elwig/Helpers/AppDbContext.cs | 8 +- Elwig/Helpers/AppDbUpdater.cs | 97 +++++++++++++++++++- Elwig/Helpers/Billing/Billing.cs | 117 ++++++++++++++++-------- Elwig/Helpers/Billing/BillingVariant.cs | 41 +++++++++ Elwig/Helpers/Utils.cs | 14 +++ Elwig/Models/DeliveryPart.cs | 7 ++ Elwig/Models/DeliveryPartBin.cs | 46 ++++++++++ Elwig/Models/PaymentDeliveryPart.cs | 31 ------- Elwig/Models/PaymentVar.cs | 31 ------- Elwig/Models/Season.cs | 32 +++++++ Elwig/Windows/TestWindow.xaml | 3 + Elwig/Windows/TestWindow.xaml.cs | 9 ++ 16 files changed, 351 insertions(+), 131 deletions(-) create mode 100644 Elwig/Helpers/Billing/BillingVariant.cs create mode 100644 Elwig/Models/DeliveryPartBin.cs diff --git a/Elwig/Documents/CreditNote.cshtml b/Elwig/Documents/CreditNote.cshtml index 2d0ebbf..c8be8e3 100644 --- a/Elwig/Documents/CreditNote.cshtml +++ b/Elwig/Documents/CreditNote.cshtml @@ -4,7 +4,7 @@ @{ Layout = "BusinessDocument"; } @{ - var bucketNum = Model.BucketNames.Length; + var binNum = Model.BinNames.Length; }

@Model.Title

@@ -30,7 +30,7 @@ Attribut(e) Gradation Zu-/Abschläge - @Raw(string.Join("
", Model.BucketNames)) + @Raw(string.Join("
", Model.BinNames)) Betrag @@ -61,21 +61,21 @@ var pmt = part.Payment; var abs = pmt?.ModAbs == null || pmt?.ModAbs == 0 ? "-" : pmt?.ModAbs.ToString("0." + string.Concat(Enumerable.Repeat('0', Model.Precision))); var rel = pmt?.ModRel == null || pmt?.ModRel == 0 ? "-" : $"{pmt?.ModRel * 100:0.00##}"; - - @part.Delivery.LsNr - @part.DPNr - @part.Variant.Name - @string.Join(" / ", part.PartAttributes.Select(a => a.AttrId)) - @($"{part.Oe:N0}") - @($"{part.Kmw:N1}") - @abs - @rel - @Raw(FormatRow(pmt?.Buckets?.ElementAtOrDefault(0), pmt?.Prices?.ElementAtOrDefault(0))) - @($"{pmt?.Amount:N2}") + + @part.Delivery.LsNr + @part.DPNr + @part.Variant.Name + @string.Join(" / ", part.PartAttributes.Select(a => a.AttrId)) + @($"{part.Oe:N0}") + @($"{part.Kmw:N1}") + @abs + @rel + @Raw(FormatRow(pmt?.DeliveryPart.Bins?.ElementAtOrDefault(0), pmt?.Prices?.ElementAtOrDefault(0))) + @($"{pmt?.Amount:N2}") - @for (int i = 1; i < bucketNum; i++) { - - @Raw(FormatRow(pmt?.Buckets?.ElementAtOrDefault(i), pmt?.Prices?.ElementAtOrDefault(i))) + @for (int i = 1; i < binNum; i++) { + + @Raw(FormatRow(pmt?.DeliveryPart.Bins?.ElementAtOrDefault(i), pmt?.Prices?.ElementAtOrDefault(i))) } last = part.SortId; diff --git a/Elwig/Documents/CreditNote.cshtml.cs b/Elwig/Documents/CreditNote.cshtml.cs index 17ef975..d324d62 100644 --- a/Elwig/Documents/CreditNote.cshtml.cs +++ b/Elwig/Documents/CreditNote.cshtml.cs @@ -10,7 +10,7 @@ namespace Elwig.Documents { public Credit Credit; public string? Text; public string CurrencySymbol; - public string[] BucketNames; + public string[] BinNames; public int Precision; public IEnumerable Parts; @@ -27,7 +27,7 @@ namespace Elwig.Documents { Text = App.Client.TextDeliveryNote; DocumentId = $"Tr.-Gutschr. {c.TgId}"; CurrencySymbol = c.Payment.Variant.Season.Currency.Symbol ?? c.Payment.Variant.Season.Currency.Code; - BucketNames = c.Payment.Variant.BucketNames; + BinNames = c.Payment.Variant.Season.BinNames; Precision = c.Payment.Variant.Season.Precision; Parts = ctx.DeliveryParts.FromSql($""" SELECT p.* diff --git a/Elwig/Documents/DeliveryNote.cshtml b/Elwig/Documents/DeliveryNote.cshtml index 851d827..51cf053 100644 --- a/Elwig/Documents/DeliveryNote.cshtml +++ b/Elwig/Documents/DeliveryNote.cshtml @@ -109,7 +109,7 @@ $"{sum:N0}"; } var sortids = Model.Delivery.Parts.Select(p => p.SortId).ToList(); - var buckets = Model.MemberBuckets.GroupBy(b => b.Item1[..2]).ToDictionary(g => g.Key, g => g.Count()); + var bins = Model.MemberBins.GroupBy(b => b.Item1[..2]).ToDictionary(g => g.Key, g => g.Count()); } Gesamtlieferung lt. gez. GA @@ -119,8 +119,8 @@ Flächenbindungen: - @foreach (var (id, name, right, obligation, sum) in Model.MemberBuckets.OrderBy(b => b.Item1)) { - if (right > 0 || obligation > 0 || (sum > 0 && buckets[id[..2]] > 1 && !id.EndsWith('_'))) { + @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)) diff --git a/Elwig/Documents/DeliveryNote.cshtml.cs b/Elwig/Documents/DeliveryNote.cshtml.cs index 631a9d7..cd9df60 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)> MemberBuckets; + public IEnumerable<(string, string, int, int, int)> MemberBins; // 0 - none // 1 - GA only @@ -27,7 +27,7 @@ namespace Elwig.Documents { $""; Text = App.Client.TextDeliveryNote; DocumentId = d.LsNr; - MemberBuckets = ctx.GetMemberBuckets(d.Member, d.Year).GetAwaiter().GetResult(); + MemberBins = ctx.GetMemberBins(d.Member, d.Year).GetAwaiter().GetResult(); } } } diff --git a/Elwig/Helpers/AppDbContext.cs b/Elwig/Helpers/AppDbContext.cs index 8a2aeb3..5d1ca11 100644 --- a/Elwig/Helpers/AppDbContext.cs +++ b/Elwig/Helpers/AppDbContext.cs @@ -206,13 +206,13 @@ namespace Elwig.Helpers { } } - public async Task> GetMemberBuckets(Member m, int year) { + public async Task> GetMemberBins(Member m, int year) { using var cnx = await ConnectAsync(); var (rights, obligations) = await Billing.Billing.GetMemberRightsObligations(m.MgNr, year, cnx); - var buckets = await Billing.Billing.GetMemberBucketWeights(m.MgNr, year, cnx); + 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(buckets.Keys)) { + 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(); @@ -221,7 +221,7 @@ namespace Elwig.Helpers { id, name, rights.TryGetValue(id, out var v1) ? v1 : 0, obligations.TryGetValue(id, out var v2) ? v2 : 0, - buckets.TryGetValue(id, out var v3) ? v3 : 0 + bins.TryGetValue(id, out var v3) ? v3 : 0 )); } diff --git a/Elwig/Helpers/AppDbUpdater.cs b/Elwig/Helpers/AppDbUpdater.cs index 4f20c54..9685181 100644 --- a/Elwig/Helpers/AppDbUpdater.cs +++ b/Elwig/Helpers/AppDbUpdater.cs @@ -4,7 +4,7 @@ using System; namespace Elwig.Helpers { public static class AppDbUpdater { - public static readonly int RequiredSchemaVersion = 2; + public static readonly int RequiredSchemaVersion = 3; private static int _versionOffset = 0; private static readonly Action[] _updaters = new[] { @@ -80,6 +80,99 @@ namespace Elwig.Helpers { ExecuteNonQuery(cnx, "ALTER TABLE delivery_part ADD COLUMN weighing_reason TEXT CHECK(NOT (manual_weighing = FALSE AND weighing_reason IS NOT NULL))"); } - private static void UpdateDbSchema_2_To_3(SqliteConnection cnx) { } + private static void UpdateDbSchema_2_To_3(SqliteConnection cnx) { + ExecuteNonQuery(cnx, "ALTER TABLE season ADD COLUMN bin_1_name TEXT DEFAULT NULL"); + ExecuteNonQuery(cnx, "ALTER TABLE season ADD COLUMN bin_2_name TEXT DEFAULT NULL"); + ExecuteNonQuery(cnx, "ALTER TABLE season ADD COLUMN bin_3_name TEXT DEFAULT NULL"); + ExecuteNonQuery(cnx, "ALTER TABLE season ADD COLUMN bin_4_name TEXT DEFAULT NULL"); + ExecuteNonQuery(cnx, "ALTER TABLE season ADD COLUMN bin_5_name TEXT DEFAULT NULL"); + ExecuteNonQuery(cnx, "ALTER TABLE season ADD COLUMN bin_6_name TEXT DEFAULT NULL"); + ExecuteNonQuery(cnx, "ALTER TABLE season ADD COLUMN bin_7_name TEXT DEFAULT NULL"); + ExecuteNonQuery(cnx, "ALTER TABLE season ADD COLUMN bin_8_name TEXT DEFAULT NULL"); + ExecuteNonQuery(cnx, "ALTER TABLE season ADD COLUMN bin_9_name TEXT DEFAULT NULL"); + ExecuteNonQuery(cnx, """ + UPDATE season + SET bin_1_name = n.bucket_1_name, + bin_2_name = n.bucket_2_name, + bin_3_name = n.bucket_3_name, + bin_4_name = n.bucket_4_name, + bin_5_name = n.bucket_5_name, + bin_6_name = n.bucket_6_name, + bin_7_name = n.bucket_7_name, + bin_8_name = n.bucket_8_name, + bin_9_name = n.bucket_9_name + FROM (SELECT year, bucket_1_name, bucket_2_name, bucket_3_name, bucket_4_name, bucket_5_name, bucket_6_name, bucket_7_name, bucket_8_name, bucket_9_name + FROM payment_variant GROUP BY year HAVING avnr = MAX(avnr)) AS n + WHERE season.year = n.year + """); + ExecuteNonQuery(cnx, """ + CREATE TABLE delivery_part_bin ( + year INTEGER NOT NULL, + did INTEGER NOT NULL, + dpnr INTEGER NOT NULL, + + bin_1 INTEGER DEFAULT NULL, + bin_2 INTEGER DEFAULT NULL, + bin_3 INTEGER DEFAULT NULL, + bin_4 INTEGER DEFAULT NULL, + bin_5 INTEGER DEFAULT NULL, + bin_6 INTEGER DEFAULT NULL, + bin_7 INTEGER DEFAULT NULL, + bin_8 INTEGER DEFAULT NULL, + bin_9 INTEGER DEFAULT NULL, + + CONSTRAINT pk_delivery_part_bin PRIMARY KEY (year, did, dpnr), + CONSTRAINT fk_delivery_part_bin_delivery_part FOREIGN KEY (year, did, dpnr) REFERENCES delivery_part (year, did, dpnr) + ON UPDATE CASCADE + ON DELETE CASCADE + ) STRICT; + """); + ExecuteNonQuery(cnx, """ + INSERT INTO delivery_part_bin + (year, did, dpnr, bin_1, bin_2, bin_3, bin_4, bin_5, bin_6, bin_7, bin_8, bin_9) + SELECT year, did, dpnr, bucket_1, bucket_2, bucket_3, bucket_4, bucket_5, bucket_6, bucket_7, bucket_8, bucket_9 + FROM payment_delivery_part + WHERE COALESCE(bucket_1, bucket_2, bucket_3, bucket_4, bucket_5, bucket_6, bucket_7, bucket_8, bucket_9) IS NOT NULL + ON CONFLICT DO NOTHING; + """); + ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_1"); + ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_2"); + ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_3"); + ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_4"); + ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_5"); + ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_6"); + ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_7"); + ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_8"); + ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_9"); + ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_1_name"); + ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_2_name"); + ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_3_name"); + ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_4_name"); + ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_5_name"); + ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_6_name"); + ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_7_name"); + ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_8_name"); + ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_9_name"); + + 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; + """); + } } } diff --git a/Elwig/Helpers/Billing/Billing.cs b/Elwig/Helpers/Billing/Billing.cs index 72bceb2..c584d60 100644 --- a/Elwig/Helpers/Billing/Billing.cs +++ b/Elwig/Helpers/Billing/Billing.cs @@ -1,4 +1,5 @@ using Microsoft.Data.Sqlite; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -6,41 +7,59 @@ using System.Threading.Tasks; namespace Elwig.Helpers.Billing { public class Billing { - private readonly int Year; - private readonly int AvNr; - private readonly AppDbContext Context; - private readonly Dictionary Attributes; - private readonly Dictionary Modifiers; - private readonly Dictionary AreaComTypes; + protected readonly int Year; + protected readonly AppDbContext Context; + protected readonly Dictionary Attributes; + protected readonly Dictionary Modifiers; + protected readonly Dictionary AreaComTypes; - public Billing(int year, int avnr) { + public Billing(int year) { Year = year; - AvNr = avnr; Context = new AppDbContext(); Attributes = Context.WineAttributes.ToDictionary(a => a.AttrId, a => a.Name); Modifiers = Context.Modifiers.Where(m => m.Year == Year).ToDictionary(m => m.ModId, m => (m.Abs, m.Rel)); AreaComTypes = Context.AreaCommitmentTypes.ToDictionary(v => v.VtrgId, v => (v.SortId, v.AttrId1, v.AttrId2, v.Discriminator, v.MinKgPerHa, v.MaxKgPerHa, v.PenaltyAmount)); } - protected async Task DeleteInDb() { + public async Task FinishSeason() { using var cnx = await AppDbContext.ConnectAsync(); using (var cmd = cnx.CreateCommand()) { - cmd.CommandText = $"DELETE FROM payment_delivery_part WHERE (year, avnr) = ({Year}, {AvNr})"; - await cmd.ExecuteNonQueryAsync(); - } - using (var cmd = cnx.CreateCommand()) { - cmd.CommandText = $"DELETE FROM payment_member WHERE (year, avnr) = ({Year}, {AvNr})"; + cmd.CommandText = $""" + UPDATE season + SET (start_date, end_date) = (SELECT MIN(date), MAX(date) FROM delivery WHERE year = {Year}), + bin_1_name = 'gebunden mit zwei Attributen', + bin_2_name = 'gebunden mit (erstem) Attribut', + bin_3_name = 'gebunden mit zweitem Attribut', + bin_4_name = 'gebunden ohne Attribut', + bin_5_name = 'ungebunden' + WHERE year = {Year} + """; await cmd.ExecuteNonQueryAsync(); } } - public async Task Calculate() { - await DeleteInDb(); - var tasks = new List(); - foreach (var mgnr in Context.Members.Select(m => m.MgNr)) { - tasks.Add(Task.Run(() => CalculateMember(mgnr))); + public async Task CalculateBins() { + var inserts = new List<(int, int, int, int, int, int, int)>(); + foreach (var mgnr in Context.Members.Where(m => m.IsActive).OrderBy(m => m.MgNr).Select(m => m.MgNr)) { + inserts.AddRange(await CalculateMemberBins(mgnr)); } - await Task.WhenAll(tasks); + using var cnx = await AppDbContext.ConnectAsync(); + using var cmd = cnx.CreateCommand(); + cmd.CommandText = $""" + INSERT INTO delivery_part_bin (year, did, dpnr, bin_1, bin_2, bin_3, bin_4, bin_5) + VALUES {string.Join(",\n ", inserts.Select(i => $"({Year}, {i.Item1}, {i.Item2}, {i.Item3}, {i.Item4}, {i.Item5}, {i.Item6}, {i.Item7})"))} + ON CONFLICT DO UPDATE + SET bin_1 = excluded.bin_1, + bin_2 = excluded.bin_2, + bin_3 = excluded.bin_3, + bin_4 = excluded.bin_4, + bin_5 = excluded.bin_5, + bin_6 = NULL, + bin_7 = NULL, + bin_8 = NULL, + bin_9 = NULL; + """; + await cmd.ExecuteNonQueryAsync(); } public static async Task<(Dictionary, Dictionary)> GetMemberRightsObligations(int mgnr, int year, SqliteConnection cnx) { @@ -69,26 +88,26 @@ namespace Elwig.Helpers.Billing { return (rights, obligations); } - public static async Task> GetMemberBucketWeights(int mgnr, int year, SqliteConnection cnx) { - var buckets = new Dictionary(); + public static async Task> GetMemberBinWeights(int mgnr, int year, SqliteConnection cnx) { + var bins = new Dictionary(); using var cmd = cnx.CreateCommand(); cmd.CommandText = $""" - SELECT bucket, weight - FROM v_bucket + SELECT bin, weight + FROM v_bin WHERE (year, mgnr) = ({year}, {mgnr}) """; var reader = await cmd.ExecuteReaderAsync(); while (await reader.ReadAsync()) { - var bucket = reader.GetString(0); - buckets[bucket] = reader.GetInt32(1); + var bin = reader.GetString(0); + bins[bin] = reader.GetInt32(1); } - return buckets; + return bins; } - protected async Task CalculateMember(int mgnr) { + protected async Task> CalculateMemberBins(int mgnr) { using var cnx = await AppDbContext.ConnectAsync(); var (rights, obligations) = await GetMemberRightsObligations(mgnr, Year, cnx); @@ -98,33 +117,51 @@ namespace Elwig.Helpers.Billing { SELECT did, dpnr, sortid, weight, kmw, qualid, attributes, modifiers FROM v_delivery WHERE (year, mgnr) = ({Year}, {mgnr}) - ORDER BY kmw DESC, weight DESC, did, dpnr + ORDER BY sortid, abgewertet ASC, LENGTH(attributes) DESC, COALESCE(attributes, '~'), kmw DESC, lsnr, dpnr """; var reader = await cmd.ExecuteReaderAsync(); while (await reader.ReadAsync()) { deliveries.Add(( reader.GetInt32(0), reader.GetInt32(1), reader.GetString(2), reader.GetInt32(3), - reader.GetDouble(4), reader.GetString(5), reader.GetString(6).Split(","), reader.GetString(7).Split(",") + reader.GetDouble(4), reader.GetString(5), + reader.IsDBNull(6) ? Array.Empty() : reader.GetString(6).Split(",").Order().ToArray(), + reader.IsDBNull(7) ? Array.Empty() : reader.GetString(7).Split(",").Order().ToArray() )); } } + List<(int, int, int, int, int, int, int)> inserts = new(); foreach (var (did, dpnr, sortid, weight, kmw, qualid, attributes, modifiers) in deliveries) { if (qualid == "WEI" || qualid == "RSW" || qualid == "LDW") { - // Nicht mindestens Qualitätswein (QUW) - using var cmd = cnx.CreateCommand(); - // TODO - cmd.CommandText = $""" - INSERT INTO payment_delivery_part (year, did, dpnr, avnr, mod_abs, mod_rel, ) - VALUES ({Year}, {did}, {dpnr}, {AvNr}, ) - """; - await cmd.ExecuteNonQueryAsync(); + // Nicht mindestens Qualitätswein (QUW) -> ungebunden + inserts.Add((did, dpnr, 0, 0, 0, 0, weight)); continue; } - // TODO - } + if (attributes.Length > 2) + throw new NotSupportedException(); + int w = weight; + int[] b = new int[4]; + foreach (var p in Utils.Permutate(attributes)) { + var c = p.Count(); + var key = sortid + string.Join("", p); + if (rights.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)); + b[i] += v; + rights[key] -= v; + w -= v; + } + } + inserts.Add((did, dpnr, b[0], b[1], b[2], b[3], weight - b[0] - b[1] - b[2] - b[3])); + } + return inserts; } } } diff --git a/Elwig/Helpers/Billing/BillingVariant.cs b/Elwig/Helpers/Billing/BillingVariant.cs new file mode 100644 index 0000000..4038e3d --- /dev/null +++ b/Elwig/Helpers/Billing/BillingVariant.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Elwig.Helpers.Billing { + public class BillingVariant : Billing { + + private readonly int AvNr; + + public BillingVariant(int year, int avnr) : base(year) { + AvNr = avnr; + } + + protected async Task DeleteInDb() { + using var cnx = await AppDbContext.ConnectAsync(); + using (var cmd = cnx.CreateCommand()) { + cmd.CommandText = $"DELETE FROM payment_delivery_part WHERE (year, avnr) = ({Year}, {AvNr})"; + await cmd.ExecuteNonQueryAsync(); + } + using (var cmd = cnx.CreateCommand()) { + cmd.CommandText = $"DELETE FROM payment_member WHERE (year, avnr) = ({Year}, {AvNr})"; + await cmd.ExecuteNonQueryAsync(); + } + } + + public async Task CalculatePrices() { + await DeleteInDb(); + var tasks = new List(); + foreach (var mgnr in Context.Members.Select(m => m.MgNr)) { + tasks.Add(Task.Run(() => CalculateMemberPrices(mgnr))); + } + await Task.WhenAll(tasks); + } + + protected async Task CalculateMemberPrices(int mgnr) { + + } + } +} diff --git a/Elwig/Helpers/Utils.cs b/Elwig/Helpers/Utils.cs index 6c6387e..a2470c3 100644 --- a/Elwig/Helpers/Utils.cs +++ b/Elwig/Helpers/Utils.cs @@ -326,5 +326,19 @@ namespace Elwig.Helpers { return (familyName, fullName.Replace(familyName, "").Replace(" ", " ").Trim()); } } + + public static IEnumerable> Permutate(IEnumerable input) { + List> output = new(); + for (int i = 0; i < Math.Pow(2, input.Count()); i++) { + List t = new(); + for (int j = 0; j < 30; j++) { + if ((i & (1 << j)) != 0) { + t.Add(input.ElementAt(j)); + } + } + output.Add(t); + } + return output.OrderByDescending(l => l.Count()); + } } } diff --git a/Elwig/Models/DeliveryPart.cs b/Elwig/Models/DeliveryPart.cs index a288a3b..b8583a7 100644 --- a/Elwig/Models/DeliveryPart.cs +++ b/Elwig/Models/DeliveryPart.cs @@ -113,5 +113,12 @@ namespace Elwig.Models { [NotMapped] public string OriginString => Origin.OriginString + "\n" + (Kg?.Gl != null ? $" / {Kg.Gl.Name}" : "") + (Kg != null ? $" / {Kg.AtKg.Gem.Name} / KG {Kg.AtKg.Name}" : "") + (Rd != null ? $" / Ried {Rd.Name}" : ""); + + [InverseProperty("Part")] + public virtual DeliveryPartBin Bin { get; private set; } + + [NotMapped] + public int[] Bins => (new int?[] { Bin.Bin1, Bin.Bin2, Bin.Bin3, Bin.Bin4, Bin.Bin5, Bin.Bin6, Bin.Bin7, Bin.Bin8, Bin.Bin9 }) + .Where(b => b != null).Select(b => b.Value).ToArray(); } } diff --git a/Elwig/Models/DeliveryPartBin.cs b/Elwig/Models/DeliveryPartBin.cs new file mode 100644 index 0000000..576c833 --- /dev/null +++ b/Elwig/Models/DeliveryPartBin.cs @@ -0,0 +1,46 @@ +using Microsoft.EntityFrameworkCore; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Elwig.Models { + [Table("delivery_part_bin"), PrimaryKey("Year", "DId", "DPNr")] + public class DeliveryPartBin { + [Column("year")] + public int Year { get; set; } + + [Column("did")] + public int DId { get; set; } + + [Column("dpnr")] + public int DPNr { get; set; } + + [ForeignKey("Year, DId, DPNr")] + public virtual DeliveryPart Part { get; private set; } + + [Column("bin_1")] + public int? Bin1 { get; set; } + + [Column("bin_2")] + public int? Bin2 { get; set; } + + [Column("bin_3")] + public int? Bin3 { get; set; } + + [Column("bin_4")] + public int? Bin4 { get; set; } + + [Column("bin_5")] + public int? Bin5 { get; set; } + + [Column("bin_6")] + public int? Bin6 { get; set; } + + [Column("bin_7")] + public int? Bin7 { get; set; } + + [Column("bin_8")] + public int? Bin8 { get; set; } + + [Column("bin_9")] + public int? Bin9 { get; set; } + } +} diff --git a/Elwig/Models/PaymentDeliveryPart.cs b/Elwig/Models/PaymentDeliveryPart.cs index b70646d..7266282 100644 --- a/Elwig/Models/PaymentDeliveryPart.cs +++ b/Elwig/Models/PaymentDeliveryPart.cs @@ -34,37 +34,6 @@ namespace Elwig.Models { set => ModRelValue = (double)value; } - [Column("bucket_1")] - public int? Bucket1 { get; set; } - - [Column("bucket_2")] - public int? Bucket2 { get; set; } - - [Column("bucket_3")] - public int? Bucket3 { get; set; } - - [Column("bucket_4")] - public int? Bucket4 { get; set; } - - [Column("bucket_5")] - public int? Bucket5 { get; set; } - - [Column("bucket_6")] - public int? Bucket6 { get; set; } - - [Column("bucket_7")] - public int? Bucket7 { get; set; } - - [Column("bucket_8")] - public int? Bucket8 { get; set; } - - [Column("bucket_9")] - public int? Bucket9 { get; set; } - - [NotMapped] - public int[] Buckets => (new int?[] { Bucket1, Bucket2, Bucket3, Bucket4, Bucket5, Bucket6, Bucket7, Bucket8, Bucket9 }) - .Where(b => b != null).Select(b => b.Value).ToArray(); - [Column("price_1")] public long? Price1Value { get; set; } [NotMapped] diff --git a/Elwig/Models/PaymentVar.cs b/Elwig/Models/PaymentVar.cs index cf6a6ef..49fbaeb 100644 --- a/Elwig/Models/PaymentVar.cs +++ b/Elwig/Models/PaymentVar.cs @@ -40,37 +40,6 @@ namespace Elwig.Models { [Column("calc_time")] public int? CalcTime { get; set; } - [Column("bucket_1_name")] - public string? Bucket1Name { get; set; } - - [Column("bucket_2_name")] - public string? Bucket2Name { get; set; } - - [Column("bucket_3_name")] - public string? Bucket3Name { get; set; } - - [Column("bucket_4_name")] - public string? Bucket4Name { get; set; } - - [Column("bucket_5_name")] - public string? Bucket5Name { get; set; } - - [Column("bucket_6_name")] - public string? Bucket6Name { get; set; } - - [Column("bucket_7_name")] - public string? Bucket7Name { get; set; } - - [Column("bucket_8_name")] - public string? Bucket8Name { get; set; } - - [Column("bucket_9_name")] - public string? Bucket9Name { get; set; } - - [NotMapped] - public string[] BucketNames => (new string?[] { Bucket1Name, Bucket2Name, Bucket3Name, Bucket4Name, Bucket5Name, Bucket6Name, Bucket7Name, Bucket8Name, Bucket9Name }) - .Where(n => n != null).Select(n => n ?? "").ToArray(); - [Column("comment")] public string? Comment { get; set; } diff --git a/Elwig/Models/Season.cs b/Elwig/Models/Season.cs index 6c6d09d..834a93c 100644 --- a/Elwig/Models/Season.cs +++ b/Elwig/Models/Season.cs @@ -3,6 +3,7 @@ using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; namespace Elwig.Models { [Table("season"), PrimaryKey("Year")] @@ -42,6 +43,37 @@ namespace Elwig.Models { } } + [Column("bin_1_name")] + public string? Bin1Name { get; set; } + + [Column("bin_2_name")] + public string? Bin2Name { get; set; } + + [Column("bin_3_name")] + public string? Bin3Name { get; set; } + + [Column("bin_4_name")] + public string? Bin4Name { get; set; } + + [Column("bin_5_name")] + public string? Bin5Name { get; set; } + + [Column("bin_6_name")] + public string? Bin6Name { get; set; } + + [Column("bin_7_name")] + public string? Bin7Name { get; set; } + + [Column("bin_8_name")] + public string? Bin8Name { get; set; } + + [Column("bin_9_name")] + public string? Bin9Name { get; set; } + + [NotMapped] + public string[] BinNames => (new string?[] { Bin1Name, Bin2Name, Bin3Name, Bin4Name, Bin5Name, Bin6Name, Bin7Name, Bin8Name, Bin9Name }) + .Where(n => n != null).Select(n => n ?? "").ToArray(); + [ForeignKey("CurrencyCode")] public virtual Currency Currency { get; private set; } diff --git a/Elwig/Windows/TestWindow.xaml b/Elwig/Windows/TestWindow.xaml index 0d4442e..e816cda 100644 --- a/Elwig/Windows/TestWindow.xaml +++ b/Elwig/Windows/TestWindow.xaml @@ -23,5 +23,8 @@ Margin="260,190,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/>