svn commit: r1597235 - in /ofbiz/trunk: applications/order/data/ applications/product/config/ applications/product/entitydef/ applications/product/servicedef/ applications/product/src/org/ofbiz/product/subscription/ applications/product/widget/catalog/...

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

svn commit: r1597235 - in /ofbiz/trunk: applications/order/data/ applications/product/config/ applications/product/entitydef/ applications/product/servicedef/ applications/product/src/org/ofbiz/product/subscription/ applications/product/widget/catalog/...

jleroux@apache.org
Author: jleroux
Date: Sat May 24 04:37:16 2014
New Revision: 1597235

URL: http://svn.apache.org/r1597235
Log:
A (slightly modified) patch from Yvan Cauchy for "Trigger for service to run upon subscription expiry" https://issues.apache.org/jira/browse/OFBIZ-5333

We are also proposing to add the following fields:

    ProductSubscriptionResource.gracePeriodOnExpiry
    ProductSubscriptionResource.gracePeriodOnExpiryUomId
    Subscription.gracePeriodOnExpiry
    Subscription.gracePeriodOnExpiryUomId

This will allow the runServiceOnSubscriptionExpiry to trigger the service in SubscriptionResource.serviceNameOnExpiry only after

nowTimeStamp >= Subscription.thruDate + Subscription.gracePeriodOnExpiry.

This provides flexibility in managing the triggering of the deprovisioning service, similar to the flexibility in automatic subscription extensions enabled by Subscription.canclAutmExtTime for runSubscriptionAutoReorders.

jleroux:
I replaced tabs by spaces
Will commit the change in serviceResult.ftl apart
Slightly reformatted SubscriptionServices.runServiceOnSubscriptionExpiry()
Slightly reformatted and added comment in services_subscription.xml for the 2 new services

Modified:
    ofbiz/trunk/applications/order/data/OrderScheduledServices.xml
    ofbiz/trunk/applications/product/config/ProductUiLabels.xml
    ofbiz/trunk/applications/product/entitydef/entitymodel.xml
    ofbiz/trunk/applications/product/servicedef/services_subscription.xml
    ofbiz/trunk/applications/product/src/org/ofbiz/product/subscription/SubscriptionServices.java
    ofbiz/trunk/applications/product/widget/catalog/SubscriptionForms.xml
    ofbiz/trunk/specialpurpose/ecommerce/data/DemoProduct.xml

Modified: ofbiz/trunk/applications/order/data/OrderScheduledServices.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/order/data/OrderScheduledServices.xml?rev=1597235&r1=1597234&r2=1597235&view=diff
==============================================================================
--- ofbiz/trunk/applications/order/data/OrderScheduledServices.xml (original)
+++ ofbiz/trunk/applications/order/data/OrderScheduledServices.xml Sat May 24 04:37:16 2014
@@ -31,5 +31,5 @@ under the License.
     <JobSandbox jobId="8007" jobName="Replacement Held Order Auto-Cancel" runTime="2000-01-01 00:00:00.000" serviceName="autoCancelReplacementOrders" poolId="pool" runAsUser="system" tempExprId="MIDNIGHT_DAILY" maxRecurrenceCount="-1"/>
     <JobSandbox jobId="8008" jobName="Create Also Bought Product Associations" runTime="2000-01-01 00:00:00.000" serviceName="createAlsoBoughtProductAssocs" poolId="pool" runAsUser="system" tempExprId="MIDNIGHT_DAILY" maxRecurrenceCount="-1"/>
     <JobSandbox jobId="8009" jobName="Delete auto-save shopping list for anonymous users" runTime="2000-01-01 00:00:00.000" serviceName="autoDeleteAutoSaveShoppingList" poolId="pool" runAsUser="system" tempExprId="MIDNIGHT_DAILY" maxRecurrenceCount="-1"/>
-
+    <JobSandbox jobId="8010" jobName="Run Nominated Service on Subscription Expiry" runTime="2014-04-21 00:00:00.000" serviceName="runServiceOnSubscriptionExpiry" poolId="pool" runAsUser="system" tempExprId="MIDNIGHT_DAILY" maxRecurrenceCount="-1"/>
 </entity-engine-xml>

Modified: ofbiz/trunk/applications/product/config/ProductUiLabels.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/product/config/ProductUiLabels.xml?rev=1597235&r1=1597234&r2=1597235&view=diff
==============================================================================
--- ofbiz/trunk/applications/product/config/ProductUiLabels.xml (original)
+++ ofbiz/trunk/applications/product/config/ProductUiLabels.xml Sat May 24 04:37:16 2014
@@ -17566,6 +17566,9 @@
         <value xml:lang="zh">商品收据</value>
         <value xml:lang="zh_TW">成品收據</value>
     </property>
+    <property key="ProductGracePeriodUomId">
+        <value xml:lang="en">Grace Period UOM Id</value>
+    </property>
     <property key="ProductGrams">
         <value xml:lang="de">Gramm</value>
         <value xml:lang="en">Grams</value>
@@ -28795,7 +28798,7 @@
         <value xml:lang="ja">リソースID ${subscriptionResourceId} の処理で申込作成中にエラー</value>
         <value xml:lang="vi">Lỗi tạo Bảo lãnh bán hàng khi xá»­ lý Tài nguyên có Id ${subscriptionResourceId}</value>
         <value xml:lang="zh">创建订阅、当处理资源标识 ${subscriptionResourceId} 时出错</value>
-    </property>
+    </property>    
     <property key="ProductSubscriptionUpdateError">
         <value xml:lang="en">Error processing subscription update with ID ${subscriptionId}</value>
         <value xml:lang="it">Errore durante l'aggiornamento dell'abbonamento ${subscriptionId}</value>

Modified: ofbiz/trunk/applications/product/entitydef/entitymodel.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/product/entitydef/entitymodel.xml?rev=1597235&r1=1597234&r2=1597235&view=diff
==============================================================================
--- ofbiz/trunk/applications/product/entitydef/entitymodel.xml (original)
+++ ofbiz/trunk/applications/product/entitydef/entitymodel.xml Sat May 24 04:37:16 2014
@@ -4394,6 +4394,8 @@ under the License.
         <field name="automaticExtend" type="indicator"><description>If this subscription is automatically extended with the same period as the initial period.</description></field>
         <field name="canclAutmExtTime" type="numeric"><description>The time period (before the end of the thruDate) after which the automatic extension of the subscription will be executed.</description></field>
         <field name="canclAutmExtTimeUomId" type="id"><description>Unit Of Measure used for the automatic extension of the subscription.</description></field>
+        <field name="gracePeriodOnExpiry" type="numeric"><description>The time period (after the end of the thruDate) after which the subscription will be expired.</description></field>
+        <field name="gracePeriodOnExpiryUomId" type="id"><description>Unit Of Measure used for the grace period of the subscription.</description></field>
         <prim-key field="productId"/>
         <prim-key field="subscriptionResourceId"/>
         <prim-key field="fromDate"/>
@@ -4418,6 +4420,9 @@ under the License.
         <relation type="one" fk-name="PROD_SBRS_MTU" title="MaxLifeTime" rel-entity-name="Uom">
             <key-map field-name="maxLifeTimeUomId" rel-field-name="uomId"/>
         </relation>
+        <relation type="one" fk-name="PROD_SBRS_GTU" title="GracePeriod" rel-entity-name="Uom">
+            <key-map field-name="gracePeriodOnExpiryUomId" rel-field-name="uomId"/>
+        </relation>
     </entity>
     <entity entity-name="Subscription"
             package-name="org.ofbiz.product.subscription"
@@ -4455,6 +4460,9 @@ under the License.
         <field name="automaticExtend" type="indicator"><description>If this subscription is automatically extended with the same period as the initial period.</description></field>
         <field name="canclAutmExtTime" type="numeric"><description>The time period (before the end of the thruDate) after which the automatic extension of the subscription will be executed.</description></field>
         <field name="canclAutmExtTimeUomId" type="id"><description>Unit Of Measure used for the automatic extension of the subscription.</description></field>
+        <field name="gracePeriodOnExpiry" type="numeric"><description>The time period (before the end of the thruDate) after which the automatic extension of the subscription will be executed.</description></field>
+        <field name="gracePeriodOnExpiryUomId" type="id"><description>Unit Of Measure used for the automatic extension of the subscription.</description></field>
+        <field name="serviceNameOnExpiry" type="long-varchar"><description>Name of service which will run on subscription expiration.</description></field>
         <prim-key field="subscriptionId"/>
         <relation type="one" fk-name="SUBSC_SRESRC" rel-entity-name="SubscriptionResource">
             <key-map field-name="subscriptionResourceId"/>
@@ -4526,6 +4534,9 @@ under the License.
         <relation type="many" rel-entity-name="SubscriptionTypeAttr">
             <key-map field-name="subscriptionTypeId"/>
         </relation>
+        <relation type="one" fk-name="SUBSC_GTU" title="GracePeriod" rel-entity-name="Uom">
+            <key-map field-name="gracePeriodOnExpiryUomId" rel-field-name="uomId"/>
+        </relation>
     </entity>
     <entity entity-name="SubscriptionActivity" package-name="org.ofbiz.product.subscription" title="Subscription Activity Entity">
         <field name="subscriptionActivityId" type="id-ne"></field>
@@ -4568,6 +4579,7 @@ under the License.
         <field name="description" type="description"></field>
         <field name="contentId" type="id"><description>Optional (use if applicable) ID of a Content record that this would represent a subscription to.</description></field>
         <field name="webSiteId" type="id"><description>Optional (use if applicable) ID of a WebSite record that this would represent a subscription to.</description></field>
+        <field name="serviceNameOnExpiry" type="long-varchar"><description>Name of service which will run on subscription expiration.</description></field>
         <prim-key field="subscriptionResourceId"/>
         <relation type="one" fk-name="SUBSC_RES_PARENT" title="Parent" rel-entity-name="SubscriptionResource">
             <key-map field-name="parentResourceId" rel-field-name="subscriptionResourceId"/>

Modified: ofbiz/trunk/applications/product/servicedef/services_subscription.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/product/servicedef/services_subscription.xml?rev=1597235&r1=1597234&r2=1597235&view=diff
==============================================================================
--- ofbiz/trunk/applications/product/servicedef/services_subscription.xml (original)
+++ ofbiz/trunk/applications/product/servicedef/services_subscription.xml Sat May 24 04:37:16 2014
@@ -110,6 +110,8 @@ under the License.
         <attribute name="canclAutmExtTime" type="Integer" mode="IN" optional="true"/>
         <attribute name="canclAutmExtTimeUomId" type="String" mode="IN" optional="true"/>
         <attribute name="alwaysCreateNewRecord" type="String" mode="IN" optional="true"><!-- This defaults to Y (true) which means new Subscription records will be created instead of updating old ones with new thruDates. This keeps a more complete history of subscription activity. --></attribute>
+        <attribute name="gracePeriodOnExpiry" type="Integer" mode="IN" optional="true"/>
+        <attribute name="gracePeriodOnExpiryUomId" type="String" mode="IN" optional="true"/>
         <attribute name="subscriptionId" type="String" mode="OUT" optional="false"/>
     </service>
     <service name="processExtendSubscriptionByProduct" engine="java" auth="true"
@@ -155,4 +157,17 @@ under the License.
         <description>Subscription Permission Checking Logic</description>
         <implements service="permissionInterface"/>
     </service>
+    
+    <service name="runServiceOnSubscriptionExpiry" engine="java" auth="true" use-transaction="true"
+        location="org.ofbiz.product.subscription.SubscriptionServices" invoke="runServiceOnSubscriptionExpiry">
+        <description>A service designed to be automatically run by job scheduler to trigger another service to run for each subscription which has expired.
+            This is done by looking for all subscriptions for which thruDate and gracePeriodOnExpiry are expired and where the automaticExtend flag is set to "N".
+            The service to run is found in SubscriptionResource.ServiceNameOnExpiry (by default OOTB: runSubscriptionExpired, see below)</description>
+    </service>
+    <service name="runSubscriptionExpired" engine="java" auth="true" use-transaction="true"
+        location="org.ofbiz.product.subscription.SubscriptionServices" invoke="runSubscriptionExpired">
+        <description>A dummy service to test subscription expiration, expected to change depending upon the specific service logic that providers will write.
+            See https://issues.apache.org/jira/browse/OFBIZ-5333 for more information</description>
+        <attribute name="subscriptionId" type="String" mode="IN" optional="false"/>
+    </service>
 </services>

Modified: ofbiz/trunk/applications/product/src/org/ofbiz/product/subscription/SubscriptionServices.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/product/src/org/ofbiz/product/subscription/SubscriptionServices.java?rev=1597235&r1=1597234&r2=1597235&view=diff
==============================================================================
--- ofbiz/trunk/applications/product/src/org/ofbiz/product/subscription/SubscriptionServices.java (original)
+++ ofbiz/trunk/applications/product/src/org/ofbiz/product/subscription/SubscriptionServices.java Sat May 24 04:37:16 2014
@@ -20,6 +20,7 @@ package org.ofbiz.product.subscription;
 
 import java.math.BigDecimal;
 import java.sql.Timestamp;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -35,6 +36,8 @@ import org.ofbiz.common.uom.UomWorker;
 import org.ofbiz.entity.Delegator;
 import org.ofbiz.entity.GenericEntityException;
 import org.ofbiz.entity.GenericValue;
+import org.ofbiz.entity.condition.EntityCondition;
+import org.ofbiz.entity.condition.EntityOperator;
 import org.ofbiz.entity.util.EntityUtil;
 import org.ofbiz.service.DispatchContext;
 import org.ofbiz.service.GenericServiceException;
@@ -219,6 +222,8 @@ public class SubscriptionServices {
                 subContext.put("automaticExtend", productSubscriptionResource.get("automaticExtend"));
                 subContext.put("canclAutmExtTime", productSubscriptionResource.get("canclAutmExtTime"));
                 subContext.put("canclAutmExtTimeUomId", productSubscriptionResource.get("canclAutmExtTimeUomId"));
+                subContext.put("gracePeriodOnExpiry", productSubscriptionResource.get("gracePeriodOnExpiry"));
+                subContext.put("gracePeriodOnExpiryUomId", productSubscriptionResource.get("gracePeriodOnExpiryUomId"));
 
                 Map<String, Object> ctx = dctx.getModelService("processExtendSubscription").makeValid(subContext, ModelService.IN_PARAM);
                 Map<String, Object> processExtendSubscriptionResult = dispatcher.runSync("processExtendSubscription", ctx);
@@ -298,4 +303,97 @@ public class SubscriptionServices {
 
         return ServiceUtil.returnSuccess();
     }
+    
+    public static Map<String, Object> runServiceOnSubscriptionExpiry( DispatchContext dctx, Map<String, ? extends Object> context) {
+        LocalDispatcher dispatcher = dctx.getDispatcher();
+        Delegator delegator = dctx.getDelegator();
+        
+        GenericValue userLogin = (GenericValue) context.get("userLogin");        
+        Map<String, Object> result = new HashMap<String, Object>();
+        Map<String, Object> expiryMap = new HashMap<String, Object>();
+        String gracePeriodOnExpiry = null;
+        String gracePeriodOnExpiryUomId = null;
+        String subscriptionId = null;
+        
+        try {
+            EntityCondition cond1 = EntityCondition.makeCondition("automaticExtend", EntityOperator.EQUALS, "N");
+            EntityCondition cond2 = EntityCondition.makeCondition("automaticExtend", EntityOperator.EQUALS, null);
+            EntityCondition cond = EntityCondition.makeCondition(UtilMisc.toList(cond1, cond2), EntityOperator.OR);
+            List<GenericValue> subscriptionList = null;
+            subscriptionList = delegator.findList("Subscription", cond, null,null, null, false);
+            
+            if (subscriptionList != null) {
+                for (GenericValue subscription : subscriptionList) {
+                    Calendar currentDate = Calendar.getInstance();
+                    currentDate.setTime(UtilDateTime.nowTimestamp());
+                    // check if the thruDate + grace period (if provided) is earlier than today's date
+                    Calendar endDateSubscription = Calendar.getInstance();
+                    int field = Calendar.MONTH;
+                    String subscriptionResourceId = subscription.getString("subscriptionResourceId");
+                    GenericValue subscriptionResource = null;
+                    subscriptionResource = delegator.findOne("SubscriptionResource", UtilMisc.toMap("subscriptionResourceId", subscriptionResourceId), false);
+                    subscriptionId = subscription.getString("subscriptionId");
+                    gracePeriodOnExpiry = subscription.getString("gracePeriodOnExpiry");
+                    gracePeriodOnExpiryUomId = subscription.getString("gracePeriodOnExpiryUomId");
+                    String serviceNameOnExpiry = subscriptionResource.getString("serviceNameOnExpiry");
+                    endDateSubscription.setTime(subscription.getTimestamp("thruDate"));
+                    
+                    if (gracePeriodOnExpiry != null && gracePeriodOnExpiryUomId != null) {
+                        if ("TF_day".equals(gracePeriodOnExpiryUomId)) {
+                            field = Calendar.DAY_OF_YEAR;
+                        } else if ("TF_wk".equals(gracePeriodOnExpiryUomId)) {
+                            field = Calendar.WEEK_OF_YEAR;
+                        } else if ("TF_mon".equals(gracePeriodOnExpiryUomId)) {
+                            field = Calendar.MONTH;
+                        } else if ("TF_yr".equals(gracePeriodOnExpiryUomId)) {
+                            field = Calendar.YEAR;
+                        } else {
+                            Debug.logWarning("Don't know anything about gracePeriodOnExpiryUomId [" + gracePeriodOnExpiryUomId + "], defaulting to month", module);
+                        }
+                        endDateSubscription.add(field, Integer.valueOf(gracePeriodOnExpiry).intValue());
+                    }
+                    
+                    if ((currentDate.after(endDateSubscription) || currentDate.equals(endDateSubscription)) && serviceNameOnExpiry != null) {
+                        if (userLogin != null) {
+                            expiryMap.put("userLogin", userLogin);
+                        }
+                        if (subscriptionId != null) {
+                            expiryMap.put("subscriptionId", subscriptionId);
+                        }
+                        result = dispatcher.runSync(serviceNameOnExpiry, expiryMap);
+                        if (ServiceUtil.isSuccess(result)) {
+                            Debug.logInfo("Subscription expired successfully for subscription ID:" + subscriptionId, module);
+                        } else if (ServiceUtil.isError(result)) {
+                            result = null;
+                            Debug.logError("Error expiring subscription while processing with subscriptionId: " + subscriptionId, module);
+                        }
+
+                        if (result != null && subscriptionId != null) {
+                            Debug.logInfo("Service mentioned in serviceNameOnExpiry called with result: " + result.get("successMessage"), module);
+                        } else if (result == null && subscriptionId != null) {
+                            Debug.logError("Subscription couldn't be expired for subscriptionId: " + subscriptionId, module);
+                            return ServiceUtil.returnError("Subscription couldn't be expired for subscriptionId: " + subscriptionId);
+                        }
+                    }
+                }
+            }
+        } catch (GenericServiceException e) {
+            Debug.logError("Error while calling service specified in serviceNameOnExpiry", module);
+            return ServiceUtil.returnError(e.toString());
+        } catch (GenericEntityException e) {
+            Debug.logError(e, module);
+        }
+
+        return result;
+    }
+
+    public static Map<String, Object> runSubscriptionExpired(
+            DispatchContext dctx, Map<String, ? extends Object> context) {
+        String subscriptionId = (String) context.get("subscriptionId");
+        Map<String, Object> result = new HashMap<String, Object>();
+        if (subscriptionId != null) {
+            return ServiceUtil.returnSuccess("runSubscriptionExpired service called successfully with subscriptionId " + subscriptionId);
+        }
+        return result;
+    }
 }

Modified: ofbiz/trunk/applications/product/widget/catalog/SubscriptionForms.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/product/widget/catalog/SubscriptionForms.xml?rev=1597235&r1=1597234&r2=1597235&view=diff
==============================================================================
--- ofbiz/trunk/applications/product/widget/catalog/SubscriptionForms.xml (original)
+++ ofbiz/trunk/applications/product/widget/catalog/SubscriptionForms.xml Sat May 24 04:37:16 2014
@@ -296,6 +296,15 @@ under the License.
                 </entity-options>
             </drop-down>
         </field>
+        <field name="gracePeriodOnExpiryUomId" title="${uiLabelMap.ProductGracePeriodUomId}">
+            <drop-down allow-empty="true">
+                <entity-options entity-name="Uom" key-field-name="uomId" description="${description}">
+                    <entity-constraint name="uomTypeId" value="TIME_FREQ_MEASURE"/>
+                    <entity-order-by field-name="description"/>
+                </entity-options>
+            </drop-down>
+        </field>
+        <field name="serviceNameOnExpiry" ><ignored/></field>
         <field name="submitButton" title="${uiLabelMap.CommonUpdate}" widget-style="smallSubmit"><submit button-type="button"/></field>
     </form>
 

Modified: ofbiz/trunk/specialpurpose/ecommerce/data/DemoProduct.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/specialpurpose/ecommerce/data/DemoProduct.xml?rev=1597235&r1=1597234&r2=1597235&view=diff
==============================================================================
--- ofbiz/trunk/specialpurpose/ecommerce/data/DemoProduct.xml (original)
+++ ofbiz/trunk/specialpurpose/ecommerce/data/DemoProduct.xml Sat May 24 04:37:16 2014
@@ -617,8 +617,8 @@ under the License.
 
     <!-- test Subscription product, a Gizmo Newsletter -->
     <Product productId="GZ-NEWS-1MO" productTypeId="DIGITAL_GOOD" primaryProductCategoryId="101" productName="Gizmo Newsletter 1 Month" internalName="Gizmo Newsletter 1 Month Subscription" description="A 1 month subscription to the Gizmo Newsletter: can be used immediately after purchase." longDescription="This newsletter will give you regular updates on the wonderful world of Gizmos!" taxable="Y" chargeShipping="N" autoCreateKeywords="Y" isVirtual="N" isVariant="N" createdDate="2001-05-13 12:00:00.0" createdByUserLogin="admin" lastModifiedDate="2001-05-13 12:00:00.0" lastModifiedByUserLogin="admin"/>
-    <SubscriptionResource subscriptionResourceId="GZ-NEWS" description="Gizmo Newsletter"/>
-    <ProductSubscriptionResource productId="GZ-NEWS-1MO" subscriptionResourceId="GZ-NEWS" fromDate="2001-05-13 12:00:00.0" useTime="1" useTimeUomId="TF_mon"/>
+    <SubscriptionResource subscriptionResourceId="GZ-NEWS" description="Gizmo Newsletter" serviceNameOnExpiry="runSubscriptionExpired"/>
+    <ProductSubscriptionResource productId="GZ-NEWS-1MO" subscriptionResourceId="GZ-NEWS" fromDate="2001-05-13 12:00:00.0" useTime="1" useTimeUomId="TF_mon" gracePeriodOnExpiry="1" gracePeriodOnExpiryUomId="TF_mon"/>
     <ProductPrice productId="GZ-NEWS-1MO" productPricePurposeId="PURCHASE" productPriceTypeId="DEFAULT_PRICE" currencyUomId="USD" productStoreGroupId="_NA_" fromDate="2001-05-13 12:00:00.0" price="3.99" createdDate="2001-05-13 12:00:00.0" createdByUserLogin="admin" lastModifiedDate="2001-05-13 12:00:00.0" lastModifiedByUserLogin="admin"/>
     <ProductPrice productId="GZ-NEWS-1MO" productPricePurposeId="PURCHASE" productPriceTypeId="LIST_PRICE" currencyUomId="USD" productStoreGroupId="_NA_" fromDate="2001-05-13 12:00:00.0" price="5.0" createdDate="2001-05-13 12:00:00.0" createdByUserLogin="admin" lastModifiedDate="2001-05-13 12:00:00.0" lastModifiedByUserLogin="admin"/>
     <DataResource dataResourceTypeId="ELECTRONIC_TEXT" dataResourceId="GZ-NEWS-1MO-ALT" localeString="en"/>
@@ -628,8 +628,8 @@ under the License.
     <Content contentId="GZ-NEWS-1MO-ALT" contentTypeId="DOCUMENT" dataResourceId="GZ-NEWS-1MO-ALT" localeString="en"/>
     <Content contentId="CGZ-NEWS-1MO-ALTEN" contentTypeId="DOCUMENT" dataResourceId="DRGZ-NEWS-1MO-ALTEN" localeString="en_US"/>
     <ContentAssoc contentId="GZ-NEWS-1MO-ALT" contentIdTo="CGZ-NEWS-1MO-ALTEN" contentAssocTypeId="ALTERNATE_LOCALE" fromDate="2011-04-26 12:00:00.0"/>
-    <ProductContent productId="GZ-NEWS-1MO" contentId="GZ-NEWS-1MO-ALT" productContentTypeId="ALTERNATIVE_URL" fromDate="2001-05-13 12:00:00.0"/>
-
+    <ProductContent productId="GZ-NEWS-1MO" contentId="GZ-NEWS-1MO-ALT" productContentTypeId="ALTERNATIVE_URL" fromDate="2001-05-13 12:00:00.0"/>    
+  
     <!-- test Digital Download product -->
     <Product productId="GZ-DIG" productTypeId="DIGITAL_GOOD" primaryProductCategoryId="101" productName="Digital Gizmo" internalName="Digital Gizmo" description="A digital gizmo: can be downloaded immediately after purchase." longDescription="This gizmo is part of an exciting new breed that needs no corporeal form: it is all digital! Buy and download it now!" taxable="Y" chargeShipping="N" autoCreateKeywords="Y" isVirtual="N" isVariant="N" createdDate="2001-05-13 12:00:00.0" createdByUserLogin="admin" lastModifiedDate="2001-05-13 12:00:00.0" lastModifiedByUserLogin="admin"/>
     <DataResource dataResourceId="GZ-DIG" dataResourceTypeId="OFBIZ_FILE_BIN" mimeTypeId="image/gif" dataResourceName="Digital Gizmo Image" objectInfo="framework/images/webapp/images/ofbiz_logo.gif"/>