git_bsmd/ENI-2/ENI2/ENI2/DetailRootControl.xaml.cs
Daniel Schick 695c07a5f7 Version 3.9.2
Diverse Korrekturen für Go-Live, gelöschte-ID Bug gefunden (war den Core als stat. Dictionary Key Objekt zu behalten und später nicht mehr neu zu laden, so überschrieb der "alte" Core die neue Version mit der Visit-Id.
2017-12-05 17:48:57 +00:00

616 lines
28 KiB
C#

// Copyright (c) 2017 schick Informatik
// Description: Control für die Auftragsbearbeitung (Rahmen)
//
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Data;
using bsmd.database;
using ENI2.DetailViewControls;
using ENI2.Util;
using ENI2.EditControls;
namespace ENI2
{
/// <summary>
/// Interaction logic for DetailRootControl.xaml
/// </summary>
public partial class DetailRootControl : UserControl
{
#region Fields
private MessageCore _core;
private List<MessageGroup> _listBoxList = new List<MessageGroup>();
private List<Message> _messages;
private Dictionary<string, DetailBaseControl> controlCache = new Dictionary<string, DetailBaseControl>();
private Dictionary<Message.NotificationClass, string> messageClassControlDict = new Dictionary<Message.NotificationClass, string>();
private Guid userId = Guid.NewGuid(); // remove THIS!!
private object messageListLock = new object();
private HighlightService highlightService = new HighlightService();
// Validation
protected List<MessageError> _vErrors = new List<MessageError>();
protected List<MessageViolation> _vViolations = new List<MessageViolation>();
#endregion
#region Properties
public MessageCore Core { get { return this._core; } }
public bool LockedByOtherUser { get; set; }
public ReportingParty LockedBy { get; set; }
internal event DatabaseEntityWatchdog.DatabaseEntityChangedHandler HighlightReset;
public bool HasUnsavedChanges
{
get { return (this.buttonSave.Visibility == Visibility.Visible); } // schwach aber es wird's tun
}
#endregion
#region Construction
public DetailRootControl(MessageCore aCore)
{
_core = aCore;
InitializeComponent();
shipNameLabel.Text = aCore.Shipname;
displayIdLabel.Text = aCore.DisplayId;
// Listbox befüllen
this._listBoxList.Add(new MessageGroup() { MessageGroupName = Properties.Resources.textOverview, MessageGroupControlType = typeof(OverViewDetailControl), ImagePath = "Resources/documents.png" });
this._listBoxList.Add(new MessageGroup() { MessageGroupName = Properties.Resources.textPortCall, MessageGroupControlType = typeof(PortCallDetailControl), ImagePath = "Resources/eye_blue.png" });
this._listBoxList.Add(new MessageGroup() { MessageGroupName = Properties.Resources.textPortNotification, MessageGroupControlType = typeof(PortNotificationDetailControl), ImagePath = "Resources/anchor.png" });
this._listBoxList.Add(new MessageGroup() { MessageGroupName = Properties.Resources.textWaste, MessageGroupControlType = typeof(WasteDetailControl), ImagePath = "Resources/garbage.png" });
this._listBoxList.Add(new MessageGroup() { MessageGroupName = Properties.Resources.textArrivalNotification, MessageGroupControlType = typeof(ArrivalNotificationDetailControl), ImagePath = "Resources/arrow_down_right_red.png" });
this._listBoxList.Add(new MessageGroup() { MessageGroupName = Properties.Resources.textSecurity, MessageGroupControlType = typeof(SecurityDetailControl), ImagePath = "Resources/shield_yellow.png" });
this._listBoxList.Add(new MessageGroup() { MessageGroupName = Properties.Resources.textPSC72h, MessageGroupControlType = typeof(PSC72hDetailControl), ImagePath = "Resources/alarmclock.png" });
this._listBoxList.Add(new MessageGroup() { MessageGroupName = Properties.Resources.textMDH, MessageGroupControlType = typeof(MaritimeHealthDeclarationDetailControl), ImagePath = "Resources/medical_bag.png" });
this._listBoxList.Add(new MessageGroup() { MessageGroupName = Properties.Resources.textDepartureNotification, MessageGroupControlType = typeof(DepartureNotificationDetailControl), ImagePath = "Resources/arrow_up_right_green.png" });
this._listBoxList.Add(new MessageGroup() { MessageGroupName = Properties.Resources.textShipData, MessageGroupControlType = typeof(ShipDataDetailControl), ImagePath = "Resources/containership.png" });
this._listBoxList.Add(new MessageGroup() { MessageGroupName = Properties.Resources.textBorderPolice, MessageGroupControlType = typeof(BorderPoliceDetailControl), ImagePath = "Resources/policeman_german.png" });
this._listBoxList.Add(new MessageGroup() { MessageGroupName = Properties.Resources.textDGArrival, MessageGroupControlType = typeof(DangerousGoodsDetailControl), ImagePath = "Resources/sign_warning_radiation.png" });
this._listBoxList.Add(new MessageGroup() { MessageGroupName = Properties.Resources.textDGDeparture, MessageGroupControlType = typeof(DangerousGoodsDetailControl), ImagePath = "Resources/sign_warning_radiation.png" });
this._listBoxList.Add(new MessageGroup() { MessageGroupName = Properties.Resources.textTowage, MessageGroupControlType = typeof(TowageDetailControl), ImagePath = "Resources/ship2.png" });
this.listBoxMessages.ItemsSource = this._listBoxList;
_messages = DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).GetMessagesForCore(_core, DBManager.MessageLoad.ALL);
List<Message> missingMessages = bsmd.database.Util.CreateMessagesForCore(_core, _messages, this.LockedBy);
_messages.AddRange(missingMessages);
BindingOperations.EnableCollectionSynchronization(_messages, this.messageListLock);
Dispatcher.BeginInvoke((Action)(() => this.listBoxMessages.SelectedIndex = 0));
}
#endregion
#region public methods
public void CoreChanged(MessageCore newCore)
{
this._core = newCore;
Application.Current.Dispatcher.Invoke(delegate
{
if(controlCache.ContainsKey(Properties.Resources.textOverview))
{
OverViewDetailControl ovdc = controlCache[Properties.Resources.textOverview] as OverViewDetailControl;
ovdc.Core = newCore;
ovdc.UpdateCore();
}
});
}
#endregion
#region class MessageGroup
/// <summary>
/// Klasse um ein Element der Listbox darzustellen (notwendig für das Databinding)
/// </summary>
public class MessageGroup
{
public Type MessageGroupControlType { get; set; }
public string MessageGroupName { get; set; }
public string ImagePath { get; set; }
}
#endregion
#region event handler
private void listBoxMessages_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if(listBoxMessages.SelectedItem != null)
{
MessageGroup mg = this.listBoxMessages.SelectedItem as MessageGroup;
if (!controlCache.ContainsKey(mg.MessageGroupName))
{
// create control instance for display:
DetailBaseControl detailControl = (DetailBaseControl)Activator.CreateInstance(mg.MessageGroupControlType);
// Spezial-Balkon für die Wiederverwendung von HAZD / HAZA als ein Control (es tut mir leid :D)
if (mg.MessageGroupName.Equals(Properties.Resources.textDGDeparture))
((DangerousGoodsDetailControl)detailControl).IsDeparture = true;
detailControl.Core = _core;
detailControl.Messages = _messages;
detailControl.LockedByOtherUser = this.LockedByOtherUser;
detailControl.JumpToListElementRequest += (index) =>
{
if ((index >= 0) && (index < _listBoxList.Count))
{
this.listBoxMessages.SelectedIndex = index;
}
};
detailControl.RequestReload += DetailControl_RequestReload;
detailControl.NotificationClassChanged += DetailControl_NotificationClassChanged;
detailControl.ResetControlCache += DetailControl_ResetControlCache;
detailControl.RequestValidate += DetailControl_RequestValidate;
detailControl.RequestDisable += DetailControl_RequestDisable;
detailControl.RequestSendValidation += DetailControl_RequestSendValidation;
detailControl.Initialize();
bool isEnabled = !this.LockedByOtherUser;
detailControl.SetEnabled(isEnabled);
if (!isEnabled && (detailControl is OverViewDetailControl) && !(_core.Cancelled ?? false))
((OverViewDetailControl)detailControl).ShowLockedBy(this.LockedBy);
controlCache.Add(mg.MessageGroupName, detailControl);
this.buttonSave.Visibility = Visibility.Hidden;
}
else
{
// Control has been created before: Set visibility of "save" button:
bool hasDirtyMessages = false;
DetailBaseControl dbc = controlCache[mg.MessageGroupName];
foreach (Message message in dbc.ControlMessages)
if (message.IsDirty)
hasDirtyMessages = true;
this.buttonSave.Visibility = hasDirtyMessages ? Visibility.Visible : Visibility.Hidden;
}
// plug it in ;-)
detailView.Children.Clear();
// zuerst Vio dann Error
controlCache[mg.MessageGroupName].HighlightViolationMessageContainer();
controlCache[mg.MessageGroupName].HighlightErrorMessageContainer();
detailView.Children.Add(controlCache[mg.MessageGroupName]);
}
}
private void DetailControl_RequestDisable()
{
this.LockedByOtherUser = true; // fake flag
// clear existing controls
this.detailView.Children.Clear();
this.controlCache.Clear();
// return to "new" overview
Dispatcher.BeginInvoke((Action)(() => this.listBoxMessages_SelectionChanged(this, null)));
}
private void DetailControl_ResetControlCache(string messageGroupName)
{
if (messageGroupName.IsNullOrEmpty()) return;
if (controlCache.ContainsKey(messageGroupName))
controlCache.Remove(messageGroupName);
}
private void buttonSave_Click(object sender, RoutedEventArgs e)
{
MessageBoxResult result = MessageBox.Show(Properties.Resources.textQuestionSavePage, Properties.Resources.textConfirmation,
MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
Util.UIHelper.SetBusyState();
DetailBaseControl currentControl = this.detailView.Children[0] as DetailBaseControl;
if (currentControl != null)
{
foreach (Message message in currentControl.ControlMessages)
{
this.SaveMessage(message);
}
this.buttonSave.Visibility = Visibility.Hidden;
if(currentControl is OverViewDetailControl)
{
// ggf. hat sich die Ticketnr geändert..
DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(currentControl.Core);
}
}
}
this.DetailControl_RequestReload();
}
private void SaveMessage(Message message)
{
if (message.IsDirty || message.IsNew)
{
if ((message.Status == Message.MessageStatus.ACCEPTED) &&
((message.InternalStatus == Message.BSMDStatus.CONFIRMED) || (message.InternalStatus == Message.BSMDStatus.VIOLATION)))
message.InternalStatus = Message.BSMDStatus.UPDATED;
string userName = "?";
if(App.UserId.HasValue && DBManager.Instance.GetReportingPartyDict().ContainsKey(App.UserId.Value))
{
userName = DBManager.Instance.GetReportingPartyDict()[App.UserId.Value].Logon;
}
message.ChangedBy = string.Format("{0} at {1}", userName, DateTime.Now);
DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(message);
if (message.MessageNotificationClass == Message.NotificationClass.CREW)
{
foreach(CREW crew in message.Elements)
{
if (crew.IsNew || crew.IsDirty) DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(crew);
}
}
else if (message.MessageNotificationClass == Message.NotificationClass.PAS)
{
foreach(PAS pas in message.Elements)
{
if(pas.IsNew || pas.IsDirty) DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(pas);
}
}
else
{
message.SaveElements();
}
message.IsDirty = false;
}
}
private void buttonSaveAll_Click(object sender, RoutedEventArgs e)
{
MessageBoxResult result = MessageBox.Show(Properties.Resources.textQuestionSaveAll, Properties.Resources.textConfirmation,
MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
Util.UIHelper.SetBusyState();
foreach (Message message in this._messages)
{
this.SaveMessage(message);
}
DetailBaseControl currentControl = this.detailView.Children[0] as DetailBaseControl;
if (currentControl is OverViewDetailControl)
{
// ggf. hat sich die Ticketnr geändert..
DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(currentControl.Core);
}
this.buttonSaveAll.Visibility = Visibility.Hidden;
this.buttonSave.Visibility = Visibility.Hidden;
}
this.DetailControl_RequestReload();
}
private void DetailControl_NotificationClassChanged(Message.NotificationClass notificationClass)
{
// in der Übersicht die Meldeklasse als geändert markieren..?
this.buttonSaveAll.Visibility = Visibility.Visible;
this.buttonSave.Visibility = Visibility.Visible;
}
private void DetailControl_RequestLock(bool shouldLock)
{
if(App.LockingServiceClient == null)
{
return;
}
if (shouldLock)
{
try
{
Guid lockedUserId = App.LockingServiceClient.Lock(this.Core.Id.Value, this.userId);
if (lockedUserId == Guid.Empty)
{
// lock successful
this.Core.Locked = true;
}
else
{
// TODO: locking failed: Notify User
}
}
catch(Exception ex)
{
Trace.WriteLine(ex.ToString());
// TODO
}
}
else
{
App.LockingServiceClient.Unlock(this.Core.Id.Value, this.userId);
this.Core.Locked = false;
}
}
private void DetailControl_RequestReload()
{
/// core und messages neu laden
///
this._core = DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).GetMessageCoreById(this.Core.Id.Value);
this._messages = DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).GetMessagesForCore(this._core, DBManager.MessageLoad.ALL);
// clear existing controls
this.detailView.Children.Clear();
this.controlCache.Clear();
// return to "new" overview
Dispatcher.BeginInvoke((Action)(() => this.listBoxMessages_SelectionChanged(this, null)));
// if the entity has been highlighted (through remote change detection), reset this here
this.OnHighlightReset();
}
private void DetailControl_RequestSendValidation()
{
this.Validate(false);
foreach(Message aMessage in this._messages)
{
if(aMessage.InternalStatus == Message.BSMDStatus.TOSEND)
{
foreach(MessageError messageError in this._vErrors)
{
if(messageError.NotificationClass == aMessage.MessageNotificationClassDisplay)
{
aMessage.InternalStatus = Message.BSMDStatus.SUSPENDED;
aMessage.ChangedBy = "";
aMessage.StatusInfo = string.Format("Validation error: {0}", messageError.ErrorText);
DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(aMessage);
break;
}
}
}
}
// NOA_NOD vor ATA vor ATD Versendereihenfolge sicherstellen
if ((this.Core.InitialHIS == Message.NSWProvider.DUDR) || (this.Core.InitialHIS == Message.NSWProvider.DUDR_TEST))
{
bool noa_nod_queued_or_sent = false;
bool ata_queued_or_sent = false;
foreach (Message aMessage in this._messages)
{
if (aMessage.MessageNotificationClass == Message.NotificationClass.NOA_NOD)
{
if ((aMessage.SendSuccess ?? false) || (aMessage.InternalStatus == Message.BSMDStatus.TOSEND))
noa_nod_queued_or_sent = true;
}
if (aMessage.MessageNotificationClass == Message.NotificationClass.ATA)
{
if ((aMessage.SendSuccess ?? false) || (aMessage.InternalStatus == Message.BSMDStatus.TOSEND))
ata_queued_or_sent = true;
}
}
foreach (Message aMessage in this._messages)
{
if ((aMessage.MessageNotificationClass == Message.NotificationClass.ATA) && (aMessage.InternalStatus == Message.BSMDStatus.TOSEND))
{
if (!noa_nod_queued_or_sent)
{
aMessage.InternalStatus = Message.BSMDStatus.SUSPENDED;
aMessage.ChangedBy = "";
aMessage.StatusInfo = string.Format("Validation error: NOA_NOD must be sent before ATA");
DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(aMessage);
}
}
if ((aMessage.MessageNotificationClass == Message.NotificationClass.ATD) && (aMessage.InternalStatus == Message.BSMDStatus.TOSEND))
{
if (!noa_nod_queued_or_sent)
{
aMessage.InternalStatus = Message.BSMDStatus.SUSPENDED;
aMessage.ChangedBy = "";
aMessage.StatusInfo = string.Format("Validation error: NOA_NOD must be sent before ATD");
DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(aMessage);
}
if (!ata_queued_or_sent)
{
aMessage.InternalStatus = Message.BSMDStatus.SUSPENDED;
aMessage.ChangedBy = "";
aMessage.StatusInfo = string.Format("Validation error: ATA must be sent before ATD");
DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(aMessage);
}
}
}
}
}
private void DetailControl_RequestValidate()
{
this.Validate(true);
}
private void Validate(bool showMessages)
{
this._vErrors.Clear();
this._vViolations.Clear();
// TODO: clear highlighting
Util.UIHelper.SetBusyState();
RuleEngine ruleEngine = new RuleEngine();
foreach (Message aMessage in _messages)
{
if (!aMessage.EvaluateForValidation(this.Core.IsTransit)) continue;
List<MessageError> errors = new List<MessageError>();
List<MessageViolation> violations = new List<MessageViolation>();
ruleEngine.ValidateMessage(aMessage, out errors, out violations);
string messageGroup = this.MessageGroupForMessage(aMessage);
if (messageGroup != null)
{
foreach (MessageError me in errors)
me.MessageGroupName = messageGroup;
foreach (MessageViolation mv in violations)
mv.MessageGroupName = messageGroup;
}
this._vErrors.AddRange(errors);
this._vViolations.AddRange(violations);
}
foreach (MessageError me in this._vErrors)
{
this.highlightService.HighlightError(me, this.GetContainerForMessageGroupName(me.MessageGroupName));
}
foreach (MessageViolation mv in this._vViolations)
{
this.highlightService.HighlightViolation(mv, this.GetContainerForMessageGroupName(mv.MessageGroupName));
}
// "neue" regelbasierte Validierung: Hier werden die einzelnen Regeln geprüft.
bsmd.database.ValidationRule.PrepareNameLookupDict(this.Core, this._messages);
List<bsmd.database.ValidationRule> validationRules = DBManager.Instance.GetValidationRules();
foreach (bsmd.database.ValidationRule validationRule in validationRules) {
if (!validationRule.IsActive ?? false) continue;
// Regel auspacken
ConditionGroup cg = ValidationCondition.LoadFromString(validationRule.Rule);
// außer der Nachricht müssten noch die "gescheiterten" Felder zurückgeliefert werden, damit ein Highlighting stattfinden kann!
List<ValidationField> failedFieldList = new List<ValidationField>();
string resultMessage = string.Empty;
if (cg != null) {
if (ValidationCondition.CheckConditions(this.Core, this._messages, cg, failedFieldList, out resultMessage) ?? false)
{
// Regel hat zugeschlagen -> reporting
Trace.WriteLine(string.Format("Rule {0} resulted in {1}", validationRule.Name, resultMessage));
} else
{
Trace.WriteLine(string.Format("Rule {0} passed", validationRule.Name));
}
}
}
if (showMessages)
{
// Show error and violation dialog
if (this._vErrors.Count > 0)
{
ErrorListDialog eld = new ErrorListDialog();
eld.IsModal = false;
eld.Errors = this._vErrors;
eld.Show();
}
if (this._vViolations.Count > 0)
{
ViolationListDialog vld = new ViolationListDialog();
vld.IsModal = false;
vld.Violations = this._vViolations;
vld.Show();
}
if((this._vErrors.Count == 0) && (this._vViolations.Count == 0))
{
MessageBox.Show(Properties.Resources.textValidationOK, Properties.Resources.textValidation, MessageBoxButton.OK, MessageBoxImage.Information);
}
}
}
#endregion
#region private / protected methods
protected virtual void OnHighlightReset()
{
this.HighlightReset?.Invoke(this.Core);
}
private DependencyObject GetContainerForMessageGroupName(string messageGroupName)
{
if (controlCache.ContainsKey(messageGroupName))
return controlCache[messageGroupName];
return null;
}
protected string MessageGroupForMessage(Message mh)
{
if (mh == null) return null;
switch(mh.MessageNotificationClass)
{
case Message.NotificationClass.ATA:
case Message.NotificationClass.TIEFA:
case Message.NotificationClass.POBA:
case Message.NotificationClass.BKRA:
return Properties.Resources.textArrivalNotification;
case Message.NotificationClass.BPOL:
case Message.NotificationClass.CREW:
case Message.NotificationClass.CREWD:
case Message.NotificationClass.PAS:
case Message.NotificationClass.PASD:
return Properties.Resources.textBorderPolice;
case Message.NotificationClass.HAZA:
return Properties.Resources.textDGArrival;
case Message.NotificationClass.HAZD:
return Properties.Resources.textDGDeparture;
case Message.NotificationClass.ATD:
case Message.NotificationClass.TIEFD:
case Message.NotificationClass.POBD:
case Message.NotificationClass.BKRD:
return Properties.Resources.textDepartureNotification;
case Message.NotificationClass.MDH:
return Properties.Resources.textMDH;
case Message.NotificationClass.NOA_NOD:
case Message.NotificationClass.AGNT:
return Properties.Resources.textPortCall;
case Message.NotificationClass.NAME:
case Message.NotificationClass.INFO:
case Message.NotificationClass.SERV:
case Message.NotificationClass.LADG:
return Properties.Resources.textPortNotification;
case Message.NotificationClass.PRE72H:
return Properties.Resources.textPSC72h;
case Message.NotificationClass.SEC:
return Properties.Resources.textSecurity;
case Message.NotificationClass.STAT:
return Properties.Resources.textShipData;
case Message.NotificationClass.TOWA:
case Message.NotificationClass.TOWD:
return Properties.Resources.textTowage;
case Message.NotificationClass.WAS:
return Properties.Resources.textWaste;
}
return null;
}
#endregion
}
}