// 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 { /// /// Interaction logic for MaerskListControl.xaml /// public partial class MaerskListControl : UserControl { #region Fields private readonly ObservableCollection maerskDataList = new ObservableCollection(); 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 /// /// Locode of the port that is concerned by this import list. Is to be set in the surrounding container: /// 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 /// /// "Status" of a Maersk Data element is a momentary evaluation that is relevant for display purposes only /// It is not saved /// 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 filterDict = new Dictionary(); // 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 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 temp; temp = new ObservableCollection(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 importData = new List(); 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 exportList = new List(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 requestList = new List(); 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 } }