Compare commits

...

161 Commits

Author SHA1 Message Date
e8bd81ccc7 Installer: Fix typo 2023-10-20 00:13:28 +02:00
f26d55af68 Bump version to 0.4.3 2023-10-20 00:10:22 +02:00
e46ddb9cdc DeliveryConfirmationsDialog: move to DeliveryConfirmationsWindow 2023-10-19 23:50:21 +02:00
3cf3ca71d6 Update fetch-resources.bat 2023-10-19 23:41:14 +02:00
1d4ab7b264 Update fetch-resources.bat 2023-10-19 23:32:47 +02:00
841f8bfb84 Documents/Pdf: fix progression 2023-10-19 23:25:55 +02:00
a075189bdd Added WinziPrint.exe to Wix Installer 2023-10-19 23:22:18 +02:00
e52475a4bf Documents: fix WinziPrint args 2023-10-19 22:45:31 +02:00
8ce4911317 DeliveryConfirmationsDialog: small fixes 2023-10-19 22:35:31 +02:00
c190ce1474 Documents/Pdf: Add progress tracker 2023-10-19 22:16:33 +02:00
f19de3ae6e Billing: fix calc issue 2023-10-19 18:54:47 +02:00
e56e209506 DeliveryConfirmation: Remove footer todo 2023-10-19 17:07:18 +02:00
28fb4f6fa2 DeliveryConfirmation: Cache deliveries of all members 2023-10-19 17:04:35 +02:00
a832879b73 Documents: use WinziPrint instead of WeasyPrint 2023-10-19 16:38:03 +02:00
a2f49e1b8b Bump version to 0.4.2 2023-10-19 01:10:13 +02:00
99c3474fb6 App: Fix wolkersdorf im weinviertel branch name issue 2023-10-19 01:09:26 +02:00
9924795888 DeliveryAdminWindow: make only non-predicate quality levels searchable 2023-10-19 01:09:03 +02:00
733ab0d208 Add wine variety and attribute to AreaComAdminWindow datagrid 2023-10-19 00:18:28 +02:00
6e2ba56a7c SeasonFinishWindow: Implement Über-/Unterlieferungen 2023-10-19 00:09:38 +02:00
2507a2695f DeliveryConfirmation: Change stat table headings 2023-10-18 18:44:12 +02:00
ddd83bf63d Bump version to 0.4.1 2023-10-18 01:31:22 +02:00
685793f0fb Billing: Implement feature 'avoid under deliveries' 2023-10-18 01:30:34 +02:00
f6712704ee DeliveryConfirmationsDialog: Fix button width 2023-10-18 01:22:17 +02:00
de500854c4 AppDbContext: Add v_area_commitment_bin 2023-10-17 23:48:16 +02:00
d992b6a206 Document: Add member name to pdf viewer window title 2023-10-17 23:10:19 +02:00
d2b96736bb DeliveryConfirmation: remove name from title 2023-10-17 23:02:28 +02:00
50b9f4e207 AppDbContext: add name for _ attribute 2023-10-17 22:58:22 +02:00
45fc0893b1 DeliveryConfirmation: fix tables 2023-10-17 22:56:21 +02:00
c4dd56075d DeliveryConfirmationsDialog: Use Print-Tag 2023-10-17 20:53:40 +02:00
f1084c716a DeliveryConfirmationsDialog: Add AllMemberInput checkbox 2023-10-17 20:50:16 +02:00
2f8e4ca812 Bump version to 0.4.0 2023-10-17 01:08:37 +02:00
450f5d8109 Dialogs: Add DeliveryConfirmationsDialog 2023-10-17 01:07:38 +02:00
62d6707d10 DeliveryConfirmation: no linebreaks in stat table 2023-10-17 00:58:01 +02:00
2bcf26cc8d MainWindow: Add version to header 2023-10-16 23:45:14 +02:00
daddd069a3 MemberAdminWindow: Add more letterhead sorting options 2023-10-16 23:25:22 +02:00
3b3489b492 SeasonFinishWindow: enhnace button handling 2023-10-16 22:53:04 +02:00
505ee0ad24 SeasonFinishWindow: Change buttons 2023-10-16 22:46:18 +02:00
0b79fa192e Billing: Fix forced attribute calculation 2023-10-16 22:41:12 +02:00
ff2968c989 DeliveryConfirmation: Add Statistics table 2023-10-16 22:23:42 +02:00
8c49cc8ef2 DeliveryConfirmation: align weight to bottom 2023-10-16 18:40:19 +02:00
f0751499ea DeliveryConfirmation: Add user editable Text 2023-10-16 18:24:17 +02:00
d4dd84394b SeasonFinishWindow: Add checkboxes for options 2023-10-15 23:14:40 +02:00
3b4340b5e8 Database: Change bins once again... 2023-10-15 22:46:19 +02:00
4db147e582 Billing: honor gebunden field 2023-10-15 15:13:59 +02:00
c997acc95d TestWindow: Remove CalcBinsButton 2023-10-15 15:11:40 +02:00
f8126c392e DeliveryPart: Add gebunden field 2023-10-15 13:40:29 +02:00
b6d74d3c07 DeliveryConfirmation: enhance calculation 2023-10-15 13:12:40 +02:00
62bf425313 Billing: differentiate between flag FillLowerBins 2023-10-15 01:09:55 +02:00
5657a8f90a Windows: Add SeasonFinishWindow 2023-10-15 00:38:54 +02:00
56578a0a9d Documents: Add DeliveryConfirmation 2023-10-15 00:34:40 +02:00
e71d9516ec Billing: small fixes 2023-10-15 00:11:42 +02:00
658c10c2a4 WineAttr: Add fill_lower_bins 2023-10-14 23:56:41 +02:00
ff3defe52d Database: fix v_bucket update 2023-10-14 23:51:54 +02:00
a489f13d99 Billing: set unused Bin names to NULL 2023-10-14 21:03:23 +02:00
20d4bbf7ac DeliveryJournal: correctly use .Include() 2023-10-14 17:31:13 +02:00
604094a603 MemberAdminWindow: Add Print Letterhead option for current Member 2023-10-14 14:28:41 +02:00
522fed818f Billing: Make bin calculate way more efficient 2023-10-14 14:20:44 +02:00
3930534273 MemberAdminWindow: also display GA from active only members 2023-10-12 13:37:25 +02:00
4d950b2597 Billing: Add feature to calculate member/delivery bins 2023-10-11 23:46:38 +02:00
badf4ce955 DeliveryAdminWindow: add TODO for ToolTips 2023-10-11 10:02:31 +02:00
7d8c670ed2 DeliveryNote: Make stats changeable in BaseDataWindow 2023-10-10 19:19:29 +02:00
91a3786cd9 DeliveryNote: style expaneded table differently 2023-10-10 18:09:20 +02:00
73af12a64d Documents: remove script tags and comments 2023-10-10 16:34:10 +02:00
0e9bae4ec9 DeliveryNote: Rework according to Herbert 2023-10-10 16:24:16 +02:00
9df8056616 Bump version to 0.3.7 2023-10-06 01:22:20 +02:00
9e9195b9c0 MemberAdminWIndow: add status bar 2023-10-05 01:43:44 +02:00
1625f15f92 MemberAdminWindow: add gridsplitter 2023-10-04 23:28:45 +02:00
6a8bd9c932 DeliveryAdminWindow: Add member name row to DeliveryList 2023-10-04 10:06:51 +02:00
05da8eefac DeliveryAdminWindow: Add attribute list to DeliveryPartList 2023-10-04 10:01:07 +02:00
04badb658b Bump version to 0.3.6 2023-10-03 23:05:51 +02:00
8f2f5b28cf DeliveryAdminWindow: Allow to exclude varieties from filter 2023-10-03 22:58:13 +02:00
d5e4e0a29d DeliveryAdminWindow: Update delivery filter 2023-10-03 22:32:49 +02:00
96570dffd4 DeliveryAdminWindow: fix attribute display bug 2023-10-03 19:38:33 +02:00
e79f4baa2f DeiveryAdminWindow: Display percentage of weight in status bar 2023-10-01 09:37:17 +02:00
b79ba14f9e DeliveryAdminWindow: Print Hotfix 2023-09-30 09:55:59 +02:00
de298ffef1 Bump verion to 0.3.4 2023-09-29 23:01:39 +02:00
eaf7b6bd41 DeliveryJournal: Fix ordering 2 2023-09-29 15:38:30 +02:00
2bb8205da0 DeliveryJournal: Fix ordering 2023-09-29 15:25:29 +02:00
f623aa1fee DeliveryAdminWindow: use 'unter' instead of 'bis' for gradation in journal 2023-09-29 15:16:44 +02:00
00e7eeb774 DeliveryAdminWindow: Fix delivery ordering 2023-09-29 15:01:35 +02:00
47d51ded51 DeliveryAdminWindow: Allow date and time to be edited 2023-09-29 14:46:28 +02:00
532bb826e1 MemberAdminWindow: Only print letterheads of members with contact via post checkbox 2023-09-29 13:06:27 +02:00
8193bf483c BusinessDocument: Update Address generation 2023-09-29 12:59:00 +02:00
34dcaf26d9 DeliveryAdminWindow: Center WineVariety column 2023-09-28 22:03:58 +02:00
7411f570ee DeliveryAdminWindow: Add ToolTip to StatusBar 2023-09-28 21:34:55 +02:00
52702f3fa2 DeliveryAdminWindow: Add GridSplitter 2023-09-28 21:28:31 +02:00
7f3573cede DeliveryAdminWindow: Add Gradation to DataGrid 2023-09-28 20:55:54 +02:00
72359dc8be MemberAdminWindow: Add warning for printing Letterheads 2023-09-28 20:17:53 +02:00
0e17aa5408 Windows: Use Cursor.AppStarting instead of Cursor.Wait 2023-09-28 20:09:12 +02:00
2bf850bc55 Documents/Pdf: Add /s to PdfToPrinter call 2023-09-28 20:05:29 +02:00
aadf536d13 Windows: Fix Tag="Print" issue 2023-09-28 20:04:05 +02:00
3be6371be1 ControlUtils: Fix FinAllChildren 2023-09-28 20:03:00 +02:00
ca1b68aa4f Documents: Add Letterhead 2023-09-28 19:38:29 +02:00
d4e5ac6753 MemberAdminWindow: Send email to all addresses of member 2023-09-28 15:50:04 +02:00
c9f49927a8 BusinessDocument: Do not hard code destination country 2023-09-28 14:35:54 +02:00
1794b5b8ca BusinessDocument: Use Ort Name instead of Destination Name 2023-09-28 14:23:18 +02:00
7347439034 Bump version number 2023-09-28 14:06:47 +02:00
51ad8f99fd DeliveryAdminWindow: Add DeliveryJournal filter options 2023-09-19 19:31:02 +02:00
39279a5dda Bump version to 0.3.2 2023-09-19 16:29:34 +02:00
5271f357f5 DeliveryAdminWindow: Add DeliveryJournal 2023-09-19 02:11:30 +02:00
13ba3f90f6 DeliveryAdminWindow: Add Filter to BKI export 2023-09-19 00:57:59 +02:00
43be8bf391 DeliveryAdminWindow: Empty scale only after saving to database 2023-09-19 00:47:10 +02:00
826a76c56c AppDbUpdater: Add schema_version 2 2023-09-19 00:37:46 +02:00
efaae5f490 DeliveryAdminWindow: 'Activate()' window after awaiting doc.Print(2) 2023-09-18 21:48:03 +02:00
3a73265a75 ContextWindow: Change renew context event handling 2023-09-18 21:46:14 +02:00
a6fef7fd9b Utils: Fix GetSearchScore for multiple occurances of keyword in haystack 2023-09-18 21:17:37 +02:00
a08df4c3ed DeliveryAdminWindow: Add auto focus for weighing buttons on enter 2023-09-18 20:59:59 +02:00
9701af9e36 AppDbUpdater: Fix version comparison 2023-09-18 10:03:48 +02:00
b4f1eeee84 Bump version to 0.3.1 2023-09-18 02:05:16 +02:00
2922fe0138 BaseDataWindow: Remove DecimalInput check 2023-09-18 01:40:59 +02:00
704facbc6b App: Use Ort insteas of Destination for Branch Location 2023-09-18 01:32:54 +02:00
404e8a0c27 BaseDataWindow: Fix Modifier creation 2023-09-18 01:30:45 +02:00
ef621fab2d Validator: Fix usage of maxLen and maxDecimal in CheckDecimal 2023-09-18 01:27:15 +02:00
0938e33fe1 DeliveryAdminWindow: Fix LNr calculation for new deliveries 2023-09-18 01:04:09 +02:00
6b5c283e10 Update dependencies 2023-09-17 22:12:15 +02:00
b6400c41c4 Bump version to 0.3.0 2023-09-17 21:31:06 +02:00
24b7078a05 Add AppDbUpdater 2023-09-17 20:48:07 +02:00
dc215d3350 BusinessDocument: Make aside table a bit more compact 2023-09-15 14:35:25 +02:00
b80cbc037c AreaComAdminWindow: Add comment input 2023-09-14 23:43:38 +02:00
4891501f62 DeliveryAdminWindow: Fix filter count check 2023-09-14 23:27:43 +02:00
6d3adc48f6 DeliveryNote: Add manual weighing reason 2023-09-14 23:25:37 +02:00
f5eea1e906 Models: Add MemberEmailAddr 2023-09-14 22:32:28 +02:00
ba691f4d17 Documents: Make business documents more compact 2023-09-14 20:44:48 +02:00
470f092482 DeliveryAdminWindow: Move manual weighing button to last position 2023-09-14 17:51:48 +02:00
e9e4c75edd Bump version to 0.2.1 2023-09-13 23:37:26 +02:00
ff1a4e7182 Documents: Add ShowFoldMarks flag 2023-09-13 23:22:35 +02:00
1e9cad6de7 DeliveryAdminWindow: Use last scale error as default manual weighing reason 2023-09-13 23:19:04 +02:00
62fe087598 DeliveryAdminWindow: Hopefully fix 'Erste Übernahme' bug 2023-09-13 22:57:21 +02:00
7f01b85878 HelpersUtilsTest: fix file encoding 2023-09-13 22:04:01 +02:00
a659d07db2 Config: add debug flag 2023-09-13 22:02:50 +02:00
b2a78907cf Bump version to 0.2.0 2023-09-12 22:52:28 +02:00
7bcf532b26 DeliveryAdminWindow: Verschlimmbesserung 2023-09-12 22:51:03 +02:00
595f9a049c ManualWeighingDialog: Do not require Reason 2023-09-12 22:51:03 +02:00
80ed90941d Include all files in Documents except .cs files 2023-09-12 21:50:56 +02:00
77cee53f2d MainWindow: Cleanup and move parts to TestWindow 2023-09-12 21:41:00 +02:00
1a673f4b3a Documents: Add CreditNote 2023-09-12 21:40:33 +02:00
5ad8c88319 Models: Add Credit 2023-09-12 21:40:04 +02:00
30aaa64f59 BusinessDocument: Add UseBillingAddress flag 2023-09-12 13:49:11 +02:00
352bf840c3 DeliveryAdminWindow: Add user confirmation on closing when creating 2023-09-10 23:03:42 +02:00
898cece0d3 DeliveryAdminWindow: Small quality of life fixes 2023-09-09 21:33:37 +02:00
0b05cc4e10 Documents: move styles 2023-09-08 13:38:26 +02:00
74fa08e95d Windows: Remove '-verwaltung' from window names 2023-09-08 13:31:16 +02:00
bc6148624c DeliveryAdminWindow: Add export option for BKI 2023-09-08 12:44:29 +02:00
f5f00a7739 Export: Add Ebics and improve Bki export 2023-09-08 00:43:53 +02:00
2de4739e9d Models: Small payment fixes 2023-09-07 16:54:33 +02:00
47e8ab7e62 Export/Bki: Add 0.0 to KMW output 2023-09-07 16:40:21 +02:00
8a678509c5 Utils: Add UTF8 encoding without BOM 2023-09-07 16:39:49 +02:00
de53bfdd2b IAddress: Add IAddress 2023-09-07 13:49:56 +02:00
f9d95a48f2 Export: Refactor interfaces and classes 2023-09-07 00:44:28 +02:00
be734b880f Models: Add payment_member 2023-09-07 00:40:53 +02:00
1c45e95ef3 Utils: Add SplitAddress and SplitName 2023-09-07 00:39:45 +02:00
7086a72fab Windows: Add cursor waiting 2023-09-06 18:04:13 +02:00
540236f878 Documents: fix page breaking issues 2023-09-06 18:00:48 +02:00
aab95ee444 Documents: Split stylesheet into multiple files 2023-09-06 17:01:59 +02:00
2b7d19199a Add WeasyPrint to convert PDFs 2023-09-06 16:01:48 +02:00
28b424fe65 Graph.cs: move from Models/ to Helpers/Billing/ 2023-09-06 14:04:14 +02:00
324c5db94e ChartWindow: use System.Text.Json instead of Newtonsoft.Json 2023-09-06 14:00:30 +02:00
faaeefe6ce Documents: Update documents 2023-09-05 20:41:03 +02:00
b76d43a5ff ClientParameters: Change GWK to Winzerkeller 2023-09-05 15:20:53 +02:00
8b48882c86 BaseDataWindow: Keep IBAN spaces when saving 2023-09-05 15:16:23 +02:00
286279b89f Fixed ChartWindow 2023-09-04 13:15:51 +02:00
94 changed files with 4486 additions and 1517 deletions

View File

@ -11,6 +11,7 @@ using System.Windows.Threading;
using System.Globalization; using System.Globalization;
using System.Threading; using System.Threading;
using System.Windows.Markup; using System.Windows.Markup;
using System.Reflection;
namespace Elwig { namespace Elwig {
public partial class App : Application { public partial class App : Application {
@ -20,10 +21,23 @@ namespace Elwig {
public static readonly string TempPath = Path.Combine(Path.GetTempPath(), "Elwig"); public static readonly string TempPath = Path.Combine(Path.GetTempPath(), "Elwig");
public static readonly Config Config = new(DataPath + "config.ini"); public static readonly Config Config = new(DataPath + "config.ini");
public static int VersionMajor { get; private set; }
public static int VersionMinor { get; private set; }
public static int VersionPatch { get; private set; }
public static string Version {
get => $"{VersionMajor}.{VersionMinor}.{VersionPatch}";
private set {
var p = value.Split(".").Select(p => int.Parse(p.Trim())).ToArray();
VersionMajor = p.ElementAtOrDefault(0);
VersionMinor = p.ElementAtOrDefault(1);
VersionPatch = p.ElementAtOrDefault(2);
}
}
public static string ZwstId { get; private set; } public static string ZwstId { get; private set; }
public static string BranchName { get; private set; } public static string BranchName { get; private set; }
public static int? BranchPlz { get; private set; } public static int? BranchPlz { get; private set; }
public static string? BranchOrt { get; private set; } public static string? BranchLocation { get; private set; }
public static string? BranchAddress { get; private set; } public static string? BranchAddress { get; private set; }
public static string? BranchPhoneNr { get; private set; } public static string? BranchPhoneNr { get; private set; }
public static string? BranchFaxNr { get; private set; } public static string? BranchFaxNr { get; private set; }
@ -36,7 +50,7 @@ namespace Elwig {
public App() : base() { public App() : base() {
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance); System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
Directory.CreateDirectory(App.TempPath); Directory.CreateDirectory(TempPath);
Directory.CreateDirectory(DataPath); Directory.CreateDirectory(DataPath);
MainDispatcher = Dispatcher; MainDispatcher = Dispatcher;
Scales = Array.Empty<IScale>(); Scales = Array.Empty<IScale>();
@ -56,36 +70,35 @@ namespace Elwig {
new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)) new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag))
); );
Version = typeof(App).GetTypeInfo().Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion ?? "";
try {
AppDbUpdater.CheckDb();
} catch (Exception e) {
MessageBox.Show($"Invalid Database:\n\n{e.Message}", "Invalid Database", MessageBoxButton.OK, MessageBoxImage.Error);
Shutdown();
return;
}
Dictionary<string, (string, string, int?, string?, string?, string?, string?, string?)> branches = new(); Dictionary<string, (string, string, int?, string?, string?, string?, string?, string?)> branches = new();
using (var ctx = new AppDbContext()) { using (var ctx = new AppDbContext()) {
branches = ctx.Branches.ToDictionary(b => b.Name.ToLower(), b => (b.ZwstId, b.Name, b.PostalDest?.AtPlz?.Plz, b.PostalDest?.AtPlz?.Ort.Name, b.Address, b.PhoneNr, b.FaxNr, b.MobileNr));
try { try {
if (!ctx.Database.CanConnect()) { Client = new(ctx);
MessageBox.Show($"Invalid Database:\n\n{Config.DatabaseFile}", "Invalid Database", MessageBoxButton.OK, MessageBoxImage.Error);
Shutdown();
return;
} else {
branches = ctx.Branches.ToDictionary(b => b.Name.ToLower(), b => (b.ZwstId, b.Name, b.PostalDest?.AtPlz?.Plz, b.PostalDest?.AtPlz?.Dest, b.Address, b.PhoneNr, b.FaxNr, b.MobileNr));
try {
Client = new(ctx);
} catch (Exception e) {
MessageBox.Show($"Fehler beim Laden der Mandantendaten:\n\n{e.Message}", "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
Shutdown();
return;
}
}
} catch (Exception e) { } catch (Exception e) {
MessageBox.Show($"Invalid Database:\n\n{e.Message}", "Invalid Database", MessageBoxButton.OK, MessageBoxImage.Error); MessageBox.Show($"Fehler beim Laden der Mandantendaten:\n\n{e.Message}", "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
Shutdown(); Shutdown();
return; return;
} }
} }
Utils.RunBackground("HTML Initialization", () => Documents.Html.Init(PrintingReadyChanged)); Utils.RunBackground("HTML Initialization", () => Documents.Html.Init(PrintingReadyChanged));
Utils.RunBackground("PDF Initialization", () => Documents.Pdf.Init(PrintingReadyChanged)); Utils.RunBackground("PDF Initialization", () => Documents.Pdf.Init(PrintingReadyChanged));
var list = new List<IScale>(); var list = new List<IScale>();
foreach (var s in Config.Scales) { foreach (var s in Config.Scales) {
var id = s[0];
try { try {
var id = s[0];
var type = s[1]?.ToLower(); var type = s[1]?.ToLower();
var model = s[2]; var model = s[2];
var cnx = s[3]; var cnx = s[3];
@ -99,6 +112,7 @@ namespace Elwig {
throw new ArgumentException($"Invalid scale type: \"{type}\""); throw new ArgumentException($"Invalid scale type: \"{type}\"");
} }
} catch (Exception e) { } catch (Exception e) {
list.Add(new InvalidScale(id));
MessageBox.Show($"Unable to create scale {s[0]}:\n\n{e.Message}", "Scale Error", MessageBoxButton.OK, MessageBoxImage.Error); MessageBox.Show($"Unable to create scale {s[0]}:\n\n{e.Message}", "Scale Error", MessageBoxButton.OK, MessageBoxImage.Error);
} }
} }
@ -113,7 +127,7 @@ namespace Elwig {
ZwstId = entry.Item1; ZwstId = entry.Item1;
BranchName = entry.Item2; BranchName = entry.Item2;
BranchPlz = entry.Item3; BranchPlz = entry.Item3;
BranchOrt = entry.Item4; BranchLocation = entry.Item4?.Split(" im ")[0]; // FIXME
BranchAddress = entry.Item5; BranchAddress = entry.Item5;
BranchPhoneNr = entry.Item6; BranchPhoneNr = entry.Item6;
BranchFaxNr = entry.Item7; BranchFaxNr = entry.Item7;
@ -124,7 +138,7 @@ namespace Elwig {
ZwstId = entry.Item1; ZwstId = entry.Item1;
BranchName = entry.Item2; BranchName = entry.Item2;
BranchPlz = entry.Item3; BranchPlz = entry.Item3;
BranchOrt = entry.Item4; BranchLocation = entry.Item4?.Split(" im ")[0]; // FIXME
BranchAddress = entry.Item5; BranchAddress = entry.Item5;
BranchPhoneNr = entry.Item6; BranchPhoneNr = entry.Item6;
BranchFaxNr = entry.Item7; BranchFaxNr = entry.Item7;
@ -137,20 +151,18 @@ namespace Elwig {
base.OnStartup(evt); base.OnStartup(evt);
} }
protected override void OnExit(ExitEventArgs evt) {
Utils.RunBackground("PDF Close", () => Documents.Pdf.Close());
base.OnExit(evt);
}
private void PrintingReadyChanged() { private void PrintingReadyChanged() {
Dispatcher.BeginInvoke(OnPrintingReadyChanged, new EventArgs()); Dispatcher.BeginInvoke(OnPrintingReadyChanged, new EventArgs());
} }
protected void OnPrintingReadyChanged(EventArgs evt) { protected void OnPrintingReadyChanged(EventArgs evt) {
foreach (Window w in Windows) { foreach (Window w in Windows) {
foreach (var b in ControlUtils.FindAllChildren<Button>(w).Where(b => "Print".Equals(b.Tag))) { foreach (var b in ControlUtils.FindAllChildren<Button>(w).Where(b => b.Tag?.ToString() == "Print")) {
b.IsEnabled = IsPrintingReady; b.IsEnabled = IsPrintingReady;
} }
foreach (var i in ControlUtils.FindAllChildren<MenuItem>(w).Where(i => i.Tag?.ToString() == "Print")) {
i.IsEnabled = IsPrintingReady;
}
} }
} }
} }

View File

@ -9,24 +9,21 @@ namespace Elwig.Dialogs {
public int Weight = 0; public int Weight = 0;
public string? Reason = null; public string? Reason = null;
public ManualWeighingDialog() { public ManualWeighingDialog(string? reason = null) {
InitializeComponent(); InitializeComponent();
ReasonInput.Text = reason;
} }
private void ConfirmButton_Click(object sender, RoutedEventArgs evt) { private void ConfirmButton_Click(object sender, RoutedEventArgs evt) {
DialogResult = true; DialogResult = true;
Weight = int.Parse(WeightInput.Text); Weight = int.Parse(WeightInput.Text);
Reason = Regex.Replace(ReasonInput.Text, @"\s+", "").Trim(); Reason = Regex.Replace(ReasonInput.Text, @"\s+", " ").Trim();
if (Reason == "") { if (Reason == "") Reason = null;
Reason = null;
} else if (!Reason.EndsWith(".") || !Reason.EndsWith("!") || !Reason.EndsWith("?")) {
Reason += ".";
}
Close(); Close();
} }
private void UpdateButtons() { private void UpdateButtons() {
ConfirmButton.IsEnabled = WeightInput.Text.Length > 0 && ReasonInput.Text.Trim().Length > 0; ConfirmButton.IsEnabled = WeightInput.Text.Length > 0;
} }
private void WeightInput_TextChanged(object sender, TextChangedEventArgs evt) { private void WeightInput_TextChanged(object sender, TextChangedEventArgs evt) {

View File

@ -2,7 +2,7 @@
@inherits TemplatePage<Elwig.Documents.BusinessDocument> @inherits TemplatePage<Elwig.Documents.BusinessDocument>
@model Elwig.Documents.BusinessDocument @model Elwig.Documents.BusinessDocument
@{ Layout = "Document"; } @{ Layout = "Document"; }
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\style-businessdocument.css" />
<div class="info-wrapper"> <div class="info-wrapper">
<div class="address-wrapper"> <div class="address-wrapper">
<div class="sender"> <div class="sender">
@ -14,7 +14,8 @@
<address>@Model.Address</address> <address>@Model.Address</address>
</div> </div>
<aside>@Raw(Model.Aside)</aside> <aside>@Raw(Model.Aside)</aside>
@if (Model.ShowDateAndLocation) {
<div class="date">@Model.Location, am @($"{Model.Date:dd.MM.yyyy}")</div>
}
</div> </div>
<main> @RenderBody()
@RenderBody()
</main>

View File

@ -1,19 +1,23 @@
using Elwig.Helpers;
using Elwig.Models; using Elwig.Models;
namespace Elwig.Documents { namespace Elwig.Documents {
public abstract class BusinessDocument : Document { public abstract class BusinessDocument : Document {
public bool ShowDateAndLocation = false;
public Member Member; public Member Member;
public bool IncludeSender = false; public bool IncludeSender = false;
public string Aside { get; set; } public bool UseBillingAddress = false;
public string? Location { get; set; } public string Aside;
public string? Location;
public BusinessDocument(string title, Member m, bool includeSender = false) : base(title) { public BusinessDocument(string title, Member m, bool includeSender = false) : base(title) {
Member = m; Member = m;
Location = App.BranchName; Location = App.BranchLocation;
IncludeSender = includeSender; IncludeSender = includeSender;
var uid = (m.UstIdNr ?? "-") + (m.IsBuchführend ? "" : " <i>(pauschaliert)</i>"); var uid = (m.UstIdNr ?? "-") + (m.IsBuchführend ? "" : " <i>(pauschaliert)</i>");
Aside = $"<table><colgroup><col span='1' style='width: 2.25cm;'/><col span='1' style='width: 100%;'/></colgroup>" + Aside = $"<table><colgroup><col span='1' style='width: 22.5mm;'/><col span='1' style='width: 42.5mm;'/></colgroup>" +
$"<thead><tr><th colspan='2'>Mitglied</th></tr></thead><tbody>" + $"<thead><tr><th colspan='2'>Mitglied</th></tr></thead><tbody>" +
$"<tr><th>Mitglieds-Nr.</th><td>{m.MgNr}</td></tr>" + $"<tr><th>Mitglieds-Nr.</th><td>{m.MgNr}</td></tr>" +
$"<tr><th>Betriebs-Nr.</th><td>{m.LfbisNr}</td></tr>" + $"<tr><th>Betriebs-Nr.</th><td>{m.LfbisNr}</td></tr>" +
@ -23,13 +27,9 @@ namespace Elwig.Documents {
public string Address { public string Address {
get { get {
var b = Member.BillingAddress; IAddress addr = (Member.BillingAddress != null && UseBillingAddress) ? Member.BillingAddress : Member;
var plz = (b == null) ? Member.PostalDest.AtPlz : b.PostalDest.AtPlz; var plz = addr.PostalDest.AtPlz;
if (b != null) { return (addr is BillingAddr ? $"{addr.Name}\n" : "") + $"{Member.AdministrativeName}\n{addr.Address}\n{plz?.Plz} {plz?.Ort.Name.Split(",")[0]}\n{addr.PostalDest.Country.Name}";
return $"{b.Name}\n{Member.AdministrativeName}\n{b.Address}\n{plz.Plz} {plz.Dest}\nÖsterreich";
} else {
return $"{Member.AdministrativeName}\n{Member.Address}\n{plz.Plz} {plz.Dest}\nÖsterreich";
}
} }
} }
} }

View File

@ -2,7 +2,8 @@
@inherits TemplatePage<Elwig.Documents.BusinessLetter> @inherits TemplatePage<Elwig.Documents.BusinessLetter>
@model Elwig.Documents.BusinessLetter @model Elwig.Documents.BusinessLetter
@{ Layout = "BusinessDocument"; } @{ Layout = "BusinessDocument"; }
<main>
<p>Sehr geehrtes Mitglied,</p> <p>Sehr geehrtes Mitglied,</p>
<p>nein.</p> <p>nein.</p>
<p>Mit freundlichen Grüßen<br/>Ihre Winzergenossenschaft</p> <p>Mit freundlichen Grüßen<br/>Ihre Winzergenossenschaft</p>
</main>

View File

@ -0,0 +1,87 @@
@using RazorLight
@inherits TemplatePage<Elwig.Documents.CreditNote>
@model Elwig.Documents.CreditNote
@{ Layout = "BusinessDocument"; }
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\style-creditnote.css"/>
@{
var binNum = Model.BinNames.Length;
}
<main>
<h1>@Model.Title</h1>
<table class="credit">
<colgroup>
<col style="width: 24mm;"/>
<col style="width: 6mm;"/>
<col style="width: 31mm;"/>
<col style="width: 15mm;"/>
<col style="width: 8mm;"/>
<col style="width: 10mm;"/>
<col style="width: 12mm;"/>
<col style="width: 10mm;"/>
<col style="width: 12mm;"/>
<col style="width: 15mm;"/>
<col style="width: 17mm;"/>
</colgroup>
<thead>
<tr>
<th rowspan="3" style="text-align: left;">Lieferschein-Nr.</th>
<th rowspan="3">Pos.</th>
<th rowspan="3" style="text-align: left;">Sorte</th>
<th rowspan="3" style="text-align: left;">Attribut(e)</th>
<th rowspan="2" colspan="2">Gradation</th>
<th colspan="2">Zu-/Abschläge</th>
<th colspan="2">@Raw(string.Join("<br/>", Model.BinNames))</th>
<th rowspan="2">Betrag</th>
</tr>
<tr>
<th>Abs.</th>
<th>Rel.</th>
<th>Gewicht</th>
<th>Preis</th>
</tr>
<tr>
<th>[°Oe]</th>
<th>[°KMW]</th>
<th>[@Model.CurrencySymbol/kg]</th>
<th>[%]</th>
<th>[kg]</th>
<th>[@Model.CurrencySymbol/kg]</th>
<th>[@Model.CurrencySymbol]</th>
</tr>
</thead>
<tbody>
@{
string FormatRow(int? weight, decimal? amount) {
var w = weight == null || weight == 0 ? "-" : $"{weight:N0}";
return $"<td class='weight'>{w}</td><td class='amount'>{amount?.ToString("0." + string.Concat(Enumerable.Repeat('0', Model.Precision)))}</td>";
}
string? last = null;
}
@foreach (var part in Model.Parts) {
var pmt = part.Payment;
var abs = pmt?.ModAbs == null || pmt?.ModAbs == 0 ? "-" : pmt?.ModAbs.ToString("0." + string.Concat(Enumerable.Repeat('0', Model.Precision)));
var rel = pmt?.ModRel == null || pmt?.ModRel == 0 ? "-" : $"{pmt?.ModRel * 100:0.00##}";
<tr class="first @(binNum <= 1 ? "last" : "") @(last != null && last != part.SortId ? "new" : "")">
<td rowspan="@binNum" class="lsnr">@part.Delivery.LsNr</td>
<td rowspan="@binNum" class="dpnr">@part.DPNr</td>
<td rowspan="@binNum" class="variant">@part.Variant.Name</td>
<td rowspan="@binNum" class="attribute">@string.Join(" / ", part.PartAttributes.Select(a => a.AttrId))</td>
<td rowspan="@binNum" class="oe">@($"{part.Oe:N0}")</td>
<td rowspan="@binNum" class="kmw">@($"{part.Kmw:N1}")</td>
<td rowspan="@binNum" class="abs">@abs</td>
<td rowspan="@binNum" class="rel">@rel</td>
<!--FIXME price-->
@Raw(FormatRow(pmt?.DeliveryPart.Bins?.ElementAtOrDefault(0)?.Value, 0))
<td rowspan="@binNum" class="amount sum">@($"{pmt?.Amount:N2}")</td>
</tr>
@for (int i = 1; i < binNum; i++) {
<tr class="@(i == binNum - 1 ? "last" : "")">
<!--FIXME price-->
@Raw(FormatRow(pmt?.DeliveryPart.Bins?.ElementAtOrDefault(i)?.Value, 0))
</tr>
}
last = part.SortId;
}
</tbody>
</table>
</main>

View File

@ -0,0 +1,40 @@
using Elwig.Helpers;
using Elwig.Models;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
namespace Elwig.Documents {
public class CreditNote : BusinessDocument {
public Credit Credit;
public string? Text;
public string CurrencySymbol;
public string[] BinNames;
public int Precision;
public IEnumerable<DeliveryPart> Parts;
public CreditNote(Credit c, AppDbContext ctx) : base($"Traubengutschrift Nr. {c.TgId} {c.Payment.Variant.Name}", c.Member) {
UseBillingAddress = true;
ShowDateAndLocation = true;
Credit = c;
Aside = Aside.Replace("</table>", "") +
$"<thead><tr><th colspan='2'>Gutschrift</th></tr></thead><tbody>" +
$"<tr><th>TG-Nr.</th><td>{c.TgId}</td></tr>" +
$"<tr><th>Überw. am</th><td>{c.Payment.Variant.TransferDate:dd.MM.yyyy}</td></tr>" +
$"<tr><th>Datum/Zeit</th><td>{c.ModifiedTimestamp:dd.MM.yyyy} / {c.ModifiedTimestamp:HH:mm}</td></tr>" +
$"</tbody></table>";
Text = App.Client.TextDeliveryNote;
DocumentId = $"Tr.-Gutschr. {c.TgId}";
CurrencySymbol = c.Payment.Variant.Season.Currency.Symbol ?? c.Payment.Variant.Season.Currency.Code;
BinNames = new string[0]; // FIXME
Precision = c.Payment.Variant.Season.Precision;
Parts = ctx.DeliveryParts.FromSql($"""
SELECT p.*
FROM v_delivery v
JOIN delivery_part p ON (p.year, p.did, p.dpnr) = (v.year, v.did, v.dpnr)
WHERE (v.year, v.mgnr) = ({c.Year}, {c.Member.MgNr})
ORDER BY sortid, LENGTH(attributes) DESC, attributes, kmw DESC, date, time, dpnr
""").ToList();
}
}}

View File

@ -0,0 +1,166 @@
@using RazorLight
@inherits TemplatePage<Elwig.Documents.DeliveryConfirmation>
@model Elwig.Documents.DeliveryConfirmation
@{ Layout = "BusinessDocument"; }
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\style-deliveryconfirmation.css"/>
<main>
<h1>@Model.Title</h1>
<table class="delivery-confirmation">
<colgroup>
<col style="width: 25mm;"/>
<col style="width: 5mm;"/>
<col style="width: 20mm;"/>
<col style="width: 21mm;"/>
<col style="width: 19mm;"/>
<col style="width: 10mm;"/>
<col style="width: 10mm;"/>
<col style="width: 15mm;"/>
<col style="width: 12mm;"/>
<col style="width: 14mm;"/>
<col style="width: 14mm;"/>
</colgroup>
<thead>
<tr>
<th rowspan="2" style="text-align: left;">Lieferschein-Nr.</th>
<th rowspan="2">Pos.</th>
<th rowspan="2" style="text-align: left;">Sorte</th>
<th rowspan="2" style="text-align: left;">Attribut(e)</th>
<th rowspan="2" style="text-align: left;">Qualitätsstufe</th>
<th colspan="2">Gradation</th>
<th colspan="2">Flächenbindung</th>
<th>Gewicht</th>
<th>Davon<br/>abzuwerten</th>
</tr>
<tr>
<th>[°Oe]</th>
<th>[°KMW]</th>
<th colspan="2">[kg]</th>
<th>[kg]</th>
<th>[kg]</th>
</tr>
</thead>
<tbody>
@{
var lastSortId = "";
}
@foreach (var p in Model.Deliveries) {
var bins = p.Bins.Where(b => b.Value > 0).OrderByDescending(b => b.BinNr).ToArray();
var rowsBins = bins.Length;
var mods = p.Modifiers.Select(m => m.Name).ToArray();
var rowsMod = mods.Length + 1;
var rows = Math.Max(rowsBins, rowsMod);
var first = true;
@for (int i = 0; i < rows; i++) {
<tr class="@(first ? "first" : "") @(p.SortId != lastSortId && lastSortId != "" ? "new": "") @(rows > i + 1 ? "trailing" : "")">
@if (first) {
<td rowspan="@rows">@p.Delivery.LsNr</td>
<td rowspan="@rows">@p.DPNr</td>
<td class="small">@p.Variant.Name</td>
<td class="small">@p.AttributesString</td>
<td class="small">@p.Quality.Name</td>
<td rowspan="@rows" class="grad">@($"{p.Oe:N0}")</td>
<td rowspan="@rows" class="grad">@($"{p.Kmw:N1}")</td>
}
@if (i > 0 && i <= mods.Length) {
<td colspan="3" class="mod">@(mods[i - 1])</td>
} else if (i > 0) {
<td colspan="3"></td>
}
@if (i < bins.Length) {
var bin = bins[i];
<td class="geb">@(bin.Discr == "_" ? "ungeb." : $"geb. {p.SortId}{bin.Discr}"):</td>
<td class="weight">@($"{bin.Value:N0}")</td>
} else {
<td colspan="2"></td>
}
@if (i == bins.Length - 1) {
<td class="weight">@($"{p.Weight:N0}")</td>
} else {
<td></td>
}
@if (first) {
<td rowspan="@rows" class="weight"></td>
first = false;
}
</tr>
lastSortId = p.SortId;
}
}
<tr class="sum">
<td colspan="8">Gesamt:</td>
<td colspan="2" class="weight">@($"{Model.Deliveries.Sum(p => p.Weight):N0}")</td>
<td></td>
</tr>
</tbody>
</table>
<table class="delivery-confirmation-stats">
<colgroup>
<col style="width: 45mm;"/>
<col style="width: 17mm;"/>
<col style="width: 17mm;"/>
<col style="width: 17mm;"/>
<col style="width: 19mm;"/>
<col style="width: 16mm;"/>
<col style="width: 17mm;"/>
<col style="width: 17mm;"/>
</colgroup>
<thead>
<tr>
<th><b>Lese @Model.Year</b> per @($"{Model.Date:dd.MM.yyyy}") [kg]</th>
<th>Lieferpflicht</th>
<th>Lieferrecht</th>
<th>Unterliefert</th>
<th>Noch lieferbar</th>
<th>Überliefert</th>
<th>Zugeteilt</th>
<th>Geliefert</th>
</tr>
</thead>
<tbody>
@{
string FormatRow(int mode, int obligation, int right, int sum, int? payment = null) {
var isGa = mode == 0;
payment ??= sum;
return $"<td>{(mode == 1 ? "" : obligation == 0 ? "-" : $"{obligation:N0}")}</td>" +
$"<td>{(mode == 1 ? "" : right == 0 ? "-" : $"{right:N0}")}</td>" +
$"<td>{(mode == 1 ? "" : payment < obligation ? $"<b>{obligation - payment:N0}\x3c/b>" : "-")}</td>" +
$"<td>{(mode == 1 ? "" : payment >= obligation && sum <= right ? $"{right - sum:N0}" : "-")}</td>" +
$"<td>{(mode == 1 ? "" : obligation == 0 && right == 0 ? "-" : (sum > right ? ((isGa ? "<b>" : "") + $"{sum - right:N0}" + (isGa ? "</b>" : "")) : "-"))}</td>" +
$"<td>{(mode != 2 ? "" : obligation == 0 && right == 0 ? "-" : $"{payment:N0}")}</td>" +
$"<td>{sum:N0}</td>";
}
var mBins = Model.MemberBins.Where(b => b.Value.Item2 > 0 || b.Value.Item3 > 0 || b.Value.Item4 > 0).ToList();
var fbVars = mBins.Where(b => b.Value.Item2 > 0 || b.Value.Item3 > 0).Select(b => b.Key.Replace("_", "")).Order().ToArray();
var fbs = mBins.Where(b => fbVars.Contains(b.Key)).OrderBy(b => b.Value.Item1);
var rem = mBins.Where(b => !fbVars.Contains(b.Key)).OrderBy(b => b.Value.Item1);
}
<tr>
<th>Gesamtlieferung lt. gez. GA</th>
@Raw(FormatRow(0, Model.Member.DeliveryObligation, Model.Member.DeliveryRight, Model.Member.Deliveries.Where(d => d.Year == Model.Year).Sum(d => d.Weight)))
</tr>
@if (rem.Any()) {
<tr class="subheading"><th colspan="8">Sortenaufteilung:</th></tr>
}
@foreach (var (id, (name, right, obligation, sum, payment)) in rem) {
<tr>
<th>@name</th>
@Raw(FormatRow(1, obligation, right, sum, payment))
</tr>
}
@if (fbs.Any()){
<tr class="subheading"><th colspan="8">Flächenbindungen:</th></tr>
}
@foreach (var (id, (name, right, obligation, sum, payment)) in fbs) {
<tr>
<th>@name</th>
@Raw(FormatRow(2, obligation, right, sum, payment))
</tr>
}
</tbody>
</table>
<div class="text" style="margin-top: 2em;">
@if (Model.Text != null) {
<p class="comment" style="white-space: pre-wrap; break-inside: avoid;">@Model.Text</p>
}
</div>
</main>

View File

@ -0,0 +1,35 @@
using Elwig.Helpers;
using Elwig.Models;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
namespace Elwig.Documents {
public class DeliveryConfirmation : BusinessDocument {
public int Year;
public IEnumerable<DeliveryPart> Deliveries;
public string? Text = App.Client.TextDeliveryConfirmation;
public Dictionary<string, (string, int, int, int, int)> MemberBins;
public DeliveryConfirmation(AppDbContext ctx, int year, Member m, IEnumerable<DeliveryPart>? deliveries = null) :
base($"Anlieferungsbestätigung {year}", m) {
Year = year;
ShowDateAndLocation = true;
UseBillingAddress = true;
IncludeSender = true;
DocumentId = $"Anl.-Best. {Year}/{m.MgNr}";
Deliveries = deliveries ?? ctx.DeliveryParts.FromSqlRaw($"""
SELECT p.*
FROM v_delivery v
JOIN delivery_part p ON (p.year, p.did, p.dpnr) = (v.year, v.did, v.dpnr)
WHERE (v.year, v.mgnr) = ({Year}, {m.MgNr})
ORDER BY v.sortid, v.abgewertet ASC,
COALESCE(LENGTH(v.attributes), 0) ASC, attribute_prio DESC, COALESCE(v.attributes, '~'),
v.kmw DESC, v.lsnr, v.dpnr
""")
.ToList();
MemberBins = ctx.GetMemberBins(Year, m.MgNr).GetAwaiter().GetResult();
}
}
}

View File

@ -0,0 +1,67 @@
@using RazorLight
@inherits TemplatePage<Elwig.Documents.DeliveryJournal>
@model Elwig.Documents.DeliveryJournal
@{ Layout = "Document"; }
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\style-deliveryjournal.css"/>
<main>
<h1>Lieferjournal</h1>
<h2>@Model.Filter</h2>
<table class="journal">
<colgroup>
<col style="width: 25mm;"/>
<col style="width: 5mm;"/>
<col style="width: 17mm;"/>
<col style="width: 10mm;"/>
<col style="width: 8mm;"/>
<col style="width: 38mm;"/>
<col style="width: 28mm;"/>
<col style="width: 10mm;"/>
<col style="width: 10mm;"/>
<col style="width: 14mm;"/>
</colgroup>
<thead>
<tr>
<th rowspan="2" style="text-align: left;">Lieferschein-Nr.</th>
<th rowspan="2">Pos.</th>
<th rowspan="2">Datum</th>
<th rowspan="2">Zeit</th>
<th colspan="2" rowspan="2" style="text-align: left;">Mitglied</th>
<th rowspan="2" style="text-align: left;">Sorte</th>
<th colspan="2">Gradation</th>
<th>Gewicht</th>
</tr>
<tr>
<th>[°Oe]</th>
<th>[°KMW]</th>
<th>[kg]</th>
</tr>
</thead>
<tbody>
@foreach (var p in Model.Deliveries) {
<tr>
<td>@p.Delivery.LsNr</td>
<td>@p.DPNr</td>
<td>@($"{p.Delivery.Date:dd.MM.yyyy}")</td>
<td>@($"{p.Delivery.Time:HH:mm}")</td>
<td class="mgnr">@p.Delivery.Member.MgNr</td>
<td>@p.Delivery.Member.AdministrativeName</td>
<td>@p.Variant.Name</td>
<td class="grad">@($"{p.Oe:N0}")</td>
<td class="grad">@($"{p.Kmw:N1}")</td>
<td class="weight">@($"{p.Weight:N0}")</td>
</tr>
}
<tr class="sum">
@{
var kmw = Elwig.Helpers.Utils.AggregateDeliveryPartsKmw(Model.Deliveries);
var oe = Elwig.Helpers.Utils.KmwToOe(kmw);
}
<td colspan="2">Gesamt:</td>
<td colspan="5">(Teil-)Lieferungen: @($"{Model.Deliveries.DistinctBy(p => p.Delivery).Count():N0}") (@($"{Model.Deliveries.Count():N0}"))</td>
<td class="grad">@($"{oe:N0}")</td>
<td class="grad">@($"{kmw:N1}")</td>
<td class="weight">@($"{Model.Deliveries.Sum(p => p.Weight):N0}")</td>
</tr>
</tbody>
</table>
</main>

View File

@ -0,0 +1,33 @@
using Elwig.Helpers;
using Elwig.Models;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Elwig.Documents {
public class DeliveryJournal : Document {
public string Filter;
public IEnumerable<DeliveryPart> Deliveries;
public DeliveryJournal(string filter, IEnumerable<DeliveryPart> deliveries) : base($"Lieferjournal {filter}") {
Filter = filter;
Deliveries = deliveries;
}
public DeliveryJournal(string filter, IQueryable<DeliveryPart> deliveries) :
this(filter, deliveries
.Include(p => p.Delivery).ThenInclude(d => d.Member)
.Include(p => p.Variant)
.ToList()) { }
public DeliveryJournal(AppDbContext ctx, DateOnly date) :
this(date.ToString("dd.MM.yyyy"), ctx.DeliveryParts
.Where(p => p.Delivery.DateString == date.ToString("yyy-MM-dd"))
.OrderBy(p => p.Delivery.DateString)
.ThenBy(p => p.Delivery.TimeString)
.ThenBy(p => p.Delivery.LsNr)
.ThenBy(p => p.DPNr)) { }
}
}

View File

@ -2,47 +2,20 @@
@inherits TemplatePage<Elwig.Documents.DeliveryNote> @inherits TemplatePage<Elwig.Documents.DeliveryNote>
@model Elwig.Documents.DeliveryNote @model Elwig.Documents.DeliveryNote
@{ Layout = "BusinessDocument"; } @{ Layout = "BusinessDocument"; }
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\style-deliverynote.css" />
<div class="date">@Model.Location, am @($"{Model.Date:dd.MM.yyyy}")</div> <main>
<h1>@Model.Title</h1> <h1>@Model.Title</h1>
@{
bool displayStats = true; // Model.Delivery.Year == Model.CurrentNextSeason
}
<script>
document.addEventListener("DOMContentLoaded", () => {
const hidden = document.getElementsByClassName("hidden")[0];
const bottom = hidden.offsetTop + hidden.offsetHeight;
const cm = bottom * 2.54 / 96 * window.devicePixelRatio;
if (cm > 25.75) {
// force page break
const table = document.getElementsByClassName("delivery")[0];
const hr = document.createElement("hr");
hr.classList.add("page-break");
table.before(hr);
const stats = document.getElementById("delivery-stats");
stats.getElementsByTagName("table")[0].classList.add("expanded");
hr.before(stats);
const p = document.createElement("p");
p.innerText = "Siehe nächste Seite."
stats.before(p);
}
});
</script>
<table class="delivery"> <table class="delivery">
<colgroup> <colgroup>
<col style="width: 1cm;"/> <col style="width: 10.00mm;"/>
<col style="width: 25%;"/> <col style="width: 21.25mm;"/>
<col style="width: 25%;"/> <col style="width: 21.25mm;"/>
<col style="width: 25%;"/> <col style="width: 21.25mm;"/>
<col style="width: 25%;"/> <col style="width: 21.25mm;"/>
<col style="width: 3cm;"/> <col style="width: 30.00mm;"/>
<col style="width: 1.25cm;"/> <col style="width: 12.50mm;"/>
<col style="width: 1.25cm;"/> <col style="width: 12.50mm;"/>
<col style="width: 1.5cm;"/> <col style="width: 15.00mm;"/>
</colgroup> </colgroup>
<thead> <thead>
<tr> <tr>
@ -79,7 +52,8 @@
} }
} }
<tr><td></td><td colspan="5"> <tr><td></td><td colspan="5">
@Raw(part.ManualWeighing ? "<i>Handwiegung</i>" : $"<i>Waage:</i> {part.ScaleId ?? "?"}, <i>ID:</i> {part.WeighingId ?? "?"}") (@(part.IsGerebelt ? "gerebelt gewogen" : "nicht gerebelt gewogen")) @Raw(part.ManualWeighing ? "<i>Handwiegung</i>" : $"<i>Waage:</i> {part.ScaleId ?? "?"}, <i>ID:</i> {part.WeighingId ?? "?"}")
(@(part.IsGerebelt ? "gerebelt gewogen" : "nicht gerebelt gewogen"))@Raw(part.WeighingReason != null ? $", <i>Begründung:</i>" : "") @part.WeighingReason
</td></tr> </td></tr>
@if (part.Comment != null) { @if (part.Comment != null) {
<tr><td></td><td colspan="5"><i>Anmerkung:</i> @part.Comment</td></tr> <tr><td></td><td colspan="5"><i>Anmerkung:</i> @part.Comment</td></tr>
@ -101,59 +75,64 @@
@if (Model.Delivery.Comment != null) { @if (Model.Delivery.Comment != null) {
<p class="comment">Amerkung zur Lieferung: @Model.Delivery.Comment</p> <p class="comment">Amerkung zur Lieferung: @Model.Delivery.Comment</p>
} }
@if (displayStats) { @if (Model.DisplayStats > 0) {
<div id="delivery-stats"> <table class="delivery-note-stats @(Model.DisplayStats > 2 ? "expanded" : "")">
<table class="delivery-stats"> <colgroup>
<colgroup> <col style="width: 45mm;"/>
<col style="width: 100%;"/> <col style="width: 20mm;"/>
<col style="width: 2cm;"/> <col style="width: 20mm;"/>
<col style="width: 2cm;"/> <col style="width: 20mm;"/>
<col style="width: 2cm;"/> <col style="width: 20mm;"/>
<col style="width: 2cm;"/> <col style="width: 20mm;"/>
<col style="width: 2cm;"/> <col style="width: 20mm;"/>
<col style="width: 2cm;"/> </colgroup>
</colgroup> <thead>
<thead> <tr>
<tr> <th><b>Lese @Model.Delivery.Year</b> per @($"{Model.Date:dd.MM.yyyy}") [kg]</th>
<th><b>Gesamtlieferung</b> [kg]</th> <th>Lieferpflicht</th>
<th>Lieferpflicht</th> <th>Lieferrecht</th>
<th>Lieferrecht</th> <th>Unterliefert</th>
<th>Unterliefert</th> <th>Noch lieferbar</th>
<th>Noch zu liefern</th> <th>Überliefert</th>
<th>Überliefert</th> <th>Geliefert</th>
<th>Geliefert</th> </tr>
</tr> </thead>
</thead> <tbody>
<tbody> @{
@{ string FormatRow(int obligation, int right, int sum) {
string FormatRow(int obligation, int right, int sum) { return $"<td>{obligation:N0}</td>" +
return $"<td>{obligation:N0}</td>" + $"<td>{right:N0}</td>" +
$"<td>{right:N0}</td>" + $"<td>{(sum < obligation ? $"{obligation - sum:N0}" : "-")}</td>" +
$"<td>{(sum < obligation ? $"{obligation - sum:N0}" : "-")}</td>" + $"<td>{(sum >= obligation && sum <= right ? $"{right - sum:N0}" : "-")}</td>" +
$"<td>{(sum >= obligation && sum <= right ? $"{right - sum:N0}" : "-")}</td>" + $"<td>{(sum > right ? $"{sum - right:N0}" : "-")}</td>" +
$"<td>{(sum > right ? $"{sum - right:N0}" : "-")}</td>" + $"<td>{sum:N0}</td>";
$"<td>{sum:N0}</td>";
}
var sortids = Model.Delivery.Parts.Select(p => p.SortId).ToList();
} }
<tr> var sortids = Model.Delivery.Parts.Select(p => p.SortId).ToList();
<th>Geschäftsanteile</th> var bins = Model.MemberBins.GroupBy(b => b.Key[..2]).ToDictionary(g => g.Key, g => g.Count());
@Raw(FormatRow(Model.Member.DeliveryObligation, Model.Member.DeliveryRight, Model.Member.Deliveries.Where(d => d.Year == Model.Delivery.Year).Sum(d => d.Weight))) }
<tr>
<th>Gesamtlieferung lt. gez. GA</th>
@Raw(FormatRow(Model.Member.DeliveryObligation, Model.Member.DeliveryRight, Model.Member.Deliveries.Where(d => d.Year == Model.Delivery.Year).Sum(d => d.Weight)))
</tr>
@if (Model.DisplayStats > 1) {
<tr class="subheading">
<th>Flächenbindungen:</th>
</tr> </tr>
@foreach (var (id, name, right, obligation, sum) in Model.MemberBuckets.OrderBy(b => b.Item1)) { @foreach (var (id, (name, right, obligation, sum, _)) in Model.MemberBins.OrderBy(b => b.Key)) {
if (right > 0 && obligation > 0) { if (right > 0 || obligation > 0 || (sum > 0 && bins[id[..2]] > 1 && !id.EndsWith('_'))) {
<tr class="@(sortids.Contains(id[..2]) ? "" : "optional")"> <tr class="@(sortids.Contains(id[..2]) ? "" : "optional")">
<th>@name</th> <th>@name</th>
@Raw(FormatRow(obligation, right, sum)) @Raw(FormatRow(obligation, right, sum))
</tr> </tr>
} }
} }
</tbody> }
</table> </tbody>
</div> </table>
} }
</main>
@for (int i = 0; i < 2; i++) { @for (int i = 0; i < 2; i++) {
<div class="@(i == 0 ? "hidden" : "bottom")"> <div class="text @(i == 0 ? "hidden" : "bottom")">
@if (Model.Text != null) { @if (Model.Text != null) {
<p class="comment">@Model.Text</p> <p class="comment">@Model.Text</p>
} }

View File

@ -7,9 +7,17 @@ namespace Elwig.Documents {
public Delivery Delivery; public Delivery Delivery;
public string? Text; public string? Text;
public IEnumerable<(string, string, int, int, int)> MemberBuckets; public Dictionary<string, (string, int, int, int, int)> MemberBins;
// 0 - none
// 1 - GA only
// 2 - GA only and area commitments of varieties from delivery note
// 3 - full
public int DisplayStats = App.Client.ModeDeliveryNoteStats;
public DeliveryNote(Delivery d, AppDbContext ctx) : base($"Traubenübernahmeschein Nr. {d.LsNr}", d.Member) { public DeliveryNote(Delivery d, AppDbContext ctx) : base($"Traubenübernahmeschein Nr. {d.LsNr}", d.Member) {
UseBillingAddress = true;
ShowDateAndLocation = true;
Delivery = d; Delivery = d;
Aside = Aside.Replace("</table>", "") + Aside = Aside.Replace("</table>", "") +
$"<thead><tr><th colspan='2'>Lieferung</th></tr></thead><tbody>" + $"<thead><tr><th colspan='2'>Lieferung</th></tr></thead><tbody>" +
@ -19,7 +27,7 @@ namespace Elwig.Documents {
$"</tbody></table>"; $"</tbody></table>";
Text = App.Client.TextDeliveryNote; Text = App.Client.TextDeliveryNote;
DocumentId = d.LsNr; DocumentId = d.LsNr;
MemberBuckets = ctx.GetMemberBuckets(d.Member, d.Year).GetAwaiter().GetResult(); MemberBins = ctx.GetMemberBins(d.Year, d.Member.MgNr).GetAwaiter().GetResult();
} }
} }
} }

View File

@ -1,26 +1,24 @@
@using RazorLight @using RazorLight
@inherits TemplatePage<Elwig.Documents.Document> @inherits TemplatePage<Elwig.Documents.Document>
@model Elwig.Documents.Document @model Elwig.Documents.Document
<!DOCTYPE html> <!DOCTYPE html>
<html lang="de-AT"> <html lang="de-AT">
<head> <head>
<title>@Model.Title</title> <title>@Model.Title</title>
<meta name="author" value="@Model.Author"/>
<meta charset="UTF-8"/> <meta charset="UTF-8"/>
<script> <link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\style.css"/>
window.PagedConfig = { auto: false }; <link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\style-page.css"/>
if (!navigator.webdriver) {
window.addEventListener("beforeprint", async () => { await window.PagedPolyfill.preview(); });
window.addEventListener("afterprint", () => { location.reload(); });
}
</script>
<script src="@Raw(Model.DataPath)\resources\paged.polyfill.js"></script>
<link rel="stylesheet" href="@Raw(Model.DataPath)\resources\style.css" />
</head> </head>
<body> <body>
<div class="m1"></div> @if (Model.ShowFoldMarks) {
<div class="m2"></div> <div class="m1"></div>
<div class="m3"></div> <div class="m2"></div>
<div class="m3"></div>
<div class="m1 r"></div>
<div class="m2 r"></div>
<div class="m3 r"></div>
}
<div class="footer-wrapper"> <div class="footer-wrapper">
<div class="pre-footer"> <div class="pre-footer">
<span class="date">@($"{Model.Date:dddd, d. MMMM yyyy}")</span> <span class="date">@($"{Model.Date:dddd, d. MMMM yyyy}")</span>

View File

@ -2,18 +2,32 @@ using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.IO; using System.IO;
using Elwig.Helpers; using Elwig.Helpers;
using System.Collections.Generic;
using System.Linq;
namespace Elwig.Documents { namespace Elwig.Documents {
public abstract class Document : IDisposable { public abstract partial class Document : IDisposable {
private TempFile? PdfFile = null; private TempFile? _pdfFile = null;
public bool ShowFoldMarks = App.Config.Debug;
public string DataPath;
public int CurrentNextSeason;
public string? DocumentId;
public string Title;
public string Author;
public string Header;
public string Footer;
public DateTime Date;
public Document(string title) { public Document(string title) {
var c = App.Client; var c = App.Client;
DataPath = App.DataPath; DataPath = App.DataPath;
CurrentNextSeason = Utils.CurrentNextSeason; CurrentNextSeason = Utils.CurrentNextSeason;
Title = title; Title = title;
Header = $"<h1>{c.Name}</h1>"; Author = c.NameFull;
Header = $"<div class='name'>{c.Name}</div><div class='suffix'>{c.NameSuffix}</div><div class='type'>{c.NameTypeFull}</div>";
Footer = Utils.GenerateFooter("<br/>", " \u00b7 ") Footer = Utils.GenerateFooter("<br/>", " \u00b7 ")
.Item(c.NameFull).NextLine() .Item(c.NameFull).NextLine()
.Item(c.Address).Item($"{c.Plz} {c.Ort}").Item("Österreich").Item("Tel.", c.PhoneNr).Item("Fax", c.FaxNr).NextLine() .Item(c.Address).Item($"{c.Plz} {c.Ort}").Item("Österreich").Item("Tel.", c.PhoneNr).Item("Fax", c.FaxNr).NextLine()
@ -28,18 +42,14 @@ namespace Elwig.Documents {
} }
public void Dispose() { public void Dispose() {
PdfFile?.Dispose(); _pdfFile?.Dispose();
PdfFile = null; _pdfFile = null;
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
public string DataPath { get; set; } public static Document Merge(IEnumerable<Document> docs) {
public int CurrentNextSeason { get; set; } return new MergedDocument(docs);
public string Title { get; set; } }
public string Header { get; set; }
public string Footer { get; set; }
public DateTime Date { get; set; }
public string? DocumentId { get; set; }
private async Task<string> Render() { private async Task<string> Render() {
string name; string name;
@ -47,35 +57,76 @@ namespace Elwig.Documents {
name = "BusinessLetter"; name = "BusinessLetter";
} else if (this is DeliveryNote) { } else if (this is DeliveryNote) {
name = "DeliveryNote"; name = "DeliveryNote";
} else if (this is CreditNote) {
name = "CreditNote";
} else if (this is DeliveryJournal) {
name = "DeliveryJournal";
} else if (this is Letterhead) {
name = "Letterhead";
} else if (this is DeliveryConfirmation) {
name = "DeliveryConfirmation";
} else { } else {
throw new InvalidOperationException("Invalid document object"); throw new InvalidOperationException("Invalid document object");
} }
return await Html.CompileRenderAsync(name, this); return await Render(name);
} }
public async Task Generate() { private async Task<string> Render(string name) {
var pdf = new TempFile("pdf"); return await Html.CompileRenderAsync(name, this); ;
using (var tmpHtml = new TempFile("html")) { }
await File.WriteAllTextAsync(tmpHtml.FilePath, await Render());
await Pdf.Convert(tmpHtml.FilePath, pdf.FilePath); public async Task Generate(IProgress<double>? progress = null) {
progress?.Report(0.0);
if (this is MergedDocument m) {
var pdf = new TempFile("pdf");
var tmpHtmls = new List<TempFile>();
var n = m.Documents.Count();
int i = 0;
foreach (var doc in m.Documents) {
var tmpHtml = new TempFile("html");
await File.WriteAllTextAsync(tmpHtml.FilePath, await doc.Render(), Utils.UTF8);
tmpHtmls.Add(tmpHtml);
i++;
progress?.Report(50.0 * i / n);
}
progress?.Report(50.0);
await Pdf.Convert(tmpHtmls.Select(f => f.FileName), pdf.FileName, new Progress<double>(v => progress?.Report(50.0 + v / 2)));
foreach (var tmp in tmpHtmls) {
tmp.Dispose();
}
_pdfFile = pdf;
} else {
var pdf = new TempFile("pdf");
using (var tmpHtml = new TempFile("html")) {
await File.WriteAllTextAsync(tmpHtml.FilePath, await Render(), Utils.UTF8);
progress?.Report(50.0);
await Pdf.Convert(tmpHtml.FilePath, pdf.FilePath);
}
_pdfFile = pdf;
} }
Pdf.UpdateMetadata(pdf.FilePath, Title, App.Client.NameFull); progress?.Report(100.0);
PdfFile = pdf;
} }
public void SaveTo(string pdfPath) { public void SaveTo(string pdfPath) {
if (PdfFile == null) throw new InvalidOperationException("Pdf file has not been generated yet"); if (_pdfFile == null) throw new InvalidOperationException("Pdf file has not been generated yet");
File.Copy(PdfFile.FilePath, pdfPath); File.Copy(_pdfFile.FilePath, pdfPath);
} }
public async Task Print(int copies = 1) { public async Task Print(int copies = 1) {
if (PdfFile == null) throw new InvalidOperationException("Pdf file has not been generated yet"); if (_pdfFile == null) throw new InvalidOperationException("Pdf file has not been generated yet");
await Pdf.Print(PdfFile.FilePath, copies); await Pdf.Print(_pdfFile.FilePath, copies);
} }
public void Show() { public void Show() {
if (PdfFile == null) throw new InvalidOperationException("Pdf file has not been generated yet"); if (_pdfFile == null) throw new InvalidOperationException("Pdf file has not been generated yet");
Pdf.Show(PdfFile.NewReference(), Title); Pdf.Show(_pdfFile.NewReference(), Title + (this is BusinessDocument b ? $" - {b.Member.Name}" : ""));
}
private class MergedDocument : Document {
public IEnumerable<Document> Documents;
public MergedDocument(IEnumerable<Document> docs) : base("Mehrere Dokumente") {
Documents = docs;
}
} }
} }
} }

View File

@ -18,6 +18,10 @@ namespace Elwig.Documents {
await e.CompileTemplateAsync("BusinessDocument"); await e.CompileTemplateAsync("BusinessDocument");
await e.CompileTemplateAsync("BusinessLetter"); await e.CompileTemplateAsync("BusinessLetter");
await e.CompileTemplateAsync("DeliveryNote"); await e.CompileTemplateAsync("DeliveryNote");
await e.CompileTemplateAsync("CreditNote");
await e.CompileTemplateAsync("DeliveryJournal");
await e.CompileTemplateAsync("Letterhead");
await e.CompileTemplateAsync("DeliveryConfirmation");
Engine = e; Engine = e;
evtHandler(); evtHandler();

View File

@ -0,0 +1,9 @@
@using RazorLight
@inherits TemplatePage<Elwig.Documents.Letterhead>
@model Elwig.Documents.Letterhead
@{ Layout = "BusinessDocument"; }
<style>
header, .footer-wrapper {
visibility: hidden;
}
</style>

View File

@ -0,0 +1,9 @@
using Elwig.Models;
namespace Elwig.Documents {
public class Letterhead : BusinessDocument {
public Letterhead(Member m) : base($"Briefkopf {m.Name}", m, true) {
Aside = "";
}
}
}

View File

@ -1,64 +1,58 @@
using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows;
using PdfSharp.Pdf.IO;
using PuppeteerSharp;
using Elwig.Helpers; using Elwig.Helpers;
using Elwig.Windows; using Elwig.Windows;
using System.Diagnostics; using System.Diagnostics;
using System;
using System.IO;
using System.Collections.Generic;
using System.Windows;
using System.Text.RegularExpressions;
using System.Linq;
namespace Elwig.Documents { namespace Elwig.Documents {
public static class Pdf { public static class Pdf {
private static readonly string Chromium = @"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe";
private static readonly string PdfToPrinter = App.ExePath + "PDFtoPrinter.exe"; private static readonly string PdfToPrinter = App.ExePath + "PDFtoPrinter.exe";
private static IBrowser? Browser = null; private static readonly string WinziPrint = App.ExePath + "WinziPrint.exe";
public static bool IsReady => Browser != null; private static Process? WinziPrintProc;
public static bool IsReady => WinziPrintProc != null;
public static async Task Init(Action evtHandler) { public static async Task Init(Action evtHandler) {
Browser = await Puppeteer.LaunchAsync(new LaunchOptions { var p = new Process() { StartInfo = new() {
Headless = true, FileName = WinziPrint,
ExecutablePath = Chromium, Arguments = $"-p -e utf-8 -d \"{App.TempPath}\" -",
// paged.js uses XHRs to load styles, so this is needed CreateNoWindow = true,
Args = new[] { "--allow-file-access-from-files" }, UseShellExecute = false,
}); RedirectStandardInput = true,
RedirectStandardOutput = true
} };
p.Start();
WinziPrintProc = p;
evtHandler(); evtHandler();
} }
public static async Task Close() { public static async Task<IEnumerable<int>> Convert(string htmlPath, string pdfPath, IProgress<double>? progress = null) {
if (Browser == null) return; return await Convert(new string[] { htmlPath }, pdfPath, progress);
await Browser.CloseAsync();
Browser = null;
} }
public static async Task Convert(string htmlPath, string pdfPath) { public static async Task<IEnumerable<int>> Convert(IEnumerable<string> htmlPath, string pdfPath, IProgress<double>? progress = null) {
if (Browser == null) throw new InvalidOperationException("The puppeteer engine has not been initialized yet"); if (WinziPrintProc == null) throw new InvalidOperationException("The WinziPrint process has not been initialized yet");
using var page = await Browser.NewPageAsync(); progress?.Report(0.0);
page.Console += OnConsole; await WinziPrintProc.StandardInput.WriteLineAsync($"{string.Join(';', htmlPath)};{pdfPath}");
await page.GoToAsync($"file://{htmlPath}"); while (true) {
await page.EvaluateFunctionAsync("async () => { await window.PagedPolyfill.preview(); }"); var line = await WinziPrintProc.StandardOutput.ReadLineAsync() ?? throw new IOException("Invalid response from WinziPrint");
await page.PdfAsync(pdfPath, new() { if (line.StartsWith("error:")) {
PreferCSSPageSize = true, MessageBox.Show(line[6..].Trim(), "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
//Format = PaperFormat.A4, return Array.Empty<int>();
DisplayHeaderFooter = false, } else if (line.StartsWith("progress:")) {
MarginOptions = new() { var parts = line[9..].Trim().Split('/').Select(int.Parse).ToArray();
Top = "0mm", progress?.Report(100.0 * parts[0] / parts[1]);
Right = "0mm", } else if (line.StartsWith("success:")) {
Bottom = "0mm", var m = Regex.Match(line, @"\(([0-9, ]+)\)");
Left = "0mm", return m.Groups[1].Value.Split(", ").Select(int.Parse);
}, }
}); }
}
private static void OnConsole(object? sender, ConsoleEventArgs e) {
MessageBox.Show(e.Message.Text);
}
public static void UpdateMetadata(string path, string title, string author) {
using var doc = PdfReader.Open(path);
doc.Info.Title = title;
doc.Info.Author = author;
doc.Save(path);
} }
public static void Show(TempFile file, string title) { public static void Show(TempFile file, string title) {
@ -78,6 +72,7 @@ namespace Elwig.Documents {
public static async Task Print(string path, int copies = 1) { public static async Task Print(string path, int copies = 1) {
var p = new Process() { StartInfo = new() { FileName = PdfToPrinter } }; var p = new Process() { StartInfo = new() { FileName = PdfToPrinter } };
p.StartInfo.ArgumentList.Add(path); p.StartInfo.ArgumentList.Add(path);
p.StartInfo.ArgumentList.Add("/s");
p.StartInfo.ArgumentList.Add($"copies={copies}"); p.StartInfo.ArgumentList.Add($"copies={copies}");
p.Start(); p.Start();
await p.WaitForExitAsync(); await p.WaitForExitAsync();

View File

@ -0,0 +1,142 @@
.address-wrapper, aside, main {
overflow: hidden;
}
.spacing {
height: 20mm;
}
.info-wrapper {
width: 100%;
height: 45mm;
margin: 0 0 2mm 0;
position: relative;
}
.info-wrapper .date {
text-align: right;
position: absolute;
right: 0;
bottom: -1.5em;
}
.address-wrapper {
height: 45mm;
width: 85mm;
margin: 0;
padding: 5mm;
position: absolute;
left: -5mm;
top: 0;
}
.address-wrapper .sender {
height: 4em;
font-size: 8pt;
padding: 1em 0;
}
address {
height: 5em;
white-space: pre-line;
font-size: 12pt;
font-style: normal;
}
aside {
height: 40mm;
width: 75mm;
margin: 0;
position: absolute;
left: 100mm;
top: 5mm;
}
aside table {
border-collapse: collapse;
border: 0.5pt solid #808080;
width: 65mm;
margin-right: 10mm;
}
aside table thead:not(:first-child) tr {
border-top: 0.5pt solid #808080;
}
aside table thead th {
background-color: #E0E0E0;
font-size: 10pt;
}
aside table tbody th,
aside table tbody td {
text-align: left;
font-size: 10pt;
}
aside table tbody th {
padding: 0.25mm 0.5mm 0.25mm 1mm;
}
aside table tbody td {
padding: 0.25mm 0;
}
aside table tbody th {
font-weight: normal;
}
main {
margin: 2em 0 1em 0;
}
main > *:first-child {
margin-top: 0;
}
main h1, .main-wrapper p {
font-size: 12pt;
margin: 1em 0;
text-align: justify;
}
.main-wrapper p {
widows: 3;
orphans: 3;
hyphens: manual;
}
main h1 {
margin-top: 0;
margin-bottom: 2em;
}
.main-wrapper p.comment {
font-size: 10pt;
}
.main-wrapper .hidden {
break-before: avoid;
}
.main-wrapper .bottom {
bottom: 0;
position: absolute;
width: 165mm;
}
.main-wrapper .signatures {
width: 100%;
display: flex;
justify-content: space-around;
margin: 20mm 0 2mm 0;
}
.main-wrapper .signatures > * {
width: 50mm;
border-top: 0.5pt solid black;
padding-top: 1mm;
text-align: center;
font-size: 10pt;
}

View File

@ -0,0 +1,64 @@
table.credit {
font-size: 10pt;
}
table.credit th,
table.credit td {
padding: 0 0.25mm;
}
table.credit thead {
font-size: 8pt
}
table.credit thead th {
font-weight: normal;
font-style: italic;
vertical-align: bottom;
}
table.credit td {
vertical-align: top;
}
table.credit .oe,
table.credit .kmw {
text-align: center;
}
table.credit .dpnr {
text-align: center;
}
table.credit .amount,
table.credit .weight {
text-align: right;
}
table.credit .rel,
table.credit .abs {
text-align: center;
}
table.credit .amount.sum {
vertical-align: bottom;
padding-bottom: 1mm;
}
table.credit tbody tr:not(.first):not(.last) {
break-before: avoid;
break-after: avoid;
}
table.credit tbody tr.first td {
padding-top: 1mm;
}
table.credit tbody tr.last td {
padding-bottom: 1mm;
}
table.credit tbody tr.new {
border-top: 0.5pt solid black;
}

View File

@ -0,0 +1,113 @@
table.delivery-confirmation {
font-size: 10pt;
margin-bottom: 5mm;
}
table.delivery-confirmation thead {
font-size: 8pt;
}
table.delivery-confirmation thead th {
font-weight: normal;
font-style: italic;
}
table.delivery-confirmation td {
overflow: hidden;
white-space: nowrap;
}
table.delivery-confirmation td[rowspan] {
vertical-align: top;
}
table.delivery-confirmation .weight {
text-align: right;
}
table.delivery-confirmation .grad {
text-align: center;
}
table.delivery-confirmation .geb {
font-size: 8pt;
}
table.delivery-confirmation .mod {
font-size: 8pt;
padding-left: 5mm;
}
table.delivery-confirmation .small {
font-size: 8pt;
}
table.delivery-confirmation tr.new td {
border-top: 0.5pt solid black;
}
table.delivery-confirmation tr:not(.first) {
break-before: avoid;
}
table.delivery-confirmation tr:not(.first) td {
padding-top: 0;
}
table.delivery-confirmation tr.trailing td {
padding-bottom: 0;
}
table.delivery-confirmation tr.sum {
border-top: 0.5pt solid black;
break-before: avoid;
font-weight: bold;
font-size: 12pt;
}
table.delivery-confirmation tr.sum td {
padding-top: 1mm;
}
table.delivery-confirmation-stats {
font-size: 10pt;
break-inside: avoid;
}
table.delivery-confirmation-stats th,
table.delivery-confirmation-stats td {
padding: 0.125mm 0;
overflow: hidden;
white-space: nowrap;
}
table.delivery-confirmation-stats tr.subheading th {
text-align: left;
}
table.delivery-confirmation-stats thead th {
font-weight: normal;
font-style: italic;
text-align: right;
font-size: 8pt;
}
table.delivery-confirmation-stats thead th:first-child {
text-align: left;
}
table.delivery-confirmation-stats td {
text-align: right;
}
table.delivery-confirmation-stats tbody th {
font-weight: normal;
font-style: italic;
text-align: left;
}
table.delivery-confirmation-stats tr.subheading th {
font-weight: bold;
border-top: 0.5pt solid black;
}

View File

@ -0,0 +1,47 @@
h1 {
text-align: center;
font-size: 24pt;
margin-bottom: 2mm;
}
h2 {
text-align: center;
font-size: 14pt;
margin-top: 2mm;
}
table.journal {
font-size: 10pt;
}
table.journal thead {
font-size: 8pt;
}
table.journal th {
font-weight: normal;
font-style: italic;
}
table.journal td {
overflow: hidden;
white-space: nowrap;
}
table.journal .mgnr,
table.journal .weight {
text-align: right;
}
table.journal .grad {
text-align: center;
}
table.journal tr.sum {
font-weight: bold;
}
table.journal tr.sum td {
border-top: 0.5pt solid black;
}

View File

@ -0,0 +1,97 @@
main h1 {
margin-bottom: 1.5em !important;
}
table.delivery {
margin-bottom: 5mm;
}
table.delivery tr:not(.main) {
break-before: avoid;
}
table.delivery th {
font-weight: normal;
font-style: italic;
font-size: 10pt;
}
table.delivery th.main {
text-align: left;
}
table.delivery tr.main td {
font-weight: bold;
padding-top: 2mm;
}
table.delivery tbody tr:not(.main) td {
font-size: 8pt;
}
table.delivery tr.tight:not(.first) td {
padding-top: 0;
padding-bottom: 0;
}
table.delivery tr.tight.first td {
padding-bottom: 0;
}
table.delivery tr.tight:has(+ tr:not(.tight)) td {
padding-bottom: 0.5mm !important;
}
table.delivery tr.sum {
border-top: 0.5pt solid black;
break-before: avoid;
}
table.delivery tr.sum td {
padding-top: 1mm;
}
table.delivery-note-stats {
font-size: 8pt;
break-inside: avoid;
break-after: avoid;
}
table.delivery-note-stats th,
table.delivery-note-stats td {
padding: 0.125mm 0;
}
table.delivery-note-stats:not(.expanded) tr.optional {
display: none;
}
table.delivery-note-stats tr.subheading th {
text-align: left;
}
table.delivery-note-stats.expanded tr.subheading:not(:has(~ tr)),
table.delivery-note-stats tr.subheading:not(:has(~ tr:not(.optional))) {
display: none;
}
table.delivery-note-stats thead th {
font-weight: normal;
font-style: italic;
text-align: right;
}
table.delivery-note-stats thead th:first-child {
text-align: left;
}
table.delivery-note-stats td {
text-align: right;
}
table.delivery-note-stats tbody th {
font-weight: normal;
font-style: italic;
text-align: left;
}

View File

@ -0,0 +1,74 @@
.m1, .m2, .m3 {
height: 0;
width: 10mm;
position: fixed;
left: -25mm;
border-top: 0.5pt solid black;
}
.m1.r, .m2.r, .m3.r {
left: initial;
right: -20mm;
}
.m1 {top: 80mm;}
.m2 {top: 123.5mm;}
.m3 {top: 185mm;}
.page-break {
break-before: page;
}
hr.page-break {
display: none;
}
.document-break {
break-before: page;
}
@page {
size: A4;
margin: 25mm 20mm 35mm 25mm;
@bottom-center {
content: element(page-footer);
}
}
@media screen {
body, header, .footer-wrapper {
width: 210mm;
}
header, .address-wrapper, aside, main {
border: 1px solid lightgray;
}
.m1, .m2, .m3 {
display: none;
}
header {
top: 0;
}
.spacing {
height: 45mm;
}
.main-wrapper {
margin: 0 20mm 40mm 25mm;
}
.footer-wrapper {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: white;
}
}
@media print {
.page::after {
content: "Seite " counter(page) " von " counter(pages) !important;
}
}

View File

@ -12,75 +12,10 @@ body {
margin: 0; margin: 0;
} }
.m1, .m2, .m3 { table {
height: 0;
width: 1cm;
position: fixed;
left: 0;
border-top: 1pt solid black;
}
.m1 {top: 105mm;}
.m2 {top: 148.5mm;}
.m3 {top: 210mm;}
header, .address-wrapper, aside, main {
overflow: hidden;
}
header {
height: 45mm;
padding: 5mm;
position: absolute;
top: -25mm;
left: 0;
right: 0;
text-align: center;
}
header h1{
font-size: 18pt;
margin-top: 1cm;
}
.spacing {
height: 20mm;
}
.info-wrapper {
width: 100%; width: 100%;
height: 45mm; border-collapse: collapse;
margin: 0 0 8.46mm 0; table-layout: fixed;
position: relative;
}
.address-wrapper {
height: 45mm;
width: 85mm;
margin: 0;
padding: 5mm;
position: absolute;
left: 20mm;
top: 0;
display: flex;
flex-direction: column;
justify-content: flex-end;
}
.address-wrapper .sender {
flex: 17.7mm 1 1;
font-size: 8pt;
display: flex;
flex-direction: column;
justify-content: flex-end;
padding-bottom: 2mm;
}
address {
flex: 27.3mm 1 1;
white-space: pre-line;
font-size: 12pt;
font-style: normal;
} }
table td, table td,
@ -88,90 +23,50 @@ table th {
padding: 0.5mm 1mm; padding: 0.5mm 1mm;
} }
aside { table th {
height: 40mm; text-align: center;
width: 75mm; }
margin: 0;
header {
height: 45mm;
padding: 10mm 0 0 0;
position: absolute; position: absolute;
left: 125mm; top: -25mm;
top: 5mm; left: 0;
right: 0;
text-align: center;
overflow: hidden;
} }
aside table { header .name {
border-collapse: collapse; font-size: 18pt;
border: 1pt solid #808080; margin-top: 8mm;
width: calc(100% - 1cm); font-weight: bold;
margin-right: 1cm;
} }
aside table thead:not(:first-child) tr { header .suffix {
border-top: 1pt solid #808080; font-size: 14pt;
font-weight: bold;
} }
aside table thead th { header .type {
background-color: #E0E0E0; font-size: 12pt;
font-size: 10pt;
}
aside table tbody th,
aside table tbody td {
text-align: left;
font-size: 10pt;
}
aside table tbody th {
font-weight: normal; font-weight: normal;
} }
main {
margin: 8.46mm 20mm 4.23mm 25mm;
}
main :first-child {
margin-top: 0;
}
main h1, main p {
font-size: 12pt;
margin: 1em 0;
text-align: justify;
}
main p {
widows: 3;
orphans: 3;
hyphens: auto;
}
main .date {
margin-bottom: 2em;
text-align: right;
}
main h1 {
margin-bottom: 2em;
}
main p.comment {
font-size: 10pt;
}
.footer-wrapper { .footer-wrapper {
padding: 0 20mm 0 25mm;
position: running(page-footer); position: running(page-footer);
bottom: 0; width: 165mm;
left: 0;
right: 0;
} }
.pre-footer { .pre-footer {
margin: 4.23mm 0; margin: 1em 0;
font-size: 10pt; font-size: 10pt;
display: flex;
} }
.pre-footer > * { .pre-footer > * {
flex: 5cm 1 1; display: inline-block;
width: 33%;
} }
.pre-footer .date { .pre-footer .date {
@ -185,183 +80,27 @@ main p.comment {
.pre-footer .page { .pre-footer .page {
text-align: right; text-align: right;
float: right;
} }
.pre-fotter .page::after { .pre-footer .page::after {
content: "Seite 1 von 1"; content: "Seite 1 von 1";
} }
footer { footer {
font-size: 10pt; font-size: 10pt;
border-top: 1pt solid black; border-top: 0.5pt solid black;
height: 25mm; height: 25mm;
padding-top: 1mm; padding-top: 1mm;
text-align: center; text-align: center;
} }
table {
width: 100%;
border-collapse: collapse;
table-layout: fixed;
}
table.delivery {
margin-bottom: 5mm;
}
table.delivery th {
font-weight: normal;
font-style: italic;
font-size: 10pt;
}
table.delivery th.main {
text-align: left;
}
table.delivery tr.main td {
font-weight: bold;
padding-top: 2mm;
}
table.delivery tbody tr:not(.main) td {
font-size: 8pt;
}
table.delivery tr.tight:not(.first) td {
padding-top: 0;
padding-bottom: 0;
}
table.delivery tr.tight.first td {
padding-bottom: 0;
}
table.delivery tr.tight:has(+ tr:not(.tight)) td {
padding-bottom: 0.5mm !important;
}
table.delivery tr.sum {
border-top: 1pt solid black;
}
table.delivery tr.sum td {
padding-top: 1mm;
}
table.delivery-stats {
font-size: 8pt;
}
table.delivery-stats.expanded th,
table.delivery-stats.expanded td {
padding: 0.25mm 0;
}
table.delivery-stats:not(.expanded) th,
table.delivery-stats:not(.expanded) td {
padding: 0.125mm 0;
}
table.delivery-stats:not(.expanded) tr.optional {
display: none;
}
table.delivery-stats thead th {
font-weight: normal;
font-style: italic;
text-align: right;
}
table.delivery-stats thead th:first-child {
text-align: left;
}
table.delivery-stats.expanded tbody {
font-size: 10pt;
}
table.delivery-stats td {
text-align: right;
}
table.delivery-stats tbody th {
font-weight: normal;
font-style: italic;
text-align: left;
}
.hidden { .hidden {
visibility: hidden; visibility: hidden;
} }
main .bottom {
bottom: 0;
position: absolute;
width: calc(100% - 25mm - 20mm);
}
main .signatures {
width: 100%;
display: flex;
justify-content: space-around;
margin: 20mm 0 2mm 0;
}
main .signatures > * {
width: 5cm;
border-top: 1pt solid black;
padding-top: 1mm;
text-align: center;
font-size: 10pt;
}
hr { hr {
border: none; border: none;
border-top: 1pt solid black; border-top: 0.5pt solid black;
margin: 5mm 0; margin: 5mm 0;
} }
hr.page-break {
display: none;
break-after: page;
}
@page {
size: A4;
margin: 25mm 0 35mm 0;
@bottom-center {
content: element(page-footer);
}
}
@media screen {
body, header, .footer-wrapper {
width: 210mm;
}
header, .address-wrapper, aside, main {
border: 1pt solid lightgray;
}
.m1, .m2, .m3 {display: none;}
header {top: 0;}
.spacing {height: 45mm;}
.main-wrapper {
margin-bottom: 40mm;
}
.footer-wrapper {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: white;
}
}
@media print {
.page::after {
content: "Seite " counter(page) " von " counter(pages);
}
.footer-wrapper {
display: none;
}
}

View File

@ -7,7 +7,7 @@
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
<PreserveCompilationContext>true</PreserveCompilationContext> <PreserveCompilationContext>true</PreserveCompilationContext>
<ApplicationIcon>elwig.ico</ApplicationIcon> <ApplicationIcon>elwig.ico</ApplicationIcon>
<Version>0.1.0</Version> <Version>0.4.3</Version>
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages> <SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
</PropertyGroup> </PropertyGroup>
@ -18,12 +18,11 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.5.1" /> <PackageReference Include="Extended.Wpf.Toolkit" Version="4.5.1" />
<PackageReference Include="ini-parser" Version="2.5.2" /> <PackageReference Include="ini-parser" Version="2.5.2" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.21" /> <PackageReference Include="LinqKit" Version="1.2.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="7.0.10" /> <PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.22" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.10" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="7.0.11" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.11" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.1938.49" /> <PackageReference Include="Microsoft.Web.WebView2" Version="1.0.1938.49" />
<PackageReference Include="PdfSharp" Version="1.50.5147" />
<PackageReference Include="PuppeteerSharp" Version="11.0.2" />
<PackageReference Include="RazorLight" Version="2.3.1" /> <PackageReference Include="RazorLight" Version="2.3.1" />
<PackageReference Include="ScottPlot.WPF" Version="4.1.67" /> <PackageReference Include="ScottPlot.WPF" Version="4.1.67" />
<PackageReference Include="System.IO.Ports" Version="7.0.0" /> <PackageReference Include="System.IO.Ports" Version="7.0.0" />

View File

@ -43,6 +43,8 @@ namespace Elwig.Helpers {
public DbSet<DeliveryPartAttr> DeliveryPartAttributes { get; private set; } public DbSet<DeliveryPartAttr> DeliveryPartAttributes { get; private set; }
public DbSet<DeliveryPartModifier> DeliveryPartModifiers { get; private set; } public DbSet<DeliveryPartModifier> DeliveryPartModifiers { get; private set; }
public DbSet<PaymentVar> PaymentVariants { get; private set; } public DbSet<PaymentVar> PaymentVariants { get; private set; }
public DbSet<PaymentMember> MemberPayments { get; private set; }
public DbSet<Credit> Credits { get; private set; }
private readonly StreamWriter? LogFile = null; private readonly StreamWriter? LogFile = null;
public static DateTime LastWriteTime => File.GetLastWriteTime(App.Config.DatabaseFile); public static DateTime LastWriteTime => File.GetLastWriteTime(App.Config.DatabaseFile);
@ -51,6 +53,10 @@ namespace Elwig.Helpers {
public static string ConnectionString => $"Data Source=\"{App.Config.DatabaseFile}\"; Foreign Keys=True; Mode=ReadWrite; Cache=Default"; public static string ConnectionString => $"Data Source=\"{App.Config.DatabaseFile}\"; Foreign Keys=True; Mode=ReadWrite; Cache=Default";
private readonly Dictionary<int, Dictionary<int, Dictionary<string, (int, int)>>> _memberRightsAndObligations = new();
private readonly Dictionary<int, Dictionary<int, Dictionary<string, int>>> _memberDeliveryBins = new();
private readonly Dictionary<int, Dictionary<int, Dictionary<string, int>>> _memberPaymentBins = new();
public AppDbContext() { public AppDbContext() {
if (App.Config.DatabaseLog != null) { if (App.Config.DatabaseLog != null) {
try { try {
@ -204,26 +210,101 @@ namespace Elwig.Helpers {
} }
} }
public async Task<IEnumerable<(string, string, int, int, int)>> GetMemberBuckets(Member m, int year) { private async Task FetchMemberRightsAndObligations(int year, SqliteConnection? cnx = null) {
using var cnx = await ConnectAsync(); var ownCnx = cnx == null;
var (rights, obligations) = await Billing.Billing.GetMemberRightsObligations(m.MgNr, year, cnx); cnx ??= await ConnectAsync();
var buckets = await Billing.Billing.GetMemberBucketWeights(m.MgNr, year, cnx); var bins = new Dictionary<int, Dictionary<string, (int, int)>>();
using (var cmd = cnx.CreateCommand()) {
var list = new List<(string, string, int, int, int)>(); cmd.CommandText = $"SELECT mgnr, bin, min_kg, max_kg FROM v_area_commitment_bin WHERE year = {year}";
foreach (var id in rights.Keys.Union(obligations.Keys).Union(buckets.Keys)) { using var reader = await cmd.ExecuteReaderAsync();
var s = await WineVarieties.FindAsync(id[..2]); while (await reader.ReadAsync()) {
var attrIds = id[2..]; var mgnr = reader.GetInt32(0);
var a = await WineAttributes.Where(a => attrIds.Contains(a.AttrId)).ToListAsync(); var vtrgid = reader.GetString(1);
var name = (s?.Name ?? "") + (a.Count > 0 ? $" ({string.Join(" / ", a.Select(a => a.Name))})" : ""); if (!bins.ContainsKey(mgnr)) bins[mgnr] = new();
list.Add(( bins[mgnr][vtrgid] = (reader.GetInt32(3), reader.GetInt32(2));
id, name, }
rights.TryGetValue(id, out var v1) ? v1 : 0,
obligations.TryGetValue(id, out var v2) ? v2 : 0,
buckets.TryGetValue(id, out var v3) ? v3 : 0
));
} }
if (ownCnx) await cnx.DisposeAsync();
_memberRightsAndObligations[year] = bins;
}
return list; private async Task FetchMemberDeliveryBins(int year, SqliteConnection? cnx = null) {
var ownCnx = cnx == null;
cnx ??= await ConnectAsync();
var bins = new Dictionary<int, Dictionary<string, int>>();
using (var cmd = cnx.CreateCommand()) {
cmd.CommandText = $"SELECT mgnr, bin, weight FROM v_delivery_bin WHERE year = {year}";
using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync()) {
var mgnr = reader.GetInt32(0);
var bin = reader.GetString(1);
if (!bins.ContainsKey(mgnr)) bins[mgnr] = new();
bins[mgnr][bin] = reader.GetInt32(2);
}
}
if (ownCnx) await cnx.DisposeAsync();
_memberDeliveryBins[year] = bins;
}
private async Task FetchMemberPaymentBins(int year, SqliteConnection? cnx = null) {
var ownCnx = cnx == null;
cnx ??= await ConnectAsync();
var bins = new Dictionary<int, Dictionary<string, int>>();
using (var cmd = cnx.CreateCommand()) {
cmd.CommandText = $"SELECT mgnr, bin, weight FROM v_payment_bin WHERE year = {year}";
using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync()) {
var mgnr = reader.GetInt32(0);
var bin = reader.GetString(1);
if (!bins.ContainsKey(mgnr)) bins[mgnr] = new();
bins[mgnr][bin] = reader.GetInt32(2);
}
}
if (ownCnx) await cnx.DisposeAsync();
_memberPaymentBins[year] = bins;
}
public async Task<Dictionary<string, (int, int)>> GetMemberRightsAndObligations(int year, int mgnr, SqliteConnection? cnx = null) {
if (!_memberRightsAndObligations.ContainsKey(year))
await FetchMemberRightsAndObligations(year, cnx);
return _memberRightsAndObligations[year].GetValueOrDefault(mgnr, new());
}
public async Task<Dictionary<string, int>> GetMemberDeliveryBins(int year, int mgnr, SqliteConnection? cnx = null) {
if (!_memberDeliveryBins.ContainsKey(year))
await FetchMemberDeliveryBins(year, cnx);
return _memberDeliveryBins[year].GetValueOrDefault(mgnr, new());
}
public async Task<Dictionary<string, int>> GetMemberPaymentBins(int year, int mgnr, SqliteConnection? cnx = null) {
if (!_memberPaymentBins.ContainsKey(year))
await FetchMemberPaymentBins(year, cnx);
return _memberPaymentBins[year].GetValueOrDefault(mgnr, new());
}
public async Task<Dictionary<string, (string, int, int, int, int)>> GetMemberBins(int year, int mgnr, SqliteConnection? cnx = null) {
var ownCnx = cnx == null;
cnx ??= await ConnectAsync();
var rightsAndObligations = await GetMemberRightsAndObligations(year, mgnr, cnx);
var deliveryBins = await GetMemberDeliveryBins(year, mgnr, cnx);
var paymentBins = await GetMemberPaymentBins(year, mgnr, cnx);
if (ownCnx) await cnx.DisposeAsync();
var bins = new Dictionary<string, (string, int, int, int, int)>();
foreach (var id in rightsAndObligations.Keys.Union(deliveryBins.Keys).Union(paymentBins.Keys)) {
var variety = await WineVarieties.FindAsync(id[..2]);
var attrIds = id[2..];
var attrs = await WineAttributes.Where(a => attrIds.Contains(a.AttrId)).ToListAsync();
var name = (variety?.Name ?? "") + (attrIds == "_" ? " (kein Qual.Wein)" : attrs.Count > 0 ? $" ({string.Join(" / ", attrs.Select(a => a.Name))})" : "");
bins[id] = (
name,
rightsAndObligations.GetValueOrDefault(id).Item1,
rightsAndObligations.GetValueOrDefault(id).Item2,
deliveryBins.GetValueOrDefault(id),
paymentBins.GetValueOrDefault(id)
);
}
return bins;
} }
} }
} }

View File

@ -0,0 +1,217 @@
using Microsoft.Data.Sqlite;
using System;
namespace Elwig.Helpers {
public static class AppDbUpdater {
public static readonly int RequiredSchemaVersion = 4;
private static int _versionOffset = 0;
private static readonly Action<SqliteConnection>[] _updaters = new[] {
UpdateDbSchema_1_To_2, UpdateDbSchema_2_To_3, UpdateDbSchema_3_To_4
};
private static void ExecuteNonQuery(SqliteConnection cnx, string sql) {
using var cmd = cnx.CreateCommand();
cmd.CommandText = sql;
cmd.ExecuteNonQuery();
}
private static object? ExecuteScalar(SqliteConnection cnx, string sql) {
using var cmd = cnx.CreateCommand();
cmd.CommandText = sql;
return cmd.ExecuteScalar();
}
public static string CheckDb() {
using var cnx = AppDbContext.Connect();
var applId = (long?)ExecuteScalar(cnx, "PRAGMA application_id") ?? 0;
if (applId != 0x454C5747) throw new Exception("Invalid application_id of database");
var schemaVers = (long?)ExecuteScalar(cnx, "PRAGMA schema_version") ?? 0;
_versionOffset = (int)(schemaVers % 100);
if (_versionOffset != 0) {
// schema was modified manually/externally
// TODO issue warning
}
UpdateDbSchema(cnx, (int)(schemaVers / 100), RequiredSchemaVersion);
var userVers = (long?)ExecuteScalar(cnx, "PRAGMA user_version") ?? 0;
var major = userVers >> 24;
var minor = (userVers >> 16) & 0xFF;
var patch = userVers & 0xFFFF;
if (App.VersionMajor > major ||
(App.VersionMajor == major && App.VersionMinor > minor) ||
(App.VersionMajor == major && App.VersionMinor == minor && App.VersionPatch > patch))
{
long vers = (App.VersionMajor << 24) | (App.VersionMinor << 16) | App.VersionPatch;
ExecuteNonQuery(cnx, $"PRAGMA user_version = {vers}");
}
return $"{major}.{minor}.{patch}";
}
private static void UpdateDbSchema(SqliteConnection cnx, int fromVersion, int toVersion) {
if (fromVersion == toVersion) {
return;
} else if (fromVersion > toVersion) {
throw new Exception("schema_version of database is too new");
} else if (toVersion - 1 > _updaters.Length) {
throw new Exception("Unable to update database schema: Updater not implemented");
} else if (fromVersion <= 0) {
throw new Exception("schema_version of database is invalid");
}
ExecuteNonQuery(cnx, "PRAGMA locking_mode = EXCLUSIVE");
ExecuteNonQuery(cnx, "BEGIN EXCLUSIVE");
for (int i = fromVersion; i < toVersion; i++) {
_updaters[i - 1](cnx);
}
ExecuteNonQuery(cnx, "COMMIT");
ExecuteNonQuery(cnx, "VACUUM");
ExecuteNonQuery(cnx, $"PRAGMA schema_version = {toVersion * 100 + _versionOffset}");
}
private static void UpdateDbSchema_1_To_2(SqliteConnection cnx) {
ExecuteNonQuery(cnx, "DROP VIEW v_area_commitment");
ExecuteNonQuery(cnx, "ALTER TABLE delivery_part DROP COLUMN weighing_reason");
ExecuteNonQuery(cnx, "ALTER TABLE delivery_part ADD COLUMN weighing_reason TEXT CHECK(NOT (manual_weighing = FALSE AND weighing_reason IS NOT NULL))");
}
private static void UpdateDbSchema_2_To_3(SqliteConnection cnx) {
ExecuteNonQuery(cnx, """
CREATE TABLE delivery_part_bin (
year INTEGER NOT NULL,
did INTEGER NOT NULL,
dpnr INTEGER NOT NULL,
binnr INTEGER NOT NULL,
discr TEXT NOT NULL,
value INTEGER NOT NULL,
CONSTRAINT pk_delivery_part_bin PRIMARY KEY (year, did, dpnr, binnr),
CONSTRAINT fk_delivery_part_bin_delivery_part FOREIGN KEY (year, did, dpnr) REFERENCES delivery_part (year, did, dpnr)
ON UPDATE CASCADE
ON DELETE CASCADE
) STRICT;
""");
ExecuteNonQuery(cnx, """
INSERT INTO delivery_part_bin (year, did, dpnr, binnr, discr, value)
SELECT year, did, dpnr, 0, '_', bucket_2 + bucket_3
FROM payment_delivery_part
WHERE COALESCE(bucket_1, bucket_2, bucket_3, bucket_4, bucket_5, bucket_6, bucket_7, bucket_8, bucket_9) IS NOT NULL
ON CONFLICT DO NOTHING;
""");
ExecuteNonQuery(cnx, """
INSERT INTO delivery_part_bin (year, did, dpnr, binnr, discr, value)
SELECT d.year, d.did, d.dpnr, 1, COALESCE(attributes, ''), bucket_1
FROM payment_delivery_part p
JOIN v_delivery d ON (d.year, d.did, d.dpnr) = (p.year, p.did, p.dpnr)
WHERE COALESCE(bucket_1, bucket_2, bucket_3, bucket_4, bucket_5, bucket_6, bucket_7, bucket_8, bucket_9) IS NOT NULL
ON CONFLICT DO NOTHING;
""");
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_1");
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_2");
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_3");
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_4");
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_5");
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_6");
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_7");
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_8");
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_9");
ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_1_name");
ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_2_name");
ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_3_name");
ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_4_name");
ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_5_name");
ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_6_name");
ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_7_name");
ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_8_name");
ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_9_name");
ExecuteNonQuery(cnx, "ALTER TABLE delivery_part ADD COLUMN gebunden INTEGER CHECK (gebunden IN (TRUE, FALSE)) DEFAULT NULL");
ExecuteNonQuery(cnx, "DROP VIEW v_delivery");
ExecuteNonQuery(cnx, """
CREATE VIEW v_delivery AS
SELECT s.*, GROUP_CONCAT(o.modid) AS modifiers
FROM (SELECT p.year, p.did, p.dpnr,
d.date, d.time, d.zwstid, d.lnr, d.lsnr,
m.mgnr, m.family_name, m.given_name,
p.sortid, p.weight, p.kmw, ROUND(p.kmw * (4.54 + 0.022 * p.kmw), 0) AS oe, p.qualid, p.hkid, p.kgnr, p.rdnr,
p.gerebelt, p.gebunden,
p.qualid IN (SELECT l.qualid FROM wine_quality_level l WHERE NOT l.predicate AND (p.kmw >= l.min_kmw OR l.min_kmw IS NULL) ORDER BY l.min_kmw DESC LIMIT 1,100) AS abgewertet,
p.qualid NOT IN ('WEI', 'RSW', 'LDW') AS min_quw,
GROUP_CONCAT(a.attrid) AS attributes,
COALESCE(SUM(a.fill_lower_bins), 0) AS attribute_prio,
d.comment, p.comment AS part_comment
FROM delivery_part p
JOIN delivery d ON (d.year, d.did) = (p.year, p.did)
JOIN member m ON m.mgnr = d.mgnr
LEFT JOIN delivery_part_attribute pa ON (pa.year, pa.did, pa.dpnr) = (p.year, p.did, p.dpnr)
LEFT JOIN wine_attribute a ON a.attrid = pa.attrid
GROUP BY p.year, p.did, p.dpnr
ORDER BY p.year, p.did, p.dpnr, a.attrid) s
LEFT JOIN delivery_part_modifier o ON (o.year, o.did, o.dpnr) = (s.year, s.did, s.dpnr)
GROUP BY s.year, s.lsnr, s.dpnr
ORDER BY s.year, s.lsnr, s.dpnr, o.modid;
""");
ExecuteNonQuery(cnx, "DROP VIEW v_bucket");
ExecuteNonQuery(cnx, """
CREATE VIEW v_delivery_bin AS
SELECT year, mgnr,
sortid || IIF(min_quw, REPLACE(COALESCE(attributes, ''), ',', ''), '_') AS bin,
SUM(weight) AS weight
FROM v_delivery
GROUP BY year, mgnr, bin
ORDER BY year, mgnr, LENGTH(bin) DESC, bin;
""");
ExecuteNonQuery(cnx, """
CREATE VIEW v_payment_bin AS
SELECT d.year, d.mgnr,
sortid || discr AS bin,
SUM(value) AS weight
FROM v_delivery d
JOIN delivery_part_bin b ON (b.year, b.did, b.dpnr) = (d.year, d.did, d.dpnr)
GROUP BY d.year, d.mgnr, bin
HAVING SUM(value) > 0
ORDER BY d.year, d.mgnr, bin;
""");
ExecuteNonQuery(cnx, "ALTER TABLE wine_attribute ADD COLUMN fill_lower_bins INTEGER NOT NULL CHECK (fill_lower_bins IN (0, 1, 2)) DEFAULT 0");
}
private static void UpdateDbSchema_3_To_4(SqliteConnection cnx) {
ExecuteNonQuery(cnx, "DROP VIEW v_payment_bin");
ExecuteNonQuery(cnx, """
CREATE VIEW v_payment_bin AS
SELECT d.year, d.mgnr,
sortid || discr AS bin,
SUM(value) AS weight
FROM v_delivery d
JOIN delivery_part_bin b ON (b.year, b.did, b.dpnr) = (d.year, d.did, d.dpnr)
GROUP BY d.year, d.mgnr, bin
HAVING SUM(value) > 0
ORDER BY d.year, d.mgnr, LENGTH(bin) DESC, bin;
""");
ExecuteNonQuery(cnx, """
CREATE VIEW v_area_commitment_bin AS
SELECT s.year, c.mgnr,
c.vtrgid AS bin,
CAST(ROUND(SUM(COALESCE(area * min_kg_per_ha, 0)) / 10000.0, 0) AS INTEGER) AS min_kg,
CAST(ROUND(SUM(COALESCE(area * max_kg_per_ha, 0)) / 10000.0, 0) AS INTEGER) AS max_kg
FROM area_commitment c, season s
JOIN area_commitment_type t ON t.vtrgid = c.vtrgid
WHERE (year_from IS NULL OR year_from <= s.year) AND
(year_to IS NULL OR year_to >= s.year)
GROUP BY s.year, c.mgnr, c.vtrgid
ORDER BY s.year, c.mgnr, LENGTH(c.vtrgid) DESC, c.vtrgid;
""");
}
}
}

View File

@ -1,4 +1,5 @@
using Microsoft.Data.Sqlite; using Microsoft.Data.Sqlite;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -6,125 +7,210 @@ using System.Threading.Tasks;
namespace Elwig.Helpers.Billing { namespace Elwig.Helpers.Billing {
public class Billing { public class Billing {
private readonly int Year; protected readonly int Year;
private readonly int AvNr; protected readonly AppDbContext Context;
private readonly AppDbContext Context; protected readonly Dictionary<string, string> Attributes;
private readonly Dictionary<string, string> Attributes; protected readonly Dictionary<string, (decimal?, decimal?)> Modifiers;
private readonly Dictionary<string, (decimal?, decimal?)> Modifiers; protected readonly Dictionary<string, (string, string?, string?, string?, int?, int?, decimal?)> AreaComTypes;
private readonly Dictionary<string, (string, string?, string?, string?, int?, int?, decimal?)> AreaComTypes;
public Billing(int year, int avnr) { public Billing(int year) {
Year = year; Year = year;
AvNr = avnr;
Context = new AppDbContext(); Context = new AppDbContext();
Attributes = Context.WineAttributes.ToDictionary(a => a.AttrId, a => a.Name); Attributes = Context.WineAttributes.ToDictionary(a => a.AttrId, a => a.Name);
Modifiers = Context.Modifiers.Where(m => m.Year == Year).ToDictionary(m => m.ModId, m => (m.Abs, m.Rel)); Modifiers = Context.Modifiers.Where(m => m.Year == Year).ToDictionary(m => m.ModId, m => (m.Abs, m.Rel));
AreaComTypes = Context.AreaCommitmentTypes.ToDictionary(v => v.VtrgId, v => (v.SortId, v.AttrId1, v.AttrId2, v.Discriminator, v.MinKgPerHa, v.MaxKgPerHa, v.PenaltyAmount)); AreaComTypes = Context.AreaCommitmentTypes.ToDictionary(v => v.VtrgId, v => (v.SortId, v.AttrId1, v.AttrId2, v.Discriminator, v.MinKgPerHa, v.MaxKgPerHa, v.PenaltyAmount));
} }
protected async Task DeleteInDb() { public async Task FinishSeason() {
using var cnx = await AppDbContext.ConnectAsync(); using var cnx = await AppDbContext.ConnectAsync();
using (var cmd = cnx.CreateCommand()) {
cmd.CommandText = $"DELETE FROM payment_delivery_part WHERE (year, avnr) = ({Year}, {AvNr})";
await cmd.ExecuteNonQueryAsync();
}
using (var cmd = cnx.CreateCommand()) {
cmd.CommandText = $"DELETE FROM payment_member WHERE (year, avnr) = ({Year}, {AvNr})";
await cmd.ExecuteNonQueryAsync();
}
}
public async Task Calculate() {
await DeleteInDb();
var tasks = new List<Task>();
foreach (var mgnr in Context.Members.Select(m => m.MgNr)) {
tasks.Add(Task.Run(() => CalculateMember(mgnr)));
}
await Task.WhenAll(tasks);
}
public static async Task<(Dictionary<string, int>, Dictionary<string, int>)> GetMemberRightsObligations(int mgnr, int year, SqliteConnection cnx) {
var rights = new Dictionary<string, int>();
var obligations = new Dictionary<string, int>();
using var cmd = cnx.CreateCommand();
cmd.CommandText = $"""
SELECT t.vtrgid,
SUM(COALESCE(area * min_kg_per_ha, 0)) / 10000 AS min_kg,
SUM(COALESCE(area * max_kg_per_ha, 0)) / 10000 AS max_kg
FROM area_commitment c
JOIN area_commitment_type t ON t.vtrgid = c.vtrgid
WHERE mgnr = {mgnr} AND (year_from IS NULL OR year_from <= {year}) AND (year_to IS NULL OR year_to >= {year})
GROUP BY t.vtrgid
ORDER BY LENGTH(t.vtrgid) DESC, t.vtrgid
""";
var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync()) {
var vtrgid = reader.GetString(0);
obligations[vtrgid] = reader.GetInt32(1);
rights[vtrgid] = reader.GetInt32(2);
}
return (rights, obligations);
}
public static async Task<Dictionary<string, int>> GetMemberBucketWeights(int mgnr, int year, SqliteConnection cnx) {
var buckets = new Dictionary<string, int>();
using var cmd = cnx.CreateCommand();
cmd.CommandText = $"""
SELECT bucket, weight
FROM v_bucket
WHERE (year, mgnr) = ({year}, {mgnr})
""";
var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync()) {
var bucket = reader.GetString(0);
buckets[bucket] = reader.GetInt32(1);
}
return buckets;
}
protected async Task CalculateMember(int mgnr) {
using var cnx = await AppDbContext.ConnectAsync();
var (rights, obligations) = await GetMemberRightsObligations(mgnr, Year, cnx);
var deliveries = new List<(int, int, string, int, double, string, string[], string[])>();
using (var cmd = cnx.CreateCommand()) { using (var cmd = cnx.CreateCommand()) {
cmd.CommandText = $""" cmd.CommandText = $"""
SELECT did, dpnr, sortid, weight, kmw, qualid, attributes, modifiers UPDATE season
FROM v_delivery SET (start_date, end_date) = (SELECT MIN(date), MAX(date) FROM delivery WHERE year = {Year})
WHERE (year, mgnr) = ({Year}, {mgnr}) WHERE year = {Year}
ORDER BY kmw DESC, weight DESC, did, dpnr
"""; """;
var reader = await cmd.ExecuteReaderAsync(); await cmd.ExecuteNonQueryAsync();
}
using (var cmd = cnx.CreateCommand()) {
cmd.CommandText = $"DELETE FROM delivery_part_bin WHERE year = {Year}";
await cmd.ExecuteNonQueryAsync();
}
}
public async Task CalculateBins(bool allowAttrsIntoLowerBins, bool avoidUnderDeliveries, bool honorGebunden) {
var attrVals = Context.WineAttributes.ToDictionary(a => a.AttrId, a => a.FillLowerBins);
var attrForced = attrVals.Where(a => a.Value == 0).Select(a => a.Key).ToArray();
using var cnx = await AppDbContext.ConnectAsync();
await Context.GetMemberRightsAndObligations(Year, 0, cnx);
var inserts = new List<(int, int, int, string, int)>();
var deliveries = new List<(int, int, int, string, int, double, string, string[], string[], bool?)>();
using (var cmd = cnx.CreateCommand()) {
cmd.CommandText = $"""
SELECT mgnr, did, dpnr, sortid, weight, kmw, qualid, attributes, modifiers, gebunden
FROM v_delivery
WHERE year = {Year}
ORDER BY mgnr, sortid, abgewertet ASC, {(honorGebunden ? "gebunden IS NOT NULL DESC, gebunden DESC," : "")}
COALESCE(LENGTH(attributes), 0) ASC, attribute_prio DESC, COALESCE(attributes, '~'),
kmw DESC, lsnr, dpnr
""";
using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync()) { while (await reader.ReadAsync()) {
deliveries.Add(( deliveries.Add((
reader.GetInt32(0), reader.GetInt32(1), reader.GetString(2), reader.GetInt32(3), reader.GetInt32(0), reader.GetInt32(1), reader.GetInt32(2), reader.GetString(3), reader.GetInt32(4),
reader.GetDouble(4), reader.GetString(5), reader.GetString(6).Split(","), reader.GetString(7).Split(",") reader.GetDouble(5), reader.GetString(6),
reader.IsDBNull(7) ? Array.Empty<string>() : reader.GetString(7).Split(",").Order().ToArray(),
reader.IsDBNull(8) ? Array.Empty<string>() : reader.GetString(8).Split(",").Order().ToArray(),
reader.IsDBNull(9) ? null : reader.GetBoolean(9)
)); ));
} }
} }
foreach (var (did, dpnr, sortid, weight, kmw, qualid, attributes, modifiers) in deliveries) { int lastMgNr = 0;
if (qualid == "WEI" || qualid == "RSW" || qualid == "LDW") { Dictionary<string, (int, int)>? rightsAndObligations = null;
Dictionary<string, int> used = new();
foreach (var (mgnr, did, dpnr, sortid, weight, kmw, qualid, attributes, modifiers, gebunden) in deliveries) {
if (lastMgNr != mgnr) {
rightsAndObligations = await Context.GetMemberRightsAndObligations(Year, mgnr);
used = new();
}
if ((honorGebunden && gebunden == false) ||
rightsAndObligations == null || rightsAndObligations.Count == 0 ||
qualid == "WEI" || qualid == "RSW" || qualid == "LDW")
{
// Explizit als ungebunden markiert,
// Mitglied hat keine Flächenbindungen, oder
// Nicht mindestens Qualitätswein (QUW) // Nicht mindestens Qualitätswein (QUW)
using var cmd = cnx.CreateCommand(); // -> ungebunden
// TODO inserts.Add((did, dpnr, 0, "_", weight));
cmd.CommandText = $"""
INSERT INTO payment_delivery_part (year, did, dpnr, avnr, mod_abs, mod_rel, )
VALUES ({Year}, {did}, {dpnr}, {AvNr}, )
""";
await cmd.ExecuteNonQueryAsync();
continue; continue;
} }
// TODO if (attributes.Length > 2)
throw new NotSupportedException();
int w = weight;
foreach (var p in Utils.Permutate(attributes, attributes.Intersect(attrForced))) {
var c = p.Count();
var key = sortid + string.Join("", p);
if (rightsAndObligations.ContainsKey(key)) {
int i = 4;
if (c == 1) {
i = (p.ElementAt(0) == attributes[0]) ? 2 : 3;
} else if (c == 0) {
i = 1;
}
var u = used.GetValueOrDefault(key, 0);
var vr = Math.Max(0, Math.Min(rightsAndObligations[key].Item1 - u, w));
var vo = Math.Max(0, Math.Min(rightsAndObligations[key].Item2 - u, w));
var v = (attributes.Length == 0 || attributes.Select(a => attrVals[a]).Min() == 2) ? vr : vo;
used[key] = u + v;
inserts.Add((did, dpnr, i, key[2..], v));
w -= v;
}
if (w == 0 || !allowAttrsIntoLowerBins) break;
}
inserts.Add((did, dpnr, 0, "_", w));
lastMgNr = mgnr;
} }
using (var cmd = cnx.CreateCommand()) {
cmd.CommandText = $"""
INSERT INTO delivery_part_bin (year, did, dpnr, binnr, discr, value)
VALUES {string.Join(",\n ", inserts.Select(i => $"({Year}, {i.Item1}, {i.Item2}, {i.Item3}, '{i.Item4}', {i.Item5})"))}
ON CONFLICT DO UPDATE
SET discr = excluded.discr, value = value + excluded.value
""";
await cmd.ExecuteNonQueryAsync();
}
if (!avoidUnderDeliveries)
return;
var underDeliveries = new Dictionary<(int, string), int>();
using (var cmd = cnx.CreateCommand()) {
cmd.CommandText = $"""
SELECT c.mgnr, c.bin, COALESCE(p.weight, 0) - c.min_kg AS diff
FROM v_area_commitment_bin c
LEFT JOIN v_payment_bin p ON (p.year, p.mgnr, p.bin) = (c.year, c.mgnr, c.bin)
WHERE c.year = {Year} AND LENGTH(c.bin) = 2 AND diff < 0
""";
using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync()) {
underDeliveries[(reader.GetInt32(0), reader.GetString(1))] = reader.GetInt32(2);
}
}
var fittingBins = new Dictionary<(int, string), int>();
using (var cmd = cnx.CreateCommand()) {
cmd.CommandText = $"""
SELECT c.mgnr, c.bin, COALESCE(p.weight, 0) - c.min_kg AS diff
FROM v_area_commitment_bin c
LEFT JOIN v_payment_bin p ON (p.year, p.mgnr, p.bin) = (c.year, c.mgnr, c.bin)
WHERE c.year = {Year} AND LENGTH(c.bin) = 3 AND diff > 0
""";
using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync()) {
fittingBins[(reader.GetInt32(0), reader.GetString(1))] = reader.GetInt32(2);
}
}
foreach (var item in fittingBins) {
var mgnr = item.Key.Item1;
var id = item.Key.Item2[..2];
var attr = item.Key.Item2[2..];
var available = item.Value;
var needed = -underDeliveries.GetValueOrDefault((mgnr, id), 0);
if (needed <= 0) continue;
var fittingDeliveries = new List<(int, int, int, int)>();
using (var cmd = cnx.CreateCommand()) {
cmd.CommandText = $"""
SELECT d.did, d.dpnr, d.weight, b.value
FROM v_delivery d
JOIN delivery_part_bin b ON (b.year, b.did, b.dpnr) = (d.year, d.did, d.dpnr) AND b.discr = '{attr}'
WHERE d.year = {Year} AND mgnr = {mgnr} AND sortid = '{id}' AND attributes = '{attr}'
ORDER BY kmw ASC, lsnr DESC, d.dpnr DESC
""";
using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync()) {
fittingDeliveries.Add((
reader.GetInt32(0), reader.GetInt32(1), reader.GetInt32(2), reader.GetInt32(3)
));
}
}
var negChanges = new List<(int, int, int, int)>();
var posChanges = new List<(int, int, int, int)>();
foreach (var (did, dpnr, _, w) in fittingDeliveries) {
int v = Math.Min(needed, w);
needed -= v;
posChanges.Add((did, dpnr, 1, v));
negChanges.Add((did, dpnr, 2, w - v));
if (needed == 0) break;
}
using (var cmd = cnx.CreateCommand()) {
cmd.CommandText = $"""
INSERT INTO delivery_part_bin (year, did, dpnr, binnr, discr, value)
VALUES {string.Join(",\n ", posChanges.Select(i => $"({Year}, {i.Item1}, {i.Item2}, {i.Item3}, '', {i.Item4})"))}
ON CONFLICT DO UPDATE
SET value = value + excluded.value
""";
await cmd.ExecuteNonQueryAsync();
}
using (var cmd = cnx.CreateCommand()) {
cmd.CommandText = $"""
INSERT INTO delivery_part_bin (year, did, dpnr, binnr, discr, value)
VALUES {string.Join(",\n ", negChanges.Select(i => $"({Year}, {i.Item1}, {i.Item2}, {i.Item3}, '', {i.Item4})"))}
ON CONFLICT DO UPDATE
SET value = excluded.value
""";
await cmd.ExecuteNonQueryAsync();
}
}
} }
} }
} }

View File

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Elwig.Helpers.Billing {
public class BillingVariant : Billing {
private readonly int AvNr;
public BillingVariant(int year, int avnr) : base(year) {
AvNr = avnr;
}
protected async Task DeleteInDb() {
using var cnx = await AppDbContext.ConnectAsync();
using (var cmd = cnx.CreateCommand()) {
cmd.CommandText = $"DELETE FROM payment_delivery_part WHERE (year, avnr) = ({Year}, {AvNr})";
await cmd.ExecuteNonQueryAsync();
}
using (var cmd = cnx.CreateCommand()) {
cmd.CommandText = $"DELETE FROM payment_member WHERE (year, avnr) = ({Year}, {AvNr})";
await cmd.ExecuteNonQueryAsync();
}
}
public async Task CalculatePrices() {
await DeleteInDb();
var tasks = new List<Task>();
foreach (var mgnr in Context.Members.Select(m => m.MgNr)) {
tasks.Add(Task.Run(() => CalculateMemberPrices(mgnr)));
}
await Task.WhenAll(tasks);
}
protected async Task CalculateMemberPrices(int mgnr) {
}
}
}

View File

@ -1,14 +1,10 @@
using Newtonsoft.Json.Linq;
using ScottPlot; using ScottPlot;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq; using System.Linq;
using System.Text; using System.Text.Json.Nodes;
using System.Threading.Tasks;
using System.Windows.Markup;
namespace Elwig.Models { namespace Elwig.Helpers.Billing {
public class Graph : ICloneable { public class Graph : ICloneable {
public string Type { get; set; } public string Type { get; set; }
@ -30,7 +26,7 @@ namespace Elwig.Models {
DataY = DataGen.Zeros(MaxX - MinX + 1); DataY = DataGen.Zeros(MaxX - MinX + 1);
} }
public Graph(string type, int num, JToken graphData, string contracts, int minX, int maxX) { public Graph(string type, int num, JsonObject graphData, string contracts, int minX, int maxX) {
Type = type; Type = type;
Num = num; Num = num;
Contracts = contracts; Contracts = contracts;
@ -52,8 +48,8 @@ namespace Elwig.Models {
DataY = dataY; DataY = dataY;
} }
private void ParseGraphData(JToken graphData) { private void ParseGraphData(JsonObject graphData) {
var GraphPoints = graphData.Children().OfType<JProperty>().ToDictionary(p => int.Parse(p.Name[..^2]), p => (double)p.Value); var GraphPoints = graphData.ToDictionary(p => int.Parse(p.Key[..^2]), p => (double)p.Value?.AsValue());
if (GraphPoints.Keys.Count < 1) { if (GraphPoints.Keys.Count < 1) {
return; return;
@ -99,19 +95,19 @@ namespace Elwig.Models {
} }
} }
public JObject ToJson() { public JsonObject ToJson() {
JObject graph = new(); JsonObject graph = new();
if (DataY[0] != DataY[1]) { if (DataY[0] != DataY[1]) {
graph.Add(new JProperty(DataX[0] + Type.ToLower(), Math.Round(DataY[0], 4))); graph.Add(new KeyValuePair<string, JsonNode?>(DataX[0] + Type.ToLower(), Math.Round(DataY[0], 4)));
} }
for (int i = 1; i < DataX.Length - 1; i++) { for (int i = 1; i < DataX.Length - 1; i++) {
if (Math.Round(DataY[i] - DataY[i - 1], 4) != Math.Round(DataY[i + 1] - DataY[i], 4)) { if (Math.Round(DataY[i] - DataY[i - 1], 4) != Math.Round(DataY[i + 1] - DataY[i], 4)) {
graph.Add(new JProperty(DataX[i] + Type.ToLower(), Math.Round(DataY[i], 4))); graph.Add(new KeyValuePair<string, JsonNode?>(DataX[i] + Type.ToLower(), Math.Round(DataY[i], 4)));
} }
} }
if (DataY[^1] != DataY[^2]) { if (DataY[^1] != DataY[^2]) {
graph.Add(new JProperty(DataX[^1] + Type.ToLower(), Math.Round(DataY[^1], 4))); graph.Add(new KeyValuePair<string, JsonNode?>(DataX[^1] + Type.ToLower(), Math.Round(DataY[^1], 4)));
} }
return graph; return graph;
} }

View File

@ -0,0 +1,32 @@
using Elwig.Models;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Elwig.Helpers.Billing {
public class Transaction {
public readonly Member Member;
public readonly long AmountCent;
public readonly string Currency;
public readonly int Nr;
public Transaction(Member m, decimal amount, string currency, int nr) {
Member = m;
AmountCent = (long)Math.Round(amount * 100);
Currency = currency;
Nr = nr;
}
public static IEnumerable<Transaction> FromPaymentVariant(PaymentVar variant) {
var last = variant.Season.PaymentVariants.Where(v => v.TransferDate != null).OrderBy(v => v.TransferDate).LastOrDefault();
var dict = last?.MemberPayments.ToDictionary(m => m.MgNr, m => m.Amount) ?? new();
return variant.Credits
.OrderBy(c => c.MgNr)
.Select(c => new Transaction(c.Member, c.Amount, variant.Season.CurrencyCode, c.TgNr))
.ToList();
}
public static string FormatAmountCent(long cents) => $"{cents / 100}.{cents % 100:00}";
}
}

View File

@ -7,16 +7,23 @@ using System.Threading.Tasks;
namespace Elwig.Helpers { namespace Elwig.Helpers {
public class ClientParameters { public class ClientParameters {
public enum Type { Matzen, GWK }; public enum Type { Matzen, Winzerkeller };
public bool IsMatzen => Client == Type.Matzen; public bool IsMatzen => Client == Type.Matzen;
public bool IsGWK => Client == Type.GWK; public bool IsWinzerkeller => Client == Type.Winzerkeller;
public bool IsWolkersdorf => Client == Type.Winzerkeller && App.ZwstId == "W";
public bool IsHaugsdorf => Client == Type.Winzerkeller && App.ZwstId == "H";
public bool IsSitzendorf => Client == Type.Winzerkeller && App.ZwstId == "S";
public string NameToken; public string NameToken;
public string NameShort; public string NameShort;
public string Name; public string Name;
public string? NameSuffix; public string? NameSuffix;
public string NameType; public string NameType;
public string NameTypeFull => NameType.Replace(".", "").Replace(" ", "").ToLower() switch {
"reggenmbh" => "registrierte Genossenschaft mit beschränkter Haftung",
_ => NameType,
};
public string NameFull => NameSuffix == null ? $"{Name} {NameType}" : $"{Name}, {NameSuffix}, {NameType}"; public string NameFull => NameSuffix == null ? $"{Name} {NameType}" : $"{Name}, {NameSuffix}, {NameType}";
public Type? Client; public Type? Client;
@ -48,7 +55,10 @@ namespace Elwig.Helpers {
public decimal VatReduced; public decimal VatReduced;
public decimal VatFlatRate; public decimal VatFlatRate;
public int ModeDeliveryNoteStats;
public string? TextDeliveryNote; public string? TextDeliveryNote;
public string? TextDeliveryConfirmation;
public ClientParameters(AppDbContext ctx) : this(ctx.ClientParameters.ToDictionary(e => e.Param, e => e.Value)) { } public ClientParameters(AppDbContext ctx) : this(ctx.ClientParameters.ToDictionary(e => e.Param, e => e.Value)) { }
@ -61,7 +71,7 @@ namespace Elwig.Helpers {
NameType = parameters["CLIENT_NAME_TYPE"] ?? throw new KeyNotFoundException(); NameType = parameters["CLIENT_NAME_TYPE"] ?? throw new KeyNotFoundException();
switch (Name) { switch (Name) {
case "Winzergenossenschaft für Matzen und Umgebung": Client = Type.Matzen; break; case "Winzergenossenschaft für Matzen und Umgebung": Client = Type.Matzen; break;
case "Winzerkeller im Weinviertel": Client = Type.GWK; break; case "Winzerkeller im Weinviertel": Client = Type.Winzerkeller; break;
}; };
Plz = int.Parse(parameters["CLIENT_PLZ"] ?? ""); Plz = int.Parse(parameters["CLIENT_PLZ"] ?? "");
@ -82,14 +92,31 @@ namespace Elwig.Helpers {
VatReduced = decimal.Parse((parameters["VAT_REDUCED"] ?? "").Replace(".", ",")); VatReduced = decimal.Parse((parameters["VAT_REDUCED"] ?? "").Replace(".", ","));
VatFlatRate = decimal.Parse((parameters["VAT_FLATRATE"] ?? "").Replace(".", ",")); VatFlatRate = decimal.Parse((parameters["VAT_FLATRATE"] ?? "").Replace(".", ","));
switch (parameters.GetValueOrDefault("MODE_DELIVERYNOTE_STATS", "SHORT")?.ToUpper()) {
case "NONE": ModeDeliveryNoteStats = 0; break;
case "GA_ONLY": ModeDeliveryNoteStats = 1; break;
case "SHORT": ModeDeliveryNoteStats = 2; break;
case "FULL": ModeDeliveryNoteStats = 3; break;
}
Sender2 = parameters.GetValueOrDefault("DOCUMENT_SENDER") ?? ""; Sender2 = parameters.GetValueOrDefault("DOCUMENT_SENDER") ?? "";
TextDeliveryNote = parameters.GetValueOrDefault("TEXT_DELIVERYNOTE"); TextDeliveryNote = parameters.GetValueOrDefault("TEXT_DELIVERYNOTE");
if (TextDeliveryNote == "") TextDeliveryNote = null;
TextDeliveryConfirmation = parameters.GetValueOrDefault("TEXT_DELIVERYCONFIRMATION");
if (TextDeliveryConfirmation == "") TextDeliveryConfirmation = null;
} catch { } catch {
throw new KeyNotFoundException(); throw new KeyNotFoundException();
} }
} }
private IEnumerable<(string, string?)> GetParamValues() { private IEnumerable<(string, string?)> GetParamValues() {
string deliveryNoteStats = "SHORT";
switch (ModeDeliveryNoteStats) {
case 0: deliveryNoteStats = "NONE"; break;
case 1: deliveryNoteStats = "GA_ONLY"; break;
case 2: deliveryNoteStats = "SHORT"; break;
case 3: deliveryNoteStats = "FULL"; break;
}
return new (string, string?)[] { return new (string, string?)[] {
("CLIENT_NAME_TOKEN", NameToken), ("CLIENT_NAME_TOKEN", NameToken),
("CLIENT_NAME_SHORT", NameShort), ("CLIENT_NAME_SHORT", NameShort),
@ -112,8 +139,10 @@ namespace Elwig.Helpers {
("VAT_NORMAL", VatNormal.ToString().Replace(",", ".")), ("VAT_NORMAL", VatNormal.ToString().Replace(",", ".")),
("VAT_REDUCED", VatReduced.ToString().Replace(",", ".")), ("VAT_REDUCED", VatReduced.ToString().Replace(",", ".")),
("VAT_FLATRATE", VatFlatRate.ToString().Replace(",", ".")), ("VAT_FLATRATE", VatFlatRate.ToString().Replace(",", ".")),
("MODE_DELIVERYNOTE_STATS", deliveryNoteStats),
("DOCUMENT_SENDER", Sender2), ("DOCUMENT_SENDER", Sender2),
("TEXT_DELIVERYNOTE", TextDeliveryNote), ("TEXT_DELIVERYNOTE", TextDeliveryNote),
("TEXT_DELIVERYCONFIRMATION", TextDeliveryConfirmation),
}; };
} }

View File

@ -1,7 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using IniParser; using IniParser;
using IniParser.Model; using IniParser.Model;
@ -9,6 +8,7 @@ namespace Elwig.Helpers {
public class Config { public class Config {
private readonly string FileName; private readonly string FileName;
public bool Debug;
public string DatabaseFile = App.DataPath + "database.sqlite3"; public string DatabaseFile = App.DataPath + "database.sqlite3";
public string? DatabaseLog = null; public string? DatabaseLog = null;
public string? Branch = null; public string? Branch = null;
@ -25,7 +25,7 @@ namespace Elwig.Helpers {
var parser = new FileIniDataParser(); var parser = new FileIniDataParser();
IniData? ini = null; IniData? ini = null;
try { try {
ini = parser.ReadFile(FileName, Encoding.UTF8); ini = parser.ReadFile(FileName, Utils.UTF8);
} catch {} } catch {}
if (ini == null || !ini.TryGetKey("database.file", out string db)) { if (ini == null || !ini.TryGetKey("database.file", out string db)) {
@ -50,6 +50,13 @@ namespace Elwig.Helpers {
Branch = branch; Branch = branch;
} }
if (ini == null || !ini.TryGetKey("general.debug", out string debug)) {
Debug = false;
} else {
debug = debug.ToLower();
Debug = debug == "1" || debug == "true" || debug == "yes" || debug == "on";
}
ScaleList.Clear(); ScaleList.Clear();
Scales = ScaleList; Scales = ScaleList;
if (ini != null) { if (ini != null) {
@ -70,9 +77,10 @@ namespace Elwig.Helpers {
} }
public void Write() { public void Write() {
using var file = new StreamWriter(FileName, false, Encoding.UTF8); using var file = new StreamWriter(FileName, false, Utils.UTF8);
file.Write($"\r\n[general]\r\n"); file.Write($"\r\n[general]\r\n");
if (Branch != null) file.Write($"branch = {Branch}\r\n"); if (Branch != null) file.Write($"branch = {Branch}\r\n");
if (Debug) file.Write("debug = true\r\n");
file.Write($"\r\n[database]\r\nfile = {DatabaseFile}\r\n"); file.Write($"\r\n[database]\r\nfile = {DatabaseFile}\r\n");
if (DatabaseLog != null) file.Write($"log = {DatabaseLog}\r\n"); if (DatabaseLog != null) file.Write($"log = {DatabaseLog}\r\n");
foreach (var s in ScaleList) { foreach (var s in ScaleList) {

View File

@ -58,7 +58,8 @@ namespace Elwig.Helpers {
continue; continue;
} else if (child is T t) { } else if (child is T t) {
yield return t; yield return t;
} else if (child is DependencyObject childDepOpj) { }
if (child is DependencyObject childDepOpj) {
foreach (T childOfChild in FindAllChildren<T>(childDepOpj)) { foreach (T childOfChild in FindAllChildren<T>(childDepOpj)) {
yield return childOfChild; yield return childOfChild;
} }

View File

@ -1,4 +1,67 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System;
namespace Elwig.Helpers.Export { namespace Elwig.Helpers.Export {
public class Bki {
using Row = Tuple<(string?, string?, string?, string?, string, int, string, int), (string, int, string, string, string, int, string, double, double)>;
public class Bki : Csv<Row> {
private readonly string _clientData;
public Bki(string filename) : base(filename, ';', Encoding.Latin1) {
Header = """
EDV-Liste zum automatischen Einlesen in die Weindatenbank;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Stammdaten;;;;;;;;;;;;;;Transportschein;;;;;;;;;;;;;;;;
Empfänger:;;;;;;;Versender:;;;;;;;;;;;;;;;;;;;;;;;
Betriebsnr;Name od. Firmenname;Vorname;Straße;Hausnr;Plz;Ort;Betriebsnr;Name od. Firmenname;Vorname;Straße;Hausnr;Plz;Ort;Datum der Lieferung;Menge in kg;Art;Weiß;Rot;Sorte1;Sorte2;Sorte3;Qualitätsstufe;Jahrgang;Herkunft;°KMW;°Oe;Vollablieferer;Ha Gesamt;Ha Ertragsfähig;Flächenbindung in Ha für AMA
""";
var c = App.Client;
var (a1, a2) = Utils.SplitAddress(c.Address);
_clientData = $"{c.LfbisNr};{c.NameFull};;{a1};{a2};{c.Plz};{c.Ort}";
}
public async Task ExportAsync(int year) {
using var cnx = await AppDbContext.ConnectAsync();
using var cmd = cnx.CreateCommand();
cmd.CommandText = $"""
SELECT lfbis_nr, family_name, name, billing_name, address, plz, ort, area,
date, weight, type, sortid, qualid, year, hkid, kmw, oe
FROM v_bki_delivery
WHERE year = {year}
""";
var r = await cmd.ExecuteReaderAsync();
List<Row> rows = new();
while (await r.ReadAsync()) {
rows.Add(new(
(r.IsDBNull(0) ? null : r.GetString(0), r.IsDBNull(1) ? null : r.GetString(1), r.IsDBNull(2) ? null : r.GetString(2), r.IsDBNull(3) ? null : r.GetString(3), r.GetString(4), r.GetInt32(5), r.GetString(6), r.GetInt32(7)),
(r.GetString(8), r.GetInt32(9), r.GetString(10), r.GetString(11), r.GetString(12), r.GetInt32(13), r.GetString(14), r.GetDouble(15), r.GetDouble(16))
));
}
await ExportAsync(rows);
}
public void Export(int year) {
ExportAsync(year).GetAwaiter().GetResult();
}
public override string FormatRow(Row row) {
var (member, delivery) = row;
var (lfBisNr, familyName, name, billingName, address, plz, ort, area) = member;
var (date, weight, type, sortid, qualid, year, hkid, kmw, oe) = delivery;
var (n1, n2) = billingName == null ? (familyName, name) : Utils.SplitName(billingName, familyName);
var (a1, a2) = Utils.SplitAddress(address);
var memberData = $"{lfBisNr};{n1};{n2};{a1};{a2};{plz};{ort}";
var deliveryData = $"{string.Join(".", date.Split("-").Reverse())};{weight};TB;{(type == "W" ? "J" : "")};{(type == "R" ? "J" : "")};{sortid};;;{qualid};{year};{hkid};{kmw:0.0};{oe:0}";
var vollData = $"N;;;{area / 10_000.0}";
return $"{_clientData};{memberData};{deliveryData};{vollData}";
}
} }
} }

View File

@ -1,4 +1,61 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Elwig.Helpers.Export { namespace Elwig.Helpers.Export {
public class Csv { public abstract class Csv<T> : IExporter<T> {
public static string FileExtension => "csv";
private readonly StreamWriter _writer;
protected readonly char Separator;
protected string? Header;
public Csv(string filename, char separator = ';') : this(filename, separator, Utils.UTF8) { }
public Csv(string filename, char separator, Encoding encoding) {
_writer = new StreamWriter(filename, false, encoding);
Separator = separator;
}
public void Dispose() {
GC.SuppressFinalize(this);
_writer.Dispose();
}
public ValueTask DisposeAsync() {
GC.SuppressFinalize(this);
return _writer.DisposeAsync();
}
public async Task ExportAsync(IEnumerable<T> data, IProgress<double>? progress = null) {
progress?.Report(0.0);
int count = data.Count() + 2, i = 0;
if (Header != null) await _writer.WriteLineAsync(Header);
progress?.Report(100.0 * ++i / count);
foreach (var row in data) {
await _writer.WriteLineAsync(FormatRow(row));
progress?.Report(100.0 * ++i / count);
}
await _writer.FlushAsync();
progress?.Report(100.0);
}
public void Export(IEnumerable<T> data, IProgress<double>? progress = null) {
ExportAsync(data, progress).GetAwaiter().GetResult();
}
public string FormatRow(IEnumerable row) {
return string.Join(Separator, row);
}
public abstract string FormatRow(T row);
} }
} }

View File

@ -1,15 +1,109 @@
using Elwig.Helpers.Billing;
using Elwig.Models; using Elwig.Models;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Elwig.Helpers.Export { namespace Elwig.Helpers.Export {
public class Ebics : IBankingProvider { public class Ebics : IBankingExporter {
public string FileExtension => "xml"; public static string FileExtension => "xml";
public Task Export(string filename, int avnr, IEnumerable<Member> members) { private readonly StreamWriter _writer;
throw new NotImplementedException(); private readonly DateOnly _date;
private readonly int _year;
private readonly string _name;
private readonly int _nr;
public Ebics(PaymentVar variant, string filename) {
_writer = new(filename, false, Utils.UTF8);
_date = variant.TransferDate ?? DateOnly.Parse("2021-01-10"); //throw new ArgumentException("TransferDate has to be set in PaymentVar");
_year = variant.Year;
_name = variant.Name;
_nr = variant.AvNr;
}
public void Dispose() {
GC.SuppressFinalize(this);
_writer.Dispose();
}
public ValueTask DisposeAsync() {
GC.SuppressFinalize(this);
return _writer.DisposeAsync();
}
public void Export(IEnumerable<Transaction> transactions, IProgress<double>? progress = null) {
ExportAsync(transactions, progress).GetAwaiter().GetResult();
}
public async Task ExportAsync(IEnumerable<Transaction> transactions, IProgress<double>? progress = null) {
progress?.Report(0.0);
var nbOfTxs = transactions.Count();
int count = nbOfTxs + 2, i = 0;
var ctrlSum = Transaction.FormatAmountCent(transactions.Sum(tx => tx.AmountCent));
var msgId = $"ELWIG-{App.Client.NameToken}-{_year}-AV{_nr:00}";
var pmtInfId = $"{msgId}-1";
await _writer.WriteLineAsync($"""
<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09 pain.001.001.09.xsd">
<CstmrCdtTrfInitn>
<GrpHdr>
<MsgId>{msgId}</MsgId>
<CreDtTm>{DateTime.UtcNow:o}</CreDtTm>
<NbOfTxs>{nbOfTxs}</NbOfTxs>
<CtrlSum>{ctrlSum}</CtrlSum>
<InitgPty><Nm>{App.Client.NameFull}</Nm></InitgPty>
</GrpHdr>
<PmtInf>
<PmtInfId>{pmtInfId}</PmtInfId>
<PmtMtd>TRF</PmtMtd>
<NbOfTxs>{nbOfTxs}</NbOfTxs>
<CtrlSum>{ctrlSum}</CtrlSum>
<ReqdExctnDt><Dt>{_date:yyyy-MM-dd}</Dt></ReqdExctnDt>
<Dbtr><Nm>{App.Client.NameFull}</Nm></Dbtr>
<DbtrAcct><Id><IBAN>{App.Client.Iban?.Replace(" ", "")}</IBAN></Id></DbtrAcct>
<DbtrAgt><FinInstnId><BICFI>{App.Client.Bic ?? "NOTPROVIDED"}</BICFI></FinInstnId></DbtrAgt>
""");
progress?.Report(100.0 * ++i / count);
foreach (var tx in transactions) {
var a = (IAddress?)tx.Member.BillingAddress ?? tx.Member;
var (a1, a2) = Utils.SplitAddress(a.Address);
var id = $"ELWIG-{App.Client.NameToken}-{_year}-TG{tx.Nr:0000}";
var info = $"{_name} - Traubengutschrift {_year}/{tx.Nr:000}";
await _writer.WriteLineAsync($"""
<CdtTrfTxInf>
<PmtId><EndToEndId>{id}</EndToEndId></PmtId>
<Amt><InstdAmt Ccy="{tx.Currency}">{Transaction.FormatAmountCent(tx.AmountCent)}</InstdAmt></Amt>
<Cdtr>
<Nm>{a.Name}</Nm>
<PstlAdr>
<StrtNm>{a1}</StrtNm><BldgNb>{a2}</BldgNb>
<PstCd>{a.PostalDest.AtPlz?.Plz}</PstCd><TwnNm>{a.PostalDest.AtPlz?.Ort.Name}</TwnNm>
<Ctry>{a.PostalDest.Country.Alpha2}</Ctry>
</PstlAdr>
</Cdtr>
<CdtrAcct><Id><IBAN>{tx.Member.Iban}</IBAN></Id><CdtrAcct>
<CdtrAgt><FinInstnId><BICFI>{tx.Member.Bic ?? "NOTPROVIDED"}</BICFI></FinInstnId></CdtrAgt>
<RmtInf><Ustrd>{info}</Ustrd></RmtInf>
</CdtTrfTxInf>
""");
progress?.Report(100.0 * ++i / count);
}
await _writer.WriteLineAsync("""
</PmtInf>
</CstmrCdtTrfInitn>
</Document>
""");
await _writer.FlushAsync();
progress?.Report(100.0);
} }
} }
} }

View File

@ -1,14 +1,14 @@
using Elwig.Models; using Elwig.Helpers.Billing;
using System; using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Elwig.Helpers.Export { namespace Elwig.Helpers.Export {
public class Elba : IBankingProvider { public class Elba : Csv<Transaction>, IBankingExporter {
public string FileExtension => "elba"; public static new string FileExtension => "elba";
public Task Export(string filename, int avnr, IEnumerable<Member> members) { public Elba(string filename) : base(filename) { }
public override string FormatRow(Transaction row) {
throw new NotImplementedException(); throw new NotImplementedException();
} }
} }

View File

@ -0,0 +1,8 @@
using Elwig.Helpers.Billing;
namespace Elwig.Helpers.Export {
/// <summary>
/// Interface for exporting banking data
/// </summary>
public interface IBankingExporter : IExporter<Transaction> { }
}

View File

@ -1,24 +0,0 @@
using Elwig.Models;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Elwig.Helpers.Export {
/// <summary>
/// Interface for exporting banking data
/// </summary>
public interface IBankingProvider {
/// <summary>
/// The default file extension of the exported files to be used (whithout a preceding ".")
/// </summary>
string FileExtension { get; }
/// <summary>
/// Export the member payment data of the given payment variant to the given file.
/// (The amount of the last payed variant is deducted from the calculated amount.)
/// </summary>
/// <param name="filename">The file to export the data to</param>
/// <param name="avnr">The number of the payment variant to export</param>
/// <param name="members">The members whose data to include in the export</param>
Task Export(string filename, int avnr, IEnumerable<Member> members);
}
}

View File

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Elwig.Helpers.Export {
public interface IExporter<T> : IDisposable, IAsyncDisposable {
/// <summary>
/// The default file extension of the exported files to be used (whithout a preceding ".")
/// </summary>
static abstract string FileExtension { get; }
/// <summary>
/// Export the given data to the given file.
/// </summary>
/// <param name="data">The data to be exported</param>
/// <param name="progress">The progress object to report to</param>
void Export(IEnumerable<T> data, IProgress<double>? progress = null);
/// <summary>
/// Asynchronosly export the given data to the given file.
/// </summary>
/// <param name="data">The data to be exported</param>
/// <param name="progress">The progress object to report to</param>
Task ExportAsync(IEnumerable<T> data, IProgress<double>? progress = null);
}
}

View File

@ -0,0 +1,9 @@
using Elwig.Models;
namespace Elwig.Helpers {
public interface IAddress {
string Name { get; }
string Address { get; }
PostalDest PostalDest { get; }
}
}

View File

@ -5,6 +5,7 @@ namespace Elwig.Helpers {
public sealed class TempFile : IDisposable { public sealed class TempFile : IDisposable {
private int Usages = 0; private int Usages = 0;
public string FilePath { get; private set; } public string FilePath { get; private set; }
public string FileName => Path.GetFileName(FilePath);
public TempFile() : this(null) { } public TempFile() : this(null) { }

View File

@ -15,6 +15,8 @@ using Elwig.Models;
namespace Elwig.Helpers { namespace Elwig.Helpers {
public static partial class Utils { public static partial class Utils {
public static readonly Encoding UTF8 = new UTF8Encoding(false, true);
public static int CurrentYear => DateTime.Now.Year; public static int CurrentYear => DateTime.Now.Year;
public static int CurrentNextSeason => DateTime.Now.Year - (DateTime.Now.Month <= 3 ? 1 : 0); public static int CurrentNextSeason => DateTime.Now.Year - (DateTime.Now.Month <= 3 ? 1 : 0);
public static int CurrentLastSeason => DateTime.Now.Year - (DateTime.Now.Month <= 7 ? 1 : 0); public static int CurrentLastSeason => DateTime.Now.Year - (DateTime.Now.Month <= 7 ? 1 : 0);
@ -22,9 +24,10 @@ namespace Elwig.Helpers {
public static readonly Regex SerialRegex = GeneratedSerialRegex(); public static readonly Regex SerialRegex = GeneratedSerialRegex();
public static readonly Regex TcpRegex = GeneratedTcpRegex(); public static readonly Regex TcpRegex = GeneratedTcpRegex();
public static readonly Regex PartialDateRegex = GeneratedPartialDateRegex(); public static readonly Regex DateFromToRegex = GeneratedFromToDateRegex();
public static readonly Regex FromToRegex = GeneratedFromToRegex(); public static readonly Regex FromToRegex = GeneratedFromToRegex();
public static readonly Regex FromToTimeRegex = GeneratedFromToTimeRegex(); public static readonly Regex FromToTimeRegex = GeneratedFromToTimeRegex();
public static readonly Regex AddressRegex = GeneratedAddressRegex();
[GeneratedRegex("^serial://([A-Za-z0-9]+):([0-9]+)(,([5-9]),([NOEMSnoems]),(0|1|1\\.5|2|))?$", RegexOptions.Compiled)] [GeneratedRegex("^serial://([A-Za-z0-9]+):([0-9]+)(,([5-9]),([NOEMSnoems]),(0|1|1\\.5|2|))?$", RegexOptions.Compiled)]
private static partial Regex GeneratedSerialRegex(); private static partial Regex GeneratedSerialRegex();
@ -32,8 +35,8 @@ namespace Elwig.Helpers {
[GeneratedRegex("^tcp://([A-Za-z0-9._-]+):([0-9]+)$", RegexOptions.Compiled)] [GeneratedRegex("^tcp://([A-Za-z0-9._-]+):([0-9]+)$", RegexOptions.Compiled)]
private static partial Regex GeneratedTcpRegex(); private static partial Regex GeneratedTcpRegex();
[GeneratedRegex(@"^(0?[1-9]|[12][0-9]|3[01])\.(0?[1-9]|1[0-2])\.$", RegexOptions.Compiled)] [GeneratedRegex(@"^(-?(0?[1-9]|[12][0-9]|3[01])\.(0?[1-9]|1[0-2])\.([0-9]{4})?-?){1,2}$", RegexOptions.Compiled)]
private static partial Regex GeneratedPartialDateRegex(); private static partial Regex GeneratedFromToDateRegex();
[GeneratedRegex(@"^([0-9]+([\.,][0-9]+)?)?-([0-9]+([\.,][0-9]+)?)?$", RegexOptions.Compiled)] [GeneratedRegex(@"^([0-9]+([\.,][0-9]+)?)?-([0-9]+([\.,][0-9]+)?)?$", RegexOptions.Compiled)]
private static partial Regex GeneratedFromToRegex(); private static partial Regex GeneratedFromToRegex();
@ -41,6 +44,9 @@ namespace Elwig.Helpers {
[GeneratedRegex(@"^([0-9]{1,2}:[0-9]{2})?-([0-9]{1,2}:[0-9]{2})?$", RegexOptions.Compiled)] [GeneratedRegex(@"^([0-9]{1,2}:[0-9]{2})?-([0-9]{1,2}:[0-9]{2})?$", RegexOptions.Compiled)]
private static partial Regex GeneratedFromToTimeRegex(); private static partial Regex GeneratedFromToTimeRegex();
[GeneratedRegex(@"^(.*?) +([0-9].*)$", RegexOptions.Compiled)]
private static partial Regex GeneratedAddressRegex();
private static readonly ushort[] Crc16ModbusTable = { private static readonly ushort[] Crc16ModbusTable = {
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
@ -151,8 +157,12 @@ namespace Elwig.Helpers {
} }
public static void MailTo(string emailAddress) { public static void MailTo(string emailAddress) {
MailTo(new string[] { emailAddress });
}
public static void MailTo(IEnumerable<string> emailAddresses) {
Process.Start(new ProcessStartInfo() { Process.Start(new ProcessStartInfo() {
FileName = $"mailto:{emailAddress}", FileName = $"mailto:{string.Join(",%20", emailAddresses)}",
UseShellExecute = true, UseShellExecute = true,
}); });
} }
@ -187,34 +197,33 @@ namespace Elwig.Helpers {
if (!searchKeywords.Any()) if (!searchKeywords.Any())
return 0; return 0;
return words return searchKeywords
.Select(w => { .Select(k => {
w = w?.ToLower(); k = k.ToLower();
var p = w?.ToLower()?.Split(" "); var scores = words.Select(w => {
if (w == null || p == null) w = w?.ToLower();
return 0; var p = w?.ToLower()?.Split(" ");
if (w == null || p == null) {
var t1 = searchKeywords.Where(f => w == f).Select(f => f.Length).OrderDescending().FirstOrDefault(0); return 0;
var t2 = searchKeywords.Where(f => p.Any(a => a == f)).Select(f => f.Length).OrderDescending().FirstOrDefault(0); } else if (k == w) {
var t3 = searchKeywords.Where(f => p.Any(a => a.StartsWith(f))).Select(f => f.Length).OrderDescending().FirstOrDefault(0); return 4 + k.Length;
var t4 = searchKeywords.Where(f => w.Contains(f)).Select(f => f.Length).OrderDescending().FirstOrDefault(0); } else if (p.Any(a => a == k)) {
if (t1 > 0) { return 3 + k.Length;
return 4 + t1; } else if (p.Any(a => a.StartsWith(k))) {
} else if (t2 > 0) { return 2 + k.Length;
return 3 + t2; } else if (w.Contains(k)) {
} else if (t3 > 0) { return 1 + k.Length;
return 2 + t3; } else {
} else if (t4 > 0) { return 0;
return 1 + t4; }
} else { });
return 0; return scores.Max() + scores.Count(s => s > 0);
}
}) })
.Sum(); .Sum();
} }
public static (int, string?)? ShowManualWeighingDialog() { public static (int, string?)? ShowManualWeighingDialog(string? reason = null) {
var d = new ManualWeighingDialog(); var d = new ManualWeighingDialog(reason);
return d.ShowDialog() == true ? (d.Weight, d.Reason) : null; return d.ShowDialog() == true ? (d.Weight, d.Reason) : null;
} }
@ -292,5 +301,45 @@ namespace Elwig.Helpers {
public static string GenerateLsNr(Delivery d) => GenerateLsNr(d.Date, d.ZwstId, d.LNr); public static string GenerateLsNr(Delivery d) => GenerateLsNr(d.Date, d.ZwstId, d.LNr);
public static string GenerateLsNr(DateOnly date, string zwstid, int lnr) => $"{date:yyyyMMdd}{zwstid}{lnr:000}"; public static string GenerateLsNr(DateOnly date, string zwstid, int lnr) => $"{date:yyyyMMdd}{zwstid}{lnr:000}";
public static (string, string?) SplitAddress(string address) {
var m = AddressRegex.Match(address);
return (m.Groups[1].Value, m.Groups[2].Value);
}
public static (string, string?) SplitName(string fullName, string? familyName) {
if (familyName == null || familyName == "") return (fullName, null);
var p0 = fullName.ToLower().IndexOf(familyName.ToLower());
if (p0 == -1) return (fullName, null);
var p1 = fullName.IndexOf(" und ");
var p2 = fullName.ToLower().LastIndexOf(" und ");
if (p1 != p2) {
if (p0 > p1) {
// A und B familyName [und ...]
return (fullName[p0..^0], fullName[0..(p0 - 1)]);
} else {
// familyName und ... A und B
var p3 = fullName.LastIndexOf(' ', p2 - 1);
return (fullName[0..p3], fullName[(p3 + 1)..^0]);
}
} else {
return (familyName, fullName.Replace(familyName, "").Replace(" ", " ").Trim());
}
}
public static IEnumerable<IEnumerable<T>> Permutate<T>(IEnumerable<T> input, IEnumerable<T>? forced = null) {
HashSet<IEnumerable<T>> output = new();
for (int i = 0; i < Math.Pow(2, input.Count()); i++) {
List<T> t = new();
for (int j = 0; j < 30; j++) {
var e = input.ElementAtOrDefault(j);
if (e != null && ((forced?.Contains(e) ?? false) || (i & (1 << j)) != 0)) {
t.Add(e);
}
}
output.Add(t);
}
return output.OrderByDescending(l => l.Count());
}
} }
} }

View File

@ -70,9 +70,9 @@ namespace Elwig.Helpers {
for (int i = 0; i < input.Text.Length; i++) { for (int i = 0; i < input.Text.Length; i++) {
char ch = input.Text[i]; char ch = input.Text[i];
if (char.IsAsciiDigit(ch)) { if (char.IsAsciiDigit(ch)) {
if (v2 == -1 && v1 < maxLen) { if (v2 == -1 && (maxLen == -1 || v1 < maxLen)) {
text += ch; v1++; text += ch; v1++;
} else if (v2 != -1 && v2 < maxDecimal) { } else if (v2 != -1 && (maxDecimal == -1 || v2 < maxDecimal)) {
text += ch; v2++; text += ch; v2++;
} }
} else if (v2 == 0-1 && ch == ',' || ch == '.') { } else if (v2 == 0-1 && ch == ',' || ch == '.') {
@ -521,6 +521,36 @@ namespace Elwig.Helpers {
return new(true, null); return new(true, null);
} }
public static ValidationResult CheckTime(TextBox input, bool required) {
string text = "";
int pos = input.CaretIndex;
int v = 0;
for (int i = 0; i < input.Text.Length; i++) {
char ch = input.Text[i];
if (v >= 0 && v < 5 && v != 2 && char.IsAsciiDigit(ch)) {
if ((v == 0 && ch <= '2') || (v == 1 && (text[0] < '2' || ch <= '3')) || (v == 3 && ch <= '5') || v == 4) {
text += ch;
v++;
}
} else if (v == 2 && ch == ':') {
text += ch;
v++;
}
if (i == input.CaretIndex - 1)
pos = text.Length;
}
input.Text = text;
input.CaretIndex = pos;
if (text.Length == 0) {
return required ? new(false, "Wert ist nicht optional") : new(true, null);
} else if (v != 5) {
return new(false, "Zeit ist ungültig");
} else {
return new(true, null);
}
}
public static ValidationResult CheckFbNr(TextBox input, bool required, AppDbContext ctx, AreaCom? c) { public static ValidationResult CheckFbNr(TextBox input, bool required, AppDbContext ctx, AreaCom? c) {
var res = CheckInteger(input, required); var res = CheckInteger(input, required);
if (!res.IsValid) { if (!res.IsValid) {

View File

@ -0,0 +1,44 @@
using System;
using System.Threading.Tasks;
namespace Elwig.Helpers.Weighing {
public class InvalidScale : IScale {
public string Manufacturer => "NONE";
public string Model => "NONE";
public string ScaleId { get; private set; }
public int InternalScaleNr => 0;
public bool IsReady => false;
public bool HasFillingClearance => false;
public int? WeightLimit => null;
public string? LogPath => null;
public InvalidScale(string id) {
ScaleId = id;
}
public void Dispose() {
GC.SuppressFinalize(this);
}
public Task<WeighingResult> Weigh() {
throw new NotImplementedException();
}
public Task<WeighingResult> GetCurrentWeight() {
throw new NotImplementedException();
}
public Task Empty() {
throw new NotImplementedException();
}
public Task GrantFillingClearance() {
throw new NotImplementedException();
}
public Task RevokeFillingClearance() {
throw new NotImplementedException();
}
}
}

View File

@ -1,5 +1,6 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using IndexAttribute = Microsoft.EntityFrameworkCore.IndexAttribute;
namespace Elwig.Models { namespace Elwig.Models {
[Table("AT_plz_dest"), PrimaryKey("Id"), Index("Plz", "Okz", IsUnique = true)] [Table("AT_plz_dest"), PrimaryKey("Id"), Index("Plz", "Okz", IsUnique = true)]

View File

@ -1,6 +1,7 @@
using Elwig.Helpers; using Elwig.Helpers;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using IndexAttribute = Microsoft.EntityFrameworkCore.IndexAttribute;
namespace Elwig.Models { namespace Elwig.Models {
[Table("area_commitment_type"), PrimaryKey("VtrgId"), Index("SortId", "AttrId1", "AttrId2", "Discriminator")] [Table("area_commitment_type"), PrimaryKey("VtrgId"), Index("SortId", "AttrId1", "AttrId2", "Discriminator")]

View File

@ -1,9 +1,10 @@
using Elwig.Helpers;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
namespace Elwig.Models { namespace Elwig.Models {
[Table("member_billing_address"), PrimaryKey("MgNr")] [Table("member_billing_address"), PrimaryKey("MgNr")]
public class BillingAddr { public class BillingAddr : IAddress {
[Column("mgnr")] [Column("mgnr")]
public int MgNr { get; set; } public int MgNr { get; set; }

View File

@ -1,5 +1,6 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using IndexAttribute = Microsoft.EntityFrameworkCore.IndexAttribute;
namespace Elwig.Models { namespace Elwig.Models {
[Table("country"), PrimaryKey("Num"), Index("Alpha2", IsUnique = true), Index("Alpha3", IsUnique = true)] [Table("country"), PrimaryKey("Num"), Index("Alpha2", IsUnique = true), Index("Alpha3", IsUnique = true)]

105
Elwig/Models/Credit.cs Normal file
View File

@ -0,0 +1,105 @@
using Elwig.Helpers;
using Microsoft.EntityFrameworkCore;
using System;
using System.ComponentModel.DataAnnotations.Schema;
using IndexAttribute = Microsoft.EntityFrameworkCore.IndexAttribute;
namespace Elwig.Models {
[Table("credit"), PrimaryKey("Year", "TgNr"), Index("Year", "AvNr", "MgNr", IsUnique = true)]
public class Credit {
[Column("year")]
public int Year { get; set; }
[Column("tgnr")]
public int TgNr { get; set; }
[NotMapped]
public string TgId => $"{Year}/{TgNr:000}";
[Column("mgnr")]
public int MgNr { get; set; }
[Column("avnr")]
public int AvNr { get; set; }
[Column("net_amount")]
public long NetAmountValue { get; set; }
[NotMapped]
public decimal NetAmount {
get => Utils.DecFromDb(NetAmountValue, 2);
set => NetAmountValue = Utils.DecToDb(value, 2);
}
[Column("prev_net_amount")]
public long? PrevNetAmountValue { get; set; }
[NotMapped]
public decimal? PrevNetAmount {
get => PrevNetAmountValue != null ? Utils.DecFromDb(PrevNetAmountValue.Value, 2) : null;
set => PrevNetAmountValue = value != null ? Utils.DecToDb(value.Value, 2) : null;
}
[Column("vat")]
public double VatValue { get; set; }
[NotMapped]
public decimal Vat {
get => (decimal)VatValue;
set => VatValue = (double)value;
}
[Column("vat_amount")]
public long VatAmountValue { get; private set; }
[NotMapped]
public decimal VatAmount {
get => Utils.DecFromDb(VatAmountValue, 2);
}
[Column("gross_amount")]
public long GrossAmountValue { get; private set; }
[NotMapped]
public decimal GrossAmount {
get => Utils.DecFromDb(GrossAmountValue, 2);
}
[Column("modifiers")]
public long? ModifiersValue { get; set; }
[NotMapped]
public decimal? Modifiers {
get => ModifiersValue != null ? Utils.DecFromDb(ModifiersValue.Value, 2) : null;
set => ModifiersValue = value != null ? Utils.DecToDb(value.Value, 2) : null;
}
[Column("prev_modifiers")]
public long? PrevModifiersValue { get; set; }
[NotMapped]
public decimal? PrevModifiers {
get => PrevModifiersValue != null ? Utils.DecFromDb(PrevModifiersValue.Value, 2) : null;
set => PrevModifiersValue = value != null ? Utils.DecToDb(value.Value, 2) : null;
}
[Column("amount")]
public long AmountValue { get; private set; }
[NotMapped]
public decimal Amount {
get => Utils.DecFromDb(AmountValue, 2);
}
[Column("ctime")]
public long CTime { get; private set; }
[NotMapped]
public DateTime CreatedTimestamp => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
[Column("mtime")]
public long MTime { get; private set; }
[NotMapped]
public DateTime ModifiedTimestamp => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
[ForeignKey("Year, AvNr, MgNr")]
public virtual PaymentMember Payment { get; private set; }
[ForeignKey("Year, AvNr")]
public virtual PaymentVar Variant { get; private set; }
[ForeignKey("MgNr")]
public virtual Member Member { get; private set; }
}
}

View File

@ -4,6 +4,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.Linq; using System.Linq;
using IndexAttribute = Microsoft.EntityFrameworkCore.IndexAttribute;
namespace Elwig.Models { namespace Elwig.Models {
[Table("delivery"), PrimaryKey("Year", "DId"), Index("DateString", "ZwstId", "LNr", IsUnique = true), Index("LsNr", IsUnique = true)] [Table("delivery"), PrimaryKey("Year", "DId"), Index("DateString", "ZwstId", "LNr", IsUnique = true), Index("LsNr", IsUnique = true)]
@ -19,12 +20,8 @@ namespace Elwig.Models {
[NotMapped] [NotMapped]
public DateOnly Date { public DateOnly Date {
get { get => DateOnly.ParseExact(DateString, "yyyy-MM-dd");
return DateOnly.ParseExact(DateString, "yyyy-MM-dd"); set => DateString = value.ToString("yyyy-MM-dd");
}
set {
DateString = value.ToString("yyyy-MM-dd");
}
} }
[Column("time")] [Column("time")]
@ -32,19 +29,13 @@ namespace Elwig.Models {
[NotMapped] [NotMapped]
public TimeOnly? Time { public TimeOnly? Time {
get { get => (TimeString == null) ? null : TimeOnly.ParseExact(TimeString, "HH:mm:ss");
return (TimeString == null) ? null : TimeOnly.ParseExact(TimeString, "HH:mm:ss"); set => TimeString = value?.ToString("HH:mm:ss");
}
set {
TimeString = value?.ToString("HH:mm:ss");
}
} }
[NotMapped] [NotMapped]
public DateTime DateTime { public DateTime DateTime {
get { get => Date.ToDateTime(Time ?? TimeOnly.MinValue);
return Date.ToDateTime(Time ?? TimeOnly.MinValue);
}
set { set {
Date = DateOnly.FromDateTime(value); Date = DateOnly.FromDateTime(value);
Time = TimeOnly.FromDateTime(value); Time = TimeOnly.FromDateTime(value);
@ -72,6 +63,9 @@ namespace Elwig.Models {
[Column("comment")] [Column("comment")]
public string? Comment { get; set; } public string? Comment { get; set; }
[ForeignKey("Year")]
public virtual Season Season { get; private set; }
[InverseProperty("Delivery")] [InverseProperty("Delivery")]
public virtual ISet<DeliveryPart> Parts { get; private set; } public virtual ISet<DeliveryPart> Parts { get; private set; }

View File

@ -30,15 +30,10 @@ namespace Elwig.Models {
[Column("kmw")] [Column("kmw")]
public double Kmw { get; set; } public double Kmw { get; set; }
[NotMapped] [NotMapped]
public double Oe { public double Oe {
get { get => Utils.KmwToOe(Kmw);
return Utils.KmwToOe(Kmw); set => Kmw = Utils.OeToKmw(value);
}
set {
Kmw = Utils.OeToKmw(value);
}
} }
[Column("qualid")] [Column("qualid")]
@ -80,6 +75,9 @@ namespace Elwig.Models {
[Column("lesewagen")] [Column("lesewagen")]
public bool? IsLesewagen { get; set; } public bool? IsLesewagen { get; set; }
[Column("gebunden")]
public bool? IsGebunden { get; set; }
[Column("temperature")] [Column("temperature")]
public double? Temperature { get; set; } public double? Temperature { get; set; }
@ -92,6 +90,9 @@ namespace Elwig.Models {
[Column("weighing_id")] [Column("weighing_id")]
public string? WeighingId { get; set; } public string? WeighingId { get; set; }
[Column("weighing_reason")]
public string? WeighingReason { get; set; }
[Column("comment")] [Column("comment")]
public string? Comment { get; set; } public string? Comment { get; set; }
@ -101,12 +102,22 @@ namespace Elwig.Models {
[NotMapped] [NotMapped]
public IEnumerable<WineAttr> Attributes => PartAttributes.Select(a => a.Attr); public IEnumerable<WineAttr> Attributes => PartAttributes.Select(a => a.Attr);
[NotMapped]
public string AttributesString => string.Join(" / ", Attributes);
[InverseProperty("Part")] [InverseProperty("Part")]
public virtual ISet<DeliveryPartModifier> PartModifiers { get; private set; } public virtual ISet<DeliveryPartModifier> PartModifiers { get; private set; }
[NotMapped] [NotMapped]
public IEnumerable<Modifier> Modifiers => PartModifiers.Select(m => m.Modifier).OrderBy(m => m.Ordering); public IEnumerable<Modifier> Modifiers => PartModifiers.Select(m => m.Modifier).OrderBy(m => m.Ordering);
[InverseProperty("DeliveryPart")]
public virtual PaymentDeliveryPart? Payment { get; private set; }
[NotMapped]
public string OriginString => Origin.OriginString + "\n" + (Kg?.Gl != null ? $" / {Kg.Gl.Name}" : "") + (Kg != null ? $" / {Kg.AtKg.Gem.Name} / KG {Kg.AtKg.Name}" : "") + (Rd != null ? $" / Ried {Rd.Name}" : ""); public string OriginString => Origin.OriginString + "\n" + (Kg?.Gl != null ? $" / {Kg.Gl.Name}" : "") + (Kg != null ? $" / {Kg.AtKg.Gem.Name} / KG {Kg.AtKg.Name}" : "") + (Rd != null ? $" / Ried {Rd.Name}" : "");
[InverseProperty("Part")]
public virtual ISet<DeliveryPartBin> Bins { get; private set; }
} }
} }

View File

@ -0,0 +1,28 @@
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;
namespace Elwig.Models {
[Table("delivery_part_bin"), PrimaryKey("Year", "DId", "DPNr", "BinNr")]
public class DeliveryPartBin {
[Column("year")]
public int Year { get; set; }
[Column("did")]
public int DId { get; set; }
[Column("dpnr")]
public int DPNr { get; set; }
[Column("binnr")]
public int BinNr { get; set; }
[Column("discr")]
public string Discr { get; set; }
[Column("value")]
public int Value { get; set; }
[ForeignKey("Year, DId, DPNr")]
public virtual DeliveryPart Part { get; private set; }
}
}

View File

@ -7,7 +7,7 @@ using System.Linq;
namespace Elwig.Models { namespace Elwig.Models {
[Table("member"), PrimaryKey("MgNr")] [Table("member"), PrimaryKey("MgNr")]
public class Member { public class Member : IAddress {
[Column("mgnr")] [Column("mgnr")]
public int MgNr { get; set; } public int MgNr { get; set; }
@ -128,9 +128,6 @@ namespace Elwig.Models {
[Column("address")] [Column("address")]
public string Address { get; set; } public string Address { get; set; }
[Column("email")]
public string? Email { get; set; }
[Column("default_kgnr")] [Column("default_kgnr")]
public int? DefaultKgNr { get; set; } public int? DefaultKgNr { get; set; }
@ -174,6 +171,9 @@ namespace Elwig.Models {
[InverseProperty("Member")] [InverseProperty("Member")]
public virtual ISet<MemberTelNr> TelephoneNumbers { get; private set; } public virtual ISet<MemberTelNr> TelephoneNumbers { get; private set; }
[InverseProperty("member")]
public virtual ISet<MemberEmailAddr> EmailAddresses { get; private set; }
public string FullAddress => $"{Address}, {PostalDest.AtPlz.Plz} {PostalDest.AtPlz.Ort.Name}"; public string FullAddress => $"{Address}, {PostalDest.AtPlz.Plz} {PostalDest.AtPlz.Ort.Name}";
public int DeliveryRight => BusinessShares * App.Client.DeliveryRight; public int DeliveryRight => BusinessShares * App.Client.DeliveryRight;

View File

@ -0,0 +1,22 @@
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;
namespace Elwig.Models {
[Table("member_email_address"), PrimaryKey("MgNr", "Nr")]
public class MemberEmailAddr {
[Column("mgnr")]
public int MgNr { get; set; }
[Column("nr")]
public int Nr { get; set; }
[Column("address")]
public string Address { get; set; }
[Column("comment")]
public string? Comment { get; set; }
[ForeignKey("MgNr")]
public virtual Member Member { get; private set; }
}
}

View File

@ -0,0 +1,49 @@
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;
namespace Elwig.Models {
[Table("payment_delivery_part"), PrimaryKey("Year", "DId", "DPNr", "AvNr")]
public class PaymentDeliveryPart {
[Column("year")]
public int Year { get; set; }
[Column("did")]
public int DId { get; set; }
[Column("dpnr")]
public int DPNr { get; set; }
[Column("avnr")]
public int AvNr { get; set; }
[Column("mod_abs")]
public long ModAbsValue { get; set; }
[NotMapped]
public decimal ModAbs {
get => Variant.Season.DecFromDb(ModAbsValue);
set => ModAbsValue = Variant.Season.DecToDb(value);
}
[Column("mod_rel")]
public double ModRelValue { get; set; }
[NotMapped]
public decimal ModRel {
get => (decimal)ModRelValue;
set => ModRelValue = (double)value;
}
[Column("amount")]
public long? AmountValue { get; set; }
[NotMapped]
public decimal? Amount {
get => AmountValue != null ? Variant.Season.DecFromDb(AmountValue.Value) : null;
set => AmountValue = value != null ? Variant.Season.DecToDb(value.Value) : null;
}
[ForeignKey("Year, AvNr")]
public virtual PaymentVar Variant { get; private set; }
[ForeignKey("Year, DId, DPNr")]
public virtual DeliveryPart DeliveryPart { get; private set; }
}
}

View File

@ -0,0 +1,44 @@
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;
namespace Elwig.Models {
[Table("payment_delivery_part_bin"), PrimaryKey("Year", "DId", "DPNr", "BinNr", "AvNr")]
public class PaymentDeliveryPartBin {
[Column("year")]
public int Year { get; set; }
[Column("did")]
public int DId { get; set; }
[Column("dpnr")]
public int DPNr { get; set; }
[Column("binnr")]
public int BinNr { get; set; }
[Column("avnr")]
public int AvNr { get; set; }
[Column("price")]
public long PriceValue { get; set; }
[NotMapped]
public decimal Price {
get => Variant.Season.DecFromDb(PriceValue);
set => PriceValue = Variant.Season.DecToDb(value);
}
[Column("amount")]
public long AmountValue { get; set; }
[NotMapped]
public decimal Amount {
get => Variant.Season.DecFromDb(AmountValue);
set => AmountValue = Variant.Season.DecToDb(value);
}
[ForeignKey("Year, AvNr")]
public virtual PaymentVar Variant { get; private set; }
[ForeignKey("Year, DId, DPNr")]
public virtual DeliveryPart DeliveryPart { get; private set; }
}
}

View File

@ -0,0 +1,34 @@
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;
namespace Elwig.Models {
[Table("payment_member"), PrimaryKey("Year", "AvNr", "MgNr")]
public class PaymentMember {
[Column("year")]
public int Year { get; set; }
[Column("avnr")]
public int AvNr { get; set; }
[Column("mgnr")]
public int MgNr { get; set; }
[Column("amount")]
public long AmountValue { get; set; }
[NotMapped]
public decimal Amount {
get => Variant.Season.DecFromDb(AmountValue);
set => AmountValue = Variant.Season.DecToDb(value);
}
[ForeignKey("Year, AvNr")]
public virtual PaymentVar Variant { get; private set; }
[ForeignKey("MgNr")]
public virtual Member Member { get; private set; }
[InverseProperty("Payment")]
public virtual Credit? Credit { get; private set; }
}
}

View File

@ -1,6 +1,8 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System; using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
namespace Elwig.Models { namespace Elwig.Models {
[Table("payment_variant"), PrimaryKey("Year", "AvNr")] [Table("payment_variant"), PrimaryKey("Year", "AvNr")]
@ -19,12 +21,17 @@ namespace Elwig.Models {
[NotMapped] [NotMapped]
public DateOnly Date { public DateOnly Date {
get { get => DateOnly.ParseExact(DateString, "yyyy-MM-dd");
return DateOnly.ParseExact(DateString, "yyyy-MM-dd"); set => DateString = value.ToString("yyyy-MM-dd");
} }
set {
DateString = value.ToString("yyyy-MM-dd"); [Column("transfer_date")]
} public string? TransferDateString { get; set; }
[NotMapped]
public DateOnly? TransferDate {
get => TransferDateString != null ? DateOnly.ParseExact(TransferDateString, "yyyy-MM-dd") : null;
set => TransferDateString = value?.ToString("yyyy-MM-dd");
} }
[Column("test_variant")] [Column("test_variant")]
@ -33,37 +40,19 @@ namespace Elwig.Models {
[Column("calc_time")] [Column("calc_time")]
public int? CalcTime { get; set; } public int? CalcTime { get; set; }
[Column("bucket_1_name")]
public string? Bucket1Name { get; set; }
[Column("bucket_2_name")]
public string? Bucket2Name { get; set; }
[Column("bucket_3_name")]
public string? Bucket3Name { get; set; }
[Column("bucket_4_name")]
public string? Bucket4Name { get; set; }
[Column("bucket_5_name")]
public string? Bucket5Name { get; set; }
[Column("bucket_6_name")]
public string? Bucket6Name { get; set; }
[Column("bucket_7_name")]
public string? Bucket7Name { get; set; }
[Column("bucket_8_name")]
public string? Bucket8Name { get; set; }
[Column("bucket_9_name")]
public string? Bucket9Name { get; set; }
[Column("comment")] [Column("comment")]
public string? Comment { get; set; } public string? Comment { get; set; }
[Column("data")] [Column("data")]
public string Data { get; set; } public string Data { get; set; }
[ForeignKey("Year")]
public virtual Season Season { get; private set; }
[InverseProperty("Variant")]
public virtual ISet<PaymentMember> MemberPayments { get; private set; }
[InverseProperty("Variant")]
public virtual ISet<Credit> Credits { get; private set; }
} }
} }

View File

@ -48,6 +48,12 @@ namespace Elwig.Models {
[InverseProperty("Season")] [InverseProperty("Season")]
public virtual ISet<Modifier> Modifiers { get; private set; } public virtual ISet<Modifier> Modifiers { get; private set; }
[InverseProperty("Season")]
public virtual ISet<PaymentVar> PaymentVariants { get; private set; }
[InverseProperty("Season")]
public virtual ISet<Delivery> Deliveries { get; private set; }
public decimal DecFromDb(long value) { public decimal DecFromDb(long value) {
return Utils.DecFromDb(value, Precision); return Utils.DecFromDb(value, Precision);
} }

View File

@ -13,6 +13,9 @@ namespace Elwig.Models {
[Column("max_kg_per_ha")] [Column("max_kg_per_ha")]
public int? MaxKgPerHa { get; set; } public int? MaxKgPerHa { get; set; }
[Column("fill_lower_bins")]
public int FillLowerBins { get; set; }
[Column("active")] [Column("active")]
public bool IsActive { get; set; } public bool IsActive { get; set; }
public override string ToString() { public override string ToString() {

View File

@ -3,6 +3,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.Linq; using System.Linq;
using IndexAttribute = Microsoft.EntityFrameworkCore.IndexAttribute;
namespace Elwig.Models { namespace Elwig.Models {
[Table("wine_origin"), PrimaryKey("HkId"), Index("Name", IsUnique = true)] [Table("wine_origin"), PrimaryKey("HkId"), Index("Name", IsUnique = true)]

View File

@ -18,6 +18,9 @@ namespace Elwig.Models {
public string CommentFormat => (Comment != null) ? $" ({Comment})" : ""; public string CommentFormat => (Comment != null) ? $" ({Comment})" : "";
public bool IsRed => Type == "R";
public bool IsWhite => Type == "W";
public override string ToString() { public override string ToString() {
return Name; return Name;
} }

View File

@ -16,22 +16,23 @@ namespace Elwig.Windows {
protected Control[] ExemptInputs { private get; set; } protected Control[] ExemptInputs { private get; set; }
protected Control[] RequiredInputs { private get; set; } protected Control[] RequiredInputs { private get; set; }
private bool _IsEditing; private bool _isEditing;
private bool _IsCreating; private bool _isCreating;
protected bool IsEditing { protected bool IsEditing {
get { return _IsEditing; } get { return _isEditing; }
set { set {
_IsEditing = value; _isEditing = value;
LockContext = IsEditing; LockContext = IsEditing || IsCreating;
} }
} }
protected bool IsCreating { protected bool IsCreating {
get { return _IsCreating; } get { return _isCreating; }
set { set {
_IsCreating = value; _isCreating = value;
LockContext = IsEditing; LockContext = IsEditing || IsCreating;
} }
} }
protected bool DoShowWarningWindows = true;
protected bool IsClosing { get; private set; } protected bool IsClosing { get; private set; }
private TextBox[] TextBoxInputs; private TextBox[] TextBoxInputs;
@ -81,6 +82,14 @@ namespace Elwig.Windows {
} }
private void OnClosing(object? sender, CancelEventArgs evt) { private void OnClosing(object? sender, CancelEventArgs evt) {
if ((IsCreating || IsEditing) && HasChanged) {
var r = System.Windows.MessageBox.Show("Soll das Fenster wirklich geschlossen werden?", "Schlie<69>en best<73>tigen",
MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
if (r != MessageBoxResult.Yes) {
evt.Cancel = true;
return;
}
}
IsClosing = true; IsClosing = true;
} }
@ -90,7 +99,7 @@ namespace Elwig.Windows {
abstract protected void UpdateButtons(); abstract protected void UpdateButtons();
protected override async Task RenewContext() { protected override async Task OnRenewContext() {
for (int i = 0; i < PlzInputs.Length; i++) for (int i = 0; i < PlzInputs.Length; i++)
UpdatePlz(PlzInputs[i], PlzOrtInputs[i]); UpdatePlz(PlzInputs[i], PlzOrtInputs[i]);
} }
@ -292,12 +301,20 @@ namespace Elwig.Windows {
} }
protected bool HasChanged => protected bool HasChanged =>
!IsValid || IsEditing && (
TextBoxInputs.Any(InputHasChanged) || !IsValid ||
ComboBoxInputs.Any(InputHasChanged) || TextBoxInputs.Any(InputHasChanged) ||
CheckComboBoxInputs.Any(InputHasChanged) || ComboBoxInputs.Any(InputHasChanged) ||
CheckBoxInputs.Any(InputHasChanged) || CheckComboBoxInputs.Any(InputHasChanged) ||
RadioButtonInputs.Any(InputHasChanged); CheckBoxInputs.Any(InputHasChanged) ||
RadioButtonInputs.Any(InputHasChanged)
) || IsCreating && (
TextBoxInputs.Any(i => InputIsNotDefault(i) || (!i.IsReadOnly && i.Text != "")) ||
ComboBoxInputs.Any(i => InputIsNotDefault(i) || (i.IsEnabled && i.SelectedItem != null)) ||
CheckComboBoxInputs.Any(i => InputIsNotDefault(i) || i.SelectedItem != null) ||
CheckBoxInputs.Any(InputIsNotDefault) ||
RadioButtonInputs.Any(InputIsNotDefault)
);
protected void UpdatePlz(TextBox plzInput, ComboBox ortInput) { protected void UpdatePlz(TextBox plzInput, ComboBox ortInput) {
var plzInputValid = GetInputValid(plzInput); var plzInputValid = GetInputValid(plzInput);
@ -363,7 +380,7 @@ namespace Elwig.Windows {
} }
protected bool InputLostFocus(TextBox input, ValidationResult res, string? msg = null) { protected bool InputLostFocus(TextBox input, ValidationResult res, string? msg = null) {
if (!res.IsValid && !IsClosing && (IsEditing || IsCreating)) if (DoShowWarningWindows && !res.IsValid && !IsClosing && (IsEditing || IsCreating))
System.Windows.MessageBox.Show(res.ErrorContent.ToString(), msg ?? res.ErrorContent.ToString(), MessageBoxButton.OK, MessageBoxImage.Warning); System.Windows.MessageBox.Show(res.ErrorContent.ToString(), msg ?? res.ErrorContent.ToString(), MessageBoxButton.OK, MessageBoxImage.Warning);
return res.IsValid; return res.IsValid;
} }
@ -396,7 +413,7 @@ namespace Elwig.Windows {
UpdateButtons(); UpdateButtons();
} }
protected void TextBox_TextChanged(object sender, RoutedEventArgs evt) { protected void TextBox_TextChanged(object sender, RoutedEventArgs? evt) {
var input = (TextBox)sender; var input = (TextBox)sender;
if (SenderIsRequired(input) && input.Text.Length == 0) { if (SenderIsRequired(input) && input.Text.Length == 0) {
ValidateInput(input, false); ValidateInput(input, false);
@ -445,6 +462,10 @@ namespace Elwig.Windows {
InputTextChanged((TextBox)sender, Validator.CheckInteger); InputTextChanged((TextBox)sender, Validator.CheckInteger);
} }
protected void DecimalInput_TextChanged(object sender, RoutedEventArgs evt) {
InputTextChanged((TextBox)sender, Validator.CheckDecimal);
}
protected void PartialDateInput_TextChanged(object sender, RoutedEventArgs evt) { protected void PartialDateInput_TextChanged(object sender, RoutedEventArgs evt) {
InputTextChanged((TextBox)sender, Validator.CheckPartialDate); InputTextChanged((TextBox)sender, Validator.CheckPartialDate);
} }
@ -461,6 +482,14 @@ namespace Elwig.Windows {
InputLostFocus((TextBox)sender, Validator.CheckDate); InputLostFocus((TextBox)sender, Validator.CheckDate);
} }
protected void TimeInput_TextChanged(object sender, RoutedEventArgs evt) {
InputTextChanged((TextBox)sender, Validator.CheckTime);
}
protected void TimeInput_LostFocus(object sender, RoutedEventArgs evt) {
InputLostFocus((TextBox)sender, Validator.CheckTime);
}
protected void PlzInput_TextChanged(object sender, RoutedEventArgs evt) { protected void PlzInput_TextChanged(object sender, RoutedEventArgs evt) {
var plz = (TextBox)sender; var plz = (TextBox)sender;
InputTextChanged(plz, Validator.CheckPlz); InputTextChanged(plz, Validator.CheckPlz);

View File

@ -6,7 +6,7 @@
xmlns:local="clr-namespace:Elwig.Windows" xmlns:local="clr-namespace:Elwig.Windows"
mc:Ignorable="d" mc:Ignorable="d"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
Title="Flächenbindugen - Elwig" Height="480" Width="850" Title="Flächenbindungen - Elwig" Height="480" Width="1100"
Loaded="Window_Loaded"> Loaded="Window_Loaded">
<Window.Resources> <Window.Resources>
<Style TargetType="Label"> <Style TargetType="Label">
@ -52,7 +52,7 @@
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
<ColumnDefinition Width="330"/> <ColumnDefinition Width="340"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Menu Grid.ColumnSpan="2" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White"> <Menu Grid.ColumnSpan="2" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White">
@ -74,13 +74,15 @@
<DataGridTextColumn Header="Katastralgemeinde" Binding="{Binding Kg.AtKg.Name}" Width="6*"/> <DataGridTextColumn Header="Katastralgemeinde" Binding="{Binding Kg.AtKg.Name}" Width="6*"/>
<DataGridTextColumn Header="Ried" Binding="{Binding Rd.Name}" Width="4*"/> <DataGridTextColumn Header="Ried" Binding="{Binding Rd.Name}" Width="4*"/>
<DataGridTextColumn Header="Parzelle" Binding="{Binding GstNr}" Width="4*"/> <DataGridTextColumn Header="Parzelle" Binding="{Binding GstNr}" Width="4*"/>
<DataGridTextColumn Header="Fläche" Binding="{Binding Area, StringFormat='{}{0:N0} m²'}" Width="4*"> <DataGridTextColumn Header="Fläche" Binding="{Binding Area, StringFormat='{}{0:N0} m²'}" Width="3*">
<DataGridTextColumn.CellStyle> <DataGridTextColumn.CellStyle>
<Style> <Style>
<Setter Property="TextBlock.TextAlignment" Value="Right"/> <Setter Property="TextBlock.TextAlignment" Value="Right"/>
</Style> </Style>
</DataGridTextColumn.CellStyle> </DataGridTextColumn.CellStyle>
</DataGridTextColumn> </DataGridTextColumn>
<DataGridTextColumn Header="Sorte" Binding="{Binding AreaComType.WineVar.Name}" Width="4*"/>
<DataGridTextColumn Header="Attribut" Binding="{Binding AreaComType.WineAttr1.Name}" Width="3*"/>
</DataGrid.Columns> </DataGrid.Columns>
</DataGrid> </DataGrid>
@ -131,6 +133,10 @@
<Label Content="Bewirt.-Art:" Margin="10,100,0,0" Grid.Column="0" Grid.ColumnSpan="2"/> <Label Content="Bewirt.-Art:" Margin="10,100,0,0" Grid.Column="0" Grid.ColumnSpan="2"/>
<ComboBox x:Name="WineCultivationInput" DisplayMemberPath="Name" TextSearch.TextPath="Name" <ComboBox x:Name="WineCultivationInput" DisplayMemberPath="Name" TextSearch.TextPath="Name"
HorizontalAlignment="Stretch" Margin="0,100,10,0" Grid.Column="1" Grid.ColumnSpan="3"/> HorizontalAlignment="Stretch" Margin="0,100,10,0" Grid.Column="1" Grid.ColumnSpan="3"/>
<Label Content="Anmerkung:" Margin="10,130,0,0" Grid.Column="0" Grid.ColumnSpan="2"/>
<TextBox x:Name="CommentInput" TextChanged="TextBox_TextChanged"
HorizontalAlignment="Stretch" Margin="0,130,10,0" Grid.Column="1" Grid.ColumnSpan="3"/>
</Grid> </Grid>
</GroupBox> </GroupBox>

View File

@ -40,11 +40,8 @@ namespace Elwig.Windows {
private async Task RefreshAreaCommitmentListQuery() { private async Task RefreshAreaCommitmentListQuery() {
List<AreaCom> areaComs = await Context.AreaCommitments.Where(a => a.MgNr == Member.MgNr).OrderBy(a => a.FbNr).ToListAsync(); List<AreaCom> areaComs = await Context.AreaCommitments.Where(a => a.MgNr == Member.MgNr).OrderBy(a => a.FbNr).ToListAsync();
ControlUtils.RenewItemsSource(AreaCommitmentList, areaComs, i => (i as AreaCom)?.FbNr,
ControlUtils.RenewItemsSource(AreaCommitmentList, areaComs, i => (i as AreaCom)?.FbNr); AreaCommitmentList_SelectionChanged, ControlUtils.RenewSourceDefault.None);
if (areaComs.Count == 1)
AreaCommitmentList.SelectedIndex = 0;
RefreshInputs(); RefreshInputs();
} }
@ -82,6 +79,8 @@ namespace Elwig.Windows {
AreaComTypeInput.SelectedItem = a.AreaComType; AreaComTypeInput.SelectedItem = a.AreaComType;
WineCultivationInput.SelectedItem = a.WineCult; WineCultivationInput.SelectedItem = a.WineCult;
CommentInput.Text = a.Comment;
FinishInputFilling(); FinishInputFilling();
} }
@ -96,8 +95,8 @@ namespace Elwig.Windows {
ValidateRequiredInputs(); ValidateRequiredInputs();
} }
protected override async Task RenewContext() { protected override async Task OnRenewContext() {
await base.RenewContext(); await base.OnRenewContext();
ControlUtils.RenewItemsSource(KgInput, await Context.WbKgs.Select(k => k.AtKg).OrderBy(k => k.Name).ToListAsync(), i => (i as AT_Kg)?.KgNr); ControlUtils.RenewItemsSource(KgInput, await Context.WbKgs.Select(k => k.AtKg).OrderBy(k => k.Name).ToListAsync(), i => (i as AT_Kg)?.KgNr);
ControlUtils.RenewItemsSource(AreaComTypeInput, await Context.AreaCommitmentTypes.OrderBy(v => v.VtrgId).ToListAsync(), i => (i as AreaComType)?.VtrgId); ControlUtils.RenewItemsSource(AreaComTypeInput, await Context.AreaCommitmentTypes.OrderBy(v => v.VtrgId).ToListAsync(), i => (i as AreaComType)?.VtrgId);
ControlUtils.RenewItemsSource(WineCultivationInput, await Context.WineCultivations.OrderBy(c => c.Name).ToListAsync(), i => (i as WineCult)?.CultId); ControlUtils.RenewItemsSource(WineCultivationInput, await Context.WineCultivations.OrderBy(c => c.Name).ToListAsync(), i => (i as WineCult)?.CultId);
@ -151,6 +150,7 @@ namespace Elwig.Windows {
a.Area = int.Parse(AreaInput.Text); a.Area = int.Parse(AreaInput.Text);
a.VtrgId = (AreaComTypeInput.SelectedItem as AreaComType)?.VtrgId; a.VtrgId = (AreaComTypeInput.SelectedItem as AreaComType)?.VtrgId;
a.CultId = (WineCultivationInput.SelectedItem as WineCult)?.CultId; a.CultId = (WineCultivationInput.SelectedItem as WineCult)?.CultId;
a.Comment = (CommentInput.Text == "") ? null : CommentInput.Text;
EntityEntry<AreaCom>? tr = null; EntityEntry<AreaCom>? tr = null;
try { try {

View File

@ -215,7 +215,45 @@
</TabItem> </TabItem>
<TabItem Header="Textelemente"> <TabItem Header="Textelemente">
<ScrollViewer VerticalScrollBarVisibility="Visible">
<StackPanel>
<GroupBox Header="Lieferschein" Margin="10,10,10,10" Height="180">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="370"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="TextElementDeliveryNote" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10,10,10,10" Height="Auto"
TextChanged="TextBox_TextChanged"/>
<GroupBox Grid.Column="1" Header="Tabelle zur Gesamtlieferung" Margin="10,10,10,10" VerticalAlignment="Center">
<StackPanel Margin="5,5,0,5">
<RadioButton GroupName="ModeDeliveryNote" x:Name="ModeDeliveryNoteNone" Content="Verbergen"
Checked="RadioButton_Changed" Unchecked="RadioButton_Changed"/>
<RadioButton GroupName="ModeDeliveryNote" x:Name="ModeDeliveryNoteGaOnly" Content="Nur Gesamtlieferung lt. Geschäftsanteilen anzeigen"
Checked="RadioButton_Changed" Unchecked="RadioButton_Changed"/>
<RadioButton GroupName="ModeDeliveryNote" x:Name="ModeDeliveryNoteShort" Content="Nur Gesamtlieferung und Flächenbindungen von auf dem&#xA;Lieferschein angeführten Sorten anzeigen"
Checked="RadioButton_Changed" Unchecked="RadioButton_Changed"/>
<RadioButton GroupName="ModeDeliveryNote" x:Name="ModeDeliveryNoteFull" Content="Vollständig anzeigen"
Checked="RadioButton_Changed" Unchecked="RadioButton_Changed"/>
</StackPanel>
</GroupBox>
</Grid>
</GroupBox>
<GroupBox Header="Anlieferungsbestätigung" Margin="10,10,10,10" Height="250">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="TextElementDeliveryConfirmation" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible" AcceptsReturn="True"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10,10,10,10" Height="Auto"
TextChanged="TextBox_TextChanged"/>
</Grid>
</GroupBox>
</StackPanel>
</ScrollViewer>
</TabItem> </TabItem>
</TabControl> </TabControl>

View File

@ -52,8 +52,8 @@ namespace Elwig.Windows {
FillInputs(App.Client); FillInputs(App.Client);
} }
protected override async Task RenewContext() { protected override async Task OnRenewContext() {
await base.RenewContext(); await base.OnRenewContext();
ControlUtils.RenewItemsSource(SeasonList, await Context.Seasons.OrderByDescending(s => s.Year).ToListAsync(), s => (s as Season)?.Year, null, ControlUtils.RenewSourceDefault.First); ControlUtils.RenewItemsSource(SeasonList, await Context.Seasons.OrderByDescending(s => s.Year).ToListAsync(), s => (s as Season)?.Year, null, ControlUtils.RenewSourceDefault.First);
var year = (SeasonList.SelectedItem as Season)?.Year; var year = (SeasonList.SelectedItem as Season)?.Year;
ControlUtils.RenewItemsSource(SeasonModifierList, await Context.Modifiers.Where(m => m.Year == year).OrderBy(m => m.Ordering).ToListAsync(), m => (m as Modifier)?.ModId); ControlUtils.RenewItemsSource(SeasonModifierList, await Context.Modifiers.Where(m => m.Year == year).OrderBy(m => m.Ordering).ToListAsync(), m => (m as Modifier)?.ModId);
@ -117,6 +117,7 @@ namespace Elwig.Windows {
await Context.SaveChangesAsync(); await Context.SaveChangesAsync();
foreach (var mod in ModifierList.Where(m => !ModifierIds.ContainsKey(m))) { foreach (var mod in ModifierList.Where(m => !ModifierIds.ContainsKey(m))) {
if (mod.ModId == null) continue;
await Context.AddAsync(mod); await Context.AddAsync(mod);
} }
await Context.SaveChangesAsync(); await Context.SaveChangesAsync();
@ -200,6 +201,15 @@ namespace Elwig.Windows {
ClientEmailAddressInput.Text = p.EmailAddress; ClientEmailAddressInput.Text = p.EmailAddress;
ClientWebsiteInput.Text = p.Website; ClientWebsiteInput.Text = p.Website;
TextElementDeliveryNote.Text = p.TextDeliveryNote;
switch (p.ModeDeliveryNoteStats) {
case 0: ModeDeliveryNoteNone.IsChecked = true; break;
case 1: ModeDeliveryNoteGaOnly.IsChecked = true; break;
case 2: ModeDeliveryNoteShort.IsChecked = true; break;
case 3: ModeDeliveryNoteFull.IsChecked = true; break;
}
TextElementDeliveryConfirmation.Text = p.TextDeliveryConfirmation;
FinishInputFilling(); FinishInputFilling();
} }
@ -212,7 +222,7 @@ namespace Elwig.Windows {
p.Address = ClientAddressInput.Text; p.Address = ClientAddressInput.Text;
p.Plz = int.Parse(ClientPlzInput.Text); p.Plz = int.Parse(ClientPlzInput.Text);
p.Ort = ClientOrtInput.Text; p.Ort = ClientOrtInput.Text;
p.Iban = ClientIbanInput.Text.Length > 0 ? ClientIbanInput.Text.Replace(" ", "") : null; p.Iban = ClientIbanInput.Text.Length > 0 ? ClientIbanInput.Text : null;
p.Bic = ClientBicInput.Text.Length > 0 ? ClientBicInput.Text : null; p.Bic = ClientBicInput.Text.Length > 0 ? ClientBicInput.Text : null;
p.UstIdNr = ClientUstIdNrInput.Text.Length > 0 ? ClientUstIdNrInput.Text : null; p.UstIdNr = ClientUstIdNrInput.Text.Length > 0 ? ClientUstIdNrInput.Text : null;
p.LfbisNr = ClientLfbisNrInput.Text.Length > 0 ? ClientLfbisNrInput.Text : null; p.LfbisNr = ClientLfbisNrInput.Text.Length > 0 ? ClientLfbisNrInput.Text : null;
@ -221,6 +231,10 @@ namespace Elwig.Windows {
p.EmailAddress = ClientEmailAddressInput.Text.Length > 0 ? ClientEmailAddressInput.Text : null; p.EmailAddress = ClientEmailAddressInput.Text.Length > 0 ? ClientEmailAddressInput.Text : null;
p.Website = ClientWebsiteInput.Text.Length > 0 ? ClientWebsiteInput.Text : null; p.Website = ClientWebsiteInput.Text.Length > 0 ? ClientWebsiteInput.Text : null;
p.TextDeliveryNote = TextElementDeliveryNote.Text.Length > 0 ? TextElementDeliveryNote.Text : null;
p.ModeDeliveryNoteStats = (ModeDeliveryNoteNone.IsChecked == true) ? 0 : (ModeDeliveryNoteGaOnly.IsChecked == true) ? 1 : (ModeDeliveryNoteShort.IsChecked == true) ? 2 : (ModeDeliveryNoteFull.IsChecked == true) ? 3 : 2;
p.TextDeliveryConfirmation = TextElementDeliveryConfirmation.Text.Length > 0 ? TextElementDeliveryConfirmation.Text : null;
await p.UpdateValues(); await p.UpdateValues();
} }
@ -321,6 +335,7 @@ namespace Elwig.Windows {
} }
private void SeasonModifierRelInput_TextChanged(object sender, TextChangedEventArgs evt) { private void SeasonModifierRelInput_TextChanged(object sender, TextChangedEventArgs evt) {
// DecimalInput_TextChanged(sender, evt); FIXME '-' is ignored
if ((!IsEditing && !IsCreating) || SeasonModifierList.SelectedItem is not Modifier mod) return; if ((!IsEditing && !IsCreating) || SeasonModifierList.SelectedItem is not Modifier mod) return;
ModifiersChanged = ModifiersChanged || (SeasonModifierRelInput.Text ?? "") != ((SeasonModifierList.SelectedItem as Modifier)?.Rel?.ToString() ?? ""); ModifiersChanged = ModifiersChanged || (SeasonModifierRelInput.Text ?? "") != ((SeasonModifierList.SelectedItem as Modifier)?.Rel?.ToString() ?? "");
if (ModifierUpdate) return; if (ModifierUpdate) return;
@ -331,11 +346,13 @@ namespace Elwig.Windows {
} }
private void SeasonModifierAbsInput_TextChanged(object sender, TextChangedEventArgs evt) { private void SeasonModifierAbsInput_TextChanged(object sender, TextChangedEventArgs evt) {
if ((!IsEditing && !IsCreating) || SeasonModifierList.SelectedItem is not Modifier mod) return; // DecimalInput_TextChanged(sender, evt); FIXME '-' is ignored
if ((!IsEditing && !IsCreating) || SeasonModifierList.SelectedItem is not Modifier mod || SeasonList.SelectedItem is not Season s) return;
ModifiersChanged = ModifiersChanged || (SeasonModifierAbsInput.Text ?? "") != ((SeasonModifierList.SelectedItem as Modifier)?.Abs?.ToString() ?? ""); ModifiersChanged = ModifiersChanged || (SeasonModifierAbsInput.Text ?? "") != ((SeasonModifierList.SelectedItem as Modifier)?.Abs?.ToString() ?? "");
if (ModifierUpdate) return; if (ModifierUpdate) return;
mod.Abs = decimal.TryParse(SeasonModifierAbsInput.Text, out var v) ? v : null; // FIXME ValueStr does not work in ModifierList when modifier is newly created
if (mod.Abs != null) SeasonModifierRelInput.Text = ""; mod.AbsValue = decimal.TryParse(SeasonModifierAbsInput.Text, out var v) ? Utils.DecToDb(v, s.Precision) : null;
if (mod.AbsValue != null) SeasonModifierRelInput.Text = "";
CollectionViewSource.GetDefaultView(ModifierList).Refresh(); CollectionViewSource.GetDefaultView(ModifierList).Refresh();
UpdateButtons(); UpdateButtons();
} }

View File

@ -1,21 +1,17 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Drawing; using System.Drawing;
using System.Linq; using System.Linq;
using System.Text.Json.Nodes;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input; using System.Windows.Input;
using Elwig.Helpers; using Elwig.Helpers;
using Elwig.Helpers.Billing;
using Elwig.Models; using Elwig.Models;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.ChangeTracking;
using Newtonsoft.Json.Linq;
using ScottPlot; using ScottPlot;
using ScottPlot.Plottable; using ScottPlot.Plottable;
@ -69,14 +65,14 @@ namespace Elwig.Windows {
} }
PaymentVar paymentVar = paymentVars[0]; PaymentVar paymentVar = paymentVars[0];
var data = JToken.Parse(paymentVar.Data); var data = JsonNode.Parse(paymentVar.Data).AsObject();
var auszahlungsSorten = data["AuszahlungSorten"]; var auszahlungsSorten = data["AuszahlungSorten"]?.AsObject();
if (auszahlungsSorten == null) { if (auszahlungsSorten == null) {
return; return;
} }
var Graphs = auszahlungsSorten["Kurven"]; var Graphs = auszahlungsSorten["Kurven"]?.AsArray();
if (Graphs == null) { if (Graphs == null) {
return; return;
@ -86,7 +82,7 @@ namespace Elwig.Windows {
int i = 1; int i = 1;
foreach (var graph in Graphs) { foreach (var graph in Graphs) {
GraphsList.Add(new Graph("Oe", i, graph, ParseContracts(auszahlungsSorten, i - 1), 50, 140)); GraphsList.Add(new Graph("Oe", i, graph?.AsObject(), ParseContracts(auszahlungsSorten, i - 1), 50, 140));
i++; i++;
} }
@ -98,17 +94,15 @@ namespace Elwig.Windows {
RefreshInputs(); RefreshInputs();
} }
private String ParseContracts(JToken auszahlungsSorten, int num) { private String ParseContracts(JsonObject auszahlungsSorten, int num) {
List<string> contracts = new(); List<string> contracts = new();
foreach (var sorte in auszahlungsSorten.Children().OfType<JToken>()) { foreach (var sorte in auszahlungsSorten) {
if (((JProperty)sorte).Name == "Kurven") { if (sorte.Key == "Kurven") continue;
continue; foreach (var attribut in sorte.Value.AsObject()) {
} foreach (var bindung in attribut.Value.AsObject()) {
foreach (var attribut in sorte.Values().OfType<JToken>()) { if ((int)bindung.Value.AsValue() == num) {
foreach (var bindung in attribut.Values().OfType<JProperty>()) { contracts.Add($"{sorte.Key}/{attribut.Key}/{bindung.Key}");
if ((int)(bindung).Value == num) {
contracts.Add($"{((JProperty)sorte).Name}/{((JProperty)attribut).Name}/{bindung.Name}");
} }
} }
} }
@ -125,15 +119,14 @@ namespace Elwig.Windows {
} }
PaymentVar paymentVar = paymentVars[0]; PaymentVar paymentVar = paymentVars[0];
var data = JToken.Parse(paymentVar.Data); var data = JsonNode.Parse(paymentVar.Data).AsObject();
var auszahlungsSorten = data["AuszahlungSorten"]; var auszahlungsSorten = data["AuszahlungSorten"]?.AsObject();
if (auszahlungsSorten == null) { if (auszahlungsSorten == null) {
return false; return false;
} }
var Graphs = auszahlungsSorten["Kurven"]; var Graphs = auszahlungsSorten["Kurven"]?.AsObject();
if (Graphs == null) { if (Graphs == null) {
return false; return false;
} }
@ -141,26 +134,24 @@ namespace Elwig.Windows {
int i = 1; int i = 1;
foreach (var graph in Graphs) { foreach (var graph in Graphs) {
if (i == num) { if (i == num) {
graph.Remove(); Graphs.Remove(graph.Key);
break; break;
} }
i++; i++;
} }
foreach (var sorte in auszahlungsSorten.Children().OfType<JToken>()) { foreach (var sorte in auszahlungsSorten) {
if (((JProperty)sorte).Name == "Kurven") { if (sorte.Key == "Kurven") continue;
continue; foreach (var attribut in sorte.Value.AsObject()) {
} var bindungen = attribut.Value.AsObject();
foreach (var attribut in sorte.Values().OfType<JToken>()) { foreach (var bindung in bindungen) {
List<JProperty> itemsToRemove = new(); int v = (int)bindung.Value;
foreach (var bindung in attribut.Values().OfType<JProperty>()) { if (v == num - 1) {
if ((int)bindung.Value == num - 1) { bindungen.Remove(bindung.Key);
itemsToRemove.Add(bindung); } else if (v > num - 1) {
} else if ((int)bindung.Value > num - 1) { bindungen[bindung.Key] = v - 1;
bindung.Value = (int)bindung.Value - 1;
} }
} }
itemsToRemove.ForEach(i => i.Remove());
} }
} }
@ -223,14 +214,15 @@ namespace Elwig.Windows {
FinishInputFilling(); FinishInputFilling();
} }
protected override async Task RenewContext() { protected override async Task OnRenewContext() {
await base.RenewContext(); await base.OnRenewContext();
await RefreshGraphList(); await RefreshGraphList();
} }
private void InitPlot() { private void InitPlot() {
OechslePricePlotScatter = OechslePricePlot.Plot.AddScatter(Graph.DataX, Graph.DataY); OechslePricePlotScatter = OechslePricePlot.Plot.AddScatter(Graph.DataX, Graph.DataY);
OechslePricePlot.Configuration.DoubleClickBenchmark = false;
OechslePricePlotScatter.LineColor = Color.Blue; OechslePricePlotScatter.LineColor = Color.Blue;
OechslePricePlotScatter.MarkerColor = Color.Blue; OechslePricePlotScatter.MarkerColor = Color.Blue;
OechslePricePlotScatter.MarkerSize = 9; OechslePricePlotScatter.MarkerSize = 9;
@ -398,7 +390,7 @@ namespace Elwig.Windows {
PriceInput.Text = Graph.DataY[PrimaryMarkedPointIndex].ToString(); PriceInput.Text = Graph.DataY[PrimaryMarkedPointIndex].ToString();
EnableActionButtons(); if (IsEditing || IsCreating) EnableActionButtons();
OechslePricePlot.Render(); OechslePricePlot.Render();
return; return;
} }
@ -430,6 +422,8 @@ namespace Elwig.Windows {
return; return;
} }
FlattenGraph(0, PrimaryMarkedPointIndex, Graph.DataY[PrimaryMarkedPointIndex]); FlattenGraph(0, PrimaryMarkedPointIndex, Graph.DataY[PrimaryMarkedPointIndex]);
SaveButton.IsEnabled = true;
ResetButton.IsEnabled = true;
} }
private void RightFlatButton_Click(object sender, RoutedEventArgs evt) { private void RightFlatButton_Click(object sender, RoutedEventArgs evt) {
@ -437,6 +431,8 @@ namespace Elwig.Windows {
return; return;
} }
FlattenGraph(PrimaryMarkedPointIndex, Graph.DataY.Length - 1, Graph.DataY[PrimaryMarkedPointIndex]); FlattenGraph(PrimaryMarkedPointIndex, Graph.DataY.Length - 1, Graph.DataY[PrimaryMarkedPointIndex]);
SaveButton.IsEnabled = true;
ResetButton.IsEnabled = true;
} }
private void InterpolateButton_Click(object sender, RoutedEventArgs evt) { private void InterpolateButton_Click(object sender, RoutedEventArgs evt) {
@ -451,6 +447,8 @@ namespace Elwig.Windows {
for (int i = lowIndex; i < highIndex - 1; i++) { for (int i = lowIndex; i < highIndex - 1; i++) {
Graph.DataY[i + 1] = Math.Round(Graph.DataY[i] + step, 4); // TODO richtig runden Graph.DataY[i + 1] = Math.Round(Graph.DataY[i] + step, 4); // TODO richtig runden
} }
SaveButton.IsEnabled = true;
ResetButton.IsEnabled = true;
} }
private void LinearIncreaseButton_Click(object sender, RoutedEventArgs e) { private void LinearIncreaseButton_Click(object sender, RoutedEventArgs e) {
@ -462,6 +460,8 @@ namespace Elwig.Windows {
return; return;
} }
LinearIncreaseGraph(PrimaryMarkedPointIndex, Graph.DataY.Length - 1, priceIncrease.Value); LinearIncreaseGraph(PrimaryMarkedPointIndex, Graph.DataY.Length - 1, priceIncrease.Value);
SaveButton.IsEnabled = true;
ResetButton.IsEnabled = true;
} }
private void OechslePricePlot_MouseDown(object sender, MouseEventArgs e) { private void OechslePricePlot_MouseDown(object sender, MouseEventArgs e) {
@ -662,23 +662,22 @@ namespace Elwig.Windows {
} }
PaymentVar paymentVar = paymentVars[0]; PaymentVar paymentVar = paymentVars[0];
var data = JToken.Parse(paymentVar.Data); var data = JsonNode.Parse(paymentVar.Data).AsObject();
var auszahlungsSorten = data["AuszahlungSorten"]; var auszahlungsSorten = data["AuszahlungSorten"];
if (auszahlungsSorten == null) { if (auszahlungsSorten == null) {
return null; return null;
} }
var Graphs = auszahlungsSorten["Kurven"]; var Graphs = auszahlungsSorten["Kurven"].AsArray();
if (Graphs == null) { if (Graphs == null) {
return null; return null;
} }
if (IsEditing) { if (IsEditing) {
((JArray)Graphs)[g.Num - 1] = g.ToJson(); Graphs[g.Num - 1] = g.ToJson();
} else if(IsCreating) { } else if(IsCreating) {
((JArray)Graphs).Add(g.ToJson()); Graphs.Add(g.ToJson());
} else { } else {
return null; return null;
} }

View File

@ -7,30 +7,32 @@ using System.Windows.Threading;
namespace Elwig.Windows { namespace Elwig.Windows {
public abstract class ContextWindow : Window { public abstract class ContextWindow : Window {
public static readonly int RenewSec = 10;
protected AppDbContext Context { get; private set; } protected AppDbContext Context { get; private set; }
protected bool LockContext { get; set; } = false; protected bool LockContext { get; set; } = false;
private readonly DispatcherTimer ContextRenewTimer; private readonly DispatcherTimer _timer;
private static readonly int ContextRenewSec = 10; private bool _renewPending = false;
public ContextWindow() : base() { public ContextWindow() : base() {
ContextRenewTimer = new DispatcherTimer(); _timer = new DispatcherTimer();
ContextRenewTimer.Tick += new EventHandler(OnRenewContext); _timer.Tick += new EventHandler(OnShouldRenewContext);
ContextRenewTimer.Interval = new TimeSpan(0, 0, ContextRenewSec); _timer.Interval = new TimeSpan(0, 0, RenewSec);
ContextRenewTimer.Start(); _timer.Start();
Context = new(); Context = new();
Loaded += OnLoaded; Loaded += OnLoaded;
} }
private void OnRenewContext(object? sender, EventArgs evt) { private async void OnShouldRenewContext(object? sender, EventArgs evt) {
if (LockContext || !Context.HasBackendChanged) return; if (!Context.HasBackendChanged) return;
Context.Dispose(); _renewPending = true;
Context = new(); if (LockContext) return;
RenewContext().GetAwaiter().GetResult(); await RenewContext();
} }
private void OnLoaded(object sender, RoutedEventArgs evt) { private void OnLoaded(object sender, RoutedEventArgs evt) {
RenewContext().GetAwaiter().GetResult(); OnRenewContext().GetAwaiter().GetResult();
} }
protected override void OnClosed(EventArgs evt) { protected override void OnClosed(EventArgs evt) {
@ -38,6 +40,14 @@ namespace Elwig.Windows {
Context.Dispose(); Context.Dispose();
} }
abstract protected Task RenewContext(); protected async Task RenewContext() {
if (!_renewPending) return;
Context.Dispose();
Context = new();
await OnRenewContext();
_renewPending = false;
}
abstract protected Task OnRenewContext();
} }
} }

View File

@ -5,7 +5,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Elwig.Windows" xmlns:local="clr-namespace:Elwig.Windows"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
Title="Lieferungsverwaltung - Elwig" Height="720" Width="1100" MinHeight="700" MinWidth="1000" Title="Lieferungen - Elwig" Height="720" Width="1100" MinHeight="720" MinWidth="1000"
Loaded="Window_Loaded"> Loaded="Window_Loaded">
<Window.Resources> <Window.Resources>
<Style TargetType="Label"> <Style TargetType="Label">
@ -43,37 +43,42 @@
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="19"/> <RowDefinition Height="19"/>
<RowDefinition Height="0.625*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
<RowDefinition Height="24"/> <RowDefinition Height="24"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" MinWidth="400"/> <ColumnDefinition Width="1*" MinWidth="400"/>
<ColumnDefinition Width="1*"/> <ColumnDefinition Width="5"/>
<ColumnDefinition Width="1*"/> <ColumnDefinition Width="2*" MinWidth="560"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Menu Grid.ColumnSpan="3" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White"> <Menu Grid.ColumnSpan="3" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White">
<MenuItem Header="Lieferung">
</MenuItem>
<MenuItem Header="Drucken"> <MenuItem Header="Drucken">
<MenuItem x:Name="Menu_Print_ShowDeliveryNote" Header="Lieferschein anzeigen" IsEnabled="False" <MenuItem x:Name="Menu_Print_ShowDeliveryNote" Header="Lieferschein anzeigen" IsEnabled="False"
Click="Menu_Print_ShowDeliveryNote_Click"/> Click="Menu_Print_ShowDeliveryNote_Click"/>
<MenuItem x:Name="Menu_Print_PrintDeliveryNote" Header="Lieferschein drucken" IsEnabled="False" <MenuItem x:Name="Menu_Print_PrintDeliveryNote" Header="Lieferschein drucken" IsEnabled="False"
Click="Menu_Print_PrintDeliveryNote_Click"/> Click="Menu_Print_PrintDeliveryNote_Click"/>
<MenuItem x:Name="Menu_Print_DeliveryJournal" Header="Lieferjournal">
<MenuItem x:Name="Menu_Print_DeliveryJournal_ShowToday" Header="von heute anzeigen" IsEnabled="False" Tag="Print"
Click="Menu_Print_DeliveryJournal_ShowToday_Click"/>
<MenuItem x:Name="Menu_Print_DeliveryJournal_PrintToday" Header="von heute drucken" IsEnabled="False" Tag="Print"
Click="Menu_Print_DeliveryJournal_PrintToday_Click"/>
<MenuItem x:Name="Menu_Print_DeliveryJournal_ShowFilter" Header="aus Filtern anzeigen" IsEnabled="False" Tag="Print"
Click="Menu_Print_DeliveryJournal_ShowFilter_Click"/>
<MenuItem x:Name="Menu_Print_DeliveryJournal_PrintFilter" Header="aus Filtern drucken" IsEnabled="False" Tag="Print"
Click="Menu_Print_DeliveryJournal_PrintFilter_Click"/>
</MenuItem>
</MenuItem> </MenuItem>
<MenuItem Header="Exportieren"> <MenuItem Header="Exportieren">
<MenuItem x:Name="Menu_Export_Bki" Header="Traubentransportscheinliste (BKI)" <MenuItem x:Name="Menu_Export_Bki" Header="Traubentransportscheinliste (BKI)"/>
Click="Menu_Export_Bki_Click"/>
</MenuItem> </MenuItem>
<MenuItem Header="Werkzeuge"> <MenuItem Header="Einstellungen">
<MenuItem Header="Alle Lieferscheine überprüfen"/> <MenuItem x:Name="Menu_Settings_EnableFreeEditing" Header="Freie Bearbeitung aktivieren"
IsCheckable="True" Checked="Menu_Settings_EnableFreeEditing_Checked" Unchecked="Menu_Settings_EnableFreeEditing_Unchecked"/>
</MenuItem> </MenuItem>
</Menu> </Menu>
<Grid Grid.RowSpan="4" Grid.Row="1" Margin="5,0,5,0"> <Grid Grid.Row="1" Margin="5,0,0,0">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="42"/> <RowDefinition Height="42"/>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
@ -86,7 +91,8 @@
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<TextBox x:Name="SearchInput" Grid.ColumnSpan="3" Margin="5,10,161,0" IsReadOnly="False" <TextBox x:Name="SearchInput" Grid.ColumnSpan="3" Margin="5,10,161,0" IsReadOnly="False"
TextChanged="SearchInput_TextChanged"/> TextChanged="SearchInput_TextChanged"
ToolTip="Lieferungen filtern und durchsuchen. Die Filter sind beliebig kombinierbar.&#xA;&#xA;Filtern nach:&#xA;Sorte: z.B. GV, ZW, rr, sa, !gv (ausgenommen GV), ...&#xA;Qualitätsstufe: z.B. QUW, kab, ldw, ...&#xA;Gradation: z.B. &gt;73, &lt;15, 17-18, 15-, &gt;17,5, 62-75, ...&#xA;Mitglied: z.B. 1234, 987, ...&#xA;Saison: z.B. 2020, &gt;2015, 2017-2019, &lt;2005, 2019-, ...&#xA;Zweigstelle: z.B. musterort, ...&#xA;Attribute: z.B. kabinett, !kabinett (alle außer kabinett), ...&#xA;Datum: z.B. 1.9., 15.9.-10.10., -15.10.2020, ...&#xA;Uhrzeit: z.B. 06:00-08:00, 18:00-, ...&#xA;Freitext: z.B. Lieferscheinnummern, &quot;quw&quot; (sucht nach dem Text &quot;quw&quot;)"/>
<xctk:IntegerUpDown Name="SeasonInput" Grid.ColumnSpan="3" Height="25" Width="56" FontSize="14" Minimum="1000" Maximum="9999" <xctk:IntegerUpDown Name="SeasonInput" Grid.ColumnSpan="3" Height="25" Width="56" FontSize="14" Minimum="1000" Maximum="9999"
Margin="0,10,100,0" VerticalAlignment="Top" HorizontalAlignment="Right" Margin="0,10,100,0" VerticalAlignment="Top" HorizontalAlignment="Right"
ValueChanged="SeasonInput_ValueChanged"/> ValueChanged="SeasonInput_ValueChanged"/>
@ -101,17 +107,50 @@
CanUserDeleteRows="False" CanUserResizeRows="False" CanUserAddRows="False" CanUserDeleteRows="False" CanUserResizeRows="False" CanUserAddRows="False"
Margin="5,0,5,0" Grid.Row="1" FontSize="14" Grid.ColumnSpan="3"> Margin="5,0,5,0" Grid.Row="1" FontSize="14" Grid.ColumnSpan="3">
<DataGrid.Columns> <DataGrid.Columns>
<DataGridTextColumn Header="MgNr." Binding="{Binding MgNr}" Width="70"/> <DataGridTextColumn Header="MgNr." Binding="{Binding MgNr, StringFormat='{}{0} '}" Width="50">
<DataGridTextColumn Header="Datum" Binding="{Binding Date, StringFormat='dd.MM.yy'}" Width="70"/>
<DataGridTextColumn Header="Zeit" Binding="{Binding Time, StringFormat='HH:mm'}" Width="70"/>
<DataGridTextColumn Header="Sorte" Binding="{Binding SortIdString}" Width="60"/>
<DataGridTextColumn Header="Gewicht" Binding="{Binding Weight, StringFormat='{}{0:N0} kg'}" Width="70">
<DataGridTextColumn.CellStyle> <DataGridTextColumn.CellStyle>
<Style> <Style>
<Setter Property="TextBlock.TextAlignment" Value="Right"/> <Setter Property="TextBlock.TextAlignment" Value="Right"/>
</Style> </Style>
</DataGridTextColumn.CellStyle> </DataGridTextColumn.CellStyle>
</DataGridTextColumn> </DataGridTextColumn>
<DataGridTextColumn Header="Datum" Binding="{Binding Date, StringFormat='dd.MM.yy'}" Width="70">
<DataGridTextColumn.CellStyle>
<Style>
<Setter Property="TextBlock.TextAlignment" Value="Center"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Zeit" Binding="{Binding Time, StringFormat='HH:mm'}" Width="50">
<DataGridTextColumn.CellStyle>
<Style>
<Setter Property="TextBlock.TextAlignment" Value="Center"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Sorte" Binding="{Binding SortIdString}" Width="50">
<DataGridTextColumn.CellStyle>
<Style>
<Setter Property="TextBlock.TextAlignment" Value="Center"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Gewicht" Binding="{Binding Weight, StringFormat='{}{0:N0} kg '}" Width="75">
<DataGridTextColumn.CellStyle>
<Style>
<Setter Property="TextBlock.TextAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Gradation" Binding="{Binding Kmw, StringFormat='{}{0:N1}° '}" Width="50">
<DataGridTextColumn.CellStyle>
<Style>
<Setter Property="TextBlock.TextAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="LsNr." Binding="{Binding LsNr}" Width="120"/>
<DataGridTextColumn Header="Mitglied" Binding="{Binding Member.AdministrativeName}" Width="180"/>
</DataGrid.Columns> </DataGrid.Columns>
</DataGrid> </DataGrid>
@ -152,238 +191,260 @@
Click="CancelButton_Click"/> Click="CancelButton_Click"/>
</Grid> </Grid>
<GroupBox Header="Mitglied" Grid.Column="1" Grid.Row="1" Margin="5,5,5,5"> <GridSplitter Grid.Column="1" Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="Mitglied:" Margin="10,10,0,0" Grid.Column="0"/> <Grid Grid.Row="1" Grid.Column="2">
<TextBox x:Name="MgNrInput" Width="48" Grid.Row="1" Grid.Column="1" Margin="0,10,0,0" HorizontalAlignment="Left" TextAlignment="Right" <Grid.RowDefinitions>
TextChanged="MgNrInput_TextChanged" LostFocus="MgNrInput_LostFocus"/> <RowDefinition Height="0.625*"/>
<ComboBox x:Name="MemberInput" Grid.Column="1" Margin="53,10,10,10" IsEditable="True" <RowDefinition Height="*"/>
ItemTemplate="{StaticResource MemberAdminNameTemplate}" TextSearch.TextPath="AdministrativeName" <RowDefinition Height="*"/>
SelectionChanged="MemberInput_SelectionChanged"/> <RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="Wohnort:" Margin="10,38,0,0" Grid.Column="0"/> <GroupBox Header="Mitglied" Grid.Column="0" Grid.Row="0" Margin="5,5,5,5">
<TextBox x:Name="MemberAddressField" Grid.Column="1" Margin="0,40,10,10" FontSize="12" Height="22" <Grid>
IsReadOnly="True" IsTabStop="False"/> <Grid.ColumnDefinitions>
</Grid> <ColumnDefinition Width="70"/>
</GroupBox> <ColumnDefinition/>
</Grid.ColumnDefinitions>
<GroupBox Header="Lieferung" Grid.Column="1" Grid.Row="2" Margin="5,5,5,5"> <Label Content="Mitglied:" Margin="10,10,0,0" Grid.Column="0"/>
<Grid> <TextBox x:Name="MgNrInput" Width="48" Grid.Row="1" Grid.Column="1" Margin="0,10,0,0" HorizontalAlignment="Left" TextAlignment="Right"
<Grid.ColumnDefinitions> TextChanged="MgNrInput_TextChanged" LostFocus="MgNrInput_LostFocus" KeyUp="Input_KeyUp"/>
<ColumnDefinition Width="100"/> <ComboBox x:Name="MemberInput" Grid.Column="1" Margin="53,10,10,10" IsEditable="True"
<ColumnDefinition/> ItemTemplate="{StaticResource MemberAdminNameTemplate}" TextSearch.TextPath="AdministrativeName"
</Grid.ColumnDefinitions> SelectionChanged="MemberInput_SelectionChanged" KeyUp="Input_KeyUp"/>
<Label Content="LieferscheinNr.:" Margin="10,10,0,0" Grid.Column="0"/> <Label Content="Wohnort:" Margin="10,38,0,0" Grid.Column="0"/>
<TextBox x:Name="LsNrInput" Width="126" Grid.Column="1" HorizontalAlignment="Left" Margin="0,10,0,0" <TextBox x:Name="MemberAddressField" Grid.Column="1" Margin="0,40,10,10" FontSize="12" Height="22"
IsReadOnly="True" IsTabStop="False"/> IsReadOnly="True" IsTabStop="False"/>
<Label Content="Datum/Uhrzeit:" Margin="10,40,0,0" Grid.Column="0"/>
<TextBox x:Name="DateInput" Width="77" Grid.Column="1" HorizontalAlignment="Left" Margin="0,40,0,0"
IsReadOnly="True" IsTabStop="False"
TextChanged="DateInput_TextChanged"/>
<TextBox x:Name="TimeInput" Width="44" Grid.Column="1" HorizontalAlignment="Left" Margin="82,40,0,0"
IsReadOnly="True" IsTabStop="False"/>
<Label Content="Zweigstelle:" Margin="10,70,0,0" Grid.Column="0"/>
<ComboBox x:Name="BranchInput" Width="126" Margin="0,70,10,0" Grid.Column="1" HorizontalAlignment="Left"
IsEnabled="False"
DisplayMemberPath="Name" TextSearch.TextPath="Name"/>
<Label Content="Anmerkung:" Margin="10,100,0,10"/>
<TextBox x:Name="CommentInput" Grid.Column="1" Margin="0,100,10,10"
TextChanged="TextBox_TextChanged"/>
</Grid>
</GroupBox>
<GroupBox Header="Sorte" Grid.Column="2" Grid.Row="1" Margin="5,5,5,5">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="Sorte:" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="SortIdInput" Width="36" Grid.Row="1" Grid.Column="1" Margin="0,10,0,0" HorizontalAlignment="Left"
TextChanged="SortIdInput_TextChanged" LostFocus="SortIdInput_LostFocus"/>
<ComboBox x:Name="WineVarietyInput" Grid.Row="1" Grid.Column="1" Margin="41,10,10,10"
ItemTemplate="{StaticResource WineVarietyTemplate}" TextSearch.TextPath="Name"
SelectionChanged="WineVarietyInput_SelectionChanged"/>
<Label Content="Attribute:" Margin="10,40,0,0" Grid.Column="0"/>
<xctk:CheckComboBox x:Name="AttributesInput" Grid.Row="1" Grid.Column="1" Margin="0,40,10,10"
DisplayMemberPath="Name" Delimiter=", " AllItemsSelectedContent="Alle"
ItemSelectionChanged="AttributesInput_SelectionChanged"/>
</Grid>
</GroupBox>
<GroupBox Header="Gradation" Grid.Column="2" Grid.Row="2" Margin="5,5,5,5">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="Gradation:" Margin="10,10,10,10"/>
<Grid Grid.Column="1" Width="54" Height="25" Margin="0,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top">
<TextBox x:Name="GradationOeInput" TextAlignment="Right" Padding="2,2,23,2"
TextChanged="GradationOeInput_TextChanged" LostFocus="GradationOeInput_LostFocus"/>
<Label Content="°Oe" Margin="0,4,3,0" HorizontalAlignment="Right" FontSize="10"/>
</Grid> </Grid>
<Label Content="=" Margin="60,10,10,10" Grid.Column="1"/> </GroupBox>
<Grid Grid.Column="1" Width="68" Height="25" Margin="78,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top">
<TextBox x:Name="GradationKmwInput" TextAlignment="Right" Padding="2,2,34,2" SnapsToDevicePixels="True" <GroupBox Header="Lieferung" Grid.Column="0" Grid.Row="1" Margin="5,5,5,5">
TextChanged="GradationKmwInput_TextChanged" LostFocus="GradationKmwInput_LostFocus"/> <Grid>
<Label Content="°KMW" Margin="0,4,3,0" HorizontalAlignment="Right" FontSize="10"/> <Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="LieferscheinNr.:" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="LsNrInput" Width="126" Grid.Column="1" HorizontalAlignment="Left" Margin="0,10,0,0"
IsReadOnly="True" IsTabStop="False"
TextChanged="TextBox_TextChanged"/>
<Label Content="Datum/Uhrzeit:" Margin="10,40,0,0" Grid.Column="0"/>
<TextBox x:Name="DateInput" Width="77" Grid.Column="1" HorizontalAlignment="Left" Margin="0,40,0,0"
IsReadOnly="True"
TextChanged="DateInput_TextChanged" LostFocus="DateInput_LostFocus"/>
<TextBox x:Name="TimeInput" Width="44" Grid.Column="1" HorizontalAlignment="Left" Margin="82,40,0,0"
IsReadOnly="True"
TextChanged="TimeInput_TextChanged" LostFocus="TimeInput_LostFocus"/>
<Label Content="Zweigstelle:" Margin="10,70,0,0" Grid.Column="0"/>
<ComboBox x:Name="BranchInput" Width="126" Margin="0,70,10,0" Grid.Column="1" HorizontalAlignment="Left"
IsEnabled="False"
SelectionChanged="BranchInput_SelectionChanged"
DisplayMemberPath="Name" TextSearch.TextPath="Name"/>
<Label Content="Anmerkung:" Margin="10,100,0,10"/>
<TextBox x:Name="CommentInput" Grid.Column="1" Margin="0,100,10,10"
TextChanged="TextBox_TextChanged"/>
</Grid> </Grid>
</GroupBox>
<Label Content="Qualitätsstufe:" Margin="10,40,10,10"/> <GroupBox Header="Sorte" Grid.Column="1" Grid.Row="0" Margin="5,5,5,5">
<ComboBox x:Name="WineQualityLevelInput" Width="146" Margin="0,40,10,10" Grid.Column="1" HorizontalAlignment="Left" <Grid>
ItemTemplate="{StaticResource WineQualityLevelTemplate}" <Grid.ColumnDefinitions>
SelectionChanged="WineQualityLevelInput_SelectionChanged"/> <ColumnDefinition Width="70"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<CheckBox x:Name="AbgewertetInput" Content="Abgewertet" IsEnabled="False" <Label Content="Sorte:" Margin="10,10,0,0" Grid.Column="0"/>
VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,75,10,10" Grid.Column="1"/> <TextBox x:Name="SortIdInput" Width="36" Grid.Row="1" Grid.Column="1" Margin="0,10,0,0" HorizontalAlignment="Left"
</Grid> TextChanged="SortIdInput_TextChanged" LostFocus="SortIdInput_LostFocus" KeyUp="Input_KeyUp"/>
</GroupBox> <ComboBox x:Name="WineVarietyInput" Grid.Row="1" Grid.Column="1" Margin="41,10,10,10"
ItemTemplate="{StaticResource WineVarietyTemplate}" TextSearch.TextPath="Name"
SelectionChanged="WineVarietyInput_SelectionChanged" KeyUp="Input_KeyUp"/>
<GroupBox Header="Gewicht" Grid.Column="2" Grid.Row="3" Margin="5,5,5,5"> <Label Content="Attribute:" Margin="10,40,0,0" Grid.Column="0"/>
<Grid> <xctk:CheckComboBox x:Name="AttributesInput" Grid.Row="1" Grid.Column="1" Margin="0,40,10,10"
<Grid.ColumnDefinitions> DisplayMemberPath="Name" Delimiter=", " AllItemsSelectedContent="Alle"
<ColumnDefinition Width="70"/> ItemSelectionChanged="AttributesInput_SelectionChanged" KeyUp="Input_KeyUp"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="Gewicht:" Margin="10,10,10,10"/>
<Grid Grid.Column="1" Width="70" Height="25" Margin="0,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top">
<TextBox x:Name="WeightInput" TextAlignment="Right" Padding="2,2,17,2" IsReadOnly="True"
TextChanged="WeightInput_TextChanged"/>
<Label Content="kg" Margin="0,4,3,0" HorizontalAlignment="Right" FontSize="10"/>
</Grid> </Grid>
</GroupBox>
<CheckBox x:Name="ManualWeighingInput" Content="Handwiegung" IsEnabled="False" <GroupBox Header="Gradation" Grid.Column="1" Grid.Row="1" Margin="5,5,5,5">
VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,45,10,10" Grid.Column="0" Grid.ColumnSpan="2" <Grid>
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/> <Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="Gradation:" Margin="10,10,10,10"/>
<Grid Grid.Column="1" Width="54" Height="25" Margin="0,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top">
<TextBox x:Name="GradationOeInput" TextAlignment="Right" Padding="2,2,23,2"
TextChanged="GradationOeInput_TextChanged" LostFocus="GradationOeInput_LostFocus" KeyUp="Input_KeyUp"/>
<Label Content="°Oe" Margin="0,4,3,0" HorizontalAlignment="Right" FontSize="10"/>
</Grid>
<Label Content="=" Margin="60,10,10,10" Grid.Column="1"/>
<Grid Grid.Column="1" Width="68" Height="25" Margin="78,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top">
<TextBox x:Name="GradationKmwInput" TextAlignment="Right" Padding="2,2,34,2" SnapsToDevicePixels="True"
TextChanged="GradationKmwInput_TextChanged" LostFocus="GradationKmwInput_LostFocus" KeyUp="Input_KeyUp"/>
<Label Content="°KMW" Margin="0,4,3,0" HorizontalAlignment="Right" FontSize="10"/>
</Grid>
<CheckBox x:Name="GerebeltGewogenInput" Content="Gerebelt gewogen" <Label Content="Qualitätsstufe:" Margin="10,40,10,10"/>
VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,75,10,10" Grid.Column="0" Grid.ColumnSpan="2" <ComboBox x:Name="WineQualityLevelInput" Width="146" Margin="0,40,10,10" Grid.Column="1" HorizontalAlignment="Left"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/> ItemTemplate="{StaticResource WineQualityLevelTemplate}"
SelectionChanged="WineQualityLevelInput_SelectionChanged" KeyUp="Input_KeyUp"/>
<Button x:Name="WeighingManualButton" Content="Handwiegung" Width="120" <CheckBox x:Name="AbgewertetInput" Content="Abgewertet" IsEnabled="False"
Click="WeighingManualButton_Click" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,75,10,10" Grid.Column="0" Grid.ColumnSpan="2"/>
VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,10,10,10" Grid.Column="2"/>
<Button x:Name="WeighingAButton" Content="Wiegen A" Width="120"
Click="WeighingButton_Click"
VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,42,10,10" Grid.Column="2"/>
<Button x:Name="WeighingBButton" Content="Wiegen B" Width="120"
Click="WeighingButton_Click"
VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,74,10,10" Grid.Column="2"/>
<Button x:Name="WeighingCButton" Content="Wiegen C" Width="120"
Click="WeighingButton_Click"
VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,106,10,10" Grid.Column="2"/>
<Button x:Name="WeighingDButton" Content="Wiegen D" Width="120"
Click="WeighingButton_Click"
VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,138,10,10" Grid.Column="2"/>
</Grid>
</GroupBox>
<GroupBox Header="Sonstiges" Grid.Column="2" Grid.Row="4" Margin="5,5,5,10">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="65"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="Zu-/Abschläge:" Margin="10,10,0,10"/>
<xctk:CheckComboBox x:Name="ModifiersInput" Margin="0,10,10,10" Grid.Column="1" Grid.ColumnSpan="2"
ItemTemplate="{StaticResource ModifierTemplate}" Delimiter=", " AllItemsSelectedContent="Alle"
ItemSelectionChanged="ModifiersInput_SelectionChanged"/>
<Label Content="Anmerkung:" Margin="10,40,0,10"/>
<TextBox x:Name="PartCommentInput" Grid.Column="1" Margin="0,40,10,10" Grid.ColumnSpan="2"
TextChanged="TextBox_TextChanged"/>
<Label Content="Temperatur:" Margin="10,70,0,10"/>
<Grid Grid.Column="1" Height="25" Margin="0,70,10,10" VerticalAlignment="Top">
<TextBox x:Name="TemperatureInput" TextAlignment="Right" Padding="2,2,16,2"
TextChanged="TemperatureAcidInput_TextChanged" LostFocus="TemperatureAcidInput_LostFocus"/>
<Label Content="°C" Margin="0,4,3,0" HorizontalAlignment="Right" FontSize="10"/>
</Grid> </Grid>
</GroupBox>
<Label Content="Säure:" Margin="10,100,0,10"/> <GroupBox Header="Gewicht" Grid.Column="1" Grid.Row="2" Margin="5,5,5,5">
<Grid Grid.Column="1" Height="25" Margin="0,100,10,10" VerticalAlignment="Top"> <Grid>
<TextBox x:Name="AcidInput" TextAlignment="Right" Padding="2,2,19,2" <Grid.ColumnDefinitions>
TextChanged="TemperatureAcidInput_TextChanged" LostFocus="TemperatureAcidInput_LostFocus"/> <ColumnDefinition Width="70"/>
<Label Content="g/l" Margin="0,4,3,0" HorizontalAlignment="Right" FontSize="10"/> <ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="Gewicht:" Margin="10,10,10,10"/>
<Grid Grid.Column="1" Width="70" Height="25" Margin="0,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top">
<TextBox x:Name="WeightInput" TextAlignment="Right" Padding="2,2,17,2" IsReadOnly="True"
TextChanged="WeightInput_TextChanged"/>
<Label Content="kg" Margin="0,4,3,0" HorizontalAlignment="Right" FontSize="10"/>
</Grid>
<CheckBox x:Name="ManualWeighingInput" Content="Handwiegung" IsEnabled="False"
VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,45,10,10" Grid.Column="0" Grid.ColumnSpan="2"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/>
<CheckBox x:Name="GerebeltGewogenInput" Content="Gerebelt gewogen"
VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,75,10,10" Grid.Column="0" Grid.ColumnSpan="2"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/>
<Button x:Name="WeighingAButton" Content="Wiegen A" Width="120"
Click="WeighingButton_Click"
VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,10,10,10" Grid.Column="2"/>
<Button x:Name="WeighingBButton" Content="Wiegen B" Width="120"
Click="WeighingButton_Click"
VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,42,10,10" Grid.Column="2"/>
<Button x:Name="WeighingCButton" Content="Wiegen C" Width="120"
Click="WeighingButton_Click"
VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,74,10,10" Grid.Column="2"/>
<Button x:Name="WeighingDButton" Content="Wiegen D" Width="120"
Click="WeighingButton_Click"
VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,106,10,10" Grid.Column="2"/>
<Button x:Name="WeighingManualButton" Content="Handwiegung" Width="120"
Click="WeighingManualButton_Click"
VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,138,10,10" Grid.Column="2"/>
</Grid> </Grid>
</GroupBox>
<CheckBox x:Name="LesewagenInput" Content="Lesewagen" Margin="10,75,0,0" Grid.Column="2" <GroupBox Header="Sonstiges" Grid.Column="1" Grid.Row="3" Margin="5,5,5,10">
VerticalAlignment="Top" HorizontalAlignment="Left" <Grid>
Checked="LesewagenInput_Changed" Unchecked="LesewagenInput_Changed"/> <Grid.ColumnDefinitions>
<CheckBox x:Name="HandPickedInput" Content="Handlese" Margin="10,105,0,0" Grid.Column="2" IsThreeState="True" <ColumnDefinition Width="100"/>
VerticalAlignment="Top" HorizontalAlignment="Left" <ColumnDefinition Width="65"/>
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/> <ColumnDefinition/>
</Grid> </Grid.ColumnDefinitions>
</GroupBox>
<GroupBox Header="Teillieferungen" Grid.Column="1" Grid.Row="3" Margin="5,5,5,5"> <Label Content="Zu-/Abschläge:" Margin="10,10,0,10"/>
<Grid> <xctk:CheckComboBox x:Name="ModifiersInput" Margin="0,10,10,10" Grid.Column="1" Grid.ColumnSpan="2"
<Grid.ColumnDefinitions> ItemTemplate="{StaticResource ModifierTemplate}" Delimiter=", " AllItemsSelectedContent="Alle"
<ColumnDefinition/> ItemSelectionChanged="ModifiersInput_SelectionChanged"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListBox x:Name="DeliveryPartList" Margin="5,5,5,38" Grid.ColumnSpan="2"
SelectionChanged="DeliveryPartList_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding DPNr}" Width="20"/>
<TextBlock Text="{Binding SortId}" Width="30"/>
<TextBlock Text="{Binding Kmw, StringFormat='{}{0:0.0}°'}" Width="40" TextAlignment="Right" Padding="0,0,10,0"/>
<TextBlock Text="{Binding QualId}" Width="30"/>
<TextBlock Text="{Binding Weight, StringFormat='{}{0:N0} kg'}" Width="60" TextAlignment="Right"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button x:Name="ExtractDeliveryPartButton" Content="Extrahieren" IsEnabled="False" <Label Content="Anmerkung:" Margin="10,40,0,10"/>
ToolTip="Ausgewählte Teillieferung aus aktueller Lieferung entfernen und entweder anderer oder neuer Lieferung zuordnen" <TextBox x:Name="PartCommentInput" Grid.Column="1" Margin="0,40,10,10" Grid.ColumnSpan="2"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,10,2.5,5" Grid.Column="0" Grid.Row="2" TextChanged="TextBox_TextChanged"/>
Click="ExtractDeliveryPartButton_Click"/>
<Button x:Name="DeleteDeliveryPartButton" Content="Löschen" IsEnabled="False"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,10,5,5" Grid.Column="1" Grid.Row="2"
Click="DeleteDeliveryPartButton_Click"/>
</Grid>
</GroupBox>
<GroupBox Header="Herkunft" Grid.Column="1" Grid.Row="4" Margin="5,5,5,10"> <Label Content="Temperatur:" Margin="10,70,0,10"/>
<Grid> <Grid Grid.Column="1" Height="25" Margin="0,70,10,10" VerticalAlignment="Top">
<Grid.ColumnDefinitions> <TextBox x:Name="TemperatureInput" TextAlignment="Right" Padding="2,2,16,2"
<ColumnDefinition Width="100"/> TextChanged="TemperatureAcidInput_TextChanged" LostFocus="TemperatureAcidInput_LostFocus"/>
<ColumnDefinition/> <Label Content="°C" Margin="0,4,3,0" HorizontalAlignment="Right" FontSize="10"/>
</Grid.ColumnDefinitions> </Grid>
<Label Content="Weinbaugebiet:" Margin="10,10,0,10" Grid.Column="0"/> <Label Content="Säure:" Margin="10,100,0,10"/>
<ComboBox x:Name="WineOriginInput" Margin="0,10,10,10" Grid.Column="1" <Grid Grid.Column="1" Height="25" Margin="0,100,10,10" VerticalAlignment="Top">
ItemTemplate="{StaticResource WineOriginTemplate}"/> <TextBox x:Name="AcidInput" TextAlignment="Right" Padding="2,2,19,2"
TextChanged="TemperatureAcidInput_TextChanged" LostFocus="TemperatureAcidInput_LostFocus"/>
<Label Content="g/l" Margin="0,4,3,0" HorizontalAlignment="Right" FontSize="10"/>
</Grid>
<Label Content="Weinbau-KG:" Margin="10,40,0,10" Grid.Column="0"/> <CheckBox x:Name="LesewagenInput" Content="Lesewagen" Margin="10,75,0,0" Grid.Column="2"
<ComboBox x:Name="WineKgInput" Margin="0,40,10,10" Grid.Column="1" VerticalAlignment="Top" HorizontalAlignment="Left"
DisplayMemberPath="Name" Checked="LesewagenInput_Changed" Unchecked="LesewagenInput_Changed"/>
SelectionChanged="WineKgInput_SelectionChanged"/> <CheckBox x:Name="HandPickedInput" Content="Handlese" Margin="10,105,0,0" Grid.Column="2" IsThreeState="True"
VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/>
</Grid>
</GroupBox>
<Label Content="Ried:" Margin="10,70,0,10" Grid.Column="0"/> <GroupBox Header="Teillieferungen" Grid.Column="0" Grid.Row="2" Margin="5,5,5,5">
<ComboBox x:Name="WineRdInput" Margin="0,70,10,10" Grid.Column="1" <Grid>
DisplayMemberPath="Name"/> <Grid.ColumnDefinitions>
</Grid> <ColumnDefinition/>
</GroupBox> <ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListBox x:Name="DeliveryPartList" Margin="5,5,5,38" Grid.ColumnSpan="2"
SelectionChanged="DeliveryPartList_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding DPNr}" Width="20"/>
<TextBlock Text="{Binding SortId}" Width="30"/>
<TextBlock Text="{Binding Kmw, StringFormat='{}{0:0.0}°'}" Width="40" TextAlignment="Right" Padding="0,0,10,0"/>
<TextBlock Text="{Binding QualId}" Width="30"/>
<TextBlock Text="{Binding Weight, StringFormat='{}{0:N0} kg'}" Width="60" TextAlignment="Right" Padding="0,0,10,0"/>
<TextBlock Text="{Binding AttributesString}" Width="100"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button x:Name="ExtractDeliveryPartButton" Content="Extrahieren" IsEnabled="False"
ToolTip="Ausgewählte Teillieferung aus aktueller Lieferung entfernen und entweder anderer oder neuer Lieferung zuordnen"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,10,2.5,5" Grid.Column="0" Grid.Row="2"
Click="ExtractDeliveryPartButton_Click"/>
<Button x:Name="DeleteDeliveryPartButton" Content="Löschen" IsEnabled="False"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,10,5,5" Grid.Column="1" Grid.Row="2"
Click="DeleteDeliveryPartButton_Click"/>
</Grid>
</GroupBox>
<GroupBox Header="Herkunft" Grid.Column="0" Grid.Row="3" Margin="5,5,5,10">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="Weinbaugebiet:" Margin="10,10,0,10" Grid.Column="0"/>
<ComboBox x:Name="WineOriginInput" Margin="0,10,10,10" Grid.Column="1"
ItemTemplate="{StaticResource WineOriginTemplate}"/>
<Label Content="Weinbau-KG:" Margin="10,40,0,10" Grid.Column="0"/>
<ComboBox x:Name="WineKgInput" Margin="0,40,10,10" Grid.Column="1"
DisplayMemberPath="Name"
SelectionChanged="WineKgInput_SelectionChanged"/>
<Label Content="Ried:" Margin="10,70,0,10" Grid.Column="0"/>
<ComboBox x:Name="WineRdInput" Margin="0,70,10,10" Grid.Column="1"
DisplayMemberPath="Name"/>
<CheckBox x:Name="GebundenInput" Content="Gebunden" Margin="10,105,0,0" Grid.Column="0" Grid.ColumnSpan="2" IsThreeState="True"
VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/>
</Grid>
</GroupBox>
</Grid>
<StatusBar Grid.Row="5" Grid.ColumnSpan="3" BorderThickness="0,1,0,0" BorderBrush="Gray"> <StatusBar Grid.Row="5" Grid.ColumnSpan="3" BorderThickness="0,1,0,0" BorderBrush="Gray">
<StatusBar.ItemsPanel> <StatusBar.ItemsPanel>

View File

@ -1,8 +1,11 @@
using Elwig.Documents; using Elwig.Documents;
using Elwig.Helpers; using Elwig.Helpers;
using Elwig.Helpers.Export;
using Elwig.Models; using Elwig.Models;
using LinqKit;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.Win32;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -23,6 +26,7 @@ namespace Elwig.Windows {
private List<string> TextFilter = new(); private List<string> TextFilter = new();
private readonly RoutedCommand CtrlF = new(); private readonly RoutedCommand CtrlF = new();
private string? LastScaleError = null;
private string? ManualWeighingReason = null; private string? ManualWeighingReason = null;
private string? ScaleId = null; private string? ScaleId = null;
private string? WeighingId = null; private string? WeighingId = null;
@ -58,6 +62,8 @@ namespace Elwig.Windows {
SearchInput.TextChanged -= SearchInput_TextChanged; SearchInput.TextChanged -= SearchInput_TextChanged;
SeasonInput.Value = Utils.CurrentLastSeason; SeasonInput.Value = Utils.CurrentLastSeason;
DoShowWarningWindows = false;
if (IsReceipt) { if (IsReceipt) {
Title = "Übernahme - Elwig"; Title = "Übernahme - Elwig";
TodayOnlyInput.IsChecked = true; TodayOnlyInput.IsChecked = true;
@ -71,6 +77,7 @@ namespace Elwig.Windows {
if (n >= 2) WeighingBButton.Content = $"Wiegen {App.Scales[1].ScaleId}"; if (n >= 2) WeighingBButton.Content = $"Wiegen {App.Scales[1].ScaleId}";
if (n >= 3) WeighingCButton.Content = $"Wiegen {App.Scales[2].ScaleId}"; if (n >= 3) WeighingCButton.Content = $"Wiegen {App.Scales[2].ScaleId}";
if (n >= 4) WeighingDButton.Content = $"Wiegen {App.Scales[3].ScaleId}"; if (n >= 4) WeighingDButton.Content = $"Wiegen {App.Scales[3].ScaleId}";
WeighingManualButton.Margin = new Thickness(10, 10 + n * 32, 10, 10);
} else { } else {
WeighingManualButton.Visibility = Visibility.Hidden; WeighingManualButton.Visibility = Visibility.Hidden;
WeighingAButton.Visibility = Visibility.Hidden; WeighingAButton.Visibility = Visibility.Hidden;
@ -87,13 +94,18 @@ namespace Elwig.Windows {
AllSeasonsInput.IsChecked = true; AllSeasonsInput.IsChecked = true;
} }
private async void Window_Loaded(object sender, RoutedEventArgs evt) { private void Window_Loaded(object sender, RoutedEventArgs evt) {
Menu_Print_DeliveryJournal_ShowToday.IsEnabled = App.IsPrintingReady;
Menu_Print_DeliveryJournal_PrintToday.IsEnabled = App.IsPrintingReady;
Menu_Print_DeliveryJournal_ShowFilter.IsEnabled = App.IsPrintingReady;
Menu_Print_DeliveryJournal_PrintFilter.IsEnabled = App.IsPrintingReady;
OnSecondPassed(null, null); OnSecondPassed(null, null);
Timer.Start(); Timer.Start();
LockInputs(); LockInputs();
if (IsReceipt) { if (IsReceipt) {
NewDeliveryButton_Click(null, null); NewDeliveryButton_Click(null, null);
if ((await Context.Seasons.FindAsync(Utils.CurrentYear)) == null) { if ((Context.Seasons.Find(Utils.CurrentYear)) == null) {
MessageBox.Show("Die Saison für das aktuelle Jahr wurde noch nicht erstellt. Neue Lieferungen können nicht abgespeichert werden.", MessageBox.Show("Die Saison für das aktuelle Jahr wurde noch nicht erstellt. Neue Lieferungen können nicht abgespeichert werden.",
"Saison noch nicht erstellt", MessageBoxButton.OK, MessageBoxImage.Error); "Saison noch nicht erstellt", MessageBoxButton.OK, MessageBoxImage.Error);
} }
@ -102,36 +114,111 @@ namespace Elwig.Windows {
private async void Menu_Print_ShowDeliveryNote_Click(object sender, RoutedEventArgs evt) { private async void Menu_Print_ShowDeliveryNote_Click(object sender, RoutedEventArgs evt) {
if (DeliveryList.SelectedItem is not Delivery d) return; if (DeliveryList.SelectedItem is not Delivery d) return;
Mouse.OverrideCursor = Cursors.AppStarting;
using var doc = new DeliveryNote(d, Context); using var doc = new DeliveryNote(d, Context);
await doc.Generate(); await doc.Generate();
Mouse.OverrideCursor = null;
doc.Show(); doc.Show();
} }
private async void Menu_Print_PrintDeliveryNote_Click(object sender, RoutedEventArgs evt) { private async void Menu_Print_PrintDeliveryNote_Click(object sender, RoutedEventArgs evt) {
if (DeliveryList.SelectedItem is not Delivery d) return; if (DeliveryList.SelectedItem is not Delivery d) return;
Mouse.OverrideCursor = Cursors.AppStarting;
using var doc = new DeliveryNote(d, Context); using var doc = new DeliveryNote(d, Context);
await doc.Generate(); await doc.Generate();
Mouse.OverrideCursor = null;
await doc.Print(); await doc.Print();
} }
private void Menu_Export_Bki_Click(object sender, RoutedEventArgs evt) { private async void Menu_Export_Bki_Click(object sender, RoutedEventArgs evt) {
// TODO export Traubentransportscheinliste if (sender is not MenuItem m) return;
var year = int.Parse(m.Header.ToString()?.Split(" ")[^1] ?? Utils.CurrentLastSeason.ToString());
var d = new SaveFileDialog() {
FileName = $"{App.Client.NameToken}-Traubentransportscheinliste-{year}.{Bki.FileExtension}",
DefaultExt = Bki.FileExtension,
Filter = "CSV-Datei (*.csv)|*.csv",
Title = $"Traubentransportscheinliste (BKI) speichern unter - Elwig"
};
if (d.ShowDialog() == true) {
Mouse.OverrideCursor = Cursors.AppStarting;
using var file = new Bki(d.FileName);
await file.ExportAsync(year);
Mouse.OverrideCursor = null;
}
}
private async void Menu_Print_DeliveryJournal_ShowToday_Click(object sender, RoutedEventArgs evt) {
Mouse.OverrideCursor = Cursors.AppStarting;
var doc = new DeliveryJournal(Context, DateOnly.FromDateTime(Utils.Today));
await doc.Generate();
Mouse.OverrideCursor = null;
doc.Show();
}
private async void Menu_Print_DeliveryJournal_PrintToday_Click(object sender, RoutedEventArgs evt) {
Mouse.OverrideCursor = Cursors.AppStarting;
var doc = new DeliveryJournal(Context, DateOnly.FromDateTime(Utils.Today));
await doc.Generate();
Mouse.OverrideCursor = null;
await doc.Print();
}
private async void Menu_Print_DeliveryJournal_ShowFilter_Click(object sender, RoutedEventArgs evt) {
Mouse.OverrideCursor = Cursors.AppStarting;
var (f, _, d, _) = await GetFilters();
var doc = new DeliveryJournal(string.Join(" / ", f), d);
await doc.Generate();
Mouse.OverrideCursor = null;
doc.Show();
}
private async void Menu_Print_DeliveryJournal_PrintFilter_Click(object sender, RoutedEventArgs evt) {
Mouse.OverrideCursor = Cursors.AppStarting;
var (f, _, d, _) = await GetFilters();
var doc = new DeliveryJournal(string.Join(" / ", f), d);
await doc.Generate();
Mouse.OverrideCursor = null;
doc.Show();
}
private void Menu_Settings_EnableFreeEditing_Checked(object sender, RoutedEventArgs evt) {
if (IsEditing || IsCreating) {
DateInput.IsReadOnly = false;
TimeInput.IsReadOnly = false;
BranchInput.IsEnabled = true;
if (IsCreating) TimeInput.Text = "";
OnSecondPassed(null, null);
}
}
private void Menu_Settings_EnableFreeEditing_Unchecked(object sender, RoutedEventArgs evt) {
DateInput.IsReadOnly = true;
TimeInput.IsReadOnly = true;
BranchInput.IsEnabled = false;
OnSecondPassed(null, null);
} }
private void OnSecondPassed(object? sender, EventArgs? evt) { private void OnSecondPassed(object? sender, EventArgs? evt) {
if (IsReceipt && IsCreating) { if (IsReceipt && IsCreating && !Menu_Settings_EnableFreeEditing.IsChecked) {
var now = DateTime.Now; var now = DateTime.Now;
TimeInput.Text = now.ToString("HH:mm"); TimeInput.Text = now.ToString("HH:mm");
DateInput.Text = now.ToString("dd.MM.yyyy"); DateInput.Text = now.ToString("dd.MM.yyyy");
SetDefaultValue(TimeInput);
SetDefaultValue(DateInput);
} }
} }
private void InitialInputs() { private void InitialInputs() {
LastScaleError = null;
WeighingId = null;
ScaleId = null;
ManualWeighingReason = null;
ClearOriginalValues(); ClearOriginalValues();
ClearDefaultValues(); ClearDefaultValues();
HandPickedInput.IsChecked = null; HandPickedInput.IsChecked = null;
if (App.Client.IsMatzen) { if (App.Client.IsMatzen || App.Client.IsWolkersdorf) {
GerebeltGewogenInput.IsChecked = true; GerebeltGewogenInput.IsChecked = true;
GerebeltGewogenInput.IsEnabled = false; GerebeltGewogenInput.IsEnabled = false;
SetDefaultValue(GerebeltGewogenInput); SetDefaultValue(GerebeltGewogenInput);
@ -140,6 +227,17 @@ namespace Elwig.Windows {
GerebeltGewogenInput.IsEnabled = true; GerebeltGewogenInput.IsEnabled = true;
UnsetDefaultValue(GerebeltGewogenInput); UnsetDefaultValue(GerebeltGewogenInput);
} }
if (App.Client.IsMatzen || App.Client.IsWinzerkeller) {
GebundenInput.IsChecked = null;
GebundenInput.IsEnabled = false;
SetDefaultValue(GebundenInput);
} else {
GebundenInput.IsChecked = null;
GebundenInput.IsEnabled = true;
UnsetDefaultValue(GebundenInput);
}
WineQualityLevelInput.IsEnabled = false;
SetDefaultValue(HandPickedInput); SetDefaultValue(HandPickedInput);
ValidateRequiredInputs(); ValidateRequiredInputs();
@ -157,59 +255,107 @@ namespace Elwig.Windows {
bool ch = HasChanged, v = IsValid; bool ch = HasChanged, v = IsValid;
ResetButton.IsEnabled = ch; ResetButton.IsEnabled = ch;
SaveButton.IsEnabled = v && ch; SaveButton.IsEnabled = v && ch;
FinishButton.IsEnabled = v || !ch; FinishButton.IsEnabled = v && ch;
NewDeliveryPartButton.IsEnabled = v; NewDeliveryPartButton.IsEnabled = v && ch;
CancelCreatingButton.IsEnabled = DeliveryList.SelectedItem == null || DeliveryPartList.SelectedItem == null; CancelCreatingButton.IsEnabled = DeliveryList.SelectedItem == null || DeliveryPartList.SelectedItem == null;
} }
private void Input_KeyUp(object sender, KeyEventArgs evt) {
if (sender is not Control ctrl) return;
if (evt.Key != Key.Enter) return;
if (ctrl == MgNrInput || ctrl == MemberInput) {
SortIdInput.Focus();
SortIdInput.SelectAll();
} else if (ctrl == SortIdInput || ctrl == WineVarietyInput || ctrl == AttributesInput) {
GradationOeInput.Focus();
GradationOeInput.SelectAll();
} else if (ctrl == GradationKmwInput || ctrl == GradationOeInput || ctrl == WineQualityLevelInput) {
if (WeighingAButton.IsVisible) WeighingAButton.Focus();
else WeighingManualButton.Focus();
}
}
private async Task RefreshDeliveryList() { private async Task RefreshDeliveryList() {
await RefreshDeliveryListQuery(); await RefreshDeliveryListQuery();
} }
private async Task RefreshDeliveryListQuery(bool updateSort = false) { private async Task<(List<string>, IQueryable<Delivery>, IQueryable<DeliveryPart>, List<string>)> GetFilters() {
List<string> filterNames = new();
IQueryable<Delivery> deliveryQuery = Context.Deliveries; IQueryable<Delivery> deliveryQuery = Context.Deliveries;
if (Member != null) { if (Member != null) {
deliveryQuery = deliveryQuery.Where(d => d.MgNr == Member.MgNr); deliveryQuery = deliveryQuery.Where(d => d.MgNr == Member.MgNr);
filterNames.Add(Member.AdministrativeName);
} }
if (TodayOnlyInput.IsChecked == true) { if (TodayOnlyInput.IsChecked == true) {
deliveryQuery = deliveryQuery deliveryQuery = deliveryQuery
.Where(d => (d.DateString == Utils.Today.ToString("yyyy-MM-dd") && d.TimeString.CompareTo("03:00:00") > 0) || .Where(d => (d.DateString == Utils.Today.ToString("yyyy-MM-dd") && (d.TimeString == null || d.TimeString.CompareTo("03:00:00") > 0)) ||
(d.DateString == Utils.Today.AddDays(1).ToString("yyyy-MM-dd") && d.TimeString.CompareTo("03:00:00") <= 0)); (d.DateString == Utils.Today.AddDays(1).ToString("yyyy-MM-dd") && (d.TimeString == null || d.TimeString.CompareTo("03:00:00") <= 0)));
filterNames.Add(Utils.Today.ToString("dd.MM.yyyy"));
} else if (AllSeasonsInput.IsChecked == false) { } else if (AllSeasonsInput.IsChecked == false) {
deliveryQuery = deliveryQuery.Where(d => d.Year == SeasonInput.Value); deliveryQuery = deliveryQuery.Where(d => d.Year == SeasonInput.Value);
filterNames.Add(SeasonInput.Value.ToString() ?? "");
} }
IQueryable<DeliveryPart> dpq = deliveryQuery
.SelectMany(d => d.Parts)
.OrderBy(p => p.Delivery.DateString)
.ThenBy(p => p.Delivery.TimeString)
.ThenBy(p => p.Delivery.LsNr)
.ThenBy(p => p.DPNr);
var filterVar = new List<string>(); var filterVar = new List<string>();
var filterNotVar = new List<string>();
var filterQual = new List<string>(); var filterQual = new List<string>();
var filterMgNr = new List<int>(); var filterMgNr = new List<int>();
var filterDate = new List<string>(); var filterZwst = new List<string>();
var filterPartDate = new List<string>(); var filterAttr = new List<string>();
string? filterTimeGt = null; var filterNotAttr = new List<string>();
string? filterTimeLt = null; var filterDate = new List<(string?, string?)>();
int filterYearGt = 0; var filterTime = new List<(string?, string?)>();
int filterYearLt = 0; int filterYearGt = 0, filterYearLt = 0;
double filterKmwGt = 0; double filterKmwGt = 0, filterKmwLt = 0;
double filterKmwLt = 0; double filterOeGt = 0, filterOeLt = 0;
double filterOeGt = 0;
double filterOeLt = 0;
var filter = TextFilter.ToList(); var filter = TextFilter.ToList();
if (filter.Count > 0) { if (filter.Count > 0) {
var var = await Context.WineVarieties.Select(v => v.SortId).ToListAsync(); var var = await Context.WineVarieties.ToDictionaryAsync(v => v.SortId, v => v);
var qual = await Context.WineQualityLevels.Select(q => q.QualId).ToListAsync(); var qual = await Context.WineQualityLevels.Where(q => !q.IsPredicate).ToDictionaryAsync(q => q.QualId, q => q);
var mgnr = await Context.Members.Select(m => m.MgNr.ToString()).ToListAsync(); var mgnr = await Context.Members.ToDictionaryAsync(m => m.MgNr.ToString(), m => m);
var zwst = await Context.Branches.ToDictionaryAsync(b => b.Name.ToLower().Split(" ")[0], b => b);
var attr = await Context.WineAttributes.ToDictionaryAsync(a => a.Name.ToLower().Split(" ")[0], a => a);
for (int i = 0; i < filter.Count; i++) { for (int i = 0; i < filter.Count; i++) {
var e = filter[i]; var e = filter[i];
if (e.Length == 2 && var.Contains(e.ToUpper())) { if (e.Length == 2 && var.ContainsKey(e.ToUpper())) {
filterVar.Add(e.ToUpper()); filterVar.Add(e.ToUpper());
filter.RemoveAt(i--); filter.RemoveAt(i--);
} else if (e.Length == 3 && qual.Contains(e.ToUpper())) { filterNames.Add(var[e.ToUpper()].Name);
} else if (e.Length == 3 && e[0] == '!' && var.ContainsKey(e[1..].ToUpper())) {
filterNotVar.Add(e[1..].ToUpper());
filter.RemoveAt(i--);
filterNames.Add("außer " + var[e[1..].ToUpper()].Name);
} else if (e.Length == 3 && qual.ContainsKey(e.ToUpper())) {
filterQual.Add(e.ToUpper()); filterQual.Add(e.ToUpper());
filter.RemoveAt(i--); filter.RemoveAt(i--);
} else if (e.All(char.IsAsciiDigit) && mgnr.Contains(e)) { filterNames.Add(qual[e.ToUpper()].Name);
} else if (e.All(char.IsAsciiDigit) && mgnr.ContainsKey(e)) {
filterMgNr.Add(int.Parse(e)); filterMgNr.Add(int.Parse(e));
filter.RemoveAt(i--); filter.RemoveAt(i--);
filterNames.Add(mgnr[e].AdministrativeName);
} else if (attr.ContainsKey(e.ToLower())) {
var a = attr[e.ToLower()];
filterAttr.Add(a.AttrId);
filter.RemoveAt(i--);
filterNames.Add($"Attribut {a.Name}");
} else if (e[0] == '!' && attr.ContainsKey(e[1..].ToLower())) {
var a = attr[e[1..].ToLower()];
filterNotAttr.Add(a.AttrId);
filter.RemoveAt(i--);
filterNames.Add($"ohne Attribut {a.Name}");
} else if (zwst.ContainsKey(e.ToLower())) {
var b = zwst[e.ToLower()];
filterZwst.Add(b.ZwstId);
filter.RemoveAt(i--);
filterNames.Add($"Zweigstelle {b.Name}");
} else if (e.StartsWith(">") || e.StartsWith("<")) { } else if (e.StartsWith(">") || e.StartsWith("<")) {
if (double.TryParse(e[1..], out var num)) { if (double.TryParse(e[1..], out var num)) {
switch ((e[0], num)) { switch ((e[0], num)) {
@ -221,12 +367,6 @@ namespace Elwig.Windows {
case ('<', _): filterOeLt = num; break; case ('<', _): filterOeLt = num; break;
} }
filter.RemoveAt(i--); filter.RemoveAt(i--);
} else if (TimeOnly.TryParse(e[1..], out var time)) {
switch ((e[0], time)) {
case ('>', _): filterTimeGt = $"{time:HH:mm}"; break;
case ('<', _): filterTimeLt = $"{time:HH:mm}"; break;
}
filter.RemoveAt(i--);
} }
if (e.Length == 1) filter.RemoveAt(i--); if (e.Length == 1) filter.RemoveAt(i--);
} else if (e.Length > 1 && Utils.FromToRegex.IsMatch(e)) { } else if (e.Length > 1 && Utils.FromToRegex.IsMatch(e)) {
@ -234,14 +374,14 @@ namespace Elwig.Windows {
double? from = (parts[0].Length > 0) ? double.Parse(parts[0].Replace(".", ",")) : null; double? from = (parts[0].Length > 0) ? double.Parse(parts[0].Replace(".", ",")) : null;
double? to = (parts[1].Length > 0) ? double.Parse(parts[1].Replace(".", ",")) : null; double? to = (parts[1].Length > 0) ? double.Parse(parts[1].Replace(".", ",")) : null;
switch ((from, to)) { switch ((from, to)) {
case (<= 30, <= 30): case ( <= 30, <= 30):
case (<= 30, null): case ( <= 30, null):
case (null, <= 30): case (null, <= 30):
filterKmwGt = from ?? 0; filterKmwGt = from ?? 0;
filterKmwLt = to ?? 0; filterKmwLt = to ?? 0;
break; break;
case (>= 1900, >= 1900): case ( >= 1900, >= 1900):
case (>= 1900, null): case ( >= 1900, null):
case (null, >= 1900): case (null, >= 1900):
filterYearGt = (int)(from ?? 0); filterYearGt = (int)(from ?? 0);
filterYearLt = (int)(to ?? -1) + 1; filterYearLt = (int)(to ?? -1) + 1;
@ -254,16 +394,58 @@ namespace Elwig.Windows {
filter.RemoveAt(i--); filter.RemoveAt(i--);
} else if (e.Length > 1 && Utils.FromToTimeRegex.IsMatch(e)) { } else if (e.Length > 1 && Utils.FromToTimeRegex.IsMatch(e)) {
var parts = e.Split("-"); var parts = e.Split("-");
filterTimeGt = TimeOnly.TryParse(parts[0], out var from) ? $"{from:HH:mm}" : null; filterTime.Add((TimeOnly.TryParse(parts[0], out var from) ? $"{from:HH:mm}" : null, TimeOnly.TryParse(parts[1], out var to) ? $"{to:HH:mm}" : null));
filterTimeLt = TimeOnly.TryParse(parts[1], out var to) ? $"{to:HH:mm}" : null;
filter.RemoveAt(i--); filter.RemoveAt(i--);
var t = filterTime.Last();
if (t.Item1 != null && t.Item2 != null) {
filterNames.Add($"{t.Item1}{t.Item2}");
} else if (t.Item1 != null) {
filterNames.Add($"ab {t.Item1}");
} else if (t.Item2 != null) {
filterNames.Add($"bis {t.Item2}");
}
} else if (DateOnly.TryParse(e, out var date)) { } else if (DateOnly.TryParse(e, out var date)) {
filterDate.Add($"{date:yyyy-MM-dd}"); var s = date.ToString("yyyy-MM-dd");
filter.RemoveAt(i--); filterDate.Add((s, s));
} else if (Utils.PartialDateRegex.IsMatch(e)) {
var parts = e.Split(".");
filterPartDate.Add($"-{int.Parse(parts[1]):00}-{int.Parse(parts[0]):00}");
filter.RemoveAt(i--); filter.RemoveAt(i--);
if (filterNames.Contains(SeasonInput.Value.ToString()) && SeasonInput.Value == date.Year)
filterNames.Remove(SeasonInput.Value.ToString());
filterNames.Add(date.ToString("dd.MM.yyyy"));
} else if (Utils.DateFromToRegex.IsMatch(e)) {
var parts = e.Split("-");
if (parts.Length == 1) {
// single date
var dParts = parts[0].Split('.');
var s = $"{dParts[2]}-{dParts[1].PadLeft(2, '0')}-{dParts[0].PadLeft(2, '0')}";
filterDate.Add((s, s));
filter.RemoveAt(i--);
var n = string.Join('.', s.Split('-').Reverse());
if (dParts[2] == "") {
filterNames.Remove(SeasonInput.Value.ToString());
filterNames.Add(n + SeasonInput.Value.ToString());
} else {
if (SeasonInput.Value.ToString() == dParts[2])
filterNames.Remove(SeasonInput.Value.ToString());
filterNames.Add(n);
}
} else if (parts.Length == 2) {
// from/to date
var d1Parts = parts[0].Split('.');
var d2Parts = parts[1].Split('.');
var s1 = d1Parts.Length < 2 ? null : $"{d1Parts.ElementAtOrDefault(2)}-{d1Parts[1].PadLeft(2, '0')}-{d1Parts[0].PadLeft(2, '0')}";
var s2 = d2Parts.Length < 2 ? null : $"{d2Parts.ElementAtOrDefault(2)}-{d2Parts[1].PadLeft(2, '0')}-{d2Parts[0].PadLeft(2, '0')}";
filterDate.Add((s1, s2));
filter.RemoveAt(i--);
var n1 = s1 == null ? null : string.Join('.', s1.Split('-').Reverse());
var n2 = s2 == null ? null : string.Join('.', s2.Split('-').Reverse());
if (n1 != null && n2 != null) {
filterNames.Add($"{n1}{n2}");
} else if (n1 != null) {
filterNames.Add($"ab dem {n1}");
} else if (n2 != null) {
filterNames.Add($"bis zum {n2}");
}
}
} else if (e.Length > 2 && e.StartsWith("\"") && e.EndsWith("\"")) { } else if (e.Length > 2 && e.StartsWith("\"") && e.EndsWith("\"")) {
filter[i] = e[1..^1]; filter[i] = e[1..^1];
} else if (e.Length <= 2) { } else if (e.Length <= 2) {
@ -271,22 +453,67 @@ namespace Elwig.Windows {
} }
} }
if (filterMgNr.Count > 0) deliveryQuery = deliveryQuery.Where(d => filterMgNr.Contains(d.MgNr)); if (filterYearGt > 0) dpq = dpq.Where(p => p.Year >= filterYearGt);
if (filterDate.Count > 0) deliveryQuery = deliveryQuery.Where(d => filterDate.Contains(d.DateString)); if (filterYearLt > 0) dpq = dpq.Where(p => p.Year < filterYearLt);
if (filterPartDate.Count > 0) deliveryQuery = deliveryQuery.Where(d => filterPartDate.Contains(d.DateString.Substring(4))); if (filterMgNr.Count > 0) dpq = dpq.Where(p => filterMgNr.Contains(p.Delivery.MgNr));
if (filterYearGt > 0) deliveryQuery = deliveryQuery.Where(d => d.Year >= filterYearGt); if (filterDate.Count > 0) {
if (filterYearLt > 0) deliveryQuery = deliveryQuery.Where(d => d.Year < filterYearLt); var pr = PredicateBuilder.New<DeliveryPart>(false);
if (filterTimeGt != null) deliveryQuery = deliveryQuery.Where(d => filterTimeGt.CompareTo(d.TimeString) <= 0); foreach (var (d1, d2) in filterDate)
if (filterTimeLt != null) deliveryQuery = deliveryQuery.Where(d => filterTimeLt.CompareTo(d.TimeString) > 0); pr.Or(p => (d1 == null || d1.CompareTo(p.Delivery.DateString.Substring(10 - d1.Length)) <= 0) && (d2 == null || d2.CompareTo(p.Delivery.DateString.Substring(10 - d2.Length)) >= 0));
if (filterVar.Count > 0) deliveryQuery = deliveryQuery.Where(d => d.Parts.Any(p => filterVar.Contains(p.SortId))); dpq = dpq.Where(pr);
if (filterQual.Count > 0) deliveryQuery = deliveryQuery.Where(d => d.Parts.Any(p => filterQual.Contains(p.QualId))); }
if (filterKmwGt > 0) deliveryQuery = deliveryQuery.Where(d => d.Parts.Any(p => p.Kmw >= filterKmwGt)); if (filterTime.Count > 0) {
if (filterKmwLt > 0) deliveryQuery = deliveryQuery.Where(d => d.Parts.Any(p => p.Kmw < filterKmwLt)); var pr = PredicateBuilder.New<DeliveryPart>(false);
if (filterOeGt > 0) deliveryQuery = deliveryQuery.Where(d => d.Parts.Any(p => p.Kmw * (4.54 + 0.022 * p.Kmw) >= filterOeGt)); foreach (var (t1, t2) in filterTime)
if (filterOeLt > 0) deliveryQuery = deliveryQuery.Where(d => d.Parts.Any(p => p.Kmw * (4.54 + 0.022 * p.Kmw) < filterOeLt)); pr.Or(p => (t1 == null || t1.CompareTo(p.Delivery.TimeString) <= 0) && (t2 == null || t2.CompareTo(p.Delivery.TimeString) > 0));
dpq = dpq.Where(p => p.Delivery.TimeString != null).Where(pr);
}
if (filterVar.Count > 0) dpq = dpq.Where(p => filterVar.Contains(p.SortId));
if (filterNotVar.Count > 0) dpq = dpq.Where(p => !filterNotVar.Contains(p.SortId));
if (filterQual.Count > 0) dpq = dpq.Where(p => filterQual.Contains(p.QualId));
if (filterZwst.Count > 0) dpq = dpq.Where(p => filterZwst.Contains(p.Delivery.ZwstId));
if (filterAttr.Count > 0)
foreach (var a in filterAttr)
dpq = dpq.Where(p => p.PartAttributes.Select(a => a.Attr.AttrId).Contains(a));
if (filterNotAttr.Count > 0)
foreach (var a in filterNotAttr)
dpq = dpq.Where(p => !p.PartAttributes.Select(a => a.Attr.AttrId).Contains(a));
if (filterKmwGt > 0) dpq = dpq.Where(p => p.Kmw >= filterKmwGt);
if (filterKmwLt > 0) dpq = dpq.Where(p => p.Kmw < filterKmwLt);
if (filterOeGt > 0) dpq = dpq.Where(p => p.Kmw * (4.54 + 0.022 * p.Kmw) >= filterOeGt);
if (filterOeLt > 0) dpq = dpq.Where(p => p.Kmw * (4.54 + 0.022 * p.Kmw) < filterOeLt);
if (filterYearGt > 0 && filterYearLt > 0) {
filterNames.Insert(0, $"{filterYearGt}{filterYearLt - 1}");
} else if (filterYearGt > 0) {
filterNames.Insert(0, $"ab {filterYearGt}");
} else if (filterYearLt > 0) {
filterNames.Insert(0, $"bis {filterYearLt - 1}");
}
if (filterKmwGt > 0 && filterKmwLt > 0) {
filterNames.Add($"{filterKmwGt:N1}{filterKmwLt:N1} °KMW");
} else if (filterKmwGt > 0) {
filterNames.Add($"ab {filterKmwGt:N1} °KMW");
} else if (filterKmwLt > 0) {
filterNames.Add($"unter {filterKmwLt:N1} °KMW");
}
if (filterOeGt > 0 && filterOeLt > 0) {
filterNames.Add($"{filterOeGt:N1}{filterOeLt:N1} °Oe");
} else if (filterOeGt > 0) {
filterNames.Add($"ab {filterOeGt:N1} °Oe");
} else if (filterOeLt > 0) {
filterNames.Add($"unter {filterOeLt:N1} °Oe");
}
} }
List<Delivery> deliveries = await deliveryQuery.OrderByDescending(d => d.DateString).ThenByDescending(d => d.TimeString).ToListAsync(); return (filterNames, dpq.Select(p => p.Delivery).Distinct().OrderBy(d => d.DateString).ThenBy(d => d.TimeString), dpq, filter);
}
private async Task RefreshDeliveryListQuery(bool updateSort = false) {
var (_, deliveryQuery, deliveryPartsQuery, filter) = await GetFilters();
var deliveries = await deliveryQuery.ToListAsync();
deliveries.Reverse();
if (filter.Count > 0 && deliveries.Count > 0) { if (filter.Count > 0 && deliveries.Count > 0) {
var dict = deliveries.AsParallel() var dict = deliveries.AsParallel()
.ToDictionary(d => d, d => d.SearchScore(TextFilter)) .ToDictionary(d => d, d => d.SearchScore(TextFilter))
@ -299,22 +526,16 @@ namespace Elwig.Windows {
.ToList(); .ToList();
} }
ControlUtils.RenewItemsSource(DeliveryList, deliveries, d => ((d as Delivery)?.Year, (d as Delivery)?.DId), DeliveryList_SelectionChanged, ControlUtils.RenewSourceDefault.IfOnly, !updateSort); ControlUtils.RenewItemsSource(DeliveryList, deliveries, d => ((d as Delivery)?.Year, (d as Delivery)?.DId),
DeliveryList_SelectionChanged, filter.Count > 0 ? ControlUtils.RenewSourceDefault.IfOnly : ControlUtils.RenewSourceDefault.None, !updateSort);
var members = deliveries.Select(d => d.Member).DistinctBy(m => m.MgNr).ToList(); var members = deliveries.Select(d => d.Member).DistinctBy(m => m.MgNr).ToList();
StatusMembers.Text = $"Mitglieder: {members.Count}" + (members.Count > 0 && members.Count <= 4 ? $" ({string.Join(", ", members.Select(m => m.AdministrativeName))})" : ""); StatusMembers.Text = $"Mitglieder: {members.Count}" + (members.Count > 0 && members.Count <= 4 ? $" ({string.Join(", ", members.Select(m => m.AdministrativeName))})" : "");
StatusMembers.ToolTip = StatusMembers.Text;
StatusDeliveries.Text = $"Lieferungen: {deliveries.Count}"; StatusDeliveries.Text = $"Lieferungen: {deliveries.Count}";
if (filter.Count == 0) { if (filter.Count == 0) {
var partsQuery = deliveryQuery.SelectMany(d => d.Parts); var deliveryParts = deliveryPartsQuery;
if (filterVar.Count > 0) partsQuery = partsQuery.Where(p => filterVar.Contains(p.SortId));
if (filterQual.Count > 0) partsQuery = partsQuery.Where(p => filterQual.Contains(p.QualId));
if (filterKmwGt > 0) partsQuery = partsQuery.Where(p => p.Kmw >= filterKmwGt);
if (filterKmwLt > 0) partsQuery = partsQuery.Where(p => p.Kmw < filterKmwLt);
if (filterOeGt > 0) partsQuery = partsQuery.Where(p => p.Kmw * (4.54 + 0.022 * p.Kmw) >= filterOeGt);
if (filterOeLt > 0) partsQuery = partsQuery.Where(p => p.Kmw * (4.54 + 0.022 * p.Kmw) < filterOeLt);
var deliveryParts = partsQuery;
var n = await deliveryParts.CountAsync(); var n = await deliveryParts.CountAsync();
StatusDeliveries.Text = $"Lieferungen: {deliveries.Count} ({n})"; StatusDeliveries.Text = $"Lieferungen: {deliveries.Count} ({n})";
var varieties = await deliveryParts.Select(d => d.SortId).Distinct().ToListAsync(); var varieties = await deliveryParts.Select(d => d.SortId).Distinct().ToListAsync();
@ -332,9 +553,8 @@ namespace Elwig.Windows {
StatusGradation.Text = "Gradation: -"; StatusGradation.Text = "Gradation: -";
} }
if (n > 0 && (n <= 200 || TodayOnlyInput.IsChecked == true)) { if (n > 0 && (n <= 200 || TodayOnlyInput.IsChecked == true)) {
var parts = (await deliveryParts.ToListAsync()); var parts = await deliveryParts.ToListAsync();
var groups = parts var groups = parts
.GroupBy(p => string.Join("/", p.Attributes.Select(a => a.Name))) .GroupBy(p => string.Join("/", p.Attributes.Select(a => a.Name)))
.Select(g => (g.Key, g.Sum(p => p.Weight), g.Min(p => p.Kmw), Utils.AggregateDeliveryPartsKmw(g), g.Max(p => p.Kmw))) .Select(g => (g.Key, g.Sum(p => p.Weight), g.Min(p => p.Kmw), Utils.AggregateDeliveryPartsKmw(g), g.Max(p => p.Kmw)))
@ -344,8 +564,8 @@ namespace Elwig.Windows {
if (groups.Count == 1) { if (groups.Count == 1) {
var g = groups.First().Key; var g = groups.First().Key;
if (g != "") { if (g != "") {
StatusWeight.Text += $" ({g})"; StatusWeight.Text += $" [{g}]";
StatusGradation.Text += $" ({g})"; StatusGradation.Text += $" [{g}]";
} }
var sortGroups = parts var sortGroups = parts
@ -355,12 +575,12 @@ namespace Elwig.Windows {
.ToList(); .ToList();
if (sortGroups.Count > 1 && sortGroups.Count <= 4) { if (sortGroups.Count > 1 && sortGroups.Count <= 4) {
StatusWeight.Text += $" = {string.Join(" + ", sortGroups.Select(g => $"{g.Item2:N0} kg" + (g.Key == "" ? "" : $" ({g.Key})")))}"; StatusWeight.Text += $" = {string.Join(" + ", sortGroups.Select(g => $"{g.Item2:N0} kg ({(double)g.Item2 / weight:0%})" + (g.Key == "" ? "" : $" [{g.Key}]")))}";
StatusGradation.Text += $" = {string.Join(" + ", sortGroups.Select(g => $"{g.Item3:N1}/{g.Item4:N1}/{g.Item5:N1}" + (g.Key == "" ? "" : $" ({g.Key})")))}"; StatusGradation.Text += $" = {string.Join(" + ", sortGroups.Select(g => $"{g.Item3:N1}/{g.Item4:N1}/{g.Item5:N1}" + (g.Key == "" ? "" : $" [{g.Key}]")))}";
} }
} else if (groups.Count <= 4) { } else if (groups.Count <= 4) {
StatusWeight.Text += $" = {string.Join(" + ", groups.Select(g => $"{g.Item2:N0} kg" + (g.Key == "" ? "" : $" ({g.Key})")))}"; StatusWeight.Text += $" = {string.Join(" + ", groups.Select(g => $"{g.Item2:N0} kg ({(double)g.Item2 / weight:0%})" + (g.Key == "" ? "" : $" [{g.Key}]")))}";
StatusGradation.Text += $" = {string.Join(" + ", groups.Select(g => $"{g.Item3:N1}/{g.Item4:N1}/{g.Item5:N1}" + (g.Key == "" ? "" : $" ({g.Key})")))}"; StatusGradation.Text += $" = {string.Join(" + ", groups.Select(g => $"{g.Item3:N1}/{g.Item4:N1}/{g.Item5:N1}" + (g.Key == "" ? "" : $" [{g.Key}]")))}";
} }
} }
} else { } else {
@ -368,10 +588,15 @@ namespace Elwig.Windows {
StatusWeight.Text = "Gewicht: -"; StatusWeight.Text = "Gewicht: -";
StatusGradation.Text = "Gradation: -"; StatusGradation.Text = "Gradation: -";
} }
StatusVarieties.ToolTip = StatusVarieties.Text;
// TODO display Weight/Gradation with newlines in ToolTip and grouped by sortid AND attributes
StatusWeight.ToolTip = StatusWeight.Text;
StatusGradation.ToolTip = StatusGradation.Text;
} }
protected override async Task RenewContext() { protected override async Task OnRenewContext() {
await base.RenewContext(); await base.OnRenewContext();
if (Member != null) { if (Member != null) {
if (Context.Members.Find(Member.MgNr) is not Member m) { if (Context.Members.Find(Member.MgNr) is not Member m) {
@ -382,13 +607,22 @@ namespace Elwig.Windows {
Title = $"Lieferungen - {Member.AdministrativeName} - Elwig"; Title = $"Lieferungen - {Member.AdministrativeName} - Elwig";
} }
Menu_Export_Bki.Items.Clear();
foreach (var s in await Context.Seasons.OrderByDescending(s => s.Year).ToListAsync()) {
var i = new MenuItem {
Header = $"Season {s.Year}",
};
i.Click += Menu_Export_Bki_Click;
Menu_Export_Bki.Items.Add(i);
}
await RefreshDeliveryList(); await RefreshDeliveryList();
var d = DeliveryList.SelectedItem as Delivery; var d = DeliveryList.SelectedItem as Delivery;
var y = d?.Year ?? Utils.CurrentLastSeason; var y = d?.Year ?? Utils.CurrentLastSeason;
ControlUtils.RenewItemsSource(MemberInput, await Context.Members.OrderBy(m => m.FamilyName).ThenBy(m => m.GivenName).ToListAsync(), i => (i as Member)?.MgNr); ControlUtils.RenewItemsSource(MemberInput, await Context.Members.Where(m => m.IsActive || !IsCreating).OrderBy(m => m.FamilyName).ThenBy(m => m.GivenName).ToListAsync(), i => (i as Member)?.MgNr);
ControlUtils.RenewItemsSource(BranchInput, await Context.Branches.OrderBy(b => b.Name).ToListAsync(), i => (i as Branch)?.ZwstId); ControlUtils.RenewItemsSource(BranchInput, await Context.Branches.OrderBy(b => b.Name).ToListAsync(), i => (i as Branch)?.ZwstId);
ControlUtils.RenewItemsSource(WineVarietyInput, await Context.WineVarieties.OrderBy(v => v.Name).ToListAsync(), i => (i as WineVar)?.SortId); ControlUtils.RenewItemsSource(WineVarietyInput, await Context.WineVarieties.OrderBy(v => v.Name).ToListAsync(), i => (i as WineVar)?.SortId);
ControlUtils.RenewItemsSource(AttributesInput, await Context.WineAttributes.Where(a => IsCreating || a.IsActive).OrderBy(a => a.Name).ToListAsync(), i => (i as WineAttr)?.AttrId); ControlUtils.RenewItemsSource(AttributesInput, await Context.WineAttributes.Where(a => !IsCreating || a.IsActive).OrderBy(a => a.Name).ToListAsync(), i => (i as WineAttr)?.AttrId);
ControlUtils.RenewItemsSource(WineQualityLevelInput, await Context.WineQualityLevels.ToListAsync(), i => (i as WineQualLevel)?.QualId); ControlUtils.RenewItemsSource(WineQualityLevelInput, await Context.WineQualityLevels.ToListAsync(), i => (i as WineQualLevel)?.QualId);
ControlUtils.RenewItemsSource(ModifiersInput, await Context.Modifiers.Where(m => m.Year == y).OrderBy(m => m.Ordering).ToListAsync(), i => (i as Modifier)?.ModId); ControlUtils.RenewItemsSource(ModifiersInput, await Context.Modifiers.Where(m => m.Year == y).OrderBy(m => m.Ordering).ToListAsync(), i => (i as Modifier)?.ModId);
ControlUtils.RenewItemsSource(WineOriginInput, (await Context.WineOrigins.ToListAsync()).OrderByDescending(o => o.SortKey).ThenBy(o => o.HkId), i => (i as WineOrigin)?.HkId); ControlUtils.RenewItemsSource(WineOriginInput, (await Context.WineOrigins.ToListAsync()).OrderByDescending(o => o.SortKey).ThenBy(o => o.HkId), i => (i as WineOrigin)?.HkId);
@ -399,6 +633,7 @@ namespace Elwig.Windows {
if (IsCreating) await UpdateLsNr(); if (IsCreating) await UpdateLsNr();
await RefreshDeliveryParts(); await RefreshDeliveryParts();
RefreshInputs();
} }
private void FocusSearchInput(object sender, RoutedEventArgs evt) { private void FocusSearchInput(object sender, RoutedEventArgs evt) {
@ -476,9 +711,11 @@ namespace Elwig.Windows {
AcidInput.Text = (p != null && p.Acid != null) ? $"{p.Acid:N1}" : ""; AcidInput.Text = (p != null && p.Acid != null) ? $"{p.Acid:N1}" : "";
LesewagenInput.IsChecked = p?.IsLesewagen ?? false; LesewagenInput.IsChecked = p?.IsLesewagen ?? false;
HandPickedInput.IsChecked = p?.IsHandPicked; HandPickedInput.IsChecked = p?.IsHandPicked;
GebundenInput.IsChecked = p?.IsGebunden;
ScaleId = p?.ScaleId; ScaleId = p?.ScaleId;
WeighingId = p?.WeighingId; WeighingId = p?.WeighingId;
ManualWeighingReason = p?.WeighingReason;
FinishInputFilling(); FinishInputFilling();
} }
@ -509,13 +746,13 @@ namespace Elwig.Windows {
p.DPNr = dpnr; p.DPNr = dpnr;
d.DateString = string.Join("-", DateInput.Text.Split(".").Reverse()); d.DateString = string.Join("-", DateInput.Text.Split(".").Reverse());
if (IsCreating || InputHasChanged(DateInput)) { if (deliveryNew || InputHasChanged(DateInput)) {
d.LNr = await Context.NextLNr(d.Date); d.LNr = await Context.NextLNr(d.Date);
} }
if (IsCreating) { if (IsCreating && !InputIsNotDefault(TimeInput)) {
d.TimeString = DateTime.Now.ToString("HH:mm:ss"); d.TimeString = DateTime.Now.ToString("HH:mm:ss");
} else if (InputHasChanged(TimeInput)) { } else if (IsCreating || InputHasChanged(TimeInput)) {
d.TimeString = TimeInput.Text + ":00"; d.TimeString = (TimeInput.Text != "") ? TimeInput.Text + ":00" : null;
} }
d.ZwstId = (BranchInput.SelectedItem as Branch)?.ZwstId; d.ZwstId = (BranchInput.SelectedItem as Branch)?.ZwstId;
d.LsNr = LsNrInput.Text; d.LsNr = LsNrInput.Text;
@ -532,6 +769,7 @@ namespace Elwig.Windows {
p.IsGerebelt = GerebeltGewogenInput.IsChecked ?? false; p.IsGerebelt = GerebeltGewogenInput.IsChecked ?? false;
p.IsHandPicked = HandPickedInput.IsChecked; p.IsHandPicked = HandPickedInput.IsChecked;
p.IsLesewagen = LesewagenInput.IsChecked; p.IsLesewagen = LesewagenInput.IsChecked;
p.IsGebunden = GebundenInput.IsChecked;
p.Temperature = (TemperatureInput.Text == "") ? null : double.Parse(TemperatureInput.Text); p.Temperature = (TemperatureInput.Text == "") ? null : double.Parse(TemperatureInput.Text);
p.Acid = (AcidInput.Text == "") ? null : double.Parse(AcidInput.Text); p.Acid = (AcidInput.Text == "") ? null : double.Parse(AcidInput.Text);
p.Comment = (PartCommentInput.Text == "") ? null : PartCommentInput.Text; p.Comment = (PartCommentInput.Text == "") ? null : PartCommentInput.Text;
@ -540,8 +778,7 @@ namespace Elwig.Windows {
p.ManualWeighing = ManualWeighingInput.IsChecked ?? false; p.ManualWeighing = ManualWeighingInput.IsChecked ?? false;
p.ScaleId = ScaleId; p.ScaleId = ScaleId;
p.WeighingId = WeighingId; p.WeighingId = WeighingId;
if (ManualWeighingReason != null) p.WeighingReason = ManualWeighingReason;
p.Comment = (p.Comment != null ? $"{p.Comment} / " : "") + $"Begründung Handwiegung: {ManualWeighingReason}";
EntityEntry<Delivery>? dEntry = null; EntityEntry<Delivery>? dEntry = null;
EntityEntry<DeliveryPart>? pEntry = null; EntityEntry<DeliveryPart>? pEntry = null;
@ -601,18 +838,23 @@ namespace Elwig.Windows {
WeightInput.Text = $"{res.Weight:N0}"; WeightInput.Text = $"{res.Weight:N0}";
ScaleId = s.ScaleId; ScaleId = s.ScaleId;
WeighingId = res.FullWeighingId; WeighingId = res.FullWeighingId;
s.Empty();
} else { } else {
WeightInput.Text = ""; WeightInput.Text = "";
ScaleId = null; ScaleId = null;
WeighingId = null; WeighingId = null;
} }
ManualWeighingReason = null; LastScaleError = null;
ManualWeighingInput.IsChecked = false;
} catch (Exception e) { } catch (Exception e) {
LastScaleError = e.Message.Split(": ")[^1];
WeightInput.Text = "";
ScaleId = null;
WeighingId = null;
MessageBox.Show($"Beim Wiegen ist ein Fehler aufgetreten:\n\n{e.Message}", "Waagenfehler", MessageBox.Show($"Beim Wiegen ist ein Fehler aufgetreten:\n\n{e.Message}", "Waagenfehler",
MessageBoxButton.OK, MessageBoxImage.Error); MessageBoxButton.OK, MessageBoxImage.Error);
} }
ManualWeighingReason = null;
ManualWeighingInput.IsChecked = false;
base.TextBox_TextChanged(WeightInput, null);
EnableWeighingButtons(); EnableWeighingButtons();
UpdateButtons(); UpdateButtons();
} }
@ -688,7 +930,7 @@ namespace Elwig.Windows {
MemberInput.SelectedItem = valid ? Context.Members.Find(int.Parse(MgNrInput.Text)) : null; MemberInput.SelectedItem = valid ? Context.Members.Find(int.Parse(MgNrInput.Text)) : null;
} }
private void MemberInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) { private void MemberInput_SelectionChanged(object? sender, SelectionChangedEventArgs? evt) {
var m = MemberInput.SelectedItem as Member; var m = MemberInput.SelectedItem as Member;
if (m != null) MgNrInput.Text = m.MgNr.ToString(); if (m != null) MgNrInput.Text = m.MgNr.ToString();
MemberAddressField.Text = m?.FullAddress; MemberAddressField.Text = m?.FullAddress;
@ -701,33 +943,64 @@ namespace Elwig.Windows {
} }
} }
private void EmptyScale() {
var scale = App.Scales.Where(s => s.ScaleId == ScaleId).FirstOrDefault();
if (scale == null) return;
scale.Empty();
}
private async void NewDeliveryPartButton_Click(object sender, RoutedEventArgs evt) { private async void NewDeliveryPartButton_Click(object sender, RoutedEventArgs evt) {
FinishButton.IsEnabled = false;
NewDeliveryPartButton.IsEnabled = false;
NewDeliveryPartButton.Cursor = Cursors.Wait;
DeliveryPartList.IsEnabled = false; DeliveryPartList.IsEnabled = false;
var p = await UpdateDeliveryPart(DeliveryList.SelectedItem as Delivery, DeliveryPartList.SelectedItem as DeliveryPart); var p = await UpdateDeliveryPart(DeliveryList.SelectedItem as Delivery, DeliveryPartList.SelectedItem as DeliveryPart);
EmptyScale();
await RefreshDeliveryList(); await RefreshDeliveryList();
await RefreshDeliveryParts(); await RefreshDeliveryParts();
NewDeliveryPartButton.Cursor = null;
DeliveryList.SelectedItem = p?.Delivery; DeliveryList.SelectedItem = p?.Delivery;
DeliveryPartList.SelectedItem = null; DeliveryPartList.SelectedItem = null;
RefreshInputs();
InitialInputs(); InitialInputs();
} }
private async void FinishButton_Click(object sender, RoutedEventArgs evt) { private async void FinishButton_Click(object sender, RoutedEventArgs evt) {
FinishButton.IsEnabled = false;
NewDeliveryPartButton.IsEnabled = false;
FinishButton.Cursor = Cursors.Wait;
DeliveryPartList.IsEnabled = false; DeliveryPartList.IsEnabled = false;
var p = await UpdateDeliveryPart(DeliveryList.SelectedItem as Delivery, DeliveryPartList.SelectedItem as DeliveryPart); var p = await UpdateDeliveryPart(DeliveryList.SelectedItem as Delivery, DeliveryPartList.SelectedItem as DeliveryPart);
EmptyScale();
await RefreshDeliveryList(); await RefreshDeliveryList();
await RefreshDeliveryParts(); await RefreshDeliveryParts();
if (p?.Delivery != null) { if (p?.Delivery != null) {
Mouse.OverrideCursor = Cursors.AppStarting;
using var doc = new DeliveryNote(p.Delivery, Context); using var doc = new DeliveryNote(p.Delivery, Context);
await doc.Generate(); await doc.Generate();
doc.Show(); Mouse.OverrideCursor = null;
//await doc.Print(2); if (App.Config.Debug) {
doc.Show();
} else {
await doc.Print(2);
}
} }
FinishButton.Cursor = null;
DeliveryList.SelectedItem = null; DeliveryList.SelectedItem = null;
await RenewContext();
RefreshInputs();
InitInputs(); InitInputs();
} }
private void CancelCreatingButton_Click(object sender, RoutedEventArgs evt) { private async void CancelCreatingButton_Click(object sender, RoutedEventArgs evt) {
ControlUtils.RenewItemsSource(AttributesInput, Context.WineAttributes.OrderBy(a => a.Name).ToList(), i => (i as WineAttr)?.AttrId); if (IsCreating && HasChanged) {
var r = MessageBox.Show("Soll der Vorgang wirklich abgebrochen werden?", "Abbrechen bestätigen",
MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
if (r != MessageBoxResult.Yes) return;
}
ControlUtils.RenewItemsSource(AttributesInput, await Context.WineAttributes.OrderBy(a => a.Name).ToListAsync(), i => (i as WineAttr)?.AttrId);
ControlUtils.RenewItemsSource(MemberInput, await Context.Members.Where(m => m.IsActive || !IsReceipt).OrderBy(m => m.FamilyName).ThenBy(m => m.GivenName).ToListAsync(), i => (i as Member)?.MgNr);
if (DeliveryList.SelectedItem is not Delivery d) { if (DeliveryList.SelectedItem is not Delivery d) {
// switch away from creating mode // switch away from creating mode
IsCreating = false; IsCreating = false;
@ -737,6 +1010,7 @@ namespace Elwig.Windows {
DisableWeighingButtons(); DisableWeighingButtons();
HideFinishNewPartDeliveryCancelButtons(); HideFinishNewPartDeliveryCancelButtons();
ShowNewEditDeleteButtons(); ShowNewEditDeleteButtons();
await RenewContext();
RefreshInputs(); RefreshInputs();
ClearInputStates(); ClearInputStates();
LockInputs(); LockInputs();
@ -748,8 +1022,11 @@ namespace Elwig.Windows {
} }
} }
private void NewDeliveryButton_Click(object? sender, RoutedEventArgs? evt) { private async void NewDeliveryButton_Click(object? sender, RoutedEventArgs? evt) {
ControlUtils.RenewItemsSource(AttributesInput, Context.WineAttributes.Where(a => a.IsActive).OrderBy(a => a.Name).ToList(), i => (i as WineAttr)?.AttrId); TodayOnlyInput.IsChecked = true;
SearchInput.Text = "";
ControlUtils.RenewItemsSource(AttributesInput, await Context.WineAttributes.Where(a => a.IsActive).OrderBy(a => a.Name).ToListAsync(), i => (i as WineAttr)?.AttrId);
ControlUtils.RenewItemsSource(MemberInput, await Context.Members.Where(m => m.IsActive || !IsReceipt).OrderBy(m => m.FamilyName).ThenBy(m => m.GivenName).ToListAsync(), i => (i as Member)?.MgNr);
IsCreating = true; IsCreating = true;
DeliveryList.IsEnabled = false; DeliveryList.IsEnabled = false;
DeliveryPartList.IsEnabled = false; DeliveryPartList.IsEnabled = false;
@ -769,6 +1046,7 @@ namespace Elwig.Windows {
try { try {
if (res == null || res <= 0) if (res == null || res <= 0)
return; return;
Mouse.OverrideCursor = Cursors.AppStarting;
ClearOriginalValues(); ClearOriginalValues();
if (res >= p.Weight) { if (res >= p.Weight) {
ControlUtils.SelectComboBoxItem(WineQualityLevelInput, q => (q as WineQualLevel)?.QualId, "WEI"); ControlUtils.SelectComboBoxItem(WineQualityLevelInput, q => (q as WineQualLevel)?.QualId, "WEI");
@ -797,6 +1075,7 @@ namespace Elwig.Windows {
} }
await Context.SaveChangesAsync(); await Context.SaveChangesAsync();
await RefreshDeliveryParts(); await RefreshDeliveryParts();
Mouse.OverrideCursor = null;
FinishInputFilling(); FinishInputFilling();
} catch (Exception exc) { } catch (Exception exc) {
if (entry1 != null) { if (entry1 != null) {
@ -814,7 +1093,7 @@ namespace Elwig.Windows {
} }
private void WeighingManualButton_Click(object sender, RoutedEventArgs evt) { private void WeighingManualButton_Click(object sender, RoutedEventArgs evt) {
var res = Utils.ShowManualWeighingDialog(); var res = Utils.ShowManualWeighingDialog(LastScaleError);
if (res == null) return; if (res == null) return;
WeightInput.Text = $"{res?.Item1:N0}"; WeightInput.Text = $"{res?.Item1:N0}";
ManualWeighingInput.IsChecked = true; ManualWeighingInput.IsChecked = true;
@ -849,21 +1128,28 @@ namespace Elwig.Windows {
$"Soll die Lieferung {d.LsNr} ({d.Member.AdministrativeName}, MgNr. {d.Member.MgNr}) wirklich unwiderruflich gelöscht werden?", $"Soll die Lieferung {d.LsNr} ({d.Member.AdministrativeName}, MgNr. {d.Member.MgNr}) wirklich unwiderruflich gelöscht werden?",
"Lieferung löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No); "Lieferung löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
if (r == MessageBoxResult.Yes) { if (r == MessageBoxResult.Yes) {
Mouse.OverrideCursor = Cursors.AppStarting;
Context.Remove(d); Context.Remove(d);
await Context.SaveChangesAsync(); await Context.SaveChangesAsync();
await RefreshDeliveryList(); await RefreshDeliveryList();
await RefreshDeliveryParts(); await RefreshDeliveryParts();
Mouse.OverrideCursor = null;
} }
} }
private async void SaveButton_Click(object sender, RoutedEventArgs evt) { private async void SaveButton_Click(object sender, RoutedEventArgs evt) {
DeliveryPart p = await UpdateDeliveryPart(DeliveryList.SelectedItem as Delivery, DeliveryPartList.SelectedItem as DeliveryPart); SaveButton.IsEnabled = false;
SaveButton.Cursor = Cursors.Wait;
IsEditing = false; IsEditing = false;
IsCreating = false; IsCreating = false;
DeliveryList.IsEnabled = true; DeliveryList.IsEnabled = true;
DeliveryPartList.IsEnabled = true; DeliveryPartList.IsEnabled = true;
DeliveryPart p = await UpdateDeliveryPart(DeliveryList.SelectedItem as Delivery, DeliveryPartList.SelectedItem as DeliveryPart);
SaveButton.Cursor = null;
HideSaveResetCancelButtons(); HideSaveResetCancelButtons();
ShowNewEditDeleteButtons(); ShowNewEditDeleteButtons();
LockInputs(); LockInputs();
@ -917,20 +1203,20 @@ namespace Elwig.Windows {
day = day.AddDays(-1); day = day.AddDays(-1);
var lsnrs = await Context.Deliveries var lsnrs = await Context.Deliveries
.Where(d => d.ZwstId == delivery.ZwstId) .Where(d => d.ZwstId == delivery.ZwstId)
.Where(d => (d.DateString == day.ToString("yyyy-MM-dd") && d.TimeString.CompareTo("03:00:00") > 0) || .Where(d => (d.DateString == day.ToString("yyyy-MM-dd") && (d.TimeString == null || d.TimeString.CompareTo("03:00:00") > 0)) ||
(d.DateString == day.AddDays(1).ToString("yyyy-MM-dd") && d.TimeString.CompareTo("03:00:00") <= 0)) (d.DateString == day.AddDays(1).ToString("yyyy-MM-dd") && (d.TimeString == null || d.TimeString.CompareTo("03:00:00") <= 0)))
.Where(d => d.LsNr != delivery.LsNr) .Where(d => d.LsNr != delivery.LsNr)
.OrderBy(d => d.LsNr) .OrderBy(d => d.LsNr)
.Select(d => d.LsNr) .Select(d => d.LsNr)
.ToListAsync(); .ToListAsync();
var res = Utils.ShowDeliveryExtractionDialog($"{delivery.LsNr}/{p.DPNr}", delivery.Member.AdministrativeName, count == 1, lsnrs); var res = Utils.ShowDeliveryExtractionDialog($"{delivery.LsNr}/{p.DPNr}", delivery.Member.AdministrativeName, count == 1, lsnrs);
if (res == null) return;
EntityEntry<Delivery>? entry = null; EntityEntry<Delivery>? entry = null;
try { try {
Delivery? d = null; Delivery? d = null;
if (res == null) { Mouse.OverrideCursor = Cursors.AppStarting;
return; if (res == "new") {
} else if (res == "new") {
d = Context.CreateProxy<Delivery>(); d = Context.CreateProxy<Delivery>();
d.Date = delivery.Date; d.Date = delivery.Date;
d.Time = delivery.Time; d.Time = delivery.Time;
@ -958,6 +1244,7 @@ namespace Elwig.Windows {
await Context.Entry(p).ReloadAsync(); await Context.Entry(p).ReloadAsync();
await Context.Entry(delivery).ReloadAsync(); await Context.Entry(delivery).ReloadAsync();
Mouse.OverrideCursor = null;
await RefreshDeliveryList(); await RefreshDeliveryList();
DeliveryList.SelectedItem = d; DeliveryList.SelectedItem = d;
} catch (Exception exc) { } catch (Exception exc) {
@ -979,9 +1266,11 @@ namespace Elwig.Windows {
$"Soll die Teillieferung Nr. {p.DPNr} wirklich unwiderruflich gelöscht werden?", $"Soll die Teillieferung Nr. {p.DPNr} wirklich unwiderruflich gelöscht werden?",
"Lieferung löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No); "Lieferung löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
if (r == MessageBoxResult.Yes) { if (r == MessageBoxResult.Yes) {
Mouse.OverrideCursor = Cursors.AppStarting;
Context.Remove(p); Context.Remove(p);
await Context.SaveChangesAsync(); await Context.SaveChangesAsync();
await RefreshDeliveryParts(); await RefreshDeliveryParts();
Mouse.OverrideCursor = null;
} }
} }
@ -1026,8 +1315,8 @@ namespace Elwig.Windows {
} }
private void ShowFinishNewPartDeliveryCancelButtons() { private void ShowFinishNewPartDeliveryCancelButtons() {
FinishButton.IsEnabled = IsCreating && IsValid; FinishButton.IsEnabled = false;
NewDeliveryPartButton.IsEnabled = IsCreating && IsValid; NewDeliveryPartButton.IsEnabled = false;
CancelCreatingButton.IsEnabled = true; CancelCreatingButton.IsEnabled = true;
FinishButton.Visibility = Visibility.Visible; FinishButton.Visibility = Visibility.Visible;
NewDeliveryPartButton.Visibility = Visibility.Visible; NewDeliveryPartButton.Visibility = Visibility.Visible;
@ -1067,9 +1356,9 @@ namespace Elwig.Windows {
AbgewertetInput.IsEnabled = false; AbgewertetInput.IsEnabled = false;
ManualWeighingInput.IsEnabled = false; ManualWeighingInput.IsEnabled = false;
LsNrInput.IsReadOnly = true; LsNrInput.IsReadOnly = true;
DateInput.IsReadOnly = true; DateInput.IsReadOnly = !Menu_Settings_EnableFreeEditing.IsChecked;
TimeInput.IsReadOnly = true; TimeInput.IsReadOnly = !Menu_Settings_EnableFreeEditing.IsChecked;
BranchInput.IsEnabled = false; BranchInput.IsEnabled = Menu_Settings_EnableFreeEditing.IsChecked;
} }
private void DisableWeighingButtons() { private void DisableWeighingButtons() {
@ -1082,25 +1371,36 @@ namespace Elwig.Windows {
private void EnableWeighingButtons() { private void EnableWeighingButtons() {
WeighingManualButton.IsEnabled = true; WeighingManualButton.IsEnabled = true;
WeighingAButton.IsEnabled = true; var n = App.Scales.Count;
WeighingBButton.IsEnabled = true; WeighingAButton.IsEnabled = n > 0 && App.Scales[0].IsReady;
WeighingCButton.IsEnabled = true; WeighingBButton.IsEnabled = n > 1 && App.Scales[1].IsReady;
WeighingDButton.IsEnabled = true; WeighingCButton.IsEnabled = n > 2 && App.Scales[2].IsReady;
WeighingDButton.IsEnabled = n > 3 && App.Scales[3].IsReady;
} }
private async Task UpdateLsNr() { private async Task UpdateLsNr() {
if (DateInput.Text == "" || BranchInput.SelectedItem == null) { if (DateInput.Text == "" || BranchInput.SelectedItem == null) {
LsNrInput.Text = ""; LsNrInput.Text = "";
} else { } else {
var branch = (Branch)BranchInput.SelectedItem; try {
var date = DateOnly.ParseExact(DateInput.Text, "dd.MM.yyyy"); var branch = (Branch)BranchInput.SelectedItem;
var lnr = await Context.NextLNr(date); var date = DateOnly.ParseExact(DateInput.Text, "dd.MM.yyyy");
LsNrInput.Text = Utils.GenerateLsNr(date, branch.ZwstId, lnr); var lnr = await Context.NextLNr(date);
LsNrInput.Text = Utils.GenerateLsNr(date, branch.ZwstId, lnr);
} catch {
LsNrInput.Text = "";
}
} }
} }
private void DateInput_TextChanged(object sender, TextChangedEventArgs evt) { private void DateInput_TextChanged(object sender, TextChangedEventArgs evt) {
if (IsCreating) UpdateLsNr().GetAwaiter().GetResult(); base.DateInput_TextChanged(sender, evt);
if (IsEditing || IsCreating) UpdateLsNr().GetAwaiter().GetResult();
}
private void BranchInput_SelectionChanged(object sender, RoutedEventArgs evt) {
base.ComboBox_SelectionChanged(sender, evt);
if (IsEditing || IsCreating) UpdateLsNr().GetAwaiter().GetResult();
} }
private void UpdateWineVariety(bool valid) { private void UpdateWineVariety(bool valid) {
@ -1196,8 +1496,10 @@ namespace Elwig.Windows {
} }
private void ModifiersInput_SelectionChanged(object sender, ItemSelectionChangedEventArgs evt) { private void ModifiersInput_SelectionChanged(object sender, ItemSelectionChangedEventArgs evt) {
if ((IsEditing || IsCreating) && App.Client.IsMatzen) { if (!IsEditing && !IsCreating) return;
var mod = ModifiersInput.SelectedItems.Cast<Modifier>(); var mod = ModifiersInput.SelectedItems.Cast<Modifier>();
var source = ModifiersInput.ItemsSource.Cast<Modifier>();
if (App.Client.IsMatzen) {
var kl = mod.Where(m => m.Name.StartsWith("Klasse ")); var kl = mod.Where(m => m.Name.StartsWith("Klasse "));
if (kl.Count() > 1) { if (kl.Count() > 1) {
foreach (var r in kl.Take(kl.Count() - 1)) ModifiersInput.SelectedItems.Remove(r); foreach (var r in kl.Take(kl.Count() - 1)) ModifiersInput.SelectedItems.Remove(r);
@ -1206,11 +1508,14 @@ namespace Elwig.Windows {
} }
private void LesewagenInput_Changed(object sender, RoutedEventArgs evt) { private void LesewagenInput_Changed(object sender, RoutedEventArgs evt) {
if ((IsEditing || IsCreating) && App.Client.IsMatzen) { if (!IsEditing && !IsCreating) return;
var mod = ModifiersInput.SelectedItems.Cast<Modifier>(); var mod = ModifiersInput.SelectedItems.Cast<Modifier>();
var source = ModifiersInput.ItemsSource.Cast<Modifier>();
var lw = LesewagenInput.IsChecked == true;
if (App.Client.IsMatzen) {
var kl = mod.Where(m => m.Name.StartsWith("Klasse ")).Select(m => m.ModId).LastOrDefault("A")[0]; var kl = mod.Where(m => m.Name.StartsWith("Klasse ")).Select(m => m.ModId).LastOrDefault("A")[0];
if (LesewagenInput.IsChecked == true) kl++; else kl--; if (lw) kl++; else kl--;
var newKl = ModifiersInput.ItemsSource.Cast<Modifier>().Where(m => m.ModId == kl.ToString()).FirstOrDefault(); var newKl = source.Where(m => m.ModId == kl.ToString()).FirstOrDefault();
if (newKl != null) ModifiersInput.SelectedItems.Add(newKl); if (newKl != null) ModifiersInput.SelectedItems.Add(newKl);
} }
CheckBox_Changed(sender, evt); CheckBox_Changed(sender, evt);

View File

@ -0,0 +1,38 @@
<local:ContextWindow x:Class="Elwig.Dialogs.DeliveryConfirmationsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Elwig.Windows"
mc:Ignorable="d"
Loaded="Window_Loaded"
Title="Anlieferungsbestätingungen - Elwig" Height="500" Width="800" MinHeight="400" MinWidth="600">
<Grid>
<GroupBox Header="Sortieren nach" Margin="10,10,10,10" Width="180" Height="80" VerticalAlignment="Top" HorizontalAlignment="Left">
<StackPanel Margin="5,5,0,5">
<RadioButton GroupName="Order" x:Name="OrderMgNrInput" Content="Mitgliedsnummer" IsChecked="True"/>
<RadioButton GroupName="Order" x:Name="OrderNameInput" Content="Name"/>
<RadioButton GroupName="Order" x:Name="OrderPlzInput" Content="PLZ, Ort, Name"/>
</StackPanel>
</GroupBox>
<CheckBox x:Name="AllMembersInput" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="10,100,10,10">
<TextBlock>Auch Mitglieder ohne<LineBreak/>Lieferungen miteinbeziehen</TextBlock>
</CheckBox>
<TextBox x:Name="TextElement" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible" AcceptsReturn="True"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="200,10,10,10" Height="Auto"/>
<ProgressBar x:Name="ProgressBar" Margin="10,10,10,106" Height="27" Width="180"
VerticalAlignment="Bottom" HorizontalAlignment="Left"/>
<Button x:Name="TestButton" Content="Stichprobe" FontSize="14" Width="180" Margin="10,10,10,74" Height="27" Tag="Print" IsEnabled="False"
Click="TestButton_Click"
VerticalAlignment="Bottom" HorizontalAlignment="Left"/>
<Button x:Name="ShowButton" Content="Vorschau" FontSize="14" Width="180" Margin="10,10,10,42" Height="27" Tag="Print" IsEnabled="False"
Click="ShowButton_Click"
VerticalAlignment="Bottom" HorizontalAlignment="Left"/>
<Button x:Name="PrintButton" Content="Drucken" FontSize="14" Width="180" Margin="10,10,10,10" Height="27" Tag="Print" IsEnabled="False"
Click="PrintButton_Click"
VerticalAlignment="Bottom" HorizontalAlignment="Left"/>
</Grid>
</local:ContextWindow>

View File

@ -0,0 +1,128 @@
using Elwig.Documents;
using Elwig.Models;
using Elwig.Windows;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace Elwig.Dialogs {
public partial class DeliveryConfirmationsWindow : ContextWindow {
public readonly int Year;
public DeliveryConfirmationsWindow(int year) {
InitializeComponent();
Year = year;
Title = $"Anlieferungsbestätigungen - Lese {Year} - Elwig";
TextElement.Text = App.Client.TextDeliveryConfirmation;
if (!App.Config.Debug) {
TestButton.Visibility = Visibility.Hidden;
var m = ProgressBar.Margin;
ProgressBar.Margin = new(m.Left, m.Top, m.Right, m.Bottom - 32);
}
}
private void Window_Loaded(object sender, RoutedEventArgs evt) {
TestButton.IsEnabled = App.IsPrintingReady;
ShowButton.IsEnabled = App.IsPrintingReady;
PrintButton.IsEnabled = App.IsPrintingReady;
}
protected override async Task OnRenewContext() { }
private async Task UpdateTextParameter() {
var text = TextElement.Text;
if (text.Length == 0) text = null;
if (text != App.Client.TextDeliveryConfirmation) {
App.Client.TextDeliveryConfirmation = text;
await App.Client.UpdateValues();
}
}
private async Task Generate(int mode) {
Mouse.OverrideCursor = Cursors.AppStarting;
await UpdateTextParameter();
IQueryable<Member> members;
if (AllMembersInput.IsChecked == true) {
members = Context.Members.Where(m => m.IsActive);
} else {
members = Context.Members.FromSqlRaw($"""
SELECT m.*
FROM member m
INNER JOIN delivery d ON d.mgnr = m.mgnr
WHERE d.year = {Year}
GROUP BY m.mgnr
""");
}
if (OrderMgNrInput.IsChecked == true) {
members = members
.OrderBy(m => m.MgNr);
} else if (OrderNameInput.IsChecked == true) {
members = members
.OrderBy(m => m.FamilyName)
.ThenBy(m => m.GivenName)
.ThenBy(m => m.MgNr);
} else if (OrderPlzInput.IsChecked == true) {
members = members
.OrderBy(m => m.PostalDest.AtPlz.Plz)
.ThenBy(m => m.PostalDest.AtPlz.Ort.Name)
.ThenBy(m => m.FamilyName)
.ThenBy(m => m.GivenName)
.ThenBy(m => m.MgNr);
}
IEnumerable<Member> list = await members.ToListAsync();
if (mode == 0) {
var r = new Random().Next(0, 10);
list = list.Where((_, n) => n % 10 == r);
}
var deliveries = await Context.DeliveryParts.FromSqlRaw($"""
SELECT p.*
FROM v_delivery v
JOIN delivery_part p ON (p.year, p.did, p.dpnr) = (v.year, v.did, v.dpnr)
WHERE v.year = {Year}
ORDER BY v.sortid, v.abgewertet ASC,
COALESCE(LENGTH(v.attributes), 0) ASC, attribute_prio DESC, COALESCE(v.attributes, '~'),
v.kmw DESC, v.lsnr, v.dpnr
""")
.ToListAsync();
using var doc = Document.Merge(list.Select(m => new DeliveryConfirmation(Context, Year, m, deliveries.Where(d => d.Delivery.MgNr == m.MgNr).ToList()))); ;
await doc.Generate(new Progress<double>(v => {
ProgressBar.Value = v;
}));
Mouse.OverrideCursor = null;
if (mode < 2) {
doc.Show();
return;
}
if (App.Config.Debug) {
doc.Show();
} else {
await doc.Print();
}
Close();
}
private async void TestButton_Click(object sender, RoutedEventArgs evt) {
await Generate(0);
}
private async void ShowButton_Click(object sender, RoutedEventArgs evt) {
await Generate(1);
}
private async void PrintButton_Click(object sender, RoutedEventArgs evt) {
await Generate(2);
}
}
}

View File

@ -8,6 +8,8 @@
Loaded="Window_Loaded"> Loaded="Window_Loaded">
<Window.Resources> <Window.Resources>
<Style TargetType="Button"> <Style TargetType="Button">
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="FontSize" Value="14"/> <Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="9,3"/> <Setter Property="Padding" Value="9,3"/>
<Setter Property="Height" Value="32"/> <Setter Property="Height" Value="32"/>
@ -23,41 +25,39 @@
<MenuItem Header="Über"/> <MenuItem Header="Über"/>
</MenuItem> </MenuItem>
</Menu> </Menu>
<Grid Height="100" VerticalAlignment="Top" Margin="0,25,0,0"> <Grid Height="100" VerticalAlignment="Top" Margin="25,25,0,0">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/> <ColumnDefinition Width="100"/>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Image Source="/elwig.png" RenderOptions.BitmapScalingMode="HighQuality" Grid.Column="0" <Image Source="/elwig.png" RenderOptions.BitmapScalingMode="HighQuality" Grid.Column="0"
HorizontalAlignment="Left" Margin="5,5,5,5" VerticalAlignment="Top"/> HorizontalAlignment="Left" Margin="5,5,5,5" VerticalAlignment="Top"/>
<Label Grid.Column="1" Content="Elwig" FontSize="32" <TextBlock Grid.Column="1" FontSize="32" HorizontalAlignment="Left" Margin="0,5,0,0" VerticalAlignment="Top">
HorizontalAlignment="Left" Margin="0,10,0,0" VerticalAlignment="Top"/> Elwig
<Label Grid.Column="1" Content="Elektonische Winzer-" </TextBlock>
HorizontalAlignment="Left" Margin="0,55,0,0" VerticalAlignment="Top"/> <TextBlock Grid.Column="1" HorizontalAlignment="Left" Margin="0,50,0,0" VerticalAlignment="Top" LineHeight="14" LineStackingStrategy="BlockLineHeight">
<Label Grid.Column="1" Content="genossenschaftsverwaltung" Elektonische Winzer-<LineBreak/>
HorizontalAlignment="Left" Margin="0,70,0,0" VerticalAlignment="Top"/> genossenschaftsverwaltung
</TextBlock>
<TextBlock x:Name="VersionField" Grid.Column="1" FontSize="10" HorizontalAlignment="Left" Margin="0,80,0,0" VerticalAlignment="Top">
Version: ?
</TextBlock>
</Grid> </Grid>
<Button x:Name="MemberAdminButton" Content="Mitgliederverwaltung" Click="MemberAdminButton_Click" <Button x:Name="MemberAdminButton" Content="Mitglieder" Click="MemberAdminButton_Click"
Margin="50,160,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/> Margin="50,160,0,0"/>
<Button x:Name="MemberListButton" Content="Mitgliederliste" Click="MemberListButton_Click"
Margin="50,200,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<Button x:Name="ReceiptButton" Content="Übernahme" Click="ReceiptButton_Click" <Button x:Name="ReceiptButton" Content="Übernahme" Click="ReceiptButton_Click"
Margin="50,240,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/> Margin="50,200,0,0"/>
<Button x:Name="DeliveryAdminButton" Content="Lieferungsverwaltung" Click="DeliveryAdminButton_Click" <Button x:Name="DeliveryAdminButton" Content="Lieferungen" Click="DeliveryAdminButton_Click"
Margin="50,280,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/> Margin="50,240,0,0"/>
<Button x:Name="DeliveryListButton" Content="Lieferungungen" Click="DeliveryListButton_Click" IsEnabled="False" <Button x:Name="SeasonFinishButton" Content="Leseabschluss" Click="SeasonFinishButton_Click"
Margin="50,320,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/> Margin="50,280,0,0"/>
<Button x:Name="BaseDataButton" Content="Stammdaten" Click="BaseDataButton_Click" <Button x:Name="BaseDataButton" Content="Stammdaten" Click="BaseDataButton_Click"
Margin="260,160,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/> Margin="50,320,0,0"/>
<Button x:Name="PdfButton" Content="PDF Erzeugen" Click="PdfButton_Click" Tag="Print"
Margin="260,200,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<Button x:Name="TestWindowButton" Content="Test Fenster" Click="TestWindowButton_Click" <Button x:Name="TestWindowButton" Content="Test Fenster" Click="TestWindowButton_Click"
Margin="260,240,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/> Margin="260,280,0,0"/>
<Button x:Name="QueryWindowButton" Content="Datenbankabfragen" Click="QueryWindowButton_Click" <Button x:Name="QueryWindowButton" Content="Datenbankabfragen" Click="QueryWindowButton_Click"
Margin="260,280,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/> Margin="260,320,0,0"/>
<Button x:Name="PaymentWindowButton" Content="Auszahlung" Click="PaymentWindowButton_Click"
Margin="260,320,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/>
</Grid> </Grid>
</Window> </Window>

View File

@ -1,18 +1,20 @@
using System.Linq; using System.Reflection;
using System.Windows; using System.Windows;
using Elwig.Documents;
using Elwig.Helpers;
namespace Elwig.Windows { namespace Elwig.Windows {
public partial class MainWindow : Window { public partial class MainWindow : Window {
public MainWindow() { public MainWindow() {
InitializeComponent(); InitializeComponent();
var v = Assembly.GetExecutingAssembly().GetName().Version;
VersionField.Text = "Version: " + (v == null ? "?" : $"{v.Major}.{v.Minor}.{v.Build}");
if (!App.Config.Debug) {
TestWindowButton.Visibility = Visibility.Hidden;
//QueryWindowButton.Visibility = Visibility.Hidden;
}
} }
private void Window_Loaded(object sender, RoutedEventArgs evt) { private void Window_Loaded(object sender, RoutedEventArgs evt) { }
PdfButton.IsEnabled = App.IsPrintingReady;
}
private void MemberAdminButton_Click(object sender, RoutedEventArgs evt) { private void MemberAdminButton_Click(object sender, RoutedEventArgs evt) {
var w = new MemberAdminWindow(); var w = new MemberAdminWindow();
@ -38,15 +40,6 @@ namespace Elwig.Windows {
// TODO // TODO
} }
private void PdfButton_Click(object sender, RoutedEventArgs evt) {
Utils.RunBackground("PDF Generation", async () => {
using var ctx = new AppDbContext();
using var doc = new DeliveryNote(ctx.Deliveries.OrderBy(d => d.Parts.Count).ThenBy(d => d.Year).ThenBy(d => d.DId).Last(), ctx);
await doc.Generate();
doc.Show();
});
}
private void TestWindowButton_Click(object sender, RoutedEventArgs evt) { private void TestWindowButton_Click(object sender, RoutedEventArgs evt) {
var w = new TestWindow(); var w = new TestWindow();
w.Show(); w.Show();
@ -62,8 +55,8 @@ namespace Elwig.Windows {
w.Show(); w.Show();
} }
private void PaymentWindowButton_Click(object sender, RoutedEventArgs e) { private void SeasonFinishButton_Click(object sender, RoutedEventArgs e) {
var w = new ChartWindow(); var w = new SeasonFinishWindow();
w.Show(); w.Show();
} }
} }

View File

@ -4,7 +4,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Elwig.Windows" xmlns:local="clr-namespace:Elwig.Windows"
Title="Mitgliederverwaltung - Elwig" Height="670" Width="1250" MinHeight="600" MinWidth="1000" Title="Mitglieder - Elwig" Height="700" Width="1250" MinHeight="650" MinWidth="1150"
Loaded="Window_Loaded"> Loaded="Window_Loaded">
<Window.Resources> <Window.Resources>
<Style TargetType="Label"> <Style TargetType="Label">
@ -36,16 +36,13 @@
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="19"/> <RowDefinition Height="19"/>
<RowDefinition Height="0.8*"/> <RowDefinition Height="1*"/>
<RowDefinition Height="0.8*"/> <RowDefinition Height="24"/>
<RowDefinition Height="0.2*"/>
<RowDefinition Height="1.3*"/>
<RowDefinition Height="0.8*"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="370"/> <ColumnDefinition Width="1*" MinWidth="300"/>
<ColumnDefinition Width="1*"/> <ColumnDefinition Width="5"/>
<ColumnDefinition Width="1*"/> <ColumnDefinition Width="2.5*" MinWidth="800"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Menu Grid.ColumnSpan="3" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White"> <Menu Grid.ColumnSpan="3" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White">
@ -54,8 +51,17 @@
Click="Menu_Member_SendEmail_Click"/> Click="Menu_Member_SendEmail_Click"/>
</MenuItem> </MenuItem>
<MenuItem Header="Drucken"> <MenuItem Header="Drucken">
<MenuItem x:Name="Menu_Print_Letterhead" Header="Briefkopf drucken"
Click="Menu_Print_Letterhead_Click"/>
<MenuItem Header="Stammdatenblatt drucken"/> <MenuItem Header="Stammdatenblatt drucken"/>
<MenuItem Header="Mitgliederliste drucken"/> <MenuItem Header="Briefköpfe drucken">
<MenuItem x:Name="Menu_Print_Letterheads_MgNr" Header="nach MgNr. sortiert" IsEnabled="False" Tag="Print"
Click="Menu_Print_Letterheads_MgNr_Click"/>
<MenuItem x:Name="Menu_Print_Letterheads_Name" Header="nach Name sortiert" IsEnabled="False" Tag="Print"
Click="Menu_Print_Letterheads_Name_Click"/>
<MenuItem x:Name="Menu_Print_Letterheads_Plz" Header="nach PLZ, Ort, Name sortiert" IsEnabled="False" Tag="Print"
Click="Menu_Print_Letterheads_Plz_Click"/>
</MenuItem>
</MenuItem> </MenuItem>
<MenuItem Header="Rundschreiben"> <MenuItem Header="Rundschreiben">
<MenuItem Header="Runschreiben ausschicken"/> <MenuItem Header="Runschreiben ausschicken"/>
@ -66,7 +72,7 @@
</MenuItem> </MenuItem>
</Menu> </Menu>
<Grid Grid.RowSpan="5" Grid.Row="1" Margin="5,0,5,0"> <Grid Grid.Row="1" Margin="5,0,0,0">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="39"/> <RowDefinition Height="39"/>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
@ -88,9 +94,23 @@
SelectionChanged="MemberList_SelectionChanged" SelectionChanged="MemberList_SelectionChanged"
Margin="5,0,5,0" Grid.Row="1" FontSize="14" Grid.ColumnSpan="3"> Margin="5,0,5,0" Grid.Row="1" FontSize="14" Grid.ColumnSpan="3">
<DataGrid.Columns> <DataGrid.Columns>
<DataGridTextColumn Header="MgNr." Binding="{Binding MgNr}" Width="70"/> <DataGridTextColumn Header="MgNr." Binding="{Binding MgNr, StringFormat='{}{0} '}" Width="50">
<DataGridTextColumn Header="Nachname" Binding="{Binding FamilyName}" Width="4*"/> <DataGridTextColumn.CellStyle>
<DataGridTextColumn Header="Vorname" Binding="{Binding GivenName}" Width="3*"/> <Style>
<Setter Property="TextBlock.TextAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Nachname" Binding="{Binding FamilyName}" Width="140"/>
<DataGridTextColumn Header="Vorname" Binding="{Binding GivenName}" Width="140"/>
<DataGridTextColumn Header="GA" Binding="{Binding BusinessShares, StringFormat='{}{0} '}" Width="40">
<DataGridTextColumn.CellStyle>
<Style>
<Setter Property="TextBlock.TextAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Rechnungsadresse" Binding="{Binding BillingAddress.Name}" Width="200"/>
</DataGrid.Columns> </DataGrid.Columns>
</DataGrid> </DataGrid>
@ -115,252 +135,307 @@
Click="CancelButton_Click"/> Click="CancelButton_Click"/>
</Grid> </Grid>
<GroupBox Header="Persönliche Daten" Grid.Column="1" Grid.Row="1" Grid.RowSpan="2" Margin="5,5,5,5"> <GridSplitter Grid.Column="1" Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Label Content="MgNr.:" Margin="10,10,0,0" Grid.Column="0"/> <Grid Grid.Column="2" Grid.Row="1">
<TextBox x:Name="MgNrInput" Margin="0,10,0,0" Width="48" Grid.Column="1" TextAlignment="Right" HorizontalAlignment="Left" <Grid.RowDefinitions>
TextChanged="MgNrInput_TextChanged" LostFocus="MgNrInput_LostFocus"/> <RowDefinition Height="0.8*"/>
<RowDefinition Height="0.8*"/>
<RowDefinition Height="0.2*"/>
<RowDefinition Height="1.3*"/>
<RowDefinition Height="0.8*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="Vorg.:" Margin="10,10,0,0" Grid.Column="2"/> <GroupBox Header="Persönliche Daten" Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" Margin="5,5,5,5">
<TextBox x:Name="PredecessorMgNrInput" Margin="0,10,10,0" Width="48" Grid.Column="3" TextAlignment="Right" HorizontalAlignment="Left" <Grid>
TextChanged="PredecessorMgNrInput_TextChanged" LostFocus="PredecessorMgNrInput_LostFocus"/> <Grid.ColumnDefinitions>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Label Content="Präfix:" Margin="10,40,0,0" Grid.Column="2"/> <Label Content="MgNr.:" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="PrefixInput" Margin="0,40,10,0" Grid.Column="3" <TextBox x:Name="MgNrInput" Margin="0,10,0,0" Width="48" Grid.Column="1" TextAlignment="Right" HorizontalAlignment="Left"
TextChanged="TextBox_TextChanged"/> TextChanged="MgNrInput_TextChanged" LostFocus="MgNrInput_LostFocus"/>
<Label Content="Vorname:" Margin="10,40,0,0" Grid.Column="0"/> <Label Content="Vorg.:" Margin="10,10,0,0" Grid.Column="2"/>
<TextBox x:Name="GivenNameInput" Margin="0,40,0,0" Grid.Column="1" <TextBox x:Name="PredecessorMgNrInput" Margin="0,10,10,0" Width="48" Grid.Column="3" TextAlignment="Right" HorizontalAlignment="Left"
TextChanged="TextBox_TextChanged"/> TextChanged="PredecessorMgNrInput_TextChanged" LostFocus="PredecessorMgNrInput_LostFocus"/>
<Label Content="Nachname:" Margin="10,70,0,0" Grid.Column="0"/> <Label Content="Präfix:" Margin="10,40,0,0" Grid.Column="2"/>
<TextBox x:Name="FamilyNameInput" Margin="0,70,0,0" Grid.Column="1" <TextBox x:Name="PrefixInput" Margin="0,40,10,0" Grid.Column="3"
TextChanged="TextBox_TextChanged"/> TextChanged="TextBox_TextChanged"/>
<Label Content="Suffix:" Margin="10,70,0,0" Grid.Column="2"/> <Label Content="Vorname:" Margin="10,40,0,0" Grid.Column="0"/>
<TextBox x:Name="SuffixInput" Margin="0,70,10,0" Grid.Column="3" <TextBox x:Name="GivenNameInput" Margin="0,40,0,0" Grid.Column="1"
TextChanged="TextBox_TextChanged"/> TextChanged="TextBox_TextChanged"/>
<Label Content="Geburtstag:" Margin="10,100,0,0" Grid.Column="0"/> <Label Content="Nachname:" Margin="10,70,0,0" Grid.Column="0"/>
<TextBox x:Name="BirthdayInput" Margin="0,100,0,0" Grid.Column="1" Width="78" TextAlignment="Right" HorizontalAlignment="Left" <TextBox x:Name="FamilyNameInput" Margin="0,70,0,0" Grid.Column="1"
TextChanged="PartialDateInput_TextChanged" LostFocus="PartialDateInput_LostFocus"/> TextChanged="TextBox_TextChanged"/>
<Label Content="Alter:" Margin="85,100,0,0" Grid.Column="1" Grid.ColumnSpan="3"/> <Label Content="Suffix:" Margin="10,70,0,0" Grid.Column="2"/>
<TextBlock x:Name="Age" Text="-" Margin="119,104,0,0" Grid.Column="1" Grid.ColumnSpan="3" TextWrapping="NoWrap" VerticalAlignment="Top"/> <TextBox x:Name="SuffixInput" Margin="0,70,10,0" Grid.Column="3"
TextChanged="TextBox_TextChanged"/>
<CheckBox x:Name="DeceasedInput" Content="Verstorben" IsEnabled="False" <Label Content="Geburtstag:" Margin="10,100,0,0" Grid.Column="0"/>
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed" <TextBox x:Name="BirthdayInput" Margin="0,100,0,0" Grid.Column="1" Width="78" TextAlignment="Right" HorizontalAlignment="Left"
Grid.Column="3" HorizontalAlignment="Left" Margin="0,105,0,0" VerticalAlignment="Top" IsChecked="False"/> TextChanged="PartialDateInput_TextChanged" LostFocus="PartialDateInput_LostFocus"/>
<Label Content="Adresse:" Margin="10,130,0,0"/> <Label Content="Alter:" Margin="85,100,0,0" Grid.Column="1" Grid.ColumnSpan="3"/>
<TextBox x:Name="AddressInput" Margin="0,130,10,0" Grid.Column="1" Grid.ColumnSpan="3" <TextBlock x:Name="Age" Text="-" Margin="119,104,0,0" Grid.Column="1" Grid.ColumnSpan="3" TextWrapping="NoWrap" VerticalAlignment="Top"/>
TextChanged="TextBox_TextChanged"/>
<Label Content="PLZ/Ort:" Margin="10,160,0,0" Grid.Column="0"/> <CheckBox x:Name="DeceasedInput" Content="Verstorben" IsEnabled="False"
<TextBox x:Name="PlzInput" Margin="0,160,0,0" Width="42" Grid.Column="1" HorizontalAlignment="Left" Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
TextChanged="PlzInput_TextChanged" LostFocus="PlzInput_LostFocus" Tag="PLZ"/> Grid.Column="3" HorizontalAlignment="Left" Margin="0,105,0,0" VerticalAlignment="Top" IsChecked="False"/>
<ComboBox x:Name="OrtInput" ItemTemplate="{StaticResource PostalDestTemplate}" TextSearch.TextPath="Ort.Name"
Margin="47,160,10,0" Grid.Column="1" Grid.ColumnSpan="3"/>
</Grid>
</GroupBox>
<GroupBox Header="Kontaktdaten" Grid.Column="1" Grid.Row="3" Grid.RowSpan="2" Margin="5,5,5,5">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="135"/>
<ColumnDefinition Width="*" MinWidth="180"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Label Content="E-Mail-Adresse:" Margin="10,10,0,0" Grid.Column="0"/> <Label Content="Adresse:" Margin="10,130,0,0"/>
<TextBox x:Name="EmailAddressInput" Margin="0,10,10,0" Grid.Column="1" Grid.ColumnSpan="2" <TextBox x:Name="AddressInput" Margin="0,130,10,0" Grid.Column="1" Grid.ColumnSpan="3"
TextChanged="EmailAddressInput_TextChanged" LostFocus="EmailAddressInput_LostFocus"/> TextChanged="TextBox_TextChanged"/>
<ComboBox x:Name="PhoneNr1TypeInput" DisplayMemberPath="Value" Margin="6,40,5,0" FontSize="12" Padding="6,4,4,4"/> <Label Content="PLZ/Ort:" Margin="10,160,0,0" Grid.Column="0"/>
<TextBox x:Name="PhoneNr1Input" Margin="0,40,5,0" Grid.Column="1" <TextBox x:Name="PlzInput" Margin="0,160,0,0" Width="42" Grid.Column="1" HorizontalAlignment="Left"
TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/> TextChanged="PlzInput_TextChanged" LostFocus="PlzInput_LostFocus" Tag="PLZ"/>
<TextBox x:Name="PhoneNr1CommentInput" Margin="0,40,10,0" Grid.Column="2" <ComboBox x:Name="OrtInput" ItemTemplate="{StaticResource PostalDestTemplate}" TextSearch.TextPath="Ort.Name"
TextChanged="TextBox_TextChanged"/> Margin="47,160,10,0" Grid.Column="1" Grid.ColumnSpan="3"/>
</Grid>
</GroupBox>
<GroupBox Header="Kontaktdaten" Grid.Column="0" Grid.Row="2" Grid.RowSpan="2" Margin="5,5,5,5">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="135"/>
<ColumnDefinition Width="*" MinWidth="180"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<ComboBox x:Name="PhoneNr2TypeInput" DisplayMemberPath="Value" Margin="6,70,5,0" FontSize="12" Padding="6,4,4,4"/> <Label Content="E-Mail-Adresse (1):" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="PhoneNr2Input" Margin="0,70,5,0" Grid.Column="1" <TextBox x:Name="EmailAddress1Input" Margin="0,10,10,0" Grid.Column="1" Grid.ColumnSpan="2"
TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/> TextChanged="EmailAddressInput_TextChanged" LostFocus="EmailAddressInput_LostFocus"/>
<TextBox x:Name="PhoneNr2CommentInput" Margin="0,70,10,0" Grid.Column="2"
TextChanged="TextBox_TextChanged"/>
<ComboBox x:Name="PhoneNr3TypeInput" DisplayMemberPath="Value" Margin="6,100,5,0" FontSize="12" Padding="6,4,4,4"/> <Label Content="E-Mail-Adresse (2):" Margin="10,40,0,0" Grid.Column="0"/>
<TextBox x:Name="PhoneNr3Input" Margin="0,100,5,0" Grid.Column="1" <TextBox x:Name="EmailAddress2Input" Margin="0,40,10,0" Grid.Column="1" Grid.ColumnSpan="2"
TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/> TextChanged="EmailAddressInput_TextChanged" LostFocus="EmailAddressInput_LostFocus"/>
<TextBox x:Name="PhoneNr3CommentInput" Margin="0,100,10,0" Grid.Column="2"
TextChanged="TextBox_TextChanged"/>
<ComboBox x:Name="PhoneNr4TypeInput" DisplayMemberPath="Value" Margin="6,130,5,0" FontSize="12" Padding="6,4,4,4"/> <ComboBox x:Name="PhoneNr1TypeInput" DisplayMemberPath="Value" Margin="6,70,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="PhoneNr4Input" Margin="0,130,5,0" Grid.Column="1" <TextBox x:Name="PhoneNr1Input" Margin="0,70,5,0" Grid.Column="1"
TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/> TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/>
<TextBox x:Name="PhoneNr4CommentInput" Margin="0,130,10,0" Grid.Column="2" <TextBox x:Name="PhoneNr1CommentInput" Margin="0,70,10,0" Grid.Column="2"
TextChanged="TextBox_TextChanged"/> TextChanged="TextBox_TextChanged"/>
<ComboBox x:Name="PhoneNr5TypeInput" DisplayMemberPath="Value" Margin="6,160,5,0" FontSize="12" Padding="6,4,4,4"/> <ComboBox x:Name="PhoneNr2TypeInput" DisplayMemberPath="Value" Margin="6,100,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="PhoneNr5Input" Margin="0,160,5,0" Grid.Column="1" <TextBox x:Name="PhoneNr2Input" Margin="0,100,5,0" Grid.Column="1"
TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/> TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/>
<TextBox x:Name="PhoneNr5CommentInput" Margin="0,160,10,0" Grid.Column="2" <TextBox x:Name="PhoneNr2CommentInput" Margin="0,100,10,0" Grid.Column="2"
TextChanged="TextBox_TextChanged"/> TextChanged="TextBox_TextChanged"/>
<ComboBox x:Name="PhoneNr6TypeInput" DisplayMemberPath="Value" Margin="6,190,5,0" FontSize="12" Padding="6,4,4,4"/> <ComboBox x:Name="PhoneNr3TypeInput" DisplayMemberPath="Value" Margin="6,130,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="PhoneNr6Input" Margin="0,190,5,0" Grid.Column="1" <TextBox x:Name="PhoneNr3Input" Margin="0,130,5,0" Grid.Column="1"
TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/> TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/>
<TextBox x:Name="PhoneNr6CommentInput" Margin="0,190,10,0" Grid.Column="2" <TextBox x:Name="PhoneNr3CommentInput" Margin="0,130,10,0" Grid.Column="2"
TextChanged="TextBox_TextChanged"/> TextChanged="TextBox_TextChanged"/>
<ComboBox x:Name="PhoneNr7TypeInput" DisplayMemberPath="Value" Margin="6,220,5,0" FontSize="12" Padding="6,4,4,4"/> <ComboBox x:Name="PhoneNr4TypeInput" DisplayMemberPath="Value" Margin="6,160,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="PhoneNr7Input" Margin="0,220,5,0" Grid.Column="1" <TextBox x:Name="PhoneNr4Input" Margin="0,160,5,0" Grid.Column="1"
TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/> TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/>
<TextBox x:Name="PhoneNr7CommentInput" Margin="0,220,10,0" Grid.Column="2" <TextBox x:Name="PhoneNr4CommentInput" Margin="0,160,10,0" Grid.Column="2"
TextChanged="TextBox_TextChanged"/> TextChanged="TextBox_TextChanged"/>
<ComboBox x:Name="PhoneNr8TypeInput" DisplayMemberPath="Value" Margin="6,250,5,0" FontSize="12" Padding="6,4,4,4"/> <ComboBox x:Name="PhoneNr5TypeInput" DisplayMemberPath="Value" Margin="6,190,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="PhoneNr8Input" Margin="0,250,5,0" Grid.Column="1" <TextBox x:Name="PhoneNr5Input" Margin="0,190,5,0" Grid.Column="1"
TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/> TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/>
<TextBox x:Name="PhoneNr8CommentInput" Margin="0,250,10,0" Grid.Column="2" <TextBox x:Name="PhoneNr5CommentInput" Margin="0,190,10,0" Grid.Column="2"
TextChanged="TextBox_TextChanged"/> TextChanged="TextBox_TextChanged"/>
<ComboBox x:Name="PhoneNr9TypeInput" DisplayMemberPath="Value" Margin="6,280,5,0" FontSize="12" Padding="6,4,4,4"/> <ComboBox x:Name="PhoneNr6TypeInput" DisplayMemberPath="Value" Margin="6,220,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="PhoneNr9Input" Margin="0,280,5,0" Grid.Column="1" <TextBox x:Name="PhoneNr6Input" Margin="0,220,5,0" Grid.Column="1"
TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/> TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/>
<TextBox x:Name="PhoneNr9CommentInput" Margin="0,280,10,0" Grid.Column="2" <TextBox x:Name="PhoneNr6CommentInput" Margin="0,220,10,0" Grid.Column="2"
TextChanged="TextBox_TextChanged"/> TextChanged="TextBox_TextChanged"/>
</Grid>
</GroupBox>
<GroupBox Header="Bankverbindung" Grid.Column="1" Grid.Row="5" Margin="5,5,5,10">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="65"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="IBAN:" Margin="10,10,0,0" Grid.Column="0"/> <ComboBox x:Name="PhoneNr7TypeInput" DisplayMemberPath="Value" Margin="6,250,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="IbanInput" Margin="0,10,10,0" Grid.Column="1" <TextBox x:Name="PhoneNr7Input" Margin="0,250,5,0" Grid.Column="1"
TextChanged="IbanInput_TextChanged" LostFocus="IbanInput_LostFocus"/> TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/>
<TextBox x:Name="PhoneNr7CommentInput" Margin="0,250,10,0" Grid.Column="2"
TextChanged="TextBox_TextChanged"/>
<Label Content="BIC:" Margin="10,40,0,0" Grid.Column="0"/> <ComboBox x:Name="PhoneNr8TypeInput" DisplayMemberPath="Value" Margin="6,280,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="BicInput" Margin="0,40,10,0" Grid.Column="1" <TextBox x:Name="PhoneNr8Input" Margin="0,280,5,0" Grid.Column="1"
TextChanged="BicInput_TextChanged" LostFocus="BicInput_LostFocus"/> TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/>
</Grid> <TextBox x:Name="PhoneNr8CommentInput" Margin="0,280,10,0" Grid.Column="2"
</GroupBox> TextChanged="TextBox_TextChanged"/>
<GroupBox Header="Betrieb" Grid.Column="2" Grid.Row="1" Grid.RowSpan="1" Margin="5,5,5,5">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="90"/>
<ColumnDefinition Width="150"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="UID:" Margin="10,10,0,0" Grid.Column="0"/> <ComboBox x:Name="PhoneNr9TypeInput" DisplayMemberPath="Value" Margin="6,310,5,0" FontSize="12" Padding="6,4,4,4"/>
<TextBox x:Name="UstIdNrInput" Margin="0,10,10,0" Grid.Column="1" Width="96" HorizontalAlignment="Left" <TextBox x:Name="PhoneNr9Input" Margin="0,310,5,0" Grid.Column="1"
TextChanged="UstIdNrInput_TextChanged" LostFocus="UstIdNrInput_LostFocus"/> TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/>
<TextBox x:Name="PhoneNr9CommentInput" Margin="0,280,10,0" Grid.Column="2"
TextChanged="TextBox_TextChanged"/>
</Grid>
</GroupBox>
<GroupBox Header="Bankverbindung" Grid.Column="0" Grid.Row="4" Margin="5,5,5,10">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="65"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="Betriebs-Nr.:" Margin="10,40,0,0" Grid.Column="0"/> <Label Content="IBAN:" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="LfbisNrInput" Margin="0,40,10,0" Grid.Column="1" Width="64" HorizontalAlignment="Left" TextAlignment="Right" <TextBox x:Name="IbanInput" Margin="0,10,10,0" Grid.Column="1"
TextChanged="LfbisNrInput_TextChanged" LostFocus="LfbisNrInput_LostFocus"/> TextChanged="IbanInput_TextChanged" LostFocus="IbanInput_LostFocus"/>
<CheckBox x:Name="BuchführendInput" Content="Buchführend" IsEnabled="False" <Label Content="BIC:" Margin="10,40,0,0" Grid.Column="0"/>
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed" <TextBox x:Name="BicInput" Margin="0,40,10,0" Grid.Column="1"
Grid.Column="2" HorizontalAlignment="Left" Margin="10,15,0,0" VerticalAlignment="Top" IsChecked="False"/> TextChanged="BicInput_TextChanged" LostFocus="BicInput_LostFocus"/>
</Grid> </Grid>
</GroupBox> </GroupBox>
<GroupBox Header="Rechnungsadresse (optional)" Grid.Column="2" Grid.Row="2" Grid.RowSpan="2" Margin="5,5,5,5"> <GroupBox Header="Betrieb" Grid.Column="1" Grid.Row="0" Grid.RowSpan="1" Margin="5,5,5,5">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="65"/> <ColumnDefinition Width="90"/>
<ColumnDefinition/> <ColumnDefinition Width="150"/>
</Grid.ColumnDefinitions> <ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="Name:" Margin="10,10,0,0" Grid.Column="0"/> <Label Content="UID:" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="BillingNameInput" Margin="0,10,10,10" Grid.Column="1" Grid.ColumnSpan="2" <TextBox x:Name="UstIdNrInput" Margin="0,10,10,0" Grid.Column="1" Width="96" HorizontalAlignment="Left"
TextChanged="TextBox_TextChanged"/> TextChanged="UstIdNrInput_TextChanged" LostFocus="UstIdNrInput_LostFocus"/>
<Label Content="Adresse:" Margin="10,40,0,0" Grid.Column="0"/> <Label Content="Betriebs-Nr.:" Margin="10,40,0,0" Grid.Column="0"/>
<TextBox x:Name="BillingAddressInput" Margin="0,40,10,0" Grid.Column="1" <TextBox x:Name="LfbisNrInput" Margin="0,40,10,0" Grid.Column="1" Width="64" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="TextBox_TextChanged"/> TextChanged="LfbisNrInput_TextChanged" LostFocus="LfbisNrInput_LostFocus"/>
<Label Content="PLZ/Ort:" Margin="10,70,0,0" Grid.Column="0"/> <CheckBox x:Name="BuchführendInput" Content="Buchführend" IsEnabled="False"
<TextBox x:Name="BillingPlzInput" Margin="0,70,0,0" Width="42" Grid.Column="1" HorizontalAlignment="Left" Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
TextChanged="PlzInput_TextChanged" LostFocus="PlzInput_LostFocus" Tag="PLZ"/> Grid.Column="2" HorizontalAlignment="Left" Margin="10,15,0,0" VerticalAlignment="Top" IsChecked="False"/>
<ComboBox x:Name="BillingOrtInput" ItemTemplate="{StaticResource PostalDestTemplate}" TextSearch.TextPath="Ort.Name" </Grid>
Margin="47,70,10,0" Grid.Column="1"/> </GroupBox>
</Grid> <GroupBox Header="Rechnungsadresse (optional)" Grid.Column="1" Grid.Row="1" Grid.RowSpan="2" Margin="5,5,5,5">
</GroupBox> <Grid>
<GroupBox Header="Genossenschaft" Grid.Column="2" Grid.Row="4" Grid.RowSpan="2" Margin="5,5,5,10"> <Grid.ColumnDefinitions>
<Grid> <ColumnDefinition Width="65"/>
<Grid.ColumnDefinitions> <ColumnDefinition/>
<ColumnDefinition Width="120"/> </Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="Eintritt:" Margin="10,10,0,0" Grid.Column="0"/> <Label Content="Name:" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="EntryDateInput" Margin="0,10,10,0" Width="78" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right" <TextBox x:Name="BillingNameInput" Margin="0,10,10,10" Grid.Column="1" Grid.ColumnSpan="2"
TextChanged="DateInput_TextChanged" LostFocus="DateInput_LostFocus"/> TextChanged="TextBox_TextChanged"/>
<Label Content="Austritt:" Margin="10,40,0,0" Grid.Column="0"/> <Label Content="Adresse:" Margin="10,40,0,0" Grid.Column="0"/>
<TextBox x:Name="ExitDateInput" Margin="0,40,10,0" Width="78" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right" <TextBox x:Name="BillingAddressInput" Margin="0,40,10,0" Grid.Column="1"
TextChanged="DateInput_TextChanged" LostFocus="DateInput_LostFocus"/> TextChanged="TextBox_TextChanged"/>
<Label Content="Geschäftsanteile:" Margin="10,70,0,0" Grid.Column="0"/> <Label Content="PLZ/Ort:" Margin="10,70,0,0" Grid.Column="0"/>
<TextBox x:Name="BusinessSharesInput" Margin="0,70,10,0" Width="48" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right" <TextBox x:Name="BillingPlzInput" Margin="0,70,0,0" Width="42" Grid.Column="1" HorizontalAlignment="Left"
TextChanged="IntegerInput_TextChanged"/> TextChanged="PlzInput_TextChanged" LostFocus="PlzInput_LostFocus" Tag="PLZ"/>
<ComboBox x:Name="BillingOrtInput" ItemTemplate="{StaticResource PostalDestTemplate}" TextSearch.TextPath="Ort.Name"
Margin="47,70,10,0" Grid.Column="1"/>
</Grid>
</GroupBox>
<GroupBox Header="Genossenschaft" Grid.Column="1" Grid.Row="3" Grid.RowSpan="2" Margin="5,5,5,10">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="120"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="BH-Konto:" Margin="10,100,0,0" Grid.Column="0"/> <Label Content="Eintritt:" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="AccountingNrInput" Margin="0,100,10,0" Grid.Column="1" <TextBox x:Name="EntryDateInput" Margin="0,10,10,0" Width="78" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="TextBox_TextChanged"/> TextChanged="DateInput_TextChanged" LostFocus="DateInput_LostFocus"/>
<CheckBox x:Name="ActiveInput" Content="Aktiv" IsEnabled="False" <Label Content="Austritt:" Margin="10,40,0,0" Grid.Column="0"/>
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed" <TextBox x:Name="ExitDateInput" Margin="0,40,10,0" Width="78" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right"
Grid.Column="2" HorizontalAlignment="Left" Margin="10,15,0,0" VerticalAlignment="Top" IsChecked="False"/> TextChanged="DateInput_TextChanged" LostFocus="DateInput_LostFocus"/>
<CheckBox x:Name="VollLieferantInput" Content="Volllieferant" IsEnabled="False" <Label Content="Geschäftsanteile:" Margin="10,70,0,0" Grid.Column="0"/>
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed" <TextBox x:Name="BusinessSharesInput" Margin="0,70,10,0" Width="48" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right"
Grid.Column="2" HorizontalAlignment="Left" Margin="10,45,0,0" VerticalAlignment="Top" IsChecked="False"/> TextChanged="IntegerInput_TextChanged"/>
<CheckBox x:Name="FunkionärInput" Content="Funktionär" IsEnabled="False" <Label Content="BH-Konto:" Margin="10,100,0,0" Grid.Column="0"/>
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed" <TextBox x:Name="AccountingNrInput" Margin="0,100,10,0" Grid.Column="1"
Grid.Column="2" HorizontalAlignment="Left" Margin="10,75,0,0" VerticalAlignment="Top" IsChecked="False"/> TextChanged="TextBox_TextChanged"/>
<Label Content="Stamm-Zwst.:" Margin="10,130,0,0" Grid.Column="0"/> <CheckBox x:Name="ActiveInput" Content="Aktiv" IsEnabled="False"
<ComboBox x:Name="BranchInput" DisplayMemberPath="Name" TextSearch.TextPath="Name" Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
Margin="0,130,10,0" Grid.Column="1" Grid.ColumnSpan="2"/> Grid.Column="2" HorizontalAlignment="Left" Margin="10,15,0,0" VerticalAlignment="Top" IsChecked="False"/>
<Label Content="Stammgemeinde:" Margin="10,160,0,0" Grid.Column="0"/> <CheckBox x:Name="VollLieferantInput" Content="Volllieferant" IsEnabled="False"
<ComboBox x:Name="DefaultKgInput" DisplayMemberPath="Name" TextSearch.TextPath="Name" Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
Margin="0,160,10,0" Grid.Column="1" Grid.ColumnSpan="2"/> Grid.Column="2" HorizontalAlignment="Left" Margin="10,45,0,0" VerticalAlignment="Top" IsChecked="False"/>
<Label Content="Anmerkung:" Margin="10,190,0,0" Grid.Column="0"/> <CheckBox x:Name="FunkionärInput" Content="Funktionär" IsEnabled="False"
<TextBox x:Name="CommentInput" Margin="0,190,10,0" Grid.Column="1" Grid.ColumnSpan="2" Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
TextChanged="TextBox_TextChanged"/> Grid.Column="2" HorizontalAlignment="Left" Margin="10,75,0,0" VerticalAlignment="Top" IsChecked="False"/>
<Label Content="Kontaktart:" Margin="10,220,0,0" Grid.Column="0"/> <Label Content="Stamm-Zwst.:" Margin="10,130,0,0" Grid.Column="0"/>
<CheckBox x:Name="ContactPostalInput" Content="Post" IsEnabled="False" <ComboBox x:Name="BranchInput" DisplayMemberPath="Name" TextSearch.TextPath="Name"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed" Margin="0,130,10,0" Grid.Column="1" Grid.ColumnSpan="2"/>
HorizontalAlignment="Left" Margin="0,225,0,0" VerticalAlignment="Top" Grid.Column="1" Grid.ColumnSpan="2"/>
<CheckBox x:Name="ContactEmailInput" Content="E-Mail" IsEnabled="False"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
HorizontalAlignment="Left" Margin="60,225,0,0" VerticalAlignment="Top" Grid.Column="1" Grid.ColumnSpan="2"/>
<Label Content="Gebundene Fläche:" Margin="10,250,0,0" Grid.Column="0"/> <Label Content="Stammgemeinde:" Margin="10,160,0,0" Grid.Column="0"/>
<TextBlock x:Name="AreaCommitment" Text="- m²" <ComboBox x:Name="DefaultKgInput" DisplayMemberPath="Name" TextSearch.TextPath="Name"
Grid.Column="1" HorizontalAlignment="Stretch" Margin="5,252,5,0" TextWrapping="NoWrap" VerticalAlignment="Top" FontSize="14" TextAlignment="Right"/> Margin="0,160,10,0" Grid.Column="1" Grid.ColumnSpan="2"/>
<Button x:Name="DeliveryButton" Content="Lieferungen" Click="DeliveryButton_Click" IsEnabled="False" <Label Content="Anmerkung:" Margin="10,190,0,0" Grid.Column="0"/>
HorizontalAlignment="Right" Margin="10,00,10,37" Width="150" VerticalAlignment="Bottom" Grid.ColumnSpan="3"/> <TextBox x:Name="CommentInput" Margin="0,190,10,0" Grid.Column="1" Grid.ColumnSpan="2"
<Button x:Name="AreaCommitmentButton" Content="Flächenbindungen" Click="AreaCommitmentButton_Click" IsEnabled="False" TextChanged="TextBox_TextChanged"/>
HorizontalAlignment="Right" Margin="10,10,10,5" Width="150" VerticalAlignment="Bottom" Grid.ColumnSpan="3"/>
</Grid> <Label Content="Kontaktart:" Margin="10,220,0,0" Grid.Column="0"/>
</GroupBox> <CheckBox x:Name="ContactPostalInput" Content="Post" IsEnabled="False"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
HorizontalAlignment="Left" Margin="0,225,0,0" VerticalAlignment="Top" Grid.Column="1" Grid.ColumnSpan="2"/>
<CheckBox x:Name="ContactEmailInput" Content="E-Mail" IsEnabled="False"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
HorizontalAlignment="Left" Margin="60,225,0,0" VerticalAlignment="Top" Grid.Column="1" Grid.ColumnSpan="2"/>
<Button x:Name="DeliveryButton" Content="Lieferungen" Click="DeliveryButton_Click" IsEnabled="False"
HorizontalAlignment="Right" Margin="10,00,10,37" Width="150" VerticalAlignment="Bottom" Grid.ColumnSpan="3"/>
<Button x:Name="AreaCommitmentButton" Content="Flächenbindungen" Click="AreaCommitmentButton_Click" IsEnabled="False"
HorizontalAlignment="Right" Margin="10,10,10,5" Width="150" VerticalAlignment="Bottom" Grid.ColumnSpan="3"/>
</Grid>
</GroupBox>
</Grid>
<StatusBar Grid.Row="5" Grid.ColumnSpan="3" BorderThickness="0,1,0,0" BorderBrush="Gray">
<StatusBar.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="3*"/>
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</StatusBar.ItemsPanel>
<StatusBarItem>
<TextBlock Name="StatusMembers" Text="Mitglieder: -"/>
</StatusBarItem>
<Separator Grid.Column="1"/>
<StatusBarItem Grid.Column="2">
<TextBlock Name="StatusBusinessShares" Text="Geschäftsanteile: -"/>
</StatusBarItem>
<Separator Grid.Column="3"/>
<StatusBarItem Grid.Column="4">
<TextBlock Name="StatusAreaCommitment" Text="Gebundene Fläche: -"/>
</StatusBarItem>
<Separator Grid.Column="5"/>
<StatusBarItem Grid.Column="6">
<TextBlock Name="StatusDeliveriesLastSeason" Text="Lieferungen (letzte Saison): -"/>
</StatusBarItem>
<Separator Grid.Column="7"/>
<StatusBarItem Grid.Column="8">
<TextBlock Name="StatusDeliveriesThisSeason" Text="Lieferungen (aktuelle Saison): -"/>
</StatusBarItem>
</StatusBar>
</Grid> </Grid>
</local:AdministrationWindow> </local:AdministrationWindow>

View File

@ -10,6 +10,7 @@ using Elwig.Models;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.ChangeTracking;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using Elwig.Documents;
namespace Elwig.Windows { namespace Elwig.Windows {
public partial class MemberAdminWindow : AdministrationWindow { public partial class MemberAdminWindow : AdministrationWindow {
@ -54,6 +55,9 @@ namespace Elwig.Windows {
} }
private void Window_Loaded(object sender, RoutedEventArgs evt) { private void Window_Loaded(object sender, RoutedEventArgs evt) {
Menu_Print_Letterheads_MgNr.IsEnabled = App.IsPrintingReady;
Menu_Print_Letterheads_Name.IsEnabled = App.IsPrintingReady;
ActiveMemberInput.IsChecked = true; ActiveMemberInput.IsChecked = true;
UpdatePhoneNrInputVisibility(); UpdatePhoneNrInputVisibility();
LockInputs(); LockInputs();
@ -89,7 +93,8 @@ namespace Elwig.Windows {
.ToList(); .ToList();
} }
ControlUtils.RenewItemsSource(MemberList, members, i => (i as Member)?.MgNr, MemberList_SelectionChanged, ControlUtils.RenewSourceDefault.IfOnly, !updateSort); ControlUtils.RenewItemsSource(MemberList, members, i => (i as Member)?.MgNr,
MemberList_SelectionChanged, TextFilter.Count > 0 ? ControlUtils.RenewSourceDefault.IfOnly : ControlUtils.RenewSourceDefault.None, !updateSort);
} }
private void RefreshInputs(bool validate = false) { private void RefreshInputs(bool validate = false) {
@ -130,11 +135,13 @@ namespace Elwig.Windows {
ValidateRequiredInputs(); ValidateRequiredInputs();
} }
protected override async Task RenewContext() { protected override async Task OnRenewContext() {
await base.RenewContext(); await base.OnRenewContext();
ControlUtils.RenewItemsSource(BranchInput, await Context.Branches.OrderBy(b => b.Name).ToListAsync(), i => (i as Branch)?.ZwstId); ControlUtils.RenewItemsSource(BranchInput, await Context.Branches.OrderBy(b => b.Name).ToListAsync(), i => (i as Branch)?.ZwstId);
ControlUtils.RenewItemsSource(DefaultKgInput, await Context.WbKgs.Select(k => k.AtKg).OrderBy(k => k.Name).ToListAsync(), i => (i as AT_Kg)?.KgNr); ControlUtils.RenewItemsSource(DefaultKgInput, await Context.WbKgs.Select(k => k.AtKg).OrderBy(k => k.Name).ToListAsync(), i => (i as AT_Kg)?.KgNr);
await RefreshMemberList(); await RefreshMemberList();
StatusMembers.Text = $"Mitglieder: {await Context.Members.CountAsync(m => m.IsActive):N0} ({await Context.Members.CountAsync():N0})";
StatusBusinessShares.Text = $"Geschäftsanteile: {await Context.Members.Where(m => m.IsActive).SumAsync(m => m.BusinessShares):N0} ({await Context.Members.SumAsync(m => m.BusinessShares):N0})";
} }
private void SetPhoneNrInput(int nr, string? type, string? number, string? comment) { private void SetPhoneNrInput(int nr, string? type, string? number, string? comment) {
@ -265,7 +272,71 @@ namespace Elwig.Windows {
} }
private void Menu_Member_SendEmail_Click(object sender, RoutedEventArgs evt) { private void Menu_Member_SendEmail_Click(object sender, RoutedEventArgs evt) {
Utils.MailTo(((Member)MemberList.SelectedItem).Email); if (MemberList.SelectedItem is not Member m)
return;
Utils.MailTo(m.EmailAddresses.Select(a => a.Address));
}
private async void Menu_Print_Letterhead_Click(object sender, RoutedEventArgs evt) {
if (MemberList.SelectedItem is not Member m)
return;
Mouse.OverrideCursor = Cursors.AppStarting;
using var doc = new Letterhead(m);
await doc.Generate();
Mouse.OverrideCursor = null;
if (App.Config.Debug) {
doc.Show();
} else {
await doc.Print();
}
}
private async Task PrintLetterheads(int ordering) {
var n = await Context.Members.CountAsync(m => m.IsActive);
var res = MessageBox.Show(
$"Sollen wirklich {n} Seiten gedruckt werden?", "Ausdruck Bestätigen",
MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
if (res != MessageBoxResult.Yes)
return;
Mouse.OverrideCursor = Cursors.AppStarting;
var members = Context.Members.Where(m => m.IsActive && m.ContactViaPost);
switch (ordering) {
case 0: members = members
.OrderBy(m => m.MgNr);
break;
case 1: members = members
.OrderBy(m => m.FamilyName)
.ThenBy(m => m.GivenName)
.ThenBy(m => m.MgNr);
break;
case 2: members = members
.OrderBy(m => m.PostalDest.AtPlz.Plz)
.ThenBy(m => m.PostalDest.AtPlz.Ort.Name)
.ThenBy(m => m.FamilyName)
.ThenBy(m => m.GivenName)
.ThenBy(m => m.MgNr);
break;
}
using var doc = Document.Merge((await members.ToListAsync()).Select(m => new Letterhead(m)));
await doc.Generate();
Mouse.OverrideCursor = null;
if (App.Config.Debug) {
doc.Show();
} else {
await doc.Print();
}
}
private async void Menu_Print_Letterheads_MgNr_Click(object sender, RoutedEventArgs evt) {
await PrintLetterheads(0);
}
private async void Menu_Print_Letterheads_Name_Click(object sender, RoutedEventArgs evt) {
await PrintLetterheads(1);
}
private async void Menu_Print_Letterheads_Plz_Click(object sender, RoutedEventArgs evt) {
await PrintLetterheads(2);
} }
private void FocusSearchInput(object sender, RoutedEventArgs evt) { private void FocusSearchInput(object sender, RoutedEventArgs evt) {
@ -348,8 +419,6 @@ namespace Elwig.Windows {
m.PostalDestId = ((AT_PlzDest)OrtInput.SelectedItem).Id; m.PostalDestId = ((AT_PlzDest)OrtInput.SelectedItem).Id;
m.Address = AddressInput.Text; m.Address = AddressInput.Text;
m.Email = (EmailAddressInput.Text == "") ? null : EmailAddressInput.Text;
m.Iban = (IbanInput.Text == "") ? null : IbanInput.Text.Replace(" ", ""); m.Iban = (IbanInput.Text == "") ? null : IbanInput.Text.Replace(" ", "");
m.Bic = (BicInput.Text == "") ? null : BicInput.Text; m.Bic = (BicInput.Text == "") ? null : BicInput.Text;
@ -423,6 +492,27 @@ namespace Elwig.Windows {
} }
} }
for (int i = 0; i < 2; i++) {
var input = i == 0 ? EmailAddress1Input : EmailAddress2Input;
var emailAddr = m.EmailAddresses.FirstOrDefault(a => a.Nr - 1 == i);
if (input.Text == "") {
if (emailAddr != null) {
Context.Remove(emailAddr);
}
} else {
MemberEmailAddr a = emailAddr ?? Context.CreateProxy<MemberEmailAddr>();
a.Nr = i + 1;
a.Address = input.Text;
a.Comment = null;
if (emailAddr == null) {
a.MgNr = newMgNr;
await Context.AddAsync(a);
} else {
Context.Update(a);
}
}
}
await Context.SaveChangesAsync(); await Context.SaveChangesAsync();
if (newMgNr != m.MgNr) { if (newMgNr != m.MgNr) {
@ -474,7 +564,10 @@ namespace Elwig.Windows {
OrtInput.SelectedItem = null; OrtInput.SelectedItem = null;
} }
EmailAddressInput.Text = m.Email; var emailAddrs = m.EmailAddresses.OrderBy(a => a.Nr).ToList();
EmailAddress1Input.Text = emailAddrs.Count > 0 ? emailAddrs[0].Address : "";
EmailAddress2Input.Text = emailAddrs.Count > 1 ? emailAddrs[1].Address : "";
var phoneNrs = m.TelephoneNumbers.OrderBy(p => p.Nr).ToList(); var phoneNrs = m.TelephoneNumbers.OrderBy(p => p.Nr).ToList();
for (int i = 0; i < PhoneNrInputs.Length; i++) { for (int i = 0; i < PhoneNrInputs.Length; i++) {
if (i < phoneNrs.Count) { if (i < phoneNrs.Count) {
@ -522,16 +615,24 @@ namespace Elwig.Windows {
ContactPostalInput.IsChecked = m.ContactViaPost; ContactPostalInput.IsChecked = m.ContactViaPost;
ContactEmailInput.IsChecked = m.ContactViaEmail; ContactEmailInput.IsChecked = m.ContactViaEmail;
AreaCommitment.Text = $"{m.ActiveAreaCommitments.Select(c => c.Area).Sum():N0} m²"; var d1 = Context.Deliveries.Where(d => d.Year == Utils.CurrentLastSeason && d.MgNr == m.MgNr);
var d2 = Context.Deliveries.Where(d => d.Year == Utils.CurrentLastSeason - 1 && d.MgNr == m.MgNr);
StatusDeliveriesLastSeason.Text = $"Lieferungen ({Utils.CurrentLastSeason - 1}): {d2.Count():N0} ({d2.Sum(d => d.Parts.Count):N0}), {d2.SelectMany(d => d.Parts).Sum(p => p.Weight):N0} kg";
StatusDeliveriesThisSeason.Text = $"Lieferungen ({Utils.CurrentLastSeason}): {d1.Count():N0} ({d1.Sum(d => d.Parts.Count):N0}), {d1.SelectMany(d => d.Parts).Sum(p => p.Weight):N0} kg";
StatusAreaCommitment.Text = $"Gebundene Fläche: {m.ActiveAreaCommitments.Select(c => c.Area).Sum():N0} m²";
Menu_Member_SendEmail.IsEnabled = m.Email != null; Menu_Member_SendEmail.IsEnabled = m.EmailAddresses.Count > 0;
Menu_Print_Letterhead.IsEnabled = true;
FinishInputFilling(); FinishInputFilling();
} }
new protected void ClearInputs(bool validate = false) { new protected void ClearInputs(bool validate = false) {
Menu_Member_SendEmail.IsEnabled = false; Menu_Member_SendEmail.IsEnabled = false;
AreaCommitment.Text = "- m²"; Menu_Print_Letterhead.IsEnabled = false;
StatusDeliveriesLastSeason.Text = $"Lieferungen ({Utils.CurrentLastSeason - 1}): -";
StatusDeliveriesThisSeason.Text = $"Lieferungen ({Utils.CurrentLastSeason}): -";
StatusAreaCommitment.Text = "Gebundene Fläche: -";
Age.Text = "-"; Age.Text = "-";
base.ClearInputs(validate); base.ClearInputs(validate);
} }

View File

@ -0,0 +1,50 @@
<local:ContextWindow x:Class="Elwig.Windows.SeasonFinishWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Elwig.Windows"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
mc:Ignorable="d"
Loaded="Window_Loaded"
Title="Leseabschluss - Elwig" Height="450" Width="800">
<Window.Resources>
<Style TargetType="Button">
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="9,3"/>
<Setter Property="Height" Value="32"/>
<Setter Property="Width" Value="200"/>
</Style>
</Window.Resources>
<Grid>
<Label Content="Saison:" Margin="50,40,0,0" VerticalAlignment="Top" HorizontalAlignment="Left" Padding="2,4,2,4" Height="25"/>
<xctk:IntegerUpDown Name="SeasonInput" Height="25" Width="56" FontSize="14" Minimum="1000" Maximum="9999"
Margin="110,40,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"
ValueChanged="SeasonInput_ValueChanged"/>
<Button x:Name="CalculateBinsButton" Content="Aufteilung Berechnen"
Click="CalculateBinsButton_Click"
Margin="50,80,0,0"/>
<CheckBox x:Name="AllowAttrIntoLowerBinsInput" Content="Erlauben Lieferungen auch auf (konfigurierte) &quot;schlechtere&quot; Flächenbindungen aufzuteilen" IsChecked="True"
VerticalAlignment="Top" HorizontalAlignment="Left" Margin="255,68,0,0"/>
<CheckBox x:Name="AvoidUnderDeliveriesInput" Content="Unterlieferungen durch Abzug bei &quot;besseren&quot; Flächenbindungen vermeiden" IsChecked="True"
VerticalAlignment="Top" HorizontalAlignment="Left" Margin="255,88,0,0"/>
<CheckBox x:Name="HonorGebundenInput" Margin="255,108,0,0" VerticalAlignment="Top">
<TextBlock>Bei Lieferungen das Feld <Italic>Gebunden</Italic> berücksichtigen</TextBlock>
</CheckBox>
<Button x:Name="DeliveryConfirmationButton" Content="Anlieferungsbestätigungen"
Click="DeliveryConfirmationButton_Click"
Margin="50,122,0,0"/>
<Button x:Name="OverUnderDeliveryButton" Content="Über-/Unterlieferungen"
Click="OverUnderDeliveryButton_Click"
Margin="50,164,0,0"/>
<Button x:Name="PaymentButton" Content="Auszahlung"
Click="PaymentButton_Click"
Margin="50,206,0,0"/>
</Grid>
</local:ContextWindow>

View File

@ -0,0 +1,155 @@
using Elwig.Dialogs;
using Elwig.Helpers;
using Elwig.Helpers.Billing;
using Microsoft.Win32;
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace Elwig.Windows {
public partial class SeasonFinishWindow : ContextWindow {
public SeasonFinishWindow() {
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs evt) {
SeasonInput.Value = Utils.CurrentLastSeason;
}
protected override async Task OnRenewContext() {
SeasonInput_ValueChanged(null, null);
}
private async void SeasonInput_ValueChanged(object? sender, RoutedEventArgs? evt) {
var s0 = await Context.Seasons.FindAsync(SeasonInput.Value);
var s1 = await Context.Seasons.FindAsync(SeasonInput.Value + 1);
var valid = (s0 != null);
var last = (s1 == null);
CalculateBinsButton.IsEnabled = valid && last;
DeliveryConfirmationButton.IsEnabled = valid;
OverUnderDeliveryButton.IsEnabled = valid;
}
private async void CalculateBinsButton_Click(object sender, RoutedEventArgs evt) {
if (SeasonInput.Value is not int year)
return;
CalculateBinsButton.IsEnabled = false;
Mouse.OverrideCursor = Cursors.AppStarting;
var b = new Billing(year);
await b.FinishSeason();
await b.CalculateBins(
AllowAttrIntoLowerBinsInput.IsChecked ?? false,
AvoidUnderDeliveriesInput.IsChecked ?? false,
HonorGebundenInput.IsChecked ?? false);
Mouse.OverrideCursor = null;
CalculateBinsButton.IsEnabled = true;
}
private void DeliveryConfirmationButton_Click(object sender, RoutedEventArgs evt) {
if (SeasonInput.Value is not int year)
return;
var w = new DeliveryConfirmationsWindow(year);
w.Show();
}
private async void OverUnderDeliveryButton_Click(object sender, RoutedEventArgs evt) {
if (SeasonInput.Value is not int year)
return;
var d = new SaveFileDialog() {
FileName = $"Über-Unterlieferungen-{year}.csv",
DefaultExt = "csv",
Filter = "CSV-Datei (*.csv)|*.csv",
Title = $"Über-/Unterlieferungen {year} speichern unter - Elwig"
};
if (d.ShowDialog() == false)
return;
Mouse.OverrideCursor = Cursors.AppStarting;
try {
using var file = new StreamWriter(d.FileName, false, Encoding.Latin1);
using var cnx = await AppDbContext.ConnectAsync();
await file.WriteLineAsync($"Auswertungen {year};;;;;;;;;;;");
await file.WriteLineAsync($";;;;;;;;;;;");
await file.WriteLineAsync($"Über-/Unterlieferungen lt. gez. GA;;;;;;;;;;;");
await file.WriteLineAsync($"MgNr;Name;Vorname;Adresse;PLZ;Ort;GA;Lieferpflicht;Lieferrecht;Geliefert;Über-/Unterliefert;Prozent");
using (var cmd = cnx.CreateCommand()) {
cmd.CommandText = $"""
SELECT m.mgnr, m.family_name, m.given_name, p.plz, o.name, m.address, m.business_shares,
m.business_shares * (SELECT value FROM client_parameter WHERE param = 'DELIVERY_OBLIGATION') AS min_kg,
m.business_shares * (SELECT value FROM client_parameter WHERE param = 'DELIVERY_RIGHT') AS max_kg,
COALESCE(SUM(d.weight), 0) AS sum
FROM member m
LEFT JOIN AT_plz_dest p ON p.id = m.postal_dest
LEFT JOIN AT_ort o ON o.okz = p.okz
LEFT JOIN v_delivery d ON d.mgnr = m.mgnr AND d.year = {year}
WHERE m.active = 1
GROUP BY d.year, m.mgnr
ORDER BY sum = 0 DESC, 100.0 * sum / max_kg, m.mgnr;
""";
using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync()) {
var mgnr = reader.GetInt32(0);
var familyName = reader.GetString(1);
var givenName = reader.GetString(2);
var plz = reader.GetInt32(3);
var ort = reader.GetString(4);
var addr = reader.GetString(5);
var ga = reader.GetInt32(6);
var minKg = reader.GetInt32(7);
var maxKg = reader.GetInt32(8);
var sum = reader.GetInt32(9);
var s1 = sum < minKg ? $"{sum - minKg}" : sum > maxKg ? $"{sum - maxKg}" : "";
var s2 = sum < minKg ? $"{sum * 100.0 / minKg - 100.0:0.0}" : sum > maxKg ? $"{sum * 100.0 / maxKg - 100:0.0}" : "";
await file.WriteLineAsync($"{mgnr};{familyName};{givenName};{addr};{plz};{ort};{ga};{minKg};{maxKg};{sum};{s1};{s2}");
}
}
await file.WriteLineAsync($";;;;;;;;;;;");
await file.WriteLineAsync($"Unterlieferungen lt. Flächenbindungen;;;;;;;;;;;");
await file.WriteLineAsync($"MgNr;Name;Vorname;Adresse;PLZ;Ort;Vertrag;Lieferpflicht;Lieferrecht;Geliefert;Unterliefert;Prozent");
using (var cmd = cnx.CreateCommand()) {
cmd.CommandText = $"""
SELECT m.mgnr, m.family_name, m.given_name, p.plz, o.name, m.address,
c.bin, c.min_kg, c.max_kg, b.weight
FROM member m
LEFT JOIN AT_plz_dest p ON p.id = m.postal_dest
LEFT JOIN AT_ort o ON o.okz = p.okz
JOIN v_area_commitment_bin c ON c.mgnr = m.mgnr AND c.year = {year}
LEFT JOIN v_payment_bin b ON (b.mgnr, b.bin) = (m.mgnr, c.bin) AND b.year = {year}
WHERE m.active = 1 AND b.weight < c.min_kg
ORDER BY m.mgnr, c.bin
""";
using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync()) {
var mgnr = reader.GetInt32(0);
var familyName = reader.GetString(1);
var givenName = reader.GetString(2);
var plz = reader.GetInt32(3);
var ort = reader.GetString(4);
var addr = reader.GetString(5);
var id = reader.GetString(6);
var minKg = reader.GetInt32(7);
var maxKg = reader.GetInt32(8);
var sum = reader.GetInt32(9);
await file.WriteLineAsync($"{mgnr};{familyName};{givenName};{addr};{plz};{ort};{id};{minKg};{maxKg};{sum};{sum - minKg};{sum * 100.0 / minKg - 100.0:0.0}");
}
}
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
}
private void PaymentButton_Click(object sender, RoutedEventArgs evt) {
var w = new ChartWindow();
w.Show();
}
}
}

View File

@ -5,7 +5,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Elwig.Windows" xmlns:local="clr-namespace:Elwig.Windows"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
Title="Test Fenster - Elwig" MinHeight="400" MinWidth="325" Height="450" Width="800" ResizeMode="CanResize"> Title="Test Fenster - Elwig" MinHeight="400" MinWidth="325" Height="450" Width="800" ResizeMode="CanResize" Loaded="Window_Loaded">
<Grid> <Grid>
<xctk:CheckComboBox x:Name="MyComboBox" HorizontalAlignment="Left" Margin="216,186,0,0" VerticalAlignment="Top" Delimiter=", " <xctk:CheckComboBox x:Name="MyComboBox" HorizontalAlignment="Left" Margin="216,186,0,0" VerticalAlignment="Top" Delimiter=", "
SelectedValue="{Binding SelectedValue}" SelectedValue="{Binding SelectedValue}"
@ -18,5 +18,10 @@
<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="PdfDeliveryButton" Content="Lieferschein Erzeugen" Click="PdfDeliveryButton_Click" Tag="Print" IsEnabled="False"
Margin="260,190,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<Button x:Name="PdfCreditButton" Content="Gutschrift Erzeugen" Click="PdfCreditButton_Click" Tag="Print" IsEnabled="False"
Margin="260,160,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/>
</Grid> </Grid>
</Window> </Window>

View File

@ -1,16 +1,27 @@
using Elwig.Documents;
using Elwig.Helpers;
using Elwig.Helpers.Billing;
using Microsoft.EntityFrameworkCore;
using System; using System;
using System.Linq; using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Input;
using Xceed.Wpf.Toolkit.Primitives; using Xceed.Wpf.Toolkit.Primitives;
namespace Elwig.Windows { namespace Elwig.Windows {
public partial class TestWindow : Window { public partial class TestWindow : Window {
public TestWindow() { public TestWindow() {
InitializeComponent(); InitializeComponent();
MyComboBox.ItemsSource = new string[] { "Klasse A" , "Klasse B", "Klasse C", "Klasse D", "Klasse E", "Klasse F" }; MyComboBox.ItemsSource = new string[] { "Klasse A" , "Klasse B", "Klasse C", "Klasse D", "Klasse E", "Klasse F" };
MyListBox.ItemsSource = new string[] { "Test 1", "Test 2", "Test 3", "Test 4" }; MyListBox.ItemsSource = new string[] { "Test 1", "Test 2", "Test 3", "Test 4" };
} }
private void Window_Loaded(object sender, RoutedEventArgs evt) {
PdfDeliveryButton.IsEnabled = App.IsPrintingReady;
PdfCreditButton.IsEnabled = App.IsPrintingReady;
}
private void OnItemSelectionChanged(object sender, ItemSelectionChangedEventArgs e) { private void OnItemSelectionChanged(object sender, ItemSelectionChangedEventArgs e) {
MyText.Text = string.Join(", ", MyComboBox.SelectedItems.Cast<string>()); MyText.Text = string.Join(", ", MyComboBox.SelectedItems.Cast<string>());
} }
@ -39,5 +50,23 @@ namespace Elwig.Windows {
var w = new ChartWindow(); var w = new ChartWindow();
w.Show(); w.Show();
} }
private async void PdfDeliveryButton_Click(object sender, RoutedEventArgs evt) {
Mouse.OverrideCursor = Cursors.AppStarting;
using var ctx = new AppDbContext();
using var doc = new DeliveryNote(await ctx.Deliveries.OrderBy(d => d.Parts.Count).ThenBy(d => d.Year).ThenBy(d => d.DId).LastAsync(), ctx);
await doc.Generate();
doc.Show();
Mouse.OverrideCursor = null;
}
private async void PdfCreditButton_Click(object sender, RoutedEventArgs evt) {
Mouse.OverrideCursor = Cursors.AppStarting;
using var ctx = new AppDbContext();
using var doc = new CreditNote(await ctx.Credits.FirstAsync(), ctx);
await doc.Generate();
doc.Show();
Mouse.OverrideCursor = null;
}
} }
} }

View File

@ -1,6 +1,6 @@
::mkdir "C:\Program Files\Elwig" ::mkdir "C:\Program Files\Elwig"
::curl -s "http://www.columbia.edu/~em36/PDFtoPrinter.exe" -z "C:\Program Files\Elwig\PDFtoPrinter.exe" -o "C:\Program Files\Elwig\PDFtoPrinter.exe" ::curl -s "http://www.columbia.edu/~em36/PDFtoPrinter.exe" -z "C:\Program Files\Elwig\PDFtoPrinter.exe" -o "C:\Program Files\Elwig\PDFtoPrinter.exe"
mkdir "C:\ProgramData\Elwig\resources" mkdir "C:\ProgramData\Elwig\resources"
curl -s -L "https://unpkg.com/pagedjs/dist/paged.polyfill.js" -o "C:\ProgramData\Elwig\resources\paged.polyfill.js" copy /b /y Documents\*.css "C:\ProgramData\Elwig\resources"
copy /b /y "Documents\style.css" "C:\ProgramData\Elwig\resources\style.css"
copy /b /y Documents\*.cshtml "C:\ProgramData\Elwig\resources" copy /b /y Documents\*.cshtml "C:\ProgramData\Elwig\resources"
::copy /b /y ..\Installer\Files\*.exe "C:\Program Files\Elwig\"

View File

@ -1,12 +0,0 @@
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Fragment>
<ComponentGroup Id="DocumentTemplateComponents" Directory="ConfigFolderResources">
<Component>
<File Source="$(TargetDir)\paged.polyfill.js" />
</Component>
<Component>
<File Source="$(var.ElwigProjectDir)\Documents\style.css" />
</Component>
</ComponentGroup>
</Fragment>
</Wix>

View File

@ -23,10 +23,10 @@
--> -->
<xsl:key <xsl:key
name="FileToRemove" name="FileToRemove"
match="wix:Component[ substring( wix:File/@Source, string-length( wix:File/@Source ) - 6 ) != '.cshtml' ]" match="wix:Component[ substring( wix:File/@Source, string-length( wix:File/@Source ) - 2 ) = '.cs' ]"
use="@Id" use="@Id"
/> />
<!-- Get the last 4 characters of a string using `substring( s, len(s) - 3 )`, it uses -3 and not -4 because XSLT uses 1-based indexes, not 0-based indexes. --> <!-- Get the last 3 characters of a string using `substring( s, len(s) - 2 )`, it uses -2 and not -3 because XSLT uses 1-based indexes, not 0-based indexes. -->
<!-- By default, copy all elements and nodes into the output... --> <!-- By default, copy all elements and nodes into the output... -->
<xsl:template match="@*|node()"> <xsl:template match="@*|node()">

Binary file not shown.

View File

@ -2,6 +2,7 @@
[general] [general]
; Only needed, if more than one branch is stored in database ; Only needed, if more than one branch is stored in database
branch = Zweigstelle branch = Zweigstelle
;debug = true
[database] [database]
; Relative or absolute path to database file ; Relative or absolute path to database file

View File

@ -25,14 +25,13 @@
</Task> </Task>
</UsingTask> </UsingTask>
<Target Name="CustomBeforeBuild" BeforeTargets="BeforeBuild"> <Target Name="CustomBeforeBuild" BeforeTargets="BeforeBuild">
<Exec Command="curl -s -L &quot;https://unpkg.com/pagedjs/dist/paged.polyfill.js&quot; -o &quot;$(TargetDir)paged.polyfill.js&quot;" />
<Exec Command="curl -s &quot;http://www.columbia.edu/~em36/PDFtoPrinter.exe&quot; -z &quot;$(TargetDir)PDFtoPrinter.exe&quot; -o &quot;$(TargetDir)PDFtoPrinter.exe&quot;" /> <Exec Command="curl -s &quot;http://www.columbia.edu/~em36/PDFtoPrinter.exe&quot; -z &quot;$(TargetDir)PDFtoPrinter.exe&quot; -o &quot;$(TargetDir)PDFtoPrinter.exe&quot;" />
<Exec Command="dotnet publish &quot;$(SolutionDir)Elwig\Elwig.csproj&quot; &quot;/p:PublishProfile=$(SolutionDir)\Elwig\Properties\PublishProfiles\FolderProfile.pubxml&quot;" /> <Exec Command="dotnet publish &quot;$(SolutionDir)Elwig\Elwig.csproj&quot; &quot;/p:PublishProfile=$(SolutionDir)\Elwig\Properties\PublishProfiles\FolderProfile.pubxml&quot;" />
<GetFileVersion AssemblyPath="..\Elwig\bin\Publish\Elwig.exe"> <GetFileVersion AssemblyPath="..\Elwig\bin\Publish\Elwig.exe">
<Output TaskParameter="Version" PropertyName="ElwigFileVersion" /> <Output TaskParameter="Version" PropertyName="ElwigFileVersion" />
</GetFileVersion> </GetFileVersion>
<PropertyGroup> <PropertyGroup>
<DefineConstants>ProductVersion=$(ElwigFileVersion);BuildPath=..\Elwig\bin\Publish;DocumentTemplatesPath=..\Elwig\Documents;ElwigProjectDir=..\Elwig</DefineConstants> <DefineConstants>ProductVersion=$(ElwigFileVersion);BuildPath=..\Elwig\bin\Publish;DocumentPath=..\Elwig\Documents;ElwigProjectDir=..\Elwig</DefineConstants>
</PropertyGroup> </PropertyGroup>
</Target> </Target>
<ItemGroup> <ItemGroup>
@ -50,13 +49,13 @@
<ComponentGroupName>DocumentTemplates</ComponentGroupName> <ComponentGroupName>DocumentTemplates</ComponentGroupName>
<DirectoryRefId>ConfigFolderResources</DirectoryRefId> <DirectoryRefId>ConfigFolderResources</DirectoryRefId>
<SuppressRootDirectory>true</SuppressRootDirectory> <SuppressRootDirectory>true</SuppressRootDirectory>
<PreprocessorVariable>DocumentTemplatesPath</PreprocessorVariable> <PreprocessorVariable>DocumentPath</PreprocessorVariable>
<Transforms>DocumentTemplatesTransform.xslt</Transforms> <Transforms>DocumentTransform.xslt</Transforms>
</HarvestDirectory> </HarvestDirectory>
<BindPath BindName="DocumentTemplateBindPath" Include="../Elwig/Documents" /> <BindPath BindName="DocumentTemplateBindPath" Include="../Elwig/Documents" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="DocumentTemplatesTransform.xslt" /> <None Include="DocumentTransform.xslt" />
<None Include="BuildFilesTransform.xslt" /> <None Include="BuildFilesTransform.xslt" />
<None Include="Files\config.ini" /> <None Include="Files\config.ini" />
</ItemGroup> </ItemGroup>

View File

@ -7,9 +7,12 @@
<Component Directory="ConfigFolder" Permanent="true" NeverOverwrite="true"> <Component Directory="ConfigFolder" Permanent="true" NeverOverwrite="true">
<File Source="$(ProjectDir)\Files\config.ini" Id="config.ini"/> <File Source="$(ProjectDir)\Files\config.ini" Id="config.ini"/>
</Component> </Component>
<Component Directory="InstallFolder"> <Component Directory="InstallFolder">
<File Source="$(TargetDir)\PDFtoPrinter.exe" Id="PDFtoPrinter.exe"/> <File Source="$(ProjectDir)\Files\WinziPrint.exe" Id="WinziPrint.exe"/>
</Component> </Component>
<Component Directory="InstallFolder">
<File Source="$(TargetDir)\PDFtoPrinter.exe" Id="PDFtoPrinter.exe"/>
</Component>
</ComponentGroup> </ComponentGroup>
</Fragment> </Fragment>
</Wix> </Wix>

View File

@ -11,7 +11,6 @@
<ComponentGroupRef Id="MainComponents"/> <ComponentGroupRef Id="MainComponents"/>
<ComponentGroupRef Id="BuildFiles"/> <ComponentGroupRef Id="BuildFiles"/>
<ComponentGroupRef Id="DocumentTemplates"/> <ComponentGroupRef Id="DocumentTemplates"/>
<ComponentGroupRef Id="DocumentTemplateComponents"/>
</Feature> </Feature>
</Package> </Package>
</Wix> </Wix>

View File

@ -68,5 +68,37 @@ namespace Tests {
Assert.Throws<ArgumentException>(() => Utils.Modulo("789", -1)); Assert.Throws<ArgumentException>(() => Utils.Modulo("789", -1));
}); });
} }
[Test]
public void Test_SplitAddress() {
Assert.Multiple(() => {
Assert.That(Utils.SplitAddress("Winzerstraße 1"), Is.EqualTo(("Winzerstraße", "1")));
Assert.That(Utils.SplitAddress("Auf dem Feld 12"), Is.EqualTo(("Auf dem Feld", "12")));
Assert.That(Utils.SplitAddress("Winzerstraße 5a"), Is.EqualTo(("Winzerstraße", "5a")));
Assert.That(Utils.SplitAddress("Winzerstraße 1-3/2"), Is.EqualTo(("Winzerstraße", "1-3/2")));
Assert.That(Utils.SplitAddress("Winzerstraße 3/4/5"), Is.EqualTo(("Winzerstraße", "3/4/5")));
Assert.That(Utils.SplitAddress("Winzerstraße 7/2/4/77"), Is.EqualTo(("Winzerstraße", "7/2/4/77")));
Assert.That(Utils.SplitAddress("Winzerstraße 95b"), Is.EqualTo(("Winzerstraße", "95b")));
Assert.That(Utils.SplitAddress("Winzerstraße 1, TOP 3"), Is.EqualTo(("Winzerstraße", "1, TOP 3")));
});
}
[Test]
public void Test_SplitName() {
Assert.Multiple(() => {
Assert.That(Utils.SplitName("Max Bauer", "Bauer"), Is.EqualTo(("Bauer", "Max")));
Assert.That(Utils.SplitName("Bauer Max", "Bauer"), Is.EqualTo(("Bauer", "Max")));
Assert.That(Utils.SplitName("Max und Moritz Bauer", "Bauer"), Is.EqualTo(("Bauer", "Max und Moritz")));
Assert.That(Utils.SplitName("Bauer Max und Moritz", "Bauer"), Is.EqualTo(("Bauer", "Max und Moritz")));
Assert.That(Utils.SplitName("Bauer GesbR", "Bauer"), Is.EqualTo(("Bauer", "GesbR")));
Assert.That(Utils.SplitName("Max und Moritz Bauer GesbR", "Bauer"), Is.EqualTo(("Bauer", "Max und Moritz GesbR")));
Assert.That(Utils.SplitName("Bauer Max und Moritz GesbR", "Bauer"), Is.EqualTo(("Bauer", "Max und Moritz GesbR")));
Assert.That(Utils.SplitName("Weingut Bauer", "Bauer"), Is.EqualTo(("Bauer", "Weingut")));
Assert.That(Utils.SplitName("Bauer Weingut", "Bauer"), Is.EqualTo(("Bauer", "Weingut")));
Assert.That(Utils.SplitName("Max und Moritz Bauer und Mustermann", "Bauer"), Is.EqualTo(("Bauer und Mustermann", "Max und Moritz")));
Assert.That(Utils.SplitName("Bauer und Mustermann Max und Moritz", "Bauer"), Is.EqualTo(("Bauer und Mustermann", "Max und Moritz")));
Assert.That(Utils.SplitName("ABC GesbR", "Bauer"), Is.EqualTo(((string, string?))("ABC GesbR", null)));
});
}
} }
} }