using System; using System.Collections.Generic; using System.Linq; using System.Windows; using System.Windows.Controls; using Comal.Classes; using InABox.Clients; using InABox.Core; using InABox.DynamicGrid; using InABox.Wpf; using InABox.WPF; using PRSDesktop.Panels.Products.Locations; using Syncfusion.Windows.Controls.RichTextBoxAdv; using Exception = System.Exception; namespace PRSDesktop; public class StockHoldingGrid : DynamicDataGrid { private enum MovementAction { Receive, Issue, Transfer } private MovementAction _action; private StockHolding? _holding; private Button IssueButton; private Button ReceiveButton; private DynamicDataGrid smg; //Button ReserveButton = null; private Button TransferButton; private Button RecalculateButton; public StockHoldingGrid() : base() { ColumnsTag = "StockHolding"; } 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; RecalculateButton = AddButton("Recalculate", PRSDesktop.Resources.service.AsBitmapImage(), RecalculateHoldings, DynamicGridButtonPosition.Right); 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); HiddenColumns.Add(x => x.Dimensions.UnitSize); ActionColumns.Add(new DynamicMenuColumn(BuildMenu) { Position = DynamicActionColumnPosition.End }); } private bool RecalculateHoldings(Button arg1, CoreRow[] arg2) { Dictionary messages = new(); void AddMessage(String type) { messages.TryGetValue(type, out int count); messages[type] = ++count; } Progress.ShowModal("Recalculating", progress => { progress.Report("Loading Data"); MultiQuery query = new MultiQuery(); query.Add( new Filter(x => x.Location.ID).IsEqualTo(Location.ID), new Columns(x => x.ID) .Add(x => x.Product.ID) .Add(x => x.Job.ID) .Add(x => x.Style.ID) .Add(x => x.Dimensions.Unit.ID) .Add(x => x.Dimensions.Length) .Add(x => x.Dimensions.Width) .Add(x => x.Dimensions.Height) .Add(x => x.Dimensions.Quantity) .Add(x => x.Dimensions.Value) .Add(x => x.Dimensions.UnitSize) .Add(x => x.Units) .Add(x => x.AverageValue) ); query.Add( new Filter(x => x.Location.ID).IsEqualTo(Location.ID), new Columns(x => x.ID) .Add(x => x.Product.ID) .Add(x => x.Job.ID) .Add(x => x.Style.ID) .Add(x => x.Dimensions.Unit.ID) .Add(x => x.Dimensions.Length) .Add(x => x.Dimensions.Width) .Add(x => x.Dimensions.Height) .Add(x => x.Dimensions.Quantity) .Add(x => x.Dimensions.Value) .Add(x => x.Dimensions.UnitSize) .Add(x => x.Units) .Add(x => x.Cost) ); query.Query(); var holdings = query.Get().ToObjects().ToList(); var movements = query.Get().ToObjects().ToList(); progress.Report("Processing"); var updates = new List(); while (movements.Any()) { var first = movements.First(); var selected = movements.Where(x => x.Product.ID == first.Product.ID && x.Job.ID == first.Job.ID && x.Style.ID == first.Style.ID && x.Dimensions.Unit.ID == first.Dimensions.Unit.ID && x.Dimensions.Length.IsEffectivelyEqual(first.Dimensions.Length) && x.Dimensions.Width.IsEffectivelyEqual(first.Dimensions.Width) && x.Dimensions.Height.IsEffectivelyEqual(first.Dimensions.Height) && x.Dimensions.Quantity.IsEffectivelyEqual(first.Dimensions.Quantity) && x.Dimensions.Weight.IsEffectivelyEqual(first.Dimensions.Weight) && x.Dimensions.Value.IsEffectivelyEqual(first.Dimensions.Value) && String.Equals(x.Dimensions.UnitSize, first.Dimensions.UnitSize) ); var units = selected.Aggregate(0.0d, (t, s) => t += s.Units); var cost = selected.Aggregate(0.0d, (t, s) => t += (s.Units * s.Cost)); var holding = holdings.FirstOrDefault(x => x.Product.ID == first.Product.ID && x.Job.ID == first.Job.ID && x.Style.ID == first.Style.ID && x.Dimensions.Unit.ID == first.Dimensions.Unit.ID && x.Dimensions.Length.IsEffectivelyEqual(first.Dimensions.Length) && x.Dimensions.Width.IsEffectivelyEqual(first.Dimensions.Width) && x.Dimensions.Height.IsEffectivelyEqual(first.Dimensions.Height) && x.Dimensions.Quantity.IsEffectivelyEqual(first.Dimensions.Quantity) && x.Dimensions.Weight.IsEffectivelyEqual(first.Dimensions.Weight) && x.Dimensions.Length.IsEffectivelyEqual(first.Dimensions.Length) && String.Equals(x.Dimensions.UnitSize, first.Dimensions.UnitSize) ); if (holding == null) { holding = new StockHolding(); holding.Location.ID = Location.ID; holding.Product.ID = first.Product.ID; holding.Style.ID = first.Style.ID; holding.Job.ID = first.Job.ID; holding.Dimensions.Unit.ID = first.Dimensions.Unit.ID; holding.Dimensions.Length = first.Dimensions.Length; holding.Dimensions.Width = first.Dimensions.Width; holding.Dimensions.Height = first.Dimensions.Height; holding.Dimensions.Quantity = first.Dimensions.Quantity; holding.Dimensions.Weight = first.Dimensions.Weight; holding.Dimensions.Value = first.Dimensions.Value; holding.Dimensions.UnitSize = first.Dimensions.UnitSize; } holding.Units = units; holding.AverageValue = units.IsEffectivelyEqual(0.0F) ? 0.0d : cost / units; if (holdings.Contains(holding)) holdings.Remove(holding); if (holding.IsChanged() && !holding.Units.IsEffectivelyEqual(0.0f)) { AddMessage(holding.ID != Guid.Empty ? "updated" : "added"); updates.Add(holding); } movements.RemoveAll(x => selected.Any(s => s.ID == x.ID)); } foreach (var holding in holdings) AddMessage("deleted"); if (updates.Any()) { progress.Report($"Updating {updates.Count} Holdings"); new Client().Save(updates.Where(x => x.IsChanged()), "Updated by Recalculation"); } if (holdings.Any()) { progress.Report($"Deleting {holdings.Count} Holdings"); new Client().Delete(holdings, "Removed by Recalculation"); } }); MessageWindow.ShowMessage( messages.Any() ? String.Join("\n", messages.Select(x => $"{x.Value} holdings {x.Key}")) : "Nothing to Update!" ,"Recalculate"); return true; } public override DynamicGridColumns GenerateColumns() { var columns = new DynamicGridColumns(); 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); return columns; } protected override void DoReconfigure(FluentList options) { base.DoReconfigure(options); options .BeginUpdate() .Remove(DynamicGridOption.AddRows) .Remove(DynamicGridOption.EditRows) .Remove(DynamicGridOption.DeleteRows) .Add(DynamicGridOption.RecordCount) .Add(DynamicGridOption.SelectColumns) .Add(DynamicGridOption.FilterRows) .EndUpdate(); } private IEnumerable LoadRequisitionItems(StockHolding holding) { var items = Client.Query( new Filter(x => x.ID).InQuery(StockHolding.GetFilter(holding), x => x.JobRequisitionItem.ID), new Columns(x => x.ID) .Add(x => x.Job.JobNumber) .Add(x => x.Requisition.Number) .Add(x => x.Requisition.Description) .Add(x => x.Qty)) .ToObjects(); if (!holding.Available.IsEffectivelyEqual(0.0F)) items = CoreUtils.One(new JobRequisitionItem() { Qty = holding.Available }).Concat(items); return items; } private void BuildMenu(DynamicMenuColumn column, CoreRow? row) { if (row is null) return; var holding = row.ToObject(); if (holding.Available.IsEffectivelyEqual(holding.Units)) column.AddItem("(No Requisitions in this Holding", null, null).IsEnabled = false; else column.AddItem("View Requisition Items", null, ViewRequisitions_Click); column.AddSeparator(); var requiitems = LoadRequisitionItems(holding).ToList(); column.AddItem("Relocate Items", null, r => RelocateItems(holding, requiitems.ToArray())); } private class StockJobSelection : BaseObject { [EditorSequence(1)] public JobLink Job { get; set; } [EditorSequence(2)] public double Qty { get; set; } } private void RelocateItems(StockHolding holding, JobRequisitionItem[] requiitems) { var win = new StockHoldingRelocationWindow(holding, requiitems); if (win.ShowDialog() == true) { var quantities = win.GetQuantities(); var target = win.GetTargetLocation(); List updates = new List(); foreach (var requiitem in requiitems) { if (!quantities.TryGetValue(requiitem.ID, out var qty)) continue; var mout = new StockMovement(); mout.Location.ID = holding.Location.ID; mout.Product.ID = holding.Product.ID; mout.Style.ID = holding.Style.ID; mout.Dimensions.CopyFrom(holding.Dimensions); mout.Job.ID = holding.Job.ID; mout.Issued = Math.Min(requiitem.Qty, qty); mout.Cost = holding.AverageValue; mout.JobRequisitionItem.ID = requiitem.ID; mout.Type = StockMovementType.TransferOut; mout.Date = DateTime.Now; mout.IsTransfer = true; mout.Employee.ID = App.EmployeeID; mout.Notes = $"Moved to {target.Code} by {App.EmployeeName}"; updates.Add(mout); var min = new StockMovement(); min.Location.ID = target.ID; min.Product.ID = holding.Product.ID; min.Style.ID = holding.Style.ID; min.Dimensions.CopyFrom(holding.Dimensions); min.Job.ID = holding.Job.ID; min.Received = mout.Issued; min.Cost = holding.AverageValue; min.JobRequisitionItem.ID = requiitem.ID; min.Transaction = mout.Transaction; min.Type = StockMovementType.TransferIn; min.Date = mout.Date; min.IsTransfer = true; min.Employee.ID = App.EmployeeID; min.Notes = $"Moved From {holding.Location.Code} by {App.EmployeeName}"; updates.Add(min); } SaveBatch(StockMovementBatchType.Transfer, updates); DoChanged(); Refresh(false, true); } } private void ViewRequisitions_Click(CoreRow? row) { if (row is null) return; var holding = row.ToObject(); var grid = (DynamicGridUtils.CreateDynamicGrid(typeof(DynamicDataGrid<>), typeof(JobRequisitionItem)) as DynamicDataGrid)!; grid.OnDefineFilter += (type) => { if(type == typeof(JobRequisitionItem)) { return new Filter(x => x.ID) .InQuery( StockHolding.GetFilter(holding), x => x.JobRequisitionItem.ID); } else { return null; } }; DynamicGridUtils.CreateGridWindow("Job Requisition Items for stock holding", grid).ShowDialog(); } 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 = App.EmployeeID; movement.Type = StockMovementType.Receive; movement.CommitChanges(); var holding = rows.First().ToObject(); var smg = CheckStockMovementGrid(MovementAction.Receive, holding); var result = smg.EditItems(new[] { movement }); if (result) { DoChanged(); SaveBatch(StockMovementBatchType.Receipt, new StockMovement[] { movement}); } return result; } private static void SaveBatch(StockMovementBatchType type, IList movements) { var batch = new StockMovementBatch(); batch.Type = type; batch.Notes = batch.Type + " batch created from Desktop Stock Location Screen"; batch.Employee.ID = App.EmployeeID; new Client().Save(batch, "created from Desktop Stock Location Screen"); foreach (var mvt in movements) { mvt.Batch.ID = batch.ID; } new Client().Save(movements, "Updating batch from Desktop Stock Location Screen"); } private bool IssueStock(Button arg1, CoreRow[] rows) { if (rows?.Length != 1) { MessageWindow.ShowMessage("Please select an item to issue", "No selected items"); return false; } var holding = rows.First().ToObject(); var items = LoadRequisitionItems(holding).AsArray(); DoIssue(holding, items); return false; } private IEnumerable CreateIssue(StockHolding holding, double qty, Guid jobID, Guid requiID) { var issue = CreateMovementFromHolding(holding); issue.Job.ID = jobID; issue.Type = StockMovementType.Issue; issue.JobRequisitionItem.ID = requiID; issue.Issued = qty; issue.Notes = $"Issued by {App.EmployeeName}"; yield return issue; if (holding.Job.ID != issue.Job.ID) { var xferout = CreateMovementFromHolding(holding); xferout.Type = StockMovementType.TransferOut; xferout.JobRequisitionItem.ID = requiID; xferout.Issued = qty; xferout.Transaction = issue.Transaction; xferout.IsTransfer = true; xferout.Notes = $"Issued by {App.EmployeeName}"; yield return xferout; var xferin = CreateMovementFromHolding(holding); xferin.Job.ID = issue.Job.ID; xferin.Type = StockMovementType.TransferIn; xferin.JobRequisitionItem.ID = requiID; xferin.Received = qty; xferin.Transaction = issue.Transaction; xferin.IsTransfer = true; xferin.Notes = $"Issued by {App.EmployeeName}"; yield return xferin; } } private void DoIssue(StockHolding holding, JobRequisitionItem[] requiitems) { var updates = new List(); if(requiitems.Length > 1 || requiitems.Any(x => x.ID != Guid.Empty)) { var win = new StockHoldingRelocationWindow(holding, requiitems) { IsTargetEditable = false }; if (win.ShowDialog() == true) { var quantities = win.GetQuantities(); var target = win.GetTargetLocation(); foreach(var requi in requiitems) { if (!quantities.TryGetValue(requi.ID, out var qty)) continue; updates.AddRange(CreateIssue(holding, qty, requi.ID != Guid.Empty ? requi.Job.ID : holding.Job.ID, requi.ID)); } DoChanged(); Refresh(false,true); SaveBatch(StockMovementBatchType.Issue, updates.ToArray()); } } else { var sjs = new StockJobSelection(); sjs.Job.ID = holding.Job.ID; sjs.Qty = requiitems[0].Qty; var sjg = new DynamicItemsListGrid(); sjg.OnValidate += (sender, items, errors) => { if (items[0].Qty > requiitems[0].Qty) errors.Add($"Qty must not exceed {requiitems[0].Qty}"); }; if (sjg.EditItems(new StockJobSelection[] { sjs })) { var mvts = CreateIssue(holding, sjs.Qty, sjs.Job.ID, Guid.Empty); SaveBatch(StockMovementBatchType.Issue, mvts.AsArray()); } } } private bool TransferStock(Button arg1, CoreRow[] rows) { if (rows?.Length != 1) return false; var holding = rows.First().ToObject(); var items = LoadRequisitionItems(holding).AsArray(); DoTransfer(holding, items); return false; } private void DoTransfer(StockHolding holding, JobRequisitionItem[] requiitems) { if (requiitems.Length > 1 || requiitems[0].Requisition.ID != Guid.Empty) { RelocateItems(holding, requiitems); return; } var movement = CreateMovementFromHolding(holding); movement.JobRequisitionItem.ID = requiitems[0].ID; movement.Received = holding.Units; movement.IsTransfer = true; movement.Type = StockMovementType.TransferIn; var smg = CheckStockMovementGrid(MovementAction.Transfer, holding); var result = smg.EditItems(new[] { movement }); var mvts = new List { movement }; if (result) { var other = CreateMovementFromHolding(holding); other.Issued = movement.Received; other.Transaction = movement.Transaction; other.IsTransfer = true; other.Type = StockMovementType.TransferOut; other.JobRequisitionItem.ID = requiitems[0].ID; var changes = new List(); 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; Client.Save(other, ""); mvts.Add(other); } if (result) { DoChanged(); SaveBatch(StockMovementBatchType.Transfer, mvts.ToArray()); } Refresh(false, true); } private StockMovement CreateMovementFromHolding(StockHolding holding) { var movement = new StockMovement(); movement.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.Employee.ID = App.EmployeeID; movement.Dimensions.CopyFrom(holding.Dimensions); movement.Cost = holding.AverageValue; movement.CommitChanges(); return movement; } public IStockLocation Location { get; set; } protected override void SelectItems(CoreRow[]? rows) { base.SelectItems(rows); 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 CheckStockMovementGrid(MovementAction action, StockHolding holding) { _action = action; _holding = holding; if (smg == null) { smg = new DynamicDataGrid(); smg.OnCustomiseEditor += StockMovementCustomiseEditor; smg.OnValidate += StockMovementValidate; smg.OnEditorValueChanged += StockMovementValueChanged; } return smg; } private Dictionary StockMovementValueChanged(IDynamicEditorForm form, string name, object value) { var result = new Dictionary(); 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 errors) { if (items.Any(x => x.Received == 0 && x.Issued == 0)) { 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 (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 (!errors.Any() && _action == MovementAction.Transfer) foreach (var item in items) { var changes = new List(); 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"); } } 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("Product.ID")) editor.Editable = _action == MovementAction.Receive ? Editable.Enabled : Editable.Disabled; if (column.ColumnName.Equals("Product.NettCost")) editor.Editable = 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 = _action == MovementAction.Receive ? Editable.Enabled : Editable.Hidden; if (column.ColumnName.Equals("Job.ID")) editor.Editable = _action == MovementAction.Receive && items?.FirstOrDefault()?.Job.IsValid() == true ? Editable.Disabled : Editable.Enabled; if (column.ColumnName.Equals(nameof(StockMovement.Received))) { editor.Editable = _action == MovementAction.Receive || _action == MovementAction.Transfer ? Editable.Enabled : Editable.Hidden; editor.Caption = "Quantity"; } if (column.ColumnName.Equals(nameof(StockMovement.Issued))) { editor.Editable = _action == MovementAction.Issue ? Editable.Enabled : Editable.Hidden; editor.Caption = "Quantity"; } } protected override void Reload(Filters criteria, Columns columns, ref SortOrder? sort, Action action) { ReceiveButton.IsEnabled = Location != null && Location.ID != Guid.Empty; if (Location == null) criteria.Add(new Filter().None()); else criteria.Add(new Filter(x => x.Location.ID).IsEqualTo(Location.ID)); base.Reload(criteria, columns, ref sort, action); } }