瀏覽代碼

Added JobRequiItemPurchasOrderItem class, and fixed status calculations in the store. ReservationManagement screen removing dead code.

Kenric Nugteren 1 年之前
父節點
當前提交
adba43bb28

+ 50 - 6
prs.classes/Entities/Job/Requisitions/JobRequisitionItem.cs

@@ -11,15 +11,51 @@ namespace Comal.Classes
 {
     public enum JobRequisitionItemStatus
     {
-        NotChecked,
+        NotChecked, // Default
+        /// <summary>
+        /// All required stock has been received, and is in the correct <see cref="ProductStyle"/>.
+        /// </summary>
+        /// <remarks>
+        /// This can be set even if there are unreceived <see cref="JobRequisitionItemPurchaseOrderItem"/>,
+        /// since if we got the stock some other way, we still think of it as allocated.
+        /// </remarks>
         Allocated,
+        /// <summary>
+        /// All required stock has been received, but some is not in the correct <see cref="ProductStyle"/>, meaning a treatment is required.
+        /// </summary>
+        /// <remarks>
+        /// This can be set even if there are unreceived <see cref="JobRequisitionItemPurchaseOrderItem"/>,
+        /// since if we got the stock some other way, we still think of it as having the stock allocated.
+        /// </remarks>
         TreatmentRequired,
+        /// <summary>
+        /// The <see cref="JobRequisitionItem.OrderRequired"/> has been set, but there are no <see cref="JobRequisitionItemPurchaseOrderItem"/>s for 
+        /// this <see cref="JobRequisitionItem"/>.
+        /// </summary>
         OrderRequired,
-        TreatmentOnOrder,
+        /// <summary>
+        /// We don't yet have all the stock, and there is at least one unreceived <see cref="JobRequisitionItemPurchaseOrderItem"/> of type
+        /// <see cref="JobRequisitionItemPurchaseOrderItemType.Stock"/>.
+        /// </summary>
         OnOrder,
-        Received,
-        TreatmentReceived,
+        /// <summary>
+        /// We don't yet have all the stock, and there is at least one unreceived <see cref="JobRequisitionItemPurchaseOrderItem"/> of type
+        /// <see cref="JobRequisitionItemPurchaseOrderItemType.Treatment"/> and none of type <see cref="JobRequisitionItemPurchaseOrderItemType.Stock"/>.
+        /// </summary>
+        TreatmentOnOrder,
+
+        [Obsolete]
+        Received,// Drop
+        [Obsolete]
+        TreatmentReceived,// Drop
+
+        /// <summary>
+        /// The <see cref="JobRequisitionItem"/> has been cancelled, meaning it has a non-empty <see cref="JobRequisitionItem.Cancelled"/>.
+        /// </summary>
         Cancelled,
+        /// <summary>
+        /// The <see cref="JobRequisitionItem/"> has been archived, meaning it has a non-empty <see cref="JobRequisitionItem.Archived"/>.
+        /// </summary>
         Archived
     }
     public class JobRequisitionItemTotalQtyFormula : IFormula<JobRequisitionItem, double>
@@ -86,14 +122,22 @@ namespace Comal.Classes
         public JobRequisitionItemStatus Status { get; set; } = JobRequisitionItemStatus.NotChecked;
 
         [EntityRelationship(DeleteAction.SetNull)]
+        [RequiredColumn]
+        [Obsolete("Replaced with JobRequisitionItemPurchaseOrderItem")]
         public PurchaseOrderItemLink PurchaseOrderItem { get; set; }
 
+        [RequiredColumn]
         public DateTime Cancelled { get; set; } = DateTime.MinValue;
-        
+
+        [RequiredColumn]
         public DateTime Archived { get; set; } = DateTime.MinValue;
-        
+
+        [RequiredColumn]
         public DateTime Ordered { get; set; } = DateTime.MinValue;
 
+        [RequiredColumn]
+        public DateTime OrderRequired { get; set; } = DateTime.MinValue;
+
         [NullEditor]
         public long Sequence { get; set; }
 

+ 1 - 0
prs.classes/Entities/Job/Requisitions/JobRequisitionItemLink.cs

@@ -8,6 +8,7 @@ namespace Comal.Classes
     public class JobRequisitionItemLink : EntityLink<JobRequisitionItem>
     {
         [LookupEditor(typeof(JobRequisitionItem))]
+        [RequiredColumn]
         public override Guid ID { get; set; }
 
         [EntityRelationship(DeleteAction.Cascade)]

+ 15 - 0
prs.classes/Entities/Job/Requisitions/JobRequisitionItemPurchaseOrderItem.cs

@@ -0,0 +1,15 @@
+using InABox.Core;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Comal.Classes
+{
+    public class JobRequisitionItemPurchaseOrderItem : Entity, IOneToMany<JobRequisitionItem>
+    {
+        [NullEditor]
+        public JobRequisitionItemLink JobRequisitionItem { get; set; }
+
+        public PurchaseOrderItemLink PurchaseOrderItem { get; set; }
+    }
+}

+ 1 - 0
prs.classes/Entities/PurchaseOrder/PurchaseOrderItem.cs

@@ -131,6 +131,7 @@ namespace Comal.Classes
         public DateTime DueDate { get; set; }
 
         [NullEditor]
+        [Obsolete]
         public StockMovementLink StockMovement { get; set; }
 
         [EntityRelationship(DeleteAction.Cascade)]

+ 1 - 0
prs.classes/Entities/PurchaseOrder/PurchaseOrderItemLink.cs

@@ -26,6 +26,7 @@ namespace Comal.Classes
         public DateTime DueDate { get; set; }
 
         [DateEditor(Editable = Editable.Hidden)]
+        [RequiredColumn]
         public DateTime ReceivedDate { get; set; }
 
         [TextBoxEditor(Editable = Editable.Hidden)]

+ 1 - 0
prs.classes/Entities/Stock/StockMovement.cs

@@ -158,6 +158,7 @@ namespace Comal.Classes
         public PurchaseOrderItemLink OrderItem { get; set; }
 
         [NullEditor]
+        [RequiredColumn]
         public JobRequisitionItemLink JobRequisitionItem { get; set; }
 
         [Aggregate(typeof(StockMovementDocumentCount))]

+ 266 - 267
prs.desktop/Panels/Products/Reservation Management/JobRequisitionPurchasing.xaml.cs

@@ -8,331 +8,330 @@ using System.ComponentModel;
 using System.Linq;
 using System.Windows.Controls;
 
-namespace PRSDesktop
+namespace PRSDesktop;
+
+/// <summary>
+/// Interaction logic for JobRequisitionPurchasing.xaml
+/// </summary>
+public partial class JobRequisitionPurchasing : UserControl, IBasePanel, IDynamicEditorHost
 {
-    /// <summary>
-    /// Interaction logic for JobRequisitionPurchasing.xaml
-    /// </summary>
-    public partial class JobRequisitionPurchasing : UserControl, IBasePanel, IDynamicEditorHost
-    {
-        public delegate void PurchaseOrderSaved();
-        public event PurchaseOrderSaved OnPurchaseOrderSaved;
+    public delegate void PurchaseOrderSaved();
+    public event PurchaseOrderSaved OnPurchaseOrderSaved;
 
-        public List<JobRequisitionItem> JobRequiItems;
+    public List<JobRequisitionItem> JobRequiItems;
 
-        public event DataModelUpdateEvent? OnUpdateDataModel;
+    public event DataModelUpdateEvent? OnUpdateDataModel;
 
-        public PurchaseOrder Order { get; set; } = new PurchaseOrder();
+    public PurchaseOrder Order { get; set; } = new PurchaseOrder();
 
-        bool bLoaded = false;
-        public void LoadFromRequiLine()
-        {
-            if (bLoaded || JobRequiItems.Count == 0)
-                return;
-
-            var poid = JobRequiItems[0].PurchaseOrderItem.PurchaseOrderLink.ID;
-            if (poid != Guid.Empty && poid != Order.ID)
-                LoadPO(poid);              
-            else if (poid == Guid.Empty)
-                ClearEditor();
-        }
+    bool bLoaded = false;
+    public void LoadFromRequiLine()
+    {
+        if (bLoaded || JobRequiItems.Count == 0)
+            return;
+
+        var poid = JobRequiItems[0].PurchaseOrderItem.PurchaseOrderLink.ID;
+        if (poid != Guid.Empty && poid != Order.ID)
+            LoadPO(poid);              
+        else if (poid == Guid.Empty)
+            ClearEditor();
+    }
 
-        private void LoadPO(Guid poid)
+    private void LoadPO(Guid poid)
+    {
+        var po = new Client<PurchaseOrder>().Query(
+                    new Filter<PurchaseOrder>(x => x.ID).IsEqualTo(poid),
+                    new Columns<PurchaseOrder>
+                    (
+                        x => x.ID,
+                        x => x.SupplierLink.ID,
+                        x => x.SupplierLink.Code,
+                        x => x.SupplierLink.Name,
+                        x => x.Description,
+                        x => x.Category.ID,
+                        x => x.Category.Code,
+                        x => x.Category.Description,
+                        x => x.RaisedBy.ID,
+                        x => x.RaisedBy.Code,
+                        x => x.RaisedBy.Name,
+                        x => x.DueDate,
+                        x => x.IssuedBy.ID,
+                        x => x.IssuedBy.Code,
+                        x => x.IssuedBy.Name,
+                        x => x.IssuedDate,
+                        x => x.ClosedDate,
+                        x => x.PONumber
+                        ))
+                    .Rows.FirstOrDefault()?.ToObject<PurchaseOrder>();
+        if (po != null)
         {
-            var po = new Client<PurchaseOrder>().Query(
-                        new Filter<PurchaseOrder>(x => x.ID).IsEqualTo(poid),
-                        new Columns<PurchaseOrder>
-                        (
-                            x => x.ID,
-                            x => x.SupplierLink.ID,
-                            x => x.SupplierLink.Code,
-                            x => x.SupplierLink.Name,
-                            x => x.Description,
-                            x => x.Category.ID,
-                            x => x.Category.Code,
-                            x => x.Category.Description,
-                            x => x.RaisedBy.ID,
-                            x => x.RaisedBy.Code,
-                            x => x.RaisedBy.Name,
-                            x => x.DueDate,
-                            x => x.IssuedBy.ID,
-                            x => x.IssuedBy.Code,
-                            x => x.IssuedBy.Name,
-                            x => x.IssuedDate,
-                            x => x.ClosedDate,
-                            x => x.PONumber
-                            ))
-                        .Rows.FirstOrDefault()?.ToObject<PurchaseOrder>();
-            if (po != null)
-            {
-                Order = po;
-                CreatePOEditor(new BaseObject[] { po });
-            }
+            Order = po;
+            CreatePOEditor(new BaseObject[] { po });
         }
+    }
 
-        public IEnumerable<DynamicGridColumn> Columns => throw new NotImplementedException();
+    public IEnumerable<DynamicGridColumn> Columns => throw new NotImplementedException();
 
-        public bool IsReady { get; set; }
+    public bool IsReady { get; set; }
 
-        public string SectionName { get; set; }
+    public string SectionName { get; set; }
 
-        public JobRequisitionPurchasing()
-        {
-            InitializeComponent();
-            empName = new Client<Employee>().Query(new Filter<Employee>(x => x.UserLink.ID).IsEqualTo(ClientFactory.UserGuid)).Rows.FirstOrDefault().Get<Employee, string>(x => x.Name);
-            CreatePOEditor();
-            //JobRequiItems = new List<JobRequisitionItem>();
-        }
+    public JobRequisitionPurchasing()
+    {
+        InitializeComponent();
+        empName = new Client<Employee>().Query(new Filter<Employee>(x => x.UserLink.ID).IsEqualTo(ClientFactory.UserGuid)).Rows.FirstOrDefault().Get<Employee, string>(x => x.Name);
+        CreatePOEditor();
+        //JobRequiItems = new List<JobRequisitionItem>();
+    }
 
-        private string CheckDate(DateTime date)
-        {
-            return date.IsEmpty() ? "" : date.ToShortDateString();
-        }
+    private string CheckDate(DateTime date)
+    {
+        return date.IsEmpty() ? "" : date.ToShortDateString();
+    }
+
+    public bool EditorChanged {get; private set; }
+    private string empName = "";
 
-        public bool EditorChanged {get; private set; }
-        private string empName = "";
+    private void CreatePOEditor(BaseObject[]? items = null)
+    {
+        DetailBorder.Child = null;
+
+        Editor = new EmbeddedDynamicEditorForm();
+        Editor.SetLayoutType<VerticalDynamicEditorGridLayout>();
+        Editor.SetValue(Grid.RowProperty, 1);
+        Editor.SetValue(Grid.ColumnProperty, 0);
+        Editor.SetValue(Grid.ColumnSpanProperty, 4);
+        Editor.HighlightButtons = true;
+        Editor.HideButtons = true;
+        
+        EditorChanged = false;
+
+        Editor.OnAfterEditorValueChanged += (sender, args) =>
+        {
+            EditorChanged = true;
+            Editor.HideButtons = false;
+            return null;
+        };
 
-        private void CreatePOEditor(BaseObject[]? items = null)
+        Editor.OnOK += () =>
         {
-            DetailBorder.Child = null;
-
-            Editor = new EmbeddedDynamicEditorForm();
-            Editor.SetLayoutType<VerticalDynamicEditorGridLayout>();
-            Editor.SetValue(Grid.RowProperty, 1);
-            Editor.SetValue(Grid.ColumnProperty, 0);
-            Editor.SetValue(Grid.ColumnSpanProperty, 4);
-            Editor.HighlightButtons = true;
+            var cancel = new System.ComponentModel.CancelEventArgs();
+            Editor.SaveItem(cancel);
+            Editor.HideButtons = true;
+            UpdateJobRequiItems();
+            OnPurchaseOrderSaved?.Invoke();
+            EditorChanged = false;
+            bLoaded = false;
+        };
+        Editor.OnCancel += () =>
+        {                
             Editor.HideButtons = true;
-            
+            bLoaded = false;
+            ClearEditor();
             EditorChanged = false;
+        };
+        Editor.OnChanged += (sender, args) =>
+        {
+            EditorChanged = true;
+            Editor.HideButtons = false;
+        };
 
-            Editor.OnAfterEditorValueChanged += (sender, args) =>
-            {
-                EditorChanged = true;
-                Editor.HideButtons = false;
-                return null;
-            };
-
-            Editor.OnOK += () =>
-            {
-                var cancel = new System.ComponentModel.CancelEventArgs();
-                Editor.SaveItem(cancel);
-                Editor.HideButtons = true;
-                UpdateJobRequiItems();
-                OnPurchaseOrderSaved?.Invoke();
-                EditorChanged = false;
-                bLoaded = false;
-            };
-            Editor.OnCancel += () =>
-            {                
-                Editor.HideButtons = true;
-                bLoaded = false;
-                ClearEditor();
-                EditorChanged = false;
-            };
-            Editor.OnChanged += (sender, args) =>
-            {
-                EditorChanged = true;
-                Editor.HideButtons = false;
-            };
-
-            DetailBorder.Child = Editor;
-
-            var grid = DynamicGridUtils.CreateDynamicGrid(typeof(DynamicDataGrid<>), typeof(PurchaseOrder));
-            grid.InitialiseEditorForm(Editor, items ?? new object[] { Activator.CreateInstance(typeof(PurchaseOrder))! });
-
-            var loadPO = new Button
-            {
-                Content = "Load PO",
-                Height = 30,
-                Margin = new System.Windows.Thickness(2, 0, 0, 0),
-                Padding = new System.Windows.Thickness(15, 0, 15, 0)
-            };
-            loadPO.Click += LoadPO_Click;
-            Editor.AddButton(loadPO);
-        }
+        DetailBorder.Child = Editor;
 
-        private void LoadPO_Click(object sender, System.Windows.RoutedEventArgs e)
-        {
-            var popup = new PopupList(typeof(PurchaseOrder), Guid.Empty, new string[0]);
-            if(popup.ShowDialog() == true)
-            {
-                bLoaded = true;
-                LoadPO(popup.ID);
-            }
-        }
+        var grid = DynamicGridUtils.CreateDynamicGrid(typeof(DynamicDataGrid<>), typeof(PurchaseOrder));
+        grid.InitialiseEditorForm(Editor, items ?? new object[] { Activator.CreateInstance(typeof(PurchaseOrder))! });
 
-        private void UpdateJobRequiItems()
+        var loadPO = new Button
         {
-            var page = Editor.Pages.Find(x => x.GetType() == typeof(SupplierPurchaseOrderItemOneToMany)) as SupplierPurchaseOrderItemOneToMany;
-            if (page.Items.Count != 0)
-                MatchRequiItems(page.Items);
-        }
+            Content = "Load PO",
+            Height = 30,
+            Margin = new System.Windows.Thickness(2, 0, 0, 0),
+            Padding = new System.Windows.Thickness(15, 0, 15, 0)
+        };
+        loadPO.Click += LoadPO_Click;
+        Editor.AddButton(loadPO);
+    }
 
-        private void MatchRequiItems(List<PurchaseOrderItem> poItems)
+    private void LoadPO_Click(object sender, System.Windows.RoutedEventArgs e)
+    {
+        var popup = new PopupList(typeof(PurchaseOrder), Guid.Empty, new string[0]);
+        if(popup.ShowDialog() == true)
         {
-            List<JobRequisitionItem> toSave = new List<JobRequisitionItem>();
-            foreach (var JobReqItem in JobRequiItems)
-            {
-                if (JobReqItem.PurchaseOrderItem.ID == Guid.Empty)
-                    foreach (var item in poItems)
-                        if (MatchReqItemToPOItem(JobReqItem, item))
-                            toSave.Add(UpdateJobReqItemWithPODetails(JobReqItem, item));
-            }
-            if (toSave.Count > 0)
-                new Client<JobRequisitionItem>().Save(toSave, "Updated from Job Requi Review Create Purchase Order");
+            bLoaded = true;
+            LoadPO(popup.ID);
         }
+    }
+
+    private void UpdateJobRequiItems()
+    {
+        var page = Editor.Pages.Find(x => x.GetType() == typeof(SupplierPurchaseOrderItemOneToMany)) as SupplierPurchaseOrderItemOneToMany;
+        if (page.Items.Count != 0)
+            MatchRequiItems(page.Items);
+    }
 
-        private bool MatchReqItemToPOItem(JobRequisitionItem JobReqItem, PurchaseOrderItem item)
+    private void MatchRequiItems(List<PurchaseOrderItem> poItems)
+    {
+        List<JobRequisitionItem> toSave = new List<JobRequisitionItem>();
+        foreach (var JobReqItem in JobRequiItems)
         {
-            if (JobReqItem.Product.ID == item.Product.ID &&
-                                JobReqItem.Dimensions.UnitSize == item.Dimensions.UnitSize &&
-                                JobReqItem.Requisition.Job.ID == item.Job.ID)
-                return true;
-            else
-                return false;
+            if (JobReqItem.PurchaseOrderItem.ID == Guid.Empty)
+                foreach (var item in poItems)
+                    if (MatchReqItemToPOItem(JobReqItem, item))
+                        toSave.Add(UpdateJobReqItemWithPODetails(JobReqItem, item));
         }
+        if (toSave.Count > 0)
+            new Client<JobRequisitionItem>().Save(toSave, "Updated from Job Requi Review Create Purchase Order");
+    }
+
+    private bool MatchReqItemToPOItem(JobRequisitionItem JobReqItem, PurchaseOrderItem item)
+    {
+        if (JobReqItem.Product.ID == item.Product.ID &&
+                            JobReqItem.Dimensions.UnitSize == item.Dimensions.UnitSize &&
+                            JobReqItem.Requisition.Job.ID == item.Job.ID)
+            return true;
+        else
+            return false;
+    }
 
-        private JobRequisitionItem UpdateJobReqItemWithPODetails(JobRequisitionItem JobReqItem, PurchaseOrderItem poItem)
+    private JobRequisitionItem UpdateJobReqItemWithPODetails(JobRequisitionItem JobReqItem, PurchaseOrderItem poItem)
+    {
+        JobReqItem.PurchaseOrderItem.ID = poItem.ID;
+        JobReqItem.PurchaseOrderItem.DueDate = poItem.DueDate;
+        if (JobReqItem.Status != JobRequisitionItemStatus.OnOrder)
         {
-            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;
+            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 void ClearEditor()
-        {
-            Order = new PurchaseOrder();
-            CreatePOEditor();
+    private void ClearEditor()
+    {
+        Order = new PurchaseOrder();
+        CreatePOEditor();
 
-            //if (Popup is not null)
-            //{
-            //    Popup.Value = Guid.Empty;
-            //}
-        }
+        //if (Popup is not null)
+        //{
+        //    Popup.Value = Guid.Empty;
+        //}
+    }
 
-        public void DropItems(CoreRow[] rows)
-        {
-            var page = Editor.Pages.OfType<SupplierPurchaseOrderItemOneToMany>().First();
-            if (page.Items.Count == 0)
-                page.LoadItems(CreatePOItemsFromRequiItems(rows).ToArray());
-            else
-                page.LoadItems(CombineItems(rows, page.Items));
-        }
+    public void DropItems(CoreRow[] rows)
+    {
+        var page = Editor.Pages.OfType<SupplierPurchaseOrderItemOneToMany>().First();
+        if (page.Items.Count == 0)
+            page.LoadItems(CreatePOItemsFromRequiItems(rows).ToArray());
+        else
+            page.LoadItems(CombineItems(rows, page.Items));
+    }
 
-        private PurchaseOrderItem[] CombineItems(CoreRow[] selected, List<PurchaseOrderItem> existingItems)
-        {
-            List<PurchaseOrderItem> items = new List<PurchaseOrderItem>();
-            items.AddRange(existingItems);
-            items.AddRange(CreatePOItemsFromRequiItems(selected, existingItems));
-            return items.ToArray();
-        }
+    private PurchaseOrderItem[] CombineItems(CoreRow[] selected, List<PurchaseOrderItem> existingItems)
+    {
+        List<PurchaseOrderItem> items = new List<PurchaseOrderItem>();
+        items.AddRange(existingItems);
+        items.AddRange(CreatePOItemsFromRequiItems(selected, existingItems));
+        return items.ToArray();
+    }
 
-        private List<PurchaseOrderItem> CreatePOItemsFromRequiItems(CoreRow[] selected, List<PurchaseOrderItem>? comparison = null)
+    private List<PurchaseOrderItem> CreatePOItemsFromRequiItems(CoreRow[] selected, List<PurchaseOrderItem>? comparison = null)
+    {
+        List<PurchaseOrderItem> items = new List<PurchaseOrderItem>();
+        foreach (CoreRow row in selected)
         {
-            List<PurchaseOrderItem> items = new List<PurchaseOrderItem>();
-            foreach (CoreRow row in selected)
-            {
-                JobRequisitionItem JobReqItem = row.ToObject<JobRequisitionItem>();
-                JobRequiItems.Add(JobReqItem);
-                if (JobReqItem.PurchaseOrderItem.ID != Guid.Empty)
-                    continue;
-
-                PurchaseOrderItem POItem = new PurchaseOrderItem();
-                POItem.Product.ID = JobReqItem.Product.ID;
-                POItem.Product.Synchronise(JobReqItem.Product);
-                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.Job.JobNumber = JobReqItem.Requisition.Job.JobNumber;
-                POItem.Job.Name = JobReqItem.Requisition.Job.Name;
-                POItem.Dimensions.UnitSize = JobReqItem.Dimensions.UnitSize;
-                if (comparison != null && !Duplicated(POItem, comparison))
-                    items.Add(POItem);
-                else if (comparison == null)
-                    items.Add(POItem);
-            }
-            return items;
+            JobRequisitionItem JobReqItem = row.ToObject<JobRequisitionItem>();
+            JobRequiItems.Add(JobReqItem);
+            if (JobReqItem.PurchaseOrderItem.ID != Guid.Empty)
+                continue;
+
+            PurchaseOrderItem POItem = new PurchaseOrderItem();
+            POItem.Product.ID = JobReqItem.Product.ID;
+            POItem.Product.Synchronise(JobReqItem.Product);
+            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.Job.JobNumber = JobReqItem.Requisition.Job.JobNumber;
+            POItem.Job.Name = JobReqItem.Requisition.Job.Name;
+            POItem.Dimensions.UnitSize = JobReqItem.Dimensions.UnitSize;
+            if (comparison != null && !Duplicated(POItem, comparison))
+                items.Add(POItem);
+            else if (comparison == null)
+                items.Add(POItem);
         }
+        return items;
+    }
 
-        private bool Duplicated(PurchaseOrderItem newItem, List<PurchaseOrderItem> existing)
-        {
-            if (existing.Find(x =>
-                x.Product.ID == newItem.Product.ID
-                && x.Qty == newItem.Qty
-                && x.Style.ID == newItem.Style.ID
-                && x.Job.ID == newItem.Job.ID
-                && x.Dimensions.UnitSize == newItem.Dimensions.UnitSize
-                )
-                != null)
-                return true;
-
-            return false;
-        }
+    private bool Duplicated(PurchaseOrderItem newItem, List<PurchaseOrderItem> existing)
+    {
+        if (existing.Find(x =>
+            x.Product.ID == newItem.Product.ID
+            && x.Qty == newItem.Qty
+            && x.Style.ID == newItem.Style.ID
+            && x.Job.ID == newItem.Job.ID
+            && x.Dimensions.UnitSize == newItem.Dimensions.UnitSize
+            )
+            != null)
+            return true;
+
+        return false;
+    }
 
 
-        public void LoadColumns(string column, Dictionary<string, string> columns)
-        {
+    public void LoadColumns(string column, Dictionary<string, string> columns)
+    {
 
-        }
+    }
 
-        public IFilter? DefineFilter(Type type) => LookupFactory.DefineFilter(type);
+    public IFilter? DefineFilter(Type type) => LookupFactory.DefineFilter(type);
 
-        public void LoadLookups(ILookupEditorControl sender)
-        {
-            var editor = sender.EditorDefinition as ILookupEditor;
-            var colname = sender.ColumnName;
+    public void LoadLookups(ILookupEditorControl sender)
+    {
+        var editor = sender.EditorDefinition as ILookupEditor;
+        var colname = sender.ColumnName;
 
-            var values = editor.Values(colname, Editor.Items);
-            sender.LoadLookups(values);
-        }
+        var values = editor.Values(colname, Editor.Items);
+        sender.LoadLookups(values);
+    }
 
-        object?[] IDynamicEditorHost.GetItems() => Editor.Items;
+    object?[] IDynamicEditorHost.GetItems() => Editor.Items;
 
-        public BaseEditor? GetEditor(DynamicGridColumn column) => column.Editor.CloneEditor();
+    public BaseEditor? GetEditor(DynamicGridColumn column) => column.Editor.CloneEditor();
 
-        public void CreateToolbarButtons(IPanelHost host)
-        {
+    public void CreateToolbarButtons(IPanelHost host)
+    {
 
-        }
+    }
 
-        public Dictionary<string, object[]> Selected()
-        {
-            return new Dictionary<string, object[]>();
-        }
+    public Dictionary<string, object[]> Selected()
+    {
+        return new Dictionary<string, object[]>();
+    }
 
-        public void Heartbeat(TimeSpan time)
-        {
+    public void Heartbeat(TimeSpan time)
+    {
 
-        }
+    }
 
-        public void Setup()
-        {
+    public void Setup()
+    {
 
-        }
+    }
 
-        public void Shutdown(CancelEventArgs? cancel)
-        {
+    public void Shutdown(CancelEventArgs? cancel)
+    {
 
-        }
+    }
 
-        public void Refresh()
-        {
+    public void Refresh()
+    {
 
-        }
+    }
 
-        public DataModel DataModel(Selection selection)
-        {
-            return new EmptyDataModel();
-        }
+    public DataModel DataModel(Selection selection)
+    {
+        return new EmptyDataModel();
     }
 }

+ 204 - 564
prs.desktop/Panels/Products/Reservation Management/JobRequisitionReviewGrid.cs

@@ -3,6 +3,7 @@ using InABox.Clients;
 using InABox.Configuration;
 using InABox.Core;
 using InABox.DynamicGrid;
+using InABox.Wpf;
 using InABox.WPF;
 using System;
 using System.Collections.Generic;
@@ -11,612 +12,251 @@ using System.Linq;
 using System.Windows;
 using System.Windows.Controls;
 
-namespace PRSDesktop
-{
-    public class JobRequisitionReviewUserSettings : IUserConfigurationSettings
-    {
-        [Obsolete]
-        private CoreFilterDefinition? _currentFilter;
+namespace PRSDesktop;
 
-        [Obsolete]
-        public CoreFilterDefinition? CurrentFilter
-        {
-            get => _currentFilter;
-            set
-            {
-                if (value is not null)
-                {
-                    Filters = new DynamicGridSelectedFilterSettings(new List<CoreFilterDefinition> { value }, false, null);
-                }
-            }
-        }
-
-        public DynamicGridSelectedFilterSettings Filters { get; set; } = new();
-    }
+public class JobRequisitionReviewUserSettings : IUserConfigurationSettings
+{
+    [Obsolete]
+    private CoreFilterDefinition? _currentFilter;
 
-    public delegate void JobRequiItemSelect(CoreRow[] rows);
-    public delegate void GridRefresh();
-    public class JobRequisitionReviewGrid : DynamicDataGrid<JobRequisitionItem>
+    [Obsolete]
+    public CoreFilterDefinition? CurrentFilter
     {
-        public event JobRequiItemSelect OnJobRequiItemSelected;
-        public event GridRefresh OnGridRefresh;
-        public Guid empID = new Guid();
-        string empName = "";
-
-        JobRequisitionReviewUserSettings FilterSettings = new JobRequisitionReviewUserSettings();
-
-        Dictionary<Guid, double> JobRequisReservedQty = new Dictionary<Guid, double>();
-        public JobRequisitionReviewGrid()
-        {
-            FilterSettings = new UserConfiguration<JobRequisitionReviewUserSettings>().Load();
-            FilterComponent.SetSettings(FilterSettings.Filters, false);
-
-            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<JobRequisitionItem>())
-                ActionColumns.Add(new DynamicMenuColumn(BuildMenu));
-
-            ColumnsTag = "JobRequisitionReview";
-
-            CoreTable table = new Client<Employee>().Query(new Filter<Employee>(x => x.UserLink.UserID).IsEqualTo(ClientFactory.UserID), new Columns<Employee>(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();
-            }
-
-            FilterComponent.OnFiltersSelected += GridOnFilterSelected;
-        }
-        protected override void DoReconfigure(FluentList<DynamicGridOption> options)
-        {
-            base.DoReconfigure(options);
-
-            options.BeginUpdate().AddRange(
-                DynamicGridOption.FilterRows,
-                DynamicGridOption.SelectColumns,
-                DynamicGridOption.RecordCount,
-                DynamicGridOption.DragSource
-                )
-                .Remove(DynamicGridOption.AddRows)
-                .Remove(DynamicGridOption.ImportData)
-                .Remove(DynamicGridOption.ExportData)
-                .Remove(DynamicGridOption.Print)
-                .Remove(DynamicGridOption.ShowHelp)
-                .EndUpdate();
-        }
-
-        private void GridOnFilterSelected(DynamicGridSelectedFilterSettings settings)
-        {
-            new UserConfiguration<JobRequisitionReviewUserSettings>().Save(new JobRequisitionReviewUserSettings { Filters = settings });
-            OnGridRefresh?.Invoke();
-        }
-
-        private void LoadStockMovements()
-        {
-            CoreTable table = new Client<StockMovement>().Query(
-                new Filter<StockMovement>(x => x.JobRequisitionItem.ID).IsNotEqualTo(Guid.Empty),
-                new Columns<StockMovement>(
-                    x => x.JobRequisitionItem.ID,
-                    x => x.Received
-                    )
-                );
-            foreach (CoreRow row in table.Rows)
-            {
-                var requiID = row.Get<StockMovement, Guid>(x => x.JobRequisitionItem.ID);
-                var qty = row.Get<StockMovement, double>(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)
+        get => _currentFilter;
+        set
         {
-            CoreTable table = new Client<StockMovement>().Query(new Filter<StockMovement>(x => x.JobRequisitionItem.ID).IsEqualTo(requiItemID),
-                new Columns<StockMovement>(
-                    x => x.JobRequisitionItem.ID,
-                    x => x.Received
-                    )
-                );
-            if (table.Rows.Any())
+            if (value is not null)
             {
-                foreach (CoreRow row in table.Rows)
-                {
-                    var requiID = row.Get<StockMovement, Guid>(x => x.JobRequisitionItem.ID);
-                    var qty = row.Get<StockMovement, double>(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<JobRequisitionItem, Guid>(x => x.ID);
-
-            string status = "Not Checked";
-
-            if (row.Get<JobRequisitionItem, DateTime>(x => x.Cancelled) != DateTime.MinValue)
-                return "Cancelled";
-
-            if (row.Get<JobRequisitionItem, DateTime>(x => x.Archived) != DateTime.MinValue)
-                return "Archived";
-
-            if (row.Get<JobRequisitionItem, DateTime>(x => x.Ordered) != DateTime.MinValue && !JobRequisReservedQty.ContainsKey(id))
-                return "On Order";
-
-            if (row.Get<JobRequisitionItem, JobRequisitionItemStatus>(x => x.Status) == JobRequisitionItemStatus.OrderRequired)
-                return "Order Required";
-
-            if (JobRequisReservedQty.ContainsKey(id))
-                if (JobRequisReservedQty[id] >= row.Get<JobRequisitionItem, double>(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<JobRequisitionItem>();
-            //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;
-        }
-
-        protected override void SelectItems(CoreRow[]? rows)
-        {
-            base.SelectItems(rows);
-            OnJobRequiItemSelected?.Invoke(rows ?? Array.Empty<CoreRow>());
-        }
-
-        private bool CreateTreatmentPO(Button button, CoreRow[] rows)
-        {
-            return true;
-        }
-
-
-        public override DynamicGridColumns GenerateColumns()
-        {
-            var columns = new DynamicGridColumns();
-            columns.Add<JobRequisitionItem, DateTime>(x => x.Created, 80, "Date", "", Alignment.MiddleLeft);
-            columns.Add<JobRequisitionItem, string>(x => x.Requisition.Job.JobNumber, 70, "Job", "", Alignment.MiddleLeft);
-            columns.Add<JobRequisitionItem, int>(x => x.Requisition.Number, 50, "NO.", "", Alignment.MiddleLeft);
-            columns.Add<JobRequisitionItem, string>(x => x.Product.Code, 70, "Code", "", Alignment.MiddleLeft);
-            columns.Add<JobRequisitionItem, string>(x => x.Product.Name, 200, "Product Name", "", Alignment.MiddleLeft);
-            columns.Add<JobRequisitionItem, string>(x => x.Style.Description, 150, "Style", "", Alignment.MiddleLeft);
-            columns.Add<JobRequisitionItem, double>(x => x.Qty, 50, "Qty", "", Alignment.MiddleLeft);
-            columns.Add<JobRequisitionItem, string>(x => x.Dimensions.UnitSize, 50, "Size", "", Alignment.MiddleLeft);
-            columns.Add<JobRequisitionItem, string>(x => x.PurchaseOrderItem.PurchaseOrderLink.PONumber, 80, "PO Number", "", Alignment.MiddleLeft);
-            columns.Add<JobRequisitionItem, DateTime>(x => x.PurchaseOrderItem.DueDate, 80, "Due", "", Alignment.MiddleLeft);
-            columns.Add<JobRequisitionItem, DateTime>(x => x.PurchaseOrderItem.ReceivedDate, 80, "Received", "", Alignment.MiddleLeft);
-            columns.Add<JobRequisitionItem, string>(x => x.Notes, 300, "Notes", "", Alignment.MiddleLeft);
-            return columns;
-        }
-
-        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.Allocated && !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<JobRequisitionItem>();
-            if (CheckValidAction(item, false))
-            {
-                SaveRow(row, JobRequisitionItemStatus.Allocated, "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<JobRequisitionItem> items = new List<JobRequisitionItem>();
-            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<JobRequisitionItem>().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)
-        {
-            if (row is null) return;
-
-            var item = row.ToObject<JobRequisitionItem>();
-            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");
-                }
+                Filters = new DynamicGridSelectedFilterSettings(new List<CoreFilterDefinition> { value }, false, null);
             }
         }
+    }
 
-        private void Archive_Clicked(CoreRow? row)
-        {
-            if (row is null) return;
+    public DynamicGridSelectedFilterSettings Filters { get; set; } = new();
+}
 
-            var item = row.ToObject<JobRequisitionItem>();
-            item.Archived = DateTime.Now;
-            SaveRow(row, JobRequisitionItemStatus.Archived, "Line marked as Archived by " + empName + " on " + DateTime.Now.ToString("dd MMM yy"));
-        }
+public delegate void JobRequiItemSelect(CoreRow[] rows);
+public delegate void GridRefresh();
+public class JobRequisitionReviewGrid : DynamicDataGrid<JobRequisitionItem>
+{
+    private readonly JobRequisitionReviewUserSettings FilterSettings = new JobRequisitionReviewUserSettings();
 
-        private void OrderRequired_Clicked(CoreRow? row)
-        {
-            if (row is null) return;
+    public JobRequisitionReviewGrid()
+    {
+        FilterSettings = new UserConfiguration<JobRequisitionReviewUserSettings>().Load();
+        FilterComponent.SetSettings(FilterSettings.Filters, false);
+
+        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);
+
+        if (Security.CanEdit<JobRequisitionItem>())
+            ActionColumns.Add(new DynamicMenuColumn(BuildMenu));
+
+        ColumnsTag = "JobRequisitionReview";
+
+        FilterComponent.OnFiltersSelected += GridOnFilterSelected;
+    }
+    protected override void DoReconfigure(FluentList<DynamicGridOption> options)
+    {
+        base.DoReconfigure(options);
+
+        options.BeginUpdate().AddRange(
+            DynamicGridOption.FilterRows,
+            DynamicGridOption.SelectColumns,
+            DynamicGridOption.RecordCount,
+            DynamicGridOption.DragSource
+            )
+            .Remove(DynamicGridOption.AddRows)
+            .Remove(DynamicGridOption.ImportData)
+            .Remove(DynamicGridOption.ExportData)
+            .Remove(DynamicGridOption.Print)
+            .Remove(DynamicGridOption.ShowHelp)
+            .EndUpdate();
+    }
 
-            var item = row.ToObject<JobRequisitionItem>();
-            if (CheckValidAction(item, false))
-                SaveRow(row, JobRequisitionItemStatus.OrderRequired, "Line marked as Order Required by " + empName + " on " + DateTime.Now.ToString("dd MMM yy"));
-        }
+    private void GridOnFilterSelected(DynamicGridSelectedFilterSettings settings)
+    {
+        new UserConfiguration<JobRequisitionReviewUserSettings>().Save(new JobRequisitionReviewUserSettings { Filters = settings });
+        Refresh(false, true);
+    }
 
-        private void TreatmentRequired_Clicked(CoreRow row)
-        {
-            JobRequisitionItem item = row.ToObject<JobRequisitionItem>();
-            if (item.Status != JobRequisitionItemStatus.Allocated)
-            {
-                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"));
-        }
+    public override DynamicGridColumns GenerateColumns()
+    {
+        var columns = new DynamicGridColumns();
+        columns.Add<JobRequisitionItem, DateTime>(x => x.Created, 80, "Date", "", Alignment.MiddleLeft);
+        columns.Add<JobRequisitionItem, string>(x => x.Requisition.Job.JobNumber, 70, "Job", "", Alignment.MiddleLeft);
+        columns.Add<JobRequisitionItem, int>(x => x.Requisition.Number, 50, "NO.", "", Alignment.MiddleLeft);
+        columns.Add<JobRequisitionItem, string>(x => x.Product.Code, 70, "Code", "", Alignment.MiddleLeft);
+        columns.Add<JobRequisitionItem, string>(x => x.Product.Name, 200, "Product Name", "", Alignment.MiddleLeft);
+        columns.Add<JobRequisitionItem, string>(x => x.Style.Description, 150, "Style", "", Alignment.MiddleLeft);
+        columns.Add<JobRequisitionItem, double>(x => x.Qty, 50, "Qty", "", Alignment.MiddleLeft);
+        columns.Add<JobRequisitionItem, string>(x => x.Dimensions.UnitSize, 50, "Size", "", Alignment.MiddleLeft);
+        columns.Add<JobRequisitionItem, string>(x => x.PurchaseOrderItem.PurchaseOrderLink.PONumber, 80, "PO Number", "", Alignment.MiddleLeft);
+        columns.Add<JobRequisitionItem, DateTime>(x => x.PurchaseOrderItem.DueDate, 80, "Due", "", Alignment.MiddleLeft);
+        columns.Add<JobRequisitionItem, DateTime>(x => x.PurchaseOrderItem.ReceivedDate, 80, "Received", "", Alignment.MiddleLeft);
+        columns.Add<JobRequisitionItem, string>(x => x.Notes, 300, "Notes", "", Alignment.MiddleLeft);
+        return columns;
+    }
 
-        private void Uncheck_Clicked(CoreRow row)
-        {
-            string extraMessage = "";
-            JobRequisitionItem item = row.ToObject<JobRequisitionItem>();
-            if (!CheckValidAction(item, true))
-                return;
-            var table = Client.Query(
-                new Filter<StockMovement>(x => x.JobRequisitionItem.ID).IsEqualTo(item.ID),
-                new Columns<StockMovement>(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;
-                }
-                var movements = table.ToObjects<StockMovement>().ToList();
-                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:dd MMM yy}");
-        }
+    #region Action Column Buttons
 
-        private bool CreatePurchaseOrder(Button btn, CoreRow[] rows)
+    private bool CheckValidAction(JobRequisitionItem item)
+    {
+        bool valid = true;
+        if (item.Status == JobRequisitionItemStatus.Allocated)
         {
-            if (!rows.Any())
-            {
-                MessageBox.Show("Please select at least one row to add to Purchase Order!");
-                return false;
-            }
-
-            PurchaseOrder purchaseOrder = new PurchaseOrder();
-            purchaseOrder.Description = "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<PurchaseOrder>().ToArray());
-            };
-            return page.EditItems(new[] { purchaseOrder }, LoadPurchaseOrderItems, true);
+            MessageWindow.ShowMessage("Item has already been reserved!", "Error", image: MessageWindow.WarningImage);
+            return false;
         }
-
-        private void PurchaseOrderOnSave(IDynamicEditorForm form, PurchaseOrder[] items)
+        else if (item.Status == JobRequisitionItemStatus.OnOrder)
         {
-            Progress.Show("Working");
-
-            Guid POID = items[0].ID;
-
-            CoreTable table = new Client<PurchaseOrderItem>().Query(new Filter<PurchaseOrderItem>(x => x.PurchaseOrderLink.ID).IsEqualTo(POID),
-                new Columns<PurchaseOrderItem>(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<PurchaseOrderItem>());
-
-                var requiItems = MatchRequiItems(poItems, new List<JobRequisitionItem>());
-
-                if (requiItems.Count > 0)
-                    SaveAndRefreshScreen(requiItems);
-            }
-            Progress.Close();
+            MessageWindow.ShowMessage("Item is already on order!", "Error", image: MessageWindow.WarningImage);
+            return false;
         }
+        return valid;
+    }
 
-        private void SaveAndRefreshScreen(List<JobRequisitionItem> requiItems)
-        {
-            new Client<JobRequisitionItem>().Save(requiItems, "Updated on Create Purchase Order from Job Requi Dashboard");
-            OnGridRefresh?.Invoke();
-        }
+    private void SplitLine(JobRequisitionItem item, double oldItemQty, double newItemQty, string notes)
+    {
+        List<JobRequisitionItem> items = new List<JobRequisitionItem>();
+        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);
+        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, "Success");
+
+        Refresh(false, true);
+    }
 
-        private List<PurchaseOrderItem> AddPOItems(CoreTable table, List<PurchaseOrderItem> poItems)
-        {
-            foreach (CoreRow row in table.Rows)
-            {
-                PurchaseOrderItem poItem = row.ToObject<PurchaseOrderItem>();
-                poItems.Add(poItem);
-            }
-            return poItems;
-        }
+    private void SplitLine_Clicked(CoreRow? row)
+    {
+        if (row is null) return;
 
-        private List<JobRequisitionItem> MatchRequiItems(List<PurchaseOrderItem> poItems, List<JobRequisitionItem> requiItems)
+        var item = row.ToObject<JobRequisitionItem>();
+        if (CheckValidAction(item))
         {
-            foreach (CoreRow row in SelectedRows)
+            int units = Convert.ToInt32(item.Qty);
+            if (NumberEdit.Execute("Enter amount to split", 1, units, ref units))
             {
-                JobRequisitionItem JobReqItem = row.ToObject<JobRequisitionItem>();
-                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));
-                }
+                SplitLine(item, item.Qty - units, units, "Line split");
             }
-            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 void Archive_Clicked(CoreRow? row)
+    {
+        if (row is null) return;
 
-        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;
-        }
+        var item = row.ToObject<JobRequisitionItem>();
+        item.Archived = DateTime.Now;
+        item.Notes += Environment.NewLine + "Line marked as Archived by " + App.EmployeeName + " on " + DateTime.Now.ToString("dd MMM yy");
 
-        private string QueryUnitSize(Guid productID)
-        {
-            var table = new Client<Product>().Query(new Filter<Product>(x => x.ID).IsEqualTo(productID),
-                new Columns<Product>(x => x.DefaultInstance.Dimensions.UnitSize));
-            return table.Rows.FirstOrDefault().Get<Product, string>(x => x.DefaultInstance.Dimensions.UnitSize);
-        }
+        Client.Save(item, "Updated From Job Requisition Review Dashboard");
 
-        private Guid QueryJobID(Guid iD)
-        {
-            CoreTable table = new Client<JobRequisition>().Query(new Filter<JobRequisition>(x => x.ID).IsEqualTo(iD),
-                new Columns<JobRequisition>(x => x.Job.ID));
-            return table.Rows.FirstOrDefault().Get<JobRequisition, Guid>(x => x.Job.ID);
-        }
+        Refresh(false, true);
+    }
 
-        private CoreTable LoadPurchaseOrderItems(Type arg)
-        {
-            Progress.Show("Working");
-            var result = new CoreTable();
-            result.LoadColumns(typeof(PurchaseOrderItem));
-            List<PurchaseOrderItem> items = new List<PurchaseOrderItem>();
-            foreach (CoreRow row in SelectedRows)
-            {
-                JobRequisitionItem JobReqItem = row.ToObject<JobRequisitionItem>();
-                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
+    private void OrderRequired_Clicked(CoreRow? row)
+    {
+        if (row is null) return;
 
-        #region Utils
-        private void SaveRow(CoreRow row, JobRequisitionItemStatus status, string note)
+        var item = row.ToObject<JobRequisitionItem>();
+        if (CheckValidAction(item))
         {
-            if (row == null)
-                return;
-            var id = row.Get<JobRequisitionItem, Guid>(c => c.ID);
-            JobRequisitionItem item = Data.Rows.Where(r => r.Get<JobRequisitionItem, Guid>(c => c.ID).Equals(id)).FirstOrDefault().ToObject<JobRequisitionItem>();
-            item.Status = status;
-            item.Notes = item.Notes + Environment.NewLine + note;
-            new Client<JobRequisitionItem>().Save(item, "Updated From Job Requisition Review Dashboard");
+            item.OrderRequired = DateTime.Now;
+            item.Notes += Environment.NewLine + "Line marked as Order Required by " + App.EmployeeName + " on " + DateTime.Now.ToString("dd MMM yy");
 
-            OnGridRefresh?.Invoke();
+            Client.Save(item, "Updated From Job Requisition Review Dashboard");
 
+            Refresh(false, true);
         }
+    }
 
-        private void SaveItem(JobRequisitionItem item, JobRequisitionItemStatus status, string note)
-        {
-            item.Status = status;
-            item.Notes = item.Notes + Environment.NewLine + note;
-            new Client<JobRequisitionItem>().Save(item, "Updated From Job Requisition Review Dashboard");
-            OnGridRefresh?.Invoke();
-        }
-
-        private void MultiSaveRows(CoreRow[] rows, JobRequisitionItemStatus status, string note)
-        {
-            List<JobRequisitionItem> items = new List<JobRequisitionItem>();
-            foreach (CoreRow row in rows)
-            {
-                var id = row.Get<JobRequisitionItem, Guid>(c => c.ID);
-                JobRequisitionItem item = Data.Rows.Where(r => r.Get<JobRequisitionItem, Guid>(c => c.ID).Equals(id)).FirstOrDefault().ToObject<JobRequisitionItem>();
-                item.Status = status;
-                item.Notes = item.Notes + Environment.NewLine + note;
-                items.Add(item);
-            }
-            new Client<JobRequisitionItem>().Save(items, "Updated From Job Requisition Review Dashboard");
-            OnGridRefresh?.Invoke();
-        }
-
-
-        protected override void Reload(Filters<JobRequisitionItem> criteria, Columns<JobRequisitionItem> columns, ref SortOrder<JobRequisitionItem>? sort, Action<CoreTable?, Exception?> action)
-        {
-            criteria.Add(new Filter<JobRequisitionItem>(x => x.Requisition.Approved).IsNotEqualTo(DateTime.MinValue));
-
-            criteria.Add(new Filter<JobRequisitionItem>(x => x.Archived).IsEqualTo(DateTime.MinValue));
+    #endregion
 
-            sort = new SortOrder<JobRequisitionItem>(x => x.Requisition.Number, SortDirection.Descending);
-            base.Reload(criteria, columns, ref sort, action);
-        }
+    protected override void Reload(Filters<JobRequisitionItem> criteria, Columns<JobRequisitionItem> columns, ref SortOrder<JobRequisitionItem>? sort, Action<CoreTable?, Exception?> action)
+    {
+        criteria.Add(new Filter<JobRequisitionItem>(x => x.Requisition.Approved).IsNotEqualTo(DateTime.MinValue));
 
+        criteria.Add(new Filter<JobRequisitionItem>(x => x.Archived).IsEqualTo(DateTime.MinValue));
 
-        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
+        sort = new SortOrder<JobRequisitionItem>(x => x.Requisition.Number, SortDirection.Descending);
+        base.Reload(criteria, columns, ref sort, action);
     }
 
-    public class JobRequiReviewDashboardFilterItem
+    private void BuildMenu(DynamicMenuColumn column, CoreRow? row)
     {
-        public Guid SupplierID { get; set; }
-        public Guid ProductID { get; set; }
-        public string Text { get; set; }
+        column.AddItem("Order Required", PRSDesktop.Resources.purchase, OrderRequired_Clicked);
+        column.AddItem("Split Line", PRSDesktop.Resources.split, SplitLine_Clicked);
+        column.AddItem("Archive", PRSDesktop.Resources.archive, Archive_Clicked);
+    }
+}
 
-        public JobRequiReviewDashboardFilterItem()
-        {
-            SupplierID = Guid.Empty;
-            ProductID = Guid.Empty;
-            Text = "";
-        }
+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 = "";
     }
 }

+ 15 - 59
prs.desktop/Panels/Products/Reservation Management/JobRequisitionsPanel.xaml.cs

@@ -14,6 +14,7 @@ using System.Windows.Controls.Primitives;
 using System.Windows.Input;
 using System.Windows.Media;
 using InABox.WPF.Themes;
+using NPOI.SS.Formula.Functions;
 
 namespace PRSDesktop
 {
@@ -112,35 +113,8 @@ namespace PRSDesktop
 
         private void ConfigSettingsClick(PanelAction obj)
         {
-            var pages = new DynamicEditorPages();
-            var buttons = new DynamicEditorButtons();
-            buttons.Add(
-                "",
-                PRSDesktop.Resources.help.AsBitmapImage(),
-                _settings,
-                (f, i) =>
-                {
-                    Process.Start(new ProcessStartInfo("https://prsdigital.com.au/wiki/index.php/" + typeof(Equipment).Name.SplitCamelCase().Replace(" ", "_"))
-                    { UseShellExecute = true });
-                }
-            );
-            var propertyEditor = new DynamicEditorForm(typeof(JobRequisitionPanelSettings), pages, buttons);
-            propertyEditor.OnDefineLookups += sender =>
-            {
-                var editor = sender.EditorDefinition as ILookupEditor;
-                var colname = sender.ColumnName;
-                var values = editor.Values(colname, new[] { _settings });
-                sender.LoadLookups(values);
-            };
-            propertyEditor.OnEditorValueChanged += (sender, name, value) =>
-            {
-                CoreUtils.SetPropertyValue(_settings, name, value);
-                return new Dictionary<string, object?>();
-            };
-
-            propertyEditor.Items = new BaseObject[] { _settings };
-
-            if (propertyEditor.ShowDialog() == true)
+            var grid = new DynamicItemsListGrid<JobRequisitionPanelSettings>();
+            if(grid.EditItems(new JobRequisitionPanelSettings[] { _settings }))
             {
                 new GlobalConfiguration<JobRequisitionPanelSettings>().Save(_settings);
                 holdings.CompanyDefaultStyle = _settings.ProductStyle;
@@ -173,46 +147,33 @@ namespace PRSDesktop
         public void Setup()
         {
             Mode = PanelMode.Reserve;
-            SetupJobRequiItemGrids();
+
+            JobRequiItems.OnSelectItem += OnJobRequiItemSelected;
+            JobRequiItems.Refresh(true, false);
 
             _settings = new GlobalConfiguration<JobRequisitionPanelSettings>().Load();
             holdings.CompanyDefaultStyle = _settings.ProductStyle;
 
             holdings.OnHoldingsReviewRefresh += Holdings_OnHoldingsReviewRefresh;
 
-            purchasing.OnPurchaseOrderSaved += () => { RefreshJobRequiGrids(); };
-        }
-
-
-        private void Holdings_OnHoldingsReviewRefresh()
-        {
-            Refresh();
-            RefreshJobRequiGrids();
-        }
-
-        private void RefreshJobRequiGrids()
-        {
-            JobRequiItems.bRefreshing = false;           
-            JobRequiItems.Refresh(false, true);
+            purchasing.OnPurchaseOrderSaved += Refresh;
         }
 
-        private void SetupJobRequiItemGrids()
+        private void OnJobRequiItemSelected(object sender, DynamicGridSelectionEventArgs e)
         {
-            JobRequiItems.OnGridRefresh += JobRequiItems_OnGridRefresh;
-            JobRequiItems.OnJobRequiItemSelected += OnJobRequiItemSelected;
-            JobRequiItems.Refresh(true, true);
-        }
-
-        private void OnJobRequiItemSelected(CoreRow[] rows)
-        {
-            holdings.Item = rows.FirstOrDefault()?.ToObject<JobRequisitionItem>();
+            holdings.Item = e.Rows?.FirstOrDefault()?.ToObject<JobRequisitionItem>();
             if (mode == PanelMode.Purchase)
             {
-                purchasing.JobRequiItems = rows.ToObjects<JobRequisitionItem>().ToList();
+                purchasing.JobRequiItems = e.Rows?.ToObjects<JobRequisitionItem>().ToList() ?? new List<JobRequisitionItem>();
                 purchasing.LoadFromRequiLine();
             }
         }
 
+        private void Holdings_OnHoldingsReviewRefresh()
+        {
+            Refresh();
+        }
+
         private List<JobRequisitionItem> CreateList(CoreRow[] rows)
         {
             List<JobRequisitionItem> list = new List<JobRequisitionItem>();
@@ -222,11 +183,6 @@ namespace PRSDesktop
             return list;
         }
 
-        private void JobRequiItems_OnGridRefresh()
-        {
-            RefreshJobRequiGrids();
-        }
-
         private void CheckSaved(CancelEventArgs cancel)
         {
             if (!purchasing.EditorChanged)

+ 0 - 1
prs.desktop/Panels/Products/Reservation Management/StockSelectionPage.xaml.cs

@@ -181,7 +181,6 @@ namespace PRSDesktop
             List<JobRequisitionItem> items = new List<JobRequisitionItem>();
             JobRequisitionItem newItem = new JobRequisitionItem();
             newItem.Requisition.ID = item.Requisition.ID;
-            newItem.Status = JobRequisitionItemStatus.NotChecked;
             newItem.Requisition.Job.ID = item.Requisition.Job.ID;
             newItem.Requisition.Job.JobNumber = item.Requisition.Job.JobNumber;
             newItem.Requisition.Job.Name = item.Requisition.Job.Name;

+ 146 - 58
prs.stores/JobRequisitionItemStore.cs

@@ -1,89 +1,177 @@
 using Comal.Classes;
 using Comal.Stores;
 using InABox.Core;
+using InABox.Database;
+using RazorEngine.Compilation.ImpromptuInterface.InvokeExt;
 using System;
+using System.Collections.Generic;
 using System.Linq;
 
-namespace PRSStores
+namespace PRSStores;
+
+public class JobRequisitionItemStore : BaseStore<JobRequisitionItem>
 {
-    public class JobRequisitionItemStore : BaseStore<JobRequisitionItem>
+    protected override void BeforeSave(JobRequisitionItem item)
     {
-        protected override void BeforeSave(JobRequisitionItem item)
+        if (item.ID != Guid.Empty)
         {
-            if (item.ID != Guid.Empty)
+            var newStatus = CalculateStatus(this, item.ID);
+            if(newStatus != null)
             {
-                var table = DoQuery(item);
-                item = DoStatusChecks(item, table);
+                item.Status = newStatus.Value;
             }
-            base.BeforeSave(item);
         }
+        base.BeforeSave(item);
+    }
+
+    private static Columns<JobRequisitionItem> StatusRequiredColumns()
+    {
+        return new Columns<JobRequisitionItem>(
+            x => x.ID,
+            x => x.Archived,
+            x => x.Cancelled,
+            x => x.OrderRequired,
+            x => x.Status,
+            x => x.Style.ID,
+            x => x.Product.ID,
+            x => x.Qty);
+    }
 
-        private CoreTable DoQuery(JobRequisitionItem item)
+    private static JobRequisitionItemStatus? CalculateStatus(IStore store, JobRequisitionItem item)
+    {
+        if (item.Archived != DateTime.MinValue)
         {
-            return Provider.Query<StockMovement>(
-               new Filter<StockMovement>(x => x.JobRequisitionItem.ID).IsEqualTo(item.ID),
-               new Columns<StockMovement>(
-                   x => x.Received,
-                    x => x.Style.ID
-                   ));
+            return JobRequisitionItemStatus.Archived;
         }
-
-        public static JobRequisitionItem DoStatusChecks(JobRequisitionItem item, CoreTable table)
+        else if (item.Cancelled != DateTime.MinValue)
+        {
+            return JobRequisitionItemStatus.Cancelled;
+        }
+        else
         {
-            if (item.Cancelled != DateTime.MinValue)
-                item.Status = JobRequisitionItemStatus.Cancelled;
-            else if (item.Archived != DateTime.MinValue)
-                item.Status = JobRequisitionItemStatus.Archived;
-            //this can only get set from the "Create Treatment PO" custom module
-            else if (item.Status == JobRequisitionItemStatus.TreatmentOnOrder || item.Status == JobRequisitionItemStatus.TreatmentReceived)
+            var stockMovements = store.Provider.Query(
+                new Filter<StockMovement>(x => x.JobRequisitionItem.ID).IsEqualTo(item.ID),
+                new Columns<StockMovement>(
+                    x => x.Units,
+                    x => x.Style.ID))
+                .ToObjects<StockMovement>();
+            var styleTotal = 0.0;
+            var total = 0.0;
+            foreach (var mvt in stockMovements)
             {
-                if (item.PurchaseOrderItem.ReceivedDate != DateTime.MinValue)
-                    item.Status = JobRequisitionItemStatus.TreatmentReceived;
+                if (mvt.Style.ID == item.Style.ID)
+                {
+                    styleTotal += mvt.Units;
+                }
+                total += mvt.Units;
             }
-            else if (item.Ordered != DateTime.MinValue && item.PurchaseOrderItem.ReceivedDate == DateTime.MinValue)
-                item.Status = JobRequisitionItemStatus.OnOrder;
-            else if (item.PurchaseOrderItem.ReceivedDate != DateTime.MinValue)
+
+            var remStyle = item.Qty - styleTotal;
+            var remTotal = item.Qty - total;
+
+            if (remStyle <= 0)
+            {
+                return JobRequisitionItemStatus.Allocated;
+            }
+            else if (remTotal <= 0)
             {
-                item.Status = JobRequisitionItemStatus.Received;
-                item = CheckReservedOrTreatmentRequired(item, table);
+                // Find all unreceived POItems for this guy that are treatments (i.e., wrong product ID).
+                var jriPois = store.Provider.Query(
+                    new Filter<JobRequisitionItemPurchaseOrderItem>(x => x.JobRequisitionItem.ID).IsEqualTo(item.ID)
+                        .And(x => x.PurchaseOrderItem.ReceivedDate).IsEqualTo(DateTime.MinValue)
+                        .And(x => x.PurchaseOrderItem.Product.ID).IsNotEqualTo(item.Product.ID),
+                    new Columns<JobRequisitionItemPurchaseOrderItem>());
+                if (jriPois.Rows.Count > 0)
+                {
+                    return JobRequisitionItemStatus.TreatmentOnOrder;
+                }
+                else
+                {
+                    return JobRequisitionItemStatus.TreatmentRequired;
+                }
             }
-            else if (item.Status == JobRequisitionItemStatus.OrderRequired)
-                return item;
             else
-                item = CheckReservedOrTreatmentRequired(item, table);
-
-            return item;
-        }
-
+            {
+                // Find all unreceived POItems for this guy.
+                var jriPois = store.Provider.Query(
+                    new Filter<JobRequisitionItemPurchaseOrderItem>(x => x.JobRequisitionItem.ID).IsEqualTo(item.ID)
+                        .And(x => x.PurchaseOrderItem.ReceivedDate).IsEqualTo(DateTime.MinValue),
+                    new Columns<JobRequisitionItemPurchaseOrderItem>(x => x.PurchaseOrderItem.Product.ID)
+                        .Add(x => x.PurchaseOrderItem.Qty))
+                    .ToObjects<JobRequisitionItemPurchaseOrderItem>()
+                    .ToList();
+                var stockOrders = jriPois.Where(x => x.PurchaseOrderItem.Product.ID == item.Product.ID).ToList();
+                var treatmentOrders = jriPois.Where(x => x.PurchaseOrderItem.Product.ID != item.Product.ID).ToList();
 
+                remTotal -= stockOrders.Sum(x => x.PurchaseOrderItem.Qty);
+                if (remTotal <= 0)
+                {
+                    if (stockOrders.Count > 0)
+                    {
+                        return JobRequisitionItemStatus.OnOrder;
+                    }
+                    else
+                    {
+                        // This should be impossible to reach. We are at this point because remTotal <= 0, but stockOrders was an empty list. Therefore
+                        // remTotal is was <= 0 before checking PurchaseOrderItems, but then we should be TreatmentRequired, as above.
+                        Logger.Send(LogType.Error, store.UserID, $"Internal assertion failed: there is enough stock, but we didn't reach the correct clause.");
 
-        private static JobRequisitionItem CheckReservedOrTreatmentRequired(JobRequisitionItem item, CoreTable table)
-        {
-            if (!table.Rows.Any())
-            {
-                item.Status = JobRequisitionItemStatus.NotChecked;
-                return item;
+                        if (treatmentOrders.Count > 0)
+                        {
+                            return JobRequisitionItemStatus.TreatmentOnOrder;
+                        }
+                        else
+                        {
+                            return JobRequisitionItemStatus.TreatmentRequired;
+                        }
+                    }
+                }
+                else if (item.OrderRequired != DateTime.MinValue)
+                {
+                    return JobRequisitionItemStatus.OrderRequired;
+                }
+                else
+                {
+                    // Even after all the orders have come through, we still don't have enough. We must order more.
+                    return JobRequisitionItemStatus.NotChecked;
+                }
             }
+        }
+    }
 
-            bool treatmentRequired = false;
-            double total = 0;
-            foreach (var row in table.Rows)
-            {
-                total = total + row.Get<StockMovement, double>(x => x.Received);
-                if (row.Get<StockMovement, Guid>(x => x.Style.ID) != item.Style.ID)
-                    treatmentRequired = true;
-            }
+    public static JobRequisitionItemStatus? CalculateStatus(IStore store, Guid jobRequiItemID)
+    {
+        var item = store.Provider.Query(
+            new Filter<JobRequisitionItem>(x => x.ID).IsEqualTo(jobRequiItemID),
+            StatusRequiredColumns())
+            .ToObjects<JobRequisitionItem>()
+            .FirstOrDefault();
+        if(item is null)
+        {
+            Logger.Send(LogType.Error, store.UserID, $"No {nameof(JobRequisitionItem)} with ID {jobRequiItemID}");
+            return null;
+        }
+        return CalculateStatus(store, item);
+    }
 
-            if (total >= item.Qty)
+    public static void UpdateStatus(IStore store, Guid jobRequiItemID)
+    {
+        var item = store.Provider.Query(
+            new Filter<JobRequisitionItem>(x => x.ID).IsEqualTo(jobRequiItemID),
+            StatusRequiredColumns())
+            .ToObjects<JobRequisitionItem>()
+            .FirstOrDefault();
+        if (item is null)
+        {
+            Logger.Send(LogType.Error, store.UserID, $"No {nameof(JobRequisitionItem)} with ID {jobRequiItemID}");
+        }
+        else
+        {
+            var newStatus = CalculateStatus(store, item);
+            if (newStatus.HasValue)
             {
-                item.Status = JobRequisitionItemStatus.Allocated;
-                if (treatmentRequired)
-                    item.Status = JobRequisitionItemStatus.TreatmentRequired;
+                item.Status = newStatus.Value;
             }
-            else
-                item.Status = JobRequisitionItemStatus.NotChecked;
-
-            return item;
         }
     }
 }

+ 305 - 321
prs.stores/PurchaseOrderItemStore.cs

@@ -4,374 +4,358 @@ using System.Linq;
 using System.Threading.Tasks;
 using Comal.Classes;
 using InABox.Core;
+using PRSStores;
 
-namespace Comal.Stores
+namespace Comal.Stores;
+
+internal class PurchaseOrderItemStore : BaseStore<PurchaseOrderItem>
 {
-    internal class PurchaseOrderItemStore : BaseStore<PurchaseOrderItem>
+
+    private void UpdateStockMovements(PurchaseOrderItem entity)
     {
+        if (!entity.Product.IsValid())
+        {
+            Logger.Send(LogType.Information, UserID, "PurchaseOrderItem.Product.ID is blank!");
+            return;
+        }
+        
+        if (entity.Qty == 0)
+        {
+            Logger.Send(LogType.Information, UserID, "PurchaseOrderItem Qty is blank!");
+            return;
+        }
 
-        private void UpdateStockMovements(PurchaseOrderItem entity)
+        var locationid = entity.StockLocation.ID;
+        var locationValid = entity.StockLocation.IsValid();
+
+        var jobRequisitionItemTask = Task<Guid>.Run(() =>
         {
-            if (!entity.Product.IsValid())
-            {
-                Logger.Send(LogType.Information, UserID, "PurchaseOrderItem.Product.ID is blank!");
-                return;
-            }
-            
-            if (entity.Qty == 0)
-            {
-                Logger.Send(LogType.Information, UserID, "PurchaseOrderItem Qty is blank!");
-                return;
-            }
+            return Provider.Query(
+                new Filter<JobRequisitionItemPurchaseOrderItem>(x => x.PurchaseOrderItem.ID).IsEqualTo(entity.ID),
+                new Columns<JobRequisitionItemPurchaseOrderItem>(x => x.JobRequisitionItem.ID))
+                .ToObjects<JobRequisitionItemPurchaseOrderItem>().FirstOrDefault()?.JobRequisitionItem.ID ?? Guid.Empty;
+        });
 
-            var locationid = entity.StockLocation.ID;
-            var locationValid = entity.StockLocation.IsValid();
+        var movementtask = new Task<List<StockMovement>>(() =>
+        {
+            var result = Provider.Query(
+                new Filter<StockMovement>(x => x.OrderItem.ID).IsEqualTo(entity.ID),
+                new Columns<StockMovement>(
+                    x => x.ID,
+                    x => x.Date,
+                    x => x.Product.ID,
+                    x => x.Received,
+                    x => x.Employee.ID,
+                    x => x.OrderItem.ID,
+                    x => x.Job.ID,
+                    x => x.Location.ID,
+                    x => x.Dimensions.Unit.ID,
+                    x => x.Dimensions.Unit.Formula,
+                    x => x.Dimensions.Unit.Format,
+                    x => x.Dimensions.Quantity,
+                    x => x.Dimensions.Length,
+                    x => x.Dimensions.Width,
+                    x => x.Dimensions.Height,
+                    x => x.Dimensions.Weight,
+                    x => x.Notes,
+                    x => x.Cost,
+                    x => x.Dimensions.Unit.HasHeight,
+                    x => x.Dimensions.Unit.HasLength,
+                    x => x.Dimensions.Unit.HasWidth,
+                    x => x.Dimensions.Unit.HasWeight,
+                    x => x.Dimensions.Unit.HasQuantity,
+                    x => x.Dimensions.Unit.Formula,
+                    x => x.Dimensions.Unit.Format,
+                    x => x.Dimensions.Unit.Code,
+                    x => x.Dimensions.Unit.Description
+                )
+            ).Rows.Select(x => x.ToObject<StockMovement>()).ToList();
+            if (!result.Any())
+                result.Add(new StockMovement());
+            return result;
+        });
+        movementtask.Start();
 
-            var movementtask = new Task<List<StockMovement>>(() =>
-            {
-                var result = Provider.Query(
-                    new Filter<StockMovement>(x => x.OrderItem.ID).IsEqualTo(entity.ID),
-                    new Columns<StockMovement>(
-                        x => x.ID,
-                        x => x.Date,
-                        x => x.Product.ID,
-                        x => x.Received,
-                        x => x.Employee.ID,
-                        x => x.OrderItem.ID,
-                        x => x.Job.ID,
-                        x => x.Location.ID,
-                        x => x.Dimensions.Unit.ID,
-                        x => x.Dimensions.Unit.Formula,
-                        x => x.Dimensions.Unit.Format,
-                        x => x.Dimensions.Quantity,
-                        x => x.Dimensions.Length,
-                        x => x.Dimensions.Width,
-                        x => x.Dimensions.Height,
-                        x => x.Dimensions.Weight,
-                        x => x.Notes,
-                        x => x.Cost,
-                        x => x.Dimensions.Unit.HasHeight,
-                        x => x.Dimensions.Unit.HasLength,
-                        x => x.Dimensions.Unit.HasWidth,
-                        x => x.Dimensions.Unit.HasWeight,
-                        x => x.Dimensions.Unit.HasQuantity,
-                        x => x.Dimensions.Unit.Formula,
-                        x => x.Dimensions.Unit.Format,
-                        x => x.Dimensions.Unit.Code,
-                        x => x.Dimensions.Unit.Description
-                    )
-                ).Rows.Select(x => x.ToObject<StockMovement>()).ToList();
-                if (!result.Any())
-                    result.Add(new StockMovement());
-                return result;
-            });
-            movementtask.Start();
+        var instancetask = new Task<CoreRow?>(() =>
+        {
+            return Provider.Query(
+                new Filter<ProductInstance>(x => x.Product.ID).IsEqualTo(entity.Product.ID)
+                    .And(x=>x.Style.ID).IsEqualTo(entity.Style.ID)
+                    .And(x=>x.Dimensions.UnitSize).IsEqualTo(entity.Dimensions.UnitSize),
+                new Columns<ProductInstance>(
+                    x => x.ID,
+                    x => x.Product.NonStock,
+                    x => x.Product.DefaultLocation.ID,
+                    x => x.Product.Warehouse.ID,
+                    x => x.Dimensions.Unit.ID,
+                    x => x.Dimensions.Unit.Formula,
+                    x => x.Dimensions.Unit.Format,
+                    x => x.Dimensions.Quantity,
+                    x => x.Dimensions.Length,
+                    x => x.Dimensions.Width,
+                    x => x.Dimensions.Height,
+                    x => x.Dimensions.Weight,
+                    x => x.Dimensions.Unit.HasHeight,
+                    x => x.Dimensions.Unit.HasLength,
+                    x => x.Dimensions.Unit.HasWidth,
+                    x => x.Dimensions.Unit.HasWeight,
+                    x => x.Dimensions.Unit.HasQuantity,
+                    x => x.Dimensions.Unit.Formula,
+                    x => x.Dimensions.Unit.Format,
+                    x => x.Dimensions.Unit.Code,
+                    x => x.Dimensions.Unit.Description,
+                    
+                    x => x.FreeStock,
+                    x => x.AverageCost,
+                    x => x.LastCost
+                )
+            ).Rows.FirstOrDefault();
+        });
+        instancetask.Start();
+        
+        var producttask = new Task<CoreRow?>(() =>
+        {
+            return Provider.Query(
+                new Filter<Product>(x => x.ID).IsEqualTo(entity.Product.ID),
+                new Columns<Product>(
+                    x => x.ID,
+                    x => x.DefaultLocation.ID,
+                    x => x.Warehouse.ID,
+                    x => x.DefaultInstance.Dimensions.Unit.ID,
+                    x => x.DefaultInstance.Dimensions.Unit.Formula,
+                    x => x.DefaultInstance.Dimensions.Unit.Format,
+                    x => x.DefaultInstance.Dimensions.Quantity,
+                    x => x.DefaultInstance.Dimensions.Length,
+                    x => x.DefaultInstance.Dimensions.Width,
+                    x => x.DefaultInstance.Dimensions.Height,
+                    x => x.DefaultInstance.Dimensions.Weight,
+                    x => x.NonStock,
+                    x => x.DefaultInstance.Dimensions.Unit.HasHeight,
+                    x => x.DefaultInstance.Dimensions.Unit.HasLength,
+                    x => x.DefaultInstance.Dimensions.Unit.HasWidth,
+                    x => x.DefaultInstance.Dimensions.Unit.HasWeight,
+                    x => x.DefaultInstance.Dimensions.Unit.HasQuantity,
+                    x => x.DefaultInstance.Dimensions.Unit.Formula,
+                    x => x.DefaultInstance.Dimensions.Unit.Format,
+                    x => x.DefaultInstance.Dimensions.Unit.Code,
+                    x => x.DefaultInstance.Dimensions.Unit.Description
+                )
+            ).Rows.FirstOrDefault();
+        });
+        producttask.Start();
 
-            var instancetask = new Task<CoreRow?>(() =>
-            {
-                return Provider.Query(
-                    new Filter<ProductInstance>(x => x.Product.ID).IsEqualTo(entity.Product.ID)
-                        .And(x=>x.Style.ID).IsEqualTo(entity.Style.ID)
-                        .And(x=>x.Dimensions.UnitSize).IsEqualTo(entity.Dimensions.UnitSize),
-                    new Columns<ProductInstance>(
-                        x => x.ID,
-                        x => x.Product.NonStock,
-                        x => x.Product.DefaultLocation.ID,
-                        x => x.Product.Warehouse.ID,
-                        x => x.Dimensions.Unit.ID,
-                        x => x.Dimensions.Unit.Formula,
-                        x => x.Dimensions.Unit.Format,
-                        x => x.Dimensions.Quantity,
-                        x => x.Dimensions.Length,
-                        x => x.Dimensions.Width,
-                        x => x.Dimensions.Height,
-                        x => x.Dimensions.Weight,
-                        x => x.Dimensions.Unit.HasHeight,
-                        x => x.Dimensions.Unit.HasLength,
-                        x => x.Dimensions.Unit.HasWidth,
-                        x => x.Dimensions.Unit.HasWeight,
-                        x => x.Dimensions.Unit.HasQuantity,
-                        x => x.Dimensions.Unit.Formula,
-                        x => x.Dimensions.Unit.Format,
-                        x => x.Dimensions.Unit.Code,
-                        x => x.Dimensions.Unit.Description,
-                        
-                        x => x.FreeStock,
-                        x => x.AverageCost,
-                        x => x.LastCost
-                    )
-                ).Rows.FirstOrDefault();
-            });
-            instancetask.Start();
-            
-            var producttask = new Task<CoreRow?>(() =>
-            {
-                return Provider.Query(
-                    new Filter<Product>(x => x.ID).IsEqualTo(entity.Product.ID),
-                    new Columns<Product>(
-                        x => x.ID,
-                        x => x.DefaultLocation.ID,
-                        x => x.Warehouse.ID,
-                        x => x.DefaultInstance.Dimensions.Unit.ID,
-                        x => x.DefaultInstance.Dimensions.Unit.Formula,
-                        x => x.DefaultInstance.Dimensions.Unit.Format,
-                        x => x.DefaultInstance.Dimensions.Quantity,
-                        x => x.DefaultInstance.Dimensions.Length,
-                        x => x.DefaultInstance.Dimensions.Width,
-                        x => x.DefaultInstance.Dimensions.Height,
-                        x => x.DefaultInstance.Dimensions.Weight,
-                        x => x.NonStock,
-                        x => x.DefaultInstance.Dimensions.Unit.HasHeight,
-                        x => x.DefaultInstance.Dimensions.Unit.HasLength,
-                        x => x.DefaultInstance.Dimensions.Unit.HasWidth,
-                        x => x.DefaultInstance.Dimensions.Unit.HasWeight,
-                        x => x.DefaultInstance.Dimensions.Unit.HasQuantity,
-                        x => x.DefaultInstance.Dimensions.Unit.Formula,
-                        x => x.DefaultInstance.Dimensions.Unit.Format,
-                        x => x.DefaultInstance.Dimensions.Unit.Code,
-                        x => x.DefaultInstance.Dimensions.Unit.Description
-                    )
-                ).Rows.FirstOrDefault();
-            });
-            producttask.Start();
+        var locationtask = new Task<CoreTable>(() =>
+        {
+            return Provider.Query(
+                new Filter<StockLocation>(x => x.Default).IsEqualTo(true),
+                new Columns<StockLocation>(x => x.ID, x => x.Warehouse.ID, x => x.Warehouse.Default)
+            );
+        });
+        locationtask.Start();
 
-            var locationtask = new Task<CoreTable>(() =>
-            {
-                return Provider.Query(
-                    new Filter<StockLocation>(x => x.Default).IsEqualTo(true),
-                    new Columns<StockLocation>(x => x.ID, x => x.Warehouse.ID, x => x.Warehouse.Default)
-                );
-            });
-            locationtask.Start();
+        Task.WaitAll(movementtask, producttask, locationtask, instancetask, jobRequisitionItemTask);
 
-            Task.WaitAll(movementtask, producttask, locationtask, instancetask);
+        var movements = movementtask.Result;
+        var instancerow = instancetask.Result;
+        var productrow = producttask.Result;
+        var defaultlocations = locationtask.Result;
+        var jobRequisitionItemID = jobRequisitionItemTask.Result;
+        
+        if (productrow is null)
+        {
+            Logger.Send(LogType.Information, UserID, "Cannot Find PurchaseOrderItem.Product.ID!");
+            return;
+        }
+        
+        if (productrow.Get<Product, bool>(x => x.NonStock))
+        {
+            Logger.Send(LogType.Information, UserID, "PurchaseOrderItem.Product is marked as Non Stock!");
+            return;
+        }
+        
+        if (!locationValid)
+        {
+            Logger.Send(LogType.Information, UserID, "PurchaseOrderItem.Location.ID is blank!");
 
-            var movements = movementtask.Result;
-            var instancerow = instancetask.Result;
-            var productrow = producttask.Result;
-            var defaultlocations = locationtask.Result;
-            
-            if (productrow is null)
+            var productlocationid = productrow.EntityLinkID<Product, StockLocationLink>(x => x.DefaultLocation) ?? Guid.Empty;
+            if (productlocationid != Guid.Empty)
             {
-                Logger.Send(LogType.Information, UserID, "Cannot Find PurchaseOrderItem.Product.ID!");
-                return;
-            }
-            
-            if (productrow.Get<Product, bool>(x => x.NonStock))
-            {
-                Logger.Send(LogType.Information, UserID, "PurchaseOrderItem.Product is marked as Non Stock!");
-                return;
+                Logger.Send(LogType.Information, UserID, "- Using Product.DefaultLocation.ID as location");
+                locationid = productlocationid;
             }
-            
-            if (!locationValid)
+            else
             {
-                Logger.Send(LogType.Information, UserID, "PurchaseOrderItem.Location.ID is blank!");
-
-                var productlocationid = productrow.EntityLinkID<Product, StockLocationLink>(x => x.DefaultLocation) ?? Guid.Empty;
-                if (productlocationid != Guid.Empty)
-                {
-                    Logger.Send(LogType.Information, UserID, "- Using Product.DefaultLocation.ID as location");
-                    locationid = productlocationid;
-                }
-                else
-                {
-                    var productwarehouseid = productrow.Get<Product, Guid>(c => c.Warehouse.ID);
-                    var row = defaultlocations.Rows.FirstOrDefault(r => r.Get<StockLocation, Guid>(c => c.Warehouse.ID) == productwarehouseid);
-                    if (row != null)
-                    {
-                        Logger.Send(LogType.Information, UserID, "- Using Product.Warehouse -> Default as location");
-                        locationid = row.Get<StockLocation, Guid>(x => x.ID);
-                    }
-                }
-
-                if (locationid == Guid.Empty)
+                var productwarehouseid = productrow.Get<Product, Guid>(c => c.Warehouse.ID);
+                var row = defaultlocations.Rows.FirstOrDefault(r => r.Get<StockLocation, Guid>(c => c.Warehouse.ID) == productwarehouseid);
+                if (row != null)
                 {
-                    var row = defaultlocations.Rows.FirstOrDefault(r => r.Get<StockLocation, bool>(c => c.Warehouse.Default));
-                    if (row != null)
-                    {
-                        Logger.Send(LogType.Information, UserID, "- Using Default Warehouse -> Default Location as location");
-                        locationid = row.Get<StockLocation, Guid>(x => x.ID);
-                    }
+                    Logger.Send(LogType.Information, UserID, "- Using Product.Warehouse -> Default as location");
+                    locationid = row.Get<StockLocation, Guid>(x => x.ID);
                 }
+            }
 
-                if (locationid == Guid.Empty)
+            if (locationid == Guid.Empty)
+            {
+                var row = defaultlocations.Rows.FirstOrDefault(r => r.Get<StockLocation, bool>(c => c.Warehouse.Default));
+                if (row != null)
                 {
-                    Logger.Send(LogType.Information, UserID, "- Cannot find Location : Skipping Movement Creation");
-                    return;
+                    Logger.Send(LogType.Information, UserID, "- Using Default Warehouse -> Default Location as location");
+                    locationid = row.Get<StockLocation, Guid>(x => x.ID);
                 }
             }
-            
-            if (
-                (entity.Dimensions.Unit.ID == Guid.Empty)
-                && (entity.Dimensions.Height == 0)
-                && (entity.Dimensions.Width == 0)
-                && (entity.Dimensions.Length == 0)
-                && (entity.Dimensions.Weight == 0)
-                )
+
+            if (locationid == Guid.Empty)
             {
-                Logger.Send(LogType.Information, UserID, "PurchaseOrderItem.Unit Size is zero!");
-                entity.Dimensions.CopyFrom(productrow.ToObject<Product>().DefaultInstance.Dimensions);
+                Logger.Send(LogType.Information, UserID, "- Cannot find Location : Skipping Movement Creation");
+                return;
             }
+        }
+        
+        if (
+            (entity.Dimensions.Unit.ID == Guid.Empty)
+            && (entity.Dimensions.Height == 0)
+            && (entity.Dimensions.Width == 0)
+            && (entity.Dimensions.Length == 0)
+            && (entity.Dimensions.Weight == 0)
+            )
+        {
+            Logger.Send(LogType.Information, UserID, "PurchaseOrderItem.Unit Size is zero!");
+            entity.Dimensions.CopyFrom(productrow.ToObject<Product>().DefaultInstance.Dimensions);
+        }
 
-            if (entity.Job.ID == Guid.Empty)
+        if (entity.Job.ID == Guid.Empty)
+        {
+            var instance = instancerow?.ToObject<ProductInstance>();
+            if (instance == null)
             {
-                var instance = instancerow?.ToObject<ProductInstance>();
-                if (instance == null)
-                {
-                    instance = new ProductInstance();
-                    instance.Product.ID = entity.Product.ID;
-                    instance.Style.ID = entity.Style.ID;
-                    instance.Dimensions.Unit.ID = entity.Dimensions.Unit.ID;
-                    instance.Dimensions.Height = entity.Dimensions.Height;
-                    instance.Dimensions.Length = entity.Dimensions.Length;
-                    instance.Dimensions.Width = entity.Dimensions.Width;
-                    instance.Dimensions.Weight = entity.Dimensions.Weight;
-                    instance.Dimensions.Quantity = entity.Dimensions.Quantity;
-                    instance.Dimensions.UnitSize = entity.Dimensions.UnitSize;
-                    instance.Dimensions.Value = entity.Dimensions.Value;
-                    instance.Dimensions.UnitSize = entity.Dimensions.UnitSize;
-                }
+                instance = new ProductInstance();
+                instance.Product.ID = entity.Product.ID;
+                instance.Style.ID = entity.Style.ID;
+                instance.Dimensions.Unit.ID = entity.Dimensions.Unit.ID;
+                instance.Dimensions.Height = entity.Dimensions.Height;
+                instance.Dimensions.Length = entity.Dimensions.Length;
+                instance.Dimensions.Width = entity.Dimensions.Width;
+                instance.Dimensions.Weight = entity.Dimensions.Weight;
+                instance.Dimensions.Quantity = entity.Dimensions.Quantity;
+                instance.Dimensions.UnitSize = entity.Dimensions.UnitSize;
+                instance.Dimensions.Value = entity.Dimensions.Value;
+                instance.Dimensions.UnitSize = entity.Dimensions.UnitSize;
+            }
 
-                instance.LastCost = entity.Cost;
-                    
-                //var product = productrow.ToObject<Product>();
+            instance.LastCost = entity.Cost;
+                
+            //var product = productrow.ToObject<Product>();
 
-                var freeqty = instance.FreeStock;
-                var freeavg = instance.AverageCost;
-                var freecost = instance.FreeStock * freeavg;
+            var freeqty = instance.FreeStock;
+            var freeavg = instance.AverageCost;
+            var freecost = instance.FreeStock * freeavg;
 
-                var poqty = entity.Qty * (Math.Abs(entity.Dimensions.Value) > 0.0001F ? entity.Dimensions.Value : 1.0F);
-                var pocost = entity.Cost * poqty;
+            var poqty = entity.Qty * (Math.Abs(entity.Dimensions.Value) > 0.0001F ? entity.Dimensions.Value : 1.0F);
+            var pocost = entity.Cost * poqty;
 
-                var totalqty = freeqty + poqty;
-                var totalcost = freecost + pocost;
+            var totalqty = freeqty + poqty;
+            var totalcost = freecost + pocost;
 
-                var averagecost = Math.Abs(totalqty) > 0.0001F
-                    ? totalcost / totalqty
-                    : pocost;
+            var averagecost = Math.Abs(totalqty) > 0.0001F
+                ? totalcost / totalqty
+                : pocost;
 
-                if (Math.Abs(averagecost - freeavg) > 0.0001F)
-                {
-                    instance.AverageCost = averagecost;
-                    FindSubStore<ProductInstance>().Save(instance,
-                        $"Updated Average Cost: " +
-                        $"({freeqty} @ {freeavg:C2}) + ({poqty} @ {entity.Cost:C2}) = {totalcost:C2} / {totalqty}"
-                    );
-                }
-            }
-
-            foreach (var movement in movements)
+            if (Math.Abs(averagecost - freeavg) > 0.0001F)
             {
-                movement.Batch.Type = StockMovementBatchType.Receipt;
-                movement.Date = entity.ReceivedDate;
-                movement.Product.ID = entity.Product.ID;
-                movement.Received = entity.Qty;
-                movement.Employee.ID = Guid.Empty;
-                movement.OrderItem.ID = entity.ID;
-                movement.Job.ID = entity.Job.ID;
-                movement.Location.ID = locationid;
-                movement.Style.ID = entity.Style.ID;
-                movement.Style.Code = entity.Style.Code;
-                movement.Style.Description = entity.Style.Description;
-                movement.Notes = string.Format("Received on PO {0}", entity.PurchaseOrderLink.PONumber);
-                movement.Cost = entity.Cost;
-                movement.Type = StockMovementType.Receive;
-                movement.Dimensions.Unit.ID = entity.Dimensions.Unit.ID;
-                movement.Dimensions.Height = entity.Dimensions.Height;
-                movement.Dimensions.Length = entity.Dimensions.Length;
-                movement.Dimensions.Width = entity.Dimensions.Width;
-                movement.Dimensions.Weight = entity.Dimensions.Weight;
-                movement.Dimensions.Quantity = entity.Dimensions.Quantity;
-                movement.Dimensions.UnitSize = entity.Dimensions.UnitSize;
-                movement.Dimensions.Value = entity.Dimensions.Value;
-                movement.Dimensions.UnitSize = entity.Dimensions.UnitSize;
-
+                instance.AverageCost = averagecost;
+                FindSubStore<ProductInstance>().Save(instance,
+                    $"Updated Average Cost: " +
+                    $"({freeqty} @ {freeavg:C2}) + ({poqty} @ {entity.Cost:C2}) = {totalcost:C2} / {totalqty}"
+                );
             }
+        }
 
-            var updates = movements.Where(x => x.IsChanged());
-            if (updates.Any())
-                FindSubStore<StockMovement>().Save(updates, "Updated by Purchase Order Modification");
+        foreach (var movement in movements)
+        {
+            movement.Batch.Type = StockMovementBatchType.Receipt;
+            movement.Date = entity.ReceivedDate;
+            movement.Product.ID = entity.Product.ID;
+            movement.Received = entity.Qty;
+            movement.Employee.ID = Guid.Empty;
+            movement.OrderItem.ID = entity.ID;
+            movement.Job.ID = entity.Job.ID;
+            movement.Location.ID = locationid;
+            movement.Style.ID = entity.Style.ID;
+            movement.Style.Code = entity.Style.Code;
+            movement.Style.Description = entity.Style.Description;
+            movement.Notes = string.Format("Received on PO {0}", entity.PurchaseOrderLink.PONumber);
+            movement.Cost = entity.Cost;
+            movement.Type = StockMovementType.Receive;
+            movement.Dimensions.Unit.ID = entity.Dimensions.Unit.ID;
+            movement.Dimensions.Height = entity.Dimensions.Height;
+            movement.Dimensions.Length = entity.Dimensions.Length;
+            movement.Dimensions.Width = entity.Dimensions.Width;
+            movement.Dimensions.Weight = entity.Dimensions.Weight;
+            movement.Dimensions.Quantity = entity.Dimensions.Quantity;
+            movement.Dimensions.UnitSize = entity.Dimensions.UnitSize;
+            movement.Dimensions.Value = entity.Dimensions.Value;
+            movement.Dimensions.UnitSize = entity.Dimensions.UnitSize;
+            movement.JobRequisitionItem.ID = jobRequisitionItemID;
         }
 
+        var updates = movements.Where(x => x.IsChanged());
+        if (updates.Any())
+            FindSubStore<StockMovement>().Save(updates, "Updated by Purchase Order Modification");
+    }
 
-        private void DeleteStockMovements(PurchaseOrderItem entity)
-        {
-            var movements = Provider.Query(
-                new Filter<StockMovement>(x => x.OrderItem.ID).IsEqualTo(entity.ID),
-                new Columns<StockMovement>(x => x.ID)
-            ).Rows.Select(x => x.ToObject<StockMovement>());
 
-            if (movements.Any())
-                FindSubStore<StockMovement>().Delete(movements, "Purchase Order Item marked as Unreceived");
-        }
-        
+    private void DeleteStockMovements(PurchaseOrderItem entity)
+    {
+        var movements = Provider.Query(
+            new Filter<StockMovement>(x => x.OrderItem.ID).IsEqualTo(entity.ID),
+            new Columns<StockMovement>(x => x.ID)
+        ).Rows.Select(x => x.ToObject<StockMovement>());
+
+        if (movements.Any())
+            FindSubStore<StockMovement>().Delete(movements, "Purchase Order Item marked as Unreceived");
+    }
+    
 
-        protected override void AfterSave(PurchaseOrderItem entity)
+    protected override void AfterSave(PurchaseOrderItem entity)
+    {
+        base.AfterSave(entity);
+        
+        if (entity.HasOriginalValue<PurchaseOrderItem,DateTime>(x=>x.ReceivedDate))
         {
-            base.AfterSave(entity);
-            
-            if (entity.HasOriginalValue<PurchaseOrderItem,DateTime>(x=>x.ReceivedDate))
+            if (entity.ReceivedDate.IsEmpty())
+                DeleteStockMovements(entity);
+            else
             {
-                if (entity.ReceivedDate.IsEmpty())
-                    DeleteStockMovements(entity);
-                else
+                var item = Provider
+                    .Query(new Filter<PurchaseOrderItem>(x => x.ID).IsEqualTo(entity.ID))
+                    .Rows.FirstOrDefault()?.ToObject<PurchaseOrderItem>();
+                if (item != null)
                 {
-                    var item = Provider
-                        .Query(new Filter<PurchaseOrderItem>(x => x.ID).IsEqualTo(entity.ID))
-                        .Rows.FirstOrDefault()?.ToObject<PurchaseOrderItem>();
-                    if (item != null)
-                    {
-                        UpdateStockMovements(item);
-                        UpdateJobRequiItems(item);
-                    }
+                    UpdateStockMovements(item);
+                    UpdateJobRequiItems(item);
                 }
             }
         }
+    }
 
-        private void UpdateJobRequiItems(PurchaseOrderItem entity)
-        {
-            var table = Provider.Query(
-                new Filter<JobRequisitionItem>(x => x.PurchaseOrderItem.ID).IsEqualTo(entity.ID),
-                new Columns<JobRequisitionItem>(x => x.ID, x => x.Status)
-                );
-            if (table.Rows.Any())
-            {
-                JobRequisitionItem item = table.Rows.FirstOrDefault().ToObject<JobRequisitionItem>();
-                if (item.Status == JobRequisitionItemStatus.TreatmentOnOrder)
-                    item.Status = JobRequisitionItemStatus.TreatmentReceived;
-                else
-                    item.Status = JobRequisitionItemStatus.Received;
-
-                Provider.Save(item);
-            }
-        }
-
-        protected override void BeforeDelete(PurchaseOrderItem entity)
-        {
-            base.BeforeDelete(entity);
-            DeleteStockMovements(entity);
-        }
-
-        protected override void AfterDelete(PurchaseOrderItem entity)
+    private void UpdateJobRequiItems(PurchaseOrderItem entity)
+    {
+        var jri = Provider.Query(
+            new Filter<JobRequisitionItem>(x => x.ID).InQuery(
+                new Filter<JobRequisitionItemPurchaseOrderItem>(x => x.PurchaseOrderItem.ID).IsEqualTo(entity.ID),
+                x => x.JobRequisitionItem.ID),
+            new Columns<JobRequisitionItem>(x => x.ID))
+            .ToObjects<JobRequisitionItem>()
+            .FirstOrDefault();
+        if (jri is not null)
         {
-            base.AfterDelete(entity);
-            RemoveJobRequisitionItemLink(entity);
+            JobRequisitionItemStore.UpdateStatus(this, jri.ID);
         }
+    }
 
-        private void RemoveJobRequisitionItemLink(PurchaseOrderItem entity)
-        {
-            CoreTable table = Provider.Query<JobRequisitionItem>
-                (
-                new Filter<JobRequisitionItem>(x => x.PurchaseOrderItem.ID).IsEqualTo(entity.ID),
-                new Columns<JobRequisitionItem>(x => x.ID, x => x.PurchaseOrderItem.PurchaseOrderLink.PONumber, x => x.Status)
-                );
-            if (table.Rows.Any())
-            {
-                JobRequisitionItem item = table.Rows.FirstOrDefault().ToObject<JobRequisitionItem>();
-                item.PurchaseOrderItem.PurchaseOrderLink.PONumber = "";
-                item.Status = JobRequisitionItemStatus.NotChecked;
-                Provider.Save<JobRequisitionItem>(item);
-            }
-        }
+    protected override void BeforeDelete(PurchaseOrderItem entity)
+    {
+        base.BeforeDelete(entity);
+        DeleteStockMovements(entity);
     }
 }

+ 7 - 39
prs.stores/StockMovementStore.cs

@@ -6,49 +6,17 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 
-namespace PRSStores
+namespace PRSStores;
+
+public class StockMovementStore : BaseStore<StockMovement>
 {
-    public class StockMovementStore : BaseStore<StockMovement>
+    protected override void AfterSave(StockMovement sm)
     {
-        protected override void AfterSave(StockMovement sm)
-        {
-            if (sm.JobRequisitionItem.ID != Guid.Empty)
-            {
-                CoreTable table = Provider.Query<JobRequisitionItem>(
-                    new Filter<JobRequisitionItem>(x => x.ID).IsEqualTo(sm.JobRequisitionItem.ID),
-                    new Columns<JobRequisitionItem>(x => x.ID,
-                    x => x.Cancelled, 
-                    x => x.Archived,
-                    x => x.PurchaseOrderItem.ReceivedDate, 
-                    x => x.Ordered,
-                    x => x.Qty,
-                    x => x.Style.ID, 
-                    x => x.Status
-                    )
-                    );
-                var item = table.Rows.FirstOrDefault().ToObject<JobRequisitionItem>();
-
-                item = JobRequisitionItemStore.DoStatusChecks(item, DoQuery(item));
-                Provider.Save(item);
-            }
-
-            base.AfterSave(sm);
-        }
-
-        private JobRequisitionItem QueryJobRequiItem(Guid itemID)
+        if (sm.JobRequisitionItem.ID != Guid.Empty)
         {
-            CoreTable table = Provider.Query<JobRequisitionItem>(new Filter<JobRequisitionItem>(x => x.ID).IsEqualTo(itemID));
-            return table.Rows.FirstOrDefault().ToObject<JobRequisitionItem>();
+            JobRequisitionItemStore.UpdateStatus(this, sm.JobRequisitionItem.ID);
         }
 
-        private CoreTable DoQuery(JobRequisitionItem item)
-        {
-            return Provider.Query<StockMovement>(
-               new Filter<StockMovement>(x => x.JobRequisitionItem.ID).IsEqualTo(item.ID),
-               new Columns<StockMovement>(
-                   x => x.Received, 
-                   x => x.Style.ID
-                   ));
-        }
+        base.AfterSave(sm);
     }
 }