Monday, November 18, 2013

Bing Integration with Jquery

Bing provides an interesting to alternative to Google maps for integration and if you want to integrate using Jquery. Please copy paste the example in a .html file and test it with a zip-code or city.

The key that I have in the example below will expire in 90 days so please use the below link to create a new key:
http://www.microsoft.com/maps/

Copy the below section after this line in a .html  file:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Use Bing Maps REST Services with jQuery to build an autocomplete box and find a location dynamically</title>
    <script src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.5.1.js" type="text/javascript"></script>
    <script src="http://ajax.aspnetcdn.com/ajax/jquery.ui/1.8.10/jquery-ui.js" type="text/javascript"></script>
    <link href="http://ajax.aspnetcdn.com/ajax/jquery.ui/1.8.10/themes/redmond/jquery-ui.css" rel="stylesheet" type="text/css" />
    <style type="text/css">
        .ui-autocomplete-loading
        {
            background: white url('images/ui-anim_basic_16x16.gif') right center no-repeat;
        }
        #searchBox
        {
            width: 25em;
        }
    </style>

    <script type="text/javascript">
        $(document).ready(function () {
            $("#searchBox").autocomplete({
                source: function (request, response) {
                    $.ajax({
                        url: "http://dev.virtualearth.net/REST/v1/Locations",
                        dataType: "jsonp",
                        data: {
                            key: "AlJKmxkiJg2u0CIDEyaTM6CWC9jQ_q1pf4_xzxPdEJoaT_KsgKRy73ksHyl24oe5",
                            q: request.term
                        },
                        jsonp: "jsonp",
                        success: function (data) {
                            var result = data.resourceSets[0];
                            if (result) {
                                if (result.estimatedTotal > 0) {
                                    response($.map(result.resources, function (item) {
                                        return {
                                            data: item,
                                            label: item.name + ' (' + item.address.countryRegion + ')',
                                            value: item.name
                                        }
                                    }));
                                }
                            }
                        }
                    });
                },
                minLength: 1,
                change: function (event, ui) {
                    if (!ui.item)
                        $("#searchBox").val('');
                },
                select: function (event, ui) {
                    displaySelectedItem(ui.item.data);
                }
            });
        });

        function displaySelectedItem(item) {
            $("#searchResult").empty().append('Result: ' + item.name).append(' (Latitude: ' + item.point.coordinates[0] + ' Longitude: ' + item.point.coordinates[1] + ')');
        }
    </script>
</head>
<body>
    <div>
        <div class="ui-widget">
            <label for="searchBox">
                Search:
            </label>
            <input id="searchBox" />
        </div>
        <div id="searchResult" class="ui-widget" style="margin-top: 1em;">
        </div>
    </div>
</body>
</html>

SQL | Joins Explained | Using Commerce tables.

This example below outlines lot of SQL Join functionality that we use in a lot of queries in our codes. The below example use commerce tables to demonstrate the joins functionality.

--EXAMPLE 2 OUTER JOIN Table XORDERITEMS:
CREATE TABLE XCATENTRY (catentry_id NUMBER,partnumber varchar2(255), lastupdate timestamp);

--2. Data insertion into table
insert into  XCATENTRY values (11,'DSC_1',sysdate-800);
insert into  XCATENTRY values (33,'DSC_3',sysdate-700);
insert into  XCATENTRY values (1010,'DSC_10',sysdate-500);
insert into  XCATENTRY values (1111,'DSC_11',sysdate-300);

output::
Tables Orders:
ORDERS_ID    PARTNUMBER    STATUS
-----------------------------------------
1        DSC_1    P
2        DSC_2    Y
3        DSC_3    c
4        DSC_4    c
5        DSC_5    c
6        DSC_4    P
7        DSC_3    y
8        DSC_7    c
9        DSC_8    P
10        DSC_9    P

Tables Catentry:
CATENTRY_ID   PARTNUMBER  LASTUPDATE
--------------------------------------------
11        DSC_1    29-08-2011 13:53:21.000000
33        DSC_3    07-12-2011 13:53:21.000000
1010        DSC_10    24-06-2012 13:53:21.000000
1111        DSC_11    10-01-2013 13:53:21.000000

Query: For fetching all partnumber from left table along with matching partnumber */

--Query 1
select o.partnumber from XORDERITEMS o left outer join xcatentry c on o.partnumber = c.partnumber;

--Query 2
select o.partnumber from XORDERITEMS o , xcatentry c where o.partnumber = c.partnumber(+);

output:
PARTNUMBER
DSC_1
DSC_3
DSC_3
DSC_4
DSC_4
DSC_8
DSC_9
DSC_7
DSC_5
DSC_2

--Query: For fetching all partnumber from right table along with matching partnumber

--QUERY
select distinct c.* from XORDERITEMS o right outer join xcatentry c on o.partnumber = c.partnumber;

--ALTERNATE QUERY:
select distinct c.* from XORDERITEMS o , xcatentry c where o.partnumber(+) = c.partnumber;

Result:
CATENTRY_ID    PARTNUMBER    LASTUPDATE
11    DSC_1    29-08-2011 13:53:21.000000
33    DSC_3    07-12-2011 13:53:21.000000
1010    DSC_10    24-06-2012 13:53:21.000000
1111    DSC_11    10-01-2013 13:53:21.000000

-- Query: For fetching all partnumber from left and right table along with matching partnumber

select o.*,c.* from XORDERITEMS o full outer  join xcatentry c on o.partnumber = c.partnumber;


ORDERS_ID    PARTNUMBER    STATUS    CATENTRY_ID    PARTNUMBER_1    LASTUPDATE
1    DSC_1    P    11    DSC_1    29-08-2011 13:53:21.000000
7    DSC_3    y    33    DSC_3    07-12-2011 13:53:21.000000
3    DSC_3    c    33    DSC_3    07-12-2011 13:53:21.000000
6    DSC_4    P
4    DSC_4    c
9    DSC_8    P
10    DSC_9    P
8    DSC_7    c
5    DSC_5    c
2    DSC_2    Y
            1111    DSC_11    10-01-2013 13:53:21.000000
            1010    DSC_10    24-06-2012 13:53:21.000000


--EXAMPLE 3 Query with date range
--Query 1: select partnumber of year 2011
select partnumber from xcatentry where to_date(to_char ( lastupdate,'DD/MM/YYYY'),'DD/MM/YYYY') BETWEEN TO_DATE('01/01/2011','DD/MM/YYYY') AND TO_DATE('31/12/2011','DD/MM/YYYY')

output:
PARTNUMBER
DSC_1
DSC_3

--Query 2: select partnumber of year 2011 and 2012 but not in orders tables
select partnumber from xcatentry c where to_date(to_char ( c.lastupdate,'DD/MM/YYYY'),'DD/MM/YYYY') BETWEEN TO_DATE('01/01/2011','DD/MM/YYYY') AND TO_DATE('31/12/2012','DD/MM/YYYY')
and not exists  (select 1 from XORDERITEMS o where o.partnumber = c.partnumber)

RESULTS:
PARTNUMBER
DSC_10

--EXAMPLE 4  Query : Count the status
select status,count(*) from orders group by status order by status;

output:
STATUS    COUNT(*)
P          4
Y          1
c          4
y          1

Tables XORDERITEMS:
ORDERS_ID         PARTNUMBER   STATUS
-----------------------------------------
1                              DSC_1   P
2                              DSC_2   Y
3                              DSC_3   c
4                              DSC_4   c
5                              DSC_5   c
6                              DSC_4   P
7                              DSC_3   y
8                              DSC_7   c
9                              DSC_8   P
10                           DSC_9   P

Tables XCatentry:
CATENTRY_ID   PARTNUMBER  LASTUPDATE
--------------------------------------------
11                           DSC_1   29-08-2011 13:53:21.000000
33                           DSC_3   07-12-2011 13:53:21.000000
1010                       DSC_10 24-06-2012 13:53:21.000000
1111                       DSC_11 10-01-2013 13:53:21.000000

Tables Xcatentdesc:
CATENTRY_ID    LANGUAGE_ID SHORTDESCRIPTION
11           -1            DSC_1_DESCRIPTION
33           -1            DSC_3_DESCRIPTION
1010       -1            DSC_10_DESCRIPTION
1111       -1            DSC_11_DESCRIPTION

Query 1: Example: inner join - two table
select o.orders_id, o.partnumber,c.lastupdate from Xorders o inner join Xcatentry c on o.partnumber= c.partnumber;
o/p:
ORDERS_ID         PARTNUMBER   LASTUPDATE
1              DSC_1   8/29/2011 1:53:21.000000 PM
3              DSC_3   12/7/2011 1:53:21.000000 PM
7              DSC_3   12/7/2011 1:53:21.000000 PM

Query 2: Example: inner join - three table
select o.orders_id, o.partnumber,cd.shortdescription from Xorders o inner join Xcatentry c on o.partnumber= c.partnumber inner join Xcatentdesc cd on c.catentry_id
= cd.catentry_id;

output:
ORDERS_ID         PARTNUMBER   SHORTDESCRIPTION
1              DSC_1   DSC_1_DESCRIPTION
3              DSC_3   DSC_3_DESCRIPTION
7              DSC_3   DSC_3_DESCRIPTION


Friday, November 8, 2013

SQL | Analysis and Query to find items that are not part of input list

I will have a few SQL series blogs and as a WCS developer, I feel it is very important to have good SQL skills.

--Exampple . Find all part numbers that are not found in orders table from the list (DSC_1,DSC_3, DSC_10, DSC_11)  in the XORDERITEMS table below.
Result: DSC_10, DSC_11

--1. Table creation:

CREATE TABLE XORDERITEMS (orders_id NUMBER,partnumber varchar2(255), status varchar2(1)) NOLOGGING;

--2. Data insertion into table
insert into /*+ APPEND */  XORDERITEMS values (1,'DSC_1','P');
insert into /*+ APPEND */  XORDERITEMS values (2,'DSC_2','Y');
insert into /*+ APPEND */  XORDERITEMS values (3,'DSC_3','c');
insert into /*+ APPEND */  XORDERITEMS values (4,'DSC_4','c');
insert into /*+ APPEND */  XORDERITEMS values (5,'DSC_5','c');
insert into /*+ APPEND */  XORDERITEMS values (6,'DSC_4','P');
insert into /*+ APPEND */  XORDERITEMS values (7,'DSC_3','y');
insert into /*+ APPEND */  XORDERITEMS values (8,'DSC_7','c');
insert into /*+ APPEND */  XORDERITEMS values (9,'DSC_8','P');
insert into /*+ APPEND */  XORDERITEMS values (10,'DSC_9','P');

--two alternates to fetch the above result.
--Query-1
select pno partnumber from
(SELECT TRIM(SUBSTR ( partnumber , INSTR (partnumber, ',', 1, level ) + 1 , INSTR (partnumber, ',', 1, level+1 ) - INSTR (partnumber, ',', 1, level) -1)) pno
FROM ( SELECT ','||'DSC_1,DSC_3,DSC_10,DSC_11'||',' AS partnumber FROM dual )
CONNECT BY level <= LENGTH(partnumber)-LENGTH(REPLACE(partnumber,',',''))-1 )
where pno not in (select partnumber from XORDERITEMS);

--Alternate Query
select pno partnumber from
(select 'DSC_1' pno from dual
union
select 'DSC_3' pno from dual
union
select 'DSC_10' pno from dual
union
select 'DSC_11' pno from dual)
where pno not in (select partnumber from XORDERITEMS);


Thursday, October 10, 2013

Tax customizations to update tax from third party !!

While customizing tax integration with third party, it is important to understand the end points provided by commerce to customize.
Please find below out of box commerce tables and commands that needs to be customized.


INSERT INTO CALMETHOD (CALMETHOD_ID, STOREENT_ID, CALUSAGE_ID, TASKNAME, 
DESCRIPTION, SUBCLASS, NAME) VALUES ((select coalesce((min(calmethod_id)-1),1)
from calmethod), 10701 , -3,'com.custom.commerce.order.calculation.ApplyCalculationUsageCmd', 'custommethod for calculation sales taxes', 12, 'ApplyCalculationUsageSalesTax')

update stencalusg SET CALMETHOD_ID_APP = 
(SELECT CALMETHOD_ID FROM CALMETHOD WHERE TASKNAME = 'com. custom .commerce.order.calculation.ApplyCalculationUsageCmd'
AND STOREENT_ID = 10701 AND CALUSAGE_ID = -3) and calusage_id=-3

insert into CMDREG (STOREENT_ID, INTERFACENAME, DESCRIPTION, CLASSNAME, PROPERTIES, LASTUPDATE, TARGET) values 
(0,'com.custom.commerce.order.calculation.ApplyCalculationUsageCmd','Sales Tax calculation usage for third-party tax 
providers','com.custom.commerce.order.calculation.ApplyCalculationUsageSalesTaxCmdImpl',null,null,'Local')

//out of the box command required to be extended.
public class ApplyCalculationUsageSalesTaxCmdImpl extends ApplyCalculationUsageCmdImpl implements com.custom.commerce.order.calculation.ApplyCalculationUsageCmd{

    public static final String CLASSNAME = ApplyCalculationUsageSalesTaxCmdImpl.class.getName();

    private static Logger LOGGER = Logger.getLogger(CLASSNAME);

   public void performExecute()

    throws ECException
   {
       String methodName = "performExecute";
       if (LoggingHelper.isEntryExitTraceEnabled(LOGGER)) {
           LOGGER.entering(CLASSNAME, methodName);
       }
    
     //get tax from Third party
       BigDecimal orderTax = new BigDecimal(100.00);
       
       Item[] items = super.getItems();
       // set on first order item
       for (int i = 0; i < 1; i++) {
            items[i].setSalesTaxTotal(orderTax);
            items[i].commit();
       }
       if (LoggingHelper.isEntryExitTraceEnabled(LOGGER)) {
           LOGGER.exiting(CLASSNAME, methodName);
       }
   }   


Wednesday, October 9, 2013

Creating Assets in management center and displaying in front end.


Steps in CMC:
1. Go to Assets menu and select the appropriate stores.
2. Create a file, make sure to give a exten (.pdf, jpg), even in the name.
3. Go to Attachment selection inside Asset Menu, Create a attachment, the name given in attachment, can be used in the front end for links.
4. Go to Catalog menu and select a store and create a category inside master catalog categories and reference the attachments created in step 3.

Extended CategoryDataBean:
public void populate() throws Exception{
Enumeration categoriesEnum=CatalogGroupCache.findByIdentifierAndStore(this.getCatIdentifier(),this.getCommandContext().getStoreId());
if (categoriesEnum.hasMoreElements()) {
CachedCatalogGroupAccessBean cgpCached= (CachedCatalogGroupAccessBean) categoriesEnum.nextElement();
catGroupId=cgpCached.getCatalogGroupReferenceNumber();
}
super.setCategoryId(catGroupId);
super.populate(); }

JSP Code:

<wcbase:useBean id="documentsCategory" classname="com.custom.commerce.catalog.beans.EXTCategoryDataBean" >
<c:set property="catIdentifier" value="documents" target="${documentsCategory}" />
</wcbase:useBean>

<c:set var="allAttachemnts" value="${mediaDownloadsCategory.allAttachments}" />
<c:if test="${!empty allAttachemnts}">
<c:forEach items="${allAttachemnts}" var="attachmentDB" varStatus="status">
<c:forEach items="${attachmentDB.attachmentAssets}" var="attachmentAssets" varStatus="status">
<c:if test="${fn:indexOf(attachmentAssets.attachmentAssetPath,'PDF_') > 0 }">
<a href="${hostPath}${attachmentAssets.objectPath}${attachmentAssets.attachmentAssetPath}"><c:out value="${attachmentDB.fileName}"/></a>
</c:if>
<c:if test="${fn:indexOf(attachmentAssets.attachmentAssetPath,'DOCUMENT_') > 0 }">
<div class="image"><img border="0" src="${hostPath}${attachmentAssets.objectPath}${attachmentAssets.attachmentAssetPath}"/></div>
</c:if>
</c:forEach>
</br>
</c:forEach>
</c:if>


Content Assets upload settings in wc-server.xml: Can change the number of files and the interval.

<ManagedFileUpdateEARConfiguration display="false">
        <ContentManagedFileEARUpdate Implementation="com.ibm.commerce.filepublish.util.ContentManagedFileEARUpdateImpl"/>
        <ContentManagedFileHandler Implementation="com.ibm.commerce.filepublish.util.ContentManagedFileHandlerImpl"/>
        <ProductionServerInformation applicationName="WC_demo"/>
        <ModuleInformation moduleName="Stores.war"/>
        <EvaluationCriteria minNumOfFilesForUpdate="4" minSecFromLastUpload="300"/>
    </ManagedFileUpdateEARConfiguration>


CoreMetrics | basics !!

Coremetrics provides web analytics and the other big competitor in this space is Omniture. The analytics integration is provided using Javascript. In Websphere commerce there is a tighter integration in the form of tags that are provided out of the box and  coremetrics reports are provided in management center in newer versions of WCS and older versions still need to use coremetrics site to view reports.

  1. Create a coremetrics account.
  2. Update WC\xml\config\bi\biConfig.xml with correct configuration for coremetrics configuration. clientId is the contract ID is usually one for test environments and 1 for production and ssoKey is generated from Coremetrics support.
  3. Configuration in wc-server.xml 
    • <configuration
                    cmClientID=""
                    password=""
                    serviceURL="https://wscreceiver.coremetrics.com/Receiver/sendEventData"
                    sslKeyPassphrase="WebAS"
                    sslKeyStore="/usr/WebSphere/AppServer/profiles/demo/etc/DummyServerKeyFile.jks"
                    sslTrustPassphrase="WebAS"
                    sslTrustStore="/usr/WebSphere/AppServer/profiles/demo/etc/DummyServerTrustFile.jks"
                    transmitClassName="com.ibm.commerce.bi.events.transmit.CMWebServiceTransmitter"
                    transmitEnabled="false" username=""/&gt
  4. Access Coremetrics reports
    • Open a Web browser to the following URL: https://welcome.coremetrics.com
    • Log in using your Client ID, username, and password. 
    • Starting V7, Feature Pack 3. We can also view reports directly from Management Center using links in the Catalogs, Marketing and Promotions tool.
    There are several tags for CoreMetrics, one example is pageView and most commonly used is cm

    Include the tag library in the pages
    <%@ taglib uri="http://commerce.ibm.com/coremetrics"  prefix="cm" %>

    PageView Tag: Most commonly used tag to track pages.

        <flow:ifEnabled feature="Analytics">
            <c:choose>
                <c:when test="${isCSR}">
                    <cm:pageview pagename="${storeCountryCode}:SEARCH PAGE" extraparms="YES" />
                </c:when>
                <c:otherwise>
                    <cm:pageview pagename="${storeCountryCode}:
    SEARCH PAGE" extraparms="NO" />
                </c:otherwise>
            </c:choose>
        </flow:ifEnabled>


    Conversion tag: For Ajax calls

    <cm:conversion eventId="${WCParam.eventId}" category="WISHLIST" actionType="2" points="10" returnAsJSON="true"/>

    campurl Tag: Mostly for static pages

                                  <cm:campurl espotData="${marketingSpotDatas}" id="clickInfoCommand" url="${clickInfoURLForAnalytics}" 

    References: More tags
    http://pic.dhe.ibm.com/infocenter/wchelp/v7r0m0/topic/com.ibm.commerce.Coremetrics.doc/refs/rmttagsinstorejsps.htm
    Web 2.0 integration:
    http://pic.dhe.ibm.com/infocenter/wchelp/v7r0m0/topic/com.ibm.commerce.Coremetrics.doc/concepts/cmtweb20intro.htm



    Wednesday, July 31, 2013

    Keys table EJB | script to correct counter values

    When you do migration from one version of commerce to the next or any other scenarios or due to data load scenarios some times the keys table counters are messed and causes lot of errors in application server logs when using those corresponding EJB's.

    A lot of discretion is warranted when doing stuff to the keys table e.g. counter should be greater than the lower bound and lesser than the upper bound
    You can generate the SQL statements from the script below and run them against the corresponding commerce schema where the issue was found.

    SQL Script to see which tables might need to be fixed:

    select 'select max(' || columnname||')+1 - (select counter from keys where tablename = '''|| tablename||'''), '''|| tablename || ''' from ' || tablename from keys

     SQL script to do the actual updates:
    select concat(concat(concat('update keys set counter=',concat(concat(
    concat(concat('(select max(',columnname),')+1 from ' ),tablename),') where tablename=''')),tablename),''' and counter <= '|| '(select max('||columnname||') from '|| tablename||')' ||';') from keys

    Monday, July 29, 2013

    Abstract Task command multi store esites model

    This is my 100th blog and am happy to share my leanings. Please do leave a comment, if you have any ideas or questions or you like reading any specific areas.
     It is very important to understand the classic java abstract classes and methods and it is very useful when implementing task commands in multiple e-site scenario's, specially for tax/shipping/environment fee and other country specific, site specific entities.

    Command Reg entries:  Making sure common command is registered to store 0 so all stores can pick up that command and store specific customization, can be implemented and registered for specific store e.g. 10151 below.

                interfacename="com.custom.commerce.order.commands.CustomCalculateTaxTaskCmd"
            classname="com.custom.commerce.order.commands.CustomUSCalculateTaxTaskCmdTaskCmdImpl"
            description="US store implementation of tax command" />
       
                interfacename="com.custom.commerce.order.commands.CustomCalculateTaxTaskCmd"
            classname="com.custom.commerce.order.commands.CustomNonUSCalculateTaxTaskCmdTaskCmdImpl"
            description="Non US store implementation of tax command" />



    Creating abstract Task command with abstract methods:

    public interface CustomCalculateTaxTaskCmd extends TaskCommand {
        public void seTaxId(String taxId);
    }
    public abstract class CustomCalculateTaxTaskCmdTaskCmdImplextends TaskCommandImpl
            implements CustomCalculateTaxTaskCmd{
                    protected String taxId;
                    public void setTaxId(taxId) {
                           this.taxId=taxId;
                    }
                    public void performExecute() throws ECException {
                                super.performExecute();
                                initialize();
                                customFilter();
                    }
                   private void initialize() throws Exception {
                    //Implement common initialization of access beans and any common params
                   }
                   protected void  filter1() {
                         //custom filter
                   }
                   protected void  filter2() {
                         //custom filter code
                   }
    .....
                   protected void  filter5() {
                         //custom filter code
                   }
                  protected abstract void customFilter() throws Exception;
                  
    }
    //Custom implementation extending abstract command where the abstract method customFilter() is implemented and filter1 and filter2 methods are implemented in the abstract command
    public CustomUSCalculateTaxTaskCmdTaskCmdImpl extends CustomCalculateTaxTaskCmdTaskCmdImplextends {
                    public void performExecute() throws ECException {
                               super.performExecute();
                    }
                    protected void customFilter() throws Exception {
                               filter1();
                               filter2();
                    }
    }
    //Custom implementation extending abstract command, where the abstract method customFilter() is implemented and filter1() and filter2() methods are implemented in the abstract command
    public CustomNonUSCalculateTaxTaskCmdTaskCmdImpl extends CustomCalculateTaxTaskCmdTaskCmdImpl {
                    public void performExecute() throws ECException {
                               super.performExecute();
                    }
                    protected void customFilter() throws Exception {
                               filter1();
                               filter5();
                    }
    }

    Saturday, July 20, 2013

    ATP inventory concepts | Order Management System!!


    This blog does not give any coding example but folks working on WCS implementations, who work on Inventory and ATP and It is one of the most important concepts of an eCommerce site. Managing supply and demand and making sure the inventory is correctly updated on order placement is very important for running any business.

    Important Inventory and ATP terminology and concepts: These are high level concepts and I am sure back-end systems such as SAP, Oracle, Sterling order management systems use these terms and each one of them could have slight variants.

    • Supply is all the inventory available.
    • Demand is all the orders placed and not yet fulfilled.--Reservations/Allocated Orders/Future expected demand.
    • Supply Type or Inventory buckets, this is gives a clear picture of Inventory availability and it could be moved between multiple buckets such (QA hold or Intransit or) . This is very important in calculating available inventory for consumption.
    • Demand Type is the group of quantities committed across various commitment channels.
    • Demand Supply Matching: This is also called available to sell calculation, Supply with a sufficient lifespan     along with demand.
    • Simple example of Available to Sell calculation
        • (Sum of All checked supply types) – (sum of all corresponding demand type)
        • Available To Sell = (Onhand + Intransit +  Qty in PO) – (Open Order + Reserved + Allocated)
    • Availability on hand, back order, preorder (Future Inventory).
    • Managing inventory at the plant level or distribution level.
    • Available-to-promise (ATP) enables to decide the availability of a item for current and future demand. (simplest version is supply-demand is ATP)
    • Based on threshold and Inventory availability -- Availability high\low\future inventory.
    • Inventory sourcing at the plant level or pool level.
    • Order fulfillment process, there are multiple demand types -Open/Reserved/Scheduled/Allocated.
    • ATP Monitor versus Inventory Monitor.
    • ATP Monitoring  modes:
      • Activity Based: Raises an event in real time every time an item goes      above or below one of the thresholds.
      • Quick Sync: Re-sends the most recently published inventory availability information.
      • Full Sync: Monitors all of the items regardless of activity and publishes the inventory information for all of the items.


    ResolveParameter class, an Important commerce tool !!

    ResolveParameter is a very important class when implementing custom code in commerce and I am sure, it is a very important tool for commerce developers doing custom implementations. When a custom implementation requires to post multiple name/value pairs of the same type, this class is really useful.
    e.g. URL params
    catentryId_12343=value1,catentryId_22343=value2,catentryId_32343=value3
    orderItemStatus_10000=value10001,orderitemsStatus_10004=value

    //resolve params in request properties, returns the hashtable of name, value pairs.
    public void setRequestProperties(TypedProperty reqProperties) throws ECException {
    ihshCatentry_id  = ResolveParameter.resolveValues(OrderConstants.EC_CATENTRY_ID,  requestProperties, false);
    }


    Couple of other methods from ResolveParameter that I find useful from time to time.

    /*
    * returns the order item status from the hash table using the resolve parameter method.
    */
    protected String getOrderItemStatus(Integer i, Hashtable hashOrderItemStatus )
      throws InvalidParameterValueException
    {
    String methodName = "getOrderItemStatus";
      String orderItemStatus;
      try
      {
      orderItemStatus = ResolveParameter.getString(i, hashOrderItemStatus);
      } catch (NumberFormatException ex) {
        throw new InvalidParameterValueException("orderItemId",
          ex.getMessage());
      }
      return orderItemStatus;
    }    
    /*
    * returns the order item item id from the hash table using the resolve parameter method.
    */
    protected Long getOrderItemId(Integer i, Hashtable hashOrderitemId )
      throws InvalidParameterValueException
    {

      Long orderItemId;
      try
      {
        orderItemId = ResolveParameter.getLong(i, hashOrderitemId);
      } catch (NumberFormatException ex) {
        throw new InvalidParameterValueException("orderItemId",
          ex.getMessage());
      }
      return orderItemId;
    }  

    Thursday, June 6, 2013

    Sales Catalog ---SOLR

    I received a question regarding SOLR setup for sales catalog. This blog provides a quick write up on the question and the tools provided out of the box. SOLR is totally based on master catalog and locale (en_US, fr_CA..etc) and it does not need any configuration for different sales catalogs for a single store or multiple stores.
    Solr document is created during indexing and it's configured in wc-data-config.xml, there's a bunch of sqls in there and schema.xml defines all the fields in the 'document'. All the store relationships are updated as a part of those out of the box SQL's to the document.

    There are a few utilities provided out of the box. setupSearchIndex takes master catalogId and languageId
    that sets up the WCDE_ENT70/search/pre-processConfig  and WCDE_ENT70/search/solr/
    when running on the server, you'll probably want to use the "-remote true" option.

    Once the index is setup, the next step is to  run preprocess, use di-preprocess and to re-index, use di-buildindex

    PreProcessing step is to flatten data and buildIndex is used to building the search index.

    Explanation of document from InfoCenter:
    Solr maintains one or more indexes, which are searchable collections of items called documents. When using Solr to support catalog search, the documents in the index represent catalog entries.
    Adding a document to an index is often referred to as indexing the document.
    Each document is composed of a set of attributes called fields. For example, a catalog entry document can have fields such as Partnumber, Name, and Description.
    When a document is added to an index, each field in the document can be indexed or stored, or indexed and stored. An indexed field is one that can be used for searching, sorting, and faceting. If the field is indexed, document IDs can be determined from field values. A stored field is one where its value can be retrieved by a search. Alternatively, if the field is stored, field values can be determined from document IDs.

    Reference: definition of document:
    http://pic.dhe.ibm.com/infocenter/wchelp/v7r0m0/topic/com.ibm.commerce.developer.doc/concepts/csdsearchindex.htm

    Saturday, April 20, 2013

    ByPassHttpAdapter | XML post to commerce URL

    This is a very useful extension when you need to build an extension where if SOAP is not accepted and authToken based authentication is not an option for inbound request.
    if you want an external site to post XML into commerce. This would be a good approach.

    Sample code and wc-server.xml

    public class BypassHttpProgramAdapterImpl extends HttpProgramAdapterImpl implements
    HttpAdapter, HttpAdapterFactory {

    /*override the method to return a null if bypass condition is true, you can add bypass condition as a custom XML element in wc-server.xml*/
    public HttpAdapter createAdapter(HttpServletRequest request, HttpServletResponse  response, TypedProperty param ) {
        //check for extension code.
    }


    }

    <HttpAdapter
          deviceFormatId="-10000"
          deviceFormatType="XmlHttp"
          deviceFormatTypeId="-10000"
          enabled="true"
          factoryClassname="com.custom.programadapter.XXBypassHttpProgramAdapterImpl" name="XML/HTTP">
          <ProgramAdapter>
            <SessionContext ....
              <SessionContextConfig/>
            </SessionContext>
            <Configuration
              supportedCharacterEncoding="ISO8859-1, UTF-8, utf-8"
              supportedContentTypes="text/xml, text/xml; charset=UTF-8, text/xml-SOAP, text/xml; charset=utf-8"
              supportedMessageMappers="WCS.SOAPINTEGRATION, WCS.INTEGRATION" supportedMethods="POST, M-POST"/>
          </ProgramAdapter>
        </HttpAdapter>

    Thursday, April 11, 2013

    Pros and Cons of Ajax

    Ajax Simplified: I have been meaning to put this together, even for my own understanding and pros and cons and the SEO impact of using Ajax.

    Pros:
    1. Ajax does a great job of not fully loading the page, so refresh smaller areas/faster
    2. A lot of fancy things auto complete\Edit in place\ratings\RSS reader and also sorts of gimmicks.
    3. On refresh, it does not trigger a HTTP Post as opposed to traditional web apps.
    4. Simpler actions makes the over all site faster, things like e.g. Facebook like

    Cons:
    1. SEO suffers as most crawlers are links based and have limited JS support so if you use fewer URL's to do a lot of actions, this is bad for SEO visibility of the site.
    2. User experience, can not navigate to the path on refresh, so this could be bad user experience, if the Ajax is heavily used and you navigate to 3-4 levels and if you need to go back or forward.

    Good Example of Ajax implemented site with SEO friendly URLs:  https://github.com/
    As you would see here with github, even though it is Ajax, the links are changing which is kind of best of both worlds: https://github.com/karmi/tire
    Limitation of the above approach: Only HTML5 supports this feature--History supported browsers (most modern browsers). Push the URL to the browsers and ajax takes care of all the handling.You need to implement If\Else for older browsers and fortunately there is an API available, please find below link

    https://github.com/browserstate/history.js

    Top 10 Ajax applications in commerce (WCS):  I will fill this soon.

    Sunday, March 10, 2013

    Transaction timeout error | Long running feeds| New transaction

    If you had to run feeds that would take longer than appserver transaction timeout or any process that runs for ever and if you have see the transaction timeout exception as below for any long running processes

    0ddf0003a SystemErr     R Caused by: org.omg.CORBA.TRANSACTION_ROLLEDBACK: javax.transaction.TransactionRolledbackException:  ; nested exception is:
          javax.transaction.TransactionRolledbackException: Transaction is ended due to timeout  vmcid: 0x0  minor code: 0  completed: No
    0ddf0003a SystemErr     R    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    0ddf0003a SystemErr     R    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:80)


    I would not advise this code solution for all scenarios and only after considering all scenarios, If you need to add code to create new transaction and manage your transactions. Find below code example.


    Solution: Using new transaction in your code.

    int transactionSize=5000; //example transaction size

    try{
    for (int recordCount=0; recordCount <= recordsFromDB.length; recordCount++)
        TransactionManager.begin();

        //do business logic
        if ( (transactionSize != null) && (recordCount%transactionSize.longValue() == 0) ) {
                    TransactionManager.commit();              
        } 

    catch (Exception e) {
                    TransactionManager.rollback();
                    logging(METHOD_NAME, "Exception at performExecute" + e.getMessage());
     }
               
       

    Friday, February 8, 2013

    Facebook meta tags using commerce JSP

    This post goes through an example from commerce JSP to generate, FaceBook meta tags. The code snippet below prints the meta tags for 2 different kinds of commerce product types, Items and Packages. Based on the type of the product, a different commerce data bean is selected.

    Thursday, January 31, 2013

    Optimistic locking implementation in EJB

    If you are using EJB's in WCS and optimistic locking avoids database deadlocks and helps lower locks placed on the database and allows more applications to run concurrently against the database.Optimistic locking is implemented by creating OPTCOUNTER column for each table.

    1) Add OPTCOUNTER column in the table and optCounter CMP field with unchecked getter\setter methods option in creating new field in CMP bean.
    2) Map the table OPTCOUNTER to the optCounter field as a CMP managed field, I see this step missing for all EJB's currently implemented so I did not add optCounter mapping either for the new EJB.
    3) Adding this.initializeOptCounter(new XCustomKey(this.customId))  in ejbCreate method()
    4) Adding optimistic locking in DeploymentDescriptor --> Bean tab, by checking the Concurrency Control (Enable Optimistic locking)
     5) Adding triggers for optCounter and also a file to add these.

    CREATE or REPLACE TRIGGER perf_xcustom_1 before update ON XCUSTOM for each row
    WHEN ((new.optcounter is null) or (new.optcounter = old.optcounter))
    begin if (:old.optcounter < 32767) then :new.optcounter := :old.optcounter + 1; else :new.optcounter := 1; end if; END;

    From Infocenter explanation for implementing trigger:
    For optimistic locking to work properly, every query that updates a database table row must increment the OPTCOUNTER column value, or reset it to 1 when the current value is 32767. The WebSphere Commerce server uses this technique. However, if database table rows are updated by other code or manual procedures that do not update the OPTCOUNTER column values, then the database triggers defined in the WC_installdir/schema/db_type/wcs.perf.trigger.sql (where db_type is the database type (DB2, Oracle)) schema files ensure that the OPTCOUNTER column values are incremented properly.

    Reference:
    http://pic.dhe.ibm.com/infocenter/wchelp/v7r0m0/topic/com.ibm.commerce.admin.doc/concepts/cpmoptlock.htm

    On redirect removing properties from request


    Removing parameters from request when redirecting in commerce is an interesting problem and this is not something that needs to be implemented in regular flows.
    In the scenario when you have logonId and logonPassword  passed into form post, removing logonId/logonPassword from request properties in perform execute or creating a new request property and setting in command context does not fix the issue.

    responseProp.put(ECConstants.EC_VIEWTASKNAME,ECConstants.EC_GENERIC_REDIRECTVIEW);
    responseProp.put(ECConstants.EC_REDIRECTURL,"RedirectViewSample");
    setResponseProperties(responseProp);

    Solution: OverRide setViewInputProperties and remove the properties from request to the redirect view.

       public void setViewInputProperties(TypedProperty reqProperties){
            reqProperties.remove("logonId");
            reqProperties.remove("logonPassword");       
            this.viewReqProperties = reqProperties;
        }