Monday, August 8, 2011

Scriptable Cart - Full Sample By Jason K

We've MOVED!!!! www.codeboxllc.com/ksc

Hey Guys. I've been meaning to post this up but lost track of time due to extermination work I've been doing.
Couple months ago, I've worked with Jason K from NetSuite to trouble shoot remove/add issues I was having with my particular scriptable cart process. What this code is SUPPOSED to do:
When an item is added with certain country, it pro grammatically adds country specific surcharge item to the cart. In increments existing surcharge item in the cart if it already exists, it adds new country item if it doesn't exists. Removing parent item will also either increments or decrements the surcharge item.
I have not tried out his code personally but he assures me that it worked for him when he was fully testing out the process in his environment.

I'm hoping this will help in your effort to implement Webstore Scriptable Cart/Checkout form:


/*
Summary:  A custom item option is attached to a certificate which allows the shopper to choose a country.
When the shopper does this, a surcharge will be added to the cart based on the country chosen.  The shopper
can also add another certificate to the cart with a different country and there will be a separate line item
for each surcharge for each country.


This script also syncs up quantities for each cert-country combination.  For example, if the shopper increments,
decrements, or adds another Canada certificate to the cart, the quantity of the canadian surcharge will follow
that quantity.  This will NOT affect the quantity of other surcharges that are not the same country.


Finally, when a certificate is removed from the cart, the associated surcharge is also removed.


There are two customizations that were done to the sales order to support this script:


1) a new custom body field "custbody_processing", a text field, was added to ensure that no infinite looping
is seen.


2) a new custom body field "custbody_deletedcountry", a text field, is added and used by the validateDelete
event. The problem with recalc during a deletion is that we don't know what was deleted.  But, in our
custom validateDelete, if a cert was deleted, we save the cert's country in this custom field.  Then, during
recalc, we can just look up what surcharge needs to be removed and remove it.


There are also a couple of utility functions included below, including safeSelectNewLineItem and safeSelectLineItem.
In some cases, I've noticed that selectNewLineItem and selectLineItem throws an error if the script left the current
item row in an uncommitted state.  Using these functions will ensure that a commit will be done if need be before
a new row is selected.  The other functions should be self-explanatory.


*/
var certId = "74"; // ID of the general certificate, will including an item option custcol_certcountry
var certCountry = "custcol_certcountry"; // name of the custom column


// Map to define what surcharge item ID belongs to which country.
// In this case, I've only set up two countries along with two non inventory items.
var surchargeMap = {"US":"76", "CA":"75"};


// In order to make things easier for us, we will track the type of cert
// that is deleted via a validateDelete.  Will get the country
function validateDelete(type)
{
if (type == 'item')
{
var itemId = nlapiGetCurrentLineItemValue('item','item');
if (itemId == certId)
{
// if we get here, we know a certificate is being deleted.  Store the country in our
// custom body field for use in recalc.
var country = nlapiGetCurrentLineItemValue('item', certCountry);
log('Item ID '+itemId+' being deleted for country '+country+' - storing this in custom field');
nlapiSetFieldValue('custbody_deletedcountry', country);
}
}


return true; // Always return either true or false during validation
}


function onRecalc(type, action)
{
if (type != 'item') { return; }


try
{
var processing = nlapiGetFieldValue('custbody_processing');
if (processing != null && processing == 'T')
{
// We are in a secondary recalc, so exit
return;
}


// If we've passed the previous check, we are in the first
// level of recalc, so set processing flag
nlapiSetFieldValue('custbody_processing', 'T');


if (action=='commit')
{
doCommit();
}


if (action=='remove')
{
doRemove();
}
}
catch (err)
{
log("General Error: "+err.message);
}
finally
{
// Now, even with an error, we want to reset the processing flag
// so use a finally-clause to ensure this happens no matter what
nlapiSetFieldValue('custbody_processing', 'F');
}
}




// Called when a commit is done for recalc
function doCommit()
{
var thisItem = nlapiGetCurrentLineItemValue('item','item');


// Ignore any add-to-carts that are not certifications
if (thisItem != certId) { return; }


var thisQty = nlapiGetCurrentLineItemValue('item','quantity');
log("Current qty of cert = "+thisQty);


var thisCountry = nlapiGetCurrentLineItemValue('item', certCountry);
log("Current country of cert: "+thisCountry);


var surchargeId = surchargeMap[thisCountry];


if (isEmpty(surchargeId))
{
log("No surcharge ID found for country "+thisCountry+" - skipping processing");
return;
}


var surchargeLine = findItem(surchargeId);


if (surchargeLine == -1)
{
// We need to add a surcharge here
log("surcharge not found - adding surcharge");
addItem(surchargeId, thisQty);
}
else
{
surchargeQty = nlapiGetLineItemValue('item', 'quantity', surchargeLine);
log("Surcharge found - current Surcharge qty "+surchargeQty);


if (surchargeQty != thisQty)
{
log("updating quantity of surcharge");
safeSelectLineItem(surchargeLine);
nlapiSetCurrentLineItemValue('item', 'quantity', thisQty);
nlapiCommitLineItem('item');
}
}
}


// Called when a remove is done for recalc.  We have tracked the country that was
// deleted in the custom field, so this should be straightforward.  If there's no
// value in the custom field, don't do anything.
function doRemove()
{
// First, we get the deleted country from custbody_deleteditem
var deletedCountry= nlapiGetFieldValue('custbody_deletedcountry');


// If it's empty, this means that the item that was deleted was not a cert, so ignore.
if (isEmpty(deletedCountry))
{
return;
}


// Start a try-block here so after the remove we are sure that custbody_deletedcountry
// is cleared out even during an error
try
{
log('Country '+deletedCountry+' has been deleted, finding related surcharge');


var surchargeId = surchargeMap[deletedCountry];
var surchargeLine = findItem(surchargeId);


// only delete if appropriate surcharge is found
if (surchargeLine > 0)
{
log('Surcharge item for country '+deletedCountry+' found on line '+surchargeLine+' - removing');
safeSelectLineItem(surchargeLine);
nlapiRemoveLineItem('item', surchargeLine);
}
}
finally
{
nlapiSetFieldValue('custbody_deletedcountry', '');
}
}


// This function adds the given item to the order
function addItem(itemID, qty)
{
safeSelectNewLineItem();
nlapiSetCurrentLineItemValue('item', 'item', itemID);
nlapiSetCurrentLineItemValue('item', 'quantity', qty);
nlapiCommitLineItem('item');
}




// Sometimes, a selectNewLineItem results in an error if there's uncommitted
// values in the item list.  This makes sure this does not happen.
function safeSelectNewLineItem()
{
if (notEmpty(nlapiGetCurrentLineItemValue('item','item')))
{
nlapiCommitLineItem('item');
}


nlapiSelectNewLineItem('item');
}


// Sometimes, a selectLineItem results in an error if there's uncommitted
// values in the item list.  This makes sure this does not happen.
function safeSelectLineItem(itemLine)
{
if (notEmpty(nlapiGetCurrentLineItemValue('item','item')))
{
nlapiCommitLineItem('item');
}


nlapiSelectLineItem('item', itemLine);
}


// General find function for the item list
function findItem(itemID)
{
var cnt = nlapiGetLineItemCount('item');
for (var i=1; i <= cnt; i++)
{
if (nlapiGetLineItemValue('item','item',i) == itemID)
{
return i;
}
}


return -1; // -1 means not found
}




function isEmpty(tmp)
{
return tmp == null || tmp == '';
}


function notEmpty(tmp)
{
return !isEmpty(tmp);
}




function log(msg)
{
alert(msg);
}


No comments:

Post a Comment