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..66e2bd0 --- /dev/null +++ modules/uc_recurring_order/uc_recurring_order.module @@ -0,0 +1,280 @@ + 'Recurring orders', + 'description' => 'Edit recurring order information.', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('uc_recurring_order_form'), + 'access arguments' => array('administer store'), + 'type' => MENU_LOCAL_TASK, + 'weight' => 0, + ); + return $items; +} + +/** + * 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) { + $form['recurring_option'] = array( + '#type' => 'select', + '#title' => t('Recurring order'), + '#description' => t('Select to have this order automatically.'), + '#options' => _uc_recurring_order_recurring_options(), + ); + $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 = $form_state['values']['recurring_option']; + $next_renewal = strtotime('+'. $recurring); + if ($next_renewal > 0) { + $_SESSION['recurring_option'] = $recurring_option; + drupal_set_message(t('@recurring. 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']); + } + } +} + +/** + * + */ +function _uc_recurring_order_recurring_options() { + return array( + '' => t('Single order (not recurring)'), + '1 months' => t('Monthly order'), + ); +} + +/** + * 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 enter discount coupons. + */ +function uc_recurring_order_pane_checkout($op, &$arg1, $arg2) { + switch ($op) { + case 'view': + drupal_add_js(drupal_get_path('module', 'uc_recurring_order') .'/uc_recurring_order.js'); + + // Use coupon code 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('Recurring order'), + '#default_value' => $recurring, + '#options' => _uc_recurring_order_recurring_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 TRUE; + } + + if ($arg2['recurring_option']) { + $recurring_options = _uc_recurring_order_recurring_options(); + $recuring = $arg2['recurring_option']; + $next_renewal = strtotime('+'. $recurring); + if ($next_renewal > 0) { + $_SESSION['recurring_option'] = $recurring_option; + drupal_set_message(t('@recurring. 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']); + } + } + return TRUE; + + case 'settings': + $form['uc_recurring_order_pane_description'] = array( + '#type' => 'textarea', + '#title' => t('Checkout pane message'), + '#default_value' => variable_get('uc_recurring_pane_description', 'Select to have this order automatically.'), + ); + return $form; + } +} + +/** + * 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 = $_SESSION['recurring_option']; + $next_renewal = strtotime('+'. $recurring); + if ($next_renewal > 0 && variable_get('uc_recurring_checkout_process', TRUE)) { + if (uc_recurring_order_process_order($arg1) === 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; + } +} + +/** + * 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']; + } +} + +/** + * 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; +}