|
@@ -9,6 +9,8 @@ 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;
|
|
@@ -264,6 +266,21 @@ public class StockHoldingGrid : DynamicDataGrid<StockHolding>
|
|
|
.EndUpdate();
|
|
|
}
|
|
|
|
|
|
+ private IEnumerable<JobRequisitionItem> LoadRequisitionItems(StockHolding holding)
|
|
|
+ {
|
|
|
+ var items = Client.Query(
|
|
|
+ new Filter<JobRequisitionItem>(x => x.ID).InQuery(StockHolding.GetFilter(holding), x => x.JobRequisitionItem.ID),
|
|
|
+ new Columns<JobRequisitionItem>(x => x.ID)
|
|
|
+ .Add(x => x.Job.JobNumber)
|
|
|
+ .Add(x => x.Requisition.Number)
|
|
|
+ .Add(x => x.Requisition.Description)
|
|
|
+ .Add(x => x.Qty))
|
|
|
+ .ToObjects<JobRequisitionItem>();
|
|
|
+ if (!holding.Available.EqualsWithTolerance(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;
|
|
@@ -276,46 +293,9 @@ public class StockHoldingGrid : DynamicDataGrid<StockHolding>
|
|
|
|
|
|
column.AddSeparator();
|
|
|
|
|
|
- var requiitems = new Client<JobRequisitionItem>()
|
|
|
- .Query(
|
|
|
- new Filter<JobRequisitionItem>(x => x.ID).InQuery(StockHolding.GetFilter(holding), x => x.JobRequisitionItem.ID),
|
|
|
- new Columns<JobRequisitionItem>(x=>x.ID)
|
|
|
- .Add(x=>x.Job.JobNumber)
|
|
|
- .Add(x=>x.Requisition.Number)
|
|
|
- .Add(x=>x.Requisition.Description)
|
|
|
- .Add(x=>x.Qty)
|
|
|
- ).ToObjects<JobRequisitionItem>()
|
|
|
- .ToList();
|
|
|
- if (!holding.Available.EqualsWithTolerance(0.0F))
|
|
|
- requiitems.Insert(0, new JobRequisitionItem() { Qty = holding.Available });
|
|
|
-
|
|
|
- if (requiitems.Count <= 1)
|
|
|
- column.AddItem("Relocate Items", null, r => RelocateItems(holding, requiitems.ToArray()));
|
|
|
- else
|
|
|
- {
|
|
|
- var header = column.AddItem("Relocate Items", null, null);
|
|
|
- column.AddItem("All Items", null, r => RelocateItems(holding, requiitems.ToArray()), header);
|
|
|
- column.AddSeparator(header);
|
|
|
-
|
|
|
- if (!holding.Available.EqualsWithTolerance(0.0F))
|
|
|
- column.AddItem("Un-Requisitioned Items", null, r => RelocateItems(holding, requiitems.Where(x=>x.ID == Guid.Empty).ToArray()), header);
|
|
|
-
|
|
|
- foreach (var requiitem in requiitems)
|
|
|
- {
|
|
|
- string name =
|
|
|
- $"{requiitem.Job.JobNumber}:{requiitem.Requisition.Number} {requiitem.Requisition.Description} ({requiitem.Qty})";
|
|
|
- column.AddItem(name, null, r => RelocateItems(holding, requiitems.Where(x=>x.ID == requiitem.ID).ToArray()), header);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private class StockLocationSelection : BaseObject
|
|
|
- {
|
|
|
- [EditorSequence(1)]
|
|
|
- public StockLocationLink Location { get; set; }
|
|
|
+ var requiitems = LoadRequisitionItems(holding).ToList();
|
|
|
|
|
|
- [EditorSequence(2)]
|
|
|
- public double Qty { get; set; }
|
|
|
+ column.AddItem("Relocate Items", null, r => RelocateItems(holding, requiitems.ToArray()));
|
|
|
}
|
|
|
|
|
|
private class StockJobSelection : BaseObject
|
|
@@ -326,48 +306,38 @@ public class StockHoldingGrid : DynamicDataGrid<StockHolding>
|
|
|
[EditorSequence(2)]
|
|
|
public double Qty { get; set; }
|
|
|
}
|
|
|
-
|
|
|
|
|
|
private void RelocateItems(StockHolding holding, JobRequisitionItem[] requiitems)
|
|
|
{
|
|
|
- var max = requiitems.Aggregate(0.0d, (t, x) => t += x.Qty);
|
|
|
- var grid = new DynamicItemsListGrid<StockLocationSelection>();
|
|
|
- grid.OnValidate += (sender, items, errors) =>
|
|
|
- {
|
|
|
- var total = items.Aggregate(0.0d, (t, x) => t += x.Qty);
|
|
|
- if (total > max)
|
|
|
- errors.Add($"Qty must not exceed {max}");
|
|
|
- };
|
|
|
- grid.OnCustomiseEditor += (sender, items, column, editor) =>
|
|
|
- {
|
|
|
- if (column.ColumnName.Equals(nameof(StockLocationSelection.Qty)) && requiitems?.Length != 1)
|
|
|
- editor.Editable = Editable.Disabled;
|
|
|
- };
|
|
|
- var selection = new StockLocationSelection() { Qty = max };
|
|
|
- if (grid.EditItems(new StockLocationSelection[] { selection }))
|
|
|
+ var win = new StockHoldingRelocationWindow(holding, requiitems);
|
|
|
+ if (win.ShowDialog() == true)
|
|
|
{
|
|
|
+ var quantities = win.GetQuantities();
|
|
|
+ var target = win.GetTargetLocation();
|
|
|
+
|
|
|
List<StockMovement> updates = new List<StockMovement>();
|
|
|
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, selection.Qty);
|
|
|
+ mout.Issued = Math.Min(requiitem.Qty, qty);
|
|
|
mout.Cost = holding.AverageValue;
|
|
|
mout.JobRequisitionItem.ID = requiitem.ID;
|
|
|
- mout.Transaction = Guid.NewGuid();
|
|
|
mout.Type = StockMovementType.TransferOut;
|
|
|
mout.Date = DateTime.Now;
|
|
|
mout.IsTransfer = true;
|
|
|
mout.Employee.ID = App.EmployeeID;
|
|
|
- mout.Notes = $"Moved to {selection.Location.Code} by {App.EmployeeName}";
|
|
|
+ mout.Notes = $"Moved to {target.Code} by {App.EmployeeName}";
|
|
|
updates.Add(mout);
|
|
|
|
|
|
var min = new StockMovement();
|
|
|
- min.Location.ID = selection.Location.ID;
|
|
|
+ min.Location.ID = target.ID;
|
|
|
min.Product.ID = holding.Product.ID;
|
|
|
min.Style.ID = holding.Style.ID;
|
|
|
min.Dimensions.CopyFrom(holding.Dimensions);
|
|
@@ -383,9 +353,10 @@ public class StockHoldingGrid : DynamicDataGrid<StockHolding>
|
|
|
min.Notes = $"Moved From {holding.Location.Code} by {App.EmployeeName}";
|
|
|
updates.Add(min);
|
|
|
}
|
|
|
- new Client<StockMovement>().Save(updates, "Relocated from Stock Locations Screen");
|
|
|
+ SaveBatch(StockMovementBatchType.Transfer, updates);
|
|
|
+ DoChanged();
|
|
|
+ Refresh(false, true);
|
|
|
}
|
|
|
-
|
|
|
}
|
|
|
|
|
|
private void ViewRequisitions_Click(CoreRow? row)
|
|
@@ -412,125 +383,6 @@ public class StockHoldingGrid : DynamicDataGrid<StockHolding>
|
|
|
DynamicGridUtils.CreateGridWindow("Job Requisition Items for stock holding", grid).ShowDialog();
|
|
|
}
|
|
|
|
|
|
- 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<StockMovement> CheckStockMovementGrid(MovementAction action, StockHolding holding)
|
|
|
- {
|
|
|
- _action = action;
|
|
|
- _holding = holding;
|
|
|
- if (smg == null)
|
|
|
- {
|
|
|
- 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);
|
|
|
- }
|
|
|
-
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- private void StockMovementValidate(object sender, StockMovement[] items, List<string> 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<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");
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- 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 ? Editable.Enabled : Editable.Hidden;
|
|
|
- editor.Caption = "Quantity";
|
|
|
- }
|
|
|
- if (column.ColumnName.Equals(nameof(StockMovement.Issued)))
|
|
|
- {
|
|
|
- editor.Editable = _action == MovementAction.Issue || _action == MovementAction.Transfer ? Editable.Enabled : Editable.Hidden;
|
|
|
- editor.Caption = "Quantity";
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
private bool ReceiveStock(Button arg1, CoreRow[] rows)
|
|
|
{
|
|
|
var movement = new StockMovement();
|
|
@@ -560,7 +412,7 @@ public class StockHoldingGrid : DynamicDataGrid<StockHolding>
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
- private void SaveBatch(StockMovementBatchType type, StockMovement[] movements)
|
|
|
+ private static void SaveBatch(StockMovementBatchType type, IList<StockMovement> movements)
|
|
|
{
|
|
|
var batch = new StockMovementBatch();
|
|
|
batch.Type = type;
|
|
@@ -585,150 +437,89 @@ public class StockHoldingGrid : DynamicDataGrid<StockHolding>
|
|
|
}
|
|
|
|
|
|
var holding = rows.First().ToObject<StockHolding>();
|
|
|
- SelectAllocation(holding, (h, items) => DoIssue(h, items));
|
|
|
+ var items = LoadRequisitionItems(holding).AsArray();
|
|
|
+
|
|
|
+ DoIssue(holding, items);
|
|
|
return false;
|
|
|
-
|
|
|
}
|
|
|
|
|
|
- private void DoIssue(StockHolding holding, JobRequisitionItem[] requiitems)
|
|
|
+ private IEnumerable<StockMovement> 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<StockMovement>();
|
|
|
- // At this point we either have
|
|
|
- // 1. All Items (Multiple requiitems) -> no editing available
|
|
|
- // 2. UnRequid Items (single requiitem, requiid = empty) - Full Editor
|
|
|
- // 3. Requi'd Item (single requiitem, requiid != empty - Quantity only
|
|
|
|
|
|
- if (requiitems.Length > 1)
|
|
|
+ if(requiitems.Length > 0 || requiitems.Any(x => x.ID != Guid.Empty))
|
|
|
{
|
|
|
- if (MessageWindow.ShowOKCancel(
|
|
|
- "This will issue everything from this holding!\nAre you sure you wish to continue?",
|
|
|
- "Confirm Issue", null) == true)
|
|
|
+ var win = new StockHoldingRelocationWindow(holding, requiitems)
|
|
|
{
|
|
|
- foreach (var requiitem in requiitems)
|
|
|
+ IsTargetEditable = false
|
|
|
+ };
|
|
|
+ if (win.ShowDialog() == true)
|
|
|
+ {
|
|
|
+ var quantities = win.GetQuantities();
|
|
|
+ var target = win.GetTargetLocation();
|
|
|
+
|
|
|
+ foreach(var requi in requiitems)
|
|
|
{
|
|
|
- var movement = CreateMovementFromHolding(holding);
|
|
|
- movement.Type = StockMovementType.Issue;
|
|
|
- movement.JobRequisitionItem.ID = requiitem.ID;
|
|
|
- movement.Issued = requiitem.Qty;
|
|
|
- movement.Transaction = Guid.NewGuid();
|
|
|
- movement.Notes = $"Issued by {App.EmployeeName}";
|
|
|
- updates.Add(movement);
|
|
|
+ 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));
|
|
|
}
|
|
|
- SaveBatch(StockMovementBatchType.Issue, updates.ToArray());
|
|
|
DoChanged();
|
|
|
Refresh(false,true);
|
|
|
+ SaveBatch(StockMovementBatchType.Issue, updates.ToArray());
|
|
|
}
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- var sjs = new StockJobSelection();
|
|
|
- sjs.Job.ID = holding.Job.ID;
|
|
|
- sjs.Qty = requiitems[0].Qty;
|
|
|
-
|
|
|
- var sjg = new DynamicItemsListGrid<StockJobSelection>();
|
|
|
- sjg.OnValidate += (sender, items, errors) =>
|
|
|
- {
|
|
|
- if (items[0].Qty > requiitems[0].Qty)
|
|
|
- errors.Add($"Qty must not exceed {requiitems[0].Qty}");
|
|
|
- };
|
|
|
- sjg.OnCustomiseEditor += (sender, items, column, editor) =>
|
|
|
- {
|
|
|
- if (column.ColumnName.Equals(CoreUtils.GetFullPropertyName<StockJobSelection, Guid>(x => x.Job.ID, ".")))
|
|
|
- editor.Editable = requiitems[0].ID == Guid.Empty
|
|
|
- ? Editable.Enabled
|
|
|
- : Editable.Disabled;
|
|
|
- };
|
|
|
- if (sjg.EditItems(new StockJobSelection[] { sjs }))
|
|
|
- {
|
|
|
- var issue = CreateMovementFromHolding(holding);
|
|
|
- issue.Job.ID = sjs.Job.ID;
|
|
|
- issue.Type = StockMovementType.Issue;
|
|
|
- issue.JobRequisitionItem.ID = requiitems[0].ID;
|
|
|
- issue.Issued = sjs.Qty;
|
|
|
- issue.Transaction = Guid.NewGuid();
|
|
|
- issue.Notes = $"Issued by {App.EmployeeName}";
|
|
|
- updates.Add(issue);
|
|
|
- if (holding.Job.ID != issue.Job.ID)
|
|
|
- {
|
|
|
- var xferout = CreateMovementFromHolding(holding);
|
|
|
- xferout.Type = StockMovementType.TransferOut;
|
|
|
- xferout.JobRequisitionItem.ID = requiitems[0].ID;
|
|
|
- xferout.Issued = sjs.Qty;
|
|
|
- xferout.Transaction = issue.Transaction;
|
|
|
- xferout.IsTransfer = true;
|
|
|
- xferout.Notes = $"Issued by {App.EmployeeName}";
|
|
|
- updates.Add(xferout);
|
|
|
-
|
|
|
- var xferin = CreateMovementFromHolding(holding);
|
|
|
- xferin.Job.ID = sjs.Job.ID;
|
|
|
- xferin.Type = StockMovementType.TransferOut;
|
|
|
- xferin.JobRequisitionItem.ID = requiitems[0].ID;
|
|
|
- xferin.Received = sjs.Qty;
|
|
|
- xferin.Transaction = issue.Transaction;
|
|
|
- xferin.IsTransfer = true;
|
|
|
- xferin.Notes = $"Issued by {App.EmployeeName}";
|
|
|
- updates.Add(xferin);
|
|
|
-
|
|
|
- }
|
|
|
- SaveBatch(StockMovementBatchType.Issue, updates.ToArray());
|
|
|
- DoChanged();
|
|
|
- Refresh(false,true);
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- private void SelectAllocation(StockHolding holding, Action<StockHolding,JobRequisitionItem[]> action)
|
|
|
- {
|
|
|
-
|
|
|
- var requiitems = new Client<JobRequisitionItem>()
|
|
|
- .Query(
|
|
|
- new Filter<JobRequisitionItem>(x => x.ID).InQuery(StockHolding.GetFilter(holding), x => x.JobRequisitionItem.ID),
|
|
|
- new Columns<JobRequisitionItem>(x=>x.ID)
|
|
|
- .Add(x=>x.Job.JobNumber)
|
|
|
- .Add(x=>x.Requisition.Number)
|
|
|
- .Add(x=>x.Requisition.Description)
|
|
|
- .Add(x=>x.Qty)
|
|
|
- ).ToObjects<JobRequisitionItem>()
|
|
|
- .ToList();
|
|
|
- if (!holding.Available.EqualsWithTolerance(0.0F))
|
|
|
- requiitems.Insert(0, new JobRequisitionItem() { Qty = holding.Available });
|
|
|
-
|
|
|
- if (requiitems.Count <= 1)
|
|
|
- action(holding, requiitems.ToArray());
|
|
|
else
|
|
|
{
|
|
|
+ var sjs = new StockJobSelection();
|
|
|
+ sjs.Job.ID = holding.Job.ID;
|
|
|
+ sjs.Qty = requiitems[0].Qty;
|
|
|
|
|
|
- ContextMenu menu = new ContextMenu();
|
|
|
-
|
|
|
- MenuItem all = new MenuItem();
|
|
|
- all.Header =
|
|
|
- $"All Items ({holding.Units})";
|
|
|
- all.Click += (sender, args) => action(holding,requiitems.ToArray());
|
|
|
- menu.Items.Add(all);
|
|
|
- menu.Items.Add(new Separator());
|
|
|
-
|
|
|
- if (!holding.Available.EqualsWithTolerance(0.0F))
|
|
|
+ var sjg = new DynamicItemsListGrid<StockJobSelection>();
|
|
|
+ sjg.OnValidate += (sender, items, errors) =>
|
|
|
{
|
|
|
- MenuItem item = new MenuItem();
|
|
|
- item.Header =
|
|
|
- $"Un-Requisitioned Items ({holding.Available})";
|
|
|
- item.Click += (sender, args) =>
|
|
|
- action(holding, requiitems.Where(x => x.ID == Guid.Empty).ToArray());
|
|
|
- menu.Items.Add(item);
|
|
|
- }
|
|
|
-
|
|
|
- foreach (var requiitem in requiitems)
|
|
|
+ if (items[0].Qty > requiitems[0].Qty)
|
|
|
+ errors.Add($"Qty must not exceed {requiitems[0].Qty}");
|
|
|
+ };
|
|
|
+ if (sjg.EditItems(new StockJobSelection[] { sjs }))
|
|
|
{
|
|
|
- MenuItem item = new MenuItem();
|
|
|
- item.Header =
|
|
|
- $"{requiitem.Job.JobNumber}:{requiitem.Requisition.Number} {requiitem.Requisition.Description} ({requiitem.Qty})";
|
|
|
- item.Click += (sender, args) => action(holding, requiitems.Where(x=>x.ID == requiitem.ID).ToArray());
|
|
|
- menu.Items.Add(item);
|
|
|
-
|
|
|
+ var mvts = CreateIssue(holding, sjs.Qty, sjs.Job.ID, Guid.Empty);
|
|
|
+ SaveBatch(StockMovementBatchType.Issue, mvts.AsArray());
|
|
|
}
|
|
|
- menu.IsOpen = true;
|
|
|
}
|
|
|
-
|
|
|
}
|
|
|
|
|
|
private bool TransferStock(Button arg1, CoreRow[] rows)
|
|
@@ -737,7 +528,9 @@ public class StockHoldingGrid : DynamicDataGrid<StockHolding>
|
|
|
return false;
|
|
|
|
|
|
var holding = rows.First().ToObject<StockHolding>();
|
|
|
- SelectAllocation(holding, (h,items) => DoTransfer(h, items) );
|
|
|
+ var items = LoadRequisitionItems(holding).AsArray();
|
|
|
+
|
|
|
+ DoTransfer(holding, items);
|
|
|
return false;
|
|
|
}
|
|
|
|
|
@@ -798,7 +591,7 @@ public class StockHoldingGrid : DynamicDataGrid<StockHolding>
|
|
|
DoChanged();
|
|
|
SaveBatch(StockMovementBatchType.Transfer, mvts.ToArray());
|
|
|
}
|
|
|
- Refresh(true,false);
|
|
|
+ Refresh(false, true);
|
|
|
}
|
|
|
|
|
|
private StockMovement CreateMovementFromHolding(StockHolding holding)
|
|
@@ -819,6 +612,125 @@ public class StockHoldingGrid : DynamicDataGrid<StockHolding>
|
|
|
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<StockMovement> CheckStockMovementGrid(MovementAction action, StockHolding holding)
|
|
|
+ {
|
|
|
+ _action = action;
|
|
|
+ _holding = holding;
|
|
|
+ if (smg == null)
|
|
|
+ {
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void StockMovementValidate(object sender, StockMovement[] items, List<string> 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<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");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ 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 ? Editable.Enabled : Editable.Hidden;
|
|
|
+ editor.Caption = "Quantity";
|
|
|
+ }
|
|
|
+ if (column.ColumnName.Equals(nameof(StockMovement.Issued)))
|
|
|
+ {
|
|
|
+ editor.Editable = _action == MovementAction.Issue || _action == MovementAction.Transfer ? Editable.Enabled : Editable.Hidden;
|
|
|
+ editor.Caption = "Quantity";
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
|
|
|
protected override void Reload(Filters<StockHolding> criteria, Columns<StockHolding> columns, ref SortOrder<StockHolding>? sort, Action<CoreTable?, Exception?> action)
|
|
|
{
|