diff --git modules/uc_recurring_order/uc_recurring_order.info modules/uc_recurring_order/uc_recurring_order.info new file mode 100644 index 0000000..d9bdd3d --- /dev/null +++ modules/uc_recurring_order/uc_recurring_order.info @@ -0,0 +1,5 @@ +name = Recurring Fees - Order +description = Recurring Fees for duplicating entire orders. +dependencies[] = uc_recurring +package = Ubercart - payment +core = 6.x diff --git modules/uc_recurring_order/uc_recurring_order.module modules/uc_recurring_order/uc_recurring_order.module new file mode 100644 index 0000000..229c773 --- /dev/null +++ modules/uc_recurring_order/uc_recurring_order.module @@ -0,0 +1,360 @@ + 'Recurring orders', + 'description' => 'Edit recurring order information.', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('uc_recurring_order_settings_form'), + 'access arguments' => array('administer store'), + 'type' => MENU_LOCAL_TASK, + 'weight' => 0, + ); + return $items; +} + +/** + * Settings for recurring orders + */ +function uc_recurring_order_settings_form($form_state) { + drupal_set_title(t('Recurring Order Settings')); + $intoptions = _uc_recurring_order_recurring_options(); + $form['pane_settings'] = array( + '#type' => 'fieldset', + '#title' => t('Pane Text Settings'), + '#collapsible' => TRUE, + ); + $form['pane_settings']['uc_recurring_order_pane_description'] = array( + '#type' => 'textarea', + '#title' => t('Checkout pane message'), + '#default_value' => t(variable_get('uc_recurring_pane_description', 'Select to have this order automatically repeat,')), + ); + $form['pane_settings']['uc_recurring_order_pane_title'] = array( + '#type' => 'textfield', + '#title' => t('Checkout and Review Pane Title'), + '#default_value' => t(variable_get('uc_recurring_order_pane_title', 'Recurring Order')), + ); + $form['pane_settings']['uc_recurring_order_pane_empty'] = array( + '#type' => 'textarea', + '#title' => t('No Recurring Order Text'), + '#description' => t('Text to display on the order review page if customer has not signed up for a recurring order.'), + '#default_value' => t(variable_get('uc_recurring_order_pane_empty', 'This order will not repeat.')), + ); + $form['interval_options'] = array( + '#type' => 'select', + '#multiple' => TRUE, + '#options' => $intoptions, + '#default_value' => variable_get('uc_recurring_order_interval_options', $intoptions), + '#title' => t('Available Recurring Interval Options'), + ); + + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Save'), + ); + + return $form; +} + +function uc_recurring_order_settings_form_submit($form, &$form_state) { + $intervals = array(); + foreach ($form_state['values']['interval_options'] as $int => $option) { + $intervals[check_plain($option)] = check_plain($option); + } + variable_set('uc_recurring_order_interval_options', $intervals); + variable_set('uc_recurring_pane_description', check_plain($form_state['values']['uc_recurring_order_pane_description'])); + variable_set('uc_recurring_order_pane_title', check_plain($form_state['values']['uc_recurring_order_pane_title'])); + variable_set('uc_recurring_order_pane_empty', check_plain($form_state['values']['uc_recurring_order_pane_empty'])); +} + + +/** + * Implementation of hook_cart_pane(). + */ +function uc_recurring_order_cart_pane($items) { + $panes[] = array( + 'id' => 'recurring', + 'body' => drupal_get_form('uc_recurring_order_pane_cart'), + 'title' => t('Recurring order'), + 'desc' => t("Allows shoppers to select to have their order automatically re-occur."), + 'weight' => 1, + 'enabled' => TRUE, + ); + return $panes; +} + +/** + * Cart pane recurring form. + */ +function uc_recurring_order_pane_cart($form_state) { + $options = array(); + $intervals = variable_get('uc_recurring_order_interval_options', _uc_recurring_order_recurring_options()); + $values = _uc_recurring_order_recurring_options(); + foreach ($intervals as $int => $option) { + $options[$option] = $values[$option]; + } + + $form['recurring_option'] = array( + '#type' => 'select', + '#title' => t(variable_get('uc_recurring_order_pane_title', 'Recurring Order')), + '#description' => t(variable_get('uc_recurring_pane_description', 'Select to have this order automatically repeat,')), + '#options' => $options, + '#default_value' => $_SESSION['recurring_option'], + ); + $form['apply'] = array( + '#type' => 'submit', + '#value' => t('Apply to order'), + ); + return $form; +} + +function uc_recurring_order_pane_cart_submit($form, &$form_state) { + if ($form_state['values']['recurring_option']) { + $recurring_options = _uc_recurring_order_recurring_options(); + $recurring_option = check_plain($form_state['values']['recurring_option']); + $next_renewal = strtotime('+'. $recurring_option); + if ($next_renewal > 0) { + $_SESSION['recurring_option'] = $recurring_option; + drupal_set_message(t('Your next order after this will occur on @next.', array('@recurring' => $recurring_options[$recurring], '@next' => format_date($next_renewal, 'short')))); + } + else { + unset($_SESSION['uc_recurring_order']); + } + } +} + +/** + * Default options + */ +function _uc_recurring_order_recurring_options() { + return array( + '0 months' => t('Single order (not recurring)'), + '1 months' => t('Monthly order'), + '3 months' => t('Every Three Months'), + '6 months' => t('Every 6 Months'), + '12 months' => t('Once a year'), + ); +} + +/** + * Implementation of hook_checkout_pane(). + * + * Show a pane just above the order total that allows shoppers to select + * recurring option for the order. + */ +function uc_recurring_order_checkout_pane() { + $panes[] = array( + 'id' => 'recurring', + 'callback' => 'uc_recurring_order_pane_checkout', + 'title' => t('Recurring order'), + 'desc' => t("Allows shoppers to select to have their order automatically re-occur."), + 'weight' => 5, + 'process' => TRUE, + ); + return $panes; +} + +/** + * Checkout Pane callback function. + * + * Used to display a form in the checkout process so that customers + * can select recurring/repeat option. + */ +function uc_recurring_order_pane_checkout($op, &$arg1, $arg2) { + switch ($op) { + case 'view': + $options = array(); + $intervals = variable_get('uc_recurring_order_interval_options', _uc_recurring_order_recurring_options()); + $values = _uc_recurring_order_recurring_options(); + foreach ($intervals as $int => $option) { + $options[$option] = $values[$option]; + } + + // Use recurring order info from cart pane if available. + if ($_SESSION['recurring_option']) { + $recurring = $_SESSION['recurring_option']; + unset($_SESSION['recurring_option']); + } + else { + $recurring = $arg1->data['recurring_option']; + } + + $description = t('Select to have this order automatically.'); + $contents['recurring_option'] = array( + '#type' => 'select', + '#title' => t(variable_get('uc_recurring_order_pane_title', 'Recurring Order')), + '#default_value' => $recurring, + '#options' => $options, + ); + return array('description' => $description, 'contents' => $contents); + + case 'process': + // Can't renew orders that include product with recurring payments. + if (($products = uc_recurring_get_recurring_products_in_order($order))) { + unset($_SESSION['uc_recurring_order']); + drupal_set_message(t('Unable to create recurring order when it contains recurring products'), 'warning'); + return FALSE; + } + + if ($arg2['recurring_option']) { + $recurring_options = _uc_recurring_order_recurring_options(); + $recurring = check_plain($arg2['recurring_option']); + $next_renewal = strtotime('+'. $recurring); + if ($next_renewal > 0) { + $arg1->data['recurring_option'] = $recurring; + } + else { + unset($_SESSION['recurring']); + } + } + return TRUE; + + case 'review': + if ($arg1->data['recurring_option']) { + $next_renewal = strtotime('+'. $arg1->data['recurring_option']); + $review[] = array('title' =>t(variable_get('uc_recurring_order_pane_title', 'Recurring Order')), 'data' =>t('Your next order after this will occur on @next.', array('@recurring' => $arg1->data['recurring_option'], '@next' => format_date($next_renewal, 'short')))); + } + else { + $review[] = array('title' =>t(variable_get('uc_recurring_order_pane_title', 'Recurring Order')), 'data' => t(variable_get('uc_recurring_order_pane_empty', 'This order will not repeat.'))); + } + return $review; + } +} + +/** + * Implementation of hook_order(). + */ +function uc_recurring_order_order($op, &$arg1, $arg2) { + switch ($op) { + // TODO: Allow admin to create a recurring order from "create order" page. + case 'submit': + $recurring = $arg1->data['recurring_option']; + $next_renewal = strtotime('+'. $recurring); + if ($next_renewal > time() && variable_get('uc_recurring_checkout_process', TRUE)) { + if (uc_recurring_order_process_order($arg1, $recurring) === FALSE) { + return array(array('pass' => FALSE, 'message' => t('Your order cannot be completed, because we could not process your recurring payment. Please review your payment details and contact us to complete your order if the problem persists.'))); + } + } + break; + case 'save': + db_query("UPDATE {uc_recurring_users} SET fee_amount = %f WHERE order_id = %d", uc_order_get_total($arg1), $arg1->order_id); + if (db_affected_rows !== 0) { + uc_order_comment_save($arg1->order_id, 0, t('The recurring order amount was updated.'), 'admin'); + } + break; + } +} + +/** + * Process a recurring order. + */ +function uc_recurring_order_process_order($order, $recurring) { + if (variable_get('uc_recurring_order_enabled', TRUE)) { + $payment_method = !empty($order->payment_method) ? $order->payment_method : 'default'; + if (!($fee_handler = uc_recurring_get_recurring_info($payment_method))) { + drupal_set_message(t('A handler for processing and renewing recurring fees cannot be found for the @payment-method payment method.', array('@payment-method' => $order->payment_method)), 'error'); + return FALSE; + } + + // Create a new fee object. + $fee = new StdClass(); + $fee->uid = $order->uid; + + $fee->fee_handler = $fee_handler['fee handler']; + + $fee->created = time(); + $fee->order_id = $order->order_id; + + // If the product fee amount is 0, it means we need to use the product + // price. This allows recurring fees to be adjusted by attributes. + $fee->fee_amount = $order->order_total; + + // Add the product's title as the order title. + $fee->fee_title = t('Renewal of product @title', array('@title' => $product['product']->title)); + + $fee->next_charge = strtotime('+'. $recurring); + $fee->regular_interval = $recurring; + $fee->remaining_intervals = -1; // hard coded to unlimited + $fee->charged_intervals = 0; + + $products = $order->products; + foreach($products as $index => $product) { + unset($products[$index]->order_product_id); + unset($products[$index]->order_id); + } + + $fee->data = array( + 'uc_recurring_order' => array( + 'products' => $products, + ), + 'recurring orders' => array(), + ); + $fee->attempts = 0; + $fee->pfid = NULL; + $fee->order_product_id = NULL; + $fee->own_handler = !empty($fee_handler['own handler']); + + drupal_alter('recurring_fee_user_create', $fee); + + if (uc_recurring_invoke($fee->fee_handler, 'process callback', array($order, &$fee))) { + $rfid = uc_recurring_fee_user_save($fee); + uc_order_comment_save($order->order_id, $user->uid, t('Recurring fee @rfid added to order.', array('@recurring-view-fee' => url('admin/store/orders/recurring/view/fee/'. $rfid), '@rfid' => $rfid))); + } + else { + return FALSE; + } + } + return $return; +} + +/** + * Implements hook_recurring_renewal_pending() + */ +function uc_recurring_order_recurring_renewal_pending(&$order, $fee) { + // recreate order + if (!empty($fee->data['uc_recurring_order'])) { + $order->products = $fee->data['uc_recurring_order']['products']; + $result = db_query("SELECT * FROM {uc_order_line_items} WHERE order_id = %d", $fee->order_id); + while ($line_item = db_fetch_array($result)) { + uc_order_line_item_add($order->order_id, $line_item['type'], $line_item['title'], $line_item['amount'], $line_item['weight'], $line_item['data']); + } + } +} + +/** + * Alter the Edit screen to show the products in the order. + */ +function uc_recurring_order_form_uc_recurring_admin_edit_form_alter(&$form, $form_state) { + $rfid = $form['#parameters'][2]; + + $fee = uc_recurring_fee_user_load($rfid); + + if ($fee->data['uc_recurring_order']) { + foreach($fee->data['uc_recurring_order']['products'] as $product) { + $row = array( + 'title' => $product->title, + 'qty' => $product->qty, + 'price' => $product->price, + ); + $rows[] = $row; + } + $form['products'] = array( + '#type' => 'fieldset', + '#title' => t('Products'), + '#description' => theme('table', array('Product', 'Quantity', 'Price'), $rows), + '#weight' => -10, + ); + } +} diff --git uc_recurring.install uc_recurring.install index 3233d1a..22aa060 100644 --- uc_recurring.install +++ uc_recurring.install @@ -171,7 +171,7 @@ function uc_recurring_schema() { 'description' => 'The product ID.', 'type' => 'int', 'unsigned' => TRUE, - 'not null' => TRUE, + 'not null' => FALSE, 'default' => 0, ), 'own_handler' => array( @@ -496,3 +496,12 @@ function uc_recurring_update_6008() { return $ret; } + +/** + * Allow product ID to be NULL if this recurring fee is not for a specific product. + */ +function uc_recurring_update_6009() { + $ret = array(); + db_change_field($ret, 'uc_recurring_users', 'order_product_id', 'order_product_id', array('type' => 'int', 'size' => 'small', 'unsigned' => TRUE, 'not null' => FALSE, 'default' => 0)); + return $ret; +}