using Elwig.Documents; using Elwig.Helpers; using Elwig.Helpers.Billing; using Elwig.Models.Dtos; using Elwig.Models.Entities; using MailKit.Net.Smtp; using Microsoft.EntityFrameworkCore; using Microsoft.Win32; using MimeKit; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Input; namespace Elwig.Windows { public partial class MailWindow : ContextWindow { // used for document sorting while generating! public enum DocType { Undefined, Custom, MemberDataSheet, DeliveryConfirmation, CreditNote } public class SelectedDoc(DocType type, string name, object? details = null) { public DocType Type = type; public string Name { get; set; } = name; public object? Details = details; } public class GeneratedDoc { public DocType Type; public Document Doc; public GeneratedDoc(string pdfPath) { Type = DocType.Custom; Doc = Document.FromPdf(pdfPath); } public GeneratedDoc(Document doc) { Type = doc is MemberDataSheet ? DocType.MemberDataSheet : doc is DeliveryConfirmation ? DocType.DeliveryConfirmation : doc is CreditNote ? DocType.CreditNote : DocType.Undefined; Doc = doc; } } public static readonly string[] AvaiableDocuments = [ MemberDataSheet.Name, DeliveryConfirmation.Name, CreditNote.Name, ]; public readonly int Year; public ObservableCollection SelectedDocs = []; public IEnumerable Recipients = []; protected Document? PrintDocument; protected Dictionary>? PrintMemberDocuments; protected Dictionary>? EmailDocuments; public static readonly DependencyProperty PostalAllCountProperty = DependencyProperty.Register(nameof(PostalAllCount), typeof(int), typeof(MailWindow)); public int PostalAllCount { get => (int)GetValue(PostalAllCountProperty); private set => SetValue(PostalAllCountProperty, value); } public static readonly DependencyProperty PostalWishCountProperty = DependencyProperty.Register(nameof(PostalWishCount), typeof(int), typeof(MailWindow)); public int PostalWishCount { get => (int)GetValue(PostalWishCountProperty); private set => SetValue(PostalWishCountProperty, value); } public static readonly DependencyProperty PostalNoEmailCountProperty = DependencyProperty.Register(nameof(PostalNoEmailCount), typeof(int), typeof(MailWindow)); public int PostalNoEmailCount { get => (int)GetValue(PostalNoEmailCountProperty); private set => SetValue(PostalNoEmailCountProperty, value); } public static readonly DependencyProperty EmailAllCountProperty = DependencyProperty.Register(nameof(EmailAllCount), typeof(int), typeof(MailWindow)); public int EmailAllCount { get => (int)GetValue(EmailAllCountProperty); private set => SetValue(EmailAllCountProperty, value); } public static readonly DependencyProperty EmailWishCountProperty = DependencyProperty.Register(nameof(EmailWishCount), typeof(int), typeof(MailWindow)); public int EmailWishCount { get => (int)GetValue(EmailWishCountProperty); private set => SetValue(EmailWishCountProperty, value); } private ICommand? _deleteCommand; public ICommand DeleteCommand => _deleteCommand ??= new ActionCommand(() => { var idx = SelectedDocumentsList.SelectedIndex; if (idx == -1) return; SelectedDocs.RemoveAt(SelectedDocumentsList.SelectedIndex); SelectedDocumentsList.SelectedIndex = idx < SelectedDocumentsList.Items.Count ? idx : idx - 1; }); // powershell -Command "$(Get-WmiObject -Class Win32_Printer | Where-Object {$_.Default -eq $True}).Name" public MailWindow(int? year = null) { InitializeComponent(); using (var ctx = new AppDbContext()) { Year = year ?? ctx.Seasons.OrderBy(s => s.Year).LastOrDefault()?.Year ?? Utils.Today.Year; Title = $"Rundschreiben - Lese {Year} - Elwig"; } AvaiableDocumentsList.ItemsSource = AvaiableDocuments; SelectedDocumentsList.ItemsSource = SelectedDocs; DocumentNonDeliverersInput.Visibility = Visibility.Hidden; DocumentFooterLabel.Visibility = Visibility.Hidden; DeliveryConfirmationFooterInput.Visibility = Visibility.Hidden; CreditNoteFooterInput.Visibility = Visibility.Hidden; RecipientsActiveMembersInput.IsChecked = true; MemberOrganicIndifferentInput.IsChecked = true; MemberFunktionärIndifferentInput.IsChecked = true; DeliveryConfirmationFooterInput.Text = App.Client.TextDeliveryConfirmation; CreditNoteFooterInput.Text = App.Client.TextCreditNote; DocumentNonDeliverersInput.IsChecked = App.Client.MailIncludeNonDeliverers; DoublePagedInput.IsChecked = App.Client.MailDoublePaged; switch (App.Client.MailSendPostal) { case 0: PostalNobodyInput.IsChecked = true; break; case 1: PostalNoEmailInput.IsChecked = true; break; case 2: PostalWishInput.IsChecked = true; break; case 3: PostalAllInput.IsChecked = true; break; } switch (App.Client.MailSendEmail) { case 0: EmailNobodyInput.IsChecked = true; break; case 1: EmailWishInput.IsChecked = true; break; case 2: EmailAllInput.IsChecked = true; break; } switch (App.Client.MailOrdering) { case 0: OrderMgNrInput.IsChecked = true; break; case 1: OrderNameInput.IsChecked = true; break; case 2: OrderPlzInput.IsChecked = true; break; } PostalSender1.Text = App.Client.Sender1; PostalSender2.Text = App.Client.Sender2; PostalLocation.Text = App.BranchLocation; PostalDate.Text = $"{Utils.Today:dd.MM.yyyy}"; EmailSubjectInput.Text = App.Client.TextEmailSubject ?? "Rundschreiben"; EmailBodyInput.Text = App.Client.TextEmailBody ?? "Sehr geehrtes Mitglied,\n\nim Anhang finden Sie das aktuelle Rundschreiben.\n\nIhre Winzergenossenschaft\n"; } protected override async Task OnRenewContext(AppDbContext ctx) { var season = await ctx.Seasons.FindAsync(Year); var l = new List { MemberDataSheet.Name }; if (season != null) { l.Add($"{DeliveryConfirmation.Name} {Year}"); l.AddRange(season.PaymentVariants.OrderBy(v => v.AvNr).Select(v => $"{CreditNote.Name} – {v.Name}")); } AvaiableDocumentsList.ItemsSource = l; ControlUtils.RenewItemsSource(MemberBranchInput, await ctx.Branches .Where(b => b.Members.Count != 0) .OrderBy(b => b.Name) .ToListAsync(), MemberInput_SelectionChanged); if (MemberBranchInput.SelectedItems.Count == 0) { MemberBranchInput.SelectionChanged -= MemberInput_SelectionChanged; MemberBranchInput.SelectAll(); MemberBranchInput.SelectionChanged += MemberInput_SelectionChanged; } ControlUtils.RenewItemsSource(MemberKgInput, await ctx.Katastralgemeinden .Where(k => k.WbKg!.Members.Count != 0) .OrderBy(k => k.Name) .ToListAsync(), MemberInput_SelectionChanged); if (MemberKgInput.SelectedItems.Count == 0) { MemberKgInput.SelectionChanged -= MemberInput_SelectionChanged; MemberKgInput.SelectAll(); MemberKgInput.SelectionChanged += MemberInput_SelectionChanged; } ControlUtils.RenewItemsSource(MemberAreaComInput, await ctx.AreaCommitmentTypes .OrderBy(a => a.VtrgId) .ToListAsync(), MemberInput_SelectionChanged); if (MemberAreaComInput.SelectedItems.Count == 0) { MemberAreaComInput.SelectionChanged -= MemberInput_SelectionChanged; MemberAreaComInput.SelectAll(); MemberAreaComInput.SelectionChanged += MemberInput_SelectionChanged; } ControlUtils.RenewItemsSource(MemberDeliveryAncmtInput, await ctx.DeliverySchedules .Where(s => s.Year == Year) .OrderBy(s => s.DateString) .ThenBy(s => s.Branch.Name) .ThenBy(s => s.Description) .ToListAsync(), MemberInput_SelectionChanged); if (MemberDeliveryAncmtInput.SelectedItems.Count == 0) { MemberDeliveryAncmtInput.SelectionChanged -= MemberInput_SelectionChanged; MemberDeliveryAncmtInput.SelectAll(); MemberDeliveryAncmtInput.SelectionChanged += MemberInput_SelectionChanged; } ControlUtils.RenewItemsSource(MemberCustomInput, await ctx.Members .Where(m => m.IsActive) .OrderBy(m => m.Name) .ThenBy(m => m.GivenName) .Include(m => m.Branch) .Include(m => m.DefaultWbKg!.AtKg) .Include(m => m.EmailAddresses) .Include(m => m.TelephoneNumbers) .Include(m => m.PostalDest.AtPlz!.Ort) .Include(m => m.PostalDest.AtPlz!.Country) .Include(m => m.BillingAddress!.PostalDest.AtPlz!.Ort) .Include(m => m.BillingAddress!.PostalDest.AtPlz!.Country) .ToListAsync(), MemberInput_SelectionChanged); if (MemberCustomInput.SelectedItems.Count == 0) { MemberCustomInput.SelectionChanged -= MemberInput_SelectionChanged; MemberCustomInput.SelectAll(); MemberCustomInput.SelectionChanged += MemberInput_SelectionChanged; } await UpdateRecipients(ctx); } private void ResetDocuments() { DisposeDocs(); if (IsLoaded) { PreviewButton.IsEnabled = false; PrintButton.IsEnabled = false; EmailButton.IsEnabled = false; ProgressBar.Value = 0; } } private void LockInputs() { DocumentAddButton.IsEnabled = false; DocumentRemoveButton.IsEnabled = false; SelectDocumentButton.IsEnabled = false; foreach (var tb in ControlUtils.FindAllChildren(this, [])) tb.IsReadOnly = true; foreach (var cb in ControlUtils.FindAllChildren(this, [])) cb.IsEnabled = false; foreach (var cb in ControlUtils.FindAllChildren(this, [])) cb.IsEnabled = false; foreach (var lb in ControlUtils.FindAllChildren(this, [])) lb.IsEnabled = false; foreach (var rb in ControlUtils.FindAllChildren(this, [])) rb.IsEnabled = false; } private void UnlockInputs() { DocumentAddButton.IsEnabled = true; DocumentRemoveButton.IsEnabled = true; SelectDocumentButton.IsEnabled = true; foreach (var tb in ControlUtils.FindAllChildren(this, [])) tb.IsReadOnly = false; foreach (var cb in ControlUtils.FindAllChildren(this, [])) cb.IsEnabled = true; foreach (var cb in ControlUtils.FindAllChildren(this, [])) cb.IsEnabled = true; foreach (var lb in ControlUtils.FindAllChildren(this, [])) lb.IsEnabled = true; foreach (var rb in ControlUtils.FindAllChildren(this, [])) rb.IsEnabled = true; } private void Menu_Help_Log_Click(object sender, RoutedEventArgs evt) { var w = new MailLogWindow(); w.Show(); } private void ContinueButton_Click(object sender, RoutedEventArgs evt) { TabControl.SelectedIndex = 1; TabControl.AllowDrop = false; } private void BackButton_Click(object sender, RoutedEventArgs evt) { TabControl.SelectedIndex = 0; TabControl.AllowDrop = true; } private async void Document_Drop(object sender, DragEventArgs evt) { if (evt.Data.GetDataPresent(DataFormats.FileDrop)) { var files = (string[])evt.Data.GetData(DataFormats.FileDrop); foreach (var file in files) { if (Path.GetExtension(file) == ".pdf") { SelectedDocs.Add(new(DocType.Custom, Path.GetFileName(file), file)); } } using var ctx = new AppDbContext(); await UpdateRecipients(ctx); } } private void Document_PreviwDragOver(object sender, DragEventArgs evt) { evt.Handled = TabControl.SelectedIndex == 0; } private void AvaiableDocumentsList_SelectionChanged(object sender, RoutedEventArgs evt) { DocumentAddButton.IsEnabled = AvaiableDocumentsList.SelectedIndex != -1; } private void SelectedDocumentsList_SelectionChanged(object sender, RoutedEventArgs evt) { DocumentRemoveButton.IsEnabled = SelectedDocumentsList.SelectedIndex != -1; if (SelectedDocumentsList.SelectedItem is SelectedDoc doc) { DocumentBox.Header = doc.Name; if (doc.Type == DocType.DeliveryConfirmation) { DocumentNonDeliverersInput.Visibility = Visibility.Visible; DocumentFooterLabel.Visibility = Visibility.Visible; DeliveryConfirmationFooterInput.Visibility = Visibility.Visible; CreditNoteFooterInput.Visibility = Visibility.Hidden; DocumentFooterLabel.Margin = new(10, 40, 0, 10); } else if (doc.Type == DocType.CreditNote) { DocumentNonDeliverersInput.Visibility = Visibility.Hidden; DocumentFooterLabel.Visibility = Visibility.Visible; DeliveryConfirmationFooterInput.Visibility = Visibility.Hidden; CreditNoteFooterInput.Visibility = Visibility.Visible; DocumentFooterLabel.Margin = new(10, 10, 0, 10); } else { DocumentNonDeliverersInput.Visibility = Visibility.Hidden; DocumentFooterLabel.Visibility = Visibility.Hidden; DeliveryConfirmationFooterInput.Visibility = Visibility.Hidden; CreditNoteFooterInput.Visibility = Visibility.Hidden; } } else { DocumentBox.Header = "Dokument"; DocumentNonDeliverersInput.Visibility = Visibility.Hidden; DocumentFooterLabel.Visibility = Visibility.Hidden; DeliveryConfirmationFooterInput.Visibility = Visibility.Hidden; CreditNoteFooterInput.Visibility = Visibility.Hidden; } } private async void DocumentAddButton_Click(object sender, RoutedEventArgs evt) { var idx = AvaiableDocumentsList.SelectedIndex; using var ctx = new AppDbContext(); if (AvaiableDocumentsList.SelectedItem is not string s) return; if (idx == 0) { SelectedDocs.Add(new(DocType.MemberDataSheet, s, null)); } else if (idx == 1) { SelectedDocs.Add(new(DocType.DeliveryConfirmation, s, Year)); RecipientsDeliveryMembersInput.IsChecked = true; } else if (idx >= 2) { var name = s.Split(" – ")[^1]; var pv = await ctx.PaymentVariants.SingleAsync(v => v.Year == Year && v.Name == name)!; SelectedDocs.Add(new(DocType.CreditNote, s, (pv.Year, pv.AvNr))); RecipientsDeliveryMembersInput.IsChecked = true; } SelectedDocumentsList.SelectedIndex = SelectedDocs.Count - 1; await UpdateRecipients(ctx); } private async void DocumentRemoveButton_Click(object sender, RoutedEventArgs evt) { DeleteCommand.Execute(null); using var ctx = new AppDbContext(); await UpdateRecipients(ctx); } private async void SelectDocumentButton_Click(object sender, RoutedEventArgs evt) { var d = new OpenFileDialog() { Title = "Dokument auswählen - Elwig", DefaultExt = "pdf", Filter = "PDF-Dokument (*.pdf)|*.pdf", Multiselect = true, }; if (d.ShowDialog() == true) { foreach (var file in d.FileNames) { if (Path.GetExtension(file) == ".pdf") { SelectedDocs.Add(new(DocType.Custom, Path.GetFileName(file), file)); } } using var ctx = new AppDbContext(); await UpdateRecipients(ctx); } } private async void RecipientsInput_Changed(object sender, RoutedEventArgs evt) { var vis = RecipientsCustomInput.IsChecked == true ? Visibility.Hidden : Visibility.Visible; MemberBranchLabel.Visibility = vis; MemberBranchInput.Visibility = vis; MemberKgLabel.Visibility = vis; MemberKgInput.Visibility = vis; MemberOrganicLabel.Visibility = vis; MemberOrganicYesInput.Visibility = vis; MemberOrganicNoInput.Visibility = vis; MemberOrganicIndifferentInput.Visibility = vis; MemberFunktionärLabel.Visibility = vis; MemberFunktionärYesInput.Visibility = vis; MemberFunktionärNoInput.Visibility = vis; MemberFunktionärIndifferentInput.Visibility = vis; MemberAreaComInput.Visibility = RecipientsAreaComMembersInput.IsChecked == true ? Visibility.Visible : Visibility.Hidden; MemberAreaComLabel.Visibility = RecipientsAreaComMembersInput.IsChecked == true ? Visibility.Visible : Visibility.Hidden; MemberDeliveryAncmtInput.Visibility = RecipientsDeliveryAncmtMembersInput.IsChecked == true ? Visibility.Visible : Visibility.Hidden; MemberDeliveryAncmtLabel.Visibility = RecipientsDeliveryAncmtMembersInput.IsChecked == true ? Visibility.Visible : Visibility.Hidden; MemberCustomInput.Visibility = RecipientsCustomInput.IsChecked == true ? Visibility.Visible : Visibility.Hidden; using var ctx = new AppDbContext(); await UpdateRecipients(ctx); } private async void MemberInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) { using var ctx = new AppDbContext(); await UpdateRecipients(ctx); } private async void MemberInput_Checked(object sender, RoutedEventArgs evt) { using var ctx = new AppDbContext(); await UpdateRecipients(ctx); } private void Date_TextChanged(object sender, RoutedEventArgs evt) { Validator.CheckDate((TextBox)sender, true); ResetDocuments(); } private void Date_LostFocus(object sender, RoutedEventArgs evt) { var res = Validator.CheckDate((TextBox)sender, true); if (!res.IsValid) ((TextBox)sender).Text = $"{Utils.Today:dd.MM.yyyy}"; } private async Task UpdateRecipients(AppDbContext ctx) { if (RecipientsCustomInput.IsChecked == true) { Recipients = MemberCustomInput.SelectedItems.Cast().ToList(); } else { IQueryable query = ctx.Members; if (MemberBranchInput.SelectedItems.Count != MemberBranchInput.Items.Count) { var zwst = MemberBranchInput.SelectedItems.Cast().Select(b => b.ZwstId).ToList(); query = query.Where(m => zwst.Contains(m.ZwstId!)); } if (MemberKgInput.SelectedItems.Count != MemberKgInput.Items.Count) { var kgs = MemberKgInput.SelectedItems.Cast().Select(k => k.KgNr).ToList(); query = query.Where(m => kgs.Contains((int)m.DefaultKgNr!)); } if (MemberOrganicYesInput.IsChecked == true) { query = query.Where(m => m.IsOrganic); } else if (MemberOrganicNoInput.IsChecked == true) { query = query.Where(m => !m.IsOrganic); } if (MemberFunktionärYesInput.IsChecked == true) { query = query.Where(m => m.IsFunktionär); } else if (MemberFunktionärNoInput.IsChecked == true) { query = query.Where(m => !m.IsFunktionär); } if (RecipientsAreaComMembersInput.IsChecked == true) { var vtrg = MemberAreaComInput.SelectedItems.Cast().Select(a => a.VtrgId).ToList(); query = query.Where(m => m.IsActive && m.AreaCommitments.AsQueryable().Where(Utils.ActiveAreaCommitments(Year)).Any(c => vtrg.Contains(c.VtrgId))); } else if (RecipientsDeliveryAncmtMembersInput.IsChecked == true) { var dsnrs = MemberDeliveryAncmtInput.SelectedItems.Cast().Select(s => s.DsNr).ToList(); query = query.Where(m => m.Announcements.Any(a => a.Year == Year && dsnrs.Contains(a.DsNr))); } else if (RecipientsDeliveryMembersInput.IsChecked == true) { query = query.Where(m => m.Deliveries.Any(d => d.Year == Year)); } else if (RecipientsNonDeliveryMembersInput.IsChecked == true) { query = query.Where(m => m.IsActive && !m.Deliveries.Any(d => d.Year == Year)); } else if (RecipientsActiveMembersInput.IsChecked == true && SelectedDocs.Any(d => d.Type == DocType.DeliveryConfirmation || d.Type == DocType.CreditNote)) { query = query.Where(m => m.IsActive || m.Deliveries.Any(d => d.Year == Year)); } else { query = query.Where(m => m.IsActive); } Recipients = await query .Include(m => m.Branch) .Include(m => m.DefaultWbKg!.AtKg) .Include(m => m.EmailAddresses) .Include(m => m.TelephoneNumbers) .Include(m => m.PostalDest.AtPlz!.Ort) .Include(m => m.PostalDest.AtPlz!.Country) .Include(m => m.BillingAddress!.PostalDest.AtPlz!.Ort) .Include(m => m.BillingAddress!.PostalDest.AtPlz!.Country) .ToListAsync(); } UpdatePostalEmailRecipients(); } private void EmailInput_Changed(object sender, RoutedEventArgs evt) { UpdatePostalEmailRecipients(); } private void UpdatePostalEmailRecipients() { var modeEmail = EmailAllInput.IsChecked == true ? 2 : EmailWishInput.IsChecked == true ? 1 : 0; var modePostal = PostalAllInput.IsChecked == true ? 3 : PostalWishInput.IsChecked == true ? 2 : PostalNoEmailInput.IsChecked == true ? 1 : 0; EmailAllCount = Recipients.Count(m => m.EmailAddresses.Count > 0); EmailWishCount = Recipients.Count(m => m.EmailAddresses.Count > 0 && m.ContactViaEmail); PostalAllCount = Recipients.Count(); PostalWishCount = Recipients.Count(m => m.ContactViaPost); var countEmail = (modeEmail == 2 ? EmailAllCount : modeEmail == 1 ? EmailWishCount : 0); PostalNoEmailCount = PostalAllCount - countEmail; var countPostal = (modePostal == 3 ? PostalAllCount : modePostal == 2 ? PostalWishCount : modePostal == 1 ? PostalNoEmailCount : 0); if (IsLoaded) { StatusRecipients.Text = $"{Recipients.Count():N0}"; StatusPostalRecipients.Text = $"{countPostal:N0}"; StatusEmailRecipients.Text = $"{countEmail:N0}"; } ResetDocuments(); } private async Task UpdateClientParameters() { var changed = false; var dcInclude = DocumentNonDeliverersInput.IsChecked ?? false; if (dcInclude != App.Client.MailIncludeNonDeliverers) { App.Client.MailIncludeNonDeliverers = dcInclude; changed = true; } var dcText = DeliveryConfirmationFooterInput.Text.Trim(); if (dcText.Length == 0) dcText = null; if (dcText != App.Client.TextDeliveryConfirmation) { App.Client.TextDeliveryConfirmation = dcText; changed = true; } var cdText = CreditNoteFooterInput.Text.Trim(); if (cdText.Length == 0) cdText = null; if (cdText != App.Client.TextCreditNote) { App.Client.TextCreditNote = cdText; changed = true; } var sendPostal = 0; if (PostalAllInput.IsChecked ?? false) { sendPostal = 3; } else if (PostalWishInput.IsChecked ?? false) { sendPostal = 2; } else if (PostalNoEmailInput.IsChecked ?? false) { sendPostal = 1; } if (sendPostal != App.Client.MailSendPostal) { App.Client.MailSendPostal = sendPostal; changed = true; } var sendEmail = 0; if (EmailAllInput.IsChecked ?? false) { sendEmail = 2; } else if (EmailWishInput.IsChecked ?? false) { sendEmail = 1; } if (sendEmail != App.Client.MailSendEmail) { App.Client.MailSendEmail = sendEmail; changed = true; } var ordering = 0; if (OrderNameInput.IsChecked ?? false) { ordering = 1; } else if (OrderPlzInput.IsChecked ?? false) { ordering = 2; } if (ordering != App.Client.MailOrdering) { App.Client.MailOrdering = ordering; changed = true; } var mailDoublePaged = DoublePagedInput.IsChecked ?? false; if (mailDoublePaged != App.Client.MailDoublePaged) { App.Client.MailDoublePaged = mailDoublePaged; changed = true; } var sender2 = PostalSender2.Text.Trim(); if (sender2 != App.Client.Sender2) { App.Client.Sender2 = sender2; changed = true; } var emailSubject = EmailSubjectInput.Text.Trim(); if (emailSubject.Length == 0) emailSubject = null; if (emailSubject != App.Client.TextEmailSubject) { App.Client.TextEmailSubject = emailSubject; changed = true; } var emailBody = EmailBodyInput.Text.Trim(); if (emailBody.Length == 0) emailBody = null; if (emailBody != App.Client.TextEmailBody) { App.Client.TextEmailBody = emailBody; changed = true; } if (changed) await App.Client.UpdateValues(); } private void DisposeDocs() { PrintDocument?.Dispose(); PrintDocument = null; if (EmailDocuments != null) { foreach (var (m, docs) in EmailDocuments) { foreach (var d in docs) { d.Dispose(); } } EmailDocuments = null; } } private void Window_Closed(object sender, EventArgs evt) { DisposeDocs(); } private async void GenerateButton_Click(object sender, RoutedEventArgs evt) { LockInputs(); PreviewButton.IsEnabled = false; PrintButton.IsEnabled = false; EmailButton.IsEnabled = false; Mouse.OverrideCursor = Cursors.AppStarting; GenerateButton.IsEnabled = false; DisposeDocs(); await UpdateClientParameters(); using var ctx = new AppDbContext(); var doublePaged = DoublePagedInput.IsChecked == true; var location = PostalLocation.Text.Trim(); var docs = SelectedDocs.OrderByDescending(d => d.Type).ToList(); IEnumerable recipients = Recipients; if (OrderMgNrInput.IsChecked == true) { recipients = recipients .OrderBy(m => m.MgNr) .ToList(); } else if (OrderNameInput.IsChecked == true) { recipients = recipients .OrderBy(m => m.Name) .ThenBy(m => m.GivenName) .ThenBy(m => m.MgNr) .ToList(); } else if (OrderPlzInput.IsChecked == true) { if (docs.Any(d => d.Type == DocType.DeliveryConfirmation || d.Type == DocType.CreditNote)) { recipients = recipients .OrderBy(m => m.BillingAddress?.PostalDest.AtPlz?.Plz ?? m.PostalDest.AtPlz?.Plz) .ThenBy(m => m.BillingAddress?.PostalDest.AtPlz?.Ort.Name ?? m.PostalDest.AtPlz?.Ort.Name) .ThenBy(m => m.Name) .ThenBy(m => m.GivenName) .ThenBy(m => m.MgNr) .ToList(); } else { recipients = recipients .OrderBy(m => m.PostalDest.AtPlz?.Plz) .ThenBy(m => m.PostalDest.AtPlz?.Ort.Name) .ThenBy(m => m.Name) .ThenBy(m => m.GivenName) .ThenBy(m => m.MgNr) .ToList(); } } Dictionary> dcData = []; Dictionary<(int, int), (IDictionary, IDictionary, BillingData)> cnData = []; foreach (var doc in docs) { if (doc.Type == DocType.DeliveryConfirmation) { var year = (int)doc.Details!; try { var b = new Billing(year); await b.FinishSeason(); await b.CalculateBuckets(); App.HintContextChange(); dcData[year] = await DeliveryConfirmationDeliveryData.ForSeason(ctx.DeliveryParts, year); } catch (Exception exc) { MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); UnlockInputs(); GenerateButton.IsEnabled = true; Mouse.OverrideCursor = null; return; } } else if (doc.Type == DocType.CreditNote) { var details = ((int, int))doc.Details!; var year = details.Item1; var avnr = details.Item2; try { cnData[(year, avnr)] = ( await CreditNoteDeliveryData.ForPaymentVariant(ctx.CreditNoteDeliveryRows, ctx.Seasons, year, avnr), await ctx.MemberPayments.Where(p => p.Year == year && p.AvNr == avnr).ToDictionaryAsync(c => c.MgNr), BillingData.FromJson((await ctx.PaymentVariants.FindAsync(year, avnr))!.Data) ); } catch (Exception exc) { MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); UnlockInputs(); GenerateButton.IsEnabled = true; Mouse.OverrideCursor = null; return; } await ctx.GetMemberAreaCommitmentBuckets(year, 0); } } var postalDate = DateOnly.ParseExact(PostalDate.Text, "dd.MM.yyyy"); var memberDocs = recipients.Select(m => new { Member = m, Docs = docs.SelectMany(doc => { try { if (doc.Type == DocType.Custom) { return [new GeneratedDoc((string)doc.Details!)]; } else if (doc.Type == DocType.MemberDataSheet) { return [new GeneratedDoc(new MemberDataSheet(m, ctx) { Date = postalDate })]; } else if (doc.Type == DocType.DeliveryConfirmation) { var year = (int)doc.Details!; DeliveryConfirmationDeliveryData data; if (dcData[year].TryGetValue(m.MgNr, out var d)) { data = d; } else if (App.Client.MailIncludeNonDeliverers) { data = DeliveryConfirmationDeliveryData.CreateEmpty(year, m); } else { return []; } return [new GeneratedDoc(new DeliveryConfirmation(ctx, year, m, data) { Date = postalDate })]; } else if (doc.Type == DocType.CreditNote) { var details = ((int, int))doc.Details!; var year = details.Item1; var avnr = details.Item2; var data = cnData[(year, avnr)]; try { return [new GeneratedDoc(new CreditNote( ctx, data.Item2[m.MgNr], data.Item1[m.MgNr], data.Item3.ConsiderContractPenalties, data.Item3.ConsiderTotalPenalty, data.Item3.ConsiderAutoBusinessShares, data.Item3.ConsiderCustomModifiers, ctx.GetMemberUnderDelivery(year, m.MgNr).GetAwaiter().GetResult() ) { Date = postalDate })]; } catch (Exception) { return []; } } else { throw new NotImplementedException("Invalid DocType"); } } catch (Exception exc) { MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); return []; } }).ToList() }).ToList(); var printMode = PostalAllInput.IsChecked == true ? 3 : PostalWishInput.IsChecked == true ? 2 : PostalNoEmailInput.IsChecked == true ? 1 : 0; var emailMode = EmailAllInput.IsChecked == true ? 2 : EmailWishInput.IsChecked == true ? 1 : 0; double printNum = printMode == 3 ? PostalAllCount : printMode == 2 ? PostalWishCount : printMode == 2 ? PostalNoEmailCount : 0; double emailNum = emailMode == 2 ? EmailAllCount : emailMode == 1 ? EmailWishCount : 0; double totalNum = printNum + emailNum; var email = memberDocs .Where(d => d.Member.EmailAddresses.Count > 0 && (emailMode == 2 || (emailMode == 1 && d.Member.ContactViaEmail))) .ToDictionary(d => d.Member, m => { var docs = m.Docs.Select(d => d.Doc).ToList(); foreach (var doc in docs) { doc!.DoublePaged = false; if (doc is BusinessDocument b) { b.IncludeSender = false; b.Location = location; } }; return docs; }); var emailRecipients = email.Select(d => d.Key.MgNr).ToHashSet(); try { foreach (var item1 in email.Select((e, i) => new { Index = i, e.Key, e.Value })) { foreach (var item2 in item1.Value.Select((d, i) => new { Index = i, Doc = d })) { await item2.Doc.Generate(new Progress(v => { ProgressBar.Value = v * (item2.Index + 1) / item1.Value.Count / totalNum + 100.0 * item1.Index / totalNum; })); } } } catch (Exception exc) { MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); UnlockInputs(); GenerateButton.IsEnabled = true; Mouse.OverrideCursor = null; return; } if (email.Count > 0) { EmailDocuments = email; } var printMemberDocs = memberDocs .Where(d => printMode == 3 || (printMode == 2 && d.Member.ContactViaPost) || (printMode == 1 && !emailRecipients.Contains(d.Member.MgNr))) .ToList(); var printDocs = printMemberDocs .SelectMany(m => { var docs = m.Docs.Select(d => d.Doc).ToList(); if (docs.Count == 0 || m.Docs[0].Type == DocType.Custom) { docs.Insert(0, new Letterhead(m.Member)); } docs.ForEach(doc => { doc.DoublePaged = doublePaged; if (doc is BusinessDocument b) b.Location = location; }); if (docs.Count > 0 && docs[0] is BusinessDocument b) b.IncludeSender = true; return docs; }) .ToList(); if (printDocs.Count > 0) { try { var print = Document.Merge(printDocs); print.DoublePaged = doublePaged; await print.Generate(new Progress(v => { ProgressBar.Value = 100.0 * emailNum / totalNum + v * printNum / totalNum; })); PrintDocument = print; PrintMemberDocuments = printMemberDocs.ToDictionary(m => m.Member, m => m.Docs.Select(d => d.Doc).ToList()); } catch (Exception exc) { MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); UnlockInputs(); GenerateButton.IsEnabled = true; Mouse.OverrideCursor = null; return; } } else { PrintDocument = null; PrintMemberDocuments = null; } ProgressBar.Value = 100.0; UnlockInputs(); GenerateButton.IsEnabled = true; Mouse.OverrideCursor = null; PreviewButton.IsEnabled = true; PrintButton.IsEnabled = PrintDocument != null; EmailButton.IsEnabled = EmailDocuments != null && App.Config.Smtp != null; } private void PreviewButton_Click(object sender, RoutedEventArgs evt) { var d = new OpenFolderDialog() { Title = "Ordner auswählen - Elwig", }; if (d.ShowDialog() == true) { Mouse.OverrideCursor = Cursors.AppStarting; PrintDocument?.SaveTo($"{d.FolderName}/Print.pdf"); if (EmailDocuments != null) { foreach (var (m, docs) in EmailDocuments) { var folder = $"{d.FolderName}/E-Mail/{m.AdministrativeName.Trim()}"; Directory.CreateDirectory(folder); foreach (var item in docs.Select((d, i) => new { Index = i, Doc = d })) { var doc = item.Doc; var name = Utils.NormalizeFileName(doc.Title); doc.SaveTo($"{folder}/{item.Index + 1:00}.{name}.pdf"); } } } Mouse.OverrideCursor = null; Process.Start("explorer.exe", d.FolderName); } } private async void PrintButton_Click(object sender, RoutedEventArgs evt) { if (PrintDocument == null || PrintMemberDocuments == null) return; PrintButton.IsEnabled = false; GenerateButton.IsEnabled = false; LockInputs(); var res = MessageBox.Show($"Sollen {PrintDocument.Pages} Blätter ({PrintDocument.TotalPages} Seiten) gedruckt werden?", "Rundschreiben drucken", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No); if (res == MessageBoxResult.Yes) { Mouse.OverrideCursor = Cursors.AppStarting; if (App.Config.Debug) { PrintDocument.Show(); } else { await PrintDocument.Print(); await Utils.AddSentMails( PrintMemberDocuments.Select(d => ( "postal", d.Key.MgNr, d.Key.AdministrativeName, new string[] { d.Value.Select(d => (d as BusinessDocument)?.Address).FirstOrDefault(a => a != null) ?? d.Key.FullAddress }, d.Value.Select(d => d.Title).FirstOrDefault("Briefkopf"), d.Value.Select(d => d.Title).ToArray() )) ); } Mouse.OverrideCursor = null; } PrintButton.IsEnabled = true; GenerateButton.IsEnabled = true; UnlockInputs(); } private async void EmailButton_Click(object sender, RoutedEventArgs evt) { if (App.Config.Smtp == null || EmailDocuments == null) return; EmailButton.IsEnabled = false; GenerateButton.IsEnabled = false; LockInputs(); SmtpClient? client = null; try { Mouse.OverrideCursor = Cursors.AppStarting; client = await Utils.GetSmtpClient(); Mouse.OverrideCursor = null; var res = MessageBox.Show($"Sollen {EmailDocuments.Count:N0} E-Mails verschickt werden?", "Rundschreiben verschicken", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No); if (res != MessageBoxResult.Yes) { return; } Mouse.OverrideCursor = Cursors.AppStarting; var subject = EmailSubjectInput.Text; var text = EmailBodyInput.Text; await Utils.AddSentMailBody(subject, text, EmailDocuments.Count); foreach (var (m, docs) in EmailDocuments) { using var msg = new MimeMessage(); msg.From.Add(new MailboxAddress(App.Client.NameFull, App.Config.Smtp.Value.From)); msg.To.AddRange(m.EmailAddresses.OrderBy(a => a.Nr).Select(a => new MailboxAddress(m.AdministrativeName, a.Address))); msg.Subject = subject; var body = new Multipart("mixed") { new TextPart("plain") { Text = text } }; foreach (var doc in docs) { var name = Utils.NormalizeFileName(doc.Title); body.Add(doc.AsEmailAttachment($"{name}.pdf")); } msg.Body = body; await client!.SendAsync(msg); await Utils.AddSentMails([( "email", m.MgNr, m.AdministrativeName, m.EmailAddresses.OrderBy(a => a.Nr).Select(a => a.Address).ToArray(), subject, docs.Select(d => d.Title).ToArray() )]); } MessageBox.Show("Erfolgreich alle E-Mails verschickt!", "Rundschreiben verschicken", MessageBoxButton.OK, MessageBoxImage.Information); } catch (Exception exc) { MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); } finally { if (client != null) await client.DisconnectAsync(true); client?.Dispose(); EmailButton.IsEnabled = true; GenerateButton.IsEnabled = true; UnlockInputs(); Mouse.OverrideCursor = null; } } public void AddDeliveryConfirmation() { if (!GenerateButton.IsEnabled) return; AvaiableDocumentsList.SelectedIndex = 1; if (AvaiableDocumentsList.SelectedItem is not string s || SelectedDocs.Any(d => d.Type == DocType.DeliveryConfirmation)) return; SelectedDocs.Add(new(DocType.DeliveryConfirmation, s, (Year, DocumentNonDeliverersInput.IsChecked == true))); SelectedDocumentsList.SelectedIndex = SelectedDocs.Count - 1; RecipientsDeliveryMembersInput.IsChecked = true; } public void AddCreditNote(int index) { if (!GenerateButton.IsEnabled) return; AvaiableDocumentsList.SelectedIndex = 2 + index; if (AvaiableDocumentsList.SelectedItem is not string s || SelectedDocs.Any(d => d.Type == DocType.CreditNote)) return; var name = s.Split(" – ")[^1]; using var ctx = new AppDbContext(); var pv = ctx.PaymentVariants.Single(v => v.Year == Year && v.Name == name)!; SelectedDocs.Add(new(DocType.CreditNote, s, (pv.Year, pv.AvNr))); SelectedDocumentsList.SelectedIndex = SelectedDocs.Count - 1; RecipientsDeliveryMembersInput.IsChecked = true; } private void DocumentInput_TextChanged(object sender, TextChangedEventArgs evt) { ResetDocuments(); } private void PostalLocation_TextChanged(object sender, TextChangedEventArgs evt) { ResetDocuments(); } private void PostalInput_Changed(object sender, RoutedEventArgs evt) { UpdatePostalEmailRecipients(); } private void OrderInput_Changed(object sender, RoutedEventArgs evt) { ResetDocuments(); } private void DoublePagedInput_Changed(object sender, RoutedEventArgs evt) { ResetDocuments(); } private void PostalSender_TextChanged(object sender, TextChangedEventArgs evt) { ResetDocuments(); } } }