LicensingHandler.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. using com.sun.xml.@internal.fastinfoset.sax;
  7. using Comal.Classes;
  8. using GenHTTP.Api.Protocol;
  9. using GenHTTP.Modules.IO;
  10. using InABox.Clients;
  11. using InABox.Core;
  12. using NPOI.HSSF.Util;
  13. using PRS.Shared;
  14. using PRSServices;
  15. using RequestMethod = GenHTTP.Api.Protocol.RequestMethod;
  16. namespace PRSLicensing;
  17. public class LicensingHandler : Handler<LicensingHandlerProperties>
  18. {
  19. private LicensingHandlerProperties? _properties;
  20. public override void Init(LicensingHandlerProperties properties)
  21. {
  22. _properties = properties;
  23. }
  24. private IResponseBuilder RetrieveFees(IRequest request)
  25. {
  26. if (_properties == null)
  27. return request.Respond().Status(ResponseStatus.BadRequest);
  28. var lsr = Serialization.Deserialize<LicenseFeeRequest>(request.Content);
  29. if (lsr == null)
  30. return request.Respond().Status(ResponseStatus.BadRequest);
  31. Logger.Send(LogType.Information, "", $"License Enquiry Received ({lsr.RegistrationID})");
  32. var productids = _properties.EngineProperties.Mappings.Select(x => x.Product.ID).ToArray();
  33. if (!productids.Any())
  34. {
  35. Logger.Send(LogType.Error,"","Engine Product Mapping List is Empty");
  36. return request.Respond().Status(ResponseStatus.BadRequest);
  37. }
  38. var query = new MultiQuery();
  39. query.Add(
  40. new Filter<Product>(x=>x.ID).InList(productids),
  41. Columns.None<Product>().Add(x=>x.ID)
  42. .Add(x=>x.NettCost)
  43. .Add(x=>x.Charge.Chargeable)
  44. .Add(x=>x.Charge.PriceType)
  45. .Add(x=>x.Charge.Markup)
  46. .Add(x=>x.Charge.Price)
  47. );
  48. if (lsr.RegistrationID != Guid.Empty)
  49. query.Add(
  50. new Filter<CustomerProduct>(x=>x.Customer.ID).IsEqualTo(lsr.RegistrationID).And(x=>x.Product.ID).InList(productids),
  51. Columns.None<CustomerProduct>().Add(x=>x.Product.ID)
  52. .Add(x=>x.Product.NettCost)
  53. .Add(x=>x.Charge.Chargeable)
  54. .Add(x=>x.Charge.PriceType)
  55. .Add(x=>x.Charge.Markup)
  56. .Add(x=>x.Charge.Price)
  57. );
  58. query.Query();
  59. var products = query.Get<Product>().Rows.Select(x => x.ToObject<Product>()).ToArray();
  60. var customerproducts = lsr.RegistrationID != Guid.Empty
  61. ? query.Get<CustomerProduct>().Rows.Select(x => x.ToObject<CustomerProduct>()).ToArray()
  62. : new CustomerProduct[] { };
  63. var result = new LicenseFeeResponse();
  64. foreach (var mapping in _properties.EngineProperties.Mappings)
  65. {
  66. var customer = customerproducts.FirstOrDefault(x => x.Product.ID == mapping.Product.ID);
  67. if (customer != null)
  68. {
  69. result.LicenseFees[mapping.License] = customer.Charge.PriceType == ProductPriceType.CostPlus
  70. ? customer.Product.NettCost * (100F + customer.Charge.Markup) / 100F
  71. : customer.Charge.Price;
  72. }
  73. else
  74. {
  75. var product = products.FirstOrDefault(x => x.ID == mapping.Product.ID);
  76. result.LicenseFees[mapping.License] = product != null
  77. ? product.Charge.PriceType == ProductPriceType.CostPlus
  78. ? product.NettCost * (100F + product.Charge.Markup) / 100F
  79. : product.Charge.Price
  80. : 0.0F;
  81. }
  82. }
  83. foreach (var timediscount in _properties.EngineProperties.TimeDiscounts)
  84. result.TimeDiscounts[timediscount.Months] = timediscount.Discount;
  85. foreach (var userdiscount in _properties.EngineProperties.UserDiscounts)
  86. result.UserDiscounts[userdiscount.Users] = userdiscount.Discount;
  87. return request.Respond().Status(ResponseStatus.OK).Content(Serialization.Serialize(result));
  88. }
  89. private static List<Tuple<int, Func<DateTime, DateTime>>> renewalPeriods = new List<Tuple<int, Func<DateTime, DateTime>>> {
  90. new(1, x => x.AddDays(-7)),
  91. new(3, x => x.AddDays(-14)),
  92. new(6, x => x.AddMonths(-1))
  93. };
  94. private int GetMonthDifference(DateTime date1, DateTime date2){
  95. var months = (date2.Year - date1.Year) * 12 + (date2.Month - date1.Month);
  96. if(date2.Day >= date1.Day){
  97. return months;
  98. }
  99. return months - 1;
  100. }
  101. private LicenseData GenerateLicense(LicenseRenewalRequest renewalRequest){
  102. var renewalPeriodInMonths = GetMonthDifference(renewalRequest.DateRenewed, renewalRequest.NewExpiry);
  103. var renewalAvailable = renewalPeriods
  104. .Where(x => renewalPeriodInMonths >= x.Item1)
  105. .MaxBy(x => x.Item1)
  106. .Item2(renewalRequest.NewExpiry);
  107. var newLicense = LicenseUtils.RenewLicense(renewalRequest.OldLicense, renewalRequest.DateRenewed, renewalRequest.NewExpiry, renewalAvailable, renewalRequest.Addresses);
  108. return newLicense;
  109. }
  110. private string NewCustomerCode(LicenseRenewalRequest renewalRequest)
  111. {
  112. // Try to build a 5 character abbreviation of the company name
  113. // is ACME Incorporated should become ACMIN
  114. // while P T Barnum should become PTBAR and so on
  115. String code = "";
  116. var codecomps = renewalRequest.Company.CompanyName
  117. .ToUpper()
  118. .Split(' ')
  119. .ToList();
  120. while (code.Length < 5 && codecomps.Any())
  121. {
  122. var chunk = new string(codecomps.First().Take(Math.Min(3,5-code.Length)).ToArray());
  123. code += chunk;
  124. codecomps.RemoveAt(0);
  125. }
  126. var codes = new Client<Customer>().Query(
  127. new Filter<Customer>(x => x.Code).BeginsWith(code),
  128. Columns.None<Customer>().Add(x => x.Code))?.Rows.Select(x => x.Get<Customer, string>(x => x.Code)
  129. ).ToList() ?? new List<string>();
  130. var i = 1;
  131. while(codes.Contains($"{code}{i:D3}"))
  132. i++;
  133. return $"{code}{i:D3}";
  134. }
  135. private Customer CreateNewCustomer(LicenseRenewalRequest renewalRequest){
  136. Logger.Send(LogType.Information, "", "Creating new customer");
  137. var customer = new Customer {
  138. Code = NewCustomerCode(renewalRequest),
  139. Name = renewalRequest.Company.CompanyName,
  140. ABN = renewalRequest.Company.ABN,
  141. Delivery = renewalRequest.Company.DeliveryAddress,
  142. Email = renewalRequest.Company.Email,
  143. Postal = renewalRequest.Company.PostalAddress
  144. };
  145. new Client<Customer>().Save(customer, "Created by License Renewal");
  146. return customer;
  147. }
  148. private CustomerDocument CreateNewCustomerDocument(Guid customerID, string fileName, string data){
  149. var document = new Document {
  150. FileName = fileName,
  151. Private = true,
  152. TimeStamp = DateTime.Now,
  153. Data = Encoding.UTF8.GetBytes(data)
  154. };
  155. document.CRC = CoreUtils.CalculateCRC(document.Data);
  156. new Client<Document>().Save(document, "");
  157. var documentType = new Client<DocumentType>()
  158. .Query(
  159. new Filter<DocumentType>(x => x.Code).IsEqualTo("LICENSE"),
  160. Columns.None<DocumentType>().Add(x => x.ID))
  161. .Rows.FirstOrDefault()?.Get<DocumentType, Guid>(x => x.ID) ?? Guid.Empty;
  162. if(documentType == Guid.Empty){
  163. Logger.Send(LogType.Error, "", "Document Type 'LICENSE' doesn't exist");
  164. }
  165. var customerDocument = new CustomerDocument();
  166. customerDocument.Type.ID = documentType;
  167. customerDocument.EntityLink.ID = customerID;
  168. customerDocument.DocumentLink.ID = document.ID;
  169. new Client<CustomerDocument>().Save(customerDocument, "Created by License Renewal");
  170. return customerDocument;
  171. }
  172. private void CreateInvoice(Guid customerID, LicenseRenewalRequest renewalRequest){
  173. var invoiceLines = new List<InvoiceLine>();
  174. var notes = new List<string>();
  175. foreach(var item in renewalRequest.LicenseTracking){
  176. var invoiceLine = new InvoiceLine {
  177. Description = $"{item.Caption} - {item.Users} Users @ ${item.Rate:F2} per user",
  178. ExTax = item.ExGST
  179. };
  180. invoiceLines.Add(invoiceLine);
  181. notes.Add(invoiceLine.Description);
  182. }
  183. var discountLine = new InvoiceLine {
  184. Description = $"${renewalRequest.Discount:F2} discount",
  185. ExTax = -renewalRequest.Discount
  186. };
  187. invoiceLines.Add(discountLine);
  188. notes.Add(discountLine.Description);
  189. var invoice = new Invoice {
  190. Date = DateTime.Today,
  191. Description = $"PRS License Renewal",
  192. };
  193. invoice.CustomerLink.ID = customerID;
  194. new Client<Invoice>().Save(invoice, "Created by License Renewal");
  195. foreach(var line in invoiceLines){
  196. line.InvoiceLink.ID = invoice.ID;
  197. }
  198. new Client<InvoiceLine>().Save(invoiceLines, "");
  199. var receipt = new Receipt {
  200. Date = DateTime.Today,
  201. Notes = $"PRS Renewal Invoice #{invoice.Number} ({renewalRequest.TransactionID})"
  202. };
  203. new Client<Receipt>().Save(receipt, "");
  204. var invoiceReceipt = new InvoiceReceipt {
  205. Notes = "Receipt for License Renewal",
  206. Amount = renewalRequest.Net
  207. };
  208. invoiceReceipt.InvoiceLink.ID = invoice.ID;
  209. invoiceReceipt.ReceiptLink.ID = receipt.ID;
  210. new Client<InvoiceReceipt>().Save(invoiceReceipt, "");
  211. }
  212. private IResponseBuilder RenewLicense(IRequest request)
  213. {
  214. var renewal = Serialization.Deserialize<LicenseRenewalRequest>(request.Content);
  215. if(renewal == null){
  216. return request.Respond().Status(ResponseStatus.BadRequest);
  217. }
  218. Logger.Send(LogType.Information, "", $"Request for license renewal from {renewal.Company.CompanyName}");
  219. var customerID = renewal.OldLicense.CustomerID;
  220. if(customerID == Guid.Empty){
  221. customerID = CreateNewCustomer(renewal).ID;
  222. renewal.OldLicense.CustomerID = customerID;
  223. }
  224. Logger.Send(LogType.Information, "", "Generating new license");
  225. var newLicense = GenerateLicense(renewal);
  226. var newLicenseData = LicenseUtils.EncryptLicense(newLicense);
  227. if(newLicenseData == null){
  228. Logger.Send(LogType.Error, "", "Encryption of new license failed!");
  229. return request.Respond().Status(ResponseStatus.InternalServerError);
  230. }
  231. Logger.Send(LogType.Information, "", "Generating customer document");
  232. var customerDocument = CreateNewCustomerDocument(customerID, $"{renewal.Company.CompanyName} - PRS License - {DateTime.Now:dd MMM yyyy}.txt", newLicenseData);
  233. Logger.Send(LogType.Information, "", "Creating invoice");
  234. CreateInvoice(customerID, renewal);
  235. LicenseRenewalResult result = new()
  236. {
  237. License = newLicenseData
  238. };
  239. return request.Respond()
  240. .Status(ResponseStatus.OK)
  241. .Content(Serialization.Serialize(result)); // Send just the encrypted data.
  242. }
  243. private IResponseBuilder Ping(IRequest request)
  244. {
  245. return request.Respond().Status(ResponseStatus.OK);
  246. }
  247. private IResponseBuilder HandleGET(IRequest request)
  248. {
  249. var endpoint = request.Target.Current?.Value.ToLower() ?? "";
  250. if (endpoint == nameof(Ping).ToLower())
  251. return Ping(request);
  252. return request.Respond().Status(ResponseStatus.NotFound);
  253. }
  254. private IResponseBuilder HandlePOST(IRequest request)
  255. {
  256. var endpoint = request.Target.Current?.Value.ToLower() ?? "";
  257. if (endpoint.Equals(nameof(LicenseFeeRequest).ToLower()))
  258. return RetrieveFees(request);
  259. if (endpoint.Equals(nameof(LicenseRenewalRequest).ToLower()))
  260. return RenewLicense(request);
  261. return request.Respond().Status(ResponseStatus.NotFound);
  262. }
  263. public override ValueTask<IResponse?> HandleAsync(IRequest request)
  264. {
  265. try
  266. {
  267. switch (request.Method.KnownMethod)
  268. {
  269. case RequestMethod.GET:
  270. return new ValueTask<IResponse?>(HandleGET(request).Build());
  271. case RequestMethod.POST:
  272. return new ValueTask<IResponse?>(HandlePOST(request).Build());
  273. default:
  274. Logger.Send(LogType.Error, ClientFactory.UserID, $"Request method {request.Method.RawMethod} unknown");
  275. return new ValueTask<IResponse?>(request.Respond().Status(ResponseStatus.MethodNotAllowed).Build());
  276. }
  277. }
  278. catch (Exception eListen)
  279. {
  280. Logger.Send(LogType.Error, ClientFactory.UserID, eListen.Message);
  281. return new ValueTask<IResponse?>(request.Respond().Status(ResponseStatus.InternalServerError).Build());
  282. }
  283. }
  284. }