svn commit: r1643077 [1/2] - in /ofbiz/trunk/applications: commonext/webapp/ordermgr-js/ order/config/ order/script/org/ofbiz/order/order/ order/script/org/ofbiz/order/test/ order/servicedef/ order/src/org/ofbiz/order/order/ order/src/org/ofbiz/order/s...

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

svn commit: r1643077 [1/2] - in /ofbiz/trunk/applications: commonext/webapp/ordermgr-js/ order/config/ order/script/org/ofbiz/order/order/ order/script/org/ofbiz/order/test/ order/servicedef/ order/src/org/ofbiz/order/order/ order/src/org/ofbiz/order/s...

jleroux@apache.org
Author: jleroux
Date: Wed Dec  3 10:15:20 2014
New Revision: 1643077

URL: http://svn.apache.org/r1643077
Log:
Implements "Allow to edit ship groups contents after and order has been created" https://issues.apache.org/jira/browse/OFBIZ-5761

From an UI perspective this adds a "View ship group by order item" button which allows to create new ship groups by order items and to move the quantity of the order item between ship groups.
This is the adaptation of the "oisg-management" addon for R12.04 to the trunk. Thanks to Nicolas and Leila from Nereides for their help

For more information refer to the Jira issue.

Modified:
    ofbiz/trunk/applications/commonext/webapp/ordermgr-js/OrderShippingInfo.js
    ofbiz/trunk/applications/order/config/OrderErrorUiLabels.xml
    ofbiz/trunk/applications/order/config/OrderUiLabels.xml
    ofbiz/trunk/applications/order/script/org/ofbiz/order/order/OrderServices.xml
    ofbiz/trunk/applications/order/script/org/ofbiz/order/test/ShoppingCartTests.xml
    ofbiz/trunk/applications/order/servicedef/services.xml
    ofbiz/trunk/applications/order/src/org/ofbiz/order/order/OrderReadHelper.java
    ofbiz/trunk/applications/order/src/org/ofbiz/order/order/OrderServices.java
    ofbiz/trunk/applications/order/src/org/ofbiz/order/shoppingcart/ShoppingCart.java
    ofbiz/trunk/applications/order/src/org/ofbiz/order/shoppingcart/ShoppingCartItem.java
    ofbiz/trunk/applications/order/src/org/ofbiz/order/shoppingcart/ShoppingCartServices.java
    ofbiz/trunk/applications/order/testdef/ShoppingCartTests.xml
    ofbiz/trunk/applications/order/webapp/ordermgr/WEB-INF/controller.xml
    ofbiz/trunk/applications/order/webapp/ordermgr/order/editorderitems.ftl
    ofbiz/trunk/applications/order/webapp/ordermgr/order/ordershippinginfo.ftl

Modified: ofbiz/trunk/applications/commonext/webapp/ordermgr-js/OrderShippingInfo.js
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/commonext/webapp/ordermgr-js/OrderShippingInfo.js?rev=1643077&r1=1643076&r2=1643077&view=diff
==============================================================================
--- ofbiz/trunk/applications/commonext/webapp/ordermgr-js/OrderShippingInfo.js (original)
+++ ofbiz/trunk/applications/commonext/webapp/ordermgr-js/OrderShippingInfo.js Wed Dec  3 10:15:20 2014
@@ -31,4 +31,137 @@ jQuery(document).ready( function() {
         // Populate state list based on default country
         getAssociatedStateList('countryGeoId', 'stateProvinceGeoId', 'advice-required-stateProvinceGeoId', 'states');
     }
-});
\ No newline at end of file
+});
+
+function showEdit(edit, index) {
+    var sufix = index;
+    if (sufix == '-1') {
+       sufix = "";
+    }
+
+    //display / hide edit element
+    var element = document.getElementById("edit" + sufix);
+    if (element != null) {
+       var objBranch = element.style;
+       if ("edit" == edit) {
+         objBranch.display = "block";
+       } else {
+         objBranch.display = "none";
+       }
+    }
+    var element = document.getElementById("display" + sufix);
+    if (element != null) {
+       var objBranch = element.style;
+       if (edit == "display") {
+          objBranch.display = "block";
+       } else {
+          objBranch.display = "none";
+       }
+    }
+
+    var next = true;
+    for(var i = 0; next ; i++) {
+      //hide / show display quantity
+      var element = document.getElementById("displayQuantity" + sufix + i);
+      if (element != null) {
+        var objBranch = element.style;
+        if (edit == "display") {
+          objBranch.display = "block";
+        } else {
+          objBranch.display = "none";
+        }
+      }
+
+      //hide / show edit quantity
+      var element = document.getElementById("editQuantity" + sufix + i);
+      if (element != null) {
+        var objBranch = element.style;
+        if (edit == "edit") {
+          objBranch.display = "block";
+        } else {
+          objBranch.display = "none";
+        }
+      }
+      if (element == null) {
+         next = false;
+      }
+    }
+
+    //Hide display OISG edit view
+    var element = document.getElementById("OISGEdit" + sufix);
+    if (element != null) {
+      var objBranch = element.style;
+      if (edit == "edit") {
+        objBranch.display = "block";
+      }
+      else {
+        objBranch.display = "none";
+      }
+    }
+}
+
+function restoreEditField(index) {
+    var sufix = index;
+    if (sufix == '-1')
+      sufix = "";
+
+    //display / hide edit element
+    var next = true;
+    for(var i = 0; next ; i++) {
+      var editElement = document.getElementById("edit" + index + "_o_" + i);
+      if (editElement == null) {
+         next = false;
+      } else {
+         editElement.value = editElement.title;
+      }
+    }
+}
+
+function showShipByDate(e, id) {
+    var element = document.getElementById(id);
+    if (e.value == "new") {
+       element.style.display = "block";
+    } else {
+       element.style.display = "none";
+    }
+}
+
+function showView(view, index) {
+    var sufix = index;
+    if (sufix == '-1') {
+       sufix = "";
+    }
+    //display / hide buttonDisplay element
+    var element = document.getElementById("display" + sufix);
+    if (element != null) {  
+      var objBranch = element.style;
+      if ("view" == view) {
+        objBranch.display = "none";
+      } else {
+        objBranch.display = "block";
+      }
+
+      //display / hide buttonEdit element
+      var element = document.getElementById("view" + sufix);
+      if (element != null) {  
+        var objBranch = element.style;
+        if ("view" == view) {
+          objBranch.display = "block";
+        } else {
+          objBranch.display = "none";
+        }
+      }
+
+      //Hide display OISG show view
+      var element = document.getElementById("OISGView" + sufix);
+      if (element != null) {
+        var objBranch = element.style;
+        if (view == "view") {
+          objBranch.display = "block";
+        }
+        else {
+          objBranch.display = "none";
+        }
+      }
+    }
+}

Modified: ofbiz/trunk/applications/order/config/OrderErrorUiLabels.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/order/config/OrderErrorUiLabels.xml?rev=1643077&r1=1643076&r2=1643077&view=diff
==============================================================================
--- ofbiz/trunk/applications/order/config/OrderErrorUiLabels.xml (original)
+++ ofbiz/trunk/applications/order/config/OrderErrorUiLabels.xml Wed Dec  3 10:15:20 2014
@@ -211,11 +211,15 @@
         <value xml:lang="zh">无法更新;设置OrderShipmentPreference时出现问题</value>
         <value xml:lang="zh_TW">無法更新;設置OrderShipmentPreference時出現問題</value>
     </property>
+    <property key="OrderCartShipGroupAssocNotFound">
+        <value xml:lang="en">Cart ship group association not found</value>
+        <value xml:lang="fr">Association avec le groupe d'expédition non trouvée</value>
+    </property>
     <property key="OrderCartShipGroupNotFound">
         <value xml:lang="de">Warenkorb-Versandgruppe nicht gefunden [ ${groupIndex} ]</value>
         <value xml:lang="en">Cart ship group not found [ ${groupIndex} ]</value>
         <value xml:lang="es">Grupo de envío del carro no encontrado [ ${groupIndex} ]</value>
-        <value xml:lang="fr">Groupe de chariots non trouvé [ ${groupIndex} ]</value>
+        <value xml:lang="fr">Groupe d'expédition non trouvé [ ${groupIndex} ]</value>
         <value xml:lang="it">Gruppo Spedizione Carrello non trovato [ ${groupIndex} ]</value>
         <value xml:lang="ja">買い物かご発送グループが見つかりません [ ${groupIndex} ]</value>
         <value xml:lang="nl">Winkelwagen verzendgroep niet gevinden [ ${groupIndex} ]</value>
@@ -227,6 +231,18 @@
         <value xml:lang="zh">没有找到购物车运输组 [ ${groupIndex} ]</value>
         <value xml:lang="zh_TW">沒有找到購物車運輸組 [ ${groupIndex} ]</value>
     </property>
+    <property key="OrderCartShipGroupSeqIdIsMandatory">
+        <value xml:lang="en">Cart shipGroupSeqId is mandatory</value>
+        <value xml:lang="fr">La reference du groupe d'expédition est obligatoire</value>
+    </property>
+    <property key="OrderCartShipGroupPartyCarrierNotFound">
+        <value xml:lang="en">No Party Carrier found for [${partyId}]</value>
+        <value xml:lang="fr">Aucun transporteur trouvé pour la reference [${partyId}]</value>
+    </property>
+    <property key="OrderCartShipGroupShipmentMethodNotFound">
+        <value xml:lang="en">No Shipment Method found for [${shipmentMethodTypeId}]</value>
+        <value xml:lang="fr">Aucun mode d'expedition trouvé pour la reference [${shipmentMethodTypeId}]</value>
+    </property>
     <property key="OrderCaughtExceptionOnCartUpdate">
         <value xml:lang="de">Fehler bei Änderung des Warenkorbes. </value>
         <value xml:lang="en">Caught exception on cart update. </value>
@@ -323,7 +339,7 @@
     </property>
     <property key="OrderCouldNotFindRelatedFixedAssetForTheProduct">
         <value xml:lang="de">Zugehörige Anlage zu Produkt ${productId} konnte nicht gefunden werden</value>
-        <value xml:lang="en">Could not find related Fixed Asset for the product : ${productId}</value>
+        <value xml:lang="en">Could not find related Fixed Asset for the product: ${productId}</value>
         <value xml:lang="es">No se puede encontrar el activo fijo relacionado para el producto: ${productId}</value>
         <value xml:lang="fr">Vous ne pouvez pas trouver le Fixed Asset pour l'article : ${productId}</value>
         <value xml:lang="it">Non è possibile trovare Cespite per il prodotto : ${productId}</value>
@@ -1167,6 +1183,10 @@
         <value xml:lang="zh">错误:订单明细和/或订单头不存在</value>
         <value xml:lang="zh_TW">錯誤:訂單明細和/或訂單頭不存在</value>
     </property>
+    <property key="OrderErrorOrderItemAlreadyRelatedToShipGroup">
+        <value xml:lang="en">Order Item is already related To This OISG</value>
+        <value xml:lang="fr">La ligne de commande est déja associée au groupe d'expédition</value>
+    </property>
     <property key="OrderErrorOrderItemCantBeModified">
         <value xml:lang="de">FEHLER : Auftragsposition kann nicht geändert werden</value>
         <value xml:lang="en">ERROR : OrderItem can't be modified</value>
@@ -2886,6 +2906,18 @@
         <value xml:lang="zh">产品店铺不存在</value>
         <value xml:lang="zh_TW">產品店鋪不存在</value>
     </property>
+    <property key="OrderQuantityAssociatedCannotBeNullOrNegative">
+        <value xml:lang="en">Quantity associated can not be null or negative.</value>
+        <value xml:lang="fr">La quantité associée ne peut être négative ou nulle.</value>
+    </property>
+    <property key="OrderQuantityAssociatedIsBiggerThanOrderItemQuantity">
+        <value xml:lang="en">Quantity associated is bigger than order item quantity.</value>
+        <value xml:lang="fr">La quantité associée est plus grande que celle commandée.</value>
+    </property>
+    <property key="OrderQuantityAssociatedIsLessThanOrderItemQuantity">
+        <value xml:lang="en">Quantity associated is less than order item quantity.</value>
+        <value xml:lang="fr">La quantité totale associée aux groupe(s) d'expédition(s) est plus petite que celle commandée.</value>
+    </property>
     <property key="OrderQuickAddOrderItemError">
         <value xml:lang="de">Schnelles Hinzufügen von Auftragspositionen</value>
         <value xml:lang="en">Quick  Add  Order  Item</value>
@@ -4178,6 +4210,10 @@
         <value xml:lang="zh">获得订单明细的运输组相关资源列表时出现问题</value>
         <value xml:lang="zh_TW">獲得訂單明細的運輸組相關資源列表時出現問題</value>
     </property>
+    <property key="OrderUnableToAddItemToOISG">
+        <value xml:lang="en">Unable to add order item to ship group: </value>
+        <value xml:lang="fr">Impossible d'ajouter la ligne de commande au groupe d'expédition : </value>
+    </property>
     <property key="OrderUnableToAddItemToShoppingList">
         <value xml:lang="de">Fehler beim Hinzufügen einer Position zur Einkaufsliste - ${shoppingListId}</value>
         <value xml:lang="en">Unable to add item to shopping list - ${shoppingListId}</value>
@@ -4193,6 +4229,10 @@
         <value xml:lang="zh">无法把明细添加到购物列表 - ${shoppingListId}</value>
         <value xml:lang="zh_TW">無法把明細添加到購物列表 - ${shoppingListId}</value>
     </property>
+    <property key="OrderUnableToAddOISGToOrder">
+        <value xml:lang="en">Unable to add ship group to order: </value>
+        <value xml:lang="fr">Impossible d'ajouter le groupe d'expédition : </value>
+    </property>
     <property key="OrderUnableToCancelItemInventoryReservation">
         <value xml:lang="de">Fehler beim Stornieren einer Bestandsreservierung</value>
         <value xml:lang="en">Unable to cancel item inventory reservation</value>
@@ -4477,6 +4517,10 @@
         <value xml:lang="zh">不能更新明细的评论</value>
         <value xml:lang="zh_TW">不能更新明細的評論</value>
     </property>
+    <property key="OrderUnableToUpdateOrderItemFromOISG">
+        <value xml:lang="en">Unable to update order item from the ship group: </value>
+        <value xml:lang="fr">Impossible de mettre à jour la ligne de commande depuis le groupe d'expédition : </value>
+    </property>
     <property key="OrderUnableToUpdateInventoryReservations">
         <value xml:lang="de">Bestandsreservierungen können nicht aktualisiert werden: ${itemMsgInfo}</value>
         <value xml:lang="en">Unable to update inventory reservations : ${itemMsgInfo}</value>

Modified: ofbiz/trunk/applications/order/config/OrderUiLabels.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/order/config/OrderUiLabels.xml?rev=1643077&r1=1643076&r2=1643077&view=diff
==============================================================================
--- ofbiz/trunk/applications/order/config/OrderUiLabels.xml (original)
+++ ofbiz/trunk/applications/order/config/OrderUiLabels.xml Wed Dec  3 10:15:20 2014
@@ -13230,6 +13230,26 @@
         <value xml:lang="zh">请稍候,正在处理订单...</value>
         <value xml:lang="zh_TW">請稍候,正在處理訂單...</value>
     </property>
+    <property key="DeleteOrderItemShipGroup">
+        <value xml:lang="en">Delete this ship group</value>
+        <value xml:lang="fr">Supprimer le groupe d'expédition</value>
+    </property>
+    <property key="OrderAddToshipGroup">
+        <value xml:lang="en">Add to ship group</value>
+        <value xml:lang="fr">Ajouter au groupe d'expédition</value>
+    </property>
+    <property key="OrderItemId">
+        <value xml:lang="en">Ref</value>
+        <value xml:lang="fr">N° ligne</value>
+    </property>
+    <property key="OrderShipmentInformationByOISG">
+        <value xml:lang="en">View by ship group</value>
+        <value xml:lang="fr">Voir par groupe d'expédition</value>
+    </property>
+    <property key="OrderShipmentInformationByOrderItem">
+        <value xml:lang="en">View ship group by order item</value>
+        <value xml:lang="fr">Voir les groupes d'expédition par ligne de commandes</value>
+    </property>
     <property key="OrderSupplierData">
         <value xml:lang="de">Daten Lieferant</value>
         <value xml:lang="en">Supplier Data</value>

Modified: ofbiz/trunk/applications/order/script/org/ofbiz/order/order/OrderServices.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/order/script/org/ofbiz/order/order/OrderServices.xml?rev=1643077&r1=1643076&r2=1643077&view=diff
==============================================================================
--- ofbiz/trunk/applications/order/script/org/ofbiz/order/order/OrderServices.xml (original)
+++ ofbiz/trunk/applications/order/script/org/ofbiz/order/order/OrderServices.xml Wed Dec  3 10:15:20 2014
@@ -1459,4 +1459,49 @@ under the License.
             <call-service service-name="createPaymentApplication" in-map-name="createCtx"/>
         </if-not-empty>
     </simple-method>
+    
+    <simple-method method-name="MoveItemBetweenShipGroups" short-description="Move order items between ship groups">
+        <entity-one entity-name="OrderItemShipGroupAssoc" value-field="orderItemShipGroupAssoc">
+            <field-map field-name="orderId" from-field="parameters.orderId"/>
+            <field-map field-name="orderItemSeqId" from-field="parameters.orderItemSeqId"/>
+            <field-map field-name="shipGroupSeqId" from-field="parameters.toGroupIndex"/>
+        </entity-one>
+        <if-empty field="orderItemShipGroupAssoc">
+            <set-service-fields service-name="addOrderItemShipGroupAssoc" map="parameters" to-map="map"/>
+            <set field="map.quantity" value="0" type="BigDecimal"/>
+            <set field="map.shipGroupSeqId" from="parameters.toGroupIndex"/>
+            <call-service service-name="addOrderItemShipGroupAssoc" in-map-name="map"/>
+            <entity-one entity-name="OrderItemShipGroupAssoc" value-field="orderItemShipGroupAssoc">
+                <field-map field-name="orderId" from-field="parameters.orderId"/>
+                <field-map field-name="orderItemSeqId" from-field="parameters.orderItemSeqId"/>
+                <field-map field-name="shipGroupSeqId" from-field="parameters.toGroupIndex"/>
+            </entity-one>
+        </if-empty>
+        <clear-field field="map"/>
+        <set field="map.orderId" from-field="parameters.orderId"/>
+        <set field="map.orderItemSeqId" from-field="parameters.orderItemSeqId"/>
+        <set field="map.shipGroupSeqId" from-field="parameters.toGroupIndex"/>
+        <set field="map.quantity" value="${orderItemShipGroupAssoc.quantity + parameters.quantity}" type="BigDecimal"/>
+        <call-service service-name="updateOrderItemShipGroupAssoc" in-map-name="map"/>
+        
+        <entity-one entity-name="OrderItemShipGroupAssoc" value-field="orderItemShipGroupAssoc">
+            <field-map field-name="orderId" from-field="parameters.orderId"/>
+            <field-map field-name="orderItemSeqId" from-field="parameters.orderItemSeqId"/>
+            <field-map field-name="shipGroupSeqId" from-field="parameters.fromGroupIndex"/>
+        </entity-one>
+        <if-empty field="orderItemShipGroupAssoc">
+            <add-error>
+                <fail-message message="The orderItemShipGroupAssoc qualified by orderId=${parameters.orderId} orderItemSeqId=${parameters.orderItemSeqId} shipGroupSeqId=${parameters.fromGroupIndex} does not exist"/>
+            </add-error>
+        </if-empty>
+        <check-errors/>
+        
+        <clear-field field="map"/>
+        <set field="map.orderId" from-field="parameters.orderId"/>
+        <set field="map.orderItemSeqId" from-field="parameters.orderItemSeqId"/>
+        <set field="map.shipGroupSeqId" from-field="parameters.fromGroupIndex"/>
+        <set field="map.quantity" value="${orderItemShipGroupAssoc.quantity - parameters.quantity}" type="BigDecimal"/>
+        <call-service service-name="updateOrderItemShipGroupAssoc" in-map-name="map"/>
+    </simple-method>
+    
 </simple-methods>

Modified: ofbiz/trunk/applications/order/script/org/ofbiz/order/test/ShoppingCartTests.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/order/script/org/ofbiz/order/test/ShoppingCartTests.xml?rev=1643077&r1=1643076&r2=1643077&view=diff
==============================================================================
--- ofbiz/trunk/applications/order/script/org/ofbiz/order/test/ShoppingCartTests.xml (original)
+++ ofbiz/trunk/applications/order/script/org/ofbiz/order/test/ShoppingCartTests.xml Wed Dec  3 10:15:20 2014
@@ -25,7 +25,24 @@ under the License.
         <entity-one entity-name="UserLogin" value-field="userLogin">
             <field-map field-name="userLoginId" value="system"/>
         </entity-one>
+        
+        <set field="map.userLogin" from="userLogin"/>
+        
+        <call-service service-name="testCreateShoppinCartAndOrder" in-map-name="map">
+            <result-to-field result-name="orderMap"/>
+        </call-service>
+        
+        <if-not-empty field="orderMap">
+            <log level="info" message="------------ ORDERID : [${orderMap.orderId}] ------------"/>
+        </if-not-empty>
+
+        <assert><not><if-empty field="orderMap.orderId"/></not></assert>
+        <check-errors/>
+    </simple-method>
 
+    <simple-method method-name="testCreateShoppinCartAndOrder" short-description="Create an order using a shopping cart">
+        <set field="userLogin" from="parameters.userLogin"/>
+        
         <!-- Shopping Cart new Instance -->
         <set field="delegator" from-field="parameters.delegator" type="Object"/>
         <set field="dispatcher" from-field="parameters.dispatcher" type="Object"/>
@@ -80,7 +97,7 @@ under the License.
         <set field="prodCatalogId" value="DemoCatalog" type="String"/>
         <set field="index" value="0" type="Integer"/>
         <set field="productId" value="GZ-2644" type="String"/>
-        <set field="quantity" value="1" type="BigDecimal"/>
+        <set field="quantity" value="5" type="BigDecimal"/>
         <set field="selectedAmount" value="0" type="BigDecimal"/>
         <set field="unitPrice" value="38.4" type="BigDecimal"/>
         <set field="itemType" value="PRODUCT_ORDER_ITEM" type="String"/>
@@ -245,20 +262,15 @@ under the License.
         <!-- Shopping Cart checkout and create order -->
         <call-bsh><![CDATA[
             checkOutHelper = new org.ofbiz.order.shoppingcart.CheckOutHelper(dispatcher, delegator, shoppingCart);
-            java.util.Map orderCreate = checkOutHelper.createOrder(userLogin);
-            parameters.put("orderMap", orderCreate);
-        ]]></call-bsh>
-
+            java.util.Map orderMap = checkOutHelper.createOrder(userLogin);
+            parameters.put("orderMap", orderMap);]]>
+        </call-bsh>
+        
         <!-- Clear Shopping Cart -->
         <call-object-method method-name="clear" obj-field="shoppingCart"/>
-
-        <set field="orderMap" from-field="parameters.orderMap"/>
-        <if-not-empty field="orderMap">
-            <log level="info" message="------------ ORDERID : [${orderMap.orderId}] ------------"/>
-        </if-not-empty>
-
-        <assert><not><if-empty field="orderMap.orderId"/></not></assert>
-        <check-errors/>
+        
+        <field-to-result field="parameters.orderMap" result-name="orderMap"/>
+        
     </simple-method>
 
     <simple-method method-name="testCreateOrderRentalProduct" short-description="Test create order rental of product" login-required="false">
@@ -671,5 +683,76 @@ under the License.
         <assert><not><if-empty field="orderId"/></not></assert>
         <check-errors/>
     </simple-method>
+    
+    
+    <simple-method method-name="testOrderMoveItemBetweenShipGoups" login-required="false"
+        short-description="Create an order with 2 ship groups and 3 items and move items between ship groups">
+        <entity-one entity-name="UserLogin" value-field="userLogin">
+            <field-map field-name="userLoginId" value="system"/>
+        </entity-one>
+                
+        <set field="map.userLogin" from="userLogin"/>
+        <call-service service-name="testCreateShoppinCartAndOrder" in-map-name="map">
+            <result-to-field result-name="orderMap"/>
+        </call-service>
+
+        <assert><not><if-empty field="orderMap.orderId"/></not></assert>
+        <check-errors/>
+        
+        <entity-and entity-name="OrderItem" list="orderItems">
+            <field-map field-name="orderId" from-field="orderMap.orderId"/>
+            <field-map field-name="productId" value="GZ-2644"/>
+        </entity-and>
+        <first-from-list entry="orderItem" list="orderItems"/>
+        
+        <set field="map.orderId" from="orderMap.orderId"/>
+        <set field="map.contactMechId" value="9015"/>
+        <set field="map.carrierPartyId" value="UPS"/>
+        <set field="map.shipmentMethodTypeId" value="NEXT_DAY"/>
+        <call-service service-name="createOrderItemShipGroup" in-map-name="map"/>
+        
+        <clear-field field="map"/>
+        <set field="map.userLogin" from="userLogin" type="GenericValue"/>
+        <set field="map.orderId" from="orderMap.orderId"/>
+        <set field="map.orderItemSeqId" from="orderItem.orderItemSeqId"/>
+        <set field="map.fromGroupIndex" value="00001"/>
+        <set field="map.toGroupIndex" value="00002"/>
+        <set field="map.quantity" value="2" type="BigDecimal"/>
+        <call-service service-name="MoveItemBetweenShipGroups" in-map-name="map"/>
+        
+        <entity-one entity-name="OrderItemShipGroupAssoc" value-field="orderItemShipGroupAssoc1">
+            <field-map field-name="orderId" from-field="orderMap.orderId"/>
+            <field-map field-name="orderItemSeqId" from-field="orderItem.orderItemSeqId"/>
+            <field-map field-name="shipGroupSeqId" value="00001"/>
+        </entity-one>
+        <assert><if-compare field="orderItemShipGroupAssoc1.quantity" operator="equals" value="3"></if-compare></assert>
+        <check-errors/>
 
+        <entity-one entity-name="OrderItemShipGroupAssoc" value-field="orderItemShipGroupAssoc2">
+            <field-map field-name="orderId" from-field="orderMap.orderId"/>
+            <field-map field-name="orderItemSeqId" from-field="orderItem.orderItemSeqId"/>
+            <field-map field-name="shipGroupSeqId" value="00002"/>
+        </entity-one>
+        <assert><if-compare field="orderItemShipGroupAssoc2.quantity" operator="equals" value="2"></if-compare></assert>
+        <check-errors/>
+        
+        <clear-field field="map"/>
+        <set field="map.userLogin" from="userLogin" type="GenericValue"/>
+        <set field="map.orderId" from="orderMap.orderId"/>
+        <set field="map.orderItemSeqId" from="orderItem.orderItemSeqId"/>
+        <set field="map.shipGroupSeqId" value="00002"/>
+        <call-service service-name="deleteOrderItemShipGroupAssoc" in-map-name="map"/>
+        
+        <clear-field field="map"/>
+        <set field="map.userLogin" from="userLogin" type="GenericValue"/>
+        <set field="map.orderId" from="orderMap.orderId"/>
+        <set field="map.shipGroupSeqId" value="00002"/>
+        <call-service service-name="deleteOrderItemShipGroup" in-map-name="map"/>
+        <entity-count entity-name="OrderItemShipGroup" count-field="orderItemShipGroupCount">
+            <condition-expr field-name="orderId" from-field="orderMap.orderId"/>
+        </entity-count>
+        <assert><if-compare field="orderItemShipGroupCount" operator="equals" value="1"></if-compare></assert>
+        <check-errors/>
+    </simple-method>
+    
 </simple-methods>

Modified: ofbiz/trunk/applications/order/servicedef/services.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/order/servicedef/services.xml?rev=1643077&r1=1643076&r2=1643077&view=diff
==============================================================================
--- ofbiz/trunk/applications/order/servicedef/services.xml (original)
+++ ofbiz/trunk/applications/order/servicedef/services.xml Wed Dec  3 10:15:20 2014
@@ -1069,6 +1069,42 @@ under the License.
         <attribute name="availabalityList" type="List" mode="OUT"/>
     </service>
     
+    <service name="addOrderItemShipGroup" engine="java" auth="true" default-entity-name="OrderItemShipGroup"
+             location="org.ofbiz.order.order.OrderServices" invoke="addOrderItemShipGroup">
+        <description>Creates a new OrderItemShipGroup with maySplit and isGift filled.</description>
+        <auto-attributes include="pk" mode="IN" optional="false"/>
+        <auto-attributes include="nonpk" mode="IN" optional="true"/>
+        <override name="shipGroupSeqId" mode="INOUT" optional="true"/>
+    </service>
+    <service name="deleteOrderItemShipGroup" engine="java" auth="true" default-entity-name="OrderItemShipGroup"
+             location="org.ofbiz.order.order.OrderServices"
+             invoke="deleteOrderItemShipGroup">
+        <description>delete Order Item Ship Group </description>
+        <auto-attributes include="pk" mode="IN" optional="false"/>
+    </service>
+
+    <service name="addOrderItemShipGroupAssoc" engine="java" auth="true" default-entity-name="OrderItemShipGroupAssoc"
+             location="org.ofbiz.order.order.OrderServices"
+             invoke="addOrderItemShipGroupAssoc">
+        <description>add Order Item Ship Group Assoc and if order item ship group not exit, create it before</description>
+        <implements service="addOrderItemShipGroup" optional="true"/>
+        <auto-attributes include="pk" mode="IN" optional="false"/>
+        <auto-attributes include="nonpk" mode="IN" optional="true"/>
+    </service>
+    <service name="updateOrderItemShipGroupAssoc" engine="java" auth="true" default-entity-name="OrderItemShipGroupAssoc"
+             location="org.ofbiz.order.order.OrderServices"
+             invoke="updateOrderItemShipGroupAssoc">
+        <description>update OrderItem from OISG, totalQuantity is used only if controller is a multi services </description>
+        <auto-attributes include="all" mode="IN" optional="true"/>
+        <attribute name="totalQuantity" type="BigDecimal"  mode="INOUT"  optional="true"/>
+        <attribute name="rowCount" type="Integer" mode="IN" optional="true"/>
+        <attribute name="rowNumber" type="Integer" mode="INOUT" optional="true"/>
+    </service>
+    <service name="deleteOrderItemShipGroupAssoc" engine="entity-auto" auth="true" default-entity-name="OrderItemShipGroupAssoc" invoke="delete">
+        <description>delete Order Item Ship Group Assoc</description>
+        <auto-attributes include="pk" mode="IN" optional="false"/>
+    </service>
+
     <service name="createQuoteTerm" engine="simple" default-entity-name="QuoteTerm"
         location="component://order/script/org/ofbiz/order/quote/QuoteServices.xml" invoke="createQuoteTerm">
         <description>
@@ -1102,8 +1138,14 @@ under the License.
     </service>
     
     <service name="createTestOrderRentalProduct" engine="simple" auth="true"
-         location="component://order/script/org/ofbiz/order/test/ShoppingCartTests.xml" invoke="testCreateOrderRentalProduct">
-         <description>Create Test Order Rental of an asset which is shipped from and returned to inventory</description>
+        location="component://order/script/org/ofbiz/order/test/ShoppingCartTests.xml" invoke="testCreateOrderRentalProduct">
+        <description>Create Test Order Rental of an asset which is shipped from and returned to inventory</description>
+    </service>
+    
+    <service name="testCreateShoppinCartAndOrder" engine="simple" auth="true"
+        location="component://order/script/org/ofbiz/order/test/ShoppingCartTests.xml" invoke="testCreateShoppinCartAndOrder">
+        <description>Create an order using a shopping cart - only used internally in ShoppingCartTests.xml for test purpose</description>
+        <attribute name="orderMap" type="Map" mode="OUT"/>
     </service>
     
     <!-- Order Item Attribute -->
@@ -1135,4 +1177,14 @@ under the License.
         <attribute name="quantity" type="BigDecimal" mode="IN" optional="false"/>
     </service>
     
+    <service name="MoveItemBetweenShipGroups" engine="simple"
+        location="component://order/script/org/ofbiz/order/order/OrderServices.xml" invoke="MoveItemBetweenShipGroups">
+        <description>Move order items between ship groups</description>
+        <attribute name="orderId" type="String" mode="IN"/>
+        <attribute name="orderItemSeqId" type="String" mode="IN"/>
+        <attribute name="fromGroupIndex" type="String" mode="IN"/>
+        <attribute name="toGroupIndex" type="String" mode="IN"/>
+        <attribute name="quantity" type="BigDecimal" mode="IN"/>
+    </service>
+    
 </services>

Modified: ofbiz/trunk/applications/order/src/org/ofbiz/order/order/OrderReadHelper.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/order/src/org/ofbiz/order/order/OrderReadHelper.java?rev=1643077&r1=1643076&r2=1643077&view=diff
==============================================================================
--- ofbiz/trunk/applications/order/src/org/ofbiz/order/order/OrderReadHelper.java (original)
+++ ofbiz/trunk/applications/order/src/org/ofbiz/order/order/OrderReadHelper.java Wed Dec  3 10:15:20 2014
@@ -1391,15 +1391,12 @@ public class OrderReadHelper {
         try {
             orderDeliverySchedule = EntityQuery.use(delegator).from("OrderDeliverySchedule").where("orderId", orderId, "orderItemSeqId", "_NA_").queryOne();
         } catch (GenericEntityException e) {
+            if (Debug.infoOn()) Debug.logInfo(" OrderDeliverySchedule not found for order " + orderId, module);
+            return false;
         }
-        Timestamp estimatedShipDate = null;
-        if (orderDeliverySchedule != null && orderDeliverySchedule.get("estimatedReadyDate") != null) {
-            estimatedShipDate = orderDeliverySchedule.getTimestamp("estimatedReadyDate");
-        }
-        if (estimatedShipDate != null && UtilDateTime.nowTimestamp().after(estimatedShipDate)) {
-            return true;
-        }
-        return false;
+        if (orderDeliverySchedule == null) return false;
+        Timestamp estimatedShipDate = orderDeliverySchedule.getTimestamp("estimatedReadyDate");
+        return estimatedShipDate != null && UtilDateTime.nowTimestamp().after(estimatedShipDate);
     }
 
     public boolean getRejectedOrderItems() {
@@ -1985,6 +1982,38 @@ public class OrderReadHelper {
                 BigDecimal issueQty = issue.getBigDecimal("quantity");
                 BigDecimal cancelQty = issue.getBigDecimal("cancelQuantity");
                 if (cancelQty == null) {
+                    cancelQty = ZERO;
+                }
+                if (issueQty == null) {
+                    issueQty = ZERO;
+                }
+                quantityShipped = quantityShipped.add(issueQty.subtract(cancelQty)).setScale(scale, rounding);
+            }
+        }
+        return quantityShipped.setScale(scale, rounding);
+    }
+
+    public BigDecimal getItemShipGroupAssocShippedQuantity(GenericValue orderItem, String shipGroupSeqId) {
+        BigDecimal quantityShipped = ZERO;
+
+        if (orderItem == null) return null;
+        if (this.orderItemIssuances == null) {
+            Delegator delegator = orderItem.getDelegator();
+            try {
+                orderItemIssuances = EntityQuery.use(delegator).from("ItemIssuance").where("orderId", orderItem.get("orderId"), "shipGroupSeqId", shipGroupSeqId).queryList();                
+            } catch (GenericEntityException e) {
+                Debug.logWarning(e, "Trouble getting ItemIssuance(s)", module);
+            }
+        }
+
+        // filter the issuance
+        Map<String, Object> filter = UtilMisc.toMap("orderItemSeqId", orderItem.get("orderItemSeqId"), "shipGroupSeqId", shipGroupSeqId);
+        List<GenericValue> issuances = EntityUtil.filterByAnd(orderItemIssuances, filter);
+        if (UtilValidate.isNotEmpty(issuances)) {
+            for (GenericValue issue : issuances) {
+                BigDecimal issueQty = issue.getBigDecimal("quantity");
+                BigDecimal cancelQty = issue.getBigDecimal("cancelQuantity");
+                if (cancelQty == null) {
                     cancelQty = ZERO;
                 }
                 if (issueQty == null) {

Modified: ofbiz/trunk/applications/order/src/org/ofbiz/order/order/OrderServices.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/order/src/org/ofbiz/order/order/OrderServices.java?rev=1643077&r1=1643076&r2=1643077&view=diff
==============================================================================
--- ofbiz/trunk/applications/order/src/org/ofbiz/order/order/OrderServices.java (original)
+++ ofbiz/trunk/applications/order/src/org/ofbiz/order/order/OrderServices.java Wed Dec  3 10:15:20 2014
@@ -44,7 +44,6 @@ import org.ofbiz.base.util.GeneralExcept
 import org.ofbiz.base.util.GeneralRuntimeException;
 import org.ofbiz.base.util.ObjectType;
 import org.ofbiz.base.util.UtilDateTime;
-import org.ofbiz.base.util.UtilFormatOut;
 import org.ofbiz.base.util.UtilGenerics;
 import org.ofbiz.base.util.UtilMisc;
 import org.ofbiz.base.util.UtilNumber;
@@ -96,6 +95,7 @@ public class OrderServices {
     public static final String resource = "OrderUiLabels";
     public static final String resource_error = "OrderErrorUiLabels";
     public static final String resourceProduct = "ProductUiLabels";
+    public static final String resourceCommon = "CommonUiLabels";
 
     public static Map<String, String> salesAttributeRoleMap = FastMap.newInstance();
     public static Map<String, String> purchaseAttributeRoleMap = FastMap.newInstance();
@@ -572,7 +572,7 @@ public class OrderServices {
         // before processing orderItems process orderItemGroups so that they'll be in place for the foreign keys and what not
         List<GenericValue> orderItemGroups = UtilGenerics.checkList(context.get("orderItemGroups"));
         if (UtilValidate.isNotEmpty(orderItemGroups)) {
-            for (GenericValue orderItemGroup : orderItemGroups){
+            for (GenericValue orderItemGroup : orderItemGroups) {
                 orderItemGroup.set("orderId", orderId);
                 toBeStored.add(orderItemGroup);
             }
@@ -1196,7 +1196,7 @@ public class OrderServices {
             countProductQuantityOrdered(ctx, context);
         }
         return ServiceUtil.returnSuccess();
-    }            
+    }
 
     public static void reserveInventory(Delegator delegator, LocalDispatcher dispatcher, GenericValue userLogin, Locale locale, List<GenericValue> orderItemShipGroupInfo, List<String> dropShipGroupIds, Map<String, GenericValue> itemValuesBySeqId, String orderTypeId, String productStoreId, List<String> resErrorMessages) throws GeneralException {
         boolean isImmediatelyFulfilled = false;
@@ -2128,6 +2128,17 @@ public class OrderServices {
                                 "OrderUnableToSetCancelQuantity", UtilMisc.toMap("itemMsgInfo",itemMsgInfo), locale));
                     }
 
+                    Map<String, Object> localCtx = UtilMisc.toMap("userLogin", userLogin,
+                            "orderId", orderItem.getString("orderId"),
+                            "orderItemSeqId", orderItem.getString("orderItemSeqId"),
+                            "shipGroupSeqId", orderItemShipGroupAssoc.getString("shipGroupSeqId"));
+                    try {
+                        dispatcher.runSync("deleteOrderItemShipGroupAssoc", localCtx);
+                    } catch (GenericServiceException e) {
+                        Debug.logError(e, module);
+                        return ServiceUtil.returnError(e.getMessage());
+                    }
+
                     //  create order item change record
                     if (!"Y".equals(orderItem.getString("isPromo"))) {
                         String reasonEnumId = null;
@@ -3577,6 +3588,8 @@ public class OrderServices {
                     "OrderShoppingCartEmpty", locale));
         }
 
+        shipGroupIdx = cart.getShipInfoIndex(shipGroupSeqId);
+
         // add in the new product
         try {
             if ("PURCHASE_ORDER".equals(cart.getOrderType())) {
@@ -3611,6 +3624,9 @@ public class OrderServices {
 
                 // set the item in the selected ship group
                 item.setDesiredDeliveryDate(itemDesiredDeliveryDate);
+                shipGroupIdx = cart.getShipInfoIndex(shipGroupSeqId);
+                int itemId = cart.getItemIndex(item);
+                cart.positionItemToGroup(itemId, quantity, cart.getItemShipGroupIndex(itemId), shipGroupIdx, false);
                 cart.clearItemShipInfo(item);
                 cart.setItemShipGroupQty(item, item.getQuantity(), shipGroupIdx);
             }
@@ -3845,10 +3861,13 @@ public class OrderServices {
             // set the group qty
             ShoppingCartItem cartItem = cart.findCartItem(itemInfo[0]);
             if (cartItem != null) {
-            Debug.logInfo("Shipping info (before) for group #" + (groupIdx-1) + " [" + cart.getShipmentMethodTypeId(groupIdx-1) + " / " + cart.getCarrierPartyId(groupIdx-1) + "]", module);
-            cart.setItemShipGroupQty(cartItem, groupQty, groupIdx - 1);
-            Debug.logInfo("Set ship group qty: [" + itemInfo[0] + " / " + itemInfo[1] + " (" + (groupIdx-1) + ")] " + groupQty, module);
-            Debug.logInfo("Shipping info (after) for group #" + (groupIdx-1) + " [" + cart.getShipmentMethodTypeId(groupIdx-1) + " / " + cart.getCarrierPartyId(groupIdx-1) + "]", module);
+                int shipGroupIndex = cart.getShipInfoIndex(itemInfo[1]);
+                if (Debug.infoOn()) Debug.logInfo("Shipping info (before) for group #" + (shipGroupIndex) + " [" + cart.getShipmentMethodTypeId(shipGroupIndex) + " / " + cart.getCarrierPartyId(shipGroupIndex) + "]", module);
+                cart.setItemShipGroupQty(cartItem, groupQty, shipGroupIndex);
+                if (Debug.infoOn()) {
+                    Debug.logInfo("Set ship group qty: [" + itemInfo[0] + " / " + itemInfo[1] + " (" + (shipGroupIndex) + ")] " + groupQty, module);
+                    Debug.logInfo("Shipping info (after) for group #" + (shipGroupIndex) + " [" + cart.getShipmentMethodTypeId(shipGroupIndex) + " / " + cart.getCarrierPartyId(shipGroupIndex) + "]", module);
+                }
             }
         }
 
@@ -4049,7 +4068,33 @@ public class OrderServices {
             exprs.add(EntityCondition.makeCondition("orderAdjustmentTypeId", EntityOperator.EQUALS, "VAT_PRICE_CORRECT"));
             adjExprs.add(EntityCondition.makeCondition(exprs, EntityOperator.OR));
             EntityCondition cond = EntityCondition.makeCondition(adjExprs, EntityOperator.AND);
-            delegator.removeByCondition("OrderAdjustment", cond);
+            List<GenericValue> orderAdjustmentsToDelete = EntityQuery.use(delegator).from("OrderAdjustment").where(cond).queryList();
+            List<GenericValue> orderAdjustmentsToStore = new LinkedList<GenericValue>();
+            List<GenericValue> orderAdjustmentsToRemove = new LinkedList<GenericValue>();
+            if (UtilValidate.isNotEmpty(orderAdjustmentsToDelete)) {
+                for (GenericValue orderAdjustment : orderAdjustmentsToDelete) {
+                    //check if the adjustment has a related entry in entity OrderAdjustmentBilling
+                    List<GenericValue> oaBilling = orderAdjustment.getRelated("OrderAdjustmentBilling", null, null, false);
+                    if (UtilValidate.isNotEmpty(oaBilling)) {
+                        orderAdjustmentsToRemove.add(orderAdjustment);
+                        if ("SALES_TAX".equals(orderAdjustment.get("orderAdjustmentTypeId"))) {
+                            //if the orderAdjustment is  a sale tax, set the amount to 0 to avoid amount addition
+                            orderAdjustmentsToStore.add(orderAdjustment);
+                        }
+                    }
+                }
+            }
+            //then remove order Adjustment of the list
+            if (UtilValidate.isNotEmpty(orderAdjustmentsToDelete)) {
+                orderAdjustmentsToDelete.removeAll(orderAdjustmentsToRemove);
+                delegator.removeAll(orderAdjustmentsToDelete);
+            }
+            if (UtilValidate.isNotEmpty(orderAdjustmentsToStore)) {
+                for (GenericValue orderAdjustment : orderAdjustmentsToStore) {
+                    orderAdjustment.set("amount", BigDecimal.ZERO);
+                }
+                delegator.storeAll(orderAdjustmentsToStore);
+            }
         } catch (GenericEntityException e) {
             Debug.logError(e, module);
             throw new GeneralException(e.getMessage());
@@ -4152,18 +4197,15 @@ public class OrderServices {
         toStore.addAll(cart.makeOrderItems());
         toStore.addAll(cart.makeAllAdjustments());
 
-        String shipGroupSeqId = null;
         long groupIndex = cart.getShipInfoSize();
         if (!deleteItems) {
             for (long itr = 1; itr <= groupIndex; itr++) {
-                shipGroupSeqId = UtilFormatOut.formatPaddedNumber(itr, 5);
                 List<GenericValue> removeList = new ArrayList<GenericValue>();
                 for (GenericValue stored: toStore) {
                     if ("OrderAdjustment".equals(stored.getEntityName())) {
                         if (("SHIPPING_CHARGES".equals(stored.get("orderAdjustmentTypeId")) ||
                                "SALES_TAX".equals(stored.get("orderAdjustmentTypeId"))) &&
-                                stored.get("orderId").equals(orderId) &&
-                                stored.get("shipGroupSeqId").equals(shipGroupSeqId)) {
+                                stored.get("orderId").equals(orderId)) {
                             // Removing objects from toStore list for old Shipping and Handling Charges Adjustment and Sales Tax Adjustment.
                             removeList.add(stored);
                         }
@@ -5637,6 +5679,442 @@ public class OrderServices {
                 "OrderRunSubscriptionAutoReorders", UtilMisc.toMap("count", count), locale));
     }
 
+    /**
+     * Create an OrderItemShipGroup record
+     * @param ctx
+     * @param context
+     * @return
+     * @throws GenericEntityException
+     */
+    public static Map<String, Object> addOrderItemShipGroup(DispatchContext dctx, Map<String, Object> context) {
+        Delegator delegator = dctx.getDelegator();
+        Locale locale = (Locale) context.get("locale" );
+        Map<String, Object> result = ServiceUtil.returnSuccess();
+        String orderId = (String) context.get("orderId");
+
+        //main message error
+        String mainErrorMessage = UtilProperties.getMessage(resource_error, "OrderUnableToAddOISGToOrder", locale);
+        Map<String, Object> createOrderItemShipGroupMap = null;
+        try {
+            createOrderItemShipGroupMap = dctx.makeValidContext("createOrderItemShipGroup", "IN", context);
+        } catch (GenericServiceException gse) {
+            String errMsg = mainErrorMessage + gse.toString();
+            return ServiceUtil.returnError(errMsg);
+        }
+
+        try {
+            //test if party is a valid carrier
+            String carrierPartyId = (String) context.get("carrierPartyId");
+            GenericValue carrierRole = EntityQuery.use(delegator).from("PartyRole").where("partyId", carrierPartyId, "roleTypeId", "CARRIER").cache().queryOne();
+            if (UtilValidate.isNotEmpty(carrierPartyId) && UtilValidate.isEmpty(carrierRole)) {
+                String errMsg = mainErrorMessage + UtilProperties.getMessage(resource_error, "OrderCartShipGroupPartyCarrierNotFound", UtilMisc.toMap("partyId", carrierPartyId), locale);
+                return ServiceUtil.returnError(errMsg);
+            }
+
+            //test if shipmentMethodTypeId is available for carrier party
+            String shipmentMethodTypeId = (String) context.get("shipmentMethodTypeId");
+            if (UtilValidate.isNotEmpty(shipmentMethodTypeId)) {
+                // carrierPartyId is not in shipmentMethodTypeId
+                if (shipmentMethodTypeId.indexOf("_o_" ) == -1) {
+                    GenericValue shipmentMethod = EntityQuery.use(delegator).from("CarrierShipmentMethod").where("partyId", carrierPartyId, "roleTypeId", "CARRIER", "shipmentMethodTypeId", shipmentMethodTypeId).cache().queryOne();
+                    if (UtilValidate.isEmpty(shipmentMethod)) {
+                        String errMsg = mainErrorMessage + UtilProperties.getMessage(resource_error, "OrderCartShipGroupShipmentMethodNotFound", UtilMisc.toMap("shipmentMethodTypeId", shipmentMethodTypeId), locale);
+                        return ServiceUtil.returnError(errMsg);
+                    }
+                } else {
+                    // carrierPartyId is in shipmentMethodTypeId
+                    String[] carrierShipmentMethod = shipmentMethodTypeId.split("_o_");
+                    if (carrierShipmentMethod.length == 2) {
+                        shipmentMethodTypeId = carrierShipmentMethod[0];
+                        carrierPartyId = carrierShipmentMethod[1];
+                    }
+                    context.put("carrierPartyId", carrierPartyId);
+                    context.put("shipmentMethodTypeId", shipmentMethodTypeId);
+                }
+            }
+
+            List<GenericValue> oisgs = EntityQuery.use(delegator).from("OrderItemShipGroup").where("orderId", orderId).orderBy("shipGroupSeqId DESC").queryList();
+            if (UtilValidate.isNotEmpty(oisgs)) {
+                GenericValue oisg = EntityUtil.getFirst(oisgs);
+                // set shipmentMethodTypeId, carrierPartyId, carrierRoleTypeId, contactMechId when shipmentMethodTypeId and carrierPartyId are empty
+                if (UtilValidate.isEmpty(carrierPartyId) && UtilValidate.isEmpty(shipmentMethodTypeId)) {
+                    createOrderItemShipGroupMap.put("shipmentMethodTypeId", oisg.get("shipmentMethodTypeId"));
+                    createOrderItemShipGroupMap.put("carrierPartyId", oisg.get("carrierPartyId"));
+                    createOrderItemShipGroupMap.put("carrierRoleTypeId", oisg.get("carrierRoleTypeId"));
+                    createOrderItemShipGroupMap.put("contactMechId", oisg.get("contactMechId"));
+                }
+            }
+        } catch (GenericEntityException gee) {
+            String errMsg = mainErrorMessage + gee.toString();
+            return ServiceUtil.returnError(errMsg);
+        }
+
+        // set maySplit and isGift for the new oisg to No if they are not present
+        if (UtilValidate.isEmpty(createOrderItemShipGroupMap.get("maySplit"))) {
+            createOrderItemShipGroupMap.put("maySplit", "N");
+        }
+        if (UtilValidate.isEmpty(createOrderItemShipGroupMap.get("isGift"))) {
+            createOrderItemShipGroupMap.put("isGift", "N");
+        }
+
+        //create new oisg
+        try {
+            result = dctx.getDispatcher().runSync("createOrderItemShipGroup", createOrderItemShipGroupMap);
+        } catch (GenericServiceException gse) {
+            String errMsg = mainErrorMessage + gse.toString();
+            return ServiceUtil.returnError(errMsg);
+        }
+
+        if (ServiceUtil.isError(result)) {
+            String errMsg = UtilProperties.getMessage(resource, mainErrorMessage + result.get("errorMessage"), locale);
+            return ServiceUtil.returnError(errMsg);
+        }
+        return result;
+    }
+
+    /**
+     * Remove an OrderItemShipGroup record
+     * @param ctx
+     * @param context: a map containing in paramaters
+     * @return result: a map containing out parameters
+     * @throws GenericEntityException
+     */
+    public static Map deleteOrderItemShipGroup(DispatchContext ctx, Map context) throws GenericEntityException {
+        Delegator delegator = ctx.getDelegator();
+        Map<String, Object> result = new HashMap<String, Object>();
+        
+        GenericValue orderItemShipGroup = (GenericValue) context.get("orderItemShipGroup");
+        if (UtilValidate.isEmpty(orderItemShipGroup)) {
+            String orderId= (String) context.get("orderId");
+            GenericValue orderHeader = EntityQuery.use(delegator).from("OrderHeader").where("orderId", orderId).queryOne();
+            String shipGroupSeqId= (String) context.get("shipGroupSeqId");
+            if (UtilValidate.isNotEmpty(orderHeader) && UtilValidate.isNotEmpty(shipGroupSeqId)) {
+                orderItemShipGroup = EntityQuery.use(delegator).from("OrderItemShipGroup").where("orderId", orderId, "shipGroupSeqId", shipGroupSeqId).queryOne();
+                if (UtilValidate.isEmpty(orderItemShipGroup)) {
+                    return ServiceUtil.returnError("OrderItemShipGroup Does Not Exist");
+                }
+            }
+        }
+        if (UtilValidate.isNotEmpty(orderItemShipGroup)) {
+            orderItemShipGroup.remove();
+            result.put(ModelService.RESPONSE_MESSAGE, ModelService.RESPOND_SUCCESS);
+        }
+        return result;
+    }
+
+    /**
+     * Create orderItem and shipGroup association
+     * @param dctx
+     * @param context
+     * @return
+     * @throws GenericEntityException
+     */
+    public static Map addOrderItemShipGroupAssoc(DispatchContext dctx, Map<String, Object> context) throws GenericEntityException {
+        Delegator delegator = dctx.getDelegator();
+        LocalDispatcher dispatcher = dctx.getDispatcher();
+        Locale locale = (Locale) context.get("locale" );
+        String orderId = (String) context.get("orderId");
+        String orderItemSeqId = (String) context.get("orderItemSeqId");
+        String shipGroupSeqId = (String) context.get("shipGroupSeqId");
+        BigDecimal quantity = (BigDecimal) context.get("quantity");
+
+        //main message error
+        String mainErrorMessage = UtilProperties.getMessage(resource_error, "OrderUnableToAddItemToOISG", locale);
+        //test orderItem and check status
+        GenericValue orderItem = EntityQuery.use(delegator).from("OrderItem").where("orderId", orderId, "orderItemSeqId", orderItemSeqId).queryOne();
+        if (orderItem == null) {
+            String errMsg = mainErrorMessage + UtilProperties.getMessage(resource_error, "OrderErrorOrderItemNotFound", UtilMisc.toMap("orderId", orderId, "orderItemSeqId", orderItemSeqId), locale);
+            return ServiceUtil.returnError(errMsg);
+        }
+        String statusId = orderItem.getString("statusId");
+        // add OISG only if orderItem is not already prepared
+        if ("ITEM_CREATED".equals(statusId) || "ITEM_APPROVED".equals(statusId)) {
+            //find OISG
+            //by default create a new orderItemShipGroup if null with default carrier and contact from the first OISG
+            if ("new".equals(shipGroupSeqId)) {
+                try {
+                    Map<String, Object> addOrderItemShipGroupMap = dctx.makeValidContext("addOrderItemShipGroup", "IN", context);
+                    addOrderItemShipGroupMap.remove("shipGroupSeqId");
+                    //get default OrderItemShipGroup value for carrier and contact data  
+                    List<GenericValue> oisgas = orderItem.getRelated("OrderItemShipGroupAssoc", null, null, false);
+                    if (UtilValidate.isNotEmpty(oisgas)) {
+                        GenericValue oisga = EntityUtil.getFirst(oisgas);
+                        GenericValue oisg = oisga.getRelatedOne("OrderItemShipGroup", false);
+                        if (UtilValidate.isNotEmpty(oisg)) {
+                            addOrderItemShipGroupMap.put("shipmentMethodTypeId", oisg.get("shipmentMethodTypeId"));
+                            addOrderItemShipGroupMap.put("carrierPartyId", oisg.get("carrierPartyId"));
+                            addOrderItemShipGroupMap.put("carrierRoleTypeId", oisg.get("carrierRoleTypeId"));
+                            addOrderItemShipGroupMap.put("contactMechId", oisg.get("contactMechId"));
+                        }
+                    }
+                    //call  service to create new oisg
+                    Map<String, Object> result = null;
+                    result = dispatcher.runSync("addOrderItemShipGroup", addOrderItemShipGroupMap);
+                    if (result.containsKey("shipGroupSeqId")) {
+                        shipGroupSeqId = (String) result.get("shipGroupSeqId");
+                    }
+                } catch (GenericServiceException e) {
+                    String errMsg = UtilProperties.getMessage(resource, mainErrorMessage, locale);
+                    return ServiceUtil.returnError(errMsg);
+                }
+            }
+            GenericValue orderItemShipGroup = EntityQuery.use(delegator).from("OrderItemShipGroup").where("orderId", orderId, "shipGroupSeqId", shipGroupSeqId).queryOne();
+            if (UtilValidate.isEmpty(orderItemShipGroup)) {
+                String errMsg = mainErrorMessage + UtilProperties.getMessage(resource_error, "OrderCartShipGroupNotFound", UtilMisc.toMap("groupIndex", shipGroupSeqId), locale);
+                return ServiceUtil.returnError(errMsg);
+            }
+            //now test quantity parameter
+            //if quantity is null or negative then display error
+            if (quantity == null || quantity.compareTo(BigDecimal.ZERO) == -1) {
+                String errMsg = mainErrorMessage + UtilProperties.getMessage(resource_error, "OrderQuantityAssociatedCannotBeNullOrNegative", locale);
+                return ServiceUtil.returnError(errMsg);
+            }
+            //test if this association already exist if yes display error
+            GenericValue oisgAssoc = EntityQuery.use(delegator).from("OrderItemShipGroupAssoc").where("orderId", orderId, "orderItemSeqId", orderItem.get("orderItemSeqId"), "shipGroupSeqId", shipGroupSeqId).queryOne();
+            if (UtilValidate.isNotEmpty(oisgAssoc)) {
+                String errMsg = mainErrorMessage + UtilProperties.getMessage(resource_error, "OrderErrorOrderItemAlreadyRelatedToShipGroup", locale);
+                return ServiceUtil.returnError(errMsg);
+            }
+            //no error, create OISGA
+            oisgAssoc = delegator.makeValue("OrderItemShipGroupAssoc", UtilMisc.toMap("orderId", orderId, "orderItemSeqId", orderItem.get("orderItemSeqId"), "shipGroupSeqId", shipGroupSeqId));
+            oisgAssoc.set("quantity", quantity);
+            oisgAssoc.create();
+            return ServiceUtil.returnSuccess();
+        } else {
+            String errMsg = UtilProperties.getMessage(resource, mainErrorMessage + orderItem, locale);
+            return ServiceUtil.returnError(errMsg);
+        }
+    }
+
+    /**
+     * Update orderItem and shipgroup association
+     * @param ctx
+     * @param context
+     * @return
+     * @throws GeneralException
+     */
+    public static Map updateOrderItemShipGroupAssoc(DispatchContext dctx, Map context) throws GeneralException{
+        Map<String, Object> result = ServiceUtil.returnSuccess();
+        String message = null;
+        Delegator delegator = dctx.getDelegator();
+        LocalDispatcher dispatcher = dctx.getDispatcher();
+        Locale locale = (Locale) context.get("locale" );
+        GenericValue userLogin = (GenericValue) context.get("userLogin" );
+
+        String orderId = (String) context.get("orderId");
+        String orderItemSeqId = (String) context.get("orderItemSeqId");
+        String shipGroupSeqId = (String) context.get("shipGroupSeqId");
+        BigDecimal quantity = (BigDecimal) context.get("quantity");
+        if (UtilValidate.isEmpty(quantity)) {
+            quantity = BigDecimal.ZERO;
+        }
+        BigDecimal totalQuantity = (BigDecimal) context.get("totalQuantity");
+        if (UtilValidate.isEmpty(totalQuantity)) {
+            totalQuantity = BigDecimal.ZERO;
+        }
+
+        //main message error
+        String mainErrorMessage = UtilProperties.getMessage(resource_error, "OrderUnableToUpdateOrderItemFromOISG", locale);
+        Integer rowCount = (Integer) context.get("rowCount");
+        Integer rowNumber = (Integer) context.get("rowNumber"); //total row number
+
+        if (rowNumber == null) {
+            Long count = EntityQuery.use(delegator).from("OrderItemShipGroupAssoc").where("orderId", orderId, "orderItemSeqId", orderItemSeqId).queryCount();
+            if (count != null) {
+                rowNumber = new Integer(count.intValue());
+                result.put("rowNumber", rowNumber);
+            }
+        }
+
+        //find OISG Assoc
+        GenericValue oisga = EntityQuery.use(delegator).from("OrderItemShipGroupAssoc").where("orderId", orderId, "orderItemSeqId", orderItemSeqId, "shipGroupSeqId", shipGroupSeqId).queryOne();
+        if (UtilValidate.isEmpty(oisga)) {
+            String errMsg = mainErrorMessage + " : Order Item Ship Group Assoc Does Not Exist";
+            Debug.logError(errMsg, module);
+            return ServiceUtil.returnError(errMsg);
+        }
+
+        // find OISG associated with oisga
+        GenericValue oisg = EntityQuery.use(delegator).from("OrderItemShipGroup").where("orderId", orderId, "shipGroupSeqId", shipGroupSeqId).queryOne();
+        //find OrderItem
+        GenericValue orderItem = EntityQuery.use(delegator).from("OrderItem").where("orderId", orderId, "orderItemSeqId", orderItemSeqId).queryOne();
+        if (UtilValidate.isEmpty(orderItem)) {
+            String errMsg = mainErrorMessage + UtilProperties.getMessage(resource_error, "OrderErrorOrderItemNotFound", UtilMisc.toMap("orderId", orderId, "orderItemSeqId", orderItemSeqId), locale);
+            return ServiceUtil.returnError(errMsg);
+        }
+
+        // update OISGA  
+        if (oisg != null) {
+            //if quantity is 0, delete this association only if there is several oisgaoc
+            if (ZERO.compareTo(quantity) == 0) {
+                // test if  there is only one oisgaoc then display errror
+                if (rowNumber.intValue() == 1) {
+                    String errMsg = mainErrorMessage + UtilProperties.getMessage(resource_error, "OrderQuantityAssociatedCannotBeNullOrNegative", locale);
+                    Debug.logError(errMsg, module);
+                    return ServiceUtil.returnError(errMsg);
+                }
+                try {
+                    Map<String, Object> cancelOrderInventoryReservationMap = dctx.makeValidContext("cancelOrderInventoryReservation", "IN", context);
+                    Map<String, Object> localResult = dispatcher.runSync("cancelOrderInventoryReservation", cancelOrderInventoryReservationMap);
+                    if (ServiceUtil.isError(localResult)) return localResult;
+                    Map<String, Object> deleteOrderItemShipGroupAssocMap = dctx.makeValidContext("deleteOrderItemShipGroupAssoc", "IN", context);
+                    localResult = dispatcher.runSync("deleteOrderItemShipGroupAssoc", deleteOrderItemShipGroupAssocMap);
+                    if (ServiceUtil.isError(localResult)) return localResult;
+                } catch (GenericServiceException e) {
+                    return ServiceUtil.returnError(e.toString());
+                }
+                //Only for multi service calling and the last row : test if orderItem quantity equals OrderItemShipGroupAssocs quantitys
+                if (rowCount != null && rowNumber != null ) {
+                    int rowCountInt = rowCount .intValue();
+                    int rowNumberInt = rowNumber .intValue();
+                    if (rowCountInt == rowNumberInt - 1) {
+                        try {
+                            message = validateOrderItemShipGroupAssoc(delegator, dispatcher, orderItem, totalQuantity, oisga, userLogin, locale);
+                        }
+                        catch (Exception e) {
+                            String errMsg = mainErrorMessage + UtilProperties.getMessage(resource_error, "OrderQuantityAssociatedIsLessThanOrderItemQuantity", locale);
+                            Debug.logError(errMsg, module);
+                            return ServiceUtil.returnError(errMsg);
+                        }
+                    }
+                }
+                result.put("totalQuantity", totalQuantity);
+                if (UtilValidate.isNotEmpty(message)) {
+                    result.put("successMessage", message);
+                }
+                return result;
+           }
+
+           BigDecimal actualQuantity = totalQuantity.add(quantity);
+           BigDecimal qty = (BigDecimal) orderItem.get("quantity");
+           if (UtilValidate.isEmpty(qty)) {
+               qty = BigDecimal.ZERO;
+           }
+           BigDecimal cancelQty = (BigDecimal) orderItem.get("cancelQuantity");
+           if (UtilValidate.isEmpty(cancelQty)) {
+               cancelQty = BigDecimal.ZERO;
+           }
+           BigDecimal orderItemQuantity = qty.subtract(cancelQty);
+            if (actualQuantity.compareTo(orderItemQuantity ) > 0) {
+                String errMsg = mainErrorMessage + UtilProperties.getMessage(resource_error, "OrderQuantityAssociatedIsBiggerThanOrderItemQuantity", locale);
+                Debug.logError(errMsg, module);
+                return ServiceUtil.returnError(errMsg);
+            }
+
+            //if quantity is bigger than OI then display error
+            if (quantity.compareTo(orderItemQuantity) > 0) {
+                String errMsg = mainErrorMessage + UtilProperties.getMessage(resource_error, "OrderQuantityAssociatedIsBiggerThanOrderItemQuantity", locale);
+                Debug.logError(errMsg, module);
+                return ServiceUtil.returnError(errMsg);
+            }
+            oisga.set("quantity", quantity);
+            //store new values
+            oisga.store();
+            // reserve the inventory
+            GenericValue orderHeader = EntityQuery.use(delegator).from("OrderHeader").where("orderId", orderId).queryOne();
+            if (UtilValidate.isNotEmpty(orderHeader)) {
+                Map<String, Object> cancelResp = dispatcher.runSync("cancelOrderInventoryReservation", UtilMisc.toMap("userLogin", userLogin, "orderId", orderId, "orderItemSeqId", orderItemSeqId, "shipGroupSeqId", shipGroupSeqId ));
+                if (ServiceUtil.isError(cancelResp)) {
+                    throw new GeneralException(ServiceUtil.getErrorMessage(cancelResp));
+                }
+                String productStoreId = orderHeader.getString("productStoreId");
+                String orderTypeId = orderHeader.getString("orderTypeId");
+                List<String> resErrorMessages = new LinkedList<String>();
+                if (Debug.infoOn()) Debug.logInfo("Calling reserve inventory...", module);
+                reserveInventory(delegator, dispatcher, userLogin, locale, UtilMisc.toList(oisga), null, UtilMisc.<String, GenericValue>toMap(orderItemSeqId, orderItem), orderTypeId, productStoreId, resErrorMessages);
+            }
+
+            //update totalQuantity
+            totalQuantity = totalQuantity.add(quantity);
+            result.put("totalQuantity", totalQuantity);
+
+            //Only for multi service calling and the last row : test if orderItem quantity equals OrderItemShipGroupAssocs quantitys
+            if (rowCount != null && rowNumber != null ) {
+                int rowCountInt = rowCount .intValue();
+                int rowNumberInt = rowNumber .intValue();
+                if (rowCountInt == rowNumberInt - 1) {
+                    try {
+                        message = validateOrderItemShipGroupAssoc(delegator, dispatcher, orderItem, totalQuantity,  oisga, userLogin, locale);
+                    }
+                    catch (GeneralException e) {
+                        String errMsg = mainErrorMessage + UtilProperties.getMessage(resource_error, "OrderQuantityAssociatedIsLessThanOrderItemQuantity", locale);
+                        Debug.logError(errMsg, module);
+                        return ServiceUtil.returnError(errMsg);
+                    }
+                }
+                if (UtilValidate.isNotEmpty(message)) {
+                    result.put("successMessage", message);
+                }
+            }
+        } else {
+            //update totalQuantity
+            totalQuantity = totalQuantity.add(quantity);
+            result.put("totalQuantity", totalQuantity);
+        }
+        return result;
+    }
+
+    /**
+     * Validate OrderItemShipGroupAssoc quantity
+     * This service should be called after updateOrderItemShipGroupAssoc
+     * test if orderItem quantity equals OrderItemShipGroupAssocs quantities
+     * if not then get the last orderItemShipgroupAssoc estimated shipDate and add quantity to this OrderItemShipGroupAssoc
+     * @param ctx
+     * @param context
+     * @return
+     * @throws GeneralException
+     */
+    private static String validateOrderItemShipGroupAssoc(Delegator delegator, LocalDispatcher dispatcher, GenericValue orderItem, BigDecimal totalQuantity, GenericValue lastOISGAssoc, GenericValue userLogin, Locale locale)
+           throws GeneralException {
+        String result = null;
+        BigDecimal qty = (BigDecimal) orderItem.get("quantity");
+        if (UtilValidate.isEmpty(qty)) {
+            qty = BigDecimal.ZERO;
+        }
+        BigDecimal cancelQty = (BigDecimal) orderItem.get("cancelQuantity");
+        if (UtilValidate.isEmpty(cancelQty)) {
+            cancelQty = BigDecimal.ZERO;
+        }
+
+        BigDecimal orderItemQuantity = qty.subtract(cancelQty);
+        if (totalQuantity.compareTo(orderItemQuantity) < 0) {
+            //if quantity in orderItem is bigger than in totalQUantity then added missing quantity in ShipGroupAssoc
+            BigDecimal adjustementQuantity = orderItemQuantity.subtract( totalQuantity);
+            BigDecimal lastOISGAssocQuantity = (BigDecimal) lastOISGAssoc.get("quantity");
+            if (UtilValidate.isEmpty(lastOISGAssocQuantity)) {
+                lastOISGAssocQuantity = BigDecimal.ZERO;
+            }
+            BigDecimal oisgaQty = lastOISGAssocQuantity.add(adjustementQuantity);
+            lastOISGAssoc.set("quantity", oisgaQty);
+            lastOISGAssoc.store();
+
+            // reserve the inventory
+            GenericValue orderHeader = EntityQuery.use(delegator).from("OrderHeader").where("orderId", lastOISGAssoc.get("orderId")).queryOne();
+            if (UtilValidate.isNotEmpty(orderHeader)) {
+                Map<String, Object> cancelOrderInventoryReservationMap = UtilMisc.toMap("userLogin", userLogin, "locale", locale);
+                cancelOrderInventoryReservationMap.put("orderId", lastOISGAssoc.get("orderId"));
+                cancelOrderInventoryReservationMap.put("orderItemSeqId", lastOISGAssoc.get("orderItemSeqId"));
+                cancelOrderInventoryReservationMap.put("shipGroupSeqId", lastOISGAssoc.get("shipGroupSeqId"));
+                Map<String, Object> cancelResp = dispatcher.runSync("cancelOrderInventoryReservation", cancelOrderInventoryReservationMap);
+                if (ServiceUtil.isError(cancelResp)) {
+                    throw new GeneralException(ServiceUtil.getErrorMessage(cancelResp));
+                }
+                String productStoreId = orderHeader.getString("productStoreId");
+                String orderTypeId = orderHeader.getString("orderTypeId");
+                List<String> resErrorMessages = new LinkedList<String>();
+                if (Debug.infoOn()) Debug.logInfo("Calling reserve inventory...", module);
+                reserveInventory(delegator, dispatcher, userLogin, locale, UtilMisc.toList(lastOISGAssoc), null, UtilMisc.<String, GenericValue>toMap(lastOISGAssoc.getString("orderItemSeqId"), orderItem), orderTypeId, productStoreId, resErrorMessages);
+            }
+
+            //return warning message
+            Map<String, Object> messageParameters = new HashMap<String, Object>();
+            messageParameters.put("shipByDate", lastOISGAssoc.getRelatedOne("OrderItemShipGroup", false).getString("shipByDate"));
+            messageParameters.put("adjustementQuantity", adjustementQuantity);
+            return "Order OISG Assoc Quantity Auto Completed";
+        }
+        return result;
+    }
+
     public static Map<String, Object> setShippingInstructions(DispatchContext dctx, Map<String, ? extends Object> context) {
         Delegator delegator = dctx.getDelegator();
         String orderId = (String) context.get("orderId");

Modified: ofbiz/trunk/applications/order/src/org/ofbiz/order/shoppingcart/ShoppingCart.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/order/src/org/ofbiz/order/shoppingcart/ShoppingCart.java?rev=1643077&r1=1643076&r2=1643077&view=diff
==============================================================================
--- ofbiz/trunk/applications/order/src/org/ofbiz/order/shoppingcart/ShoppingCart.java (original)
+++ ofbiz/trunk/applications/order/src/org/ofbiz/order/shoppingcart/ShoppingCart.java Wed Dec  3 10:15:20 2014
@@ -2209,6 +2209,39 @@ public class ShoppingCart implements Ite
         }
     }
 
+    public int getShipInfoIndex (String shipGroupSeqId) {
+        int idx = -1;
+        for (int i=0; i<shipInfo.size(); i++) {
+            CartShipInfo csi = (CartShipInfo) shipInfo.get(i);
+            if (csi.shipGroupSeqId.equals(shipGroupSeqId)) {
+                idx = i;
+                break;
+            }
+        }
+        return idx;
+    }
+
+    /**
+    * Return index of the ship group where the item is located
+    * @return
+    */
+    public int getItemShipGroupIndex(int itemId) {
+    int shipGroupIndex = this.getShipGroupSize() - 1;
+    ShoppingCartItem item = this.findCartItem(itemId);
+    int result=0;
+    for (int i = 0; i <(shipGroupIndex + 1); i++) {
+       CartShipInfo csi = this.getShipInfo(i);
+       Iterator it = csi.shipItemInfo.keySet().iterator();
+        while (it.hasNext()) {
+            ShoppingCartItem item2 = (ShoppingCartItem) it.next();
+            if (item.equals(item2) ) {
+                result = i;
+            }
+        }
+    }
+    return result;
+    }
+
     /** Sets the shipping contact mech id. */
     public void setShippingContactMechId(int idx, String shippingContactMechId) {
         CartShipInfo csi = this.getShipInfo(idx);
@@ -3922,7 +3955,12 @@ public class ShoppingCart implements Ite
         List<GenericValue> groups = new LinkedList<GenericValue>();
         long seqId = 1;
         for (CartShipInfo csi : this.shipInfo) {
-            groups.addAll(csi.makeItemShipGroupAndAssoc(this.getDelegator(), this, seqId));
+            String shipGroupSeqId = csi.shipGroupSeqId;
+            if (shipGroupSeqId != null) {
+                groups.addAll(csi.makeItemShipGroupAndAssoc(this.getDelegator(), this, shipGroupSeqId));
+            } else {
+                groups.addAll(csi.makeItemShipGroupAndAssoc(this.getDelegator(), this, UtilFormatOut.formatPaddedNumber(seqId, 5), true));
+            }
             seqId++;
         }
         return groups;
@@ -4505,8 +4543,11 @@ public class ShoppingCart implements Ite
             }
         }
 
-        public List<GenericValue> makeItemShipGroupAndAssoc(Delegator delegator, ShoppingCart cart, long groupIndex) {
-            shipGroupSeqId = UtilFormatOut.formatPaddedNumber(groupIndex, 5);
+        public List<GenericValue> makeItemShipGroupAndAssoc(Delegator delegator, ShoppingCart cart, String shipGroupSeqId) {
+            return makeItemShipGroupAndAssoc(delegator, cart, shipGroupSeqId, false);
+        }
+
+        public List<GenericValue> makeItemShipGroupAndAssoc(Delegator delegator, ShoppingCart cart, String shipGroupSeqId, boolean newShipGroup) {
             List<GenericValue> values = new LinkedList<GenericValue>();
 
             // create order contact mech for shipping address

Modified: ofbiz/trunk/applications/order/src/org/ofbiz/order/shoppingcart/ShoppingCartItem.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/order/src/org/ofbiz/order/shoppingcart/ShoppingCartItem.java?rev=1643077&r1=1643076&r2=1643077&view=diff
==============================================================================
--- ofbiz/trunk/applications/order/src/org/ofbiz/order/shoppingcart/ShoppingCartItem.java (original)
+++ ofbiz/trunk/applications/order/src/org/ofbiz/order/shoppingcart/ShoppingCartItem.java Wed Dec  3 10:15:20 2014
@@ -1073,6 +1073,11 @@ public class ShoppingCartItem implements
 
         // set the item ship group
         if (resetShipGroup) {
+            int itemId = cart.getItemIndex(this);
+            int shipGroupIndex = 0;
+            if (itemId != -1) {
+                shipGroupIndex = cart.getItemShipGroupIndex(itemId);
+            }
             cart.clearItemShipInfo(this);
 
             /*
@@ -1132,7 +1137,7 @@ public class ShoppingCartItem implements
             }
             cart.setItemShipGroupQty(this, quantity, shipGroupIndex);
             */
-            cart.setItemShipGroupQty(this, quantity, 0);
+            cart.setItemShipGroupQty(this, quantity, shipGroupIndex);
         }
     }
 

Modified: ofbiz/trunk/applications/order/src/org/ofbiz/order/shoppingcart/ShoppingCartServices.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/order/src/org/ofbiz/order/shoppingcart/ShoppingCartServices.java?rev=1643077&r1=1643076&r2=1643077&view=diff
==============================================================================
--- ofbiz/trunk/applications/order/src/org/ofbiz/order/shoppingcart/ShoppingCartServices.java (original)
+++ ofbiz/trunk/applications/order/src/org/ofbiz/order/shoppingcart/ShoppingCartServices.java Wed Dec  3 10:15:20 2014
@@ -30,7 +30,6 @@ import java.util.Map;
 import javolution.util.FastList;
 import javolution.util.FastMap;
 
-import org.apache.commons.lang.math.NumberUtils;
 import org.ofbiz.base.util.Debug;
 import org.ofbiz.base.util.GeneralException;
 import org.ofbiz.base.util.UtilDateTime;
@@ -312,6 +311,7 @@ public class ShoppingCartServices {
             int newShipInfoIndex = cart.addShipInfo();
 
             // shouldn't be gaps in it but allow for that just in case
+            /*
             String cartShipGroupIndexStr = orderItemShipGroup.getString("shipGroupSeqId");
             int cartShipGroupIndex = NumberUtils.toInt(cartShipGroupIndexStr);
 
@@ -321,6 +321,7 @@ public class ShoppingCartServices {
                     newShipInfoIndex = cart.addShipInfo();
                 }
             }
+            */
 
             CartShipInfo cartShipInfo = cart.getShipInfo(newShipInfoIndex);
 
@@ -337,6 +338,7 @@ public class ShoppingCartServices {
             cartShipInfo.setVendorPartyId(orderItemShipGroup.getString("vendorPartyId"));
             cartShipInfo.setShipGroupSeqId(orderItemShipGroup.getString("shipGroupSeqId"));
             cartShipInfo.shipTaxAdj.addAll(orh.getOrderHeaderAdjustmentsTax(orderItemShipGroup.getString("shipGroupSeqId")));
+            cart.setShipGroupSeqId(newShipInfoIndex - 1, orderItemShipGroup.getString("shipGroupSeqId"));
         }
 
         List<GenericValue> orderItems = orh.getOrderItems();
@@ -564,6 +566,9 @@ public class ShoppingCartServices {
                     List<GenericValue> orderItemAdjustments = orh.getOrderItemAdjustments(item);
                     // set the item's ship group info
                     List<GenericValue> shipGroupAssocs = orh.getOrderItemShipGroupAssocs(item);
+                    if (UtilValidate.isNotEmpty(shipGroupAssocs)) {
+                        shipGroupAssocs = EntityUtil.orderBy(shipGroupAssocs, UtilMisc.toList("-shipGroupSeqId"));
+                    }
                     for (int g = 0; g < shipGroupAssocs.size(); g++) {
                         GenericValue sgAssoc = shipGroupAssocs.get(g);
                         BigDecimal shipGroupQty = OrderReadHelper.getOrderItemShipGroupQuantity(sgAssoc);
@@ -572,9 +577,7 @@ public class ShoppingCartServices {
                         }
 
                         String cartShipGroupIndexStr = sgAssoc.getString("shipGroupSeqId");
-                        int cartShipGroupIndex = NumberUtils.toInt(cartShipGroupIndexStr);
-                        cartShipGroupIndex = cartShipGroupIndex - 1;
-
+                        int cartShipGroupIndex = cart.getShipInfoIndex(cartShipGroupIndexStr);
                         if (cartShipGroupIndex > 0) {
                             cart.positionItemToGroup(itemIndex, shipGroupQty, 0, cartShipGroupIndex, false);
                         }

Modified: ofbiz/trunk/applications/order/testdef/ShoppingCartTests.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/order/testdef/ShoppingCartTests.xml?rev=1643077&r1=1643076&r2=1643077&view=diff
==============================================================================
--- ofbiz/trunk/applications/order/testdef/ShoppingCartTests.xml (original)
+++ ofbiz/trunk/applications/order/testdef/ShoppingCartTests.xml Wed Dec  3 10:15:20 2014
@@ -40,4 +40,8 @@ under the License.
     <test-case case-name="configurableServiceOrder-test">
         <simple-method-test location="component://order/script/org/ofbiz/order/test/ShoppingCartTests.xml" name="testCreateOrderConfigurableServiceProduct"/>
     </test-case>
+
+    <test-case case-name="testOrderMoveItemBetweenShipGoups">
+        <simple-method-test location="component://order/script/org/ofbiz/order/test/ShoppingCartTests.xml" name="testOrderMoveItemBetweenShipGoups"/>
+    </test-case>
 </test-suite>

Modified: ofbiz/trunk/applications/order/webapp/ordermgr/WEB-INF/controller.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/order/webapp/ordermgr/WEB-INF/controller.xml?rev=1643077&r1=1643076&r2=1643077&view=diff
==============================================================================
--- ofbiz/trunk/applications/order/webapp/ordermgr/WEB-INF/controller.xml (original)
+++ ofbiz/trunk/applications/order/webapp/ordermgr/WEB-INF/controller.xml Wed Dec  3 10:15:20 2014
@@ -1731,6 +1731,38 @@ under the License.
         <response name="success" type="view" value="product"/>
     </request-map>
 
+    <request-map uri="AddOrderItemShipGroup">
+        <security auth="true" https="true"/>
+        <event type="service" path="" invoke="addOrderItemShipGroup"/>
+        <response name="success" type="view" value="orderview"/>
+        <response name="error" type="view" value="orderview"/>
+    </request-map>
+    <request-map uri="DeleteOrderItemShipGroup">
+        <security auth="true" https="true"/>
+        <event type="service" path="" invoke="deleteOrderItemShipGroup"/>
+        <response name="success" type="view" value="orderview"/>
+        <response name="error" type="view" value="orderview"/>
+    </request-map>
+
+    <request-map uri="AddOrderItemShipGroupAssoc">
+        <security auth="true" https="true"/>
+        <event type="service" invoke="addOrderItemShipGroupAssoc"/>
+        <response name="success" type="view" value="orderview"/>
+        <response name="error" type="view" value="orderview"/>
+    </request-map>
+    <request-map uri="UpdateOrderItemShipGroupAssoc">
+        <security auth="true" https="true"/>
+        <event type="service-multi" invoke="updateOrderItemShipGroupAssoc"/>
+        <response name="success" type="view" value="orderview"/>
+        <response name="error" type="view" value="orderview"/>
+    </request-map>
+    <request-map uri="DeleteOrderItemShipGroupAssoc">
+        <security auth="true" https="true"/>
+        <event type="service" invoke="deleteOrderItemShipGroupAssoc"/>
+        <response name="success" type="view" value="orderview"/>
+        <response name="error" type="view" value="orderview"/>
+    </request-map>
+
     <!-- Lookup request mappings -->
     <request-map uri="LookupPerson"><security https="true" auth="true"/><response name="success" type="view" value="LookupPerson"/></request-map>
     <request-map uri="LookupPartyGroup"><security https="true" auth="true"/><response name="success" type="view" value="LookupPartyGroup"/></request-map>