399 lines
18 KiB
C#
399 lines
18 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) {
|
|
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<PaymentVar> 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();
|
|
}
|
|
}
|
|
}
|