Before you can use the split products modification, you will need to have made the changes as described under install_dimensional_support.txt. The mod described here extends the dimensional support!

Upload the included file split_products.php to the admin folder (same level as categories.php) and the accompanying language file split_products.php to admin/includes/languages/english/

----------------
Run the following sql in phpMyAdmin or similar tool:

DROP TABLE IF EXISTS products_split;
CREATE TABLE products_split (
  id int UNSIGNED NOT NULL auto_increment,
  products_id int(11) NOT NULL DEFAULT 0,
  products_weight decimal(5,2) not null,
  products_length DECIMAL(6,2) DEFAULT 12 NOT NULL,
  products_width DECIMAL(6,2) DEFAULT 12 NOT NULL,
  products_height DECIMAL(6,2) DEFAULT 12 NOT NULL,
  products_ready_to_ship TINYINT(1) DEFAULT 0 NOT NULL,
  value_fraction FLOAT(7,5) DEFAULT 0.5 NOT NULL,
  PRIMARY KEY (id),
  INDEX (products_id)
);

----------------
catalog/includes/database_tables.php

**ADD**

  define('TABLE_PRODUCTS_SPLIT', 'products_split');

----------------
admin/includes/database_tables.php

**ADD**

  define('TABLE_PRODUCTS_SPLIT', 'products_split');

----------------
admin/includes/filenames.php

**ADD**

  define('FILENAME_SPLIT_PRODUCT','split_product.php');

----------------
admin/includes/languages/english/categories.php

**ADD**

  define('TEXT_PRODUCTS_SPLIT_PRODUCT','Split in several items:');
  define('NAME_WINDOW_SPLIT_PRODUCTS_POPUP', 'Split Products');
  define('TEXT_MOUSE_OVER_SPLIT_PRODUCTS', 'Edit Split Items');
  define('TEXT_CANT_SPLIT_NEW_PRODUCT', 'New products must be saved to the database before they can be set as containing multiple boxes.');

----------------
admin/categories.php

Line 609-612

**AFTER**

            <td class="main"><?php echo TEXT_PRODUCTS_READY_TO_SHIP; ?></td>
            <td class="main"><?php echo tep_draw_separator('pixel_trans.gif', '24', '15') . '&nbsp;' . tep_draw_checkbox_field('products_ready_to_ship', '1', ($product['products_ready_to_ship'] == '1')) . TEXT_READY_SHIP_EXPLAIN; ?></td>
          </tr>

**ADD**

<!-- // begin split products -->
          <tr>
            <td class="main"><?php echo TEXT_PRODUCTS_SPLIT_PRODUCT; ?></td>
<?php
   if (isset($pInfo->products_id) && tep_not_null($pInfo->products_id)) {
      $check_split_query = tep_db_query("select count(*) as total from " . TABLE_PRODUCTS_SPLIT . " where products_id = '" . (int)$pInfo->products_id . "'");
      $check_split = tep_db_fetch_array($check_split_query);
?>
            <td class="main"><?php echo tep_draw_separator('pixel_trans.gif', '22', '15') . '&nbsp;' . ($check_split['total'] < 1 ? TEXT_NO : $check_split['total']); ?>&nbsp;&nbsp;<?php echo tep_draw_button(TEXT_MOUSE_OVER_SPLIT_PRODUCTS, 'pencil', 'javascript:void(0)', 'primary', array('params' => 'onclick="window.open(\'' . tep_href_link(FILENAME_SPLIT_PRODUCT, 'pid=' . $pInfo->products_id, 'NONSSL') . '\',\'' . NAME_WINDOW_SPLIT_PRODUCTS_POPUP . '\',\'menubar=yes,resizable=yes,scrollbars=yes,status=no,location=no,width=700,height=380\');return false"'));?></td>
          </tr>
<?php
  } else {
    echo '<td class="main">' . TEXT_CANT_SPLIT_NEW_PRODUCT . "</td></tr>\n";
  } // end split products
?>


***NEAR END OF FILE FIND:

          } elseif (isset($pInfo) && is_object($pInfo)) { // product info box contents
            $heading[] = array('text' => '<strong>' . tep_get_products_name($pInfo->products_id, $languages_id) . '</strong>');

            $contents[] = array('align' => 'center', 'text' => tep_draw_button(IMAGE_EDIT, 'document', tep_href_link(FILENAME_CATEGORIES, 'cPath=' . $cPath . '&pID=' . $pInfo->products_id . '&action=new_product')) . tep_draw_button(IMAGE_DELETE, 'trash', tep_href_link(FILENAME_CATEGORIES, 'cPath=' . $cPath . '&pID=' . $pInfo->products_id . '&action=delete_product')) . tep_draw_button(IMAGE_MOVE, 'arrow-4', tep_href_link(FILENAME_CATEGORIES, 'cPath=' . $cPath . '&pID=' . $pInfo->products_id . '&action=move_product')) . tep_draw_button(IMAGE_COPY_TO, 'copy', tep_href_link(FILENAME_CATEGORIES, 'cPath=' . $cPath . '&pID=' . $pInfo->products_id . '&action=copy_to')));


***ADD AFTER

            $contents[] = array('align' => 'center', 'text' => tep_draw_button(TEXT_MOUSE_OVER_SPLIT_PRODUCTS, 'pencil', 'javascript:void(0)', 'secondary', array('params' => 'onclick="window.open(\'' . tep_href_link(FILENAME_SPLIT_PRODUCT, 'pid=' . $pInfo->products_id, 'NONSSL') . '\',\'' . NAME_WINDOW_SPLIT_PRODUCTS_POPUP . '\',\'menubar=yes,resizable=yes,scrollbars=yes,status=no,location=no,width=700,height=380\');return false"')));


***FIND NEAR TOP OF FILE UNDERNEATH:

      case 'insert_product':
      case 'update_product':

***Find this section:

          $sql_data_array = array('products_quantity' => (int)tep_db_prepare_input($HTTP_POST_VARS['products_quantity']),
                                  'products_model' => tep_db_prepare_input($HTTP_POST_VARS['products_model']),
                                  'image_display' => tep_db_prepare_input($HTTP_POST_VARS['image_display']),
                                  'products_price' => tep_db_prepare_input($HTTP_POST_VARS['products_price']),
                                  'products_date_available' => $products_date_available,
                                  'products_weight' => (float)tep_db_prepare_input($HTTP_POST_VARS['products_weight']),
	                                'products_length' => (float)tep_db_prepare_input($HTTP_POST_VARS['products_length']),
	                                'products_width' => (float)tep_db_prepare_input($HTTP_POST_VARS['products_width']),
 	                                'products_height' => (float)tep_db_prepare_input($HTTP_POST_VARS['products_height']),
	                                'products_ready_to_ship' => (isset($HTTP_POST_VARS['products_ready_to_ship']) && (tep_db_prepare_input($HTTP_POST_VARS['products_ready_to_ship']) == '1') ? 1 : 0),
                                  'products_status' => tep_db_prepare_input($HTTP_POST_VARS['products_status']),
                                  'products_tax_class_id' => tep_db_prepare_input($HTTP_POST_VARS['products_tax_class_id']),
                                  'manufacturers_id' => (int)tep_db_prepare_input($HTTP_POST_VARS['manufacturers_id']));


***ADD after it:

        if (isset($HTTP_GET_VARS['pID'])) {
          $split_check_query = tep_db_query("select products_weight from " . TABLE_PRODUCTS_SPLIT . " where products_id = '" . (int)$HTTP_GET_VARS['pID'] . "'");
          if (tep_db_num_rows($split_check_query) > 0) { // calculate total weight from split
            $weight = 0;
            while ($check = tep_db_fetch_array($split_check_query)) {
              $weight += $check['products_weight'];
            }
						$sql_data_array['products_weight'] = $weight;
          }
        }

***SCROLL DOWN AND FIND:

          } elseif ($HTTP_POST_VARS['copy_as'] == 'duplicate') {
            // product table copy modified to copy ALL added fields
            $product_query = tep_db_query("select * from " . TABLE_PRODUCTS . " where products_id = " . (int)$products_id);
            $product = tep_db_fetch_array($product_query);
            unset($product['products_id']); // remove the original product id, this is automatically replaced with a new id
            $product['products_status'] = 0; // duplicate starts as not displayed in catalog as with original osCommerce code
            $product['products_date_added'] = 'now()'; // duplicate product is added now
						$product['products_last_modified'] = 'null'; // duplicate product has not been modified yet
            if (empty($product['products_date_available'])) $product['products_date_available'] = 'null'; // if empty keeps duplicate blank instead of 0000-00-00
            tep_db_perform(TABLE_PRODUCTS, $product); // insert the duplicate product
            $dup_products_id = tep_db_insert_id();

***ADD AFTER IT:

            // begin split products
            $split_query = tep_db_query('select * from ' . TABLE_PRODUCTS_SPLIT . ' where products_id = ' . (int)$products_id);
            while ($item = tep_db_fetch_array($split_query)) {
              tep_db_query('insert into ' . TABLE_PRODUCTS_SPLIT . " (products_id, products_weight, products_length, products_width, products_height, products_ready_to_ship, value_fraction) VALUES ('" . (int)$dup_products_id . "', '" . tep_db_input($item['products_weight']) . "', '" . tep_db_input($item['products_length']) . "', '" . tep_db_input($item['products_width']) . "', '" . tep_db_input($item['products_height']) . "', '" . tep_db_input($item['products_ready_to_ship']) . "', '" . tep_db_input($item['value_fraction']) . "')");
            }
            // end split products

------------------------
admin/includes/functions/general.php

***FIND the function definition for tep_remove_product

In that definition find the section that reads:

    tep_db_query("delete from " . TABLE_SPECIALS . " where products_id = '" . (int)$product_id . "'");
    tep_db_query("delete from " . TABLE_PRODUCTS . " where products_id = '" . (int)$product_id . "'");
    tep_db_query("delete from " . TABLE_PRODUCTS_TO_CATEGORIES . " where products_id = '" . (int)$product_id . "'");
    tep_db_query("delete from " . TABLE_PRODUCTS_TO_NOTES . " where products_id = '" . (int)$product_id . "'");
    tep_db_query("delete from " . TABLE_PRODUCTS_DESCRIPTION . " where products_id = '" . (int)$product_id . "'");
    tep_db_query("delete from " . TABLE_PRODUCTS_ATTRIBUTES . " where products_id = '" . (int)$product_id . "'");
    tep_db_query("delete from " . TABLE_CUSTOMERS_BASKET . " where products_id = '" . (int)$product_id . "' or products_id like '" . (int)$product_id . "{%'");
    tep_db_query("delete from " . TABLE_CUSTOMERS_BASKET_ATTRIBUTES . " where products_id = '" . (int)$product_id . "' or products_id like '" . (int)$product_id . "{%'");

***and immediately below it ADD:

    tep_db_query("delete from " . TABLE_PRODUCTS_SPLIT . " where products_id = '" . (int)$product_id . "'");

----------------
catalog/includes/classes/shopping_cart.php

ADD the following code (before the last } )


// get_products_for_packaging is a special function for split product support in the class packing
// assumes that you have added the sql for upsxml, which adds the table products_split
   function get_products_for_packaging() {
      if (!is_array($this->contents)) return false;
      $products_array = array();
      // get the list of product information
      $products = $this->get_products();
      // cycle through list
      foreach ($products as $product) {
        $split_query = tep_db_query("select * from " . TABLE_PRODUCTS_SPLIT . " where products_id = " . (int)$product['id']);
        // is this a split product?
        if (tep_db_num_rows($split_query) > 0) {
          // save the total prices of the split product
          $product_price = $product['price'];
          $product_final_price = $product['final_price'];
          while ($split_info = tep_db_fetch_array($split_query)) {
            // for each piece of the product replace only the information that is unique to the piece
            // other information from the product will remain unchanged
            $product['weight'] = $split_info['products_weight'];
            $product['length'] = $split_info['products_length'];
            $product['width'] = $split_info['products_width'];
            $product['height'] = $split_info['products_height'];
            $product['ready_to_ship'] = $split_info['products_ready_to_ship'];
            $product['price'] = round(($split_info['value_fraction'] * $product_price), 4);
            $product['final_price'] = round(($split_info['value_fraction'] * $product_final_price), 4);
            // save the updated product piece
            $products_array[] = $product;
          } // end while
        } else {
          // not a split product, save it directly
          $products_array[] = $product;
        }
      } // end foreach

      return $products_array;
   }

----------------
In catalog/includes/classes/packing.php (from version 1.3.0 and upward)

you will find around line 45:

        if (method_exists($cart, 'get_products_for_packaging') ) {
          $productsArray = $cart->get_products_for_packaging();
        } else {
          $productsArray = $cart->get_products();
        }

Once you added the function get_products_for_packaging to the class shopping_cart, this function will be used to pick up the details for packaging. If you have mods to the original get_products function be assured that these are now copied in the function get_products_for_packaging since it uses the get_products function to get the original list!!
----------------
From admin/categories.php you can edit the splitting of the products in a pop-up window, the info and button for opening the pop-up window are found right under the place where you enter length, width, height and ready-to-ship for a product (see screenshot_split_products.gif). Any entries of dimensions and ready-to-ship will be superseded by those for the items that the product is split into.
Make sure the value_fraction for the entries for a product add up to 1 (otherwise the splitting of the value will have ramifications on insurance values!)