/**
* 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.account.service.debtrecovery;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Query;
import org.joda.time.LocalDate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.axelor.apps.account.db.Account;
import com.axelor.apps.account.db.AccountConfig;
import com.axelor.apps.account.db.Invoice;
import com.axelor.apps.account.db.Move;
import com.axelor.apps.account.db.MoveLine;
import com.axelor.apps.account.db.Reconcile;
import com.axelor.apps.account.db.repo.MoveLineRepository;
import com.axelor.apps.account.db.repo.MoveRepository;
import com.axelor.apps.account.service.ReconcileService;
import com.axelor.apps.account.service.config.AccountConfigService;
import com.axelor.apps.account.service.move.MoveLineService;
import com.axelor.apps.account.service.move.MoveService;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Partner;
import com.axelor.apps.base.service.administration.GeneralService;
import com.axelor.db.JPA;
import com.axelor.exception.AxelorException;
import com.axelor.inject.Beans;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
public class DoubtfulCustomerService {
private final Logger log = LoggerFactory.getLogger( getClass() );
protected MoveService moveService;
protected MoveRepository moveRepo;
protected MoveLineService moveLineService;
protected MoveLineRepository moveLineRepo;
protected ReconcileService reconcileService;
protected AccountConfigService accountConfigService;
protected LocalDate today;
@Inject
public DoubtfulCustomerService(MoveService moveService, MoveRepository moveRepo, MoveLineService moveLineService, MoveLineRepository moveLineRepo,
ReconcileService reconcileService, AccountConfigService accountConfigService) {
this.moveService = moveService;
this.moveRepo = moveRepo;
this.moveLineService = moveLineService;
this.moveLineRepo = moveLineRepo;
this.reconcileService = reconcileService;
this.accountConfigService = accountConfigService;
this.today = Beans.get(GeneralService.class).getTodayDate();
}
/**
* Procédure permettant de vérifier le remplissage des champs dans la société, nécessaire au traitement du passage en client douteux
* @param company
* Une société
* @throws AxelorException
*/
public void testCompanyField(Company company) throws AxelorException {
AccountConfig accountConfig = accountConfigService.getAccountConfig(company);
accountConfigService.getDoubtfulCustomerAccount(accountConfig);
accountConfigService.getMiscOperationJournal(accountConfig);
accountConfigService.getSixMonthDebtPassReason(accountConfig);
accountConfigService.getThreeMonthDebtPassReason(accountConfig);
}
/**
*
* Procédure permettant de créer les écritures de passage en client douteux pour chaque écriture de facture
* @param moveLineList
* Une liste d'écritures de facture
* @param doubtfulCustomerAccount
* Un compte client douteux
* @param debtPassReason
* Un motif de passage en client douteux
* @throws AxelorException
*/
public void createDoubtFulCustomerMove(List<Move> moveList, Account doubtfulCustomerAccount, String debtPassReason) throws AxelorException {
for(Move move : moveList) {
this.createDoubtFulCustomerMove(move, doubtfulCustomerAccount, debtPassReason);
}
}
/**
*
* Procédure permettant de créer les écritures de passage en client douteux pour chaque écriture de facture
* @param moveLineList
* Une liste d'écritures de facture
* @param doubtfulCustomerAccount
* Un compte client douteux
* @param debtPassReason
* Un motif de passage en client douteux
* @throws AxelorException
*/
@Transactional(rollbackOn = {AxelorException.class, Exception.class})
public void createDoubtFulCustomerMove(Move move, Account doubtfulCustomerAccount, String debtPassReason) throws AxelorException {
log.debug("Ecriture concernée : {} ",move.getReference());
BigDecimal totalAmountRemaining = BigDecimal.ZERO;
Company company = move.getCompany();
Partner partner = move.getPartner();
Move newMove = moveService.getMoveCreateService().createMove(company.getAccountConfig().getMiscOperationJournal(), company, move.getInvoice(), partner, move.getPaymentMode());
int ref = 1;
List<Reconcile> reconcileList = new ArrayList<Reconcile>();
List<MoveLine> moveLineList = move.getMoveLineList();
for(MoveLine moveLine : moveLineList) {
if(moveLine.getAccount().getReconcileOk()
&& moveLine.getAmountRemaining().compareTo(BigDecimal.ZERO) > 0
&& moveLine.getAccount() != doubtfulCustomerAccount
&& moveLine.getDebit().compareTo(BigDecimal.ZERO) > 0) {
BigDecimal amountRemaining = moveLine.getAmountRemaining();
// Ecriture au crédit sur le 411
MoveLine creditMoveLine = moveLineService.createMoveLine(newMove , moveLine.getPartner(), moveLine.getAccount(), amountRemaining, false, today, ref, null);
newMove.getMoveLineList().add(creditMoveLine);
Reconcile reconcile = reconcileService.createReconcile(moveLine, creditMoveLine, amountRemaining, false);
reconcileList.add(reconcile);
totalAmountRemaining = totalAmountRemaining.add(amountRemaining);
ref++;
}
}
// Ecriture au débit sur le 416 (client douteux)
MoveLine debitMoveLine = moveLineService.createMoveLine(newMove , newMove.getPartner(), doubtfulCustomerAccount, totalAmountRemaining, true, today, ref, null);
newMove.getMoveLineList().add(debitMoveLine);
debitMoveLine.setPassageReason(debtPassReason);
moveService.getMoveValidateService().validateMove(newMove);
moveRepo.save(newMove);
for(Reconcile reconcile : reconcileList) {
reconcileService.confirmReconcile(reconcile);
}
this.invoiceProcess(newMove, doubtfulCustomerAccount, debtPassReason);
}
public void createDoubtFulCustomerRejectMove(List<MoveLine> moveLineList, Account doubtfulCustomerAccount, String debtPassReason) throws AxelorException {
for(MoveLine moveLine : moveLineList) {
this.createDoubtFulCustomerRejectMove(moveLine, doubtfulCustomerAccount, debtPassReason);
}
}
/**
* Procédure permettant de créer les écritures de passage en client douteux pour chaque ligne d'écriture de rejet de facture
* @param moveLineList
* Une liste de lignes d'écritures de rejet de facture
* @param doubtfulCustomerAccount
* Un compte client douteux
* @param debtPassReason
* Un motif de passage en client douteux
* @throws AxelorException
*/
@Transactional(rollbackOn = {AxelorException.class, Exception.class})
public void createDoubtFulCustomerRejectMove(MoveLine moveLine, Account doubtfulCustomerAccount, String debtPassReason) throws AxelorException {
log.debug("Ecriture concernée : {} ",moveLine.getName());
Company company = moveLine.getMove().getCompany();
Partner partner = moveLine.getPartner();
Move newMove = moveService.getMoveCreateService().createMove(company.getAccountConfig().getMiscOperationJournal(), company, null, partner, moveLine.getMove().getPaymentMode());
List<Reconcile> reconcileList = new ArrayList<Reconcile>();
BigDecimal amountRemaining = moveLine.getAmountRemaining();
// Ecriture au crédit sur le 411
MoveLine creditMoveLine = moveLineService.createMoveLine(newMove , partner, moveLine.getAccount(), amountRemaining, false, today, 1, null);
newMove.addMoveLineListItem(creditMoveLine);
Reconcile reconcile = reconcileService.createReconcile(moveLine, creditMoveLine, amountRemaining, false);
reconcileList.add(reconcile);
reconcileService.confirmReconcile(reconcile);
// Ecriture au débit sur le 416 (client douteux)
MoveLine debitMoveLine = moveLineService.createMoveLine(newMove , newMove.getPartner(), doubtfulCustomerAccount, amountRemaining, true, today, 2, null);
newMove.getMoveLineList().add(debitMoveLine);
debitMoveLine.setInvoiceReject(moveLine.getInvoiceReject());
debitMoveLine.setPassageReason(debtPassReason);
moveService.getMoveValidateService().validateMove(newMove);
moveRepo.save(newMove);
this.invoiceRejectProcess(debitMoveLine, doubtfulCustomerAccount, debtPassReason);
}
/**
* Procédure permettant de mettre à jour le motif de passage en client douteux, et créer l'évènement lié.
* @param moveList
* Une liste d'éciture de facture
* @param doubtfulCustomerAccount
* Un compte client douteux
* @param debtPassReason
* Un motif de passage en client douteux
*/
public void updateDoubtfulCustomerMove(List<Move> moveList, Account doubtfulCustomerAccount, String debtPassReason) {
for(Move move : moveList) {
for(MoveLine moveLine : move.getMoveLineList()) {
if(moveLine.getAccount().equals(doubtfulCustomerAccount) && moveLine.getDebit().compareTo(BigDecimal.ZERO) > 0) {
moveLine.setPassageReason(debtPassReason);
moveLineRepo.save(moveLine);
break;
}
}
}
}
/**
* Procédure permettant de mettre à jour les champs de la facture avec la nouvelle écriture de débit sur le compte 416
* @param move
* La nouvelle écriture de débit sur le compte 416
* @param doubtfulCustomerAccount
* Un compte client douteux
* @param debtPassReason
* Un motif de passage en client douteux
* @throws AxelorException
*/
public Invoice invoiceProcess(Move move, Account doubtfulCustomerAccount, String debtPassReason) throws AxelorException {
Invoice invoice = move.getInvoice();
if(invoice != null) {
invoice.setOldMove(invoice.getMove());
invoice.setMove(move);
invoice.setPartnerAccount(doubtfulCustomerAccount);
invoice.setDoubtfulCustomerOk(true);
// Recalcule du restant à payer de la facture
invoice.setCompanyInTaxTotalRemaining(moveService.getMoveToolService().getInTaxTotalRemaining(invoice));
}
return invoice;
}
/**
* Procédure permettant de mettre à jour les champs d'une facture rejetée avec la nouvelle écriture de débit sur le compte 416
* @param move
* La nouvelle ligne d'écriture de débit sur le compte 416
* @param doubtfulCustomerAccount
* Un compte client douteux
* @param debtPassReason
* Un motif de passage en client douteux
*/
public Invoice invoiceRejectProcess(MoveLine moveLine, Account doubtfulCustomerAccount, String debtPassReason) {
Invoice invoice = moveLine.getInvoiceReject();
invoice.setRejectMoveLine(moveLine);
// invoice.setPartnerAccount(doubtfulCustomerAccount);
invoice.setDoubtfulCustomerOk(true);
return invoice;
}
/**
* Fonction permettant de récupérer les écritures de facture à transférer sur le compte client douteux
* @param rule
* Le règle à appliquer :
* <ul>
* <li>0 = Créance de + 6 mois</li>
* <li>1 = Créance de + 3 mois</li>
* </ul>
* @param doubtfulCustomerAccount
* Le compte client douteux
* @param company
* La société
* @return
* Les écritures de facture à transférer sur le compte client douteux
*/
public List<Move> getMove(int rule, Account doubtfulCustomerAccount, Company company) {
LocalDate date = null;
switch (rule) {
//Créance de + 6 mois
case 0 :
date = this.today.minusMonths(company.getAccountConfig().getSixMonthDebtMonthNumber());
break;
//Créance de + 3 mois
case 1 :
date = this.today.minusMonths(company.getAccountConfig().getThreeMonthDebtMontsNumber());
break;
default:
break;
}
log.debug("Date de créance prise en compte : {} ",date);
String request = "SELECT DISTINCT m FROM MoveLine ml, Move m WHERE ml.move = m AND ml.company.id = "+ company.getId() +" AND ml.account.reconcileOk = 'true' " +
"AND ml.invoice IS NOT NULL AND ml.amountRemaining > 0.00 AND ml.debit > 0.00 AND ml.dueDate < '"+ date.toString() +
"' AND ml.account.id != "+doubtfulCustomerAccount.getId();
log.debug("Requete : {} ",request);
Query query = JPA.em().createQuery(request);
@SuppressWarnings("unchecked")
List<Move> moveList = query.getResultList();
return moveList;
}
/**
* Fonction permettant de récupérer les lignes d'écriture de rejet de facture à transférer sur le compte client douteux
* @param rule
* Le règle à appliquer :
* <ul>
* <li>0 = Créance de + 6 mois</li>
* <li>1 = Créance de + 3 mois</li>
* </ul>
* @param doubtfulCustomerAccount
* Le compte client douteux
* @param company
* La société
* @return
* Les lignes d'écriture de rejet de facture à transférer sur le comtpe client douteux
*/
public List<? extends MoveLine> getRejectMoveLine(int rule, Account doubtfulCustomerAccount, Company company) {
LocalDate date = null;
List<? extends MoveLine> moveLineList = null;
switch (rule) {
//Créance de + 6 mois
case 0 :
date = this.today.minusMonths(company.getAccountConfig().getSixMonthDebtMonthNumber());
moveLineList = moveLineRepo.all().filter("self.company = ?1 AND self.account.reconcileOk = 'true' " +
"AND self.invoiceReject IS NOT NULL AND self.amountRemaining > 0.00 AND self.debit > 0.00 AND self.dueDate < ?2 " +
"AND self.account != ?3",company, date, doubtfulCustomerAccount).fetch();
break;
//Créance de + 3 mois
case 1 :
date = this.today.minusMonths(company.getAccountConfig().getThreeMonthDebtMontsNumber());
moveLineList = moveLineRepo.all().filter("self.company = ?1 AND self.account.reconcileOk = 'true' " +
"AND self.invoiceReject IS NOT NULL AND self.amountRemaining > 0.00 AND self.debit > 0.00 AND self.dueDate < ?2 " +
"AND self.account != ?3",company, date, doubtfulCustomerAccount).fetch();
break;
default:
break;
}
log.debug("Date de créance prise en compte : {} ",date);
return moveLineList;
}
}