git_bsmd/ENI2/Controls/MaerskListControl.xaml.cs
2023-02-24 18:34:34 +01:00

541 lines
22 KiB
C#

// Copyright (c) 2017 / 2023 schick Informatik
// Description: Request dbh ids for Maersk data lists
//
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Win32;
using bsmd.database;
using ExcelDataReader;
using System.Collections.ObjectModel;
using ENI2.Excel;
using ENI2.Locode;
using ENI2.Util;
using System.Diagnostics;
using System.Linq;
namespace ENI2.Controls
{
/// <summary>
/// Interaction logic for MaerskListControl.xaml
/// </summary>
public partial class MaerskListControl : UserControl
{
#region Fields
private readonly ObservableCollection<MaerskData> maerskDataList = new ObservableCollection<MaerskData>();
private const uint MAX_EMPTY_ROWS_ON_IMPORT = 3; // import breaks if more than this count of empty rows have been read
private readonly DatabaseEntityWatchdog _dbWatchDog;
#endregion
#region Construction
public MaerskListControl()
{
InitializeComponent();
Loaded += POList_Loaded;
this.dateTimePickerFrom.Value = DateTime.Today.AddDays(-14);
this.dateTimePickerTo.Value = DateTime.Today.AddDays(14);
this._dbWatchDog = new DatabaseEntityWatchdog();
this._dbWatchDog.DatabaseEntityChanged += _dbWatchDog_DatabaseEntityChanged;
this._dbWatchDog.VisitTransitIdUpdated += _dbWatchDog_VisitTransitIdUpdated;
}
private async void _dbWatchDog_VisitTransitIdUpdated(DatabaseEntity entity)
{
if (entity is MessageCore core)
{
foreach (MaerskData md in this.maerskDataList)
{
if ((md.MessageCore != null) && (md.MessageCore.Id == core.Id))
{
md.MessageCore = core;
md.Status = MaerskData.MDStatus.ID;
md.ColM = core.VisitId;
await DBManagerAsync.Save(md);
_dbWatchDog.UnRegister(core);
this.Dispatcher.Invoke(() =>
{
this.dataGridPOCores.Items.Refresh();
if(_dbWatchDog.Idle)
busyControl.BusyState = Util.UIHelper.BusyStateEnum.NEUTRAL;
});
}
}
}
}
private void _dbWatchDog_DatabaseEntityChanged(DatabaseEntity entity)
{
if (entity is MessageCore core)
System.Diagnostics.Trace.WriteLine($"Core state changed to {core.BSMDStatusInternal}");
}
#endregion
#region Properties
/// <summary>
/// Locode of the port that is concerned by this import list. Is to be set in the surrounding container:
/// </summary>
public string PortLocode { get; set; }
#endregion
#region events
public event MessageCore.MessageCoreSelectedHandler MessageCoreSelected;
#endregion
#region control event handler
private void POList_Loaded(object sender, RoutedEventArgs e)
{
dataGridPOCores.ItemsSource = maerskDataList;
}
private async void dataGridPOCores_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
if (e.EditAction == DataGridEditAction.Commit)
{
if (e.Column == gridColumnRemark)
{
MaerskData maerskData = this.maerskDataList[e.Row.GetIndex()];
var el = e.EditingElement as System.Windows.Controls.TextBox;
if (el.Text.Length > 100)
{
el.Text = el.Text.Trim().Substring(0, 100);
}
maerskData.Remark = el.Text;
if (maerskData.MessageCore != null)
await DBManagerAsync.Save(maerskData);
}
/*
if(e.Column == gridColumnGroup)
{
var el = e.EditingElement as ComboBox;
DictionaryEntry selectedItem = (DictionaryEntry) el.SelectedItem;
MessageCore.CoreFlags coreFlag = (MessageCore.CoreFlags) Enum.Parse(typeof(MessageCore.CoreFlags), selectedItem.Value.ToString());
MessageCore editedCore = this.filteredResult[e.Row.GetIndex()];
// clear all first
editedCore.SetFlag(false, MessageCore.CoreFlags.MAERSK_BHV);
editedCore.SetFlag(false, MessageCore.CoreFlags.SEAGO_BHV);
editedCore.SetFlag(false, MessageCore.CoreFlags.SEAGO_WHV);
editedCore.SetFlag(false, MessageCore.CoreFlags.HOEGH);
if (coreFlag != MessageCore.CoreFlags.NONE)
{
editedCore.SetFlag(true, coreFlag);
}
// buttonSaveChanges.IsEnabled = true;
editedCore.IsDirty = true;
}
if(e.Column == gridColumnATA)
{
var el = e.EditingElement as System.Windows.Controls.TextBox;
if(DateTime.TryParse(el.Text, out DateTime localATA))
{
MessageCore editedCore = this.filteredResult[e.Row.GetIndex()];
editedCore.ATA = DateTime.SpecifyKind(localATA, DateTimeKind.Local).ToUniversalTime();
// buttonSaveChanges.IsEnabled = true;
editedCore.IsDirty = true;
}
else
{
el.Text = string.Empty;
e.Cancel = true;
}
}
*/
}
}
#endregion
#region private methods
/// <summary>
/// "Status" of a Maersk Data element is a momentary evaluation that is relevant for display purposes only
/// It is not saved
/// </summary>
private void UpdateStatus(MaerskData md)
{
// here we set the following: NO_ID, NO_ID_AND_DUE, ID, NO_ETA and DONE
// the other status are set during active processing
if(md.ETA.HasValue)
{
if((md.ETA.Value - DateTime.Now).TotalSeconds > 0) // future
{
if(!md.ColM.IsNullOrEmpty() || ((md.MessageCore != null) && (!md.MessageCore.VisitId.IsNullOrEmpty())))
{
md.Status = MaerskData.MDStatus.ID;
}
else
{
if(md.ETA.Value.IsNextXDays(3))
{
md.Status = MaerskData.MDStatus.NO_ID_AND_DUE;
}
else
{
md.Status = MaerskData.MDStatus.NO_ID;
}
}
}
else // past
{
md.Status = MaerskData.MDStatus.DONE;
}
}
else
{
// no ETA means done
md.Status = MaerskData.MDStatus.NO_ETA;
}
}
private async void PerformSearch()
{
busyControl.BusyState = Util.UIHelper.BusyStateEnum.BUSY;
Dictionary<MessageCore.SearchFilterType, string> filterDict = new Dictionary<MessageCore.SearchFilterType, string>();
// Die Suche findet in dem eingestellten Intervall statt
uint from = this.dateTimePickerFrom.Value.Value.ToUniversalTime().ToUnixTimeStamp();
uint to = this.dateTimePickerTo.Value.Value.ToUniversalTime().ToUnixTimeStamp();
filterDict.Add(MessageCore.SearchFilterType.FILTER_ETA, string.Format("{0}:{1}", from.ToString() ?? "", to.ToString() ?? ""));
// eingeschränkt auf flags
filterDict.Add(MessageCore.SearchFilterType.FILTER_FLAG_EQ, "0");
// suche auslösen
List<MessageCore> searchResult = DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).GetMessageCoresWithFilters(filterDict);
// alle anderen Häfen weg
searchResult.RemoveAll(item => (item.PoC == null) || (!item.PoC.Equals(PortLocode)));
// alles entfernen was keine Maersk Xtra-Data hat (=noch nicht schon einmal importiert wurde)
foreach(MessageCore core in searchResult)
{
MaerskData md = await DBManagerAsync.LoadMaerskDataForCoreAsync(core.Id.Value);
if(md != null)
{
md.MessageCore = core;
md.MessageCoreId = core.Id.Value;
this.UpdateStatus(md);
if(!maerskDataList.Contains(md)) // DatabaseEntity implements IEquatable
this.maerskDataList.Add(md);
}
}
this.SortItemSource();
this.dataGridPOCores.SelectedItem = null;
busyControl.BusyState = Util.UIHelper.BusyStateEnum.NEUTRAL;
}
private string ReadFieldAsString(IExcelDataReader reader, int fieldNum)
{
if (fieldNum >= reader.FieldCount) return null;
if (reader.GetFieldType(fieldNum) == typeof(string))
return reader.GetString(fieldNum).Clean();
if (reader.GetFieldType(fieldNum) == typeof(DateTime))
return reader.GetDateTime(fieldNum).ToString();
if (reader.GetFieldType(fieldNum) == typeof(int))
return reader.GetInt32(fieldNum).ToString();
if (reader.GetFieldType(fieldNum) == typeof(double))
return ((int) reader.GetDouble(fieldNum)).ToString();
Type theType = reader.GetFieldType(fieldNum);
return null;
}
private void SortItemSource()
{
ObservableCollection<MaerskData> temp;
temp = new ObservableCollection<MaerskData>(this.maerskDataList.OrderBy(p => p.ColA));
this.maerskDataList.Clear();
foreach (MaerskData md in temp)
this.maerskDataList.Add(md);
}
#endregion
#region button event handler
private void buttonLoad_Click(object sender, RoutedEventArgs e)
{
Util.UIHelper.SetBusyState();
this.PerformSearch();
}
private async void buttonImport_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog
{
Filter = "Excel Files|*.xls;*.xlsx"
};
if (ofd.ShowDialog() ?? false)
{
FileStream stream;
try
{
stream = File.Open(ofd.FileName, FileMode.Open, FileAccess.Read, FileShare.Read);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
using (IExcelDataReader reader = ExcelReaderFactory.CreateReader(stream))
{
List<MaerskData> importData = new List<MaerskData>();
uint emptyRowCnt = 0;
bool isFirstRow = true;
try
{
while (reader.Read())
{
if (isFirstRow)
{
isFirstRow = false; // this must be a header row, skip
continue;
}
if (reader.FieldCount < 13)
{
throw new InvalidDataException("Sheet must have 13 columns of data");
}
MaerskData md = new MaerskData();
if (!reader.IsDBNull(0))
{
if (reader.GetFieldType(0) == typeof(DateTime))
{
md.ETA = reader.GetDateTime(0);
md.ColA = md.ETA.ToString();
}
else
{
md.ColA = reader.GetString(0);
if (DateTime.TryParse(md.ColA, out DateTime aDateTime))
md.ETA = aDateTime;
}
}
if (!reader.IsDBNull(1)) md.ColB = ReadFieldAsString(reader, 1);
if (!reader.IsDBNull(2)) md.ColC = ReadFieldAsString(reader, 2);
if (!reader.IsDBNull(3)) md.ColD = ReadFieldAsString(reader, 3);
if (!reader.IsDBNull(4)) md.ColE = ReadFieldAsString(reader, 4);
if (!reader.IsDBNull(5)) md.ColF = ReadFieldAsString(reader, 5);
if (!reader.IsDBNull(6)) md.ColG = ReadFieldAsString(reader, 6);
if (!reader.IsDBNull(7)) md.ColH = ReadFieldAsString(reader, 7);
if (!reader.IsDBNull(8)) md.ColI = ReadFieldAsString(reader, 8);
if (md.ColI != null)
{
if ((md.ColI.Contains("bremerhaven", StringComparison.OrdinalIgnoreCase) && this.PortLocode.Equals("DEWVN")) ||
(md.ColI.Contains("eurogate", StringComparison.OrdinalIgnoreCase) && this.PortLocode.Equals("DEBRV")))
throw new InvalidOperationException($"{md.ColI} found in import to {PortLocode}, this is probably an error. Aborting import");
}
if (!reader.IsDBNull(9)) md.ColJ = ReadFieldAsString(reader, 9);
if (!reader.IsDBNull(10)) md.ColK = ReadFieldAsString(reader, 10);
if (!reader.IsDBNull(11)) md.ColL = ReadFieldAsString(reader, 11);
if (!reader.IsDBNull(12)) md.ColM = ReadFieldAsString(reader, 12);
if (!reader.IsDBNull(13)) md.Remark = ReadFieldAsString(reader, 13);
if (!md.ColF.IsNullOrEmpty()) // only add this if IMO is set
importData.Add(md);
else
emptyRowCnt++;
if (emptyRowCnt > MAX_EMPTY_ROWS_ON_IMPORT) break;
if (isFirstRow) isFirstRow = false;
}
}
catch (Exception ex)
{
MessageBox.Show("Error reading Excel: " + ex.Message, Properties.Resources.textCaptionError, MessageBoxButton.OK, MessageBoxImage.Error);
}
if (importData.Count > 0)
{
busyControl.BusyState = Util.UIHelper.BusyStateEnum.BUSY;
foreach (MaerskData md in importData)
{
if (this.maerskDataList.Contains(md))
{
// update record with imported record
MaerskData foundData = this.maerskDataList.First((m) => (m.ColF != null) ? m.ColF.Equals(md.ColF) : (md.ColF == null) &&
(m.ColG != null) ? m.ColG.Equals(md.ColG) : (md.ColG == null) &&
(m.ColH != null) ? m.ColH.Equals(md.ColH) : (md.ColH == null));
if(foundData.ETA.HasValue && ((foundData.ETA.Value - DateTime.Now).TotalSeconds > 0) && foundData.Update(md))
foundData.Status = MaerskData.MDStatus.UPDATED;
}
else
{
if (!md.ColM.IsNullOrEmpty())
{
md.MessageCore = await DBManagerAsync.LoadCoreByVisitIdAsync(md.ColM);
}
this.UpdateStatus(md);
maerskDataList.Add(md);
}
}
this.SortItemSource();
busyControl.BusyState = Util.UIHelper.BusyStateEnum.NEUTRAL;
}
this.dataGridPOCores.Items.Refresh();
}
stream.Close();
}
}
private void buttonExport_Click(object sender, RoutedEventArgs e)
{
// Export of the current collection
string predefFilename = string.Format("{0}_{1}.xlsx", this.PortLocode, DateTime.Today.ToString("yyyyMMdd"));
SaveFileDialog sfd = new SaveFileDialog()
{
Filter = "Excel Files|*.xlsx;*.xls",
FileName = predefFilename
};
if(sfd.ShowDialog() ?? false)
{
Util.UIHelper.SetBusyState();
ExcelManager em = new ExcelManager();
try
{
List<MaerskData> exportList = new List<MaerskData>(this.maerskDataList);
em.ExportMaersk(exportList, sfd.FileName);
Process.Start(sfd.FileName);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Export failed", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
/*
private async void buttonSave_Click(object sender, RoutedEventArgs e)
{
busyControl.BusyState = Util.UIHelper.BusyStateEnum.BUSY;
// save the current list to DB (only if the entries have matching cores!)
foreach(MaerskData md in this.maerskDataList)
{
if(md.MessageCore != null)
{
await DBManagerAsync.Save(md);
}
}
busyControl.BusyState = Util.UIHelper.BusyStateEnum.NEUTRAL;
}
*/
private async void buttonRequestIds_Click(object sender, RoutedEventArgs e)
{
// find all entries from now until 3 days into the future and track parallel requests
List<MaerskData> requestList = new List<MaerskData>();
if (sender == this.buttonRequestIds)
{
foreach (MaerskData md in this.dataGridPOCores.SelectedItems)
{
if (md.MessageCore?.VisitId.IsNullOrEmpty() == false) continue; // already requested
if (md.ColM?.IsNullOrEmpty() == false) continue; // already something there
requestList.Add(md);
}
}
if(sender == this.buttonRequestPlus3)
{
foreach(MaerskData md in this.maerskDataList)
{
if(((md.MessageCore == null) && md.ColM.IsNullOrEmpty()) ||
((md.MessageCore != null) && md.MessageCore.VisitId.IsNullOrEmpty()))
{
if (md.ETA.HasValue && md.ETA.Value.IsNextXDays(3))
requestList.Add(md);
}
}
}
if(requestList.Count == 0)
{
MessageBox.Show("No valid rows selected", "Warning", MessageBoxButton.OK, MessageBoxImage.Exclamation);
}
else
{
busyControl.BusyState = Util.UIHelper.BusyStateEnum.BUSY;
foreach (MaerskData md in requestList)
{
md.Status = MaerskData.MDStatus.REQUESTING_ID;
// create MessageCore and message classes
md.MessageCore = new MessageCore();
md.MessageCore.InitialHIS = Message.NSWProvider.DBH_MAERSK;
md.MessageCore.IMO = md.ColF;
md.MessageCore.ETA = md.ETA;
md.MessageCore.IsTransit = false;
md.MessageCore.PoC = this.PortLocode;
md.MessageCore.Portname = LocodeDB.PortNameFromLocode(md.MessageCore.PoC);
md.MessageCore.BSMDStatusInternal = MessageCore.BSMDStatus.TOSEND;
md.MessageCore.Incoming = true;
md.MessageCore.DefaultReportingPartyId = App.UserId.Value;
await DBManagerAsync.Save(md.MessageCore);
md.MessageCoreId = md.MessageCore.Id.Value;
await DBManagerAsync.Save(md);
// Meldeklassen für neuen Anlauf erzeugen
// TODO: pre-set certain fields taken from Maersk data
await bsmd.database.Util.CreateMessagesForCoreAsync(md.MessageCore, null);
// watchdog registrieren
this._dbWatchDog.Register(md.MessageCore);
}
}
this.dataGridPOCores.Items.Refresh();
}
#endregion
#region grid event handler
private void dataGridPOCores_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (sender != null)
{
DataGrid grid = sender as DataGrid;
if (grid?.SelectedItems?.Count == 1)
{
MaerskData md = grid.SelectedItem as MaerskData;
if(md.MessageCore != null) {
Util.UIHelper.SetBusyState();
this.MessageCoreSelected?.Invoke(md.MessageCore);
}
}
}
}
#endregion
}
}