Compare commits

...

23 Commits

Author SHA1 Message Date
c6cd9d7c73 Elwig: Bump version to 0.6.3 2024-01-29 14:52:31 +01:00
1b28752f4c PaymentVariantsWindow: Add button to export Buchungsliste 2024-01-29 14:51:03 +01:00
e0bdbee2ae AppDbUpdater: Add new VIEWs for penalties 2024-01-29 14:49:07 +01:00
ff3bd5cea5 Dtos: Use collection initializer 2024-01-29 12:13:15 +01:00
116d88d3d6 Ods: Add support for decimals and add number grouping 2024-01-29 12:12:02 +01:00
6bcb2fb406 Dtos: Rename CreditNote and DeliveryConfirmation DTOs 2024-01-28 22:52:26 +01:00
8665c93702 CreditNote: Add footer text 2024-01-28 22:15:30 +01:00
62496a0770 BillingTest: Small refactoring 2024-01-28 20:32:10 +01:00
8678a02318 BillingTest: Use Assert.That(...) more often 2024-01-28 20:22:38 +01:00
9de7fad139 Tests: Add BillingTest.Test_01_NoActiveAreaCommitments() 2024-01-28 20:12:37 +01:00
85c8783f7e [#4] MemberAdminWindow: Add more input fields for email addresses 2024-01-28 16:22:54 +01:00
75e02751f0 [#12] DeliveryAdminWindow: Add abgewertet filter keyword 2024-01-28 11:01:58 +01:00
ef1c3b25cf MemberListWindow: Remove window 2024-01-27 23:03:01 +01:00
255953a658 ChartWindow: Set limits to 120 Oe and 1.5 euro 2024-01-27 22:53:40 +01:00
9470b26aec ChartWindow: Define legend items as static 2024-01-27 22:47:49 +01:00
3a2bf81bd9 ChartWindow: Rework varibute selection 2024-01-27 22:41:31 +01:00
d3aca196dd Elwig: Rename (wine) variant to variety 2024-01-27 11:38:08 +01:00
519e903d1c ChartWindow: Rename graph to curve for UI 2024-01-27 11:38:08 +01:00
dd568b81e8 Billing: Rename attributeVariants and contracts to vaributes 2024-01-27 11:37:51 +01:00
31b0ae245d curl: Follow all redirects 2024-01-25 19:41:40 +01:00
46498ce337 Printing: Show message box when error in printing process occurs 2024-01-25 19:40:56 +01:00
0fff698a5d ChartWindow: Only display contracts delivered in current season 2024-01-25 12:42:46 +01:00
a71c6685f0 BillingVariant: Calculate member modifiers only if delivery modifiers are considered 2024-01-25 12:27:53 +01:00
54 changed files with 992 additions and 497 deletions

View File

@ -119,6 +119,11 @@ main h1 {
font-size: 10pt; font-size: 10pt;
} }
.main-wrapper p.custom {
white-space: pre-wrap;
break-inside: avoid;
}
.main-wrapper .hidden { .main-wrapper .hidden {
break-before: avoid; break-before: avoid;
} }

View File

@ -11,7 +11,7 @@ namespace Elwig.Documents {
public PaymentMember? Payment; public PaymentMember? Payment;
public Credit? Credit; public Credit? Credit;
public CreditNoteData Data; public CreditNoteDeliveryData Data;
public string? Text; public string? Text;
public string CurrencySymbol; public string CurrencySymbol;
public int Precision; public int Precision;
@ -20,7 +20,7 @@ namespace Elwig.Documents {
public decimal MemberTotalUnderDelivery; public decimal MemberTotalUnderDelivery;
public decimal MemberAutoBusinessShares; public decimal MemberAutoBusinessShares;
public CreditNote(AppDbContext ctx, PaymentMember p, CreditNoteData data, Dictionary<string, UnderDelivery>? underDeliveries = null) : public CreditNote(AppDbContext ctx, PaymentMember p, CreditNoteDeliveryData data, 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) { base($"{Name} {(p.Credit != null ? $"Nr. {p.Credit.Year}/{p.Credit.TgNr:000}" : p.Member.Name)} {p.Variant.Name}", p.Member) {
UseBillingAddress = true; UseBillingAddress = true;
ShowDateAndLocation = true; ShowDateAndLocation = true;
@ -50,18 +50,18 @@ namespace Elwig.Documents {
$"<tr><th>Überw. am</th><td>{p.Variant.TransferDate:dd.MM.yyyy}</td></tr>" + $"<tr><th>Überw. am</th><td>{p.Variant.TransferDate:dd.MM.yyyy}</td></tr>" +
$"<tr><th>Datum/Zeit</th><td>{p.Credit?.ModifiedTimestamp:dd.MM.yyyy} / {p.Credit?.ModifiedTimestamp:HH:mm}</td></tr>" + $"<tr><th>Datum/Zeit</th><td>{p.Credit?.ModifiedTimestamp:dd.MM.yyyy} / {p.Credit?.ModifiedTimestamp:HH:mm}</td></tr>" +
$"</tbody></table>"; $"</tbody></table>";
Text = App.Client.TextDeliveryNote; Text = App.Client.TextCreditNote;
DocumentId = $"Tr.-Gutschr. " + (p.Credit != null ? $"{p.Credit.Year}/{p.Credit.TgNr:000}" : p.MgNr); DocumentId = $"Tr.-Gutschr. " + (p.Credit != null ? $"{p.Credit.Year}/{p.Credit.TgNr:000}" : p.MgNr);
CurrencySymbol = season.Currency.Symbol ?? season.Currency.Code; CurrencySymbol = season.Currency.Symbol ?? season.Currency.Code;
Precision = season.Precision; Precision = season.Precision;
var variants = ctx.WineVarieties.ToDictionary(v => v.SortId, v => v); var varieties = ctx.WineVarieties.ToDictionary(v => v.SortId, v => v);
var attributes = ctx.WineAttributes.ToDictionary(a => a.AttrId, a => a); var attributes = ctx.WineAttributes.ToDictionary(a => a.AttrId, a => a);
var comTypes = ctx.AreaCommitmentTypes.ToDictionary(t => t.VtrgId, t => t); var comTypes = ctx.AreaCommitmentTypes.ToDictionary(t => t.VtrgId, t => t);
MemberUnderDeliveries = underDeliveries? MemberUnderDeliveries = underDeliveries?
.OrderBy(u => u.Key) .OrderBy(u => u.Key)
.Select(u => ( .Select(u => (
variants[u.Key[..2]].Name + (u.Key.Length > 2 ? " " + attributes[u.Key[2..]].Name : ""), varieties[u.Key[..2]].Name + (u.Key.Length > 2 ? " " + attributes[u.Key[2..]].Name : ""),
u.Value.Diff, u.Value.Diff,
u.Value.Diff * (comTypes[u.Key].PenaltyPerKg ?? 0) u.Value.Diff * (comTypes[u.Key].PenaltyPerKg ?? 0)
- (comTypes[u.Key].PenaltyAmount ?? 0) - (comTypes[u.Key].PenaltyAmount ?? 0)

View File

@ -49,7 +49,7 @@
@if (i == 0) { @if (i == 0) {
<td rowspan="@rows">@p.LsNr</td> <td rowspan="@rows">@p.LsNr</td>
<td rowspan="@rows">@p.DPNr</td> <td rowspan="@rows">@p.DPNr</td>
<td class="small">@p.Variant</td> <td class="small">@p.Variety</td>
<td class="small">@p.Attribute</td> <td class="small">@p.Attribute</td>
<td rowspan="@rows" class="center">@($"{p.Gradation.Oe:N0}")</td> <td rowspan="@rows" class="center">@($"{p.Gradation.Oe:N0}")</td>
<td rowspan="@rows" class="center">@($"{p.Gradation.Kmw:N1}")</td> <td rowspan="@rows" class="center">@($"{p.Gradation.Kmw:N1}")</td>
@ -158,4 +158,10 @@
} }
</tbody> </tbody>
</table> </table>
<p>Überweisung erfolgt auf Konto @(Elwig.Helpers.Utils.FormatIban(Model.Member.Iban ?? "-")).</p>
<div style="margin-top: 1em;">
@if (Model.Text != null) {
<p class="custom">@Model.Text</p>
}
</div>
</main> </main>

View File

@ -10,11 +10,11 @@ namespace Elwig.Documents {
public new static string Name => "Anlieferungsbestätigung"; public new static string Name => "Anlieferungsbestätigung";
public Season Season; public Season Season;
public DeliveryConfirmationData Data; public DeliveryConfirmationDeliveryData Data;
public string? Text = App.Client.TextDeliveryConfirmation; public string? Text = App.Client.TextDeliveryConfirmation;
public Dictionary<string, MemberBucket> MemberBuckets; public Dictionary<string, MemberBucket> MemberBuckets;
public DeliveryConfirmation(AppDbContext ctx, int year, Member m, DeliveryConfirmationData data) : public DeliveryConfirmation(AppDbContext ctx, int year, Member m, DeliveryConfirmationDeliveryData data) :
base($"{Name} {year}", m) { base($"{Name} {year}", m) {
Season = ctx.Seasons.Find(year) ?? throw new ArgumentException("invalid season"); Season = ctx.Seasons.Find(year) ?? throw new ArgumentException("invalid season");
ShowDateAndLocation = true; ShowDateAndLocation = true;

View File

@ -42,17 +42,17 @@
</thead> </thead>
<tbody> <tbody>
@{ @{
var lastVariant = ""; var lastVariety = "";
} }
@foreach (var p in Model.Data.Rows) { @foreach (var p in Model.Data.Rows) {
var rows = Math.Max(p.Buckets.Length, p.Modifiers.Length + 1); var rows = Math.Max(p.Buckets.Length, p.Modifiers.Length + 1);
var first = true; var first = true;
@for (int i = 0; i < rows; i++) { @for (int i = 0; i < rows; i++) {
<tr class="@(first ? "first" : "") @(p.Variant != lastVariant && lastVariant != "" ? "new": "") @(rows > i + 1 ? "last" : "")"> <tr class="@(first ? "first" : "") @(p.Variety != lastVariety && lastVariety != "" ? "new": "") @(rows > i + 1 ? "last" : "")">
@if (first) { @if (first) {
<td rowspan="@rows">@p.LsNr</td> <td rowspan="@rows">@p.LsNr</td>
<td rowspan="@rows">@p.DPNr</td> <td rowspan="@rows">@p.DPNr</td>
<td class="small">@p.Variant</td> <td class="small">@p.Variety</td>
<td class="small">@p.Attribute</td> <td class="small">@p.Attribute</td>
<td class="small">@p.QualityLevel</td> <td class="small">@p.QualityLevel</td>
<td rowspan="@rows" class="center">@($"{p.Gradation.Oe:N0}")</td> <td rowspan="@rows" class="center">@($"{p.Gradation.Oe:N0}")</td>
@ -80,7 +80,7 @@
first = false; first = false;
} }
</tr> </tr>
lastVariant = p.Variant; lastVariety = p.Variety;
} }
} }
<tr class="sum bold"> <tr class="sum bold">
@ -92,9 +92,9 @@
</table> </table>
@Raw(BusinessDocument.PrintSortenaufteilung(Model.MemberBuckets)) @Raw(BusinessDocument.PrintSortenaufteilung(Model.MemberBuckets))
@Raw(Model.PrintBucketTable(Model.Season, Model.MemberBuckets, includePayment: true)) @Raw(Model.PrintBucketTable(Model.Season, Model.MemberBuckets, includePayment: true))
<div class="text" style="margin-top: 2em;"> <div style="margin-top: 2em;">
@if (Model.Text != null) { @if (Model.Text != null) {
<p class="comment" style="white-space: pre-wrap; break-inside: avoid;">@Model.Text</p> <p class="custom comment">@Model.Text</p>
} }
</div> </div>
</main> </main>

View File

@ -19,7 +19,7 @@ namespace Elwig.Documents {
public DeliveryJournal(string filter, IQueryable<DeliveryPart> deliveries) : public DeliveryJournal(string filter, IQueryable<DeliveryPart> deliveries) :
this(filter, deliveries this(filter, deliveries
.Include(p => p.Delivery).ThenInclude(d => d.Member) .Include(p => p.Delivery).ThenInclude(d => d.Member)
.Include(p => p.Variant) .Include(p => p.Variety)
.ToList()) { } .ToList()) { }
public DeliveryJournal(AppDbContext ctx, DateOnly date) : public DeliveryJournal(AppDbContext ctx, DateOnly date) :

View File

@ -45,7 +45,7 @@
<td class="small">@($"{p.Delivery.Time:HH:mm}")</td> <td class="small">@($"{p.Delivery.Time:HH:mm}")</td>
<td class="number">@p.Delivery.Member.MgNr</td> <td class="number">@p.Delivery.Member.MgNr</td>
<td class="small">@p.Delivery.Member.AdministrativeName</td> <td class="small">@p.Delivery.Member.AdministrativeName</td>
<td class="small">@p.Variant.Name</td> <td class="small">@p.Variety.Name</td>
<td class="center">@($"{p.Oe:N0}")</td> <td class="center">@($"{p.Oe:N0}")</td>
<td class="center">@($"{p.Kmw:N1}")</td> <td class="center">@($"{p.Kmw:N1}")</td>
<td class="number">@($"{p.Weight:N0}")</td> <td class="number">@($"{p.Weight:N0}")</td>

View File

@ -36,7 +36,7 @@
@foreach (var part in Model.Delivery.Parts.OrderBy(p => p.DPNr)) { @foreach (var part in Model.Delivery.Parts.OrderBy(p => p.DPNr)) {
<tr class="main"> <tr class="main">
<td class="center">@part.DPNr</td> <td class="center">@part.DPNr</td>
<td colspan="2">@part.Variant.Name</td> <td colspan="2">@part.Variety.Name</td>
<td colspan="2">@part.Attribute?.Name</td> <td colspan="2">@part.Attribute?.Name</td>
<td>@part.Quality.Name</td> <td>@part.Quality.Name</td>
<td class="center">@($"{part.Oe:N0}")</td> <td class="center">@($"{part.Oe:N0}")</td>

View File

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

View File

@ -58,6 +58,7 @@ namespace Elwig.Helpers {
public DbSet<OverUnderDeliveryRow> OverUnderDeliveryRows { get; private set; } public DbSet<OverUnderDeliveryRow> OverUnderDeliveryRows { get; private set; }
public DbSet<AreaComUnderDeliveryRowSingle> AreaComUnderDeliveryRows { get; private set; } public DbSet<AreaComUnderDeliveryRowSingle> AreaComUnderDeliveryRows { get; private set; }
public DbSet<MemberDeliveryPerVariantRowSingle> MemberDeliveryPerVariantRows { get; private set; } public DbSet<MemberDeliveryPerVariantRowSingle> MemberDeliveryPerVariantRows { get; private set; }
public DbSet<CreditNoteDeliveryRowSingle> CreditNoteDeliveryRows { get; private set; }
public DbSet<CreditNoteRowSingle> CreditNoteRows { get; private set; } public DbSet<CreditNoteRowSingle> CreditNoteRows { get; private set; }
private readonly StreamWriter? LogFile = null; private readonly StreamWriter? LogFile = null;

View File

@ -9,7 +9,7 @@ namespace Elwig.Helpers {
public static class AppDbUpdater { public static class AppDbUpdater {
// Don't forget to update value in Tests/fetch-resources.bat! // Don't forget to update value in Tests/fetch-resources.bat!
public static readonly int RequiredSchemaVersion = 13; public static readonly int RequiredSchemaVersion = 14;
private static int VersionOffset = 0; private static int VersionOffset = 0;

View File

@ -150,31 +150,31 @@ namespace Elwig.Helpers.Billing {
return dict; return dict;
} }
protected static Dictionary<string, JsonValue> GetSelection(JsonNode value, IEnumerable<string> attributeVariants) { protected static Dictionary<string, JsonValue> GetSelection(JsonNode value, IEnumerable<string> vaributes) {
if (value is JsonValue flatRate) { if (value is JsonValue flatRate) {
return attributeVariants.ToDictionary(e => e, _ => flatRate); return vaributes.ToDictionary(e => e, _ => flatRate);
} if (value is not JsonObject data) { } if (value is not JsonObject data) {
throw new InvalidOperationException(); throw new InvalidOperationException();
} }
Dictionary<string, JsonValue> dict; Dictionary<string, JsonValue> dict;
if (data["default"] is JsonValue def) { if (data["default"] is JsonValue def) {
dict = attributeVariants.ToDictionary(e => e, _ => def); dict = vaributes.ToDictionary(e => e, _ => def);
} else { } else {
dict = []; dict = [];
} }
var variants = data.Where(p => !p.Key.StartsWith('/') && p.Key.Length == 2); var varieties = data.Where(p => !p.Key.StartsWith('/') && p.Key.Length == 2);
var attributes = data.Where(p => p.Key.StartsWith('/')); var attributes = data.Where(p => p.Key.StartsWith('/'));
var others = data.Where(p => !p.Key.StartsWith('/') && p.Key.Length > 2 && p.Key != "default"); var others = data.Where(p => !p.Key.StartsWith('/') && p.Key.Length > 2 && p.Key != "default");
foreach (var (idx, v) in variants) { foreach (var (idx, v) in varieties) {
var curve = v?.AsValue() ?? throw new InvalidOperationException(); var curve = v?.AsValue() ?? throw new InvalidOperationException();
foreach (var i in attributeVariants.Where(e => e.StartsWith(idx[..^1]))) { foreach (var i in vaributes.Where(e => e.StartsWith(idx[..^1]))) {
dict[i] = curve; dict[i] = curve;
} }
} }
foreach (var (idx, v) in attributes) { foreach (var (idx, v) in attributes) {
var curve = v?.AsValue() ?? throw new InvalidOperationException(); var curve = v?.AsValue() ?? throw new InvalidOperationException();
foreach (var i in attributeVariants.Where(e => e[2..] == idx[1..])) { foreach (var i in vaributes.Where(e => e[2..] == idx[1..])) {
dict[i] = curve; dict[i] = curve;
} }
} }
@ -257,7 +257,7 @@ namespace Elwig.Helpers.Billing {
return curve; return curve;
} }
protected static void CollapsePaymentData(JsonObject data, IEnumerable<string> attributeVariants) { protected static void CollapsePaymentData(JsonObject data, IEnumerable<string> vaributes, bool useDefault = true) {
Dictionary<string, List<string>> rev1 = []; Dictionary<string, List<string>> rev1 = [];
Dictionary<decimal, List<string>> rev2 = []; Dictionary<decimal, List<string>> rev2 = [];
foreach (var (k, v) in data) { foreach (var (k, v) in data) {
@ -273,18 +273,18 @@ namespace Elwig.Helpers.Billing {
} }
if (!data.ContainsKey("default")) { if (!data.ContainsKey("default")) {
foreach (var (v, ks) in rev1) { foreach (var (v, ks) in rev1) {
if (ks.Count >= attributeVariants.Count() / 2.0) { if ((ks.Count >= vaributes.Count() * 0.5 && useDefault) || ks.Count == vaributes.Count()) {
foreach (var k in ks) data.Remove(k); foreach (var k in ks) data.Remove(k);
data["default"] = v; data["default"] = v;
CollapsePaymentData(data, attributeVariants); CollapsePaymentData(data, vaributes, useDefault);
return; return;
} }
} }
foreach (var (v, ks) in rev2) { foreach (var (v, ks) in rev2) {
if (ks.Count >= attributeVariants.Count() / 2.0) { if ((ks.Count >= vaributes.Count() * 0.5 && useDefault) || ks.Count == vaributes.Count()) {
foreach (var k in ks) data.Remove(k); foreach (var k in ks) data.Remove(k);
data["default"] = v; data["default"] = v;
CollapsePaymentData(data, attributeVariants); CollapsePaymentData(data, vaributes, useDefault);
return; return;
} }
} }
@ -296,17 +296,17 @@ namespace Elwig.Helpers.Billing {
.Distinct() .Distinct()
.ToList(); .ToList();
foreach (var idx in attributes) { foreach (var idx in attributes) {
var len = attributeVariants.Count(e => e.EndsWith(idx)); var len = vaributes.Count(e => e.EndsWith(idx));
foreach (var (v, ks) in rev1) { foreach (var (v, ks) in rev1) {
var myKs = ks.Where(k => k.EndsWith(idx)).ToList(); var myKs = ks.Where(k => k.EndsWith(idx)).ToList();
if (myKs.Count > 1 && myKs.Count >= len / 2.0) { if (myKs.Count > 1 && ((myKs.Count >= len * 0.5 && useDefault) || myKs.Count == len)) {
foreach (var k in myKs) data.Remove(k); foreach (var k in myKs) data.Remove(k);
data[idx] = v; data[idx] = v;
} }
} }
foreach (var (v, ks) in rev2) { foreach (var (v, ks) in rev2) {
var myKs = ks.Where(k => k.EndsWith(idx)).ToList(); var myKs = ks.Where(k => k.EndsWith(idx)).ToList();
if (myKs.Count > 1 && myKs.Count >= len / 2.0) { if (myKs.Count > 1 && ((myKs.Count >= len * 0.5 && useDefault) || myKs.Count == len)) {
foreach (var k in myKs) data.Remove(k); foreach (var k in myKs) data.Remove(k);
data[idx] = v; data[idx] = v;
} }
@ -314,7 +314,13 @@ namespace Elwig.Helpers.Billing {
} }
} }
public static JsonObject FromGraphEntries(IEnumerable<GraphEntry> graphEntries, BillingData? origData = null, IEnumerable<string>? attributeVariants = null) { public static JsonObject FromGraphEntries(
IEnumerable<GraphEntry> graphEntries,
BillingData? origData = null,
IEnumerable<string>? vaributes = null,
bool useDefaultPayment = true,
bool useDefaultQuality = true
) {
var payment = new JsonObject(); var payment = new JsonObject();
var qualityWei = new JsonObject(); var qualityWei = new JsonObject();
var curves = new JsonArray(); var curves = new JsonArray();
@ -331,7 +337,7 @@ namespace Elwig.Helpers.Billing {
} else { } else {
continue; continue;
} }
foreach (var c in entry.Contracts) { foreach (var c in entry.Vaributes) {
if (entry.Abgewertet) { if (entry.Abgewertet) {
qualityWei[$"{c.Variety?.SortId}/{c.Attribute?.AttrId}"] = node.DeepClone(); qualityWei[$"{c.Variety?.SortId}/{c.Attribute?.AttrId}"] = node.DeepClone();
} else { } else {
@ -340,8 +346,8 @@ namespace Elwig.Helpers.Billing {
} }
} }
CollapsePaymentData(payment, attributeVariants ?? payment.Select(e => e.Key).ToList()); CollapsePaymentData(payment, vaributes ?? payment.Select(e => e.Key).ToList(), useDefaultPayment);
CollapsePaymentData(qualityWei, attributeVariants ?? qualityWei.Select(e => e.Key).ToList()); CollapsePaymentData(qualityWei, vaributes ?? qualityWei.Select(e => e.Key).ToList(), useDefaultQuality);
var data = new JsonObject { var data = new JsonObject {
["mode"] = "elwig", ["mode"] = "elwig",
@ -359,16 +365,16 @@ namespace Elwig.Helpers.Billing {
if (payment.Count == 0) { if (payment.Count == 0) {
data["payment"] = 0; data["payment"] = 0;
} else if (payment.Count == 1) { } else if (payment.Count == 1 && payment.First().Key == "default") {
data["payment"] = payment.Single().Value?.DeepClone(); data["payment"] = payment.Single().Value?.DeepClone();
} else { } else {
data["payment"] = payment; data["payment"] = payment;
} }
if (qualityWei.Count == 1) { if (qualityWei.Count == 1 && qualityWei.First().Key == "default") {
data["quality"] = new JsonObject() { data["quality"] = new JsonObject() {
["WEI"] = qualityWei.Single().Value?.DeepClone() ["WEI"] = qualityWei.Single().Value?.DeepClone()
}; };
} else if (qualityWei.Count > 1) { } else if (qualityWei.Count >= 1) {
data["quality"] = new JsonObject() { data["quality"] = new JsonObject() {
["WEI"] = qualityWei ["WEI"] = qualityWei
}; };

View File

@ -15,7 +15,7 @@ namespace Elwig.Helpers.Billing {
public BillingVariant(int year, int avnr) : base(year) { public BillingVariant(int year, int avnr) : base(year) {
AvNr = avnr; AvNr = avnr;
PaymentVariant = Context.PaymentVariants.Find(Year, AvNr) ?? throw new ArgumentException("PaymentVar not found"); PaymentVariant = Context.PaymentVariants.Find(Year, AvNr) ?? throw new ArgumentException("PaymentVar not found");
Data = PaymentBillingData.FromJson(PaymentVariant.Data, Utils.GetAttributeVarieties(Context, Year)); Data = PaymentBillingData.FromJson(PaymentVariant.Data, Utils.GetVaributes(Context, Year, onlyDelivered: false));
} }
public async Task Calculate() { public async Task Calculate() {
@ -24,9 +24,10 @@ namespace Elwig.Helpers.Billing {
await DeleteInDb(cnx); await DeleteInDb(cnx);
await SetCalcTime(cnx); await SetCalcTime(cnx);
await CalculatePrices(cnx); await CalculatePrices(cnx);
if (Data.ConsiderDelieryModifiers) if (Data.ConsiderDelieryModifiers) {
await CalculateDeliveryModifiers(cnx); await CalculateDeliveryModifiers(cnx);
await CalculateMemberModifiers(cnx); await CalculateMemberModifiers(cnx);
}
await tx.CommitAsync(); await tx.CommitAsync();
} }
@ -42,11 +43,10 @@ namespace Elwig.Helpers.Billing {
ROUND(p.amount / POW(10, s.precision - 2)) AS net_amount, ROUND(p.amount / POW(10, s.precision - 2)) AS net_amount,
ROUND(lp.amount / POW(10, s.precision - 2)) AS prev_amount, ROUND(lp.amount / POW(10, s.precision - 2)) AS prev_amount,
IIF(m.buchführend, s.vat_normal, s.vat_flatrate) AS vat, IIF(m.buchführend, s.vat_normal, s.vat_flatrate) AS vat,
ROUND( ROUND(IIF({Data.ConsiderContractPenalties}, COALESCE(u.total_penalty, 0), 0) / POW(10, 4 - 2)) +
IIF({Data.ConsiderContractPenalties}, COALESCE(u.total_penalty, 0) / POW(10, 4 - 2), 0) + ROUND(IIF({Data.ConsiderTotalPenalty}, COALESCE(b.total_penalty, 0), 0) / POW(10, s.precision - 2)) +
IIF({Data.ConsiderTotalPenalty}, COALESCE(b.total_penalty, 0), 0) + ROUND(IIF({Data.ConsiderAutoBusinessShares}, -COALESCE(a.total_amount, 0), 0) / POW(10, s.precision - 2))
IIF({Data.ConsiderAutoBusinessShares}, -COALESCE(a.business_shares * s.bs_value, 0), 0) / POW(10, s.precision - 2) AS modifiers,
) AS modifiers,
lc.modifiers AS prev_modifiers lc.modifiers AS prev_modifiers
FROM season s FROM season s
JOIN payment_variant v ON v.year = s.year JOIN payment_variant v ON v.year = s.year
@ -62,26 +62,9 @@ namespace Elwig.Helpers.Billing {
LEFT JOIN payment_member lp ON (lp.year, lp.avnr, lp.mgnr) = (l.year, l.avnr, m.mgnr) LEFT JOIN payment_member lp ON (lp.year, lp.avnr, lp.mgnr) = (l.year, l.avnr, m.mgnr)
LEFT JOIN payment_member p ON (p.year, p.avnr, p.mgnr) = (v.year, v.avnr, m.mgnr) 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 credit lc ON (lc.year, lc.avnr, lc.mgnr) = (l.year, l.avnr, m.mgnr)
LEFT JOIN (SELECT year, mgnr, LEFT JOIN v_penalty_area_commitments u ON (u.year, u.mgnr) = (s.year, m.mgnr)
SUM(COALESCE(IIF(u.weight = 0, -t.penalty_none, 0), 0) + LEFT JOIN v_penalty_business_shares b ON (b.year, b.mgnr) = (s.year, m.mgnr)
COALESCE(IIF(u.diff < 0, -t.penalty_amount, 0), 0) + LEFT JOIN v_auto_business_shares a ON (a.year, a.mgnr) = (s.year, m.mgnr)
COALESCE(u.diff * t.penalty_per_kg, 0)) AS total_penalty
FROM v_under_delivery u
JOIN area_commitment_type t ON t.vtrgid = u.bucket
GROUP BY year, mgnr) u ON (u.year, u.mgnr) = (s.year, m.mgnr)
LEFT JOIN (SELECT s.year, u.mgnr,
(COALESCE(IIF(u.weight = 0, -s.penalty_none, 0), 0) +
COALESCE(IIF(u.diff < 0, -s.penalty_amount, 0), 0) +
COALESCE(u.diff * s.penalty_per_kg, 0)
) / POW(10, s.precision - 2) AS total_penalty
FROM v_total_under_delivery u
JOIN season s ON s.year = u.year
WHERE u.diff < 0) b ON (b.year, b.mgnr) = (s.year, m.mgnr)
LEFT JOIN (SELECT h.mgnr, h.business_shares
FROM member_history h
WHERE type = 'auto' AND
date >= '{Year}-06-01' AND
date < '{Year + 1}-06-01') a ON a.mgnr = m.mgnr
WHERE s.year = {Year} AND v.avnr = {AvNr}; WHERE s.year = {Year} AND v.avnr = {AvNr};
UPDATE payment_variant SET test_variant = FALSE WHERE (year, avnr) = ({Year}, {AvNr}); UPDATE payment_variant SET test_variant = FALSE WHERE (year, avnr) = ({Year}, {AvNr});

View File

@ -7,15 +7,15 @@ using System.Text.Json.Nodes;
namespace Elwig.Helpers.Billing { namespace Elwig.Helpers.Billing {
public class EditBillingData : BillingData { public class EditBillingData : BillingData {
protected readonly IEnumerable<string> AttributeVariants; protected readonly IEnumerable<string> Vaributes;
public EditBillingData(JsonObject data, IEnumerable<string> attributeVariants) : public EditBillingData(JsonObject data, IEnumerable<string> vaributes) :
base(data) { base(data) {
AttributeVariants = attributeVariants; Vaributes = vaributes;
} }
public static EditBillingData FromJson(string json, IEnumerable<string> attributeVariants) { public static EditBillingData FromJson(string json, IEnumerable<string> vaributes) {
return new(ParseJson(json), attributeVariants); return new(ParseJson(json), vaributes);
} }
private (Dictionary<int, Curve>, Dictionary<int, List<string>>) GetGraphEntries(JsonNode root) { private (Dictionary<int, Curve>, Dictionary<int, List<string>>) GetGraphEntries(JsonNode root) {
@ -56,7 +56,7 @@ namespace Elwig.Helpers.Billing {
} }
Dictionary<int, List<string>> dict3 = curves.ToDictionary(c => c.Key, _ => new List<string>()); Dictionary<int, List<string>> dict3 = curves.ToDictionary(c => c.Key, _ => new List<string>());
foreach (var (selector, value) in GetSelection(root, AttributeVariants)) { foreach (var (selector, value) in GetSelection(root, Vaributes)) {
int? idx = null; int? idx = null;
if (value.TryGetValue<decimal>(out var val)) { if (value.TryGetValue<decimal>(out var val)) {
idx = Array.IndexOf(virtCurves, val) + virtOffset; idx = Array.IndexOf(virtCurves, val) + virtOffset;
@ -70,12 +70,16 @@ namespace Elwig.Helpers.Billing {
return (curves, dict3); return (curves, dict3);
} }
private static List<GraphEntry> CreateGraphEntries(AppDbContext ctx, int precision, Dictionary<int, Curve> curves, Dictionary<int, List<string>> entries) { private static List<GraphEntry> CreateGraphEntries(
AppDbContext ctx, int precision,
Dictionary<int, Curve> curves,
Dictionary<int, List<string>> entries
) {
var vars = ctx.WineVarieties.ToDictionary(v => v.SortId, v => v); var vars = ctx.WineVarieties.ToDictionary(v => v.SortId, v => v);
var attrs = ctx.WineAttributes.ToDictionary(a => a.AttrId, a => a); var attrs = ctx.WineAttributes.ToDictionary(a => a.AttrId, a => a);
return entries return entries
.Select(e => new GraphEntry(e.Key, precision, curves[e.Key], e.Value .Select(e => new GraphEntry(e.Key, precision, curves[e.Key], e.Value
.Select(s => new ContractSelection(vars[s[..2]], s.Length > 2 ? attrs[s[2..]] : null)) .Select(s => new Varibute(vars[s[..2]], s.Length > 2 ? attrs[s[2..]] : null))
.ToList())) .ToList()))
.ToList(); .ToList();
} }
@ -83,7 +87,7 @@ namespace Elwig.Helpers.Billing {
public IEnumerable<GraphEntry> GetPaymentGraphEntries(AppDbContext ctx, Season season) { public IEnumerable<GraphEntry> GetPaymentGraphEntries(AppDbContext ctx, Season season) {
var root = GetPaymentEntry(); var root = GetPaymentEntry();
var (curves, entries) = GetGraphEntries(root); var (curves, entries) = GetGraphEntries(root);
return CreateGraphEntries(ctx, season.Precision, curves, entries).Where(e => e.Contracts.Count > 0); return CreateGraphEntries(ctx, season.Precision, curves, entries).Where(e => e.Vaributes.Count > 0);
} }
public IEnumerable<GraphEntry> GetQualityGraphEntries(AppDbContext ctx, Season season, int idOffset = 0) { public IEnumerable<GraphEntry> GetQualityGraphEntries(AppDbContext ctx, Season season, int idOffset = 0) {
@ -91,7 +95,7 @@ namespace Elwig.Helpers.Billing {
if (root == null || root["WEI"] is not JsonNode qualityWei) if (root == null || root["WEI"] is not JsonNode qualityWei)
return []; return [];
var (curves, entries) = GetGraphEntries(qualityWei); var (curves, entries) = GetGraphEntries(qualityWei);
var list = CreateGraphEntries(ctx, season.Precision, curves, entries).Where(e => e.Contracts.Count > 0); var list = CreateGraphEntries(ctx, season.Precision, curves, entries).Where(e => e.Vaributes.Count > 0);
foreach (var e in list) { foreach (var e in list) {
e.Id += idOffset; e.Id += idOffset;
e.Abgewertet = true; e.Abgewertet = true;

View File

@ -7,7 +7,7 @@ namespace Elwig.Helpers.Billing {
public const int MinX = 50; public const int MinX = 50;
public const int MinXGeb = 73; public const int MinXGeb = 73;
public const int MaxX = 140; public const int MaxX = 120;
public int Id { get; set; } public int Id { get; set; }
public BillingData.CurveMode Mode { get; set; } public BillingData.CurveMode Mode { get; set; }
@ -36,10 +36,10 @@ namespace Elwig.Helpers.Billing {
} }
} }
public List<ContractSelection> Contracts { get; set; } public List<Varibute> Vaributes { get; set; }
public string ContractsStringSimple => (Abgewertet ? "Abgew.: " : "") + (Contracts.Count != 0 ? (Contracts.Count >= 25 ? "Restliche Sorten" : string.Join(", ", Contracts.Select(c => c.Listing))) : "-"); public string VaributeStringSimple => (Abgewertet ? "Abgew.: " : "") + (Vaributes.Count != 0 ? (Vaributes.Count >= 25 ? "Restliche Sorten" : string.Join(", ", Vaributes.Select(c => c.Listing))) : "-");
public string ContractsString => Contracts.Count != 0 ? string.Join("\n", Contracts.Select(c => c.FullName)) : "-"; public string VaributeString => Vaributes.Count != 0 ? string.Join("\n", Vaributes.Select(c => c.FullName)) : "-";
public string ContractsStringChange => (Abgewertet ? "A." : "") + string.Join(",", Contracts.Select(c => c.Listing)); public string VaributeStringChange => (Abgewertet ? "A." : "") + string.Join(",", Vaributes.Select(c => c.Listing));
private readonly int Precision; private readonly int Precision;
public GraphEntry(int id, int precision, BillingData.CurveMode mode) { public GraphEntry(int id, int precision, BillingData.CurveMode mode) {
@ -47,7 +47,7 @@ namespace Elwig.Helpers.Billing {
Precision = precision; Precision = precision;
Mode = mode; Mode = mode;
DataGraph = new Graph(precision, MinX, MaxX); ; DataGraph = new Graph(precision, MinX, MaxX); ;
Contracts = []; Vaributes = [];
} }
public GraphEntry(int id, int precision, BillingData.CurveMode mode, Dictionary<double, decimal> data, Dictionary<double, decimal>? gebunden) : public GraphEntry(int id, int precision, BillingData.CurveMode mode, Dictionary<double, decimal> data, Dictionary<double, decimal>? gebunden) :
@ -56,21 +56,21 @@ namespace Elwig.Helpers.Billing {
if (gebunden != null) GebundenGraph = new Graph(gebunden, precision, MinXGeb, MaxX); if (gebunden != null) GebundenGraph = new Graph(gebunden, precision, MinXGeb, MaxX);
} }
public GraphEntry(int id, int precision, BillingData.Curve curve, List<ContractSelection> contracts) : public GraphEntry(int id, int precision, BillingData.Curve curve, List<Varibute> vaributes) :
this(id, precision, curve.Mode) { this(id, precision, curve.Mode) {
DataGraph = new Graph(curve.Normal, precision, MinX, MaxX); DataGraph = new Graph(curve.Normal, precision, MinX, MaxX);
if (curve.Gebunden != null) if (curve.Gebunden != null)
GebundenGraph = new Graph(curve.Gebunden, precision, MinXGeb, MaxX); GebundenGraph = new Graph(curve.Gebunden, precision, MinXGeb, MaxX);
Contracts = contracts; Vaributes = vaributes;
} }
private GraphEntry(int id, int precision, BillingData.CurveMode mode, Graph dataGraph, Graph? gebundenGraph, List<ContractSelection> contracts) { private GraphEntry(int id, int precision, BillingData.CurveMode mode, Graph dataGraph, Graph? gebundenGraph, List<Varibute> vaributes) {
Id = id; Id = id;
Precision = precision; Precision = precision;
Mode = mode; Mode = mode;
DataGraph = dataGraph; DataGraph = dataGraph;
GebundenGraph = gebundenGraph; GebundenGraph = gebundenGraph;
Contracts = contracts; Vaributes = vaributes;
} }
public void AddGebundenGraph() { public void AddGebundenGraph() {

View File

@ -9,24 +9,24 @@ namespace Elwig.Helpers.Billing {
protected readonly Dictionary<int, Curve> Curves; protected readonly Dictionary<int, Curve> Curves;
protected readonly Dictionary<string, Curve> PaymentData; protected readonly Dictionary<string, Curve> PaymentData;
protected readonly Dictionary<string, Curve> QualityData; protected readonly Dictionary<string, Curve> QualityData;
protected readonly IEnumerable<string> AttributeVariants; protected readonly IEnumerable<string> Vaributes;
public PaymentBillingData(JsonObject data, IEnumerable<string> attributeVariants) : public PaymentBillingData(JsonObject data, IEnumerable<string> vaributes) :
base(data) { base(data) {
if (attributeVariants.Any(e => e.Any(c => c < 'A' || c > 'Z'))) if (vaributes.Any(e => e.Any(c => c < 'A' || c > 'Z')))
throw new ArgumentException("Invalid attributeVariants"); throw new ArgumentException("Invalid vaributes");
AttributeVariants = attributeVariants; Vaributes = vaributes;
Curves = GetCurves(); Curves = GetCurves();
PaymentData = GetPaymentData(); PaymentData = GetPaymentData();
QualityData = GetQualityData(); QualityData = GetQualityData();
} }
public static PaymentBillingData FromJson(string json, IEnumerable<string> attributeVariants) { public static PaymentBillingData FromJson(string json, IEnumerable<string> vaributes) {
return new(ParseJson(json), attributeVariants); return new(ParseJson(json), vaributes);
} }
private Dictionary<string, Curve> GetData(JsonNode data) { private Dictionary<string, Curve> GetData(JsonNode data) {
return GetSelection(data, AttributeVariants).ToDictionary(e => e.Key, e => LookupCurve(e.Value)); return GetSelection(data, Vaributes).ToDictionary(e => e.Key, e => LookupCurve(e.Value));
} }
protected Dictionary<string, Curve> GetPaymentData() { protected Dictionary<string, Curve> GetPaymentData() {

View File

@ -2,14 +2,17 @@
using System; using System;
namespace Elwig.Helpers.Billing { namespace Elwig.Helpers.Billing {
public class ContractSelection : IComparable<ContractSelection> { public class Varibute : IComparable<Varibute> {
public WineVar? Variety { get; } public WineVar? Variety { get; }
public WineAttr? Attribute { get; } public WineAttr? Attribute { get; }
public int? AssignedGraphId { get; set; }
public int? AssignedAbgewGraphId { get; set; }
public string Listing => $"{Variety?.SortId}{Attribute?.AttrId}"; public string Listing => $"{Variety?.SortId}{Attribute?.AttrId}";
public string FullName => $"{Variety?.Name}" + (Variety != null && Attribute != null ? " " : "") + $"{Attribute?.Name}"; public string FullName => $"{Variety?.Name}" + (Variety != null && Attribute != null ? " " : "") + $"{Attribute?.Name}";
public ContractSelection(WineVar? var, WineAttr? attr) { public Varibute(WineVar? var, WineAttr? attr) {
Variety = var; Variety = var;
Attribute = attr; Attribute = attr;
} }
@ -18,7 +21,7 @@ namespace Elwig.Helpers.Billing {
return Listing; return Listing;
} }
public int CompareTo(ContractSelection? other) { public int CompareTo(Varibute? other) {
return Listing.CompareTo(other?.Listing); return Listing.CompareTo(other?.Listing);
} }
} }

View File

@ -60,6 +60,7 @@ namespace Elwig.Helpers {
public string? TextDeliveryNote; public string? TextDeliveryNote;
public string? TextDeliveryConfirmation; public string? TextDeliveryConfirmation;
public string? TextCreditNote;
public ClientParameters(AppDbContext ctx) : this(ctx.ClientParameters.ToDictionary(e => e.Param, e => e.Value)) { } public ClientParameters(AppDbContext ctx) : this(ctx.ClientParameters.ToDictionary(e => e.Param, e => e.Value)) { }
@ -99,6 +100,8 @@ namespace Elwig.Helpers {
if (TextDeliveryNote == "") TextDeliveryNote = null; if (TextDeliveryNote == "") TextDeliveryNote = null;
TextDeliveryConfirmation = parameters.GetValueOrDefault("TEXT_DELIVERYCONFIRMATION"); TextDeliveryConfirmation = parameters.GetValueOrDefault("TEXT_DELIVERYCONFIRMATION");
if (TextDeliveryConfirmation == "") TextDeliveryConfirmation = null; if (TextDeliveryConfirmation == "") TextDeliveryConfirmation = null;
TextCreditNote = parameters.GetValueOrDefault("TEXT_CREDITNOTE");
if (TextCreditNote == "") TextCreditNote = null;
} catch { } catch {
throw new KeyNotFoundException(); throw new KeyNotFoundException();
} }
@ -133,6 +136,7 @@ namespace Elwig.Helpers {
("DOCUMENT_SENDER", Sender2), ("DOCUMENT_SENDER", Sender2),
("TEXT_DELIVERYNOTE", TextDeliveryNote), ("TEXT_DELIVERYNOTE", TextDeliveryNote),
("TEXT_DELIVERYCONFIRMATION", TextDeliveryConfirmation), ("TEXT_DELIVERYCONFIRMATION", TextDeliveryConfirmation),
("TEXT_CREDITNOTE", TextCreditNote),
}; };
} }

View File

@ -108,19 +108,19 @@ namespace Elwig.Helpers.Export {
<style:paragraph-properties fo:text-align="center"/> <style:paragraph-properties fo:text-align="center"/>
<style:text-properties fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold"/> <style:text-properties fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
</style:style> </style:style>
<number:number-style style:name="NN0"><number:number number:decimal-places="0" number:min-decimal-places="0" number:min-integer-digits="1"/></number:number-style> <number:number-style style:name="NN0"><number:number number:decimal-places="0" number:min-decimal-places="0" number:min-integer-digits="1" number:grouping="true"/></number:number-style>
<style:style style:name="N0" style:family="table-cell" style:parent-style-name="default" style:data-style-name="NN0"/> <style:style style:name="N0" style:family="table-cell" style:parent-style-name="default" style:data-style-name="NN0"/>
<number:number-style style:name="NN1"><number:number number:decimal-places="1" number:min-decimal-places="1" number:min-integer-digits="1"/></number:number-style> <number:number-style style:name="NN1"><number:number number:decimal-places="1" number:min-decimal-places="1" number:min-integer-digits="1" number:grouping="true"/></number:number-style>
<style:style style:name="N1" style:family="table-cell" style:parent-style-name="default" style:data-style-name="NN1"/> <style:style style:name="N1" style:family="table-cell" style:parent-style-name="default" style:data-style-name="NN1"/>
<number:number-style style:name="NN2"><number:number number:decimal-places="2" number:min-decimal-places="2" number:min-integer-digits="1"/></number:number-style> <number:number-style style:name="NN2"><number:number number:decimal-places="2" number:min-decimal-places="2" number:min-integer-digits="1" number:grouping="true"/></number:number-style>
<style:style style:name="N2" style:family="table-cell" style:parent-style-name="default" style:data-style-name="NN2"/> <style:style style:name="N2" style:family="table-cell" style:parent-style-name="default" style:data-style-name="NN2"/>
<number:number-style style:name="NN3"><number:number number:decimal-places="3" number:min-decimal-places="3" number:min-integer-digits="1"/></number:number-style> <number:number-style style:name="NN3"><number:number number:decimal-places="3" number:min-decimal-places="3" number:min-integer-digits="1" number:grouping="true"/></number:number-style>
<style:style style:name="N3" style:family="table-cell" style:parent-style-name="default" style:data-style-name="NN3"/> <style:style style:name="N3" style:family="table-cell" style:parent-style-name="default" style:data-style-name="NN3"/>
<number:number-style style:name="NN4"><number:number number:decimal-places="4" number:min-decimal-places="4" number:min-integer-digits="1"/></number:number-style> <number:number-style style:name="NN4"><number:number number:decimal-places="4" number:min-decimal-places="4" number:min-integer-digits="1" number:grouping="true"/></number:number-style>
<style:style style:name="N4" style:family="table-cell" style:parent-style-name="default" style:data-style-name="NN4"/> <style:style style:name="N4" style:family="table-cell" style:parent-style-name="default" style:data-style-name="NN4"/>
<number:number-style style:name="NN5"><number:number number:decimal-places="5" number:min-decimal-places="5" number:min-integer-digits="1"/></number:number-style> <number:number-style style:name="NN5"><number:number number:decimal-places="5" number:min-decimal-places="5" number:min-integer-digits="1" number:grouping="true"/></number:number-style>
<style:style style:name="N5" style:family="table-cell" style:parent-style-name="default" style:data-style-name="NN5"/> <style:style style:name="N5" style:family="table-cell" style:parent-style-name="default" style:data-style-name="NN5"/>
<number:number-style style:name="NN6"><number:number number:decimal-places="6" number:min-decimal-places="6" number:min-integer-digits="1"/></number:number-style> <number:number-style style:name="NN6"><number:number number:decimal-places="6" number:min-decimal-places="6" number:min-integer-digits="1" number:grouping="true"/></number:number-style>
<style:style style:name="N6" style:family="table-cell" style:parent-style-name="default" style:data-style-name="NN6"/> <style:style style:name="N6" style:family="table-cell" style:parent-style-name="default" style:data-style-name="NN6"/>
</office:automatic-styles> </office:automatic-styles>
<office:body> <office:body>
@ -262,13 +262,14 @@ namespace Elwig.Helpers.Export {
string c; string c;
if (data == null) { if (data == null) {
c = $"<{ct}{add}/>"; c = $"<{ct}{add}/>";
} else if (data is float || data is double || data is byte || data is char || } else if (data is decimal || data is float || data is double || data is byte || data is char ||
data is short || data is ushort || data is int || data is uint || data is long || data is ulong) { data is short || data is ushort || data is int || data is uint || data is long || data is ulong) {
double v = double.Parse(data?.ToString() ?? "0"); // use default culture for ToString and Parse()! double v = double.Parse(data?.ToString() ?? "0"); // use default culture for ToString and Parse()!
if (units != null && units.Length > 0) { if (units != null && units.Length > 0) {
int n = -1; int n = -1;
switch (units[0]) { switch (units[0]) {
case "%": n = 1; data = $"{v:N1}"; break; case "%": n = 1; data = $"{v:N1}"; break;
case "€": n = 2; data = $"{v:N2}"; break;
case "°KMW": n = 1; data = $"{v:N1}"; break; case "°KMW": n = 1; data = $"{v:N1}"; break;
case "°Oe": n = 0; data = $"{v:N0}"; break; case "°Oe": n = 0; data = $"{v:N0}"; break;
} }

View File

@ -74,12 +74,16 @@ namespace Elwig.Helpers.Printing {
} }
public static async Task Print(string path, int copies = 1) { public static async Task Print(string path, int copies = 1) {
var p = new Process() { StartInfo = new() { FileName = PdfToPrinter } }; try {
p.StartInfo.ArgumentList.Add(path); var p = new Process() { StartInfo = new() { FileName = PdfToPrinter } };
p.StartInfo.ArgumentList.Add("/s"); p.StartInfo.ArgumentList.Add(path);
p.StartInfo.ArgumentList.Add($"copies={copies}"); p.StartInfo.ArgumentList.Add("/s");
p.Start(); p.StartInfo.ArgumentList.Add($"copies={copies}");
await p.WaitForExitAsync(); p.Start();
await p.WaitForExitAsync();
} catch (Exception e) {
MessageBox.Show("Beim Drucken ist ein Fehler aufgetreten:\n\n" + e.Message, "Fehler beim Drucken");
}
} }
} }
} }

View File

@ -163,7 +163,7 @@ namespace Elwig.Helpers {
} }
public static string FormatIban(string iban) { public static string FormatIban(string iban) {
return Regex.Replace(iban, ".{4}", "$0 "); return Regex.Replace(iban.Trim(), ".{4}", "$0 ").Trim();
} }
public static void RunBackground(string title, Func<Task> a) { public static void RunBackground(string title, Func<Task> a) {
@ -362,25 +362,21 @@ namespace Elwig.Helpers {
return output.OrderByDescending(l => l.Count()); return output.OrderByDescending(l => l.Count());
} }
public static List<string> GetAttributeVarieties(AppDbContext ctx, int year, bool withSlash = false) { public static List<string> GetVaributes(AppDbContext ctx, int year, bool withSlash = false, bool onlyDelivered = true) {
return ctx.DeliveryParts var varieties = ctx.WineVarieties.Select(v => v.SortId).ToList();
var delivered = ctx.DeliveryParts
.Where(d => d.Year == year) .Where(d => d.Year == year)
.Select(d => $"{d.SortId}{(withSlash ? "/" : "")}{d.AttrId}") .Select(d => $"{d.SortId}{(withSlash ? "/" : "")}{d.AttrId}")
.Distinct() .Distinct()
.ToList()
.Union(ctx.WineVarieties.Select(v => v.SortId))
.ToList(); .ToList();
return [.. (onlyDelivered ? delivered : delivered.Union(varieties)).Order()];
} }
public static List<ContractSelection> GetContractsForYear(AppDbContext ctx, int year) { public static List<Varibute> GetVaributeList(AppDbContext ctx, int year, bool onlyDelivered = true) {
return ctx.DeliveryParts var varieties = ctx.WineVarieties.ToDictionary(v => v.SortId, v => v);
.Where(p => p.Year == year) var attributes = ctx.WineAttributes.ToDictionary(a => a.AttrId, a => a);
.Select(d => new ContractSelection(d.Variant, d.Attribute)) return GetVaributes(ctx, year, false, onlyDelivered)
.Distinct() .Select(s => new Varibute(varieties[s[..2]], s.Length > 2 ? attributes[s[2..]] : null))
.ToList()
.Union(ctx.WineVarieties.Select(v => new ContractSelection(v, null)))
.DistinctBy(c => c.Listing)
.Order()
.ToList(); .ToList();
} }
} }

View File

@ -1,5 +1,4 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.Linq; using System.Linq;
@ -8,7 +7,7 @@ using System.Threading.Tasks;
namespace Elwig.Models.Dtos { namespace Elwig.Models.Dtos {
public class AreaComUnderDeliveryData : DataTable<AreaComUnderDeliveryRow> { public class AreaComUnderDeliveryData : DataTable<AreaComUnderDeliveryRow> {
private static readonly (string, string, string?, int)[] FieldNames = new[] { private static readonly (string, string, string?, int)[] FieldNames = [
("MgNr", "MgNr.", null, 12), ("MgNr", "MgNr.", null, 12),
("Name", "Name", null, 40), ("Name", "Name", null, 40),
("GivenName", "Vorname", null, 40), ("GivenName", "Vorname", null, 40),
@ -20,7 +19,7 @@ namespace Elwig.Models.Dtos {
("DeliveryObligations", "Lieferpflicht", "kg", 22), ("DeliveryObligations", "Lieferpflicht", "kg", 22),
("Weights", "Geliefert", "kg", 22), ("Weights", "Geliefert", "kg", 22),
("UnderDeliveries", "Unterliefert", "kg|%", 34), ("UnderDeliveries", "Unterliefert", "kg|%", 34),
}; ];
public AreaComUnderDeliveryData(IEnumerable<AreaComUnderDeliveryRow> rows, int year) : public AreaComUnderDeliveryData(IEnumerable<AreaComUnderDeliveryRow> rows, int year) :
base($"Unterlieferungen FB", $"Unterlieferungen laut Flächenbindungen {year}", rows, FieldNames) { base($"Unterlieferungen FB", $"Unterlieferungen laut Flächenbindungen {year}", rows, FieldNames) {

View File

@ -1,162 +1,180 @@
using Elwig.Helpers; using Elwig.Helpers;
using Elwig.Helpers.Billing; using Elwig.Helpers.Billing;
using Elwig.Models.Entities;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.Core.Common.CommandTrees.ExpressionBuilder;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Elwig.Models.Dtos { namespace Elwig.Models.Dtos {
public class CreditNoteData : DataTable<CreditNoteRow> { public class CreditNoteData : DataTable<CreditNoteRow> {
private static readonly (string, string, string?)[] FieldNames = new[] { private static readonly (string, string, string?, int)[] FieldNames = [
("", "", (string?)null), // TODO ("MgNr", "MgNr.", null, 12),
}; ("Name", "Name", null, 40),
("GivenName", "Vorname", null, 40),
("Address", "Adresse", null, 60),
("Plz", "PLZ", null, 10),
("Locality", "Ort", null, 60),
("Iban", "IBAN", null, 45),
("TgNr", "TG-Nr.", null, 20),
("Sum", "Zwischens.", "€", 20),
("Surcharge", "Zuschlag", "€", 20),
("Total", "Gesamt", "€", 20),
("ConsideredSum", "Berückstgt.", "€", 20),
("Net", "Netto", "€", 20),
("Vat1", "10% MwSt.", "€", 20),
("Vat2", "13% MwSt.", "€", 20),
("Gross", "Brutto", "€", 20),
("Penalties", "Pönalen FB", "€", 20),
("Penalty", "Unterl. GA", "€", 20),
("AutoBs", "GA Nachz.", "€", 20),
("Others", "Sonstige", "€", 20),
("Considered", "Berückstgt.", "€", 20),
("Amount", "Betrag", "€", 20),
];
private readonly int Year; public CreditNoteData(IEnumerable<CreditNoteRow> rows, int year, string name) :
private readonly int? TgNr; base($"Buchungsliste", $"Buchungsliste {name} {year}", rows, FieldNames) {
private readonly int? AvNr;
private readonly int? MgNr;
private CreditNoteData(IEnumerable<CreditNoteRow> rows, int year, int? tgnr, int? avnr = null, int? mgnr = null) :
base($"Traubengutschrift {year}/{tgnr}", rows, FieldNames) {
Year = year;
TgNr = tgnr;
AvNr = avnr;
MgNr = mgnr;
} }
public static async Task<IDictionary<int, CreditNoteData>> ForPaymentVariant(DbSet<CreditNoteRowSingle> table, DbSet<Season> seasons, int year, int avnr) { public static async Task<CreditNoteData> ForPaymentVariant(AppDbContext ctx, int year, int avnr) {
return (await FromDbSet(table, year, avnr)) var variant = await ctx.PaymentVariants.FindAsync(year, avnr);
.GroupBy( var name = variant!.Name;
r => new { r.Year, r.AvNr, r.MgNr, r.TgNr, r.DId, r.DPNr }, var data = BillingData.FromJson(variant!.Data);
(k, g) => new CreditNoteRow(g, seasons)) var rows = (await FromDbSet(ctx.CreditNoteRows, year, avnr)).Select(r => new CreditNoteRow(r, data)).ToList();
.GroupBy( return new CreditNoteData(rows, year, name);
r => new { r.Year, r.AvNr, r.MgNr, r.TgNr },
(k, g) => new CreditNoteData(g, k.Year, k.TgNr, mgnr: k.MgNr))
.ToDictionary(d => d.MgNr ?? 0);
} }
private static async Task<IEnumerable<CreditNoteRowSingle>> FromDbSet(DbSet<CreditNoteRowSingle> table, int? year = null, int? avnr = null, int? mgnr = null) { private static async Task<IEnumerable<CreditNoteRowSingle>> FromDbSet(DbSet<CreditNoteRowSingle> table, int year, int avnr) {
var y = year?.ToString() ?? "NULL"; return await table.FromSql($"""
var v = avnr?.ToString() ?? "NULL"; SELECT m.mgnr, m.family_name, m.given_name, p.plz, o.name AS ort, m.address, m.iban, c.tgnr, s.year, s.precision,
var m = mgnr?.ToString() ?? "NULL"; p.amount - p.net_amount AS surcharge,
return await table.FromSqlRaw($""" c.net_amount, c.prev_net_amount, c.vat, c.vat_amount, c.gross_amount, c.modifiers, c.prev_modifiers, c.amount,
SELECT d.year, c.tgnr, v.avnr, d.mgnr, d.did, d.lsnr, d.dpnr, d.weight, d.modifiers, ROUND(COALESCE(u.total_penalty, 0) / POW(10, 4 - 2)) AS fb_penalty,
b.bktnr, d.sortid, b.discr, b.value, pb.price, pb.amount, p.net_amount, p.amount AS total_amount, ROUND(COALESCE(b.total_penalty, 0) / POW(10, s.precision - 2)) AS bs_penalty,
s.name AS variant, a.name AS attribute, q.name AS quality_level, d.oe, d.kmw ROUND(COALESCE(a.total_amount, 0) / POW(10, s.precision - 2)) AS auto_bs
FROM v_delivery d FROM credit c
JOIN wine_variety s ON s.sortid = d.sortid LEFT JOIN member m ON m.mgnr = c.mgnr
LEFT JOIN wine_attribute a ON a.attrid = d.attrid LEFT JOIN payment_member p ON (p.year, p.avnr, p.mgnr) = (c.year, c.avnr, c.mgnr)
JOIN wine_quality_level q ON q.qualid = d.qualid LEFT JOIN AT_plz_dest p ON p.id = m.postal_dest
LEFT JOIN delivery_part_bucket b ON (b.year, b.did, b.dpnr) = (d.year, d.did, d.dpnr) LEFT JOIN AT_ort o ON o.okz = p.okz
LEFT JOIN payment_variant v ON v.year = d.year LEFT JOIN season s ON s.year = c.year
LEFT JOIN payment_delivery_part p ON (p.year, p.did, p.dpnr, p.avnr) = (d.year, d.did, d.dpnr, v.avnr) LEFT JOIN v_penalty_area_commitments u ON (u.year, u.mgnr) = (s.year, m.mgnr)
LEFT JOIN payment_delivery_part_bucket pb ON (pb.year, pb.did, pb.dpnr, pb.bktnr, pb.avnr) = (b.year, b.did, b.dpnr, b.bktnr, v.avnr) LEFT JOIN v_penalty_business_shares b ON (b.year, b.mgnr) = (s.year, m.mgnr)
LEFT JOIN credit c ON (c.year, c.avnr, c.mgnr) = (d.year, v.avnr, d.mgnr) LEFT JOIN v_auto_business_shares a ON (a.year, a.mgnr) = (s.year, m.mgnr)
WHERE b.value > 0 AND (d.year = {y} OR {y} IS NULL) AND (v.avnr = {v} OR {v} IS NULL) AND (d.mgnr = {m} OR {m} IS NULL) WHERE c.year = {year} AND c.avnr = {avnr}
ORDER BY d.year, v.avnr, d.mgnr, d.lsnr, d.dpnr ORDER BY m.mgnr
""").ToListAsync(); """).ToListAsync();
} }
} }
public class CreditNoteRow { public class CreditNoteRow {
public int Year;
public int? TgNr;
public int AvNr;
public int MgNr; public int MgNr;
public string Name;
public string GivenName;
public string Address;
public int Plz;
public string Locality;
public string Iban;
public string TgNr;
public decimal Sum;
public decimal? Surcharge;
public decimal Total;
public decimal? ConsideredSum;
public decimal Net;
public decimal? Vat1, Vat2;
public decimal Gross;
public decimal? Penalties;
public decimal? Penalty;
public decimal? AutoBs;
public decimal? Others;
public decimal? Considered;
public decimal Amount;
public string LsNr; public CreditNoteRow(CreditNoteRowSingle row, BillingData data) {
public int DPNr; byte prec1 = 2, prec2 = row.Precision;
public string Variant; MgNr = row.MgNr;
public string? Attribute; Name = row.Name;
public string[] Modifiers; GivenName = row.GivenName;
public string QualityLevel; Address = row.Address;
public (double Oe, double Kmw) Gradation; Plz = row.Plz;
public (string Name, int Value, decimal? Price, decimal? Amount)[] Buckets; Locality = row.Locality;
public decimal? TotalModifiers; Iban = Utils.FormatIban(row.Iban);
public decimal? Amount; TgNr = $"{row.Year}/{row.TgNr}";
Total = Utils.DecFromDb(row.NetAmount, prec1);
public CreditNoteRow(IEnumerable<CreditNoteRowSingle> rows, DbSet<Season> seasons) { Surcharge = (row.Surcharge == null || row.Surcharge == 0) ? null : Utils.DecFromDb((long)row.Surcharge, prec2);
var f = rows.First(); Sum = Total - (Surcharge ?? 0);
Year = f.Year; ConsideredSum = (row.PrevNetAmount == null ||row.PrevNetAmount == 0) ? null : -Utils.DecFromDb((long)row.PrevNetAmount, prec1);
TgNr = f.TgNr; Net = Total + (ConsideredSum ?? 0);
MgNr = f.MgNr; if (row.Vat == 0.10) {
var season = seasons.Find(Year); Vat1 = Utils.DecFromDb(row.VatAmount, prec1);
} else if (row.Vat == 0.13) {
LsNr = f.LsNr; Vat2 = Utils.DecFromDb(row.VatAmount, prec1);
DPNr = f.DPNr; }
Variant = f.Variant; decimal mod = (row.Modifiers == null) ? 0 : Utils.DecFromDb((long)row.Modifiers, prec1);
Attribute = f.Attribute; if (data.ConsiderContractPenalties)
var modifiers = (IEnumerable<Modifier>)(f.Modifiers ?? "").Split(',') Penalties = (row.FbPenalty == null || row.FbPenalty == 0) ? null : Utils.DecFromDb((long)row.FbPenalty, prec1);
.Select(m => season?.Modifiers.FirstOrDefault(s => s.ModId == m)) if (data.ConsiderTotalPenalty)
.Where(m => m != null) Penalty = (row.BsPealty == null || row.BsPealty == 0) ? null : Utils.DecFromDb((long)row.BsPealty, prec1);
.OrderBy(m => m.Ordering) if (data.ConsiderAutoBusinessShares)
.ToList(); AutoBs = (row.AutoBs == null || row.AutoBs == 0) ? null : -Utils.DecFromDb((long)row.AutoBs, prec1);
Modifiers = modifiers.Select(m => m.Name).ToArray(); mod -= (Penalties ?? 0) + (Penalty ?? 0) + (AutoBs ?? 0);
QualityLevel = f.QualityLevel; Others = (mod == 0) ? null : mod;
Gradation = (f.Oe, f.Kmw); Gross = Utils.DecFromDb(row.GrossAmount, prec1);
Buckets = rows Considered = (row.PrevModifiers == null || row.PrevModifiers == 0) ? null : -Utils.DecFromDb((long)row.PrevModifiers, prec1);
.Where(b => b.Value > 0) Amount = Utils.DecFromDb(row.Amount, prec1);
.OrderByDescending(b => b.BktNr)
.Select(b => (b.Discr == "_" ? "ungeb." : $"geb. {f.SortId}{b.Discr}", b.Value,
b.Price != null ? season?.DecFromDb((long)b.Price) : null,
b.Amount != null ? season?.DecFromDb((long)b.Amount) : null))
.ToArray();
Amount = f.TotalAmount != null ? season?.DecFromDb((long)f.TotalAmount) : null;
var netAmount = f.NetAmount != null ? season?.DecFromDb((long)f.NetAmount) : null;
TotalModifiers = Amount - netAmount;
} }
} }
[Keyless] [Keyless]
public class CreditNoteRowSingle { public class CreditNoteRowSingle {
[Column("year")]
public int Year { get; set; }
[Column("tgnr")]
public int? TgNr { get; set; }
[Column("avnr")]
public int? AvNr { get; set; }
[Column("mgnr")] [Column("mgnr")]
public int MgNr { get; set; } public int MgNr { get; set; }
[Column("did")] [Column("family_name")]
public int DId { get; set; } public string Name { get; set; }
[Column("lsnr")] [Column("given_name")]
public string LsNr { get; set; } public string GivenName { get; set; }
[Column("dpnr")] [Column("address")]
public int DPNr { get; set; } public string Address { get; set; }
[Column("weight")] [Column("plz")]
public int Weight { get; set; } public int Plz { get; set; }
[Column("modifiers")] [Column("ort")]
public string? Modifiers { get; set; } public string LocalityFull { get; set; }
[Column("bktnr")] [NotMapped]
public int BktNr { get; set; } public string Locality => LocalityFull.Split(",")[0];
[Column("sortid")] [Column("iban")]
public string SortId { get; set; } public string Iban { get; set; }
[Column("discr")] [Column("year")]
public string Discr { get; set; } public int Year { get; set; }
[Column("value")] [Column("precision")]
public int Value { get; set; } public byte Precision { get; set; }
[Column("price")] [Column("tgnr")]
public long? Price { get; set; } public string TgNr { get; set; }
[Column("amount")] [Column("surcharge")]
public long? Amount { get; set; } public long? Surcharge { get; set; }
[Column("net_amount")] [Column("net_amount")]
public long? NetAmount { get; set; } public long NetAmount { get; set; }
[Column("total_amount")] [Column("prev_net_amount")]
public long? TotalAmount { get; set; } public long? PrevNetAmount { get; set; }
[Column("variant")] [Column("vat")]
public string Variant { get; set; } public double Vat { get; set; }
[Column("attribute")] [Column("vat_amount")]
public string? Attribute { get; set; } public long VatAmount { get; set; }
[Column("quality_level")] [Column("gross_amount")]
public string QualityLevel { get; set; } public long GrossAmount { get; set; }
[Column("oe")] [Column("modifiers")]
public double Oe { get; set; } public long? Modifiers { get; set; }
[Column("kmw")] [Column("prev_modifiers")]
public double Kmw { get; set; } public long? PrevModifiers { get; set; }
[Column("amount")]
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; }
} }
} }

View File

@ -0,0 +1,159 @@
using Elwig.Models.Entities;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;
namespace Elwig.Models.Dtos {
public class CreditNoteDeliveryData : DataTable<CreditNoteDeliveryRow> {
private static readonly (string, string, string?)[] FieldNames = [
("", "", null), // TODO
];
private readonly int Year;
private readonly int? TgNr;
private readonly int? AvNr;
private readonly int? MgNr;
private CreditNoteDeliveryData(IEnumerable<CreditNoteDeliveryRow> rows, int year, int? tgnr, int? avnr = null, int? mgnr = null) :
base($"Traubengutschrift {year}/{tgnr}", rows, FieldNames) {
Year = year;
TgNr = tgnr;
AvNr = avnr;
MgNr = mgnr;
}
public static async Task<IDictionary<int, CreditNoteDeliveryData>> ForPaymentVariant(DbSet<CreditNoteDeliveryRowSingle> table, DbSet<Season> seasons, int year, int avnr) {
return (await FromDbSet(table, year, avnr))
.GroupBy(
r => new { r.Year, r.AvNr, r.MgNr, r.TgNr, r.DId, r.DPNr },
(k, g) => new CreditNoteDeliveryRow(g, seasons))
.GroupBy(
r => new { r.Year, r.AvNr, r.MgNr, r.TgNr },
(k, g) => new CreditNoteDeliveryData(g, k.Year, k.TgNr, mgnr: k.MgNr))
.ToDictionary(d => d.MgNr ?? 0);
}
private static async Task<IEnumerable<CreditNoteDeliveryRowSingle>> FromDbSet(DbSet<CreditNoteDeliveryRowSingle> table, int? year = null, int? avnr = null, int? mgnr = null) {
var y = year?.ToString() ?? "NULL";
var v = avnr?.ToString() ?? "NULL";
var m = mgnr?.ToString() ?? "NULL";
return await table.FromSqlRaw($"""
SELECT d.year, c.tgnr, v.avnr, d.mgnr, d.did, d.lsnr, d.dpnr, d.weight, d.modifiers,
b.bktnr, d.sortid, b.discr, b.value, pb.price, pb.amount, p.net_amount, p.amount AS total_amount,
s.name AS variety, a.name AS attribute, q.name AS quality_level, d.oe, d.kmw
FROM v_delivery d
JOIN wine_variety s ON s.sortid = d.sortid
LEFT JOIN wine_attribute a ON a.attrid = d.attrid
JOIN wine_quality_level q ON q.qualid = d.qualid
LEFT JOIN delivery_part_bucket b ON (b.year, b.did, b.dpnr) = (d.year, d.did, d.dpnr)
LEFT JOIN payment_variant v ON v.year = d.year
LEFT JOIN payment_delivery_part p ON (p.year, p.did, p.dpnr, p.avnr) = (d.year, d.did, d.dpnr, v.avnr)
LEFT JOIN payment_delivery_part_bucket pb ON (pb.year, pb.did, pb.dpnr, pb.bktnr, pb.avnr) = (b.year, b.did, b.dpnr, b.bktnr, v.avnr)
LEFT JOIN credit c ON (c.year, c.avnr, c.mgnr) = (d.year, v.avnr, d.mgnr)
WHERE b.value > 0 AND (d.year = {y} OR {y} IS NULL) AND (v.avnr = {v} OR {v} IS NULL) AND (d.mgnr = {m} OR {m} IS NULL)
ORDER BY d.year, v.avnr, d.mgnr, d.lsnr, d.dpnr
""").ToListAsync();
}
}
public class CreditNoteDeliveryRow {
public int Year;
public int? TgNr;
public int AvNr;
public int MgNr;
public string LsNr;
public int DPNr;
public string Variety;
public string? Attribute;
public string[] Modifiers;
public string QualityLevel;
public (double Oe, double Kmw) Gradation;
public (string Name, int Value, decimal? Price, decimal? Amount)[] Buckets;
public decimal? TotalModifiers;
public decimal? Amount;
public CreditNoteDeliveryRow(IEnumerable<CreditNoteDeliveryRowSingle> rows, DbSet<Season> seasons) {
var f = rows.First();
Year = f.Year;
TgNr = f.TgNr;
MgNr = f.MgNr;
var season = seasons.Find(Year);
LsNr = f.LsNr;
DPNr = f.DPNr;
Variety = f.Variety;
Attribute = f.Attribute;
var modifiers = (IEnumerable<Modifier>)(f.Modifiers ?? "").Split(',')
.Select(m => season?.Modifiers.FirstOrDefault(s => s.ModId == m))
.Where(m => m != null)
.OrderBy(m => m.Ordering)
.ToList();
Modifiers = modifiers.Select(m => m.Name).ToArray();
QualityLevel = f.QualityLevel;
Gradation = (f.Oe, f.Kmw);
Buckets = rows
.Where(b => b.Value > 0)
.OrderByDescending(b => b.BktNr)
.Select(b => (b.Discr == "_" ? "ungeb." : $"geb. {f.SortId}{b.Discr}", b.Value,
b.Price != null ? season?.DecFromDb((long)b.Price) : null,
b.Amount != null ? season?.DecFromDb((long)b.Amount) : null))
.ToArray();
Amount = f.TotalAmount != null ? season?.DecFromDb((long)f.TotalAmount) : null;
var netAmount = f.NetAmount != null ? season?.DecFromDb((long)f.NetAmount) : null;
TotalModifiers = Amount - netAmount;
}
}
[Keyless]
public class CreditNoteDeliveryRowSingle {
[Column("year")]
public int Year { get; set; }
[Column("tgnr")]
public int? TgNr { get; set; }
[Column("avnr")]
public int? AvNr { get; set; }
[Column("mgnr")]
public int MgNr { get; set; }
[Column("did")]
public int DId { get; set; }
[Column("lsnr")]
public string LsNr { get; set; }
[Column("dpnr")]
public int DPNr { get; set; }
[Column("weight")]
public int Weight { get; set; }
[Column("modifiers")]
public string? Modifiers { get; set; }
[Column("bktnr")]
public int BktNr { get; set; }
[Column("sortid")]
public string SortId { get; set; }
[Column("discr")]
public string Discr { get; set; }
[Column("value")]
public int Value { get; set; }
[Column("price")]
public long? Price { get; set; }
[Column("amount")]
public long? Amount { get; set; }
[Column("net_amount")]
public long? NetAmount { get; set; }
[Column("total_amount")]
public long? TotalAmount { get; set; }
[Column("variety")]
public string Variety { get; set; }
[Column("attribute")]
public string? Attribute { get; set; }
[Column("quality_level")]
public string QualityLevel { get; set; }
[Column("oe")]
public double Oe { get; set; }
[Column("kmw")]
public double Kmw { get; set; }
}
}

View File

@ -5,42 +5,42 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Elwig.Models.Dtos { namespace Elwig.Models.Dtos {
public class DeliveryConfirmationData : DataTable<DeliveryConfirmationRow> { public class DeliveryConfirmationDeliveryData : DataTable<DeliveryConfirmationDeliveryRow> {
private static readonly (string, string, string?, int)[] FieldNames = new[] { private static readonly (string, string, string?, int)[] FieldNames = [
("LsNr", "LsNr.", null, 26), ("LsNr", "LsNr.", null, 26),
("DPNr", "Pos.", null, 8), ("DPNr", "Pos.", null, 8),
("Variant", "Sorte", null, 40), ("Variety", "Sorte", null, 40),
("Attribute", "Attribut", null, 20), ("Attribute", "Attribut", null, 20),
("Modifiers", "Zu-/Abschläge", null, 30), ("Modifiers", "Zu-/Abschläge", null, 30),
("QualityLevel", "Qualitätsstufe", null, 25), ("QualityLevel", "Qualitätsstufe", null, 25),
("Gradation", "Gradation", "°Oe|°KMW", 32), ("Gradation", "Gradation", "°Oe|°KMW", 32),
("Buckets", "Flächenbindung", "|kg", 36), ("Buckets", "Flächenbindung", "|kg", 36),
("Weight", "Gewicht", "kg", 16), ("Weight", "Gewicht", "kg", 16),
}; ];
private readonly int MgNr; private readonly int MgNr;
private DeliveryConfirmationData(IEnumerable<DeliveryConfirmationRow> rows, int year, Member m) : private DeliveryConfirmationDeliveryData(IEnumerable<DeliveryConfirmationDeliveryRow> rows, int year, Member m) :
base($"Anlieferungsbestätigung", $"Anlieferungsbestätigung {year} {m.AdministrativeName}", rows, FieldNames) { base($"Anlieferungsbestätigung", $"Anlieferungsbestätigung {year} {m.AdministrativeName}", rows, FieldNames) {
MgNr = m.MgNr; MgNr = m.MgNr;
} }
public static DeliveryConfirmationData CreateEmpty(int year, Member m) { public static DeliveryConfirmationDeliveryData CreateEmpty(int year, Member m) {
return new([], year, m); return new([], year, m);
} }
public static async Task<IDictionary<int, DeliveryConfirmationData>> ForSeason(DbSet<DeliveryPart> table, int year) { public static async Task<IDictionary<int, DeliveryConfirmationDeliveryData>> ForSeason(DbSet<DeliveryPart> table, int year) {
return (await FromDbSet(table, year)) return (await FromDbSet(table, year))
.GroupBy( .GroupBy(
p => p.Delivery.Member, p => p.Delivery.Member,
p => new DeliveryConfirmationRow(p), p => new DeliveryConfirmationDeliveryRow(p),
(k, g) => new DeliveryConfirmationData(g, year, k) (k, g) => new DeliveryConfirmationDeliveryData(g, year, k)
).ToDictionary(d => d.MgNr, d => d); ).ToDictionary(d => d.MgNr, d => d);
} }
public static async Task<DeliveryConfirmationData> ForMember(DbSet<DeliveryPart> table, int year, Member m) { public static async Task<DeliveryConfirmationDeliveryData> ForMember(DbSet<DeliveryPart> table, int year, Member m) {
return new DeliveryConfirmationData((await FromDbSet(table, year, m.MgNr)).Select(p => new DeliveryConfirmationRow(p)), year, m); return new DeliveryConfirmationDeliveryData((await FromDbSet(table, year, m.MgNr)).Select(p => new DeliveryConfirmationDeliveryRow(p)), year, m);
} }
private static async Task<IEnumerable<DeliveryPart>> FromDbSet(DbSet<DeliveryPart> table, int? year = null, int? mgnr = null) { private static async Task<IEnumerable<DeliveryPart>> FromDbSet(DbSet<DeliveryPart> table, int? year = null, int? mgnr = null) {
@ -51,7 +51,7 @@ namespace Elwig.Models.Dtos {
if (mgnr != null) q = q.Where(p => p.Delivery.MgNr == mgnr); if (mgnr != null) q = q.Where(p => p.Delivery.MgNr == mgnr);
await q await q
.Include(p => p.Delivery) .Include(p => p.Delivery)
.Include(p => p.Variant) .Include(p => p.Variety)
.Include(p => p.Attribute) .Include(p => p.Attribute)
.Include(p => p.Quality) .Include(p => p.Quality)
.Include(p => p.Buckets) .Include(p => p.Buckets)
@ -68,10 +68,10 @@ namespace Elwig.Models.Dtos {
} }
} }
public class DeliveryConfirmationRow { public class DeliveryConfirmationDeliveryRow {
public string LsNr; public string LsNr;
public int DPNr; public int DPNr;
public string Variant; public string Variety;
public string? Attribute; public string? Attribute;
public string QualityLevel; public string QualityLevel;
public (double Oe, double Kmw) Gradation; public (double Oe, double Kmw) Gradation;
@ -79,11 +79,11 @@ namespace Elwig.Models.Dtos {
public int Weight; public int Weight;
public (string Name, int Value)[] Buckets; public (string Name, int Value)[] Buckets;
public DeliveryConfirmationRow(DeliveryPart p) { public DeliveryConfirmationDeliveryRow(DeliveryPart p) {
var d = p.Delivery; var d = p.Delivery;
LsNr = d.LsNr; LsNr = d.LsNr;
DPNr = p.DPNr; DPNr = p.DPNr;
Variant = p.Variant.Name; Variety = p.Variety.Name;
Attribute = p.Attribute?.Name; Attribute = p.Attribute?.Name;
QualityLevel = p.Quality.Name; QualityLevel = p.Quality.Name;
Gradation = (p.Oe, p.Kmw); Gradation = (p.Oe, p.Kmw);

View File

@ -8,7 +8,7 @@ using System.Threading.Tasks;
namespace Elwig.Models.Dtos { namespace Elwig.Models.Dtos {
public class MemberDeliveryPerVariantData : DataTable<MemberDeliveryPerVariantRow> { public class MemberDeliveryPerVariantData : DataTable<MemberDeliveryPerVariantRow> {
private static readonly (string, string, string?, int)[] FieldNames = new[] { private static readonly (string, string, string?, int)[] FieldNames = [
("MgNr", "MgNr.", null, 12), ("MgNr", "MgNr.", null, 12),
("Name", "Name", null, 40), ("Name", "Name", null, 40),
("GivenName", "Vorname", null, 40), ("GivenName", "Vorname", null, 40),
@ -20,7 +20,7 @@ namespace Elwig.Models.Dtos {
("Weights", "Geliefert", "kg", 22), ("Weights", "Geliefert", "kg", 22),
("Areas", "Fläche", "m²", 22), ("Areas", "Fläche", "m²", 22),
("Yields", "Ertrag", "kg/ha", 22), ("Yields", "Ertrag", "kg/ha", 22),
}; ];
public MemberDeliveryPerVariantData(IEnumerable<MemberDeliveryPerVariantRow> rows, int year) : public MemberDeliveryPerVariantData(IEnumerable<MemberDeliveryPerVariantRow> rows, int year) :

View File

@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace Elwig.Models.Dtos { namespace Elwig.Models.Dtos {
public class OverUnderDeliveryData : DataTable<OverUnderDeliveryRow> { public class OverUnderDeliveryData : DataTable<OverUnderDeliveryRow> {
private static readonly (string, string, string?, int)[] FieldNames = new[] { private static readonly (string, string, string?, int)[] FieldNames = [
("MgNr", "MgNr.", null, 12), ("MgNr", "MgNr.", null, 12),
("Name", "Name", null, 40), ("Name", "Name", null, 40),
("GivenName", "Vorname", null, 40), ("GivenName", "Vorname", null, 40),
@ -19,7 +19,7 @@ namespace Elwig.Models.Dtos {
("DeliveryRight", "Lieferrecht", "kg", 22), ("DeliveryRight", "Lieferrecht", "kg", 22),
("Weight", "Geliefert", "kg", 22), ("Weight", "Geliefert", "kg", 22),
("OverUnderDelivery", "Über-/Unterliefert", "kg|%", 34), ("OverUnderDelivery", "Über-/Unterliefert", "kg|%", 34),
}; ];
public OverUnderDeliveryData(IEnumerable<OverUnderDeliveryRow> rows, int year) : public OverUnderDeliveryData(IEnumerable<OverUnderDeliveryRow> rows, int year) :
base($"Über-Unterlieferungen", $"Über- und Unterlieferungen laut gezeichneten Geschäftsanteilen {year}", rows, FieldNames) { base($"Über-Unterlieferungen", $"Über- und Unterlieferungen laut gezeichneten Geschäftsanteilen {year}", rows, FieldNames) {

View File

@ -23,7 +23,7 @@ namespace Elwig.Models.Entities {
public string SortId { get; set; } public string SortId { get; set; }
[ForeignKey("SortId")] [ForeignKey("SortId")]
public virtual WineVar Variant { get; private set; } public virtual WineVar Variety { get; private set; }
[Column("attrid")] [Column("attrid")]
public string? AttrId { get; set; } public string? AttrId { get; set; }

View File

@ -1,4 +1,4 @@
-- schema version 11 to 12 -- schema version 12 to 13
ALTER TABLE season ADD COLUMN bs_value INTEGER; ALTER TABLE season ADD COLUMN bs_value INTEGER;

View File

@ -0,0 +1,33 @@
-- schema version 13 to 14
CREATE VIEW v_penalty_area_commitments AS
SELECT year, mgnr,
SUM(COALESCE(IIF(u.weight = 0, -t.penalty_none, 0), 0) +
COALESCE(IIF(u.diff < 0, -t.penalty_amount, 0), 0) +
COALESCE(u.diff * t.penalty_per_kg, 0)
) AS total_penalty
FROM v_under_delivery u
JOIN area_commitment_type t ON t.vtrgid = u.bucket
GROUP BY year, mgnr
HAVING total_penalty < 0
ORDER BY year, mgnr;
CREATE VIEW v_penalty_business_shares AS
SELECT s.year, u.mgnr,
(COALESCE(IIF(u.weight = 0, -s.penalty_none, 0), 0) +
COALESCE(IIF(u.diff < 0, -s.penalty_amount, 0), 0) +
COALESCE(u.diff * s.penalty_per_kg, 0)
) AS total_penalty
FROM v_total_under_delivery u
JOIN season s ON s.year = u.year
WHERE u.diff < 0 AND total_penalty < 0
ORDER BY s.year, u.mgnr;
CREATE VIEW v_auto_business_shares AS
SELECT s.year, h.mgnr,
SUM(h.business_shares) AS business_shares,
SUM(h.business_shares) * s.bs_value AS total_amount
FROM member_history h, season s
WHERE h.type = 'auto' AND h.date >= s.year || '-01-01' AND h.date <= s.year || '-12-31'
GROUP BY s.year, h.mgnr
ORDER BY s.year, h.mgnr;

View File

@ -535,15 +535,18 @@
</GroupBox> </GroupBox>
<GroupBox Header="Anlieferungsbestätigung" Margin="10,10,10,10" Height="250"> <GroupBox Header="Anlieferungsbestätigung" Margin="10,10,10,10" Height="250">
<Grid> <Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="TextElementDeliveryConfirmation" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible" AcceptsReturn="True" <TextBox x:Name="TextElementDeliveryConfirmation" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible" AcceptsReturn="True"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10,10,10,10" Height="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10,10,10,10" Height="Auto"
TextChanged="TextBox_TextChanged"/> TextChanged="TextBox_TextChanged"/>
</Grid> </Grid>
</GroupBox> </GroupBox>
<GroupBox Header="Traubengutschrift" Margin="10,10,10,10" Height="250">
<Grid>
<TextBox x:Name="TextElementCreditNote" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible" AcceptsReturn="True"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10,10,10,10" Height="Auto"
TextChanged="TextBox_TextChanged"/>
</Grid>
</GroupBox>
</StackPanel> </StackPanel>
</ScrollViewer> </ScrollViewer>
</TabItem> </TabItem>

View File

@ -12,11 +12,11 @@ namespace Elwig.Windows {
public BaseDataWindow() { public BaseDataWindow() {
InitializeComponent(); InitializeComponent();
RequiredInputs = new Control[] { RequiredInputs = [
ClientNameInput, ClientNameTypeInput, ClientNameTokenInput, ClientNameShortInput, ClientNameInput, ClientNameTypeInput, ClientNameTokenInput, ClientNameShortInput,
ClientAddressInput, ClientPlzInput, ClientOrtInput, ClientAddressInput, ClientPlzInput, ClientOrtInput,
}; ];
ExemptInputs = new Control[] { ExemptInputs = [
ClientNameFull, ClientNameFull,
BranchIdInput, BranchNameInput, BranchPlzInput, BranchOrtInput, BranchIdInput, BranchNameInput, BranchPlzInput, BranchOrtInput,
BranchAddressInput, BranchPhoneNrInput, BranchFaxNrInput, BranchMobileNrInput, BranchAddressInput, BranchPhoneNrInput, BranchFaxNrInput, BranchMobileNrInput,
@ -30,7 +30,7 @@ namespace Elwig.Windows {
SeasonMinKgPerBsInput.TextBox, SeasonMaxKgPerBsInput.TextBox, SeasonBsValueInput.TextBox, SeasonMinKgPerBsInput.TextBox, SeasonMaxKgPerBsInput.TextBox, SeasonBsValueInput.TextBox,
SeasonPenaltyPerKgInput.TextBox, SeasonPenaltyInput.TextBox, SeasonPenaltyNoneInput.TextBox, SeasonPenaltyPerKgInput.TextBox, SeasonPenaltyInput.TextBox, SeasonPenaltyNoneInput.TextBox,
SeasonModifierIdInput, SeasonModifierNameInput, SeasonModifierRelInput.TextBox, SeasonModifierAbsInput.TextBox, SeasonModifierIdInput, SeasonModifierNameInput, SeasonModifierRelInput.TextBox, SeasonModifierAbsInput.TextBox,
}; ];
WineAttributeFillLowerInput.Visibility = Visibility.Hidden; WineAttributeFillLowerInput.Visibility = Visibility.Hidden;
WineAttributeFillLowerLabel.Visibility = Visibility.Hidden; WineAttributeFillLowerLabel.Visibility = Visibility.Hidden;
} }
@ -297,6 +297,7 @@ namespace Elwig.Windows {
case 3: ModeDeliveryNoteFull.IsChecked = true; break; case 3: ModeDeliveryNoteFull.IsChecked = true; break;
} }
TextElementDeliveryConfirmation.Text = p.TextDeliveryConfirmation; TextElementDeliveryConfirmation.Text = p.TextDeliveryConfirmation;
TextElementCreditNote.Text = p.TextCreditNote;
FinishInputFilling(); FinishInputFilling();
} }
@ -322,6 +323,7 @@ namespace Elwig.Windows {
p.TextDeliveryNote = TextElementDeliveryNote.Text.Length > 0 ? TextElementDeliveryNote.Text : null; p.TextDeliveryNote = TextElementDeliveryNote.Text.Length > 0 ? TextElementDeliveryNote.Text : null;
p.ModeDeliveryNoteStats = (ModeDeliveryNoteNone.IsChecked == true) ? 0 : (ModeDeliveryNoteGaOnly.IsChecked == true) ? 1 : (ModeDeliveryNoteShort.IsChecked == true) ? 2 : (ModeDeliveryNoteFull.IsChecked == true) ? 3 : 2; p.ModeDeliveryNoteStats = (ModeDeliveryNoteNone.IsChecked == true) ? 0 : (ModeDeliveryNoteGaOnly.IsChecked == true) ? 1 : (ModeDeliveryNoteShort.IsChecked == true) ? 2 : (ModeDeliveryNoteFull.IsChecked == true) ? 3 : 2;
p.TextDeliveryConfirmation = TextElementDeliveryConfirmation.Text.Length > 0 ? TextElementDeliveryConfirmation.Text : null; p.TextDeliveryConfirmation = TextElementDeliveryConfirmation.Text.Length > 0 ? TextElementDeliveryConfirmation.Text : null;
p.TextCreditNote = TextElementCreditNote.Text.Length > 0 ? TextElementCreditNote.Text : null;
await p.UpdateValues(); await p.UpdateValues();
} }

View File

@ -61,14 +61,17 @@
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Label Content="Für:" Margin="10,-2,0,0" FontSize="14" Grid.Column="0" VerticalAlignment="Center"/> <Label Content="Für:" Margin="10,-2,0,0" FontSize="14" Grid.Column="0" VerticalAlignment="Center"/>
<xctk:CheckComboBox x:Name="ContractInput" Margin="50,0,0,0" Grid.Column="0" <xctk:CheckComboBox x:Name="VaributeInput" Margin="50,0,0,0" Grid.Column="0" Width="500" Height="25" HorizontalAlignment="Left"
Delimiter=", " AllItemsSelectedContent="Alle" IsEnabled="False" ItemSelectionChanged="ContractInput_Changed" IsSelectAllActive="True" SelectAllContent="Alle Sorten" Delimiter=", " AllItemsSelectedContent="Alle Sorten"
Width="500" Height="25" HorizontalAlignment="Left"> IsEnabled="False" ItemSelectionChanged="VaributeInput_Changed">
<xctk:CheckComboBox.ItemTemplate> <xctk:CheckComboBox.ItemTemplate>
<DataTemplate> <DataTemplate>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Variety.Name}" Width="150"/> <TextBlock Text="{Binding Variety.Name}" Width="150"/>
<TextBlock Text="{Binding Attribute.Name}"/> <TextBlock Text="{Binding Variety.Type}" Width="30"/>
<TextBlock Text="{Binding Attribute.Name}" Width="120"/>
<TextBlock Text="{Binding AssignedGraphId}" Width="30"/>
<TextBlock Text="{Binding AssignedAbgewGraphId}" Width="30"/>
</StackPanel> </StackPanel>
</DataTemplate> </DataTemplate>
</xctk:CheckComboBox.ItemTemplate> </xctk:CheckComboBox.ItemTemplate>
@ -83,7 +86,7 @@
<DataTemplate> <DataTemplate>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Id}" Width="30"/> <TextBlock Text="{Binding Id}" Width="30"/>
<TextBlock Text="{Binding ContractsStringSimple}" ToolTip="{Binding ContractsString}"/> <TextBlock Text="{Binding VaributeStringSimple}" ToolTip="{Binding VaributeString}"/>
</StackPanel> </StackPanel>
</DataTemplate> </DataTemplate>
</ListBox.ItemTemplate> </ListBox.ItemTemplate>

View File

@ -33,14 +33,28 @@ namespace Elwig.Windows {
private Marker PrimaryMarkedPointPlot; private Marker PrimaryMarkedPointPlot;
private Marker SecondaryMarkedPointPlot; private Marker SecondaryMarkedPointPlot;
private Text TooltipPlot; private Text TooltipPlot;
private LegendItem UngebundenLegend;
private LegendItem GebundenLegend;
private LegendItem LDWLegend;
private LegendItem QUWLegend;
private LegendItem KABLegend;
private (Graph? graph, int index) LastHighlighted = (null, -1); private static readonly LegendItem
private (Graph? graph, int index) Highlighted = (null, -1); UngebundenLegend = new() {
Label = "Ungebunden", LineWidth = 1, LineColor = ColorUngebunden,
Marker = new(MarkerShape.FilledCircle, 5, ColorUngebunden)
},
GebundenLegend = new() {
Label = "Gebunden", LineWidth = 1, LineColor = ColorGebunden,
Marker = new(MarkerShape.FilledCircle, 5, ColorGebunden)
},
LdwLegend = new() {
Label = "68 °Oe (LDW)", LineWidth = 2, LineColor = Colors.Red, Marker = MarkerStyle.None
},
QuwLegend = new() {
Label = "73 °Oe (QUW)", LineWidth = 2, LineColor = Colors.Orange, Marker = MarkerStyle.None
},
KabLegend = new() {
Label = "84 °Oe (KAB)", LineWidth = 2, LineColor = Colors.Green, Marker = MarkerStyle.None
};
private (Graph? Graph, int Index) LastHighlighted = (null, -1);
private (Graph? Graph, int Index) Highlighted = (null, -1);
private Graph? ActiveGraph = null; private Graph? ActiveGraph = null;
private int PrimaryMarkedPoint = -1; private int PrimaryMarkedPoint = -1;
private int SecondaryMarkedPoint = -1; private int SecondaryMarkedPoint = -1;
@ -50,6 +64,9 @@ namespace Elwig.Windows {
private List<GraphEntry> GraphEntries = []; private List<GraphEntry> GraphEntries = [];
private GraphEntry? SelectedGraphEntry => (GraphEntry)GraphList.SelectedItem; private GraphEntry? SelectedGraphEntry => (GraphEntry)GraphList.SelectedItem;
private List<Varibute> Vaributes = [];
private bool AllVaributesAssigned => Vaributes.All(v => v.AssignedGraphId != null);
private bool AllVaributesAssignedAbgew => Vaributes.All(v => v.AssignedAbgewGraphId != null);
public ChartWindow(int year, int avnr) { public ChartWindow(int year, int avnr) {
InitializeComponent(); InitializeComponent();
@ -84,18 +101,29 @@ namespace Elwig.Windows {
PaymentVar = await Context.PaymentVariants.FindAsync(Year, AvNr) ?? throw new ArgumentException("PaymentVar not found"); PaymentVar = await Context.PaymentVariants.FindAsync(Year, AvNr) ?? throw new ArgumentException("PaymentVar not found");
Season = await Context.Seasons.FindAsync(Year) ?? throw new ArgumentException("Season not found"); Season = await Context.Seasons.FindAsync(Year) ?? throw new ArgumentException("Season not found");
var data = EditBillingData.FromJson(PaymentVar.Data, Utils.GetAttributeVarieties(Context, Year)); var data = EditBillingData.FromJson(PaymentVar.Data, Utils.GetVaributes(Context, Year));
var paymentEntries = data.GetPaymentGraphEntries(Context, Season); var paymentEntries = data.GetPaymentGraphEntries(Context, Season);
GraphEntries = [ GraphEntries = [
..paymentEntries, ..paymentEntries,
..data.GetQualityGraphEntries(Context, Season, paymentEntries.Max(e => e.Id)) ..data.GetQualityGraphEntries(Context, Season, paymentEntries.Any() ? paymentEntries.Max(e => e.Id) : 0)
]; ];
Vaributes = Utils.GetVaributeList(Context, Year);
GraphEntries.ForEach(e => {
e.Vaributes.ForEach(v => {
var found = Vaributes.Find(a => a.Attribute?.AttrId == v.Attribute?.AttrId && a.Variety?.SortId == v.Variety?.SortId);
if (found == null) return;
if (e.Abgewertet) {
found.AssignedAbgewGraphId = e.Id;
} else {
found.AssignedGraphId = e.Id;
}
});
});
var contracts = Utils.GetContractsForYear(Context, Year);
FillingInputs = true; FillingInputs = true;
ControlUtils.RenewItemsSource(ContractInput, contracts, g => (g as ContractSelection)?.Listing); ControlUtils.RenewItemsSource(VaributeInput, Vaributes, v => (v as Varibute)?.Listing);
FillingInputs = false; FillingInputs = false;
ControlUtils.RenewItemsSource(GraphList, GraphEntries, g => (g as GraphEntry)?.ContractsStringChange, GraphList_SelectionChanged, ControlUtils.RenewSourceDefault.First); ControlUtils.RenewItemsSource(GraphList, GraphEntries, g => (g as GraphEntry)?.VaributeStringChange, GraphList_SelectionChanged, ControlUtils.RenewSourceDefault.First);
RefreshInputs(); RefreshInputs();
} }
@ -108,7 +136,7 @@ namespace Elwig.Windows {
GebundenTypeFixed.IsEnabled = true; GebundenTypeFixed.IsEnabled = true;
GebundenTypeGraph.IsEnabled = true; GebundenTypeGraph.IsEnabled = true;
GebundenTypeNone.IsEnabled = true; GebundenTypeNone.IsEnabled = true;
ContractInput.IsEnabled = true; VaributeInput.IsEnabled = true;
AbgewertetInput.IsEnabled = true; AbgewertetInput.IsEnabled = true;
EnableOptionButtons(); EnableOptionButtons();
FillInputs(); FillInputs();
@ -127,7 +155,7 @@ namespace Elwig.Windows {
GebundenTypeFixed.IsEnabled = false; GebundenTypeFixed.IsEnabled = false;
GebundenTypeGraph.IsEnabled = false; GebundenTypeGraph.IsEnabled = false;
GebundenTypeNone.IsEnabled = false; GebundenTypeNone.IsEnabled = false;
ContractInput.IsEnabled = false; VaributeInput.IsEnabled = false;
AbgewertetInput.IsEnabled = false; AbgewertetInput.IsEnabled = false;
} }
GC.Collect(); GC.Collect();
@ -148,7 +176,7 @@ namespace Elwig.Windows {
GebundenFlatBonus.Text = ""; GebundenFlatBonus.Text = "";
} }
ControlUtils.SelectCheckComboBoxItems(ContractInput, SelectedGraphEntry?.Contracts ?? [], i => (i as ContractSelection)?.Listing); ControlUtils.SelectCheckComboBoxItems(VaributeInput, SelectedGraphEntry?.Vaributes ?? [], i => (i as Varibute)?.Listing);
InitPlot(); InitPlot();
OechslePricePlot.IsEnabled = true; OechslePricePlot.IsEnabled = true;
@ -160,41 +188,6 @@ namespace Elwig.Windows {
} }
private void InitPlot() { private void InitPlot() {
UngebundenLegend = new LegendItem() {
Label = "Ungebunden",
LineWidth = 1,
LineColor = ColorUngebunden,
Marker = new MarkerStyle(MarkerShape.FilledCircle, 5, ColorUngebunden)
};
GebundenLegend = new LegendItem() {
Label = "Gebunden",
LineWidth = 1,
LineColor = ColorGebunden,
Marker = new MarkerStyle(MarkerShape.FilledCircle, 5, ColorGebunden)
};
LDWLegend = new LegendItem() {
Label = "68 °Oe (LDW)",
LineWidth = 2,
LineColor = Colors.Red,
Marker = MarkerStyle.None
};
QUWLegend = new LegendItem() {
Label = "73 °Oe (QUW)",
LineWidth = 2,
LineColor = Colors.Orange,
Marker = MarkerStyle.None
};
KABLegend = new LegendItem() {
Label = "84 °Oe (KAB)",
LineWidth = 2,
LineColor = Colors.Green,
Marker = MarkerStyle.None
};
RefreshGradationLines(); RefreshGradationLines();
if (SelectedGraphEntry?.GebundenGraph != null) { if (SelectedGraphEntry?.GebundenGraph != null) {
@ -230,7 +223,7 @@ namespace Elwig.Windows {
//OechslePricePlot.Plot.XAxis.ManualTickSpacing(1); //OechslePricePlot.Plot.XAxis.ManualTickSpacing(1);
//OechslePricePlot.Plot.YAxis.ManualTickSpacing(0.1); //OechslePricePlot.Plot.YAxis.ManualTickSpacing(0.1);
OechslePricePlot.Plot.Axes.SetLimits(Math.Min(GraphEntry.MinX, GraphEntry.MinXGeb) - 1, GraphEntry.MaxX + 1, -0.1, 2); OechslePricePlot.Plot.Axes.SetLimits(Math.Min(GraphEntry.MinX, GraphEntry.MinXGeb) - 1, GraphEntry.MaxX + 1, -0.1, 1.5);
//OechslePricePlot.Plot.Layout(padding: 0); //OechslePricePlot.Plot.Layout(padding: 0);
//OechslePricePlot.Plot.XAxis2.Layout(padding: 0); //OechslePricePlot.Plot.XAxis2.Layout(padding: 0);
@ -317,7 +310,7 @@ namespace Elwig.Windows {
OechslePricePlot.Plot.Axes.Rules.Clear(); OechslePricePlot.Plot.Axes.Rules.Clear();
OechslePricePlot.Plot.Axes.Rules.Add(BoundaryRule); OechslePricePlot.Plot.Axes.Rules.Add(BoundaryRule);
OechslePricePlot.Plot.Axes.Rules.Add(SpanRule); OechslePricePlot.Plot.Axes.Rules.Add(SpanRule);
OechslePricePlot.Plot.Axes.SetLimits(GraphEntry.MinX - 1, GraphEntry.MaxX + 1, -0.1, 2); OechslePricePlot.Plot.Axes.SetLimits(GraphEntry.MinX - 1, GraphEntry.MaxX + 1, -0.1, 1.5);
} }
private void UnlockZoom() { private void UnlockZoom() {
@ -372,9 +365,9 @@ namespace Elwig.Windows {
OechslePricePlot.Plot.Legend.Location = Alignment.UpperLeft; OechslePricePlot.Plot.Legend.Location = Alignment.UpperLeft;
OechslePricePlot.Plot.Legend.IsVisible = true; OechslePricePlot.Plot.Legend.IsVisible = true;
OechslePricePlot.Plot.Legend.ManualItems.Add(LDWLegend); OechslePricePlot.Plot.Legend.ManualItems.Add(LdwLegend);
OechslePricePlot.Plot.Legend.ManualItems.Add(QUWLegend); OechslePricePlot.Plot.Legend.ManualItems.Add(QuwLegend);
OechslePricePlot.Plot.Legend.ManualItems.Add(KABLegend); OechslePricePlot.Plot.Legend.ManualItems.Add(KabLegend);
OechslePricePlot.Plot.Legend.ManualItems.Add(UngebundenLegend); OechslePricePlot.Plot.Legend.ManualItems.Add(UngebundenLegend);
if (SelectedGraphEntry?.GebundenGraph != null) OechslePricePlot.Plot.Legend.ManualItems.Add(GebundenLegend); if (SelectedGraphEntry?.GebundenGraph != null) OechslePricePlot.Plot.Legend.ManualItems.Add(GebundenLegend);
} }
@ -481,10 +474,10 @@ namespace Elwig.Windows {
if (HoverActive) { if (HoverActive) {
if (PaymentVar.TestVariant && Keyboard.IsKeyDown(System.Windows.Input.Key.LeftCtrl)) { if (PaymentVar.TestVariant && Keyboard.IsKeyDown(System.Windows.Input.Key.LeftCtrl)) {
if (PrimaryMarkedPoint == -1 || ActiveGraph == null || ActiveGraph != Highlighted.graph) { if (PrimaryMarkedPoint == -1 || ActiveGraph == null || ActiveGraph != Highlighted.Graph) {
return; return;
} }
SecondaryMarkedPoint = Highlighted.index; SecondaryMarkedPoint = Highlighted.Index;
ChangeMarker(SecondaryMarkedPointPlot, true, ActiveGraph.GetOechsleAt(SecondaryMarkedPoint), ActiveGraph.GetPriceAt(SecondaryMarkedPoint)); ChangeMarker(SecondaryMarkedPointPlot, true, ActiveGraph.GetOechsleAt(SecondaryMarkedPoint), ActiveGraph.GetPriceAt(SecondaryMarkedPoint));
@ -493,13 +486,13 @@ namespace Elwig.Windows {
return; return;
} }
PrimaryMarkedPoint = Highlighted.index; PrimaryMarkedPoint = Highlighted.Index;
if (ActiveGraph != Highlighted.graph) ChangeActiveGraph(Highlighted.graph); if (ActiveGraph != Highlighted.Graph) ChangeActiveGraph(Highlighted.Graph);
ChangeMarker(PrimaryMarkedPointPlot, true, ActiveGraph.GetOechsleAt(PrimaryMarkedPoint), ActiveGraph.GetPriceAt(PrimaryMarkedPoint)); ChangeMarker(PrimaryMarkedPointPlot, true, ActiveGraph.GetOechsleAt(PrimaryMarkedPoint), ActiveGraph.GetPriceAt(PrimaryMarkedPoint));
OechsleInput.Text = Highlighted.graph.GetOechsleAt(Highlighted.index).ToString(); OechsleInput.Text = Highlighted.Graph.GetOechsleAt(Highlighted.Index).ToString();
PriceInput.Text = Highlighted.graph.GetPriceAt(Highlighted.index).ToString(); PriceInput.Text = Highlighted.Graph.GetPriceAt(Highlighted.Index).ToString();
EnableActionButtons(); EnableActionButtons();
} else { } else {
@ -622,8 +615,8 @@ namespace Elwig.Windows {
if (SelectedGraphEntry == null) return; if (SelectedGraphEntry == null) return;
var r = MessageBox.Show( var r = MessageBox.Show(
$"Soll der Graph {SelectedGraphEntry.Id} (verwendet in folgenden Verträgen: {SelectedGraphEntry.ContractsStringSimple}) wirklich gelöscht werden?", $"Soll die Kurve {SelectedGraphEntry.Id} (verwendet in folgenden Verträgen: {SelectedGraphEntry.VaributeStringSimple}) wirklich gelöscht werden?",
"Graph löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No); "Kurve löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
if (r == MessageBoxResult.Yes) { if (r == MessageBoxResult.Yes) {
GraphEntries.Remove(SelectedGraphEntry); GraphEntries.Remove(SelectedGraphEntry);
@ -635,7 +628,8 @@ namespace Elwig.Windows {
private async void SaveButton_Click(object sender, RoutedEventArgs e) { private async void SaveButton_Click(object sender, RoutedEventArgs e) {
var origData = BillingData.FromJson(PaymentVar.Data); var origData = BillingData.FromJson(PaymentVar.Data);
var data = BillingData.FromGraphEntries(GraphEntries, origData, Utils.GetAttributeVarieties(Context, Year, true)); var data = BillingData.FromGraphEntries(GraphEntries, origData, Utils.GetVaributes(Context, Year, withSlash: true),
AllVaributesAssigned, AllVaributesAssignedAbgew);
EntityEntry<PaymentVar>? tr = null; EntityEntry<PaymentVar>? tr = null;
try { try {
@ -715,32 +709,45 @@ namespace Elwig.Windows {
} }
} }
private void ContractInput_Changed(object sender, ItemSelectionChangedEventArgs e) { private void VaributeInput_Changed(object sender, ItemSelectionChangedEventArgs e) {
if (FillingInputs) return; if (FillingInputs || e.Item is not Varibute v) return;
if (e.IsSelected == true) { var isOpen = VaributeInput.IsDropDownOpen;
bool success = RemoveContractFromOtherGraphEntries(e.Item.ToString()); if (e.IsSelected) {
if (!success) { if (RemoveVaributeFromOthers(e.Item.ToString())) {
ContractInput.SelectedItems.Remove(e.Item); if (AbgewertetInput.IsChecked == true) {
v.AssignedAbgewGraphId = SelectedGraphEntry?.Id;
} else {
v.AssignedGraphId = SelectedGraphEntry?.Id;
}
} else {
VaributeInput.SelectedItems.Remove(e.Item);
}
} else {
if (AbgewertetInput.IsChecked == true) {
v.AssignedAbgewGraphId = null;
} else {
v.AssignedGraphId = null;
} }
} }
var r = ContractInput.SelectedItems.Cast<ContractSelection>(); SelectedGraphEntry!.Vaributes = VaributeInput.SelectedItems.Cast<Varibute>().ToList();
SelectedGraphEntry!.Contracts = r.ToList();
SetHasChanged(); SetHasChanged();
GraphList.Items.Refresh(); GraphList.Items.Refresh();
VaributeInput.Items.Refresh();
VaributeInput.IsDropDownOpen = isOpen;
} }
private bool RemoveContractFromOtherGraphEntries(string? contract) { private bool RemoveVaributeFromOthers(string? varibute) {
if (contract == null) return true; if (varibute == null) return true;
foreach (var ge in GraphEntries) { foreach (var ge in GraphEntries) {
if (ge != SelectedGraphEntry && ge.Abgewertet == SelectedGraphEntry?.Abgewertet) { if (ge != SelectedGraphEntry && ge.Abgewertet == SelectedGraphEntry?.Abgewertet) {
var toRemove = ge.Contracts.Where(c => c.Listing.Equals(contract)).ToList(); var toRemove = ge.Vaributes.Where(c => c.Listing.Equals(varibute)).ToList();
if (toRemove.Count == 0) continue; if (toRemove.Count == 0) continue;
var r = MessageBox.Show($"Achtung: {string.Join(", ", toRemove)} ist bereits in Graph {ge.Id} in Verwendung!\nSoll die Zuweisung dort entfernt werden?", "Entfernen bestätigen", var r = MessageBox.Show($"Achtung: {string.Join(", ", toRemove)} ist bereits in Kurve {ge.Id} in Verwendung!\nSoll die Zuweisung dort entfernt werden?", "Entfernen bestätigen",
MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No); MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
if (r != MessageBoxResult.Yes) { if (r != MessageBoxResult.Yes) {
return false; return false;
} }
ge.Contracts.RemoveAll(c => c.Listing.Equals(contract)); ge.Vaributes.RemoveAll(c => c.Listing.Equals(varibute));
} }
} }
return true; return true;

View File

@ -101,12 +101,12 @@
Filtern nach:<LineBreak/> Filtern nach:<LineBreak/>
<Bold>Sorte</Bold>: z.B. GV, ZW, rr, sa, !gv (ausgenommen GV), ...<LineBreak/> <Bold>Sorte</Bold>: z.B. GV, ZW, rr, sa, !gv (ausgenommen GV), ...<LineBreak/>
<Bold>Rot/Weiß</Bold>: z.B. r, Rot, w, weiß, ...<LineBreak/> <Bold>Rot/Weiß</Bold>: z.B. r, Rot, w, weiß, ...<LineBreak/>
<Bold>Qualitätsstufe</Bold>: z.B. QUW, kab, !ldw (ausgenommen LDW), ...<LineBreak/> <Bold>Qualitätsstufe</Bold>: z.B. QUW, kab, !ldw (ausgenommen LDW), abgew[ertet], ...<LineBreak/>
<Bold>Gradation</Bold>: z.B. &gt;73, &lt;15, 17-18, 15-, &gt;17,5, 62-75, ...<LineBreak/> <Bold>Gradation</Bold>: z.B. &gt;73, &lt;15, 17-18, 15-, &gt;17,5, 62-75, ...<LineBreak/>
<Bold>Mitglied</Bold>: z.B. 1234, 987, ...<LineBreak/> <Bold>Mitglied</Bold>: z.B. 1234, 987, ...<LineBreak/>
<Bold>Saison</Bold>: z.B. 2020, &gt;2015, 2017-2019, &lt;2005, 2019-, ...<LineBreak/> <Bold>Saison</Bold>: z.B. 2020, &gt;2015, 2017-2019, &lt;2005, 2019-, ...<LineBreak/>
<Bold>Zweigstelle</Bold>: z.B. musterort, ...<LineBreak/> <Bold>Zweigstelle</Bold>: z.B. musterort, ...<LineBreak/>
<Bold>Attribute</Bold>: z.B. kabinett, !kabinett (alle außer kabinett), ...<LineBreak/> <Bold>Attribut</Bold>: z.B. kabinett, !kabinett (alle außer kabinett), ...<LineBreak/>
<Bold>Datum</Bold>: z.B. 1.9., 15.9.-10.10., -15.10.2020, ...<LineBreak/> <Bold>Datum</Bold>: z.B. 1.9., 15.9.-10.10., -15.10.2020, ...<LineBreak/>
<Bold>Uhrzeit</Bold>: z.B. 06:00-08:00, 18:00-, ...<LineBreak/> <Bold>Uhrzeit</Bold>: z.B. 06:00-08:00, 18:00-, ...<LineBreak/>
<Bold>Freitext</Bold>: z.B. Lieferscheinnummern, Anmerkung, "quw" (sucht nach dem Text "quw") <Bold>Freitext</Bold>: z.B. Lieferscheinnummern, Anmerkung, "quw" (sucht nach dem Text "quw")

View File

@ -365,13 +365,23 @@ namespace Elwig.Windows {
filter.RemoveAt(i--); filter.RemoveAt(i--);
filterNames.Add("außer " + var[e[1..].ToUpper()].Name); filterNames.Add("außer " + var[e[1..].ToUpper()].Name);
} else if (e.Length == 3 && qual.ContainsKey(e.ToUpper())) { } else if (e.Length == 3 && qual.ContainsKey(e.ToUpper())) {
filterQual.Add(e.ToUpper()); var qualId = e.ToUpper();
filterQual.Add(qualId);
filter.RemoveAt(i--); filter.RemoveAt(i--);
filterNames.Add(qual[e.ToUpper()].Name); filterNames.Add(qualId == "WEI" ? "abgewertet" : qual[e.ToUpper()].Name);
} else if (e[0] == '!' && qual.ContainsKey(e[1..].ToUpper())) { } else if (e[0] == '!' && qual.ContainsKey(e[1..].ToUpper())) {
filterNotQual.Add(e[1..].ToUpper()); var qualId = e[1..].ToUpper();
filterNotQual.Add(qualId);
filter.RemoveAt(i--); filter.RemoveAt(i--);
filterNames.Add("außer " + qual[e[1..].ToUpper()].Name); filterNames.Add(qualId == "WEI" ? "nicht abgewertet" : "außer " + qual[e[1..].ToUpper()].Name);
} else if (e.Length >= 5 && e.Length <= 10 && "abgewertet".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
filterQual.Add("WEI");
filter.RemoveAt(i--);
filterNames.Add("abgewertet");
} else if (e.Length >= 6 && e.Length <= 11 && "!abgewertet".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
filterNotQual.Add("WEI");
filter.RemoveAt(i--);
filterNames.Add("nicht abgewertet");
} else if (e.All(char.IsAsciiDigit) && mgnr.TryGetValue(e, out var member)) { } else if (e.All(char.IsAsciiDigit) && mgnr.TryGetValue(e, out var member)) {
filterMgNr.Add(int.Parse(e)); filterMgNr.Add(int.Parse(e));
filter.RemoveAt(i--); filter.RemoveAt(i--);

View File

@ -74,9 +74,9 @@ namespace Elwig.Dialogs {
} }
IEnumerable<Member> list = await members.ToListAsync(); IEnumerable<Member> list = await members.ToListAsync();
var data = await DeliveryConfirmationData.ForSeason(Context.DeliveryParts, Year); var data = await DeliveryConfirmationDeliveryData.ForSeason(Context.DeliveryParts, Year);
using var doc = Document.Merge(list.Select(m => using var doc = Document.Merge(list.Select(m =>
new DeliveryConfirmation(Context, Year, m, data.TryGetValue(m.MgNr, out var d) ? d : DeliveryConfirmationData.CreateEmpty(Year, m)) { new DeliveryConfirmation(Context, Year, m, data.TryGetValue(m.MgNr, out var d) ? d : DeliveryConfirmationDeliveryData.CreateEmpty(Year, m)) {
//DoubleSided = true //DoubleSided = true
} }
)); ));

View File

@ -27,11 +27,6 @@ namespace Elwig.Windows {
w.Show(); w.Show();
} }
private void MemberListButton_Click(object sender, RoutedEventArgs evt) {
var w = new MemberListWindow();
w.Show();
}
private void ReceiptButton_Click(object sender, RoutedEventArgs evt) { private void ReceiptButton_Click(object sender, RoutedEventArgs evt) {
App.FocusReceipt(); App.FocusReceipt();
} }

View File

@ -2,8 +2,6 @@
x:Class="Elwig.Windows.MemberAdminWindow" x:Class="Elwig.Windows.MemberAdminWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Elwig.Windows" xmlns:local="clr-namespace:Elwig.Windows"
Title="Mitglieder - Elwig" Height="700" Width="1250" MinHeight="650" MinWidth="1150" Title="Mitglieder - Elwig" Height="700" Width="1250" MinHeight="650" MinWidth="1150"
Loaded="Window_Loaded"> Loaded="Window_Loaded">
@ -239,14 +237,42 @@
<ColumnDefinition Width="2*"/> <ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Label Content="E-Mail-Adresse (1):" Margin="10,10,0,0" Grid.Column="0"/> <Label x:Name="EmailAddress1Label" Content="E-Mail-Adresse:" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="EmailAddress1Input" Margin="0,10,10,0" Grid.Column="1" Grid.ColumnSpan="2" <TextBox x:Name="EmailAddress1Input" Margin="0,10,10,0" Grid.Column="1" Grid.ColumnSpan="2"
TextChanged="EmailAddressInput_TextChanged" LostFocus="EmailAddressInput_LostFocus"/> TextChanged="EmailAddressInput_TextChanged" LostFocus="EmailAddressInput_LostFocus"/>
<Label Content="E-Mail-Adresse (2):" Margin="10,40,0,0" Grid.Column="0"/> <Label x:Name="EmailAddress2Label" Content="E-Mail-Adresse:" Margin="10,40,0,0" Grid.Column="0"/>
<TextBox x:Name="EmailAddress2Input" Margin="0,40,10,0" Grid.Column="1" Grid.ColumnSpan="2" <TextBox x:Name="EmailAddress2Input" Margin="0,40,10,0" Grid.Column="1" Grid.ColumnSpan="2"
TextChanged="EmailAddressInput_TextChanged" LostFocus="EmailAddressInput_LostFocus"/> TextChanged="EmailAddressInput_TextChanged" LostFocus="EmailAddressInput_LostFocus"/>
<Label x:Name="EmailAddress3Label" Content="E-Mail-Adresse:" Margin="10,70,0,0" Grid.Column="0"/>
<TextBox x:Name="EmailAddress3Input" Margin="0,70,10,0" Grid.Column="1" Grid.ColumnSpan="2"
TextChanged="EmailAddressInput_TextChanged" LostFocus="EmailAddressInput_LostFocus"/>
<Label x:Name="EmailAddress4Label" Content="E-Mail-Adresse:" Margin="10,100,0,0" Grid.Column="0"/>
<TextBox x:Name="EmailAddress4Input" Margin="0,100,10,0" Grid.Column="1" Grid.ColumnSpan="2"
TextChanged="EmailAddressInput_TextChanged" LostFocus="EmailAddressInput_LostFocus"/>
<Label x:Name="EmailAddress5Label" Content="E-Mail-Adresse:" Margin="10,130,0,0" Grid.Column="0"/>
<TextBox x:Name="EmailAddress5Input" Margin="0,130,10,0" Grid.Column="1" Grid.ColumnSpan="2"
TextChanged="EmailAddressInput_TextChanged" LostFocus="EmailAddressInput_LostFocus"/>
<Label x:Name="EmailAddress6Label" Content="E-Mail-Adresse:" Margin="10,160,0,0" Grid.Column="0"/>
<TextBox x:Name="EmailAddress6Input" Margin="0,160,10,0" Grid.Column="1" Grid.ColumnSpan="2"
TextChanged="EmailAddressInput_TextChanged" LostFocus="EmailAddressInput_LostFocus"/>
<Label x:Name="EmailAddress7Label" Content="E-Mail-Adresse:" Margin="10,190,0,0" Grid.Column="0"/>
<TextBox x:Name="EmailAddress7Input" Margin="0,190,10,0" Grid.Column="1" Grid.ColumnSpan="2"
TextChanged="EmailAddressInput_TextChanged" LostFocus="EmailAddressInput_LostFocus"/>
<Label x:Name="EmailAddress8Label" Content="E-Mail-Adresse:" Margin="10,210,0,0" Grid.Column="0"/>
<TextBox x:Name="EmailAddress8Input" Margin="0,210,10,0" Grid.Column="1" Grid.ColumnSpan="2"
TextChanged="EmailAddressInput_TextChanged" LostFocus="EmailAddressInput_LostFocus"/>
<Label x:Name="EmailAddress9Label" Content="E-Mail-Adresse:" Margin="10,250,0,0" Grid.Column="0"/>
<TextBox x:Name="EmailAddress9Input" Margin="0,250,10,0" Grid.Column="1" Grid.ColumnSpan="2"
TextChanged="EmailAddressInput_TextChanged" LostFocus="EmailAddressInput_LostFocus"/>
<ComboBox x:Name="PhoneNr1TypeInput" DisplayMemberPath="Value" Margin="6,70,5,0" FontSize="12" Padding="6,4,4,4"/> <ComboBox x:Name="PhoneNr1TypeInput" DisplayMemberPath="Value" Margin="6,70,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="PhoneNr1Input" Margin="0,70,5,0" Grid.Column="1" <TextBox x:Name="PhoneNr1Input" Margin="0,70,5,0" Grid.Column="1"
TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/> TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/>

View File

@ -17,7 +17,8 @@ namespace Elwig.Windows {
public partial class MemberAdminWindow : AdministrationWindow { public partial class MemberAdminWindow : AdministrationWindow {
private List<string> TextFilter = []; private List<string> TextFilter = [];
private readonly (ComboBox, TextBox, TextBox)[] PhoneNrInputs; private readonly (ComboBox Type, TextBox Number, TextBox Comment)[] PhoneNrInputs;
private readonly (Label Label, TextBox Address)[] EmailAddressInputs;
private readonly RoutedCommand CtrlF = new("CtrlF", typeof(MemberAdminWindow), [new KeyGesture(Key.F, ModifierKeys.Control)]); private readonly RoutedCommand CtrlF = new("CtrlF", typeof(MemberAdminWindow), [new KeyGesture(Key.F, ModifierKeys.Control)]);
private readonly RoutedCommand CtrlP = new("CtrlP", typeof(MemberAdminWindow), [new KeyGesture(Key.P, ModifierKeys.Control)]); private readonly RoutedCommand CtrlP = new("CtrlP", typeof(MemberAdminWindow), [new KeyGesture(Key.P, ModifierKeys.Control)]);
@ -38,6 +39,17 @@ namespace Elwig.Windows {
AddressInput, PlzInput, OrtInput, BillingOrtInput, AddressInput, PlzInput, OrtInput, BillingOrtInput,
BusinessSharesInput, BranchInput, DefaultKgInput BusinessSharesInput, BranchInput, DefaultKgInput
]; ];
EmailAddressInputs = [
(EmailAddress1Label, EmailAddress1Input),
(EmailAddress2Label, EmailAddress2Input),
(EmailAddress3Label, EmailAddress3Input),
(EmailAddress4Label, EmailAddress4Input),
(EmailAddress5Label, EmailAddress5Input),
(EmailAddress6Label, EmailAddress6Input),
(EmailAddress7Label, EmailAddress7Input),
(EmailAddress8Label, EmailAddress8Input),
(EmailAddress9Label, EmailAddress9Input),
];
PhoneNrInputs = [ PhoneNrInputs = [
(PhoneNr1TypeInput, PhoneNr1Input, PhoneNr1CommentInput), (PhoneNr1TypeInput, PhoneNr1Input, PhoneNr1CommentInput),
(PhoneNr2TypeInput, PhoneNr2Input, PhoneNr2CommentInput), (PhoneNr2TypeInput, PhoneNr2Input, PhoneNr2CommentInput),
@ -63,7 +75,7 @@ namespace Elwig.Windows {
Menu_Print_MemberDataSheet.IsEnabled = App.IsPrintingReady; Menu_Print_MemberDataSheet.IsEnabled = App.IsPrintingReady;
ActiveMemberInput.IsChecked = true; ActiveMemberInput.IsChecked = true;
UpdatePhoneNrInputVisibility(); UpdateContactInfoVisibility();
LockInputs(); LockInputs();
} }
@ -236,27 +248,55 @@ namespace Elwig.Windows {
private void SetPhoneNrInput(int nr, string? type, string? number, string? comment) { private void SetPhoneNrInput(int nr, string? type, string? number, string? comment) {
var inputs = PhoneNrInputs[nr]; var inputs = PhoneNrInputs[nr];
inputs.Item1.SelectedItem = (type == null) ? null : inputs.Item1.ItemsSource.Cast<KeyValuePair<string, string>>().FirstOrDefault(p => p.Key == type); inputs.Type.SelectedItem = (type == null) ? null : inputs.Type.ItemsSource.Cast<KeyValuePair<string, string>>().FirstOrDefault(p => p.Key == type);
inputs.Item2.Text = number; inputs.Number.Text = number;
inputs.Item3.Text = comment; inputs.Comment.Text = comment;
}
private void SetEmailAddressInput(int nr, string? address) {
var inputs = EmailAddressInputs[nr];
inputs.Address.Text = address;
} }
private (string, string, string?)? GetPhoneNrInput(int nr) { private (string, string, string?)? GetPhoneNrInput(int nr) {
var inputs = PhoneNrInputs[nr]; var inputs = PhoneNrInputs[nr];
var number = inputs.Item2.Text; var number = inputs.Number.Text;
if (string.IsNullOrEmpty(number)) if (string.IsNullOrEmpty(number))
return null; return null;
var type = (inputs.Item1.SelectedItem as KeyValuePair<string, string>?)?.Key ?? (number.StartsWith("+43 ") && number[4] == '6' ? "mobile" : "landline"); var type = (inputs.Type.SelectedItem as KeyValuePair<string, string>?)?.Key ?? (number.StartsWith("+43 ") && number[4] == '6' ? "mobile" : "landline");
var comment = inputs.Item3.Text; var comment = inputs.Comment.Text;
return (type, number, comment == "" ? null : comment); return (type, number, comment == "" ? null : comment);
} }
private void SetPhoneNrInputVisible(int nr, bool visible) { private string? GetEmailAddressInput(int nr) {
var inputs = EmailAddressInputs[nr];
return inputs.Address.Text == "" ? null : inputs.Address.Text;
}
private void SetPhoneNrInputVisible(int nr, bool visible, int? position = null) {
var inputs = PhoneNrInputs[nr]; var inputs = PhoneNrInputs[nr];
if (position is int p) {
var mt = 10 + p * 30;
inputs.Type.Margin = new(6, mt, 5, 0);
inputs.Number.Margin = new(0, mt, 5, 0);
inputs.Comment.Margin = new(0, mt, 10, 0);
}
var vis = visible ? Visibility.Visible : Visibility.Hidden; var vis = visible ? Visibility.Visible : Visibility.Hidden;
inputs.Item1.Visibility = vis; inputs.Type.Visibility = vis;
inputs.Item2.Visibility = vis; inputs.Number.Visibility = vis;
inputs.Item3.Visibility = vis; inputs.Comment.Visibility = vis;
}
private void SetEmailAddressInputVisible(int nr, bool visible, int? position = null) {
var inputs = EmailAddressInputs[nr];
if (position is int p) {
var mt = 10 + p * 30;
inputs.Label.Margin = new(10, mt, 0, 0);
inputs.Address.Margin = new(0, mt, 10, 0);
}
var vis = visible ? Visibility.Visible : Visibility.Hidden;
inputs.Label.Visibility = vis;
inputs.Address.Visibility = vis;
} }
private void MemberList_SelectionChanged(object sender, RoutedEventArgs evt) { private void MemberList_SelectionChanged(object sender, RoutedEventArgs evt) {
@ -274,7 +314,7 @@ namespace Elwig.Windows {
HideNewEditDeleteButtons(); HideNewEditDeleteButtons();
ShowSaveResetCancelButtons(); ShowSaveResetCancelButtons();
UnlockInputs(); UnlockInputs();
UpdatePhoneNrInputVisibility(true); UpdateContactInfoVisibility(true);
InitInputs(); InitInputs();
LockSearchInputs(); LockSearchInputs();
} }
@ -289,7 +329,7 @@ namespace Elwig.Windows {
HideNewEditDeleteButtons(); HideNewEditDeleteButtons();
ShowSaveResetCancelButtons(); ShowSaveResetCancelButtons();
UnlockInputs(); UnlockInputs();
UpdatePhoneNrInputVisibility(true); UpdateContactInfoVisibility(true);
LockSearchInputs(); LockSearchInputs();
} }
@ -315,7 +355,7 @@ namespace Elwig.Windows {
HideSaveResetCancelButtons(); HideSaveResetCancelButtons();
ShowNewEditDeleteButtons(); ShowNewEditDeleteButtons();
LockInputs(); LockInputs();
UpdatePhoneNrInputVisibility(); UpdateContactInfoVisibility();
UnlockSearchInputs(); UnlockSearchInputs();
FinishInputFilling(); FinishInputFilling();
await RefreshMemberList(); await RefreshMemberList();
@ -342,7 +382,7 @@ namespace Elwig.Windows {
ShowNewEditDeleteButtons(); ShowNewEditDeleteButtons();
RefreshInputs(); RefreshInputs();
LockInputs(); LockInputs();
UpdatePhoneNrInputVisibility(); UpdateContactInfoVisibility();
UnlockSearchInputs(); UnlockSearchInputs();
} }
@ -504,13 +544,24 @@ namespace Elwig.Windows {
ActiveMemberInput.IsEnabled = true; ActiveMemberInput.IsEnabled = true;
} }
private void UpdatePhoneNrInputVisibility(bool extra = false) { private void UpdateContactInfoVisibility(bool extra = false) {
bool lastVisible = true; var m = MemberList.SelectedItem as Member;
var m = (Member)MemberList.SelectedItem; bool lastVisible;
int num = 0;
lastVisible = true;
for (int i = 0; i < EmailAddressInputs.Length; i++) {
var input = EmailAddressInputs[i];
var vis = !string.IsNullOrEmpty(input.Address.Text) || (m?.EmailAddresses.Any(a => a.Nr - 1 == i) ?? false);
var cVis = vis || (extra && lastVisible);
SetEmailAddressInputVisible(i, cVis, cVis ? num++ : null);
lastVisible = vis;
}
lastVisible = true;
for (int i = 0; i < PhoneNrInputs.Length; i++) { for (int i = 0; i < PhoneNrInputs.Length; i++) {
var input = PhoneNrInputs[i]; var input = PhoneNrInputs[i];
var vis = !string.IsNullOrEmpty(input.Item2.Text) || (m?.TelephoneNumbers.Any(p => p.Nr - 1 == i) ?? false); var vis = !string.IsNullOrEmpty(input.Number.Text) || (m?.TelephoneNumbers.Any(n => n.Nr - 1 == i) ?? false);
SetPhoneNrInputVisible(i, vis || (extra && lastVisible)); var cVis = vis || (extra && lastVisible);
SetPhoneNrInputVisible(i, cVis, cVis ? num++ : null);
lastVisible = vis; lastVisible = vis;
} }
} }
@ -605,17 +656,17 @@ namespace Elwig.Windows {
} }
} }
for (int i = 0; i < 2; i++) { for (int i = 0; i < EmailAddressInputs.Length; i++) {
var input = i == 0 ? EmailAddress1Input : EmailAddress2Input; var input = GetEmailAddressInput(i);
var emailAddr = m.EmailAddresses.FirstOrDefault(a => a.Nr - 1 == i); var emailAddr = m.EmailAddresses.FirstOrDefault(a => a.Nr - 1 == i);
if (input.Text == "") { if (input == null || input == "") {
if (emailAddr != null) { if (emailAddr != null) {
Context.Remove(emailAddr); Context.Remove(emailAddr);
} }
} else { } else {
MemberEmailAddr a = emailAddr ?? Context.CreateProxy<MemberEmailAddr>(); MemberEmailAddr a = emailAddr ?? Context.CreateProxy<MemberEmailAddr>();
a.Nr = i + 1; a.Nr = i + 1;
a.Address = input.Text; a.Address = input ?? "";
a.Comment = null; a.Comment = null;
if (emailAddr == null) { if (emailAddr == null) {
a.MgNr = newMgNr; a.MgNr = newMgNr;
@ -678,8 +729,14 @@ namespace Elwig.Windows {
} }
var emailAddrs = m.EmailAddresses.OrderBy(a => a.Nr).ToList(); var emailAddrs = m.EmailAddresses.OrderBy(a => a.Nr).ToList();
EmailAddress1Input.Text = emailAddrs.Count > 0 ? emailAddrs[0].Address : ""; for (int i = 0; i< EmailAddressInputs.Length; i++) {
EmailAddress2Input.Text = emailAddrs.Count > 1 ? emailAddrs[1].Address : ""; if (i < emailAddrs.Count) {
var emailAddr = emailAddrs[i];
SetEmailAddressInput(i, emailAddr.Address);
} else {
SetEmailAddressInput(i, null);
}
}
var phoneNrs = m.TelephoneNumbers.OrderBy(p => p.Nr).ToList(); var phoneNrs = m.TelephoneNumbers.OrderBy(p => p.Nr).ToList();
for (int i = 0; i < PhoneNrInputs.Length; i++) { for (int i = 0; i < PhoneNrInputs.Length; i++) {
@ -690,7 +747,7 @@ namespace Elwig.Windows {
SetPhoneNrInput(i, null, null, null); SetPhoneNrInput(i, null, null, null);
} }
} }
UpdatePhoneNrInputVisibility(IsEditing || IsCreating); UpdateContactInfoVisibility(IsEditing || IsCreating);
IbanInput.Text = m.Iban; IbanInput.Text = m.Iban;
BicInput.Text = m.Bic; BicInput.Text = m.Bic;
@ -786,9 +843,14 @@ namespace Elwig.Windows {
InputLostFocus((TextBox)sender, Validator.CheckPredecessorMgNr); InputLostFocus((TextBox)sender, Validator.CheckPredecessorMgNr);
} }
private new void EmailAddressInput_TextChanged(object sender, TextChangedEventArgs evt) {
base.EmailAddressInput_TextChanged(sender, evt);
UpdateContactInfoVisibility(IsEditing || IsCreating);
}
private new void PhoneNrInput_TextChanged(object sender, TextChangedEventArgs evt) { private new void PhoneNrInput_TextChanged(object sender, TextChangedEventArgs evt) {
base.PhoneNrInput_TextChanged(sender, evt); base.PhoneNrInput_TextChanged(sender, evt);
UpdatePhoneNrInputVisibility(IsEditing || IsCreating); UpdateContactInfoVisibility(IsEditing || IsCreating);
} }
private void KgDetailsButton_Click(object sender, RoutedEventArgs evt) { private void KgDetailsButton_Click(object sender, RoutedEventArgs evt) {

View File

@ -1,22 +0,0 @@
<Window x:Class="Elwig.Windows.MemberListWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Elwig.Windows"
mc:Ignorable="d"
Title="Mitgliederliste - Elwig" Height="450" Width="800">
<Grid>
<DataGrid x:Name="MemberList" AutoGenerateColumns="False" HeadersVisibility="Column" IsReadOnly="True" GridLinesVisibility="None" SelectionMode="Single"
CanUserDeleteRows="False" CanUserResizeRows="False" CanUserAddRows="False" FontSize="14">
<DataGrid.Columns>
<DataGridTextColumn Header="MgNr." Binding="{Binding MgNr}" Width="70"/>
<DataGridTextColumn Header="Präfix" Binding="{Binding Prefix}" Width="100"/>
<DataGridTextColumn Header="Vorname" Binding="{Binding GivenName}" Width="100"/>
<DataGridTextColumn Header="Weitere Namen" Binding="{Binding MiddleName}" Width="100"/>
<DataGridTextColumn Header="Nachname" Binding="{Binding FamilyName}" Width="100"/>
<DataGridTextColumn Header="Suffix" Binding="{Binding Suffix}" Width="100"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>

View File

@ -1,27 +0,0 @@
using Elwig.Helpers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace Elwig.Windows {
public partial class MemberListWindow : Window {
private readonly AppDbContext Context = new();
public MemberListWindow() {
InitializeComponent();
MemberList.ItemsSource = Context.Members.ToList();
}
}
}

View File

@ -6,7 +6,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Elwig.Windows" xmlns:local="clr-namespace:Elwig.Windows"
mc:Ignorable="d" mc:Ignorable="d"
Title="Auszahlungsvarianten - Elwig" Height="500" Width="820" MinHeight="500" MinWidth="820"> Title="Auszahlungsvarianten - Elwig" Height="510" Width="820" MinHeight="500" MinWidth="820">
<Window.Resources> <Window.Resources>
<Style TargetType="Label"> <Style TargetType="Label">
<Setter Property="HorizontalAlignment" Value="Left"/> <Setter Property="HorizontalAlignment" Value="Left"/>
@ -42,7 +42,7 @@
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
<RowDefinition Height="200"/> <RowDefinition Height="220"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<ListBox x:Name="PaymentVariantList" Margin="10,10,35,10" Grid.RowSpan="2" SelectionChanged="PaymentVariantList_SelectionChanged"> <ListBox x:Name="PaymentVariantList" Margin="10,10,35,10" Grid.RowSpan="2" SelectionChanged="PaymentVariantList_SelectionChanged">
@ -163,6 +163,9 @@
<Button x:Name="ExportButton" Content="Exportieren" FontSize="14" Width="180" Margin="10,10,10,10" Height="27" IsEnabled="False" <Button x:Name="ExportButton" Content="Exportieren" FontSize="14" Width="180" Margin="10,10,10,10" Height="27" IsEnabled="False"
Click="ExportButton_Click" Click="ExportButton_Click"
VerticalAlignment="Top" HorizontalAlignment="Left"/> VerticalAlignment="Top" HorizontalAlignment="Left"/>
<Button x:Name="TransactionButton" Content="Buchungsliste" FontSize="14" Width="180" Margin="10,42,10,10" Height="27" IsEnabled="False"
Click="TransactionButton_Click"
VerticalAlignment="Top" HorizontalAlignment="Left"/>
<ProgressBar x:Name="ProgressBar" Margin="10,10,10,74" Height="27" Width="180" <ProgressBar x:Name="ProgressBar" Margin="10,10,10,74" Height="27" Width="180"
VerticalAlignment="Bottom" HorizontalAlignment="Left"/> VerticalAlignment="Bottom" HorizontalAlignment="Left"/>

View File

@ -57,6 +57,7 @@ namespace Elwig.Windows {
ShowButton.IsEnabled = true; ShowButton.IsEnabled = true;
PrintButton.IsEnabled = true; PrintButton.IsEnabled = true;
ExportButton.IsEnabled = locked; ExportButton.IsEnabled = locked;
TransactionButton.IsEnabled = locked;
NameInput.Text = v.Name; NameInput.Text = v.Name;
NameInput.IsReadOnly = false; NameInput.IsReadOnly = false;
@ -101,6 +102,7 @@ namespace Elwig.Windows {
ShowButton.IsEnabled = false; ShowButton.IsEnabled = false;
PrintButton.IsEnabled = false; PrintButton.IsEnabled = false;
ExportButton.IsEnabled = false; ExportButton.IsEnabled = false;
TransactionButton.IsEnabled = false;
BillingData = null; BillingData = null;
NameInput.Text = ""; NameInput.Text = "";
@ -173,7 +175,7 @@ namespace Elwig.Windows {
v.Name = "Neue Auszahlungsvariante"; v.Name = "Neue Auszahlungsvariante";
v.TestVariant = true; v.TestVariant = true;
v.DateString = $"{DateTime.Today:yyyy-MM-dd}"; v.DateString = $"{DateTime.Today:yyyy-MM-dd}";
v.Data = "{\"mode\": \"elwig\", \"version\": 1, \"payment\": 1.0, \"quality\": {\"WEI\": 0}, \"curves\": []}"; v.Data = "{\"mode\": \"elwig\", \"version\": 1, \"payment\": {}, \"curves\": []}";
await Context.AddAsync(v); await Context.AddAsync(v);
await Context.SaveChangesAsync(); await Context.SaveChangesAsync();
@ -311,6 +313,31 @@ namespace Elwig.Windows {
} }
} }
private async void TransactionButton_Click(object sender, RoutedEventArgs evt) {
if (PaymentVariantList.SelectedValue is not PaymentVar v) {
return;
}
var d = new SaveFileDialog() {
FileName = $"{App.Client.NameToken}-Buchungsliste-{v.Year}-{v.Name.Trim().Replace(' ', '-')}.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"Buchungsliste speichern unter - Elwig"
};
if (d.ShowDialog() == true) {
TransactionButton.IsEnabled = false;
Mouse.OverrideCursor = Cursors.AppStarting;
try {
var tbl = await CreditNoteData.ForPaymentVariant(Context, v.Year, v.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;
TransactionButton.IsEnabled = true;
}
}
private async void SaveButton_Click(object sender, RoutedEventArgs evt) { private async void SaveButton_Click(object sender, RoutedEventArgs evt) {
if (PaymentVariantList.SelectedItem is not PaymentVar v || BillingData == null) return; if (PaymentVariantList.SelectedItem is not PaymentVar v || BillingData == null) return;
try { try {
@ -488,7 +515,7 @@ namespace Elwig.Windows {
members = members.OrderBy(m => m.MgNr); members = members.OrderBy(m => m.MgNr);
IEnumerable<Member> list = await members.ToListAsync(); IEnumerable<Member> list = await members.ToListAsync();
var data = await CreditNoteData.ForPaymentVariant(Context.CreditNoteRows, Context.Seasons, v.Year, v.AvNr); var data = await CreditNoteDeliveryData.ForPaymentVariant(Context.CreditNoteDeliveryRows, Context.Seasons, v.Year, v.AvNr);
var payments = await Context.MemberPayments.Where(p => p.Year == v.Year && p.AvNr == v.AvNr).ToDictionaryAsync(c => c.MgNr); var payments = await Context.MemberPayments.Where(p => p.Year == v.Year && p.AvNr == v.AvNr).ToDictionaryAsync(c => c.MgNr);
await Context.GetMemberAreaCommitmentBuckets(Year, 0); await Context.GetMemberAreaCommitmentBuckets(Year, 0);
using var doc = Document.Merge(list.Select(m => using var doc = Document.Merge(list.Select(m =>

View File

@ -48,7 +48,7 @@ namespace Elwig.Windows {
private async void ZipButton_Click(object sender, RoutedEventArgs evt) { private async void ZipButton_Click(object sender, RoutedEventArgs evt) {
using var ctx = new AppDbContext(); using var ctx = new AppDbContext();
using var ods = new OdsFile(@"C:\Users\Lorenz\Desktop\test.ods"); using var ods = new OdsFile(@"C:\Users\Lorenz\Desktop\test.ods");
await ods.AddTable(await DeliveryConfirmationData.ForMember(ctx.DeliveryParts, 2023, ctx.Members.Find(2948))); await ods.AddTable(await DeliveryConfirmationDeliveryData.ForMember(ctx.DeliveryParts, 2023, ctx.Members.Find(2948)));
} }
} }
} }

View File

@ -1,5 +1,5 @@
::mkdir "C:\Program Files\Elwig" ::mkdir "C:\Program Files\Elwig"
::curl -s "http://www.columbia.edu/~em36/PDFtoPrinter.exe" -z "C:\Program Files\Elwig\PDFtoPrinter.exe" -o "C:\Program Files\Elwig\PDFtoPrinter.exe" ::curl -s -L "http://www.columbia.edu/~em36/PDFtoPrinter.exe" -z "C:\Program Files\Elwig\PDFtoPrinter.exe" -o "C:\Program Files\Elwig\PDFtoPrinter.exe"
mkdir "C:\ProgramData\Elwig\resources" mkdir "C:\ProgramData\Elwig\resources"
copy /b /y Documents\*.css "C:\ProgramData\Elwig\resources" copy /b /y Documents\*.css "C:\ProgramData\Elwig\resources"
copy /b /y Documents\*.cshtml "C:\ProgramData\Elwig\resources" copy /b /y Documents\*.cshtml "C:\ProgramData\Elwig\resources"

View File

@ -25,7 +25,7 @@
</Task> </Task>
</UsingTask> </UsingTask>
<Target Name="CustomBeforeBuild" BeforeTargets="BeforeBuild"> <Target Name="CustomBeforeBuild" BeforeTargets="BeforeBuild">
<Exec Command="curl -s &quot;http://www.columbia.edu/~em36/PDFtoPrinter.exe&quot; -z &quot;$(TargetDir)PDFtoPrinter.exe&quot; -o &quot;$(TargetDir)PDFtoPrinter.exe&quot;" /> <Exec Command="curl -s -L &quot;http://www.columbia.edu/~em36/PDFtoPrinter.exe&quot; -z &quot;$(TargetDir)PDFtoPrinter.exe&quot; -o &quot;$(TargetDir)PDFtoPrinter.exe&quot;" />
<Exec Command="dotnet publish &quot;$(SolutionDir)Elwig\Elwig.csproj&quot; &quot;/p:PublishProfile=$(SolutionDir)\Elwig\Properties\PublishProfiles\FolderProfile.pubxml&quot;" /> <Exec Command="dotnet publish &quot;$(SolutionDir)Elwig\Elwig.csproj&quot; &quot;/p:PublishProfile=$(SolutionDir)\Elwig\Properties\PublishProfiles\FolderProfile.pubxml&quot;" />
<GetFileVersion AssemblyPath="..\Elwig\bin\Publish\Elwig.exe"> <GetFileVersion AssemblyPath="..\Elwig\bin\Publish\Elwig.exe">
<Output TaskParameter="Version" PropertyName="ElwigFileVersion" /> <Output TaskParameter="Version" PropertyName="ElwigFileVersion" />

View File

@ -5,8 +5,8 @@
<Cultures>de-AT</Cultures> <Cultures>de-AT</Cultures>
</PropertyGroup> </PropertyGroup>
<Target Name="CustomBeforeBuild" BeforeTargets="BeforeBuild"> <Target Name="CustomBeforeBuild" BeforeTargets="BeforeBuild">
<Exec Command='curl -L -s "https://go.microsoft.com/fwlink/p/?LinkId=2124703" -z "$(TargetDir)MicrosoftEdgeWebview2Setup.exe" -o "$(TargetDir)MicrosoftEdgeWebview2Setup.exe"' /> <Exec Command='curl -s -L "https://go.microsoft.com/fwlink/p/?LinkId=2124703" -z "$(TargetDir)MicrosoftEdgeWebview2Setup.exe" -o "$(TargetDir)MicrosoftEdgeWebview2Setup.exe"' />
<Exec Command='curl -L -s "https://aka.ms/vs/17/release/vc_redist.x86.exe" -z "$(TargetDir)VC_redist.x86.exe" -o "$(TargetDir)VC_redist.x86.exe"' /> <Exec Command='curl -s -L "https://aka.ms/vs/17/release/vc_redist.x86.exe" -z "$(TargetDir)VC_redist.x86.exe" -o "$(TargetDir)VC_redist.x86.exe"' />
<PropertyGroup> <PropertyGroup>
<DefineConstants>ElwigProjectDir=..\Elwig</DefineConstants> <DefineConstants>ElwigProjectDir=..\Elwig</DefineConstants>
</PropertyGroup> </PropertyGroup>

View File

@ -1,4 +1,5 @@
using Elwig.Helpers; using Elwig.Helpers;
using Elwig.Helpers.Billing;
using Microsoft.Data.Sqlite; using Microsoft.Data.Sqlite;
using System.Reflection; using System.Reflection;
@ -16,6 +17,11 @@ namespace Tests {
await AppDbContext.ExecuteEmbeddedScript(Connection, Assembly.GetExecutingAssembly(), "Tests.Resources.Insert.sql"); await AppDbContext.ExecuteEmbeddedScript(Connection, Assembly.GetExecutingAssembly(), "Tests.Resources.Insert.sql");
} }
[OneTimeSetUp]
public async Task SetupBillingData() {
await BillingData.Init();
}
[OneTimeTearDown] [OneTimeTearDown]
public async Task TeardownDatabase() { public async Task TeardownDatabase() {
AppDbContext.ConnectionStringOverride = null; AppDbContext.ConnectionStringOverride = null;

View File

@ -8,12 +8,7 @@ namespace Tests.HelperTests {
public class BillingDataTest { public class BillingDataTest {
private static readonly JsonSerializerOptions JsonOpts = new() { WriteIndented = true }; private static readonly JsonSerializerOptions JsonOpts = new() { WriteIndented = true };
private static readonly string[] AttributeVariants = ["GV", "GVD", "GVK", "GVS", "GVZ", "WR", "WRS", "ZW", "ZWS", "ZWZ"]; private static readonly string[] Vaributes = ["GV", "GVD", "GVK", "GVS", "GVZ", "WR", "WRS", "ZW", "ZWS", "ZWZ"];
[OneTimeSetUp]
public async Task SetupBilling() {
await BillingData.Init();
}
private static (string, string?) GetSortIdAttrId(string bucket) { private static (string, string?) GetSortIdAttrId(string bucket) {
return (bucket[..2], bucket.Length > 2 ? bucket[2..] : null); return (bucket[..2], bucket.Length > 2 ? bucket[2..] : null);
@ -52,7 +47,7 @@ namespace Tests.HelperTests {
"payment": 0.5, "payment": 0.5,
"curves": [] "curves": []
} }
""", AttributeVariants); """, Vaributes);
Assert.Multiple(() => { Assert.Multiple(() => {
TestCalcOe(data, "GV", 73, 0.5m); TestCalcOe(data, "GV", 73, 0.5m);
TestCalcOe(data, "WRS", 74, 0.5m); TestCalcOe(data, "WRS", 74, 0.5m);
@ -77,7 +72,7 @@ namespace Tests.HelperTests {
"geb": 0.10 "geb": 0.10
}] }]
} }
""", AttributeVariants); """, Vaributes);
Assert.Multiple(() => { Assert.Multiple(() => {
TestCalcOe(data, "GV", 70, 0.25m); TestCalcOe(data, "GV", 70, 0.25m);
TestCalcOe(data, "GV", 72, 0.25m); TestCalcOe(data, "GV", 72, 0.25m);
@ -114,7 +109,7 @@ namespace Tests.HelperTests {
} }
}] }]
} }
""", AttributeVariants); """, Vaributes);
Assert.Multiple(() => { Assert.Multiple(() => {
TestCalcKmw(data, "GV", 13.00, 0.10m); TestCalcKmw(data, "GV", 13.00, 0.10m);
TestCalcKmw(data, "GV", 13.50, 0.10m); TestCalcKmw(data, "GV", 13.50, 0.10m);
@ -148,7 +143,7 @@ namespace Tests.HelperTests {
}, },
"curves": [] "curves": []
} }
""", AttributeVariants); """, Vaributes);
Assert.Multiple(() => { Assert.Multiple(() => {
TestCalcOe(data, "WR", 73, 0.10m); TestCalcOe(data, "WR", 73, 0.10m);
TestCalcOe(data, "WRS", 73, 0.15m); TestCalcOe(data, "WRS", 73, 0.15m);
@ -179,7 +174,7 @@ namespace Tests.HelperTests {
}, },
"curves": [] "curves": []
} }
""", AttributeVariants); """, Vaributes);
Assert.Multiple(() => { Assert.Multiple(() => {
TestCalcOe(data, "GV", 75, 0.30m, qualid: "WEI"); TestCalcOe(data, "GV", 75, 0.30m, qualid: "WEI");
TestCalcOe(data, "ZW", 76, 0.25m, qualid: "WEI"); TestCalcOe(data, "ZW", 76, 0.25m, qualid: "WEI");
@ -221,7 +216,7 @@ namespace Tests.HelperTests {
} }
}] }]
} }
""", AttributeVariants); """, Vaributes);
Assert.Multiple(() => { Assert.Multiple(() => {
TestCalcKmw(data, "GV", 15.0, 2.0m); TestCalcKmw(data, "GV", 15.0, 2.0m);
TestCalcKmw(data, "GV", 15.5, 2.272727m); TestCalcKmw(data, "GV", 15.5, 2.272727m);
@ -281,7 +276,7 @@ namespace Tests.HelperTests {
} }
}] }]
} }
""", AttributeVariants); """, Vaributes);
Assert.Multiple(() => { Assert.Multiple(() => {
TestCalcKmw(data, "GV", 15.0, 0.75m); TestCalcKmw(data, "GV", 15.0, 0.75m);
TestCalcKmw(data, "GVS", 15.0, 0.50m); TestCalcKmw(data, "GVS", 15.0, 0.50m);
@ -340,7 +335,7 @@ namespace Tests.HelperTests {
} }
}] }]
} }
""", AttributeVariants); """, Vaributes);
Assert.Multiple(() => { Assert.Multiple(() => {
TestCalcOe(data, "GVK", 73, 0.032m); TestCalcOe(data, "GVK", 73, 0.032m);
TestCalcOe(data, "ZWS", 74, 0.033m); TestCalcOe(data, "ZWS", 74, 0.033m);
@ -349,11 +344,11 @@ namespace Tests.HelperTests {
}); });
} }
private static List<ContractSelection> GetSelection(IEnumerable<string> attVars) { private static List<Varibute> GetSelection(IEnumerable<string> attVars) {
return attVars.Select(s => { return attVars.Select(s => {
var sortid = s[..2]; var sortid = s[..2];
var attrid = s.Length > 2 ? s[2..] : null; var attrid = s.Length > 2 ? s[2..] : null;
return new ContractSelection( return new Varibute(
new WineVar(sortid, sortid), new WineVar(sortid, sortid),
attrid == null ? null : new WineAttr(attrid, attrid) attrid == null ? null : new WineAttr(attrid, attrid)
); );

View File

@ -1,4 +1,5 @@
using Elwig.Helpers; using Elwig.Helpers;
using Elwig.Helpers.Billing;
using Microsoft.Data.Sqlite; using Microsoft.Data.Sqlite;
using System.Reflection; using System.Reflection;
@ -6,6 +7,9 @@ namespace Tests.HelperTests {
[TestFixture] [TestFixture]
public class BillingTest { public class BillingTest {
private const int Year1 = 2020, Year2 = 2020;
private const int MgNr1 = 101, MgNr2 = 102, MgNr3 = 103, MgNr4 = 104;
private SqliteConnection? Connection; private SqliteConnection? Connection;
[OneTimeSetUp] [OneTimeSetUp]
@ -22,9 +26,120 @@ namespace Tests.HelperTests {
Connection = null; Connection = null;
} }
[TearDown]
public async Task CleanupDatabasePayment() {
if (Connection == null) return;
await AppDbContext.ExecuteBatch(Connection, """
DELETE FROM credit;
DELETE FROM delivery_part_bucket;
DELETE FROM payment_variant;
""");
}
private Task<Dictionary<string, AreaComBucket>> GetMemberAreaCommitmentBuckets(int year, int mgnr) {
var ctx = new AppDbContext();
return ctx.GetMemberAreaCommitmentBuckets(year, mgnr, Connection);
}
private Task<Dictionary<string, int>> GetMemberDeliveryBuckets(int year, int mgnr) {
var ctx = new AppDbContext();
return ctx.GetMemberDeliveryBuckets(year, mgnr, Connection);
}
private Task<Dictionary<string, int>> GetMemberPaymentBuckets(int year, int mgnr) {
var ctx = new AppDbContext();
return ctx.GetMemberPaymentBuckets(year, mgnr, Connection);
}
private async Task<Dictionary<(string, string), int>> GetMemberDeliveryPrices(int year, int mgnr) {
var buckets = new Dictionary<(string, string), int>();
using (var cmd = Connection!.CreateCommand()) {
cmd.CommandText = $"""
SELECT lsnr || '/' || d.dpnr, d.sortid || b.discr, price
FROM v_delivery d
LEFT JOIN payment_delivery_part_bucket p ON (p.year, p.did, p.dpnr) = (d.year, d.did, d.dpnr)
LEFT JOIN delivery_part_bucket b ON (b.year, b.did, b.dpnr, b.bktnr) = (p.year, p.did, p.dpnr, p.bktnr)
WHERE d.year = {year} AND mgnr = {mgnr}
""";
using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync()) {
var lsnr = reader.GetString(0);
var bucket = reader.GetString(1);
buckets[(lsnr, bucket)] = reader.GetInt32(2);
}
}
return buckets;
}
private Task InsertPaymentVariant(int year, int avnr, string data) {
return AppDbContext.ExecuteBatch(Connection!, $"""
INSERT INTO payment_variant (year, avnr, name, date, transfer_date, test_variant, calc_time, data)
VALUES ({year}, {avnr}, 'Test', '2021-01-15', NULL, TRUE, NULL, '{data}');
""");
}
[Test] [Test]
public void Test() { public async Task Test_01_NoActiveAreaCommitments() {
// TODO int mgnr = MgNr1, year = Year1;
await InsertPaymentVariant(year, 1, """
{
"mode": "elwig",
"version": 1,
"payment": {
"GV/": "curve:0",
"GV/B": "curve:1",
"GV/K": "curve:2"
},
"quality": {"WEI": 0.1},
"curves": [{
"id": 0,
"mode": "oe",
"data": {"15kmw": 0.5},
"geb": 0.1
}, {
"id": 1,
"mode": "oe",
"data": {"15kmw": 0.54},
"geb": 0.1
}, {
"id": 2,
"mode": "oe",
"data": {"15kmw": 0.61},
"geb": 0.1
}]
}
""");
var areaCom = await GetMemberAreaCommitmentBuckets(year, mgnr);
Assert.That(areaCom, Is.Empty);
var delivery = await GetMemberDeliveryBuckets(year, mgnr);
Assert.Multiple(() => {
Assert.That(delivery, Has.Count.EqualTo(4));
Assert.That(delivery["GV"], Is.EqualTo(16_000));
Assert.That(delivery["GV_"], Is.EqualTo( 1_000));
Assert.That(delivery["GVB"], Is.EqualTo( 8_000));
Assert.That(delivery["GVK"], Is.EqualTo( 4_000));
});
BillingVariant b = new(year, 1);
await b.CalculateBuckets(false, false, false);
var payment = await GetMemberPaymentBuckets(year, mgnr);
Assert.Multiple(() => {
Assert.That(payment, Has.Count.EqualTo(1));
Assert.That(payment["GV_"], Is.EqualTo(17_000));
});
await b.Calculate();
var prices = await GetMemberDeliveryPrices(year, mgnr);
Assert.Multiple(() => {
Assert.That(prices, Has.Count.EqualTo(6));
Assert.That(prices[("20201001X001/1", "GV_")], Is.EqualTo(0_6100));
Assert.That(prices[("20201001X001/2", "GV_")], Is.EqualTo(0_5000));
Assert.That(prices[("20201001X002/1", "GV_")], Is.EqualTo(0_5400));
Assert.That(prices[("20201001X002/2", "GV_")], Is.EqualTo(0_5400));
Assert.That(prices[("20201001X003/1", "GV_")], Is.EqualTo(0_1000));
Assert.That(prices[("20201001X003/2", "GV_")], Is.EqualTo(0_5000));
});
} }
} }
} }

View File

@ -1 +1,11 @@
-- deletes for HelpersBillingTest -- deletes for HelperTests.BillingTest
DELETE FROM credit;
DELETE FROM payment_variant
DELETE FROM delivery;
DELETE FROM area_commitment;
DELETE FROM area_commitment_type;
DELETE FROM season;
DELETE FROM member;
DELETE FROM wine_attribute;
DELETE FROM wine_cultivation;

View File

@ -1 +1,59 @@
-- inserts for HelpersBillingTest -- inserts for HelperTests.BillingTest
INSERT INTO wine_cultivation (cultid, name, description) VALUES
('N', 'Normal', NULL),
('K', 'KIP', 'Kontrollierte Integrierte Produktion'),
('B', 'Org. Biologisch', 'Organisch Biologisch');
INSERT INTO wine_attribute (attrid, name, active, max_kg_per_ha, strict, fill_lower) VALUES
('B', 'Bio', TRUE, NULL, FALSE, 0),
('K', 'Kabinett', TRUE, NULL, FALSE, 0),
('D', 'DAC', TRUE, 7500, FALSE, 0),
('S', 'Saft', TRUE, NULL, FALSE, 0),
('Z', 'Sekt', TRUE, NULL, FALSE, 0),
('P', 'Vertrag P', TRUE, NULL, TRUE, 0),
('Q', 'Vertrag Q', TRUE, NULL, TRUE, 1),
('R', 'Vertrag R', TRUE, NULL, TRUE, 2);
INSERT INTO area_commitment_type (vtrgid, sortid, attrid, disc, min_kg_per_ha, penalty_per_kg, penalty_amount, penalty_none) VALUES
('ZW', 'ZW', NULL, NULL, 2500, 600, NULL, NULL),
('GV', 'GV', NULL, NULL, 5000, 500, NULL, NULL),
('GVK', 'GV', 'K', NULL, 5000, 500, NULL, NULL),
('GVD', 'GV', 'D', NULL, 5000, 1000, NULL, NULL),
('GVP', 'GV', 'P', NULL, 5000, 1000, 1000000, NULL),
('GVQ', 'GV', 'Q', NULL, 5000, 1000, 1000000, NULL),
('GVR', 'GV', 'R', NULL, 5000, 1000, 1000000, NULL);
INSERT INTO member (mgnr, given_name, family_name, zwstid, volllieferant, buchführend, country, postal_dest, address, default_kgnr) VALUES
(101, 'Max', 'Mustermann', 'X', FALSE, FALSE, 40, 222303524, 'Winzerstraße 1', 06109),
(102, 'Wernhardt', 'Weinbauer', 'X', FALSE, FALSE, 40, 222303524, 'Winzerstraße 2', 06109),
(103, 'Matthäus', 'Musterbauer', 'X', FALSE, FALSE, 40, 212005138, 'Brünner Straße 10', 15224),
(104, 'Waltraud', 'Winzer', 'X', FALSE, FALSE, 40, 212005138, 'Wiener Straße 15', 15224);
INSERT INTO area_commitment (fbnr, mgnr, vtrgid, cultid, area, kgnr, gstnr, rdnr, year_from, year_to) VALUES
( 1, 101, 'GV', 'K', 10000, 06109, '123/4', NULL, 2000, 2019),
( 2, 101, 'GV', 'B', 10000, 06109, '123/5', NULL, 2025, 2030);
INSERT INTO season (year, currency, min_kg_per_bs, max_kg_per_bs, penalty_per_kg, penalty_amount, penalty_none, start_date, end_date) VALUES
(2020, 'EUR', 1000, 2000, NULL, NULL, NULL, NULL, NULL),
(2021, 'EUR', 2000, 4000, NULL, NULL, NULL, NULL, NULL);
INSERT INTO modifier (year, modid, ordering, name, abs, rel, standard, quick_select) VALUES
(2020, 'S', 0, 'Geschädigte Trauben', NULL, -0.1, FALSE, FALSE),
(2020, 'A', 0, 'Keine Voranmeldung', -1000, NULL, FALSE, FALSE);
INSERT INTO delivery (mgnr, year, did, date, time, zwstid, lnr) VALUES
(101, 2020, 1, '2020-10-01', NULL, 'X', 1),
(101, 2020, 2, '2020-10-01', NULL, 'X', 2),
(101, 2020, 3, '2020-10-01', NULL, 'X', 3);
INSERT INTO delivery_part (year, did, dpnr, sortid, attrid, weight, kmw, qualid, hkid, kgnr, gerebelt, manual_weighing, spl_check, scale_id, weighing_id, weighing_reason) VALUES
(2020, 1, 1, 'GV', 'K', 4000, 17, 'KAB', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2020, 1, 2, 'GV', NULL, 4000, 16, 'QUW', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2020, 2, 1, 'GV', 'B', 4000, 15, 'QUW', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2020, 2, 2, 'GV', 'B', 4000, 16, 'QUW', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2020, 3, 1, 'GV', NULL, 500, 15, 'WEI', 'OEST', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2020, 3, 2, 'GV', NULL, 500, 14, 'LDW', 'WLXX', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL);
INSERT INTO delivery_part_modifier (year, did, dpnr, modid) VALUES
(2020, 1, 2, 'S');

View File

@ -1 +1 @@
curl -s "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=13" -u "elwig:ganzGeheim123!" -o "Resources\Create.sql"