Helpers/Export: Add Dto file export
This commit is contained in:
187
Elwig/Helpers/Export/Ods.cs
Normal file
187
Elwig/Helpers/Export/Ods.cs
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
using Elwig.Models.Dtos;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data.Entity.Core.Common.CommandTrees.ExpressionBuilder;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace Elwig.Helpers.Export {
|
||||||
|
public class OdsFile : IDisposable, IAsyncDisposable {
|
||||||
|
|
||||||
|
protected readonly string FileName;
|
||||||
|
protected readonly ZipArchive ZipArchive;
|
||||||
|
protected StreamWriter? Content;
|
||||||
|
|
||||||
|
public OdsFile(string filename) {
|
||||||
|
FileName = filename;
|
||||||
|
File.Delete(filename);
|
||||||
|
ZipArchive = ZipFile.Open(FileName, ZipArchiveMode.Create); ;
|
||||||
|
Content = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() {
|
||||||
|
DisposeAsync().GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask DisposeAsync() {
|
||||||
|
await AddTrailer();
|
||||||
|
Content?.Close();
|
||||||
|
Content?.DisposeAsync();
|
||||||
|
ZipArchive?.Dispose();
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task AddHeader() {
|
||||||
|
var mimetype = ZipArchive.CreateEntry("mimetype", CompressionLevel.NoCompression);
|
||||||
|
using (var stream = mimetype.Open()) {
|
||||||
|
using var writer = new StreamWriter(stream, Utils.UTF8);
|
||||||
|
await writer.WriteAsync("application/vnd.oasis.opendocument.spreadsheet");
|
||||||
|
}
|
||||||
|
|
||||||
|
var manifest = ZipArchive.CreateEntry("META-INF/manifest.xml");
|
||||||
|
using (var stream = manifest.Open()) {
|
||||||
|
using var writer = new StreamWriter(stream, Utils.UTF8);
|
||||||
|
await writer.WriteAsync("""
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0" manifest:version="1.3">
|
||||||
|
<manifest:file-entry manifest:full-path="/" manifest:version="1.3" manifest:media-type="application/vnd.oasis.opendocument.spreadsheet"/>
|
||||||
|
<manifest:file-entry manifest:full-path="content.xml" manifest:media-type="text/xml"/>
|
||||||
|
<manifest:file-entry manifest:full-path="styles.xml" manifest:media-type="text/xml"/>
|
||||||
|
<manifest:file-entry manifest:full-path="meta.xml" manifest:media-type="text/xml"/>
|
||||||
|
</manifest:manifest>
|
||||||
|
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
var styles = ZipArchive.CreateEntry("styles.xml");
|
||||||
|
using (var stream = styles.Open()) {
|
||||||
|
using var writer = new StreamWriter(stream, Utils.UTF8);
|
||||||
|
await writer.WriteAsync("""
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<office:document-styles xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" office:version="1.3">
|
||||||
|
<office:styles>
|
||||||
|
<style:default-style style:family="table-cell">
|
||||||
|
<style:text-properties fo:language="de" fo:country="AT"/>
|
||||||
|
</style:default-style>
|
||||||
|
<style:style style:name="default" style:family="table-cell">
|
||||||
|
<style:table-cell-properties style:vertical-align="top"/>
|
||||||
|
</style:style>
|
||||||
|
<style:style style:name="header" style:family="table-cell" style:parent-style-name="default">
|
||||||
|
<style:table-cell-properties style:text-align-source="fix" style:repeat-content="false"/>
|
||||||
|
<style:paragraph-properties fo:text-align="center"/>
|
||||||
|
<style:text-properties fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold" fo:font-size="16pt"/>
|
||||||
|
</style:style>
|
||||||
|
<style:style style:name="th" style:family="table-cell" style:parent-style-name="default">
|
||||||
|
<style:table-cell-properties style:text-align-source="fix" style:repeat-content="false"/>
|
||||||
|
<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>
|
||||||
|
</office:styles>
|
||||||
|
</office:document-styles>
|
||||||
|
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
var meta = ZipArchive.CreateEntry("meta.xml");
|
||||||
|
using (var stream = meta.Open()) {
|
||||||
|
using var writer = new StreamWriter(stream, Utils.UTF8);
|
||||||
|
var now = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ");
|
||||||
|
await writer.WriteAsync($"""
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<office:document-meta xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" office:version="1.3">
|
||||||
|
<office:meta>
|
||||||
|
<meta:generator>Elwig {App.Version}</meta:generator>
|
||||||
|
<meta:initial-creator>Elwig</meta:initial-creator>
|
||||||
|
<dc:creator>Elwig</dc:creator>
|
||||||
|
<meta:creation-date>{now}</meta:creation-date>
|
||||||
|
<dc:date>{now}</dc:date>
|
||||||
|
</office:meta>
|
||||||
|
</office:document-meta>
|
||||||
|
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
var content = ZipArchive.CreateEntry("content.xml");
|
||||||
|
Content = new StreamWriter(content.Open(), Utils.UTF8);
|
||||||
|
await Content.WriteAsync("""
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<office:document-content xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" office:version="1.3">
|
||||||
|
<office:body>
|
||||||
|
<office:spreadsheet>
|
||||||
|
<table:calculation-settings table:case-sensitive="false" table:search-criteria-must-apply-to-whole-cell="true" table:use-wildcards="true" table:use-regular-expressions="false" table:automatic-find-labels="false"/>
|
||||||
|
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task AddTrailer() {
|
||||||
|
if (Content == null) await AddHeader();
|
||||||
|
if (Content == null) return;
|
||||||
|
await Content.WriteAsync(" </office:spreadsheet>\r\n </office:body>\r\n</office:document-content>\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task AddTable<T>(DataTable<T> table) {
|
||||||
|
if (Content == null) await AddHeader();
|
||||||
|
if (Content == null) return;
|
||||||
|
var totalSpan = table.ColumnSpans.Sum(s => s.Item2);
|
||||||
|
|
||||||
|
await Content.WriteAsync(
|
||||||
|
$" <table:table table:name=\"{table.FullName}\">\r\n" +
|
||||||
|
$" <table:table-column table:default-cell-style-name=\"default\"/>\r\n" +
|
||||||
|
$" <table:table-row>\r\n" +
|
||||||
|
FormatCell(table.FullName, colSpan: totalSpan, style: "header") +
|
||||||
|
$" </table:table-row>\r\n" +
|
||||||
|
$" <table:table-row>\r\n" +
|
||||||
|
$" <table:table-cell table:number-columns-repeated=\"{totalSpan}\"/>\r\n" +
|
||||||
|
$" </table:table-row>\r\n" +
|
||||||
|
$" <table:table-row>\r\n");
|
||||||
|
foreach (var (name, span) in table.ColumnSpans) {
|
||||||
|
await Content.WriteAsync(FormatCell(name, colSpan: span, style: "th"));
|
||||||
|
}
|
||||||
|
await Content.WriteAsync(" </table:table-row>\r\n");
|
||||||
|
|
||||||
|
foreach (var row in table.GetData()) {
|
||||||
|
await FormatRow(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Content.WriteAsync(" </table:table>\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task FormatRow(IEnumerable<object?> row) {
|
||||||
|
if (Content == null) throw new InvalidOperationException();
|
||||||
|
var arrays = row.Where(c => c is Array).Cast<Array>().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(" <table:table-row>\r\n");
|
||||||
|
foreach (var data in row) {
|
||||||
|
if (data is Array a) {
|
||||||
|
await Content.WriteAsync(i < a.Length ? FormatCell(a.GetValue(i)) : " <table:table-cell/>\r\n");
|
||||||
|
} else {
|
||||||
|
await Content.WriteAsync(i == 0 ? FormatCell(data, rowNum) : " <table:covered-table-cell/>\r\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await Content.WriteAsync(" </table:table-row>\r\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static string FormatCell(object? data, int rowSpan = 1, int colSpan = 1, string? style = "default", bool forceString = false) {
|
||||||
|
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)));
|
||||||
|
|
||||||
|
var add = (style != null ? $" table:style-name=\"{style}\"" : "") + (rowSpan > 1 || colSpan > 1 ? $" table:number-rows-spanned=\"{rowSpan}\" table:number-columns-spanned=\"{colSpan}\"" : "");
|
||||||
|
string c;
|
||||||
|
if (!forceString && data == null) {
|
||||||
|
c = $"<table:table-cell{add}/>";
|
||||||
|
} 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 = $"<table:table-cell office:value-type=\"float\" office:value=\"{data.ToString()?.Replace(",", ".")}\"{add}><text:p>{data}</text:p></table:table-cell>";
|
||||||
|
} else {
|
||||||
|
c = $"<table:table-cell office:value-type=\"string\"{add}><text:p>{data}</text:p></table:table-cell>";
|
||||||
|
}
|
||||||
|
|
||||||
|
return c = $" {c}\r\n" + (colSpan > 1 ? $" <table:covered-table-cell table:number-rows-repeated=\"{colSpan - 1}\"/>\r\n" : "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
@ -9,15 +10,27 @@ namespace Elwig.Models.Dtos {
|
|||||||
public string FullName { get; set; }
|
public string FullName { get; set; }
|
||||||
public IEnumerable<T> Rows { get; private set; }
|
public IEnumerable<T> Rows { get; private set; }
|
||||||
public int RowNum => Rows.Count();
|
public int RowNum => Rows.Count();
|
||||||
public IEnumerable<string> ColumnNames => _fieldMap.Select(m => m.Item2);
|
public IEnumerable<(string, Type?)> ColumnDefs => _map.Select(m => (m.Item1, m.Item2?.PropertyType ?? m.Item3?.FieldType));
|
||||||
|
public IEnumerable<string> ColumnNames => ColumnDefs.Select(m => m.Item1);
|
||||||
|
public IEnumerable<Type?> ColumnTypes => ColumnDefs.Select(m => m.Item2);
|
||||||
|
public IEnumerable<(string, int)> ColumnSpans => ColumnDefs.Select(c => {
|
||||||
|
var type = c.Item2;
|
||||||
|
var elType = type?.GetElementType();
|
||||||
|
return (c.Item1,
|
||||||
|
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();
|
||||||
public int ColNum => ColumnNames.Count();
|
public int ColNum => ColumnNames.Count();
|
||||||
|
private readonly PropertyInfo[] _properties;
|
||||||
private readonly FieldInfo[] _fields;
|
private readonly FieldInfo[] _fields;
|
||||||
private readonly (FieldInfo, string)[] _fieldMap;
|
private readonly (string, PropertyInfo?, FieldInfo?)[] _map;
|
||||||
|
|
||||||
public DataTable(string name, string fullName, IEnumerable<T> rows, IEnumerable<(string, string)>? colNames = null) {
|
public DataTable(string name, string fullName, IEnumerable<T> rows, IEnumerable<(string, string)>? colNames = null) {
|
||||||
_fields = typeof(T).GetFields();
|
_fields = typeof(T).GetFields();
|
||||||
var dict = colNames?.ToDictionary(n => n.Item1, n => n.Item2);
|
_properties = typeof(T).GetProperties();
|
||||||
_fieldMap = (dict == null ? _fields.Select(f => (f, f.Name)) : _fields.Select(f => (f, dict[f.Name]))).ToArray();
|
colNames ??= _properties.Select(p => p.Name).Union(_fields.Select(f => f.Name)).Select(i => (i, i)).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;
|
Name = name;
|
||||||
FullName = fullName;
|
FullName = fullName;
|
||||||
Rows = rows;
|
Rows = rows;
|
||||||
@ -27,7 +40,7 @@ namespace Elwig.Models.Dtos {
|
|||||||
this(name, name, rows, colNames) { }
|
this(name, name, rows, colNames) { }
|
||||||
|
|
||||||
protected IEnumerable<(string, object?)> GetNamedRowData(T row) {
|
protected IEnumerable<(string, object?)> GetNamedRowData(T row) {
|
||||||
return _fieldMap.Select(i => (i.Item2, i.Item1.GetValue(row)));
|
return _map.Select(i => (i.Item1, i.Item2?.GetValue(row) ?? i.Item3?.GetValue(row)));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected IEnumerable<object?> GetRowData(T row) {
|
protected IEnumerable<object?> GetRowData(T row) {
|
||||||
|
@ -7,23 +7,23 @@ using System.Threading.Tasks;
|
|||||||
namespace Elwig.Models.Dtos {
|
namespace Elwig.Models.Dtos {
|
||||||
public class DeliveryConfirmationData : DataTable<DeliveryConfirmationRow> {
|
public class DeliveryConfirmationData : DataTable<DeliveryConfirmationRow> {
|
||||||
|
|
||||||
private static readonly (string, string)[] _fields = new[] {
|
private static readonly (string, string)[] _fieldNames = new[] {
|
||||||
("LsNr", "LsNr."),
|
("LsNr", "LsNr."),
|
||||||
("DPNr", "Pos."),
|
("DPNr", "Pos."),
|
||||||
("Variant", "Sorte"),
|
("Variant", "Sorte"),
|
||||||
("Attribute", "Attribut"),
|
("Attribute", "Attribut"),
|
||||||
("Modifiers", "Zu-/Abschläge"),
|
("Modifiers", "Zu-/Abschläge"),
|
||||||
("QualityLevel", "Qualitätsstufe"),
|
("QualityLevel", "Qualitätsstufe"),
|
||||||
("GradationOe", "°Oe"),
|
("Oe", "°Oe"),
|
||||||
("GradationKmw", "°KMW"),
|
("Kmw", "°KMW"),
|
||||||
("Commitment", "Flächenbindung"),
|
("Buckets", "Flächenbindung"),
|
||||||
("Weight", "Gewicht"),
|
("Weight", "Gewicht"),
|
||||||
};
|
};
|
||||||
|
|
||||||
public int MgNr { get; private set; }
|
public int MgNr { get; private set; }
|
||||||
|
|
||||||
private DeliveryConfirmationData(IEnumerable<DeliveryConfirmationRow> rows, int mgnr) :
|
private DeliveryConfirmationData(IEnumerable<DeliveryConfirmationRow> rows, int mgnr) :
|
||||||
base("Anl.-Best.", "Anlieferungsbestätigung", rows, _fields) {
|
base("Anl.-Best.", "Anlieferungsbestätigung", rows, _fieldNames) {
|
||||||
MgNr = mgnr;
|
MgNr = mgnr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,16 +61,16 @@ namespace Elwig.Models.Dtos {
|
|||||||
|
|
||||||
public class DeliveryConfirmationRow {
|
public class DeliveryConfirmationRow {
|
||||||
|
|
||||||
public string LsNr { get; set; }
|
public string LsNr;
|
||||||
public int DPNr { get; set; }
|
public int DPNr;
|
||||||
public string Variant { get; set; }
|
public string Variant;
|
||||||
public string? Attribute { get; set; }
|
public string? Attribute;
|
||||||
public string QualityLevel { get; set; }
|
public string QualityLevel;
|
||||||
public double Oe { get; set; }
|
public double Oe;
|
||||||
public double Kmw { get; set; }
|
public double Kmw;
|
||||||
public string[] Modifiers { get; set; }
|
public string[] Modifiers;
|
||||||
public int Weight { get; set; }
|
public int Weight;
|
||||||
public (string, int)[] Buckets { get; set; }
|
public (string, int)[] Buckets;
|
||||||
|
|
||||||
public DeliveryConfirmationRow(DeliveryPart p) {
|
public DeliveryConfirmationRow(DeliveryPart p) {
|
||||||
var d = p.Delivery;
|
var d = p.Delivery;
|
||||||
|
@ -18,6 +18,8 @@
|
|||||||
<TextBlock x:Name="Output" Height="20" Width="200" Margin="470,329,0,0" HorizontalAlignment="Left" VerticalAlignment="Top"/>
|
<TextBlock x:Name="Output" Height="20" Width="200" Margin="470,329,0,0" HorizontalAlignment="Left" VerticalAlignment="Top"/>
|
||||||
<Button x:Name="ChartButton" Content="Chart" Click="ChartButton_Click"
|
<Button x:Name="ChartButton" Content="Chart" Click="ChartButton_Click"
|
||||||
Margin="50,240,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/>
|
Margin="50,240,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/>
|
||||||
|
<Button x:Name="ZipButton" Content="ZIP-File" Click="ZipButton_Click"
|
||||||
|
Margin="50,270,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/>
|
||||||
|
|
||||||
<Button x:Name="PdfDeliveryButton" Content="Lieferschein Erzeugen" Click="PdfDeliveryButton_Click" Tag="Print" IsEnabled="False"
|
<Button x:Name="PdfDeliveryButton" Content="Lieferschein Erzeugen" Click="PdfDeliveryButton_Click" Tag="Print" IsEnabled="False"
|
||||||
Margin="260,190,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/>
|
Margin="260,190,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/>
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
using Elwig.Documents;
|
using Elwig.Documents;
|
||||||
using Elwig.Helpers;
|
using Elwig.Helpers;
|
||||||
using Elwig.Helpers.Billing;
|
using Elwig.Helpers.Billing;
|
||||||
|
using Elwig.Helpers.Export;
|
||||||
|
using Elwig.Models.Dtos;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
@ -51,6 +55,12 @@ namespace Elwig.Windows {
|
|||||||
w.Show();
|
w.Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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, 2948));
|
||||||
|
}
|
||||||
|
|
||||||
private async void PdfDeliveryButton_Click(object sender, RoutedEventArgs evt) {
|
private async void PdfDeliveryButton_Click(object sender, RoutedEventArgs evt) {
|
||||||
Mouse.OverrideCursor = Cursors.AppStarting;
|
Mouse.OverrideCursor = Cursors.AppStarting;
|
||||||
using var ctx = new AppDbContext();
|
using var ctx = new AppDbContext();
|
||||||
|
Reference in New Issue
Block a user