316 lines
18 KiB
C#
316 lines
18 KiB
C#
using Elwig.Helpers;
|
||
using Elwig.Models.Dtos;
|
||
using Elwig.Models.Entities;
|
||
using iText.Kernel.Pdf;
|
||
using iText.Layout.Borders;
|
||
using iText.Layout.Element;
|
||
using iText.Layout.Properties;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
|
||
namespace Elwig.Documents {
|
||
public class CreditNote : BusinessDocument {
|
||
|
||
public new static string Name => "Traubengutschrift";
|
||
|
||
public PaymentMember? Payment;
|
||
public Credit? Credit;
|
||
public CreditNoteDeliveryData Data;
|
||
public string? Text;
|
||
public string CurrencySymbol;
|
||
public int Precision;
|
||
public string MemberModifier;
|
||
public List<(string Name, int Kg, decimal Amount)>? MemberUnderDeliveries;
|
||
public decimal MemberTotalUnderDelivery;
|
||
public int MemberAutoBusinessShares;
|
||
public decimal MemberAutoBusinessSharesAmount;
|
||
public PaymentCustom? CustomPayment;
|
||
|
||
public CreditNote(
|
||
AppDbContext ctx,
|
||
PaymentMember p,
|
||
CreditNoteDeliveryData data,
|
||
bool considerContractPenalties,
|
||
bool considerTotalPenalty,
|
||
bool considerAutoBusinessShares,
|
||
bool considerCustomModifiers,
|
||
Dictionary<string, UnderDelivery>? underDeliveries = null
|
||
) :
|
||
base($"{Name} {(p.Credit != null ? $"Nr. {p.Credit.Year}/{p.Credit.TgNr:000}" : p.Member.FullName)} – {p.Variant.Name}", p.Member) {
|
||
UseBillingAddress = true;
|
||
ShowDateAndLocation = true;
|
||
Data = data;
|
||
Payment = p;
|
||
Credit = p.Credit;
|
||
IsPreview = Payment == null || Credit == null;
|
||
var season = p.Variant.Season;
|
||
if (considerCustomModifiers) {
|
||
CustomPayment = ctx.CustomPayments.Find(p.Year, p.MgNr);
|
||
}
|
||
|
||
var mod = App.Client.IsMatzen ? ctx.Modifiers.Where(m => m.Year == season.Year && m.Name.StartsWith("Treue")).FirstOrDefault() : null;
|
||
if (CustomPayment?.ModComment != null) {
|
||
MemberModifier = CustomPayment.ModComment;
|
||
} else if (mod != null) {
|
||
MemberModifier = $"{mod.Name} ({mod.PublicValueStr})";
|
||
} else {
|
||
MemberModifier = "Sonstige Zu-/Abschläge";
|
||
}
|
||
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;
|
||
|
||
if (considerTotalPenalty) {
|
||
var total = data.Rows.SelectMany(r => r.Buckets).Sum(b => b.Value);
|
||
var totalUnderDelivery = total - p.Member.BusinessShares * season.MinKgPerBusinessShare;
|
||
MemberTotalUnderDelivery = totalUnderDelivery < 0 ? totalUnderDelivery * (season.PenaltyPerKg ?? 0) - (season.PenaltyAmount ?? 0) - (season.PenaltyPerBsAmount * Math.Floor(-(decimal)totalUnderDelivery / season.MinKgPerBusinessShare) ?? 0) : 0;
|
||
if (total == 0)
|
||
MemberTotalUnderDelivery -= (season.PenaltyNone ?? 0) + (season.PenaltyPerBsNone * p.Member.BusinessShares ?? 0);
|
||
}
|
||
if (considerAutoBusinessShares) {
|
||
var fromDate = $"{season.Year}-01-01";
|
||
var toDate = $"{season.Year}-12-31";
|
||
MemberAutoBusinessShares = ctx.MemberHistory
|
||
.Where(h => h.MgNr == p.Member.MgNr && h.Type == "auto")
|
||
.Where(h => h.DateString.CompareTo(fromDate) >= 0 && h.DateString.CompareTo(toDate) <= 0)
|
||
.Sum(h => h.BusinessShares);
|
||
MemberAutoBusinessSharesAmount = MemberAutoBusinessShares * (-season.BusinessShareValue ?? 0);
|
||
}
|
||
if (considerContractPenalties) {
|
||
var varieties = ctx.WineVarieties.ToDictionary(v => v.SortId, v => v);
|
||
var attributes = ctx.WineAttributes.ToDictionary(a => a.AttrId, a => a);
|
||
var comTypes = ctx.AreaCommitmentTypes.ToDictionary(t => t.VtrgId, t => t);
|
||
MemberUnderDeliveries = underDeliveries?
|
||
.OrderBy(u => u.Key)
|
||
.Select(u => (
|
||
varieties[u.Key[..2]].Name + (u.Key.Length > 2 ? " " + attributes[u.Key[2..]].Name : ""),
|
||
u.Value.Diff,
|
||
u.Value.Diff * (comTypes[u.Key].PenaltyPerKg ?? 0)
|
||
- (comTypes[u.Key].PenaltyAmount ?? 0)
|
||
- ((u.Value.Weight == 0 ? comTypes[u.Key].PenaltyNone : null) ?? 0)))
|
||
.Where(u => u.Item3 != 0)
|
||
.ToList();
|
||
}
|
||
}
|
||
|
||
protected override void RenderHeader(iText.Layout.Document doc, PdfDocument pdf) {
|
||
base.RenderHeader(doc, pdf);
|
||
Aside?.AddCell(NewAsideCell("Gutschrift", 2))
|
||
.AddCell(NewAsideCell("TG-Nr.:", isName: true)).AddCell(NewAsideCell(Payment?.Credit != null ? $"{Payment.Credit.Year}/{Payment.Credit.TgNr:000}" : "-"))
|
||
.AddCell(NewAsideCell("Datum:", isName: true)).AddCell(NewAsideCell($"{Payment?.Variant.Date:dd.MM.yyyy}"))
|
||
.AddCell(NewAsideCell("Überw. am:", isName: true)).AddCell(NewAsideCell($"{Payment?.Variant.TransferDate:dd.MM.yyyy}"));
|
||
}
|
||
|
||
protected override void RenderBody(iText.Layout.Document doc, PdfDocument pdf) {
|
||
base.RenderBody(doc, pdf);
|
||
doc.Add(NewCreditTable(Data));
|
||
|
||
var div = new Table(ColsMM(60, 105))
|
||
.SetWidth(UnitValue.CreatePercentValue(100)).SetFixedLayout()
|
||
.SetBorderCollapse(BorderCollapsePropertyValue.COLLAPSE);
|
||
var hint = new KernedParagraph(8)
|
||
.Add(Italic("Hinweis:\n" +
|
||
$"Die Summe der Lieferungen und die Summe der anfal\u00adlenden Pönalen werden mit " +
|
||
$"{Payment?.Variant.Season.Precision} Nach\u00adkomma-stellen berechnent, " +
|
||
$"erst das Ergebnis wird kauf-männisch auf 2 Nach\u00adkomma\u00adstellen gerundet."))
|
||
.SetWidth(56 * PtInMM).SetMarginsMM(4, 2, 4, 2);
|
||
div.AddCell(new Cell(1, 2).SetPadding(0).SetBorder(Border.NO_BORDER).SetBorderTop(new SolidBorder(BorderThickness)));
|
||
div.AddCell(new Cell(3, 1).SetPadding(0).SetBorder(Border.NO_BORDER).Add(hint).SetKeepTogether(true));
|
||
var tbl1 = new Table(ColsMM(70, 5, 5, 25))
|
||
.SetWidth(UnitValue.CreatePercentValue(100)).SetFixedLayout()
|
||
.SetBorderCollapse(BorderCollapsePropertyValue.COLLAPSE)
|
||
.SetKeepTogether(true);
|
||
var tbl2 = new Table(ColsMM(70, 5, 5, 25))
|
||
.SetWidth(UnitValue.CreatePercentValue(100)).SetFixedLayout()
|
||
.SetBorderCollapse(BorderCollapsePropertyValue.COLLAPSE)
|
||
.SetKeepTogether(true);
|
||
var tbl3 = new Table(ColsMM(70, 5, 5, 25))
|
||
.SetWidth(UnitValue.CreatePercentValue(100)).SetFixedLayout()
|
||
.SetBorderCollapse(BorderCollapsePropertyValue.COLLAPSE)
|
||
.SetKeepTogether(true);
|
||
|
||
var sum = Data.Rows.Sum(p => p.Amount);
|
||
if (Payment == null) {
|
||
tbl1.AddCells(FormatRow("Gesamt", sum, bold: true, noTopBorder: true));
|
||
} else {
|
||
var noBorder = true;
|
||
if (Payment.NetAmount != Payment.Amount) {
|
||
tbl1.AddCells(FormatRow("Zwischensumme", Payment.NetAmount, noTopBorder: noBorder));
|
||
noBorder = false;
|
||
tbl1.AddCells(FormatRow(MemberModifier, Payment.Amount - Payment.NetAmount, add: true));
|
||
}
|
||
if (Credit == null) {
|
||
tbl1.AddCells(FormatRow("Gesamtbetrag", Payment.Amount, bold: true, noTopBorder: noBorder));
|
||
// TODO Mock VAT
|
||
} else {
|
||
var hasPrev = Credit.PrevNetAmount != null;
|
||
tbl1.AddCells(FormatRow(hasPrev ? "Gesamtbetrag" : "Nettobetrag", Credit.NetAmount, bold: true, noTopBorder: noBorder));
|
||
if (hasPrev) {
|
||
tbl1.AddCells(FormatRow("Bisher berücksichtigt", -Credit.PrevNetAmount, add: true));
|
||
tbl1.AddCells(FormatRow("Nettobetrag", Credit.NetAmount - (Credit.PrevNetAmount ?? 0)));
|
||
}
|
||
tbl1.AddCells(FormatRow($"Mehrwertsteuer ({Credit.Vat * 100} %)", Credit.VatAmount, add: true));
|
||
tbl1.AddCells(FormatRow("Bruttobetrag", Credit.GrossAmount, bold: true));
|
||
}
|
||
}
|
||
|
||
decimal penalty = 0;
|
||
string? comment = null;
|
||
|
||
if (MemberUnderDeliveries != null && MemberUnderDeliveries.Count > 0) {
|
||
tbl2.AddCell(NewTd("Anfallende Pönalen durch Unterlieferungen:", colspan: 2).SetPaddingTopMM(5))
|
||
.AddCell(NewCell(colspan: 2));
|
||
foreach (var u in MemberUnderDeliveries) {
|
||
tbl2.AddCells(FormatRow($"{u.Name} ({u.Kg:N0} kg)", u.Amount, add: true, subCat: true));
|
||
penalty += u.Amount;
|
||
}
|
||
penalty = Math.Round(penalty, 2, MidpointRounding.AwayFromZero);
|
||
}
|
||
if (MemberTotalUnderDelivery != 0) {
|
||
tbl2.AddCells(FormatRow("Unterlieferung (GA)", MemberTotalUnderDelivery, add: true));
|
||
penalty += MemberTotalUnderDelivery;
|
||
}
|
||
if (MemberAutoBusinessSharesAmount != 0) {
|
||
tbl2.AddCells(FormatRow($"Autom. Nachz. von GA ({MemberAutoBusinessShares})", MemberAutoBusinessSharesAmount, add: true));
|
||
penalty += MemberAutoBusinessSharesAmount;
|
||
}
|
||
if (CustomPayment?.Amount != null) {
|
||
comment = CustomPayment.Comment;
|
||
string text = (CustomPayment.Amount.Value < 0 ? "Weitere Abzüge" : "Weitere Zuschläge") + (comment != null ? "*" : "");
|
||
if (comment != null && comment!.Length <= 30) {
|
||
text = comment;
|
||
comment = null;
|
||
}
|
||
tbl2.AddCells(FormatRow(text, CustomPayment.Amount.Value, add: true));
|
||
penalty += CustomPayment.Amount.Value;
|
||
}
|
||
|
||
if (Credit == null) {
|
||
tbl3.AddCells(FormatRow("Auszahlungsbetrag", (Payment?.Amount + penalty) ?? (sum + penalty), bold: true));
|
||
} else {
|
||
var diff = Credit.Modifiers - penalty;
|
||
if (diff != 0) {
|
||
tbl3.AddCells(FormatRow(diff < 0 ? "Sonstige Abzüge" : "Sonstige Zuschläge", diff, add: true));
|
||
}
|
||
if (Credit.PrevModifiers != null && Credit.PrevModifiers != 0) {
|
||
tbl3.AddCells(FormatRow("Bereits berücksichtigte Abzüge", -Credit.PrevModifiers, add: true));
|
||
}
|
||
tbl3.AddCells(FormatRow("Auszahlungsbetrag", Credit.Amount, bold: true));
|
||
}
|
||
|
||
div.AddCell(new Cell().SetPadding(0).SetBorder(Border.NO_BORDER).Add(tbl1));
|
||
div.AddCell(new Cell().SetPadding(0).SetBorder(Border.NO_BORDER).Add(tbl2));
|
||
div.AddCell(new Cell().SetPadding(0).SetBorder(Border.NO_BORDER).Add(tbl3));
|
||
doc.Add(div);
|
||
|
||
if (comment != null) {
|
||
doc.Add(new KernedParagraph($"*{comment}", 12).SetMarginTopMM(10));
|
||
}
|
||
doc.Add(new KernedParagraph($"Überweisung erfolgt auf Konto {Utils.FormatIban(Member.Iban ?? "-")}.", 12).SetPaddingTopMM(10));
|
||
if (Text != null) {
|
||
doc.Add(new KernedParagraph(Text, 12).SetMarginTop(12).SetKeepTogether(true));
|
||
}
|
||
}
|
||
|
||
protected Cell[] FormatRow(string name, decimal? value, bool add = false, bool bold = false, bool subCat = false, bool noTopBorder = false) {
|
||
float textSize = subCat ? 8 : !add ? 12 : 10;
|
||
float numSize = subCat ? 8 : 12;
|
||
var border = !add && !noTopBorder;
|
||
var pad = !add && !noTopBorder ? 1 : 0.5f;
|
||
return [
|
||
NewTd($"{name}:", textSize, bold: bold, borderTop: border).SetPaddingTopMM(pad),
|
||
NewTd(value < 0 ? "–" : (add ? "+" : ""), numSize, right: true, bold: bold, borderTop: border).SetPaddingTopMM(pad),
|
||
NewTd(CurrencySymbol, numSize, bold: bold, borderTop: border).SetPaddingTopMM(pad),
|
||
NewTd($"{Math.Abs(value ?? 0):N2}", numSize, right: true, bold: bold, borderTop: border).SetPaddingTopMM(pad),
|
||
];
|
||
}
|
||
|
||
protected Table NewCreditTable(CreditNoteDeliveryData data) {
|
||
var tbl = new Table(ColsMM(25, 6, 36, 10, 10, 15, 12, 13, 5, 17, 16), true)
|
||
.SetWidth(UnitValue.CreatePercentValue(100)).SetFixedLayout()
|
||
.SetBorderCollapse(BorderCollapsePropertyValue.COLLAPSE);
|
||
|
||
tbl.AddHeaderCell(NewTh("Lieferschein-Nr.", rowspan: 2, left: true))
|
||
.AddHeaderCell(NewTh("Pos.", rowspan: 2).SetPaddingLeft(0).SetPaddingRight(0))
|
||
.AddHeaderCell(NewTh("Sorte/Attribut/Bewirtschaftg.\nZu-/Abschlag", rowspan: 2, left: true))
|
||
.AddHeaderCell(NewTh("Gradation", colspan: 2))
|
||
.AddHeaderCell(NewTh("Flächenbindung", colspan: 2))
|
||
.AddHeaderCell(NewTh("Preis"))
|
||
.AddHeaderCell(NewTh("Rbl.").SetPaddingLeft(0).SetPaddingRight(0))
|
||
.AddHeaderCell(NewTh("Zu-/Abschläge").SetPaddingLeft(0).SetPaddingRight(0))
|
||
.AddHeaderCell(NewTh("Betrag"))
|
||
.AddHeaderCell(NewTh("[°Oe]"))
|
||
.AddHeaderCell(NewTh("[°KMW]").SetPaddingLeft(0).SetPaddingRight(0))
|
||
.AddHeaderCell(NewTh("[kg]", colspan: 2))
|
||
.AddHeaderCell(NewTh($"[{CurrencySymbol}/kg]"))
|
||
.AddHeaderCell(NewTh("[%]").SetPaddingLeft(0).SetPaddingRight(0))
|
||
.AddHeaderCell(NewTh($"[{CurrencySymbol}]"))
|
||
.AddHeaderCell(NewTh($"[{CurrencySymbol}]"));
|
||
|
||
foreach (var p in data.Rows) {
|
||
var sub = new Table(ColsMM(25, 6, 36, 10, 10, 15, 12, 13, 5, 17, 16))
|
||
.SetWidth(UnitValue.CreatePercentValue(100)).SetFixedLayout()
|
||
.SetBorderCollapse(BorderCollapsePropertyValue.COLLAPSE)
|
||
.SetKeepTogether(true);
|
||
var attr = p.Attribute != null || p.Cultivation != null || p.QualId == "WEI";
|
||
var rows = Math.Max(p.Buckets.Length, 1 + (attr ? 1 : 0) + p.Modifiers.Length);
|
||
for (int i = 0; i < rows; i++) {
|
||
if (i == 0) {
|
||
sub.AddCell(NewTd(p.LsNr))
|
||
.AddCell(NewTd($"{p.DPNr:N0}", center: true).SetPaddingLeft(0).SetPaddingRight(0))
|
||
.AddCell(NewTd(p.Variety))
|
||
.AddCell(NewTd($"{p.Gradation.Oe:N0}", center: true))
|
||
.AddCell(NewTd($"{p.Gradation.Kmw:N1}", center: true));
|
||
} else if (i == 1 && attr) {
|
||
var varibute = new KernedParagraph(8);
|
||
if (p.Attribute != null) varibute.Add(Normal(p.Attribute));
|
||
if (p.Attribute != null && p.Cultivation != null) varibute.Add(Normal(" / "));
|
||
if (p.Cultivation != null) varibute.Add(Normal(p.Cultivation));
|
||
if ((p.Attribute != null || p.Cultivation != null) && p.QualId == "WEI") varibute.Add(Normal(" / "));
|
||
if (p.QualId == "WEI") varibute.Add(Italic("abgew."));
|
||
sub.AddCell(NewCell(colspan: 2))
|
||
.AddCell(NewTd(varibute, colspan: 3).SetPaddingTop(0));
|
||
} else if (i - (rows - p.Modifiers.Length) < p.Modifiers.Length) {
|
||
sub.AddCell(NewCell(colspan: 2))
|
||
.AddCell(NewTd(p.Modifiers[i - (rows - p.Modifiers.Length)], 8, colspan: 3).SetPaddingTop(0).SetPaddingLeftMM(5));
|
||
} else {
|
||
sub.AddCell(NewCell(colspan: 5));
|
||
}
|
||
|
||
if (i < p.Buckets.Length) {
|
||
var bucket = p.Buckets[i];
|
||
var pad = i == 0 ? 0.5f : 0;
|
||
sub.AddCell(NewTd($"{bucket.Name}:", 8)
|
||
.SetPaddingTopMM(pad))
|
||
.AddCell(NewTd($"{bucket.Value:N0}", right: true)
|
||
.SetPaddingTopMM(pad))
|
||
.AddCell(NewTd($"{bucket.Price:N4}", right: true)
|
||
.SetPaddingTopMM(pad));
|
||
} else {
|
||
sub.AddCell(NewCell(colspan: 3));
|
||
}
|
||
|
||
if (i == p.Buckets.Length - 1) {
|
||
var rebelMod = p.WeighingModifier * 100;
|
||
var totalMod = p.TotalModifiers ?? 0;
|
||
var pad = i == 0 ? 0.5f : 0;
|
||
sub.AddCell(NewTd(rebelMod == 0 ? "-" : (Utils.GetSign(rebelMod) + $"{Math.Abs(rebelMod):0.0##}"), 6, center: true)
|
||
.SetPaddingTopMM(pad))
|
||
.AddCell(NewTd(totalMod == 0 ? "-" : Utils.GetSign(totalMod) + $"{Math.Abs(totalMod):N2}", center: totalMod == 0, right: true)
|
||
.SetPaddingTopMM(pad))
|
||
.AddCell(NewTd($"{p.Amount:N2}", right: true)
|
||
.SetPaddingTopMM(pad));
|
||
} else {
|
||
sub.AddCell(NewCell(colspan: 3));
|
||
}
|
||
}
|
||
tbl.AddCell(new Cell(1, 12).SetPadding(0).SetBorder(Border.NO_BORDER).Add(sub));
|
||
}
|
||
|
||
return tbl;
|
||
}
|
||
}
|
||
}
|