Compare commits

...

13 Commits

38 changed files with 781 additions and 307 deletions
+5
View File
@@ -119,6 +119,11 @@ main h1 {
font-size: 10pt;
}
.main-wrapper p.custom {
white-space: pre-wrap;
break-inside: avoid;
}
.main-wrapper .hidden {
break-before: avoid;
}
+3 -3
View File
@@ -11,7 +11,7 @@ namespace Elwig.Documents {
public PaymentMember? Payment;
public Credit? Credit;
public CreditNoteData Data;
public CreditNoteDeliveryData Data;
public string? Text;
public string CurrencySymbol;
public int Precision;
@@ -20,7 +20,7 @@ namespace Elwig.Documents {
public decimal MemberTotalUnderDelivery;
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) {
UseBillingAddress = true;
ShowDateAndLocation = true;
@@ -50,7 +50,7 @@ namespace Elwig.Documents {
$"<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>" +
$"</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);
CurrencySymbol = season.Currency.Symbol ?? season.Currency.Code;
Precision = season.Precision;
+6
View File
@@ -158,4 +158,10 @@
}
</tbody>
</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>
+2 -2
View File
@@ -10,11 +10,11 @@ namespace Elwig.Documents {
public new static string Name => "Anlieferungsbestätigung";
public Season Season;
public DeliveryConfirmationData Data;
public DeliveryConfirmationDeliveryData Data;
public string? Text = App.Client.TextDeliveryConfirmation;
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) {
Season = ctx.Seasons.Find(year) ?? throw new ArgumentException("invalid season");
ShowDateAndLocation = true;
+2 -2
View File
@@ -92,9 +92,9 @@
</table>
@Raw(BusinessDocument.PrintSortenaufteilung(Model.MemberBuckets))
@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) {
<p class="comment" style="white-space: pre-wrap; break-inside: avoid;">@Model.Text</p>
<p class="custom comment">@Model.Text</p>
}
</div>
</main>
+1 -1
View File
@@ -7,7 +7,7 @@
<UseWPF>true</UseWPF>
<PreserveCompilationContext>true</PreserveCompilationContext>
<ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon>
<Version>0.6.1</Version>
<Version>0.6.3</Version>
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
</PropertyGroup>
+1
View File
@@ -58,6 +58,7 @@ namespace Elwig.Helpers {
public DbSet<OverUnderDeliveryRow> OverUnderDeliveryRows { get; private set; }
public DbSet<AreaComUnderDeliveryRowSingle> AreaComUnderDeliveryRows { get; private set; }
public DbSet<MemberDeliveryPerVariantRowSingle> MemberDeliveryPerVariantRows { get; private set; }
public DbSet<CreditNoteDeliveryRowSingle> CreditNoteDeliveryRows { get; private set; }
public DbSet<CreditNoteRowSingle> CreditNoteRows { get; private set; }
private readonly StreamWriter? LogFile = null;
+1 -1
View File
@@ -9,7 +9,7 @@ namespace Elwig.Helpers {
public static class AppDbUpdater {
// Don't forget to update value in Tests/fetch-resources.bat!
public static readonly int RequiredSchemaVersion = 13;
public static readonly int RequiredSchemaVersion = 14;
private static int VersionOffset = 0;
+7 -25
View File
@@ -43,11 +43,10 @@ namespace Elwig.Helpers.Billing {
ROUND(p.amount / POW(10, s.precision - 2)) AS net_amount,
ROUND(lp.amount / POW(10, s.precision - 2)) AS prev_amount,
IIF(m.buchführend, s.vat_normal, s.vat_flatrate) AS vat,
ROUND(
IIF({Data.ConsiderContractPenalties}, COALESCE(u.total_penalty, 0) / POW(10, 4 - 2), 0) +
IIF({Data.ConsiderTotalPenalty}, COALESCE(b.total_penalty, 0), 0) +
IIF({Data.ConsiderAutoBusinessShares}, -COALESCE(a.business_shares * s.bs_value, 0), 0) / POW(10, s.precision - 2)
) AS modifiers,
ROUND(IIF({Data.ConsiderContractPenalties}, COALESCE(u.total_penalty, 0), 0) / POW(10, 4 - 2)) +
ROUND(IIF({Data.ConsiderTotalPenalty}, COALESCE(b.total_penalty, 0), 0) / POW(10, s.precision - 2)) +
ROUND(IIF({Data.ConsiderAutoBusinessShares}, -COALESCE(a.total_amount, 0), 0) / POW(10, s.precision - 2))
AS modifiers,
lc.modifiers AS prev_modifiers
FROM season s
JOIN payment_variant v ON v.year = s.year
@@ -63,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 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 (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) 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
LEFT JOIN v_penalty_area_commitments u ON (u.year, u.mgnr) = (s.year, m.mgnr)
LEFT JOIN v_penalty_business_shares b ON (b.year, b.mgnr) = (s.year, m.mgnr)
LEFT JOIN v_auto_business_shares a ON (a.year, a.mgnr) = (s.year, m.mgnr)
WHERE s.year = {Year} AND v.avnr = {AvNr};
UPDATE payment_variant SET test_variant = FALSE WHERE (year, avnr) = ({Year}, {AvNr});
+4
View File
@@ -60,6 +60,7 @@ namespace Elwig.Helpers {
public string? TextDeliveryNote;
public string? TextDeliveryConfirmation;
public string? TextCreditNote;
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;
TextDeliveryConfirmation = parameters.GetValueOrDefault("TEXT_DELIVERYCONFIRMATION");
if (TextDeliveryConfirmation == "") TextDeliveryConfirmation = null;
TextCreditNote = parameters.GetValueOrDefault("TEXT_CREDITNOTE");
if (TextCreditNote == "") TextCreditNote = null;
} catch {
throw new KeyNotFoundException();
}
@@ -133,6 +136,7 @@ namespace Elwig.Helpers {
("DOCUMENT_SENDER", Sender2),
("TEXT_DELIVERYNOTE", TextDeliveryNote),
("TEXT_DELIVERYCONFIRMATION", TextDeliveryConfirmation),
("TEXT_CREDITNOTE", TextCreditNote),
};
}
+9 -8
View File
@@ -108,19 +108,19 @@ namespace Elwig.Helpers.Export {
<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: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"/>
<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"/>
<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"/>
<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"/>
<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"/>
<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"/>
<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"/>
</office:automatic-styles>
<office:body>
@@ -262,13 +262,14 @@ namespace Elwig.Helpers.Export {
string c;
if (data == null) {
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) {
double v = double.Parse(data?.ToString() ?? "0"); // use default culture for ToString and Parse()!
if (units != null && units.Length > 0) {
int n = -1;
switch (units[0]) {
case "%": n = 1; data = $"{v:N1}"; break;
case "€": n = 2; data = $"{v:N2}"; break;
case "°KMW": n = 1; data = $"{v:N1}"; break;
case "°Oe": n = 0; data = $"{v:N0}"; break;
}
+1 -1
View File
@@ -163,7 +163,7 @@ namespace Elwig.Helpers {
}
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) {
+2 -3
View File
@@ -1,5 +1,4 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
@@ -8,7 +7,7 @@ using System.Threading.Tasks;
namespace Elwig.Models.Dtos {
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),
("Name", "Name", null, 40),
("GivenName", "Vorname", null, 40),
@@ -20,7 +19,7 @@ namespace Elwig.Models.Dtos {
("DeliveryObligations", "Lieferpflicht", "kg", 22),
("Weights", "Geliefert", "kg", 22),
("UnderDeliveries", "Unterliefert", "kg|%", 34),
};
];
public AreaComUnderDeliveryData(IEnumerable<AreaComUnderDeliveryRow> rows, int year) :
base($"Unterlieferungen FB", $"Unterlieferungen laut Flächenbindungen {year}", rows, FieldNames) {
+149 -128
View File
@@ -1,159 +1,180 @@
using Elwig.Models.Entities;
using Elwig.Helpers;
using Elwig.Helpers.Billing;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.Core.Common.CommandTrees.ExpressionBuilder;
using System.Linq;
using System.Threading.Tasks;
namespace Elwig.Models.Dtos {
public class CreditNoteData : DataTable<CreditNoteRow> {
private static readonly (string, string, string?)[] FieldNames = new[] {
("", "", (string?)null), // TODO
};
private static readonly (string, string, string?, int)[] FieldNames = [
("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;
private readonly int? TgNr;
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 CreditNoteData(IEnumerable<CreditNoteRow> rows, int year, string name) :
base($"Buchungsliste", $"Buchungsliste {name} {year}", rows, FieldNames) {
}
public static async Task<IDictionary<int, CreditNoteData>> ForPaymentVariant(DbSet<CreditNoteRowSingle> 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 CreditNoteRow(g, seasons))
.GroupBy(
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);
public static async Task<CreditNoteData> ForPaymentVariant(AppDbContext ctx, int year, int avnr) {
var variant = await ctx.PaymentVariants.FindAsync(year, avnr);
var name = variant!.Name;
var data = BillingData.FromJson(variant!.Data);
var rows = (await FromDbSet(ctx.CreditNoteRows, year, avnr)).Select(r => new CreditNoteRow(r, data)).ToList();
return new CreditNoteData(rows, year, name);
}
private static async Task<IEnumerable<CreditNoteRowSingle>> FromDbSet(DbSet<CreditNoteRowSingle> 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
private static async Task<IEnumerable<CreditNoteRowSingle>> FromDbSet(DbSet<CreditNoteRowSingle> table, int year, int avnr) {
return await table.FromSql($"""
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,
p.amount - p.net_amount AS surcharge,
c.net_amount, c.prev_net_amount, c.vat, c.vat_amount, c.gross_amount, c.modifiers, c.prev_modifiers, c.amount,
ROUND(COALESCE(u.total_penalty, 0) / POW(10, 4 - 2)) AS fb_penalty,
ROUND(COALESCE(b.total_penalty, 0) / POW(10, s.precision - 2)) AS bs_penalty,
ROUND(COALESCE(a.total_amount, 0) / POW(10, s.precision - 2)) AS auto_bs
FROM credit c
LEFT JOIN member m ON m.mgnr = c.mgnr
LEFT JOIN payment_member p ON (p.year, p.avnr, p.mgnr) = (c.year, c.avnr, c.mgnr)
LEFT JOIN AT_plz_dest p ON p.id = m.postal_dest
LEFT JOIN AT_ort o ON o.okz = p.okz
LEFT JOIN season s ON s.year = c.year
LEFT JOIN v_penalty_area_commitments u ON (u.year, u.mgnr) = (s.year, m.mgnr)
LEFT JOIN v_penalty_business_shares b ON (b.year, b.mgnr) = (s.year, m.mgnr)
LEFT JOIN v_auto_business_shares a ON (a.year, a.mgnr) = (s.year, m.mgnr)
WHERE c.year = {year} AND c.avnr = {avnr}
ORDER BY m.mgnr
""").ToListAsync();
}
}
public class CreditNoteRow {
public int Year;
public int? TgNr;
public int AvNr;
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 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 CreditNoteRow(IEnumerable<CreditNoteRowSingle> 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;
public CreditNoteRow(CreditNoteRowSingle row, BillingData data) {
byte prec1 = 2, prec2 = row.Precision;
MgNr = row.MgNr;
Name = row.Name;
GivenName = row.GivenName;
Address = row.Address;
Plz = row.Plz;
Locality = row.Locality;
Iban = Utils.FormatIban(row.Iban);
TgNr = $"{row.Year}/{row.TgNr}";
Total = Utils.DecFromDb(row.NetAmount, prec1);
Surcharge = (row.Surcharge == null || row.Surcharge == 0) ? null : Utils.DecFromDb((long)row.Surcharge, prec2);
Sum = Total - (Surcharge ?? 0);
ConsideredSum = (row.PrevNetAmount == null ||row.PrevNetAmount == 0) ? null : -Utils.DecFromDb((long)row.PrevNetAmount, prec1);
Net = Total + (ConsideredSum ?? 0);
if (row.Vat == 0.10) {
Vat1 = Utils.DecFromDb(row.VatAmount, prec1);
} else if (row.Vat == 0.13) {
Vat2 = Utils.DecFromDb(row.VatAmount, prec1);
}
decimal mod = (row.Modifiers == null) ? 0 : Utils.DecFromDb((long)row.Modifiers, prec1);
if (data.ConsiderContractPenalties)
Penalties = (row.FbPenalty == null || row.FbPenalty == 0) ? null : Utils.DecFromDb((long)row.FbPenalty, prec1);
if (data.ConsiderTotalPenalty)
Penalty = (row.BsPealty == null || row.BsPealty == 0) ? null : Utils.DecFromDb((long)row.BsPealty, prec1);
if (data.ConsiderAutoBusinessShares)
AutoBs = (row.AutoBs == null || row.AutoBs == 0) ? null : -Utils.DecFromDb((long)row.AutoBs, prec1);
mod -= (Penalties ?? 0) + (Penalty ?? 0) + (AutoBs ?? 0);
Others = (mod == 0) ? null : mod;
Gross = Utils.DecFromDb(row.GrossAmount, prec1);
Considered = (row.PrevModifiers == null || row.PrevModifiers == 0) ? null : -Utils.DecFromDb((long)row.PrevModifiers, prec1);
Amount = Utils.DecFromDb(row.Amount, prec1);
}
}
[Keyless]
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")]
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("family_name")]
public string Name { get; set; }
[Column("given_name")]
public string GivenName { get; set; }
[Column("address")]
public string Address { get; set; }
[Column("plz")]
public int Plz { get; set; }
[Column("ort")]
public string LocalityFull { get; set; }
[NotMapped]
public string Locality => LocalityFull.Split(",")[0];
[Column("iban")]
public string Iban { get; set; }
[Column("year")]
public int Year { get; set; }
[Column("precision")]
public byte Precision { get; set; }
[Column("tgnr")]
public string TgNr { get; set; }
[Column("surcharge")]
public long? Surcharge { 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; }
public long NetAmount { get; set; }
[Column("prev_net_amount")]
public long? PrevNetAmount { get; set; }
[Column("vat")]
public double Vat { get; set; }
[Column("vat_amount")]
public long VatAmount { get; set; }
[Column("gross_amount")]
public long GrossAmount { get; set; }
[Column("modifiers")]
public long? Modifiers { get; set; }
[Column("prev_modifiers")]
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; }
}
}
+159
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; }
}
}
@@ -5,9 +5,9 @@ using System.Linq;
using System.Threading.Tasks;
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),
("DPNr", "Pos.", null, 8),
("Variety", "Sorte", null, 40),
@@ -17,30 +17,30 @@ namespace Elwig.Models.Dtos {
("Gradation", "Gradation", "°Oe|°KMW", 32),
("Buckets", "Flächenbindung", "|kg", 36),
("Weight", "Gewicht", "kg", 16),
};
];
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) {
MgNr = m.MgNr;
}
public static DeliveryConfirmationData CreateEmpty(int year, Member m) {
public static DeliveryConfirmationDeliveryData CreateEmpty(int year, Member 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))
.GroupBy(
p => p.Delivery.Member,
p => new DeliveryConfirmationRow(p),
(k, g) => new DeliveryConfirmationData(g, year, k)
p => new DeliveryConfirmationDeliveryRow(p),
(k, g) => new DeliveryConfirmationDeliveryData(g, year, k)
).ToDictionary(d => d.MgNr, d => d);
}
public static async Task<DeliveryConfirmationData> ForMember(DbSet<DeliveryPart> table, int year, Member m) {
return new DeliveryConfirmationData((await FromDbSet(table, year, m.MgNr)).Select(p => new DeliveryConfirmationRow(p)), year, m);
public static async Task<DeliveryConfirmationDeliveryData> ForMember(DbSet<DeliveryPart> table, int year, Member 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) {
@@ -68,7 +68,7 @@ namespace Elwig.Models.Dtos {
}
}
public class DeliveryConfirmationRow {
public class DeliveryConfirmationDeliveryRow {
public string LsNr;
public int DPNr;
public string Variety;
@@ -79,7 +79,7 @@ namespace Elwig.Models.Dtos {
public int Weight;
public (string Name, int Value)[] Buckets;
public DeliveryConfirmationRow(DeliveryPart p) {
public DeliveryConfirmationDeliveryRow(DeliveryPart p) {
var d = p.Delivery;
LsNr = d.LsNr;
DPNr = p.DPNr;
@@ -8,7 +8,7 @@ using System.Threading.Tasks;
namespace Elwig.Models.Dtos {
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),
("Name", "Name", null, 40),
("GivenName", "Vorname", null, 40),
@@ -20,7 +20,7 @@ namespace Elwig.Models.Dtos {
("Weights", "Geliefert", "kg", 22),
("Areas", "Fläche", "m²", 22),
("Yields", "Ertrag", "kg/ha", 22),
};
];
public MemberDeliveryPerVariantData(IEnumerable<MemberDeliveryPerVariantRow> rows, int year) :
+2 -2
View File
@@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace Elwig.Models.Dtos {
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),
("Name", "Name", null, 40),
("GivenName", "Vorname", null, 40),
@@ -19,7 +19,7 @@ namespace Elwig.Models.Dtos {
("DeliveryRight", "Lieferrecht", "kg", 22),
("Weight", "Geliefert", "kg", 22),
("OverUnderDelivery", "Über-/Unterliefert", "kg|%", 34),
};
];
public OverUnderDeliveryData(IEnumerable<OverUnderDeliveryRow> rows, int year) :
base($"Über-Unterlieferungen", $"Über- und Unterlieferungen laut gezeichneten Geschäftsanteilen {year}", rows, FieldNames) {
+1 -1
View File
@@ -1,4 +1,4 @@
-- schema version 11 to 12
-- schema version 12 to 13
ALTER TABLE season ADD COLUMN bs_value INTEGER;
+33
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;
+7 -4
View File
@@ -535,15 +535,18 @@
</GroupBox>
<GroupBox Header="Anlieferungsbestätigung" Margin="10,10,10,10" Height="250">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="TextElementDeliveryConfirmation" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible" AcceptsReturn="True"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10,10,10,10" Height="Auto"
TextChanged="TextBox_TextChanged"/>
</Grid>
</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>
</ScrollViewer>
</TabItem>
+6 -4
View File
@@ -12,11 +12,11 @@ namespace Elwig.Windows {
public BaseDataWindow() {
InitializeComponent();
RequiredInputs = new Control[] {
RequiredInputs = [
ClientNameInput, ClientNameTypeInput, ClientNameTokenInput, ClientNameShortInput,
ClientAddressInput, ClientPlzInput, ClientOrtInput,
};
ExemptInputs = new Control[] {
];
ExemptInputs = [
ClientNameFull,
BranchIdInput, BranchNameInput, BranchPlzInput, BranchOrtInput,
BranchAddressInput, BranchPhoneNrInput, BranchFaxNrInput, BranchMobileNrInput,
@@ -30,7 +30,7 @@ namespace Elwig.Windows {
SeasonMinKgPerBsInput.TextBox, SeasonMaxKgPerBsInput.TextBox, SeasonBsValueInput.TextBox,
SeasonPenaltyPerKgInput.TextBox, SeasonPenaltyInput.TextBox, SeasonPenaltyNoneInput.TextBox,
SeasonModifierIdInput, SeasonModifierNameInput, SeasonModifierRelInput.TextBox, SeasonModifierAbsInput.TextBox,
};
];
WineAttributeFillLowerInput.Visibility = Visibility.Hidden;
WineAttributeFillLowerLabel.Visibility = Visibility.Hidden;
}
@@ -297,6 +297,7 @@ namespace Elwig.Windows {
case 3: ModeDeliveryNoteFull.IsChecked = true; break;
}
TextElementDeliveryConfirmation.Text = p.TextDeliveryConfirmation;
TextElementCreditNote.Text = p.TextCreditNote;
FinishInputFilling();
}
@@ -322,6 +323,7 @@ namespace Elwig.Windows {
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.TextDeliveryConfirmation = TextElementDeliveryConfirmation.Text.Length > 0 ? TextElementDeliveryConfirmation.Text : null;
p.TextCreditNote = TextElementCreditNote.Text.Length > 0 ? TextElementCreditNote.Text : null;
await p.UpdateValues();
}
+2 -2
View File
@@ -101,12 +101,12 @@
Filtern nach:<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>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>Mitglied</Bold>: z.B. 1234, 987, ...<LineBreak/>
<Bold>Saison</Bold>: z.B. 2020, &gt;2015, 2017-2019, &lt;2005, 2019-, ...<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>Uhrzeit</Bold>: z.B. 06:00-08:00, 18:00-, ...<LineBreak/>
<Bold>Freitext</Bold>: z.B. Lieferscheinnummern, Anmerkung, "quw" (sucht nach dem Text "quw")
+14 -4
View File
@@ -365,13 +365,23 @@ namespace Elwig.Windows {
filter.RemoveAt(i--);
filterNames.Add("außer " + var[e[1..].ToUpper()].Name);
} else if (e.Length == 3 && qual.ContainsKey(e.ToUpper())) {
filterQual.Add(e.ToUpper());
var qualId = e.ToUpper();
filterQual.Add(qualId);
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())) {
filterNotQual.Add(e[1..].ToUpper());
var qualId = e[1..].ToUpper();
filterNotQual.Add(qualId);
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)) {
filterMgNr.Add(int.Parse(e));
filter.RemoveAt(i--);
@@ -74,9 +74,9 @@ namespace Elwig.Dialogs {
}
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 =>
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
}
));
-5
View File
@@ -27,11 +27,6 @@ namespace Elwig.Windows {
w.Show();
}
private void MemberListButton_Click(object sender, RoutedEventArgs evt) {
var w = new MemberListWindow();
w.Show();
}
private void ReceiptButton_Click(object sender, RoutedEventArgs evt) {
App.FocusReceipt();
}
+30 -4
View File
@@ -2,8 +2,6 @@
x:Class="Elwig.Windows.MemberAdminWindow"
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"
Title="Mitglieder - Elwig" Height="700" Width="1250" MinHeight="650" MinWidth="1150"
Loaded="Window_Loaded">
@@ -239,14 +237,42 @@
<ColumnDefinition Width="2*"/>
</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"
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"
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"/>
<TextBox x:Name="PhoneNr1Input" Margin="0,70,5,0" Grid.Column="1"
TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/>
+91 -29
View File
@@ -17,7 +17,8 @@ namespace Elwig.Windows {
public partial class MemberAdminWindow : AdministrationWindow {
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 CtrlP = new("CtrlP", typeof(MemberAdminWindow), [new KeyGesture(Key.P, ModifierKeys.Control)]);
@@ -38,6 +39,17 @@ namespace Elwig.Windows {
AddressInput, PlzInput, OrtInput, BillingOrtInput,
BusinessSharesInput, BranchInput, DefaultKgInput
];
EmailAddressInputs = [
(EmailAddress1Label, EmailAddress1Input),
(EmailAddress2Label, EmailAddress2Input),
(EmailAddress3Label, EmailAddress3Input),
(EmailAddress4Label, EmailAddress4Input),
(EmailAddress5Label, EmailAddress5Input),
(EmailAddress6Label, EmailAddress6Input),
(EmailAddress7Label, EmailAddress7Input),
(EmailAddress8Label, EmailAddress8Input),
(EmailAddress9Label, EmailAddress9Input),
];
PhoneNrInputs = [
(PhoneNr1TypeInput, PhoneNr1Input, PhoneNr1CommentInput),
(PhoneNr2TypeInput, PhoneNr2Input, PhoneNr2CommentInput),
@@ -63,7 +75,7 @@ namespace Elwig.Windows {
Menu_Print_MemberDataSheet.IsEnabled = App.IsPrintingReady;
ActiveMemberInput.IsChecked = true;
UpdatePhoneNrInputVisibility();
UpdateContactInfoVisibility();
LockInputs();
}
@@ -236,27 +248,55 @@ namespace Elwig.Windows {
private void SetPhoneNrInput(int nr, string? type, string? number, string? comment) {
var inputs = PhoneNrInputs[nr];
inputs.Item1.SelectedItem = (type == null) ? null : inputs.Item1.ItemsSource.Cast<KeyValuePair<string, string>>().FirstOrDefault(p => p.Key == type);
inputs.Item2.Text = number;
inputs.Item3.Text = comment;
inputs.Type.SelectedItem = (type == null) ? null : inputs.Type.ItemsSource.Cast<KeyValuePair<string, string>>().FirstOrDefault(p => p.Key == type);
inputs.Number.Text = number;
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) {
var inputs = PhoneNrInputs[nr];
var number = inputs.Item2.Text;
var number = inputs.Number.Text;
if (string.IsNullOrEmpty(number))
return null;
var type = (inputs.Item1.SelectedItem as KeyValuePair<string, string>?)?.Key ?? (number.StartsWith("+43 ") && number[4] == '6' ? "mobile" : "landline");
var comment = inputs.Item3.Text;
var type = (inputs.Type.SelectedItem as KeyValuePair<string, string>?)?.Key ?? (number.StartsWith("+43 ") && number[4] == '6' ? "mobile" : "landline");
var comment = inputs.Comment.Text;
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];
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;
inputs.Item1.Visibility = vis;
inputs.Item2.Visibility = vis;
inputs.Item3.Visibility = vis;
inputs.Type.Visibility = vis;
inputs.Number.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) {
@@ -274,7 +314,7 @@ namespace Elwig.Windows {
HideNewEditDeleteButtons();
ShowSaveResetCancelButtons();
UnlockInputs();
UpdatePhoneNrInputVisibility(true);
UpdateContactInfoVisibility(true);
InitInputs();
LockSearchInputs();
}
@@ -289,7 +329,7 @@ namespace Elwig.Windows {
HideNewEditDeleteButtons();
ShowSaveResetCancelButtons();
UnlockInputs();
UpdatePhoneNrInputVisibility(true);
UpdateContactInfoVisibility(true);
LockSearchInputs();
}
@@ -315,7 +355,7 @@ namespace Elwig.Windows {
HideSaveResetCancelButtons();
ShowNewEditDeleteButtons();
LockInputs();
UpdatePhoneNrInputVisibility();
UpdateContactInfoVisibility();
UnlockSearchInputs();
FinishInputFilling();
await RefreshMemberList();
@@ -342,7 +382,7 @@ namespace Elwig.Windows {
ShowNewEditDeleteButtons();
RefreshInputs();
LockInputs();
UpdatePhoneNrInputVisibility();
UpdateContactInfoVisibility();
UnlockSearchInputs();
}
@@ -504,13 +544,24 @@ namespace Elwig.Windows {
ActiveMemberInput.IsEnabled = true;
}
private void UpdatePhoneNrInputVisibility(bool extra = false) {
bool lastVisible = true;
var m = (Member)MemberList.SelectedItem;
private void UpdateContactInfoVisibility(bool extra = false) {
var m = MemberList.SelectedItem as Member;
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++) {
var input = PhoneNrInputs[i];
var vis = !string.IsNullOrEmpty(input.Item2.Text) || (m?.TelephoneNumbers.Any(p => p.Nr - 1 == i) ?? false);
SetPhoneNrInputVisible(i, vis || (extra && lastVisible));
var vis = !string.IsNullOrEmpty(input.Number.Text) || (m?.TelephoneNumbers.Any(n => n.Nr - 1 == i) ?? false);
var cVis = vis || (extra && lastVisible);
SetPhoneNrInputVisible(i, cVis, cVis ? num++ : null);
lastVisible = vis;
}
}
@@ -605,17 +656,17 @@ namespace Elwig.Windows {
}
}
for (int i = 0; i < 2; i++) {
var input = i == 0 ? EmailAddress1Input : EmailAddress2Input;
for (int i = 0; i < EmailAddressInputs.Length; i++) {
var input = GetEmailAddressInput(i);
var emailAddr = m.EmailAddresses.FirstOrDefault(a => a.Nr - 1 == i);
if (input.Text == "") {
if (input == null || input == "") {
if (emailAddr != null) {
Context.Remove(emailAddr);
}
} else {
MemberEmailAddr a = emailAddr ?? Context.CreateProxy<MemberEmailAddr>();
a.Nr = i + 1;
a.Address = input.Text;
a.Address = input ?? "";
a.Comment = null;
if (emailAddr == null) {
a.MgNr = newMgNr;
@@ -678,8 +729,14 @@ namespace Elwig.Windows {
}
var emailAddrs = m.EmailAddresses.OrderBy(a => a.Nr).ToList();
EmailAddress1Input.Text = emailAddrs.Count > 0 ? emailAddrs[0].Address : "";
EmailAddress2Input.Text = emailAddrs.Count > 1 ? emailAddrs[1].Address : "";
for (int i = 0; i< EmailAddressInputs.Length; i++) {
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();
for (int i = 0; i < PhoneNrInputs.Length; i++) {
@@ -690,7 +747,7 @@ namespace Elwig.Windows {
SetPhoneNrInput(i, null, null, null);
}
}
UpdatePhoneNrInputVisibility(IsEditing || IsCreating);
UpdateContactInfoVisibility(IsEditing || IsCreating);
IbanInput.Text = m.Iban;
BicInput.Text = m.Bic;
@@ -786,9 +843,14 @@ namespace Elwig.Windows {
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) {
base.PhoneNrInput_TextChanged(sender, evt);
UpdatePhoneNrInputVisibility(IsEditing || IsCreating);
UpdateContactInfoVisibility(IsEditing || IsCreating);
}
private void KgDetailsButton_Click(object sender, RoutedEventArgs evt) {
-22
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>
-27
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();
}
}
}
+5 -2
View File
@@ -6,7 +6,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Elwig.Windows"
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>
<Style TargetType="Label">
<Setter Property="HorizontalAlignment" Value="Left"/>
@@ -42,7 +42,7 @@
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="200"/>
<RowDefinition Height="220"/>
</Grid.RowDefinitions>
<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"
Click="ExportButton_Click"
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"
VerticalAlignment="Bottom" HorizontalAlignment="Left"/>
+28 -1
View File
@@ -57,6 +57,7 @@ namespace Elwig.Windows {
ShowButton.IsEnabled = true;
PrintButton.IsEnabled = true;
ExportButton.IsEnabled = locked;
TransactionButton.IsEnabled = locked;
NameInput.Text = v.Name;
NameInput.IsReadOnly = false;
@@ -101,6 +102,7 @@ namespace Elwig.Windows {
ShowButton.IsEnabled = false;
PrintButton.IsEnabled = false;
ExportButton.IsEnabled = false;
TransactionButton.IsEnabled = false;
BillingData = null;
NameInput.Text = "";
@@ -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) {
if (PaymentVariantList.SelectedItem is not PaymentVar v || BillingData == null) return;
try {
@@ -488,7 +515,7 @@ namespace Elwig.Windows {
members = members.OrderBy(m => m.MgNr);
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);
await Context.GetMemberAreaCommitmentBuckets(Year, 0);
using var doc = Document.Merge(list.Select(m =>
+1 -1
View File
@@ -48,7 +48,7 @@ namespace Elwig.Windows {
private async void ZipButton_Click(object sender, RoutedEventArgs evt) {
using var ctx = new AppDbContext();
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)));
}
}
}
+6
View File
@@ -1,4 +1,5 @@
using Elwig.Helpers;
using Elwig.Helpers.Billing;
using Microsoft.Data.Sqlite;
using System.Reflection;
@@ -16,6 +17,11 @@ namespace Tests {
await AppDbContext.ExecuteEmbeddedScript(Connection, Assembly.GetExecutingAssembly(), "Tests.Resources.Insert.sql");
}
[OneTimeSetUp]
public async Task SetupBillingData() {
await BillingData.Init();
}
[OneTimeTearDown]
public async Task TeardownDatabase() {
AppDbContext.ConnectionStringOverride = null;
-5
View File
@@ -10,11 +10,6 @@ namespace Tests.HelperTests {
private static readonly JsonSerializerOptions JsonOpts = new() { WriteIndented = true };
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) {
return (bucket[..2], bucket.Length > 2 ? bucket[2..] : null);
}
+117 -2
View File
@@ -1,4 +1,5 @@
using Elwig.Helpers;
using Elwig.Helpers.Billing;
using Microsoft.Data.Sqlite;
using System.Reflection;
@@ -6,6 +7,9 @@ namespace Tests.HelperTests {
[TestFixture]
public class BillingTest {
private const int Year1 = 2020, Year2 = 2020;
private const int MgNr1 = 101, MgNr2 = 102, MgNr3 = 103, MgNr4 = 104;
private SqliteConnection? Connection;
[OneTimeSetUp]
@@ -22,9 +26,120 @@ namespace Tests.HelperTests {
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]
public void Test() {
// TODO
public async Task Test_01_NoActiveAreaCommitments() {
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));
});
}
}
}
+11 -1
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;
+59 -1
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');