All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.broadleafcommerce.core.pricing.service.fulfillment.provider.BandedFulfillmentPricingProvider Maven / Gradle / Ivy

There is a newer version: 3.1.15-GA
Show newest version
/*
 * Copyright 2008-2013 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.broadleafcommerce.core.pricing.service.fulfillment.provider;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.broadleafcommerce.common.currency.util.BroadleafCurrencyUtils;
import org.broadleafcommerce.common.money.Money;
import org.broadleafcommerce.common.util.UnitOfMeasureUtil;
import org.broadleafcommerce.common.util.WeightUnitOfMeasureType;
import org.broadleafcommerce.common.vendor.service.exception.FulfillmentPriceException;
import org.broadleafcommerce.core.catalog.domain.Sku;
import org.broadleafcommerce.core.order.domain.BundleOrderItem;
import org.broadleafcommerce.core.order.domain.DiscreteOrderItem;
import org.broadleafcommerce.core.order.domain.FulfillmentGroup;
import org.broadleafcommerce.core.order.domain.FulfillmentGroupItem;
import org.broadleafcommerce.core.order.domain.FulfillmentOption;
import org.broadleafcommerce.core.order.fulfillment.domain.BandedPriceFulfillmentOption;
import org.broadleafcommerce.core.order.fulfillment.domain.BandedWeightFulfillmentOption;
import org.broadleafcommerce.core.order.fulfillment.domain.FulfillmentBand;
import org.broadleafcommerce.core.order.fulfillment.domain.FulfillmentPriceBand;
import org.broadleafcommerce.core.order.fulfillment.domain.FulfillmentWeightBand;
import org.broadleafcommerce.core.order.service.type.FulfillmentBandResultAmountType;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 

Used in conjunction with {@link BandedPriceFulfillmentOption} and {@link BandedWeightFulfillmentOption}. * If 2 bands are configured equal to each other (meaning, there are 2 {@link FulfillmentPriceBand}s that have the * same retail price minimum or 2 {@link FulfillmentWeightBand}s that have the same minimum weight), * this will choose the cheaper rate of the 2

*

If the retail total does not fall within a configured price band, the total cost of fulfillment is zero

*

* Note: For {@link BandedWeightFulfillmentOption}, this assumes that all of your weights have the same units *

* @author Phillip Verheyden * @see {@link BandedPriceFulfillmentOption}, {@link FulfillmentPriceBand} */ public class BandedFulfillmentPricingProvider implements FulfillmentPricingProvider { protected static final Log LOG = LogFactory.getLog(BandedFulfillmentPricingProvider.class); @Override public boolean canCalculateCostForFulfillmentGroup(FulfillmentGroup fulfillmentGroup, FulfillmentOption option) { return (option instanceof BandedPriceFulfillmentOption) || (option instanceof BandedWeightFulfillmentOption); } @Override public FulfillmentGroup calculateCostForFulfillmentGroup(FulfillmentGroup fulfillmentGroup) throws FulfillmentPriceException { if (fulfillmentGroup.getFulfillmentGroupItems().size() == 0) { LOG.warn("fulfillment group (" + fulfillmentGroup.getId() + ") does not contain any fulfillment group items. Unable to price banded shipping"); fulfillmentGroup.setShippingPrice(Money.ZERO); fulfillmentGroup.setSaleShippingPrice(Money.ZERO); fulfillmentGroup.setRetailShippingPrice(Money.ZERO); return fulfillmentGroup; } if (canCalculateCostForFulfillmentGroup(fulfillmentGroup, fulfillmentGroup.getFulfillmentOption())) { //In this case, the estimation logic is the same as calculation logic. Call the estimation service to get the prices. HashSet options = new HashSet(); options.add(fulfillmentGroup.getFulfillmentOption()); FulfillmentEstimationResponse response = estimateCostForFulfillmentGroup(fulfillmentGroup, options); fulfillmentGroup.setSaleShippingPrice(response.getFulfillmentOptionPrices().get(fulfillmentGroup.getFulfillmentOption())); fulfillmentGroup.setRetailShippingPrice(response.getFulfillmentOptionPrices().get(fulfillmentGroup.getFulfillmentOption())); fulfillmentGroup.setShippingPrice(response.getFulfillmentOptionPrices().get(fulfillmentGroup.getFulfillmentOption())); return fulfillmentGroup; } throw new FulfillmentPriceException("An unsupported FulfillmentOption was passed to the calculateCostForFulfillmentGroup method"); } @Override public FulfillmentEstimationResponse estimateCostForFulfillmentGroup(FulfillmentGroup fulfillmentGroup, Set options) throws FulfillmentPriceException { //Set up the response object FulfillmentEstimationResponse res = new FulfillmentEstimationResponse(); HashMap shippingPrices = new HashMap(); res.setFulfillmentOptionPrices(shippingPrices); for (FulfillmentOption option : options) { if (canCalculateCostForFulfillmentGroup(fulfillmentGroup, option)) { List bands = null; if (option instanceof BandedPriceFulfillmentOption) { bands = ((BandedPriceFulfillmentOption) option).getBands(); } else if (option instanceof BandedWeightFulfillmentOption) { bands = ((BandedWeightFulfillmentOption) option).getBands(); } if (bands == null || bands.isEmpty()) { //Something is misconfigured. There are no bands associated with this fulfillment option throw new IllegalStateException("There were no Fulfillment Price Bands configured for a BandedPriceFulfillmentOption with ID: " + option.getId()); } //Calculate the amount that the band will be applied to BigDecimal retailTotal = BigDecimal.ZERO; BigDecimal flatTotal = BigDecimal.ZERO; BigDecimal weightTotal = BigDecimal.ZERO; boolean foundCandidateForBand = false; for (FulfillmentGroupItem fulfillmentGroupItem : fulfillmentGroup.getFulfillmentGroupItems()) { //If this item has a Sku associated with it which also has a flat rate for this fulfillment option, don't add it to the price //or weight total but instead tack it onto the final rate boolean addToTotal = true; Sku sku = null; if (fulfillmentGroupItem.getOrderItem() instanceof DiscreteOrderItem) { sku = ((DiscreteOrderItem)fulfillmentGroupItem.getOrderItem()).getSku(); } else if (fulfillmentGroupItem.getOrderItem() instanceof BundleOrderItem) { sku = ((BundleOrderItem)fulfillmentGroupItem.getOrderItem()).getSku(); } if (sku != null && option.getUseFlatRates()) { BigDecimal rate = sku.getFulfillmentFlatRates().get(option); if (rate != null) { addToTotal = false; flatTotal = flatTotal.add(rate); } } if (addToTotal) { foundCandidateForBand = true; BigDecimal price = (fulfillmentGroupItem.getTotalItemAmount() != null) ? fulfillmentGroupItem.getTotalItemAmount().getAmount() : null; if (price == null) { price = fulfillmentGroupItem.getOrderItem().getAveragePrice().getAmount().multiply(BigDecimal.valueOf(fulfillmentGroupItem.getQuantity())); } retailTotal = retailTotal.add(price); if (sku != null && sku.getWeight() != null && sku.getWeight().getWeight() != null) { BigDecimal convertedWeight = convertWeight(sku.getWeight().getWeight(), sku.getWeight().getWeightUnitOfMeasure()).multiply(BigDecimal.valueOf(fulfillmentGroupItem.getQuantity())); weightTotal = weightTotal.add(convertedWeight); } } } //Used to keep track of the lowest price when there is are bands that have the same //minimum amount BigDecimal lowestBandFulfillmentPrice = null; //Used to keep track of the amount for the lowest band fulfillment price. Used to compare //if 2 bands are configured for the same minimum amount BigDecimal lowestBandFulfillmentPriceMinimumAmount = BigDecimal.ZERO; if (foundCandidateForBand) { for (FulfillmentBand band : bands) { BigDecimal bandMinimumAmount = BigDecimal.ZERO; boolean foundMatch = false; if (band instanceof FulfillmentPriceBand) { bandMinimumAmount = ((FulfillmentPriceBand) band).getRetailPriceMinimumAmount(); foundMatch = retailTotal.compareTo(bandMinimumAmount) >= 0; } else if (band instanceof FulfillmentWeightBand) { bandMinimumAmount = ((FulfillmentWeightBand) band).getMinimumWeight(); foundMatch = weightTotal.compareTo(bandMinimumAmount) >= 0; } if (foundMatch) { //So far, we've found a potential match //Now, determine if this is a percentage or actual amount FulfillmentBandResultAmountType resultAmountType = band.getResultAmountType(); BigDecimal bandFulfillmentPrice = null; if (FulfillmentBandResultAmountType.RATE.equals(resultAmountType)) { bandFulfillmentPrice = band.getResultAmount(); } else if (FulfillmentBandResultAmountType.PERCENTAGE.equals(resultAmountType)) { //Since this is a percentage, we calculate the result amount based on retailTotal and the band percentage bandFulfillmentPrice = retailTotal.multiply(band.getResultAmount()); } else { LOG.warn("Unknown FulfillmentBandResultAmountType: " + resultAmountType.getType() + " Should be RATE or PERCENTAGE. Ignoring."); } if (bandFulfillmentPrice != null) { //haven't initialized the lowest price yet so just take on this one if (lowestBandFulfillmentPrice == null) { lowestBandFulfillmentPrice = bandFulfillmentPrice; lowestBandFulfillmentPriceMinimumAmount = bandMinimumAmount; } //If there is a duplicate price band (meaning, 2 price bands are configured with the same miniumum retail price) //then the lowest fulfillment amount should only be updated if the result of the current band being looked at //is cheaper if (lowestBandFulfillmentPriceMinimumAmount.compareTo(bandMinimumAmount) == 0) { if (bandFulfillmentPrice.compareTo(lowestBandFulfillmentPrice) <= 0) { lowestBandFulfillmentPrice = bandFulfillmentPrice; lowestBandFulfillmentPriceMinimumAmount = bandMinimumAmount; } } else if (bandMinimumAmount.compareTo(lowestBandFulfillmentPriceMinimumAmount) > 0) { lowestBandFulfillmentPrice = bandFulfillmentPrice; lowestBandFulfillmentPriceMinimumAmount = bandMinimumAmount; } } else { throw new IllegalStateException("Bands must have a non-null fulfillment price"); } } } } //If I didn't find a valid band, initialize the fulfillment price to zero if (lowestBandFulfillmentPrice == null) { lowestBandFulfillmentPrice = BigDecimal.ZERO; } //add the flat rate amount calculated on the Sku lowestBandFulfillmentPrice = lowestBandFulfillmentPrice.add(flatTotal); shippingPrices.put(option, BroadleafCurrencyUtils.getMoney(lowestBandFulfillmentPrice, fulfillmentGroup.getOrder().getCurrency())); } } return res; } /** * Default implementation is to convert everything to pounds for consistent weight types * * @param weight * @param type * @return */ protected BigDecimal convertWeight(BigDecimal weight, WeightUnitOfMeasureType type) { return UnitOfMeasureUtil.findPounds(weight, type); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy