org.openbravo.financial.FinancialUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.openbravo.financial.FinancialUtils.java

Source

/*
 *************************************************************************
 * The contents of this file are subject to the Openbravo  Public  License
 * Version  1.0  (the  "License"),  being   the  Mozilla   Public  License
 * Version 1.1  with a permitted attribution clause; you may not  use this
 * file except in compliance with the License. You  may  obtain  a copy of
 * the License at http://www.openbravo.com/legal/license.html
 * Software distributed under the License  is  distributed  on  an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific  language  governing  rights  and  limitations
 * under the License.
 * The Original Code is Openbravo ERP.
 * The Initial Developer of the Original Code is Openbravo SLU
 * All portions are Copyright (C) 2012 Openbravo SLU
 * All Rights Reserved.
 * Contributor(s):  ______________________________________.
 *************************************************************************
 */
package org.openbravo.financial;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang.time.DateUtils;
import org.apache.log4j.Logger;
import org.hibernate.criterion.Restrictions;
import org.openbravo.base.exception.OBException;
import org.openbravo.base.util.Check;
import org.openbravo.dal.core.DalUtil;
import org.openbravo.dal.core.OBContext;
import org.openbravo.dal.service.OBCriteria;
import org.openbravo.dal.service.OBDal;
import org.openbravo.dal.service.OBQuery;
import org.openbravo.erpCommon.utility.OBDateUtils;
import org.openbravo.erpCommon.utility.OBMessageUtils;
import org.openbravo.model.ad.system.Client;
import org.openbravo.model.common.currency.ConversionRate;
import org.openbravo.model.common.currency.ConversionRateDoc;
import org.openbravo.model.common.currency.Currency;
import org.openbravo.model.common.enterprise.Organization;
import org.openbravo.model.common.plm.Product;
import org.openbravo.model.pricing.pricelist.PriceList;
import org.openbravo.model.pricing.pricelist.PriceListVersion;
import org.openbravo.model.pricing.pricelist.ProductPrice;
import org.openbravo.service.db.CallStoredProcedure;

public class FinancialUtils {
    private static final Logger log4j = Logger.getLogger(FinancialUtils.class);

    public static final String PRECISION_STANDARD = "A";
    public static final String PRECISION_COSTING = "C";
    public static final String PRECISION_PRICE = "P";

    /**
     * @see #getProductStdPrice(Product, Date, boolean, PriceList, Currency, Organization)
     */
    public static BigDecimal getProductStdPrice(Product product, Date date, boolean useSalesPriceList,
            Currency currency, Organization organization) throws OBException {
        return getProductStdPrice(product, date, useSalesPriceList, null, currency, organization);
    }

    /**
     * Calculates the Standard Price of the given Product. It uses the
     * {@link #getProductPrice(Product, Date, boolean, PriceList) getProductPrice()} method to get the
     * ProductPrice to be used. In case a conversion is needed it uses the
     * {@link #getConvertedAmount(BigDecimal, Currency, Currency, Date, Organization, String)
     * getConvertedAmount()} method.
     * 
     * @param product
     *          Product to get its ProductPrice.
     * @param date
     *          Date when Product Price is needed.
     * @param useSalesPriceList
     *          boolean to set if the price list should be a sales or purchase price list.
     * @param pricelist
     *          PriceList to get its ProductPrice
     * @param currency
     *          Currency to convert to the returned price.
     * @param organization
     *          Organization where price needs to be used to retrieve the proper conversion rate.
     * @return a BigDecimal with the Standard Price of the Product for the given parameters.
     * @throws OBException
     *           when no valid ProductPrice is found.
     */
    public static BigDecimal getProductStdPrice(Product product, Date date, boolean useSalesPriceList,
            PriceList pricelist, Currency currency, Organization organization) throws OBException {
        ProductPrice pp = getProductPrice(product, date, useSalesPriceList, pricelist);
        BigDecimal price = pp.getStandardPrice();
        if (!DalUtil.getId(pp.getPriceListVersion().getPriceList().getCurrency()).equals(currency.getId())) {
            // Conversion is needed.
            price = getConvertedAmount(price, pp.getPriceListVersion().getPriceList().getCurrency(), currency, date,
                    organization, PRECISION_PRICE);
        }

        return price;
    }

    /**
     * @see #getProductPrice(Product, Date, boolean, PriceList, boolean)
     */
    public static ProductPrice getProductPrice(Product product, Date date, boolean useSalesPriceList)
            throws OBException {
        return getProductPrice(product, date, useSalesPriceList, null, true);
    }

    /**
     * @see #getProductPrice(Product, Date, boolean, PriceList, boolean)
     */
    public static ProductPrice getProductPrice(Product product, Date date, boolean useSalesPriceList,
            PriceList priceList) throws OBException {
        return getProductPrice(product, date, useSalesPriceList, priceList, true);
    }

    /**
     * Method to get a valid ProductPrice for the given Product. It only considers PriceList versions
     * valid on the given date. If a PriceList is given it searches on that one. If PriceList null is
     * passed it search on any Sales or Purchase PriceList based on the useSalesPriceList.
     * 
     * @param product
     *          Product to get its ProductPrice.
     * @param date
     *          Date when Product Price is needed.
     * @param useSalesPriceList
     *          boolean to set if the price list should be a sales or purchase price list.
     * @param priceList
     *          PriceList to get its ProductPrice
     * @param throwException
     *          boolean to determine if an exception has to be thrown when no pricelist is found.
     * @return a valid ProductPrice for the given parameters. Null is no exception is to be thrown.
     * @throws OBException
     *           when no valid ProductPrice is found and throwException is true.
     */
    public static ProductPrice getProductPrice(Product product, Date date, boolean useSalesPriceList,
            PriceList priceList, boolean throwException) throws OBException {
        StringBuffer where = new StringBuffer();
        where.append(" as pp");
        where.append("   join pp." + ProductPrice.PROPERTY_PRICELISTVERSION + " as plv");
        where.append("   join plv." + PriceListVersion.PROPERTY_PRICELIST + " as pl");
        where.append(" where pp." + ProductPrice.PROPERTY_PRODUCT + " = :product");
        where.append("   and plv." + PriceListVersion.PROPERTY_VALIDFROMDATE + " <= :date");
        if (priceList != null) {
            where.append("   and pl = :pricelist");
        } else {
            where.append("   and pl." + PriceList.PROPERTY_SALESPRICELIST + " = :salespricelist");
        }
        where.append(" order by pl." + PriceList.PROPERTY_DEFAULT + " desc, plv."
                + PriceListVersion.PROPERTY_VALIDFROMDATE + " desc");

        OBQuery<ProductPrice> ppQry = OBDal.getInstance().createQuery(ProductPrice.class, where.toString());
        ppQry.setNamedParameter("product", product);
        ppQry.setNamedParameter("date", date);
        if (priceList != null) {
            ppQry.setNamedParameter("pricelist", priceList);
        } else {
            ppQry.setNamedParameter("salespricelist", useSalesPriceList);
        }

        List<ProductPrice> ppList = ppQry.list();
        if (ppList.isEmpty()) {
            // No product price found.
            if (throwException) {
                throw new OBException("@PriceListVersionNotFound@. @Product@: " + product.getIdentifier()
                        + " @Date@: " + OBDateUtils.formatDate(date));
            } else {
                return null;
            }
        }
        return ppList.get(0);
    }

    /**
     * Method to get the conversion rate defined at system level. If there is not a conversion rate
     * defined on the given Organization it is searched recursively on its parent organization until
     * one is found. If no conversion rate is found null is returned.
     * 
     * @param date
     *          Date conversion is being performed.
     * @param fromCurrency
     *          Currency to convert from.
     * @param toCurrency
     *          Currency to convert to.
     * @param org
     *          Organization of the document that needs to be converted.
     * @return a valid ConversionRate for the given parameters, null if none is found.
     */
    public static ConversionRate getConversionRate(Date date, Currency fromCurrency, Currency toCurrency,
            Organization org, Client client) {
        ConversionRate conversionRate;
        // Conversion rate records do not get into account timestamp.
        Date dateWithoutTimestamp = DateUtils
                .setHours(DateUtils.setMinutes(DateUtils.setSeconds(DateUtils.setMilliseconds(date, 0), 0), 0), 0);
        // Readable Client Org filters to false as organization is filtered explicitly.
        OBContext.setAdminMode(false);
        try {
            final OBCriteria<ConversionRate> obcConvRate = OBDal.getInstance().createCriteria(ConversionRate.class);
            obcConvRate.add(Restrictions.eq(ConversionRate.PROPERTY_ORGANIZATION, org));
            obcConvRate.add(Restrictions.eq(ConversionRate.PROPERTY_CLIENT, client));
            obcConvRate.add(Restrictions.eq(ConversionRate.PROPERTY_CURRENCY, fromCurrency));
            obcConvRate.add(Restrictions.eq(ConversionRate.PROPERTY_TOCURRENCY, toCurrency));
            obcConvRate.add(Restrictions.le(ConversionRate.PROPERTY_VALIDFROMDATE, dateWithoutTimestamp));
            obcConvRate.add(Restrictions.ge(ConversionRate.PROPERTY_VALIDTODATE, dateWithoutTimestamp));
            obcConvRate.setFilterOnReadableClients(false);
            obcConvRate.setFilterOnReadableOrganization(false);
            conversionRate = (ConversionRate) obcConvRate.uniqueResult();
            if (conversionRate != null) {
                return conversionRate;
            }
            if ("0".equals(org.getId())) {
                return null;
            } else {
                return getConversionRate(date, fromCurrency, toCurrency,
                        OBContext.getOBContext().getOrganizationStructureProvider(client.getId()).getParentOrg(org),
                        client);
            }
        } catch (Exception e) {
            log4j.error("Exception calculating conversion rate.", e);
            return null;
        } finally {
            OBContext.restorePreviousMode();
        }
    }

    /**
     * It calls
     * {@link FinancialUtils#getConvertedAmount(BigDecimal, Currency, Currency, Date, Organization, String, List)}
     * with an empty list of conversion rates at document level
     */
    public static BigDecimal getConvertedAmount(BigDecimal amount, Currency curFrom, Currency curTo, Date date,
            Organization org, String strPrecision) throws OBException {
        return getConvertedAmount(amount, curFrom, curTo, date, org, strPrecision,
                Collections.<ConversionRateDoc>emptyList());
    }

    /**
     * Converts an amount.
     * 
     * @param amount
     *          BigDecimal amount to convert.
     * @param curFrom
     *          Currency to convert from.
     * @param curTo
     *          Currency to convert to.
     * @param date
     *          Date conversion is being performed.
     * @param org
     *          Organization of the document that needs to be converted.
     * @param strPrecision
     *          type of precision to be used to round the converted amount.
     * @param rateDocs
     *          list of conversion rates defined on the document of the amount that needs to be
     *          converted.
     * @return a BigDecimal representing the converted amount.
     * @throws OBException
     *           when no Conversion Rate is found for the given parameters.
     */
    public static BigDecimal getConvertedAmount(BigDecimal amount, Currency curFrom, Currency curTo, Date date,
            Organization org, String strPrecision, List<ConversionRateDoc> rateDocs) throws OBException {
        Check.isNotNull(rateDocs, OBMessageUtils.messageBD("ParameterMissing") + " (rateDocs)");
        if (curFrom.getId().equals(curTo.getId()) || amount.signum() == 0) {
            return amount;
        }
        BigDecimal rate = null;
        if (rateDocs.size() > 0) {
            for (ConversionRateDoc rateDoc : rateDocs) {
                if (curFrom.getId().equals(rateDoc.getCurrency().getId())
                        && curTo.getId().equals(rateDoc.getToCurrency().getId())) {
                    rate = rateDoc.getRate();
                    break;
                }
            }
        }
        if (rate == null) {
            ConversionRate cr = getConversionRate(date, curFrom, curTo, org, org.getClient());
            if (cr == null) {
                throw new OBException("@NoCurrencyConversion@ " + curFrom.getISOCode() + " @to@ "
                        + curTo.getISOCode() + " @ForDate@ " + OBDateUtils.formatDate(date)
                        + " @And@ @ACCS_AD_ORG_ID_D@ " + org.getIdentifier());
            }
            rate = cr.getMultipleRateBy();
        }
        Long precision = curTo.getStandardPrecision();
        if (PRECISION_COSTING.equals(strPrecision)) {
            precision = curTo.getCostingPrecision();
        } else if (PRECISION_PRICE.equals(strPrecision)) {
            precision = curTo.getPricePrecision();
        }
        return amount.multiply(rate).setScale(precision.intValue(), RoundingMode.HALF_UP);
    }

    /**
     * Returns the Currency of a Legal Entity. If there is no one defined, returns the currency of the
     * Client.
     */
    public static Currency getLegalEntityCurrency(Organization organization) {
        Organization legalEntity = OBContext.getOBContext()
                .getOrganizationStructureProvider(organization.getClient().getId()).getLegalEntity(organization);
        Currency currency = (legalEntity.getCurrency() != null) ? legalEntity.getCurrency()
                : organization.getClient().getCurrency();
        return currency;
    }

    /**
     * Calculates the net unit price using the C_GET_NET_PRICE_FROM_GROSS stored procedure.
     * 
     * @param strTaxId
     *          Tax that applies to the price.
     * @param grossAmount
     *          Gross Amount to calculate the net unit price from.
     * @param pricePrecision
     *          Precision to round the result to.
     * @param alternateAmount
     *          alternate amount in case the tax uses it.
     * @param quantity
     *          number of units to divide the amount to get the price.
     * @return the net unit price
     */
    public static BigDecimal calculateNetFromGross(String strTaxId, BigDecimal grossAmount, int pricePrecision,
            BigDecimal alternateAmount, BigDecimal quantity) {
        if (grossAmount.compareTo(BigDecimal.ZERO) == 0) {
            return BigDecimal.ZERO;
        }
        final List<Object> parameters = new ArrayList<Object>();
        parameters.add(strTaxId);
        parameters.add(grossAmount);
        // TODO: Alternate Base Amount
        parameters.add(alternateAmount);
        parameters.add(pricePrecision);
        parameters.add(quantity);

        final String procedureName = "C_GET_NET_PRICE_FROM_GROSS";
        final BigDecimal lineNetAmount = (BigDecimal) CallStoredProcedure.getInstance().call(procedureName,
                parameters, null);
        return lineNetAmount;
    }
}