List of usage examples for java.math BigDecimal signum
public int signum()
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()); } }