DEVELOPING A B2B QUOTING SYSTEM IN DEMANDWARE Ana C. Rodriguez November, 2015 LYONSCG White Paper: Developing a B2B Quoting System in Demandware 1 DEVELOPING A B2B QUOTING SYSTEM Forrester Research expects B2B eCommerce sales to reach $780 billion and represent 9.3 percent of all B2B sales by the end of 2015. So, it’s no wonder more B2B companies are creating an online experience – one that reflects the customer expectations set by B2C eCommerce. B2B companies understand they can realize many of the same benefits consumer brands and retailers enjoy. Providing wholesalers, purchasing agents, clients and other vendors with an online store can enhance supply chain efficiencies, reduce customer service costs, eliminate order errors, and free up a sales force to focus on expanding market share. However, the nature of B2B creates some special needs when it comes to developing an online store. One of those is that not all customers are alike. B2B must deal with the fact that prices fluctuate often with customer-specific pricing and volume-based discounts. And so, a quoting system is required. Automating the quoting process to provide a rapid response can make the all the difference when it comes to increasing order conversions. At the same time, it can sometimes require custom development to create a system that meets a company’s specific needs. LYONSCG recently developed a quoting solution for a Demandware client that’s very scalable and easy to maintain. Let’s take a look at how we went about it. DESIGN OF B2B QUOTATION SYSTEM IN DEMANDWARE The use case below represents the solution divided in three steps: 1) create quote, 2) place order (based on a quote) and 3) cancel quote. Figure 1: Quoting System Use Case LYONSCG White Paper: Developing a B2B Quoting System in Demandware 2 Create quote will have as final result a quote, built through the Storefront checkout process and stored in a Product List with specific quoting custom attributes set. Place order is the process of creating an actual order based on a quote. All the discounts and price adjustments set in a quote should be honored when the order is placed. Finally, cancel quote is the process to delete a quote permanently. User could be a storefront user or a CSR (Customer Service Representative), depending on business requirements. The original implementation uses CSSuite (Customer Service Suite) to support the solution and allows quotes to be handled exclusively by CSRs. IMPLEMENTATION Create Quote Figure 2: Quoting System Create Quote Use Case Business requirements: Quotes must be created from Storefront Products can be added to or removed from shopping cart Product quantities can be adjusted Coupons and promotions can be applied Shipping address and shipping method are mandatory Billing step must be skipped (billing address & payment info will be provided when order is placed) Review quote page is displayed prior to saving the quote Saving a quote displays a quote confirmation page Quotes are stored in a Product List LYONSCG White Paper: Developing a B2B Quoting System in Demandware 3 Create quote is implemented as an alternative flow on Storefront checkout. Figure 3: Create Quote Detailed Use Case Step: Action: Create Shopping Cart Shipping Add Products to Cart Shipping Address Remove Products from Cart Shipping Method Change Product Quantities Apply Coupons Apply Promotions Billing Review Quote Quote Confirmation SKIP Confirm Quote Quote is created! LYONSCG White Paper: Developing a B2B Quoting System in Demandware 4 Implementation steps: 1) User access storefront 2) User creates shopping cart (same process as checkout) 3) User fills out shipping address (same process as checkout) 4) User selects shipping method (same process as checkout) 5) System skips billing step To create an alternate flow on checkout and skip the billing step an action must be added to the shipping pipeline (COShipping). Figure 4: createQuote Action on Shipping Pipeline The new action will be triggered by a button on shipping page (something like “Continue Create Quote”) and invoke a call on COShipping pipeline. - New action on singleshipping form: <action formid="save" valid-form="true"/> <action formid="selectAddress" valid-form="false"/> <action formid="createQuote" valid-form="true"/> LYONSCG White Paper: Developing a B2B Quoting System in Demandware - 5 “Continue Create Quote” button on singleshipping template: <button class="button-fancy-large" type="submit" name= "${pdict.CurrentForms.singleshipping.shippingAddress.createQuote.htmlName}" value="Create Quote"> <span>Continue Create Quote &lt;</span> </button> Figure 5: Continue Create Quote button on Storefront Checkout - New action call and jump node on COShipping pipeline (as seen on Figure 4) LYONSCG White Paper: Developing a B2B Quoting System in Demandware 6) 6 System displays review quote page: Review quote page displays pretty much the same information as review order page, minus billing address and billing info sections. A specific pipeline for quoting was created (COCreateQuote) to hold all the pipeline calls. Note below the pipeline COCreateQuote-Summary looks like COSummary-Start, except validate payment call was removed. Figure 6: COCreateQuote-Summary Pipeline LYONSCG White Paper: Developing a B2B Quoting System in Demandware 7) 7 User confirms create quote: Clicking on Save Quote on review quote page will save the quote and redirect the user to confirmation quote page. Create quote process consists of: - Validate quote (basket) Validation prior to create a quote is much simpler than the validation performed before placing an actual order: make sure the basket has products and validate cart for checkout. What is NOT validated nor processed: payments (validation and handling) and personal information. Figure 7: Validation on COCreateQuote-Start Pipeline LYONSCG White Paper: Developing a B2B Quoting System in Demandware - 8 Create product list Figure 8: Create Product List on COCreateQuote-Start Pipeline Create product list, simply put, replaces create order on the pipeline. Once a product list is created, the custom attributes values are set manually using a transactional script (CreateProductListFromBasket). Below is the representation of custom attributes for product list and product list item when creating quotes. Figure 9: ProductList Custom Attributes Object Representation LYONSCG White Paper: Developing a B2B Quoting System in Demandware 9 Figure 10: ProductListItem Custom Attributes Object Representation Notice on the representations above the function GetPriceAdjustments. This function iterates through each price adjustment (basket and product) and returns a JSON string that will be assigned to the custom attribute (quotePriceAdjustments on ProductList, quoteProductPriceAdjustments on ProductListItem). These attributes will be retrieved later (during place order) so all discounts and price adjustments applied to the quote can be honored when an order is placed. function getPriceAdjustments(priceAdjustments : Collection) { var priceAdjustmentList = []; var it : Iterator = priceAdjustments.iterator(); while (it.hasNext()) { var priceAdjustment : PriceAdjustment = it.next(); var priceAdjustmentDescription = (priceAdjustment.basedOnCoupon ? "Coupon: " : "") + priceAdjustment.lineItemText; var priceAdjustmentObject = JSON.stringify({ description : priceAdjustmentDescription ,amount: priceAdjustment.priceValue }); priceAdjustmentList.push(priceAdjustmentObject); } return priceAdjustmentList; } LYONSCG White Paper: Developing a B2B Quoting System in Demandware - 10 Clear basket When an actual order is placed, the basket is destroyed automatically. Without order placement – which is the case in quoting – while it’s not possible to destroy the basket, it must be cleared manually. The script ClearBasket is the last transactional call made before finalizing the creation of a quote. It will not only clear the basket, but it will also return two objects: one containing quote totals and a second one with shipment details (shipping address and shipping method). Figure 11: QuoteTotals Object Representation Figure 12: Shipment Object Representation The basket is finally cleared when all the products are removed. … var it : Iterator = basket.allProductLineItems.iterator(); while (it.hasNext()) { var pli = it.next(); basket.removeProductLineItem(pli); } … LYONSCG White Paper: Developing a B2B Quoting System in Demandware 8) 11 System displays confirmation quote page: The confirmation quote page looks like the confirmation order page, minus billing address and payment information. The data displayed it’s loaded from Quote Totals and Shipment objects (see step 7 for implementation details). Figure 13: Confirmation Quote Page LYONSCG White Paper: Developing a B2B Quoting System in Demandware 12 Place Order Figure 14: Quoting System Place Order Use Case Business requirements: No changes can be made to quotes Price adjustments and coupons applied to a quote must be honored Billing address must be entered Payment method must be entered Placing an order will create an order based on the quote Quote will be deleted permanently from the system Implementation: Place order consists of loading the quote into a basket and adding billing address and payment info for order placement. LYONSCG White Paper: Developing a B2B Quoting System in Demandware 13 Figure 15: Place Order Detailed Use Case Step: Select Quote Billing Order Confirmation Action: Set Product List Items Billing Address Order is created! Set Price Adjustments Payment Method Quote is deleted! Set Customer Set Shipping Address Set Shipping Method Implementation Steps: 1) User selects quote There are many ways to implement the selection of a quote. The implementation served as reference for this document used CSSuite (Customer Service Suite) to list customers’ quotes and allow a CSR (Customer Service Representative) to select one. Implementing a list of customers’ quotes on Storefront would be another doable option. Once a quote is selected, it must be loaded into a basket so order placement can get started. All the data contained in the quote must be loaded into the basket: products, price adjustments, customer info, shipping address and shipping method. LYONSCG White Paper: Developing a B2B Quoting System in Demandware - 14 Loading products from quote into basket To load products from a quote into the basket, simply iterate through the quote (product list) items and use the appropriate method to add each product to the basket. //iterate through the quote items and add each one of them to the basket var it : Iterator = quote.getProductItems().iterator(); while (it.hasNext()) { var pli : ProductListItem = it.next(); //add product to basket var productLineItem : ProductLineItem = basket.createProductLineItem(pli.getProductID(), defaultShipment); productLineItem.setQuantityValue(pli.getQuantityValue()); //adjust product price on basket productLineItem.custom.quoteProductPrice = pli.custom.quoteProductPrice; //set product price adjustment if ("quoteProductPriceAdjustments" in pli.custom) { createPriceAdjustments(productLineItem, pli.custom.quoteProductPriceAdjustments); } } LYONSCG White Paper: Developing a B2B Quoting System in Demandware 15 Product price adjustments persisted in the custom attribute quoteProductPriceAdjustments in ProductListItem object will be iterated (see createPriceAdjustments function below) so the adjustments can be set on the basket. function createPriceAdjustments(lineItemCtnr : LineItemCtnr, priceAdjustmentList : List) { for (var i=0; i < priceAdjustmentList.length; i++) { var priceAdjustmentObject = JSON.parse(priceAdjustmentList[i]); var adjustment : PriceAdjustment = lineItemCtnr.createPriceAdjustment(UUIDUtils.createUUID()); try { adjustment.setLineItemText(priceAdjustmentObject.description); adjustment.setPriceValue(priceAdjustmentObject.amount); } catch (e) { lineItemCtnr.removePriceAdjustment(adjustment); return PIPELET_ERROR; } } } - Set price adjustments Price adjustments applied to the quote will be processed the same way the product price adjustments described above. if ("quotePriceAdjustments" in quote.custom) { createPriceAdjustments(basket, quote.custom.quotePriceAdjustments); } LYONSCG White Paper: Developing a B2B Quoting System in Demandware - 16 Set customer Figure 16: Basket Customer Attributes Representation - Set shipping address Figure 17: Basket Shipping Address Attributes Representation LYONSCG White Paper: Developing a B2B Quoting System in Demandware - 17 Set shipping method To set the shipping method, iterate through all the shipping methods to find the one that matches what’s set on the quote and should be set on the basket. var shippingMethod = null; if ('quoteShippingMethodID' in quote.custom) { var itShippingMethods : Iterator = ShippingMgr.getAllShippingMethods().iterator(); while (itShippingMethods.hasNext()) { var shippingMethod : ShippingMethod = itShippingMethods.next(); if (shippingMethod.ID == quote.custom.quoteShippingMethodID) { defaultShipment.setShippingMethod(shippingMethod); break; } } } Using the code below, it’s possible to override the shipping price, honoring any shipping promotion that was applied at the time the quote was created. defaultShipment.getStandardShippingLineItem().setPriceValue( quote.custom.quoteShippingTotalPrice); 2) User fills out billing address (same process as checkout) 3) User selects payment method (same process as checkout) 4) User confirm place order LYONSCG White Paper: Developing a B2B Quoting System in Demandware When user confirms place an order, two things happen: - An actual order is created (same process as checkout) The basket will go through all the necessary validations to place an order. - Quote is deleted Once order is placed, the quote is deleted permanently from the system. Cancel Quote Figure 18: Quoting System Cancel Quote Use Case 18 LYONSCG White Paper: Developing a B2B Quoting System in Demandware 19 Business requirements: A quote can be cancelled at any time Quote will be deleted permanently from the system Implementation: The solution this document was based on used CSSuite to implement this last part. The process consists of offering the user a list of quotes where any of them can actually be deleted. To delete a quote (product list) without writing any code, simply use pipelets (see Figure 18 for reference). CONCLUSION Although quoting is a large implementation, breaking the requirements into small parts and implementing each one as an independent module simplified the process. Also, leveraging the Storefront code was definitely key to reducing coding complexity and avoiding having to “reinvent the wheel”. Using this document as base reference, new functionalities can be added easily, depending on the customers’ requirements. LYONSCG White Paper: Implementing a Quoting System in Demandware 1 Thank You About LYONSCG LYONSCG is the industry’s premier eCommerce digital agency, serving brand, retail, and B2B organizations with tailored eCommerce solutions that maximize online potential. Headquartered in Chicago, the firm offers a full range of services beginning with digital strategy and digital marketing and extending through experience design, platform implementation, application development, hosting and support. The approach is holistic—to provide every client with a creative, robust and increasingly profitable eCommerce website. LYONSCG is eCommerce Realized! LYONSCG 20 N. Wacker Drive, Suite 1750 Chicago, IL 60606 P: 312.506.2020 F: 312.506.2022 W: www.lyonscg.com All information contained within this document is proprietary and confidential © Lyons Consulting Group 2015