Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
ecommerce_checkout.inc
1 <?php
17 require_once SQ_CORE_PACKAGE_PATH.'/page/page.inc';
18 require_once SQ_PACKAGES_PATH.'/ecommerce/lib/ecommerce_cart_processor.inc';
19 require_once SQ_PACKAGES_PATH.'/ecommerce/ecommerce_order/ecommerce_order.inc';
20 
21 
34 class Ecommerce_Checkout extends Page
35 {
36 
45 
46 
51  var $insufficient_quantities = FALSE;
52 
53 
62  function __construct($assetid=0)
63  {
64  // initialise the temporary order container so as to avoid extra checking
65  if (!isset($_SESSION['sq_local_cart_contents'])) {
66  $_SESSION['sq_local_cart_contents'] = Array();
67  }
68 
69  $this->_ser_attrs = TRUE;
70 
71  parent::__construct($assetid);
72 
73  }//end constructor
74 
75 
83  {
84  return Array(
85  'checkout' => 'Checkout',
86  'confirmation' => 'Confirmation',
87  'failure' => 'Failure',
88  'receipt' => 'Receipt',
89  );
90 
91  }//end _getAvailableContexts()
92 
93 
107  function _getAllowedLinks()
108  {
109  $allowed_links = parent::_getAllowedLinks();
110  $allowed_links[SQ_LINK_NOTICE]['ecommerce_delivery_method'] = Array('card' => 'M', 'exclusive' => FALSE);
111  $allowed_links[SQ_LINK_NOTICE]['ecommerce_order'] = Array('card' => 'M', 'exclusive' => FALSE);
112 
113  return $allowed_links;
114 
115  }//end _getAllowedLinks()
116 
117 
128  protected function _getName($short_name=FALSE, $contextid=NULL)
129  {
130  // No context specified, using the current context
131  if ($contextid === NULL) {
132  $contextid = $GLOBALS['SQ_SYSTEM']->getContextId();
133  }//end if
134 
135  // Obtain the attribute value for Name from the specified Context
136  $attr = ($short_name) ? 'short_name' : 'name';
137  $values = $GLOBALS['SQ_SYSTEM']->am->getAttributeValuesByName($attr, $this->type(), Array($this->id), $contextid);
138  if (empty($values) === TRUE) {
139  return parent::_getName($short_name, $contextid);
140  } else {
141  return $values[$this->id];
142  }
143 
144  }//end _getName()
145 
146 
153  function printBody()
154  {
155  // since we are doing a lot of editing stuff with the checkout page, it's
156  // probably best if we tie everything together using db2
157  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
158 
159  // process interface. this determines the print context
160  $this->_processInterface();
161 
162  // error occured in processing interface, just return
163  if (empty($this->print_context)) {
164  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
165  return;
166  }
167 
168  // print context
169  $this->_printContext();
170  if (array_get_index($this->_tmp, 'delete_all_orders_after_printing', FALSE)) {
171 
172  if (!isset($_SESSION['sq_local_cart_contents']['editing_order'])) {
173  // clear global cart
174  $cart = new Ecommerce_Cart_Processor($this->id);
175  $cart->clearCart();
176  unset($cart);
177  }
178 
179  // clear local cart
180  $_SESSION['sq_local_cart_contents'] = Array();
181  }
182 
183  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
184 
185  }//end printBody()
186 
187 
194  function _retrieveOrder()
195  {
196  $order_id = array_get_index($_SESSION['sq_local_cart_contents'], 'pending_order_id', FALSE);
197  if (!$order_id && isset($_SESSION['sq_local_cart_contents']['editing_order']) && $_SESSION['sq_local_cart_contents']['editing_order']) {
198  // ok are we editing an existing order
199  $order_id = array_get_index($_SESSION['sq_local_cart_contents'], 'order_id', FALSE);
200  }
201 
202  if (isset($order_id) && $order_id) {
203  return $GLOBALS['SQ_SYSTEM']->am->getAsset($order_id);
204  } else {
205  return NULL;
206  }
207 
208  }//_retrieveOrder()
209 
210 
220  function _logOrderAction($action, &$order=NULL)
221  {
222  if (is_null($order)) {
223  $order = $this->_retrieveOrder();
224  if (is_null($order)) {
225  return;
226  }
227  }
228 
229  $order_history = $order->attr('order_history');
230  $index = date('r');
231  while (isset($order_history[$index])) {
232  $index .= ' ';
233  }
234  $order_history[$index] = $action;
235  $order->setAttrValue('order_history', $order_history);
236 
237  $GLOBALS['SQ_SYSTEM']->setRunLevel(SQ_RUN_LEVEL_FORCED);
238  $order->saveAttributes();
239  $GLOBALS['SQ_SYSTEM']->restoreRunLevel();
240 
241  }//_logOrderAction()
242 
243 
253  function _processInterface()
254  {
255  // edit Ecommerce Order. Check permission and lock order
256  // if the user doesn't have permission or lock, return without setting
257  // print_context
258 
259  $order_id = array_get_index($_REQUEST, 'edit_order_id', 0);
260 
261  if (!empty($order_id)) {
262  $order = $GLOBALS['SQ_SYSTEM']->am->getAsset($order_id);
263 
264  $this->_logOrderAction(translate('ecom_order_history_existing_order_provided'), $order);
265 
266  // order exists, and has write access
267  if (!empty($order) && $order->writeAccess()) {
268 
269  if ($GLOBALS['SQ_SYSTEM']->am->acquireLock($order->id, 'attribute')) {
270  $_SESSION['sq_local_cart_contents']['order_id'] = $order->id;
271  $_SESSION['sq_local_cart_contents']['delivery_id'] = $order->attr('delivery_id');
272  $_SESSION['sq_local_cart_contents']['delivery_state'] = $order->attr('delivery_state');
273  $_SESSION['sq_local_cart_contents']['cart_contents'] = $order->attr('products');
274 
275  $this->print_context = 'checkout';
276  $_SESSION['sq_local_cart_contents']['delivery_state']['last_context'] = 'checkout';
277 
278  // set edit flag true
279  $_SESSION['sq_local_cart_contents']['editing_order'] = TRUE;
280 
281  } else {
282  echo 'This order is currently being edited';
283  }
284 
285  } else {
286  echo 'You cannot edit this order';
287  }
288  return;
289 
290  }
291 
292  // default print context is'checkout'
293  $this->print_context = 'checkout';
294 
295  $request_action = array_get_index($_REQUEST, $this->getPrefix().'_action');
296 
297  // if context is checkout, compare the global and local carts,
298  // if they are different, update the local cart
299  if (empty($request_action)) {
300  if ($this->print_context === 'checkout') {
301  $global_cart = new Ecommerce_Cart_Processor($this->id);
302 
303  // unset previous order id and delivery information
304  unset($_SESSION['sq_local_cart_contents']['order_id']);
305  unset($_SESSION['sq_local_cart_contents']['delivery_id']);
306  unset($_SESSION['sq_local_cart_contents']['delivery_state']);
307  unset($_SESSION['sq_local_cart_contents']['editing_order']);
308  unset($_SESSION['sq_local_cart_contents']['success_order_id']);
309 
310  $local_cart_empty = empty($_SESSION['sq_local_cart_contents']['cart_contents']);
311 
312  $need_update = FALSE;
313  if ($local_cart_empty) {
314  // if global is not empty, update
315  if ($global_cart->getCount() != 0) {
316  $need_update = TRUE;
317  }
318  } else {
319  // if global and local carts are different, update
320  $local_cart = new Ecommerce_Cart_Processor();
321  $local_cart->setCartContainer($_SESSION['sq_local_cart_contents']['cart_contents']);
322 
323  if (strcmp($global_cart->hashCart(), $local_cart->hashCart()) != 0) {
324  $need_update = TRUE;
325  }
326  }
327 
328  // update the local cart
329  if ($need_update) {
330  $_SESSION['sq_local_cart_contents']['cart_contents'] = $global_cart->getCart();
331  }
332 
333  }//end if
334  return;
335  }
336 
337  if (is_array($request_action)) {
338  $action = key($request_action);
339  } else {
340  // cater for GET vars
341  $action = $request_action;
342  }
343 
344  $success = TRUE;
345 
346  $this->_logOrderAction(translate('ecom_order_history_action', ucwords(str_replace('_',' ', $action))));
347 
348  switch ($action) {
349  case 'go_to_confirmation':
350  $delivery_method_id = array_get_index($_REQUEST, 'sq_delivery_method_id');
351 
352  // if we're already at confirmation, why are we going again?
353  if (isset($_SESSION['sq_local_cart_contents']['delivery_state'])) {
354  if ($_SESSION['sq_local_cart_contents']['delivery_state']['last_context'] == 'confirmation') {
355  $this->print_context = 'confirmation';
356  break;
357  }
358  }
359 
360  if (!empty($delivery_method_id) && !empty($_SESSION['sq_local_cart_contents']['cart_contents'])) {
361 
362  $delivery_method = $GLOBALS['SQ_SYSTEM']->am->getAsset($delivery_method_id);
363  if (empty($delivery_method)) {
364  $this->print_context = 'checkout';
365  return;
366  }
367 
368  // delivery method state has cart contents
369  $_SESSION['sq_local_cart_contents']['delivery_state']['cart_contents'] = $_SESSION['sq_local_cart_contents']['cart_contents'];
370  $delivery_method->setState($_SESSION['sq_local_cart_contents']['delivery_state']);
371 
372  // process the form questions in the delivery interface and place into session var
373  // also retrieve a more comprehensive version returned by the same function
374  $form_answers = $delivery_method->processInputInterface();
375  if ($form_answers === FALSE) {
376  $this->_tmp['delivery_method_errors'] = $delivery_method->getErrors();
377  $success = FALSE;
378  }
379 
380  // set any region specific options
381  $country_question = $this->attr('country_question');
382  $taxable_countries = $this->attr('taxable_countries');
383 
384  // find the country code they have selected as their address
385  if (isset($form_answers[$country_question])) {
386  $country_code = $form_answers[$country_question]['value'];
387  if (is_array($country_code)) {
388  // need a scalar
389  $country_code = $country_code[0];
390  }
391  }
392 
393  // only set region specific options if we have a country code to compare against
394  $has_country_code = isset($country_code);
395  if ($has_country_code || $this->attr('accept_empty_country_question')) {
396  // place all region specific options here to evaluate later
397  $_SESSION['sq_region_specific'] = Array();
398  // if customer is not defined as a taxable country then exclude tax
399  if ($has_country_code) {
400  if (!in_array($country_code, $taxable_countries)) {
401  $_SESSION['sq_region_specific']['remove_tax'] = TRUE;
402  } else {
403  $_SESSION['sq_region_specific']['remove_tax'] = FALSE;
404  }
405  }//end if
406 
407  // delivery prices per weight metric
408  if ($has_country_code) {
409  $delivery_weight_fees = $this->attr('delivery_weight_fee_mapping');
410  foreach ($delivery_weight_fees as $key => $data) {
411  if (in_array($country_code, $data['countries'])) {
412  // found a valid mapping for this country so record the fee
413  $_SESSION['sq_region_specific']['delivery_weight_fee']['fee'] = $data['price'];
414  // only find the first occurrence of the country
415  break;
416  }
417  }
418  }//end if
419 
420  // record the item weight source
421  $selector_type = $this->attr('item_weight_selector');
422  switch ($selector_type) {
423  case 'metadata':
424  $fieldid = $this->attr('item_weight_metadata_source');
425  if (!empty($fieldid)) {
426  $_SESSION['sq_region_specific']['delivery_weight_fee']['metadata_fieldid'] = $fieldid;
427  }
428  break;
429 
430  case 'attribute':
431  $attribute_name = $this->attr('item_weight_attribute_source');
432  if (!empty($attribute_name)) {
433  $_SESSION['sq_region_specific']['delivery_weight_fee']['attribute_name'] = $attribute_name;
434  }
435  break;
436  }
437 
438  //if there is no custom fee, use the default fee
439  if (!isset($_SESSION['sq_region_specific']['delivery_weight_fee']['fee'])) {
440  // use default value price per weight metric
441  $default_price_weight = $this->attr('default_delivery_weight_fee');
442  $_SESSION['sq_region_specific']['delivery_weight_fee']['fee'] = $default_price_weight;
443  }
444 
445  //if there is default maximum number of product quantity, add it to the session variable
446  $default_max_product_quantity = $this->attr('default_max_product_quantity');
447  if (!empty($default_max_product_quantity)) {
448  $_SESSION['sq_region_specific']['delivery_weight_fee']['max_quantity'] = $default_max_product_quantity;
449  }
450 
451  // additional international delivery fee
452  if ($has_country_code) {
453  $exempt_countries = $this->attr('international_delivery_fee_exempt_countries');
454  if (!in_array($country_code, $exempt_countries)) {
455  // you are going to be charged with this fee
456  $international_fee = $this->attr('international_delivery_fee');
457  if (!empty($international_fee)) {
458  $_SESSION['sq_region_specific']['international_delivery_fee'] = $international_fee;
459  }
460  }
461  }//end if
462 
463  // additional conditional delivery fee
464  if ($has_country_code) {
465  $conditional_fees = $this->attr('conditional_delivery_fees');
466  $comparison_data = Array();
467  if (!empty($conditional_fees)) {
468  foreach ($conditional_fees as $key => $data) {
469  if (in_array($country_code, $data['countries'])) {
470  unset($data['countries']);
471  $comparison_data[] = $data;
472  }
473  }
474  if (!empty($comparison_data)) {
475  $_SESSION['sq_region_specific']['conditions'] = $comparison_data;
476  }
477  }
478  }//end if
479 
480  //if maximum delivery fee is set (!= 0), add it to the session variable
481  $max_delivery_fee = $this->attr('max_delivery_fee');
482  if ($max_delivery_fee != 0) {
483  $_SESSION['sq_region_specific']['max_delivery_fee'] = $max_delivery_fee;
484  }
485 
486  } else if (isset($_SESSION['sq_region_specific'])) {
487  unset($_SESSION['sq_region_specific']);
488  }//end if
489 
490  // create a temporary order
491  $_SESSION['sq_local_cart_contents']['delivery_id'] = $delivery_method_id;
492  $_SESSION['sq_local_cart_contents']['delivery_state'] = $delivery_method->getState();
493  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($delivery_method);
494  }//end if
495 
496  // if cart is empty, there's no point in checking out
497  if ($success && empty($_SESSION['sq_local_cart_contents']['cart_contents'])) {
498  $this->_tmp['delivery_method_errors'][] = 'The cart is empty. We cannot perform a checkout.';
499  $success = FALSE;
500  }
501  if ($success) {
502  $this->print_context = 'confirmation';
503  } else {
504  $this->print_context = 'checkout';
505  }
506 
507  if (isset($_SESSION['sq_local_cart_contents']['editing_order'])) {
508  $_SESSION['sq_local_cart_contents']['editing_order'] = TRUE;
509  }
510 
511  break;
512 
513  case 'return_to_checkout':
514  // determine if taxable by comparing the default value of the question with taxable countries
515  $country_question = $this->attr('country_question');
516  if (!empty($country_question)) {
517  $question = $GLOBALS['SQ_SYSTEM']->am->getAsset($this->attr('country_question'));
518  $default_value = $question->attr('default');
519  if (isset($default_value[0]) && in_array($default_value[0], $this->attr('taxable_countries'))) {
520  $_SESSION['sq_region_specific']['remove_tax'] = FALSE;
521  } else {
522  $_SESSION['sq_region_specific']['remove_tax'] = TRUE;
523  }
524  } else {
525  $_SESSION['sq_region_specific']['remove_tax'] = FALSE;
526  }
527 
528  $this->print_context = 'checkout';
529  break;
530 
531  case 'payment_gateway_successful':
532  case 'confirm_order':
533  // components should be processed here
534  // delivery processing
535  $delivery_method = $this->_getActiveDeliveryMethod();
536  if (empty($delivery_method)) {
537  $this->_tmp['delivery_method_errors'][] = 'Delivery method not found during the confirmation';
538  $this->print_context = 'failure';
539  break;
540  }
541 
542  // create an order if there are none pending, or the pending order does not exist
543  if ((!isset($_SESSION['sq_local_cart_contents']['pending_order_id']) || !$GLOBALS['SQ_SYSTEM']->am->assetExists($_SESSION['sq_local_cart_contents']['pending_order_id']))) {
544  // also check if we are editing an existing order
545  if (!(isset($_SESSION['sq_local_cart_contents']['editing_order']) && $_SESSION['sq_local_cart_contents']['editing_order'])) {
546  // create the pending order
547  $order_asset = $this->_createOrder($_SESSION['sq_local_cart_contents']['cart_contents']);
548 
549  // so everyone knows that there is an existing pending order and to ensure that we do not create new ones unnecessarily
550  $_SESSION['sq_local_cart_contents']['pending_order_id'] = $order_asset->id;
551 
552  // Save initial order details here so orders can be tracked in case of the following events:
553  // 1. Failure to contact the remote gateway
554  // 2. Failure to return from the remote gateway with "success"
555  //
556  // This ensures that disputed orders can be manually reconciled between Matrix and gateway records if required
557 
558  $this->_saveOrderSummary($order_asset, $delivery_method);
559  $this->_logOrderAction(translate('ecom_order_history_customer_details_saved'), $order_asset);
560  }
561  }
562 
563  // let the delivery method do its thing
564  $this->_processOrder($delivery_method);
565 
566  break;
567 
568  }//end switch
569 
570  // set the last context we saw to this one
571  $_SESSION['sq_local_cart_contents']['delivery_state']['last_context'] = $this->print_context;
572  if (isset($delivery_method)) {
573  $delivery_method->setState($_SESSION['sq_local_cart_contents']['delivery_state']);
574  }
575 
576  return;
577 
578  }//end _processInterface()
579 
580 
589  function _processOrder(&$delivery_method)
590  {
591  // check whether we're editing an old order or completing an existing one
592  if (isset($_SESSION['sq_local_cart_contents']['order_id'])) {
593  $order_asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($_SESSION['sq_local_cart_contents']['order_id']);
594 
595  $this->_logOrderAction(translate('ecom_order_history_commence_process_existing'), $order_asset);
596  }
597 
598  //prepare the addresses XML to write to the Ecommerce Order asset
599  $payment_gateway_xml = '';
600 
601  // does this order need to be reviewed, i.e. placed in the Orders To Review folder
602  $review_order = FALSE;
603 
604  if (empty($order_asset)) {
605  // have we processed this order with a payment gateway yet?
606  $payment_response = array_get_index($_SESSION, 'SQ_ECOM_RESPONSE', FALSE);
607 
608  // check quantities before completing this order
609  // don't check here if we have just paid
610  if (!$payment_response && $this->attr('enforce_quantities')) {
611  // do we have enough products to complete an order?
612  if (!$this->_updateQuantities(FALSE)) {
613  $this->print_context = 'failure';
614  return;
615  } else {
616  $this->insufficient_quantities = FALSE;
617  }
618  }
619 
620  if (!$payment_response) {
621  // process the payment gateway if one is present and if we haven't done so already
622  $payment_gateway_attr =& $delivery_method->getAttribute('payment_gateway', TRUE);
623  $payment_gateway_id = NULL;
624  // Only try to find payment gateway if this delivery method support payment gateway, some methods might not.
625  if (!is_null($payment_gateway_attr)) {
626  $payment_gateway_id = $delivery_method->attr('payment_gateway');
627  }//end if
628  if (!empty($payment_gateway_id)) {
629  // get cart total cost
630  $cart = new Ecommerce_Cart_Processor();
631  $cart->setCartContainer($_SESSION['sq_local_cart_contents']['cart_contents']);
632  // whether we are removing tax from this order
633  $remove_tax = isset($_SESSION['sq_region_specific']['remove_tax']) ? $_SESSION['sq_region_specific']['remove_tax'] : FALSE;
634  $total_cost = $cart->getGrandTotalExDelivery($this->attr('taxation_rate')/100, $remove_tax, $this->getFlatCharges());
635  $delivery_cost = $cart->getTotalDeliveryCosts($total_cost);
636 
637  // customer pays here!
638  $this->_logOrderAction(translate('ecom_order_history_payment_gateway'));
639  $delivery_method->processPaymentGateway(round($total_cost + $delivery_cost, $this->attr('float_precision')));
640  return;
641 
642  } else if ($delivery_method->attr('gateway_required')){
643  $this->_tmp['delivery_method_errors'][] = translate('ecom_checkout_error_no_payment_gateway_specified');
644  $this->print_context = 'failure';
645  return;
646  }
647  }
648 
649  // we only come here if customer has just paid for this order
650  // complete the order whatever happens and flag if quantity was incorrect in the end
651  // e.g. customer pays for order, in the meantime quantity changes, by the time payment goes through, the quantity may be incorrect
652  // but we don't want the order to fail because the customer has already paid
653 
654  // unset this because the payment experience was just completed for this session
655  unset($_SESSION['SQ_ECOM_RESPONSE']);
656 
657  // check quantities
658  if ($this->attr('enforce_quantities')) {
659  if (!$this->_updateQuantities(FALSE)) {
660  // product quantity was negative
661  // set keyword replacement message for email(s) to be sent
662  ob_start();
663  foreach ($this->insufficient_quantity_assets as $quantity => $asset_name) {
664  echo $asset_name.":\t -> \t".abs($quantity)."\n";
665  }
666  $neg_quantity_list = ob_get_clean();
667  $review_order = TRUE;
668  // the order has to go through because it has been paid for
669  $this->print_context = 'receipt';
670 
671  $this->_logOrderAction(translate('ecom_order_history_review_insufficient_quantities'));
672  }
673  }
674 
675  // the order is completed so move it into the correct folder
676  $pending_order_id = array_get_index($_SESSION['sq_local_cart_contents'], 'pending_order_id', NULL);
677  $dest_folder_name = $review_order ? 'Orders To Review' : 'Completed Orders';
678  if (!is_null($pending_order_id)) {
679  $order_asset = $this->_moveOrder($pending_order_id, $review_order);
680 
681  $this->_logOrderAction(translate('ecom_order_history_order_moved', $dest_folder_name), $order_asset);
682 
683  // Record the reference number returned from the Gateway
684  if (isset($_SESSION['SQ_ECOM_REF_NO']) && !empty($_SESSION['SQ_ECOM_REF_NO'])) {
685  $order_asset->setAttrValue('ecom_ref_no', $_SESSION['SQ_ECOM_REF_NO']);
686  $this->_logOrderAction(translate('ecom_order_history_ecom_ref_recorded'), $order_asset);
687  } else {
688  $this->_logOrderAction(translate('ecom_order_history_ecom_ref_not_recorded'), $order_asset);
689  }
690 
691  //customer has just paid for this order
692  if ($payment_response) {
693  //set ecommerce order's details
694  $order_asset->setAttrValue('transaction_id', array_get_index($payment_response, 'TRANSACTION', ''));
695  $order_asset->setAttrValue('billing_name', array_get_index($payment_response, 'BILLING_NAME', ''));
696  $order_asset->setAttrValue('billing_addr', array_get_index($payment_response, 'BILLING_ADDR', ''));
697  $order_asset->setAttrValue('delivery_name', array_get_index($payment_response, 'DELIVERY_NAME', ''));
698  $order_asset->setAttrValue('delivery_addr', array_get_index($payment_response, 'DELIVERY_ADDR', ''));
699  $payment_gateway_xml = array_get_index($payment_response, 'PAYMENT_GATEWAY_XML', '');
700  }
701 
702  }
703 
704  if (!isset($order_asset) || is_null($order_asset)) {
705  $this->_tmp['delivery_method_errors'][] = translate('ecom_checkout_error_unable_to_move_order', $dest_folder_name);
706  $this->print_context = 'failure';
707  return;
708  } else {
709  // order moved and the pending order is no more
710  unset($_SESSION['sq_local_cart_contents']['pending_order_id']);
711  }
712 
713  } else if ($_SESSION['sq_local_cart_contents']['editing_order']) {
714  // editing an existing order, so we do not process it via the payment gateway
715  $this->_logOrderAction(translate('ecom_order_history_skip_payment_gateway'), $order_asset);
716  }
717 
718  $delivery_method->setState($_SESSION['sq_local_cart_contents']['delivery_state']);
719 
720  // get the arguments for customised receipts if set
721  $format_bodycopy = NULL;
722  $contents_bodycopy = NULL;
723  if ($this->attr('customise_receipt')) {
724  $format_bodycopy = $this->getBodycopy('receipt_format', TRUE);
725  $contents_bodycopy = $this->getBodycopy('receipt_contents', TRUE);
726  }
727 
728  $this->_logOrderAction(translate('ecom_order_history_commence_delivery_method'), $order_asset);
729 
730  // process the delivery and provide the tax name and tax rate
731  $delivery_processing_status = $delivery_method->processDelivery($order_asset, $contents_bodycopy, $format_bodycopy, $this->attr('currency_symbol'), $this->attr('float_precision'), $this->attr('taxation_name'), $this->attr('taxation_rate')/100.0, $this->getFlatCharges(), isset($neg_quantity_list) ? $neg_quantity_list : '', isset($neg_quantity_list) ? $delivery_method->attr('negative_quantity_message') : '');
732 
733  $this->_logOrderAction(translate('ecom_order_history_end_delivery_method'), $order_asset);
734 
735  $_SESSION['sq_local_cart_contents']['delivery_state'] = $delivery_method->getState();
736 
737  $this->_saveOrderSummary($order_asset, $delivery_method, $payment_gateway_xml);
738 
739  // delivery failed
740  if (!$delivery_processing_status) {
741  $order_asset->setAttrValue('status', SQ_ECOM_ORDER_STATUS_FAILED);
742  $order_asset->setAttrValue('status_message', translate('ecom_order_status_failed'));
743 
744  $this->_tmp['failure_summary'] = $delivery_method->getFailureSummary();
745  $this->print_context = 'failure';
746  } else {
747  // delivery succeeded and we can update product quantities
748  if ($this->attr('enforce_quantities')) {
749  $this->_updateQuantities();
750 
751  $this->_logOrderAction(translate('ecom_order_history_quantities_updated'), $order_asset);
752  }
753  }
754 
755  // Save attributes, LOCKING
756  $GLOBALS['SQ_SYSTEM']->setRunLevel(SQ_RUN_LEVEL_FORCED);
757  if (!$order_asset->saveAttributes()) {
758  $this->print_context = 'failure';
759  }
760  $GLOBALS['SQ_SYSTEM']->restoreRunLevel();
761  if ($this->print_context !== 'failure') {
762 
763  // order processing successful!
764  $this->print_context = 'receipt';
765  $_SESSION['sq_local_cart_contents']['success_order_id'] = $order_asset->id;
766  $this->_tmp['delete_all_orders_after_printing'] = TRUE;
767  $GLOBALS['SQ_SYSTEM']->broadcastTriggerEvent('trigger_event_ecommerce_order_submitted', $order_asset);
768  }
769 
770  // Anything else that needs to be done after the order is processed
771  $this->finishCheckout($order_asset);
772 
773  }//end _processOrder()
774 
775 
784  function _updateQuantities($update=TRUE)
785  {
786  $local_cart_contents = $_SESSION['sq_local_cart_contents']['cart_contents'];
787  $ok = TRUE;
788  foreach ($local_cart_contents as $assetid => $info) {
789  if ($GLOBALS['SQ_SYSTEM']->am->assetExists($assetid)) {
790  // determine which to use: metadata field or attribute name
791  $source = $this->attr('quantity_selector');
792  switch ($source) {
793  case 'metadata':
794  $quantity_field = $this->attr('quantity_field');
795  if (!empty($quantity_field)) {
796  $mm = $GLOBALS['SQ_SYSTEM']->getMetadataManager();
797  $current_quantity = $mm->getMetadataValueByAssetid($assetid, $quantity_field);
798  $new_quantity = $current_quantity - $info['quantity'];
799  if ($new_quantity < 0) {
800  // we cannot put this order through because there are not enough items
801  $this->insufficient_quantities = TRUE;
802  $ok = FALSE;
803  // add name of asset to error list for displaying on frontend
804  $asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($assetid);
805  $this->insufficient_quantity_assets[$new_quantity] = $asset->name;
806  }
807  if ($update) {
808  $metadata = Array(
809  $quantity_field => Array(
810  Array(
811  'value' => $new_quantity,
812  ),
813  ),
814  );
815  $GLOBALS['SQ_SYSTEM']->setRunLevel(SQ_RUN_LEVEL_FORCED);
816  if (!$mm->setMetadata($assetid, $metadata) || !$mm->generateContentFile($assetid)) {
817  trigger_localised_error('ECOM024', E_USER_WARNING, $assetid);
818  $this->print_context = 'failure';
819  }
820  $GLOBALS['SQ_SYSTEM']->restoreRunLevel();
821  }
822  }
823  break;
824 
825  case 'attribute':
826  $quantity_attribute = $this->attr('quantity_attribute');
827  if (!empty($quantity_attribute)) {
828  $asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($assetid);
829  $current_quantity = $asset->attr($quantity_attribute);
830  $new_quantity = $current_quantity - $info['quantity'];
831  if ($new_quantity < 0) {
832  // we cannot put this order through because there are not enough items
833  $this->insufficient_quantities = TRUE;
834  $ok = FALSE;
835  // add name of asset to error list for displaying on frontend
836  $this->insufficient_quantity_assets[$new_quantity] = $asset->name;
837  }
838  if ($update) {
839  $asset->setAttrValue($quantity_attribute, $new_quantity);
840  $GLOBALS['SQ_SYSTEM']->setRunLevel(SQ_RUN_LEVEL_FORCED);
841  if (!$asset->saveAttributes()) {
842  trigger_localised_error('ECOM024', E_USER_WARNING, $assetid);
843  $this->print_context = 'failure';
844  }
845  $GLOBALS['SQ_SYSTEM']->restoreRunLevel();
846  }
847  }
848  break;
849  }//end switch
850  }//end if
851  }//end foreach
852 
853  return $ok;
854 
855  }//end _updateQuantities()
856 
857 
867  function _printContext()
868  {
869  $bodycopy_folder_link = $GLOBALS['SQ_SYSTEM']->am->getLink($this->id, SQ_LINK_TYPE_2, 'folder', TRUE, 'bodycopies');
870  if (empty($bodycopy_folder_link)) {
871  trigger_localised_error('ECOM0017', E_USER_WARNING);
872  return;
873  }
874 
875  $content_bodycopy_link = $GLOBALS['SQ_SYSTEM']->am->getLink($bodycopy_folder_link['minorid'], SQ_LINK_TYPE_2, 'bodycopy', TRUE, $this->print_context);
876  $content_bodycopy = $GLOBALS['SQ_SYSTEM']->am->getAsset($content_bodycopy_link['minorid'], $content_bodycopy_link['minor_type_code']);
877  if (is_null($content_bodycopy)) {
878  trigger_localised_error('ECOM0018', E_USER_WARNING);
879  return;
880  }
881 
882  require_once SQ_FUDGE_PATH.'/general/text.inc';
883  $keywords = $content_bodycopy->getKeywords();
884  $replacements = Array();
885  foreach ($keywords as $keyword) {
886  $replacements[$keyword] = $this->getKeywordReplacement($keyword);
887  }
888 
889  $form_req = $this->_isFormRequired();
890 
891  if ($form_req) {
892 
893  $active_delivery_method_id = $this->_getActiveDeliveryMethodId();
894 
895  if (is_null($active_delivery_method_id )) {
896  $delivery_ids = $this->_getDeliveryAssetIds();
897  $active_delivery_method_id = array_shift($delivery_ids);
898  }
899 
900  if ($active_delivery_method_id) {
901  $delivery_method = $GLOBALS['SQ_SYSTEM']->am->getAsset($active_delivery_method_id);
902  $delivery_form = $delivery_method ? $delivery_method->getForm() : NULL;
903 
904  $onsubmit = $delivery_form && $delivery_form->attr('client_side') ? 'onSubmit="return beforeSubmit_'.$delivery_form->getPrefix().'(this)"' : '';
905  } else {
906  $onsubmit = '';
907  }
908 
909  echo '<form action="'.$_SERVER['PHP_SELF'].'" method="post" '.$onsubmit.'>';
910  }
911 
912  $content_bodycopy->setKeywordReplacements($replacements);
913  $content_bodycopy->printBody();
914 
915  if ($form_req) echo '</form>';
916 
917  }//end _printContext()
918 
919 
930  function _createAdditional(&$link)
931  {
932  if (!parent::_createAdditional($link)) return FALSE;
933 
934  $success = TRUE;
935 
936  $GLOBALS['SQ_SYSTEM']->am->includeAsset('folder');
937 
938  $sub_assets = Array(
939  'bodycopies' => 'folder',
940  'pending_orders' => 'folder',
941  'completed_orders' => 'folder',
942  'orders_to_review' => 'folder',
943  );
944 
945  $bodycopy_folder = NULL;
946  foreach ($sub_assets as $link_value => $type) {
947  $asset = new $type();
948  $copy_link = Array(
949  'asset' => &$this,
950  'value' => $link_value,
951  'link_type' => SQ_LINK_TYPE_2,
952  'is_dependant' => 1,
953  'is_exclusive' => 1,
954  );
955 
956  $asset->setAttrValue('name', ucwords(str_replace('_',' ', $link_value)));
957  if (!$asset->create($copy_link)) $success = FALSE;
958 
959  if ($link_value == 'bodycopies') {
960  $bodycopy_folder = $asset;
961  }
962 
963  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($asset);
964  unset($asset);
965 
966  if (!$success) break;
967  }
968 
969  if ($success) {
970  // now create context bodycopies
971  $GLOBALS['SQ_SYSTEM']->am->includeAsset('bodycopy');
972  foreach ($this->_getAvailableContexts() as $context_link_value => $context_bodycopy_name) {
973  $asset = new Bodycopy();
974 
975  $copy_link = Array(
976  'asset' => &$bodycopy_folder,
977  'value' => $context_link_value,
978  'link_type' => SQ_LINK_TYPE_2,
979  'is_dependant' => 1,
980  'is_exclusive' => 1,
981  );
982 
983  $asset->setAttrValue('name', $context_bodycopy_name);
984 
985  $success = $asset->create($copy_link);
986  unset($asset);
987  if (!$success) break;
988  }
989  }
990 
991  return $success;
992 
993  }//end _createAdditional()
994 
995 
1004  function _createOrder($cart_items)
1005  {
1006  // error flag
1007  $success = TRUE;
1008 
1009  $am = $GLOBALS['SQ_SYSTEM']->am;
1010 
1011  // create ecommerce order
1012  $order_folder_link = $am->getLink($this->id, SQ_LINK_TYPE_2, 'folder', TRUE, 'pending_orders');
1013  if (empty($order_folder_link)) return NULL;
1014  $order_folder_id = $order_folder_link['minorid'];
1015 
1016  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
1017  $GLOBALS['SQ_SYSTEM']->doTransaction('BEGIN');
1018 
1019  // set runlevel
1020  $GLOBALS['SQ_SYSTEM']->setRunLevel(SQ_RUN_LEVEL_FORCED);
1021 
1022  $orders_folder = $am->getAsset($order_folder_id);
1023  $ecommerce_order = new Ecommerce_Order();
1024 
1025  $order_link = Array(
1026  'asset' => &$orders_folder,
1027  'link_type' => SQ_LINK_TYPE_2,
1028  );
1029 
1030  if (!$ecommerce_order->create($order_link)) {
1031  $success = FALSE;
1032  }
1033 
1034  // add products from the session (cart) to ecommerce order
1035  if ($success && !empty($cart_items)) {
1036  $ecommerce_order->setAttrValue('products', $cart_items);
1037 
1038  $this->_logOrderAction(translate('ecom_order_history_order_created'), $ecommerce_order);
1039 
1040  } else {
1041  $success = FALSE;
1042  }
1043 
1044  // notice link from Checkout to Ecommerce_Order
1045  if ($success && ($am->createAssetLink($this, $ecommerce_order, SQ_LINK_NOTICE, 'checkout') == 0)) {
1046  $success = FALSE;
1047  }
1048 
1049  // set order status and delivery id
1050  if ($success) {
1051  $ecommerce_order->setAttrValue('status', SQ_ECOM_ORDER_STATUS_PROCESSING);
1052  $ecommerce_order->setAttrValue('status_message', translate('ecom_order_status_processing'));
1053  $ecommerce_order->setAttrValue('delivery_id', $this->_getActiveDeliveryMethodId());
1054  }
1055 
1056  // save attributes
1057  if (!$am->acquireLock($ecommerce_order->id, 'attributes')) {
1058  $success = FALSE;
1059  }
1060  if ($success) {
1061  if (!$ecommerce_order->saveAttributes()) {
1062  $success = FALSE;
1063  }
1064  }
1065  $am->releaseLock($ecommerce_order->id, 'attributes');
1066 
1067  // restore runlevel
1068  $GLOBALS['SQ_SYSTEM']->restoreRunLevel();
1069 
1070  if (!$success) {
1071  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
1072  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
1073  return NULL;
1074  }
1075  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
1076  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
1077 
1078  return $ecommerce_order;
1079 
1080  }//end _createOrder()
1081 
1082 
1092  function _moveOrder($order_id, $review)
1093  {
1094  // error flag
1095  $success = TRUE;
1096 
1097  $am = $GLOBALS['SQ_SYSTEM']->am;
1098 
1099  if (empty($order_id)) return NULL;
1100  $ecommerce_order = $am->getAsset($order_id);
1101 
1102  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
1103  $GLOBALS['SQ_SYSTEM']->doTransaction('BEGIN');
1104 
1105  // set runlevel
1106  $GLOBALS['SQ_SYSTEM']->setRunLevel(SQ_RUN_LEVEL_FORCED);
1107 
1108  // get linkid of the ecommerce order to move
1109  $pending_orders_link = $am->getLink($this->id, SQ_LINK_TYPE_2, 'folder', TRUE, 'pending_orders');
1110  $order_link_info = $am->getLinkByAsset($pending_orders_link['minorid'], $order_id);
1111 
1112  // get destination folder assetid
1113  $dest_link_value = $review ? 'orders_to_review' : 'completed_orders';
1114  $order_folder_link = $am->getLink($this->id, SQ_LINK_TYPE_2, 'folder', TRUE, $dest_link_value);
1115 
1116  // now move the ecommerce order to its final resting place
1117  if (!$am->moveLink($order_link_info['linkid'], $order_folder_link['minorid'], SQ_LINK_TYPE_2, 0)) {
1118  $success = FALSE;
1119  }
1120 
1121  // restore runlevel
1122  $GLOBALS['SQ_SYSTEM']->restoreRunLevel();
1123 
1124  if (!$success) {
1125  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
1126  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
1127  return NULL;
1128  }
1129 
1130  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
1131  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
1132 
1133  return $ecommerce_order;
1134 
1135  }//end _moveOrder()
1136 
1137 
1145  {
1146  if (!isset($this->_tmp['delivery_ids'])) {
1147  $this->_tmp['delivery_ids'] = Array();
1148 
1149  $delivery_links = $GLOBALS['SQ_SYSTEM']->am->getLinks($this->id, SQ_LINK_NOTICE, '', FALSE, 'major', 'delivery_method');
1150  foreach ($delivery_links as $one_link) {
1151  $this->_tmp['delivery_ids'][] = $one_link['minorid'];
1152  }
1153  }
1154 
1155  return $this->_tmp['delivery_ids'];
1156 
1157  }//end _getDeliveryAssetIds()
1158 
1159 
1170  function _registerFormField($field_name)
1171  {
1172  if (!empty($field_name)) {
1173  $this->_tmp['registered_form_fields'][] = $field_name;
1174  $this->_registerFormRequirement();
1175  }
1176 
1177  }//end _registerFormField()
1178 
1179 
1189  {
1190  $this->_tmp['form_required'] = TRUE;
1191 
1192  }//end _registerFormRequirement()
1193 
1194 
1204  function _isFormRequired()
1205  {
1206  return array_get_index($this->_tmp, 'form_required', FALSE);
1207 
1208  }//end _isFormRequired()
1209 
1210 
1220  {
1221  $delivery_method = NULL;
1222 
1223  $delivery_method_id = $this->_getActiveDeliveryMethodId();
1224  if (!is_null($delivery_method_id)) {
1225  $delivery_method = $GLOBALS['SQ_SYSTEM']->am->getAsset($delivery_method_id);
1226  $delivery_method_state = array_get_index($_SESSION['sq_local_cart_contents'], 'delivery_state', Array());
1227  $delivery_method->setState($delivery_method_state);
1228  }
1229 
1230  return $delivery_method;
1231 
1232  }//end _getActiveDeliveryMethod()
1233 
1234 
1244  {
1245  return array_get_index($_SESSION['sq_local_cart_contents'], 'delivery_id');
1246 
1247  }//end _getActiveDeliveryMethodId()
1248 
1249 
1257  {
1258  return;
1259 
1260  }//end _getProductReceipt()
1261 
1262 
1269  function getFlatCharges()
1270  {
1271  $flat_charges = $this->attr('flat_charge_assetid');
1272  $flat_charge_array = Array();
1273  $GLOBALS['SQ_SYSTEM']->setRunLevel(SQ_RUN_LEVEL_FORCED);
1274 
1275  foreach ($flat_charges as $charge_id) {
1276  $tmp_charge = $GLOBALS['SQ_SYSTEM']->am->getAsset($charge_id);
1277  $price = $tmp_charge->attr('price');
1278  if ($price <= 0) $price = 0;
1279 
1280  $flat_charge_array = array_merge($flat_charge_array, Array($tmp_charge->name => $price));
1281  }
1282 
1283  $GLOBALS['SQ_SYSTEM']->restoreRunLevel();
1284  return $flat_charge_array;
1285 
1286  }//end getFlatCharges()
1287 
1288 
1297  function _getCartSummary($html=TRUE)
1298  {
1299  $cart = new Ecommerce_Cart_Processor();
1300  $cart->setCartContainer($_SESSION['sq_local_cart_contents']['cart_contents']);
1301 
1302  ob_start();
1303  if ($html) echo '<pre>';
1304 
1305  // if products are taxed, remove tax from final amount
1306  $remove_tax = isset($_SESSION['sq_region_specific']['remove_tax']) ? $_SESSION['sq_region_specific']['remove_tax'] : FALSE;
1307  echo $cart->printReceipt($this->attr('taxation_name'), ($this->attr('taxation_rate')/100.0), $this->getFlatCharges(), $remove_tax, $this->attr('currency_symbol'), $this->attr('float_precision'), $this->attr('refund_info_text'));
1308 
1309  if ($html) echo '</pre>';
1310  $cart_summary = ob_get_clean();
1311  unset($cart);
1312  return $cart_summary;
1313 
1314  }//end _getCartSummary()
1315 
1316 
1323  function _getCartXml()
1324  {
1325  $cart = new Ecommerce_Cart_Processor();
1326  $cart->setCartContainer($_SESSION['sq_local_cart_contents']['cart_contents']);
1327 
1328  // if products are taxed, remove tax from final amount
1329  $remove_tax = isset($_SESSION['sq_region_specific']['remove_tax']) ? $_SESSION['sq_region_specific']['remove_tax'] : FALSE;
1330 
1331  return $cart->getCartXml($this->attr('taxation_name'), ($this->attr('taxation_rate')/100.0), $this->getFlatCharges(), $remove_tax, $this->attr('currency_symbol'), $this->attr('float_precision'));
1332 
1333  }//end _getCartXml()
1334 
1335 
1344  function _getCartTotal($html=TRUE)
1345  {
1346  $cart = new Ecommerce_Cart_Processor();
1347  $cart->setCartContainer($_SESSION['sq_local_cart_contents']['cart_contents']);
1348 
1349  ob_start();
1350  if ($html) echo '<pre>';
1351 
1352  // if products are taxed, remove tax from final amount
1353  $remove_tax = isset($_SESSION['sq_region_specific']['remove_tax']) ? $_SESSION['sq_region_specific']['remove_tax'] : FALSE;
1354  echo $cart->getGrandTotalExDelivery($this->attr('taxation_rate')/100, $remove_tax, $this->getFlatCharges());
1355 
1356  if ($html) echo '</pre>';
1357  $total_cost = ob_get_clean();
1358  unset($cart);
1359  return $total_cost;
1360 
1361  }//end _getCartTotal()
1362 
1363 
1365 
1366 
1381  function getKeywordReplacement($keyword)
1382  {
1383  $keyword_replaced = FALSE;
1384 
1385  if (isset($_SESSION['sq_local_cart_contents']['editing_order']) && !empty($_SESSION['sq_local_cart_contents']['editing_order'])) {
1386  $orderid = $_SESSION['sq_local_cart_contents']['order_id'];
1387  $order = $GLOBALS['SQ_SYSTEM']->am->getAsset($orderid);
1388 
1389  if (!empty($order)) {
1390  if (strcmp('asset_assetid', $keyword) == 0) {
1391  $replacement = $order->id;
1392  $keyword_replaced = TRUE;
1393  } else if (0 === strpos($keyword, 'asset_attribute_')) {
1394  $attr_name = substr($keyword, strlen('asset_attribute_'));
1395  if (array_key_exists($attr_name, $order->vars)) {
1396  $attr = $order->getAttribute($attr_name);
1397  if (!$attr->is_admin) {
1398  $replacement = $this->attr($attr_name);
1399  $keyword_replaced = TRUE;
1400  }
1401  }
1402  } else {
1403  $mm = $GLOBALS['SQ_SYSTEM']->getMetadataManager();
1404  $dummy_array = Array($keyword);
1405  $mm_keyword = $mm->generateKeywordReplacements($order, $dummy_array, FALSE);
1406  if (isset($mm_keyword[$keyword])) {
1407  $replacement = $mm_keyword[$keyword];
1408  $keyword_replaced = TRUE;
1409  }
1410  }
1411  }//end if (!empty($order))
1412  }//end if
1413 
1414  if (!$keyword_replaced) {
1415  $replacement = parent::getKeywordReplacement($keyword);
1416  }
1417 
1418  return $replacement;
1419 
1420  }//end getKeywordReplacement()
1421 
1422 
1432  function onRequestKeywords(&$broadcaster, $vars=Array())
1433  {
1434  $keywords = Array();
1435 
1436  if (!is_array($vars['keywords'])) {
1437  $vars['keywords'] = Array();
1438  }
1439 
1440  $bodycopy_folder_link = $GLOBALS['SQ_SYSTEM']->am->getLink($this->id, SQ_LINK_TYPE_2, 'folder', TRUE, 'bodycopies');
1441  // not our bodycopy, just bail out
1442  if (empty($bodycopy_folder_link)) {
1443  return;
1444  } else {
1445  $bodycopy_folder_id = $bodycopy_folder_link['minorid'];
1446  }
1447 
1448  $customise_receipt = $this->attr('customise_receipt');
1449 
1450  $broadcaster_parents = $GLOBALS['SQ_SYSTEM']->am->getParents($broadcaster->id, 'bodycopy', TRUE);
1451  foreach (array_keys($broadcaster_parents) as $bodycopy_id) {
1452  // get the bodycopy context for bodycopies linked underneath the Bodycopies folder
1453  $content_bodycopy_link = $GLOBALS['SQ_SYSTEM']->am->getLinkByAsset($bodycopy_folder_id, $bodycopy_id);
1454  if (!empty($content_bodycopy_link) && !empty($content_bodycopy_link['value'])) {
1455  $bodycopy_context = $content_bodycopy_link['value'];
1456  break;
1457  }
1458 
1459  // get the bodycopy context for bodycopies linked underneath the Customised Receipt Bodycopies folder
1460  if ($customise_receipt) {
1461  $customised_receipt_folder_link = $GLOBALS['SQ_SYSTEM']->am->getLink($bodycopy_folder_id, SQ_LINK_TYPE_2, 'folder', TRUE, 'customised_receipt_folder');
1462  $content_bodycopy_link = $GLOBALS['SQ_SYSTEM']->am->getLinkByAsset($customised_receipt_folder_link['minorid'], $bodycopy_id);
1463  if (!empty($content_bodycopy_link) && !empty($content_bodycopy_link['value'])) {
1464  $bodycopy_context = $content_bodycopy_link['value'];
1465  break;
1466  }
1467  }
1468  }
1469 
1470  // check if the found bodycopy context is recognised
1471  $receipt_contexts = Array(
1472  'receipt_format',
1473  'receipt_contents',
1474  );
1475 
1476  $contexts = $this->_getAvailableContexts();
1477  if (empty($bodycopy_context) || !isset($contexts[$bodycopy_context]) && !in_array($bodycopy_context, $receipt_contexts)) {
1478  return;
1479  }
1480 
1481  require_once SQ_FUDGE_PATH.'/general/text.inc';
1482 
1483  $context_kw_fn = '_get'.ucwords_no_space($bodycopy_context).'ContextKeywords';
1484  if (method_exists($this, $context_kw_fn)) {
1485  $vars['keywords'] = array_merge($vars['keywords'], $this->$context_kw_fn());
1486  }
1487 
1488  }//end onRequestKeywords()
1489 
1490 
1498  {
1499  if ($this->attr('customise_receipt')) {
1500  $keywords['customised_receipt'] = translate('ecom_checkout_keyword_customised_receipt');
1501  }
1502 
1503  $keywords['cart_summary'] = translate('ecom_checkout_keyword_cart_summary');
1504  $keywords['delivery_selector'] = translate('ecom_checkout_keyword_delivery_selector');
1505  $keywords['delivery_interface'] = translate('ecom_checkout_keyword_delivery_interface');
1506  $keywords['error_messages'] = translate('ecom_checkout_keyword_error_messages');
1507  $keywords['go_to_confirmation_button'] = translate('ecom_checkout_keyword_go_to_confirmation_button');
1508  $keywords['items_count'] = translate('ecom_checkout_keyword_items_count');
1509  $keywords['items_total_count'] = translate('ecom_checkout_keyword_total_items_count');
1510 
1511  return $keywords;
1512 
1513  }//end _getCheckoutContextKeywords()
1514 
1515 
1523  {
1524  if ($this->attr('customise_receipt')) {
1525  $keywords['customised_receipt'] = translate('ecom_checkout_keyword_customised_receipt');
1526  }
1527 
1528  $keywords['cart_summary'] = translate('ecom_checkout_keyword_cart_summary');
1529  $keywords['confirmation_summary'] = translate('ecom_checkout_keyword_confirmation_summary');
1530  $keywords['confirm_order_button'] = translate('ecom_checkout_keyword_confirm_order_button');
1531  $keywords['return_to_checkout_button'] = translate('ecom_checkout_keyword_return_to_checkout_button');
1532  $keywords['items_count'] = translate('ecom_checkout_keyword_items_count');
1533  $keywords['items_total_count'] = translate('ecom_checkout_keyword_total_items_count');
1534 
1535  return $keywords;
1536 
1537  }//end _getConfirmationContextKeywords()
1538 
1539 
1547  {
1548  if ($this->attr('customise_receipt')) {
1549  $keywords['customised_receipt'] = translate('ecom_checkout_keyword_customised_receipt');
1550  }
1551 
1552  $keywords['receipt_summary'] = translate('ecom_checkout_keyword_receipt_summary');
1553  $keywords['cart_summary'] = translate('ecom_checkout_keyword_cart_summary');
1554  $keywords['order_xml'] = translate('ecom_checkout_keyword_order_xml');
1555 
1556  return $keywords;
1557 
1558  }//end _getReceiptContextKeywords()
1559 
1560 
1568  {
1569  $keywords['failure_summary'] = translate('ecom_checkout_keyword_failure_summary');
1570  if ($this->attr('enforce_quantities')) {
1571  $keywords['insufficient_quantity_message'] = translate('ecom_checkout_keyword_insufficient_quantity_message');
1572  }
1573  $keywords['return_to_checkout_button'] = translate('ecom_checkout_keyword_return_to_checkout_button');
1574 
1575  return $keywords;
1576 
1577  }//end _getFailureContextKeywords()
1578 
1579 
1587  {
1588  $keywords['item_quantity_added'] = translate('ecom_checkout_keyword_item_quantity_added');
1589  $keywords['item_price_incl_tax'] = translate('ecom_checkout_keyword_item_price_incl_tax');
1590  $keywords['item_tax'] = translate('ecom_checkout_keyword_item_tax');
1591 
1592  $keywords['total_item_price'] = translate('ecom_checkout_keyword_total_item_price');
1593  $keywords['total_item_tax'] = translate('ecom_checkout_keyword_total_item_tax');
1594 
1595  return $keywords;
1596 
1597  }//end _getReceiptFormatContextKeywords()
1598 
1599 
1607  {
1608  $keywords['receipt_listing'] = translate('ecom_checkout_keyword_receipt_listing');
1609 
1610  $keywords['total_tax'] = translate('ecom_checkout_keyword_total_tax');
1611  $keyowrds['tax_name'] = translate('ecom_checkout_keyword_tax_name');
1612 
1613  $keywords['flat_fees_table'] = translate('ecom_checkout_keyword_flat_fees_table');
1614  $keywords['flat_fees_total'] = translate('ecom_checkout_keyword_flat_fees_total');
1615  $keywords['sub_total'] = translate('ecom_checkout_keyword_sub_total');
1616  $keywords['delivery_total'] = translate('ecom_checkout_keyword_delivery_total');
1617  $keywords['grand_total'] = translate('ecom_checkout_keyword_grant_total');
1618 
1619  return $keywords;
1620 
1621  }//end _getReceiptContentsContextKeywords()
1622 
1623 
1625 
1626 
1634  {
1635  $delivery_ids = $this->_getDeliveryAssetIds();
1636  $multiple_asset_info = $GLOBALS['SQ_SYSTEM']->am->getAssetInfo($delivery_ids);
1637 
1638  $selection_contents = Array(); // Array('' => '-- '.translate('ecom_checkout_select_delivery_method').' --');
1639  foreach ($multiple_asset_info as $assetid => $asset_info) {
1640  $selection_contents[$assetid] = $asset_info['name'];
1641  }
1642 
1643  $active_delivery_method_id = $this->_getActiveDeliveryMethodId();
1644 
1645  if (is_null($active_delivery_method_id )) {
1646  $active_delivery_method_id = array_shift($delivery_ids);
1647  }
1648 
1649  $this->_registerFormRequirement();
1650  ob_start();
1651  ?>
1652  <script type="text/javascript">
1653  //<![CDATA[
1654  var current_method_id = <?php echo $active_delivery_method_id; ?> ;
1655 
1656  function handleDeliverySelectChange(field) {
1657  selected_id = field.options[field.selectedIndex].value;
1658 
1659  if ( current_method_id != null ) {
1660  document.getElementById('sq_delivery_method_' + current_method_id).style.display = 'none';
1661  }
1662  current_method_id = selected_id;
1663  document.getElementById('sq_delivery_method_' + current_method_id).style.display = 'block';
1664 
1665  }
1666  //]]>
1667  </script>
1668 
1669  <?php
1670  combo_box('sq_delivery_method_id', $selection_contents, FALSE, $active_delivery_method_id, 0, 'onchange="handleDeliverySelectChange(this)"');
1671 
1672  return ob_get_clean();
1673 
1674  }//end getDeliverySelectorKeywordReplacement()
1675 
1676 
1684  {
1685 
1686  $delivery_ids = $this->_getDeliveryAssetIds();
1687  $active_delivery_method = $this->_getActiveDeliveryMethod();
1688 
1689  if (is_null($active_delivery_method )) {
1690  $active_delivery_method_id = array_shift($delivery_ids);
1691  } else {
1692  $active_delivery_method_id = $active_delivery_method->id;
1693  }
1694 
1695  $this->_registerFormRequirement();
1696  ob_start();
1697  foreach ($this->_getDeliveryAssetIds() as $one_assetid) {
1698  $delivery_method = $GLOBALS['SQ_SYSTEM']->am->getAsset($one_assetid);
1699 
1700  if ($active_delivery_method_id == $one_assetid) {
1701  $display = 'block';
1702  } else {
1703  $display = 'none';
1704  }
1705 
1706  echo "\n".'<div id="sq_delivery_method_'.$one_assetid.'" style="display:'.$display.'" >'."\n".$delivery_method->getInputInterface()."\n</div>\n";
1707 
1708  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($delivery_method);
1709  }
1710 
1711  return ob_get_clean();
1712 
1713  }//end getDeliveryInterfaceKeywordReplacement()
1714 
1715 
1723  {
1724  $field_value = $this->getPrefix().'_action[confirm_order]';
1725  $field_id = $this->getPrefix().'_action_confirm_order';
1726 
1727  $this->_registerFormField($field_value);
1728  ob_start();
1729  submit_button($field_value, $this->attr('confirm_order_button_text'), '', ' id="'.$field_id.'"');
1730  return ob_get_clean();
1731 
1732  }//end getConfirmOrderButtonKeywordReplacement()
1733 
1734 
1742  {
1743  $field_value = $this->getPrefix().'_action[return_to_checkout]';
1744  $field_id = $this->getPrefix().'_action_return_to_checkout';
1745  $this->_registerFormField($field_value);
1746  ob_start();
1747  submit_button($field_value, $this->attr('return_to_checkout_button_text'), '', ' id="'.$field_id.'"');
1748  return ob_get_clean();
1749 
1750  }//end getReturnToCheckoutButtonKeywordReplacement()
1751 
1752 
1760  {
1761  $field_value = $this->getPrefix().'_action[go_to_confirmation]';
1762  $field_id = $this->getPrefix().'_action_go_to_confirmation';
1763  $this->_registerFormField($field_value);
1764  ob_start();
1765  submit_button($field_value, $this->attr('go_to_confirmation_button_text'), '', ' id="'.$field_id.'"');
1766  return ob_get_clean();
1767 
1768  }//end getGoToConfirmationButtonKeywordReplacement()
1769 
1770 
1780  {
1781  ob_start();
1782  $delivery_method = $this->_getActiveDeliveryMethod();
1783  if (!is_null($delivery_method)) {
1784  echo $delivery_method->getConfirmationSummary();
1785  }
1786  return ob_get_clean();
1787 
1788  }//end getConfirmationSummaryKeywordReplacement()
1789 
1790 
1798  {
1799  ob_start();
1800  $delivery_method = $this->_getActiveDeliveryMethod();
1801  if (!is_null($delivery_method)) {
1802  echo $delivery_method->getReceiptSummary();
1803  }
1804  return ob_get_clean();
1805 
1806  }//end getReceiptSummaryKeywordReplacement()
1807 
1808 
1816  {
1817  // essentially the cart's current receipt
1818  return $this->_getCartSummary(TRUE);
1819 
1820  }//end getCartSummaryKeywordReplacement()
1821 
1822 
1830  {
1831  $order = NULL;
1832  if (isset($_SESSION['sq_local_cart_contents']['success_order_id'])) {
1833  $order = $GLOBALS['SQ_SYSTEM']->am->getAsset($_SESSION['sq_local_cart_contents']['success_order_id']);
1834  }
1835 
1836  return is_null($order)? '' : $order->attr('order_xml');
1837 
1838  }//end getOrderXmlKeywordReplacement()
1839 
1840 
1848  {
1849  ob_start();
1850  if (!empty($this->_tmp['delivery_method_errors'])) {
1851  echo '<ul>';
1852  foreach ($this->_tmp['delivery_method_errors'] as $message) {
1853  echo '<li>'.$message.'</li>';
1854  }
1855  echo '</ul>';
1856  }
1857  return ob_get_clean();
1858 
1859  }//end getErrorMessagesKeywordReplacement()
1860 
1861 
1869  {
1870  ob_start();
1871  $delivery_method = $this->_getActiveDeliveryMethod();
1872  if (!is_null($delivery_method)) {
1873  echo $delivery_method->getFailureSummary();
1874  }
1875  return ob_get_clean();
1876 
1877  }//end getFailureSummaryKeywordReplacement()
1878 
1879 
1887  {
1888  if ($this->insufficient_quantities) {
1889  ob_start();
1890  echo $this->attr('quantity_message_checkout');
1891  echo '<ul>';
1892  foreach ($this->insufficient_quantity_assets as $asset_name) {
1893  echo '<li>';
1894  echo $asset_name;
1895  echo '</li>';
1896  }
1897  echo '</ul>';
1898  return ob_get_clean();
1899  }
1900 
1901  }//end getInsufficientQuantityMessageKeywordReplacement()
1902 
1903 
1911  {
1912  $cart = new Ecommerce_Cart_Processor();
1913  $cart->setCartContainer($_SESSION['sq_local_cart_contents']['cart_contents']);
1914  return $cart->getUniqueCount();
1915 
1916  }//end getItemsCountKeywordReplacement()
1917 
1918 
1926  {
1927  $cart = new Ecommerce_Cart_Processor();
1928  $cart->setCartContainer($_SESSION['sq_local_cart_contents']['cart_contents']);
1929  return $cart->getCount();
1930 
1931  }//end getItemsTotalCountKeywordReplacement()
1932 
1933 
1941  {
1942  if ($this->attr('customise_receipt')) {
1943  $cart = new Ecommerce_Cart_Processor();
1944  $cart->setCartContainer($_SESSION['sq_local_cart_contents']['cart_contents']);
1945 
1946  ob_start();
1947 
1948  // if products are taxed, remove tax from final amount
1949  $remove_tax = isset($_SESSION['sq_region_specific']['remove_tax']) ? $_SESSION['sq_region_specific']['remove_tax'] : FALSE;
1950  // get the customised receipt bodycopies
1951  $contents_bodycopy = $this->getBodycopy('receipt_contents', TRUE);
1952  $format_bodycopy = $this->getBodycopy('receipt_format', TRUE);
1953  echo $cart->printCustomisedReceipt($contents_bodycopy, $format_bodycopy, $this->attr('taxation_name'), ($this->attr('taxation_rate')/100.0), $this->getFlatCharges(), $remove_tax, $this->attr('currency_symbol'), $this->attr('float_precision'));
1954 
1955  $customised_receipt = ob_get_clean();
1956  unset($cart);
1957 
1958  return $customised_receipt;
1959  }
1960 
1961  }//end getCustomisedReceiptKeywordReplacement()
1962 
1963 
1973  function getBodycopy($name='', $receipt_folder=FALSE)
1974  {
1975  $null = NULL;
1976 
1977  if (!$name) return $null;
1978  $am = $GLOBALS['SQ_SYSTEM']->am;
1979 
1980  $folder_link = $GLOBALS['SQ_SYSTEM']->am->getLink($this->id, SQ_LINK_TYPE_2, 'folder', TRUE, 'bodycopies');
1981  if (empty($folder_link)) return $null;
1982 
1983  $folder = $am->getAsset($folder_link['minorid'], $folder_link['minor_type_code']);
1984  if (is_null($folder)) return $null;
1985 
1986  if ($receipt_folder) {
1987  $folder_link = $GLOBALS['SQ_SYSTEM']->am->getLink($folder->id, SQ_LINK_TYPE_2, 'folder', TRUE, 'customised_receipt_folder');
1988  if (empty($folder_link)) return $null;
1989  $folder = $am->getAsset($folder_link['minorid'], $folder_link['minor_type_code']);
1990  if (is_null($folder)) return $null;
1991  }
1992 
1993  $bodycopy_link = $GLOBALS['SQ_SYSTEM']->am->getLink($folder->id, SQ_LINK_TYPE_2, 'bodycopy', TRUE, $name);
1994  if (empty($bodycopy_link)) return $null;
1995 
1996  $bodycopy = $am->getAsset($bodycopy_link['minorid'], $bodycopy_link['minor_type_code']);
1997 
1998  return $bodycopy;
1999 
2000  }//end getBodycopy()
2001 
2002 
2013  function saveAttributes($dont_run_updated=FALSE)
2014  {
2015  if (!parent::saveAttributes($dont_run_updated)) {
2016  return FALSE;
2017  }
2018 
2019  // create customised receipt bodycopies
2020  if ($this->id) {
2021  $customise_receipt = $this->attr('customise_receipt');
2022  if ($customise_receipt) {
2023  $bodycopies_folder_link = $GLOBALS['SQ_SYSTEM']->am->getLink($this->id, SQ_LINK_TYPE_2, 'folder', TRUE, 'bodycopies');
2024  $customised_receipt_folder_link = $GLOBALS['SQ_SYSTEM']->am->getLink($bodycopies_folder_link['minorid'], SQ_LINK_TYPE_2, 'folder', TRUE, 'customised_receipt_folder');
2025  // create folder and bodycopies
2026  if (empty($customised_receipt_folder_link)) {
2027  $GLOBALS['SQ_SYSTEM']->am->includeAsset('folder');
2028 
2029  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
2030  $db = $GLOBALS['SQ_SYSTEM']->db;
2031 
2032  $GLOBALS['SQ_SYSTEM']->doTransaction('BEGIN');
2033 
2034  // create a folder for the bodycopies
2035  $GLOBALS['SQ_SYSTEM']->am->includeAsset('folder');
2036  $folder = new Folder();
2037  $folder->setAttrValue('name', 'Customised Receipt Bodycopies');
2038 
2039  $bodycopies_folder = $GLOBALS['SQ_SYSTEM']->am->getAsset($bodycopies_folder_link['minorid']);
2040 
2041  $folder_link = Array('asset' => &$bodycopies_folder, 'link_type' => SQ_LINK_TYPE_2, 'value' => 'customised_receipt_folder', 'is_dependant' => 1, 'is_exclusive' => 1);
2042 
2043  if (!$folder->create($folder_link)) {
2044  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
2045  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
2046  return FALSE;
2047  }
2048 
2049  $bodycopies = Array(
2050  'Receipt Format',
2051  'Receipt Contents',
2052  );
2053  foreach ($bodycopies as $name) {
2054  // create a link to the folder so we can put the bodycopies in it
2055  $copy_link = Array('asset' => &$folder, 'link_type' => SQ_LINK_TYPE_2, 'is_dependant' => 1, 'is_exclusive' => 1, 'value' => strtolower(str_replace(' ', '_', $name)));
2056 
2057  $GLOBALS['SQ_SYSTEM']->am->includeAsset('bodycopy');
2058  $bodycopy = new Bodycopy();
2059  $bodycopy->setAttrValue('name', $name);
2060 
2061  if (!$bodycopy->create($copy_link)) {
2062  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
2063  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
2064  return FALSE;
2065  }
2066  }
2067 
2068  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
2069  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
2070 
2071  }
2072  }
2073  }
2074 
2075  return TRUE;
2076 
2077  }//end saveAttributes()
2078 
2079 
2090  private function _saveOrderSummary(ECommerce_Order &$order_asset, ECommerce_Delivery_Method &$delivery_method, $payment_gateway_xml='')
2091  {
2092  // set the order summary
2093  $order_summary = 'Order ID: '.$order_asset->id;
2094  $order_summary .= $this->_getCartSummary();
2095  //get cart XML
2096  $cart_xml = $this->_getCartXml();
2097 
2098  $order_summary .= $delivery_method->getReceipt();
2099  //get delivery method XML
2100  $delivery_method_xml = $delivery_method->getDeliveryMethodXML();
2101 
2102  //get the default character set of Matrix System
2103  $default_char_set = SQ_CONF_DEFAULT_CHARACTER_SET;
2104 
2105  //create order xml
2106  $order_xml = '<'.'?xml version="1.0" encoding="'.$default_char_set.'"?'.">\n".
2107  "<order>\n".
2108  ' <id>'.$order_asset->id."</id>\n".
2109  $cart_xml."\n".
2110  $delivery_method_xml.$payment_gateway_xml."\n".
2111  "</order>";
2112 
2113  // if there has been a negative quantity order placed (can only happen if payment gateway is used), append the message
2114  if (isset($neg_quantity_list)) {
2115  $order_summary .= '<br />'.$delivery_method->attr('negative_quantity_message');
2116  $order_summary .= $neg_quantity_list;
2117  }
2118 
2119  $total_cost = $this->_getCartTotal(FALSE);
2120 
2121  $order_asset->setAttrValue('summary', $order_summary);
2122  $order_asset->setAttrValue('order_xml', $order_xml);
2123  $order_asset->setAttrValue('delivery_id', $_SESSION['sq_local_cart_contents']['delivery_id']);
2124  $order_asset->setAttrValue('delivery_state', $_SESSION['sq_local_cart_contents']['delivery_state']);
2125  $order_asset->setAttrValue('order_grand_total', $total_cost);
2126 
2127  }//end _saveOrderSummary()
2128 
2129 
2138  public function finishCheckout(&$order_asset)
2139  {
2140  // Execute the checkout's completion actions configured for this checkout page
2141  $success = $this->_performCheckoutCompletionActions($order_asset);
2142 
2143  return $success;
2144 
2145  }// end finishCheckout()
2146 
2147 
2156  private function _performCheckoutCompletionActions(&$order_asset)
2157  {
2158  $actions = $this->attr('actions');
2159  $success = TRUE;
2160  foreach($actions as $action) {
2161  $action_type = $action['type_code'];
2162  $GLOBALS['SQ_SYSTEM']->am->includeAsset($action_type);
2163 
2164  if ($action['active'] && call_user_func(Array($action_type, 'isValid'), $this, $action['settings'])) {
2165  $success &= call_user_func(Array($action_type, 'execute'), $this, $order_asset, $action['settings']);
2166  }
2167  }
2168 
2169  return $success;
2170 
2171  }//end _performCheckoutCompletionActions()
2172 
2173 
2174 }//end class
2175 ?>