com.axelor.apps.cash.management.service.ForecastRecapService.java Source code

Java tutorial

Introduction

Here is the source code for com.axelor.apps.cash.management.service.ForecastRecapService.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.cash.management.service;

import java.io.IOException;

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

import org.joda.time.LocalDate;

import com.axelor.apps.ReportFactory;
import com.axelor.apps.account.db.Invoice;
import com.axelor.apps.account.db.repo.InvoiceRepository;
import com.axelor.apps.base.service.CurrencyService;
import com.axelor.apps.base.service.administration.GeneralService;
import com.axelor.apps.cash.management.db.Forecast;
import com.axelor.apps.cash.management.db.ForecastReason;
import com.axelor.apps.cash.management.db.ForecastRecap;
import com.axelor.apps.cash.management.db.ForecastRecapLine;
import com.axelor.apps.cash.management.db.repo.ForecastRecapLineRepository;
import com.axelor.apps.cash.management.db.repo.ForecastRecapRepository;
import com.axelor.apps.cash.management.db.repo.ForecastRepository;
import com.axelor.apps.cash.management.report.IReport;
import com.axelor.apps.crm.db.Opportunity;
import com.axelor.apps.crm.db.repo.OpportunityRepository;
import com.axelor.apps.hr.db.Employee;
import com.axelor.apps.hr.db.Expense;
import com.axelor.apps.hr.db.repo.EmployeeRepository;
import com.axelor.apps.hr.db.repo.ExpenseRepository;
import com.axelor.apps.purchase.db.PurchaseOrder;
import com.axelor.apps.purchase.db.repo.PurchaseOrderRepository;
import com.axelor.apps.sale.db.SaleOrder;
import com.axelor.apps.sale.db.repo.SaleOrderRepository;
import com.axelor.apps.supplychain.db.Timetable;
import com.axelor.apps.supplychain.db.repo.TimetableRepository;
import com.axelor.apps.supplychain.service.TimetableService;
import com.axelor.exception.AxelorException;
import com.axelor.inject.Beans;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import com.axelor.i18n.I18n;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ForecastRecapService {

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

    @Inject
    protected TimetableService timetableService;

    @Inject
    protected GeneralService generalService;

    @Inject
    protected ForecastRepository forecastRepo;

    @Inject
    protected ForecastRecapLineRepository forecastRecapLineRepo;

    @Inject
    protected CurrencyService currencyService;

    @Inject
    protected ReportFactory reportFactory;

    public void populate(ForecastRecap forecastRecap) throws AxelorException {
        List<ForecastRecapLine> forecastRecapLineList = forecastRecap.getForecastRecapLineList();
        if (forecastRecapLineList != null && !forecastRecapLineList.isEmpty()) {
            for (ForecastRecapLine forecastRecapLine : forecastRecapLineList) {
                if (forecastRecapLine.getId() != null && forecastRecapLine.getId() > 0) {
                    forecastRecapLineRepo.remove(forecastRecapLine);
                }
            }
            forecastRecapLineList.clear();
        }
        forecastRecap.setCurrentBalance(forecastRecap.getStartingBalance());
        if (forecastRecap.getOpportunitiesTypeSelect() != null
                && forecastRecap.getOpportunitiesTypeSelect() > ForecastRecapRepository.OPPORTUNITY_TYPE_NO) {
            this.populateWithOpportunities(forecastRecap);
        }
        this.populateWithInvoices(forecastRecap);
        this.populateWithSalaries(forecastRecap);
        this.populateWithTimetables(forecastRecap);
        this.populateWithForecasts(forecastRecap);
        this.populateWithExpenses(forecastRecap);
        forecastRecap.setEndingBalance(forecastRecap.getCurrentBalance());
    }

    public void populateWithOpportunities(ForecastRecap forecastRecap) throws AxelorException {
        List<Opportunity> opportunityList = new ArrayList<Opportunity>();
        if (forecastRecap.getBankDetails() != null) {
            opportunityList = Beans.get(OpportunityRepository.class).all().filter(
                    "self.company = ?1 AND self.bankDetails = ?2 AND self.expectedCloseDate BETWEEN ?3 AND ?4 AND self.saleOrder = null",
                    forecastRecap.getCompany(), forecastRecap.getBankDetails(), forecastRecap.getFromDate(),
                    forecastRecap.getToDate()).fetch();
        } else {
            opportunityList = Beans.get(OpportunityRepository.class).all().filter(
                    "self.company = ?1 AND self.expectedCloseDate BETWEEN ?2 AND ?3 AND self.saleOrder = null",
                    forecastRecap.getCompany(), forecastRecap.getFromDate(), forecastRecap.getToDate()).fetch();
        }
        for (Opportunity opportunity : opportunityList) {
            BigDecimal amountCompanyCurr = BigDecimal.ZERO;
            if (forecastRecap.getOpportunitiesTypeSelect() == ForecastRecapRepository.OPPORTUNITY_TYPE_BASE) {
                amountCompanyCurr = currencyService.getAmountCurrencyConverted(opportunity.getCurrency(),
                        opportunity.getCompany().getCurrency(),
                        opportunity.getAmount().multiply(opportunity.getProbability()).divide(new BigDecimal(100),
                                2, RoundingMode.HALF_UP),
                        generalService.getTodayDate()).setScale(2, RoundingMode.HALF_UP);
                forecastRecap.setCurrentBalance(forecastRecap.getCurrentBalance().add(amountCompanyCurr));
                forecastRecap.addForecastRecapLineListItem(
                        this.createForecastRecapLine(opportunity.getExpectedCloseDate(), 1, null, amountCompanyCurr,
                                forecastRecap.getCurrentBalance()));
            } else if (forecastRecap
                    .getOpportunitiesTypeSelect() == ForecastRecapRepository.OPPORTUNITY_TYPE_BEST) {
                amountCompanyCurr = currencyService.getAmountCurrencyConverted(opportunity.getCurrency(),
                        opportunity.getCompany().getCurrency(),
                        new BigDecimal(opportunity.getBestCase()).multiply(opportunity.getProbability()).divide(
                                new BigDecimal(100), 2, RoundingMode.HALF_UP),
                        generalService.getTodayDate()).setScale(2, RoundingMode.HALF_UP);
                forecastRecap.setCurrentBalance(forecastRecap.getCurrentBalance().add(amountCompanyCurr));
                forecastRecap.addForecastRecapLineListItem(
                        this.createForecastRecapLine(opportunity.getExpectedCloseDate(), 1, null, amountCompanyCurr,
                                forecastRecap.getCurrentBalance()));
            } else {
                amountCompanyCurr = currencyService.getAmountCurrencyConverted(opportunity.getCurrency(),
                        opportunity.getCompany().getCurrency(),
                        new BigDecimal(opportunity.getWorstCase()).multiply(opportunity.getProbability()).divide(
                                new BigDecimal(100), 2, RoundingMode.HALF_UP),
                        generalService.getTodayDate()).setScale(2, RoundingMode.HALF_UP);
                forecastRecap.setCurrentBalance(forecastRecap.getCurrentBalance().add(amountCompanyCurr));
                forecastRecap.addForecastRecapLineListItem(
                        this.createForecastRecapLine(opportunity.getExpectedCloseDate(), 1, null, amountCompanyCurr,
                                forecastRecap.getCurrentBalance()));
            }
        }
    }

    public void getOpportunities(ForecastRecap forecastRecap, Map<LocalDate, BigDecimal> mapExpected,
            Map<LocalDate, BigDecimal> mapConfirmed) throws AxelorException {
        List<Opportunity> opportunityList = new ArrayList<Opportunity>();
        if (forecastRecap.getBankDetails() != null) {
            opportunityList = Beans.get(OpportunityRepository.class).all().filter(
                    "self.company = ?1 AND self.bankDetails = ?2 AND self.expectedCloseDate BETWEEN ?3 AND ?4 AND self.saleOrder = null",
                    forecastRecap.getCompany(), forecastRecap.getBankDetails(), forecastRecap.getFromDate(),
                    forecastRecap.getToDate()).fetch();
        } else {
            opportunityList = Beans.get(OpportunityRepository.class).all().filter(
                    "self.company = ?1 AND self.expectedCloseDate BETWEEN ?2 AND ?3 AND self.saleOrder = null",
                    forecastRecap.getCompany(), forecastRecap.getFromDate(), forecastRecap.getToDate()).fetch();
        }
        for (Opportunity opportunity : opportunityList) {
            BigDecimal amountCompanyCurr = BigDecimal.ZERO;
            if (forecastRecap.getOpportunitiesTypeSelect() == ForecastRecapRepository.OPPORTUNITY_TYPE_BASE) {
                amountCompanyCurr = currencyService.getAmountCurrencyConverted(opportunity.getCurrency(),
                        opportunity.getCompany().getCurrency(),
                        opportunity.getAmount().multiply(opportunity.getProbability()).divide(new BigDecimal(100),
                                2, RoundingMode.HALF_UP),
                        generalService.getTodayDate()).setScale(2, RoundingMode.HALF_UP);
            } else if (forecastRecap
                    .getOpportunitiesTypeSelect() == ForecastRecapRepository.OPPORTUNITY_TYPE_BEST) {
                amountCompanyCurr = currencyService.getAmountCurrencyConverted(opportunity.getCurrency(),
                        opportunity.getCompany().getCurrency(),
                        new BigDecimal(opportunity.getBestCase()).multiply(opportunity.getProbability()).divide(
                                new BigDecimal(100), 2, RoundingMode.HALF_UP),
                        generalService.getTodayDate()).setScale(2, RoundingMode.HALF_UP);
            } else {
                amountCompanyCurr = currencyService.getAmountCurrencyConverted(opportunity.getCurrency(),
                        opportunity.getCompany().getCurrency(),
                        new BigDecimal(opportunity.getWorstCase()).multiply(opportunity.getProbability()).divide(
                                new BigDecimal(100), 2, RoundingMode.HALF_UP),
                        generalService.getTodayDate()).setScale(2, RoundingMode.HALF_UP);
            }
            if (opportunity.getSalesStageSelect() == 9) {
                if (mapExpected.containsKey(opportunity.getExpectedCloseDate())) {
                    mapExpected.put(opportunity.getExpectedCloseDate(),
                            mapExpected.get(opportunity.getExpectedCloseDate()).add(amountCompanyCurr));
                } else {
                    mapExpected.put(opportunity.getExpectedCloseDate(), amountCompanyCurr);
                }
            } else {
                if (mapConfirmed.containsKey(opportunity.getExpectedCloseDate())) {
                    mapConfirmed.put(opportunity.getExpectedCloseDate(),
                            mapConfirmed.get(opportunity.getExpectedCloseDate()).add(amountCompanyCurr));
                } else {
                    mapConfirmed.put(opportunity.getExpectedCloseDate(), amountCompanyCurr);
                }
            }
        }
    }

    public void populateWithInvoices(ForecastRecap forecastRecap) {
        List<Invoice> invoiceList = new ArrayList<Invoice>();
        if (forecastRecap.getBankDetails() != null) {
            invoiceList = Beans.get(InvoiceRepository.class).all().filter(
                    "self.company = ?1 AND self.companyBankDetails = ?2 AND self.statusSelect = 3 AND self.estimatedPaymentDate BETWEEN ?3 AND ?4 AND self.companyInTaxTotalRemaining != 0",
                    forecastRecap.getCompany(), forecastRecap.getBankDetails(), forecastRecap.getFromDate(),
                    forecastRecap.getToDate()).fetch();
        } else {
            invoiceList = Beans.get(InvoiceRepository.class).all().filter(
                    "self.company = ?1 AND self.statusSelect = 3 AND self.estimatedPaymentDate BETWEEN ?2 AND ?3 AND self.companyInTaxTotalRemaining != 0",
                    forecastRecap.getCompany(), forecastRecap.getFromDate(), forecastRecap.getToDate()).fetch();
        }
        for (Invoice invoice : invoiceList) {
            BigDecimal amountPaidExTax = invoice.getAmountPaid().multiply(invoice.getCompanyExTaxTotal())
                    .divide(invoice.getCompanyInTaxTotal(), 2, RoundingMode.HALF_UP);
            BigDecimal amount = invoice.getCompanyExTaxTotal().subtract(amountPaidExTax);
            if (invoice.getOperationTypeSelect() == 2 || invoice.getOperationTypeSelect() == 3) {
                forecastRecap.setCurrentBalance(forecastRecap.getCurrentBalance().add(amount));
                forecastRecap.addForecastRecapLineListItem(this.createForecastRecapLine(
                        invoice.getEstimatedPaymentDate(), 1, null, amount, forecastRecap.getCurrentBalance()));
            }
            if (invoice.getOperationTypeSelect() == 1 || invoice.getOperationTypeSelect() == 4) {
                forecastRecap.setCurrentBalance(forecastRecap.getCurrentBalance().subtract(amount));
                forecastRecap.addForecastRecapLineListItem(this.createForecastRecapLine(
                        invoice.getEstimatedPaymentDate(), 2, null, amount, forecastRecap.getCurrentBalance()));
            }
        }
    }

    public void getInvoices(ForecastRecap forecastRecap, Map<LocalDate, BigDecimal> mapExpected,
            Map<LocalDate, BigDecimal> mapConfirmed) {
        List<Invoice> invoiceList = new ArrayList<Invoice>();
        if (forecastRecap.getBankDetails() != null) {
            invoiceList = Beans.get(InvoiceRepository.class).all().filter(
                    "self.company = ?1 AND self.companyBankDetails = ?2 AND self.statusSelect = 3 AND self.estimatedPaymentDate BETWEEN ?3 AND ?4 AND self.companyInTaxTotalRemaining != 0",
                    forecastRecap.getCompany(), forecastRecap.getBankDetails(), forecastRecap.getFromDate(),
                    forecastRecap.getToDate()).fetch();
        } else {
            invoiceList = Beans.get(InvoiceRepository.class).all().filter(
                    "self.company = ?1 AND self.statusSelect = 3 AND self.estimatedPaymentDate BETWEEN ?2 AND ?3 AND self.companyInTaxTotalRemaining != 0",
                    forecastRecap.getCompany(), forecastRecap.getFromDate(), forecastRecap.getToDate()).fetch();
        }
        for (Invoice invoice : invoiceList) {
            BigDecimal amountPaidExTax = invoice.getAmountPaid().multiply(invoice.getCompanyExTaxTotal())
                    .divide(invoice.getCompanyInTaxTotal(), 2, RoundingMode.HALF_UP);
            BigDecimal amount = invoice.getCompanyExTaxTotal().subtract(amountPaidExTax);
            if (invoice.getOperationTypeSelect() == 2 || invoice.getOperationTypeSelect() == 3) {
                if (mapConfirmed.containsKey(invoice.getEstimatedPaymentDate())) {
                    mapConfirmed.put(invoice.getEstimatedPaymentDate(),
                            mapConfirmed.get(invoice.getEstimatedPaymentDate()).add(amount));
                } else {
                    mapConfirmed.put(invoice.getEstimatedPaymentDate(), amount);
                }
            }
        }
    }

    public void populateWithSalaries(ForecastRecap forecastRecap) {
        List<Employee> employeeList = new ArrayList<Employee>();
        if (forecastRecap.getBankDetails() != null) {
            employeeList = Beans.get(EmployeeRepository.class).all()
                    .filter("self.user.activeCompany = ?1 AND self.bankDetails = ?2", forecastRecap.getCompany(),
                            forecastRecap.getBankDetails())
                    .fetch();
        } else {
            employeeList = Beans.get(EmployeeRepository.class).all()
                    .filter("self.user.activeCompany = ?1", forecastRecap.getCompany()).fetch();
        }
        LocalDate itDate = new LocalDate(forecastRecap.getFromDate());
        while (!itDate.isAfter(forecastRecap.getToDate())) {
            if (itDate.isEqual(new LocalDate(itDate.getYear(), itDate.getMonthOfYear(),
                    itDate.dayOfMonth().getMaximumValue()))) {
                for (Employee employee : employeeList) {
                    forecastRecap.setCurrentBalance(forecastRecap.getCurrentBalance().subtract(employee
                            .getHourlyRate().multiply(employee.getWeeklyWorkHours().multiply(new BigDecimal(4)))));
                    forecastRecap.addForecastRecapLineListItem(this.createForecastRecapLine(itDate, 2, null,
                            employee.getHourlyRate()
                                    .multiply(employee.getWeeklyWorkHours().multiply(new BigDecimal(4))),
                            forecastRecap.getCurrentBalance()));
                }
                itDate = itDate.plusMonths(1);
            } else {
                itDate = new LocalDate(itDate.getYear(), itDate.getMonthOfYear(),
                        itDate.dayOfMonth().getMaximumValue());
            }
        }
    }

    public void populateWithTimetables(ForecastRecap forecastRecap) throws AxelorException {
        List<Timetable> timetableSaleOrderList = new ArrayList<Timetable>();
        List<Timetable> timetablePurchaseOrderList = new ArrayList<Timetable>();
        if (forecastRecap.getBankDetails() != null) {
            timetableSaleOrderList = Beans.get(TimetableRepository.class).all()
                    .filter("self.estimatedDate BETWEEN ?1 AND ?2 AND self.saleOrder.company = ?3 AND"
                            + " self.saleOrder.companyBankDetails = ?4 AND self.saleOrder.statusSelect = 3 AND self.amountToInvoice != 0",
                            forecastRecap.getFromDate(), forecastRecap.getToDate(), forecastRecap.getCompany(),
                            forecastRecap.getBankDetails())
                    .fetch();
            timetablePurchaseOrderList = Beans.get(TimetableRepository.class).all()
                    .filter("self.estimatedDate BETWEEN ?1 AND ?2 AND self.purchaseOrder.company = ?3 AND"
                            + " self.purchaseOrder.companyBankDetails = ?4 AND self.purchaseOrder.statusSelect = 3 AND self.amountToInvoice != 0",
                            forecastRecap.getFromDate(), forecastRecap.getToDate(), forecastRecap.getCompany(),
                            forecastRecap.getBankDetails())
                    .fetch();
        } else {
            timetableSaleOrderList = Beans.get(TimetableRepository.class).all()
                    .filter("self.estimatedDate BETWEEN ?1 AND ?2 AND self.saleOrder.company = ?3 AND"
                            + " self.saleOrder.statusSelect = 3 AND self.amountToInvoice != 0",
                            forecastRecap.getFromDate(), forecastRecap.getToDate(), forecastRecap.getCompany())
                    .fetch();
            timetablePurchaseOrderList = Beans.get(TimetableRepository.class).all()
                    .filter("self.estimatedDate BETWEEN ?1 AND ?2 AND self.purchaseOrder.company = ?3 AND"
                            + " self.purchaseOrder.statusSelect = 3 AND self.amountToInvoice != 0",
                            forecastRecap.getFromDate(), forecastRecap.getToDate(), forecastRecap.getCompany())
                    .fetch();
        }
        for (Timetable timetable : timetableSaleOrderList) {
            timetableService.updateTimetable(timetable.getSaleOrder());
            BigDecimal amountCompanyCurr = currencyService
                    .getAmountCurrencyConverted(timetable.getSaleOrder().getCurrency(),
                            timetable.getSaleOrder().getCompany().getCurrency(), timetable.getAmountToInvoice(),
                            generalService.getTodayDate())
                    .setScale(2, RoundingMode.HALF_UP);
            forecastRecap.setCurrentBalance(forecastRecap.getCurrentBalance().add(amountCompanyCurr));
            forecastRecap.addForecastRecapLineListItem(this.createForecastRecapLine(timetable.getEstimatedDate(), 1,
                    null, amountCompanyCurr, forecastRecap.getCurrentBalance()));
        }
        for (Timetable timetable : timetablePurchaseOrderList) {
            timetableService.updateTimetable(timetable.getPurchaseOrder());
            BigDecimal amountCompanyCurr = currencyService
                    .getAmountCurrencyConverted(timetable.getPurchaseOrder().getCurrency(),
                            timetable.getPurchaseOrder().getCompany().getCurrency(), timetable.getAmountToInvoice(),
                            generalService.getTodayDate())
                    .setScale(2, RoundingMode.HALF_UP);
            forecastRecap.setCurrentBalance(forecastRecap.getCurrentBalance().subtract(amountCompanyCurr));
            forecastRecap.addForecastRecapLineListItem(this.createForecastRecapLine(timetable.getEstimatedDate(), 2,
                    null, amountCompanyCurr, forecastRecap.getCurrentBalance()));
        }
    }

    public void populateWithTimetablesOrOrders(ForecastRecap forecastRecap) throws AxelorException {
        List<Timetable> timetableSaleOrderList = new ArrayList<Timetable>();
        List<SaleOrder> saleOrderList = new ArrayList<SaleOrder>();
        List<Timetable> timetablePurchaseOrderList = new ArrayList<Timetable>();
        List<PurchaseOrder> purchaseOrderList = new ArrayList<PurchaseOrder>();
        if (forecastRecap.getBankDetails() != null) {
            timetableSaleOrderList = Beans.get(TimetableRepository.class).all()
                    .filter("self.estimatedDate BETWEEN ?1 AND ?2 AND self.saleOrder.company = ?3 AND"
                            + " self.saleOrder.companyBankDetails = ?4 AND self.saleOrder.statusSelect = 3 AND self.amountToInvoice != 0",
                            forecastRecap.getFromDate(), forecastRecap.getToDate(), forecastRecap.getCompany(),
                            forecastRecap.getBankDetails())
                    .fetch();
            timetablePurchaseOrderList = Beans.get(TimetableRepository.class).all()
                    .filter("self.estimatedDate BETWEEN ?1 AND ?2 AND self.purchaseOrder.company = ?3 AND"
                            + " self.purchaseOrder.companyBankDetails = ?4 AND self.purchaseOrder.statusSelect = 3 AND self.amountToInvoice != 0",
                            forecastRecap.getFromDate(), forecastRecap.getToDate(), forecastRecap.getCompany(),
                            forecastRecap.getBankDetails())
                    .fetch();
        } else {
            timetableSaleOrderList = Beans.get(TimetableRepository.class).all()
                    .filter("self.estimatedDate BETWEEN ?1 AND ?2 AND self.saleOrder.company = ?3 AND"
                            + " self.saleOrder.statusSelect = 3 AND self.amountToInvoice != 0",
                            forecastRecap.getFromDate(), forecastRecap.getToDate(), forecastRecap.getCompany())
                    .fetch();
            timetablePurchaseOrderList = Beans.get(TimetableRepository.class).all()
                    .filter("self.estimatedDate BETWEEN ?1 AND ?2 AND self.purchaseOrder.company = ?3 AND"
                            + " self.purchaseOrder.statusSelect = 3 AND self.amountToInvoice != 0",
                            forecastRecap.getFromDate(), forecastRecap.getToDate(), forecastRecap.getCompany())
                    .fetch();
        }
        for (Timetable timetable : timetableSaleOrderList) {
            saleOrderList.add(timetable.getSaleOrder());
            timetableService.updateTimetable(timetable.getSaleOrder());
            BigDecimal amountCompanyCurr = currencyService
                    .getAmountCurrencyConverted(timetable.getSaleOrder().getCurrency(),
                            timetable.getSaleOrder().getCompany().getCurrency(), timetable.getAmountToInvoice(),
                            generalService.getTodayDate())
                    .setScale(2, RoundingMode.HALF_UP);
            forecastRecap.setCurrentBalance(forecastRecap.getCurrentBalance().add(amountCompanyCurr));
            forecastRecap.addForecastRecapLineListItem(this.createForecastRecapLine(timetable.getEstimatedDate(), 1,
                    null, amountCompanyCurr, forecastRecap.getCurrentBalance()));
        }
        for (Timetable timetable : timetablePurchaseOrderList) {
            purchaseOrderList.add(timetable.getPurchaseOrder());
            timetableService.updateTimetable(timetable.getPurchaseOrder());
            BigDecimal amountCompanyCurr = currencyService
                    .getAmountCurrencyConverted(timetable.getPurchaseOrder().getCurrency(),
                            timetable.getPurchaseOrder().getCompany().getCurrency(), timetable.getAmountToInvoice(),
                            generalService.getTodayDate())
                    .setScale(2, RoundingMode.HALF_UP);
            forecastRecap.setCurrentBalance(forecastRecap.getCurrentBalance().subtract(amountCompanyCurr));
            forecastRecap.addForecastRecapLineListItem(this.createForecastRecapLine(timetable.getEstimatedDate(), 2,
                    null, amountCompanyCurr, forecastRecap.getCurrentBalance()));
        }
        List<SaleOrder> saleOrderNoTimeTableList = new ArrayList<SaleOrder>();
        List<PurchaseOrder> purchaseOrderNoTimetableList = new ArrayList<PurchaseOrder>();
        if (forecastRecap.getBankDetails() != null) {
            saleOrderNoTimeTableList = Beans.get(SaleOrderRepository.class).all()
                    .filter("self.expectedRealisationDate BETWEEN ?1 AND ?2 AND self.company = ?3 AND"
                            + " self.companyBankDetails = ?4 AND (self.statusSelect = 2 OR self.statusSelect = 3)",
                            forecastRecap.getFromDate(), forecastRecap.getToDate(), forecastRecap.getCompany(),
                            forecastRecap.getBankDetails())
                    .fetch();
            purchaseOrderNoTimetableList = Beans.get(PurchaseOrderRepository.class).all()
                    .filter("self.expectedRealisationDate BETWEEN ?1 AND ?2 AND self.company = ?3 AND"
                            + " self.companyBankDetails = ?4 AND self.statusSelect = 3",
                            forecastRecap.getFromDate(), forecastRecap.getToDate(), forecastRecap.getCompany(),
                            forecastRecap.getBankDetails())
                    .fetch();
        } else {
            saleOrderNoTimeTableList = Beans.get(SaleOrderRepository.class).all()
                    .filter("self.expectedRealisationDate BETWEEN ?1 AND ?2 AND self.company = ?3 AND"
                            + " (self.statusSelect = 2 OR self.statusSelect = 3)", forecastRecap.getFromDate(),
                            forecastRecap.getToDate(), forecastRecap.getCompany())
                    .fetch();
            purchaseOrderNoTimetableList = Beans.get(PurchaseOrderRepository.class).all()
                    .filter("self.expectedRealisationDate BETWEEN ?1 AND ?2 AND self.company = ?3 AND"
                            + " self.statusSelect = 3", forecastRecap.getFromDate(), forecastRecap.getToDate(),
                            forecastRecap.getCompany())
                    .fetch();
        }
        for (SaleOrder saleOrder : saleOrderNoTimeTableList) {
            if (!saleOrderList.contains(saleOrder)) {
                BigDecimal amountCompanyCurr = saleOrder.getCompanyExTaxTotal()
                        .subtract(saleOrder.getAmountInvoiced());
                if (amountCompanyCurr.compareTo(BigDecimal.ZERO) == 0) {
                    forecastRecap.setCurrentBalance(forecastRecap.getCurrentBalance().add(amountCompanyCurr));
                    forecastRecap.addForecastRecapLineListItem(
                            this.createForecastRecapLine(saleOrder.getExpectedRealisationDate(), 1, null,
                                    amountCompanyCurr, forecastRecap.getCurrentBalance()));
                }
            }
        }
        for (PurchaseOrder purchaseOrder : purchaseOrderNoTimetableList) {
            if (!purchaseOrderList.contains(purchaseOrder)) {
                BigDecimal amountCompanyCurr = purchaseOrder.getCompanyExTaxTotal()
                        .subtract(purchaseOrder.getAmountInvoiced());
                if (amountCompanyCurr.compareTo(BigDecimal.ZERO) == 0) {
                    forecastRecap.setCurrentBalance(forecastRecap.getCurrentBalance().subtract(amountCompanyCurr));
                    forecastRecap.addForecastRecapLineListItem(
                            this.createForecastRecapLine(purchaseOrder.getExpectedRealisationDate(), 2, null,
                                    amountCompanyCurr, forecastRecap.getCurrentBalance()));
                }
            }
        }
    }

    public void getTimetablesOrOrders(ForecastRecap forecastRecap, Map<LocalDate, BigDecimal> mapExpected,
            Map<LocalDate, BigDecimal> mapConfirmed) throws AxelorException {
        List<Timetable> timetableSaleOrderList = new ArrayList<Timetable>();
        List<SaleOrder> saleOrderList = new ArrayList<SaleOrder>();
        if (forecastRecap.getBankDetails() != null) {
            timetableSaleOrderList = Beans.get(TimetableRepository.class).all()
                    .filter("self.estimatedDate BETWEEN ?1 AND ?2 AND self.saleOrder.company = ?3 AND"
                            + " self.saleOrder.companyBankDetails = ?4 AND (self.saleOrder.statusSelect = 2 OR self.saleOrder.statusSelect = 3) AND self.amountToInvoice != 0",
                            forecastRecap.getFromDate(), forecastRecap.getToDate(), forecastRecap.getCompany(),
                            forecastRecap.getBankDetails())
                    .fetch();
        } else {
            timetableSaleOrderList = Beans.get(TimetableRepository.class).all()
                    .filter("self.estimatedDate BETWEEN ?1 AND ?2 AND self.saleOrder.company = ?3 AND"
                            + " (self.saleOrder.statusSelect = 2 OR self.saleOrder.statusSelect = 3) AND self.amountToInvoice != 0",
                            forecastRecap.getFromDate(), forecastRecap.getToDate(), forecastRecap.getCompany())
                    .fetch();
        }
        for (Timetable timetable : timetableSaleOrderList) {
            saleOrderList.add(timetable.getSaleOrder());
            timetableService.updateTimetable(timetable.getSaleOrder());
            BigDecimal amountCompanyCurr = currencyService
                    .getAmountCurrencyConverted(timetable.getSaleOrder().getCurrency(),
                            timetable.getSaleOrder().getCompany().getCurrency(), timetable.getAmountToInvoice(),
                            generalService.getTodayDate())
                    .setScale(2, RoundingMode.HALF_UP);
            if (timetable.getSaleOrder().getStatusSelect() == 2) {
                if (mapExpected.containsKey(timetable.getEstimatedDate())) {
                    mapExpected.put(timetable.getEstimatedDate(),
                            mapExpected.get(timetable.getEstimatedDate()).add(amountCompanyCurr));
                } else {
                    mapExpected.put(timetable.getEstimatedDate(), amountCompanyCurr);
                }
            } else {
                if (mapConfirmed.containsKey(timetable.getEstimatedDate())) {
                    mapConfirmed.put(timetable.getEstimatedDate(),
                            mapConfirmed.get(timetable.getEstimatedDate()).add(amountCompanyCurr));
                } else {
                    mapConfirmed.put(timetable.getEstimatedDate(), amountCompanyCurr);
                }
            }
        }
        List<SaleOrder> saleOrderNoTimeTableList = new ArrayList<SaleOrder>();
        if (forecastRecap.getBankDetails() != null) {
            saleOrderNoTimeTableList = Beans.get(SaleOrderRepository.class).all()
                    .filter("self.expectedRealisationDate BETWEEN ?1 AND ?2 AND self.company = ?3 AND"
                            + " self.companyBankDetails = ?4 AND (self.statusSelect = 2 OR self.statusSelect = 3)",
                            forecastRecap.getFromDate(), forecastRecap.getToDate(), forecastRecap.getCompany(),
                            forecastRecap.getBankDetails())
                    .fetch();
        } else {
            saleOrderNoTimeTableList = Beans.get(SaleOrderRepository.class).all()
                    .filter("self.expectedRealisationDate BETWEEN ?1 AND ?2 AND self.company = ?3 AND"
                            + " (self.statusSelect = 2 OR self.statusSelect = 3)", forecastRecap.getFromDate(),
                            forecastRecap.getToDate(), forecastRecap.getCompany())
                    .fetch();
        }
        for (SaleOrder saleOrder : saleOrderNoTimeTableList) {
            if (!saleOrderList.contains(saleOrder)) {
                BigDecimal amountCompanyCurr = saleOrder.getCompanyExTaxTotal()
                        .subtract(saleOrder.getAmountInvoiced());
                if (amountCompanyCurr.compareTo(BigDecimal.ZERO) == 0) {
                    if (saleOrder.getStatusSelect() == 2) {
                        if (mapExpected.containsKey(saleOrder.getExpectedRealisationDate())) {
                            mapExpected.put(saleOrder.getExpectedRealisationDate(),
                                    mapExpected.get(saleOrder.getExpectedRealisationDate()).add(amountCompanyCurr));
                        } else {
                            mapExpected.put(saleOrder.getExpectedRealisationDate(), amountCompanyCurr);
                        }
                    } else {
                        if (mapConfirmed.containsKey(saleOrder.getExpectedRealisationDate())) {
                            mapConfirmed.put(saleOrder.getExpectedRealisationDate(), mapConfirmed
                                    .get(saleOrder.getExpectedRealisationDate()).add(amountCompanyCurr));
                        } else {
                            mapConfirmed.put(saleOrder.getExpectedRealisationDate(), amountCompanyCurr);
                        }
                    }
                }
            }
        }
    }

    @Transactional
    public void populateWithForecasts(ForecastRecap forecastRecap) {
        List<Forecast> forecastList = new ArrayList<Forecast>();
        if (forecastRecap.getBankDetails() != null) {
            forecastList = Beans.get(ForecastRepository.class).all()
                    .filter("self.estimatedDate BETWEEN ?1 AND ?2 AND self.company = ?3 AND"
                            + " self.bankDetails = ?4 AND (self.realizedSelect = 2 OR (self.realizedSelect = 3 AND self.estimatedDate <= ?5))",
                            forecastRecap.getFromDate(), forecastRecap.getToDate(), forecastRecap.getCompany(),
                            forecastRecap.getBankDetails(), generalService.getTodayDate())
                    .fetch();
        } else {
            forecastList = Beans.get(ForecastRepository.class).all()
                    .filter("self.estimatedDate BETWEEN ?1 AND ?2 AND self.company = ?3 AND"
                            + " (self.realizedSelect = 2 OR (self.realizedSelect = 3 AND self.estimatedDate <= ?4))",
                            forecastRecap.getFromDate(), forecastRecap.getToDate(), forecastRecap.getCompany(),
                            generalService.getTodayDate())
                    .fetch();
        }
        for (Forecast forecast : forecastList) {
            if (forecast.getTypeSelect() == 1) {
                forecastRecap.setCurrentBalance(forecastRecap.getCurrentBalance().add(forecast.getAmount()));
            } else {
                forecastRecap.setCurrentBalance(forecastRecap.getCurrentBalance().subtract(forecast.getAmount()));
            }
            forecastRecap.addForecastRecapLineListItem(
                    this.createForecastRecapLine(forecast.getEstimatedDate(), forecast.getTypeSelect(),
                            forecast.getForecastReason(), forecast.getAmount(), forecastRecap.getCurrentBalance()));
            forecast.setRealizedSelect(ForecastRepository.REALISED_SELECT_YES);
            forecastRepo.save(forecast);
        }
    }

    public void populateWithForecastsNoSave(ForecastRecap forecastRecap) {
        List<Forecast> forecastList = new ArrayList<Forecast>();
        if (forecastRecap.getBankDetails() != null) {
            forecastList = Beans.get(ForecastRepository.class).all()
                    .filter("self.estimatedDate BETWEEN ?1 AND ?2 AND self.company = ?3 AND"
                            + " self.bankDetails = ?4 AND (self.realizedSelect = 2 OR (self.realizedSelect = 3 AND self.estimatedDate <= ?5))",
                            forecastRecap.getFromDate(), forecastRecap.getToDate(), forecastRecap.getCompany(),
                            forecastRecap.getBankDetails(), generalService.getTodayDate())
                    .fetch();
        } else {
            forecastList = Beans.get(ForecastRepository.class).all()
                    .filter("self.estimatedDate BETWEEN ?1 AND ?2 AND self.company = ?3 AND"
                            + " (self.realizedSelect = 2 OR (self.realizedSelect = 3 AND self.estimatedDate <= ?4))",
                            forecastRecap.getFromDate(), forecastRecap.getToDate(), forecastRecap.getCompany(),
                            generalService.getTodayDate())
                    .fetch();
        }
        for (Forecast forecast : forecastList) {
            if (forecast.getTypeSelect() == 1) {
                forecastRecap.setCurrentBalance(forecastRecap.getCurrentBalance().add(forecast.getAmount()));
            } else {
                forecastRecap.setCurrentBalance(forecastRecap.getCurrentBalance().subtract(forecast.getAmount()));
            }
            forecastRecap.addForecastRecapLineListItem(
                    this.createForecastRecapLine(forecast.getEstimatedDate(), forecast.getTypeSelect(),
                            forecast.getForecastReason(), forecast.getAmount(), forecastRecap.getCurrentBalance()));
        }
    }

    public void getForecasts(ForecastRecap forecastRecap, Map<LocalDate, BigDecimal> mapExpected,
            Map<LocalDate, BigDecimal> mapConfirmed) {
        List<Forecast> forecastList = new ArrayList<Forecast>();
        if (forecastRecap.getBankDetails() != null) {
            forecastList = Beans.get(ForecastRepository.class).all()
                    .filter("self.estimatedDate BETWEEN ?1 AND ?2 AND self.company = ?3 AND"
                            + " self.bankDetails = ?4 AND (self.realizedSelect = 2 OR self.realizedSelect = 3)",
                            forecastRecap.getFromDate(), forecastRecap.getToDate(), forecastRecap.getCompany(),
                            forecastRecap.getBankDetails())
                    .fetch();
        } else {
            forecastList = Beans.get(ForecastRepository.class).all()
                    .filter("self.estimatedDate BETWEEN ?1 AND ?2 AND self.company = ?3 AND"
                            + " (self.realizedSelect = 2 OR self.realizedSelect = 3)", forecastRecap.getFromDate(),
                            forecastRecap.getToDate(), forecastRecap.getCompany())
                    .fetch();
        }
        for (Forecast forecast : forecastList) {
            if (forecast.getTypeSelect() == 1) {
                if (forecast.getRealizedSelect() == 2) {
                    if (mapExpected.containsKey(forecast.getEstimatedDate())) {
                        mapExpected.put(forecast.getEstimatedDate(),
                                mapExpected.get(forecast.getEstimatedDate()).add(forecast.getAmount()));
                    } else {
                        mapExpected.put(forecast.getEstimatedDate(), forecast.getAmount());
                    }
                } else {
                    if (mapConfirmed.containsKey(forecast.getEstimatedDate())) {
                        mapConfirmed.put(forecast.getEstimatedDate(),
                                mapConfirmed.get(forecast.getEstimatedDate()).add(forecast.getAmount()));
                    } else {
                        mapConfirmed.put(forecast.getEstimatedDate(), forecast.getAmount());
                    }
                }
            }
        }
    }

    public void populateWithExpenses(ForecastRecap forecastRecap) {
        List<Expense> expenseList = new ArrayList<Expense>();
        if (forecastRecap.getBankDetails() != null) {
            expenseList = Beans.get(ExpenseRepository.class).all()
                    .filter("self.validationDate BETWEEN ?1 AND ?2 AND self.company = ?3 AND"
                            + " self.bankDetails = ?4 AND self.statusSelect = 3", forecastRecap.getFromDate(),
                            forecastRecap.getToDate(), forecastRecap.getCompany(), forecastRecap.getBankDetails())
                    .fetch();
        } else {
            expenseList = Beans.get(ExpenseRepository.class).all()
                    .filter("self.validationDate BETWEEN ?1 AND ?2 AND self.company = ?3 AND"
                            + " self.statusSelect = 3", forecastRecap.getFromDate(), forecastRecap.getToDate(),
                            forecastRecap.getCompany())
                    .fetch();
        }
        for (Expense expense : expenseList) {
            forecastRecap.setCurrentBalance(forecastRecap.getCurrentBalance().subtract(expense.getExTaxTotal()));
            forecastRecap.addForecastRecapLineListItem(this.createForecastRecapLine(expense.getValidationDate(), 2,
                    null, expense.getExTaxTotal(), forecastRecap.getCurrentBalance()));
        }
    }

    public ForecastRecapLine createForecastRecapLine(LocalDate date, int type, ForecastReason reason,
            BigDecimal amount, BigDecimal balance) {
        ForecastRecapLine forecastRecapLine = new ForecastRecapLine();
        forecastRecapLine.setEstimatedDate(date);
        forecastRecapLine.setTypeSelect(type);
        forecastRecapLine.setForecastReason(reason);
        forecastRecapLine.setAmount(amount);
        forecastRecapLine.setBalance(balance);
        return forecastRecapLine;
    }

    public String getURLForecastRecapPDF(ForecastRecap forecastRecap) throws AxelorException {
        String language = "";
        try {
            language = forecastRecap.getCompany().getPrintingSettings().getLanguageSelect() != null
                    ? forecastRecap.getCompany().getPrintingSettings().getLanguageSelect()
                    : "en";
        } catch (NullPointerException e) {
            language = "en";
        }
        language = language.equals("") ? "en" : language;

        String title = I18n.get("ForecastRecap");
        title += forecastRecap.getId();

        return reportFactory.createReport(IReport.FORECAST_RECAP, title + "-${date}")
                .addParam("ForecastRecapId", forecastRecap.getId().toString()).addParam("Locale", language)
                .generate().getFileLink();

    }
}