200827PX Added member filter for payment receipts (not finished yet), added sending pdf via E-Mail

This commit is contained in:
phantomix 2020-08-27 17:59:36 +02:00
parent dcf3b05fff
commit 852322578a
4 changed files with 148 additions and 24 deletions

View File

@ -17,6 +17,7 @@ namespace dezentrale.core
public interface IPaymentReceiptProcessData
{
List<Member> MemberList { get; set; }
string DataTemplate { get; set; }
IntermediateFormat DataFormat { get; set; }
@ -24,6 +25,8 @@ namespace dezentrale.core
string FileNamePattern { get; set; }
DateTime StartDate { get; set; }
DateTime EndDate { get; set; }
string StartDateString { get; }
string EndDateString { get; }
}
//In order to be able to ReplaceReflect, we need to have the SvgData and SvgFileName field separated in a class
@ -66,7 +69,21 @@ namespace dezentrale.core
uint step = 0;
try
{
List<string> intermediateFiles = new List<string>();
List<Member> memberList = data.MemberList ?? Program.members.Entries;
// In preparation to send E-Mail and combine multiple files into one mail later,
// we're using a List of KVP with key = filename, value = member reference
List<KeyValuePair<string, Member>> intermediateFiles = new List<KeyValuePair<string, Member>>();
string intermediateExt = "txt";
string finalExt = ".pdf";
switch (data.DataFormat)
{
case IntermediateFormat.Text: intermediateExt = ".txt"; finalExt = ".txt"; break;
case IntermediateFormat.SvgInkscape092: intermediateExt = ".svg"; break;
case IntermediateFormat.SvgInkscapeNew: intermediateExt = ".svg"; break;
case IntermediateFormat.Latex: intermediateExt = ".tex"; break;
}
LogTarget.StepStarted(step, "Loading source SVG template");
IntermediateFile svgTemplate = new IntermediateFile(data.DataTemplate);
@ -86,7 +103,7 @@ namespace dezentrale.core
LogTarget.StepStarted(++step, "Processing all payments to generate temporary SVGs (main step)");
foreach(MoneyTransfer mt in transfers)
{
Member m = Program.members.Find(mt.MemberNumber);
Member m = memberList.Find(x => x.Number == mt.MemberNumber);
if(m == null)
{
LogTarget.LogLine($"Cannot find member {mt.MemberNumber} referenced in MoneyTransfer {mt.Id} (from {mt.ValutaDate})", LogEvent.ELogLevel.Error, "PaymentReceiptProcess");
@ -99,30 +116,49 @@ namespace dezentrale.core
}
IntermediateFile output = svgTemplate.ReplaceReflect(mt).ReplaceReflect(m);
LogTarget.LogLine($"Generating file \"{output.FileName}\"", LogEvent.ELogLevel.Info, "PaymentReceiptProcess");
File.WriteAllText(output.FileName, output.Data);
intermediateFiles.Add(output.FileName);
File.WriteAllText(Path.Combine(data.OutputDirectory, output.FileName + intermediateExt), output.Data);
intermediateFiles.Add(new KeyValuePair<string,Member>(output.FileName,m));
}
LogTarget.StepStarted(++step, "Converting intermediate files to PDF using Inkscape");
foreach (string iFile in intermediateFiles)
LogTarget.StepStarted(++step, "Converting intermediate files to PDF");
foreach (KeyValuePair<string,Member> kvp in intermediateFiles)
{
switch(data.DataFormat)
string sourceFile = Path.Combine(data.OutputDirectory, kvp.Key + intermediateExt);
string pdf = Path.Combine(data.OutputDirectory, kvp.Key + finalExt);
switch (data.DataFormat)
{
case IntermediateFormat.Text: break;
case IntermediateFormat.Text:
LogTarget.LogLine($"Skipping PDF generation for: \"{kvp.Key}{intermediateExt}\"", LogEvent.ELogLevel.Info, "PaymentReceiptProcess");
break;
case IntermediateFormat.SvgInkscape092:
ImportExportBase.RunProcess("inkscape", $"{iFile} --export-pdf={iFile}.pdf", ".", LogTarget);
ImportExportBase.RunProcess("inkscape", $"\"{sourceFile}\" --export-pdf={pdf}", ".", LogTarget);
break;
case IntermediateFormat.SvgInkscapeNew:
ImportExportBase.RunProcess("inkscape", $"{iFile} --export-type=pdf --export-filename={iFile}.pdf", ".", LogTarget);
ImportExportBase.RunProcess("inkscape", $"\"{sourceFile}\" --export-type=pdf --export-filename={pdf}", ".", LogTarget);
break;
case IntermediateFormat.Latex:
ImportExportBase.RunProcess("pdflatex", $"{iFile}", ".", LogTarget);
ImportExportBase.RunProcess("pdflatex", $"\"{sourceFile}\"", ".", LogTarget);
break;
}
}
LogTarget.StepStarted(++step, "E-Mail the PDFs to the members");
LogTarget.LogLine($"This step is still TODO", LogEvent.ELogLevel.Info, "PaymentReceiptProcess");
FormMail receiptMail = FormMail.GenerateMemberPaymentReceipts().ReplaceReflect(data);
foreach (Member m in memberList)
{
//gather entries per member
List<string> outputs = new List<string>();
foreach (KeyValuePair<string, Member> kvp in intermediateFiles)
{
if (m.Number == kvp.Value.Number) outputs.Add(Path.Combine(data.OutputDirectory, kvp.Key + finalExt));
}
//send E-Mail
m.StartLogEvent($"Payment receipts E-Mail ({data.StartDateString} ... {data.EndDateString})",LogEvent.eEventType.EMail, Program.config.LocalUser);
LogSubEvent lse = receiptMail.Send(m, outputs);
m.SaveToFile(true);
}
LogTarget.StepCompleted(4, $"E-Mail the PDFs to the members", true);

View File

@ -569,6 +569,30 @@ namespace dezentrale.model
+ "Dies ist eine automatisch generierte E-Mail.\n"
+ "This is an auto-generated E-Mail.\n",
};
}
public static FormMail GenerateMemberPaymentReceipts()
{
return new FormMail()
{
To = "{EMailName} <{EMail}>",
Subject = "dezentrale-members - Bestätigungen über Mitgliedsbeiträge (Membership payment receipts)",
Body = "Hallo {EMailName}!\n"
+ "\n"
+ "Danke für die Bezahlung deiner Mitgliedsbeiträge!\n"
+ "Anbei die Bestätigungen für die Mitgliedsbeiträge im Zeitraum von {StartDateString} bis {EndDateString}.\n"
+ "\n"
+ "\n"
+ "Hello {EMailName}!\n"
+ "\n"
+ "Thank you for paying your membership fees!\n"
+ "Attached to this E-Mail there are the membership payment receipts from {StartDateString} to {EndDateString}.\n"
+ "\n"
+ "\n"
+ "--\n"
+ "Dies ist eine automatisch generierte E-Mail.\n"
+ "This is an auto-generated E-Mail.\n",
};
}
}
}

View File

@ -241,11 +241,16 @@ namespace dezentrale.view
}
private void mnuMain_Payments_Receipts(object sender, EventArgs e)
{
int year = DateTime.Now.Year - 1;
frmPaymentReceipts receipts = new frmPaymentReceipts()
{
StartDate = new DateTime(2020, 01, 01),
EndDate = new DateTime(2020, 12, 31),
FileNamePattern = "Member{MemberNumber}-{ValutaDateString}-{AmountString}{Currency}"
MemberList = new List<Member>() { Program.members.Find(4) },
DataTemplate = "dezentrale-beitragsquittung-template.svg",
DataFormat = IntermediateFormat.SvgInkscape092,
StartDate = new DateTime(year, 01, 01),
EndDate = new DateTime(year, 12, 31),
FileNamePattern = "Member{MemberNumber}-{ValutaDateString}-{AmountString}{Currency}",
OutputDirectory="."
};
DialogResult dr = receipts.ShowDialog();
if(dr == DialogResult.OK)

View File

@ -11,13 +11,15 @@ namespace dezentrale.view
{
public class frmPaymentReceipts : FormWithOkCancel, IPaymentReceiptProcessData
{
public List<Member> MemberList { get; set; } = null;
public string DataTemplate { get; set; } = "";
public IntermediateFormat DataFormat { get; set; } = IntermediateFormat.Text;
public string OutputDirectory { get; set; } = "";
public string FileNamePattern { get; set; } = "{Number}-{Date}";
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public string StartDateString { get { return StartDate.ToShortDateString(); } }
public string EndDateString { get { return EndDate .ToShortDateString(); } }
private TextBox tbTemplate;
@ -31,7 +33,8 @@ namespace dezentrale.view
{
DialogResult = DialogResult.Cancel;
this.StartPosition = FormStartPosition.CenterParent;
this.Size = new System.Drawing.Size(500, 400);
this.Size = new System.Drawing.Size(483, 185);
this.MinimumSize = new System.Drawing.Size(455, 185);
this.Text = "Generate payment receipts";
this.Controls.Add(new Label()
{
@ -43,13 +46,15 @@ namespace dezentrale.view
this.Controls.Add(tbTemplate = new TextBox()
{
Location = new Point(lm + 113, 0 * line + tm),
Width = 200,
Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right,
Width = 300,
});
Button btnBrowseTemplate;
this.Controls.Add(btnBrowseTemplate = new Button()
{
Text = "...",
Location = new Point(lm + 333, 0 * line + tm),
Location = new Point(lm + 417, 0 * line + tm),
Anchor = AnchorStyles.Top | AnchorStyles.Right,
Width = 40,
});
btnBrowseTemplate.Click += btnBrowseTemplate_Click;
@ -65,7 +70,8 @@ namespace dezentrale.view
{
DropDownStyle = ComboBoxStyle.DropDownList,
Location = new Point(lm + 113, 1 * line + tm),
Width = 200,
Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right,
Width = 345,
});
foreach (IntermediateFormat fmt in Enum.GetValues(typeof(IntermediateFormat)))
cbDataFormat.Items.Add(fmt);
@ -80,13 +86,15 @@ namespace dezentrale.view
this.Controls.Add(tbOutputDirectory = new TextBox()
{
Location = new Point(lm + 113, 2 * line + tm),
Width = 200,
Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right,
Width = 300,
});
Button btnBrowseOutputDir;
this.Controls.Add(btnBrowseOutputDir = new Button()
{
Text = "...",
Location = new Point(lm + 333, 2 * line + tm),
Location = new Point(lm + 417, 2 * line + tm),
Anchor = AnchorStyles.Top | AnchorStyles.Right,
Width = 40,
});
btnBrowseOutputDir.Click += btnBrowseOutputDir_Click;
@ -101,19 +109,65 @@ namespace dezentrale.view
this.Controls.Add(tbFileNamePattern = new TextBox()
{
Location = new Point(lm + 113, 3 * line + tm),
Width = 240,
Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right,
Width = 345,
});
this.Controls.Add(new Label()
{
Text = "Start date:",
Location = new Point(lm, 4 * line + tm + labelOffs),
Size = new Size(110, labelHeight),
TextAlign = ContentAlignment.BottomRight,
});
this.Controls.Add(dtStartDate = new DateTimePicker()
{
Location = new Point(lm + 113, 4 * line + tm),
Width = 100,
Format = DateTimePickerFormat.Short,
});
this.Controls.Add(new Label()
{
Text = "00:00:00",
Location = new Point(lm + 160, 4 * line + tm + labelOffs),
Size = new Size(110, labelHeight),
TextAlign = ContentAlignment.BottomRight,
});
this.Controls.Add(new Label()
{
Text = "End date:",
Location = new Point(lm, 5 * line + tm + labelOffs),
Size = new Size(110, labelHeight),
TextAlign = ContentAlignment.BottomRight,
});
this.Controls.Add(dtEndDate = new DateTimePicker()
{
Location = new Point(lm + 113, 5 * line + tm),
Width = 100,
Format = DateTimePickerFormat.Short,
});
this.Controls.Add(new Label()
{
Text = "23:59:59",
Location = new Point(lm + 160, 5 * line + tm + labelOffs),
Size = new Size(110, labelHeight),
TextAlign = ContentAlignment.BottomRight,
});
AddOkCancel(this, btnOK_Click, btnCancel_Click);
//We must run the filling of the data fields in the load event, as they
//might have been changed after the constructor is executed
this.Shown += (sender, e) =>
this.Load += (sender, e) =>
{
tbTemplate.Text = DataTemplate;
cbDataFormat.SelectedItem = DataFormat;
tbOutputDirectory.Text = OutputDirectory;
tbFileNamePattern.Text = FileNamePattern;
dtStartDate.Value = StartDate;
dtEndDate.Value = EndDate;
};
}
@ -140,6 +194,11 @@ namespace dezentrale.view
private void btnOK_Click(object sender, EventArgs e)
{
DataTemplate = tbTemplate.Text;
DataFormat = (IntermediateFormat) cbDataFormat.SelectedItem;
OutputDirectory = tbOutputDirectory.Text;
FileNamePattern = tbFileNamePattern.Text;
StartDate = dtStartDate.Value;
EndDate = dtEndDate.Value.AddSeconds(24*60*60 - 1);
DialogResult = DialogResult.OK;
this.Close();
}