Browse Source

Added more sophisticated SplitLine functionality to POI grid

Kenric Nugteren 11 months ago
parent
commit
ad53585136

+ 1 - 0
prs.classes/Entities/Job/Requisitions/JobRequisitionItemLink.cs

@@ -36,6 +36,7 @@ namespace Comal.Classes
 
         [EditorSequence(5)]
         [EnumLookupEditor(typeof(JobRequisitionItemStatus))]
+        [Obsolete("", true)]
         public JobRequisitionItemStatus Status { get; set; } = JobRequisitionItemStatus.NotChecked;
 
         [NullEditor]

+ 35 - 26
prs.desktop/Panels/PurchaseOrders/SupplierPurchaseOrderItemOneToMany.cs

@@ -9,6 +9,8 @@ using InABox.Clients;
 using InABox.Core;
 using InABox.DynamicGrid;
 using InABox.WPF;
+using PRSDesktop.Panels.PurchaseOrders;
+using Syncfusion.Windows.Tools.Controls;
 
 namespace PRSDesktop;
 
@@ -164,27 +166,24 @@ public class SupplierPurchaseOrderItemOneToMany : DynamicOneToManyGrid<PurchaseO
         if (row is null)
             return;
 
-        var qty = row.Get<PurchaseOrderItem, double>(x => x.Qty);
-        var value = qty / 2;
-        if(DoubleEdit.Execute("Enter quantity to split on:", 0.0, qty, ref value))
-        {
-            var poi = LoadItem(row);
-
-            IList<PurchaseOrderItemAllocation> _allocations;
-            if(poi.ID == Guid.Empty)
-            {
-                // If not saved yet, any allocations will be in the transient list.
-                _allocations = Allocations.Where(x => x.Item1 == poi).Select(x => x.Item2).ToList();
-            }
-            else
-            {
-                // Otherwise, they'll all be in the database.
-                _allocations = Client.Query(
-                    new Filter<PurchaseOrderItemAllocation>(x => x.Item.ID).IsEqualTo(poi.ID),
-                    Columns.None<PurchaseOrderItemAllocation>().Add(x => x.Job.ID).Add(x => x.JobRequisitionItem.ID))
-                    .ToList<PurchaseOrderItemAllocation>();
-            }
+        var grid = new SupplierPurchaseOrderItemSplitGrid();
+        var poi = LoadItem(row);
 
+        var _allocations = Allocations.Where(x => x.Item1 == poi).Select(x => x.Item2).ToList();
+        if(poi.ID != Guid.Empty)
+        {
+            // Otherwise, they'll all be in the database.
+            _allocations.AddRange(Client.Query(
+                new Filter<PurchaseOrderItemAllocation>(x => x.Item.ID).IsEqualTo(poi.ID)
+                    .And(x => x.ID).NotInList(_allocations.ToArray(x => x.ID)),
+                Columns.None<PurchaseOrderItemAllocation>()
+                    .Add(x => x.ID)
+                    .Add(x => x.Job.ID).Add(x => x.JobRequisitionItem.ID).Add(x => x.Quantity).Add(x => x.Nominated))
+                .ToObjects<PurchaseOrderItemAllocation>());
+        }
+        var items = _allocations.ToList(SupplierPurchaseOrderItemSplit.FromAllocation);
+        if (SupplierPurchaseOrderItemSplitWindow.Execute(poi.Qty, items, out var value))
+        {
             var newLine = CreateItem();
             newLine.BillLine.ID = poi.BillLine.ID;
             newLine.BillLine.Synchronise(poi.BillLine);
@@ -222,16 +221,26 @@ public class SupplierPurchaseOrderItemOneToMany : DynamicOneToManyGrid<PurchaseO
             newLine.DueDate = poi.DueDate;
             newLine.SupplierCode = poi.SupplierCode;
 
+            newLine.Qty = poi.Qty - value;
             poi.Qty = value;
-            newLine.Qty = qty - value;
 
-            foreach(var allocation in _allocations)
+            foreach(var item in items)
             {
+                var allocation = _allocations.FirstOrDefault(x => x.ID == item.AllocationID);
+                if (allocation is null) continue;
+
                 // Add to a list to be saved later.
-                var jriPoi = new PurchaseOrderItemAllocation();
-                jriPoi.Job.CopyFrom(allocation.Job);
-                jriPoi.JobRequisitionItem.CopyFrom(allocation.JobRequisitionItem);
-                Allocations.Add(new(newLine, jriPoi));
+                var newAllocation = new PurchaseOrderItemAllocation();
+                newAllocation.Job.CopyFrom(allocation.Job);
+                newAllocation.JobRequisitionItem.CopyFrom(allocation.JobRequisitionItem);
+                newAllocation.Nominated = allocation.Nominated;
+
+                newAllocation.Quantity = allocation.Quantity - item.SplitQuantity;
+                allocation.Quantity = item.SplitQuantity;
+
+                // Save both allocations.
+                Allocations.Add(new(poi, allocation));
+                Allocations.Add(new(newLine, newAllocation));
             }
 
             SaveItem(poi);

+ 91 - 0
prs.desktop/Panels/PurchaseOrders/SupplierPurchaseOrderItemSplitGrid.cs

@@ -0,0 +1,91 @@
+using Comal.Classes;
+using InABox.Core;
+using InABox.Clients;
+using InABox.DynamicGrid;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace PRSDesktop.Panels.PurchaseOrders;
+
+public class SupplierPurchaseOrderItemSplit : BaseObject
+{
+    public JobLink Job { get; set; }
+
+    public JobRequisitionItemLink JobRequisitionItem { get; set; }
+
+    [DoubleEditor]
+    [Editable(Editable.DisabledOnDirectEdit)]
+    public double CurrentQuantity { get; set; }
+
+    [DoubleEditor]
+    public double SplitQuantity { get; set; }
+
+    public Guid AllocationID { get; set; }
+
+    public static SupplierPurchaseOrderItemSplit FromAllocation(PurchaseOrderItemAllocation allocation)
+    {
+        var split = new SupplierPurchaseOrderItemSplit
+        {
+            AllocationID = allocation.ID
+        };
+        split.Job.CopyFrom(allocation.Job);
+        split.JobRequisitionItem.CopyFrom(allocation.JobRequisitionItem);
+        split.CurrentQuantity = allocation.Quantity;
+        split.SplitQuantity = Math.Floor(allocation.Quantity / 2);
+        return split;
+    }
+}
+
+internal class SupplierPurchaseOrderItemSplitGrid : DynamicItemsListGrid<SupplierPurchaseOrderItemSplit>
+{
+    private readonly Column<SupplierPurchaseOrderItemSplit> SplitQuantityColumn = new Column<SupplierPurchaseOrderItemSplit>(x => x.SplitQuantity);
+
+    protected override void DoReconfigure(DynamicGridOptions options)
+    {
+        base.DoReconfigure(options);
+
+        options.DirectEdit = true;
+    }
+
+    public override DynamicGridColumns GenerateColumns()
+    {
+        return ExtractColumns(Columns.None<SupplierPurchaseOrderItemSplit>()
+            .Add(x => x.Job.JobNumber)
+            .Add(x => x.JobRequisitionItem.Requisition.Number)
+            .Add(x => x.JobRequisitionItem.Requisition.Description)
+            .Add(x => x.CurrentQuantity)
+            .Add(x => x.SplitQuantity));
+    }
+
+    protected override void OnAfterEditorValueChanged(DynamicEditorGrid? grid, SupplierPurchaseOrderItemSplit[] items, AfterEditorValueChangedArgs args, Dictionary<string, object?> changes)
+    {
+        if (changes.TryGetValue(SplitQuantityColumn.Property, out var value) && value is double d)
+        {
+            foreach(var item in items)
+            {
+                CoreUtils.MonitorChanges(item, () =>
+                {
+                    if (d < 0)
+                    {
+                        item.SplitQuantity = 0.0;
+                    }
+                    else if (d > item.CurrentQuantity)
+                    {
+                        item.SplitQuantity = item.CurrentQuantity;
+                    }
+                }, changes);
+            }
+        }
+        base.OnAfterEditorValueChanged(grid, items, args, changes);
+    }
+
+    protected override void Reload(Filters<SupplierPurchaseOrderItemSplit> criteria, Columns<SupplierPurchaseOrderItemSplit> columns, ref SortOrder<SupplierPurchaseOrderItemSplit>? sort, CancellationToken token, Action<CoreTable?, Exception?> action)
+    {
+        Items.LoadForeignProperties(columns);
+        base.Reload(criteria, columns, ref sort, token, action);
+    }
+}

+ 74 - 0
prs.desktop/Panels/PurchaseOrders/SupplierPurchaseOrderItemSplitWindow.xaml

@@ -0,0 +1,74 @@
+<Window x:Class="PRSDesktop.Panels.PurchaseOrders.SupplierPurchaseOrderItemSplitWindow"
+        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+        xmlns:local="clr-namespace:PRSDesktop.Panels.PurchaseOrders"
+        xmlns:syncfusion="http://schemas.syncfusion.com/wpf"
+        mc:Ignorable="d"
+        Title="SupplierPurchaseOrderItemSplitWindow" Height="400" Width="550"
+        x:Name="Window">
+    <Grid Margin="5" DataContext="{Binding ElementName=Window}">
+        <Grid.RowDefinitions>
+            <RowDefinition Height="40"/>
+            <RowDefinition Height="*"/>
+            <RowDefinition Height="Auto"/>
+        </Grid.RowDefinitions>
+        <Grid.ColumnDefinitions>
+            <ColumnDefinition Width="Auto"/>
+            <ColumnDefinition Width="60"/>
+            <ColumnDefinition Width="*"/>
+            <ColumnDefinition Width="Auto"/>
+            <ColumnDefinition Width="60"/>
+        </Grid.ColumnDefinitions>
+        <Label Grid.Column="0" Grid.Row="0"
+               Content="Current Quantity:"
+               VerticalAlignment="Stretch" VerticalContentAlignment="Center"/>
+        <syncfusion:DoubleTextBox Grid.Column="1"
+                                  Value="{Binding OldQuantity}" 
+                                  HorizontalContentAlignment="Center"
+                                  VerticalAlignment="Stretch"
+                                  VerticalContentAlignment="Center"
+                                  Margin="0,5,5,5"
+                                  Background="WhiteSmoke"
+                                  IsEnabled="False"/>
+        <Label Grid.Column="3" Grid.Row="0"
+               Content="Split Quantity:"
+               VerticalAlignment="Stretch" VerticalContentAlignment="Center"/>
+        <syncfusion:DoubleTextBox Grid.Column="4"
+                                  Value="{Binding NewQuantity}" 
+                                  MinValue="0"
+                                  MaxValue="{Binding OldQuantity}"
+                                  HorizontalContentAlignment="Center"
+                                  VerticalAlignment="Stretch"
+                                  VerticalContentAlignment="Center"
+                                  Margin="0,5,5,5">
+            <syncfusion:DoubleTextBox.Style>
+                <Style TargetType="syncfusion:DoubleTextBox">
+                    <Setter Property="Background" Value="LightYellow"/>
+                    <Style.Triggers>
+                        <DataTrigger Binding="{Binding InvalidSplit}" Value="True">
+                            <Setter Property="Background" Value="Salmon"/>
+                        </DataTrigger>
+                    </Style.Triggers>
+                </Style>
+            </syncfusion:DoubleTextBox.Style>
+        </syncfusion:DoubleTextBox>
+
+        <local:SupplierPurchaseOrderItemSplitGrid Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="5"
+                                                  x:Name="Grid"
+                                                  OnChanged="Grid_OnChanged"/>
+
+        <DockPanel x:Name="Buttons" Grid.Row="2" LastChildFill="False" Grid.ColumnSpan="5">
+            <Button x:Name="CancelButton" Click="CancelButton_Click"
+                    Content="Cancel"
+                    Margin="5,5,0,0" Padding="5" MinWidth="60"
+                    DockPanel.Dock="Right"/>
+            <Button x:Name="OKButton" Click="OKButton_Click"
+                    Content="OK"
+                    Margin="5,5,0,0" Padding="5" MinWidth="60"
+                    DockPanel.Dock="Right"
+                    IsEnabled="{Binding ElementName=Window,Path=CanSave}"/>
+        </DockPanel>
+    </Grid>
+</Window>

+ 98 - 0
prs.desktop/Panels/PurchaseOrders/SupplierPurchaseOrderItemSplitWindow.xaml.cs

@@ -0,0 +1,98 @@
+using jdk.nashorn.@internal.ir;
+using System;
+using System.Collections.Generic;
+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.PurchaseOrders;
+
+/// <summary>
+/// Interaction logic for SupplierPurchaseOrderItemSplitWindow.xaml
+/// </summary>
+public partial class SupplierPurchaseOrderItemSplitWindow : Window, INotifyPropertyChanged
+{
+    public bool CanSave
+    {
+        get
+        {
+            return true;
+        }
+    }
+
+    private double _oldQuantity;
+    public double OldQuantity
+    {
+        get => _oldQuantity;
+        set
+        {
+            _oldQuantity = value;
+            OnPropertyChanged();
+        }
+    }
+
+    private double _newQuantity;
+    public double NewQuantity
+    {
+        get => _newQuantity;
+        set
+        {
+            _newQuantity = value;
+            OnPropertyChanged();
+            OnPropertyChanged(nameof(InvalidSplit));
+        }
+    }
+
+    public double TotalSplits => Grid?.Items.Sum(x => x.SplitQuantity) ?? 0.0;
+
+    public bool InvalidSplit => TotalSplits > NewQuantity;
+
+    public SupplierPurchaseOrderItemSplitWindow()
+    {
+        InitializeComponent();
+    }
+
+    private void OKButton_Click(object sender, RoutedEventArgs e)
+    {
+        DialogResult = true;
+    }
+
+    private void CancelButton_Click(object sender, RoutedEventArgs e)
+    {
+        DialogResult = false;
+    }
+
+    public static bool Execute(double quantity, List<SupplierPurchaseOrderItemSplit> items, out double splitQuantity)
+    {
+        var window = new SupplierPurchaseOrderItemSplitWindow();
+        window.OldQuantity = quantity;
+        window.Grid.Items = items;
+        window.Grid.Refresh(true, true);
+        if(window.ShowDialog() == true)
+        {
+            splitQuantity = window.NewQuantity;
+            return true;
+        }
+        else
+        {
+            splitQuantity = 0.0;
+            return false;
+        }
+    }
+
+    private void Grid_OnChanged(object sender, EventArgs e)
+    {
+        OnPropertyChanged(nameof(TotalSplits));
+        OnPropertyChanged(nameof(InvalidSplit));
+    }
+}