Example usage for java.math BigDecimal signum

List of usage examples for java.math BigDecimal signum

Introduction

In this page you can find the example usage for java.math BigDecimal signum.

Prototype

public int signum() 

Source Link

Document

Returns the signum function of this BigDecimal .

Usage

From source file:org.egov.wtms.application.service.collection.WaterTaxExternalService.java

public WaterReceiptDetails payWaterTax(PayWaterTaxDetails payWaterTaxDetails) {

    WaterReceiptDetails waterReceiptDetails = null;
    String currentInstallmentYear = EMPTY;
    BigDecimal totalAmountToBePaid = ZERO;
    WaterConnectionDetails waterConnectionDetails = new WaterConnectionDetails();

    String consumerNo = payWaterTaxDetails.getConsumerNo();
    if (isNotBlank(consumerNo) || isNotBlank(payWaterTaxDetails.getApplicaionNumber()))
        waterConnectionDetails = waterConnectionDetailsService.findByApplicationNumberOrConsumerCodeAndStatus(
                isNotBlank(consumerNo) ? consumerNo : payWaterTaxDetails.getApplicaionNumber(), ACTIVE);

    if (INPROGRESS.equals(waterConnectionDetails.getConnectionStatus()))
        currentInstallmentYear = toYearFormat(connectionDemandService
                .getCurrentInstallment(MODULE_NAME, WaterTaxConstants.YEARLY, new Date()).getInstallmentYear());
    else if (ACTIVE.equals(waterConnectionDetails.getConnectionStatus())
            && ConnectionType.NON_METERED.equals(waterConnectionDetails.getConnectionType()))
        currentInstallmentYear = toYearFormat(connectionDemandService
                .getCurrentInstallment(PROPERTY_MODULE_NAME, null, new Date()).getInstallmentYear());
    else if (ACTIVE.equals(waterConnectionDetails.getConnectionStatus())
            && ConnectionType.METERED.equals(waterConnectionDetails.getConnectionType()))
        currentInstallmentYear = toYearFormat(connectionDemandService
                .getCurrentInstallment(MODULE_NAME, WaterTaxConstants.MONTHLY, new Date())
                .getInstallmentYear());//from  w  w w  . ja  va 2s .  co m

    AssessmentDetails assessmentDetails = propertyExtnUtils.getAssessmentDetailsForFlag(
            waterConnectionDetails.getConnection().getPropertyIdentifier(),
            PropertyExternalService.FLAG_FULL_DETAILS, BasicPropertyStatus.ALL);

    waterConnectionBillable.setWaterConnectionDetails(waterConnectionDetails);
    waterConnectionBillable.setAssessmentDetails(assessmentDetails);
    waterConnectionBillable.setUserId(2L);
    ApplicationThreadLocals.setUserId(2L);

    BillReferenceNumberGenerator billRefeNumber = beanResolver
            .getAutoNumberServiceFor(BillReferenceNumberGenerator.class);
    waterConnectionBillable.setReferenceNumber(billRefeNumber.generateBillNumber(currentInstallmentYear));
    waterConnectionBillable.setBillType(connectionDemandService.getBillTypeByCode(BILLTYPE_AUTO));
    waterConnectionBillable.setTransanctionReferenceNumber(payWaterTaxDetails.getTransactionId());
    waterConnectionBillable.setServiceCode(WATERTAX_CHARGES_SERVICE_CODE);
    waterConnectionBillable.getCurrentDemand().setMinAmtPayable(ZERO);

    EgBill egBill = generateBill(waterConnectionBillable);
    for (EgBillDetails billDetails : egBill.getEgBillDetails())
        if (!billDetails.getDescription().contains(PropertyTaxConstants.DEMANDRSN_STR_ADVANCE)
                && billDetails.getCrAmount().signum() > 0)
            totalAmountToBePaid = totalAmountToBePaid.add(billDetails.getCrAmount());

    BillReceiptInfo billReceiptInfo = getBillReceiptInforForwaterTax(payWaterTaxDetails, egBill);

    if (billReceiptInfo != null) {
        waterReceiptDetails = new WaterReceiptDetails();
        waterReceiptDetails.setReceiptNo(billReceiptInfo.getReceiptNum());
        waterReceiptDetails.setReceiptDate(getFormattedDate(billReceiptInfo.getReceiptDate(), "dd-MM-yyyy"));
        waterReceiptDetails.setPayeeName(billReceiptInfo.getPayeeName());
        waterReceiptDetails.setPayeeAddress(billReceiptInfo.getPayeeAddress());
        waterReceiptDetails.setBillReferenceNo(billReceiptInfo.getBillReferenceNum());
        waterReceiptDetails.setServiceName(billReceiptInfo.getServiceName());
        waterReceiptDetails.setDescription(billReceiptInfo.getDescription());
        waterReceiptDetails.setPaidBy(billReceiptInfo.getPaidBy());
        waterReceiptDetails.setPaymentMode(payWaterTaxDetails.getPaymentMode());
        waterReceiptDetails.setPaymentAmount(billReceiptInfo.getTotalAmount());
        waterReceiptDetails.setTransactionId(billReceiptInfo.getManualReceiptNumber());
        String[] paidFrom = null;
        String[] paidTo = null;
        Installment fromInstallment = new Installment();
        Installment toInstallment = new Installment();
        if (totalAmountToBePaid.signum() > 0) {
            List<ReceiptAccountInfo> receiptAccountsList = new ArrayList<>(billReceiptInfo.getAccountDetails());

            Collections.sort(receiptAccountsList, (rcptAcctInfo1, rcptAcctInfo2) -> {
                if (rcptAcctInfo1.getOrderNumber() != null && rcptAcctInfo2.getOrderNumber() != null)
                    return rcptAcctInfo1.getOrderNumber().compareTo(rcptAcctInfo2.getOrderNumber());
                return 0;
            });
            for (ReceiptAccountInfo rcptAcctInfo : receiptAccountsList)
                if (rcptAcctInfo.getCrAmount().signum() > 0 && !rcptAcctInfo.getDescription()
                        .contains(WaterTaxConstants.DEMANDRSN_REASON_ADVANCE)) {
                    if (paidFrom == null) {
                        paidFrom = rcptAcctInfo.getDescription().split("-", 2);
                        paidFrom = paidFrom[1].split("#", 2);
                    }
                    paidTo = rcptAcctInfo.getDescription().split("-", 2);
                    paidTo = paidTo[1].split("#", 2);
                }

            if (paidFrom != null)
                fromInstallment = installmentDao.getInsatllmentByModuleAndDescription(
                        moduleService.getModuleByName(PROPERTY_MODULE_NAME), paidFrom[0].trim());
            if (paidTo != null)
                toInstallment = installmentDao.getInsatllmentByModuleAndDescription(
                        moduleService.getModuleByName(PROPERTY_MODULE_NAME), paidTo[0].trim());
        }

        if (totalAmountToBePaid.signum() == 0) {
            waterReceiptDetails.setPaymentPeriod(EMPTY);
            waterReceiptDetails.setPaymentType(PAYMENT_TYPE_ADVANCE);
        } else
            waterReceiptDetails.setPaymentPeriod(getDefaultFormattedDate(fromInstallment.getFromDate())
                    .concat(" to ").concat(getDefaultFormattedDate(toInstallment.getToDate())));

        if (payWaterTaxDetails.getPaymentAmount().compareTo(totalAmountToBePaid) > 0)
            waterReceiptDetails.setPaymentType(PAYMENT_TYPE_ADVANCE);
        else if (totalAmountToBePaid.compareTo(payWaterTaxDetails.getPaymentAmount()) > 0)
            waterReceiptDetails.setPaymentType(WaterTaxConstants.PAYMENT_TYPE_PARTIALLY);
        else
            waterReceiptDetails.setPaymentType(WaterTaxConstants.PAYMENT_TYPE_FULLY);
        ErrorDetails errorDetails = new ErrorDetails();
        errorDetails.setErrorCode(WaterTaxConstants.THIRD_PARTY_ERR_CODE_SUCCESS);
        errorDetails.setErrorMessage(WaterTaxConstants.THIRD_PARTY_ERR_MSG_SUCCESS);
        waterReceiptDetails.setErrorDetails(errorDetails);
    }
    return waterReceiptDetails;
}

From source file:org.ofbiz.accounting.invoice.InvoiceServices.java

public static Map<String, Object> createInvoicesFromShipments(DispatchContext dctx,
        Map<String, ? extends Object> context) {
    Delegator delegator = dctx.getDelegator();
    LocalDispatcher dispatcher = dctx.getDispatcher();
    List<String> shipmentIds = UtilGenerics.checkList(context.get("shipmentIds"));
    Locale locale = (Locale) context.get("locale");
    Boolean createSalesInvoicesForDropShipments = (Boolean) context.get("createSalesInvoicesForDropShipments");
    if (UtilValidate.isEmpty(createSalesInvoicesForDropShipments))
        createSalesInvoicesForDropShipments = Boolean.FALSE;

    boolean salesShipmentFound = false;
    boolean purchaseShipmentFound = false;
    boolean dropShipmentFound = false;

    List<String> invoicesCreated = FastList.newInstance();

    //DEJ20060520: not used? planned to be used? List shipmentIdList = new LinkedList();
    for (String tmpShipmentId : shipmentIds) {
        try {//from   www .  j  a v a 2 s  . c  o  m
            GenericValue shipment = EntityQuery.use(delegator).from("Shipment")
                    .where("shipmentId", tmpShipmentId).queryOne();
            if ((shipment.getString("shipmentTypeId") != null)
                    && (shipment.getString("shipmentTypeId").equals("PURCHASE_SHIPMENT"))) {
                purchaseShipmentFound = true;
            } else if ((shipment.getString("shipmentTypeId") != null)
                    && (shipment.getString("shipmentTypeId").equals("DROP_SHIPMENT"))) {
                dropShipmentFound = true;
            } else {
                salesShipmentFound = true;
            }
            if (purchaseShipmentFound && salesShipmentFound && dropShipmentFound) {
                return ServiceUtil.returnError(UtilProperties.getMessage(
                        resource, "AccountingShipmentsOfDifferentTypes", UtilMisc.toMap("tmpShipmentId",
                                tmpShipmentId, "shipmentTypeId", shipment.getString("shipmentTypeId")),
                        locale));
            }
        } catch (GenericEntityException e) {
            Debug.logError(e, "Trouble getting Shipment entity for shipment " + tmpShipmentId, module);
            return ServiceUtil
                    .returnError(UtilProperties.getMessage(resource, "AccountingTroubleGettingShipmentEntity",
                            UtilMisc.toMap("tmpShipmentId", tmpShipmentId), locale));
        }
    }
    EntityQuery shipmentQuery = EntityQuery.use(delegator)
            .where(EntityCondition.makeCondition("shipmentId", EntityOperator.IN, shipmentIds))
            .orderBy("shipmentId");
    // check the status of the shipment

    // get the items of the shipment.  They can come from ItemIssuance if the shipment were from a sales order, ShipmentReceipt
    // if it were a purchase order or from the order items of the (possibly linked) orders if the shipment is a drop shipment
    List<GenericValue> items = null;
    List<GenericValue> orderItemAssocs = null;
    try {
        if (purchaseShipmentFound) {
            items = shipmentQuery.from("ShipmentReceipt").queryList();
            // filter out items which have been received but are not actually owned by an internal organization, so they should not be on a purchase invoice
            Iterator<GenericValue> itemsIter = items.iterator();
            while (itemsIter.hasNext()) {
                GenericValue item = itemsIter.next();
                GenericValue inventoryItem = item.getRelatedOne("InventoryItem", false);
                GenericValue ownerPartyRole = EntityQuery.use(delegator).from("PartyRole").where("partyId",
                        inventoryItem.get("ownerPartyId"), "roleTypeId", "INTERNAL_ORGANIZATIO").cache()
                        .queryOne();
                if (UtilValidate.isEmpty(ownerPartyRole)) {
                    itemsIter.remove();
                }
            }
        } else if (dropShipmentFound) {

            List<GenericValue> shipments = shipmentQuery.from("Shipment").queryList();

            // Get the list of purchase order IDs related to the shipments
            List<String> purchaseOrderIds = EntityUtil.getFieldListFromEntityList(shipments, "primaryOrderId",
                    true);

            if (createSalesInvoicesForDropShipments) {

                // If a sales invoice is being created for a drop shipment, we have to reference the original sales order items
                // Get the list of the linked orderIds (original sales orders)
                orderItemAssocs = EntityQuery.use(delegator).from("OrderItemAssoc")
                        .where(EntityCondition.makeCondition("toOrderId", EntityOperator.IN, purchaseOrderIds))
                        .queryList();

                // Get only the order items which are indirectly related to the purchase order - this limits the list to the drop ship group(s)
                items = EntityUtil.getRelated("FromOrderItem", null, orderItemAssocs, false);
            } else {

                // If it's a purchase invoice being created, the order items for that purchase orders can be used directly
                items = EntityQuery.use(delegator).from("OrderItem")
                        .where(EntityCondition.makeCondition("orderId", EntityOperator.IN, purchaseOrderIds))
                        .queryList();
            }
        } else {
            items = shipmentQuery.from("ItemIssuance").queryList();
        }
    } catch (GenericEntityException e) {
        Debug.logError(e, "Problem getting issued items from shipments", module);
        return ServiceUtil.returnError(
                UtilProperties.getMessage(resource, "AccountingProblemGettingItemsFromShipments", locale));
    }
    if (items.size() == 0) {
        Debug.logInfo("No items issued for shipments", module);
        return ServiceUtil.returnSuccess();
    }

    // group items by order
    Map<String, List<GenericValue>> shippedOrderItems = FastMap.newInstance();
    for (GenericValue item : items) {
        String orderId = item.getString("orderId");
        String orderItemSeqId = item.getString("orderItemSeqId");
        List<GenericValue> itemsByOrder = shippedOrderItems.get(orderId);
        if (itemsByOrder == null) {
            itemsByOrder = FastList.newInstance();
        }

        // check and make sure we haven't already billed for this issuance or shipment receipt
        List<EntityCondition> billFields = FastList.newInstance();
        billFields.add(EntityCondition.makeCondition("orderId", orderId));
        billFields.add(EntityCondition.makeCondition("orderItemSeqId", orderItemSeqId));
        billFields
                .add(EntityCondition.makeCondition("statusId", EntityOperator.NOT_EQUAL, "INVOICE_CANCELLED"));

        if (dropShipmentFound) {

            // Drop shipments have neither issuances nor receipts, so this check is meaningless
            itemsByOrder.add(item);
            shippedOrderItems.put(orderId, itemsByOrder);
            continue;
        } else if (item.getEntityName().equals("ItemIssuance")) {
            billFields.add(EntityCondition.makeCondition("itemIssuanceId", item.get("itemIssuanceId")));
        } else if (item.getEntityName().equals("ShipmentReceipt")) {
            billFields.add(EntityCondition.makeCondition("shipmentReceiptId", item.getString("receiptId")));
        }
        List<GenericValue> itemBillings = null;
        try {
            itemBillings = EntityQuery.use(delegator).from("OrderItemBillingAndInvoiceAndItem")
                    .where(billFields).queryList();
        } catch (GenericEntityException e) {
            Debug.logError(e, "Problem looking up OrderItemBilling records for " + billFields, module);
            return ServiceUtil.returnError(
                    UtilProperties.getMessage(resource, "AccountingProblemLookingUpOrderItemBilling",
                            UtilMisc.toMap("billFields", billFields), locale));
        }

        // if none found, then okay to bill
        if (itemBillings.size() == 0) {
            itemsByOrder.add(item);
        }

        // update the map with modified list
        shippedOrderItems.put(orderId, itemsByOrder);
    }

    // make sure we aren't billing items already invoiced i.e. items billed as digital (FINDIG)
    Set<String> orders = shippedOrderItems.keySet();
    for (String orderId : orders) {

        // we'll only use this list to figure out which ones to send
        List<GenericValue> billItems = shippedOrderItems.get(orderId);

        // a new list to be used to pass to the create invoice service
        List<GenericValue> toBillItems = FastList.newInstance();

        // map of available quantities so we only have to calc once
        Map<String, BigDecimal> itemQtyAvail = FastMap.newInstance();

        // now we will check each issuance and make sure it hasn't already been billed
        for (GenericValue issue : billItems) {
            BigDecimal issueQty = ZERO;

            if (issue.getEntityName().equals("ShipmentReceipt")) {
                issueQty = issue.getBigDecimal("quantityAccepted");
            } else {
                issueQty = issue.getBigDecimal("quantity");
            }

            BigDecimal billAvail = itemQtyAvail.get(issue.getString("orderItemSeqId"));
            if (billAvail == null) {
                List<EntityCondition> lookup = FastList.newInstance();
                lookup.add(EntityCondition.makeCondition("orderId", orderId));
                lookup.add(EntityCondition.makeCondition("orderItemSeqId", issue.get("orderItemSeqId")));
                lookup.add(EntityCondition.makeCondition("statusId", EntityOperator.NOT_EQUAL,
                        "INVOICE_CANCELLED"));
                GenericValue orderItem = null;
                List<GenericValue> billed = null;
                BigDecimal orderedQty = null;
                try {
                    orderItem = issue.getEntityName().equals("OrderItem") ? issue
                            : issue.getRelatedOne("OrderItem", false);

                    // total ordered
                    orderedQty = orderItem.getBigDecimal("quantity");

                    if (dropShipmentFound && createSalesInvoicesForDropShipments.booleanValue()) {

                        // Override the issueQty with the quantity from the purchase order item
                        GenericValue orderItemAssoc = EntityUtil.getFirst(EntityUtil.filterByAnd(
                                orderItemAssocs, UtilMisc.toMap("orderId", issue.getString("orderId"),
                                        "orderItemSeqId", issue.getString("orderItemSeqId"))));
                        GenericValue purchaseOrderItem = orderItemAssoc.getRelatedOne("ToOrderItem", false);
                        orderItem.set("quantity", purchaseOrderItem.getBigDecimal("quantity"));
                        issueQty = purchaseOrderItem.getBigDecimal("quantity");
                    }
                    billed = EntityQuery.use(delegator).from("OrderItemBillingAndInvoiceAndItem").where(lookup)
                            .queryList();
                } catch (GenericEntityException e) {
                    Debug.logError(e, "Problem getting OrderItem/OrderItemBilling records " + lookup, module);
                    return ServiceUtil.returnError(UtilProperties.getMessage(resource,
                            "AccountingProblemGettingOrderItemOrderItemBilling",
                            UtilMisc.toMap("lookup", lookup), locale));
                }

                // add up the already billed total
                if (billed.size() > 0) {
                    BigDecimal billedQuantity = ZERO;
                    for (GenericValue oib : billed) {
                        BigDecimal qty = oib.getBigDecimal("quantity");
                        if (qty != null) {
                            billedQuantity = billedQuantity.add(qty).setScale(DECIMALS, ROUNDING);
                        }
                    }
                    BigDecimal leftToBill = orderedQty.subtract(billedQuantity).setScale(DECIMALS, ROUNDING);
                    billAvail = leftToBill;
                } else {
                    billAvail = orderedQty;
                }
            }

            // no available means we cannot bill anymore
            if (billAvail != null && billAvail.signum() == 1) { // this checks if billAvail is a positive non-zero number
                if (issueQty != null && issueQty.compareTo(billAvail) > 0) {
                    // can only bill some of the issuance; others have been billed already
                    if ("ShipmentReceipt".equals(issue.getEntityName())) {
                        issue.set("quantityAccepted", billAvail);
                    } else {
                        issue.set("quantity", billAvail);
                    }
                    billAvail = ZERO;
                } else {
                    // now have been billed
                    billAvail = billAvail.subtract(issueQty).setScale(DECIMALS, ROUNDING);
                }

                // okay to bill these items; but none else
                toBillItems.add(issue);
            }

            // update the available to bill quantity for the next pass
            itemQtyAvail.put(issue.getString("orderItemSeqId"), billAvail);
        }

        OrderReadHelper orh = new OrderReadHelper(delegator, orderId);

        GenericValue productStore = orh.getProductStore();
        String prorateShipping = productStore != null ? productStore.getString("prorateShipping") : "N";

        // If shipping charges are not prorated, the shipments need to be examined for additional shipping charges
        if ("N".equalsIgnoreCase(prorateShipping)) {

            // Get the set of filtered shipments
            List<GenericValue> invoiceableShipments = null;
            try {
                if (dropShipmentFound) {

                    List<String> invoiceablePrimaryOrderIds = null;
                    if (createSalesInvoicesForDropShipments) {

                        // If a sales invoice is being created for the drop shipment, we need to reference back to the original purchase order IDs

                        // Get the IDs for orders which have billable items
                        List<String> invoiceableLinkedOrderIds = EntityUtil
                                .getFieldListFromEntityList(toBillItems, "orderId", true);

                        // Get back the IDs of the purchase orders - this will be a list of the purchase order items which are billable by virtue of not having been
                        //  invoiced in a previous sales invoice
                        List<GenericValue> reverseOrderItemAssocs = EntityUtil
                                .filterByCondition(orderItemAssocs, EntityCondition.makeCondition("orderId",
                                        EntityOperator.IN, invoiceableLinkedOrderIds));
                        invoiceablePrimaryOrderIds = EntityUtil
                                .getFieldListFromEntityList(reverseOrderItemAssocs, "toOrderId", true);

                    } else {

                        // If a purchase order is being created for a drop shipment, the purchase order IDs can be used directly
                        invoiceablePrimaryOrderIds = EntityUtil.getFieldListFromEntityList(toBillItems,
                                "orderId", true);

                    }

                    // Get the list of shipments which are associated with the filtered purchase orders
                    if (!UtilValidate.isEmpty(invoiceablePrimaryOrderIds)) {
                        invoiceableShipments = EntityQuery.use(delegator).from("Shipment")
                                .where(UtilMisc.toList(
                                        EntityCondition.makeCondition("primaryOrderId", EntityOperator.IN,
                                                invoiceablePrimaryOrderIds),
                                        EntityCondition.makeCondition("shipmentId", EntityOperator.IN,
                                                shipmentIds)))
                                .queryList();
                    }
                } else {
                    List<String> invoiceableShipmentIds = EntityUtil.getFieldListFromEntityList(toBillItems,
                            "shipmentId", true);
                    if (UtilValidate.isNotEmpty(invoiceableShipmentIds)) {
                        invoiceableShipments = EntityQuery
                                .use(delegator).from("Shipment").where(EntityCondition
                                        .makeCondition("shipmentId", EntityOperator.IN, invoiceableShipmentIds))
                                .queryList();
                    }
                }
            } catch (GenericEntityException e) {
                Debug.logError(e, "Trouble calling createInvoicesFromShipments service", module);
                return ServiceUtil.returnError(UtilProperties.getMessage(resource,
                        "AccountingTroubleCallingCreateInvoicesFromShipmentsService", locale));
            }

            // Total the additional shipping charges for the shipments
            Map<GenericValue, BigDecimal> additionalShippingCharges = FastMap.newInstance();
            BigDecimal totalAdditionalShippingCharges = ZERO;
            if (UtilValidate.isNotEmpty(invoiceableShipments)) {
                for (GenericValue shipment : invoiceableShipments) {
                    if (shipment.get("additionalShippingCharge") == null)
                        continue;
                    BigDecimal shipmentAdditionalShippingCharges = shipment
                            .getBigDecimal("additionalShippingCharge").setScale(DECIMALS, ROUNDING);
                    additionalShippingCharges.put(shipment, shipmentAdditionalShippingCharges);
                    totalAdditionalShippingCharges = totalAdditionalShippingCharges
                            .add(shipmentAdditionalShippingCharges);
                }
            }

            // If the additional shipping charges are greater than zero, process them
            if (totalAdditionalShippingCharges.signum() == 1) {

                // Add an OrderAdjustment to the order for each additional shipping charge
                for (Map.Entry<GenericValue, BigDecimal> entry : additionalShippingCharges.entrySet()) {
                    GenericValue shipment = entry.getKey();
                    BigDecimal additionalShippingCharge = entry.getValue();
                    String shipmentId = shipment.getString("shipmentId");
                    Map<String, Object> createOrderAdjustmentContext = FastMap.newInstance();
                    createOrderAdjustmentContext.put("orderId", orderId);
                    createOrderAdjustmentContext.put("orderAdjustmentTypeId", "SHIPPING_CHARGES");
                    String addtlChargeDescription = shipment.getString("addtlShippingChargeDesc");
                    if (UtilValidate.isEmpty(addtlChargeDescription)) {
                        addtlChargeDescription = UtilProperties.getMessage(resource,
                                "AccountingAdditionalShippingChargeForShipment",
                                UtilMisc.toMap("shipmentId", shipmentId), locale);
                    }
                    createOrderAdjustmentContext.put("description", addtlChargeDescription);
                    createOrderAdjustmentContext.put("sourceReferenceId", shipmentId);
                    createOrderAdjustmentContext.put("amount", additionalShippingCharge);
                    createOrderAdjustmentContext.put("userLogin", context.get("userLogin"));
                    String shippingOrderAdjustmentId = null;
                    try {
                        Map<String, Object> createOrderAdjustmentResult = dispatcher
                                .runSync("createOrderAdjustment", createOrderAdjustmentContext);
                        shippingOrderAdjustmentId = (String) createOrderAdjustmentResult
                                .get("orderAdjustmentId");
                    } catch (GenericServiceException e) {
                        Debug.logError(e, "Trouble calling createOrderAdjustment service", module);
                        return ServiceUtil.returnError(UtilProperties.getMessage(resource,
                                "AccountingTroubleCallingCreateOrderAdjustmentService", locale));
                    }

                    // Obtain a list of OrderAdjustments due to tax on the shipping charges, if any
                    GenericValue billToParty = orh.getBillToParty();
                    GenericValue payToParty = orh.getBillFromParty();
                    GenericValue destinationContactMech = null;
                    try {
                        destinationContactMech = shipment.getRelatedOne("DestinationPostalAddress", false);
                    } catch (GenericEntityException e) {
                        Debug.logError(e,
                                "Trouble calling createInvoicesFromShipment service; invoice not created for shipment "
                                        + shipmentId,
                                module);
                        return ServiceUtil.returnError(UtilProperties.getMessage(resource,
                                "AccountingTroubleCallingCreateInvoicesFromShipmentService", locale));
                    }

                    List<Object> emptyList = FastList.newInstance();
                    Map<String, Object> calcTaxContext = FastMap.newInstance();
                    calcTaxContext.put("productStoreId", orh.getProductStoreId());
                    calcTaxContext.put("payToPartyId", payToParty.getString("partyId"));
                    calcTaxContext.put("billToPartyId", billToParty.getString("partyId"));
                    calcTaxContext.put("orderShippingAmount", totalAdditionalShippingCharges);
                    calcTaxContext.put("shippingAddress", destinationContactMech);

                    // These parameters don't matter if we're only worried about adjustments on the shipping charges
                    calcTaxContext.put("itemProductList", emptyList);
                    calcTaxContext.put("itemAmountList", emptyList);
                    calcTaxContext.put("itemPriceList", emptyList);
                    calcTaxContext.put("itemQuantityList", emptyList);
                    calcTaxContext.put("itemShippingList", emptyList);

                    Map<String, Object> calcTaxResult = null;
                    try {
                        calcTaxResult = dispatcher.runSync("calcTax", calcTaxContext);
                    } catch (GenericServiceException e) {
                        Debug.logError(e, "Trouble calling calcTaxService", module);
                        return ServiceUtil.returnError(UtilProperties.getMessage(resource,
                                "AccountingTroubleCallingCalcTaxService", locale));
                    }
                    List<GenericValue> orderAdjustments = UtilGenerics
                            .checkList(calcTaxResult.get("orderAdjustments"));

                    // If we have any OrderAdjustments due to tax on shipping, store them and add them to the total
                    if (orderAdjustments != null) {
                        for (GenericValue orderAdjustment : orderAdjustments) {
                            totalAdditionalShippingCharges = totalAdditionalShippingCharges
                                    .add(orderAdjustment.getBigDecimal("amount").setScale(DECIMALS, ROUNDING));
                            orderAdjustment.set("orderAdjustmentId", delegator.getNextSeqId("OrderAdjustment"));
                            orderAdjustment.set("orderId", orderId);
                            orderAdjustment.set("orderItemSeqId", "_NA_");
                            orderAdjustment.set("shipGroupSeqId", shipment.getString("primaryShipGroupSeqId"));
                            orderAdjustment.set("originalAdjustmentId", shippingOrderAdjustmentId);
                        }
                        try {
                            delegator.storeAll(orderAdjustments);
                        } catch (GenericEntityException e) {
                            Debug.logError(e, "Problem storing OrderAdjustments: " + orderAdjustments, module);
                            return ServiceUtil.returnError(UtilProperties.getMessage(resource,
                                    "AccountingProblemStoringOrderAdjustments",
                                    UtilMisc.toMap("orderAdjustments", orderAdjustments), locale));
                        }
                    }

                    // If part of the order was paid via credit card, try to charge it for the additional shipping
                    List<GenericValue> orderPaymentPreferences = null;
                    try {
                        orderPaymentPreferences = EntityQuery.use(delegator).from("OrderPaymentPreference")
                                .where("orderId", orderId, "paymentMethodTypeId", "CREDIT_CARD").queryList();
                    } catch (GenericEntityException e) {
                        Debug.logError(e, "Problem getting OrderPaymentPreference records", module);
                        return ServiceUtil.returnError(UtilProperties.getMessage(resource,
                                "AccountingProblemGettingOrderPaymentPreferences", locale));
                    }

                    //  Use the first credit card we find, for the sake of simplicity
                    String paymentMethodId = null;
                    GenericValue cardOrderPaymentPref = EntityUtil.getFirst(orderPaymentPreferences);
                    if (cardOrderPaymentPref != null) {
                        paymentMethodId = cardOrderPaymentPref.getString("paymentMethodId");
                    }

                    if (paymentMethodId != null) {

                        // Release all outstanding (not settled or cancelled) authorizations, while keeping a running
                        //  total of their amounts so that the total plus the additional shipping charges can be authorized again
                        //  all at once.
                        BigDecimal totalNewAuthAmount = totalAdditionalShippingCharges.setScale(DECIMALS,
                                ROUNDING);
                        for (GenericValue orderPaymentPreference : orderPaymentPreferences) {
                            if (!(orderPaymentPreference.getString("statusId").equals("PAYMENT_SETTLED")
                                    || orderPaymentPreference.getString("statusId")
                                            .equals("PAYMENT_CANCELLED"))) {
                                GenericValue authTransaction = PaymentGatewayServices
                                        .getAuthTransaction(orderPaymentPreference);
                                if (authTransaction != null && authTransaction.get("amount") != null) {

                                    // Update the total authorized amount
                                    totalNewAuthAmount = totalNewAuthAmount.add(authTransaction
                                            .getBigDecimal("amount").setScale(DECIMALS, ROUNDING));

                                    // Release the authorization for the OrderPaymentPreference
                                    Map<String, Object> prefReleaseResult = null;
                                    try {
                                        prefReleaseResult = dispatcher.runSync("releaseOrderPaymentPreference",
                                                UtilMisc.toMap("orderPaymentPreferenceId",
                                                        orderPaymentPreference
                                                                .getString("orderPaymentPreferenceId"),
                                                        "userLogin", context.get("userLogin")));
                                    } catch (GenericServiceException e) {
                                        Debug.logError(e,
                                                "Trouble calling releaseOrderPaymentPreference service",
                                                module);
                                        return ServiceUtil.returnError(UtilProperties.getMessage(resource,
                                                "AccountingTroubleCallingReleaseOrderPaymentPreferenceService",
                                                locale));
                                    }
                                    if (ServiceUtil.isError(prefReleaseResult)
                                            || ServiceUtil.isFailure(prefReleaseResult)) {
                                        String errMsg = ServiceUtil.getErrorMessage(prefReleaseResult);
                                        Debug.logError(errMsg, module);
                                        return ServiceUtil.returnError(errMsg);
                                    }
                                }
                            }
                        }

                        // Create a new OrderPaymentPreference for the order to handle the new (totalled) charge. Don't
                        //  set the maxAmount so that it doesn't interfere with other authorizations
                        Map<String, Object> serviceContext = UtilMisc.toMap("orderId", orderId,
                                "paymentMethodId", paymentMethodId, "paymentMethodTypeId", "CREDIT_CARD",
                                "userLogin", context.get("userLogin"));
                        String orderPaymentPreferenceId = null;
                        try {
                            Map<String, Object> result = dispatcher.runSync("createOrderPaymentPreference",
                                    serviceContext);
                            orderPaymentPreferenceId = (String) result.get("orderPaymentPreferenceId");
                        } catch (GenericServiceException e) {
                            Debug.logError(e, "Trouble calling createOrderPaymentPreference service", module);
                            return ServiceUtil.returnError(UtilProperties.getMessage(resource,
                                    "AccountingTroubleCallingCreateOrderPaymentPreferenceService", locale));
                        }

                        // Attempt to authorize the new orderPaymentPreference
                        Map<String, Object> authResult = null;
                        try {
                            // Use an overrideAmount because the maxAmount wasn't set on the OrderPaymentPreference
                            authResult = dispatcher.runSync("authOrderPaymentPreference",
                                    UtilMisc.toMap("orderPaymentPreferenceId", orderPaymentPreferenceId,
                                            "overrideAmount", totalNewAuthAmount, "userLogin",
                                            context.get("userLogin")));
                        } catch (GenericServiceException e) {
                            Debug.logError(e, "Trouble calling authOrderPaymentPreference service", module);
                            return ServiceUtil.returnError(UtilProperties.getMessage(resource,
                                    "AccountingTroubleCallingAuthOrderPaymentPreferenceService", locale));
                        }

                        // If the authorization fails, create the invoice anyway, but make a note of it
                        boolean authFinished = ((Boolean) authResult.get("finished")).booleanValue();
                        boolean authErrors = ((Boolean) authResult.get("errors")).booleanValue();
                        if (authErrors || !authFinished) {
                            String errMsg = UtilProperties.getMessage(resource,
                                    "AccountingUnableToAuthAdditionalShipCharges",
                                    UtilMisc.toMap("shipmentId", shipmentId, "paymentMethodId", paymentMethodId,
                                            "orderPaymentPreferenceId", orderPaymentPreferenceId),
                                    locale);
                            Debug.logError(errMsg, module);
                        }

                    }
                }
            }
        } else {
            Debug.logInfo(UtilProperties.getMessage(resource, "AccountingIgnoringAdditionalShipCharges",
                    UtilMisc.toMap("productStoreId", orh.getProductStoreId()), locale), module);
        }

        String invoiceId = null;
        GenericValue shipmentItemBilling = null;
        String shipmentId = shipmentIds.get(0);
        try {
            shipmentItemBilling = EntityQuery.use(delegator).from("ShipmentItemBilling")
                    .where("shipmentId", shipmentId).queryFirst();
        } catch (GenericEntityException e) {
            return ServiceUtil.returnError(
                    UtilProperties.getMessage(resource, "AccountingProblemGettingShipmentItemBilling", locale));
        }
        if (shipmentItemBilling != null) {
            invoiceId = shipmentItemBilling.getString("invoiceId");
        }

        // call the createInvoiceForOrder service for each order
        Map<String, Object> serviceContext = UtilMisc.toMap("orderId", orderId, "billItems", toBillItems,
                "invoiceId", invoiceId, "eventDate", context.get("eventDate"), "userLogin",
                context.get("userLogin"));
        try {
            Map<String, Object> result = dispatcher.runSync("createInvoiceForOrder", serviceContext);
            invoicesCreated.add((String) result.get("invoiceId"));
        } catch (GenericServiceException e) {
            Debug.logError(e, "Trouble calling createInvoiceForOrder service; invoice not created for shipment",
                    module);
            return ServiceUtil.returnError(UtilProperties.getMessage(resource,
                    "AccountingTroubleCallingCreateInvoiceForOrderService", locale));
        }
    }

    Map<String, Object> response = ServiceUtil.returnSuccess();
    response.put("invoicesCreated", invoicesCreated);
    return response;
}

From source file:org.apache.ofbiz.accounting.invoice.InvoiceServices.java

public static Map<String, Object> createInvoicesFromShipments(DispatchContext dctx,
        Map<String, ? extends Object> context) {
    Delegator delegator = dctx.getDelegator();
    LocalDispatcher dispatcher = dctx.getDispatcher();
    List<String> shipmentIds = UtilGenerics.checkList(context.get("shipmentIds"));
    Locale locale = (Locale) context.get("locale");
    Boolean createSalesInvoicesForDropShipments = (Boolean) context.get("createSalesInvoicesForDropShipments");
    if (UtilValidate.isEmpty(createSalesInvoicesForDropShipments))
        createSalesInvoicesForDropShipments = Boolean.FALSE;

    boolean salesShipmentFound = false;
    boolean purchaseShipmentFound = false;
    boolean dropShipmentFound = false;

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

    //DEJ20060520: not used? planned to be used? List shipmentIdList = new LinkedList();
    for (String tmpShipmentId : shipmentIds) {
        try {//from ww  w.  ja v a2  s  .  c om
            GenericValue shipment = EntityQuery.use(delegator).from("Shipment")
                    .where("shipmentId", tmpShipmentId).queryOne();
            if ((shipment.getString("shipmentTypeId") != null)
                    && (shipment.getString("shipmentTypeId").equals("PURCHASE_SHIPMENT"))) {
                purchaseShipmentFound = true;
            } else if ((shipment.getString("shipmentTypeId") != null)
                    && (shipment.getString("shipmentTypeId").equals("DROP_SHIPMENT"))) {
                dropShipmentFound = true;
            } else {
                salesShipmentFound = true;
            }
            if (purchaseShipmentFound && salesShipmentFound && dropShipmentFound) {
                return ServiceUtil.returnError(UtilProperties.getMessage(
                        resource, "AccountingShipmentsOfDifferentTypes", UtilMisc.toMap("tmpShipmentId",
                                tmpShipmentId, "shipmentTypeId", shipment.getString("shipmentTypeId")),
                        locale));
            }
        } catch (GenericEntityException e) {
            Debug.logError(e, "Trouble getting Shipment entity for shipment " + tmpShipmentId, module);
            return ServiceUtil
                    .returnError(UtilProperties.getMessage(resource, "AccountingTroubleGettingShipmentEntity",
                            UtilMisc.toMap("tmpShipmentId", tmpShipmentId), locale));
        }
    }
    EntityQuery shipmentQuery = EntityQuery.use(delegator)
            .where(EntityCondition.makeCondition("shipmentId", EntityOperator.IN, shipmentIds))
            .orderBy("shipmentId");
    // check the status of the shipment

    // get the items of the shipment.  They can come from ItemIssuance if the shipment were from a sales order, ShipmentReceipt
    // if it were a purchase order or from the order items of the (possibly linked) orders if the shipment is a drop shipment
    List<GenericValue> items = null;
    List<GenericValue> orderItemAssocs = null;
    try {
        if (purchaseShipmentFound) {
            items = shipmentQuery.from("ShipmentReceipt").queryList();
            // filter out items which have been received but are not actually owned by an internal organization, so they should not be on a purchase invoice
            Iterator<GenericValue> itemsIter = items.iterator();
            while (itemsIter.hasNext()) {
                GenericValue item = itemsIter.next();
                GenericValue inventoryItem = item.getRelatedOne("InventoryItem", false);
                GenericValue ownerPartyRole = EntityQuery.use(delegator).from("PartyRole").where("partyId",
                        inventoryItem.get("ownerPartyId"), "roleTypeId", "INTERNAL_ORGANIZATIO").cache()
                        .queryOne();
                if (UtilValidate.isEmpty(ownerPartyRole)) {
                    itemsIter.remove();
                }
            }
        } else if (dropShipmentFound) {

            List<GenericValue> shipments = shipmentQuery.from("Shipment").queryList();

            // Get the list of purchase order IDs related to the shipments
            List<String> purchaseOrderIds = EntityUtil.getFieldListFromEntityList(shipments, "primaryOrderId",
                    true);

            if (createSalesInvoicesForDropShipments) {

                // If a sales invoice is being created for a drop shipment, we have to reference the original sales order items
                // Get the list of the linked orderIds (original sales orders)
                orderItemAssocs = EntityQuery.use(delegator).from("OrderItemAssoc")
                        .where(EntityCondition.makeCondition("toOrderId", EntityOperator.IN, purchaseOrderIds))
                        .queryList();

                // Get only the order items which are indirectly related to the purchase order - this limits the list to the drop ship group(s)
                items = EntityUtil.getRelated("FromOrderItem", null, orderItemAssocs, false);
            } else {

                // If it's a purchase invoice being created, the order items for that purchase orders can be used directly
                items = EntityQuery.use(delegator).from("OrderItem")
                        .where(EntityCondition.makeCondition("orderId", EntityOperator.IN, purchaseOrderIds))
                        .queryList();
            }
        } else {
            items = shipmentQuery.from("ItemIssuance").queryList();
        }
    } catch (GenericEntityException e) {
        Debug.logError(e, "Problem getting issued items from shipments", module);
        return ServiceUtil.returnError(
                UtilProperties.getMessage(resource, "AccountingProblemGettingItemsFromShipments", locale));
    }
    if (items.size() == 0) {
        Debug.logInfo("No items issued for shipments", module);
        return ServiceUtil.returnSuccess();
    }

    // group items by order
    Map<String, List<GenericValue>> shippedOrderItems = new HashMap<String, List<GenericValue>>();
    for (GenericValue item : items) {
        String orderId = item.getString("orderId");
        String orderItemSeqId = item.getString("orderItemSeqId");
        List<GenericValue> itemsByOrder = shippedOrderItems.get(orderId);
        if (itemsByOrder == null) {
            itemsByOrder = new LinkedList<GenericValue>();
        }

        // check and make sure we haven't already billed for this issuance or shipment receipt
        List<EntityCondition> billFields = new LinkedList<EntityCondition>();
        billFields.add(EntityCondition.makeCondition("orderId", orderId));
        billFields.add(EntityCondition.makeCondition("orderItemSeqId", orderItemSeqId));
        billFields
                .add(EntityCondition.makeCondition("statusId", EntityOperator.NOT_EQUAL, "INVOICE_CANCELLED"));

        if (dropShipmentFound) {

            // Drop shipments have neither issuances nor receipts, so this check is meaningless
            itemsByOrder.add(item);
            shippedOrderItems.put(orderId, itemsByOrder);
            continue;
        } else if (item.getEntityName().equals("ItemIssuance")) {
            billFields.add(EntityCondition.makeCondition("itemIssuanceId", item.get("itemIssuanceId")));
        } else if (item.getEntityName().equals("ShipmentReceipt")) {
            billFields.add(EntityCondition.makeCondition("shipmentReceiptId", item.getString("receiptId")));
        }
        List<GenericValue> itemBillings = null;
        try {
            itemBillings = EntityQuery.use(delegator).from("OrderItemBillingAndInvoiceAndItem")
                    .where(billFields).queryList();
        } catch (GenericEntityException e) {
            Debug.logError(e, "Problem looking up OrderItemBilling records for " + billFields, module);
            return ServiceUtil.returnError(
                    UtilProperties.getMessage(resource, "AccountingProblemLookingUpOrderItemBilling",
                            UtilMisc.toMap("billFields", billFields), locale));
        }

        // if none found, then okay to bill
        if (itemBillings.size() == 0) {
            itemsByOrder.add(item);
        }

        // update the map with modified list
        shippedOrderItems.put(orderId, itemsByOrder);
    }

    // make sure we aren't billing items already invoiced i.e. items billed as digital (FINDIG)
    Set<String> orders = shippedOrderItems.keySet();
    for (String orderId : orders) {

        // we'll only use this list to figure out which ones to send
        List<GenericValue> billItems = shippedOrderItems.get(orderId);

        // a new list to be used to pass to the create invoice service
        List<GenericValue> toBillItems = new LinkedList<GenericValue>();

        // map of available quantities so we only have to calc once
        Map<String, BigDecimal> itemQtyAvail = new HashMap<String, BigDecimal>();

        // now we will check each issuance and make sure it hasn't already been billed
        for (GenericValue issue : billItems) {
            BigDecimal issueQty = ZERO;

            if (issue.getEntityName().equals("ShipmentReceipt")) {
                issueQty = issue.getBigDecimal("quantityAccepted");
            } else {
                issueQty = issue.getBigDecimal("quantity");
            }

            BigDecimal billAvail = itemQtyAvail.get(issue.getString("orderItemSeqId"));
            if (billAvail == null) {
                List<EntityCondition> lookup = new LinkedList<EntityCondition>();
                lookup.add(EntityCondition.makeCondition("orderId", orderId));
                lookup.add(EntityCondition.makeCondition("orderItemSeqId", issue.get("orderItemSeqId")));
                lookup.add(EntityCondition.makeCondition("statusId", EntityOperator.NOT_EQUAL,
                        "INVOICE_CANCELLED"));
                GenericValue orderItem = null;
                List<GenericValue> billed = null;
                BigDecimal orderedQty = null;
                try {
                    orderItem = issue.getEntityName().equals("OrderItem") ? issue
                            : issue.getRelatedOne("OrderItem", false);

                    // total ordered
                    orderedQty = orderItem.getBigDecimal("quantity");

                    if (dropShipmentFound && createSalesInvoicesForDropShipments.booleanValue()) {

                        // Override the issueQty with the quantity from the purchase order item
                        GenericValue orderItemAssoc = EntityUtil.getFirst(EntityUtil.filterByAnd(
                                orderItemAssocs, UtilMisc.toMap("orderId", issue.getString("orderId"),
                                        "orderItemSeqId", issue.getString("orderItemSeqId"))));
                        GenericValue purchaseOrderItem = orderItemAssoc.getRelatedOne("ToOrderItem", false);
                        orderItem.set("quantity", purchaseOrderItem.getBigDecimal("quantity"));
                        issueQty = purchaseOrderItem.getBigDecimal("quantity");
                    }
                    billed = EntityQuery.use(delegator).from("OrderItemBillingAndInvoiceAndItem").where(lookup)
                            .queryList();
                } catch (GenericEntityException e) {
                    Debug.logError(e, "Problem getting OrderItem/OrderItemBilling records " + lookup, module);
                    return ServiceUtil.returnError(UtilProperties.getMessage(resource,
                            "AccountingProblemGettingOrderItemOrderItemBilling",
                            UtilMisc.toMap("lookup", lookup), locale));
                }

                // add up the already billed total
                if (billed.size() > 0) {
                    BigDecimal billedQuantity = ZERO;
                    for (GenericValue oib : billed) {
                        BigDecimal qty = oib.getBigDecimal("quantity");
                        if (qty != null) {
                            billedQuantity = billedQuantity.add(qty).setScale(DECIMALS, ROUNDING);
                        }
                    }
                    BigDecimal leftToBill = orderedQty.subtract(billedQuantity).setScale(DECIMALS, ROUNDING);
                    billAvail = leftToBill;
                } else {
                    billAvail = orderedQty;
                }
            }

            // no available means we cannot bill anymore
            if (billAvail != null && billAvail.signum() == 1) { // this checks if billAvail is a positive non-zero number
                if (issueQty != null && issueQty.compareTo(billAvail) > 0) {
                    // can only bill some of the issuance; others have been billed already
                    if ("ShipmentReceipt".equals(issue.getEntityName())) {
                        issue.set("quantityAccepted", billAvail);
                    } else {
                        issue.set("quantity", billAvail);
                    }
                    billAvail = ZERO;
                } else {
                    // now have been billed
                    billAvail = billAvail.subtract(issueQty).setScale(DECIMALS, ROUNDING);
                }

                // okay to bill these items; but none else
                toBillItems.add(issue);
            }

            // update the available to bill quantity for the next pass
            itemQtyAvail.put(issue.getString("orderItemSeqId"), billAvail);
        }

        OrderReadHelper orh = new OrderReadHelper(delegator, orderId);

        GenericValue productStore = orh.getProductStore();
        String prorateShipping = productStore != null ? productStore.getString("prorateShipping") : "N";

        // If shipping charges are not prorated, the shipments need to be examined for additional shipping charges
        if ("N".equalsIgnoreCase(prorateShipping)) {

            // Get the set of filtered shipments
            List<GenericValue> invoiceableShipments = null;
            try {
                if (dropShipmentFound) {

                    List<String> invoiceablePrimaryOrderIds = null;
                    if (createSalesInvoicesForDropShipments) {

                        // If a sales invoice is being created for the drop shipment, we need to reference back to the original purchase order IDs

                        // Get the IDs for orders which have billable items
                        List<String> invoiceableLinkedOrderIds = EntityUtil
                                .getFieldListFromEntityList(toBillItems, "orderId", true);

                        // Get back the IDs of the purchase orders - this will be a list of the purchase order items which are billable by virtue of not having been
                        //  invoiced in a previous sales invoice
                        List<GenericValue> reverseOrderItemAssocs = EntityUtil
                                .filterByCondition(orderItemAssocs, EntityCondition.makeCondition("orderId",
                                        EntityOperator.IN, invoiceableLinkedOrderIds));
                        invoiceablePrimaryOrderIds = EntityUtil
                                .getFieldListFromEntityList(reverseOrderItemAssocs, "toOrderId", true);

                    } else {

                        // If a purchase order is being created for a drop shipment, the purchase order IDs can be used directly
                        invoiceablePrimaryOrderIds = EntityUtil.getFieldListFromEntityList(toBillItems,
                                "orderId", true);

                    }

                    // Get the list of shipments which are associated with the filtered purchase orders
                    if (!UtilValidate.isEmpty(invoiceablePrimaryOrderIds)) {
                        invoiceableShipments = EntityQuery.use(delegator).from("Shipment")
                                .where(UtilMisc.toList(
                                        EntityCondition.makeCondition("primaryOrderId", EntityOperator.IN,
                                                invoiceablePrimaryOrderIds),
                                        EntityCondition.makeCondition("shipmentId", EntityOperator.IN,
                                                shipmentIds)))
                                .queryList();
                    }
                } else {
                    List<String> invoiceableShipmentIds = EntityUtil.getFieldListFromEntityList(toBillItems,
                            "shipmentId", true);
                    if (UtilValidate.isNotEmpty(invoiceableShipmentIds)) {
                        invoiceableShipments = EntityQuery
                                .use(delegator).from("Shipment").where(EntityCondition
                                        .makeCondition("shipmentId", EntityOperator.IN, invoiceableShipmentIds))
                                .queryList();
                    }
                }
            } catch (GenericEntityException e) {
                Debug.logError(e, "Trouble calling createInvoicesFromShipments service", module);
                return ServiceUtil.returnError(UtilProperties.getMessage(resource,
                        "AccountingTroubleCallingCreateInvoicesFromShipmentsService", locale));
            }

            // Total the additional shipping charges for the shipments
            Map<GenericValue, BigDecimal> additionalShippingCharges = new HashMap<GenericValue, BigDecimal>();
            BigDecimal totalAdditionalShippingCharges = ZERO;
            if (UtilValidate.isNotEmpty(invoiceableShipments)) {
                for (GenericValue shipment : invoiceableShipments) {
                    if (shipment.get("additionalShippingCharge") == null)
                        continue;
                    BigDecimal shipmentAdditionalShippingCharges = shipment
                            .getBigDecimal("additionalShippingCharge").setScale(DECIMALS, ROUNDING);
                    additionalShippingCharges.put(shipment, shipmentAdditionalShippingCharges);
                    totalAdditionalShippingCharges = totalAdditionalShippingCharges
                            .add(shipmentAdditionalShippingCharges);
                }
            }

            // If the additional shipping charges are greater than zero, process them
            if (totalAdditionalShippingCharges.signum() == 1) {

                // Add an OrderAdjustment to the order for each additional shipping charge
                for (Map.Entry<GenericValue, BigDecimal> entry : additionalShippingCharges.entrySet()) {
                    GenericValue shipment = entry.getKey();
                    BigDecimal additionalShippingCharge = entry.getValue();
                    String shipmentId = shipment.getString("shipmentId");
                    Map<String, Object> createOrderAdjustmentContext = new HashMap<String, Object>();
                    createOrderAdjustmentContext.put("orderId", orderId);
                    createOrderAdjustmentContext.put("orderAdjustmentTypeId", "SHIPPING_CHARGES");
                    String addtlChargeDescription = shipment.getString("addtlShippingChargeDesc");
                    if (UtilValidate.isEmpty(addtlChargeDescription)) {
                        addtlChargeDescription = UtilProperties.getMessage(resource,
                                "AccountingAdditionalShippingChargeForShipment",
                                UtilMisc.toMap("shipmentId", shipmentId), locale);
                    }
                    createOrderAdjustmentContext.put("description", addtlChargeDescription);
                    createOrderAdjustmentContext.put("sourceReferenceId", shipmentId);
                    createOrderAdjustmentContext.put("amount", additionalShippingCharge);
                    createOrderAdjustmentContext.put("userLogin", context.get("userLogin"));
                    String shippingOrderAdjustmentId = null;
                    try {
                        Map<String, Object> createOrderAdjustmentResult = dispatcher
                                .runSync("createOrderAdjustment", createOrderAdjustmentContext);
                        shippingOrderAdjustmentId = (String) createOrderAdjustmentResult
                                .get("orderAdjustmentId");
                    } catch (GenericServiceException e) {
                        Debug.logError(e, "Trouble calling createOrderAdjustment service", module);
                        return ServiceUtil.returnError(UtilProperties.getMessage(resource,
                                "AccountingTroubleCallingCreateOrderAdjustmentService", locale));
                    }

                    // Obtain a list of OrderAdjustments due to tax on the shipping charges, if any
                    GenericValue billToParty = orh.getBillToParty();
                    GenericValue payToParty = orh.getBillFromParty();
                    GenericValue destinationContactMech = null;
                    try {
                        destinationContactMech = shipment.getRelatedOne("DestinationPostalAddress", false);
                    } catch (GenericEntityException e) {
                        Debug.logError(e,
                                "Trouble calling createInvoicesFromShipment service; invoice not created for shipment "
                                        + shipmentId,
                                module);
                        return ServiceUtil.returnError(UtilProperties.getMessage(resource,
                                "AccountingTroubleCallingCreateInvoicesFromShipmentService", locale));
                    }

                    List<Object> emptyList = new LinkedList<Object>();
                    Map<String, Object> calcTaxContext = new HashMap<String, Object>();
                    calcTaxContext.put("productStoreId", orh.getProductStoreId());
                    calcTaxContext.put("payToPartyId", payToParty.getString("partyId"));
                    calcTaxContext.put("billToPartyId", billToParty.getString("partyId"));
                    calcTaxContext.put("orderShippingAmount", totalAdditionalShippingCharges);
                    calcTaxContext.put("shippingAddress", destinationContactMech);

                    // These parameters don't matter if we're only worried about adjustments on the shipping charges
                    calcTaxContext.put("itemProductList", emptyList);
                    calcTaxContext.put("itemAmountList", emptyList);
                    calcTaxContext.put("itemPriceList", emptyList);
                    calcTaxContext.put("itemQuantityList", emptyList);
                    calcTaxContext.put("itemShippingList", emptyList);

                    Map<String, Object> calcTaxResult = null;
                    try {
                        calcTaxResult = dispatcher.runSync("calcTax", calcTaxContext);
                    } catch (GenericServiceException e) {
                        Debug.logError(e, "Trouble calling calcTaxService", module);
                        return ServiceUtil.returnError(UtilProperties.getMessage(resource,
                                "AccountingTroubleCallingCalcTaxService", locale));
                    }
                    List<GenericValue> orderAdjustments = UtilGenerics
                            .checkList(calcTaxResult.get("orderAdjustments"));

                    // If we have any OrderAdjustments due to tax on shipping, store them and add them to the total
                    if (orderAdjustments != null) {
                        for (GenericValue orderAdjustment : orderAdjustments) {
                            totalAdditionalShippingCharges = totalAdditionalShippingCharges
                                    .add(orderAdjustment.getBigDecimal("amount").setScale(DECIMALS, ROUNDING));
                            orderAdjustment.set("orderAdjustmentId", delegator.getNextSeqId("OrderAdjustment"));
                            orderAdjustment.set("orderId", orderId);
                            orderAdjustment.set("orderItemSeqId", "_NA_");
                            orderAdjustment.set("shipGroupSeqId", shipment.getString("primaryShipGroupSeqId"));
                            orderAdjustment.set("originalAdjustmentId", shippingOrderAdjustmentId);
                        }
                        try {
                            delegator.storeAll(orderAdjustments);
                        } catch (GenericEntityException e) {
                            Debug.logError(e, "Problem storing OrderAdjustments: " + orderAdjustments, module);
                            return ServiceUtil.returnError(UtilProperties.getMessage(resource,
                                    "AccountingProblemStoringOrderAdjustments",
                                    UtilMisc.toMap("orderAdjustments", orderAdjustments), locale));
                        }
                    }

                    // If part of the order was paid via credit card, try to charge it for the additional shipping
                    List<GenericValue> orderPaymentPreferences = null;
                    try {
                        orderPaymentPreferences = EntityQuery.use(delegator).from("OrderPaymentPreference")
                                .where("orderId", orderId, "paymentMethodTypeId", "CREDIT_CARD").queryList();
                    } catch (GenericEntityException e) {
                        Debug.logError(e, "Problem getting OrderPaymentPreference records", module);
                        return ServiceUtil.returnError(UtilProperties.getMessage(resource,
                                "AccountingProblemGettingOrderPaymentPreferences", locale));
                    }

                    //  Use the first credit card we find, for the sake of simplicity
                    String paymentMethodId = null;
                    GenericValue cardOrderPaymentPref = EntityUtil.getFirst(orderPaymentPreferences);
                    if (cardOrderPaymentPref != null) {
                        paymentMethodId = cardOrderPaymentPref.getString("paymentMethodId");
                    }

                    if (paymentMethodId != null) {

                        // Release all outstanding (not settled or cancelled) authorizations, while keeping a running
                        //  total of their amounts so that the total plus the additional shipping charges can be authorized again
                        //  all at once.
                        BigDecimal totalNewAuthAmount = totalAdditionalShippingCharges.setScale(DECIMALS,
                                ROUNDING);
                        for (GenericValue orderPaymentPreference : orderPaymentPreferences) {
                            if (!(orderPaymentPreference.getString("statusId").equals("PAYMENT_SETTLED")
                                    || orderPaymentPreference.getString("statusId")
                                            .equals("PAYMENT_CANCELLED"))) {
                                GenericValue authTransaction = PaymentGatewayServices
                                        .getAuthTransaction(orderPaymentPreference);
                                if (authTransaction != null && authTransaction.get("amount") != null) {

                                    // Update the total authorized amount
                                    totalNewAuthAmount = totalNewAuthAmount.add(authTransaction
                                            .getBigDecimal("amount").setScale(DECIMALS, ROUNDING));

                                    // Release the authorization for the OrderPaymentPreference
                                    Map<String, Object> prefReleaseResult = null;
                                    try {
                                        prefReleaseResult = dispatcher.runSync("releaseOrderPaymentPreference",
                                                UtilMisc.toMap("orderPaymentPreferenceId",
                                                        orderPaymentPreference
                                                                .getString("orderPaymentPreferenceId"),
                                                        "userLogin", context.get("userLogin")));
                                    } catch (GenericServiceException e) {
                                        Debug.logError(e,
                                                "Trouble calling releaseOrderPaymentPreference service",
                                                module);
                                        return ServiceUtil.returnError(UtilProperties.getMessage(resource,
                                                "AccountingTroubleCallingReleaseOrderPaymentPreferenceService",
                                                locale));
                                    }
                                    if (ServiceUtil.isError(prefReleaseResult)
                                            || ServiceUtil.isFailure(prefReleaseResult)) {
                                        String errMsg = ServiceUtil.getErrorMessage(prefReleaseResult);
                                        Debug.logError(errMsg, module);
                                        return ServiceUtil.returnError(errMsg);
                                    }
                                }
                            }
                        }

                        // Create a new OrderPaymentPreference for the order to handle the new (totalled) charge. Don't
                        //  set the maxAmount so that it doesn't interfere with other authorizations
                        Map<String, Object> serviceContext = UtilMisc.toMap("orderId", orderId,
                                "paymentMethodId", paymentMethodId, "paymentMethodTypeId", "CREDIT_CARD",
                                "userLogin", context.get("userLogin"));
                        String orderPaymentPreferenceId = null;
                        try {
                            Map<String, Object> result = dispatcher.runSync("createOrderPaymentPreference",
                                    serviceContext);
                            orderPaymentPreferenceId = (String) result.get("orderPaymentPreferenceId");
                        } catch (GenericServiceException e) {
                            Debug.logError(e, "Trouble calling createOrderPaymentPreference service", module);
                            return ServiceUtil.returnError(UtilProperties.getMessage(resource,
                                    "AccountingTroubleCallingCreateOrderPaymentPreferenceService", locale));
                        }

                        // Attempt to authorize the new orderPaymentPreference
                        Map<String, Object> authResult = null;
                        try {
                            // Use an overrideAmount because the maxAmount wasn't set on the OrderPaymentPreference
                            authResult = dispatcher.runSync("authOrderPaymentPreference",
                                    UtilMisc.toMap("orderPaymentPreferenceId", orderPaymentPreferenceId,
                                            "overrideAmount", totalNewAuthAmount, "userLogin",
                                            context.get("userLogin")));
                        } catch (GenericServiceException e) {
                            Debug.logError(e, "Trouble calling authOrderPaymentPreference service", module);
                            return ServiceUtil.returnError(UtilProperties.getMessage(resource,
                                    "AccountingTroubleCallingAuthOrderPaymentPreferenceService", locale));
                        }

                        // If the authorization fails, create the invoice anyway, but make a note of it
                        boolean authFinished = ((Boolean) authResult.get("finished")).booleanValue();
                        boolean authErrors = ((Boolean) authResult.get("errors")).booleanValue();
                        if (authErrors || !authFinished) {
                            String errMsg = UtilProperties.getMessage(resource,
                                    "AccountingUnableToAuthAdditionalShipCharges",
                                    UtilMisc.toMap("shipmentId", shipmentId, "paymentMethodId", paymentMethodId,
                                            "orderPaymentPreferenceId", orderPaymentPreferenceId),
                                    locale);
                            Debug.logError(errMsg, module);
                        }

                    }
                }
            }
        } else {
            Debug.logInfo(UtilProperties.getMessage(resource, "AccountingIgnoringAdditionalShipCharges",
                    UtilMisc.toMap("productStoreId", orh.getProductStoreId()), locale), module);
        }

        String invoiceId = null;
        GenericValue shipmentItemBilling = null;
        String shipmentId = shipmentIds.get(0);
        try {
            shipmentItemBilling = EntityQuery.use(delegator).from("ShipmentItemBilling")
                    .where("shipmentId", shipmentId).queryFirst();
        } catch (GenericEntityException e) {
            return ServiceUtil.returnError(
                    UtilProperties.getMessage(resource, "AccountingProblemGettingShipmentItemBilling", locale));
        }
        if (shipmentItemBilling != null) {
            invoiceId = shipmentItemBilling.getString("invoiceId");
        }

        // call the createInvoiceForOrder service for each order
        Map<String, Object> serviceContext = UtilMisc.toMap("orderId", orderId, "billItems", toBillItems,
                "invoiceId", invoiceId, "eventDate", context.get("eventDate"), "userLogin",
                context.get("userLogin"));
        try {
            Map<String, Object> result = dispatcher.runSync("createInvoiceForOrder", serviceContext);
            invoicesCreated.add((String) result.get("invoiceId"));
        } catch (GenericServiceException e) {
            Debug.logError(e, "Trouble calling createInvoiceForOrder service; invoice not created for shipment",
                    module);
            return ServiceUtil.returnError(UtilProperties.getMessage(resource,
                    "AccountingTroubleCallingCreateInvoiceForOrderService", locale));
        }
    }

    Map<String, Object> response = ServiceUtil.returnSuccess();
    response.put("invoicesCreated", invoicesCreated);
    return response;
}

From source file:org.ofbiz.order.order.OrderServices.java

/** Service for checking and re-calc the tax amount */
public static Map<String, Object> recalcOrderTax(DispatchContext ctx, Map<String, ? extends Object> context) {
    LocalDispatcher dispatcher = ctx.getDispatcher();
    Delegator delegator = ctx.getDelegator();
    String orderId = (String) context.get("orderId");
    GenericValue userLogin = (GenericValue) context.get("userLogin");
    Locale locale = (Locale) context.get("locale");

    // check and make sure we have permission to change the order
    Security security = ctx.getSecurity();
    boolean hasPermission = OrderServices.hasPermission(orderId, userLogin, "UPDATE", security, delegator);
    if (!hasPermission) {
        return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
                "OrderYouDoNotHavePermissionToChangeThisOrdersStatus", locale));
    }/*from w w  w  . j  av  a  2 s  . c om*/

    // get the order header
    GenericValue orderHeader = null;
    try {
        orderHeader = delegator.findByPrimaryKey("OrderHeader", UtilMisc.toMap("orderId", orderId));
    } catch (GenericEntityException e) {
        return ServiceUtil.returnError(
                UtilProperties.getMessage(resource_error, "OrderErrorCannotGetOrderHeaderEntity", locale)
                        + e.getMessage());
    }

    if (orderHeader == null) {
        return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
                "OrderErrorNoValidOrderHeaderFoundForOrderId", UtilMisc.toMap("orderId", orderId), locale));
    }

    // don't charge tax on purchase orders, better we still do.....
    //        if ("PURCHASE_ORDER".equals(orderHeader.getString("orderTypeId"))) {
    //            return ServiceUtil.returnSuccess();
    //        }

    // Retrieve the order tax adjustments
    List<GenericValue> orderTaxAdjustments = null;
    try {
        orderTaxAdjustments = delegator.findByAnd("OrderAdjustment", UtilMisc.toMap("orderId", orderId));
        orderTaxAdjustments = OrderReadHelper.fetchTaxAdjustments(orderTaxAdjustments);
    } catch (GenericEntityException e) {
        Debug.logError(e, "Unable to retrieve SALES_TAX adjustments for order : " + orderId, module);
        return ServiceUtil.returnError(
                UtilProperties.getMessage(resource_error, "OrderUnableToRetrieveSalesTaxAdjustments", locale));
    }

    // Accumulate the total existing tax adjustment
    BigDecimal totalExistingOrderTax = ZERO;
    Iterator<GenericValue> otait = UtilMisc.toIterator(orderTaxAdjustments);
    while (otait != null && otait.hasNext()) {
        GenericValue orderTaxAdjustment = otait.next();
        if (orderTaxAdjustment.get("amount") != null) {
            totalExistingOrderTax = totalExistingOrderTax
                    .add(orderTaxAdjustment.getBigDecimal("amount").setScale(taxDecimals, taxRounding));
        }
    }

    // Recalculate the taxes for the order
    BigDecimal totalNewOrderTax = ZERO;
    OrderReadHelper orh = new OrderReadHelper(orderHeader);
    List<GenericValue> shipGroups = orh.getOrderItemShipGroups();
    if (shipGroups != null) {
        Iterator<GenericValue> itr = shipGroups.iterator();
        while (itr.hasNext()) {
            GenericValue shipGroup = itr.next();
            String shipGroupSeqId = shipGroup.getString("shipGroupSeqId");

            List<GenericValue> validOrderItems = orh.getValidOrderItems(shipGroupSeqId);
            if (validOrderItems != null) {
                // prepare the inital lists
                List<GenericValue> products = new ArrayList<GenericValue>(validOrderItems.size());
                List<BigDecimal> amounts = new ArrayList<BigDecimal>(validOrderItems.size());
                List<BigDecimal> shipAmts = new ArrayList<BigDecimal>(validOrderItems.size());
                List<BigDecimal> itPrices = new ArrayList<BigDecimal>(validOrderItems.size());
                List<BigDecimal> itQuantities = new ArrayList<BigDecimal>(validOrderItems.size());

                // adjustments and total
                List<GenericValue> allAdjustments = orh.getAdjustments();
                List<GenericValue> orderHeaderAdjustments = OrderReadHelper
                        .getOrderHeaderAdjustments(allAdjustments, shipGroupSeqId);
                BigDecimal orderSubTotal = OrderReadHelper.getOrderItemsSubTotal(validOrderItems,
                        allAdjustments);

                // shipping amount
                BigDecimal orderShipping = OrderReadHelper.calcOrderAdjustments(orderHeaderAdjustments,
                        orderSubTotal, false, false, true);

                //promotions amount
                BigDecimal orderPromotions = OrderReadHelper.calcOrderPromoAdjustmentsBd(allAdjustments);

                // build up the list of tax calc service parameters
                for (int i = 0; i < validOrderItems.size(); i++) {
                    GenericValue orderItem = validOrderItems.get(i);
                    String productId = orderItem.getString("productId");
                    try {
                        products.add(i,
                                delegator.findByPrimaryKey("Product", UtilMisc.toMap("productId", productId))); // get the product entity
                        amounts.add(i,
                                OrderReadHelper.getOrderItemSubTotal(orderItem, allAdjustments, true, false)); // get the item amount
                        shipAmts.add(i, OrderReadHelper.getOrderItemAdjustmentsTotal(orderItem, allAdjustments,
                                false, false, true)); // get the shipping amount
                        itPrices.add(i, orderItem.getBigDecimal("unitPrice"));
                        itQuantities.add(i, orderItem.getBigDecimal("quantity"));
                    } catch (GenericEntityException e) {
                        Debug.logError(e, "Cannot read order item entity : " + orderItem, module);
                        return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
                                "OrderCannotReadTheOrderItemEntity", locale));
                    }
                }

                GenericValue shippingAddress = orh.getShippingAddress(shipGroupSeqId);
                // no shipping address, try the billing address
                if (shippingAddress == null) {
                    List<GenericValue> billingAddressList = orh.getBillingLocations();
                    if (billingAddressList.size() > 0) {
                        shippingAddress = billingAddressList.get(0);
                    }
                }

                // TODO and NOTE DEJ20070816: this is NOT a good way to determine if this is a face-to-face or immediatelyFulfilled order
                //this should be made consistent with the CheckOutHelper.makeTaxContext(int shipGroup, GenericValue shipAddress) method
                if (shippingAddress == null) {
                    // face-to-face order; use the facility address
                    String facilityId = orderHeader.getString("originFacilityId");
                    if (facilityId != null) {
                        GenericValue facilityContactMech = ContactMechWorker.getFacilityContactMechByPurpose(
                                delegator, facilityId,
                                UtilMisc.toList("SHIP_ORIG_LOCATION", "PRIMARY_LOCATION"));
                        if (facilityContactMech != null) {
                            try {
                                shippingAddress = delegator.findByPrimaryKey("PostalAddress", UtilMisc.toMap(
                                        "contactMechId", facilityContactMech.getString("contactMechId")));
                            } catch (GenericEntityException e) {
                                Debug.logError(e, module);
                            }
                        }
                    }
                }

                // if shippingAddress is still null then don't calculate tax; it may be an situation where no tax is applicable, or the data is bad and we don't have a way to find an address to check tax for
                if (shippingAddress == null) {
                    Debug.logWarning("Not calculating tax for Order [" + orderId
                            + "] because there is no shippingAddress, and no address on the origin facility ["
                            + orderHeader.getString("originFacilityId") + "]", module);
                    continue;
                }

                // prepare the service context
                Map<String, Object> serviceContext = UtilMisc.<String, Object>toMap("productStoreId",
                        orh.getProductStoreId(), "itemProductList", products, "itemAmountList", amounts,
                        "itemShippingList", shipAmts, "itemPriceList", itPrices, "itemQuantityList",
                        itQuantities, "orderShippingAmount", orderShipping);
                serviceContext.put("shippingAddress", shippingAddress);
                serviceContext.put("orderPromotionsAmount", orderPromotions);
                if (orh.getBillToParty() != null)
                    serviceContext.put("billToPartyId", orh.getBillToParty().getString("partyId"));
                if (orh.getBillFromParty() != null)
                    serviceContext.put("payToPartyId", orh.getBillFromParty().getString("partyId"));

                // invoke the calcTax service
                Map<String, Object> serviceResult = null;
                try {
                    serviceResult = dispatcher.runSync("calcTax", serviceContext);
                } catch (GenericServiceException e) {
                    Debug.logError(e, module);
                    return ServiceUtil.returnError(UtilProperties.getMessage(resource_error,
                            "OrderProblemOccurredInTaxService", locale));
                }

                if (ServiceUtil.isError(serviceResult)) {
                    return ServiceUtil.returnError(ServiceUtil.getErrorMessage(serviceResult));
                }

                // the adjustments (returned in order) from the tax service
                List<GenericValue> orderAdj = UtilGenerics.checkList(serviceResult.get("orderAdjustments"));
                List<List<GenericValue>> itemAdj = UtilGenerics.checkList(serviceResult.get("itemAdjustments"));

                // Accumulate the new tax total from the recalculated header adjustments
                if (UtilValidate.isNotEmpty(orderAdj)) {
                    Iterator<GenericValue> oai = orderAdj.iterator();
                    while (oai.hasNext()) {
                        GenericValue oa = oai.next();
                        if (oa.get("amount") != null) {
                            totalNewOrderTax = totalNewOrderTax
                                    .add(oa.getBigDecimal("amount").setScale(taxDecimals, taxRounding));
                        }

                    }
                }

                // Accumulate the new tax total from the recalculated item adjustments
                if (UtilValidate.isNotEmpty(itemAdj)) {
                    for (int i = 0; i < itemAdj.size(); i++) {
                        List<GenericValue> itemAdjustments = itemAdj.get(i);
                        Iterator<GenericValue> ida = itemAdjustments.iterator();
                        while (ida.hasNext()) {
                            GenericValue ia = ida.next();
                            if (ia.get("amount") != null) {
                                totalNewOrderTax = totalNewOrderTax
                                        .add(ia.getBigDecimal("amount").setScale(taxDecimals, taxRounding));
                            }
                        }
                    }
                }
            }
        }

        // Determine the difference between existing and new tax adjustment totals, if any
        BigDecimal orderTaxDifference = totalNewOrderTax.subtract(totalExistingOrderTax).setScale(taxDecimals,
                taxRounding);

        // If the total has changed, create an OrderAdjustment to reflect the fact
        if (orderTaxDifference.signum() != 0) {
            Map<String, Object> createOrderAdjContext = new HashMap<String, Object>();
            createOrderAdjContext.put("orderAdjustmentTypeId", "SALES_TAX");
            createOrderAdjContext.put("orderId", orderId);
            createOrderAdjContext.put("orderItemSeqId", "_NA_");
            createOrderAdjContext.put("shipGroupSeqId", "_NA_");
            createOrderAdjContext.put("description", "Tax adjustment due to order change");
            createOrderAdjContext.put("amount", orderTaxDifference);
            createOrderAdjContext.put("userLogin", userLogin);
            Map<String, Object> createOrderAdjResponse = null;
            try {
                createOrderAdjResponse = dispatcher.runSync("createOrderAdjustment", createOrderAdjContext);
            } catch (GenericServiceException e) {
                String createOrderAdjErrMsg = UtilProperties.getMessage(resource_error,
                        "OrderErrorCallingCreateOrderAdjustmentService", locale);
                Debug.logError(createOrderAdjErrMsg, module);
                return ServiceUtil.returnError(createOrderAdjErrMsg);
            }
            if (ServiceUtil.isError(createOrderAdjResponse)) {
                Debug.logError(ServiceUtil.getErrorMessage(createOrderAdjResponse), module);
                return ServiceUtil.returnError(ServiceUtil.getErrorMessage(createOrderAdjResponse));
            }
        }
    }

    return ServiceUtil.returnSuccess();
}

From source file:org.ofbiz.accounting.invoice.InvoiceServices.java

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 == -1) {
        return ServiceUtil.returnError(
                UtilProperties.getMessage(resource, "AccountingAritmeticPropertiesNotConfigured", locale));
    }//from w w w  .ja v a2 s . co m

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

    String defaultInvoiceProcessing = EntityUtilProperties.getPropertyValue("AccountingConfig",
            "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 = FastList.newInstance();

    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 (defaultInvoiceProcessing.equals("YY")) {
        invoiceProcessing = true;
    } else if (defaultInvoiceProcessing.equals("NN")) {
        invoiceProcessing = false;
    } else if (defaultInvoiceProcessing.equals("Y")) {
        invoiceProcessing = !"Y".equals(changeProcessing);
    } else if (defaultInvoiceProcessing.equals("N")) {
        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 = 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 = ZERO;
    // amount available on the payment reduced by the already applied amounts
    BigDecimal amountAppliedMax = ZERO;
    // the maximum that can be applied taking payment,invoice,invoiceitem,billing account in concideration
    // if maxApplied is missing, this value can be used,
    // Payment this should be checked after the invoice checking because it is possible the currency is changed
    GenericValue payment = null;
    String currencyUomId = null;
    if (paymentId == null || paymentId.equals("")) {
        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));
        }
        paymentApplyAvailable = payment.getBigDecimal("amount")
                .subtract(PaymentWorker.getPaymentApplied(payment)).setScale(DECIMALS, ROUNDING);

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

        currencyUomId = payment.getString("currencyUomId");

        // if the amount to apply is 0 give it amount the payment still need
        // to apply
        if (amountApplied.signum() == 0) {
            amountAppliedMax = paymentApplyAvailable;
        }

    }

    // the "TO" Payment.....
    BigDecimal toPaymentApplyAvailable = ZERO;
    GenericValue toPayment = null;
    if (toPaymentId != null && !toPaymentId.equals("")) {
        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));
        }
        toPaymentApplyAvailable = toPayment.getBigDecimal("amount")
                .subtract(PaymentWorker.getPaymentApplied(toPayment)).setScale(DECIMALS, ROUNDING);

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

        // if the amount to apply is less then required by the payment reduce it
        if (amountAppliedMax.compareTo(toPaymentApplyAvailable) > 0) {
            amountAppliedMax = toPaymentApplyAvailable;
        }

        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 && !billingAccountId.equals("")) {
        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));
        }
        // 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 = ZERO;
    // amount available on the invoice reduced by the already applied amounts
    BigDecimal invoiceItemApplyAvailable = 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.getString("statusId").equals("INVOICE_CANCELLED")) {
                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);
                if (amountApplied.signum() == 0) {
                    amountAppliedMax = paymentApplyAvailable;
                }
            }

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

            // adjust the amountAppliedMax value if required....
            if (invoiceApplyAvailable.compareTo(amountAppliedMax) < 0) {
                amountAppliedMax = invoiceApplyAvailable;
            }

            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
                // (paymentApplicationId == null)
                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(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(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(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(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 null value to
                        // a real Item number
                        newInvoiceItemApplyAvailable = invoiceItemApplyAvailable.subtract(amountApplied)
                                .setScale(DECIMALS, ROUNDING);
                        if (newInvoiceItemApplyAvailable.compareTo(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(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(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(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(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(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.length() > 0)
                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 && useHighestAmount.equals("Y")) {
        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, "AccountingApplicationSuccess",
                UtilMisc.<String, Object>toMap("amountApplied", amountApplied, "paymentId", paymentId,
                        "isoCode", currencyUomId, "toMessage", toMessage),
                locale);
    }
    // report error messages if any
    if (errorMessageList.size() > 0) {
        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);
        } else { // spread the amount over every single item number
            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.size() == 0) {
                errorMessageList
                        .add(UtilProperties.getMessage(resource, "AccountingNoInvoiceItemsFoundForInvoice",
                                UtilMisc.toMap("invoiceId", invoiceId), locale));
                return ServiceUtil.returnError(errorMessageList);
            } else { // we found some invoice items, start processing....
                // 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(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 = ZERO;
                    // item total amount - already applied (if any)
                    BigDecimal alreadyApplied = 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 = 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);
                    }

                    // check if either the invoice or the payment is fully
                    // applied, when yes change the status to paid
                    // which triggers the ledger routines....
                    /*
                     * if
                     * (InvoiceWorker.getInvoiceTotal(invoice).equals(InvoiceWorker.getInvoiceApplied(invoice))) {
                     * try { dispatcher.runSync("setInvoiceStatus",
                     * UtilMisc.toMap("invoiceId",invoiceId,"statusId","INVOICE_PAID")); }
                     * catch (GenericServiceException e1) {
                     * Debug.logError(e1, "Error updating invoice status",
                     * module); } }
                     *
                     * if
                     * (payment.getBigDecimal("amount").equals(PaymentWorker.getPaymentApplied(payment))) {
                     * GenericValue appliedPayment = (GenericValue)
                     * delegator.makeValue("Payment",
                     * UtilMisc.toMap("paymentId",paymentId,"statusId","INVOICE_PAID"));
                     * try { appliedPayment.store(); } catch
                     * (GenericEntityException e) {
                     * ServiceUtil.returnError(e.getMessage()); } }
                     */
                }

                if (errorMessageList.size() > 0) {
                    return ServiceUtil.returnError(errorMessageList);
                } else {
                    if (successMessage != null) {
                        return ServiceUtil.returnSuccess(successMessage);
                    } else {
                        return ServiceUtil.returnSuccess();
                    }
                }
            }
        }
    }

    // if no paymentApplicationId supplied create a new record with the data
    // supplied...
    if (paymentApplicationId == null && amountApplied != null) {
        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);
    }

    // should never come here...
    errorMessageList.add(
            UtilProperties.getMessage(resource, "AccountingPaymentApplicationParameterUnsuitable", locale));
    errorMessageList
            .add(UtilProperties.getMessage(resource, "AccountingPaymentApplicationParameterListUnsuitable",
                    UtilMisc.toMap("invoiceId", invoiceId, "invoiceItemSeqId", invoiceItemSeqId, "paymentId",
                            paymentId, "toPaymentId", toPaymentId, "paymentApplicationId", paymentApplicationId,
                            "amountApplied", amountApplied),
                    locale));
    return ServiceUtil.returnError(errorMessageList);
}

From source file:org.apache.ofbiz.accounting.invoice.InvoiceServices.java

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 == -1) {
        return ServiceUtil.returnError(
                UtilProperties.getMessage(resource, "AccountingAritmeticPropertiesNotConfigured", locale));
    }/*w  w w  .  j  a  va  2s.c o  m*/

    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<String>();

    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 (defaultInvoiceProcessing.equals("YY")) {
        invoiceProcessing = true;
    } else if (defaultInvoiceProcessing.equals("NN")) {
        invoiceProcessing = false;
    } else if (defaultInvoiceProcessing.equals("Y")) {
        invoiceProcessing = !"Y".equals(changeProcessing);
    } else if (defaultInvoiceProcessing.equals("N")) {
        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 = 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 = ZERO;
    // amount available on the payment reduced by the already applied amounts
    BigDecimal amountAppliedMax = ZERO;
    // the maximum that can be applied taking payment,invoice,invoiceitem,billing account in concideration
    // if maxApplied is missing, this value can be used,
    // Payment this should be checked after the invoice checking because it is possible the currency is changed
    GenericValue payment = null;
    String currencyUomId = null;
    if (paymentId == null || paymentId.equals("")) {
        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));
        }
        paymentApplyAvailable = payment.getBigDecimal("amount")
                .subtract(PaymentWorker.getPaymentApplied(payment)).setScale(DECIMALS, ROUNDING);

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

        currencyUomId = payment.getString("currencyUomId");

        // if the amount to apply is 0 give it amount the payment still need
        // to apply
        if (amountApplied.signum() == 0) {
            amountAppliedMax = paymentApplyAvailable;
        }

    }

    // the "TO" Payment.....
    BigDecimal toPaymentApplyAvailable = ZERO;
    GenericValue toPayment = null;
    if (toPaymentId != null && !toPaymentId.equals("")) {
        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));
        }
        toPaymentApplyAvailable = toPayment.getBigDecimal("amount")
                .subtract(PaymentWorker.getPaymentApplied(toPayment)).setScale(DECIMALS, ROUNDING);

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

        // if the amount to apply is less then required by the payment reduce it
        if (amountAppliedMax.compareTo(toPaymentApplyAvailable) > 0) {
            amountAppliedMax = toPaymentApplyAvailable;
        }

        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 && !billingAccountId.equals("")) {
        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));
        }
        // 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 = ZERO;
    // amount available on the invoice reduced by the already applied amounts
    BigDecimal invoiceItemApplyAvailable = 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.getString("statusId").equals("INVOICE_CANCELLED")) {
                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);
                if (amountApplied.signum() == 0) {
                    amountAppliedMax = paymentApplyAvailable;
                }
            }

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

            // adjust the amountAppliedMax value if required....
            if (invoiceApplyAvailable.compareTo(amountAppliedMax) < 0) {
                amountAppliedMax = invoiceApplyAvailable;
            }

            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(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(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(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(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 null value to
                        // a real Item number
                        newInvoiceItemApplyAvailable = invoiceItemApplyAvailable.subtract(amountApplied)
                                .setScale(DECIMALS, ROUNDING);
                        if (newInvoiceItemApplyAvailable.compareTo(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(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(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(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(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(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.length() > 0)
                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 && useHighestAmount.equals("Y")) {
        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, "AccountingApplicationSuccess",
                UtilMisc.<String, Object>toMap("amountApplied", amountApplied, "paymentId", paymentId,
                        "isoCode", currencyUomId, "toMessage", toMessage),
                locale);
    }
    // report error messages if any
    if (errorMessageList.size() > 0) {
        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);
        } else { // spread the amount over every single item number
            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.size() == 0) {
                errorMessageList
                        .add(UtilProperties.getMessage(resource, "AccountingNoInvoiceItemsFoundForInvoice",
                                UtilMisc.toMap("invoiceId", invoiceId), locale));
                return ServiceUtil.returnError(errorMessageList);
            } else { // we found some invoice items, start processing....
                // 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(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 = ZERO;
                    // item total amount - already applied (if any)
                    BigDecimal alreadyApplied = 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 = 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.size() > 0) {
                    return ServiceUtil.returnError(errorMessageList);
                } else {
                    if (successMessage != null) {
                        return ServiceUtil.returnSuccess(successMessage);
                    } else {
                        return ServiceUtil.returnSuccess();
                    }
                }
            }
        }
    }

    // if no paymentApplicationId supplied create a new record with the data
    // supplied...
    if (paymentApplicationId == null && amountApplied != null) {
        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);
    }

    // should never come here...
    errorMessageList.add(
            UtilProperties.getMessage(resource, "AccountingPaymentApplicationParameterUnsuitable", locale));
    errorMessageList
            .add(UtilProperties.getMessage(resource, "AccountingPaymentApplicationParameterListUnsuitable",
                    UtilMisc.toMap("invoiceId", invoiceId, "invoiceItemSeqId", invoiceItemSeqId, "paymentId",
                            paymentId, "toPaymentId", toPaymentId, "paymentApplicationId", paymentApplicationId,
                            "amountApplied", amountApplied),
                    locale));
    return ServiceUtil.returnError(errorMessageList);
}

From source file:org.ofbiz.order.order.OrderServices.java

/**
 * Determines the total amount invoiced for a given order item over all invoices by totalling the item subtotal (via OrderItemBilling),
 *  any adjustments for that item (via OrderAdjustmentBilling), and the item's share of any order-level adjustments (that calculated
 *  by applying the percentage of the items total that the item represents to the order-level adjustments total (also via
 *  OrderAdjustmentBilling). Also returns the quantity invoiced for the item over all invoices, to aid in prorating.
 * @param dctx DispatchContext/*  w  ww  .j  a  v  a  2  s. c o m*/
 * @param context Map
 * @return Map
 */
public static Map<String, Object> getOrderItemInvoicedAmountAndQuantity(DispatchContext dctx,
        Map<String, ? extends Object> context) {
    Delegator delegator = dctx.getDelegator();
    Locale locale = (Locale) context.get("locale");

    String orderId = (String) context.get("orderId");
    String orderItemSeqId = (String) context.get("orderItemSeqId");

    GenericValue orderHeader = null;
    GenericValue orderItemToCheck = null;
    BigDecimal orderItemTotalValue = ZERO;
    BigDecimal invoicedQuantity = ZERO; // Quantity invoiced for the target order item
    try {

        orderHeader = delegator.findByPrimaryKey("OrderHeader", UtilMisc.toMap("orderId", orderId));
        if (UtilValidate.isEmpty(orderHeader)) {
            String errorMessage = UtilProperties.getMessage(resource_error, "OrderErrorOrderIdNotFound",
                    context, locale);
            Debug.logError(errorMessage, module);
            return ServiceUtil.returnError(errorMessage);
        }
        orderItemToCheck = delegator.findByPrimaryKey("OrderItem",
                UtilMisc.toMap("orderId", orderId, "orderItemSeqId", orderItemSeqId));
        if (UtilValidate.isEmpty(orderItemToCheck)) {
            String errorMessage = UtilProperties.getMessage(resource_error, "OrderErrorOrderItemNotFound",
                    context, locale);
            Debug.logError(errorMessage, module);
            return ServiceUtil.returnError(errorMessage);
        }

        BigDecimal orderItemsSubtotal = ZERO; // Aggregated value of order items, non-tax and non-shipping item-level adjustments
        BigDecimal invoicedTotal = ZERO; // Amount invoiced for the target order item
        BigDecimal itemAdjustments = ZERO; // Item-level tax- and shipping-adjustments

        // Aggregate the order items subtotal
        List<GenericValue> orderItems = orderHeader.getRelated("OrderItem", UtilMisc.toList("orderItemSeqId"));
        Iterator<GenericValue> oit = orderItems.iterator();
        while (oit.hasNext()) {
            GenericValue orderItem = oit.next();

            // Look at the orderItemBillings to discover the amount and quantity ever invoiced for this order item
            List<GenericValue> orderItemBillings = delegator.findByAnd("OrderItemBilling",
                    UtilMisc.toMap("orderId", orderId, "orderItemSeqId", orderItem.get("orderItemSeqId")));
            Iterator<GenericValue> oibit = orderItemBillings.iterator();
            while (oibit.hasNext()) {
                GenericValue orderItemBilling = oibit.next();
                BigDecimal quantity = orderItemBilling.getBigDecimal("quantity");
                BigDecimal amount = orderItemBilling.getBigDecimal("amount").setScale(orderDecimals,
                        orderRounding);
                if (UtilValidate.isEmpty(invoicedQuantity) || UtilValidate.isEmpty(amount))
                    continue;

                // Add the item base amount to the subtotal
                orderItemsSubtotal = orderItemsSubtotal.add(quantity.multiply(amount));

                // If the item is the target order item, add the invoiced quantity and amount to their respective totals
                if (orderItemSeqId.equals(orderItem.get("orderItemSeqId"))) {
                    invoicedQuantity = invoicedQuantity.add(quantity);
                    invoicedTotal = invoicedTotal.add(quantity.multiply(amount));
                }
            }

            // Retrieve the adjustments for this item
            List<GenericValue> orderAdjustments = delegator.findByAnd("OrderAdjustment",
                    UtilMisc.toMap("orderId", orderId, "orderItemSeqId", orderItem.get("orderItemSeqId")));
            Iterator<GenericValue> oait = orderAdjustments.iterator();
            while (oait.hasNext()) {
                GenericValue orderAdjustment = oait.next();
                String orderAdjustmentTypeId = orderAdjustment.getString("orderAdjustmentTypeId");
                boolean includeInTax = orderAdjustment.getBoolean("includeInTax").booleanValue();
                // Look at the orderAdjustmentBillings to discove the amount ever invoiced for this order adjustment
                List<GenericValue> orderAdjustmentBillings = delegator.findByAnd("OrderAdjustmentBilling",
                        UtilMisc.toMap("orderAdjustmentId", orderAdjustment.get("orderAdjustmentId")));
                Iterator<GenericValue> oabit = orderAdjustmentBillings.iterator();
                while (oabit.hasNext()) {
                    GenericValue orderAjustmentBilling = oabit.next();
                    BigDecimal amount = orderAjustmentBilling.getBigDecimal("amount").setScale(orderDecimals,
                            orderRounding);
                    if (UtilValidate.isEmpty(amount))
                        continue;

                    if (includeInTax || "SALES_TAX".equals(orderAdjustmentTypeId)
                            || "SHIPPING_CHARGES".equals(orderAdjustmentTypeId)) {
                        if (orderItemSeqId.equals(orderItem.get("orderItemSeqId"))) {

                            // Add tax- and shipping-adjustment amounts to the total adjustments for the target order item
                            itemAdjustments = itemAdjustments.add(amount);
                        }
                    } else {

                        // Add non-tax and non-shipping adjustment amounts to the order items subtotal
                        orderItemsSubtotal = orderItemsSubtotal.add(amount);
                        if (orderItemSeqId.equals(orderItem.get("orderItemSeqId"))) {

                            // If the item is the target order item, add non-tax and non-shipping adjustment amounts to the invoiced total
                            invoicedTotal = invoicedTotal.add(amount);
                        }
                    }
                }
            }
        }

        // Total the order-header-level adjustments for the order
        BigDecimal orderHeaderAdjustmentsTotalValue = ZERO;
        List<GenericValue> orderHeaderAdjustments = delegator.findByAnd("OrderAdjustment",
                UtilMisc.toMap("orderId", orderId, "orderItemSeqId", "_NA_"));
        Iterator<GenericValue> ohait = orderHeaderAdjustments.iterator();
        while (ohait.hasNext()) {
            GenericValue orderHeaderAdjustment = ohait.next();
            List<GenericValue> orderHeaderAdjustmentBillings = delegator.findByAnd("OrderAdjustmentBilling",
                    UtilMisc.toMap("orderAdjustmentId", orderHeaderAdjustment.get("orderAdjustmentId")));
            Iterator<GenericValue> ohabit = orderHeaderAdjustmentBillings.iterator();
            while (ohabit.hasNext()) {
                GenericValue orderHeaderAdjustmentBilling = ohabit.next();
                BigDecimal amount = orderHeaderAdjustmentBilling.getBigDecimal("amount").setScale(orderDecimals,
                        orderRounding);
                if (UtilValidate.isEmpty(amount))
                    continue;
                orderHeaderAdjustmentsTotalValue = orderHeaderAdjustmentsTotalValue.add(amount);
            }
        }

        // How much of the order-level adjustments total does the target order item represent? The assumption is: the same
        //  proportion of the adjustments as of the invoiced total for the item to the invoiced total for all items. These
        //  figures don't take tax- and shipping- adjustments into account, so as to be in accordance with the code in InvoiceServices
        BigDecimal invoicedAmountProportion = ZERO;
        if (orderItemsSubtotal.signum() != 0) {
            invoicedAmountProportion = invoicedTotal.divide(orderItemsSubtotal, 5, orderRounding);
        }
        BigDecimal orderItemHeaderAjustmentAmount = orderHeaderAdjustmentsTotalValue
                .multiply(invoicedAmountProportion);
        orderItemTotalValue = invoicedTotal.add(orderItemHeaderAjustmentAmount);

        // Add back the tax- and shipping- item-level adjustments for the order item
        orderItemTotalValue = orderItemTotalValue.add(itemAdjustments);

    } catch (GenericEntityException e) {
        Debug.logError(e, module);
        return ServiceUtil.returnError(e.getMessage());
    }

    Map<String, Object> result = ServiceUtil.returnSuccess();
    result.put("invoicedAmount", orderItemTotalValue.setScale(orderDecimals, orderRounding));
    result.put("invoicedQuantity", invoicedQuantity.setScale(orderDecimals, orderRounding));
    return result;
}

From source file:org.ofbiz.accounting.invoice.InvoiceServices.java

public static Map<String, Object> createInvoiceFromReturn(DispatchContext dctx, Map<String, Object> context) {
    Delegator delegator = dctx.getDelegator();
    LocalDispatcher dispatcher = dctx.getDispatcher();
    GenericValue userLogin = (GenericValue) context.get("userLogin");
    Locale locale = (Locale) context.get("locale");

    String returnId = (String) context.get("returnId");
    List<GenericValue> billItems = UtilGenerics.checkList(context.get("billItems"));
    String errorMsg = UtilProperties.getMessage(resource, "AccountingErrorCreatingInvoiceForReturn",
            UtilMisc.toMap("returnId", returnId), locale);
    // List invoicesCreated = new ArrayList();
    try {//  w  w w . ja  v a 2  s  .c  o m
        String invoiceTypeId;
        String description;
        // get the return header
        GenericValue returnHeader = EntityQuery.use(delegator).from("ReturnHeader").where("returnId", returnId)
                .queryOne();
        if (returnHeader == null || returnHeader.get("returnHeaderTypeId") == null) {
            return ServiceUtil.returnError("Return type cannot be null");
        }

        if (returnHeader.getString("returnHeaderTypeId").startsWith("CUSTOMER_")) {
            invoiceTypeId = "CUST_RTN_INVOICE";
            description = "Return Invoice for Customer Return #" + returnId;
        } else {
            invoiceTypeId = "PURC_RTN_INVOICE";
            description = "Return Invoice for Vendor Return #" + returnId;
        }
        // set the invoice data
        Map<String, Object> input = UtilMisc.<String, Object>toMap("invoiceTypeId", invoiceTypeId, "statusId",
                "INVOICE_IN_PROCESS");
        input.put("partyId", returnHeader.get("toPartyId"));
        input.put("partyIdFrom", returnHeader.get("fromPartyId"));
        input.put("currencyUomId", returnHeader.get("currencyUomId"));
        input.put("invoiceDate", UtilDateTime.nowTimestamp());
        input.put("description", description);
        input.put("billingAccountId", returnHeader.get("billingAccountId"));
        input.put("userLogin", userLogin);

        // call the service to create the invoice
        Map<String, Object> serviceResults = dispatcher.runSync("createInvoice", input);
        if (ServiceUtil.isError(serviceResults)) {
            return ServiceUtil.returnError(errorMsg, null, null, serviceResults);
        }
        String invoiceId = (String) serviceResults.get("invoiceId");

        // keep track of the invoice total vs the promised return total (how much the customer promised to return)
        BigDecimal invoiceTotal = ZERO;
        BigDecimal promisedTotal = ZERO;

        // loop through shipment receipts to create invoice items and return item billings for each item and adjustment
        int invoiceItemSeqNum = 1;
        String invoiceItemSeqId = UtilFormatOut.formatPaddedNumber(invoiceItemSeqNum,
                INVOICE_ITEM_SEQUENCE_ID_DIGITS);

        for (GenericValue item : billItems) {
            boolean shipmentReceiptFound = false;
            boolean itemIssuanceFound = false;
            if ("ShipmentReceipt".equals(item.getEntityName())) {
                shipmentReceiptFound = true;
            } else if ("ItemIssuance".equals(item.getEntityName())) {
                itemIssuanceFound = true;
            } else {
                Debug.logError("Unexpected entity " + item + " of type " + item.getEntityName(), module);
            }
            // we need the related return item and product
            GenericValue returnItem = null;
            if (shipmentReceiptFound) {
                returnItem = item.getRelatedOne("ReturnItem", true);
            } else if (itemIssuanceFound) {
                GenericValue shipmentItem = item.getRelatedOne("ShipmentItem", true);
                GenericValue returnItemShipment = EntityUtil
                        .getFirst(shipmentItem.getRelated("ReturnItemShipment", null, null, false));
                returnItem = returnItemShipment.getRelatedOne("ReturnItem", true);
            }
            if (returnItem == null)
                continue; // Just to prevent NPE
            GenericValue product = returnItem.getRelatedOne("Product", true);

            // extract the return price as a big decimal for convenience
            BigDecimal returnPrice = returnItem.getBigDecimal("returnPrice");

            // determine invoice item type from the return item type
            String invoiceItemTypeId = getInvoiceItemType(delegator, returnItem.getString("returnItemTypeId"),
                    null, invoiceTypeId, null);
            if (invoiceItemTypeId == null) {
                return ServiceUtil.returnError(errorMsg + UtilProperties.getMessage(resource,
                        "AccountingNoKnownInvoiceItemTypeReturnItemType",
                        UtilMisc.toMap("returnItemTypeId", returnItem.getString("returnItemTypeId")), locale));
            }
            BigDecimal quantity = BigDecimal.ZERO;
            if (shipmentReceiptFound) {
                quantity = item.getBigDecimal("quantityAccepted");
            } else if (itemIssuanceFound) {
                quantity = item.getBigDecimal("quantity");
            }

            // create the invoice item for this shipment receipt
            input = UtilMisc.toMap("invoiceId", invoiceId, "invoiceItemTypeId", invoiceItemTypeId, "quantity",
                    quantity);
            input.put("invoiceItemSeqId", "" + invoiceItemSeqId); // turn the int into a string with ("" + int) hack
            input.put("amount", returnItem.get("returnPrice"));
            input.put("productId", returnItem.get("productId"));
            input.put("taxableFlag", product.get("taxable"));
            input.put("description", returnItem.get("description"));
            // TODO: what about the productFeatureId?
            input.put("userLogin", userLogin);
            serviceResults = dispatcher.runSync("createInvoiceItem", input);
            if (ServiceUtil.isError(serviceResults)) {
                return ServiceUtil.returnError(errorMsg, null, null, serviceResults);
            }

            // copy the return item information into ReturnItemBilling
            input = UtilMisc.toMap("returnId", returnId, "returnItemSeqId", returnItem.get("returnItemSeqId"),
                    "invoiceId", invoiceId);
            input.put("invoiceItemSeqId", "" + invoiceItemSeqId); // turn the int into a string with ("" + int) hack
            input.put("quantity", quantity);
            input.put("amount", returnItem.get("returnPrice"));
            input.put("userLogin", userLogin);
            if (shipmentReceiptFound) {
                input.put("shipmentReceiptId", item.get("receiptId"));
            }
            serviceResults = dispatcher.runSync("createReturnItemBilling", input);
            if (ServiceUtil.isError(serviceResults)) {
                return ServiceUtil.returnError(errorMsg, null, null, serviceResults);
            }
            if (Debug.verboseOn()) {
                Debug.logVerbose("Creating Invoice Item with amount " + returnPrice + " and quantity "
                        + quantity + " for shipment [" + item.getString("shipmentId") + ":"
                        + item.getString("shipmentItemSeqId") + "]", module);
            }

            String parentInvoiceItemSeqId = invoiceItemSeqId;
            // increment the seqId counter after creating the invoice item and return item billing
            invoiceItemSeqNum += 1;
            invoiceItemSeqId = UtilFormatOut.formatPaddedNumber(invoiceItemSeqNum,
                    INVOICE_ITEM_SEQUENCE_ID_DIGITS);

            // keep a running total (note: a returnItem may have many receipts. hence, the promised total quantity is the receipt quantityAccepted + quantityRejected)
            BigDecimal cancelQuantity = ZERO;
            if (shipmentReceiptFound) {
                cancelQuantity = item.getBigDecimal("quantityRejected");
            } else if (itemIssuanceFound) {
                cancelQuantity = item.getBigDecimal("cancelQuantity");
            }
            if (cancelQuantity == null)
                cancelQuantity = ZERO;
            BigDecimal actualAmount = returnPrice.multiply(quantity).setScale(DECIMALS, ROUNDING);
            BigDecimal promisedAmount = returnPrice.multiply(quantity.add(cancelQuantity)).setScale(DECIMALS,
                    ROUNDING);
            invoiceTotal = invoiceTotal.add(actualAmount).setScale(DECIMALS, ROUNDING);
            promisedTotal = promisedTotal.add(promisedAmount).setScale(DECIMALS, ROUNDING);

            // for each adjustment related to this ReturnItem, create a separate invoice item
            List<GenericValue> adjustments = returnItem.getRelated("ReturnAdjustment", null, null, true);
            for (GenericValue adjustment : adjustments) {

                if (adjustment.get("amount") == null) {
                    Debug.logWarning("Return adjustment [" + adjustment.get("returnAdjustmentId")
                            + "] has null amount and will be skipped", module);
                    continue;
                }

                // determine invoice item type from the return item type
                invoiceItemTypeId = getInvoiceItemType(delegator,
                        adjustment.getString("returnAdjustmentTypeId"), null, invoiceTypeId, null);
                if (invoiceItemTypeId == null) {
                    return ServiceUtil
                            .returnError(
                                    errorMsg + UtilProperties
                                            .getMessage(resource,
                                                    "AccountingNoKnownInvoiceItemTypeReturnAdjustmentType",
                                                    UtilMisc.toMap("returnAdjustmentTypeId",
                                                            adjustment.getString("returnAdjustmentTypeId")),
                                                    locale));
                }

                // prorate the adjustment amount by the returned amount; do not round ratio
                BigDecimal ratio = quantity.divide(returnItem.getBigDecimal("returnQuantity"), 100, ROUNDING);
                BigDecimal amount = adjustment.getBigDecimal("amount");
                amount = amount.multiply(ratio).setScale(DECIMALS, ROUNDING);
                if (Debug.verboseOn()) {
                    Debug.logVerbose("Creating Invoice Item with amount " + adjustment.getBigDecimal("amount")
                            + " prorated to " + amount + " for return adjustment ["
                            + adjustment.getString("returnAdjustmentId") + "]", module);
                }

                // prepare invoice item data for this adjustment
                input = UtilMisc.toMap("invoiceId", invoiceId, "invoiceItemTypeId", invoiceItemTypeId,
                        "quantity", BigDecimal.ONE);
                input.put("amount", amount);
                input.put("invoiceItemSeqId", "" + invoiceItemSeqId); // turn the int into a string with ("" + int) hack
                input.put("productId", returnItem.get("productId"));
                input.put("description", adjustment.get("description"));
                input.put("overrideGlAccountId", adjustment.get("overrideGlAccountId"));
                input.put("parentInvoiceId", invoiceId);
                input.put("parentInvoiceItemSeqId", parentInvoiceItemSeqId);
                input.put("taxAuthPartyId", adjustment.get("taxAuthPartyId"));
                input.put("taxAuthGeoId", adjustment.get("taxAuthGeoId"));
                input.put("userLogin", userLogin);

                // only set taxable flag when the adjustment is not a tax
                // TODO: Note that we use the value of Product.taxable here. This is not an ideal solution. Instead, use returnAdjustment.includeInTax
                if (adjustment.get("returnAdjustmentTypeId").equals("RET_SALES_TAX_ADJ")) {
                    input.put("taxableFlag", "N");
                }

                // create the invoice item
                serviceResults = dispatcher.runSync("createInvoiceItem", input);
                if (ServiceUtil.isError(serviceResults)) {
                    return ServiceUtil.returnError(errorMsg, null, null, serviceResults);
                }

                // increment the seqId counter
                invoiceItemSeqNum += 1;
                invoiceItemSeqId = UtilFormatOut.formatPaddedNumber(invoiceItemSeqNum,
                        INVOICE_ITEM_SEQUENCE_ID_DIGITS);

                // keep a running total (promised adjustment in this case is the same as the invoice adjustment)
                invoiceTotal = invoiceTotal.add(amount).setScale(DECIMALS, ROUNDING);
                promisedTotal = promisedTotal.add(amount).setScale(DECIMALS, ROUNDING);
            }
        }

        // ratio of the invoice total to the promised total so far or zero if the amounts were zero
        BigDecimal actualToPromisedRatio = ZERO;
        if (invoiceTotal.signum() != 0) {
            actualToPromisedRatio = invoiceTotal.divide(promisedTotal, 100, ROUNDING); // do not round ratio
        }

        // loop through return-wide adjustments and create invoice items for each
        List<GenericValue> adjustments = returnHeader.getRelated("ReturnAdjustment",
                UtilMisc.toMap("returnItemSeqId", "_NA_"), null, true);
        for (GenericValue adjustment : adjustments) {

            // determine invoice item type from the return item type
            String invoiceItemTypeId = getInvoiceItemType(delegator,
                    adjustment.getString("returnAdjustmentTypeId"), null, invoiceTypeId, null);
            if (invoiceItemTypeId == null) {
                return ServiceUtil
                        .returnError(
                                errorMsg + UtilProperties
                                        .getMessage(resource,
                                                "AccountingNoKnownInvoiceItemTypeReturnAdjustmentType",
                                                UtilMisc.toMap("returnAdjustmentTypeId",
                                                        adjustment.getString("returnAdjustmentTypeId")),
                                                locale));
            }

            // prorate the adjustment amount by the actual to promised ratio
            BigDecimal amount = adjustment.getBigDecimal("amount").multiply(actualToPromisedRatio)
                    .setScale(DECIMALS, ROUNDING);
            if (Debug.verboseOn()) {
                Debug.logVerbose("Creating Invoice Item with amount " + adjustment.getBigDecimal("amount")
                        + " prorated to " + amount + " for return adjustment ["
                        + adjustment.getString("returnAdjustmentId") + "]", module);
            }

            // prepare the invoice item for the return-wide adjustment
            input = UtilMisc.toMap("invoiceId", invoiceId, "invoiceItemTypeId", invoiceItemTypeId, "quantity",
                    BigDecimal.ONE);
            input.put("amount", amount);
            input.put("invoiceItemSeqId", "" + invoiceItemSeqId); // turn the int into a string with ("" + int) hack
            input.put("description", adjustment.get("description"));
            input.put("overrideGlAccountId", adjustment.get("overrideGlAccountId"));
            input.put("taxAuthPartyId", adjustment.get("taxAuthPartyId"));
            input.put("taxAuthGeoId", adjustment.get("taxAuthGeoId"));
            input.put("userLogin", userLogin);

            // XXX TODO Note: we need to implement ReturnAdjustment.includeInTax for this to work properly
            input.put("taxableFlag", adjustment.get("includeInTax"));

            // create the invoice item
            serviceResults = dispatcher.runSync("createInvoiceItem", input);
            if (ServiceUtil.isError(serviceResults)) {
                return ServiceUtil.returnError(errorMsg, null, null, serviceResults);
            }

            // increment the seqId counter
            invoiceItemSeqNum += 1;
            invoiceItemSeqId = UtilFormatOut.formatPaddedNumber(invoiceItemSeqNum,
                    INVOICE_ITEM_SEQUENCE_ID_DIGITS);
        }

        // Set the invoice to READY
        serviceResults = dispatcher.runSync("setInvoiceStatus", UtilMisc.<String, Object>toMap("invoiceId",
                invoiceId, "statusId", "INVOICE_READY", "userLogin", userLogin));
        if (ServiceUtil.isError(serviceResults)) {
            return ServiceUtil.returnError(errorMsg, null, null, serviceResults);
        }

        // return the invoiceId
        Map<String, Object> results = ServiceUtil.returnSuccess();
        results.put("invoiceId", invoiceId);
        return results;
    } catch (GenericServiceException e) {
        Debug.logError(e, errorMsg + e.getMessage(), module);
        return ServiceUtil.returnError(errorMsg + e.getMessage());
    } catch (GenericEntityException e) {
        Debug.logError(e, errorMsg + e.getMessage(), module);
        return ServiceUtil.returnError(errorMsg + e.getMessage());
    }
}

From source file:org.apache.ofbiz.accounting.invoice.InvoiceServices.java

public static Map<String, Object> createInvoiceFromReturn(DispatchContext dctx, Map<String, Object> context) {
    Delegator delegator = dctx.getDelegator();
    LocalDispatcher dispatcher = dctx.getDispatcher();
    GenericValue userLogin = (GenericValue) context.get("userLogin");
    Locale locale = (Locale) context.get("locale");

    String returnId = (String) context.get("returnId");
    List<GenericValue> billItems = UtilGenerics.checkList(context.get("billItems"));
    String errorMsg = UtilProperties.getMessage(resource, "AccountingErrorCreatingInvoiceForReturn",
            UtilMisc.toMap("returnId", returnId), locale);
    // List invoicesCreated = new ArrayList();
    try {//from  w  w  w .j  av a2s .  c o  m
        String invoiceTypeId;
        String description;
        // get the return header
        GenericValue returnHeader = EntityQuery.use(delegator).from("ReturnHeader").where("returnId", returnId)
                .queryOne();
        if (returnHeader == null || returnHeader.get("returnHeaderTypeId") == null) {
            return ServiceUtil.returnError(
                    UtilProperties.getMessage(resource, "AccountingReturnTypeCannotBeNull", locale));
        }

        if (returnHeader.getString("returnHeaderTypeId").startsWith("CUSTOMER_")) {
            invoiceTypeId = "CUST_RTN_INVOICE";
            description = "Return Invoice for Customer Return #" + returnId;
        } else {
            invoiceTypeId = "PURC_RTN_INVOICE";
            description = "Return Invoice for Vendor Return #" + returnId;
        }

        List<GenericValue> returnItems = returnHeader.getRelated("ReturnItem", null, null, false);
        if (!returnItems.isEmpty()) {
            for (GenericValue returnItem : returnItems) {
                if ("RETURN_COMPLETED".equals(returnItem.getString("statusId"))) {
                    GenericValue product = returnItem.getRelatedOne("Product", false);
                    if (!ProductWorker.isPhysical(product)) {
                        boolean isNonPhysicalItemToReturn = false;
                        List<GenericValue> returnItemBillings = returnItem.getRelated("ReturnItemBilling", null,
                                null, false);

                        if (!returnItemBillings.isEmpty()) {
                            GenericValue invoice = EntityUtil.getFirst(returnItemBillings)
                                    .getRelatedOne("Invoice", false);
                            if ("INVOICE_CANCELLED".equals(invoice.getString("statusId"))) {
                                isNonPhysicalItemToReturn = true;
                            }
                        } else {
                            isNonPhysicalItemToReturn = true;
                        }

                        if (isNonPhysicalItemToReturn) {
                            if (UtilValidate.isEmpty(billItems)) {
                                billItems = new ArrayList<GenericValue>();
                            }

                            billItems.add(returnItem);
                        }
                    }
                }
            }
        }

        Map<String, Object> results = ServiceUtil.returnSuccess();
        if (UtilValidate.isNotEmpty(billItems)) {
            // set the invoice data
            Map<String, Object> input = UtilMisc.<String, Object>toMap("invoiceTypeId", invoiceTypeId,
                    "statusId", "INVOICE_IN_PROCESS");
            input.put("partyId", returnHeader.get("toPartyId"));
            input.put("partyIdFrom", returnHeader.get("fromPartyId"));
            input.put("currencyUomId", returnHeader.get("currencyUomId"));
            input.put("invoiceDate", UtilDateTime.nowTimestamp());
            input.put("description", description);
            input.put("billingAccountId", returnHeader.get("billingAccountId"));
            input.put("userLogin", userLogin);

            // call the service to create the invoice
            Map<String, Object> serviceResults = dispatcher.runSync("createInvoice", input);
            if (ServiceUtil.isError(serviceResults)) {
                return ServiceUtil.returnError(errorMsg, null, null, serviceResults);
            }
            String invoiceId = (String) serviceResults.get("invoiceId");

            // keep track of the invoice total vs the promised return total (how much the customer promised to return)
            BigDecimal invoiceTotal = ZERO;
            BigDecimal promisedTotal = ZERO;

            // loop through shipment receipts to create invoice items and return item billings for each item and adjustment
            int invoiceItemSeqNum = 1;
            String invoiceItemSeqId = UtilFormatOut.formatPaddedNumber(invoiceItemSeqNum,
                    INVOICE_ITEM_SEQUENCE_ID_DIGITS);

            for (GenericValue item : billItems) {
                boolean shipmentReceiptFound = false;
                boolean itemIssuanceFound = false;
                GenericValue returnItem = null;
                BigDecimal quantity = BigDecimal.ZERO;

                if ("ShipmentReceipt".equals(item.getEntityName())) {
                    shipmentReceiptFound = true;
                } else if ("ItemIssuance".equals(item.getEntityName())) {
                    itemIssuanceFound = true;
                } else if ("ReturnItem".equals(item.getEntityName())) {
                    quantity = item.getBigDecimal("returnQuantity");
                    returnItem = item;
                } else {
                    Debug.logError("Unexpected entity " + item + " of type " + item.getEntityName(), module);
                }
                // we need the related return item and product
                if (shipmentReceiptFound) {
                    returnItem = item.getRelatedOne("ReturnItem", true);
                } else if (itemIssuanceFound) {
                    GenericValue shipmentItem = item.getRelatedOne("ShipmentItem", true);
                    GenericValue returnItemShipment = EntityUtil
                            .getFirst(shipmentItem.getRelated("ReturnItemShipment", null, null, false));
                    returnItem = returnItemShipment.getRelatedOne("ReturnItem", true);
                }
                if (returnItem == null)
                    continue; // Just to prevent NPE
                GenericValue product = returnItem.getRelatedOne("Product", true);

                // extract the return price as a big decimal for convenience
                BigDecimal returnPrice = returnItem.getBigDecimal("returnPrice");

                // determine invoice item type from the return item type
                String invoiceItemTypeId = getInvoiceItemType(delegator,
                        returnItem.getString("returnItemTypeId"), null, invoiceTypeId, null);
                if (invoiceItemTypeId == null) {
                    return ServiceUtil.returnError(errorMsg + UtilProperties.getMessage(resource,
                            "AccountingNoKnownInvoiceItemTypeReturnItemType",
                            UtilMisc.toMap("returnItemTypeId", returnItem.getString("returnItemTypeId")),
                            locale));
                }
                if (shipmentReceiptFound) {
                    quantity = item.getBigDecimal("quantityAccepted");
                } else if (itemIssuanceFound) {
                    quantity = item.getBigDecimal("quantity");
                }

                // create the invoice item for this shipment receipt
                input = UtilMisc.toMap("invoiceId", invoiceId, "invoiceItemTypeId", invoiceItemTypeId,
                        "quantity", quantity);
                input.put("invoiceItemSeqId", "" + invoiceItemSeqId); // turn the int into a string with ("" + int) hack
                input.put("amount", returnItem.get("returnPrice"));
                input.put("productId", returnItem.get("productId"));
                input.put("taxableFlag", product.get("taxable"));
                input.put("description", returnItem.get("description"));
                // TODO: what about the productFeatureId?
                input.put("userLogin", userLogin);
                serviceResults = dispatcher.runSync("createInvoiceItem", input);
                if (ServiceUtil.isError(serviceResults)) {
                    return ServiceUtil.returnError(errorMsg, null, null, serviceResults);
                }

                // copy the return item information into ReturnItemBilling
                input = UtilMisc.toMap("returnId", returnId, "returnItemSeqId",
                        returnItem.get("returnItemSeqId"), "invoiceId", invoiceId);
                input.put("invoiceItemSeqId", "" + invoiceItemSeqId); // turn the int into a string with ("" + int) hack
                input.put("quantity", quantity);
                input.put("amount", returnItem.get("returnPrice"));
                input.put("userLogin", userLogin);
                if (shipmentReceiptFound) {
                    input.put("shipmentReceiptId", item.get("receiptId"));
                }
                serviceResults = dispatcher.runSync("createReturnItemBilling", input);
                if (ServiceUtil.isError(serviceResults)) {
                    return ServiceUtil.returnError(errorMsg, null, null, serviceResults);
                }
                if (Debug.verboseOn()) {
                    Debug.logVerbose("Creating Invoice Item with amount " + returnPrice + " and quantity "
                            + quantity + " for shipment [" + item.getString("shipmentId") + ":"
                            + item.getString("shipmentItemSeqId") + "]", module);
                }

                String parentInvoiceItemSeqId = invoiceItemSeqId;
                // increment the seqId counter after creating the invoice item and return item billing
                invoiceItemSeqNum += 1;
                invoiceItemSeqId = UtilFormatOut.formatPaddedNumber(invoiceItemSeqNum,
                        INVOICE_ITEM_SEQUENCE_ID_DIGITS);

                // keep a running total (note: a returnItem may have many receipts. hence, the promised total quantity is the receipt quantityAccepted + quantityRejected)
                BigDecimal cancelQuantity = ZERO;
                if (shipmentReceiptFound) {
                    cancelQuantity = item.getBigDecimal("quantityRejected");
                } else if (itemIssuanceFound) {
                    cancelQuantity = item.getBigDecimal("cancelQuantity");
                }
                if (cancelQuantity == null)
                    cancelQuantity = ZERO;
                BigDecimal actualAmount = returnPrice.multiply(quantity).setScale(DECIMALS, ROUNDING);
                BigDecimal promisedAmount = returnPrice.multiply(quantity.add(cancelQuantity))
                        .setScale(DECIMALS, ROUNDING);
                invoiceTotal = invoiceTotal.add(actualAmount).setScale(DECIMALS, ROUNDING);
                promisedTotal = promisedTotal.add(promisedAmount).setScale(DECIMALS, ROUNDING);

                // for each adjustment related to this ReturnItem, create a separate invoice item
                List<GenericValue> adjustments = returnItem.getRelated("ReturnAdjustment", null, null, true);
                for (GenericValue adjustment : adjustments) {

                    if (adjustment.get("amount") == null) {
                        Debug.logWarning("Return adjustment [" + adjustment.get("returnAdjustmentId")
                                + "] has null amount and will be skipped", module);
                        continue;
                    }

                    // determine invoice item type from the return item type
                    invoiceItemTypeId = getInvoiceItemType(delegator,
                            adjustment.getString("returnAdjustmentTypeId"), null, invoiceTypeId, null);
                    if (invoiceItemTypeId == null) {
                        return ServiceUtil
                                .returnError(
                                        errorMsg + UtilProperties.getMessage(resource,
                                                "AccountingNoKnownInvoiceItemTypeReturnAdjustmentType",
                                                UtilMisc.toMap("returnAdjustmentTypeId",
                                                        adjustment.getString("returnAdjustmentTypeId")),
                                                locale));
                    }

                    // prorate the adjustment amount by the returned amount; do not round ratio
                    BigDecimal ratio = quantity.divide(returnItem.getBigDecimal("returnQuantity"), 100,
                            ROUNDING);
                    BigDecimal amount = adjustment.getBigDecimal("amount");
                    amount = amount.multiply(ratio).setScale(DECIMALS, ROUNDING);
                    if (Debug.verboseOn()) {
                        Debug.logVerbose("Creating Invoice Item with amount "
                                + adjustment.getBigDecimal("amount") + " prorated to " + amount
                                + " for return adjustment [" + adjustment.getString("returnAdjustmentId") + "]",
                                module);
                    }

                    // prepare invoice item data for this adjustment
                    input = UtilMisc.toMap("invoiceId", invoiceId, "invoiceItemTypeId", invoiceItemTypeId,
                            "quantity", BigDecimal.ONE);
                    input.put("amount", amount);
                    input.put("invoiceItemSeqId", "" + invoiceItemSeqId); // turn the int into a string with ("" + int) hack
                    input.put("productId", returnItem.get("productId"));
                    input.put("description", adjustment.get("description"));
                    input.put("overrideGlAccountId", adjustment.get("overrideGlAccountId"));
                    input.put("parentInvoiceId", invoiceId);
                    input.put("parentInvoiceItemSeqId", parentInvoiceItemSeqId);
                    input.put("taxAuthPartyId", adjustment.get("taxAuthPartyId"));
                    input.put("taxAuthGeoId", adjustment.get("taxAuthGeoId"));
                    input.put("userLogin", userLogin);

                    // only set taxable flag when the adjustment is not a tax
                    // TODO: Note that we use the value of Product.taxable here. This is not an ideal solution. Instead, use returnAdjustment.includeInTax
                    if (adjustment.get("returnAdjustmentTypeId").equals("RET_SALES_TAX_ADJ")) {
                        input.put("taxableFlag", "N");
                    }

                    // create the invoice item
                    serviceResults = dispatcher.runSync("createInvoiceItem", input);
                    if (ServiceUtil.isError(serviceResults)) {
                        return ServiceUtil.returnError(errorMsg, null, null, serviceResults);
                    }

                    // increment the seqId counter
                    invoiceItemSeqNum += 1;
                    invoiceItemSeqId = UtilFormatOut.formatPaddedNumber(invoiceItemSeqNum,
                            INVOICE_ITEM_SEQUENCE_ID_DIGITS);

                    // keep a running total (promised adjustment in this case is the same as the invoice adjustment)
                    invoiceTotal = invoiceTotal.add(amount).setScale(DECIMALS, ROUNDING);
                    promisedTotal = promisedTotal.add(amount).setScale(DECIMALS, ROUNDING);
                }
            }

            // ratio of the invoice total to the promised total so far or zero if the amounts were zero
            BigDecimal actualToPromisedRatio = ZERO;
            if (invoiceTotal.signum() != 0) {
                actualToPromisedRatio = invoiceTotal.divide(promisedTotal, 100, ROUNDING); // do not round ratio
            }

            // loop through return-wide adjustments and create invoice items for each
            List<GenericValue> adjustments = returnHeader.getRelated("ReturnAdjustment",
                    UtilMisc.toMap("returnItemSeqId", "_NA_"), null, true);
            for (GenericValue adjustment : adjustments) {

                // determine invoice item type from the return item type
                String invoiceItemTypeId = getInvoiceItemType(delegator,
                        adjustment.getString("returnAdjustmentTypeId"), null, invoiceTypeId, null);
                if (invoiceItemTypeId == null) {
                    return ServiceUtil
                            .returnError(
                                    errorMsg + UtilProperties
                                            .getMessage(resource,
                                                    "AccountingNoKnownInvoiceItemTypeReturnAdjustmentType",
                                                    UtilMisc.toMap("returnAdjustmentTypeId",
                                                            adjustment.getString("returnAdjustmentTypeId")),
                                                    locale));
                }

                // prorate the adjustment amount by the actual to promised ratio
                BigDecimal amount = adjustment.getBigDecimal("amount").multiply(actualToPromisedRatio)
                        .setScale(DECIMALS, ROUNDING);
                if (Debug.verboseOn()) {
                    Debug.logVerbose("Creating Invoice Item with amount " + adjustment.getBigDecimal("amount")
                            + " prorated to " + amount + " for return adjustment ["
                            + adjustment.getString("returnAdjustmentId") + "]", module);
                }

                // prepare the invoice item for the return-wide adjustment
                input = UtilMisc.toMap("invoiceId", invoiceId, "invoiceItemTypeId", invoiceItemTypeId,
                        "quantity", BigDecimal.ONE);
                input.put("amount", amount);
                input.put("invoiceItemSeqId", "" + invoiceItemSeqId); // turn the int into a string with ("" + int) hack
                input.put("description", adjustment.get("description"));
                input.put("overrideGlAccountId", adjustment.get("overrideGlAccountId"));
                input.put("taxAuthPartyId", adjustment.get("taxAuthPartyId"));
                input.put("taxAuthGeoId", adjustment.get("taxAuthGeoId"));
                input.put("userLogin", userLogin);

                // XXX TODO Note: we need to implement ReturnAdjustment.includeInTax for this to work properly
                input.put("taxableFlag", adjustment.get("includeInTax"));

                // create the invoice item
                serviceResults = dispatcher.runSync("createInvoiceItem", input);
                if (ServiceUtil.isError(serviceResults)) {
                    return ServiceUtil.returnError(errorMsg, null, null, serviceResults);
                }

                // increment the seqId counter
                invoiceItemSeqNum += 1;
                invoiceItemSeqId = UtilFormatOut.formatPaddedNumber(invoiceItemSeqNum,
                        INVOICE_ITEM_SEQUENCE_ID_DIGITS);
            }

            // Set the invoice to READY
            serviceResults = dispatcher.runSync("setInvoiceStatus", UtilMisc.<String, Object>toMap("invoiceId",
                    invoiceId, "statusId", "INVOICE_READY", "userLogin", userLogin));
            if (ServiceUtil.isError(serviceResults)) {
                return ServiceUtil.returnError(errorMsg, null, null, serviceResults);
            }

            // return the invoiceId
            results.put("invoiceId", invoiceId);
        }
        return results;
    } catch (GenericServiceException e) {
        Debug.logError(e, errorMsg + e.getMessage(), module);
        return ServiceUtil.returnError(errorMsg + e.getMessage());
    } catch (GenericEntityException e) {
        Debug.logError(e, errorMsg + e.getMessage(), module);
        return ServiceUtil.returnError(errorMsg + e.getMessage());
    }
}