com.axelor.apps.supplychain.service.MrpServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.axelor.apps.supplychain.service.MrpServiceImpl.java

Source

/**
 * Axelor Business Solutions
 *
 * Copyright (C) 2015 Axelor (<http://axelor.com>).
 *
 * This program is free software: you can redistribute it and/or  modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.axelor.apps.supplychain.service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.joda.time.LocalDate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.axelor.apps.base.db.Partner;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.db.SupplierCatalog;
import com.axelor.apps.base.db.Unit;
import com.axelor.apps.base.db.repo.ProductRepository;
import com.axelor.apps.base.service.UnitConversionService;
import com.axelor.apps.base.service.administration.GeneralService;
import com.axelor.apps.purchase.db.IPurchaseOrder;
import com.axelor.apps.purchase.db.PurchaseOrder;
import com.axelor.apps.purchase.db.PurchaseOrderLine;
import com.axelor.apps.purchase.db.repo.PurchaseOrderLineRepository;
import com.axelor.apps.sale.db.ISaleOrder;
import com.axelor.apps.sale.db.SaleOrder;
import com.axelor.apps.sale.db.SaleOrderLine;
import com.axelor.apps.sale.db.repo.SaleOrderLineRepository;
import com.axelor.apps.sale.db.repo.SaleOrderRepository;
import com.axelor.apps.stock.db.Location;
import com.axelor.apps.stock.db.LocationLine;
import com.axelor.apps.stock.db.MinStockRules;
import com.axelor.apps.stock.db.repo.LocationLineRepository;
import com.axelor.apps.stock.db.repo.LocationRepository;
import com.axelor.apps.stock.db.repo.MinStockRulesRepository;
import com.axelor.apps.stock.service.MinStockRulesService;
import com.axelor.apps.supplychain.db.Mrp;
import com.axelor.apps.supplychain.db.MrpFamily;
import com.axelor.apps.supplychain.db.MrpForecast;
import com.axelor.apps.supplychain.db.MrpLine;
import com.axelor.apps.supplychain.db.MrpLineOrigin;
import com.axelor.apps.supplychain.db.MrpLineType;
import com.axelor.apps.supplychain.db.repo.MrpForecastRepository;
import com.axelor.apps.supplychain.db.repo.MrpLineRepository;
import com.axelor.apps.supplychain.db.repo.MrpLineTypeRepository;
import com.axelor.apps.supplychain.db.repo.MrpRepository;
import com.axelor.apps.supplychain.exception.IExceptionMessage;
import com.axelor.db.Model;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.IException;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;

public class MrpServiceImpl implements MrpService {

    private final Logger log = LoggerFactory.getLogger(getClass());

    protected MrpRepository mrpRepository;
    protected LocationRepository locationRepository;
    protected ProductRepository productRepository;
    protected LocationLineRepository locationLineRepository;
    protected MrpLineTypeRepository mrpLineTypeRepository;
    protected PurchaseOrderLineRepository purchaseOrderLineRepository;
    protected SaleOrderLineRepository saleOrderLineRepository;
    protected MrpLineRepository mrpLineRepository;
    protected MinStockRulesService minStockRulesService;
    protected MrpLineService mrpLineService;
    protected MrpForecastRepository mrpForecastRepository;

    protected LocalDate today;

    protected List<Location> locationList = Lists.newArrayList();
    protected Map<Product, Integer> productMap = Maps.newHashMap();
    protected Mrp mrp;

    @Inject
    public MrpServiceImpl(GeneralService generalService, MrpRepository mrpRepository,
            LocationRepository locationRepository, ProductRepository productRepository,
            LocationLineRepository locationLineRepository, MrpLineTypeRepository mrpLineTypeRepository,
            PurchaseOrderLineRepository purchaseOrderLineRepository,
            SaleOrderLineRepository saleOrderLineRepository, MrpLineRepository mrpLineRepository,
            MinStockRulesService minStockRulesService, MrpLineService mrpLineService,
            MrpForecastRepository mrpForecastRepository) {

        this.mrpRepository = mrpRepository;
        this.locationRepository = locationRepository;
        this.productRepository = productRepository;
        this.locationLineRepository = locationLineRepository;
        this.mrpLineTypeRepository = mrpLineTypeRepository;
        this.purchaseOrderLineRepository = purchaseOrderLineRepository;
        this.saleOrderLineRepository = saleOrderLineRepository;
        this.mrpLineRepository = mrpLineRepository;
        this.minStockRulesService = minStockRulesService;
        this.mrpLineService = mrpLineService;
        this.mrpForecastRepository = mrpForecastRepository;

        this.today = generalService.getTodayDate();
    }

    public void runCalculation(Mrp mrp) throws AxelorException {

        this.startMrp(mrpRepository.find(mrp.getId()));
        this.completeMrp(mrpRepository.find(mrp.getId()));
        this.doCalulation(mrpRepository.find(mrp.getId()));
    }

    @Transactional(rollbackOn = { AxelorException.class, Exception.class })
    protected void startMrp(Mrp mrp) {

        log.debug("Start MRP");

        mrp.setStatusSelect(MrpRepository.STATUS_CALCULATION_STARTED);

        //TODO check that the different types used for purchase/manufOrder proposal are in stock type
        //TODO check that all types exist + override the method on production module

        mrp.clearMrpLineList();

        mrpRepository.save(mrp);
    }

    @Transactional(rollbackOn = { AxelorException.class, Exception.class })
    public void reset(Mrp mrp) {

        mrp.setStatusSelect(MrpRepository.STATUS_DRAFT);

        mrp.clearMrpLineList();

        mrpRepository.save(mrp);

    }

    @Transactional(rollbackOn = { AxelorException.class, Exception.class })
    protected void completeMrp(Mrp mrp) throws AxelorException {

        log.debug("Complete MRP");

        // Initialize 
        this.mrp = mrp;
        this.locationList = this.getAllLocationAndSubLocation(mrp.getLocation());
        this.assignProductAndLevel(this.getProductList());

        // Get the stock for each product on each location
        this.createAvailableStockMrpLines();

        this.createPurchaseMrpLines();

        this.createSaleOrderMrpLines();

        this.createSaleForecastMrpLines();

        mrpRepository.save(mrp);
    }

    @Transactional(rollbackOn = { AxelorException.class, Exception.class })
    protected void doCalulation(Mrp mrp) throws AxelorException {

        log.debug("Do calculation");

        mrpRepository.save(mrp);

        this.checkInsufficientCumulativeQty();

        //      this.consolidateMrp(mrp);

        mrp.setStatusSelect(MrpRepository.STATUS_CALCULATION_ENDED);

    }

    protected void checkInsufficientCumulativeQty() throws AxelorException {

        for (int level = 0; level <= this.getMaxLevel(); level++) {

            for (Product product : this.getProductList(level)) {

                this.checkInsufficientCumulativeQty(product);

            }

        }

    }

    /**
     * Get the list of product for a level
     * @param level
     * @return
     */
    protected List<Product> getProductList(int level) {

        List<Product> productList = Lists.newArrayList();

        for (Product product : this.productMap.keySet()) {

            if (this.productMap.get(product) == level) {
                productList.add(product);
            }

        }

        return productList;

    }

    protected int getMaxLevel() {

        int maxLevel = 0;

        for (int level : this.productMap.values()) {

            if (level > maxLevel) {
                maxLevel = level;
            }

        }

        return maxLevel;
    }

    protected void checkInsufficientCumulativeQty(Product product) throws AxelorException {

        boolean doASecondPass = false;

        this.computeCumulativeQty(product);

        List<MrpLine> mrpLineList = mrpLineRepository.all()
                .filter("self.mrp = ?1 AND self.product = ?2", mrp, product).order("maturityDate")
                .order("mrpLineType.typeSelect").order("mrpLineType.sequence").order("id").fetch();

        for (MrpLine mrpLine : mrpLineList) {

            BigDecimal cumulativeQty = mrpLine.getCumulativeQty();

            MrpLineType mrpLineType = mrpLine.getMrpLineType();

            boolean isProposalElement = this.isProposalElement(mrpLineType);

            BigDecimal minQty = mrpLine.getMinQty();

            if (mrpLine.getMrpLineType().getElementSelect() != MrpLineTypeRepository.ELEMENT_AVAILABLE_STOCK
                    && (!isProposalElement
                            || (isProposalElement && mrpLineType.getTypeSelect() == MrpLineTypeRepository.TYPE_OUT))
                    && cumulativeQty.compareTo(mrpLine.getMinQty()) == -1) {

                log.debug("Cumulative qty ({} < {}) is insufficient for product ({}) at the maturity date ({})",
                        cumulativeQty, minQty, product.getFullName(), mrpLine.getMaturityDate());

                BigDecimal reorderQty = minQty.subtract(cumulativeQty);

                MinStockRules minStockRules = minStockRulesService.getMinStockRules(product, mrpLine.getLocation(),
                        MinStockRulesRepository.TYPE_FUTURE);

                if (minStockRules != null) {
                    reorderQty = reorderQty.max(minStockRules.getReOrderQty());
                }

                MrpLineType mrpLineTypeProposal = this.getMrpLineTypeForProposal(minStockRules);

                this.createProposalMrpLine(product, mrpLineTypeProposal, reorderQty, mrpLine.getLocation(),
                        mrpLine.getMaturityDate(), mrpLine.getMrpLineOriginList(),
                        mrpLine.getRelatedToSelectName());

                doASecondPass = true;

                break;
            }
        }

        if (doASecondPass) {
            mrpRepository.save(mrp);

            this.checkInsufficientCumulativeQty(product);
        }

    }

    public MrpLine getPreviousProposalMrpLine(Product product, MrpLineType mrpLineType, Location location,
            LocalDate maturityDate) {

        LocalDate startPeriodDate = maturityDate;

        MrpFamily mrpFamily = product.getMrpFamily();

        if (mrpFamily != null) {

            if (mrpFamily.getDayNb() == 0) {
                return null;
            }

            startPeriodDate = maturityDate.minusDays(mrpFamily.getDayNb());

        }

        return mrpLineRepository.all().filter(
                "self.mrp = ?1 AND self.product = ?2 AND self.mrpLineType = ?3 AND self.location = ?4 AND self.maturityDate > ?5 AND self.maturityDate <= ?6",
                mrp, product, mrpLineType, location, startPeriodDate, maturityDate).fetchOne();

    }

    protected void createProposalMrpLine(Product product, MrpLineType mrpLineType, BigDecimal reorderQty,
            Location location, LocalDate maturityDate, List<MrpLineOrigin> mrpLineOriginList,
            String relatedToSelectName) throws AxelorException {

        if (mrpLineType.getElementSelect() == MrpLineTypeRepository.ELEMENT_PURCHASE_PROPOSAL) {
            maturityDate = maturityDate.minusDays(product.getSupplierDeliveryTime());
            reorderQty = reorderQty.max(this.getSupplierCatalogMinQty(product));
        }

        MrpLine mrpLine = this.getPreviousProposalMrpLine(product, mrpLineType, location, maturityDate);

        if (mrpLine != null) {
            mrpLine.setQty(mrpLine.getQty().add(reorderQty));
            mrpLine.setRelatedToSelectName(null);

        } else {

            mrpLine = mrpLineRepository.save(
                    this.createMrpLine(product, mrpLineType, reorderQty, maturityDate, BigDecimal.ZERO, location));
            mrp.addMrpLineListItem(mrpLine);
            mrpLine.setRelatedToSelectName(relatedToSelectName);

        }

        this.copyMrpLineOrigins(mrpLine, mrpLineOriginList);

    }

    protected BigDecimal getSupplierCatalogMinQty(Product product) {

        Partner supplierPartner = product.getDefaultSupplierPartner();

        if (supplierPartner != null) {

            for (SupplierCatalog supplierCatalog : product.getSupplierCatalogList()) {

                if (supplierCatalog.getSupplierPartner().equals(supplierPartner)) {
                    return supplierCatalog.getMinQty();
                }
            }
        }
        return BigDecimal.ZERO;
    }

    protected MrpLineType getMrpLineTypeForProposal(MinStockRules minStockRules) throws AxelorException {

        return this.getMrpLineType(MrpLineTypeRepository.ELEMENT_PURCHASE_PROPOSAL);

    }

    protected void consolidateMrp() {

        List<MrpLine> mrpLineList = mrpLineRepository.all().filter("self.mrp = ?1", mrp).order("self.product.code")
                .order("maturityDate").order("mrpLineType.typeSelect").order("mrpLineType.sequence").order("id")
                .fetch();

        Map<List<Object>, MrpLine> map = Maps.newHashMap();
        MrpLine consolidateMrpLine = null;
        List<Object> keys = new ArrayList<Object>();

        for (MrpLine mrpLine : mrpLineList) {

            MrpLineType mrpLineType = mrpLine.getMrpLineType();

            keys.clear();
            keys.add(mrpLineType);
            keys.add(mrpLine.getProduct());
            keys.add(mrpLine.getMaturityDate());
            keys.add(mrpLine.getLocation());

            if (map.containsKey(keys)) {

                consolidateMrpLine = map.get(keys);
                consolidateMrpLine.setQty(consolidateMrpLine.getQty().add(mrpLine.getQty()));
                consolidateMrpLine
                        .setCumulativeQty(consolidateMrpLine.getCumulativeQty().add(mrpLine.getCumulativeQty()));

            } else {
                map.put(keys, mrpLine);
            }

        }

        mrp.getMrpLineList().clear();

        mrp.getMrpLineList().addAll(map.values());

    }

    protected boolean isProposalElement(MrpLineType mrpLineType) {

        if (mrpLineType.getElementSelect() == MrpLineTypeRepository.ELEMENT_PURCHASE_PROPOSAL) {

            return true;

        }

        return false;
    }

    protected void computeCumulativeQty() {

        for (Product product : this.productMap.keySet()) {

            this.computeCumulativeQty(product);

        }

    }

    protected void computeCumulativeQty(Product product) {

        List<MrpLine> mrpLineList = mrpLineRepository.all()
                .filter("self.mrp = ?1 AND self.product = ?2", mrp, product).order("maturityDate")
                .order("mrpLineType.typeSelect").order("mrpLineType.sequence").order("id").fetch();

        BigDecimal previousCumulativeQty = BigDecimal.ZERO;

        for (MrpLine mrpLine : mrpLineList) {

            if (mrpLine.getMrpLineType().getElementSelect() == MrpLineTypeRepository.ELEMENT_AVAILABLE_STOCK) {

                mrpLine.setCumulativeQty(mrpLine.getQty());
            } else {

                mrpLine.setCumulativeQty(previousCumulativeQty.add(mrpLine.getQty()));

            }

            previousCumulativeQty = mrpLine.getCumulativeQty();

            log.debug("Cumulative qty is ({}) for product ({}) and move ({}) at the maturity date ({})",
                    previousCumulativeQty, mrpLine.getProduct().getFullName(), mrpLine.getMrpLineType().getName(),
                    mrpLine.getMaturityDate());

        }

    }

    // achat ferme
    protected void createPurchaseMrpLines() throws AxelorException {

        MrpLineType purchaseProposalMrpLineType = this.getMrpLineType(MrpLineTypeRepository.ELEMENT_PURCHASE_ORDER);

        // TODO : Manage the case where order is partially delivered
        List<PurchaseOrderLine> purchaseOrderLineList = purchaseOrderLineRepository.all().filter(
                "self.product in (?1) AND self.purchaseOrder.location in (?2) AND self.purchaseOrder.receiptState = ?3 "
                        + "AND self.purchaseOrder.statusSelect = ?4",
                this.productMap.keySet(), this.locationList, IPurchaseOrder.STATE_NOT_RECEIVED,
                IPurchaseOrder.STATUS_VALIDATED).fetch();

        for (PurchaseOrderLine purchaseOrderLine : purchaseOrderLineList) {

            PurchaseOrder purchaseOrder = purchaseOrderLine.getPurchaseOrder();

            LocalDate maturityDate = purchaseOrderLine.getEstimatedDelivDate();

            if (maturityDate == null) {
                maturityDate = purchaseOrder.getDeliveryDate();
            }
            if (maturityDate == null) {
                maturityDate = purchaseOrder.getOrderDate();
            }

            if (this.isBeforeEndDate(maturityDate)) {
                Unit unit = purchaseOrderLine.getProduct().getUnit();
                BigDecimal qty = purchaseOrderLine.getQty();
                if (!unit.equals(purchaseOrderLine.getUnit())) {
                    qty = Beans.get(UnitConversionService.class).convertWithProduct(purchaseOrderLine.getUnit(),
                            unit, qty, purchaseOrderLine.getProduct());
                }
                mrp.addMrpLineListItem(
                        this.createMrpLine(purchaseOrderLine.getProduct(), purchaseProposalMrpLineType, qty,
                                maturityDate, BigDecimal.ZERO, purchaseOrder.getLocation(), purchaseOrderLine));
            }
        }
    }

    // Vente ferme
    protected void createSaleOrderMrpLines() throws AxelorException {

        MrpLineType saleForecastMrpLineType = this.getMrpLineType(MrpLineTypeRepository.ELEMENT_SALE_ORDER);

        // TODO : Manage the case where order is partially delivered
        List<SaleOrderLine> saleOrderLineList = new ArrayList<>();

        if (mrp.getSaleOrderLineSet().isEmpty()) {

            saleOrderLineList.addAll(saleOrderLineRepository.all().filter(
                    "self.product in (?1) AND self.saleOrder.location in (?2) AND self.saleOrder.deliveryState = ?3 "
                            + "AND self.saleOrder.statusSelect = ?4",
                    this.productMap.keySet(), this.locationList, SaleOrderRepository.STATE_NOT_DELIVERED,
                    ISaleOrder.STATUS_ORDER_CONFIRMED).fetch());

        } else {

            saleOrderLineList.addAll(mrp.getSaleOrderLineSet());

        }

        for (SaleOrderLine saleOrderLine : saleOrderLineList) {

            SaleOrder saleOrder = saleOrderLine.getSaleOrder();

            LocalDate maturityDate = saleOrderLine.getEstimatedDelivDate();

            if (maturityDate == null) {
                maturityDate = saleOrder.getDeliveryDate();
            }
            if (maturityDate == null) {
                maturityDate = saleOrder.getOrderDate();
            }
            if (maturityDate == null) {
                maturityDate = saleOrder.getCreationDate();
            }

            if (this.isBeforeEndDate(maturityDate)) {
                Unit unit = saleOrderLine.getProduct().getUnit();
                BigDecimal qty = saleOrderLine.getQty();
                if (!unit.equals(saleOrderLine.getUnit())) {
                    qty = Beans.get(UnitConversionService.class).convertWithProduct(saleOrderLine.getUnit(), unit,
                            qty, saleOrderLine.getProduct());
                }
                mrp.addMrpLineListItem(this.createMrpLine(saleOrderLine.getProduct(), saleForecastMrpLineType, qty,
                        maturityDate, BigDecimal.ZERO, saleOrder.getLocation(), saleOrderLine));
            }

        }
    }

    protected void createSaleForecastMrpLines() throws AxelorException {

        MrpLineType saleForecastMrpLineType = this.getMrpLineType(MrpLineTypeRepository.ELEMENT_SALE_FORECAST);

        List<MrpForecast> mrpForecastList = new ArrayList<>();

        if (mrp.getMrpForecastSet().isEmpty()) {

            mrpForecastList.addAll(mrpForecastRepository.all()
                    .filter("self.product in (?1) AND self.location in (?2) AND self.forecastDate >= ?3",
                            this.productMap.keySet(), this.locationList, today, today)
                    .fetch());

        } else {
            mrpForecastList.addAll(mrp.getMrpForecastSet());
        }

        for (MrpForecast mrpForecast : mrpForecastList) {

            LocalDate maturityDate = mrpForecast.getForecastDate();

            if (this.isBeforeEndDate(maturityDate)) {
                Unit unit = mrpForecast.getProduct().getUnit();
                BigDecimal qty = mrpForecast.getQty();
                if (!unit.equals(mrpForecast.getUnit())) {
                    qty = Beans.get(UnitConversionService.class).convertWithProduct(mrpForecast.getUnit(), unit,
                            qty, mrpForecast.getProduct());
                }
                mrp.addMrpLineListItem(this.createMrpLine(mrpForecast.getProduct(), saleForecastMrpLineType, qty,
                        maturityDate, BigDecimal.ZERO, mrpForecast.getLocation(), mrpForecast));
            }
        }
    }

    public boolean isBeforeEndDate(LocalDate maturityDate) {

        if (maturityDate != null && !maturityDate.isBefore(today)
                && (mrp.getEndDate() == null || !maturityDate.isAfter(mrp.getEndDate()))) {

            return true;
        }

        return false;
    }

    protected void createAvailableStockMrpLines() throws AxelorException {

        MrpLineType availableStockMrpLineType = this.getMrpLineType(MrpLineTypeRepository.ELEMENT_AVAILABLE_STOCK);

        for (Product product : this.productMap.keySet()) {

            for (Location location : this.locationList) {

                mrp.addMrpLineListItem(
                        this.createAvailableStockMrpLine(product, location, availableStockMrpLineType));

            }
        }
    }

    protected MrpLine createAvailableStockMrpLine(Product product, Location location,
            MrpLineType availableStockMrpLineType) {

        BigDecimal qty = BigDecimal.ZERO;

        LocationLine locationLine = this.getLocationLine(product, location);

        if (locationLine != null) {

            qty = locationLine.getCurrentQty();
        }

        return this.createMrpLine(product, availableStockMrpLineType, qty, today, qty, location);
    }

    protected MrpLineType getMrpLineType(int elementSelect) throws AxelorException {

        MrpLineType mrpLineType = mrpLineTypeRepository.all().filter("self.elementSelect = ?1", elementSelect)
                .fetchOne();

        if (mrpLineType != null) {

            return mrpLineType;
        }

        throw new AxelorException(
                String.format(I18n.get(IExceptionMessage.MRP_MISSING_MRP_LINE_TYPE), elementSelect),
                IException.CONFIGURATION_ERROR);

        //TODO get the right label in fact of integer value

    }

    protected LocationLine getLocationLine(Product product, Location location) {

        return locationLineRepository.all().filter("self.location = ?1 AND self.product = ?2", location, product)
                .fetchOne();

    }

    protected Set<Product> getProductList() throws AxelorException {

        Set<Product> productSet = Sets.newHashSet();

        if (!mrp.getProductSet().isEmpty()) {

            productSet.addAll(mrp.getProductSet());

        }

        if (!mrp.getProductCategorySet().isEmpty()) {

            productSet.addAll(productRepository.all().filter(
                    "self.productCategory in (?1) AND self.productTypeSelect = ?2 AND self.excludeFromMrp = false",
                    mrp.getProductCategorySet(), ProductRepository.PRODUCT_TYPE_STORABLE).fetch());

        }

        if (!mrp.getProductFamilySet().isEmpty()) {

            productSet.addAll(productRepository.all().filter(
                    "self.productFamily in (?1) AND self.productTypeSelect = ?2 AND self.excludeFromMrp = false",
                    mrp.getProductFamilySet(), ProductRepository.PRODUCT_TYPE_STORABLE).fetch());

        }

        for (SaleOrderLine saleOrderLine : mrp.getSaleOrderLineSet()) {

            productSet.add(saleOrderLine.getProduct());
        }

        for (MrpForecast mrpForecast : mrp.getMrpForecastSet()) {

            productSet.add(mrpForecast.getProduct());

        }

        if (productSet.isEmpty()) {
            throw new AxelorException(String.format(I18n.get(IExceptionMessage.MRP_NO_PRODUCT)),
                    IException.CONFIGURATION_ERROR);
        }

        return productSet;

    }

    public boolean isMrpProduct(Product product) {

        if (product != null && !product.getExcludeFromMrp()
                && product.getProductTypeSelect().equals(ProductRepository.PRODUCT_TYPE_STORABLE)) {

            return true;
        }

        return false;

    }

    protected void assignProductAndLevel(Set<Product> productList) {

        for (Product product : productList) {

            this.assignProductAndLevel(product);

        }

    }

    protected void assignProductAndLevel(Product product) {

        log.debug("Add of the product : {}", product.getFullName());
        this.productMap.put(product, 0);

    }

    protected MrpLine createMrpLine(Product product, MrpLineType mrpLineType, BigDecimal qty,
            LocalDate maturityDate, BigDecimal cumulativeQty, Location location, Model... models) {

        return mrpLineService.createMrpLine(product, this.productMap.get(product), mrpLineType, qty, maturityDate,
                cumulativeQty, location, models);

    }

    protected void copyMrpLineOrigins(MrpLine mrpLine, List<MrpLineOrigin> mrpLineOriginList) {

        if (mrpLineOriginList != null) {

            for (MrpLineOrigin mrpLineOrigin : mrpLineOriginList) {

                mrpLine.addMrpLineOriginListItem(mrpLineService.copyMrpLineOrigin(mrpLineOrigin));

            }

        }

    }

    protected List<Location> getAllLocationAndSubLocation(Location location) {

        List<Location> subLocationList = locationRepository.all().filter("self.parent = ?1", location).fetch();

        for (Location subLocation : subLocationList) {

            subLocationList.addAll(this.getAllLocationAndSubLocation(subLocation));

        }

        subLocationList.add(location);

        return subLocationList;
    }

    @Transactional(rollbackOn = { AxelorException.class, Exception.class })
    public void generateProposals(Mrp mrp) throws AxelorException {

        for (MrpLine mrpLine : mrp.getMrpLineList()) {

            mrpLineService.generateProposal(mrpLine);

        }

    }

}