Documentation Home

Add To Cart Workflow For Heat Clinic Tutorial

As a developer, you may be presented with interesting requirements in your implementation of Broadleaf Commerce. For example, a restriction preventing adding a hot sauce with heat range 1 into a cart that already has a hot sauce with heat range 5 might be desirable. Let's hook into the Add to Cart Workflow to enforce this!

You may notice that this tutorial is very similar to the other tutorial on modifying the order submit workflow

Create a new activity

We need to add an activity that checks for the heat range restriction. In the site project, we'll create ValidateHeatRangeRestrictionActivity.java in the com.mycompany.order.service.workflow.add package with the following contents:

@Component("validateHeatRangeRestrictionActivity")
public class ValidateHeatRangeRestrictionActivity extends BaseActivity {
    private static final Log LOG = LogFactory.getLog(ValidateHeatRangeRestrictionActivity.class);

    public static final int ORDER = 1500;

    @Resource(name = "blCatalogService")
    protected CatalogService catalogService;

    public ValidateHeatRangeRestrictionActivity() {
        setOrder(ORDER);
    }

    @Override
    public ProcessContext execute(ProcessContext context) throws Exception {
        // Get our seed data
        CartOperationRequest request = (CartOperationRequest) context.getSeedData();
        Long skuId = request.getItemRequest().getSkuId();
        Order cart = request.getOrder();

        Sku sku = catalogService.findSkuById(skuId);
        Product product = sku.getProduct();
        Integer heatRange = getHeatRange(product);

        // Heat Range will be null for non-hot sauce products
        if (heatRange != null && heatRange == 1) {
            for (DiscreteOrderItem doi : cart.getDiscreteOrderItems()) {
                Integer doiHeatRange = getHeatRange(doi.getProduct());
                if (doiHeatRange != null && doiHeatRange == 5) {
                    throw new InvalidSauceHeatRangeException("Trying to add heat range 1 when heat range 5 in cart");
                }
            }
        }

        return context;
    }

    /**
     * @return the heatRange attribute of a product if it exists -- null otherwise
     */
    protected Integer getHeatRange(Product product) {
        ProductAttribute heatRangeAttr = product.getProductAttributes().get("heatRange");

        // Heat Range will be null for non-hot sauce products
        if (heatRangeAttr != null) {
            try {
                return Integer.parseInt(heatRangeAttr.getValue());
            } catch (Exception e) {
                // All The Exceptions!! (We'll return null, do nothing)
            }
        }

        return null;
    }
}

We also need the exception that gets thrown if the heat range isn't high enough:

public class InvalidSauceHeatRangeException extends RuntimeException {

    private static final long serialVersionUID = -8828778897245219116L;

    public InvalidSauceHeatRangeException() {
        super();
    }

    public InvalidSauceHeatRangeException(String message, Throwable cause) {
        super(message, cause);
    }

    public InvalidSauceHeatRangeException(String message) {
        super(message);
    }

    public InvalidSauceHeatRangeException(Throwable cause) {
        super(cause);
    }

}

Add our activity to the workflow

XML configuration to add the new activity to the list of add item activities:

<bean id="customAddItemWorkflowActivities" class="org.springframework.beans.factory.config.ListFactoryBean">
    <property name="sourceList">
        <list>
            <ref bean="validateHeatRangeRestrictionActivity"/>
        </list>
    </property>
</bean>
<bean class="org.broadleafcommerce.common.extensibility.context.merge.LateStageMergeBeanPostProcessor">
    <property name="sourceRef" value="customAddItemWorkflowActivities"/>
    <property name="targetRef" value="blAddItemWorkflowActivities"/>
</bean>

Java configuration to add the new activity to the list of add item activities:

@Merge("blAddItemWorkflowActivities")
public List<?> customAddItemWorkflowActivities(ValidateHeatRangeRestrictionActivity validateHeatRangeRestrictionActivity) {
    return Arrays.asList(validateHeatRangeRestrictionActivity);
}

and our add item restriction is in place. Notice that order does need to specified because the activity sets its own implicit order in its constructor.

Handle the error scenario

We want to let the customer know about this restriction if their add was unsuccessful. We'll modify the catch block in the addJson method in CartController to handle this exception.

...
} catch (AddToCartException e) {
if (e.getCause() instanceof RequiredAttributeNotProvidedException) {
    responseMap.put("error", "allOptionsRequired");
} else if (e.getCause() instanceof InvalidSauceHeatRangeException) {
    responseMap.put("error", "invalidHeatRange");
} else {
    throw e;
}
}
...

and then we'll handle it in the JavaScript as well. We'll want to look at cartOperations.js in the handler that is bound to input.addToCart and input.addToWishlist. Change this chunk:

if (data.error == 'allOptionsRequired') {
    $errorSpan.css('display', 'block');
    $errorSpan.effect('highlight', {}, 1000);
} else {
    HC.showNotification("Error adding to cart");
}

to this:

if (data.error == 'allOptionsRequired') {
    $errorSpan.css('display', 'block');
    $errorSpan.effect('highlight', {}, 1000);
} else if (data.error == 'invalidHeatRange') {
    HC.showNotification("This hot sauce isn't hot enough!");
} else {
    HC.showNotification("Error adding to cart");
}

And that's it! You've now completely added the heat range restriction! Congratulations on alienating your customers and selling less hot sauce!