Ejemplo n.º 1
0
 /**
  * Determines if this coupon is valid for a cart
  * 
  * @param \Shop\Models\Carts $cart
  * @throws \Exception
  */
 public function cartValid(\Shop\Models\Carts $cart)
 {
     // Set $this->__is_validated = true if YES, cart can use this coupon
     // throw an Exception if NO, cart cannot use this coupon
     /**
      * is the coupon published?
      */
     if (!$this->published()) {
         throw new \Exception('This coupon is expired.');
     }
     /**
      * Only 1 user-submitted coupon per cart,
      * and if the auto-coupon is exclusive, it can't be added with others
      */
     // If this is a user-submitted coupon && there are other user-submitted coupons in the cart, fail
     if (empty($this->usage_automatic) && $cart->userCoupons() && $cart->userCoupons()[0]['code'] != $this->code) {
         throw new \Exception('Only one coupon allowed per cart');
     }
     // if this is an automatic coupon && usage_with_others == 0 && there are other automatic coupons in the cart
     if ($this->usage_automatic && empty($this->usage_with_others) && $cart->autoCoupons()) {
         throw new \Exception('This coupon cannot be combined with others');
     }
     // TODO take min_subtotal_amount_currency into account once we have currencies sorted
     if (!empty($this->min_subtotal_amount) && $cart->subtotal() < $this->min_subtotal_amount) {
         throw new \Exception('Cart has not met the minimum required subtotal');
     }
     // TODO take min_order_amount_currency into account once we have currencies sorted
     $total = $cart->subtotal() - $cart->giftCardTotal() - $cart->discountTotal() - $cart->creditTotal();
     // Add back the value of this coupon in case it is already applied
     foreach ($cart->allCoupons() as $coupon) {
         if ((string) $coupon['_id'] == (string) $this->id) {
             $total = $total + $coupon['amount'];
             break;
         }
     }
     if (!empty($this->min_order_amount) && $total < $this->min_order_amount) {
         throw new \Exception('Cart has not met the minimum required amount');
     }
     /**
      * check that at least one of the $this->required_products is in the cart
      */
     if (!empty($this->required_products)) {
         // get the IDs of all products in this cart
         $product_ids = array();
         foreach ($cart->items as $cartitem) {
             $product_ids[] = (string) \Dsc\ArrayHelper::get($cartitem, 'product_id');
         }
         $intersection = array_intersect($this->required_products, $product_ids);
         if (empty($intersection)) {
             throw new \Exception('Coupon does not apply to any products in your cart.');
         }
     }
     /**
      * check that at least one of the $this->required_coupons is in the cart
      */
     if (!empty($this->required_coupons)) {
         // get the IDs of all coupons in this cart
         $coupon_ids = array();
         foreach ($cart->userCoupons() as $coupon) {
             $coupon_ids[] = (string) $coupon['_id'];
         }
         foreach ($cart->autoCoupons() as $coupon) {
             $coupon_ids[] = (string) $coupon['_id'];
         }
         $intersection = array_intersect($this->required_coupons, $coupon_ids);
         if (empty($intersection)) {
             throw new \Exception('Cart does not have any of the required coupons');
         }
     }
     /**
      * check that at least one of the products from $this->required_collections is in the cart
      */
     if (!empty($this->required_collections)) {
         // get the IDs of all products in this cart
         $product_ids = array();
         foreach ($cart->items as $cartitem) {
             $product_ids[] = (string) \Dsc\ArrayHelper::get($cartitem, 'product_id');
         }
         $found = false;
         foreach ($this->required_collections as $collection_id) {
             $collection_product_ids = \Shop\Models\Collections::productIds($collection_id);
             $intersection = array_intersect($collection_product_ids, $product_ids);
             if (!empty($intersection)) {
                 $found = true;
                 break;
                 // if its found, break the foreach loop
             }
         }
         if (!$found) {
             throw new \Exception('Coupon does not apply to any products in your cart.');
         }
     }
     /**
      * evaluate shopper groups against $this->groups
      */
     if (!empty($this->groups)) {
         $groups = array();
         $user = (new \Users\Models\Users())->setState('filter.id', $cart->user_id)->getItem();
         if (empty($cart->user_id) || empty($user->id)) {
             // Get the default group
             $group_id = \Shop\Models\Settings::fetch()->{'users.default_group'};
             if (!empty($group_id)) {
                 $groups[] = (new \Users\Models\Groups())->setState('filter.id', (string) $group_id)->getItem();
             }
         } elseif (!empty($user->id)) {
             $groups = $user->groups();
         }
         $group_ids = array();
         foreach ($groups as $group) {
             $group_ids[] = (string) $group->id;
         }
         switch ($this->groups_method) {
             case "none":
                 $intersection = array_intersect($this->groups, $group_ids);
                 if (!empty($intersection)) {
                     throw new \Exception('Your order does not qualify for this discount.');
                 }
                 break;
             case "all":
                 // $missing_groups == the ones from $this->groups that are NOT in $group_ids
                 $missing_groups = array_diff($this->groups, $group_ids);
                 if (!empty($missing_groups)) {
                     throw new \Exception('Your order does not qualify for this discount.');
                 }
                 break;
             case "one":
             default:
                 $intersection = array_intersect($this->groups, $group_ids);
                 if (empty($intersection)) {
                     throw new \Exception('Your order does not qualify for this discount.');
                 }
                 break;
         }
     }
     /**
      * using geo_address_type (shipping/billing) from the cart, check that it is in geo_countries | geo_regions (if either is set)
      */
     if (!empty($this->geo_countries) || !empty($this->geo_regions)) {
         // ok, so which of the addresses should we evaluate?
         switch ($this->geo_address_type) {
             case "billing":
                 $region = $cart->billingRegion();
                 $country = $cart->billingCountry();
                 break;
             case "shipping":
             default:
                 $region = $cart->shippingRegion();
                 $country = $cart->shippingCountry();
                 break;
         }
         if (is_null($region) && !empty($this->geo_regions) || is_null($country) && !empty($this->geo_countries)) {
             throw new \Exception('Customer cannot use this coupon until we know your address');
         }
         if (!empty($this->geo_countries)) {
             // eval the country
             if (!in_array($country, $this->geo_countries)) {
                 throw new \Exception('Shipping address is invalid');
             }
         }
         if (!empty($this->geo_regions)) {
             // eval the region
             if (!in_array($region, $this->geo_regions)) {
                 throw new \Exception('Shipping address is invalid');
             }
         }
     }
     /**
      * Check the usage of the coupon
      */
     if (strlen($this->usage_max)) {
         // usage_max = number of times TOTAL that the coupon may be used
         // count the orders with coupon.code
         $total_count = (new \Shop\Models\Orders())->collection()->count(array('coupons.code' => $this->code));
         if ((int) $this->usage_max <= (int) $total_count) {
             throw new \Exception('Coupon cannot be used any more');
         }
     }
     if (strlen($this->usage_max_per_customer)) {
         // usage_max_per_customer = number of times this customer may use this coupon
         // count the orders with coupon.code for user.id
         $user_count = (new \Shop\Models\Orders())->collection()->count(array('coupons.code' => $this->code, 'user_id' => $cart->user_id));
         if ((int) $this->usage_max_per_customer <= (int) $user_count) {
             throw new \Exception('You cannot use this coupon any more');
         }
     }
     /**
      * Check, if this isn't generated code
      */
     if (!empty($this->generated_code)) {
         $key = new \MongoRegex('/' . $this->generated_code . '/i');
         $result = \Shop\Models\Coupons::collection()->aggregate(array('$match' => array('_id' => new \MongoId((string) $this->id))), array('$unwind' => '$codes.list'), array('$match' => array("codes.list.code" => $key)), array('$group' => array('_id' => '$title', 'used_code' => array('$sum' => '$codes.list.used'))));
         if (count($result['result'])) {
             if ($result['result'][0]['used_code']) {
                 throw new \Exception('You cannot use this coupon any more');
             }
         } else {
             throw new \Exception('Coupon "' . $this->generated_code . '" is no longer available.');
         }
     }
     /**
      * if we made it this far, the cart is valid for this coupon
      */
     $this->__is_validated = true;
     return $this;
 }