Browse Source

Added Payment Terms to Customers, Suppliers, Invoices and Bills

frogsoftware 2 hours ago
parent
commit
4177e11476

+ 53 - 3
prs.classes/Entities/Bill/Bill.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Collections.Generic;
 using InABox.Core;
 
 namespace Comal.Classes
@@ -25,10 +26,14 @@ namespace Comal.Classes
         public DateTime BillDate { get; set; }
         
         [EditorSequence(5)]
+        [LookupDefinition(typeof(AccountsPayablePaymentTermsLookup<Bill>))]
+        public PaymentTermsLink Terms { get; set; }
+        
+        [EditorSequence(6)]
         [DateEditor]
         public DateTime PaymentDate { get; set; }
 
-        [EditorSequence(6)]
+        [EditorSequence(7)]
         [DateEditor]
         public DateTime AccountingDate { get; set; }
         
@@ -86,11 +91,56 @@ namespace Comal.Classes
         [NullEditor]
         public string PostedReference { get; set; }
 
+        private static string BILLDATE = CoreUtils.GetFullPropertyName<Bill, DateTime>(x => x.BillDate, ".");
+        private static string TERMSCALCULATION = CoreUtils.GetFullPropertyName<Bill, String>(x => x.Terms.Calculation, ".");
+        private static string SUPPLIERLINKTERMSCALCULATION = CoreUtils.GetFullPropertyName<Bill, String>(x => x.SupplierLink.Terms.Calculation, ".");
+        private static string AMOUNTPAID = CoreUtils.GetFullPropertyName<Bill, double>(x => x.AmountPaid, ".");
+        private static string INCTAX = CoreUtils.GetFullPropertyName<Bill, double>(x => x.IncTax, ".");
+        
         protected override void DoPropertyChanged(string name, object? before, object? after)
         {
             base.DoPropertyChanged(name, before, after);
-            if (name.Equals("AmountPaid") || name.Equals("IncTax"))
-                Balance = IncTax - AmountPaid;
+            if (name.Equals(AMOUNTPAID))
+                Balance = IncTax - (double)(after ?? 0.0);
+            else if (name.Equals(INCTAX))
+                Balance = (double)(after ?? 0.0) - AmountPaid;
+            else if (name.Equals(BILLDATE))
+                PaymentDate =  CalculateTerms((DateTime)(after ?? ""),Terms.Calculation);
+            else if (name.Equals(TERMSCALCULATION))
+                PaymentDate =  CalculateTerms(BillDate, (string)(after ?? ""));
+            else if (name.Equals(SUPPLIERLINKTERMSCALCULATION))
+                PaymentDate =  CalculateTerms(BillDate, (string)(after ?? ""));
+        }
+
+        private DateTime CalculateTerms(DateTime date, string calculation)
+        {
+            if (string.IsNullOrWhiteSpace(calculation))
+                return date;
+            
+            var variables = new Dictionary<string, object?>()
+            {
+                { "Date", date }
+            };
+            
+            try
+            {
+                var expr = new CoreExpression(calculation);
+                var eval = expr.Evaluate(variables);
+                return (DateTime)(CoreUtils.ChangeType(eval, typeof(DateTime)) ?? date);
+            }
+            catch (Exception e)
+            {
+                Logger.Send(LogType.Information, "", $"Error in Formula: [{calculation}] ({e.Message})");
+                return date;
+            }
+        }
+
+        static Bill()
+        {
+            LinkedProperties.Register<Bill,PaymentTermsLink,Guid>(x=>x.SupplierLink.Terms,x=>x.ID, x=>x.Terms.ID);
+            LinkedProperties.Register<Bill,PaymentTermsLink,String>(x=>x.SupplierLink.Terms,x=>x.Code, x=>x.Terms.Code);
+            LinkedProperties.Register<Bill,PaymentTermsLink,String>(x=>x.SupplierLink.Terms,x=>x.Description, x=>x.Terms.Description);
+            LinkedProperties.Register<Bill,PaymentTermsLink,String>(x=>x.SupplierLink.Terms,x=>x.Calculation, x=>x.Terms.Calculation);
         }
     }
 }

+ 7 - 3
prs.classes/Entities/Customer/Customer.cs

@@ -97,18 +97,22 @@ namespace Comal.Classes
         [IntegerEditor(Editable = Editable.Hidden, Summary = Summary.Sum)]
         [Aggregate(typeof(CustomerSites))]
         public int Sites { get; set; }
-
+        
         [EditorSequence("Accounts", 4)]
+        [LookupDefinition(typeof(AccountsReceivablePaymentTermsLookup<Customer>))]
+        public PaymentTermsLink Terms { get; set; }
+
+        [EditorSequence("Accounts", 5)]
         [CurrencyEditor(Editable = Editable.Disabled, Summary = Summary.Sum)]
         [Aggregate(typeof(CustomerSales))]
         public double TotalSales { get; set; }
 
-        [EditorSequence("Accounts", 5)]
+        [EditorSequence("Accounts", 6)]
         [CurrencyEditor(Editable = Editable.Disabled, Summary = Summary.Sum)]
         [Aggregate(typeof(CustomerReceipts))]
         public double TotalReceipts { get; set; }
 
-        [EditorSequence("Accounts", 6)]
+        [EditorSequence("Accounts", 7)]
         [CurrencyEditor(Editable = Editable.Hidden, Summary = Summary.Sum)]
         [Formula(typeof(CustomerBalance))]
         public double Balance { get; set; }

+ 7 - 0
prs.classes/Entities/Customer/CustomerLink.cs

@@ -14,6 +14,9 @@ namespace Comal.Classes
         [TextBoxEditor(Editable = Editable.Hidden, Visible = Visible.Optional)]
         public string Name { get; set; }
         
+        [NullEditor]
+        public PaymentTermsLink Terms { get; set; }
+        
     }
 
     public class CustomerLink : EntityLink<Customer>
@@ -40,5 +43,9 @@ namespace Comal.Classes
 
         [NullEditor]
         public Address Delivery { get; set; }
+        
+        [NullEditor]
+        [RequiredColumn]
+        public PaymentTermsLink Terms { get; set; }
     }
 }

+ 56 - 5
prs.classes/Entities/Invoice/Invoice.cs

@@ -103,6 +103,14 @@ namespace Comal.Classes
         [ComplexFormula(typeof(BalanceFormula))]
         public double Balance { get; set; }
         
+        [EditorSequence(13)]
+        [LookupDefinition(typeof(AccountsReceivablePaymentTermsLookup<Invoice>))]
+        public PaymentTermsLink Terms { get; set; }
+        
+        [EditorSequence(14)]
+        [DateEditor(Visible = Visible.Default, TodayVisible = true)]
+        public DateTime DueDate { get; set; }
+        
         [NullEditor]
         [LoggableProperty]
         [RequiredColumn]
@@ -135,14 +143,57 @@ namespace Comal.Classes
         {
             return string.Format("{0}: {1}", Number, Description);
         }
-
+        
+        private static string DATE = CoreUtils.GetFullPropertyName<Invoice, DateTime>(x => x.Date, ".");
+        private static string CUSTOMERLINKTERMSCALCULATION = CoreUtils.GetFullPropertyName<Invoice, String>(x => x.CustomerLink.Terms.Calculation, ".");
+        private static string TERMSCALCULATION = CoreUtils.GetFullPropertyName<Invoice, String>(x => x.Terms.Calculation, ".");
+        private static string AMOUNTPAID = CoreUtils.GetFullPropertyName<Invoice, double>(x => x.AmountPaid, ".");
+        private static string INCTAX = CoreUtils.GetFullPropertyName<Invoice, double>(x => x.IncTax, ".");
+        
         protected override void DoPropertyChanged(string name, object? before, object? after)
         {
             base.DoPropertyChanged(name, before, after);
-            if (name.Equals("IncTax"))
-                Balance = (double)after - AmountPaid;
-            else if (name.Equals("AmountPaid"))
-                Balance = IncTax - (double)after;
+            if (name.Equals(AMOUNTPAID))
+                Balance = IncTax - (double)(after ?? 0.0);
+            else if (name.Equals(INCTAX))
+                Balance = (double)(after ?? 0.0) - AmountPaid;
+            else if (name.Equals(DATE))
+                DueDate =  CalculateTerms((DateTime)(after ?? ""),Terms.Calculation);
+            else if (name.Equals(TERMSCALCULATION))
+                DueDate =  CalculateTerms(Date, (string)(after ?? ""));
+            else if (name.Equals(CUSTOMERLINKTERMSCALCULATION))
+                DueDate =  CalculateTerms(Date, (string)(after ?? ""));
+        }
+
+        private DateTime CalculateTerms(DateTime date, string calculation)
+        {
+            if (string.IsNullOrWhiteSpace(calculation))
+                return date;
+            
+            var variables = new Dictionary<string, object?>()
+            {
+                { "Date", date }
+            };
+            
+            try
+            {
+                var expr = new CoreExpression(calculation);
+                var eval = expr.Evaluate(variables);
+                return (DateTime)(CoreUtils.ChangeType(eval, typeof(DateTime)) ?? date);
+            }
+            catch (Exception e)
+            {
+                Logger.Send(LogType.Information, "", $"Error in Formula: [{calculation}] ({e.Message})");
+                return date;
+            }
+        }
+
+        static Invoice()
+        {
+            LinkedProperties.Register<Invoice,PaymentTermsLink,Guid>(x=>x.CustomerLink.Terms,x=>x.ID, x=>x.Terms.ID);
+            LinkedProperties.Register<Invoice,PaymentTermsLink,String>(x=>x.CustomerLink.Terms,x=>x.Code, x=>x.Terms.Code);
+            LinkedProperties.Register<Invoice,PaymentTermsLink,String>(x=>x.CustomerLink.Terms,x=>x.Description, x=>x.Terms.Description);
+            LinkedProperties.Register<Invoice,PaymentTermsLink,String>(x=>x.CustomerLink.Terms,x=>x.Calculation, x=>x.Terms.Calculation);
         }
         
     }

+ 39 - 0
prs.classes/Entities/PaymentTerms/PaymentTerms.cs

@@ -0,0 +1,39 @@
+using System;
+using InABox.Core;
+
+namespace Comal.Classes
+{
+
+    public class PaymentTermsCalculationModel : IExpressionModel<DateTime>
+    {
+        public DateTime Date { get; set; }
+        
+    }
+    
+    public class PaymentTerms : Entity, IRemotable, IPersistent, ILicense<CoreLicense>
+    {
+        [EditorSequence(1)]
+        [UniqueCodeEditor(Visible=Visible.Default, Editable=Editable.Enabled)]
+        public string Code { get; set; }
+        
+        [EditorSequence(2)]
+        [TextBoxEditor]
+        public string Description { get; set; }
+        
+        [EditorSequence(3)]
+        [ExpressionEditor(typeof(PaymentTermsCalculationModel))]
+        public string Calculation { get; set; }
+        
+        [EditorSequence(4)]
+        [CheckBoxEditor]
+        public bool AccountsReceivable { get; set; }
+        
+        [EditorSequence(5)]
+        [CheckBoxEditor]
+        public bool AccountsPayable { get; set; }
+        
+        [EditorSequence(6)]
+        [CheckBoxEditor]
+        public bool Active { get; set; }
+        }
+}

+ 24 - 0
prs.classes/Entities/PaymentTerms/PaymentTermsLink.cs

@@ -0,0 +1,24 @@
+using System;
+using InABox.Core;
+
+namespace Comal.Classes
+{
+    public class PaymentTermsLink : EntityLink<PaymentTerms>
+    {
+        [LookupEditor(typeof(PaymentTerms))]
+        public override Guid ID { get; set; }
+        
+        [EditorSequence(1)]
+        [CodeEditor(Visible = Visible.Optional, Editable = Editable.Hidden)]
+        public string Code { get; set; }
+        
+        [EditorSequence(2)]
+        [TextBoxEditor(Visible = Visible.Optional, Editable = Editable.Hidden)]
+        public string Description { get; set; }
+        
+        [EditorSequence(3)]
+        [RequiredColumn]
+        [TextBoxEditor(Visible = Visible.Hidden, Editable=Editable.Hidden)]
+        public string Calculation { get; set; }
+    }
+}

+ 36 - 0
prs.classes/Entities/PaymentTerms/PaymentTermsLookup.cs

@@ -0,0 +1,36 @@
+using System;
+using InABox.Core;
+
+namespace Comal.Classes
+{
+    public abstract class AbstractPaymentTermsLookup<T> : LookupDefinitionGenerator<PaymentTerms, T>
+        where T : Entity
+    {
+        
+        public override Columns<PaymentTerms> DefineColumns()
+        {
+            return base.DefineColumns()
+                .Add(x=>x.Code)
+                .Add(x=>x.Description);
+        }
+
+        public override string? FormatDisplay(CoreRow row)
+            => $"{row.Get<PaymentTerms, String>(x => x.Code)}: {row.Get<PaymentTerms, String>(x => x.Description)}";
+    }
+
+    public class AccountsReceivablePaymentTermsLookup<T> : AbstractPaymentTermsLookup<T>
+        where T : Entity
+    {
+        public override Filter<PaymentTerms> DefineFilter(T[] items)
+            => new Filter<PaymentTerms>(x=>x.Active).IsEqualTo(true).And(x => x.AccountsReceivable).IsEqualTo(true);
+
+    }
+    
+    public class AccountsPayablePaymentTermsLookup<T> : AbstractPaymentTermsLookup<T>
+        where T : Entity
+    {
+        public override Filter<PaymentTerms> DefineFilter(T[] items)
+            => new Filter<PaymentTerms>(x=>x.Active).IsEqualTo(true).And(x => x.AccountsPayable).IsEqualTo(true);
+
+    }
+}

+ 8 - 0
prs.classes/Entities/Supplier/Supplier.cs

@@ -94,18 +94,26 @@ namespace Comal.Classes
         [EditorSequence("Accounts",3)]
         public ForeignCurrencyLink Currency { get; set; }
         
+        [EditorSequence("Accounts", 4)]
+        [LookupDefinition(typeof(AccountsPayablePaymentTermsLookup<Supplier>))]
+        public PaymentTermsLink Terms { get; set; }
+        
+        [EditorSequence("Accounts", 5)]
         [CurrencyEditor(Editable = Editable.Hidden, Summary = Summary.Sum)]
         [Aggregate(typeof(SupplierOrderBalance))]
         public double OrderBalance { get; set; }
 
+        [EditorSequence("Accounts", 6)]
         [CurrencyEditor(Editable = Editable.Hidden, Summary = Summary.Sum)]
         [Aggregate(typeof(SupplierTotalBills))]
         public double TotalBills { get; set; }
 
+        [EditorSequence("Accounts", 7)]
         [CurrencyEditor(Editable = Editable.Hidden, Summary = Summary.Sum)]
         [Aggregate(typeof(SupplierTotalPayments))]
         public double TotalPayments { get; set; }
 
+        [EditorSequence("Accounts", 8)]
         [CurrencyEditor(Editable = Editable.Hidden, Summary = Summary.Sum)]
         [Formula(typeof(SupplierBalance))]
         public double Balance { get; set; }

+ 4 - 0
prs.classes/Entities/Supplier/SupplierLink.cs

@@ -18,5 +18,9 @@ namespace Comal.Classes
         
         [RequiredColumn]
         public ForeignCurrencyLink Currency { get; set; }
+        
+        [NullEditor]
+        [RequiredColumn]
+        public PaymentTermsLink Terms { get; set; }
     }
 }

+ 10 - 1
prs.desktop/Setups/AccountsSetupActions.cs

@@ -23,16 +23,25 @@ public static class AccountsSetupActions
             var list = new MasterList(typeof(TaxCode));
             list.ShowDialog();
         });
+        
         host.CreateSetupActionIfCanView<ReceiptType>("Receipt Types", PRSDesktop.Resources.receipt, (action) =>
         {
             var list = new MasterList(typeof(ReceiptType));
             list.ShowDialog();
         });
+        
         host.CreateSetupActionIfCanView<PaymentType>("Payment Types", PRSDesktop.Resources.payment, (action) =>
         {
-            var list = new MasterList(typeof(PaymentType));
+            var list = new MasterList(typeof(PaymentTerms));
             list.ShowDialog();
         });
+        
+        host.CreateSetupActionIfCanView<PaymentTerms>("Payment Terms", PRSDesktop.Resources.assignments, (action) =>
+        {
+            var list = new MasterList(typeof(PaymentTerms));
+            list.ShowDialog();
+        });
+        
         host.CreateSetupActionIfCanView<CostCentre>("Cost Centres", PRSDesktop.Resources.costcentre, (action) =>
         {
             var list = new MasterList(typeof(CostCentre));