using Comal.Classes; using InABox.Clients; using InABox.Core; using InABox.DynamicGrid; using Microsoft.CodeAnalysis.VisualBasic.Syntax; using Org.BouncyCastle.Asn1.Mozilla; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; namespace PRSDesktop.Panels.Products.Locations; /// /// Interaction logic for StockHoldingRelocationWindow.xaml /// public partial class StockHoldingRelocationWindow : Window, INotifyPropertyChanged { public StockHolding From { get; private init; } public enum RelocationMode { Issue, Transfer, ReleaseAllocations } private RelocationMode _mode; public RelocationMode Mode { get => _mode; set { _mode = value; UpdateMode(); } } public bool ShowHeader => ShowFrom || IsTargetEditable || IsJobEditable; private bool _showFrom = true; public bool ShowFrom { get => _showFrom; set { _showFrom = value; OnPropertyChanged(); OnPropertyChanged(nameof(ShowHeader)); } } private bool _isTargetEditable = true; public bool IsTargetEditable { get => _isTargetEditable; set { _isTargetEditable = value; OnPropertyChanged(); OnPropertyChanged(nameof(ShowHeader)); } } private bool _isJobEditable = false; public bool IsJobEditable { get => _isJobEditable; set { _isJobEditable = value; OnPropertyChanged(); OnPropertyChanged(nameof(ShowHeader)); } } private StockLocation? _originalTarget; public StockLocation? OriginalTarget { get => _originalTarget; set { _originalTarget = value; To = value; } } private Job? _originalJob; public Job? OriginalJob { get => _originalJob; set { _originalJob = value; Job = value; } } private Job? _job; public Job? Job { get => _job; set { _job = value; OnPropertyChanged(); OnPropertyChanged(nameof(CanSave)); OnPropertyChanged($"{nameof(Job)}.{nameof(Job.JobNumber)}"); OnPropertyChanged($"{nameof(Job)}.{nameof(Job.Name)}"); UpdateMode(); } } private StockLocation? _to; public StockLocation? To { get => _to; set { _to = value; OnPropertyChanged(); OnPropertyChanged(nameof(CanSave)); OnPropertyChanged($"{nameof(To)}.{nameof(To.Code)}"); OnPropertyChanged($"{nameof(To)}.{nameof(To.Description)}"); } } private bool _requireChangeTarget = false; /// /// Require that the target location be changed before allowing the "Save" button to be clicked. /// public bool RequireChangeTarget { get => _requireChangeTarget; set { _requireChangeTarget = value; OnPropertyChanged(); OnPropertyChanged(nameof(CanSave)); } } public bool CanSave { get { if(Grid.TotalIssued <= 0) { return false; } if(IsTargetEditable && (To?.ID ?? Guid.Empty) == Guid.Empty) { return false; } if (RequireChangeTarget) { if(IsTargetEditable && IsJobEditable) { return (To?.ID ?? Guid.Empty) != (OriginalTarget?.ID ?? Guid.Empty) || (Job?.ID ?? Guid.Empty) != (OriginalJob?.ID ?? Guid.Empty); } else if (IsTargetEditable) { return (To?.ID ?? Guid.Empty) != (OriginalTarget?.ID ?? Guid.Empty); } else if (IsJobEditable) { return (Job?.ID ?? Guid.Empty) != (OriginalJob?.ID ?? Guid.Empty); } } return true; } } public StockHoldingRelocationWindow(StockHolding from, IEnumerable items, RelocationMode mode) { Client.EnsureColumns(from, Columns.None().Add(x => x.Location.Code)); From = from; InitializeComponent(); SetRequisitionItems(items); Mode = mode; } private void UpdateMode() { if(Mode == RelocationMode.Issue) { var jobID = Job?.ID ?? Guid.Empty; foreach(var item in Grid.Items) { if(item.JRI.ID != Guid.Empty && item.JRI.Job.ID != jobID) { item.Editable = false; item.Issued = 0; } else { item.Editable = true; } } Title = "Issue Items"; IsJobEditable = true; IsTargetEditable = false; ShowFrom = true; } else if(Mode == RelocationMode.Transfer) { foreach(var item in Grid.Items) { item.Editable = item.JRI.ID == Guid.Empty || Security.IsAllowed(); } IsJobEditable = true; IsTargetEditable = true; ShowFrom = false; RequireChangeTarget = true; Title = "Transfer Stock"; } else if(Mode == RelocationMode.ReleaseAllocations) { ShowFrom = false; IsTargetEditable = false; IsJobEditable = false; Title = "Release Stock"; } } private void Grid_PropertyChanged(object sender, PropertyChangedEventArgs e) { if(e.PropertyName == nameof(Grid.TotalIssued)) { OnPropertyChanged(nameof(CanSave)); } } public void SetRequisitionItems(IEnumerable items) { var rItems = items.AsIList(); var rIDs = rItems.Select(x => x.ID).Where(x => x != Guid.Empty).ToArray(); var quantities = Client.Query( StockHolding.GetFilter(From) .Combine(new Filter(x => x.JobRequisitionItem.ID).InList(rIDs)), Columns.None().Add(x => x.Units).Add(x => x.JobRequisitionItem.ID)) .ToObjects().GroupBy(x => x.JobRequisitionItem.ID).ToDictionary(x => x.Key, x => x.Sum(x => x.Units)); var requidItems = new List(); var newItems = new List(); foreach(var item in rItems) { var qty = item.ID != Guid.Empty ? quantities.GetValueOrDefault(item.ID) : item.Qty; if(item.ID != Guid.Empty && qty <= 0) { // We can't do anything with this; it shouldn't be shown. continue; } var newItem = new StockHoldingRelocationItem { Issued = 0, Quantity = qty, MaxValue = item.ID == Guid.Empty ? double.MaxValue : qty, Text = item.ID == Guid.Empty ? "Unrequisitioned Items" : $"{item.Job.JobNumber}: #{item.Requisition.Number} ({item.Requisition.Description})", JRI = item }; if(item.ID == Guid.Empty) { newItems.Add(newItem); } else { requidItems.Add(newItem); } } int i = 1; foreach(var item in requidItems) { item.ItemNumber = $"{i}. "; newItems.Add(item); ++i; } Grid.Items = newItems; Grid.Refresh(true, true); } public Dictionary GetQuantities() { return Grid.Items.ToDictionary(x => x.JRI.ID, x => x.Issued); } public StockLocation GetTargetLocation() { return To ?? new StockLocation(); } public event PropertyChangedEventHandler? PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } #region Target Location public static StockLocation? LookupLocation(string? column = null, string? value = null) { var grid = new MultiSelectDialog( LookupFactory.DefineFilter(), Columns.None().Add(x => x.ID).Add(x => x.Code).Add(x => x.Description), multiselect: false); if (grid.ShowDialog(column, value)) { return grid.Data().Rows.First().ToObject(); } else { return null; } } private bool DoLookupLocation(string? column, string? value) { if(LookupLocation(column, value) is StockLocation location) { To = location; return true; } else { return false; } } private void ToBox_LostFocus(object sender, RoutedEventArgs e) { DoSearchLocation(); } private void ToButton_Click(object sender, RoutedEventArgs e) { DoLookupLocation(null, null); } private void DoSearchLocation() { if (ToBox.Text.IsNullOrWhiteSpace() || ToBox.Text == To?.Code) { To = null; return; } var location = Client.Query( new Filter(x => x.Code).IsEqualTo(ToBox.Text), Columns.None().Add(x => x.ID).Add(x => x.Code).Add(x => x.Description)) .ToObjects().FirstOrDefault(); if(location is not null) { To = location; } else { if(!DoLookupLocation(nameof(To.Code), ToBox.Text)) { To = null; } } } private void ToBox_OnKeyUp(object sender, KeyEventArgs e) { if (e.Key == Key.Enter) DoSearchLocation(); } #endregion #region Job private bool DoLookupJob(string? column, string? value) { var grid = new MultiSelectDialog( LookupFactory.DefineFilter(), Columns.None().Add(x => x.ID).Add(x => x.JobNumber).Add(x => x.Name), multiselect: false); if (grid.ShowDialog(column, value)) { Job = grid.Data().Rows.First().ToObject(); return true; } else { return false; } } private void JobBox_LostFocus(object sender, RoutedEventArgs e) { DoSearchJob(); } private void JobButton_Click(object sender, RoutedEventArgs e) { DoLookupJob(null, null); } private void DoSearchJob() { if (JobBox.Text.IsNullOrWhiteSpace() || JobBox.Text == To?.Code) { Job = null; return; } var job = Client.Query( new Filter(x => x.JobNumber).IsEqualTo(JobBox.Text), Columns.None().Add(x => x.ID).Add(x => x.JobNumber).Add(x => x.Name)) .ToObjects().FirstOrDefault(); if(job is not null) { Job = job; } else { if(!DoLookupJob(nameof(Job.JobNumber), JobBox.Text)) { Job = null; } } } private void JobBox_OnKeyUp(object sender, KeyEventArgs e) { if (e.Key == Key.Enter) DoSearchJob(); } #endregion private void OKButton_Click(object sender, RoutedEventArgs e) { DialogResult = true; Close(); } private void CancelButton_Click(object sender, RoutedEventArgs e) { DialogResult = false; Close(); } }