org.openbravo.advpaymentmngt.process.FIN_AddPayment.java Source code

Java tutorial

Introduction

Here is the source code for org.openbravo.advpaymentmngt.process.FIN_AddPayment.java

Source

/*
 *************************************************************************
 * The contents of this file are subject to the Openbravo  Public  License
 * Version  1.0  (the  "License"),  being   the  Mozilla   Public  License
 * Version 1.1  with a permitted attribution clause; you may not  use this
 * file except in compliance with the License. You  may  obtain  a copy of
 * the License at http://www.openbravo.com/legal/license.html
 * Software distributed under the License  is  distributed  on  an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific  language  governing  rights  and  limitations
 * under the License.
 * The Original Code is Openbravo ERP.
 * The Initial Developer of the Original Code is Openbravo SLU
 * All portions are Copyright (C) 2010-2015 Openbravo SLU
 * All Rights Reserved.
 * Contributor(s):  ______________________________________.
 *************************************************************************
 */
package org.openbravo.advpaymentmngt.process;

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;

import javax.servlet.ServletException;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.openbravo.advpaymentmngt.dao.AdvPaymentMngtDao;
import org.openbravo.advpaymentmngt.utility.FIN_Utility;
import org.openbravo.base.exception.OBException;
import org.openbravo.base.secureApp.VariablesSecureApp;
import org.openbravo.base.session.OBPropertiesProvider;
import org.openbravo.base.structure.BaseOBObject;
import org.openbravo.dal.core.DalUtil;
import org.openbravo.dal.core.OBContext;
import org.openbravo.dal.service.OBCriteria;
import org.openbravo.dal.service.OBDal;
import org.openbravo.dal.service.OBDao;
import org.openbravo.data.FieldProvider;
import org.openbravo.database.ConnectionProvider;
import org.openbravo.erpCommon.utility.CashVATUtil;
import org.openbravo.erpCommon.utility.FieldProviderFactory;
import org.openbravo.erpCommon.utility.OBError;
import org.openbravo.erpCommon.utility.Utility;
import org.openbravo.model.common.businesspartner.BusinessPartner;
import org.openbravo.model.common.currency.Currency;
import org.openbravo.model.common.enterprise.DocumentType;
import org.openbravo.model.common.enterprise.Organization;
import org.openbravo.model.common.enterprise.OrganizationInformation;
import org.openbravo.model.common.invoice.Invoice;
import org.openbravo.model.common.plm.Product;
import org.openbravo.model.financialmgmt.accounting.Costcenter;
import org.openbravo.model.financialmgmt.accounting.UserDimension1;
import org.openbravo.model.financialmgmt.accounting.UserDimension2;
import org.openbravo.model.financialmgmt.gl.GLItem;
import org.openbravo.model.financialmgmt.payment.FIN_FinancialAccount;
import org.openbravo.model.financialmgmt.payment.FIN_Payment;
import org.openbravo.model.financialmgmt.payment.FIN_PaymentDetail;
import org.openbravo.model.financialmgmt.payment.FIN_PaymentMethod;
import org.openbravo.model.financialmgmt.payment.FIN_PaymentPropDetail;
import org.openbravo.model.financialmgmt.payment.FIN_PaymentProposal;
import org.openbravo.model.financialmgmt.payment.FIN_PaymentSchedInvV;
import org.openbravo.model.financialmgmt.payment.FIN_PaymentSchedule;
import org.openbravo.model.financialmgmt.payment.FIN_PaymentScheduleDetail;
import org.openbravo.model.financialmgmt.payment.FinAccPaymentMethod;
import org.openbravo.model.marketing.Campaign;
import org.openbravo.model.materialmgmt.cost.ABCActivity;
import org.openbravo.model.project.Project;
import org.openbravo.model.sales.SalesRegion;
import org.openbravo.scheduling.ProcessBundle;

public class FIN_AddPayment {
    private static AdvPaymentMngtDao dao;

    /**
     * Saves the payment and the payment details based on the given Payment Schedule Details. If no
     * FIN_Payment is given it creates a new one.
     * 
     * If the Payment Scheduled Detail is not completely paid and the difference is not written a new
     * Payment Schedule Detail is created with the difference.
     * 
     * If a Refund Amount is given an extra Payment Detail will be created with it.
     * 
     * @param _payment
     *          FIN_Payment where new payment details will be saved.
     * @param isReceipt
     *          boolean to define if the Payment is a Receipt Payment (true) or a Payable Payment
     *          (false). Used when no FIN_Payment is given.
     * @param docType
     *          DocumentType of the Payment. Used when no FIN_Payment is given.
     * @param strPaymentDocumentNo
     *          String with the Document Number of the new payment. Used when no FIN_Payment is given.
     * @param businessPartner
     *          BusinessPartner of the new Payment. Used when no FIN_Payment is given.
     * @param paymentMethod
     *          FIN_PaymentMethod of the new Payment. Used when no FIN_Payment is given.
     * @param finAccount
     *          FIN_FinancialAccount of the new Payment. Used when no FIN_Payment is given.
     * @param strPaymentAmount
     *          String with the Payment Amount of the new Payment. Used when no FIN_Payment is given.
     * @param paymentDate
     *          Date when the Payment is done. Used when no FIN_Payment is given.
     * @param organization
     *          Organization of the new Payment. Used when no FIN_Payment is given.
     * @param selectedPaymentScheduleDetails
     *          List of FIN_PaymentScheduleDetail to be included in the Payment. If one of the items
     *          is contained in other payment the method will throw an exception. Prevent
     *          invoice/order to be paid several times.
     * @param selectedPaymentScheduleDetailsAmounts
     *          HashMap with the Amount to be paid for each Scheduled Payment Detail.
     * @param isWriteoff
     *          Boolean to write off the difference when the payment amount is lower than the Payment
     *          Scheduled PAyment Detail amount.
     * @param isRefund
     *          Not used.
     * @param paymentCurrency
     *          The currency that the payment is being made in. Will default to financial account
     *          currency if not specified
     * @param finTxnConvertRate
     *          Exchange rate to convert between payment currency and financial account currency for
     *          this payment. Defaults to 1.0 if not supplied
     * @param finTxnAmount
     *          Amount of payment in currency of financial account
     * @return The FIN_Payment OBObject containing all the Payment Details.
     */
    public static FIN_Payment savePayment(FIN_Payment _payment, boolean isReceipt, DocumentType docType,
            String strPaymentDocumentNo, BusinessPartner businessPartner, FIN_PaymentMethod paymentMethod,
            FIN_FinancialAccount finAccount, String strPaymentAmount, Date paymentDate, Organization organization,
            String referenceNo, List<FIN_PaymentScheduleDetail> selectedPaymentScheduleDetails,
            HashMap<String, BigDecimal> selectedPaymentScheduleDetailsAmounts, boolean isWriteoff, boolean isRefund,
            Currency paymentCurrency, BigDecimal finTxnConvertRate, BigDecimal finTxnAmount) {
        dao = new AdvPaymentMngtDao();

        BigDecimal assignedAmount = BigDecimal.ZERO;
        final FIN_Payment payment;
        if (_payment != null) {
            payment = _payment;
        } else {
            payment = dao.getNewPayment(isReceipt, organization, docType, strPaymentDocumentNo, businessPartner,
                    paymentMethod, finAccount, strPaymentAmount, paymentDate, referenceNo, paymentCurrency,
                    finTxnConvertRate, finTxnAmount);
            try {
                OBDal.getInstance().flush();
            } catch (Exception e) {
                throw new OBException(FIN_Utility.getExceptionMessage(e));
            }
        }

        for (FIN_PaymentDetail paymentDetail : payment.getFINPaymentDetailList()) {
            assignedAmount = assignedAmount.add(paymentDetail.getAmount());
        }
        // FIXME: added to access the FIN_PaymentSchedule and FIN_PaymentScheduleDetail tables to be
        // removed when new security implementation is done
        OBContext.setAdminMode();
        try {
            for (FIN_PaymentScheduleDetail paymentScheduleDetail : selectedPaymentScheduleDetails) {
                BigDecimal paymentDetailAmount = selectedPaymentScheduleDetailsAmounts
                        .get(paymentScheduleDetail.getId());
                // Payment Schedule Detail already linked to a payment detail.
                OBDal.getInstance().refresh(paymentScheduleDetail);

                BigDecimal assignedAmountDiff = updatePaymentDetail(paymentScheduleDetail, payment,
                        paymentDetailAmount, isWriteoff);
                assignedAmount = assignedAmount.add(assignedAmountDiff);
            }
            // TODO: Review this condition !=0??
            if (assignedAmount.compareTo(payment.getAmount()) == -1) {
                FIN_PaymentScheduleDetail refundScheduleDetail = dao.getNewPaymentScheduleDetail(
                        payment.getOrganization(), payment.getAmount().subtract(assignedAmount));
                dao.getNewPaymentDetail(payment, refundScheduleDetail, payment.getAmount().subtract(assignedAmount),
                        BigDecimal.ZERO, false, null);
            }
        } catch (final Exception e) {
            e.printStackTrace(System.err);
            throw new OBException(e);
        } finally {
            OBContext.restorePreviousMode();
        }

        return payment;
    }

    /*
     * Temporary method to supply defaults for exchange Rate and converted amount
     */
    public static FIN_Payment savePayment(FIN_Payment _payment, boolean isReceipt, DocumentType docType,
            String strPaymentDocumentNo, BusinessPartner businessPartner, FIN_PaymentMethod paymentMethod,
            FIN_FinancialAccount finAccount, String strPaymentAmount, Date paymentDate, Organization organization,
            String referenceNo, List<FIN_PaymentScheduleDetail> selectedPaymentScheduleDetails,
            HashMap<String, BigDecimal> selectedPaymentScheduleDetailsAmounts, boolean isWriteoff,
            boolean isRefund) {
        return savePayment(_payment, isReceipt, docType, strPaymentDocumentNo, businessPartner, paymentMethod,
                finAccount, strPaymentAmount, paymentDate, organization, referenceNo,
                selectedPaymentScheduleDetails, selectedPaymentScheduleDetailsAmounts, isWriteoff, isRefund, null,
                null, null);
    }

    /**
     * Updates the paymentScheduleDetail with the paymentDetailAmount. If it is not related to the
     * Payment a new Payment Detail is created. If isWriteoff is true and the amount is different to
     * the outstanding amount the difference is written off.
     * 
     * @param paymentScheduleDetail
     *          the Payment Schedule Detail to be assigned to the Payment
     * @param payment
     *          the FIN_Payment that it is being paid
     * @param paymentDetailAmount
     *          the amount of this paymentScheduleDetail that it is being paid
     * @param isWriteoff
     *          flag to write off the difference when there is an outstanding amount remaining to pay
     * @return a BigDecimal with the amount newly assigned to the payment. For example, when the
     *         paymentScheduleDetail is already related to the payment and its amount is not changed
     *         BigDecimal.ZERO is returned.
     * @throws OBException
     *           when the paymentDetailAmount is related to a different payment.
     */
    public static BigDecimal updatePaymentDetail(FIN_PaymentScheduleDetail paymentScheduleDetail,
            FIN_Payment payment, BigDecimal paymentDetailAmount, boolean isWriteoff) throws OBException {
        BigDecimal assignedAmount = paymentDetailAmount;
        if (paymentScheduleDetail.getPaymentDetails() != null) {
            if (!paymentScheduleDetail.getPaymentDetails().getFinPayment().getId().equals(payment.getId())) {
                // If payment schedule detail belongs to a different payment
                throw new OBException(String.format(FIN_Utility.messageBD("APRM_PsdInSeveralPayments"),
                        paymentScheduleDetail.getIdentifier()));
            }
            // Detail for this payment already exists. Payment being edited
            // If amount has changed payment schedule details needs to be updated. Aggregate amount
            // coming from unpaid schedule detail which remains unpaid
            if (paymentScheduleDetail.getAmount().add(paymentScheduleDetail.getWriteoffAmount())
                    .compareTo(paymentDetailAmount) != 0) {
                // update Amounts as they have changed
                assignedAmount = assignedAmount.subtract(paymentScheduleDetail.getPaymentDetails().getAmount());
                // update detail with the new value
                List<FIN_PaymentScheduleDetail> outStandingPSDs = getOutstandingPSDs(paymentScheduleDetail);
                BigDecimal difference = paymentScheduleDetail.getAmount()
                        .add(paymentScheduleDetail.getWriteoffAmount()).subtract(paymentDetailAmount);
                // Assume doubtful debt is always positive
                BigDecimal doubtFulDebtAmount = BigDecimal.ZERO;
                if (outStandingPSDs.size() == 0) {
                    doubtFulDebtAmount = getDoubtFulDebtAmount(
                            paymentScheduleDetail.getAmount().add(paymentScheduleDetail.getWriteoffAmount()),
                            paymentDetailAmount, paymentScheduleDetail.getDoubtfulDebtAmount());
                    if (!isWriteoff) {
                        // No outstanding PSD exists so one needs to be created for the difference
                        FIN_PaymentScheduleDetail outstandingPSD = (FIN_PaymentScheduleDetail) DalUtil
                                .copy(paymentScheduleDetail, false);
                        outstandingPSD.setAmount(difference);
                        outstandingPSD.setWriteoffAmount(BigDecimal.ZERO);
                        outstandingPSD.setDoubtfulDebtAmount(
                                paymentScheduleDetail.getDoubtfulDebtAmount().subtract(doubtFulDebtAmount));
                        outstandingPSD.setPaymentDetails(null);
                        paymentScheduleDetail.setAmount(
                                paymentScheduleDetail.getAmount().add(paymentScheduleDetail.getWriteoffAmount()));
                        paymentScheduleDetail.setWriteoffAmount(BigDecimal.ZERO);
                        paymentScheduleDetail.getPaymentDetails().setWriteoffAmount(BigDecimal.ZERO);
                        OBDal.getInstance().save(outstandingPSD);
                    } else {
                        // If it is write Off then incorporate all doubtful debt
                        doubtFulDebtAmount = paymentScheduleDetail.getDoubtfulDebtAmount();
                        // Set difference as writeoff
                        paymentScheduleDetail.setWriteoffAmount(difference);
                        paymentScheduleDetail.setDoubtfulDebtAmount(doubtFulDebtAmount);
                        OBDal.getInstance().save(paymentScheduleDetail);
                        paymentScheduleDetail.getPaymentDetails().setWriteoffAmount(difference);
                        OBDal.getInstance().save(paymentScheduleDetail.getPaymentDetails());
                    }
                } else {
                    FIN_PaymentScheduleDetail outstandingPSD = outStandingPSDs.get(0);
                    if (!isWriteoff) {
                        if (outstandingPSD.getAmount().add(difference).signum() == 0) {
                            // If the outstanding psd amount is zero after adding the difference delete it.
                            doubtFulDebtAmount = paymentScheduleDetail.getDoubtfulDebtAmount()
                                    .add(outstandingPSD.getDoubtfulDebtAmount());
                            OBDal.getInstance().remove(outstandingPSD);
                        } else {
                            // update existing PD with difference
                            doubtFulDebtAmount = getDoubtFulDebtAmount(
                                    paymentScheduleDetail.getAmount().add(outstandingPSD.getAmount()),
                                    paymentDetailAmount, paymentScheduleDetail.getDoubtfulDebtAmount()
                                            .add(outstandingPSD.getDoubtfulDebtAmount()));
                            outstandingPSD.setAmount(outstandingPSD.getAmount().add(difference));
                            outstandingPSD.setDoubtfulDebtAmount(outstandingPSD.getDoubtfulDebtAmount().add(
                                    paymentScheduleDetail.getDoubtfulDebtAmount().subtract(doubtFulDebtAmount)));
                            OBDal.getInstance().save(outstandingPSD);
                        }
                        paymentScheduleDetail.setWriteoffAmount(BigDecimal.ZERO);
                        paymentScheduleDetail.getPaymentDetails().setWriteoffAmount(BigDecimal.ZERO);
                    } else {
                        paymentScheduleDetail.setWriteoffAmount(difference.add(outstandingPSD.getAmount()));
                        doubtFulDebtAmount = outstandingPSD.getDoubtfulDebtAmount()
                                .add(paymentScheduleDetail.getDoubtfulDebtAmount());
                        OBDal.getInstance().save(paymentScheduleDetail);
                        paymentScheduleDetail.getPaymentDetails()
                                .setWriteoffAmount(difference.add(outstandingPSD.getAmount()));
                        OBDal.getInstance().save(paymentScheduleDetail.getPaymentDetails());
                        OBDal.getInstance().remove(outstandingPSD);
                    }
                }
                paymentScheduleDetail.setAmount(paymentDetailAmount);
                paymentScheduleDetail.setDoubtfulDebtAmount(doubtFulDebtAmount);
                OBDal.getInstance().save(paymentScheduleDetail);
                paymentScheduleDetail.getPaymentDetails().setAmount(paymentDetailAmount);
                OBDal.getInstance().save(paymentScheduleDetail.getPaymentDetails());
            } else if (isWriteoff) {
                // The amount of the Payment Detail has not changed but the outstanding amount is written
                // off.
                // If any outstanding psd is found it is deleted and the payment schedule detail is
                // updated.
                List<FIN_PaymentScheduleDetail> outStandingPSDs = getOutstandingPSDs(paymentScheduleDetail);
                BigDecimal writeOffAmt = BigDecimal.ZERO;
                BigDecimal doubtfulAmt = BigDecimal.ZERO;
                for (FIN_PaymentScheduleDetail outstandingPSD : outStandingPSDs) {
                    writeOffAmt = writeOffAmt.add(outstandingPSD.getAmount());
                    doubtfulAmt = doubtfulAmt.add(outstandingPSD.getDoubtfulDebtAmount());
                    OBDal.getInstance().remove(outstandingPSD);
                }
                paymentScheduleDetail.setWriteoffAmount(writeOffAmt);
                paymentScheduleDetail
                        .setDoubtfulDebtAmount(paymentScheduleDetail.getDoubtfulDebtAmount().add(doubtfulAmt));
                OBDal.getInstance().save(paymentScheduleDetail);
                paymentScheduleDetail.getPaymentDetails().setWriteoffAmount(writeOffAmt);
                OBDal.getInstance().save(paymentScheduleDetail.getPaymentDetails());
            }
        } else {
            // If detail to be added is zero amount, skip it
            if (paymentDetailAmount.signum() == 0 && !isWriteoff) {
                return BigDecimal.ZERO;
            }
            dao = new AdvPaymentMngtDao();

            BigDecimal amountDifference = paymentScheduleDetail.getAmount().subtract(paymentDetailAmount);
            // Debt Payment
            BigDecimal doubtfulDebtAmount = getDoubtFulDebtAmount(
                    paymentScheduleDetail.getAmount().add(paymentScheduleDetail.getWriteoffAmount()),
                    paymentDetailAmount, paymentScheduleDetail.getDoubtfulDebtAmount());
            if (amountDifference.signum() != 0) {
                if (!isWriteoff) {
                    dao.duplicateScheduleDetail(paymentScheduleDetail, amountDifference,
                            paymentScheduleDetail.getDoubtfulDebtAmount().subtract(doubtfulDebtAmount));
                    amountDifference = BigDecimal.ZERO;
                } else {
                    doubtfulDebtAmount = paymentScheduleDetail.getDoubtfulDebtAmount();
                    paymentScheduleDetail.setWriteoffAmount(amountDifference);
                }
                paymentScheduleDetail.setAmount(paymentDetailAmount);
                paymentScheduleDetail.setDoubtfulDebtAmount(doubtfulDebtAmount);
                OBDal.getInstance().save(paymentScheduleDetail);
            }
            dao.getNewPaymentDetail(payment, paymentScheduleDetail, paymentDetailAmount, amountDifference, false,
                    null);
        }
        return assignedAmount;
    }

    public static FIN_Payment setFinancialTransactionAmountAndRate(VariablesSecureApp vars, FIN_Payment payment,
            BigDecimal _finTxnConvertRate, BigDecimal _finTxnAmount) {
        if (payment == null) {
            return payment;
        }

        BigDecimal paymentAmount = payment.getAmount();
        if (paymentAmount == null) {
            paymentAmount = BigDecimal.ZERO;
        }
        BigDecimal finTxnConvertRate = _finTxnConvertRate;
        if (finTxnConvertRate == null || finTxnConvertRate.compareTo(BigDecimal.ZERO) <= 0) {
            finTxnConvertRate = BigDecimal.ONE;
        }
        BigDecimal finTxnAmount = _finTxnAmount;
        if (finTxnAmount == null || finTxnAmount.compareTo(BigDecimal.ZERO) == 0) {
            finTxnAmount = paymentAmount.multiply(finTxnConvertRate);
        } else if (paymentAmount.compareTo(BigDecimal.ZERO) != 0) {
            // Correct exchange rate for rounding that occurs in UI
            finTxnConvertRate = finTxnAmount.divide(paymentAmount, MathContext.DECIMAL64);
            if (vars != null) {
                DecimalFormat generalQtyRelationFmt = Utility.getFormat(vars, "generalQtyEdition");
                finTxnConvertRate = finTxnConvertRate.setScale(generalQtyRelationFmt.getMaximumFractionDigits(),
                        BigDecimal.ROUND_HALF_UP);
            }
        }

        payment.setFinancialTransactionAmount(finTxnAmount);
        payment.setFinancialTransactionConvertRate(finTxnConvertRate);

        return payment;
    }

    public static FIN_Payment setFinancialTransactionAmountAndRate(FIN_Payment payment,
            BigDecimal finTxnConvertRate, BigDecimal finTxnAmount) {
        return setFinancialTransactionAmountAndRate(null, payment, finTxnConvertRate, finTxnAmount);
    }

    public static FIN_Payment createRefundPayment(ConnectionProvider conProvider, VariablesSecureApp vars,
            FIN_Payment payment, BigDecimal refundAmount) {
        return createRefundPayment(conProvider, vars, payment, refundAmount, null);
    }

    public static FIN_Payment createRefundPayment(ConnectionProvider conProvider, VariablesSecureApp vars,
            FIN_Payment payment, BigDecimal refundAmount, BigDecimal conversionRate) {
        dao = new AdvPaymentMngtDao();
        FIN_Payment refundPayment;
        if (payment.getFINPaymentDetailList().isEmpty())
            refundPayment = payment;
        else {
            refundPayment = (FIN_Payment) DalUtil.copy(payment, false);
            String strDescription = Utility.messageBD(conProvider, "APRM_RefundPayment", vars.getLanguage());
            strDescription += ": " + payment.getDocumentNo();
            refundPayment.setDescription(strDescription);
            refundPayment.setGeneratedCredit(BigDecimal.ZERO);
            final String strDocumentNo = FIN_Utility.getDocumentNo(payment.getOrganization(),
                    payment.getDocumentType().getDocumentCategory(), "DocumentNo_FIN_Payment");
            refundPayment.setDocumentNo(strDocumentNo);
        }
        refundPayment.setProcessed(false);
        refundPayment.setStatus("RPAP");
        OBDal.getInstance().save(refundPayment);
        OBDal.getInstance().flush();
        refundPayment.setAmount(refundAmount);
        refundPayment.setUsedCredit(refundAmount.negate());

        setFinancialTransactionAmountAndRate(refundPayment, conversionRate, null);

        FIN_PaymentScheduleDetail refundScheduleDetail = dao.getNewPaymentScheduleDetail(payment.getOrganization(),
                refundAmount);
        dao.getNewPaymentDetail(refundPayment, refundScheduleDetail, refundAmount, BigDecimal.ZERO, true, null);

        return refundPayment;
    }

    /**
     * Adds new Details to the given Payment Proposal based on the List of Payment Schedule Details.
     * 
     * @param paymentProposal
     *          FIN_PaymentProposal where new Details are added.
     * @param paymentAmount
     *          Total amount to be paid.
     * @param selectedPaymentScheduleDetails
     *          List of FIN_PaymentScheduleDetail that needs to be added to the Payment Proposal.
     * @param selectedPaymentScheduleDetailAmounts
     *          HashMap with the Amount to be paid for each Scheduled Payment Detail.
     * @param writeOffAmt
     *          Total amount to be written off.
     */
    public static void savePaymentProposal(FIN_PaymentProposal paymentProposal, BigDecimal paymentAmount,
            List<FIN_PaymentScheduleDetail> selectedPaymentScheduleDetails,
            HashMap<String, BigDecimal> selectedPaymentScheduleDetailAmounts, BigDecimal writeOffAmt) {
        dao = new AdvPaymentMngtDao();
        paymentProposal.setAmount(paymentAmount);
        paymentProposal.setWriteoffAmount((writeOffAmt != null) ? writeOffAmt : BigDecimal.ZERO);
        BigDecimal convertRate = paymentProposal.getFinancialTransactionConvertRate();
        if (BigDecimal.ONE.equals(convertRate)) {
            paymentProposal.setFinancialTransactionAmount(paymentAmount);
        } else {
            Currency finAccountCurrency = paymentProposal.getAccount().getCurrency();
            BigDecimal finAccountTxnAmount = paymentAmount.multiply(convertRate);
            long faPrecision = finAccountCurrency.getStandardPrecision();
            finAccountTxnAmount = finAccountTxnAmount.setScale((int) faPrecision, RoundingMode.HALF_UP);

            paymentProposal.setFinancialTransactionAmount(finAccountTxnAmount);
        }

        for (FIN_PaymentScheduleDetail paymentScheduleDetail : selectedPaymentScheduleDetails) {
            BigDecimal detailWriteOffAmt = null;
            if (writeOffAmt != null)
                detailWriteOffAmt = paymentScheduleDetail.getAmount()
                        .subtract(selectedPaymentScheduleDetailAmounts.get(paymentScheduleDetail.getId()));

            dao.getNewPaymentProposalDetail(paymentProposal.getOrganization(), paymentProposal,
                    paymentScheduleDetail, selectedPaymentScheduleDetailAmounts.get(paymentScheduleDetail.getId()),
                    detailWriteOffAmt, null);
        }
    }

    /**
     * It adds to the Payment a new Payment Detail with the given GL Item and amount.
     * 
     * @param payment
     *          Payment where the new Payment Detail needs to be added.
     * @param glitemAmount
     *          Amount of the new Payment Detail.
     * @param glitem
     *          GLItem to be set in the new Payment Detail.
     */
    public static void saveGLItem(FIN_Payment payment, BigDecimal glitemAmount, GLItem glitem) {
        // FIXME: added to access the FIN_PaymentSchedule and FIN_PaymentScheduleDetail tables to be
        // removed when new security implementation is done
        dao = new AdvPaymentMngtDao();
        OBContext.setAdminMode();
        try {
            FIN_PaymentScheduleDetail psd = dao.getNewPaymentScheduleDetail(payment.getOrganization(),
                    glitemAmount);
            FIN_PaymentDetail pd = dao.getNewPaymentDetail(payment, psd, glitemAmount, BigDecimal.ZERO, false,
                    glitem);
            pd.setFinPayment(payment);
            OBDal.getInstance().save(pd);
            OBDal.getInstance().save(payment);
        } finally {
            OBContext.restorePreviousMode();
        }
    }

    /**
     * It adds to the Payment a new Payment Detail with the given GL Item, amount and accounting
     * dimensions
     * 
     * @param payment
     *          Payment where the new Payment Detail needs to be added.
     * @param glitemAmount
     *          Amount of the new Payment Detail.
     * @param glitem
     *          GLItem to be set in the new Payment Detail.
     * @param businessPartner
     *          accounting dimension
     * @param product
     *          accounting dimension
     * @param project
     *          accounting dimension
     * @param campaign
     *          accounting dimension
     * @param activity
     *          accounting dimension
     * @param salesRegion
     *          accounting dimension
     * @param costCenter
     *          accounting dimension
     * @param user1
     *          accounting dimension
     * @param user2
     *          accounting dimension
     */
    public static void saveGLItem(FIN_Payment payment, BigDecimal glitemAmount, GLItem glitem,
            BusinessPartner businessPartner, Product product, Project project, Campaign campaign,
            ABCActivity activity, SalesRegion salesRegion, Costcenter costCenter, UserDimension1 user1,
            UserDimension2 user2) {
        // FIXME: added to access the FIN_PaymentSchedule and FIN_PaymentScheduleDetail tables to be
        // removed when new security implementation is done
        dao = new AdvPaymentMngtDao();
        OBContext.setAdminMode();
        try {
            FIN_PaymentScheduleDetail psd = dao.getNewPaymentScheduleDetail(payment.getOrganization(), glitemAmount,
                    businessPartner, product, project, campaign, activity, salesRegion, costCenter, user1, user2);
            FIN_PaymentDetail pd = dao.getNewPaymentDetail(payment, psd, glitemAmount, BigDecimal.ZERO, false,
                    glitem);
            pd.setFinPayment(payment);
            OBDal.getInstance().save(pd);
            OBDal.getInstance().save(payment);
        } finally {
            OBContext.restorePreviousMode();
        }
    }

    /**
     * It adds to the Payment a new Payment Detail with the given GL Item, amount and accounting
     * dimensions
     * 
     * @param payment
     *          Payment where the new Payment Detail needs to be added.
     * @param glitemAmount
     *          Amount of the new Payment Detail.
     * @param glitem
     *          GLItem to be set in the new Payment Detail.
     * @param businessPartner
     *          accounting dimension
     * @param product
     *          accounting dimension
     * @param project
     *          accounting dimension
     * @param campaign
     *          accounting dimension
     * @param activity
     *          accounting dimension
     * @param salesRegion
     *          accounting dimension
     */
    public static void saveGLItem(FIN_Payment payment, BigDecimal glitemAmount, GLItem glitem,
            BusinessPartner businessPartner, Product product, Project project, Campaign campaign,
            ABCActivity activity, SalesRegion salesRegion) {
        saveGLItem(payment, glitemAmount, glitem, businessPartner, product, project, campaign, activity,
                salesRegion, null, null, null);
    }

    /**
     * Removes the Payment Detail from the Payment when the Detail is related to a GLItem
     * 
     * @param payment
     *          FIN_Payment that contains the Payment Detail.
     * @param paymentDetail
     *          FIN_PaymentDetail to be removed.
     */
    public static void removeGLItem(FIN_Payment payment, FIN_PaymentDetail paymentDetail) {
        // FIXME: added to access the FIN_PaymentSchedule and FIN_PaymentScheduleDetail tables to be
        // removed when new security implementation is done
        dao = new AdvPaymentMngtDao();
        OBContext.setAdminMode();
        try {
            List<FIN_PaymentDetail> pdl = payment.getFINPaymentDetailList();
            if (paymentDetail != null) {
                pdl.remove(paymentDetail);
                OBDal.getInstance().remove(paymentDetail);
            } else {
                List<String> pdlIDs = new ArrayList<String>();
                for (FIN_PaymentDetail deletePaymentDetail : pdl)
                    pdlIDs.add(deletePaymentDetail.getId());

                for (String pdlID : pdlIDs) {
                    pdl.remove(dao.getObject(FIN_PaymentDetail.class, pdlID));
                    OBDal.getInstance().remove(dao.getObject(FIN_PaymentDetail.class, pdlID));
                }
            }
            payment.setFINPaymentDetailList(pdl);
            OBDal.getInstance().save(payment);
            OBDal.getInstance().flush();
        } finally {
            OBContext.restorePreviousMode();
        }
    }

    /**
     * It adds to the scheduledPaymentDetails List the FIN_PaymentScheduleDetails given in the
     * strSelectedPaymentDetailsIds comma separated String of Id's that are not yet included on it.
     * 
     * @param scheduledPaymentDetails
     *          List of FIN_PaymentScheduleDetail.
     * @param strSelectedPaymentDetailsIds
     *          String of comma separated id's that needs to be included in the List if they are not
     *          present.
     * @return returns a List of FIN_PaymentScheduleDetail including all the Payment Schedule Details.
     */
    public static List<FIN_PaymentScheduleDetail> getSelectedPaymentDetails(
            List<FIN_PaymentScheduleDetail> scheduledPaymentDetails, String strSelectedPaymentDetailsIds) {
        final List<FIN_PaymentScheduleDetail> selectedScheduledPaymentDetails;
        if (scheduledPaymentDetails == null)
            selectedScheduledPaymentDetails = new ArrayList<FIN_PaymentScheduleDetail>();
        else
            selectedScheduledPaymentDetails = scheduledPaymentDetails;
        // FIXME: added to access the FIN_PaymentSchedule and FIN_PaymentScheduleDetail tables to be
        // removed when new security implementation is done
        OBContext.setAdminMode();
        try {
            // selected scheduled payments list
            final List<FIN_PaymentScheduleDetail> tempSelectedScheduledPaymentDetails = FIN_Utility
                    .getOBObjectList(FIN_PaymentScheduleDetail.class, strSelectedPaymentDetailsIds);
            for (FIN_PaymentScheduleDetail tempPaymentScheduleDetail : tempSelectedScheduledPaymentDetails) {
                if (!selectedScheduledPaymentDetails.contains(tempPaymentScheduleDetail))
                    selectedScheduledPaymentDetails.add(tempPaymentScheduleDetail);

            }
        } finally {
            OBContext.restorePreviousMode();
        }
        return selectedScheduledPaymentDetails;
    }

    /**
     * Creates a HashMap with the FIN_PaymentScheduleDetail id's and the amount gotten from the
     * Session.
     * 
     * The amounts are stored in Session like "inpPaymentAmount"+paymentScheduleDetail.Id
     * 
     * @param vars
     *          VariablseSecureApp with the session data.
     * @param selectedPaymentScheduleDetails
     *          List of FIN_PaymentScheduleDetails that need to be included in the HashMap.
     * @return A HashMap mapping the FIN_PaymentScheduleDetail's Id with the corresponding amount.
     */
    public static HashMap<String, BigDecimal> getSelectedPaymentDetailsAndAmount(VariablesSecureApp vars,
            List<FIN_PaymentScheduleDetail> selectedPaymentScheduleDetails) throws ServletException {
        return getSelectedBaseOBObjectAmount(vars, selectedPaymentScheduleDetails, "inpPaymentAmount");
    }

    /**
     * Creates a HashMap with the BaseOBObject id's and the amount gotten from the Session.
     * 
     * The amounts are stored in Session like "htmlElementId"+basobObject.Id
     * 
     * @param vars
     *          VariablseSecureApp with the session data.
     * @param selectedBaseOBObjects
     *          List of bobs that need to be included in the HashMap.
     * @return A HashMap mapping the Id with the corresponding amount.
     */
    public static <T extends BaseOBObject> HashMap<String, BigDecimal> getSelectedBaseOBObjectAmount(
            VariablesSecureApp vars, List<T> selectedBaseOBObjects, String htmlElementId) throws ServletException {
        HashMap<String, BigDecimal> selectedBaseOBObjectAmounts = new HashMap<String, BigDecimal>();

        for (final T o : selectedBaseOBObjects) {
            selectedBaseOBObjectAmounts.put((String) o.getId(),
                    new BigDecimal(vars.getNumericParameter(htmlElementId + (String) o.getId(), "")));
        }
        return selectedBaseOBObjectAmounts;
    }

    private static List<FIN_PaymentScheduleDetail> getOrderedPaymentScheduleDetails(Set<String> psdSet) {
        OBCriteria<FIN_PaymentScheduleDetail> orderedPSDs = OBDal.getInstance()
                .createCriteria(FIN_PaymentScheduleDetail.class);
        orderedPSDs.add(Restrictions.in(FIN_PaymentScheduleDetail.PROPERTY_ID, psdSet));
        orderedPSDs.addOrderBy(FIN_PaymentScheduleDetail.PROPERTY_AMOUNT, true);
        return orderedPSDs.list();
    }

    private static HashMap<String, BigDecimal> calculateAmounts(BigDecimal recordAmount, Set<String> psdSet) {
        BigDecimal remainingAmount = recordAmount;
        HashMap<String, BigDecimal> recordsAmounts = new HashMap<String, BigDecimal>();
        // PSD needs to be properly ordered to ensure negative amounts are processed first
        List<FIN_PaymentScheduleDetail> psds = getOrderedPaymentScheduleDetails(psdSet);
        for (FIN_PaymentScheduleDetail paymentScheduleDetail : psds) {
            BigDecimal outstandingAmount = paymentScheduleDetail.getAmount();
            // Manage negative amounts
            if ((remainingAmount.compareTo(BigDecimal.ZERO) > 0
                    && remainingAmount.compareTo(outstandingAmount) >= 0)
                    || ((remainingAmount.compareTo(BigDecimal.ZERO) == -1
                            && outstandingAmount.compareTo(BigDecimal.ZERO) == -1)
                            && (remainingAmount.compareTo(outstandingAmount) <= 0))) {
                recordsAmounts.put(paymentScheduleDetail.getId(), outstandingAmount);
                remainingAmount = remainingAmount.subtract(outstandingAmount);
            } else {
                recordsAmounts.put(paymentScheduleDetail.getId(), remainingAmount);
                if (psdSet.size() > 0) {
                    remainingAmount = BigDecimal.ZERO;
                }
            }

        }
        return recordsAmounts;
    }

    /**
     * Builds a FieldProvider with a set of Payment Schedule Details based on the
     * selectedScheduledPaymentDetails and filteredScheduledPaymentDetails Lists. When the firstLoad
     * parameter is true the "paymentAmount" field is loaded from the corresponding Payment Proposal
     * Detail if it exists, when false it gets the amount from session.
     * 
     * @param vars
     *          VariablesSecureApp containing the Session data.
     * @param selectedScheduledPaymentDetails
     *          List of FIN_PaymentScheduleDetails that need to be selected by default.
     * @param filteredScheduledPaymentDetails
     *          List of FIN_PaymentScheduleDetails that need to be unselected by default.
     * @param firstLoad
     *          Boolean to set if the PaymentAmount is gotten from the PaymentProposal (true) or from
     *          Session (false)
     * @param paymentProposal
     *          PaymentProposal used to get the amount when firstLoad is true.
     * @return a FieldProvider object with all the given FIN_PaymentScheduleDetails.
     */
    public static FieldProvider[] getShownScheduledPaymentDetails(VariablesSecureApp vars,
            List<FIN_PaymentScheduleDetail> selectedScheduledPaymentDetails,
            List<FIN_PaymentScheduleDetail> filteredScheduledPaymentDetails, boolean firstLoad,
            FIN_PaymentProposal paymentProposal) throws ServletException {
        return getShownScheduledPaymentDetails(vars, selectedScheduledPaymentDetails,
                filteredScheduledPaymentDetails, firstLoad, paymentProposal, null);
    }

    public static FieldProvider[] getShownScheduledPaymentDetails(VariablesSecureApp vars,
            List<FIN_PaymentScheduleDetail> selectedScheduledPaymentDetails,
            List<FIN_PaymentScheduleDetail> filteredScheduledPaymentDetails, boolean firstLoad,
            FIN_PaymentProposal paymentProposal, String strSelectedPaymentDetails) throws ServletException {
        return getShownScheduledPaymentDetails(vars, selectedScheduledPaymentDetails,
                filteredScheduledPaymentDetails, firstLoad, paymentProposal, strSelectedPaymentDetails, false);
    }

    public static FieldProvider[] getShownScheduledPaymentDetails(VariablesSecureApp vars,
            List<FIN_PaymentScheduleDetail> selectedScheduledPaymentDetails,
            List<FIN_PaymentScheduleDetail> filteredScheduledPaymentDetails, boolean firstLoad,
            FIN_PaymentProposal paymentProposal, String strSelectedPaymentDetails, boolean showDoubtfulDebtAmount)
            throws ServletException {

        String strSelectedRecords = "";
        if (!"".equals(strSelectedPaymentDetails) && strSelectedPaymentDetails != null) {
            strSelectedRecords = strSelectedPaymentDetails;
            strSelectedRecords = strSelectedRecords.replace("(", "");
            strSelectedRecords = strSelectedRecords.replace(")", "");
        }
        dao = new AdvPaymentMngtDao();
        final List<FIN_PaymentScheduleDetail> shownScheduledPaymentDetails = new ArrayList<FIN_PaymentScheduleDetail>();
        shownScheduledPaymentDetails.addAll(selectedScheduledPaymentDetails);
        shownScheduledPaymentDetails.addAll(filteredScheduledPaymentDetails);
        FIN_PaymentScheduleDetail[] FIN_PaymentScheduleDetails = new FIN_PaymentScheduleDetail[0];
        FIN_PaymentScheduleDetails = shownScheduledPaymentDetails.toArray(FIN_PaymentScheduleDetails);
        FieldProvider[] data = FieldProviderFactory.getFieldProviderArray(shownScheduledPaymentDetails);
        String dateFormat = OBPropertiesProvider.getInstance().getOpenbravoProperties()
                .getProperty("dateFormat.java");
        SimpleDateFormat dateFormater = new SimpleDateFormat(dateFormat);
        // FIXME: added to access the FIN_PaymentSchedule and FIN_PaymentScheduleDetail tables to be
        // removed when new security implementation is done
        OBContext.setAdminMode();
        try {
            for (int i = 0; i < data.length; i++) {
                String selectedId = (selectedScheduledPaymentDetails.contains(FIN_PaymentScheduleDetails[i]))
                        ? FIN_PaymentScheduleDetails[i].getId()
                        : "";
                // If selectedId belongs to a grouping selection calculate whether it should be selected or
                // not
                if (!"".equals(selectedId) && !"".equals(strSelectedPaymentDetails)
                        && strSelectedPaymentDetails != null) {
                    StringTokenizer records = new StringTokenizer(strSelectedRecords, "'");
                    Set<String> recordSet = new LinkedHashSet<String>();
                    while (records.hasMoreTokens()) {
                        recordSet.add(records.nextToken());
                    }
                    if (recordSet.contains(selectedId)) {
                        FieldProviderFactory.setField(data[i], "finSelectedPaymentDetailId", selectedId);
                    } else {
                        String selectedRecord = FIN_PaymentScheduleDetails[i].getId();
                        // Find record which contains psdId
                        Set<String> psdIdSet = new LinkedHashSet<String>();
                        for (String record : recordSet) {
                            if (record.contains(selectedId)) {
                                selectedRecord = record;
                                StringTokenizer st = new StringTokenizer(record, ",");
                                while (st.hasMoreTokens()) {
                                    psdIdSet.add(st.nextToken());
                                }
                            }
                        }
                        String psdAmount = vars.getNumericParameter("inpPaymentAmount" + selectedRecord, "");

                        HashMap<String, BigDecimal> idsAmounts = calculateAmounts(new BigDecimal(psdAmount),
                                psdIdSet);
                        FieldProviderFactory.setField(data[i], "finSelectedPaymentDetailId", selectedId);
                        FieldProviderFactory.setField(data[i], "paymentAmount",
                                idsAmounts.get(selectedId).toString());
                    }
                }

                FieldProviderFactory.setField(data[i], "finSelectedPaymentDetailId",
                        (selectedScheduledPaymentDetails.contains(FIN_PaymentScheduleDetails[i]))
                                ? FIN_PaymentScheduleDetails[i].getId()
                                : "");
                FieldProviderFactory.setField(data[i], "finScheduledPaymentDetailId",
                        FIN_PaymentScheduleDetails[i].getId());
                if (FIN_PaymentScheduleDetails[i].getOrderPaymentSchedule() != null) {
                    FieldProviderFactory.setField(data[i], "orderNr",
                            FIN_PaymentScheduleDetails[i].getOrderPaymentSchedule().getOrder().getDocumentNo());
                    FieldProviderFactory.setField(data[i], "orderNrTrunc",
                            FIN_PaymentScheduleDetails[i].getOrderPaymentSchedule().getOrder().getDocumentNo());
                    FieldProviderFactory.setField(data[i], "orderPaymentScheduleId",
                            FIN_PaymentScheduleDetails[i].getOrderPaymentSchedule().getId());
                } else {
                    FieldProviderFactory.setField(data[i], "orderNr", "");
                    FieldProviderFactory.setField(data[i], "orderNrTrunc", "");
                    FieldProviderFactory.setField(data[i], "orderPaymentScheduleId", "");
                }
                if (FIN_PaymentScheduleDetails[i].getInvoicePaymentSchedule() != null) {
                    FIN_PaymentSchedule psd = FIN_PaymentScheduleDetails[i].getInvoicePaymentSchedule();
                    OrganizationInformation orgInfo = OBDao.getActiveOBObjectList(psd.getOrganization(),
                            Organization.PROPERTY_ORGANIZATIONINFORMATIONLIST) != null
                                    ? (OrganizationInformation) OBDao.getActiveOBObjectList(psd.getOrganization(),
                                            Organization.PROPERTY_ORGANIZATIONINFORMATIONLIST).get(0)
                                    : null;
                    if (!psd.getInvoice().isSalesTransaction() && orgInfo != null
                            && orgInfo.getAPRMPaymentDescription().equals("Supplier Reference")) {
                        // When the Organization of the Invoice sets that the Invoice Document No. is the
                        // supplier's
                        FieldProviderFactory.setField(data[i], "invoiceNr", FIN_PaymentScheduleDetails[i]
                                .getInvoicePaymentSchedule().getInvoice().getOrderReference());
                        FieldProviderFactory.setField(data[i], "invoiceNrTrunc", FIN_PaymentScheduleDetails[i]
                                .getInvoicePaymentSchedule().getInvoice().getOrderReference());
                    } else {
                        // When the Organization of the Invoice sets that the Invoice Document No. is the
                        // default
                        // Invoice Number
                        FieldProviderFactory.setField(data[i], "invoiceNr", FIN_PaymentScheduleDetails[i]
                                .getInvoicePaymentSchedule().getInvoice().getDocumentNo());
                        FieldProviderFactory.setField(data[i], "invoiceNrTrunc", FIN_PaymentScheduleDetails[i]
                                .getInvoicePaymentSchedule().getInvoice().getDocumentNo());
                    }
                    FieldProviderFactory.setField(data[i], "invoicePaymentScheduleId",
                            FIN_PaymentScheduleDetails[i].getInvoicePaymentSchedule().getId());
                } else {
                    FieldProviderFactory.setField(data[i], "invoiceNr", "");
                    FieldProviderFactory.setField(data[i], "invoiceNrTrunc", "");
                    FieldProviderFactory.setField(data[i], "invoicePaymentScheduleId", "");
                }
                if (FIN_PaymentScheduleDetails[i].getInvoicePaymentSchedule() != null) {
                    FieldProviderFactory.setField(data[i], "expectedDate",
                            dateFormater.format(
                                    FIN_PaymentScheduleDetails[i].getInvoicePaymentSchedule().getExpectedDate())
                                    .toString());
                    FieldProviderFactory.setField(data[i], "dueDate",
                            dateFormater
                                    .format(FIN_PaymentScheduleDetails[i].getInvoicePaymentSchedule().getDueDate())
                                    .toString());
                    FieldProviderFactory.setField(data[i], "transactionDate", dateFormater.format(
                            FIN_PaymentScheduleDetails[i].getInvoicePaymentSchedule().getInvoice().getInvoiceDate())
                            .toString());
                    FieldProviderFactory.setField(data[i], "invoicedAmount", FIN_PaymentScheduleDetails[i]
                            .getInvoicePaymentSchedule().getInvoice().getGrandTotalAmount().toString());
                    FieldProviderFactory.setField(data[i], "expectedAmount",
                            FIN_PaymentScheduleDetails[i].getInvoicePaymentSchedule().getAmount().toString());

                    // Truncate Business Partner
                    String businessPartner = FIN_PaymentScheduleDetails[i].getInvoicePaymentSchedule().getInvoice()
                            .getBusinessPartner().getIdentifier();
                    FieldProviderFactory.setField(data[i], "businessPartnerId", FIN_PaymentScheduleDetails[i]
                            .getInvoicePaymentSchedule().getInvoice().getBusinessPartner().getId());
                    String truncateBusinessPartner = (businessPartner.length() > 18)
                            ? businessPartner.substring(0, 15).concat("...").toString()
                            : businessPartner;
                    FieldProviderFactory.setField(data[i], "businessPartnerName",
                            (businessPartner.length() > 18) ? businessPartner : "");
                    FieldProviderFactory.setField(data[i], "businessPartnerNameTrunc", truncateBusinessPartner);

                    // Truncate Payment Method
                    String paymentMethodName = FIN_PaymentScheduleDetails[i].getInvoicePaymentSchedule()
                            .getFinPaymentmethod().getName();
                    String truncatePaymentMethodName = (paymentMethodName.length() > 18)
                            ? paymentMethodName.substring(0, 15).concat("...").toString()
                            : paymentMethodName;
                    FieldProviderFactory.setField(data[i], "paymentMethodName",
                            (paymentMethodName.length() > 18) ? paymentMethodName : "");
                    FieldProviderFactory.setField(data[i], "paymentMethodNameTrunc", truncatePaymentMethodName);

                    if (FIN_PaymentScheduleDetails[i].getInvoicePaymentSchedule().getFINPaymentPriority() != null) {
                        FieldProviderFactory.setField(data[i], "gridLineColor", FIN_PaymentScheduleDetails[i]
                                .getInvoicePaymentSchedule().getFINPaymentPriority().getColor());
                    }
                } else {
                    FieldProviderFactory.setField(data[i], "expectedDate",
                            dateFormater.format(
                                    FIN_PaymentScheduleDetails[i].getOrderPaymentSchedule().getExpectedDate())
                                    .toString());
                    FieldProviderFactory.setField(data[i], "dueDate",
                            dateFormater
                                    .format(FIN_PaymentScheduleDetails[i].getOrderPaymentSchedule().getDueDate())
                                    .toString());
                    FieldProviderFactory.setField(data[i], "transactionDate", dateFormater.format(
                            FIN_PaymentScheduleDetails[i].getOrderPaymentSchedule().getOrder().getOrderDate())
                            .toString());
                    FieldProviderFactory.setField(data[i], "invoicedAmount", "");
                    FieldProviderFactory.setField(data[i], "expectedAmount",
                            FIN_PaymentScheduleDetails[i].getOrderPaymentSchedule().getAmount().toString());

                    // Truncate Business Partner
                    String businessPartner = FIN_PaymentScheduleDetails[i].getOrderPaymentSchedule().getOrder()
                            .getBusinessPartner().getIdentifier();
                    FieldProviderFactory.setField(data[i], "businessPartnerId", FIN_PaymentScheduleDetails[i]
                            .getOrderPaymentSchedule().getOrder().getBusinessPartner().getId());
                    String truncateBusinessPartner = (businessPartner.length() > 18)
                            ? businessPartner.substring(0, 15).concat("...").toString()
                            : businessPartner;
                    FieldProviderFactory.setField(data[i], "businessPartnerName",
                            (businessPartner.length() > 18) ? businessPartner : "");
                    FieldProviderFactory.setField(data[i], "businessPartnerNameTrunc", truncateBusinessPartner);

                    // Truncate Payment Method
                    String paymentMethodName = FIN_PaymentScheduleDetails[i].getOrderPaymentSchedule()
                            .getFinPaymentmethod().getName();
                    String truncatePaymentMethodName = (paymentMethodName.length() > 18)
                            ? paymentMethodName.substring(0, 15).concat("...").toString()
                            : paymentMethodName;
                    FieldProviderFactory.setField(data[i], "paymentMethodName",
                            (paymentMethodName.length() > 18) ? paymentMethodName : "");
                    FieldProviderFactory.setField(data[i], "paymentMethodNameTrunc", truncatePaymentMethodName);

                    if (FIN_PaymentScheduleDetails[i].getOrderPaymentSchedule().getFINPaymentPriority() != null) {
                        FieldProviderFactory.setField(data[i], "gridLineColor", FIN_PaymentScheduleDetails[i]
                                .getOrderPaymentSchedule().getFINPaymentPriority().getColor());
                    }
                }
                FieldProviderFactory.setField(data[i], "outstandingAmount",
                        FIN_PaymentScheduleDetails[i].getAmount().toString());
                FieldProviderFactory.setField(data[i], "doubtfulDebtAmount",
                        FIN_PaymentScheduleDetails[i].getDoubtfulDebtAmount().toString());
                FieldProviderFactory.setField(data[i], "displayDoubtfulDebt",
                        showDoubtfulDebtAmount ? "" : "display: none;");

                String strPaymentAmt = "";
                String strDifference = "";
                if (firstLoad && (selectedScheduledPaymentDetails.contains(FIN_PaymentScheduleDetails[i]))
                        && paymentProposal != null) {
                    strPaymentAmt = dao.getPaymentProposalDetailAmount(FIN_PaymentScheduleDetails[i],
                            paymentProposal);
                } else {
                    strPaymentAmt = vars
                            .getNumericParameter("inpPaymentAmount" + FIN_PaymentScheduleDetails[i].getId(), "");
                }
                if (!"".equals(strPaymentAmt)) {
                    strDifference = FIN_PaymentScheduleDetails[i].getAmount()
                            .subtract(new BigDecimal(strPaymentAmt)).toString();
                }
                if (data[i].getField("paymentAmount") == null || "".equals(data[i].getField("paymentAmount"))) {
                    FieldProviderFactory.setField(data[i], "paymentAmount", strPaymentAmt);
                }
                FieldProviderFactory.setField(data[i], "difference", strDifference);
                FieldProviderFactory.setField(data[i], "rownum", String.valueOf(i));

            }
        } finally {
            OBContext.restorePreviousMode();
        }
        return data;
    }

    /**
     * Returns a List of FIN_PaymentScheduleDetails related to the Proposal Details of the given
     * Payment Proposal.
     * 
     * @param paymentProposal
     */
    public static List<FIN_PaymentScheduleDetail> getSelectedPendingPaymentsFromProposal(
            FIN_PaymentProposal paymentProposal) {
        List<FIN_PaymentScheduleDetail> existingPaymentScheduleDetail = new ArrayList<FIN_PaymentScheduleDetail>();
        for (FIN_PaymentPropDetail proposalDetail : paymentProposal.getFINPaymentPropDetailList())
            existingPaymentScheduleDetail.add(proposalDetail.getFINPaymentScheduledetail());

        return existingPaymentScheduleDetail;
    }

    /**
     * This method groups several payment schedule details by {PaymentDetails, OrderPaymenSchedule,
     * InvoicePaymentSchedule}.
     * 
     * @param psd
     *          Payment Schedule Detail base. The amount will be updated here.
     */
    public static void mergePaymentScheduleDetails(FIN_PaymentScheduleDetail psd) {
        // FIXME: added to access the FIN_PaymentSchedule and FIN_PaymentScheduleDetail tables to be
        // removed when new security implementation is done

        OBContext.setAdminMode();
        try {
            OBCriteria<FIN_PaymentScheduleDetail> psdFilter = OBDal.getInstance()
                    .createCriteria(FIN_PaymentScheduleDetail.class);
            psdFilter.add(Restrictions.eq(FIN_PaymentScheduleDetail.PROPERTY_CLIENT, psd.getClient()));
            psdFilter.add(Restrictions.eq(FIN_PaymentScheduleDetail.PROPERTY_ORGANIZATION, psd.getOrganization()));
            psdFilter.add(Restrictions.isNull(FIN_PaymentScheduleDetail.PROPERTY_PAYMENTDETAILS));
            if (psd.getOrderPaymentSchedule() == null) {
                psdFilter.add(Restrictions.isNull(FIN_PaymentScheduleDetail.PROPERTY_ORDERPAYMENTSCHEDULE));
            } else {
                psdFilter.add(Restrictions.eq(FIN_PaymentScheduleDetail.PROPERTY_ORDERPAYMENTSCHEDULE,
                        psd.getOrderPaymentSchedule()));
            }
            if (psd.getInvoicePaymentSchedule() == null) {
                psdFilter.add(Restrictions.isNull(FIN_PaymentScheduleDetail.PROPERTY_INVOICEPAYMENTSCHEDULE));
            } else {
                psdFilter.add(Restrictions.eq(FIN_PaymentScheduleDetail.PROPERTY_INVOICEPAYMENTSCHEDULE,
                        psd.getInvoicePaymentSchedule()));
            }

            // Update amount and remove payment schedule detail
            final List<String> removedPDSIds = new ArrayList<String>();
            for (FIN_PaymentScheduleDetail psdToRemove : psdFilter.list()) {
                psd.setAmount(psd.getAmount().add(psdToRemove.getAmount()));
                psd.setDoubtfulDebtAmount(psd.getDoubtfulDebtAmount().add(psdToRemove.getDoubtfulDebtAmount()));
                // TODO: Set 0 as default value for writeoffamt column in FIN_Payment_ScheduleDetail table
                BigDecimal sum1 = (psd.getWriteoffAmount() == null) ? BigDecimal.ZERO : psd.getWriteoffAmount();
                BigDecimal sum2 = (psdToRemove.getWriteoffAmount() == null) ? BigDecimal.ZERO
                        : psdToRemove.getWriteoffAmount();
                psd.setWriteoffAmount(sum1.add(sum2));

                OBDal.getInstance().save(psdToRemove);
                removedPDSIds.add(psdToRemove.getId());
            }

            for (String pdToRm : removedPDSIds) {
                FIN_PaymentScheduleDetail psdToRemove = OBDal.getInstance().get(FIN_PaymentScheduleDetail.class,
                        pdToRm);
                if (psdToRemove.getInvoicePaymentSchedule() != null) {
                    psdToRemove.getInvoicePaymentSchedule().getFINPaymentScheduleDetailInvoicePaymentScheduleList()
                            .remove(psdToRemove);
                    OBDal.getInstance().save(psdToRemove.getInvoicePaymentSchedule());
                }
                if (psdToRemove.getOrderPaymentSchedule() != null) {
                    psdToRemove.getOrderPaymentSchedule().getFINPaymentScheduleDetailOrderPaymentScheduleList()
                            .remove(psdToRemove);
                    OBDal.getInstance().save(psdToRemove.getOrderPaymentSchedule());
                }
                OBDal.getInstance().remove(psdToRemove);
            }

            psd.setAmount(psd.getAmount()
                    .add((psd.getWriteoffAmount() == null) ? BigDecimal.ZERO : psd.getWriteoffAmount()));
            psd.setWriteoffAmount(BigDecimal.ZERO);
            psd.setPaymentDetails(null);
            OBDal.getInstance().save(psd);
            OBDal.getInstance().flush();
            OBDal.getInstance().getSession().refresh(psd);

        } catch (Exception e) {
            throw new OBException(e);
        } finally {
            OBContext.restorePreviousMode();
        }
    }

    /**
     * Update Payment Schedule amounts with the amount of the Payment Schedule Detail or Payment
     * Detail. Useful when paying orders
     * 
     * @param paymentSchedule
     *          Payment Schedule to be updated
     * @param amount
     *          Amount of the Payment Schedule Detail or Payment Detail
     * @param writeOffAmount
     *          Write off amount, null or 0 if not applicable.
     * 
     * @Deprecated This method doesn't support Cash VAT flow, so it's better to use
     *             {@link #updatePaymentDetail(FIN_PaymentScheduleDetail, FIN_Payment, BigDecimal, boolean)}
     */
    @Deprecated
    public static void updatePaymentScheduleAmounts(FIN_PaymentSchedule paymentSchedule, BigDecimal amount,
            BigDecimal writeOffAmount) {
        updatePaymentScheduleAmounts(null, paymentSchedule, amount, writeOffAmount);
    }

    /**
     * Update Payment Schedule amounts with the amount of the Payment Schedule Detail or Payment
     * Detail. Useful when paying invoices. It supports Invoices with Cash VAT, creating the records
     * into the Cash VAT management table (InvoiceTaxCashVAT)
     * 
     * @param paymentDetail
     *          payment
     * @param paymentSchedule
     *          Payment Schedule to be updated
     * @param amount
     *          Amount of the Payment Schedule Detail or Payment Detail
     * @param writeOffAmount
     *          Write off amount, null or 0 if not applicable.
     */
    public static void updatePaymentScheduleAmounts(FIN_PaymentDetail paymentDetail,
            FIN_PaymentSchedule paymentSchedule, BigDecimal amount, BigDecimal writeOffAmount) {
        paymentSchedule.setPaidAmount(paymentSchedule.getPaidAmount().add(amount));
        paymentSchedule.setOutstandingAmount(paymentSchedule.getOutstandingAmount().subtract(amount));
        if (writeOffAmount != null && writeOffAmount.compareTo(BigDecimal.ZERO) != 0) {
            paymentSchedule.setPaidAmount(paymentSchedule.getPaidAmount().add(writeOffAmount));
            paymentSchedule.setOutstandingAmount(paymentSchedule.getOutstandingAmount().subtract(writeOffAmount));
        }
        OBDal.getInstance().save(paymentSchedule);
        CashVATUtil.createInvoiceTaxCashVAT(paymentDetail, paymentSchedule, amount.add(writeOffAmount));
        if (paymentSchedule.getInvoice() != null) {
            updateInvoicePaymentMonitor(paymentSchedule, amount, writeOffAmount);
        }
    }

    /**
     * Method used to update the payment monitor based on the payment made by the user.
     * 
     * @param invoicePaymentSchedule
     * @param amount
     *          Amount of the transaction.
     * @param writeOffAmount
     *          Amount that has been wrote off.
     */
    private static void updateInvoicePaymentMonitor(FIN_PaymentSchedule invoicePaymentSchedule, BigDecimal amount,
            BigDecimal writeOffAmount) {
        Invoice invoice = invoicePaymentSchedule.getInvoice();
        Date dueDate = invoicePaymentSchedule.getDueDate();
        boolean isDueDateFlag = dueDate.compareTo(new Date()) <= 0;
        invoice.setTotalPaid(invoice.getTotalPaid().add(amount));
        invoice.setLastCalculatedOnDate(new Date());
        invoice.setOutstandingAmount(invoice.getOutstandingAmount().subtract(amount));
        if (isDueDateFlag)
            invoice.setDueAmount(invoice.getDueAmount().subtract(amount));
        if (writeOffAmount != null && writeOffAmount.compareTo(BigDecimal.ZERO) != 0) {
            invoice.setTotalPaid(invoice.getTotalPaid().add(writeOffAmount));
            invoice.setOutstandingAmount(invoice.getOutstandingAmount().subtract(writeOffAmount));
            if (isDueDateFlag)
                invoice.setDueAmount(invoice.getDueAmount().subtract(writeOffAmount));
        }

        if (0 == invoice.getOutstandingAmount().compareTo(BigDecimal.ZERO)) {
            Date finalSettlementDate = getFinalSettlementDate(invoice);
            // If date is null invoice amount = 0 then nothing to set
            if (finalSettlementDate != null) {
                invoice.setFinalSettlementDate(finalSettlementDate);
                invoice.setDaysSalesOutstanding(
                        FIN_Utility.getDaysBetween(invoice.getInvoiceDate(), finalSettlementDate));
            }
            invoice.setPaymentComplete(true);
        } else {
            invoice.setPaymentComplete(false);
            invoice.setFinalSettlementDate(null);
        }
        List<FIN_PaymentSchedule> paymentSchedList = invoice.getFINPaymentScheduleList();
        Date firstDueDate = null;
        for (FIN_PaymentSchedule paymentSchedule : paymentSchedList) {
            if (paymentSchedule.getOutstandingAmount().compareTo(BigDecimal.ZERO) > 0
                    && (firstDueDate == null || firstDueDate.after(paymentSchedule.getDueDate())))
                firstDueDate = paymentSchedule.getDueDate();
        }

        BigDecimal overdueAmount = calculateOverdueAmount(invoicePaymentSchedule);
        invoice.setPercentageOverdue(overdueAmount.multiply(new BigDecimal("100"))
                .divide(invoice.getGrandTotalAmount(), 2, BigDecimal.ROUND_HALF_UP).longValue());

        if (firstDueDate != null)
            invoice.setDaysTillDue(FIN_Utility.getDaysToDue(firstDueDate));
        else
            invoice.setDaysTillDue(0L);
        OBDal.getInstance().save(invoice);
    }

    private static BigDecimal calculateOverdueAmount(FIN_PaymentSchedule invoicePaymentSchedule) {
        Invoice invoice = invoicePaymentSchedule.getInvoice();
        BigDecimal overdueOriginal = BigDecimal.ZERO;
        FIN_PaymentScheduleDetail currentPSD = getLastCreatedPaymentScheduleDetail(invoicePaymentSchedule);
        for (FIN_PaymentSchedule paymentSchedule : invoice.getFINPaymentScheduleList()) {
            Date paymentDueDate = paymentSchedule.getDueDate();
            for (FIN_PaymentScheduleDetail psd : paymentSchedule
                    .getFINPaymentScheduleDetailInvoicePaymentScheduleList()) {
                if (!psd.isCanceled() && psd.getPaymentDetails() != null
                        && (psd.isInvoicePaid() || currentPSD.getId().equals(psd.getId()))) {
                    Date paymentDate = psd.getPaymentDetails().getFinPayment().getPaymentDate();
                    if (paymentDate.after(paymentDueDate)) {
                        overdueOriginal = overdueOriginal.add(psd.getAmount());
                    }
                }
            }

        }
        return overdueOriginal;
    }

    private static FIN_PaymentScheduleDetail getLastCreatedPaymentScheduleDetail(
            FIN_PaymentSchedule invoicePaymentSchedule) {
        final OBCriteria<FIN_PaymentScheduleDetail> obc = OBDal.getInstance()
                .createCriteria(FIN_PaymentScheduleDetail.class);
        OBContext.setAdminMode();
        try {
            obc.add(Restrictions.eq(FIN_PaymentScheduleDetail.PROPERTY_INVOICEPAYMENTSCHEDULE,
                    invoicePaymentSchedule));
            obc.addOrderBy(FIN_PaymentScheduleDetail.PROPERTY_CREATIONDATE, false);
            obc.setMaxResults(1);
            return (FIN_PaymentScheduleDetail) obc.uniqueResult();
        } finally {
            OBContext.restorePreviousMode();
        }

    }

    /**
     * Returns the date in which last payment for this invoice took place
     */
    private static Date getFinalSettlementDate(Invoice invoice) {
        final OBCriteria<FIN_PaymentSchedInvV> obc = OBDal.getInstance().createCriteria(FIN_PaymentSchedInvV.class);
        OBContext.setAdminMode();
        try {
            obc.add(Restrictions.eq(FIN_PaymentSchedInvV.PROPERTY_INVOICE, invoice));
            obc.setProjection(Projections.max(FIN_PaymentSchedInvV.PROPERTY_LASTPAYMENT));
            return (Date) obc.uniqueResult();
        } finally {
            OBContext.restorePreviousMode();
        }
    }

    /**
     * Returns true if a financial account transactions has to be automatically triggered after
     * payment is processed.
     * 
     * @param payment
     * @return Returns true if a financial account transactions has to be automatically triggered
     *         after payment is processed.
     */
    public static Boolean isForcedFinancialAccountTransaction(FIN_Payment payment) {
        OBCriteria<FinAccPaymentMethod> psdFilter = OBDal.getInstance().createCriteria(FinAccPaymentMethod.class);
        psdFilter.add(Restrictions.eq(FinAccPaymentMethod.PROPERTY_ACCOUNT, payment.getAccount()));
        psdFilter.add(Restrictions.eq(FinAccPaymentMethod.PROPERTY_PAYMENTMETHOD, payment.getPaymentMethod()));
        for (FinAccPaymentMethod paymentMethod : psdFilter.list()) {
            return payment.isReceipt() ? paymentMethod.isAutomaticDeposit() : paymentMethod.isAutomaticWithdrawn();
        }
        return false;
    }

    /**
     * Method used to get a list of payments identifiers associated to a payment proposal
     * 
     * @param paymentProposal
     * @return List of payment identifiers
     */
    @SuppressWarnings("unchecked")
    public static List<String> getPaymentFromPaymentProposal(FIN_PaymentProposal paymentProposal) {
        // FIXME: added to access the FIN_PaymentSchedule and FIN_PaymentScheduleDetail tables to be
        // removed when new security implementation is done
        OBContext.setAdminMode();
        try {
            StringBuilder hql = new StringBuilder();
            final Session session = OBDal.getInstance().getSession();
            hql.append("SELECT distinct(p." + FIN_Payment.PROPERTY_ID + ") ");
            hql.append("FROM " + FIN_PaymentPropDetail.TABLE_NAME + " as ppd ");
            hql.append("inner join ppd." + FIN_PaymentPropDetail.PROPERTY_FINPAYMENTSCHEDULEDETAIL + " as psd ");
            hql.append("inner join psd." + FIN_PaymentScheduleDetail.PROPERTY_PAYMENTDETAILS + " as pd ");
            hql.append("inner join pd." + FIN_PaymentDetail.PROPERTY_FINPAYMENT + " as p ");
            hql.append("WHERE ppd." + FIN_PaymentPropDetail.PROPERTY_FINPAYMENTPROPOSAL + "."
                    + FIN_PaymentProposal.PROPERTY_ID + "= ?");
            final Query obqPay = session.createQuery(hql.toString());
            obqPay.setParameter(0, paymentProposal.getId());

            return obqPay.list();

        } catch (Exception e) {
            throw new OBException(e);
        } finally {
            OBContext.restorePreviousMode();
        }
    }

    /**
     * It calls the PAyment Process for the given payment and action.
     * 
     * @param vars
     *          VariablesSecureApp with the session data.
     * @param conn
     *          ConnectionProvider with the connection being used.
     * @param strAction
     *          String with the action of the process. {P, D, R}
     * @param payment
     *          FIN_Payment that needs to be processed.
     * @return a OBError with the result message of the process.
     * @throws Exception
     */
    public static OBError processPayment(VariablesSecureApp vars, ConnectionProvider conn, String strAction,
            FIN_Payment payment) throws Exception {
        OBError myMessage = processPayment(vars, conn, strAction, payment, null, null);
        return myMessage;
    }

    /**
     * It calls the PAyment Process for the given payment, action and origin.
     * 
     * @param vars
     *          VariablesSecureApp with the session data.
     * @param conn
     *          ConnectionProvider with the connection being used.
     * @param strAction
     *          String with the action of the process. {P, D, R}
     * @param payment
     *          FIN_Payment that needs to be processed.
     * @param comingFrom
     *          Origin where the process is invoked
     * @return a OBError with the result message of the process.
     * @throws Exception
     */
    public static OBError processPayment(VariablesSecureApp vars, ConnectionProvider conn, String strAction,
            FIN_Payment payment, String comingFrom) throws Exception {
        OBError myMessage = processPayment(vars, conn, strAction, payment, comingFrom, null);
        return myMessage;
    }

    /**
     * It calls the PAyment Process for the given payment, action and origin.
     * 
     * @param vars
     *          VariablesSecureApp with the session data.
     * @param conn
     *          ConnectionProvider with the connection being used.
     * @param strAction
     *          String with the action of the process. {P, D, R}
     * @param payment
     *          FIN_Payment that needs to be processed.
     * @param comingFrom
     *          Origin where the process is invoked
     * @param selectedCreditLineIds
     *          Id's of selected lines in Credit to Use grid
     * @return a OBError with the result message of the process.
     * @throws Exception
     */
    public static OBError processPayment(VariablesSecureApp vars, ConnectionProvider conn, String strAction,
            FIN_Payment payment, String comingFrom, String selectedCreditLineIds) throws Exception {
        ProcessBundle pb = new ProcessBundle("6255BE488882480599C81284B70CD9B3", vars).init(conn);
        HashMap<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("action", strAction);
        parameters.put("Fin_Payment_ID", payment.getId());
        parameters.put("comingFrom", comingFrom);
        parameters.put("selectedCreditLineIds", selectedCreditLineIds);
        pb.setParams(parameters);
        OBError myMessage = null;
        new FIN_PaymentProcess().execute(pb);
        myMessage = (OBError) pb.getResult();
        return myMessage;
    }

    /**
     * It calls the Payment Proposal Process for the given payment proposal and action.
     * 
     * @param vars
     *          VariablesSecureApp with the session data.
     * @param conn
     *          ConnectionProvider with the connection being used.
     * @param strProcessProposalAction
     *          String with the action of the process. {GSP, RE}
     * @param strFinPaymentProposalId
     *          String with FIN_PaymentProposal Id to be processed.
     * @return a OBError with the result message of the process.
     * @throws Exception
     */
    public static OBError processPaymentProposal(VariablesSecureApp vars, ConnectionProvider conn,
            String strProcessProposalAction, String strFinPaymentProposalId) throws Exception {
        ProcessBundle pb = new ProcessBundle("D16966FBF9604A3D91A50DC83C6EA8E3", vars).init(conn);
        HashMap<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("processProposalAction", strProcessProposalAction);
        parameters.put("Fin_Payment_Proposal_ID", strFinPaymentProposalId);
        pb.setParams(parameters);
        OBError myMessage = null;
        new FIN_PaymentProposalProcess().execute(pb);
        myMessage = (OBError) pb.getResult();
        return myMessage;
    }

    /**
     * It calls the Bank Statement Process for the given bank statement and action.
     * 
     * @param vars
     *          VariablesSecureApp with the session data.
     * @param conn
     *          ConnectionProvider with the connection being used.
     * @param strBankStatementAction
     *          String with the action of the process. {P, R}
     * @param strBankStatementId
     *          String with FIN_BankStatement Id to be processed.
     * @return a OBError with the result message of the process.
     * @throws Exception
     */
    public static OBError processBankStatement(VariablesSecureApp vars, ConnectionProvider conn,
            String strBankStatementAction, String strBankStatementId) throws Exception {
        ProcessBundle pb = new ProcessBundle("58A9261BACEF45DDA526F29D8557272D", vars).init(conn);
        HashMap<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("action", strBankStatementAction);
        parameters.put("FIN_Bankstatement_ID", strBankStatementId);
        pb.setParams(parameters);
        OBError myMessage = null;
        new FIN_BankStatementProcess().execute(pb);
        myMessage = (OBError) pb.getResult();
        return myMessage;
    }

    public static List<FIN_PaymentScheduleDetail> getOutstandingPSDs(
            FIN_PaymentScheduleDetail paymentScheduleDetail) {
        OBContext.setAdminMode();
        try {
            OBCriteria<FIN_PaymentScheduleDetail> obc = OBDal.getInstance()
                    .createCriteria(FIN_PaymentScheduleDetail.class);
            obc.add(Restrictions.isNull(FIN_PaymentScheduleDetail.PROPERTY_PAYMENTDETAILS));
            if (paymentScheduleDetail.getInvoicePaymentSchedule() != null) {
                obc.add(Restrictions.eq(FIN_PaymentScheduleDetail.PROPERTY_INVOICEPAYMENTSCHEDULE,
                        paymentScheduleDetail.getInvoicePaymentSchedule()));
            }
            if (paymentScheduleDetail.getOrderPaymentSchedule() != null) {
                obc.add(Restrictions.eq(FIN_PaymentScheduleDetail.PROPERTY_ORDERPAYMENTSCHEDULE,
                        paymentScheduleDetail.getOrderPaymentSchedule()));
            }
            return obc.list();
        } finally {
            OBContext.restorePreviousMode();
        }
    }

    public static List<FIN_PaymentScheduleDetail> getGLItemScheduleDetails(FIN_Payment payment) {
        if (payment.getFINPaymentDetailList().size() > 0) {
            OBContext.setAdminMode();
            try {
                OBCriteria<FIN_PaymentScheduleDetail> selectedGLItems = OBDal.getInstance()
                        .createCriteria(FIN_PaymentScheduleDetail.class);
                selectedGLItems.createAlias(FIN_PaymentScheduleDetail.PROPERTY_PAYMENTDETAILS, "pd");
                selectedGLItems.add(Restrictions.in(FIN_PaymentScheduleDetail.PROPERTY_PAYMENTDETAILS,
                        payment.getFINPaymentDetailList()));
                selectedGLItems.add(Restrictions.isNotNull("pd." + FIN_PaymentDetail.PROPERTY_GLITEM));
                return selectedGLItems.list();
            } finally {
                OBContext.restorePreviousMode();
            }
        } else {
            return new ArrayList<FIN_PaymentScheduleDetail>();
        }
    }

    /**
     * Calculates the resultant doubtful debt amount. Used when editing payment schedule detail amount
     * to be collected.
     * 
     * @param scheduleDetailsTotalAmount
     *          Payment Schedule Detail amount.
     * @param paymentAmount
     *          Amount selected to be collected. Always less or equal than scheduleDetailAmount.
     * @param doubtfulDebtTotalAmount
     *          Payment Schedule Detail doubtFulDebt amount.
     * @return resultant doubtful debt amount. Zero if no doubtful debt amount was present.
     */
    private static BigDecimal getDoubtFulDebtAmount(BigDecimal scheduleDetailsTotalAmount, BigDecimal paymentAmount,
            BigDecimal doubtfulDebtTotalAmount) {
        BigDecimal calculatedDoubtFulDebtAmount = BigDecimal.ZERO;
        if (doubtfulDebtTotalAmount.compareTo(BigDecimal.ZERO) == 0) {
            return calculatedDoubtFulDebtAmount;
        }
        calculatedDoubtFulDebtAmount = paymentAmount
                .subtract(scheduleDetailsTotalAmount.subtract(doubtfulDebtTotalAmount));
        // There can not be negative Doubtful Debt Amounts. If it is negative, set it to Zero as the
        // other Payment Schedule Detail will compensate it.
        if (calculatedDoubtFulDebtAmount.signum() > 0) {
            return calculatedDoubtFulDebtAmount;
        }
        return BigDecimal.ZERO;
    }
}