[] CreditNote: Overhaul CreditNote

This commit is contained in:
2023-12-22 15:30:39 +01:00
parent 82f93746ab
commit b9a2893d80
10 changed files with 226 additions and 79 deletions

@ -5,6 +5,7 @@ using Elwig.Models.Entities;
namespace Elwig.Documents { namespace Elwig.Documents {
public class CreditNote : BusinessDocument { public class CreditNote : BusinessDocument {
public PaymentMember? Payment;
public Credit? Credit; public Credit? Credit;
public CreditNoteData Data; public CreditNoteData Data;
public string? Text; public string? Text;
@ -16,6 +17,7 @@ namespace Elwig.Documents {
UseBillingAddress = true; UseBillingAddress = true;
ShowDateAndLocation = true; ShowDateAndLocation = true;
Data = data; Data = data;
Payment = p;
Credit = p.Credit; Credit = p.Credit;
Aside = Aside.Replace("</table>", "") + Aside = Aside.Replace("</table>", "") +
$"<thead><tr><th colspan='2'>Gutschrift</th></tr></thead><tbody>" + $"<thead><tr><th colspan='2'>Gutschrift</th></tr></thead><tbody>" +

@ -1,3 +1,4 @@
@using Elwig.Helpers
@using RazorLight @using RazorLight
@inherits TemplatePage<Elwig.Documents.CreditNote> @inherits TemplatePage<Elwig.Documents.CreditNote>
@model Elwig.Documents.CreditNote @model Elwig.Documents.CreditNote
@ -8,84 +9,89 @@
<table class="credit"> <table class="credit">
<colgroup> <colgroup>
<col style="width: 25mm;"/> <col style="width: 25mm;"/>
<col style="width: 5mm;"/> <col style="width: 5mm;"/>
<col style="width: 20mm;"/>
<col style="width: 20mm;"/> <col style="width: 20mm;"/>
<col style="width: 18mm;"/>
<col style="width: 10mm;"/> <col style="width: 10mm;"/>
<col style="width: 10mm;"/> <col style="width: 10mm;"/>
<col style="width: 15mm;"/> <col style="width: 15mm;"/>
<col style="width: 12mm;"/> <col style="width: 12mm;"/>
<col style="width: 15mm;"/> <col style="width: 15mm;"/>
<col style="width: 10mm;"/> <col style="width: 17mm;"/>
<col style="width: 10mm;"/> <col style="width: 16mm;"/>
<col style="width: 15mm;"/>
</colgroup> </colgroup>
<thead> <thead>
<tr> <tr>
<th rowspan="3" style="text-align: left;">Lieferschein-Nr.</th> <th rowspan="2" style="text-align: left;">Lieferschein-Nr.</th>
<th rowspan="3">Pos.</th> <th rowspan="2" class="narrow">Pos.</th>
<th rowspan="3" style="text-align: left;">Sorte</th> <th rowspan="2" style="text-align: left;">Sorte</th>
<th rowspan="3" style="text-align: left;">Attribut</th> <th rowspan="2" style="text-align: left;">Attribut</th>
<th rowspan="2" colspan="2">Gradation</th> <th colspan="2">Gradation</th>
<th rowspan="2" colspan="2">Flächenbindung</th> <th colspan="2">Flächenbindung</th>
<th rowspan="2">Preis</th> <th>Preis</th>
<th colspan="2">Zu-/Abschläge</th> <th class="narrow">Zu-/Abschläge</th>
<th rowspan="2">Betrag</th> <th>Betrag</th>
</tr> </tr>
<tr> <tr>
<th>Rel.</th> <th class="unit">[°Oe]</th>
<th>Abs.</th> <th class="unit narrow">[°KMW]</th>
</tr> <th class="unit" colspan="2">[kg]</th>
<tr> <th class="unit">[@Model.CurrencySymbol/kg]</th>
<th>[°Oe]</th> <th class="unit">[@Model.CurrencySymbol]</th>
<th>[°KMW]</th> <th class="unit">[@Model.CurrencySymbol]</th>
<th colspan="2">[kg]</th>
<th>[@Model.CurrencySymbol/kg]</th>
<th>[%]</th>
<th>[@Model.CurrencySymbol/kg]</th>
<th>[@Model.CurrencySymbol]</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@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;
//var pmt = p.Payment;
var abs = 0; // pmt?.ModAbs == null || pmt?.ModAbs == 0 ? "-" : pmt?.ModAbs.ToString("0." + string.Concat(Enumerable.Repeat('0', Model.Precision)));
var rel = 0; // pmt?.ModRel == null || pmt?.ModRel == 0 ? "-" : $"{pmt?.ModRel * 100:0.00##}";
@for (int i = 0; i < rows; i++) { @for (int i = 0; i < rows; i++) {
<tr class="@(first ? "first" : "") @(rows > i + 1 ? "trailing" : "")"> <tr class="@(first ? "first" : "") @(rows > i + 1 ? "last" : "")">
@if (first) { @if (first) {
<td rowspan="@rows" class="lsnr">@p.LsNr</td> <td rowspan="@rows">@p.LsNr</td>
<td rowspan="@rows" class="dpnr">@p.DPNr</td> <td rowspan="@rows">@p.DPNr</td>
<td class="variant small">@p.Variant</td> <td class="small">@p.Variant</td>
<td class="attribute small">@p.Attribute</td> <td class="small">@p.Attribute</td>
<td rowspan="@rows" class="oe">@($"{p.Gradation.Oe:N0}")</td> <td rowspan="@rows" class="center">@($"{p.Gradation.Oe:N0}")</td>
<td rowspan="@rows" class="kmw">@($"{p.Gradation.Kmw:N1}")</td> <td rowspan="@rows" class="center">@($"{p.Gradation.Kmw:N1}")</td>
} }
@if (i > 0 && i <= p.Modifiers.Length) { @if (i > 0 && i <= p.Modifiers.Length) {
<td colspan="2" class="mod">@(p.Modifiers[i - 1])</td> <td colspan="2" class="small mod">@p.Modifiers[i - 1]</td>
} else if (i > 0) { } else if (i > 0) {
<td colspan="2"></td> <td colspan="2"></td>
} }
@if (i < p.Buckets.Length) { @if (i < p.Buckets.Length) {
var bucket = p.Buckets[i]; var bucket = p.Buckets[i];
<td class="geb small">@bucket.Name:</td> <td class="small">@bucket.Name:</td>
<td class="weight">@($"{bucket.Value:N0}")</td> <td class="number">@($"{bucket.Value:N0}")</td>
<td class="price">@($"{bucket.Price:N4}")</td> <td class="number">@($"{bucket.Price:N4}")</td>
} else { } else {
<td colspan="3"></td> <td colspan="3"></td>
} }
@if (first) { @if (i == p.Buckets.Length - 1) {
<td rowspan="@rows" class="rel">@rel</td> var totalMod = p.TotalModifiers ?? 0;
<td rowspan="@rows" class="abs">@abs</td> <td class="number@(totalMod == 0 ? " center" : "")">@(totalMod == 0 ? "-" : Utils.GetSign(totalMod) + $"{Math.Abs(totalMod):N2}")</td>
<!-- FIXME rel/abs mods --> <td class="number">@($"{p.Amount:N2}")</td>
<td rowspan="@rows" class="amount">@($"{p.Buckets.Sum(b => b.Amount):N2}")</td> } else {
first = false; <td colspan="2"></td>
} }
</tr> </tr>
first = false;
} }
} }
@if (Model.Payment == null) {
<tr class="sum">
<td colspan="7"></td>
<td colspan="2">Gesamt:</td>
<td colspan="2" class="number">@($"{Model.Data.Rows.Sum(p => p.Amount):N2}")</td>
</tr>
} else {
<tr class="sum">
<td colspan="7"></td>
<td colspan="2">Gesamt:</td>
<td colspan="2" class="number">@($"{Model.Payment.NetAmount:N2}")</td>
</tr>
}
</tbody> </tbody>
</table> </table>
</main> </main>

@ -1,6 +1,7 @@
table.credit .amount.sum {
padding-bottom: 1mm; table.credit .mod {
padding-left: 5mm;
} }
table.credit tbody tr:not(.first):not(.last) { table.credit tbody tr:not(.first):not(.last) {
@ -8,10 +9,18 @@ table.credit tbody tr:not(.first):not(.last) {
break-after: avoid; break-after: avoid;
} }
table.credit tbody tr.first td { table.credit tr:not(.first) td {
padding-top: 1mm; padding-top: 0;
} }
table.credit tbody tr.last td { table.credit tr.last td {
padding-bottom: 1mm; padding-bottom: 0;
}
table.credit tr.sum {
font-size: 12pt;
}
table.credit tr.sum td {
padding-top: 1mm;
} }

@ -9,7 +9,7 @@
<table class="delivery-confirmation"> <table class="delivery-confirmation">
<colgroup> <colgroup>
<col style="width: 25mm;"/> <col style="width: 25mm;"/>
<col style="width: 5mm;"/> <col style="width: 5mm;"/>
<col style="width: 20mm;"/> <col style="width: 20mm;"/>
<col style="width: 21mm;"/> <col style="width: 21mm;"/>
<col style="width: 19mm;"/> <col style="width: 19mm;"/>
@ -35,7 +35,7 @@
<tr> <tr>
<th class="unit">[°Oe]</th> <th class="unit">[°Oe]</th>
<th class="unit narrow">[°KMW]</th> <th class="unit narrow">[°KMW]</th>
<th class="unit"colspan="2">[kg]</th> <th class="unit" colspan="2">[kg]</th>
<th class="unit">[kg]</th> <th class="unit">[kg]</th>
<th class="unit">[kg]</th> <th class="unit">[kg]</th>
</tr> </tr>
@ -48,7 +48,7 @@
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 ? "trailing" : "")"> <tr class="@(first ? "first" : "") @(p.Variant != lastVariant && lastVariant != "" ? "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>

@ -11,7 +11,7 @@ table.delivery-confirmation tr:not(.first) td {
padding-top: 0; padding-top: 0;
} }
table.delivery-confirmation tr.trailing td { table.delivery-confirmation tr.last td {
padding-bottom: 0; padding-bottom: 0;
} }

@ -1,17 +1,16 @@
using Microsoft.Data.Sqlite; using Microsoft.Data.Sqlite;
using System; using System;
using System.Windows;
namespace Elwig.Helpers { namespace Elwig.Helpers {
public static class AppDbUpdater { public static class AppDbUpdater {
public static readonly int RequiredSchemaVersion = 10; public static readonly int RequiredSchemaVersion = 11;
private static int _versionOffset = 0; private static int _versionOffset = 0;
private static readonly Action<SqliteConnection>[] _updaters = new[] { private static readonly Action<SqliteConnection>[] _updaters = new[] {
UpdateDbSchema_1_To_2, UpdateDbSchema_2_To_3, UpdateDbSchema_3_To_4, UpdateDbSchema_4_To_5, UpdateDbSchema_1_To_2, UpdateDbSchema_2_To_3, UpdateDbSchema_3_To_4, UpdateDbSchema_4_To_5,
UpdateDbSchema_5_To_6, UpdateDBSchema_6_To_7, UpdateDbSchema_7_To_8, UpdateDbSchema_8_To_9, UpdateDbSchema_5_To_6, UpdateDBSchema_6_To_7, UpdateDbSchema_7_To_8, UpdateDbSchema_8_To_9,
UpdateDbSchema_9_To_10, UpdateDbSchema_9_To_10, UpdateDbSchema_10_To_11
}; };
private static void ExecuteNonQuery(SqliteConnection cnx, string sql) { private static void ExecuteNonQuery(SqliteConnection cnx, string sql) {
@ -47,8 +46,7 @@ namespace Elwig.Helpers {
if (App.VersionMajor > major || if (App.VersionMajor > major ||
(App.VersionMajor == major && App.VersionMinor > minor) || (App.VersionMajor == major && App.VersionMinor > minor) ||
(App.VersionMajor == major && App.VersionMinor == minor && App.VersionPatch > patch)) (App.VersionMajor == major && App.VersionMinor == minor && App.VersionPatch > patch)) {
{
long vers = (App.VersionMajor << 24) | (App.VersionMinor << 16) | App.VersionPatch; long vers = (App.VersionMajor << 24) | (App.VersionMinor << 16) | App.VersionPatch;
ExecuteNonQuery(cnx, $"PRAGMA user_version = {vers}"); ExecuteNonQuery(cnx, $"PRAGMA user_version = {vers}");
} }
@ -736,5 +734,71 @@ namespace Elwig.Helpers {
ORDER BY year, mgnr, bucket; ORDER BY year, mgnr, bucket;
"""); """);
} }
private static void UpdateDbSchema_10_To_11(SqliteConnection cnx) {
// Drop columns, if they exist...
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN price_1");
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN price_2");
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN price_3");
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN price_4");
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN price_5");
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN price_6");
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN price_7");
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN price_8");
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN price_9");
ExecuteNonQuery(cnx, "DROP TRIGGER t_payment_delivery_part_i");
ExecuteNonQuery(cnx, """
CREATE TRIGGER t_payment_delivery_part_i
AFTER INSERT ON payment_delivery_part FOR EACH ROW
BEGIN
INSERT INTO payment_member (year, avnr, mgnr, net_amount)
SELECT year, NEW.avnr, mgnr, NEW.amount FROM delivery WHERE (year, did) = (NEW.year, NEW.did)
ON CONFLICT DO UPDATE SET net_amount = net_amount + excluded.net_amount;
END;
""");
ExecuteNonQuery(cnx, "DROP TRIGGER t_payment_delivery_part_u");
ExecuteNonQuery(cnx, """
CREATE TRIGGER t_payment_delivery_part_u
AFTER UPDATE ON payment_delivery_part FOR EACH ROW
BEGIN
UPDATE payment_member
SET net_amount = net_amount - OLD.amount
WHERE (year, avnr, mgnr) IN (SELECT year, OLD.avnr, mgnr FROM delivery WHERE (year, did) = (OLD.year, OLD.did));
INSERT INTO payment_member (year, avnr, mgnr, net_amount)
SELECT year, NEW.avnr, mgnr, NEW.amount FROM delivery WHERE (year, did) = (NEW.year, NEW.did)
ON CONFLICT DO UPDATE SET net_amount = net_amount + excluded.net_amount;
END;
""");
ExecuteNonQuery(cnx, "DROP TRIGGER t_payment_delivery_part_d");
ExecuteNonQuery(cnx, """
CREATE TRIGGER t_payment_delivery_part_d
AFTER DELETE ON payment_delivery_part FOR EACH ROW
BEGIN
UPDATE payment_member
SET net_amount = net_amount - OLD.amount
WHERE (year, avnr, mgnr) IN (SELECT year, OLD.avnr, mgnr FROM delivery WHERE (year, did) = (OLD.year, OLD.did));
END;
""");
ExecuteNonQuery(cnx, "DROP TRIGGER t_payment_delivery_part_bucket_u");
ExecuteNonQuery(cnx, """
CREATE TRIGGER t_payment_delivery_part_bucket_u
AFTER UPDATE ON payment_delivery_part_bucket FOR EACH ROW
BEGIN
UPDATE payment_delivery_part
SET net_amount = net_amount - OLD.amount
WHERE (year, did, dpnr, avnr) = (OLD.year, OLD.did, OLD.dpnr, OLD.avnr);
UPDATE payment_delivery_part
SET net_amount = net_amount + NEW.amount
WHERE (year, did, dpnr, avnr) = (NEW.year, NEW.did, NEW.dpnr, NEW.avnr);
END;
""");
ExecuteNonQuery(cnx, "ALTER TABLE payment_member DROP COLUMN amount");
ExecuteNonQuery(cnx, "ALTER TABLE payment_member ADD COLUMN amount INTEGER NOT NULL GENERATED ALWAYS AS (ROUND(net_amount * (1 + mod_rel) + mod_abs)) VIRTUAL");
}
} }
} }

@ -11,6 +11,12 @@ namespace Elwig.Helpers.Billing {
AvNr = avnr; AvNr = avnr;
} }
public async Task Calculate() {
await DeleteInDb();
await CalculatePrices();
await CalculateModifiers();
}
protected async Task DeleteInDb() { protected async Task DeleteInDb() {
using var cnx = await AppDbContext.ConnectAsync(); using var cnx = await AppDbContext.ConnectAsync();
using (var cmd = cnx.CreateCommand()) { using (var cmd = cnx.CreateCommand()) {
@ -27,8 +33,7 @@ namespace Elwig.Helpers.Billing {
} }
} }
public async Task CalculatePrices() { protected async Task CalculatePrices() {
await DeleteInDb();
using var cnx = await AppDbContext.ConnectAsync(); using var cnx = await AppDbContext.ConnectAsync();
var parts = new List<(int Year, int DId, int DPNr, int BktNr, string SortId, string Discr, int Value, bool MinQuw, double Oe, double Kmw)>(); var parts = new List<(int Year, int DId, int DPNr, int BktNr, string SortId, string Discr, int Value, bool MinQuw, double Oe, double Kmw)>();
@ -65,5 +70,20 @@ namespace Elwig.Helpers.Billing {
await cmd.ExecuteNonQueryAsync(); await cmd.ExecuteNonQueryAsync();
} }
} }
protected async Task CalculateModifiers() {
using var cnx = await AppDbContext.ConnectAsync();
using var cmd = cnx.CreateCommand();
cmd.CommandText = $"""
INSERT INTO payment_delivery_part (year, did, dpnr, avnr, net_amount, mod_abs, mod_rel)
SELECT d.year, d.did, d.dpnr, {AvNr}, 0, COALESCE(m.abs, 0), COALESCE(m.rel, 0)
FROM delivery_part d
LEFT JOIN delivery_part_modifier p ON (p.year, p.did, p.dpnr) = (d.year, d.did, d.dpnr)
LEFT JOIN modifier m ON m.modid = p.modid
WHERE d.year = {Year}
ON CONFLICT DO UPDATE SET mod_abs = mod_abs + excluded.mod_abs, mod_rel = mod_rel + excluded.mod_rel;
""";
await cmd.ExecuteNonQueryAsync();
}
} }
} }

@ -1,4 +1,5 @@
using Elwig.Helpers; using Elwig.Helpers;
using Elwig.Helpers.Billing;
using Elwig.Models.Entities; using Elwig.Models.Entities;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System; using System;
@ -27,11 +28,11 @@ namespace Elwig.Models.Dtos {
MgNr = mgnr; MgNr = mgnr;
} }
public static async Task<IDictionary<int, CreditNoteData>> ForPaymentVariant(DbSet<CreditNoteRowSingle> table, int year, int avnr) { public static async Task<IDictionary<int, CreditNoteData>> ForPaymentVariant(DbSet<CreditNoteRowSingle> table, DbSet<Season> seasons, int year, int avnr) {
return (await FromDbSet(table, year, avnr)) return (await FromDbSet(table, year, avnr))
.GroupBy( .GroupBy(
r => new { r.Year, r.AvNr, r.MgNr, r.TgNr, r.DId, r.DPNr }, r => new { r.Year, r.AvNr, r.MgNr, r.TgNr, r.DId, r.DPNr },
(k, g) => new CreditNoteRow(g)) (k, g) => new CreditNoteRow(g, seasons))
.GroupBy( .GroupBy(
r => new { r.Year, r.AvNr, r.MgNr, r.TgNr }, r => new { r.Year, r.AvNr, r.MgNr, r.TgNr },
(k, g) => new CreditNoteData(g, k.Year, k.TgNr, mgnr: k.MgNr)) (k, g) => new CreditNoteData(g, k.Year, k.TgNr, mgnr: k.MgNr))
@ -43,17 +44,20 @@ namespace Elwig.Models.Dtos {
var v = avnr?.ToString() ?? "NULL"; var v = avnr?.ToString() ?? "NULL";
var m = mgnr?.ToString() ?? "NULL"; var m = mgnr?.ToString() ?? "NULL";
return await table.FromSqlRaw($""" return await table.FromSqlRaw($"""
SELECT d.year, c.tgnr, p.avnr, d.mgnr, d.did, d.lsnr, d.dpnr, b.bktnr, d.sortid, b.discr, b.value, p.price, p.amount, SELECT d.year, c.tgnr, v.avnr, d.mgnr, d.did, d.lsnr, d.dpnr, d.weight, d.modifiers,
v.name AS variant, a.name AS attribute, q.name AS quality_level, d.oe, d.kmw b.bktnr, d.sortid, b.discr, b.value, pb.price, pb.amount, p.net_amount, p.amount AS total_amount,
s.name AS variant, a.name AS attribute, q.name AS quality_level, d.oe, d.kmw
FROM v_delivery d FROM v_delivery d
JOIN wine_variety v ON d.sortid = v.sortid JOIN wine_variety s ON s.sortid = d.sortid
LEFT JOIN wine_attribute a ON a.attrid = d.attrid LEFT JOIN wine_attribute a ON a.attrid = d.attrid
JOIN wine_quality_level q ON q.qualid = d.qualid 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 delivery_part_bucket b ON (b.year, b.did, b.dpnr) = (d.year, d.did, d.dpnr)
LEFT JOIN payment_delivery_part_bucket p ON (p.year, p.did, p.dpnr, p.bktnr) = (b.year, b.did, b.dpnr, b.bktnr) LEFT JOIN payment_variant v ON v.year = d.year
LEFT JOIN credit c ON (c.year, c.avnr, c.mgnr) = (d.year, p.avnr, d.mgnr) LEFT JOIN payment_delivery_part p ON (p.year, p.did, p.dpnr, p.avnr) = (d.year, d.did, d.dpnr, v.avnr)
WHERE b.value > 0 AND (d.year = {y} OR {y} IS NULL) AND (p.avnr = {v} OR {v} IS NULL OR p.avnr IS NULL) AND (d.mgnr = {m} OR {m} IS NULL) LEFT JOIN payment_delivery_part_bucket pb ON (pb.year, pb.did, pb.dpnr, pb.bktnr) = (b.year, b.did, b.dpnr, b.bktnr)
ORDER BY d.year, p.avnr, d.mgnr, d.lsnr, d.dpnr 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(); """).ToListAsync();
} }
} }
@ -73,28 +77,38 @@ namespace Elwig.Models.Dtos {
public string QualityLevel; public string QualityLevel;
public (double Oe, double Kmw) Gradation; public (double Oe, double Kmw) Gradation;
public (string Name, int Value, decimal? Price, decimal? Amount)[] Buckets; public (string Name, int Value, decimal? Price, decimal? Amount)[] Buckets;
public decimal? TotalModifiers;
public decimal? Amount;
public CreditNoteRow(IEnumerable<CreditNoteRowSingle> rows) { public CreditNoteRow(IEnumerable<CreditNoteRowSingle> rows, DbSet<Season> seasons) {
var f = rows.First(); var f = rows.First();
Year = f.Year; Year = f.Year;
TgNr = f.TgNr; TgNr = f.TgNr;
MgNr = f.MgNr; MgNr = f.MgNr;
var season = seasons.Find(Year);
LsNr = f.LsNr; LsNr = f.LsNr;
DPNr = f.DPNr; DPNr = f.DPNr;
Variant = f.Variant; Variant = f.Variant;
Attribute = f.Attribute; Attribute = f.Attribute;
Modifiers = Array.Empty<string>(); // TODO 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; QualityLevel = f.QualityLevel;
Gradation = (f.Oe, f.Kmw); Gradation = (f.Oe, f.Kmw);
Buckets = rows Buckets = rows
.Where(b => b.Value > 0) .Where(b => b.Value > 0)
.OrderByDescending(b => b.BktNr) .OrderByDescending(b => b.BktNr)
// FIXME precision
.Select(b => (b.Discr == "_" ? "ungeb." : $"geb. {f.SortId}{b.Discr}", b.Value, .Select(b => (b.Discr == "_" ? "ungeb." : $"geb. {f.SortId}{b.Discr}", b.Value,
b.Price != null ? (decimal?)Utils.DecFromDb((long)b.Price, 4) : null, b.Price != null ? season?.DecFromDb((long)b.Price) : null,
b.Amount != null ? (decimal?)Utils.DecFromDb((long)b.Amount, 4) : null)) b.Amount != null ? season?.DecFromDb((long)b.Amount) : null))
.ToArray(); .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;
} }
} }
@ -114,6 +128,10 @@ namespace Elwig.Models.Dtos {
public string LsNr { get; set; } public string LsNr { get; set; }
[Column("dpnr")] [Column("dpnr")]
public int DPNr { get; set; } public int DPNr { get; set; }
[Column("weight")]
public int Weight { get; set; }
[Column("modifiers")]
public string? Modifiers { get; set; }
[Column("bktnr")] [Column("bktnr")]
public int BktNr { get; set; } public int BktNr { get; set; }
[Column("sortid")] [Column("sortid")]
@ -126,6 +144,10 @@ namespace Elwig.Models.Dtos {
public long? Price { get; set; } public long? Price { get; set; }
[Column("amount")] [Column("amount")]
public long? Amount { get; set; } public long? Amount { get; set; }
[Column("net_amount")]
public long? NetAmount { get; set; }
[Column("total_amount")]
public long? TotalAmount { get; set; }
[Column("variant")] [Column("variant")]
public string Variant { get; set; } public string Variant { get; set; }
[Column("attribute")] [Column("attribute")]

@ -13,9 +13,33 @@ namespace Elwig.Models.Entities {
[Column("mgnr")] [Column("mgnr")]
public int MgNr { get; set; } public int MgNr { get; set; }
[Column("net_amount")]
public long NetAmountValue { get; set; }
[NotMapped]
public decimal NetAmount {
get => Variant.Season.DecFromDb(NetAmountValue);
set => NetAmountValue = Variant.Season.DecToDb(value);
}
[Column("mod_abs")]
public long ModAbsValue { get; set; }
[NotMapped]
public decimal ModAbs {
get => Variant.Season.DecFromDb(ModAbsValue);
set => ModAbsValue = Variant.Season.DecToDb(value);
}
[Column("mod_rel")]
public double ModRelValue { get; set; }
[NotMapped]
public decimal ModRel {
get => (decimal)ModRelValue;
set => ModRelValue = (double)value;
}
[Column("amount")] [Column("amount")]
public long AmountValue { get; set; } public long AmountValue { get; set; }
[NotMapped] [NotMapped]
public decimal Amount { public decimal Amount {
get => Variant.Season.DecFromDb(AmountValue); get => Variant.Season.DecFromDb(AmountValue);

@ -75,7 +75,7 @@ namespace Elwig.Windows {
CalculateButton.IsEnabled = false; CalculateButton.IsEnabled = false;
Mouse.OverrideCursor = Cursors.AppStarting; Mouse.OverrideCursor = Cursors.AppStarting;
var b = new BillingVariant(v.Year, v.AvNr); var b = new BillingVariant(v.Year, v.AvNr);
await b.CalculatePrices(); await b.Calculate();
Mouse.OverrideCursor = null; Mouse.OverrideCursor = null;
CalculateButton.IsEnabled = true; CalculateButton.IsEnabled = true;
} }
@ -110,7 +110,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, v.Year, v.AvNr); var data = await CreditNoteData.ForPaymentVariant(Context.CreditNoteRows, 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);
using var doc = Document.Merge(list.Select(m => using var doc = Document.Merge(list.Select(m =>
new CreditNote(Context, payments[m.MgNr], data[m.MgNr]) new CreditNote(Context, payments[m.MgNr], data[m.MgNr])