using Comal.Classes; using InABox.Clients; using InABox.Configuration; using InABox.Core; using InABox.DynamicGrid; using InABox.Wpf; using InABox.WPF; using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using InABox.Core.Reports; using InABox.Wpf.Reports; using Microsoft.Xaml.Behaviors.Core; using PRSDimensionUtils; using Syncfusion.Data.Extensions; using Brush = System.Windows.Media.Brush; using Button = System.Windows.Controls.Button; using ContextMenu = System.Windows.Controls.ContextMenu; using MenuItem = System.Windows.Controls.MenuItem; using FluentResults; namespace PRSDesktop; public delegate void JobRequiItemSelect(CoreRow[] rows); public delegate void GridRefresh(); public class ReservationManagementItemGrid : DynamicDataGrid, ISpecificGrid { public Guid? JobID { get; set; } public ReservationManagementUserSettings UserSettings { get; set; } private Button CreateOrder; private Button ArchiveButton; private Button CreatePickingList; private Button ConsolidateHoldings; public bool ShowColors { get; set; } public int DueDateAlert { get; set; } public int DueDateWarning { get; set; } public ProductStyleLink CompanyDefaultStyle { get; set; } private class UIComponent : DynamicGridGridUIComponent { private ReservationManagementItemGrid Grid; public UIComponent(ReservationManagementItemGrid grid) { Grid = grid; Parent = grid; } protected override Brush? GetCellBackground(CoreRow row, DynamicColumnBase column) { if (!Grid.ShowColors) return null; if (column is DynamicGridColumn col) { if (String.Equals(col.ColumnName, CoreUtils.GetFullPropertyName(x => x.Requisition.DueDate, "."))) { var due = row.Get(x => x.Requisition.DueDate); if (!due.IsEmpty()) { var background = DateTime.Today > due.Date ? Colors.LightSalmon : DateTime.Today.AddDays(Grid.DueDateWarning) >= due.Date ? Colors.Orange : DateTime.Today.AddDays(Grid.DueDateAlert) >= due.Date ? Colors.LightYellow : Colors.LightGreen; return new SolidColorBrush(background) { Opacity = 0.5 }; } } } else if(column is DynamicActionColumn dac) { if (dac == Grid.InStockColumn || dac == Grid.OnOrderColumn) { return Colors.LightBlue.ToBrush(0.5); } else if (dac == Grid.TreatmentRequiredColumn || dac == Grid.TreatmentOnOrderColumn) { return Colors.Plum.ToBrush(0.5); } } var qty = row.Get(x => x.Qty); if(row.Get(x => x.Issued) >= qty) { // Everything has been issued. return Colors.Silver.ToBrush(0.5); } else if(row.Get(x => x.Allocated) + row.Get(x => x.Issued) >= qty) { // Everything has been allocated. return Colors.LightGreen.ToBrush(0.5); } else if(row.Get(x => x.InStock) + row.Get(x => x.Issued) >= qty) { // Everything is in stock, but needs treatment return Colors.Orange.ToBrush(0.5); } else if(row.Get(x => x.OnOrder) + row.Get(x => x.InStock) + row.Get(x => x.Issued) >= qty) { // Ordered, but not received. return Colors.LightYellow.ToBrush(); } else { // Not enough in stock. return Colors.LightSalmon.ToBrush(0.5); } } } private DynamicActionColumn InStockColumn; private DynamicActionColumn OnOrderColumn; private DynamicActionColumn TreatmentRequiredColumn; private DynamicActionColumn TreatmentOnOrderColumn; private DynamicActionColumn AllocatedColumn; private DynamicActionColumn PickRequestedColumn; private DynamicActionColumn IssuedColumn; public ReservationManagementItemGrid() { UserSettings = new UserConfiguration().Load(); FilterComponent.SetSettings(UserSettings.Filters, false); HiddenColumns.Add(x => x.ID); HiddenColumns.Add(x => x.Qty); HiddenColumns.Add(x => x.InStock); HiddenColumns.Add(x => x.OnOrder); HiddenColumns.Add(x => x.TreatmentOnOrder); HiddenColumns.Add(x => x.TreatmentRequired); HiddenColumns.Add(x => x.Allocated); HiddenColumns.Add(x => x.PickRequested); HiddenColumns.Add(x => x.Issued); HiddenColumns.Add(x => x.Product.ID); HiddenColumns.Add(x => x.Product.Name); HiddenColumns.Add(x => x.Product.Code); HiddenColumns.Add(x => x.Product.Group.ID); HiddenColumns.Add(x => x.Product.Group.Code); HiddenColumns.Add(x => x.Product.Group.Description); HiddenColumns.Add(x => x.Style.Group.ID); HiddenColumns.Add(x => x.Style.ID); HiddenColumns.Add(x => x.Style.Code); HiddenColumns.Add(x => x.Style.Description); HiddenColumns.Add(x => x.Requisition.ID); HiddenColumns.Add(x => x.Requisition.Job.ID); HiddenColumns.Add(x => x.Requisition.Job.JobNumber); HiddenColumns.Add(x => x.Requisition.Job.Name); HiddenColumns.Add(x => x.Requisition.Number); HiddenColumns.Add(x => x.Requisition.DueDate); HiddenColumns.Add(x => x.Job.ID); HiddenColumns.Add(x => x.Job.Name); HiddenColumns.Add(x => x.Job.JobNumber); HiddenColumns.Add(x => x.Dimensions.UnitSize); HiddenColumns.Add(x => x.Dimensions.Length); HiddenColumns.Add(x => x.Dimensions.Width); HiddenColumns.Add(x => x.Dimensions.Height); HiddenColumns.Add(x => x.Dimensions.Weight); HiddenColumns.Add(x => x.Dimensions.Quantity); HiddenColumns.Add(x => x.Dimensions.Value); HiddenColumns.Add(x => x.Dimensions.Unit.ID); HiddenColumns.Add(x => x.Dimensions.Unit.HasLength); HiddenColumns.Add(x => x.Dimensions.Unit.HasHeight); HiddenColumns.Add(x => x.Dimensions.Unit.HasWidth); HiddenColumns.Add(x => x.Dimensions.Unit.HasWeight); HiddenColumns.Add(x => x.Dimensions.Unit.HasQuantity); HiddenColumns.Add(x => x.Dimensions.Unit.Formula); HiddenColumns.Add(x => x.Dimensions.Unit.Format); HiddenColumns.Add(x => x.Dimensions.Unit.Code); HiddenColumns.Add(x => x.Dimensions.Unit.Description); HiddenColumns.Add(x => x.Dimensions.Unit.Conversion); HiddenColumns.Add(x=>x.UnitCost); AddDoubleColumn(x => x.Qty, "Qty."); InStockColumn = AddDoubleColumn(x => x.InStock, "Stk.", tooltip: "Total units currently in stock, regardless of style."); OnOrderColumn = AddDoubleColumn(x => x.OnOrder, "Ord.", tooltip: "Total units currently on order."); TreatmentRequiredColumn = AddDoubleColumn(x => x.TreatmentRequired, "Req.", tooltip: "Total units in stock needing treatment."); TreatmentOnOrderColumn = AddDoubleColumn(x => x.TreatmentOnOrder, "Ord.", tooltip: "Total units currently on order for treatment."); AllocatedColumn = AddDoubleColumn(x => x.Allocated, "Stk.", tooltip: "Total units in stock in the correct style."); PickRequestedColumn = AddDoubleColumn(x => x.PickRequested, "P/L.", tooltip: "Total units on picking lists."); IssuedColumn = AddDoubleColumn(x => x.Issued, "Iss.", tooltip: "Total units issued."); if (Security.CanEdit()) ActionColumns.Add(new DynamicMenuColumn(BuildMenu)); ColumnsTag = "JobRequisitionReview"; FilterComponent.OnFiltersSelected += GridOnFilterSelected; CreateOrder = AddButton("Orders", PRSDesktop.Resources.purchase.AsBitmapImage(), PurchaseOrder_Click); CreateOrder.IsEnabled = false; CreatePickingList = AddButton("Create Picking List", PRSDesktop.Resources.trolley.AsBitmapImage(), DoCreatePickingList); CreatePickingList.IsEnabled = false; ArchiveButton = AddButton("Archive", PRSDesktop.Resources.archive.AsBitmapImage(), ArchiveButton_Clicked); ArchiveButton.IsEnabled = false; ConsolidateHoldings = AddButton("Consolidate", PRSDesktop.Resources.product.AsBitmapImage(), ConsolidateHoldings_Clicked); ConsolidateHoldings.IsEnabled = false; } private bool ConsolidateHoldings_Clicked(Button button, CoreRow[] rows) { ContextMenu menu = new ContextMenu(); menu.Items.Add( new MenuItem() { Header = "Move to Location", Command = new ActionCommand(() => { MultiSelectDialog dlg = new MultiSelectDialog(new Filter(x => x.Active).IsEqualTo(true), null, false); if (dlg.ShowDialog() == true) ConsolidateStockLocations(rows, dlg.Items().First()); }) } ); menu.Items.Add(new Separator()); menu.Items.Add( new MenuItem() { Header = "Create New Location", Command = new ActionCommand(() => { var _location = new StockLocation(); var _grid = DynamicGridUtils.CreateDynamicGrid(typeof(DynamicDataGrid<>)); if (_grid.EditItems( [ _location ] )) { ConsolidateStockLocations(rows, _location); } }) } ); menu.IsOpen = true; return false; } private void ConsolidateStockLocations(CoreRow[] rows, StockLocation location) { Progress.ShowModal("Consolidating Items", progress => { var _ids = rows.Select(r => r.Get(x => x.ID)) .Distinct() .ToArray(); var _allmovements = Client.Query( new Filter(x => x.JobRequisitionItem.ID).InList(_ids), Columns.None() .Add(x=>x.Product.ID) .Add(x=>x.Style.ID) .Add(x=>x.Location.ID) .Add(x=>x.Job.ID) .Add(x=>x.Units) .Add(x=>x.Cost) .Add(x=>x.JobRequisitionItem.ID) .AddDimensionsColumns(x=>x.Dimensions) ); if (_allmovements.Rows.Any()) { progress.Report("Creating Batch"); var _batch = new StockMovementBatch(); _batch.TimeStamp = DateTime.Now; _batch.Type = StockMovementBatchType.Transfer; _batch.Employee.ID = App.EmployeeID; Client.Save(_batch, "Consolidating requisition items into single location"); progress.Report("Creating Transactions"); List _updates = new(); foreach (var _id in _ids) { var _movements = _allmovements.Rows .Where(r => r.Get(x=>x.JobRequisitionItem.ID) == _id) .ToObjects() .ToArray(); var _holdings = StockHoldingExtensions.GroupMovements(_movements).ToArray(); foreach (var _holding in _holdings) { var _transout = _holding.CreateMovement(); _transout.Employee.ID = _batch.Employee.ID; _transout.Issued = _holding.Units; _transout.Cost = _holding.AverageValue; _transout.Type = StockMovementType.TransferOut; _transout.JobRequisitionItem.ID = _id; _transout.Batch.ID = _batch.ID; _transout.Notes = $"Consolidating requisition item holdings from {_holding.Location.Code} to {location.Code}"; _updates.Add(_transout); var _transin = _holding.CreateMovement(); _transin.Date = _transout.Date.AddTicks(1); _transin.Employee.ID = _batch.Employee.ID; _transin.Location.ID = location.ID; _transin.Received = _holding.Units; _transin.Cost = _holding.AverageValue; _transin.Type = StockMovementType.TransferIn; _transin.Transaction = _transout.Transaction; _transin.JobRequisitionItem.ID = _id; _transin.Batch.ID = _batch.ID; _transin.Notes = $"Consolidating requisition item holdings from {_holding.Location.Code} to {location.Code}"; _updates.Add(_transin); } } Client.Save(_updates,$"Consolidated requisition item holdings to {location.Code}"); } }); } #region CreatePurchaseOrder private bool PurchaseOrder_Click(Button button, CoreRow[] rows) { if(rows.Length == 0) { return false; } var menu = new ContextMenu(); menu.AddItem("Create Purchase Order", null, rows, CreatePurchaseOrder); menu.AddItem("Add to Existing Order", null, rows, AddToExistingOrder); menu.IsOpen = true; return false; } private void CreatePurchaseOrder(CoreRow[] rows) { var mr = MessageWindow.ShowYesNoCancel("Include Style in Order?", "Confirm"); if (mr == MessageWindowResult.Cancel) return; var items = new List(); foreach(var row in rows) { var jri = row.ToObject(); if (mr != MessageWindowResult.Yes) { jri.Style.CopyFrom(new ProductStyle()); } var qtyRequired = DimensionUtils.ConvertDimensions(jri.Dimensions, Math.Max(jri.Qty - jri.InStock - jri.OnOrder, 0.0), (f,c) => Client.Query(f,c)); var item = items.FirstOrDefault(x => x.Product.ID == jri.Product.ID && x.Style.ID == jri.Style.ID && x.Dimensions.Equals(jri.Dimensions) ); if (item == null) { item = new StockForecastOrderData(jri.Product, jri.Style, jri.Dimensions); items.Add(item); } item.RequiredQuantity += qtyRequired; item.SetRequiredQuantity(jri.Job.ID, jri.ID, $"{jri.Job.JobNumber}-{jri.Requisition.Number}", qtyRequired); } var window = new StockForecastOrderScreen(items, false) { Owner = Application.Current.MainWindow }; if (window.ShowDialog() == true) { window.CreateOrders("Reservation Management"); Refresh(false, true); } } Window? PurchasingWindow; private void AddToExistingOrder(CoreRow[] rows) { var dlg = new MultiSelectDialog( new Filter(x => x.ClosedDate).IsEqualTo(DateTime.MinValue), Columns.None().Add(x => x.ID), false); if (dlg.ShowDialog()) { var id = dlg.IDs().First(); var jris = rows.ToArray(); var pois = new List<(PurchaseOrderItem poi, PurchaseOrderItemAllocation poia)>(); foreach (var jri in jris) { var poItem = new PurchaseOrderItem { Description = jri.Product.Name, Qty = Math.Max(jri.Qty - (jri.InStock + jri.Issued), 0.0) }; poItem.Product.ID = jri.Product.ID; poItem.Product.Synchronise(jri.Product); poItem.Dimensions.CopyFrom(jri.Dimensions); poItem.Style.ID = jri.Style.ID; poItem.Style.Synchronise(jri.Style); poItem.PurchaseOrderLink.ID = id; var allocation = new PurchaseOrderItemAllocation(); allocation.Job.CopyFrom(jri.Job); allocation.JobRequisitionItem.CopyFrom(jri); allocation.Quantity = poItem.Qty; pois.Add((poItem, allocation)); } Client.Save(pois.Select(x => x.poi), "Created from Reservation Management Screen"); foreach(var (poi, poia) in pois) { poia.Item.CopyFrom(poi); } Client.Save(pois.Select(x => x.poia), "Created from Reservation Management Screen"); ViewPurchaseOrder(id); } } #endregion #region CreatePickingList private bool DoCreatePickingList(Button button, CoreRow[] rows) { if (rows.Length == 0) return false; if (rows.All(r => r.Get(x => x.PickRequested).IsEffectivelyEqual(r.Get(x => x.Qty)))) { MessageWindow.ShowMessage("All Items have been picked!", "Error", image: MessageWindow.WarningImage); return false; } var _jris = rows.ToObjects() .GroupBy(x=>new Tuple(x.Requisition.Job.ID, x.Requisition.Job.JobNumber, x.Requisition.Job.Name)) .ToArray(); List _pls = new List(); Progress.ShowModal("Creating Picking Lists", progress => { Task _movementquery = Task.Run( () => Client.Query( new Filter(x => x.JobRequisitionItem.ID) .InList(rows.Select(r => r.Get(c=>c.ID)).ToArray()) ).ToObjects() .ToArray() ); foreach (var _job in _jris) { var _pl = new Requisition(); _pl.JobLink.ID = _job.Key.Item1; _pl.JobLink.JobNumber = _job.Key.Item2; _pl.JobLink.Name = _job.Key.Item3; _pl.RequestedBy.ID = App.EmployeeID; _pl.Notes = new string[] { $"Items requested by {App.EmployeeName}" }; _pls.Add(_pl); } Client.Save(_pls, "Created from Reservation Management Screen"); progress.Report("Retrieving Allocations"); _movementquery.Wait(); progress.Report("Creating Picking List Items"); List _plitems = new List(); foreach (var _pl in _pls) { var _pljris = _jris.First(x => x.Key.Item1 == _pl.JobLink.ID); foreach (var _pljri in _pljris) { double qtyrequired = _pljri.Qty; double unitCost = _pljri.UnitCost; var dimensions = _pljri.Dimensions.Copy(); if (!String.IsNullOrWhiteSpace(_pljri.Dimensions.Unit.Conversion)) { var qty = DimensionUtils.ConvertDimensions(dimensions, _pljri.Qty, Client.Provider); if (!qty.IsEffectivelyEqual(_pljri.Qty)) { unitCost = _pljri.UnitCost * _pljri.Qty / (qty.IsEffectivelyEqual(0.0) ? 1.0 : qty); qtyrequired = qty; } } var _locations = _movementquery.Result.Where(x => x.JobRequisitionItem.ID == _pljri.ID) .GroupBy(x => x.Location.ID); foreach (var _location in _locations) { if (!qtyrequired.IsEffectivelyEqual(0.0)) { var _plitem = new RequisitionItem(); _plitem.RequisitionLink.CopyFrom(_pl); _plitem.JobRequisitionItem.CopyFrom(_pljri); _plitem.SourceJRI.CopyFrom(_pljri); _plitem.Product.CopyFrom(_pljri.Product); _plitem.Style.CopyFrom(_pljri.Style); _plitem.Dimensions.CopyFrom(dimensions); _plitem.Location.ID = _location.Key; _plitem.Quantity = Math.Min(qtyrequired, _location.Sum(x => x.Units)); _plitem.ActualQuantity = _plitem.Quantity; _plitem.Cost = unitCost; qtyrequired -= qtyrequired; _plitems.Add(_plitem); } } if (!qtyrequired.IsEffectivelyEqual(0.0)) { var _plitem = new RequisitionItem(); _plitem.RequisitionLink.CopyFrom(_pl); _plitem.JobRequisitionItem.CopyFrom(_pljri); _plitem.Product.CopyFrom(_pljri.Product); _plitem.Style.CopyFrom(_pljri.Style); _plitem.Dimensions.CopyFrom(dimensions); _plitem.Quantity = qtyrequired; _plitem.Cost = unitCost; _plitems.Add(_plitem); } } } Client.Save(_plitems,"Created from Reservation Management Screen"); }); MessageWindow.ShowMessage( $"Created the following Picking Lists:\n- {string.Join("\n- ",_pls.Select(x=>$"{x.Number}: {x.JobLink.JobNumber} {x.JobLink.Name}"))}", "Picking Lists Created"); return true; } #endregion private DynamicActionColumn AddDoubleColumn(Expression> property, string header, string? tooltip = null) { var col = new DynamicTextColumn(property) { Format = "F2", HeaderText = header, Width = 50, }; if(tooltip != null) { col.ToolTip = (c, r) => { return c.TextToolTip(tooltip); }; } ActionColumns.Add(col); return col; } protected override IDynamicGridUIComponent CreateUIComponent() { return new UIComponent(this); } protected override void DoReconfigure(DynamicGridOptions options) { base.DoReconfigure(options); options.FilterRows = true; options.SelectColumns = true; options.RecordCount = true; options.AddRows = false; options.ImportData = false; options.ExportData = false; options.Print = false; options.ShowHelp = false; options.MultiSelect = true; options.ReorderRows = false; } private void GridOnFilterSelected(DynamicGridSelectedFilterSettings settings) { new UserConfiguration().Save(new ReservationManagementUserSettings { Filters = settings }); Refresh(false, true); } public override DynamicGridColumns GenerateColumns() { var columns = new DynamicGridColumns(); columns.Add(x => x.Requisition.DueDate, 80, "Due", "", Alignment.MiddleCenter); columns.Add(x => x.Requisition.Job.JobNumber, 70, "Job", "", Alignment.MiddleCenter); columns.Add(x => x.Requisition.Number, 50, "Requi", "", Alignment.MiddleCenter); columns.Add(x => x.Product.Code, 100, "Product Code", "", Alignment.MiddleLeft); columns.Add(x => x.Product.Name, 0, "Product Name", "", Alignment.MiddleLeft); columns.Add(x => x.Style.Code, 100, "Style", "", Alignment.MiddleLeft); columns.Add(x => x.Dimensions.UnitSize, 70, "Size", "", Alignment.MiddleLeft); return columns; } protected override void ConfigureColumnGroups() { GetColumnGrouping() .AddGroup("Stock", InStockColumn, OnOrderColumn) .AddGroup("Treatment", TreatmentRequiredColumn, TreatmentOnOrderColumn) .AddGroup("Allocated", AllocatedColumn, IssuedColumn); } protected override void SelectItems(CoreRow[]? rows) { base.SelectItems(rows); bool bAny = rows?.Any() == true; ArchiveButton.IsEnabled = bAny; CreateOrder.IsEnabled = bAny; CreatePickingList.IsEnabled = bAny; ConsolidateHoldings.IsEnabled = bAny; } #region Action Column Buttons private void BuildMenu(DynamicMenuColumn column, CoreRow? row) { if (row is null) return; column.AddItem("Allocate Stock", PRSDesktop.Resources.trolley, AllocateStock_Clicked); column.AddSeparator(); column.AddItem("Substitute Item", PRSDesktop.Resources.refresh, SubstituteItem_Clicked); if (!String.IsNullOrWhiteSpace(row.Get(x=>x.Dimensions.Unit.Conversion))) column.AddItem("Convert Dimensions", PRSDesktop.Resources.anonymous, ConvertDimensions_Clicked); //column.AddItem("Order Required", PRSDesktop.Resources.purchase, OrderRequired_Clicked); column.AddItem("Split Line", PRSDesktop.Resources.split, SplitLine_Clicked); if (Security.CanView()) { column.AddSeparator(); column.AddItem("View Holdings", PRSDesktop.Resources.warehouse, ViewStockHoldings); column.AddItem("View Stock Movements", PRSDesktop.Resources.forklift, ViewStockMovements); var viewOrder = column.AddItem("View Purchase Order", PRSDesktop.Resources.purchase, null); viewOrder.AddItem("Loading...", null, null, enabled: false); var task = Task.Run(() => { return Client.Query( new Filter(x => x.JobRequisitionItem.ID).IsEqualTo(row.Get(x => x.ID)), Columns.None() .Add(x => x.Item.PurchaseOrderLink.ID) .Add(x => x.Item.PurchaseOrderLink.PONumber) .Add(x => x.Item.PurchaseOrderLink.SupplierLink.Name)) .ToTuples( x => x.Item.PurchaseOrderLink.ID, x => x.Item.PurchaseOrderLink.PONumber, x => x.Item.PurchaseOrderLink.SupplierLink.Name) .ToArray(); }); task.ContinueWith(orders => { viewOrder.Items.Clear(); if (orders.Result.Length > 0) { foreach (var tuple in orders.Result) { if (tuple.Item1 != Guid.Empty) { viewOrder.AddItem($"{tuple.Item2}: {tuple.Item3}", PRSDesktop.Resources.purchase, tuple.Item1, ViewPurchaseOrder); } } } else { viewOrder.IsEnabled = false; } }, TaskScheduler.FromCurrentSynchronizationContext()); } } private void ConvertDimensions_Clicked(CoreRow? row) { var jri = row?.ToObject(); if (jri == null) return; if (MessageWindow.ShowOKCancel("Standardise Dimensions?", "Confirm")) { Progress.ShowModal("Standardising Dimensions", progress => { var qty = jri.Qty; DimensionUtils.ConvertDimensions(jri.Dimensions, ref qty, (f, c) => Client.Query(f, c)); jri.Qty = qty; Client.Save(jri, "Converted Dimensions in Reservation Management Screen"); }); Refresh(false,true); } } private void AllocateStock_Clicked(CoreRow? row) { if (row is null) return; var jri = row.ToObject(); var window = new ReservationManagementHoldingsWindow(jri, CompanyDefaultStyle) { Owner = Application.Current.MainWindow }; window.OnHoldingsReviewRefresh += () => { var data = Client.Query( new Filter(x => x.ID).IsEqualTo(jri.ID), DataColumns()); jri = data.ToObjects().FirstOrDefault(); if(jri is not null) { UpdateRow(row, jri); } }; window.ShowDialog(); } private void SubstituteItem_Clicked(CoreRow? row) { var jri = row?.ToObject(); if (jri == null) return; var window = new ReservationManagementSubstitutionWindow(jri, UserSettings) { Owner = Application.Current.MainWindow }; if (window.ShowDialog() == true) Refresh(false,true); } private bool CheckValidAction(JobRequisitionItem item) { bool valid = true; if (item.Allocated + item.Issued >= item.Qty) { MessageWindow.ShowMessage("Item has already been reserved!", "Error", image: MessageWindow.WarningImage); return false; } else if (item.OnOrder >= 0) { MessageWindow.ShowMessage("Item is already on order!", "Error", image: MessageWindow.WarningImage); return false; } else if (item.InStock >= item.Qty) { MessageWindow.ShowMessage("Item is already in stock!", "Error", image: MessageWindow.WarningImage); return false; } return valid; } private void SplitLine(JobRequisitionItem item, double oldItemQty, double newItemQty, string notes) { var items = new List(); var newItem = new JobRequisitionItem(); newItem.Job.CopyFrom(item.Job); newItem.Requisition.ID = item.Requisition.ID; newItem.Requisition.Job.ID = item.Requisition.Job.ID; newItem.Product.ID = item.Product.ID; newItem.Dimensions.CopyFrom(item.Dimensions); newItem.Style.ID = item.Style.ID; newItem.Notes = item.Notes + Environment.NewLine + notes; item.Notes = newItem.Notes; item.Qty = oldItemQty; newItem.Qty = newItemQty; items.Add(newItem); items.Add(item); Client.Save(items, "Split lines from Job Requi Item Review Dashboard"); MessageWindow.ShowMessage($"Line split - original line Qty is now {item.Qty}. New line Qty is {newItem.Qty}", "Lines split"); Refresh(false, true); } private void SplitLine_Clicked(CoreRow? row) { if (row is null) return; var item = row.ToObject(); if (CheckValidAction(item)) { var units = item.Qty - item.InStock; if(DoubleEdit.Execute("Enter amount to split", 1, units, 2, ref units)) { SplitLine(item, item.Qty - units, units, "Line split"); } } } private static bool Archive(IEnumerable items) { var itemsList = items.AsIList(); var toChange = new List(); foreach(var item in itemsList) { if (item.Allocated + item.Issued < item.Qty) { var win = MessageWindow.New() .Message($"Requisition item for requisition {item.Requisition.Number} is not fully allocated; " + $"Are you sure you wish to archive this item?") .Title("Confirm Archive") .AddYesButton("Archive"); if(itemsList.Count > 1) { win.AddNoButton("Skip"); } var result = win .AddCancelButton() .Display().Result; if (result == MessageWindowResult.Cancel) { return false; } else if(result == MessageWindowResult.Yes) { toChange.Add(item); } } else { toChange.Add(item); } } if(toChange.Count == 0) { MessageWindow.ShowMessage("No items archived.", "Cancelled"); return false; } foreach(var item in toChange) { item.Archived = DateTime.Now; item.Notes += Environment.NewLine + "Line marked as Archived by " + App.EmployeeName + " on " + DateTime.Now.ToString("dd MMM yy"); } Client.Save(toChange, "Updated From Job Requisition Review Dashboard"); return true; } private bool ArchiveButton_Clicked(Button _btn, CoreRow[] rows) { return Archive(rows.ToObjects()); } private void Archive_Clicked(CoreRow? row) { if (row is null) return; if (Archive(CoreUtils.One(row.ToObject()))) { Refresh(false, true); } } // private void OrderRequired_Clicked(CoreRow? row) // { // if (row is null) return; // // var item = row.ToObject(); // if (CheckValidAction(item)) // { // item.OrderRequired = DateTime.Now; // item.Notes += Environment.NewLine + "Line marked as Order Required by " + App.EmployeeName + " on " + DateTime.Now.ToString("dd MMM yy"); // // Client.Save(item, "Updated From Job Requisition Review Dashboard"); // // Refresh(false, true); // } // } private void ViewPurchaseOrder(Guid id) { var _entity = Client.Query( new Filter(x => x.ID).IsEqualTo(id), DynamicGridUtils.LoadEditorColumns(Columns.None())) .ToObjects().FirstOrDefault(); if (_entity is null) return; var window = new DynamicEditorForm() { Title = "View Purchase Order Details", }; window.Form.DisableOKIfUnchanged = true; window.Form.SetLayoutType(); new SupplierPurchaseOrders().InitialiseEditorForm(window, new PurchaseOrder[] { _entity }, null, true); var reportButton = new Button() { Content = new System.Windows.Controls.Image() { Source = PRSDesktop.Resources.printer.AsBitmapImage(32, 32) }, Command = new ActionCommand(() => ShowReports(id)), Width = 40 }; window.Form.AddButton(reportButton); window.Form.OnChanged += (o, e) => { reportButton.IsEnabled = false; }; if (window.ShowDialog() == true) { Refresh(false, true); } } private static void ShowReports(Guid id) { var _model = new AutoDataModel(new Filter(x => x.ID).IsEqualTo(id)); var menu = new ContextMenu(); var templates = ReportUtils.LoadReports("Purchase Orders", _model, Columns.None().Add(x => x.ID, x => x.Name)); ReportUtils.PopulateMenu(menu, "Purchase Orders", _model, Security.IsAllowed(), true); if (menu.Items.Count == 0) menu.AddItem("No reports", null, null, enabled: false); menu.IsOpen = true; } private void ViewStockHoldings(CoreRow? row) { if (row is null) return; var _movements = Client.Query( new Filter(x => x.JobRequisitionItem.ID) .IsEqualTo(row.Get(x => x.ID)), Columns.None() .Add(x => x.ID) .Add(x=>x.Location.ID) .Add(x=>x.Location.Code) .Add(x=>x.Location.Description) .Add(x=>x.Style.ID) .Add(x=>x.Style.Code) .Add(x=>x.Style.Description) .AddDimensionsColumns(x=>x.Dimensions) .Add(x=>x.Units) .Add(x=>x.Cost) ).ToObjects(); var holdings = StockHoldingExtensions.GroupMovements(_movements); var grid = new DynamicItemsListGrid() { Items = holdings }; grid.ColumnsLoaded += (sender, args) => { args.ColumnGroupings.Clear(); args.Columns.Clear(); args.Add(x => x.Location.Code, 150, "Location", "", Alignment.MiddleLeft); args.Add(x => x.Style.Code, 150, "Style Code", "", Alignment.MiddleLeft); args.Add(x => x.Style.Description, 0, "Style", "", Alignment.MiddleLeft); args.Add(x => x.Dimensions.UnitSize, 150, "Unit Size", "", Alignment.MiddleLeft); args.Add(x => x.Units); args.Add(x => x.AverageValue, caption: "Cost $"); }; var window = DynamicGridUtils.CreateGridWindow("Stock Holdings", grid); window.ShowDialog(); } private void ViewStockMovements(CoreRow? row) { if (row is null) return; var requiID = row.Get(x => x.ID); DynamicDataGrid grid; if (DynamicGridUtils.TryFindDynamicGrid(typeof(DynamicDataGrid<>), typeof(StockMovement), out var gridType)) { grid = (Activator.CreateInstance(gridType) as DynamicDataGrid)!; } else { grid = new DynamicDataGrid(); grid.OnReconfigure += (options) => { options.SelectColumns = true; }; grid.Reconfigure(); } grid.OnDefineFilter += (t) => new Filter(x => x.JobRequisitionItem.ID).IsEqualTo(requiID); var window = DynamicGridUtils.CreateGridWindow("Stock movements", grid); window.ShowDialog(); } #endregion protected override void Reload( Filters criteria, Columns columns, ref SortOrder? sort, CancellationToken token, Action action) { if (JobID.HasValue) criteria.Add(new Filter(x => x.Requisition.Job.ID).IsEqualTo(JobID.Value)); criteria.Add(new Filter(x => x.Requisition.Approved).IsNotEqualTo(DateTime.MinValue)); criteria.Add(new Filter(x => x.Archived).IsEqualTo(DateTime.MinValue)); criteria.Add(new Filter(x => x.Cancelled).IsEqualTo(DateTime.MinValue)); sort = new SortOrder(x => x.Requisition.Number, SortDirection.Descending); base.Reload(criteria, columns, ref sort, token, action); } } public class JobRequiReviewDashboardFilterItem { public Guid SupplierID { get; set; } public Guid ProductID { get; set; } public string Text { get; set; } public JobRequiReviewDashboardFilterItem() { SupplierID = Guid.Empty; ProductID = Guid.Empty; Text = ""; } }