210228PX Added Mail for MV cancelling, MVs are displayed in List with Add/Edit/Cancel(Delete) function, Worked on MV toolbar buttons
Possible bugfix for first-start db-changed error message (untested yet)
This commit is contained in:
parent
d8b6dccae5
commit
4b88de2d15
|
@ -88,6 +88,7 @@ namespace dezentrale
|
|||
catch (FileNotFoundException)
|
||||
{
|
||||
config.DbDirectory = Path.Combine(DmDirectory, Configuration.DefaultDbDirectory);
|
||||
config.DbChangedSinceExport = false;
|
||||
XmlData.SaveToFile(ConfigFile, config); Console.WriteLine("Created new configuration file.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -126,7 +127,7 @@ namespace dezentrale
|
|||
Console.WriteLine("Creating new MV list file");
|
||||
try
|
||||
{
|
||||
mvList.SaveToFile();
|
||||
mvList.SaveToFile(false);
|
||||
} catch(Exception ex)
|
||||
{
|
||||
Console.WriteLine("Error while creating new MV list:");
|
||||
|
|
|
@ -563,5 +563,23 @@ namespace dezentrale.model
|
|||
+ "This is an auto-generated E-Mail.\n",
|
||||
};
|
||||
}
|
||||
|
||||
public static FormMail GenerateMvCancelNotification()
|
||||
{
|
||||
return new FormMail()
|
||||
{
|
||||
To = "{EMailName} <{EMail}>",
|
||||
Subject = "Mitgliederversammlung abgebrochen",
|
||||
Body = "Hallo {EMailName}!\n"
|
||||
+ "\n"
|
||||
+ "Folgende Einladung zur MV ist hinfällig:\n"
|
||||
+ "The following MV invitation is cancelled:\n"
|
||||
+ "Titel/Subject: \"{InviteHeadline}\"\n"
|
||||
+ "\n"
|
||||
+ "--\n"
|
||||
+ "Dies ist eine automatisch generierte E-Mail.\n"
|
||||
+ "This is an auto-generated E-Mail.\n",
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
41
model/Mv.cs
41
model/Mv.cs
|
@ -7,11 +7,11 @@ namespace dezentrale.model
|
|||
{
|
||||
public class MvInvitedMember
|
||||
{
|
||||
public uint MemberNumber { get; set; }
|
||||
public bool Invited { get; set; } = false;
|
||||
public DateTime InvitationDate { get; set; }
|
||||
public string AuthCode { get; set; } = "";
|
||||
public bool AttendedMv { get; set; } = false;
|
||||
[XmlAttribute] public uint MemberNumber { get; set; }
|
||||
[XmlAttribute] public bool Invited { get; set; } = false;
|
||||
[XmlAttribute] public DateTime InvitationDate { get; set; }
|
||||
[XmlAttribute] public string AuthCode { get; set; } = "";
|
||||
[XmlAttribute] public bool AttendedMv { get; set; } = false;
|
||||
|
||||
[XmlIgnore] public string NumberString { get { return $"{MemberNumber:D3}"; } }
|
||||
[XmlIgnore] public string InvitedString { get { return Invited ? "Yes" : "No"; } }
|
||||
|
@ -37,7 +37,7 @@ namespace dezentrale.model
|
|||
}
|
||||
}
|
||||
|
||||
public class Mv : XmlData, IMvInvitationData
|
||||
public class Mv : IMvInvitationData
|
||||
{
|
||||
public enum MvStatus
|
||||
{
|
||||
|
@ -48,15 +48,15 @@ namespace dezentrale.model
|
|||
Cancelled = 0xFF,
|
||||
}
|
||||
|
||||
public MvStatus Status { get; set; }
|
||||
public DateTime EventDate { get; set; }
|
||||
public string Place { get; set; }
|
||||
public string Agenda { get; set; }
|
||||
public string InviteHeadline { get; set; }
|
||||
public string InviteBody { get; set; }
|
||||
public List<MvInvitedMember> Invited { get; set; } = new List<MvInvitedMember>();
|
||||
[XmlAttribute] public MvStatus Status { get; set; }
|
||||
[XmlAttribute] public DateTime EventDate { get; set; }
|
||||
[XmlElement] public string Place { get; set; }
|
||||
[XmlElement] public string Agenda { get; set; }
|
||||
[XmlElement] public string InviteHeadline { get; set; }
|
||||
[XmlElement] public string InviteBody { get; set; }
|
||||
[XmlElement] public List<MvInvitedMember> Members { get; set; } = new List<MvInvitedMember>();
|
||||
|
||||
public List<Blob> Attachments { get; set; } = new List<Blob>();
|
||||
[XmlElement] public List<Blob> Attachments { get; set; } = new List<Blob>();
|
||||
|
||||
[XmlIgnore] public string AgendaNumberedString
|
||||
{
|
||||
|
@ -89,6 +89,7 @@ namespace dezentrale.model
|
|||
Place = "Räumlichkeiten des dezentrale e.V., Dreilindenstr. 19, 04177 Leipzig";
|
||||
Agenda = "Bestimmung des Versammlungsleiters sowie Protokollanten\n"
|
||||
+ "Feststellung der ordnungsgemäßen Einberufung\n"
|
||||
+ "Mitgliedsstatus-Änderung zwischen \"Fördermitglied\" und \"reguläres Mitglied\"\n"
|
||||
+ "Feststellung der Beschlussfähigkeit\n"
|
||||
+ "Genehmigung der Tagesordnung\n"
|
||||
+ "Genehmigung des Protokolls der letzten Mitgliederversammlung\n"
|
||||
|
@ -97,16 +98,22 @@ namespace dezentrale.model
|
|||
+ "Neuwahl des Vorstandes\n"
|
||||
+ "Verschiedenes";
|
||||
InviteHeadline = "Einladung zur Mitgliederversammlung des dezentrale e.V. am {EventDate}";
|
||||
InviteBody = "Hallo {EMailName},\n\nhiermit möchte ich Dich als Mitglied\n"
|
||||
InviteBody = "Hallo {EMailName},\n\nhiermit möchte ich Dich als Mitglied\n"
|
||||
+ "des dezentrale e.V. zur Mitgliederversammlung einladen.\n\n"
|
||||
+ "Ort: {Place}\n"
|
||||
+ "Datum und Uhrzeit: {EventDate}\n"
|
||||
+ "Dein Authentifizierungscode: {MvAuthenticationCode}\n\n"
|
||||
+ "Agenda:\n-------\n"
|
||||
+ "{AgendaNumberedString}\n\n"
|
||||
+ "Bitte denkt daran, dass für die Beschlussfähigkeit 51% der regulären Mitglieder vonnöten sind.\n\n"
|
||||
+ "Bitte denkt daran, dass für die Beschlussfähigkeit 51% der regulären Mitglieder vonnöten sind [1].\n"
|
||||
+ "\n\n"
|
||||
+ "Liebe Grüße,\n\n\n"
|
||||
+ $"{Program.config.LocalUser}";
|
||||
+ $"{Program.config.LocalUser}\n\n\n\n"
|
||||
+ "[1] Unsere Satzung unterscheidet zwischen regulären und Fördermitgliedern.\n"
|
||||
+ " Der Wechsel zu Fördermitglied ist jederzeit möglich, der Wechsel zu\n"
|
||||
+ " regulärem Mitglied am Anfang jeder MV. Fördermitglieder stimmen nicht\n"
|
||||
+ " mit ab und fallen daher aus dem Quorum (51%) raus. Der Vorstand kann\n"
|
||||
+ " Auskunft über Deinen Mitgliedsstatus geben.";
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -26,17 +26,20 @@ namespace dezentrale.model
|
|||
return new MvList();
|
||||
}
|
||||
}
|
||||
public static bool SaveToFile(MvList list)
|
||||
public static bool SaveToFile(MvList list, bool setDbChangedStatus = true)
|
||||
{
|
||||
string mvFile = System.IO.Path.Combine(Program.config.DbDirectory, FileName);
|
||||
Program.config.DbChangedSinceExport = true;
|
||||
Program.config.LastDbLocalChange = DateTime.Now;
|
||||
XmlData.SaveToFile(Program.ConfigFile, Program.config);
|
||||
if (setDbChangedStatus)
|
||||
{
|
||||
Program.config.DbChangedSinceExport = true;
|
||||
Program.config.LastDbLocalChange = DateTime.Now;
|
||||
XmlData.SaveToFile(Program.ConfigFile, Program.config);
|
||||
}
|
||||
return XmlData.SaveToFile(mvFile, list);
|
||||
}
|
||||
public bool SaveToFile()
|
||||
public bool SaveToFile(bool setDbChangedStatus = true)
|
||||
{
|
||||
return SaveToFile(this);
|
||||
return SaveToFile(this, setDbChangedStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,8 @@ namespace dezentrale.view
|
|||
e.NewValue = e.CurrentValue;
|
||||
};
|
||||
|
||||
MenuItem filterMenu = cm.MenuItems.Add("Filter by column...");
|
||||
//todo: Add column-filtering (implementation with search dialog)
|
||||
// MenuItem filterMenu = cm.MenuItems.Add("Filter by column...");
|
||||
foreach (ConfigLVDataHandler col in actualColumns)
|
||||
{
|
||||
if(col.FilterAvailable)
|
||||
|
@ -40,14 +41,16 @@ namespace dezentrale.view
|
|||
}
|
||||
if (col.Visible) this.Columns.Add(new ColumnHeader() { Width = col.Width, Text = col.Display, TextAlign = col.TextAlign, Tag = col, });
|
||||
}
|
||||
if(filterMenu.MenuItems.Count < 1)
|
||||
//todo: Add column-filtering (implementation with search dialog)
|
||||
/* if(filterMenu.MenuItems.Count < 1)
|
||||
{
|
||||
MenuItem unavailable = new MenuItem("unavailable");
|
||||
unavailable.Enabled = false;
|
||||
filterMenu.MenuItems.Add(unavailable);
|
||||
}
|
||||
*/
|
||||
}
|
||||
public CustomListView(List<ConfigLVColumn> actualColumns, EventHandler<ColumnsChangedArgs> ColumnsChanged = null, bool checkBoxes = true, bool columnConfiguration = true) : base()
|
||||
public CustomListView(List<ConfigLVColumn> actualColumns, EventHandler<ColumnsChangedArgs> ColumnsChanged = null, bool checkBoxes = false, bool columnConfiguration = true) : base()
|
||||
{
|
||||
this.ColumnsChanged = ColumnsChanged;
|
||||
this.checkBoxes = checkBoxes;
|
||||
|
|
|
@ -66,7 +66,7 @@ namespace dezentrale.view
|
|||
};
|
||||
} }
|
||||
|
||||
public LVMembers() : base(Program.config.MemberListColumns, LVMembers_ColumnsChanged) { }
|
||||
public LVMembers() : base(Program.config.MemberListColumns, LVMembers_ColumnsChanged, true) { }
|
||||
|
||||
private static void LVMembers_ColumnsChanged(object sender, ColumnsChangedArgs e)
|
||||
{
|
||||
|
|
|
@ -58,7 +58,7 @@ namespace dezentrale.view
|
|||
};
|
||||
}
|
||||
}
|
||||
public MT() : base(Program.config.MTListColumns, LVMoneyTransfers_ColumnsChanged, false) { }
|
||||
public MT() : base(Program.config.MTListColumns, LVMoneyTransfers_ColumnsChanged) { }
|
||||
}
|
||||
|
||||
private MT mT = new MT() { Dock = DockStyle.Fill, };
|
||||
|
|
|
@ -47,7 +47,7 @@ namespace dezentrale.view
|
|||
}
|
||||
}
|
||||
|
||||
public LvMvInvitations() : base(Program.config.MvInvitationsListColumns, LvMvInvitations_ColumnsChanged, false) { }
|
||||
public LvMvInvitations() : base(Program.config.MvInvitationsListColumns, LvMvInvitations_ColumnsChanged) { }
|
||||
|
||||
private static void LvMvInvitations_ColumnsChanged(object sender, ColumnsChangedArgs e)
|
||||
{
|
||||
|
|
|
@ -70,7 +70,8 @@ namespace dezentrale.view
|
|||
new MenuItem("MV")
|
||||
{ MenuItems = {
|
||||
new MenuItem("&New MV...", lstMv_New), //Todo: Do we even need this here?
|
||||
new MenuItem("&Cancel selected MV...", lstMv_CancelSelected), //Todo: Do we even need this here?
|
||||
new MenuItem("&Edit selected MV...", lstMv_Edit), //Todo: Do we even need this here?
|
||||
new MenuItem("&Cancel/Remove selected MV...", lstMv_CancelSelected), //Todo: Do we even need this here?
|
||||
} },
|
||||
new MenuItem("Payments")
|
||||
{ MenuItems = {
|
||||
|
@ -106,8 +107,9 @@ namespace dezentrale.view
|
|||
|
||||
TabPage tabMvList = new TabPage("MvList");
|
||||
tabMvList.Controls.Add(lstMv = new LvMv() { Dock = DockStyle.Fill, });
|
||||
lstMv.AddMenuItem("New MV...", lstMv_New);
|
||||
lstMv.AddMenuItem("Cancel selected MV...", lstMv_CancelSelected);
|
||||
lstMv.AddMenuItem("New MV...", lstMv_New);
|
||||
lstMv.AddMenuItem("Edit selected MV", lstMv_Edit);
|
||||
lstMv.AddMenuItem("Cancel/Remove selected MV...", lstMv_CancelSelected);
|
||||
TabPage tabMoneyTransfers = new TabPage("MoneyTransfers");
|
||||
BuildMoneyTransfers(tabMoneyTransfers);
|
||||
|
||||
|
@ -119,7 +121,10 @@ namespace dezentrale.view
|
|||
this.ResumeLayout(false);
|
||||
|
||||
//Fill list with data from members
|
||||
lstMembers.LoadFromList(Program.members.Entries);
|
||||
lstMembers.LoadFromList(Program.members.Entries);
|
||||
|
||||
//Fill mv list with data from mv
|
||||
lstMv.LoadFromList(Program.mvList.Entries);
|
||||
|
||||
//Check for needed import
|
||||
DateTime now = DateTime.Now;
|
||||
|
@ -259,8 +264,23 @@ namespace dezentrale.view
|
|||
Program.mvList.Entries.Add(newMv.mv);
|
||||
Program.mvList.SaveToFile();
|
||||
newMv.ShowDialog();
|
||||
Program.mvList.SaveToFile();
|
||||
lstMv.AddEntry(newMv.mv);
|
||||
}
|
||||
}
|
||||
|
||||
Program.mvList.SaveToFile();
|
||||
private void lstMv_Edit(object sender, EventArgs e)
|
||||
{
|
||||
Mv mv = lstMv.GetFirstSelectedItem();
|
||||
if (mv == null)
|
||||
{
|
||||
MessageBox.Show("No MV entry selected");
|
||||
} else
|
||||
{
|
||||
frmMv editMv = new frmMv(mv);
|
||||
editMv.ShowDialog();
|
||||
Program.mvList.SaveToFile();
|
||||
lstMv.UpdateEntry(editMv.mv);
|
||||
}
|
||||
//if(newMv.DialogResult == DialogResult.OK)
|
||||
//lstMv.AddEntry(newMv.GetMv());
|
||||
|
@ -298,11 +318,61 @@ namespace dezentrale.view
|
|||
*/
|
||||
}
|
||||
private void lstMv_CancelSelected(object sender, EventArgs e)
|
||||
{
|
||||
//Cancels selected MV
|
||||
//- check if there's a valid selection
|
||||
//- ask if the program shall send cancellation mails to the invided members
|
||||
//- set mv status to Cancelled
|
||||
{
|
||||
Mv mv = lstMv.GetFirstSelectedItem();
|
||||
if (mv == null)
|
||||
{
|
||||
MessageBox.Show("No MV entry selected");
|
||||
}
|
||||
else if(mv.Status == Mv.MvStatus.Cancelled)
|
||||
{
|
||||
MessageBox.Show("MV is already cancelled");
|
||||
} else
|
||||
{
|
||||
DialogResult res;
|
||||
if (mv.Status == Mv.MvStatus.InPreparation)
|
||||
{
|
||||
res = MessageBox.Show("MV is in preparation state, do you also want to delete it completely?", "Also delete MV?", MessageBoxButtons.YesNoCancel);
|
||||
} else
|
||||
{
|
||||
res = MessageBox.Show("Do you really want to cancel this MV?", "Cancel MV?", MessageBoxButtons.OKCancel);
|
||||
}
|
||||
switch(res)
|
||||
{
|
||||
case DialogResult.Yes: //Delete MV
|
||||
//mv.Status = Mv.MvStatus.InPreparation
|
||||
lstMv.RemoveEntry(mv);
|
||||
Program.mvList.Entries.Remove(mv);
|
||||
Program.mvList.SaveToFile();
|
||||
break;
|
||||
case DialogResult.No: //Set status to Cancelled
|
||||
case DialogResult.OK:
|
||||
//Send cancellation mails?
|
||||
if (mv.Status != Mv.MvStatus.InPreparation)
|
||||
{
|
||||
res = MessageBox.Show("Send cancel mails to previously invited members?", "Send cancel mails?", MessageBoxButtons.YesNo);
|
||||
if (res == DialogResult.Yes)
|
||||
{
|
||||
FormMail fm = FormMail.GenerateMvCancelNotification().ReplaceReflect(mv);
|
||||
foreach (MvInvitedMember mvi in mv.Members)
|
||||
{
|
||||
if (!mvi.Invited) continue;
|
||||
mvi.Member.StartLogEvent("MV cancelled", LogEvent.eEventType.EMail);
|
||||
mvi.Member.CurrentLog.SubEvents.Add(fm.Send(mvi.Member));
|
||||
mvi.Member.SaveToFile();
|
||||
}
|
||||
}
|
||||
}
|
||||
mv.Status = Mv.MvStatus.Cancelled;
|
||||
lstMv.UpdateEntry(mv);
|
||||
Program.mvList.SaveToFile();
|
||||
break;
|
||||
|
||||
default:
|
||||
case DialogResult.Cancel: //Do nothing
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace dezentrale.view
|
|||
public Mv mv { get; set; }
|
||||
private TabControl tabControl;
|
||||
|
||||
//private TabPage tabInvitationSettings;
|
||||
private TabPage tabInvitationSettings;
|
||||
//DateTime
|
||||
private DateTimePicker mvDate, mvTime;
|
||||
//Place
|
||||
|
@ -31,11 +31,11 @@ namespace dezentrale.view
|
|||
//Subject
|
||||
private TextBox tbInviteHeadline;
|
||||
private TextBox tbInviteBody;
|
||||
//private TabPage tabMemberInvitation;
|
||||
private TabPage tabMemberInvitation;
|
||||
//List with all members and invited yes|no
|
||||
private LvMvInvitations lvMvInvitations;
|
||||
//Option to invite selected|all members
|
||||
//private TabPage tabRunningMv;
|
||||
private TabPage tabRunningMv;
|
||||
//List with invited members with authenticated status
|
||||
//Textbox to paste auth codes from the users
|
||||
//textbox to hold an external MV protocol URL and/or
|
||||
|
@ -44,13 +44,43 @@ namespace dezentrale.view
|
|||
|
||||
private TextBox tbPreviewHeadline;
|
||||
private TextBox tbPreviewBody;
|
||||
private void UpdateButtons()
|
||||
|
||||
//! \brief UpdateGui() will refresh the GUI components for this
|
||||
//! form according to the current MV state and visible
|
||||
//! GUI tab. Having this centralized in one function is
|
||||
//! useful for avoiding duplicate and hard to maintain code.<br/>
|
||||
//! Subtasks:
|
||||
//! - Enable/Disable GUI fields (like invitation text)
|
||||
//! - Add the correct set of buttons to the toolbar
|
||||
private void UpdateGui()
|
||||
{
|
||||
ClearButtons();
|
||||
switch(mv.Status)
|
||||
TabPage selectedTab = tabControl.SelectedTab;
|
||||
if(selectedTab == tabInvitationSettings)
|
||||
{
|
||||
Button btn =
|
||||
AddButton("Your advert here", null);
|
||||
btn.Enabled = (this.mv.Status == Mv.MvStatus.InPreparation || this.mv.Status == Mv.MvStatus.InvitationSent);
|
||||
} else if(selectedTab == tabMemberInvitation)
|
||||
{
|
||||
AddButton("Invite selected", null);
|
||||
} else if(selectedTab == tabRunningMv)
|
||||
{
|
||||
|
||||
}
|
||||
AddButton("Close", btnClose_Click);
|
||||
|
||||
|
||||
mvDate.Enabled = mv.Status == Mv.MvStatus.InPreparation;
|
||||
mvTime.Enabled = mv.Status == Mv.MvStatus.InPreparation;
|
||||
tbPlace.Enabled = mv.Status == Mv.MvStatus.InPreparation;
|
||||
tbMvAgenda.Enabled = mv.Status == Mv.MvStatus.InPreparation;
|
||||
tbInviteHeadline.Enabled = mv.Status == Mv.MvStatus.InPreparation;
|
||||
tbInviteBody.Enabled = mv.Status == Mv.MvStatus.InPreparation;
|
||||
switch (mv.Status)
|
||||
{
|
||||
case Mv.MvStatus.InPreparation:
|
||||
AddButton("Refresh preview", null);
|
||||
|
||||
break;
|
||||
case Mv.MvStatus.InvitationSent:
|
||||
case Mv.MvStatus.Started:
|
||||
|
@ -58,7 +88,6 @@ namespace dezentrale.view
|
|||
case Mv.MvStatus.Cancelled:
|
||||
break;
|
||||
}
|
||||
AddButton("Close", btnClose_Click);
|
||||
}
|
||||
|
||||
private void RefreshPreview()
|
||||
|
@ -278,13 +307,16 @@ namespace dezentrale.view
|
|||
Size = new System.Drawing.Size(this.Width - 28, this.Height - 65),
|
||||
TabPages =
|
||||
{
|
||||
BuildPageInvitationSettings(),
|
||||
BuildPageMemberInvitation(),
|
||||
BuildPageRunningMv(),
|
||||
( tabInvitationSettings = BuildPageInvitationSettings() ),
|
||||
( tabMemberInvitation = BuildPageMemberInvitation() ),
|
||||
( tabRunningMv = BuildPageRunningMv() ),
|
||||
},
|
||||
Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom,
|
||||
});
|
||||
|
||||
//In order to update the tool buttons etc, we need to call UpdateGui() here
|
||||
tabControl.SelectedIndexChanged += (sender, e) => { UpdateGui(); };
|
||||
|
||||
//Setup all controls values
|
||||
mvDate.Value = this.mv.EventDate;
|
||||
mvTime.Value = this.mv.EventDate;
|
||||
|
@ -293,7 +325,7 @@ namespace dezentrale.view
|
|||
tbInviteHeadline.Text = this.mv.InviteHeadline;
|
||||
tbInviteBody.Text = this.mv.InviteBody;
|
||||
|
||||
UpdateButtons();
|
||||
UpdateGui();
|
||||
programmaticChange = false;
|
||||
RefreshPreview();
|
||||
|
||||
|
@ -310,10 +342,10 @@ namespace dezentrale.view
|
|||
if (m.Status == Member.eStatus.Active)
|
||||
{
|
||||
//find invitation entry
|
||||
if(this.mv.Invited.Find((x) => x.MemberNumber == m.Number) == null)
|
||||
if(this.mv.Members.Find((x) => x.MemberNumber == m.Number) == null)
|
||||
//if there is none, create one
|
||||
{
|
||||
this.mv.Invited.Add(new MvInvitedMember()
|
||||
this.mv.Members.Add(new MvInvitedMember()
|
||||
{
|
||||
MemberNumber = m.Number,
|
||||
});
|
||||
|
@ -322,7 +354,7 @@ namespace dezentrale.view
|
|||
}
|
||||
}
|
||||
|
||||
foreach(MvInvitedMember mvi in this.mv.Invited)
|
||||
foreach(MvInvitedMember mvi in this.mv.Members)
|
||||
lvMvInvitations.AddEntry(mvi);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue