using Comal.Classes; using InABox.Clients; using InABox.Configuration; using InABox.Core; using InABox.DynamicGrid; using InABox.WPF; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Windows; using System.Windows.Controls; namespace PRSDesktop { public class JobRequisitionReviewUserSettings : IUserConfigurationSettings { public DynamicGridFilter Filter { get; set; } public JobRequisitionReviewUserSettings() { Filter = new DynamicGridFilter(); } } public delegate void JobRequiItemSelect(CoreRow[] rows); public delegate void GridRefresh(); public class JobRequisitionReviewGrid : DynamicDataGrid { public event JobRequiItemSelect OnJobRequiItemSelected; public event GridRefresh OnGridRefresh; public Guid empID = new Guid(); string empName = ""; bool bIncludeArchived = false; bool bViewCancelled = false; JobRequisitionReviewUserSettings FilterSettings = new JobRequisitionReviewUserSettings(); Dictionary JobRequisReservedQty = new Dictionary(); public JobRequisitionReviewGrid() { FilterSettings = new UserConfiguration().Load(); if (!string.IsNullOrWhiteSpace(FilterSettings.Filter.Name)) { SelectedFilter = new(FilterSettings.Filter.Name, Serialization.Deserialize>(FilterSettings.Filter.Filter)); UpdateFilterButton(InABox.Wpf.Resources.filter_set); } Options.AddRange( DynamicGridOption.FilterRows, DynamicGridOption.SelectColumns, DynamicGridOption.RecordCount ); Options.Remove(DynamicGridOption.AddRows); Options.Remove(DynamicGridOption.EditRows); Options.Remove(DynamicGridOption.ImportData); Options.Remove(DynamicGridOption.ExportData); Options.Remove(DynamicGridOption.Print); Options.Remove(DynamicGridOption.ShowHelp); HiddenColumns.Add(x => x.ID); HiddenColumns.Add(x => x.Product.ID); 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.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.PurchaseOrderItem.ID); HiddenColumns.Add(x => x.PurchaseOrderItem.PurchaseOrderLink.ID); HiddenColumns.Add(x => x.PurchaseOrderItem.PurchaseOrderLink.PONumber); HiddenColumns.Add(x => x.PurchaseOrderItem.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); LoadStockMovements(); if (Security.CanEdit()) ActionColumns.Add(new DynamicMenuColumn(BuildMenu)); //if (Security.CanEdit()) // AddButton("Create Purchase Order", null, CreatePurchaseOrder); //if (Security.CanEdit()) // AddButton("Create Treatment PO", null, CreateTreatmentPO); AddButton("Include Archived", null, ViewArchived); AddButton("Include Cancelled", null, ViewCancelled); OnSelectItem += JobRequisitionReviewGrid_OnSelectItem; ColumnsTag = "JobRequisitionReview"; CoreTable table = new Client().Query(new Filter(x => x.UserLink.UserID).IsEqualTo(ClientFactory.UserID), new Columns(x => x.ID, x => x.Name)); if (table.Rows.Any()) { empID = Guid.Parse(table.Rows.FirstOrDefault().Values[0].ToString()); empName = table.Rows.FirstOrDefault().Values[1].ToString(); } OnFilterSelected += GridOnFilterSelected; //Migrate(); } private void GridOnFilterSelected(DynamicGridFilter filter, Bitmap image) { new UserConfiguration().Save(new JobRequisitionReviewUserSettings { Filter = filter }); OnGridRefresh?.Invoke(); } private void Migrate() { CoreTable table = new Client().Query(null, new Columns(x => x.ID, x => x.PurchaseOrderItem.PurchaseOrderLink.ID)); Dictionary reqPOids = new Dictionary(); List affected = new List(); foreach (CoreRow row in table.Rows) { var item = row.ToObject(); if (item.PurchaseOrderItem.PurchaseOrderLink.ID != Guid.Empty) { reqPOids.Add(row.Get(x => x.ID), item.PurchaseOrderItem.PurchaseOrderLink.ID); affected.Add(row.ToObject()); } } List list = new List(); CoreTable POs = new Client().Query(new Filter(x => x.ID).InList(reqPOids.Values.ToArray()), new Columns(x => x.ID, x => x.Created)); foreach (CoreRow row in POs.Rows) { var items = affected.Where(x => x.PurchaseOrderItem.PurchaseOrderLink.ID == row.Get(x => x.ID)); foreach (var item in items) { item.Ordered = row.Get(x => x.Created); list.Add(item); } } new Client().Save(list, "Migrate"); } private void LoadStockMovements() { CoreTable table = new Client().Query( new Filter(x => x.JobRequisitionItem.ID).IsNotEqualTo(Guid.Empty), new Columns( x => x.JobRequisitionItem.ID, x => x.Received ) ); foreach (CoreRow row in table.Rows) { var requiID = row.Get(x => x.JobRequisitionItem.ID); var qty = row.Get(x => x.Received); if (!JobRequisReservedQty.ContainsKey(requiID)) JobRequisReservedQty.Add(requiID, qty); else { double newQty = JobRequisReservedQty[requiID] + qty; JobRequisReservedQty.Remove(requiID); JobRequisReservedQty.Add(requiID, newQty); } } } public void AddStockMovements(Guid requiItemID) { CoreTable table = new Client().Query(new Filter(x => x.JobRequisitionItem.ID).IsEqualTo(requiItemID), new Columns( x => x.JobRequisitionItem.ID, x => x.Received ) ); if (table.Rows.Any()) { foreach (CoreRow row in table.Rows) { var requiID = row.Get(x => x.JobRequisitionItem.ID); var qty = row.Get(x => x.Received); if (!JobRequisReservedQty.ContainsKey(requiID)) JobRequisReservedQty.Add(requiID, qty); else if (JobRequisReservedQty.ContainsKey(requiID) && row == table.Rows.First()) { JobRequisReservedQty.Remove(requiID); JobRequisReservedQty.Add(requiID, qty); } else { double newQty = JobRequisReservedQty[requiID] + qty; JobRequisReservedQty.Remove(requiID); JobRequisReservedQty.Add(requiID, newQty); } } } } private string? CreateStatus(CoreRow? row) { Guid id = row.Get(x => x.ID); string status = "Not Checked"; if (row.Get(x => x.Cancelled) != DateTime.MinValue) return "Cancelled"; if (row.Get(x => x.Archived) != DateTime.MinValue) return "Archived"; if (row.Get(x => x.Ordered) != DateTime.MinValue && !JobRequisReservedQty.ContainsKey(id)) return "On Order"; if (row.Get(x => x.Status) == JobRequisitionItemStatus.OrderRequired) return "Order Required"; if (JobRequisReservedQty.ContainsKey(id)) if (JobRequisReservedQty[id] >= row.Get(x => x.Qty)) return "Reserved"; return status; } protected override DynamicGridStyle GetRowStyle(CoreRow row, DynamicGridStyle style) { var result = base.GetRowStyle(row, style); //var item = row.ToObject(); //if (item.Status == JobRequisitionItemStatus.NotChecked) //{ // var rowstyle = new DynamicGridRowStyle(); // rowstyle.Background = new SolidColorBrush(Colors.LightSalmon); // return rowstyle; //} //if (item.Status == JobRequisitionItemStatus.OrderRequired) //{ // var rowstyle = new DynamicGridRowStyle(); // rowstyle.Background = new SolidColorBrush(Colors.LightCoral); // return rowstyle; //} return result; } private void JobRequisitionReviewGrid_OnSelectItem(object sender, DynamicGridSelectionEventArgs e) { OnJobRequiItemSelected?.Invoke(SelectedRows); } private bool ViewCancelled(Button button, CoreRow[] rows) { if (bViewCancelled) { bViewCancelled = false; button.Content = "Include Cancelled"; } else { bViewCancelled = true; button.Content = "Exclude Cancelled"; } OnGridRefresh?.Invoke(); return true; } private bool ViewArchived(Button button, CoreRow[] rows) { if (bIncludeArchived) { bIncludeArchived = false; button.Content = "Include Archived"; } else { bIncludeArchived = true; button.Content = "Exclude Archived"; } OnGridRefresh?.Invoke(); return true; } private bool CreateTreatmentPO(Button button, CoreRow[] rows) { return true; } protected override void GenerateColumns(DynamicGridColumns columns) { columns.Add(x => x.Created, 80, "Date", "", Alignment.MiddleLeft); columns.Add(x => x.Requisition.Job.JobNumber, 70, "Job", "", Alignment.MiddleLeft); columns.Add(x => x.Requisition.Number, 50, "NO.", "", Alignment.MiddleLeft); columns.Add(x => x.Product.Code, 70, "Code", "", Alignment.MiddleLeft); columns.Add(x => x.Product.Name, 200, "Product Name", "", Alignment.MiddleLeft); columns.Add(x => x.Style.Description, 150, "Style", "", Alignment.MiddleLeft); columns.Add(x => x.Qty, 50, "Qty", "", Alignment.MiddleLeft); columns.Add(x => x.Dimensions.UnitSize, 50, "Size", "", Alignment.MiddleLeft); columns.Add(x => x.PurchaseOrderItem.PurchaseOrderLink.PONumber, 80, "PO Number", "", Alignment.MiddleLeft); columns.Add(x => x.PurchaseOrderItem.PONumber, 80, "PO Number", "", Alignment.MiddleLeft); columns.Add(x => x.PurchaseOrderItem.DueDate, 80, "Due", "", Alignment.MiddleLeft); columns.Add(x => x.PurchaseOrderItem.ReceivedDate, 80, "Received", "", Alignment.MiddleLeft); columns.Add(x => x.Notes, 300, "Notes", "", Alignment.MiddleLeft); } private DynamicMenuStatus EmptyReturnFunction(CoreRow row) { return DynamicMenuStatus.Enabled; } #region Action Column Buttons private bool CheckValidAction(JobRequisitionItem item, bool bypassReserved) { bool valid = true; if (item.Status == JobRequisitionItemStatus.Reserved && !bypassReserved) { MessageBox.Show("Error. Item has already been reserved!"); return false; } else if (item.Status == JobRequisitionItemStatus.OnOrder) { MessageBox.Show("Error. Item is already on order!"); return false; } if (item.Status == JobRequisitionItemStatus.Received) { MessageBox.Show("Error. Item has already been recieved!"); return false; } return valid; } private void MarkReserved_Clicked(CoreRow? row) { JobRequisitionItem item = row.ToObject(); if (CheckValidAction(item, false)) { SaveRow(row, JobRequisitionItemStatus.Reserved, "Line marked as Reserved by " + empName + " on " + DateTime.Now.ToString("dd MMM yy")); } } private void SplitLine(JobRequisitionItem item, double oldItemQty, double newItemQty, string notes) { List items = new List(); JobRequisitionItem newItem = new JobRequisitionItem(); newItem.Requisition.ID = item.Requisition.ID; newItem.Requisition.Job.ID = item.Requisition.Job.ID; newItem.Requisition.Job.JobNumber = item.Requisition.Job.JobNumber; newItem.Requisition.Job.Name = item.Requisition.Job.Name; newItem.Product.ID = item.Product.ID; newItem.Product.Name = item.Product.Name; newItem.Product.Code = item.Product.Code; newItem.Product.Group.ID = item.Product.Group.ID; newItem.Product.Group.Description = item.Product.Group.Description; newItem.Dimensions.CopyFrom(item.Dimensions); newItem.Style.ID = item.Style.ID; newItem.Style.Description = item.Style.Description; newItem.Style.Code = item.Style.Code; newItem.Notes = item.Notes + Environment.NewLine + notes; item.Notes = newItem.Notes; item.Qty = oldItemQty; newItem.Qty = newItemQty; items.Add(newItem); items.Add(item); new Client().Save(items, "Split lines from Job Requi Item Review Dashboard"); MessageBox.Show("Line split - original line Qty is now " + item.Qty + ". New line Qty is " + newItem.Qty, "Success"); OnGridRefresh?.Invoke(); } private void SplitLine_Clicked(CoreRow row) { JobRequisitionItem item = row.ToObject(); if (CheckValidAction(item, false)) { int units = Convert.ToInt32(item.Qty); if (NumberEdit.Execute("Enter amount to split", 1, units, ref units)) { SplitLine(item, item.Qty - units, units, "Line split"); } } } private void Archive_Clicked(CoreRow row) { JobRequisitionItem item = row.ToObject(); item.Archived = DateTime.Now; SaveRow(row, JobRequisitionItemStatus.Archived, "Line marked as Archived by " + empName + " on " + DateTime.Now.ToString("dd MMM yy")); } private void OrderRequired_Clicked(CoreRow row) { JobRequisitionItem item = row.ToObject(); if (CheckValidAction(item, false)) SaveRow(row, JobRequisitionItemStatus.OrderRequired, "Line marked as Order Required by " + empName + " on " + DateTime.Now.ToString("dd MMM yy")); } private void TreatmentRequired_Clicked(CoreRow row) { JobRequisitionItem item = row.ToObject(); if (item.Status != JobRequisitionItemStatus.Reserved) { MessageBox.Show("Stock must first be reserved for this item"); return; } SaveRow(row, JobRequisitionItemStatus.TreatmentRequired, "Line marked as Treatment Required by " + empName + " on " + DateTime.Now.ToString("dd MMM yy")); } private void Uncheck_Clicked(CoreRow row) { string extraMessage = ""; JobRequisitionItem item = row.ToObject(); if (!CheckValidAction(item, true)) return; CoreTable table = new Client().Query ( new Filter(x => x.JobRequisitionItem.ID).IsEqualTo(item.ID), new Columns(x => x.ID) ); if (table.Rows.Any()) { var result = MessageBox.Show("This will reverse stock movements already created for this Requisition Item. Proceed?", "Alert", MessageBoxButton.YesNo); switch (result) { case MessageBoxResult.Yes: break; case MessageBoxResult.No: return; default: return; } List movements = new List(); foreach (CoreRow stockmovementRow in table.Rows) { StockMovement movement = new StockMovement(); movement.ID = Guid.Parse(stockmovementRow.Values[0].ToString()); movements.Add(movement); } new Client().Delete(movements, "Stock movements reversed from Job Requisition Item Review Dashboard"); extraMessage = " and Stock Movements Reversed "; } SaveRow(row, JobRequisitionItemStatus.NotChecked, "Line marked as Not Checked by " + empName + extraMessage + " on " + DateTime.Now.ToString("dd MMM yy")); } private bool CreatePurchaseOrder(Button btn, CoreRow[] rows) { if (!rows.Any()) { MessageBox.Show("Please select at least one row to add to Purchase Order!"); return false; } PurchaseOrder purchaseOrder = new PurchaseOrder(); purchaseOrder.Notes = "Created from Job Requi Item Review Screen" + System.Environment.NewLine; purchaseOrder.RaisedBy.ID = empID; var page = new SupplierPurchaseOrders(); page.OnAfterSave += (form, items) => { PurchaseOrderOnSave(form, items.Cast().ToArray()); }; return page.EditItems(new[] { purchaseOrder }, LoadPurchaseOrderItems, true); } private void PurchaseOrderOnSave(IDynamicEditorForm form, PurchaseOrder[] items) { Progress.Show("Working"); Guid POID = items[0].ID; CoreTable table = new Client().Query(new Filter(x => x.PurchaseOrderLink.ID).IsEqualTo(POID), new Columns(x => x.ID, x => x.Product.ID, x => x.Qty, x => x.Dimensions.UnitSize, x => x.DueDate, x => x.Job.ID)); if (table.Rows.Any()) { var poItems = AddPOItems(table, new List()); var requiItems = MatchRequiItems(poItems, new List()); if (requiItems.Count > 0) SaveAndRefreshScreen(requiItems); } Progress.Close(); } private void SaveAndRefreshScreen(List requiItems) { new Client().Save(requiItems, "Updated on Create Purchase Order from Job Requi Dashboard"); OnGridRefresh?.Invoke(); } private List AddPOItems(CoreTable table, List poItems) { foreach (CoreRow row in table.Rows) { PurchaseOrderItem poItem = row.ToObject(); poItems.Add(poItem); } return poItems; } private List MatchRequiItems(List poItems, List requiItems) { foreach (CoreRow row in SelectedRows) { JobRequisitionItem JobReqItem = row.ToObject(); foreach (var item in poItems) { if (string.IsNullOrWhiteSpace(JobReqItem.Dimensions.UnitSize)) JobReqItem.Dimensions.UnitSize = QueryUnitSize(JobReqItem.Product.ID); if (string.IsNullOrWhiteSpace(item.Dimensions.UnitSize)) item.Dimensions.UnitSize = QueryUnitSize(item.Product.ID); if (JobReqItem.Job.ID == Guid.Empty) JobReqItem.Job.ID = QueryJobID(JobReqItem.Requisition.ID); if (item.Job.ID == Guid.Empty) item.Job.ID = QueryJobID(JobReqItem.Requisition.ID); if (MatchReqItemToPOItem(JobReqItem, item)) requiItems.Add(UpdateJobReqItemWithPODetails(JobReqItem, item)); } } return requiItems; } private JobRequisitionItem UpdateJobReqItemWithPODetails(JobRequisitionItem JobReqItem, PurchaseOrderItem poItem) { JobReqItem.PurchaseOrderItem.ID = poItem.ID; JobReqItem.PurchaseOrderItem.DueDate = poItem.DueDate; if (JobReqItem.Status != JobRequisitionItemStatus.OnOrder) { JobReqItem.Notes = JobReqItem.Notes + Environment.NewLine + "Line marked as On Order by " + empName + " on " + DateTime.Now.ToString("dd MMM yy"); JobReqItem.Ordered = poItem.Created; } return JobReqItem; } private bool MatchReqItemToPOItem(JobRequisitionItem JobReqItem, PurchaseOrderItem item) { if (JobReqItem.Product.ID == item.Product.ID && JobReqItem.Dimensions.UnitSize == item.Dimensions.UnitSize && JobReqItem.Job.ID == item.Job.ID) return true; else return false; } private string QueryUnitSize(Guid productID) { CoreTable table = new Client().Query(new Filter(x => x.ID).IsEqualTo(productID), new Columns(x => x.Dimensions.UnitSize)); return table.Rows.FirstOrDefault().Get("Dimensions.UnitSize"); } private Guid QueryJobID(Guid iD) { CoreTable table = new Client().Query(new Filter(x => x.ID).IsEqualTo(iD), new Columns(x => x.Job.ID)); return table.Rows.FirstOrDefault().Get("Job.ID"); } private CoreTable LoadPurchaseOrderItems(Type arg) { Progress.Show("Working"); var result = new CoreTable(); result.LoadColumns(typeof(PurchaseOrderItem)); List items = new List(); foreach (CoreRow row in SelectedRows) { JobRequisitionItem JobReqItem = row.ToObject(); PurchaseOrderItem POItem = new PurchaseOrderItem(); POItem.Product.ID = JobReqItem.Product.ID; POItem.Product.Code = JobReqItem.Product.Code; POItem.Product.Name = JobReqItem.Product.Name; POItem.Description = JobReqItem.Product.Name; POItem.Qty = JobReqItem.Qty; POItem.Dimensions.CopyFrom(JobReqItem.Dimensions); POItem.Style.ID = JobReqItem.Style.ID; POItem.Style.Code = JobReqItem.Style.Code; POItem.Style.Description = JobReqItem.Style.Description; POItem.Job.ID = JobReqItem.Requisition.Job.ID; POItem.Dimensions.UnitSize = JobReqItem.Dimensions.UnitSize; items.Add(POItem); } result.LoadRows(items); Progress.Close(); return result; } #endregion #region Utils private void SaveRow(CoreRow row, JobRequisitionItemStatus status, string note) { if (row == null) return; var id = row.Get(c => c.ID); JobRequisitionItem item = Data.Rows.Where(r => r.Get(c => c.ID).Equals(id)).FirstOrDefault().ToObject(); item.Status = status; item.Notes = item.Notes + Environment.NewLine + note; new Client().Save(item, "Updated From Job Requisition Review Dashboard"); OnGridRefresh?.Invoke(); } private void SaveItem(JobRequisitionItem item, JobRequisitionItemStatus status, string note) { item.Status = status; item.Notes = item.Notes + Environment.NewLine + note; new Client().Save(item, "Updated From Job Requisition Review Dashboard"); OnGridRefresh?.Invoke(); } private void MultiSaveRows(CoreRow[] rows, JobRequisitionItemStatus status, string note) { List items = new List(); foreach (CoreRow row in rows) { var id = row.Get(c => c.ID); JobRequisitionItem item = Data.Rows.Where(r => r.Get(c => c.ID).Equals(id)).FirstOrDefault().ToObject(); item.Status = status; item.Notes = item.Notes + Environment.NewLine + note; items.Add(item); } new Client().Save(items, "Updated From Job Requisition Review Dashboard"); OnGridRefresh?.Invoke(); } protected override void Reload(Filters criteria, Columns columns, ref SortOrder sort, Action action) { criteria.Add(new Filter(x => x.Requisition.Approved).IsNotEqualTo(DateTime.MinValue)); if (!bViewCancelled) criteria.Add(new Filter(x => x.Status).IsNotEqualTo(JobRequisitionItemStatus.Cancelled)); if (!bIncludeArchived) criteria.Add(new Filter(x => x.Status).IsNotEqualTo(JobRequisitionItemStatus.Archived)); sort = new SortOrder(x => x.Requisition.Number, SortDirection.Descending); base.Reload(criteria, columns, ref sort, action); } private void BuildMenu(DynamicMenuColumn column, CoreRow row) { // column.AddItem("Treatment Required", PRSDesktop.Resources.palette, TreatmentRequired_Clicked); column.AddItem("Order Required", PRSDesktop.Resources.purchase, OrderRequired_Clicked); //column.AddItem("Mark as Not Checked", PRSDesktop.Resources.disabled, Uncheck_Clicked); column.AddItem("Split Line", PRSDesktop.Resources.split, SplitLine_Clicked); column.AddItem("Archive", PRSDesktop.Resources.archive, Archive_Clicked); } #endregion } 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 = ""; } } }