dmdb/model/Member.cs

329 lines
17 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml.Serialization;
using dezentrale.model.money;
namespace dezentrale.model
{
public class Member : XmlData, IEquatable<Member>, IComparable<Member>
{
public enum eRole
{
Normal = 0,
Vorstandsvorsitzender,
Schatzmeister,
Schriftfuehrer,
Beisitzer,
}
public enum eType
{
Regulaer = 0,
Foerdermitglied
}
public enum eStatus
{
Uninitialized = 0,
Greeted,
Active,
Bannend,
Disabled,
Deleted,
}
public enum ePaymentClass
{
Reduced = 0,
Normal,
}
private uint number = 0;
private eRole role = eRole.Normal;
private eType type = eType.Regulaer;
private eStatus status = eStatus.Uninitialized;
private string remarks = "";
private uint mvMiss = 0;
private Int64 accountBalance = 0;
private bool evaluateAccountInCharge = false;
private string bankAccountInCharge = ""; //CSV Import: this bank account is associated with the member
private string bankTransferRegEx = "";
private string nickname = "";
private string firstName = "";
private string lastName = "";
private string street = "";
private string houseNo = "";
private string zipcode = "";
private string city = "";
private Country.eCountry countryCode = Country.eCountry.Germany;
private DateTime birthday;
private string email = "";
private string emailName = ""; //Name to use via E-Mail
private string pgpFingerprint = "";
private bool mvInvitationByPost = false;
private DateTime spawnDate;
private UInt64 paymentAmount = Program.config.RegularPaymentAmount;
private ePaymentClass paymentClass = ePaymentClass.Normal;
private bool paymentNotify = false;
private DateTime memberFormDate;
private DateTime reducedFeeValid;
private int debtLevel = 0;
//This is a bit ugly but I didn't have a good idea how to solve it better.
//The main goal is to track every change to every property.
//Therefore, private variables are defined and the get/set methods are handled manually.
//A property change is then tracked using LogPropertyChange()
[XmlAttribute] public uint Number { get { return number; } set { LogPropertyChange("Number", number, value); number = value; } }
[XmlAttribute] public eType Type { get { return type; } set { LogPropertyChange("Type", type, value); type = value; } }
[XmlAttribute] public eStatus Status { get { return status; } set { LogPropertyChange("Status", status, value); status = value; } }
[XmlElement] public eRole Role { get { return role; } set { LogPropertyChange("Role", role, value); role = value; } }
[XmlElement] public string Remarks { get { return remarks; } set { LogPropertyChange("Remarks", remarks, value); remarks = value; } }
[XmlElement] public uint MvMissCounter { get { return mvMiss; } set { LogPropertyChange("MvMissCounter", mvMiss, value); mvMiss = value; } }
[XmlElement] public Int64 AccountBalance { get { return accountBalance; } set { LogPropertyChange("AccountBalance", accountBalance, value); accountBalance = value; } }
[XmlElement] public bool EvaluateAccountInCharge { get { return evaluateAccountInCharge; } set { LogPropertyChange("EvaluateAccountInCharge", evaluateAccountInCharge, value); evaluateAccountInCharge = value; } }
[XmlElement] public string BankAccountInCharge { get { return bankAccountInCharge; } set { LogPropertyChange("BankAccountInCharge", bankAccountInCharge, value); bankAccountInCharge = value; } }
[XmlElement] public string BankTransferRegEx { get { return bankTransferRegEx; } set { LogPropertyChange("BankTransferRegEx", bankTransferRegEx, value); bankTransferRegEx = value; } }
//personal data
[XmlAttribute] public string Nickname { get { return nickname; } set { LogPropertyChange("NickName", nickname, value); nickname = value; } }
[XmlElement] public string FirstName { get { return firstName; } set { LogPropertyChange("FirstName", firstName, value); firstName = value; } }
[XmlElement] public string LastName { get { return lastName; } set { LogPropertyChange("LastName", lastName, value); lastName = value; } }
[XmlElement] public string Street { get { return street; } set { LogPropertyChange("Street", street, value); street = value; } }
[XmlElement] public string HouseNumber { get { return houseNo; } set { LogPropertyChange("HouseNumber", houseNo, value); houseNo = value; } }
[XmlElement] public string Zipcode { get { return zipcode; } set { LogPropertyChange("Zipcode", zipcode, value); zipcode = value; } }
[XmlElement] public string City { get { return city; } set { LogPropertyChange("City", city, value); city = value; } }
[XmlIgnore] public Country.eCountry CountryCode { get { return countryCode; } set { LogPropertyChange("CountryCode", countryCode, value); countryCode = value; } }
[XmlElement("Country")] public uint CountryUInt32 { get { return (uint)CountryCode; } set { CountryCode = (Country.eCountry)value; } }
[XmlElement] public DateTime Birthday { get { return birthday; } set { LogPropertyChange("Birthday", birthday, value); birthday = value; } }
[XmlElement] public string EMail { get { return email; } set { LogPropertyChange("EMail", email, value); email = value; } }
[XmlElement] public string EMailName { get { return emailName; } set { LogPropertyChange("EMailName", emailName, value); emailName = value; } }
[XmlElement] public string PgpFingerprint { get { return pgpFingerprint; } set { LogPropertyChange("PgpFingerprint", pgpFingerprint, value); pgpFingerprint = value; } }
//membership organizational data
[XmlElement] public bool MvInvitationByPost { get { return mvInvitationByPost; } set { LogPropertyChange("MvInvitationByPost", mvInvitationByPost, value); mvInvitationByPost = value; } }
[XmlElement] public DateTime SpawnDate { get { return spawnDate; } set { LogPropertyChange("SpawnDate", spawnDate, value); spawnDate = value; } }
[XmlElement] public UInt64 PaymentAmount { get { return paymentAmount; } set { LogPropertyChange("PaymentAmount", paymentAmount, value); paymentAmount = value; } }
[XmlElement] public ePaymentClass PaymentClass { get { return paymentClass; } set { LogPropertyChange("PaymentClass", paymentClass, value); paymentClass = value; } }
[XmlElement] public DateTime ReducedFeeValid { get { return reducedFeeValid; } set { LogPropertyChange("ReducedFeeValid", reducedFeeValid, value); reducedFeeValid = value; } }
[XmlElement] public bool PaymentNotify { get { return paymentNotify; } set { LogPropertyChange("PaymentNotify", paymentNotify, value); paymentNotify = value; } }
[XmlElement] public DateTime MemberFormDate { get { return memberFormDate; } set { LogPropertyChange("MemberFormDate", memberFormDate, value); memberFormDate = value; } }
//internal management data
[XmlElement] public int DebtLevel { get { return debtLevel; } set { LogPropertyChange("DebtLevel", debtLevel, value); debtLevel = value; } }
[XmlElement] public DateTime GreetedDate { get; set; }
[XmlElement] public DateTime LastPaymentProcessed { get; set; } = DateTime.Now;
[XmlElement] public uint PaymentsTotal { get; set; }
[XmlElement] public DateTime LastBalanceDegrade { get; set; } = DateTime.Now;
[XmlElement] public DateTime LastCronjobBalanceMail { get; set; } = DateTime.Now;
[XmlElement] public DateTime LastCronjobReducedFeeMail { get; set; } = DateTime.Now;
[XmlElement] public DateTime MvEventDate { get; set; }
[XmlElement] public DateTime MvDateInvited { get; set; }
[XmlElement("MoneyTransferId")] public List<string> MoneyTransfersIds { get; set; } = new List<string>();
[XmlIgnore] public string AccountBalanceString { get { return $"{((float)accountBalance / 100)}"; } }
[XmlIgnore] public string PaymentAmountString { get { return $"{((float)paymentAmount / 100)}"; } }
[XmlIgnore] public string PaymentAmountCurrency { get { return Program.config.RegularPaymentCurrency; } }
[XmlIgnore]
public string PaymentDueMonth
{
get
{
int monthsPaid = (int)Math.Floor((float)AccountBalance / (float)PaymentAmount) + 1;
DateTime dueMonth = DateTime.Now.AddMonths(monthsPaid);
if (dueMonth < spawnDate)
return spawnDate.ToString("yyyy-MM");
else
return dueMonth.ToString("yyyy-MM");
}
}
public Member() { }
public Member(uint number)
{
this.number = number;
}
[XmlIgnore] public string NumberString { get { return $"{number:D3}"; } }
public bool Equals(Member other)
{
return number == other.Number;
}
public int CompareTo(Member other)
{
if (other == null) return 1;
else return number.CompareTo(other.Number);
}
public bool SaveToFile(string comment = null, bool finishLog = true)
{
if (finishLog) FinishLogEvent(comment);
string completePath = System.IO.Path.Combine(Program.config.DbDirectory, GetFileName());
Program.config.DbChangedSinceExport = true;
Program.config.LastDbLocalChange = DateTime.Now;
XmlData.SaveToFile(Program.ConfigFile, Program.config);
return XmlData.SaveToFile(completePath, this);
}
public string GetFileName() { return $"member-{Number:D3}.xml"; }
public bool CheckBankTransfer(BankTransfer t)
{
if (!evaluateAccountInCharge) return false;
try
{
if (!string.IsNullOrEmpty(BankTransferRegEx))
{
if(!t.TransferReason.ToLower().Contains(BankTransferRegEx.ToLower()))
return false;
//if (Regex.IsMatch(t.TransferReason, BankTransferRegEx)) return true;
}
}
catch (Exception ex)
{
Console.WriteLine($"Member {this.Number:D3} invalid RegEx: {ex.Message}");
}
return bankAccountInCharge.Equals(t.IBAN);
//on banktransfers, AccountInCharge is the receiver IBAN, we need the sender IBAN here
}
private void PaymentAdjustBalance(Int64 amount, string user = null)
{
if (CurrentLog == null) StartLogEvent("PaymentAdjustBalance", LogEvent.eEventType.MembershipPayment, user);
Int64 result = accountBalance + amount;
if (amount > 0) PaymentsTotal += (uint)amount;
CurrentLog.SubEvents.Add(new LogSubEvent()
{
Topic = $"Amount = {((float)amount / 100)}",
Type = LogEvent.eEventType.MembershipPayment,
Details = $"Old balance = {((float)accountBalance / 100)}\nAdd Amount = {((float)amount / 100)}\nNew balance = {((float)result / 100)}",
});
accountBalance = result;
}
public void TestMail()
{
FormMail testMail = FormMail.GenerateTestmail();
try
{
testMail.Send(this);
} catch(Exception ex)
{
Console.WriteLine($"Cannot send mail to <{this.EMail}>: {ex.Message}");
throw ex;
}
}
public void AccountStatusMail(string user = null)
{
StartLogEvent("Member Status Mail", LogEvent.eEventType.EMail, user);
FormMail accountStatusMail = FormMail.GenerateSingleMemberStatusReport();
try
{
CurrentLog.SubEvents.Add(accountStatusMail.Send(this));
SaveToFile();
} catch(Exception ex)
{
CurrentLog.SubEvents.Add(new LogSubEvent() { Type = LogEvent.eEventType.Error, Topic = "Email notification error", Details = ex.Message, });
SaveToFile();
throw ex;
}
}
public void ApplyMoneyTransfer(MoneyTransfer t, string user = null)
{
Console.WriteLine($"Member.ApplyMoneyTransfer(): {t.Amount} to member {Number}");
if (CurrentLog == null) StartLogEvent($"MoneyTransfer ({t.AmountString} {t.Currency})", LogEvent.eEventType.MembershipPayment, user);
LogEvent.eEventType evt = LogEvent.eEventType.Generic;
switch (t.TransferType)
{
case MoneyTransfer.eTransferType.Donation:
evt = LogEvent.eEventType.Donation; break;
case MoneyTransfer.eTransferType.MembershipFee:
evt = LogEvent.eEventType.MembershipFee; break;
case MoneyTransfer.eTransferType.MembershipPayment:
evt = LogEvent.eEventType.MembershipPayment; break;
default: break;
}
CurrentLog.SubEvents.Add(new LogSubEvent()
{
Topic = $"{t.GetType().Name} {t.Id}",
Type = evt,
Details = $"Amount = {t.AmountString} {t.Currency}",
});
MoneyTransfersIds.Add(t.Id);
t.MemberNumber = Number;
if (t.TransferType == MoneyTransfer.eTransferType.MembershipPayment)
{
PaymentAdjustBalance(t.Amount);
LastPaymentProcessed = DateTime.Now;
//find out if we need to send a mail to Schatzmeister (i.e. amount is odd in respect to the monthly fee)
Int64 months = t.Amount / (Int64)PaymentAmount;
bool odd = months * (Int64)PaymentAmount != t.Amount;
ConfigEMail smContact = Program.config.Schatzmeister;
FormMail fm = null;
if (this.Status == eStatus.Deleted || this.Status == eStatus.Disabled)
{
fm = FormMail.GenerateDisabledMemberGettingPayments2SM();
}
else if (odd)
{
fm = new FormMail()
{
To = $"{smContact}",
Subject = $"Schiefe Zahlung von Mitglied {Number} ({Nickname}, {t.AmountString} {t.Currency})",
Body = "s. Betreff.\n"
+ $"Type = {t.GetType()}\n"
+ $"TransferAmount = {t.AmountString}\n"
+ $"PaymentAmount = {PaymentAmountString } (monthly fee)\n"
+ $"accountBalance = {AccountBalanceString} (new)\n"
+ $"Next payment for this member is due at {PaymentDueMonth}\n"
+ "\n\n--\n(automatische mail)"
};
}
if (fm != null)
{
Console.WriteLine($"Member.ApplyMoneyTransfer(): sm={smContact}");
try
{
CurrentLog.SubEvents.Add(fm.Send());
}
catch (Exception ex)
{
CurrentLog.SubEvents.Add(new LogSubEvent() { Type = LogEvent.eEventType.Error, Topic = "Email notification error", Details = ex.Message, });
}
}
if (paymentNotify)
{
FormMail notify = FormMail.GenerateMemberPaymentNotify(odd).ReplaceReflect(t);
try
{
CurrentLog.SubEvents.Add(notify.Send(this));
} catch(Exception ex)
{
CurrentLog.SubEvents.Add(new LogSubEvent() { Type = LogEvent.eEventType.Error, Topic = "Email notification error", Details = ex.Message, });
}
}
}
try { SaveToFile(null, false); }
catch (Exception ex) { Console.WriteLine($"Error while saving member: {ex.Message}"); }
}
}
}