using Comal.Classes;
using InABox.Clients;
using InABox.Configuration;
using InABox.Core;
using InABox.DynamicGrid;
using InABox.WPF;
using Microsoft.Win32;
using NPOI.SS.Formula.Functions;
using NPOI.SS.UserModel;
using NPOI.SS.Util;
using NPOI.XSSF.UserModel;
using org.omg.PortableInterceptor;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
namespace PRSDesktop
{
///
/// Interaction logic for StockTakeWindow.xaml
///
public partial class StockTakeWindow : Window
{
#region Fields / constructor
CoreRow[] Rows;
List StockTakeHoldings = new List();
List OriginalHoldings = new List();
Guid EmployeeID = Guid.Empty;
public StockTakeWindow(CoreRow[] rows)
{
InitializeComponent();
Rows = rows;
Setup();
}
#endregion
#region Setup
private void Setup()
{
SetupStockHoldingGrid();
Progress.ShowModal("Loading", (progress) =>
{
EmployeeID = new Client().Query(new Filter(x => x.UserLink.ID).IsEqualTo(ClientFactory.UserGuid)).Rows.FirstOrDefault().Get(x => x.ID);
QueryList(Rows.Select(x => x.Get(c => c.ID)));
Dispatcher.Invoke(() =>
{
Holdings.Items = StockTakeHoldings;
Holdings.Refresh(true, true);
});
});
}
private void SetupStockHoldingGrid()
{
Holdings.Reconfigure(options =>
{
options.Remove(DynamicGridOption.EditRows);
options.Remove(DynamicGridOption.DeleteRows);
options.Remove(DynamicGridOption.ImportData);
options.Remove(DynamicGridOption.ExportData);
options.Remove(DynamicGridOption.SelectColumns);
options.Add(DynamicGridOption.RecordCount);
options.Add(DynamicGridOption.AddRows);
options.Add(DynamicGridOption.DirectEdit);
});
Holdings.OnCustomiseEditor += Page_OnCustomiseEditor;
Holdings.OnValidate += Page_OnValidate;
Holdings.OnBeforeSave += Page_OnSave;
Holdings.AddButton("Pre-Fill Actual Qty", PRSDesktop.Resources.copy.AsBitmapImage(), FillValues);
Holdings.AddButton("Reset Actual Qty to Zero", PRSDesktop.Resources.refresh.AsBitmapImage(), ClearValues);
Holdings.AddButton("Export", PRSDesktop.Resources.doc_xls.AsBitmapImage(), Export);
}
private void QueryList(IEnumerable ids)
{
CoreTable table = new Client().Query(new Filter(x => x.Location.ID).InList(ids.ToArray())
.And(x => x.Dimensions.UnitSize).IsNotEqualTo(null)
.And(x => x.Qty).IsGreaterThan(0.1),
new Columns(
x => x.ID,
x => x.Qty,
x => x.Units,
x => x.Job.ID,
x => x.Job.JobNumber,
x => x.Product.ID,
x => x.Product.Code,
x => x.Product.Name,
x => x.Style.ID,
x => x.Style.Code,
x => x.Style.Description,
x => x.Location.ID,
x => x.Location.Code,
x => x.Location.Description,
x => x.Dimensions.Unit.ID,
x => x.Dimensions.UnitSize,
x => x.Dimensions.Unit.Format,
x => x.Dimensions.Unit.Formula,
x => x.Dimensions.Unit.HasLength,
x => x.Dimensions.Unit.HasHeight,
x => x.Dimensions.Unit.HasWeight,
x => x.Dimensions.Unit.HasWidth,
x => x.Dimensions.Unit.HasQuantity,
x => x.Dimensions.Quantity,
x => x.Dimensions.Length,
x => x.Dimensions.Height,
x => x.Dimensions.Width,
x => x.Dimensions.Weight
));
foreach (CoreRow row in table.Rows)
{
StockHolding holding = row.ToObject();
OriginalHoldings.Add(holding);
StockTakeHolding displayHolding = new StockTakeHolding();
displayHolding.ID = holding.ID;
displayHolding.OriginalQty = holding.Units;
displayHolding.Job.ID = holding.Job.ID;
displayHolding.Job.JobNumber = holding.Job.JobNumber;
displayHolding.Product.ID = holding.Product.ID;
displayHolding.Product.Code = holding.Product.Code;
displayHolding.Product.Name = holding.Product.Name;
displayHolding.Style.ID = holding.Style.ID;
displayHolding.Style.Code = holding.Style.Code;
displayHolding.Style.Description = holding.Style.Description;
displayHolding.Location.ID = holding.Location.ID;
displayHolding.Location.Code = holding.Location.Code;
displayHolding.Location.Description = holding.Location.Description;
displayHolding = CopyDimensions(displayHolding, holding);
StockTakeHoldings.Add(displayHolding);
}
}
private StockTakeHolding CopyDimensions(StockTakeHolding display, StockHolding holding)
{
display.Dimensions.Unit.ID = holding.Dimensions.Unit.ID;
display.Dimensions.Unit.HasQuantity = holding.Dimensions.Unit.HasQuantity;
display.Dimensions.Unit.HasLength = holding.Dimensions.Unit.HasLength;
display.Dimensions.Unit.HasHeight = holding.Dimensions.Unit.HasHeight;
display.Dimensions.Unit.HasWeight = holding.Dimensions.Unit.HasWeight;
display.Dimensions.Unit.HasWidth = holding.Dimensions.Unit.HasWidth;
display.Dimensions.Quantity = holding.Dimensions.Quantity;
display.Dimensions.Length = holding.Dimensions.Length;
display.Dimensions.Height = holding.Dimensions.Height;
display.Dimensions.Weight = holding.Dimensions.Weight;
display.Dimensions.Width = holding.Dimensions.Width;
display.Dimensions.Unit.Format = holding.Dimensions.Unit.Format;
display.Dimensions.Unit.Formula = holding.Dimensions.Unit.Formula;
return display;
}
#region Editor Setup
private void Page_OnSave(IDynamicEditorForm editor, BaseObject[] items)
{
foreach (StockTakeHolding item in items.Cast())
{
item.UpdateDimensions();
}
}
private void Page_OnValidate(object sender, StockTakeHolding[] items, List errors)
{
var bQty = false;
var bProd = false;
var bLocn = false;
foreach (var item in items)
{
bQty = bQty || item.NewQty <= 0.0001F;
bProd = bProd || !item.Product.IsValid();
bLocn = bLocn || !item.Location.IsValid();
}
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");
}
private void Page_OnCustomiseEditor(IDynamicEditorForm sender, StockTakeHolding[]? items, DynamicGridColumn column, BaseEditor editor)
{
if (column.ColumnName.Equals("OriginalQty"))
editor.Editable = Editable.Hidden;
if (column.ColumnName.Equals("NewQty"))
editor.Caption = "Qty";
if (column.ColumnName.Equals("Dimensions.Quantity"))
editor.Editable = Editable.Hidden;
if (column.ColumnName.Equals("Dimensions.Length"))
editor.Editable = Editable.Hidden;
if (column.ColumnName.Equals("Dimensions.Weight"))
editor.Editable = Editable.Hidden;
if (column.ColumnName.Equals("Dimensions.Height"))
editor.Editable = Editable.Hidden;
if (column.ColumnName.Equals("Dimensions.Width"))
editor.Editable = Editable.Hidden;
}
#endregion
#region Added Buttons
private bool ClearValues(Button arg1, CoreRow[] arg2)
{
var result = MessageBox.Show("This will clear all new quantities entered. Proceed?", "Warning", MessageBoxButton.YesNo);
if (result != MessageBoxResult.Yes)
return false;
foreach (var holding in Holdings.Items)
holding.ClearQty();
return true;
}
private bool FillValues(Button arg1, CoreRow[] rows)
{
var result = MessageBox.Show("This will prefill all new quantities with the original quantity. Proceed?", "Warning", MessageBoxButton.YesNo);
if (result != MessageBoxResult.Yes)
return false;
foreach (var holding in Holdings.Items)
holding.PreFillQty();
return true;
}
private void Cancel_Button_Click(object sender, RoutedEventArgs e)
{
bool changes = false;
foreach (var holding in StockTakeHoldings)
{
if (holding.NewQty != 0)
changes = true;
}
if (changes)
{
var result = MessageBox.Show("Warning - any unsaved changes will be lost. Continue?", "Warning", MessageBoxButton.YesNo);
if (result == MessageBoxResult.Yes)
Close();
}
else
Close();
}
private bool Export(Button arg1, CoreRow[] arg2)
{
var result = AddColumns();
result.LoadRows(Holdings.Items);
ExcelExporter.DoExport(result, "Stock Take " + DateTime.Now.ToString("HH:mm dd MMM yy"));
return true;
}
private CoreTable AddColumns()
{
var result = new CoreTable();
result.Columns.Add(
new CoreColumn()
{
ColumnName = "Location.Code",
DataType = typeof(string)
});
result.Columns.Add(
new CoreColumn()
{
ColumnName = "Product.Code",
DataType = typeof(string)
});
result.Columns.Add(
new CoreColumn()
{
ColumnName = "Product.Name",
DataType = typeof(string)
});
result.Columns.Add(
new CoreColumn()
{
ColumnName = "Dimensions.UnitSize",
DataType = typeof(string)
});
result.Columns.Add(
new CoreColumn()
{
ColumnName = "Job.JobNumber",
DataType = typeof(string)
});
result.Columns.Add(
new CoreColumn()
{
ColumnName = "Job.Name",
DataType = typeof(string)
});
result.Columns.Add(
new CoreColumn()
{
ColumnName = "Style.Description",
DataType = typeof(string)
});
result.Columns.Add(
new CoreColumn()
{
ColumnName = "OriginalQty",
DataType = typeof(double)
});
result.Columns.Add(
new CoreColumn()
{
ColumnName = "NewQty",
DataType = typeof(double)
});
return result;
}
#endregion
#endregion
#region Saving Stocktake
#region Button Press / Validate
private void Ok_Button_Click(object sender, RoutedEventArgs e)
{
var result = MessageBox.Show("This will now generate stock movements for each line, whether the quantity has changed or not. Proceed?", "Information", MessageBoxButton.YesNo);
if (result != MessageBoxResult.Yes)
return;
if (!ValidateQty())
return;
CreateMovements();
MessageBox.Show("Success - Stocktake complete");
Close();
}
private bool ValidateQty()
{
bool zerosPresent = false;
foreach (var holding in StockTakeHoldings)
{
if (holding.NewQty < 0)
{
MessageBox.Show("Negative quantities are not allowed!");
return false;
}
if (holding.NewQty < 0.001)
zerosPresent = true;
}
if (zerosPresent)
{
var result = MessageBox.Show("One or more rows have been left as Zero. This will create an issuing stock movement of any existing stock for that row. Proceed?", "Warning", MessageBoxButton.YesNo);
if (result != MessageBoxResult.Yes)
return false;
}
return true;
}
#endregion
#region Stock Movements
private void CreateMovements()
{
var batch = CreateBatch();
var movements = CompareHoldingsAndCreateMovements(batch.ID);
new Client().Save(movements, "Created on Desktop Stocktake");
}
private StockMovementBatch CreateBatch()
{
StockMovementBatch batch = new StockMovementBatch();
batch.Notes = "Stocktake";
batch.Employee.ID = EmployeeID;
batch.Type = StockMovementBatchType.Stocktake;
new Client().Save(batch, "Created on Desktop Stocktake");
return batch;
}
private List CompareHoldingsAndCreateMovements(Guid batchID)
{
List movements = new List();
foreach (var holding in StockTakeHoldings)
{
StockHolding original = OriginalHoldings.Find(x => x.ID == holding.ID);
if (holding.NewQty < 0.001 && holding.NewQty > 0)
holding.NewQty = 0;
if (original != null)
movements.Add(CreateMovement(holding, holding.NewQty - holding.OriginalQty, batchID));
else
movements.Add(CreateMovement(holding, holding.NewQty, batchID));
}
return movements;
}
private StockMovement CreateMovement(StockTakeHolding holding, double qty, Guid batchID)
{
var movement = CreateBaseMovement(holding, batchID);
return DetermineMovementType(movement, qty);
}
private StockMovement CreateBaseMovement(StockTakeHolding holding, Guid batchID)
{
var movement = new StockMovement();
movement.Batch.ID = batchID;
movement.IsTransfer = false;
movement.Issued = 0;
movement.Received = 0;
movement.Product.ID = holding.Product.ID;
movement.Job.ID = holding.Job.ID;
movement.Style.ID = holding.Style.ID;
movement.Employee.ID = EmployeeID;
movement.Notes = holding.Notes;
movement = CopyDimensions(movement, holding);
movement.Date = DateTime.Now;
movement.Location.ID = holding.Location.ID;
movement.IsRemnant = false;
movement.Type = StockMovementType.StockTake;
return movement;
}
private static StockMovement CopyDimensions(StockMovement movement, StockTakeHolding holding)
{
movement.Dimensions.Unit.ID = holding.Dimensions.Unit.ID;
movement.Dimensions.Unit.HasQuantity = holding.Dimensions.Unit.HasQuantity;
movement.Dimensions.Unit.HasLength = holding.Dimensions.Unit.HasLength;
movement.Dimensions.Unit.HasHeight = holding.Dimensions.Unit.HasHeight;
movement.Dimensions.Unit.HasWeight = holding.Dimensions.Unit.HasWeight;
movement.Dimensions.Unit.HasWidth = holding.Dimensions.Unit.HasWidth;
movement.Dimensions.Quantity = holding.Dimensions.Quantity;
movement.Dimensions.Length = holding.Dimensions.Length;
movement.Dimensions.Height = holding.Dimensions.Height;
movement.Dimensions.Weight = holding.Dimensions.Weight;
movement.Dimensions.Width = holding.Dimensions.Width;
movement.Dimensions.Unit.Format = holding.Dimensions.Unit.Format;
movement.Dimensions.Unit.Formula = holding.Dimensions.Unit.Formula;
return movement;
}
private static StockMovement DetermineMovementType(StockMovement movement, double qty)
{
if (qty < 0)
{
movement.Issued = 0 - qty;
}
if (qty > 0)
{
movement.Received = qty;
}
if (movement.Issued != 0 || movement.Received != 0)
movement.Notes = string.IsNullOrWhiteSpace(movement.Notes) ? "Updated Qty on Stocktake" : movement.Notes + ". Updated Qty on Stocktake";
else
movement.Notes = "Correct Qty during Stocktake";
return movement;
}
#endregion
#endregion
}
#region Intermediate Class
public class StockTakeHolding : BaseObject
{
[DoubleEditor(Editable = Editable.Disabled)]
public double OriginalQty { get; set; } = 0;
[DoubleEditor(Editable = Editable.Enabled)]
public double NewQty { get; set; } = 0.00000000000000000000001;
[NullEditor]
public Guid ID { get; set; } = Guid.Empty;
public ProductLink Product { get; set; }
public ProductStyleLink Style { get; set; }
public StockLocationLink Location { get; set; }
public JobLink Job { get; set; }
public StockDimensions Dimensions { get; set; }
[MemoEditor(Editable = Editable.Enabled)]
public string Notes { get; set; } = "Stocktake: ";
public void UpdateDimensions()
{
if (Product.ID != Guid.Empty)
{
CoreTable table = new Client().Query(new Filter(x => x.ID).IsEqualTo(Product.ID),
new Columns(
x => x.Dimensions.Unit.ID,
x => x.Dimensions.Unit.HasQuantity,
x => x.Dimensions.Unit.HasLength,
x => x.Dimensions.Unit.HasHeight,
x => x.Dimensions.Unit.HasWeight,
x => x.Dimensions.Unit.HasWidth,
x => x.Dimensions.Quantity,
x => x.Dimensions.Length,
x => x.Dimensions.Height,
x => x.Dimensions.Weight,
x => x.Dimensions.Width,
x => x.Dimensions.Unit.Format,
x => x.Dimensions.Unit.Formula,
x => x.Dimensions.UnitSize
));
Product product = table.Rows.FirstOrDefault().ToObject();
Dimensions.Unit.ID = product.Dimensions.Unit.ID;
Dimensions.Unit.HasQuantity = product.Dimensions.Unit.HasQuantity;
Dimensions.Unit.HasLength = product.Dimensions.Unit.HasLength;
Dimensions.Unit.HasHeight = product.Dimensions.Unit.HasHeight;
Dimensions.Unit.HasWeight = product.Dimensions.Unit.HasWeight;
Dimensions.Unit.HasWidth = product.Dimensions.Unit.HasWidth;
Dimensions.Quantity = product.Dimensions.Quantity;
Dimensions.Length = product.Dimensions.Length;
Dimensions.Height = product.Dimensions.Height;
Dimensions.Weight = product.Dimensions.Weight;
Dimensions.Width = product.Dimensions.Width;
Dimensions.Unit.Format = product.Dimensions.Unit.Format;
Dimensions.Unit.Formula = product.Dimensions.Unit.Formula;
Dimensions.UnitSize = product.Dimensions.UnitSize;
}
}
public void PreFillQty()
{
if (OriginalQty != 0)
NewQty = OriginalQty;
}
public void ClearQty()
{
NewQty = 0.00000000000000000000001;
}
}
#endregion
#region Grid
public class StockTakeHoldingGrid : DynamicItemsListGrid
{
protected override void Reload(Filters criteria, Columns columns, ref SortOrder? sort, Action action)
{
base.Reload(criteria, columns, ref sort, action);
}
protected override void DoAdd(bool OpenEditorOnDirectEdit = false)
{
base.DoAdd(true);
}
protected override DynamicGridColumns LoadColumns()
{
var columns = new DynamicGridColumns();
columns.Add(x => x.Location.Code, 150, "Location Code", "", Alignment.MiddleCenter);
columns.Add(x => x.Product.Code, 100, "Product Code", "", Alignment.MiddleCenter);
columns.Add(x => x.Product.Name, 0, "Product Name", "", Alignment.MiddleCenter);
columns.Add(x => x.Dimensions.UnitSize, 80, "Dimensions Unit", "", Alignment.MiddleCenter);
columns.Add(x => x.Job.JobNumber, 80, "Job Number", "", Alignment.MiddleCenter);
columns.Add(x => x.Job.Name, 80, "Job Name", "", Alignment.MiddleCenter);
columns.Add(x => x.Style.Description, 80, "Style", "", Alignment.MiddleCenter);
columns.Add(x => x.OriginalQty, 80, "Original Qty", "", Alignment.MiddleCenter);
columns.Add(x => x.NewQty, 80, "Actual Qty", "", Alignment.MiddleCenter);
columns.Add(x => x.Notes, 200, "Notes (Optional)", "", Alignment.MiddleCenter);
return columns;
}
}
#endregion
}