// Copyright (c) 2012, Outercurve Foundation. // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: // // - Redistributions of source code must retain the above copyright notice, this // list of conditions and the following disclaimer. // // - Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // - Neither the name of the Outercurve Foundation nor the names of its // contributors may be used to endorse or promote products derived from this // software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using System; using System.Globalization; using System.IO; using System.Text; using System.Xml; using System.Data; using System.Collections; using System.Collections.Generic; using System.Configuration; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; using ES = WebsitePanel.EnterpriseServer; using System.Threading; using WebsitePanel.EnterpriseServer; using WebsitePanel.Templates; namespace WebsitePanel.Ecommerce.EnterpriseServer { public class InvoiceController { private InvoiceController() { } #region Ecommerce v2.1.0 routines #region Helper methods public static string BuildAddXmlForInvoiceItems(List items) { XmlDocument xml = new XmlDocument(); XmlElement root = xml.CreateElement("items"); foreach (InvoiceItem item in items) { XmlElement node = xml.CreateElement("item"); node.SetAttribute("serviceid", item.ServiceId.ToString()); node.SetAttribute("itemname", item.ItemName); node.SetAttribute("typename", item.TypeName); node.SetAttribute("quantity", item.Quantity.ToString()); node.SetAttribute("total", item.Total.ToString(CultureInfo.InvariantCulture)); node.SetAttribute("subtotal", item.SubTotal.ToString(CultureInfo.InvariantCulture)); node.SetAttribute("unitprice", item.UnitPrice.ToString(CultureInfo.InvariantCulture)); root.AppendChild(node); } return root.OuterXml; } public static List CalculateInvoiceLinesForServices(List services) { List lines = new List(); foreach (int serviceId in services) { ProductType svc_type = ServiceController.GetServiceItemType(serviceId); // IServiceProvisioning controller = (IServiceProvisioning)Activator.CreateInstance( Type.GetType(svc_type.ProvisioningController)); // InvoiceItem[] ilines = controller.CalculateInvoiceLines(serviceId); foreach (InvoiceItem iline in ilines) { iline.SubTotal = iline.UnitPrice * iline.Quantity; iline.Total = iline.SubTotal; } // lines.AddRange(ilines); } // return lines; } #endregion public static int VoidCustomerInvoice(int invoiceId) { SecurityResult result = StorehouseController.CheckAccountIsAdminOrReseller(); if (!result.Success) return result.ResultCode; // void EcommerceProvider.VoidCustomerInvoice(ES.SecurityContext.User.UserId, invoiceId); // return 0; } public static Taxation GetCustomerTaxation(string contractId, string country, string state) { return ES.ObjectUtils.FillObjectFromDataReader( EcommerceProvider.GetCustomerTaxation(contractId, country, state)); } public static void CalculateInvoiceChargeAmounts(Taxation tax, List items, out decimal totalAmount, out decimal subTotalAmount, out decimal taxAmount) { totalAmount = subTotalAmount = taxAmount = 0; // calculate all invoice items foreach (InvoiceItem item in items) subTotalAmount += item.SubTotal; // totalAmount = subTotalAmount; // tax applies if (tax != null) { switch (tax.Type) { case TaxationType.Fixed: taxAmount = tax.Amount; totalAmount += taxAmount; break; case TaxationType.Percentage: taxAmount = (subTotalAmount / 100) * tax.Amount; totalAmount += taxAmount; break; case TaxationType.TaxIncluded: taxAmount = totalAmount - totalAmount * 100 / (100M + tax.Amount); subTotalAmount -= taxAmount; break; } } } public static int AddInvoice(string contractId, List invoiceLines, KeyValueBunch extraArgs) { ContractAccount account = ContractSystem.ContractController.GetContractAccountSettings(contractId); // read customer tax Taxation tax = GetCustomerTaxation(contractId, account[ContractAccount.COUNTRY], account[ContractAccount.STATE]); int taxationId = (tax == null) ? -1 : tax.TaxationId; // Calculate invoice amounts decimal totalAmount = 0, subTotalAmount = 0, taxAmount = 0; CalculateInvoiceChargeAmounts(tax, invoiceLines, out totalAmount, out subTotalAmount, out taxAmount); // align svcs suspend date int[] svcs = new int[invoiceLines.Count]; for (int i = 0; i < invoiceLines.Count; i++) svcs[i] = invoiceLines[i].ServiceId; DateTime sdateAligned = ServiceController.GetSvcsSuspendDateAligned(svcs, DateTime.Now); // StoreSettings settings = StorehouseController.GetStoreSettings(ES.SecurityContext.User.UserId, StoreSettings.SYSTEM_SETTINGS); // get invoice grace period in days int gracePeriod = Common.Utils.Utils.ParseInt(settings["InvoiceGracePeriod"], 0); // if (gracePeriod < 0) gracePeriod = 0; // DateTime created = DateTime.Now; DateTime dueDate = sdateAligned.AddDays(gracePeriod); // return AddInvoice(contractId, created, dueDate, taxationId, totalAmount, subTotalAmount, taxAmount, invoiceLines, extraArgs); } public static int AddInvoice(string contractId, DateTime created, DateTime dueDate, int taxationId, decimal totalAmount, decimal subTotalAmount, decimal taxAmount, List invoiceLines, KeyValueBunch extraArgs) { // try { Contract contract = ContractSystem.ContractController.GetContract(contractId); // ES.TaskManager.StartTask(SystemTasks.SOURCE_ECOMMERCE, SystemTasks.TASK_ADD_INVOICE); // build xml representation string invoiceLinesXml = BuildAddXmlForInvoiceItems(invoiceLines); // add invoice int result = EcommerceProvider.AddInvoice(contractId, created, dueDate, taxationId, totalAmount, subTotalAmount, taxAmount, invoiceLinesXml, StorehouseController.GetBaseCurrency(contract.ResellerId)); // check error if (result < 1) return result; // EXIT // build invoice number Invoice invoice = GetCustomerInvoiceInternally(result); StoreSettings settings = StorehouseController.GetStoreSettings(contract.ResellerId, StoreSettings.SYSTEM_SETTINGS); if (!String.IsNullOrEmpty(settings["InvoiceNumberFormat"])) { Hashtable options = new Hashtable(); options["ID"] = result; invoice.InvoiceNumber = StorehouseController.ApplyStringCustomFormat( settings["InvoiceNumberFormat"], options); } else { invoice.InvoiceNumber = result.ToString(); } // update invoice InvoiceController.UpdateInvoice(invoice.InvoiceId, invoice.InvoiceNumber, invoice.DueDate, invoice.Total, invoice.SubTotal, invoice.TaxationId, invoice.TaxAmount, invoice.Currency); // Hashtable parameters = new Hashtable(); parameters.Add(SystemTaskParams.PARAM_CONTRACT, contract); parameters.Add(SystemTaskParams.PARAM_INVOICE, invoice); parameters.Add(SystemTaskParams.PARAM_INVOICE_LINES, invoiceLines); parameters.Add(SystemTaskParams.PARAM_EXTRA_ARGS, extraArgs); TaskManager.UpdateParams(parameters); // return result; } catch (Exception ex) { throw ES.TaskManager.WriteError(ex); } finally { ES.TaskManager.CompleteTask(); } } public static int UpdateInvoice(int invoiceId, string invoiceNumber, DateTime dueDate, decimal total, decimal subTotal, int taxationId, decimal taxAmount, string currency) { return EcommerceProvider.UpdateInvoice(ES.SecurityContext.User.UserId, invoiceId, invoiceNumber, dueDate, total, subTotal, taxationId, taxAmount, currency); } public static Invoice GetCustomerInvoiceInternally(int invoiceId) { return ES.ObjectUtils.FillObjectFromDataReader( EcommerceProvider.GetCustomerInvoice(ES.SecurityContext.User.UserId, invoiceId)); } public static List GetCustomerInvoiceItems(int invoiceId) { return ES.ObjectUtils.CreateListFromDataReader( EcommerceProvider.GetCustomerInvoiceItems(ES.SecurityContext.User.UserId, invoiceId)); } public static string GetCustomerInvoiceFormattedInternally(int invoiceId, string cultureName) { Invoice invoice = GetCustomerInvoiceInternally(invoiceId); // return GetCustomerInvoiceFormattedInternally(invoice.ContractId, invoiceId, cultureName); } internal static string GetCustomerInvoiceFormattedInternally(string contractId, int invoiceId, string cultureName) { Contract contract = ContractSystem.ContractController.GetContract(contractId); ContractAccount accountSettings = ContractSystem.ContractController.GetContractAccountSettings(contractId); // return GetCustomerInvoiceFormattedInternally(contract, invoiceId, accountSettings, cultureName); } internal static string GetCustomerInvoiceFormattedInternally(Contract contract, int invoiceId, ContractAccount accountSettings, string cultureName) { // Invoice invoiceInfo = GetCustomerInvoiceInternally(invoiceId); // impersonate ES.SecurityContext.SetThreadPrincipal(contract.ResellerId); // load settings StoreSettings settings = StorehouseController.GetStoreSettings(contract.ResellerId, StoreSettings.NEW_INVOICE); // string templateBody = settings["HtmlBody"]; // Taxation invoiceTax = StorehouseController.GetTaxation(contract.ResellerId, invoiceInfo.TaxationId); // List invoiceLines = GetCustomerInvoiceItems(invoiceId); Dictionary invoiceServices = ServiceController.GetServicesDictionary(invoiceLines); // Template tm = new Template(templateBody); tm["Invoice"] = invoiceInfo; tm["InvoiceLines"] = invoiceLines; tm["InvoiceServices"] = invoiceServices; tm["Customer"] = accountSettings; tm["Tax"] = invoiceTax; StringWriter writer = new StringWriter(); try { // Preserve an original thread culture CultureInfo originalCulture = Thread.CurrentThread.CurrentCulture; // if (!String.IsNullOrEmpty(cultureName)) { try { CultureInfo formattingCulture = new CultureInfo(cultureName); // Empty currency symbol to supporty 3-letters ISO format // hardcorded in HTML templates formattingCulture.NumberFormat.CurrencySymbol = String.Empty; // Set formatting culture Thread.CurrentThread.CurrentCulture = formattingCulture; } catch (Exception ex) { TaskManager.WriteWarning("Wrong culture name has been provided. Culture name: {0}.", cultureName); TaskManager.WriteWarning(ex.StackTrace); TaskManager.WriteWarning(ex.Message); } } // Process template tm.Evaluate(writer); templateBody = writer.ToString(); // Revert the original thread's culture back Thread.CurrentThread.CurrentCulture = originalCulture; } catch (ParserException ex) { return String.Format("Error in template (Line {0}, Column {1}): {2}", ex.Line, ex.Column, ex.Message); } return templateBody; } public static List GetUnpaidInvoices(int resellerId) { return ES.ObjectUtils.CreateListFromDataReader( EcommerceProvider.GetUnpaidInvoices(ES.SecurityContext.User.UserId, resellerId)); } #endregion #region New implementation public static List GetInvoicesItemsToActivate(int resellerId) { return ES.ObjectUtils.CreateListFromDataReader( EcommerceProvider.GetInvoicesItemsToActivate(ES.SecurityContext.User.UserId, resellerId)); } public static List GetInvoicesItemsOverdue(int resellerId, DateTime dueDate) { return ES.ObjectUtils.CreateListFromDataReader( EcommerceProvider.GetInvoicesItemsOverdue(ES.SecurityContext.User.UserId, resellerId, dueDate)); } public static int SetInvoiceItemProcessed(int invoiceId, int itemId) { return EcommerceProvider.SetInvoiceItemProcessed(invoiceId, itemId); } #endregion #region Provisioning routines #endregion } }