Browse Source

Merge remote-tracking branch 'origin/kenric' into frank

frogsoftware 1 year ago
parent
commit
1625bd10e7

+ 6 - 5
prs.classes/Entities/Bill/Bill.cs

@@ -21,8 +21,12 @@ namespace Comal.Classes
         
         [EditorSequence(4)]
         [DateEditor]
-        public DateTime PaymentDate { get; set; } 
-        
+        public DateTime PaymentDate { get; set; }
+
+        [EditorSequence(5)]
+        [DateEditor]
+        public DateTime AccountingDate { get; set; }
+
         [EditorSequence("Additional",1)]
         [TimestampEditor]
         public DateTime Checked { get; set; }
@@ -31,9 +35,6 @@ namespace Comal.Classes
         [TimestampEditor]
         public DateTime Approved { get; set; }
         
-        [EditorSequence("Additional",3)]
-        [DateEditor]
-        public DateTime AccountingDate { get; set; }
         
         [DoubleEditor(Editable = Editable.Hidden, Summary = Summary.Sum)]
         [Aggregate(typeof(BillExTax))]

+ 13 - 2
prs.classes/Entities/PurchaseOrder/PurchaseOrder.cs

@@ -76,7 +76,7 @@ namespace Comal.Classes
 
     [UserTracking(typeof(Bill))]
     public class PurchaseOrder : Entity, IRemotable, IPersistent, IStringAutoIncrement<PurchaseOrder>, IOneToMany<Supplier>,
-        ILicense<AccountsPayableLicense>, IScannable
+        ILicense<AccountsPayableLicense>, IScannable, IPostable
     {
         private bool bChanging;
 
@@ -149,7 +149,18 @@ namespace Comal.Classes
         [DoNotPersist]
         [DoNotSerialize]
         public static string PONumberPrefix { get; set; }
-        
+
+        [NullEditor]
+        [LoggableProperty]
+        public DateTime Posted { get; set; }
+
+        [NullEditor]
+        [LoggableProperty]
+        public PostedStatus PostedStatus { get; set; }
+
+        [NullEditor]
+        public string PostedNote { get; set; }
+
         public Expression<Func<PurchaseOrder, string>> AutoIncrementField() => x => x.PONumber;
         public Filter<PurchaseOrder> AutoIncrementFilter() => null;
         public string AutoIncrementPrefix() => PONumberPrefix;

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

@@ -5,7 +5,7 @@ namespace Comal.Classes
 {
     public class PurchaseOrderItemLink : EntityLink<PurchaseOrderItem>
     {
-        [LookupEditor(typeof(PurchaseOrderItem))]
+        [PopupEditor(typeof(PurchaseOrderItem))]
         public override Guid ID { get; set; }
 
         [TextBoxEditor(Visible = Visible.Default, Editable = Editable.Hidden)]

+ 23 - 2
prs.classes/Entities/PurchaseOrder/PurchaseOrderItemLookups.cs

@@ -1,8 +1,10 @@
 using InABox.Core;
+using System;
+using System.Linq;
 
 namespace Comal.Classes
 {
-    public class PurchaseOrderItemLookups : EntityLookup<PurchaseOrderItem>
+    public class PurchaseOrderItemLookups : EntityLookup<PurchaseOrderItem>, ILookupDefinition<PurchaseOrderItem, BillLine>
     {
         public override Columns<PurchaseOrderItem> DefineColumns()
         {
@@ -13,11 +15,30 @@ namespace Comal.Classes
             );
         }
 
-        public override Filter<PurchaseOrderItem> DefineFilter()
+        public override Filter<PurchaseOrderItem>? DefineFilter()
         {
             return null;
         }
 
+        public Filter<PurchaseOrderItem> DefineFilter(BillLine[] items)
+        {
+            if (!items.Any())
+            {
+                return new Filter<PurchaseOrderItem>().None();
+            }
+            var supplierID = items.Select(x => x.BillLink.SupplierLink.ID).Distinct().SingleOrDefault();
+            if(supplierID == Guid.Empty)
+            {
+                return new Filter<PurchaseOrderItem>().None();
+            }
+            return new Filter<PurchaseOrderItem>(x => x.PurchaseOrderLink.SupplierLink.ID).IsEqualTo(supplierID);
+        }
+
+        public Columns<BillLine> DefineFilterColumns()
+        {
+            return new Columns<BillLine>(x => x.BillLink.SupplierLink.ID);
+        }
+
         public override SortOrder<PurchaseOrderItem> DefineSortOrder()
         {
             return new SortOrder<PurchaseOrderItem>(x => x.PurchaseOrderLink.PONumber).ThenBy(x => x.Created);

+ 4 - 1
prs.desktop/Panels/Suppliers/SupplierPurchaseOrderPanel.xaml.cs

@@ -55,7 +55,10 @@ namespace PRSDesktop
 
         public void CreateToolbarButtons(IPanelHost host)
         {
-            //
+            PostUtils.CreateToolbarButtons(host,
+                () => (DataModel(Selection.Selected) as IDataModel<PurchaseOrder>)!,
+                () => Orders.Refresh(false, true),
+                true);
         }
 
         public string SectionName => "Purchase Orders";

+ 32 - 5
prs.shared/Posters/Timberline/BillTimberlinePoster.cs

@@ -236,8 +236,32 @@ public class Module
             model.SetIsDefault<CoreTable>(false, alias: "CompanyInformation");
             model.SetIsDefault<Employee>(false);
 
+            model.SetColumns<Bill>(new Columns<Bill>(x => x.ID)
+                .Add(x => x.SupplierLink.Code)
+                .Add(x => x.Number)
+                .Add(x => x.IncTax)
+                .Add(x => x.Tax)
+                .Add(x => x.BillDate)
+                .Add(x => x.AccountingDate)
+                .Add(x => x.PaymentDate));
+
+            model.SetColumns(new Columns<BillLine>(x => x.ID)
+                .Add(x => x.BillLink.ID)
+                .Add(x => x.TaxCode.Code)
+                .Add(x => x.IncTax)
+                .Add(x => x.Tax)
+                .Add(x => x.Description)
+                .Add(x => x.OrderItem.ID),
+                alias: "Bill_BillLine");
+
             model.SetIsDefault<BillLine>(true, "Bill_BillLine");
-            model.AddChildTable<BillLine, PurchaseOrderItem>(x => x.OrderItem.ID, x => x.ID, isdefault: true, parentalias: "Bill_BillLine", childalias: "POItem");
+            model.AddChildTable<BillLine, PurchaseOrderItem>(x => x.OrderItem.ID, x => x.ID, isdefault: true,
+                parentalias: "Bill_BillLine", childalias: "POItem",
+                columns: new Columns<PurchaseOrderItem>(x => x.ID)
+                    .Add(x => x.PONumber)
+                    .Add(x => x.Job.JobNumber)
+                    .Add(x => x.Qty)
+                    .Add(x => x.Cost));
 
             Script?.Execute(methodname: "BeforePost", parameters: new object[] { model });
             return true;
@@ -255,7 +279,12 @@ public class Module
         private List<BillTimberlineHeader> DoProcess(IDataModel<Bill> model)
         {
             var apifs = new List<BillTimberlineHeader>();
-            foreach(var bill in model.GetTable<Bill>().ToObjects<Bill>())
+
+            var lines = model.GetTable<BillLine>("Bill_BillLine").ToObjects<BillLine>()
+                .GroupBy(x => x.BillLink.ID).ToDictionary(x => x.Key, x => x.ToList());
+            var purchaseOrderItems = model.GetTable<PurchaseOrderItem>("POItem").ToObjects<PurchaseOrderItem>()
+                .ToDictionary(x => x.ID, x => x);
+            foreach (var bill in model.GetTable<Bill>().ToObjects<Bill>())
             {
                 var apif = new BillTimberlineHeader
                 {
@@ -282,9 +311,7 @@ public class Module
                 };
                 ProcessHeader(model, bill, apif);
 
-                var purchaseOrderItems = model.GetTable<PurchaseOrderItem>("POItem").ToObjects<PurchaseOrderItem>()
-                    .ToDictionary(x => x.ID, x => x);
-                foreach (var billLine in model.GetTable<BillLine>("Bill_BillLine").ToObjects<BillLine>())
+                foreach (var billLine in lines.GetValueOrDefault(bill.ID) ?? Enumerable.Empty<BillLine>())
                 {
                     var apdf = new BillTimberlineDistribution
                     {

+ 315 - 0
prs.shared/Posters/Timberline/PurchaseOrderTimberlinePoster.cs

@@ -0,0 +1,315 @@
+using Comal.Classes;
+using CsvHelper.Configuration.Attributes;
+using CsvHelper;
+using InABox.Core.Postable;
+using InABox.Core;
+using InABox.Poster.Timberline;
+using InABox.Scripting;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.Win32;
+
+namespace PRS.Shared
+{
+    public class PurchaseOrderTimberlineHeader
+    {
+        [Ignore]
+        public List<PurchaseOrderTimberlineLine> Lines { get; set; } = new();
+
+        [Index(1)]
+        public string RecordID { get; set; } = "APIF";
+
+        [Index(2)]
+        public string Vendor { get; set; }
+
+        [Index(3)]
+        public string Invoice { get; set; }
+
+        [Index(4)]
+        public string Description { get; set; }
+
+        [Index(5)]
+        public double Amount { get; set; }
+
+        [Index(6)]
+        public double Tax { get; set; }
+
+        [Index(7)]
+        public double DiscountOffered { get; set; }
+
+        [Index(8)]
+        public double MiscDeduction { get; set; }
+
+        [Index(9)]
+        public DateTime InvoiceDate { get; set; }
+
+        [Index(10)]
+        public DateTime DateReceived { get; set; }
+
+        [Index(11)]
+        public DateTime DiscountDate { get; set; }
+
+        [Index(12)]
+        public DateTime PaymentDate { get; set; }
+
+        [Index(13)]
+        public DateTime AccountingDate { get; set; }
+
+        [Index(14)]
+        public string InvoiceCode1 { get; set; }
+
+        [Index(15)]
+        public string InvoiceCode2 { get; set; }
+
+        [Index(16)]
+        public string SmryPayeeName { get; set; }
+
+        [Index(17)]
+        public string SmryPayeeAddress1 { get; set; }
+
+        [Index(18)]
+        public string SmryPayeeAddress2 { get; set; }
+
+        [Index(19)]
+        public string SmryPayeeCity { get; set; }
+
+        [Index(20)]
+        public string SmryPayeeState { get; set; }
+
+        [Index(21)]
+        public string SmryPayeeZip { get; set; }
+    }
+
+    public class PurchaseOrderTimberlineLine
+    {
+        [Index(1)]
+        public string RecordID { get; set; } = "APDF";
+
+        [Index(2)]
+        public string Commitment { get; set; }
+
+        [Index(3)]
+        public int CommitmentLineItem { get; set; }
+
+        [Index(4)]
+        public string Equipment { get; set; }
+
+        [Index(5)]
+        public string EQCostCode { get; set; }
+
+        [Index(6)]
+        public string Job { get; set; }
+
+        [Index(7)]
+        public string Extra { get; set; }
+
+        [Index(8)]
+        public string CostCode { get; set; }
+
+        [Index(9)]
+        public string Category { get; set; }
+
+        [Index(10)]
+        public string BLStdItem { get; set; }
+
+        [Index(11)]
+        public string Reserved { get; set; }
+
+        [Index(12)]
+        public string ExpenseAccount { get; set; }
+
+        [Index(13)]
+        public string APAccount { get; set; }
+
+        [Index(14)]
+        public double TaxablePayments { get; set; }
+
+        [Index(15)]
+        public string TaxGroup { get; set; }
+
+        [Index(16)]
+        public double Units { get; set; }
+
+        [Index(17)]
+        public double UnitCost { get; set; }
+
+        [Index(18)]
+        public double Amount { get; set; }
+
+        [Index(19)]
+        public double Tax { get; set; }
+
+        [Index(20)]
+        public double TaxLiability { get; set; }
+
+        [Index(21)]
+        public double DiscountOffered { get; set; }
+
+        [Index(22)]
+        public double Retainage { get; set; }
+
+        [Index(23)]
+        public double MiscDeduction { get; set; }
+
+        [Index(24)]
+        [BooleanTrueValues("t", "T", "1", "Y", "y")]
+        [BooleanFalseValues("f", "F", "0", "N", "n")]
+        public bool TaxablePaymentsExempt { get; set; }
+
+        [Index(25)]
+        public string DistCode { get; set; }
+
+        [Index(26)]
+        public string MiscEntry1 { get; set; }
+
+        [Index(27)]
+        public double MiscEntryUnits1 { get; set; }
+
+        [Index(28)]
+        public string MiscEntry2 { get; set; }
+
+        [Index(29)]
+        public double MiscEntryUnits2 { get; set; }
+
+        [Index(30)]
+        public double MeterOdometer { get; set; }
+
+        [Index(31)]
+        public string Description { get; set; }
+
+        [Index(32)]
+        public string Authorization { get; set; }
+
+        [Index(33)]
+        public string JointPayee { get; set; }
+    }
+
+    public class PurchaseOrderTimberlineSettings : TimberlinePosterSettings<PurchaseOrder>
+    {
+        protected override string DefaultScript()
+        {
+            return @"
+using PRS.Shared;
+using InABox.Core;
+using System.Collections.Generic;
+
+public class Module
+{
+    public void BeforePost(IDataModel<PurchaseOrder> model)
+    {
+        // Perform pre-processing
+    }
+
+    public void ProcessHeader(IDataModel<PurchaseOrder> model, PurchaseOrder purchaseOrder, PurchaseOrderTimberlineHeader header)
+    {
+        // Do extra processing for a purchase order
+    }
+
+    public void ProcessLine(IDataModel<PurchaseOrder> model, PurchaseOrderItem purchaseOrderItem, PurchaseOrderTimberlineLine line)
+    {
+        // Do extra processing for a purchase order line
+    }
+
+    public void AfterPost(IDataModel<PurchaseOrder> model)
+    {
+        // Perform post-processing
+    }
+}";
+        }
+    }
+
+    public class PurchaseOrderTimberlinePoster : ITimberlinePoster<PurchaseOrder, PurchaseOrderTimberlineSettings>
+    {
+        public ScriptDocument? Script { get; set; }
+
+        public bool BeforePost(IDataModel<PurchaseOrder> model)
+        {
+            model.SetIsDefault<Document>(false, alias: "CompanyLogo");
+            model.SetIsDefault<CoreTable>(false, alias: "CompanyInformation");
+            model.SetIsDefault<Employee>(false);
+
+            Script?.Execute(methodname: "BeforePost", parameters: new object[] { model });
+            return true;
+        }
+
+        private void ProcessHeader(IDataModel<PurchaseOrder> model, PurchaseOrder bill, PurchaseOrderTimberlineHeader header)
+        {
+            Script?.Execute(methodname: "ProcessHeader", parameters: new object[] { model, bill, header });
+        }
+        private void ProcessLine(IDataModel<PurchaseOrder> model, PurchaseOrderItem purchaseOrderItem, PurchaseOrderTimberlineLine line)
+        {
+            Script?.Execute(methodname: "ProcessLine", parameters: new object[] { model, purchaseOrderItem, line });
+        }
+
+        private List<PurchaseOrderTimberlineHeader> DoProcess(IDataModel<PurchaseOrder> model)
+        {
+            var cs = new List<PurchaseOrderTimberlineHeader>();
+
+            var lines = model.GetTable<PurchaseOrderItem>("PurchaseOrder_PurchaseOrderItem").ToObjects<PurchaseOrderItem>()
+                .GroupBy(x => x.PurchaseOrderLink.ID).ToDictionary(x => x.Key, x => x.ToList());
+            foreach (var purchaseOrder in model.GetTable<PurchaseOrder>().ToObjects<PurchaseOrder>())
+            {
+                var c = new PurchaseOrderTimberlineHeader
+                {
+                };
+                ProcessHeader(model, purchaseOrder, c);
+
+                foreach (var purchaseOrderItem in lines.GetValueOrDefault(purchaseOrder.ID) ?? Enumerable.Empty<PurchaseOrderItem>())
+                {
+                    var ci = new PurchaseOrderTimberlineLine
+                    {
+                    };
+
+                    ProcessLine(model, purchaseOrderItem, ci);
+                    c.Lines.Add(ci);
+                }
+                cs.Add(c);
+            }
+            return cs;
+        }
+
+        public bool Process(IDataModel<PurchaseOrder> model)
+        {
+            var cs = DoProcess(model);
+
+            var dlg = new SaveFileDialog()
+            {
+                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 c in cs)
+                {
+                    csv.WriteRecord(c);
+                    csv.NextRecord();
+                    foreach (var ci in c.Lines)
+                    {
+                        csv.WriteRecord(ci);
+                        csv.NextRecord();
+                    }
+                }
+                return true;
+            }
+            else
+            {
+                throw new PostCancelledException();
+            }
+        }
+        public void AfterPost(IDataModel<PurchaseOrder> model)
+        {
+            Script?.Execute(methodname: "AfterPost", parameters: new object[] { model });
+        }
+    }
+
+    public class PurchaseOrderTimberlinePosterEngine<T> : TimberlinePosterEngine<PurchaseOrder, PurchaseOrderTimberlineSettings>
+    {
+    }
+}