org.openbravo.costing.AverageCostAdjustment.java Source code

Java tutorial

Introduction

Here is the source code for org.openbravo.costing.AverageCostAdjustment.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.List;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.criterion.Restrictions;
import org.openbravo.base.exception.OBException;
import org.openbravo.base.provider.OBProvider;
import org.openbravo.base.structure.BaseOBObject;
import org.openbravo.client.kernel.ComponentProvider;
import org.openbravo.costing.CostingAlgorithm.CostDimension;
import org.openbravo.costing.CostingServer.TrxType;
import org.openbravo.dal.core.DalUtil;
import org.openbravo.dal.core.OBContext;
import org.openbravo.dal.security.OrganizationStructureProvider;
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.financial.FinancialUtils;
import org.openbravo.model.ad.system.Client;
import org.openbravo.model.common.currency.Currency;
import org.openbravo.model.common.enterprise.Locator;
import org.openbravo.model.common.enterprise.Organization;
import org.openbravo.model.common.enterprise.Warehouse;
import org.openbravo.model.common.plm.Product;
import org.openbravo.model.materialmgmt.cost.CostAdjustmentLine;
import org.openbravo.model.materialmgmt.cost.Costing;
import org.openbravo.model.materialmgmt.cost.CostingRule;
import org.openbravo.model.materialmgmt.cost.TransactionCost;
import org.openbravo.model.materialmgmt.transaction.MaterialTransaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@ComponentProvider.Qualifier("org.openbravo.costing.AverageAlgorithm")
public class AverageCostAdjustment extends CostingAlgorithmAdjustmentImp {
    private static final Logger log = LoggerFactory.getLogger(CostAdjustmentProcess.class);
    private String bdCostingId;

    @Override
    protected void init(CostAdjustmentLine costAdjLine) {
        super.init(costAdjLine);
        bdCostingId = "";
    }

    @Override
    protected void getRelatedTransactionsByAlgorithm() {
        // Search all transactions after the date of the adjusted line and recalculate the costs of them
        // to adjust differences
        MaterialTransaction basetrx = getTransaction();
        // Transactions of closing inventories are managed by generic CostAdjustmentProcess adjusting
        // the cost of the related opening inventory.
        if (basetrx.getPhysicalInventoryLine() != null
                && basetrx.getPhysicalInventoryLine().getRelatedInventory() != null) {
            return;
        }
        BigDecimal signMultiplier = new BigDecimal(basetrx.getMovementQuantity().signum());
        Date trxDate = basetrx.getTransactionProcessDate();

        BigDecimal adjustmentBalance = BigDecimal.ZERO;
        BigDecimal unitCostAdjustmentBalance = BigDecimal.ZERO;
        // Initialize adjustment balance looping through all cost adjustment lines of current
        // transaction.
        log.debug("Initialize adjustment balance");
        CostAdjustmentLine baseCAL = getCostAdjLine();
        for (CostAdjustmentLine costAdjLine : getTrxAdjustmentLines(basetrx)) {
            if (costAdjLine.isSource() && !costAdjLine.isRelatedTransactionAdjusted()
                    && !costAdjLine.getId().equals(strCostAdjLineId)) {
                searchRelatedTransactionCosts(costAdjLine);
            }

            costAdjLine.setRelatedTransactionAdjusted(Boolean.TRUE);
            if (!costAdjLine.getId().equals(strCostAdjLineId)) {
                costAdjLine.setParentCostAdjustmentLine(baseCAL);
            }
            OBDal.getInstance().save(costAdjLine);
            // If the cost adjustment line has Transaction Costs those adjustment amount are included
            // in the Current Value Amount and not in the Adjustment Balance
            if (!costAdjLine.getTransactionCostList().isEmpty()) {
                continue;
            }
            BigDecimal adjustmentAmt = costAdjLine.getAdjustmentAmount();
            if (!strCostCurrencyId.equals(costAdjLine.getCurrency().getId())) {
                adjustmentAmt = FinancialUtils.getConvertedAmount(adjustmentAmt, costAdjLine.getCurrency(),
                        getCostCurrency(), costAdjLine.getAccountingDate(), getCostOrg(),
                        FinancialUtils.PRECISION_STANDARD);
            }
            adjustmentBalance = adjustmentBalance.add(adjustmentAmt.multiply(signMultiplier));
            if (costAdjLine.isUnitCost()) {
                unitCostAdjustmentBalance = unitCostAdjustmentBalance.add(adjustmentAmt);
            }
        }

        // Initialize current stock qty and value amt.
        BigDecimal currentStock = CostAdjustmentUtils.getStockOnTransactionDate(getCostOrg(), basetrx,
                getCostDimensions(), isManufacturingProduct, areBackdatedTrxFixed);
        BigDecimal currentValueAmt = CostAdjustmentUtils.getValuedStockOnTransactionDate(getCostOrg(), basetrx,
                getCostDimensions(), isManufacturingProduct, areBackdatedTrxFixed, getCostCurrency());
        log.debug(
                "Adjustment balance: " + adjustmentBalance.toPlainString() + ", current stock {}, current value {}",
                currentStock.toPlainString(), currentValueAmt.toPlainString());

        // Initialize current unit cost including the cost adjustments.
        Costing costing = AverageAlgorithm.getProductCost(trxDate, basetrx.getProduct(), getCostDimensions(),
                getCostOrg());
        if (costing == null) {
            throw new OBException("@NoAvgCostDefined@ @Organization@: " + getCostOrg().getName() + ", @Product@: "
                    + basetrx.getProduct().getName() + ", @Date@: " + OBDateUtils.formatDate(trxDate));
        }
        BigDecimal cost = null;
        // If current stock is zero the cost is not modified until a related transaction that modifies
        // the stock is found.
        if (currentStock.signum() != 0) {
            cost = currentValueAmt.add(adjustmentBalance).divide(currentStock, costCurPrecission,
                    RoundingMode.HALF_UP);
        }
        log.debug("Starting average cost {}", cost == null ? "not cost" : cost.toPlainString());
        if (cost != null && (AverageAlgorithm.modifiesAverage(trxType) || !baseCAL.isBackdatedTrx())) {
            BigDecimal trxUnitCost = CostAdjustmentUtils.getTrxCost(basetrx, true, getCostCurrency());
            BigDecimal trxPrice = null;
            if (basetrx.getMovementQuantity().signum() == 0) {
                trxPrice = BigDecimal.ZERO;
            } else {
                trxPrice = trxUnitCost.add(unitCostAdjustmentBalance).divide(basetrx.getMovementQuantity().abs(),
                        costCurPrecission, RoundingMode.HALF_UP);
            }
            if (checkNegativeStockCorrection && currentStock.compareTo(basetrx.getMovementQuantity()) < 0
                    && cost.compareTo(trxPrice) != 0 && !baseCAL.isNegativeStockCorrection()
                    && AverageAlgorithm.modifiesAverage(trxType)) {
                // stock was negative and cost different than trx price then Negative Stock Correction
                // is added
                BigDecimal trxSignMultiplier = new BigDecimal(basetrx.getMovementQuantity().signum());
                BigDecimal negCorrAmt = trxPrice.multiply(currentStock)
                        .setScale(stdCurPrecission, RoundingMode.HALF_UP).subtract(currentValueAmt)
                        .subtract(adjustmentBalance);
                adjustmentBalance = adjustmentBalance.add(negCorrAmt.multiply(trxSignMultiplier));
                // If there is a difference insert a cost adjustment line.
                CostAdjustmentLine newCAL = insertCostAdjustmentLine(basetrx, negCorrAmt, null);
                newCAL.setNegativeStockCorrection(Boolean.TRUE);
                newCAL.setRelatedTransactionAdjusted(Boolean.TRUE);
                newCAL.setUnitCost(Boolean.FALSE);
                OBDal.getInstance().save(newCAL);
                cost = trxPrice;
                log.debug("Negative stock correction. Amount: {}, new cost {}", negCorrAmt.toPlainString(),
                        cost.toPlainString());
            }
            if (basetrx.getMaterialMgmtCostingList().size() == 0) {
                Date newDate = new Date();
                Date dateTo = costing.getEndingDate();
                costing.setEndingDate(newDate);
                OBDal.getInstance().save(costing);
                Costing newCosting = OBProvider.getInstance().get(Costing.class);
                newCosting.setCost(cost);
                newCosting.setCurrency(
                        (Currency) OBDal.getInstance().getProxy(Currency.ENTITY_NAME, strCostCurrencyId));
                newCosting.setStartingDate(newDate);
                newCosting.setEndingDate(dateTo);
                newCosting.setInventoryTransaction(basetrx);
                newCosting.setProduct(basetrx.getProduct());
                if (isManufacturingProduct) {
                    newCosting.setOrganization(
                            (Organization) OBDal.getInstance().getProxy(Organization.ENTITY_NAME, "0"));
                } else {
                    newCosting.setOrganization(
                            (Organization) OBDal.getInstance().getProxy(Organization.ENTITY_NAME, strCostOrgId));
                }
                newCosting.setQuantity(basetrx.getMovementQuantity());
                newCosting.setTotalMovementQuantity(currentStock);
                newCosting.setPrice(cost);
                newCosting.setCostType("AVA");
                newCosting.setManual(Boolean.FALSE);
                newCosting.setPermanent(Boolean.TRUE);
                newCosting.setProduction(trxType == TrxType.ManufacturingProduced);
                newCosting.setWarehouse((Warehouse) getCostDimensions().get(CostDimension.Warehouse));
                OBDal.getInstance().save(newCosting);
                OBDal.getInstance().flush();
            } else {
                Costing curCosting = basetrx.getMaterialMgmtCostingList().get(0);

                if (curCosting.getCost().compareTo(cost) != 0
                        || curCosting.getTotalMovementQuantity().compareTo(currentStock) != 0) {
                    curCosting.setPermanent(Boolean.FALSE);
                    OBDal.getInstance().save(curCosting);
                    OBDal.getInstance().flush();
                    // Update existing costing
                    if (curCosting.getCost().compareTo(cost) != 0) {
                        if (curCosting.getOriginalCost() == null) {
                            curCosting.setOriginalCost(curCosting.getCost());
                        }
                        curCosting.setCost(cost);
                        curCosting.setPrice(trxPrice);
                    }
                    curCosting.setTotalMovementQuantity(currentStock);
                    curCosting.setPermanent(Boolean.TRUE);
                    OBDal.getInstance().flush();
                    OBDal.getInstance().save(curCosting);
                }
            }
        }

        // Modify isManufacturingProduct flag in case it has changed at some point.
        isManufacturingProduct = ((String) DalUtil.getId(costing.getOrganization())).equals("0");

        ScrollableResults trxs = getRelatedTransactions();
        String strCurrentCurId = strCostCurrencyId;
        try {
            while (trxs.next()) {
                MaterialTransaction trx = (MaterialTransaction) trxs.get()[0];
                log.debug("Process related transaction {}", trx.getIdentifier());
                BigDecimal trxSignMultiplier = new BigDecimal(trx.getMovementQuantity().signum());
                BigDecimal trxAdjAmt = BigDecimal.ZERO;
                BigDecimal trxUnitCostAdjAmt = BigDecimal.ZERO;
                if (StringUtils.isNotEmpty(bdCostingId) && !isBackdatedTransaction(trx)) {
                    // If there is a backdated source adjustment pending modify the dates of its m_costing.
                    updateBDCostingTimeRange(trx);
                    // This update is done only on the first related transaction.
                    bdCostingId = "";
                }

                if (!strCurrentCurId.equals(trx.getCurrency().getId())) {
                    Currency curCurrency = OBDal.getInstance().get(Currency.class, strCurrentCurId);
                    Organization costOrg = getCostOrg();

                    currentValueAmt = FinancialUtils.getConvertedAmount(currentValueAmt, curCurrency,
                            trx.getCurrency(), trx.getMovementDate(), costOrg, FinancialUtils.PRECISION_STANDARD);
                    if (cost != null) {
                        cost = FinancialUtils.getConvertedAmount(cost, curCurrency, trx.getCurrency(),
                                trx.getMovementDate(), costOrg, FinancialUtils.PRECISION_COSTING);
                    }

                    strCurrentCurId = trx.getCurrency().getId();
                }

                List<CostAdjustmentLine> existingAdjLines = getTrxAdjustmentLines(trx);
                for (CostAdjustmentLine existingCAL : existingAdjLines) {
                    if (existingCAL.isSource() && !existingCAL.isRelatedTransactionAdjusted()) {
                        searchRelatedTransactionCosts(existingCAL);
                    }
                    if (existingCAL.getTransactionCostList().isEmpty()
                            && !existingCAL.isRelatedTransactionAdjusted()) {
                        BigDecimal adjustmentAmt = existingCAL.getAdjustmentAmount();
                        if (!strCurrentCurId.equals(existingCAL.getCurrency().getId())) {
                            Currency curCurrency = OBDal.getInstance().get(Currency.class, strCurrentCurId);
                            adjustmentAmt = FinancialUtils.getConvertedAmount(adjustmentAmt,
                                    existingCAL.getCurrency(), curCurrency, existingCAL.getAccountingDate(),
                                    getCostOrg(), FinancialUtils.PRECISION_STANDARD);
                        }
                        trxAdjAmt = trxAdjAmt.add(adjustmentAmt);
                        adjustmentBalance = adjustmentBalance.add(adjustmentAmt.multiply(trxSignMultiplier));
                        if (existingCAL.isUnitCost()) {
                            trxUnitCostAdjAmt = trxUnitCostAdjAmt.add(adjustmentAmt);
                        }
                    }

                    existingCAL.setRelatedTransactionAdjusted(Boolean.TRUE);
                    existingCAL.setParentCostAdjustmentLine((CostAdjustmentLine) OBDal.getInstance()
                            .getProxy(CostAdjustmentLine.ENTITY_NAME, strCostAdjLineId));

                    OBDal.getInstance().save(existingCAL);
                }
                log.debug("Current trx adj amount of existing CALs {}", trxAdjAmt.toPlainString());

                BigDecimal trxCost = CostAdjustmentUtils.getTrxCost(trx, false,
                        OBDal.getInstance().get(Currency.class, strCurrentCurId));
                BigDecimal trxUnitCost = CostAdjustmentUtils.getTrxCost(trx, true,
                        OBDal.getInstance().get(Currency.class, strCurrentCurId));
                currentValueAmt = currentValueAmt.add(trxCost.multiply(trxSignMultiplier));
                currentStock = currentStock.add(trx.getMovementQuantity());
                log.debug("Updated current stock {} and, current value {}", currentStock.toPlainString(),
                        currentValueAmt.toPlainString());

                TrxType currentTrxType = TrxType.getTrxType(trx);

                if (AverageAlgorithm.modifiesAverage(currentTrxType)) {
                    // Recalculate average, if current stock is zero the average is not modified
                    if (currentStock.signum() != 0) {
                        cost = currentValueAmt.add(adjustmentBalance).divide(currentStock, costCurPrecission,
                                RoundingMode.HALF_UP);
                    }
                    if (cost == null) {
                        continue;
                    }
                    log.debug("New average cost: {}", cost.toPlainString());
                    Costing curCosting = trx.getMaterialMgmtCostingList().get(0);
                    BigDecimal trxPrice = null;
                    if (trx.getMovementQuantity().signum() == 0) {
                        trxPrice = BigDecimal.ZERO;
                    } else {
                        trxPrice = trxUnitCost.add(trxUnitCostAdjAmt).divide(trx.getMovementQuantity().abs(),
                                costCurPrecission, RoundingMode.HALF_UP);
                    }

                    if (checkNegativeStockCorrection && currentStock.compareTo(trx.getMovementQuantity()) < 0
                            && cost.compareTo(trxPrice) != 0) {
                        // stock was negative and cost different than trx price then Negative Stock Correction
                        // is added
                        BigDecimal negCorrAmt = trxPrice.multiply(currentStock)
                                .setScale(stdCurPrecission, RoundingMode.HALF_UP).subtract(currentValueAmt)
                                .subtract(adjustmentBalance);
                        adjustmentBalance = adjustmentBalance.add(negCorrAmt.multiply(trxSignMultiplier));
                        trxAdjAmt = trxAdjAmt.add(negCorrAmt.multiply(trxSignMultiplier));
                        // If there is a difference insert a cost adjustment line.
                        CostAdjustmentLine newCAL = insertCostAdjustmentLine(trx, negCorrAmt, null);
                        newCAL.setNegativeStockCorrection(Boolean.TRUE);
                        newCAL.setRelatedTransactionAdjusted(Boolean.TRUE);
                        newCAL.setUnitCost(Boolean.FALSE);
                        OBDal.getInstance().save(newCAL);
                        cost = trxPrice;
                        log.debug("Negative stock correction. Amount: {}, new cost {}", negCorrAmt.toPlainString(),
                                cost.toPlainString());
                    }

                    if (curCosting.getCost().compareTo(cost) == 0 && StringUtils.isEmpty(bdCostingId)
                            && curCosting.getTotalMovementQuantity().compareTo(currentStock) == 0) {
                        // new cost hasn't changed and total movement qty is equal to current stock, following
                        // transactions will have the same cost, so no more
                        // related transactions are needed to include.
                        // If bdCosting is not empty it is needed to loop through the next related transaction
                        // to set the new time ringe of the costing.
                        log.debug("New cost matches existing cost. Adjustment finished.");
                        return;
                    } else {
                        // Update existing costing
                        curCosting.setPermanent(Boolean.FALSE);
                        OBDal.getInstance().save(curCosting);
                        OBDal.getInstance().flush();
                        if (curCosting.getCost().compareTo(cost) != 0) {
                            if (curCosting.getOriginalCost() == null) {
                                curCosting.setOriginalCost(curCosting.getCost());
                            }
                            curCosting.setPrice(trxPrice);
                            curCosting.setCost(cost);
                        }
                        curCosting.setTotalMovementQuantity(currentStock);
                        curCosting.setPermanent(Boolean.TRUE);
                        OBDal.getInstance().save(curCosting);
                    }
                } else if (cost != null && !isVoidedTrx(trx, currentTrxType)) {
                    if (!trx.isCostPermanent()) {
                        // Check current trx unit cost matches new expected cost
                        BigDecimal expectedCost = cost.multiply(trx.getMovementQuantity().abs())
                                .setScale(stdCurPrecission, RoundingMode.HALF_UP);
                        BigDecimal unitCost = CostAdjustmentUtils.getTrxCost(trx, true,
                                OBDal.getInstance().get(Currency.class, strCurrentCurId));
                        unitCost = unitCost.add(trxAdjAmt);
                        log.debug("Is adjustment needed? Expected {} vs Current {}", expectedCost.toPlainString(),
                                unitCost.toPlainString());
                        if (expectedCost.compareTo(unitCost) != 0) {
                            trxAdjAmt = trxAdjAmt.add(expectedCost.subtract(unitCost).multiply(trxSignMultiplier));
                            trxUnitCostAdjAmt = trxUnitCostAdjAmt.add(expectedCost.subtract(unitCost));
                            adjustmentBalance = adjustmentBalance
                                    .add(expectedCost.subtract(unitCost).multiply(trxSignMultiplier));
                            // If there is a difference insert a cost adjustment line.
                            CostAdjustmentLine newCAL = insertCostAdjustmentLine(trx,
                                    expectedCost.subtract(unitCost), null);
                            newCAL.setRelatedTransactionAdjusted(Boolean.TRUE);
                            OBDal.getInstance().save(newCAL);
                            log.debug("Adjustment added. Amount {}.",
                                    expectedCost.subtract(unitCost).toPlainString());
                        }
                    }
                    if (trx.getMaterialMgmtCostingList().size() != 0) {
                        Costing curCosting = trx.getMaterialMgmtCostingList().get(0);
                        if (currentStock.signum() != 0) {
                            cost = currentValueAmt.add(adjustmentBalance).divide(currentStock, costCurPrecission,
                                    RoundingMode.HALF_UP);
                        }
                        BigDecimal trxPrice = null;
                        if (trx.getMovementQuantity().signum() == 0) {
                            trxPrice = BigDecimal.ZERO;
                        } else {
                            trxPrice = trxUnitCost.add(trxUnitCostAdjAmt).divide(trx.getMovementQuantity().abs(),
                                    costCurPrecission, RoundingMode.HALF_UP);
                        }
                        if (curCosting.getCost().compareTo(cost) != 0
                                || curCosting.getTotalMovementQuantity().compareTo(currentStock) != 0) {
                            curCosting.setPermanent(Boolean.FALSE);
                            OBDal.getInstance().save(curCosting);
                            OBDal.getInstance().flush();
                            if (curCosting.getCost().compareTo(cost) != 0) {
                                if (curCosting.getOriginalCost() == null) {
                                    curCosting.setOriginalCost(curCosting.getCost());
                                }
                                curCosting.setPrice(trxPrice);
                                curCosting.setCost(cost);
                            }
                            curCosting.setTotalMovementQuantity(currentStock);
                            curCosting.setPermanent(Boolean.TRUE);
                            OBDal.getInstance().save(curCosting);
                        }
                    }
                }

                OBDal.getInstance().flush();
                OBDal.getInstance().getSession().clear();
            }
        } finally {
            trxs.close();
        }

        if (getCostingRule().getEndingDate() == null && cost != null) {
            // This is the current costing rule. Check if current average cost needs to be updated.
            Costing currentCosting = AverageAlgorithm.getProductCost(new Date(), basetrx.getProduct(),
                    getCostDimensions(), getCostOrg());
            if (currentCosting == null) {
                throw new OBException("@NoAvgCostDefined@ @Organization@: " + getCostOrg().getName()
                        + ", @Product@: " + basetrx.getProduct().getName() + ", @Date@: "
                        + OBDateUtils.formatDate(new Date()));
            }
            if (currentCosting.getCost().compareTo(cost) != 0) {
                basetrx = getTransaction();
                Date newDate = new Date();
                Date dateTo = currentCosting.getEndingDate();
                currentCosting.setEndingDate(newDate);
                OBDal.getInstance().save(currentCosting);
                Costing newCosting = OBProvider.getInstance().get(Costing.class);
                newCosting.setCost(cost);
                newCosting.setCurrency(
                        (Currency) OBDal.getInstance().getProxy(Currency.ENTITY_NAME, strCurrentCurId));
                newCosting.setStartingDate(newDate);
                newCosting.setEndingDate(dateTo);
                newCosting.setInventoryTransaction(null);
                newCosting.setProduct(basetrx.getProduct());
                if (isManufacturingProduct) {
                    newCosting.setOrganization(
                            (Organization) OBDal.getInstance().getProxy(Organization.ENTITY_NAME, "0"));
                } else {
                    newCosting.setOrganization(
                            (Organization) OBDal.getInstance().getProxy(Organization.ENTITY_NAME, strCostOrgId));
                }
                newCosting.setQuantity(null);
                newCosting.setTotalMovementQuantity(currentStock);
                newCosting.setPrice(cost);
                newCosting.setCostType("AVA");
                newCosting.setManual(Boolean.FALSE);
                newCosting.setPermanent(Boolean.TRUE);
                newCosting.setProduction(trxType == TrxType.ManufacturingProduced);
                newCosting.setWarehouse((Warehouse) getCostDimensions().get(CostDimension.Warehouse));
                OBDal.getInstance().save(newCosting);
            }
        }
    }

    @Override
    protected void calculateBackdatedTrxAdjustment(CostAdjustmentLine costAdjLine) {
        MaterialTransaction trx = costAdjLine.getInventoryTransaction();
        TrxType calTrxType = TrxType.getTrxType(trx);
        if (AverageAlgorithm.modifiesAverage(calTrxType)) {
            // The bdCosting average related to the backdated transaction needs to be moved to its correct
            // date range, the last costing is the average cost that currently finishes when the costing
            // that needs to be moved starts. The "lastCosting" ending date needs to be updated to end in
            // the same date than the backdated costing so there is no gap between average costs.
            // The bdCosting dates are updated later when the first related transaction is checked.
            Costing bdCosting = trx.getMaterialMgmtCostingList().get(0);
            extendPreviousCosting(bdCosting);
        }
        super.calculateBackdatedTrxAdjustment(costAdjLine);
    }

    @Override
    protected BigDecimal getOutgoingBackdatedTrxAdjAmt(CostAdjustmentLine costAdjLine) {
        // Calculate the average cost on the transaction's movement date and adjust the cost if needed.
        MaterialTransaction trx = costAdjLine.getInventoryTransaction();
        Costing costing = getAvgCostOnMovementDate(trx, getCostDimensions(), getCostOrg(), areBackdatedTrxFixed);

        if (costing == null) {
            // In case the backdated transaction is on a date where the stock was not initialized there
            // isn't any costing entry related to an inventory transaction which results in a null
            // costing.
            // Try again with average algorithm getProductCost method using the movement date as
            // parameter.
            costing = AverageAlgorithm.getProductCost(trx.getMovementDate(), trx.getProduct(), getCostDimensions(),
                    getCostOrg());
        }

        if (costing == null) {
            String errorMessage = OBMessageUtils.parseTranslation("@NoAvgCostDefined@ @Organization@: "
                    + getCostOrg().getName() + ", @Product@: " + trx.getProduct().getName() + ", @Date@: "
                    + OBDateUtils.formatDate(trx.getMovementDate()));
            throw new OBException(errorMessage);
        }
        BigDecimal cost = costing.getCost();
        Currency costCurrency = getCostCurrency();
        if (costing.getCurrency() != costCurrency) {
            cost = FinancialUtils.getConvertedAmount(costing.getCost(), costing.getCurrency(), costCurrency,
                    trx.getTransactionProcessDate(), getCostOrg(), FinancialUtils.PRECISION_COSTING);
        }
        BigDecimal expectedCostAmt = trx.getMovementQuantity().abs().multiply(cost).setScale(stdCurPrecission,
                RoundingMode.HALF_UP);
        BigDecimal currentCost = trx.getTransactionCost();
        return expectedCostAmt.subtract(currentCost);
    }

    @Override
    protected BigDecimal getDefaultCostDifference(TrxType calTrxType, CostAdjustmentLine costAdjLine) {
        MaterialTransaction trx = costAdjLine.getInventoryTransaction();
        Costing costing = getAvgCostOnMovementDate(trx, getCostDimensions(), getCostOrg(), areBackdatedTrxFixed);
        if (costing == null) {
            // In case the backdated transaction is on a date where the stock was not initialized there
            // isn't any costing entry related to an inventory transaction which results in a null
            // costing.
            // Try again with average algorithm getProductCost method using the movement date as
            // parameter.
            costing = AverageAlgorithm.getProductCost(trx.getMovementDate(), trx.getProduct(), getCostDimensions(),
                    getCostOrg());
        }
        if (costing != null) {
            BigDecimal defaultCost = costing.getCost();
            Currency costCurrency = getCostCurrency();
            if (costing.getCurrency() != costCurrency) {
                defaultCost = FinancialUtils.getConvertedAmount(costing.getCost(), costing.getCurrency(),
                        costCurrency, trx.getTransactionProcessDate(), getCostOrg(),
                        FinancialUtils.PRECISION_COSTING);
            }
            BigDecimal trxCalculatedCost = CostAdjustmentUtils.getTrxCost(trx, true, getCostCurrency());
            defaultCost = trx.getMovementQuantity().abs().multiply(defaultCost).setScale(stdCurPrecission,
                    RoundingMode.HALF_UP);
            return defaultCost.subtract(trxCalculatedCost);
        }
        return super.getDefaultCostDifference(calTrxType, costAdjLine);
    }

    private ScrollableResults getRelatedTransactions() {
        CostingRule costingRule = getCostingRule();
        HashMap<CostDimension, BaseOBObject> costDimensions = getCostDimensions();
        OrganizationStructureProvider osp = OBContext.getOBContext()
                .getOrganizationStructureProvider(costingRule.getClient().getId());
        Set<String> orgs = osp.getChildTree(strCostOrgId, true);
        if (isManufacturingProduct) {
            orgs = osp.getChildTree("0", false);
            costDimensions = CostingUtils.getEmptyDimensions();
        }
        Warehouse warehouse = (Warehouse) costDimensions.get(CostDimension.Warehouse);
        MaterialTransaction trx = getTransaction();

        StringBuffer wh = new StringBuffer();
        wh.append(" as trx");
        wh.append("\n join trx." + Product.PROPERTY_ORGANIZATION + " as org");
        wh.append("\n join trx." + Product.PROPERTY_STORAGEBIN + " as loc");
        wh.append("\n , " + org.openbravo.model.ad.domain.List.ENTITY_NAME + " as trxtype");
        wh.append("\n where trxtype." + CostAdjustmentUtils.propADListReference + ".id = :refid");
        wh.append("  and trxtype." + CostAdjustmentUtils.propADListValue + " = trx."
                + MaterialTransaction.PROPERTY_MOVEMENTTYPE);

        wh.append("  and trx." + MaterialTransaction.PROPERTY_ISCOSTCALCULATED + " = true");
        wh.append("  and trx." + MaterialTransaction.PROPERTY_PRODUCT + " = :product");
        // Consider only transactions with movement date equal or later than the movement date of the
        // adjusted transaction. But for transactions with the same movement date only those with a
        // transaction date after the process date of the adjusted transaction.
        wh.append(" and (");

        if (costingRule.isBackdatedTransactionsFixed()) {
            wh.append("  (trx." + MaterialTransaction.PROPERTY_TRANSACTIONPROCESSDATE + " < :fixbdt");
            wh.append("  and (");
        }

        wh.append("   trx." + MaterialTransaction.PROPERTY_TRANSACTIONPROCESSDATE + " > :trxdate");
        wh.append("   or (");
        wh.append("    trx." + MaterialTransaction.PROPERTY_TRANSACTIONPROCESSDATE + " = :trxdate");
        wh.append("    and (");
        wh.append("     trxtype." + CostAdjustmentUtils.propADListPriority + " > :trxtypeprio");
        wh.append("     or (");
        wh.append("      trxtype." + CostAdjustmentUtils.propADListPriority + " = :trxtypeprio");
        wh.append("      and trx." + MaterialTransaction.PROPERTY_MOVEMENTQUANTITY + " < :trxqty");
        wh.append("      or (");
        wh.append("        trx." + MaterialTransaction.PROPERTY_MOVEMENTQUANTITY + " = :trxqty");
        wh.append("        and trx." + MaterialTransaction.PROPERTY_ID + " > :trxid");
        wh.append("  )))))");

        if (costingRule.isBackdatedTransactionsFixed()) {
            wh.append(" ) or (");

            wh.append("  trx." + MaterialTransaction.PROPERTY_TRANSACTIONPROCESSDATE + " >= :fixbdt");
            wh.append("  and (");
            wh.append("   trx." + MaterialTransaction.PROPERTY_MOVEMENTDATE + " > :mvtdate");
            wh.append("   or (");
            wh.append("    trx." + MaterialTransaction.PROPERTY_MOVEMENTDATE + " = :mvtdate");
            // If there are more than one trx on the same trx process date filter out those types with
            // less
            // priority and / or higher quantity.
            wh.append("    and (");
            wh.append("     trx." + MaterialTransaction.PROPERTY_TRANSACTIONPROCESSDATE + " > :trxdate");
            wh.append("     or (");
            wh.append("      trx." + MaterialTransaction.PROPERTY_TRANSACTIONPROCESSDATE + " = :trxdate");
            wh.append("      and (");
            wh.append("       trxtype." + CostAdjustmentUtils.propADListPriority + " > :trxtypeprio");
            wh.append("       or (");
            wh.append("        trxtype." + CostAdjustmentUtils.propADListPriority + " = :trxtypeprio");
            wh.append("        and trx." + MaterialTransaction.PROPERTY_MOVEMENTQUANTITY + " < :trxqty");
            wh.append("         or (");
            wh.append("          trx." + MaterialTransaction.PROPERTY_MOVEMENTQUANTITY + " = :trxqty");
            wh.append("          and trx." + MaterialTransaction.PROPERTY_ID + " > :trxid");
            wh.append("    )))))");
            wh.append(" )))");
        }
        wh.append(")");
        wh.append("  and org.id in (:orgs)");
        if (warehouse != null) {
            wh.append("  and loc." + Locator.PROPERTY_WAREHOUSE + " = :warehouse");
        }
        if (costingRule.getEndingDate() != null) {
            wh.append("  and trx." + MaterialTransaction.PROPERTY_TRANSACTIONPROCESSDATE + " <= :enddate");
        }
        wh.append("  and trx." + MaterialTransaction.PROPERTY_TRANSACTIONPROCESSDATE + " > :startdate ");
        wh.append("\n order by ");
        if (areBackdatedTrxFixed) {
            // CASE WHEN trx.trxprocessdate < :fixfrom THEN 1-1-1900
            // ELSE trx.movmenetdate END
            wh.append(" trx." + MaterialTransaction.PROPERTY_MOVEMENTDATE + ", ");
        }
        wh.append(" trx." + MaterialTransaction.PROPERTY_TRANSACTIONPROCESSDATE);
        wh.append(" , trxtype." + CostAdjustmentUtils.propADListPriority);
        wh.append(" , trx." + MaterialTransaction.PROPERTY_MOVEMENTQUANTITY + " desc");
        wh.append(" , trx." + MaterialTransaction.PROPERTY_ID);

        OBQuery<MaterialTransaction> trxQry = OBDal.getInstance().createQuery(MaterialTransaction.class,
                wh.toString());
        trxQry.setFilterOnReadableOrganization(false);
        trxQry.setFilterOnReadableClients(false);
        trxQry.setNamedParameter("refid", CostAdjustmentUtils.MovementTypeRefID);
        trxQry.setNamedParameter("product", trx.getProduct());
        if (costingRule.isBackdatedTransactionsFixed()) {
            trxQry.setNamedParameter("mvtdate", trx.getMovementDate());
            trxQry.setNamedParameter("fixbdt", CostingUtils.getCostingRuleFixBackdatedFrom(costingRule));
        }
        trxQry.setNamedParameter("trxtypeprio", CostAdjustmentUtils.getTrxTypePrio(trx.getMovementType()));
        trxQry.setNamedParameter("trxdate", trx.getTransactionProcessDate());
        trxQry.setNamedParameter("trxqty", trx.getMovementQuantity());
        trxQry.setNamedParameter("trxid", trx.getId());
        trxQry.setNamedParameter("orgs", orgs);
        if (warehouse != null) {
            trxQry.setNamedParameter("warehouse", warehouse);
        }
        if (costingRule.getEndingDate() != null) {
            trxQry.setNamedParameter("enddate", costingRule.getEndingDate());
        }
        trxQry.setNamedParameter("startdate", CostingUtils.getCostingRuleStartingDate(costingRule));

        return trxQry.scroll(ScrollMode.FORWARD_ONLY);
    }

    private List<CostAdjustmentLine> getTrxAdjustmentLines(MaterialTransaction trx) {
        OBCriteria<CostAdjustmentLine> critLines = OBDal.getInstance().createCriteria(CostAdjustmentLine.class);
        critLines.add(Restrictions.eq(CostAdjustmentLine.PROPERTY_COSTADJUSTMENT, getCostAdj()));
        critLines.add(Restrictions.eq(CostAdjustmentLine.PROPERTY_INVENTORYTRANSACTION, trx));
        critLines.add(Restrictions.eq(CostAdjustmentLine.PROPERTY_ISRELATEDTRANSACTIONADJUSTED, false));

        return critLines.list();
    }

    @Override
    protected void calculateNegativeStockCorrectionAdjustmentAmount(CostAdjustmentLine costAdjLine) {
        MaterialTransaction basetrx = costAdjLine.getInventoryTransaction();
        boolean areBaseTrxBackdatedFixed = getCostingRule().isBackdatedTransactionsFixed() && !CostingUtils
                .getCostingRuleFixBackdatedFrom(getCostingRule()).before(basetrx.getTransactionProcessDate());
        BigDecimal currentStock = CostAdjustmentUtils.getStockOnTransactionDate(getCostOrg(), basetrx,
                getCostDimensions(), isManufacturingProduct, areBaseTrxBackdatedFixed);
        BigDecimal currentValueAmt = CostAdjustmentUtils.getValuedStockOnTransactionDate(getCostOrg(), basetrx,
                getCostDimensions(), isManufacturingProduct, areBaseTrxBackdatedFixed, getCostCurrency());

        Costing curCosting = basetrx.getMaterialMgmtCostingList().get(0);
        BigDecimal trxPrice = curCosting.getPrice();
        BigDecimal adjustAmt = currentStock.multiply(trxPrice).setScale(stdCurPrecission, RoundingMode.HALF_UP)
                .subtract(currentValueAmt);

        costAdjLine.setCurrency((Currency) OBDal.getInstance().getProxy(Currency.ENTITY_NAME, strCostCurrencyId));
        costAdjLine.setAdjustmentAmount(adjustAmt);
        OBDal.getInstance().save(costAdjLine);
    }

    /**
     * Calculates the average cost value of the transaction.
     */
    protected static Costing getAvgCostOnMovementDate(MaterialTransaction trx,
            HashMap<CostDimension, BaseOBObject> costDimensions, Organization costOrg,
            boolean areBackdatedTrxFixed) {

        // Get child tree of organizations.
        OrganizationStructureProvider osp = OBContext.getOBContext()
                .getOrganizationStructureProvider(costOrg.getClient().getId());
        Set<String> orgs = osp.getChildTree(costOrg.getId(), true);

        StringBuffer where = new StringBuffer();
        where.append(" as c");
        where.append("\n  join c." + TransactionCost.PROPERTY_INVENTORYTRANSACTION + " as trx");
        where.append("\n  join trx." + MaterialTransaction.PROPERTY_STORAGEBIN + " as locator");
        where.append("\n , " + org.openbravo.model.ad.domain.List.ENTITY_NAME + " as trxtype");
        where.append("\n where trxtype." + CostAdjustmentUtils.propADListReference + ".id = :refid");
        where.append("  and trxtype." + CostAdjustmentUtils.propADListValue + " = trx."
                + MaterialTransaction.PROPERTY_MOVEMENTTYPE);

        where.append("   and trx." + MaterialTransaction.PROPERTY_PRODUCT + ".id = :product");
        if (areBackdatedTrxFixed) {
            where.append("  and (");
            where.append("   trx." + MaterialTransaction.PROPERTY_MOVEMENTDATE + " < :mvtdate");
            where.append("   or (");
            where.append("    trx." + MaterialTransaction.PROPERTY_MOVEMENTDATE + " = :mvtdate");
        }
        // If there are more than one trx on the same trx process date filter out those types with less
        // priority and / or higher quantity.
        where.append(" and (");
        where.append("  trx." + MaterialTransaction.PROPERTY_TRANSACTIONPROCESSDATE + " < :trxdate");
        where.append("  or (");
        where.append("   trx." + MaterialTransaction.PROPERTY_TRANSACTIONPROCESSDATE + " = :trxdate");
        where.append("   and (");
        where.append("    trxtype." + CostAdjustmentUtils.propADListPriority + " < :trxtypeprio");
        where.append("    or (");
        where.append("     trxtype." + CostAdjustmentUtils.propADListPriority + " = :trxtypeprio");
        where.append("     and trx." + MaterialTransaction.PROPERTY_MOVEMENTQUANTITY + " >= :trxqty");
        where.append(" ))))");

        if (areBackdatedTrxFixed) {
            where.append("  ))");
        }
        // Include only transactions that have its cost calculated
        where.append("   and trx." + MaterialTransaction.PROPERTY_ISCOSTCALCULATED + " = true");
        if (costDimensions.get(CostDimension.Warehouse) != null) {
            where.append("  and locator." + Locator.PROPERTY_WAREHOUSE + ".id = :warehouse");
        }
        where.append("   and trx." + MaterialTransaction.PROPERTY_ORGANIZATION + ".id in (:orgs)");
        where.append(" order by ");
        if (areBackdatedTrxFixed) {
            where.append(" trx." + MaterialTransaction.PROPERTY_MOVEMENTDATE + " desc, ");
        }
        where.append(" trx." + MaterialTransaction.PROPERTY_TRANSACTIONPROCESSDATE + " desc ");
        where.append(" , trxtype." + CostAdjustmentUtils.propADListPriority + " desc ");
        where.append(" , trx." + MaterialTransaction.PROPERTY_MOVEMENTQUANTITY);
        where.append(" , trx." + MaterialTransaction.PROPERTY_ID);

        OBQuery<Costing> qryCost = OBDal.getInstance().createQuery(Costing.class, where.toString());
        qryCost.setNamedParameter("refid", CostAdjustmentUtils.MovementTypeRefID);
        qryCost.setNamedParameter("product", trx.getProduct().getId());
        qryCost.setNamedParameter("mvtdate", trx.getMovementDate());
        qryCost.setNamedParameter("trxdate", trx.getTransactionProcessDate());
        qryCost.setNamedParameter("trxtypeprio", CostAdjustmentUtils.getTrxTypePrio(trx.getMovementType()));
        qryCost.setNamedParameter("trxqty", trx.getMovementQuantity());

        if (costDimensions.get(CostDimension.Warehouse) != null) {
            qryCost.setNamedParameter("warehouse", costDimensions.get(CostDimension.Warehouse).getId());
        }
        qryCost.setNamedParameter("orgs", orgs);
        qryCost.setMaxResult(1);
        return qryCost.uniqueResult();
    }

    /**
     * Extends the Average Costing ending date to include the time range that leaves the given
     * backdated average costing when this is moved to the correct time range.
     * 
     * It stored the backdated costing id in a local field to be updated with the new time range when
     * the next related transaction is processed.
     * 
     * @param bdCosting
     *          the backdated costing
     */
    private void extendPreviousCosting(Costing bdCosting) {
        StringBuffer where = new StringBuffer();
        where.append(" as c");
        where.append("  left join c." + Costing.PROPERTY_INVENTORYTRANSACTION + " as trx");
        where.append(" where c." + Costing.PROPERTY_PRODUCT + " = :product");
        // FIXME: remove when manufacturing costs are fully migrated
        if (bdCosting.getProduct().isProduction()) {
            where.append("  and c." + Costing.PROPERTY_CLIENT + " = :client");
        } else {
            where.append("  and c." + Costing.PROPERTY_ORGANIZATION + " = :org");
        }
        where.append("   and c." + Costing.PROPERTY_COSTTYPE + " = 'AVA'");
        if (bdCosting.getWarehouse() == null) {
            where.append(" and c." + Costing.PROPERTY_WAREHOUSE + " is null");
        } else {
            where.append(" and c." + Costing.PROPERTY_WAREHOUSE + " = :warehouse");
        }
        where.append("   and c." + Costing.PROPERTY_ENDINGDATE + " = :endDate");

        where.append(" order by ");
        where.append(" trx." + MaterialTransaction.PROPERTY_MOVEMENTDATE + " desc, ");
        where.append(" trx." + MaterialTransaction.PROPERTY_TRANSACTIONPROCESSDATE + " desc");

        OBQuery<Costing> qryCosting = OBDal.getInstance().createQuery(Costing.class, where.toString());
        qryCosting.setNamedParameter("product", bdCosting.getProduct());
        // FIXME: remove when manufacturing costs are fully migrated
        if (bdCosting.getProduct().isProduction()) {
            qryCosting.setNamedParameter("client", bdCosting.getClient());
        } else {
            qryCosting.setNamedParameter("org", bdCosting.getOrganization());
        }
        if (bdCosting.getWarehouse() != null) {
            qryCosting.setNamedParameter("warehouse", bdCosting.getWarehouse());
        }
        qryCosting.setNamedParameter("endDate", bdCosting.getStartingDate());

        qryCosting.setMaxResult(1);

        Costing lastCosting = qryCosting.uniqueResult();
        bdCostingId = bdCosting.getId();
        lastCosting.setEndingDate(bdCosting.getEndingDate());
        OBDal.getInstance().save(lastCosting);
    }

    /**
     * Updates the backdated average costing time range.
     * 
     * <br>
     * The starting date of the bdCosting is the transaction process date of the given trx. The ending
     * date is defined by the average costing that is being shortened.
     * 
     * @param trx
     *          The material transaction that is used as a reference to set the new time range of the
     *          backdated average costing.
     */
    private void updateBDCostingTimeRange(MaterialTransaction trx) {
        Costing bdCosting = OBDal.getInstance().get(Costing.class, bdCostingId);
        bdCosting.setPermanent(Boolean.FALSE);
        OBDal.getInstance().save(bdCosting);
        // Fire trigger to allow to modify the average cost and starting date.
        OBDal.getInstance().flush();

        Costing curCosting = getTrxCurrentCosting(trx);
        if (curCosting != null) {
            bdCosting.setEndingDate(curCosting.getEndingDate());
            curCosting.setEndingDate(trx.getTransactionProcessDate());
            OBDal.getInstance().save(curCosting);
        } else {
            // There isn't any previous costing.
            bdCosting.setEndingDate(trx.getTransactionProcessDate());

        }
        bdCosting.setStartingDate(trx.getTransactionProcessDate());
        bdCosting.setPermanent(Boolean.TRUE);
        OBDal.getInstance().save(bdCosting);
    }

    /**
     * Returns the average costing that is valid on the given transaction process date.
     * 
     * @param trx
     *          MaterialTransaction to be used as time reference.
     * @return The average Costing
     */
    private Costing getTrxCurrentCosting(MaterialTransaction trx) {
        HashMap<CostDimension, BaseOBObject> costDimensions = getCostDimensions();
        StringBuffer where = new StringBuffer();
        where.append(" as c");
        where.append(" where c." + Costing.PROPERTY_PRODUCT + " = :product");
        // FIXME: remove when manufacturing costs are fully migrated
        if (isManufacturingProduct) {
            where.append("  and c." + Costing.PROPERTY_CLIENT + " = :client");
        } else {
            where.append("  and c." + Costing.PROPERTY_ORGANIZATION + " = :org");
        }
        if (costDimensions.get(CostDimension.Warehouse) == null) {
            where.append(" and c." + Costing.PROPERTY_WAREHOUSE + " is null");
        } else {
            where.append(" and c." + Costing.PROPERTY_WAREHOUSE + " = :warehouse");
        }
        where.append("   and c.id != :sourceid");
        where.append("   and c." + Costing.PROPERTY_ENDINGDATE + " >= :trxdate");
        // The starting date of the costing needs to be before the reference date to avoid the case when
        // the given transaction has a related average costing.
        where.append("   and c." + Costing.PROPERTY_STARTINGDATE + " < :trxdate");
        where.append(" order by c." + Costing.PROPERTY_STARTINGDATE + " desc");

        OBQuery<Costing> qryCosting = OBDal.getInstance().createQuery(Costing.class, where.toString());
        qryCosting.setNamedParameter("product", trx.getProduct());
        // FIXME: remove when manufacturing costs are fully migrated
        if (isManufacturingProduct) {
            qryCosting.setNamedParameter("client", OBDal.getInstance().get(Client.class, strClientId));
        } else {
            qryCosting.setNamedParameter("org", getCostOrg());
        }
        if (costDimensions.get(CostDimension.Warehouse) != null) {
            qryCosting.setNamedParameter("warehouse", costDimensions.get(CostDimension.Warehouse));
        }
        qryCosting.setNamedParameter("sourceid", bdCostingId);
        qryCosting.setNamedParameter("trxdate", trx.getTransactionProcessDate());

        qryCosting.setMaxResult(1);

        return qryCosting.uniqueResult();
    }

    private boolean isVoidedTrx(MaterialTransaction trx, TrxType currentTrxType) {
        // Transactions of voided documents do not need adjustment
        switch (currentTrxType) {
        case ReceiptVoid:
        case ShipmentVoid:
        case InternalConsVoid:
            return true;
        case Receipt:
        case ReceiptNegative:
        case ReceiptReturn:
        case Shipment:
        case ShipmentNegative:
        case ShipmentReturn:
            if (trx.getGoodsShipmentLine().getShipmentReceipt().getDocumentStatus().equals("VO")) {
                return true;
            }
            break;
        case InternalCons:
        case InternalConsNegative:
            if (trx.getInternalConsumptionLine().getInternalConsumption().getStatus().equals("VO")) {
                return true;
            }
            break;
        default:
            break;
        }
        return false;
    }

    /**
     * Returns true if a transaction is a backdated transaction (has related backdated transaction
     * adjustments).
     * 
     * @param trx
     *          MaterialTransaction to check if is backdated or not.
     * @return boolean
     */
    private boolean isBackdatedTransaction(MaterialTransaction trx) {
        OBCriteria<CostAdjustmentLine> critLines = OBDal.getInstance().createCriteria(CostAdjustmentLine.class);
        critLines.add(Restrictions.eq(CostAdjustmentLine.PROPERTY_INVENTORYTRANSACTION, trx));
        critLines.add(Restrictions.eq(CostAdjustmentLine.PROPERTY_ISBACKDATEDTRX, true));
        final List<CostAdjustmentLine> critLinesList = critLines.list();
        if (critLinesList.size() > 0) {
            return true;
        }
        return false;
    }
}