/**
* Axelor Business Solutions
*
* Copyright (C) 2016 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.hr.service.expense;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.joda.time.LocalDate;
import com.axelor.apps.account.db.Account;
import com.axelor.apps.account.db.AccountConfig;
import com.axelor.apps.account.db.AccountManagement;
import com.axelor.apps.account.db.AnalyticAccount;
import com.axelor.apps.account.db.AnalyticDistributionLine;
import com.axelor.apps.account.db.Invoice;
import com.axelor.apps.account.db.InvoiceLine;
import com.axelor.apps.account.db.Move;
import com.axelor.apps.account.db.MoveLine;
import com.axelor.apps.account.db.repo.AnalyticDistributionLineRepository;
import com.axelor.apps.account.db.repo.MoveRepository;
import com.axelor.apps.account.exception.IExceptionMessage;
import com.axelor.apps.account.service.AccountManagementServiceAccountImpl;
import com.axelor.apps.account.service.AnalyticDistributionLineService;
import com.axelor.apps.account.service.invoice.generator.InvoiceLineGenerator;
import com.axelor.apps.account.service.move.MoveLineService;
import com.axelor.apps.account.service.move.MoveService;
import com.axelor.apps.base.db.IPriceListLine;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.db.repo.GeneralRepository;
import com.axelor.apps.base.db.repo.ProductRepository;
import com.axelor.apps.base.service.PeriodService;
import com.axelor.apps.base.service.administration.GeneralService;
import com.axelor.apps.hr.db.Expense;
import com.axelor.apps.hr.db.ExpenseLine;
import com.axelor.apps.hr.db.IExpense;
import com.axelor.apps.hr.db.repo.ExpenseRepository;
import com.axelor.apps.hr.db.repo.TimesheetRepository;
import com.axelor.apps.hr.service.config.AccountConfigHRService;
import com.axelor.apps.project.db.ProjectTask;
import com.axelor.apps.project.db.repo.ProjectTaskRepository;
import com.axelor.auth.AuthUtils;
import com.axelor.auth.db.User;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.IException;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.rpc.ActionRequest;
import com.axelor.rpc.ActionResponse;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
public class ExpenseService {
protected MoveService moveService;
protected ExpenseRepository expenseRepository;
protected MoveLineService moveLineService;
protected AccountManagementServiceAccountImpl accountManagementService;
protected GeneralService generalService;
protected AccountConfigHRService accountConfigService;
protected AnalyticDistributionLineService analyticDistributionLineService;
@Inject
public ExpenseService(MoveService moveService, ExpenseRepository expenseRepository, MoveLineService moveLineService,
AccountManagementServiceAccountImpl accountManagementService, GeneralService generalService,
AccountConfigHRService accountConfigService, AnalyticDistributionLineService analyticDistributionLineService) {
this.moveService = moveService;
this.expenseRepository = expenseRepository;
this.moveLineService = moveLineService;
this.accountManagementService = accountManagementService;
this.generalService = generalService;
this.accountConfigService = accountConfigService;
this.analyticDistributionLineService = analyticDistributionLineService;
}
public ExpenseLine createAnalyticDistributionWithTemplate(ExpenseLine expenseLine) throws AxelorException{
List<AnalyticDistributionLine> analyticDistributionLineList = null;
analyticDistributionLineList = analyticDistributionLineService.generateLinesWithTemplate(expenseLine.getAnalyticDistributionTemplate(), expenseLine.getUntaxedAmount());
if(analyticDistributionLineList != null){
for (AnalyticDistributionLine analyticDistributionLine : analyticDistributionLineList) {
analyticDistributionLine.setExpenseLine(expenseLine);
}
}
expenseLine.setAnalyticDistributionLineList(analyticDistributionLineList);
return expenseLine;
}
public ExpenseLine computeAnalyticDistribution(ExpenseLine expenseLine) throws AxelorException{
List<AnalyticDistributionLine> analyticDistributionLineList = expenseLine.getAnalyticDistributionLineList();
if((analyticDistributionLineList == null || analyticDistributionLineList.isEmpty()) && generalService.getGeneral().getAnalyticDistributionTypeSelect() != GeneralRepository.DISTRIBUTION_TYPE_FREE){
analyticDistributionLineList = analyticDistributionLineService.generateLines(expenseLine.getUser().getPartner(), expenseLine.getExpenseProduct(), expenseLine.getExpense().getCompany(), expenseLine.getUntaxedAmount());
if(analyticDistributionLineList != null){
for (AnalyticDistributionLine analyticDistributionLine : analyticDistributionLineList) {
analyticDistributionLine.setExpenseLine(expenseLine);
analyticDistributionLine.setAmount(
analyticDistributionLine.getPercentage().multiply(analyticDistributionLine.getExpenseLine().getUntaxedAmount()
.divide(new BigDecimal(100),2,RoundingMode.HALF_UP)));
analyticDistributionLine.setDate(generalService.getTodayDate());
}
expenseLine.setAnalyticDistributionLineList(analyticDistributionLineList);
}
}
if(analyticDistributionLineList != null && generalService.getGeneral().getAnalyticDistributionTypeSelect() != GeneralRepository.DISTRIBUTION_TYPE_FREE){
for (AnalyticDistributionLine analyticDistributionLine : analyticDistributionLineList) {
analyticDistributionLine.setExpenseLine(expenseLine);
analyticDistributionLine.setAmount(analyticDistributionLineService.computeAmount(analyticDistributionLine));
analyticDistributionLine.setDate(generalService.getTodayDate());
}
}
return expenseLine;
}
public Expense compute (Expense expense){
BigDecimal exTaxTotal = BigDecimal.ZERO;
BigDecimal taxTotal = BigDecimal.ZERO;
BigDecimal inTaxTotal = BigDecimal.ZERO;
List<ExpenseLine> expenseLineList = expense.getExpenseLineList();
for (ExpenseLine expenseLine : expenseLineList) {
exTaxTotal = exTaxTotal.add(expenseLine.getUntaxedAmount());
taxTotal = taxTotal.add(expenseLine.getTotalTax());
inTaxTotal = inTaxTotal.add(expenseLine.getTotalAmount());
}
expense.setExTaxTotal(exTaxTotal);
expense.setTaxTotal(taxTotal);
expense.setInTaxTotal(inTaxTotal);
return expense;
}
@Transactional(rollbackOn = {AxelorException.class, Exception.class})
public Move ventilate(Expense expense) throws AxelorException{
LocalDate moveDate = generalService.getTodayDate();
if(expense.getMoveDate()!=null){
moveDate = expense.getMoveDate();
}
Account account = null;
AccountConfig accountConfig= accountConfigService.getAccountConfig(expense.getCompany());
if(expense.getUser().getPartner() == null){
throw new AxelorException(String.format(I18n.get(IExceptionMessage.USER_PARTNER),expense.getUser().getName()), IException.CONFIGURATION_ERROR);
}
Move move = moveService.getMoveCreateService().createMove(accountConfigService.getExpenseJournal(accountConfig), accountConfig.getCompany(), null, expense.getUser().getPartner(), moveDate, expense.getUser().getPartner().getPaymentMode());
List<MoveLine> moveLines = new ArrayList<MoveLine>();
AccountManagement accountManagement = null;
Set<AnalyticAccount> analyticAccounts = new HashSet<AnalyticAccount>();
BigDecimal exTaxTotal = null;
int moveLineId = 1;
int expenseLineId = 1;
moveLines.add( moveLineService.createMoveLine(move, expense.getUser().getPartner(), accountConfigService.getExpenseEmployeeAccount(accountConfig), expense.getInTaxTotal(), false, moveDate, moveDate, moveLineId++, ""));
for(ExpenseLine expenseLine : expense.getExpenseLineList()){
analyticAccounts.clear();
Product product = expenseLine.getExpenseProduct();
accountManagement = accountManagementService.getAccountManagement(product, expense.getCompany());
account = accountManagementService.getProductAccount(accountManagement, true);
if(account == null) {
throw new AxelorException(String.format(I18n.get(IExceptionMessage.MOVE_LINE_4),
expenseLineId,expense.getCompany().getName()), IException.CONFIGURATION_ERROR);
}
exTaxTotal = expenseLine.getUntaxedAmount();
MoveLine moveLine = moveLineService.createMoveLine(move, expense.getUser().getPartner(), account, exTaxTotal, true, moveDate, moveDate, moveLineId++, "");
for (AnalyticDistributionLine analyticDistributionLineIt : expenseLine.getAnalyticDistributionLineList()) {
AnalyticDistributionLine analyticDistributionLine = Beans.get(AnalyticDistributionLineRepository.class).copy(analyticDistributionLineIt, false);
analyticDistributionLine.setExpenseLine(null);
moveLine.addAnalyticDistributionLineListItem(analyticDistributionLine);
}
moveLines.add(moveLine);
expenseLineId++;
}
moveLineService.consolidateMoveLines(moveLines);
account = accountConfigService.getExpenseTaxAccount(accountConfig);
BigDecimal taxTotal = BigDecimal.ZERO;
for(ExpenseLine expenseLine : expense.getExpenseLineList()){
exTaxTotal = expenseLine.getTotalTax();
taxTotal = taxTotal.add(exTaxTotal);
}
MoveLine moveLine = moveLineService.createMoveLine(move, expense.getUser().getPartner(), account, taxTotal, true, moveDate, moveDate, moveLineId++, "");
moveLines.add(moveLine);
move.getMoveLineList().addAll(moveLines);
moveService.getMoveValidateService().validateMove(move);
expense.setMove(move);
expense.setVentilated(true);
expenseRepository.save(expense);
return move;
}
@Transactional(rollbackOn = {AxelorException.class, Exception.class})
public void cancel (Expense expense) throws AxelorException{
Move move = expense.getMove();
if(move == null)
{
expense.setStatusSelect(IExpense.STATUS_CANCELED);
expenseRepository.save(expense);
return;
}
Beans.get(PeriodService.class).testOpenPeriod(move.getPeriod());
try{
Beans.get(MoveRepository.class).remove(move);
expense.setMove(null);
expense.setVentilated(false);
expense.setStatusSelect(IExpense.STATUS_CANCELED);
}
catch(Exception e){
throw new AxelorException(String.format(I18n.get(IExceptionMessage.EXPENSE_CANCEL_MOVE)), IException.CONFIGURATION_ERROR);
}
expenseRepository.save(expense);
}
public List<InvoiceLine> createInvoiceLines(Invoice invoice, List<ExpenseLine> expenseLineList, int priority) throws AxelorException {
List<InvoiceLine> invoiceLineList = new ArrayList<InvoiceLine>();
int count = 0;
for(ExpenseLine expenseLine : expenseLineList) {
invoiceLineList.addAll(this.createInvoiceLine(invoice, expenseLine, priority*100+count));
count++;
expenseLine.setInvoiced(true);
}
return invoiceLineList;
}
public List<InvoiceLine> createInvoiceLine(Invoice invoice, ExpenseLine expenseLine, int priority) throws AxelorException {
Product product = expenseLine.getExpenseProduct();
InvoiceLineGenerator invoiceLineGenerator = null;
Integer atiChoice = invoice.getCompany().getAccountConfig().getInvoiceInAtiSelect();
if(atiChoice == 1 || atiChoice == 3){
invoiceLineGenerator = new InvoiceLineGenerator(invoice, product, product.getName(), expenseLine.getUntaxedAmount(),
expenseLine.getUntaxedAmount(),expenseLine.getComments(),BigDecimal.ONE,product.getUnit(), null,priority,BigDecimal.ZERO,IPriceListLine.AMOUNT_TYPE_NONE,
expenseLine.getUntaxedAmount(), expenseLine.getTotalAmount(),false) {
@Override
public List<InvoiceLine> creates() throws AxelorException {
InvoiceLine invoiceLine = this.createInvoiceLine();
List<InvoiceLine> invoiceLines = new ArrayList<InvoiceLine>();
invoiceLines.add(invoiceLine);
return invoiceLines;
}
};
}
else{
invoiceLineGenerator = new InvoiceLineGenerator(invoice, product, product.getName(), expenseLine.getTotalAmount(),
expenseLine.getTotalAmount(),expenseLine.getComments(),BigDecimal.ONE,product.getUnit(), null,priority,BigDecimal.ZERO,IPriceListLine.AMOUNT_TYPE_NONE,
expenseLine.getUntaxedAmount(), expenseLine.getTotalAmount(),false) {
@Override
public List<InvoiceLine> creates() throws AxelorException {
InvoiceLine invoiceLine = this.createInvoiceLine();
List<InvoiceLine> invoiceLines = new ArrayList<InvoiceLine>();
invoiceLines.add(invoiceLine);
return invoiceLines;
}
};
}
return invoiceLineGenerator.creates();
}
public void getExpensesTypes(ActionRequest request, ActionResponse response){
List<Map<String,String>> dataList = new ArrayList<Map<String,String>>();
try{
List<Product> productList = Beans.get(ProductRepository.class).all().filter("self.expense = true").fetch();
for (Product product : productList) {
Map<String, String> map = new HashMap<String,String>();
map.put("name", product.getName());
map.put("id", product.getId().toString());
dataList.add(map);
}
response.setData(dataList);
}
catch(Exception e){
response.setStatus(-1);
response.setError(e.getMessage());
}
}
@Transactional
public void insertExpenseLine(ActionRequest request, ActionResponse response){
User user = AuthUtils.getUser();
ProjectTask projectTask = Beans.get(ProjectTaskRepository.class).find(new Long(request.getData().get("project").toString()));
Product product = Beans.get(ProductRepository.class).find(new Long(request.getData().get("expenseProduct").toString()));
if(user != null){
Expense expense = Beans.get(ExpenseRepository.class).all().filter("self.statusSelect = 1 AND self.user.id = ?1", user.getId()).order("-id").fetchOne();
if(expense == null){
expense = new Expense();
expense.setUser(user);
expense.setCompany(user.getActiveCompany());
expense.setStatusSelect(TimesheetRepository.STATUS_DRAFT);
}
ExpenseLine expenseLine = new ExpenseLine();
expenseLine.setExpenseDate(new LocalDate(request.getData().get("date").toString()));
expenseLine.setComments(request.getData().get("comments").toString());
expenseLine.setExpenseProduct(product);
expenseLine.setToInvoice(new Boolean(request.getData().get("toInvoice").toString()));
expenseLine.setProjectTask(projectTask);
expenseLine.setUser(user);
expenseLine.setUntaxedAmount(new BigDecimal(request.getData().get("amountWithoutVat").toString()));
expenseLine.setTotalTax(new BigDecimal(request.getData().get("vatAmount").toString()));
expenseLine.setTotalAmount(expenseLine.getUntaxedAmount().add(expenseLine.getTotalTax()));
expenseLine.setJustification((byte[])request.getData().get("justification"));
expense.addExpenseLineListItem(expenseLine);
Beans.get(ExpenseRepository.class).save(expense);
}
}
}