200825PX Added PaymentReceiptProcess, moved FormMail code to ReplaceReflectEntity<T> for more versatility. Creating SVG basically works
This commit is contained in:
parent
fc97fa4695
commit
63680d6f7e
|
@ -44,7 +44,7 @@ namespace dezentrale
|
|||
{
|
||||
public class Program
|
||||
{
|
||||
public static uint VersionNumber { get; private set; } = 0x20081400;
|
||||
public static uint VersionNumber { get; private set; } = 0x20082500;
|
||||
public static string VersionString { get; private set; } = $"{VersionNumber:x}";
|
||||
|
||||
public static string AppData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using dezentrale.model;
|
||||
using dezentrale.model.money;
|
||||
|
||||
namespace dezentrale.core
|
||||
{
|
||||
public interface IPaymentReceiptProcessData
|
||||
{
|
||||
string SvgTemplate { get; set; }
|
||||
string OutputDirectory { get; set; }
|
||||
string FileNamePattern { get; set; }
|
||||
DateTime StartDate { get; set; }
|
||||
DateTime EndDate { get; set; }
|
||||
}
|
||||
|
||||
//In order to be able to ReplaceReflect, we need to have the SvgData and SvgFileName field separated in a class
|
||||
class SvgFile : ReplaceReflectEntity<SvgFile>
|
||||
{
|
||||
public string SvgFileName { get; set; } = "";
|
||||
public string SvgData { get; set; } = "";
|
||||
|
||||
public SvgFile() { }
|
||||
public SvgFile(string fileName)
|
||||
{
|
||||
SvgFileName = fileName;
|
||||
SvgData = 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 = 5;
|
||||
}
|
||||
|
||||
//! \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<string> svgOutputFiles = new List<string>();
|
||||
|
||||
LogTarget.StepStarted(step, "Loading source SVG template");
|
||||
SvgFile svgTemplate = new SvgFile(data.SvgTemplate);
|
||||
svgTemplate.SvgFileName = data.FileNamePattern;
|
||||
LogTarget.LogLine($"Loaded \"{data.SvgTemplate}\" ({svgTemplate.SvgData.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 = Program.members.Find(mt.MemberNumber);
|
||||
if(m == null)
|
||||
{
|
||||
LogTarget.LogLine($"Cannot find member {mt.MemberNumber} referenced in MoneyTransfer {mt.Id} (from {mt.ValutaDate})", LogEvent.ELogLevel.Error, "PaymentReceiptProcess");
|
||||
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;
|
||||
}
|
||||
SvgFile output = svgTemplate.ReplaceReflect(mt).ReplaceReflect(m);
|
||||
LogTarget.LogLine($"Generating file \"{output.SvgFileName}\"", LogEvent.ELogLevel.Info, "PaymentReceiptProcess");
|
||||
File.WriteAllText(output.SvgFileName, output.SvgData);
|
||||
svgOutputFiles.Add(output.SvgFileName);
|
||||
}
|
||||
|
||||
LogTarget.StepStarted(++step, "Converting SVGs to PDF using Inkscape");
|
||||
foreach (string svg in svgOutputFiles)
|
||||
{
|
||||
ImportExportBase.RunProcess("inkscape", $"{svg} --export-filename={svg}.pdf ", ".", LogTarget);
|
||||
}
|
||||
|
||||
LogTarget.StepStarted(++step, "E-Mail the PDFs to the members");
|
||||
LogTarget.LogLine($"This step is still TODO", 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace dezentrale.core
|
||||
{
|
||||
public class ReplaceReflectEntity<T> where T : ReplaceReflectEntity<T> //Only child classes are allowed as T parameters
|
||||
{
|
||||
protected ReplaceReflectEntity()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public T ReplaceReflect(object o)
|
||||
{
|
||||
T ret = (T) this.MemberwiseClone();
|
||||
|
||||
PropertyInfo[] objProperties = o.GetType().GetProperties();
|
||||
|
||||
foreach (var childProperty in ret.GetType().GetProperties())
|
||||
{
|
||||
if (childProperty.PropertyType != typeof(string)) continue;
|
||||
if (!childProperty.CanRead) continue;
|
||||
if (!childProperty.CanWrite) continue;
|
||||
|
||||
string propVal = (string)childProperty.GetValue(ret, null);
|
||||
if (propVal == null) continue;
|
||||
bool changed = false;
|
||||
foreach (var objProperty in objProperties)
|
||||
{
|
||||
if (!objProperty.CanRead) continue;
|
||||
|
||||
//check if objProperty occurs in mailProperty contents, then replace
|
||||
string token = $"{{{objProperty.Name}}}";
|
||||
string tokenValue;
|
||||
try
|
||||
{
|
||||
tokenValue = objProperty.GetValue(o, null).ToString();
|
||||
}
|
||||
catch (NullReferenceException)
|
||||
{
|
||||
//Console.WriteLine($"FormMail.ReplaceReflect({o}): property {token}: {ex.Message}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (propVal.Contains(token))
|
||||
{
|
||||
//NOTE: This is problematic because it allows the user to generate recursive replacements by setting e.g. the Nickname to "{PgpFingerprint}"
|
||||
propVal = propVal.Replace(token, tokenValue);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (changed)
|
||||
{
|
||||
childProperty.SetValue(ret, propVal, null);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -194,6 +194,8 @@
|
|||
<Compile Include="core\Utils.cs" />
|
||||
<Compile Include="view\frmPaymentReceipts.cs" />
|
||||
<Compile Include="view\FormWithOkCancel.cs" />
|
||||
<Compile Include="core\PaymentReceiptProcess.cs" />
|
||||
<Compile Include="core\ReplaceReflectEntity.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
|
|
|
@ -32,6 +32,7 @@ namespace dezentrale.model.money
|
|||
[XmlElement] public string TransferReason { get; set; } = "";
|
||||
|
||||
[XmlIgnore] public string AmountString { get { return core.Utils.Int64FPToString(Amount);/* $"{((float) Amount / 100)}";*/ } }
|
||||
[XmlIgnore] public string ValutaDateString { get { return ValutaDate.ToShortDateString(); } }
|
||||
|
||||
public override string ToString() { return Id; }
|
||||
public MoneyTransfer()
|
||||
|
@ -47,12 +48,16 @@ namespace dezentrale.model.money
|
|||
&& this.GetType().Equals(other.GetType())
|
||||
&& this.Currency.Equals(other.Currency)
|
||||
&& this.TransferReason.Equals(other.TransferReason);
|
||||
}
|
||||
|
||||
public int CompareTo(DateTime other)
|
||||
{
|
||||
return ValutaDate.CompareTo(other);
|
||||
}
|
||||
|
||||
public int CompareTo(MoneyTransfer other)
|
||||
{
|
||||
if (other == null) return 1;
|
||||
else return ValutaDate.CompareTo(other.ValutaDate);
|
||||
else return CompareTo(other.ValutaDate);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -244,8 +244,14 @@ namespace dezentrale.view
|
|||
frmPaymentReceipts receipts = new frmPaymentReceipts();
|
||||
DialogResult dr = receipts.ShowDialog();
|
||||
if(dr == DialogResult.OK)
|
||||
{
|
||||
|
||||
{
|
||||
receipts.SvgTemplate = "dezentrale-beitragsquittung-template.svg";
|
||||
receipts.StartDate = new DateTime(2020, 01, 01);
|
||||
receipts.EndDate = new DateTime(2020, 12, 31);
|
||||
receipts.FileNamePattern = "Member{MemberNumber}-{ValutaDateString}-{AmountString}{Currency}.svg";
|
||||
PaymentReceiptProcess rec = new PaymentReceiptProcess(receipts);
|
||||
frmProcessWithLog frmRec = new frmProcessWithLog(rec, true);
|
||||
dr = frmRec.ShowDialog();
|
||||
}
|
||||
}
|
||||
private void mnuMain_Help_About(object sender, EventArgs e)
|
||||
|
|
|
@ -9,7 +9,7 @@ using dezentrale.model.money;
|
|||
|
||||
namespace dezentrale.view
|
||||
{
|
||||
public class frmPaymentReceipts : FormWithOkCancel
|
||||
public class frmPaymentReceipts : FormWithOkCancel, IPaymentReceiptProcessData
|
||||
{
|
||||
|
||||
public string SvgTemplate { get; set; } = "";
|
||||
|
|
Loading…
Reference in New Issue