public static Map updatePaymentApplicationDefBd()

in applications/accounting/src/main/java/org/apache/ofbiz/accounting/invoice/InvoiceServices.java [2685:3479]


    public static Map<String, Object> updatePaymentApplicationDefBd(DispatchContext dctx, Map<String, Object> context) {
        Delegator delegator = dctx.getDelegator();
        Locale locale = (Locale) context.get("locale");

        if (DECIMALS == -1 || ROUNDING == null) {
            return ServiceUtil.returnError(UtilProperties.getMessage(RESOURCE,
                    "AccountingAritmeticPropertiesNotConfigured", locale));
        }

        if (!context.containsKey("useHighestAmount")) {
            context.put("useHighestAmount", "Y");
        }

        String defaultInvoiceProcessing = EntityUtilProperties.getPropertyValue("accounting", "invoiceProcessing", delegator);

        boolean debug = true; // show processing messages in the log..or not....

        // a 'y' in invoiceProssesing will reverse the default processing
        String changeProcessing = (String) context.get("invoiceProcessing");
        String invoiceId = (String) context.get("invoiceId");
        String invoiceItemSeqId = (String) context.get("invoiceItemSeqId");
        String paymentId = (String) context.get("paymentId");
        String toPaymentId = (String) context.get("toPaymentId");
        String paymentApplicationId = (String) context.get("paymentApplicationId");
        BigDecimal amountApplied = (BigDecimal) context.get("amountApplied");
        String billingAccountId = (String) context.get("billingAccountId");
        String taxAuthGeoId = (String) context.get("taxAuthGeoId");
        String useHighestAmount = (String) context.get("useHighestAmount");

        List<String> errorMessageList = new LinkedList<>();

        if (debug) {
            Debug.logInfo("updatePaymentApplicationDefBd input parameters..."
                    + " defaultInvoiceProcessing: " + defaultInvoiceProcessing
                    + " changeDefaultInvoiceProcessing: " + changeProcessing
                    + " useHighestAmount: " + useHighestAmount
                    + " paymentApplicationId: " + paymentApplicationId
                    + " PaymentId: " + paymentId
                    + " InvoiceId: " + invoiceId
                    + " InvoiceItemSeqId: " + invoiceItemSeqId
                    + " BillingAccountId: " + billingAccountId
                    + " toPaymentId: " + toPaymentId
                    + " amountApplied: " + amountApplied
                    + " TaxAuthGeoId: " + taxAuthGeoId, MODULE);
        }

        if (changeProcessing == null) {
            changeProcessing = "N";    // not provided, so no change
        }

        boolean invoiceProcessing = true;
        if ("YY".equals(defaultInvoiceProcessing)) {
            invoiceProcessing = true;
        } else if ("NN".equals(defaultInvoiceProcessing)) {
            invoiceProcessing = false;
        } else if ("Y".equals(defaultInvoiceProcessing)) {
            invoiceProcessing = !"Y".equals(changeProcessing);
        } else if ("N".equals(defaultInvoiceProcessing)) {
            invoiceProcessing = "Y".equals(changeProcessing);
        }

        // on a new paymentApplication check if only billing or invoice or tax
        // id is provided not 2,3... BUT a combination of billingAccountId and invoiceId is permitted - that's how you use a
        // Billing Account to pay for an Invoice
        if (paymentApplicationId == null) {
            int count = 0;
            if (invoiceId != null) {
                count++;
            }
            if (toPaymentId != null) {
                count++;
            }
            if (billingAccountId != null) {
                count++;
            }
            if (taxAuthGeoId != null) {
                count++;
            }
            if ((billingAccountId != null) && (invoiceId != null)) {
                count--;
            }
            if (count != 1) {
                errorMessageList.add(UtilProperties.getMessage(RESOURCE, "AccountingSpecifyInvoiceToPaymentBillingAccountTaxGeoId", locale));
            }
        }

        // avoid null pointer exceptions.
        if (amountApplied == null) {
            amountApplied = BigDecimal.ZERO;
        }
        // makes no sense to have an item numer without an invoice number
        if (invoiceId == null) {
            invoiceItemSeqId = null;
        }

        // retrieve all information and perform checking on the retrieved info.....

        // Payment.....
        BigDecimal paymentApplyAvailable = BigDecimal.ZERO;
        // amount available on the payment reduced by the already applied amounts
        GenericValue payment = null;
        String currencyUomId = null;
        if (paymentId == null || "".equals(paymentId)) {
            errorMessageList.add(UtilProperties.getMessage(RESOURCE, "AccountingPaymentIdBlankNotSupplied", locale));
        } else {
            try {
                payment = EntityQuery.use(delegator).from("Payment").where("paymentId", paymentId).queryOne();
            } catch (GenericEntityException e) {
                return ServiceUtil.returnError(e.getMessage());
            }
            if (payment == null) {
                errorMessageList.add(UtilProperties.getMessage(RESOURCE,
                        "AccountingPaymentRecordNotFound", UtilMisc.toMap("paymentId", paymentId), locale));
                return ServiceUtil.returnError(errorMessageList);
            }
            paymentApplyAvailable = payment.getBigDecimal("amount").subtract(PaymentWorker.getPaymentApplied(payment)).setScale(DECIMALS, ROUNDING);

            if ("PMNT_CANCELLED".equals(payment.getString("statusId"))) {
                errorMessageList.add(UtilProperties.getMessage(RESOURCE,
                        "AccountingPaymentCancelled", UtilMisc.toMap("paymentId", paymentId), locale));
            }
            if ("PMNT_CONFIRMED".equals(payment.getString("statusId"))) {
                errorMessageList.add(UtilProperties.getMessage(RESOURCE,
                        "AccountingPaymentConfirmed", UtilMisc.toMap("paymentId", paymentId), locale));
            }

            currencyUomId = payment.getString("currencyUomId");

        }

        // the "TO" Payment.....
        BigDecimal toPaymentApplyAvailable = BigDecimal.ZERO;
        GenericValue toPayment = null;
        if (toPaymentId != null && !"".equals(toPaymentId)) {
            try {
                toPayment = EntityQuery.use(delegator).from("Payment").where("paymentId", toPaymentId).queryOne();
            } catch (GenericEntityException e) {
                return ServiceUtil.returnError(e.getMessage());
            }
            if (toPayment == null) {
                errorMessageList.add(UtilProperties.getMessage(RESOURCE,
                        "AccountingPaymentRecordNotFound", UtilMisc.toMap("paymentId", toPaymentId), locale));
                return ServiceUtil.returnError(errorMessageList);
            }
            toPaymentApplyAvailable = toPayment.getBigDecimal("amount").subtract(PaymentWorker.getPaymentApplied(toPayment)).setScale(DECIMALS,
                    ROUNDING);

            if ("PMNT_CANCELLED".equals(toPayment.getString("statusId"))) {
                errorMessageList.add(UtilProperties.getMessage(RESOURCE,
                        "AccountingPaymentCancelled", UtilMisc.toMap("paymentId", paymentId), locale));
            }
            if ("PMNT_CONFIRMED".equals(toPayment.getString("statusId"))) {
                errorMessageList.add(UtilProperties.getMessage(RESOURCE,
                        "AccountingPaymentConfirmed", UtilMisc.toMap("paymentId", paymentId), locale));
            }

            if (paymentApplicationId == null) {
                // only check for new application records, update on existing records is checked in the paymentApplication section
                if (toPaymentApplyAvailable.signum() == 0) {
                    errorMessageList.add(UtilProperties.getMessage(RESOURCE,
                            "AccountingPaymentAlreadyApplied", UtilMisc.toMap("paymentId", toPaymentId), locale));
                } else {
                    // check here for too much application if a new record is
                    // added (paymentApplicationId == null)
                    if (amountApplied.compareTo(toPaymentApplyAvailable) > 0) {
                        errorMessageList.add(UtilProperties.getMessage(RESOURCE,
                                "AccountingPaymentLessRequested",
                                UtilMisc.<String, Object>toMap("paymentId", toPaymentId,
                                        "paymentApplyAvailable", toPaymentApplyAvailable,
                                        "amountApplied", amountApplied, "isoCode", currencyUomId), locale));
                    }
                }
            }

            // check if at least one send is the same as one receiver on the other payment
            if (!payment.getString("partyIdFrom").equals(toPayment.getString("partyIdTo"))
                    && !payment.getString("partyIdTo").equals(toPayment.getString("partyIdFrom"))) {
                errorMessageList.add(UtilProperties.getMessage(RESOURCE,
                        "AccountingFromPartySameToParty", locale));
            }

            if (debug) {
                Debug.logInfo("toPayment info retrieved and checked...", MODULE);
            }
        }

        // assign payment to billing account if the invoice is assigned to this billing account
        if (invoiceId != null) {
            GenericValue invoice = null;
            try {
                invoice = EntityQuery.use(delegator).from("Invoice").where("invoiceId", invoiceId).queryOne();
            } catch (GenericEntityException e) {
                return ServiceUtil.returnError(e.getMessage());
            }

            if (invoice == null) {
                errorMessageList.add(UtilProperties.getMessage(RESOURCE,
                        "AccountingInvoiceNotFound", UtilMisc.toMap("invoiceId", invoiceId), locale));
            } else {
                if (invoice.getString("billingAccountId") != null) {
                    billingAccountId = invoice.getString("billingAccountId");
                }
            }
        }

        // billing account
        GenericValue billingAccount = null;
        if (billingAccountId != null && !"".equals(billingAccountId)) {
            try {
                billingAccount = EntityQuery.use(delegator).from("BillingAccount").where("billingAccountId", billingAccountId).queryOne();
            } catch (GenericEntityException e) {
                return ServiceUtil.returnError(e.getMessage());
            }
            if (billingAccount == null) {
                errorMessageList.add(UtilProperties.getMessage(RESOURCE,
                        "AccountingBillingAccountNotFound", UtilMisc.toMap("billingAccountId", billingAccountId), locale));
                return ServiceUtil.returnError(errorMessageList);
            }
            // check the currency
            if (billingAccount.get("accountCurrencyUomId") != null && currencyUomId != null
                    && !billingAccount.getString("accountCurrencyUomId").equals(currencyUomId)) {
                errorMessageList.add(UtilProperties.getMessage(RESOURCE, "AccountingBillingAccountCurrencyProblem",
                        UtilMisc.toMap("billingAccountId", billingAccountId,
                                "accountCurrencyUomId", billingAccount.getString("accountCurrencyUomId"),
                                "paymentId", paymentId, "paymentCurrencyUomId", currencyUomId), locale));
            }

            if (debug) {
                Debug.logInfo("Billing Account info retrieved and checked...", MODULE);
            }
        }

        // get the invoice (item) information
        BigDecimal invoiceApplyAvailable = BigDecimal.ZERO;
        // amount available on the invoice reduced by the already applied amounts
        BigDecimal invoiceItemApplyAvailable = BigDecimal.ZERO;
        // amount available on the invoiceItem reduced by the already applied amounts
        GenericValue invoice = null;
        GenericValue invoiceItem = null;
        if (invoiceId != null) {
            try {
                invoice = EntityQuery.use(delegator).from("Invoice").where("invoiceId", invoiceId).queryOne();
            } catch (GenericEntityException e) {
                return ServiceUtil.returnError(e.getMessage());
            }

            if (invoice == null) {
                errorMessageList.add(UtilProperties.getMessage(RESOURCE,
                        "AccountingInvoiceNotFound", UtilMisc.toMap("invoiceId", invoiceId), locale));
            } else { // check the invoice and when supplied the invoice item...

                if ("INVOICE_CANCELLED".equals(invoice.getString("statusId"))) {
                    errorMessageList.add(UtilProperties.getMessage(RESOURCE,
                            "AccountingInvoiceCancelledCannotApplyTo", UtilMisc.toMap("invoiceId", invoiceId), locale));
                }

                // check the currency
                if (currencyUomId != null && invoice.get("currencyUomId") != null
                        && !currencyUomId.equals(invoice.getString("currencyUomId"))) {
                    Debug.logInfo(UtilProperties.getMessage(RESOURCE, "AccountingInvoicePaymentCurrencyProblem",
                            UtilMisc.toMap("invoiceCurrency", invoice.getString("currencyUomId"), "paymentCurrency", payment.getString(
                                    "currencyUomId")), locale), MODULE);
                    Debug.logInfo("will try to apply payment on the actualCurrency amount on payment", MODULE);

                    if (payment.get("actualCurrencyAmount") == null || payment.get("actualCurrencyUomId") == null) {
                        errorMessageList.add("Actual amounts are required in the currency of the invoice to make this work....");
                    } else {
                        currencyUomId = payment.getString("actualCurrencyUomId");
                        if (!currencyUomId.equals(invoice.getString("currencyUomId"))) {
                            errorMessageList.add("actual currency on payment (" + currencyUomId + ") not the same as original invoice currency ("
                                    + invoice.getString("currencyUomId") + ")");
                        }
                    }
                    paymentApplyAvailable =
                            payment.getBigDecimal("actualCurrencyAmount").subtract(PaymentWorker.getPaymentApplied(payment))
                                    .setScale(DECIMALS, ROUNDING);
                }

                // check if the invoice already covered by payments
                BigDecimal invoiceTotal = InvoiceWorker.getInvoiceTotal(invoice);
                invoiceApplyAvailable = InvoiceWorker.getInvoiceNotApplied(invoice);

                if (invoiceTotal.signum() == 0) {
                    errorMessageList.add(UtilProperties.getMessage(RESOURCE,
                            "AccountingInvoiceTotalZero", UtilMisc.toMap("invoiceId", invoiceId), locale));
                } else if (paymentApplicationId == null) {
                    // only check for new records here...updates are checked in the paymentApplication section
                    if (invoiceApplyAvailable.signum() == 0) {
                        errorMessageList.add(UtilProperties.getMessage(RESOURCE,
                                "AccountingInvoiceCompletelyApplied", UtilMisc.toMap("invoiceId", invoiceId), locale));
                        // check here for too much application if a new record(s) are
                        // added (paymentApplicationId == null)
                    } else if (amountApplied.compareTo(invoiceApplyAvailable) > 0) {
                        errorMessageList.add(UtilProperties.getMessage(RESOURCE, "AccountingInvoiceLessRequested",
                                UtilMisc.<String, Object>toMap("invoiceId", invoiceId,
                                        "invoiceApplyAvailable", invoiceApplyAvailable,
                                        "amountApplied", amountApplied,
                                        "isoCode", invoice.getString("currencyUomId")), locale));
                    }
                }

                // check if at least one sender is the same as one receiver on the invoice
                if (!payment.getString("partyIdFrom").equals(invoice.getString("partyId"))
                        && !payment.getString("partyIdTo").equals(invoice.getString("partyIdFrom"))) {
                    errorMessageList.add(UtilProperties.getMessage(RESOURCE,
                            "AccountingFromPartySameToParty", locale));
                }

                if (debug) {
                    Debug.logInfo("Invoice info retrieved and checked ...", MODULE);
                }
            }

            // if provided check the invoice item.
            if (invoiceItemSeqId != null) {
                // when itemSeqNr not provided delay checking on invoiceItemSeqId
                try {
                    invoiceItem = EntityQuery.use(delegator).from("InvoiceItem").where("invoiceId", invoiceId, "invoiceItemSeqId",
                            invoiceItemSeqId).queryOne();
                } catch (GenericEntityException e) {
                    return ServiceUtil.returnError(e.getMessage());
                }

                if (invoiceItem == null) {
                    errorMessageList.add(UtilProperties.getMessage(RESOURCE,
                            "AccountingInvoiceItemNotFound",
                            UtilMisc.toMap("invoiceId", invoiceId, "invoiceItemSeqId", invoiceItemSeqId), locale));
                } else {
                    if (invoice.get("currencyUomId") != null && currencyUomId != null && !invoice.getString("currencyUomId").equals(currencyUomId)) {
                        errorMessageList.add(UtilProperties.getMessage(RESOURCE,
                                "AccountingInvoicePaymentCurrencyProblem",
                                UtilMisc.toMap("paymentCurrencyId", currencyUomId,
                                        "itemCurrency", invoice.getString("currencyUomId")), locale));
                    }

                    // get the invoice item applied value
                    BigDecimal quantity = null;
                    if (invoiceItem.get("quantity") == null) {
                        quantity = BigDecimal.ONE;
                    } else {
                        quantity = invoiceItem.getBigDecimal("quantity").setScale(DECIMALS, ROUNDING);
                    }
                    invoiceItemApplyAvailable =
                            invoiceItem.getBigDecimal("amount").multiply(quantity).setScale(DECIMALS, ROUNDING)
                                    .subtract(InvoiceWorker.getInvoiceItemApplied(invoiceItem));
                    // check here for too much application if a new record is added
                    if (paymentApplicationId == null && amountApplied.compareTo(invoiceItemApplyAvailable) > 0) {
                        // new record
                        errorMessageList.add("Invoice(" + invoiceId + ") item(" + invoiceItemSeqId + ") has  " + invoiceItemApplyAvailable + " to "
                                + "apply but " + amountApplied + " is requested\n");
                        String uomId = invoice.getString("currencyUomId");
                        errorMessageList.add(UtilProperties.getMessage(RESOURCE, "AccountingInvoiceItemLessRequested",
                                UtilMisc.<String, Object>toMap("invoiceId", invoiceId, "invoiceItemSeqId", invoiceItemSeqId,
                                        "invoiceItemApplyAvailable", invoiceItemApplyAvailable,
                                        "amountApplied", amountApplied, "isoCode", uomId), locale));
                    }
                }
                if (debug) {
                    Debug.logInfo("InvoiceItem info retrieved and checked against the Invoice (currency and amounts) ...", MODULE);
                }
            }
        }

        // check this at the end because the invoice can change the currency.......
        if (paymentApplicationId == null) {
            // only check for new application records, update on existing records is checked in the paymentApplication section
            if (paymentApplyAvailable.signum() == 0) {
                errorMessageList.add(UtilProperties.getMessage(RESOURCE,
                        "AccountingPaymentAlreadyApplied", UtilMisc.toMap("paymentId", paymentId), locale));
            } else {
                // check here for too much application if a new record is
                // added (paymentApplicationId == null)
                if (amountApplied.compareTo(paymentApplyAvailable) > 0) {
                    errorMessageList.add(UtilProperties.getMessage(RESOURCE, "AccountingPaymentLessRequested",
                            UtilMisc.<String, Object>toMap("paymentId", paymentId,
                                    "paymentApplyAvailable", paymentApplyAvailable,
                                    "amountApplied", amountApplied, "isoCode", currencyUomId), locale));
                }
            }
        }


        // get the application record if the applicationId is supplied if not
        // create empty record.
        BigDecimal newInvoiceApplyAvailable = invoiceApplyAvailable;
        // amount available on the invoice taking into account if the invoiceItemnumber has changed
        BigDecimal newInvoiceItemApplyAvailable = invoiceItemApplyAvailable;
        // amount available on the invoiceItem taking into account if the itemnumber has changed
        BigDecimal newToPaymentApplyAvailable = toPaymentApplyAvailable;
        BigDecimal newPaymentApplyAvailable = paymentApplyAvailable;
        GenericValue paymentApplication = null;
        if (paymentApplicationId == null) {
            paymentApplication = delegator.makeValue("PaymentApplication");
            // prepare for creation
        } else { // retrieve existing paymentApplication
            try {
                paymentApplication =
                        EntityQuery.use(delegator).from("PaymentApplication").where("paymentApplicationId", paymentApplicationId).queryOne();
            } catch (GenericEntityException e) {
                return ServiceUtil.returnError(e.getMessage());
            }

            if (paymentApplication == null) {
                errorMessageList.add(UtilProperties.getMessage(RESOURCE,
                        "AccountingPaymentApplicationNotFound",
                        UtilMisc.toMap("paymentApplicationId", paymentApplicationId), locale));
                paymentApplicationId = null;
            } else {

                // if both invoiceId and BillingId is entered there was
                // obviously a change
                // only take the newly entered item, same for tax authority and toPayment
                if (paymentApplication.get("invoiceId") == null && invoiceId != null) {
                    billingAccountId = null;
                    taxAuthGeoId = null;
                    toPaymentId = null;
                } else if (paymentApplication.get("toPaymentId") == null && toPaymentId != null) {
                    invoiceId = null;
                    invoiceItemSeqId = null;
                    taxAuthGeoId = null;
                    billingAccountId = null;
                } else if (paymentApplication.get("billingAccountId") == null && billingAccountId != null) {
                    invoiceId = null;
                    invoiceItemSeqId = null;
                    toPaymentId = null;
                    taxAuthGeoId = null;
                } else if (paymentApplication.get("taxAuthGeoId") == null && taxAuthGeoId != null) {
                    invoiceId = null;
                    invoiceItemSeqId = null;
                    toPaymentId = null;
                    billingAccountId = null;
                }

                // check if the payment for too much application if an existing
                // application record is changed
                if (paymentApplyAvailable.compareTo(BigDecimal.ZERO) == 0) {
                    newPaymentApplyAvailable =
                            paymentApplyAvailable.add(paymentApplication.getBigDecimal("amountApplied")).subtract(amountApplied).setScale(DECIMALS,
                                    ROUNDING);
                } else {
                    newPaymentApplyAvailable = paymentApplyAvailable.add(paymentApplyAvailable).subtract(amountApplied).setScale(DECIMALS, ROUNDING);
                }
                if (newPaymentApplyAvailable.compareTo(BigDecimal.ZERO) < 0) {
                    errorMessageList.add(UtilProperties.getMessage(RESOURCE,
                            "AccountingPaymentNotEnough",
                            UtilMisc.<String, Object>toMap("paymentId", paymentId,
                                    "paymentApplyAvailable", paymentApplyAvailable.add(paymentApplication.getBigDecimal("amountApplied")),
                                    "amountApplied", amountApplied), locale));
                }

                if (invoiceId != null) {
                    // only when we are processing an invoice on existing paymentApplication check invoice item for to much application if the invoice
                    // number did not change
                    if (invoiceId.equals(paymentApplication.getString("invoiceId"))) {
                        // check if both the itemNumbers are null then this is a
                        // record for the whole invoice
                        if (invoiceItemSeqId == null && paymentApplication.get("invoiceItemSeqId") == null) {
                            newInvoiceApplyAvailable =
                                    invoiceApplyAvailable.add(paymentApplication.getBigDecimal("amountApplied")).subtract(amountApplied)
                                            .setScale(DECIMALS, ROUNDING);
                            if (invoiceApplyAvailable.compareTo(BigDecimal.ZERO) < 0) {
                                errorMessageList.add(UtilProperties.getMessage(RESOURCE,
                                        "AccountingInvoiceNotEnough",
                                        UtilMisc.<String, Object>toMap("tooMuch", newInvoiceApplyAvailable.negate(),
                                                "invoiceId", invoiceId), locale));
                            }
                        } else if (invoiceItemSeqId == null && paymentApplication.get("invoiceItemSeqId") != null) {
                            // check if the item number changed from a real Item number to a null value
                            newInvoiceApplyAvailable =
                                    invoiceApplyAvailable.add(paymentApplication.getBigDecimal("amountApplied"))
                                            .subtract(amountApplied).setScale(DECIMALS, ROUNDING);
                            if (invoiceApplyAvailable.compareTo(BigDecimal.ZERO) < 0) {
                                errorMessageList.add(UtilProperties.getMessage(RESOURCE,
                                        "AccountingInvoiceNotEnough",
                                        UtilMisc.<String, Object>toMap("tooMuch", newInvoiceApplyAvailable.negate(),
                                                "invoiceId", invoiceId), locale));
                            }
                        } else if (paymentApplication.get("invoiceItemSeqId") == null) {
                            // check if the item number changed from a null value to
                            // a real Item number
                            newInvoiceItemApplyAvailable = invoiceItemApplyAvailable.subtract(amountApplied).setScale(DECIMALS, ROUNDING);
                            if (newInvoiceItemApplyAvailable.compareTo(BigDecimal.ZERO) < 0) {
                                errorMessageList.add(UtilProperties.getMessage(RESOURCE,
                                        "AccountingItemInvoiceNotEnough",
                                        UtilMisc.<String, Object>toMap("tooMuch", newInvoiceItemApplyAvailable.negate(),
                                                "invoiceId", invoiceId,
                                                "invoiceItemSeqId", invoiceItemSeqId), locale));
                            }
                        } else if (invoiceItemSeqId.equals(paymentApplication.getString("invoiceItemSeqId"))) {
                            // check if the real item numbers the same
                            // item number the same numeric value
                            newInvoiceItemApplyAvailable =
                                    invoiceItemApplyAvailable.add(paymentApplication.getBigDecimal("amountApplied"))
                                            .subtract(amountApplied).setScale(DECIMALS, ROUNDING);
                            if (newInvoiceItemApplyAvailable.compareTo(BigDecimal.ZERO) < 0) {
                                errorMessageList.add(UtilProperties.getMessage(RESOURCE,
                                        "AccountingItemInvoiceNotEnough",
                                        UtilMisc.<String, Object>toMap("tooMuch", newInvoiceItemApplyAvailable.negate(),
                                                "invoiceId", invoiceId,
                                                "invoiceItemSeqId", invoiceItemSeqId), locale));
                            }
                        } else {
                            // item number changed only check new item
                            newInvoiceItemApplyAvailable = invoiceItemApplyAvailable.add(amountApplied).setScale(DECIMALS, ROUNDING);
                            if (newInvoiceItemApplyAvailable.compareTo(BigDecimal.ZERO) < 0) {
                                errorMessageList.add(UtilProperties.getMessage(RESOURCE,
                                        "AccountingItemInvoiceNotEnough",
                                        UtilMisc.<String, Object>toMap("tooMuch", newInvoiceItemApplyAvailable.negate(),
                                                "invoiceId", invoiceId,
                                                "invoiceItemSeqId", invoiceItemSeqId), locale));
                            }
                        }

                        // if the amountApplied = 0 give it the higest possible
                        // value
                        if (amountApplied.signum() == 0) {
                            if (newInvoiceItemApplyAvailable.compareTo(newPaymentApplyAvailable) < 0) {
                                amountApplied = newInvoiceItemApplyAvailable;
                                // from the item number
                            } else {
                                amountApplied = newPaymentApplyAvailable;
                                // from the payment
                            }
                        }

                        // check the invoice
                        newInvoiceApplyAvailable =
                                invoiceApplyAvailable.add(paymentApplication.getBigDecimal("amountApplied")
                                        .subtract(amountApplied)).setScale(DECIMALS, ROUNDING);
                        if (newInvoiceApplyAvailable.compareTo(BigDecimal.ZERO) < 0) {
                            errorMessageList.add(UtilProperties.getMessage(RESOURCE,
                                    "AccountingInvoiceNotEnough",
                                    UtilMisc.<String, Object>toMap("tooMuch", invoiceApplyAvailable.add(paymentApplication.getBigDecimal(
                                            "amountApplied")).subtract(amountApplied),
                                            "invoiceId", invoiceId), locale));
                        }
                    }
                }

                // check the toPayment account when only the amountApplied has
                // changed,
                if (toPaymentId != null && toPaymentId.equals(paymentApplication.getString("toPaymentId"))) {
                    newToPaymentApplyAvailable =
                            toPaymentApplyAvailable.subtract(paymentApplication.getBigDecimal("amountApplied"))
                                    .add(amountApplied).setScale(DECIMALS, ROUNDING);
                    if (newToPaymentApplyAvailable.compareTo(BigDecimal.ZERO) < 0) {
                        errorMessageList.add(UtilProperties.getMessage(RESOURCE,
                                "AccountingPaymentNotEnough",
                                UtilMisc.<String, Object>toMap("paymentId", toPaymentId,
                                        "paymentApplyAvailable", newToPaymentApplyAvailable,
                                        "amountApplied", amountApplied), locale));
                    }
                } else if (toPaymentId != null) {
                    // billing account entered number has changed so we have to
                    // check the new billing account number.
                    newToPaymentApplyAvailable = toPaymentApplyAvailable.add(amountApplied).setScale(DECIMALS, ROUNDING);
                    if (newToPaymentApplyAvailable.compareTo(BigDecimal.ZERO) < 0) {
                        errorMessageList.add(UtilProperties.getMessage(RESOURCE,
                                "AccountingPaymentNotEnough",
                                UtilMisc.<String, Object>toMap("paymentId", toPaymentId,
                                        "paymentApplyAvailable", newToPaymentApplyAvailable,
                                        "amountApplied", amountApplied), locale));
                    }

                }
            }
            if (debug) {
                Debug.logInfo("paymentApplication record info retrieved and checked...", MODULE);
            }
        }

        // show the maximumus what can be added in the payment application file.
        String toMessage = null;  // prepare for success message
        if (debug) {
            String extra = "";
            if (invoiceItemSeqId != null) {
                extra = " Invoice item(" + invoiceItemSeqId + ") amount not yet applied: " + newInvoiceItemApplyAvailable;
            }
            Debug.logInfo("checking finished, start processing with the following data... ", MODULE);
            if (invoiceId != null) {
                Debug.logInfo(" Invoice(" + invoiceId + ") amount not yet applied: " + newInvoiceApplyAvailable + extra + " Payment("
                        + paymentId + ") amount not yet applied: " + newPaymentApplyAvailable + " Requested amount to apply:" + amountApplied,
                        MODULE);
                toMessage = UtilProperties.getMessage(RESOURCE,
                        "AccountingApplicationToInvoice",
                        UtilMisc.toMap("invoiceId", invoiceId), locale);
                if (!extra.isEmpty()) {
                    toMessage = UtilProperties.getMessage(RESOURCE,
                            "AccountingApplicationToInvoiceItem",
                            UtilMisc.toMap("invoiceId", invoiceId, "invoiceItemSeqId", invoiceItemSeqId), locale);
                }
            }
            if (toPaymentId != null) {
                Debug.logInfo(" toPayment(" + toPaymentId + ") amount not yet applied: " + newToPaymentApplyAvailable + " Payment(" + paymentId
                        + ") amount not yet applied: " + newPaymentApplyAvailable + " Requested amount to apply:" + amountApplied, MODULE);
                toMessage = UtilProperties.getMessage(RESOURCE,
                        "AccountingApplicationToPayment",
                        UtilMisc.toMap("paymentId", toPaymentId), locale);
            }
            if (taxAuthGeoId != null) {
                Debug.logInfo(" taxAuthGeoId(" + taxAuthGeoId + ")  Payment(" + paymentId + ") amount not yet applied: " + newPaymentApplyAvailable
                        + " Requested amount to apply:" + amountApplied, MODULE);
                toMessage = UtilProperties.getMessage(RESOURCE,
                        "AccountingApplicationToTax",
                        UtilMisc.toMap("taxAuthGeoId", taxAuthGeoId), locale);
            }
        }
        // if the amount to apply was not provided or was zero fill it with the maximum possible and provide information to the user
        if (amountApplied.signum() == 0 && "Y".equals(useHighestAmount)) {
            amountApplied = newPaymentApplyAvailable;
            if (invoiceId != null && newInvoiceApplyAvailable.compareTo(amountApplied) < 0) {
                amountApplied = newInvoiceApplyAvailable;
                toMessage = UtilProperties.getMessage(RESOURCE,
                        "AccountingApplicationToInvoice",
                        UtilMisc.toMap("invoiceId", invoiceId), locale);
            }
            if (toPaymentId != null && newToPaymentApplyAvailable.compareTo(amountApplied) < 0) {
                amountApplied = newToPaymentApplyAvailable;
                toMessage = UtilProperties.getMessage(RESOURCE,
                        "AccountingApplicationToPayment",
                        UtilMisc.toMap("paymentId", toPaymentId), locale);
            }
        }

        String successMessage = null;
        if (amountApplied.signum() == 0) {
            errorMessageList.add(UtilProperties.getMessage(RESOURCE, "AccountingNoAmount", locale));
        } else {
            successMessage = UtilProperties.getMessage(RESOURCE,
                    "AccountingPaymentApplicationSuccess",
                    UtilMisc.<String, Object>toMap("amountApplied", amountApplied,
                            "paymentId", paymentId,
                            "isoCode", currencyUomId,
                            "toMessage", toMessage), locale);
        }
        // report error messages if any
        if (!errorMessageList.isEmpty()) {
            return ServiceUtil.returnError(errorMessageList);
        }

        // ============ start processing ======================
        // if the application is specified it is easy, update the existing record only
        if (paymentApplicationId != null) {
            // record is already retrieved previously
            if (debug) {
                Debug.logInfo("Process an existing paymentApplication record: " + paymentApplicationId, MODULE);
            }
            // update the current record
            paymentApplication.set("invoiceId", invoiceId);
            paymentApplication.set("invoiceItemSeqId", invoiceItemSeqId);
            paymentApplication.set("paymentId", paymentId);
            paymentApplication.set("toPaymentId", toPaymentId);
            paymentApplication.set("amountApplied", amountApplied);
            paymentApplication.set("billingAccountId", billingAccountId);
            paymentApplication.set("taxAuthGeoId", taxAuthGeoId);
            return storePaymentApplication(delegator, paymentApplication, locale);
        }

        // if no invoice sequence number is provided it assumed the requested paymentAmount will be
        // spread over the invoice starting with the lowest sequence number if
        // itemprocessing is on otherwise create one record
        if (invoiceId != null && paymentId != null && (invoiceItemSeqId == null)) {
            if (invoiceProcessing) {
                // create only a single record with a null seqId
                if (debug) {
                    Debug.logInfo("Try to allocate the payment to the invoice as a whole", MODULE);
                }
                paymentApplication.set("paymentId", paymentId);
                paymentApplication.set("toPaymentId", null);
                paymentApplication.set("invoiceId", invoiceId);
                paymentApplication.set("invoiceItemSeqId", null);
                paymentApplication.set("toPaymentId", null);
                paymentApplication.set("amountApplied", amountApplied);
                paymentApplication.set("billingAccountId", billingAccountId);
                paymentApplication.set("taxAuthGeoId", null);
                if (debug) {
                    Debug.logInfo("creating new paymentapplication", MODULE);
                }
                return storePaymentApplication(delegator, paymentApplication, locale);
            }
            if (debug) {
                Debug.logInfo("Try to allocate the payment to the itemnumbers of the invoice", MODULE);
            }
            // get the invoice items
            List<GenericValue> invoiceItems = null;
            try {
                invoiceItems = EntityQuery.use(delegator).from("InvoiceItem").where("invoiceId", invoiceId).queryList();
            } catch (GenericEntityException e) {
                return ServiceUtil.returnError(e.getMessage());
            }
            if (invoiceItems.isEmpty()) {
                errorMessageList.add(UtilProperties.getMessage(RESOURCE, "AccountingNoInvoiceItemsFoundForInvoice", UtilMisc.toMap("invoiceId",
                        invoiceId), locale));
                return ServiceUtil.returnError(errorMessageList);
            }
            // check if the user want to apply a smaller amount than the maximum possible on the payment
            if (amountApplied.signum() != 0 && amountApplied.compareTo(paymentApplyAvailable) < 0) {
                paymentApplyAvailable = amountApplied;
            }
            for (GenericValue currentInvoiceItem : invoiceItems) {
                if (paymentApplyAvailable.compareTo(BigDecimal.ZERO) > 0) {
                    break;
                }
                if (debug) {
                    Debug.logInfo("Start processing item: " + currentInvoiceItem.getString("invoiceItemSeqId"), MODULE);
                }
                BigDecimal itemQuantity = BigDecimal.ONE;
                if (currentInvoiceItem.get("quantity") != null && currentInvoiceItem.getBigDecimal("quantity").signum() != 0) {
                    itemQuantity = new BigDecimal(currentInvoiceItem.getString("quantity")).setScale(DECIMALS, ROUNDING);
                }
                BigDecimal itemAmount = currentInvoiceItem.getBigDecimal("amount").setScale(DECIMALS, ROUNDING);
                BigDecimal itemTotal = itemAmount.multiply(itemQuantity).setScale(DECIMALS, ROUNDING);

                // get the application(s) already allocated to this
                // item, if available
                List<GenericValue> paymentApplications = null;
                try {
                    paymentApplications = currentInvoiceItem.getRelated("PaymentApplication", null, null, false);
                } catch (GenericEntityException e) {
                    return ServiceUtil.returnError(e.getMessage());
                }
                BigDecimal tobeApplied = BigDecimal.ZERO;
                // item total amount - already applied (if any)
                BigDecimal alreadyApplied = BigDecimal.ZERO;
                if (UtilValidate.isNotEmpty(paymentApplications)) {
                    // application(s) found, add them all together
                    Iterator<GenericValue> p = paymentApplications.iterator();
                    while (p.hasNext()) {
                        paymentApplication = p.next();
                        alreadyApplied = alreadyApplied.add(paymentApplication.getBigDecimal("amountApplied").setScale(DECIMALS, ROUNDING));
                    }
                    tobeApplied = itemTotal.subtract(alreadyApplied).setScale(DECIMALS, ROUNDING);
                } else {
                    // no application connected yet
                    tobeApplied = itemTotal;
                }
                if (debug) {
                    Debug.logInfo("tobeApplied:(" + tobeApplied + ") = " + "itemTotal(" + itemTotal + ") - alreadyApplied(" + alreadyApplied + ") "
                            + "but not more then (nonapplied) paymentAmount(" + paymentApplyAvailable + ")", MODULE);
                }

                if (tobeApplied.signum() == 0) {
                    // invoiceItem already fully applied so look at the next one....
                    continue;
                }

                if (paymentApplyAvailable.compareTo(tobeApplied) > 0) {
                    paymentApplyAvailable = paymentApplyAvailable.subtract(tobeApplied);
                } else {
                    tobeApplied = paymentApplyAvailable;
                    paymentApplyAvailable = BigDecimal.ZERO;
                }

                // create application payment record but check currency
                // first if supplied
                if (invoice.get("currencyUomId") != null && currencyUomId != null && !invoice.getString("currencyUomId").equals(currencyUomId)) {
                    errorMessageList.add("Payment currency (" + currencyUomId + ") and invoice currency(" + invoice.getString("currencyUomId") + ")"
                            + " not the same\n");
                } else {
                    paymentApplication.set("paymentApplicationId", null);
                    // make sure we get a new record
                    paymentApplication.set("invoiceId", invoiceId);
                    paymentApplication.set("invoiceItemSeqId", currentInvoiceItem.getString("invoiceItemSeqId"));
                    paymentApplication.set("paymentId", paymentId);
                    paymentApplication.set("toPaymentId", toPaymentId);
                    paymentApplication.set("amountApplied", tobeApplied);
                    paymentApplication.set("billingAccountId", billingAccountId);
                    paymentApplication.set("taxAuthGeoId", taxAuthGeoId);
                    storePaymentApplication(delegator, paymentApplication, locale);
                }

            }

            if (!errorMessageList.isEmpty()) {
                return ServiceUtil.returnError(errorMessageList);
            }
            if (successMessage != null) {
                return ServiceUtil.returnSuccess(successMessage);
            }
            return ServiceUtil.returnSuccess();
        }

        // if no paymentApplicationId supplied create a new record with the data
        // supplied...
        paymentApplication.set("paymentApplicationId", paymentApplicationId);
        paymentApplication.set("invoiceId", invoiceId);
        paymentApplication.set("invoiceItemSeqId", invoiceItemSeqId);
        paymentApplication.set("paymentId", paymentId);
        paymentApplication.set("toPaymentId", toPaymentId);
        paymentApplication.set("amountApplied", amountApplied);
        paymentApplication.set("billingAccountId", billingAccountId);
        paymentApplication.set("taxAuthGeoId", taxAuthGeoId);
        return storePaymentApplication(delegator, paymentApplication, locale);

    }