Переглянути джерело

Added Parcels to Consignments

frankvandenbos 2 місяців тому
батько
коміт
9e6a9fbf21

+ 19 - 0
prs.classes/Entities/Consignment/ConsignmentParcel.cs

@@ -0,0 +1,19 @@
+using System;
+using System.Linq.Expressions;
+using InABox.Core;
+
+namespace Comal.Classes
+{
+    
+    [UserTracking(typeof(Delivery))]
+    public class ConsignmentParcel :Entity, IRemotable, IPersistent, ILicense<LogisticsLicense>
+    {
+        [NullEditor]
+        public ConsignmentLink Consignment { get; set; }
+        
+        [TextBoxEditor]
+        [EditorSequence(1)]
+        public string Description { get; set; }
+        
+    }
+}

+ 17 - 0
prs.classes/Entities/Consignment/ConsignmentParcelLink.cs

@@ -0,0 +1,17 @@
+using System;
+using InABox.Core;
+
+namespace Comal.Classes
+{
+    public class ConsignmentParcelLink : EntityLink<ConsignmentParcel>
+    {
+        [LookupEditor(typeof(ConsignmentParcel))]
+        public override Guid ID { get; set; }
+        
+        [TextBoxEditor(Visible=Visible.Optional, Editable = Editable.Hidden)]
+        [EditorSequence(1)]
+        public string Description { get; set; }
+        
+        
+    }
+}

+ 20 - 4
prs.classes/Entities/PurchaseOrder/PurchaseOrderItem.cs

@@ -151,18 +151,34 @@ namespace Comal.Classes
         [EntityRelationship(DeleteAction.SetNull)]
         public ConsignmentLink Consignment { get; set; }
         
-        [EditorSequence("Additional",7)]   
+        private class ConsignmentParcelLookup : LookupDefinitionGenerator<ConsignmentParcel, PurchaseOrderItem>
+        {
+            public override Filter<ConsignmentParcel> DefineFilter(PurchaseOrderItem[] items)
+            {
+                if (items.Select(x => x.Consignment.ID).Distinct().Count() != 1)
+                    return new Filter<ConsignmentParcel>().None();
+                return new Filter<ConsignmentParcel>(x => x.Consignment.ID).IsEqualTo(items.FirstOrDefault()?.Consignment.ID ?? CoreUtils.FullGuid);
+            }
+
+            public override Columns<PurchaseOrderItem> DefineFilterColumns()
+                => Columns.None<PurchaseOrderItem>().Add(x=>x.Consignment.ID);
+        }
+        [LookupDefinition(typeof(ConsignmentParcelLookup))]
+        [EditorSequence("Additional",7)]
+        [EntityRelationship(DeleteAction.SetNull)]
+        public ConsignmentParcelLink ConsignmentParcel { get; set; }
+        
+        [EditorSequence("Additional",8)]   
         public StockLocationLink StockLocation { get; set; }
         
         [DateTimeEditor(Visible = Visible.Default)]
-        [EditorSequence("Additional",8)]
+        [EditorSequence("Additional",9)]
         public DateTime ReceivedDate { get; set; }
 
         [TextBoxEditor(Visible = Visible.Optional)]
-        [EditorSequence("Additional",9)]
+        [EditorSequence("Additional",10)]
         public string ReceivedReference { get; set; }
         
-        
         [CurrencyEditor(Visible = Visible.Optional, Editable = Editable.Hidden, Summary = Summary.Sum)]
         public double Balance { get; set; }
 

+ 157 - 7
prs.desktop/Panels/Consignments/ConsignmentItemGrid.cs

@@ -1,33 +1,179 @@
 using System;
+using System.Collections.Generic;
 using System.Linq;
 using System.Threading;
 using System.Windows;
 using System.Windows.Controls;
+using System.Windows.Media.Imaging;
 using Comal.Classes;
 using InABox.Clients;
 using InABox.Core;
 using InABox.DynamicGrid;
 using InABox.Wpf;
 using InABox.WPF;
+using Microsoft.Xaml.Behaviors.Core;
+using PRSDesktop.Panels.PurchaseOrders;
+using PurchaseOrder = MYOB.AccountRight.SDK.Contracts.Version2.Purchase.PurchaseOrder;
+using PurchaseOrderItemAllocation = Comal.Classes.PurchaseOrderItemAllocation;
 
 namespace PRSDesktop;
 
 public class ConsignmentItemGrid : DynamicDataGrid<PurchaseOrderItem>
 {
     private Button receiveall;
-
     private Button receiveselected;
-
+    private Button allocateparcels;
+    public Guid ConsignmentID { get; set; } = CoreUtils.FullGuid;
+    public Guid ParcelID { get; set; } = Guid.Empty;
+    public bool Completed { get; set; }
+    
+    public Func<CoreTable>? GetParcels { get; set; }
+    
     protected override void Init()
     {
         base.Init();
+        HiddenColumns.Add(x=>x.ConsignmentParcel.ID);
+        HiddenColumns.Add(x=>x.ConsignmentParcel.Description);
 
+        allocateparcels = AddButton("Parcels", null, ShowParcelMenu);
+        allocateparcels.IsEnabled = false;
+        
         receiveall = AddButton("Receive All", null, ReceiveAll);
         receiveall.IsEnabled = false;
+        
         receiveselected = AddButton("Receive Selected", null, ReceiveSelected);
         receiveselected.IsEnabled = false;
-
+        
         AddButton("Assign Location", null, SupplierPurchaseOrderItemOneToMany.AssignLocation);
+
+        ActionColumns.Add(
+            new DynamicMenuColumn(
+                DoBuildMenu, 
+                row => row.Get<PurchaseOrderItem,DateTime>(x=>x.ReceivedDate).IsEmpty() 
+                    ? DynamicMenuStatus.Enabled 
+                    : DynamicMenuStatus.Disabled
+            )
+        );
+    }
+
+    private void DoBuildMenu(DynamicMenuColumn column, CoreRow? row)
+    {
+        if (row is null)
+            return;
+        column.AddItem("Split Line", PRSDesktop.Resources.split, SplitLine);
+        var parcels = GetParcels?.Invoke().ToArray<ConsignmentParcel>() ?? [];
+        if (parcels.Any())
+        {
+            column.AddSeparator();
+            foreach (var parcel in parcels.Where(x=>x.ID != Guid.Empty && x.ID != CoreUtils.FullGuid))
+                column.AddItem(parcel.Description, null, r => AllocateParcels([r], parcel));
+            if (row.Get<PurchaseOrderItem, Guid>(x => x.ConsignmentParcel.ID) != Guid.Empty)
+            {
+                column.AddSeparator();
+                column.AddItem("Clear Parcel", null, r => AllocateParcels([r], new ConsignmentParcel()));
+            }
+        }
+    }
+    
+    private void SplitLine(CoreRow? row)
+    {
+        if (row is null || !row.Get<PurchaseOrderItem, DateTime>(x => x.ReceivedDate).IsEmpty())
+            return;
+
+        Guid itemid = row.Get<PurchaseOrderItem, Guid>(x => x.ID);
+        var query = new MultiQuery();
+        query.Add(
+            new Filter<PurchaseOrderItem>(x => x.ID).IsEqualTo(itemid),
+            Columns.Local<PurchaseOrderItem>()
+        );
+        query.Add(
+            new Filter<PurchaseOrderItemAllocation>(x => x.Item.ID),
+            Columns.Local<PurchaseOrderItemAllocation>()
+        );
+        query.Query();
+        var item = query.Get<PurchaseOrderItem>().ToObjects<PurchaseOrderItem>().FirstOrDefault() ?? new PurchaseOrderItem();
+        var allocations = query.Get<PurchaseOrderItemAllocation>().ToList<PurchaseOrderItemAllocation>();
+        var splits = allocations.ToList(SupplierPurchaseOrderItemSplit.FromAllocation);
+        if (SupplierPurchaseOrderItemSplitWindow.Execute(item.Qty, splits, out var value))
+        {
+            var newItem = item.Clone();
+            newItem.ID = Guid.Empty;
+            newItem.CommitChanges();
+            newItem.Qty = item.Qty - value;
+            item.Qty = value;
+
+            foreach(var split in splits)
+            {
+                var allocation = allocations.FirstOrDefault(x => x.ID == split.AllocationID);
+                if (allocation is null) 
+                    continue;
+                
+                var newAllocation = new PurchaseOrderItemAllocation();
+                newAllocation.Job.CopyFrom(allocation.Job);
+                newAllocation.JobRequisitionItem.CopyFrom(allocation.JobRequisitionItem);
+                newAllocation.Quantity = allocation.Quantity - split.SplitQuantity;
+                allocations.Add(newAllocation);
+                
+                allocation.Quantity = split.SplitQuantity;
+
+            }
+
+            Progress.ShowModal("Saving items", progress =>
+            {
+                Client.Save([item,newItem],"Split Consignment Line");
+                Client.Save(allocations,"Split Consignment Line");
+            });
+            
+            Refresh(false,true);
+        }
+    }
+    
+    private bool ShowParcelMenu(Button button, CoreRow[] rows)
+    {
+        var parcels = GetParcels?.Invoke()?.ToArray<ConsignmentParcel>() ?? [];
+        if (!parcels.Any())
+            return false;
+        ContextMenu menu = new ContextMenu();
+        if (rows.Any(r => r.Get<PurchaseOrderItem,Guid>(c => c.ConsignmentParcel.ID) != Guid.Empty))
+        {
+            menu.Items.Add(
+                new MenuItem()
+                {
+                    Header = "Clear Allocation",
+                    Command = new ActionCommand(() => AllocateParcels(rows, new ConsignmentParcel()))
+                });
+            menu.Items.Add(new Separator());
+        }
+        
+        foreach (var parcel in parcels.Where(p => p.ID != Guid.Empty && p.ID != CoreUtils.FullGuid))
+            menu.Items.Add(
+                new MenuItem()  
+                {
+                    Header = parcel.Description,
+                    Command = new ActionCommand(() => AllocateParcels(rows, parcel))
+                }
+            );
+        
+        menu.IsOpen = true;
+        return false;
+    }
+
+    
+    private void AllocateParcels(CoreRow[] rows, ConsignmentParcel parcel)
+    {
+        List<PurchaseOrderItem> updates = new();
+        foreach (var row in rows)
+        {
+            var update = row.ToObject<PurchaseOrderItem>();
+            update.ConsignmentParcel.CopyFrom(parcel);
+            updates.Add(update);
+            
+            row.Set<PurchaseOrderItem,Guid>(x=>x.ConsignmentParcel.ID, parcel.ID);
+            row.Set<PurchaseOrderItem,string>(x=>x.ConsignmentParcel.Description, parcel.Description);
+            InvalidateRow(row);
+            
+        }
+        Client.Save(updates,"Updated from Consignment Screen", (o,e) => { });
     }
 
     protected override void DoReconfigure(DynamicGridOptions options)
@@ -38,13 +184,14 @@ public class ConsignmentItemGrid : DynamicDataGrid<PurchaseOrderItem>
         options.AddRows = true;
         options.DeleteRows = true;
         options.MultiSelect = true;
+        options.FilterRows = true;
+        options.HideDatabaseFilters = true;
     }
-
-    public Guid ConsignmentID { get; set; }
-    public bool Completed { get; set; }
-
+    
     protected override void SelectItems(CoreRow[]? rows)
     {
+        var parcels = GetParcels?.Invoke()?.ToArray<ConsignmentParcel>() ?? [];
+        allocateparcels.IsEnabled = parcels.Any() && rows != null && rows.Any(r => r.Get<PurchaseOrderItem, DateTime>(c => c.ReceivedDate).IsEmpty());
         receiveselected.IsEnabled = rows != null && rows.Any(r => r.Get<PurchaseOrderItem, DateTime>(c => c.ReceivedDate).IsEmpty());
         receiveall.IsEnabled = Data.Rows.Any(r => r.Get<PurchaseOrderItem, DateTime>(c => c.ReceivedDate).IsEmpty());
         base.SelectItems(rows);
@@ -97,6 +244,8 @@ public class ConsignmentItemGrid : DynamicDataGrid<PurchaseOrderItem>
     {
         criteria.Add(new Filter<PurchaseOrderItem>(x => x.Consignment.ID).IsEqualTo(
             ConsignmentID == Guid.Empty ? CoreUtils.FullGuid : ConsignmentID));
+        if (ParcelID != CoreUtils.FullGuid)
+            criteria.Add(new Filter<PurchaseOrderItem>(x=>x.ConsignmentParcel.ID).IsEqualTo(ParcelID));
         base.Reload(criteria, columns, ref sort, token, action);
     }
 
@@ -180,4 +329,5 @@ public class ConsignmentItemGrid : DynamicDataGrid<PurchaseOrderItem>
             new Client<PurchaseOrderItem>().Save(items, "Removed from Consignment");
         }
     }
+    
 }

+ 81 - 0
prs.desktop/Panels/Consignments/ConsignmentParcelGrid.cs

@@ -0,0 +1,81 @@
+using System;
+using System.Linq;
+using System.Threading;
+using Comal.Classes;
+using InABox.Core;
+using InABox.DynamicGrid;
+using InABox.Wpf;
+
+namespace PRSDesktop;
+
+
+public class ConsignmentParcelGrid : DynamicDataGrid<ConsignmentParcel>
+{
+    public Guid ConsignmentID { get; set; }
+    public bool Completed { get; set; }
+    
+    protected override DynamicGridColumns LoadColumns()
+    {
+        var columns = new DynamicGridColumns();
+        columns.Add<ConsignmentParcel>(x => x.Description, 0, "Parcels", "", Alignment.MiddleLeft);
+        return columns;
+    }
+    
+    protected override void Reload(
+    	Filters<ConsignmentParcel> criteria, Columns<ConsignmentParcel> columns, ref SortOrder<ConsignmentParcel>? sort,
+    	CancellationToken token, Action<CoreTable?, Exception?> action)
+    {
+        criteria.Add(new Filter<ConsignmentParcel>(x => x.Consignment.ID).IsEqualTo(ConsignmentID));
+        base.Reload(criteria, columns, ref sort, token, (table,exception) =>
+        {
+            if ((ConsignmentID != CoreUtils.FullGuid) && (table != null))
+            {
+                var row = table.NewRow();
+                row.Set<ConsignmentParcel, Guid>(x => x.ID, CoreUtils.FullGuid);
+                row.Set<ConsignmentParcel, string>(x => x.Description, "(All Items)");
+                table.Rows.Insert(0, row);
+                
+                row = table.NewRow();
+                row.Set<ConsignmentParcel, Guid>(x => x.ID, Guid.Empty);
+                row.Set<ConsignmentParcel, string>(x => x.Description, "(Unallocated Items)");
+                table.Rows.Add(row);
+            }
+            action(table,exception);
+        });
+    }
+
+    protected override bool CanCreateItems()
+    {
+        if (ConsignmentID == Guid.Empty)
+        {
+            MessageWindow.ShowMessage("Please select a Consignment first!", "Error");
+            return false;
+        }
+        
+        if (Completed)
+        {
+            MessageWindow.ShowMessage("Cannot Modify a Completed Requisition!", "Error");
+            return false;
+        }
+        
+        return base.CanCreateItems();
+        
+    }
+
+    public override ConsignmentParcel CreateItem()
+    {
+        var item = base.CreateItem();
+        item.Consignment.ID = ConsignmentID;
+        return item;
+    }
+
+    protected override void DoEdit()
+    {
+        Guid id = SelectedRows.FirstOrDefault()?.Get<ConsignmentParcel, Guid>(x => x.ID) ?? Guid.Empty;
+        if ((id == CoreUtils.FullGuid) || (id == Guid.Empty))
+            MessageWindow.ShowMessage("You cannot edit this item!", "Error");
+        else
+            base.DoEdit();
+    }
+
+}

+ 19 - 2
prs.desktop/Panels/Consignments/ConsignmentsPanel.xaml

@@ -38,6 +38,7 @@
 
         <dynamicgrid:DynamicSplitPanel.Detail>
             <DockPanel>
+                
                 <Border DockPanel.Dock="Top" BorderBrush="Gray" BorderThickness="1" Background="WhiteSmoke" VerticalAlignment="Top">
                     <Grid Margin="4,4,0,0" >
                         <Grid.ColumnDefinitions>
@@ -97,8 +98,24 @@
 
                     </Grid>
                 </Border>
-
-                <local:ConsignmentItemGrid x:Name="ConsignmentItems" DockPanel.Dock="Top" Margin="0,2,0,0" />
+                
+                <dynamicgrid:DynamicSplitPanel 
+                    DockPanel.Dock="Top"
+                    AllowableViews="Combined" 
+                    View="Combined" 
+                    Anchor="Master" 
+                    AnchorWidth="200"
+                    Margin="0,2,0,0">
+                    <dynamicgrid:DynamicSplitPanel.Master>
+                        <local:ConsignmentParcelGrid 
+                            x:Name="ConsignmentParcels" 
+                            OnSelectItem="ConsignmentParcels_OnOnSelectItem"/>
+                    </dynamicgrid:DynamicSplitPanel.Master>
+                    <dynamicgrid:DynamicSplitPanel.Detail>
+                        <local:ConsignmentItemGrid x:Name="ConsignmentItems" />
+                    </dynamicgrid:DynamicSplitPanel.Detail>
+                </dynamicgrid:DynamicSplitPanel>
+                
             </DockPanel>
         </dynamicgrid:DynamicSplitPanel.Detail>
 

+ 25 - 2
prs.desktop/Panels/Consignments/ConsignmentsPanel.xaml.cs

@@ -30,6 +30,9 @@ namespace PRSDesktop
         public ConsignmentsPanel()
         {
             InitializeComponent();
+            
+            ConsignmentItems.GetParcels = () => ConsignmentParcels.Data;
+            
             Consignments.HiddenColumns.Add(x => x.Supplier.Code);
             Consignments.HiddenColumns.Add(x => x.Supplier.Name);
             Consignments.HiddenColumns.Add(x => x.Number);
@@ -48,9 +51,20 @@ namespace PRSDesktop
             Consignments.OnSelectItem += (o, e) =>
             {
                 var row = e.Rows?.FirstOrDefault();
-                ConsignmentItems.ConsignmentID = row != null ? row.Get<Consignment, Guid>(x => x.ID) : CoreUtils.FullGuid;
-                ConsignmentItems.Completed = row == null || !row.Get<Consignment, DateTime>(x => x.Closed).IsEmpty();
+                
+                Guid consignmentid = row != null ? row.Get<Consignment, Guid>(x => x.ID) : CoreUtils.FullGuid;
+                bool completed = row == null || !row.Get<Consignment, DateTime>(x => x.Closed).IsEmpty();
+                
+                
                 LoadConsigmment(row);
+                ConsignmentParcels.ConsignmentID = consignmentid;
+                ConsignmentParcels.Completed = completed;
+                ConsignmentParcels.Refresh(false, true);
+
+                ConsignmentItems.ConsignmentID = consignmentid;
+                ConsignmentItems.ParcelID = CoreUtils.FullGuid;
+                ConsignmentItems.Completed = completed;
+                
                 ConsignmentItems.Refresh(false, true);
             };
 
@@ -140,6 +154,7 @@ namespace PRSDesktop
             Consignments.ShowAll = settings.ShowAll;
             Consignments.ColumnsTag = settings.ViewType == ScreenViewType.Register ? settings.ViewType.ToString() : "";
             Consignments.Refresh(true, false);
+            ConsignmentParcels.Refresh(true, false);
             ConsignmentItems.Refresh(true, false);
         }
 
@@ -282,5 +297,13 @@ namespace PRSDesktop
 
             new UserConfiguration<ConsignmentScreenSettings>().Save(settings);
         }
+
+        private void ConsignmentParcels_OnOnSelectItem(object sender, DynamicGridSelectionEventArgs e)
+        {
+            ConsignmentItems.ParcelID = 
+                ConsignmentParcels.SelectedRows.FirstOrDefault()?.Get<ConsignmentParcel, Guid>(x => x.ID) ??
+                CoreUtils.FullGuid;
+            ConsignmentItems.Refresh(false,true);
+        }
     }
 }

+ 1 - 0
prs.desktop/Panels/Manufacturing/ManufacturingKanban.cs

@@ -25,6 +25,7 @@ namespace PRSDesktop
         public string Trolleys { get; set; }
         public object OrderColor { get; set; }
         public string OrderStatus { get; set; }
+        public string Consignment { get; set; }
         public TimeSpan Time { get; set; }
         public double PercentageComplete { get; set; }
         public string Issues { get; set; }

+ 8 - 3
prs.desktop/Panels/Manufacturing/ManufacturingPanel.xaml.cs

@@ -102,7 +102,8 @@ namespace PRSDesktop
                         x => x.DueDate,
                         x => x.PurchaseOrderLink.SupplierLink.Code,
                         x => x.PurchaseOrderLink.PONumber,
-                        x => x.ReceivedReference
+                        x => x.Consignment.Number,
+                        x => x.ConsignmentParcel.Description
                     ),
                     null
                 )
@@ -129,8 +130,12 @@ namespace PRSDesktop
 
                 var suppliercode = orderrow.Get<PurchaseOrderItem, string>(c => c.PurchaseOrderLink.SupplierLink.Code);
                 var ponumber = orderrow.Get<PurchaseOrderItem, string>(c => c.PurchaseOrderLink.PONumber);
-                var poreference = orderrow.Get<PurchaseOrderItem, string>(c => c.ReceivedReference);
-
+                var consnumber = orderrow.Get<PurchaseOrderItem, string>(c => c.Consignment.Number);
+                var parcel = orderrow.Get<PurchaseOrderItem, string>(c => c.ConsignmentParcel.Description);
+                var poreference = !string.IsNullOrWhiteSpace(parcel)
+                    ? $"{consnumber}/{parcel}"
+                    : consnumber;
+                
                 var tag = receiveddate.IsEmpty() ? "ETA" : "RCVD";
 
                 Data.OrderItems.Add(new Tuple<Guid, DateTime, string>(

+ 6 - 2
prs.desktop/Panels/Manufacturing/ManufacturingPanelColumn.xaml

@@ -4,19 +4,21 @@
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
              xmlns:local="clr-namespace:PRSDesktop"
+             xmlns:wpf="clr-namespace:InABox.WPF;assembly=InABox.Wpf"
              mc:Ignorable="d"
              d:DesignHeight="600" d:DesignWidth="300">
 
     <UserControl.Resources>
 
         <local:DateTimeToVisibilityConverter x:Key="DateTimeToVisibilityConverter" />
+        <wpf:StringToVisibilityConverter x:Key="StringToVisibilityConverter" />
 
         <Style x:Key="DueDateStyle" TargetType="TextBlock">
             <Setter Property="Visibility"
                     Value="{Binding Path=DueDate, Converter={StaticResource DateTimeToVisibilityConverter}}" />
         </Style>
 
-        <DataTemplate x:Key="FullManufacturingPanel">
+        <DataTemplate x:Key="FullManufacturingPanel" DataType="{x:Type local:ManufacturingKanban}">
             <Grid Margin="0,1,0,1">
                 <Grid.RowDefinitions>
                     <RowDefinition Height="auto"/>
@@ -63,7 +65,9 @@
                             <TextBlock DockPanel.Dock="Top" Text="{Binding Title}" TextWrapping="Wrap"
                                        FontWeight="DemiBold" FontSize="12" />
                             <TextBlock DockPanel.Dock="Top" Text="{Binding Description}" TextWrapping="Wrap"
-                                       LineHeight="25" Padding="0,5,0,0" FontSize="12" />
+                                       Padding="0,2,0,2" FontSize="12" Visibility="{Binding Description, Converter={StaticResource StringToVisibilityConverter}}" />
+                            <TextBlock DockPanel.Dock="Top" Text="{Binding Consignment}" TextWrapping="Wrap"
+                                       Padding="0,2,0,2" FontSize="12" FontStyle="Italic" Visibility="{Binding Consignment, Converter={StaticResource StringToVisibilityConverter}}"/>
                         </DockPanel>
 
                         <Image Grid.Row="0" Grid.Column="1" VerticalAlignment="Top" Source="{Binding Path=Image}"

+ 5 - 3
prs.desktop/Panels/Manufacturing/ManufacturingPanelColumn.xaml.cs

@@ -308,10 +308,12 @@ namespace PRSDesktop
                         location
                     };
 
-                    if (orderitem != null)
-                        descrip.Add(orderitem.Item3);
-
+ 
                     model.Description = string.Join("\n", descrip);
+                    
+                    model.Consignment = orderitem?.Item3 ?? "";
+                    // if (orderitem != null)
+                    //     descrip.Add(orderitem.Item3);
 
                     model.TemplateID = packet.ManufacturingTemplateLink.ID;