282 lines
13 KiB
C#
282 lines
13 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
|
|
using dezentrale.model;
|
|
namespace dezentrale.core
|
|
{
|
|
|
|
//! \short Functionality for automatic processing that can be run in a scheduled way
|
|
//! \brief A huge port of the dezentrale-members software is designed to help with
|
|
//! previously manual tasks by defining an automated manner / refresh user
|
|
//! data according to repeated actions, e.g. membership payments.
|
|
public class Cronjob
|
|
{
|
|
//! \brief Perform a Run on one Member. This will perform all necessary actions
|
|
//! at this point.
|
|
//! \param m Member to check
|
|
//! \param sm Member entry for the Schatzmeister account, in order to send notification mails for some events
|
|
public static void Run(Member m, Member sm)
|
|
{
|
|
Console.WriteLine($"Processing member {m.Number:D3} ({m.Nickname})...");
|
|
CheckGreeting(m);
|
|
CheckSpawning(m);
|
|
CheckBalance_PreDegrade(m, sm);
|
|
BalanceDegrade(m);
|
|
CheckBalance_PostDegrade(m, sm);
|
|
}
|
|
//! \short Helper function for Run(m, sm). Determines sm automatically.
|
|
//! \param m Member to check
|
|
public static void Run(Member m)
|
|
{
|
|
Member sm = Program.members.Find(Member.eRole.Schatzmeister);
|
|
if (sm == null)
|
|
{
|
|
Console.WriteLine($"Cronjob.Run(Member {m.Number}, {m.Nickname}): ERROR, cannot find sm account!");
|
|
}
|
|
else
|
|
Run(m, sm);
|
|
}
|
|
|
|
//! \short Perform a cronjob run on a List of members.
|
|
//! \brief Perform a cronjob run on a List of members. This may be a
|
|
//! partial List (e.g. by checking member entries from GUI)
|
|
//! \param partialList List of members to process. If null, the program will process the complete member list.
|
|
public static void Run(List<Member> partialList = null)
|
|
{
|
|
Member sm = Program.members.Find(Member.eRole.Schatzmeister);
|
|
if (sm == null)
|
|
{
|
|
Console.WriteLine($"Cronjob.Run(List of {partialList.Count} members): ERROR, cannot find schatzmeister account!");
|
|
}
|
|
if (partialList == null || partialList == Program.members.Entries)
|
|
{
|
|
foreach (Member m in Program.members.Entries) Run(m, sm);
|
|
|
|
//GenerateReport();
|
|
if (!Program.config.LastCronjobRun.Equals(ProgramStartTime))
|
|
{
|
|
Member schriftfuehrer = Program.members.Find(Member.eRole.Schriftfuehrer);
|
|
try { if (sm != null) new MemberReport(true).Send(sm); }
|
|
catch (Exception ex) { Console.WriteLine($"Cronjob.GenerateReport(): Error while sending report to Schatzmeister: {ex.Message}"); }
|
|
try { if (schriftfuehrer != null) new MemberReport(false).Send(schriftfuehrer); }
|
|
catch (Exception ex) { Console.WriteLine($"Cronjob.GenerateReport(): Error while sending report to schriftfuehrer: {ex.Message}"); }
|
|
Console.WriteLine("Cronjob.Run(): Member Reports sent.");
|
|
}
|
|
|
|
Program.config.LastCronjobRun = ProgramStartTime;
|
|
try
|
|
{
|
|
XmlData.SaveToFile(Program.ConfigFile, Program.config);
|
|
Console.WriteLine("Cronjob.Run(): Saved config.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"Cronjob.Run(): Error while saving config: {ex.Message}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foreach (Member m in partialList) Run(m, sm);
|
|
}
|
|
}
|
|
|
|
//! \short Send greetings E-Mail to new member, if conditions are met.
|
|
//! \note Also sends a mail to vorstand mailinglist
|
|
//! \param m Member to check
|
|
private static void CheckGreeting(Member m)
|
|
{
|
|
if (m.Status == Member.eStatus.Uninitialized && m.EMail.Length > 0)
|
|
{
|
|
try
|
|
{
|
|
//Send greetings mail,//send mail to vorstand
|
|
m.StartLogEvent("Sending greetings", LogEvent.eEventType.Greetings, "automatic");
|
|
FormMail
|
|
tmp = FormMail.GenerateNewMemberWelcome().ReplaceReflect(m);
|
|
LogSubEvent lse = tmp.Send();
|
|
m.CurrentLog.SubEvents.Add(lse);
|
|
if (lse.Type == LogEvent.eEventType.Error) throw new Exception("Sending mail to user failed");
|
|
tmp = FormMail.GenerateNewMemberVorstand().ReplaceReflect(Program.config.VS).ReplaceReflect(m);
|
|
lse = tmp.Send();
|
|
m.CurrentLog.SubEvents.Add(lse);
|
|
if (lse.Type == LogEvent.eEventType.Error) throw new Exception("Sending mail to vorstand failed");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
m.CurrentLog.SubEvents.Add(new LogSubEvent()
|
|
{
|
|
Type = LogEvent.eEventType.Error,
|
|
Topic = $"Failed:\n{ex.Message}",
|
|
});
|
|
Console.WriteLine($"Cannot send mail: {ex.Message}");
|
|
m.SaveToFile();
|
|
return;
|
|
}
|
|
|
|
m.GreetedDate = DateTime.Now;
|
|
m.Status = Member.eStatus.Greeted;
|
|
m.SaveToFile();
|
|
}
|
|
}
|
|
|
|
//! \short Check if a previously greeted member is ready to become active.
|
|
//! \note Also sends a mail to vorstand mailinglist
|
|
private static void CheckSpawning(Member m)
|
|
{
|
|
if(m.Status != Member.eStatus.Greeted)
|
|
{
|
|
//Console.WriteLine("status != GREETED");
|
|
return;
|
|
}
|
|
{
|
|
TimeSpan ts = DateTime.Now.Subtract(m.GreetedDate);
|
|
if (ts.TotalDays < 7)
|
|
{
|
|
Console.WriteLine("TotalDays < 7");
|
|
return;
|
|
}
|
|
if (m.SpawnDate > DateTime.Now)
|
|
{
|
|
Console.WriteLine("spawnDate > now");
|
|
return;
|
|
}
|
|
if ((m.AccountBalance < (Int64) m.PaymentAmount) && (m.PaymentsTotal < m.PaymentAmount))
|
|
{
|
|
Console.WriteLine("payments < paymentAmount");
|
|
return;
|
|
}
|
|
if (m.AccountBalance < 0)
|
|
{
|
|
Console.WriteLine("accountbalance < 0");
|
|
return;
|
|
}
|
|
Console.WriteLine("Sending activation mail");
|
|
m.StartLogEvent("Sending activation mail", LogEvent.eEventType.Activation, "automatic");
|
|
//Send greetings mail, send mail to vorstand
|
|
try
|
|
{
|
|
FormMail
|
|
tmp = FormMail.GenerateMemberAccountActivated().ReplaceReflect(Program.config).ReplaceReflect(m);
|
|
LogSubEvent
|
|
lse = tmp.Send();
|
|
m.CurrentLog.SubEvents.Add(lse);
|
|
if (lse.Type == LogEvent.eEventType.Error) throw new Exception("Sending mail to user failed");
|
|
tmp = FormMail.GenerateMemberAccountVorstand().ReplaceReflect(Program.config).ReplaceReflect(Program.config.VS).ReplaceReflect(m);
|
|
lse = tmp.Send();
|
|
m.CurrentLog.SubEvents.Add(lse);
|
|
if (lse.Type == LogEvent.eEventType.Error) throw new Exception("Sending mail to vorstand failed");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
m.CurrentLog.SubEvents.Add(new LogSubEvent()
|
|
{
|
|
Type = LogEvent.eEventType.Error,
|
|
Topic = $"Failed:\n{ex.Message}",
|
|
});
|
|
Console.WriteLine($"Cannot send mail: {ex.Message}");
|
|
m.FinishLogEvent();
|
|
m.SaveToFile();
|
|
return;
|
|
}
|
|
m.Status = Member.eStatus.Active;
|
|
m.SaveToFile();
|
|
}
|
|
}
|
|
|
|
|
|
//! \brief This shall degrade the balance by a membership fee per month.
|
|
//! Calls to this function within the same month will not degrade
|
|
//! balance. Subsequent calls at the end of the month, and then
|
|
//! at the start of the next month, will degrade balance.
|
|
//! \param m Member to check
|
|
private static void BalanceDegrade(Member m)
|
|
{
|
|
if (m.Status == Member.eStatus.Greeted && (DateTime.Now < m.SpawnDate)) return;
|
|
if (m.Status == Member.eStatus.Uninitialized || m.Status == Member.eStatus.Disabled || m.Status == Member.eStatus.Deleted) return;
|
|
|
|
|
|
uint thisMonth = (uint) DateTime.Now.Month;
|
|
uint thisYear = (uint) DateTime.Now.Year;
|
|
uint months = 0;
|
|
DateTime lastDegrade = m.LastBalanceDegrade;
|
|
if (lastDegrade < m.SpawnDate) lastDegrade = m.SpawnDate;
|
|
while (lastDegrade.Month != thisMonth) { lastDegrade = lastDegrade.AddMonths(1); months++; }
|
|
while (lastDegrade.Year < thisYear) { lastDegrade = lastDegrade.AddYears(1); months += 12; }
|
|
|
|
if (months > 0)
|
|
{
|
|
m.StartLogEvent($"Membership fee: {months} Months * {m.PaymentAmount} EURcents = {months * m.PaymentAmount} EURcents", LogEvent.eEventType.MembershipFee, "automatic");
|
|
m.AccountBalance -= (Int64)(((Int64)months) * (Int64)m.PaymentAmount);
|
|
m.LastBalanceDegrade = DateTime.Now;
|
|
m.SaveToFile();
|
|
}
|
|
}
|
|
|
|
//! \brief During program execution, we want to keep a constant
|
|
//! timestamp for cronjob activity, to be able to compare for
|
|
//! multiple runs (e.g. member list) and not to store it every
|
|
//! time to disk
|
|
private static DateTime ProgramStartTime { get; set; } = DateTime.Now;
|
|
|
|
//! \short Necessary actions prior to BalanceDegrade.
|
|
private static void CheckBalance_PreDegrade(Member m, Member sm = null)
|
|
{
|
|
if (m.Status != Member.eStatus.Active || m.PaymentAmount == 0) return;
|
|
|
|
if (m.AccountBalance <= -2 * (int) m.PaymentAmount)
|
|
{
|
|
m.StartLogEvent($"Deactivation (insufficient payment)", LogEvent.eEventType.Deactivation, "automatic");
|
|
|
|
m.CurrentLog.SubEvents.Add(FormMail.GenerateBalanceNegativeMemberDeactivation().ReplaceReflect(Program.config.VS).Send(m));
|
|
m.CurrentLog.SubEvents.Add(FormMail.GenerateBalanceNegativeMemberDeactivationVS().ReplaceReflect(Program.config.VS).Send(m));
|
|
m.Status = Member.eStatus.Disabled;
|
|
m.SaveToFile();
|
|
}
|
|
|
|
}
|
|
private static void CheckBalance_PostDegrade(Member m, Member sm = null)
|
|
{
|
|
if (m.Status != Member.eStatus.Active || m.PaymentAmount == 0) return;
|
|
bool skipInsufficientNotify = false;
|
|
if (DateTime.Now.Day < 8) skipInsufficientNotify = true;
|
|
|
|
if (m.AccountBalance <= -2 * (int)m.PaymentAmount)
|
|
{
|
|
if (!skipInsufficientNotify)
|
|
{
|
|
m.StartLogEvent($"Insufficient amount of payments #2", LogEvent.eEventType.EMail, "automatic");
|
|
m.CurrentLog.SubEvents.Add(FormMail.GenerateBalanceNegativeMemberNotify2().Send(m));
|
|
if (sm != null)
|
|
{
|
|
FormMail below2sm = FormMail.GenerateBalanceNegativeNotify2SM().ReplaceReflect(m);
|
|
below2sm.To = $"{sm.Nickname} <{sm.EMail}>";
|
|
m.CurrentLog.SubEvents.Add(below2sm.Send());
|
|
} else
|
|
Console.WriteLine("ERROR: CheckBalance_PostDegrade: sm = null");
|
|
}
|
|
} else if (m.AccountBalance < 0)
|
|
{
|
|
if (!skipInsufficientNotify)
|
|
{
|
|
m.StartLogEvent($"Insufficient amount of payments #1", LogEvent.eEventType.EMail, "automatic");
|
|
m.CurrentLog.SubEvents.Add(FormMail.GenerateBalanceNegativeMemberNotify1().Send(m));
|
|
}
|
|
} else if (m.AccountBalance > 200 * 100)
|
|
{
|
|
m.StartLogEvent($"Excess amount of payments", LogEvent.eEventType.EMail, "automatic");
|
|
if (sm != null)
|
|
{
|
|
FormMail above200 = FormMail.GenerateBalanceAbove200NotifySM().ReplaceReflect(m);
|
|
above200.To = $"{sm.Nickname} <{sm.EMail}>";
|
|
m.CurrentLog.SubEvents.Add(above200.Send());
|
|
} else
|
|
Console.WriteLine("ERROR: CheckBalance_PostDegrade: sm = null");
|
|
}
|
|
if (m.CurrentLog != null)
|
|
m.SaveToFile();
|
|
}
|
|
}
|
|
}
|