Compare commits

...

7 Commits

13 changed files with 232 additions and 59 deletions

View File

@ -20,7 +20,15 @@ namespace Elwig.Documents {
public decimal MemberTotalUnderDelivery;
public decimal MemberAutoBusinessShares;
public CreditNote(AppDbContext ctx, PaymentMember p, CreditNoteDeliveryData data, Dictionary<string, UnderDelivery>? underDeliveries = null) :
public CreditNote(
AppDbContext ctx,
PaymentMember p,
CreditNoteDeliveryData data,
bool considerContractPenalties,
bool considerTotalPenalty,
bool considerAutoBusinessShares,
Dictionary<string, UnderDelivery>? underDeliveries = null
) :
base($"{Name} {(p.Credit != null ? $"Nr. {p.Credit.Year}/{p.Credit.TgNr:000}" : p.Member.Name)} {p.Variant.Name}", p.Member) {
UseBillingAddress = true;
ShowDateAndLocation = true;
@ -34,16 +42,6 @@ namespace Elwig.Documents {
} else {
MemberModifier = "Sonstige Zu-/Abschläge";
}
var total = data.Rows.SelectMany(r => r.Buckets).Sum(b => b.Value);
var totalUnderDelivery = total - p.Member.BusinessShares * season.MinKgPerBusinessShare;
MemberTotalUnderDelivery = totalUnderDelivery < 0 ? totalUnderDelivery * (season.PenaltyPerKg ?? 0) - (season.PenaltyAmount ?? 0) : 0;
var fromDate = $"{season.Year}-06-01";
var toDate = $"{season.Year + 1}-06-01";
MemberAutoBusinessShares = ctx.MemberHistory
.Where(h => h.MgNr == p.Member.MgNr && h.Type == "auto")
.Where(h => h.DateString.CompareTo(fromDate) >= 0 && h.DateString.CompareTo(toDate) < 0)
.Sum(h => h.BusinessShares) * (-season.BusinessShareValue ?? 0);
if (total == 0) MemberTotalUnderDelivery -= (season.PenaltyNone ?? 0);
Aside = Aside.Replace("</table>", "") +
$"<thead><tr><th colspan='2'>Gutschrift</th></tr></thead><tbody>" +
$"<tr><th>TG-Nr.</th><td>{(p.Credit != null ? $"{p.Credit.Year}/{p.Credit.TgNr:000}" : "-")}</td></tr>" +
@ -55,18 +53,35 @@ namespace Elwig.Documents {
CurrencySymbol = season.Currency.Symbol ?? season.Currency.Code;
Precision = season.Precision;
var varieties = ctx.WineVarieties.ToDictionary(v => v.SortId, v => v);
var attributes = ctx.WineAttributes.ToDictionary(a => a.AttrId, a => a);
var comTypes = ctx.AreaCommitmentTypes.ToDictionary(t => t.VtrgId, t => t);
MemberUnderDeliveries = underDeliveries?
.OrderBy(u => u.Key)
.Select(u => (
varieties[u.Key[..2]].Name + (u.Key.Length > 2 ? " " + attributes[u.Key[2..]].Name : ""),
u.Value.Diff,
u.Value.Diff * (comTypes[u.Key].PenaltyPerKg ?? 0)
- (comTypes[u.Key].PenaltyAmount ?? 0)
- ((u.Value.Weight == 0 ? comTypes[u.Key].PenaltyNone : null) ?? 0)))
.Where(u => u.Item3 != 0)
.ToList();
if (considerTotalPenalty) {
var total = data.Rows.SelectMany(r => r.Buckets).Sum(b => b.Value);
var totalUnderDelivery = total - p.Member.BusinessShares * season.MinKgPerBusinessShare;
MemberTotalUnderDelivery = totalUnderDelivery < 0 ? totalUnderDelivery * (season.PenaltyPerKg ?? 0) - (season.PenaltyAmount ?? 0) : 0;
if (total == 0)
MemberTotalUnderDelivery -= (season.PenaltyNone ?? 0);
}
if (considerAutoBusinessShares) {
var fromDate = $"{season.Year}-01-01";
var toDate = $"{season.Year}-12-31";
MemberAutoBusinessShares = ctx.MemberHistory
.Where(h => h.MgNr == p.Member.MgNr && h.Type == "auto")
.Where(h => h.DateString.CompareTo(fromDate) >= 0 && h.DateString.CompareTo(toDate) <= 0)
.Sum(h => h.BusinessShares) * (-season.BusinessShareValue ?? 0);
}
if (considerContractPenalties) {
var varieties = ctx.WineVarieties.ToDictionary(v => v.SortId, v => v);
var attributes = ctx.WineAttributes.ToDictionary(a => a.AttrId, a => a);
var comTypes = ctx.AreaCommitmentTypes.ToDictionary(t => t.VtrgId, t => t);
MemberUnderDeliveries = underDeliveries?
.OrderBy(u => u.Key)
.Select(u => (
varieties[u.Key[..2]].Name + (u.Key.Length > 2 ? " " + attributes[u.Key[2..]].Name : ""),
u.Value.Diff,
u.Value.Diff * (comTypes[u.Key].PenaltyPerKg ?? 0)
- (comTypes[u.Key].PenaltyAmount ?? 0)
- ((u.Value.Weight == 0 ? comTypes[u.Key].PenaltyNone : null) ?? 0)))
.Where(u => u.Item3 != 0)
.ToList();
}
}
}}

View File

@ -81,15 +81,20 @@
</table>
<div class="hint">
Hinweis:<br/>
Die Summe der Lieferungen und die Summe der anfallenden Pönalen werden mit
@Model.Payment?.Variant.Season.Precision Nachkommastellen berechnent,
erst das Ergebnis wird kaufmännisch auf 2 Nachkommastellen gerundet.
Die Summe der Lieferungen und die Summe der anfal&shy;lenden Pönalen werden mit
@Model.Payment?.Variant.Season.Precision Nach&shy;komma&shy;stellen berechnent,
erst das Ergebnis wird kauf&shy;männisch auf 2 Nach&shy;komma&shy;stellen gerundet.
</div>
<table class="credit-sum">
<colgroup>
<col style="width: auto;"/>
<col style="width: 5mm;"/>
<col style="width: 30mm;"/>
</colgroup>
@{
string FormatRow(string name, decimal? value, bool add = false, bool bold = false, bool subCat = false, bool noTopBorder = false) {
return $"<tr class=\"{(!add && !noTopBorder ? "sum" : !add ? "large" : "")} {(bold ? "large bold" : "")}\">"
+ $"<td class=\"{(subCat ? "small" : "")}\" style=\"overflow: visible;\">{name}:</td>"
+ $"<td class=\"{(subCat ? "small" : "")}\">{name}:</td>"
+ $"<td class=\"number {(subCat ? "small" : "large")}\">{(value < 0 ? "" : (add ? "+" : ""))}</td>"
+ $"<td class=\"number {(subCat ? "small" : "large")}\">"
+ $"<span class=\"fleft\">{Model.CurrencySymbol}</span>{Math.Abs(value ?? 0):N2}</td>"
@ -148,8 +153,9 @@
@if (Model.Credit == null) {
@Raw(FormatRow("Auszahlungsbetrag", (Model.Payment?.Amount + penalty) ?? (sum + penalty), bold: true))
} else {
if (Model.Credit.Modifiers - penalty != 0) {
@Raw(FormatRow("Weitere Abzüge", Model.Credit.Modifiers - penalty, add: true))
var diff = Model.Credit.Modifiers - penalty;
if (diff != 0) {
@Raw(FormatRow(diff < 0 ? "Weitere Abzüge" : "Weitere Zuschläge", diff, add: true))
}
if (Model.Credit.PrevModifiers != null && Model.Credit.PrevModifiers != 0) {
@Raw(FormatRow("Bereits berücksichtigte Abzüge", -Model.Credit.PrevModifiers, add: true))

View File

@ -24,8 +24,8 @@ table.credit tr.last td {
}
table.credit-sum {
width: 50%;
margin-left: 50%;
width: 60%;
margin-left: 40%;
}
table.credit-sum tr.sum,
@ -41,7 +41,7 @@ table.credit-sum td.sum {
.hint {
font-style: italic;
font-size: 8pt;
width: 74mm;
width: 56mm;
position: absolute;
left: 0;
margin: 2mm 4mm;

View File

@ -7,7 +7,7 @@
<UseWPF>true</UseWPF>
<PreserveCompilationContext>true</PreserveCompilationContext>
<ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon>
<Version>0.6.3</Version>
<Version>0.6.4</Version>
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
</PropertyGroup>

View File

@ -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 = 14;
public static readonly int RequiredSchemaVersion = 15;
private static int VersionOffset = 0;

View File

@ -1,3 +1,5 @@
using Elwig.Models.Entities;
using Microsoft.Data.Sqlite;
using System;
using System.Collections.Generic;
using System.Linq;
@ -8,6 +10,7 @@ namespace Elwig.Helpers.Billing {
protected readonly int Year;
protected readonly AppDbContext Context;
protected readonly Season Season;
protected readonly Dictionary<string, string> Attributes;
protected readonly Dictionary<string, (decimal?, decimal?)> Modifiers;
protected readonly Dictionary<string, (string, string?, string?, int?, decimal?)> AreaComTypes;
@ -15,6 +18,7 @@ namespace Elwig.Helpers.Billing {
public Billing(int year) {
Year = year;
Context = new AppDbContext();
Season = Context.Seasons.Find(Year)!;
Attributes = Context.WineAttributes.ToDictionary(a => a.AttrId, a => a.Name);
Modifiers = Context.Modifiers.Where(m => m.Year == Year).ToDictionary(m => m.ModId, m => (m.Abs, m.Rel));
AreaComTypes = Context.AreaCommitmentTypes.ToDictionary(v => v.VtrgId, v => (v.SortId, v.AttrId, v.Discriminator, v.MinKgPerHa, v.PenaltyAmount));
@ -26,8 +30,6 @@ namespace Elwig.Helpers.Billing {
UPDATE season
SET (start_date, end_date) = (SELECT MIN(date), MAX(date) FROM delivery WHERE year = {Year})
WHERE year = {Year};
DELETE FROM delivery_part_bucket WHERE year = {Year};
""");
}
@ -43,10 +45,11 @@ namespace Elwig.Helpers.Billing {
""");
}
public async Task CalculateBuckets(bool allowAttrsIntoLower, bool avoidUnderDeliveries, bool honorGebunden) {
public async Task CalculateBuckets(bool allowAttrsIntoLower, bool avoidUnderDeliveries, bool honorGebunden, SqliteConnection? cnx = null) {
var attrVals = Context.WineAttributes.ToDictionary(a => a.AttrId, a => (a.IsStrict, a.FillLower));
var attrForced = attrVals.Where(a => a.Value.IsStrict && a.Value.FillLower == 0).Select(a => a.Key).ToArray();
using var cnx = await AppDbContext.ConnectAsync();
var ownCnx = cnx == null;
cnx ??= await AppDbContext.ConnectAsync();
await Context.GetMemberAreaCommitmentBuckets(Year, 0, cnx);
var inserts = new List<(int, int, int, string, int)>();
@ -65,7 +68,7 @@ namespace Elwig.Helpers.Billing {
reader.GetInt32(0), reader.GetInt32(1), reader.GetInt32(2), reader.GetString(3), reader.GetInt32(4),
reader.GetDouble(5), reader.GetString(6),
reader.IsDBNull(7) ? null : reader.GetString(7),
reader.IsDBNull(8) ? Array.Empty<string>() : reader.GetString(8).Split(",").Order().ToArray(),
reader.IsDBNull(8) ? [] : reader.GetString(8).Split(",").Order().ToArray(),
reader.IsDBNull(9) ? null : reader.GetBoolean(9)
));
}
@ -73,11 +76,11 @@ namespace Elwig.Helpers.Billing {
int lastMgNr = 0;
Dictionary<string, AreaComBucket>? rightsAndObligations = null;
Dictionary<string, int> used = new();
Dictionary<string, int> used = [];
foreach (var (mgnr, did, dpnr, sortid, weight, kmw, qualid, attrid, modifiers, gebunden) in deliveries) {
if (lastMgNr != mgnr) {
rightsAndObligations = await Context.GetMemberAreaCommitmentBuckets(Year, mgnr);
used = new();
used = [];
}
if ((honorGebunden && gebunden == false) ||
rightsAndObligations == null || rightsAndObligations.Count == 0 ||
@ -92,16 +95,16 @@ namespace Elwig.Helpers.Billing {
}
int w = weight;
var attributes = attrid == null ? Array.Empty<string>() : new string[] { attrid };
var attributes = attrid == null ? [] : new string[] { attrid };
var isStrict = attrid != null && attrVals[attrid].IsStrict;
foreach (var p in Utils.Permutate(attributes, attributes.Intersect(attrForced))) {
var c = p.Count();
var key = sortid + string.Join("", p);
if (rightsAndObligations.ContainsKey(key)) {
if (rightsAndObligations.TryGetValue(key, out AreaComBucket value)) {
int i = (c == 0) ? 1 : 2;
var u = used.GetValueOrDefault(key, 0);
var vr = Math.Max(0, Math.Min(rightsAndObligations[key].Right - u, w));
var vo = Math.Max(0, Math.Min(rightsAndObligations[key].Obligation - u, w));
var vr = Math.Max(0, Math.Min(value.Right - u, w));
var vo = Math.Max(0, Math.Min(value.Obligation - u, w));
var v = (attributes.Length == c || attributes.Select(a => !attrVals[a].IsStrict ? 2 : attrVals[a].FillLower).Min() == 2) ? vr : vo;
used[key] = u + v;
if (key.Length > 2 && !isStrict) used[key[..2]] = used.GetValueOrDefault(key[..2], 0) + v;
@ -115,14 +118,17 @@ namespace Elwig.Helpers.Billing {
}
await AppDbContext.ExecuteBatch(cnx, $"""
UPDATE delivery_part_bucket SET value = 0 WHERE year = {Year};
INSERT INTO delivery_part_bucket (year, did, dpnr, bktnr, discr, value)
VALUES {string.Join(",\n ", inserts.Select(i => $"({Year}, {i.Item1}, {i.Item2}, {i.Item3}, '{i.Item4}', {i.Item5})"))}
ON CONFLICT DO UPDATE
SET discr = excluded.discr, value = value + excluded.value;
""");
if (!avoidUnderDeliveries)
if (!avoidUnderDeliveries) {
if (ownCnx) await cnx.DisposeAsync();
return;
}
// FIXME avoidUnderDelivery-calculations not always right!
@ -200,6 +206,8 @@ namespace Elwig.Helpers.Billing {
ON CONFLICT DO UPDATE
SET value = excluded.value;
""");
if (ownCnx) await cnx.DisposeAsync();
}
}
}

View File

@ -21,6 +21,11 @@ namespace Elwig.Helpers.Billing {
public async Task Calculate() {
using var cnx = await AppDbContext.ConnectAsync();
using var tx = await cnx.BeginTransactionAsync();
await CalculateBuckets(
Season.Billing_AllowAttrsIntoLower,
Season.Billing_AvoidUnderDeliveries,
Season.Billing_HonorGebunden,
cnx);
await DeleteInDb(cnx);
await SetCalcTime(cnx);
await CalculatePrices(cnx);

View File

@ -65,7 +65,6 @@ namespace Elwig.Models.Entities {
[Column("start_date")]
public string? StartDateString { get; set; }
[NotMapped]
public DateOnly? StartDate {
get => StartDateString != null ? DateOnly.ParseExact(StartDateString, "yyyy-MM-dd") : null;
@ -74,13 +73,30 @@ namespace Elwig.Models.Entities {
[Column("end_date")]
public string? EndDateString { get; set; }
[NotMapped]
public DateOnly? EndDate {
get => EndDateString != null ? DateOnly.ParseExact(EndDateString, "yyyy-MM-dd") : null;
set => EndDateString = value?.ToString("yyyy-MM-dd");
}
[Column("calc_mode")]
public int CalcMode { get; set; }
[NotMapped]
public bool Billing_HonorGebunden {
get => (CalcMode & 0x1) != 0;
set => CalcMode = value ? CalcMode | 0x1 : CalcMode & ~0x1;
}
[NotMapped]
public bool Billing_AllowAttrsIntoLower {
get => (CalcMode & 0x4) != 0;
set => CalcMode = value ? CalcMode | 0x4 : CalcMode & ~0x4;
}
[NotMapped]
public bool Billing_AvoidUnderDeliveries {
get => (CalcMode & 0x2) != 0;
set => CalcMode = value ? CalcMode | 0x2 : CalcMode & ~0x2;
}
[ForeignKey("CurrencyCode")]
public virtual Currency Currency { get; private set; }

View File

@ -0,0 +1,18 @@
-- schema version 14 to 15
ALTER TABLE season ADD COLUMN calc_mode INTEGER NOT NULL DEFAULT 0;
DROP TRIGGER t_payment_delivery_part_u;
CREATE TRIGGER t_payment_delivery_part_u
AFTER UPDATE ON payment_delivery_part FOR EACH ROW
BEGIN
UPDATE payment_member
SET net_amount = net_amount - OLD.amount
WHERE (year, avnr, mgnr) IN (SELECT year, OLD.avnr, mgnr FROM delivery WHERE (year, did) = (OLD.year, OLD.did));
INSERT INTO payment_member (year, avnr, mgnr, net_amount)
SELECT d.year, v.avnr, d.mgnr, NEW.amount
FROM delivery d, payment_variant v
WHERE (d.year, d.did) = (NEW.year, NEW.did) AND (v.year, v.avnr) = (NEW.year, NEW.avnr)
ON CONFLICT DO UPDATE SET net_amount = net_amount + excluded.net_amount;
END;

View File

@ -137,6 +137,7 @@ namespace Elwig.Windows {
(ConsiderPenaltyInput.IsChecked != BillingData?.ConsiderTotalPenalty) ||
(ConsiderAutoInput.IsChecked != BillingData?.ConsiderAutoBusinessShares));
CalculateButton.IsEnabled = !SaveButton.IsEnabled && PaymentVariantList.SelectedItem is PaymentVar { TestVariant: true };
CommitButton.IsEnabled = CalculateButton.IsEnabled;
}
private void UpdateSums() {
@ -285,9 +286,9 @@ namespace Elwig.Windows {
Mouse.OverrideCursor = Cursors.AppStarting;
var b = new BillingVariant(v.Year, v.AvNr);
await b.Revert();
await App.HintContextChange();
Mouse.OverrideCursor = null;
CommitButton.IsEnabled = true;
await App.HintContextChange();
}
private async void ExportButton_Click(object sender, RoutedEventArgs evt) {
@ -519,7 +520,15 @@ namespace Elwig.Windows {
var payments = await Context.MemberPayments.Where(p => p.Year == v.Year && p.AvNr == v.AvNr).ToDictionaryAsync(c => c.MgNr);
await Context.GetMemberAreaCommitmentBuckets(Year, 0);
using var doc = Document.Merge(list.Select(m =>
new CreditNote(Context, payments[m.MgNr], data[m.MgNr], Context.GetMemberUnderDelivery(Year, m.MgNr).GetAwaiter().GetResult())
new CreditNote(
Context,
payments[m.MgNr],
data[m.MgNr],
BillingData?.ConsiderContractPenalties ?? false,
BillingData?.ConsiderTotalPenalty ?? false,
BillingData?.ConsiderAutoBusinessShares ?? false,
Context.GetMemberUnderDelivery(Year, m.MgNr).GetAwaiter().GetResult()
)
));
await doc.Generate(new Progress<double>(v => {
ProgressBar.Value = v;

View File

@ -1,8 +1,8 @@
using Elwig.Dialogs;
using Elwig.Helpers;
using Elwig.Helpers.Billing;
using Elwig.Helpers.Export;
using Elwig.Models.Dtos;
using Elwig.Models.Entities;
using Microsoft.Win32;
using System;
using System.Threading.Tasks;
@ -33,19 +33,34 @@ namespace Elwig.Windows {
OverUnderDeliveryButton.IsEnabled = valid;
AutoBusinessSharesButton.IsEnabled = valid;
PaymentButton.IsEnabled = valid;
AllowAttrIntoLowerInput.IsEnabled = valid && last;
AvoidUnderDeliveriesInput.IsEnabled = valid && last;
HonorGebundenInput.IsEnabled = valid && last;
AllowAttrIntoLowerInput.IsChecked = s0?.Billing_AllowAttrsIntoLower;
AvoidUnderDeliveriesInput.IsChecked = s0?.Billing_AvoidUnderDeliveries;
HonorGebundenInput.IsChecked = s0?.Billing_HonorGebunden;
}
private async void CalculateBucketsButton_Click(object sender, RoutedEventArgs evt) {
if (SeasonInput.Value is not int year)
if (SeasonInput.Value is not int year || await Context.Seasons.FindAsync(year) is not Season s)
return;
CalculateBucketsButton.IsEnabled = false;
Mouse.OverrideCursor = Cursors.AppStarting;
try {
s.Billing_AllowAttrsIntoLower = AllowAttrIntoLowerInput.IsChecked ?? false;
s.Billing_AvoidUnderDeliveries = AvoidUnderDeliveriesInput.IsChecked ?? false;
s.Billing_HonorGebunden = HonorGebundenInput.IsChecked ?? false;
Context.Update(s);
await Context.SaveChangesAsync();
} catch { }
var b = new Billing(year);
await b.FinishSeason();
await b.CalculateBuckets(
AllowAttrIntoLowerInput.IsChecked ?? false,
AvoidUnderDeliveriesInput.IsChecked ?? false,
HonorGebundenInput.IsChecked ?? false);
s.Billing_AllowAttrsIntoLower,
s.Billing_AvoidUnderDeliveries,
s.Billing_HonorGebunden);
Mouse.OverrideCursor = null;
CalculateBucketsButton.IsEnabled = true;
}

View File

@ -31,8 +31,8 @@ namespace Tests.HelperTests {
if (Connection == null) return;
await AppDbContext.ExecuteBatch(Connection, """
DELETE FROM credit;
DELETE FROM delivery_part_bucket;
DELETE FROM payment_variant;
DELETE FROM delivery_part_bucket;
""");
}
@ -79,7 +79,7 @@ namespace Tests.HelperTests {
}
[Test]
public async Task Test_01_NoActiveAreaCommitments() {
public async Task Test_01_NoActiveAreaComs() {
int mgnr = MgNr1, year = Year1;
await InsertPaymentVariant(year, 1, """
{
@ -141,5 +141,86 @@ namespace Tests.HelperTests {
Assert.That(prices[("20201001X003/2", "GV_")], Is.EqualTo(0_5000));
});
}
[Test]
public async Task Test_02_SimpleNotStrictAreaComs() {
// TODO
}
[Test]
public async Task Test_03_SimpleNotStrictAreaComs_HonorGebunden() {
// TODO
}
[Test]
public async Task Test_04_ComplexNotStrictAreaComs() {
// TODO
}
[Test]
public async Task Test_05_ComplexNotStrictAreaComs_HonorGebunden() {
// TODO
}
[Test]
public async Task Test_06_StrictAreaComs_NoFillLower_NotAllowed() {
// TODO
}
[Test]
public async Task Test_07_StrictAreaComs_NoFillLower_Allowed() {
// TODO
}
[Test]
public async Task Test_08_StrictAreaComs_NoFillLower_Allowed_AvoidUnderDeliveries() {
// TODO
}
[Test]
public async Task Test_09_StrictAreaComs_FillLowerUntilObligation_NotAllowed() {
// TODO
}
[Test]
public async Task Test_10_StrictAreaComs_FillLowerUntilObligation_Allowed() {
// TODO
}
[Test]
public async Task Test_11_StrictAreaComs_FillLowerUntilObligation_Allowed_AvoidUnderDeliveries() {
// TODO
}
[Test]
public async Task Test_12_StrictAreaComs_FillLowerUntilObligation_NotAllowed() {
// TODO
}
[Test]
public async Task Test_13_StrictAreaComs_FillLowerUntilObligation_Allowed() {
// TODO
}
[Test]
public async Task Test_14_StrictAreaComs_FillLowerUntilObligation_Allowed_AvoidUnderDeliveries() {
// TODO
}
[Test]
public async Task Test_15_StrictAreaComs_FillLowerUntilRight_NotAllowed() {
// TODO
}
[Test]
public async Task Test_16_StrictAreaComs_FillLowerUntilRight_Allowed() {
// TODO
}
[Test]
public async Task Test_17_StrictAreaComs_FillLowerUntilRight_Allowed_AvoidUnderDeliveries() {
// TODO
}
}
}

View File

@ -1 +1 @@
curl -s -L "https://www.necronda.net/elwig/files/create.sql?v=13" -u "elwig:ganzGeheim123!" -o "Resources\Create.sql"
curl -s -L "https://www.necronda.net/elwig/files/create.sql?v=15" -u "elwig:ganzGeheim123!" -o "Resources\Create.sql"