Creating Buy N Get M Free Promotions

parents for sale, buy one get one free | FlickrATG’s promotion engine is pretty advanced and relatively easy to use for creating promotions as described in their Creating Promotions section in the Commerce Programming Guide.

ATG’s documentation gives numerous examples of how to create buy N get M free promotions.

For example to create a simple Buy 2 Get 1 Free promotion one would create the following rule.

“For next 2 items whose product is in the category named Foo, discount up to 1 item whose product is in the category named Foo”

The only issue with the above rule is that you cannot set conditions on customers. If you need to set conditions on customers then you have create a rule like this.

“When order contains at least 3 items whose product is in the category named Foo and at most 5 items whose product is in the category named Foo and customer’s name is Frank, discount up to 1 item whose product is in the category named Foo”

Note you will have to create separate promotions for each level, i.e. one for the 3 for 1, one for the 6 for 2, one for the 9 for 3, etc.

Also if you change a promotion I believe you have to restart the ATG server otherwise at least the current customers will still be using the old promotion.

Viewing Expert-Level Information in the ACC

William T. Sherman | FlickrSome information, such as properties and item descriptors, are marked as expert (i.e. expert=true) so that they are not displayed in the ACC unless you are an expert user.

For example:

<property name="type" data-type="enumerated" expert="true"/>

To display expert-level information in the ACC go to Tools > Preferences and select the "Show expert-level information" box.

ATG ACC Preferences

Note that this is not a "sticky" preference, i.e. you have to set it every time you restart the ACC.

Cannot Create New ATG Scenario

Though I have a distaste for scenarios on occasion there will be a time when I want to create a new scenario. On such an occasion I still start up the ACC, go to Scenarios > Scenarios and discover I can’t create a scenario because the New Scenario box is grayed out.

The issue is often because the process editor server is configured incorrectly. ATG documents how to configure the process editor server.

For example today I found I could not create a new scenario. After putting this scenarioManager.xml in localconfig/atg/scenario and then restarting ATG I was able to create new scenarios again.

<?xml version="1.0" encoding="ISO-8859-1" ?>
<process-manager-configuration>
 <process-editor-server>
  <server-name xml-combine="replace">
   CRAPBOOK-PRO:8850
  </server-name>
 </process-editor-server>
</process-manager-configuration>

Note that the server name must be the same as the one found at /atg/dynamo/service/ServerName.serverName. For more information about the atg.service.ServerName class, see Referring to Dynamo Servers in the Nucleus: Using Nucleus section of the ATG Programming Guide.

Date and Timestamp Repository Data Types

Today I noticed that my timestamp property was being saved with only the date information, not the time information. That is when I queried the corresponding column I saw something like this.

> select TO_CHAR(timestamp, 'yyyy-mm-dd hh24:mi:ss') from foo;

2007-11-17 00:00:00

The timestamp property was represented in Java using a Date class. I thought of switching it to a Timestamp class but was dubious because the only real difference is that the Timestamp class stores fractional seconds.

Then I looked at /atg/commerce/order/orderrepository.xml and the corresponding OrderImpl.java and realized that it used the timestamp repository data type while I was using the date repository date type. Since the Oracle column data type was also timestamp I found when I switched the repository data type the time information was also saved.

> select TO_CHAR(timestamp, 'yyyy-mm-dd hh24:mi:ss') from foo;

2007-11-17 20:03:00 

The moral of the story is to use the timestamp repository data type, not the date repository data type.

ATG has more documentation about this though it is not entirely true, i.e. you can use the Date Java class type with the timestamp SQL type.

CommerceItem, which one is the SKU ID?

Colors | Flickr

Colors by CurveTo

The CommerceItem has two properties, catalogRefId and catalogId.

I always forget which one is which so I am writing this post so I can remember. I guess it was because some companies have different conventions about SKU’s and products.

ATG Real World
catalogRefId SKU ID
catalogId Product ID

Maybe one day someone will explain the reason for the naming.

Note that sometimes the catalogId is null though I’ve never seen the catalogRefId be null.

You can also try to get the product ID by using the auxiliaryData property. This property has a productId property.

ATG DUST

ATG has a new open source framework for doing integrated tests called ATG DUST. It sounds interesting though it’s still somewhat raw and I have not tested it myself.

Here is the description from the website.

ATG DUST is a framework for building JUnit tests for applications built on the ATG Dynamo platform. This framework allows one to quickly write test code that depends up Nucleus or ATG Repositories. By using this framework one can drastically cut down on development time. It takes only a few seconds to start up a test with a repository, but it may take multiple minutes to start up an application server.

You can see the source and download the source tarball.

Submitting an ATG Form Using a Text Link

It has always bedeviled me how to submit an ATG form using a text link. I have asked many people about this but no one seems to know. Finally with the help of ATG support I figured it out.

Below is a simple example of how to do it. The trick is the hidden submit input which triggers ATG to call the handleSubmit method of the form handler.

<%@ taglib uri="/dspTaglib" prefix="dsp"%>

<dsp:page>

<script>
  function submit(form) {
    form.submit();
  }
</script>

<dsp:form action="<%=request.getRequestURI()%>" method="post" name="testForm">

  Foo: <dsp:input type="text" bean="TestFormHandler.foo"/>

  <p><a href="javascript:submit(document.testForm)">Submit</a>

  <dsp:input type="hidden" bean="TestFormHandler.submit" value="Submit"/>

</dsp:form>

</dsp:page>

You can also invoke the JavaScript like this:

  <p><a href="#" onclick="submit(document.testForm); return false;">Submit</a>

Updating ATG after installing new Java SDK

Recently I upgraded my Java SDK from 1.5.0_08 to 1.5.0_11 to resolve a long path name issue for compiled classes. After doing this I updated the following ATG configuration files to reflect this change.

  1. home/installconfig/config.xml
  2. home/installconfig/configXMLproperties.txt
  3. home/localconfig/dasEnv.bat
  4. uninstall/.ASE2006.3_uninstall/installvariables.properties

ATG Repository User-Defined Property Types

ATG allows one to use your own Java class data types for the properties of repository items. This is called User-Defined Property Types.

While working with these user-defined property types I learned a few things that are not explicitly documented.

  1. The source for atg.repository.FilePropertyDescriptor is supplied in the installation and it’s a good example for creating your own user-defined property type.
  2. To set values of properties in your Java class data type do two things.
    1. Configure the values of these properties using the attribute tag. For example:

      <property name="contentFile" property-type="atg.repository.FilePropertyDescriptor">
      <attribute name="pathNameProperty" value="contentFileName"/>
      </property>
    2. Access these attributes and set the corresponding properties in the setValue() method. Continuing with the above example:

      public void setValue(String pAttributeName, Object pValue) {
      super.setValue(pAttributeName, pValue);

      if (pValue == null || pAttributeName == null) return;
      if (pAttributeName.equalsIgnoreCase(PATH_NAME_PROPERTY))
      mPathNameProperty = pValue.toString();
      if (pAttributeName.equalsIgnoreCase(PATH_PREFIX))
      mPathPrefix = pValue.toString();
      }
  3. ATG’s documentation suggests it is not necessary to define the data-type attribute if you specify the type with the property-type attribute. However I found that sometimes ATG gets confused and that it is best practice to always set the data-type. So using the above example one would do this.

    <property name="contentFile" data-type="string" property-type="atg.repository.FilePropertyDescriptor">
    <attribute name="pathNameProperty" value="contentFileName"/>
    </property>
  4. Only one instance of your user-defined property type is ever instantiated. Even if your user-defined property type is used for multiple properties it will still only be instantiated once.
  5. The JavaDoc for ATG’s RepositoryPropertyDescriptor class, which one typically extends to create ones own user-defined property type, documents the getPropertyValue method like this: public java.lang.Object getPropertyValue(RepositoryItemImpl pItem, java.lang.Object pValue)

    The first argument is the instance of the repository item whose property you are defining.

    The second argument would be better named pCachedPropertyValue because it is the cached property value. If it is null then that means there is no cached value. If it is not null that means that in a previous call setPropertyValueInCache(this, yourvalue) was called. Unfortunately this cached value is not that useful because since only one instance of your user-defined property type is ever created, the cached value will be a global value.

    This cached value is cleared when invalidateCaches is called on the repository item.

    Here is what ATG support said about caching with User-Defined Property Types.

    I think user defined properties will not be cached so that will cause your code to be
    called several times. If you require some caching you would properly have to
    implement it in the code of the user-defined property.

    As a user defined property could return data from any external source that might get
    updated independently of the ATG application we can not really make a decision on
    what should be cached and what should be retrieved anew every single time.

    Kind regards,
    Olaf Doemer

  6. The property descriptor implements the Serializable interface so the developer must ensure that it is indeed serializable, i.e. all its members are serializable.

Forwarding instead of Redirecting in Form Handlers

Typically how an ATG form handler handles form submissions is that if the form submission has any errors it redirects to an error or failure URL, otherwise it redirects to the success URL. It does this when you call the form handler’s method checkFormRedirect.

However by redirecting you are creating a new request and you will lose all the information in the current request. If you want to keep them you can do this by forward instead of redirecting.

To specify that a form handler use forward instead of redirect you can either

  1. set the request parameter “atg.formHandlerUseForwards” to “true” (case insensitive) OR
  2. set the boolean property useForwards to true, either in the properties file of the form handler or using a hidden input in the JSP/JHTML form

None of this as far as I can tell is documented, probably because instead of using forwards ATG would probably prefer developers make a form handler session scoped if information needs to be preserved over multiple requests. Regardless this is all documented in the source code for atg.droplet.GenericFormHandler which is available in the ATG install location under DAS/src/Java/.

Note that if you call the response’s method sendLocalRedirect you bypass the flexibility of possibly using forwards. Instead it is preferable to call the form handler’s method redirectOrForward.