using System.Collections.Generic; using System.Linq; 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; } public async Task Calculate() { await DeleteInDb(); await SetCalcTime(); await CalculatePrices(); await CalculateModifiers(); await CalculateMemberModifiers(); } public async Task Commit() { await Revert(); using var cnx = await AppDbContext.ConnectAsync(); await AppDbContext.ExecuteBatch(cnx, $""" INSERT INTO credit (year, tgnr, mgnr, avnr, net_amount, prev_net_amount, vat, modifiers, prev_modifiers) SELECT s.year, COALESCE(t.tgnr, 0) + ROW_NUMBER() OVER(ORDER BY m.mgnr) AS tgnr, m.mgnr, v.avnr, ROUND(p.amount / POW(10, s.precision - 2)) AS net_amount, ROUND(lp.amount / POW(10, s.precision - 2)) AS prev_amount, IIF(m.buchführend, s.vat_normal, s.vat_flatrate) AS vat, ROUND(COALESCE(u.total_penalty, 0) / POW(10, 4 - 2)) AS modifiers, lc.modifiers AS prev_modifiers FROM season s JOIN payment_variant v ON v.year = s.year LEFT JOIN payment_variant l ON l.year = s.year AND l.avnr = (SELECT avnr FROM payment_variant WHERE year = s.year AND NOT test_variant ORDER BY COALESCE(transfer_date, date) DESC, avnr DESC LIMIT 1) LEFT JOIN (SELECT year, MAX(tgnr) AS tgnr FROM credit GROUP BY year) t ON t.year = s.year JOIN (SELECT DISTINCT year, mgnr FROM delivery) d ON d.year = s.year JOIN member m ON m.mgnr = d.mgnr LEFT JOIN payment_member lp ON (lp.year, lp.avnr, lp.mgnr) = (l.year, l.avnr, m.mgnr) LEFT JOIN payment_member p ON (p.year, p.avnr, p.mgnr) = (v.year, v.avnr, m.mgnr) LEFT JOIN credit lc ON (lc.year, lc.avnr, lc.mgnr) = (l.year, l.avnr, m.mgnr) LEFT JOIN (SELECT year, mgnr, SUM(COALESCE(IIF(u.weight = 0, -t.penalty_none, 0), 0) + COALESCE(IIF(u.diff < 0, -t.penalty_amount, 0), 0) + COALESCE(u.diff * t.penalty_per_kg, 0)) AS total_penalty FROM v_under_delivery u JOIN area_commitment_type t ON t.vtrgid = u.bucket GROUP BY year, mgnr) u ON (u.year, u.mgnr) = (s.year, m.mgnr) WHERE s.year = {Year} AND v.avnr = {AvNr}; UPDATE payment_variant SET test_variant = FALSE WHERE (year, avnr) = ({Year}, {AvNr}); """); } public async Task Revert() { using var cnx = await AppDbContext.ConnectAsync(); await AppDbContext.ExecuteBatch(cnx, $""" DELETE FROM credit WHERE (year, avnr) = ({Year}, {AvNr}); UPDATE payment_variant SET test_variant = TRUE WHERE (year, avnr) = ({Year}, {AvNr}); """); } protected async Task SetCalcTime() { using var cnx = await AppDbContext.ConnectAsync(); await AppDbContext.ExecuteBatch(cnx, $""" UPDATE payment_variant SET calc_time = UNIXEPOCH() WHERE (year, avnr) = ({Year}, {AvNr}) """); } protected async Task DeleteInDb() { using var cnx = await AppDbContext.ConnectAsync(); await AppDbContext.ExecuteBatch(cnx, $""" DELETE FROM payment_delivery_part_bucket WHERE (year, avnr) = ({Year}, {AvNr}); DELETE FROM payment_delivery_part WHERE (year, avnr) = ({Year}, {AvNr}); DELETE FROM payment_member WHERE (year, avnr) = ({Year}, {AvNr}); UPDATE payment_variant SET calc_time = NULL WHERE (year, avnr) = ({Year}, {AvNr}); """); } protected async Task CalculateMemberModifiers() { using var cnx = await AppDbContext.ConnectAsync(); if (App.Client.IsMatzen) { var lastYears = 3; var multiplier = 0.50; var modName = "Treue%"; await AppDbContext.ExecuteBatch(cnx, $""" INSERT INTO payment_member (year, avnr, mgnr, net_amount, mod_abs, mod_rel) SELECT c.year, {AvNr}, s.mgnr, 0, ROUND(s.sum * COALESCE(m.abs, 0)), COALESCE(m.rel, 0) FROM (SELECT {Year} AS year, mgnr, ROUND(AVG(sum) * {multiplier}) AS baseline, COUNT(*) = {lastYears} AND MIN(sum) > 0 AS allowed FROM v_stat_member WHERE year > {Year} - {lastYears} GROUP BY mgnr HAVING allowed) c JOIN v_stat_member s ON (s.year, s.mgnr) = (c.year, c.mgnr) LEFT JOIN modifier m ON m.year = c.year AND m.name LIKE '{modName}' WHERE sum >= baseline ON CONFLICT DO UPDATE SET mod_abs = mod_abs + excluded.mod_abs, mod_rel = mod_rel + excluded.mod_rel """); } } protected async Task CalculatePrices() { 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)>(); using (var cmd = cnx.CreateCommand()) { cmd.CommandText = $""" SELECT d.year, d.did, d.dpnr, b.bktnr, d.sortid, b.discr, b.value, d.min_quw, d.oe, d.kmw FROM delivery_part_bucket b JOIN v_delivery d ON (d.year, d.did, d.dpnr) = (b.year, b.did, b.dpnr) WHERE b.year = {Year} """; using var reader = await cmd.ExecuteReaderAsync(); while (await reader.ReadAsync()) { parts.Add(( reader.GetInt32(0), reader.GetInt32(1), reader.GetInt32(2), reader.GetInt32(3), reader.GetString(4), reader.GetString(5), reader.GetInt32(6), reader.GetBoolean(7), reader.GetDouble(8), reader.GetDouble(9) )); } } var inserts = new List<(int Year, int DId, int DPNr, int BktNr, long Price, long Amount)>(); foreach (var part in parts) { var price = !part.MinQuw ? 0.5m : ((part.BktNr == 2 ? 0.8m : (part.BktNr == 1 ? 0.7m : 0.6m)) + ((decimal)(part.Oe - 73) * 0.005m)); // TODO price += (AvNr - 1) * 0.2m; var priceL = Utils.DecToDb(price, 4); var amount = Utils.DecToDb(price * part.Value, 4); inserts.Add((part.Year, part.DId, part.DPNr, part.BktNr, priceL, amount)); } await AppDbContext.ExecuteBatch(cnx, $""" INSERT INTO payment_delivery_part_bucket (year, did, dpnr, bktnr, avnr, price, amount) VALUES {string.Join(",\n ", inserts.Select(i => $"({i.Year}, {i.DId}, {i.DPNr}, {i.BktNr}, {AvNr}, {i.Price}, {i.Amount})"))}; """); } protected async Task CalculateModifiers() { using var cnx = await AppDbContext.ConnectAsync(); await AppDbContext.ExecuteBatch(cnx, $""" 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 """); } } }