1303 lines
64 KiB
C#
1303 lines
64 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;
|
|
using System.Windows.Input;
|
|
using Microsoft.Office.Interop.Excel;
|
|
using System.Linq;
|
|
|
|
namespace ENI2
|
|
{
|
|
/// <summary>
|
|
/// Interaction logic for DetailRootControl.xaml
|
|
/// </summary>
|
|
public partial class DetailRootControl : UserControl
|
|
{
|
|
|
|
#region Fields
|
|
|
|
private readonly List<MessageGroup> _listBoxList = new List<MessageGroup>();
|
|
private List<Message> _messages;
|
|
private readonly Dictionary<string, DetailBaseControl> controlCache = new Dictionary<string, DetailBaseControl>();
|
|
// private readonly Dictionary<Message.NotificationClass, string> messageClassControlDict = new Dictionary<Message.NotificationClass, string>();
|
|
private readonly object messageListLock = new object();
|
|
private readonly HighlightService highlightService = new HighlightService();
|
|
|
|
// Referenzen für Fehler/Violation Dialoge (können, müssen aber nicht offen bleiben)
|
|
protected ErrorListDialog _errorListDialog = null;
|
|
protected ViolationListDialog _violationListDialog = null;
|
|
|
|
#endregion
|
|
|
|
#region Properties
|
|
|
|
public MessageCore Core { get; private set; }
|
|
|
|
public bool LockedByOtherUser { get; set; }
|
|
|
|
public ReportingParty LockedBy { get; set; }
|
|
|
|
internal event DatabaseEntityWatchdog.DatabaseEntityChangedHandler HighlightReset;
|
|
|
|
internal event Action<MessageCore> OpenNewCoreRequested;
|
|
|
|
internal event Action<Guid> ReloadCoreRequested;
|
|
|
|
public bool HasUnsavedChanges
|
|
{
|
|
get { return this.buttonSave.Visibility == Visibility.Visible; } // schwach aber es wird's tun
|
|
}
|
|
|
|
public List<string> HasUnsentMessages
|
|
{
|
|
get
|
|
{
|
|
// Bedingung:
|
|
// wenn in einer Meldeklasse zwar Daten vorhanden sind, eingespielt durch Excel import oder
|
|
// Handeingabe, diese aber NICHT gesendet wurden.
|
|
// TODO: Hier wird noch ein Flag benötigt, dass die erfolgte Anzeige des Warndialogs speichert
|
|
List<string> result = new List<string>();
|
|
|
|
foreach (Message aMessage in _messages)
|
|
{
|
|
if (((aMessage.InternalStatus == Message.BSMDStatus.UPDATED) ||
|
|
(aMessage.InternalStatus == Message.BSMDStatus.SAVED)) && // ||
|
|
// (aMessage.InternalStatus == Message.BSMDStatus.EXCEL)) &&
|
|
!aMessage.UnsentMessageWarningShown)
|
|
{
|
|
aMessage.UnsentMessageWarningShown = true;
|
|
DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(aMessage);
|
|
result.Add(aMessage.MessageNotificationClassDisplay);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
public List<string> HasUnConfirmedMessages
|
|
{
|
|
get
|
|
{
|
|
List<string> result = new List<string>();
|
|
|
|
foreach(Message aMessage in _messages)
|
|
{
|
|
if (((aMessage.InternalStatus == Message.BSMDStatus.SENT) ||
|
|
(aMessage.InternalStatus == Message.BSMDStatus.TOSEND)) &&
|
|
!aMessage.UnconfirmedMessageWarningShown)
|
|
{
|
|
aMessage.UnconfirmedMessageWarningShown = true;
|
|
DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(aMessage);
|
|
result.Add(aMessage.MessageNotificationClassDisplay);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
public bool HasCriticalInfoMissing(out string messageText)
|
|
{
|
|
messageText = "";
|
|
// Hier haben wir Logik für Spezialfälle, z.B. dass für BRE und BRV bestimmte Meldeklassen gesendet werden *müssen*
|
|
|
|
if(this.Core.PoC.Equals("DEBRV") || this.Core.PoC.Equals("DEBRE"))
|
|
{
|
|
foreach(Message aMessage in _messages)
|
|
{
|
|
if((aMessage.MessageNotificationClass == Message.NotificationClass.NOA_NOD) && (aMessage.InternalStatus != Message.BSMDStatus.CONFIRMED)) { messageText = "NOA_NOD"; return true; }
|
|
if ((aMessage.MessageNotificationClass == Message.NotificationClass.AGNT) && (aMessage.InternalStatus != Message.BSMDStatus.CONFIRMED)) { messageText = "AGNT"; return true; }
|
|
if ((aMessage.MessageNotificationClass == Message.NotificationClass.INFO) && (aMessage.InternalStatus != Message.BSMDStatus.CONFIRMED)) { messageText = "INFO"; return true; }
|
|
if ((aMessage.MessageNotificationClass == Message.NotificationClass.SEC) && (aMessage.InternalStatus != Message.BSMDStatus.CONFIRMED)) { messageText = "SEC"; return true; }
|
|
if ((aMessage.MessageNotificationClass == Message.NotificationClass.TIEFA) && (aMessage.InternalStatus != Message.BSMDStatus.CONFIRMED)) { messageText = "TIEFA"; return true; }
|
|
if ((aMessage.MessageNotificationClass == Message.NotificationClass.SERV) && (aMessage.InternalStatus != Message.BSMDStatus.CONFIRMED)) { messageText = "SERV"; return true; }
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Construction
|
|
|
|
public DetailRootControl(MessageCore aCore)
|
|
{
|
|
Core = aCore;
|
|
InitializeComponent();
|
|
shipNameLabel.Text = aCore.Shipname;
|
|
shipEMailLabel.Text = aCore.HerbergEmailContactReportingVessel;
|
|
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((System.Action)(() => this.listBoxMessages.SelectedIndex = 0));
|
|
|
|
RoutedCommand saveCmd = new RoutedCommand();
|
|
saveCmd.InputGestures.Add(new KeyGesture(Key.S, ModifierKeys.Control));
|
|
CommandBindings.Add(new CommandBinding(saveCmd, this.buttonSave_Click));
|
|
|
|
RoutedCommand saveAllCmd = new RoutedCommand();
|
|
saveAllCmd.InputGestures.Add(new KeyGesture(Key.A, ModifierKeys.Control));
|
|
CommandBindings.Add(new CommandBinding(saveAllCmd, this.buttonSaveAll_Click));
|
|
|
|
RoutedCommand reloadCmd = new RoutedCommand();
|
|
reloadCmd.InputGestures.Add(new KeyGesture(Key.R, ModifierKeys.Control));
|
|
CommandBindings.Add(new CommandBinding(reloadCmd, this.ShortCutReload));
|
|
|
|
RoutedCommand validateCmd = new RoutedCommand();
|
|
validateCmd.InputGestures.Add(new KeyGesture(Key.F, ModifierKeys.Control));
|
|
CommandBindings.Add(new CommandBinding(validateCmd, this.ShortCutValidate));
|
|
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region public methods
|
|
|
|
public void CoreChanged(MessageCore newCore)
|
|
{
|
|
this.Core = newCore;
|
|
System.Windows.Application.Current.Dispatcher.Invoke(delegate
|
|
{
|
|
if(controlCache.ContainsKey(Properties.Resources.textOverview))
|
|
{
|
|
OverViewDetailControl ovdc = controlCache[Properties.Resources.textOverview] as OverViewDetailControl;
|
|
ovdc.Core = newCore;
|
|
ovdc.UpdateCore();
|
|
}
|
|
});
|
|
}
|
|
|
|
public void ReloadCore()
|
|
{
|
|
this.DetailControl_RequestReload(this.Core.Id.Value);
|
|
}
|
|
|
|
#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.RequestCopy += DetailControl_RequestCopy;
|
|
detailControl.RequestSendValidation += DetailControl_RequestSendValidation;
|
|
|
|
detailControl.Initialize();
|
|
bool isEnabled = !this.LockedByOtherUser;
|
|
detailControl.SetEnabled(isEnabled);
|
|
|
|
if (!isEnabled && (detailControl is OverViewDetailControl control) && !(Core.Cancelled ?? false))
|
|
control.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_RequestCopy()
|
|
{
|
|
CopyDeclarationDialog cdd = new CopyDeclarationDialog();
|
|
MessageCore newCore = new MessageCore();
|
|
cdd.NewCore = newCore;
|
|
cdd.OldCore = this.Core;
|
|
|
|
cdd.Closed += (senderDialog, closeArgs) =>
|
|
{
|
|
CopyDeclarationDialog closedDialog = senderDialog as CopyDeclarationDialog;
|
|
if (closedDialog.IsOK)
|
|
{
|
|
|
|
MessageCore existingCore = null;
|
|
|
|
if (!newCore.IsDK && newCore.VisitId.IsNullOrEmpty() && newCore.TransitId.IsNullOrEmpty())
|
|
{
|
|
// deutsche Häfen fordern eine Visit-Id an, für DK erfolgt hier nur die Anlage eines Datensatzes
|
|
newCore.BSMDStatusInternal = MessageCore.BSMDStatus.TOSEND;
|
|
}
|
|
|
|
if (newCore.PoC.Equals("ZZNOK")) newCore.IsTransit = true;
|
|
|
|
newCore.Incoming = true;
|
|
newCore.DefaultReportingPartyId = this.LockedBy.Id;
|
|
|
|
if(!newCore.VisitId.IsNullOrEmpty() || !newCore.TransitId.IsNullOrEmpty())
|
|
{
|
|
if (newCore.IsTransit)
|
|
existingCore = DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).GetMessageCoreByTransitId(newCore.TransitId);
|
|
else
|
|
existingCore = DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).GetMessageCoreByVisitId(newCore.VisitId);
|
|
}
|
|
|
|
bool skipCopyTransit = false;
|
|
bool skipCopyVisit = false;
|
|
|
|
if(existingCore != null)
|
|
{
|
|
if(MessageBox.Show(Properties.Resources.textDeclarationAlreadyExists, Properties.Resources.textCaptionExists,
|
|
MessageBoxButton.OKCancel, MessageBoxImage.Question) == MessageBoxResult.OK)
|
|
{
|
|
List<Message> existingMessages = DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).GetMessagesForCore(existingCore, DBManager.MessageLoad.ALL);
|
|
foreach(Message existingMessage in existingMessages)
|
|
{
|
|
if((existingMessage.InternalStatus == Message.BSMDStatus.SENT) &&
|
|
(existingMessage.MessageNotificationClass != Message.NotificationClass.VISIT) &&
|
|
(existingMessage.MessageNotificationClass != Message.NotificationClass.TRANSIT))
|
|
{
|
|
MessageBox.Show(Properties.Resources.textMessagesAlreadySent, Properties.Resources.textCaptionError, MessageBoxButton.OK, MessageBoxImage.Stop);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// delete all existing data of core
|
|
existingCore.ETA = newCore.ETA;
|
|
existingCore.ETAKielCanal = newCore.ETAKielCanal;
|
|
newCore = existingCore;
|
|
|
|
foreach (Message existingMessage in existingMessages)
|
|
{
|
|
|
|
// Bearbeitungsinformationen für bestehende ID-Beantragung beibehalten, falls bereits vorhanden
|
|
|
|
if(existingCore.IsTransit &&
|
|
(existingMessage.MessageNotificationClass == Message.NotificationClass.TRANSIT) &&
|
|
(existingMessage.InternalStatus == Message.BSMDStatus.CONFIRMED))
|
|
{
|
|
skipCopyTransit = true;
|
|
continue;
|
|
}
|
|
|
|
if(!existingCore.IsTransit &&
|
|
(existingMessage.MessageNotificationClass == Message.NotificationClass.VISIT) &&
|
|
(existingMessage.InternalStatus == Message.BSMDStatus.CONFIRMED))
|
|
{
|
|
skipCopyVisit = true;
|
|
continue;
|
|
}
|
|
|
|
if (existingMessage is ISublistContainer sublistContainer)
|
|
{
|
|
(sublistContainer).DeleteElements();
|
|
}
|
|
|
|
DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Delete(existingMessage);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(newCore);
|
|
|
|
// Meldeklassen für neuen Anlauf erzeugen
|
|
List<Message> newMessages = new List<Message>();
|
|
foreach (Message oldMessage in this._messages)
|
|
{
|
|
if (skipCopyTransit && (oldMessage.MessageNotificationClass == Message.NotificationClass.TRANSIT)) continue;
|
|
if (skipCopyVisit && (oldMessage.MessageNotificationClass == Message.NotificationClass.VISIT)) continue;
|
|
|
|
// 10.9.19: bestimmte Meldeklassen nicht kopieren. Muss getestet werden, ob hier
|
|
// stattdessen "Leer"-Messages erzeugt werden müssen
|
|
if (oldMessage.MessageNotificationClass == Message.NotificationClass.ATA) continue;
|
|
if (oldMessage.MessageNotificationClass == Message.NotificationClass.ATD) continue;
|
|
if (oldMessage.MessageNotificationClass == Message.NotificationClass.NOA_NOD) continue;
|
|
|
|
bool isAndienKlasse = (oldMessage.MessageNotificationClass == Message.NotificationClass.AGNT) ||
|
|
(oldMessage.MessageNotificationClass == Message.NotificationClass.STAT) ||
|
|
(oldMessage.MessageNotificationClass == Message.NotificationClass.INFO) ||
|
|
(oldMessage.MessageNotificationClass == Message.NotificationClass.HAZA) ||
|
|
(oldMessage.MessageNotificationClass == Message.NotificationClass.HAZD);
|
|
|
|
if (!cdd.CopyAll && !isAndienKlasse) continue;
|
|
|
|
Message newMessage = oldMessage.Clone() as Message;
|
|
newMessage.MessageCore = newCore;
|
|
newMessage.MessageCoreId = newCore.Id;
|
|
DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(newMessage);
|
|
newMessage.SaveElements();
|
|
}
|
|
|
|
this.OnOpenNewCoreRequested(newCore);
|
|
}
|
|
};
|
|
cdd.Show();
|
|
}
|
|
|
|
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((System.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();
|
|
|
|
if (this.detailView.Children[0] is DetailBaseControl currentControl)
|
|
{
|
|
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(this.Core.Id.Value);
|
|
}
|
|
|
|
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;
|
|
else
|
|
message.InternalStatus = Message.BSMDStatus.SAVED;
|
|
|
|
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);
|
|
message.IsDirty = false;
|
|
message.UnsentMessageWarningShown = false;
|
|
message.UnconfirmedMessageWarningShown = false;
|
|
DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(message);
|
|
|
|
message.SaveElements();
|
|
|
|
if(message.MessageNotificationClass == Message.NotificationClass.ATA)
|
|
{
|
|
DetailBaseControl currentControl = this.detailView.Children[0] as DetailBaseControl;
|
|
// ggf. hat sich die Ticketnr geändert..
|
|
DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(currentControl.Core);
|
|
}
|
|
|
|
if(message.MessageNotificationClass == Message.NotificationClass.STAT)
|
|
{
|
|
DetailBaseControl currentControl = this.detailView.Children[0] as DetailBaseControl;
|
|
currentControl.Core.IsSmallShip = ((STAT)message.Elements[0]).GrossTonnage < 500;
|
|
DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(currentControl.Core);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
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(this.Core.Id.Value);
|
|
}
|
|
|
|
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 ShortCutReload(object sender, ExecutedRoutedEventArgs e)
|
|
{
|
|
this.DetailControl_RequestReload(this.Core.Id.Value);
|
|
}
|
|
|
|
private void ShortCutValidate(object sender, ExecutedRoutedEventArgs e)
|
|
{
|
|
this.DetailControl_RequestValidate();
|
|
}
|
|
|
|
private void DetailControl_RequestReload(Guid id)
|
|
{
|
|
/// core und messages neu laden
|
|
///
|
|
if (this.Core.Id.Value != id)
|
|
{
|
|
this.ReloadCoreRequested?.Invoke(id);
|
|
}
|
|
else
|
|
{
|
|
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" overviewdan
|
|
Dispatcher.BeginInvoke((System.Action)(() =>
|
|
{
|
|
this.listBoxMessages_SelectionChanged(this, null);
|
|
shipNameLabel.Text = this.Core.Shipname;
|
|
shipEMailLabel.Text = this.Core.HerbergEmailContactReportingVessel;
|
|
}
|
|
));
|
|
|
|
// if the entity has been highlighted (through remote change detection), reset this here
|
|
this.OnHighlightReset();
|
|
}
|
|
}
|
|
|
|
private void DetailControl_RequestSendValidation()
|
|
{
|
|
this.Validate(false, out _, out List<MessageError> errorList);
|
|
|
|
foreach (Message aMessage in this._messages)
|
|
{
|
|
if(aMessage.InternalStatus == Message.BSMDStatus.TOSEND)
|
|
{
|
|
foreach(MessageError messageError in errorList)
|
|
{
|
|
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)
|
|
{
|
|
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 142: 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 142: NOA_NOD must be sent before ATD");
|
|
DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(aMessage);
|
|
}
|
|
|
|
if ((!ata_queued_or_sent ) && !DBManager.Instance.GetReportingPartyDict()[App.UserId.Value].IsAdmin)
|
|
{
|
|
aMessage.InternalStatus = Message.BSMDStatus.SUSPENDED;
|
|
aMessage.ChangedBy = "";
|
|
aMessage.StatusInfo = string.Format("Validation error 143: ATA must be sent before ATD");
|
|
DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(aMessage);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void DetailControl_RequestValidate()
|
|
{
|
|
this.Validate(true, out _, out _);
|
|
}
|
|
|
|
private void Validate(bool showMessages, out List<MessageViolation> vViolations, out List<MessageError> vErrors)
|
|
{
|
|
vViolations = new List<MessageViolation>();
|
|
vErrors = new List<MessageError>();
|
|
|
|
// 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;
|
|
}
|
|
|
|
vErrors.AddRange(errors);
|
|
vViolations.AddRange(violations);
|
|
}
|
|
|
|
#region 12.11.18 / 6.3.21 / 23.5.22: globale Plausi-Prüfungen
|
|
|
|
Message crewaMessage = _messages.Find(message => message.MessageNotificationClass == Message.NotificationClass.CREWA);
|
|
Message crewdMessage = _messages.Find(message => message.MessageNotificationClass == Message.NotificationClass.CREWD);
|
|
Message pasaMessage = _messages.Find(message => message.MessageNotificationClass == Message.NotificationClass.PASA);
|
|
Message pasdMessage = _messages.Find(message => message.MessageNotificationClass == Message.NotificationClass.PASD);
|
|
Message pobaMessage = _messages.Find(message => message.MessageNotificationClass == Message.NotificationClass.POBA);
|
|
Message pobdMessage = _messages.Find(message => message.MessageNotificationClass == Message.NotificationClass.POBD);
|
|
Message secMessage = _messages.Find(message => message.MessageNotificationClass == Message.NotificationClass.SEC);
|
|
Message noanodMessage = _messages.Find(message => message.MessageNotificationClass == Message.NotificationClass.NOA_NOD);
|
|
Message mdhMessage = _messages.Find(message => message.MessageNotificationClass == Message.NotificationClass.MDH);
|
|
Message was_rcptMessage = _messages.Find(message => message.MessageNotificationClass == Message.NotificationClass.WAS_RCPT);
|
|
Message wasMessage = _messages.Find(message => message.MessageNotificationClass == Message.NotificationClass.WAS);
|
|
Message servMessage = _messages.Find(message => message.MessageNotificationClass == Message.NotificationClass.SERV);
|
|
|
|
#region CREW / PAS Count Plausibility
|
|
|
|
if (pobaMessage.Elements.Count == 1)
|
|
{
|
|
POBA poba = pobaMessage.Elements[0] as POBA;
|
|
|
|
if (crewaMessage.Elements.Count != (poba.TotalCrewMembersOnBoardUponArrival ?? 0))
|
|
{
|
|
MessageViolation mv = RuleEngine.CreateViolation(ValidationCode.IMPLAUSIBLE, "POBA crew member count different from CREW count!", null, "Crew count mismatch", null, "CREWA");
|
|
mv.MessageGroupName = Properties.Resources.textOverview;
|
|
vViolations.Add(mv);
|
|
}
|
|
|
|
if(pasaMessage.Elements.Count != (poba.TotalPassengersOnBoardUponArrival ?? 0))
|
|
{
|
|
MessageViolation mv = RuleEngine.CreateViolation(ValidationCode.IMPLAUSIBLE, "POBA passenger count different from PAS count!", null, "Passenger count mismatch", null, "PASA");
|
|
mv.MessageGroupName = Properties.Resources.textOverview;
|
|
vViolations.Add(mv);
|
|
}
|
|
|
|
POBD pobd = pobdMessage.Elements[0] as POBD;
|
|
|
|
if (crewdMessage.Elements.Count != (pobd.TotalCrewMembersOnBoardUponDeparture ?? 0))
|
|
{
|
|
MessageViolation mv = RuleEngine.CreateViolation(ValidationCode.IMPLAUSIBLE, "POBD crew member count different from CREW count!", null, "Crew count mismatch", null, "CREWD");
|
|
mv.MessageGroupName = Properties.Resources.textOverview;
|
|
vViolations.Add(mv);
|
|
}
|
|
|
|
if (pasdMessage.Elements.Count != (pobd.TotalPassengersOnBoardUponDeparture ?? 0))
|
|
{
|
|
MessageViolation mv = RuleEngine.CreateViolation(ValidationCode.IMPLAUSIBLE, "POBD passenger count different from PAS count!", null, "Passenger count mismatch", null, "PASD");
|
|
mv.MessageGroupName = Properties.Resources.textOverview;
|
|
vViolations.Add(mv);
|
|
}
|
|
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region CREW/PAS Schengen Plausibility
|
|
|
|
// Wir können davon ausgehen, dass bei allen Unterelementen die Flags gleich gesetzt sind. Das wird im Import und BorderPoliceDetailControl sichergestellt.
|
|
|
|
if(crewaMessage.Elements.Count > 0)
|
|
{
|
|
CREW crewaFirst = crewaMessage.Elements[0] as CREW;
|
|
bool crewaIsSchengen = crewaFirst.NotificationSchengen ?? false;
|
|
if (!((crewaFirst.NotificationPAX ?? false) || crewaIsSchengen)) // mindestens eins der beiden
|
|
{
|
|
vErrors.Add(RuleEngine.CreateError(ValidationCode.V181, "Pax / Schengen: one must be set", null, Properties.Resources.textOverview, null, "CREWA")); ;
|
|
}
|
|
}
|
|
|
|
if (crewdMessage != null)
|
|
{
|
|
if (crewdMessage.Elements.Count > 0)
|
|
{
|
|
CREWD crewdFirst = crewdMessage.Elements[0] as CREWD;
|
|
bool crewdIsSchengen = crewdFirst.NotificationSchengen ?? false;
|
|
if (!((crewdFirst.NotificationPAX ?? false) || crewdIsSchengen)) // mindestens eins der beiden
|
|
{
|
|
vErrors.Add(RuleEngine.CreateError(ValidationCode.V181, "Pax / Schengen: one must be set", null, Properties.Resources.textOverview, null, "CREWD")); ;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(pasaMessage.Elements.Count > 0)
|
|
{
|
|
PAS pasFirst = pasaMessage.Elements[0] as PAS;
|
|
bool pasIsSchengen = pasFirst.NotificationSchengen ?? false;
|
|
bool pasIsPAX = pasFirst.NotificationPAX ?? false;
|
|
if (!(pasIsPAX || pasIsSchengen)) // mindestens eins der beiden
|
|
{
|
|
vErrors.Add(RuleEngine.CreateError (ValidationCode.V201, "Pax / Schengen: one must be set", null, Properties.Resources.textOverview, null, "PASA"));
|
|
}
|
|
}
|
|
|
|
if (pasdMessage != null)
|
|
{
|
|
if (pasdMessage.Elements.Count > 0)
|
|
{
|
|
PASD pasdFirst = pasdMessage.Elements[0] as PASD;
|
|
bool pasdIsSchengen = pasdFirst.NotificationSchengen ?? false;
|
|
bool pasdIsPAX = pasdFirst.NotificationPAX ?? false;
|
|
if (!(pasdIsPAX || pasdIsSchengen)) // mindestens eins der beiden
|
|
{
|
|
vErrors.Add(RuleEngine.CreateError(ValidationCode.V201, "Pax / Schengen: one must be set", null, Properties.Resources.textOverview, null, "PASD"));
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region 7.11.23 > 12 Passagiere in PASA oder PASD -> CREW* und PAS* muss NotificationPAX gesetzt haben
|
|
|
|
if ((pasaMessage != null) && (pasaMessage.Elements.Count > 12))
|
|
{
|
|
PAS firstPASA = pasaMessage.Elements[0] as PAS;
|
|
if(!(firstPASA.NotificationPAX ?? false))
|
|
{
|
|
MessageViolation mv = RuleEngine.CreateViolation(ValidationCode.NO_PAX, "No PAX set (PASA > 12)", null, Properties.Resources.textOverview, null, "PASA");
|
|
vViolations.Add(mv);
|
|
}
|
|
|
|
if((crewaMessage != null) && (crewaMessage.Elements.Count > 0))
|
|
{
|
|
CREW firstCREW = crewaMessage.Elements[0] as CREW;
|
|
if(!(firstCREW.NotificationPAX ?? false))
|
|
{
|
|
MessageViolation mv = RuleEngine.CreateViolation(ValidationCode.NO_PAX, "No PAX set (PASA > 12)", null, Properties.Resources.textOverview, null, "CREWA");
|
|
vViolations.Add(mv);
|
|
}
|
|
}
|
|
}
|
|
|
|
if((pasdMessage != null) && (pasdMessage.Elements.Count > 12))
|
|
{
|
|
PASD firstPASD = pasdMessage.Elements[0] as PASD;
|
|
if(!(firstPASD.NotificationPAX ?? false))
|
|
{
|
|
MessageViolation mv = RuleEngine.CreateViolation(ValidationCode.NO_PAX, "No PAX set (PASD > 12)", null, Properties.Resources.textOverview, null, "PASD");
|
|
vViolations.Add(mv);
|
|
}
|
|
|
|
if ((crewdMessage != null) && (crewdMessage.Elements.Count > 0))
|
|
{
|
|
CREWD firstCREW = crewdMessage.Elements[0] as CREWD;
|
|
if(!(firstCREW.NotificationPAX ?? false))
|
|
{
|
|
MessageViolation mv = RuleEngine.CreateViolation(ValidationCode.NO_PAX, "No PAX set (PASD > 12)", null, Properties.Resources.textOverview, null, "CREWD");
|
|
vViolations.Add(mv);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region 4.1.23 no CREW effects warning for DE
|
|
if (crewaMessage != null)
|
|
{
|
|
MessageViolation mv = crewaMessage.ViolationList.Find((x) => x.PropertyName.Equals("Effects") && (x.ViolationCode == (int)ValidationCode.TRUNCATE));
|
|
if ((mv != null) && !Core.IsDK)
|
|
crewaMessage.ViolationList.Remove(mv);
|
|
}
|
|
|
|
if(crewdMessage != null)
|
|
{
|
|
MessageViolation mvd = crewdMessage.ViolationList.Find((x) => x.PropertyName.Equals("Effects") && (x.ViolationCode == (int)ValidationCode.TRUNCATE));
|
|
if ((mvd != null) && !Core.IsDK)
|
|
crewdMessage.ViolationList.Remove(mvd);
|
|
}
|
|
#endregion
|
|
|
|
#region WAS_RCPT double numbers
|
|
|
|
Dictionary<string, string> identDict = new Dictionary<string, string>();
|
|
|
|
foreach (WAS_RCPT was_rcpt in was_rcptMessage.Elements.Cast<WAS_RCPT>())
|
|
{
|
|
if (!was_rcpt.IdentificationNumber.IsNullOrEmpty())
|
|
{
|
|
// auf doppelte Ident Nummern prüfen
|
|
if (identDict.ContainsKey(was_rcpt.IdentificationNumber))
|
|
{
|
|
MessageViolation mv = RuleEngine.CreateViolation(ValidationCode.V221, "Identification number", null, "WAS_RCPT", was_rcpt.Identifier, "WAS_RCPT");
|
|
mv.MessageGroupName = Properties.Resources.textOverview;
|
|
vViolations.Add(mv);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
identDict.Add(was_rcpt.IdentificationNumber, "");
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Kiel Canal Timing Plausibility
|
|
|
|
if (!this.Core.IsTransit && (secMessage?.Elements.Count > 0) && (noanodMessage?.Elements.Count > 0))
|
|
{
|
|
SEC sec = secMessage.Elements[0] as SEC;
|
|
NOA_NOD noa_nod = noanodMessage.Elements[0] as NOA_NOD;
|
|
|
|
if (sec.KielCanalPassagePlanned ?? false)
|
|
{
|
|
// Überprüfung, ob die eingehende NOK-Durchfahrt auch wirklich innerhalb der eingehenden Reise liegt (bei VISIT)
|
|
bool isValidIncoming = (noa_nod.ETDFromLastPort < sec.KielCanalPassagePlannedIncomming) &&
|
|
(sec.KielCanalPassagePlannedIncomming < noa_nod.ETAToPortOfCall);
|
|
if (!sec.KielCanalPassagePlannedIncomming.HasValue) isValidIncoming = true;
|
|
|
|
if(!isValidIncoming)
|
|
{
|
|
MessageViolation mv = RuleEngine.CreateViolation(ValidationCode.IMPLAUSIBLE, "Kiel Canal incoming implausible (ETD / ETA ports)!", null, "Kiel Canal timing", null, "SEC");
|
|
mv.MessageGroupName = Properties.Resources.textOverview;
|
|
vViolations.Add(mv);
|
|
}
|
|
|
|
|
|
bool isValidOutgoing = (noa_nod.ETDFromPortOfCall < sec.KielCanalPassagePlannedOutgoing) &&
|
|
((noa_nod.NextPort == "ZZUKN") || (sec.KielCanalPassagePlannedOutgoing < noa_nod.ETAToNextPort));
|
|
if (!sec.KielCanalPassagePlannedOutgoing.HasValue) isValidOutgoing = true;
|
|
|
|
if(!isValidOutgoing)
|
|
{
|
|
MessageViolation mv = RuleEngine.CreateViolation(ValidationCode.IMPLAUSIBLE, "Kiel Canal outgoing implausible (ETD / ETA ports)!", null, "Kiel Canal timing", null, "SEC");
|
|
mv.MessageGroupName = Properties.Resources.textOverview;
|
|
vViolations.Add(mv);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IMO plausibility
|
|
|
|
if (!this.Core.IMO.IsNullOrEmpty() && !bsmd.database.Util.IsIMOValid(this.Core.IMO))
|
|
{
|
|
MessageViolation mv = RuleEngine.CreateViolation(ValidationCode.IMPLAUSIBLE, "IMO number does not follow the rules", null, "GLOBAL");
|
|
mv.MessageGroupName = Properties.Resources.textOverview;
|
|
vViolations.Add(mv);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region last port plausibility SEC vs NOANOD
|
|
|
|
if((secMessage.Elements.Count > 0) && (noanodMessage.Elements.Count > 0))
|
|
{
|
|
SEC sec = secMessage.Elements[0] as SEC;
|
|
NOA_NOD noanod = noanodMessage.Elements[0] as NOA_NOD;
|
|
if((sec.LastTenPortFacilitesCalled.Count > 0) && (!sec.SECSimplification ?? false))
|
|
{
|
|
if((sec.LastTenPortFacilitesCalled[0].PortFacilityPortLoCode == null) || !sec.LastTenPortFacilitesCalled[0].PortFacilityPortLoCode.Equals(noanod.LastPort))
|
|
{
|
|
MessageViolation mv = RuleEngine.CreateViolation(ValidationCode.IMPLAUSIBLE, "SEC last port doesn't match NOANOD last port", null, "Last port mismatch", null, "SEC");
|
|
mv.MessageGroupName = Properties.Resources.textOverview;
|
|
vViolations.Add(mv);
|
|
}
|
|
DateTime comparison;
|
|
if (noanod.ETDFromLastPort.HasValue) comparison = noanod.ETDFromLastPort.Value.ToLocalTime().Date;
|
|
else comparison = DateTime.Now.Date;
|
|
if((sec.LastTenPortFacilitesCalled[0].PortFacilityDateOfDeparture == null) || (sec.LastTenPortFacilitesCalled[0].PortFacilityDateOfDeparture != comparison))
|
|
{
|
|
MessageViolation mv = RuleEngine.CreateViolation(ValidationCode.IMPLAUSIBLE, "SEC last port departure doesn't match NOANOD ETDFromLastPort", null, "Last port departure mismatch", null, "SEC");
|
|
mv.MessageGroupName = Properties.Resources.textOverview;
|
|
vViolations.Add(mv);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region SEC / MDH last port
|
|
|
|
{ // scope to hide sec
|
|
// 5.1.22 "Generell zu Überprüfen wäre:
|
|
// Sind alle Einträge aus SEC, die innerhalb der letzten 30 Tag liegen, gem. LoCode und ETD auch in der MDH-Liste enthalten ?"
|
|
if ((secMessage.Elements[0] is SEC sec) && (mdhMessage.Elements[0] is MDH mdh))
|
|
{
|
|
NOA_NOD noa_nod = noanodMessage.Elements[0] as NOA_NOD;
|
|
DateTime refDate = DateTime.Now;
|
|
if (this.Core.IsTransit)
|
|
{
|
|
if (noa_nod.ETAToKielCanal.HasValue)
|
|
refDate = noa_nod.ETAToKielCanal.Value.Date;
|
|
else
|
|
refDate = this.Core.ETAKielCanal.Value;
|
|
}
|
|
else
|
|
{
|
|
if (noa_nod.ETAToPortOfCall.HasValue)
|
|
refDate = noa_nod.ETAToPortOfCall.Value.Date;
|
|
else
|
|
refDate = this.Core.ETA.Value;
|
|
}
|
|
|
|
foreach (LastTenPortFacilitiesCalled l10c in sec.LastTenPortFacilitesCalled)
|
|
{
|
|
if (!l10c.PortFacilityDateOfDeparture.HasValue) continue;
|
|
if ((refDate - l10c.PortFacilityDateOfDeparture.Value).TotalDays < 31)
|
|
{
|
|
if (!l10c.PortFacilityPortLoCode.IsNullOrEmpty() && l10c.PortFacilityDateOfDeparture.HasValue) // this valid entry needs a match in mdh
|
|
{
|
|
bool matchIsFound = false;
|
|
|
|
foreach (PortOfCallLast30Days poc30d in mdh.PortOfCallLast30Days)
|
|
{
|
|
if (poc30d.PortOfCallLast30DaysDateOfDeparture.HasValue && !poc30d.PortOfCallLast30DaysLocode.IsNullOrEmpty())
|
|
{
|
|
if((poc30d.PortOfCallLast30DaysDateOfDeparture.Value.Date == l10c.PortFacilityDateOfDeparture.Value.Date) &&
|
|
poc30d.PortOfCallLast30DaysLocode.Equals(l10c.PortFacilityPortLoCode, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
matchIsFound = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!matchIsFound)
|
|
{
|
|
string msg = string.Format("SEC / MDH last ports do not match (Locode, Date) at {0}", l10c.Identifier);
|
|
MessageViolation mv = RuleEngine.CreateViolation(ValidationCode.IMPLAUSIBLE, msg, null, "SEC/MDH comparison", null, "SEC");
|
|
mv.MessageGroupName = Properties.Resources.textOverview;
|
|
vViolations.Add(mv);
|
|
break; // report this only once..
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Waste disposal service provider for DEHAM
|
|
|
|
if ((!this.Core.DisplayId.IsNullOrEmpty() && this.Core.DisplayId.StartsWith("DEHAM")) ||
|
|
this.Core.PoC.Equals("DEHAM"))
|
|
{
|
|
if (wasMessage.Elements.Count > 0)
|
|
{
|
|
WAS was = wasMessage.Elements[0] as WAS;
|
|
if ((!was.WasteDisposalValidExemption ?? true) && (was.WasteDisposalServiceProvider.Count == 0))
|
|
{
|
|
MessageViolation mv = RuleEngine.CreateViolation(ValidationCode.NOT_NULL, "Check waste disposal service provider", null, "WAS service provider", null, "WAS");
|
|
mv.MessageGroupName = Properties.Resources.textOverview;
|
|
vViolations.Add(mv);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region SERV existence for DEHAM / DEBRE / DEBRV
|
|
|
|
if ((!this.Core.DisplayId.IsNullOrEmpty() && this.Core.DisplayId.StartsWith("DEHAM")) || this.Core.PoC.Equals("DEHAM") ||
|
|
(!this.Core.DisplayId.IsNullOrEmpty() && this.Core.DisplayId.StartsWith("DEBRE")) || this.Core.PoC.Equals("DEBRE") ||
|
|
(!this.Core.DisplayId.IsNullOrEmpty() && this.Core.DisplayId.StartsWith("DEBRV")) || this.Core.PoC.Equals("DEBRV"))
|
|
{
|
|
if (servMessage.Elements.Count == 0)
|
|
{
|
|
MessageViolation mv = RuleEngine.CreateViolation(ValidationCode.LIST_EMPTY, "No entry for SERV found", null, "SERV service provider", null, "SERV");
|
|
mv.MessageGroupName = Properties.Resources.textPortNotification;
|
|
vViolations.Add(mv);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
foreach (MessageError me in vErrors)
|
|
{
|
|
this.highlightService.HighlightError(me, this.GetContainerForMessageGroupName(me.MessageGroupName));
|
|
}
|
|
foreach (MessageViolation mv in 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 (vErrors.Count > 0)
|
|
{
|
|
if(this._errorListDialog == null)
|
|
{
|
|
this._errorListDialog = new ErrorListDialog();
|
|
this._errorListDialog.Closed += (o, e) => this._errorListDialog = null;
|
|
this._errorListDialog.Loaded += (o, e) => this._errorListDialog.RefreshVisible = true;
|
|
this._errorListDialog.ErrorSelected += _errorListDialog_ErrorSelected;
|
|
this._errorListDialog.RefreshClicked += _errorListDialog_RefreshClicked;
|
|
this._errorListDialog.Show();
|
|
}
|
|
else
|
|
{
|
|
this._errorListDialog.BringUp();
|
|
}
|
|
this._errorListDialog.Errors = vErrors;
|
|
}
|
|
|
|
if (vViolations.Count > 0)
|
|
{
|
|
if(this._violationListDialog == null)
|
|
{
|
|
this._violationListDialog = new ViolationListDialog();
|
|
this._violationListDialog.Closed += (o, e) => this._violationListDialog = null;
|
|
this._violationListDialog.Loaded += (o, e) => this._violationListDialog.RefreshVisible = true;
|
|
this._violationListDialog.ViolationSelected += _errorListDialog_ErrorSelected;
|
|
this._violationListDialog.RefreshClicked += _errorListDialog_RefreshClicked;
|
|
this._violationListDialog.Show();
|
|
}
|
|
else
|
|
{
|
|
this._violationListDialog.BringUp();
|
|
}
|
|
_violationListDialog.Violations = vViolations;
|
|
}
|
|
|
|
if((vErrors.Count == 0) && (vViolations.Count == 0))
|
|
{
|
|
MessageBox.Show(Properties.Resources.textValidationOK, Properties.Resources.textValidation, MessageBoxButton.OK, MessageBoxImage.Information);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
private void _errorListDialog_RefreshClicked()
|
|
{
|
|
DetailControl_RequestValidate();
|
|
}
|
|
|
|
private void _errorListDialog_ErrorSelected(DatabaseEntity obj)
|
|
{
|
|
string msgGroupName = null;
|
|
if(obj is MessageError)
|
|
{
|
|
MessageError me = obj as MessageError;
|
|
msgGroupName = me.MessageGroupName;
|
|
}
|
|
else if(obj is MessageViolation)
|
|
{
|
|
MessageViolation mv = obj as MessageViolation;
|
|
msgGroupName = mv.MessageGroupName;
|
|
}
|
|
|
|
if(msgGroupName != null)
|
|
{
|
|
for(int i=0;i<_listBoxList.Count;i++)
|
|
{
|
|
if(_listBoxList[i].MessageGroupName.Equals(msgGroupName))
|
|
{
|
|
this.listBoxMessages.SelectedIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region private / protected methods
|
|
|
|
protected virtual void OnHighlightReset()
|
|
{
|
|
this.HighlightReset?.Invoke(this.Core);
|
|
}
|
|
|
|
protected virtual void OnOpenNewCoreRequested(MessageCore newCore)
|
|
{
|
|
this.OpenNewCoreRequested?.Invoke(newCore);
|
|
}
|
|
|
|
private DependencyObject GetContainerForMessageGroupName(string messageGroupName)
|
|
{
|
|
if (messageGroupName != null)
|
|
{
|
|
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.CREWA:
|
|
case Message.NotificationClass.CREWD:
|
|
case Message.NotificationClass.PASA:
|
|
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:
|
|
case Message.NotificationClass.WAS_RCPT:
|
|
return Properties.Resources.textWaste;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
#endregion
|
|
|
|
}
|
|
}
|