org.openbravo.costing.PriceDifferenceProcess.java Source code

Java tutorial

Introduction

Here is the source code for org.openbravo.costing.PriceDifferenceProcess.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) 2014-2015 Openbravo SLU
 * All Rights Reserved.
 * Contributor(s):  ______________________________________.
 *************************************************************************
 */
package org.openbravo.costing;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.criterion.Restrictions;
import org.openbravo.base.exception.OBException;
import org.openbravo.costing.CostingServer.TrxType;
import org.openbravo.dal.service.OBCriteria;
import org.openbravo.dal.service.OBDal;
import org.openbravo.erpCommon.utility.OBMessageUtils;
import org.openbravo.financial.FinancialUtils;
import org.openbravo.model.common.currency.Currency;
import org.openbravo.model.common.enterprise.Organization;
import org.openbravo.model.common.invoice.Invoice;
import org.openbravo.model.common.plm.Product;
import org.openbravo.model.materialmgmt.cost.CostAdjustment;
import org.openbravo.model.materialmgmt.cost.CostAdjustmentLine;
import org.openbravo.model.materialmgmt.transaction.MaterialTransaction;
import org.openbravo.model.materialmgmt.transaction.ShipmentInOutLine;
import org.openbravo.model.procurement.ReceiptInvoiceMatch;

public class PriceDifferenceProcess {
    private static CostAdjustment costAdjHeader = null;

    private static boolean calculateTransactionPriceDifferenceLogic(MaterialTransaction materialTransaction)
            throws OBException {
        boolean costAdjCreated = false;

        // With Standard Algorithm, no cost adjustment is needed
        if (StringUtils.equals(materialTransaction.getCostingAlgorithm().getJavaClassName(),
                "org.openbravo.costing.StandardAlgorithm")) {
            return false;
        }

        if (materialTransaction.isCostPermanent()) {
            // Permanently adjusted transaction costs are not checked for price differences.
            return false;
        }
        Currency trxCurrency = materialTransaction.getCurrency();
        Organization trxOrg = materialTransaction.getOrganization();
        Date trxDate = materialTransaction.getMovementDate();
        int costCurPrecission = trxCurrency.getCostingPrecision().intValue();
        ShipmentInOutLine receiptLine = materialTransaction.getGoodsShipmentLine();
        if (receiptLine == null
                || !isValidPriceAdjTrx(receiptLine.getMaterialMgmtMaterialTransactionList().get(0))) {
            // We can only adjust cost of receipt lines.
            return false;
        }

        BigDecimal receiptQty = receiptLine.getMovementQuantity();
        boolean isNegativeReceipt = receiptQty.signum() == -1;
        if (isNegativeReceipt) {
            // If the receipt is negative convert the quantity to positive.
            receiptQty = receiptQty.negate();
        }

        Date costAdjDateAcct = null;
        BigDecimal invoiceAmt = BigDecimal.ZERO;

        // Calculate current transaction unit cost including existing adjustments.
        BigDecimal currentTrxUnitCost = CostAdjustmentUtils.getTrxCost(materialTransaction, true, trxCurrency);

        // Calculate expected transaction unit cost based on current invoice amounts and purchase price.
        BigDecimal expectedCost = BigDecimal.ZERO;
        BigDecimal invoiceQty = BigDecimal.ZERO;
        for (ReceiptInvoiceMatch matchInv : receiptLine.getProcurementReceiptInvoiceMatchList()) {
            Invoice invoice = matchInv.getInvoiceLine().getInvoice();
            if (invoice.getDocumentStatus().equals("VO")) {
                // Skip voided invoices.
                continue;
            }
            if (!invoice.isProcessed()) {
                // Skip not processed invoices.
                continue;
            }
            if (isNegativeReceipt) {
                // If the receipt is negative negate the invoiced quantities.
                invoiceQty = invoiceQty.add(matchInv.getQuantity().negate());
            } else {
                invoiceQty = invoiceQty.add(matchInv.getQuantity());
            }
            invoiceAmt = matchInv.getQuantity().multiply(matchInv.getInvoiceLine().getUnitPrice());

            invoiceAmt = FinancialUtils.getConvertedAmount(invoiceAmt, invoice.getCurrency(), trxCurrency, trxDate,
                    trxOrg, FinancialUtils.PRECISION_STANDARD, invoice.getCurrencyConversionRateDocList());
            expectedCost = expectedCost.add(invoiceAmt);

            Date invoiceDate = invoice.getInvoiceDate();
            if (costAdjDateAcct == null || costAdjDateAcct.before(invoiceDate)) {
                costAdjDateAcct = invoiceDate;
            }
        }

        BigDecimal notInvoicedQty = receiptQty.subtract(invoiceQty);
        if (notInvoicedQty.signum() > 0) {
            // Not all the receipt line is invoiced, add pending invoice quantity valued with current
            // order price if exists or original unit cost.
            BigDecimal basePrice = BigDecimal.ZERO;
            Currency baseCurrency = trxCurrency;
            if (receiptLine.getSalesOrderLine() != null) {
                basePrice = receiptLine.getSalesOrderLine().getUnitPrice();
                baseCurrency = receiptLine.getSalesOrderLine().getSalesOrder().getCurrency();
            } else {
                basePrice = materialTransaction.getTransactionCost().divide(receiptQty, costCurPrecission,
                        RoundingMode.HALF_UP);
            }
            BigDecimal baseAmt = notInvoicedQty.multiply(basePrice).setScale(costCurPrecission,
                    RoundingMode.HALF_UP);
            if (!baseCurrency.getId().equals(trxCurrency.getId())) {
                baseAmt = FinancialUtils.getConvertedAmount(baseAmt, baseCurrency, trxCurrency, trxDate, trxOrg,
                        FinancialUtils.PRECISION_STANDARD);
            }
            expectedCost = expectedCost.add(baseAmt);
        }
        // if the sum of trx costs with flag "isInvoiceCorrection" is distinct that the amount cost
        // generated by Match Invoice then New Cost Adjustment line is created by the difference
        if (expectedCost.compareTo(currentTrxUnitCost) != 0) {
            if (costAdjDateAcct == null) {
                costAdjDateAcct = trxDate;
            }
            createCostAdjustmenHeader(trxOrg);
            CostAdjustmentLine costAdjLine = CostAdjustmentUtils.insertCostAdjustmentLine(materialTransaction,
                    costAdjHeader, expectedCost.subtract(currentTrxUnitCost), Boolean.TRUE, costAdjDateAcct);
            costAdjLine.setNeedsPosting(Boolean.TRUE);
            OBDal.getInstance().save(costAdjLine);
            costAdjCreated = true;
        }

        return costAdjCreated;
    }

    private static boolean calculateTransactionPriceDifference(MaterialTransaction materialTransaction)
            throws OBException {

        boolean costAdjCreated = calculateTransactionPriceDifferenceLogic(materialTransaction);

        materialTransaction.setCheckpricedifference(Boolean.FALSE);
        OBDal.getInstance().save(materialTransaction);
        OBDal.getInstance().flush();

        return costAdjCreated;

    }

    public static JSONObject processPriceDifferenceTransaction(MaterialTransaction materialTransaction)
            throws OBException {
        costAdjHeader = null;

        calculateTransactionPriceDifference(materialTransaction);

        if (costAdjHeader != null) {
            OBDal.getInstance().flush();
            JSONObject message = CostAdjustmentProcess.doProcessCostAdjustment(costAdjHeader);
            try {
                message.put("documentNo", costAdjHeader.getDocumentNo());
                if (message.get("severity") != "success") {
                    throw new OBException(OBMessageUtils.parseTranslation("@ErrorProcessingCostAdj@") + ": "
                            + costAdjHeader.getDocumentNo() + " - " + message.getString("text"));
                }
            } catch (JSONException e) {
                throw new OBException(OBMessageUtils.parseTranslation("@ErrorProcessingCostAdj@"));
            }

            return message;
        } else {
            JSONObject message = new JSONObject();
            try {
                message.put("severity", "success");
                message.put("title", "");
                message.put("text", OBMessageUtils.messageBD("Success"));
            } catch (JSONException ignore) {
            }
            return message;
        }
    }

    /**
     * @return the message to be shown to the user properly formatted and translated to the user
     *         language.
     * @throws OBException
     *           when there is an error that prevents the cost adjustment to be processed.
     * @throws OBException
     */
    public static JSONObject processPriceDifference(Date date, Product product) throws OBException {

        JSONObject message = null;
        costAdjHeader = null;
        boolean costAdjCreated = false;
        int count = 0;
        OBCriteria<MaterialTransaction> mTrxs = OBDal.getInstance().createCriteria(MaterialTransaction.class);
        if (date != null) {
            mTrxs.add(Restrictions.le(MaterialTransaction.PROPERTY_MOVEMENTDATE, date));
        }
        if (product != null) {
            mTrxs.add(Restrictions.eq(MaterialTransaction.PROPERTY_PRODUCT, product));
        }
        mTrxs.add(Restrictions.eq(MaterialTransaction.PROPERTY_CHECKPRICEDIFFERENCE, true));
        mTrxs.add(Restrictions.eq(MaterialTransaction.PROPERTY_ISCOSTCALCULATED, true));
        mTrxs.addOrderBy(MaterialTransaction.PROPERTY_MOVEMENTDATE, true);
        mTrxs.addOrderBy(MaterialTransaction.PROPERTY_TRANSACTIONPROCESSDATE, true);
        ScrollableResults lines = mTrxs.scroll(ScrollMode.FORWARD_ONLY);

        try {
            while (lines.next()) {
                MaterialTransaction line = (MaterialTransaction) lines.get(0);
                costAdjCreated = calculateTransactionPriceDifference(line);
                if (costAdjCreated) {
                    count++;
                }

            }
        } finally {
            lines.close();
        }

        Map<String, String> map = new HashMap<String, String>();
        map.put("trxsNumber", Integer.toString(count));
        String messageText = OBMessageUtils.messageBD("PriceDifferenceChecked");

        if (costAdjHeader != null) {
            OBDal.getInstance().flush();
            message = CostAdjustmentProcess.doProcessCostAdjustment(costAdjHeader);
            try {
                if (message.get("severity") != "success") {
                    throw new OBException(OBMessageUtils.parseTranslation("@ErrorProcessingCostAdj@") + ": "
                            + costAdjHeader.getDocumentNo() + " - " + message.getString("text"));
                } else {
                    message.put("title", OBMessageUtils.messageBD("Success"));
                    message.put("text", OBMessageUtils.parseTranslation(messageText, map));
                }
            } catch (JSONException e) {
                throw new OBException(OBMessageUtils.parseTranslation("@ErrorProcessingCostAdj@"));
            }
            return message;
        } else {
            try {
                message = new JSONObject();
                message.put("severity", "success");
                message.put("title", OBMessageUtils.messageBD("Success"));
                message.put("text", OBMessageUtils.parseTranslation(messageText, map));
            } catch (JSONException ignore) {
            }
            return message;
        }
    }

    private static void createCostAdjustmenHeader(Organization org) {
        if (costAdjHeader == null) {
            costAdjHeader = CostAdjustmentUtils.insertCostAdjustmentHeader(org, "PDC");
            // PDC: Price Dif Correction
        }
    }

    /**
     * True if is an Incoming Transaction
     */
    private static boolean isValidPriceAdjTrx(MaterialTransaction trx) {
        TrxType transacctionType = TrxType.getTrxType(trx);
        switch (transacctionType) {
        case Receipt:
            return true;
        default:
            return false;
        }
    }
}