|
@@ -1,11 +1,14 @@
|
|
|
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;
|
|
@@ -20,6 +23,7 @@ using System.Windows.Media;
|
|
|
using System.Windows.Media.Imaging;
|
|
|
using System.Windows.Navigation;
|
|
|
using System.Windows.Shapes;
|
|
|
+using DataColumn = System.Data.DataColumn;
|
|
|
using DataRow = System.Data.DataRow;
|
|
|
|
|
|
namespace PRSDesktop.Dashboards.Manufacturing
|
|
@@ -62,6 +66,7 @@ namespace PRSDesktop.Dashboards.Manufacturing
|
|
|
|
|
|
public class JobManufacturingSummaryProperties : IDashboardProperties
|
|
|
{
|
|
|
+ public Guid JobStatus;
|
|
|
}
|
|
|
|
|
|
public class JobManufacturingSummaryElement : DashboardElement<JobManufacturingSummary, WidgetGroups.Manufacturing, JobManufacturingSummaryProperties>
|
|
@@ -71,7 +76,9 @@ namespace PRSDesktop.Dashboards.Manufacturing
|
|
|
/// <summary>
|
|
|
/// Interaction logic for JobManufacturingSummary.xaml
|
|
|
/// </summary>
|
|
|
- public partial class JobManufacturingSummary : UserControl, IDashboardWidget<WidgetGroups.Manufacturing, JobManufacturingSummaryProperties>
|
|
|
+ public partial class JobManufacturingSummary : UserControl, IDashboardWidget<WidgetGroups.Manufacturing, JobManufacturingSummaryProperties>,
|
|
|
+ IHeaderDashboard,
|
|
|
+ INotifyPropertyChanged
|
|
|
{
|
|
|
private class SectionColumn
|
|
|
{
|
|
@@ -80,15 +87,39 @@ namespace PRSDesktop.Dashboards.Manufacturing
|
|
|
|
|
|
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 List<Job> Jobs { get; set; } = 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();
|
|
@@ -96,11 +127,13 @@ namespace PRSDesktop.Dashboards.Manufacturing
|
|
|
|
|
|
public JobManufacturingSummaryProperties Properties { get; set; }
|
|
|
|
|
|
+ public DashboardHeader Header { get; } = new();
|
|
|
+
|
|
|
public void Setup()
|
|
|
{
|
|
|
Jobs = new Client<Job>()
|
|
|
.Query(
|
|
|
- LookupFactory.DefineFilter<Job>(),
|
|
|
+ new Filter<Job>().None(),
|
|
|
new Columns<Job>(x => x.ID)
|
|
|
.Add(x => x.JobNumber)
|
|
|
.Add(x => x.Name)
|
|
@@ -121,9 +154,59 @@ namespace PRSDesktop.Dashboards.Manufacturing
|
|
|
.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;
|
|
|
+
|
|
|
+ Header.Add(JobStatusBox);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void RefreshJobs()
|
|
|
+ {
|
|
|
+ Jobs = new Client<Job>()
|
|
|
+ .Query(
|
|
|
+ new Filter<Job>(x => x.JobStatus.ID).IsEqualTo(Properties.JobStatus),
|
|
|
+ new Columns<Job>(x => x.ID)
|
|
|
+ .Add(x => x.JobNumber)
|
|
|
+ .Add(x => x.Name)
|
|
|
+ .Add(x => x.Color))
|
|
|
+ .ToList<Job>();
|
|
|
+
|
|
|
+ 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;
|
|
@@ -133,30 +216,55 @@ namespace PRSDesktop.Dashboards.Manufacturing
|
|
|
Data.Columns.Add("Job", typeof(object));
|
|
|
|
|
|
var stackedHeaderRow = new StackedHeaderRow();
|
|
|
+ var stackedDetailRow = new StackedHeaderRow();
|
|
|
var factoryIdx = 0;
|
|
|
foreach (var factory in Factories)
|
|
|
{
|
|
|
- if (!Sections.TryGetValue(factory.ID, out var sections)) continue;
|
|
|
+ var columns = new List<string>();
|
|
|
|
|
|
- foreach (var section in sections)
|
|
|
+ var toBeIssued = factory.ID.ToString();
|
|
|
+ Data.Columns.Add(toBeIssued, typeof(object));
|
|
|
+ columns.Add(toBeIssued);
|
|
|
+ FactoryColumns.Add(factory.ID, new SectionColumn
|
|
|
{
|
|
|
- var columnName = section.ID.ToString();
|
|
|
- SectionColumns.Add(section.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)
|
|
|
{
|
|
|
- ColumnName = columnName,
|
|
|
- DisplayName = section.Name,
|
|
|
- Background = factoryIdx % 2 == 0 ? new SolidColorBrush(Colors.WhiteSmoke) : new SolidColorBrush(Colors.LightGray)
|
|
|
- });
|
|
|
- Data.Columns.Add(columnName, typeof(object));
|
|
|
+ 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(',', sections.Select(x => x.ID)),
|
|
|
+ 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)
|
|
@@ -167,21 +275,12 @@ namespace PRSDesktop.Dashboards.Manufacturing
|
|
|
element.SetValue(Grid.ColumnSpanProperty, columnSpan);
|
|
|
}
|
|
|
|
|
|
- public void Refresh()
|
|
|
+ private void LoadPackets(
|
|
|
+ IEnumerable<IGrouping<Guid, ManufacturingPacket>> packets,
|
|
|
+ Dictionary<Guid, SectionColumn> columns,
|
|
|
+ Dictionary<Guid, DataRow> jobRows)
|
|
|
{
|
|
|
- Data.Rows.Clear();
|
|
|
-
|
|
|
- var packets = new Client<ManufacturingPacket>()
|
|
|
- .Query(
|
|
|
- new Filter<ManufacturingPacket>(x => x.Archived).IsEqualTo(DateTime.MinValue)
|
|
|
- .And(x => x.Completed).IsEqualTo(DateTime.MinValue)
|
|
|
- .And(x => x.SetoutLink.JobLink.ID).InList(Jobs.Select(x => x.ID).ToArray()),
|
|
|
- new Columns<ManufacturingPacket>(x => x.ID)
|
|
|
- .Add(x => x.SetoutLink.JobLink.ID)
|
|
|
- .Add(x => x.StageLink.SectionID)
|
|
|
- .Add(x => x.TimeRemaining))
|
|
|
- .ToObjects<ManufacturingPacket>()
|
|
|
- .GroupBy(x => x.StageLink.SectionID)
|
|
|
+ var groupPackets = packets
|
|
|
.ToDictionary(
|
|
|
x => x.Key,
|
|
|
x => x.GroupBy(x => x.SetoutLink.JobLink.ID)
|
|
@@ -191,7 +290,28 @@ namespace PRSDesktop.Dashboards.Manufacturing
|
|
|
++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()
|
|
|
+ {
|
|
|
+ Data.Rows.Clear();
|
|
|
var rows = new Dictionary<Guid, DataRow>();
|
|
|
foreach (var job in Jobs)
|
|
|
{
|
|
@@ -201,23 +321,73 @@ namespace PRSDesktop.Dashboards.Manufacturing
|
|
|
rows[job.ID] = row;
|
|
|
}
|
|
|
|
|
|
- foreach (var (sectionID, column) in SectionColumns)
|
|
|
+ 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()),
|
|
|
+ new Columns<ManufacturingPacket>(x => x.ID)
|
|
|
+ .Add(x => x.SetoutLink.JobLink.ID)
|
|
|
+ .Add(x => x.StageLink.SectionID)
|
|
|
+ .Add(x => x.TimeRemaining))
|
|
|
+ .ToObjects<ManufacturingPacket>()
|
|
|
+ .GroupBy(x => x.StageLink.SectionID),
|
|
|
+ SectionColumns,
|
|
|
+ rows);
|
|
|
+ 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()),
|
|
|
+ new Columns<ManufacturingPacket>(x => x.ID)
|
|
|
+ .Add(x => x.SetoutLink.JobLink.ID)
|
|
|
+ .Add(x => x.TimeRemaining)
|
|
|
+ .Add(x => x.ManufacturingTemplateLink.Factory.ID))
|
|
|
+ .ToObjects<ManufacturingPacket>()
|
|
|
+ .GroupBy(x => x.ManufacturingTemplateLink.Factory.ID),
|
|
|
+ FactoryColumns,
|
|
|
+ rows);
|
|
|
+
|
|
|
+ var totals = new Dictionary<Guid, ManufacturingTotal>();
|
|
|
+ foreach(var column in Data.Columns)
|
|
|
{
|
|
|
- var sectionPackets = packets.GetValueOrDefault(sectionID);
|
|
|
- foreach (var job in Jobs)
|
|
|
+ if (column is not DataColumn dataColumn) continue;
|
|
|
+
|
|
|
+ Guid factoryID;
|
|
|
+ var sectionColumn = SectionColumns.FirstOrDefault(x => x.Value.ColumnName == dataColumn.ColumnName);
|
|
|
+ if(sectionColumn.Key != Guid.Empty)
|
|
|
{
|
|
|
- if (!rows.TryGetValue(job.ID, out var row))
|
|
|
- {
|
|
|
- continue;
|
|
|
- }
|
|
|
+ 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 (sectionPackets is null || !sectionPackets.TryGetValue(job.ID, out var model))
|
|
|
- {
|
|
|
- model = new CardModel();
|
|
|
- }
|
|
|
- row[column.ColumnName] = model;
|
|
|
+ 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];
|
|
|
+ header.HeaderText = $"Packets: {total.NPackets}, Hours: {total.NHours:f2}";
|
|
|
+ }
|
|
|
|
|
|
DataGrid.ItemsSource = Data;
|
|
|
}
|
|
@@ -230,19 +400,21 @@ namespace PRSDesktop.Dashboards.Manufacturing
|
|
|
{
|
|
|
if(Guid.TryParse(e.Column.MappingName, out var id))
|
|
|
{
|
|
|
- var column = SectionColumns[id];
|
|
|
- var style = new Style();
|
|
|
- style.Setters.Add(new Setter(GridCell.BackgroundProperty, column.Background));
|
|
|
-
|
|
|
- e.Column = new GridTemplateColumn
|
|
|
+ if(SectionColumns.TryGetValue(id, out var column) || FactoryColumns.TryGetValue(id, out column))
|
|
|
{
|
|
|
- HeaderText = column.DisplayName,
|
|
|
- CellStyle = style,
|
|
|
- MappingName = e.Column.MappingName,
|
|
|
- CellTemplate = Resources["CardTemplate"] as DataTemplate,
|
|
|
- SetCellBoundValue = true,
|
|
|
- ColumnSizer = GridLengthUnitType.Auto
|
|
|
- };
|
|
|
+ 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
|
|
|
{
|
|
@@ -265,5 +437,10 @@ namespace PRSDesktop.Dashboards.Manufacturing
|
|
|
e.Handled = true;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ private void OnPropertyChanged([CallerMemberName] string name = "")
|
|
|
+ {
|
|
|
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
|
|
|
+ }
|
|
|
}
|
|
|
}
|