How to add free item to the basket during checkout in AbleCommerce 7

Introduction

 

This short tutorial describes a common need in AbleCommerce storefronts.  You are going to see line-by-line descriptions of the tasks required and how they are accomplished with my code.  At the end, the full source code is available free for you to use as you wish.  If you want to skip the entrée and go straight to the dessert, scroll to the bottom of this post.

 

The Need

 

You want to offer your shoppers a freebie when their order total exceeds a specific amount.  Currently AbleCommerce has coupons, but coupons don’t offer this particular functionality.  Plus coupons require the shopper to enter something.  In this case, we want the freebie to be completely automatic.

 

How It Works

 

These changes are heavily commented so you can walk through the experience yourself.    All of this work is done in the OnePageCheckout.ascx.cs file.   The code itself is loaded at the very beginning of the Page_Init() function.  Remember the entire code snippet is included at the bottom of this article.

 

The routine starts by making sure we’re not on a postback.  If we’re on a postback, that means all the remaining logic has already been executed in some previous page life cycle;  since the basket cannot be altered while within the checkout page itself, there’s no need to perform the free-item check again.

if (!Page.IsPostBack)

{

 

Next we set some variables we’re going to need for this modification.  The first variable _FreeProductId represents the ProductId value of the product you want to give away for free.  It’s important to remember that you should have this product marked as Hidden else shoppers will find it and try to buy it with every order. 

The second variable is our basket total threshold.  If the basket total (of products only) meets or exceeds this amount, it qualifies for the free item.

Feel free to adjust these values to suit your particular need.

// set the product ID of the free item we want to add

int _FreeProductId = 42;

 

// set the dollar amount threshold of the basket total we want

LSDecimal _MinBasketTotal = 1000;

 

Now we need to scan the basket.  In AC7, this is easily done with the Basket class object contained in the global Token instance.  We have to look through every item in the basket.  If it’s a product-type item, we add it’s dollar total to the grand total.  We also record whether the basket item is our free item i.e. is the free item already in the basket.

// build a basket total and scan the basket for existing free item

int _FreeItemBasketItemIndex = -1;

LSDecimal _BasketTotal = 0;

for (int x=0;x < Token.Instance.User.Basket.Items.Count;x++)

{

    // pull in the basket item object

    BasketItem _BasketItem = Token.Instance.User.Basket.Items[x];

 

    // if it's a product, add it to our total

    if (_BasketItem.OrderItemType == OrderItemType.Product)

        _BasketTotal += _BasketItem.ExtendedPrice;

 

    // if it's the free item, set the ID so we can check later if the free item

    // is already in the basket

    if (_BasketItem.ProductId == _FreeProductId)

        _FreeItemBasketItemIndex = x;

}

 

Now for the real work.  We have to determine if the basket even qualifies for a free item.  If it does, AND the free item doesn’t already exist in the basket, we have some work to do.  First we build a new BasketItem object called _FreeItem with a quantity 1.  Then we add the item to the current shoppers basket and save the basket.

//see if basket total exceeds threshold

if (_BasketTotal >= _MinBasketTotal)

{

    // Ok basket qualifies for the free item

    // see if the free item already exists in basket

    // if the _FreeItemBasketItemId value is still -1, a free item was never found in the

    // current basket

    if (_FreeItemBasketItemIndex == -1)

    {

        // create a basketitem object for this free product

        BasketItem _FreeItem = BasketItemDataSource.CreateForProduct(_FreeProductId, 1);

 

        // add the item to the basket and save it

        Token.Instance.User.Basket.Items.Add(_FreeItem);

        Token.Instance.User.Basket.Save();

    }

}

 

If the basket doe NOT qualify, then we still have a little work to do.  We need to remove any free item already in the basket.  Since we recorded the index of the free item earlier in the code, it becomes a quick and easy check here.

else

{

    // basket doesn't qualify, get rid of the free item if it exists

    if (_FreeItemBasketItemIndex > -1)

    {

        // remove the item and save basket

        Token.Instance.User.Basket.Items[_FreeItemBasketItemIndex].Delete();

        Token.Instance.User.Basket.Save();

    }

}

        }

ND MOD: AbleMods.com

So that’s it.  You’ve tested the basket, added the item if necessary and made sure to remove it if the basket doesn’t qualify.

 

Summary

 

This routine was written for AbleCommerce v7.0.6 but should work in practically any flavor.   To make it work for your site, simply add all the code provided to the beginning of the Page_Init() function in OnePageCheckout.ascx.cs file found in the /ConLib/ folder of your AbleCommerce 7 installation.

 

As always, back up any original files before making changes to them.  Nothing ruins a shoppers experience more than a broken checkout page.

 

Be sure to check out all my modules available at http://www.AbleMods.com/

 

Full Source Code

// BEGIN MOD: AbleMods.com

// 3/26/2011

// This modification will add a specific product (presumably priced at 0 so it's free) if

// the basket total exceeds a specified dollar amount.  Only products are counted towards the 

// basket total.

 

// total up products in the basket

if (!Page.IsPostBack)

{

    // set the product ID of the free item we want to add

    int _FreeProductId = 42;

 

    // set the dollar amount threshold of the basket total we want

    LSDecimal _MinBasketTotal = 1000;

 

    // build a basket total and scan the basket for existing free item

    int _FreeItemBasketItemIndex = -1;

    LSDecimal _BasketTotal = 0;

    for (int x=0;x < Token.Instance.User.Basket.Items.Count;x++)

    {

        // pull in the basket item object

        BasketItem _BasketItem = Token.Instance.User.Basket.Items[x];

 

        // if it's a product, add it to our total

        if (_BasketItem.OrderItemType == OrderItemType.Product)

            _BasketTotal += _BasketItem.ExtendedPrice;

 

        // if it's the free item, set the ID so we can check later if the free item

        // is already in the basket

        if (_BasketItem.ProductId == _FreeProductId)

            _FreeItemBasketItemIndex = x;

    }

 

    //see if basket total exceeds threshold

    if (_BasketTotal >= _MinBasketTotal)

    {

        // Ok basket qualifies for the free item

        // see if the free item already exists in basket

        // if the _FreeItemBasketItemId value is still -1, a free item was never found in the

        // current basket

        if (_FreeItemBasketItemIndex == -1)

        {

            // create a basketitem object for this free product

            BasketItem _FreeItem = BasketItemDataSource.CreateForProduct(_FreeProductId, 1);

 

            // add the item to the basket and save it

            Token.Instance.User.Basket.Items.Add(_FreeItem);

            Token.Instance.User.Basket.Save();

        }

    }

    else

    {

        // basket doesn't qualify, get rid of the free item if it exists

        if (_FreeItemBasketItemIndex > -1)

        {

            // remove the item and save basket

            Token.Instance.User.Basket.Items[_FreeItemBasketItemIndex].Delete();

            Token.Instance.User.Basket.Save();

        }

    }

}

// END MOD: AbleMods.com

How to explode checkout basket items into separate shipments in AbleCommerce 7

Need:  The project requires every item ordered from the AbleCommerce 7 storefront to be assigned it’s own shipment.  The reason is each item is made separately and must ship separately.  Even if multiple quantities of the same item are purchased, each unit of 1 must be by itself on a separate shipment.

 

Justification: Existing AC7 code will automatically split basket items up into separate shipments based on the WarehouseId value assigned to each product in the basket.  However this does not split up multiple quantities of the same item.  Even if an item is set as “Ships Separately” on the Edit Product page, AC7 will not break each quantity of the multi-quantity item into separate shipments.  Thus custom code is required.

 

Solution:  Create a new helper class in /App_Code/.  I called mine AbleModsHelper.Basket.cs.  Put the following code into the file and call the routine in the basket initialization area of OnePageCheckout.ascx.cs.  You’ll probably want to comment out the original Basket.Package() code so AbleCommerce libraries don’t mess up your shipments.

 

/// <summary>

/// Builds a shipment for each quantity of each product in the supplied basket

/// </summary>

/// <param name="_Basket">Basket to which shipments will be assigned</param>

public static void MakeBasketShipments(Basket _Basket)

{

    // Reset basket item shipment assignments 

    foreach (BasketItem _BasketItem in _Basket.Items)

    {

        _BasketItem.BasketShipmentId = 0;

        _BasketItem.Save();

    }

 

    // remove any shipments after shipment[0]

    if (_Basket.Shipments.Count > 1)

        for (int x = 1; x < _Basket.Shipments.Count; x++)

            _Basket.Shipments[x].Delete();

 

    // loop through basket items and make new shipments for each item and quantity ordered

    //BasketItemCollection _NewItems = new BasketItemCollection();

    int _ItemCount = _Basket.Items.Count;

    for (int _Count = 0; _Count < _ItemCount; _Count++)

    {

        BasketItem _BasketItem = _Basket.Items[_Count];

        // skip anything that's not a product or is already Qty 1

        if (_BasketItem.OrderItemType != OrderItemType.Product | _BasketItem.Quantity <= 1)

            continue;

 

        // we've got an item with multiple quantity - split it out using the original basketitem

        // record as a template.

        // first though, we must reset the new "master" product to qty 1

        int _LoopQty = _BasketItem.Quantity - 1;

        _BasketItem.Quantity = 1;

        _BasketItem.Save();

 

        // loop through remaining quantity of this item

        for (int x = 0; x < _LoopQty; x++)

        {

            // clone the original basket item

            BasketItem _NewItem = _BasketItem.Clone();

 

            // set it's quantity and basketid

            _NewItem.Quantity = 1;

            _NewItem.BasketId = _Basket.BasketId;

 

            // save the duplicate basketitem 

            _NewItem.Save();

 

            // clone any basket inputs from the original basket item

            foreach (BasketItemInput _BasketInput in _BasketItem.Inputs)

            {

                BasketItemInput _NewInput = new BasketItemInput();

                _NewInput.BasketItemId = _NewItem.BasketItemId;

                _NewInput.InputFieldId = _BasketInput.InputFieldId;

                _NewInput.InputValue = _BasketInput.InputValue;

                _NewInput.Save();

            }

 

            // add new item it to the basket

            _Basket.Items.Add(_NewItem);

            _Basket.Save();

        }

 

    }

 

    // clear out all shipments for this basket

    _Basket.Shipments.DeleteAll();

 

    // Sort the basket so the items are grouped together by name

    BasketItemCollection _BasketItems = _Basket.Items;

    _BasketItems.Sort("Name");

 

    // We have all basket items broken into separate lines

    // Create unique shipment for each basket item that doesn't already have a shipment assigned to it

    foreach (BasketItem _BasketItem in _BasketItems)

    {

        // skip basket item if not a product

        if (_BasketItem.OrderItemType != OrderItemType.Product)

            continue;

 

        // make new shipment

        BasketShipment _Shipment = new BasketShipment();

        _Shipment.BasketId = _Basket.BasketId;

        _Shipment.WarehouseId = _BasketItem.Product.WarehouseId;

        _Shipment.AddressId = Token.Instance.User.PrimaryAddress.AddressId;

        _Shipment.Save();

 

        // add new shipment to basket

        _Basket.Shipments.Add(_Shipment);

 

        // assign this basket item to the new shipment

        _BasketItem.BasketShipmentId = _Shipment.BasketShipmentId;

        _BasketItem.Save();

 

        //// reset the parentitemid value since now this item is its own basket item record

        _BasketItem.ParentItemId = _BasketItem.BasketItemId;

        _BasketItem.Save();

 

    }

 

    // save the basket

    _Basket.Save();

 

}

QBWC sales tax are double taxed

If you are using QBWC aka QuickBooks Web Connector to import invoices or sales receipts to your company file, you may see the sales getting double taxed.

This is caused by having sales tax calculated by the original source of the data (in my case AbleCommerce 7) and then again in QuickBooks.

The solution is to change the default sales tax setting in the QuickBooks company file.  QuickBooks likes to set the default taxable status of a customer to “Taxable”.  However in situations where the data is being imported from another app, it’s quite possible that taxes are already computed.

The setting is easy to find, once you know where it is.   First, load QB and open your desired company file.

Then choose the Edit menu and select ‘Preferences’

When the preferences load, select the Sales Tax category as shown here.   image

Now select the Company Preferences tab in the main window.  You’ll see a dropdown choice for “Your Most Common Sales Tax Item”.  Change that from whatever taxable tax code is listed to a non-taxable tax code.

image

 

If you have a non-taxable sales tax item, just click the Add Sales Tax Item… button to create a new one.

How to edit existing blog posts with Windows Live Writer 2011

I finally figured out how to edit existing posts in my blog using WLW 2011.  It’s actually simple but not obvious with the Microsoft style of menus and toolbars.

First you have to click the primary app menu in WLW that looks like this:  image_thumb

Then click on Open Recent Posts.  Don’t click a choice on the fly-out menu to the right, just click the menu option itself.

Finally, the traditional File/Open dialog appears.  On the left side you will see your specific name.  Click your name and all your previous blog posts are listed. image_thumb[1]

  From there you pick one, edit as needed and just click Publish to save it back to your blog.

So far I like WLW 2011 – does a good job where other web-based editors don’t.

Odd web traffic statistics for my store/blog/support site

You’d like my support pages, my product pages and my online store would get the most traffic.  Wrong!

I just started looking seriously at web traffic stats for my www.AbleMods.com web site.  Oh what interesting details have I found.  Take a look at the top pages in a site that sells software products.  Thank goodness at least ONE of my products actually made the Most Popular Pages top 10 list Smile

image

Finally–decent code formatting for my Blog

After realizing my blog was painful to read when any source code was added, I took a few hours this morning to play around.

First step was getting Windows Live Writer – that made a world of difference with regular posts.  However this didn’t really solve the problem of code formatting.  Next stop:  Plug-ins for WLW.

I tried the first one listed with the most downloads on the plug-in website.  While this one worked, it didn’t really handle long lines by embedding them in a scrolling container.  So, back to the drawing board.

The second most downloaded code formatter (Code Snippet plugin for Windows Live Writer) did exactly what I wanted.  AHA!  Finally I’ll make it work the way I like.  So after installing and testing, my blog looked WORSE.

Finally I found the problem.  There’s a setting in BlogEngine.Net 2.x that specifically controls how blog category entries are displayed.  It was not showing the full HTML for posts when I clicked a specific category.  To fix this, just go into Settings/Basic in the Dashboard and uncheck both checkboxes here:

image

Now when you click a blog category, the blog posts show as they should.

Giving the blog some love

After setting up some traffic analysis on my www.AbleMods.com website last night, I noticed my blog posts were not formatting certain source code entries correctly.  This must have changed when I upgraded to BlogEngine 2.x last month.

So this morning I did some more research and saw a lot of references to using Windows Live Writer for your blog postings instead of the built-in TinyMCE html editor provided with BlogEngine.Net.

Boy have I been going about things the wrong way – this WLW is spiffy.

Now if I could just figure out how to get it to let me use WLW to edit existing blog posts, I could clean up this ugly mess…….

Quickbooks for AbleCommerce 7 rewrite in progress

Well I’ve been working hard for over three weeks now doing a complete rewrite of my Quickbooks Connector that transfers AbleCommerce 7 data electronically into Quickbooks using ASP.Net web services.

The first challenge was getting the entire Quickbooks API written into a strongly-typed ASP.Net class library.  I originally wrote some very basic classes that would serialize.  However it was a hack job as I was very new to XML, serialization etc.  Boy was it confusing. 

Plus none of the Quickbooks response messages were de-serialized back into strongly typed data classes.  I was actually doing a manual XML search on the response text to find any data needed from the responses.  Ugly doesn’t even begin to describe the code.  I never want to see another XML node search again.

Having such clumsy access to the response and query data meant the code was brutal to debug when something didn’t work right.

So I sat down and bit the bullet.  I built 26 strongly-typed objects that all correctly serialize and de-serialize the Quickbooks SDK requests and responses.  These are written exactly to the Intuit SDK specifications.  Testing serialization and de-serialization created some long nights but it was well worth it.

Now any response from the QB web connector can be quickly de-serialized into usable strongly-typed classes.  Responses are even typed correctly so dollar amounts are (double) and logical values are (bool).  Wow does this make things easier to program.

So now the sky’s the limit.  I’ve already implemented full support for sub-grouped GL accounts and sub-grouped inventory items.  This is becoming increasingly more popular with businesses using Quickbooks.

I’ve also vastly improved error reporting.  Troubleshooting has always been a nightmare with the QB module.  No longer though, I’ve got a solid handle on it now that I have strongly-typed responses.

Next I’m going fix another common problem: Inventory.  Many companies use Quickbooks inventory to track their stock levels.  Getting these values back into AbleCommerce is a manual process.  I am going to make this new version of the Quickbooks Web Connector for AbleCommerce 7 synchronize inventory stock levels from QuickBooks into AbleCommerce.

I’m also going to implement another inventory problem, selling in bulk.  Often an AC7 site will sell a case of something but inventory it indivdually in QuickBooks.  That poses a problem with the transfer because the quantity sold will be 1 yet the case contained 250 units.

The new version of the module will allow a "multiplier" value to be set to each product in AbleCommerce 7 so the Quantity 1 sold becomes Quantity 250 when it hits QuickBooks.

That’s all for now, more to follow.  As always, check out my http://www.Solunar.com site for all your solunar times and solunar table needs plus camping, hunting, marine equipment and yes, even boat anchors.