Replacing the ecommerce module with ubercart during a Drupal 5 to Drupal 6 upgrade: could it be so easy?

During one of the many non-trivial Drupal 5 to Drupal 6 upgrade tasks we seem to have been working on recently (pretty good success rate there I am happy to say), a client wanted to change from the e-Commerce module they had been using in Drupal 5 to the Ubercart module in Drupal 6. The constraint called for maintaining product creation dates, that is, re-cycling the product content type created by e-Commerce (v3 by the way) into a fully fledged Ubercart-integrated product node. After a few dry runs we succeeded (see script below). Could it really be so easy (comments welcome)?

As in all non-trivial Drupal 5 to Drupal 6 upgrades, we based ourselves on an in-house Gap Analysis form which leads to a customized checklist of tasks. And the first task was to replicate the legacy site on our own test server. Then, the upgrade is run on a second test instance, and each successfully completed milestone is committed to a version control system (the snapshot includes the database dump) in order to always have a solid base to fall back upon should problems develop (when problems develop). A second very useful benefit of using a version control system for each step completed is that the log serves as an admirable guide for upgrading similar sites, or should it become necessary to re-do the process.

The e-Commerce and Ubercart modules both have in common the use of a Product node module, which makes available a Product content type so that site administrators can manage their products based on product node instances.  The objective in the upgrade is to recycle those nodes as they are. So basically the site is first upgraded with the exception of the e-Commerce module (which we disable but do not uninstall of course) and without installing Ubercart yet.  That leaves the product nodes in a weird state: they are there (you can see them in Administer > Content management > Content) and they are of type product, but you cannot filter them by type product because no product content type exists on the system at this point. An uneasy feeling Cool.

At this point we installed Ubercart and its module dependencies for Drupal 6 and enabled just the Store and Product core modules. Now the product nodes became first-class citizens again, and even sported a proud "Buy" button, with price and sku display fields to boot (a glance at uc_product_load() in uc_product.module explains how Ubercart adds these fields to the node object when it is loaded). The problem was that these fields were empty, of course: Ubercart is smart, but unfortunately cannot read our minds yet.

So, it's a problem of finding where that information is to be found in the e-Commerce universe and grab it!

A study of the tables created by both modules found that while e-Commerce maintains e-commerce type data (basically price and SKU) for each product node in the ec_product table, Ubercart maintains its corresponding data in the (you guessed it) uc_product table. Cool!

So after a brief historical period of dry runs (database backups and version control commits on a test server, people!), we arrived at the following script (which we simply executed in the temporarily enabled Execute PHP block afforded by the Devel module, but you could do it via a Php enabled page or external script run in the manner of update.php):

set_time_limit (0);
$result = db_query("SELECT nid, vid, sku, price FROM {ec_product}");
$count = 0;
    while ($ec_prod = db_fetch_object($result)) {
      $count++;
      $node = new stdClass();
      $node->nid = $ec_prod->nid;      
      $node->vid = $ec_prod->vid;      
      $node->model = $ec_prod->sku;
      $node->cost = $ec_prod->price;
      $node->list_price = $ec_prod->price;
      $node->sell_price = $ec_prod->price;
      // default values
      $node->weight_units = 'kg';
      $node->length_units = 'cm';
      $node->pkg_qty = 1;
      $node->default_qty = 1;
      $node->shippable = 1;
 
      // calculate unique hash
      $node->unique_hash = md5($node->vid . $node->nid . $node->model . $node->list_price . $node->cost . $node->sell_price . $node->weight . $node->weight_units . $node->dim_length . $node->dim_width . $node->dim_height . $node->length_units . $node->pkg_qty . $node->default_qty . $node->shippable . time());
 
      // integrate product node into ubercart as uc_product
      db_query("INSERT INTO {uc_products} (vid, nid, model, list_price, cost, sell_price, weight, weight_units, length, width, height, length_units, pkg_qty, default_qty, unique_hash, ordering, shippable) VALUES (%d, %d, '%s', %f, %f, %f, %f, '%s', %f, %f, %f, '%s', %d, %d, '%s', %d, %d)",
    $node->vid, $node->nid, $node->model, $node->list_price, $node->cost, $node->sell_price, $node->weight, $node->weight_units, $node->dim_length, $node->dim_width, $node->dim_height, $node->length_units, $node->pkg_qty, $node->default_qty, $node->unique_hash, $node->ordering, $node->shippable
  );
    }
drupal_set_message('count: ' . $count);

[disclaimer]Still can't get Geshi to play nice with the TinyMce editor.[/disclaimer]

The code depends heavily on the uc_product_insert() function found in uc_product.module .  Similar investigations might yield ways to recycle the order information, as well as product images, etc., but this is the basic process. Comments?

Many Thanks

This script and info was SOOOOOOO helpful. Thanks very much.

One point to consider is Ubercart requires unique SKUs for stock control while eCommerce inventory control is independent of SKUs. This script doesn't take into account inventory control considerations.

Thanks again!