388 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			388 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
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<PaymentVar> CreatePaymentVariant(int year) {
 | 
						|
            return 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;
 | 
						|
            });
 | 
						|
        }
 | 
						|
 | 
						|
        public static async Task<PaymentVar> Duplicate(this PaymentVar orig) {
 | 
						|
            return 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;
 | 
						|
            });
 | 
						|
        }
 | 
						|
 | 
						|
        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;
 | 
						|
            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();
 | 
						|
            });
 | 
						|
        }
 | 
						|
 | 
						|
        public static async Task Calculate(int year, int avnr) {
 | 
						|
            await Task.Run(async () => {
 | 
						|
                var b = new BillingVariant(year, avnr);
 | 
						|
                await b.Calculate();
 | 
						|
            });
 | 
						|
        }
 | 
						|
 | 
						|
        public static async Task Commit(int year, int avnr) {
 | 
						|
            await Task.Run(async () => {
 | 
						|
                var b = new BillingVariant(year, avnr);
 | 
						|
                await b.Commit();
 | 
						|
            });
 | 
						|
        }
 | 
						|
 | 
						|
        public static async Task Revert(int year, int avnr) {
 | 
						|
            await Task.Run(async () => {
 | 
						|
                var b = new BillingVariant(year, avnr);
 | 
						|
                await b.Revert();
 | 
						|
            });
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |