November Happy Hour will be moved to Thursday December 5th.

Third Party Tax Calculator and Saving Taxes in EPiServer 11

Vote:
 

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
Vote:
 

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
Vote:
 

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
Vote:
 

Hello,

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

Thank you!

Kevin Larsen

#199181
Nov 19, 2018 15:12
Vote:
 

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

#199415
Nov 26, 2018 17:09
Vote:
 

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
Vote:
 

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 26, 2018 23:57
This topic was created over six months ago and has been resolved. If you have a similar question, please create a new topic and refer to this one.
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.