Index: updates.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/ecommerce/updates.inc,v retrieving revision 1.6.2.4 diff -u -F^f -r1.6.2.4 updates.inc --- updates.inc 1 Jul 2005 03:56:25 -0000 1.6.2.4 +++ updates.inc 23 Oct 2005 01:19:53 -0000 @@ -15,7 +15,8 @@ '2005-04-11' => 'update_5', '2005-04-23: first update since Drupal 4.6 release' => 'update_6', '2005-06-09' => 'update_7', - '2005-06-30' => 'update_8' + '2005-06-30' => 'update_8', + '2005-10-22' => 'update_11' ); function update_1() { @@ -120,6 +121,16 @@ function update_8() { return $ret; } +function update_11() { + if ($GLOBALS['db_type'] == 'mysql') { + $ret[] = update_sql('ALTER TABLE {ec_product} ADD parent INT(10) NOT NULL, ADD children VARCHAR(255) NOT NULL, ADD variation LONGTEXT NOT NULL'); + } + else { + // pgsql goes here. + } + return $ret; +} + function update_sql($sql) { $edit = $_POST["edit"]; $result = db_query($sql); Index: cart/cart.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/ecommerce/cart/cart.module,v retrieving revision 1.44.2.10 diff -u -F^f -r1.44.2.10 cart.module --- cart/cart.module 12 Oct 2005 14:24:15 -0000 1.44.2.10 +++ cart/cart.module 23 Oct 2005 01:19:55 -0000 @@ -132,7 +132,7 @@ function theme_cart_display_block() { foreach ($item as $i) { $node = node_load(array("nid" => $i->nid)); $total += product_adjust_price($node) * $i->qty; - $output .= l("$i->qty x $node->title", "node/$node->nid"). "
"; + $output .= l("$i->qty x $node->title", "node/" .($node->parent ? $node->parent : $node->nid)). "
"; } $output .= "
". payment_format($total) . "
"; $output .= '
'. t('Ready to checkout?', array('%checkout-url' => url('cart/checkout'))) .'
'; @@ -197,7 +197,7 @@ function cart_page() { switch (arg(1)) { case 'add': - cart_add_item(arg(2), arg(3)); + cart_add_item(arg(2), arg(3), 'redirect', $edit); $output = theme("cart_view"); break; @@ -517,8 +517,27 @@ function cart_get_id() { function cart_add_item($nid, $qty = 1, $action = "redirect", $data = array()) { /* Make sure we can add a product */ + $bool_cart_add = true; /* we assume we can until we can't */ $node = node_load(array('nid' => $nid)); - $bool_cart_add = module_invoke($node->ptype, 'productapi', $node, 'cart add item'); + + /* if this is a parent product, we do not add this directly, but the child + * instead. The parent is not really a product, just a place holder for + * all the related/sub products. */ + if ($node->children) { + if ($subproduct = module_invoke($node->ptype, 'productapi', $node, 'cart get subproduct', $data)) { + /* we have a sub product. We need to change the the parent to the + * child so the child is added to the cart, and not the parent */ + $node = $subproduct; + $nid = $node->nid; + } + else { + $bool_cart_add = false; + } + } + + /* if the parent has a subproduct then the $bool_cart_add may already be + * no, so why bother calling the hook */ + $bool_cart_add = $bool_cart_add ? module_invoke($node->ptype, 'productapi', $node, 'cart add item') : false; if (is_null($bool_cart_add)) { $bool_cart_add = true; } Index: product/product.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/ecommerce/product/product.module,v retrieving revision 1.58.2.4 diff -u -F^f -r1.58.2.4 product.module --- product/product.module 26 Sep 2005 20:48:33 -0000 1.58.2.4 +++ product/product.module 23 Oct 2005 01:19:58 -0000 @@ -41,7 +41,7 @@ function theme_product_view_collection() $columns = 3; $rows = 5; - $result = pager_query(db_rewrite_sql('SELECT n.nid FROM {node} n INNER JOIN {ec_product} p ON n.nid = p.nid WHERE n.status = 1 ORDER BY n.sticky DESC, n.created DESC'), $rows * $columns, 0); + $result = pager_query(db_rewrite_sql('SELECT n.nid FROM {node} n INNER JOIN {ec_product} p ON n.nid = p.nid WHERE n.status = 1 AND p.parent = 0 ORDER BY n.sticky DESC, n.created DESC'), $rows * $columns, 0); $output = ''; for ($i = 0; $node = db_fetch_object($result); $i++) { @@ -170,6 +170,12 @@ function product_link($type, $node = nul $links[] = t('sold out'); } } + if (user_access('create products') && !$node->parent && module_invoke($node->ptype, 'productapi', $node, 'subproduct_types')) { + $links[] = l(t('add sub product'), "node/add/product/parent/$node->nid", array(), drupal_get_destination()); + } + if ($node->parent) { + $links[] = l(t('view parent product'), "node/$node->parent"); + } } return $links; @@ -183,6 +189,10 @@ function product_load($node) { if ($products[$node->nid] === NULL) { $product = db_fetch_object(db_query('SELECT * FROM {ec_product} WHERE nid = %d', $node->nid)); + + /* unpack the variation fields stored in the ec_product table */ + $product->variation = unserialize($product->variation); + /* Merge the product info for the specific type. */ if ($product_type = module_invoke($product->ptype, 'productapi', $product, 'load')) { foreach ($product_type as $key => $value) { @@ -233,6 +243,12 @@ function product_menu($may_cache) { 'callback' => 'product_to_product', 'access' => user_access('administer products'), 'type' => MENU_LOCAL_TASK, 'weight' => 2); } + + if (db_result(db_query(db_rewrite_sql("SELECT COUNT(p.nid) FROM {ec_product} p WHERE p.nid = %d AND p.children <> ''"), arg(1))) > 0) { + $items[] = array('path' => 'node/' .arg(1) .'/subproducts', 'title' => t('sub products'), + 'callback' => 'product_sub_products', 'access' => user_access('administer products'), + 'type' => MENU_LOCAL_TASK, 'weight' => 3); + } } } @@ -318,6 +334,28 @@ function product_to_product() { } } +function product_sub_products() { + drupal_set_title(t('view sub products')); + $node = node_load(array('nid' => arg(1))); + $products = product_get_variations($node); + + $headers = array( + array('data' => t('title')), + array('data' => t('operations'), 'colspan' => 2) + ); + + foreach ($products as $nid => $product) { + $rows[] = array( + array('data' => $product->title), + array('data' => l(t('view'), "node/$nid")), + array('data' => l(t('edit'), "node/$nid/edit", array(), drupal_get_destination())), + ); + } + $output.= theme('table', $headers, $rows); + $output.= form_item('', t('%add addition sub products', array('%add' => l(t('add'), "node/add/product/parent/$node->nid", array(), drupal_get_destination())))); + print theme('page', $output); +} + /** * Implementation of hook_node_name(). */ @@ -481,7 +519,9 @@ function product_get_base_form_elements( } $output .= form_textfield(t('SKU'), 'sku', $edit->sku, 25, 50, t('If you have an unique identifier for this product from another system or database, enter that here. This is optional, as system IDs are automatically created for each product.')); $output .= form_radios(t("'Add to cart' link"), 'hide_cart_link', $edit->hide_cart_link, array(t("Visible"), t("Hidden")), t("Choose whether or not you want the 'Add to cart' link visible for this product.")); - + if ($edit->parent) { + $output .= form_hidden('parent', $edit->parent); + } return $output; } @@ -492,12 +532,35 @@ function product_get_base_form_elements( */ function product_wizard_form($edit) { + /* check to see if we have a parent product. and load it as the template */ + + if (!$edit && arg(3) == 'parent') { + if ($parent = node_load(array('nid' => arg(4)))) { + if ($ptypes = module_invoke($parent->ptype, 'productapi', $edit, 'subproduct_types')) { + foreach ($parent as $key => $value) { + if (!in_array($key, array('nid', 'status', 'children', 'parent', 'ptype'))) { + $edit->$key = $parent->$key; + } + } + if (count($ptypes) == 1) { + $edit->ptype = $ptypes[0]; + } + } + $edit->parent = $parent->nid; + } + } + /* Ask node.module to build the node form and we'll strip off the form tags. */ $node_form = ($edit->uid && $edit->name && $edit->type) ? node_form($edit) : node_add('product'); $node_form = preg_replace("''", '', $node_form); $node_form = preg_replace("''", '', $node_form); - $node_form .= product_form_product_types(); + if ($edit->ptype) { + $node_form .= form_submit(t('Create product')); + } + else { + $node_form .= product_form_product_types(); + } return form($node_form, 'post', null, array('id' => 'node-form')); } @@ -513,6 +576,16 @@ function product_form_product_types() { form_set_error('ptype', t('No product types modules are installed! Please install a product type module such as tangible.module or file.module.')); } else { + /* purge types that are not compatible with the parent product */ + if ($edit->parent) { + $parent = node_load(array('nid' => $edit->parent)); + $subproduct_types = module_invoke($parent->ptype, 'productapi', $edit, 'subproduct_types'); + foreach ($ptypes as $key => $value) { + if (!array_key_exists($key, $subproduct_types)) { + unset($ptypes[$key]); + } + } + } $ptypes_display = $ptypes_display + $ptypes; } $output = form_select(t('Type of product to create'), 'ptype', $edit->ptype, $ptypes_display, t('You cannot change the product type once it\'s created.')); @@ -620,6 +693,12 @@ function product_form_validate(&$edit) { function product_save($node) { if ($node->ptype) { $node->is_recurring = ($node->price_interval) ? 1 : 0; + + /* simple product variations will be stored in the ec_product table. + * More complex ones may need to be stored in another table. */ + if ($node->variation) { + $node->variation = serialize($node->variation); + } $fields = product_fields(); /* @@ -655,6 +734,23 @@ function product_save($node) { module_invoke(variable_get('shipping_method', 'none'), 'shippingapi', $node, 'per_product_insert'); } } + + /* update the parent product's list of children. */ + + if ($node->parent) { + if ($parent = node_load(array('nid' => $node->parent))) { + if ($parent->children) { + $children = explode(',', $parent->children); + } + else { + $children = array(); + } + if (!in_array($node->nid, $children)) { + $children[] = $node->nid; + db_query('UPDATE {ec_product} SET children = \'%s\' WHERE nid = %d', implode(',', $children), $parent->nid); + } + } + } } } @@ -1026,9 +1122,24 @@ function product_send_recurring_payment_ } /** + * retrieve all the subproducts for a parent product + */ +function product_get_variations($node) { + $products = array(); + + foreach (explode(',', $node->children) as $nid) { + if ($child = node_load(array('nid' => $nid))) { + $products[$child->nid] = $child; + } + } + + return $products; +} + +/** * The names of the database columns in the table. */ function product_fields($table = 'ec_product') { - return array('nid', 'sku', 'price', 'is_recurring', 'price_interval', 'price_unit', 'price_cycle', 'auto_charge', 'ptype', 'hide_cart_link'); + return array('nid', 'sku', 'price', 'is_recurring', 'price_interval', 'price_unit', 'price_cycle', 'auto_charge', 'ptype', 'hide_cart_link', 'parent', 'children', 'children', 'variation'); } ?> Index: product/product.mysql =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/ecommerce/product/product.mysql,v retrieving revision 1.6 diff -u -F^f -r1.6 product.mysql --- product/product.mysql 5 Mar 2005 19:47:54 -0000 1.6 +++ product/product.mysql 23 Oct 2005 01:19:58 -0000 @@ -9,6 +9,9 @@ auto_charge tinyint(3) unsigned NOT NULL default '0', ptype varchar(75) NOT NULL default '', hide_cart_link int(2) unsigned NOT NULL default '0', + parent int(10) NOT NULL default '0', + children varchar(255) NOT NULL default '', + variation longtext NOT NULL, UNIQUE KEY nid (nid), KEY ptype (ptype) ) TYPE=MyISAM; Index: store/store.mysql =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/ecommerce/store/Attic/store.mysql,v retrieving revision 1.16.2.4 diff -u -F^f -r1.16.2.4 store.mysql --- store/store.mysql 2 Oct 2005 18:15:26 -0000 1.16.2.4 +++ store/store.mysql 23 Oct 2005 01:19:59 -0000 @@ -108,6 +108,9 @@ auto_charge tinyint(3) unsigned NOT NULL default '0', ptype varchar(75) NOT NULL default '', hide_cart_link int(2) unsigned NOT NULL default '0', + parent int(10) NOT NULL default '0', + children varchar(255) NOT NULL default '', + variation longtext NOT NULL, UNIQUE KEY nid (nid), KEY ptype (ptype) ) TYPE=MyISAM;