| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583 |
- using Comal.Classes;
- using InABox.Clients;
- using InABox.Core;
- using InABox.WPF;
- using javax.smartcardio;
- using NPOI.SS.Formula.Functions;
- using PRSDesktop.WidgetGroups;
- using Syncfusion.UI.Xaml.Grid;
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Data;
- using System.Linq;
- using System.Runtime.CompilerServices;
- using System.Text;
- using System.Threading.Tasks;
- using System.Windows;
- using System.Windows.Controls;
- using System.Windows.Data;
- using System.Windows.Documents;
- using System.Windows.Input;
- using System.Windows.Media;
- using System.Windows.Media.Imaging;
- using System.Windows.Navigation;
- using System.Windows.Shapes;
- using InABox.Configuration;
- using DataColumn = System.Data.DataColumn;
- using DataRow = System.Data.DataRow;
- using Columns = InABox.Core.Columns;
- namespace PRSDesktop.Dashboards.Manufacturing
- {
- public class HeaderModel
- {
- public string Name { get; set; }
- public HeaderModel(string name)
- {
- Name = name;
- }
- }
- public class JobModel
- {
- public string JobNumber { get; set; }
- public string Name { get; set; }
- public Guid ID { get; set; }
- public double NHours { get; set; }
- public int NPackets { get; set; }
- public Visibility HoursVisibility;
- public JobModel(string jobNumber, string name, Guid ID, double nHours, int nPackets)
- {
- JobNumber = jobNumber;
- Name = name;
- this.ID = ID;
- NHours = nHours;
- NPackets = nPackets;
- }
- }
- public class CardModel
- {
- private bool _empty;
- public int NPackets { get; set; }
- public double NHours { get; set; }
- public bool Empty
- {
- get => _empty || NPackets == 0;
- set => _empty = value;
- }
- public Visibility HoursVisibility;
- public CardModel(int nPackets, int nHours)
- {
- NPackets = nPackets;
- NHours = nHours;
- Empty = false;
- }
- public CardModel()
- {
- NPackets = 0;
- NHours = 0.0;
- Empty = true;
- }
- }
- public class JobManufacturingSummaryProperties : IUserConfigurationSettings, IDashboardProperties
- {
- public Guid JobStatus;
- public bool ShowEmptyJobs;
- public bool IncludeTimeRemaining;
- }
- public class JobManufacturingSummaryElement : DashboardElement<JobManufacturingSummary, WidgetGroups.Manufacturing, JobManufacturingSummaryProperties>
- {
- }
- /// <summary>
- /// Interaction logic for JobManufacturingSummary.xaml
- /// </summary>
- public partial class JobManufacturingSummary : UserControl, IDashboardWidget<WidgetGroups.Manufacturing, JobManufacturingSummaryProperties>,
- IHeaderDashboard,
- INotifyPropertyChanged
- {
- private class SectionColumn
- {
- public string ColumnName { get; set; }
- public string DisplayName { get; set; }
- public SolidColorBrush Background { get; set; }
- }
- private class ManufacturingTotal
- {
- public int NPackets { get; set; } = 0;
- public double NHours { get; set; } = 0;
- }
- private readonly Dictionary<Guid, SectionColumn> SectionColumns = new();
- private readonly Dictionary<Guid, SectionColumn> FactoryColumns = new();
- public event PropertyChangedEventHandler? PropertyChanged;
- private List<Job> _jobs = new();
- private List<Job> Jobs
- {
- get => _jobs;
- set
- {
- _jobs = value;
- OnPropertyChanged(nameof(NumberOfJobs));
- }
- }
- private List<ManufacturingFactory> Factories { get; set; } = new();
- private Dictionary<Guid, List<ManufacturingSection>> Sections { get; set; } = new();
- private List<FrameworkElement> Cards { get; set; } = new();
- private DataTable Data { get; set; }
- private Dictionary<Guid, StackedColumn> FactoryColumnHeaders = new();
- public int NumberOfJobs => Jobs.Count;
- public JobManufacturingSummary()
- {
- InitializeComponent();
- }
- public JobManufacturingSummaryProperties Properties { get; set; }
- public event LoadSettings<JobManufacturingSummaryProperties>? LoadSettings;
- public event SaveSettings<JobManufacturingSummaryProperties>? SaveSettings;
- public DashboardHeader Header { get; } = new();
- public void Setup()
- {
- Jobs = new Client<Job>()
- .Query(
- new Filter<Job>().None(),
- Columns.None<Job>().Add(x => x.ID)
- .Add(x => x.JobNumber)
- .Add(x => x.Name)
- .Add(x => x.Color))
- .ToObjects<Job>().ToList();
- Factories = new Client<ManufacturingFactory>()
- .Query(null, Columns.None<ManufacturingFactory>().Add(x => x.ID).Add(x => x.Name), new SortOrder<ManufacturingFactory>(x => x.Sequence))
- .ToObjects<ManufacturingFactory>().ToList();
- Sections = new Client<ManufacturingSection>()
- .Query(
- new Filter<ManufacturingSection>(x => x.Hidden).IsEqualTo(false),
- Columns.None<ManufacturingSection>().Add(x => x.ID)
- .Add(x => x.Factory.ID)
- .Add(x => x.Name),
- new SortOrder<ManufacturingSection>(x => x.Sequence))
- .ToObjects<ManufacturingSection>()
- .GroupBy(x => x.Factory.ID).ToDictionary(x => x.Key, x => x.ToList());
- SetupHeader();
- SetupGrid();
- RefreshJobs();
- }
- #region Header
- private ComboBox JobStatusBox;
- private JobStatus? SelectedStatus => JobStatusBox.SelectedValue as JobStatus;
- private void SetupHeader()
- {
- JobStatusBox = new ComboBox
- {
- Padding = new Thickness(5)
- };
- var statuses = new Client<JobStatus>().Load();
- JobStatusBox.ItemsSource = statuses.Select(x => new Tuple<string, JobStatus>($"{x.Code}: {x.Description}", x));
- JobStatusBox.DisplayMemberPath = "Item1";
- JobStatusBox.SelectedValuePath = "Item2";
- JobStatusBox.SelectedValue = (Properties.JobStatus != Guid.Empty
- ? statuses.FirstOrDefault(x => x.ID == Properties.JobStatus)
- : null) ?? statuses.FirstOrDefault(x => x.Default) ?? statuses.FirstOrDefault();
- JobStatusBox.SelectionChanged += StatusBox_SelectionChanged;
- var showEmptyJobsLabel = new Label { Content = "Show Empty Jobs?" };
- var emptyJobsCheckBox = new CheckBox
- {
- IsChecked = Properties.ShowEmptyJobs,
- VerticalAlignment = VerticalAlignment.Center
- };
- emptyJobsCheckBox.Checked += EmptyJobsCheckBox_Checked;
- emptyJobsCheckBox.Unchecked += EmptyJobsCheckBox_Checked;
- var includeRemainingLabel = new Label { Content = "Include Remaining Time?" };
- var includeRemainingCheckBox = new CheckBox
- {
- IsChecked = Properties.IncludeTimeRemaining,
- VerticalAlignment = VerticalAlignment.Center
- };
- includeRemainingCheckBox.Checked += IncludeRemainingCheckBox_Checked;
- includeRemainingCheckBox.Unchecked += IncludeRemainingCheckBox_Checked;
- Header.Add(JobStatusBox)
- .Add(showEmptyJobsLabel)
- .Add(emptyJobsCheckBox)
- .Add(includeRemainingLabel)
- .Add(includeRemainingCheckBox);
- }
- private void IncludeRemainingCheckBox_Checked(object sender, RoutedEventArgs e)
- {
- if (sender is not CheckBox checkBox) return;
- Properties.IncludeTimeRemaining = checkBox.IsChecked == true;
- Refresh();
- }
- private void EmptyJobsCheckBox_Checked(object sender, RoutedEventArgs e)
- {
- if (sender is not CheckBox checkBox) return;
- Properties.ShowEmptyJobs = checkBox.IsChecked == true;
- Refresh();
- }
- private void RefreshJobs()
- {
- Jobs = new Client<Job>()
- .Query(
- new Filter<Job>(x => x.JobStatus.ID).IsEqualTo(Properties.JobStatus),
- Columns.None<Job>().Add(x => x.ID)
- .Add(x => x.JobNumber)
- .Add(x => x.Name)
- .Add(x => x.Color))
- .ToObjects<Job>().ToList();
- Refresh();
- }
- private void StatusBox_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
- {
- Properties.JobStatus = SelectedStatus?.ID ?? Guid.Empty;
- RefreshJobs();
- }
- #endregion
- private void SetupGrid()
- {
- DataGrid.HeaderTemplate = Resources["HeaderTemplate"] as DataTemplate;
- Data = new DataTable();
- Data.Columns.Add("Job", typeof(object));
- var stackedHeaderRow = new StackedHeaderRow();
- var stackedDetailRow = new StackedHeaderRow();
- var factoryIdx = 0;
- foreach (var factory in Factories)
- {
- var columns = new List<string>();
- var toBeIssued = factory.ID.ToString();
- Data.Columns.Add(toBeIssued, typeof(object));
- columns.Add(toBeIssued);
- FactoryColumns.Add(factory.ID, new SectionColumn
- {
- ColumnName = toBeIssued,
- DisplayName = "To Be Issued",
- Background = factoryIdx % 2 == 0 ? new SolidColorBrush(Colors.WhiteSmoke) : new SolidColorBrush(Colors.LightGray)
- });
- if (Sections.TryGetValue(factory.ID, out var sections))
- {
- foreach (var section in sections)
- {
- var columnName = section.ID.ToString();
- SectionColumns.Add(section.ID, new SectionColumn
- {
- ColumnName = columnName,
- DisplayName = section.Name,
- Background = factoryIdx % 2 == 0 ? new SolidColorBrush(Colors.WhiteSmoke) : new SolidColorBrush(Colors.LightGray)
- });
- Data.Columns.Add(columnName, typeof(object));
- columns.Add(columnName);
- }
- }
- var column = new StackedColumn
- {
- ChildColumns = string.Join(',', columns),
- HeaderText = ""
- };
- FactoryColumnHeaders.Add(factory.ID, column);
- stackedDetailRow.StackedColumns.Add(column);
- stackedHeaderRow.StackedColumns.Add(new StackedColumn
- {
- ChildColumns = string.Join(',', columns),
- HeaderText = factory.Name
- });
- ++factoryIdx;
- }
- DataGrid.StackedHeaderRows.Add(stackedHeaderRow);
- DataGrid.StackedHeaderRows.Add(stackedDetailRow);
- }
- private void SetPosition(FrameworkElement element, int row, int rowSpan, int column, int columnSpan)
- {
- element.SetValue(Grid.RowProperty, row);
- element.SetValue(Grid.RowSpanProperty, rowSpan);
- element.SetValue(Grid.ColumnProperty, column);
- element.SetValue(Grid.ColumnSpanProperty, columnSpan);
- }
- private void LoadPackets(
- IEnumerable<IGrouping<Guid, ManufacturingPacket>> packets,
- Dictionary<Guid, SectionColumn> columns,
- Dictionary<Guid, DataRow> jobRows)
- {
- var groupPackets = packets
- .ToDictionary(
- x => x.Key,
- x => x.GroupBy(x => x.SetoutLink.JobLink.ID)
- .ToDictionary(x => x.Key, x => x.Aggregate(new CardModel(0, 0)
- {
- HoursVisibility = Properties.IncludeTimeRemaining ? Visibility.Visible : Visibility.Collapsed
- }, (c, p) =>
- {
- c.NHours += p.TimeRemaining.TotalHours;
- ++c.NPackets;
- return c;
- })));
- foreach (var (id, column) in columns)
- {
- var packetList = groupPackets.GetValueOrDefault(id);
- foreach (var job in Jobs)
- {
- if (!jobRows.TryGetValue(job.ID, out var row))
- {
- continue;
- }
- if (packetList is null || !packetList.TryGetValue(job.ID, out var model))
- {
- model = new CardModel();
- }
- row[column.ColumnName] = model;
- }
- }
- }
- public void Refresh()
- {
- using var profiler = new Profiler(true);
- Data.Rows.Clear();
- var rows = new Dictionary<Guid, DataRow>();
- foreach (var job in Jobs)
- {
- var row = Data.NewRow();
- Data.Rows.Add(row);
- row["Job"] = job;
- rows[job.ID] = row;
- }
- var sectionColumns = Columns.None<ManufacturingPacket>().Add(x => x.ID)
- .Add(x => x.SetoutLink.JobLink.ID)
- .Add(x => x.StageLink.SectionID);
- var factoryColumns = Columns.None<ManufacturingPacket>().Add(x => x.ID)
- .Add(x => x.SetoutLink.JobLink.ID)
- .Add(x => x.ManufacturingTemplateLink.Factory.ID);
- if (Properties.IncludeTimeRemaining)
- {
- sectionColumns.Add(x => x.TimeRemaining);
- factoryColumns.Add(x => x.TimeRemaining);
- }
- profiler.Log("Setup");
- LoadPackets(
- new Client<ManufacturingPacket>()
- .Query(
- new Filter<ManufacturingPacket>(x => x.Archived).IsEqualTo(DateTime.MinValue)
- .And(x => x.StageLink.SectionID).IsNotEqualTo(Guid.Empty)
- .And(x => x.Completed).IsEqualTo(DateTime.MinValue)
- .And(x => x.SetoutLink.JobLink.ID).InList(Jobs.Select(x => x.ID).ToArray()),
- sectionColumns)
- .ToObjects<ManufacturingPacket>()
- .GroupBy(x => x.StageLink.SectionID),
- SectionColumns,
- rows);
- profiler.Log("Section");
- LoadPackets(
- new Client<ManufacturingPacket>()
- .Query(
- new Filter<ManufacturingPacket>(x => x.Archived).IsEqualTo(DateTime.MinValue)
- .And(x => x.StageLink.SectionID).IsEqualTo(Guid.Empty)
- .And(x => x.Completed).IsEqualTo(DateTime.MinValue)
- .And(x => x.SetoutLink.JobLink.ID).InList(Jobs.Select(x => x.ID).ToArray()),
- factoryColumns)
- .ToObjects<ManufacturingPacket>()
- .GroupBy(x => x.ManufacturingTemplateLink.Factory.ID),
- FactoryColumns,
- rows);
- profiler.Log("Factory");
- var removes = new List<Guid>();
- foreach(var (jobID, row) in rows)
- {
- var any = false;
- var nHours = 0.0;
- var nPackets = 0;
- foreach (var (id, column) in FactoryColumns.Concat(SectionColumns))
- {
- if (row[column.ColumnName] is CardModel model && !model.Empty)
- {
- any = true;
- nHours += model.NHours;
- nPackets += model.NPackets;
- }
- }
- if (!any)
- {
- removes.Add(jobID);
- }
- else
- {
- if(row["Job"] is Job job)
- {
- row["Job"] = new JobModel(job.JobNumber, job.Name, job.ID, nHours, nPackets)
- {
- HoursVisibility = Properties.IncludeTimeRemaining ? Visibility.Visible : Visibility.Collapsed,
- };
- }
- }
- }
- if (!Properties.ShowEmptyJobs)
- {
- foreach(var remove in removes)
- {
- if(rows.Remove(remove, out var row))
- {
- Data.Rows.Remove(row);
- }
- }
- }
- var totals = new Dictionary<Guid, ManufacturingTotal>();
- foreach(var column in Data.Columns)
- {
- if (column is not DataColumn dataColumn) continue;
- Guid factoryID;
- var sectionColumn = SectionColumns.FirstOrDefault(x => x.Value.ColumnName == dataColumn.ColumnName);
- if(sectionColumn.Key != Guid.Empty)
- {
- factoryID = Sections.FirstOrDefault(x => x.Value.Any(x => x.ID == sectionColumn.Key)).Key;
- }
- else
- {
- var factoryColumn = FactoryColumns.FirstOrDefault(x => x.Value.ColumnName == dataColumn.ColumnName);
- factoryID = factoryColumn.Key;
- }
- if (factoryID == Guid.Empty) continue;
- if(!totals.TryGetValue(factoryID, out var total))
- {
- total = new();
- totals[factoryID] = total;
- }
- foreach(var row in rows.Values)
- {
- if (row[dataColumn] is not CardModel model) continue;
- total.NHours += model.NHours;
- total.NPackets += model.NPackets;
- }
- }
- foreach(var (factoryID, total) in totals)
- {
- var header = FactoryColumnHeaders[factoryID];
- if (Properties.IncludeTimeRemaining)
- {
- header.HeaderText = $"Packets: {total.NPackets}, Hours: {total.NHours:f2}";
- }
- else
- {
- header.HeaderText = $"Packets: {total.NPackets}";
- }
- }
- DataGrid.ItemsSource = Data;
- }
- public void Shutdown(CancelEventArgs? cancel)
- {
- }
- private void DataGrid_AutoGeneratingColumn(object sender, Syncfusion.UI.Xaml.Grid.AutoGeneratingColumnArgs e)
- {
- if(Guid.TryParse(e.Column.MappingName, out var id))
- {
- if(SectionColumns.TryGetValue(id, out var column) || FactoryColumns.TryGetValue(id, out column))
- {
- var style = new Style();
- style.Setters.Add(new Setter(GridCell.BackgroundProperty, column.Background));
- e.Column = new GridTemplateColumn
- {
- HeaderText = column.DisplayName,
- CellStyle = style,
- MappingName = e.Column.MappingName,
- CellTemplate = Resources["CardTemplate"] as DataTemplate,
- SetCellBoundValue = true,
- ColumnSizer = GridLengthUnitType.Auto
- };
- }
- }
- else
- {
- e.Column = new GridTemplateColumn
- {
- CellTemplate = Resources["JobHeaderTemplate"] as DataTemplate,
- HeaderText = e.Column.HeaderText,
- MappingName = e.Column.MappingName,
- SetCellBoundValue = true,
- ColumnSizer = GridLengthUnitType.Auto
- };
- }
- }
- private void DataGrid_QueryRowHeight(object sender, QueryRowHeightEventArgs e)
- {
- if(DataGrid.GridColumnSizer.GetAutoRowHeight(e.RowIndex, new GridRowSizingOptions(), out var height))
- {
- e.Height = height;
- e.Handled = true;
- }
- }
- private void OnPropertyChanged([CallerMemberName] string name = "")
- {
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
- }
- }
- }
|