328 lines
17 KiB
C#
328 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 (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}"); }
|
|
}
|
|
}
|
|
}
|