using Elwig.Documents; using Elwig.Helpers; using Elwig.Helpers.Billing; using Elwig.Helpers.Export; using Elwig.Models.Dtos; using Elwig.Models.Entities; using Elwig.ViewModels; using Microsoft.EntityFrameworkCore; using Microsoft.Win32; using System; using System.Linq; using System.Text.Json; using System.Threading.Tasks; using System.Windows; using System.Windows.Input; namespace Elwig.Services { public static class PaymentVariantService { private static readonly JsonSerializerOptions JsonOpt = new() { WriteIndented = true }; public static void ClearInputs(this PaymentVariantsViewModel vm) { vm.IsPaymentVariantSelected = false; vm.EditText = "Bearbeiten"; vm.SaveIsEnabled = false; vm.DeleteIsEnabled = false; vm.CalculateIsEnabled = false; vm.CommitIsEnabled = false; vm.CommitVisibility = Visibility.Visible; vm.RevertIsEnabled = false; vm.RevertVisibility = Visibility.Hidden; vm.Arrow = "\xF0AF"; vm.ExportIsEnabled = false; vm.IsReadOnly = true; vm.DataIsReadOnly = true; vm.BillingData = null; vm.Name = ""; vm.Comment = ""; vm.Date = null; vm.TransferDate = null; vm.WeightModifier = null; vm.ConsiderModifiers = false; vm.ConsiderPenalties = false; vm.ConsiderPenalty = false; vm.ConsiderAuto = false; vm.ConsiderCustom = false; vm.IsEnabled = false; vm.Data = ""; vm.StatusModifierSum = "-"; vm.StatusTotalSum = "-"; vm.StatusVatSum = "-"; vm.StatusDeductionSum = "-"; vm.StatusPaymentSum = "-"; } public static void FillInputs(this PaymentVariantsViewModel vm, PaymentVar v) { var locked = !v.TestVariant; vm.IsPaymentVariantSelected = true; vm.SaveIsEnabled = !locked; vm.DeleteIsEnabled = !locked; vm.CalculateIsEnabled = !locked; vm.CommitIsEnabled = !locked && !vm.SeasonLocked; vm.CommitVisibility = !locked ? Visibility.Visible : Visibility.Hidden; vm.RevertIsEnabled = locked && !vm.SeasonLocked; vm.RevertVisibility = locked ? Visibility.Visible : Visibility.Hidden; vm.Arrow = locked ? "\xF0B0" : "\xF0AF"; vm.EditText = locked ? "Ansehen" : "Bearbeiten"; vm.ExportIsEnabled = locked; vm.IsReadOnly = false; vm.DataIsReadOnly = locked; vm.Name = v.Name; vm.Comment = v.Comment ?? ""; vm.Date = v.Date; vm.TransferDate = v.TransferDate; try { vm.BillingData = BillingData.FromJson(v.Data); vm.ConsiderModifiers = vm.BillingData.ConsiderDelieryModifiers; vm.ConsiderPenalties = vm.BillingData.ConsiderContractPenalties; vm.ConsiderPenalty = vm.BillingData.ConsiderTotalPenalty; vm.ConsiderAuto = vm.BillingData.ConsiderAutoBusinessShares; vm.ConsiderCustom = vm.BillingData.ConsiderCustomModifiers; if (vm.BillingData.NetWeightModifier != 0) { vm.WeightModifier = Math.Round(vm.BillingData.NetWeightModifier * 100.0, 8); } else if (vm.BillingData.GrossWeightModifier != 0) { vm.WeightModifier = Math.Round(vm.BillingData.GrossWeightModifier * 100.0, 8); } else { vm.WeightModifier = null; } vm.Data = JsonSerializer.Serialize(vm.BillingData.Data, JsonOpt); } catch { vm.BillingData = null; vm.ConsiderModifiers = false; vm.ConsiderPenalties = false; vm.ConsiderPenalty = false; vm.ConsiderAuto = false; vm.ConsiderCustom = false; vm.WeightModifier = null; vm.Data = v.Data; } vm.IsEnabled = !locked; vm.StatusModifierSum = "..."; vm.StatusTotalSum = "..."; vm.StatusVatSum = "..."; vm.StatusDeductionSum = "..."; vm.StatusPaymentSum = "..."; Utils.RunBackground("Variantendaten laden", async () => { await vm.UpdateSums(v); }); } private static async Task UpdateSums(this PaymentVariantsViewModel vm, PaymentVar v) { if (App.MainDispatcher == null) return; var modText = "-"; var totalText = "-"; var vatText = "-"; var deductionText = "-"; var paymentText = "-"; using var ctx = new AppDbContext(); var sym = v.Season.Currency.Symbol ?? v.Season.Currency.Code; var modSum = await ctx.PaymentDeliveryParts .Where(p => p.Year == v.Year && p.AvNr == v.AvNr) .SumAsync(p => p.AmountValue - p.NetAmountValue); modText = $"{v.Season.DecFromDb(modSum):N2} {sym}"; var totalSum = await ctx.MemberPayments .Where(p => p.Year == v.Year && p.AvNr == v.AvNr) .SumAsync(p => p.AmountValue); totalText = $"{v.Season.DecFromDb(totalSum):N2} {sym}"; await App.MainDispatcher.BeginInvoke(() => { if (vm.SelectedPaymentVariant != v) return; vm.StatusModifierSum = modText; vm.StatusTotalSum = totalText; }); var credits = ctx.Credits.Where(c => c.Year == v.Year && c.AvNr == v.AvNr); if (!credits.Any()) { long lastTotalSum = 0; decimal vatSum = 0; var currentPayments = await ctx.MemberPayments .Where(p => p.Year == v.Year && p.AvNr == v.AvNr) .ToDictionaryAsync(p => p.MgNr); var lastV = await ctx.PaymentVariants .Where(l => l.Year == v.Year && !l.TestVariant) .OrderByDescending(l => l.TransferDateString ?? l.DateString) .ThenByDescending(l => l.AvNr) .FirstOrDefaultAsync(); if (lastV != null) { var lastPayments = await ctx.MemberPayments .Where(p => p.Year == v.Year && p.AvNr == lastV.AvNr) .ToDictionaryAsync(p => p.MgNr); lastTotalSum = lastPayments.Sum(e => e.Value.AmountValue); foreach (int mgnr in currentPayments.Keys) { var c = currentPayments[mgnr]; var l = lastPayments[mgnr]; vatSum += (c.Amount - l.Amount) * (decimal)(c.Member.IsBuchführend ? v.Season.VatNormal : v.Season.VatFlatrate); } } else { vatSum = currentPayments.Sum(e => e.Value.Amount * (decimal)(e.Value.Member.IsBuchführend ? v.Season.VatNormal : v.Season.VatFlatrate)); } vatText = $"~{vatSum:N2} {sym}"; deductionText = $"- {sym}"; paymentText = $"~{v.Season.DecFromDb(totalSum - lastTotalSum) + vatSum:N2} {sym}"; } else { // all values in the credit table are stored with precision 2! totalText = $"{Utils.DecFromDb(await credits.SumAsync(c => c.NetAmountValue), 2):N2} {sym}"; vatText = $"{Utils.DecFromDb(await credits.SumAsync(c => c.VatAmountValue), 2):N2} {sym}"; deductionText = $"{-Utils.DecFromDb(await credits.SumAsync(c => c.ModifiersValue ?? 0), 2):N2} {sym}"; paymentText = $"{Utils.DecFromDb(await credits.SumAsync(c => c.AmountValue), 2):N2} {sym}"; } await App.MainDispatcher.BeginInvoke(() => { if (vm.SelectedPaymentVariant != v) return; vm.StatusModifierSum = modText; vm.StatusTotalSum = totalText; vm.StatusVatSum = vatText; vm.StatusDeductionSum = deductionText; vm.StatusPaymentSum = paymentText; }); } public static async Task GenerateSummary(PaymentVar v, ExportMode mode) { if (mode == ExportMode.SaveList) { var d = new SaveFileDialog() { FileName = $"Variantendaten-{v.Name.Trim().Replace(' ', '-')}.ods", DefaultExt = "ods", Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods", Title = $"Variantendaten {v.Name} speichern unter - Elwig" }; if (d.ShowDialog() == false) return; Mouse.OverrideCursor = Cursors.Wait; await Task.Run(async () => { try { using var ctx = new AppDbContext(); var data = await PaymentVariantSummaryData.ForPaymentVariant(v, ctx.PaymentVariantSummaryRows); using var ods = new OdsFile(d.FileName); await ods.AddTable(data); } catch (Exception exc) { MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); } }); Mouse.OverrideCursor = null; } else { Mouse.OverrideCursor = Cursors.Wait; await Task.Run(async () => { try { using var ctx = new AppDbContext(); var data = await PaymentVariantSummaryData.ForPaymentVariant(v, ctx.PaymentVariantSummaryRows); using var doc = new PaymentVariantSummary((await ctx.PaymentVariants.FindAsync(v.Year, v.AvNr))!, data); await Utils.ExportDocument(doc, mode); } catch (Exception exc) { MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); } }); Mouse.OverrideCursor = null; } } public static async Task GenerateEbics(int year, int avnr) { using var ctx = new AppDbContext(); var v = (await ctx.PaymentVariants.FindAsync(year, avnr))!; var withoutIban = v.Credits.Count(c => c.Member.Iban == null); if (withoutIban > 0) { var r = MessageBox.Show($"Achtung: Für {withoutIban:N0} Mitglieder ist kein IBAN hinterlegt.\n\nDiese werden NICHT exportiert.", "Mitglieder ohne IBAN", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel); if (r != MessageBoxResult.OK) return; } var withNegAmount = v.Credits.Count(c => c.Amount <= 0); if (withNegAmount > 0) { var r = MessageBox.Show($"Achtung: Es gibt {withNegAmount:N0} Traubengutschriften mit negativem Betrag.\n\nDiese werden NICHT exportiert.", "Traubengutschriften mit negativem Betrag", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.OK); if (r != MessageBoxResult.OK) return; } var d = new SaveFileDialog() { FileName = $"{App.Client.NameToken}-Überweisungsdaten-{v.Year}-{v.Name.Trim().Replace(' ', '-')}.{Ebics.FileExtension}", DefaultExt = Ebics.FileExtension, Filter = "EBICS-Datei (*.xml)|*.xml", Title = $"Überweisungsdaten speichern unter - Elwig", }; if (d.ShowDialog() == true) { Mouse.OverrideCursor = Cursors.Wait; await Task.Run(async () => { try { using var e = new Ebics(v, d.FileName, App.Client.ExportEbicsVersion, (Ebics.AddressMode)App.Client.ExportEbicsAddress); await e.ExportAsync(Transaction.FromPaymentVariant(v)); } catch (Exception exc) { MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); } }); Mouse.OverrideCursor = null; } } public static async Task GenerateAccountingList(int year, int avnr, string name) { var d = new SaveFileDialog() { FileName = $"{App.Client.NameToken}-Buchungsliste-{year}-{name.Trim().Replace(' ', '-')}.ods", DefaultExt = "ods", Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods", Title = $"Buchungsliste speichern unter - Elwig" }; if (d.ShowDialog() == true) { Mouse.OverrideCursor = Cursors.Wait; await Task.Run(async () => { try { using var ctx = new AppDbContext(); var tbl = await CreditNoteData.ForPaymentVariant(ctx, year, avnr); using var ods = new OdsFile(d.FileName); await ods.AddTable(tbl); } catch (Exception exc) { MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); } }); Mouse.OverrideCursor = null; } } public static async Task CreatePaymentVariant(int year) { Mouse.OverrideCursor = Cursors.Wait; var v = await Task.Run(async () => { using var ctx = new AppDbContext(); var v = new PaymentVar { Year = year, AvNr = await ctx.NextAvNr(year), Name = "Neue Auszahlungsvariante", TestVariant = true, DateString = $"{DateTime.Today:yyyy-MM-dd}", Data = "{\"mode\": \"elwig\", \"version\": 1, \"payment\": {}, \"curves\": []}", }; ctx.Add(v); await ctx.SaveChangesAsync(); return v; }); App.HintContextChange(); Mouse.OverrideCursor = null; return v; } public static async Task Duplicate(this PaymentVar orig) { var n = await Task.Run(async () => { using var ctx = new AppDbContext(); var n = new PaymentVar { Year = orig.Year, AvNr = await ctx.NextAvNr(orig.Year), Name = $"{orig.Name} (Kopie)", TestVariant = true, DateString = $"{DateTime.Today:yyyy-MM-dd}", Data = orig.Data, }; ctx.Add(n); await ctx.SaveChangesAsync(); return n; }); App.HintContextChange(); return n; } public static async Task<(int, int)> UpdatePaymentVariant(this PaymentVariantsViewModel vm, int? oldYear, int? oldAvNr) { var year = oldYear ?? Utils.CurrentYear; int avnr = 0; await Task.Run(async () => { var d = App.Config.Debug ? BillingData.FromJson(vm.Data ?? "{}") : vm.BillingData!; d.ConsiderDelieryModifiers = vm.ConsiderModifiers; d.ConsiderContractPenalties = vm.ConsiderPenalties; d.ConsiderTotalPenalty = vm.ConsiderPenalty; d.ConsiderAutoBusinessShares = vm.ConsiderAuto; d.ConsiderCustomModifiers = vm.ConsiderCustom; var modVal = vm.WeightModifier ?? 0; d.NetWeightModifier = modVal > 0 ? modVal / 100.0 : 0; d.GrossWeightModifier = modVal < 0 ? modVal / 100.0 : 0; using var ctx = new AppDbContext(); var v = new PaymentVar { Year = year, AvNr = oldAvNr ?? await ctx.NextAvNr(year), Name = vm.Name!, DateString = $"{vm.Date!.Value:yyyy-MM-dd}", TransferDate = vm.TransferDate, TestVariant = vm.SelectedPaymentVariant?.TestVariant ?? true, CalcTimeUnix = vm.SelectedPaymentVariant?.CalcTimeUnix, Comment = string.IsNullOrEmpty(vm.Comment) ? null : vm.Comment, Data = JsonSerializer.Serialize(d.Data), }; avnr = v.AvNr; ctx.Update(v); await ctx.SaveChangesAsync(); }); vm.WeightModifierChanged = false; App.HintContextChange(); return (year, avnr); } public static async Task DeletePaymentVariant(int year, int avnr) { await Task.Run(async () => { using var ctx = new AppDbContext(); var v = (await ctx.PaymentVariants.FindAsync(year, avnr))!; ctx.Remove(v); await ctx.SaveChangesAsync(); }); App.HintContextChange(); } public static async Task Calculate(int year, int avnr) { await Task.Run(async () => { var b = new BillingVariant(year, avnr); await b.Calculate(); }); App.HintContextChange(); } public static async Task Commit(int year, int avnr) { await Task.Run(async () => { var b = new BillingVariant(year, avnr); await b.Commit(); }); App.HintContextChange(); } public static async Task Revert(int year, int avnr) { await Task.Run(async () => { var b = new BillingVariant(year, avnr); await b.Revert(); }); App.HintContextChange(); } } }