/**
  * Action that gets called before we interface with our payment
  * method.
  *
  * This action is responsible for setting up an order and
  * saving it into the database (as well as a session) and then hands
  * the current request over to the relevent payment handler
  * for final processing.
  *
  * @param $request Current request object
  */
 public function index($request)
 {
     $cart = ShoppingCart::get();
     $data = array();
     $payment_data = array();
     $handler = $this->payment_handler;
     // If shopping cart doesn't exist, redirect to base
     if (!$cart->getItems()->exists() || $this->getPaymentHandler() === null) {
         return $this->redirect($cart->Link());
     }
     // Get billing and delivery details and merge into an array
     $billing_data = Session::get("Checkout.BillingDetailsForm.data");
     $delivery_data = Session::get("Checkout.DeliveryDetailsForm.data");
     // If we have applied free shipping, set that up, else get
     if (Session::get('Checkout.PostageID') == -1) {
         $postage = Checkout::CreateFreePostageObject();
     } else {
         $postage = PostageArea::get()->byID(Session::get('Checkout.PostageID'));
     }
     // If we are using a complex checkout and do not have correct
     // details redirect
     if (!Checkout::config()->simple_checkout && !$cart->isCollection() && (!$postage || !$billing_data || !$delivery_data)) {
         return $this->redirect(Checkout_Controller::create()->Link());
     }
     if ($cart->isCollection() && !$billing_data) {
         return $this->redirect(Checkout_Controller::create()->Link());
     }
     // Create an order number
     $data["OrderNumber"] = substr(chunk_split(Checkout::getRandomNumber(), 4, '-'), 0, -1);
     // Setup holder for Payment ID
     $data["PaymentID"] = 0;
     // Set status
     $data['Status'] = 'incomplete';
     // Assign billing, delivery and postage data
     if (!Checkout::config()->simple_checkout) {
         $data = array_merge($data, $billing_data);
         $data = is_array($delivery_data) ? array_merge($data, $delivery_data) : $data;
         $checkout_data = Checkout::config()->checkout_data;
         if (!$cart->isCollection()) {
             $data['PostageType'] = $postage->Title;
             $data['PostageCost'] = $postage->Cost;
             $data['PostageTax'] = $postage->Tax ? $postage->Cost / 100 * $postage->Tax : 0;
         }
         if ($cart->getDiscount()) {
             $data['Discount'] = $cart->getDiscount()->Title;
             $data['DiscountAmount'] = $cart->DiscountAmount;
         }
         // Add full country names if needed
         if (in_array("CountryFull", $checkout_data)) {
             $data['CountryFull'] = Checkout::country_name_from_code($data["Country"]);
         }
         if (in_array("DeliveryCountryFull", $checkout_data) && array_key_exists("DeliveryCountry", $data)) {
             $data['DeliveryCountryFull'] = Checkout::country_name_from_code($data["DeliveryCountry"]);
         }
         foreach ($checkout_data as $key) {
             if (array_key_exists($key, $data)) {
                 $payment_data[$key] = $data[$key];
             }
         }
     }
     // Set our order data as a generic object
     $handler->setOrderData(ArrayData::array_to_object($payment_data));
     return $handler->handleRequest($request, $this->model);
 }
 /**
  * Find relevent postage rates, based on supplied:
  * - Country
  * - Zip/postal code
  * - Weight
  * - Cost
  * - Number of Items
  * 
  * This is returned as an ArrayList that can be looped through.
  *
  * @return ArrayList
  */
 public function getPostageAreas()
 {
     $return = ArrayList::create();
     $config = SiteConfig::current_site_config();
     $cart = ShoppingCart::get();
     $discount = $cart->getDiscount();
     $filter_zipcode = strtolower(substr($this->zipcode, 0, 2));
     if ($this->include_wildcards) {
         $filter = array("Country:PartialMatch" => array($this->country_code, "*"), "ZipCode:PartialMatch" => array($filter_zipcode, "*"));
     } else {
         $filter = array("Country:PartialMatch" => $this->country_code, "ZipCode:PartialMatch" => $filter_zipcode);
     }
     // Find any postage areas that match our filter
     $postage_areas = $config->PostageAreas()->filter($filter);
     // Check if any discounts are set with free postage
     // This is a little hacky at the moment, need to find a nicer
     // way to add free shipping.
     if ($discount && $discount->Type == "Free Shipping" && (strpos($discount->Country, $this->country_code) !== false || $discount->Country == "*")) {
         $postage = Checkout::CreateFreePostageObject();
         $return->add($postage);
     }
     // Make sure we don't effect any associations
     foreach ($postage_areas as $item) {
         $return->add($item);
     }
     // Before doing anything else, remove any wildcards (if needed)
     $exact_country = false;
     // Find any countries that are exactly matched
     foreach ($return as $location) {
         if ($location->Country != "*") {
             $exact_country = true;
         }
     }
     // If exactly matched, remove any wildcards
     foreach ($return as $location) {
         if ($exact_country && $location->Country == "*" && $location->ID != -1) {
             $return->remove($location);
         }
     }
     // Now we have a list of locations, start checking for additional
     // rules an remove if not applicable.
     $total_cost = $this->cost;
     $total_weight = $this->weight;
     $total_items = $this->items;
     $max_cost = 0;
     $max_weight = 0;
     $max_items = 0;
     // First loop through and find items that are invalid
     foreach ($return as $location) {
         if ($location->Calculation == "Price" && $total_cost < $location->Unit) {
             $return->remove($location);
         }
         if ($location->Calculation == "Weight" && $total_weight < $location->Unit) {
             $return->remove($location);
         }
         if ($location->Calculation == "Items" && $total_items < $location->Unit) {
             $return->remove($location);
         }
     }
     // Now find max values based on units
     foreach ($return as $location) {
         if ($location->Calculation == "Price" && $location->Unit > $max_cost) {
             $max_cost = $location->Unit;
         }
         if ($location->Calculation == "Weight" && $location->Unit > $max_weight) {
             $max_weight = $location->Unit;
         }
         if ($location->Calculation == "Items" && $location->Unit > $max_items) {
             $max_items = $location->Unit;
         }
     }
     // Now loop through again and calculate which brackets each
     // Location fits in
     foreach ($return as $location) {
         if ($location->Calculation == "Price" && $location->Unit < $max_cost) {
             $return->remove($location);
         }
         if ($location->Calculation == "Weight" && $location->Unit < $max_weight) {
             $return->remove($location);
         }
         if ($location->Calculation == "Items" && $location->Unit < $max_items) {
             $return->remove($location);
         }
     }
     return $return;
 }