소스 검색

Added ability to view JRIs from stock holding grid, and added a set of default columns for Stock Holding grid; also made guidelines stricter for issuing from requi'd stock.

Kenric Nugteren 1 년 전
부모
커밋
6ab82ceaea
2개의 변경된 파일444개의 추가작업 그리고 462개의 파일을 삭제
  1. 1 1
      prs.desktop/Panels/Jobs/JobRequisitionItemGrid.cs
  2. 443 461
      prs.desktop/Panels/Products/Locations/StockHoldingGrid.cs

+ 1 - 1
prs.desktop/Panels/Jobs/JobRequisitionItemGrid.cs

@@ -13,7 +13,7 @@ using InABox.WPF;
 
 namespace PRSDesktop;
 
-internal class JobRequisitionItemGrid : DynamicDataGrid<JobRequisitionItem>
+internal class JobRequisitionItemGrid : DynamicDataGrid<JobRequisitionItem>, ISpecificGrid
 {
     private Button CancelItemsButton;
 

+ 443 - 461
prs.desktop/Panels/Products/Locations/StockHoldingGrid.cs

@@ -7,547 +7,529 @@ using Comal.Classes;
 using InABox.Clients;
 using InABox.Core;
 using InABox.DynamicGrid;
+using InABox.Wpf;
 using InABox.WPF;
-using Microsoft.Office.Interop.Outlook;
 using Exception = System.Exception;
 
-namespace PRSDesktop
+namespace PRSDesktop;
+
+public class StockHoldingGrid : DynamicDataGrid<StockHolding>
 {
-    public class StockHoldingGrid : DynamicDataGrid<StockHolding>
+    private enum MovementAction
     {
-        private readonly Guid _employeeid = Guid.Empty;
+        Receive,
+        Issue,
+        Transfer
+    }
+    private MovementAction _action;
+    private StockHolding? _holding;
 
-        private bool bIssuing;
+    private readonly Guid _employeeid = Guid.Empty;
 
-        private bool bReceiving;
+    private Button IssueButton;
 
-        //bool bReserving = false;
+    private Button ReceiveButton;
 
-        private readonly bool bReleasing = false;
+    private DynamicDataGrid<StockMovement> smg;
 
-        private bool bTransferring;
+    //Button ReserveButton = null;
 
-        private Button IssueButton;
+    private Button TransferButton;
 
-        private Button ReceiveButton;
+    public StockHoldingGrid() : base()
+    {         
+        ColumnsTag = "StockHolding";
 
-        private DynamicDataGrid<StockMovement> smg;
+        _employeeid = GetEmployeeID();
+    }
+    protected override void Init()
+    {
+        base.Init();
+
+        ReceiveButton = AddButton("Receive", PRSDesktop.Resources.add.AsBitmapImage(), ReceiveStock);
+        ReceiveButton.IsEnabled = false;
+
+        IssueButton = AddButton("Issue", PRSDesktop.Resources.delete.AsBitmapImage(), IssueStock);
+        IssueButton.IsEnabled = false;
+
+        //ReserveButton = AddButton("Reserve", PRSDesktop.Resources.project.AsBitmapImage(), ReserveStock);
+        //ReserveButton.Margin = new Thickness(20, ReserveButton.Margin.Top, ReserveButton.Margin.Right, ReserveButton.Margin.Bottom);
+        //ReserveButton.IsEnabled = false;
+
+        TransferButton = AddButton("Transfer", PRSDesktop.Resources.split.AsBitmapImage(), TransferStock);
+        TransferButton.Margin = new Thickness(20, TransferButton.Margin.Top, TransferButton.Margin.Right, TransferButton.Margin.Bottom);
+        TransferButton.IsEnabled = false;
+
+        HiddenColumns.Add(x => x.Product.ID);
+        HiddenColumns.Add(x => x.Job.ID);
+        HiddenColumns.Add(x => x.Job.JobNumber);
+        HiddenColumns.Add(x => x.Location.ID);
+        HiddenColumns.Add(x => x.Location.Code);
+        HiddenColumns.Add(x => x.Style.ID);
+        HiddenColumns.Add(x => x.Style.Code);
+        HiddenColumns.Add(x => x.Qty);
+        HiddenColumns.Add(x => x.Units);
+        HiddenColumns.Add(x => x.Available);
+        HiddenColumns.Add(x => x.AverageValue);
+        HiddenColumns.Add(x => x.Dimensions.Unit.ID);
+        HiddenColumns.Add(x => x.Dimensions.Unit.Description);
+        HiddenColumns.Add(x => x.Dimensions.Unit.HasHeight);
+        HiddenColumns.Add(x => x.Dimensions.Unit.HasLength);
+        HiddenColumns.Add(x => x.Dimensions.Unit.HasWidth);
+        HiddenColumns.Add(x => x.Dimensions.Unit.HasHeight);
+        HiddenColumns.Add(x => x.Dimensions.Unit.HasQuantity);
+        HiddenColumns.Add(x => x.Dimensions.Unit.Format);
+        HiddenColumns.Add(x => x.Dimensions.Unit.Formula);
+        HiddenColumns.Add(x => x.Dimensions.Length);
+        HiddenColumns.Add(x => x.Dimensions.Width);
+        HiddenColumns.Add(x => x.Dimensions.Height);
+        HiddenColumns.Add(x => x.Dimensions.Quantity);
+        HiddenColumns.Add(x => x.Dimensions.Value);
+
+        ActionColumns.Add(new DynamicMenuColumn(BuildMenu) { Position = DynamicActionColumnPosition.End });
+    }
 
-        //Button ReserveButton = null;
+    public override DynamicGridColumns GenerateColumns()
+    {
+        var columns = new DynamicGridColumns<StockHolding>();
 
-        private Button TransferButton;
+        columns.Add(x => x.Product.Code, 120, "Product Code", "", Alignment.MiddleCenter);
+        columns.Add(x => x.Product.Name, 0, "Product Name", "", Alignment.MiddleLeft);
+        columns.Add(x => x.Job.JobNumber, 50, "Job", "", Alignment.MiddleCenter);
+        columns.Add(x => x.Style.Description, 0, "Style", "", Alignment.MiddleLeft);
+        columns.Add(x => x.Dimensions.UnitSize, 100, "Size", "", Alignment.MiddleCenter);
+        columns.Add(x => x.Units, 70, "Units", "", Alignment.MiddleCenter);
+        columns.Add(x => x.Available, 70, "Available", "", Alignment.MiddleCenter);
 
-        public StockHoldingGrid() : base()
-        {         
-            ColumnsTag = "StockHolding";
+        return columns;
+    }
 
-            _employeeid = GetEmployeeID();
-        }
-        protected override void Init()
-        {
-            base.Init();
-
-            ReceiveButton = AddButton("Receive", PRSDesktop.Resources.add.AsBitmapImage(), ReceiveStock);
-            ReceiveButton.IsEnabled = false;
-
-            IssueButton = AddButton("Issue", PRSDesktop.Resources.delete.AsBitmapImage(), IssueStock);
-            IssueButton.IsEnabled = false;
-
-            //ReserveButton = AddButton("Reserve", PRSDesktop.Resources.project.AsBitmapImage(), ReserveStock);
-            //ReserveButton.Margin = new Thickness(20, ReserveButton.Margin.Top, ReserveButton.Margin.Right, ReserveButton.Margin.Bottom);
-            //ReserveButton.IsEnabled = false;
-
-            TransferButton = AddButton("Transfer", PRSDesktop.Resources.split.AsBitmapImage(), TransferStock);
-            TransferButton.Margin = new Thickness(20, TransferButton.Margin.Top, TransferButton.Margin.Right, TransferButton.Margin.Bottom);
-            TransferButton.IsEnabled = false;
-
-            HiddenColumns.Add(x => x.Product.ID);
-            HiddenColumns.Add(x => x.Job.ID);
-            HiddenColumns.Add(x => x.Job.JobNumber);
-            HiddenColumns.Add(x => x.Location.ID);
-            HiddenColumns.Add(x => x.Location.Code);
-            HiddenColumns.Add(x => x.Style.ID);
-            HiddenColumns.Add(x => x.Style.Code);
-            HiddenColumns.Add(x => x.Qty);
-            HiddenColumns.Add(x => x.Units);
-            HiddenColumns.Add(x => x.AverageValue);
-            HiddenColumns.Add(x => x.Dimensions.Unit.ID);
-            HiddenColumns.Add(x => x.Dimensions.Unit.Description);
-            HiddenColumns.Add(x => x.Dimensions.Unit.HasHeight);
-            HiddenColumns.Add(x => x.Dimensions.Unit.HasLength);
-            HiddenColumns.Add(x => x.Dimensions.Unit.HasWidth);
-            HiddenColumns.Add(x => x.Dimensions.Unit.HasHeight);
-            HiddenColumns.Add(x => x.Dimensions.Unit.HasQuantity);
-            HiddenColumns.Add(x => x.Dimensions.Unit.Format);
-            HiddenColumns.Add(x => x.Dimensions.Unit.Formula);
-            HiddenColumns.Add(x => x.Dimensions.Length);
-            HiddenColumns.Add(x => x.Dimensions.Width);
-            HiddenColumns.Add(x => x.Dimensions.Height);
-            HiddenColumns.Add(x => x.Dimensions.Quantity);
-            HiddenColumns.Add(x => x.Dimensions.Value);
-        }
-        protected override void DoReconfigure(FluentList<DynamicGridOption> options)
-        {
-            base.DoReconfigure(options);
-            options.AddRange(DynamicGridOption.RecordCount, DynamicGridOption.SelectColumns, DynamicGridOption.FilterRows);
-        }
+    protected override void DoReconfigure(FluentList<DynamicGridOption> options)
+    {
+        base.DoReconfigure(options);
+        options.AddRange(DynamicGridOption.RecordCount, DynamicGridOption.SelectColumns, DynamicGridOption.FilterRows);
+    }
 
-        public IStockLocation Location { get; set; }
+    private void BuildMenu(DynamicMenuColumn column, CoreRow? row)
+    {
+        if (row is null) return;
 
-        protected override void SelectItems(CoreRow[]? rows)
-        {
-            base.SelectItems(rows);
+        column.AddItem("View Requisition Items", null, ViewRequisitions_Click);
+    }
 
-            ReceiveButton.IsEnabled = Location != null && Location.ID != Guid.Empty;
-            IssueButton.IsEnabled = Location != null && Location.ID != Guid.Empty && rows?.Any() == true;
-            TransferButton.IsEnabled = Location != null && Location.ID != Guid.Empty && rows?.Any() == true;
-        }
+    private void ViewRequisitions_Click(CoreRow? row)
+    {
+        if (row is null) return;
+
+        var holding = row.ToObject<StockHolding>();
 
-        private void CheckStockMovementGrid()
+        var grid = (DynamicGridUtils.CreateDynamicGrid(typeof(DynamicDataGrid<>), typeof(JobRequisitionItem)) as DynamicDataGrid<JobRequisitionItem>)!;
+        grid.OnDefineFilter += (type) =>
         {
-            if (smg == null)
+            if(type == typeof(JobRequisitionItem))
             {
-                smg = new DynamicDataGrid<StockMovement>();
-                smg.OnCustomiseEditor += StockMovementCustomiseEditor;
-                smg.OnValidate += StockMovementValidate;
-                smg.OnEditorValueChanged += StockMovementValueChanged;
+                return new Filter<JobRequisitionItem>(x => x.ID)
+                    .InQuery(
+                        StockHoldingUnionGenerator.GetFilter(holding),
+                        x => x.JobRequisitionItem.ID);
             }
-        }
+            else
+            {
+                return null;
+            }
+        };
+        DynamicGridUtils.CreateGridWindow("Job Requisition Items for stock holding", grid).ShowDialog();
+    }
+
+    public IStockLocation Location { get; set; }
+
+    protected override void SelectItems(CoreRow[]? rows)
+    {
+        base.SelectItems(rows);
 
-        private Guid GetEmployeeID()
+        ReceiveButton.IsEnabled = Location != null && Location.ID != Guid.Empty;
+        IssueButton.IsEnabled = Location != null && Location.ID != Guid.Empty && rows?.Any() == true;
+        TransferButton.IsEnabled = Location != null && Location.ID != Guid.Empty && rows?.Any() == true;
+    }
+
+    private DynamicDataGrid<StockMovement> CheckStockMovementGrid(MovementAction action, StockHolding holding)
+    {
+        _action = action;
+        _holding = holding;
+        if (smg == null)
         {
-            var employee = new Client<Employee>().Query(
-                new Filter<Employee>(x => x.UserLink.ID).IsEqualTo(ClientFactory.UserGuid),
-                new Columns<Employee>(x => x.ID)
-            );
-            return employee.Rows.Any() ? employee.Rows.First().Get<Employee, Guid>(x => x.ID) : Guid.Empty;
+            smg = new DynamicDataGrid<StockMovement>();
+            smg.OnCustomiseEditor += StockMovementCustomiseEditor;
+            smg.OnValidate += StockMovementValidate;
+            smg.OnEditorValueChanged += StockMovementValueChanged;
         }
+        return smg;
+    }
 
-        private Dictionary<string, object?> StockMovementValueChanged(IDynamicEditorForm form, string name, object value)
-        {
-            var result = new Dictionary<string, object?>();
-            if (name.Equals("Location.Job.ID"))
-            {
-                var editor = form.FindEditor("Job.ID");
-                if (!value.Equals(Guid.Empty))
-                    result = DynamicGridUtils.UpdateEditorValue(form.Items, "Job.ID", value);
-                else
-                    foreach (StockMovement item in form.Items)
-                        result = DynamicGridUtils.UpdateEditorValue(new[] { item }, "Job.ID",
-                            item.Job.HasOriginalValue("ID") ? item.Job.GetOriginalValue(x => x.ID) : item.Job.ID);
-                editor.IsEnabled = value.Equals(Guid.Empty);
-            }
+    private Guid GetEmployeeID()
+    {
+        var employee = new Client<Employee>().Query(
+            new Filter<Employee>(x => x.UserLink.ID).IsEqualTo(ClientFactory.UserGuid),
+            new Columns<Employee>(x => x.ID)
+        );
+        return employee.Rows.Any() ? employee.Rows.First().Get<Employee, Guid>(x => x.ID) : Guid.Empty;
+    }
 
-            return result;
+    private Dictionary<string, object?> StockMovementValueChanged(IDynamicEditorForm form, string name, object value)
+    {
+        var result = new Dictionary<string, object?>();
+        if (name.Equals("Location.Job.ID"))
+        {
+            var editor = form.FindEditor("Job.ID");
+            if (!value.Equals(Guid.Empty))
+                result = DynamicGridUtils.UpdateEditorValue(form.Items, "Job.ID", value);
+            else
+                foreach (StockMovement item in form.Items)
+                    result = DynamicGridUtils.UpdateEditorValue(new[] { item }, "Job.ID",
+                        item.Job.HasOriginalValue("ID") ? item.Job.GetOriginalValue(x => x.ID) : item.Job.ID);
+            editor.IsEnabled = value.Equals(Guid.Empty);
         }
 
+        return result;
+    }
 
-        private void StockMovementValidate(object sender, StockMovement[] items, List<string> errors)
+
+    private void StockMovementValidate(object sender, StockMovement[] items, List<string> errors)
+    {
+        if (items.Any(x => x.Received == 0 && x.Issued == 0))
         {
-            var bQty = false;
-            //bool bJob = false;
-            var bProd = false;
-            var bLocn = false;
-            var bStyle = false;
-            
-            foreach (var item in items)
-            {
-                bQty = bQty || (item.Received == 0 && item.Issued == 0);
-                bProd = bProd || !item.Product.IsValid();
-                bLocn = bLocn || !item.Location.IsValid();
-                bStyle = bStyle || !item.Style.IsValid();
-            }
+            errors.Add("Quantity may not be zero");
+        }
+        else if(_action == MovementAction.Issue && _holding is not null && items.Any(x => x.Issued > _holding.Available))
+        {
+            errors.Add($"Quantity may not be greater than available stock ({_holding.Available})");
+        }
 
-            if (bQty)
-                errors.Add("Quantity may not be zero");
-            if (bProd)
-                errors.Add("Product may not be blank");
-            if (bLocn)
-                errors.Add("Location may not be blank");
+        if (items.Any(x => x.Product.ID == Guid.Empty))
+            errors.Add("Product may not be blank");
+        if (items.Any(x => x.Location.ID == Guid.Empty))
+            errors.Add("Location may not be blank");
 
-            //if (bStyle)
-            //    result.Add("Style may not be blank");
+        if (!errors.Any() && _action == MovementAction.Transfer)
+            foreach (var item in items)
+            {
+                var changes = new List<string>();
+                if (item.Location.HasOriginalValue(x => x.ID))
+                    changes.Add(item.Location.GetOriginalValue(x => x.Code));
 
-            if (!errors.Any() && bTransferring)
-                foreach (var item in items)
+                if (item.Job.HasOriginalValue(x => x.ID))
                 {
-                    var changes = new List<string>();
-                    if (item.Location.HasOriginalValue(x => x.ID))
-                        changes.Add(item.Location.GetOriginalValue(x => x.Code));
-
-                    if (item.Job.HasOriginalValue(x => x.ID))
-                    {
-                        var job = item.Job.GetOriginalValue(x => x.JobNumber);
-                        if (string.IsNullOrEmpty(job))
-                            job = "General Stock";
-                        changes.Add(job);
-                    }
-
-                    if (item.Style.HasOriginalValue(x => x.ID))
-                        changes.Add(item.Style.GetOriginalValue(x => x.Code));
-
-                    if (changes.Any())
-                        item.Notes = string.Format("Transferred from {0}{1}{2}",
-                            string.Join(" / ", changes.Where(x => !string.IsNullOrWhiteSpace(x))),
-                            string.IsNullOrWhiteSpace(item.Notes) ? "" : "\n", item.Notes);
-                    else
-                        errors.Add("Transfers must change either Location, Style or Job");
+                    var job = item.Job.GetOriginalValue(x => x.JobNumber);
+                    if (string.IsNullOrEmpty(job))
+                        job = "General Stock";
+                    changes.Add(job);
                 }
-        }
 
-        private void StockMovementCustomiseEditor(IDynamicEditorForm sender, StockMovement[]? items, DynamicGridColumn column, BaseEditor editor)
-        {
-            if (column.ColumnName.Equals("Location.ID"))
-                editor.Editable = bTransferring ? Editable.Enabled : Editable.Hidden;
+                if (item.Style.HasOriginalValue(x => x.ID))
+                    changes.Add(item.Style.GetOriginalValue(x => x.Code));
 
-            if (column.ColumnName.Equals("Product.NettCost"))
-                editor.Editable = Editable.Hidden;
+                if (changes.Any())
+                    item.Notes = string.Format("Transferred from {0}{1}{2}",
+                        string.Join(" / ", changes.Where(x => !string.IsNullOrWhiteSpace(x))),
+                        string.IsNullOrWhiteSpace(item.Notes) ? "" : "\n", item.Notes);
+                else
+                    errors.Add("Transfers must change either Location, Style or Job");
+            }
+    }
 
-            if (column.ColumnName.Equals("Style.ID"))
-                editor.Editable = bReceiving || bTransferring ? Editable.Enabled : Editable.Hidden;
+    private void StockMovementCustomiseEditor(IDynamicEditorForm sender, StockMovement[]? items, DynamicGridColumn column, BaseEditor editor)
+    {
+        if (column.ColumnName.Equals("Location.ID"))
+            editor.Editable = _action == MovementAction.Transfer ? Editable.Enabled : Editable.Hidden;
 
-            if (column.ColumnName.Equals("Received"))
-            {
-                editor.Editable = bReceiving || bTransferring || bReleasing ? Editable.Enabled : Editable.Hidden;
-                editor.Caption = "Quantity";
-            }
+        if (column.ColumnName.Equals("Product.NettCost"))
+            editor.Editable = Editable.Hidden;
 
-            if (column.ColumnName.Equals("Product.ID"))
-                editor.Editable = bReceiving ? Editable.Enabled : Editable.Hidden;
+        if (column.ColumnName.Equals("Style.ID"))
+            editor.Editable = _action == MovementAction.Receive || _action == MovementAction.Transfer ? Editable.Enabled : Editable.Hidden;
 
-            if (column.ColumnName.Equals("UnitSize"))
-                editor.Editable = bReceiving ? Editable.Enabled : Editable.Hidden;
+        if (column.ColumnName.Equals("Received"))
+        {
+            editor.Editable = _action == MovementAction.Receive || _action == MovementAction.Transfer ? Editable.Enabled : Editable.Hidden;
+            editor.Caption = "Quantity";
+        }
 
-            if (column.ColumnName.Equals("Job.ID"))
-                editor.Editable = bReceiving && items?.FirstOrDefault()?.Job.IsValid() == true ? Editable.Disabled : Editable.Enabled;
+        if (column.ColumnName.Equals("Product.ID"))
+            editor.Editable = _action == MovementAction.Receive ? Editable.Enabled : Editable.Hidden;
 
-            if (column.ColumnName.Equals("Issued"))
-            {
-                editor.Editable = bIssuing ? Editable.Enabled : Editable.Hidden;
-                editor.Caption = "Quantity";
-            }
+        if (column.ColumnName.Equals("UnitSize"))
+            editor.Editable = _action == MovementAction.Receive ? Editable.Enabled : Editable.Hidden;
 
-            if (column.ColumnName.Equals("Employee.ID"))
-                editor.Editable = bTransferring || bReleasing ? Editable.Hidden : Editable.Enabled;
+        if (column.ColumnName.Equals("Job.ID"))
+            editor.Editable = _action == MovementAction.Receive && items?.FirstOrDefault()?.Job.IsValid() == true ? Editable.Disabled : Editable.Enabled;
 
-            if (column.ColumnName.Equals("Units"))
-                editor.Editable = Editable.Hidden;
-            
-            if (column.ColumnName.Equals("Units"))
-                editor.Editable = bReceiving ? Editable.Enabled : Editable.Hidden;
+        if (column.ColumnName.Equals("Issued"))
+        {
+            editor.Editable = _action == MovementAction.Issue ? Editable.Enabled : Editable.Hidden;
+            editor.Caption = "Quantity";
         }
 
-        private bool ReceiveStock(Button arg1, CoreRow[] rows)
+        if (column.ColumnName.Equals("Employee.ID"))
+            editor.Editable = _action == MovementAction.Transfer ? Editable.Hidden : Editable.Enabled;
+
+        if (column.ColumnName.Equals("Units"))
+            editor.Editable = _action == MovementAction.Receive ? Editable.Enabled : Editable.Hidden;
+    }
+
+    private bool ReceiveStock(Button arg1, CoreRow[] rows)
+    {
+        var movement = new StockMovement();
+        movement.Location.ID = Location.ID;
+        movement.Location.Code = Location.Code;
+        movement.Job.ID = Location.Job.ID;
+        movement.Job.JobNumber = Location.Job.JobNumber;
+        movement.Job.Name = Location.Job.Name;
+        movement.Location.Description = Location.Description;
+        movement.Date = DateTime.Now;
+        movement.IsTransfer = false;
+        movement.Employee.ID = _employeeid;
+        movement.Type = StockMovementType.Receive;
+
+        movement.CommitChanges();
+
+        var holding = rows.First().ToObject<StockHolding>();
+        var smg = CheckStockMovementGrid(MovementAction.Receive, holding);
+        var result = smg.EditItems(new[] { movement });
+
+        if (result)
         {
-            CheckStockMovementGrid();
-            var movement = new StockMovement();
-            movement.Location.ID = Location.ID;
-            movement.Location.Code = Location.Code;
-            movement.Job.ID = Location.Job.ID;
-            movement.Job.JobNumber = Location.Job.JobNumber;
-            movement.Job.Name = Location.Job.Name;
-            movement.Location.Description = Location.Description;
-            movement.Date = DateTime.Now;
-            movement.IsTransfer = false;
-            movement.Employee.ID = _employeeid;
-            movement.Type = StockMovementType.Receive;
-
-            movement.CommitChanges();
-
-            bReceiving = true;
-            var result = smg.EditItems(new[] { movement });
-            bReceiving = false;
-
-            if (result)
-            {
-                DoChanged();
-                SaveBatch(StockMovementBatchType.Receipt, new StockMovement[] { movement});
-            }
-               
-            return result;
+            DoChanged();
+            SaveBatch(StockMovementBatchType.Receipt, new StockMovement[] { movement});
         }
+           
+        return result;
+    }
 
-        private void SaveBatch(StockMovementBatchType type, StockMovement[] movements)
-        {
-            var batch = new StockMovementBatch();
-            batch.Type = type;
-            batch.Notes = batch.Type + " batch created from Desktop Stock Location Screen";
-            batch.Employee.ID = _employeeid;
-            new Client<StockMovementBatch>().Save(batch, "created from Desktop Stock Location Screen");
-
-            foreach (var mvt in movements)
-            { 
-                mvt.Batch.ID = batch.ID;
-            }
+    private void SaveBatch(StockMovementBatchType type, StockMovement[] movements)
+    {
+        var batch = new StockMovementBatch();
+        batch.Type = type;
+        batch.Notes = batch.Type + " batch created from Desktop Stock Location Screen";
+        batch.Employee.ID = _employeeid;
+        new Client<StockMovementBatch>().Save(batch, "created from Desktop Stock Location Screen");
+
+        foreach (var mvt in movements)
+        { 
+            mvt.Batch.ID = batch.ID;
+        }
+
+        new Client<StockMovement>().Save(movements, "Updating batch from Desktop Stock Location Screen");
+    }
 
-            new Client<StockMovement>().Save(movements, "Updating batch from Desktop Stock Location Screen");
+    private bool IssueStock(Button arg1, CoreRow[] rows)
+    {
+        if (!rows.Any())
+        {
+            MessageWindow.ShowMessage("Please select an item to issue", "No selected items");
+            return false;
         }
 
-        private bool IssueStock(Button arg1, CoreRow[] rows)
+        var holding = rows.First().ToObject<StockHolding>();
+
+        if(holding.Available <= 0)
         {
-            if (!rows.Any())
-            {
-                MessageBox.Show("Please select an item to issue");
-                return false;
-            }
+            MessageWindow.ShowMessage("There is no available stock in this holding to issue.", "No available stock");
+            return false;
+        }
 
-            CheckStockMovementGrid();
+        // var jobid = rows.First().Get<StockHolding, Guid>(x => x.Job.ID);
+        // var jobno = rows.First().Get<StockHolding, string>(x => x.Job.JobNumber);
+        // var productid = rows.First().Get<StockHolding, Guid>(x => x.Product.ID);
+        // var styleid = rows.First().Get<StockHolding, Guid>(x => x.Style.ID);
+        // var stylecode = rows.First().Get<StockHolding, string>(x => x.Style.Code);
+        // var size = rows.First().Get<StockHolding, double>(x => x.UnitSize);
 
-            var holding = rows.First().ToObject<StockHolding>();
-            // var jobid = rows.First().Get<StockHolding, Guid>(x => x.Job.ID);
-            // var jobno = rows.First().Get<StockHolding, string>(x => x.Job.JobNumber);
-            // var productid = rows.First().Get<StockHolding, Guid>(x => x.Product.ID);
-            // var styleid = rows.First().Get<StockHolding, Guid>(x => x.Style.ID);
-            // var stylecode = rows.First().Get<StockHolding, string>(x => x.Style.Code);
-            // var size = rows.First().Get<StockHolding, double>(x => x.UnitSize);
+        var movement = new StockMovement
+        {
+            Date = DateTime.Now
+        };
+        movement.Location.ID = Location.ID;
+        movement.Location.Code = Location.Code;
+        movement.Location.Description = Location.Description;
+        movement.Product.ID = holding.Product.ID;
+        movement.Job.ID = holding.Job.ID;
+        movement.Job.JobNumber = holding.Job.JobNumber;
+        movement.Style.ID = holding.Style.ID;
+        movement.Style.Code = holding.Style.Code;
+        movement.Dimensions.CopyFrom(holding.Dimensions);
+        movement.IsTransfer = false;
+        movement.Employee.ID = _employeeid;
+        movement.Cost = holding.AverageValue;
+        movement.Type = StockMovementType.Issue;
+
+        movement.CommitChanges();
+
+        var smg = CheckStockMovementGrid(MovementAction.Issue, holding);
+        var result = smg.EditItems(new[] { movement });
+
+        var mvts = new List<StockMovement>
+        {
+            movement
+        };
 
-            var movement = new StockMovement
-            {
-                Date = DateTime.Now
-            };
-            movement.Location.ID = Location.ID;
-            movement.Location.Code = Location.Code;
-            movement.Location.Description = Location.Description;
-            movement.Product.ID = holding.Product.ID;
-            movement.Job.ID = holding.Job.ID;
-            movement.Job.JobNumber = holding.Job.JobNumber;
-            movement.Style.ID = holding.Style.ID;
-            movement.Style.Code = holding.Style.Code;
-            movement.Dimensions.CopyFrom(holding.Dimensions);
-            movement.IsTransfer = false;
-            movement.Employee.ID = _employeeid;
-            movement.Cost = holding.AverageValue;
-            movement.Type = StockMovementType.Issue;
-
-            movement.CommitChanges();
-
-            bIssuing = true;
-            var result = smg.EditItems(new[] { movement });
-            bIssuing = false;
-
-            var mvts = new List<StockMovement>
+        // Issuing to a different job than you received into?
+        if (result && holding.Job.ID != movement.Job.ID)
+        {
+            // Issue from Old Job (Hidden)
+            var issue = new StockMovement
             {
-                movement
+                Date = movement.Date
             };
+            issue.Product.ID = holding.Product.ID;
+            issue.Job.ID = holding.Job.ID;
+            issue.Location.ID = Location.ID;
+            issue.Style.ID = holding.Style.ID;
+            issue.Issued = movement.Issued;
+            issue.Transaction = movement.Transaction;
+            issue.Employee.ID = movement.Employee.ID;
+            issue.Dimensions.CopyFrom(holding.Dimensions);
+            issue.IsTransfer = true;
+            issue.Notes = string.Format("Transferred from {0}", holding.Job.ID.Equals(Guid.Empty) ? "General Stock" : "Job " + holding.Job.JobNumber);
+            issue.System = true;
+            issue.Cost = holding.AverageValue;
+            issue.Type = StockMovementType.TransferOut;
+
+            // Receive to New Job (Hidden)
+            var receive = new StockMovement();
+            receive.Date = movement.Date;
+            receive.Product.ID = holding.Product.ID;
+            receive.Job.ID = movement.Job.ID;
+            receive.Location.ID = Location.ID;
+            receive.Style.ID = holding.Style.ID;
+            receive.Received = movement.Issued;
+            receive.Transaction = movement.Transaction;
+            receive.Employee.ID = movement.Employee.ID;
+            receive.Dimensions.CopyFrom(holding.Dimensions);
+            receive.IsTransfer = true;
+            receive.Notes = string.Format("Transferred to {0}",
+                !movement.Job.IsValid() ? "General Stock" : "Job " + movement.Job.JobNumber);
+            receive.System = true;
+            receive.Cost = holding.AverageValue;
+            receive.Type = StockMovementType.TransferIn;
+
+            new Client<StockMovement>().Save(new[] { issue, receive }, "");
+            mvts.Add(issue);
+            mvts.Add(receive);
+        }
 
-            // Issuing to a different job than you received into?
-            if (result && holding.Job.ID != movement.Job.ID)
-            {
-                // Issue from Old Job (Hidden)
-                var issue = new StockMovement
-                {
-                    Date = movement.Date
-                };
-                issue.Product.ID = holding.Product.ID;
-                issue.Job.ID = holding.Job.ID;
-                issue.Location.ID = Location.ID;
-                issue.Style.ID = holding.Style.ID;
-                issue.Issued = movement.Issued;
-                issue.Transaction = movement.Transaction;
-                issue.Employee.ID = movement.Employee.ID;
-                issue.Dimensions.CopyFrom(holding.Dimensions);
-                issue.IsTransfer = true;
-                issue.Notes = string.Format("Transferred from {0}", holding.Job.ID.Equals(Guid.Empty) ? "General Stock" : "Job " + holding.Job.JobNumber);
-                issue.System = true;
-                issue.Cost = holding.AverageValue;
-                issue.Type = StockMovementType.TransferOut;
-
-                // Receive to New Job (Hidden)
-                var receive = new StockMovement();
-                receive.Date = movement.Date;
-                receive.Product.ID = holding.Product.ID;
-                receive.Job.ID = movement.Job.ID;
-                receive.Location.ID = Location.ID;
-                receive.Style.ID = holding.Style.ID;
-                receive.Received = movement.Issued;
-                receive.Transaction = movement.Transaction;
-                receive.Employee.ID = movement.Employee.ID;
-                receive.Dimensions.CopyFrom(holding.Dimensions);
-                receive.IsTransfer = true;
-                receive.Notes = string.Format("Transferred to {0}",
-                    !movement.Job.IsValid() ? "General Stock" : "Job " + movement.Job.JobNumber);
-                receive.System = true;
-                receive.Cost = holding.AverageValue;
-                receive.Type = StockMovementType.TransferIn;
-
-                new Client<StockMovement>().Save(new[] { issue, receive }, "");
-                mvts.Add(issue);
-                mvts.Add(receive);
-            }
-
-            if (result)
-            {
-                DoChanged();
-                SaveBatch(StockMovementBatchType.Issue, mvts.ToArray());
-            }
-            return result;
+        if (result)
+        {
+            DoChanged();
+            SaveBatch(StockMovementBatchType.Issue, mvts.ToArray());
         }
+        return result;
+    }
+
+    private bool TransferStock(Button arg1, CoreRow[] rows)
+    {
+        var holding = rows.First().ToObject<StockHolding>();
 
-        private bool TransferStock(Button arg1, CoreRow[] rows)
+        if(holding.Available <= 0)
         {
-            CheckStockMovementGrid();
+            MessageWindow.ShowMessage("There is no available stock in this holding to transfer.", "No available stock");
+            return false;
+        }
 
-            var holding = rows.First().ToObject<StockHolding>();
+        var movement = new StockMovement();
+        movement.Date = DateTime.Now;
 
-            var movement = new StockMovement();
-            movement.Date = DateTime.Now;
+        movement.Location.ID = Location.ID;
+        movement.Location.Code = Location.Code;
+        movement.Location.Description = Location.Description;
 
-            movement.Location.ID = Location.ID;
-            movement.Location.Code = Location.Code;
-            movement.Location.Description = Location.Description;
+        movement.Product.ID = holding.Product.ID;
+        movement.Job.ID = holding.Job.ID;
+        movement.Job.JobNumber = holding.Job.JobNumber;
+        movement.Style.ID = holding.Style.ID;
+        movement.Style.Code = holding.Style.Code;
+        movement.Employee.ID = _employeeid;
+        // Must happen before Received gets set so that OnPropertyChanged has stuff to work with and thus Qty is not zero.
+        movement.Dimensions.CopyFrom(holding.Dimensions);
+        movement.Received = holding.Units;
+        movement.IsTransfer = true;
+        movement.Cost = holding.AverageValue;
+        movement.Type = StockMovementType.TransferIn;
 
-            movement.Product.ID = holding.Product.ID;
-            movement.Job.ID = holding.Job.ID;
-            movement.Job.JobNumber = holding.Job.JobNumber;
-            movement.Style.ID = holding.Style.ID;
-            movement.Style.Code = holding.Style.Code;
-            movement.Employee.ID = _employeeid;
-            // Must happen before Received gets set so that OnPropertyChanged has stuff to work with and thus Qty is not zero.
-            movement.Dimensions.CopyFrom(holding.Dimensions);
-            movement.Received = holding.Units;
-            movement.IsTransfer = true;
-            movement.Cost = holding.AverageValue;
-            movement.Type = StockMovementType.TransferIn;
+        movement.CommitChanges();
 
-            movement.CommitChanges();
+        var smg = CheckStockMovementGrid(MovementAction.Transfer, holding);
+        var result = smg.EditItems(new[] { movement });
 
-            bTransferring = true;
-            var result = smg.EditItems(new[] { movement });
-            bTransferring = false;
+        var mvts = new List<StockMovement>
+        {
+            movement
+        };
 
-            var mvts = new List<StockMovement>
-            {
-                movement
-            };
+        if (result)
+        {
+            var other = new StockMovement();
+            other.Date = movement.Date;
+            other.Product.ID = holding.Product.ID;
+            other.Job.ID = holding.Job.ID;
+            other.Cost = holding.AverageValue;
+
+            other.Location.ID = Location.ID;
+            //other.Location.Code = Location.Code;
+            //other.Location.Description = Location.Description;
+
+            other.Style.ID = holding.Style.ID;
+            other.Issued = movement.Received;
+            other.Transaction = movement.Transaction;
+            other.Employee.ID = movement.Employee.ID;
+            other.Dimensions.CopyFrom(holding.Dimensions);
+            other.IsTransfer = true;
+            other.Type = StockMovementType.TransferOut;
+
+            var changes = new List<string>();
+            if (movement.Location.ID != other.Location.ID)
+                changes.Add(movement.Location.Code);
+            if (movement.Job.ID != other.Job.ID)
+                changes.Add(string.IsNullOrEmpty(movement.Job.JobNumber) ? "General Stock" : movement.Job.JobNumber);
+            if (movement.Style.ID != other.Style.ID)
+                changes.Add(movement.Style.Code);
+            //other.Notes = "Transferred to "+String.Join(" / ",changes);
+            other.Notes = string.Format("Transferred to {0}{1}{2}", string.Join(" / ",
+                    changes.Where(x => !string.IsNullOrWhiteSpace(x))),
+                string.IsNullOrWhiteSpace(movement.Notes) ? "" : "\n",
+                string.Join("\n", movement.Notes.Split('\n').Where(x => !x.StartsWith("Transferred from ")))
+            );
 
-            if (result)
-            {
-                var other = new StockMovement();
-                other.Date = movement.Date;
-                other.Product.ID = holding.Product.ID;
-                other.Job.ID = holding.Job.ID;
-                other.Cost = holding.AverageValue;
-
-                other.Location.ID = Location.ID;
-                //other.Location.Code = Location.Code;
-                //other.Location.Description = Location.Description;
-
-                other.Style.ID = holding.Style.ID;
-                other.Issued = movement.Received;
-                other.Transaction = movement.Transaction;
-                other.Employee.ID = movement.Employee.ID;
-                other.Dimensions.CopyFrom(holding.Dimensions);
-                other.IsTransfer = true;
-                other.Type = StockMovementType.TransferOut;
+            other.System = true;
 
-                var changes = new List<string>();
-                if (movement.Location.ID != other.Location.ID)
-                    changes.Add(movement.Location.Code);
-                if (movement.Job.ID != other.Job.ID)
-                    changes.Add(string.IsNullOrEmpty(movement.Job.JobNumber) ? "General Stock" : movement.Job.JobNumber);
-                if (movement.Style.ID != other.Style.ID)
-                    changes.Add(movement.Style.Code);
-                //other.Notes = "Transferred to "+String.Join(" / ",changes);
-                other.Notes = string.Format("Transferred to {0}{1}{2}", string.Join(" / ",
-                        changes.Where(x => !string.IsNullOrWhiteSpace(x))),
-                    string.IsNullOrWhiteSpace(movement.Notes) ? "" : "\n",
-                    string.Join("\n", movement.Notes.Split('\n').Where(x => !x.StartsWith("Transferred from ")))
-                );
-
-                other.System = true;
-
-                new Client<StockMovement>().Save(other, "");
-
-                mvts.Add(other);
-            }
+            Client.Save(other, "");
 
-            if (result)
-            {
-                DoChanged();
-                SaveBatch(StockMovementBatchType.Transfer, mvts.ToArray());
-            }
-            return result;
+            mvts.Add(other);
         }
 
-        //private bool ReserveStock(Button arg1, CoreRow[] rows)
-        //{
-        //    if (!rows.Any())
-        //    {
-        //        MessageBox.Show("Please select an item to transfer");
-        //        return false;
-        //    }
-
-        //    CheckStockMovementGrid();
-
-        //    Guid jobid = rows.First().Get<StockHolding, Guid>(x => x.Job.ID);
-        //    String jobno = rows.First().Get<StockHolding, String>(x => x.Job.JobNumber);
-        //    Guid productid = rows.First().Get<StockHolding, Guid>(x => x.Product.ID);
-        //    Guid styleid = rows.First().Get<StockHolding, Guid>(x => x.Style.ID);
-        //    double units = rows.First().Get<StockHolding, double>(x => x.Units);
-        //    double size = rows.First().Get<StockHolding, double>(x => x.UnitSize);
-        //    StockMovement movement = new StockMovement();
-        //    movement.Date = DateTime.Now;
-        //    movement.Location.ID = LocationID;
-        //    movement.Product.ID = productid;
-        //    movement.Style.ID = styleid;
-        //    movement.Employee.ID = GetEmployeeID();
-        //    movement.Job.ID = Guid.Empty;
-        //    movement.Received = units;
-        //    movement.UnitSize = size;
-
-        //    bReserving = jobid.Equals(Guid.Empty);
-        //    bReleasing = !bReserving;
-
-        //    movement.Notes = bReserving ? "Reserving Stock" : "Releasing from Job " + jobno;
-
-        //    bool result = smg.EditItems(new StockMovement[] { movement });
-
-
-        //    Issuing to a different job than you received into ?
-        //    if (result)
-        //    {
-        //        Issue from Old Job(Hidden)
-        //        StockMovement other = new StockMovement();
-        //        other.Date = movement.Date;
-        //        other.Product.ID = productid;
-        //        other.Job.ID = jobid;
-        //        other.Location.ID = LocationID;
-        //        other.Style.ID = styleid;
-        //        other.Issued = movement.Received;
-        //        other.Transaction = movement.Transaction;
-        //        other.Employee.ID = movement.Employee.ID;
-        //        other.UnitSize = size;
-        //        other.Notes = movement.Job.ID.Equals(Guid.Empty) ? "Releasing back to General Stock" : "Reserving for Job " + movement.Job.JobNumber;
-        //        other.System = true;
-
-        //        new Client<StockMovement>().Save(other, "");
-        //    }
-
-        //    bReserving = false;
-        //    bReleasing = false;
-
-        //    if (result)
-        //        OnChanged?.Invoke(this);
-        //    return result;
-        //}
-
-        protected override void Reload(Filters<StockHolding> criteria, Columns<StockHolding> columns, ref SortOrder<StockHolding>? sort, Action<CoreTable?, Exception?> action)
+        if (result)
         {
-            ReceiveButton.IsEnabled = Location != null && Location.ID != Guid.Empty;
-            if (Location == null)
-                criteria.Add(new Filter<StockHolding>().None());
-            else
-                criteria.Add(new Filter<StockHolding>(x => x.Location.ID).IsEqualTo(Location.ID));
-            base.Reload(criteria, columns, ref sort, action);
+            DoChanged();
+            SaveBatch(StockMovementBatchType.Transfer, mvts.ToArray());
         }
+        return result;
+    }
 
-        protected override bool FilterRecord(CoreRow row)
-        {
-            // Hackety Hackety Hack Hack stupid doubles not totalling zero when they're supposed to
-            var result = base.FilterRecord(row);
-            if (result)
-                result = Math.Abs(row.Get<StockHolding, double>(x => x.Qty)) >= 0.000001;
-            return result;
-        }
+    protected override void Reload(Filters<StockHolding> criteria, Columns<StockHolding> columns, ref SortOrder<StockHolding>? sort, Action<CoreTable?, Exception?> action)
+    {
+        ReceiveButton.IsEnabled = Location != null && Location.ID != Guid.Empty;
+        if (Location == null)
+            criteria.Add(new Filter<StockHolding>().None());
+        else
+            criteria.Add(new Filter<StockHolding>(x => x.Location.ID).IsEqualTo(Location.ID));
+        base.Reload(criteria, columns, ref sort, action);
+    }
+
+    protected override bool FilterRecord(CoreRow row)
+    {
+        // Hackety Hackety Hack Hack stupid doubles not totalling zero when they're supposed to
+        var result = base.FilterRecord(row);
+        if (result)
+            result = Math.Abs(row.Get<StockHolding, double>(x => x.Qty)) >= 0.000001;
+        return result;
     }
 }