/**
   * Procédure permettant d'exporter des factures
   *
   * @param company Une société
   * @param pse Un Export des prélèvement
   * @param pm Un mode de paiement
   * @throws AxelorException
   */
  @Transactional(rollbackOn = {AxelorException.class, Exception.class})
  public Invoice exportInvoice(
      MoveLine moveLine,
      List<MoveLine> moveLineList,
      Company company,
      long directDebitManagementMaxId)
      throws AxelorException {

    /**
     * Important : Doit être executé avant la méthode 'createPaymentMove()' afin de récupérer le
     * montant restant à prélever *
     */
    BigDecimal amountExported = moveLine.getAmountRemaining();

    this.testBankDetails(moveLine.getPartner());

    // creation d'une ecriture de paiement
    Invoice invoice =
        this.updateInvoice(
            moveLine,
            this.createPaymentMove(
                company, moveLine, company.getAccountConfig().getDirectDebitPaymentMode()),
            moveLineList,
            amountExported,
            directDebitManagementMaxId);

    invoiceRepo.save(invoice);

    return invoice;
  }
  /**
   * Y a t'il d'autres facture a exporter pour le même payeur ?
   *
   * @param moveLineList : une liste de ligne d'écriture de facture
   * @param psl
   * @return
   */
  public boolean hasOtherInvoice(List<MoveLine> moveLineList, MoveLine moveLine) {

    Partner partner = moveLine.getPartner();

    Query q =
        JPA.em()
            .createQuery(
                "select count(*) FROM MoveLine as self WHERE self IN ?1 AND self.partner = ?2 ");
    q.setParameter(1, moveLineList);
    q.setParameter(2, partner);

    if ((long) q.getSingleResult() > 1) {
      log.debug(
          "Recherche d'une autre facture à prélever (autre que l'écriture {}) pour le tiers {} : OUI",
          moveLine.getName(),
          partner.getFullName());
      return true;
    }

    log.debug(
        "Recherche d'une autre facture à prélever (autre que l'écriture {}) pour le tiers {} : NON",
        moveLine.getName(),
        partner.getFullName());

    return false;
  }
  /**
   * Méthode permettant de lettrer une écriture au débit avec une écriture au crédit
   *
   * @param debitMoveLine
   * @param creditMoveLine
   * @throws AxelorException
   */
  public void reconcile(
      MoveLine debitMoveLine, MoveLine creditMoveLine, boolean updateCustomerAccount)
      throws AxelorException {

    BigDecimal amount = debitMoveLine.getAmountRemaining().min(creditMoveLine.getAmountRemaining());
    Reconcile reconcile = this.createReconcile(debitMoveLine, creditMoveLine, amount);
    this.confirmReconcile(reconcile, updateCustomerAccount);
  }
  /**
   * Fonction calculant le montant de la contrepartie d'une écriture de prélèvement
   *
   * @param pslList Une écriture de prélèvement
   * @return Le montant total
   */
  public BigDecimal totalAmount(Move move) {
    BigDecimal total = BigDecimal.ZERO;
    for (MoveLine moveLine : move.getMoveLineList()) {
      total = total.add(moveLine.getCredit());
    }
    log.debug("Montant total : {}", total);

    return total;
  }
 /**
  * Méthode permettant de construire un log affichant une liste de lignes d'écriture
  *
  * @param paymentScheduleLineList
  * @return
  */
 public String toStringMoveLineList(List<MoveLine> moveLineList) {
   String list = " (nb = ";
   list += moveLineList.size();
   list += " ) : ";
   for (MoveLine moveLine : moveLineList) {
     list += moveLine.getName();
     list += ", ";
   }
   return list;
 }
 /**
  * Procédure permettant de récupérer l'écriture d'avoir d'un trop-perçu généré par un avoir
  *
  * @param moveLine un trop-perçu
  * @return
  */
 public Move getRefundMove(MoveLine moveLine) {
   Move move = moveLine.getMove();
   if (move.getJournal().equals(move.getCompany().getTechnicalJournal())) {
     MoveLine oppositeMoveLine = ms.getOppositeMoveLine(moveLine);
     if (oppositeMoveLine.getReconcileList1() != null
         && oppositeMoveLine.getReconcileList1().size() == 1) {
       return oppositeMoveLine.getReconcileList1().get(0).getLineCredit().getMove();
     }
   }
   return null;
 }
  /**
   * Procédure permettant de récupérer l'objet de gestion déjà créé lors du prélèvement d'une autre
   * facture
   *
   * @param mlList La liste des lignes d'écriture de facture à prélever
   * @param ml Une ligne d'écriture de facture ) prélever
   * @return L'objet de gestion trouvé
   */
  public DirectDebitManagement getDirectDebitManagement(
      List<MoveLine> moveLineList, MoveLine ml, long directDebitManagementMaxId) {

    Partner partner = ml.getPartner();

    log.debug("Récupération de l'objet de prélèvement du tiers {}", partner.getFullName());

    List<MoveLine> moveLineListResult =
        moveLineRepo
            .all()
            .filter("self IN (?1) and self.partner = ?2", moveLineList, partner)
            .fetch();

    for (MoveLine moveLine : moveLineListResult) {
      Invoice invoice = cfonbExportService.getInvoice(moveLine);

      DirectDebitManagement directDebitManagement = invoice.getDirectDebitManagement();
      if (directDebitManagement != null
          && directDebitManagement.getId() > directDebitManagementMaxId) {

        log.debug("Objet de prélèvement trouvé : {} pour le tiers {}", partner.getFullName());
        return invoice.getDirectDebitManagement();
      }
    }

    log.debug("Aucun objet de prélèvement trouvé pour le tiers {}", partner.getFullName());

    return null;
  }
  public void updatePartnerAccountingSituation(Reconcile reconcile, boolean updateCustomerAccount) {
    Company company = null;
    List<Partner> partnerList = new ArrayList<Partner>();

    MoveLine debitMoveLine = reconcile.getLineDebit();
    MoveLine creditMoveLine = reconcile.getLineCredit();
    Partner debitPartner = debitMoveLine.getPartner();
    Partner creditPartner = creditMoveLine.getPartner();

    if (debitPartner != null) {
      Move move = debitMoveLine.getMove();
      if (move != null && move.getCompany() != null) {
        partnerList.add(debitPartner);
        company = move.getCompany();
      }
    }
    if (creditPartner != null) {
      Move move = creditMoveLine.getMove();
      if (move != null && move.getCompany() != null) {
        partnerList.add(creditPartner);
        company = move.getCompany();
      }
    }

    if (partnerList != null && !partnerList.isEmpty() && company != null) {
      if (updateCustomerAccount) {
        acs.updatePartnerAccountingSituation(partnerList, company, true, true, false);
      } else {
        acs.flagPartners(partnerList, company);
      }
    }
  }
  /**
   * Procédure permettant de créer une écriture de paiement d'une facture
   *
   * @param company Une société
   * @param moveLine Une ligne d'écriture
   * @param pm Un mode de paiement
   * @param pse Un Export des prélèvement
   * @throws AxelorException
   */
  public Move createPaymentMove(Company company, MoveLine moveLine, PaymentMode paymentMode)
      throws AxelorException {

    log.debug("Create payment move");

    Move paymentMove =
        moveService
            .getMoveCreateService()
            .createMove(
                paymentModeService.getPaymentModeJournal(paymentMode, company),
                company,
                null,
                null,
                paymentMode);

    BigDecimal amountExported = moveLine.getAmountRemaining();

    this.createPaymentMoveLine(paymentMove, moveLine, 1);

    log.debug("Create payment move line");

    Account paymentModeAccount = paymentModeService.getCompanyAccount(paymentMode, company);

    String invoiceName = "";
    if (moveLine.getMove().getInvoice() != null) {
      invoiceName = moveLine.getMove().getInvoice().getInvoiceId();
    }
    MoveLine moveLineGenerated2 =
        moveLineServices.createMoveLine(
            paymentMove, null, paymentModeAccount, amountExported, true, today, 2, invoiceName);

    paymentMove.getMoveLineList().add(moveLineGenerated2);
    moveLineRepo.save(moveLineGenerated2);

    moveService.getMoveValidateService().validateMove(paymentMove);
    moveRepo.save(paymentMove);

    return paymentMove;
  }
  public void createPaymentMoveLine(Move paymentMove, MoveLine moveLine, int ref)
      throws AxelorException {
    BigDecimal amountExported = moveLine.getAmountRemaining();

    // On assigne le montant exporté pour pouvoir l'utiliser lors de la création du fichier d'export
    // CFONB
    moveLine.setAmountExportedInDirectDebit(amountExported);

    // creation d'une ecriture de paiement

    log.debug("generateAllExportInvoice - Création de la première ligne d'écriture");
    String invoiceName = "";
    if (moveLine.getMove().getInvoice() != null) {
      invoiceName = moveLine.getMove().getInvoice().getInvoiceId();
    }
    MoveLine moveLineGenerated =
        moveLineServices.createMoveLine(
            paymentMove,
            moveLine.getPartner(),
            moveLine.getAccount(),
            amountExported,
            false,
            today,
            ref,
            invoiceName);

    paymentMove.getMoveLineList().add(moveLineGenerated);

    moveLineRepo.save(moveLineGenerated);

    // Lettrage de la ligne 411 avec la ligne 411 de la facture
    log.debug("Creation du lettrage de la ligne 411 avec la ligne 411 de la facture");

    reconcileService.reconcile(moveLine, moveLineGenerated);

    log.debug("generateAllExportInvoice - Sauvegarde de l'écriture");

    moveRepo.save(paymentMove);
  }
  public List<MoveLine> getInvoiceToExport(
      Company company, LocalDate scheduleDate, Currency currency) {

    List<MoveLine> moveLineInvoiceList = new ArrayList<MoveLine>();

    PaymentMode paymentMode = company.getAccountConfig().getDirectDebitPaymentMode();

    /**
     * Selection des lignes d'écritures dont : - l'état est validé - la société est celle
     * selectionnée sur l'objet export - le compte est lettrable - le montant restant à payer est
     * supérieur à 0 et débit supérieur à 0 (équivaut à une facture et non un avoir) - le mode de
     * règlement de la facture est en prélèvement - la date d'échéance est passée - la facture est
     * remplie sur l'écriture - la facture n'est pas selectionnée sur un échéancier
     */
    List<MoveLine> moveLineList =
        moveLineRepo
            .all()
            .filter(
                "self.move.statusSelect = ?1 AND self.exportedDirectDebitOk = 'false' "
                    + "AND self.move.company = ?2 "
                    + "AND self.account.reconcileOk = ?3 AND self.amountRemaining > 0 "
                    + "AND self.debit > 0 "
                    + "AND self.dueDate <= ?5 AND self.move.invoice IS NOT NULL "
                    + "AND self.move.invoice.paymentMode = ?4 "
                    + "AND self.move.invoice.schedulePaymentOk = 'false' "
                    + "AND self.move.invoice.currency = ?5",
                MoveRepository.STATUS_VALIDATED,
                company,
                true,
                paymentMode,
                currency)
            .fetch();

    // Ajout des factures
    for (MoveLine moveLine : moveLineList) {
      if (!this.isDebitBlocking(moveLine.getMove().getInvoice())) {
        moveLineInvoiceList.add(moveLine);
      }
    }

    // Récupération des factures rejetées
    List<Invoice> invoiceRejectList =
        invoiceRepo
            .all()
            .filter(
                "self.rejectMoveLine IS NOT NULL AND self.rejectMoveLine.amountRemaining > 0 AND self.rejectMoveLine.debit > 0"
                    + " AND self.paymentMode = ?1 AND self.company = ?2 AND self.rejectMoveLine.exportedDirectDebitOk = 'false' AND self.move.statusSelect = ?3"
                    + " AND self.rejectMoveLine.account.reconcileOk = 'true' "
                    + " AND self.rejectMoveLine.invoiceReject IS NOT NULL"
                    + " AND self.currency = ?4",
                paymentMode,
                company,
                MoveRepository.STATUS_VALIDATED,
                currency)
            .fetch();

    // Ajout des factures rejetées
    for (Invoice invoice : invoiceRejectList) {

      if (!this.isDebitBlocking(invoice)) {
        moveLineInvoiceList.add(invoice.getRejectMoveLine());
      }
    }

    return moveLineInvoiceList;
  }
  /**
   * Solder le trop-perçu si il respect les règles de seuil
   *
   * @param creditMoveLine
   * @param company
   * @throws AxelorException
   */
  public void balanceCredit(MoveLine creditMoveLine, Company company, boolean updateCustomerAccount)
      throws AxelorException {
    if (creditMoveLine != null) {
      BigDecimal creditAmountRemaining = creditMoveLine.getAmountRemaining();
      LOG.debug("Montant à payer / à lettrer au crédit : {}", creditAmountRemaining);

      if (creditAmountRemaining.compareTo(BigDecimal.ZERO) > 0) {
        if (creditAmountRemaining.plus().compareTo(company.getThresholdDistanceFromRegulation())
            < 0) {

          LOG.debug("Seuil respecté");

          Partner partner = creditMoveLine.getPartner();
          Account account = creditMoveLine.getAccount();

          if (company.getMiscOperationJournal() == null) {
            throw new AxelorException(
                String.format(
                    "%s :\n Veuillez configurer un journal des O.D. pour la société %s",
                    GeneralService.getExceptionAccountingMsg(), company.getName()),
                IException.CONFIGURATION_ERROR);
          }

          Move newMove =
              ms.createMove(company.getMiscOperationJournal(), company, null, partner, null, false);

          // Création de la ligne au crédit
          MoveLine newCreditMoveLine =
              mls.createMoveLine(
                  newMove,
                  partner,
                  company.getCashPositionVariationAccount(),
                  creditAmountRemaining,
                  false,
                  false,
                  today,
                  2,
                  false,
                  false,
                  false,
                  null);

          // Création de la ligne au débit
          MoveLine newDebitMoveLine =
              mls.createMoveLine(
                  newMove,
                  partner,
                  account,
                  creditAmountRemaining,
                  true,
                  false,
                  today,
                  1,
                  false,
                  false,
                  false,
                  null);

          newMove.getMoveLineList().add(newCreditMoveLine);
          newMove.getMoveLineList().add(newDebitMoveLine);
          ms.validateMove(newMove, updateCustomerAccount);
          newMove.save();

          // Création de la réconciliation
          Reconcile newReconcile =
              this.createReconcile(newDebitMoveLine, creditMoveLine, creditAmountRemaining);
          this.confirmReconcile(newReconcile, updateCustomerAccount);
          newReconcile.save();
        }
      }
    }
  }
  /**
   * Procédure permettant de gérer les écarts de règlement, check sur la case à cocher 'Peut être
   * soldé' Alors nous utilisons la règle de gestion consitant à imputer l'écart sur un compte
   * transitoire si le seuil est respecté
   *
   * @param reconcile Une reconciliation
   * @throws AxelorException
   */
  @Transactional(rollbackOn = {AxelorException.class, Exception.class})
  public void canBeZeroBalance(Reconcile reconcile) throws AxelorException {

    MoveLine debitMoveLine = reconcile.getLineDebit();

    BigDecimal debitAmountRemaining = debitMoveLine.getAmountRemaining();
    LOG.debug("Montant à payer / à lettrer au débit : {}", debitAmountRemaining);
    if (debitAmountRemaining.compareTo(BigDecimal.ZERO) > 0) {
      Company company = reconcile.getLineDebit().getMove().getCompany();
      if (debitAmountRemaining.plus().compareTo(company.getThresholdDistanceFromRegulation()) < 0
          || reconcile.getMustBeZeroBalanceOk()) {

        LOG.debug("Seuil respecté");

        Partner partner = debitMoveLine.getPartner();
        Account account = debitMoveLine.getAccount();

        if (company.getMiscOperationJournal() == null) {
          throw new AxelorException(
              String.format(
                  "%s :\n Veuillez configurer un journal des O.D. pour la société %s",
                  GeneralService.getExceptionAccountingMsg(), company.getName()),
              IException.CONFIGURATION_ERROR);
        }

        Move newMove =
            ms.createMove(company.getMiscOperationJournal(), company, null, partner, null, false);

        // Création de la ligne au crédit
        MoveLine newCreditMoveLine =
            mls.createMoveLine(
                newMove,
                partner,
                account,
                debitAmountRemaining,
                false,
                false,
                today,
                1,
                false,
                false,
                false,
                null);

        // Création de la ligne au debit
        MoveLine newDebitMoveLine =
            mls.createMoveLine(
                newMove,
                partner,
                company.getCashPositionVariationAccount(),
                debitAmountRemaining,
                true,
                false,
                today,
                2,
                false,
                false,
                false,
                null);

        newMove.getMoveLineList().add(newCreditMoveLine);
        newMove.getMoveLineList().add(newDebitMoveLine);
        ms.validateMove(newMove);
        newMove.save();

        // Création de la réconciliation
        Reconcile newReconcile =
            this.createReconcile(debitMoveLine, newCreditMoveLine, debitAmountRemaining);
        this.confirmReconcile(newReconcile);
        newReconcile.save();
      }
    }

    reconcile.setCanBeZeroBalanceOk(false);
    LOG.debug("Fin de la gestion des écarts de règlement");
  }