/**
* 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.payment;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
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.Move;
import com.axelor.apps.account.db.MoveLine;
import com.axelor.apps.account.db.PaymentInvoiceToPay;
import com.axelor.apps.account.db.Reconcile;
import com.axelor.apps.account.service.ReconcileService;
import com.axelor.apps.account.service.move.MoveLineService;
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.google.inject.Inject;
public class PaymentService {
private final Logger log = LoggerFactory.getLogger( getClass() );
protected ReconcileService reconcileService;
protected MoveLineService moveLineService;
protected LocalDate today;
@Inject
public PaymentService(GeneralService generalService, ReconcileService reconcileService, MoveLineService moveLineService) {
this.reconcileService = reconcileService;
this.moveLineService = moveLineService;
today = generalService.getTodayDate();
}
/**
* Utiliser le trop perçu entre deux listes de lignes d'écritures (une en débit, une en crédit)
* Si cette methode doit être utilisée, penser à ordonner les listes qui lui sont passées par date croissante
* Ceci permet de payer les facture de manière chronologique.
*
* @param debitMoveLines = dûs
* @param creditMoveLines = trop-perçu
*
* @return
* @throws AxelorException
*/
public void useExcessPaymentOnMoveLines(List<MoveLine> debitMoveLines, List<MoveLine> creditMoveLines) throws AxelorException {
if(debitMoveLines != null && creditMoveLines != null){
log.debug("Emploie du trop perçu (nombre de lignes en débit : {}, nombre de ligne en crédit : {})",
new Object[]{debitMoveLines.size(), creditMoveLines.size()});
BigDecimal amount = null;
Reconcile reconcile = null;
BigDecimal debitTotalRemaining = BigDecimal.ZERO;
BigDecimal creditTotalRemaining = BigDecimal.ZERO;
for(MoveLine creditMoveLine : creditMoveLines) {
log.debug("Emploie du trop perçu : ligne en crédit : {})", creditMoveLine);
log.debug("Emploie du trop perçu : ligne en crédit (restant à payer): {})", creditMoveLine.getAmountRemaining());
creditTotalRemaining = creditTotalRemaining.add(creditMoveLine.getAmountRemaining());
}
for(MoveLine debitMoveLine : debitMoveLines) {
log.debug("Emploie du trop perçu : ligne en débit : {})", debitMoveLine);
log.debug("Emploie du trop perçu : ligne en débit (restant à payer): {})", debitMoveLine.getAmountRemaining());
debitTotalRemaining = debitTotalRemaining.add(debitMoveLine.getAmountRemaining());
}
for(MoveLine creditMoveLine : creditMoveLines){
if (creditMoveLine.getAmountRemaining().compareTo(BigDecimal.ZERO) == 1) {
for(MoveLine debitMoveLine : debitMoveLines){
if ((debitMoveLine.getAmountRemaining().compareTo(BigDecimal.ZERO) == 1) && (creditMoveLine.getAmountRemaining().compareTo(BigDecimal.ZERO) == 1)) {
if(debitMoveLine.getMaxAmountToReconcile() != null && debitMoveLine.getMaxAmountToReconcile().compareTo(BigDecimal.ZERO) > 0) {
amount = debitMoveLine.getMaxAmountToReconcile().min(creditMoveLine.getAmountRemaining());
debitMoveLine.setMaxAmountToReconcile(null);
}
else {
amount = creditMoveLine.getAmountRemaining().min(debitMoveLine.getAmountRemaining());
}
log.debug("amount : {}",amount);
log.debug("debitTotalRemaining : {}",debitTotalRemaining);
log.debug("creditTotalRemaining : {}",creditTotalRemaining);
BigDecimal nextDebitTotalRemaining = debitTotalRemaining.subtract(amount);
BigDecimal nextCreditTotalRemaining = creditTotalRemaining.subtract(amount);
// Gestion du passage en 580
if(nextDebitTotalRemaining.compareTo(BigDecimal.ZERO) <= 0
|| nextCreditTotalRemaining.compareTo(BigDecimal.ZERO) <= 0) {
log.debug("last loop");
reconcile = reconcileService.createReconcile(debitMoveLine, creditMoveLine, amount, true);
}
else {
reconcile = reconcileService.createReconcile(debitMoveLine, creditMoveLine, amount, false);
}
// End gestion du passage en 580
reconcileService.confirmReconcile(reconcile);
debitTotalRemaining= debitTotalRemaining.subtract(amount);
creditTotalRemaining = creditTotalRemaining.subtract(amount);
log.debug("Réconciliation : {}", reconcile);
}
}
}
}
}
}
/**
* Il crée des écritures de trop percu avec des montants exacts pour chaque débitMoveLines
* avec le compte du débitMoveLines.
* A la fin, si il reste un trop-percu alors créer un trop-perçu classique.
* @param debitMoveLines
* Les lignes d'écriture à payer
* @param remainingPaidAmount
* Le montant restant à payer
* @param move
* Une écriture
* @param moveLineNo
* Un numéro de ligne d'écriture
* @return
* @throws AxelorException
*/
public int createExcessPaymentWithAmount(List<MoveLine> debitMoveLines, BigDecimal remainingPaidAmount, Move move, int moveLineNo, Partner partner,
Company company, PaymentInvoiceToPay paymentInvoiceToPay, Account account, LocalDate paymentDate) throws AxelorException {
log.debug("In createExcessPaymentWithAmount");
int moveLineNo2 = moveLineNo;
BigDecimal remainingPaidAmount2 = remainingPaidAmount;
List<Reconcile> reconcileList = new ArrayList<Reconcile>();
int i = debitMoveLines.size();
for(MoveLine debitMoveLine : debitMoveLines) {
i--;
BigDecimal amountRemaining = debitMoveLine.getAmountRemaining();
//Afin de pouvoir arrêter si il n'y a plus rien pour payer
if(remainingPaidAmount2.compareTo(BigDecimal.ZERO) <= 0) {
break;
}
BigDecimal amountToPay = remainingPaidAmount2.min(amountRemaining);
String invoiceName = "";
if(debitMoveLine.getMove().getInvoice()!=null) {
invoiceName = debitMoveLine.getMove().getInvoice().getInvoiceId();
}
else {
invoiceName = paymentInvoiceToPay.getPaymentVoucher().getRef();
}
MoveLine creditMoveLine = moveLineService.createMoveLine(move,
debitMoveLine.getPartner(),
debitMoveLine.getAccount(),
amountToPay,
false,
this.today,
moveLineNo2,
invoiceName);
move.getMoveLineList().add(creditMoveLine);
// Utiliser uniquement dans le cas du paiemnt des échéances lors d'une saisie paiement
if(paymentInvoiceToPay != null) {
creditMoveLine.setPaymentScheduleLine(paymentInvoiceToPay.getMoveLine().getPaymentScheduleLine());
paymentInvoiceToPay.setMoveLineGenerated(creditMoveLine);
}
moveLineNo2++;
Reconcile reconcile = null;
// Gestion du passage en 580
if(i == 0 ) {
log.debug("last loop");
reconcile = reconcileService.createReconcile(debitMoveLine, creditMoveLine, amountToPay, true);
}
else {
reconcile = reconcileService.createReconcile(debitMoveLine, creditMoveLine, amountToPay, false);
}
// End gestion du passage en 580
reconcileList.add(reconcile);
remainingPaidAmount2 = remainingPaidAmount2.subtract(amountRemaining);
}
for(Reconcile reconcile : reconcileList) {
reconcileService.confirmReconcile(reconcile);
}
// Si il y a un restant à payer, alors on crée un trop-perçu.
if(remainingPaidAmount2.compareTo(BigDecimal.ZERO) > 0 ) {
MoveLine moveLine = moveLineService.createMoveLine(move,
partner,
account,
remainingPaidAmount2,
false,
this.today,
moveLineNo2,
null);
move.getMoveLineList().add(moveLine);
moveLineNo2++;
// Gestion du passage en 580
reconcileService.balanceCredit(moveLine);
}
log.debug("End createExcessPaymentWithAmount");
return moveLineNo2;
}
@SuppressWarnings("unchecked")
public int useExcessPaymentWithAmountConsolidated(List<MoveLine> creditMoveLines, BigDecimal remainingPaidAmount, Move move, int moveLineNo, Partner partner,
Company company, Account account, LocalDate date, LocalDate dueDate) throws AxelorException {
log.debug("In useExcessPaymentWithAmount");
int moveLineNo2 = moveLineNo;
BigDecimal remainingPaidAmount2 = remainingPaidAmount;
List<Reconcile> reconcileList = new ArrayList<Reconcile>();
int i = creditMoveLines.size();
if(i!=0) {
Query q = JPA.em().createQuery("select new map(ml.account, SUM(ml.amountRemaining)) FROM MoveLine as ml " +
"WHERE ml in ?1 group by ml.account");
q.setParameter(1, creditMoveLines);
List<Map<Account,BigDecimal>> allMap = new ArrayList<Map<Account,BigDecimal>>();
allMap = q.getResultList();
for(Map<Account,BigDecimal> map : allMap) {
Account accountMap = (Account)map.values().toArray()[1];
BigDecimal amountMap = (BigDecimal)map.values().toArray()[0];
BigDecimal amountDebit = amountMap.min(remainingPaidAmount2);
if(amountDebit.compareTo(BigDecimal.ZERO) > 0) {
MoveLine debitMoveLine = moveLineService.createMoveLine(move,
partner,
accountMap,
amountDebit,
true,
date,
dueDate,
moveLineNo2,
null);
move.getMoveLineList().add(debitMoveLine);
moveLineNo2++;
for(MoveLine creditMoveLine : creditMoveLines) {
if(creditMoveLine.getAccount().equals(accountMap)) {
Reconcile reconcile = null;
i--;
//Afin de pouvoir arrêter si il n'y a plus rien à payer
if(amountDebit.compareTo(BigDecimal.ZERO) <= 0) {
break;
}
BigDecimal amountToPay = amountDebit.min(creditMoveLine.getAmountRemaining());
// Gestion du passage en 580
if(i == 0 ) {
reconcile = reconcileService.createReconcile(debitMoveLine, creditMoveLine, amountToPay, true);
}
else {
reconcile = reconcileService.createReconcile(debitMoveLine, creditMoveLine, amountToPay, false);
}
// End gestion du passage en 580
remainingPaidAmount2 = remainingPaidAmount2.subtract(amountToPay);
amountDebit = amountDebit.subtract(amountToPay);
reconcileList.add(reconcile);
}
}
}
}
for(Reconcile reconcile : reconcileList) {
reconcileService.confirmReconcile(reconcile);
}
}
// Si il y a un restant à payer, alors on crée un dû.
if(remainingPaidAmount2.compareTo(BigDecimal.ZERO) > 0 ) {
MoveLine debitmoveLine = moveLineService.createMoveLine(move,
partner,
account,
remainingPaidAmount2,
true,
date,
dueDate,
moveLineNo2,
null);
move.getMoveLineList().add(debitmoveLine);
moveLineNo2++;
}
log.debug("End useExcessPaymentWithAmount");
return moveLineNo2;
}
}