diff --git a/Elwig/Controls/UnitTextBox.xaml b/Elwig/Controls/UnitTextBox.xaml index 1d09491..40c4ce4 100644 --- a/Elwig/Controls/UnitTextBox.xaml +++ b/Elwig/Controls/UnitTextBox.xaml @@ -5,7 +5,8 @@ - @@ -22,9 +23,19 @@ FontSize="10" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="3"/> + + + + + + + + + + diff --git a/Elwig/Helpers/AppDbContext.cs b/Elwig/Helpers/AppDbContext.cs index 5be3128..1fdb9fe 100644 --- a/Elwig/Helpers/AppDbContext.cs +++ b/Elwig/Helpers/AppDbContext.cs @@ -354,6 +354,27 @@ namespace Elwig.Helpers { _memberUnderDelivery[year] = buckets; } + public async Task> GetBusinessSharePenalties(int year) { + using var cnx = await ConnectAsync(); + var dict = new Dictionary(); + using var cmd = cnx.CreateCommand(); + cmd.CommandText = $""" + SELECT mgnr, ROUND(( + COALESCE(-s.penalty_amount, 0) + + COALESCE(-s.penalty_per_bs_amount * CEIL(CAST(-u.diff AS REAL) / s.min_kg_per_bs), 0) + + COALESCE(u.diff * s.penalty_per_kg, 0) + ) / POW(10, s.precision - 2)) + FROM v_total_under_delivery u + JOIN season s ON s.year = u.year + WHERE s.year = {year} AND u.diff < 0 + """; + using var reader = await cmd.ExecuteReaderAsync(); + while (await reader.ReadAsync()) { + dict[reader.GetInt32(0)] = reader.GetInt64(1); + } + return dict; + } + public async Task> GetMemberAreaCommitmentBuckets(int year, int mgnr, SqliteConnection? cnx = null) { if (!_memberAreaCommitmentBuckets.ContainsKey(year)) await FetchMemberAreaCommitmentBuckets(year, cnx); diff --git a/Elwig/Helpers/AppDbUpdater.cs b/Elwig/Helpers/AppDbUpdater.cs index e7fa099..a82eae6 100644 --- a/Elwig/Helpers/AppDbUpdater.cs +++ b/Elwig/Helpers/AppDbUpdater.cs @@ -9,7 +9,7 @@ namespace Elwig.Helpers { public static class AppDbUpdater { // Don't forget to update value in Tests/fetch-resources.bat! - public static readonly int RequiredSchemaVersion = 19; + public static readonly int RequiredSchemaVersion = 20; private static int VersionOffset = 0; diff --git a/Elwig/Helpers/Billing/BillingVariant.cs b/Elwig/Helpers/Billing/BillingVariant.cs index 783b8f6..6c42156 100644 --- a/Elwig/Helpers/Billing/BillingVariant.cs +++ b/Elwig/Helpers/Billing/BillingVariant.cs @@ -47,7 +47,6 @@ namespace Elwig.Helpers.Billing { ROUND(lp.amount / POW(10, s.precision - 2)) AS prev_amount, IIF(m.buchführend, s.vat_normal, s.vat_flatrate) AS vat, ROUND(IIF({Data.ConsiderContractPenalties}, COALESCE(u.total_penalty, 0), 0) / POW(10, 4 - 2)) + - ROUND(IIF({Data.ConsiderTotalPenalty}, COALESCE(b.total_penalty, 0), 0) / POW(10, s.precision - 2)) + ROUND(IIF({Data.ConsiderAutoBusinessShares}, -COALESCE(a.total_amount, 0), 0) / POW(10, s.precision - 2)) AS modifiers, lc.modifiers AS prev_modifiers @@ -66,12 +65,30 @@ namespace Elwig.Helpers.Billing { 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 v_penalty_area_commitments u ON (u.year, u.mgnr) = (s.year, m.mgnr) - LEFT JOIN v_penalty_business_shares b ON (b.year, b.mgnr) = (s.year, m.mgnr) LEFT JOIN v_auto_business_shares a ON (a.year, a.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}); """); + if (Data.ConsiderTotalPenalty) { + if (App.Client.IsWinzerkeller) { + // TODO + } else { + await AppDbContext.ExecuteBatch(cnx, $""" + UPDATE credit AS c + SET modifiers = modifiers + ROUND(( + COALESCE(-s.penalty_amount, 0) + + COALESCE(-s.penalty_per_bs_amount * CEIL(CAST(-u.diff AS REAL) / s.min_kg_per_bs), 0) + + COALESCE(u.diff * s.penalty_per_kg, 0) + ) / POW(10, s.precision - 2)) + FROM v_total_under_delivery u + JOIN season s ON s.year = u.year + WHERE c.year = {Year} AND c.avnr = {AvNr} AND (u.year, u.mgnr) = (c.year, c.mgnr) AND + u.diff < 0 + """); + } + } + await AppDbContext.ExecuteBatch(cnx, $""" + UPDATE payment_variant SET test_variant = FALSE WHERE (year, avnr) = ({Year}, {AvNr}); + """); } public async Task Revert() { diff --git a/Elwig/Models/Dtos/CreditNoteData.cs b/Elwig/Models/Dtos/CreditNoteData.cs index 2654d90..601210f 100644 --- a/Elwig/Models/Dtos/CreditNoteData.cs +++ b/Elwig/Models/Dtos/CreditNoteData.cs @@ -42,8 +42,9 @@ namespace Elwig.Models.Dtos { public static async Task ForPaymentVariant(AppDbContext ctx, int year, int avnr) { var variant = await ctx.PaymentVariants.FindAsync(year, avnr); var name = variant!.Name; + var bsPenalty = await ctx.GetBusinessSharePenalties(year); var data = BillingData.FromJson(variant!.Data); - var rows = (await FromDbSet(ctx.CreditNoteRows, year, avnr)).Select(r => new CreditNoteRow(r, data)).ToList(); + var rows = (await FromDbSet(ctx.CreditNoteRows, year, avnr)).Select(r => new CreditNoteRow(r, data, bsPenalty)).ToList(); return new CreditNoteData(rows, year, name); } @@ -56,7 +57,6 @@ namespace Elwig.Models.Dtos { p.amount - p.net_amount AS surcharge, c.net_amount, c.prev_net_amount, c.vat, c.vat_amount, c.gross_amount, c.modifiers, c.prev_modifiers, c.amount, ROUND(COALESCE(u.total_penalty, 0) / POW(10, 4 - 2)) AS fb_penalty, - ROUND(COALESCE(b.total_penalty, 0) / POW(10, s.precision - 2)) AS bs_penalty, ROUND(COALESCE(a.total_amount, 0) / POW(10, s.precision - 2)) AS auto_bs FROM credit c LEFT JOIN member m ON m.mgnr = c.mgnr @@ -65,7 +65,6 @@ namespace Elwig.Models.Dtos { LEFT JOIN AT_ort o ON o.okz = p.okz LEFT JOIN season s ON s.year = c.year LEFT JOIN v_penalty_area_commitments u ON (u.year, u.mgnr) = (s.year, m.mgnr) - LEFT JOIN v_penalty_business_shares b ON (b.year, b.mgnr) = (s.year, m.mgnr) LEFT JOIN v_auto_business_shares a ON (a.year, a.mgnr) = (s.year, m.mgnr) WHERE c.year = {year} AND c.avnr = {avnr} ORDER BY m.mgnr @@ -96,7 +95,7 @@ namespace Elwig.Models.Dtos { public decimal? Considered; public decimal Amount; - public CreditNoteRow(CreditNoteRowSingle row, BillingData data) { + public CreditNoteRow(CreditNoteRowSingle row, BillingData data, Dictionary bsPenalty) { byte prec1 = 2, prec2 = row.Precision; MgNr = row.MgNr; Name1 = row.Name1; @@ -120,7 +119,7 @@ namespace Elwig.Models.Dtos { if (data.ConsiderContractPenalties) Penalties = (row.FbPenalty == null || row.FbPenalty == 0) ? null : Utils.DecFromDb((long)row.FbPenalty, prec1); if (data.ConsiderTotalPenalty) - Penalty = (row.BsPealty == null || row.BsPealty == 0) ? null : Utils.DecFromDb((long)row.BsPealty, prec1); + Penalty = (!bsPenalty.TryGetValue(row.MgNr, out var val) || val == 0) ? null : Utils.DecFromDb(val, prec1); if (data.ConsiderAutoBusinessShares) AutoBs = (row.AutoBs == null || row.AutoBs == 0) ? null : -Utils.DecFromDb((long)row.AutoBs, prec1); mod -= (Penalties ?? 0) + (Penalty ?? 0) + (AutoBs ?? 0); @@ -175,8 +174,6 @@ namespace Elwig.Models.Dtos { public long Amount { get; set; } [Column("fb_penalty")] public long? FbPenalty { get; set; } - [Column("bs_penalty")] - public long? BsPealty { get; set; } [Column("auto_bs")] public long? AutoBs { get; set; } } diff --git a/Elwig/Models/Entities/Season.cs b/Elwig/Models/Entities/Season.cs index 75f06d6..df61962 100644 --- a/Elwig/Models/Entities/Season.cs +++ b/Elwig/Models/Entities/Season.cs @@ -55,6 +55,22 @@ namespace Elwig.Models.Entities { set => PenaltyNoneValue = value != null ? DecToDb(value.Value) : null; } + [Column("penalty_per_bs_amount")] + public long? PenaltyPerBsAmountValue { get; set; } + [NotMapped] + public decimal? PenaltyPerBsAmount { + get => PenaltyPerBsAmountValue != null ? DecFromDb(PenaltyPerBsAmountValue.Value) : null; + set => PenaltyPerBsAmountValue = value != null ? DecToDb(value.Value) : null; + } + + [Column("penalty_per_bs_none")] + public long? PenaltyPerBsNoneValue { get; set; } + [NotMapped] + public decimal? PenaltyPerBsNone { + get => PenaltyPerBsNoneValue != null ? DecFromDb(PenaltyPerBsNoneValue.Value) : null; + set => PenaltyPerBsNoneValue = value != null ? DecToDb(value.Value) : null; + } + [Column("bs_value")] public long? BusinessShareValueValue { get; set; } [NotMapped] diff --git a/Elwig/Resources/Sql/19-20.sql b/Elwig/Resources/Sql/19-20.sql new file mode 100644 index 0000000..f7e979e --- /dev/null +++ b/Elwig/Resources/Sql/19-20.sql @@ -0,0 +1,7 @@ +-- schema version 19 to 20 + +ALTER TABLE season ADD COLUMN penalty_per_bs_amount INTEGER DEFAULT NULL; +ALTER TABLE season ADD COLUMN penalty_per_bs_none INTEGER DEFAULT NULL; +DROP VIEW v_penalty_business_shares; + +UPDATE season SET penalty_none = NULL; diff --git a/Elwig/Windows/BaseDataWindow.xaml b/Elwig/Windows/BaseDataWindow.xaml index 6a80f7a..683773c 100644 --- a/Elwig/Windows/BaseDataWindow.xaml +++ b/Elwig/Windows/BaseDataWindow.xaml @@ -395,7 +395,7 @@ - + @@ -415,7 +415,7 @@ - + @@ -440,29 +440,32 @@ - diff --git a/Elwig/Windows/BaseDataWindow.xaml.Season.cs b/Elwig/Windows/BaseDataWindow.xaml.Season.cs index 8ca61e4..d0d9b0d 100644 --- a/Elwig/Windows/BaseDataWindow.xaml.Season.cs +++ b/Elwig/Windows/BaseDataWindow.xaml.Season.cs @@ -49,13 +49,17 @@ namespace Elwig.Windows { SeasonPenaltyPerKgInput.Text = s.PenaltyPerKg?.ToString() ?? ""; SeasonPenaltyInput.Text = s.PenaltyAmount?.ToString() ?? ""; SeasonPenaltyNoneInput.Text = s.PenaltyNone?.ToString() ?? ""; + SeasonPenaltyPerBsInput.Text = s.PenaltyPerBsAmount?.ToString() ?? ""; + SeasonPenaltyPerBsNoneInput.Text = s.PenaltyPerBsNone?.ToString() ?? ""; SeasonBsValueInput.Text = s.BusinessShareValue?.ToString() ?? ""; - var sym = s.Currency.Symbol ?? ""; + var sym = s.Currency.Symbol ?? s.Currency.Code; SeasonModifierAbsInput.Unit = $"{sym}/kg"; SeasonPenaltyPerKgInput.Unit = $"{sym}/kg"; SeasonPenaltyInput.Unit = sym; SeasonPenaltyNoneInput.Unit = sym; + SeasonPenaltyPerBsInput.Unit = $"{sym}/GA"; + SeasonPenaltyPerBsNoneInput.Unit = $"{sym}/GA"; SeasonBsValueInput.Unit = $"{sym}/GA"; AreaCommitmentTypePenaltyPerKgInput.Unit = $"{sym}/kg"; AreaCommitmentTypePenaltyInput.Unit = sym; @@ -72,6 +76,8 @@ namespace Elwig.Windows { SeasonPenaltyPerKgInput.Text = ""; SeasonPenaltyInput.Text = ""; SeasonPenaltyNoneInput.Text = ""; + SeasonPenaltyPerBsInput.Text = ""; + SeasonPenaltyPerBsNoneInput.Text = ""; SeasonBsValueInput.Text = ""; } _seasonUpdate = false; @@ -94,6 +100,8 @@ namespace Elwig.Windows { s.PenaltyPerKg = (SeasonPenaltyPerKgInput.Text.Length > 0) ? decimal.Parse(SeasonPenaltyPerKgInput.Text) : null; s.PenaltyAmount = (SeasonPenaltyInput.Text.Length > 0) ? decimal.Parse(SeasonPenaltyInput.Text) : null; s.PenaltyNone = (SeasonPenaltyNoneInput.Text.Length > 0) ? decimal.Parse(SeasonPenaltyNoneInput.Text) : null; + s.PenaltyPerBsAmount = (SeasonPenaltyPerBsInput.Text.Length > 0) ? decimal.Parse(SeasonPenaltyPerBsInput.Text) : null; + s.PenaltyPerBsNone = (SeasonPenaltyPerBsNoneInput.Text.Length > 0) ? decimal.Parse(SeasonPenaltyPerBsNoneInput.Text) : null; s.BusinessShareValue = (SeasonBsValueInput.Text.Length > 0) ? decimal.Parse(SeasonBsValueInput.Text) : null; UpdateButtons(); @@ -119,5 +127,11 @@ namespace Elwig.Windows { InputTextChanged((TextBox)sender, Validator.CheckDecimal((TextBox)sender, false, 4, 2)); Season_Changed(sender, evt); } + + private void SeasonPenaltyPerBsInput_TextChanged(object sender, TextChangedEventArgs evt) { + if (SeasonList.SelectedItem is not Season s) return; + InputTextChanged((TextBox)sender, Validator.CheckDecimal((TextBox)sender, false, 4, s.Precision)); + Season_Changed(sender, evt); + } } } diff --git a/Elwig/Windows/BaseDataWindow.xaml.cs b/Elwig/Windows/BaseDataWindow.xaml.cs index da9173e..ac9d780 100644 --- a/Elwig/Windows/BaseDataWindow.xaml.cs +++ b/Elwig/Windows/BaseDataWindow.xaml.cs @@ -31,6 +31,7 @@ namespace Elwig.Windows { SeasonMaxKgPerHaInput, SeasonVatNormalInput, SeasonVatFlatrateInput, SeasonStartInput, SeasonEndInput, SeasonMinKgPerBsInput, SeasonMaxKgPerBsInput, SeasonBsValueInput, SeasonPenaltyPerKgInput, SeasonPenaltyInput, SeasonPenaltyNoneInput, + SeasonPenaltyPerBsInput, SeasonPenaltyPerBsNoneInput, SeasonModifierIdInput, SeasonModifierNameInput, SeasonModifierRelInput, SeasonModifierAbsInput, ]; WineAttributeFillLowerInput.Visibility = Visibility.Hidden; diff --git a/Tests/fetch-resources.bat b/Tests/fetch-resources.bat index ccc8ca7..bef7435 100644 --- a/Tests/fetch-resources.bat +++ b/Tests/fetch-resources.bat @@ -1 +1 @@ -curl --fail -s -L "https://elwig.at/files/create.sql?v=19" -u "elwig:ganzGeheim123!" -o "Resources\Sql\Create.sql" +curl --fail -s -L "https://elwig.at/files/create.sql?v=20" -u "elwig:ganzGeheim123!" -o "Resources\Sql\Create.sql"