Jelajahi Sumber

Modified POIStore to use new allocation system.

Kenric Nugteren 8 bulan lalu
induk
melakukan
6650df7ee8

+ 4 - 3
prs.classes/EnclosedEntities/Dimensions/Dimensions.cs

@@ -355,7 +355,8 @@ namespace Comal.Classes
         public static Columns<T> LocalColumns<T>()
             where T : IDimensions
         {
-            return Columns.None<T>().Add(x => x.Unit.ID)
+            return Columns.None<T>()
+                .Add(x => x.Unit.ID)
                 .Add(x => x.Quantity)
                 .Add(x => x.Length)
                 .Add(x => x.Width)
@@ -368,7 +369,7 @@ namespace Comal.Classes
         public static Columns<T> DataColumns<T>()
             where T : IDimensions
         {
-            return Columns.None<T>().Add(x => x.Unit.ID)
+            return Columns.None<T>()
                 .Add(x => x.Unit.ID)
                 .Add(x => x.Unit.Format)
                 .Add(x => x.Unit.Formula)
@@ -389,7 +390,7 @@ namespace Comal.Classes
         public static Columns<T> AllColumns<T>()
             where T : IDimensions
         {
-            return Columns.None<T>().Add(x => x.Unit.ID)
+            return Columns.None<T>()
                 .Add(x => x.Unit.ID)
                 .Add(x => x.Unit.Format)
                 .Add(x => x.Unit.Formula)

+ 1 - 1
prs.classes/Entities/Job/Requisitions/JobRequisitionItem.cs

@@ -66,7 +66,7 @@ namespace Comal.Classes
 
     public interface IJobRequisitionItem : IEntity
     {
-
+        public DateTime Cancelled { get; set; }
     }
 
     [Caption("Items")]

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

@@ -41,5 +41,7 @@ namespace Comal.Classes
         [NullEditor]
         [Obsolete("Replaced with JobRequisitionItemPurchaseOrderItem")]
         public PurchaseOrderItemLink PurchaseOrderItem { get; set; }
+
+        public DateTime Cancelled { get; set; }
     }
 }

+ 3 - 0
prs.classes/Entities/Stock/StockHolding/StockHolding.cs

@@ -143,6 +143,9 @@ namespace Comal.Classes
         /// Create a new stock movement from an <see cref="IStockHolding"/>, copying across the "key" properties;
         /// that is, the job, product, style, location and dimensions.
         /// </summary>
+        /// <remarks>
+        /// Also sets the <see cref="StockMovement.Date"/> to today.
+        /// </remarks>
         /// <param name="holding"></param>
         /// <returns></returns>
         public static StockMovement CreateMovement(this IStockHolding holding)

+ 40 - 41
prs.stores/ProductStore.cs

@@ -1,54 +1,53 @@
 using Comal.Classes;
 using InABox.Core;
 
-namespace Comal.Stores
+namespace Comal.Stores;
+
+public class ProductStore : BaseProductStore<Product>
 {
-    public class ProductStore : BaseProductStore<Product>
+    protected override void AfterSave(Product entity)
     {
-        protected override void AfterSave(Product entity)
-        {
-            base.AfterSave(entity);
+        base.AfterSave(entity);
 
-            if (entity.HasOriginalValue("NettCost"))
-                UpdateProductComponentCost(entity);
-            
-            if (entity.UnitOfMeasure.HasOriginalValue(nameof(ProductDimensionUnitLink.ID)))
-                UpdateProductUOMs(entity);
-        }
+        if (entity.HasOriginalValue("NettCost"))
+            UpdateProductComponentCost(entity);
+        
+        if (entity.UnitOfMeasure.HasOriginalValue(nameof(ProductDimensionUnitLink.ID)))
+            UpdateProductUOMs(entity);
+    }
 
-        private static List<Type>? _stockentitytypes = null;
+    private static List<Type>? _stockentitytypes = null;
+    
+    private void UpdateProductUOMs(Product entity)
+    {
+        // If this is a new Product (ie original value is Guid.Empty)
+        if (entity.GetOriginalValue(x => x.ID, entity.ID) == Guid.Empty)
+            return;
         
-        private void UpdateProductUOMs(Product entity)
+        _stockentitytypes ??= CoreUtils.TypeList(x =>
+            x.IsSubclassOf(typeof(StockEntity))
+                && !x.HasAttribute<AutoEntity>()
+                && x.HasInterface<IPersistent>());
+        var _uom = Provider.Query(new Filter<ProductDimensionUnit>(x => x.ID).IsEqualTo(entity.UnitOfMeasure.ID))
+            .ToObjects<ProductDimensionUnit>().FirstOrDefault() ?? new ProductDimensionUnit();
+        foreach (var _stockentitytype in _stockentitytypes)
         {
-            // If this is a new Product (ie original value is Guid.Empty)
-            if (entity.GetOriginalValue(x => x.ID, entity.ID) == Guid.Empty)
-                return;
-            
-            _stockentitytypes ??= CoreUtils.TypeList(x =>
-                x.IsSubclassOf(typeof(StockEntity))
-                    && !x.HasAttribute<AutoEntity>()
-                    && x.HasInterface<IPersistent>());
-            var _uom = Provider.Query(new Filter<ProductDimensionUnit>(x => x.ID).IsEqualTo(entity.UnitOfMeasure.ID))
-                .ToObjects<ProductDimensionUnit>().FirstOrDefault() ?? new ProductDimensionUnit();
-            foreach (var _stockentitytype in _stockentitytypes)
-            {
-                var _children = Provider.Query(
-                    _stockentitytype,
-                    new Filter<StockEntity>(x => x.Product.ID).IsEqualTo(entity.ID),
-                    Columns.None<StockEntity>()
-                        .Add(x => x.ID)
-                        .AddSubColumns(x => x.Dimensions, null)
-                ).ToArray(_stockentitytype).Cast<StockEntity>().ToArray();
-                foreach (var _child in _children)
-                    _child.Dimensions.Unit.CopyFrom(_uom);
-                Provider.Save(_stockentitytype, _children.Where(x => x.IsChanged()));
-            }
+            var _children = Provider.Query(
+                _stockentitytype,
+                new Filter<StockEntity>(x => x.Product.ID).IsEqualTo(entity.ID),
+                Columns.None<StockEntity>()
+                    .Add(x => x.ID)
+                    .AddSubColumns(x => x.Dimensions, null)
+            ).ToArray(_stockentitytype).Cast<StockEntity>().ToArray();
+            foreach (var _child in _children)
+                _child.Dimensions.Unit.CopyFrom(_uom);
+            Provider.Save(_stockentitytype, _children.Where(x => x.IsChanged()));
         }
+    }
 
-        protected override void AfterDelete(Product entity)
-        {
-            base.AfterDelete(entity);
-            UpdateProductComponentCost(entity);
-        }
+    protected override void AfterDelete(Product entity)
+    {
+        base.AfterDelete(entity);
+        UpdateProductComponentCost(entity);
     }
 }

+ 69 - 104
prs.stores/PurchaseOrderItemStore.cs

@@ -84,16 +84,18 @@ internal class PurchaseOrderItemStore : BaseStore<PurchaseOrderItem>
         var locationid = entity.StockLocation.ID;
         var locationValid = entity.StockLocation.IsValid();
 
-        var jriTask = Task<Guid>.Run(() =>
+        var poiaTask = Task.Run(() =>
         {
             return Provider.Query(
                 new Filter<PurchaseOrderItemAllocation>(x => x.Item.ID).IsEqualTo(entity.ID),
-                Columns.Required<PurchaseOrderItemAllocation>()
-                )
-            .ToObjects<JobRequisitionItem>();
+                Columns.None<PurchaseOrderItemAllocation>()
+                    .Add(x => x.ID)
+                    .Add(x => x.JobRequisitionItem.ID)
+                    .Add(x => x.JobRequisitionItem.Cancelled))
+            .ToArray<PurchaseOrderItemAllocation>();
         });
 
-        var consigntask = Task<double>.Run(() =>
+        var consigntask = Task.Run(() =>
         {
             if (entity.Consignment.ID != Guid.Empty && !entity.Consignment.ExTax.IsEffectivelyEqual(0.0))
             {
@@ -101,102 +103,56 @@ internal class PurchaseOrderItemStore : BaseStore<PurchaseOrderItem>
                     new Filter<PurchaseOrderItem>(x => x.Consignment.ID).IsEqualTo(entity.Consignment.ID),
                     Columns.None<PurchaseOrderItem>().Add(x => x.ExTax)
                 ).Rows.Select(r => r.Get<PurchaseOrderItem, double>(c => c.ExTax));
-                return values.Sum(x => x);
+                return values.Sum();
+            }
+            else
+            {
+                return 0.0;
             }
-
-            return 0.0;
         });
         
-        var instancetask = new Task<CoreRow?>(() =>
+        var instancetask = Task.Run(() =>
         {
             return Provider.Query(
                 new Filter<ProductInstance>(x => x.Product.ID).IsEqualTo(entity.Product.ID)
-                    .And(x=>x.Style.ID).IsEqualTo(entity.Style.ID)
+                    .And(x => x.Style.ID).IsEqualTo(entity.Style.ID)
                     .And(x => x.Dimensions).DimensionEquals(entity.Dimensions),
-                Columns.Required<ProductInstance>().Add(
-                    x => x.ID,
-                    x => x.Product.NonStock,
-                    x => x.Product.DefaultLocation.ID,
-                    x => x.Product.Warehouse.ID,
-                    x => x.Dimensions.Unit.ID,
-                    x => x.Dimensions.Unit.Formula,
-                    x => x.Dimensions.Unit.Format,
-                    x => x.Dimensions.Quantity,
-                    x => x.Dimensions.Length,
-                    x => x.Dimensions.Width,
-                    x => x.Dimensions.Height,
-                    x => x.Dimensions.Weight,
-                    x => x.Dimensions.Unit.HasHeight,
-                    x => x.Dimensions.Unit.HasLength,
-                    x => x.Dimensions.Unit.HasWidth,
-                    x => x.Dimensions.Unit.HasWeight,
-                    x => x.Dimensions.Unit.HasQuantity,
-                    x => x.Dimensions.Unit.Formula,
-                    x => x.Dimensions.Unit.Format,
-                    x => x.Dimensions.Unit.Code,
-                    x => x.Dimensions.Unit.Description,
-                    
-                    x => x.FreeStock,
-                    x => x.AverageCost,
-                    x => x.LastCost
-                )
+                Columns.Required<ProductInstance>()
+                    .Add(x => x.ID)
+                    .Add(x => x.FreeStock)
+                    .Add(x => x.AverageCost)
+                    .Add(x => x.LastCost)
             ).Rows.FirstOrDefault();
         });
-        instancetask.Start();
         
-        var producttask = new Task<CoreRow?>(() =>
-        {
-            return Provider.Query(
+        var producttask = Task.Run(
+            () => Provider.Query(
                 new Filter<Product>(x => x.ID).IsEqualTo(entity.Product.ID),
-                Columns.None<Product>().Add(
-                    x => x.ID,
-                    x => x.DefaultLocation.ID,
-                    x => x.Warehouse.ID,
-                    x => x.DefaultInstance.Dimensions.Unit.ID,
-                    x => x.DefaultInstance.Dimensions.Unit.Formula,
-                    x => x.DefaultInstance.Dimensions.Unit.Format,
-                    x => x.DefaultInstance.Dimensions.Quantity,
-                    x => x.DefaultInstance.Dimensions.Length,
-                    x => x.DefaultInstance.Dimensions.Width,
-                    x => x.DefaultInstance.Dimensions.Height,
-                    x => x.DefaultInstance.Dimensions.Weight,
-                    x => x.NonStock,
-                    x => x.DefaultInstance.Dimensions.Unit.HasHeight,
-                    x => x.DefaultInstance.Dimensions.Unit.HasLength,
-                    x => x.DefaultInstance.Dimensions.Unit.HasWidth,
-                    x => x.DefaultInstance.Dimensions.Unit.HasWeight,
-                    x => x.DefaultInstance.Dimensions.Unit.HasQuantity,
-                    x => x.DefaultInstance.Dimensions.Unit.Formula,
-                    x => x.DefaultInstance.Dimensions.Unit.Format,
-                    x => x.DefaultInstance.Dimensions.Unit.Code,
-                    x => x.DefaultInstance.Dimensions.Unit.Description
-                )
-            ).Rows.FirstOrDefault();
-        });
-        producttask.Start();
-
-        var locationtask = new Task<CoreTable>(() =>
-        {
-            return Provider.Query(
+                Columns.None<Product>()
+                    .Add(x => x.ID)
+                    .Add(x => x.DefaultLocation.ID)
+                    .Add(x => x.Warehouse.ID)
+                    .AddDimensionsColumns(x => x.DefaultInstance.Dimensions, Dimensions.ColumnsType.All)
+                    .Add(x => x.NonStock))
+            .Rows.FirstOrDefault());
+
+        var locationtask = Task.Run(
+            () => Provider.Query(
                 new Filter<StockLocation>(x => x.Default).IsEqualTo(true),
-                Columns.None<StockLocation>().Add(x => x.ID, x => x.Warehouse.ID, x => x.Warehouse.Default)
-            );
-        });
-        locationtask.Start();
+                Columns.None<StockLocation>().Add(x => x.ID, x => x.Warehouse.ID, x => x.Warehouse.Default)));
 
-        Task.WaitAll(producttask, locationtask, instancetask, jriTask, consigntask);
+        Task.WaitAll(producttask, locationtask, instancetask, poiaTask, consigntask);
 
         var instancerow = instancetask.Result;
         var productrow = producttask.Result;
         var defaultlocations = locationtask.Result;
-        var jris = jriTask.Result.ToArray();
+        var allocations = poiaTask.Result.ToArray();
         
         if (productrow is null)
         {
             Logger.Send(LogType.Information, UserID, "Cannot Find PurchaseOrderItem.Product.ID!");
             return;
         }
-        
         if (productrow.Get<Product, bool>(x => x.NonStock))
         {
             Logger.Send(LogType.Information, UserID, "PurchaseOrderItem.Product is marked as Non Stock!");
@@ -241,13 +197,11 @@ internal class PurchaseOrderItemStore : BaseStore<PurchaseOrderItem>
             }
         }
         
-        if (
-            (entity.Dimensions.Unit.ID == Guid.Empty)
-            && (entity.Dimensions.Height == 0)
-            && (entity.Dimensions.Width == 0)
-            && (entity.Dimensions.Length == 0)
-            && (entity.Dimensions.Weight == 0)
-            )
+        if (entity.Dimensions.Unit.ID == Guid.Empty
+            && entity.Dimensions.Height == 0
+            && entity.Dimensions.Width == 0
+            && entity.Dimensions.Length == 0
+            && entity.Dimensions.Weight == 0)
         {
             Logger.Send(LogType.Information, UserID, "PurchaseOrderItem.Unit Size is zero!");
             entity.Dimensions.CopyFrom(productrow.ToObject<Product>().DefaultInstance.Dimensions);
@@ -267,8 +221,6 @@ internal class PurchaseOrderItemStore : BaseStore<PurchaseOrderItem>
             }
 
             instance.LastCost = entity.Cost;
-                
-            //var product = productrow.ToObject<Product>();
 
             var freeqty = instance.FreeStock;
             var freeavg = instance.AverageCost;
@@ -297,6 +249,8 @@ internal class PurchaseOrderItemStore : BaseStore<PurchaseOrderItem>
             }
         }
 
+        // Actual logic begins here.
+
         var batch = new StockMovementBatch
         {
             Type = StockMovementBatchType.Receipt,
@@ -313,22 +267,28 @@ internal class PurchaseOrderItemStore : BaseStore<PurchaseOrderItem>
             poCost += entity.Cost * entity.Consignment.ExTax / consigntask.Result;
         }        
         
-        foreach (var jri in jris)
+        foreach (var poia in allocations)
         {
-            // Going through each jri, make sure we don't allocate more than the po line allows
-            var jriQty = Math.Min(jri.Qty, _pototal);
-            // And reduce the po balance by the jri Allocation
-            _pototal -= jriQty;
-            
-            // Let's not make zero-quantity transactions
-            if (!jriQty.IsEffectivelyEqual(0.0))
-                CreateMovement(entity, locationid, movements, jri, jriQty, poCost);
+            var jri = poia.JobRequisitionItem.ID == Guid.Empty ? null : poia.JobRequisitionItem;
+            CreateMovement(entity, locationid, movements, poia.Job, jri, poia.Quantity, poCost);
+
+            //CreateMovement(entity, locationid, movements, poia.Job, poia.JobRequisitionItem, poia.Quantity, poCost);
+
+            //// Going through each jri, make sure we don't allocate more than the po line allows
+            //var jriQty = Math.Min(jri.Qty, _pototal);
+            //// And reduce the po balance by the jri Allocation
+            //_pototal -= jriQty;
+            //
+            //// Let's not make zero-quantity transactions
+            //if (!jriQty.IsEffectivelyEqual(0.0))
+            //    CreateMovement(entity, locationid, movements, jri, jriQty, poCost);
         }
+
+        var totalAllocations = allocations.Sum(x => x.Quantity);
         
-        // If there is any left over (ie over-ordered), now we can create a
-        // second transaction to receive the unallocated stock
-        if (!_pototal.IsEffectivelyEqual(0.0F))
-            CreateMovement(entity, locationid, movements, null, _pototal, poCost);
+        // If there is any left over (ie over/under-ordered), now we can create
+        // a second transaction to receive the unallocated stock
+        CreateMovement(entity, locationid, movements, null, null, entity.Qty - totalAllocations, poCost);
 
         FindSubStore<StockMovementBatch>().Save(batch, "Received on PO");
         foreach(var mvt in movements)
@@ -340,11 +300,16 @@ internal class PurchaseOrderItemStore : BaseStore<PurchaseOrderItem>
         entity.CancelChanges();
     }
 
-    private static void CreateMovement(PurchaseOrderItem entity, Guid locationid, List<StockMovement> movements, JobRequisitionItem jri, double qty, double cost)
+    private static void CreateMovement(PurchaseOrderItem entity, Guid locationid, List<StockMovement> movements, IJob? job, IJobRequisitionItem? jri, double qty, double cost)
     {
+        if (qty.IsEffectivelyEqual(0.0)) return;
+
         var movement = new StockMovement();
         movement.Product.ID = entity.Product.ID;
-        movement.Job.ID = entity.Job.ID;
+        if(job is not null)
+        {
+            movement.Job.ID = job.ID;
+        }
         movement.Location.ID = locationid;
         movement.Style.ID = entity.Style.ID;
         movement.Dimensions.CopyFrom(entity.Dimensions);
@@ -368,7 +333,7 @@ internal class PurchaseOrderItemStore : BaseStore<PurchaseOrderItem>
                 var tOut = movement.CreateMovement();
                 tOut.JobRequisitionItem.ID = jri.ID;
                 tOut.Date = entity.ReceivedDate;
-                tOut.Issued = jri.Qty;
+                tOut.Issued = qty;
                 tOut.OrderItem.ID = entity.ID;
                 tOut.Notes = "Internal transfer from cancelled requisition";
                 tOut.System = true;
@@ -378,7 +343,7 @@ internal class PurchaseOrderItemStore : BaseStore<PurchaseOrderItem>
                 var tIn = movement.CreateMovement();
                 tIn.Transaction = tOut.Transaction;
                 tIn.Date = entity.ReceivedDate;
-                tIn.Received = jri.Qty;
+                tIn.Received = qty;
                 tIn.OrderItem.ID = entity.ID;
                 tOut.Notes = "Internal transfer from cancelled requisition";
                 tOut.System = true;