Datafeed and Customization Help

Hi,
I'm working on putting the finishing touches on a site using FoxyCart and I've hit some pretty significant snags with trying to access the transaction datafeed and edit/change some of the Javascript to customize for my client's needs.

Here are my questions: Since I'm not using ModX (I've built my own store database), is there a better tutorial out there somewhere on how to set up the datafeed so I can use it to manage my site's inventory? I've tried to work through the documents on foxycart.com, but I must not have the technical abilities to understand what's there.

Also, my client makes one-of-a-kind items, so I'm trying to disable the quantity option in the shopping cart, as well as turn off the add to cart button if the item is already in the cart (thus no duplicate purchases). I've read through the forum to find info on this and I'm also still at a loss on how to edit the Javascript that creates the cart form fc_quantity work. This level of customization is pretty necessary for this site, but again, I'm a bit out of my league.

Hi sethjames.
The whole "one of a kind" products thing seems to be much more common than we'd anticipated. I know that somebody was working on something really amazing, so let's see if he'd be kind enough to share that code.

The "level of customization" is definitely there, but it'd take some experience to create it from scratch.

I just finished a site very similar to yours it sounds like. My client had mostly one-of-a-kind products and I wasn't using modx either.

Here's is what I did:

The basic overview is that I implemented a locking and inventory system. When somebody adds an item to their cart, it is then altered in the database to be locked for 15 minutes to that person's IP address. The lock is then renewed for a fresh 15 minutes for all of that person's cart each time they view a page on the site. If they remove the item from their cart, the product is unlocked. If they are idle for more than 15 minutes, the product is unlocked. But, if they buy the item, its status is changed from locked to sold.

Setup:

When I was setting up the product database, each product got a status field. The status was either set to AVAILABLE, SOLD, or LOCKED. Only items that were AVAILABLE were shown on the page.

The next thing I did was setup a table, which I called PRODUCT_LOCK, that had information for all the locked products. This table had stored information for which product is locked, who the lock belonged to, and when the lock expired.

The next step was to lock the items in the database. To do this, I used the fc_preprocess JS method. When somebody was attempting to add an item to their cart I first do a AJAX request to the database checking for the products status, and if the product was locked who it was locked to. (Note: it is important not to do an asynchronous request because the next step depends on the data returned from the database check.) The database check would then return a status. If the product was AVAILABLE, I would then do another AJAX request to lock the item in the database, and then return true so that the item would be added to their foxycart. If the product's status was LOCK there were two options. If the product was LOCKED to the person trying to add the item to their cart (i.e. they were trying to add the same item to their cart twice) a message would be shown saying the item is already in their cart and I would return false so that the item would not be re-added to their foxycart (giving it a quantity of two). The other option if the product was LOCKED is that somebody else has a lock on it. In this case, a message would be shown explaining that somebody else has the item in their cart, and that it may become available later if they don't purchase it. The script would then return false so that the item was not added to the person's foxycart.

Important note: I disabled the quantity field in the cart for one-of-a-kind objects by using jquery to look for products that were one-of-a-kind and replacing the input tag with a div tag with "1" in it.

Next I needed to make sure that people's lock refresh themselves automatically on every page. So, on every page of the site (Im using a custom built template system, so it wasn't a hard implementation) I added a bit of code that went through the PRODUCT_LOCK table. It did two things. First, it looked for any products that had locks which expired (expire time is less than current time). If there were any products like this, it removed the entry from the lock table and set the product's status back to AVAILABLE, allowing it be shown on the page and added to a user's cart. The next thing the script did was look for any items still in the PRODUCT_LOCK table that had the IP address of the user accessing the script. If there were any in there, the lock expire time was reset to fifteen minutes from the current time. That part of the script essentially takes care of deleting expired product locks and refreshing valid product locks.

The next thing to account for is if somebody deletes a product from their cart. To do this, I used a combination of the fc_preprocess method and the function that is called when the cart is closed (I can't remember what it is called right now). When the cart is opened I loop through the fc_json object and store a list of all the items in the cart. When the cart is closed I go through the fc_json object again and store a list of all the items in the cart in a different variable. I then compare the two against each other. For any items that were in the cart when it was opened but not in the cart when it was closed, I send an AJAX request (this one can be asynchronous if you like) to delete the product lock table entry and set the products status back to AVAILABLE.

That essentially takes care of the locking system. The last thing to account for is when somebody purchases the item. To solve this, I use the datafeed. I loop through the datafeed to get all the items that have been purchased. For any item that has been purchased, I remove the product lock in the database, and change the product's status to SOLD.

I think that covers most of it. I hope I didn't forget anything. The only thing that it doesn't account for is to remove all the items from a person's cart after 15 minutes of inactivity. This hasn't been an issue yet, as the only way this would be an issue is if somebody adds item(s) to their cart, is inactive for more than 15 minutes without closing the window or killing the session, and then tries to continue to checkout AND somebody else has purchased that same item(s) between the time window after the original user was inactive for 15 minutes and before they went to checkout afterwards. I have been talking with the foxycart team about implementing a JS method to remove all the items from the cart. Once that is implemented I will use a JS timer to accomplish this. After 14 minutes of inactivity, a warning message would be shown telling people their cart is about to expire and giving them a button to push to refresh their cart. If they push the button before the 15 minute mark, I would use an AJAX request to refresh the locks on all of the items in their cart. If they don't hit the button to refresh their cart before the 15 minute mark, all the items in their cart would be removed and they would be shown another message explaining their cart has expired and why.

Hope that helps and I didn't write anything incorrecly in this huge message. If you have any questions about my implementation, feel free to ask. I can send you some code examples or something like that if you like.

steeeeephen,
this is very helpful and hopeful information. i'm doing my best to understand all that you've written out here and conceptually it makes sense. i wonder if you might be able to send me some code examples, as i'd really like to piece this together, because it seems like what i'm looking for, but i'm in no way an AJAX expert and i'm sort of in a jam to get this project moving again.

if you have pieces you feel comfortable sharing, i'd love to take a further look at them. Feel free to email me. Thanks so much for your help.

// Here's what the JS looks like, some of it may not be the BEST way to do it, but I think its pretty solid.

// when foxybox is being opened, do some checks
function fc_PreProcess() {
// if attempting to add item to cart
if (nc_productUrl) { // I set the variable "nc_productUrl" when someboy clicks a "add to cart" link to the URL that checks the status of the product (e.g. "status.php?category=blah&id=23" or something similar that simply echoes a status code and some other product info which is interpretated by the callback method...
// check product status via non-asynchrounous AJAX request (sent via the modified built in jquery)
$j.ajax({url:nc_productUrl, async:false, success:nc_callbackStatus}); // "nc_callbackStatus" is the function called when the AJAX request has returned its info
// proceed by status ("nc_productStatus") is set in the "nc_callbackStatus" function before this
switch(nc_productStatus) {
// locked
case 0:
fc_show(null, "/store/msg?id=productLocked", false); // dont open the cart, instead show a message inside the foxy box, in this case, I have a single message page called "msg" that I use to display store messages by ID
return false; // return false to tell foxycart not to add the product to the cart
// available
case 1:
// lock the item in the database
nc_productUrl = "/store/lock-script?category="+nc_productCat+"&id="+nc_productId; // here I set the URL for another AJAX request to lock the item in the database, "nc_productCat" and "nc_productId" are both set inthe "nc_callbackStatus" callback method
$j.ajax({url:nc_productUrl, async:false, success: nc_callbackLock}); // here I send the next (lock) AJAX request, again asynchrnous, and with the "nc_callbackLock" function as the callback to make sure the lock went smoothly)
// proceed by lock status
switch(nc_productStatus) { // nc_productStatus is set in the "nc_callbackLock" method
// successfully lock in db
case 1:
// get list of skus as an array (the list of items in the cart when it was opened, so we can compare to the list of items in the cart when its closed, to unlock any items that may have been removed from the cart)
nc_json2skus();
// add this item that was just added to the cart to list of cart skus
nc_cartSkus.push(nc_productSku); // ("nc_productSku" is set in the callback)
// let the user add the item to the cart ,so return true return true;
// else, there was an error updating the database
default:
fc_show(null, "/store/msg?id=productLockError&err"+nc_productStatus); // show another message in the foxybox
return false; // and return false so that the item is not added to the cart
}
break;
// if the item has been purchased by a different user since this user has viewed the page
case 2:
fc_show(null, "/store/msg?id=productSold", false); // show an notification message
return false; // and don't let the item get added to the cart
// else a different error occured, show a error message
default:
fc_show(null, "/store/msg?id=productStatusError&err"+nc_productStatus); // in this case "nc_productStatus" has the error code explanation
return false; // dont let the item get added to the cart
}
}
// else, the user is just viewing the cart
else {
// get list of SKUs (to compare against the list of SKUs when the cart is closed to unlock any items removed from the cart)
nc_json2skus();
// and keep it going, and let foxycart open to show them their cart
return true;
}
}

// this is called when foxybox is being closed / built
function fc_BuildFoxyCart() {
// store skus when openend
origSkus = nc_cartSkus;
// if there is nothing in the cart, just let it ride
if (!origSkus || origSkus.length < 1) return true;
// get skus when closed
nc_json2skus();
// compare the two, store all the SKUs that should be unlocked into the "deleteSkus" var
deleteSkus = Array();
for(i = 0; i < origSkus.length; i++) {
if (!nc_cartSkus.in_array(origSkus[ i ])) deleteSkus.push(origSkus[ i ]);
}
// and unlock all the skus in the deleteSkus array in the databse
for(i = 0; i < deleteSkus.length; i++) {
// this is done via AJAX requests to a PHP script, the AJAX request can be asynchronous, just pass some sort of id to the script for it to use
$j.get("/store/unlock?sku="+deleteSkus[ i ], nc_callbackUnlock);
}
}

// callbacks
function nc_callbackStatus(data) {
a = data.split("|"); // in this case, my script echoes multiple variables, all seperated by a pipe, I split the variables into individual variables and store them
nc_productCat = a[0];
nc_productId = parseInt(a[1]);
nc_productSku = a[2];
nc_productStatus = parseInt(a[3]);
nc_productLockIp = a[4];
}
function nc_callbackLock(data) {
a = data.split("|"); // same idea as the other callback here....
nc_productCat = a[0];
nc_productId = parseInt(a[1]);
nc_productSku = a[2];
nc_productStatus = parseInt(a[3]);
nc_productLockIp = a[4];
}
function nc_callbackUnlock(data) {
// there really is no need for this with my setup, although I put it in here in case I ever did have a need for it, here you could check to make sure that the item was unlocked or something, but since my locks expire after 15 minutes anyways, I figure if the AJAX unlock fails, it will get unlocked later so its not a big deal
}