212 lines
11 KiB
C#
212 lines
11 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using dezentrale.model;
|
|
using dezentrale.model.money;
|
|
|
|
namespace dezentrale.core
|
|
{
|
|
public enum IntermediateFormat
|
|
{
|
|
Text,
|
|
SvgInkscape092,
|
|
SvgInkscapeNew,
|
|
Latex
|
|
}
|
|
|
|
public interface IPaymentReceiptProcessData
|
|
{
|
|
List<Member> MemberList { get; set; }
|
|
bool AllMembers { get; set; }
|
|
string DataTemplate { get; set; }
|
|
IntermediateFormat DataFormat { get; set; }
|
|
|
|
string OutputDirectory { get; set; }
|
|
string FileNamePattern { get; set; }
|
|
DateTime StartDate { get; set; }
|
|
DateTime EndDate { get; set; }
|
|
string StartDateString { get; }
|
|
string EndDateString { get; }
|
|
bool SendEmail { get; set; }
|
|
bool WriteProtectPdf { get; set; }
|
|
}
|
|
|
|
//In order to be able to ReplaceReflect, we need to have the SvgData and SvgFileName field separated in a class
|
|
class IntermediateFile : ReplaceReflectEntity<IntermediateFile>
|
|
{
|
|
|
|
public string FileName { get; set; } = "";
|
|
public string Data { get; set; } = "";
|
|
|
|
public IntermediateFile() { }
|
|
public IntermediateFile(string fileName)
|
|
{
|
|
FileName = fileName;
|
|
Data = File.ReadAllText(fileName);
|
|
}
|
|
}
|
|
|
|
//! \short Import functionality for the database contents
|
|
//! \brief The normal export workflow is to pull the newest commits from mercurial
|
|
//! or git and update/checkout them. After that, there is a packed zip file
|
|
//! available that will be unpacked to the data directory
|
|
public class PaymentReceiptProcess : BackgroundProcess
|
|
{
|
|
private IPaymentReceiptProcessData data = null;
|
|
|
|
public PaymentReceiptProcess(IPaymentReceiptProcessData data)
|
|
{
|
|
this.data = data;
|
|
|
|
Caption = "Generate receipt PDF for each membership payment";
|
|
Steps = 6;
|
|
}
|
|
|
|
//! \short Run receipt generation process
|
|
//! \brief For each member-MoneyTransfer combination, generate a
|
|
//! temporary SVG output, then run Inkscape to generate PDF
|
|
//! \return true if the generation was successful.
|
|
protected override bool Run()
|
|
{
|
|
uint step = 0;
|
|
try
|
|
{
|
|
List<Member> memberList;
|
|
bool allMembers = data.AllMembers;
|
|
if (data.MemberList == null || data.MemberList.Count < 1)
|
|
{
|
|
if(!data.AllMembers)
|
|
{
|
|
LogTarget.LogLine($"No member list provided", LogEvent.ELogLevel.Error, "PaymentReceiptProcess");
|
|
return false;
|
|
}
|
|
memberList = Program.members.Entries;
|
|
} else
|
|
{
|
|
memberList = allMembers ? Program.members.Entries : data.MemberList;
|
|
LogTarget.LogLine($"Processing {memberList.Count} entries (AllMembers={allMembers})", LogEvent.ELogLevel.Error, "PaymentReceiptProcess");
|
|
}
|
|
|
|
// 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);
|
|
svgTemplate.FileName = data.FileNamePattern;
|
|
LogTarget.LogLine($"Loaded \"{data.DataTemplate}\" ({svgTemplate.Data.Length} characters) into memory.", LogEvent.ELogLevel.Info, "PaymentReceiptProcess");
|
|
|
|
|
|
LogTarget.StepStarted(++step, "Filtering MoneyTransfers for the given timeframe");
|
|
List<MoneyTransfer> transfers = Program.MoneyTransfers.Entries.Where //All entries with
|
|
(o=>((o.TransferType == MoneyTransfer.eTransferType.MembershipPayment) //Correct transfer type,
|
|
&& (o.ValutaDate.CompareTo(data.StartDate) >= 0) //After start date,
|
|
&& (o.ValutaDate.CompareTo(data.EndDate) <= 0) //Before end date
|
|
)).ToList();
|
|
LogTarget.LogLine($"{transfers.Count} MoneyTransfers to process.", LogEvent.ELogLevel.Info, "PaymentReceiptProcess");
|
|
|
|
|
|
LogTarget.StepStarted(++step, "Processing all payments to generate temporary SVGs (main step)");
|
|
foreach(MoneyTransfer mt in transfers)
|
|
{
|
|
Member m = memberList.Find(x => x.Number == mt.MemberNumber);
|
|
if(m == null)
|
|
{
|
|
//this is only an error, if all members are selected in the dialog, we can check this.
|
|
if(allMembers)
|
|
LogTarget.LogLine($"Cannot find member {mt.MemberNumber} referenced in MoneyTransfer {mt.Id} (from {mt.ValutaDate})", LogEvent.ELogLevel.Error, "PaymentReceiptProcess");
|
|
//but in every case, we need to skip this MoneyTransfer.
|
|
continue;
|
|
}
|
|
if(!m.MoneyTransfersIds.Contains(mt.Id))
|
|
{
|
|
LogTarget.LogLine($"Member {m.Nickname} ({m.Number}) doesn't reference MoneyTransfer {mt.Id} (from {mt.ValutaDate})", LogEvent.ELogLevel.Error, "PaymentReceiptProcess");
|
|
continue;
|
|
}
|
|
IntermediateFile output = svgTemplate.ReplaceReflect(mt).ReplaceReflect(m);
|
|
LogTarget.LogLine($"Generating file \"{output.FileName}\"", LogEvent.ELogLevel.Info, "PaymentReceiptProcess");
|
|
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");
|
|
foreach (KeyValuePair<string,Member> kvp in intermediateFiles)
|
|
{
|
|
string sourceFile = Path.Combine(data.OutputDirectory, kvp.Key + intermediateExt);
|
|
string pdf = Path.Combine(data.OutputDirectory, kvp.Key + finalExt);
|
|
switch (data.DataFormat)
|
|
{
|
|
case IntermediateFormat.Text:
|
|
LogTarget.LogLine($"Skipping PDF generation for: \"{kvp.Key}{intermediateExt}\"", LogEvent.ELogLevel.Info, "PaymentReceiptProcess");
|
|
break;
|
|
case IntermediateFormat.SvgInkscape092:
|
|
ImportExportBase.RunProcess("inkscape", $"\"{sourceFile}\" --export-pdf={pdf}", ".", LogTarget);
|
|
break;
|
|
case IntermediateFormat.SvgInkscapeNew:
|
|
ImportExportBase.RunProcess("inkscape", $"\"{sourceFile}\" --export-type=pdf --export-filename={pdf}", ".", LogTarget);
|
|
break;
|
|
case IntermediateFormat.Latex:
|
|
ImportExportBase.RunProcess("pdflatex", $"\"{sourceFile}\"", ".", LogTarget);
|
|
break;
|
|
}
|
|
|
|
if(data.WriteProtectPdf && data.DataFormat != IntermediateFormat.Text)
|
|
{
|
|
//Write-protecting pdf
|
|
ImportExportBase.RunProcess("qpdf", $"--encrypt \"\" \"random-owner-pw\" 256 --modify=none -- \"{pdf}\" \"{pdf}.writeprotect\"", ".", LogTarget);
|
|
File.Replace(pdf, pdf + ".writeprotect", pdf + ".writable");
|
|
}
|
|
}
|
|
|
|
LogTarget.StepStarted(++step, "E-Mail the PDFs to the members");
|
|
if (data.SendEmail)
|
|
{
|
|
FormMail receiptMail = Program.mailTemplates.MemberPaymentReceipts.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);
|
|
try
|
|
{
|
|
m.CurrentLog.SubEvents.Add(receiptMail.Send(m, outputs));
|
|
} catch(Exception ex)
|
|
{
|
|
LogTarget.LogLine($"Cannot send payment receipts E-Mail: {ex.Message}", LogEvent.ELogLevel.Error);
|
|
m.CurrentLog.SubEvents.Add(new LogSubEvent() { Type = LogEvent.eEventType.Error, Topic = "Email notification error", Details = ex.Message, });
|
|
}
|
|
m.SaveToFile(null, true);
|
|
}
|
|
} else
|
|
{
|
|
LogTarget.LogLine($"This step is skipped.", LogEvent.ELogLevel.Info, "PaymentReceiptProcess");
|
|
}
|
|
LogTarget.StepCompleted(4, $"E-Mail the PDFs to the members", true);
|
|
|
|
return true;
|
|
} catch(Exception ex)
|
|
{
|
|
LogTarget.LogLine($"An Error occurred: {ex.Message}", LogEvent.ELogLevel.Error, "PaymentReceiptProcess");
|
|
LogTarget.StepCompleted(step, $"E-Mail the PDFs to the members", false);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
} |