Third Party Tax Calculator and Saving Taxes in EPiServer 11

 

Hello,

We use a thrid party service to calculate the taxes for our orders on our site.  I've been in the process of moving over to using the new order system and have ran into a problem with saving the tax value that is returned back from the service. (CMS 11.9.1 and Commerce 11.8.5)

Below is what we used to use to save the tax that we received.

Order Form

// Tax and total order form
orderForm.TaxTotal = orderFormTaxTotal;
orderForm.Total = orderForm.SubTotal + orderForm.ShippingTotal + orderForm.HandlingTotal + orderForm.TaxTotal;
orderForm.AcceptChanges();

Order Group

// Tax and total order
orderGroup.TaxTotal = orderTaxTotal;
orderGroup.Total = orderGroup.SubTotal + orderGroup.ShippingTotal + orderGroup.HandlingTotal + orderGroup.TaxTotal;
orderGroup.AcceptChanges();

I converted from an IOrderGroup to an OrderGroup and went through the process and saw that it hit everything correctly, but when I got to the next step in our checkout process I saw that it wasn't displaying the tax and the tax values were not saved in the database.  I made changes to use the IOrderGroup instead of the OrderGroup and when I got to the point of changing the above code I didn't see a way to save the tax value.

Is there a way of saving Tax this way?

Thank you,

Kevin Larsen

#199123 Edited, Nov 15, 2018 23:02
  • Quan Mai
    Member since: 2011
     

    If you implement your 3rd party tax service as an implementation of ITaxCalculator, and then use IOrderRepository.Save, the taxes should be calculated and saved. not sure how they are set them now?

    #199139 Nov 16, 2018 11:49
  •  

    Hi Quan,

    Below is how the code is ran currently (modified to pass in the parameters differently as the old custom implementation didn't work, and also for content of the code)

    public class TaxRateOrderActivity
    {
    	public Guid OrderActivityId { get; set; }
    
    	private static class OperationTypes
    	{
    		public static string Post = "post";
    		public static string Get = "get";
    	}
    
    	public bool ProcessActivity(OrderGroup orderGroup, string accountNumber, string licenseKey, string serviceUrl, string companyCode, string operation, string docType, string defaultTaxCode)
    	{
    		// Pre-condition information
    		OrderGroupHelper orderGroupHelper = new OrderGroupHelper((OrderGroup)orderGroup);
    		OrderAddress shippingAddress = orderGroupHelper.GetOrderShippingAddress();
    		List<LineItem> lineItems = orderGroupHelper.GetLineItems();
    
    		// Check if enough information to process
    		if (shippingAddress == null || lineItems == null || lineItems.Count() == 0)
    			return false;
    
    		// Tax Service Client
    		TaxSvc taxSvc = new TaxSvc();
    		// Header Level Parameters
    		// Required Header Parameters
    		// Set required values for taxSvc
    		// Optional Header Parameters
    		// Set optional values for taxSvc
    
    		// Base Request Info
    		GetTaxRequest getTaxRequest = new GetTaxRequest();
    		getTaxRequest.DocDate = DateTime.Now;
    		getTaxRequest.CompanyCode = companyCode;
    		getTaxRequest.DetailLevel = DetailLevel.Tax;
    		getTaxRequest.Commit = operation == OperationTypes.Post;
    		getTaxRequest.DocType = (DocumentType)Enum.Parse(typeof(DocumentType), docType);
    		getTaxRequest.CustomerUsageType = "";
    		// Order Currency
    		getTaxRequest.CurrencyCode = orderGroupHelper.orderGroup.BillingCurrency;
    		// Default Origin 
    		IWarehouse primaryWarehouse = WarehouseHelper.ListAllWarehouses().FirstOrDefault(w => w.IsPrimary);
    		getTaxRequest.OriginAddress = primaryWarehouse.ContactInformation.ToAvalaraAddress();
    		// Default Destination 
    		Address shipToAddress = shippingAddress.ToAvalaraAddress();
    		getTaxRequest.DestinationAddress = shipToAddress;
    		// Customer Info
    		CustomerContact customerContact = CustomerContext.Current.GetContactById(orderGroup.CustomerId);
    
    		getTaxRequest.CustomerCode = customerContact.Email;
    		if (customerContact.ContactOrganization != null && !string.IsNullOrEmpty(customerContact.ContactOrganization.Name))
    		{
    			getTaxRequest.CustomerCode = customerContact.ContactOrganization.Name;
    		}
    
    		// Get taxes by shipment 
    		decimal orderTaxTotal = new decimal(0);
    		foreach (OrderForm orderForm in orderGroup.OrderForms)
    		{
    			decimal orderFormTaxTotal = new decimal(0);
    			foreach (Shipment shipment in orderForm.Shipments)
    			{
    				// If no shipping method, then not ready for tax
    				if (string.IsNullOrEmpty(shipment.ShippingMethodName))
    					continue;
    
    				// Post options
    				if (operation == OperationTypes.Post && orderGroupHelper.orderGroup is PurchaseOrder)
    				{
    					getTaxRequest.PaymentDate = ((PurchaseOrder)orderGroupHelper.orderGroup).Created;
    					getTaxRequest.PurchaseOrderNo = string.Format("{0}-{1}", ((PurchaseOrder)orderGroupHelper.orderGroup).TrackingNumber, shipment.ShipmentId);
    				}
    				// Line Data
    				getTaxRequest.Lines.Clear();
    				foreach (LineItem lineItem in ShipmentHelper.GetShipmentLineItems(shipment))
    				{
    					// Updating CatalogEntryId to Code
    					CatalogEntry catEntry = CatalogEntry.Create(lineItem.Code);
    
    					Line taxLineItem = new Line();
    					taxLineItem.No = lineItem.LineItemId.ToString();
    					// Updating CatalogEntryId to Code
    					taxLineItem.ItemCode = lineItem.Code;
    					taxLineItem.Qty = Convert.ToDouble(lineItem.Quantity);
    					taxLineItem.Amount = lineItem.ExtendedPrice;
    
    					// if warehouse code is set, then use, else primary
    					IWarehouse warehouse = primaryWarehouse;
    					if (lineItem.WarehouseCode.Trim().Length > 0)
    					{
    						warehouse = WarehouseHelper.GetWarehouse(lineItem.WarehouseCode);
    					}
    
    					// Source/Destination address
    					taxLineItem.OriginAddress = warehouse.ContactInformation.ToAvalaraAddress();
    					taxLineItem.DestinationAddress = shipToAddress;
    
    					// Description
    					taxLineItem.Description = catEntry.GetDisplayName();
    
    					// Product tax code
    					string taxCode = catEntry.GetTaxCode();
    					// Is always null
    					if (string.IsNullOrEmpty(taxCode))
    					{
    						taxCode = defaultTaxCode;
    					}
    					taxLineItem.TaxCode = taxCode;
    
    					getTaxRequest.Lines.Add(taxLineItem);
    				}
    				// Add line
    				Line taxLineItem = new Line();
    				// Set taxLineItem values
    				getTaxRequest.Lines.Add(taxLineItem);
    
    				// Get Taxes
    				GetTaxResult getTaxResult = taxSvc.GetTax(getTaxRequest);
    
    				// Log result/messages in order group notes
    				string resultMessages = string.Format("Result Code: {0}", getTaxResult.ResultCode.ToString());
    				foreach (Message message in getTaxResult.Messages)
    				{
    					resultMessages += string.Format("{0}: {1}; ", message.Name, message.Summary);
    				}
    				orderGroupHelper.AddNote("Tax", "/orderactivity/tax/log", string.Format("{0} : {1}", shipment.ShipmentId.ToString(), shipment.ShippingMethodName), resultMessages);
    
    				// Check for failure
    				if (getTaxResult.ResultCode != SeverityLevel.Success)
    				{
    					return false;
    				}
    
    				// Total Tax and save to shipment
    				decimal shipmentTaxTotal = new decimal(0.0);
    				foreach (TaxLine taxLine in getTaxResult.TaxLines)
    				{
    					shipmentTaxTotal += taxLine.Tax;
    				}
    				shipment.SetShipmentTaxStatus(getTaxRequest.ExemptionNo);
    				shipment.SetShipmentTax(shipmentTaxTotal);
    				shipment.SetTaxDocCode(getTaxResult.DocCode);
    				shipment.SetShipmentTaxStatus(taxExemptStatus);
    				shipment.AcceptChanges();
    				orderFormTaxTotal += shipmentTaxTotal;
    			}
    			// Tax and total order form
    			orderForm.TaxTotal = orderFormTaxTotal;
    			orderForm.Total = orderForm.SubTotal + orderForm.ShippingTotal + orderForm.HandlingTotal + orderForm.TaxTotal;
    			orderForm.AcceptChanges();
    			orderTaxTotal += orderFormTaxTotal;
    		}
    		// Tax and total order
    		orderGroup.TaxTotal = orderTaxTotal;
    		orderGroup.Total = orderGroup.SubTotal + orderGroup.ShippingTotal + orderGroup.HandlingTotal + orderGroup.TaxTotal;
    		orderGroup.AcceptChanges();
    
    		return true;
    	}
    }

    The ProcessActivity is called after a user selects their shipping method and moves to the next step. 

    I don't completely understand what you mean by implementing the ITaxCalculator?  I tried inheriting the ITaxCalculator, but then had to implement some interface members that I'm guessing would have to have the code from the ProcessActivity worked into them?  Apologies for not understanding.

    Thank you,

    Kevin Larsen

    #199158 Edited, Nov 16, 2018 16:43
  •  

    Hello,

    Any additional feedback or information?  I'm stuck on this at the moment.

    Thank you!

    Kevin Larsen

    #199181 Nov 19, 2018 15:12
  • Mark Hall
    Member since: 2011
     

    https://world.episerver.com/documentation/developer-guides/commerce/orders/calculating-orders-intro/calculating-orders-tax-calculator/

    #199415 Nov 26, 2018 17:09
  •  

    Hi,

    I overrode the CalculateTaxTotal and can return a value in the step of our checkout process.  The problem now is that I want to be able to save that value in the Order Group/Order Form to be able to pull it again in the next steps or if the user leaves the checkout and comes back later.  The service we use charges a small fee each time it's hit, so I wouldn't want to call this each time the tax value is needed to be displayed to the user.

    I looked at the Quicksilver site, but the order information (which has the tax total) is saved in a cookie and not in the Order Group/Order Form.

    Is there a way to save the tax total to the Order Group/Order Form before the order is placed?  I saw similar questions that led to this question being asked but never answered.

    Thank you,

    Kevin Larsen

    #199418 Nov 26, 2018 18:29
  • Mark Hall
    Member since: 2011
     

    I would store the request and resposne serialized as JSON or xml to IOderForm.Properties["TaxRequest"] or ILineItem.Properties["TaxRequest"] andIOderForm.Properties["TaxResponse"] or ILineItem.Properties["TaxResponse"] If the json or xml of the request is the same then use the saved response to caculate the tax or just return the tax total already calculated.

    #199420 Edited, Nov 27, 2018 0:00