Develop > Controller layer > Application developer > Tax codes > Value Added Tax (VAT)
Display VAT information on order item display pages
The main purpose of this customization is to display the tax rate on the client side. OrderCalculateCmd will need to be extended.
The VAT calculation already happens on the server side. This example demonstrates what VAT information can be retrieved from the server. You can update the own JSP to display such information as you require.
A typical customization is provided.
- Customize OrderCalculateCmd
If VATPromotionEngineOrderCalculateCmdImpl is used as the implementation of OrderCalculateCmd after the VAT tax model has been enabled, you can write a new com.ext.commerce.order.commands.ExtVATPromotionEngineOrderCalculateCmdImpl which extends from VATPromotionEngineOrderCalculateCmdImpl.
If VATOrderCalculateCmdImpl is used as the implementation of OrderCalculateCmd after the VAT tax model has been enabled, you can write a new com.ext.commerce.order.commands.ExtVATOrderCalculateCmdImpl which extends from VATOrderCalculateCmdImpl.
The example below uses VATPromotionEngineOrderCalculateCmdImpl. Customization for both implementations is the same.
Write an ExtVATPromotionEngineOrderCalculateCmdImpl which extends PromotionEngineOrderCalculateCmdImpl, override the performExecute() method to read the tax information from calculation command's custom property into ORDERITEMS.field2.
/** Main logic **/ public void performExecute() throws ECException final String strMethod = "performExecute"; ECTrace.entry( ECTraceIdentifiers.COMPONENT_ORDER, CLASS_NAME, strMethod); // call super's main logic first to get the custom property populated super.performExecute(); // get the hash map which saves each order item's tax information from custom property // using key->'itemTaxInfo' (ITEM_TAX_INFO = ‘itemTaxInfo'). The key in this hasp map is each // order item's id. The value is an object called ItemTaxInfo. You can go to ItemTaxInfo's // API to get the APIs you need. HashMap hshItemTaxInfo = getCustomProperties()==null? null: (HashMap)getCustomProperties().get(ITEM_TAX_INFO); if(hshItemTaxInfo == null) { return; } try { OrderItemAccessBean[] abOrderItems = getOrderItems(getOrders()[0]); for(int i=0; i<abOrderItems.length; i++) { ItemTaxInfo iti = (ItemTaxInfo)hshItemTaxInfo.get(abOrderItems[i].getOrderItemIdInEJBType()); if(iti == null) { continue;} // In order to make the codes simple, here we suppose there is only one product tax category // defined for one single catalog entry, one shipping tax category defined for the shipping // charge. This should reflect most customers situations. Integer[] taxCategoryIds = iti.getAppliedTaxCategories(); // First product tax rate, and shipping tax rate-->ORDERITEMS.FIELD2 BigDecimal taxRate = null; String taxCategoryName = null; BigDecimal shippingTaxRate = null; String shippingTaxCategoryName = null; for(int j=0; j<taxCategoryIds.length; j++) { TaxCategoryAccessBean abTaxcgry = new TaxCategoryAccessBean(); abTaxcgry.setInitKey_nCategoryId(taxCategoryIds[j].toString()); abTaxcgry.refreshCopyHelper(); // Get the tax rate from ItemTaxInfo object. if(abTaxcgry.getTypeIdInEJBType().compareTo(CalculationConstants.USAGE_SALES_TAX)==0) { if(taxRate == null) { taxRate = iti.getTaxRateForCategory(taxCategoryIds[j]); taxCategoryName = abTaxcgry.getName(); } } else if (abTaxcgry.getTypeIdInEJBType().compareTo(CalculationConstants.USAGE_SHIPPING_TAX)==0) { if (shippingTaxRate == null) { shippingTaxRate = iti.getTaxRateForCategory(taxCategoryIds[j]); shippingTaxCategoryName = abTaxcgry.getName(); } } } // String strTaxRate = (taxRate==null?new String("0"):taxRate.toString()); // Compose the tax rate information into a string object. // You can define the own string structure to save the tax rates. // Here we have one tax rate for sales tax, one for shipping tax. // We define the structure as: taxName1, taxRate1,taxName2,taxRate2, // then we can use ‘,' to get each value. String strTaxRate = new String(""); if(taxCategoryName!=null) { strTaxRate += taxCategoryName; } strTaxRate += ","; strTaxRate += (taxRate==null?new String("0"):taxRate.toString()); strTaxRate += ","; if(shippingTaxCategoryName!=null) { strTaxRate += shippingTaxCategoryName; } strTaxRate += ","; strTaxRate += (shippingTaxRate==null?new String("0"):shippingTaxRate.toString()); strTaxRate += ","; // set the string info ORDERITEMS.field2. abOrderItems[i].setField2(strTaxRate); abOrderItems[i].commitCopyHelper(); } } catch (CreateException e) { throw new ECSystemException(ECMessage._ERR_CREATE_EXCEPTION, CLASS_NAME, strMethod, new Object[] { e.toString()}, e); } catch (FinderException e) { throw new ECSystemException(ECMessage._ERR_FINDER_EXCEPTION, CLASS_NAME, strMethod, new Object[] { e.toString()}, e); } catch (NamingException e) { throw new ECSystemException(ECMessage._ERR_NAMING_EXCEPTION, CLASS_NAME, strMethod, new Object[] { e.toString()}, e); } catch (RemoteException e) { throw new ECSystemException(ECMessage._ERR_REMOTE_EXCEPTION, CLASS_NAME, strMethod, new Object[] { e.toString()}, e); } }
You will need to update the CMDREG table using the following SQL statement to register this new implementation.
update cmdreg set CLASSNAME='com.ext.commerce.order.commands.ExtVATPromotionEngineOrderCalculateCmdImpl' where interfacename = 'com.ibm.commerce.order.commands.OrderCalculateCmd' and storeent_id=@customer's store id
- Customize the Order SOI service assets
Two commands need to be extended in order to have both the order item detail and summary pages show VAT information. The two commands are: com.ibm.commerce.order.facade.server.commands.ComposeOrderDetailsCmdImpl and com.ibm.commerce.order.facade.server.commands.ComposeOrderSummaryCmdImpl
The example below uses ComposeOrderDetailsCmdImpl. Customization for both commands is the same.
Write a new com.ibm.commerce.order.facade.server.commands.ExtComposeOrderDetailsCmdImpl which extends ComposeOrderDetailsCmdImpl.
Override the composeOrderItemCharges method:
//This method sets the tax rate into user data for each order item. protected OrderItemChargesType composeOrderItemCharges(String orderItemId) throws ECException { final String strMethodName = "composePaymentInstruction"; if (LoggingHelper.isEntryExitTraceEnabled(LOGGER)) { LOGGER.entering(CLASSNAME, strMethodName); } // Get the OrderItemChargesType from super class. OrderItemChargesType oiCharge = super.composeOrderItemCharges(orderItemId); try { OrderItemAccessBean abOrderItem = new OrderItemAccessBean(); abOrderItem.setInitKey_orderItemId(orderItemId); abOrderItem.refreshCopyHelper(); // Get the tax rate info from ORDERITEMS.field2 String taxInfo = abOrderItem.getField2(); // retrieve the tax rates according to the field2's structure. if(taxInfo!=null) { StringTokenizer st = new StringTokenizer(taxInfo, ","); String taxName = (String)st.nextElement(); String taxRate = (String)st.nextElement(); String shippingTaxName = (String)st.nextElement(); String shippingTaxRate = (String)st.nextElement(); // Set the tax rate information into user data. You can specify the keys themselves. // These values are used in JSP to display VAT information. UserDataType userData = CommerceFoundationFactory.eINSTANCE.createUserDataType(); userData.getUserDataField().put("taxName", taxName); userData.getUserDataField().put("taxRate", taxRate); userData.getUserDataField().put("shippingTaxName", shippingTaxName); userData.getUserDataField().put("shippingTaxRate", shippingTaxRate); oiCharge.setUserData(userData); } } catch (CreateException e) { throw new ECSystemException(ECMessage._ERR_CREATE_EXCEPTION, getClass().getName(), strMethodName,new Object[] { e.toString() }, e); } catch (FinderException e) { throw new ECSystemException(ECMessage._ERR_FINDER_EXCEPTION, getClass().getName(), strMethodName,new Object[] { e.toString() }, e); } catch (NamingException e) { throw new ECSystemException(ECMessage._ERR_NAMING_EXCEPTION, getClass().getName(), strMethodName,new Object[] { e.toString() }, e); } catch (RemoteException e) { throw new ECSystemException(ECMessage._ERR_REMOTE_EXCEPTION, getClass().getName(), strMethodName,new Object[] { e.toString() }, e); } catch (Exception e) { if (LoggingHelper.isTraceEnabled(LOGGER)) { LOGGER.logp(LoggingHelper.DEFAULT_TRACE_LOG_LEVEL, CLASSNAME, strMethodName, e.toString()); } throw new ECApplicationException(ECMessage._ERR_GENERIC, getClass().getName(),strMethodName, new Object[] { e.getMessage() }); } return oiCharge; }
Override the composeTaxByTaxCategory method
// This method adds the tax rate to tax category's name for display usage. An example to demonstrate // how to get the order level tax rate. protected TaxByTaxCategoryType composeTaxByTaxCategory(OrderTaxAccessBean aabOrderTax, String astrCurrency) throws ECException {final String strMethodName = "composeTaxByTaxCategory"; if (LoggingHelper.isEntryExitTraceEnabled(LOGGER)) { LOGGER.entering(CLASSNAME, strMethodName); } // Get the TaxByTaxCategoryType from super class. TaxByTaxCategoryType taxByTaxCategory = super.composeTaxByTaxCategory(aabOrderTax,astrCurrency); try {String taxCategoryName = taxByTaxCategory.getTaxCategoryCode(); OrderItemAccessBean[] abOrderItems = getOrderItems(aabOrderTax.getOrdersIdInEJBType()); // One tax category has one corresponding rate corresponding at one time therefore we retrieve // the rate from ORDERITEMS.field2 using the tax category name. for(int i=0;i<abOrderItems.length; i++) { String taxInfo = abOrderItems[i].getField2(); if(taxInfo!=null) { StringTokenizer st = new StringTokenizer(taxInfo, ","); String taxName = (String)st.nextElement(); String taxRate = (String)st.nextElement(); String shippingTaxName = (String)st.nextElement(); String shippingTaxRate = (String)st.nextElement(); if(taxName.equals(taxCategoryName)) { taxCategoryName += " "; taxCategoryName += taxRate; taxCategoryName += "%"; break; } else if(shippingTaxName.equals(taxCategoryName)) { taxCategoryName += " "; taxCategoryName += shippingTaxRate; taxCategoryName += "%"; break; } } } taxByTaxCategory.setTaxCategoryCode(taxCategoryName); } catch (CreateException e) { throw new ECSystemException(ECMessage._ERR_CREATE_EXCEPTION, getClass().getName(), strMethodName,new Object[] { e.toString() }, e); } catch (FinderException e) { throw new ECSystemException(ECMessage._ERR_FINDER_EXCEPTION, getClass().getName(), strMethodName,new Object[] { e.toString() }, e); } catch (NamingException e) { throw new ECSystemException(ECMessage._ERR_NAMING_EXCEPTION, getClass().getName(), strMethodName,new Object[] { e.toString() }, e); } catch (RemoteException e) { throw new ECSystemException(ECMessage._ERR_REMOTE_EXCEPTION, getClass().getName(), strMethodName,new Object[] { e.toString() }, e); } if (LoggingHelper.isEntryExitTraceEnabled(LOGGER)) { LOGGER.exiting( CLASSNAME, strMethodName, taxByTaxCategory); } return taxByTaxCategory; }
The gerOrderItems method is defined for above method.
/** * Returns the OrderItems of the specified Order. * <br> * @param aabOrder the Order. * @return the OrderItems. * @exception ECException */ protected OrderItemAccessBean[] getOrderItems(Long nOrderId) throws ECException { final String strMethodName = "getOrderItems"; try { OrderAccessBean abOrder = new OrderAccessBean(); abOrder.setInitKey_orderId(nOrderId.toString()); abOrder.refreshCopyHelper(); return abOrder.getOrderItems(); } catch (CreateException e) { throw new ECSystemException(ECMessage._ERR_CREATE_EXCEPTION, getClass().getName(), strMethodName,new Object[] { e.toString() }, e); } catch (FinderException e) { throw new ECSystemException(ECMessage._ERR_FINDER_EXCEPTION, getClass().getName(), strMethodName,new Object[] { e.toString() }, e); } catch (NamingException e) { throw new ECSystemException(ECMessage._ERR_NAMING_EXCEPTION, getClass().getName(), strMethodName,new Object[] { e.toString() }, e); } catch (RemoteException e) { throw new ECSystemException(ECMessage._ERR_REMOTE_EXCEPTION, getClass().getName(), strMethodName,new Object[] { e.toString() }, e); } }
You will need to update the CMDREG table using the following SQL statement to register this new implementation.
update cmdreg set CLASSNAME='com.ext.commerce.order.facade.server.commands.ExtComposeOrderDetailsCmdImpl' where interfacename = 'com.ibm.commerce.order.facade.server.commands.ComposeOrderCmd+IBM_Details' and storeent_id=@customer's store id
You will need to do the same customization for com.ibm.commerce.order.facade.server.commands.ComposeOrderSummaryCmdImpl, and then update the CMDREG table.
update cmdreg set CLASSNAME='com.ext.commerce.order.facade.server.commands.ExtComposeOrderSummaryCmdImpl' where interfacename = 'com.ibm.commerce.order.facade.server.commands.ComposeOrderCmd+IBM_Summary' and storeent_id=@customer's store id
- Modify the JSP to display tax rates for order pages or order item pages
You will need to modify each order or order item page which should display a VAT rate. The following three JSPs are an example:
- /Stores/WebContent/Madisons/Snippets/Order/Cart/OrderItemDetail.jsp
- /Stores/WebContent/Madisons/ShoppingArea/CheckoutSection/SingleShipment/OrderItemDetails.jsp
- /Stores/WebContent/Madisons/ShoppingArea/CheckoutSection/SingleShipment/OrderItemDetailSummary.jsp
Add the following snippet into the existing JSP:
<c:out value="${orderItem.orderItemAmount.userData.userDataField['taxName']}"/> : <c:out value="${orderItem.orderItemAmount.userData.userDataField['taxRate']}"/>% <br /> VAT amount : $<c:out value="${orderItem.orderItemAmount.salesTax.value}"/>
Will produce the following:
Modify /Stores/WebContent/Madisons/ShoppingArea/CheckoutSection/SingleShipment/SingleShipmentOrderTotalsSummary.jsp to display order level VAT rate information:
<c:forEach var="categoryTax" items="${order.orderAmount.totalTaxByTaxCategory}"> <tr> <td style="border:1px dashed blue" class="total_details" id="WC_SingleShipmentOrderTotalsSummary_td_15"><c: out value="${categoryTax.taxCategoryCode}" />:</td> <td class="total_figures" id="WC_SingleShipmentOrderTotalsSummary_td_16">$<c:out value="${categoryTax.value}" /> </td> </tr> </c:forEach>
Will produce the following: