diff --git a/Elwig/Documents/DeliveryConfirmation.cshtml b/Elwig/Documents/DeliveryConfirmation.cshtml index b11bdf3..a4a2dd0 100644 --- a/Elwig/Documents/DeliveryConfirmation.cshtml +++ b/Elwig/Documents/DeliveryConfirmation.cshtml @@ -54,8 +54,8 @@ @p.Variant @p.Attribute @p.QualityLevel - @($"{p.Oe:N0}") - @($"{p.Kmw:N1}") + @($"{p.Gradation.Oe:N0}") + @($"{p.Gradation.Kmw:N1}") } @if (i > 0 && i <= p.Modifiers.Length) { @(p.Modifiers[i - 1]) @@ -64,8 +64,8 @@ } @if (i < p.Buckets.Length) { var bucket = p.Buckets[i]; - @bucket.Item1: - @($"{bucket.Item2:N0}") + @bucket.Name: + @($"{bucket.Value:N0}") } else { } diff --git a/Elwig/Helpers/Export/Ods.cs b/Elwig/Helpers/Export/Ods.cs index db32f26..e8cd93a 100644 --- a/Elwig/Helpers/Export/Ods.cs +++ b/Elwig/Helpers/Export/Ods.cs @@ -81,7 +81,7 @@ namespace Elwig.Helpers.Export { Content = new StreamWriter(content.Open(), Utils.UTF8); await Content.WriteAsync(""" - + @@ -108,10 +108,14 @@ namespace Elwig.Helpers.Export { - + + + + + @@ -151,8 +155,8 @@ namespace Elwig.Helpers.Export { await writer.WriteAsync($""" 2 - 3 - 3 + 4 + 4 """); @@ -177,8 +181,10 @@ namespace Elwig.Helpers.Export { _tables.Add(table.Name); await Content.WriteAsync($" \r\n"); - foreach (var w in table.ColumnWidths) { - await Content.WriteAsync(" \r\n"); + foreach (var (s, w) in table.ColumnSpans.Zip(table.ColumnWidths)) { + for (int i = 0; i < s; i++) { + await Content.WriteAsync(" \r\n"); + } } await Content.WriteAsync( @@ -189,48 +195,76 @@ namespace Elwig.Helpers.Export { $" \r\n" + $" \r\n" + $" \r\n"); - foreach (var (name, span) in table.ColumnNames.Zip(table.ColumnSpans)) { - await Content.WriteAsync(FormatCell(name, colSpan: span, style: "th")); + foreach (var (name, span, units) in table.ColumnNames.Zip(table.ColumnSpans, table.ColumnUnits)) { + var hasUnits = units.Length > 0; + await Content.WriteAsync(FormatCell(name, colSpan: span, rowSpan: hasUnits ? 1 : 2, style: "th")); + } + await Content.WriteAsync(" \r\n \r\n"); + foreach (var (span, units) in table.ColumnSpans.Zip(table.ColumnUnits)) { + if (units.Length == 0) { + await Content.WriteAsync($" \r\n"); + continue; + } + foreach (var u in units) { + await Content.WriteAsync(FormatCell(u == null ? null : $"[{u}]", style: "th")); + } } await Content.WriteAsync(" \r\n"); foreach (var row in table.GetData()) { - await FormatRow(row); + await FormatRow(row, table.ColumnUnits); } await Content.WriteAsync(" \r\n"); } - protected async Task FormatRow(IEnumerable row) { + protected async Task FormatRow(IEnumerable row, IEnumerable colUnits) { if (Content == null) throw new InvalidOperationException(); var arrays = row.Where(c => c is Array).Cast().Select(c => c.Length).ToArray(); int rowNum = Math.Max(1, arrays.Length > 0 ? arrays.Max() : 0); for (int i = 0; i < rowNum; i++) { await Content.WriteAsync(" \r\n"); - foreach (var data in row) { + foreach (var (data, units) in row.Zip(colUnits)) { if (data is Array a) { - await Content.WriteAsync(i < a.Length ? FormatCell(a.GetValue(i)) : " \r\n"); + await Content.WriteAsync(i < a.Length ? FormatCell(a.GetValue(i), units: units) : $" \r\n"); } else { - await Content.WriteAsync(i == 0 ? FormatCell(data, rowNum) : " \r\n"); + await Content.WriteAsync(FormatCell(data, rowSpan: i == 0 ? rowNum : 1, isCovered: i > 0, units: units)); } } await Content.WriteAsync(" \r\n"); } } - protected static string FormatCell(object? data, int rowSpan = 1, int colSpan = 1, string? style = "default", bool forceString = false) { + private static int GetSubCols(Type? type) { + if (type != null && type.IsValueType == true && type.Name.StartsWith("ValueTuple")) + return type.GetFields().Length; + return 1; + } + + protected static string FormatCell(object? data, int rowSpan = 1, int colSpan = 1, string? style = "default", bool isCovered = false, string?[]? units = null) { if (data?.GetType().IsValueType == true && data.GetType().Name.StartsWith("ValueTuple")) - return string.Join("", data.GetType().GetFields().Select(p => FormatCell(p.GetValue(data), rowSpan, colSpan, style, forceString))); + return string.Join("", data.GetType().GetFields().Zip(units ?? Array.Empty()) + .Select(p => FormatCell(p.First.GetValue(data), rowSpan, colSpan, style, isCovered, new[] { p.Second })) + ); var add = (style != null ? $" table:style-name=\"{style}\"" : "") + (rowSpan > 1 || colSpan > 1 ? $" table:number-rows-spanned=\"{rowSpan}\" table:number-columns-spanned=\"{colSpan}\"" : ""); + string ct = isCovered ? "table:covered-table-cell" : "table:table-cell"; + var isPercent = units != null && units.Length > 0 && units[0] == "%"; + string c; - if (!forceString && data == null) { - c = $""; - } else if (!forceString && (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)) { - c = $"{data}"; + if (data == null) { + c = $"<{ct}{add}/>"; + } else if (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"); + if (isPercent) { + data = $"{v:N1}"; + add = string.Join(" ", add.Split(" ").Select(p => p.StartsWith("table:style-name=") ? "table:style-name=\"perc\"" : p)); + } + c = $"<{ct} office:value-type=\"float\" calcext:value-type=\"float\" office:value=\"{v.ToString()?.Replace(",", ".")}\"{add}>{data}"; } else { - c = $"{data}"; + c = $"<{ct} office:value-type=\"string\" calcext:value-type=\"string\"{add}>{data}"; } return $" {c}\r\n" + (colSpan > 1 ? $" \r\n" : ""); diff --git a/Elwig/Models/Dtos/AreaComUnderDeliveyData.cs b/Elwig/Models/Dtos/AreaComUnderDeliveyData.cs index 9382797..e0959e5 100644 --- a/Elwig/Models/Dtos/AreaComUnderDeliveyData.cs +++ b/Elwig/Models/Dtos/AreaComUnderDeliveyData.cs @@ -8,19 +8,19 @@ using System.Threading.Tasks; namespace Elwig.Models.Dtos { public class AreaComUnderDeliveryData : DataTable { - private static readonly (string, string, int)[] FieldNames = new[] { - ("MgNr", "MgNr.", 12), - ("Name", "Name", 40), - ("GivenName", "Vorname", 40), - ("Address", "Adresse", 60), - ("Plz", "PLZ", 10), - ("Locality", "Ort", 60), - ("VtrgIds", "Vertrag", 14), - ("Areas", "Fläche", 16), - ("DeliveryObligations", "Lieferpflicht", 22), - ("Weights", "Geliefert", 22), - ("UnderDeliveries", "Unterliefert", 22), - ("Percents", "Prozent", 16), + private static readonly (string, string, string?, int)[] FieldNames = new[] { + ("MgNr", "MgNr.", null, 12), + ("Name", "Name", null, 40), + ("GivenName", "Vorname", null, 40), + ("Address", "Adresse", null, 60), + ("Plz", "PLZ", null, 10), + ("Locality", "Ort", null, 60), + ("VtrgIds", "Vertrag", null, 14), + ("Areas", "Fläche", "m²", 16), + ("DeliveryObligations", "Lieferpflicht", "kg", 22), + ("Weights", "Geliefert", "kg", 22), + ("UnderDeliveries", "Unterliefert", "kg", 22), + ("Percents", "Prozent", "%", 16), }; public AreaComUnderDeliveryData(IEnumerable rows, int year) : @@ -65,7 +65,7 @@ namespace Elwig.Models.Dtos { .Select(v => v.First < v.Second ? (int?)v.First - v.Second : null) .ToArray(); public double?[] Percents => Weights.Zip(DeliveryObligations) - .Select(v => v.First < v.Second ? (double?)Math.Round(v.First * 100.0 / v.Second - 100.0, 1) : null) + .Select(v => v.First < v.Second ? (double?)v.First * 100.0 / v.Second - 100.0 : null) .ToArray(); public AreaComUnderDeliveryRow(IEnumerable rows) { diff --git a/Elwig/Models/Dtos/DataTable.cs b/Elwig/Models/Dtos/DataTable.cs index 2bc3841..28fb595 100644 --- a/Elwig/Models/Dtos/DataTable.cs +++ b/Elwig/Models/Dtos/DataTable.cs @@ -18,15 +18,16 @@ namespace Elwig.Models.Dtos { public IEnumerable ColumnFlatTypes { get; private set; } public IEnumerable ColumnSpans { get; private set; } public IEnumerable ColumnWidths { get; private set; } + public IEnumerable ColumnUnits { get; private set; } private readonly PropertyInfo[] _properties; private readonly FieldInfo[] _fields; private readonly (string, PropertyInfo?, FieldInfo?)[] _map; - public DataTable(string name, string fullName, IEnumerable rows, IEnumerable<(string, string, int?)>? colNames = null) { + public DataTable(string name, string fullName, IEnumerable rows, IEnumerable<(string, string, string?, int?)>? colNames = null) { _fields = typeof(T).GetFields(); _properties = typeof(T).GetProperties(); - colNames ??= _properties.Select(p => p.Name).Union(_fields.Select(f => f.Name)).Select(i => (i, i, (int?)null)).ToList(); + colNames ??= _properties.Select(p => p.Name).Union(_fields.Select(f => f.Name)).Select(i => (i, i, (string?)null, (int?)null)).ToList(); _map = colNames.Select(n => (n.Item2, _properties.FirstOrDefault(p => p?.Name == n.Item1, null), _fields.FirstOrDefault(f => f?.Name == n.Item1, null))).ToArray(); Name = name; FullName = fullName; @@ -42,27 +43,28 @@ namespace Elwig.Models.Dtos { return type != null && type.IsValueType && type.Name.StartsWith("ValueTuple") ? type.GetFields().Length : type != null && elType != null && type.IsArray && elType.IsValueType && elType.Name.StartsWith("ValueTuple") ? elType.GetFields().Length : 1; }).ToList(); - ColumnWidths = colNames.Select(c => c.Item3).ToList(); + ColumnWidths = colNames.Select(c => c.Item4).ToList(); + ColumnUnits = colNames.Select(c => c.Item3?.Split("|").Select(p => p.Length == 0 ? null : p).ToArray() ?? Array.Empty()).ToList(); } - public DataTable(string name, string fullName, IEnumerable rows, IEnumerable<(string, string)>? colNames = null) : - this(name, fullName, rows, colNames?.Select(c => (c.Item1, c.Item2, (int?)null))) { + public DataTable(string name, string fullName, IEnumerable rows, IEnumerable<(string, string, string?)>? colNames = null) : + this(name, fullName, rows, colNames?.Select(c => (c.Item1, c.Item2, c.Item3, (int?)null))) { } - public DataTable(string name, IEnumerable rows, IEnumerable<(string, string)>? colNames = null) : + public DataTable(string name, IEnumerable rows, IEnumerable<(string, string, string?)>? colNames = null) : this(name, name, rows, colNames) { } - public DataTable(string name, IEnumerable rows, IEnumerable<(string, string, int?)>? colNames = null) : + public DataTable(string name, IEnumerable rows, IEnumerable<(string, string, string?, int?)>? colNames = null) : this(name, name, rows, colNames) { } - public DataTable(string name, IEnumerable rows, IEnumerable<(string, string, int)>? colNames = null) : + public DataTable(string name, IEnumerable rows, IEnumerable<(string, string, string?, int)>? colNames = null) : this(name, name, rows, colNames) { } - public DataTable(string name, string fullName, IEnumerable rows, IEnumerable<(string, string, int)>? colNames = null) : - this(name, fullName, rows, colNames?.Select(c => (c.Item1, c.Item2, (int?)c.Item3))) { + public DataTable(string name, string fullName, IEnumerable rows, IEnumerable<(string, string, string?, int)>? colNames = null) : + this(name, fullName, rows, colNames?.Select(c => (c.Item1, c.Item2, c.Item3, (int?)c.Item4))) { } protected IEnumerable<(string, object?)> GetNamedRowData(T row) { diff --git a/Elwig/Models/Dtos/DeliveryConfirmationData.cs b/Elwig/Models/Dtos/DeliveryConfirmationData.cs index b9be339..a81af4c 100644 --- a/Elwig/Models/Dtos/DeliveryConfirmationData.cs +++ b/Elwig/Models/Dtos/DeliveryConfirmationData.cs @@ -7,17 +7,16 @@ using System.Threading.Tasks; namespace Elwig.Models.Dtos { public class DeliveryConfirmationData : DataTable { - private static readonly (string, string)[] FieldNames = new[] { - ("LsNr", "LsNr."), - ("DPNr", "Pos."), - ("Variant", "Sorte"), - ("Attribute", "Attribut"), - ("Modifiers", "Zu-/Abschläge"), - ("QualityLevel", "Qualitätsstufe"), - ("Oe", "°Oe"), - ("Kmw", "°KMW"), - ("Buckets", "Flächenbindung"), - ("Weight", "Gewicht"), + private static readonly (string, string, string?, int)[] FieldNames = new[] { + ("LsNr", "LsNr.", null, 26), + ("DPNr", "Pos.", null, 8), + ("Variant", "Sorte", null, 40), + ("Attribute", "Attribut", null, 20), + ("Modifiers", "Zu-/Abschläge", null, 30), + ("QualityLevel", "Qualitätsstufe", null, 25), + ("Gradation", "Gradation", "°Oe|°KMW", 32), + ("Buckets", "Flächenbindung", "|kg", 36), + ("Weight", "Gewicht", "kg", 16), }; private readonly int MgNr; @@ -71,11 +70,10 @@ namespace Elwig.Models.Dtos { public string Variant; public string? Attribute; public string QualityLevel; - public double Oe; - public double Kmw; + public (double Oe, double Kmw) Gradation; public string[] Modifiers; public int Weight; - public (string, int)[] Buckets; + public (string Name, int Value)[] Buckets; public DeliveryConfirmationRow(DeliveryPart p) { var d = p.Delivery; @@ -84,8 +82,7 @@ namespace Elwig.Models.Dtos { Variant = p.Variant.Name; Attribute = p.Attribute?.Name; QualityLevel = p.Quality.Name; - Oe = p.Oe; - Kmw = p.Kmw; + Gradation = (p.Oe, p.Kmw); Modifiers = p.Modifiers .Select(m => m.Name) .ToArray(); diff --git a/Elwig/Models/Dtos/MemberDeliveryPerVariantData.cs b/Elwig/Models/Dtos/MemberDeliveryPerVariantData.cs index aa6f4b1..972aa2c 100644 --- a/Elwig/Models/Dtos/MemberDeliveryPerVariantData.cs +++ b/Elwig/Models/Dtos/MemberDeliveryPerVariantData.cs @@ -8,18 +8,18 @@ using System.Threading.Tasks; namespace Elwig.Models.Dtos { public class MemberDeliveryPerVariantData : DataTable { - private static readonly (string, string, int)[] FieldNames = new[] { - ("MgNr", "MgNr.", 12), - ("Name", "Name", 40), - ("GivenName", "Vorname", 40), - ("Address", "Adresse", 60), - ("Plz", "PLZ", 10), - ("Locality", "Ort", 60), - ("SortIds", "Sorte", 12), - ("AttrIds", "Attribut", 16), - ("Weights", "Geliefert", 22), - ("Areas", "Fläche", 22), - ("Yields", "Ertrag", 22), + private static readonly (string, string, string?, int)[] FieldNames = new[] { + ("MgNr", "MgNr.", null, 12), + ("Name", "Name", null, 40), + ("GivenName", "Vorname", null, 40), + ("Address", "Adresse", null, 60), + ("Plz", "PLZ", null, 10), + ("Locality", "Ort", null, 60), + ("SortIds", "Sorte", null, 12), + ("AttrIds", "Attribut", null, 16), + ("Weights", "Geliefert", "kg", 22), + ("Areas", "Fläche", "m²", 22), + ("Yields", "Ertrag", "kg/ha", 22), }; diff --git a/Elwig/Models/Dtos/OverUnderDeliveryData.cs b/Elwig/Models/Dtos/OverUnderDeliveryData.cs index ba320ef..ed98b11 100644 --- a/Elwig/Models/Dtos/OverUnderDeliveryData.cs +++ b/Elwig/Models/Dtos/OverUnderDeliveryData.cs @@ -7,19 +7,19 @@ using System.Threading.Tasks; namespace Elwig.Models.Dtos { public class OverUnderDeliveryData : DataTable { - private static readonly (string, string, int)[] FieldNames = new[] { - ("MgNr", "MgNr.", 12), - ("Name", "Name", 40), - ("GivenName", "Vorname", 40), - ("Address", "Adresse", 60), - ("Plz", "PLZ", 10), - ("Locality", "Ort", 60), - ("BusinessShares", "GA", 10), - ("DeliveryObligation", "Lieferpflicht", 22), - ("DeliveryRight", "Lieferrecht", 22), - ("Weight", "Geliefert", 22), - ("OverUnderDelivery", "Über-/Unterliefert", 35), - ("Percent", "Prozent", 16), + private static readonly (string, string, string?, int)[] FieldNames = new[] { + ("MgNr", "MgNr.", null, 12), + ("Name", "Name", null, 40), + ("GivenName", "Vorname", null, 40), + ("Address", "Adresse", null, 60), + ("Plz", "PLZ", null, 10), + ("Locality", "Ort", null, 60), + ("BusinessShares", "GA", null, 10), + ("DeliveryObligation", "Lieferpflicht", "kg", 22), + ("DeliveryRight", "Lieferrecht", "kg", 22), + ("Weight", "Geliefert", "kg", 22), + ("OverUnderDelivery", "Über-/Unterliefert", "kg", 35), + ("Percent", "Prozent", "%", 16), }; public OverUnderDeliveryData(IEnumerable rows, int year) : @@ -75,7 +75,7 @@ namespace Elwig.Models.Dtos { Weight > DeliveryRight ? Weight - DeliveryRight : null; [NotMapped] public double? Percent => - Weight < DeliveryObligation ? Math.Round(Weight * 100.0 / DeliveryObligation - 100.0, 1) : - Weight > DeliveryRight ? Math.Round(Weight * 100.0 / DeliveryRight - 100, 1) : null; + Weight < DeliveryObligation ? Weight * 100.0 / DeliveryObligation - 100.0 : + Weight > DeliveryRight ? Weight * 100.0 / DeliveryRight - 100 : null; } }