Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ea693f5bb | ||
|
|
c84cc25f8f | ||
|
|
d44d82a614 | ||
|
|
bc093f31db |
No files matched your search
@@ -2,7 +2,7 @@ name: Test
|
||||
on:
|
||||
push:
|
||||
branches: ["**"]
|
||||
paths: ["Elwig/**", "Tests/**", ".gitea/workflows/test.yaml"]
|
||||
paths: ["Elwig/**", "Tests/**", "Installer/Files/*.exe", ".gitea/workflows/test.yaml"]
|
||||
jobs:
|
||||
test:
|
||||
name: Run tests
|
||||
|
||||
+1
-57
@@ -2,62 +2,6 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
[v1.1.0.3][v1.1.0.3] (2026-08-17) {#v1.1.0.3}
|
||||
---------------------------------------------
|
||||
|
||||
### Sonstiges {#v1.1.0.3-misc}
|
||||
|
||||
* Beim Drucken mehrerer Kopien einer PDF-Datei wird nun nur mehr ein einzelner Druck-Auftrag verwendet. (fc5ad82686)
|
||||
* Beim Synchronisieren können Geräte als Hauptgerät festgelegt werden. (eaddf0c3cb)
|
||||
* Abhängigkeiten aktualisiert. (e6e140dc08, 58bfb4f56d)
|
||||
|
||||
[v1.1.0.3]: https://git.necronda.net/winzer/elwig/releases/tag/v1.1.0.3
|
||||
|
||||
|
||||
|
||||
|
||||
[v1.1.0.2][v1.1.0.2] (2026-08-11) {#v1.1.0.2}
|
||||
---------------------------------------------
|
||||
|
||||
### Neue Funktionen {#v1.1.0.2-features}
|
||||
|
||||
* Es ist nun möglich zwischen dem Senden von E-Mail einige Sekunden zu warten (Konfigurationsdatei -> `[smtp]` -> `wait = 0.5`). (76ea3513ac)
|
||||
|
||||
### Behobene Fehler {#v1.1.0.2-bugfixes}
|
||||
|
||||
* Beim Drucken von Dokumenten waren die Seitenränder nicht korrekt. (f13a836ebd, 20b19775b1, 470c6e09e5, 07b197958d)
|
||||
|
||||
[v1.1.0.2]: https://git.necronda.net/winzer/elwig/releases/tag/v1.1.0.2
|
||||
|
||||
|
||||
|
||||
|
||||
[v1.1.0.1][v1.1.0.1] (2026-08-05) {#v1.1.0.1}
|
||||
---------------------------------------------
|
||||
|
||||
### Neue Funktionen {#v1.1.0.1-features}
|
||||
|
||||
* Auf Lieferscheinen sind nun Waage und Terminal angegeben. (309f118739)
|
||||
* Beim Starten von Elwig wird Datum/Uhrzeit auf allen angeschlossenen Waagen gesetzt. (2a3999f300)
|
||||
|
||||
### Behobene Fehler {#v1.1.0.1-bugfixes}
|
||||
|
||||
* Im Stammdaten-Fenster (`BaseDataWindow`) war es nach einem Fehler beim Bearbeiten bis zum Schließen des Fensters nicht mehr möglich zu speichern. ([#82][i82])
|
||||
|
||||
### Sonstiges {#v1.1.0.1-misc}
|
||||
|
||||
* Im Auszahlungsvarianten-Fenster (`PaymentVariantsWindow`)...
|
||||
* wurde auf 3 Knöpfen das Symbol "in neuem Fenster öffnen" hinzugefügt. (88616b2c0b)
|
||||
* wird beim "Speichern" die Variante automatisch auch berechnet um Fehler/Missverständnisse zu vermeiden. ([#83][i83])
|
||||
* Abhängigkeiten aktualisiert. (b445928c4d, 668030fbff, a8cc3c9cb9)
|
||||
|
||||
[v1.1.0.1]: https://git.necronda.net/winzer/elwig/releases/tag/v1.1.0.1
|
||||
[i82]: https://git.necronda.net/winzer/elwig/issues/82
|
||||
[i83]: https://git.necronda.net/winzer/elwig/issues/83
|
||||
|
||||
|
||||
|
||||
|
||||
[v1.1.0.0][v1.1.0.0] (2026-07-09) {#v1.1.0.0}
|
||||
---------------------------------------------
|
||||
|
||||
@@ -346,7 +290,7 @@ Changelog
|
||||
|
||||
[v1.0.3.0]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.3.0
|
||||
[i50]: https://git.necronda.net/winzer/elwig/issues/50
|
||||
[i66]: https://git.necronda.net/winzer/elwig/issues/66
|
||||
[i66]: https://git.necronda.net/winzer/elwig/issues/60
|
||||
[i71]: https://git.necronda.net/winzer/elwig/issues/71
|
||||
[i73]: https://git.necronda.net/winzer/elwig/issues/73
|
||||
|
||||
|
||||
@@ -188,7 +188,7 @@ namespace Elwig.Documents {
|
||||
if (info.Date != null || info.Time != null)
|
||||
weighing.Add(Normal(" \u2013 "));
|
||||
if (info.Time != null)
|
||||
weighing.Add(Normal($"{info.Time:HH:mm}"));
|
||||
weighing.Add(Normal($"{info.Time:HH:mm:ss}"));
|
||||
if (info.Date != null)
|
||||
weighing.Add(Normal($", {info.Date:dd.MM.yyyy}"));
|
||||
if (info.Gross != null && info.Tare != null && info.Net != null) {
|
||||
|
||||
+10
-10
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
@@ -9,10 +9,10 @@
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||
<ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon>
|
||||
<Version>1.1.0.3</Version>
|
||||
<Version>1.1.0.0</Version>
|
||||
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<ApplicationManifest>App.manifest</ApplicationManifest>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -23,21 +23,21 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="bblanchon.PDFium.Win32" Version="153.0.8009" />
|
||||
<PackageReference Include="bblanchon.PDFium.Win32" Version="152.0.7961" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.2" />
|
||||
<PackageReference Include="itext" Version="9.7.0" />
|
||||
<PackageReference Include="itext.bouncy-castle-adapter" Version="9.7.0" />
|
||||
<PackageReference Include="LinqKit" Version="1.3.11" />
|
||||
<PackageReference Include="MailKit" Version="4.17.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.11" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="10.0.11" />
|
||||
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.4129.50" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="10.0.10" />
|
||||
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.4078.44" />
|
||||
<PackageReference Include="NJsonSchema" Version="11.6.1" />
|
||||
<PackageReference Include="ScottPlot.WPF" Version="5.1.59" />
|
||||
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="3.0.5" />
|
||||
<PackageReference Include="System.IO.Hashing" Version="10.0.11" />
|
||||
<PackageReference Include="System.IO.Ports" Version="10.0.11" />
|
||||
<PackageReference Include="System.Management" Version="10.0.11" />
|
||||
<PackageReference Include="System.IO.Hashing" Version="10.0.10" />
|
||||
<PackageReference Include="System.IO.Ports" Version="10.0.10" />
|
||||
<PackageReference Include="System.Management" Version="10.0.10" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -50,7 +50,6 @@ namespace Elwig.Helpers {
|
||||
public string? SyncUrl = null;
|
||||
public string SyncUsername = "";
|
||||
public string SyncPassword = "";
|
||||
public bool SyncMain = false;
|
||||
|
||||
public string? SmtpHost = null;
|
||||
public int? SmtpPort = null;
|
||||
@@ -58,10 +57,9 @@ namespace Elwig.Helpers {
|
||||
public string? SmtpUsername = null;
|
||||
public string? SmtpPassword = null;
|
||||
public string? SmtpFrom = null;
|
||||
public double SmtpWait = 0;
|
||||
public (string Host, int Port, string Mode, string Username, string Password, string From, double Wait)? Smtp =>
|
||||
public (string Host, int Port, string Mode, string Username, string Password, string From)? Smtp =>
|
||||
SmtpHost == null || SmtpPort == null || SmtpMode == null || SmtpUsername == null || SmtpPassword == null || SmtpFrom == null ?
|
||||
null : (SmtpHost, (int)SmtpPort, SmtpMode, SmtpUsername, SmtpPassword, SmtpFrom, SmtpWait);
|
||||
null : (SmtpHost, (int)SmtpPort, SmtpMode, SmtpUsername, SmtpPassword, SmtpFrom);
|
||||
|
||||
public IList<ScaleConfig> Scales;
|
||||
private readonly List<ScaleConfig> ScaleList = [];
|
||||
@@ -87,7 +85,6 @@ namespace Elwig.Helpers {
|
||||
SyncUrl = config["sync:url"];
|
||||
SyncUsername = config["sync:username"] ?? "";
|
||||
SyncPassword = config["sync:password"] ?? "";
|
||||
SyncMain = TrueValues.Contains(config["sync:main"]?.ToLower());
|
||||
|
||||
SmtpHost = config["smtp:host"];
|
||||
SmtpPort = config["smtp:port"]?.All(char.IsAsciiDigit) == true && config["smtp:port"]?.Length > 0 ? int.Parse(config["smtp:port"]!) : null;
|
||||
@@ -95,7 +92,6 @@ namespace Elwig.Helpers {
|
||||
SmtpUsername = config["smtp:username"];
|
||||
SmtpPassword = config["smtp:password"];
|
||||
SmtpFrom = config["smtp:from"];
|
||||
SmtpWait = double.TryParse(config["smtp:wait"], out var res) ? res : 0;
|
||||
|
||||
var scales = config.AsEnumerable().Where(i => i.Key.StartsWith("scale.")).GroupBy(i => i.Key.Split(':')[0][6..]).Select(i => i.Key).Order();
|
||||
ScaleList.Clear();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Elwig.Services;
|
||||
using Elwig.Windows;
|
||||
using System;
|
||||
using System.Drawing.Printing;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elwig.Helpers.Printing {
|
||||
@@ -31,10 +32,20 @@ namespace Elwig.Helpers.Printing {
|
||||
});
|
||||
}
|
||||
|
||||
public static Task Print(string path, int copies = 1, bool doublePaged = false) {
|
||||
public static async Task Print(string path, int copies = 1, bool doublePaged = false) {
|
||||
await Print(path, new() {
|
||||
Copies = (short)copies,
|
||||
Collate = true,
|
||||
Duplex = doublePaged ? Duplex.Vertical : Duplex.Simplex,
|
||||
});
|
||||
}
|
||||
|
||||
public static Task Print(string path, PrinterSettings settings) {
|
||||
try {
|
||||
var printer = new PdfPrinter();
|
||||
printer.Print(path, copies, doublePaged);
|
||||
using var printDoc = new PdfPrintDocument(path) {
|
||||
PrinterSettings = settings,
|
||||
};
|
||||
printDoc.Print();
|
||||
} catch (Exception exc) {
|
||||
InteractionService.ShowException("Fehler beim Drucken", "Beim Drucken ist ein Fehler aufgetreten", exc);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Drawing.Printing;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Elwig.Helpers.Printing {
|
||||
public class PdfPrintDocument : PrintDocument {
|
||||
|
||||
private readonly IntPtr _handle;
|
||||
private readonly int _pageCount;
|
||||
private readonly double _dpi;
|
||||
|
||||
private int _currentPage;
|
||||
|
||||
public PdfPrintDocument(string path, string? password = null, double dpi = 300.0) :
|
||||
base() {
|
||||
_handle = PdfiumNative.FPDF_LoadDocument(path, password);
|
||||
_pageCount = PdfiumNative.FPDF_GetPageCount(_handle);
|
||||
_dpi = dpi;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing) {
|
||||
PdfiumNative.FPDF_CloseDocument(_handle);
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
protected override void OnBeginPrint(PrintEventArgs evt) {
|
||||
_currentPage = (PrinterSettings.FromPage != 0) ? (PrinterSettings.FromPage - 1) : 0;
|
||||
base.OnBeginPrint(evt);
|
||||
}
|
||||
|
||||
protected override void OnPrintPage(PrintPageEventArgs evt) {
|
||||
if (_currentPage < _pageCount) {
|
||||
IntPtr page = PdfiumNative.FPDF_LoadPage(_handle, _currentPage);
|
||||
double width = PdfiumNative.FPDF_GetPageWidth(page);
|
||||
double height = PdfiumNative.FPDF_GetPageHeight(page);
|
||||
int pixelWidth = (int)(width / 72.0 * _dpi);
|
||||
int pixelHeight = (int)(height / 72.0 * _dpi);
|
||||
|
||||
IntPtr bitmap = PdfiumNative.FPDFBitmap_Create(pixelWidth, pixelHeight, 1);
|
||||
PdfiumNative.FPDF_RenderPageBitmap(bitmap, page, 0, 0, pixelWidth, pixelHeight, 0, 0);
|
||||
|
||||
IntPtr buffer = PdfiumNative.FPDFBitmap_GetBuffer(bitmap);
|
||||
int stride = PdfiumNative.FPDFBitmap_GetStride(bitmap);
|
||||
using (var bmp = new Bitmap(pixelWidth, pixelHeight, stride, PixelFormat.Format32bppArgb, buffer)) {
|
||||
evt.Graphics?.DrawImage(bmp, evt.PageBounds);
|
||||
}
|
||||
_currentPage++;
|
||||
|
||||
PdfiumNative.FPDFBitmap_Destroy(bitmap);
|
||||
PdfiumNative.FPDF_ClosePage(page);
|
||||
}
|
||||
evt.HasMorePages = _currentPage < ((PrinterSettings.ToPage == 0) ? _pageCount : Math.Min(PrinterSettings.ToPage, _pageCount));
|
||||
base.OnPrintPage(evt);
|
||||
}
|
||||
}
|
||||
|
||||
internal partial class PdfiumNative {
|
||||
[LibraryImport("pdfium.dll")]
|
||||
public static partial void FPDF_InitLibrary();
|
||||
|
||||
[LibraryImport("pdfium.dll")]
|
||||
public static partial void FPDF_DestroyLibrary();
|
||||
|
||||
[LibraryImport("pdfium.dll", StringMarshalling = StringMarshalling.Utf8)]
|
||||
public static partial IntPtr FPDF_LoadDocument(string filePath, string? password);
|
||||
|
||||
[LibraryImport("pdfium.dll")]
|
||||
public static partial void FPDF_CloseDocument(IntPtr document);
|
||||
|
||||
[LibraryImport("pdfium.dll")]
|
||||
public static partial int FPDF_GetPageCount(IntPtr document);
|
||||
|
||||
[LibraryImport("pdfium.dll")]
|
||||
public static partial IntPtr FPDF_LoadPage(IntPtr document, int pageIndex);
|
||||
|
||||
[LibraryImport("pdfium.dll")]
|
||||
public static partial void FPDF_ClosePage(IntPtr page);
|
||||
|
||||
[LibraryImport("pdfium.dll")]
|
||||
public static partial double FPDF_GetPageWidth(IntPtr page);
|
||||
|
||||
[LibraryImport("pdfium.dll")]
|
||||
public static partial double FPDF_GetPageHeight(IntPtr page);
|
||||
|
||||
[LibraryImport("pdfium.dll")]
|
||||
public static partial IntPtr FPDFBitmap_Create(int width, int height, int alpha);
|
||||
|
||||
[LibraryImport("pdfium.dll")]
|
||||
public static partial void FPDFBitmap_Destroy(IntPtr bitmap);
|
||||
|
||||
[LibraryImport("pdfium.dll")]
|
||||
public static partial IntPtr FPDFBitmap_GetBuffer(IntPtr bitmap);
|
||||
|
||||
[LibraryImport("pdfium.dll")]
|
||||
public static partial int FPDFBitmap_GetStride(IntPtr bitmap);
|
||||
|
||||
[LibraryImport("pdfium.dll")]
|
||||
public static partial void FPDF_RenderPageBitmap(IntPtr bitmap, IntPtr page, int start_x, int start_y, int size_x, int size_y, int rotate, int flags);
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Printing;
|
||||
|
||||
namespace Elwig.Helpers.Printing {
|
||||
public class PdfPrinter {
|
||||
|
||||
public string PrinterName { get; init; }
|
||||
|
||||
public PdfPrinter(string? printerName = null) {
|
||||
PrinterName = printerName ?? GetDefaultPrinterName() ?? throw new InvalidOperationException("No default Windows printer is configured.");
|
||||
}
|
||||
|
||||
public void Print(string pdfPath, int copies = 1, bool doublePaged = false) {
|
||||
if (!File.Exists(pdfPath)) throw new FileNotFoundException("PDF file not found.", pdfPath);
|
||||
|
||||
IntPtr document = PdfiumNative.FPDF_LoadDocument(pdfPath, null);
|
||||
if (document == IntPtr.Zero) throw new InvalidOperationException("PDFium could not open the PDF.");
|
||||
|
||||
try {
|
||||
int pageCount = PdfiumNative.FPDF_GetPageCount(document);
|
||||
if (pageCount <= 0)
|
||||
return;
|
||||
|
||||
using var printer = new Win32Printer.Printer(PrinterName);
|
||||
printer.TrySetDuplex(doublePaged ? Duplexing.TwoSidedLongEdge : Duplexing.OneSided);
|
||||
printer.StartDocument(Path.GetFileName(pdfPath));
|
||||
bool documentStarted = true;
|
||||
try {
|
||||
for (int i = 0; i < copies; i++) {
|
||||
for (int j = 0; j < pageCount; j++) {
|
||||
PrintPage(document, printer, j);
|
||||
}
|
||||
}
|
||||
printer.EndDocument();
|
||||
documentStarted = false;
|
||||
} finally {
|
||||
if (documentStarted) printer.AbortDocument();
|
||||
}
|
||||
} finally {
|
||||
PdfiumNative.FPDF_CloseDocument(document);
|
||||
}
|
||||
}
|
||||
|
||||
private static void PrintPage(IntPtr document, Win32Printer.Printer printer, int pageIndex) {
|
||||
if (!PdfiumNative.FPDF_GetPageSizeByIndex(document, pageIndex, out double pageWidthPt, out double pageHeightPt)) {
|
||||
throw new InvalidOperationException($"Unable to obtain PDF page {pageIndex} size.");
|
||||
}
|
||||
|
||||
IntPtr page = PdfiumNative.FPDF_LoadPage(document, pageIndex);
|
||||
if (page == IntPtr.Zero)
|
||||
throw new InvalidOperationException($"Unable to load PDF page {pageIndex}.");
|
||||
|
||||
try {
|
||||
int renderWidth = (int)Math.Round(pageWidthPt / 72.0 * printer.DpiX);
|
||||
int renderHeight = (int)Math.Round(pageHeightPt / 72.0 * printer.DpiY);
|
||||
int offsetX = (printer.PrintableWidth - renderWidth) / 2;
|
||||
int offsetY = (printer.PrintableHeight - renderHeight) / 2;
|
||||
|
||||
printer.StartPage();
|
||||
try {
|
||||
PdfiumNative.FPDF_RenderPage(printer.Hdc, page, offsetX, offsetY, renderWidth, renderHeight, 0, PdfiumNative.FPDF_PRINTING | PdfiumNative.FPDF_ANNOT);
|
||||
} finally {
|
||||
printer.EndPage();
|
||||
}
|
||||
} finally {
|
||||
PdfiumNative.FPDF_ClosePage(page);
|
||||
}
|
||||
}
|
||||
|
||||
private static string? GetDefaultPrinterName() {
|
||||
using var key = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows NT\CurrentVersion\Windows");
|
||||
string? printer = key?.GetValue("Device") as string;
|
||||
if (string.IsNullOrWhiteSpace(printer))
|
||||
return null;
|
||||
|
||||
// Device has the form:
|
||||
// Printer Name,winspool,PORT
|
||||
int comma = printer.IndexOf(',');
|
||||
return comma >= 0 ? printer[..comma] : printer;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Elwig.Helpers.Printing {
|
||||
internal partial class PdfiumNative {
|
||||
|
||||
public const int FPDF_ANNOT = 0x01;
|
||||
public const int FPDF_PRINTING = 0x800;
|
||||
|
||||
[LibraryImport("pdfium.dll")]
|
||||
public static partial void FPDF_InitLibrary();
|
||||
|
||||
[LibraryImport("pdfium.dll")]
|
||||
public static partial void FPDF_DestroyLibrary();
|
||||
|
||||
[LibraryImport("pdfium.dll", StringMarshalling = StringMarshalling.Utf8)]
|
||||
public static partial IntPtr FPDF_LoadDocument(string filePath, string? password);
|
||||
|
||||
[LibraryImport("pdfium.dll")]
|
||||
public static partial void FPDF_CloseDocument(IntPtr document);
|
||||
|
||||
[LibraryImport("pdfium.dll")]
|
||||
public static partial int FPDF_GetPageCount(IntPtr document);
|
||||
|
||||
[LibraryImport("pdfium.dll")]
|
||||
public static partial IntPtr FPDF_LoadPage(IntPtr document, int pageIndex);
|
||||
|
||||
[LibraryImport("pdfium.dll")]
|
||||
public static partial void FPDF_ClosePage(IntPtr page);
|
||||
|
||||
[LibraryImport("pdfium.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static partial bool FPDF_GetPageSizeByIndex(IntPtr document, int pageIndex, out double width, out double height);
|
||||
|
||||
[LibraryImport("pdfium.dll")]
|
||||
public static partial void FPDF_RenderPage(IntPtr hdc, IntPtr page, int startX, int startY, int sizeX, int sizeY, int rotate, int flags);
|
||||
}
|
||||
}
|
||||
@@ -1,239 +0,0 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Printing;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Elwig.Helpers.Printing {
|
||||
public partial class Win32Printer {
|
||||
|
||||
private const int DM_OUT_BUFFER = 2;
|
||||
|
||||
private const int HORZRES = 8;
|
||||
private const int VERTRES = 10;
|
||||
private const int LOGPIXELSX = 88;
|
||||
private const int LOGPIXELSY = 90;
|
||||
private const int PHYSICALWIDTH = 110;
|
||||
private const int PHYSICALHEIGHT = 111;
|
||||
private const int PHYSICALOFFSETX = 112;
|
||||
private const int PHYSICALOFFSETY = 113;
|
||||
|
||||
private const int DM_DUPLEX = 0x1000;
|
||||
private const short DMDUP_SIMPLEX = 1;
|
||||
private const short DMDUP_VERTICAL = 2;
|
||||
private const short DMDUP_HORIZONTAL = 3;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
private struct DOCINFO {
|
||||
public int cbSize;
|
||||
|
||||
[MarshalAs(UnmanagedType.LPWStr)]
|
||||
public string? lpszDocName;
|
||||
|
||||
[MarshalAs(UnmanagedType.LPWStr)]
|
||||
public string? lpszOutput;
|
||||
|
||||
[MarshalAs(UnmanagedType.LPWStr)]
|
||||
public string? lpszDatatype;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
private struct DEVMODE {
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
|
||||
public string dmDeviceName;
|
||||
|
||||
public short dmSpecVersion;
|
||||
public short dmDriverVersion;
|
||||
public short dmSize;
|
||||
public short dmDriverExtra;
|
||||
public int dmFields;
|
||||
|
||||
public short dmOrientation;
|
||||
public short dmPaperSize;
|
||||
public short dmPaperLength;
|
||||
public short dmPaperWidth;
|
||||
public short dmScale;
|
||||
public short dmCopies;
|
||||
public short dmDefaultSource;
|
||||
public short dmPrintQuality;
|
||||
public short dmColor;
|
||||
public short dmDuplex;
|
||||
public short dmYResolution;
|
||||
public short dmTTOption;
|
||||
public short dmCollate;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
|
||||
public string dmFormName;
|
||||
|
||||
public short dmLogPixels;
|
||||
public int dmBitsPerPel;
|
||||
public int dmPelsWidth;
|
||||
public int dmPelsHeight;
|
||||
public int dmDisplayFlags;
|
||||
public int dmDisplayFrequency;
|
||||
public int dmICMMethod;
|
||||
public int dmICMIntent;
|
||||
public int dmMediaType;
|
||||
public int dmDitherType;
|
||||
public int dmReserved1;
|
||||
public int dmReserved2;
|
||||
public int dmPanningWidth;
|
||||
public int dmPanningHeight;
|
||||
}
|
||||
|
||||
[DllImport("winspool.drv", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
private static extern bool OpenPrinter(string pPrinterName, out IntPtr phPrinter, IntPtr pDefault);
|
||||
|
||||
[DllImport("winspool.drv", SetLastError = true)]
|
||||
private static extern bool ClosePrinter(IntPtr hPrinter);
|
||||
|
||||
[DllImport("winspool.drv", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
private static extern int DocumentProperties(IntPtr hwnd, IntPtr hPrinter, string pDeviceName, IntPtr pDevModeOutput, IntPtr pDevModeInput, int fMode);
|
||||
|
||||
[DllImport("gdi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
private static extern IntPtr CreateDC(string? pDriver, string? pDevice, string? pOutput, IntPtr pInitData);
|
||||
|
||||
[DllImport("gdi32.dll", SetLastError = true)]
|
||||
private static extern bool DeleteDC(IntPtr hdc);
|
||||
|
||||
[DllImport("gdi32.dll", SetLastError = true)]
|
||||
private static extern int GetDeviceCaps(IntPtr hdc, int index);
|
||||
|
||||
[DllImport("gdi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
private static extern int StartDoc(IntPtr hdc, ref DOCINFO lpdi);
|
||||
|
||||
[DllImport("gdi32.dll", SetLastError = true)]
|
||||
private static extern int EndDoc(IntPtr hdc);
|
||||
|
||||
[DllImport("gdi32.dll", SetLastError = true)]
|
||||
private static extern int AbortDoc(IntPtr hdc);
|
||||
|
||||
[DllImport("gdi32.dll", SetLastError = true)]
|
||||
private static extern int StartPage(IntPtr hdc);
|
||||
|
||||
[DllImport("gdi32.dll", SetLastError = true)]
|
||||
private static extern int EndPage(IntPtr hdc);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern IntPtr GlobalAlloc(uint uFlags, UIntPtr dwBytes);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern IntPtr GlobalFree(IntPtr hMem);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern IntPtr GlobalLock(IntPtr hMem);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern bool GlobalUnlock(IntPtr hMem);
|
||||
|
||||
public class Printer : IDisposable {
|
||||
private readonly string _printerName;
|
||||
private readonly IntPtr _printerHandle;
|
||||
private readonly IntPtr _devMode;
|
||||
private readonly IntPtr _hdc;
|
||||
|
||||
public IntPtr Hdc => _hdc;
|
||||
|
||||
public int PrintableWidth => GetDeviceCaps(_hdc, HORZRES);
|
||||
public int PrintableHeight => GetDeviceCaps(_hdc, VERTRES);
|
||||
public int DpiX => GetDeviceCaps(_hdc, LOGPIXELSX);
|
||||
public int DpiY => GetDeviceCaps(_hdc, LOGPIXELSY);
|
||||
public int PhysicalWidth => GetDeviceCaps(_hdc, PHYSICALWIDTH);
|
||||
public int PhysicalHeight => GetDeviceCaps(_hdc, PHYSICALHEIGHT);
|
||||
public int PhysicalOffsetX => GetDeviceCaps(_hdc, PHYSICALOFFSETX);
|
||||
public int PhysicalOffsetY => GetDeviceCaps(_hdc, PHYSICALOFFSETY);
|
||||
|
||||
public Printer(string printerName) {
|
||||
_printerName = printerName;
|
||||
|
||||
if (!OpenPrinter(_printerName, out _printerHandle, IntPtr.Zero)) {
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error(), $"Unable to open printer '{printerName}'.");
|
||||
}
|
||||
|
||||
try {
|
||||
_devMode = CreateDevMode();
|
||||
_hdc = CreateDC(null, _printerName, null, _devMode);
|
||||
if (_hdc == IntPtr.Zero)
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error(), $"Unable to create printer DC for '{_printerName}'.");
|
||||
} catch {
|
||||
if (_devMode != IntPtr.Zero) Marshal.FreeHGlobal(_devMode);
|
||||
ClosePrinter(_printerHandle);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public bool TrySetDuplex(Duplexing duplex) {
|
||||
var devMode = Marshal.PtrToStructure<DEVMODE>(_devMode);
|
||||
switch (duplex) {
|
||||
case Duplexing.OneSided: devMode.dmDuplex = DMDUP_SIMPLEX; break;
|
||||
case Duplexing.TwoSidedShortEdge: devMode.dmDuplex = DMDUP_HORIZONTAL; break;
|
||||
case Duplexing.TwoSidedLongEdge: devMode.dmDuplex = DMDUP_VERTICAL; break;
|
||||
}
|
||||
if ((devMode.dmFields & DM_DUPLEX) != 0) {
|
||||
Marshal.StructureToPtr(devMode, _devMode, false);
|
||||
return true;
|
||||
} else {
|
||||
devMode.dmFields |= DM_DUPLEX;
|
||||
Marshal.StructureToPtr(devMode, _devMode, false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private IntPtr CreateDevMode() {
|
||||
int size = DocumentProperties(IntPtr.Zero, _printerHandle, _printerName, IntPtr.Zero, IntPtr.Zero, 0);
|
||||
if (size <= 0)
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error(), "DocumentProperties failed.");
|
||||
|
||||
IntPtr devMode = Marshal.AllocHGlobal(size);
|
||||
int result = DocumentProperties(IntPtr.Zero, _printerHandle, _printerName, devMode, IntPtr.Zero, DM_OUT_BUFFER);
|
||||
if (result < 0) {
|
||||
Marshal.FreeHGlobal(devMode);
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error(), "Unable to obtain printer DEVMODE.");
|
||||
}
|
||||
|
||||
return devMode;
|
||||
}
|
||||
|
||||
public void StartDocument(string documentName) {
|
||||
var docInfo = new DOCINFO {
|
||||
cbSize = Marshal.SizeOf<DOCINFO>(),
|
||||
lpszDocName = documentName,
|
||||
lpszDatatype = null,
|
||||
lpszOutput = null
|
||||
};
|
||||
|
||||
if (StartDoc(_hdc, ref docInfo) <= 0)
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error(), "StartDoc failed.");
|
||||
}
|
||||
|
||||
public void StartPage() {
|
||||
if (Win32Printer.StartPage(_hdc) <= 0)
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error(), "StartPage failed.");
|
||||
}
|
||||
|
||||
public void EndPage() {
|
||||
if (Win32Printer.EndPage(_hdc) <= 0)
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error(), "EndPage failed.");
|
||||
}
|
||||
|
||||
public void EndDocument() {
|
||||
if (EndDoc(_hdc) <= 0)
|
||||
throw new Win32Exception( Marshal.GetLastWin32Error(), "EndDoc failed.");
|
||||
}
|
||||
|
||||
public void AbortDocument() {
|
||||
AbortDoc(_hdc);
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
if (_hdc != IntPtr.Zero)
|
||||
DeleteDC(_hdc);
|
||||
|
||||
if (_devMode != IntPtr.Zero)
|
||||
Marshal.FreeHGlobal(_devMode);
|
||||
|
||||
if (_printerHandle != IntPtr.Zero)
|
||||
ClosePrinter(_printerHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -516,7 +516,7 @@ namespace Elwig.Helpers {
|
||||
public static async Task<SmtpClient?> GetSmtpClient() {
|
||||
if (App.Config.Smtp == null)
|
||||
return null;
|
||||
var (host, port, mode, username, password, _, _) = App.Config.Smtp.Value;
|
||||
var (host, port, mode, username, password, _) = App.Config.Smtp.Value;
|
||||
var client = new SmtpClient();
|
||||
await client.ConnectAsync(host, port, mode == "starttls" ? SecureSocketOptions.StartTls : SecureSocketOptions.None);
|
||||
await client.AuthenticateAsync(username, password);
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace Elwig.Helpers.Weighing {
|
||||
return new SysTecITScale(config.Id, config.Model!, config.Connection!, config.Empty, config.Filling, limit, config.Log);
|
||||
} else if (config.Type == "Gassner-Enq" || config.Type == "Gassner") {
|
||||
return new GassnerEnqScale(config.Id, config.Model!, config.Connection!, config.Empty, config.Filling, limit, config.Log);
|
||||
} else if (config.Type == "Avery-Event" || config.Type == "Avery-Async") {
|
||||
} else if (config.Type == "Avery-Async") {
|
||||
return new AveryEventScale(config.Id, config.Model!, config.Connection!, config.Log, config.Required);
|
||||
} else {
|
||||
throw new ArgumentException($"Invalid scale type: \"{config.Type}\"");
|
||||
|
||||
@@ -214,7 +214,7 @@ namespace Elwig.Services {
|
||||
];
|
||||
}
|
||||
|
||||
public static async Task Download(string url, string username, string password, bool confirmImports) {
|
||||
public static async Task Download(string url, string username, string password) {
|
||||
try {
|
||||
var import = await GetFilesToImport(url, username, password);
|
||||
var paths = new List<string>();
|
||||
@@ -226,7 +226,7 @@ namespace Elwig.Services {
|
||||
paths.Add(filename);
|
||||
}
|
||||
}
|
||||
await ElwigData.Import(paths, confirmImports ? ElwigData.ImportMode.Interactively : ElwigData.ImportMode.FromBranches);
|
||||
await ElwigData.Import(paths, ElwigData.ImportMode.FromBranches);
|
||||
} catch (HttpRequestException exc) {
|
||||
InteractionService.ShowException("Daten herunterladen", "Eventuell Internetverbindung prüfen!", exc);
|
||||
} catch (TaskCanceledException exc) {
|
||||
@@ -264,14 +264,5 @@ namespace Elwig.Services {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<bool> ChangesAvailable(AppDbContext ctx, string url, string username, string password, int? year = null) {
|
||||
try {
|
||||
year ??= Utils.CurrentLastSeason;
|
||||
return await ctx.Deliveries.Where(d => d.ZwstId == App.ZwstId && d.Year == year).Where(ChangedDeliveries).AnyAsync() || (Utils.HasInternetConnectivity() && (await GetFilesToImport(url, username, password)).Count > 0);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,26 +47,26 @@ namespace Elwig.Windows {
|
||||
if (!_actChanged || _actList == null || _acts == null || _actIds == null)
|
||||
return;
|
||||
|
||||
// delete
|
||||
var delete = _acts.Where(a => a.Value == null).Select(a => a.Key).ToList();
|
||||
await ctx.AreaCommitmentTypes.Where(v => delete.Contains(v.VtrgId)).ExecuteDeleteAsync();
|
||||
|
||||
// update
|
||||
ctx.UpdateRange(_actList
|
||||
.Where(_actIds.ContainsKey)
|
||||
.Select(t => {
|
||||
t.VtrgId = _actIds[t];
|
||||
ctx.Entry(t).State = EntityState.Added;
|
||||
return t;
|
||||
}));
|
||||
foreach (var (vtrgid, _) in _acts.Where(a => a.Value == null)) {
|
||||
ctx.Remove(ctx.AreaCommitmentTypes.Find(vtrgid)!);
|
||||
}
|
||||
foreach (var (attr, old) in _actIds) {
|
||||
attr.VtrgId = old;
|
||||
}
|
||||
foreach (var (old, vtrgid) in _acts.Where(a => a.Value != null)) {
|
||||
ctx.Update(ctx.AreaCommitmentTypes.Find(old)!);
|
||||
}
|
||||
await ctx.SaveChangesAsync();
|
||||
foreach (var (old, vtrgid) in _acts.Where(a => a.Value != null && a.Key != a.Value)) {
|
||||
|
||||
foreach (var (old, vtrgid) in _acts.Where(a => a.Value != null)) {
|
||||
await ctx.Database.ExecuteSqlAsync($"UPDATE area_commitment_type SET vtrgid = {vtrgid} WHERE vtrgid = {old}");
|
||||
}
|
||||
await ctx.SaveChangesAsync();
|
||||
|
||||
// insert
|
||||
ctx.AddRange(_actList
|
||||
.Where(t => !_actIds.ContainsKey(t) && t.VtrgId != null));
|
||||
foreach (var type in _actList.Where(a => !_actIds.ContainsKey(a))) {
|
||||
if (type.VtrgId == null) continue;
|
||||
ctx.Add(type);
|
||||
}
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ namespace Elwig.Windows {
|
||||
var id = AreaCommitmentTypeIdInput.Text;
|
||||
if (old != null) _acts[old] = id;
|
||||
attr.VtrgId = id;
|
||||
attr.SortId = v?.SortId ?? "";
|
||||
attr.SortId = v?.SortId;
|
||||
attr.AttrId = a?.AttrId;
|
||||
attr.MinKgPerHa = AreaCommitmentTypeMinKgPerHaInput.Text.Length > 0 ? int.Parse(AreaCommitmentTypeMinKgPerHaInput.Text) : null;
|
||||
attr.PenaltyPerKg = AreaCommitmentTypePenaltyPerKgInput.Text.Length > 0 ? decimal.Parse(AreaCommitmentTypePenaltyPerKgInput.Text) : null;
|
||||
|
||||
@@ -41,26 +41,25 @@ namespace Elwig.Windows {
|
||||
if (!_branchChanged || _branchList == null || _branches == null || _branchIds == null)
|
||||
return;
|
||||
|
||||
// delete
|
||||
var deleteZwstIds = _branches.Where(b => b.Value == null).Select(b => b.Key).ToList();
|
||||
await ctx.Branches.Where(b => deleteZwstIds.Contains(b.ZwstId)).ExecuteDeleteAsync();
|
||||
|
||||
// update
|
||||
ctx.UpdateRange(_branchList
|
||||
.Where(_branchIds.ContainsKey)
|
||||
.Select(b => {
|
||||
b.ZwstId = _branchIds[b];
|
||||
ctx.Entry(b).State = EntityState.Added;
|
||||
return b;
|
||||
}));
|
||||
foreach (var (branch, old) in _branchIds) {
|
||||
branch.ZwstId = old;
|
||||
}
|
||||
foreach (var (old, zwstid) in _branches.Where(b => b.Value != null)) {
|
||||
ctx.Update(ctx.Branches.Find(old)!);
|
||||
}
|
||||
await ctx.SaveChangesAsync();
|
||||
foreach (var (old, zwstid) in _branches.Where(b => b.Value != null && b.Key != b.Value)) {
|
||||
|
||||
foreach (var (old, zwstid) in _branches.Where(b => b.Value != null)) {
|
||||
await ctx.Database.ExecuteSqlAsync($"UPDATE branch SET zwstid = {zwstid} WHERE zwstid = {old}");
|
||||
}
|
||||
|
||||
// insert
|
||||
ctx.AddRange(_branchList
|
||||
.Where(b => !_branchIds.ContainsKey(b) && b.ZwstId != null));
|
||||
foreach (var branch in _branchList.Where(b => !_branchIds.ContainsKey(b))) {
|
||||
if (branch.ZwstId == null) continue;
|
||||
ctx.Add(branch);
|
||||
}
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
|
||||
|
||||
@@ -48,30 +48,30 @@ namespace Elwig.Windows {
|
||||
if (!_modChanged || _modList == null || _mods == null || _modIds == null)
|
||||
return;
|
||||
|
||||
var year = (SeasonList.SelectedItem as Season)?.Year;
|
||||
int i = 0;
|
||||
foreach (var mod in _modList) mod.Ordering = ++i;
|
||||
|
||||
// delete
|
||||
var deleteModIds = _mods.Where(b => b.Value == null).Select(b => b.Key).ToList();
|
||||
await ctx.Modifiers.Where(b => b.Year == year && deleteModIds.Contains(b.ModId)).ExecuteDeleteAsync();
|
||||
|
||||
// update
|
||||
ctx.UpdateRange(_modList
|
||||
.Where(_modIds.ContainsKey)
|
||||
.Select(m => {
|
||||
m.ModId = _modIds[m];
|
||||
ctx.Entry(m).State = EntityState.Added;
|
||||
return m;
|
||||
}));
|
||||
var year = (SeasonList.SelectedItem as Season)?.Year;
|
||||
foreach (var (modid, _) in _mods.Where(m => m.Value == null)) {
|
||||
ctx.Remove(ctx.Modifiers.Find(year, modid)!);
|
||||
}
|
||||
foreach (var (mod, old) in _modIds) {
|
||||
mod.ModId = old;
|
||||
}
|
||||
foreach (var (old, modid) in _mods.Where(m => m.Value != null)) {
|
||||
ctx.Update(ctx.Modifiers.Find(year, old)!);
|
||||
}
|
||||
await ctx.SaveChangesAsync();
|
||||
|
||||
foreach (var (old, modid) in _mods.Where(m => m.Value != null)) {
|
||||
await ctx.Database.ExecuteSqlAsync($"UPDATE modifier SET modid = {modid} WHERE (year, modid) = ({year}, {old})");
|
||||
}
|
||||
await ctx.SaveChangesAsync();
|
||||
|
||||
// insert
|
||||
ctx.AddRange(_modList
|
||||
.Where(m => !_modIds.ContainsKey(m) && m.ModId != null));
|
||||
foreach (var mod in _modList.Where(m => !_modIds.ContainsKey(m))) {
|
||||
if (mod.ModId == null) continue;
|
||||
await ctx.AddAsync(mod);
|
||||
}
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
|
||||
|
||||
@@ -41,28 +41,26 @@ namespace Elwig.Windows {
|
||||
if (!_attrChanged || _attrList == null || _attrs == null || _attrIds == null)
|
||||
return;
|
||||
|
||||
// delete
|
||||
var deleteAttrIds = _attrs.Where(a => a.Value == null).Select(a => a.Key).ToList();
|
||||
await ctx.WineAttributes.Where(a => deleteAttrIds.Contains(a.AttrId)).ExecuteDeleteAsync();
|
||||
|
||||
// update
|
||||
ctx.UpdateRange(_attrList
|
||||
.Where(_attrIds.ContainsKey)
|
||||
.Select(a => {
|
||||
a.AttrId = _attrIds[a];
|
||||
ctx.Entry(a).State = EntityState.Added;
|
||||
return a;
|
||||
}));
|
||||
foreach (var (attr, old) in _attrIds) {
|
||||
attr.AttrId = old;
|
||||
}
|
||||
foreach (var (old, attrid) in _attrs.Where(a => a.Value != null)) {
|
||||
ctx.Update(ctx.WineAttributes.Find(old)!);
|
||||
}
|
||||
await ctx.SaveChangesAsync();
|
||||
|
||||
foreach (var (old, attrid) in _attrs.Where(a => a.Value != null)) {
|
||||
await ctx.Database.ExecuteSqlAsync($"UPDATE wine_attribute SET attrid = {attrid} WHERE attrid = {old}");
|
||||
await ctx.Database.ExecuteSqlAsync($"UPDATE area_commitment_type SET vtrgid = (sortid || COALESCE(attrid, '') || COALESCE(disc, '')) WHERE attrid = {attrid}");
|
||||
await ctx.Database.ExecuteSqlAsync($"UPDATE payment_variant SET data = REPLACE(REPLACE(data, {$"/{old}\""}, {$"/{attrid}\""}), {$"/{old}-"}, {$"/{attrid}-"})");
|
||||
await ctx.Database.ExecuteSqlRawAsync($"UPDATE payment_variant SET data = REPLACE(REPLACE(data, '/{old}\"', '/{attrid}\"'), '/{old}-', '/{attrid}-')");
|
||||
}
|
||||
|
||||
// insert
|
||||
ctx.AddRange(_attrList
|
||||
.Where(a => !_attrIds.ContainsKey(a) && a.AttrId != null));
|
||||
foreach (var attr in _attrList.Where(a => !_attrIds.ContainsKey(a))) {
|
||||
if (attr.AttrId == null) continue;
|
||||
ctx.Add(attr);
|
||||
}
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
|
||||
|
||||
@@ -41,27 +41,25 @@ namespace Elwig.Windows {
|
||||
if (!_cultChanged || _cultList == null || _cults == null || _cultIds == null)
|
||||
return;
|
||||
|
||||
// delete
|
||||
var deleteCultIds = _cults.Where(c => c.Value == null).Select(c => c.Key).ToList();
|
||||
await ctx.WineCultivations.Where(c => deleteCultIds.Contains(c.CultId)).ExecuteDeleteAsync();
|
||||
|
||||
// update
|
||||
ctx.UpdateRange(_cultList
|
||||
.Where(_cultIds.ContainsKey)
|
||||
.Select(c => {
|
||||
c.CultId = _cultIds[c];
|
||||
ctx.Entry(c).State = EntityState.Added;
|
||||
return c;
|
||||
}));
|
||||
foreach (var (cult, old) in _cultIds) {
|
||||
cult.CultId = old;
|
||||
}
|
||||
foreach (var (old, cultid) in _cults.Where(c => c.Value != null)) {
|
||||
ctx.Update(ctx.WineCultivations.Find(old)!);
|
||||
}
|
||||
await ctx.SaveChangesAsync();
|
||||
|
||||
foreach (var (old, cultid) in _cults.Where(c => c.Value != null)) {
|
||||
await ctx.Database.ExecuteSqlAsync($"UPDATE wine_cultivation SET cultid = {cultid} WHERE cultid = {old}");
|
||||
await ctx.Database.ExecuteSqlAsync($"UPDATE payment_variant SET data = REPLACE(data, {$"-{old}\""}, {$"-{cultid}\""})");
|
||||
await ctx.Database.ExecuteSqlRawAsync($"UPDATE payment_variant SET data = REPLACE(data, '-{old}\"', '-{cultid}\"')");
|
||||
}
|
||||
|
||||
// insert
|
||||
ctx.AddRange(_cultList
|
||||
.Where(c => !_cultIds.ContainsKey(c) && c.CultId != null));
|
||||
foreach (var cult in _cultList.Where(c => !_cultIds.ContainsKey(c))) {
|
||||
if (cult.CultId == null) continue;
|
||||
ctx.Add(cult);
|
||||
}
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ using System.Windows.Input;
|
||||
namespace Elwig.Windows {
|
||||
public partial class BaseDataWindow : AdministrationWindow {
|
||||
|
||||
protected AppDbContext? EditContext;
|
||||
|
||||
public BaseDataWindow() {
|
||||
InitializeComponent();
|
||||
RequiredInputs = [
|
||||
@@ -187,30 +189,31 @@ namespace Elwig.Windows {
|
||||
}
|
||||
|
||||
private async Task InitEditing() {
|
||||
using var ctx = new AppDbContext();
|
||||
await BranchesInitEditing(ctx);
|
||||
await WineAttributesInitEditing(ctx);
|
||||
await WineCultivationsInitEditing(ctx);
|
||||
await AreaCommitmentTypesInitEditing(ctx);
|
||||
await SeasonsInitEditing(ctx);
|
||||
await ModifiersInitEditing(ctx);
|
||||
EditContext = new AppDbContext();
|
||||
await BranchesInitEditing(EditContext);
|
||||
await WineAttributesInitEditing(EditContext);
|
||||
await WineCultivationsInitEditing(EditContext);
|
||||
await AreaCommitmentTypesInitEditing(EditContext);
|
||||
await SeasonsInitEditing(EditContext);
|
||||
await ModifiersInitEditing(EditContext);
|
||||
}
|
||||
|
||||
private async Task Save() {
|
||||
await UpdateClientParameters(App.Client);
|
||||
await UpdateParameters(Utils.CurrentLastSeason);
|
||||
using var ctx = new AppDbContext();
|
||||
using var tx = await ctx.Database.BeginTransactionAsync();
|
||||
await BranchesSave(ctx);
|
||||
await WineAttributesSave(ctx);
|
||||
await WineCultivationsSave(ctx);
|
||||
await AreaCommitmentTypesSave(ctx);
|
||||
await SeasonsSave(ctx);
|
||||
await ModifiersSave(ctx);
|
||||
using var tx = await EditContext!.Database.BeginTransactionAsync();
|
||||
await BranchesSave(EditContext!);
|
||||
await WineAttributesSave(EditContext!);
|
||||
await WineCultivationsSave(EditContext!);
|
||||
await AreaCommitmentTypesSave(EditContext!);
|
||||
await SeasonsSave(EditContext!);
|
||||
await ModifiersSave(EditContext!);
|
||||
await tx.CommitAsync();
|
||||
}
|
||||
|
||||
private async Task FinishEditing() {
|
||||
EditContext?.Dispose();
|
||||
EditContext = null;
|
||||
using var ctx = new AppDbContext();
|
||||
await BranchesFinishEditing(ctx);
|
||||
await WineAttributesFinishEditing(ctx);
|
||||
|
||||
@@ -958,7 +958,6 @@ namespace Elwig.Windows {
|
||||
m.EmailAddresses.OrderBy(a => a.Nr).Select(a => a.Address).ToArray(),
|
||||
subject, docs.Select(d => d.Title).ToArray()
|
||||
)]);
|
||||
await Task.Delay((int)(App.Config.SmtpWait * 1000));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -200,13 +200,8 @@ namespace Elwig.Windows {
|
||||
return;
|
||||
Mouse.OverrideCursor = Cursors.Wait;
|
||||
await Task.Run(async () => {
|
||||
if (App.Config.SyncMain) {
|
||||
await SyncService.Download(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword, true);
|
||||
await SyncService.UploadModified(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
|
||||
} else {
|
||||
await SyncService.Download(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword, false);
|
||||
await SyncService.UploadBranchDeliveries(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
|
||||
}
|
||||
await SyncService.Download(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
|
||||
await SyncService.UploadModified(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
|
||||
});
|
||||
Mouse.OverrideCursor = null;
|
||||
}
|
||||
@@ -216,7 +211,7 @@ namespace Elwig.Windows {
|
||||
return;
|
||||
Mouse.OverrideCursor = Cursors.Wait;
|
||||
await Task.Run(async () => {
|
||||
await SyncService.Download(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword, App.Config.SyncMain);
|
||||
await SyncService.Download(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
|
||||
});
|
||||
Mouse.OverrideCursor = null;
|
||||
}
|
||||
@@ -234,10 +229,6 @@ namespace Elwig.Windows {
|
||||
private async void Menu_Sync_UploadModified_Click(object sender, RoutedEventArgs evt) {
|
||||
if (App.Config.SyncUrl == null)
|
||||
return;
|
||||
if (!App.Config.SyncMain) {
|
||||
InteractionService.ShowInformation("Mitglieder und Lieferungen hochladen", "Hochladen nicht möglich: Dieses Gerät ist nicht das Hauptgerät!");
|
||||
return;
|
||||
}
|
||||
Mouse.OverrideCursor = Cursors.Wait;
|
||||
await Task.Run(async () => {
|
||||
await SyncService.UploadModified(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
|
||||
@@ -305,11 +296,6 @@ namespace Elwig.Windows {
|
||||
if (App.Config.SyncUrl == null)
|
||||
return;
|
||||
|
||||
if (!App.Config.SyncMain) {
|
||||
InteractionService.ShowInformation("Datenbank hochladen", "Hochladen nicht möglich: Dieses Gerät ist nicht das Hauptgerät!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!InteractionService.AskContinue("Datenbank hochladen", "Sind Sie wirklich sicher, dass Sie die Datenbank dieses\nGerätes hochladen möchten? Das sollte nur vom Hauptgerät aus passieren!"))
|
||||
return;
|
||||
|
||||
@@ -381,11 +367,7 @@ namespace Elwig.Windows {
|
||||
await Task.Delay(delay);
|
||||
var ch = false;
|
||||
using (var ctx = new AppDbContext()) {
|
||||
if (App.Config.SyncMain) {
|
||||
ch = await SyncService.ChangesAvailable(ctx, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
|
||||
} else {
|
||||
ch = await SyncService.ChangesAvailable(ctx, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword, default);
|
||||
}
|
||||
ch = await SyncService.ChangesAvailable(ctx, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
|
||||
}
|
||||
await App.MainDispatcher.BeginInvoke(() => {
|
||||
if (ch) {
|
||||
|
||||
@@ -706,24 +706,12 @@
|
||||
</TextBlock.ToolTip>
|
||||
</TextBlock>
|
||||
|
||||
<Button x:Name="DeliveryButton" IsEnabled="{Binding IsMemberSelected}"
|
||||
<Button x:Name="DeliveryButton" Content="Lieferungen" IsEnabled="{Binding IsMemberSelected}"
|
||||
Click="DeliveryButton_Click"
|
||||
HorizontalAlignment="Right" Margin="10,00,10,37" Width="160" VerticalAlignment="Bottom" Grid.ColumnSpan="3" HorizontalContentAlignment="Stretch">
|
||||
<Grid>
|
||||
<TextBlock Margin="0,0,10,0" TextAlignment="Center">Lieferungen</TextBlock>
|
||||
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text=""
|
||||
TextAlignment="Left" HorizontalAlignment="Right" Padding="0,1.5,0,0" Margin="0,0,0,0"/>
|
||||
</Grid>
|
||||
</Button>
|
||||
<Button x:Name="AreaCommitmentButton" IsEnabled="{Binding IsMemberSelected}"
|
||||
HorizontalAlignment="Right" Margin="10,00,10,37" Width="150" VerticalAlignment="Bottom" Grid.ColumnSpan="3"/>
|
||||
<Button x:Name="AreaCommitmentButton" Content="Flächenbindungen" IsEnabled="{Binding IsMemberSelected}"
|
||||
Click="AreaCommitmentButton_Click"
|
||||
HorizontalAlignment="Right" Margin="10,10,10,5" Width="160" VerticalAlignment="Bottom" Grid.ColumnSpan="3" HorizontalContentAlignment="Stretch">
|
||||
<Grid>
|
||||
<TextBlock Margin="0,0,15,0" TextAlignment="Center">Flächenbindungen</TextBlock>
|
||||
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text=""
|
||||
TextAlignment="Left" HorizontalAlignment="Right" Padding="0,1.5,0,0" Margin="0,0,0,0"/>
|
||||
</Grid>
|
||||
</Button>
|
||||
HorizontalAlignment="Right" Margin="10,10,10,5" Width="150" VerticalAlignment="Bottom" Grid.ColumnSpan="3"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
</Grid>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
xmlns:local="clr-namespace:Elwig.Windows"
|
||||
xmlns:vm="clr-namespace:Elwig.ViewModels"
|
||||
xmlns:ctrl="clr-namespace:Elwig.Controls"
|
||||
Title="Auszahlungsvarianten - Elwig" Height="480" Width="850" MinHeight="430" MinWidth="850">
|
||||
Title="Auszahlungsvarianten - Elwig" Height="480" Width="850" MinHeight="400" MinWidth="850">
|
||||
<Window.DataContext>
|
||||
<vm:PaymentVariantsViewModel/>
|
||||
</Window.DataContext>
|
||||
@@ -195,10 +195,11 @@
|
||||
Content="Benutzerdefinierte Zu-/Abschläge pro Mitglied"
|
||||
Margin="110,175,10,10" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top"
|
||||
Checked="ConsiderCustomInput_Changed" Unchecked="ConsiderCustomInput_Changed"/>
|
||||
<Label Content="" FontFamily="Segoe MDL2 Assets" FontSize="16" Grid.Row="0" Grid.Column="1" Margin="108,195,10,10"/>
|
||||
|
||||
<Grid Grid.Column="0" Grid.ColumnSpan="2" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="50,200,10,10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="130"/>
|
||||
<ColumnDefinition Width="110"/>
|
||||
<ColumnDefinition Width="27"/>
|
||||
<ColumnDefinition Width="110"/>
|
||||
<ColumnDefinition Width="27"/>
|
||||
@@ -206,7 +207,7 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="27"/>
|
||||
<RowDefinition Height="37"/>
|
||||
<RowDefinition Height="5"/>
|
||||
<RowDefinition Height="27"/>
|
||||
<RowDefinition Height="5"/>
|
||||
<RowDefinition Height="27"/>
|
||||
@@ -226,23 +227,11 @@
|
||||
</Grid.Resources>
|
||||
|
||||
<Button x:Name="EditButton" IsEnabled="{Binding IsPaymentVariantSelected}"
|
||||
Grid.Column="0" Grid.Row="2" HorizontalContentAlignment="Stretch"
|
||||
Click="EditButton_Click">
|
||||
<Grid>
|
||||
<TextBlock Margin="0,0,13,0" TextAlignment="Center" Text="{Binding EditText}"/>
|
||||
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text=""
|
||||
TextAlignment="Left" HorizontalAlignment="Right" Padding="0,1.5,0,0" Margin="0,0,5,0"/>
|
||||
</Grid>
|
||||
</Button>
|
||||
Content="{Binding EditText}" Grid.Column="0" Grid.Row="2"
|
||||
Click="EditButton_Click"/>
|
||||
<Label Content="" FontFamily="Segoe MDL2 Assets" FontSize="16" Grid.Row="2" Grid.Column="1"/>
|
||||
<Button x:Name="ModifierButton" Grid.Column="0" Grid.Row="4" HorizontalContentAlignment="Stretch"
|
||||
Click="ModifierButton_Click">
|
||||
<Grid>
|
||||
<TextBlock Margin="0,0,15,0" TextAlignment="Center">Zu-/Abschläge</TextBlock>
|
||||
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text=""
|
||||
TextAlignment="Left" HorizontalAlignment="Right" Padding="0,1.5,0,0" Margin="0,0,5,0"/>
|
||||
</Grid>
|
||||
</Button>
|
||||
<Button x:Name="ModifierButton" Content="Zu-/Abschläge" Grid.Column="0" Grid.Row="4"
|
||||
Click="ModifierButton_Click"/>
|
||||
<Label Content="" FontFamily="Segoe MDL2 Assets" FontSize="16" Grid.Row="4" Grid.Column="1" RenderTransformOrigin="0.5,0.5" >
|
||||
<Label.RenderTransform>
|
||||
<TransformGroup>
|
||||
@@ -255,14 +244,8 @@
|
||||
Content="Berechnen" Grid.Column="2" Grid.Row="2"
|
||||
Click="CalculateButton_Click"/>
|
||||
<Label Content="{Binding Arrow}" FontFamily="Segoe MDL2 Assets" FontSize="16" Grid.Row="2" Grid.Column="3"/>
|
||||
<Button x:Name="PaymentAdjustmentButton" Grid.Column="2" Grid.Row="4" HorizontalContentAlignment="Stretch"
|
||||
Click="PaymentAdjustmentButton_Click">
|
||||
<Grid>
|
||||
<TextBlock Margin="0,0,13,0" TextAlignment="Center">Anpassen</TextBlock>
|
||||
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text=""
|
||||
TextAlignment="Left" HorizontalAlignment="Right" Padding="0,1.5,0,0" Margin="0,0,5,0"/>
|
||||
</Grid>
|
||||
</Button>
|
||||
<Button x:Name="PaymentAdjustmentButton" Content="Anpassen" Grid.Column="2" Grid.Row="4"
|
||||
Click="PaymentAdjustmentButton_Click"/>
|
||||
<Label Content="{Binding Arrow}" FontFamily="Segoe MDL2 Assets" FontSize="16" Grid.Row="4" Grid.Column="3" RenderTransformOrigin="0.5,0.5" >
|
||||
<Label.RenderTransform>
|
||||
<TransformGroup>
|
||||
@@ -278,7 +261,7 @@
|
||||
Content="Freigeben" Grid.Column="4" Grid.Row="2"
|
||||
Click="RevertButton_Click"/>
|
||||
<Button x:Name="SaveButton" IsEnabled="{Binding SaveIsEnabled}"
|
||||
Content="Speichern" Grid.Column="2" Grid.Row="0"
|
||||
Content="Speichern" Grid.Column="4" Grid.Row="0"
|
||||
Click="SaveButton_Click"/>
|
||||
</Grid>
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ using Elwig.Services;
|
||||
using Elwig.ViewModels;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
@@ -240,27 +239,15 @@ namespace Elwig.Windows {
|
||||
private async void SaveButton_Click(object sender, RoutedEventArgs evt) {
|
||||
if (PaymentVariantList.SelectedItem is not PaymentVar v || ViewModel.BillingData == null)
|
||||
return;
|
||||
|
||||
Mouse.OverrideCursor = Cursors.Wait;
|
||||
try {
|
||||
await ViewModel.UpdatePaymentVariant(v.Year, v.AvNr);
|
||||
App.HintContextChange();
|
||||
} catch (Exception exc) {
|
||||
await ForceContextReload();
|
||||
InteractionService.ShowDbException("Auszahlungsvariante aktualisieren", exc);
|
||||
}
|
||||
try {
|
||||
await Task.Run(async () => {
|
||||
var b = await BillingVariant.Create(v.Year, v.AvNr);
|
||||
await b.Calculate(false);
|
||||
});
|
||||
} catch (KeyNotFoundException exc) {
|
||||
InteractionService.ShowInformation("Noch nicht alle Preise festgelegt", exc.Message);
|
||||
} catch (Exception exc) {
|
||||
InteractionService.ShowException("Berechnungsfehler", exc);
|
||||
}
|
||||
App.HintContextChange();
|
||||
Mouse.OverrideCursor = null;
|
||||
|
||||
await EnsureContextRenewed();
|
||||
CommentInput_TextChanged(null, null);
|
||||
DateInput_TextChanged(null, null);
|
||||
|
||||
@@ -16,7 +16,14 @@
|
||||
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*" />
|
||||
<assemblyIdentity
|
||||
type="win32"
|
||||
name="Microsoft.Windows.Common-Controls"
|
||||
version="6.0.0.0"
|
||||
processorArchitecture="*"
|
||||
publicKeyToken="6595b64144ccf1df"
|
||||
language="*"
|
||||
/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
</assembly>
|
||||
@@ -26,7 +26,6 @@ auto = true
|
||||
;username = ""
|
||||
;password = ""
|
||||
;from = mail@wg.at
|
||||
;wait = 0.5
|
||||
|
||||
;[scale.1]
|
||||
;type = SysTec-IT
|
||||
|
||||
@@ -13,7 +13,7 @@ About
|
||||
**Product:** Elwig
|
||||
**Description:** Electronic Management for Vintners' Cooperatives
|
||||
**Type:** ERP system
|
||||
**Version:** 1.1.0.3 ([Changelog](./CHANGELOG.md))
|
||||
**Version:** 1.1.0.0 ([Changelog](./CHANGELOG.md))
|
||||
**License:** [GNU General Public License 3.0 (GPLv3)](./LICENSE)
|
||||
**Website:** https://elwig.at/
|
||||
**Source code:** https://git.necronda.net/winzer/elwig
|
||||
@@ -33,7 +33,7 @@ Packaging: [WiX Toolset](https://www.firegiant.com/wixtoolset/)
|
||||
**Produkt:** Elwig
|
||||
**Beschreibung:** Elektronische Winzergenossenschaftsverwaltung
|
||||
**Typ:** Warenwirtschaftssystem (ERP-System)
|
||||
**Version:** 1.1.0.3 ([Änderungsprotokoll](./CHANGELOG.md))
|
||||
**Version:** 1.1.0.0 ([Änderungsprotokoll](./CHANGELOG.md))
|
||||
**Lizenz:** [GNU General Public License 3.0 (GPLv3)](./LICENSE)
|
||||
**Website:** https://elwig.at/
|
||||
**Quellcode:** https://git.necronda.net/winzer/elwig
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0-windows</TargetFramework>
|
||||
@@ -19,7 +19,7 @@
|
||||
</Target>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.9.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.8.1" />
|
||||
<PackageReference Include="Appium.WebDriver" Version="4.4.5" />
|
||||
<PackageReference Include="NReco.PdfRenderer" Version="1.6.0" />
|
||||
<PackageReference Include="NUnit" Version="4.6.1" />
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Tests.UnitTests.DocumentTests {
|
||||
1 Grüner Veltliner Qualitätswein 73 15,0 3 219
|
||||
Herkunft: Österreich / Weinland / Niederösterreich
|
||||
/ Matzner Hügel / Hohenruppersdorf / KG Hohenruppersdorf
|
||||
Waage/Terminal: ?/1, ID: 321 – 09:02, 01.10.2020
|
||||
Waage/Terminal: ?/1, ID: 321 – 09:02:46, 01.10.2020
|
||||
Brutto: 3 219 kg – Tara: 0 kg – Netto: 3 219 kg – gerebelt gewogen
|
||||
"""));
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user