/**
   * This method is used by SaleModificationForm
   *
   * @param sale
   * @return
   * @throws IabakoActionForbiddenException
   */
  @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
  public Sale save(Sale sale)
      throws IabakoActionForbiddenException, IabakoUniqueConstraintException {

    Enterprise enterprise = getEnterpriseFromSessionUser();
    if (sale.getId() != null && enterprise.getAllRelatedEnterprises().size() > 1) {
      // Do not change Enterprise!
      sale.setEnterprise(saleDAO.findById(sale.getId()).getEnterprise());
    } else {
      sale.setEnterprise(enterprise);
    }

    if (enterprise != null && enterprise.isDemo()) {
      throw new IabakoActionForbiddenException(
          "action_forbidden_for_demo_title", "action_forbidden_for_demo_text", true);
    }

    if (!getDAO().isUniqueNumber(sale)) {
      throw new IabakoUniqueConstraintException(
          messages.getLabel("validation_sale_unique_constraint_error_title"),
          messages.getLabel("validation_sale_unique_constraint_error_text", sale.getNumber()),
          true,
          true);
    }

    Sale saleFromDB = saleDAO.findById(sale.getId());
    setAttributesFromDB(sale, saleFromDB);

    if (!enterprise.getPaymentRequestConfig().isEnabled()
        || (sale.getClient() != null && sale.getClient().isPaymentRequestDisabled())) {
      // If paymentRequest disable, then do not change initial value (it doesn't come from client
      // world)
      sale.setPaymentRequestDisabled(saleFromDB.isPaymentRequestDisabled());
    }

    sale.setDate(GenericTools.getDateFromString(sale.getDateString()));

    trackingService.addTrackingToUserSession(TrackingType.saleModify, sale);

    return rawSave(sale);
  }
  /**
   * Method to ALWAYS USED IN SALE MAKE (PERSIST AND DRAFT MODE) We can't pass by cascading persist
   * of SaleProductServices, Payments and Installments because GWT does not support (serialize)
   * PersistentBag and Proxy/Lazy hibernate objects
   *
   * @param sale Sale object from GWT client (navigator/JS world)
   * @param payments List of Payment from GWT client (navigator/JS world)
   * @param installments List of Installments from GWT client (navigator/JS world)
   */
  @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
  public synchronized Sale save(
      boolean isDraft,
      Sale sale,
      List<SaleProductService> productsServices,
      List<Expense> expenses,
      List<Payment> payments,
      List<Installment> installments)
      throws IabakoActionForbiddenException, IabakoPackageForbiddenException,
          IabakoUniqueConstraintException, IabakoStockException {

    Enterprise enterprise = getEnterpriseFromSessionUser();

    if (enterprise != null && enterprise.isDemo()) {
      throw new IabakoActionForbiddenException(
          "action_forbidden_for_demo_title", "action_forbidden_for_demo_text", true);
    }

    if (!getDAO().isUniqueNumber(sale)) {
      throw new IabakoUniqueConstraintException(
          messages.getLabel("validation_sale_unique_constraint_error_title"),
          messages.getLabel("validation_sale_unique_constraint_error_text", sale.getNumber()),
          true,
          true);
    }

    if (sale.getEnterprise() == null) {
      sale.setEnterprise(enterprise);
    }

    if (sale.getClient() != null && sale.getClient().getEnterprise() == null) {
      sale.getClient().setEnterprise(enterprise);
    }

    sale.setDate(GenericTools.getDateFromString(sale.getDateString()));

    addProductsServicesToSale(isDraft, sale, productsServices);
    addCustomInstallmentsDefinition(sale, installments);

    sale.setExpenses(expenses);
    // This is because expenses is @GWTTransient and It should not be used too much (so it helps to
    // avoid unnecessary server calls).
    sale.setSaleWithExpenses(!expenses.isEmpty());

    if (!isDraft) {
      addClientTag(sale);
      addPaymentsInstallments(sale, payments, installments);
      sale.setRecalculatedTotal(
          ServerTools.round(sale.calculateAmountFromPaymentsAndInstalments()));
      sale.setTaxPercent(sale.getPonderedTaxPercent());
    } else {
      sale.setTaxPercent(sale.getPonderedTaxPercent());
      sale.setRecalculatedTotal(sale.calculateAmountFromProductsServices());
    }
    sale.setTotalBeforeTax(
        sale.getTaxPercent() > 0
            ? ServerTools.round(sale.getRecalculatedTotal() / (1 + sale.getTaxPercent() / 100))
            : null);
    sale.setTotalAfterTax(sale.getTaxPercent() > 0 ? sale.getRecalculatedTotal() : null);
    sale.setTotalNoTax(sale.getTaxPercent() > 0 ? null : sale.getRecalculatedTotal());

    boolean isCreation = sale.getId() == null;
    sale = calculateStatusAndSave(sale, isDraft);
    setBusinessInfo(sale);

    for (Expense e : expenses) {
      // Sale.expenses is a detached relationship. It must be merge manually (not by cascade
      // hibernate function)
      e.setSale(sale);
      e.setPaymentDate(GenericTools.getDateFromString(e.getPaymentDateString()));
      e.setEnterprise(enterprise);
      expenseService.save(e);
    }

    Sale quote = checkIfQuote(sale);

    if (isCreation && quote != null) {
      trackingService.addTrackingToUserSession(TrackingType.quoteTransformed, quote);

    } else if (isDraft) {
      trackingService.addTrackingToUserSession(TrackingType.saleDraft, sale);

    } else {
      trackingService.addTrackingToUserSession(TrackingType.saleNew, sale);
      userService.addInvoiceReceiptToEnterpriseBalance(sale, null);
    }

    return sale;
  }