瀏覽代碼

Fixed issues in stock movement timberline poster

Kenric Nugteren 1 年之前
父節點
當前提交
9b9fce6b11
共有 2 個文件被更改,包括 333 次插入372 次删除
  1. 314 358
      prs.shared/Posters/Timberline/StockMovementTimberlinePoster.cs
  2. 19 14
      prs.stores/RequisitionStore.cs

+ 314 - 358
prs.shared/Posters/Timberline/StockMovementTimberlinePoster.cs

@@ -18,174 +18,172 @@ using Microsoft.Win32;
 using CsvHelper.TypeConversion;
 using NPOI.SS.Formula.Functions;
 
-namespace PRS.Shared
-{
-    public interface IStockMovementTimberlineLine
-    {
-        StockMovementBatchType BatchType { get; set; }
+namespace PRS.Shared;
 
-        DateTime TransactionDate { get; set; }
+public interface IStockMovementTimberlineLine
+{
+    DateTime TransactionDate { get; set; }
 
-        DateTime AccountingDate { get; set; }
+    DateTime AccountingDate { get; set; }
 
-        string Description { get; set; }
+    string Description { get; set; }
 
-        double Amount { get; set; }
+    double Amount { get; set; }
 
-        string DebitAccount { get; set; }
+    string DebitAccount { get; set; }
 
-        string CreditAccount { get; set; }
+    string CreditAccount { get; set; }
 
-        string Reference1 { get; set; }
+    string Reference1 { get; set; }
 
-        string Reference2 { get; set; }
-    }
+    string Reference2 { get; set; }
+}
 
-    public enum StockMovementTimberlineTransactionType
-    {
-        APCost = 1,
-        JCCost = 2,
-        PRCost = 3,
-        EQCost = 4,
-        IVCost = 5
-    }
+public enum StockMovementTimberlineTransactionType
+{
+    APCost = 1,
+    JCCost = 2,
+    PRCost = 3,
+    EQCost = 4,
+    IVCost = 5
+}
 
-    public class StockMovementTimberlineTransactionTypeConverter : DefaultTypeConverter
+public class StockMovementTimberlineTransactionTypeConverter : DefaultTypeConverter
+{
+    public override object? ConvertFromString(string? text, IReaderRow row, MemberMapData memberMapData)
     {
-        public override object? ConvertFromString(string? text, IReaderRow row, MemberMapData memberMapData)
+        if (Enum.TryParse<StockMovementTimberlineTransactionType>(text, out var type))
         {
-            if (Enum.TryParse<StockMovementTimberlineTransactionType>(text, out var type))
-            {
-                return type;
-            }
-            return base.ConvertFromString(text, row, memberMapData);
+            return type;
         }
+        return base.ConvertFromString(text, row, memberMapData);
+    }
 
-        public override string? ConvertToString(object? value, IWriterRow row, MemberMapData memberMapData)
+    public override string? ConvertToString(object? value, IWriterRow row, MemberMapData memberMapData)
+    {
+        if (value is StockMovementTimberlineTransactionType type)
         {
-            if (value is StockMovementTimberlineTransactionType type)
-            {
-                return ((int)type).ToString();
-            }
-            return "";
+            return ((int)type).ToString();
         }
+        return "";
     }
+}
 
-    public class StockMovementTimberlineDirectCost : IStockMovementTimberlineLine
-    {
-        [Ignore]
-        public StockMovementBatchType BatchType { get; set; }
+public class StockMovementTimberlineDirectCost : IStockMovementTimberlineLine
+{
+    [Ignore]
+    public StockMovementBatchType BatchType { get; set; }
 
-        [Index(0)]
-        public string RecordID { get; set; } = "DC";
+    [Index(0)]
+    public string RecordID { get; set; } = "DC";
 
-        [Index(1)]
-        [TypeConverter(typeof(TimberlinePosterStringConverter), 10)]
-        public string Job { get; set; }
+    [Index(1)]
+    [TypeConverter(typeof(TimberlinePosterStringConverter), 10)]
+    public string Job { get; set; }
 
-        [Index(2)]
-        [TypeConverter(typeof(TimberlinePosterStringConverter), 10)]
-        public string Extra { get; set; }
+    [Index(2)]
+    [TypeConverter(typeof(TimberlinePosterStringConverter), 10)]
+    public string Extra { get; set; }
 
-        [Index(3)]
-        [TypeConverter(typeof(TimberlinePosterStringConverter), 12)]
-        public string CostCode { get; set; }
+    [Index(3)]
+    [TypeConverter(typeof(TimberlinePosterStringConverter), 12)]
+    public string CostCode { get; set; }
 
-        [Index(4)]
-        [TypeConverter(typeof(TimberlinePosterStringConverter), 3)]
-        public string Category { get; set; }
+    [Index(4)]
+    [TypeConverter(typeof(TimberlinePosterStringConverter), 3)]
+    public string Category { get; set; }
 
-        [Index(5)]
-        [TypeConverter(typeof(StockMovementTimberlineTransactionTypeConverter))]
-        public StockMovementTimberlineTransactionType TransactionType { get; set; }
+    [Index(5)]
+    [TypeConverter(typeof(StockMovementTimberlineTransactionTypeConverter))]
+    public StockMovementTimberlineTransactionType TransactionType { get; set; }
 
-        [Index(6)]
-        [TypeConverter(typeof(TimberlinePosterDateConverter))]
-        public DateTime TransactionDate { get; set; }
+    [Index(6)]
+    [TypeConverter(typeof(TimberlinePosterDateConverter))]
+    public DateTime TransactionDate { get; set; }
 
-        [Index(7)]
-        [TypeConverter(typeof(TimberlinePosterDateConverter))]
-        public DateTime AccountingDate { get; set; }
+    [Index(7)]
+    [TypeConverter(typeof(TimberlinePosterDateConverter))]
+    public DateTime AccountingDate { get; set; }
 
-        [Index(8)]
-        [TypeConverter(typeof(TimberlinePosterStringConverter), 30)]
-        public string Description { get; set; }
+    [Index(8)]
+    [TypeConverter(typeof(TimberlinePosterStringConverter), 30)]
+    public string Description { get; set; }
 
-        [Index(9)]
-        public double Units { get; set; }
+    [Index(9)]
+    public double Units { get; set; }
 
-        [Index(10)]
-        public double UnitCost { get; set; }
+    [Index(10)]
+    public double UnitCost { get; set; }
 
-        [Index(11)]
-        public double Amount { get; set; }
+    [Index(11)]
+    public double Amount { get; set; }
 
-        [Index(12)]
-        [TypeConverter(typeof(TimberlinePosterStringConverter), 25)]
-        public string DebitAccount { get; set; }
+    [Index(12)]
+    [TypeConverter(typeof(TimberlinePosterStringConverter), 25)]
+    public string DebitAccount { get; set; }
 
-        [Index(13)]
-        [TypeConverter(typeof(TimberlinePosterStringConverter), 25)]
-        public string CreditAccount { get; set; }
+    [Index(13)]
+    [TypeConverter(typeof(TimberlinePosterStringConverter), 25)]
+    public string CreditAccount { get; set; }
 
-        [Index(14)]
-        [TypeConverter(typeof(TimberlinePosterStringConverter), 10)]
-        public string Reference1 { get; set; }
+    [Index(14)]
+    [TypeConverter(typeof(TimberlinePosterStringConverter), 10)]
+    public string Reference1 { get; set; }
 
-        [Index(15)]
-        [TypeConverter(typeof(TimberlinePosterStringConverter), 10)]
-        public string Reference2 { get; set; }
+    [Index(15)]
+    [TypeConverter(typeof(TimberlinePosterStringConverter), 10)]
+    public string Reference2 { get; set; }
 
-        [Index(16)]
-        [TypeConverter(typeof(TimberlinePosterStringConverter), 10)]
-        public string StandardItem { get; set; }
-    }
+    [Index(16)]
+    [TypeConverter(typeof(TimberlinePosterStringConverter), 10)]
+    public string StandardItem { get; set; }
+}
 
-    public class StockMovementTimberlineGL : IStockMovementTimberlineLine
-    {
-        [Ignore]
-        public StockMovementBatchType BatchType { get; set; }
+public class StockMovementTimberlineGL : IStockMovementTimberlineLine
+{
+    [Ignore]
+    public StockMovementBatchType BatchType { get; set; }
 
-        [Index(0)]
-        public string RecordID { get; set; } = "GL";
+    [Index(0)]
+    public string RecordID { get; set; } = "GL";
 
-        [Index(1)]
-        [TypeConverter(typeof(TimberlinePosterDateConverter))]
-        public DateTime TransactionDate { get; set; }
+    [Index(1)]
+    [TypeConverter(typeof(TimberlinePosterDateConverter))]
+    public DateTime TransactionDate { get; set; }
 
-        [Index(2)]
-        [TypeConverter(typeof(TimberlinePosterDateConverter))]
-        public DateTime AccountingDate { get; set; }
+    [Index(2)]
+    [TypeConverter(typeof(TimberlinePosterDateConverter))]
+    public DateTime AccountingDate { get; set; }
 
-        [Index(3)]
-        [TypeConverter(typeof(TimberlinePosterStringConverter), 30)]
-        public string Description { get; set; }
+    [Index(3)]
+    [TypeConverter(typeof(TimberlinePosterStringConverter), 30)]
+    public string Description { get; set; }
 
-        [Index(4)]
-        public double Amount { get; set; }
+    [Index(4)]
+    public double Amount { get; set; }
 
-        [Index(5)]
-        [TypeConverter(typeof(TimberlinePosterStringConverter), 25)]
-        public string DebitAccount { get; set; }
+    [Index(5)]
+    [TypeConverter(typeof(TimberlinePosterStringConverter), 25)]
+    public string DebitAccount { get; set; }
 
-        [Index(6)]
-        [TypeConverter(typeof(TimberlinePosterStringConverter), 25)]
-        public string CreditAccount { get; set; }
+    [Index(6)]
+    [TypeConverter(typeof(TimberlinePosterStringConverter), 25)]
+    public string CreditAccount { get; set; }
 
-        [Index(7)]
-        [TypeConverter(typeof(TimberlinePosterStringConverter), 10)]
-        public string Reference1 { get; set; }
+    [Index(7)]
+    [TypeConverter(typeof(TimberlinePosterStringConverter), 10)]
+    public string Reference1 { get; set; }
 
-        [Index(8)]
-        [TypeConverter(typeof(TimberlinePosterStringConverter), 10)]
-        public string Reference2 { get; set; }
-    }
+    [Index(8)]
+    [TypeConverter(typeof(TimberlinePosterStringConverter), 10)]
+    public string Reference2 { get; set; }
+}
 
-    public class StockMovementTimberlineSettings : TimberlinePosterSettings<StockMovement>
+public class StockMovementTimberlineSettings : TimberlinePosterSettings<StockMovement>
+{
+    protected override string DefaultScript()
     {
-        protected override string DefaultScript()
-        {
-            return @"
+        return @"
 using PRS.Shared;
 using InABox.Core;
 using System.Collections.Generic;
@@ -197,13 +195,13 @@ public class Module
         // Perform pre-processing
     }
 
-    public void ProcessDirectCostLine(IDataModel<StockMovement> model, StockMovement stockMovement, StockMovementTimberlineDirectCost line)
+    public bool ProcessDirectCostLine(IDataModel<StockMovement> model, StockMovement stockMovement, StockMovementTimberlineDirectCost line)
     {
         // Do extra processing for a direct cost line; return false to fail this movement
         return true;
     }
 
-    public void ProcessGLLine(IDataModel<StockMovement> model, StockMovement stockMovement, StockMovementTimberlineGL line)
+    public bool ProcessGLLine(IDataModel<StockMovement> model, StockMovement stockMovement, StockMovementTimberlineGL line)
     {
         // Do extra processing for a GL line; return false to fail this movement
         return true;
@@ -214,301 +212,259 @@ public class Module
         // Perform post-processing
     }
 }";
-        }
+    }
+}
+
+public class StockMovementTimberlineResult : TimberlinePostResult<IStockMovementTimberlineLine, StockMovement>
+{
+}
+
+public class StockMovementTimberlinePoster : ITimberlinePoster<StockMovement, StockMovementTimberlineSettings>
+{
+    public ScriptDocument? Script { get; set; }
+    public StockMovementTimberlineSettings Settings { get; set; }
+
+    public bool BeforePost(IDataModel<StockMovement> model)
+    {
+        model.SetIsDefault<Document>(false, alias: "CompanyLogo");
+        model.SetIsDefault<CoreTable>(false, alias: "CompanyInformation");
+        model.SetIsDefault<Employee>(false);
+
+        model.SetColumns(new Columns<StockMovement>(x => x.ID).Add(x => x.Transaction));
+
+        model.AddChildTable<StockMovement, StockMovement>(x => x.Transaction, x => x.Transaction,
+            parentalias: "StockMovement", childalias: "FullTransactions",
+            isdefault: true,
+            columns: new Columns<StockMovement>(x => x.ID)
+                .Add(x => x.Transaction)
+                .Add(x => x.Job.ID)
+                .Add(x => x.Product.ID)
+                .Add(x => x.Type)
+                .Add(x => x.Units)
+                .Add(x => x.Cost)
+                .Add(x => x.Value)
+                .Add(x => x.Date)
+                .Add(x => x.Batch.ID)
+                );
+
+        model.AddLookupTable<StockMovement, Product>(x => x.Product.ID, x => x.ID, sourcealias: "FullTransactions",
+            isdefault: true,
+            columns: new Columns<Product>(x => x.ID)
+                .Add(x => x.Name)
+                .Add(x => x.CostCentre.Code)
+                .Add(x => x.PurchaseGL.Code));
+
+        model.AddLookupTable<StockMovement, Job>(x => x.Job.ID, x => x.ID, sourcealias: "FullTransactions",
+            isdefault: true,
+            columns: new Columns<Job>(x => x.ID)
+                .Add(x => x.JobNumber));
+
+        Script?.Execute(methodname: "BeforePost", parameters: new object[] { model });
+        return true;
     }
 
-    public class StockMovementTimberlineResult : TimberlinePostResult<IStockMovementTimberlineLine, StockMovement>
+    private bool ProcessDirectCostLine(IDataModel<StockMovement> model, StockMovement stockMovement, StockMovementTimberlineDirectCost line)
     {
+        return Script?.Execute(methodname: "ProcessDirectCostLine", parameters: new object[] { model, stockMovement, line }) != false;
+    }
+    private bool ProcessGLLine(IDataModel<StockMovement> model, StockMovement stockMovement, StockMovementTimberlineGL line)
+    {
+        return Script?.Execute(methodname: "ProcessGLLine", parameters: new object[] { model, stockMovement, line }) != false;
     }
 
-    public class StockMovementTimberlinePoster : ITimberlinePoster<StockMovement, StockMovementTimberlineSettings>
+    private StockMovementTimberlineResult DoProcess(IDataModel<StockMovement> model)
     {
-        public ScriptDocument? Script { get; set; }
-        public StockMovementTimberlineSettings Settings { get; set; }
+        var result = new StockMovementTimberlineResult();
 
-        public bool BeforePost(IDataModel<StockMovement> model)
-        {
-            model.SetIsDefault<Document>(false, alias: "CompanyLogo");
-            model.SetIsDefault<CoreTable>(false, alias: "CompanyInformation");
-            model.SetIsDefault<Employee>(false);
-
-            model.SetColumns(new Columns<StockMovement>(x => x.ID).Add(x => x.Transaction));
-
-            model.AddChildTable<StockMovement, StockMovement>(x => x.Transaction, x => x.Transaction,
-                parentalias: "StockMovement", childalias: "FullTransactions",
-                isdefault: true,
-                columns: new Columns<StockMovement>(x => x.ID)
-                    .Add(x => x.Transaction)
-                    .Add(x => x.Job.ID)
-                    .Add(x => x.Product.ID)
-                    .Add(x => x.Type)
-                    .Add(x => x.Units)
-                    .Add(x => x.Cost)
-                    .Add(x => x.Value)
-                    .Add(x => x.Date)
-                    .Add(x => x.Batch.ID)
-                    );
-
-            model.AddLookupTable<StockMovement, Product>(x => x.Product.ID, x => x.ID, sourcealias: "FullTransactions",
-                isdefault: true,
-                columns: new Columns<Product>(x => x.ID)
-                    .Add(x => x.Name)
-                    .Add(x => x.CostCentre.Code)
-                    .Add(x => x.PurchaseGL.Code));
-
-            model.AddLookupTable<StockMovement, Job>(x => x.Job.ID, x => x.ID, sourcealias: "FullTransactions",
-                isdefault: true,
-                columns: new Columns<Job>(x => x.ID)
-                    .Add(x => x.JobNumber));
-
-            model.AddLookupTable<StockMovement, StockMovementBatch>(x => x.Batch.ID, x => x.ID, sourcealias: "FullTransactions",
-                isdefault: true,
-                columns: new Columns<StockMovementBatch>(x => x.ID).Add(x => x.Type));
-
-            Script?.Execute(methodname: "BeforePost", parameters: new object[] { model });
-            return true;
-        }
+        var firstMovements = model.GetTable<StockMovement>();
+        var full = model.GetTable<StockMovement>("FullTransactions")
+            .ToObjects<StockMovement>().GroupBy(x => x.Transaction);
+        var products = model.GetTable<Product>().ToObjects<Product>()
+            .ToDictionary(x => x.ID, x => x);
+        var jobs = model.GetTable<Job>().ToObjects<Job>()
+            .ToDictionary(x => x.ID, x => x);
 
-        private bool ProcessDirectCostLine(IDataModel<StockMovement> model, StockMovement stockMovement, StockMovementTimberlineDirectCost line)
+        StockMovementTimberlineDirectCost CreateDirectCost(StockMovement movement)
         {
-            return Script?.Execute(methodname: "ProcessDirectCostLine", parameters: new object[] { model, stockMovement, line }) != false;
+            var job = jobs[movement.Job.ID];
+            var product = products[movement.Product.ID];
+
+            var directCost = new StockMovementTimberlineDirectCost
+            {
+                Job = job.JobNumber,
+                Extra = "",
+                CostCode = product.CostCentre.Code,
+                Category = "",
+                Units = movement.Units,
+                UnitCost = movement.Cost,
+                TransactionType = StockMovementTimberlineTransactionType.IVCost
+            };
+            return ModifyLine(directCost, movement);
         }
-        private bool ProcessGLLine(IDataModel<StockMovement> model, StockMovement stockMovement, StockMovementTimberlineGL line)
+        T ModifyLine<T>(T line, StockMovement movement)
+            where T : IStockMovementTimberlineLine
         {
-            return Script?.Execute(methodname: "ProcessGLLine", parameters: new object[] { model, stockMovement, line }) != false;
+            var product = products[movement.Product.ID];
+
+            line.TransactionDate = movement.Date;
+            line.AccountingDate = movement.Date;
+            line.Description = product.Name;
+            line.Amount = movement.Value;
+            line.CreditAccount = product.PurchaseGL.Code;
+            return line;
         }
 
-        private StockMovementTimberlineResult DoProcess(IDataModel<StockMovement> model)
+        foreach (var transaction in full)
         {
-            var result = new StockMovementTimberlineResult();
-
-            var firstMovements = model.GetTable<StockMovement>();
-            var full = model.GetTable<StockMovement>("FullTransactions")
-                .ToObjects<StockMovement>().GroupBy(x => x.Transaction);
-            var products = model.GetTable<Product>().ToObjects<Product>()
-                .ToDictionary(x => x.ID, x => x);
-            var jobs = model.GetTable<Job>().ToObjects<Job>()
-                .ToDictionary(x => x.ID, x => x);
-            var batches = model.GetTable<StockMovementBatch>().ToObjects<StockMovementBatch>()
-                .ToDictionary(x => x.ID, x => x);
-
-            StockMovementTimberlineDirectCost CreateDirectCost(StockMovement movement)
+            var movements = new List<StockMovement>();
+            foreach(var movement in transaction)
             {
-                var job = jobs[movement.Job.ID];
-                var product = products[movement.Product.ID];
-
-                var directCost = new StockMovementTimberlineDirectCost
+                if (movement.Type == StockMovementType.Issue || movement.Type == StockMovementType.Receive)
                 {
-                    Job = job.JobNumber,
-                    Extra = "",
-                    CostCode = product.CostCentre.Code,
-                    Category = "",
-                    Units = movement.Units,
-                    UnitCost = movement.Cost,
-                    TransactionType = StockMovementTimberlineTransactionType.IVCost
-                };
-                return ModifyLine(directCost, movement);
-            }
-            T ModifyLine<T>(T line, StockMovement movement)
-                where T : IStockMovementTimberlineLine
-            {
-                var product = products[movement.Product.ID];
-                var batch = batches.GetValueOrDefault(movement.Batch.ID);
-
-                line.TransactionDate = movement.Date;
-                line.AccountingDate = movement.Date;
-                line.Description = product.Name;
-                line.Amount = movement.Value;
-                line.CreditAccount = product.PurchaseGL.Code;
-                line.BatchType = batch?.Type ?? StockMovementBatchType.Transfer;
-                return line;
+                    // Ignore these ones.
+                    result.AddSuccess(movement, null);
+                }
+                else
+                {
+                    // So we only care about transfers and stocktakes
+                    movements.Add(movement);
+                }
             }
 
-            foreach (var transaction in full)
+            if(movements.Count == 1)
             {
-                var movements = new List<StockMovement>();
-                foreach(var movement in transaction)
-                {
-                    if (movement.Type != StockMovementType.TransferOut && movement.Type != StockMovementType.TransferIn)
-                    {
-                        // Ignore these ones.
-                        result.AddSuccess(movement, null);
-                    }
-                    else
-                    {
-                        movements.Add(movement);
-                    }
-                }
+                var mvt = movements[0];
 
-                if(movements.Count == 1)
+                if(mvt.Type == StockMovementType.StockTake)
                 {
-                    var mvt = movements[0];
-                    var batch = batches.GetValueOrDefault(mvt.Batch.ID);
-                    if(batch is null)
+                    if(mvt.Job.ID == Guid.Empty)
                     {
-                        // What to do?
+                        var gl = new StockMovementTimberlineGL { };
+                        gl = ModifyLine(gl, mvt);
+                        if (ProcessGLLine(model, mvt, gl))
+                        {
+                            result.AddSuccess(mvt, gl);
+                        }
+                        else
+                        {
+                            result.AddFailed(mvt, "Failed by script.");
+                        }
                     }
-
-                    if(batch?.Type == StockMovementBatchType.Stocktake)
+                    else
                     {
-                        if(mvt.Job.ID == Guid.Empty)
+                        var dc = CreateDirectCost(mvt);
+                        if (ProcessDirectCostLine(model, mvt, dc))
                         {
-                            var gl = new StockMovementTimberlineGL { };
-                            gl = ModifyLine(gl, mvt);
-                            if (ProcessGLLine(model, mvt, gl))
-                            {
-                                result.AddSuccess(mvt, gl);
-                            }
-                            else
-                            {
-                                result.AddFailed(mvt, "Failed by script.");
-                            }
+                            result.AddSuccess(mvt, dc);
                         }
                         else
                         {
-                            var dc = CreateDirectCost(mvt);
-                            if (ProcessDirectCostLine(model, mvt, dc))
-                            {
-                                result.AddSuccess(mvt, dc);
-                            }
-                            else
-                            {
-                                result.AddFailed(mvt, "Failed by script.");
-                            }
+                            result.AddFailed(mvt, "Failed by script.");
                         }
                     }
+                }
+                else
+                {
+                    result.AddSuccess(mvt, null);
+                }
+            }
+            else if(movements.Count == 2)
+            {
+                var mvtFrom = movements[0];
+                var mvtTo = movements[1];
+                if(mvtFrom.Job.ID == mvtTo.Job.ID)
+                {
+                    // Ignore these ones.
+                    result.AddSuccess(mvtFrom, null);
+                    result.AddSuccess(mvtTo, null);
+                }
+                else if(mvtFrom.Job.ID == Guid.Empty || mvtTo.Job.ID == Guid.Empty)
+                {
+                    var jobMvt = mvtFrom.Job.ID == Guid.Empty ? mvtTo : mvtFrom;
+
+                    var directCost = CreateDirectCost(jobMvt);
+                    if(ProcessDirectCostLine(model, jobMvt, directCost))
+                    {
+                        result.AddSuccess(mvtFrom, directCost);
+                        result.AddSuccess(mvtTo, directCost);
+                    }
                     else
                     {
-                        result.AddSuccess(mvt, null);
+                        result.AddFailed(mvtFrom, "Failed by script.");
+                        result.AddFailed(mvtTo, "Failed by script.");
                     }
                 }
-                else if(movements.Count == 2)
+                else
                 {
-                    var mvtFrom = movements[0];
-                    var mvtTo = movements[1];
-                    if(mvtFrom.Job.ID == mvtTo.Job.ID)
+                    var directCostFrom = CreateDirectCost(mvtFrom);
+                    var directCostTo = CreateDirectCost(mvtTo);
+
+                    if (ProcessDirectCostLine(model, mvtFrom, directCostFrom))
                     {
-                        // Ignore these ones.
-                        result.AddSuccess(mvtFrom, null);
-                        result.AddSuccess(mvtTo, null);
+                        result.AddSuccess(mvtFrom, directCostFrom);
                     }
-                    else if(mvtFrom.Job.ID == Guid.Empty || mvtTo.Job.ID == Guid.Empty)
+                    else
                     {
-                        var jobMvt = mvtFrom.Job.ID == Guid.Empty ? mvtTo : mvtFrom;
-
-                        var directCost = CreateDirectCost(jobMvt);
-                        if(ProcessDirectCostLine(model, jobMvt, directCost))
-                        {
-                            result.AddSuccess(mvtFrom, directCost);
-                            result.AddSuccess(mvtTo, directCost);
-                        }
-                        else
-                        {
-                            result.AddFailed(mvtFrom, "Failed by script.");
-                            result.AddFailed(mvtTo, "Failed by script.");
-                        }
+                        result.AddFailed(mvtFrom, "Failed by script.");
+                    }
+                    if (ProcessDirectCostLine(model, mvtTo, directCostTo))
+                    {
+                        result.AddSuccess(mvtTo, directCostTo);
                     }
                     else
                     {
-                        var directCostFrom = CreateDirectCost(mvtFrom);
-                        var directCostTo = CreateDirectCost(mvtTo);
-
-                        if (ProcessDirectCostLine(model, mvtFrom, directCostFrom))
-                        {
-                            result.AddSuccess(mvtFrom, directCostFrom);
-                        }
-                        else
-                        {
-                            result.AddFailed(mvtFrom, "Failed by script.");
-                        }
-                        if (ProcessDirectCostLine(model, mvtTo, directCostTo))
-                        {
-                            result.AddSuccess(mvtTo, directCostTo);
-                        }
-                        else
-                        {
-                            result.AddFailed(mvtTo, "Failed by script.");
-                        }
+                        result.AddFailed(mvtTo, "Failed by script.");
                     }
                 }
             }
-
-            return result;
         }
 
-        public IPostResult<StockMovement> Process(IDataModel<StockMovement> model)
-        {
-            var result = DoProcess(model);
+        return result;
+    }
 
-            var dlg = new SaveFileDialog()
-            {
-                Title = "Select Consume Direct Cost File",
-                Filter = "CSV Files (*.csv)|*.csv"
-            };
+    public IPostResult<StockMovement> Process(IDataModel<StockMovement> model)
+    {
+        var result = DoProcess(model);
 
-            if (dlg.ShowDialog() == true)
+        var dlg = new SaveFileDialog()
+        {
+            Title = "Select Output File",
+            Filter = "CSV Files (*.csv)|*.csv"
+        };
+
+        if (dlg.ShowDialog() == true)
+        {
+            using (var writer = new StreamWriter(dlg.FileName))
             {
-                using (var writer = new StreamWriter(dlg.FileName))
+                using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);
+                foreach (var line in result.Exports.Distinct())
                 {
-                    using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);
-                    foreach (var line in result.Exports.Where(x => x.BatchType != StockMovementBatchType.Stocktake))
+                    // Write the record.
+                    if(line is StockMovementTimberlineDirectCost dc)
                     {
-                        // Write the record.
-                        if(line is StockMovementTimberlineDirectCost dc)
-                        {
-                            csv.WriteRecord(dc);
-                        }
-                        else if(line is StockMovementTimberlineGL gl)
-                        {
-                            csv.WriteRecord(gl);
-                        }
-                        csv.NextRecord();
+                        csv.WriteRecord(dc);
                     }
-                }
-            }
-            else
-            {
-                throw new PostCancelledException();
-            }
-
-            dlg = new SaveFileDialog()
-            {
-                Title = "Select Adjustments File",
-                Filter = "CSV Files (*.csv)|*.csv"
-            };
-
-            if (dlg.ShowDialog() == true)
-            {
-                using (var writer = new StreamWriter(dlg.FileName))
-                {
-                    using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);
-                    foreach (var line in result.Exports.Where(x => x.BatchType == StockMovementBatchType.Stocktake))
+                    else if(line is StockMovementTimberlineGL gl)
                     {
-                        // Write the record.
-                        if (line is StockMovementTimberlineDirectCost dc)
-                        {
-                            csv.WriteRecord(dc);
-                        }
-                        else if (line is StockMovementTimberlineGL gl)
-                        {
-                            csv.WriteRecord(gl);
-                        }
+                        csv.WriteRecord(gl);
                     }
+                    csv.NextRecord();
                 }
             }
-            else
-            {
-                throw new PostCancelledException();
-            }
-            return result;
         }
-        public void AfterPost(IDataModel<StockMovement> model, IPostResult<StockMovement> result)
+        else
         {
-            Script?.Execute(methodname: "AfterPost", parameters: new object[] { model });
+            throw new PostCancelledException();
         }
-    }
 
-    public class StockMovementTimberlinePosterEngine<T> : TimberlinePosterEngine<StockMovement, StockMovementTimberlineSettings>
+        return result;
+    }
+    public void AfterPost(IDataModel<StockMovement> model, IPostResult<StockMovement> result)
     {
+        Script?.Execute(methodname: "AfterPost", parameters: new object[] { model });
     }
 }
+
+public class StockMovementTimberlinePosterEngine<T> : TimberlinePosterEngine<StockMovement, StockMovementTimberlineSettings>
+{
+}

+ 19 - 14
prs.stores/RequisitionStore.cs

@@ -223,7 +223,7 @@ namespace Comal.Stores
         #region StockMovements
 
         private StockMovement CreateStockMovement(Guid employeeid, DateTime date, Guid batchid, Guid productid, Guid locationid,
-            Guid styleid, Guid jobid, double qty, IDimensions dimensions, Guid txnid, bool system, string note)
+            Guid styleid, Guid jobid, IDimensions dimensions, Guid txnid, bool system, string note)
         {
             var movement = new StockMovement();
             movement.Batch.ID = batchid;
@@ -231,14 +231,8 @@ namespace Comal.Stores
             movement.Location.ID = locationid;
             movement.Style.ID = styleid;
             movement.Job.ID = jobid;
-            movement.Issued = qty < 0.0F ? Math.Abs(qty) : 0.0F;
-            movement.Received = qty > 0.0F ? qty : 0.0F;
-
-            movement.Type = movement.Issued > 0 ? StockMovementType.TransferOut : StockMovementType.TransferIn;
-
-            movement.Qty = qty;
-            movement.Units = qty;
             movement.Dimensions.CopyFrom(dimensions);
+
             movement.System = system;
             movement.Transaction = txnid;
             movement.Notes = note;
@@ -306,18 +300,29 @@ namespace Comal.Stores
                     // get negatives, it means probably our number are wrong I think.
 
                     // Transfer the necessary balance from General Stock...
-                    updates.Add(CreateStockMovement(entity.Employee.ID, DateTime.Now, batch.ID, productid, locationid, styleid, Guid.Empty,
-                        -extraRequired, dimensions, txnid, true, string.Format("Requisition #{0} Internal Transfer", entity.Number)));
+                    var from = CreateStockMovement(entity.Employee.ID, DateTime.Now, batch.ID, productid, locationid, styleid, Guid.Empty,
+                        dimensions, txnid, true, string.Format("Requisition #{0} Internal Transfer", entity.Number));
+                    from.Issued = extraRequired;
+                    from.Type = StockMovementType.TransferOut;
+
                     // ... to the job.
-                    updates.Add(CreateStockMovement(entity.Employee.ID, DateTime.Now, batch.ID, productid, locationid, styleid, jobid, extraRequired, dimensions, txnid, true,
-                        string.Format("Requisition #{0} Internal Transfer", entity.Number)));
+                    var to = CreateStockMovement(entity.Employee.ID, DateTime.Now, batch.ID, productid, locationid, styleid, jobid, dimensions, txnid, true,
+                        string.Format("Requisition #{0} Internal Transfer", entity.Number));
+                    from.Received = extraRequired;
+                    from.Type = StockMovementType.TransferIn;
+
+                    updates.Add(from);
+                    updates.Add(to);
 
                     // Now we have a full qty in the job holding, and we can issue to site.
                 }
 
-                updates.Add(CreateStockMovement(entity.Employee.ID, DateTime.Now, batch.ID, productid, locationid, styleid, jobid, 0.0F - qty, dimensions, txnid,
+                var mvt = CreateStockMovement(entity.Employee.ID, DateTime.Now, batch.ID, productid, locationid, styleid, jobid, dimensions, txnid,
                     false,
-                    string.Format("Requisition #{0}", entity.Number)));
+                    string.Format("Requisition #{0}", entity.Number));
+                mvt.Issued = qty;
+                mvt.Type = StockMovementType.Issue;
+                updates.Add(mvt);
             }
 
             FindSubStore<StockMovement>().Save(updates, "");