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

com.liferay.commerce.discount.internal.CommerceDiscountCalculationImpl Maven / Gradle / Ivy

/**
 * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 */

package com.liferay.commerce.discount.internal;

import com.liferay.commerce.context.CommerceContext;
import com.liferay.commerce.currency.model.CommerceCurrency;
import com.liferay.commerce.currency.model.CommerceMoney;
import com.liferay.commerce.currency.model.CommerceMoneyFactory;
import com.liferay.commerce.discount.CommerceDiscountCalculation;
import com.liferay.commerce.discount.CommerceDiscountValue;
import com.liferay.commerce.discount.constants.CommerceDiscountConstants;
import com.liferay.commerce.discount.model.CommerceDiscount;
import com.liferay.commerce.discount.model.CommerceDiscountRule;
import com.liferay.commerce.discount.rule.type.CommerceDiscountRuleType;
import com.liferay.commerce.discount.rule.type.CommerceDiscountRuleTypeRegistry;
import com.liferay.commerce.discount.service.CommerceDiscountRuleLocalService;
import com.liferay.commerce.model.CommerceOrder;
import com.liferay.commerce.product.model.CPInstance;
import com.liferay.commerce.product.service.CPInstanceLocalService;
import com.liferay.portal.kernel.dao.orm.QueryUtil;
import com.liferay.portal.kernel.exception.PortalException;
import com.liferay.portal.kernel.util.Validator;

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;

/**
 * @author Marco Leo
 */
@Component(
	property = "commerce.discount.calculation.key=v1.0",
	service = CommerceDiscountCalculation.class
)
public class CommerceDiscountCalculationImpl
	extends BaseCommerceDiscountCalculation {

	@Override
	public CommerceDiscountValue getOrderShippingCommerceDiscountValue(
			CommerceOrder commerceOrder, BigDecimal shippingAmount,
			CommerceContext commerceContext)
		throws PortalException {

		if (commerceOrder == null) {
			return null;
		}

		return _getCommerceDiscountValue(
			commerceOrder, shippingAmount, commerceContext,
			CommerceDiscountConstants.TARGET_SHIPPING);
	}

	@Override
	public CommerceDiscountValue getOrderSubtotalCommerceDiscountValue(
			CommerceOrder commerceOrder, BigDecimal subtotalAmount,
			CommerceContext commerceContext)
		throws PortalException {

		if (commerceOrder == null) {
			return null;
		}

		return _getCommerceDiscountValue(
			commerceOrder, subtotalAmount, commerceContext,
			CommerceDiscountConstants.TARGET_SUBTOTAL);
	}

	@Override
	public CommerceDiscountValue getOrderTotalCommerceDiscountValue(
			CommerceOrder commerceOrder, BigDecimal totalAmount,
			CommerceContext commerceContext)
		throws PortalException {

		if (commerceOrder == null) {
			return null;
		}

		return _getCommerceDiscountValue(
			commerceOrder, totalAmount, commerceContext,
			CommerceDiscountConstants.TARGET_TOTAL);
	}

	@Override
	public CommerceDiscountValue getProductCommerceDiscountValue(
			long cpInstanceId, int quantity, BigDecimal productUnitPrice,
			CommerceContext commerceContext)
		throws PortalException {

		CPInstance cpInstance = _cpInstanceLocalService.getCPInstance(
			cpInstanceId);

		List commerceDiscounts =
			getProductCommerceDiscountByHierarchy(
				cpInstance.getCompanyId(), commerceContext.getCommerceAccount(),
				commerceContext.getCommerceChannelId(),
				cpInstance.getCPDefinitionId());

		if (commerceDiscounts.isEmpty()) {
			return null;
		}

		String couponCode = null;

		CommerceOrder commerceOrder = commerceContext.getCommerceOrder();

		if (commerceOrder != null) {
			couponCode = commerceOrder.getCouponCode();
		}

		return _getCommerceDiscountValue(
			productUnitPrice, quantity, couponCode, commerceContext,
			commerceDiscounts);
	}

	@Override
	public CommerceDiscountValue getProductCommerceDiscountValue(
			long cpInstanceId, long commercePriceListId, int quantity,
			BigDecimal productUnitPrice, CommerceContext commerceContext)
		throws PortalException {

		return getProductCommerceDiscountValue(
			cpInstanceId, quantity, productUnitPrice, commerceContext);
	}

	private CommerceDiscountValue _getCommerceDiscountValue(
			BigDecimal amount, int quantity, String couponCode,
			CommerceContext commerceContext,
			List commerceDiscounts)
		throws PortalException {

		List commerceDiscountValues = new ArrayList<>();

		CommerceCurrency commerceCurrency =
			commerceContext.getCommerceCurrency();

		for (CommerceDiscount commerceDiscount : commerceDiscounts) {
			String discountCouponCode = commerceDiscount.getCouponCode();

			if (!Validator.isBlank(discountCouponCode) &&
				!Objects.equals(couponCode, discountCouponCode)) {

				continue;
			}

			if (_isValidDiscount(commerceContext, commerceDiscount)) {
				commerceDiscountValues.add(
					_getCommerceDiscountValue(
						commerceDiscount, amount, quantity, commerceCurrency));
			}
		}

		BigDecimal currentDiscountAmount = BigDecimal.ZERO;

		CommerceDiscountValue selectedDiscount = null;

		for (CommerceDiscountValue commerceDiscountValue :
				commerceDiscountValues) {

			if (commerceDiscountValue == null) {
				continue;
			}

			CommerceMoney discountAmount =
				commerceDiscountValue.getDiscountAmount();

			if (currentDiscountAmount.compareTo(discountAmount.getPrice()) <
					0) {

				currentDiscountAmount = discountAmount.getPrice();
				selectedDiscount = commerceDiscountValue;
			}
		}

		return selectedDiscount;
	}

	private CommerceDiscountValue _getCommerceDiscountValue(
		CommerceDiscount commerceDiscount, BigDecimal amount, int quantity,
		CommerceCurrency commerceCurrency) {

		if ((amount == null) || (amount.compareTo(BigDecimal.ZERO) <= 0)) {
			return null;
		}

		BigDecimal[] values = new BigDecimal[4];

		if (commerceDiscount.isUsePercentage()) {
			values[0] = commerceDiscount.getLevel1();
			values[1] = commerceDiscount.getLevel2();
			values[2] = commerceDiscount.getLevel3();
			values[3] = commerceDiscount.getLevel4();
		}

		BigDecimal currentDiscountAmount = BigDecimal.ZERO;

		BigDecimal discountedAmount = amount;

		if (commerceDiscount.isUsePercentage()) {
			currentDiscountAmount = _getDiscountAmount(
				discountedAmount, commerceDiscount.getLevel1());

			discountedAmount = discountedAmount.subtract(currentDiscountAmount);

			currentDiscountAmount = _getDiscountAmount(
				discountedAmount, commerceDiscount.getLevel2());

			discountedAmount = discountedAmount.subtract(currentDiscountAmount);

			currentDiscountAmount = _getDiscountAmount(
				discountedAmount, commerceDiscount.getLevel3());

			discountedAmount = discountedAmount.subtract(currentDiscountAmount);

			currentDiscountAmount = amount.subtract(discountedAmount);

			BigDecimal maximumDiscountAmount =
				commerceDiscount.getMaximumDiscountAmount();

			if ((maximumDiscountAmount.compareTo(BigDecimal.ZERO) > 0) &&
				(currentDiscountAmount.compareTo(maximumDiscountAmount) > 0)) {

				currentDiscountAmount =
					commerceDiscount.getMaximumDiscountAmount();

				discountedAmount = amount.subtract(currentDiscountAmount);
			}
		}
		else {
			currentDiscountAmount = commerceDiscount.getLevel1();

			if (currentDiscountAmount.compareTo(discountedAmount) > 0) {
				currentDiscountAmount = discountedAmount;
			}

			discountedAmount = discountedAmount.subtract(currentDiscountAmount);
		}

		CommerceMoney discountAmount = _commerceMoneyFactory.create(
			commerceCurrency,
			currentDiscountAmount.multiply(new BigDecimal(quantity)));

		RoundingMode roundingMode = RoundingMode.valueOf(
			commerceCurrency.getRoundingMode());

		return new CommerceDiscountValue(
			commerceDiscount.getCommerceDiscountId(), discountAmount,
			_getDiscountPercentage(discountedAmount, amount, roundingMode),
			values);
	}

	private CommerceDiscountValue _getCommerceDiscountValue(
			CommerceOrder commerceOrder, BigDecimal amount,
			CommerceContext commerceContext, String discountType)
		throws PortalException {

		if ((amount == null) || (amount.compareTo(BigDecimal.ZERO) <= 0)) {
			return null;
		}

		List commerceDiscounts =
			getOrderCommerceDiscountByHierarchy(
				commerceOrder.getCompanyId(),
				commerceContext.getCommerceAccount(),
				commerceContext.getCommerceChannelId(), discountType);

		if (commerceDiscounts.isEmpty()) {
			return null;
		}

		return _getCommerceDiscountValue(
			amount, 1, commerceOrder.getCouponCode(), commerceContext,
			commerceDiscounts);
	}

	private BigDecimal _getDiscountAmount(
		BigDecimal amount, BigDecimal percentage) {

		if (percentage == null) {
			return BigDecimal.ZERO;
		}

		BigDecimal discountedAmount = amount.multiply(percentage);

		return discountedAmount.divide(_ONE_HUNDRED);
	}

	private BigDecimal _getDiscountPercentage(
		BigDecimal discountedAmount, BigDecimal amount,
		RoundingMode roundingMode) {

		double actualPrice = discountedAmount.doubleValue();
		double originalPrice = amount.doubleValue();

		double percentage = actualPrice / originalPrice;

		BigDecimal discountPercentage = new BigDecimal(percentage);

		discountPercentage = discountPercentage.multiply(_ONE_HUNDRED);

		MathContext mathContext = new MathContext(
			discountPercentage.precision(), roundingMode);

		return _ONE_HUNDRED.subtract(discountPercentage, mathContext);
	}

	private boolean _isValidDiscount(
			CommerceContext commerceContext, CommerceDiscount commerceDiscount)
		throws PortalException {

		List commerceDiscountRules =
			_commerceDiscountRuleLocalService.getCommerceDiscountRules(
				commerceDiscount.getCommerceDiscountId(), QueryUtil.ALL_POS,
				QueryUtil.ALL_POS, null);

		if (commerceDiscountRules.isEmpty()) {
			return true;
		}

		for (CommerceDiscountRule commerceDiscountRule :
				commerceDiscountRules) {

			CommerceDiscountRuleType commerceDiscountRuleType =
				_commerceDiscountRuleTypeRegistry.getCommerceDiscountRuleType(
					commerceDiscountRule.getType());

			boolean commerceDiscountRuleTypeEvaluation =
				commerceDiscountRuleType.evaluate(
					commerceDiscountRule, commerceContext);

			if (!commerceDiscountRuleTypeEvaluation &&
				commerceDiscount.isRulesConjunction()) {

				return false;
			}
			else if (commerceDiscountRuleTypeEvaluation &&
					 !commerceDiscount.isRulesConjunction()) {

				return true;
			}
		}

		return commerceDiscount.isRulesConjunction();
	}

	private static final BigDecimal _ONE_HUNDRED = BigDecimal.valueOf(100);

	@Reference
	private CommerceDiscountRuleLocalService _commerceDiscountRuleLocalService;

	@Reference
	private CommerceDiscountRuleTypeRegistry _commerceDiscountRuleTypeRegistry;

	@Reference
	private CommerceMoneyFactory _commerceMoneyFactory;

	@Reference
	private CPInstanceLocalService _cpInstanceLocalService;

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy