using Comal.Classes; using Comal.Stores; using GenHTTP.Api.Protocol; using GenHTTP.Modules.IO; using InABox.Clients; using InABox.Configuration; using InABox.Core; using InABox.Rpc; using InABox.Wpf.Reports; using PRS.Shared; using PRSServer; using PRSServices; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using RequestMethod = GenHTTP.Api.Protocol.RequestMethod; namespace PRSLicensing; public class LicensingHandler : Handler { public override void Init(LicensingHandlerProperties properties) { } private IResponseBuilder LicenseSummary(IRequest request) { var summary = new LicenseSummary { LicenseFees = new() { { typeof(CoreLicense).EntityName(), 7.99 }, { typeof(DigitalFormsLicense).EntityName(), 3.99 }, { typeof(SchedulingControlLicense).EntityName(), 1.99 }, { typeof(TimeManagementLicense).EntityName(), 2.99 }, { typeof(AccountsPayableLicense).EntityName(), 1.99 }, { typeof(GPSTrackerLicense).EntityName(), 2.99 }, { typeof(LogisticsLicense).EntityName(), 4.99 }, { typeof(ScheduleEngineLicense).EntityName(), 2.99 }, { typeof(QuotesManagementLicense).EntityName(), 4.99 }, { typeof(LeaveManagementLicense).EntityName(), 2.99 }, { typeof(TaskManagementLicense).EntityName(), 1.99 }, { typeof(WarehouseLicense).EntityName(), 5.99 }, { typeof(ProjectManagementLicense).EntityName(), 4.99 }, { typeof(ManufacturingLicense).EntityName(), 4.99 }, { typeof(ProductManagementLicense).EntityName(), 2.99 }, { typeof(EquipmentLicense).EntityName(), 2.99 }, { typeof(HumanResourcesLicense).EntityName(), 2.99 }, { typeof(AccountsReceivableLicense).EntityName(), 1.99 } }, TimeDiscounts = new() { { 1, 0.00 }, { 3, 5.00 }, { 6, 5.00 }, { 12, 10.00 } }, UserDiscounts = new() { { 1, 00.31 }, { 6, 08.63 }, { 11, 16.94 }, { 21, 25.25 }, { 51, 33.57 } } }; return request.Respond().Status(ResponseStatus.OK).Content(Serialization.Serialize(summary)); } private static List>> renewalPeriods = new List>> { new(1, x => x.AddDays(-7)), new(3, x => x.AddDays(-14)), new(6, x => x.AddMonths(-1)) }; private int GetMonthDifference(DateTime date1, DateTime date2){ var months = (date2.Year - date1.Year) * 12 + (date2.Month - date1.Month); if(date2.Day >= date1.Day){ return months; } return months - 1; } private LicenseData GenerateLicense(LicenseRenewal renewal){ var renewalPeriodInMonths = GetMonthDifference(renewal.DateRenewed, renewal.NewExpiry); var renewalAvailable = renewalPeriods .Where(x => renewalPeriodInMonths >= x.Item1) .MaxBy(x => x.Item1) .Item2(renewal.NewExpiry); var newLicense = LicenseUtils.RenewLicense(renewal.OldLicense, renewal.DateRenewed, renewal.NewExpiry, renewalAvailable); return newLicense; } private string NewCustomerCode(LicenseRenewal renewal){ var code = renewal.Company.CompanyName.ToUpper(); var codes = new Client().Query( new Filter(x => x.Code).BeginsWith(code), new Columns(x => x.Code))?.Rows.Select(x => x.Get(x => x.Code)).ToList(); var tryCode = code; var i = 0; while(codes.Contains(tryCode)){ tryCode = $"{code}{i}"; ++i; } return tryCode; } private Customer CreateNewCustomer(LicenseRenewal renewal){ Logger.Send(LogType.Information, "", "Creating new customer"); var customer = new Customer { Code = NewCustomerCode(renewal), Name = $"{renewal.Company.CompanyName} - {renewal.Company.ABN}", Delivery = renewal.Company.DeliveryAddress, Email = renewal.Company.Email, Postal = renewal.Company.PostalAddress }; new Client().Save(customer, "Created by License Renewal"); return customer; } private CustomerDocument CreateNewCustomerDocument(Guid customerID, string fileName, string data){ var document = new Document { FileName = fileName, Private = true, TimeStamp = DateTime.Now, Data = Encoding.UTF8.GetBytes(data) }; document.CRC = CoreUtils.CalculateCRC(document.Data); new Client().Save(document, ""); var documentType = new Client() .Query(new Filter(x => x.Code).IsEqualTo("LICENSE"), new Columns(x => x.ID)) .Rows.FirstOrDefault()?.Get(x => x.ID) ?? Guid.Empty; if(documentType == Guid.Empty){ Logger.Send(LogType.Error, "", "Document Type 'LICENSE' doesn't exist"); } var customerDocument = new CustomerDocument(); customerDocument.Type.ID = documentType; customerDocument.EntityLink.ID = customerID; customerDocument.DocumentLink.ID = document.ID; new Client().Save(customerDocument, "Created by License Renewal"); return customerDocument; } private void CreateInvoice(Guid customerID, LicenseRenewal renewal){ var invoiceLines = new List(); var notes = new List(); foreach(var item in renewal.LicenseTracking){ var invoiceLine = new InvoiceLine { Description = $"{item.License} - {item.Users} Users @ ${item.Rate:F2} per user", ExTax = item.ExGST }; invoiceLines.Add(invoiceLine); notes.Add(invoiceLine.Description); } var discountLine = new InvoiceLine { Description = $"${renewal.Discount:F2} discount", ExTax = -renewal.Discount }; invoiceLines.Add(discountLine); notes.Add(discountLine.Description); var invoice = new Invoice { Date = DateTime.Today, Description = $"License Renewal - Stripe Transaction No. '{renewal.TransactionID}'" }; invoice.CustomerLink.ID = customerID; new Client().Save(invoice, "Created by License Renewal"); foreach(var line in invoiceLines){ line.InvoiceLink.ID = invoice.ID; } new Client().Save(invoiceLines, ""); var receipt = new Receipt { Date = DateTime.Today, Notes = string.Join('\n', notes) }; new Client().Save(receipt, ""); var invoiceReceipt = new InvoiceReceipt { Notes = "Receipt for License Renewal", Amount = renewal.Net }; invoiceReceipt.InvoiceLink.ID = invoice.ID; invoiceReceipt.ReceiptLink.ID = receipt.ID; new Client().Save(invoiceReceipt, ""); } private IResponseBuilder RenewLicense(IRequest request) { var renewal = Serialization.Deserialize(request.Content); if(renewal == null){ return request.Respond().Status(ResponseStatus.BadRequest); } Logger.Send(LogType.Information, "", $"Request for license renewal from {renewal.Company.CompanyName}"); var customerID = renewal.OldLicense.CustomerID; if(customerID == Guid.Empty){ customerID = CreateNewCustomer(renewal).ID; renewal.OldLicense.CustomerID = customerID; } Logger.Send(LogType.Information, "", "Generating new license"); var newLicense = GenerateLicense(renewal); var newLicenseData = LicenseUtils.EncryptLicense(newLicense); if(newLicenseData == null){ Logger.Send(LogType.Error, "", "Encryption of new license failed!"); return request.Respond().Status(ResponseStatus.InternalServerError); } Logger.Send(LogType.Information, "", "Generating customer document"); var customerDocument = CreateNewCustomerDocument(customerID, $"{renewal.Company.CompanyName} - PRS License - {DateTime.Now:dd MMM yyyy}.txt", newLicenseData); Logger.Send(LogType.Information, "", "Creating invoice"); CreateInvoice(customerID, renewal); return request.Respond() .Status(ResponseStatus.OK) .Content(Serialization.Serialize(new License { Data = newLicenseData } )); // Send just the encrypted data. } private IResponseBuilder Ping(IRequest request) { return request.Respond().Status(ResponseStatus.OK); } private IResponseBuilder HandleGET(IRequest request) { var endpoint = request.Target.Current?.Value.ToLower() ?? ""; if(endpoint == nameof(LicenseSummary).ToLower()) { return LicenseSummary(request); } else if(endpoint == nameof(Ping).ToLower()) { return Ping(request); } return request.Respond().Status(ResponseStatus.NotFound); } private IResponseBuilder HandlePOST(IRequest request) { var endpoint = string.Join('/', request.Target.GetRemaining()); if(endpoint == nameof(RenewLicense).ToLower()) { return RenewLicense(request); } return request.Respond().Status(ResponseStatus.NotFound); } public override ValueTask HandleAsync(IRequest request) { try { switch (request.Method.KnownMethod) { case RequestMethod.GET: return new ValueTask(HandleGET(request).Build()); case RequestMethod.POST: return new ValueTask(HandlePOST(request).Build()); default: Logger.Send(LogType.Error, ClientFactory.UserID, $"Request method {request.Method.RawMethod} unknown"); return new ValueTask(request.Respond().Status(ResponseStatus.MethodNotAllowed).Build()); } } catch (Exception eListen) { Logger.Send(LogType.Error, ClientFactory.UserID, eListen.Message); return new ValueTask(request.Respond().Status(ResponseStatus.InternalServerError).Build()); } } } public class LicensingHandlerProperties { } public class LicensingEngine : Engine { private Listener listener; public override void Run() { Logger.Send(LogType.Information, "", "Starting.."); if (string.IsNullOrWhiteSpace(Properties.Server)) { Logger.Send(LogType.Error, "", "Server is blank!"); return; } var transport = new RpcClientPipeTransport(DatabaseServerProperties.GetPipeName(Properties.Server, true)); ClientFactory.SetClientType(typeof(RpcClient<>), Platform.WebEngine, Version, transport); CheckConnection(); Logger.Send(LogType.Information, "", "Registering Classes"); StoreUtils.RegisterClasses(); CoreUtils.RegisterClasses(); ComalUtils.RegisterClasses(); PRSSharedUtils.RegisterClasses(); ReportUtils.RegisterClasses(); ConfigurationUtils.RegisterClasses(); Logger.Send(LogType.Information, "", "Starting Listener on port " + Properties.ListenPort); try { listener = new Listener(new LicensingHandlerProperties()); listener.InitHTTPS((ushort)Properties.ListenPort, CertificateFileName()); Logger.Send(LogType.Information, "", "Starting Web Listener on port " + Properties.ListenPort); listener.Start(); } catch (Exception eListen) { Logger.Send(LogType.Error, ClientFactory.UserID, eListen.Message); } } private string CertificateFileName() => Properties.CertificateFile; private void CheckConnection() { // Wait for server connection while (!Client.Ping()) { Logger.Send(LogType.Error, "", "Database server unavailable. Trying again in 30 seconds..."); Task.Delay(30_000).Wait(); Logger.Send(LogType.Information, "", "Retrying connection..."); } ClientFactory.SetBypass(); } public override void Stop() { Logger.Send(LogType.Information, "", "Stopping"); listener?.Stop(); } } public class LicensingEngineProperties : ServerProperties { [ComboLookupEditor(typeof(LicensingDatabaseServerLookupGenerator))] [EditorSequence(1)] public string Server { get; set; } [IntegerEditor] [EditorSequence(2)] public int ListenPort { get; set; } [EditorSequence(3)] [FileNameEditor("Certificate Files (*.pfx)|*.pfx")] public string CertificateFile { get; set; } public override ServerType Type() { return ServerType.Other; } } public class LicensingDatabaseServerLookupGenerator : LookupGenerator { public LicensingDatabaseServerLookupGenerator(LicensingEngineProperties[] items) : base(items) { } protected override void DoGenerateLookups() { var config = new LocalConfiguration(CoreUtils.GetCommonAppData("PRSServer"), ""); var servers = config.LoadAll(); foreach (var server in servers.Select(x => x.Value.CreateServer(x.Key))) { if (server.Type == ServerType.Database) { AddValue(server.Key, server.Name); } } } }