Thursday, 17 August 2017

Adding SKU Inventory level in BCC SKU properties general tab

Step :1:

We can achieve this functionality  in 2 ways
1. Extending RepositoryPropertyDescriptor -- It don't support logging functionality -In ATG mentioned use this one for transient property.
2. Extending GSAPropertyDescriptor It  supports logging functionality

Here I was used second approach (for transient property).
Create new class by extending GSAPropertyDescriptor class.Access inventory repository or inventorymanager to get inventory for given sku id:

Reference code:

package com.Nagesh.commerce.catalog;
import atg.adapter.gsa.GSAPropertyDescriptor;
import atg.nucleus.Nucleus;
import atg.repository.RepositoryItem;
import atg.repository.RepositoryItemImpl;

import com.Nagesh.commerce.inventory.InventoryManager;

/**
 * This class is responsible for to get inventoryLevel for the inventory repository for given SKU id
 * @author Nagesh
 */
public class SKUInventoryRepositoryPropertyDescriptor extends   GSAPropertyDescriptor {
    /**
*  Unique serialVersionUID added for serialization purpose
*/
private static final long serialVersionUID = -25670871313135177194L;
private static final String INV_MANAGER="/atg/commerce/inventory/InventoryManager";

@Override
public Object getPropertyValue(RepositoryItemImpl sku, Object pValue) {

long skuInventoryLevel = 0;
String skuId = null;
InventoryManager invManager = null;
RepositoryItem inventoryItem = null;
 try{
if (sku.getPropertyValue("id") != null) {
skuId= (String)sku.getPropertyValue("id");
logDebug("SKUInventoryRepositoryPropertyDescriptor Sku id for inventory visibility in BCC: "+skuId);

invManager = (InventoryManager) Nucleus.getGlobalNucleus().resolveName(INV_MANAGER);

if (invManager != null) {
logDebug("SKUInventoryRepositoryPropertyDescriptor InvManager component loaded ");
inventoryItem = invManager.getInvenItem(skuId);
if (null != inventoryItem) {
logDebug("SKUInventoryRepositoryPropertyDescriptor inventory Item loaded ");

if (inventoryItem.getPropertyValue("stockLevel") != null) {
skuInventoryLevel = (Long) inventoryItem.getPropertyValue("stockLevel");
logDebug("SKUInventoryRepositoryPropertyDescriptor skuid: "+skuId +" stockLevel: "+skuInventoryLevel);
}
  if(skuInventoryLevel==0 &&inventoryItem.getPropertyValue("euStockLevel") != null) {
skuInventoryLevel = (Long) inventoryItem.getPropertyValue("euStockLevel");
logDebug("SKUInventoryRepositoryPropertyDescriptor skuid: "+skuId +" euStockLevel: "+skuInventoryLevel);
}
}else{
logDebug("SKUInventoryRepositoryPropertyDescriptor inventory Item not found for skuid: "+skuId);
}
}
         }  
     }catch (Exception ex){
  logError("SKUInventoryRepositoryPropertyDescriptor Exception : "+ex.getMessage());
       }
return skuInventoryLevel;
}
}

Step :2
Add new transient  property (inventoryLevel) in BCC-Merchandising layer catalog repository definition file:

File: \config\atg\commerce\catalog\custom\customCatalog.xml
Reference code:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<gsa-template xml-combine="append">
<header>
<name>Catalog Extensions</name>
<author>Nagesh</author>
<description>
SQL Repository for inventory visibility at SKU level
</description>
</header>
 <item-descriptor name="sku"  xml-combine="append" >
     <attribute name="resourceBundle" value="atg.commerce.CustomCatalogTemplateResources" />
             
       <property name="skuInventory"  property-type="com.Nagesh.commerce.catalog.SKUInventoryRepositoryPropertyDescriptor" data-type="long" queryable="false" writable="true" readable="true" display-name-resource="skuInventory" category-resource="categoryBasics">
           <attribute name="propertySortPriority" value="-10"/>
        </property>
  </item-descriptor>
</gsa-template>

Step:3
Add message resource key/Value for the property "skuInventory" attribute "display-name-resource"

Reference code:
Store\Commerce\resources\atg\commerce\CustomCatalogTemplateResources.properties
skuInventory=Inventory Level

Step:4: Build and Deploy EAR file in server

Output BCC -UI screen with Inventory Level value


Tuesday, 12 March 2013

example working with XPath API

Step-1:
package com;
import java.io.IOException;
import java.io.StringReader;

import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;


public class XpathExtractor {
      
      
       private static int MAX_BUFFER_SIZE = 16384;

       private NamespaceContext namespaceResolver;
       private XPath xpath;
       private DocumentBuilder builder;
       private DocumentBuilderFactory domFactory;
      
       private String  itemIdXPath;
       private String  shipDateXPath;
      
      
       private XPathExpression  itemIdXPathExpression;
       private XPathExpression  shipDateXPathExpression;
      
      
       public CopyOfInstntInvExtractor() {
              xpath = XPathFactory.newInstance().newXPath();
              domFactory = DocumentBuilderFactory.newInstance();
          
       }
      
       public NamespaceContext getNamespaceResolver() {
              return namespaceResolver;
       }

       public void setNamespaceResolver(NamespaceContext namespaceResolver) {
              this.namespaceResolver = namespaceResolver;
       }

      
       public String getItemIdXPath() {
              return itemIdXPath;
       }

       public void setItemIdXPath(String itemIdXPath) {
              this.itemIdXPath = itemIdXPath;
              this.itemIdXPathExpression = this.compile(this.itemIdXPath);
              }

      
      
      
       public String getShipDateXPath() {
              return shipDateXPath;
       }

       public void setShipDateXPath(String shipDateXPath) {
              this.shipDateXPath = shipDateXPath;
              this.shipDateXPathExpression= this.compile(this.shipDateXPath);
       }

      

      
      
       public void  getInfo(String message) throws Exception
       {
             
              try {
                          
                           builder = domFactory.newDocumentBuilder();
      
                         Document doc = builder.parse( new InputSource(new StringReader(message)));
                        
                      
                      String itemID = (String) this.itemIdXPathExpression.evaluate(doc, XPathConstants.STRING);
                    
                         String shipDate = (String) this.shipDateXPathExpression.evaluate(doc, XPathConstants.STRING);
                        
                    
             
                    
              } catch (SAXException e) {
                     throw e;
                    
              } catch (IOException e) {
                     throw e;
                    
              } catch (XPathExpressionException e) {
                     throw e;
              }
                    
       }
      
      
      
       private XPathExpression compile(String expression){                 
              try {
                     return this.xpath.compile( expression );
                    
              } catch (XPathExpressionException e) {
                     String message = "Error compiling XPath Expression [" + expression + "]";
                     throw new RuntimeException(message, e);
              }
       }     


      
}
Step-2: Spring bean configuration:
<bean id="xpathExtractor" class="com. XpathExtractor ">   
       <property name="shipDateXPath"        value=" itemIdXPath/>
       <property name="itemIdXPath"          value=" shipDate " />
 </bean>

Input :xml file

Input XML file as string for the method: getInfo (-)

<?xml version="1.0" encoding="UTF-8"?>
<functionEvent >
  <itemIdXPath>10000000860002</ itemIdXPath >
  <shipDate>2013-03-12</shipDate>
</ functionEvent >

Monday, 7 May 2012

Procedure to override Item PriceList using Calculator in ATG10:


Procedure to override ItemPriceListCalculator in ATG10:
Step-1:  implement your own pricing calculator class by extending “ItemListPriceCalculator “class
Sample code like below:
public class XxPriceCalculator extends ItemListPriceCalculator {
private static final String PERFORM_MONITOR_NAME = "XxPriceCalculator";
private static final String TRUE_STRING = "true";
public final static ComponentName SHOPPING_CART= ComponentName.getComponentName("/atg/commerce/ShoppingCart");
private XxRepositoryService priceService ;
/**
* Price a single item in a context. this method will only update the price if the monthlyOnlinePrice has been set, so it should only affect talkplan prices
*
* @param pPriceXx ItemPriceInfo representing the current price Xx for the item
* @param pItem The item to price
* @param pPricingModel A RepositoryItem representing a PricingModel
* @param pProfile The user's profile
* @param pExtraParameters A Map of extra parameters to be used in the pricing, may be null
*/
@SuppressWarnings("unchecked")
@Override
public void priceItem(ItemPriceInfo pPriceXx, CommerceItem pItem, RepositoryItem pPricingModel, Locale pLocale, RepositoryItem pProfile,
Map pExtraParameters) throws PricingException {
String perfName = "priceItem";
PerformanceMonitor.startOperation(PERFORM_MONITOR_NAME, perfName);
boolean perfCancelled = false;

try {
Object priceSource = getPriceSource(pItem);
if (priceSource == null) {
try {
if (!perfCancelled) {
PerformanceMonitor.cancelOperation(PERFORM_MONITOR_NAME, perfName);
perfCancelled = true;
}} catch (PerfStackMismatchException psm) {
if (isLoggingWarning()) {
logWarning(psm);
}}
throw new PricingException(MessageFormat.format(Constants.NO_PRICE_SOURCE, new Object[] { pItem }));
}
RepositoryItem skuItem = (RepositoryItem) priceSource;
if (skuItem != null) {
if (isLoggingDebug()) {
logDebug("Calculating price for " + priceSource);
}
OrderHolder shoppingCart = (OrderHolder) this.resolveName(SHOPPING_CART, false);
if (shoppingCart != null) {
String offerAvailStatus = null;
String priceValue = null;
String XxId = null;
try {
if(null != pProfile) {
XxId = (String) pProfile.getPropertyValue("XxId");
offerAvailStatus = this.priceService.queryOfferAvailabilityStatus
(XxRepositoryService.XX_MANAGEMENT_DESCRIPTOR_NAME, XxId);
priceValue = this.priceService.queryXxPrice
(XxRepositoryService.XX_MANAGEMENT_DESCRIPTOR_NAME, XxId);
if (isLoggingDebug()) {
logDebug("XxPriceCalculator : Xx ID " + XxId
+ "Profile ID" + pProfile.getRepositoryId());
}}
if(null == offerAvailStatus ||
(null != offerAvailStatus && offerAvailStatus.equalsIgnoreCase(TRUE_STRING))) {
if (priceValue != null) {
Double price = Double.parseDouble(priceValue);
//price is not null
if (null != price) {
pPriceXx.setCurrentPriceDetails(new ArrayList());
// only need to update the price if the monthly online price has been set
priceItem(price.doubleValue(), pPriceXx, pItem, pPricingModel, pLocale, pProfile, pExtraParameters);
if (isLoggingDebug()) {
logDebug("ItemPriceInfo for " + priceSource + "=" + pPriceXx);
}
if(null != XxId) {
this.priceService.updateOfferAvailabilityStatus(XxId, TRUE_STRING);
}}}}
} catch (RepositoryException repExec) {
// TODO Auto-generated catch block
repExec.printStackTrace();
}}}
} finally {
try {
if (!perfCancelled) {
PerformanceMonitor.endOperation(PERFORM_MONITOR_NAME, perfName);
perfCancelled = true;
}} catch (PerfStackMismatchException e) {
if (isLoggingWarning()) {
logWarning(e);
}}}// end finally
}
@Override
@SuppressWarnings(XxConstants.UNCHECKED)
public void priceItems(List pPriceXxs, List pItems, RepositoryItem pPricingModel, Locale pLocale, RepositoryItem pProfile, Order pOrder,
Map pExtraParameters) throws PricingException {

for (int i = 0; i < pItems.size(); i++) {
CommerceItem cItem = (CommerceItem) pItems.get(i);
ItemPriceInfo priceInfo = (ItemPriceInfo) pPriceXxs.get(i);
Object priceSource = getPriceSource(cItem);
RepositoryItem skuItem = (RepositoryItem) priceSource;

if (skuItem != null) {
if (isLoggingDebug()) {
logDebug("Calculating price for " + priceSource);
}
String priceValue = null;
String offerAvailStatus = null;
String XxId = null;
try {
if(null != pProfile) {
XxId = (String) pProfile.getPropertyValue("XxId");
offerAvailStatus = this.priceService.queryOfferAvailabilityStatus
(XxRepositoryService.XX_MANAGEMENT_DESCRIPTOR_NAME, XxId);
priceValue = this.priceService.queryXxPrice
(XxRepositoryService.XX_MANAGEMENT_DESCRIPTOR_NAME, XxId);
}
if(null == offerAvailStatus ||
(null != offerAvailStatus && offerAvailStatus.equalsIgnoreCase(TRUE_STRING))) {
if (priceValue != null) {
Double price = Double.parseDouble(priceValue);
//price is not null
if (null != price) {
priceInfo.setCurrentPriceDetails(new ArrayList());
// only need to update the price if the monthly online price has been set
priceItem(price.doubleValue(), priceInfo, cItem, pPricingModel, pLocale, pProfile, pExtraParameters);
if (isLoggingDebug()) {
logDebug("ItemPriceInfo for " + priceSource + "=" + priceInfo);
}}}
if(null != XxId) {
this.priceService.updateOfferAvailabilityStatus(XxId, TRUE_STRING);
}}} catch (RepositoryException repExec) {
// TODO Auto-generated catch block
repExec.printStackTrace();
}}}}
public XxRepositoryService getPriceService() {
return priceService;
}
public void setPriceService(XxRepositoryService priceService) {
this.priceService = priceService;
}}

Step-2:  create ATG pricing calculator componend by using above implementation class
Ex: XxPriceCalculator.properties
#
# The ItemPricingCalculator which gets the differential handset price
#

$class=com.Xx.pricing.QuotePriceCalculator

loggingIdentifier=ItemListPriceCalculator

pricePropertyName=listPrice
requirePriceValue=false
priceFromCatalogRef=true

pricingTools=/atg/commerce/pricing/PricingTools

repository=/atg/commerce/catalog/ProductCatalog
priceListsRepository=/atg/commerce/pricing/priceLists/PriceLists

priceListService=/shop/service/PriceListService

priceService=/com/Xx/service/PriceMatchingRepositoryService
Step-3: configure your own pricing calculator component in bellow path /atg/commerce/pricing/ItemPricingEngine
EX: ItemPricingEngine.properties  
# @version $Id: //hosting-blueprint/B2CBlueprint/version/10.0.2/EStore/config/atg/commerce/pricing/ItemPricingEngine.properties#2 $$Change: 635969 $

# This set is used if you are using priceLists to store your
# prices.  This means the prices are retrived from the PriceLists
# repository.
preCalculators=\
        calculators/ItemPriceListCalculator,\
        calculators/ItemPriceListSaleCalculator,\
        /com/Xx/pricing/QuotePriceCalculator,\
        calculators/ConfigurableItemPriceListCalculator,\
        calculators/ConfigurableItemPriceListSaleCalculator
       
priceInfoClass=atg.projects.store.pricing.StoreItemPriceInfo
Step-4: configure PricingTools component in bellow path /atg/commerce/pricing/PricingTools
EX: PricingTools.properties
# @version $Id: //hosting-blueprint/B2CBlueprint/version/10.0.2/EStore/config/atg/commerce/pricing/PricingTools.properties#2 $$Change: 635969 $
$class=atg.projects.store.pricing.StorePricingTools

Step-5: configure PricingModelProperties component in bellow path /atg/commerce/pricing/PricingModelProperties
EX: PricingModelProperties.properties
# @version $Id: //hosting-blueprint/B2CBlueprint/version/10.0.2/EStore/config/atg/commerce/pricing/PricingModelProperties.properties#2 $$Change: 635969 $
# $Id: //hosting-blueprint/B2CBlueprint/version/10.0.2/EStore/config/atg/commerce/pricing/PricingModelProperties.properties#2 $
$class=atg.projects.store.pricing.StorePricingModelProperties