ソースを参照

Interim Ordering Changes

frogsoftware 9 ヶ月 前
コミット
cd5fa57471

+ 19 - 0
prs.classes/Entities/Job/LightJobLink.cs

@@ -0,0 +1,19 @@
+using System;
+using InABox.Core;
+
+namespace Comal.Classes
+{
+    public class LightJobLink : EntityLink<Job>
+    {
+
+        [NullEditor]
+        public override Guid ID { get; set; }
+        
+        [CodeEditor(Visible=Visible.Default)]
+        public string JobNumber { get; set; }
+        
+        [TextBoxEditor(Editable = Editable.Hidden)]
+        public string Name { get; set; }
+
+    }
+}

+ 14 - 0
prs.classes/Entities/PurchaseOrder/LightPurchaseOrderItemLink.cs

@@ -0,0 +1,14 @@
+using System;
+using InABox.Core;
+
+namespace Comal.Classes
+{
+    public class LightPurchaseOrderItemLink : EntityLink<PurchaseOrderItem>
+    {
+        public override Guid ID { get; set; }
+        
+        public LightPurchaseOrderLink PurchaseOrderLink { get; set; }
+        
+        public string Description { get; set; }
+    }
+}

+ 15 - 0
prs.classes/Entities/PurchaseOrder/LightPurchaseOrderLink.cs

@@ -0,0 +1,15 @@
+using System;
+using InABox.Core;
+
+namespace Comal.Classes
+{
+    public class LightPurchaseOrderLink : EntityLink<PurchaseOrder>
+    {
+        [NullEditor]
+        public override Guid ID { get; set; }
+        
+        [CodeEditor(Visible=Visible.Default)]
+        public String PONumber { get; set; }
+        
+    }
+}

+ 14 - 3
prs.classes/Entities/PurchaseOrder/PurchaseOrderItem.cs

@@ -182,8 +182,6 @@ namespace Comal.Classes
         [Obsolete]
         public StockMovementLink StockMovement { get; set; }
         
-
-
         [NullEditor]
         [EntityRelationship(DeleteAction.SetNull)]
         public ManufacturingPacketLink Packet { get; set; }
@@ -195,7 +193,10 @@ namespace Comal.Classes
         [NullEditor]
         [Aggregate(typeof(PurchaseOrderItemOpenForms))]
         public int OpenForms { get; set; }
-
+        
+        [ChildEntity(typeof(PurchaseOrderItemAllocationNominatedJob))]
+        public PurchaseOrderItemAllocationJobLink NominatedJob { get; set; }
+        
         static PurchaseOrderItem()
         {
             LinkedProperties.Register<PurchaseOrderItem, ProductLink, String>(x => x.Product, x => x.Code, x => x.SupplierCode);
@@ -278,4 +279,14 @@ namespace Comal.Classes
             
         }
     }
+    
+    public class PurchaseOrderItemAllocationNominatedJob : IChildEntityDefinition<PurchaseOrderItemAllocation>
+    {
+        public Filter<PurchaseOrderItemAllocation>? Filter { get; set; } = null;
+
+        public SortOrder<PurchaseOrderItemAllocation>? Sort { get; set; }
+            = new SortOrder<PurchaseOrderItemAllocation>(x => x.Job.JobNumber, SortDirection.Descending).ThenBy(x => x.Created, SortDirection.Descending);
+
+        public Expression<Func<PurchaseOrderItemAllocation, Guid>> Parent => x => x.Item.ID;
+    }
 }

+ 14 - 1
prs.classes/Entities/Job/Requisitions/PurchaseOrderItemAllocation.cs → prs.classes/Entities/PurchaseOrder/PurchaseOrderItemAllocation.cs

@@ -28,7 +28,20 @@ namespace Comal.Classes
         
         public double Quantity { get; set; }
 
+        [CheckBoxEditor]
+        public bool Nominated { get; set; }
+    }
+
+    
+    
+    public class PurchaseOrderItemAllocationJobLink : EntityLink<PurchaseOrderItemAllocation>
+    {
         [NullEditor]
-        public bool IsCanonical { get; set; }
+        public override Guid ID { get; set; }
+        
+        public LightJobLink Job { get; set; }
+        
+        public LightPurchaseOrderItemLink Item { get; set; }
+        
     }
 }

+ 0 - 1
prs.classes/Entities/PurchaseOrder/PurchaseOrderItemLink.cs

@@ -3,7 +3,6 @@ using InABox.Core;
 
 namespace Comal.Classes
 {
-    
     public class PurchaseOrderItemLink : EntityLink<PurchaseOrderItem>
     {
         [RequiredColumn]

+ 3 - 3
prs.desktop/Panels/Jobs/Summary/JobSummaryGrid.cs

@@ -152,9 +152,9 @@ internal class JobSummaryGrid : DynamicDataGrid<JobMaterial>, IMasterDetailContr
             {
                 var items = holding.Value.Where(
                     x => x.ID != Guid.Empty
-                         && x.Status != JobRequisitionItemStatus.Cancelled
-                         && x.Status != JobRequisitionItemStatus.Issued
-                         && x.Status != JobRequisitionItemStatus.Archived
+                         && x.Cancelled.IsEmpty()
+                         && x.Archived.IsEmpty()
+                         && (x.Qty - x.Issued).IsEffectivelyGreaterThan(0.0)
                 );
                 foreach (var item in items)
                 {

+ 12 - 3
prs.desktop/Panels/PurchaseOrders/SupplierPurchaseOrderItemOneToMany.cs

@@ -579,13 +579,19 @@ public class SupplierPurchaseOrderItemOneToMany : DynamicOneToManyGrid<PurchaseO
                     poi.Product.CopyFrom(sp.Product);
                     poi.Style.CopyFrom(sp.Style);
                     poi.Dimensions.CopyFrom(sp.Dimensions);
-                    poi.Job.CopyFrom(sp.Job);
+                    //poi.Job.CopyFrom(sp.Job);
                     poi.ForeignCurrencyCost = sp.ForeignCurrencyPrice;
                     poi.Cost = sp.CostPrice;
                     poi.CostCentre.CopyFrom(sp.Product.CostCentre);
                     poi.PurchaseGL.CopyFrom(sp.Product.PurchaseGL);
                     poi.Description = sp.Product.Name;
                     result.Add(poi);
+                    
+                    var poia = new PurchaseOrderItemAllocation();
+                    poia.Job.CopyFrom(sp.Job);
+                    poia.Nominated = true;
+                    Allocations.Add(new(poi, poia));
+                    
                 }
                 else if (pi != null)
                 {
@@ -648,14 +654,17 @@ public class SupplierPurchaseOrderItemOneToMany : DynamicOneToManyGrid<PurchaseO
                     poi.Style.ID = sp.Style.ID;
                     poi.Style.Synchronise(sp.Style);
                     poi.Dimensions.CopyFrom(sp.Dimensions);
-                    poi.Job.ID = sp.Job.ID;
-                    poi.Job.Synchronise(sp.Job);
                     poi.ForeignCurrencyCost = sp.ForeignCurrencyPrice;
                     poi.Cost = sp.CostPrice;
                     poi.CostCentre.CopyFrom(sp.Product.CostCentre);
                     poi.PurchaseGL.CopyFrom(sp.Product.PurchaseGL);
                     poi.Description = sp.Product.Name;
                     result.Add(poi);
+                    
+                    var poia = new PurchaseOrderItemAllocation();
+                    poia.Job.CopyFrom(sp.Job);
+                    poia.Nominated = true;
+                    Allocations.Add(new(poi, poia));
                 }
                 return result;
             });

+ 2 - 4
prs.desktop/Panels/Reservation Management/ReservationManagementPurchasing.xaml.cs

@@ -255,11 +255,9 @@ public partial class ReservationManagementPurchasing : UserControl
         foreach (var jobRequisitionItem in selected)
         {
             // We only want not checked or order required.
-            if(jobRequisitionItem.Status != JobRequisitionItemStatus.NotChecked &&
-                jobRequisitionItem.Status != JobRequisitionItemStatus.OrderRequired)
-            {
+            if(!jobRequisitionItem.Qty.IsEffectivelyGreaterThan(jobRequisitionItem.InStock))
                 continue;
-            }
+            
             // Filter out ones we've already got.
             if(JobRequiItems.Any(x => x.JobRequiItem.ID == jobRequisitionItem.ID))
             {

+ 54 - 54
prs.desktop/Panels/Stock Forecast/OrderScreen/StockForecastOrderingGrid.cs

@@ -28,18 +28,18 @@ namespace PRSDesktop;
 
 public class StockForecastBreakupKey(Guid jobID, Guid requiID)
 {
-    public Guid JobID { get; set; } = jobID != Guid.Empty ? jobID : throw new ArgumentException("jobID cannot be Guid.Empty!", "jobID");
+    public Guid JobID { get; set; } = jobID != Guid.Empty ? jobID : throw new ArgumentException("Job ID cannot be Empty!", "Job ID");
 
-    public Guid JobRequiItemID { get; set; } = requiID;
+    public Guid RequiID { get; set; } = requiID;
 
     public override bool Equals(object? obj)
     {
-        return obj is StockForecastBreakupKey key && key.JobID == JobID && key.JobRequiItemID == JobRequiItemID;
+        return obj is StockForecastBreakupKey key && key.JobID == JobID && key.RequiID == RequiID;
     }
 
     public override int GetHashCode()
     {
-        return HashCode.Combine(JobID, JobRequiItemID);
+        return HashCode.Combine(JobID, RequiID);
     }
 }
 
@@ -84,20 +84,22 @@ public class StockForecastOrderingItemQuantity
 {
     public event Action? Changed;
 
-    private double _stockTotal;
-    public double StockTotal
+    private double orderTotal;
+
+    public double OrderTotal
     {
-        get => _stockTotal;
+        get => orderTotal;
         set
         {
-            _stockTotal = value;
+            orderTotal = value;
             Changed?.Invoke();
         }
     }
 
-    public Dictionary<StockForecastBreakupKey, double> JobTotals { get; init; } = [];
+    public Dictionary<StockForecastBreakupKey, double> Breakups { get; init; } = [];
 
     private SupplierProduct? _supplierProduct;
+
     /// <summary>
     /// Indicates the Supplier Product that has been selected for this cell. This comes from the combobox column.
     /// </summary>
@@ -116,11 +118,6 @@ public class StockForecastOrderingItemQuantity
         Changed?.Invoke();
     }
 
-    public double JobTotal => JobTotals.Values.Sum();
-
-    public double GetTotal(StockForecastOrderingType type) => type == StockForecastOrderingType.StockOrder
-        ? StockTotal
-        : JobTotal;
 }
 
 public class StockForecastOrderingItem : BaseObject
@@ -149,6 +146,7 @@ public class StockForecastOrderingItem : BaseObject
     {
         return JobRequiredQuantities;
     }
+    
     public void SetJobRequiredQuantity(Guid jobID, Guid requiID, double requiredQuantity)
     {
         JobRequiredQuantities[new(jobID, requiID)] = requiredQuantity;
@@ -160,9 +158,7 @@ public class StockForecastOrderingItem : BaseObject
 
     public StockForecastOrderingItemQuantity GetQuantity(int i) => Quantities[i];
 
-    public double GetTotalQuantity(StockForecastOrderingType type) => type == StockForecastOrderingType.StockOrder
-        ? Quantities.Sum(x => x.StockTotal)
-        : Quantities.Sum(x => x.JobTotal);
+    public double GetTotalQuantity(StockForecastOrderingType type) => Quantities.Sum(x => x.OrderTotal);
 
     public void SetQuantities(StockForecastOrderingItemQuantity[] quantities)
     {
@@ -289,22 +285,22 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
                         continue;
                     }
 
-                    if(OrderType == StockForecastOrderingType.StockOrder && qty.StockTotal > 0)
+                    if(OrderType == StockForecastOrderingType.StockOrder && qty.OrderTotal > 0)
                     {
-                        yield return new(supplier, new(), item, qty.StockTotal, qty.SupplierProduct);
+                        yield return new(supplier, new(), item, qty.OrderTotal, qty.SupplierProduct);
                     }
                     else
                     {
                         var breakups = new List<StockForecastOrderData.QuantityBreakup>();
-                        foreach(var (key, q) in qty.JobTotals)
+                        foreach(var (key, q) in qty.Breakups)
                         {
                             // Check JobID because we are to skip the empty job (this is just the difference between all the allocations and the quantity on the PO).
                             if(q > 0 && key.JobID != Guid.Empty)
                             {
-                                breakups.Add(new(key.JobID, key.JobRequiItemID, "", q));
+                                breakups.Add(new(key.JobID, key.RequiID, "", q));
                             }
                         }
-                        yield return new(supplier, breakups, item, qty.JobTotal, qty.SupplierProduct);
+                        yield return new(supplier, breakups, item, qty.OrderTotal, qty.SupplierProduct);
                     }
                 }
             }
@@ -449,11 +445,11 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
 
         var qty = item.GetQuantity(supplierIdx);
         qty.SupplierProduct = supplierProduct;
-        qty.StockTotal = 0;
-        qty.JobTotals.Clear();
+        qty.OrderTotal = 0;
+        qty.Breakups.Clear();
         foreach(var id in item.GetJobRequiredQuantities().Keys)
         {
-            qty.JobTotals[id] = 0;
+            qty.Breakups[id] = 0;
         }
 
         return supplierProduct;
@@ -479,18 +475,10 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
             if(supplierIdx != -1)
             {
                 var qty = item.GetQuantity(supplierIdx);
-                if(OrderType == StockForecastOrderingType.StockOrder)
-                {
-                    qty.StockTotal = GetRequiredQuantity(item, selectedSupplierProduct, item.RequiredQuantity);
-                }
-                else
-                {
-                    qty.JobTotals.Clear();
-                    foreach(var (id, q) in item.GetJobRequiredQuantities())
-                    {
-                        qty.JobTotals[id] = GetRequiredQuantity(item, selectedSupplierProduct, q);
-                    }
-                }
+                qty.OrderTotal = GetRequiredQuantity(item, selectedSupplierProduct, item.RequiredQuantity);
+                qty.Breakups.Clear();
+                foreach(var (id, q) in item.GetJobRequiredQuantities())
+                        qty.Breakups[id] = q;
             }
         }
     }
@@ -576,7 +564,11 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
 
     private double GetRequiredQuantity(StockForecastOrderingItem item, SupplierProduct supplierProduct, double requiredQuantity)
     {
-        switch (item.OrderStrategy)
+        var _strategy = item.CustomStrategy
+            ? SupplierProductOrderStrategy.LowestOverstock
+            : item.OrderStrategy;
+        
+        switch (_strategy)
         {
             case SupplierProductOrderStrategy.Exact:
                 return requiredQuantity;
@@ -724,20 +716,20 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
                     quantities[newIdx] = newQty;
                     if(OrderType == StockForecastOrderingType.StockOrder)
                     {
-                        newQty.StockTotal = 0;
+                        newQty.OrderTotal = 0;
                     }
                     else
                     {
                         foreach(var id in item.GetJobRequiredQuantities().Keys)
                         {
-                            newQty.JobTotals[id] = 0;
+                            newQty.Breakups[id] = 0;
                         }
                     }
                     item.SetQuantities(quantities);
                 }
 
                 Suppliers = newSuppliers;
-
+                _loadedColumns = false;
                 Refresh(true, true);
             }
         });
@@ -833,11 +825,11 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
                     Background = new SolidColorBrush(Colors.LightYellow),
                     BorderThickness = new Thickness(0.0),
                     MinValue = 0.0,
-                    Value = Item.GetQuantity(SupplierIndex).StockTotal
+                    Value = Item.GetQuantity(SupplierIndex).OrderTotal
                 };
                 editor.ValueChanged += (o, e) =>
                 {
-                    Item.GetQuantity(SupplierIndex).StockTotal = editor.Value ?? default;
+                    Item.GetQuantity(SupplierIndex).OrderTotal = editor.Value ?? default;
                 };
                 Content = editor;
             }
@@ -856,7 +848,7 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
                     Background = new SolidColorBrush(Colors.White),
                     BorderThickness = new Thickness(0.0),
                     IsReadOnly = true,
-                    Text = string.Format("{0:F2}", Item.GetQuantity(SupplierIndex).JobTotal)
+                    Text = string.Format("{0:F2}", Item.GetQuantity(SupplierIndex).OrderTotal)
                 };
                 Grid.SetColumn(editor, 0);
                 grid.Children.Add(editor);
@@ -876,15 +868,15 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
                 {
                     var qty = Item.GetQuantity(SupplierIndex);
 
-                    Parent.LoadJobData(qty.JobTotals.Keys.Select(x => x.JobID));
-                    Parent.LoadJobRequiData(qty.JobTotals.Keys.Select(x => x.JobRequiItemID));
+                    Parent.LoadJobData(qty.Breakups.Keys.Select(x => x.JobID));
+                    Parent.LoadJobRequiData(qty.Breakups.Keys.Select(x => x.RequiID));
 
-                    var items = qty.JobTotals.Select(x =>
+                    var items = qty.Breakups.Select(x =>
                     {
                         var item = new StockForecastOrderingJobItem
                         {
                             JobID = x.Key.JobID,
-                            JobRequiID = x.Key.JobRequiItemID,
+                            JobRequiID = x.Key.RequiID,
                             RequiredQuantity = Item.GetJobRequiredQuantities().GetValueOrDefault(x.Key),
                             Quantity = x.Value
                         };
@@ -912,10 +904,10 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
                     {
                         foreach(var item in items)
                         {
-                            qty.JobTotals[new(item.JobID, item.JobRequiID)] = item.Quantity;
+                            qty.Breakups[new(item.JobID, item.JobRequiID)] = item.Quantity;
                         }
                         qty.DoChanged();
-                        editor.Text = string.Format("{0:F2}", Item.GetQuantity(SupplierIndex).JobTotal);
+                        editor.Text = string.Format("{0:F2}", Item.GetQuantity(SupplierIndex).OrderTotal);
                     }
                 };
                 grid.Children.Add(btn);
@@ -947,7 +939,7 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
             var instance = LoadItem(row);
 
             var comboBox = new ComboBox();
-
+            comboBox.Tag = idx;
             var items = SupplierProducts.Where(x => x.SupplierLink.ID == Suppliers[idx].ID && x.Product.ID == instance.Product.ID)
                 .Select(x => new KeyValuePair<SupplierProduct?, string>(
                     x,
@@ -963,7 +955,15 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
             comboBox.Bind(ComboBox.SelectedValueProperty, qty, x => x.SupplierProduct);
             comboBox.SelectionChanged += (o, e) =>
             {
+                var box = o as ComboBox; 
                 instance.CustomStrategy = true;
+                var _item = LoadItem(row);
+                var _product = ((o as ComboBox)?.SelectedValue as SupplierProduct ?? new SupplierProduct());
+                qty.OrderTotal = GetRequiredQuantity(_item, _product, _item.RequiredQuantity);
+                qty.Breakups.Clear();
+                foreach(var (id, q) in _item.GetJobRequiredQuantities())
+                    qty.Breakups[id] = q;
+                
                 InvalidateRow(row);
             };
 
@@ -977,7 +977,7 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
             return comboBox;
         })
         {
-            HeaderText = "Supplier Product.",
+            HeaderText = "U.O.M.",
             Width = 80
         };
         QuantityColumns[idx] = new DynamicTemplateColumn(row =>
@@ -1003,7 +1003,7 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
             var qty = instance.GetQuantity(idx);
             if(qty.SupplierProduct is not null)
             {
-                return $"{qty.GetTotal(OrderType) * qty.SupplierProduct.CostPrice:C2}";
+                return $"{qty.OrderTotal * qty.SupplierProduct.CostPrice:C2}";
             }
             else
             {
@@ -1084,7 +1084,7 @@ public class StockForecastOrderingGrid : DynamicItemsListGrid<StockForecastOrder
                     var qty = item.GetQuantity(SupplierIndex);
                     if(qty.SupplierProduct is not null)
                     {
-                        Sum += qty.GetTotal(Grid.OrderType) * qty.SupplierProduct.CostPrice;
+                        Sum += qty.OrderTotal * qty.SupplierProduct.CostPrice;
                     }
                 }
             }

+ 3 - 11
prs.desktop/Panels/Stock Forecast/StockForecastGrid.cs

@@ -977,19 +977,11 @@ public class StockForecastGrid : DynamicItemsListGrid<StockForecastItem>, IDataM
         {
             var item = new StockForecastOrderData(forecastItem.Product, forecastItem.Style, forecastItem.Dimensions);
             item.RequiredQuantity = Optimise ? forecastItem.Optimised : forecastItem.Required;
-
-            //item.RequiredQuantity = forecastItem.StockRequired;
-            // if(forecastItem.StockRequired > 0)
-            // {
-            //     item.SetRequiredQuantity(Guid.Empty, Guid.Empty, "", forecastItem.StockRequired);
-            // }
-             foreach(var (id, jobInfo) in forecastItem.JobInfo)
-             {
+            
+            foreach(var (id, jobInfo) in forecastItem.JobInfo)
+            {
                 if (jobInfo.Required > 0)
-                {
-                    item.RequiredQuantity += jobInfo.Required;
                     item.SetRequiredQuantity(id, Guid.Empty, jobInfo.JobNumber, jobInfo.Required);
-                }
             }
 
             items.Add(item);

+ 65 - 85
prs.shared/Database Update Scripts/Update_8_24.cs

@@ -6,13 +6,7 @@ namespace PRS.Shared.Database_Update_Scripts;
 
 internal class Update_8_24 : DatabaseUpdateScript
 {
-    private Type jripoiType = null;
     
-    public Update_8_24()
-    {
-        jripoiType = CoreUtils.GetEntity("Comal.Classes.JobRequisitionItemPurchaseOrderItem");
-    } 
-
     private static readonly int CHUNK_SIZE = 500;
     private static readonly bool DESTRUCTIVE = false;
     
@@ -22,95 +16,77 @@ internal class Update_8_24 : DatabaseUpdateScript
     {
         var _provider = DbFactory.NewProvider(Logger.Main);
         Clear_POIAs(_provider);
-        var _poias = Migrate_JRIPOIs(_provider);
-        Migrate_POIs(_provider, _poias);
+        Migrate_JRIPOIs(_provider);
+        Update_POIs(_provider);
         return true;
     }
 
     private void Clear_POIAs(IProvider provider)
     {
-        Logger.Send(LogType.Information, "", "Clearing Existing Allocations");
-        var _queue = provider.Query(
-            new Filter<PurchaseOrderItemAllocation>().All(),
-            Columns.None<PurchaseOrderItemAllocation>().Add(x => x.ID)
-        ).Rows.ToQueue();
-
-        while (_queue.Any())
-        {
-            List<PurchaseOrderItemAllocation> _poias = _queue
-                .Dequeue(CHUNK_SIZE)
-                .Select(x => x.ToObject<PurchaseOrderItemAllocation>())
-                .ToList();
-            provider.Delete(_poias, "");
-
-            Logger.Send(LogType.Information, "", $"- Deleted {_poias.Count} Allocations ({_queue.Count} remaining)");
-        }
+        Logger.Send(LogType.Information, "", "Clearing Existing POIAs");
+        var sql = $"delete from PurchaseOrderItemAllocation";
+        var deleted = provider.Update(sql);
+        Logger.Send(LogType.Information,"",$"Cleared {deleted} POIAs");        
     }
-
-    private static readonly string RANDOMGUID =
-        "lower(hex(randomblob(4)) " +
-        "|| '-' || hex(randomblob(2)) " +
-        "|| '-' || '4' || substr(hex( randomblob(2)), 2) " +
-        "|| '-' || substr('AB89', 1 + (abs(random()) % 4) , 1)  || substr(hex(randomblob(2)), 2) " +
-        "|| '-' || hex(randomblob(6)))";
-
-    private List<PurchaseOrderItemAllocation> Migrate_JRIPOIs(IProvider provider)
+    
+    private void Migrate_JRIPOIs(IProvider provider)
     {
-        // var sql = $"insert into PurchaseOrderItemAllocation (ID, [JobRequisitionItem.ID], Qty)" +
-        //           $"select " +
-        //           $"lower(hex(randomblob(4)) " +
-        //           $"|| '-' || hex(randomblob(2)) " +
-        //           $"|| '-' || '4' || substr(hex( randomblob(2)), 2) " +
-        //           $"|| '-' || substr('AB89', 1 + (abs(random()) % 4) , 1)  || substr(hex(randomblob(2)), 2) " +
-        //           $"|| '-' || hex(randomblob(6))) as ID, " +
-        //           $"jripoi.[PurchaseOrderItem.ID],jripoi.[JobRequisitionItem.ID],poi.Qty " +
-        //           $"from JobRequisitionItemPurchaseOrderItem jripoi " +
-        //           $"join PurchaseOrderItem poi on poi.[ID] = jripoi.[PurchaseOrderItem.ID]";
-        
-        var _result = new List<PurchaseOrderItemAllocation>();
-
-        if (!provider.TableExists(jripoiType))
-            return _result;
-        
-        Logger.Send(LogType.Information,"","Migrating JobRequisitionItems");
-        var _queue = provider.Query(jripoiType,
-            null,
-            Columns.None(jripoiType)
-                .Add("PurchaseOrderItem.ID")
-                .Add("PurchaseOrderItem.Qty")
-                .Add("JobRequisitionItem.ID")
-                .Add("JobRequisitionItem.Job.ID")
-        ).Rows.ToQueue();
-        
-        while (_queue.Any())
+        if (provider.TableExists("JobRequisitionItemPurchaseOrderItem"))
         {
-            List<PurchaseOrderItemAllocation> _poias = new();
-            var _rows = _queue.Dequeue(CHUNK_SIZE).ToList();
-            foreach (var _row in _rows)
+            Logger.Send(LogType.Information,"","Migrating JRIPOIs");
+            
+            var _pois = provider.Query(
+                null, 
+                Columns.None<PurchaseOrderItem>().Add(x => x.ID).Add(x => x.Qty)
+            ).ToDictionary<PurchaseOrderItem,Guid,double>(x=>x.ID, x=>x.Qty);
+
+            var _sql =
+                $"select jripoi.[purchaseorderitem.id], jripoi.[jobrequisitionitem.id], jri.[job.id], cast(poi.[qty] as double) as [qty] " +
+                $"from jobrequisitionitempurchaseorderitem jripoi " +
+                $"left outer join jobrequisitionitem jri on jripoi.[jobrequisitionitem.id] = jri.[id] " +
+                $"left outer join purchaseorderitem poi on jripoi.[purchaseorderitem.id] = poi.[id]";
+            var _queue = provider.Query(_sql)
+                .Rows.ToQueue();
+            
+            List<Guid> _ids = new();
+            while (_queue.Any())
             {
-                var _poia = new PurchaseOrderItemAllocation();
-                _poia.Item.ID = _row.Get<Guid>("PurchaseOrderItem.ID");
-                _poia.Job.ID = _row.Get<Guid>("JobRequisitionItem.Job.ID");
-                _poia.JobRequisitionItem.ID = _row.Get<Guid>("JobRequisitionItem.ID");
-                _poia.Quantity = _row.Get<double>("PurchaseOrderItem.Qty");
-                _poias.Add(_poia);
-                CoreUtils.SetPropertyValue(_row,"Job.ID",Guid.Empty);
+                List<PurchaseOrderItemAllocation> _poias = new();
+                var _jripois = _queue.Dequeue(CHUNK_SIZE).ToList();
+                foreach (var _jripoi in _jripois)
+                {
+                    Guid poi_id = Guid.TryParse(_jripoi.Get<string>("purchaseorderitem.id"), out var pid) ? pid : Guid.Empty;
+                    var _poia = new PurchaseOrderItemAllocation();
+                    _poia.Item.ID = poi_id;
+                    _poia.Job.ID = Guid.TryParse(_jripoi.Get<string>("job.id"), out var jid) ? jid : Guid.Empty;
+                    _poia.JobRequisitionItem.ID = Guid.TryParse(_jripoi.Get<string>("jobrequisitionitem.id"), out var rid) ? rid : Guid.Empty;
+                    _poia.Quantity = _jripoi.Get<double>("qty");
+                    _poia.Nominated = !_ids.Contains(poi_id);
+                    _poias.Add(_poia);
+                    _ids.Add(poi_id);
+                }
+
+                provider.Save(_poias);
+                Logger.Send(LogType.Information, "",
+                    $"- Created {_poias.Count} Allocations ({_queue.Count} remaining)");
             }
-            provider.Save(_poias);
-            Logger.Send(LogType.Information, "", $"- Created {_poias.Count} Allocations ({_queue.Count} remaining)");
-            _result.AddRange(_poias);
+
         }
         
         if (DESTRUCTIVE)
-            provider.DropTable(jripoiType);
-        
-        return _result;
+            provider.DropTable("JobRequisitionItemPurchaseOrderItem");
     }
     
-    private void Migrate_POIs(IProvider provider, List<PurchaseOrderItemAllocation> poias)
+    private void Update_POIs(IProvider provider)
     {
         Logger.Send(LogType.Information,"","Migrating PurchaseOrderItems");
-        var _ids = poias.Select(x => x.Item.ID).Distinct().ToArray();
+        
+        var _ids = provider.Query(
+            null,
+            Columns.None<PurchaseOrderItemAllocation>()
+                .Add(x=>x.Item.ID)
+            ).ExtractValues<PurchaseOrderItemAllocation,Guid>(x => x.Item.ID,true);
+        
         var _queue = provider.Query(
             new Filter<PurchaseOrderItem>("Job.ID").IsNotEqualTo(Guid.Empty),
             Columns.Required<PurchaseOrderItem>().Add("Job.ID")
@@ -120,21 +96,25 @@ internal class Update_8_24 : DatabaseUpdateScript
         {
             List<PurchaseOrderItemAllocation> _poias = new();
             var _pois = _queue.Dequeue(CHUNK_SIZE)
-                .Where(r => !_ids.Contains(r.Get<PurchaseOrderItemAllocation,Guid>(c=>c.ID)))
                 .Select(x=>x.ToObject<PurchaseOrderItem>())
                 .ToList();
             foreach (var _poi in _pois)
             {
-                var _poia = new PurchaseOrderItemAllocation();
-                _poia.Item.ID = _poi.ID;
-                _poia.Job.ID = (Guid)(CoreUtils.GetPropertyValue(_poi, "Job.ID") ?? Guid.Empty);
-                _poia.Quantity = _poi.Qty;
-                _poias.Add(_poia);
+                if (!_ids.Contains(_poi.ID))
+                {
+                    var _poia = new PurchaseOrderItemAllocation();
+                    _poia.Item.ID = _poi.ID;
+                    _poia.Job.ID = (Guid)(CoreUtils.GetPropertyValue(_poi, "Job.ID") ?? Guid.Empty);
+                    _poia.Quantity = _poi.Qty;
+                    _poia.Nominated = true;
+                    _poias.Add(_poia);
+                }
+                
                 CoreUtils.SetPropertyValue(_poi,"Job.ID",Guid.Empty);
             }
             provider.Save(_poias);
             if(DESTRUCTIVE)
-                provider.Save(_pois);
+                provider.Save(_pois.Where(x=>x.IsChanged()));
             Logger.Send(LogType.Information, "", $"- Created {_poias.Count} Allocations ({_queue.Count} remaining)");
         }
     }

+ 2 - 2
prs.shared/Posters/Timberline/BillTimberlinePoster.cs

@@ -319,11 +319,11 @@ public class BillTimberlinePoster : ITimberlinePoster<Bill, BillTimberlineSettin
             parentalias: "Bill_BillLine", childalias: "POItem",
             columns: Columns.None<PurchaseOrderItem>().Add(x => x.ID)
                 .Add(x => x.PurchaseOrderLink.PONumber)
-                .Add(x => x.Job.JobNumber)
                 .Add(x => x.Qty)
                 .Add(x => x.Description)
                 .Add(x => x.Cost)
                 .Add(x => x.PostedReference)
+                .Add(x => x.NominatedJob.Job.JobNumber)
                 );
 
         Script?.Execute(methodname: "BeforePost", parameters: new object[] { model });
@@ -428,7 +428,7 @@ public class BillTimberlinePoster : ITimberlinePoster<Bill, BillTimberlineSettin
                 if (purchaseOrderItems.TryGetValue(billLine.OrderItem.ID, out var poItem))
                 {
                     apdf.Commitment = poItem.PurchaseOrderLink.PONumber;
-                    apdf.Job = poItem.Job.JobNumber;
+                    apdf.Job = poItem.NominatedJob.Job.JobNumber;
                     if (int.TryParse(poItem.PostedReference, out var itemNumber))
                     {
                         apdf.CommitmentLineItem = itemNumber;

+ 3 - 3
prs.shared/Posters/Timberline/PurchaseOrderTimberlinePoster.cs

@@ -232,13 +232,13 @@ public class Module
                 .Add(x => x.PostedReference)
                 .Add(x => x.Description)
                 .Add(x => x.ReceivedDate)
-                .Add(x => x.Job.JobNumber)
                 .Add(x => x.CostCentre.Code)
                 .Add(x => x.TaxCode.Code)
                 .Add(x => x.Qty)
                 .Add(x => x.Cost)
                 .Add(x => x.Dimensions.UnitSize)
-                .Add(x => x.IncTax),
+                .Add(x => x.IncTax)
+                .Add(x => x.NominatedJob.Job.JobNumber),
                 alias: "PurchaseOrder_PurchaseOrderItem");
 
             Script?.Execute(methodname: "BeforePost", parameters: new object[] { model });
@@ -323,7 +323,7 @@ public class Module
                             // RetainagePercent = ,
                             DeliveryDate = purchaseOrderItem.ReceivedDate,
                             //ScopeOfWork
-                            Job = purchaseOrderItem.Job.JobNumber,
+                            Job = purchaseOrderItem.NominatedJob.Job.JobNumber,
                             //Extra = purchaseOrderItem.Job
                             CostCode = purchaseOrderItem.CostCentre.Code,
                             //Category = purchaseOrderItem.cat