You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ofbiz.apache.org by si...@apache.org on 2006/11/02 02:31:41 UTC

svn commit: r470173 - in /incubator/ofbiz/trunk/applications/accounting: config/AccountingUiLabels.properties servicedef/services_invoice.xml src/org/ofbiz/accounting/invoice/InvoiceServices.java

Author: sichen
Date: Wed Nov  1 17:31:40 2006
New Revision: 470173

URL: http://svn.apache.org/viewvc?view=rev&rev=470173
Log:
Invoicing services now support pro-rating of taxes based on product store settings and invoicing of additional shipping charges from the Shipment entity

Modified:
    incubator/ofbiz/trunk/applications/accounting/config/AccountingUiLabels.properties
    incubator/ofbiz/trunk/applications/accounting/servicedef/services_invoice.xml
    incubator/ofbiz/trunk/applications/accounting/src/org/ofbiz/accounting/invoice/InvoiceServices.java

Modified: incubator/ofbiz/trunk/applications/accounting/config/AccountingUiLabels.properties
URL: http://svn.apache.org/viewvc/incubator/ofbiz/trunk/applications/accounting/config/AccountingUiLabels.properties?view=diff&rev=470173&r1=470172&r2=470173
==============================================================================
--- incubator/ofbiz/trunk/applications/accounting/config/AccountingUiLabels.properties (original)
+++ incubator/ofbiz/trunk/applications/accounting/config/AccountingUiLabels.properties Wed Nov  1 17:31:40 2006
@@ -538,7 +538,6 @@
 AccountingErrorCreatingInvoiceFromOrderCheckPaymentAppl=Error creating invoice from order while checking payment applications
 AccountingEntityDataProblemCreatingInvoiceFromOrderItems=Entity/data problem creating invoice from order items: ${reason} 
 AccountingServiceOtherProblemCreatingInvoiceFromOrderItems=Service/other problem creating invoice from order items: ${reason} 
-AccountingTroubleCreateInvoicesFromShipmentsService=Trouble calling createInvoicesFromShipments service; invoice not created for shipment [${shipmentId}]
 AccountingShipmentsOfDifferentTypes=Shipments of different types found; shipment [${tmpShipmentId}] of type [${shipmentTypeId}] is of different type from the previous ones.
 AccountingTroubleGettingShipmentEntity=Trouble getting Shipment entity for shipment [${tmpShipmentId}]
 AccountingProblemGettingItemsFromShipments=Problem getting issued items from shipments
@@ -599,6 +598,18 @@
 AccountingInvoiceCommissionNoItems=No order or return items, not creating commission invoice
 AccountingInvoiceCommissionEntityDataProblem=Entity/data problem creating commission invoice: ${reason} 
 AccountingPageTitleAgreementPriceList=Agreement Price List
+AccountingAdditionalShippingChargeForShipment=Additional Shipping Charge For Shipment
+AccountingUnableToAuthAdditionalShipCharges=Unable to authorize additional shipping charges for shipmentId ${shipmentId} to paymentMethodId ${paymentMethodId} (orderPaymentPreferenceId ${orderPaymentPreferenceId})
+AccountingTroubleCallingAuthOrderPaymentPreferenceService=Trouble calling authOrderPaymentPreference service
+AccountingTroubleCallingCreateInvoicesFromShipmentService=Trouble calling createInvoicesFromShipment service; invoice not created for shipment [${shipmentId}]
+AccountingTroubleCallingCreateInvoicesFromShipmentsService=Trouble calling createInvoicesFromShipments service
+AccountingTroubleCallingCreateOrderAdjustmentService=Trouble calling createOrderAdjustment service
+AccountingProblemGettingOrderPaymentPreferences=Problem getting OrderPaymentPreference records
+AccountingTroubleCallingCreateOrderPaymentPreferenceService=Trouble calling createOrderPaymentPreference service
+AccountingTroubleCallingCalcTaxService=Trouble calling calcTaxService
+AccountingProblemStoringOrderAdjustments=Problem storing OrderAdjustments: ${orderAdjustments}
+AccountingErrorCreatingOrderAdjustmentBillingFromOrder=Error creating OrderAdjustmentBilling from order
+AccountingTroubleCallingCalculateInvoicedAdjustmentTotalService=Accounting trouble calling calculateInvoicedAdjustmentTotal service
 
 #Potentional Common definitions
 FormFieldTitle_emailAddressFrom=From Emailadress

Modified: incubator/ofbiz/trunk/applications/accounting/servicedef/services_invoice.xml
URL: http://svn.apache.org/viewvc/incubator/ofbiz/trunk/applications/accounting/servicedef/services_invoice.xml?view=diff&rev=470173&r1=470172&r2=470173
==============================================================================
--- incubator/ofbiz/trunk/applications/accounting/servicedef/services_invoice.xml (original)
+++ incubator/ofbiz/trunk/applications/accounting/servicedef/services_invoice.xml Wed Nov  1 17:31:40 2006
@@ -254,4 +254,10 @@
         <auto-attributes entity-name="InvoiceContactMech" include="pk" mode="IN" optional="false"/>
         <attribute name="contactMechId" type="String" mode="OUT" optional="true"/>
     </service>
+    <service name="calculateInvoicedAdjustmentTotal" engine="java"
+            location="org.ofbiz.accounting.invoice.InvoiceServices" invoke="calculateInvoicedAdjustmentTotalBd">
+        <description>Calculate the previously invoiced amount for an OrderAdjustment</description>
+        <attribute name="orderAdjustment" type="GenericValue" mode="IN" optional="false"/>
+        <attribute name="invoicedTotal" type="BigDecimal" mode="OUT" optional="false"/>
+    </service>
 </services>

Modified: incubator/ofbiz/trunk/applications/accounting/src/org/ofbiz/accounting/invoice/InvoiceServices.java
URL: http://svn.apache.org/viewvc/incubator/ofbiz/trunk/applications/accounting/src/org/ofbiz/accounting/invoice/InvoiceServices.java?view=diff&rev=470173&r1=470172&r2=470173
==============================================================================
--- incubator/ofbiz/trunk/applications/accounting/src/org/ofbiz/accounting/invoice/InvoiceServices.java (original)
+++ incubator/ofbiz/trunk/applications/accounting/src/org/ofbiz/accounting/invoice/InvoiceServices.java Wed Nov  1 17:31:40 2006
@@ -41,6 +41,7 @@
 import org.ofbiz.entity.GenericDelegator;
 import org.ofbiz.entity.GenericEntityException;
 import org.ofbiz.entity.GenericValue;
+import org.ofbiz.entity.util.EntityUtil;
 import org.ofbiz.entity.condition.EntityCondition;
 import org.ofbiz.entity.condition.EntityOperator;
 import org.ofbiz.entity.condition.EntityExpr;
@@ -473,6 +474,23 @@
                     while (itemAdjIter.hasNext()) {
                         GenericValue adj = (GenericValue) itemAdjIter.next();
                         
+                        // Check against OrderAdjustmentBilling to see how much of this adjustment has already been invoiced
+                        BigDecimal adjAlreadyInvoicedAmount = null;
+                        try {
+                            Map checkResult = dispatcher.runSync("calculateInvoicedAdjustmentTotal", UtilMisc.toMap("orderAdjustment", adj));
+                            adjAlreadyInvoicedAmount = (BigDecimal) checkResult.get("invoicedTotal");
+                        } catch (GenericServiceException e) {
+                            String errMsg = UtilProperties.getMessage(resource, "AccountingTroubleCallingCalculateInvoicedAdjustmentTotalService", locale);
+                            Debug.logError(e, errMsg, module);
+                            return ServiceUtil.returnError(errMsg);
+                        }
+        
+                        // If the absolute invoiced amount >= the abs of the adjustment amount, the full amount has already been invoiced,
+                        //  so skip this adjustment
+                        if (adjAlreadyInvoicedAmount.abs().compareTo(adj.getBigDecimal("amount").setScale(decimals, rounding).abs()) > 0) {
+                            continue;
+                        }
+        
                         BigDecimal amount = new BigDecimal(0);
                         if (adj.get("amount") != null) { 
                             // pro-rate the amount
@@ -520,7 +538,20 @@
 	                        if (ServiceUtil.isError(createInvoiceItemAdjResult)) {
 	                            return ServiceUtil.returnError(UtilProperties.getMessage(resource,"AccountingErrorCreatingInvoiceItemFromOrder",locale), null, null, createInvoiceItemAdjResult);
 	                        }
-	
+
+                            // Create the OrderAdjustmentBilling record
+                            Map createOrderAdjustmentBillingContext = FastMap.newInstance();
+                            createOrderAdjustmentBillingContext.put("orderAdjustmentId", adj.getString("orderAdjustmentId"));
+                            createOrderAdjustmentBillingContext.put("invoiceId", invoiceId);
+                            createOrderAdjustmentBillingContext.put("invoiceItemSeqId", invoiceItemSeqId);
+                            createOrderAdjustmentBillingContext.put("amount", new Double(amount.doubleValue()));
+                            createOrderAdjustmentBillingContext.put("userLogin", userLogin);
+
+                            Map createOrderAdjustmentBillingResult = dispatcher.runSync("createOrderAdjustmentBilling", createOrderAdjustmentBillingContext);
+                            if (ServiceUtil.isError(createOrderAdjustmentBillingResult)) {
+                                return ServiceUtil.returnError(UtilProperties.getMessage(resource,"AccountingErrorCreatingOrderAdjustmentBillingFromOrder",locale), null, null, createOrderAdjustmentBillingContext);
+                            }
+
 	                        // this adjustment amount
 	                        BigDecimal thisAdjAmount = new BigDecimal(amount.doubleValue()).setScale(decimals, rounding);
 	
@@ -545,22 +576,40 @@
             }
 
             // create header adjustments as line items -- always to tax/shipping last
-            List shipAdjustments = new ArrayList();
-            List taxAdjustments = new ArrayList();
+            Map shipAdjustments = new HashMap();
+            Map taxAdjustments = new HashMap();
 
             List headerAdjustments = orh.getOrderHeaderAdjustments();
             Iterator headerAdjIter = headerAdjustments.iterator();
             while (headerAdjIter.hasNext()) {
                 GenericValue adj = (GenericValue) headerAdjIter.next();
+
+                // Check against OrderAdjustmentBilling to see how much of this adjustment has already been invoiced
+                BigDecimal adjAlreadyInvoicedAmount = null;
+                try {
+                    Map checkResult = dispatcher.runSync("calculateInvoicedAdjustmentTotal", UtilMisc.toMap("orderAdjustment", adj));
+                    adjAlreadyInvoicedAmount = ((BigDecimal) checkResult.get("invoicedTotal")).setScale(decimals, rounding);
+                } catch (GenericServiceException e) {
+                    String errMsg = UtilProperties.getMessage(resource, "AccountingTroubleCallingCalculateInvoicedAdjustmentTotalService", locale);
+                    Debug.logError(e, errMsg, module);
+                    return ServiceUtil.returnError(errMsg);
+                }
+
+                // If the absolute invoiced amount >= the abs of the adjustment amount, the full amount has already been invoiced,
+                //  so skip this adjustment
+                if (adjAlreadyInvoicedAmount.abs().compareTo(adj.getBigDecimal("amount").setScale(decimals, rounding).abs()) > 0) {
+                    continue;
+                }
+
                 if ("SHIPPING_CHARGES".equals(adj.getString("orderAdjustmentTypeId"))) {
-                    shipAdjustments.add(adj);
+                    shipAdjustments.put(adj, adjAlreadyInvoicedAmount);
                 } else if ("SALES_TAX".equals(adj.getString("orderAdjustmentTypeId"))) {
-                    taxAdjustments.add(adj);
+                    taxAdjustments.put(adj, adjAlreadyInvoicedAmount);
                 } else {
                     // these will effect the shipping pro-rate (unless commented)
                     // other adjustment type
                     BigDecimal adjAmount = calcHeaderAdj(delegator, adj, invoiceType, invoiceId, invoiceItemSeqId, 
-                            orderSubTotal, invoiceSubTotal, invoiceQuantity, decimals, rounding, userLogin, dispatcher, locale);
+                            orderSubTotal, invoiceSubTotal, adj.getBigDecimal("amount").setScale(decimals, rounding), decimals, rounding, userLogin, dispatcher, locale);
                     // invoiceShipProRateAmount += adjAmount;
                     // do adjustments compound or are they based off subtotal? Here we will (unless commented)
                     // invoiceSubTotal += adjAmount;
@@ -573,41 +622,79 @@
 
             // next do the shipping adjustments.  Note that we do not want to add these to the invoiceSubTotal or orderSubTotal for pro-rating tax later, as that would cause
             // numerator/denominator problems when the shipping is not pro-rated but rather charged all on the first invoice
-            Iterator shipAdjIter = shipAdjustments.iterator();
+            Iterator shipAdjIter = shipAdjustments.keySet().iterator();
             while (shipAdjIter.hasNext()) {
                 GenericValue adj = (GenericValue) shipAdjIter.next();
+                BigDecimal adjAlreadyInvoicedAmount = (BigDecimal) shipAdjustments.get(adj);
+                
                 if ("N".equalsIgnoreCase(prorateShipping)) {
-                    if (previousInvoiceFound) {
-                        Debug.logInfo("Previous invoice found for this order [" + orderId + "]; shipping already billed", module);
-                        continue;
-                    } else {
-                        // this is the first invoice; bill it all now
-                        BigDecimal adjAmount = calcHeaderAdj(delegator, adj, invoiceType, invoiceId, invoiceItemSeqId, 
-                                new BigDecimal("1"), new BigDecimal("1"), totalItemsInOrder, decimals, rounding, userLogin, dispatcher, locale);
-                        // increment the counter
-                        invoiceItemSeqNum++;
-                        invoiceItemSeqId = UtilFormatOut.formatPaddedNumber(invoiceItemSeqNum, 2);
-                    }
+
+                    // Set the divisor and multiplier to 1 to avoid prorating
+                    BigDecimal divisor = new BigDecimal("1");
+                    BigDecimal multiplier = new BigDecimal("1");
+                    
+                    // The base amount in this case is the adjustment amount minus the total already invoiced for that adjustment, since
+                    //  it won't be prorated
+                    BigDecimal baseAmount = adj.getBigDecimal("amount").setScale(decimals, rounding).subtract(adjAlreadyInvoicedAmount);
+                    BigDecimal adjAmount = calcHeaderAdj(delegator, adj, invoiceType, invoiceId, invoiceItemSeqId, 
+                            divisor, multiplier, baseAmount, decimals, rounding, userLogin, dispatcher, locale);
                 } else {
-                    // pro-rate the shipping amount based on shippable information
+
+                    // Pro-rate the shipping amount based on shippable information
+                    BigDecimal divisor = shippableAmount;
+                    BigDecimal multiplier = invoiceShipProRateAmount;
+                    
+                    // The base amount in this case is the adjustment amount, since we want to prorate based on the full amount
+                    BigDecimal baseAmount = adj.getBigDecimal("amount").setScale(decimals, rounding);
                     BigDecimal adjAmount = calcHeaderAdj(delegator, adj, invoiceType, invoiceId, invoiceItemSeqId, 
-                            shippableAmount, invoiceShipProRateAmount, invoiceQuantity, decimals, rounding, userLogin, dispatcher, locale);
-                    // increment the counter
-                    invoiceItemSeqNum++;
-                    invoiceItemSeqId = UtilFormatOut.formatPaddedNumber(invoiceItemSeqNum, 2);
+                            divisor, multiplier, baseAmount, decimals, rounding, userLogin, dispatcher, locale);
                 }
+
+                // Increment the counter
+                invoiceItemSeqNum++;
+                invoiceItemSeqId = UtilFormatOut.formatPaddedNumber(invoiceItemSeqNum, 2);
             }
 
             // last do the tax adjustments
-            Iterator taxAdjIter = taxAdjustments.iterator();
+            String prorateTaxes = productStore.getString("prorateTaxes");
+            if (prorateTaxes == null) {
+                prorateTaxes = "Y";
+            }            
+            Iterator taxAdjIter = taxAdjustments.keySet().iterator();
             while (taxAdjIter.hasNext()) {
                 GenericValue adj = (GenericValue) taxAdjIter.next();
-                // note this should use invoice decimals & rounding instead of taxDecimals and taxRounding, because it will be added to the invoice 
-                BigDecimal adjAmount = calcHeaderAdj(delegator, adj, invoiceType, invoiceId, invoiceItemSeqId, 
-                        orderSubTotal, invoiceSubTotal, invoiceQuantity, decimals, rounding, userLogin, dispatcher, locale);
-                // this doesn't really effect anything; but just for our totals
-                // since it will no longer be used in pro-rating, we can just round off now
+                BigDecimal adjAlreadyInvoicedAmount = (BigDecimal) taxAdjustments.get(adj);
+                BigDecimal adjAmount = null;
+                
+                if ("N".equalsIgnoreCase(prorateTaxes)) {
+
+                    // Set the divisor and multiplier to 1 to avoid prorating
+                    BigDecimal divisor = new BigDecimal("1");
+                    BigDecimal multiplier = new BigDecimal("1");
+                    
+                    // The base amount in this case is the adjustment amount minus the total already invoiced for that adjustment, since
+                    //  it won't be prorated
+                    //  Note this should use invoice decimals & rounding instead of taxDecimals and taxRounding for tax adjustments, because it will be added to the invoice 
+                    BigDecimal baseAmount = adj.getBigDecimal("amount").setScale(decimals, rounding).subtract(adjAlreadyInvoicedAmount);
+                    adjAmount = calcHeaderAdj(delegator, adj, invoiceType, invoiceId, invoiceItemSeqId, 
+                             divisor, multiplier, baseAmount, decimals, rounding, userLogin, dispatcher, locale);
+                } else {
+
+                    // Pro-rate the tax amount based on shippable information
+                    BigDecimal divisor = orderSubTotal;
+                    BigDecimal multiplier = invoiceSubTotal;
+                    
+                    // The base amount in this case is the adjustment amount, since we want to prorate based on the full amount
+                    //  Note this should use invoice decimals & rounding instead of taxDecimals and taxRounding for tax adjustments, because it will be added to the invoice 
+                    BigDecimal baseAmount = adj.getBigDecimal("amount").setScale(decimals, rounding);
+                    adjAmount = calcHeaderAdj(delegator, adj, invoiceType, invoiceId, invoiceItemSeqId, 
+                            divisor, multiplier, baseAmount, decimals, rounding, userLogin, dispatcher, locale);
+                }
                 invoiceSubTotal = invoiceSubTotal.add(adjAmount).setScale(decimals, rounding);                
+
+                // Increment the counter
+                invoiceItemSeqNum++;
+                invoiceItemSeqId = UtilFormatOut.formatPaddedNumber(invoiceItemSeqNum, 2);
             }
 
             // check for previous order payments
@@ -889,8 +976,8 @@
             Map result = dispatcher.runSync("createInvoicesFromShipments", serviceContext);
             invoicesCreated = (List) result.get("invoicesCreated");
         } catch (GenericServiceException e) {
-            Debug.logError(e, "Trouble calling createInvoicesFromShipments service; invoice not created for shipment [" + shipmentId + "]", module);
-            return ServiceUtil.returnError(UtilProperties.getMessage(resource,"AccountingTroubleCreateInvoicesFromShipmentsService",UtilMisc.toMap("shipmentId",shipmentId),locale));
+            Debug.logError(e, "Trouble calling createInvoicesFromShipment service; invoice not created for shipment [" + shipmentId + "]", module);
+            return ServiceUtil.returnError(UtilProperties.getMessage(resource,"AccountingTroubleCallingCreateInvoicesFromShipmentService",UtilMisc.toMap("shipmentId",shipmentId),locale));
         }
         Map response = ServiceUtil.returnSuccess();
         response.put("invoicesCreated", invoicesCreated);
@@ -1066,6 +1153,180 @@
                 itemQtyAvail.put(issue.getString("orderItemSeqId"), billAvail);
             }
 
+            OrderReadHelper orh = new OrderReadHelper(delegator, orderId);
+            GenericValue productStore = orh.getProductStore();
+
+            // If shipping charges are not prorated, the shipments need to be examined for additional shipping charges
+            if (productStore.getString("prorateShipping").equals("N")) {
+    
+                // Get the set of filtered shipments
+                List invoiceableShipmentIds = EntityUtil.getFieldListFromEntityList(toBillItems, "shipmentId", true);
+                List invoiceableShipments = null;
+                try {
+                    invoiceableShipments = delegator.findByCondition("Shipment", new EntityExpr("shipmentId", EntityOperator.IN, invoiceableShipmentIds), null, null);
+                } catch( GenericEntityException e ) {
+                    String errMsg = UtilProperties.getMessage(resource, "AccountingTroubleCallingCreateInvoicesFromShipmentsService", locale);
+                    Debug.logError(e, errMsg, module);
+                    return ServiceUtil.returnError(errMsg);
+                }
+                
+                // Total the additional shipping charges for the shipments
+                Map additionalShippingCharges = new HashMap();
+                BigDecimal totalAdditionalShippingCharges = ZERO;
+                Iterator isit = invoiceableShipments.iterator();
+                while(isit.hasNext()) {
+                    GenericValue shipment = (GenericValue) isit.next();
+                    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
+                    Iterator ascit = additionalShippingCharges.keySet().iterator();
+                    while (ascit.hasNext()) {
+                        GenericValue shipment = (GenericValue) ascit.next();
+                        String shipmentId = shipment.getString("shipmentId");
+                        BigDecimal additionalShippingCharge = (BigDecimal) additionalShippingCharges.get(shipment);
+                        Map createOrderAdjustmentContext = new HashMap();
+                        createOrderAdjustmentContext.put("orderId", orderId);
+                        createOrderAdjustmentContext.put("orderAdjustmentTypeId", "SHIPPING_CHARGES");
+                        createOrderAdjustmentContext.put("description", UtilProperties.getMessage(resource, "AccountingAdditionalShippingChargeForShipment", locale) + " #" + shipmentId);
+                        createOrderAdjustmentContext.put("sourceReferenceId", shipmentId);
+                        createOrderAdjustmentContext.put("amount", new Double(additionalShippingCharge.doubleValue()));
+                        createOrderAdjustmentContext.put("userLogin", context.get("userLogin"));
+                        String shippingOrderAdjustmentId = null;
+                        try {
+                            Map createOrderAdjustmentResult = dispatcher.runSync("createOrderAdjustment", createOrderAdjustmentContext);
+                            shippingOrderAdjustmentId = (String) createOrderAdjustmentResult.get("orderAdjustmentId");
+                        } catch (GenericServiceException e) {
+                            String errMsg = UtilProperties.getMessage(resource, "AccountingTroubleCallingCreateOrderAdjustmentService", locale);
+                            Debug.logError(e, errMsg, module);
+                            return ServiceUtil.returnError(errMsg);
+                        }
+
+                        // 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");
+                        } catch( GenericEntityException e ) {
+                            String errMsg = UtilProperties.getMessage(resource, "AccountingTroubleCallingCreateInvoicesFromShipmentService", locale);
+                            Debug.logError(e, errMsg, module);
+                            return ServiceUtil.returnError(errMsg);
+                        }
+                        
+                        List emptyList = new ArrayList();
+                        Map calcTaxContext = new HashMap();
+                        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("itemShippingList", emptyList);
+
+                        List orderAdjustments = null;
+                        Map calcTaxResult = null;
+                        try {
+                            calcTaxResult = dispatcher.runSync("calcTax", calcTaxContext);
+                        } catch (GenericServiceException e) {
+                            String errMsg = UtilProperties.getMessage(resource, "AccountingTroubleCallingCalcTaxService", locale);
+                            Debug.logError(e, errMsg, module);
+                            return ServiceUtil.returnError(errMsg);
+                        }
+                        orderAdjustments = (List) calcTaxResult.get("orderAdjustments");
+
+                        // If we have any OrderAdjustments due to tax on shipping, store them and add them to the total
+                        if (calcTaxResult != null && orderAdjustments != null) {
+                            Iterator oait = orderAdjustments.iterator();
+                            while (oait.hasNext()) {
+                                GenericValue orderAdjustment = (GenericValue) oait.next();
+                                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 ) {
+                                String errMsg = UtilProperties.getMessage(resource, "AccountingProblemStoringOrderAdjustments", UtilMisc.toMap("orderAdjustments", orderAdjustments), locale);
+                                Debug.logError(e, errMsg, module);
+                                return ServiceUtil.returnError(errMsg);
+                            }
+                        }
+
+                        // If part of the order was paid via credit card, try to charge it for the additional shipping
+                        List orderPaymentPreferences = new ArrayList();
+                        try {
+                            orderPaymentPreferences = delegator.findByAnd("OrderPaymentPreference", UtilMisc.toMap("orderId", orderId));
+                        } catch( GenericEntityException e ) {
+                            String errMsg = UtilProperties.getMessage(resource, "AccountingProblemGettingOrderPaymentPreferences", locale);
+                            Debug.logError(e, errMsg, module);
+                            return ServiceUtil.returnError(errMsg);
+                        }
+
+                        //  Use the first credit card we find, for the sake of simplicity
+                        String paymentMethodId = null;
+                        Iterator oppit = orderPaymentPreferences.iterator();
+                        while (oppit.hasNext()) {
+                            GenericValue orderPaymentPreference = (GenericValue) oppit.next();
+                            if (orderPaymentPreference.getString("paymentMethodTypeId").equals("CREDIT_CARD")) {
+                                paymentMethodId = orderPaymentPreference.getString("paymentMethodId");
+                                break;
+                            }
+                        }
+                        
+                        if (paymentMethodId != null ) {
+                            
+                            // Create a new OrderPaymentPreference for the order to handle the additional charge. Don't
+                            //  set the maxAmount so that it doesn't interfere with other authorizations
+                            Map serviceContext = UtilMisc.toMap("orderId", orderId, "paymentMethodId", paymentMethodId, "paymentMethodTypeId", "CREDIT_CARD", "userLogin", context.get("userLogin"));
+                            String orderPaymentPreferenceId = null;
+                            try {
+                                Map result = dispatcher.runSync("createOrderPaymentPreference", serviceContext);
+                                orderPaymentPreferenceId = (String) result.get("orderPaymentPreferenceId");
+                            } catch (GenericServiceException e) {
+                                String errMsg = UtilProperties.getMessage(resource, "AccountingTroubleCallingCreateOrderPaymentPreferenceService", locale);
+                                Debug.logError(e, errMsg, module);
+                                return ServiceUtil.returnError(errMsg);
+                            }
+
+                            // Attempt to authorize the new orderPaymentPreference
+                            Map authResult = null;
+                            try {
+
+                                // Use an overrideAmount because the maxAmount wasn't set on the OrderPaymentPreference
+                                authResult = dispatcher.runSync("authOrderPaymentPreference", UtilMisc.toMap("orderPaymentPreferenceId", orderPaymentPreferenceId, "overrideAmount", new Double(totalAdditionalShippingCharges.doubleValue()), "userLogin", context.get("userLogin")));
+                            } catch (GenericServiceException e) {
+                                String errMsg = UtilProperties.getMessage(resource, "AccountingTroubleCallingAuthOrderPaymentPreferenceService", locale);
+                                Debug.logError(e, errMsg, module);
+                                return ServiceUtil.returnError(errMsg);
+                            }
+
+                            // 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);
+                            }
+                            
+                        } 
+                    } 
+                }
+            }
+
             // call the createInvoiceForOrder service for each order
             Map serviceContext = UtilMisc.toMap("orderId", orderId, "billItems", toBillItems, "userLogin", context.get("userLogin"));
             try {
@@ -1490,16 +1751,16 @@
     }
 
     private static BigDecimal calcHeaderAdj(GenericDelegator delegator, GenericValue adj, String invoiceTypeId, String invoiceId, String invoiceItemSeqId, 
-            BigDecimal divisor, BigDecimal multiplier, BigDecimal invoiceQuantity, int decimals, int rounding, GenericValue userLogin, LocalDispatcher dispatcher, Locale locale) {
+            BigDecimal divisor, BigDecimal multiplier, BigDecimal baseAmount, int decimals, int rounding, GenericValue userLogin, LocalDispatcher dispatcher, Locale locale) {
         BigDecimal adjAmount = ZERO;
         if (adj.get("amount") != null) {
+
             // pro-rate the amount
-            BigDecimal baseAdjAmount = adj.getBigDecimal("amount");
             BigDecimal amount = ZERO;
             // make sure the divisor is not 0 to avoid NaN problems; just leave the amount as 0 and skip it in essense
             if (divisor.signum() != 0) {
                 // multiply first then divide to avoid rounding errors
-                amount = baseAdjAmount.multiply(multiplier).divide(divisor, decimals, rounding);
+                amount = baseAmount.multiply(multiplier).divide(divisor, decimals, rounding);
             }
             if (amount.signum() != 0) {
                 Map createInvoiceItemContext = FastMap.newInstance();
@@ -1530,6 +1791,21 @@
                 if (ServiceUtil.isError(createInvoiceItemResult)) {
                     ServiceUtil.returnError(UtilProperties.getMessage(resource,"AccountingErrorCreatingInvoiceItemFromOrder",locale), null, null, createInvoiceItemResult);
                 }
+
+                // Create the OrderAdjustmentBilling record
+                Map createOrderAdjustmentBillingContext = FastMap.newInstance();
+                createOrderAdjustmentBillingContext.put("orderAdjustmentId", adj.getString("orderAdjustmentId"));
+                createOrderAdjustmentBillingContext.put("invoiceId", invoiceId);
+                createOrderAdjustmentBillingContext.put("invoiceItemSeqId", invoiceItemSeqId);
+                createOrderAdjustmentBillingContext.put("amount", new Double(amount.doubleValue()));
+                createOrderAdjustmentBillingContext.put("userLogin", userLogin);
+
+                try {
+                    Map createOrderAdjustmentBillingResult = dispatcher.runSync("createOrderAdjustmentBilling", createOrderAdjustmentBillingContext);
+                } catch( GenericServiceException e ) {
+                    ServiceUtil.returnError(UtilProperties.getMessage(resource,"AccountingErrorCreatingOrderAdjustmentBillingFromOrder",locale), null, null, createOrderAdjustmentBillingContext);
+                }
+
             }
             amount.setScale(decimals, rounding);
             adjAmount = amount;
@@ -1573,6 +1849,21 @@
                 if (ServiceUtil.isError(createInvoiceItemResult)) {
                     ServiceUtil.returnError(UtilProperties.getMessage(resource,"AccountingErrorCreatingInvoiceItemFromOrder",locale), null, null, createInvoiceItemResult);
                 }
+
+                // Create the OrderAdjustmentBilling record
+                Map createOrderAdjustmentBillingContext = FastMap.newInstance();
+                createOrderAdjustmentBillingContext.put("orderAdjustmentId", adj.getString("orderAdjustmentId"));
+                createOrderAdjustmentBillingContext.put("invoiceId", invoiceId);
+                createOrderAdjustmentBillingContext.put("invoiceItemSeqId", invoiceItemSeqId);
+                createOrderAdjustmentBillingContext.put("amount", new Double(amount.doubleValue()));
+                createOrderAdjustmentBillingContext.put("userLogin", userLogin);
+
+                try {
+                    Map createOrderAdjustmentBillingResult = dispatcher.runSync("createOrderAdjustmentBilling", createOrderAdjustmentBillingContext);
+                } catch( GenericServiceException e ) {
+                    ServiceUtil.returnError(UtilProperties.getMessage(resource,"AccountingErrorCreatingOrderAdjustmentBillingFromOrder",locale), null, null, createOrderAdjustmentBillingContext);
+                }
+
             }
             amount.setScale(decimals, rounding);
             adjAmount = amount;
@@ -2381,6 +2672,30 @@
         errorMessageList.add("??unsuitable parameters passed...?? This message.... should never be shown\n");
         errorMessageList.add("--Input parameters...InvoiceId:" + invoiceId + " invoiceItemSeqId:" + invoiceItemSeqId + " PaymentId:" + paymentId + " toPaymentId:" + toPaymentId + "\n  paymentApplicationId:" + paymentApplicationId + " amountApplied:" + amountApplied);
         return ServiceUtil.returnError(errorMessageList);
+    }
+
+    public static Map calculateInvoicedAdjustmentTotalBd(DispatchContext dctx, Map context) {
+        GenericDelegator delegator = dctx.getDelegator();
+        Locale locale = (Locale) context.get("locale");
+        GenericValue orderAdjustment = (GenericValue) context.get("orderAdjustment");
+        Map result = ServiceUtil.returnSuccess();
+        
+        BigDecimal invoicedTotal = ZERO;
+        List invoicedAdjustments = null;
+        try {
+            invoicedAdjustments = delegator.findByAnd("OrderAdjustmentBilling", UtilMisc.toMap("orderAdjustmentId", orderAdjustment.getString("orderAdjustmentId")));
+        } catch( GenericEntityException e ) {
+            String errMsg = UtilProperties.getMessage(resource, "AccountingTroubleCallingCalculateInvoicedAdjustmentTotalService" + ": " + e.getMessage(), locale);
+            Debug.logError(e, errMsg, module);
+            return ServiceUtil.returnError(errMsg);
+        }
+        Iterator iait = invoicedAdjustments.iterator();
+        while (iait.hasNext()) {
+            GenericValue invoicedAdjustment = (GenericValue) iait.next();
+            invoicedTotal = invoicedTotal.add(invoicedAdjustment.getBigDecimal("amount").setScale(decimals, rounding));
+        }
+        result.put("invoicedTotal", invoicedTotal);
+        return result;
     }
 
     /**