tbf/WorkflowConfigurator/UserControls/EditProcessCtrl.cs

738 lines
28 KiB
C#
Raw Normal View History

2022-01-06 14:41:50 +00:00
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using log4net;
using NHibernate;
using Common;
using Common.Forms;
using SharedDatabase;
using SharedDatabase.Entities;
using SharedDatabase.Forms;
using WorkflowConfigurator.Resources;
namespace WorkflowConfigurator.UserControls
{
public partial class EditProcessCtrl : UserControl, IMyUI
{
static readonly ILog log = LogManager.GetLogger(typeof(EditProcessCtrl));
/// <summary>
/// Group membership to unlock this screen
/// </summary>
public static GID[] UnlockAccessLevel = new GID[] { GID.TraceabilityManagement };
public static GID[] ChangeReleaseState = new GID[] { GID.TraceabilityManagement };
public OverviewOfProcessesCtrl OverviewCtrl; /// Parent control reference
///
/// The following section contains 'state'
///
public Process Process; /// Process.ReleaseStatus
bool suppressNotifications;
bool limitedUnlock;
bool completeUnlock;
bool unsavedChanges;
IList<Process> allProcesses; /// Auxiliary, list of all processes used in checks, updated in UpdateProcess()
public IEnumerable<Part> SelectedParts { get { return partsCtrl.SelectedParts; } }
public IList<Part> PartsToBeDeleted;
public IList<Workstep> WorkstepsToBeDeleted;
public EditProcessCtrl()
{
InitializeComponent();
unsavedChanges = false;
suppressNotifications = false;
saveButton.Enabled = false;
PartsToBeDeleted = new List<Part>();
WorkstepsToBeDeleted = new List<Workstep>();
partsCtrl.EditProcessCtrl = this;
workstepsCtrl.EditProcessCtrl = this;
Localize();
UpdateLockState(false, false); /// Locked
}
void Localize()
{
/// Labels
string colon = ":";
processNameLabel.Text = Strings.Workflow + colon;
processDescriptionLabel.Text = Strings.Description + colon;
createdByLabel.Text = Strings.Created_by + colon;
approvedByLabel.Text = Strings.Approved_by + colon;
inPreparationButton.Text = Strings.In_preparation;
testingButton.Text = Strings.Testing;
productionButton.Text = Strings.Production;
deactivatedButton.Text = Strings.Deactivated;
showRecordsButton.Text = Strings.Show_records;
/// Buttons
saveButton.Text = Strings.Save;
unlockButton.Text = Strings.Unlock;
notesButton.Text = Strings.Notes;
}
/// <summary>
/// Called from Overview...
/// </summary>
public void UpdateProcess(Process process, Reason reason, IList<Process> allProcesses)
{
if (allProcesses != null) this.allProcesses = allProcesses;
/// Update content
this.Process = process;
partsCtrl.UpdateProcess(process, reason, allProcesses);
workstepsCtrl.UpdateProcess(process, reason, allProcesses);
Data2UI();
/// Update UI state
StateChange(reason);
/// Reset 'unsavedChanges', a new or copied process always contains unsaved changes
unsavedChanges = (reason == Reason.NewProcess) || (reason == Reason.CopyProcess) || ((process != null) && (process.Id == 0));
saveButton.Enabled = unsavedChanges;
PartsToBeDeleted.Clear();
WorkstepsToBeDeleted.Clear();
}
/// <summary>
/// Called from Overview...
/// Finish editing the current process.
/// </summary>
/// <returns>true : proces was modified, false : process was not modified</returns>
public EditFinishedInfo FinishEditing()
{
string message;
if (!unsavedChanges)
{
/// No changes done => just return
return EditFinishedInfo.NoChanges;
}
else if (!VerifyUIData(out message))
{
/// Data in UI are not correct => Yes/No message box
if (new Forms.EditOrAbandonMsgBox(string.Format(Strings.Invalid_data_0, string.Empty), message).ShowDialog() == DialogResult.Retry)
{
/// Yes = continue editing, cancel callers action
return EditFinishedInfo.Cancel;
}
else
{
/// No = abandon changes, continue callers action
return EditFinishedInfo.NoChanges;
}
}
else
{
/// Data in UI are correct => ask whether to save them
if (MessageBox.Show(Strings.Unsaved_changes_Do_you_want_to_save_them_nowQM,
Strings.Warning,
MessageBoxButtons.YesNo,
MessageBoxIcon.Question) == DialogResult.Yes)
{
/// Yes = save changes
return SaveChanges() ? EditFinishedInfo.DataChanged : EditFinishedInfo.NoChanges;
}
else
{
/// No = abandon changes (just return)
return EditFinishedInfo.NoChanges;
}
}
}
/// <summary>
/// Retrieve data from UI and save them to database
/// </summary>
/// <returns>true when successful</returns>
bool SaveChanges()
{
/// Retrieve data from UI
UI2Data();
partsCtrl.UI2Data();
workstepsCtrl.UI2Data();
if (SaveDataToDB(SharedDatabase.TracingDB.Session))
{
/// Saving data was successful
SharedDatabase.TracingDB.Session.Refresh(Process);
return true;
}
else
{
/// Saving data failed
MessageBox.Show(Strings.Saving_changes_failed);
return false;
}
}
public void Data2UI()
{
suppressNotifications = true;
if (Process != null)
{
processNameTextBox.Text = Process.Name;
processDescriptionTextBox.Text = Process.Description;
}
else
{
processNameTextBox.Text = string.Empty;
processDescriptionTextBox.Text = string.Empty;
}
CreatedByOn2UI(Process);
UpdateProcessStateButtonsAndPicture(Process);
partsCtrl.Data2UI();
workstepsCtrl.Data2UI();
suppressNotifications = false;
}
void CreatedByOn2UI(Process process)
{
createdByTextBox.Text = (process == null) ? string.Empty : process.CreatedBy;
createdOnTextBox.Text = (process == null) ? string.Empty : process.TimeStamp.ToString("dd.MM.yyyy HH:mm");
approvedByTextBox.Text = (process == null) ? string.Empty : process.ApprovedBy;
approvedOnTextBox.Text = (process == null) ? string.Empty : process.TimeStamp2.ToString("dd.MM.yyyy HH:mm");
}
/// <summary>
/// Returns true when (modified) UI data are OK
/// </summary>
/// <returns>true when UI data are valid</returns>
public bool VerifyUIData(out string message)
{
bool uiDataValid = true;
message = string.Empty;
if (Process == null) return uiDataValid;
if (allProcesses != null)
{
foreach (var p in allProcesses)
{
if ((processNameTextBox.Text == p.Name) && (Process.Id != p.Id))
{
uiDataValid = false;
message += string.Format(Strings.Workflow_with_duplicate_name_exists) + Environment.NewLine;
break;
}
}
}
string msg;
if (!partsCtrl.VerifyUIData(out msg)) { uiDataValid = false; message += msg; } /// Parts
if (!workstepsCtrl.VerifyUIData(out msg)) { uiDataValid = false; message += msg; } /// Worksteps
return uiDataValid;
}
public void UI2Data()
{
if (this.Process != null)
{
Process.Name = processNameTextBox.Text;
Process.Description = processDescriptionTextBox.Text;
//createdByTextBox.Text = this.Process.CreatedBy;
//createdOnTextBox.Text = this.Process.TimeStamp.ToShortDateString();
//approvedByTextBox.Text = this.Process.ApprovedBy;
//approvedOnTextBox.Text = this.Process.TimeStamp2.ToShortDateString();
//stateLabel.Text = this.Process.ReleaseStatus.ToString();
}
partsCtrl.UI2Data();
workstepsCtrl.UI2Data();
}
public void Settings2UI()
{
var ls = Program.LocalSettings;
splitContainerV2.SplitterDistance = Math.Max(splitContainerV2.Panel1MinSize,
Math.Min(splitContainerV2.Width - splitContainerV2.Panel2MinSize,
(ls.SplDst2 > 0) ? Convert.ToInt32(ls.SplDst2 * Width) : 50));
partsCtrl.Settings2UI();
workstepsCtrl.Settings2UI();
}
public void UI2Settings()
{
Program.LocalSettings.SplDst2 = (float)splitContainerV2.SplitterDistance / (float)Width;
partsCtrl.UI2Settings();
workstepsCtrl.UI2Settings();
}
/// <summary>
/// Set 'unsavedChanges' and enable 'Save' button
/// </summary>
public void SetUnsavedFlag()
{
unsavedChanges = true;
saveButton.Enabled = true;
}
/// <summary>
/// Reset 'unsavedChanges' and disable 'Save' button
/// </summary>
public void ResetUnsavedFlag()
{
unsavedChanges = false;
partsCtrl.ResetUnsavedFlag();
workstepsCtrl.ResetUnsavedFlag();
saveButton.Enabled = false;
}
/// <summary>
/// Enable/disable/hide UI elements based on lock/unlock/complete unlock state
/// </summary>
/// <param name="limitedUnlock">true = unlock for released processes</param>
/// <param name="completeUnlock">true = unlock for processes in preparation (complete unlock)</param>
public void UpdateLockState(bool limitedUnlock, bool completeUnlock)
{
this.limitedUnlock = limitedUnlock;
this.completeUnlock = completeUnlock;
partsCtrl.UpdateLockState(limitedUnlock, completeUnlock); /// Parts
workstepsCtrl.UpdateLockState(limitedUnlock, completeUnlock); /// Worksteps
unlockButton.Enabled = !(completeUnlock || limitedUnlock);
processNameTextBox.Enabled = (limitedUnlock || completeUnlock);
processDescriptionTextBox.Enabled = (limitedUnlock || completeUnlock);
createdByTextBox.Enabled = false;
createdOnTextBox.Enabled = false;
approvedByTextBox.Enabled = false;
approvedOnTextBox.Enabled = false;
}
void UpdateProcessStateButtonsAndPicture(Process process)
{
inPreparationButton.Text = Strings.In_preparation;
testingButton.Text = Strings.Testing;
productionButton.Text = Strings.Production;
deactivatedButton.Text = Strings.Deactivated;
showRecordsButton.Visible = false;
if (process == null)
{
inPreparationButton.Enabled = false;
testingButton.Enabled = false;
productionButton.Enabled = false;
deactivatedButton.Enabled = false;
statePictureBox.Image = Image.FromFile(string.Format("{0}\\Pictures\\{1}.bmp", Program.ExeDirectory, "0"), true);
return;
}
switch (process.ReleaseStatus)
{
default:
case ReleaseStatus.In_preparation:
inPreparationButton.Enabled = false;
testingButton.Enabled = (limitedUnlock || completeUnlock);
testingButton.Text = Strings.Test;
productionButton.Enabled = false;
deactivatedButton.Enabled = false;
statePictureBox.Image = Image.FromFile(string.Format("{0}\\Pictures\\{1}.bmp", Program.ExeDirectory, "1"), true);
break;
case ReleaseStatus.ToBeApproved:
inPreparationButton.Enabled = (limitedUnlock || completeUnlock);
inPreparationButton.Text = Strings.Modify;
testingButton.Enabled = false;
productionButton.Enabled = (limitedUnlock || completeUnlock);
productionButton.Text = Strings.Release_to_production;
deactivatedButton.Enabled = false;
statePictureBox.Image = Image.FromFile(string.Format("{0}\\Pictures\\{1}.bmp", Program.ExeDirectory, "2"), true);
showRecordsButton.Visible = true;
break;
case ReleaseStatus.ReleasedActive:
case ReleaseStatus.Released:
inPreparationButton.Enabled = false;
testingButton.Enabled = false;
productionButton.Enabled = false;
deactivatedButton.Enabled = (limitedUnlock || completeUnlock);
deactivatedButton.Text = Strings.Deactivate;
statePictureBox.Image = Image.FromFile(string.Format("{0}\\Pictures\\{1}.bmp", Program.ExeDirectory, "3"), true);
showRecordsButton.Visible = true;
break;
case ReleaseStatus.Deactivated:
inPreparationButton.Enabled = false;
testingButton.Enabled = false;
productionButton.Enabled = (limitedUnlock || completeUnlock);
deactivatedButton.Enabled = true;
deactivatedButton.Text = Strings.Activate_again;
statePictureBox.Image = Image.FromFile(string.Format("{0}\\Pictures\\{1}.bmp", Program.ExeDirectory, "4"), true);
showRecordsButton.Visible = true;
break;
}
}
/// <summary>
/// Adds a part to the currently selected workstep.
/// Display amesssage box when addin a part fails.
/// </summary>
/// <param name="part">Part to be added</param>
/// <returns>true when a part was successfully added</returns>
public bool AddPartsToCurrentWorkstep(Part part)
{
return workstepsCtrl.AddPartsToCurrentWorkstep(part);
}
public Part RemovePartFromCurrentWorkstep()
{
return workstepsCtrl.RemovePartFromCurrentWorkstep();
}
private void notesButton_Click(object sender, EventArgs e)
{
//StringBuilder sb = new StringBuilder();
//sb.AppendLine(string.Format("AnyChange = {0}", workstepsTabControl.TabOrderChanged));
//foreach (TabPage tp in workstepsTabControl.TabPages)
//{
// sb.AppendLine(tp.Text);
//}
//MessageBox.Show(sb.ToString());
//if ((Process == null) || string.IsNullOrEmpty(Process.ReleaseNotes))
//{
// MessageBox.Show(Strings.No_release_notes);
//}
//else
//{
// MessageBox.Show(Process.ReleaseNotes);
//}
}
/// <summary>
/// Save a process to the DB
/// </summary>
/// <param name="session">DB session</param>
/// <returns>true when successful</returns>
public bool SaveDataToDB(ISession session)
{
if (Process == null) return true;
string message;
if (!VerifyUIData(out message))
{
MessageBox.Show(string.Format(Strings.Invalid_data_0, message),
Strings.Warning,
MessageBoxButtons.OK,
MessageBoxIcon.Exclamation);
return false;
}
UI2Data();
partsCtrl.UI2Data();
workstepsCtrl.UI2Data();
ITransaction transaction = session.BeginTransaction();
try
{
session.SaveOrUpdate(Process);
foreach (var part in PartsToBeDeleted) session.Delete(part);
foreach (var ws in WorkstepsToBeDeleted)
{
ws.Workflow = null;
ws.ReferencePart = null;
ws.Parts.Clear();
session.Delete(ws);
}
transaction.Commit();
session.Flush();
PartsToBeDeleted.Clear();
WorkstepsToBeDeleted.Clear();
ResetUnsavedFlag();
return true;
}
catch (Exception exc)
{
transaction.Rollback();
log.ErrorFormat("Saving process '{0}' to DB failed: {1}", Process.Name, exc.Message);
MessageBox.Show(string.Format("{0}{1}{2}", Strings.Saving_changes_failed, Environment.NewLine, exc.Message),
Strings.Error,
MessageBoxButtons.OK,
MessageBoxIcon.Exclamation);
return false;
}
}
private void processNameTextBox_TextChanged(object sender, EventArgs e)
{
if (suppressNotifications) return;
unsavedChanges = true;
saveButton.Enabled = true;
}
private void processDescriptionTextBox_TextChanged(object sender, EventArgs e)
{
if (suppressNotifications) return;
unsavedChanges = true;
saveButton.Enabled = true;
}
private void saveButton_Click(object sender, EventArgs e)
{
if (SaveDataToDB(SharedDatabase.TracingDB.Session))
{
ResetUnsavedFlag();
if (OverviewCtrl != null) OverviewCtrl.RedrawLeftPane(RedrawType.FromScratch, Process);
}
}
private void unlockButton_Click(object sender, EventArgs e) { StateChange(Reason.UnlockBtn); }
private void inPreparationButton_Click(object sender, EventArgs e) { StateChange(Reason.PrepareBtn); }
private void testingButton_Click(object sender, EventArgs e) { StateChange(Reason.TestBtn); }
private void productionButton_Click(object sender, EventArgs e) { StateChange(Reason.ReleaseBtn); }
private void deactivatedButton_Click(object sender, EventArgs e) { StateChange(Reason.DeactivateBtn); }
/// <summary>
/// Change 'ReleaseStatus' of the procedure and update UI / enable/disable UI controls accordingly
/// when changing the state of the procedure, unlocking UI or opening/creating/copying a procedure.
/// </summary>
/// <param name="reason">Reason of the state change</param>
void StateChange(Reason reason)
{
if (Process == null) return; /// No process
bool unlocked = (limitedUnlock || completeUnlock);
if (reason == Reason.OpenProcess)
{
/// Existing process was opened --> locked
UpdateLockState(false, false);
UpdateProcessStateButtonsAndPicture(Process);
return;
}
else if (reason == Reason.NewProcess)
{
/// A new process was created --> completely unlocked, optionally import a parts list
UpdateLockState(true, true);
UpdateProcessStateButtonsAndPicture(Process);
SetUnsavedFlag();
if (MessageBox.Show(Strings.Do_you_want_to_import_parts_from_a_list, Strings.Import, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
{
ImportParts();
}
return;
}
else if (reason == Reason.CopyProcess)
{
/// A new process was created as a copy of an existing process --> completely unlocked
UpdateLockState(true, true);
UpdateProcessStateButtonsAndPicture(Process);
SetUnsavedFlag();
return;
}
else if (reason == Reason.UnlockBtn)
{
/// Unlock button pressed
if (unlocked) return; /// Already unlocked
if (CurrentUser.IsMemberOf(UnlockAccessLevel) ||
new SharedDatabase.Forms.LoginDlg(UnlockAccessLevel, null).ShowDialog() == DialogResult.OK)
2022-01-06 14:41:50 +00:00
{
MainWnd.PrintTitle(false);
UpdateLockState(true, (Process.ReleaseStatus == ReleaseStatus.In_preparation));
UpdateProcessStateButtonsAndPicture(Process);
return;
}
else
{
return; /// Access not granted --> locked
}
}
///
/// Change the state of the current process
///
FinishEditing();
///
ReleaseStatus oriState = Process.ReleaseStatus;
///
switch (reason)
{
case Reason.PrepareBtn:
if (!GetAccessToStateChange(false)) return; /// Login if unsufficient access right
UpdateLockState(unlocked, unlocked);
Process.ReleaseStatus = ReleaseStatus.In_preparation;
break;
case Reason.TestBtn:
if (!GetAccessToStateChange(false)) return;
UpdateLockState(unlocked, false);
Process.ReleaseStatus = ReleaseStatus.ToBeApproved;
break;
case Reason.ReleaseBtn:
if (!GetAccessToStateChange(true)) return; /// Obligatory login when releasing a process to production
UpdateLockState(false, false);
Process.ReleaseStatus = ReleaseStatus.Released;
Process.ApprovedBy = CurrentUser.UserName();
2022-01-06 14:41:50 +00:00
Process.TimeStamp2 = DateTime.Now;
break;
case Reason.DeactivateBtn:
if (!GetAccessToStateChange(false)) return;
UpdateLockState(false, false);
if (Process.ReleaseStatus == ReleaseStatus.ReleasedActive ||
Process.ReleaseStatus == ReleaseStatus.Released)
{
Process.ReleaseStatus = ReleaseStatus.Deactivated;
}
else
{
Process.ReleaseStatus = ReleaseStatus.Released;
}
break;
default:
return; /// Otherwise do nothing
}
///
/// Save the process after changing the state, revert state to original if saving fails
///
if (SaveDataToDB(SharedDatabase.TracingDB.Session))
{
/// Saving successful
UpdateProcessStateButtonsAndPicture(Process);
CreatedByOn2UI(Process);
ResetUnsavedFlag();
if (OverviewCtrl != null) OverviewCtrl.RedrawLeftPane(RedrawType.FromScratch, Process);
}
else
{
/// Saving failed
Process.ReleaseStatus = oriState;
}
}
public void ImportParts()
{
partsCtrl.ImportParts();
}
public void ImportPartsUsingTemplate()
{
IList<string[]> patterns = new List<string[]>();
foreach (var pt in Process.Parts)
{
if (pt.Name == "0")
{
patterns.Add(pt.Description.Split(new char[]{'|'}));
}
else
{
patterns.Add(new string[] { string.Empty });
}
}
OpenFileDialog dlg = new OpenFileDialog();
if (dlg.ShowDialog() == DialogResult.OK)
{
try
{
using (TextReader reader = new StreamReader(dlg.FileName))
{
bool lineOK;
bool lineAssigned;
do
{
string line = reader.ReadLine();
lineOK = false;
lineAssigned = false;
if (line != null)
{
string[] items = line.Split(new char[] { ';' });
if (items.Length >= 2)
{
lineOK = true;
for (int i = 0; i < Process.Parts.Count; i++)
{
if (Process.Parts[i].Name == "0")
{
foreach (var pattern in patterns[i])
{
if (items[1].Contains(pattern))
{
Process.Parts[i].Name = items[0];
Process.Parts[i].Description = items[1];
lineAssigned = true;
break;
}
}
}
if (lineAssigned) break;
}
}
}
}
while (lineOK);
}
Data2UI();
}
catch (Exception)
{
MessageBox.Show(Strings.Failed_to_import_a_list_of_parts,
Strings.Warning,
MessageBoxButtons.OK,
MessageBoxIcon.Exclamation);
}
}
}
bool GetAccessToStateChange(bool forceLogin)
{
GID[] rqrdMmbrshp = ChangeReleaseState;
if (forceLogin)
{
return new LoginDlg(rqrdMmbrshp, null).ShowDialog() == DialogResult.OK;
2022-01-06 14:41:50 +00:00
}
else
{
return CurrentUser.IsMemberOf(rqrdMmbrshp) ||
new LoginDlg(rqrdMmbrshp, null).ShowDialog() == DialogResult.OK;
2022-01-06 14:41:50 +00:00
}
}
private void showRecordsButton_Click(object sender, EventArgs e)
{
if (SharedDatabase.TracingDB.Session != null)
{
IList<ReferenceRecord> refRecords = SharedDatabase.TracingDB.Session.QueryOver<ReferenceRecord>().Where(x => (x.Workflow == Process.Name)).List();
2022-01-06 14:41:50 +00:00
new Forms.ShowRecordsForm(refRecords).ShowDialog();
}
}
}
}